diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 77ec83e8..660a6fbd 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -10,7 +10,7 @@ on: - devel env: - DEFAULT_PACKAGES: libcmocka-dev zlib1g-dev libssh-dev libssl-dev libpam0g-dev + DEFAULT_PACKAGES: libcmocka-dev zlib1g-dev libssh-dev libssl-dev libpam0g-dev libcurl4-openssl-dev jobs: git-branch: @@ -85,37 +85,13 @@ jobs: make-prepend: "", make-target: "" } - - { - name: "SSH Only", - os: "ubuntu-22.04", - build-type: "Debug", - dep-build-type: "Release", - cc: "gcc", - options: "-DENABLE_TLS=OFF -DENABLE_SSH=ON", - packages: "valgrind", - snaps: "", - make-prepend: "", - make-target: "" - } - - { - name: "TLS Only", - os: "ubuntu-22.04", - build-type: "Debug", - dep-build-type: "Release", - cc: "gcc", - options: "-DENABLE_TLS=ON -DENABLE_SSH=OFF", - packages: "valgrind", - snaps: "", - make-prepend: "", - make-target: "" - } - { name: "No SSH nor TLS", os: "ubuntu-22.04", build-type: "Debug", dep-build-type: "Release", cc: "gcc", - options: "-DENABLE_TLS=OFF -DENABLE_SSH=OFF", + options: "-DENABLE_SSH_TLS=OFF", packages: "valgrind", snaps: "", make-prepend: "", diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index ff6b931b..d65fdba6 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -7,7 +7,7 @@ on: branches: [ "devel" ] env: - DEFAULT_PACKAGES: libcmocka-dev zlib1g-dev libssh-dev libssl-dev libpam0g-dev + DEFAULT_PACKAGES: libcmocka-dev zlib1g-dev libssh-dev libssl-dev libpam0g-dev libcurl4-openssl-dev jobs: git-branch: diff --git a/.github/workflows/devel-push.yml b/.github/workflows/devel-push.yml index 6289f6c1..cb694721 100644 --- a/.github/workflows/devel-push.yml +++ b/.github/workflows/devel-push.yml @@ -5,7 +5,7 @@ on: - devel env: - DEFAULT_PACKAGES: libcmocka-dev zlib1g-dev libssh-dev libssl-dev libpam0g-dev + DEFAULT_PACKAGES: libcmocka-dev zlib1g-dev libssh-dev libssl-dev libpam0g-dev libcurl4-openssl-dev COVERITY_PROJECT: CESNET%2Flibnetconf2 jobs: diff --git a/.gitignore b/.gitignore index fc5bd908..117119fb 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,3 @@ /pkg +/build +/doc/html diff --git a/CMakeLists.txt b/CMakeLists.txt index 2a29e96d..59181655 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 2.8.12) +cmake_minimum_required(VERSION 3.5) project(libnetconf2 C) @@ -57,17 +57,17 @@ endif() # Generic version of not only the library. Major version is reserved for really big changes of the project, # minor version changes with added functionality (new tool, functionality of the tool or library, ...) and # micro version is changed with a set of small changes or bugfixes anywhere in the project. -set(LIBNETCONF2_MAJOR_VERSION 2) -set(LIBNETCONF2_MINOR_VERSION 1) -set(LIBNETCONF2_MICRO_VERSION 40) +set(LIBNETCONF2_MAJOR_VERSION 3) +set(LIBNETCONF2_MINOR_VERSION 0) +set(LIBNETCONF2_MICRO_VERSION 0) set(LIBNETCONF2_VERSION ${LIBNETCONF2_MAJOR_VERSION}.${LIBNETCONF2_MINOR_VERSION}.${LIBNETCONF2_MICRO_VERSION}) # Version of the library # Major version is changed with every backward non-compatible API/ABI change in libyang, minor version changes # with backward compatible change and micro version is connected with any internal change of the library. -set(LIBNETCONF2_MAJOR_SOVERSION 3) -set(LIBNETCONF2_MINOR_SOVERSION 7) -set(LIBNETCONF2_MICRO_SOVERSION 1) +set(LIBNETCONF2_MAJOR_SOVERSION 4) +set(LIBNETCONF2_MINOR_SOVERSION 0) +set(LIBNETCONF2_MICRO_SOVERSION 0) set(LIBNETCONF2_SOVERSION_FULL ${LIBNETCONF2_MAJOR_SOVERSION}.${LIBNETCONF2_MINOR_SOVERSION}.${LIBNETCONF2_MICRO_SOVERSION}) set(LIBNETCONF2_SOVERSION ${LIBNETCONF2_MAJOR_SOVERSION}) @@ -90,8 +90,7 @@ else() endif() option(ENABLE_EXAMPLES "Build examples" ON) option(ENABLE_COVERAGE "Build code coverage report from tests" OFF) -option(ENABLE_SSH "Enable NETCONF over SSH support (via libssh)" ON) -option(ENABLE_TLS "Enable NETCONF over TLS support (via OpenSSL)" ON) +option(ENABLE_SSH_TLS "Enable NETCONF over SSH and TLS support (via libssh and OpenSSL)" ON) option(ENABLE_DNSSEC "Enable support for SSHFP retrieval using DNSSEC for SSH (requires OpenSSL and libval)" OFF) set(READ_INACTIVE_TIMEOUT 20 CACHE STRING "Maximum number of seconds waiting for new data once some data have arrived") set(READ_ACTIVE_TIMEOUT 300 CACHE STRING "Maximum number of seconds for receiving a full message") @@ -99,7 +98,6 @@ set(MAX_PSPOLL_THREAD_COUNT 6 CACHE STRING "Maximum number of threads that could set(TIMEOUT_STEP 100 CACHE STRING "Number of microseconds tasks are repeated until timeout elapses") set(YANG_MODULE_DIR "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_DATADIR}/yang/modules/libnetconf2" CACHE STRING "Directory where to copy the YANG modules to") set(CLIENT_SEARCH_DIR "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_DATADIR}/yang/modules" CACHE STRING "Default NC client YANG module search directory") -set(CALL_HOME_BACKOFF_WAIT 2 CACHE STRING "Number of seconds to wait between Call Home connection attempts") # # sources @@ -111,20 +109,21 @@ set(libsrc src/messages_server.c src/session.c src/session_client.c - src/session_server.c) + src/session_server.c + src/server_config.c + src/server_config_util.c) -if(ENABLE_SSH) +if(ENABLE_SSH_TLS) list(APPEND libsrc src/session_client_ssh.c - src/session_server_ssh.c) - set(SSH_MACRO "#ifndef NC_ENABLED_SSH\n#define NC_ENABLED_SSH\n#endif") -endif() - -if(ENABLE_TLS) - list(APPEND libsrc + src/session_server_ssh.c + src/server_config_util_ssh.c src/session_client_tls.c - src/session_server_tls.c) - set(TLS_MACRO "#ifndef NC_ENABLED_TLS\n#define NC_ENABLED_TLS\n#endif") + src/session_server_tls.c + src/server_config_util_tls.c + src/server_config_ks.c + src/server_config_ts.c) + set(SSH_TLS_MACRO "#ifndef NC_ENABLED_SSH_TLS\n#define NC_ENABLED_SSH_TLS\n#endif") endif() set(headers @@ -136,11 +135,12 @@ set(headers src/session_client.h src/session_client_ch.h src/session_server.h - src/session_server_ch.h) + src/session_server_ch.h + src/server_config.h) # files to generate doxygen from set(doxy_files - src/libnetconf.h + doc/libnetconf.doc src/log.h src/netconf.h src/session.h @@ -149,7 +149,8 @@ set(doxy_files src/session_client.h src/session_client_ch.h src/session_server.h - src/session_server_ch.h) + src/session_server_ch.h + src/server_config.h) # source files to be covered by the 'format' target set(format_sources @@ -160,13 +161,12 @@ set(format_sources src/*.c src/*.h tests/*.c - tests/client/*.c tests/pam/*.c) # # checks # -if(ENABLE_DNSSEC AND NOT ENABLE_SSH) +if(ENABLE_DNSSEC AND NOT ENABLE_SSH_TLS) message(WARNING "DNSSEC SSHFP retrieval cannot be used without SSH support.") set(ENABLE_DNSSEC OFF) endif() @@ -225,32 +225,21 @@ target_link_libraries(netconf2 ${CMAKE_THREAD_LIBS_INIT}) set(CMAKE_REQUIRED_LIBRARIES pthread) check_function_exists(pthread_rwlockattr_setkind_np HAVE_PTHREAD_RWLOCKATTR_SETKIND_NP) -# dependencies - openssl -if(ENABLE_TLS OR ENABLE_DNSSEC OR ENABLE_SSH) - find_package(OpenSSL REQUIRED) - if(ENABLE_TLS) - message(STATUS "OpenSSL found, required for TLS") - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DNC_ENABLED_TLS") - endif() - if(OPENSSL_VERSION VERSION_LESS 1.1.1) - message(WARNING "OpenSSL version ${OPENSSL_VERSION} is no longer maintained, consider an update.") - endif() - +if(ENABLE_SSH_TLS) + # dependencies - openssl + find_package(OpenSSL 3.0.0 REQUIRED) target_link_libraries(netconf2 ${OPENSSL_LIBRARIES}) include_directories(${OPENSSL_INCLUDE_DIR}) -endif() - -# dependencies - libssh -if(ENABLE_SSH) - find_package(LibSSH 0.7.1 REQUIRED) - if(LIBSSH_VERSION VERSION_EQUAL 0.9.3 OR LIBSSH_VERSION VERSION_EQUAL 0.9.4) - message(FATAL_ERROR "LibSSH ${LIBSSH_VERSION} includes regression bugs and libnetconf2 will NOT work properly, try to use another version") - endif() + # dependencies - libssh + find_package(LibSSH 0.9.5 REQUIRED) target_link_libraries(netconf2 ${LIBSSH_LIBRARIES}) list(APPEND CMAKE_REQUIRED_LIBRARIES ${LIBSSH_LIBRARIES}) include_directories(${LIBSSH_INCLUDE_DIRS}) - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DNC_ENABLED_SSH") + + # dependencies - libcurl + find_package(CURL 7.30.0 REQUIRED) + target_link_libraries(netconf2 CURL::libcurl) # crypt if(${CMAKE_SYSTEM_NAME} MATCHES "QNX") @@ -279,6 +268,9 @@ if(ENABLE_SSH) else() message(WARNING "LibPAM not found, PAM-based keyboard-interactive SSH server authentication method is disabled") endif() + + # set compiler flag + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DNC_ENABLED_SSH_TLS") endif() # dependencies - libval @@ -294,10 +286,6 @@ find_package(LibYANG ${LIBYANG_DEP_SOVERSION} REQUIRED) target_link_libraries(netconf2 ${LIBYANG_LIBRARIES}) include_directories(${LIBYANG_INCLUDE_DIRS}) -# header file compatibility - shadow.h and crypt.h -check_include_file("shadow.h" HAVE_SHADOW) -check_include_file("crypt.h" HAVE_CRYPT) - # function compatibility - getpeereid on QNX if(${CMAKE_SYSTEM_NAME} MATCHES "QNX") target_link_libraries(netconf2 -lsocket) @@ -344,8 +332,8 @@ endif() # examples if(ENABLE_EXAMPLES) - if(NOT ENABLE_SSH) - message(WARNING "Examples will not be compiled because SSH is disabled.") + if(NOT ENABLE_SSH_TLS) + message(WARNING "Examples will not be compiled because SSH and TLS are disabled.") else() add_subdirectory(examples) endif() diff --git a/CMakeModules/UseCompat.cmake b/CMakeModules/UseCompat.cmake index 144a6368..bc132b4b 100644 --- a/CMakeModules/UseCompat.cmake +++ b/CMakeModules/UseCompat.cmake @@ -62,6 +62,16 @@ macro(USE_COMPAT) check_symbol_exists(get_current_dir_name "unistd.h" HAVE_GET_CURRENT_DIR_NAME) + # crypt + check_include_file("crypt.h" HAVE_CRYPT_H) + + if(${CMAKE_SYSTEM_NAME} MATCHES "QNX") + list(APPEND CMAKE_REQUIRED_LIBRARIES -llogin) + elseif(NOT APPLE) + list(APPEND CMAKE_REQUIRED_LIBRARIES -lcrypt) + endif() + check_symbol_exists(crypt_r "crypt.h" HAVE_CRYPT_R) + TEST_BIG_ENDIAN(IS_BIG_ENDIAN) check_include_file("stdatomic.h" HAVE_STDATOMIC) diff --git a/Doxyfile.in b/Doxyfile.in index 324e670f..551c59b3 100644 --- a/Doxyfile.in +++ b/Doxyfile.in @@ -561,7 +561,7 @@ INLINE_INFO = YES # name. If set to NO, the members will appear in declaration order. # The default value is: YES. -SORT_MEMBER_DOCS = YES +SORT_MEMBER_DOCS = NO # If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the brief # descriptions of file, namespace and class members alphabetically by member @@ -2069,7 +2069,7 @@ INCLUDE_FILE_PATTERNS = # recursively expanded use the := operator instead of the = operator. # This tag requires that the tag ENABLE_PREPROCESSING is set to YES. -PREDEFINED = NC_ENABLED_SSH NC_ENABLED_TLS +PREDEFINED = NC_ENABLED_SSH_TLS # If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then this # tag can be used to specify a list of macro names that should be expanded. The @@ -2184,7 +2184,7 @@ HIDE_UNDOC_RELATIONS = YES # set to NO # The default value is: NO. -HAVE_DOT = @HAVE_DOT@ +HAVE_DOT = YES # The DOT_NUM_THREADS specifies the number of dot invocations doxygen is allowed # to run in parallel. When set to 0 doxygen will base this on the number of diff --git a/README.md b/README.md index e9e321ae..567b5e31 100644 --- a/README.md +++ b/README.md @@ -22,7 +22,8 @@ NETCONF 1.0 ([RFC 4741](https://tools.ietf.org/html/rfc4741)) as well as NETCONF * NETCONF over pre-established transport sessions (using this mechanism the communication can be tunneled through sshd(8), for instance). * NETCONF Call Home ([RFC 8071](https://tools.ietf.org/html/rfc8071)). -* NETCONF Event Notifications ([RFC 5277](https://tools.ietf.org/html/rfc5277)), +* NETCONF Event Notifications ([RFC 5277](https://tools.ietf.org/html/rfc5277)). +* Compatibility with the [ietf-netconf-server](https://datatracker.ietf.org/doc/html/draft-ietf-netconf-netconf-client-server-29#name-the-ietf-netconf-server-mod) YANG module. **libnetconf2** is maintained and further developed by the [Tools for Monitoring and Configuration](https://www.liberouter.org/) department of @@ -59,11 +60,11 @@ the `distro` directory. ## Requirements * C compiler (gcc >= 4.8.4, clang >= 3.0, ...) -* cmake >= 2.8.12 +* cmake >= 3.5.0 * [libyang](https://github.com/CESNET/libyang) -* libssh >= 0.7.1 (for SSH support) - * recommended >= 0.9.0 -* OpenSSL (for TLS support) +* libssh >= 0.9.5 (for SSH support) +* OpenSSL >= 3.0.0 (for TLS support) +* curl >= 7.30.0 #### Optional @@ -122,7 +123,7 @@ and enabling both the transport protocols can be made in the same way. The following command has actually the same effect as specifying no option since it specifies the default settings. ``` -$ cmake -DENABLE_TLS=ON -DENABLE_SSH=ON .. +$ cmake -DENABLE_SSH_TLS=ON .. ``` ### DNSSEC SSHFP Retrieval diff --git a/compat/compat.c b/compat/compat.c index d0495b28..88a3b698 100644 --- a/compat/compat.c +++ b/compat/compat.c @@ -16,6 +16,7 @@ #include "compat.h" +#include #include #include #include @@ -372,3 +373,21 @@ get_current_dir_name(void) } #endif + +#ifndef HAVE_CRYPT_R +char * +crypt_r(const char *phrase, const char *setting, struct crypt_data *data) +{ + static pthread_mutex_t crypt_lock = PTHREAD_MUTEX_INITIALIZER; + char *hash; + + (void) data; + + pthread_mutex_lock(&crypt_lock); + hash = crypt(phrase, setting); + pthread_mutex_unlock(&crypt_lock); + + return hash; +} + +#endif diff --git a/compat/compat.h.in b/compat/compat.h.in index 2c6495d6..c6209c82 100644 --- a/compat/compat.h.in +++ b/compat/compat.h.in @@ -17,6 +17,12 @@ #define _GNU_SOURCE /* pthread_rwlock_t */ +#cmakedefine HAVE_CRYPT_H + +#ifdef HAVE_CRYPT_H +# include +#endif + #include #include #include @@ -24,6 +30,7 @@ #include #include #include +#include #ifndef __WORDSIZE # if defined __x86_64__ && !defined __ILP32__ @@ -69,6 +76,7 @@ #cmakedefine HAVE_STRDUPA #cmakedefine HAVE_STRCHRNUL #cmakedefine HAVE_GET_CURRENT_DIR_NAME +#cmakedefine HAVE_CRYPT_R #ifndef bswap64 #define bswap64(val) \ @@ -204,4 +212,8 @@ char *strchrnul(const char *s, int c); char *get_current_dir_name(void); #endif +#ifndef HAVE_CRYPT_R +char *crypt_r(const char *phrase, const char *setting, struct crypt_data *data); +#endif + #endif /* _COMPAT_H_ */ diff --git a/distro/README.md b/distro/README.md new file mode 100644 index 00000000..70dba2d5 --- /dev/null +++ b/distro/README.md @@ -0,0 +1,17 @@ +# upstream packaging + +This directory contains upstream packaging sources in apkg format. + +apkg tool can be used to build packages directly from this source repo. + +See apkg docs: https://pkg.labs.nic.cz/pages/apkg/ + + +## RPM-based system (Fedora, CentOS, SUSE, ...) quickstart + +``` +sudo dnf install -y git rpm-build python3-pip +pip3 install apkg + +apkg build -b +``` diff --git a/distro/pkg/deb/control b/distro/pkg/deb/control index 9e15c613..65e25e21 100644 --- a/distro/pkg/deb/control +++ b/distro/pkg/deb/control @@ -7,14 +7,15 @@ Standards-Version: 4.5.0 Build-Depends: cmake, debhelper (>= 10), libyang2-dev, - libssl-dev, - libssh-dev (>= 0.7.1), + libssl-dev (>= 3.0.0), + libssh-dev (>= 0.9.5), libpam0g-dev, - pkg-config + pkg-config, + libcurl4-openssl-dev (>= 7.30.0) Vcs-Browser: https://github.com/CESNET/libnetconf2/tree/master Vcs-Git: https://github.com/CESNET/libnetconf2.git -Package: libnetconf2-3 +Package: libnetconf2 Depends: ${misc:Depends}, ${shlibs:Depends} Architecture: any @@ -29,7 +30,7 @@ Description: library implementing NETCONF protocol - runtime Package: libnetconf2-dev Depends: libyang2-dev, - libnetconf2-3 (= ${binary:Version}), + libnetconf2 (= ${binary:Version}), ${misc:Depends} Section: libdevel Architecture: any @@ -42,4 +43,3 @@ Description: library implementing NETCONF protocol - development files . This package contains the C headers, a pkgconfig file, and .so entry point for libnetconf2. - diff --git a/distro/pkg/deb/libnetconf2-3.install b/distro/pkg/deb/libnetconf2.install similarity index 100% rename from distro/pkg/deb/libnetconf2-3.install rename to distro/pkg/deb/libnetconf2.install diff --git a/distro/pkg/rpm/libnetconf2.spec b/distro/pkg/rpm/libnetconf2.spec index 979a5161..cfb66e48 100644 --- a/distro/pkg/rpm/libnetconf2.spec +++ b/distro/pkg/rpm/libnetconf2.spec @@ -12,6 +12,7 @@ BuildRequires: libssh-devel BuildRequires: openssl-devel BuildRequires: pam-devel BuildRequires: pkgconfig(libyang) >= 2 +BuildRequires: libcurl-devel %package devel Summary: Headers of libnetconf2 library diff --git a/src/libnetconf.h b/doc/libnetconf.doc similarity index 63% rename from src/libnetconf.h rename to doc/libnetconf.doc index 27ba007f..c91b46fe 100644 --- a/src/libnetconf.h +++ b/doc/libnetconf.doc @@ -1,32 +1,3 @@ -/** - * @file libnetconf.h - * @author Radek Krejci - * @author Michal Vasko - * @brief libnetconf2 main internal header. - * - * @copyright - * Copyright (c) 2015 - 2021 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 NC_LIBNETCONF_H_ -#define NC_LIBNETCONF_H_ - -#include "config.h" -#include "log_p.h" -#include "messages_p.h" -#include "netconf.h" -#include "session_p.h" - -/* Tests whether string is empty or non-empty. */ -#define strisempty(str) ((str)[0] == '\0') -#define strnonempty(str) ((str)[0] != '\0') - /** * @mainpage About * @@ -46,7 +17,8 @@ * - Creating NETCONF Call Home sessions ([RFC 8071](https://tools.ietf.org/html/rfc8071)). * - Creating, sending, receiving, and replying to RPCs ([RFC 4741](https://tools.ietf.org/html/rfc4741), * [RFC 6241](https://tools.ietf.org/html/rfc6241)). - * - Creating, sending and receiving NETCONF Event Notifications ([RFC 5277](https://tools.ietf.org/html/rfc5277)), + * - Creating, sending and receiving NETCONF Event Notifications ([RFC 5277](https://tools.ietf.org/html/rfc5277)). + * - Configuring the NETCONF server based on the [ietf-netconf-server](https://datatracker.ietf.org/doc/html/draft-ietf-netconf-netconf-client-server-29) YANG module * * @section about-license License * @@ -83,13 +55,11 @@ * @page howtoinit Init and Thread-safety Information * * Before working with the library, it must be initialized using ::nc_client_init() - * or ::nc_server_init(). Based on how the library was compiled, also _libssh_ and/or - * _libssh_/_libcrypto_ are initialized (for multi-threaded use) too. To prevent - * any reachable memory at the end of your application, there are complementary - * destroy functions (::nc_server_destroy() and ::nc_client_destroy() available. If your + * and/or ::nc_server_init(). To prevent any reachable memory at the end of your + * application, there are complementary destroy functions + * (::nc_server_destroy() and ::nc_client_destroy() available). If your * application is multi-threaded, call the destroy functions in the main thread, - * after all the other threads have ended. In every other thread you should call - * ::nc_thread_destroy() just before it exits. + * after all the other threads have ended. * * If _libnetconf2_ is used in accordance with this information, there should * not be memory leaks of any kind at program exit. For thread-safety details @@ -104,7 +74,7 @@ * specified via ::nc_client_set_schema_searchpath(). Alternatively, _libnetconf2_ can use callback * provided via ::nc_client_set_schema_callback(). If these ways do not succeed and the server * implements NETCONF \ operation, the schema is retrieved from the server and stored - * localy into the searchpath (if specified) for a future use. If none of these methods succeed to + * locally into the searchpath (if specified) for a future use. If none of these methods succeed to * load particular schema, the data from this schema are ignored during the communication with the * server. * @@ -150,10 +120,6 @@ * * - ::nc_server_init() * - ::nc_server_destroy() - * - * Available in both __nc_client.h__ and __nc_server.h__. - * - * - ::nc_thread_destroy() */ /** @@ -198,8 +164,6 @@ * * Available in __nc_client.h__. * - * - ::nc_client_ssh_set_auth_hostkey_check_clb() - * - ::nc_client_ssh_get_auth_hostkey_check_clb() * - ::nc_client_ssh_set_auth_password_clb() * - ::nc_client_ssh_get_auth_password_clb() * - ::nc_client_ssh_set_auth_interactive_clb() @@ -281,7 +245,6 @@ * * Available in __nc_client.h__. * - * - ::nc_client_ssh_ch_set_auth_hostkey_check_clb() * - ::nc_client_ssh_ch_set_auth_password_clb() * - ::nc_client_ssh_ch_set_auth_interactive_clb() * - ::nc_client_ssh_ch_set_auth_privkey_passphrase_clb() @@ -325,9 +288,6 @@ * determined by the context used when accepting new NETCONF sessions. Few capabilities that * cannot be learnt from the context are set with separate functions * ::nc_server_set_capab_withdefaults() and generally ::nc_server_set_capability(). - * Timeout for receiving the _hello_ message on a new session can be set - * by ::nc_server_set_hello_timeout() and the timeout for disconnecting - * an inactive session by ::nc_server_set_idle_timeout(). * * Context does not only determine server modules, but its overall * functionality as well. For every RPC the server should support, @@ -338,13 +298,7 @@ * establish SSH or TLS transport or do it yourself and only provide the file * descriptors of the connection. * - * Server options can be only set, there are no getters. - * - * To be able to accept any connections, endpoints must first be added - * with ::nc_server_add_endpt() and configured with ::nc_server_endpt_set_address() - * and ::nc_server_endpt_set_port(). For unix sockets, ::nc_server_endpt_set_perms() - * is available to set the unix socket file permissions, and ::nc_server_endpt_set_port() - * is invalid. + * To be able to accept any connections, the server must first be configured. * * Functions List * -------------- @@ -353,46 +307,112 @@ * * - ::nc_server_set_capab_withdefaults() * - ::nc_server_set_capability() - * - ::nc_server_set_hello_timeout() - * - ::nc_server_set_idle_timeout() * - * - ::nc_server_add_endpt() - * - ::nc_server_del_endpt() - * - ::nc_server_endpt_set_address() - * - ::nc_server_endpt_set_port() - * - ::nc_server_endpt_set_perms() + * Server Configuration + * === + * + * To successfully accept connections on a server, you first need to configure it. + * The *libnetconf2* server natively supports the *ietf-netconf-server YANG* module. + * This allows for a bigger scaling and flexibility of the *NETCONF* server. + * By using *ietf-netconf-server YANG* data you can express network configurations + * in a standardized and hierarchical format, enabling you to define complex network + * structures with greater ease. + * + * The process of configuring a server is comprised of two steps. The first step is creating the + * configuration data and the second is applying it. The server supports two forms of the configuration + * data - *YANG data* and *YANG diff*. + * + * YANG data + * --- + * Configuring the server using YANG data simplifies the management of network services. + * With YANG data, you build a structured configuration tree and apply it as a whole. + * This approach is user-friendly, allowing you to modify the configuration by adding or deleting nodes, + * and then deploying the updated configuration tree in its entirety, providing a way to manage your server's settings. + * The *libnetconf2* library exports API functions that can help you with creation or deletion of the *YANG* data. + * + * YANG diff + * --- + * YANG diff, enriched with operation attributes, offers advanced configuration control. + * It empowers the user to make precise changes within the configuration tree, + * enabling operations like specific node deletions, additions, and modifications. + * On the other hand, unlike YANG data, YANG diff represents only a subtree of the + * changes expecting the whole configuration to be managed externally. + * For example this is done by the tool [sysrepo](https://www.sysrepo.org/). + * + * Usage + * --- + * To be able to configure the server, the required models first need to be implemented. + * To do this, see ::nc_server_config_load_modules(). + * Not all of the *ietf-netconf-server* (and all of its associated modules) features are enabled. + * If you wish to see which features are enabled, extract them from the context after calling the mentioned function. + * + * If you wish not to create the __YANG data__ yourself, you may use the library's functions to do this for you. + * For example ::nc_server_config_add_address_port() creates __YANG data__ corresponding to an SSH/TLS endpoint. + * The variant for UNIX socket is ::nc_server_config_add_unix_socket(). You can then apply this data + * by calling ::nc_server_config_setup_data() (or ::nc_server_config_setup_diff() for diff). + * See *examples/server.c* for a simple example. + * + * You may also create entries in the keystore or truststore. For example the asymmetric key and certificate entries + * in the keystore can be then referenced as the SSH hostkeys or TLS server certificates, respectively. + * As for the truststore, you may create public key and certificate entries, which can then be used + * as SSH user's public keys or TLS server's end-entity/trust-anchor certificates, respectively. + * + * Functions List + * -------------- + * + * Available in __nc_server.h__. * + * - ::nc_server_config_load_modules() + * - ::nc_server_config_setup_diff() + * - ::nc_server_config_setup_data() + * - ::nc_server_config_setup_path() + * + * - ::nc_server_config_add_address_port() + * - ::nc_server_config_add_unix_socket() + * - ::nc_server_config_del_endpt() + * - ::nc_server_config_add_keystore_asym_key() + * - ::nc_server_config_del_keystore_asym_key() + * - ::nc_server_config_add_keystore_cert() + * - ::nc_server_config_del_keystore_cert() + * - ::nc_server_config_add_truststore_pubkey() + * - ::nc_server_config_del_truststore_pubkey() + * - ::nc_server_config_add_truststore_cert() + * - ::nc_server_config_del_truststore_cert() * * SSH * === * - * To successfully accept an SSH session you must set at least the host key using - * ::nc_server_ssh_endpt_add_hostkey(), which are ordered. This way you simply add - * some hostkey identifier, but the key itself will be retrieved always when needed - * by calling the callback set by ::nc_server_ssh_set_hostkey_clb(). + * To successfully accept an SSH session you must configure at least one host key. + * You may create this data yourself or by using ::nc_server_config_add_ssh_hostkey(). + * + * On top of that, each SSH endpoint can define it's own authorized clients and their authentication methods. + * For example if you wish to create an SSH user that can authenticate using a password, use ::nc_server_config_add_ssh_user_password(). + * Another option for authorized clients is to reference another endpoint's clients, however be careful not to create a cyclic reference + * (see ::nc_server_config_add_ssh_endpoint_client_ref()). An authorized client MUST authenticate to all of it's configured authentication methods. * - * There are also some other optional settings. Note that authorized - * public keys are set for the server as a whole, not endpoint-specifically. + * There are also some other optional settings. * * Functions List * -------------- * * Available in __nc_server.h__. * - * - ::nc_server_ssh_endpt_add_hostkey() - * - ::nc_server_ssh_endpt_del_hostkey() - * - ::nc_server_ssh_endpt_mov_hostkey() - * - ::nc_server_ssh_endpt_mod_hostkey() - * - ::nc_server_ssh_endpt_set_auth_methods() - * - ::nc_server_ssh_endpt_set_auth_attempts() - * - ::nc_server_ssh_endpt_set_auth_timeout() - * - * - ::nc_server_ssh_set_hostkey_clb() - * - * - ::nc_server_ssh_add_authkey() - * - ::nc_server_ssh_add_authkey_path() - * - ::nc_server_ssh_del_authkey() - * + * - ::nc_server_config_add_ssh_hostkey() + * - ::nc_server_config_del_ssh_hostkey() + * - ::nc_server_config_add_ssh_keystore_ref() + * - ::nc_server_config_del_ssh_keystore_ref() + * + * - ::nc_server_config_add_ssh_user_pubkey() + * - ::nc_server_config_del_ssh_user_pubkey() + * - ::nc_server_config_add_ssh_user_password() + * - ::nc_server_config_del_ssh_user_password() + * - ::nc_server_config_add_ssh_user_interactive() + * - ::nc_server_config_del_ssh_user_interactive() + * - ::nc_server_config_del_ssh_user() + * - ::nc_server_config_add_ssh_truststore_ref() + * - ::nc_server_config_del_ssh_truststore_ref() + * - ::nc_server_config_add_ssh_endpoint_client_ref() + * - ::nc_server_config_del_ssh_endpoint_client_ref() * * TLS * === @@ -400,47 +420,49 @@ * TLS works with endpoints too, but its options differ * significantly from the SSH ones, especially in the _cert-to-name_ * options that TLS uses to derive usernames from client certificates. - * So, after starting listening on an endpoint you need to set the server - * certificate (::nc_server_tls_endpt_set_server_cert()). Its actual content - * together with the matching private key will be loaded using a callback - * from ::nc_server_tls_set_server_cert_clb(). Additional certificates needed - * for the client to verify the server's certificate chain can be loaded using - * a callback from ::nc_server_tls_set_server_cert_chain_clb(). - * - * To accept client certificates, they must first be considered trusted, - * which you have three ways of achieving. You can add each of their Certificate Authority - * certificates to the trusted ones or mark a specific client certificate - * as trusted. Lastly, you can set paths with all the trusted CA certificates - * with ::nc_server_tls_endpt_set_trusted_ca_paths(). Adding specific certificates - * is also performed only as an arbitrary identificator and later retrieved from - * callback set by ::nc_server_tls_set_trusted_cert_list_clb(). But, you can add - * certficates as whole lists, not one-by-one. + * + * If you wish to listen on a TLS endpoint, you need to configure the endpoint's + * server certificate (see ::nc_server_config_add_tls_server_cert()). + * + * To accept client certificates, they must first be considered trusted. + * For each TLS endpoint you may configure two types of client certificates. + * The first type are end-entity (client) certificates. These are certificates that belong + * to given clients. These certificates need to be trusted. + * The second type are trust-anchor (certificate authority) certificates, + * which carry over the trust (a chain of trust). + * Another option is to reference another TLS endpoint's end-entity certificates, however be careful not to create a cyclic reference + * (see ::nc_server_config_add_tls_endpoint_client_ref()). * * Then, from each trusted client certificate a username must be derived * for the NETCONF session. This is accomplished by finding a matching - * _cert-to-name_ entry. They are added using ::nc_server_tls_endpt_add_ctn(). + * _cert-to-name_ entry. * - * If you need to remove trusted certificates, you can do so with ::nc_server_tls_endpt_del_trusted_cert_list(). - * To clear all Certificate Revocation Lists use ::nc_server_tls_endpt_clear_crls(). + * There are some further options. For example you can configure the TLS + * version and ciphers to be used. You may also choose to use a Certificate + * Revocation List. * * Functions List * -------------- * * Available in __nc_server.h__. * - * - ::nc_server_tls_endpt_set_server_cert() - * - ::nc_server_tls_endpt_add_trusted_cert_list() - * - ::nc_server_tls_endpt_del_trusted_cert_list() - * - ::nc_server_tls_endpt_set_trusted_ca_paths() - * - ::nc_server_tls_endpt_set_crl_paths() - * - ::nc_server_tls_endpt_clear_crls() - * - ::nc_server_tls_endpt_add_ctn() - * - ::nc_server_tls_endpt_del_ctn() - * - ::nc_server_tls_endpt_get_ctn() - * - * - ::nc_server_tls_set_server_cert_clb() - * - ::nc_server_tls_set_server_cert_chain_clb() - * - ::nc_server_tls_set_trusted_cert_list_clb() + * - ::nc_server_config_add_tls_server_cert() + * - ::nc_server_config_del_tls_server_cert() + * - ::nc_server_config_add_tls_keystore_ref() + * - ::nc_server_config_del_tls_keystore_ref() + * + * - ::nc_server_config_add_tls_client_cert() + * - ::nc_server_config_del_tls_client_cert() + * - ::nc_server_config_add_tls_client_cert_truststore_ref() + * - ::nc_server_config_del_tls_client_cert_truststore_ref() + * - ::nc_server_config_add_tls_ca_cert() + * - ::nc_server_config_del_tls_ca_cert() + * - ::nc_server_config_add_tls_ca_cert_truststore_ref() + * - ::nc_server_config_del_tls_ca_cert_truststore_ref() + * - ::nc_server_config_add_tls_endpoint_client_ref() + * - ::nc_server_config_del_tls_endpoint_client_ref() + * - ::nc_server_config_add_tls_ctn() + * - ::nc_server_config_del_tls_ctn() * * FD * == @@ -462,54 +484,64 @@ * * _Call Home_ works with endpoints just like standard sessions, but * the options are organized a bit differently and endpoints are added - * for CH clients. However, one important difference is that - * once all the mandatory options are set, _libnetconf2_ __will not__ - * immediately start connecting to a client. It will do so only after - * calling ::nc_connect_ch_client_dispatch() in a separate thread. - * - * Lastly, monitoring of these sessions is up to the application. + * for CH clients. + * You may choose one of two approaches for creating a new Call Home + * session (or in other words making a server connect to a client). + * The first is to set all the required callbacks + * by calling ::nc_server_ch_set_dispatch_data(). By setting the callbacks, + * the server will automatically start connecting to a client, whenever + * a new Call Home client is created. + * The second approach is to create the Call Home thread manually. + * To do this, you need to call ::nc_connect_ch_client_dispatch(), + * which then creates a new thread and the server will start to connect. + * Unix socket _Call Home_ sessions are not supported. * * Functions List * -------------- * * Available in __nc_server.h__. * - * - ::nc_server_ch_add_client() - * - ::nc_server_ch_del_client() - * - ::nc_server_ch_is_client() - * - ::nc_server_ch_client_add_endpt() - * - ::nc_server_ch_client_del_endpt() - * - ::nc_server_ch_client_is_endpt() - * - ::nc_server_ch_client_endpt_set_address() - * - ::nc_server_ch_client_endpt_set_port() - * - ::nc_server_ch_client_endpt_enable_keepalives() - * - ::nc_server_ch_client_endpt_set_keepalives() - * - ::nc_server_ch_client_set_conn_type() - * - ::nc_server_ch_client_periodic_set_period() - * - ::nc_server_ch_client_periodic_set_anchor_time() - * - ::nc_server_ch_client_periodic_set_idle_timeout() - * - ::nc_server_ch_client_set_start_with() - * - ::nc_server_ch_client_set_max_attempts() - * - ::nc_connect_ch_client_dispatch() - * - * - ::nc_server_ssh_ch_client_endpt_add_hostkey() - * - ::nc_server_ssh_ch_client_endpt_del_hostkey() - * - ::nc_server_ssh_ch_client_endpt_mov_hostkey() - * - ::nc_server_ssh_ch_client_endpt_set_auth_methods() - * - ::nc_server_ssh_ch_client_endpt_get_auth_methods() - * - ::nc_server_ssh_ch_client_endpt_set_auth_attempts() - * - ::nc_server_ssh_ch_client_endpt_set_auth_timeout() - * - * - ::nc_server_tls_ch_client_endpt_set_server_cert() - * - ::nc_server_tls_ch_client_endpt_add_trusted_cert_list() - * - ::nc_server_tls_ch_client_endpt_del_trusted_cert_list() - * - ::nc_server_tls_ch_client_endpt_set_trusted_ca_paths() - * - ::nc_server_tls_ch_client_endpt_set_crl_paths() - * - ::nc_server_tls_ch_client_endpt_clear_crls() - * - ::nc_server_tls_ch_client_endpt_add_ctn() - * - ::nc_server_tls_ch_client_endpt_del_ctn() - * - ::nc_server_tls_ch_client_endpt_get_ctn() - * + * - ::nc_server_config_add_ch_address_port() + * - ::nc_server_config_del_ch_client() + * - ::nc_server_config_del_ch_endpt() + * - ::nc_server_config_add_ch_persistent() + * - ::nc_server_config_add_ch_period() + * - ::nc_server_config_del_ch_period() + * - ::nc_server_config_add_ch_anchor_time() + * - ::nc_server_config_del_ch_anchor_time() + * - ::nc_server_config_add_ch_idle_timeout() + * - ::nc_server_config_del_ch_idle_timeout() + * - ::nc_server_config_add_ch_reconnect_strategy() + * - ::nc_server_config_del_ch_reconnect_strategy() + * + * - ::nc_server_config_add_ch_ssh_hostkey() + * - ::nc_server_config_del_ch_ssh_hostkey() + * - ::nc_server_config_add_ch_ssh_keystore_ref() + * - ::nc_server_config_del_ch_ssh_keystore_ref() + * - ::nc_server_config_add_ch_ssh_user_pubkey() + * - ::nc_server_config_del_ch_ssh_user_pubkey() + * - ::nc_server_config_add_ch_ssh_user_password() + * - ::nc_server_config_del_ch_ssh_user_password() + * - ::nc_server_config_add_ch_ssh_user_interactive() + * - ::nc_server_config_del_ch_ssh_user_interactive() + * - ::nc_server_config_del_ch_ssh_user() + * - ::nc_server_config_add_ch_ssh_truststore_ref() + * - ::nc_server_config_del_ch_ssh_truststore_ref() + * + * - ::nc_server_config_add_ch_tls_server_cert() + * - ::nc_server_config_del_ch_tls_server_cert() + * - ::nc_server_config_add_ch_tls_keystore_ref() + * - ::nc_server_config_del_ch_tls_keystore_ref() + * - ::nc_server_config_add_ch_tls_client_cert() + * - ::nc_server_config_del_ch_tls_client_cert() + * - ::nc_server_config_add_ch_tls_client_cert_truststore_ref() + * - ::nc_server_config_del_ch_tls_client_cert_truststore_ref() + * - ::nc_server_config_add_ch_tls_ca_cert() + * - ::nc_server_config_del_ch_tls_ca_cert() + * - ::nc_server_config_add_ch_tls_ca_cert_truststore_ref() + * - ::nc_server_config_del_ch_tls_ca_cert_truststore_ref() + * - ::nc_server_config_add_ch_tls_ctn() + * - ::nc_server_config_del_ch_tls_ctn() * * Connecting And Cleanup * ====================== @@ -611,7 +643,7 @@ * * There are several timeouts which are used throughout _libnetconf2_ to * assure that it will never indefinitely hang on any operation. Normally, - * you should not need to worry about them much necause they are set by + * you should not need to worry about them much because they are set by * default to reasonable values for common systems. However, if your * platform is not common (embedded, ...), adjusting these timeouts may * save a lot of debugging and time. @@ -622,8 +654,8 @@ * You can adjust active and inactive read timeout using `cmake` variables. * For details look into `README.md`. * - * API Functions - * ------------- + * Configurable timeouts + * --------------------- * * Once a new connection is established including transport protocol negotiations, * _hello_ message is exchanged. You can set how long will the server wait for @@ -633,23 +665,10 @@ * To free up some resources, it is possible to adjust the maximum idle period * of a session before it is disconnected. In _Call Home_, for both a persistent * and periodic connection can this idle timeout be specified separately for each - * client using corresponding functions. - * - * Lastly, SSH user authentication timeout can be also modified. It is the time + * client. Lastly, SSH user authentication timeout can be also modified. It is the time * a client has to successfully authenticate after connecting before it is disconnected. * - * Functions List - * -------------- - * - * Available in __nc_server.h__. - * - * - ::nc_server_set_hello_timeout() - * - ::nc_server_get_hello_timeout() - * - ::nc_server_set_idle_timeout() - * - ::nc_server_get_idle_timeout() - * - ::nc_server_ch_client_periodic_set_idle_timeout() - * - ::nc_server_ssh_ch_client_endpt_set_auth_timeout() - * - ::nc_server_ssh_ch_client_endpt_set_auth_timeout() + * These timeouts can be toggled by applying corresponding configuration data. */ /** @@ -665,6 +684,6 @@ /** * @defgroup server Server * @brief NETCONF server functionality. + * @{ + * @} Server */ - -#endif /* NC_LIBNETCONF_H_ */ diff --git a/examples/README.md b/examples/README.md index 46d8e9a2..6cda2d79 100644 --- a/examples/README.md +++ b/examples/README.md @@ -1,28 +1,36 @@ # libnetconf2 - examples -There are two examples `server` and `client` demonstrating a simple NETCONF server and client using libnetconf2 C library. This is an extensively documented example, which is trying to showcase the key parts of the libnetconf2 library in as simple way as possible. The library configuration is kept to the minimum just to achieve basic functionality. Two types of transport are supported in this example: _UNIX Socket_ and _SSH_ (password authentication only). In the `example.h` header there are the SSH listening IP address and port, username and password that can be edited. Both examples have the `-h` option that displays their usage. +There are two examples `server` and `client` demonstrating a simple NETCONF server and client using libnetconf2 C library. This is an extensively documented example, which is trying to showcase the key parts of the libnetconf2 library in a simple way. The library configuration is kept to the minimum just to achieve basic functionality. Two types of transport are supported in this example: _UNIX Socket_ and _SSH_. Both examples have the `-h` option that displays their usage. ## Server The example server provides `ietf-yang-library` state data that are returned as a reply to `get` RPC. In case an XPath filter is used it is properly applied on these data. If some unsupported parameters are specified, the server replies with a NETCONF error. +### Server Configuration +The server's default configuration can be found in the `config.json` file. The YANG data stored in this file define three endpoints - two for SSH and one for UNIX socket. +You can modify this configuration in any way you want, however, configuring the server may fail if the configuration is not valid. + ## Example usage -### UNIX socket -#### Server +### Server First start the server: ``` -$ server -u ./example_socket +$ server ``` -Where `-u` means UNIX socket transport will be used and `./example_socket` is the path to the socket, where the socket will be listening. +The server will be started and configured per YANG data stored in the file `config.json`. A UNIX socket with the default address `/tmp/.ln2-unix-socket` will be created. +In addition to that two SSH endpoints with the addresses `127.0.0.1:10000` and `127.0.0.1:10001` will be listening. +This first endpoint has a single user that can authenticate with a password (which is set to `admin` by default). +The second endpoint has a single user that can authenticate with a publickey (the asymmetric key pair used is stored in `admin_key` and `admin_key.pub`). -#### Client -After the server has been run, in another terminal instance: +### Client +#### UNIX socket +After the server has been run, in another terminal instance, with the default configuration: ``` -$ client -u ./example_socket get "/ietf-yang-library:yang-library/module-set/module[name='ietf-netconf']" +$ client -u /tmp/.ln2-unix-socket get "/ietf-yang-library:yang-library/module-set/module[name='ietf-netconf']" ``` -In this case, `-u` means that a connection to an UNIX socket will be attemped, `./example_socket` is the path to the UNIX socket, `get` is the name of the RPC and `/ietf-yang-library:yang-library/module-set/module[name='ietf-netconf']` is the RPC's optional XPath filter. +In this case, `-u` means that a connection to an UNIX socket will be attempted and a path to the socket needs to be specified, that is `/tmp/ln2-unix-socket` by default. +The `get` parameter is the name of the RPC and `/ietf-yang-library:yang-library/module-set/module[name='ietf-netconf']` is the RPC's optional XPath filter. -#### Server output +##### Server output ``` -Using UNIX socket! <-- server created +Listening for new connections! <-- server created Connection established <-- client joined Received RPC: get-schema <-- name of the RPC @@ -47,7 +55,7 @@ Received RPC: ``` The server received five supported RPCs. First, the client attempts to obtain basic YANG modules using `get-schema`. Then, it retrieves all the `ietf-yang-library` data to be used for creating its context, which should ideally be the same as that of the server. Next the example `get` RPC is received and lastly `close-session` RPC terminates the connection. -#### Client output +##### Client output ``` @@ -76,22 +84,15 @@ The server received five supported RPCs. First, the client attempts to obtain ba ``` The client received a single `ietf-yang-library` module based on the used filter. -### _SSH_ -#### Server -``` -# server -s /home/user/.ssh/id_rsa -``` -Where `-s` means SSH transport will be used and the next argument `/home/user/.ssh/id_rsa` is the path to an SSH hostkey, which will be used for authentication. The SSH server has to be run as `root`, because the default listening port is 830, which cannot be bound otherwise. This port can be changed in the header file. -To generate an SSH key, you can use: -``` -$ ssh-keygen -``` -#### Client +#### SSH +After the server has been run, in another terminal instance, with the default configuration: ``` -$ client -s get-config candidate +$ client -p 10000 get-config candidate ``` -In this case, `-s` means that a connection via SSH will be attemped, `get-config` is the name of the RPC and `candidate` is the source datastore for the retrieved data of the get-config RPC. -#### Server output +In this case, `-p 10000` is the port to connect to. By default the endpoint with this port has a single authorized client that needs to authenticate with a password. +The parameter `get-config` is the name of the RPC and `candidate` is the source datastore for the retrieved data of the get-config RPC. + +##### Server output ``` Using SSH! Connection established @@ -114,11 +115,18 @@ Received RPC: Received RPC: close-session ``` -#### Client output + +##### Client output ``` admin@127.0.0.1 password: <-- prompts for password, type in 'admin' ``` -The _username_ and _password_ can be found and modified in the `example.h` header file. +The _username_ in the `example.h` header file. The _password_ is located in `config.json`. + +If you wish to connect to the SSH public key endpoint, you need to specify its port and the asymmetric key pair to use. +By default the command to connect would look like so: +``` +$ ./examples/client -p 10001 -P /home/roman/libnetconf2/examples/admin_key.pub -i /home/roman/libnetconf2/examples/admin_key get +``` diff --git a/examples/admin_key b/examples/admin_key new file mode 100644 index 00000000..df71976d --- /dev/null +++ b/examples/admin_key @@ -0,0 +1,7 @@ +-----BEGIN OPENSSH PRIVATE KEY----- +b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW +QyNTUxOQAAACDq+Oq6bYOgbFoTtSTKJrod3LgmJnrjuiXzlD7P2Dt+cAAAAJC1rL1gtay9 +YAAAAAtzc2gtZWQyNTUxOQAAACDq+Oq6bYOgbFoTtSTKJrod3LgmJnrjuiXzlD7P2Dt+cA +AAAEAQm84SEphEUZEbuCRmXrMcYyv70wNEVziE/SbBC6+trOr46rptg6BsWhO1JMomuh3c +uCYmeuO6JfOUPs/YO35wAAAADXJvbWFuQHBjdmFza28= +-----END OPENSSH PRIVATE KEY----- diff --git a/examples/admin_key.pub b/examples/admin_key.pub new file mode 100644 index 00000000..533f33f7 --- /dev/null +++ b/examples/admin_key.pub @@ -0,0 +1 @@ +ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOr46rptg6BsWhO1JMomuh3cuCYmeuO6JfOUPs/YO35w test@libnetconf2 diff --git a/examples/client.c b/examples/client.c index 38512b60..8295fb77 100644 --- a/examples/client.c +++ b/examples/client.c @@ -34,12 +34,14 @@ static void help_print() { printf("Example usage:\n" - " client -u ./unix_socket get\n" + " client get\n" "\n" " Available options:\n" " -h, --help\t \tPrint usage help.\n" - " -u, --unix\t\tConnect to a UNIX socket at the place specified by .\n" - " -s, --ssh\t\tConnect to a SSH server.\n\n" + " -p, --port\t\t\tSpecify the port to connect to.\n" + " -u, --unix-path\t\tConnect to a UNIX socket located at .\n" + " -P, --ssh-pubkey\t\tSet the path to an SSH Public key.\n" + " -i, --ssh-privkey\t\tSet the path to an SSH Private key.\n\n" " Available RPCs:\n" " get [xpath-filter]\t\t\t\t\t send a RPC with optional XPath filter\n" " get-config [datastore] [xpath-filter]\t\t send a RPC with optional XPath filter and datastore, the default datastore is \"running\" \n\n"); @@ -132,16 +134,19 @@ send_rpc(struct nc_session *session, NC_RPC_TYPE rpc_type, const char *param1, c int main(int argc, char **argv) { - int rc = 0, opt, connection_type = NONE; + int rc = 0, opt, port = 0; struct nc_session *session = NULL; const char *unix_socket_path = NULL, *rpc_parameter_1 = NULL, *rpc_parameter_2 = NULL; + const char *ssh_pubkey_path = NULL, *ssh_privkey_path = NULL; struct option options[] = { - {"help", no_argument, NULL, 'h'}, - {"unix", required_argument, NULL, 'u'}, - {"ssh", no_argument, NULL, 's'}, - {"debug", no_argument, NULL, 'd'}, - {NULL, 0, NULL, 0} + {"help", no_argument, NULL, 'h'}, + {"port", required_argument, NULL, 'p'}, + {"unix-path", required_argument, NULL, 'u'}, + {"ssh-pubkey", required_argument, NULL, 'P'}, + {"ssh-privkey", required_argument, NULL, 'i'}, + {"debug", no_argument, NULL, 'd'}, + {NULL, 0, NULL, 0} }; if (argc == 1) { @@ -149,33 +154,36 @@ main(int argc, char **argv) goto cleanup; } - nc_client_init(); /* set the path to search for schemas */ nc_client_set_schema_searchpath(MODULES_DIR); opterr = 0; - while ((opt = getopt_long(argc, argv, "hu:sd", options, NULL)) != -1) { + while ((opt = getopt_long(argc, argv, "hp:u:P:i:d", options, NULL)) != -1) { switch (opt) { case 'h': help_print(); goto cleanup; + case 'p': + port = strtoul(optarg, NULL, 10); + break; + case 'u': unix_socket_path = optarg; - connection_type = UNIX; break; - case 's': - connection_type = SSH; - /* set the client SSH username to always be used when connecting to the server */ - if (nc_client_ssh_set_username(SSH_USERNAME)) { - ERR_MSG_CLEANUP("Couldn't set the SSH username\n"); - } + case 'P': + ssh_pubkey_path = optarg; + break; + + case 'i': + ssh_privkey_path = optarg; break; case 'd': nc_verbosity(NC_VERB_DEBUG); + nc_libssh_thread_verbosity(2); break; default: @@ -187,18 +195,38 @@ main(int argc, char **argv) ERR_MSG_CLEANUP("Expected the name of RPC after options\n"); } + /* check invalid args combinations */ + if (unix_socket_path && port) { + ERR_MSG_CLEANUP("Both UNIX socket path and port specified. Please choose either SSH or UNIX.\n"); + } else if (unix_socket_path && (ssh_pubkey_path || ssh_privkey_path)) { + ERR_MSG_CLEANUP("Both UNIX socket path and a path to key(s) specified. Please choose either SSH or UNIX.\n"); + } else if ((port == 10001) && (!ssh_pubkey_path || !ssh_privkey_path)) { + ERR_MSG_CLEANUP("You need to specify both paths to private and public keys, if you want to connect to a publickey endpoint.\n"); + } else if ((port == 10000) && (ssh_pubkey_path || ssh_privkey_path)) { + ERR_MSG_CLEANUP("Public or private key specified, when connecting to the password endpoint.\n"); + } else if (!unix_socket_path && !port) { + ERR_MSG_CLEANUP("Neither UNIX socket or SSH specified.\n"); + } + /* connect to the server using the specified transport protocol */ - switch (connection_type) { - case UNIX: + if (unix_socket_path) { + /* it's UNIX socket */ session = nc_connect_unix(unix_socket_path, NULL); - break; + } else { + /* it must be SSH, so set the client SSH username to always be used when connecting to the server */ + if (nc_client_ssh_set_username(SSH_USERNAME)) { + ERR_MSG_CLEANUP("Couldn't set the SSH username\n"); + } - case SSH: - session = nc_connect_ssh(SSH_ADDRESS, SSH_PORT, NULL); - break; + if (ssh_pubkey_path && ssh_privkey_path) { + /* set the client's SSH keypair to be used for authentication if necessary */ + if (nc_client_ssh_add_keypair(ssh_pubkey_path, ssh_privkey_path)) { + ERR_MSG_CLEANUP("Couldn't set client's SSH keypair.\n"); + } + } - case NONE: - ERR_MSG_CLEANUP("Expected connection type (either SSH or UNIX)!\n"); + /* try to connect via SSH */ + session = nc_connect_ssh(SSH_ADDRESS, port, NULL); } if (!session) { ERR_MSG_CLEANUP("Couldn't connect to the server\n"); diff --git a/examples/config.json b/examples/config.json new file mode 100644 index 00000000..510b1238 --- /dev/null +++ b/examples/config.json @@ -0,0 +1,97 @@ +{ + "ietf-netconf-server:netconf-server": { + "listen": { + "idle-timeout": 10, + "endpoint": [ + { + "name": "ssh-password-auth-endpt", + "ssh": { + "tcp-server-parameters": { + "local-address": "127.0.0.1", + "local-port": 10000 + }, + "ssh-server-parameters": { + "server-identity": { + "host-key": [ + { + "name": "key", + "public-key": { + "inline-definition": { + "public-key-format": "ietf-crypto-types:ssh-public-key-format", + "public-key": "AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBDRIB2eNSRWU+HNWRUGKr76ghCLg8RaMlUCps9lBjnc6ggaJl2Q+TOLn8se2wAdK3lYBMz3dcqR+SlU7eB8wJAc=", + "private-key-format": "ietf-crypto-types:ec-private-key-format", + "cleartext-private-key": "MHcCAQEEICQ2fr9Jt2xluom0YQQ7HseE8YTo5reZRVcQENKUWOrooAoGCCqGSM49AwEHoUQDQgAENEgHZ41JFZT4c1ZFQYqvvqCEIuDxFoyVQKmz2UGOdzqCBomXZD5M4ufyx7bAB0reVgEzPd1ypH5KVTt4HzAkBw==" + } + } + } + ] + }, + "client-authentication": { + "users": { + "user": [ + { + "name": "admin", + "password": "$0$admin" + } + ] + } + } + } + } + }, + { + "name": "ssh-pubkey-auth-endpt", + "ssh": { + "tcp-server-parameters": { + "local-address": "127.0.0.1", + "local-port": 10001 + }, + "ssh-server-parameters": { + "server-identity": { + "host-key": [ + { + "name": "key", + "public-key": { + "inline-definition": { + "public-key-format": "ietf-crypto-types:ssh-public-key-format", + "public-key": "AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBDRIB2eNSRWU+HNWRUGKr76ghCLg8RaMlUCps9lBjnc6ggaJl2Q+TOLn8se2wAdK3lYBMz3dcqR+SlU7eB8wJAc=", + "private-key-format": "ietf-crypto-types:ec-private-key-format", + "cleartext-private-key": "MHcCAQEEICQ2fr9Jt2xluom0YQQ7HseE8YTo5reZRVcQENKUWOrooAoGCCqGSM49AwEHoUQDQgAENEgHZ41JFZT4c1ZFQYqvvqCEIuDxFoyVQKmz2UGOdzqCBomXZD5M4ufyx7bAB0reVgEzPd1ypH5KVTt4HzAkBw==" + } + } + } + ] + }, + "client-authentication": { + "users": { + "user": [ + { + "name": "admin", + "public-keys": { + "inline-definition": { + "public-key": [ + { + "name": "admin_key.pub", + "public-key-format": "ietf-crypto-types:ssh-public-key-format", + "public-key": "AAAAC3NzaC1lZDI1NTE5AAAAIOr46rptg6BsWhO1JMomuh3cuCYmeuO6JfOUPs/YO35w" + } + ] + } + } + } + ] + } + } + } + } + }, + { + "name": "unix-socket-endpt", + "libnetconf2-netconf-server:unix-socket": { + "path": "/tmp/.ln2-unix-socket" + } + } + ] + } + } +} diff --git a/examples/example.h.in b/examples/example.h.in index c003565f..1a97f170 100644 --- a/examples/example.h.in +++ b/examples/example.h.in @@ -21,15 +21,14 @@ /* directory with library YANG modules */ #define MODULES_DIR "@CMAKE_SOURCE_DIR@/modules" +/* directory with examples source code and this header */ +#define EXAMPLES_DIR "@CMAKE_SOURCE_DIR@/examples" + /* SSH listening IP address */ #define SSH_ADDRESS "127.0.0.1" -/* SSH listening port */ -#define SSH_PORT 830 - /* SSH 'password' authentication exptected username and password */ #define SSH_USERNAME "admin" -#define SSH_PASSWORD "admin" /* time in microseconds to sleep for if there are no new RPCs and no new sessions */ #define BACKOFF_TIMEOUT_USECS 100 @@ -39,11 +38,4 @@ fprintf(stderr, "%s", msg); \ goto cleanup -/* supported server transport protocol */ -enum server_transport { - NONE = 0, - UNIX, - SSH -}; - #endif diff --git a/examples/server.c b/examples/server.c index 2a55c771..713aecb7 100644 --- a/examples/server.c +++ b/examples/server.c @@ -30,10 +30,12 @@ #include "log.h" #include "messages_server.h" #include "netconf.h" +#include "server_config.h" #include "session_server.h" #include "session_server_ch.h" volatile int exit_application = 0; +struct lyd_node *tree; static void sigint_handler(int signum) @@ -52,6 +54,7 @@ get_rpc(struct lyd_node *rpc, struct nc_session *session) struct lyd_node *filter, *err; struct lyd_meta *m, *type = NULL, *select = NULL; struct ly_set *set = NULL; + LY_ERR ret; ctx = nc_session_get_ctx(session); @@ -62,7 +65,8 @@ get_rpc(struct lyd_node *rpc, struct nc_session *session) } /* search for the optional filter in the RPC */ - if (lyd_find_path(rpc, "filter", 0, &filter)) { + ret = lyd_find_path(rpc, "filter", 0, &filter); + if (ret && (ret != LY_ENOTFOUND)) { err = nc_err(ctx, NC_ERR_OP_FAILED, NC_ERR_TYPE_APP); goto error; } @@ -199,106 +203,38 @@ help_print() } static int -hostkey_callback(const char *name, void *user_data, char **privkey_path, char **privkey_data, NC_SSH_KEY_TYPE *privkey_type) +init(struct ly_ctx **context, struct nc_pollsession **ps) { - /* return only known hostkey */ - if (strcmp(name, "server_hostkey")) { - return 1; - } - - /* the hostkey is in a file */ - *privkey_path = strdup(user_data); - *privkey_data = NULL; - *privkey_type = NC_SSH_KEY_UNKNOWN; - - return 0; -} - -static int -password_callback(const struct nc_session *session, const char *password, void *user_data) -{ - (void) user_data; - const char *username; - - /* get username from the NETCONF session */ - username = nc_session_get_username(session); - - /* compare it with the defined username and password */ - if (strcmp(username, SSH_USERNAME) || strcmp(password, SSH_PASSWORD)) { - return 1; - } - - return 0; -} - -static int -init(struct ly_ctx **context, struct nc_pollsession **ps, const char *path, NC_TRANSPORT_IMPL server_type) -{ - struct lys_module *module; int rc = 0; - const char *features[] = {"*", NULL}; + struct lyd_node *config = NULL; - /* initialize the server */ - if (nc_server_init()) { - ERR_MSG_CLEANUP("Error occurred while initializing the server.\n"); + /* create a libyang context that will determine which YANG modules will be supported by the server */ + rc = ly_ctx_new(MODULES_DIR, 0, context); + if (rc) { + ERR_MSG_CLEANUP("Error while creating a new context.\n"); } - if (server_type == NC_TI_UNIX) { - /* add a new UNIX socket endpoint with an arbitrary name main_unix */ - if (nc_server_add_endpt("main_unix", NC_TI_UNIX)) { - ERR_MSG_CLEANUP("Couldn't add end point.\n"); - } - - /* set endpoint listening address to the path from the parameter */ - if (nc_server_endpt_set_address("main_unix", path)) { - ERR_MSG_CLEANUP("Couldn't set address of end point.\n"); - } - } else { - /* add a new SSH endpoint with an arbitrary name main_ssh */ - if (nc_server_add_endpt("main_ssh", NC_TI_LIBSSH)) { - ERR_MSG_CLEANUP("Couldn't add end point.\n"); - } - - /* set generic hostkey callback which will be used for retrieving all the hostkeys */ - nc_server_ssh_set_hostkey_clb(hostkey_callback, (void *)path, NULL); - - /* set 'password' SSH authentication callback */ - nc_server_ssh_set_passwd_auth_clb(password_callback, NULL, NULL); - - /* add a new hostkey called server_hostkey, whose data will be retrieved by the hostkey callback */ - nc_server_ssh_endpt_add_hostkey("main_ssh", "server_hostkey", -1); - - /* set endpoint listening address to the defined IP address */ - if (nc_server_endpt_set_address("main_ssh", SSH_ADDRESS)) { - ERR_MSG_CLEANUP("Couldn't set address of end point.\n"); - } - - /* set endpoint listening port to the defined one */ - if (nc_server_endpt_set_port("main_ssh", SSH_PORT)) { - ERR_MSG_CLEANUP("Couldn't set port of end point.\n"); - } - - /* allow only 'password' SSH authentication method for the endpoint */ - if (nc_server_ssh_endpt_set_auth_methods("main_ssh", NC_SSH_AUTH_PASSWORD)) { - ERR_MSG_CLEANUP("Couldn't set authentication methods of end point.\n"); - } + /* implement the base NETCONF modules */ + rc = nc_server_init_ctx(context); + if (rc) { + ERR_MSG_CLEANUP("Error while initializing context.\n"); } - /* create a libyang context that will determine which YANG modules will be supported by the server */ - if (ly_ctx_new(MODULES_DIR, 0, context)) { - ERR_MSG_CLEANUP("Couldn't create new libyang context.\n"); + /* load all required modules for configuration, so the configuration of the server can be done */ + rc = nc_server_config_load_modules(context); + if (rc) { + ERR_MSG_CLEANUP("Error loading modules required for configuration of the server.\n"); } - /* support and load the base NETCONF ietf-netconf module with all its features enabled */ - module = ly_ctx_load_module(*context, "ietf-netconf", NULL, features); - if (!module) { - ERR_MSG_CLEANUP("Couldn't load ietf-netconf module.\n"); + /* apply the YANG data stored in config.json */ + rc = nc_server_config_setup_path(*context, EXAMPLES_DIR "/config.json"); + if (rc) { + ERR_MSG_CLEANUP("Application of configuration data failed.\n"); } - /* support get-schema RPC for the server to be able to send YANG modules */ - module = ly_ctx_load_module(*context, "ietf-netconf-monitoring", NULL, features); - if (!module) { - ERR_MSG_CLEANUP("Couldn't load ietf-netconf-monitoring module.\n"); + /* initialize the server */ + if (nc_server_init()) { + ERR_MSG_CLEANUP("Error occurred while initializing the server.\n"); } /* create a new poll session structure, which is used for polling RPCs sent by clients */ @@ -314,6 +250,7 @@ init(struct ly_ctx **context, struct nc_pollsession **ps, const char *path, NC_T signal(SIGINT, sigint_handler); cleanup: + lyd_free_all(config); return rc; } @@ -324,45 +261,21 @@ main(int argc, char **argv) struct ly_ctx *context = NULL; struct nc_session *session, *new_session; struct nc_pollsession *ps = NULL; - const char *unix_socket_path = NULL, *ssh_public_key_path = NULL; struct option options[] = { {"help", no_argument, NULL, 'h'}, - {"unix", required_argument, NULL, 'u'}, - {"ssh", required_argument, NULL, 's'}, {"debug", no_argument, NULL, 'd'}, {NULL, 0, NULL, 0} }; - if (argc == 1) { - help_print(); - goto cleanup; - } - opterr = 0; - while ((opt = getopt_long(argc, argv, "hu:s:d", options, NULL)) != -1) { + while ((opt = getopt_long(argc, argv, "hd", options, NULL)) != -1) { switch (opt) { case 'h': help_print(); goto cleanup; - case 'u': - unix_socket_path = optarg; - if (init(&context, &ps, unix_socket_path, NC_TI_UNIX)) { - ERR_MSG_CLEANUP("Failed to initialize a UNIX socket\n"); - } - printf("Using UNIX socket!\n"); - break; - - case 's': - ssh_public_key_path = optarg; - if (init(&context, &ps, ssh_public_key_path, NC_TI_LIBSSH)) { - ERR_MSG_CLEANUP("Failed to initialize a SSH server\n"); - } - printf("Using SSH!\n"); - break; - case 'd': nc_verbosity(NC_VERB_DEBUG); break; @@ -372,6 +285,14 @@ main(int argc, char **argv) } } + /* initialize the server */ + r = init(&context, &ps); + if (r) { + ERR_MSG_CLEANUP("Initializing the server failed."); + } + + printf("Listening for new connections!\n"); + while (!exit_application) { no_new_sessions = 0; @@ -440,6 +361,7 @@ main(int argc, char **argv) } nc_ps_free(ps); nc_server_destroy(); + lyd_free_all(tree); ly_ctx_destroy(context); return rc; } diff --git a/libnetconf2.pc.in b/libnetconf2.pc.in index 66da4697..10926a59 100644 --- a/libnetconf2.pc.in +++ b/libnetconf2.pc.in @@ -8,5 +8,5 @@ Version: @LIBNETCONF2_VERSION@ Libs: -L${libdir} -lnetconf2 Cflags: -I${includedir} -LNC2_MAX_THREAD_COUNT=@MAX_PSPOLL_THREAD_COUNT@ -LNC2_SCHEMAS_DIR=@SCHEMAS_DIR@ +LN2_MAX_THREAD_COUNT=@MAX_PSPOLL_THREAD_COUNT@ +LN2_SCHEMAS_DIR=@YANG_MODULE_DIR@ diff --git a/modules/iana-crypt-hash@2014-04-04.yang b/modules/iana-crypt-hash@2014-04-04.yang new file mode 100644 index 00000000..eaf6258c --- /dev/null +++ b/modules/iana-crypt-hash@2014-04-04.yang @@ -0,0 +1,124 @@ +module iana-crypt-hash { + namespace "urn:ietf:params:xml:ns:yang:iana-crypt-hash"; + prefix ianach; + + organization "IANA"; + contact + " Internet Assigned Numbers Authority + + Postal: ICANN + 4676 Admiralty Way, Suite 330 + Marina del Rey, CA 90292 + + Tel: +1 310 823 9358 + E-Mail: iana&iana.org"; + description + "This YANG module defines a typedef for storing passwords + using a hash function, and features to indicate which hash + functions are supported by an implementation. + + The latest revision of this YANG module can be obtained from + the IANA web site. + + Requests for new values should be made to IANA via + email (iana&iana.org). + + Copyright (c) 2014 IETF Trust and the persons identified as + authors of the code. All rights reserved. + + Redistribution and use in source and binary forms, with or + without modification, is permitted pursuant to, and subject + to the license terms contained in, the Simplified BSD License + set forth in Section 4.c of the IETF Trust's Legal Provisions + Relating to IETF Documents + (http://trustee.ietf.org/license-info). + + The initial version of this YANG module is part of RFC XXXX; + see the RFC itself for full legal notices."; + // RFC Ed.: replace XXXX with actual RFC number and remove this + // note. + + // RFC Ed.: update the date below with the date of RFC publication + // and remove this note. + revision 2014-04-04 { + description + "Initial revision."; + reference + "RFC XXXX: A YANG Data Model for System Management"; + } + + typedef crypt-hash { + type string { + pattern + '$0$.*' + + '|$1$[a-zA-Z0-9./]{1,8}$[a-zA-Z0-9./]{22}' + + '|$5$(rounds=\d+$)?[a-zA-Z0-9./]{1,16}$[a-zA-Z0-9./]{43}' + + '|$6$(rounds=\d+$)?[a-zA-Z0-9./]{1,16}$[a-zA-Z0-9./]{86}'; + } + description + "The crypt-hash type is used to store passwords using + a hash function. The algorithms for applying the hash + function and encoding the result are implemented in + various UNIX systems as the function crypt(3). + + A value of this type matches one of the forms: + + $0$ + $$$ + $$$$ + + The '$0$' prefix signals that the value is clear text. When + such a value is received by the server, a hash value is + calculated, and the string '$$$' or + $$$$ is prepended to the result. This + value is stored in the configuration data store. + + If a value starting with '$$', where is not '0', is + received, the server knows that the value already represents a + hashed value, and stores it as is in the data store. + + When a server needs to verify a password given by a user, it + finds the stored password hash string for that user, extracts + the salt, and calculates the hash with the salt and given + password as input. If the calculated hash value is the same + as the stored value, the password given by the client is + accepted. + + This type defines the following hash functions: + + id | hash function | feature + ---+---------------+------------------- + 1 | MD5 | crypt-hash-md5 + 5 | SHA-256 | crypt-hash-sha-256 + 6 | SHA-512 | crypt-hash-sha-512 + + The server indicates support for the different hash functions + by advertising the corresponding feature."; + reference + "IEEE Std 1003.1-2008 - crypt() function + RFC 1321: The MD5 Message-Digest Algorithm + FIPS.180-3.2008: Secure Hash Standard"; + } + + feature crypt-hash-md5 { + description + "Indicates that the device supports the MD5 + hash function in 'crypt-hash' values"; + reference "RFC 1321: The MD5 Message-Digest Algorithm"; + } + + feature crypt-hash-sha-256 { + description + "Indicates that the device supports the SHA-256 + hash function in 'crypt-hash' values"; + reference "FIPS.180-3.2008: Secure Hash Standard"; + } + + feature crypt-hash-sha-512 { + description + "Indicates that the device supports the SHA-512 + hash function in 'crypt-hash' values"; + reference "FIPS.180-3.2008: Secure Hash Standard"; + } + +} diff --git a/modules/iana-ssh-encryption-algs@2022-06-16.yang b/modules/iana-ssh-encryption-algs@2022-06-16.yang new file mode 100644 index 00000000..516396b1 --- /dev/null +++ b/modules/iana-ssh-encryption-algs@2022-06-16.yang @@ -0,0 +1,389 @@ +module iana-ssh-encryption-algs { + yang-version 1.1; + namespace "urn:ietf:params:xml:ns:yang:iana-ssh-encryption-algs"; + prefix sshea; + + organization + "Internet Assigned Numbers Authority (IANA)"; + + contact + "Postal: ICANN + 12025 Waterfront Drive, Suite 300 + Los Angeles, CA 90094-2536 + United States of America + Tel: +1 310 301 5800 + Email: iana@iana.org"; + + description + "This module defines identities for the encryption algorithms + defined in the 'Encryption Algorithm Names' sub-registry of the + 'Secure Shell (SSH) Protocol Parameters' registry maintained + by IANA. + + Copyright (c) 2022 IETF Trust and the persons identified as + authors of the code. All rights reserved. + + Redistribution and use in source and binary forms, with + or without modification, is permitted pursuant to, and + subject to the license terms contained in, the Revised + BSD License set forth in Section 4.c of the IETF Trust's + Legal Provisions Relating to IETF Documents + (https://trustee.ietf.org/license-info). + + The initial version of this YANG module is part of RFC EEEE + (https://www.rfc-editor.org/info/rfcEEEE); see the RFC + itself for full legal notices."; + + revision 2022-06-16 { + description + "Reflects contents of the encryption algorithms registry + on June 16, 2022."; + reference + "RFC EEEE: YANG Groupings for SSH Clients and SSH Servers"; + } + + // Typedefs + + typedef encryption-algorithm-ref { + type identityref { + base "encryption-alg-base"; + } + description + "A reference to a SSH encryption algorithm identifier."; + } + + // Identities + + identity encryption-alg-base { + description + "Base identity used to identify encryption algorithms."; + } + + identity triple-des-cbc { // YANG IDs cannot begin with a number + base encryption-alg-base; + description + "3DES-CBC"; + reference + "RFC 4253: + The Secure Shell (SSH) Transport Layer Protocol"; + } + + identity blowfish-cbc { + base encryption-alg-base; + description + "BLOWFISH-CBC"; + reference + "RFC 4253: + The Secure Shell (SSH) Transport Layer Protocol"; + } + + identity twofish256-cbc { + base encryption-alg-base; + description + "TWOFISH256-CBC"; + reference + "RFC 4253: + The Secure Shell (SSH) Transport Layer Protocol"; + } + + identity twofish-cbc { + base encryption-alg-base; + description + "TWOFISH-CBC"; + reference + "RFC 4253: + The Secure Shell (SSH) Transport Layer Protocol"; + } + + identity twofish192-cbc { + base encryption-alg-base; + description + "TWOFISH192-CBC"; + reference + "RFC 4253: + The Secure Shell (SSH) Transport Layer Protocol"; + } + identity twofish128-cbc { + base encryption-alg-base; + description + "TWOFISH128-CBC"; + reference + "RFC 4253: + The Secure Shell (SSH) Transport Layer Protocol"; + } + + identity aes256-cbc { + base encryption-alg-base; + description + "AES256-CBC"; + reference + "RFC 4253: + The Secure Shell (SSH) Transport Layer Protocol"; + } + + identity aes192-cbc { + base encryption-alg-base; + description + "AES192-CBC"; + reference + "RFC 4253: + The Secure Shell (SSH) Transport Layer Protocol"; + } + + identity aes128-cbc { + base encryption-alg-base; + status deprecated; + description + "AES128-CBC"; + reference + "RFC 4253: + The Secure Shell (SSH) Transport Layer Protocol"; + } + + identity serpent256-cbc { + base encryption-alg-base; + description + "SERPENT256-CBC"; + reference + "RFC 4253: + The Secure Shell (SSH) Transport Layer Protocol"; + } + + identity serpent192-cbc { + base encryption-alg-base; + description + "SERPENT192-CBC"; + reference + "RFC 4253: + The Secure Shell (SSH) Transport Layer Protocol"; + } + + identity serpent128-cbc { + base encryption-alg-base; + description + "SERPENT128-CBC"; + reference + "RFC 4253: + The Secure Shell (SSH) Transport Layer Protocol"; + } + + identity arcfour { + base encryption-alg-base; + status obsolete; + description + "ARCFOUR"; + reference + "RFC 8758: + Deprecating RC4 in Secure Shell (SSH)"; + } + + identity idea-cbc { + base encryption-alg-base; + description + "IDEA-CBC"; + reference + "RFC 4253: + The Secure Shell (SSH) Transport Layer Protocol"; + } + + identity cast128-cbc { + base encryption-alg-base; + description + "CAST128-CBC"; + reference + "RFC 4253: + The Secure Shell (SSH) Transport Layer Protocol"; + } + + identity none { + base encryption-alg-base; + description + "NONE"; + reference + "RFC 4253: + The Secure Shell (SSH) Transport Layer Protocol"; + } + + identity des-cbc { + base encryption-alg-base; + status obsolete; + description + "DES-CBC"; + reference + "FIPS 46-3: + Data Encryption Standard (DES)"; + } + + identity arcfour128 { + base encryption-alg-base; + status obsolete; + description + "ARCFOUR128"; + reference + "RFC 8758: + Deprecating RC4 in Secure Shell (SSH)"; + } + + identity arcfour256 { + base encryption-alg-base; + status obsolete; + description + "ARCFOUR256"; + reference + "RFC 8758: + Deprecating RC4 in Secure Shell (SSH)"; + } + + identity aes128-ctr { + base encryption-alg-base; + status deprecated; + description + "AES128-CTR"; + reference + "RFC 4344: + The Secure Shell (SSH) Transport Layer Encryption Modes"; + } + + identity aes192-ctr { + base encryption-alg-base; + description + "AES192-CTR"; + reference + "RFC 4344: + The Secure Shell (SSH) Transport Layer Encryption Modes"; + } + + identity aes256-ctr { + base encryption-alg-base; + description + "AES256-CTR"; + reference + "RFC 4344: + The Secure Shell (SSH) Transport Layer Encryption Modes"; + } + + identity triple-des-ctr { // YANG IDs cannot begin with a number + base encryption-alg-base; + description + "3DES-CTR"; + reference + "RFC 4344: + The Secure Shell (SSH) Transport Layer Encryption Modes"; + } + + identity blowfish-ctr { + base encryption-alg-base; + description + "BLOWFISH-CTR"; + reference + "RFC 4344: + The Secure Shell (SSH) Transport Layer Encryption Modes"; + } + + identity twofish128-ctr { + base encryption-alg-base; + description + "TWOFISH128-CTR"; + reference + "RFC 4344: + The Secure Shell (SSH) Transport Layer Encryption Modes"; + } + + identity twofish192-ctr { + base encryption-alg-base; + description + "TWOFISH192-CTR"; + reference + "RFC 4344: + The Secure Shell (SSH) Transport Layer Encryption Modes"; + } + + identity twofish256-ctr { + base encryption-alg-base; + description + "TWOFISH256-CTR"; + reference + "RFC 4344: + The Secure Shell (SSH) Transport Layer Encryption Modes"; + } + + identity serpent128-ctr { + base encryption-alg-base; + description + "SERPENT128-CTR"; + reference + "RFC 4344: + The Secure Shell (SSH) Transport Layer Encryption Modes"; + } + + identity serpent192-ctr { + base encryption-alg-base; + description + "SERPENT192-CTR"; + reference + "RFC 4344: + The Secure Shell (SSH) Transport Layer Encryption Modes"; + } + + identity serpent256-ctr { + base encryption-alg-base; + description + "SERPENT256-CTR"; + reference + "RFC 4344: + The Secure Shell (SSH) Transport Layer Encryption Modes"; + } + + identity idea-ctr { + base encryption-alg-base; + description + "IDEA-CTR"; + reference + "RFC 4344: + The Secure Shell (SSH) Transport Layer Encryption Modes"; + } + + identity cast128-ctr { + base encryption-alg-base; + description + "CAST128-CTR"; + reference + "RFC 4344: + The Secure Shell (SSH) Transport Layer Encryption Modes"; + } + + identity aead-aes-128-gcm { + base encryption-alg-base; + description + "AEAD_AES_128_GCM"; + reference + "RFC 5647: + AES Galois Counter Mode for the + Secure Shell Transport Layer Protocol"; + } + + identity aead-aes-256-gcm { + base encryption-alg-base; + description + "AEAD_AES_256_GCM"; + reference + "RFC 5647: + AES Galois Counter Mode for the + Secure Shell Transport Layer Protocol"; + } + + // Protocol-accessible Nodes + + container supported-algorithms { + config false; + description + "A container for a list of encryption algorithms + supported by the server."; + leaf-list supported-algorithm { + type encryption-algorithm-ref; + description + "A encryption algorithm supported by the server."; + } + } + +} diff --git a/modules/iana-ssh-key-exchange-algs@2022-06-16.yang b/modules/iana-ssh-key-exchange-algs@2022-06-16.yang new file mode 100644 index 00000000..73b1895a --- /dev/null +++ b/modules/iana-ssh-key-exchange-algs@2022-06-16.yang @@ -0,0 +1,2217 @@ +module iana-ssh-key-exchange-algs { + yang-version 1.1; + namespace "urn:ietf:params:xml:ns:yang:iana-ssh-key-exchange-algs"; + prefix sshkea; + + organization + "Internet Assigned Numbers Authority (IANA)"; + + contact + "Postal: ICANN + 12025 Waterfront Drive, Suite 300 + Los Angeles, CA 90094-2536 + United States of America + Tel: +1 310 301 5800 + Email: iana@iana.org"; + + description + "This module defines identities for the key exchange algorithms + defined in the 'Key Exchange Method Names' sub-registry of the + 'Secure Shell (SSH) Protocol Parameters' registry maintained + by IANA. + + Copyright (c) 2022 IETF Trust and the persons identified + as authors of the code. All rights reserved. + + Redistribution and use in source and binary forms, with + or without modification, is permitted pursuant to, and + subject to the license terms contained in, the Revised + BSD License set forth in Section 4.c of the IETF Trust's + Legal Provisions Relating to IETF Documents + (https://trustee.ietf.org/license-info). + + The initial version of this YANG module is part of RFC EEEE + (https://www.rfc-editor.org/info/rfcEEEE); see the RFC + itself for full legal notices."; + + revision 2022-06-16 { + description + "Reflects contents of the key exchange algorithms registry + on June 16, 2022."; + reference + "RFC EEEE: YANG Groupings for SSH Clients and SSH Servers"; + } + + // Typedefs + + typedef key-exchange-algorithm-ref { + type identityref { + base "key-exchange-alg-base"; + } + description + "A reference to a SSH key exchange algorithm identifier."; + } + + // Identities + + identity key-exchange-alg-base { + description + "Base identity used to identify key exchange algorithms."; + } + + identity diffie-hellman-group-exchange-sha1 { + base key-exchange-alg-base; + status deprecated; + description + "DIFFIE-HELLMAN-GROUP-EXCHANGE-SHA1"; + reference + "RFC 4419: + Diffie-Hellman Group Exchange for the + Secure Shell (SSH) Transport Layer Protocol"; + } + + identity diffie-hellman-group-exchange-sha256 { + base key-exchange-alg-base; + description + "DIFFIE-HELLMAN-GROUP-EXCHANGE-SHA256"; + reference + "RFC 4419: + Diffie-Hellman Group Exchange for the + Secure Shell (SSH) Transport Layer Protocol"; + } + + identity diffie-hellman-group1-sha1 { + base key-exchange-alg-base; + status deprecated; + description + "DIFFIE-HELLMAN-GROUP1-SHA1"; + reference + "RFC 4253: + The Secure Shell (SSH) Transport Layer Protocol"; + } + + identity diffie-hellman-group14-sha1 { + base key-exchange-alg-base; + status deprecated; + description + "DIFFIE-HELLMAN-GROUP14-SHA1"; + reference + "RFC 4253: + The Secure Shell (SSH) Transport Layer Protocol"; + } + + identity diffie-hellman-group14-sha256 { + base key-exchange-alg-base; + status deprecated; + description + "DIFFIE-HELLMAN-GROUP14-SHA256"; + reference + "RFC 8268: + More Modular Exponentiation (MODP) Diffie-Hellman (DH) + Key Exchange (KEX) Groups for Secure Shell (SSH)"; + } + + identity diffie-hellman-group15-sha512 { + base key-exchange-alg-base; + description + "DIFFIE-HELLMAN-GROUP15-SHA512"; + reference + "RFC 8268: + More Modular Exponentiation (MODP) Diffie-Hellman (DH) + Key Exchange (KEX) Groups for Secure Shell (SSH)"; + } + + identity diffie-hellman-group16-sha512 { + base key-exchange-alg-base; + description + "DIFFIE-HELLMAN-GROUP16-SHA512"; + reference + "RFC 8268: + More Modular Exponentiation (MODP) Diffie-Hellman (DH) + Key Exchange (KEX) Groups for Secure Shell (SSH)"; + } + + identity diffie-hellman-group17-sha512 { + base key-exchange-alg-base; + description + "DIFFIE-HELLMAN-GROUP17-SHA512"; + reference + "RFC 8268: + More Modular Exponentiation (MODP) Diffie-Hellman (DH) + Key Exchange (KEX) Groups for Secure Shell (SSH)"; + } + + identity diffie-hellman-group18-sha512 { + base key-exchange-alg-base; + description + "DIFFIE-HELLMAN-GROUP18-SHA512"; + reference + "RFC 8268: + More Modular Exponentiation (MODP) Diffie-Hellman (DH) + Key Exchange (KEX) Groups for Secure Shell (SSH)"; + } + + identity ecdh-sha2-nistp256 { + base key-exchange-alg-base; + status deprecated; + description + "ECDH-SHA2-NISTP256 (secp256r1)"; + reference + "RFC 5656: + Elliptic Curve Algorithm Integration in the + Secure Shell Transport Layer"; + } + + identity ecdh-sha2-nistp384 { + base key-exchange-alg-base; + description + "ECDH-SHA2-NISTP384 (secp384r1)"; + reference + "RFC 5656: + Elliptic Curve Algorithm Integration in the + Secure Shell Transport Layer"; + } + + identity ecdh-sha2-nistp521 { + base key-exchange-alg-base; + description + "ECDH-SHA2-NISTP521 (secp521r1)"; + reference + "RFC 5656: + Elliptic Curve Algorithm Integration in the + Secure Shell Transport Layer"; + } + + identity ecdh-sha2-1.3.132.0.1 { + base key-exchange-alg-base; + description + "ECDH-SHA2-1.3.132.0.1 (nistk163, sect163k1)"; + reference + "RFC 5656: + Elliptic Curve Algorithm Integration in the + Secure Shell Transport Layer"; + } + + identity ecdh-sha2-1.2.840.10045.3.1.1 { + base key-exchange-alg-base; + description + "ECDH-SHA2-1.2.840.10045.3.1.1 (nistp192, secp192r1)"; + reference + "RFC 5656: + Elliptic Curve Algorithm Integration in the + Secure Shell Transport Layer"; + } + + identity ecdh-sha2-1.3.132.0.33 { + base key-exchange-alg-base; + description + "ECDH-SHA2-1.3.132.0.33 (nistp224, secp224r1)"; + reference + "RFC 5656: + Elliptic Curve Algorithm Integration in the + Secure Shell Transport Layer"; + } + + identity ecdh-sha2-1.3.132.0.26 { + base key-exchange-alg-base; + description + "ECDH-SHA2-1.3.132.0.26 (nistk233, sect233k1)"; + reference + "RFC 5656: + Elliptic Curve Algorithm Integration in the + Secure Shell Transport Layer"; + } + + identity ecdh-sha2-1.3.132.0.27 { + base key-exchange-alg-base; + description + "ECDH-SHA2-1.3.132.0.27 (nistb233, sect233r1)"; + reference + "RFC 5656: + Elliptic Curve Algorithm Integration in the + Secure Shell Transport Layer"; + } + + identity ecdh-sha2-1.3.132.0.16 { + base key-exchange-alg-base; + description + "ECDH-SHA2-1.3.132.0.16 (nistk283, sect283k1)"; + reference + "RFC 5656: + Elliptic Curve Algorithm Integration in the + Secure Shell Transport Layer"; + } + + identity ecdh-sha2-1.3.132.0.36 { + base key-exchange-alg-base; + description + "ECDH-SHA2-1.3.132.0.36 (nistk409, sect409k1)"; + reference + "RFC 5656: + Elliptic Curve Algorithm Integration in the + Secure Shell Transport Layer"; + } + + identity ecdh-sha2-1.3.132.0.37 { + base key-exchange-alg-base; + description + "ECDH-SHA2-1.3.132.0.37 (nistb409, sect409r1)"; + reference + "RFC 5656: + Elliptic Curve Algorithm Integration in the + Secure Shell Transport Layer"; + } + + identity ecdh-sha2-1.3.132.0.38 { + base key-exchange-alg-base; + description + "ECDH-SHA2-1.3.132.0.38 (nistt571, sect571k1)"; + reference + "RFC 5656: + Elliptic Curve Algorithm Integration in the + Secure Shell Transport Layer"; + } + + identity ecmqv-sha2 { + base key-exchange-alg-base; + description + "ECMQV-SHA2"; + reference + "RFC 5656: + Elliptic Curve Algorithm Integration in the + Secure Shell Transport Layer"; + } + + identity gss-group1-sha1-nistp256 { + base key-exchange-alg-base; + status deprecated; + description + "GSS-GROUP1-SHA1-NISTP256 (secp256r1)"; + reference + "RFC 8732: + Generic Security Service Application Program Interface + (GSS-API) Key Exchange with SHA-2"; + } + identity gss-group1-sha1-nistp384 { + base key-exchange-alg-base; + status deprecated; + description + "GSS-GROUP1-SHA1-NISTP384 (secp384r1)"; + reference + "RFC 8732: + Generic Security Service Application Program Interface + (GSS-API) Key Exchange with SHA-2"; + } + + identity gss-group1-sha1-nistp521 { + base key-exchange-alg-base; + status deprecated; + description + "GSS-GROUP1-SHA1-NISTP521 (secp521r1)"; + reference + "RFC 8732: + Generic Security Service Application Program Interface + (GSS-API) Key Exchange with SHA-2"; + } + + identity gss-group1-sha1-1.3.132.0.1 { + base key-exchange-alg-base; + status deprecated; + description + "GSS-GROUP1-SHA1-1.3.132.0.1 (nistk163, sect163k1)"; + reference + "RFC 8732: + Generic Security Service Application Program Interface + (GSS-API) Key Exchange with SHA-2"; + } + + identity gss-group1-sha1-1.2.840.10045.3.1.1 { + base key-exchange-alg-base; + status deprecated; + description + "GSS-GROUP1-SHA1-1.2.840.10045.3.1.1 (nistp192, secp192r1)"; + reference + "RFC 8732: + Generic Security Service Application Program Interface + (GSS-API) Key Exchange with SHA-2"; + } + + identity gss-group1-sha1-1.3.132.0.33 { + base key-exchange-alg-base; + status deprecated; + description + "GSS-GROUP1-SHA1-1.3.132.0.33 (nistp224, secp224r1)"; + reference + "RFC 8732: + Generic Security Service Application Program Interface + (GSS-API) Key Exchange with SHA-2"; + } + + identity gss-group1-sha1-1.3.132.0.26 { + base key-exchange-alg-base; + status deprecated; + description + "GSS-GROUP1-SHA1-1.3.132.0.26 (nistk233, sect233k1)"; + reference + "RFC 8732: + Generic Security Service Application Program Interface + (GSS-API) Key Exchange with SHA-2"; + } + + identity gss-group1-sha1-1.3.132.0.27 { + base key-exchange-alg-base; + status deprecated; + description + "GSS-GROUP1-SHA1-1.3.132.0.27 (nistb233, sect233r1)"; + reference + "RFC 8732: + Generic Security Service Application Program Interface + (GSS-API) Key Exchange with SHA-2"; + } + + identity gss-group1-sha1-1.3.132.0.16 { + base key-exchange-alg-base; + status deprecated; + description + "GSS-GROUP1-SHA1-1.3.132.0.16 (nistk283, sect283k1)"; + reference + "RFC 8732: + Generic Security Service Application Program Interface + (GSS-API) Key Exchange with SHA-2"; + } + + identity gss-group1-sha1-1.3.132.0.36 { + base key-exchange-alg-base; + status deprecated; + description + "GSS-GROUP1-SHA1-1.3.132.0.36 (nistk409, sect409k1)"; + reference + "RFC 8732: + Generic Security Service Application Program Interface + (GSS-API) Key Exchange with SHA-2"; + } + + identity gss-group1-sha1-1.3.132.0.37 { + base key-exchange-alg-base; + status deprecated; + description + "GSS-GROUP1-SHA1-1.3.132.0.37 (nistb409, sect409r1)"; + reference + "RFC 8732: + Generic Security Service Application Program Interface + (GSS-API) Key Exchange with SHA-2"; + } + + identity gss-group1-sha1-1.3.132.0.38 { + base key-exchange-alg-base; + status deprecated; + description + "GSS-GROUP1-SHA1-1.3.132.0.38 (nistt571, sect571k1)"; + reference + "RFC 8732: + Generic Security Service Application Program Interface + (GSS-API) Key Exchange with SHA-2"; + } + + identity gss-group1-sha1-curve25519-sha256 { + base key-exchange-alg-base; + status deprecated; + description + "GSS-GROUP1-SHA1-CURVE25519-SHA256"; + reference + "RFC 8732: + Generic Security Service Application Program Interface + (GSS-API) Key Exchange with SHA-2"; + } + + identity gss-group1-sha1-curve448-sha512 { + base key-exchange-alg-base; + status deprecated; + description + "GSS-GROUP1-SHA1-CURVE448-SHA512"; + reference + "RFC 8732: + Generic Security Service Application Program Interface + (GSS-API) Key Exchange with SHA-2"; + } + + identity gss-group14-sha1-nistp256 { + base key-exchange-alg-base; + status deprecated; + description + "GSS-GROUP14-SHA1-NISTP256 (secp256r1)"; + reference + "RFC 8732: + Generic Security Service Application Program Interface + (GSS-API) Key Exchange with SHA-2"; + } + + identity gss-group14-sha1-nistp384 { + base key-exchange-alg-base; + status deprecated; + description + "GSS-GROUP14-SHA1-NISTP384 (secp384r1)"; + reference + "RFC 8732: + Generic Security Service Application Program Interface + (GSS-API) Key Exchange with SHA-2"; + } + + identity gss-group14-sha1-nistp521 { + base key-exchange-alg-base; + status deprecated; + description + "GSS-GROUP14-SHA1-NISTP521 (secp521r1)"; + reference + "RFC 8732: + Generic Security Service Application Program Interface + (GSS-API) Key Exchange with SHA-2"; + } + + identity gss-group14-sha1-1.3.132.0.1 { + base key-exchange-alg-base; + status deprecated; + description + "GSS-GROUP14-SHA1-1.3.132.0.1 (nistk163, sect163k1)"; + reference + "RFC 8732: + Generic Security Service Application Program Interface + (GSS-API) Key Exchange with SHA-2"; + } + + identity gss-group14-sha1-1.2.840.10045.3.1.1 { + base key-exchange-alg-base; + status deprecated; + description + "GSS-GROUP14-SHA1-1.2.840.10045.3.1.1 (nistp192, secp192r1)"; + reference + "RFC 8732: + Generic Security Service Application Program Interface + (GSS-API) Key Exchange with SHA-2"; + } + + identity gss-group14-sha1-1.3.132.0.33 { + base key-exchange-alg-base; + status deprecated; + description + "GSS-GROUP14-SHA1-1.3.132.0.33 (nistp224, secp224r1)"; + reference + "RFC 8732: + Generic Security Service Application Program Interface + (GSS-API) Key Exchange with SHA-2"; + } + + identity gss-group14-sha1-1.3.132.0.26 { + base key-exchange-alg-base; + status deprecated; + description + "GSS-GROUP14-SHA1-1.3.132.0.26 (nistk233, sect233k1)"; + reference + "RFC 8732: + Generic Security Service Application Program Interface + (GSS-API) Key Exchange with SHA-2"; + } + + identity gss-group14-sha1-1.3.132.0.27 { + base key-exchange-alg-base; + status deprecated; + description + "GSS-GROUP14-SHA1-1.3.132.0.27 (nistb233, sect233r1)"; + reference + "RFC 8732: + Generic Security Service Application Program Interface + (GSS-API) Key Exchange with SHA-2"; + } + + identity gss-group14-sha1-1.3.132.0.16 { + base key-exchange-alg-base; + status deprecated; + description + "GSS-GROUP14-SHA1-1.3.132.0.16 (nistk283, sect283k1)"; + reference + "RFC 8732: + Generic Security Service Application Program Interface + (GSS-API) Key Exchange with SHA-2"; + } + + identity gss-group14-sha1-1.3.132.0.36 { + base key-exchange-alg-base; + status deprecated; + description + "GSS-GROUP14-SHA1-1.3.132.0.36 (nistk409, sect409k1)"; + reference + "RFC 8732: + Generic Security Service Application Program Interface + (GSS-API) Key Exchange with SHA-2"; + } + + identity gss-group14-sha1-1.3.132.0.37 { + base key-exchange-alg-base; + status deprecated; + description + "GSS-GROUP14-SHA1-1.3.132.0.37 (nistb409, sect409r1)"; + reference + "RFC 8732: + Generic Security Service Application Program Interface + (GSS-API) Key Exchange with SHA-2"; + } + + identity gss-group14-sha1-1.3.132.0.38 { + base key-exchange-alg-base; + status deprecated; + description + "GSS-GROUP14-SHA1-1.3.132.0.38 (nistt571, sect571k1)"; + reference + "RFC 8732: + Generic Security Service Application Program Interface + (GSS-API) Key Exchange with SHA-2"; + } + + identity gss-group14-sha1-curve25519-sha256 { + base key-exchange-alg-base; + status deprecated; + description + "GSS-GROUP14-SHA1-CURVE25519-SHA256"; + reference + "RFC 8732: + Generic Security Service Application Program Interface + (GSS-API) Key Exchange with SHA-2"; + } + + identity gss-group14-sha1-curve448-sha512 { + base key-exchange-alg-base; + status deprecated; + description + "GSS-GROUP14-SHA1-CURVE448-SHA512"; + reference + "RFC 8732: + Generic Security Service Application Program Interface + (GSS-API) Key Exchange with SHA-2"; + } + + identity gss-gex-sha1-nistp256 { + base key-exchange-alg-base; + status deprecated; + description + "GSS-GEX-SHA1-NISTP256 (secp256r1)"; + reference + "RFC 8732: + Generic Security Service Application Program Interface + (GSS-API) Key Exchange with SHA-2"; + } + + identity gss-gex-sha1-nistp384 { + base key-exchange-alg-base; + status deprecated; + description + "GSS-GEX-SHA1-NISTP384 (secp384r1)"; + reference + "RFC 8732: + Generic Security Service Application Program Interface + (GSS-API) Key Exchange with SHA-2"; + } + + identity gss-gex-sha1-nistp521 { + base key-exchange-alg-base; + status deprecated; + description + "GSS-GEX-SHA1-NISTP521 (secp521r1)"; + reference + "RFC 8732: + Generic Security Service Application Program Interface + (GSS-API) Key Exchange with SHA-2"; + } + + identity gss-gex-sha1-1.3.132.0.1 { + base key-exchange-alg-base; + status deprecated; + description + "GSS-GEX-SHA1-1.3.132.0.1 (nistk163, sect163k1)"; + reference + "RFC 8732: + Generic Security Service Application Program Interface + (GSS-API) Key Exchange with SHA-2"; + } + + identity gss-gex-sha1-1.2.840.10045.3.1.1 { + base key-exchange-alg-base; + status deprecated; + description + "GSS-GEX-SHA1-1.2.840.10045.3.1.1 (nistp192, secp192r1)"; + reference + "RFC 8732: + Generic Security Service Application Program Interface + (GSS-API) Key Exchange with SHA-2"; + } + + identity gss-gex-sha1-1.3.132.0.33 { + base key-exchange-alg-base; + status deprecated; + description + "GSS-GEX-SHA1-1.3.132.0.33 (nistp224, secp224r1)"; + reference + "RFC 8732: + Generic Security Service Application Program Interface + (GSS-API) Key Exchange with SHA-2"; + } + + identity gss-gex-sha1-1.3.132.0.26 { + base key-exchange-alg-base; + status deprecated; + description + "GSS-GEX-SHA1-1.3.132.0.26 (nistk233, sect233k1)"; + reference + "RFC 8732: + Generic Security Service Application Program Interface + (GSS-API) Key Exchange with SHA-2"; + } + + identity gss-gex-sha1-1.3.132.0.27 { + base key-exchange-alg-base; + status deprecated; + description + "GSS-GEX-SHA1-1.3.132.0.27 (nistb233, sect233r1)"; + reference + "RFC 8732: + Generic Security Service Application Program Interface + (GSS-API) Key Exchange with SHA-2"; + } + identity gss-gex-sha1-1.3.132.0.16 { + base key-exchange-alg-base; + status deprecated; + description + "GSS-GEX-SHA1-1.3.132.0.16 (nistk283, sect283k1)"; + reference + "RFC 8732: + Generic Security Service Application Program Interface + (GSS-API) Key Exchange with SHA-2"; + } + + identity gss-gex-sha1-1.3.132.0.36 { + base key-exchange-alg-base; + status deprecated; + description + "GSS-GEX-SHA1-1.3.132.0.36 (nistk409, sect409k1)"; + reference + "RFC 8732: + Generic Security Service Application Program Interface + (GSS-API) Key Exchange with SHA-2"; + } + + identity gss-gex-sha1-1.3.132.0.37 { + base key-exchange-alg-base; + status deprecated; + description + "GSS-GEX-SHA1-1.3.132.0.37 (nistb409, sect409r1)"; + reference + "RFC 8732: + Generic Security Service Application Program Interface + (GSS-API) Key Exchange with SHA-2"; + } + + identity gss-gex-sha1-1.3.132.0.38 { + base key-exchange-alg-base; + status deprecated; + description + "GSS-GEX-SHA1-1.3.132.0.38 (nistt571, sect571k1)"; + reference + "RFC 8732: + Generic Security Service Application Program Interface + (GSS-API) Key Exchange with SHA-2"; + } + + identity gss-gex-sha1-curve25519-sha256 { + base key-exchange-alg-base; + status deprecated; + description + "GSS-GEX-SHA1-CURVE25519-SHA256"; + reference + "RFC 8732: + Generic Security Service Application Program Interface + (GSS-API) Key Exchange with SHA-2"; + } + + identity gss-gex-sha1-curve448-sha512 { + base key-exchange-alg-base; + status deprecated; + description + "GSS-GEX-SHA1-CURVE448-SHA512"; + reference + "RFC 8732: + Generic Security Service Application Program Interface + (GSS-API) Key Exchange with SHA-2"; + } + + identity rsa1024-sha1 { + base key-exchange-alg-base; + status obsolete; + description + "RSA1024-SHA1"; + reference + "RFC 4432: + RSA Key Exchange for the Secure Shell (SSH) + Transport Layer Protocol"; + } + + identity rsa2048-sha256 { + base key-exchange-alg-base; + description + "RSA2048-SHA256"; + reference + "RFC 4432: + RSA Key Exchange for the Secure Shell (SSH) + Transport Layer Protocol"; + } + + identity ext-info-s { + base key-exchange-alg-base; + description + "EXT-INFO-S"; + reference + "RFC 8308: + Extension Negotiation in the Secure Shell (SSH) Protocol"; + } + + identity ext-info-c { + base key-exchange-alg-base; + description + "EXT-INFO-C"; + reference + "RFC 8308: + Extension Negotiation in the Secure Shell (SSH) Protocol"; + } + + identity gss-group14-sha256-nistp256 { + base key-exchange-alg-base; + description + "GSS-GROUP14-SHA256-NISTP256 (secp256r1)"; + reference + "RFC 8732: + Generic Security Service Application Program Interface + (GSS-API) Key Exchange with SHA-2"; + } + + identity gss-group14-sha256-nistp384 { + base key-exchange-alg-base; + description + "GSS-GROUP14-SHA256-NISTP384 (secp384r1)"; + reference + "RFC 8732: + Generic Security Service Application Program Interface + (GSS-API) Key Exchange with SHA-2"; + } + + identity gss-group14-sha256-nistp521 { + base key-exchange-alg-base; + description + "GSS-GROUP14-SHA256-NISTP521 (secp521r1)"; + reference + "RFC 8732: + Generic Security Service Application Program Interface + (GSS-API) Key Exchange with SHA-2"; + } + + identity gss-group14-sha256-1.3.132.0.1 { + base key-exchange-alg-base; + description + "GSS-GROUP14-SHA256-1.3.132.0.1 (nistk163, sect163k1)"; + reference + "RFC 8732: + Generic Security Service Application Program Interface + (GSS-API) Key Exchange with SHA-2"; + } + identity gss-group14-sha256-1.2.840.10045.3.1.1 { + base key-exchange-alg-base; + description + "GSS-GROUP14-SHA256-1.2.840.10045.3.1.1 (nistp192, secp192r1)"; + reference + "RFC 8732: + Generic Security Service Application Program Interface + (GSS-API) Key Exchange with SHA-2"; + } + + identity gss-group14-sha256-1.3.132.0.33 { + base key-exchange-alg-base; + description + "GSS-GROUP14-SHA256-1.3.132.0.33 (nistp224, secp224r1)"; + reference + "RFC 8732: + Generic Security Service Application Program Interface + (GSS-API) Key Exchange with SHA-2"; + } + + identity gss-group14-sha256-1.3.132.0.26 { + base key-exchange-alg-base; + description + "GSS-GROUP14-SHA256-1.3.132.0.26 (nistk233, sect233k1)"; + reference + "RFC 8732: + Generic Security Service Application Program Interface + (GSS-API) Key Exchange with SHA-2"; + } + + identity gss-group14-sha256-1.3.132.0.27 { + base key-exchange-alg-base; + description + "GSS-GROUP14-SHA256-1.3.132.0.27 (nistb233, sect233r1)"; + reference + "RFC 8732: + Generic Security Service Application Program Interface + (GSS-API) Key Exchange with SHA-2"; + } + + identity gss-group14-sha256-1.3.132.0.16 { + base key-exchange-alg-base; + description + "GSS-GROUP14-SHA256-1.3.132.0.16 (nistk283, sect283k1)"; + reference + "RFC 8732: + Generic Security Service Application Program Interface + (GSS-API) Key Exchange with SHA-2"; + } + + identity gss-group14-sha256-1.3.132.0.36 { + base key-exchange-alg-base; + description + "GSS-GROUP14-SHA256-1.3.132.0.36 (nistk409, sect409k1)"; + reference + "RFC 8732: + Generic Security Service Application Program Interface + (GSS-API) Key Exchange with SHA-2"; + } + + identity gss-group14-sha256-1.3.132.0.37 { + base key-exchange-alg-base; + description + "GSS-GROUP14-SHA256-1.3.132.0.37 (nistb409, sect409r1)"; + reference + "RFC 8732: + Generic Security Service Application Program Interface + (GSS-API) Key Exchange with SHA-2"; + } + + identity gss-group14-sha256-1.3.132.0.38 { + base key-exchange-alg-base; + description + "GSS-GROUP14-SHA256-1.3.132.0.38 (nistt571, sect571k1)"; + reference + "RFC 8732: + Generic Security Service Application Program Interface + (GSS-API) Key Exchange with SHA-2"; + } + + identity gss-group14-sha256-curve25519-sha256 { + base key-exchange-alg-base; + description + "GSS-GROUP14-SHA256-CURVE25519-SHA256"; + reference + "RFC 8732: + Generic Security Service Application Program Interface + (GSS-API) Key Exchange with SHA-2"; + } + + identity gss-group14-sha256-curve448-sha512 { + base key-exchange-alg-base; + description + "GSS-GROUP14-SHA256-CURVE448-SHA512"; + reference + "RFC 8732: + Generic Security Service Application Program Interface + (GSS-API) Key Exchange with SHA-2"; + } + + identity gss-group15-sha512-nistp256 { + base key-exchange-alg-base; + description + "GSS-GROUP15-SHA512-NISTP256 (secp256r1)"; + reference + "RFC 8732: + Generic Security Service Application Program Interface + (GSS-API) Key Exchange with SHA-2"; + } + + identity gss-group15-sha512-nistp384 { + base key-exchange-alg-base; + description + "GSS-GROUP15-SHA512-NISTP384 (secp384r1)"; + reference + "RFC 8732: + Generic Security Service Application Program Interface + (GSS-API) Key Exchange with SHA-2"; + } + + identity gss-group15-sha512-nistp521 { + base key-exchange-alg-base; + description + "GSS-GROUP15-SHA512-NISTP521 (secp521r1)"; + reference + "RFC 8732: + Generic Security Service Application Program Interface + (GSS-API) Key Exchange with SHA-2"; + } + + identity gss-group15-sha512-1.3.132.0.1 { + base key-exchange-alg-base; + description + "GSS-GROUP15-SHA512-1.3.132.0.1 (nistk163, sect163k1)"; + reference + "RFC 8732: + Generic Security Service Application Program Interface + (GSS-API) Key Exchange with SHA-2"; + } + + identity gss-group15-sha512-1.2.840.10045.3.1.1 { + base key-exchange-alg-base; + description + "GSS-GROUP15-SHA512-1.2.840.10045.3.1.1 (nistp192, secp192r1)"; + reference + "RFC 8732: + Generic Security Service Application Program Interface + (GSS-API) Key Exchange with SHA-2"; + } + + identity gss-group15-sha512-1.3.132.0.33 { + base key-exchange-alg-base; + description + "GSS-GROUP15-SHA512-1.3.132.0.33 (nistp224, secp224r1)"; + reference + "RFC 8732: + Generic Security Service Application Program Interface + (GSS-API) Key Exchange with SHA-2"; + } + + identity gss-group15-sha512-1.3.132.0.26 { + base key-exchange-alg-base; + description + "GSS-GROUP15-SHA512-1.3.132.0.26 (nistk233, sect233k1)"; + reference + "RFC 8732: + Generic Security Service Application Program Interface + (GSS-API) Key Exchange with SHA-2"; + } + + identity gss-group15-sha512-1.3.132.0.27 { + base key-exchange-alg-base; + description + "GSS-GROUP15-SHA512-1.3.132.0.27 (nistb233, sect233r1)"; + reference + "RFC 8732: + Generic Security Service Application Program Interface + (GSS-API) Key Exchange with SHA-2"; + } + + identity gss-group15-sha512-1.3.132.0.16 { + base key-exchange-alg-base; + description + "GSS-GROUP15-SHA512-1.3.132.0.16 (nistk283, sect283k1)"; + reference + "RFC 8732: + Generic Security Service Application Program Interface + (GSS-API) Key Exchange with SHA-2"; + } + + identity gss-group15-sha512-1.3.132.0.36 { + base key-exchange-alg-base; + description + "GSS-GROUP15-SHA512-1.3.132.0.36 (nistk409, sect409k1)"; + reference + "RFC 8732: + Generic Security Service Application Program Interface + (GSS-API) Key Exchange with SHA-2"; + } + + identity gss-group15-sha512-1.3.132.0.37 { + base key-exchange-alg-base; + description + "GSS-GROUP15-SHA512-1.3.132.0.37 (nistb409, sect409r1)"; + reference + "RFC 8732: + Generic Security Service Application Program Interface + (GSS-API) Key Exchange with SHA-2"; + } + + identity gss-group15-sha512-1.3.132.0.38 { + base key-exchange-alg-base; + description + "GSS-GROUP15-SHA512-1.3.132.0.38 (nistt571, sect571k1)"; + reference + "RFC 8732: + Generic Security Service Application Program Interface + (GSS-API) Key Exchange with SHA-2"; + } + + identity gss-group15-sha512-curve25519-sha256 { + base key-exchange-alg-base; + description + "GSS-GROUP15-SHA512-CURVE25519-SHA256"; + reference + "RFC 8732: + Generic Security Service Application Program Interface + (GSS-API) Key Exchange with SHA-2"; + } + + identity gss-group15-sha512-curve448-sha512 { + base key-exchange-alg-base; + description + "GSS-GROUP15-SHA512-CURVE448-SHA512"; + reference + "RFC 8732: + Generic Security Service Application Program Interface + (GSS-API) Key Exchange with SHA-2"; + } + + identity gss-group16-sha512-nistp256 { + base key-exchange-alg-base; + description + "GSS-GROUP16-SHA512-NISTP256 (secp256r1)"; + reference + "RFC 8732: + Generic Security Service Application Program Interface + (GSS-API) Key Exchange with SHA-2"; + } + + identity gss-group16-sha512-nistp384 { + base key-exchange-alg-base; + description + "GSS-GROUP16-SHA512-NISTP384 (secp384r1)"; + reference + "RFC 8732: + Generic Security Service Application Program Interface + (GSS-API) Key Exchange with SHA-2"; + } + + identity gss-group16-sha512-nistp521 { + base key-exchange-alg-base; + description + "GSS-GROUP16-SHA512-NISTP521 (secp521r1)"; + reference + "RFC 8732: + Generic Security Service Application Program Interface + (GSS-API) Key Exchange with SHA-2"; + } + + identity gss-group16-sha512-1.3.132.0.1 { + base key-exchange-alg-base; + description + "GSS-GROUP16-SHA512-1.3.132.0.1 (nistk163, sect163k1)"; + reference + "RFC 8732: + Generic Security Service Application Program Interface + (GSS-API) Key Exchange with SHA-2"; + } + + identity gss-group16-sha512-1.2.840.10045.3.1.1 { + base key-exchange-alg-base; + description + "GSS-GROUP16-SHA512-1.2.840.10045.3.1.1 (nistp192, secp192r1)"; + reference + "RFC 8732: + Generic Security Service Application Program Interface + (GSS-API) Key Exchange with SHA-2"; + } + + identity gss-group16-sha512-1.3.132.0.33 { + base key-exchange-alg-base; + description + "GSS-GROUP16-SHA512-1.3.132.0.33 (nistp224, secp224r1)"; + reference + "RFC 8732: + Generic Security Service Application Program Interface + (GSS-API) Key Exchange with SHA-2"; + } + + identity gss-group16-sha512-1.3.132.0.26 { + base key-exchange-alg-base; + description + "GSS-GROUP16-SHA512-1.3.132.0.26 (nistk233, sect233k1)"; + reference + "RFC 8732: + Generic Security Service Application Program Interface + (GSS-API) Key Exchange with SHA-2"; + } + + identity gss-group16-sha512-1.3.132.0.27 { + base key-exchange-alg-base; + description + "GSS-GROUP16-SHA512-1.3.132.0.27 (nistb233, sect233r1)"; + reference + "RFC 8732: + Generic Security Service Application Program Interface + (GSS-API) Key Exchange with SHA-2"; + } + + identity gss-group16-sha512-1.3.132.0.16 { + base key-exchange-alg-base; + description + "GSS-GROUP16-SHA512-1.3.132.0.16 (nistk283, sect283k1)"; + reference + "RFC 8732: + Generic Security Service Application Program Interface + (GSS-API) Key Exchange with SHA-2"; + } + + identity gss-group16-sha512-1.3.132.0.36 { + base key-exchange-alg-base; + description + "GSS-GROUP16-SHA512-1.3.132.0.36 (nistk409, sect409k1)"; + reference + "RFC 8732: + Generic Security Service Application Program Interface + (GSS-API) Key Exchange with SHA-2"; + } + + identity gss-group16-sha512-1.3.132.0.37 { + base key-exchange-alg-base; + description + "GSS-GROUP16-SHA512-1.3.132.0.37 (nistb409, sect409r1)"; + reference + "RFC 8732: + Generic Security Service Application Program Interface + (GSS-API) Key Exchange with SHA-2"; + } + + identity gss-group16-sha512-1.3.132.0.38 { + base key-exchange-alg-base; + description + "GSS-GROUP16-SHA512-1.3.132.0.38 (nistt571, sect571k1)"; + reference + "RFC 8732: + Generic Security Service Application Program Interface + (GSS-API) Key Exchange with SHA-2"; + } + + identity gss-group16-sha512-curve25519-sha256 { + base key-exchange-alg-base; + description + "GSS-GROUP16-SHA512-CURVE25519-SHA256"; + reference + "RFC 8732: + Generic Security Service Application Program Interface + (GSS-API) Key Exchange with SHA-2"; + } + + identity gss-group16-sha512-curve448-sha512 { + base key-exchange-alg-base; + description + "GSS-GROUP16-SHA512-CURVE448-SHA512"; + reference + "RFC 8732: + Generic Security Service Application Program Interface + (GSS-API) Key Exchange with SHA-2"; + } + + identity gss-group17-sha512-nistp256 { + base key-exchange-alg-base; + description + "GSS-GROUP17-SHA512-NISTP256 (secp256r1)"; + reference + "RFC 8732: + Generic Security Service Application Program Interface + (GSS-API) Key Exchange with SHA-2"; + } + + identity gss-group17-sha512-nistp384 { + base key-exchange-alg-base; + description + "GSS-GROUP17-SHA512-NISTP384 (secp384r1)"; + reference + "RFC 8732: + Generic Security Service Application Program Interface + (GSS-API) Key Exchange with SHA-2"; + } + + identity gss-group17-sha512-nistp521 { + base key-exchange-alg-base; + description + "GSS-GROUP17-SHA512-NISTP521 (secp521r1)"; + reference + "RFC 8732: + Generic Security Service Application Program Interface + (GSS-API) Key Exchange with SHA-2"; + } + + identity gss-group17-sha512-1.3.132.0.1 { + base key-exchange-alg-base; + description + "GSS-GROUP17-SHA512-1.3.132.0.1 (nistk163, sect163k1)"; + reference + "RFC 8732: + Generic Security Service Application Program Interface + (GSS-API) Key Exchange with SHA-2"; + } + + identity gss-group17-sha512-1.2.840.10045.3.1.1 { + base key-exchange-alg-base; + description + "GSS-GROUP17-SHA512-1.2.840.10045.3.1.1 (nistp192, secp192r1)"; + reference + "RFC 8732: + Generic Security Service Application Program Interface + (GSS-API) Key Exchange with SHA-2"; + } + + identity gss-group17-sha512-1.3.132.0.33 { + base key-exchange-alg-base; + description + "GSS-GROUP17-SHA512-1.3.132.0.33 (nistp224, secp224r1)"; + reference + "RFC 8732: + Generic Security Service Application Program Interface + (GSS-API) Key Exchange with SHA-2"; + } + + identity gss-group17-sha512-1.3.132.0.26 { + base key-exchange-alg-base; + description + "GSS-GROUP17-SHA512-1.3.132.0.26 (nistk233, sect233k1)"; + reference + "RFC 8732: + Generic Security Service Application Program Interface + (GSS-API) Key Exchange with SHA-2"; + } + + identity gss-group17-sha512-1.3.132.0.27 { + base key-exchange-alg-base; + description + "GSS-GROUP17-SHA512-1.3.132.0.27 (nistb233, sect233r1)"; + reference + "RFC 8732: + Generic Security Service Application Program Interface + (GSS-API) Key Exchange with SHA-2"; + } + + identity gss-group17-sha512-1.3.132.0.16 { + base key-exchange-alg-base; + description + "GSS-GROUP17-SHA512-1.3.132.0.16 (nistk283, sect283k1)"; + reference + "RFC 8732: + Generic Security Service Application Program Interface + (GSS-API) Key Exchange with SHA-2"; + } + + identity gss-group17-sha512-1.3.132.0.36 { + base key-exchange-alg-base; + description + "GSS-GROUP17-SHA512-1.3.132.0.36 (nistk409, sect409k1)"; + reference + "RFC 8732: + Generic Security Service Application Program Interface + (GSS-API) Key Exchange with SHA-2"; + } + + identity gss-group17-sha512-1.3.132.0.37 { + base key-exchange-alg-base; + description + "GSS-GROUP17-SHA512-1.3.132.0.37 (nistb409, sect409r1)"; + reference + "RFC 8732: + Generic Security Service Application Program Interface + (GSS-API) Key Exchange with SHA-2"; + } + + identity gss-group17-sha512-1.3.132.0.38 { + base key-exchange-alg-base; + description + "GSS-GROUP17-SHA512-1.3.132.0.38 (nistt571, sect571k1)"; + reference + "RFC 8732: + Generic Security Service Application Program Interface + (GSS-API) Key Exchange with SHA-2"; + } + + identity gss-group17-sha512-curve25519-sha256 { + base key-exchange-alg-base; + description + "GSS-GROUP17-SHA512-CURVE25519-SHA256"; + reference + "RFC 8732: + Generic Security Service Application Program Interface + (GSS-API) Key Exchange with SHA-2"; + } + + identity gss-group17-sha512-curve448-sha512 { + base key-exchange-alg-base; + description + "GSS-GROUP17-SHA512-CURVE448-SHA512"; + reference + "RFC 8732: + Generic Security Service Application Program Interface + (GSS-API) Key Exchange with SHA-2"; + } + + identity gss-group18-sha512-nistp256 { + base key-exchange-alg-base; + description + "GSS-GROUP18-SHA512-NISTP256 (secp256r1)"; + reference + "RFC 8732: + Generic Security Service Application Program Interface + (GSS-API) Key Exchange with SHA-2"; + } + + identity gss-group18-sha512-nistp384 { + base key-exchange-alg-base; + description + "GSS-GROUP18-SHA512-NISTP384 (secp384r1)"; + reference + "RFC 8732: + Generic Security Service Application Program Interface + (GSS-API) Key Exchange with SHA-2"; + } + + identity gss-group18-sha512-nistp521 { + base key-exchange-alg-base; + description + "GSS-GROUP18-SHA512-NISTP521 (secp521r1)"; + reference + "RFC 8732: + Generic Security Service Application Program Interface + (GSS-API) Key Exchange with SHA-2"; + } + + identity gss-group18-sha512-1.3.132.0.1 { + base key-exchange-alg-base; + description + "GSS-GROUP18-SHA512-1.3.132.0.1 (nistk163, sect163k1)"; + reference + "RFC 8732: + Generic Security Service Application Program Interface + (GSS-API) Key Exchange with SHA-2"; + } + + identity gss-group18-sha512-1.2.840.10045.3.1.1 { + base key-exchange-alg-base; + description + "GSS-GROUP18-SHA512-1.2.840.10045.3.1.1 (nistp192, secp192r1)"; + reference + "RFC 8732: + Generic Security Service Application Program Interface + (GSS-API) Key Exchange with SHA-2"; + } + + identity gss-group18-sha512-1.3.132.0.33 { + base key-exchange-alg-base; + description + "GSS-GROUP18-SHA512-1.3.132.0.33 (nistp224, secp224r1)"; + reference + "RFC 8732: + Generic Security Service Application Program Interface + (GSS-API) Key Exchange with SHA-2"; + } + + identity gss-group18-sha512-1.3.132.0.26 { + base key-exchange-alg-base; + description + "GSS-GROUP18-SHA512-1.3.132.0.26 (nistk233, sect233k1)"; + reference + "RFC 8732: + Generic Security Service Application Program Interface + (GSS-API) Key Exchange with SHA-2"; + } + + identity gss-group18-sha512-1.3.132.0.27 { + base key-exchange-alg-base; + description + "GSS-GROUP18-SHA512-1.3.132.0.27 (nistb233, sect233r1)"; + reference + "RFC 8732: + Generic Security Service Application Program Interface + (GSS-API) Key Exchange with SHA-2"; + } + + identity gss-group18-sha512-1.3.132.0.16 { + base key-exchange-alg-base; + description + "GSS-GROUP18-SHA512-1.3.132.0.16 (nistk283, sect283k1)"; + reference + "RFC 8732: + Generic Security Service Application Program Interface + (GSS-API) Key Exchange with SHA-2"; + } + + identity gss-group18-sha512-1.3.132.0.36 { + base key-exchange-alg-base; + description + "GSS-GROUP18-SHA512-1.3.132.0.36 (nistk409, sect409k1)"; + reference + "RFC 8732: + Generic Security Service Application Program Interface + (GSS-API) Key Exchange with SHA-2"; + } + + identity gss-group18-sha512-1.3.132.0.37 { + base key-exchange-alg-base; + description + "GSS-GROUP18-SHA512-1.3.132.0.37 (nistb409, sect409r1)"; + reference + "RFC 8732: + Generic Security Service Application Program Interface + (GSS-API) Key Exchange with SHA-2"; + } + + identity gss-group18-sha512-1.3.132.0.38 { + base key-exchange-alg-base; + description + "GSS-GROUP18-SHA512-1.3.132.0.38 (nistt571, sect571k1)"; + reference + "RFC 8732: + Generic Security Service Application Program Interface + (GSS-API) Key Exchange with SHA-2"; + } + + identity gss-group18-sha512-curve25519-sha256 { + base key-exchange-alg-base; + description + "GSS-GROUP18-SHA512-CURVE25519-SHA256"; + reference + "RFC 8732: + Generic Security Service Application Program Interface + (GSS-API) Key Exchange with SHA-2"; + } + + identity gss-group18-sha512-curve448-sha512 { + base key-exchange-alg-base; + description + "GSS-GROUP18-SHA512-CURVE448-SHA512"; + reference + "RFC 8732: + Generic Security Service Application Program Interface + (GSS-API) Key Exchange with SHA-2"; + } + + identity gss-nistp256-sha256-nistp256 { + base key-exchange-alg-base; + description + "GSS-NISTP256-SHA256-NISTP256 (secp256r1)"; + reference + "RFC 8732: + Generic Security Service Application Program Interface + (GSS-API) Key Exchange with SHA-2"; + } + + identity gss-nistp256-sha256-nistp384 { + base key-exchange-alg-base; + description + "GSS-NISTP256-SHA256-NISTP384 (secp384r1)"; + reference + "RFC 8732: + Generic Security Service Application Program Interface + (GSS-API) Key Exchange with SHA-2"; + } + + identity gss-nistp256-sha256-nistp521 { + base key-exchange-alg-base; + description + "GSS-NISTP256-SHA256-NISTP521 (secp521r1)"; + reference + "RFC 8732: + Generic Security Service Application Program Interface + (GSS-API) Key Exchange with SHA-2"; + } + + identity gss-nistp256-sha256-1.3.132.0.1 { + base key-exchange-alg-base; + description + "GSS-NISTP256-SHA256-1.3.132.0.1 (nistk163, sect163k1)"; + reference + "RFC 8732: + Generic Security Service Application Program Interface + (GSS-API) Key Exchange with SHA-2"; + } + + identity gss-nistp256-sha256-1.2.840.10045.3.1.1 { + base key-exchange-alg-base; + description + "GSS-NISTP256-SHA256-1.2.840.10045.3.1.1 (nistp192, secp192r1)"; + reference + "RFC 8732: + Generic Security Service Application Program Interface + (GSS-API) Key Exchange with SHA-2"; + } + + identity gss-nistp256-sha256-1.3.132.0.33 { + base key-exchange-alg-base; + description + "GSS-NISTP256-SHA256-1.3.132.0.33 (nistp224, secp224r1)"; + reference + "RFC 8732: + Generic Security Service Application Program Interface + (GSS-API) Key Exchange with SHA-2"; + } + + identity gss-nistp256-sha256-1.3.132.0.26 { + base key-exchange-alg-base; + description + "GSS-NISTP256-SHA256-1.3.132.0.26 (nistk233, sect233k1)"; + reference + "RFC 8732: + Generic Security Service Application Program Interface + (GSS-API) Key Exchange with SHA-2"; + } + + identity gss-nistp256-sha256-1.3.132.0.27 { + base key-exchange-alg-base; + description + "GSS-NISTP256-SHA256-1.3.132.0.27 (nistb233, sect233r1)"; + reference + "RFC 8732: + Generic Security Service Application Program Interface + (GSS-API) Key Exchange with SHA-2"; + } + + identity gss-nistp256-sha256-1.3.132.0.16 { + base key-exchange-alg-base; + description + "GSS-NISTP256-SHA256-1.3.132.0.16 (nistk283, sect283k1)"; + reference + "RFC 8732: + Generic Security Service Application Program Interface + (GSS-API) Key Exchange with SHA-2"; + } + + identity gss-nistp256-sha256-1.3.132.0.36 { + base key-exchange-alg-base; + description + "GSS-NISTP256-SHA256-1.3.132.0.36 (nistk409, sect409k1)"; + reference + "RFC 8732: + Generic Security Service Application Program Interface + (GSS-API) Key Exchange with SHA-2"; + } + + identity gss-nistp256-sha256-1.3.132.0.37 { + base key-exchange-alg-base; + description + "GSS-NISTP256-SHA256-1.3.132.0.37 (nistb409, sect409r1)"; + reference + "RFC 8732: + Generic Security Service Application Program Interface + (GSS-API) Key Exchange with SHA-2"; + } + + identity gss-nistp256-sha256-1.3.132.0.38 { + base key-exchange-alg-base; + description + "GSS-NISTP256-SHA256-1.3.132.0.38 (nistt571, sect571k1)"; + reference + "RFC 8732: + Generic Security Service Application Program Interface + (GSS-API) Key Exchange with SHA-2"; + } + + identity gss-nistp256-sha256-curve25519-sha256 { + base key-exchange-alg-base; + description + "GSS-NISTP256-SHA256-CURVE25519-SHA256"; + reference + "RFC 8732: + Generic Security Service Application Program Interface + (GSS-API) Key Exchange with SHA-2"; + } + + identity gss-nistp256-sha256-curve448-sha512 { + base key-exchange-alg-base; + description + "GSS-NISTP256-SHA256-CURVE448-SHA512"; + reference + "RFC 8732: + Generic Security Service Application Program Interface + (GSS-API) Key Exchange with SHA-2"; + } + + identity gss-nistp384-sha384-nistp256 { + base key-exchange-alg-base; + description + "GSS-NISTP384-SHA384-NISTP256 (secp256r1)"; + reference + "RFC 8732: + Generic Security Service Application Program Interface + (GSS-API) Key Exchange with SHA-2"; + } + + identity gss-nistp384-sha384-nistp384 { + base key-exchange-alg-base; + description + "GSS-NISTP384-SHA384-NISTP384 (secp384r1)"; + reference + "RFC 8732: + Generic Security Service Application Program Interface + (GSS-API) Key Exchange with SHA-2"; + } + + identity gss-nistp384-sha384-nistp521 { + base key-exchange-alg-base; + description + "GSS-NISTP384-SHA384-NISTP521 (secp521r1)"; + reference + "RFC 8732: + Generic Security Service Application Program Interface + (GSS-API) Key Exchange with SHA-2"; + } + + identity gss-nistp384-sha384-1.3.132.0.1 { + base key-exchange-alg-base; + description + "GSS-NISTP384-SHA384-1.3.132.0.1 (nistk163, sect163k1)"; + reference + "RFC 8732: + Generic Security Service Application Program Interface + (GSS-API) Key Exchange with SHA-2"; + } + + identity gss-nistp384-sha384-1.2.840.10045.3.1.1 { + base key-exchange-alg-base; + description + "GSS-NISTP384-SHA384-1.2.840.10045.3.1.1 (nistp192, secp192r1)"; + reference + "RFC 8732: + Generic Security Service Application Program Interface + (GSS-API) Key Exchange with SHA-2"; + } + + identity gss-nistp384-sha384-1.3.132.0.33 { + base key-exchange-alg-base; + description + "GSS-NISTP384-SHA384-1.3.132.0.33 (nistp224, secp224r1)"; + reference + "RFC 8732: + Generic Security Service Application Program Interface + (GSS-API) Key Exchange with SHA-2"; + } + + identity gss-nistp384-sha384-1.3.132.0.26 { + base key-exchange-alg-base; + description + "GSS-NISTP384-SHA384-1.3.132.0.26 (nistk233, sect233k1)"; + reference + "RFC 8732: + Generic Security Service Application Program Interface + (GSS-API) Key Exchange with SHA-2"; + } + + identity gss-nistp384-sha384-1.3.132.0.27 { + base key-exchange-alg-base; + description + "GSS-NISTP384-SHA384-1.3.132.0.27 (nistb233, sect233r1)"; + reference + "RFC 8732: + Generic Security Service Application Program Interface + (GSS-API) Key Exchange with SHA-2"; + } + + identity gss-nistp384-sha384-1.3.132.0.16 { + base key-exchange-alg-base; + description + "GSS-NISTP384-SHA384-1.3.132.0.16 (nistk283, sect283k1)"; + reference + "RFC 8732: + Generic Security Service Application Program Interface + (GSS-API) Key Exchange with SHA-2"; + } + + identity gss-nistp384-sha384-1.3.132.0.36 { + base key-exchange-alg-base; + description + "GSS-NISTP384-SHA384-1.3.132.0.36 (nistk409, sect409k1)"; + reference + "RFC 8732: + Generic Security Service Application Program Interface + (GSS-API) Key Exchange with SHA-2"; + } + + identity gss-nistp384-sha384-1.3.132.0.37 { + base key-exchange-alg-base; + description + "GSS-NISTP384-SHA384-1.3.132.0.37 (nistb409, sect409r1)"; + reference + "RFC 8732: + Generic Security Service Application Program Interface + (GSS-API) Key Exchange with SHA-2"; + } + + identity gss-nistp384-sha384-1.3.132.0.38 { + base key-exchange-alg-base; + description + "GSS-NISTP384-SHA384-1.3.132.0.38 (nistt571, sect571k1)"; + reference + "RFC 8732: + Generic Security Service Application Program Interface + (GSS-API) Key Exchange with SHA-2"; + } + + identity gss-nistp384-sha384-curve25519-sha256 { + base key-exchange-alg-base; + description + "GSS-NISTP384-SHA384-CURVE25519-SHA256"; + reference + "RFC 8732: + Generic Security Service Application Program Interface + (GSS-API) Key Exchange with SHA-2"; + } + + identity gss-nistp384-sha384-curve448-sha512 { + base key-exchange-alg-base; + description + "GSS-NISTP384-SHA384-CURVE448-SHA512"; + reference + "RFC 8732: + Generic Security Service Application Program Interface + (GSS-API) Key Exchange with SHA-2"; + } + + identity gss-nistp521-sha512-nistp256 { + base key-exchange-alg-base; + description + "GSS-NISTP521-SHA512-NISTP256 (secp256r1)"; + reference + "RFC 8732: + Generic Security Service Application Program Interface + (GSS-API) Key Exchange with SHA-2"; + } + + identity gss-nistp521-sha512-nistp384 { + base key-exchange-alg-base; + description + "GSS-NISTP521-SHA512-NISTP384 (secp384r1)"; + reference + "RFC 8732: + Generic Security Service Application Program Interface + (GSS-API) Key Exchange with SHA-2"; + } + + identity gss-nistp521-sha512-nistp521 { + base key-exchange-alg-base; + description + "GSS-NISTP521-SHA512-NISTP521 (secp521r1)"; + reference + "RFC 8732: + Generic Security Service Application Program Interface + (GSS-API) Key Exchange with SHA-2"; + } + + identity gss-nistp521-sha512-1.3.132.0.1 { + base key-exchange-alg-base; + description + "GSS-NISTP521-SHA512-1.3.132.0.1 (nistk163, sect163k1)"; + reference + "RFC 8732: + Generic Security Service Application Program Interface + (GSS-API) Key Exchange with SHA-2"; + } + + identity gss-nistp521-sha512-1.2.840.10045.3.1.1 { + base key-exchange-alg-base; + description + "GSS-NISTP521-SHA512-1.2.840.10045.3.1.1 (nistp192, secp192r1)"; + reference + "RFC 8732: + Generic Security Service Application Program Interface + (GSS-API) Key Exchange with SHA-2"; + } + + identity gss-nistp521-sha512-1.3.132.0.33 { + base key-exchange-alg-base; + description + "GSS-NISTP521-SHA512-1.3.132.0.33 (nistp224, secp224r1)"; + reference + "RFC 8732: + Generic Security Service Application Program Interface + (GSS-API) Key Exchange with SHA-2"; + } + + identity gss-nistp521-sha512-1.3.132.0.26 { + base key-exchange-alg-base; + description + "GSS-NISTP521-SHA512-1.3.132.0.26 (nistk233, sect233k1)"; + reference + "RFC 8732: + Generic Security Service Application Program Interface + (GSS-API) Key Exchange with SHA-2"; + } + + identity gss-nistp521-sha512-1.3.132.0.27 { + base key-exchange-alg-base; + description + "GSS-NISTP521-SHA512-1.3.132.0.27 (nistb233, sect233r1)"; + reference + "RFC 8732: + Generic Security Service Application Program Interface + (GSS-API) Key Exchange with SHA-2"; + } + + identity gss-nistp521-sha512-1.3.132.0.16 { + base key-exchange-alg-base; + description + "GSS-NISTP521-SHA512-1.3.132.0.16 (nistk283, sect283k1)"; + reference + "RFC 8732: + Generic Security Service Application Program Interface + (GSS-API) Key Exchange with SHA-2"; + } + + identity gss-nistp521-sha512-1.3.132.0.36 { + base key-exchange-alg-base; + description + "GSS-NISTP521-SHA512-1.3.132.0.36 (nistk409, sect409k1)"; + reference + "RFC 8732: + Generic Security Service Application Program Interface + (GSS-API) Key Exchange with SHA-2"; + } + + identity gss-nistp521-sha512-1.3.132.0.37 { + base key-exchange-alg-base; + description + "GSS-NISTP521-SHA512-1.3.132.0.37 (nistb409, sect409r1)"; + reference + "RFC 8732: + Generic Security Service Application Program Interface + (GSS-API) Key Exchange with SHA-2"; + } + + identity gss-nistp521-sha512-1.3.132.0.38 { + base key-exchange-alg-base; + description + "GSS-NISTP521-SHA512-1.3.132.0.38 (nistt571, sect571k1)"; + reference + "RFC 8732: + Generic Security Service Application Program Interface + (GSS-API) Key Exchange with SHA-2"; + } + + identity gss-nistp521-sha512-curve25519-sha256 { + base key-exchange-alg-base; + description + "GSS-NISTP521-SHA512-CURVE25519-SHA256"; + reference + "RFC 8732: + Generic Security Service Application Program Interface + (GSS-API) Key Exchange with SHA-2"; + } + + identity gss-nistp521-sha512-curve448-sha512 { + base key-exchange-alg-base; + description + "GSS-NISTP521-SHA512-CURVE448-SHA512"; + reference + "RFC 8732: + Generic Security Service Application Program Interface + (GSS-API) Key Exchange with SHA-2"; + } + + identity gss-curve25519-sha256-nistp256 { + base key-exchange-alg-base; + description + "GSS-CURVE25519-SHA256-NISTP256 (secp256r1)"; + reference + "RFC 8732: + Generic Security Service Application Program Interface + (GSS-API) Key Exchange with SHA-2"; + } + + identity gss-curve25519-sha256-nistp384 { + base key-exchange-alg-base; + description + "GSS-CURVE25519-SHA256-NISTP384 (secp384r1)"; + reference + "RFC 8732: + Generic Security Service Application Program Interface + (GSS-API) Key Exchange with SHA-2"; + } + + identity gss-curve25519-sha256-nistp521 { + base key-exchange-alg-base; + description + "GSS-CURVE25519-SHA256-NISTP521 (secp521r1)"; + reference + "RFC 8732: + Generic Security Service Application Program Interface + (GSS-API) Key Exchange with SHA-2"; + } + + identity gss-curve25519-sha256-1.3.132.0.1 { + base key-exchange-alg-base; + description + "GSS-CURVE25519-SHA256-1.3.132.0.1 (nistk163, sect163k1)"; + reference + "RFC 8732: + Generic Security Service Application Program Interface + (GSS-API) Key Exchange with SHA-2"; + } + + identity gss-curve25519-sha256-1.2.840.10045.3.1.1 { + base key-exchange-alg-base; + description + "GSS-CURVE25519-SHA256-1.2.840.10045.3.1.1 (nistp192, + secp192r1)"; + reference + "RFC 8732: + Generic Security Service Application Program Interface + (GSS-API) Key Exchange with SHA-2"; + } + + identity gss-curve25519-sha256-1.3.132.0.33 { + base key-exchange-alg-base; + description + "GSS-CURVE25519-SHA256-1.3.132.0.33 (nistp224, secp224r1)"; + reference + "RFC 8732: + Generic Security Service Application Program Interface + (GSS-API) Key Exchange with SHA-2"; + } + + identity gss-curve25519-sha256-1.3.132.0.26 { + base key-exchange-alg-base; + description + "GSS-CURVE25519-SHA256-1.3.132.0.26 (nistk233, sect233k1)"; + reference + "RFC 8732: + Generic Security Service Application Program Interface + (GSS-API) Key Exchange with SHA-2"; + } + + identity gss-curve25519-sha256-1.3.132.0.27 { + base key-exchange-alg-base; + description + "GSS-CURVE25519-SHA256-1.3.132.0.27 (nistb233, sect233r1)"; + reference + "RFC 8732: + Generic Security Service Application Program Interface + (GSS-API) Key Exchange with SHA-2"; + } + + identity gss-curve25519-sha256-1.3.132.0.16 { + base key-exchange-alg-base; + description + "GSS-CURVE25519-SHA256-1.3.132.0.16 (nistk283, sect283k1)"; + reference + "RFC 8732: + Generic Security Service Application Program Interface + (GSS-API) Key Exchange with SHA-2"; + } + + identity gss-curve25519-sha256-1.3.132.0.36 { + base key-exchange-alg-base; + description + "GSS-CURVE25519-SHA256-1.3.132.0.36 (nistk409, sect409k1)"; + reference + "RFC 8732: + Generic Security Service Application Program Interface + (GSS-API) Key Exchange with SHA-2"; + } + + identity gss-curve25519-sha256-1.3.132.0.37 { + base key-exchange-alg-base; + description + "GSS-CURVE25519-SHA256-1.3.132.0.37 (nistb409, sect409r1)"; + reference + "RFC 8732: + Generic Security Service Application Program Interface + (GSS-API) Key Exchange with SHA-2"; + } + + identity gss-curve25519-sha256-1.3.132.0.38 { + base key-exchange-alg-base; + description + "GSS-CURVE25519-SHA256-1.3.132.0.38 (nistt571, sect571k1)"; + reference + "RFC 8732: + Generic Security Service Application Program Interface + (GSS-API) Key Exchange with SHA-2"; + } + identity gss-curve25519-sha256-curve25519-sha256 { + base key-exchange-alg-base; + description + "GSS-CURVE25519-SHA256-CURVE25519-SHA256"; + reference + "RFC 8732: + Generic Security Service Application Program Interface + (GSS-API) Key Exchange with SHA-2"; + } + + identity gss-curve25519-sha256-curve448-sha512 { + base key-exchange-alg-base; + description + "GSS-CURVE25519-SHA256-CURVE448-SHA512"; + reference + "RFC 8732: + Generic Security Service Application Program Interface + (GSS-API) Key Exchange with SHA-2"; + } + + identity gss-curve448-sha512-nistp256 { + base key-exchange-alg-base; + description + "GSS-CURVE448-SHA512-NISTP256 (secp256r1)"; + reference + "RFC 8732: + Generic Security Service Application Program Interface + (GSS-API) Key Exchange with SHA-2"; + } + + identity gss-curve448-sha512-nistp384 { + base key-exchange-alg-base; + description + "GSS-CURVE448-SHA512-NISTP384 (secp384r1)"; + reference + "RFC 8732: + Generic Security Service Application Program Interface + (GSS-API) Key Exchange with SHA-2"; + } + + identity gss-curve448-sha512-nistp521 { + base key-exchange-alg-base; + description + "GSS-CURVE448-SHA512-NISTP521 (secp521r1)"; + reference + "RFC 8732: + Generic Security Service Application Program Interface + (GSS-API) Key Exchange with SHA-2"; + } + + identity gss-curve448-sha512-1.3.132.0.1 { + base key-exchange-alg-base; + description + "GSS-CURVE448-SHA512-1.3.132.0.1 (nistk163, sect163k1)"; + reference + "RFC 8732: + Generic Security Service Application Program Interface + (GSS-API) Key Exchange with SHA-2"; + } + + identity gss-curve448-sha512-1.2.840.10045.3.1.1 { + base key-exchange-alg-base; + description + "GSS-CURVE448-SHA512-1.2.840.10045.3.1.1 (nistp192, secp192r1)"; + reference + "RFC 8732: + Generic Security Service Application Program Interface + (GSS-API) Key Exchange with SHA-2"; + } + + identity gss-curve448-sha512-1.3.132.0.33 { + base key-exchange-alg-base; + description + "GSS-CURVE448-SHA512-1.3.132.0.33 (nistp224, secp224r1)"; + reference + "RFC 8732: + Generic Security Service Application Program Interface + (GSS-API) Key Exchange with SHA-2"; + } + + identity gss-curve448-sha512-1.3.132.0.26 { + base key-exchange-alg-base; + description + "GSS-CURVE448-SHA512-1.3.132.0.26 (nistk233, sect233k1)"; + reference + "RFC 8732: + Generic Security Service Application Program Interface + (GSS-API) Key Exchange with SHA-2"; + } + + identity gss-curve448-sha512-1.3.132.0.27 { + base key-exchange-alg-base; + description + "GSS-CURVE448-SHA512-1.3.132.0.27 (nistb233, sect233r1)"; + reference + "RFC 8732: + Generic Security Service Application Program Interface + (GSS-API) Key Exchange with SHA-2"; + } + + identity gss-curve448-sha512-1.3.132.0.16 { + base key-exchange-alg-base; + description + "GSS-CURVE448-SHA512-1.3.132.0.16 (nistk283, sect283k1)"; + reference + "RFC 8732: + Generic Security Service Application Program Interface + (GSS-API) Key Exchange with SHA-2"; + } + + identity gss-curve448-sha512-1.3.132.0.36 { + base key-exchange-alg-base; + description + "GSS-CURVE448-SHA512-1.3.132.0.36 (nistk409, sect409k1)"; + reference + "RFC 8732: + Generic Security Service Application Program Interface + (GSS-API) Key Exchange with SHA-2"; + } + + identity gss-curve448-sha512-1.3.132.0.37 { + base key-exchange-alg-base; + description + "GSS-CURVE448-SHA512-1.3.132.0.37 (nistb409, sect409r1)"; + reference + "RFC 8732: + Generic Security Service Application Program Interface + (GSS-API) Key Exchange with SHA-2"; + } + + identity gss-curve448-sha512-1.3.132.0.38 { + base key-exchange-alg-base; + description + "GSS-CURVE448-SHA512-1.3.132.0.38 (nistt571, sect571k1)"; + reference + "RFC 8732: + Generic Security Service Application Program Interface + (GSS-API) Key Exchange with SHA-2"; + } + + identity gss-curve448-sha512-curve25519-sha256 { + base key-exchange-alg-base; + description + "GSS-CURVE448-SHA512-CURVE25519-SHA256"; + reference + "RFC 8732: + Generic Security Service Application Program Interface + (GSS-API) Key Exchange with SHA-2"; + } + + identity gss-curve448-sha512-curve448-sha512 { + base key-exchange-alg-base; + description + "GSS-CURVE448-SHA512-CURVE448-SHA512"; + reference + "RFC 8732: + Generic Security Service Application Program Interface + (GSS-API) Key Exchange with SHA-2"; + } + + identity curve25519-sha256 { + base key-exchange-alg-base; + description + "CURVE25519-SHA256"; + reference + "RFC 8731: + Secure Shell (SSH) Key Exchange Method + Using Curve25519 and Curve448"; + } + + identity curve448-sha512 { + base key-exchange-alg-base; + description + "CURVE448-SHA512"; + reference + "RFC 8731: + Secure Shell (SSH) Key Exchange Method + Using Curve25519 and Curve448"; + } + + // Protocol-accessible Nodes + + container supported-algorithms { + config false; + description + "A container for a list of key exchange algorithms + supported by the server."; + leaf-list supported-algorithm { + type key-exchange-algorithm-ref; + description + "A key exchange algorithm supported by the server."; + } + } + +} diff --git a/modules/iana-ssh-mac-algs@2022-06-16.yang b/modules/iana-ssh-mac-algs@2022-06-16.yang new file mode 100644 index 00000000..9cf3ae01 --- /dev/null +++ b/modules/iana-ssh-mac-algs@2022-06-16.yang @@ -0,0 +1,162 @@ +module iana-ssh-mac-algs { + yang-version 1.1; + namespace "urn:ietf:params:xml:ns:yang:iana-ssh-mac-algs"; + prefix sshma; + + organization + "Internet Assigned Numbers Authority (IANA)"; + + contact + "Postal: ICANN + 12025 Waterfront Drive, Suite 300 + Los Angeles, CA 90094-2536 + United States of America + Tel: +1 310 301 5800 + Email: iana@iana.org"; + + description + "This module defines identities for the MAC algorithms + defined in the 'MAC Algorithm Names' sub-registry of the + 'Secure Shell (SSH) Protocol Parameters' registry maintained + by IANA. + + Copyright (c) 2022 IETF Trust and the persons identified as + authors of the code. All rights reserved. + + Redistribution and use in source and binary forms, with + or without modification, is permitted pursuant to, and + subject to the license terms contained in, the Revised + BSD License set forth in Section 4.c of the IETF Trust's + Legal Provisions Relating to IETF Documents + (https://trustee.ietf.org/license-info). + + The initial version of this YANG module is part of RFC EEEE + (https://www.rfc-editor.org/info/rfcEEEE); see the RFC + itself for full legal notices."; + + revision 2022-06-16 { + description + "Reflects contents of the MAC algorithms registry on + June 16, 2022."; + reference + "RFC EEEE: YANG Groupings for SSH Clients and SSH Servers"; + } + + // Typedefs + + typedef mac-algorithm-ref { + type identityref { + base "mac-alg-base"; + } + description + "A reference to a SSH mac algorithm identifier."; + } + + // Identities + + identity mac-alg-base { + description + "Base identity used to identify message authentication + code (MAC) algorithms."; + } + + identity hmac-sha1 { + base mac-alg-base; + description + "HMAC-SHA1"; + reference + "RFC 4253: + The Secure Shell (SSH) Transport Layer Protocol"; + } + + identity hmac-sha1-96 { + base mac-alg-base; + description + "HMAC-SHA1-96"; + reference + "RFC 4253: + The Secure Shell (SSH) Transport Layer Protocol"; + } + + identity hmac-md5 { + base mac-alg-base; + description + "HMAC-MD5"; + reference + "RFC 4253: + The Secure Shell (SSH) Transport Layer Protocol"; + } + + identity hmac-md5-96 { + base mac-alg-base; + description + "HMAC-MD5-96"; + reference + "RFC 4253: + The Secure Shell (SSH) Transport Layer Protocol"; + } + + identity none { + base mac-alg-base; + description + "NONE"; + reference + "RFC 4253: + The Secure Shell (SSH) Transport Layer Protocol"; + } + + identity aead-aes-128-gcm { + base mac-alg-base; + description + "AEAD_AES_128_GCM"; + reference + "RFC 5647: + AES Galois Counter Mode for the + Secure Shell Transport Layer Protocol"; + } + + identity aead-aes-256-gcm { + base mac-alg-base; + description + "AEAD_AES_256_GCM"; + reference + "RFC 5647: + AES Galois Counter Mode for the + Secure Shell Transport Layer Protocol"; + } + + identity hmac-sha2-256 { + base mac-alg-base; + description + "HMAC-SHA2-256"; + reference + "RFC 6668: + SHA-2 Data Integrity Verification for the + Secure Shell (SSH) Transport Layer Protocol"; + } + + identity hmac-sha2-512 { + base mac-alg-base; + description + "HMAC-SHA2-512"; + reference + "RFC 6668: + SHA-2 Data Integrity Verification for the + Secure Shell (SSH) Transport Layer Protocol"; + } + + // Protocol-accessible Nodes + + container supported-algorithms { + config false; + description + "A container for a list of MAC algorithms + supported by the server."; + leaf-list supported-algorithm { + type mac-algorithm-ref; + description + "A MAC algorithm supported by the server."; + } + } + +} diff --git a/modules/iana-ssh-public-key-algs@2022-06-16.yang b/modules/iana-ssh-public-key-algs@2022-06-16.yang new file mode 100644 index 00000000..ae25bbc0 --- /dev/null +++ b/modules/iana-ssh-public-key-algs@2022-06-16.yang @@ -0,0 +1,436 @@ +module iana-ssh-public-key-algs { + yang-version 1.1; + namespace "urn:ietf:params:xml:ns:yang:iana-ssh-public-key-algs"; + prefix sshpka; + + organization + "Internet Assigned Numbers Authority (IANA)"; + contact + "Postal: ICANN + 12025 Waterfront Drive, Suite 300 + Los Angeles, CA 90094-2536 + United States of America + Tel: +1 310 301 5800 + Email: iana@iana.org"; + + description + "This module defines identities for the public key algorithms + defined in the 'Public Key Algorithm Names' sub-registry of the + 'Secure Shell (SSH) Protocol Parameters' registry maintained + by IANA. + + Copyright (c) 2022 IETF Trust and the persons identified as + authors of the code. All rights reserved. + + Redistribution and use in source and binary forms, with + or without modification, is permitted pursuant to, and + subject to the license terms contained in, the Revised + BSD License set forth in Section 4.c of the IETF Trust's + Legal Provisions Relating to IETF Documents + (https://trustee.ietf.org/license-info). + + The initial version of this YANG module is part of RFC EEEE + (https://www.rfc-editor.org/info/rfcEEEE); see the RFC + itself for full legal notices."; + + revision 2022-06-16 { + description + "Reflects contents of the public key algorithms registry + on June 16, 2022."; + reference + "RFC EEEE: YANG Groupings for SSH Clients and SSH Servers"; + } + + // Typedefs + + typedef public-key-algorithm-ref { + type identityref { + base "public-key-alg-base"; + } + description + "A reference to a SSH public key algorithm identifier."; + } + + // Identities + identity public-key-alg-base { + description + "Base identity used to identify public key algorithms."; + } + + identity ssh-dss { + base public-key-alg-base; + description + "SSH-DSS"; + reference + "RFC 4253: + The Secure Shell (SSH) Transport Layer Protocol"; + } + + identity ssh-rsa { + base public-key-alg-base; + description + "SSH-RSA"; + reference + "RFC 4253: + The Secure Shell (SSH) Transport Layer Protocol"; + } + + identity rsa-sha2-256 { + base public-key-alg-base; + description + "RSA-SHA2-256"; + reference + "RFC 8332: + Use of RSA Keys with SHA-256 and SHA-512 + in the Secure Shell (SSH) Protocol"; + } + + identity rsa-sha2-512 { + base public-key-alg-base; + description + "RSA-SHA2-512"; + reference + "RFC 8332: + Use of RSA Keys with SHA-256 and SHA-512 + in the Secure Shell (SSH) Protocol"; + } + + identity spki-sign-rsa { + base public-key-alg-base; + description + "SPKI-SIGN-RSA"; + reference + "RFC 4253: + The Secure Shell (SSH) Transport Layer Protocol"; + } + + identity spki-sign-dss { + base public-key-alg-base; + description + "SPKI-SIGN-DSS"; + reference + "RFC 4253: + The Secure Shell (SSH) Transport Layer Protocol"; + } + + identity pgp-sign-rsa { + base public-key-alg-base; + description + "PGP-SIGN-RSA"; + reference + "RFC 4253: + The Secure Shell (SSH) Transport Layer Protocol"; + } + + identity pgp-sign-dss { + base public-key-alg-base; + description + "PGP-SIGN-DSS"; + reference + "RFC 4253: + The Secure Shell (SSH) Transport Layer Protocol"; + } + + identity null { + base public-key-alg-base; + description + "NULL"; + reference + "RFC 4462: + Generic Security Service Application Program Interface + (GSS-API) Authentication and Key Exchange for the + Secure Shell (SSH) Protocol"; + } + + identity ecdsa-sha2-nistp256 { + base public-key-alg-base; + status deprecated; + description + "ECDSA-SHA2-NISTP256 (secp256r1)"; + reference + "RFC 5656: + Elliptic Curve Algorithm Integration in the + Secure Shell Transport Layer"; + } + + identity ecdsa-sha2-nistp384 { + base public-key-alg-base; + description + "ECDSA-SHA2-NISTP384 (secp384r1)"; + reference + "RFC 5656: + Elliptic Curve Algorithm Integration in the + Secure Shell Transport Layer"; + } + + identity ecdsa-sha2-nistp521 { + base public-key-alg-base; + description + "ECDSA-SHA2-NISTP521 (secp521r1)"; + reference + "RFC 5656: + Elliptic Curve Algorithm Integration in the + Secure Shell Transport Layer"; + } + + identity ecdsa-sha2-1.3.132.0.1 { + base public-key-alg-base; + description + "ECDSA-SHA2-1.3.132.0.1 (nistk163, sect163k1)"; + reference + "RFC 5656: + Elliptic Curve Algorithm Integration in the + Secure Shell Transport Layer"; + } + + identity ecdsa-sha2-1.2.840.10045.3.1.1 { + base public-key-alg-base; + description + "ECDSA-SHA2-1.2.840.10045.3.1.1 (nistp192, secp192r1)"; + reference + "RFC 5656: + Elliptic Curve Algorithm Integration in the + Secure Shell Transport Layer"; + } + + identity ecdsa-sha2-1.3.132.0.33 { + base public-key-alg-base; + description + "ECDSA-SHA2-1.3.132.0.33 (nistp224, secp224r1)"; + reference + "RFC 5656: + Elliptic Curve Algorithm Integration in the + Secure Shell Transport Layer"; + } + + identity ecdsa-sha2-1.3.132.0.26 { + base public-key-alg-base; + description + "ECDSA-SHA2-1.3.132.0.26 (nistk233, sect233k1)"; + reference + "RFC 5656: + Elliptic Curve Algorithm Integration in the + Secure Shell Transport Layer"; + } + + identity ecdsa-sha2-1.3.132.0.27 { + base public-key-alg-base; + description + "ECDSA-SHA2-1.3.132.0.27 (nistb233, sect233r1)"; + reference + "RFC 5656: + Elliptic Curve Algorithm Integration in the + Secure Shell Transport Layer"; + } + + identity ecdsa-sha2-1.3.132.0.16 { + base public-key-alg-base; + description + "ECDSA-SHA2-1.3.132.0.16 (nistk283, sect283k1)"; + reference + "RFC 5656: + Elliptic Curve Algorithm Integration in the + Secure Shell Transport Layer"; + } + + identity ecdsa-sha2-1.3.132.0.36 { + base public-key-alg-base; + description + "ECDSA-SHA2-1.3.132.0.36 (nistk409, sect409k1)"; + reference + "RFC 5656: + Elliptic Curve Algorithm Integration in the + Secure Shell Transport Layer"; + } + + identity ecdsa-sha2-1.3.132.0.37 { + base public-key-alg-base; + description + "ECDSA-SHA2-1.3.132.0.37 (nistb409, sect409r1)"; + reference + "RFC 5656: + Elliptic Curve Algorithm Integration in the + Secure Shell Transport Layer"; + } + + identity ecdsa-sha2-1.3.132.0.38 { + base public-key-alg-base; + description + "ECDSA-SHA2-1.3.132.0.38 (nistt571, sect571k1)"; + reference + "RFC 5656: + Elliptic Curve Algorithm Integration in the + Secure Shell Transport Layer"; + } + + identity x509v3-ssh-dss { + base public-key-alg-base; + description + "X509V3-SSH-DSS"; + reference + "RFC 6187: + X.509v3 Certificates for Secure Shell Authentication"; + } + + identity x509v3-ssh-rsa { + base public-key-alg-base; + description + "X509V3-SSH-RSA"; + reference + "RFC 6187: + X.509v3 Certificates for Secure Shell Authentication"; + } + + identity x509v3-rsa2048-sha256 { + base public-key-alg-base; + status deprecated; + description + "X509V3-RSA2048-SHA256"; + reference + "RFC 6187: + X.509v3 Certificates for Secure Shell Authentication"; + } + + identity x509v3-ecdsa-sha2-nistp256 { + base public-key-alg-base; + description + "X509V3-ECDSA-SHA2-NISTP256 (secp256r1)"; + reference + "RFC 6187: + X.509v3 Certificates for Secure Shell Authentication"; + } + + identity x509v3-ecdsa-sha2-nistp384 { + base public-key-alg-base; + description + "X509V3-ECDSA-SHA2-NISTP384 (secp384r1)"; + reference + "RFC 6187: + X.509v3 Certificates for Secure Shell Authentication"; + } + + identity x509v3-ecdsa-sha2-nistp521 { + base public-key-alg-base; + description + "X509V3-ECDSA-SHA2-NISTP521 (secp521r1)"; + reference + "RFC 6187: + X.509v3 Certificates for Secure Shell Authentication"; + } + + identity x509v3-ecdsa-sha2-1.3.132.0.1 { + base public-key-alg-base; + description + "X509V3-ECDSA-SHA2-1.3.132.0.1 (nistk163, sect163k1)"; + reference + "RFC 6187: + X.509v3 Certificates for Secure Shell Authentication"; + } + + identity x509v3-ecdsa-sha2-1.2.840.10045.3.1.1 { + base public-key-alg-base; + description + "X509V3-ECDSA-SHA2-1.2.840.10045.3.1.1 (nistp192, secp192r1)"; + reference + "RFC 6187: + X.509v3 Certificates for Secure Shell Authentication"; + } + + identity x509v3-ecdsa-sha2-1.3.132.0.33 { + base public-key-alg-base; + description + "X509V3-ECDSA-SHA2-1.3.132.0.33 (nistp224, secp224r1)"; + reference + "RFC 6187: + X.509v3 Certificates for Secure Shell Authentication"; + } + + identity x509v3-ecdsa-sha2-1.3.132.0.26 { + base public-key-alg-base; + description + "X509V3-ECDSA-SHA2-1.3.132.0.26 (nistk233, sect233k1)"; + reference + "RFC 6187: + X.509v3 Certificates for Secure Shell Authentication"; + } + + identity x509v3-ecdsa-sha2-1.3.132.0.27 { + base public-key-alg-base; + description + "X509V3-ECDSA-SHA2-1.3.132.0.27 (nistb233, sect233r1)"; + reference + "RFC 6187: + X.509v3 Certificates for Secure Shell Authentication"; + } + + identity x509v3-ecdsa-sha2-1.3.132.0.16 { + base public-key-alg-base; + description + "X509V3-ECDSA-SHA2-1.3.132.0.16 (nistk283, sect283k1)"; + reference + "RFC 6187: + X.509v3 Certificates for Secure Shell Authentication"; + } + + identity x509v3-ecdsa-sha2-1.3.132.0.36 { + base public-key-alg-base; + description + "X509V3-ECDSA-SHA2-1.3.132.0.36 (nistk409, sect409k1)"; + reference + "RFC 6187: + X.509v3 Certificates for Secure Shell Authentication"; + } + + identity x509v3-ecdsa-sha2-1.3.132.0.37 { + base public-key-alg-base; + description + "X509V3-ECDSA-SHA2-1.3.132.0.37 (nistb409, sect409r1)"; + reference + "RFC 6187: + X.509v3 Certificates for Secure Shell Authentication"; + } + + identity x509v3-ecdsa-sha2-1.3.132.0.38 { + base public-key-alg-base; + description + "X509V3-ECDSA-SHA2-1.3.132.0.38 (nistt571, sect571k1)"; + reference + "RFC 6187: + X.509v3 Certificates for Secure Shell Authentication"; + } + + identity ssh-ed25519 { + base public-key-alg-base; + description + "SSH-ED25519"; + reference + "RFC 8709: + Ed25519 and Ed448 Public Key Algorithms for the + Secure Shell (SSH) Protocol"; + } + + identity ssh-ed448 { + base public-key-alg-base; + description + "SSH-ED448"; + reference + "RFC 8709: + Ed25519 and Ed448 Public Key Algorithms for the + Secure Shell (SSH) Protocol"; + } + + // Protocol-accessible Nodes + + container supported-algorithms { + config false; + description + "A container for a list of public key algorithms + supported by the server."; + leaf-list supported-algorithm { + type public-key-algorithm-ref; + description + "A public key algorithm supported by the server."; + } + } + +} diff --git a/modules/iana-tls-cipher-suite-algs@2022-06-16.yang b/modules/iana-tls-cipher-suite-algs@2022-06-16.yang new file mode 100644 index 00000000..2b914d80 --- /dev/null +++ b/modules/iana-tls-cipher-suite-algs@2022-06-16.yang @@ -0,0 +1,3777 @@ +module iana-tls-cipher-suite-algs { + yang-version 1.1; + namespace "urn:ietf:params:xml:ns:yang:iana-tls-cipher-suite-algs"; + prefix tlscsa; + + organization + "Internet Assigned Numbers Authority (IANA)"; + + contact + "Postal: ICANN + 12025 Waterfront Drive, Suite 300 + Los Angeles, CA 90094-2536 + United States of America + Tel: +1 310 301 5800 + Email: iana@iana.org"; + + description + "This module defines identities for the Cipher Suite + algorithms defined in the 'TLS Cipher Suites' sub-registry + of the 'Transport Layer Security (TLS) Parameters' registry + maintained by IANA. + + Copyright (c) 2022 IETF Trust and the persons identified as + authors of the code. All rights reserved. + + Redistribution and use in source and binary forms, with + or without modification, is permitted pursuant to, and + subject to the license terms contained in, the Revised + BSD License set forth in Section 4.c of the IETF Trust's + Legal Provisions Relating to IETF Documents + (https://trustee.ietf.org/license-info). + + The initial version of this YANG module is part of RFC FFFF + (https://www.rfc-editor.org/info/rfcFFFF); see the RFC + itself for full legal notices."; + + revision 2022-06-16 { + description + "Reflect contents of the public key algorithms registry + on June 16, 2022."; + reference + "RFC FFFF: YANG Groupings for TLS Clients and TLS Servers"; + } + + // Typedefs + + typedef cipher-suite-algorithm-ref { + type identityref { + base "cipher-suite-alg-base"; + } + description + "A reference to a TLS cipher suite algorithm identifier."; + } + + // Identities + + identity cipher-suite-alg-base { + description + "Base identity used to identify TLS cipher suites."; + } + + identity tls-null-with-null-null { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-NULL-WITH-NULL-NULL"; + reference + "RFC 5246: + The Transport Layer Security (TLS) Protocol Version 1.2"; + } + + identity tls-rsa-with-null-md5 { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-RSA-WITH-NULL-MD5"; + reference + "RFC 5246: + The Transport Layer Security (TLS) Protocol Version 1.2"; + } + + identity tls-rsa-with-null-sha { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-RSA-WITH-NULL-SHA"; + reference + "RFC 5246: + The Transport Layer Security (TLS) Protocol Version 1.2"; + } + + identity tls-rsa-export-with-rc4-40-md5 { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-RSA-EXPORT-WITH-RC4-40-MD5"; + reference + "RFC 4346: + The TLS Protocol Version 1.1 + RFC 6347: + Datagram Transport Layer Security version 1.2"; + } + + identity tls-rsa-with-rc4-128-md5 { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-RSA-WITH-RC4-128-MD5"; + reference + "RFC 5246: + The Transport Layer Security (TLS) Protocol Version 1.2 + RFC 6347: + Datagram Transport Layer Security version 1.2"; + } + + identity tls-rsa-with-rc4-128-sha { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-RSA-WITH-RC4-128-SHA"; + reference + "RFC 5246: + The Transport Layer Security (TLS) Protocol Version 1.2 + RFC 6347: + Datagram Transport Layer Security version 1.2"; + } + + identity tls-rsa-export-with-rc2-cbc-40-md5 { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-RSA-EXPORT-WITH-RC2-CBC-40-MD5"; + reference + "RFC 4346: + The TLS Protocol Version 1.1"; + } + + identity tls-rsa-with-idea-cbc-sha { + base cipher-suite-alg-base; + status obsolete; + description + "TLS-RSA-WITH-IDEA-CBC-SHA"; + reference + "RFC 5469: + DES and IDEA Cipher Suites for + Transport Layer Security (TLS) + RFC 5469: + DES and IDEA Cipher Suites for + Transport Layer Security (TLS)"; + } + + identity tls-rsa-export-with-des40-cbc-sha { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-RSA-EXPORT-WITH-DES40-CBC-SHA"; + reference + "RFC 4346: + The TLS Protocol Version 1.1"; + } + + identity tls-rsa-with-des-cbc-sha { + base cipher-suite-alg-base; + status obsolete; + description + "TLS-RSA-WITH-DES-CBC-SHA"; + reference + "RFC 5469: + DES and IDEA Cipher Suites for + Transport Layer Security (TLS) + RFC 5469: + DES and IDEA Cipher Suites for + Transport Layer Security (TLS)"; + } + + identity tls-rsa-with-3des-ede-cbc-sha { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-RSA-WITH-3DES-EDE-CBC-SHA"; + reference + "RFC 5246: + The Transport Layer Security (TLS) Protocol Version 1.2"; + } + + identity tls-dh-dss-export-with-des40-cbc-sha { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-DH-DSS-EXPORT-WITH-DES40-CBC-SHA"; + reference + "RFC 4346: + The TLS Protocol Version 1.1"; + } + + identity tls-dh-dss-with-des-cbc-sha { + base cipher-suite-alg-base; + status obsolete; + description + "TLS-DH-DSS-WITH-DES-CBC-SHA"; + reference + "RFC 5469: + DES and IDEA Cipher Suites for + Transport Layer Security (TLS) + RFC 5469: + DES and IDEA Cipher Suites for + Transport Layer Security (TLS)"; + } + + identity tls-dh-dss-with-3des-ede-cbc-sha { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-DH-DSS-WITH-3DES-EDE-CBC-SHA"; + reference + "RFC 5246: + The Transport Layer Security (TLS) Protocol Version 1.2"; + } + + identity tls-dh-rsa-export-with-des40-cbc-sha { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-DH-RSA-EXPORT-WITH-DES40-CBC-SHA"; + reference + "RFC 4346: + The TLS Protocol Version 1.1"; + } + + identity tls-dh-rsa-with-des-cbc-sha { + base cipher-suite-alg-base; + status obsolete; + description + "TLS-DH-RSA-WITH-DES-CBC-SHA"; + reference + "RFC 5469: + DES and IDEA Cipher Suites for + Transport Layer Security (TLS) + RFC 5469: + DES and IDEA Cipher Suites for + Transport Layer Security (TLS)"; + } + + identity tls-dh-rsa-with-3des-ede-cbc-sha { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-DH-RSA-WITH-3DES-EDE-CBC-SHA"; + reference + "RFC 5246: + The Transport Layer Security (TLS) Protocol Version 1.2"; + } + + identity tls-dhe-dss-export-with-des40-cbc-sha { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-DHE-DSS-EXPORT-WITH-DES40-CBC-SHA"; + reference + "RFC 4346: + The TLS Protocol Version 1.1"; + } + + identity tls-dhe-dss-with-des-cbc-sha { + base cipher-suite-alg-base; + status obsolete; + description + "TLS-DHE-DSS-WITH-DES-CBC-SHA"; + reference + "RFC 5469: + DES and IDEA Cipher Suites for + Transport Layer Security (TLS) + RFC 5469: + DES and IDEA Cipher Suites for + Transport Layer Security (TLS)"; + } + + identity tls-dhe-dss-with-3des-ede-cbc-sha { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-DHE-DSS-WITH-3DES-EDE-CBC-SHA"; + reference + "RFC 5246: + The Transport Layer Security (TLS) Protocol Version 1.2"; + } + + identity tls-dhe-rsa-export-with-des40-cbc-sha { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-DHE-RSA-EXPORT-WITH-DES40-CBC-SHA"; + reference + "RFC 4346: + The TLS Protocol Version 1.1"; + } + + identity tls-dhe-rsa-with-des-cbc-sha { + base cipher-suite-alg-base; + status obsolete; + description + "TLS-DHE-RSA-WITH-DES-CBC-SHA"; + reference + "RFC 5469: + DES and IDEA Cipher Suites for + Transport Layer Security (TLS) + RFC 5469: + DES and IDEA Cipher Suites for + Transport Layer Security (TLS)"; + } + + identity tls-dhe-rsa-with-3des-ede-cbc-sha { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-DHE-RSA-WITH-3DES-EDE-CBC-SHA"; + reference + "RFC 5246: + The Transport Layer Security (TLS) Protocol Version 1.2"; + } + + identity tls-dh-anon-export-with-rc4-40-md5 { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-DH-ANON-EXPORT-WITH-RC4-40-MD5"; + reference + "RFC 4346: + The TLS Protocol Version 1.1 + RFC 6347: + Datagram Transport Layer Security version 1.2"; + } + + identity tls-dh-anon-with-rc4-128-md5 { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-DH-ANON-WITH-RC4-128-MD5"; + reference + "RFC 5246: + The Transport Layer Security (TLS) Protocol Version 1.2 + RFC 6347: + Datagram Transport Layer Security version 1.2"; + } + + identity tls-dh-anon-export-with-des40-cbc-sha { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-DH-ANON-EXPORT-WITH-DES40-CBC-SHA"; + reference + "RFC 4346: + The TLS Protocol Version 1.1"; + } + + identity tls-dh-anon-with-des-cbc-sha { + base cipher-suite-alg-base; + status obsolete; + description + "TLS-DH-ANON-WITH-DES-CBC-SHA"; + reference + "RFC 5469: + DES and IDEA Cipher Suites for + Transport Layer Security (TLS) + RFC 5469: + DES and IDEA Cipher Suites for + Transport Layer Security (TLS)"; + } + + identity tls-dh-anon-with-3des-ede-cbc-sha { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-DH-ANON-WITH-3DES-EDE-CBC-SHA"; + reference + "RFC 5246: + The Transport Layer Security (TLS) Protocol Version 1.2"; + } + + identity tls-krb5-with-des-cbc-sha { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-KRB5-WITH-DES-CBC-SHA"; + reference + "RFC 2712: + Addition of Kerberos Cipher Suites to + Transport Layer Security (TLS)"; + } + + identity tls-krb5-with-3des-ede-cbc-sha { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-KRB5-WITH-3DES-EDE-CBC-SHA"; + reference + "RFC 2712: + Addition of Kerberos Cipher Suites to + Transport Layer Security (TLS)"; + } + + identity tls-krb5-with-rc4-128-sha { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-KRB5-WITH-RC4-128-SHA"; + reference + "RFC 2712: + Addition of Kerberos Cipher Suites to + Transport Layer Security (TLS) + RFC 6347: + Datagram Transport Layer Security version 1.2"; + } + + identity tls-krb5-with-idea-cbc-sha { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-KRB5-WITH-IDEA-CBC-SHA"; + reference + "RFC 2712: + Addition of Kerberos Cipher Suites to + Transport Layer Security (TLS)"; + } + + identity tls-krb5-with-des-cbc-md5 { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-KRB5-WITH-DES-CBC-MD5"; + reference + "RFC 2712: + Addition of Kerberos Cipher Suites to + Transport Layer Security (TLS)"; + } + + identity tls-krb5-with-3des-ede-cbc-md5 { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-KRB5-WITH-3DES-EDE-CBC-MD5"; + reference + "RFC 2712: + Addition of Kerberos Cipher Suites to + Transport Layer Security (TLS)"; + } + + identity tls-krb5-with-rc4-128-md5 { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-KRB5-WITH-RC4-128-MD5"; + reference + "RFC 2712: + Addition of Kerberos Cipher Suites to + Transport Layer Security (TLS) + RFC 6347: + Datagram Transport Layer Security version 1.2"; + } + + identity tls-krb5-with-idea-cbc-md5 { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-KRB5-WITH-IDEA-CBC-MD5"; + reference + "RFC 2712: + Addition of Kerberos Cipher Suites to + Transport Layer Security (TLS)"; + } + + identity tls-krb5-export-with-des-cbc-40-sha { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-KRB5-EXPORT-WITH-DES-CBC-40-SHA"; + reference + "RFC 2712: + Addition of Kerberos Cipher Suites to + Transport Layer Security (TLS)"; + } + + identity tls-krb5-export-with-rc2-cbc-40-sha { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-KRB5-EXPORT-WITH-RC2-CBC-40-SHA"; + reference + "RFC 2712: + Addition of Kerberos Cipher Suites to + Transport Layer Security (TLS)"; + } + + identity tls-krb5-export-with-rc4-40-sha { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-KRB5-EXPORT-WITH-RC4-40-SHA"; + reference + "RFC 2712: + Addition of Kerberos Cipher Suites to + Transport Layer Security (TLS) + RFC 6347: + Datagram Transport Layer Security version 1.2"; + } + + identity tls-krb5-export-with-des-cbc-40-md5 { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-KRB5-EXPORT-WITH-DES-CBC-40-MD5"; + reference + "RFC 2712: + Addition of Kerberos Cipher Suites to + Transport Layer Security (TLS)"; + } + + identity tls-krb5-export-with-rc2-cbc-40-md5 { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-KRB5-EXPORT-WITH-RC2-CBC-40-MD5"; + reference + "RFC 2712: + Addition of Kerberos Cipher Suites to + Transport Layer Security (TLS)"; + } + identity tls-krb5-export-with-rc4-40-md5 { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-KRB5-EXPORT-WITH-RC4-40-MD5"; + reference + "RFC 2712: + Addition of Kerberos Cipher Suites to + Transport Layer Security (TLS) + RFC 6347: + Datagram Transport Layer Security version 1.2"; + } + + identity tls-psk-with-null-sha { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-PSK-WITH-NULL-SHA"; + reference + "RFC 4785: + Pre-Shared Key Cipher Suites with NULL Encryption for + Transport Layer Security (TLS)"; + } + + identity tls-dhe-psk-with-null-sha { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-DHE-PSK-WITH-NULL-SHA"; + reference + "RFC 4785: + Pre-Shared Key Cipher Suites with NULL Encryption for + Transport Layer Security (TLS)"; + } + + identity tls-rsa-psk-with-null-sha { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-RSA-PSK-WITH-NULL-SHA"; + reference + "RFC 4785: + Pre-Shared Key Cipher Suites with NULL Encryption for + Transport Layer Security (TLS)"; + } + + identity tls-rsa-with-aes-128-cbc-sha { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-RSA-WITH-AES-128-CBC-SHA"; + reference + "RFC 5246: + The Transport Layer Security (TLS) Protocol Version 1.2"; + } + + identity tls-dh-dss-with-aes-128-cbc-sha { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-DH-DSS-WITH-AES-128-CBC-SHA"; + reference + "RFC 5246: + The Transport Layer Security (TLS) Protocol Version 1.2"; + } + + identity tls-dh-rsa-with-aes-128-cbc-sha { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-DH-RSA-WITH-AES-128-CBC-SHA"; + reference + "RFC 5246: + The Transport Layer Security (TLS) Protocol Version 1.2"; + } + + identity tls-dhe-dss-with-aes-128-cbc-sha { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-DHE-DSS-WITH-AES-128-CBC-SHA"; + reference + "RFC 5246: + The Transport Layer Security (TLS) Protocol Version 1.2"; + } + + identity tls-dhe-rsa-with-aes-128-cbc-sha { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-DHE-RSA-WITH-AES-128-CBC-SHA"; + reference + "RFC 5246: + The Transport Layer Security (TLS) Protocol Version 1.2"; + } + + identity tls-dh-anon-with-aes-128-cbc-sha { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-DH-ANON-WITH-AES-128-CBC-SHA"; + reference + "RFC 5246: + The Transport Layer Security (TLS) Protocol Version 1.2"; + } + + identity tls-rsa-with-aes-256-cbc-sha { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-RSA-WITH-AES-256-CBC-SHA"; + reference + "RFC 5246: + The Transport Layer Security (TLS) Protocol Version 1.2"; + } + + identity tls-dh-dss-with-aes-256-cbc-sha { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-DH-DSS-WITH-AES-256-CBC-SHA"; + reference + "RFC 5246: + The Transport Layer Security (TLS) Protocol Version 1.2"; + } + + identity tls-dh-rsa-with-aes-256-cbc-sha { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-DH-RSA-WITH-AES-256-CBC-SHA"; + reference + "RFC 5246: + The Transport Layer Security (TLS) Protocol Version 1.2"; + } + + identity tls-dhe-dss-with-aes-256-cbc-sha { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-DHE-DSS-WITH-AES-256-CBC-SHA"; + reference + "RFC 5246: + The Transport Layer Security (TLS) Protocol Version 1.2"; + } + + identity tls-dhe-rsa-with-aes-256-cbc-sha { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-DHE-RSA-WITH-AES-256-CBC-SHA"; + reference + "RFC 5246: + The Transport Layer Security (TLS) Protocol Version 1.2"; + } + + identity tls-dh-anon-with-aes-256-cbc-sha { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-DH-ANON-WITH-AES-256-CBC-SHA"; + reference + "RFC 5246: + The Transport Layer Security (TLS) Protocol Version 1.2"; + } + + identity tls-rsa-with-null-sha256 { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-RSA-WITH-NULL-SHA256"; + reference + "RFC 5246: + The Transport Layer Security (TLS) Protocol Version 1.2"; + } + + identity tls-rsa-with-aes-128-cbc-sha256 { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-RSA-WITH-AES-128-CBC-SHA256"; + reference + "RFC 5246: + The Transport Layer Security (TLS) Protocol Version 1.2"; + } + + identity tls-rsa-with-aes-256-cbc-sha256 { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-RSA-WITH-AES-256-CBC-SHA256"; + reference + "RFC 5246: + The Transport Layer Security (TLS) Protocol Version 1.2"; + } + + identity tls-dh-dss-with-aes-128-cbc-sha256 { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-DH-DSS-WITH-AES-128-CBC-SHA256"; + reference + "RFC 5246: + The Transport Layer Security (TLS) Protocol Version 1.2"; + } + + identity tls-dh-rsa-with-aes-128-cbc-sha256 { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-DH-RSA-WITH-AES-128-CBC-SHA256"; + reference + "RFC 5246: + The Transport Layer Security (TLS) Protocol Version 1.2"; + } + + identity tls-dhe-dss-with-aes-128-cbc-sha256 { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-DHE-DSS-WITH-AES-128-CBC-SHA256"; + reference + "RFC 5246: + The Transport Layer Security (TLS) Protocol Version 1.2"; + } + + identity tls-rsa-with-camellia-128-cbc-sha { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-RSA-WITH-CAMELLIA-128-CBC-SHA"; + reference + "RFC 5932: + Camellia Cipher Suites for TLS"; + } + + identity tls-dh-dss-with-camellia-128-cbc-sha { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-DH-DSS-WITH-CAMELLIA-128-CBC-SHA"; + reference + "RFC 5932: + Camellia Cipher Suites for TLS"; + } + + identity tls-dh-rsa-with-camellia-128-cbc-sha { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-DH-RSA-WITH-CAMELLIA-128-CBC-SHA"; + reference + "RFC 5932: + Camellia Cipher Suites for TLS"; + } + + identity tls-dhe-dss-with-camellia-128-cbc-sha { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-DHE-DSS-WITH-CAMELLIA-128-CBC-SHA"; + reference + "RFC 5932: + Camellia Cipher Suites for TLS"; + } + + identity tls-dhe-rsa-with-camellia-128-cbc-sha { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-DHE-RSA-WITH-CAMELLIA-128-CBC-SHA"; + reference + "RFC 5932: + Camellia Cipher Suites for TLS"; + } + + identity tls-dh-anon-with-camellia-128-cbc-sha { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-DH-ANON-WITH-CAMELLIA-128-CBC-SHA"; + reference + "RFC 5932: + Camellia Cipher Suites for TLS"; + } + + identity tls-dhe-rsa-with-aes-128-cbc-sha256 { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-DHE-RSA-WITH-AES-128-CBC-SHA256"; + reference + "RFC 5246: + The Transport Layer Security (TLS) Protocol Version 1.2"; + } + + identity tls-dh-dss-with-aes-256-cbc-sha256 { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-DH-DSS-WITH-AES-256-CBC-SHA256"; + reference + "RFC 5246: + The Transport Layer Security (TLS) Protocol Version 1.2"; + } + + identity tls-dh-rsa-with-aes-256-cbc-sha256 { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-DH-RSA-WITH-AES-256-CBC-SHA256"; + reference + "RFC 5246: + The Transport Layer Security (TLS) Protocol Version 1.2"; + } + + identity tls-dhe-dss-with-aes-256-cbc-sha256 { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-DHE-DSS-WITH-AES-256-CBC-SHA256"; + reference + "RFC 5246: + The Transport Layer Security (TLS) Protocol Version 1.2"; + } + + identity tls-dhe-rsa-with-aes-256-cbc-sha256 { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-DHE-RSA-WITH-AES-256-CBC-SHA256"; + reference + "RFC 5246: + The Transport Layer Security (TLS) Protocol Version 1.2"; + } + + identity tls-dh-anon-with-aes-128-cbc-sha256 { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-DH-ANON-WITH-AES-128-CBC-SHA256"; + reference + "RFC 5246: + The Transport Layer Security (TLS) Protocol Version 1.2"; + } + + identity tls-dh-anon-with-aes-256-cbc-sha256 { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-DH-ANON-WITH-AES-256-CBC-SHA256"; + reference + "RFC 5246: + The Transport Layer Security (TLS) Protocol Version 1.2"; + } + + identity tls-rsa-with-camellia-256-cbc-sha { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-RSA-WITH-CAMELLIA-256-CBC-SHA"; + reference + "RFC 5932: + Camellia Cipher Suites for TLS"; + } + + identity tls-dh-dss-with-camellia-256-cbc-sha { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-DH-DSS-WITH-CAMELLIA-256-CBC-SHA"; + reference + "RFC 5932: + Camellia Cipher Suites for TLS"; + } + + identity tls-dh-rsa-with-camellia-256-cbc-sha { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-DH-RSA-WITH-CAMELLIA-256-CBC-SHA"; + reference + "RFC 5932: + Camellia Cipher Suites for TLS"; + } + + identity tls-dhe-dss-with-camellia-256-cbc-sha { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-DHE-DSS-WITH-CAMELLIA-256-CBC-SHA"; + reference + "RFC 5932: + Camellia Cipher Suites for TLS"; + } + + identity tls-dhe-rsa-with-camellia-256-cbc-sha { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-DHE-RSA-WITH-CAMELLIA-256-CBC-SHA"; + reference + "RFC 5932: + Camellia Cipher Suites for TLS"; + } + + identity tls-dh-anon-with-camellia-256-cbc-sha { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-DH-ANON-WITH-CAMELLIA-256-CBC-SHA"; + reference + "RFC 5932: + Camellia Cipher Suites for TLS"; + } + + identity tls-psk-with-rc4-128-sha { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-PSK-WITH-RC4-128-SHA"; + reference + "RFC 4279: + Pre-Shared Key Ciphersuites for + Transport Layer Security (TLS) + RFC 6347: + Datagram Transport Layer Security version 1.2"; + } + + identity tls-psk-with-3des-ede-cbc-sha { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-PSK-WITH-3DES-EDE-CBC-SHA"; + reference + "RFC 4279: + Pre-Shared Key Ciphersuites for + Transport Layer Security (TLS)"; + } + + identity tls-psk-with-aes-128-cbc-sha { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-PSK-WITH-AES-128-CBC-SHA"; + reference + "RFC 4279: + Pre-Shared Key Ciphersuites for + Transport Layer Security (TLS)"; + } + + identity tls-psk-with-aes-256-cbc-sha { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-PSK-WITH-AES-256-CBC-SHA"; + reference + "RFC 4279: + Pre-Shared Key Ciphersuites for + Transport Layer Security (TLS)"; + } + + identity tls-dhe-psk-with-rc4-128-sha { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-DHE-PSK-WITH-RC4-128-SHA"; + reference + "RFC 4279: + Pre-Shared Key Ciphersuites for + Transport Layer Security (TLS) + RFC 6347: + Datagram Transport Layer Security version 1.2"; + } + + identity tls-dhe-psk-with-3des-ede-cbc-sha { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-DHE-PSK-WITH-3DES-EDE-CBC-SHA"; + reference + "RFC 4279: + Pre-Shared Key Ciphersuites for + Transport Layer Security (TLS)"; + } + + identity tls-dhe-psk-with-aes-128-cbc-sha { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-DHE-PSK-WITH-AES-128-CBC-SHA"; + reference + "RFC 4279: + Pre-Shared Key Ciphersuites for + Transport Layer Security (TLS)"; + } + + identity tls-dhe-psk-with-aes-256-cbc-sha { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-DHE-PSK-WITH-AES-256-CBC-SHA"; + reference + "RFC 4279: + Pre-Shared Key Ciphersuites for + Transport Layer Security (TLS)"; + } + + identity tls-rsa-psk-with-rc4-128-sha { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-RSA-PSK-WITH-RC4-128-SHA"; + reference + "RFC 4279: + Pre-Shared Key Ciphersuites for + Transport Layer Security (TLS) + RFC 6347: + Datagram Transport Layer Security version 1.2"; + } + + identity tls-rsa-psk-with-3des-ede-cbc-sha { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-RSA-PSK-WITH-3DES-EDE-CBC-SHA"; + reference + "RFC 4279: + Pre-Shared Key Ciphersuites for + Transport Layer Security (TLS)"; + } + + identity tls-rsa-psk-with-aes-128-cbc-sha { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-RSA-PSK-WITH-AES-128-CBC-SHA"; + reference + "RFC 4279: + Pre-Shared Key Ciphersuites for + Transport Layer Security (TLS)"; + } + + identity tls-rsa-psk-with-aes-256-cbc-sha { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-RSA-PSK-WITH-AES-256-CBC-SHA"; + reference + "RFC 4279: + Pre-Shared Key Ciphersuites for + Transport Layer Security (TLS)"; + } + + identity tls-rsa-with-seed-cbc-sha { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-RSA-WITH-SEED-CBC-SHA"; + reference + "RFC 4162: + Addition of SEED Ciphersuites to + Transport Layer Security (TLS)"; + } + + identity tls-dh-dss-with-seed-cbc-sha { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-DH-DSS-WITH-SEED-CBC-SHA"; + reference + "RFC 4162: + Addition of SEED Ciphersuites to + Transport Layer Security (TLS)"; + } + + identity tls-dh-rsa-with-seed-cbc-sha { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-DH-RSA-WITH-SEED-CBC-SHA"; + reference + "RFC 4162: + Addition of SEED Ciphersuites to + Transport Layer Security (TLS)"; + } + + identity tls-dhe-dss-with-seed-cbc-sha { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-DHE-DSS-WITH-SEED-CBC-SHA"; + reference + "RFC 4162: + Addition of SEED Ciphersuites to + Transport Layer Security (TLS)"; + } + + identity tls-dhe-rsa-with-seed-cbc-sha { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-DHE-RSA-WITH-SEED-CBC-SHA"; + reference + "RFC 4162: + Addition of SEED Ciphersuites to + Transport Layer Security (TLS)"; + } + + identity tls-dh-anon-with-seed-cbc-sha { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-DH-ANON-WITH-SEED-CBC-SHA"; + reference + "RFC 4162: + Addition of SEED Ciphersuites to + Transport Layer Security (TLS)"; + } + + identity tls-rsa-with-aes-128-gcm-sha256 { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-RSA-WITH-AES-128-GCM-SHA256"; + reference + "RFC 5288: + AES-GCM Cipher Suites for TLS"; + } + + identity tls-rsa-with-aes-256-gcm-sha384 { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-RSA-WITH-AES-256-GCM-SHA384"; + reference + "RFC 5288: + AES-GCM Cipher Suites for TLS"; + } + + identity tls-dhe-rsa-with-aes-128-gcm-sha256 { + base cipher-suite-alg-base; + description + "TLS-DHE-RSA-WITH-AES-128-GCM-SHA256"; + reference + "RFC 5288: + AES-GCM Cipher Suites for TLS"; + } + + identity tls-dhe-rsa-with-aes-256-gcm-sha384 { + base cipher-suite-alg-base; + description + "TLS-DHE-RSA-WITH-AES-256-GCM-SHA384"; + reference + "RFC 5288: + AES-GCM Cipher Suites for TLS"; + } + + identity tls-dh-rsa-with-aes-128-gcm-sha256 { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-DH-RSA-WITH-AES-128-GCM-SHA256"; + reference + "RFC 5288: + AES-GCM Cipher Suites for TLS"; + } + + identity tls-dh-rsa-with-aes-256-gcm-sha384 { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-DH-RSA-WITH-AES-256-GCM-SHA384"; + reference + "RFC 5288: + AES-GCM Cipher Suites for TLS"; + } + + identity tls-dhe-dss-with-aes-128-gcm-sha256 { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-DHE-DSS-WITH-AES-128-GCM-SHA256"; + reference + "RFC 5288: + AES-GCM Cipher Suites for TLS"; + } + + identity tls-dhe-dss-with-aes-256-gcm-sha384 { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-DHE-DSS-WITH-AES-256-GCM-SHA384"; + reference + "RFC 5288: + AES-GCM Cipher Suites for TLS"; + } + + identity tls-dh-dss-with-aes-128-gcm-sha256 { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-DH-DSS-WITH-AES-128-GCM-SHA256"; + reference + "RFC 5288: + AES-GCM Cipher Suites for TLS"; + } + + identity tls-dh-dss-with-aes-256-gcm-sha384 { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-DH-DSS-WITH-AES-256-GCM-SHA384"; + reference + "RFC 5288: + AES-GCM Cipher Suites for TLS"; + } + + identity tls-dh-anon-with-aes-128-gcm-sha256 { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-DH-ANON-WITH-AES-128-GCM-SHA256"; + reference + "RFC 5288: + AES-GCM Cipher Suites for TLS"; + } + + identity tls-dh-anon-with-aes-256-gcm-sha384 { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-DH-ANON-WITH-AES-256-GCM-SHA384"; + reference + "RFC 5288: + AES-GCM Cipher Suites for TLS"; + } + + identity tls-psk-with-aes-128-gcm-sha256 { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-PSK-WITH-AES-128-GCM-SHA256"; + reference + "RFC 5487: + Pre-Shared Key Cipher Suites for Transport Layer Security + (TLS) with SHA-256/384 and AES Galois Counter Mode"; + } + + identity tls-psk-with-aes-256-gcm-sha384 { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-PSK-WITH-AES-256-GCM-SHA384"; + reference + "RFC 5487: + Pre-Shared Key Cipher Suites for Transport Layer Security + (TLS) with SHA-256/384 and AES Galois Counter Mode"; + } + + identity tls-dhe-psk-with-aes-128-gcm-sha256 { + base cipher-suite-alg-base; + description + "TLS-DHE-PSK-WITH-AES-128-GCM-SHA256"; + reference + "RFC 5487: + Pre-Shared Key Cipher Suites for Transport Layer Security + (TLS) with SHA-256/384 and AES Galois Counter Mode"; + } + + identity tls-dhe-psk-with-aes-256-gcm-sha384 { + base cipher-suite-alg-base; + description + "TLS-DHE-PSK-WITH-AES-256-GCM-SHA384"; + reference + "RFC 5487: + Pre-Shared Key Cipher Suites for Transport Layer Security + (TLS) with SHA-256/384 and AES Galois Counter Mode"; + } + + identity tls-rsa-psk-with-aes-128-gcm-sha256 { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-RSA-PSK-WITH-AES-128-GCM-SHA256"; + reference + "RFC 5487: + Pre-Shared Key Cipher Suites for Transport Layer Security + (TLS) with SHA-256/384 and AES Galois Counter Mode"; + } + + identity tls-rsa-psk-with-aes-256-gcm-sha384 { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-RSA-PSK-WITH-AES-256-GCM-SHA384"; + reference + "RFC 5487: + Pre-Shared Key Cipher Suites for Transport Layer Security + (TLS) with SHA-256/384 and AES Galois Counter Mode"; + } + + identity tls-psk-with-aes-128-cbc-sha256 { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-PSK-WITH-AES-128-CBC-SHA256"; + reference + "RFC 5487: + Pre-Shared Key Cipher Suites for Transport Layer Security + (TLS) with SHA-256/384 and AES Galois Counter Mode"; + } + + identity tls-psk-with-aes-256-cbc-sha384 { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-PSK-WITH-AES-256-CBC-SHA384"; + reference + "RFC 5487: + Pre-Shared Key Cipher Suites for Transport Layer Security + (TLS) with SHA-256/384 and AES Galois Counter Mode"; + } + + identity tls-psk-with-null-sha256 { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-PSK-WITH-NULL-SHA256"; + reference + "RFC 5487: + Pre-Shared Key Cipher Suites for Transport Layer Security + (TLS) with SHA-256/384 and AES Galois Counter Mode"; + } + + identity tls-psk-with-null-sha384 { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-PSK-WITH-NULL-SHA384"; + reference + "RFC 5487: + Pre-Shared Key Cipher Suites for Transport Layer Security + (TLS) with SHA-256/384 and AES Galois Counter Mode"; + } + + identity tls-dhe-psk-with-aes-128-cbc-sha256 { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-DHE-PSK-WITH-AES-128-CBC-SHA256"; + reference + "RFC 5487: + Pre-Shared Key Cipher Suites for Transport Layer Security + (TLS) with SHA-256/384 and AES Galois Counter Mode"; + } + + identity tls-dhe-psk-with-aes-256-cbc-sha384 { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-DHE-PSK-WITH-AES-256-CBC-SHA384"; + reference + "RFC 5487: + Pre-Shared Key Cipher Suites for Transport Layer Security + (TLS) with SHA-256/384 and AES Galois Counter Mode"; + } + + identity tls-dhe-psk-with-null-sha256 { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-DHE-PSK-WITH-NULL-SHA256"; + reference + "RFC 5487: + Pre-Shared Key Cipher Suites for Transport Layer Security + (TLS) with SHA-256/384 and AES Galois Counter Mode"; + } + + identity tls-dhe-psk-with-null-sha384 { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-DHE-PSK-WITH-NULL-SHA384"; + reference + "RFC 5487: + Pre-Shared Key Cipher Suites for Transport Layer Security + (TLS) with SHA-256/384 and AES Galois Counter Mode"; + } + + identity tls-rsa-psk-with-aes-128-cbc-sha256 { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-RSA-PSK-WITH-AES-128-CBC-SHA256"; + reference + "RFC 5487: + Pre-Shared Key Cipher Suites for Transport Layer Security + (TLS) with SHA-256/384 and AES Galois Counter Mode"; + } + + identity tls-rsa-psk-with-aes-256-cbc-sha384 { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-RSA-PSK-WITH-AES-256-CBC-SHA384"; + reference + "RFC 5487: + Pre-Shared Key Cipher Suites for Transport Layer Security + (TLS) with SHA-256/384 and AES Galois Counter Mode"; + } + + identity tls-rsa-psk-with-null-sha256 { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-RSA-PSK-WITH-NULL-SHA256"; + reference + "RFC 5487: + Pre-Shared Key Cipher Suites for Transport Layer Security + (TLS) with SHA-256/384 and AES Galois Counter Mode"; + } + + identity tls-rsa-psk-with-null-sha384 { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-RSA-PSK-WITH-NULL-SHA384"; + reference + "RFC 5487: + Pre-Shared Key Cipher Suites for Transport Layer Security + (TLS) with SHA-256/384 and AES Galois Counter Mode"; + } + + identity tls-rsa-with-camellia-128-cbc-sha256 { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-RSA-WITH-CAMELLIA-128-CBC-SHA256"; + reference + "RFC 5932: + Camellia Cipher Suites for TLS"; + } + + identity tls-dh-dss-with-camellia-128-cbc-sha256 { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-DH-DSS-WITH-CAMELLIA-128-CBC-SHA256"; + reference + "RFC 5932: + Camellia Cipher Suites for TLS"; + } + + identity tls-dh-rsa-with-camellia-128-cbc-sha256 { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-DH-RSA-WITH-CAMELLIA-128-CBC-SHA256"; + reference + "RFC 5932: + Camellia Cipher Suites for TLS"; + } + + identity tls-dhe-dss-with-camellia-128-cbc-sha256 { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-DHE-DSS-WITH-CAMELLIA-128-CBC-SHA256"; + reference + "RFC 5932: + Camellia Cipher Suites for TLS"; + } + + identity tls-dhe-rsa-with-camellia-128-cbc-sha256 { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-DHE-RSA-WITH-CAMELLIA-128-CBC-SHA256"; + reference + "RFC 5932: + Camellia Cipher Suites for TLS"; + } + + identity tls-dh-anon-with-camellia-128-cbc-sha256 { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-DH-ANON-WITH-CAMELLIA-128-CBC-SHA256"; + reference + "RFC 5932: + Camellia Cipher Suites for TLS"; + } + + identity tls-rsa-with-camellia-256-cbc-sha256 { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-RSA-WITH-CAMELLIA-256-CBC-SHA256"; + reference + "RFC 5932: + Camellia Cipher Suites for TLS"; + } + + identity tls-dh-dss-with-camellia-256-cbc-sha256 { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-DH-DSS-WITH-CAMELLIA-256-CBC-SHA256"; + reference + "RFC 5932: + Camellia Cipher Suites for TLS"; + } + + identity tls-dh-rsa-with-camellia-256-cbc-sha256 { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-DH-RSA-WITH-CAMELLIA-256-CBC-SHA256"; + reference + "RFC 5932: + Camellia Cipher Suites for TLS"; + } + + identity tls-dhe-dss-with-camellia-256-cbc-sha256 { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-DHE-DSS-WITH-CAMELLIA-256-CBC-SHA256"; + reference + "RFC 5932: + Camellia Cipher Suites for TLS"; + } + + identity tls-dhe-rsa-with-camellia-256-cbc-sha256 { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-DHE-RSA-WITH-CAMELLIA-256-CBC-SHA256"; + reference + "RFC 5932: + Camellia Cipher Suites for TLS"; + } + + identity tls-dh-anon-with-camellia-256-cbc-sha256 { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-DH-ANON-WITH-CAMELLIA-256-CBC-SHA256"; + reference + "RFC 5932: + Camellia Cipher Suites for TLS"; + } + + identity tls-sm4-gcm-sm3 { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-SM4-GCM-SM3"; + reference + "RFC 8998: + ShangMi (SM) Cipher Suites for Transport Layer Security + (TLS) Protocol Version 1.3"; + } + + identity tls-sm4-ccm-sm3 { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-SM4-CCM-SM3"; + reference + "RFC 8998: + ShangMi (SM) Cipher Suites for Transport Layer Security + (TLS) Protocol Version 1.3"; + } + + identity tls-empty-renegotiation-info-scsv { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-EMPTY-RENEGOTIATION-INFO-SCSV"; + reference + "RFC 5746: + Transport Layer Security (TLS) + Renegotiation Indication Extension"; + } + + identity tls-aes-128-gcm-sha256 { + base cipher-suite-alg-base; + description + "TLS-AES-128-GCM-SHA256"; + reference + "RFC 8446: + The Transport Layer Security (TLS) Protocol Version 1.3"; + } + + identity tls-aes-256-gcm-sha384 { + base cipher-suite-alg-base; + description + "TLS-AES-256-GCM-SHA384"; + reference + "RFC 8446: + The Transport Layer Security (TLS) Protocol Version 1.3"; + } + identity tls-chacha20-poly1305-sha256 { + base cipher-suite-alg-base; + description + "TLS-CHACHA20-POLY1305-SHA256"; + reference + "RFC 8446: + The Transport Layer Security (TLS) Protocol Version 1.3"; + } + + identity tls-aes-128-ccm-sha256 { + base cipher-suite-alg-base; + description + "TLS-AES-128-CCM-SHA256"; + reference + "RFC 8446: + The Transport Layer Security (TLS) Protocol Version 1.3"; + } + + identity tls-aes-128-ccm-8-sha256 { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-AES-128-CCM-8-SHA256"; + reference + "RFC 8446: + The Transport Layer Security (TLS) Protocol Version 1.3"; + } + + identity tls-fallback-scsv { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-FALLBACK-SCSV"; + reference + "RFC 7507: + TLS Fallback Signaling Cipher Suite Value (SCSV) + for Preventing Protocol Downgrade Attacks"; + } + + identity tls-ecdh-ecdsa-with-null-sha { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-ECDH-ECDSA-WITH-NULL-SHA"; + reference + "RFC 8422: + Elliptic Curve Cryptography (ECC) Cipher Suites for + Transport Layer Security (TLS) Versions 1.2 and Earlier"; + } + + identity tls-ecdh-ecdsa-with-rc4-128-sha { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-ECDH-ECDSA-WITH-RC4-128-SHA"; + reference + "RFC 8422: + Elliptic Curve Cryptography (ECC) Cipher Suites for + Transport Layer Security (TLS) Versions 1.2 and Earlier + RFC 6347: + Datagram Transport Layer Security version 1.2"; + } + + identity tls-ecdh-ecdsa-with-3des-ede-cbc-sha { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-ECDH-ECDSA-WITH-3DES-EDE-CBC-SHA"; + reference + "RFC 8422: + Elliptic Curve Cryptography (ECC) Cipher Suites for + Transport Layer Security (TLS) Versions 1.2 and Earlier"; + } + + identity tls-ecdh-ecdsa-with-aes-128-cbc-sha { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-ECDH-ECDSA-WITH-AES-128-CBC-SHA"; + reference + "RFC 8422: + Elliptic Curve Cryptography (ECC) Cipher Suites for + Transport Layer Security (TLS) Versions 1.2 and Earlier"; + } + + identity tls-ecdh-ecdsa-with-aes-256-cbc-sha { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-ECDH-ECDSA-WITH-AES-256-CBC-SHA"; + reference + "RFC 8422: + Elliptic Curve Cryptography (ECC) Cipher Suites for + Transport Layer Security (TLS) Versions 1.2 and Earlier"; + } + + identity tls-ecdhe-ecdsa-with-null-sha { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-ECDHE-ECDSA-WITH-NULL-SHA"; + reference + "RFC 8422: + Elliptic Curve Cryptography (ECC) Cipher Suites for + Transport Layer Security (TLS) Versions 1.2 and Earlier"; + } + + identity tls-ecdhe-ecdsa-with-rc4-128-sha { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-ECDHE-ECDSA-WITH-RC4-128-SHA"; + reference + "RFC 8422: + Elliptic Curve Cryptography (ECC) Cipher Suites for + Transport Layer Security (TLS) Versions 1.2 and Earlier + RFC 6347: + Datagram Transport Layer Security version 1.2"; + } + + identity tls-ecdhe-ecdsa-with-3des-ede-cbc-sha { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-ECDHE-ECDSA-WITH-3DES-EDE-CBC-SHA"; + reference + "RFC 8422: + Elliptic Curve Cryptography (ECC) Cipher Suites for + Transport Layer Security (TLS) Versions 1.2 and Earlier"; + } + + identity tls-ecdhe-ecdsa-with-aes-128-cbc-sha { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-ECDHE-ECDSA-WITH-AES-128-CBC-SHA"; + reference + "RFC 8422: + Elliptic Curve Cryptography (ECC) Cipher Suites for + Transport Layer Security (TLS) Versions 1.2 and Earlier"; + } + + identity tls-ecdhe-ecdsa-with-aes-256-cbc-sha { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-ECDHE-ECDSA-WITH-AES-256-CBC-SHA"; + reference + "RFC 8422: + Elliptic Curve Cryptography (ECC) Cipher Suites for + Transport Layer Security (TLS) Versions 1.2 and Earlier"; + } + + identity tls-ecdh-rsa-with-null-sha { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-ECDH-RSA-WITH-NULL-SHA"; + reference + "RFC 8422: + Elliptic Curve Cryptography (ECC) Cipher Suites for + Transport Layer Security (TLS) Versions 1.2 and Earlier"; + } + + identity tls-ecdh-rsa-with-rc4-128-sha { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-ECDH-RSA-WITH-RC4-128-SHA"; + reference + "RFC 8422: + Elliptic Curve Cryptography (ECC) Cipher Suites for + Transport Layer Security (TLS) Versions 1.2 and Earlier + RFC 6347: + Datagram Transport Layer Security version 1.2"; + } + + identity tls-ecdh-rsa-with-3des-ede-cbc-sha { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-ECDH-RSA-WITH-3DES-EDE-CBC-SHA"; + reference + "RFC 8422: + Elliptic Curve Cryptography (ECC) Cipher Suites for + Transport Layer Security (TLS) Versions 1.2 and Earlier"; + } + + identity tls-ecdh-rsa-with-aes-128-cbc-sha { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-ECDH-RSA-WITH-AES-128-CBC-SHA"; + reference + "RFC 8422: + Elliptic Curve Cryptography (ECC) Cipher Suites for + Transport Layer Security (TLS) Versions 1.2 and Earlier"; + } + + identity tls-ecdh-rsa-with-aes-256-cbc-sha { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-ECDH-RSA-WITH-AES-256-CBC-SHA"; + reference + "RFC 8422: + Elliptic Curve Cryptography (ECC) Cipher Suites for + Transport Layer Security (TLS) Versions 1.2 and Earlier"; + } + + identity tls-ecdhe-rsa-with-null-sha { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-ECDHE-RSA-WITH-NULL-SHA"; + reference + "RFC 8422: + Elliptic Curve Cryptography (ECC) Cipher Suites for + Transport Layer Security (TLS) Versions 1.2 and Earlier"; + } + + identity tls-ecdhe-rsa-with-rc4-128-sha { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-ECDHE-RSA-WITH-RC4-128-SHA"; + reference + "RFC 8422: + Elliptic Curve Cryptography (ECC) Cipher Suites for + Transport Layer Security (TLS) Versions 1.2 and Earlier + RFC 6347: + Datagram Transport Layer Security version 1.2"; + } + + identity tls-ecdhe-rsa-with-3des-ede-cbc-sha { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-ECDHE-RSA-WITH-3DES-EDE-CBC-SHA"; + reference + "RFC 8422: + Elliptic Curve Cryptography (ECC) Cipher Suites for + Transport Layer Security (TLS) Versions 1.2 and Earlier"; + } + + identity tls-ecdhe-rsa-with-aes-128-cbc-sha { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-ECDHE-RSA-WITH-AES-128-CBC-SHA"; + reference + "RFC 8422: + Elliptic Curve Cryptography (ECC) Cipher Suites for + Transport Layer Security (TLS) Versions 1.2 and Earlier"; + } + + identity tls-ecdhe-rsa-with-aes-256-cbc-sha { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-ECDHE-RSA-WITH-AES-256-CBC-SHA"; + reference + "RFC 8422: + Elliptic Curve Cryptography (ECC) Cipher Suites for + Transport Layer Security (TLS) Versions 1.2 and Earlier"; + } + + identity tls-ecdh-anon-with-null-sha { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-ECDH-ANON-WITH-NULL-SHA"; + reference + "RFC 8422: + Elliptic Curve Cryptography (ECC) Cipher Suites for + Transport Layer Security (TLS) Versions 1.2 and Earlier"; + } + + identity tls-ecdh-anon-with-rc4-128-sha { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-ECDH-ANON-WITH-RC4-128-SHA"; + reference + "RFC 8422: + Elliptic Curve Cryptography (ECC) Cipher Suites for + Transport Layer Security (TLS) Versions 1.2 and Earlier + RFC 6347: + Datagram Transport Layer Security version 1.2"; + } + + identity tls-ecdh-anon-with-3des-ede-cbc-sha { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-ECDH-ANON-WITH-3DES-EDE-CBC-SHA"; + reference + "RFC 8422: + Elliptic Curve Cryptography (ECC) Cipher Suites for + Transport Layer Security (TLS) Versions 1.2 and Earlier"; + } + + identity tls-ecdh-anon-with-aes-128-cbc-sha { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-ECDH-ANON-WITH-AES-128-CBC-SHA"; + reference + "RFC 8422: + Elliptic Curve Cryptography (ECC) Cipher Suites for + Transport Layer Security (TLS) Versions 1.2 and Earlier"; + } + + identity tls-ecdh-anon-with-aes-256-cbc-sha { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-ECDH-ANON-WITH-AES-256-CBC-SHA"; + reference + "RFC 8422: + Elliptic Curve Cryptography (ECC) Cipher Suites for + Transport Layer Security (TLS) Versions 1.2 and Earlier"; + } + + identity tls-srp-sha-with-3des-ede-cbc-sha { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-SRP-SHA-WITH-3DES-EDE-CBC-SHA"; + reference + "RFC 5054: + Using SRP for TLS Authentication"; + } + + identity tls-srp-sha-rsa-with-3des-ede-cbc-sha { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-SRP-SHA-RSA-WITH-3DES-EDE-CBC-SHA"; + reference + "RFC 5054: + Using SRP for TLS Authentication"; + } + + identity tls-srp-sha-dss-with-3des-ede-cbc-sha { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-SRP-SHA-DSS-WITH-3DES-EDE-CBC-SHA"; + reference + "RFC 5054: + Using SRP for TLS Authentication"; + } + + identity tls-srp-sha-with-aes-128-cbc-sha { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-SRP-SHA-WITH-AES-128-CBC-SHA"; + reference + "RFC 5054: + Using SRP for TLS Authentication"; + } + + identity tls-srp-sha-rsa-with-aes-128-cbc-sha { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-SRP-SHA-RSA-WITH-AES-128-CBC-SHA"; + reference + "RFC 5054: + Using SRP for TLS Authentication"; + } + + identity tls-srp-sha-dss-with-aes-128-cbc-sha { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-SRP-SHA-DSS-WITH-AES-128-CBC-SHA"; + reference + "RFC 5054: + Using SRP for TLS Authentication"; + } + + identity tls-srp-sha-with-aes-256-cbc-sha { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-SRP-SHA-WITH-AES-256-CBC-SHA"; + reference + "RFC 5054: + Using SRP for TLS Authentication"; + } + + identity tls-srp-sha-rsa-with-aes-256-cbc-sha { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-SRP-SHA-RSA-WITH-AES-256-CBC-SHA"; + reference + "RFC 5054: + Using SRP for TLS Authentication"; + } + + identity tls-srp-sha-dss-with-aes-256-cbc-sha { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-SRP-SHA-DSS-WITH-AES-256-CBC-SHA"; + reference + "RFC 5054: + Using SRP for TLS Authentication"; + } + + identity tls-ecdhe-ecdsa-with-aes-128-cbc-sha256 { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-ECDHE-ECDSA-WITH-AES-128-CBC-SHA256"; + reference + "RFC 5289: + TLS Elliptic Curve Cipher Suites with SHA-256/384 + and AES Galois Counter Mode"; + } + + identity tls-ecdhe-ecdsa-with-aes-256-cbc-sha384 { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-ECDHE-ECDSA-WITH-AES-256-CBC-SHA384"; + reference + "RFC 5289: + TLS Elliptic Curve Cipher Suites with SHA-256/384 + and AES Galois Counter Mode"; + } + + identity tls-ecdh-ecdsa-with-aes-128-cbc-sha256 { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-ECDH-ECDSA-WITH-AES-128-CBC-SHA256"; + reference + "RFC 5289: + TLS Elliptic Curve Cipher Suites with SHA-256/384 + and AES Galois Counter Mode"; + } + + identity tls-ecdh-ecdsa-with-aes-256-cbc-sha384 { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-ECDH-ECDSA-WITH-AES-256-CBC-SHA384"; + reference + "RFC 5289: + TLS Elliptic Curve Cipher Suites with SHA-256/384 + and AES Galois Counter Mode"; + } + + identity tls-ecdhe-rsa-with-aes-128-cbc-sha256 { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-ECDHE-RSA-WITH-AES-128-CBC-SHA256"; + reference + "RFC 5289: + TLS Elliptic Curve Cipher Suites with SHA-256/384 + and AES Galois Counter Mode"; + } + + identity tls-ecdhe-rsa-with-aes-256-cbc-sha384 { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-ECDHE-RSA-WITH-AES-256-CBC-SHA384"; + reference + "RFC 5289: + TLS Elliptic Curve Cipher Suites with SHA-256/384 + and AES Galois Counter Mode"; + } + + identity tls-ecdh-rsa-with-aes-128-cbc-sha256 { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-ECDH-RSA-WITH-AES-128-CBC-SHA256"; + reference + "RFC 5289: + TLS Elliptic Curve Cipher Suites with SHA-256/384 + and AES Galois Counter Mode"; + } + + identity tls-ecdh-rsa-with-aes-256-cbc-sha384 { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-ECDH-RSA-WITH-AES-256-CBC-SHA384"; + reference + "RFC 5289: + TLS Elliptic Curve Cipher Suites with SHA-256/384 + and AES Galois Counter Mode"; + } + + identity tls-ecdhe-ecdsa-with-aes-128-gcm-sha256 { + base cipher-suite-alg-base; + description + "TLS-ECDHE-ECDSA-WITH-AES-128-GCM-SHA256"; + reference + "RFC 5289: + TLS Elliptic Curve Cipher Suites with SHA-256/384 + and AES Galois Counter Mode"; + } + + identity tls-ecdhe-ecdsa-with-aes-256-gcm-sha384 { + base cipher-suite-alg-base; + description + "TLS-ECDHE-ECDSA-WITH-AES-256-GCM-SHA384"; + reference + "RFC 5289: + TLS Elliptic Curve Cipher Suites with SHA-256/384 + and AES Galois Counter Mode"; + } + + identity tls-ecdh-ecdsa-with-aes-128-gcm-sha256 { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-ECDH-ECDSA-WITH-AES-128-GCM-SHA256"; + reference + "RFC 5289: + TLS Elliptic Curve Cipher Suites with SHA-256/384 + and AES Galois Counter Mode"; + } + + identity tls-ecdh-ecdsa-with-aes-256-gcm-sha384 { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-ECDH-ECDSA-WITH-AES-256-GCM-SHA384"; + reference + "RFC 5289: + TLS Elliptic Curve Cipher Suites with SHA-256/384 + and AES Galois Counter Mode"; + } + + identity tls-ecdhe-rsa-with-aes-128-gcm-sha256 { + base cipher-suite-alg-base; + description + "TLS-ECDHE-RSA-WITH-AES-128-GCM-SHA256"; + reference + "RFC 5289: + TLS Elliptic Curve Cipher Suites with SHA-256/384 + and AES Galois Counter Mode"; + } + + identity tls-ecdhe-rsa-with-aes-256-gcm-sha384 { + base cipher-suite-alg-base; + description + "TLS-ECDHE-RSA-WITH-AES-256-GCM-SHA384"; + reference + "RFC 5289: + TLS Elliptic Curve Cipher Suites with SHA-256/384 + and AES Galois Counter Mode"; + } + + identity tls-ecdh-rsa-with-aes-128-gcm-sha256 { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-ECDH-RSA-WITH-AES-128-GCM-SHA256"; + reference + "RFC 5289: + TLS Elliptic Curve Cipher Suites with SHA-256/384 + and AES Galois Counter Mode"; + } + + identity tls-ecdh-rsa-with-aes-256-gcm-sha384 { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-ECDH-RSA-WITH-AES-256-GCM-SHA384"; + reference + "RFC 5289: + TLS Elliptic Curve Cipher Suites with SHA-256/384 + and AES Galois Counter Mode"; + } + + identity tls-ecdhe-psk-with-rc4-128-sha { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-ECDHE-PSK-WITH-RC4-128-SHA"; + reference + "RFC 5489: + ECDHE_PSK Ciphersuites for Transport Layer Security (TLS) + RFC 6347: + Datagram Transport Layer Security version 1.2"; + } + + identity tls-ecdhe-psk-with-3des-ede-cbc-sha { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-ECDHE-PSK-WITH-3DES-EDE-CBC-SHA"; + reference + "RFC 5489: + ECDHE_PSK Ciphersuites for Transport Layer Security (TLS)"; + } + + identity tls-ecdhe-psk-with-aes-128-cbc-sha { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-ECDHE-PSK-WITH-AES-128-CBC-SHA"; + reference + "RFC 5489: + ECDHE_PSK Ciphersuites for Transport Layer Security (TLS)"; + } + + identity tls-ecdhe-psk-with-aes-256-cbc-sha { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-ECDHE-PSK-WITH-AES-256-CBC-SHA"; + reference + "RFC 5489: + ECDHE_PSK Ciphersuites for Transport Layer Security (TLS)"; + } + + identity tls-ecdhe-psk-with-aes-128-cbc-sha256 { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-ECDHE-PSK-WITH-AES-128-CBC-SHA256"; + reference + "RFC 5489: + ECDHE_PSK Ciphersuites for Transport Layer Security (TLS)"; + } + + identity tls-ecdhe-psk-with-aes-256-cbc-sha384 { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-ECDHE-PSK-WITH-AES-256-CBC-SHA384"; + reference + "RFC 5489: + ECDHE_PSK Ciphersuites for Transport Layer Security (TLS)"; + } + + identity tls-ecdhe-psk-with-null-sha { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-ECDHE-PSK-WITH-NULL-SHA"; + reference + "RFC 5489: + ECDHE_PSK Ciphersuites for Transport Layer Security (TLS)"; + } + + identity tls-ecdhe-psk-with-null-sha256 { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-ECDHE-PSK-WITH-NULL-SHA256"; + reference + "RFC 5489: + ECDHE_PSK Ciphersuites for Transport Layer Security (TLS)"; + } + + identity tls-ecdhe-psk-with-null-sha384 { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-ECDHE-PSK-WITH-NULL-SHA384"; + reference + "RFC 5489: + ECDHE_PSK Ciphersuites for Transport Layer Security (TLS)"; + } + + identity tls-rsa-with-aria-128-cbc-sha256 { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-RSA-WITH-ARIA-128-CBC-SHA256"; + reference + "RFC 6209: + Addition of the ARIA Cipher Suites to + Transport Layer Security (TLS)"; + } + + identity tls-rsa-with-aria-256-cbc-sha384 { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-RSA-WITH-ARIA-256-CBC-SHA384"; + reference + "RFC 6209: + Addition of the ARIA Cipher Suites to + Transport Layer Security (TLS)"; + } + + identity tls-dh-dss-with-aria-128-cbc-sha256 { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-DH-DSS-WITH-ARIA-128-CBC-SHA256"; + reference + "RFC 6209: + Addition of the ARIA Cipher Suites to + Transport Layer Security (TLS)"; + } + + identity tls-dh-dss-with-aria-256-cbc-sha384 { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-DH-DSS-WITH-ARIA-256-CBC-SHA384"; + reference + "RFC 6209: + Addition of the ARIA Cipher Suites to + Transport Layer Security (TLS)"; + } + + identity tls-dh-rsa-with-aria-128-cbc-sha256 { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-DH-RSA-WITH-ARIA-128-CBC-SHA256"; + reference + "RFC 6209: + Addition of the ARIA Cipher Suites to + Transport Layer Security (TLS)"; + } + + identity tls-dh-rsa-with-aria-256-cbc-sha384 { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-DH-RSA-WITH-ARIA-256-CBC-SHA384"; + reference + "RFC 6209: + Addition of the ARIA Cipher Suites to + Transport Layer Security (TLS)"; + } + + identity tls-dhe-dss-with-aria-128-cbc-sha256 { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-DHE-DSS-WITH-ARIA-128-CBC-SHA256"; + reference + "RFC 6209: + Addition of the ARIA Cipher Suites to + Transport Layer Security (TLS)"; + } + + identity tls-dhe-dss-with-aria-256-cbc-sha384 { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-DHE-DSS-WITH-ARIA-256-CBC-SHA384"; + reference + "RFC 6209: + Addition of the ARIA Cipher Suites to + Transport Layer Security (TLS)"; + } + + identity tls-dhe-rsa-with-aria-128-cbc-sha256 { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-DHE-RSA-WITH-ARIA-128-CBC-SHA256"; + reference + "RFC 6209: + Addition of the ARIA Cipher Suites to + Transport Layer Security (TLS)"; + } + + identity tls-dhe-rsa-with-aria-256-cbc-sha384 { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-DHE-RSA-WITH-ARIA-256-CBC-SHA384"; + reference + "RFC 6209: + Addition of the ARIA Cipher Suites to + Transport Layer Security (TLS)"; + } + + identity tls-dh-anon-with-aria-128-cbc-sha256 { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-DH-ANON-WITH-ARIA-128-CBC-SHA256"; + reference + "RFC 6209: + Addition of the ARIA Cipher Suites to + Transport Layer Security (TLS)"; + } + + identity tls-dh-anon-with-aria-256-cbc-sha384 { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-DH-ANON-WITH-ARIA-256-CBC-SHA384"; + reference + "RFC 6209: + Addition of the ARIA Cipher Suites to + Transport Layer Security (TLS)"; + } + + identity tls-ecdhe-ecdsa-with-aria-128-cbc-sha256 { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-ECDHE-ECDSA-WITH-ARIA-128-CBC-SHA256"; + reference + "RFC 6209: + Addition of the ARIA Cipher Suites to + Transport Layer Security (TLS)"; + } + + identity tls-ecdhe-ecdsa-with-aria-256-cbc-sha384 { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-ECDHE-ECDSA-WITH-ARIA-256-CBC-SHA384"; + reference + "RFC 6209: + Addition of the ARIA Cipher Suites to + Transport Layer Security (TLS)"; + } + + identity tls-ecdh-ecdsa-with-aria-128-cbc-sha256 { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-ECDH-ECDSA-WITH-ARIA-128-CBC-SHA256"; + reference + "RFC 6209: + Addition of the ARIA Cipher Suites to + Transport Layer Security (TLS)"; + } + + identity tls-ecdh-ecdsa-with-aria-256-cbc-sha384 { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-ECDH-ECDSA-WITH-ARIA-256-CBC-SHA384"; + reference + "RFC 6209: + Addition of the ARIA Cipher Suites to + Transport Layer Security (TLS)"; + } + + identity tls-ecdhe-rsa-with-aria-128-cbc-sha256 { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-ECDHE-RSA-WITH-ARIA-128-CBC-SHA256"; + reference + "RFC 6209: + Addition of the ARIA Cipher Suites to + Transport Layer Security (TLS)"; + } + identity tls-ecdhe-rsa-with-aria-256-cbc-sha384 { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-ECDHE-RSA-WITH-ARIA-256-CBC-SHA384"; + reference + "RFC 6209: + Addition of the ARIA Cipher Suites to + Transport Layer Security (TLS)"; + } + + identity tls-ecdh-rsa-with-aria-128-cbc-sha256 { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-ECDH-RSA-WITH-ARIA-128-CBC-SHA256"; + reference + "RFC 6209: + Addition of the ARIA Cipher Suites to + Transport Layer Security (TLS)"; + } + + identity tls-ecdh-rsa-with-aria-256-cbc-sha384 { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-ECDH-RSA-WITH-ARIA-256-CBC-SHA384"; + reference + "RFC 6209: + Addition of the ARIA Cipher Suites to + Transport Layer Security (TLS)"; + } + + identity tls-rsa-with-aria-128-gcm-sha256 { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-RSA-WITH-ARIA-128-GCM-SHA256"; + reference + "RFC 6209: + Addition of the ARIA Cipher Suites to + Transport Layer Security (TLS)"; + } + + identity tls-rsa-with-aria-256-gcm-sha384 { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-RSA-WITH-ARIA-256-GCM-SHA384"; + reference + "RFC 6209: + Addition of the ARIA Cipher Suites to + Transport Layer Security (TLS)"; + } + + identity tls-dhe-rsa-with-aria-128-gcm-sha256 { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-DHE-RSA-WITH-ARIA-128-GCM-SHA256"; + reference + "RFC 6209: + Addition of the ARIA Cipher Suites to + Transport Layer Security (TLS)"; + } + + identity tls-dhe-rsa-with-aria-256-gcm-sha384 { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-DHE-RSA-WITH-ARIA-256-GCM-SHA384"; + reference + "RFC 6209: + Addition of the ARIA Cipher Suites to + Transport Layer Security (TLS)"; + } + + identity tls-dh-rsa-with-aria-128-gcm-sha256 { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-DH-RSA-WITH-ARIA-128-GCM-SHA256"; + reference + "RFC 6209: + Addition of the ARIA Cipher Suites to + Transport Layer Security (TLS)"; + } + + identity tls-dh-rsa-with-aria-256-gcm-sha384 { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-DH-RSA-WITH-ARIA-256-GCM-SHA384"; + reference + "RFC 6209: + Addition of the ARIA Cipher Suites to + Transport Layer Security (TLS)"; + } + + identity tls-dhe-dss-with-aria-128-gcm-sha256 { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-DHE-DSS-WITH-ARIA-128-GCM-SHA256"; + reference + "RFC 6209: + Addition of the ARIA Cipher Suites to + Transport Layer Security (TLS)"; + } + + identity tls-dhe-dss-with-aria-256-gcm-sha384 { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-DHE-DSS-WITH-ARIA-256-GCM-SHA384"; + reference + "RFC 6209: + Addition of the ARIA Cipher Suites to + Transport Layer Security (TLS)"; + } + + identity tls-dh-dss-with-aria-128-gcm-sha256 { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-DH-DSS-WITH-ARIA-128-GCM-SHA256"; + reference + "RFC 6209: + Addition of the ARIA Cipher Suites to + Transport Layer Security (TLS)"; + } + + identity tls-dh-dss-with-aria-256-gcm-sha384 { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-DH-DSS-WITH-ARIA-256-GCM-SHA384"; + reference + "RFC 6209: + Addition of the ARIA Cipher Suites to + Transport Layer Security (TLS)"; + } + + identity tls-dh-anon-with-aria-128-gcm-sha256 { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-DH-ANON-WITH-ARIA-128-GCM-SHA256"; + reference + "RFC 6209: + Addition of the ARIA Cipher Suites to + Transport Layer Security (TLS)"; + } + + identity tls-dh-anon-with-aria-256-gcm-sha384 { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-DH-ANON-WITH-ARIA-256-GCM-SHA384"; + reference + "RFC 6209: + Addition of the ARIA Cipher Suites to + Transport Layer Security (TLS)"; + } + + identity tls-ecdhe-ecdsa-with-aria-128-gcm-sha256 { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-ECDHE-ECDSA-WITH-ARIA-128-GCM-SHA256"; + reference + "RFC 6209: + Addition of the ARIA Cipher Suites to + Transport Layer Security (TLS)"; + } + + identity tls-ecdhe-ecdsa-with-aria-256-gcm-sha384 { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-ECDHE-ECDSA-WITH-ARIA-256-GCM-SHA384"; + reference + "RFC 6209: + Addition of the ARIA Cipher Suites to + Transport Layer Security (TLS)"; + } + + identity tls-ecdh-ecdsa-with-aria-128-gcm-sha256 { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-ECDH-ECDSA-WITH-ARIA-128-GCM-SHA256"; + reference + "RFC 6209: + Addition of the ARIA Cipher Suites to + Transport Layer Security (TLS)"; + } + + identity tls-ecdh-ecdsa-with-aria-256-gcm-sha384 { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-ECDH-ECDSA-WITH-ARIA-256-GCM-SHA384"; + reference + "RFC 6209: + Addition of the ARIA Cipher Suites to + Transport Layer Security (TLS)"; + } + + identity tls-ecdhe-rsa-with-aria-128-gcm-sha256 { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-ECDHE-RSA-WITH-ARIA-128-GCM-SHA256"; + reference + "RFC 6209: + Addition of the ARIA Cipher Suites to + Transport Layer Security (TLS)"; + } + + identity tls-ecdhe-rsa-with-aria-256-gcm-sha384 { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-ECDHE-RSA-WITH-ARIA-256-GCM-SHA384"; + reference + "RFC 6209: + Addition of the ARIA Cipher Suites to + Transport Layer Security (TLS)"; + } + + identity tls-ecdh-rsa-with-aria-128-gcm-sha256 { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-ECDH-RSA-WITH-ARIA-128-GCM-SHA256"; + reference + "RFC 6209: + Addition of the ARIA Cipher Suites to + Transport Layer Security (TLS)"; + } + + identity tls-ecdh-rsa-with-aria-256-gcm-sha384 { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-ECDH-RSA-WITH-ARIA-256-GCM-SHA384"; + reference + "RFC 6209: + Addition of the ARIA Cipher Suites to + Transport Layer Security (TLS)"; + } + + identity tls-psk-with-aria-128-cbc-sha256 { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-PSK-WITH-ARIA-128-CBC-SHA256"; + reference + "RFC 6209: + Addition of the ARIA Cipher Suites to + Transport Layer Security (TLS)"; + } + + identity tls-psk-with-aria-256-cbc-sha384 { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-PSK-WITH-ARIA-256-CBC-SHA384"; + reference + "RFC 6209: + Addition of the ARIA Cipher Suites to + Transport Layer Security (TLS)"; + } + + identity tls-dhe-psk-with-aria-128-cbc-sha256 { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-DHE-PSK-WITH-ARIA-128-CBC-SHA256"; + reference + "RFC 6209: + Addition of the ARIA Cipher Suites to + Transport Layer Security (TLS)"; + } + + identity tls-dhe-psk-with-aria-256-cbc-sha384 { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-DHE-PSK-WITH-ARIA-256-CBC-SHA384"; + reference + "RFC 6209: + Addition of the ARIA Cipher Suites to + Transport Layer Security (TLS)"; + } + + identity tls-rsa-psk-with-aria-128-cbc-sha256 { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-RSA-PSK-WITH-ARIA-128-CBC-SHA256"; + reference + "RFC 6209: + Addition of the ARIA Cipher Suites to + Transport Layer Security (TLS)"; + } + + identity tls-rsa-psk-with-aria-256-cbc-sha384 { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-RSA-PSK-WITH-ARIA-256-CBC-SHA384"; + reference + "RFC 6209: + Addition of the ARIA Cipher Suites to + Transport Layer Security (TLS)"; + } + + identity tls-psk-with-aria-128-gcm-sha256 { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-PSK-WITH-ARIA-128-GCM-SHA256"; + reference + "RFC 6209: + Addition of the ARIA Cipher Suites to + Transport Layer Security (TLS)"; + } + + identity tls-psk-with-aria-256-gcm-sha384 { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-PSK-WITH-ARIA-256-GCM-SHA384"; + reference + "RFC 6209: + Addition of the ARIA Cipher Suites to + Transport Layer Security (TLS)"; + } + + identity tls-dhe-psk-with-aria-128-gcm-sha256 { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-DHE-PSK-WITH-ARIA-128-GCM-SHA256"; + reference + "RFC 6209: + Addition of the ARIA Cipher Suites to + Transport Layer Security (TLS)"; + } + + identity tls-dhe-psk-with-aria-256-gcm-sha384 { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-DHE-PSK-WITH-ARIA-256-GCM-SHA384"; + reference + "RFC 6209: + Addition of the ARIA Cipher Suites to + Transport Layer Security (TLS)"; + } + + identity tls-rsa-psk-with-aria-128-gcm-sha256 { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-RSA-PSK-WITH-ARIA-128-GCM-SHA256"; + reference + "RFC 6209: + Addition of the ARIA Cipher Suites to + Transport Layer Security (TLS)"; + } + + identity tls-rsa-psk-with-aria-256-gcm-sha384 { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-RSA-PSK-WITH-ARIA-256-GCM-SHA384"; + reference + "RFC 6209: + Addition of the ARIA Cipher Suites to + Transport Layer Security (TLS)"; + } + identity tls-ecdhe-psk-with-aria-128-cbc-sha256 { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-ECDHE-PSK-WITH-ARIA-128-CBC-SHA256"; + reference + "RFC 6209: + Addition of the ARIA Cipher Suites to + Transport Layer Security (TLS)"; + } + + identity tls-ecdhe-psk-with-aria-256-cbc-sha384 { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-ECDHE-PSK-WITH-ARIA-256-CBC-SHA384"; + reference + "RFC 6209: + Addition of the ARIA Cipher Suites to + Transport Layer Security (TLS)"; + } + + identity tls-ecdhe-ecdsa-with-camellia-128-cbc-sha256 { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-ECDHE-ECDSA-WITH-CAMELLIA-128-CBC-SHA256"; + reference + "RFC 6367: + Addition of the Camellia Cipher Suites to + Transport Layer Security (TLS)"; + } + + identity tls-ecdhe-ecdsa-with-camellia-256-cbc-sha384 { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-ECDHE-ECDSA-WITH-CAMELLIA-256-CBC-SHA384"; + reference + "RFC 6367: + Addition of the Camellia Cipher Suites to + Transport Layer Security (TLS)"; + } + + identity tls-ecdh-ecdsa-with-camellia-128-cbc-sha256 { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-ECDH-ECDSA-WITH-CAMELLIA-128-CBC-SHA256"; + reference + "RFC 6367: + Addition of the Camellia Cipher Suites to + Transport Layer Security (TLS)"; + } + + identity tls-ecdh-ecdsa-with-camellia-256-cbc-sha384 { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-ECDH-ECDSA-WITH-CAMELLIA-256-CBC-SHA384"; + reference + "RFC 6367: + Addition of the Camellia Cipher Suites to + Transport Layer Security (TLS)"; + } + + identity tls-ecdhe-rsa-with-camellia-128-cbc-sha256 { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-ECDHE-RSA-WITH-CAMELLIA-128-CBC-SHA256"; + reference + "RFC 6367: + Addition of the Camellia Cipher Suites to + Transport Layer Security (TLS)"; + } + + identity tls-ecdhe-rsa-with-camellia-256-cbc-sha384 { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-ECDHE-RSA-WITH-CAMELLIA-256-CBC-SHA384"; + reference + "RFC 6367: + Addition of the Camellia Cipher Suites to + Transport Layer Security (TLS)"; + } + + identity tls-ecdh-rsa-with-camellia-128-cbc-sha256 { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-ECDH-RSA-WITH-CAMELLIA-128-CBC-SHA256"; + reference + "RFC 6367: + Addition of the Camellia Cipher Suites to + Transport Layer Security (TLS)"; + } + + identity tls-ecdh-rsa-with-camellia-256-cbc-sha384 { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-ECDH-RSA-WITH-CAMELLIA-256-CBC-SHA384"; + reference + "RFC 6367: + Addition of the Camellia Cipher Suites to + Transport Layer Security (TLS)"; + } + + identity tls-rsa-with-camellia-128-gcm-sha256 { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-RSA-WITH-CAMELLIA-128-GCM-SHA256"; + reference + "RFC 6367: + Addition of the Camellia Cipher Suites to + Transport Layer Security (TLS)"; + } + + identity tls-rsa-with-camellia-256-gcm-sha384 { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-RSA-WITH-CAMELLIA-256-GCM-SHA384"; + reference + "RFC 6367: + Addition of the Camellia Cipher Suites to + Transport Layer Security (TLS)"; + } + + identity tls-dhe-rsa-with-camellia-128-gcm-sha256 { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-DHE-RSA-WITH-CAMELLIA-128-GCM-SHA256"; + reference + "RFC 6367: + Addition of the Camellia Cipher Suites to + Transport Layer Security (TLS)"; + } + + identity tls-dhe-rsa-with-camellia-256-gcm-sha384 { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-DHE-RSA-WITH-CAMELLIA-256-GCM-SHA384"; + reference + "RFC 6367: + Addition of the Camellia Cipher Suites to + Transport Layer Security (TLS)"; + } + + identity tls-dh-rsa-with-camellia-128-gcm-sha256 { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-DH-RSA-WITH-CAMELLIA-128-GCM-SHA256"; + reference + "RFC 6367: + Addition of the Camellia Cipher Suites to + Transport Layer Security (TLS)"; + } + + identity tls-dh-rsa-with-camellia-256-gcm-sha384 { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-DH-RSA-WITH-CAMELLIA-256-GCM-SHA384"; + reference + "RFC 6367: + Addition of the Camellia Cipher Suites to + Transport Layer Security (TLS)"; + } + + identity tls-dhe-dss-with-camellia-128-gcm-sha256 { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-DHE-DSS-WITH-CAMELLIA-128-GCM-SHA256"; + reference + "RFC 6367: + Addition of the Camellia Cipher Suites to + Transport Layer Security (TLS)"; + } + + identity tls-dhe-dss-with-camellia-256-gcm-sha384 { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-DHE-DSS-WITH-CAMELLIA-256-GCM-SHA384"; + reference + "RFC 6367: + Addition of the Camellia Cipher Suites to + Transport Layer Security (TLS)"; + } + + identity tls-dh-dss-with-camellia-128-gcm-sha256 { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-DH-DSS-WITH-CAMELLIA-128-GCM-SHA256"; + reference + "RFC 6367: + Addition of the Camellia Cipher Suites to + Transport Layer Security (TLS)"; + } + + identity tls-dh-dss-with-camellia-256-gcm-sha384 { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-DH-DSS-WITH-CAMELLIA-256-GCM-SHA384"; + reference + "RFC 6367: + Addition of the Camellia Cipher Suites to + Transport Layer Security (TLS)"; + } + + identity tls-dh-anon-with-camellia-128-gcm-sha256 { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-DH-ANON-WITH-CAMELLIA-128-GCM-SHA256"; + reference + "RFC 6367: + Addition of the Camellia Cipher Suites to + Transport Layer Security (TLS)"; + } + + identity tls-dh-anon-with-camellia-256-gcm-sha384 { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-DH-ANON-WITH-CAMELLIA-256-GCM-SHA384"; + reference + "RFC 6367: + Addition of the Camellia Cipher Suites to + Transport Layer Security (TLS)"; + } + + identity tls-ecdhe-ecdsa-with-camellia-128-gcm-sha256 { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-ECDHE-ECDSA-WITH-CAMELLIA-128-GCM-SHA256"; + reference + "RFC 6367: + Addition of the Camellia Cipher Suites to + Transport Layer Security (TLS)"; + } + + identity tls-ecdhe-ecdsa-with-camellia-256-gcm-sha384 { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-ECDHE-ECDSA-WITH-CAMELLIA-256-GCM-SHA384"; + reference + "RFC 6367: + Addition of the Camellia Cipher Suites to + Transport Layer Security (TLS)"; + } + + identity tls-ecdh-ecdsa-with-camellia-128-gcm-sha256 { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-ECDH-ECDSA-WITH-CAMELLIA-128-GCM-SHA256"; + reference + "RFC 6367: + Addition of the Camellia Cipher Suites to + Transport Layer Security (TLS)"; + } + + identity tls-ecdh-ecdsa-with-camellia-256-gcm-sha384 { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-ECDH-ECDSA-WITH-CAMELLIA-256-GCM-SHA384"; + reference + "RFC 6367: + Addition of the Camellia Cipher Suites to + Transport Layer Security (TLS)"; + } + + identity tls-ecdhe-rsa-with-camellia-128-gcm-sha256 { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-ECDHE-RSA-WITH-CAMELLIA-128-GCM-SHA256"; + reference + "RFC 6367: + Addition of the Camellia Cipher Suites to + Transport Layer Security (TLS)"; + } + + identity tls-ecdhe-rsa-with-camellia-256-gcm-sha384 { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-ECDHE-RSA-WITH-CAMELLIA-256-GCM-SHA384"; + reference + "RFC 6367: + Addition of the Camellia Cipher Suites to + Transport Layer Security (TLS)"; + } + + identity tls-ecdh-rsa-with-camellia-128-gcm-sha256 { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-ECDH-RSA-WITH-CAMELLIA-128-GCM-SHA256"; + reference + "RFC 6367: + Addition of the Camellia Cipher Suites to + Transport Layer Security (TLS)"; + } + + identity tls-ecdh-rsa-with-camellia-256-gcm-sha384 { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-ECDH-RSA-WITH-CAMELLIA-256-GCM-SHA384"; + reference + "RFC 6367: + Addition of the Camellia Cipher Suites to + Transport Layer Security (TLS)"; + } + + identity tls-psk-with-camellia-128-gcm-sha256 { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-PSK-WITH-CAMELLIA-128-GCM-SHA256"; + reference + "RFC 6367: + Addition of the Camellia Cipher Suites to + Transport Layer Security (TLS)"; + } + + identity tls-psk-with-camellia-256-gcm-sha384 { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-PSK-WITH-CAMELLIA-256-GCM-SHA384"; + reference + "RFC 6367: + Addition of the Camellia Cipher Suites to + Transport Layer Security (TLS)"; + } + + identity tls-dhe-psk-with-camellia-128-gcm-sha256 { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-DHE-PSK-WITH-CAMELLIA-128-GCM-SHA256"; + reference + "RFC 6367: + Addition of the Camellia Cipher Suites to + Transport Layer Security (TLS)"; + } + + identity tls-dhe-psk-with-camellia-256-gcm-sha384 { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-DHE-PSK-WITH-CAMELLIA-256-GCM-SHA384"; + reference + "RFC 6367: + Addition of the Camellia Cipher Suites to + Transport Layer Security (TLS)"; + } + + identity tls-rsa-psk-with-camellia-128-gcm-sha256 { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-RSA-PSK-WITH-CAMELLIA-128-GCM-SHA256"; + reference + "RFC 6367: + Addition of the Camellia Cipher Suites to + Transport Layer Security (TLS)"; + } + identity tls-rsa-psk-with-camellia-256-gcm-sha384 { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-RSA-PSK-WITH-CAMELLIA-256-GCM-SHA384"; + reference + "RFC 6367: + Addition of the Camellia Cipher Suites to + Transport Layer Security (TLS)"; + } + + identity tls-psk-with-camellia-128-cbc-sha256 { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-PSK-WITH-CAMELLIA-128-CBC-SHA256"; + reference + "RFC 6367: + Addition of the Camellia Cipher Suites to + Transport Layer Security (TLS)"; + } + + identity tls-psk-with-camellia-256-cbc-sha384 { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-PSK-WITH-CAMELLIA-256-CBC-SHA384"; + reference + "RFC 6367: + Addition of the Camellia Cipher Suites to + Transport Layer Security (TLS)"; + } + + identity tls-dhe-psk-with-camellia-128-cbc-sha256 { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-DHE-PSK-WITH-CAMELLIA-128-CBC-SHA256"; + reference + "RFC 6367: + Addition of the Camellia Cipher Suites to + Transport Layer Security (TLS)"; + } + + identity tls-dhe-psk-with-camellia-256-cbc-sha384 { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-DHE-PSK-WITH-CAMELLIA-256-CBC-SHA384"; + reference + "RFC 6367: + Addition of the Camellia Cipher Suites to + Transport Layer Security (TLS)"; + } + + identity tls-rsa-psk-with-camellia-128-cbc-sha256 { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-RSA-PSK-WITH-CAMELLIA-128-CBC-SHA256"; + reference + "RFC 6367: + Addition of the Camellia Cipher Suites to + Transport Layer Security (TLS)"; + } + + identity tls-rsa-psk-with-camellia-256-cbc-sha384 { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-RSA-PSK-WITH-CAMELLIA-256-CBC-SHA384"; + reference + "RFC 6367: + Addition of the Camellia Cipher Suites to + Transport Layer Security (TLS)"; + } + + identity tls-ecdhe-psk-with-camellia-128-cbc-sha256 { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-ECDHE-PSK-WITH-CAMELLIA-128-CBC-SHA256"; + reference + "RFC 6367: + Addition of the Camellia Cipher Suites to + Transport Layer Security (TLS)"; + } + + identity tls-ecdhe-psk-with-camellia-256-cbc-sha384 { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-ECDHE-PSK-WITH-CAMELLIA-256-CBC-SHA384"; + reference + "RFC 6367: + Addition of the Camellia Cipher Suites to + Transport Layer Security (TLS)"; + } + + identity tls-rsa-with-aes-128-ccm { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-RSA-WITH-AES-128-CCM"; + reference + "RFC 6655: + AES-CCM Cipher Suites for TLS"; + } + + identity tls-rsa-with-aes-256-ccm { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-RSA-WITH-AES-256-CCM"; + reference + "RFC 6655: + AES-CCM Cipher Suites for TLS"; + } + + identity tls-dhe-rsa-with-aes-128-ccm { + base cipher-suite-alg-base; + description + "TLS-DHE-RSA-WITH-AES-128-CCM"; + reference + "RFC 6655: + AES-CCM Cipher Suites for TLS"; + } + + identity tls-dhe-rsa-with-aes-256-ccm { + base cipher-suite-alg-base; + description + "TLS-DHE-RSA-WITH-AES-256-CCM"; + reference + "RFC 6655: + AES-CCM Cipher Suites for TLS"; + } + + identity tls-rsa-with-aes-128-ccm-8 { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-RSA-WITH-AES-128-CCM-8"; + reference + "RFC 6655: + AES-CCM Cipher Suites for TLS"; + } + + identity tls-rsa-with-aes-256-ccm-8 { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-RSA-WITH-AES-256-CCM-8"; + reference + "RFC 6655: + AES-CCM Cipher Suites for TLS"; + } + + identity tls-dhe-rsa-with-aes-128-ccm-8 { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-DHE-RSA-WITH-AES-128-CCM-8"; + reference + "RFC 6655: + AES-CCM Cipher Suites for TLS"; + } + + identity tls-dhe-rsa-with-aes-256-ccm-8 { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-DHE-RSA-WITH-AES-256-CCM-8"; + reference + "RFC 6655: + AES-CCM Cipher Suites for TLS"; + } + + identity tls-psk-with-aes-128-ccm { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-PSK-WITH-AES-128-CCM"; + reference + "RFC 6655: + AES-CCM Cipher Suites for TLS"; + } + + identity tls-psk-with-aes-256-ccm { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-PSK-WITH-AES-256-CCM"; + reference + "RFC 6655: + AES-CCM Cipher Suites for TLS"; + } + + identity tls-dhe-psk-with-aes-128-ccm { + base cipher-suite-alg-base; + description + "TLS-DHE-PSK-WITH-AES-128-CCM"; + reference + "RFC 6655: + AES-CCM Cipher Suites for TLS"; + } + + identity tls-dhe-psk-with-aes-256-ccm { + base cipher-suite-alg-base; + description + "TLS-DHE-PSK-WITH-AES-256-CCM"; + reference + "RFC 6655: + AES-CCM Cipher Suites for TLS"; + } + + identity tls-psk-with-aes-128-ccm-8 { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-PSK-WITH-AES-128-CCM-8"; + reference + "RFC 6655: + AES-CCM Cipher Suites for TLS"; + } + + identity tls-psk-with-aes-256-ccm-8 { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-PSK-WITH-AES-256-CCM-8"; + reference + "RFC 6655: + AES-CCM Cipher Suites for TLS"; + } + + identity tls-psk-dhe-with-aes-128-ccm-8 { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-PSK-DHE-WITH-AES-128-CCM-8"; + reference + "RFC 6655: + AES-CCM Cipher Suites for TLS"; + } + + identity tls-psk-dhe-with-aes-256-ccm-8 { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-PSK-DHE-WITH-AES-256-CCM-8"; + reference + "RFC 6655: + AES-CCM Cipher Suites for TLS"; + } + + identity tls-ecdhe-ecdsa-with-aes-128-ccm { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-ECDHE-ECDSA-WITH-AES-128-CCM"; + reference + "RFC 7251: + AES-CCM ECC Cipher Suites for TLS"; + } + + identity tls-ecdhe-ecdsa-with-aes-256-ccm { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-ECDHE-ECDSA-WITH-AES-256-CCM"; + reference + "RFC 7251: + AES-CCM ECC Cipher Suites for TLS"; + } + + identity tls-ecdhe-ecdsa-with-aes-128-ccm-8 { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-ECDHE-ECDSA-WITH-AES-128-CCM-8"; + reference + "RFC 7251: + AES-CCM ECC Cipher Suites for TLS"; + } + + identity tls-ecdhe-ecdsa-with-aes-256-ccm-8 { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-ECDHE-ECDSA-WITH-AES-256-CCM-8"; + reference + "RFC 7251: + AES-CCM ECC Cipher Suites for TLS"; + } + + identity tls-eccpwd-with-aes-128-gcm-sha256 { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-ECCPWD-WITH-AES-128-GCM-SHA256"; + reference + "RFC 8492: + Secure Password Ciphersuites for + Transport Layer Security (TLS)"; + } + + identity tls-eccpwd-with-aes-256-gcm-sha384 { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-ECCPWD-WITH-AES-256-GCM-SHA384"; + reference + "RFC 8492: + Secure Password Ciphersuites for + Transport Layer Security (TLS)"; + } + + identity tls-eccpwd-with-aes-128-ccm-sha256 { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-ECCPWD-WITH-AES-128-CCM-SHA256"; + reference + "RFC 8492: + Secure Password Ciphersuites for + Transport Layer Security (TLS)"; + } + + identity tls-eccpwd-with-aes-256-ccm-sha384 { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-ECCPWD-WITH-AES-256-CCM-SHA384"; + reference + "RFC 8492: + Secure Password Ciphersuites for + Transport Layer Security (TLS)"; + } + + identity tls-sha256-sha256 { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-SHA256-SHA256"; + reference + "RFC 9150: + TLS 1.3 Authentication and Integrity-Only Cipher Suites"; + } + + identity tls-sha384-sha384 { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-SHA384-SHA384"; + reference + "RFC 9150: + TLS 1.3 Authentication and Integrity-Only Cipher Suites"; + } + + identity tls-gostr341112-256-with-kuznyechik-ctr-omac { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-GOSTR341112-256-WITH-KUZNYECHIK-CTR-OMAC"; + reference + "RFC 9189: + GOST Cipher Suites for Transport Layer Security (TLS) + Protocol Version 1.2"; + } + + identity tls-gostr341112-256-with-magma-ctr-omac { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-GOSTR341112-256-WITH-MAGMA-CTR-OMAC"; + reference + "RFC 9189: + GOST Cipher Suites for Transport Layer Security (TLS) + Protocol Version 1.2"; + } + + identity tls-gostr341112-256-with-28147-cnt-imit { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-GOSTR341112-256-WITH-28147-CNT-IMIT"; + reference + "RFC 9189: + GOST Cipher Suites for Transport Layer Security (TLS) + Protocol Version 1.2"; + } + + identity tls-ecdhe-rsa-with-chacha20-poly1305-sha256 { + base cipher-suite-alg-base; + description + "TLS-ECDHE-RSA-WITH-CHACHA20-POLY1305-SHA256"; + reference + "RFC 7905: + ChaCha20-Poly1305 Cipher Suites for + Transport Layer Security (TLS)"; + } + + identity tls-ecdhe-ecdsa-with-chacha20-poly1305-sha256 { + base cipher-suite-alg-base; + description + "TLS-ECDHE-ECDSA-WITH-CHACHA20-POLY1305-SHA256"; + reference + "RFC 7905: + ChaCha20-Poly1305 Cipher Suites for + Transport Layer Security (TLS)"; + } + + identity tls-dhe-rsa-with-chacha20-poly1305-sha256 { + base cipher-suite-alg-base; + description + "TLS-DHE-RSA-WITH-CHACHA20-POLY1305-SHA256"; + reference + "RFC 7905: + ChaCha20-Poly1305 Cipher Suites for + Transport Layer Security (TLS)"; + } + + identity tls-psk-with-chacha20-poly1305-sha256 { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-PSK-WITH-CHACHA20-POLY1305-SHA256"; + reference + "RFC 7905: + ChaCha20-Poly1305 Cipher Suites for + Transport Layer Security (TLS)"; + } + identity tls-ecdhe-psk-with-chacha20-poly1305-sha256 { + base cipher-suite-alg-base; + description + "TLS-ECDHE-PSK-WITH-CHACHA20-POLY1305-SHA256"; + reference + "RFC 7905: + ChaCha20-Poly1305 Cipher Suites for + Transport Layer Security (TLS)"; + } + + identity tls-dhe-psk-with-chacha20-poly1305-sha256 { + base cipher-suite-alg-base; + description + "TLS-DHE-PSK-WITH-CHACHA20-POLY1305-SHA256"; + reference + "RFC 7905: + ChaCha20-Poly1305 Cipher Suites for + Transport Layer Security (TLS)"; + } + + identity tls-rsa-psk-with-chacha20-poly1305-sha256 { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-RSA-PSK-WITH-CHACHA20-POLY1305-SHA256"; + reference + "RFC 7905: + ChaCha20-Poly1305 Cipher Suites for + Transport Layer Security (TLS)"; + } + + identity tls-ecdhe-psk-with-aes-128-gcm-sha256 { + base cipher-suite-alg-base; + description + "TLS-ECDHE-PSK-WITH-AES-128-GCM-SHA256"; + reference + "RFC 8442: + ECDHE_PSK with AES-GCM and AES-CCM Cipher Suites"; + } + + identity tls-ecdhe-psk-with-aes-256-gcm-sha384 { + base cipher-suite-alg-base; + description + "TLS-ECDHE-PSK-WITH-AES-256-GCM-SHA384"; + reference + "RFC 8442: + ECDHE_PSK with AES-GCM and AES-CCM Cipher Suites"; + } + identity tls-ecdhe-psk-with-aes-128-ccm-8-sha256 { + base cipher-suite-alg-base; + status deprecated; + description + "TLS-ECDHE-PSK-WITH-AES-128-CCM-8-SHA256"; + reference + "RFC 8442: + ECDHE_PSK with AES-GCM and AES-CCM Cipher Suites"; + } + + identity tls-ecdhe-psk-with-aes-128-ccm-sha256 { + base cipher-suite-alg-base; + description + "TLS-ECDHE-PSK-WITH-AES-128-CCM-SHA256"; + reference + "RFC 8442: + ECDHE_PSK with AES-GCM and AES-CCM Cipher Suites"; + } + + // Protocol-accessible Nodes + + container supported-algorithms { + config false; + description + "A container for a list of cipher suite algorithms supported + by the server."; + leaf-list supported-algorithm { + type cipher-suite-algorithm-ref; + description + "A cipher suite algorithm supported by the server."; + } + } + +} diff --git a/modules/ietf-crypto-types@2023-04-17.yang b/modules/ietf-crypto-types@2023-04-17.yang new file mode 100644 index 00000000..517e0162 --- /dev/null +++ b/modules/ietf-crypto-types@2023-04-17.yang @@ -0,0 +1,1070 @@ +module ietf-crypto-types { + yang-version 1.1; + namespace "urn:ietf:params:xml:ns:yang:ietf-crypto-types"; + prefix ct; + + import ietf-yang-types { + prefix yang; + reference + "RFC 6991: Common YANG Data Types"; + } + + import ietf-netconf-acm { + prefix nacm; + reference + "RFC 8341: Network Configuration Access Control Model"; + } + + organization + "IETF NETCONF (Network Configuration) Working Group"; + + contact + "WG Web: https://datatracker.ietf.org/wg/netconf + WG List: NETCONF WG list + Author: Kent Watsen "; + + description + "This module defines common YANG types for cryptographic + applications. + + Copyright (c) 2023 IETF Trust and the persons identified + as authors of the code. All rights reserved. + + Redistribution and use in source and binary forms, with + or without modification, is permitted pursuant to, and + subject to the license terms contained in, the Revised + BSD License set forth in Section 4.c of the IETF Trust's + Legal Provisions Relating to IETF Documents + (https://trustee.ietf.org/license-info). + + This version of this YANG module is part of RFC AAAA + (https://www.rfc-editor.org/info/rfcAAAA); see the RFC + itself for full legal notices. + + The key words 'MUST', 'MUST NOT', 'REQUIRED', 'SHALL', + 'SHALL NOT', 'SHOULD', 'SHOULD NOT', 'RECOMMENDED', + 'NOT RECOMMENDED', 'MAY', and 'OPTIONAL' in this document + are to be interpreted as described in BCP 14 (RFC 2119) + (RFC 8174) when, and only when, they appear in all + capitals, as shown here."; + + revision 2023-04-17 { + description + "Initial version"; + reference + "RFC AAAA: YANG Data Types and Groupings for Cryptography"; + } + + /****************/ + /* Features */ + /****************/ + + feature one-symmetric-key-format { + description + "Indicates that the server supports the + 'one-symmetric-key-format' identity."; + } + + feature one-asymmetric-key-format { + description + "Indicates that the server supports the + 'one-asymmetric-key-format' identity."; + } + + feature symmetrically-encrypted-value-format { + description + "Indicates that the server supports the + 'symmetrically-encrypted-value-format' identity."; + } + + feature asymmetrically-encrypted-value-format { + description + "Indicates that the server supports the + 'asymmetrically-encrypted-value-format' identity."; + } + + feature cms-enveloped-data-format { + description + "Indicates that the server supports the + 'cms-enveloped-data-format' identity."; + } + + feature cms-encrypted-data-format { + description + "Indicates that the server supports the + 'cms-encrypted-data-format' identity."; + } + feature p10-csr-format { + description + "Indicates that the server implements support + for generating P10-based CSRs, as defined + in RFC 2986."; + reference + "RFC 2986: PKCS #10: Certification Request Syntax + Specification Version 1.7"; + } + + feature csr-generation { + description + "Indicates that the server implements the + 'generate-csr' action."; + } + + feature certificate-expiration-notification { + description + "Indicates that the server implements the + 'certificate-expiration' notification."; + } + + feature cleartext-passwords { + description + "Indicates that the server supports cleartext + passwords."; + } + + feature encrypted-passwords { + description + "Indicates that the server supports password + encryption."; + } + + feature cleartext-symmetric-keys { + description + "Indicates that the server supports cleartext + symmetric keys."; + } + + feature hidden-symmetric-keys { + description + "Indicates that the server supports hidden keys."; + } + + feature encrypted-symmetric-keys { + description + "Indicates that the server supports encryption + of symmetric keys."; + } + + feature cleartext-private-keys { + description + "Indicates that the server supports cleartext + private keys."; + } + + feature hidden-private-keys { + description + "Indicates that the server supports hidden keys."; + } + + feature encrypted-private-keys { + description + "Indicates that the server supports encryption + of private keys."; + } + + /*************************************************/ + /* Base Identities for Key Format Structures */ + /*************************************************/ + + identity symmetric-key-format { + description + "Base key-format identity for symmetric keys."; + } + + identity public-key-format { + description + "Base key-format identity for public keys."; + } + + identity private-key-format { + description + "Base key-format identity for private keys."; + } + + /****************************************************/ + /* Identities for Private Key Format Structures */ + /****************************************************/ + + identity rsa-private-key-format { + base private-key-format; + description + "Indicates that the private key value is encoded as + an RSAPrivateKey (from RFC 3447), encoded using ASN.1 + distinguished encoding rules (DER), as specified in + ITU-T X.690."; + reference + "RFC 3447: + PKCS #1: RSA Cryptography Specifications Version 2.2 + ITU-T X.690: + Information technology - ASN.1 encoding rules: + Specification of Basic Encoding Rules (BER), + Canonical Encoding Rules (CER) and Distinguished + Encoding Rules (DER) 02/2021."; + } + + identity ec-private-key-format { + base private-key-format; + description + "Indicates that the private key value is encoded as + an ECPrivateKey (from RFC 5915), encoded using ASN.1 + distinguished encoding rules (DER), as specified in + ITU-T X.690."; + reference + "RFC 5915: + Elliptic Curve Private Key Structure + ITU-T X.690: + Information technology - ASN.1 encoding rules: + Specification of Basic Encoding Rules (BER), + Canonical Encoding Rules (CER) and Distinguished + Encoding Rules (DER) 02/2021."; + } + + identity one-asymmetric-key-format { + if-feature "one-asymmetric-key-format"; + base private-key-format; + description + "Indicates that the private key value is a CMS + OneAsymmetricKey structure, as defined in RFC 5958, + encoded using ASN.1 distinguished encoding rules + (DER), as specified in ITU-T X.690."; + reference + "RFC 5958: Asymmetric Key Packages + ITU-T X.690: + Information technology - ASN.1 encoding rules: + Specification of Basic Encoding Rules (BER), + Canonical Encoding Rules (CER) and Distinguished + Encoding Rules (DER) 02/2021."; + } + + /***************************************************/ + /* Identities for Public Key Format Structures */ + /***************************************************/ + + identity ssh-public-key-format { + base public-key-format; + description + "Indicates that the public key value is an SSH public key, + as specified by RFC 4253, Section 6.6, i.e.: + + string certificate or public key format + identifier + byte[n] key/certificate data."; + reference + "RFC 4253: The Secure Shell (SSH) Transport Layer Protocol"; + } + + identity subject-public-key-info-format { + base public-key-format; + description + "Indicates that the public key value is a SubjectPublicKeyInfo + structure, as described in RFC 5280 encoded using ASN.1 + distinguished encoding rules (DER), as specified in + ITU-T X.690."; + reference + "RFC 5280: + Internet X.509 Public Key Infrastructure Certificate + and Certificate Revocation List (CRL) Profile + ITU-T X.690: + Information technology - ASN.1 encoding rules: + Specification of Basic Encoding Rules (BER), + Canonical Encoding Rules (CER) and Distinguished + Encoding Rules (DER) 02/2021."; + } + + /******************************************************/ + /* Identities for Symmetric Key Format Structures */ + /******************************************************/ + + identity octet-string-key-format { + base symmetric-key-format; + description + "Indicates that the key is encoded as a raw octet string. + The length of the octet string MUST be appropriate for + the associated algorithm's block size. + + The identity of the associated algorithm is outside the + scope of this specification. This is also true when + the octet string has been encrypted."; + } + identity one-symmetric-key-format { + if-feature "one-symmetric-key-format"; + base symmetric-key-format; + description + "Indicates that the private key value is a CMS + OneSymmetricKey structure, as defined in RFC 6031, + encoded using ASN.1 distinguished encoding rules + (DER), as specified in ITU-T X.690."; + reference + "RFC 6031: Cryptographic Message Syntax (CMS) + Symmetric Key Package Content Type + ITU-T X.690: + Information technology - ASN.1 encoding rules: + Specification of Basic Encoding Rules (BER), + Canonical Encoding Rules (CER) and Distinguished + Encoding Rules (DER) 02/2021."; + } + + /*************************************************/ + /* Identities for Encrypted Value Structures */ + /*************************************************/ + + identity encrypted-value-format { + description + "Base format identity for encrypted values."; + } + + identity symmetrically-encrypted-value-format { + if-feature "symmetrically-encrypted-value-format"; + base encrypted-value-format; + description + "Base format identity for symmetrically encrypted + values."; + } + + identity asymmetrically-encrypted-value-format { + if-feature "asymmetrically-encrypted-value-format"; + base encrypted-value-format; + description + "Base format identity for asymmetrically encrypted + values."; + } + + identity cms-encrypted-data-format { + if-feature "cms-encrypted-data-format"; + base symmetrically-encrypted-value-format; + description + "Indicates that the encrypted value conforms to + the 'encrypted-data-cms' type with the constraint + that the 'unprotectedAttrs' value is not set."; + reference + "RFC 5652: Cryptographic Message Syntax (CMS) + ITU-T X.690: + Information technology - ASN.1 encoding rules: + Specification of Basic Encoding Rules (BER), + Canonical Encoding Rules (CER) and Distinguished + Encoding Rules (DER) 02/2021."; + } + + identity cms-enveloped-data-format { + if-feature "cms-enveloped-data-format"; + base asymmetrically-encrypted-value-format; + description + "Indicates that the encrypted value conforms to the + 'enveloped-data-cms' type with the following constraints: + + The EnvelopedData structure MUST have exactly one + 'RecipientInfo'. + + If the asymmetric key supports public key cryptography + (e.g., RSA), then the 'RecipientInfo' must be a + 'KeyTransRecipientInfo' with the 'RecipientIdentifier' + using a 'subjectKeyIdentifier' with the value set using + 'method 1' in RFC 7093 over the recipient's public key. + + Otherwise, if the asymmetric key supports key agreement + (e.g., ECC), then the 'RecipientInfo' must be a + 'KeyAgreeRecipientInfo'. The 'OriginatorIdentifierOrKey' + value must use the 'OriginatorPublicKey' alternative. + The 'UserKeyingMaterial' value must not be present. + There must be exactly one 'RecipientEncryptedKeys' value + having the 'KeyAgreeRecipientIdentifier' set to 'rKeyId' + with the value set using 'method 1' in RFC 7093 over the + recipient's public key."; + reference + "RFC 5652: Cryptographic Message Syntax (CMS) + RFC 7093: + Additional Methods for Generating Key + Identifiers Values + ITU-T X.690: + Information technology - ASN.1 encoding rules: + Specification of Basic Encoding Rules (BER), + Canonical Encoding Rules (CER) and Distinguished + Encoding Rules (DER) 02/2021."; + } + + /*********************************************************/ + /* Identities for Certificate Signing Request Formats */ + /*********************************************************/ + + identity csr-format { + description + "A base identity for the certificate signing request + formats. Additional derived identities MAY be defined + by future efforts."; + } + + identity p10-csr-format { + if-feature "p10-csr-format"; + base csr-format; + description + "Indicates the 'CertificationRequest' structure + defined in RFC 2986."; + reference + "RFC 2986: PKCS #10: Certification Request Syntax + Specification Version 1.7"; + } + + /***************************************************/ + /* Typedefs for ASN.1 structures from RFC 2986 */ + /***************************************************/ + + typedef csr-info { + type binary; + description + "A CertificationRequestInfo structure, as defined in + RFC 2986, encoded using ASN.1 distinguished encoding + rules (DER), as specified in ITU-T X.690."; + reference + "RFC 2986: PKCS #10: Certification Request Syntax + Specification Version 1.7 + ITU-T X.690: + Information technology - ASN.1 encoding rules: + Specification of Basic Encoding Rules (BER), + Canonical Encoding Rules (CER) and Distinguished + Encoding Rules (DER) 02/2021."; + } + + typedef p10-csr { + type binary; + description + "A CertificationRequest structure, as specified in + RFC 2986, encoded using ASN.1 distinguished encoding + rules (DER), as specified in ITU-T X.690."; + reference + "RFC 2986: + PKCS #10: Certification Request Syntax Specification + Version 1.7 + ITU-T X.690: + Information technology - ASN.1 encoding rules: + Specification of Basic Encoding Rules (BER), + Canonical Encoding Rules (CER) and Distinguished + Encoding Rules (DER) 02/2021."; + } + + /***************************************************/ + /* Typedefs for ASN.1 structures from RFC 5280 */ + /***************************************************/ + + typedef x509 { + type binary; + description + "A Certificate structure, as specified in RFC 5280, + encoded using ASN.1 distinguished encoding rules (DER), + as specified in ITU-T X.690."; + reference + "RFC 5280: + Internet X.509 Public Key Infrastructure Certificate + and Certificate Revocation List (CRL) Profile + ITU-T X.690: + Information technology - ASN.1 encoding rules: + Specification of Basic Encoding Rules (BER), + Canonical Encoding Rules (CER) and Distinguished + Encoding Rules (DER) 02/2021."; + } + + typedef crl { + type binary; + description + "A CertificateList structure, as specified in RFC 5280, + encoded using ASN.1 distinguished encoding rules (DER), + as specified in ITU-T X.690."; + reference + "RFC 5280: + Internet X.509 Public Key Infrastructure Certificate + and Certificate Revocation List (CRL) Profile + ITU-T X.690: + Information technology - ASN.1 encoding rules: + Specification of Basic Encoding Rules (BER), + Canonical Encoding Rules (CER) and Distinguished + Encoding Rules (DER) 02/2021."; + } + + /***************************************************/ + /* Typedefs for ASN.1 structures from RFC 6960 */ + /***************************************************/ + + typedef oscp-request { + type binary; + description + "A OCSPRequest structure, as specified in RFC 6960, + encoded using ASN.1 distinguished encoding rules + (DER), as specified in ITU-T X.690."; + reference + "RFC 6960: + X.509 Internet Public Key Infrastructure Online + Certificate Status Protocol - OCSP + ITU-T X.690: + Information technology - ASN.1 encoding rules: + Specification of Basic Encoding Rules (BER), + Canonical Encoding Rules (CER) and Distinguished + Encoding Rules (DER) 02/2021."; + } + + typedef oscp-response { + type binary; + description + "A OCSPResponse structure, as specified in RFC 6960, + encoded using ASN.1 distinguished encoding rules + (DER), as specified in ITU-T X.690."; + reference + "RFC 6960: + X.509 Internet Public Key Infrastructure Online + Certificate Status Protocol - OCSP + ITU-T X.690: + Information technology - ASN.1 encoding rules: + Specification of Basic Encoding Rules (BER), + Canonical Encoding Rules (CER) and Distinguished + Encoding Rules (DER) 02/2021."; + } + + /***********************************************/ + /* Typedefs for ASN.1 structures from 5652 */ + /***********************************************/ + + typedef cms { + type binary; + description + "A ContentInfo structure, as specified in RFC 5652, + encoded using ASN.1 distinguished encoding rules (DER), + as specified in ITU-T X.690."; + reference + "RFC 5652: + Cryptographic Message Syntax (CMS) + ITU-T X.690: + Information technology - ASN.1 encoding rules: + Specification of Basic Encoding Rules (BER), + Canonical Encoding Rules (CER) and Distinguished + Encoding Rules (DER) 02/2021."; + } + + typedef data-content-cms { + type cms; + description + "A CMS structure whose top-most content type MUST be the + data content type, as described by Section 4 in RFC 5652."; + reference + "RFC 5652: Cryptographic Message Syntax (CMS)"; + } + + typedef signed-data-cms { + type cms; + description + "A CMS structure whose top-most content type MUST be the + signed-data content type, as described by Section 5 in + RFC 5652."; + reference + "RFC 5652: Cryptographic Message Syntax (CMS)"; + } + + typedef enveloped-data-cms { + type cms; + description + "A CMS structure whose top-most content type MUST be the + enveloped-data content type, as described by Section 6 + in RFC 5652."; + reference + "RFC 5652: Cryptographic Message Syntax (CMS)"; + } + + typedef digested-data-cms { + type cms; + description + "A CMS structure whose top-most content type MUST be the + digested-data content type, as described by Section 7 + in RFC 5652."; + reference + "RFC 5652: Cryptographic Message Syntax (CMS)"; + } + + typedef encrypted-data-cms { + type cms; + description + "A CMS structure whose top-most content type MUST be the + encrypted-data content type, as described by Section 8 + in RFC 5652."; + reference + "RFC 5652: Cryptographic Message Syntax (CMS)"; + } + + typedef authenticated-data-cms { + type cms; + description + "A CMS structure whose top-most content type MUST be the + authenticated-data content type, as described by Section 9 + in RFC 5652."; + reference + "RFC 5652: Cryptographic Message Syntax (CMS)"; + } + + /*********************************************************/ + /* Typedefs for ASN.1 structures related to RFC 5280 */ + /*********************************************************/ + + typedef trust-anchor-cert-x509 { + type x509; + description + "A Certificate structure that MUST encode a self-signed + root certificate."; + } + + typedef end-entity-cert-x509 { + type x509; + description + "A Certificate structure that MUST encode a certificate + that is neither self-signed nor having Basic constraint + CA true."; + } + + /*********************************************************/ + /* Typedefs for ASN.1 structures related to RFC 5652 */ + /*********************************************************/ + + typedef trust-anchor-cert-cms { + type signed-data-cms; + description + "A CMS SignedData structure that MUST contain the chain of + X.509 certificates needed to authenticate the certificate + presented by a client or end-entity. + + The CMS MUST contain only a single chain of certificates. + The client or end-entity certificate MUST only authenticate + to the last intermediate CA certificate listed in the chain. + + In all cases, the chain MUST include a self-signed root + certificate. In the case where the root certificate is + itself the issuer of the client or end-entity certificate, + only one certificate is present. + + This CMS structure MAY (as applicable where this type is + used) also contain suitably fresh (as defined by local + policy) revocation objects with which the device can + verify the revocation status of the certificates. + + This CMS encodes the degenerate form of the SignedData + structure (RFC 5652, Section 5.2) that is commonly used + to disseminate X.509 certificates and revocation objects + (RFC 5280)."; + reference + "RFC 5280: + Internet X.509 Public Key Infrastructure Certificate + and Certificate Revocation List (CRL) Profile. + RFC 5652: + Cryptographic Message Syntax (CMS)"; + } + + typedef end-entity-cert-cms { + type signed-data-cms; + description + "A CMS SignedData structure that MUST contain the end + entity certificate itself, and MAY contain any number + of intermediate certificates leading up to a trust + anchor certificate. The trust anchor certificate + MAY be included as well. + + The CMS MUST contain a single end entity certificate. + The CMS MUST NOT contain any spurious certificates. + + This CMS structure MAY (as applicable where this type is + used) also contain suitably fresh (as defined by local + policy) revocation objects with which the device can + verify the revocation status of the certificates. + + This CMS encodes the degenerate form of the SignedData + structure (RFC 5652, Section 5.2) that is commonly + used to disseminate X.509 certificates and revocation + objects (RFC 5280)."; + + reference + "RFC 5280: + Internet X.509 Public Key Infrastructure Certificate + and Certificate Revocation List (CRL) Profile. + RFC 5652: + Cryptographic Message Syntax (CMS)"; + } + + /*****************/ + /* Groupings */ + /*****************/ + + grouping encrypted-value-grouping { + description + "A reusable grouping for a value that has been encrypted by + a referenced symmetric or asymmetric key."; + container encrypted-by { + nacm:default-deny-write; + description + "An empty container enabling a reference to the key that + encrypted the value to be augmented in. The referenced + key MUST be a symmetric key or an asymmetric key. + + A symmetric key MUST be referenced via a leaf node called + 'symmetric-key-ref'. An asymmetric key MUST be referenced + via a leaf node called 'asymmetric-key-ref'. + + The leaf nodes MUST be direct descendants in the data tree, + and MAY be direct descendants in the schema tree (e.g., + choice/case statements are allowed, but not a container)."; + } + leaf encrypted-value-format { + type identityref { + base encrypted-value-format; + } + mandatory true; + description + "Identifies the format of the 'encrypted-value' leaf. + + If 'encrypted-by' points to a symmetric key, then a + 'symmetrically-encrypted-value-format' based identity + MUST by set (e.g., cms-encrypted-data-format). + + If 'encrypted-by' points to an asymmetric key, then an + 'asymmetrically-encrypted-value-format' based identity + MUST by set (e.g., cms-enveloped-data-format)."; + } + leaf encrypted-value { + nacm:default-deny-write; + type binary; + must '../encrypted-by'; + mandatory true; + description + "The value, encrypted using the referenced symmetric + or asymmetric key. The value MUST be encoded using + the format associated with the 'encrypted-value-format' + leaf."; + } + } + + grouping password-grouping { + description + "A password that may be encrypted."; + choice password-type { + nacm:default-deny-write; + mandatory true; + description + "Choice between password types."; + case cleartext-password { + if-feature "cleartext-passwords"; + leaf cleartext-password { + nacm:default-deny-all; + type string; + description + "The cleartext value of the password."; + } + } + case encrypted-password { + if-feature "encrypted-passwords"; + container encrypted-password { + description + "A container for the encrypted password value."; + uses encrypted-value-grouping; + } + } + } + } + + grouping symmetric-key-grouping { + description + "A symmetric key."; + leaf key-format { + nacm:default-deny-write; + type identityref { + base symmetric-key-format; + } + description + "Identifies the symmetric key's format. Implementations + SHOULD ensure that the incoming symmetric key value is + encoded in the specified format. + + For encrypted keys, the value is the decrypted key's + format (i.e., the 'encrypted-value-format' conveys the + encrypted key's format."; + } + choice key-type { + nacm:default-deny-write; + mandatory true; + description + "Choice between key types."; + case cleartext-key { + leaf cleartext-key { + if-feature "cleartext-symmetric-keys"; + nacm:default-deny-all; + type binary; + must '../key-format'; + description + "The binary value of the key. The interpretation of + the value is defined by the 'key-format' field."; + } + } + case hidden-key { + if-feature "hidden-symmetric-keys"; + leaf hidden-key { + type empty; + must 'not(../key-format)'; + description + "A hidden key. How such keys are created is outside + the scope of this module."; + } + } + case encrypted-key { + if-feature "encrypted-symmetric-keys"; + container encrypted-key { + must '../key-format'; + description + "A container for the encrypted symmetric key value. + The interpretation of the 'encrypted-value' node + is via the 'key-format' node"; + uses encrypted-value-grouping; + } + } + } + } + + grouping public-key-grouping { + description + "A public key."; + leaf public-key-format { + nacm:default-deny-write; + type identityref { + base public-key-format; + } + description + "Identifies the public key's format. Implementations SHOULD + ensure that the incoming public key value is encoded in the + specified format."; + } + leaf public-key { + nacm:default-deny-write; + type binary; + description + "The binary value of the public key. The interpretation + of the value is defined by 'public-key-format' field."; + } + } + + grouping asymmetric-key-pair-grouping { + description + "A private key and its associated public key. Implementations + SHOULD ensure that the two keys are a matching pair."; + uses public-key-grouping; + leaf private-key-format { + nacm:default-deny-write; + type identityref { + base private-key-format; + } + description + "Identifies the private key's format. Implementations SHOULD + ensure that the incoming private key value is encoded in the + specified format. + + For encrypted keys, the value is the decrypted key's + format (i.e., the 'encrypted-value-format' conveys the + encrypted key's format."; + } + choice private-key-type { + nacm:default-deny-write; + mandatory true; + description + "Choice between key types."; + case cleartext-private-key { + if-feature "cleartext-private-keys"; + leaf cleartext-private-key { + nacm:default-deny-all; + type binary; + must '../private-key-format'; + description + "The value of the binary key The key's value is + interpreted by the 'private-key-format' field."; + } + } + case hidden-private-key { + if-feature "hidden-private-keys"; + leaf hidden-private-key { + type empty; + must 'not(../private-key-format)'; + description + "A hidden key. How such keys are created is + outside the scope of this module."; + } + } + case encrypted-private-key { + if-feature "encrypted-private-keys"; + container encrypted-private-key { + must '../private-key-format'; + description + "A container for the encrypted asymmetric private key + value. The interpretation of the 'encrypted-value' + node is via the 'private-key-format' node"; + uses encrypted-value-grouping; + } + } + } + } + + grouping certificate-expiration-grouping { + description + "A notification for when a certificate is about to, or + already has, expired."; + notification certificate-expiration { + if-feature "certificate-expiration-notification"; + description + "A notification indicating that the configured certificate + is either about to expire or has already expired. When to + send notifications is an implementation specific decision, + but it is RECOMMENDED that a notification be sent once a + month for 3 months, then once a week for four weeks, and + then once a day thereafter until the issue is resolved."; + leaf expiration-date { + type yang:date-and-time; + mandatory true; + description + "Identifies the expiration date on the certificate."; + } + } + } + + grouping trust-anchor-cert-grouping { + description + "A trust anchor certificate, and a notification for when + it is about to (or already has) expire."; + leaf cert-data { + nacm:default-deny-write; + type trust-anchor-cert-cms; + description + "The binary certificate data for this certificate."; + } + uses certificate-expiration-grouping; + } + + grouping end-entity-cert-grouping { + description + "An end entity certificate, and a notification for when + it is about to (or already has) expire. Implementations + SHOULD assert that, where used, the end entity certificate + contains the expected public key."; + leaf cert-data { + nacm:default-deny-write; + type end-entity-cert-cms; + description + "The binary certificate data for this certificate."; + } + uses certificate-expiration-grouping; + } + + grouping generate-csr-grouping { + description + "Defines the 'generate-csr' action."; + action generate-csr { + if-feature "csr-generation"; + nacm:default-deny-all; + description + "Generates a certificate signing request structure for + the associated asymmetric key using the passed subject + and attribute values. + + This action statement is only available when the + associated 'public-key-format' node's value is + 'subject-public-key-info-format'."; + reference + "RFC 6125: + Representation and Verification of Domain-Based + Application Service Identity within Internet Public Key + Infrastructure Using X.509 (PKIX) Certificates in the + Context of Transport Layer Security (TLS)"; + input { + leaf csr-format { + type identityref { + base csr-format; + } + mandatory true; + description + "Specifies the format for the returned certificate."; + } + leaf csr-info { + type csr-info; + mandatory true; + description + "A CertificationRequestInfo structure, as defined in + RFC 2986. + + Enables the client to provide a fully-populated + CertificationRequestInfo structure that the server + only needs to sign in order to generate the complete + 'CertificationRequest' structure to return in the + 'output'. + + The 'AlgorithmIdentifier' field contained inside + the 'SubjectPublicKeyInfo' field MUST be one known + to be supported by the device."; + reference + "RFC 2986: + PKCS #10: Certification Request Syntax Specification + RFC AAAA: + YANG Data Types and Groupings for Cryptography"; + } + } + output { + choice csr-type { + mandatory true; + description + "A choice amongst certificate signing request formats. + Additional formats MAY be augmented into this 'choice' + statement by future efforts."; + case p10-csr { + leaf p10-csr { + type p10-csr; + description + "A CertificationRequest, as defined in RFC 2986."; + } + description + "A CertificationRequest, as defined in RFC 2986."; + reference + "RFC 2986: + PKCS #10: Certification Request Syntax Specification + RFC AAAA: + YANG Data Types and Groupings for Cryptography"; + } + } + } + } + } // generate-csr-grouping + + grouping asymmetric-key-pair-with-cert-grouping { + description + "A private/public key pair and an associated certificate. + Implementations SHOULD assert that the certificate contains + the matching public key."; + uses asymmetric-key-pair-grouping; + uses end-entity-cert-grouping; + uses generate-csr-grouping; + } // asymmetric-key-pair-with-cert-grouping + + grouping asymmetric-key-pair-with-certs-grouping { + description + "A private/public key pair and a list of associated + certificates. Implementations SHOULD assert that + certificates contain the matching public key."; + uses asymmetric-key-pair-grouping; + container certificates { + nacm:default-deny-write; + description + "Certificates associated with this asymmetric key."; + list certificate { + key "name"; + description + "A certificate for this asymmetric key."; + leaf name { + type string; + description + "An arbitrary name for the certificate."; + } + uses end-entity-cert-grouping { + refine "cert-data" { + mandatory true; + } + } + } + } + uses generate-csr-grouping; + } // asymmetric-key-pair-with-certs-grouping + +} diff --git a/modules/ietf-keystore@2023-04-17.yang b/modules/ietf-keystore@2023-04-17.yang new file mode 100644 index 00000000..8e158fab --- /dev/null +++ b/modules/ietf-keystore@2023-04-17.yang @@ -0,0 +1,411 @@ +module ietf-keystore { + yang-version 1.1; + namespace "urn:ietf:params:xml:ns:yang:ietf-keystore"; + prefix ks; + + import ietf-netconf-acm { + prefix nacm; + reference + "RFC 8341: Network Configuration Access Control Model"; + } + + import ietf-crypto-types { + prefix ct; + reference + "RFC AAAA: YANG Data Types and Groupings for Cryptography"; + } + + organization + "IETF NETCONF (Network Configuration) Working Group"; + + contact + "WG Web: https://datatracker.ietf.org/wg/netconf + WG List: NETCONF WG list + Author: Kent Watsen "; + + description + "This module defines a 'keystore' to centralize management + of security credentials. + + Copyright (c) 2023 IETF Trust and the persons identified + as authors of the code. All rights reserved. + + Redistribution and use in source and binary forms, with + or without modification, is permitted pursuant to, and + subject to the license terms contained in, the Revised + BSD License set forth in Section 4.c of the IETF Trust's + Legal Provisions Relating to IETF Documents + (https://trustee.ietf.org/license-info). + + This version of this YANG module is part of RFC CCCC + (https://www.rfc-editor.org/info/rfcCCCC); see the RFC + itself for full legal notices. + + The key words 'MUST', 'MUST NOT', 'REQUIRED', 'SHALL', + 'SHALL NOT', 'SHOULD', 'SHOULD NOT', 'RECOMMENDED', + 'NOT RECOMMENDED', 'MAY', and 'OPTIONAL' in this document + are to be interpreted as described in BCP 14 (RFC 2119) + (RFC 8174) when, and only when, they appear in all + capitals, as shown here."; + + revision 2023-04-17 { + description + "Initial version"; + reference + "RFC CCCC: A YANG Data Model for a Keystore"; + } + + /****************/ + /* Features */ + /****************/ + + feature central-keystore-supported { + description + "The 'central-keystore-supported' feature indicates that + the server supports the keystore (i.e., implements the + 'ietf-keystore' module)."; + } + + feature inline-definitions-supported { + description + "The 'inline-definitions-supported' feature indicates that + the server supports locally-defined keys."; + } + + feature asymmetric-keys { + description + "The 'asymmetric-keys' feature indicates that the server + implements the /keystore/asymmetric-keys subtree."; + + } + + feature symmetric-keys { + description + "The 'symmetric-keys' feature indicates that the server + implements the /keystore/symmetric-keys subtree."; + } + + /****************/ + /* Typedefs */ + /****************/ + + typedef symmetric-key-ref { + type leafref { + path "/ks:keystore/ks:symmetric-keys/ks:symmetric-key" + + "/ks:name"; + } + description + "This typedef enables modules to easily define a reference + to a symmetric key stored in the keystore, when this + module is implemented."; + } + + typedef asymmetric-key-ref { + type leafref { + path "/ks:keystore/ks:asymmetric-keys/ks:asymmetric-key" + + "/ks:name"; + } + description + "This typedef enables modules to easily define a reference + to an asymmetric key stored in the keystore, when this + module is implemented."; + } + + /*****************/ + /* Groupings */ + /*****************/ + + grouping encrypted-by-choice-grouping { + description + "A grouping that defines a 'choice' statement that can be + augmented into the 'encrypted-by' node, present in the + 'symmetric-key-grouping' and 'asymmetric-key-pair-grouping' + groupings defined in RFC AAAA, enabling references to keys + in the keystore, when this module is implemented."; + choice encrypted-by-choice { + nacm:default-deny-write; + mandatory true; + description + "A choice amongst other symmetric or asymmetric keys."; + case symmetric-key-ref { + if-feature "central-keystore-supported"; + if-feature "symmetric-keys"; + leaf symmetric-key-ref { + type ks:symmetric-key-ref; + description + "Identifies the symmetric key used to encrypt the + associated key."; + } + } + case asymmetric-key-ref { + if-feature "central-keystore-supported"; + if-feature "asymmetric-keys"; + leaf asymmetric-key-ref { + type ks:asymmetric-key-ref; + description + "Identifies the asymmetric key whose public key + encrypted the associated key."; + } + } + } + } + + grouping asymmetric-key-certificate-ref-grouping { + description + "This grouping defines a reference to a specific certificate + associated with an asymmetric key stored in the keystore, + when this module is implemented."; + leaf asymmetric-key { + nacm:default-deny-write; + if-feature "central-keystore-supported"; + if-feature "asymmetric-keys"; + type ks:asymmetric-key-ref; + must '../certificate'; + description + "A reference to an asymmetric key in the keystore."; + } + leaf certificate { + nacm:default-deny-write; + type leafref { + path "/ks:keystore/ks:asymmetric-keys/ks:asymmetric-key" + + "[ks:name = current()/../asymmetric-key]/" + + "ks:certificates/ks:certificate/ks:name"; + } + must '../asymmetric-key'; + description + "A reference to a specific certificate of the + asymmetric key in the keystore."; + } + } + + // inline-or-keystore-* groupings + + grouping inline-or-keystore-symmetric-key-grouping { + description + "A grouping that expands to allow the symmetric key to be + either stored locally, i.e., within the using data model, + or a reference to a symmetric key stored in the keystore. + + Servers that do not 'implement' this module, and hence + 'central-keystore-supported' is not defined, SHOULD + augment in custom 'case' statements enabling references + to the alternate keystore locations."; + choice inline-or-keystore { + nacm:default-deny-write; + mandatory true; + description + "A choice between an inlined definition and a definition + that exists in the keystore."; + case inline { + if-feature "inline-definitions-supported"; + container inline-definition { + description + "Container to hold the local key definition."; + uses ct:symmetric-key-grouping; + } + } + case keystore { + if-feature "central-keystore-supported"; + if-feature "symmetric-keys"; + leaf keystore-reference { + type ks:symmetric-key-ref; + description + "A reference to an symmetric key that exists in + the keystore, when this module is implemented."; + } + } + } + } + + grouping inline-or-keystore-asymmetric-key-grouping { + description + "A grouping that expands to allow the asymmetric key to be + either stored locally, i.e., within the using data model, + or a reference to an asymmetric key stored in the keystore. + + Servers that do not 'implement' this module, and hence + 'central-keystore-supported' is not defined, SHOULD + augment in custom 'case' statements enabling references + to the alternate keystore locations."; + choice inline-or-keystore { + nacm:default-deny-write; + mandatory true; + description + "A choice between an inlined definition and a definition + that exists in the keystore."; + case inline { + if-feature "inline-definitions-supported"; + container inline-definition { + description + "Container to hold the local key definition."; + uses ct:asymmetric-key-pair-grouping; + } + } + case keystore { + if-feature "central-keystore-supported"; + if-feature "asymmetric-keys"; + leaf keystore-reference { + type ks:asymmetric-key-ref; + description + "A reference to an asymmetric key that exists in + the keystore, when this module is implemented. The + intent is to reference just the asymmetric key + without any regard for any certificates that may + be associated with it."; + } + } + } + } + + grouping inline-or-keystore-asymmetric-key-with-certs-grouping { + description + "A grouping that expands to allow an asymmetric key and + its associated certificates to be either stored locally, + i.e., within the using data model, or a reference to an + asymmetric key (and its associated certificates) stored + in the keystore. + + Servers that do not 'implement' this module, and hence + 'central-keystore-supported' is not defined, SHOULD + augment in custom 'case' statements enabling references + to the alternate keystore locations."; + choice inline-or-keystore { + nacm:default-deny-write; + mandatory true; + description + "A choice between an inlined definition and a definition + that exists in the keystore."; + case inline { + if-feature "inline-definitions-supported"; + container inline-definition { + description + "Container to hold the local key definition."; + uses ct:asymmetric-key-pair-with-certs-grouping; + } + } + case keystore { + if-feature "central-keystore-supported"; + if-feature "asymmetric-keys"; + leaf keystore-reference { + type ks:asymmetric-key-ref; + description + "A reference to an asymmetric-key (and all of its + associated certificates) in the keystore, when + this module is implemented."; + } + } + } + } + + grouping inline-or-keystore-end-entity-cert-with-key-grouping { + description + "A grouping that expands to allow an end-entity certificate + (and its associated asymmetric key pair) to be either stored + locally, i.e., within the using data model, or a reference + to a specific certificate in the keystore. + + Servers that do not 'implement' this module, and hence + 'central-keystore-supported' is not defined, SHOULD + augment in custom 'case' statements enabling references + to the alternate keystore locations."; + choice inline-or-keystore { + nacm:default-deny-write; + mandatory true; + description + "A choice between an inlined definition and a definition + that exists in the keystore."; + case inline { + if-feature "inline-definitions-supported"; + container inline-definition { + description + "Container to hold the local key definition."; + uses ct:asymmetric-key-pair-with-cert-grouping; + } + } + case keystore { + if-feature "central-keystore-supported"; + if-feature "asymmetric-keys"; + container keystore-reference { + uses asymmetric-key-certificate-ref-grouping; + description + "A reference to a specific certificate associated with + an asymmetric key stored in the keystore, when this + module is implemented."; + } + } + } + } + + grouping keystore-grouping { + description + "Grouping definition enables use in other contexts. If ever + done, implementations MUST augment new 'case' statements + into the various inline-or-keystore 'choice' statements to + supply leafrefs to the model-specific location(s)."; + container asymmetric-keys { + nacm:default-deny-write; + if-feature "asymmetric-keys"; + description + "A list of asymmetric keys."; + list asymmetric-key { + key "name"; + description + "An asymmetric key."; + leaf name { + type string; + description + "An arbitrary name for the asymmetric key."; + } + uses ct:asymmetric-key-pair-with-certs-grouping; + } + } + container symmetric-keys { + nacm:default-deny-write; + if-feature "symmetric-keys"; + description + "A list of symmetric keys."; + list symmetric-key { + key "name"; + description + "A symmetric key."; + leaf name { + type string; + description + "An arbitrary name for the symmetric key."; + } + uses ct:symmetric-key-grouping; + } + } + } + + /*********************************/ + /* Protocol accessible nodes */ + /*********************************/ + + container keystore { + if-feature central-keystore-supported; + description + "A central keystore containing a list of symmetric keys and + a list of asymmetric keys."; + nacm:default-deny-write; + uses keystore-grouping { + augment "symmetric-keys/symmetric-key/key-type/encrypted-key/" + + "encrypted-key/encrypted-by" { + description + "Augments in a choice statement enabling the encrypting + key to be any other symmetric or asymmetric key in the + central keystore."; + uses encrypted-by-choice-grouping; + } + augment "asymmetric-keys/asymmetric-key/private-key-type/" + + "encrypted-private-key/encrypted-private-key/" + + "encrypted-by" { + description + "Augments in a choice statement enabling the encrypting + key to be any other symmetric or asymmetric key in the + central keystore."; + uses encrypted-by-choice-grouping; + } + } + } +} diff --git a/modules/ietf-netconf-server@2023-04-17.yang b/modules/ietf-netconf-server@2023-04-17.yang new file mode 100644 index 00000000..ddb05954 --- /dev/null +++ b/modules/ietf-netconf-server@2023-04-17.yang @@ -0,0 +1,680 @@ +module ietf-netconf-server { + yang-version 1.1; + namespace "urn:ietf:params:xml:ns:yang:ietf-netconf-server"; + prefix ncs; + + import ietf-yang-types { + prefix yang; + reference + "RFC 6991: Common YANG Data Types"; + } + + import ietf-x509-cert-to-name { + prefix x509c2n; + reference + "RFC 7407: A YANG Data Model for SNMP Configuration"; + } + + import ietf-tcp-client { + prefix tcpc; + reference + "RFC DDDD: YANG Groupings for TCP Clients and TCP Servers"; + } + + import ietf-tcp-server { + prefix tcps; + reference + "RFC DDDD: YANG Groupings for TCP Clients and TCP Servers"; + } + + import ietf-ssh-common { + prefix sshcmn; + reference + "RFC EEEE: YANG Groupings for SSH Clients and SSH Servers"; + } + + import ietf-ssh-server { + prefix sshs; + reference + "RFC EEEE: YANG Groupings for SSH Clients and SSH Servers"; + } + + import ietf-tls-server { + prefix tlss; + reference + "RFC FFFF: YANG Groupings for TLS Clients and TLS Servers"; + } + + organization + "IETF NETCONF (Network Configuration) Working Group"; + + contact + "WG Web: https://datatracker.ietf.org/wg/netconf + WG List: NETCONF WG list + Author: Kent Watsen "; + + description + "This module contains a collection of YANG definitions + for configuring NETCONF servers. + + Copyright (c) 2023 IETF Trust and the persons identified + as authors of the code. All rights reserved. + + Redistribution and use in source and binary forms, with + or without modification, is permitted pursuant to, and + subject to the license terms contained in, the Revised + BSD License set forth in Section 4.c of the IETF Trust's + Legal Provisions Relating to IETF Documents + (https://trustee.ietf.org/license-info). + + This version of this YANG module is part of RFC HHHH + (https://www.rfc-editor.org/info/rfcHHHH); see the RFC + itself for full legal notices. + + The key words 'MUST', 'MUST NOT', 'REQUIRED', 'SHALL', + 'SHALL NOT', 'SHOULD', 'SHOULD NOT', 'RECOMMENDED', + 'NOT RECOMMENDED', 'MAY', and 'OPTIONAL' in this document + are to be interpreted as described in BCP 14 (RFC 2119) + (RFC 8174) when, and only when, they appear in all + capitals, as shown here."; + + revision 2023-04-17 { + description + "Initial version"; + reference + "RFC HHHH: NETCONF Client and Server Models"; + } + + // Features + + feature ssh-listen { + description + "The 'ssh-listen' feature indicates that the NETCONF server + supports opening a port to accept NETCONF over SSH + client connections."; + reference + "RFC 6242: + Using the NETCONF Protocol over Secure Shell (SSH)"; + } + + feature tls-listen { + description + "The 'tls-listen' feature indicates that the NETCONF server + supports opening a port to accept NETCONF over TLS + client connections."; + reference + "RFC 7589: Using the NETCONF Protocol over Transport + Layer Security (TLS) with Mutual X.509 + Authentication"; + } + + feature ssh-call-home { + description + "The 'ssh-call-home' feature indicates that the NETCONF + server supports initiating a NETCONF over SSH call + home connection to NETCONF clients."; + reference + "RFC 8071: NETCONF Call Home and RESTCONF Call Home"; + } + + feature tls-call-home { + description + "The 'tls-call-home' feature indicates that the NETCONF + server supports initiating a NETCONF over TLS call + home connection to NETCONF clients."; + reference + "RFC 8071: NETCONF Call Home and RESTCONF Call Home"; + } + + feature central-netconf-server-supported { + description + "The 'central-netconf-server-supported' feature indicates + that the server supports the top-level 'netconf-server' + node. + + This feature is needed as some servers may want to use + features defined in this module, which requires this + module to be implemented, without having to support + the top-level 'netconf-server' node."; + } + + // Groupings + + grouping netconf-server-grouping { + description + "A reusable grouping for configuring a NETCONF server + without any consideration for how underlying transport + sessions are established. + + Note that this grouping uses a fairly typical descendant + node name such that a stack of 'uses' statements will + have name conflicts. It is intended that the consuming + data model will resolve the issue by wrapping the 'uses' + statement in a container called, e.g., + 'netconf-server-parameters'. This model purposely does + not do this itself so as to provide maximum flexibility + to consuming models."; + + container client-identity-mappings { + description + "Specifies mappings through which NETCONF client X.509 + certificates are used to determine a NETCONF username, + per RFC 7407. + + For TLS-based transports, if no matching and valid + cert-to-name list entry can be found, then the NETCONF + server MUST close the connection, and MUST NOT accept + NETCONF messages over it, per Section 7 in RFC 7589. + + For SSH-based transports, a matching cert-to-name + entry overrides the username provided by the SSH + implementation, consistent with the second paragraph + of Section 3 in RFC 6242."; + reference + "RFC 6242: + Using the NETCONF Protocol over Secure Shell (SSH) + RFC 7589: + Using the NETCONF Protocol over Transport Layer + Security (TLS) with Mutual X.509 Authentication"; + uses x509c2n:cert-to-name { + refine "cert-to-name/fingerprint" { + mandatory false; + description + "A 'fingerprint' value does not need to be specified + when the 'cert-to-name' mapping is independent of + fingerprint matching. A 'cert-to-name' having no + fingerprint value will match any client certificate + and therefore should only be present at the end of + the user-ordered 'cert-to-name' list."; + } + } + } + } + + grouping netconf-server-listen-stack-grouping { + description + "A reusable grouping for configuring a NETCONF server + 'listen' protocol stack for a single connection."; + choice transport { + mandatory true; + description + "Selects between available transports."; + case ssh { + if-feature "ssh-listen"; + container ssh { + description + "SSH-specific listening configuration for inbound + connections."; + container tcp-server-parameters { + description + "A wrapper around the TCP client parameters + to avoid name collisions."; + uses tcps:tcp-server-grouping { + refine "local-port" { + default "830"; + description + "The NETCONF server will listen on the + IANA-assigned well-known port value + for 'netconf-ssh' (830) if no value + is specified."; + } + } + } + container ssh-server-parameters { + description + "A wrapper around the SSH server parameters + to avoid name collisions."; + uses sshs:ssh-server-grouping; + } + container netconf-server-parameters { + description + "A wrapper around the NETCONF server parameters + to avoid name collisions."; + uses ncs:netconf-server-grouping { + refine "client-identity-mappings" { + if-feature "sshcmn:ssh-x509-certs"; + description + "Augments in an 'if-feature' statement + ensuring the 'client-identity-mappings' + descendant is enabled only when SSH + supports X.509 certificates."; + } + augment "client-identity-mappings" { + description + "Adds a flag indicating if a cert-to-name + is required."; + leaf mapping-required { + type boolean; + description + "Indicates that the cert-to-name mapping + is required (i.e., the SSH-level username + is ignored)."; + } + } + } + } + } + } + case tls { + if-feature "tls-listen"; + container tls { + description + "TLS-specific listening configuration for inbound + connections."; + container tcp-server-parameters { + description + "A wrapper around the TCP client parameters + to avoid name collisions."; + uses tcps:tcp-server-grouping { + refine "local-port" { + default "6513"; + description + "The NETCONF server will listen on the + IANA-assigned well-known port value + for 'netconf-tls' (6513) if no value + is specified."; + } + } + } + container tls-server-parameters { + description + "A wrapper around the TLS server parameters to + avoid name collisions."; + uses tlss:tls-server-grouping { + refine "client-authentication" { + must 'ca-certs or ee-certs'; + description + "NETCONF/TLS servers MUST validate client + certificates. This configures certificates + at the socket-level (i.e. bags), more + discriminating client-certificate checks + SHOULD be implemented by the application."; + reference + "RFC 7589: + Using the NETCONF Protocol over Transport Layer + Security (TLS) with Mutual X.509 Authentication"; + } + } + } + container netconf-server-parameters { + description + "A wrapper around the NETCONF server parameters + to avoid name collisions."; + uses ncs:netconf-server-grouping { + refine "client-identity-mappings/cert-to-name" { + min-elements 1; + description + "The TLS transport requires a mapping."; + } + } + } + } + } + } + } + + grouping netconf-server-callhome-stack-grouping { + description + "A reusable grouping for configuring a NETCONF server + 'call-home' protocol stack, for a single connection."; + choice transport { + mandatory true; + description + "Selects between available transports."; + case ssh { + if-feature "ssh-call-home"; + container ssh { + description + "Specifies SSH-specific call-home transport + configuration."; + container tcp-client-parameters { + description + "A wrapper around the TCP client parameters + to avoid name collisions."; + uses tcpc:tcp-client-grouping { + refine "remote-port" { + default "4334"; + description + "The NETCONF server will attempt to connect + to the IANA-assigned well-known port for + 'netconf-ch-ssh' (4334) if no value is + specified."; + } + } + } + container ssh-server-parameters { + description + "A wrapper around the SSH server parameters + to avoid name collisions."; + uses sshs:ssh-server-grouping; + } + container netconf-server-parameters { + description + "A wrapper around the NETCONF server parameters + to avoid name collisions."; + uses ncs:netconf-server-grouping { + refine "client-identity-mappings" { + if-feature "sshcmn:ssh-x509-certs"; + description + "Augments in an 'if-feature' statement + ensuring the 'client-identity-mappings' + descendant is enabled only when SSH + supports X.509 certificates."; + } + augment "client-identity-mappings" { + description + "Adds a flag indicating if a cert-to-name + is required."; + leaf mapping-required { + type boolean; + description + "Indicates that the cert-to-name mapping + is required (i.e., the SSH-level username + is ignored)."; + } + } + } + } + } + } + case tls { + if-feature "tls-call-home"; + container tls { + description + "Specifies TLS-specific call-home transport + configuration."; + container tcp-client-parameters { + description + "A wrapper around the TCP client parameters + to avoid name collisions."; + uses tcpc:tcp-client-grouping { + refine "remote-port" { + default "4335"; + description + "The NETCONF server will attempt to connect + to the IANA-assigned well-known port for + 'netconf-ch-tls' (4335) if no value is + specified."; + } + } + } + container tls-server-parameters { + description + "A wrapper around the TLS server parameters to + avoid name collisions."; + uses tlss:tls-server-grouping { + refine "client-authentication" { + must 'ca-certs or ee-certs'; + description + "NETCONF/TLS servers MUST validate client + certificates. This configures certificates + at the socket-level (i.e. bags), more + discriminating client-certificate checks + SHOULD be implemented by the application."; + reference + "RFC 7589: + Using the NETCONF Protocol over Transport Layer + Security (TLS) with Mutual X.509 Authentication"; + } + } + } + container netconf-server-parameters { + description + "A wrapper around the NETCONF server parameters + to avoid name collisions."; + uses ncs:netconf-server-grouping { + refine "client-identity-mappings/cert-to-name" { + min-elements 1; + description + "The TLS transport requires a mapping."; + } + } + } + } + } + } + } + + grouping netconf-server-app-grouping { + description + "A reusable grouping for configuring a NETCONF server + application that supports both 'listen' and 'call-home' + protocol stacks for a multiplicity of connections."; + container listen { + if-feature "ssh-listen or tls-listen"; + presence + "Indicates that server-listening ports have been configured. + This statement is present so the mandatory descendant + nodes do not imply that this node must be configured."; + description + "Configures listen behavior"; + leaf idle-timeout { + type uint16; + units "seconds"; + default "180"; // three minutes + description + "Specifies the maximum number of seconds that a NETCONF + session may remain idle. A NETCONF session will be + dropped if it is idle for an interval longer than this + number of seconds. If set to zero, then the server + will never drop a session because it is idle."; + } + list endpoint { + key "name"; + min-elements 1; + description + "List of endpoints to listen for NETCONF connections."; + leaf name { + type string; + description + "An arbitrary name for the NETCONF listen endpoint."; + } + uses netconf-server-listen-stack-grouping; + } + } + container call-home { + if-feature "ssh-call-home or tls-call-home"; + presence + "Indicates that server-initiated call home connections have + been configured. This statement is present so the mandatory + descendant nodes do not imply that this node must be + configured."; + description + "Configures the NETCONF server to initiate the underlying + transport connection to NETCONF clients."; + list netconf-client { + key "name"; + min-elements 1; + description + "List of NETCONF clients the NETCONF server is to + maintain simultaneous call-home connections with."; + leaf name { + type string; + description + "An arbitrary name for the remote NETCONF client."; + } + container endpoints { + description + "Container for the list of endpoints."; + list endpoint { + key "name"; + min-elements 1; + ordered-by user; + description + "A non-empty user-ordered list of endpoints for this + NETCONF server to try to connect to in sequence. + Defining more than one enables high-availability."; + leaf name { + type string; + description + "An arbitrary name for this endpoint."; + } + uses netconf-server-callhome-stack-grouping; + } + } + container connection-type { + description + "Indicates the NETCONF server's preference for how the + NETCONF connection is maintained."; + choice connection-type { + mandatory true; + description + "Selects between available connection types."; + case persistent-connection { + container persistent { + presence + "Indicates that a persistent connection is to be + maintained."; + description + "Maintain a persistent connection to the NETCONF + client. If the connection goes down, immediately + start trying to reconnect to the NETCONF client, + using the reconnection strategy. + + This connection type minimizes any NETCONF client + to NETCONF server data-transfer delay, albeit at + the expense of holding resources longer."; + } + } + case periodic-connection { + container periodic { + presence "Indicates that a periodic connection is + to be maintained."; + description + "Periodically connect to the NETCONF client. + + This connection type decreases resource + utilization, albeit with increased delay in + NETCONF client to NETCONF server interactions. + + The NETCONF client SHOULD gracefully close the + connection using upon completing + planned activities. If the NETCONF session is + not closed gracefully, the NETCONF server MUST + immediately attempt to reestablish the connection. + + Connections are established at the same start + time regardless how long the previous connection + stayed open. + + In the case that the previous connection is still + active (i.e., the NETCONF client has not closed + it yet), establishing a new connection is NOT + RECOMMENDED."; + leaf period { + type uint16; + units "minutes"; + default "60"; + description + "Duration of time between periodic connections."; + } + leaf anchor-time { + type yang:date-and-time { + // constrained to minute-level granularity + pattern '[0-9]{4}-(1[0-2]|0[1-9])-(0[1-9]|[1-2]' + + '[0-9]|3[0-1])T(0[0-9]|1[0-9]|2[0-3]):[' + + '0-5][0-9]:00(Z|[\+\-]((1[0-3]|0[0-9]):' + + '([0-5][0-9])|14:00))?'; + } + description + "Designates a timestamp before or after which a + series of periodic connections are determined. + The periodic connections occur at a whole + multiple interval from the anchor time. + + If an 'anchor-time' is not provided, then the + server may implicitly set it to the time when + this configuraton is applied (e.g., on boot). + + For example, for an anchor time is 15 minutes + past midnight and a period interval of 24 hours, + then a periodic connection will occur 15 minutes + past midnight everyday."; + } + leaf idle-timeout { + type uint16; + units "seconds"; + default "180"; // three minutes + description + "Specifies the maximum number of seconds that + a NETCONF session may remain idle. A NETCONF + session will be dropped if it is idle for an + interval longer than this number of seconds. + If set to zero, then the server will never + drop a session because it is idle."; + } + } + } // case periodic-connection + } // choice connection-type + } // container connection-type + container reconnect-strategy { + description + "The reconnection strategy directs how a NETCONF server + reconnects to a NETCONF client, after discovering its + connection to the client has dropped, even if due to a + reboot. The NETCONF server starts with the specified + endpoint and tries to connect to it max-attempts times + before trying the next endpoint in the list (round + robin)."; + leaf start-with { + type enumeration { + enum first-listed { + description + "Indicates that reconnections should start with + the first endpoint listed."; + } + enum last-connected { + description + "Indicates that reconnections should start with + the endpoint last connected to. If no previous + connection has ever been established, then the + first endpoint configured is used. NETCONF + servers SHOULD be able to remember the last + endpoint connected to across reboots."; + } + enum random-selection { + description + "Indicates that reconnections should start with + a random endpoint."; + } + } + default "first-listed"; + description + "Specifies which of the NETCONF client's endpoints + the NETCONF server should start with when trying + to connect to the NETCONF client."; + } + leaf max-wait { + type uint16 { + range "1..max"; + } + units "seconds"; + default "5"; + description + "Specifies the amount of time in seconds after which, + if the connection is not established, an endpoint + connection attempt is considered unsuccessful."; + } + leaf max-attempts { + type uint8 { + range "1..max"; + } + default "3"; + description + "Specifies the number times the NETCONF server tries + to connect to a specific endpoint before moving on + to the next endpoint in the list (round robin)."; + } + } // container reconnect-strategy + } // list netconf-client + } // container call-home + } // grouping netconf-server-app-grouping + + // Protocol accessible node for servers that implement this module. + container netconf-server { + if-feature central-netconf-server-supported; + uses netconf-server-app-grouping; + description + "Top-level container for NETCONF server configuration."; + } +} diff --git a/modules/ietf-ssh-common@2023-04-17.yang b/modules/ietf-ssh-common@2023-04-17.yang new file mode 100644 index 00000000..d331660f --- /dev/null +++ b/modules/ietf-ssh-common@2023-04-17.yang @@ -0,0 +1,256 @@ +module ietf-ssh-common { + yang-version 1.1; + namespace "urn:ietf:params:xml:ns:yang:ietf-ssh-common"; + prefix sshcmn; + + import iana-ssh-encryption-algs { + prefix sshea; + reference + "RFC EEEE: YANG Groupings for SSH Clients and SSH Servers"; + } + + import iana-ssh-key-exchange-algs { + prefix sshkea; + reference + "RFC EEEE: YANG Groupings for SSH Clients and SSH Servers"; + } + + import iana-ssh-mac-algs { + prefix sshma; + reference + "RFC EEEE: YANG Groupings for SSH Clients and SSH Servers"; + } + + import iana-ssh-public-key-algs { + prefix sshpka; + reference + "RFC EEEE: YANG Groupings for SSH Clients and SSH Servers"; + } + + import ietf-crypto-types { + prefix ct; + reference + "RFC AAAA: YANG Data Types and Groupings for Cryptography"; + } + + import ietf-keystore { + prefix ks; + reference + "RFC CCCC: A YANG Data Model for a Keystore"; + } + + organization + "IETF NETCONF (Network Configuration) Working Group"; + + contact + "WG Web: https://datatracker.ietf.org/wg/netconf + WG List: NETCONF WG list + Author: Kent Watsen + Author: Gary Wu "; + + description + "This module defines a common features and groupings for + Secure Shell (SSH). + + Copyright (c) 2023 IETF Trust and the persons identified + as authors of the code. All rights reserved. + Redistribution and use in source and binary forms, with + or without modification, is permitted pursuant to, and + subject to the license terms contained in, the Revised + BSD License set forth in Section 4.c of the IETF Trust's + Legal Provisions Relating to IETF Documents + (https://trustee.ietf.org/license-info). + + This version of this YANG module is part of RFC EEEE + (https://www.rfc-editor.org/info/rfcEEEE); see the RFC + itself for full legal notices. + + The key words 'MUST', 'MUST NOT', 'REQUIRED', 'SHALL', + 'SHALL NOT', 'SHOULD', 'SHOULD NOT', 'RECOMMENDED', + 'NOT RECOMMENDED', 'MAY', and 'OPTIONAL' in this document + are to be interpreted as described in BCP 14 (RFC 2119) + (RFC 8174) when, and only when, they appear in all + capitals, as shown here."; + + revision 2023-04-17 { + description + "Initial version"; + reference + "RFC EEEE: YANG Groupings for SSH Clients and SSH Servers"; + } + + // Features + + feature ssh-x509-certs { + description + "X.509v3 certificates are supported for SSH."; + reference + "RFC 6187: X.509v3 Certificates for Secure Shell + Authentication"; + } + + feature transport-params { + description + "SSH transport layer parameters are configurable."; + } + + feature public-key-generation { + description + "Indicates that the server implements the + 'generate-public-key' RPC."; + } + + // Groupings + + grouping transport-params-grouping { + description + "A reusable grouping for SSH transport parameters."; + reference + "RFC 4253: The Secure Shell (SSH) Transport Layer Protocol"; + container host-key { + description + "Parameters regarding host key."; + leaf-list host-key-alg { + type identityref { + base sshpka:public-key-alg-base; + } + ordered-by user; + description + "Acceptable host key algorithms in order of decreasing + preference. + + If this leaf-list is not configured (has zero elements) + the acceptable host key algorithms are implementation- + defined."; + reference + "RFC EEEE: YANG Groupings for SSH Clients and SSH Servers"; + } + } + container key-exchange { + description + "Parameters regarding key exchange."; + leaf-list key-exchange-alg { + type identityref { + base sshkea:key-exchange-alg-base; + } + ordered-by user; + description + "Acceptable key exchange algorithms in order of decreasing + preference. + + If this leaf-list is not configured (has zero elements) + the acceptable key exchange algorithms are implementation + defined."; + } + } + container encryption { + description + "Parameters regarding encryption."; + leaf-list encryption-alg { + type identityref { + base sshea:encryption-alg-base; + } + ordered-by user; + description + "Acceptable encryption algorithms in order of decreasing + preference. + + If this leaf-list is not configured (has zero elements) + the acceptable encryption algorithms are implementation + defined."; + } + } + container mac { + description + "Parameters regarding message authentication code (MAC)."; + leaf-list mac-alg { + type identityref { + base sshma:mac-alg-base; + } + ordered-by user; + description + "Acceptable MAC algorithms in order of decreasing + preference. + + If this leaf-list is not configured (has zero elements) + the acceptable MAC algorithms are implementation- + defined."; + } + } + } + + // Protocol-accessible Nodes + + rpc generate-public-key { + if-feature "public-key-generation"; + description + "Requests the device to generate an public key using + the specified key algorithm."; + input { + leaf algorithm { + type sshpka:public-key-algorithm-ref; + mandatory true; + description + "The algorithm to be used when generating the key."; + } + leaf bits { + type uint16; + description + "Specifies the number of bits in the key to create. + For RSA keys, the minimum size is 1024 bits and + the default is 3072 bits. Generally, 3072 bits is + considered sufficient. DSA keys must be exactly 1024 + bits as specified by FIPS 186-6. For ECDSA keys, the + 'bits' value determines the key length by selecting + from one of three elliptic curve sizes: 256, 384 or + 521 bits. Attempting to use bit lengths other than + these three values for ECDSA keys will fail. ECDSA-SK, + Ed25519 and Ed25519-SK keys have a fixed length and + the 'bits' value, if specified, will be ignored."; + reference + "FIPS 186-6: Digital Signature Standard (DSS)"; + } + choice private-key-encoding { + mandatory true; + description + "A choice amongst optional private key handling."; + case cleartext { + if-feature "ct:encrypted-private-keys"; + leaf cleartext { + type empty; + description + "Indicates that the private key is to be returned + as a cleartext value."; + } + } + case encrypt { + if-feature "ct:encrypted-private-keys"; + container encrypt-with { + description + "Indicates that the key is to be encrypted using + the specified symmetric or asymmetric key."; + uses ks:encrypted-by-choice-grouping; + } + } + case hide { + if-feature "ct:hidden-private-keys"; + leaf hide { + type empty; + description + "Indicates that the private key is to be hidden. + + Unlike the 'cleartext' and 'encrypt' options, the + key returned is a placeholder for an internally + stored key. See the 'Support for Built-in Keys' + section in RFC CCCC for information about hidden + keys."; + } + } + } + } + output { + uses ct:asymmetric-key-pair-grouping; + } + } // end generate-public-key + +} diff --git a/modules/ietf-ssh-server@2023-04-17.yang b/modules/ietf-ssh-server@2023-04-17.yang new file mode 100644 index 00000000..b5b564e7 --- /dev/null +++ b/modules/ietf-ssh-server@2023-04-17.yang @@ -0,0 +1,423 @@ +module ietf-ssh-server { + yang-version 1.1; + namespace "urn:ietf:params:xml:ns:yang:ietf-ssh-server"; + prefix sshs; + + import iana-crypt-hash { + prefix ianach; + reference + "RFC 7317: A YANG Data Model for System Management"; + } + + import ietf-netconf-acm { + prefix nacm; + reference + "RFC 8341: Network Configuration Access Control Model"; + } + + import ietf-crypto-types { + prefix ct; + reference + "RFC AAAA: YANG Data Types and Groupings for Cryptography"; + } + + import ietf-truststore { + prefix ts; + reference + "RFC BBBB: A YANG Data Model for a Truststore"; + } + + import ietf-keystore { + prefix ks; + reference + "RFC CCCC: A YANG Data Model for a Keystore"; + } + + import ietf-ssh-common { + prefix sshcmn; + reference + "RFC EEEE: YANG Groupings for SSH Clients and SSH Servers"; + } + + organization + "IETF NETCONF (Network Configuration) Working Group"; + + contact + "WG Web: https://datatracker.ietf.org/wg/netconf + WG List: NETCONF WG list + Author: Kent Watsen "; + + description + "This module defines a reusable grouping for SSH servers that + can be used as a basis for specific SSH server instances. + + Copyright (c) 2023 IETF Trust and the persons identified + as authors of the code. All rights reserved. + + Redistribution and use in source and binary forms, with + or without modification, is permitted pursuant to, and + subject to the license terms contained in, the Revised + BSD License set forth in Section 4.c of the IETF Trust's + Legal Provisions Relating to IETF Documents + (https://trustee.ietf.org/license-info). + + This version of this YANG module is part of RFC EEEE + (https://www.rfc-editor.org/info/rfcEEEE); see the RFC + itself for full legal notices. + + The key words 'MUST', 'MUST NOT', 'REQUIRED', 'SHALL', + 'SHALL NOT', 'SHOULD', 'SHOULD NOT', 'RECOMMENDED', + 'NOT RECOMMENDED', 'MAY', and 'OPTIONAL' in this document + are to be interpreted as described in BCP 14 (RFC 2119) + (RFC 8174) when, and only when, they appear in all + capitals, as shown here."; + + revision 2023-04-17 { + description + "Initial version"; + reference + "RFC EEEE: YANG Groupings for SSH Clients and SSH Servers"; + } + + // Features + + feature ssh-server-keepalives { + description + "Per socket SSH keepalive parameters are configurable for + SSH servers on the server implementing this feature."; + } + + feature local-users-supported { + description + "Indicates that the configuration for users can be + configured herein, as opposed to in an application + specific location."; + } + + feature local-user-auth-publickey { + if-feature "local-users-supported"; + description + "Indicates that the 'publickey' authentication type, + per RFC 4252, is supported for locally-defined users. + + The 'publickey' authentication type is required by + RFC 4252, but common implementations enable it to + be disabled."; + reference + "RFC 4252: + The Secure Shell (SSH) Authentication Protocol"; + } + + feature local-user-auth-password { + if-feature "local-users-supported"; + description + "Indicates that the 'password' authentication type, + per RFC 4252, is supported for locally-defined users."; + reference + "RFC 4252: + The Secure Shell (SSH) Authentication Protocol"; + } + + feature local-user-auth-hostbased { + if-feature "local-users-supported"; + description + "Indicates that the 'hostbased' authentication type, + per RFC 4252, is supported for locally-defined users."; + reference + "RFC 4252: + The Secure Shell (SSH) Authentication Protocol"; + } + feature local-user-auth-none { + if-feature "local-users-supported"; + description + "Indicates that the 'none' authentication type, per + RFC 4252, is supported. It is NOT RECOMMENDED to + enable this feature."; + reference + "RFC 4252: + The Secure Shell (SSH) Authentication Protocol"; + } + + // Groupings + + grouping ssh-server-grouping { + description + "A reusable grouping for configuring a SSH server without + any consideration for how underlying TCP sessions are + established. + + Note that this grouping uses fairly typical descendant + node names such that a nesting of 'uses' statements will + have name conflicts. It is intended that the consuming + data model will resolve the issue (e.g., by wrapping + the 'uses' statement in a container called + 'ssh-server-parameters'). This model purposely does + not do this itself so as to provide maximum flexibility + to consuming models."; + + container server-identity { + nacm:default-deny-write; + description + "The list of host keys the SSH server will present when + establishing a SSH connection."; + list host-key { + key "name"; + min-elements 1; + ordered-by user; + description + "An ordered list of host keys (see RFC 4251) the SSH + server will use to construct its ordered list of + algorithms, when sending its SSH_MSG_KEXINIT message, + as defined in Section 7.1 of RFC 4253."; + reference + "RFC 4251: The Secure Shell (SSH) Protocol Architecture + RFC 4253: The Secure Shell (SSH) Transport Layer + Protocol"; + leaf name { + type string; + description + "An arbitrary name for this host key"; + } + choice host-key-type { + mandatory true; + description + "The type of host key being specified"; + container public-key { + description + "A locally-defined or referenced asymmetric key pair + to be used for the SSH server's host key."; + reference + "RFC CCCC: A YANG Data Model for a Keystore"; + uses ks:inline-or-keystore-asymmetric-key-grouping { + refine "inline-or-keystore/inline/inline-definition" { + must 'derived-from-or-self(public-key-format,' + + ' "ct:ssh-public-key-format")'; + + } + refine "inline-or-keystore/keystore/" + + "keystore-reference" { + must 'derived-from-or-self(deref(.)/../ks:public-' + + 'key-format, "ct:ssh-public-key-format")'; + } + } + } + container certificate { + if-feature "sshcmn:ssh-x509-certs"; + description + "A locally-defined or referenced end-entity + certificate to be used for the SSH server's + host key."; + reference + "RFC CCCC: A YANG Data Model for a Keystore"; + uses + ks:inline-or-keystore-end-entity-cert-with-key-grouping{ + refine "inline-or-keystore/inline/inline-definition" { + must 'derived-from-or-self(public-key-format,' + + ' "ct:subject-public-key-info-format")'; + } + refine "inline-or-keystore/keystore/keystore-reference" + + "/asymmetric-key" { + must + 'derived-from-or-self(deref(.)/../ks:public-key-' + + 'format, "ct:subject-public-key-info-format")'; + } + } + } + } + } + } // container server-identity + + container client-authentication { + nacm:default-deny-write; + description + "Specifies how the SSH server can be configured to + authenticate SSH clients. See RFC 4252 for a general + discussion about SSH authentication."; + reference + "RFC 4252: The Secure Shell (SSH) Transport Layer"; + container users { + if-feature "local-users-supported"; + description + "A list of locally configured users."; + list user { + key "name"; + description + "A locally configured user. + + The server SHOULD derive the list of authentication + 'method names' returned to the SSH client from the + descendant nodes configured herein, per Sections + 5.1 and 5.2 in RFC 4252. + + The authentication methods are unordered. Clients + must authenticate to all configured methods. + Whenever a choice amongst methods arises, + implementations SHOULD use a default ordering + that prioritizes automation over human-interaction."; + leaf name { + type string; + description + "The 'user name' for the SSH client, as defined in + the SSH_MSG_USERAUTH_REQUEST message in RFC 4253."; + reference + "RFC 4253: The Secure Shell (SSH) Transport Layer + Protocol"; + } + container public-keys { + if-feature "local-user-auth-publickey"; + presence + "Indicates that public keys have been configured. + This statement is present so the mandatory descendant + nodes do not imply that this node must be + configured."; + description + "A set of SSH public keys may be used by the SSH + server to authenticate this user. A user is + authenticated if its public key is an exact + match to a configured public key."; + reference + "RFC BBBB: A YANG Data Model for a Truststore"; + uses ts:inline-or-truststore-public-keys-grouping { + refine "inline-or-truststore/inline/inline-definition/" + + "public-key" { + must 'derived-from-or-self(public-key-format,' + + ' "ct:ssh-public-key-format")'; + } + refine "inline-or-truststore/truststore/truststore-" + + "reference" { + must 'not(deref(.)/../ts:public-key/ts:public-key-' + + 'format[not(derived-from-or-self(., "ct:ssh-' + + 'public-key-format"))])'; + } + } + } + leaf password { + if-feature "local-user-auth-password"; + type ianach:crypt-hash; + description + "The password for this user."; + } + container hostbased { + if-feature "local-user-auth-hostbased"; + presence + "Indicates that hostbased [RFC4252] keys have been + configured. This statement is present so the + mandatory descendant nodes do not imply that this + node must be configured."; + description + "A set of SSH host keys used by the SSH server to + authenticate this user's host. A user's host is + authenticated if its host key is an exact match + to a configured host key."; + reference + "RFC 4252: The Secure Shell (SSH) Transport Layer + RFC BBBB: A YANG Data Model for a Truststore"; + uses ts:inline-or-truststore-public-keys-grouping { + refine "inline-or-truststore/inline/inline-definition/" + + "public-key" { + must 'derived-from-or-self(public-key-format,' + + ' "ct:ssh-public-key-format")'; + } + refine "inline-or-truststore/truststore/truststore-" + + "reference" { + must 'not(deref(.)/../ts:public-key/ts:public-key-' + + 'format[not(derived-from-or-self(., "ct:ssh-' + + 'public-key-format"))])'; + } + } + } + leaf none { + if-feature "local-user-auth-none"; + type empty; + description + "Indicates that the 'none' method is configured + for this user."; + reference + "RFC 4252: The Secure Shell (SSH) Authentication + Protocol."; + } + } + } + container ca-certs { + if-feature "sshcmn:ssh-x509-certs"; + presence + "Indicates that CA certificates have been configured. + This statement is present so the mandatory descendant + nodes do not imply this node must be configured."; + description + "A set of certificate authority (CA) certificates used by + the SSH server to authenticate SSH client certificates. + A client certificate is authenticated if it has a valid + chain of trust to a configured CA certificate."; + reference + "RFC BBBB: A YANG Data Model for a Truststore"; + uses ts:inline-or-truststore-certs-grouping; + } + container ee-certs { + if-feature "sshcmn:ssh-x509-certs"; + presence + "Indicates that EE certificates have been configured. + This statement is present so the mandatory descendant + nodes do not imply this node must be configured."; + description + "A set of client certificates (i.e., end entity + certificates) used by the SSH server to authenticate + the certificates presented by SSH clients. A client + certificate is authenticated if it is an exact match + to a configured end-entity certificate."; + reference + "RFC BBBB: A YANG Data Model for a Truststore"; + uses ts:inline-or-truststore-certs-grouping; + } + } // container client-authentication + + container transport-params { + nacm:default-deny-write; + if-feature "sshcmn:transport-params"; + description + "Configurable parameters of the SSH transport layer."; + uses sshcmn:transport-params-grouping; + } // container transport-params + + container keepalives { + nacm:default-deny-write; + if-feature "ssh-server-keepalives"; + presence + "Indicates that the SSH server proactively tests the + aliveness of the remote SSH client."; + description + "Configures the keep-alive policy, to proactively test + the aliveness of the SSH client. An unresponsive SSH + client is dropped after approximately max-wait * + max-attempts seconds. Per Section 4 of RFC 4254, + the SSH server SHOULD send an SSH_MSG_GLOBAL_REQUEST + message with a purposely nonexistent 'request name' + value (e.g., keepalive@ietf.org) and the 'want reply' + value set to '1'."; + reference + "RFC 4254: The Secure Shell (SSH) Connection Protocol"; + leaf max-wait { + type uint16 { + range "1..max"; + } + units "seconds"; + default "30"; + description + "Sets the amount of time in seconds after which + if no data has been received from the SSH client, + a SSH-level message will be sent to test the + aliveness of the SSH client."; + } + leaf max-attempts { + type uint8; + default "3"; + description + "Sets the maximum number of sequential keep-alive + messages that can fail to obtain a response from + the SSH client before assuming the SSH client is + no longer alive."; + } + } + } // grouping ssh-server-grouping + +} diff --git a/modules/ietf-tcp-client@2023-04-17.yang b/modules/ietf-tcp-client@2023-04-17.yang new file mode 100644 index 00000000..95e62149 --- /dev/null +++ b/modules/ietf-tcp-client@2023-04-17.yang @@ -0,0 +1,326 @@ +module ietf-tcp-client { + yang-version 1.1; + namespace "urn:ietf:params:xml:ns:yang:ietf-tcp-client"; + prefix tcpc; + + import ietf-inet-types { + prefix inet; + reference + "RFC 6991: Common YANG Data Types"; + } + + import ietf-crypto-types { + prefix ct; + reference + "RFC AAAA: YANG Data Types and Groupings for Cryptography"; + } + + import ietf-tcp-common { + prefix tcpcmn; + reference + "RFC DDDD: YANG Groupings for TCP Clients and TCP Servers"; + } + + organization + "IETF NETCONF (Network Configuration) Working Group and the + IETF TCP Maintenance and Minor Extensions (TCPM) Working Group"; + + contact + "WG Web: https://datatracker.ietf.org/wg/netconf + https://datatracker.ietf.org/wg/tcpm + WG List: NETCONF WG list + TCPM WG list + Authors: Kent Watsen + Michael Scharf + "; + + description + "This module defines reusable groupings for TCP clients that + can be used as a basis for specific TCP client instances. + + Copyright (c) 2023 IETF Trust and the persons identified + as authors of the code. All rights reserved. + + Redistribution and use in source and binary forms, with + or without modification, is permitted pursuant to, and + subject to the license terms contained in, the Revised + BSD License set forth in Section 4.c of the IETF Trust's + Legal Provisions Relating to IETF Documents + (https://trustee.ietf.org/license-info). + + This version of this YANG module is part of RFC DDDD + (https://www.rfc-editor.org/info/rfcDDDD); see the RFC + itself for full legal notices. + + The key words 'MUST', 'MUST NOT', 'REQUIRED', 'SHALL', + 'SHALL NOT', 'SHOULD', 'SHOULD NOT', 'RECOMMENDED', + 'NOT RECOMMENDED', 'MAY', and 'OPTIONAL' in this document + are to be interpreted as described in BCP 14 (RFC 2119) + (RFC 8174) when, and only when, they appear in all + capitals, as shown here."; + + revision 2023-04-17 { + description + "Initial version"; + reference + "RFC DDDD: YANG Groupings for TCP Clients and TCP Servers"; + } + + // Features + + feature local-binding-supported { + description + "Indicates that the server supports configuring local + bindings (i.e., the local address and local port) for + TCP clients."; + } + + feature tcp-client-keepalives { + description + "Per socket TCP keepalive parameters are configurable for + TCP clients on the server implementing this feature."; + reference + "RFC 9293: Transmission Control Protocol (TCP)"; + } + + feature proxy-connect { + description + "Proxy connection configuration is configurable for + TCP clients on the server implementing this feature. + Currently supports SOCKS 4, SOCKS 4a, and SOCKS 5."; + reference + "SOCKS Proceedings: + 1992 Usenix Security Symposium. + OpenSSH message: + SOCKS 4A: A Simple Extension to SOCKS 4 Protocol + https://www.openssh.com/txt/socks4a.protocol + RFC 1928: + SOCKS Protocol Version 5"; + } + + feature socks5-gss-api { + description + "Indicates that the server, when acting as a TCP-client, + supports authenticating to a SOCKS Version 5 proxy server + using GSSAPI credentials."; + reference + "RFC 1928: SOCKS Protocol Version 5"; + } + + feature socks5-username-password { + description + "Indicates that the server, when acting as a TCP-client, + supports authenticating to a SOCKS Version 5 proxy server + using 'username' and 'password' credentials."; + reference + "RFC 1928: SOCKS Protocol Version 5"; + } + + // Groupings + + grouping tcp-client-grouping { + description + "A reusable grouping for configuring a TCP client. + + Note that this grouping uses fairly typical descendant + node names such that a stack of 'uses' statements will + have name conflicts. It is intended that the consuming + data model will resolve the issue (e.g., by wrapping + the 'uses' statement in a container called + 'tcp-client-parameters'). This model purposely does + not do this itself so as to provide maximum flexibility + to consuming models."; + + leaf remote-address { + type inet:host; + mandatory true; + description + "The IP address or hostname of the remote peer to + establish a connection with. If a domain name is + configured, then the DNS resolution should happen on + each connection attempt. If the DNS resolution + results in multiple IP addresses, the IP addresses + are tried according to local preference order until + a connection has been established or until all IP + addresses have failed."; + } + leaf remote-port { + type inet:port-number; + default "0"; + description + "The IP port number for the remote peer to establish a + connection with. An invalid default value is used + so that importing modules may 'refine' it with the + appropriate default port number value."; + } + leaf local-address { + if-feature "local-binding-supported"; + type inet:ip-address; + description + "The local IP address/interface to bind to for when + connecting to the remote peer. INADDR_ANY ('0.0.0.0') or + INADDR6_ANY ('0:0:0:0:0:0:0:0' a.k.a. '::') MAY be used to + explicitly indicate the implicit default, that the server + can bind to any IPv4 or IPv6 addresses, respectively."; + } + leaf local-port { + if-feature "local-binding-supported"; + type inet:port-number; + default "0"; + description + "The local IP port number to bind to for when connecting + to the remote peer. The port number '0', which is the + default value, indicates that any available local port + number may be used."; + } + container proxy-server { + if-feature "proxy-connect"; + presence + "Indicates that a proxy connection has been configured. + Present so that the mandatory descendant nodes do not + imply that this node must be configured."; + choice proxy-type { + mandatory true; + description + "Selects a proxy connection protocol."; + case socks4 { + container socks4-parameters { + leaf remote-address { + type inet:ip-address; + mandatory true; + description + "The IP address of the proxy server."; + } + leaf remote-port { + type inet:port-number; + default "1080"; + description + "The IP port number for the proxy server."; + } + description + "Parameters for connecting to a TCP-based proxy + server using the SOCKS4 protocol."; + reference + "SOCKS, Proceedings: 1992 Usenix Security Symposium."; + } + } + case socks4a { + container socks4a-parameters { + leaf remote-address { + type inet:host; + mandatory true; + description + "The IP address or hostname of the proxy server."; + } + leaf remote-port { + type inet:port-number; + default "1080"; + description + "The IP port number for the proxy server."; + } + description + "Parameters for connecting to a TCP-based proxy + server using the SOCKS4a protocol."; + reference + "SOCKS Proceedings: + 1992 Usenix Security Symposium. + OpenSSH message: + SOCKS 4A: A Simple Extension to SOCKS 4 Protocol + https://www.openssh.com/txt/socks4a.protocol"; + } + } + case socks5 { + container socks5-parameters { + leaf remote-address { + type inet:host; + mandatory true; + description + "The IP address or hostname of the proxy server."; + } + leaf remote-port { + type inet:port-number; + default "1080"; + description + "The IP port number for the proxy server."; + } + container authentication-parameters { + presence + "Indicates that an authentication mechanism + has been configured. Present so that the + mandatory descendant nodes do not imply that + this node must be configured."; + description + "A container for SOCKS Version 5 authentication + mechanisms. + + A complete list of methods is defined at: + https://www.iana.org/assignments/socks-methods + /socks-methods.xhtml."; + reference + "RFC 1928: SOCKS Protocol Version 5"; + choice auth-type { + mandatory true; + description + "A choice amongst supported SOCKS Version 5 + authentication mechanisms."; + case gss-api { + if-feature "socks5-gss-api"; + container gss-api { + description + "Contains GSS-API configuration. Defines + as an empty container to enable specific + GSS-API configuration to be augmented in + by future modules."; + reference + "RFC 1928: SOCKS Protocol Version 5 + RFC 2743: Generic Security Service + Application Program Interface + Version 2, Update 1"; + } + } + case username-password { + if-feature "socks5-username-password"; + container username-password { + leaf username { + type string; + mandatory true; + description + "The 'username' value to use for client + identification."; + } + uses ct:password-grouping { + description + "The password to be used for client + authentication."; + } + description + "Contains Username/Password configuration."; + reference + "RFC 1929: Username/Password Authentication + for SOCKS V5"; + } + } + } + } + description + "Parameters for connecting to a TCP-based proxy server + using the SOCKS5 protocol."; + reference + "RFC 1928: SOCKS Protocol Version 5"; + } + } + } + description + "Proxy server settings."; + } + + uses tcpcmn:tcp-common-grouping { + augment "keepalives" { + if-feature "tcp-client-keepalives"; + description + "Add an if-feature statement so that implementations + can choose to support TCP client keepalives."; + } + } + } +} diff --git a/modules/ietf-tcp-common@2023-04-17.yang b/modules/ietf-tcp-common@2023-04-17.yang new file mode 100644 index 00000000..100380ff --- /dev/null +++ b/modules/ietf-tcp-common@2023-04-17.yang @@ -0,0 +1,117 @@ +module ietf-tcp-common { + yang-version 1.1; + namespace "urn:ietf:params:xml:ns:yang:ietf-tcp-common"; + prefix tcpcmn; + + organization + "IETF NETCONF (Network Configuration) Working Group and the + IETF TCP Maintenance and Minor Extensions (TCPM) Working Group"; + + contact + "WG Web: https://datatracker.ietf.org/wg/netconf + https://datatracker.ietf.org/wg/tcpm + WG List: NETCONF WG list + TCPM WG list + Authors: Kent Watsen + Michael Scharf + "; + + description + "This module defines reusable groupings for TCP commons that + can be used as a basis for specific TCP common instances. + + Copyright (c) 2023 IETF Trust and the persons identified + as authors of the code. All rights reserved. + + Redistribution and use in source and binary forms, with + or without modification, is permitted pursuant to, and + subject to the license terms contained in, the Revised + BSD License set forth in Section 4.c of the IETF Trust's + Legal Provisions Relating to IETF Documents + (https://trustee.ietf.org/license-info). + + This version of this YANG module is part of RFC DDDD + (https://www.rfc-editor.org/info/rfcDDDD); see the RFC + itself for full legal notices. + + The key words 'MUST', 'MUST NOT', 'REQUIRED', 'SHALL', + 'SHALL NOT', 'SHOULD', 'SHOULD NOT', 'RECOMMENDED', + 'NOT RECOMMENDED', 'MAY', and 'OPTIONAL' in this document + are to be interpreted as described in BCP 14 (RFC 2119) + (RFC 8174) when, and only when, they appear in all + capitals, as shown here."; + + revision 2023-04-17 { + description + "Initial version"; + reference + "RFC DDDD: YANG Groupings for TCP Clients and TCP Servers"; + } + + // Features + + feature keepalives-supported { + description + "Indicates that keepalives are supported."; + } + + // Groupings + grouping tcp-common-grouping { + description + "A reusable grouping for configuring TCP parameters common + to TCP connections as well as the operating system as a + whole."; + container keepalives { + if-feature "keepalives-supported"; + presence + "Indicates that keepalives are enabled. This statement is + present so the mandatory descendant nodes do not imply that + this node must be configured."; + description + "Configures the keep-alive policy, to proactively test the + aliveness of the TCP peer. An unresponsive TCP peer is + dropped after approximately (idle-time + max-probes + * probe-interval) seconds."; + reference + "RFC 9293: + Transmission Control Protocol (TCP), Section 3.8.4.."; + leaf idle-time { + type uint16 { + range "1..max"; + } + units "seconds"; + mandatory true; + description + "Sets the amount of time after which if no data has been + received from the TCP peer, a TCP-level probe message + will be sent to test the aliveness of the TCP peer. + Two hours (7200 seconds) is safe value, per RFC 1122."; + reference + "RFC 1122: + Requirements for Internet Hosts -- Communication Layers"; + } + leaf max-probes { + type uint16 { + range "1..max"; + } + mandatory true; + description + "Sets the maximum number of sequential keep-alive probes + that can fail to obtain a response from the TCP peer + before assuming the TCP peer is no longer alive."; + } + leaf probe-interval { + type uint16 { + range "1..max"; + } + units "seconds"; + mandatory true; + description + "Sets the time interval between failed probes. The interval + SHOULD be significantly longer than one second in order to + avoid harm on a congested link."; + } + } // container keepalives + } // grouping tcp-common-grouping + +} diff --git a/modules/ietf-tcp-server@2023-04-17.yang b/modules/ietf-tcp-server@2023-04-17.yang new file mode 100644 index 00000000..73449448 --- /dev/null +++ b/modules/ietf-tcp-server@2023-04-17.yang @@ -0,0 +1,115 @@ +module ietf-tcp-server { + yang-version 1.1; + namespace "urn:ietf:params:xml:ns:yang:ietf-tcp-server"; + prefix tcps; + + import ietf-inet-types { + prefix inet; + reference + "RFC 6991: Common YANG Data Types"; + } + + import ietf-tcp-common { + prefix tcpcmn; + reference + "RFC DDDD: YANG Groupings for TCP Clients and TCP Servers"; + } + + organization + "IETF NETCONF (Network Configuration) Working Group and the + IETF TCP Maintenance and Minor Extensions (TCPM) Working Group"; + + contact + "WG Web: https://datatracker.ietf.org/wg/netconf + https://datatracker.ietf.org/wg/tcpm + WG List: NETCONF WG list + TCPM WG list + Authors: Kent Watsen + Michael Scharf + "; + description + "This module defines reusable groupings for TCP servers that + can be used as a basis for specific TCP server instances. + + Copyright (c) 2023 IETF Trust and the persons identified + as authors of the code. All rights reserved. + + Redistribution and use in source and binary forms, with + or without modification, is permitted pursuant to, and + subject to the license terms contained in, the Revised + BSD License set forth in Section 4.c of the IETF Trust's + Legal Provisions Relating to IETF Documents + (https://trustee.ietf.org/license-info). + + This version of this YANG module is part of RFC DDDD + (https://www.rfc-editor.org/info/rfcDDDD); see the RFC + itself for full legal notices. + + The key words 'MUST', 'MUST NOT', 'REQUIRED', 'SHALL', + 'SHALL NOT', 'SHOULD', 'SHOULD NOT', 'RECOMMENDED', + 'NOT RECOMMENDED', 'MAY', and 'OPTIONAL' in this document + are to be interpreted as described in BCP 14 (RFC 2119) + (RFC 8174) when, and only when, they appear in all + capitals, as shown here."; + + revision 2023-04-17 { + description + "Initial version"; + reference + "RFC DDDD: YANG Groupings for TCP Clients and TCP Servers"; + } + + // Features + + feature tcp-server-keepalives { + description + "Per socket TCP keepalive parameters are configurable for + TCP servers on the server implementing this feature."; + reference + "RFC 9293: Transmission Control Protocol (TCP)"; + } + + // Groupings + + grouping tcp-server-grouping { + description + "A reusable grouping for configuring a TCP server. + + Note that this grouping uses fairly typical descendant + node names such that a stack of 'uses' statements will + have name conflicts. It is intended that the consuming + data model will resolve the issue (e.g., by wrapping + the 'uses' statement in a container called + 'tcp-server-parameters'). This model purposely does + not do this itself so as to provide maximum flexibility + to consuming models."; + leaf local-address { + type inet:ip-address; + mandatory true; + description + "The local IP address to listen on for incoming + TCP client connections. INADDR_ANY (0.0.0.0) or + INADDR6_ANY (0:0:0:0:0:0:0:0 a.k.a. ::) MUST be + used when the server is to listen on all IPv4 or + IPv6 addresses, respectively."; + } + leaf local-port { + type inet:port-number; + default "0"; + description + "The local port number to listen on for incoming TCP + client connections. An invalid default value (0) + is used (instead of 'mandatory true') so that an + application level data model may 'refine' it with + an application specific default port number value."; + } + uses tcpcmn:tcp-common-grouping { + augment "keepalives" { + if-feature "tcp-server-keepalives"; + description + "Add an if-feature statement so that implementations + can choose to support TCP server keepalives."; + } + } + } +} diff --git a/modules/ietf-tls-common@2023-04-17.yang b/modules/ietf-tls-common@2023-04-17.yang new file mode 100644 index 00000000..5ad06f41 --- /dev/null +++ b/modules/ietf-tls-common@2023-04-17.yang @@ -0,0 +1,312 @@ +module ietf-tls-common { + yang-version 1.1; + namespace "urn:ietf:params:xml:ns:yang:ietf-tls-common"; + prefix tlscmn; + + import iana-tls-cipher-suite-algs { + prefix tlscsa; + reference + "RFC FFFF: YANG Groupings for TLS Clients and SSH Servers"; + } + + import ietf-crypto-types { + prefix ct; + reference + "RFC AAAA: YANG Data Types and Groupings for Cryptography"; + } + + import ietf-keystore { + prefix ks; + reference + "RFC CCCC: A YANG Data Model for a Keystore"; + } + + organization + "IETF NETCONF (Network Configuration) Working Group"; + + contact + "WG List: NETCONF WG list + WG Web: https://datatracker.ietf.org/wg/netconf + Author: Kent Watsen + Author: Jeff Hartley + Author: Gary Wu "; + + description + "This module defines a common features and groupings for + Transport Layer Security (TLS). + + Copyright (c) 2023 IETF Trust and the persons identified + as authors of the code. All rights reserved. + + Redistribution and use in source and binary forms, with + or without modification, is permitted pursuant to, and + subject to the license terms contained in, the Revised + BSD License set forth in Section 4.c of the IETF Trust's + Legal Provisions Relating to IETF Documents + (https://trustee.ietf.org/license-info). + + This version of this YANG module is part of RFC FFFF + (https://www.rfc-editor.org/info/rfcFFFF); see the RFC + itself for full legal notices. + + The key words 'MUST', 'MUST NOT', 'REQUIRED', 'SHALL', + 'SHALL NOT', 'SHOULD', 'SHOULD NOT', 'RECOMMENDED', + 'NOT RECOMMENDED', 'MAY', and 'OPTIONAL' in this document + are to be interpreted as described in BCP 14 (RFC 2119) + (RFC 8174) when, and only when, they appear in all + capitals, as shown here."; + + revision 2023-04-17 { + description + "Initial version"; + reference + "RFC FFFF: YANG Groupings for TLS Clients and TLS Servers"; + } + + // Features + + feature tls10 { + status "obsolete"; + description + "TLS Protocol Version 1.0 is supported. TLS 1.0 is obsolete + and thus it is NOT RECOMMENDED to enable this feature."; + reference + "RFC 2246: The TLS Protocol Version 1.0"; + } + + feature tls11 { + status "obsolete"; + description + "TLS Protocol Version 1.1 is supported. TLS 1.1 is obsolete + and thus it is NOT RECOMMENDED to enable this feature."; + reference + "RFC 4346: The Transport Layer Security (TLS) Protocol + Version 1.1"; + } + + feature tls12 { + status "deprecated"; + description + "TLS Protocol Version 1.2 is supported TLS 1.2 is obsolete + and thus it is NOT RECOMMENDED to enable this feature."; + reference + "RFC 5246: The Transport Layer Security (TLS) Protocol + Version 1.2"; + } + + feature tls13 { + description + "TLS Protocol Version 1.3 is supported."; + reference + "RFC 8446: The Transport Layer Security (TLS) + Protocol Version 1.3"; + } + + feature hello-params { + description + "TLS hello message parameters are configurable."; + } + + feature public-key-generation { + description + "Indicates that the server implements the + 'generate-public-key' RPC."; + } + + // Identities + + identity tls-version-base { + description + "Base identity used to identify TLS protocol versions."; + } + + identity tls10 { + if-feature "tls10"; + base tls-version-base; + status "obsolete"; + description + "TLS Protocol Version 1.0."; + reference + "RFC 2246: The TLS Protocol Version 1.0"; + } + + identity tls11 { + if-feature "tls11"; + base tls-version-base; + status "obsolete"; + description + "TLS Protocol Version 1.1."; + reference + "RFC 4346: The Transport Layer Security (TLS) Protocol + Version 1.1"; + } + + identity tls12 { + if-feature "tls12"; + base tls-version-base; + status "deprecated"; + description + "TLS Protocol Version 1.2."; + reference + "RFC 5246: The Transport Layer Security (TLS) Protocol + Version 1.2"; + } + + identity tls13 { + if-feature "tls13"; + base tls-version-base; + description + "TLS Protocol Version 1.3."; + reference + "RFC 8446: The Transport Layer Security (TLS) + Protocol Version 1.3"; + } + + typedef epsk-supported-hash { + type enumeration { + enum sha-256 { + description + "The SHA-256 Hash."; + } + enum sha-384 { + description + "The SHA-384 Hash."; + } + } + description + "As per Section 4.2.11 of RFC 8446, the hash algorithm + supported by an instance of an External Pre-Shared + Key (EPSK)."; + reference + "RFC 8446: The Transport Layer Security (TLS) + Protocol Version 1.3 + I-D.ietf-tls-external-psk-importer: Importing + External PSKs for TLS + I-D.ietf-tls-external-psk-guidance: Guidance + for External PSK Usage in TLS"; + } + + // Groupings + + grouping hello-params-grouping { + description + "A reusable grouping for TLS hello message parameters."; + reference + "RFC 5246: The Transport Layer Security (TLS) Protocol + Version 1.2 + RFC 8446: The Transport Layer Security (TLS) Protocol + Version 1.3"; + container tls-versions { + description + "Parameters regarding TLS versions."; + leaf-list tls-version { + type identityref { + base tls-version-base; + } + description + "Acceptable TLS protocol versions. + + If this leaf-list is not configured (has zero elements) + the acceptable TLS protocol versions are implementation- + defined."; + } + } + container cipher-suites { + description + "Parameters regarding cipher suites."; + leaf-list cipher-suite { + type identityref { + base tlscsa:cipher-suite-alg-base; + } + ordered-by user; + description + "Acceptable cipher suites in order of descending + preference. The configured host key algorithms should + be compatible with the algorithm used by the configured + private key. Please see Section 5 of RFC FFFF for + valid combinations. + + If this leaf-list is not configured (has zero elements) + the acceptable cipher suites are implementation- + defined."; + reference + "RFC FFFF: YANG Groupings for TLS Clients and TLS Servers"; + } + } + } // hello-params-grouping + + rpc generate-public-key { + if-feature "public-key-generation"; + description + "Requests the device to generate an public key using + the specified key algorithm."; + input { + leaf algorithm { + type tlscsa:cipher-suite-algorithm-ref; + mandatory true; + description + "The cipher suite algorithm that the generated key is + to work with. Implementations derive the public key + algorithm from the cipher suite algorithm. Example: + cipher suite 'tls-rsa-with-aes-256-cbc-sha256' maps + to the RSA public key."; + } + leaf bits { + type uint16; + description + "Specifies the number of bits in the key to create. + For RSA keys, the minimum size is 1024 bits and + the default is 3072 bits. Generally, 3072 bits is + considered sufficient. DSA keys must be exactly 1024 + bits as specified by FIPS 186-2. For elliptical + keys, the 'bits' value determines the key length + of the curve (e.g., 256, 384 or 521), where valid + values supported by the server are conveyed via an + unspecified mechanism. For some public algorithms, + the keys have a fixed length and the 'bits' value, + if specified, will be ignored."; + } + choice private-key-encoding { + default cleartext; + description + "A choice amongst optional private key handling."; + case cleartext { + if-feature "ct:cleartext-private-keys"; + leaf cleartext { + type empty; + description + "Indicates that the private key is to be returned + as a cleartext value."; + } + } + case encrypt { + if-feature "ct:encrypted-private-keys"; + container encrypt-with { + description + "Indicates that the key is to be encrypted using + the specified symmetric or asymmetric key."; + uses ks:encrypted-by-choice-grouping; + } + } + case hide { + if-feature "ct:hidden-private-keys"; + leaf hide { + type empty; + description + "Indicates that the private key is to be hidden. + + Unlike the 'cleartext' and 'encrypt' options, the + key returned is a placeholder for an internally + stored key. See the 'Support for Built-in Keys' + section in RFC CCCC for information about hidden + keys."; + } + } + } + } + output { + uses ct:asymmetric-key-pair-grouping; + } + } // end generate-public-key + +} diff --git a/modules/ietf-tls-server@2023-04-17.yang b/modules/ietf-tls-server@2023-04-17.yang new file mode 100644 index 00000000..70db1502 --- /dev/null +++ b/modules/ietf-tls-server@2023-04-17.yang @@ -0,0 +1,528 @@ +module ietf-tls-server { + yang-version 1.1; + namespace "urn:ietf:params:xml:ns:yang:ietf-tls-server"; + prefix tlss; + + import ietf-netconf-acm { + prefix nacm; + reference + "RFC 8341: Network Configuration Access Control Model"; + } + + import ietf-crypto-types { + prefix ct; + reference + "RFC AAAA: YANG Data Types and Groupings for Cryptography"; + } + + import ietf-truststore { + prefix ts; + reference + "RFC BBBB: A YANG Data Model for a Truststore"; + } + + import ietf-keystore { + prefix ks; + reference + "RFC CCCC: A YANG Data Model for a Keystore"; + } + + import ietf-tls-common { + prefix tlscmn; + reference + "RFC FFFF: YANG Groupings for TLS Clients and TLS Servers"; + } + + organization + "IETF NETCONF (Network Configuration) Working Group"; + + contact + "WG List: NETCONF WG list + WG Web: https://datatracker.ietf.org/wg/netconf + Author: Kent Watsen + Author: Jeff Hartley "; + + description + "This module defines reusable groupings for TLS servers that + can be used as a basis for specific TLS server instances. + + Copyright (c) 2023 IETF Trust and the persons identified + as authors of the code. All rights reserved. + + Redistribution and use in source and binary forms, with + or without modification, is permitted pursuant to, and + subject to the license terms contained in, the Revised + BSD License set forth in Section 4.c of the IETF Trust's + Legal Provisions Relating to IETF Documents + (https://trustee.ietf.org/license-info). + + This version of this YANG module is part of RFC FFFF + (https://www.rfc-editor.org/info/rfcFFFF); see the RFC + itself for full legal notices. + + The key words 'MUST', 'MUST NOT', 'REQUIRED', 'SHALL', + 'SHALL NOT', 'SHOULD', 'SHOULD NOT', 'RECOMMENDED', + 'NOT RECOMMENDED', 'MAY', and 'OPTIONAL' in this document + are to be interpreted as described in BCP 14 (RFC 2119) + (RFC 8174) when, and only when, they appear in all + capitals, as shown here."; + + revision 2023-04-17 { + description + "Initial version"; + reference + "RFC FFFF: YANG Groupings for TLS Clients and TLS Servers"; + } + + // Features + + feature tls-server-keepalives { + description + "Per socket TLS keepalive parameters are configurable for + TLS servers on the server implementing this feature."; + } + + feature server-ident-x509-cert { + description + "Indicates that the server supports identifying itself + using X.509 certificates."; + reference + "RFC 5280: + Internet X.509 Public Key Infrastructure Certificate + and Certificate Revocation List (CRL) Profile"; + } + + feature server-ident-raw-public-key { + description + "Indicates that the server supports identifying itself + using raw public keys."; + reference + "RFC 7250: + Using Raw Public Keys in Transport Layer Security (TLS) + and Datagram Transport Layer Security (DTLS)"; + } + + feature server-ident-tls12-psk { + description + "Indicates that the server supports identifying itself + using TLS-1.2 PSKs (pre-shared or pairwise-symmetric keys)."; + reference + "RFC 4279: + Pre-Shared Key Ciphersuites for Transport Layer Security + (TLS)"; + } + + feature server-ident-tls13-epsk { + description + "Indicates that the server supports identifying itself + using TLS-1.3 External PSKs (pre-shared keys)."; + reference + "RFC 8446: + The Transport Layer Security (TLS) Protocol Version 1.3"; + } + + feature client-auth-supported { + description + "Indicates that the configuration for how to authenticate + clients can be configured herein. TLS-level client + authentication may not be needed when client authentication + is expected to occur only at another protocol layer."; + } + + feature client-auth-x509-cert { + description + "Indicates that the server supports authenticating clients + using X.509 certificates."; + reference + "RFC 5280: + Internet X.509 Public Key Infrastructure Certificate + and Certificate Revocation List (CRL) Profile"; + } + + feature client-auth-raw-public-key { + description + "Indicates that the server supports authenticating clients + using raw public keys."; + reference + "RFC 7250: + Using Raw Public Keys in Transport Layer Security (TLS) + and Datagram Transport Layer Security (DTLS)"; + } + + feature client-auth-tls12-psk { + description + "Indicates that the server supports authenticating clients + using PSKs (pre-shared or pairwise-symmetric keys)."; + reference + "RFC 4279: + Pre-Shared Key Ciphersuites for Transport Layer Security + (TLS)"; + } + + feature client-auth-tls13-epsk { + description + "Indicates that the server supports authenticating clients + using TLS-1.3 External PSKs (pre-shared keys)."; + reference + "RFC 8446: + The Transport Layer Security (TLS) Protocol Version 1.3"; + } + + // Groupings + + grouping tls-server-grouping { + description + "A reusable grouping for configuring a TLS server without + any consideration for how underlying TCP sessions are + established. + + Note that this grouping uses fairly typical descendant + node names such that a stack of 'uses' statements will + have name conflicts. It is intended that the consuming + data model will resolve the issue (e.g., by wrapping + the 'uses' statement in a container called + 'tls-server-parameters'). This model purposely does + not do this itself so as to provide maximum flexibility + to consuming models."; + + container server-identity { + nacm:default-deny-write; + description + "A locally-defined or referenced end-entity certificate, + including any configured intermediate certificates, the + TLS server will present when establishing a TLS connection + in its Certificate message, as defined in Section 7.4.2 + in RFC 5246 and Section 4.4.2 in RFC 8446."; + reference + "RFC 5246: The Transport Layer Security (TLS) Protocol + Version 1.2 + RFC 8446: The Transport Layer Security (TLS) Protocol + Version 1.3 + RFC CCCC: A YANG Data Model for a Keystore"; + choice auth-type { + mandatory true; + description + "A choice amongst authentication types, of which one must + be enabled (via its associated 'feature') and selected."; + case certificate { + if-feature "server-ident-x509-cert"; + container certificate { + description + "Specifies the server identity using a certificate."; + uses + "ks:inline-or-keystore-end-entity-cert-with-key-" + + "grouping" { + refine "inline-or-keystore/inline/inline-definition" { + must 'derived-from-or-self(public-key-format,' + + ' "ct:subject-public-key-info-format")'; + } + refine "inline-or-keystore/keystore/keystore-reference" + + "/asymmetric-key" { + must 'derived-from-or-self(deref(.)/../ks:public-' + + 'key-format, "ct:subject-public-key-info-' + + 'format")'; + } + } + } + } + case raw-private-key { + if-feature "server-ident-raw-public-key"; + container raw-private-key { + description + "Specifies the server identity using a raw + private key."; + uses ks:inline-or-keystore-asymmetric-key-grouping { + refine "inline-or-keystore/inline/inline-definition" { + must 'derived-from-or-self(public-key-format,' + + ' "ct:subject-public-key-info-format")'; + } + refine + "inline-or-keystore/keystore/keystore-reference" { + must 'derived-from-or-self(deref(.)/../ks:public-' + + 'key-format, "ct:subject-public-key-info-' + + 'format")'; + } + } + } + } + case tls12-psk { + if-feature "server-ident-tls12-psk"; + container tls12-psk { + description + "Specifies the server identity using a PSK (pre-shared + or pairwise-symmetric key)."; + uses ks:inline-or-keystore-symmetric-key-grouping; + leaf id_hint { + type string; + description + "The key 'psk_identity_hint' value used in the TLS + 'ServerKeyExchange' message."; + reference + "RFC 4279: Pre-Shared Key Ciphersuites for + Transport Layer Security (TLS)"; + } + } + } + case tls13-epsk { + if-feature "server-ident-tls13-epsk"; + container tls13-epsk { + description + "An External Pre-Shared Key (EPSK) is established + or provisioned out-of-band, i.e., not from a TLS + connection. An EPSK is a tuple of (Base Key, + External Identity, Hash). External PSKs MUST + NOT be imported for (D)TLS 1.2 or prior versions. + When PSKs are provisioned out of band, the PSK + identity and the KDF hash algorithm to be used + with the PSK MUST also be provisioned. + + The structure of this container is designed + to satisfy the requirements of RFC 8446 + Section 4.2.11, the recommendations from + I-D ietf-tls-external-psk-guidance Section 6, + and the EPSK input fields detailed in + I-D draft-ietf-tls-external-psk-importer + Section 3.1. The base-key is based upon + ks:inline-or-keystore-symmetric-key-grouping + in order to provide users with flexible and + secure storage options."; + reference + "RFC 8446: The Transport Layer Security (TLS) + Protocol Version 1.3 + I-D.ietf-tls-external-psk-importer: Importing + External PSKs for TLS + I-D.ietf-tls-external-psk-guidance: Guidance + for External PSK Usage in TLS"; + uses ks:inline-or-keystore-symmetric-key-grouping; + leaf external-identity { + type string; + mandatory true; + description + "As per Section 4.2.11 of RFC 8446, and Section 4.1 + of I-D. ietf-tls-external-psk-guidance: A sequence + of bytes used to identify an EPSK. A label for a + pre-shared key established externally."; + reference + "RFC 8446: The Transport Layer Security (TLS) + Protocol Version 1.3 + I-D.ietf-tls-external-psk-guidance: + Guidance for External PSK Usage in TLS"; + } + leaf hash { + type tlscmn:epsk-supported-hash; + mandatory true; + description + "As per Section 4.2.11 of RFC 8446, for externally + established PSKs, the Hash algorithm MUST be set + when the PSK is established or default to SHA-256 + if no such algorithm is defined. The server MUST + ensure that it selects a compatible PSK (if any) + and cipher suite. Each PSK MUST only be used + with a single hash function."; + reference + "RFC 8446: The Transport Layer Security (TLS) + Protocol Version 1.3"; + } + leaf context { + type string; + description + "As per Section 4.1 of I-D. + ietf-tls-external-psk-guidance: Context + may include information about peer roles or + identities to mitigate Selfie-style reflection + attacks [Selfie]. If the EPSK is a key derived + from some other protocol or sequence of protocols, + context MUST include a channel binding for the + deriving protocols [RFC5056]. The details of + this binding are protocol specific."; + reference + "I-D.ietf-tls-external-psk-importer: + Importing External PSKs for TLS + I-D.ietf-tls-external-psk-guidance: + Guidance for External PSK Usage in TLS"; + } + leaf target-protocol { + type uint16; + description + "As per Section 3.1 of I-D. + ietf-tls-external-psk-guidance: The protocol + for which a PSK is imported for use."; + reference + "I-D.ietf-tls-external-psk-importer: + Importing External PSKs for TLS"; + } + leaf target-kdf { + type uint16; + description + "As per Section 3.1 of I-D. + ietf-tls-external-psk-guidance: The specific Key + Derivation Function (KDF) for which a PSK is + imported for use."; + reference + "I-D.ietf-tls-external-psk-importer: + Importing External PSKs for TLS"; + } + } + } + } + } // container server-identity + + container client-authentication { + if-feature "client-auth-supported"; + nacm:default-deny-write; + must 'ca-certs or ee-certs or raw-public-keys or tls12-psks + or tls13-epsks'; + presence + "Indicates that client authentication is supported (i.e., + that the server will request clients send certificates). + If not configured, the TLS server SHOULD NOT request the + TLS clients provide authentication credentials."; + description + "Specifies how the TLS server can authenticate TLS clients. + Any combination of credentials is additive and unordered. + + Note that no configuration is required for PSK (pre-shared + or pairwise-symmetric key) based authentication as the key + is necessarily the same as configured in the '../server- + identity' node."; + container ca-certs { + if-feature "client-auth-x509-cert"; + presence + "Indicates that CA certificates have been configured. + This statement is present so the mandatory descendant + nodes do not imply that this node must be configured."; + description + "A set of certificate authority (CA) certificates used by + the TLS server to authenticate TLS client certificates. + A client certificate is authenticated if it has a valid + chain of trust to a configured CA certificate."; + reference + "RFC BBBB: A YANG Data Model for a Truststore"; + uses ts:inline-or-truststore-certs-grouping; + } + container ee-certs { + if-feature "client-auth-x509-cert"; + presence + "Indicates that EE certificates have been configured. + This statement is present so the mandatory descendant + nodes do not imply that this node must be configured."; + description + "A set of client certificates (i.e., end entity + certificates) used by the TLS server to authenticate + certificates presented by TLS clients. A client + certificate is authenticated if it is an exact + match to a configured client certificate."; + reference + "RFC BBBB: A YANG Data Model for a Truststore"; + uses ts:inline-or-truststore-certs-grouping; + } + container raw-public-keys { + if-feature "client-auth-raw-public-key"; + presence + "Indicates that raw public keys have been configured. + This statement is present so the mandatory descendant + nodes do not imply that this node must be configured."; + description + "A set of raw public keys used by the TLS server to + authenticate raw public keys presented by the TLS + client. A raw public key is authenticated if it + is an exact match to a configured raw public key."; + reference + "RFC BBBB: A YANG Data Model for a Truststore"; + uses ts:inline-or-truststore-public-keys-grouping { + refine "inline-or-truststore/inline/inline-definition/" + + "public-key" { + must 'derived-from-or-self(public-key-format,' + + ' "ct:subject-public-key-info-format")'; + } + refine "inline-or-truststore/truststore/truststore-" + + "reference" { + must 'not(deref(.)/../ts:public-key/ts:public-key-' + + 'format[not(derived-from-or-self(., "ct:subject-' + + 'public-key-info-format"))])'; + } + } + } + leaf tls12-psks { + if-feature "client-auth-tls12-psk"; + type empty; + description + "Indicates that the TLS server can authenticate TLS clients + using configured PSKs (pre-shared or pairwise-symmetric + keys). + + No configuration is required since the PSK value is the + same as PSK value configured in the 'server-identity' + node."; + } + leaf tls13-epsks { + if-feature "client-auth-tls13-epsk"; + type empty; + description + "Indicates that the TLS 1.3 server can authenticate TLS + clients using configured external PSKs (pre-shared keys). + + No configuration is required since the PSK value is the + same as PSK value configured in the 'server-identity' + node."; + } + } // container client-authentication + + container hello-params { + nacm:default-deny-write; + if-feature "tlscmn:hello-params"; + uses tlscmn:hello-params-grouping; + description + "Configurable parameters for the TLS hello message."; + } // container hello-params + + container keepalives { + nacm:default-deny-write; + if-feature "tls-server-keepalives"; + description + "Configures the keepalive policy for the TLS server."; + leaf peer-allowed-to-send { + type empty; + description + "Indicates that the remote TLS client is allowed to send + HeartbeatRequest messages, as defined by RFC 6520 + to this TLS server."; + reference + "RFC 6520: Transport Layer Security (TLS) and Datagram + Transport Layer Security (DTLS) Heartbeat Extension"; + } + container test-peer-aliveness { + presence + "Indicates that the TLS server proactively tests the + aliveness of the remote TLS client."; + description + "Configures the keep-alive policy to proactively test + the aliveness of the TLS client. An unresponsive + TLS client is dropped after approximately max-wait + * max-attempts seconds."; + leaf max-wait { + type uint16 { + range "1..max"; + } + units "seconds"; + default "30"; + description + "Sets the amount of time in seconds after which if + no data has been received from the TLS client, a + TLS-level message will be sent to test the + aliveness of the TLS client."; + } + leaf max-attempts { + type uint8; + default "3"; + description + "Sets the maximum number of sequential keep-alive + messages that can fail to obtain a response from + the TLS client before assuming the TLS client is + no longer alive."; + } + } + } // container keepalives + } // grouping tls-server-grouping + +} diff --git a/modules/ietf-truststore@2023-04-17.yang b/modules/ietf-truststore@2023-04-17.yang new file mode 100644 index 00000000..cd0d875f --- /dev/null +++ b/modules/ietf-truststore@2023-04-17.yang @@ -0,0 +1,339 @@ +module ietf-truststore { + yang-version 1.1; + namespace "urn:ietf:params:xml:ns:yang:ietf-truststore"; + prefix ts; + + import ietf-netconf-acm { + prefix nacm; + reference + "RFC 8341: Network Configuration Access Control Model"; + } + + import ietf-crypto-types { + prefix ct; + reference + "RFC AAAA: YANG Data Types and Groupings for Cryptography"; + } + + organization + "IETF NETCONF (Network Configuration) Working Group"; + + contact + "WG Web : https://datatracker.ietf.org/wg/netconf + WG List : NETCONF WG list + Author : Kent Watsen "; + description + "This module defines a 'truststore' to centralize management + of trust anchors including certificates and public keys. + + Copyright (c) 2023 IETF Trust and the persons identified + as authors of the code. All rights reserved. + + Redistribution and use in source and binary forms, with + or without modification, is permitted pursuant to, and + subject to the license terms contained in, the Revised + BSD License set forth in Section 4.c of the IETF Trust's + Legal Provisions Relating to IETF Documents + (https://trustee.ietf.org/license-info). + + This version of this YANG module is part of RFC BBBB + (https://www.rfc-editor.org/info/rfcBBBB); see the RFC + itself for full legal notices. + + The key words 'MUST', 'MUST NOT', 'REQUIRED', 'SHALL', + 'SHALL NOT', 'SHOULD', 'SHOULD NOT', 'RECOMMENDED', + 'NOT RECOMMENDED', 'MAY', and 'OPTIONAL' in this document + are to be interpreted as described in BCP 14 (RFC 2119) + (RFC 8174) when, and only when, they appear in all + capitals, as shown here."; + + revision 2023-04-17 { + description + "Initial version"; + reference + "RFC BBBB: A YANG Data Model for a Truststore"; + } + + /****************/ + /* Features */ + /****************/ + + feature central-truststore-supported { + description + "The 'central-truststore-supported' feature indicates that + the server supports the truststore (i.e., implements the + 'ietf-truststore' module)."; + } + + feature inline-definitions-supported { + description + "The 'inline-definitions-supported' feature indicates that + the server supports locally-defined trust anchors."; + } + feature certificates { + description + "The 'certificates' feature indicates that the server + implements the /truststore/certificate-bags subtree."; + } + + feature public-keys { + description + "The 'public-keys' feature indicates that the server + implements the /truststore/public-key-bags subtree."; + } + + /****************/ + /* Typedefs */ + /****************/ + + typedef certificate-bag-ref { + type leafref { + path "/ts:truststore/ts:certificate-bags/" + + "ts:certificate-bag/ts:name"; + } + description + "This typedef defines a reference to a certificate bag + in the truststore, when this module is implemented."; + } + + typedef certificate-ref { + type leafref { + path "/ts:truststore/ts:certificate-bags/ts:certificate-bag" + + "[ts:name = current()/../ts:certificate-bag]/" + + "ts:certificate/ts:name"; + } + description + "This typedef defines a reference to a specific certificate + in a certificate bag in the truststore, when this module + is implemented. This typedef requires that there exist a + sibling 'leaf' node called 'certificate-bag' that SHOULD + have the typedef 'certificate-bag-ref'."; + } + + typedef public-key-bag-ref { + type leafref { + path "/ts:truststore/ts:public-key-bags/" + + "ts:public-key-bag/ts:name"; + } + description + "This typedef defines a reference to a public key bag + in the truststore, when this module is implemented."; + } + + typedef public-key-ref { + type leafref { + path "/ts:truststore/ts:public-key-bags/ts:public-key-bag" + + "[ts:name = current()/../ts:public-key-bag]/" + + "ts:public-key/ts:name"; + } + description + "This typedef defines a reference to a specific public key + in a public key bag in the truststore, when this module is + implemented. This typedef requires that there exist a + sibling 'leaf' node called 'public-key-bag' that SHOULD + have the typedef 'public-key-bag-ref'."; + } + + /*****************/ + /* Groupings */ + /*****************/ + + grouping inline-or-truststore-certs-grouping { + description + "A grouping that allows the certificates to be either + configured locally, within the using data model, or be a + reference to a certificate bag stored in the truststore. + + Servers that do not 'implement' this module, and hence + 'central-truststore-supported' is not defined, SHOULD + augment in custom 'case' statements enabling references + to the alternate truststore locations."; + choice inline-or-truststore { + nacm:default-deny-write; + mandatory true; + description + "A choice between an inlined definition and a definition + that exists in the truststore."; + case inline { + if-feature "inline-definitions-supported"; + container inline-definition { + description + "A container for locally configured trust anchor + certificates."; + list certificate { + key "name"; + min-elements 1; + description + "A trust anchor certificate."; + leaf name { + type string; + description + "An arbitrary name for this certificate."; + } + uses ct:trust-anchor-cert-grouping { + refine "cert-data" { + mandatory true; + } + } + } + } + } + case truststore { + if-feature "central-truststore-supported"; + if-feature "certificates"; + leaf truststore-reference { + type ts:certificate-bag-ref; + description + "A reference to a certificate bag that exists in the + truststore, when this module is implemented."; + } + } + } + } + + grouping inline-or-truststore-public-keys-grouping { + description + "A grouping that allows the public keys to be either + configured locally, within the using data model, or be a + reference to a public key bag stored in the truststore. + + Servers that do not 'implement' this module, and hence + 'central-truststore-supported' is not defined, SHOULD + augment in custom 'case' statements enabling references + to the alternate truststore locations."; + choice inline-or-truststore { + nacm:default-deny-write; + mandatory true; + description + "A choice between an inlined definition and a definition + that exists in the truststore."; + case inline { + if-feature "inline-definitions-supported"; + container inline-definition { + description + "A container to hold local public key definitions."; + list public-key { + key "name"; + description + "A public key definition."; + leaf name { + type string; + description + "An arbitrary name for this public key."; + } + uses ct:public-key-grouping; + } + } + } + case truststore { + if-feature "central-truststore-supported"; + if-feature "public-keys"; + leaf truststore-reference { + type ts:public-key-bag-ref; + description + "A reference to a bag of public keys that exists + in the truststore, when this module is implemented."; + } + } + } + } + + grouping truststore-grouping { + description + "A grouping definition that enables use in other contexts. + Where used, implementations MUST augment new 'case' + statements into the various inline-or-truststore 'choice' + statements to supply leafrefs to the model-specific + location(s)."; + container certificate-bags { + nacm:default-deny-write; + if-feature "certificates"; + description + "A collection of certificate bags."; + list certificate-bag { + key "name"; + description + "A bag of certificates. Each bag of certificates SHOULD + be for a specific purpose. For instance, one bag could + be used to authenticate a specific set of servers, while + another could be used to authenticate a specific set of + clients."; + leaf name { + type string; + description + "An arbitrary name for this bag of certificates."; + } + leaf description { + type string; + description + "A description for this bag of certificates. The + intended purpose for the bag SHOULD be described."; + } + list certificate { + key "name"; + description + "A trust anchor certificate."; + leaf name { + type string; + description + "An arbitrary name for this certificate."; + } + uses ct:trust-anchor-cert-grouping { + refine "cert-data" { + mandatory true; + } + } + } + } + } + container public-key-bags { + nacm:default-deny-write; + if-feature "public-keys"; + description + "A collection of public key bags."; + list public-key-bag { + key "name"; + description + "A bag of public keys. Each bag of keys SHOULD be for + a specific purpose. For instance, one bag could be used + authenticate a specific set of servers, while another + could be used to authenticate a specific set of clients."; + leaf name { + type string; + description + "An arbitrary name for this bag of public keys."; + } + leaf description { + type string; + description + "A description for this bag public keys. The + intended purpose for the bag SHOULD be described."; + } + list public-key { + key "name"; + description + "A public key."; + leaf name { + type string; + description + "An arbitrary name for this public key."; + } + uses ct:public-key-grouping; + } + } + } + } + + /*********************************/ + /* Protocol accessible nodes */ + /*********************************/ + + container truststore { + if-feature central-truststore-supported; + nacm:default-deny-write; + description + "The truststore contains bags of certificates and + public keys."; + uses truststore-grouping; + } +} diff --git a/modules/ietf-x509-cert-to-name@2014-12-10.yang b/modules/ietf-x509-cert-to-name@2014-12-10.yang new file mode 100644 index 00000000..53b5484a --- /dev/null +++ b/modules/ietf-x509-cert-to-name@2014-12-10.yang @@ -0,0 +1,314 @@ + module ietf-x509-cert-to-name { + + yang-version 1; + + namespace + "urn:ietf:params:xml:ns:yang:ietf-x509-cert-to-name"; + + prefix x509c2n; + + import ietf-yang-types { + prefix yang; + } + + organization + "IETF NETMOD (NETCONF Data Modeling Language) Working Group"; + + contact + "WG Web: +WG List: + +WG Chair: Thomas Nadeau + + +WG Chair: Juergen Schoenwaelder + + +Editor: Martin Bjorklund + + +Editor: Juergen Schoenwaelder + "; + + description + "This module contains a collection of YANG definitions for +extracting a name from an X.509 certificate. +The algorithm used to extract a name from an X.509 certificate +was first defined in RFC 6353. + +Copyright (c) 2014 IETF Trust and the persons identified as +authors of the code. All rights reserved. + +Redistribution and use in source and binary forms, with or +without modification, is permitted pursuant to, and subject +to the license terms contained in, the Simplified BSD License +set forth in Section 4.c of the IETF Trust's Legal Provisions +Relating to IETF Documents +(http://trustee.ietf.org/license-info). + +This version of this YANG module is part of RFC 7407; see +the RFC itself for full legal notices."; + + reference + "RFC 6353: Transport Layer Security (TLS) Transport Model for + the Simple Network Management Protocol (SNMP)"; + + + revision "2014-12-10" { + description "Initial revision."; + reference + "RFC 7407: A YANG Data Model for SNMP Configuration"; + + } + + + typedef tls-fingerprint { + type yang:hex-string { + pattern + '([0-9a-fA-F]){2}(:([0-9a-fA-F]){2}){0,254}'; + } + description + "A fingerprint value that can be used to uniquely reference +other data of potentially arbitrary length. + +A tls-fingerprint value is composed of a 1-octet hashing +algorithm identifier followed by the fingerprint value. The +first octet value identifying the hashing algorithm is taken +from the IANA 'TLS HashAlgorithm Registry' (RFC 5246). The +remaining octets are filled using the results of the hashing +algorithm."; + reference + "RFC 6353: Transport Layer Security (TLS) Transport Model + for the Simple Network Management Protocol (SNMP). + SNMP-TLS-TM-MIB.SnmpTLSFingerprint"; + + } + + identity cert-to-name { + description + "Base identity for algorithms to derive a name from a +certificate."; + } + + identity specified { + base cert-to-name; + description + "Directly specifies the name to be used for the certificate. +The value of the leaf 'name' in the cert-to-name list is +used."; + reference + "RFC 6353: Transport Layer Security (TLS) Transport Model + for the Simple Network Management Protocol (SNMP). + SNMP-TLS-TM-MIB.snmpTlstmCertSpecified"; + + } + + identity san-rfc822-name { + base cert-to-name; + description + "Maps a subjectAltName's rfc822Name to a name. The local part +of the rfc822Name is passed unaltered, but the host-part of +the name must be passed in lowercase. For example, the +rfc822Name field FooBar@Example.COM is mapped to name +FooBar@example.com."; + reference + "RFC 6353: Transport Layer Security (TLS) Transport Model + for the Simple Network Management Protocol (SNMP). + SNMP-TLS-TM-MIB.snmpTlstmCertSANRFC822Name"; + + } + + identity san-dns-name { + base cert-to-name; + description + "Maps a subjectAltName's dNSName to a name after first +converting it to all lowercase (RFC 5280 does not specify +converting to lowercase, so this involves an extra step). +This mapping results in a 1:1 correspondence between +subjectAltName dNSName values and the name values."; + reference + "RFC 6353: Transport Layer Security (TLS) Transport Model + for the Simple Network Management Protocol (SNMP). + SNMP-TLS-TM-MIB.snmpTlstmCertSANDNSName"; + + } + + identity san-ip-address { + base cert-to-name; + description + "Maps a subjectAltName's iPAddress to a name by +transforming the binary-encoded address as follows: + + 1) for IPv4, the value is converted into a + decimal-dotted quad address (e.g., '192.0.2.1'). + + 2) for IPv6 addresses, the value is converted into a + 32-character, all-lowercase hexadecimal string + without any colon separators. + +This mapping results in a 1:1 correspondence between +subjectAltName iPAddress values and the name values."; + reference + "RFC 6353: Transport Layer Security (TLS) Transport Model + for the Simple Network Management Protocol (SNMP). + SNMP-TLS-TM-MIB.snmpTlstmCertSANIpAddress"; + + } + + identity san-any { + base cert-to-name; + description + "Maps any of the following fields using the corresponding +mapping algorithms: + + +------------+-----------------+ + | Type | Algorithm | + |------------+-----------------| + | rfc822Name | san-rfc822-name | + | dNSName | san-dns-name | + | iPAddress | san-ip-address | + +------------+-----------------+ + +The first matching subjectAltName value found in the +certificate of the above types MUST be used when deriving +the name. The mapping algorithm specified in the +'Algorithm' column MUST be used to derive the name. + +This mapping results in a 1:1 correspondence between +subjectAltName values and name values. The three sub-mapping +algorithms produced by this combined algorithm cannot produce +conflicting results between themselves."; + reference + "RFC 6353: Transport Layer Security (TLS) Transport Model + for the Simple Network Management Protocol (SNMP). + SNMP-TLS-TM-MIB.snmpTlstmCertSANAny"; + + } + + identity common-name { + base cert-to-name; + description + "Maps a certificate's CommonName to a name after converting +it to a UTF-8 encoding. The usage of CommonNames is +deprecated, and users are encouraged to use subjectAltName +mapping methods instead. This mapping results in a 1:1 +correspondence between certificate CommonName values and name +values."; + reference + "RFC 6353: Transport Layer Security (TLS) Transport Model + for the Simple Network Management Protocol (SNMP). + SNMP-TLS-TM-MIB.snmpTlstmCertCommonName"; + + } + + grouping cert-to-name { + description + "Defines nodes for mapping certificates to names. Modules +that use this grouping should describe how the resulting +name is used."; + list cert-to-name { + key "id"; + description + "This list defines how certificates are mapped to names. +The name is derived by considering each cert-to-name +list entry in order. The cert-to-name entry's fingerprint +determines whether the list entry is a match: + +1) If the cert-to-name list entry's fingerprint value + matches that of the presented certificate, then consider + the list entry a successful match. + +2) If the cert-to-name list entry's fingerprint value + matches that of a locally held copy of a trusted CA + certificate, and that CA certificate was part of the CA + certificate chain to the presented certificate, then + consider the list entry a successful match. + +Once a matching cert-to-name list entry has been found, the +map-type is used to determine how the name associated with +the certificate should be determined. See the map-type +leaf's description for details on determining the name value. +If it is impossible to determine a name from the cert-to-name +list entry's data combined with the data presented in the +certificate, then additional cert-to-name list entries MUST +be searched to look for another potential match. + +Security administrators are encouraged to make use of +certificates with subjectAltName fields that can be mapped to +names so that a single root CA certificate can allow all +child certificates' subjectAltName fields to map directly to +a name via a 1:1 transformation."; + reference + "RFC 6353: Transport Layer Security (TLS) Transport Model + for the Simple Network Management Protocol (SNMP). + SNMP-TLS-TM-MIB.snmpTlstmCertToTSNEntry"; + + leaf id { + type uint32; + description + "The id specifies the order in which the entries in the +cert-to-name list are searched. Entries with lower +numbers are searched first."; + reference + "RFC 6353: Transport Layer Security (TLS) Transport Model + for the Simple Network Management Protocol + (SNMP). + SNMP-TLS-TM-MIB.snmpTlstmCertToTSNID"; + + } + + leaf fingerprint { + type tls-fingerprint; + mandatory true; + description + "Specifies a value with which the fingerprint of the +full certificate presented by the peer is compared. If +the fingerprint of the full certificate presented by the +peer does not match the fingerprint configured, then the +entry is skipped, and the search for a match continues."; + reference + "RFC 6353: Transport Layer Security (TLS) Transport Model + for the Simple Network Management Protocol + (SNMP). + SNMP-TLS-TM-MIB.snmpTlstmCertToTSNFingerprint"; + + } + + leaf map-type { + type identityref { + base cert-to-name; + } + mandatory true; + description + "Specifies the algorithm used to map the certificate +presented by the peer to a name. + +Mappings that need additional configuration objects should +use the 'when' statement to make them conditional based on +the map-type."; + reference + "RFC 6353: Transport Layer Security (TLS) Transport Model + for the Simple Network Management Protocol + (SNMP). + SNMP-TLS-TM-MIB.snmpTlstmCertToTSNMapType"; + + } + + leaf name { + when + "../map-type = 'x509c2n:specified'"; + type string; + mandatory true; + description + "Directly specifies the NETCONF username when the +map-type is 'specified'."; + reference + "RFC 6353: Transport Layer Security (TLS) Transport Model + for the Simple Network Management Protocol + (SNMP). + SNMP-TLS-TM-MIB.snmpTlstmCertToTSNData"; + + } + } // list cert-to-name + } // grouping cert-to-name + } // module ietf-x509-cert-to-name \ No newline at end of file diff --git a/modules/libnetconf2-netconf-server@2023-09-07.yang b/modules/libnetconf2-netconf-server@2023-09-07.yang new file mode 100644 index 00000000..0ef941f0 --- /dev/null +++ b/modules/libnetconf2-netconf-server@2023-09-07.yang @@ -0,0 +1,423 @@ +module libnetconf2-netconf-server { + yang-version 1.1; + namespace "urn:cesnet:libnetconf2-netconf-server"; + prefix np2; + + import ietf-netconf-server { + prefix ncs; + } + + import ietf-crypto-types { + prefix ct; + } + + import iana-ssh-public-key-algs { + prefix sshpka; + } + + import iana-ssh-key-exchange-algs { + prefix sshkea; + } + + import iana-ssh-encryption-algs { + prefix sshea; + } + + import iana-ssh-mac-algs { + prefix sshma; + } + + import ietf-tls-server { + prefix tlss; + } + + revision "2023-09-07" { + description "Initial revision."; + } + + /* + identity ed25519-private-key-format { + base ct:private-key-format; + description + "This identity would indicate that the + private key is encoded in a ED25519PrivateKey + format. However no such format is currently + standardized or even exists. + + If you wish to use a private key that uses + an ED25519 algorithm, you need to pick either + the private-key-info-format or + openssh-private-key-format identity."; + } +*/ + + identity private-key-info-format { + base ct:private-key-format; + description + "Indicates that the private key is encoded + as a PrivateKeyInfo structure (from RFC 5208). + + The expected header of the private key: + -----BEGIN PRIVATE KEY----- + The expected footer of the private key: + -----END PRIVATE KEY----- + + Supported private key algorithms to use with + this format are: RSA, EC and ED25519. + + Commonly used public key format for this + type of private key is represented by the + SubjectPublicKeyInfo identity."; + + reference + "RFC 5208: PKCS #8: Private-Key Information + Syntax Specification Version 1.2"; + } + + identity openssh-private-key-format { + base ct:private-key-format; + description + "Indicates that the private key is encoded + in the OpenSSH format. + + The expected header of the private key: + -----BEGIN OPENSSH PRIVATE KEY----- + The expected footer of the private key: + -----END OPENSSH PRIVATE KEY----- + + Supported private key algorithms to use with + this format are: RSA, EC and ED25519. + + Commonly used public key format for this + type of private key is either the + SSH2 public key format (from RFC 4716) + or the Public key format defined in RFC 4253, + Section 6.6."; + + reference + "The OpenSSH Private Key Format: + https://github.com/openssh/openssh-portable/blob/master/PROTOCOL.key + + RFC 4716: + The Secure Shell (SSH) Public Key File Format + + RFC 4253: + The Secure Shell (SSH) Transport Layer Protocol"; + } + + identity openssh-ssh-ed25519-cert-v01 { + base sshpka:public-key-alg-base; + description + "SSH-ED25519-CERT-V01@OPENSSH.COM"; + reference + "OpenSSH PROTOCOL.certkeys: + https://cvsweb.openbsd.org/src/usr.bin/ssh/PROTOCOL.certkeys?annotate=HEAD"; + } + + identity openssh-ecdsa-sha2-nistp521-cert-v01 { + base sshpka:public-key-alg-base; + description + "ECDSA-SHA2-NISTP521-CERT-V01@OPENSSH.COM"; + reference + "OpenSSH PROTOCOL.certkeys: + https://cvsweb.openbsd.org/src/usr.bin/ssh/PROTOCOL.certkeys?annotate=HEAD"; + } + + identity openssh-ecdsa-sha2-nistp384-cert-v01 { + base sshpka:public-key-alg-base; + description + "ECDSA-SHA2-NISTP384-CERT-V01@OPENSSH.COM"; + reference + "OpenSSH PROTOCOL.certkeys: + https://cvsweb.openbsd.org/src/usr.bin/ssh/PROTOCOL.certkeys?annotate=HEAD"; + } + + identity openssh-ecdsa-sha2-nistp256-cert-v01 { + base sshpka:public-key-alg-base; + description + "ECDSA-SHA2-NISTP256-CERT-V01@OPENSSH.COM"; + reference + "OpenSSH PROTOCOL.certkeys: + https://cvsweb.openbsd.org/src/usr.bin/ssh/PROTOCOL.certkeys?annotate=HEAD"; + } + + identity openssh-rsa-sha2-512-cert-v01 { + base sshpka:public-key-alg-base; + description + "RSA-SHA2-512-CERT-V01@OPENSSH.COM"; + reference + "OpenSSH PROTOCOL.certkeys: + https://cvsweb.openbsd.org/src/usr.bin/ssh/PROTOCOL.certkeys?annotate=HEAD"; + } + + identity openssh-rsa-sha2-256-cert-v01 { + base sshpka:public-key-alg-base; + description + "RSA-SHA2-256-CERT-V01@OPENSSH.COM"; + reference + "OpenSSH PROTOCOL.certkeys: + https://cvsweb.openbsd.org/src/usr.bin/ssh/PROTOCOL.certkeys?annotate=HEAD"; + } + + identity openssh-ssh-rsa-cert-v01 { + base sshpka:public-key-alg-base; + description + "SSH-RSA-CERT-V01@OPENSSH.COM"; + reference + "OpenSSH PROTOCOL.certkeys: + https://cvsweb.openbsd.org/src/usr.bin/ssh/PROTOCOL.certkeys?annotate=HEAD"; + } + + identity openssh-ssh-dss-cert-v01 { + base sshpka:public-key-alg-base; + description + "SSH-DSS-CERT-V01@OPENSSH.COM"; + reference + "OpenSSH PROTOCOL.certkeys: + https://cvsweb.openbsd.org/src/usr.bin/ssh/PROTOCOL.certkeys?annotate=HEAD"; + } + + identity libssh-curve25519-sha256 { + base sshkea:key-exchange-alg-base; + description + "CURVE25519-SHA256@LIBSSH.ORG"; + reference + "curve25519-sha256@libssh.org specification: + https://git.libssh.org/projects/libssh.git/tree/doc/curve25519-sha256@libssh.org.txt"; + } + + identity openssh-chacha20-poly1305 { + base sshea:encryption-alg-base; + description + "CHACHA20-POLY1305@OPENSSH.COM"; + reference + "OpenSSH PROTOCOL.chacha20poly1305: + https://cvsweb.openbsd.org/src/usr.bin/ssh/PROTOCOL.chacha20poly1305?annotate=HEAD"; + } + + identity openssh-aes256-gcm { + base sshea:encryption-alg-base; + description + "AES256-GCM@OPENSSH.COM"; + reference + "OpenSSH PROTOCOL, Section 1.6: + https://cvsweb.openbsd.org/src/usr.bin/ssh/PROTOCOL?annotate=HEAD"; + } + + identity openssh-aes128-gcm { + base sshea:encryption-alg-base; + description + "AES128-GCM@OPENSSH.COM"; + reference + "OpenSSH PROTOCOL, Section 1.6: + https://cvsweb.openbsd.org/src/usr.bin/ssh/PROTOCOL?annotate=HEAD"; + } + + identity openssh-hmac-sha2-256-etm { + base sshma:mac-alg-base; + description + "HMAC-SHA2-256-ETM@OPENSSH.COM"; + reference + "OpenSSH PROTOCOL: + https://cvsweb.openbsd.org/src/usr.bin/ssh/PROTOCOL?annotate=HEAD"; + } + + identity openssh-hmac-sha2-512-etm { + base sshma:mac-alg-base; + description + "HMAC-SHA2-512-ETM@OPENSSH.COM"; + reference + "OpenSSH PROTOCOL: + https://cvsweb.openbsd.org/src/usr.bin/ssh/PROTOCOL?annotate=HEAD"; + } + + identity openssh-hmac-sha1-etm { + base sshma:mac-alg-base; + description + "HMAC-SHA1-ETM@OPENSSH.COM"; + reference + "OpenSSH PROTOCOL: + https://cvsweb.openbsd.org/src/usr.bin/ssh/PROTOCOL?annotate=HEAD"; + } + + grouping ssh-authentication-params-grouping { + description + "Grouping for SSH authentication parameters."; + + leaf auth-attempts { + type uint16; + default 3; + description + "Represents the number of failed attempts before an authentication is deemed unsuccessful."; + } + + leaf auth-timeout { + type uint16; + default 30; + units "seconds"; + description + "Represents the maximum amount of seconds an authentication can go on for."; + } + } + + grouping keyboard-interactive-grouping { + description + "Grouping for the SSH Keyboard interactive authentication method."; + + container keyboard-interactive { + presence "Indicates that PAM configuration file name has been configured. + This statement is present so the mandatory descendant + nodes do not imply that this node must be + configured."; + description + "Keyboard interactive SSH authentication method."; + leaf pam-config-file-name { + type string; + mandatory true; + } + leaf pam-config-file-dir { + type string; + } + } + } + + grouping endpoint-reference-grouping { + description + "Reference to another endpoint. The purpose is to use the referenced endpoint's authentication mechanisms. + If a connection occurs on an endpoint, the connecting user will be tried to be authenticated + using the given endpoint's defined methods. If the user wasn't authenticated and the endpoint + references another endpoint, the authentication will be tried again. However, this time + using the referenced endpoint's mechanisms. The references can be + multiple, however there must not be a cycle."; + + leaf endpoint-reference { + type leafref { + path "/ncs:netconf-server/ncs:listen/ncs:endpoint/ncs:name"; + } + } + } + + grouping certificate-revocation-list-grouping { + description + "A grouping for the Certificate Revocation List, which is used + to authenticate clients or to deny access for certain certificates. + The given Certificate Revocation List must be PEM or DER encoded."; + + reference + "RFC 5280: + Internet X.509 Public Key Infrastructure Certificate + and Certificate Revocation List (CRL) Profile"; + + choice certificate-revocation-list { + leaf crl-url { + type string; + description + "An URL from which the Certificate Revocation List will be + downloaded and used. The HTTP protocol works, but other + protocols, such as FTP, may work as well."; + } + + leaf crl-path { + type string; + description + "A path to a Certificate Revocation List file."; + } + + leaf crl-cert-ext { + type empty; + description + "Indicates that the Certificate Revocation List + Distribution Points extension will be used to fetch + Certificate Revocation Lists from. This will be done + for all the configured Certificate Authority certificates."; + + reference + "RFC 5280: + Internet X.509 Public Key Infrastructure Certificate + and Certificate Revocation List (CRL) Profile, Section 4.2.1.13"; + } + } + } + + augment "/ncs:netconf-server" { + leaf hello-timeout { + type uint16; + default 60; + description + "Represents the maximum number of seconds the server will wait for receiving a hello message."; + } + } + + augment "/ncs:netconf-server" { + leaf idle-timeout { + type uint16; + default 0; + description + "Represents the maximum number of seconds a NETCONF session may remain idle. The value of 0 represents indefinitely."; + } + } + + augment "/ncs:netconf-server/ncs:listen/ncs:endpoint/ncs:transport/ncs:ssh/ncs:ssh/ncs:ssh-server-parameters/ncs:client-authentication" { + uses ssh-authentication-params-grouping; + } + + augment "/ncs:netconf-server/ncs:call-home/ncs:netconf-client/ncs:endpoints/ncs:endpoint/ncs:transport/ncs:ssh/ncs:ssh/ncs:ssh-server-parameters/ncs:client-authentication" { + uses ssh-authentication-params-grouping; + } + + augment "/ncs:netconf-server/ncs:listen/ncs:endpoint/ncs:transport/ncs:ssh/ncs:ssh/ncs:ssh-server-parameters/ncs:client-authentication/ncs:users/ncs:user" { + uses keyboard-interactive-grouping; + } + + augment "/ncs:netconf-server/ncs:call-home/ncs:netconf-client/ncs:endpoints/ncs:endpoint/ncs:transport/ncs:ssh/ncs:ssh/ncs:ssh-server-parameters/ncs:client-authentication/ncs:users/ncs:user" { + uses keyboard-interactive-grouping; + } + + augment "/ncs:netconf-server/ncs:listen/ncs:endpoint/ncs:transport" { + case unix-socket { + container unix-socket { + description + "Defines a new transport called UNIX socket."; + leaf path { + type string; + mandatory true; + } + leaf mode { + type string { + pattern '[0124567]{3}'; + } + } + leaf uid { + type uint16; + } + leaf gid { + type uint16; + } + } + } + } + + augment "/ncs:netconf-server/ncs:listen/ncs:endpoint/ncs:transport/ncs:ssh/ncs:ssh/ncs:ssh-server-parameters/ncs:client-authentication" { + uses endpoint-reference-grouping; + } + + augment "/ncs:netconf-server/ncs:call-home/ncs:netconf-client/ncs:endpoints/ncs:endpoint/ncs:transport/ncs:ssh/ncs:ssh/ncs:ssh-server-parameters/ncs:client-authentication" { + uses endpoint-reference-grouping; + } + + augment "/ncs:netconf-server/ncs:listen/ncs:endpoint/ncs:transport/ncs:tls/ncs:tls/ncs:tls-server-parameters/ncs:client-authentication" { + uses endpoint-reference-grouping; + } + + augment "/ncs:netconf-server/ncs:call-home/ncs:netconf-client/ncs:endpoints/ncs:endpoint/ncs:transport/ncs:tls/ncs:tls/ncs:tls-server-parameters/ncs:client-authentication" { + uses endpoint-reference-grouping; + } + + augment "/ncs:netconf-server/ncs:listen/ncs:endpoint/ncs:transport/ncs:tls/ncs:tls/ncs:tls-server-parameters/ncs:client-authentication" { + uses certificate-revocation-list-grouping; + } + + augment "/ncs:netconf-server/ncs:call-home/ncs:netconf-client/ncs:endpoints/ncs:endpoint/ncs:transport/ncs:tls/ncs:tls/ncs:tls-server-parameters/ncs:client-authentication" { + uses certificate-revocation-list-grouping; + } +} diff --git a/nc_client.h.in b/nc_client.h.in index 86664842..471b5995 100644 --- a/nc_client.h.in +++ b/nc_client.h.in @@ -16,13 +16,12 @@ #ifndef NC_CLIENT_H_ #define NC_CLIENT_H_ -@SSH_MACRO@ -@TLS_MACRO@ - #ifdef __cplusplus extern "C" { #endif +@SSH_TLS_MACRO@ + #include #include #include diff --git a/nc_server.h.in b/nc_server.h.in index c1f4bdc7..a4ad8ca6 100644 --- a/nc_server.h.in +++ b/nc_server.h.in @@ -16,16 +16,16 @@ #ifndef NC_SERVER_H_ #define NC_SERVER_H_ -@SSH_MACRO@ -@TLS_MACRO@ - #ifdef __cplusplus extern "C" { #endif +@SSH_TLS_MACRO@ + #include #include #include +#include #include #include diff --git a/src/config.h.in b/src/config.h.in index 736eaee7..6dc9fa16 100644 --- a/src/config.h.in +++ b/src/config.h.in @@ -49,6 +49,11 @@ */ #cmakedefine LIBPAM_HAVE_CONFDIR +/* + * Location of installed YANG modules on the system + */ +#define NC_SERVER_SEARCH_DIR "@YANG_MODULE_DIR@" + /* * Location of installed YANG modules on the system */ @@ -75,11 +80,6 @@ */ #define NC_TIMEOUT_STEP @TIMEOUT_STEP@ -/* - * Time waited between Call Home endpoint session creation attempts (s). - */ -#define NC_CH_ENDPT_BACKOFF_WAIT @CALL_HOME_BACKOFF_WAIT@ - /* Portability feature-check macros. */ #cmakedefine HAVE_PTHREAD_RWLOCKATTR_SETKIND_NP diff --git a/src/io.c b/src/io.c index b2cf495f..d51278f0 100644 --- a/src/io.c +++ b/src/io.c @@ -22,20 +22,27 @@ #include #include #include +#include #include #include #include #include #include -#ifdef NC_ENABLED_TLS +#ifdef NC_ENABLED_SSH_TLS # include -#endif +# include +#endif /* NC_ENABLED_SSH_TLS */ #include #include "compat.h" -#include "libnetconf.h" +#include "config.h" +#include "log_p.h" +#include "messages_p.h" +#include "netconf.h" +#include "session.h" +#include "session_p.h" const char *nc_msgtype2str[] = { "error", @@ -51,7 +58,7 @@ const char *nc_msgtype2str[] = { #define BUFFERSIZE 512 -#ifdef NC_ENABLED_TLS +#ifdef NC_ENABLED_SSH_TLS static char * nc_ssl_error_get_reasons(void) @@ -67,25 +74,19 @@ nc_ssl_error_get_reasons(void) /* add "; " */ reason_size += 2; reasons = nc_realloc(reasons, reason_size); - if (!reasons) { - ERRMEM; - return NULL; - } + 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); - if (!reasons) { - ERRMEM; - return NULL; - } + NC_CHECK_ERRMEM_RET(!reasons, NULL); reason_len += sprintf(reasons + reason_len, "%s", ERR_reason_error_string(e)); } return reasons; } -#endif +#endif /* NC_ENABLED_SSH_TLS */ static ssize_t nc_read(struct nc_session *session, char *buf, size_t count, uint32_t inact_timeout, struct timespec *ts_act_timeout) @@ -140,7 +141,7 @@ nc_read(struct nc_session *session, char *buf, size_t count, uint32_t inact_time } break; -#ifdef NC_ENABLED_SSH +#ifdef NC_ENABLED_SSH_TLS case NC_TI_LIBSSH: /* read via libssh */ r = ssh_channel_read(session->ti.libssh.channel, buf + readd, count - readd, 0); @@ -162,9 +163,7 @@ nc_read(struct nc_session *session, char *buf, size_t count, uint32_t inact_time break; } break; -#endif -#ifdef NC_ENABLED_TLS case NC_TI_OPENSSL: /* read via OpenSSL */ ERR_clear_error(); @@ -203,7 +202,7 @@ nc_read(struct nc_session *session, char *buf, size_t count, uint32_t inact_time } } break; -#endif +#endif /* NC_ENABLED_SSH_TLS */ } if (r == 0) { @@ -248,10 +247,7 @@ nc_read_chunk(struct nc_session *session, size_t len, uint32_t inact_timeout, st } *chunk = malloc((len + 1) * sizeof **chunk); - if (!*chunk) { - ERRMEM; - return -1; - } + NC_CHECK_ERRMEM_RET(!*chunk, -1); r = nc_read(session, *chunk, len, inact_timeout, ts_act_timeout); if (r <= 0) { @@ -281,10 +277,7 @@ nc_read_until(struct nc_session *session, const char *endtag, size_t limit, uint size = BUFFERSIZE; } chunk = malloc((size + 1) * sizeof *chunk); - if (!chunk) { - ERRMEM; - return -1; - } + NC_CHECK_ERRMEM_RET(!chunk, -1); len = strlen(endtag); while (1) { @@ -300,10 +293,7 @@ nc_read_until(struct nc_session *session, const char *endtag, size_t limit, uint /* get more memory */ size = size + BUFFERSIZE; chunk = nc_realloc(chunk, (size + 1) * sizeof *chunk); - if (!chunk) { - ERRMEM; - return -1; - } + NC_CHECK_ERRMEM_RET(!chunk, -1); } /* get another character */ @@ -426,11 +416,7 @@ nc_read_msg_io(struct nc_session *session, int io_timeout, struct ly_in **msg, i /* realloc message buffer, remember to count terminating null byte */ data = nc_realloc(data, len + chunk_len + 1); - if (!data) { - ERRMEM; - ret = -1; - goto cleanup; - } + NC_CHECK_ERRMEM_GOTO(!data, ret = -1, cleanup); memcpy(data + len, chunk, chunk_len); len += chunk_len; data[len] = '\0'; @@ -477,7 +463,7 @@ nc_read_poll(struct nc_session *session, int io_timeout) } switch (session->ti_type) { -#ifdef NC_ENABLED_SSH +#ifdef NC_ENABLED_SSH_TLS case NC_TI_LIBSSH: /* EINTR is handled, it resumes waiting */ ret = ssh_channel_poll_timeout(session->ti.libssh.channel, io_timeout, 0); @@ -499,8 +485,6 @@ nc_read_poll(struct nc_session *session, int io_timeout) fds.revents = 0; } break; -#endif -#ifdef NC_ENABLED_TLS case NC_TI_OPENSSL: ret = SSL_pending(session->ti.tls); if (ret) { @@ -511,7 +495,7 @@ nc_read_poll(struct nc_session *session, int io_timeout) } fds.fd = SSL_get_fd(session->ti.tls); -#endif +#endif /* NC_ENABLED_SSH_TLS */ /* fallthrough */ case NC_TI_FD: case NC_TI_UNIX: @@ -610,15 +594,13 @@ nc_session_is_connected(struct nc_session *session) case NC_TI_UNIX: fds.fd = session->ti.unixsock.sock; break; -#ifdef NC_ENABLED_SSH +#ifdef NC_ENABLED_SSH_TLS case NC_TI_LIBSSH: return ssh_is_connected(session->ti.libssh.session); -#endif -#ifdef NC_ENABLED_TLS case NC_TI_OPENSSL: fds.fd = SSL_get_fd(session->ti.tls); break; -#endif +#endif /* NC_ENABLED_SSH_TLS */ default: return 0; } @@ -656,9 +638,9 @@ nc_write(struct nc_session *session, const void *buf, size_t count) int c, fd, interrupted; size_t written = 0; -#ifdef NC_ENABLED_TLS +#ifdef NC_ENABLED_SSH_TLS unsigned long e; -#endif +#endif /* NC_ENABLED_SSH_TLS */ if ((session->status != NC_STATUS_RUNNING) && (session->status != NC_STATUS_STARTING)) { return -1; @@ -692,7 +674,7 @@ nc_write(struct nc_session *session, const void *buf, size_t count) } break; -#ifdef NC_ENABLED_SSH +#ifdef NC_ENABLED_SSH_TLS case NC_TI_LIBSSH: 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)) { @@ -710,8 +692,6 @@ nc_write(struct nc_session *session, const void *buf, size_t count) return -1; } break; -#endif -#ifdef NC_ENABLED_TLS case NC_TI_OPENSSL: c = SSL_write(session->ti.tls, (char *)(buf + written), count - written); if (c < 1) { @@ -739,7 +719,7 @@ nc_write(struct nc_session *session, const void *buf, size_t count) } } break; -#endif +#endif /* NC_ENABLED_SSH_TLS */ default: ERRINT; return -1; @@ -952,11 +932,7 @@ nc_write_msg_io(struct nc_session *session, int io_timeout, int type, ...) /* open */ count = asprintf(&buf, "", NC_NS_BASE, session->opts.client.msgid + 1, attrs ? attrs : ""); - if (count == -1) { - ERRMEM; - ret = NC_MSG_ERROR; - goto cleanup; - } + NC_CHECK_ERRMEM_GOTO(count == -1, ret = NC_MSG_ERROR, cleanup); nc_write_clb((void *)&arg, buf, count, 0); free(buf); @@ -1115,11 +1091,7 @@ nc_write_msg_io(struct nc_session *session, int io_timeout, int type, ...) sid = va_arg(ap, uint32_t *); count = asprintf(&buf, "", NC_NS_BASE); - if (count == -1) { - ERRMEM; - ret = NC_MSG_ERROR; - goto cleanup; - } + NC_CHECK_ERRMEM_GOTO(count == -1, ret = NC_MSG_ERROR, cleanup); nc_write_clb((void *)&arg, buf, count, 0); free(buf); for (i = 0; capabilities[i]; i++) { @@ -1129,11 +1101,7 @@ nc_write_msg_io(struct nc_session *session, int io_timeout, int type, ...) } if (sid) { count = asprintf(&buf, "%u", *sid); - if (count == -1) { - ERRMEM; - ret = NC_MSG_ERROR; - goto cleanup; - } + NC_CHECK_ERRMEM_GOTO(count == -1, ret = NC_MSG_ERROR, cleanup); nc_write_clb((void *)&arg, buf, count, 0); free(buf); } else { @@ -1177,7 +1145,7 @@ nc_realloc(void *ptr, size_t size) } struct passwd * -nc_getpwuid(uid_t uid, struct passwd *pwd_buf, char **buf, size_t *buf_size) +nc_getpw(uid_t uid, const char *username, struct passwd *pwd_buf, char **buf, size_t *buf_size) { struct passwd *pwd = NULL; long sys_size; @@ -1195,16 +1163,21 @@ nc_getpwuid(uid_t uid, struct passwd *pwd_buf, char **buf, size_t *buf_size) /* allocate some buffer */ *buf = nc_realloc(*buf, *buf_size); - if (!*buf) { - ERRMEM; - return NULL; - } + NC_CHECK_ERRMEM_RET(!*buf, NULL); - ret = getpwuid_r(uid, pwd_buf, *buf, *buf_size, &pwd); + if (username) { + ret = getpwnam_r(username, pwd_buf, *buf, *buf_size, &pwd); + } else { + ret = getpwuid_r(uid, pwd_buf, *buf, *buf_size, &pwd); + } } while (ret && (ret == ERANGE)); if (ret) { - ERR(NULL, "Retrieving UID \"%lu\" passwd entry failed (%s).", (unsigned long)uid, strerror(ret)); + if (username) { + ERR(NULL, "Retrieving username \"%s\" passwd entry failed (%s).", username, strerror(ret)); + } else { + ERR(NULL, "Retrieving UID \"%lu\" passwd entry failed (%s).", (unsigned long)uid, strerror(ret)); + } } return pwd; } diff --git a/src/log.c b/src/log.c index c87b4fb4..84634821 100644 --- a/src/log.c +++ b/src/log.c @@ -17,16 +17,18 @@ #include #include +#include #include -#ifdef NC_ENABLED_SSH +#ifdef NC_ENABLED_SSH_TLS #include -#endif +#endif /* NC_ENABLED_SSH_TLS */ #include "compat.h" -#include "libnetconf.h" +#include "config.h" #include "log.h" +#include "session_p.h" /** * @brief libnetconf verbose level variable @@ -54,7 +56,7 @@ struct { {NC_VERB_DEBUG_LOWLVL, "[DBL]"} }; -#ifdef NC_ENABLED_SSH +#ifdef NC_ENABLED_SSH_TLS API void nc_libssh_thread_verbosity(int level) @@ -62,7 +64,7 @@ nc_libssh_thread_verbosity(int level) ssh_set_log_level(level); } -#endif +#endif /* NC_ENABLED_SSH_TLS */ static void prv_vprintf(const struct nc_session *session, NC_VERB_LEVEL level, const char *format, va_list args) @@ -134,14 +136,6 @@ nc_ly_log_clb(LY_LOG_LEVEL lvl, const char *msg, const char *UNUSED(path)) } } -API void -nc_set_print_clb(void (*clb)(NC_VERB_LEVEL, const char *)) -{ - print_clb = NULL; - depr_print_clb = clb; - ly_set_log_clb(nc_ly_log_clb, 1); -} - API void nc_set_print_clb_session(void (*clb)(const struct nc_session *, NC_VERB_LEVEL, const char *)) { diff --git a/src/log.h b/src/log.h index 1e4978e3..8564db13 100644 --- a/src/log.h +++ b/src/log.h @@ -50,7 +50,7 @@ typedef enum NC_VERB_LEVEL { */ void nc_verbosity(NC_VERB_LEVEL level); -#ifdef NC_ENABLED_SSH +#ifdef NC_ENABLED_SSH_TLS /** * @brief Set libssh verbosity level. @@ -69,14 +69,7 @@ void nc_verbosity(NC_VERB_LEVEL level); */ void nc_libssh_thread_verbosity(int level); -#endif - -/** - * @brief Deprecated, use ::nc_set_print_clb_session() instead. - * - * @param[in] clb Callback that is called for every message. - */ -void nc_set_print_clb(void (*clb)(NC_VERB_LEVEL, const char *)); +#endif /* NC_ENABLED_SSH_TLS */ /** * @brief Set libnetconf print callback. diff --git a/src/log_p.h b/src/log_p.h index 5f772d49..c4f6c9e3 100644 --- a/src/log_p.h +++ b/src/log_p.h @@ -16,7 +16,7 @@ #ifndef NC_LOG_PRIVATE_H_ #define NC_LOG_PRIVATE_H_ -#include +#include #include "compat.h" #include "log.h" @@ -42,15 +42,56 @@ extern ATOMIC_T verbose_level; /* * Verbose printing macros */ -#define ERR(session, format, args ...) prv_printf(session,NC_VERB_ERROR,format,##args) -#define WRN(session, format, args ...) if(ATOMIC_LOAD_RELAXED(verbose_level)>=NC_VERB_WARNING){prv_printf(session,NC_VERB_WARNING,format,##args);} -#define VRB(session, format, args ...) if(ATOMIC_LOAD_RELAXED(verbose_level)>=NC_VERB_VERBOSE){prv_printf(session,NC_VERB_VERBOSE,format,##args);} -#define DBG(session, format, args ...) if(ATOMIC_LOAD_RELAXED(verbose_level)>=NC_VERB_DEBUG){prv_printf(session,NC_VERB_DEBUG,format,##args);} -#define DBL(session, format, args ...) if(ATOMIC_LOAD_RELAXED(verbose_level)>=NC_VERB_DEBUG_LOWLVL){prv_printf(session,NC_VERB_DEBUG_LOWLVL,format,##args);} +#define ERR(session, ...) prv_printf(session, NC_VERB_ERROR, __VA_ARGS__) +#define WRN(session, ...) if(ATOMIC_LOAD_RELAXED(verbose_level)>=NC_VERB_WARNING){prv_printf(session, NC_VERB_WARNING, __VA_ARGS__);} +#define VRB(session, ...) if(ATOMIC_LOAD_RELAXED(verbose_level)>=NC_VERB_VERBOSE){prv_printf(session, NC_VERB_VERBOSE, __VA_ARGS__);} +#define DBG(session, ...) if(ATOMIC_LOAD_RELAXED(verbose_level)>=NC_VERB_DEBUG){prv_printf(session, NC_VERB_DEBUG, __VA_ARGS__);} +#define DBL(session, ...) if(ATOMIC_LOAD_RELAXED(verbose_level)>=NC_VERB_DEBUG_LOWLVL){prv_printf(session, NC_VERB_DEBUG_LOWLVL, __VA_ARGS__);} #define ERRMEM ERR(NULL, "%s: memory reallocation failed (%s:%d).", __func__, __FILE__, __LINE__) -#define ERRARG(arg) ERR(NULL, "%s: invalid argument (%s).", __func__, arg) #define ERRINIT ERR(NULL, "%s: libnetconf2 not initialized.", __func__) #define ERRINT ERR(NULL, "%s: internal error (%s:%d).", __func__, __FILE__, __LINE__) +#define ERRARG(session, ARG) ERR(session, "Invalid argument %s (%s()).", #ARG, __func__) + +#define NC_CHECK_ERRMEM_RET(COND, RET) if ((COND)) {ERRMEM; return (RET);} +#define NC_CHECK_ERRMEM_GOTO(COND, RET, GOTO) if ((COND)) {ERRMEM; RET; goto GOTO;} + +#define GETMACRO1(_1, NAME, ...) NAME +#define GETMACRO2(_1, _2, NAME, ...) NAME +#define GETMACRO3(_1, _2, _3, NAME, ...) NAME +#define GETMACRO4(_1, _2, _3, _4, NAME, ...) NAME +#define GETMACRO5(_1, _2, _3, _4, _5, NAME, ...) NAME +#define GETMACRO6(_1, _2, _3, _4, _5, _6, NAME, ...) NAME +#define GETMACRO7(_1, _2, _3, _4, _5, _6, _7, NAME, ...) NAME +#define GETMACRO8(_1, _2, _3, _4, _5, _6, _7, _8, NAME, ...) NAME + +#define NC_CHECK_ARG_RET1(session, ARG, RETVAL) if (!(ARG)) {ERRARG(session, ARG);return RETVAL;} +#define NC_CHECK_ARG_RET2(session, ARG1, ARG2, RETVAL)\ + NC_CHECK_ARG_RET1(session, ARG1, RETVAL);\ + NC_CHECK_ARG_RET1(session, ARG2, RETVAL) +#define NC_CHECK_ARG_RET3(session, ARG1, ARG2, ARG3, RETVAL)\ + NC_CHECK_ARG_RET2(session, ARG1, ARG2, RETVAL);\ + NC_CHECK_ARG_RET1(session, ARG3, RETVAL) +#define NC_CHECK_ARG_RET4(session, ARG1, ARG2, ARG3, ARG4, RETVAL)\ + NC_CHECK_ARG_RET3(session, ARG1, ARG2, ARG3, RETVAL);\ + NC_CHECK_ARG_RET1(session, ARG4, RETVAL) +#define NC_CHECK_ARG_RET5(session, ARG1, ARG2, ARG3, ARG4, ARG5, RETVAL)\ + NC_CHECK_ARG_RET4(session, ARG1, ARG2, ARG3, ARG4, RETVAL);\ + NC_CHECK_ARG_RET1(session, ARG5, RETVAL) +#define NC_CHECK_ARG_RET6(session, ARG1, ARG2, ARG3, ARG4, ARG5, ARG6, RETVAL)\ + NC_CHECK_ARG_RET5(session, ARG1, ARG2, ARG3, ARG4, ARG5, RETVAL);\ + NC_CHECK_ARG_RET1(session, ARG6, RETVAL) +#define NC_CHECK_ARG_RET7(session, ARG1, ARG2, ARG3, ARG4, ARG5, ARG6, ARG7, RETVAL)\ + NC_CHECK_ARG_RET6(session, ARG1, ARG2, ARG3, ARG4, ARG5, ARG6, RETVAL);\ + NC_CHECK_ARG_RET1(session, ARG7, RETVAL) + +/** + * @brief Function's parameters checking macro + * + * @param session Session that is logged. + * @param ... Parameters of the function to check. The last parameter is the value that is returned on error. + */ +#define NC_CHECK_ARG_RET(session, ...) GETMACRO8(__VA_ARGS__, NC_CHECK_ARG_RET7, NC_CHECK_ARG_RET6, NC_CHECK_ARG_RET5,\ + NC_CHECK_ARG_RET4, NC_CHECK_ARG_RET3, NC_CHECK_ARG_RET2, NC_CHECK_ARG_RET1, DUMMY) (session, __VA_ARGS__) #endif /* NC_LOG_PRIVATE_H_ */ diff --git a/src/messages_client.c b/src/messages_client.c index 04c0b3c0..bc58b861 100644 --- a/src/messages_client.c +++ b/src/messages_client.c @@ -17,13 +17,17 @@ #include #include -#include #include #include #include -#include "libnetconf.h" +#include "compat.h" +#include "config.h" +#include "log_p.h" +#include "messages_client.h" +#include "messages_p.h" +#include "netconf.h" const char *rpcedit_dfltop2str[] = {NULL, "merge", "replace", "none"}; const char *rpcedit_testopt2str[] = {NULL, "test-then-set", "set", "test-only"}; @@ -32,10 +36,7 @@ const char *rpcedit_erropt2str[] = {NULL, "stop-on-error", "continue-on-error", API NC_RPC_TYPE nc_rpc_get_type(const struct nc_rpc *rpc) { - if (!rpc) { - ERRARG("rpc"); - return 0; - } + NC_CHECK_ARG_RET(NULL, rpc, 0); return rpc->type; } @@ -45,16 +46,14 @@ nc_rpc_act_generic(const struct lyd_node *data, NC_PARAMTYPE paramtype) { struct nc_rpc_act_generic *rpc; - if (!data || data->next || (data->prev != data)) { - ERRARG("data"); + NC_CHECK_ARG_RET(NULL, data, NULL); + if (data->next || (data->prev != data)) { + ERR(NULL, "nc_rpc_act_generic missing data"); return NULL; } rpc = malloc(sizeof *rpc); - if (!rpc) { - ERRMEM; - return NULL; - } + NC_CHECK_ERRMEM_RET(!rpc, NULL); rpc->type = NC_RPC_ACT_GENERIC; rpc->has_data = 1; @@ -76,16 +75,10 @@ nc_rpc_act_generic_xml(const char *xml_str, NC_PARAMTYPE paramtype) { struct nc_rpc_act_generic *rpc; - if (!xml_str) { - ERRARG("xml_str"); - return NULL; - } + NC_CHECK_ARG_RET(NULL, xml_str, NULL); rpc = malloc(sizeof *rpc); - if (!rpc) { - ERRMEM; - return NULL; - } + NC_CHECK_ERRMEM_RET(!rpc, NULL); rpc->type = NC_RPC_ACT_GENERIC; rpc->has_data = 0; @@ -104,10 +97,7 @@ nc_rpc_getconfig(NC_DATASTORE source, const char *filter, NC_WD_MODE wd_mode, NC { struct nc_rpc_getconfig *rpc; - if (!source) { - ERRARG("source"); - return NULL; - } + NC_CHECK_ARG_RET(NULL, source, NULL); if (filter && filter[0] && (filter[0] != '<') && (filter[0] != '/') && !isalpha(filter[0])) { ERR(NULL, "Filter is neither an XML subtree nor an XPath expression (invalid first char '%c').", filter[0]); @@ -115,10 +105,7 @@ nc_rpc_getconfig(NC_DATASTORE source, const char *filter, NC_WD_MODE wd_mode, NC } rpc = malloc(sizeof *rpc); - if (!rpc) { - ERRMEM; - return NULL; - } + NC_CHECK_ERRMEM_RET(!rpc, NULL); rpc->type = NC_RPC_GETCONFIG; rpc->source = source; @@ -139,13 +126,7 @@ nc_rpc_edit(NC_DATASTORE target, NC_RPC_EDIT_DFLTOP default_op, NC_RPC_EDIT_TEST { struct nc_rpc_edit *rpc; - if (!target) { - ERRARG("target"); - return NULL; - } else if (!edit_content) { - ERRARG("edit_content"); - return NULL; - } + NC_CHECK_ARG_RET(NULL, target, edit_content, NULL); if (edit_content[0] && (edit_content[0] != '<') && !isalpha(edit_content[0])) { ERR(NULL, " content is neither a URL nor an XML config (invalid first char '%c').", edit_content[0]); @@ -153,10 +134,7 @@ nc_rpc_edit(NC_DATASTORE target, NC_RPC_EDIT_DFLTOP default_op, NC_RPC_EDIT_TEST } rpc = malloc(sizeof *rpc); - if (!rpc) { - ERRMEM; - return NULL; - } + NC_CHECK_ERRMEM_RET(!rpc, NULL); rpc->type = NC_RPC_EDIT; rpc->target = target; @@ -179,13 +157,7 @@ nc_rpc_copy(NC_DATASTORE target, const char *url_trg, NC_DATASTORE source, const { struct nc_rpc_copy *rpc; - if (!target) { - ERRARG("target"); - return NULL; - } else if (!source) { - ERRARG("source"); - return NULL; - } + NC_CHECK_ARG_RET(NULL, target, source, NULL); if (url_or_config_src && url_or_config_src[0] && (url_or_config_src[0] != '<') && !isalpha(url_or_config_src[0])) { ERR(NULL, " source is neither a URL nor an XML config (invalid first char '%c').", url_or_config_src[0]); @@ -193,10 +165,7 @@ nc_rpc_copy(NC_DATASTORE target, const char *url_trg, NC_DATASTORE source, const } rpc = malloc(sizeof *rpc); - if (!rpc) { - ERRMEM; - return NULL; - } + NC_CHECK_ERRMEM_RET(!rpc, NULL); rpc->type = NC_RPC_COPY; rpc->target = target; @@ -222,16 +191,10 @@ nc_rpc_delete(NC_DATASTORE target, const char *url, NC_PARAMTYPE paramtype) { struct nc_rpc_delete *rpc; - if (!target) { - ERRARG("target"); - return NULL; - } + NC_CHECK_ARG_RET(NULL, target, NULL); rpc = malloc(sizeof *rpc); - if (!rpc) { - ERRMEM; - return NULL; - } + NC_CHECK_ERRMEM_RET(!rpc, NULL); rpc->type = NC_RPC_DELETE; rpc->target = target; @@ -250,16 +213,10 @@ nc_rpc_lock(NC_DATASTORE target) { struct nc_rpc_lock *rpc; - if (!target) { - ERRARG("target"); - return NULL; - } + NC_CHECK_ARG_RET(NULL, target, NULL); rpc = malloc(sizeof *rpc); - if (!rpc) { - ERRMEM; - return NULL; - } + NC_CHECK_ERRMEM_RET(!rpc, NULL); rpc->type = NC_RPC_LOCK; rpc->target = target; @@ -272,16 +229,10 @@ nc_rpc_unlock(NC_DATASTORE target) { struct nc_rpc_lock *rpc; - if (!target) { - ERRARG("target"); - return NULL; - } + NC_CHECK_ARG_RET(NULL, target, NULL); rpc = malloc(sizeof *rpc); - if (!rpc) { - ERRMEM; - return NULL; - } + NC_CHECK_ERRMEM_RET(!rpc, NULL); rpc->type = NC_RPC_UNLOCK; rpc->target = target; @@ -300,10 +251,7 @@ nc_rpc_get(const char *filter, NC_WD_MODE wd_mode, NC_PARAMTYPE paramtype) } rpc = malloc(sizeof *rpc); - if (!rpc) { - ERRMEM; - return NULL; - } + NC_CHECK_ERRMEM_RET(!rpc, NULL); rpc->type = NC_RPC_GET; if (filter && (paramtype == NC_PARAMTYPE_DUP_AND_FREE)) { @@ -322,16 +270,10 @@ nc_rpc_kill(uint32_t session_id) { struct nc_rpc_kill *rpc; - if (!session_id) { - ERRARG("session_id"); - return NULL; - } + NC_CHECK_ARG_RET(NULL, session_id, NULL); rpc = malloc(sizeof *rpc); - if (!rpc) { - ERRMEM; - return NULL; - } + NC_CHECK_ERRMEM_RET(!rpc, NULL); rpc->type = NC_RPC_KILL; rpc->sid = session_id; @@ -346,10 +288,7 @@ nc_rpc_commit(int confirmed, uint32_t confirm_timeout, const char *persist, cons struct nc_rpc_commit *rpc; rpc = malloc(sizeof *rpc); - if (!rpc) { - ERRMEM; - return NULL; - } + NC_CHECK_ERRMEM_RET(!rpc, NULL); rpc->type = NC_RPC_COMMIT; rpc->confirmed = confirmed; @@ -375,10 +314,7 @@ nc_rpc_discard(void) struct nc_rpc *rpc; rpc = malloc(sizeof *rpc); - if (!rpc) { - ERRMEM; - return NULL; - } + NC_CHECK_ERRMEM_RET(!rpc, NULL); rpc->type = NC_RPC_DISCARD; @@ -391,10 +327,7 @@ nc_rpc_cancel(const char *persist_id, NC_PARAMTYPE paramtype) struct nc_rpc_cancel *rpc; rpc = malloc(sizeof *rpc); - if (!rpc) { - ERRMEM; - return NULL; - } + NC_CHECK_ERRMEM_RET(!rpc, NULL); rpc->type = NC_RPC_CANCEL; if (persist_id && (paramtype == NC_PARAMTYPE_DUP_AND_FREE)) { @@ -412,10 +345,7 @@ nc_rpc_validate(NC_DATASTORE source, const char *url_or_config, NC_PARAMTYPE par { struct nc_rpc_validate *rpc; - if (!source) { - ERRARG("source"); - return NULL; - } + NC_CHECK_ARG_RET(NULL, source, NULL); if (url_or_config && url_or_config[0] && (url_or_config[0] != '<') && !isalpha(url_or_config[0])) { ERR(NULL, " source is neither a URL nor an XML config (invalid first char '%c').", url_or_config[0]); @@ -423,10 +353,7 @@ nc_rpc_validate(NC_DATASTORE source, const char *url_or_config, NC_PARAMTYPE par } rpc = malloc(sizeof *rpc); - if (!rpc) { - ERRMEM; - return NULL; - } + NC_CHECK_ERRMEM_RET(!rpc, NULL); rpc->type = NC_RPC_VALIDATE; rpc->source = source; @@ -445,16 +372,10 @@ nc_rpc_getschema(const char *identifier, const char *version, const char *format { struct nc_rpc_getschema *rpc; - if (!identifier) { - ERRARG("identifier"); - return NULL; - } + NC_CHECK_ARG_RET(NULL, identifier, NULL); rpc = malloc(sizeof *rpc); - if (!rpc) { - ERRMEM; - return NULL; - } + NC_CHECK_ERRMEM_RET(!rpc, NULL); rpc->type = NC_RPC_GETSCHEMA; if (paramtype == NC_PARAMTYPE_DUP_AND_FREE) { @@ -489,10 +410,7 @@ nc_rpc_subscribe(const char *stream_name, const char *filter, const char *start_ } rpc = malloc(sizeof *rpc); - if (!rpc) { - ERRMEM; - return NULL; - } + NC_CHECK_ERRMEM_RET(!rpc, NULL); rpc->type = NC_RPC_SUBSCRIBE; if (stream_name && (paramtype == NC_PARAMTYPE_DUP_AND_FREE)) { @@ -528,19 +446,15 @@ nc_rpc_getdata(const char *datastore, const char *filter, const char *config_fil struct nc_rpc_getdata *rpc = NULL; int i; + NC_CHECK_ARG_RET(NULL, datastore, NULL); + if (filter && filter[0] && (filter[0] != '<') && (filter[0] != '/') && !isalpha(filter[0])) { ERR(NULL, "Filter is neither an XML subtree nor an XPath expression (invalid first char '%c').", filter[0]); return NULL; - } else if (!datastore) { - ERRARG("datastore"); - return NULL; } rpc = calloc(1, sizeof *rpc); - if (!rpc) { - ERRMEM; - return NULL; - } + NC_CHECK_ERRMEM_RET(!rpc, NULL); rpc->free = (paramtype == NC_PARAMTYPE_CONST ? 0 : 1); rpc->type = NC_RPC_GETDATA; @@ -561,16 +475,10 @@ nc_rpc_getdata(const char *datastore, const char *filter, const char *config_fil } if (origin_filter && (paramtype == NC_PARAMTYPE_DUP_AND_FREE)) { rpc->origin_filter = malloc(origin_filter_count * sizeof *rpc->origin_filter); - if (!rpc->origin_filter) { - ERRMEM; - goto error; - } + NC_CHECK_ERRMEM_GOTO(!rpc->origin_filter, , error); for (i = 0; i < origin_filter_count; ++i) { rpc->origin_filter[i] = strdup(origin_filter[i]); - if (!rpc->origin_filter[i]) { - ERRMEM; - goto error; - } + NC_CHECK_ERRMEM_GOTO(!rpc->origin_filter[i], , error); ++rpc->origin_filter_count; } } else { @@ -594,13 +502,7 @@ nc_rpc_editdata(const char *datastore, NC_RPC_EDIT_DFLTOP default_op, const char { struct nc_rpc_editdata *rpc; - if (!datastore) { - ERRARG("datastore"); - return NULL; - } else if (!edit_content) { - ERRARG("edit_content"); - return NULL; - } + NC_CHECK_ARG_RET(NULL, datastore, edit_content, NULL); if (edit_content[0] && (edit_content[0] != '<') && !isalpha(edit_content[0])) { ERR(NULL, " content is neither a URL nor an XML config (invalid first char '%c').", edit_content[0]); @@ -608,10 +510,7 @@ nc_rpc_editdata(const char *datastore, NC_RPC_EDIT_DFLTOP default_op, const char } rpc = malloc(sizeof *rpc); - if (!rpc) { - ERRMEM; - return NULL; - } + NC_CHECK_ERRMEM_RET(!rpc, NULL); rpc->type = NC_RPC_EDITDATA; if (paramtype == NC_PARAMTYPE_DUP_AND_FREE) { @@ -636,10 +535,7 @@ nc_rpc_establishsub(const char *filter, const char *stream_name, const char *sta { struct nc_rpc_establishsub *rpc; - if (!stream_name) { - ERRARG("stream_name"); - return NULL; - } + NC_CHECK_ARG_RET(NULL, stream_name, NULL); if (filter && filter[0] && (filter[0] != '<') && (filter[0] != '/') && !isalpha(filter[0])) { ERR(NULL, "Filter is not an XML subtree, an XPath expression, not a filter reference (invalid first char '%c').", @@ -648,10 +544,7 @@ nc_rpc_establishsub(const char *filter, const char *stream_name, const char *sta } rpc = malloc(sizeof *rpc); - if (!rpc) { - ERRMEM; - return NULL; - } + NC_CHECK_ERRMEM_RET(!rpc, NULL); rpc->type = NC_RPC_ESTABLISHSUB; if (filter && (paramtype == NC_PARAMTYPE_DUP_AND_FREE)) { @@ -689,10 +582,7 @@ nc_rpc_modifysub(uint32_t id, const char *filter, const char *stop_time, NC_PARA { struct nc_rpc_modifysub *rpc; - if (!id) { - ERRARG("id"); - return NULL; - } + NC_CHECK_ARG_RET(NULL, id, NULL); if (filter && filter[0] && (filter[0] != '<') && (filter[0] != '/') && !isalpha(filter[0])) { ERR(NULL, "Filter is not an XML subtree, an XPath expression, not a filter reference (invalid first char '%c').", @@ -701,10 +591,7 @@ nc_rpc_modifysub(uint32_t id, const char *filter, const char *stop_time, NC_PARA } rpc = malloc(sizeof *rpc); - if (!rpc) { - ERRMEM; - return NULL; - } + NC_CHECK_ERRMEM_RET(!rpc, NULL); rpc->type = NC_RPC_MODIFYSUB; rpc->id = id; @@ -728,16 +615,10 @@ nc_rpc_deletesub(uint32_t id) { struct nc_rpc_deletesub *rpc; - if (!id) { - ERRARG("id"); - return NULL; - } + NC_CHECK_ARG_RET(NULL, id, NULL); rpc = malloc(sizeof *rpc); - if (!rpc) { - ERRMEM; - return NULL; - } + NC_CHECK_ERRMEM_RET(!rpc, NULL); rpc->type = NC_RPC_DELETESUB; rpc->id = id; @@ -750,16 +631,10 @@ nc_rpc_killsub(uint32_t id) { struct nc_rpc_killsub *rpc; - if (!id) { - ERRARG("id"); - return NULL; - } + NC_CHECK_ARG_RET(NULL, id, NULL); rpc = malloc(sizeof *rpc); - if (!rpc) { - ERRMEM; - return NULL; - } + NC_CHECK_ERRMEM_RET(!rpc, NULL); rpc->type = NC_RPC_KILLSUB; rpc->id = id; @@ -773,13 +648,7 @@ nc_rpc_establishpush_periodic(const char *datastore, const char *filter, const c { struct nc_rpc_establishpush *rpc; - if (!datastore) { - ERRARG("datastore"); - return NULL; - } else if (!period) { - ERRARG("period"); - return NULL; - } + NC_CHECK_ARG_RET(NULL, datastore, period, NULL); if (filter && filter[0] && (filter[0] != '<') && (filter[0] != '/') && !isalpha(filter[0])) { ERR(NULL, "Filter is not an XML subtree, an XPath expression, not a filter reference (invalid first char '%c').", @@ -788,10 +657,7 @@ nc_rpc_establishpush_periodic(const char *datastore, const char *filter, const c } rpc = malloc(sizeof *rpc); - if (!rpc) { - ERRMEM; - return NULL; - } + NC_CHECK_ERRMEM_RET(!rpc, NULL); rpc->type = NC_RPC_ESTABLISHPUSH; if (paramtype == NC_PARAMTYPE_DUP_AND_FREE) { @@ -833,10 +699,7 @@ nc_rpc_establishpush_onchange(const char *datastore, const char *filter, const c struct nc_rpc_establishpush *rpc; uint32_t i; - if (!datastore) { - ERRARG("datastore"); - return NULL; - } + NC_CHECK_ARG_RET(NULL, datastore, NULL); if (filter && filter[0] && (filter[0] != '<') && (filter[0] != '/') && !isalpha(filter[0])) { ERR(NULL, "Filter is not an XML subtree, an XPath expression, not a filter reference (invalid first char '%c').", @@ -845,10 +708,7 @@ nc_rpc_establishpush_onchange(const char *datastore, const char *filter, const c } rpc = malloc(sizeof *rpc); - if (!rpc) { - ERRMEM; - return NULL; - } + NC_CHECK_ERRMEM_RET(!rpc, NULL); rpc->type = NC_RPC_ESTABLISHPUSH; if (paramtype == NC_PARAMTYPE_DUP_AND_FREE) { @@ -895,13 +755,7 @@ nc_rpc_modifypush_periodic(uint32_t id, const char *datastore, const char *filte { struct nc_rpc_modifypush *rpc; - if (!id) { - ERRARG("id"); - return NULL; - } else if (!datastore) { - ERRARG("datastore"); - return NULL; - } + NC_CHECK_ARG_RET(NULL, id, datastore, NULL); if (filter && filter[0] && (filter[0] != '<') && (filter[0] != '/') && !isalpha(filter[0])) { ERR(NULL, "Filter is not an XML subtree, an XPath expression, not a filter reference (invalid first char '%c').", @@ -910,10 +764,7 @@ nc_rpc_modifypush_periodic(uint32_t id, const char *datastore, const char *filte } rpc = malloc(sizeof *rpc); - if (!rpc) { - ERRMEM; - return NULL; - } + NC_CHECK_ERRMEM_RET(!rpc, NULL); rpc->type = NC_RPC_MODIFYPUSH; rpc->id = id; @@ -950,13 +801,7 @@ nc_rpc_modifypush_onchange(uint32_t id, const char *datastore, const char *filte { struct nc_rpc_modifypush *rpc; - if (!id) { - ERRARG("id"); - return NULL; - } else if (!datastore) { - ERRARG("datastore"); - return NULL; - } + NC_CHECK_ARG_RET(NULL, id, datastore, NULL); if (filter && filter[0] && (filter[0] != '<') && (filter[0] != '/') && !isalpha(filter[0])) { ERR(NULL, "Filter is not an XML subtree, an XPath expression, not a filter reference (invalid first char '%c').", @@ -965,10 +810,7 @@ nc_rpc_modifypush_onchange(uint32_t id, const char *datastore, const char *filte } rpc = malloc(sizeof *rpc); - if (!rpc) { - ERRMEM; - return NULL; - } + NC_CHECK_ERRMEM_RET(!rpc, NULL); rpc->type = NC_RPC_MODIFYPUSH; rpc->id = id; @@ -999,16 +841,10 @@ nc_rpc_resyncsub(uint32_t id) { struct nc_rpc_resyncsub *rpc; - if (!id) { - ERRARG("id"); - return NULL; - } + NC_CHECK_ARG_RET(NULL, id, NULL); rpc = malloc(sizeof *rpc); - if (!rpc) { - ERRMEM; - return NULL; - } + NC_CHECK_ERRMEM_RET(!rpc, NULL); rpc->type = NC_RPC_RESYNCSUB; rpc->id = id; diff --git a/src/messages_p.h b/src/messages_p.h index 0ea41885..5bf3d3aa 100644 --- a/src/messages_p.h +++ b/src/messages_p.h @@ -16,10 +16,13 @@ #ifndef NC_MESSAGES_P_H_ #define NC_MESSAGES_P_H_ +#include + #include #include "messages_client.h" #include "messages_server.h" +#include "netconf.h" extern const char *rpcedit_dfltop2str[]; extern const char *rpcedit_testopt2str[]; diff --git a/src/messages_server.c b/src/messages_server.c index 078d0419..b02f5a1e 100644 --- a/src/messages_server.c +++ b/src/messages_server.c @@ -24,8 +24,11 @@ #include #include "compat.h" -#include "libnetconf.h" -#include "session_server.h" +#include "config.h" +#include "log_p.h" +#include "messages_p.h" +#include "messages_server.h" +#include "netconf.h" extern struct nc_server_opts server_opts; @@ -35,10 +38,7 @@ nc_server_reply_ok(void) struct nc_server_reply *ret; ret = malloc(sizeof *ret); - if (!ret) { - ERRMEM; - return NULL; - } + NC_CHECK_ERRMEM_RET(!ret, NULL); ret->type = NC_RPL_OK; return ret; @@ -49,16 +49,15 @@ nc_server_reply_data(struct lyd_node *data, NC_WD_MODE wd, NC_PARAMTYPE paramtyp { struct nc_server_reply_data *ret; - if (!data || !(data->schema->nodetype & (LYS_RPC | LYS_ACTION))) { - ERRARG("data"); + NC_CHECK_ARG_RET(NULL, data, NULL); + + if (!(data->schema->nodetype & (LYS_RPC | LYS_ACTION))) { + ERR(NULL, "nc_server_reply_data bad data"); return NULL; } ret = malloc(sizeof *ret); - if (!ret) { - ERRMEM; - return NULL; - } + NC_CHECK_ERRMEM_RET(!ret, NULL); ret->type = NC_RPL_DATA; ret->wd = wd; @@ -83,16 +82,10 @@ nc_server_reply_err(struct lyd_node *err) { struct nc_server_reply_error *ret; - if (!err) { - ERRARG("err"); - return NULL; - } + NC_CHECK_ARG_RET(NULL, err, NULL); ret = malloc(sizeof *ret); - if (!ret) { - ERRMEM; - return NULL; - } + NC_CHECK_ERRMEM_RET(!ret, NULL); ret->type = NC_RPL_ERROR; ret->err = err; @@ -104,11 +97,10 @@ nc_server_reply_add_err(struct nc_server_reply *reply, struct lyd_node *err) { struct nc_server_reply_error *err_rpl; - if (!reply || (reply->type != NC_RPL_ERROR)) { - ERRARG("reply"); - return -1; - } else if (!err) { - ERRARG("err"); + NC_CHECK_ARG_RET(NULL, reply, err, -1); + + if (reply->type != NC_RPL_ERROR) { + ERR(NULL, "nc_server_reply_add_err() bad reply type"); return -1; } @@ -122,8 +114,10 @@ nc_server_reply_get_last_err(const struct nc_server_reply *reply) { struct nc_server_reply_error *err_rpl; - if (!reply || (reply->type != NC_RPL_ERROR)) { - ERRARG("reply"); + NC_CHECK_ARG_RET(NULL, reply, NULL); + + if (reply->type != NC_RPL_ERROR) { + ERR(NULL, "nc_server_reply_get_last_err() bad reply type"); return NULL; } @@ -273,10 +267,7 @@ nc_err(const struct ly_ctx *ctx, NC_ERR tag, ...) const char *arg1, *arg2; uint32_t sid; - if (!tag) { - ERRARG("tag"); - return NULL; - } + NC_CHECK_ARG_RET(NULL, tag, NULL); /* rpc-error */ if (lyd_new_opaq2(NULL, ctx, "rpc-error", NULL, NULL, NC_NS_BASE, &err)) { @@ -294,7 +285,7 @@ nc_err(const struct ly_ctx *ctx, NC_ERR tag, ...) case NC_ERR_OP_NOT_SUPPORTED: type = (NC_ERR_TYPE)va_arg(ap, int); /* NC_ERR_TYPE enum is automatically promoted to int */ if ((type != NC_ERR_TYPE_PROT) && (type != NC_ERR_TYPE_APP)) { - ERRARG("type"); + ERRARG(NULL, "type"); goto fail; } break; @@ -307,7 +298,7 @@ nc_err(const struct ly_ctx *ctx, NC_ERR tag, ...) case NC_ERR_UNKNOWN_ATTR: type = (NC_ERR_TYPE)va_arg(ap, int); if (type == NC_ERR_TYPE_TRAN) { - ERRARG("type"); + ERRARG(NULL, "type"); goto fail; } break; @@ -316,14 +307,14 @@ nc_err(const struct ly_ctx *ctx, NC_ERR tag, ...) case NC_ERR_UNKNOWN_ELEM: type = (NC_ERR_TYPE)va_arg(ap, int); if ((type != NC_ERR_TYPE_PROT) && (type != NC_ERR_TYPE_APP)) { - ERRARG("type"); + ERRARG(NULL, "type"); goto fail; } break; case NC_ERR_UNKNOWN_NS: type = (NC_ERR_TYPE)va_arg(ap, int); if ((type != NC_ERR_TYPE_PROT) && (type != NC_ERR_TYPE_APP)) { - ERRARG("type"); + ERRARG(NULL, "type"); goto fail; } break; @@ -337,7 +328,7 @@ nc_err(const struct ly_ctx *ctx, NC_ERR tag, ...) case NC_ERR_OP_FAILED: type = (NC_ERR_TYPE)va_arg(ap, int); if (type == NC_ERR_TYPE_TRAN) { - ERRARG("type"); + ERRARG(NULL, "type"); goto fail; } break; @@ -345,7 +336,7 @@ nc_err(const struct ly_ctx *ctx, NC_ERR tag, ...) type = NC_ERR_TYPE_RPC; break; default: - ERRARG("tag"); + ERRARG(NULL, "tag"); goto fail; } if (lyd_new_opaq2(err, NULL, "error-type", nc_err_type2str(type), NULL, NC_NS_BASE, NULL)) { @@ -422,7 +413,7 @@ nc_err(const struct ly_ctx *ctx, NC_ERR tag, ...) nc_err_set_msg(err, "A message could not be handled because it failed to be parsed correctly.", "en"); break; default: - ERRARG("tag"); + ERRARG(NULL, "tag"); goto fail; } @@ -469,7 +460,7 @@ nc_err(const struct ly_ctx *ctx, NC_ERR tag, ...) nc_err_set_sid(err, sid); break; default: - ERRARG("tag"); + ERRARG(NULL, "tag"); goto fail; } @@ -487,10 +478,7 @@ nc_err_get_type(const struct lyd_node *err) { struct lyd_node *match; - if (!err) { - ERRARG("err"); - return 0; - } + NC_CHECK_ARG_RET(NULL, err, 0); lyd_find_sibling_opaq_next(lyd_child(err), "error-type", &match); if (match) { @@ -505,10 +493,7 @@ nc_err_get_tag(const struct lyd_node *err) { struct lyd_node *match; - if (!err) { - ERRARG("err"); - return 0; - } + NC_CHECK_ARG_RET(NULL, err, 0); lyd_find_sibling_opaq_next(lyd_child(err), "error-tag", &match); if (match) { @@ -523,13 +508,7 @@ nc_err_set_app_tag(struct lyd_node *err, const char *error_app_tag) { struct lyd_node *match; - if (!err) { - ERRARG("err"); - return -1; - } else if (!error_app_tag) { - ERRARG("error_app_tag"); - return -1; - } + NC_CHECK_ARG_RET(NULL, err, error_app_tag, -1); /* remove previous node */ lyd_find_sibling_opaq_next(lyd_child(err), "error-app-tag", &match); @@ -549,10 +528,7 @@ nc_err_get_app_tag(const struct lyd_node *err) { struct lyd_node *match; - if (!err) { - ERRARG("err"); - return NULL; - } + NC_CHECK_ARG_RET(NULL, err, NULL); lyd_find_sibling_opaq_next(lyd_child(err), "error-app-tag", &match); if (match) { @@ -567,13 +543,7 @@ nc_err_set_path(struct lyd_node *err, const char *error_path) { struct lyd_node *match; - if (!err) { - ERRARG("err"); - return -1; - } else if (!error_path) { - ERRARG("error_path"); - return -1; - } + NC_CHECK_ARG_RET(NULL, err, error_path, -1); /* remove previous node */ lyd_find_sibling_opaq_next(lyd_child(err), "error-path", &match); @@ -593,10 +563,7 @@ nc_err_get_path(const struct lyd_node *err) { struct lyd_node *match; - if (!err) { - ERRARG("err"); - return 0; - } + NC_CHECK_ARG_RET(NULL, err, NULL); lyd_find_sibling_opaq_next(lyd_child(err), "error-path", &match); if (match) { @@ -612,13 +579,7 @@ nc_err_set_msg(struct lyd_node *err, const char *error_message, const char *lang struct lyd_node *match; struct lyd_attr *attr; - if (!err) { - ERRARG("err"); - return -1; - } else if (!error_message) { - ERRARG("error_message"); - return -1; - } + NC_CHECK_ARG_RET(NULL, err, error_message, -1); /* remove previous message */ lyd_find_sibling_opaq_next(lyd_child(err), "error-message", &match); @@ -642,10 +603,7 @@ nc_err_get_msg(const struct lyd_node *err) { struct lyd_node *match; - if (!err) { - ERRARG("err"); - return NULL; - } + NC_CHECK_ARG_RET(NULL, err, NULL); lyd_find_sibling_opaq_next(lyd_child(err), "error-message", &match); if (match) { @@ -661,10 +619,7 @@ nc_err_set_sid(struct lyd_node *err, uint32_t session_id) struct lyd_node *match, *info; char buf[22]; - if (!err) { - ERRARG("err"); - return -1; - } + NC_CHECK_ARG_RET(NULL, err, -1); /* find error-info */ lyd_find_sibling_opaq_next(lyd_child(err), "error-info", &info); @@ -691,13 +646,7 @@ nc_err_add_bad_attr(struct lyd_node *err, const char *attr_name) { struct lyd_node *info; - if (!err) { - ERRARG("err"); - return -1; - } else if (!attr_name) { - ERRARG("attr_name"); - return -1; - } + NC_CHECK_ARG_RET(NULL, err, attr_name, -1); /* find error-info */ lyd_find_sibling_opaq_next(lyd_child(err), "error-info", &info); @@ -717,13 +666,7 @@ nc_err_add_bad_elem(struct lyd_node *err, const char *elem_name) { struct lyd_node *info; - if (!err) { - ERRARG("err"); - return -1; - } else if (!elem_name) { - ERRARG("elem_name"); - return -1; - } + NC_CHECK_ARG_RET(NULL, err, elem_name, -1); /* find error-info */ lyd_find_sibling_opaq_next(lyd_child(err), "error-info", &info); @@ -743,13 +686,7 @@ nc_err_add_bad_ns(struct lyd_node *err, const char *ns_name) { struct lyd_node *info; - if (!err) { - ERRARG("err"); - return -1; - } else if (!ns_name) { - ERRARG("ns_name"); - return -1; - } + NC_CHECK_ARG_RET(NULL, err, ns_name, -1); /* find error-info */ lyd_find_sibling_opaq_next(lyd_child(err), "error-info", &info); @@ -769,13 +706,7 @@ nc_err_add_info_other(struct lyd_node *err, struct lyd_node *other) { struct lyd_node *info; - if (!err) { - ERRARG("err"); - return -1; - } else if (!other) { - ERRARG("other"); - return -1; - } + NC_CHECK_ARG_RET(NULL, err, other, -1); /* find error-info */ lyd_find_sibling_opaq_next(lyd_child(err), "error-info", &info); @@ -840,13 +771,7 @@ nc_server_notif_new(struct lyd_node *event, char *eventtime, NC_PARAMTYPE paramt struct lyd_node *elem; int found; - if (!event) { - ERRARG("event"); - return NULL; - } else if (!eventtime) { - ERRARG("eventtime"); - return NULL; - } + NC_CHECK_ARG_RET(NULL, event, eventtime, NULL); /* check that there is a notification */ found = 0; @@ -858,15 +783,12 @@ nc_server_notif_new(struct lyd_node *event, char *eventtime, NC_PARAMTYPE paramt LYD_TREE_DFS_END(event, elem); } if (!found) { - ERRARG("event"); + ERRARG(NULL, "event"); return NULL; } ntf = malloc(sizeof *ntf); - if (!ntf) { - ERRMEM; - return NULL; - } + NC_CHECK_ERRMEM_RET(!ntf, NULL); if (paramtype == NC_PARAMTYPE_DUP_AND_FREE) { ntf->eventtime = strdup(eventtime); @@ -900,10 +822,7 @@ nc_server_notif_free(struct nc_server_notif *notif) API const char * nc_server_notif_get_time(const struct nc_server_notif *notif) { - if (!notif) { - ERRARG("notif"); - return NULL; - } + NC_CHECK_ARG_RET(NULL, notif, NULL); return notif->eventtime; } diff --git a/src/messages_server.h b/src/messages_server.h index a9bbbae0..f783bf81 100644 --- a/src/messages_server.h +++ b/src/messages_server.h @@ -20,9 +20,11 @@ extern "C" { #endif -#include +#include #include +#include + #include "netconf.h" #include "session.h" @@ -107,7 +109,7 @@ struct nc_server_reply *nc_server_reply_data(struct lyd_node *data, NC_WD_MODE w /** * @brief Create an ERROR rpc-reply object. * - * @param[in] err Errors created by ::nc_err(). It will be freed with the returned object. + * @param[in] err Errors created by nc_err(). It will be freed with the returned object. * @return rpc-reply object, NULL on error. */ struct nc_server_reply *nc_server_reply_err(struct lyd_node *err); @@ -116,7 +118,7 @@ struct nc_server_reply *nc_server_reply_err(struct lyd_node *err); * @brief Add another error opaque data node tree to an ERROR rpc-reply object. * * @param[in] reply ERROR reply to add to. - * @param[in] err Error created by ::nc_err(). It will be freed with the returned object. + * @param[in] err Error created by nc_err(). It will be freed with the returned object. * @return 0 on success, -1 on errror. */ int nc_server_reply_add_err(struct nc_server_reply *reply, struct lyd_node *err); diff --git a/src/netconf.h b/src/netconf.h index 48058cb2..1e25cce7 100644 --- a/src/netconf.h +++ b/src/netconf.h @@ -20,8 +20,6 @@ extern "C" { #endif -#include - /** * @addtogroup misc * @{ diff --git a/src/server_config.c b/src/server_config.c new file mode 100644 index 00000000..d41ea211 --- /dev/null +++ b/src/server_config.c @@ -0,0 +1,4595 @@ +/** + * @file server_config.c + * @author Roman Janota + * @brief libnetconf2 server configuration functions + * + * @copyright + * Copyright (c) 2022-2023 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 + +#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" +#include "server_config.h" +#include "server_config_p.h" +#include "session_p.h" + +#ifdef NC_ENABLED_SSH_TLS + +/* All libssh supported host-key, key-exchange, encryption and mac algorithms as of version 0.10.90 */ + +static const char *supported_hostkey_algs[] = { + "openssh-ssh-ed25519-cert-v01", "openssh-ecdsa-sha2-nistp521-cert-v01", + "openssh-ecdsa-sha2-nistp384-cert-v01", "openssh-ecdsa-sha2-nistp256-cert-v01", + "openssh-rsa-sha2-512-cert-v01", "openssh-rsa-sha2-256-cert-v01", + "openssh-ssh-rsa-cert-v01", "openssh-ssh-dss-cert-v01", + "ssh-ed25519", "ecdsa-sha2-nistp521", "ecdsa-sha2-nistp384", "ecdsa-sha2-nistp256", + "rsa-sha2-512", "rsa-sha2-256", "ssh-rsa", "ssh-dss", NULL +}; + +static const char *supported_kex_algs[] = { + "diffie-hellman-group-exchange-sha1", "curve25519-sha256", "libssh-curve25519-sha256", + "ecdh-sha2-nistp256", "ecdh-sha2-nistp384", "ecdh-sha2-nistp521", "diffie-hellman-group18-sha512", + "diffie-hellman-group16-sha512", "diffie-hellman-group-exchange-sha256", "diffie-hellman-group14-sha256", NULL +}; + +static const char *supported_encryption_algs[] = { + "openssh-chacha20-poly1305", "openssh-aes256-gcm", "openssh-aes128-gcm", + "aes256-ctr", "aes192-ctr", "aes128-ctr", "aes256-cbc", "aes192-cbc", "aes128-cbc", + "blowfish-cbc", "triple-des-cbc", "none", NULL +}; + +static const char *supported_mac_algs[] = { + "openssh-hmac-sha2-256-etm", "openssh-hmac-sha2-512-etm", "openssh-hmac-sha1-etm", + "hmac-sha2-256", "hmac-sha2-512", "hmac-sha1", NULL +}; + +#endif /* NC_ENABLED_SSH_TLS */ + +extern struct nc_server_opts server_opts; + +/* returns true if a node is a part of the listen subtree */ +static int +is_listen(const struct lyd_node *node) +{ + assert(node); + + while (node) { + if (!strcmp(LYD_NAME(node), "listen")) { + break; + } + node = lyd_parent(node); + } + + return node != NULL; +} + +/* returns true if a node is a part of the Call Home subtree */ +static int +is_ch(const struct lyd_node *node) +{ + assert(node); + + while (node) { + if (!strcmp(LYD_NAME(node), "call-home")) { + break; + } + node = lyd_parent(node); + } + + return node != NULL; +} + +#ifdef NC_ENABLED_SSH_TLS + +/* returns true if a node is a part of the ssh subtree */ +static int +is_ssh(const struct lyd_node *node) +{ + assert(node); + + while (node) { + if (!strcmp(LYD_NAME(node), "ssh")) { + break; + } + node = lyd_parent(node); + } + + return node != NULL; +} + +/* returns true if a node is a part of the tls subtree */ +static int +is_tls(const struct lyd_node *node) +{ + assert(node); + + while (node) { + if (!strcmp(LYD_NAME(node), "tls")) { + break; + } + node = lyd_parent(node); + } + + return node != NULL; +} + +#endif /* NC_ENABLED_SSH_TLS */ + +/* gets the endpoint struct (and optionally bind) based on node's location in the YANG data tree */ +static int +nc_server_config_get_endpt(const struct lyd_node *node, struct nc_endpt **endpt, struct nc_bind **bind) +{ + uint16_t i; + const char *name; + + assert(node && endpt); + name = LYD_NAME(node); + + while (node) { + if (!strcmp(LYD_NAME(node), "endpoint")) { + break; + } + node = lyd_parent(node); + } + + if (!node) { + ERR(NULL, "Node \"%s\" is not contained in an endpoint subtree.", name); + return 1; + } + + node = lyd_child(node); + assert(!strcmp(LYD_NAME(node), "name")); + name = lyd_get_value(node); + + for (i = 0; i < server_opts.endpt_count; i++) { + if (!strcmp(server_opts.endpts[i].name, name)) { + *endpt = &server_opts.endpts[i]; + if (bind) { + *bind = &server_opts.binds[i]; + } + return 0; + } + } + + ERR(NULL, "Endpoint \"%s\" was not found.", name); + return 1; +} + +/* gets the ch_client struct based on node's location in the YANG data tree */ +static int +nc_server_config_get_ch_client(const struct lyd_node *node, struct nc_ch_client **ch_client) +{ + uint16_t i; + const char *name; + + assert(node && ch_client); + name = LYD_NAME(node); + + while (node) { + if (!strcmp(LYD_NAME(node), "netconf-client")) { + break; + } + node = lyd_parent(node); + } + + if (!node) { + ERR(NULL, "Node \"%s\" is not contained in a netconf-client subtree.", name); + return 1; + } + + node = lyd_child(node); + assert(!strcmp(LYD_NAME(node), "name")); + name = lyd_get_value(node); + + /* LOCK */ + pthread_rwlock_rdlock(&server_opts.ch_client_lock); + for (i = 0; i < server_opts.ch_client_count; i++) { + if (!strcmp(server_opts.ch_clients[i].name, name)) { + *ch_client = &server_opts.ch_clients[i]; + /* UNLOCK */ + pthread_rwlock_unlock(&server_opts.ch_client_lock); + return 0; + } + } + + /* UNLOCK */ + pthread_rwlock_unlock(&server_opts.ch_client_lock); + ERR(NULL, "Call-home client \"%s\" was not found.", name); + return 1; +} + +/* gets the ch_endpt struct based on node's location in the YANG data tree */ +static int +nc_server_config_get_ch_endpt(const struct lyd_node *node, struct nc_ch_endpt **ch_endpt) +{ + uint16_t i; + const char *name; + struct nc_ch_client *ch_client; + + assert(node && ch_endpt); + name = LYD_NAME(node); + + if (nc_server_config_get_ch_client(node, &ch_client)) { + return 1; + } + + while (node) { + if (!strcmp(LYD_NAME(node), "endpoint")) { + break; + } + node = lyd_parent(node); + } + + if (!node) { + ERR(NULL, "Node \"%s\" is not contained in a call-home endpoint subtree.", name); + return 1; + } + + node = lyd_child(node); + assert(!strcmp(LYD_NAME(node), "name")); + name = lyd_get_value(node); + + /* LOCK */ + pthread_rwlock_rdlock(&server_opts.ch_client_lock); + for (i = 0; i < ch_client->ch_endpt_count; i++) { + if (!strcmp(ch_client->ch_endpts[i].name, name)) { + *ch_endpt = &ch_client->ch_endpts[i]; + /* UNLOCK */ + pthread_rwlock_unlock(&server_opts.ch_client_lock); + return 0; + } + } + + /* UNLOCK */ + pthread_rwlock_unlock(&server_opts.ch_client_lock); + ERR(NULL, "Call-home client's \"%s\" endpoint \"%s\" was not found.", ch_client->name, name); + return 1; +} + +#ifdef NC_ENABLED_SSH_TLS + +/* gets the ssh_opts struct based on node's location in the YANG data tree */ +static int +nc_server_config_get_ssh_opts(const struct lyd_node *node, struct nc_server_ssh_opts **opts) +{ + struct nc_endpt *endpt; + struct nc_ch_endpt *ch_endpt; + + assert(node && opts); + + if (is_listen(node)) { + if (nc_server_config_get_endpt(node, &endpt, NULL)) { + return 1; + } + *opts = endpt->opts.ssh; + } else { + if (nc_server_config_get_ch_endpt(node, &ch_endpt)) { + return 1; + } + *opts = ch_endpt->opts.ssh; + } + + return 0; +} + +/* gets the hostkey struct based on node's location in the YANG data tree */ +static int +nc_server_config_get_hostkey(const struct lyd_node *node, struct nc_hostkey **hostkey) +{ + uint16_t i; + const char *name; + struct nc_server_ssh_opts *opts; + + assert(node && hostkey); + name = LYD_NAME(node); + + while (node) { + if (!strcmp(LYD_NAME(node), "host-key")) { + break; + } + node = lyd_parent(node); + } + + if (!node) { + ERR(NULL, "Node \"%s\" is not contained in a host-key subtree.", name); + return 1; + } + + node = lyd_child(node); + assert(!strcmp(LYD_NAME(node), "name")); + name = lyd_get_value(node); + + if (nc_server_config_get_ssh_opts(node, &opts)) { + return 1; + } + for (i = 0; i < opts->hostkey_count; i++) { + if (!strcmp(opts->hostkeys[i].name, name)) { + *hostkey = &opts->hostkeys[i]; + return 0; + } + } + + ERR(NULL, "Host-key \"%s\" was not found.", name); + return 1; +} + +/* gets the client_auth struct based on node's location in the YANG data tree */ +static int +nc_server_config_get_auth_client(const struct lyd_node *node, struct nc_auth_client **auth_client) +{ + uint16_t i; + const char *name; + struct nc_server_ssh_opts *opts; + + assert(node && auth_client); + name = LYD_NAME(node); + + while (node) { + if (!strcmp(LYD_NAME(node), "user")) { + break; + } + node = lyd_parent(node); + } + + if (!node) { + ERR(NULL, "Node \"%s\" is not contained in a client-authentication subtree.", name); + return 1; + } + + node = lyd_child(node); + assert(!strcmp(LYD_NAME(node), "name")); + name = lyd_get_value(node); + + if (nc_server_config_get_ssh_opts(node, &opts)) { + return 1; + } + for (i = 0; i < opts->client_count; i++) { + if (!strcmp(opts->auth_clients[i].username, name)) { + *auth_client = &opts->auth_clients[i]; + return 0; + } + } + + ERR(NULL, "Authorized key \"%s\" was not found.", name); + return 1; +} + +/* gets the pubkey struct based on node's location in the YANG data tree */ +static int +nc_server_config_get_pubkey(const struct lyd_node *node, struct nc_public_key **pubkey) +{ + uint16_t i; + const char *name; + struct nc_auth_client *auth_client; + + assert(node && pubkey); + name = LYD_NAME(node); + + node = lyd_parent(node); + while (node) { + if (!strcmp(LYD_NAME(node), "public-key")) { + break; + } + node = lyd_parent(node); + } + + if (!node) { + ERR(NULL, "Node \"%s\" is not contained in a public-key subtree.", name); + return 1; + } + + node = lyd_child(node); + assert(!strcmp(LYD_NAME(node), "name")); + name = lyd_get_value(node); + + if (nc_server_config_get_auth_client(node, &auth_client)) { + return 1; + } + for (i = 0; i < auth_client->pubkey_count; i++) { + if (!strcmp(auth_client->pubkeys[i].name, name)) { + *pubkey = &auth_client->pubkeys[i]; + return 0; + } + } + + ERR(NULL, "Public key \"%s\" was not found.", name); + return 1; +} + +/* gets the tls_opts struct based on node's location in the YANG data tree */ +static int +nc_server_config_get_tls_opts(const struct lyd_node *node, struct nc_server_tls_opts **opts) +{ + struct nc_endpt *endpt; + struct nc_ch_endpt *ch_endpt; + + assert(node && opts); + + if (is_listen(node)) { + if (nc_server_config_get_endpt(node, &endpt, NULL)) { + return 1; + } + *opts = endpt->opts.tls; + } else { + if (nc_server_config_get_ch_endpt(node, &ch_endpt)) { + return 1; + } + *opts = ch_endpt->opts.tls; + } + + return 0; +} + +/* gets the cert struct based on node's location in the YANG data tree */ +static int +nc_server_config_get_cert(const struct lyd_node *node, struct nc_certificate **cert) +{ + uint16_t i; + const char *name; + struct nc_cert_grouping *auth_client; + struct nc_server_tls_opts *opts; + int is_cert_end_entity; + struct lyd_node *name_node; + + assert(node && cert); + name = LYD_NAME(node); + + /* check if node is in certificate subtree */ + while (node) { + if (!strcmp(LYD_NAME(node), "certificate")) { + break; + } + node = lyd_parent(node); + } + if (!node) { + ERR(NULL, "Node \"%s\" is not contained in a certificate subtree.", name); + return 1; + } + + /* it's child should be the list's key, check later */ + name_node = lyd_child(node); + + /* it's in certificate node, now check if it's end entity or certificate authority */ + while (node) { + if (!strcmp(LYD_NAME(node), "ee-certs")) { + is_cert_end_entity = 1; + break; + } else if (!strcmp(LYD_NAME(node), "ca-certs")) { + is_cert_end_entity = 0; + break; + } + node = lyd_parent(node); + } + if (!node) { + ERR(NULL, "Node \"%s\" is not contained in ee-certs nor ca-certs subtree.", name); + return 1; + } + + assert(!strcmp(LYD_NAME(name_node), "name")); + name = lyd_get_value(name_node); + + if (nc_server_config_get_tls_opts(node, &opts)) { + return 1; + } + if (is_cert_end_entity) { + auth_client = &opts->ee_certs; + } else { + auth_client = &opts->ca_certs; + } + + for (i = 0; i < auth_client->cert_count; i++) { + if (!strcmp(auth_client->certs[i].name, name)) { + *cert = &auth_client->certs[i]; + return 0; + } + } + + ERR(NULL, "Certificate \"%s\" was not found.", name); + return 1; +} + +/* gets the ctn struct based on node's location in the YANG data tree */ +static int +nc_server_config_get_ctn(const struct lyd_node *node, struct nc_ctn **ctn) +{ + uint32_t id; + struct nc_ctn *iter; + struct nc_server_tls_opts *opts; + const char *name; + + assert(node && ctn); + name = LYD_NAME(node); + + node = lyd_parent(node); + while (node) { + if (!strcmp(LYD_NAME(node), "cert-to-name")) { + break; + } + node = lyd_parent(node); + } + + if (!node) { + ERR(NULL, "Node \"%s\" is not contained in a cert-to-name subtree.", name); + return 1; + } + + node = lyd_child(node); + assert(!strcmp(LYD_NAME(node), "id")); + id = ((struct lyd_node_term *)node)->value.uint32; + + if (nc_server_config_get_tls_opts(node, &opts)) { + return 1; + } + + iter = opts->ctn; + while (iter) { + if (iter->id == id) { + *ctn = iter; + return 0; + } + + iter = iter->next; + } + + ERR(NULL, "Cert-to-name entry with id \"%d\" was not found.", id); + return 1; +} + +NC_PRIVKEY_FORMAT +nc_server_config_get_private_key_type(const char *format) +{ + if (!strcmp(format, "rsa-private-key-format")) { + return NC_PRIVKEY_FORMAT_RSA; + } else if (!strcmp(format, "ec-private-key-format")) { + return NC_PRIVKEY_FORMAT_EC; + } else if (!strcmp(format, "private-key-info-format")) { + return NC_PRIVKEY_FORMAT_X509; + } else if (!strcmp(format, "openssh-private-key-format")) { + return NC_PRIVKEY_FORMAT_OPENSSH; + } else { + ERR(NULL, "Private key format (%s) not supported.", format); + return NC_PRIVKEY_FORMAT_UNKNOWN; + } +} + +#endif /* NC_ENABLED_SSH_TLS */ + +/* gets the ch_client struct based on node's location in the YANG data tree and locks it for reading */ +static int +nc_server_config_get_ch_client_with_lock(const struct lyd_node *node, struct nc_ch_client **ch_client) +{ + uint16_t i; + const char *name; + + assert(node && ch_client); + name = LYD_NAME(node); + + while (node) { + if (!strcmp(LYD_NAME(node), "netconf-client")) { + break; + } + node = lyd_parent(node); + } + + if (!node) { + ERR(NULL, "Node \"%s\" is not contained in a netconf-client subtree.", name); + return 1; + } + + node = lyd_child(node); + assert(!strcmp(LYD_NAME(node), "name")); + name = lyd_get_value(node); + + /* LOCK */ + pthread_rwlock_rdlock(&server_opts.ch_client_lock); + for (i = 0; i < server_opts.ch_client_count; i++) { + if (!strcmp(server_opts.ch_clients[i].name, name)) { + /* LOCK */ + pthread_mutex_lock(&server_opts.ch_clients[i].lock); + *ch_client = &server_opts.ch_clients[i]; + return 0; + } + } + + /* UNLOCK */ + pthread_rwlock_unlock(&server_opts.ch_client_lock); + ERR(NULL, "Call-home client \"%s\" was not found.", name); + return 1; +} + +static void +nc_ch_client_unlock(struct nc_ch_client *client) +{ + assert(client); + + pthread_mutex_unlock(&client->lock); + pthread_rwlock_unlock(&server_opts.ch_client_lock); +} + +int +equal_parent_name(const struct lyd_node *node, uint16_t parent_count, const char *parent_name) +{ + uint16_t i; + + assert(node && parent_count && parent_name); + + node = lyd_parent(node); + for (i = 1; i < parent_count; i++) { + node = lyd_parent(node); + } + + if (!strcmp(LYD_NAME(node), parent_name)) { + return 1; + } + + return 0; +} + +int +nc_server_config_realloc(const char *key_value, void **ptr, size_t size, uint16_t *count) +{ + int ret = 0; + void *tmp; + char **name; + + tmp = realloc(*ptr, (*count + 1) * size); + NC_CHECK_ERRMEM_GOTO(!tmp, ret = 1, cleanup); + *ptr = tmp; + + /* set the newly allocated memory to 0 */ + memset((char *)(*ptr) + (*count * size), 0, size); + (*count)++; + + /* access the first member of the supposed structure */ + name = (char **)((*ptr) + ((*count - 1) * size)); + + /* and set it's value */ + *name = strdup(key_value); + NC_CHECK_ERRMEM_GOTO(!*name, ret = 1, cleanup); + +cleanup: + return ret; +} + +#ifdef NC_ENABLED_SSH_TLS + +static void +nc_server_config_del_hostkey(struct nc_server_ssh_opts *opts, struct nc_hostkey *hostkey) +{ + assert(hostkey->store == NC_STORE_LOCAL || hostkey->store == NC_STORE_KEYSTORE); + + free(hostkey->name); + + if (hostkey->store == NC_STORE_LOCAL) { + free(hostkey->key.pubkey_data); + free(hostkey->key.privkey_data); + } else { + free(hostkey->ks_ref); + } + + opts->hostkey_count--; + if (!opts->hostkey_count) { + free(opts->hostkeys); + opts->hostkeys = NULL; + } else if (hostkey != &opts->hostkeys[opts->hostkey_count]) { + memcpy(hostkey, &opts->hostkeys[opts->hostkey_count], sizeof *opts->hostkeys); + } +} + +static void +nc_server_config_del_auth_client_pubkey(struct nc_auth_client *auth_client, struct nc_public_key *pubkey) +{ + free(pubkey->name); + free(pubkey->data); + + auth_client->pubkey_count--; + if (!auth_client->pubkey_count) { + free(auth_client->pubkeys); + auth_client->pubkeys = NULL; + } else if (pubkey != &auth_client->pubkeys[auth_client->pubkey_count]) { + memcpy(pubkey, &auth_client->pubkeys[auth_client->pubkey_count], sizeof *auth_client->pubkeys); + } +} + +static void +nc_server_config_del_auth_client(struct nc_server_ssh_opts *opts, struct nc_auth_client *auth_client) +{ + uint16_t i, pubkey_count; + + free(auth_client->username); + + if (auth_client->store == NC_STORE_LOCAL) { + pubkey_count = auth_client->pubkey_count; + for (i = 0; i < pubkey_count; i++) { + nc_server_config_del_auth_client_pubkey(auth_client, &auth_client->pubkeys[i]); + } + } else { + free(auth_client->ts_ref); + } + + free(auth_client->password); + free(auth_client->pam_config_name); + free(auth_client->pam_config_dir); + + opts->client_count--; + if (!opts->client_count) { + free(opts->auth_clients); + opts->auth_clients = NULL; + } else if (auth_client != &opts->auth_clients[opts->client_count]) { + memcpy(auth_client, &opts->auth_clients[opts->client_count], sizeof *opts->auth_clients); + } +} + +static void +nc_server_config_del_ssh_opts(struct nc_bind *bind, struct nc_server_ssh_opts *opts) +{ + uint16_t i, hostkey_count, client_count; + + if (bind) { + free(bind->address); + if (bind->sock > -1) { + close(bind->sock); + } + } + + /* store in variable because it gets decremented in the function call */ + hostkey_count = opts->hostkey_count; + for (i = 0; i < hostkey_count; i++) { + nc_server_config_del_hostkey(opts, &opts->hostkeys[i]); + } + + client_count = opts->client_count; + for (i = 0; i < client_count; i++) { + nc_server_config_del_auth_client(opts, &opts->auth_clients[i]); + } + + free(opts->hostkey_algs); + free(opts->kex_algs); + free(opts->encryption_algs); + free(opts->mac_algs); + + free(opts); +} + +static void +nc_server_config_del_endpt_references(const char *referenced_endpt_name) +{ + uint16_t i, j; + + for (i = 0; i < server_opts.endpt_count; i++) { + if (server_opts.endpts[i].referenced_endpt_name) { + if (!strcmp(server_opts.endpts[i].referenced_endpt_name, 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) { + server_opts.endpts[i].opts.ssh->referenced_endpt_name = NULL; + } else { + server_opts.endpts[i].opts.tls->referenced_endpt_name = NULL; + } + } + } + } + + /* LOCK */ + pthread_rwlock_rdlock(&server_opts.ch_client_lock); + for (i = 0; i < server_opts.ch_client_count; i++) { + /* LOCK */ + pthread_mutex_lock(&server_opts.ch_clients[i].lock); + for (j = 0; j < server_opts.ch_clients[i].ch_endpt_count; j++) { + if (server_opts.ch_clients[i].ch_endpts[j].referenced_endpt_name) { + if (!strcmp(server_opts.ch_clients[i].ch_endpts[j].referenced_endpt_name, 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) { + 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; + } + } + } + } + /* UNLOCK */ + pthread_mutex_unlock(&server_opts.ch_clients[i].lock); + } + + /* UNLOCK */ + pthread_rwlock_unlock(&server_opts.ch_client_lock); +} + +void +nc_server_config_del_endpt_ssh(struct nc_endpt *endpt, struct nc_bind *bind) +{ + /* delete any references to this endpoint */ + nc_server_config_del_endpt_references(endpt->name); + free(endpt->name); + + free(endpt->referenced_endpt_name); + nc_server_config_del_ssh_opts(bind, endpt->opts.ssh); + + server_opts.endpt_count--; + if (!server_opts.endpt_count) { + free(server_opts.endpts); + free(server_opts.binds); + server_opts.endpts = NULL; + server_opts.binds = NULL; + } else if (endpt != &server_opts.endpts[server_opts.endpt_count]) { + memcpy(endpt, &server_opts.endpts[server_opts.endpt_count], sizeof *server_opts.endpts); + memcpy(bind, &server_opts.binds[server_opts.endpt_count], sizeof *server_opts.binds); + } +} + +#endif /* NC_ENABLED_SSH_TLS */ + +void +nc_server_config_del_unix_socket_opts(struct nc_bind *bind, struct nc_server_unix_opts *opts) +{ + if (bind->sock > -1) { + close(bind->sock); + } + + unlink(bind->address); + free(bind->address); + free(opts->address); + + free(opts); +} + +void +nc_server_config_del_endpt_unix_socket(struct nc_endpt *endpt, struct nc_bind *bind) +{ + free(endpt->name); + nc_server_config_del_unix_socket_opts(bind, endpt->opts.unixsock); + + server_opts.endpt_count--; + if (!server_opts.endpt_count) { + free(server_opts.endpts); + free(server_opts.binds); + server_opts.endpts = NULL; + server_opts.binds = NULL; + } else if (endpt != &server_opts.endpts[server_opts.endpt_count]) { + memcpy(endpt, &server_opts.endpts[server_opts.endpt_count], sizeof *server_opts.endpts); + memcpy(bind, &server_opts.binds[server_opts.endpt_count], sizeof *server_opts.binds); + } +} + +#ifdef NC_ENABLED_SSH_TLS + +static void +nc_server_config_del_cert(struct nc_cert_grouping *certs, struct nc_certificate *cert) +{ + free(cert->name); + free(cert->data); + + certs->cert_count--; + if (!certs->cert_count) { + free(certs->certs); + certs->certs = NULL; + } else if (cert != &certs->certs[certs->cert_count]) { + memcpy(cert, &certs->certs[certs->cert_count], sizeof *certs->certs); + } +} + +static void +nc_server_config_del_certs(struct nc_cert_grouping *certs_grp) +{ + uint16_t i; + + if (certs_grp->store == NC_STORE_LOCAL) { + for (i = 0; i < certs_grp->cert_count; i++) { + free(certs_grp->certs[i].name); + free(certs_grp->certs[i].data); + } + free(certs_grp->certs); + certs_grp->certs = NULL; + } else { + free(certs_grp->ts_ref); + } +} + +static void +nc_server_config_del_ctn(struct nc_server_tls_opts *opts, struct nc_ctn *ctn) +{ + struct nc_ctn *iter; + + free(ctn->name); + free(ctn->fingerprint); + + if (opts->ctn == ctn) { + /* it's the first in the list */ + opts->ctn = ctn->next; + free(ctn); + return; + } + + for (iter = opts->ctn; iter; iter = iter->next) { + if (iter->next == ctn) { + /* found the ctn */ + break; + } + } + + iter->next = ctn->next; + free(ctn); +} + +static void +nc_server_config_del_ctns(struct nc_server_tls_opts *opts) +{ + struct nc_ctn *cur, *next; + + for (cur = opts->ctn; cur; cur = next) { + next = cur->next; + free(cur->name); + free(cur->fingerprint); + free(cur); + } + + opts->ctn = NULL; +} + +static void +nc_server_config_del_tls_opts(struct nc_bind *bind, struct nc_server_tls_opts *opts) +{ + if (bind) { + free(bind->address); + if (bind->sock > -1) { + close(bind->sock); + } + } + + if (opts->store == NC_STORE_LOCAL) { + free(opts->pubkey_data); + free(opts->privkey_data); + free(opts->cert_data); + } else { + free(opts->key_ref); + free(opts->cert_ref); + } + + nc_server_config_del_certs(&opts->ca_certs); + nc_server_config_del_certs(&opts->ee_certs); + + free(opts->crl_path); + free(opts->crl_url); + X509_STORE_free(opts->crl_store); + + nc_server_config_del_ctns(opts); + free(opts->ciphers); + free(opts); +} + +static void +nc_server_config_del_endpt_tls(struct nc_endpt *endpt, struct nc_bind *bind) +{ + /* delete any references to this endpoint */ + nc_server_config_del_endpt_references(endpt->name); + free(endpt->name); + + free(endpt->referenced_endpt_name); + + nc_server_config_del_tls_opts(bind, endpt->opts.tls); + + server_opts.endpt_count--; + if (!server_opts.endpt_count) { + free(server_opts.endpts); + free(server_opts.binds); + server_opts.endpts = NULL; + server_opts.binds = NULL; + } else if (endpt != &server_opts.endpts[server_opts.endpt_count]) { + memcpy(endpt, &server_opts.endpts[server_opts.endpt_count], sizeof *server_opts.endpts); + memcpy(bind, &server_opts.binds[server_opts.endpt_count], sizeof *server_opts.binds); + } +} + +#endif /* NC_ENABLED_SSH_TLS */ + +/* presence container */ +int +nc_server_config_listen(const struct lyd_node *node, NC_OPERATION op) +{ + uint16_t i, endpt_count; + + (void) node; + + assert(op == NC_OP_CREATE || op == NC_OP_DELETE); + + if (op == NC_OP_DELETE) { + endpt_count = server_opts.endpt_count; + for (i = 0; i < endpt_count; i++) { + switch (server_opts.endpts[i].ti) { +#ifdef NC_ENABLED_SSH_TLS + case NC_TI_LIBSSH: + nc_server_config_del_endpt_ssh(&server_opts.endpts[i], &server_opts.binds[i]); + break; + case NC_TI_OPENSSL: + nc_server_config_del_endpt_tls(&server_opts.endpts[i], &server_opts.binds[i]); + break; +#endif /* NC_ENABLED_SSH_TLS */ + case NC_TI_UNIX: + nc_server_config_del_endpt_unix_socket(&server_opts.endpts[i], &server_opts.binds[i]); + break; + case NC_TI_NONE: + case NC_TI_FD: + ERRINT; + return 1; + } + } + } + + return 0; +} + +static void +nc_server_config_ch_del_endpt(struct nc_ch_client *ch_client, struct nc_ch_endpt *ch_endpt) +{ + free(ch_endpt->name); + +#ifdef NC_ENABLED_SSH_TLS + free(ch_endpt->address); + if (ch_endpt->sock_pending > -1) { + close(ch_endpt->sock_pending); + ch_endpt->sock_pending = -1; + } + free(ch_endpt->referenced_endpt_name); +#endif /* NC_ENABLED_SSH_TLS */ + + switch (ch_endpt->ti) { +#ifdef NC_ENABLED_SSH_TLS + case NC_TI_LIBSSH: + nc_server_config_del_ssh_opts(NULL, ch_endpt->opts.ssh); + break; + case NC_TI_OPENSSL: + nc_server_config_del_tls_opts(NULL, ch_endpt->opts.tls); + break; +#endif /* NC_ENABLED_SSH_TLS */ + default: + ERRINT; + break; + } + + ch_client->ch_endpt_count--; + if (!ch_client->ch_endpt_count) { + free(ch_client->ch_endpts); + ch_client->ch_endpts = NULL; + } +} + +static void +nc_server_config_ch_del_client(struct nc_ch_client *ch_client) +{ + uint16_t i, ch_endpt_count; + struct nc_ch_client client; + pthread_t tid; + + /* WR LOCK */ + pthread_rwlock_wrlock(&server_opts.ch_client_lock); + + /* copy the client we want to delete into a local variable */ + memcpy(&client, ch_client, sizeof *ch_client); + /* get his tid */ + tid = client.tid; + + /* delete the client */ + server_opts.ch_client_count--; + if (!server_opts.ch_client_count) { + free(server_opts.ch_clients); + server_opts.ch_clients = NULL; + } else { + memcpy(ch_client, &server_opts.ch_clients[server_opts.ch_client_count], sizeof *server_opts.ch_clients); + } + + /* WR UNLOCK */ + pthread_rwlock_unlock(&server_opts.ch_client_lock); + + /* RD LOCK */ + pthread_rwlock_rdlock(&server_opts.ch_client_lock); + /* MUTEX LOCK */ + pthread_mutex_lock(&client.lock); + + if (client.thread_data->thread_running) { + /* CH COND LOCK */ + pthread_mutex_lock(&client.thread_data->cond_lock); + client.thread_data->thread_running = 0; + pthread_cond_signal(&client.thread_data->cond); + /* CH COND UNLOCK */ + pthread_mutex_unlock(&client.thread_data->cond_lock); + + /* MUTEX UNLOCK */ + pthread_mutex_unlock(&client.lock); + /* RD UNLOCK */ + pthread_rwlock_unlock(&server_opts.ch_client_lock); + + /* wait for the thread to terminate */ + pthread_join(tid, NULL); + } + + /* free its members */ + free(client.name); + + ch_endpt_count = client.ch_endpt_count; + for (i = 0; i < ch_endpt_count; i++) { + nc_server_config_ch_del_endpt(&client, &client.ch_endpts[i]); + } +} + +int +nc_server_config_ch(const struct lyd_node *node, NC_OPERATION op) +{ + uint16_t i, ch_client_count; + + (void) node; + + if (op == NC_OP_DELETE) { + ch_client_count = server_opts.ch_client_count; + for (i = 0; i < ch_client_count; i++) { + nc_server_config_ch_del_client(&server_opts.ch_clients[i]); + } + } + + return 0; +} + +/* default leaf */ +static int +nc_server_config_hello_timeout(const struct lyd_node *node, NC_OPERATION op) +{ + assert(!strcmp(LYD_NAME(node), "hello-timeout")); + + if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) { + server_opts.hello_timeout = ((struct lyd_node_term *)node)->value.uint16; + } else { + /* default value */ + server_opts.hello_timeout = 60; + } + + return 0; +} + +/* default leaf */ +static int +nc_server_config_idle_timeout(const struct lyd_node *node, NC_OPERATION op) +{ + struct nc_ch_client *ch_client; + + assert(!strcmp(LYD_NAME(node), "idle-timeout")); + + if (is_ch(node)) { + /* call-home idle timeout */ + if (nc_server_config_get_ch_client_with_lock(node, &ch_client)) { + /* to avoid unlock on fail */ + return 1; + } + + if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) { + ch_client->idle_timeout = ((struct lyd_node_term *)node)->value.uint16; + } else if (op == NC_OP_DELETE) { + ch_client->idle_timeout = 180; + } + + nc_ch_client_unlock(ch_client); + } else { + /* whole server idle timeout */ + if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) { + server_opts.idle_timeout = ((struct lyd_node_term *)node)->value.uint16; + } else { + /* default value */ + server_opts.idle_timeout = 0; + } + } + + return 0; +} + +static int +nc_server_config_create_bind(void) +{ + int ret = 0; + void *tmp; + + tmp = realloc(server_opts.binds, (server_opts.endpt_count + 1) * sizeof *server_opts.binds); + NC_CHECK_ERRMEM_GOTO(!tmp, ret = 1, cleanup); + server_opts.binds = tmp; + memset(&server_opts.binds[server_opts.endpt_count], 0, sizeof *server_opts.binds); + + server_opts.binds[server_opts.endpt_count].sock = -1; + +cleanup: + return ret; +} + +static int +nc_server_config_create_endpoint(const struct lyd_node *node) +{ + if (nc_server_config_create_bind()) { + return 1; + } + + node = lyd_child(node); + assert(!strcmp(LYD_NAME(node), "name")); + + return nc_server_config_realloc(lyd_get_value(node), (void **)&server_opts.endpts, sizeof *server_opts.endpts, &server_opts.endpt_count); +} + +static int +nc_server_config_ch_create_endpoint(const struct lyd_node *node, struct nc_ch_client *ch_client) +{ + node = lyd_child(node); + assert(!strcmp(LYD_NAME(node), "name")); + + return nc_server_config_realloc(lyd_get_value(node), (void **)&ch_client->ch_endpts, sizeof *ch_client->ch_endpts, &ch_client->ch_endpt_count); +} + +/* list */ +static int +nc_server_config_endpoint(const struct lyd_node *node, NC_OPERATION op) +{ + int ret = 0; + struct nc_endpt *endpt; + struct nc_bind *bind; + struct nc_ch_endpt *ch_endpt; + struct nc_ch_client *ch_client; + + assert(!strcmp(LYD_NAME(node), "endpoint")); + + if (is_listen(node)) { + /* listen */ + if (op == NC_OP_CREATE) { + ret = nc_server_config_create_endpoint(node); + if (ret) { + goto cleanup; + } + } else if (op == NC_OP_DELETE) { + /* free all children */ + if (nc_server_config_get_endpt(node, &endpt, &bind)) { + ret = 1; + goto cleanup; + } + + switch (endpt->ti) { +#ifdef NC_ENABLED_SSH_TLS + case NC_TI_LIBSSH: + nc_server_config_del_endpt_ssh(endpt, bind); + break; + case NC_TI_OPENSSL: + nc_server_config_del_endpt_tls(endpt, bind); + break; +#endif /* NC_ENABLED_SSH_TLS */ + case NC_TI_UNIX: + nc_server_config_del_endpt_unix_socket(endpt, bind); + break; + case NC_TI_NONE: + case NC_TI_FD: + ERRINT; + ret = 1; + goto cleanup; + } + } + } else if (is_ch(node)) { + /* LOCK */ + if (nc_server_config_get_ch_client_with_lock(node, &ch_client)) { + /* to avoid unlock on fail */ + return 1; + } + + if (op == NC_OP_CREATE) { + ret = nc_server_config_ch_create_endpoint(node, ch_client); + if (ret) { + goto cleanup; + } + + /* init ch sock */ + ch_client->ch_endpts[ch_client->ch_endpt_count - 1].sock_pending = -1; + } else if (op == NC_OP_DELETE) { + if (nc_server_config_get_ch_endpt(node, &ch_endpt)) { + ret = 1; + goto cleanup; + } + + nc_server_config_ch_del_endpt(ch_client, ch_endpt); + } + } + +cleanup: + if (is_ch(node)) { + /* UNLOCK */ + nc_ch_client_unlock(ch_client); + } + return ret; +} + +#ifdef NC_ENABLED_SSH_TLS + +static int +nc_server_config_create_ssh(struct nc_endpt *endpt) +{ + endpt->ti = NC_TI_LIBSSH; + endpt->opts.ssh = calloc(1, sizeof(struct nc_server_ssh_opts)); + NC_CHECK_ERRMEM_RET(!endpt->opts.ssh, 1); + + return 0; +} + +static int +nc_server_config_ch_create_ssh(struct nc_ch_endpt *ch_endpt) +{ + ch_endpt->ti = NC_TI_LIBSSH; + ch_endpt->opts.ssh = calloc(1, sizeof(struct nc_server_ssh_opts)); + NC_CHECK_ERRMEM_RET(!ch_endpt->opts.ssh, 1); + + return 0; +} + +/* NP container */ +static int +nc_server_config_ssh(const struct lyd_node *node, NC_OPERATION op) +{ + struct nc_endpt *endpt; + struct nc_bind *bind; + struct nc_ch_endpt *ch_endpt; + struct nc_ch_client *ch_client; + int ret = 0; + + assert(!strcmp(LYD_NAME(node), "ssh")); + + if (is_listen(node)) { + if (nc_server_config_get_endpt(node, &endpt, &bind)) { + ret = 1; + goto cleanup; + } + + if (op == NC_OP_CREATE) { + ret = nc_server_config_create_ssh(endpt); + if (ret) { + goto cleanup; + } + } else if (op == NC_OP_DELETE) { + nc_server_config_del_ssh_opts(bind, endpt->opts.ssh); + } + } else { + /* LOCK */ + if (nc_server_config_get_ch_client_with_lock(node, &ch_client)) { + /* to avoid unlock on fail */ + return 1; + } + + if (nc_server_config_get_ch_endpt(node, &ch_endpt)) { + ret = 1; + goto cleanup; + } + + if (op == NC_OP_CREATE) { + ret = nc_server_config_ch_create_ssh(ch_endpt); + if (ret) { + goto cleanup; + } + } else if (op == NC_OP_DELETE) { + nc_server_config_del_ssh_opts(NULL, ch_endpt->opts.ssh); + } + } + +cleanup: + if (is_ch(node)) { + /* UNLOCK */ + nc_ch_client_unlock(ch_client); + } + return ret; +} + +static int +nc_server_config_create_tls(struct nc_endpt *endpt) +{ + endpt->ti = NC_TI_OPENSSL; + endpt->opts.tls = calloc(1, sizeof *endpt->opts.tls); + NC_CHECK_ERRMEM_RET(!endpt->opts.tls, 1); + + return 0; +} + +static int +nc_server_config_ch_create_tls(struct nc_ch_endpt *ch_endpt) +{ + ch_endpt->ti = NC_TI_OPENSSL; + ch_endpt->opts.tls = calloc(1, sizeof(struct nc_server_tls_opts)); + NC_CHECK_ERRMEM_RET(!ch_endpt->opts.tls, 1); + + return 0; +} + +static int +nc_server_config_tls(const struct lyd_node *node, NC_OPERATION op) +{ + struct nc_endpt *endpt; + struct nc_bind *bind; + struct nc_ch_endpt *ch_endpt; + struct nc_ch_client *ch_client; + int ret = 0; + + assert(!strcmp(LYD_NAME(node), "tls")); + + if (is_listen(node)) { + if (nc_server_config_get_endpt(node, &endpt, &bind)) { + ret = 1; + goto cleanup; + } + + if (op == NC_OP_CREATE) { + ret = nc_server_config_create_tls(endpt); + if (ret) { + goto cleanup; + } + } else if (op == NC_OP_DELETE) { + nc_server_config_del_tls_opts(bind, endpt->opts.tls); + } + } else { + /* LOCK */ + if (nc_server_config_get_ch_client_with_lock(node, &ch_client)) { + /* to avoid unlock on fail */ + return 1; + } + + if (nc_server_config_get_ch_endpt(node, &ch_endpt)) { + ret = 1; + goto cleanup; + } + + if (op == NC_OP_CREATE) { + ret = nc_server_config_ch_create_tls(ch_endpt); + if (ret) { + goto cleanup; + } + } else if (op == NC_OP_DELETE) { + nc_server_config_del_tls_opts(NULL, ch_endpt->opts.tls); + } + } + +cleanup: + if (is_ch(node)) { + /* UNLOCK */ + nc_ch_client_unlock(ch_client); + } + return ret; +} + +#endif /* NC_ENABLED_SSH_TLS */ + +static int +nc_server_config_set_address_port(struct nc_endpt *endpt, struct nc_bind *bind, const char *address, uint16_t port) +{ + int sock = -1, set_addr, ret = 0; + + assert((address && !port) || (!address && port) || (endpt->ti == NC_TI_UNIX)); + + if (address) { + set_addr = 1; + } else { + set_addr = 0; + } + + if (set_addr) { + port = bind->port; + } else { + address = bind->address; + } + + /* we have all the information we need to create a listening socket */ + if ((address && port) || (endpt->ti == NC_TI_UNIX)) { + /* create new socket, close the old one */ + if (endpt->ti == NC_TI_UNIX) { + sock = nc_sock_listen_unix(endpt->opts.unixsock); + } else { + sock = nc_sock_listen_inet(address, port, &endpt->ka); + } + + if (sock == -1) { + ret = 1; + goto cleanup; + } + + if (bind->sock > -1) { + close(bind->sock); + } + bind->sock = sock; + } + + if (sock > -1) { + switch (endpt->ti) { + case NC_TI_UNIX: + VRB(NULL, "Listening on %s for UNIX connections.", endpt->opts.unixsock->address); + break; +#ifdef NC_ENABLED_SSH_TLS + case NC_TI_LIBSSH: + VRB(NULL, "Listening on %s:%u for SSH connections.", address, port); + break; + case NC_TI_OPENSSL: + VRB(NULL, "Listening on %s:%u for TLS connections.", address, port); + break; +#endif /* NC_ENABLED_SSH_TLS */ + default: + ERRINT; + ret = 1; + break; + } + } + +cleanup: + return ret; +} + +#ifdef NC_ENABLED_SSH_TLS + +/* mandatory leaf */ +static int +nc_server_config_local_address(const struct lyd_node *node, NC_OPERATION op) +{ + struct nc_endpt *endpt; + struct nc_bind *bind; + int ret = 0; + + (void) op; + + assert(!strcmp(LYD_NAME(node), "local-address")); + + if (equal_parent_name(node, 4, "listen")) { + if (nc_server_config_get_endpt(node, &endpt, &bind)) { + ret = 1; + goto cleanup; + } + + free(bind->address); + bind->address = strdup(lyd_get_value(node)); + NC_CHECK_ERRMEM_GOTO(!bind->address, ret = 1, cleanup); + + ret = nc_server_config_set_address_port(endpt, bind, lyd_get_value(node), 0); + if (ret) { + goto cleanup; + } + } + +cleanup: + return ret; +} + +/* leaf with default value */ +static int +nc_server_config_local_port(const struct lyd_node *node, NC_OPERATION op) +{ + struct nc_endpt *endpt; + struct nc_bind *bind; + int ret = 0; + + assert(!strcmp(LYD_NAME(node), "local-port")); + + if (equal_parent_name(node, 4, "listen")) { + if (nc_server_config_get_endpt(node, &endpt, &bind)) { + ret = 1; + goto cleanup; + } + + if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) { + bind->port = ((struct lyd_node_term *)node)->value.uint16; + } else { + /* delete -> set to default */ + bind->port = 0; + } + + ret = nc_server_config_set_address_port(endpt, bind, NULL, bind->port); + if (ret) { + goto cleanup; + } + } + +cleanup: + return ret; +} + +/* P container */ +static int +nc_server_config_keepalives(const struct lyd_node *node, NC_OPERATION op) +{ + int ret = 0; + struct nc_endpt *endpt; + struct nc_bind *bind; + struct nc_ch_endpt *ch_endpt; + struct nc_ch_client *ch_client; + + assert(!strcmp(LYD_NAME(node), "keepalives")); + + if (is_listen(node) && equal_parent_name(node, 1, "tcp-server-parameters")) { + if (nc_server_config_get_endpt(node, &endpt, &bind)) { + ret = 1; + goto cleanup; + } + + if (op == NC_OP_CREATE) { + endpt->ka.enabled = 1; + } else { + endpt->ka.enabled = 0; + } + ret = nc_sock_configure_keepalive(bind->sock, &endpt->ka); + if (ret) { + goto cleanup; + } + } else if (is_ch(node) && equal_parent_name(node, 1, "tcp-client-parameters")) { + /* LOCK */ + if (nc_server_config_get_ch_client_with_lock(node, &ch_client)) { + /* to avoid unlock on fail */ + return 1; + } + + if (nc_server_config_get_ch_endpt(node, &ch_endpt)) { + ret = 1; + goto cleanup; + } + + if (op == NC_OP_CREATE) { + ch_endpt->ka.enabled = 1; + } else { + ch_endpt->ka.enabled = 0; + } + } + +cleanup: + if (is_ch(node) && equal_parent_name(node, 1, "tcp-client-parameters")) { + /* UNLOCK */ + nc_ch_client_unlock(ch_client); + } + return ret; +} + +/* mandatory leaf */ +static int +nc_server_config_idle_time(const struct lyd_node *node, NC_OPERATION op) +{ + int ret = 0; + struct nc_endpt *endpt; + struct nc_bind *bind; + struct nc_ch_endpt *ch_endpt; + struct nc_ch_client *ch_client; + + assert(!strcmp(LYD_NAME(node), "idle-time")); + + if (is_listen(node) && equal_parent_name(node, 2, "tcp-server-parameters")) { + if (nc_server_config_get_endpt(node, &endpt, &bind)) { + ret = 1; + goto cleanup; + } + + if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) { + endpt->ka.idle_time = ((struct lyd_node_term *)node)->value.uint16; + } else { + endpt->ka.idle_time = 0; + } + ret = nc_sock_configure_keepalive(bind->sock, &endpt->ka); + if (ret) { + goto cleanup; + } + } else if (is_ch(node) && equal_parent_name(node, 2, "tcp-client-parameters")) { + /* LOCK */ + if (nc_server_config_get_ch_client_with_lock(node, &ch_client)) { + /* to avoid unlock on fail */ + return 1; + } + + if (nc_server_config_get_ch_endpt(node, &ch_endpt)) { + ret = 1; + goto cleanup; + } + + if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) { + ch_endpt->ka.idle_time = ((struct lyd_node_term *)node)->value.uint16; + } else { + ch_endpt->ka.idle_time = 0; + } + } + +cleanup: + if (is_ch(node) && equal_parent_name(node, 2, "tcp-client-parameters")) { + /* UNLOCK */ + nc_ch_client_unlock(ch_client); + } + return ret; +} + +/* mandatory leaf */ +static int +nc_server_config_max_probes(const struct lyd_node *node, NC_OPERATION op) +{ + int ret = 0; + struct nc_endpt *endpt; + struct nc_bind *bind; + struct nc_ch_endpt *ch_endpt; + struct nc_ch_client *ch_client; + + assert(!strcmp(LYD_NAME(node), "max-probes")); + + if (is_listen(node) && equal_parent_name(node, 2, "tcp-server-parameters")) { + if (nc_server_config_get_endpt(node, &endpt, &bind)) { + ret = 1; + goto cleanup; + } + + if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) { + endpt->ka.max_probes = ((struct lyd_node_term *)node)->value.uint16; + } else { + endpt->ka.max_probes = 0; + } + ret = nc_sock_configure_keepalive(bind->sock, &endpt->ka); + if (ret) { + goto cleanup; + } + } else if (is_ch(node) && equal_parent_name(node, 2, "tcp-client-parameters")) { + /* LOCK */ + if (nc_server_config_get_ch_client_with_lock(node, &ch_client)) { + /* to avoid unlock on fail */ + return 1; + } + + if (nc_server_config_get_ch_endpt(node, &ch_endpt)) { + ret = 1; + goto cleanup; + } + + if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) { + ch_endpt->ka.max_probes = ((struct lyd_node_term *)node)->value.uint16; + } else { + ch_endpt->ka.max_probes = 0; + } + } + +cleanup: + if (is_ch(node) && equal_parent_name(node, 2, "tcp-client-parameters")) { + /* UNLOCK */ + nc_ch_client_unlock(ch_client); + } + return ret; +} + +/* mandatory leaf */ +static int +nc_server_config_probe_interval(const struct lyd_node *node, NC_OPERATION op) +{ + int ret = 0; + struct nc_endpt *endpt; + struct nc_bind *bind; + struct nc_ch_endpt *ch_endpt; + struct nc_ch_client *ch_client; + + assert(!strcmp(LYD_NAME(node), "probe-interval")); + + if (is_listen(node) && equal_parent_name(node, 2, "tcp-server-parameters")) { + if (nc_server_config_get_endpt(node, &endpt, &bind)) { + ret = 1; + goto cleanup; + } + + if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) { + endpt->ka.probe_interval = ((struct lyd_node_term *)node)->value.uint16; + } else { + endpt->ka.probe_interval = 0; + } + ret = nc_sock_configure_keepalive(bind->sock, &endpt->ka); + if (ret) { + goto cleanup; + } + } else if (is_ch(node) && equal_parent_name(node, 2, "tcp-client-parameters")) { + /* LOCK */ + if (nc_server_config_get_ch_client_with_lock(node, &ch_client)) { + /* to avoid unlock on fail */ + return 1; + } + + if (nc_server_config_get_ch_endpt(node, &ch_endpt)) { + ret = 1; + goto cleanup; + } + + if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) { + ch_endpt->ka.probe_interval = ((struct lyd_node_term *)node)->value.uint16; + } else { + ch_endpt->ka.max_probes = 0; + } + } + +cleanup: + if (is_ch(node) && equal_parent_name(node, 2, "tcp-client-parameters")) { + /* UNLOCK */ + nc_ch_client_unlock(ch_client); + } + return ret; +} + +static int +nc_server_config_create_host_key(const struct lyd_node *node, struct nc_server_ssh_opts *opts) +{ + node = lyd_child(node); + assert(!strcmp(LYD_NAME(node), "name")); + + return nc_server_config_realloc(lyd_get_value(node), (void **)&opts->hostkeys, sizeof *opts->hostkeys, &opts->hostkey_count); +} + +/* list */ +static int +nc_server_config_host_key(const struct lyd_node *node, NC_OPERATION op) +{ + int ret = 0; + struct nc_hostkey *hostkey; + struct nc_server_ssh_opts *opts; + struct nc_ch_client *ch_client; + + assert(!strcmp(LYD_NAME(node), "host-key")); + + if (nc_server_config_get_ssh_opts(node, &opts)) { + ret = 1; + goto cleanup; + } + + if (equal_parent_name(node, 1, "server-identity")) { + /* LOCK */ + if (is_ch(node) && nc_server_config_get_ch_client_with_lock(node, &ch_client)) { + /* to avoid unlock on fail */ + return 1; + } + + if (op == NC_OP_CREATE) { + ret = nc_server_config_create_host_key(node, opts); + if (ret) { + goto cleanup; + } + } else if (op == NC_OP_DELETE) { + if (nc_server_config_get_hostkey(node, &hostkey)) { + ret = 1; + goto cleanup; + } + nc_server_config_del_hostkey(opts, hostkey); + } + } + +cleanup: + if (is_ch(node) && equal_parent_name(node, 1, "server-identity")) { + /* UNLOCK */ + nc_ch_client_unlock(ch_client); + } + return ret; +} + +/* mandatory leaf */ +static int +nc_server_config_public_key_format(const struct lyd_node *node, NC_OPERATION op) +{ + int ret = 0; + const char *format; + NC_PUBKEY_FORMAT pubkey_type; + struct nc_public_key *pubkey; + struct nc_hostkey *hostkey; + struct nc_server_tls_opts *opts; + struct nc_ch_client *ch_client; + + assert(!strcmp(LYD_NAME(node), "public-key-format")); + + format = ((struct lyd_node_term *)node)->value.ident->name; + if (!strcmp(format, "ssh-public-key-format")) { + pubkey_type = NC_PUBKEY_FORMAT_SSH; + } else if (!strcmp(format, "subject-public-key-info-format")) { + pubkey_type = NC_PUBKEY_FORMAT_X509; + } else { + ERR(NULL, "Public key format (%s) not supported.", format); + ret = 1; + goto cleanup; + } + + /* LOCK */ + if (is_ch(node) && nc_server_config_get_ch_client_with_lock(node, &ch_client)) { + /* to avoid unlock on fail */ + return 1; + } + + if (is_ssh(node) && equal_parent_name(node, 4, "server-identity")) { + /* SSH hostkey public key fmt */ + if (nc_server_config_get_hostkey(node, &hostkey)) { + ret = 1; + goto cleanup; + } + + if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) { + hostkey->key.pubkey_type = pubkey_type; + } + } else if (is_ssh(node) && equal_parent_name(node, 6, "client-authentication")) { + /* SSH client auth public key fmt */ + if (nc_server_config_get_pubkey(node, &pubkey)) { + ret = 1; + goto cleanup; + } + + if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) { + pubkey->type = pubkey_type; + } + } else if (is_tls(node) && equal_parent_name(node, 3, "server-identity")) { + /* TLS server-identity */ + if (nc_server_config_get_tls_opts(node, &opts)) { + ret = 1; + goto cleanup; + } + + if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) { + opts->pubkey_type = pubkey_type; + } + } + +cleanup: + if (is_ch(node)) { + /* UNLOCK */ + nc_ch_client_unlock(ch_client); + } + return ret; +} + +static int +nc_server_config_create_auth_key_public_key_list(const struct lyd_node *node, struct nc_auth_client *auth_client) +{ + assert(!strcmp(LYD_NAME(node), "public-key")); + + node = lyd_child(node); + assert(!strcmp(LYD_NAME(node), "name")); + + return nc_server_config_realloc(lyd_get_value(node), (void **)&auth_client->pubkeys, sizeof *auth_client->pubkeys, &auth_client->pubkey_count); +} + +static int +nc_server_config_replace_auth_key_public_key_leaf(const struct lyd_node *node, struct nc_public_key *pubkey) +{ + free(pubkey->data); + pubkey->data = strdup(lyd_get_value(node)); + NC_CHECK_ERRMEM_RET(!pubkey->data, 1); + + return 0; +} + +static int +nc_server_config_replace_host_key_public_key(const struct lyd_node *node, struct nc_hostkey *hostkey) +{ + free(hostkey->key.pubkey_data); + hostkey->key.pubkey_data = strdup(lyd_get_value(node)); + NC_CHECK_ERRMEM_RET(!hostkey->key.pubkey_data, 1); + + return 0; +} + +static int +nc_server_config_tls_replace_server_public_key(const struct lyd_node *node, struct nc_server_tls_opts *opts) +{ + free(opts->pubkey_data); + opts->pubkey_data = strdup(lyd_get_value(node)); + NC_CHECK_ERRMEM_RET(!opts->pubkey_data, 1); + + return 0; +} + +static int +nc_server_config_public_key(const struct lyd_node *node, NC_OPERATION op) +{ + int ret = 0; + struct nc_hostkey *hostkey; + struct nc_auth_client *auth_client; + struct nc_public_key *pubkey; + struct nc_server_tls_opts *opts; + struct nc_ch_client *ch_client; + + assert(!strcmp(LYD_NAME(node), "public-key")); + + /* LOCK */ + if (is_ch(node) && nc_server_config_get_ch_client_with_lock(node, &ch_client)) { + /* to avoid unlock on fail */ + return 1; + } + + if (is_ssh(node) && equal_parent_name(node, 3, "host-key")) { + /* server's public-key, mandatory leaf */ + if (nc_server_config_get_hostkey(node, &hostkey)) { + ret = 1; + goto cleanup; + } + + /* the public key must not be SubjectPublicKeyInfoFormat, as per the ietf-netconf-server model */ + if (nc_is_pk_subject_public_key_info(lyd_get_value(node))) { + ERR(NULL, "Using Public Key in the SubjectPublicKeyInfo format as an SSH hostkey is forbidden!"); + ret = 1; + goto cleanup; + } + + if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) { + /* set to local */ + hostkey->store = NC_STORE_LOCAL; + + ret = nc_server_config_replace_host_key_public_key(node, hostkey); + if (ret) { + goto cleanup; + } + } + } else if (is_ssh(node) && equal_parent_name(node, 5, "client-authentication")) { + /* client auth pubkeys, list */ + if (nc_server_config_get_auth_client(node, &auth_client)) { + ret = 1; + goto cleanup; + } + + if (op == NC_OP_CREATE) { + /* set to local */ + auth_client->store = NC_STORE_LOCAL; + + ret = nc_server_config_create_auth_key_public_key_list(node, auth_client); + if (ret) { + goto cleanup; + } + } else if (op == NC_OP_DELETE) { + if (nc_server_config_get_pubkey(node, &pubkey)) { + ret = 1; + goto cleanup; + } + + nc_server_config_del_auth_client_pubkey(auth_client, pubkey); + } + } else if (is_ssh(node) && equal_parent_name(node, 6, "client-authentication")) { + /* client auth pubkey, leaf */ + if (nc_server_config_get_pubkey(node, &pubkey)) { + ret = 1; + goto cleanup; + } + + /* the public key must not be SubjectPublicKeyInfoFormat, as per the ietf-netconf-server model */ + if (nc_is_pk_subject_public_key_info(lyd_get_value(node))) { + ERR(NULL, "Using Public Key in the SubjectPublicKeyInfo format as an SSH user's key is forbidden!"); + ret = 1; + goto cleanup; + } + + if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) { + ret = nc_server_config_replace_auth_key_public_key_leaf(node, pubkey); + if (ret) { + goto cleanup; + } + } else if (op == NC_OP_DELETE) { + free(pubkey->data); + pubkey->data = NULL; + } + } else if (is_tls(node) && equal_parent_name(node, 3, "server-identity")) { + /* TLS server-identity */ + if (nc_server_config_get_tls_opts(node, &opts)) { + ret = 1; + goto cleanup; + } + + /* the public key must be SubjectPublicKeyInfoFormat, as per the ietf-netconf-server model */ + if (!nc_is_pk_subject_public_key_info(lyd_get_value(node))) { + ERR(NULL, "TLS server certificate's Public Key must be in the SubjectPublicKeyInfo format!"); + ret = 1; + goto cleanup; + } + + if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) { + /* set to local */ + opts->store = NC_STORE_LOCAL; + + ret = nc_server_config_tls_replace_server_public_key(node, opts); + if (ret) { + goto cleanup; + } + } + } + +cleanup: + if (is_ch(node)) { + /* UNLOCK */ + nc_ch_client_unlock(ch_client); + } + return ret; +} + +/* leaf */ +static int +nc_server_config_private_key_format(const struct lyd_node *node, NC_OPERATION op) +{ + int ret = 0; + const char *format; + NC_PRIVKEY_FORMAT privkey_type; + struct nc_hostkey *hostkey; + struct nc_server_tls_opts *opts; + struct nc_ch_client *ch_client; + + (void) op; + + assert(!strcmp(LYD_NAME(node), "private-key-format")); + + format = ((struct lyd_node_term *)node)->value.ident->name; + if (!format) { + ret = 1; + goto cleanup; + } + + privkey_type = nc_server_config_get_private_key_type(format); + if (privkey_type == NC_PRIVKEY_FORMAT_UNKNOWN) { + ERR(NULL, "Unknown private key format."); + ret = 1; + goto cleanup; + } + + /* LOCK */ + if (is_ch(node) && nc_server_config_get_ch_client_with_lock(node, &ch_client)) { + /* to avoid unlock on fail */ + return 1; + } + + if (is_ssh(node)) { + /* ssh */ + if (nc_server_config_get_hostkey(node, &hostkey)) { + ret = 1; + goto cleanup; + } + + hostkey->key.privkey_type = privkey_type; + } else if (is_tls(node)) { + /* tls */ + if (nc_server_config_get_tls_opts(node, &opts)) { + ret = 1; + goto cleanup; + } + + opts->privkey_type = privkey_type; + } + +cleanup: + if (is_ch(node)) { + /* UNLOCK */ + nc_ch_client_unlock(ch_client); + } + return ret; +} + +static int +nc_server_config_cleartext_private_key(const struct lyd_node *node, NC_OPERATION op) +{ + int ret = 0; + struct nc_hostkey *hostkey; + struct nc_server_tls_opts *opts; + struct nc_ch_client *ch_client; + + assert(!strcmp(LYD_NAME(node), "cleartext-private-key")); + + /* LOCK */ + if (is_ch(node) && nc_server_config_get_ch_client_with_lock(node, &ch_client)) { + /* to avoid unlock on fail */ + return 1; + } + + if (is_ssh(node)) { + /* ssh */ + if (nc_server_config_get_hostkey(node, &hostkey)) { + ret = 1; + goto cleanup; + } + + if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) { + free(hostkey->key.privkey_data); + hostkey->key.privkey_data = strdup(lyd_get_value(node)); + NC_CHECK_ERRMEM_GOTO(!hostkey->key.privkey_data, ret = 1, cleanup); + } else { + free(hostkey->key.privkey_data); + hostkey->key.privkey_data = NULL; + } + } else if (is_tls(node)) { + /* tls */ + if (nc_server_config_get_tls_opts(node, &opts)) { + ret = 1; + goto cleanup; + } + + if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) { + free(opts->privkey_data); + opts->privkey_data = strdup(lyd_get_value(node)); + NC_CHECK_ERRMEM_GOTO(!opts->privkey_data, ret = 1, cleanup); + } else { + free(opts->privkey_data); + opts->privkey_data = NULL; + } + } + +cleanup: + if (is_ch(node)) { + /* UNLOCK */ + nc_ch_client_unlock(ch_client); + } + return ret; +} + +/* leaf */ +static int +nc_server_config_keystore_reference(const struct lyd_node *node, NC_OPERATION op) +{ + int ret = 0; + struct nc_hostkey *hostkey; + struct nc_ch_client *ch_client; + + assert(!strcmp(LYD_NAME(node), "keystore-reference")); + + /* LOCK */ + if (is_ch(node) && nc_server_config_get_ch_client_with_lock(node, &ch_client)) { + /* to avoid unlock on fail */ + return 1; + } + + if (is_ssh(node) && equal_parent_name(node, 3, "server-identity")) { + if (nc_server_config_get_hostkey(node, &hostkey)) { + ret = 1; + goto cleanup; + } + + if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) { + /* set to keystore */ + hostkey->store = NC_STORE_KEYSTORE; + + free(hostkey->ks_ref); + hostkey->ks_ref = strdup(lyd_get_value(node)); + NC_CHECK_ERRMEM_GOTO(!hostkey->ks_ref, ret = 1, cleanup); + } else if (op == NC_OP_DELETE) { + free(hostkey->ks_ref); + hostkey->ks_ref = NULL; + } + } + +cleanup: + if (is_ch(node)) { + /* UNLOCK */ + nc_ch_client_unlock(ch_client); + } + return ret; +} + +static int +nc_server_config_create_auth_client(const struct lyd_node *node, struct nc_server_ssh_opts *opts) +{ + node = lyd_child(node); + assert(!strcmp(LYD_NAME(node), "name")); + + return nc_server_config_realloc(lyd_get_value(node), (void **)&opts->auth_clients, sizeof *opts->auth_clients, &opts->client_count); +} + +/* list */ +static int +nc_server_config_user(const struct lyd_node *node, NC_OPERATION op) +{ + int ret = 0; + struct nc_auth_client *auth_client; + struct nc_server_ssh_opts *opts; + struct nc_ch_client *ch_client; + + assert(!strcmp(LYD_NAME(node), "user")); + + /* LOCK */ + if (is_ch(node) && nc_server_config_get_ch_client_with_lock(node, &ch_client)) { + /* to avoid unlock on fail */ + return 1; + } + + if (nc_server_config_get_ssh_opts(node, &opts)) { + ret = 1; + goto cleanup; + } + + if (op == NC_OP_CREATE) { + ret = nc_server_config_create_auth_client(node, opts); + if (ret) { + goto cleanup; + } + } else if (op == NC_OP_DELETE) { + if (nc_server_config_get_auth_client(node, &auth_client)) { + ret = 1; + goto cleanup; + } + + nc_server_config_del_auth_client(opts, auth_client); + } + +cleanup: + if (is_ch(node)) { + /* UNLOCK */ + nc_ch_client_unlock(ch_client); + } + return ret; +} + +static int +nc_server_config_auth_attempts(const struct lyd_node *node, NC_OPERATION op) +{ + int ret = 0; + struct nc_server_ssh_opts *opts; + struct nc_ch_client *ch_client; + + assert(!strcmp(LYD_NAME(node), "auth-attempts")); + + /* LOCK */ + if (is_ch(node) && nc_server_config_get_ch_client_with_lock(node, &ch_client)) { + /* to avoid unlock on fail */ + return 1; + } + + if (nc_server_config_get_ssh_opts(node, &opts)) { + ret = 1; + goto cleanup; + } + + if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) { + opts->auth_attempts = ((struct lyd_node_term *)node)->value.uint16; + } + +cleanup: + if (is_ch(node)) { + /* UNLOCK */ + nc_ch_client_unlock(ch_client); + } + return ret; +} + +static int +nc_server_config_auth_timeout(const struct lyd_node *node, NC_OPERATION op) +{ + int ret = 0; + struct nc_server_ssh_opts *opts; + struct nc_ch_client *ch_client; + + assert(!strcmp(LYD_NAME(node), "auth-timeout")); + + /* LOCK */ + if (is_ch(node) && nc_server_config_get_ch_client_with_lock(node, &ch_client)) { + /* to avoid unlock on fail */ + return 1; + } + + if (nc_server_config_get_ssh_opts(node, &opts)) { + ret = 1; + goto cleanup; + } + + if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) { + opts->auth_timeout = ((struct lyd_node_term *)node)->value.uint16; + } + +cleanup: + if (is_ch(node)) { + /* UNLOCK */ + nc_ch_client_unlock(ch_client); + } + return ret; +} + +/* leaf */ +static int +nc_server_config_truststore_reference(const struct lyd_node *node, NC_OPERATION op) +{ + int ret = 0; + struct nc_auth_client *auth_client; + struct nc_ch_client *ch_client; + struct nc_server_tls_opts *opts; + struct nc_cert_grouping *certs_grp; + + assert(!strcmp(LYD_NAME(node), "truststore-reference")); + + /* LOCK */ + if (is_ch(node) && nc_server_config_get_ch_client_with_lock(node, &ch_client)) { + /* to avoid unlock on fail */ + return 1; + } + + if (is_ssh(node) && equal_parent_name(node, 1, "public-keys")) { + if (nc_server_config_get_auth_client(node, &auth_client)) { + ret = 1; + goto cleanup; + } + + if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) { + /* set to truststore */ + auth_client->store = NC_STORE_TRUSTSTORE; + + free(auth_client->ts_ref); + auth_client->ts_ref = strdup(lyd_get_value(node)); + NC_CHECK_ERRMEM_GOTO(!auth_client->ts_ref, ret = 1, cleanup); + } else if (op == NC_OP_DELETE) { + free(auth_client->ts_ref); + auth_client->ts_ref = NULL; + } + } else if (is_tls(node) && (equal_parent_name(node, 1, "ca-certs") || equal_parent_name(node, 1, "ee-certs"))) { + /* ee-certs or ca-certs */ + if (nc_server_config_get_tls_opts(node, &opts)) { + ret = 1; + goto cleanup; + } + + if (equal_parent_name(node, 1, "ca-certs")) { + certs_grp = &opts->ca_certs; + } else { + certs_grp = &opts->ee_certs; + } + + if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) { + /* set to truststore */ + certs_grp->store = NC_STORE_TRUSTSTORE; + + free(certs_grp->ts_ref); + certs_grp->ts_ref = strdup(lyd_get_value(node)); + NC_CHECK_ERRMEM_GOTO(!certs_grp->ts_ref, ret = 1, cleanup); + } else if (op == NC_OP_DELETE) { + free(certs_grp->ts_ref); + certs_grp->ts_ref = NULL; + } + } + +cleanup: + if (is_ch(node)) { + /* UNLOCK */ + nc_ch_client_unlock(ch_client); + } + return ret; +} + +/* leaf */ +static int +nc_server_config_password(const struct lyd_node *node, NC_OPERATION op) +{ + int ret = 0; + struct nc_auth_client *auth_client; + struct nc_ch_client *ch_client; + + assert(!strcmp(LYD_NAME(node), "password")); + + /* LOCK */ + if (is_ch(node) && nc_server_config_get_ch_client_with_lock(node, &ch_client)) { + /* to avoid unlock on fail */ + return 1; + } + + if (nc_server_config_get_auth_client(node, &auth_client)) { + ret = 1; + goto cleanup; + } + + if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) { + free(auth_client->password); + auth_client->password = strdup(lyd_get_value(node)); + NC_CHECK_ERRMEM_GOTO(!auth_client->password, ret = 1, cleanup); + } else { + free(auth_client->password); + auth_client->password = NULL; + } + +cleanup: + if (is_ch(node)) { + /* UNLOCK */ + nc_ch_client_unlock(ch_client); + } + return ret; +} + +static int +nc_server_config_pam_name(const struct lyd_node *node, NC_OPERATION op) +{ + int ret = 0; + struct nc_auth_client *auth_client; + struct nc_ch_client *ch_client; + + assert(!strcmp(LYD_NAME(node), "pam-config-file-name")); + + /* LOCK */ + if (is_ch(node) && nc_server_config_get_ch_client_with_lock(node, &ch_client)) { + /* to avoid unlock on fail */ + return 1; + } + + if (nc_server_config_get_auth_client(node, &auth_client)) { + ret = 1; + goto cleanup; + } + + if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) { + free(auth_client->pam_config_name); + auth_client->pam_config_name = strdup(lyd_get_value(node)); + NC_CHECK_ERRMEM_GOTO(!auth_client->pam_config_name, ret = 1, cleanup); + } else { + free(auth_client->pam_config_name); + auth_client->pam_config_name = NULL; + } + +cleanup: + if (is_ch(node)) { + /* UNLOCK */ + nc_ch_client_unlock(ch_client); + } + return ret; +} + +static int +nc_server_config_pam_dir(const struct lyd_node *node, NC_OPERATION op) +{ + int ret = 0; + struct nc_auth_client *auth_client; + struct nc_ch_client *ch_client; + + assert(!strcmp(LYD_NAME(node), "pam-config-file-dir")); + + /* LOCK */ + if (is_ch(node) && nc_server_config_get_ch_client_with_lock(node, &ch_client)) { + /* to avoid unlock on fail */ + return 1; + } + + if (nc_server_config_get_auth_client(node, &auth_client)) { + ret = 1; + goto cleanup; + } + + if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) { + free(auth_client->pam_config_dir); + auth_client->pam_config_dir = strdup(lyd_get_value(node)); + NC_CHECK_ERRMEM_GOTO(!auth_client->pam_config_dir, ret = 1, cleanup); + } else { + free(auth_client->pam_config_dir); + auth_client->pam_config_dir = NULL; + } + +cleanup: + if (is_ch(node)) { + /* UNLOCK */ + nc_ch_client_unlock(ch_client); + } + return ret; +} + +/* leaf */ +static int +nc_server_config_none(const struct lyd_node *node, NC_OPERATION op) +{ + int ret = 0; + struct nc_auth_client *auth_client; + struct nc_ch_client *ch_client; + + assert(!strcmp(LYD_NAME(node), "none")); + + /* LOCK */ + if (is_ch(node) && nc_server_config_get_ch_client_with_lock(node, &ch_client)) { + /* to avoid unlock on fail */ + return 1; + } + + if (nc_server_config_get_auth_client(node, &auth_client)) { + ret = 1; + goto cleanup; + } + + if (op == NC_OP_CREATE) { + auth_client->supports_none = 1; + } else { + auth_client->supports_none = 0; + } + +cleanup: + if (is_ch(node)) { + /* UNLOCK */ + nc_ch_client_unlock(ch_client); + } + return ret; +} + +static int +nc_server_config_delete_substring(const char *haystack, const char *needle, const char delim) +{ + size_t needle_len = strlen(needle); + char *substr; + int substr_found = 0, ret = 0; + + while ((substr = strstr(haystack, needle))) { + /* iterate over all the substrings */ + if (((substr == haystack) && (*(substr + needle_len) == delim)) || + ((substr != haystack) && (*(substr - 1) == delim) && (*(substr + needle_len) == delim))) { + /* either the first element of the string or somewhere in the middle */ + memmove(substr, substr + needle_len + 1, strlen(substr + needle_len + 1)); + substr_found = 1; + break; + } else if ((*(substr - 1) == delim) && (*(substr + needle_len) == '\0')) { + /* the last element of the string */ + *(substr - 1) = '\0'; + substr_found = 1; + break; + } + haystack = substr + 1; + } + if (!substr_found) { + ret = 1; + } + + return ret; +} + +static int +nc_server_config_transport_params(const char *algorithm, char **alg_store, NC_OPERATION op) +{ + int ret = 0; + char *alg = NULL; + + if (!strncmp(algorithm, "openssh-", 8)) { + /* if the name starts with openssh, convert it to it's original libssh accepted form */ + ret = asprintf(&alg, "%s@openssh.com", algorithm + 8); + NC_CHECK_ERRMEM_GOTO(ret == -1, ret = 1; alg = NULL, cleanup); + } else if (!strncmp(algorithm, "libssh-", 7)) { + /* if the name starts with libssh, convert it to it's original libssh accepted form */ + ret = asprintf(&alg, "%s@libssh.org", algorithm + 7); + NC_CHECK_ERRMEM_GOTO(ret == -1, ret = 1; alg = NULL, cleanup); + } else { + alg = strdup(algorithm); + NC_CHECK_ERRMEM_GOTO(!alg, ret = 1, cleanup); + } + + if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) { + if (!*alg_store) { + /* first call */ + *alg_store = strdup(alg); + NC_CHECK_ERRMEM_GOTO(!*alg_store, ret = 1, cleanup); + } else { + /* +1 because of ',' between algorithms */ + *alg_store = nc_realloc(*alg_store, strlen(*alg_store) + strlen(alg) + 1 + 1); + NC_CHECK_ERRMEM_GOTO(!*alg_store, ret = 1, cleanup); + strcat(*alg_store, ","); + strcat(*alg_store, alg); + } + } else { + /* delete */ + ret = nc_server_config_delete_substring(*alg_store, alg, ','); + if (ret) { + ERR(NULL, "Unable to delete an algorithm (%s), which was not previously added.", alg); + goto cleanup; + } + } + +cleanup: + free(alg); + return ret; +} + +/* leaf-list */ +static int +nc_server_config_host_key_alg(const struct lyd_node *node, NC_OPERATION op) +{ + int ret = 0; + const char *alg; + uint8_t i; + struct nc_server_ssh_opts *opts; + struct nc_ch_client *ch_client; + + assert(!strcmp(LYD_NAME(node), "host-key-alg")); + + /* LOCK */ + if (is_ch(node) && nc_server_config_get_ch_client_with_lock(node, &ch_client)) { + /* to avoid unlock on fail */ + return 1; + } + + if (nc_server_config_get_ssh_opts(node, &opts)) { + ret = 1; + goto cleanup; + } + + /* get the algorithm name and compare it with algs supported by libssh */ + alg = ((struct lyd_node_term *)node)->value.ident->name; + i = 0; + while (supported_hostkey_algs[i]) { + if (!strcmp(supported_hostkey_algs[i], alg)) { + if (nc_server_config_transport_params(alg, &opts->hostkey_algs, op)) { + ret = 1; + goto cleanup; + } + break; + } + i++; + } + if (!supported_hostkey_algs[i]) { + /* algorithm not supported */ + ERR(NULL, "Public key algorithm (%s) not supported by libssh.", alg); + ret = 1; + } + +cleanup: + if (is_ch(node)) { + /* UNLOCK */ + nc_ch_client_unlock(ch_client); + } + return ret; +} + +/* leaf-list */ +static int +nc_server_config_kex_alg(const struct lyd_node *node, NC_OPERATION op) +{ + int ret = 0; + const char *alg; + uint8_t i; + struct nc_server_ssh_opts *opts; + struct nc_ch_client *ch_client; + + assert(!strcmp(LYD_NAME(node), "key-exchange-alg")); + + /* LOCK */ + if (is_ch(node) && nc_server_config_get_ch_client_with_lock(node, &ch_client)) { + /* to avoid unlock on fail */ + return 1; + } + + if (nc_server_config_get_ssh_opts(node, &opts)) { + ret = 1; + goto cleanup; + } + + /* get the algorithm name and compare it with algs supported by libssh */ + alg = ((struct lyd_node_term *)node)->value.ident->name; + i = 0; + while (supported_kex_algs[i]) { + if (!strcmp(supported_kex_algs[i], alg)) { + if (nc_server_config_transport_params(alg, &opts->kex_algs, op)) { + ret = 1; + goto cleanup; + } + break; + } + i++; + } + if (!supported_kex_algs[i]) { + /* algorithm not supported */ + ERR(NULL, "Key exchange algorithm (%s) not supported by libssh.", alg); + ret = 1; + } + +cleanup: + if (is_ch(node)) { + /* UNLOCK */ + nc_ch_client_unlock(ch_client); + } + return ret; +} + +/* leaf-list */ +static int +nc_server_config_encryption_alg(const struct lyd_node *node, NC_OPERATION op) +{ + int ret = 0; + const char *alg; + uint8_t i; + struct nc_server_ssh_opts *opts; + struct nc_ch_client *ch_client; + + assert(!strcmp(LYD_NAME(node), "encryption-alg")); + + /* LOCK */ + if (is_ch(node) && nc_server_config_get_ch_client_with_lock(node, &ch_client)) { + /* to avoid unlock on fail */ + return 1; + } + + if (nc_server_config_get_ssh_opts(node, &opts)) { + ret = 1; + goto cleanup; + } + + /* get the algorithm name and compare it with algs supported by libssh */ + alg = ((struct lyd_node_term *)node)->value.ident->name; + i = 0; + while (supported_encryption_algs[i]) { + if (!strcmp(supported_encryption_algs[i], alg)) { + if (nc_server_config_transport_params(alg, &opts->encryption_algs, op)) { + ret = 1; + goto cleanup; + } + break; + } + i++; + } + if (!supported_encryption_algs[i]) { + /* algorithm not supported */ + ERR(NULL, "Encryption algorithm (%s) not supported by libssh.", alg); + ret = 1; + } + +cleanup: + if (is_ch(node)) { + /* UNLOCK */ + nc_ch_client_unlock(ch_client); + } + return ret; +} + +/* leaf-list */ +static int +nc_server_config_mac_alg(const struct lyd_node *node, NC_OPERATION op) +{ + int ret = 0; + const char *alg; + uint8_t i; + struct nc_server_ssh_opts *opts; + struct nc_ch_client *ch_client; + + assert(!strcmp(LYD_NAME(node), "mac-alg")); + + /* LOCK */ + if (is_ch(node) && nc_server_config_get_ch_client_with_lock(node, &ch_client)) { + /* to avoid unlock on fail */ + return 1; + } + + if (nc_server_config_get_ssh_opts(node, &opts)) { + ret = 1; + goto cleanup; + } + + /* get the algorithm name and compare it with algs supported by libssh */ + alg = ((struct lyd_node_term *)node)->value.ident->name; + i = 0; + while (supported_mac_algs[i]) { + if (!strcmp(supported_mac_algs[i], alg)) { + if (nc_server_config_transport_params(alg, &opts->mac_algs, op)) { + ret = 1; + goto cleanup; + } + break; + } + i++; + } + if (!supported_mac_algs[i]) { + /* algorithm not supported */ + ERR(NULL, "MAC algorithm (%s) not supported by libssh.", alg); + ret = 1; + } + +cleanup: + if (is_ch(node)) { + /* UNLOCK */ + nc_ch_client_unlock(ch_client); + } + return ret; +} + +#endif /* NC_ENABLED_SSH_TLS */ + +static int +nc_server_config_create_unix_socket(struct nc_endpt *endpt) +{ + endpt->ti = NC_TI_UNIX; + endpt->opts.unixsock = calloc(1, sizeof *endpt->opts.unixsock); + NC_CHECK_ERRMEM_RET(!endpt->opts.unixsock, 1); + + /* set default values */ + endpt->opts.unixsock->mode = -1; + endpt->opts.unixsock->uid = -1; + endpt->opts.unixsock->gid = -1; + + return 0; +} + +static int +nc_server_config_unix_socket(const struct lyd_node *node, NC_OPERATION op) +{ + int ret = 0; + uint32_t log_options = 0; + struct nc_endpt *endpt; + struct nc_bind *bind; + struct nc_server_unix_opts *opts; + struct lyd_node *data = NULL; + + assert(!strcmp(LYD_NAME(node), "unix-socket")); + + if (nc_server_config_get_endpt(node, &endpt, &bind)) { + ret = 1; + goto cleanup; + } + + if (op == NC_OP_CREATE) { + if (nc_server_config_create_unix_socket(endpt)) { + ret = 1; + goto cleanup; + } + + opts = endpt->opts.unixsock; + + lyd_find_path(node, "path", 0, &data); + assert(data); + + opts->address = strdup(lyd_get_value(data)); + bind->address = strdup(lyd_get_value(data)); + NC_CHECK_ERRMEM_GOTO(!opts->address || !bind->address, ret = 1, cleanup); + + /* silently search for non-mandatory parameters */ + ly_temp_log_options(&log_options); + ret = lyd_find_path(node, "mode", 0, &data); + if (!ret) { + opts->mode = strtol(lyd_get_value(data), NULL, 8); + } + + ret = lyd_find_path(node, "uid", 0, &data); + if (!ret) { + opts->uid = strtol(lyd_get_value(data), NULL, 10); + } + + ret = lyd_find_path(node, "gid", 0, &data); + if (!ret) { + opts->gid = strtol(lyd_get_value(data), NULL, 10); + } + + /* reset the logging options */ + ly_temp_log_options(NULL); + + ret = nc_server_config_set_address_port(endpt, bind, NULL, 0); + if (ret) { + goto cleanup; + } + } else if (op == NC_OP_DELETE) { + nc_server_config_del_unix_socket_opts(bind, endpt->opts.unixsock); + } + +cleanup: + return ret; +} + +#ifdef NC_ENABLED_SSH_TLS + +static int +nc_server_config_check_endpt_reference_cycle(struct nc_endpt *original, struct nc_endpt *next) +{ + if (!next->referenced_endpt_name) { + /* no further reference -> no cycle */ + return 0; + } + + if (!strcmp(original->name, next->referenced_endpt_name)) { + /* found cycle */ + return 1; + } else { + if (nc_server_get_referenced_endpt(next->referenced_endpt_name, &next)) { + /* referenced endpoint does not exist */ + return 1; + } + + /* continue further */ + return nc_server_config_check_endpt_reference_cycle(original, next); + } +} + +/** + * @brief Set all endpoint references. + * + * @return 0 on success, 1 on error. + */ +static int +nc_server_config_check_endpt_references(void) +{ + uint16_t i, j; + struct nc_endpt *referenced_endpt = NULL; + + /* first do listen endpoints */ + for (i = 0; i < server_opts.endpt_count; i++) { + /* go through all the endpoints */ + if (server_opts.endpts[i].referenced_endpt_name) { + /* get referenced endpt */ + if (nc_server_get_referenced_endpt(server_opts.endpts[i].referenced_endpt_name, &referenced_endpt)) { + ERR(NULL, "Endpoint \"%s\" referenced by endpoint \"%s\" does not exist.", + server_opts.endpts[i].referenced_endpt_name, server_opts.endpts[i].name); + return 1; + } + + /* check if the endpoint references itself */ + if (&server_opts.endpts[i] == referenced_endpt) { + ERR(NULL, "Endpoint \"%s\" references itself.", server_opts.endpts[i].name); + return 1; + } + + /* check transport */ + if ((server_opts.endpts[i].ti != referenced_endpt->ti)) { + 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)) { + 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; + } + + /* check cyclic reference */ + if (nc_server_config_check_endpt_reference_cycle(&server_opts.endpts[i], referenced_endpt)) { + ERR(NULL, "Endpoint \"%s\" referenced by endpoint \"%s\" creates a cycle.", + server_opts.endpts[i].referenced_endpt_name, server_opts.endpts[i].name); + return 1; + } + + /* 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) { + 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; + } + } + } + + /* now check all the call home endpoints */ + /* LOCK */ + pthread_rwlock_rdlock(&server_opts.ch_client_lock); + for (i = 0; i < server_opts.ch_client_count; i++) { + /* LOCK */ + pthread_mutex_lock(&server_opts.ch_clients[i].lock); + for (j = 0; j < server_opts.ch_clients[i].ch_endpt_count; j++) { + if (server_opts.ch_clients[i].ch_endpts[j].referenced_endpt_name) { + /* get referenced endpt */ + if (nc_server_get_referenced_endpt(server_opts.ch_clients[i].ch_endpts[j].referenced_endpt_name, &referenced_endpt)) { + ERR(NULL, "Endpoint \"%s\" referenced by call home endpoint \"%s\" does not exist.", + server_opts.ch_clients[i].ch_endpts[j].referenced_endpt_name, server_opts.ch_clients[i].ch_endpts[j].name); + goto ch_fail; + } + + /* check transport */ + if (server_opts.ch_clients[i].ch_endpts[j].ti != referenced_endpt->ti) { + 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)) { + 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; + } + + /* check cyclic reference */ + if (nc_server_config_check_endpt_reference_cycle(referenced_endpt, referenced_endpt)) { + ERR(NULL, "Endpoint \"%s\" referenced by call home endpoint \"%s\" creates a cycle.", + server_opts.ch_clients[i].ch_endpts[j].referenced_endpt_name, server_opts.ch_clients[i].ch_endpts[j].name); + goto ch_fail; + } + + /* 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) { + 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; + } + } + } + /* UNLOCK */ + pthread_mutex_unlock(&server_opts.ch_clients[i].lock); + } + + /* UNLOCK */ + pthread_rwlock_unlock(&server_opts.ch_client_lock); + return 0; + +ch_fail: + /* UNLOCK */ + pthread_mutex_unlock(&server_opts.ch_clients[i].lock); + /* UNLOCK */ + pthread_rwlock_unlock(&server_opts.ch_client_lock); + return 1; +} + +static int +nc_server_config_endpoint_reference(const struct lyd_node *node, NC_OPERATION op) +{ + int ret = 0; + struct nc_endpt *endpt = NULL; + struct nc_ch_client *ch_client; + struct nc_ch_endpt *ch_endpt = NULL; + struct nc_server_ssh_opts *ssh = NULL; + struct nc_server_tls_opts *tls = NULL; + + assert(!strcmp(LYD_NAME(node), "endpoint-reference")); + + if (is_ch(node) && nc_server_config_get_ch_client_with_lock(node, &ch_client)) { + /* to avoid unlock on fail */ + return 1; + } + + /* get endpt */ + if (is_listen(node)) { + ret = nc_server_config_get_endpt(node, &endpt, NULL); + } else { + ret = nc_server_config_get_ch_endpt(node, &ch_endpt); + } + if (ret) { + goto cleanup; + } + + if (op == NC_OP_DELETE) { + if (endpt) { + free(endpt->referenced_endpt_name); + endpt->referenced_endpt_name = NULL; + } else { + free(ch_endpt->referenced_endpt_name); + ch_endpt->referenced_endpt_name = NULL; + } + if (is_ssh(node)) { + if (nc_server_config_get_ssh_opts(node, &ssh)) { + ret = 1; + goto cleanup; + } + + ssh->referenced_endpt_name = NULL; + } else { + if (nc_server_config_get_tls_opts(node, &tls)) { + ret = 1; + goto cleanup; + } + + tls->referenced_endpt_name = NULL; + } + + goto cleanup; + } else { + /* just set the name, check it once configuring of all nodes is done */ + if (endpt) { + free(endpt->referenced_endpt_name); + endpt->referenced_endpt_name = strdup(lyd_get_value(node)); + NC_CHECK_ERRMEM_GOTO(!endpt->referenced_endpt_name, ret = 1, cleanup); + } else { + free(ch_endpt->referenced_endpt_name); + ch_endpt->referenced_endpt_name = strdup(lyd_get_value(node)); + NC_CHECK_ERRMEM_GOTO(!ch_endpt->referenced_endpt_name, ret = 1, cleanup); + } + + goto cleanup; + } + +cleanup: + if (is_ch(node)) { + /* UNLOCK */ + nc_ch_client_unlock(ch_client); + } + + return ret; +} + +static int +nc_server_config_cert_data(const struct lyd_node *node, NC_OPERATION op) +{ + int ret = 0; + struct nc_certificate *cert; + struct nc_server_tls_opts *opts; + struct nc_ch_client *ch_client; + + assert(!strcmp(LYD_NAME(node), "cert-data")); + + /* LOCK */ + if (is_ch(node) && nc_server_config_get_ch_client_with_lock(node, &ch_client)) { + /* to avoid unlock on fail */ + return 1; + } + + if (equal_parent_name(node, 3, "server-identity")) { + if (nc_server_config_get_tls_opts(node, &opts)) { + ret = 1; + goto cleanup; + } + + if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) { + free(opts->cert_data); + opts->cert_data = strdup(lyd_get_value(node)); + NC_CHECK_ERRMEM_GOTO(!opts->cert_data, ret = 1, cleanup); + } + } else if (equal_parent_name(node, 3, "ca-certs") || equal_parent_name(node, 3, "ee-certs")) { + if (nc_server_config_get_cert(node, &cert)) { + ret = 1; + goto cleanup; + } + + if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) { + free(cert->data); + cert->data = strdup(lyd_get_value(node)); + NC_CHECK_ERRMEM_GOTO(!cert->data, ret = 1, cleanup); + } else { + free(cert->data); + cert->data = NULL; + } + } + +cleanup: + if (is_ch(node)) { + /* UNLOCK */ + nc_ch_client_unlock(ch_client); + } + return ret; +} + +static int +nc_server_config_asymmetric_key(const struct lyd_node *node, NC_OPERATION op) +{ + int ret = 0; + struct nc_server_tls_opts *opts; + struct nc_ch_client *ch_client; + + assert(!strcmp(LYD_NAME(node), "asymmetric-key")); + + /* LOCK */ + if (is_ch(node) && nc_server_config_get_ch_client_with_lock(node, &ch_client)) { + /* to avoid unlock on fail */ + return 1; + } + + if (nc_server_config_get_tls_opts(node, &opts)) { + ret = 1; + goto cleanup; + } + + if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) { + /* set to keystore */ + opts->store = NC_STORE_KEYSTORE; + + free(opts->key_ref); + opts->key_ref = strdup(lyd_get_value(node)); + NC_CHECK_ERRMEM_GOTO(!opts->key_ref, ret = 1, cleanup); + } else { + free(opts->key_ref); + opts->key_ref = NULL; + } + +cleanup: + if (is_ch(node)) { + /* UNLOCK */ + nc_ch_client_unlock(ch_client); + } + return ret; +} + +static int +nc_server_config_create_ca_certs_certificate(const struct lyd_node *node, struct nc_server_tls_opts *opts) +{ + assert(!strcmp(LYD_NAME(node), "certificate")); + + node = lyd_child(node); + assert(!strcmp(LYD_NAME(node), "name")); + + return nc_server_config_realloc(lyd_get_value(node), (void **)&opts->ca_certs.certs, sizeof *opts->ca_certs.certs, &opts->ca_certs.cert_count); +} + +static int +nc_server_config_create_ee_certs_certificate(const struct lyd_node *node, struct nc_server_tls_opts *opts) +{ + assert(!strcmp(LYD_NAME(node), "certificate")); + + node = lyd_child(node); + assert(!strcmp(LYD_NAME(node), "name")); + + return nc_server_config_realloc(lyd_get_value(node), (void **)&opts->ee_certs.certs, sizeof *opts->ee_certs.certs, &opts->ee_certs.cert_count); +} + +static int +nc_server_config_certificate(const struct lyd_node *node, NC_OPERATION op) +{ + int ret = 0; + struct nc_server_tls_opts *opts; + struct nc_ch_client *ch_client; + struct nc_certificate *cert; + + assert(!strcmp(LYD_NAME(node), "certificate")); + + /* LOCK */ + if (is_ch(node) && nc_server_config_get_ch_client_with_lock(node, &ch_client)) { + /* to avoid unlock on fail */ + return 1; + } + + if (nc_server_config_get_tls_opts(node, &opts)) { + ret = 1; + goto cleanup; + } + + if (equal_parent_name(node, 1, "keystore-reference")) { + /* TLS server-identity */ + if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) { + /* set to keystore */ + opts->store = NC_STORE_KEYSTORE; + + free(opts->cert_ref); + opts->cert_ref = strdup(lyd_get_value(node)); + NC_CHECK_ERRMEM_GOTO(!opts->cert_ref, ret = 1, cleanup); + } else { + free(opts->cert_ref); + opts->cert_ref = NULL; + } + } else if (equal_parent_name(node, 2, "ca-certs")) { + /* TLS client auth certificate authority */ + if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) { + ret = nc_server_config_create_ca_certs_certificate(node, opts); + if (ret) { + goto cleanup; + } + } else { + if (nc_server_config_get_cert(node, &cert)) { + ret = 1; + goto cleanup; + } + nc_server_config_del_cert(&opts->ca_certs, cert); + } + } else if (equal_parent_name(node, 2, "ee-certs")) { + /* TLS client auth end entity */ + if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) { + ret = nc_server_config_create_ee_certs_certificate(node, opts); + if (ret) { + goto cleanup; + } + } else { + if (nc_server_config_get_cert(node, &cert)) { + ret = 1; + goto cleanup; + } + nc_server_config_del_cert(&opts->ee_certs, cert); + } + } + +cleanup: + if (is_ch(node)) { + /* UNLOCK */ + nc_ch_client_unlock(ch_client); + } + return ret; +} + +static int +nc_server_config_create_cert_to_name(const struct lyd_node *node, struct nc_server_tls_opts *opts) +{ + int ret = 0; + struct lyd_node *n; + struct nc_ctn *new, *iter; + const char *map_type, *name; + uint32_t id; + NC_TLS_CTN_MAPTYPE m_type; + + assert(!strcmp(LYD_NAME(node), "cert-to-name")); + + /* get all the data */ + /* find the list's key */ + lyd_find_path(node, "id", 0, &n); + assert(n); + id = ((struct lyd_node_term *)n)->value.uint32; + + /* find the ctn's name */ + lyd_find_path(node, "name", 0, &n); + assert(n); + name = lyd_get_value(n); + + /* find the ctn's map-type */ + lyd_find_path(node, "map-type", 0, &n); + assert(n); + map_type = ((struct lyd_node_term *)n)->value.ident->name; + if (!strcmp(map_type, "specified")) { + m_type = NC_TLS_CTN_SPECIFIED; + } else if (!strcmp(map_type, "san-rfc822-name")) { + m_type = NC_TLS_CTN_SAN_RFC822_NAME; + } else if (!strcmp(map_type, "san-dns-name")) { + m_type = NC_TLS_CTN_SAN_DNS_NAME; + } else if (!strcmp(map_type, "san-ip-address")) { + m_type = NC_TLS_CTN_SAN_IP_ADDRESS; + } else if (!strcmp(map_type, "san-any")) { + m_type = NC_TLS_CTN_SAN_ANY; + } else if (!strcmp(map_type, "common-name")) { + m_type = NC_TLS_CTN_COMMON_NAME; + } else { + ERR(NULL, "Map-type identity \"%s\" not supported.", map_type); + ret = 1; + goto cleanup; + } + + /* create new ctn */ + new = calloc(1, sizeof *new); + NC_CHECK_ERRMEM_GOTO(!new, ret = 1, cleanup); + + /* find the right place for insertion */ + if (!opts->ctn) { + /* inserting the first one */ + opts->ctn = new; + } else if (opts->ctn->id > id) { + /* insert at the beginning */ + new->next = opts->ctn; + opts->ctn = new; + } else { + /* have to find the right place */ + for (iter = opts->ctn; iter->next && iter->next->id <= id; iter = iter->next) {} + if (iter->id == id) { + /* collision */ + new = iter; + } else { + new->next = iter->next; + iter->next = new; + } + } + + /* insert the right data */ + new->id = id; + free(new->name); + new->name = strdup(name); + NC_CHECK_ERRMEM_GOTO(!new->name, ret = 1, cleanup); + new->map_type = m_type; + +cleanup: + return ret; +} + +static int +nc_server_config_cert_to_name(const struct lyd_node *node, NC_OPERATION op) +{ + int ret = 0; + struct nc_server_tls_opts *opts; + struct nc_ctn *ctn; + struct nc_ch_client *ch_client; + + assert(!strcmp(LYD_NAME(node), "cert-to-name")); + + /* LOCK */ + if (is_ch(node) && nc_server_config_get_ch_client_with_lock(node, &ch_client)) { + /* to avoid unlock on fail */ + return 1; + } + + if (nc_server_config_get_tls_opts(node, &opts)) { + ret = 1; + goto cleanup; + } + + if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) { + ret = nc_server_config_create_cert_to_name(node, opts); + if (ret) { + goto cleanup; + } + } else { + /* find the given ctn entry */ + if (nc_server_config_get_ctn(node, &ctn)) { + ret = 1; + goto cleanup; + } + nc_server_config_del_ctn(opts, ctn); + } + +cleanup: + if (is_ch(node)) { + /* UNLOCK */ + nc_ch_client_unlock(ch_client); + } + return ret; +} + +static int +nc_server_config_fingerprint(const struct lyd_node *node, NC_OPERATION op) +{ + int ret = 0; + struct nc_ctn *ctn; + struct nc_ch_client *ch_client; + + assert(!strcmp(LYD_NAME(node), "fingerprint")); + + /* LOCK */ + if (is_ch(node) && nc_server_config_get_ch_client_with_lock(node, &ch_client)) { + /* to avoid unlock on fail */ + return 1; + } + + if (nc_server_config_get_ctn(node, &ctn)) { + ret = 1; + goto cleanup; + } + + if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) { + free(ctn->fingerprint); + ctn->fingerprint = strdup(lyd_get_value(node)); + NC_CHECK_ERRMEM_GOTO(!ctn->fingerprint, ret = 1, cleanup); + } else { + free(ctn->fingerprint); + ctn->fingerprint = NULL; + } + +cleanup: + if (is_ch(node)) { + /* UNLOCK */ + nc_ch_client_unlock(ch_client); + } + return ret; +} + +static int +nc_server_config_tls_version(const struct lyd_node *node, NC_OPERATION op) +{ + int ret = 0; + struct nc_server_tls_opts *opts; + const char *version = NULL; + struct nc_ch_client *ch_client; + NC_TLS_VERSION tls_version; + + assert(!strcmp(LYD_NAME(node), "tls-version")); + + /* LOCK */ + if (is_ch(node) && nc_server_config_get_ch_client_with_lock(node, &ch_client)) { + /* to avoid unlock on fail */ + return 1; + } + + if (nc_server_config_get_tls_opts(node, &opts)) { + ret = 1; + goto cleanup; + } + + /* str to tls_version */ + version = ((struct lyd_node_term *)node)->value.ident->name; + if (!strcmp(version, "tls10")) { + tls_version = NC_TLS_VERSION_10; + } else if (!strcmp(version, "tls11")) { + tls_version = NC_TLS_VERSION_11; + } else if (!strcmp(version, "tls12")) { + tls_version = NC_TLS_VERSION_12; + } else if (!strcmp(version, "tls13")) { + tls_version = NC_TLS_VERSION_13; + } else { + ERR(NULL, "TLS version \"%s\" not supported.", version); + ret = 1; + goto cleanup; + } + + if (op == NC_OP_CREATE) { + /* add the version if it isn't there already */ + opts->tls_versions |= tls_version; + } else if ((op == NC_OP_DELETE) && (opts->tls_versions & tls_version)) { + /* delete the version if it is there */ + opts->tls_versions &= ~tls_version; + } + +cleanup: + if (is_ch(node)) { + /* UNLOCK */ + nc_ch_client_unlock(ch_client); + } + return ret; +} + +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); + + 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]); + } + } + 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); + } + +cleanup: + free(ssl_cipher); + return ret; +} + +static int +nc_server_config_del_cipher_suite(struct nc_server_tls_opts *opts, const char *cipher) +{ + int ret = 0; + + ret = nc_server_config_delete_substring(opts->ciphers, cipher, ':'); + if (ret) { + ERR(NULL, "Unable to delete a cipher (%s), which was not previously added.", cipher); + return 1; + } + + return 0; +} + +static int +nc_server_config_cipher_suite(const struct lyd_node *node, NC_OPERATION op) +{ + int ret = 0; + struct nc_server_tls_opts *opts; + const char *cipher = NULL; + struct nc_ch_client *ch_client; + + assert(!strcmp(LYD_NAME(node), "cipher-suite")); + + /* LOCK */ + if (is_ch(node) && nc_server_config_get_ch_client_with_lock(node, &ch_client)) { + /* to avoid unlock on fail */ + return 1; + } + + if (nc_server_config_get_tls_opts(node, &opts)) { + ret = 1; + goto cleanup; + } + + cipher = ((struct lyd_node_term *)node)->value.ident->name; + if (op == NC_OP_CREATE) { + ret = nc_server_config_create_cipher_suite(opts, cipher); + if (ret) { + goto cleanup; + } + } else if (op == NC_OP_DELETE) { + ret = nc_server_config_del_cipher_suite(opts, cipher); + if (ret) { + goto cleanup; + } + } + +cleanup: + if (is_ch(node)) { + /* UNLOCK */ + nc_ch_client_unlock(ch_client); + } + return ret; +} + +static int +nc_server_config_crl_url(const struct lyd_node *node, NC_OPERATION op) +{ + int ret = 0; + struct nc_server_tls_opts *opts; + struct nc_ch_client *ch_client; + + assert(!strcmp(LYD_NAME(node), "crl-url")); + + /* LOCK */ + if (is_ch(node) && nc_server_config_get_ch_client_with_lock(node, &ch_client)) { + /* to avoid unlock on fail */ + return 1; + } + + if (nc_server_config_get_tls_opts(node, &opts)) { + ret = 1; + goto cleanup; + } + + if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) { + free(opts->crl_url); + opts->crl_url = strdup(lyd_get_value(node)); + NC_CHECK_ERRMEM_GOTO(!opts->crl_url, ret = 1, cleanup); + } else if (op == NC_OP_DELETE) { + free(opts->crl_url); + opts->crl_url = NULL; + } + +cleanup: + if (is_ch(node)) { + /* UNLOCK */ + nc_ch_client_unlock(ch_client); + } + return ret; +} + +static int +nc_server_config_crl_path(const struct lyd_node *node, NC_OPERATION op) +{ + int ret = 0; + struct nc_server_tls_opts *opts; + struct nc_ch_client *ch_client; + + assert(!strcmp(LYD_NAME(node), "crl-path")); + + /* LOCK */ + if (is_ch(node) && nc_server_config_get_ch_client_with_lock(node, &ch_client)) { + /* to avoid unlock on fail */ + return 1; + } + + if (nc_server_config_get_tls_opts(node, &opts)) { + ret = 1; + goto cleanup; + } + + if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) { + free(opts->crl_path); + opts->crl_path = strdup(lyd_get_value(node)); + NC_CHECK_ERRMEM_GOTO(!opts->crl_path, ret = 1, cleanup); + } else if (op == NC_OP_DELETE) { + free(opts->crl_path); + opts->crl_path = NULL; + } + +cleanup: + if (is_ch(node)) { + /* UNLOCK */ + nc_ch_client_unlock(ch_client); + } + return ret; +} + +static int +nc_server_config_crl_cert_ext(const struct lyd_node *node, NC_OPERATION op) +{ + int ret = 0; + struct nc_server_tls_opts *opts; + struct nc_ch_client *ch_client; + + assert(!strcmp(LYD_NAME(node), "crl-cert-ext")); + + /* LOCK */ + if (is_ch(node) && nc_server_config_get_ch_client_with_lock(node, &ch_client)) { + /* to avoid unlock on fail */ + return 1; + } + + if (nc_server_config_get_tls_opts(node, &opts)) { + ret = 1; + goto cleanup; + } + + if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) { + opts->crl_cert_ext = 1; + } else if (op == NC_OP_DELETE) { + opts->crl_cert_ext = 0; + } + +cleanup: + if (is_ch(node)) { + /* UNLOCK */ + nc_ch_client_unlock(ch_client); + } + return ret; +} + +#endif /* NC_ENABLED_SSH_TLS */ + +static int +nc_server_config_create_netconf_client(const struct lyd_node *node) +{ + int ret = 0; + + node = lyd_child(node); + assert(!strcmp(LYD_NAME(node), "name")); + + /* LOCK */ + pthread_rwlock_wrlock(&server_opts.ch_client_lock); + + ret = nc_server_config_realloc(lyd_get_value(node), (void **)&server_opts.ch_clients, sizeof *server_opts.ch_clients, &server_opts.ch_client_count); + if (ret) { + goto cleanup; + } + + server_opts.ch_clients[server_opts.ch_client_count - 1].id = ATOMIC_INC_RELAXED(server_opts.new_client_id); + server_opts.ch_clients[server_opts.ch_client_count - 1].start_with = NC_CH_FIRST_LISTED; + server_opts.ch_clients[server_opts.ch_client_count - 1].max_attempts = 3; + + pthread_mutex_init(&server_opts.ch_clients[server_opts.ch_client_count - 1].lock, NULL); + +cleanup: + /* UNLOCK */ + pthread_rwlock_unlock(&server_opts.ch_client_lock); + return ret; +} + +static int +nc_server_config_netconf_client(const struct lyd_node *node, NC_OPERATION op) +{ + int ret = 0; + struct nc_ch_client *ch_client; + + assert(!strcmp(LYD_NAME(node), "netconf-client")); + + if (op == NC_OP_CREATE) { + ret = nc_server_config_create_netconf_client(node); + if (ret) { + goto cleanup; + } + +#ifdef NC_ENABLED_SSH_TLS + if (server_opts.ch_dispatch_data.acquire_ctx_cb && server_opts.ch_dispatch_data.release_ctx_cb && + server_opts.ch_dispatch_data.new_session_cb) { + /* we have all we need for dispatching a new call home thread */ + ret = nc_connect_ch_client_dispatch(lyd_get_value(lyd_child(node)), server_opts.ch_dispatch_data.acquire_ctx_cb, + server_opts.ch_dispatch_data.release_ctx_cb, server_opts.ch_dispatch_data.ctx_cb_data, + server_opts.ch_dispatch_data.new_session_cb, server_opts.ch_dispatch_data.new_session_cb_data); + if (ret) { + ERR(NULL, "Dispatching a new Call Home thread failed for Call Home client \"%s\".", lyd_get_value(lyd_child(node))); + goto cleanup; + } + } +#endif /* NC_ENABLED_SSH_TLS */ + } else if (op == NC_OP_DELETE) { + if (nc_server_config_get_ch_client(node, &ch_client)) { + ret = 1; + goto cleanup; + } + + nc_server_config_ch_del_client(ch_client); + } + +cleanup: + return ret; +} + +#ifdef NC_ENABLED_SSH_TLS + +static int +nc_server_config_remote_address(const struct lyd_node *node, NC_OPERATION op) +{ + int ret = 0; + struct nc_ch_endpt *ch_endpt; + struct nc_ch_client *ch_client; + + assert(!strcmp(LYD_NAME(node), "remote-address")); + + /* LOCK */ + if (nc_server_config_get_ch_client_with_lock(node, &ch_client)) { + /* to avoid unlock on fail */ + return 1; + } + + if (nc_server_config_get_ch_endpt(node, &ch_endpt)) { + ret = 1; + goto cleanup; + } + + if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) { + free(ch_endpt->address); + ch_endpt->address = strdup(lyd_get_value(node)); + NC_CHECK_ERRMEM_GOTO(!ch_endpt->address, ret = 1, cleanup); + } else { + free(ch_endpt->address); + ch_endpt->address = NULL; + } + +cleanup: + /* UNLOCK */ + nc_ch_client_unlock(ch_client); + return ret; +} + +static int +nc_server_config_remote_port(const struct lyd_node *node, NC_OPERATION op) +{ + int ret = 0; + struct nc_ch_endpt *ch_endpt; + struct nc_ch_client *ch_client; + + assert(!strcmp(LYD_NAME(node), "remote-port")); + + /* LOCK */ + if (nc_server_config_get_ch_client_with_lock(node, &ch_client)) { + /* to avoid unlock on fail */ + return 1; + } + + if (nc_server_config_get_ch_endpt(node, &ch_endpt)) { + ret = 1; + goto cleanup; + } + + if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) { + ch_endpt->port = ((struct lyd_node_term *)node)->value.uint16; + } else { + ch_endpt->port = 0; + } + +cleanup: + /* UNLOCK */ + nc_ch_client_unlock(ch_client); + return ret; +} + +#endif /* NC_ENABLED_SSH_TLS */ + +static int +nc_server_config_persistent(const struct lyd_node *node, NC_OPERATION op) +{ + int ret = 0; + struct nc_ch_client *ch_client; + + assert(!strcmp(LYD_NAME(node), "persistent")); + + (void) op; + + /* LOCK */ + if (nc_server_config_get_ch_client_with_lock(node, &ch_client)) { + /* to avoid unlock on fail */ + return 1; + } + + ch_client->conn_type = NC_CH_PERSIST; + + /* UNLOCK */ + nc_ch_client_unlock(ch_client); + + return ret; +} + +static int +nc_server_config_periodic(const struct lyd_node *node, NC_OPERATION op) +{ + int ret = 0; + struct nc_ch_client *ch_client; + + assert(!strcmp(LYD_NAME(node), "periodic")); + + (void) op; + + /* LOCK */ + if (nc_server_config_get_ch_client_with_lock(node, &ch_client)) { + /* to avoid unlock on fail */ + return 1; + } + + ch_client->conn_type = NC_CH_PERIOD; + /* set default values */ + ch_client->period = 60; + ch_client->anchor_time = 0; + ch_client->idle_timeout = 180; + + /* UNLOCK */ + nc_ch_client_unlock(ch_client); + + return ret; +} + +static int +nc_server_config_period(const struct lyd_node *node, NC_OPERATION op) +{ + int ret = 0; + struct nc_ch_client *ch_client; + + assert(!strcmp(LYD_NAME(node), "period")); + + /* LOCK */ + if (nc_server_config_get_ch_client_with_lock(node, &ch_client)) { + /* to avoid unlock on fail */ + return 1; + } + + if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) { + ch_client->period = ((struct lyd_node_term *)node)->value.uint16; + } else if (op == NC_OP_DELETE) { + ch_client->period = 60; + } + + /* UNLOCK */ + nc_ch_client_unlock(ch_client); + + return ret; +} + +static int +nc_server_config_anchor_time(const struct lyd_node *node, NC_OPERATION op) +{ + int ret = 0; + struct nc_ch_client *ch_client; + struct lyd_value_date_and_time *anchor_time; + + assert(!strcmp(LYD_NAME(node), "anchor-time")); + + /* LOCK */ + if (nc_server_config_get_ch_client_with_lock(node, &ch_client)) { + /* to avoid unlock on fail */ + return 1; + } + + /* get the value of time from the node directly */ + LYD_VALUE_GET(&((struct lyd_node_term *)node)->value, anchor_time); + + if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) { + ch_client->anchor_time = anchor_time->time; + } else if (op == NC_OP_DELETE) { + ch_client->anchor_time = 0; + } + + /* UNLOCK */ + nc_ch_client_unlock(ch_client); + return ret; +} + +static int +nc_server_config_reconnect_strategy(const struct lyd_node *node, NC_OPERATION op) +{ + int ret = 0; + struct nc_ch_client *ch_client; + + assert(!strcmp(LYD_NAME(node), "reconnect-strategy")); + + (void) op; + + /* LOCK */ + if (nc_server_config_get_ch_client_with_lock(node, &ch_client)) { + /* to avoid unlock on fail */ + return 1; + } + + /* set to default values */ + ch_client->start_with = NC_CH_FIRST_LISTED; + ch_client->max_wait = 5; + ch_client->max_attempts = 3; + + /* UNLOCK */ + nc_ch_client_unlock(ch_client); + + return ret; +} + +static int +nc_server_config_start_with(const struct lyd_node *node, NC_OPERATION op) +{ + int ret = 0; + struct nc_ch_client *ch_client; + const char *value; + + assert(!strcmp(LYD_NAME(node), "start-with")); + + /* LOCK */ + if (nc_server_config_get_ch_client_with_lock(node, &ch_client)) { + /* to avoid unlock on fail */ + return 1; + } + + if (op == NC_OP_DELETE) { + ch_client->start_with = NC_CH_FIRST_LISTED; + goto cleanup; + } + + value = lyd_get_value(node); + if (!strcmp(value, "first-listed")) { + ch_client->start_with = NC_CH_FIRST_LISTED; + } else if (!strcmp(value, "last-connected")) { + ch_client->start_with = NC_CH_LAST_CONNECTED; + } else if (!strcmp(value, "random-selection")) { + ch_client->start_with = NC_CH_RANDOM; + } else { + ERR(NULL, "Unexpected start-with value \"%s\".", value); + ret = 1; + goto cleanup; + } + +cleanup: + /* UNLOCK */ + nc_ch_client_unlock(ch_client); + return ret; +} + +static int +nc_server_config_max_wait(const struct lyd_node *node, NC_OPERATION op) +{ + int ret = 0; + struct nc_ch_client *ch_client; + + assert(!strcmp(LYD_NAME(node), "max-wait")); + + /* LOCK */ + if (nc_server_config_get_ch_client_with_lock(node, &ch_client)) { + /* to avoid unlock on fail */ + return 1; + } + + if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) { + ch_client->max_wait = ((struct lyd_node_term *)node)->value.uint16; + } else { + ch_client->max_wait = 5; + } + + /* UNLOCK */ + nc_ch_client_unlock(ch_client); + + return ret; +} + +static int +nc_server_config_max_attempts(const struct lyd_node *node, NC_OPERATION op) +{ + int ret = 0; + struct nc_ch_client *ch_client; + + assert(!strcmp(LYD_NAME(node), "max-attempts")); + + /* LOCK */ + if (nc_server_config_get_ch_client_with_lock(node, &ch_client)) { + /* to avoid unlock on fail */ + return 1; + } + + if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) { + ch_client->max_attempts = ((struct lyd_node_term *)node)->value.uint8; + } else { + ch_client->max_attempts = 3; + } + + /* UNLOCK */ + nc_ch_client_unlock(ch_client); + + return ret; +} + +static int +nc_server_config_parse_netconf_server(const struct lyd_node *node, NC_OPERATION op) +{ + const char *name = LYD_NAME(node); + int ret = 0; + + if (!strcmp(name, "listen")) { + ret = nc_server_config_listen(node, op); + } else if (!strcmp(name, "call-home")) { + ret = nc_server_config_ch(node, op); + } else if (!strcmp(name, "hello-timeout")) { + ret = nc_server_config_hello_timeout(node, op); + } else if (!strcmp(name, "endpoint")) { + ret = nc_server_config_endpoint(node, op); + } else if (!strcmp(name, "unix-socket")) { + ret = nc_server_config_unix_socket(node, op); + } +#ifdef NC_ENABLED_SSH_TLS + else if (!strcmp(name, "ssh")) { + ret = nc_server_config_ssh(node, op); + } else if (!strcmp(name, "local-address")) { + ret = nc_server_config_local_address(node, op); + } else if (!strcmp(name, "local-port")) { + ret = nc_server_config_local_port(node, op); + } else if (!strcmp(name, "keepalives")) { + ret = nc_server_config_keepalives(node, op); + } else if (!strcmp(name, "idle-time")) { + ret = nc_server_config_idle_time(node, op); + } else if (!strcmp(name, "max-probes")) { + ret = nc_server_config_max_probes(node, op); + } else if (!strcmp(name, "probe-interval")) { + ret = nc_server_config_probe_interval(node, op); + } else if (!strcmp(name, "host-key")) { + ret = nc_server_config_host_key(node, op); + } else if (!strcmp(name, "public-key-format")) { + ret = nc_server_config_public_key_format(node, op); + } else if (!strcmp(name, "public-key")) { + ret = nc_server_config_public_key(node, op); + } else if (!strcmp(name, "private-key-format")) { + ret = nc_server_config_private_key_format(node, op); + } else if (!strcmp(name, "cleartext-private-key")) { + ret = nc_server_config_cleartext_private_key(node, op); + } else if (!strcmp(name, "keystore-reference")) { + ret = nc_server_config_keystore_reference(node, op); + } else if (!strcmp(name, "user")) { + ret = nc_server_config_user(node, op); + } else if (!strcmp(name, "auth-attempts")) { + ret = nc_server_config_auth_attempts(node, op); + } else if (!strcmp(name, "auth-timeout")) { + ret = nc_server_config_auth_timeout(node, op); + } else if (!strcmp(name, "truststore-reference")) { + ret = nc_server_config_truststore_reference(node, op); + } else if (!strcmp(name, "password")) { + ret = nc_server_config_password(node, op); + } else if (!strcmp(name, "pam-config-file-name")) { + ret = nc_server_config_pam_name(node, op); + } else if (!strcmp(name, "pam-config-file-dir")) { + ret = nc_server_config_pam_dir(node, op); + } else if (!strcmp(name, "none")) { + ret = nc_server_config_none(node, op); + } else if (!strcmp(name, "host-key-alg")) { + ret = nc_server_config_host_key_alg(node, op); + } else if (!strcmp(name, "key-exchange-alg")) { + ret = nc_server_config_kex_alg(node, op); + } else if (!strcmp(name, "encryption-alg")) { + ret = nc_server_config_encryption_alg(node, op); + } else if (!strcmp(name, "mac-alg")) { + ret = nc_server_config_mac_alg(node, op); + } else if (!strcmp(name, "endpoint-reference")) { + ret = nc_server_config_endpoint_reference(node, op); + } else if (!strcmp(name, "tls")) { + ret = nc_server_config_tls(node, op); + } else if (!strcmp(name, "cert-data")) { + ret = nc_server_config_cert_data(node, op); + } else if (!strcmp(name, "asymmetric-key")) { + ret = nc_server_config_asymmetric_key(node, op); + } else if (!strcmp(name, "certificate")) { + ret = nc_server_config_certificate(node, op); + } else if (!strcmp(name, "cert-to-name")) { + ret = nc_server_config_cert_to_name(node, op); + } else if (!strcmp(name, "fingerprint")) { + ret = nc_server_config_fingerprint(node, op); + } else if (!strcmp(name, "tls-version")) { + ret = nc_server_config_tls_version(node, op); + } else if (!strcmp(name, "cipher-suite")) { + ret = nc_server_config_cipher_suite(node, op); + } else if (!strcmp(name, "crl-url")) { + ret = nc_server_config_crl_url(node, op); + } else if (!strcmp(name, "crl-path")) { + ret = nc_server_config_crl_path(node, op); + } else if (!strcmp(name, "crl-cert-ext")) { + ret = nc_server_config_crl_cert_ext(node, op); + } +#endif /* NC_ENABLED_SSH_TLS */ + else if (!strcmp(name, "netconf-client")) { + ret = nc_server_config_netconf_client(node, op); + } +#ifdef NC_ENABLED_SSH_TLS + else if (!strcmp(name, "remote-address")) { + ret = nc_server_config_remote_address(node, op); + } else if (!strcmp(name, "remote-port")) { + ret = nc_server_config_remote_port(node, op); + } +#endif /* NC_ENABLED_SSH_TLS */ + else if (!strcmp(name, "persistent")) { + ret = nc_server_config_persistent(node, op); + } else if (!strcmp(name, "periodic")) { + ret = nc_server_config_periodic(node, op); + } else if (!strcmp(name, "period")) { + ret = nc_server_config_period(node, op); + } else if (!strcmp(name, "anchor-time")) { + ret = nc_server_config_anchor_time(node, op); + } else if (!strcmp(name, "idle-timeout")) { + ret = nc_server_config_idle_timeout(node, op); + } else if (!strcmp(name, "reconnect-strategy")) { + ret = nc_server_config_reconnect_strategy(node, op); + } else if (!strcmp(name, "start-with")) { + ret = nc_server_config_start_with(node, op); + } else if (!strcmp(name, "max-wait")) { + ret = nc_server_config_max_wait(node, op); + } else if (!strcmp(name, "max-attempts")) { + ret = nc_server_config_max_attempts(node, op); + } + + if (ret) { + ERR(NULL, "Configuring node \"%s\" failed.", LYD_NAME(node)); + return 1; + } + + return 0; +} + +int +nc_server_config_parse_tree(const struct lyd_node *node, NC_OPERATION parent_op, NC_MODULE module) +{ + struct lyd_node *child; + struct lyd_meta *m; + NC_OPERATION current_op = NC_OP_UNKNOWN; + int ret; + + assert(node); + + /* get current op if there is any */ + if ((m = lyd_find_meta(node->meta, NULL, "yang:operation"))) { + if (!strcmp(lyd_get_meta_value(m), "create")) { + current_op = NC_OP_CREATE; + } else if (!strcmp(lyd_get_meta_value(m), "delete")) { + current_op = NC_OP_DELETE; + } else if (!strcmp(lyd_get_meta_value(m), "replace")) { + current_op = NC_OP_REPLACE; + } else if (!strcmp(lyd_get_meta_value(m), "none")) { + current_op = NC_OP_NONE; + } + } + + /* node has no op, inherit from the parent */ + if (!current_op) { + if (!parent_op) { + ERR(NULL, "Unknown operation for node \"%s\".", LYD_NAME(node)); + return 1; + } + + current_op = parent_op; + } + + switch (current_op) { + case NC_OP_NONE: + break; + case NC_OP_CREATE: + case NC_OP_DELETE: + case NC_OP_REPLACE: +#ifdef NC_ENABLED_SSH_TLS + if (module == NC_MODULE_KEYSTORE) { + ret = nc_server_config_parse_keystore(node, current_op); + } else if (module == NC_MODULE_TRUSTSTORE) { + ret = nc_server_config_parse_truststore(node, current_op); + } else +#endif /* NC_ENABLED_SSH_TLS */ + { + ret = nc_server_config_parse_netconf_server(node, current_op); + } + if (ret) { + return ret; + } + break; + default: + break; + } + + if (current_op != NC_OP_DELETE) { + LY_LIST_FOR(lyd_child(node), child) { + if (nc_server_config_parse_tree(child, current_op, module)) { + return 1; + } + } + } + return 0; +} + +API int +nc_server_config_load_modules(struct ly_ctx **ctx) +{ + int i, new_ctx = 0; + + if (!*ctx) { + if (ly_ctx_new(NC_SERVER_SEARCH_DIR, 0, ctx)) { + ERR(NULL, "Couldn't create new libyang context.\n"); + goto error; + } + new_ctx = 1; + } + + /* all features */ + const char *ietf_nectonf_server[] = {"ssh-listen", "tls-listen", "ssh-call-home", "tls-call-home", "central-netconf-server-supported", NULL}; + /* all features */ + const char *ietf_x509_cert_to_name[] = {NULL}; + /* no private-key-encryption, csr-generation, p10-csr-format, certificate-expiration-notification, + * encrypted-passwords, hidden-symmetric-keys, encrypted-symmetric-keys, hidden-private-keys, encrypted-private-keys, + * one-symmetric-key-format, one-asymmetric-key-format, symmetrically-encrypted-value-format, + * asymmetrically-encrypted-value-format, cms-enveloped-data-format, cms-encrypted-data-format, + * cleartext-symmetric-keys */ + const char *ietf_crypto_types[] = {"cleartext-passwords", "cleartext-private-keys", NULL}; + /* all features */ + const char *ietf_tcp_common[] = {"keepalives-supported", NULL}; + /* all features */ + const char *ietf_tcp_server[] = {"tcp-server-keepalives", NULL}; + /* no proxy-connect, socks5-gss-api, socks5-username-password, local-binding-supported ? */ + const char *ietf_tcp_client[] = {"tcp-client-keepalives", NULL}; + /* no ssh-x509-certs, public-key-generation */ + const char *ietf_ssh_common[] = {"transport-params", NULL}; + /* no ssh-server-keepalives and local-user-auth-hostbased */ + const char *ietf_ssh_server[] = {"local-users-supported", "local-user-auth-publickey", "local-user-auth-password", "local-user-auth-none", NULL}; + /* all features */ + const char *iana_ssh_encryption_algs[] = {NULL}; + /* all features */ + const char *iana_ssh_key_exchange_algs[] = {NULL}; + /* all features */ + const char *iana_ssh_mac_algs[] = {NULL}; + /* all features */ + const char *iana_ssh_public_key_algs[] = {NULL}; + /* all features */ + const char *iana_crypt_hash[] = {"crypt-hash-md5", "crypt-hash-sha-256", "crypt-hash-sha-512", NULL}; + /* no symmetric-keys */ + const char *ietf_keystore[] = {"central-keystore-supported", "inline-definitions-supported", "asymmetric-keys", NULL}; + /* all features */ + const char *ietf_truststore[] = {"central-truststore-supported", "inline-definitions-supported", "certificates", "public-keys", NULL}; + /* no public-key-generation */ + const char *ietf_tls_common[] = {"tls10", "tls11", "tls12", "tls13", "hello-params", NULL}; + /* no tls-server-keepalives, server-ident-raw-public-key, server-ident-tls12-psk, server-ident-tls13-epsk, + * client-auth-raw-public-key, client-auth-tls12-psk, client-auth-tls13-epsk */ + const char *ietf_tls_server[] = {"server-ident-x509-cert", "client-auth-supported", "client-auth-x509-cert", NULL}; + /* all features */ + const char *iana_tls_cipher_suite_algs[] = {NULL}; + /* all features */ + const char *libnetconf2_netconf_server[] = {NULL}; + + const char *module_names[] = { + "ietf-netconf-server", "ietf-x509-cert-to-name", "ietf-crypto-types", "ietf-tcp-common", "ietf-tcp-server", + "ietf-tcp-client", "ietf-ssh-common", "ietf-ssh-server", "iana-ssh-encryption-algs", + "iana-ssh-key-exchange-algs", "iana-ssh-mac-algs", "iana-ssh-public-key-algs", "iana-crypt-hash", + "ietf-keystore", "ietf-truststore", "ietf-tls-common", "ietf-tls-server", "iana-tls-cipher-suite-algs", + "libnetconf2-netconf-server", NULL + }; + + const char **module_features[] = { + ietf_nectonf_server, ietf_x509_cert_to_name, ietf_crypto_types, ietf_tcp_common, + ietf_tcp_server, ietf_tcp_client, ietf_ssh_common, ietf_ssh_server, iana_ssh_encryption_algs, + iana_ssh_key_exchange_algs, iana_ssh_mac_algs, iana_ssh_public_key_algs, iana_crypt_hash, + ietf_keystore, ietf_truststore, ietf_tls_common, ietf_tls_server, iana_tls_cipher_suite_algs, + libnetconf2_netconf_server, NULL + }; + + for (i = 0; module_names[i] != NULL; i++) { + if (!ly_ctx_load_module(*ctx, module_names[i], NULL, module_features[i])) { + ERR(NULL, "Loading module \"%s\" failed.\n", module_names[i]); + goto error; + } + } + + return 0; + +error: + if (new_ctx) { + ly_ctx_destroy(*ctx); + *ctx = NULL; + } + return 1; +} + +static int +nc_server_config_fill_nectonf_server(const struct lyd_node *data, NC_OPERATION op) +{ + int ret = 0; + uint32_t log_options = 0; + struct lyd_node *tree; + + /* silently search for ietf-netconf-server, it may not be present */ + ly_temp_log_options(&log_options); + + ret = lyd_find_path(data, "/ietf-netconf-server:netconf-server", 0, &tree); + if (ret || (tree->flags & LYD_DEFAULT)) { + /* not found */ + ret = 0; + goto cleanup; + } + + if (nc_server_config_parse_tree(tree, op, NC_MODULE_NETCONF_SERVER)) { + ret = 1; + goto cleanup; + } + +#ifdef NC_ENABLED_SSH_TLS + /* check and set all endpoint references */ + if (nc_server_config_check_endpt_references()) { + ret = 1; + goto cleanup; + } +#endif /* NC_ENABLED_SSH_TLS */ + +cleanup: + /* reset the logging options back to what they were */ + ly_temp_log_options(NULL); + return ret; +} + +API int +nc_server_config_setup_diff(const struct lyd_node *data) +{ + int ret = 0; + + NC_CHECK_ARG_RET(NULL, data, 1); + + /* LOCK */ + pthread_rwlock_wrlock(&server_opts.config_lock); + +#ifdef NC_ENABLED_SSH_TLS + /* configure keystore */ + ret = nc_server_config_fill_keystore(data, NC_OP_UNKNOWN); + if (ret) { + ERR(NULL, "Filling keystore failed."); + goto cleanup; + } + + /* configure truststore */ + ret = nc_server_config_fill_truststore(data, NC_OP_UNKNOWN); + if (ret) { + ERR(NULL, "Filling truststore failed."); + goto cleanup; + } +#endif /* NC_ENABLED_SSH_TLS */ + + /* configure netconf-server */ + ret = nc_server_config_fill_nectonf_server(data, NC_OP_UNKNOWN); + if (ret) { + ERR(NULL, "Filling netconf-server failed."); + goto cleanup; + } + +cleanup: + /* UNLOCK */ + pthread_rwlock_unlock(&server_opts.config_lock); + return ret; +} + +API int +nc_server_config_setup_data(const struct lyd_node *data) +{ + int ret = 0; + struct lyd_node *tree, *iter, *root; + + NC_CHECK_ARG_RET(NULL, data, 1); + + /* LOCK */ + pthread_rwlock_wrlock(&server_opts.config_lock); + + /* find the netconf-server node */ + ret = lyd_find_path(data, "/ietf-netconf-server:netconf-server", 0, &root); + if (ret) { + ERR(NULL, "Unable to find the netconf-server container in the YANG data."); + goto cleanup; + } + + /* iterate through all the nodes and make sure there is no operation attribute */ + LY_LIST_FOR(root, tree) { + LYD_TREE_DFS_BEGIN(tree, iter) { + if (lyd_find_meta(iter->meta, NULL, "yang:operation")) { + ERR(NULL, "Unexpected operation attribute in the YANG data."); + ret = 1; + goto cleanup; + } + LYD_TREE_DFS_END(tree, iter); + } + } + + /* delete the current configuration */ + nc_server_config_listen(NULL, NC_OP_DELETE); + nc_server_config_ch(NULL, NC_OP_DELETE); +#ifdef NC_ENABLED_SSH_TLS + nc_server_config_ks_keystore(NULL, NC_OP_DELETE); + nc_server_config_ts_truststore(NULL, NC_OP_DELETE); + + /* configure keystore */ + ret = nc_server_config_fill_keystore(data, NC_OP_CREATE); + if (ret) { + ERR(NULL, "Filling keystore failed."); + goto cleanup; + } + + /* configure truststore */ + ret = nc_server_config_fill_truststore(data, NC_OP_CREATE); + if (ret) { + ERR(NULL, "Filling truststore failed."); + goto cleanup; + } +#endif /* NC_ENABLED_SSH_TLS */ + + /* configure netconf-server */ + ret = nc_server_config_fill_nectonf_server(data, NC_OP_CREATE); + if (ret) { + ERR(NULL, "Filling netconf-server failed."); + goto cleanup; + } + +cleanup: + /* UNLOCK */ + pthread_rwlock_unlock(&server_opts.config_lock); + return ret; +} + +API int +nc_server_config_setup_path(const struct ly_ctx *ctx, const char *path) +{ + struct lyd_node *tree = NULL; + int ret = 0; + + NC_CHECK_ARG_RET(NULL, path, 1); + + ret = lyd_parse_data_path(ctx, path, LYD_UNKNOWN, LYD_PARSE_NO_STATE | LYD_PARSE_STRICT, LYD_VALIDATE_NO_STATE, &tree); + if (ret) { + goto cleanup; + } + + ret = nc_server_config_setup_data(tree); + if (ret) { + goto cleanup; + } + +cleanup: + lyd_free_all(tree); + return ret; +} diff --git a/src/server_config.h b/src/server_config.h new file mode 100644 index 00000000..f0bc71cd --- /dev/null +++ b/src/server_config.h @@ -0,0 +1,1351 @@ +/** + * @file server_config.h + * @author Roman Janota + * @brief libnetconf2 server configuration + * + * @copyright + * Copyright (c) 2023 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 NC_CONFIG_SERVER_H_ +#define NC_CONFIG_SERVER_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include + +#include + +#include "session.h" + +/** + * @defgroup server_config Server Configuration + * @ingroup server + * + * @brief Server-side configuration creation and application + * @{ + */ + +/** + * @} Server Configuration + */ + +/** + * @defgroup server_config_functions Server Configuration Functions + * @ingroup server_config + * + * @brief Server-side configuration functions + * @{ + */ + +/** + * @brief Implements all the required modules and their features in the context. + * Needs to be called before any other configuration functions. + * + * If ctx is : + * - NULL: a new context will be created and if the call is successful you have to free it, + * - non NULL: modules will simply be implemented. + * + * Implemented modules: ietf-netconf-server, ietf-x509-cert-to-name, ietf-crypto-types, + * ietf-tcp-common, ietf-ssh-common, iana-ssh-encryption-algs, iana-ssh-key-exchange-algs, + * iana-ssh-mac-algs, iana-ssh-public-key-algs, ietf-keystore, ietf-ssh-server, ietf-truststore, + * ietf-tls-server and libnetconf2-netconf-server. + * + * @param[in, out] ctx Optional context in which the modules will be implemented. Created if *ctx is null. + * @return 0 on success, 1 on error. + */ +int nc_server_config_load_modules(struct ly_ctx **ctx); + +/** + * @brief Configure server based on the given diff. + * + * Context must already have implemented the required modules, see ::nc_server_config_load_modules(). + * + * @param[in] diff YANG diff belonging to either ietf-netconf-server, ietf-keystore or ietf-truststore modules. + * The top level node HAS to have an operation (create, replace, delete or none). + * @return 0 on success, 1 on error. + */ +int nc_server_config_setup_diff(const struct lyd_node *diff); + +/** + * @brief Configure server based on the given data. + * + * Behaves as if all the nodes in data had the replace operation. That means that the current configuration will be deleted + * and just the given data will be applied. + * Context must already have implemented the required modules, see ::nc_server_config_load_modules(). + * + * @param[in] data YANG data belonging to either ietf-netconf-server, ietf-keystore or ietf-truststore modules. + * This data __must be valid__. No node can have an operation attribute. + * @return 0 on success, 1 on error. + */ +int nc_server_config_setup_data(const struct lyd_node *data); + +/** + * @brief Configure server based on the given data stored in a file. + * + * Wrapper around ::nc_server_config_setup_data() hiding work with parsing the data. + * Context must already have implemented the required modules, see ::nc_server_config_load_modules(). + * + * @param[in] ctx libyang context. + * @param[in] path Path to a file with ietf-netconf-server, ietf-keystore or ietf-truststore YANG data. + * This data __must be valid__. No node can have an operation attribute. + * @return 0 on success, 1 on error. + */ +int nc_server_config_setup_path(const struct ly_ctx *ctx, const char *path); + +#ifdef NC_ENABLED_SSH_TLS + +/** + * @brief Creates new YANG configuration data nodes for address and port. + * + * @param[in] ctx libyang context. + * @param[in] endpt_name Arbitrary identifier of the endpoint. + * If an endpoint with this identifier already exists, its contents might be changed. + * @param[in] transport Either SSH or TLS transport for the given endpoint. + * @param[in] address New listening address. + * @param[in] port New listening port. + * @param[in,out] config Configuration YANG data tree. If *config is NULL, it will be created. + * Otherwise the new YANG data will be added to the previous data and may override it. + * @return 0 on success, non-zero otherwise. + */ +int nc_server_config_add_address_port(const struct ly_ctx *ctx, const char *endpt_name, NC_TRANSPORT_IMPL transport, + const char *address, uint16_t port, struct lyd_node **config); + +#endif /* NC_ENABLED_SSH_TLS */ + +/** + * @brief Creates new YANG data nodes for a UNIX socket. + * + * @param[in] ctx libyang context. + * @param[in] endpt_name Arbitrary identifier of the endpoint. + * If an endpoint with this identifier already exists, its contents might be changed. + * @param[in] path Path to the socket. + * @param[in] mode New mode, use -1 for default. + * @param[in] uid New uid, use -1 for default + * @param[in] gid New gid, use -1 for default + * @param[in,out] config Configuration YANG data tree. If *config is NULL, it will be created. + * Otherwise the new YANG data will be added to the previous data and may override it. + * @return 0 on success, non-zero otherwise. + */ +int nc_server_config_add_unix_socket(const struct ly_ctx *ctx, const char *endpt_name, const char *path, + mode_t mode, uid_t uid, gid_t gid, struct lyd_node **config); + +/** + * @brief Deletes an endpoint from the YANG data. + * + * @param[in] endpt_name Optional identifier of an endpoint to be deleted. + * If NULL, all of the endpoints will be deleted. + * @param[in,out] config Modified configuration YANG data tree. + * @return 0 on success, non-zero otherwise. + */ +int nc_server_config_del_endpt(const char *endpt_name, struct lyd_node **config); + +#ifdef NC_ENABLED_SSH_TLS + +/** + * @brief Creates new YANG data nodes for an asymmetric key in the keystore. + * + * @param[in] ctx libyang context. + * @param[in] ti Transport in which the key pair will be used. Either SSH or TLS. + * @param[in] asym_key_name Identifier of the asymmetric key pair. + * This identifier is used to reference the key pair. + * @param[in] privkey_path Path to a private key file. + * @param[in] pubkey_path Optional path a public key file. + * If not supplied, it will be generated from the private key. + * @param[in,out] config Configuration YANG data tree. If *config is NULL, it will be created. + * Otherwise the new YANG data will be added to the previous data and may override it. + * @return 0 on success, non-zero otherwise. + */ +int nc_server_config_add_keystore_asym_key(const struct ly_ctx *ctx, NC_TRANSPORT_IMPL ti, const char *asym_key_name, + const char *privkey_path, const char *pubkey_path, struct lyd_node **config); + +/** + * @brief Deletes a keystore's asymmetric key from the YANG data. + * + * @param[in] asym_key_name Optional identifier of the asymmetric key to be deleted. + * If NULL, all of the asymmetric keys in the keystore will be deleted. + * @param[in,out] config Configuration YANG data tree. + * @return 0 on success, non-zero otherwise. + */ +int nc_server_config_del_keystore_asym_key(const char *asym_key_name, struct lyd_node **config); + +/** + * @brief Creates new YANG data nodes for a certificate in the keystore. + * + * A certificate can not exist without its asymmetric key, so you must create an asymmetric key + * with the same identifier you pass to this function. + * + * @param[in] ctx libyang context. + * @param[in] asym_key_name Arbitrary identifier of the asymmetric key. + * If an asymmetric key pair with this name already exists, its contents will be changed. + * @param[in] cert_name Arbitrary identifier of the key pair's certificate. + * If a certificate with this name already exists, its contents will be changed. + * @param[in] cert_path Path to the PEM encoded certificate file. + * @param[in,out] config Configuration YANG data tree. If *config is NULL, it will be created. + * Otherwise the new YANG data will be added to the previous data and may override it. + * @return 0 on success, non-zero otherwise. + */ +int nc_server_config_add_keystore_cert(const struct ly_ctx *ctx, const char *asym_key_name, const char *cert_name, + const char *cert_path, struct lyd_node **config); + +/** + * @brief Deletes a keystore's certificate from the YANG data. + * + * @param[in] asym_key_name Identifier of an existing asymmetric key pair. + * @param[in] cert_name Optional identifier of a certificate to be deleted. + * If NULL, all of the certificates belonging to the asymmetric key pair will be deleted. + * @param[in,out] config Configuration YANG data tree. If *config is NULL, it will be created. + * Otherwise the new YANG data will be added to the previous data and may override it. + * @return 0 on success, non-zero otherwise. + */ +int nc_server_config_del_keystore_cert(const char *asym_key_name, const char *cert_name, struct lyd_node **config); + +/** + * @brief Creates new YANG data nodes for a public key in the truststore. + * + * @param[in] ctx libyang context. + * @param[in] pub_bag_name Arbitrary identifier of the public key bag. + * This name is used to reference the public keys in the bag. + * If a public key bag with this name already exists, its contents will be changed. + * @param[in] pubkey_name Arbitrary identifier of the public key. + * If a public key with this name already exists in the given bag, its contents will be changed. + * @param[in] pubkey_path Path to a file containing a public key. + * @param[in,out] config Configuration YANG data tree. If *config is NULL, it will be created. + * Otherwise the new YANG data will be added to the previous data and may override it. + * @return 0 on success, non-zero otherwise. + */ +int nc_server_config_add_truststore_pubkey(const struct ly_ctx *ctx, const char *pub_bag_name, const char *pubkey_name, + const char *pubkey_path, struct lyd_node **config); + +/** + * @brief Deletes a truststore's public key from the YANG data. + * + * @param[in] pub_bag_name Identifier of an existing public key bag. + * @param[in] pubkey_name Optional identifier of a public key to be deleted. + * If NULL, all of the public keys in the given bag will be deleted. + * @param[in,out] config Configuration YANG data tree. + * @return 0 on success, non-zero otherwise. + */ +int nc_server_config_del_truststore_pubkey(const char *pub_bag_name, const char *pubkey_name, struct lyd_node **config); + +/** + * @brief Creates new YANG data nodes for a certificate in the truststore. + * + * @param[in] ctx libyang context. + * @param[in] cert_bag_name Arbitrary identifier of the certificate bag. + * This name is used to reference the certificates in the bag. + * If a certificate bag with this name already exists, its contents will be changed. + * @param[in] cert_name Arbitrary identifier of the certificate. + * If a certificate with this name already exists in the given bag, its contents will be changed. + * @param[in] cert_path Path to a file containing a PEM encoded certificate. + * @param[in,out] config Configuration YANG data tree. If *config is NULL, it will be created. + * Otherwise the new YANG data will be added to the previous data and may override it. + * @return 0 on success, non-zero otherwise. + */ +int nc_server_config_add_truststore_cert(const struct ly_ctx *ctx, const char *cert_bag_name, const char *cert_name, + const char *cert_path, struct lyd_node **config); + +/** + * @brief Deletes a truststore's certificate from the YANG data. + * + * @param[in] cert_bag_name Identifier of an existing certificate bag. + * @param[in] cert_name Optional identifier of a certificate to be deleted. + * If NULL, all of the certificates in the given bag will be deleted. + * @param[in,out] config Configuration YANG data tree. + * @return 0 on success, non-zero otherwise. + */ +int nc_server_config_del_truststore_cert(const char *cert_bag_name, + const char *cert_name, struct lyd_node **config); + +/** + * @} Server Configuration Functions + */ + +/** + * @defgroup server_config_ssh SSH Server Configuration + * @ingroup server_config + * + * @brief SSH server configuration creation and deletion + * @{ + */ + +/** + * @brief Creates new YANG configuration data nodes for a hostkey. + * + * @param[in] ctx libyang context. + * @param[in] endpt_name Arbitrary identifier of the endpoint. + * If an endpoint with this identifier already exists, its hostkey might be changed. + * @param[in] hostkey_name Arbitrary identifier of the hostkey. + * If a hostkey with this identifier already exists, its contents will be changed. + * @param[in] privkey_path Path to a file containing a private key. + * The private key has to be in a PEM format. Only RSA and ECDSA keys are supported. + * @param[in] pubkey_path Optional path to a file containing a public key. If NULL, public key will be + * generated from the private key. + * @param[in,out] config Configuration YANG data tree. If *config is NULL, it will be created. + * Otherwise the new YANG data will be added to the previous data and may override it. + * @return 0 on success, non-zero otherwise. + */ +int nc_server_config_add_ssh_hostkey(const struct ly_ctx *ctx, const char *endpt_name, const char *hostkey_name, + const char *privkey_path, const char *pubkey_path, struct lyd_node **config); + +/** + * @brief Deletes a hostkey from the YANG data. + * + * @param[in] ctx libyang context. + * @param[in] endpt_name Identifier of an existing endpoint. + * @param[in] hostkey_name Optional identifier of the hostkey to be deleted. + * If NULL, all of the hostkeys on this endpoint will be deleted. + * @param[in,out] config Configuration YANG data tree. + * @return 0 on success, non-zero otherwise. + */ +int nc_server_config_del_ssh_hostkey(const struct ly_ctx *ctx, const char *endpt_name, + const char *hostkey_name, struct lyd_node **config); + +/** + * @brief Creates new YANG data nodes for a reference to an asymmetric key located in the keystore. + * + * This asymmetric key pair will be used as the SSH hostkey. + * + * @param[in] ctx libyang context. + * @param[in] endpt_name Arbitrary identifier of an endpoint. + * If an endpoint with this identifier already exists, its contents will be changed. + * @param[in] hostkey_name Arbitrary identifier of the endpoint's hostkey. + * If an endpoint's hostkey with this identifier already exists, its contents will be changed. + * @param[in] keystore_reference Name of the asymmetric key pair to be referenced and used as a hostkey. + * @param[in,out] config Configuration YANG data tree. If *config is NULL, it will be created. + * Otherwise the new YANG data will be added to the previous data and may override it. + * @return 0 on success, non-zero otherwise. + */ +int nc_server_config_add_ssh_keystore_ref(const struct ly_ctx *ctx, const char *endpt_name, const char *hostkey_name, + const char *keystore_reference, struct lyd_node **config); + +/** + * @brief Deletes a keystore reference from the YANG data. + * + * @param[in] endpt_name Identifier of an existing endpoint. + * @param[in] hostkey_name Identifier of an existing hostkey on the given endpoint. + * @param[in,out] config Configuration YANG data tree. + * @return 0 on success, non-zero otherwise. + */ +int nc_server_config_del_ssh_keystore_ref(const char *endpt_name, const char *hostkey_name, + struct lyd_node **config); + +/** + * @brief Creates new YANG configuration data nodes for an SSH user's public key authentication method. + * + * @param[in] ctx libyang context. + * @param[in] endpt_name Arbitrary identifier of the endpoint. + * If an endpoint with this identifier already exists, its user might be changed. + * @param[in] user_name Arbitrary identifier of the user. + * If an user with this identifier already exists, its contents will be changed. + * @param[in] pubkey_name Arbitrary identifier of the user's public key. + * If a public key with this identifier already exists for this user, its contents will be changed. + * @param[in] pubkey_path Path to a file containing the user's public key. + * @param[in,out] config Configuration YANG data tree. If *config is NULL, it will be created. + * Otherwise the new YANG data will be added to the previous data and may override it. + * @return 0 on success, non-zero otherwise. + */ +int nc_server_config_add_ssh_user_pubkey(const struct ly_ctx *ctx, const char *endpt_name, + const char *user_name, const char *pubkey_name, const char *pubkey_path, struct lyd_node **config); + +/** + * @brief Deletes an SSH user's public key from the YANG data. + * + * @param[in] endpt_name Identifier of an existing endpoint. + * @param[in] user_name Identifier of an existing user on the given endpoint. + * @param[in] pubkey_name Optional identifier of a public key to be deleted. + * If NULL, all of the users public keys will be deleted. + * @param[in,out] config Modified configuration YANG data tree. + * @return 0 on success, non-zero otherwise. + */ +int nc_server_config_del_ssh_user_pubkey(const char *endpt_name, const char *user_name, + const char *pubkey_name, struct lyd_node **config); + +/** + * @brief Creates new YANG configuration data nodes for an SSH user's password authentication method. + * + * @param[in] ctx libyang context. + * @param[in] endpt_name Arbitrary identifier of the endpoint. + * If an endpoint with this identifier already exists, its user might be changed. + * @param[in] user_name Arbitrary identifier of the user. + * If an user with this identifier already exists, its contents will be changed. + * @param[in] password Clear-text password to be set for the user. It will be hashed. + * @param[in,out] config Configuration YANG data tree. If *config is NULL, it will be created. + * Otherwise the new YANG data will be added to the previous data and may override it. + * @return 0 on success, non-zero otherwise. + */ +int nc_server_config_add_ssh_user_password(const struct ly_ctx *ctx, const char *endpt_name, + const char *user_name, const char *password, struct lyd_node **config); + +/** + * @brief Deletes an SSH user's password from the YANG data. + * + * @param[in] endpt_name Identifier of an existing endpoint. + * @param[in] user_name Identifier of an existing user on the given endpoint. + * @param[in,out] config Modified configuration YANG data tree. + * @return 0 on success, non-zero otherwise. + */ +int nc_server_config_del_ssh_user_password(const char *endpt_name, const char *user_name, + struct lyd_node **config); + +/** + * @brief Creates new YANG configuration data nodes for an SSH user's keyboard interactive authentication method. + * + * @param[in] ctx libyang context. + * @param[in] endpt_name Arbitrary identifier of the endpoint. + * If an endpoint with this identifier already exists, its user might be changed. + * @param[in] user_name Arbitrary identifier of the user. + * If an user with this identifier already exists, its contents will be changed. + * @param[in] pam_config_name Name of the PAM configuration file. + * @param[in] pam_config_dir Optional. The absolute path to the directory in which the configuration file + * with the name pam_config_name is located. A newer version (>= 1.4) of PAM library is required to be able to specify + * the path. If NULL is passed, then the PAM's system directories will be searched (usually /etc/pam.d/). + * @param[in,out] config Configuration YANG data tree. If *config is NULL, it will be created. + * Otherwise the new YANG data will be added to the previous data and may override it. + * @return 0 on success, non-zero otherwise. + */ +int nc_server_config_add_ssh_user_interactive(const struct ly_ctx *ctx, const char *endpt_name, + const char *user_name, const char *pam_config_name, const char *pam_config_dir, struct lyd_node **config); + +/** + * @brief Deletes an SSH user's keyboard interactive authentication from the YANG data. + * + * @param[in] endpt_name Identifier of an existing endpoint. + * @param[in] user_name Identifier of an existing user on the given endpoint. + * @param[in,out] config Modified configuration YANG data tree. + * @return 0 on success, non-zero otherwise. + */ +int nc_server_config_del_ssh_user_interactive(const char *endpt_name, const char *user_name, + struct lyd_node **config); + +/** + * @brief Deletes an SSH user from the YANG data. + * + * @param[in] endpt_name Identifier of an existing endpoint. + * @param[in] user_name Optional identifier of an user to be deleted. + * If NULL, all of the users on this endpoint will be deleted. + * @param[in,out] config Modified configuration YANG data tree. + * @return 0 on success, non-zero otherwise. + */ +int nc_server_config_del_ssh_user(const char *endpt_name, + const char *user_name, struct lyd_node **config); + +/** + * @brief Creates new YANG data nodes for a reference to a public key bag located in the truststore. + * + * The public key's located in the bag will be used for client authentication. + * + * @param[in] ctx libyang context. + * @param[in] endpt_name Arbitrary identifier of an endpoint. + * If an endpoint with this identifier already exists, its contents will be changed. + * @param[in] user_name Arbitrary identifier of the endpoint's user. + * If an endpoint's user with this identifier already exists, its contents will be changed. + * @param[in] truststore_reference Name of the public key bag to be referenced and used for authentication. + * @param[in,out] config Configuration YANG data tree. If *config is NULL, it will be created. + * Otherwise the new YANG data will be added to the previous data and may override it. + * @return 0 on success, non-zero otherwise. + */ +int nc_server_config_add_ssh_truststore_ref(const struct ly_ctx *ctx, const char *endpt_name, const char *user_name, + const char *truststore_reference, struct lyd_node **config); + +/** + * @brief Deletes a truststore reference from the YANG data. + * + * @param[in] endpt_name Identifier of an existing endpoint. + * @param[in] user_name Identifier of an user on the given endpoint whose truststore reference will be deleted. + * @param[in,out] config Modified configuration YANG data tree. + * @return 0 on success, non-zero otherwise. + */ +int nc_server_config_del_ssh_truststore_ref(const char *endpt_name, const char *user_name, + struct lyd_node **config); + +/** + * @brief Creates new YANG configuration data nodes, which will be a reference to another SSH endpoint's users. + * + * Whenever a client tries to connect to the referencing endpoint, all of its users will be tried first. If no match is + * found, the referenced endpoint's configured users will be tried. + * + * @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. + * @param[in] referenced_endpt Identifier of an endpoint, which has to exist whenever this data + * is applied. The referenced endpoint can reference another one and so on, but there mustn't be a cycle. + * @param[in,out] config Configuration YANG data tree. If *config is NULL, it will be created. + * Otherwise the new YANG data will be added to the previous data and may override it. + * @return 0 on success, non-zero otherwise. + */ +int nc_server_config_add_ssh_endpoint_client_ref(const struct ly_ctx *ctx, const char *endpt_name, + const char *referenced_endpt, struct lyd_node **config); + +/** + * @brief Deletes reference to another SSH endpoint's users from the YANG data. + * + * @param[in] endpt_name Identifier of an existing endpoint. + * @param[in,out] config Modified configuration YANG data tree. + * @return 0 on success, non-zero otherwise. + */ +int nc_server_config_del_ssh_endpoint_client_ref(const char *endpt_name, struct lyd_node **config); + +/** + * @} SSH Server Configuration + */ + +/** + * @defgroup server_config_tls TLS Server Configuration + * @ingroup server_config + * + * @brief TLS server configuration creation and deletion + * @{ + */ + +/** + * @brief Creates new YANG configuration data nodes for a server's certificate. + * + * @param[in] ctx libyang context. + * @param[in] endpt_name Arbitrary identifier of the endpoint. + * If an endpoint with this identifier already exists, its server certificate will be changed. + * @param[in] privkey_path Path to the server's PEM encoded private key file. + * @param[in] pubkey_path Optional path to the server's public key file. If not provided, + * it will be generated from the private key. + * @param[in] cert_path Path to the server's certificate file. + * @param[in,out] config Configuration YANG data tree. If *config is NULL, it will be created. + * Otherwise the new YANG data will be added to the previous data and may override it. + * @return 0 on success, non-zero otherwise. + */ +int nc_server_config_add_tls_server_cert(const struct ly_ctx *ctx, const char *endpt_name, const char *privkey_path, + const char *pubkey_path, const char *cert_path, struct lyd_node **config); + +/** + * @brief Deletes the server's certificate from the YANG data. + * + * @param[in] endpt_name Identifier of an existing endpoint. + * @param[in,out] config Modified configuration YANG data tree. + * @return 0 on success, non-zero otherwise. + */ +int nc_server_config_del_tls_server_cert(const char *endpt_name, struct lyd_node **config); + +/** + * @brief Creates new YANG configuration data nodes for a keystore reference to the TLS server's 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. + * @param[in] asym_key_ref Name of the asymmetric key pair in the keystore to be referenced. + * @param[in] cert_ref Name of the certificate, which must belong to the given asymmetric key pair, to be referenced. + * @param[in,out] config Configuration YANG data tree. If *config is NULL, it will be created. + * Otherwise the new YANG data will be added to the previous data and may override it. + * @return 0 on success, non-zero otherwise. + */ +int nc_server_config_add_tls_keystore_ref(const struct ly_ctx *ctx, const char *endpt_name, const char *asym_key_ref, + const char *cert_ref, struct lyd_node **config); + +/** + * @brief Deletes a TLS server certificate keystore reference from the YANG data. + * + * @param[in] endpt_name Identifier of an existing endpoint. + * @param[in,out] config Modified configuration YANG data tree. + * @return 0 on success, non-zero otherwise. + */ +int nc_server_config_del_tls_keystore_ref(const char *endpt_name, struct lyd_node **config); + +/** + * @brief Creates new YANG configuration data nodes for a client's (end-entity) 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. + * @param[in] cert_name Arbitrary identifier of the client's certificate. + * If a client certificate with this identifier already exists, it will be changed. + * @param[in] cert_path Path to the client's certificate file. + * @param[in,out] config Configuration YANG data tree. If *config is NULL, it will be created. + * Otherwise the new YANG data will be added to the previous data and may override it. + * @return 0 on success, non-zero otherwise. + */ +int nc_server_config_add_tls_client_cert(const struct ly_ctx *ctx, const char *endpt_name, const char *cert_name, + const char *cert_path, struct lyd_node **config); + +/** + * @brief Deletes a client (end-entity) certificate from the YANG data. + * + * @param[in] endpt_name Identifier of an existing endpoint. + * @param[in] cert_name Optional name of a certificate to be deleted. + * If NULL, all of the end-entity certificates on the given endpoint will be deleted. + * @param[in,out] config Modified configuration YANG data tree. + * @return 0 on success, non-zero otherwise. + */ +int nc_server_config_del_tls_client_cert(const char *endpt_name, const char *cert_name, struct lyd_node **config); + +/** + * @brief Creates new YANG configuration data nodes for a truststore reference to a set of client (end-entity) certificates. + * + * @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. + * @param[in] cert_bag_ref Identifier of the certificate bag in the truststore to be referenced. + * @param[in,out] config Configuration YANG data tree. If *config is NULL, it will be created. + * Otherwise the new YANG data will be added to the previous data and may override it. + * @return 0 on success, non-zero otherwise. + */ +int nc_server_config_add_tls_client_cert_truststore_ref(const struct ly_ctx *ctx, const char *endpt_name, + const char *cert_bag_ref, struct lyd_node **config); + +/** + * @brief Deletes a client (end-entity) certificates truststore reference from the YANG data. + * + * @param[in] endpt_name Identifier of an existing endpoint. + * @param[in,out] config Modified configuration YANG data tree. + * @return 0 on success, non-zero otherwise. + */ +int nc_server_config_del_tls_client_cert_truststore_ref(const char *endpt_name, struct lyd_node **config); + +/** + * @brief Creates new YANG configuration data nodes for a client certificate authority (trust-anchor) 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. + * @param[in] cert_name Arbitrary identifier of the certificate authority certificate. + * If a CA with this identifier already exists, it will be changed. + * @param[in] cert_path Path to the CA certificate file. + * @param[in,out] config Configuration YANG data tree. If *config is NULL, it will be created. + * Otherwise the new YANG data will be added to the previous data and may override it. + * @return 0 on success, non-zero otherwise. + */ +int nc_server_config_add_tls_ca_cert(const struct ly_ctx *ctx, const char *endpt_name, const char *cert_name, + const char *cert_path, struct lyd_node **config); + +/** + * @brief Deletes a client certificate authority (trust-anchor) certificate from the YANG data. + * + * @param[in] endpt_name Identifier of an existing endpoint. + * @param[in] cert_name Optional name of a certificate to be deleted. + * If NULL, all of the CA certificates on the given endpoint will be deleted. + * @param[in,out] config Modified configuration YANG data tree. + * @return 0 on success, non-zero otherwise. + */ +int nc_server_config_del_tls_ca_cert(const char *endpt_name, const char *cert_name, struct lyd_node **config); + +/** + * @brief Creates new YANG configuration data nodes for a truststore reference to a set of client certificate authority (trust-anchor) certificates. + * + * @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. + * @param[in] cert_bag_ref Identifier of the certificate bag in the truststore to be referenced. + * @param[in,out] config Configuration YANG data tree. If *config is NULL, it will be created. + * Otherwise the new YANG data will be added to the previous data and may override it. + * @return 0 on success, non-zero otherwise. + */ +int nc_server_config_add_tls_ca_cert_truststore_ref(const struct ly_ctx *ctx, const char *endpt_name, + const char *cert_bag_ref, struct lyd_node **config); + +/** + * @brief Deletes a client certificate authority (trust-anchor) certificates truststore reference from the YANG data. + * + * @param[in] endpt_name Identifier of an existing endpoint. + * @param[in,out] config Modified configuration YANG data tree. + * @return 0 on success, non-zero otherwise. + */ +int nc_server_config_del_tls_ca_cert_truststore_ref(const char *endpt_name, struct lyd_node **config); + +/** + * @brief Creates new YANG configuration data nodes, which will be a reference to another TLS endpoint's certificates. + * + * Whenever an user tries to connect to the referencing endpoint, all of its certificates will be tried first. If no match is + * found, the referenced endpoint's configured certificates will be tried. The same applies to cert-to-name entries. + * + * @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. + * @param[in] referenced_endpt Identifier of an endpoint, which has to exist whenever this data + * is applied. The referenced endpoint can reference another one and so on, but there mustn't be a cycle. + * @param[in,out] config Configuration YANG data tree. If *config is NULL, it will be created. + * Otherwise the new YANG data will be added to the previous data and may override it. + * @return 0 on success, non-zero otherwise. + */ +int nc_server_config_add_tls_endpoint_client_ref(const struct ly_ctx *ctx, const char *endpt_name, + const char *referenced_endpt, struct lyd_node **config); + +/** + * @brief Deletes reference to another TLS endpoint's users from the YANG data. + * + * @param[in] endpt_name Identifier of an existing endpoint. + * @param[in,out] config Modified configuration YANG data tree. + * @return 0 on success, non-zero otherwise. + */ +int nc_server_config_del_tls_endpoint_client_ref(const char *endpt_name, struct lyd_node **config); + +/** + * @brief Creates new YANG configuration data nodes for a cert-to-name entry. + * + * @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. + * @param[in] id ID of the entry. The lower the ID, the higher the priority of the entry (it will be checked earlier). + * @param[in] fingerprint Optional fingerprint of the entry. The fingerprint should always be set, however if it is + * not set, it will match any certificate. Entry with no fingerprint should therefore be placed only as the last entry. + * @param[in] map_type Mapping username to the certificate option. + * @param[in] name Username for this cert-to-name entry. + * @param[in,out] config Configuration YANG data tree. If *config is NULL, it will be created. + * Otherwise the new YANG data will be added to the previous data and may override it. + * @return 0 on success, non-zero otherwise. + */ +int nc_server_config_add_tls_ctn(const struct ly_ctx *ctx, const char *endpt_name, uint32_t id, const char *fingerprint, + NC_TLS_CTN_MAPTYPE map_type, const char *name, struct lyd_node **config); + +/** + * @brief Deletes a cert-to-name entry from the YANG data. + * + * @param[in] endpt_name Identifier of an existing endpoint. + * @param[in] id Optional ID of the CTN entry. + * If 0, all of the cert-to-name entries on the given endpoint will be deleted. + * @param[in,out] config Modified configuration YANG data tree. + * @return 0 on success, non-zero otherwise. + */ +int nc_server_config_del_tls_ctn(const char *endpt_name, uint32_t id, struct lyd_node **config); + +/** + * @} TLS Server Configuration + */ + +/** + * @defgroup server_config_ch Call Home Server Configuration + * @ingroup server_config + * + * @brief Call Home server configuration creation and deletion + * @{ + */ + +/** + * @} Call Home Server Configuration + */ + +/** + * @defgroup server_config_ch_functions Call Home Server Configuration Functions + * @ingroup server_config_ch + * + * @brief Call Home server configuration functions + * @{ + */ + +/** + * @brief Creates new YANG configuration data nodes for a Call Home client's address and port. + * + * @param[in] ctx libyang context. + * @param[in] client_name Arbitrary identifier of the Call Home client. + * If a Call Home client with this identifier already exists, its contents will be changed. + * @param[in] endpt_name Arbitrary identifier of the client's endpoint. + * If the client's endpoint with this identifier already exists, its contents will be changed. + * @param[in] transport Transport protocol to be used on this endpoint - either SSH or TLS. + * @param[in] address Address to connect to. + * @param[in] port Port to connect to. + * @param[in,out] config Configuration YANG data tree. If *config is NULL, it will be created. + * Otherwise the new YANG data will be added to the previous data and may override it. + * @return 0 on success, non-zero otherwise. + */ +int nc_server_config_add_ch_address_port(const struct ly_ctx *ctx, const char *client_name, const char *endpt_name, + NC_TRANSPORT_IMPL transport, const char *address, const char *port, struct lyd_node **config); + +#endif /* NC_ENABLED_SSH_TLS */ + +/** + * @brief Deletes a Call Home client from the YANG data. + * + * @param[in] client_name Optional identifier of a client to be deleted. + * If NULL, all of the Call Home clients will be deleted. + * @param[in,out] config Modified configuration YANG data tree. + * @return 0 on success, non-zero otherwise. + */ +int nc_server_config_del_ch_client(const char *client_name, struct lyd_node **config); + +/** + * @brief Deletes a Call Home endpoint from the YANG data. + * + * @param[in] client_name Identifier of an existing Call Home client. + * @param[in] endpt_name Optional identifier of a CH endpoint to be deleted. + * If NULL, all of the CH endpoints which belong to the given client will be deleted. + * @param[in,out] config Modified configuration YANG data tree. + * @return 0 on success, non-zero otherwise. + */ +int nc_server_config_del_ch_endpt(const char *client_name, const char *endpt_name, struct lyd_node **config); + +/** + * @brief Creates new YANG configuration data nodes for the Call Home persistent connection type. + * + * This is the default connection type. If periodic connection type was set before, it will be unset. + * + * @param[in] ctx libyang context. + * @param[in] client_name Arbitrary identifier of the Call Home client. + * If a Call Home client with this identifier already exists, its contents will be changed. + * @param[in,out] config Configuration YANG data tree. If *config is NULL, it will be created. + * Otherwise the new YANG data will be added to the previous data and may override it. + * @return 0 on success, non-zero otherwise. + */ +int nc_server_config_add_ch_persistent(const struct ly_ctx *ctx, const char *client_name, struct lyd_node **config); + +/** + * @brief Creates new YANG configuration data nodes for the period parameter of the Call Home periodic connection type. + * + * If called, the persistent connection type will be replaced by periodic. + * + * @param[in] ctx libyang context. + * @param[in] client_name Arbitrary identifier of the Call Home client. + * If a Call Home client with this identifier already exists, its contents will be changed. + * @param[in] period Duration between periodic connections in minutes. + * @param[in,out] config Configuration YANG data tree. If *config is NULL, it will be created. + * Otherwise the new YANG data will be added to the previous data and may override it. + * @return 0 on success, non-zero otherwise. + */ +int nc_server_config_add_ch_period(const struct ly_ctx *ctx, const char *client_name, uint16_t period, + struct lyd_node **config); + +/** + * @brief Deletes the Call Home period parameter of the periodic connection type from the YANG data. + * + * This behaves the same as setting the period to 60 minutes, which is the default value of this node. + * + * @param[in] client_name Identifier of an existing Call Home client. + * @param[in,out] config Modified configuration YANG data tree. + * @return 0 on success, non-zero otherwise. + */ +int nc_server_config_del_ch_period(const char *client_name, struct lyd_node **config); + +/** + * @brief Creates new YANG configuration data nodes for the anchor time parameter of the Call Home periodic connection type. + * + * If called, the persistent connection type will be replaced by periodic. + * + * @param[in] ctx libyang context. + * @param[in] client_name Arbitrary identifier of the Call Home client. + * If a Call Home client with this identifier already exists, its contents will be changed. + * @param[in] anchor_time Timestamp before or after which a series of periodic connections are determined. + * @param[in,out] config Configuration YANG data tree. If *config is NULL, it will be created. + * Otherwise the new YANG data will be added to the previous data and may override it. + * @return 0 on success, non-zero otherwise. + */ +int nc_server_config_add_ch_anchor_time(const struct ly_ctx *ctx, const char *client_name, + const char *anchor_time, struct lyd_node **config); + +/** + * @brief Deletes the Call Home anchor time parameter of the periodic connection type from the YANG data. + * + * @param[in] client_name Identifier of an existing Call Home client. + * @param[in,out] config Modified configuration YANG data tree. + * @return 0 on success, non-zero otherwise. + */ +int nc_server_config_del_ch_anchor_time(const char *client_name, struct lyd_node **config); + +/** + * @brief Creates new YANG configuration data nodes for the idle timeout parameter of the Call Home periodic connection type. + * + * If called, the persistent connection type will be replaced by periodic. + * + * @param[in] ctx libyang context. + * @param[in] client_name Arbitrary identifier of the Call Home client. + * If a Call Home client with this identifier already exists, its contents will be changed. + * @param[in] idle_timeout Specifies the maximum number of seconds that a session may remain idle. + * @param[in,out] config Configuration YANG data tree. If *config is NULL, it will be created. + * Otherwise the new YANG data will be added to the previous data and may override it. + * @return 0 on success, non-zero otherwise. + */ +int nc_server_config_add_ch_idle_timeout(const struct ly_ctx *ctx, const char *client_name, + uint16_t idle_timeout, struct lyd_node **config); + +/** + * @brief Deletes the Call Home idle timeout parameter of the periodic connection type from the YANG data. + * + * This behaves the same as setting the timeout to 180 seconds, which is the default value of this node. + * + * @param[in] client_name Identifier of an existing Call Home client. + * @param[in,out] config Modified configuration YANG data tree. + * @return 0 on success, non-zero otherwise. + */ +int nc_server_config_del_ch_idle_timeout(const char *client_name, struct lyd_node **config); + +/** + * @brief Creates new YANG configuration data nodes for the Call Home reconnect strategy. + * + * @param[in] ctx libyang context. + * @param[in] client_name Arbitrary identifier of the Call Home client. + * If a Call Home client with this identifier already exists, its contents will be changed. + * @param[in] start_with Specifies which endpoint to try if a connection is unsuccessful. Default value is NC_CH_FIRST_LISTED. + * @param[in] max_wait The number of seconds after which a connection to an endpoint is deemed unsuccessful. Default value if 5. + * @param[in] max_attempts The number of unsuccessful connection attempts before moving to the next endpoint. Default value is 3. + * @param[in,out] config Configuration YANG data tree. If *config is NULL, it will be created. + * Otherwise the new YANG data will be added to the previous data and may override it. + * @return 0 on success, non-zero otherwise. + */ +int nc_server_config_add_ch_reconnect_strategy(const struct ly_ctx *ctx, const char *client_name, + NC_CH_START_WITH start_with, uint16_t max_wait, uint8_t max_attempts, struct lyd_node **config); + +/** + * @brief Resets the values of the Call Home reconnect strategy nodes to their defaults. + * + * The default values are: start-with = NC_CH_FIRST_LISTED, max-wait = 5 and max-attempts = 3. + * + * @param[in] client_name Identifier of an existing Call Home client. + * @param[in,out] config Modified configuration YANG data tree. + * @return 0 on success, non-zero otherwise. + */ +int nc_server_config_del_ch_reconnect_strategy(const char *client_name, struct lyd_node **config); + +/** + * @} Call Home Server Configuration Functions + */ + +#ifdef NC_ENABLED_SSH_TLS + +/** + * @defgroup server_config_ch_ssh SSH Call Home Server Configuration + * @ingroup server_config_ch + * + * @brief SSH Call Home server configuration creation and deletion + * @{ + */ + +/** + * @brief Creates new YANG data nodes for a Call Home SSH hostkey. + * + * @param[in] ctx libyang context. + * @param[in] client_name Arbitrary identifier of the Call Home client. + * If a Call Home client with this identifier already exists, its contents will be changed. + * @param[in] endpt_name Arbitrary identifier of the client's endpoint. + * If the client's endpoint with this identifier already exists, its contents will be changed. + * @param[in] hostkey_name Arbitrary identifier of the endpoint's hostkey. + * If the endpoint's hostkey with this identifier already exists, its contents will be changed. + * @param[in] privkey_path Path to a file containing a private key. + * The private key has to be in a PEM format. Only RSA and ECDSA keys are supported. + * @param[in] pubkey_path Path to a file containing a public key. If NULL, public key will be + * generated from the private key. + * @param[in,out] config Configuration YANG data tree. If *config is NULL, it will be created. + * Otherwise the new YANG data will be added to the previous data and may override it. + * @return 0 on success, non-zero otherwise. + */ +int nc_server_config_add_ch_ssh_hostkey(const struct ly_ctx *ctx, const char *client_name, const char *endpt_name, + const char *hostkey_name, const char *privkey_path, const char *pubkey_path, struct lyd_node **config); + +/** + * @brief Deletes a Call Home hostkey from the YANG data. + * + * @param[in] client_name Identifier of an existing Call Home client. + * @param[in] endpt_name Identifier of an existing endpoint that belongs to the given CH client. + * @param[in] hostkey_name Optional identifier of a hostkey to be deleted. + * If NULL, all of the hostkeys on the given endpoint will be deleted. + * @param[in,out] config Modified configuration YANG data tree. + * @return 0 on success, non-zero otherwise. + */ +int nc_server_config_del_ch_ssh_hostkey(const char *client_name, const char *endpt_name, + const char *hostkey_name, struct lyd_node **config); + +/** + * @brief Creates new YANG data nodes for a reference to an asymmetric key located in the keystore. + * + * This asymmetric key pair will be used as the Call Home SSH hostkey. + * + * @param[in] ctx libyang context. + * @param[in] client_name Arbitrary identifier of the Call Home client. + * If a Call Home client with this identifier already exists, its contents will be changed. + * @param[in] endpt_name Arbitrary identifier of the client's endpoint. + * If the client's endpoint with this identifier already exists, its contents will be changed. + * @param[in] hostkey_name Arbitrary identifier of the endpoint's hostkey. + * If the endpoint's hostkey with this identifier already exists, its contents will be changed. + * @param[in] keystore_reference Name of the asymmetric key pair to be referenced and used as a hostkey. + * @param[in,out] config Configuration YANG data tree. If *config is NULL, it will be created. + * Otherwise the new YANG data will be added to the previous data and may override it. + * @return 0 on success, non-zero otherwise. + */ +int nc_server_config_add_ch_ssh_keystore_ref(const struct ly_ctx *ctx, const char *client_name, + const char *endpt_name, const char *hostkey_name, const char *keystore_reference, struct lyd_node **config); + +/** + * @brief Deletes a Call Home keystore reference from the YANG data. + * + * @param[in] client_name Identifier of an existing Call Home client. + * @param[in] endpt_name Identifier of an existing endpoint that belongs to the given CH client. + * @param[in] hostkey_name Identifier of an existing hostkey that belongs to the given CH endpoint. + * @param[in,out] config Modified configuration YANG data tree. + * @return 0 on success, non-zero otherwise. + */ +int nc_server_config_del_ch_ssh_keystore_ref(const char *client_name, const char *endpt_name, + const char *hostkey_name, struct lyd_node **config); + +/** + * @brief Creates new YANG data nodes for a Call Home SSH user's public key authentication method. + * + * @param[in] ctx libyang context. + * @param[in] client_name Arbitrary identifier of the Call Home client. + * If a Call Home client with this identifier already exists, its contents will be changed. + * @param[in] endpt_name Arbitrary identifier of the client's endpoint. + * If the client's endpoint with this identifier already exists, its contents will be changed. + * @param[in] user_name Arbitrary identifier of the endpoint's user. + * If the endpoint's user with this identifier already exists, its contents will be changed. + * @param[in] pubkey_name Arbitrary identifier of the user's public key. + * If the user's public key with this identifier already exists, its contents will be changed. + * @param[in] pubkey_path Path to a file containing a public key. + * @param[in,out] config Configuration YANG data tree. If *config is NULL, it will be created. + * Otherwise the new YANG data will be added to the previous data and may override it. + * @return 0 on success, non-zero otherwise. + */ +int nc_server_config_add_ch_ssh_user_pubkey(const struct ly_ctx *ctx, const char *client_name, const char *endpt_name, + const char *user_name, const char *pubkey_name, const char *pubkey_path, struct lyd_node **config); + +/** + * @brief Deletes a Call Home SSH user's public key from the YANG data. + * + * @param[in] client_name Identifier of an existing Call Home client. + * @param[in] endpt_name Identifier of an existing endpoint that belongs to the given CH client. + * @param[in] user_name Identifier of an existing SSH user that belongs to the given CH endpoint. + * @param[in] pubkey_name Optional identifier of a public key to be deleted. + * If NULL, all of the public keys which belong to the given SSH user will be deleted. + * @param[in,out] config Modified configuration YANG data tree. + * @return 0 on success, non-zero otherwise. + */ +int nc_server_config_del_ch_ssh_user_pubkey(const char *client_name, const char *endpt_name, + const char *user_name, const char *pubkey_name, struct lyd_node **config); + +/** + * @brief Creates new YANG data nodes for a Call Home SSH user's password authentication method. + * + * @param[in] ctx libyang context. + * @param[in] client_name Arbitrary identifier of the Call Home client. + * If a Call Home client with this identifier already exists, its contents will be changed. + * @param[in] endpt_name Arbitrary identifier of the client's endpoint. + * If the client's endpoint with this identifier already exists, its contents will be changed. + * @param[in] user_name Arbitrary identifier of the endpoint's user. + * If the endpoint's user with this identifier already exists, its contents will be changed. + * @param[in] password Clear-text password to be set for the user. It will be hashed. + * @param[in,out] config Configuration YANG data tree. If *config is NULL, it will be created. + * Otherwise the new YANG data will be added to the previous data and may override it. + * @return 0 on success, non-zero otherwise. + */ +int nc_server_config_add_ch_ssh_user_password(const struct ly_ctx *ctx, const char *client_name, const char *endpt_name, + const char *user_name, const char *password, struct lyd_node **config); + +/** + * @brief Deletes a Call Home SSH user's password from the YANG data. + * + * @param[in] client_name Identifier of an existing Call Home client. + * @param[in] endpt_name Identifier of an existing endpoint that belongs to the given CH client. + * @param[in] user_name Identifier of an existing SSH user that belongs to the given CH endpoint. + * @param[in,out] config Modified configuration YANG data tree. + * @return 0 on success, non-zero otherwise. + */ +int nc_server_config_del_ch_ssh_user_password(const char *client_name, const char *endpt_name, + const char *user_name, struct lyd_node **config); + +/** + * @brief Creates new YANG configuration data nodes for a Call Home SSH user's keyboard interactive authentication method. + * + * @param[in] ctx libyang context. + * @param[in] client_name Arbitrary identifier of the Call Home client. + * If a Call Home client with this identifier already exists, its contents will be changed. + * @param[in] endpt_name Arbitrary identifier of the client's endpoint. + * If the client's endpoint with this identifier already exists, its contents will be changed. + * @param[in] user_name Arbitrary identifier of the endpoint's user. + * If the endpoint's user with this identifier already exists, its contents will be changed. + * @param[in] pam_config_name Name of the PAM configuration file. + * @param[in] pam_config_dir Optional. The absolute path to the directory in which the configuration file + * with the name pam_config_name is located. A newer version (>= 1.4) of PAM library is required to be able to specify + * the path. If NULL is passed, then the PAM's system directories will be searched (usually /etc/pam.d/). + * @param[in,out] config Configuration YANG data tree. If *config is NULL, it will be created. + * Otherwise the new YANG data will be added to the previous data and may override it. + * @return 0 on success, non-zero otherwise. + */ +int nc_server_config_add_ch_ssh_user_interactive(const struct ly_ctx *ctx, const char *client_name, const char *endpt_name, + const char *user_name, const char *pam_config_name, const char *pam_config_dir, struct lyd_node **config); + +/** + * @brief Deletes a Call Home SSH user's keyboard interactive authentication from the YANG data. + * + * @param[in] client_name Identifier of an existing Call Home client. + * @param[in] endpt_name Identifier of an existing endpoint that belongs to the given CH client. + * @param[in] user_name Identifier of an existing SSH user that belongs to the given CH endpoint. + * @param[in,out] config Modified configuration YANG data tree. + * @return 0 on success, non-zero otherwise. + */ +int nc_server_config_del_ch_ssh_user_interactive(const char *client_name, const char *endpt_name, + const char *user_name, struct lyd_node **config); + +/** + * @brief Deletes a Call Home SSH user from the YANG data. + * + * @param[in] client_name Identifier of an existing Call Home client. + * @param[in] endpt_name Identifier of an existing endpoint that belongs to the given CH client. + * @param[in] user_name Identifier of an existing SSH user that belongs to the given CH endpoint. + * @param[in,out] config Modified configuration YANG data tree. + * @return 0 on success, non-zero otherwise. + */ +int nc_server_config_del_ch_ssh_user(const char *client_name, const char *endpt_name, + const char *user_name, struct lyd_node **config); + +/** + * @brief Creates new YANG data nodes for a reference to a public key bag located in the truststore. + * + * The public key's located in the bag will be used for Call Home SSH client authentication. + * + * @param[in] ctx libyang context. + * @param[in] client_name Arbitrary identifier of the Call Home client. + * If a Call Home client with this identifier already exists, its contents will be changed. + * @param[in] endpt_name Arbitrary identifier of the client's endpoint. + * If the client's endpoint with this identifier already exists, its contents will be changed. + * @param[in] user_name Arbitrary identifier of the endpoint's user. + * If the endpoint's user with this identifier already exists, its contents will be changed. + * @param[in] truststore_reference Name of the public key bag to be referenced and used for authentication. + * @param[in,out] config Configuration YANG data tree. If *config is NULL, it will be created. + * Otherwise the new YANG data will be added to the previous data and may override it. + * @return 0 on success, non-zero otherwise. + */ +int nc_server_config_add_ch_ssh_truststore_ref(const struct ly_ctx *ctx, const char *client_name, + const char *endpt_name, const char *user_name, const char *truststore_reference, struct lyd_node **config); + +/** + * @brief Deletes a Call Home SSH truststore reference from the YANG data. + * + * @param[in] client_name Identifier of an existing Call Home client. + * @param[in] endpt_name Identifier of an existing endpoint that belongs to the given CH client. + * @param[in] user_name Identifier of an existing SSH user that belongs to the given CH endpoint. + * @param[in,out] config Modified configuration YANG data tree. + * @return 0 on success, non-zero otherwise. + */ +int nc_server_config_del_ch_ssh_truststore_ref(const char *client_name, const char *endpt_name, + const char *user_name, struct lyd_node **config); + +/** + * @} SSH Call Home Server Configuration + */ + +/** + * @defgroup server_config_ch_tls TLS Call Home Server Configuration + * @ingroup server_config_ch + * + * @brief TLS Call Home server configuration creation and deletion + * @{ + */ + +/** + * @brief Creates new YANG configuration data nodes for a Call Home server's certificate. + * + * @param[in] ctx libyang context. + * @param[in] client_name Arbitrary identifier of the Call Home client. + * If a Call Home client with this identifier already exists, its contents will be changed. + * @param[in] endpt_name Arbitrary identifier of the Call Home client's endpoint. + * If a Call Home client's endpoint with this identifier already exists, its contents will be changed. + * @param[in] privkey_path Path to the server's PEM encoded private key file. + * @param[in] pubkey_path Optional path to the server's public key file. If not provided, + * it will be generated from the private key. + * @param[in] cert_path Path to the server's certificate file. + * @param[in,out] config Configuration YANG data tree. If *config is NULL, it will be created. + * Otherwise the new YANG data will be added to the previous data and may override it. + * @return 0 on success, non-zero otherwise. + */ +int nc_server_config_add_ch_tls_server_cert(const struct ly_ctx *ctx, const char *client_name, const char *endpt_name, + const char *privkey_path, const char *pubkey_path, const char *cert_path, struct lyd_node **config); + +/** + * @brief Deletes a Call Home server certificate from the YANG data. + * + * @param[in] client_name Identifier of an existing Call Home client. + * @param[in] endpt_name Identifier of an existing Call Home endpoint that belongs to the given client. + * @param[in,out] config Modified configuration YANG data tree. + * @return 0 on success, non-zero otherwise. + */ +int nc_server_config_del_ch_tls_server_cert(const char *client_name, const char *endpt_name, + struct lyd_node **config); + +/** + * @brief Creates new YANG configuration data nodes for a keystore reference to the Call Home TLS server's certificate. + * + * @param[in] ctx libyang context. + * @param[in] client_name Arbitrary identifier of the Call Home client. + * If a Call Home client with this identifier already exists, its contents will be changed. + * @param[in] endpt_name Arbitrary identifier of the Call Home client's endpoint. + * If a Call Home client's endpoint with this identifier already exists, its contents will be changed. + * @param[in] asym_key_ref Name of the asymmetric key pair in the keystore to be referenced. + * @param[in] cert_ref Name of the certificate, which must belong to the given asymmetric key pair, to be referenced. + * @param[in,out] config Configuration YANG data tree. If *config is NULL, it will be created. + * Otherwise the new YANG data will be added to the previous data and may override it. + * @return 0 on success, non-zero otherwise. + */ +int nc_server_config_add_ch_tls_keystore_ref(const struct ly_ctx *ctx, const char *client_name, + const char *endpt_name, const char *asym_key_ref, const char *cert_ref, struct lyd_node **config); + +/** + * @brief Deletes a TLS server certificate keystore reference from the YANG data. + * + * @param[in] client_name Identifier of an existing Call Home client. + * @param[in] endpt_name Identifier of an existing Call Home endpoint that belongs to the given client. + * @param[in,out] config Modified configuration YANG data tree. + * @return 0 on success, non-zero otherwise. + */ +int nc_server_config_del_ch_tls_keystore_ref(const char *client_name, const char *endpt_name, + struct lyd_node **config); + +/** + * @brief Creates new YANG configuration data nodes for a Call Home client's (end-entity) certificate. + * + * @param[in] ctx libyang context. + * @param[in] client_name Arbitrary identifier of the Call Home client. + * If a Call Home client with this identifier already exists, its contents will be changed. + * @param[in] endpt_name Arbitrary identifier of the Call Home client's endpoint. + * If a Call Home client's endpoint with this identifier already exists, its contents will be changed. + * @param[in] cert_name Arbitrary identifier of the Call Home endpoint's end-entity certificate. + * If an Call Home endpoint's end-entity certificate with this identifier already exists, its contents will be changed. + * @param[in] cert_path Path to the certificate file. + * @param[in,out] config Configuration YANG data tree. If *config is NULL, it will be created. + * Otherwise the new YANG data will be added to the previous data and may override it. + * @return 0 on success, non-zero otherwise. + */ +int nc_server_config_add_ch_tls_client_cert(const struct ly_ctx *ctx, const char *client_name, const char *endpt_name, + const char *cert_name, const char *cert_path, struct lyd_node **config); + +/** + * @brief Deletes a Call Home client (end-entity) certificate from the YANG data. + * + * @param[in] client_name Identifier of an existing Call Home client. + * @param[in] endpt_name Identifier of an existing Call Home endpoint that belongs to the given client. + * @param[in] cert_name Optional identifier of a client certificate to be deleted. + * If NULL, all of the client certificates will be deleted. + * @param[in,out] config Modified configuration YANG data tree. + * @return 0 on success, non-zero otherwise. + */ +int nc_server_config_del_ch_tls_client_cert(const char *client_name, const char *endpt_name, + const char *cert_name, struct lyd_node **config); + +/** + * @brief Creates new YANG configuration data nodes for a Call Home truststore reference to a set of client (end-entity) certificates. + * + * @param[in] ctx libyang context. + * @param[in] client_name Arbitrary identifier of the Call Home client. + * If a Call Home client with this identifier already exists, its contents will be changed. + * @param[in] endpt_name Arbitrary identifier of the Call Home client's endpoint. + * If a Call Home client's endpoint with this identifier already exists, its contents will be changed. + * @param[in] cert_bag_ref Identifier of the certificate bag in the truststore to be referenced. + * @param[in,out] config Configuration YANG data tree. If *config is NULL, it will be created. + * Otherwise the new YANG data will be added to the previous data and may override it. + * @return 0 on success, non-zero otherwise. + */ +int nc_server_config_add_ch_tls_client_cert_truststore_ref(const struct ly_ctx *ctx, const char *client_name, + const char *endpt_name, const char *cert_bag_ref, struct lyd_node **config); + +/** + * @brief Deletes a Call Home client (end-entity) certificates truststore reference from the YANG data. + * + * @param[in] client_name Identifier of an existing Call Home client. + * @param[in] endpt_name Identifier of an existing Call Home endpoint that belongs to the given client. + * @param[in,out] config Modified configuration YANG data tree. + * @return 0 on success, non-zero otherwise. + */ +int nc_server_config_del_ch_tls_client_cert_truststore_ref(const char *client_name, const char *endpt_name, + struct lyd_node **config); + +/** + * @brief Creates new YANG configuration data nodes for a client certificate authority (trust-anchor) certificate. + * + * @param[in] ctx libyang context. + * @param[in] client_name Arbitrary identifier of the Call Home client. + * If a Call Home client with this identifier already exists, its contents will be changed. + * @param[in] endpt_name Arbitrary identifier of the Call Home client's endpoint. + * If a Call Home client's endpoint with this identifier already exists, its contents will be changed. + * @param[in] cert_name Arbitrary identifier of the Call Home endpoint's certificate authority certificate. + * If an Call Home endpoint's CA certificate with this identifier already exists, its contents will be changed. + * @param[in] cert_path Path to the certificate file. + * @param[in,out] config Configuration YANG data tree. If *config is NULL, it will be created. + * Otherwise the new YANG data will be added to the previous data and may override it. + * @return 0 on success, non-zero otherwise. + */ +int nc_server_config_add_ch_tls_ca_cert(const struct ly_ctx *ctx, const char *client_name, const char *endpt_name, + const char *cert_name, const char *cert_path, struct lyd_node **config); + +/** + * @brief Deletes a Call Home client certificate authority (trust-anchor) certificate from the YANG data. + * + * @param[in] client_name Identifier of an existing Call Home client. + * @param[in] endpt_name Identifier of an existing Call Home endpoint that belongs to the given client. + * @param[in] cert_name Optional identifier of a CA certificate to be deleted. + * If NULL, all of the CA certificates will be deleted. + * @param[in,out] config Modified configuration YANG data tree. + * @return 0 on success, non-zero otherwise. + */ +int nc_server_config_del_ch_tls_ca_cert(const char *client_name, const char *endpt_name, + const char *cert_name, struct lyd_node **config); + +/** + * @brief Creates new YANG configuration data nodes for a Call Home truststore reference to a set of client certificate authority (trust-anchor) certificates. + * + * @param[in] ctx libyang context. + * @param[in] client_name Arbitrary identifier of the Call Home client. + * If a Call Home client with this identifier already exists, its contents will be changed. + * @param[in] endpt_name Arbitrary identifier of the Call Home client's endpoint. + * If a Call Home client's endpoint with this identifier already exists, its contents will be changed. + * @param[in] cert_bag_ref Identifier of the certificate bag in the truststore to be referenced. + * @param[in,out] config Configuration YANG data tree. If *config is NULL, it will be created. + * Otherwise the new YANG data will be added to the previous data and may override it. + * @return 0 on success, non-zero otherwise. + */ +int nc_server_config_add_ch_tls_ca_cert_truststore_ref(const struct ly_ctx *ctx, const char *client_name, + const char *endpt_name, const char *cert_bag_ref, struct lyd_node **config); + +/** + * @brief Deletes a Call Home client certificate authority (trust-anchor) certificates truststore reference from the YANG data. + * + * @param[in] client_name Identifier of an existing Call Home client. + * @param[in] endpt_name Identifier of an existing Call Home endpoint that belongs to the given client. + * @param[in,out] config Modified configuration YANG data tree. + * @return 0 on success, non-zero otherwise. + */ +int nc_server_config_del_ch_tls_ca_cert_truststore_ref(const char *client_name, const char *endpt_name, + struct lyd_node **config); + +/** + * @brief Creates new YANG configuration data nodes for a Call Home cert-to-name entry. + * + * @param[in] ctx libyang context. + * @param[in] client_name Arbitrary identifier of the Call Home client. + * If a Call Home client with this identifier already exists, its contents will be changed. + * @param[in] endpt_name Arbitrary identifier of the Call Home client's endpoint. + * If a Call Home client's endpoint with this identifier already exists, its contents will be changed. + * @param[in] id ID of the entry. The lower the ID, the higher the priority of the entry (it will be checked earlier). + * @param[in] fingerprint Optional fingerprint of the entry. The fingerprint should always be set, however if it is + * not set, it will match any certificate. Entry with no fingerprint should therefore be placed only as the last entry. + * @param[in] map_type Mapping username to the certificate option. + * @param[in] name Username for this cert-to-name entry. + * @param[in,out] config Configuration YANG data tree. If *config is NULL, it will be created. + * Otherwise the new YANG data will be added to the previous data and may override it. + * @return 0 on success, non-zero otherwise. + */ +int nc_server_config_add_ch_tls_ctn(const struct ly_ctx *ctx, const char *client_name, const char *endpt_name, + uint32_t id, const char *fingerprint, NC_TLS_CTN_MAPTYPE map_type, const char *name, struct lyd_node **config); + +/** + * @brief Deletes a Call Home cert-to-name entry from the YANG data. + * + * @param[in] client_name Identifier of an existing Call Home client. + * @param[in] endpt_name Identifier of an existing Call Home endpoint that belongs to the given client. + * @param[in] id Optional identifier of the Call Home CTN entry to be deleted. + * If 0, all of the CTN entries will be deleted. + * @param[in,out] config Modified configuration YANG data tree. + * @return 0 on success, non-zero otherwise. + */ +int nc_server_config_del_ch_tls_ctn(const char *client_name, const char *endpt_name, + uint32_t id, struct lyd_node **config); + +/** + * @} TLS Call Home Server Configuration + */ + +#endif /* NC_ENABLED_SSH_TLS */ + +#ifdef __cplusplus +} +#endif + +#endif /* NC_SESSION_SERVER_H_ */ diff --git a/src/server_config_ks.c b/src/server_config_ks.c new file mode 100644 index 00000000..9540f3d5 --- /dev/null +++ b/src/server_config_ks.c @@ -0,0 +1,447 @@ +/** + * @file server_config_ks.c + * @author Roman Janota + * @brief libnetconf2 keystore configuration functions + * + * @copyright + * Copyright (c) 2023 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 "compat.h" +#include "log_p.h" +#include "server_config_p.h" +#include "session_p.h" + +extern struct nc_server_opts server_opts; + +/** + * @brief Get the pointer to an asymmetric key structure based on node's location in the YANG data. + * + * @param[in] node Node from which the asymmetric key containing this node is derived. + * @param[out] askey Asymmetric key containing the node. + * @return 0 on success, 1 on error. + */ +static int +nc_server_config_get_asymmetric_key(const struct lyd_node *node, struct nc_asymmetric_key **askey) +{ + uint16_t i; + const char *askey_name; + struct nc_keystore *ks; + const char *node_name = LYD_NAME(node); + + assert(node && askey); + + while (node) { + if (!strcmp(LYD_NAME(node), "asymmetric-key")) { + break; + } + node = lyd_parent(node); + } + + if (!node) { + ERR(NULL, "Node \"%s\" is not contained in an asymmetric-key subtree.", node_name); + return 1; + } + + node = lyd_child(node); + assert(!strcmp(LYD_NAME(node), "name")); + askey_name = lyd_get_value(node); + + ks = &server_opts.keystore; + for (i = 0; i < ks->asym_key_count; i++) { + if (!strcmp(ks->asym_keys[i].name, askey_name)) { + *askey = &ks->asym_keys[i]; + return 0; + } + } + + ERR(NULL, "Asymmetric key \"%s\" was not found.", askey_name); + return 1; +} + +/** + * @brief Get the pointer to a certificate structure based on node's location in the YANG data. + * + * @param[in] node Node from which the certificate containing this node is derived. + * @param[out] cert Certificate containing the node. + * @return 0 on success, 1 on error. + */ +static int +nc_server_config_get_certificate(const struct lyd_node *node, struct nc_certificate **cert) +{ + uint16_t i; + const char *cert_name; + struct nc_asymmetric_key *askey; + const char *node_name = LYD_NAME(node); + + assert(node && cert); + + if (nc_server_config_get_asymmetric_key(node, &askey)) { + return 1; + } + + while (node) { + if (!strcmp(LYD_NAME(node), "certificate")) { + break; + } + node = lyd_parent(node); + } + + if (!node) { + ERR(NULL, "Node \"%s\" is not contained in a certificate subtree.", node_name); + return 1; + } + + node = lyd_child(node); + assert(!strcmp(LYD_NAME(node), "name")); + cert_name = lyd_get_value(node); + + for (i = 0; i < askey->cert_count; i++) { + if (!strcmp(askey->certs[i].name, cert_name)) { + *cert = &askey->certs[i]; + return 0; + } + } + + ERR(NULL, "Certificate \"%s\" was not found.", cert_name); + return 1; +} + +static void +nc_server_config_ks_del_asymmetric_key_cert(struct nc_asymmetric_key *key, struct nc_certificate *cert) +{ + free(cert->name); + free(cert->data); + + key->cert_count--; + if (!key->cert_count) { + free(key->certs); + key->certs = NULL; + } else if (cert != &key->certs[key->cert_count]) { + memcpy(cert, &key->certs[key->cert_count], sizeof *key->certs); + } +} + +static void +nc_server_config_ks_del_asymmetric_key(struct nc_asymmetric_key *key) +{ + uint16_t i, cert_count; + struct nc_keystore *ks = &server_opts.keystore; + + free(key->name); + free(key->pubkey_data); + free(key->privkey_data); + + cert_count = key->cert_count; + for (i = 0; i < cert_count; i++) { + nc_server_config_ks_del_asymmetric_key_cert(key, &key->certs[i]); + } + + ks->asym_key_count--; + if (!ks->asym_key_count) { + free(ks->asym_keys); + ks->asym_keys = NULL; + } else if (key != &ks->asym_keys[ks->asym_key_count]) { + memcpy(key, &ks->asym_keys[ks->asym_key_count], sizeof *ks->asym_keys); + } +} + +static int +nc_server_config_ks_asymmetric_keys(const struct lyd_node *node, NC_OPERATION op) +{ + struct nc_keystore *ks = &server_opts.keystore; + uint16_t i, asym_key_count; + + (void) node; + + if (op == NC_OP_DELETE) { + asym_key_count = ks->asym_key_count; + for (i = 0; i < asym_key_count; i++) { + nc_server_config_ks_del_asymmetric_key(&ks->asym_keys[i]); + } + } + + return 0; +} + +int +nc_server_config_ks_keystore(const struct lyd_node *node, NC_OPERATION op) +{ + (void) node; + + if (op == NC_OP_DELETE) { + nc_server_config_ks_asymmetric_keys(NULL, NC_OP_DELETE); + } + + return 0; +} + +static int +nc_server_config_ks_create_asymmetric_key(const struct lyd_node *node) +{ + struct nc_keystore *ks = &server_opts.keystore; + + node = lyd_child(node); + assert(!strcmp(LYD_NAME(node), "name")); + + return nc_server_config_realloc(lyd_get_value(node), (void **)&ks->asym_keys, sizeof *ks->asym_keys, &ks->asym_key_count); +} + +static int +nc_server_config_ks_asymmetric_key(const struct lyd_node *node, NC_OPERATION op) +{ + int ret = 0; + struct nc_asymmetric_key *key; + + assert(!strcmp(LYD_NAME(node), "asymmetric-key")); + + if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) { + ret = nc_server_config_ks_create_asymmetric_key(node); + } else { + if (nc_server_config_get_asymmetric_key(node, &key)) { + ret = 1; + goto cleanup; + } + + nc_server_config_ks_del_asymmetric_key(key); + } + +cleanup: + return ret; +} + +static int +nc_server_config_ks_public_key_format(const struct lyd_node *node, NC_OPERATION op) +{ + struct nc_asymmetric_key *key; + const char *format; + + (void) op; + + assert(!strcmp(LYD_NAME(node), "public-key-format")); + + if (nc_server_config_get_asymmetric_key(node, &key)) { + return 1; + } + + format = ((struct lyd_node_term *)node)->value.ident->name; + if (!strcmp(format, "ssh-public-key-format")) { + key->pubkey_type = NC_PUBKEY_FORMAT_SSH; + } else if (!strcmp(format, "subject-public-key-info-format")) { + key->pubkey_type = NC_PUBKEY_FORMAT_X509; + } else { + ERR(NULL, "Public key format (%s) not supported.", format); + } + + return 0; +} + +static int +nc_server_config_ks_public_key(const struct lyd_node *node, NC_OPERATION op) +{ + struct nc_asymmetric_key *key; + + (void) op; + + assert(!strcmp(LYD_NAME(node), "public-key")); + + if (nc_server_config_get_asymmetric_key(node, &key)) { + return 1; + } + + /* replace the pubkey */ + free(key->pubkey_data); + key->pubkey_data = strdup(lyd_get_value(node)); + NC_CHECK_ERRMEM_RET(!key->pubkey_data, 1); + + return 0; +} + +static int +nc_server_config_ks_private_key_format(const struct lyd_node *node, NC_OPERATION op) +{ + struct nc_asymmetric_key *key; + const char *format; + NC_PRIVKEY_FORMAT privkey_type; + + (void) op; + + assert(!strcmp(LYD_NAME(node), "private-key-format")); + + if (nc_server_config_get_asymmetric_key(node, &key)) { + return 1; + } + + format = ((struct lyd_node_term *)node)->value.ident->name; + if (!format) { + return 1; + } + + privkey_type = nc_server_config_get_private_key_type(format); + if (privkey_type == NC_PRIVKEY_FORMAT_UNKNOWN) { + return 1; + } + key->privkey_type = privkey_type; + + return 0; +} + +static int +nc_server_config_ks_cleartext_private_key(const struct lyd_node *node, NC_OPERATION op) +{ + struct nc_asymmetric_key *key; + + assert(!strcmp(LYD_NAME(node), "cleartext-private-key")); + + if (nc_server_config_get_asymmetric_key(node, &key)) { + return 1; + } + + if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) { + /* replace the privkey */ + free(key->privkey_data); + key->privkey_data = strdup(lyd_get_value(node)); + NC_CHECK_ERRMEM_RET(!key->privkey_data, 1); + } else if (op == NC_OP_DELETE) { + free(key->privkey_data); + key->privkey_data = NULL; + } + + return 0; +} + +static int +nc_server_config_ks_create_certificate(const struct lyd_node *node, struct nc_asymmetric_key *key) +{ + node = lyd_child(node); + assert(!strcmp(LYD_NAME(node), "name")); + + return nc_server_config_realloc(lyd_get_value(node), (void **)&key->certs, sizeof *key->certs, &key->cert_count); +} + +static int +nc_server_config_ks_certificate(const struct lyd_node *node, NC_OPERATION op) +{ + int ret = 0; + struct nc_asymmetric_key *key; + struct nc_certificate *cert; + + assert(!strcmp(LYD_NAME(node), "certificate")); + + if (nc_server_config_get_asymmetric_key(node, &key)) { + ret = 1; + goto cleanup; + } + + if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) { + ret = nc_server_config_ks_create_certificate(node, key); + } else { + if (nc_server_config_get_certificate(node, &cert)) { + ret = 1; + goto cleanup; + } + + nc_server_config_ks_del_asymmetric_key_cert(key, cert); + } + +cleanup: + return ret; +} + +static int +nc_server_config_ks_cert_data(const struct lyd_node *node, NC_OPERATION op) +{ + struct nc_certificate *cert; + + assert(!strcmp(LYD_NAME(node), "cert-data")); + + if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) { + if (nc_server_config_get_certificate(node, &cert)) { + return 1; + } + + /* replace the cert data */ + free(cert->data); + cert->data = strdup(lyd_get_value(node)); + NC_CHECK_ERRMEM_RET(!cert->data, 1); + } + + return 0; +} + +int +nc_server_config_parse_keystore(const struct lyd_node *node, NC_OPERATION op) +{ + const char *name = LYD_NAME(node); + int ret = 0; + + if (!strcmp(name, "keystore")) { + ret = nc_server_config_ks_keystore(node, op); + } else if (!strcmp(name, "asymmetric-keys")) { + ret = nc_server_config_ks_asymmetric_keys(node, op); + } else if (!strcmp(name, "asymmetric-key")) { + ret = nc_server_config_ks_asymmetric_key(node, op); + } else if (!strcmp(name, "public-key-format")) { + ret = nc_server_config_ks_public_key_format(node, op); + } else if (!strcmp(name, "public-key")) { + ret = nc_server_config_ks_public_key(node, op); + } else if (!strcmp(name, "private-key-format")) { + ret = nc_server_config_ks_private_key_format(node, op); + } else if (!strcmp(name, "cleartext-private-key")) { + ret = nc_server_config_ks_cleartext_private_key(node, op); + } else if (!strcmp(name, "certificate")) { + ret = nc_server_config_ks_certificate(node, op); + } else if (!strcmp(name, "cert-data")) { + ret = nc_server_config_ks_cert_data(node, op); + } + + if (ret) { + ERR(NULL, "Configuring (%s) failed.", name); + return 1; + } + + return 0; +} + +int +nc_server_config_fill_keystore(const struct lyd_node *data, NC_OPERATION op) +{ + int ret = 0; + uint32_t prev_lo; + struct lyd_node *tree; + + /* silently search for nodes, some of them may not be present */ + prev_lo = ly_log_options(0); + + ret = lyd_find_path(data, "/ietf-keystore:keystore", 0, &tree); + if (ret || (tree->flags & LYD_DEFAULT)) { + /* not found */ + ret = 0; + goto cleanup; + } + + if (nc_server_config_parse_tree(tree, op, NC_MODULE_KEYSTORE)) { + ret = 1; + goto cleanup; + } + +cleanup: + /* reset the logging options back to what they were */ + ly_log_options(prev_lo); + return ret; +} diff --git a/src/server_config_p.h b/src/server_config_p.h new file mode 100644 index 00000000..15584257 --- /dev/null +++ b/src/server_config_p.h @@ -0,0 +1,157 @@ +/** + * @file server_config_p.h + * @author Roman Janota + * @brief libnetconf2 server configuration + * + * @copyright + * Copyright (c) 2023 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 NC_CONFIG_SERVER_P_H_ +#define NC_CONFIG_SERVER_P_H_ + +#include +#include +#include + +#include "session_p.h" + +/** + * Enumeration of ietf-netconf-server's modules/trees (top-level containers) + */ +typedef enum { + NC_MODULE_NETCONF_SERVER, + NC_MODULE_KEYSTORE, + NC_MODULE_TRUSTSTORE +} NC_MODULE; + +#ifdef NC_ENABLED_SSH_TLS + +/** + * @brief Get private key type from YANG identity stored in a string. + * + * @param[in] format Value of the YANG identityref. + * @return Private key format on success, NC_PRIVKEY_FORMAT_UNKNOWN otherwise. + */ +NC_PRIVKEY_FORMAT nc_server_config_get_private_key_type(const char *format); + +#endif /* NC_ENABLED_SSH_TLS */ + +/** + * @brief Compares the nth-parent name. + * + * @param[in] node Node of which nth-parent to compare. + * @param[in] parent_count Count of parents. + * @param[in] parent_name Expected name of the parent. + * @return 1 if the name matches, 0 otherwise. + */ +int equal_parent_name(const struct lyd_node *node, uint16_t parent_count, const char *parent_name); + +/** + * @brief Generic realloc function for arrays of structures representing YANG lists whose first member is the key (char *) + * + * @param[in] key_value Value of the key, which will be assigned to the first member of the given struct. + * @param[in] size Size of a member of the array. + * @param[in,out] ptr Pointer to the beginning of the given array, which will be reallocated. + * @param[in,out] count Count of members in the array, incremented at the end. + * @return 0 on success, 1 on error. + */ +int nc_server_config_realloc(const char *key_value, void **ptr, size_t size, uint16_t *count); + +/** + * @brief Recursively parse the given tree and apply it's data to the server's configuration. + * + * @param[in] node YANG data tree. + * @param[in] parent_op Operation of the parent. + * @param[in] module Module for which to parse the data - either ietf-netconf-server, ietf-keystore or ietf-truststore + * @return 0 on success, 1 on error. + */ +int nc_server_config_parse_tree(const struct lyd_node *node, NC_OPERATION parent_op, NC_MODULE module); + +/** + * @brief Configures the listen subtree in the ietf-netconf-server module. + * + * @param[in] node Listen YANG data node. + * @param[in] op Operation to be done on the subtree. Only does something if the operation is NC_OP_DELETE. + * @return 0 on success, 1 on error. + */ +int nc_server_config_listen(const struct lyd_node *node, NC_OPERATION op); + +/** + * @brief Configures the Call Home subtree in the ietf-netconf-server module. + * + * @param[in] node call-home YANG data node. + * @param[in] op Operation to be done on the subtree. Only does something if the operation is NC_OP_DELETE. + * @return 0 on success, 1 on error. + */ +int nc_server_config_ch(const struct lyd_node *node, NC_OPERATION op); + +#ifdef NC_ENABLED_SSH_TLS + +/** KEYSTORE **/ + +/** + * @brief Checks if keystore tree is present in the data and if yes, tries to apply it's data. + * + * @param[in] data YANG data tree. + * @param[in] op Operation saying what to do with the top-level node. + * @return 0 either if keystore is not present or if it is and application was successful, 1 on error. + */ +int nc_server_config_fill_keystore(const struct lyd_node *data, NC_OPERATION op); + +/** + * @brief Parse the given node, which belongs to the ietf-keystore subtree, and apply it's data to the server's configuration. + * + * @param[in] node YANG data node. + * @param[in] op Operation saying what to do with the node. + * @return 0 on success, 1 on error. + */ +int nc_server_config_parse_keystore(const struct lyd_node *node, NC_OPERATION op); + +/** + * @brief Configures the keystore subtree in the ietf-keystore module. + * + * @param[in] node Keystore YANG data node. + * @param[in] op Operation to be done on the subtree. Only does something if the operation is NC_OP_DELETE. + * @return 0. + */ +int nc_server_config_ks_keystore(const struct lyd_node *node, NC_OPERATION op); + +/** TRUSTSTORE **/ + +/** + * @brief Checks if truststore tree is present in the data and if yes, tries to apply it's data. + * + * @param[in] data YANG data tree. + * @param[in] op Operation saying what to do with the top-level node. + * @return 0 either if truststore is not present or if it is and application was successful, 1 on error. + */ +int nc_server_config_fill_truststore(const struct lyd_node *data, NC_OPERATION op); + +/** + * @brief Parse the given node, which belongs to the ietf-truststore subtree, and apply it's data to the server's configuration. + * + * @param[in] node YANG data node. + * @param[in] op Operation saying what to do with the node. + * @return 0 on success, 1 on error. + */ +int nc_server_config_parse_truststore(const struct lyd_node *node, NC_OPERATION op); + +/** + * @brief Configures the truststore subtree in the ietf-truststore module. + * + * @param[in] node Truststore YANG data node. + * @param[in] op Operation to be done on the subtree. Only does something if the operation is NC_OP_DELETE. + * @return 0. + */ +int nc_server_config_ts_truststore(const struct lyd_node *node, NC_OPERATION op); + +#endif /* NC_ENABLED_SSH_TLS */ + +#endif /* NC_CONFIG_SERVER_P_H_ */ diff --git a/src/server_config_ts.c b/src/server_config_ts.c new file mode 100644 index 00000000..14f684c3 --- /dev/null +++ b/src/server_config_ts.c @@ -0,0 +1,602 @@ +/** + * @file server_config_ts.c + * @author Roman Janota + * @brief libnetconf2 truststore configuration functions + * + * @copyright + * Copyright (c) 2023 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 "compat.h" +#include "log_p.h" +#include "server_config_p.h" +#include "session_p.h" + +extern struct nc_server_opts server_opts; + +/** + * @brief Get the pointer to a certificate bag structure based on node's location in the YANG data. + * + * @param[in] node Node from which the certificate bag containing this node is derived. + * @param[out] cbag Certificate bag containing the node. + * @return 0 on success, 1 on error. + */ +static int +nc_server_config_get_certificate_bag(const struct lyd_node *node, struct nc_certificate_bag **cbag) +{ + uint16_t i; + const char *cbag_name; + struct nc_truststore *ts; + const char *node_name = LYD_NAME(node); + + assert(node && cbag); + + while (node) { + if (!strcmp(LYD_NAME(node), "certificate-bag")) { + break; + } + node = lyd_parent(node); + } + + if (!node) { + ERR(NULL, "Node \"%s\" is not contained in a certificate-bag subtree.", node_name); + return 1; + } + + node = lyd_child(node); + assert(!strcmp(LYD_NAME(node), "name")); + cbag_name = lyd_get_value(node); + + ts = &server_opts.truststore; + for (i = 0; i < ts->cert_bag_count; i++) { + if (!strcmp(ts->cert_bags[i].name, cbag_name)) { + *cbag = &ts->cert_bags[i]; + return 0; + } + } + + ERR(NULL, "Certificate bag \"%s\" was not found.", cbag_name); + return 1; +} + +/** + * @brief Get the pointer to a certificate structure based on node's location in the YANG data. + * + * @param[in] node Node from which the certificate containing this node is derived. + * @param[out] cert Certificate containing the node. + * @return 0 on success, 1 on error. + */ +static int +nc_server_config_get_certificate(const struct lyd_node *node, struct nc_certificate **cert) +{ + uint16_t i; + const char *cert_name; + struct nc_certificate_bag *cbag; + const char *node_name = LYD_NAME(node); + + assert(node && cert); + + if (nc_server_config_get_certificate_bag(node, &cbag)) { + return 1; + } + + while (node) { + if (!strcmp(LYD_NAME(node), "certificate")) { + break; + } + node = lyd_parent(node); + } + + if (!node) { + ERR(NULL, "Node \"%s\" is not contained in a certificate subtree.", node_name); + return 1; + } + + node = lyd_child(node); + assert(!strcmp(LYD_NAME(node), "name")); + cert_name = lyd_get_value(node); + + for (i = 0; i < cbag->cert_count; i++) { + if (!strcmp(cbag->certs[i].name, cert_name)) { + *cert = &cbag->certs[i]; + return 0; + } + } + + ERR(NULL, "Certificate \"%s\" was not found.", cert_name); + return 1; +} + +/** + * @brief Get the pointer to a public key bag structure based on node's location in the YANG data. + * + * @param[in] node Node from which the public key bag containing this node is derived. + * @param[out] pbag Public key bag containing the node. + * @return 0 on success, 1 on error. + */ +static int +nc_server_config_get_public_key_bag(const struct lyd_node *node, struct nc_public_key_bag **pbag) +{ + uint16_t i; + const char *pbag_name; + struct nc_truststore *ts; + const char *node_name = LYD_NAME(node); + + assert(node && pbag); + + while (node) { + if (!strcmp(LYD_NAME(node), "public-key-bag")) { + break; + } + node = lyd_parent(node); + } + + if (!node) { + ERR(NULL, "Node \"%s\" is not contained in a public-key-bag subtree.", node_name); + return 1; + } + + node = lyd_child(node); + assert(!strcmp(LYD_NAME(node), "name")); + pbag_name = lyd_get_value(node); + + ts = &server_opts.truststore; + for (i = 0; i < ts->pub_bag_count; i++) { + if (!strcmp(ts->pub_bags[i].name, pbag_name)) { + *pbag = &ts->pub_bags[i]; + return 0; + } + } + + ERR(NULL, "Public key bag \"%s\" was not found.", pbag_name); + return 1; +} + +/** + * @brief Get the pointer to a public key structure based on node's location in the YANG data. + * + * @param[in] node Node from which the public key containing this node is derived. + * @param[out] pkey Public key containing the node. + * @return 0 on success, 1 on error. + */ +static int +nc_server_config_get_public_key(const struct lyd_node *node, struct nc_public_key **pkey) +{ + uint16_t i; + const char *pkey_name; + struct nc_public_key_bag *pbag; + const char *node_name = LYD_NAME(node); + + assert(node && pkey); + + if (nc_server_config_get_public_key_bag(node, &pbag)) { + return 1; + } + + while (node) { + if (!strcmp(LYD_NAME(node), "public-key")) { + if (lyd_child(node)) { + /* check if it's not the leaf public-key, only case about the list */ + break; + } + } + + node = lyd_parent(node); + } + + if (!node) { + ERR(NULL, "Node \"%s\" is not contained in a public-key subtree.", node_name); + return 1; + } + + node = lyd_child(node); + assert(!strcmp(LYD_NAME(node), "name")); + pkey_name = lyd_get_value(node); + + for (i = 0; i < pbag->pubkey_count; i++) { + if (!strcmp(pbag->pubkeys[i].name, pkey_name)) { + *pkey = &pbag->pubkeys[i]; + return 0; + } + } + + ERR(NULL, "Public key \"%s\" was not found.", pkey_name); + return 1; +} + +static void +nc_server_config_ts_del_certificate(struct nc_certificate_bag *cbag, struct nc_certificate *cert) +{ + free(cert->name); + free(cert->data); + + cbag->cert_count--; + if (!cbag->cert_count) { + free(cbag->certs); + cbag->certs = NULL; + } else if (cert != &cbag->certs[cbag->cert_count]) { + memcpy(cert, &cbag->certs[cbag->cert_count], sizeof *cbag->certs); + } +} + +static void +nc_server_config_ts_del_public_key(struct nc_public_key_bag *pbag, struct nc_public_key *pkey) +{ + free(pkey->name); + free(pkey->data); + + pbag->pubkey_count--; + if (!pbag->pubkey_count) { + free(pbag->pubkeys); + pbag->pubkeys = NULL; + } else if (pkey != &pbag->pubkeys[pbag->pubkey_count]) { + memcpy(pkey, &pbag->pubkeys[pbag->pubkey_count], sizeof *pbag->pubkeys); + } +} + +static void +nc_server_config_ts_del_certificate_bag(struct nc_certificate_bag *cbag) +{ + uint16_t i, cert_count; + struct nc_truststore *ts = &server_opts.truststore; + + free(cbag->name); + + cert_count = cbag->cert_count; + for (i = 0; i < cert_count; i++) { + nc_server_config_ts_del_certificate(cbag, &cbag->certs[i]); + } + + ts->cert_bag_count--; + if (!ts->cert_bag_count) { + free(ts->cert_bags); + ts->cert_bags = NULL; + } else if (cbag != &ts->cert_bags[ts->cert_bag_count]) { + memcpy(cbag, &ts->cert_bags[ts->cert_bag_count], sizeof *ts->cert_bags); + } +} + +static void +nc_server_config_ts_del_public_key_bag(struct nc_public_key_bag *pbag) +{ + uint16_t i, pubkey_count; + struct nc_truststore *ts = &server_opts.truststore; + + free(pbag->name); + + pubkey_count = pbag->pubkey_count; + for (i = 0; i < pubkey_count; i++) { + nc_server_config_ts_del_public_key(pbag, &pbag->pubkeys[i]); + } + + ts->pub_bag_count--; + if (!ts->pub_bag_count) { + free(ts->pub_bags); + ts->pub_bags = NULL; + } else if (pbag != &ts->pub_bags[ts->pub_bag_count]) { + memcpy(pbag, &ts->pub_bags[ts->pub_bag_count], sizeof *ts->pub_bags); + } +} + +static int +nc_server_config_ts_certificate_bags(const struct lyd_node *node, NC_OPERATION op) +{ + uint16_t i, cert_bag_count; + struct nc_truststore *ts = &server_opts.truststore; + + (void) node; + + if (op == NC_OP_DELETE) { + cert_bag_count = ts->cert_bag_count; + for (i = 0; i < cert_bag_count; i++) { + nc_server_config_ts_del_certificate_bag(&ts->cert_bags[i]); + } + } + + return 0; +} + +static int +nc_server_config_ts_public_key_bags(const struct lyd_node *node, NC_OPERATION op) +{ + uint16_t i, pub_bag_count; + struct nc_truststore *ts = &server_opts.truststore; + + (void) node; + + if (op == NC_OP_DELETE) { + pub_bag_count = ts->pub_bag_count; + for (i = 0; i < pub_bag_count; i++) { + nc_server_config_ts_del_public_key_bag(&ts->pub_bags[i]); + } + } + + return 0; +} + +int +nc_server_config_ts_truststore(const struct lyd_node *node, NC_OPERATION op) +{ + (void) node; + + if (op == NC_OP_DELETE) { + nc_server_config_ts_certificate_bags(NULL, NC_OP_DELETE); + nc_server_config_ts_public_key_bags(NULL, NC_OP_DELETE); + } + + return 0; +} + +static int +nc_server_config_ts_create_certificate_bag(const struct lyd_node *node) +{ + struct nc_truststore *ts = &server_opts.truststore; + + node = lyd_child(node); + assert(!strcmp(LYD_NAME(node), "name")); + + return nc_server_config_realloc(lyd_get_value(node), (void **)&ts->cert_bags, sizeof *ts->cert_bags, &ts->cert_bag_count); +} + +static int +nc_server_config_ts_certificate_bag(const struct lyd_node *node, NC_OPERATION op) +{ + struct nc_certificate_bag *bag; + + assert(!strcmp(LYD_NAME(node), "certificate-bag")); + + if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) { + if (nc_server_config_ts_create_certificate_bag(node)) { + return 1; + } + } else { + if (nc_server_config_get_certificate_bag(node, &bag)) { + return 1; + } + + nc_server_config_ts_del_certificate_bag(bag); + } + + return 0; +} + +static int +nc_server_config_ts_create_certificate(const struct lyd_node *node, struct nc_certificate_bag *bag) +{ + node = lyd_child(node); + assert(!strcmp(LYD_NAME(node), "name")); + + return nc_server_config_realloc(lyd_get_value(node), (void **)&bag->certs, sizeof *bag->certs, &bag->cert_count); +} + +static int +nc_server_config_ts_certificate(const struct lyd_node *node, NC_OPERATION op) +{ + struct nc_certificate_bag *bag; + struct nc_certificate *cert; + + assert(!strcmp(LYD_NAME(node), "certificate")); + + if (nc_server_config_get_certificate_bag(node, &bag)) { + return 1; + } + + if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) { + if (nc_server_config_ts_create_certificate(node, bag)) { + return 1; + } + } else { + if (nc_server_config_get_certificate(node, &cert)) { + return 1; + } + + nc_server_config_ts_del_certificate(bag, cert); + } + + return 0; +} + +static int +nc_server_config_ts_cert_data(const struct lyd_node *node, NC_OPERATION op) +{ + struct nc_certificate *cert; + + if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) { + if (nc_server_config_get_certificate(node, &cert)) { + return 1; + } + + free(cert->data); + cert->data = strdup(lyd_get_value(node)); + NC_CHECK_ERRMEM_RET(!cert->data, 1); + } + + return 0; +} + +static int +nc_server_config_ts_create_public_key_bag(const struct lyd_node *node) +{ + struct nc_truststore *ts = &server_opts.truststore; + + node = lyd_child(node); + assert(!strcmp(LYD_NAME(node), "name")); + + return nc_server_config_realloc(lyd_get_value(node), (void **)&ts->pub_bags, sizeof *ts->pub_bags, &ts->pub_bag_count); +} + +static int +nc_server_config_ts_public_key_bag(const struct lyd_node *node, NC_OPERATION op) +{ + struct nc_public_key_bag *pbag; + + assert(!strcmp(LYD_NAME(node), "public-key-bag")); + + if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) { + if (nc_server_config_ts_create_public_key_bag(node)) { + return 1; + } + } else { + if (nc_server_config_get_public_key_bag(node, &pbag)) { + return 1; + } + + nc_server_config_ts_del_public_key_bag(pbag); + } + + return 0; +} + +static int +nc_server_config_ts_create_public_key(const struct lyd_node *node, struct nc_public_key_bag *bag) +{ + node = lyd_child(node); + assert(!strcmp(LYD_NAME(node), "name")); + + return nc_server_config_realloc(lyd_get_value(node), (void **)&bag->pubkeys, sizeof *bag->pubkeys, &bag->pubkey_count); +} + +static int +nc_server_config_ts_public_key(const struct lyd_node *node, NC_OPERATION op) +{ + int ret = 0; + struct nc_public_key_bag *bag; + struct nc_public_key *pkey; + + if (nc_server_config_get_public_key_bag(node, &bag)) { + ret = 1; + goto cleanup; + } + + if (equal_parent_name(node, 1, "public-key-bag")) { + /* public-key list */ + if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) { + ret = nc_server_config_ts_create_public_key(node, bag); + if (ret) { + goto cleanup; + } + } else { + if (nc_server_config_get_public_key(node, &pkey)) { + ret = 1; + goto cleanup; + } + + nc_server_config_ts_del_public_key(bag, pkey); + } + } else { + /* public-key leaf */ + if (nc_server_config_get_public_key(node, &pkey)) { + ret = 1; + goto cleanup; + } + + /* replace the public key */ + free(pkey->data); + pkey->data = strdup(lyd_get_value(node)); + NC_CHECK_ERRMEM_GOTO(!pkey->data, ret = 1, cleanup); + } + +cleanup: + return ret; +} + +static int +nc_server_config_ts_public_key_format(const struct lyd_node *node, NC_OPERATION op) +{ + const char *format; + struct nc_public_key *pkey; + + (void) op; + + if (nc_server_config_get_public_key(node, &pkey)) { + return 1; + } + + format = ((struct lyd_node_term *)node)->value.ident->name; + if (!strcmp(format, "ssh-public-key-format")) { + pkey->type = NC_PUBKEY_FORMAT_SSH; + } else if (!strcmp(format, "subject-public-key-info-format")) { + pkey->type = NC_PUBKEY_FORMAT_X509; + } else { + ERR(NULL, "Public key format (%s) not supported.", format); + } + + return 0; +} + +int +nc_server_config_parse_truststore(const struct lyd_node *node, NC_OPERATION op) +{ + const char *name = LYD_NAME(node); + int ret = 0; + + if (!strcmp(name, "truststore")) { + ret = nc_server_config_ts_truststore(node, op); + } else if (!strcmp(name, "certificate-bags")) { + ret = nc_server_config_ts_certificate_bags(node, op); + } else if (!strcmp(name, "certificate-bag")) { + ret = nc_server_config_ts_certificate_bag(node, op); + } else if (!strcmp(name, "certificate")) { + ret = nc_server_config_ts_certificate(node, op); + } else if (!strcmp(name, "cert-data")) { + ret = nc_server_config_ts_cert_data(node, op); + } else if (!strcmp(name, "public-key-bags")) { + ret = nc_server_config_ts_public_key_bags(node, op); + } else if (!strcmp(name, "public-key-bag")) { + ret = nc_server_config_ts_public_key_bag(node, op); + } else if (!strcmp(name, "public-key")) { + ret = nc_server_config_ts_public_key(node, op); + } else if (!strcmp(name, "public-key-format")) { + ret = nc_server_config_ts_public_key_format(node, op); + } + + if (ret) { + ERR(NULL, "Configuring (%s) failed.", name); + return 1; + } + + return 0; +} + +int +nc_server_config_fill_truststore(const struct lyd_node *data, NC_OPERATION op) +{ + int ret = 0; + uint32_t prev_lo; + struct lyd_node *tree; + + /* silently search for nodes, some of them may not be present */ + prev_lo = ly_log_options(0); + + ret = lyd_find_path(data, "/ietf-truststore:truststore", 0, &tree); + if (ret || (tree->flags & LYD_DEFAULT)) { + /* not found */ + ret = 0; + goto cleanup; + } + + if (nc_server_config_parse_tree(tree, op, NC_MODULE_TRUSTSTORE)) { + ret = 1; + goto cleanup; + } + +cleanup: + /* reset the logging options back to what they were */ + ly_log_options(prev_lo); + return ret; +} diff --git a/src/server_config_util.c b/src/server_config_util.c new file mode 100644 index 00000000..914004d0 --- /dev/null +++ b/src/server_config_util.c @@ -0,0 +1,1543 @@ +/** + * @file server_config_util.c + * @author Roman Janota + * @brief libnetconf2 server configuration utilities + * + * @copyright + * Copyright (c) 2023 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 "server_config_util.h" + +#include + +#include +#include +#include +#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" + +int +nc_server_config_create(const struct ly_ctx *ctx, struct lyd_node **tree, const char *value, const char *path_fmt, ...) +{ + int ret = 0; + va_list ap; + char *path = NULL; + + NC_CHECK_ARG_RET(NULL, ctx, tree, path_fmt, 1); + + va_start(ap, path_fmt); + + /* create the path from the format */ + ret = vasprintf(&path, path_fmt, ap); + NC_CHECK_ERRMEM_GOTO(ret == -1, ret = 1; path = NULL, cleanup); + + /* create the nodes in the path */ + if (!*tree) { + ret = lyd_new_path(*tree, ctx, path, value, LYD_NEW_PATH_UPDATE, tree); + } else { + /* this could output NULL if no new nodes, lyd_find_path would fail then */ + ret = lyd_new_path(*tree, ctx, path, value, LYD_NEW_PATH_UPDATE, NULL); + } + if (ret) { + goto cleanup; + } + + /* set the node to the top level node */ + ret = lyd_find_path(*tree, "/ietf-netconf-server:netconf-server", 0, tree); + if (ret) { + goto cleanup; + } + + /* add all default nodes */ + ret = lyd_new_implicit_tree(*tree, LYD_IMPLICIT_NO_STATE, NULL); + if (ret) { + goto cleanup; + } + +cleanup: + free(path); + va_end(ap); + return ret; +} + +int +nc_server_config_append(const struct ly_ctx *ctx, const char *parent_path, const char *child_name, + const char *value, struct lyd_node **tree) +{ + int ret = 0; + char *path = NULL; + + NC_CHECK_ARG_RET(NULL, ctx, parent_path, child_name, tree, 1); + + /* create the path by appending child to the parent path */ + ret = asprintf(&path, "%s/%s", parent_path, child_name); + NC_CHECK_ERRMEM_GOTO(ret == -1, ret = 1; path = NULL, cleanup); + + /* create the nodes in the path */ + if (!*tree) { + ret = lyd_new_path(*tree, ctx, path, value, LYD_NEW_PATH_UPDATE, tree); + } else { + /* this could output NULL if no new nodes, lyd_find_path would fail then */ + ret = lyd_new_path(*tree, ctx, path, value, LYD_NEW_PATH_UPDATE, NULL); + } + if (ret) { + goto cleanup; + } + + /* set the node to the top level node */ + ret = lyd_find_path(*tree, "/ietf-netconf-server:netconf-server", 0, tree); + if (ret) { + goto cleanup; + } + + /* add all default nodes */ + ret = lyd_new_implicit_tree(*tree, LYD_IMPLICIT_NO_STATE, NULL); + if (ret) { + goto cleanup; + } + +cleanup: + free(path); + return ret; +} + +int +nc_server_config_delete(struct lyd_node **tree, const char *path_fmt, ...) +{ + int ret = 0; + va_list ap; + char *path = NULL; + struct lyd_node *sub = NULL; + + NC_CHECK_ARG_RET(NULL, tree, path_fmt, 1); + + va_start(ap, path_fmt); + + /* create the path from the format */ + ret = vasprintf(&path, path_fmt, ap); + NC_CHECK_ERRMEM_GOTO(ret == -1, ret = 1; path = NULL, cleanup); + + /* find the node we want to delete */ + ret = lyd_find_path(*tree, path, 0, &sub); + if (ret) { + goto cleanup; + } + + lyd_free_tree(sub); + + /* set the node to top level container */ + ret = lyd_find_path(*tree, "/ietf-netconf-server:netconf-server", 0, tree); + if (ret) { + goto cleanup; + } + + /* add all default nodes */ + ret = lyd_new_implicit_tree(*tree, LYD_IMPLICIT_NO_STATE, NULL); + if (ret) { + goto cleanup; + } + +cleanup: + free(path); + va_end(ap); + return ret; +} + +int +nc_server_config_check_delete(struct lyd_node **tree, const char *path_fmt, ...) +{ + int ret = 0; + va_list ap; + char *path = NULL; + struct lyd_node *sub = NULL; + + NC_CHECK_ARG_RET(NULL, tree, path_fmt, 1); + + va_start(ap, path_fmt); + + /* create the path from the format */ + ret = vasprintf(&path, path_fmt, ap); + NC_CHECK_ERRMEM_GOTO(ret == -1, ret = 1; path = NULL, cleanup); + + /* find the node we want to delete */ + ret = lyd_find_path(*tree, path, 0, &sub); + if ((ret == LY_EINCOMPLETE) || (ret == LY_ENOTFOUND)) { + ret = 0; + goto cleanup; + } else if (ret) { + ERR(NULL, "Unable to delete node in the path \"%s\".", path); + goto cleanup; + } + + lyd_free_tree(sub); + + /* set the node to top level container */ + ret = lyd_find_path(*tree, "/ietf-netconf-server:netconf-server", 0, tree); + if (ret) { + goto cleanup; + } + +cleanup: + free(path); + va_end(ap); + return ret; +} + +#ifdef NC_ENABLED_SSH_TLS + +const char * +nc_server_config_util_privkey_format_to_identityref(NC_PRIVKEY_FORMAT format) +{ + switch (format) { + case NC_PRIVKEY_FORMAT_RSA: + return "ietf-crypto-types:rsa-private-key-format"; + case NC_PRIVKEY_FORMAT_EC: + return "ietf-crypto-types:ec-private-key-format"; + case NC_PRIVKEY_FORMAT_X509: + return "libnetconf2-netconf-server:private-key-info-format"; + case NC_PRIVKEY_FORMAT_OPENSSH: + return "libnetconf2-netconf-server:openssh-private-key-format"; + default: + ERR(NULL, "Private key type not supported."); + return NULL; + } +} + +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) +{ + int ret = 0; + unsigned char *bin_tmp = NULL; + + NC_CHECK_ARG_RET(NULL, bn, bin, bin_len, 1); + + *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 the highest bit in the MSB is set a byte with the value 0 has to be prepended */ + if (bin_tmp[0] & 0x80) { + *bin = malloc(*bin_len + 1); + NC_CHECK_ERRMEM_GOTO(!*bin, ret = 1, cleanup); + (*bin)[0] = 0; + memcpy(*bin + 1, bin_tmp, *bin_len); + (*bin_len)++; + } else { + *bin = malloc(*bin_len); + NC_CHECK_ERRMEM_GOTO(!*bin, ret = 1, cleanup); + memcpy(*bin, bin_tmp, *bin_len); + } + +cleanup: + free(bin_tmp); + return ret; +} + +/* 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) +{ + int ret = 0, e_len, n_len, p_len, bin_len; + BIGNUM *e = NULL, *n = NULL, *p = 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")) { + /* 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())); + 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)) { + ret = 1; + goto cleanup; + } + + alg_name_len = strlen(algorithm_name); + /* buffer for public key in binary, which looks like this: + * alg_name len (4 bytes), alg_name, PK exponent len (4 bytes), PK exponent, modulus len (4 bytes), modulus + */ + bin_len = 4 + alg_name_len + 4 + e_len + 4 + n_len; + bin = malloc(bin_len); + NC_CHECK_ERRMEM_GOTO(!bin, ret = 1, cleanup); + + /* to network byte order (big endian) */ + alg_name_len_be = htonl(alg_name_len); + e_len_be = htonl(e_len); + n_len_be = htonl(n_len); + + /* create the public key in binary */ + bin_tmp = bin; + memcpy(bin_tmp, &alg_name_len_be, 4); + bin_tmp += 4; + memcpy(bin_tmp, algorithm_name, alg_name_len); + bin_tmp += alg_name_len; + memcpy(bin_tmp, &e_len_be, 4); + bin_tmp += 4; + memcpy(bin_tmp, e_bin, e_len); + bin_tmp += e_len; + 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")) { + /* 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())); + ret = 1; + goto cleanup; + } + + /* get alg and curve names */ + if (!strcmp(ec_group, "P-256") || !strcmp(ec_group, "secp256r1") || !strcmp(ec_group, "prime256v1")) { + algorithm_name = "ecdsa-sha2-nistp256"; + curve_name = "nistp256"; + } else if (!strcmp(ec_group, "P-384") || !strcmp(ec_group, "secp384r1")) { + algorithm_name = "ecdsa-sha2-nistp384"; + curve_name = "nistp384"; + } else if (!strcmp(ec_group, "P-521") || !strcmp(ec_group, "secp521r1")) { + algorithm_name = "ecdsa-sha2-nistp521"; + curve_name = "nistp521"; + } else { + ERR(NULL, "EC group \"%s\" not supported.", ec_group); + ret = 1; + goto cleanup; + } + + /* 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 = 1; + goto cleanup; + } + + /* prepare buffer for converting p to binary */ + p_bin = malloc(BN_num_bytes(p)); + NC_CHECK_ERRMEM_GOTO(!p_bin, ret = 1, cleanup); + /* convert to binary */ + p_len = BN_bn2bin(p, p_bin); + + alg_name_len = strlen(algorithm_name); + curve_name_len = strlen(curve_name); + /* buffer for public key in binary, which looks like so: + * alg_name len (4 bytes), alg_name, curve_name len (4 bytes), curve_name, PK point p len (4 bytes), PK point p + */ + bin_len = 4 + alg_name_len + 4 + curve_name_len + 4 + p_len; + bin = malloc(bin_len); + NC_CHECK_ERRMEM_GOTO(!bin, ret = 1, cleanup); + + /* to network byte order (big endian) */ + alg_name_len_be = htonl(alg_name_len); + curve_name_len_be = htonl(curve_name_len); + p_len_be = htonl(p_len); + + /* create the public key in binary */ + bin_tmp = bin; + memcpy(bin_tmp, &alg_name_len_be, 4); + bin_tmp += 4; + memcpy(bin_tmp, algorithm_name, alg_name_len); + bin_tmp += alg_name_len; + memcpy(bin_tmp, &curve_name_len_be, 4); + bin_tmp += 4; + memcpy(bin_tmp, curve_name, curve_name_len); + bin_tmp += curve_name_len; + 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; + goto cleanup; + } + + /* convert created bin to b64 */ + ret = nc_server_config_util_pubkey_bin_to_b64(bin, bin_len, pubkey); + if (ret) { + ERR(NULL, "Converting public key from binary to base64 failed."); + goto cleanup; + } + +cleanup: + 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) +{ + int ret = 0, len; + BIO *bio = NULL; + char *pub_b64 = 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())); + 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)); + NC_CHECK_ERRMEM_GOTO(!*pubkey, ret = 1, cleanup); + +cleanup: + BIO_free(bio); + 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; + + 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; + } + + 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; + goto cleanup; + } + c[cert_len] = '\0'; + + /* strip the cert of the header and footer */ + *cert = strdup(c + strlen(NC_PEM_CERTIFICATE_HEADER)); + 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); + return ret; +} + +static int +nc_server_config_util_read_pubkey_ssh2(FILE *f, char **pubkey) +{ + char *buffer = NULL; + size_t size = 0, pubkey_len = 0; + void *tmp; + ssize_t read; + int ret = 0; + + NC_CHECK_ARG_RET(NULL, f, pubkey, 1); + + /* read lines from the file and create the public key without NL from it */ + while ((read = getline(&buffer, &size, f)) > 0) { + if (!strncmp(buffer, "----", 4)) { + /* skip header and footer */ + continue; + } + + if (!strncmp(buffer, "Comment:", 8)) { + /* skip a comment */ + continue; + } + + if (buffer[read - 1] == '\n') { + /* avoid NL */ + read--; + } + + tmp = realloc(*pubkey, pubkey_len + read + 1); + NC_CHECK_ERRMEM_GOTO(!tmp, ret = 1, cleanup); + + *pubkey = tmp; + memcpy(*pubkey + pubkey_len, buffer, read); + pubkey_len += read; + } + + if (!pubkey_len) { + ERR(NULL, "Unexpected public key format."); + ret = 1; + goto cleanup; + } + + (*pubkey)[pubkey_len] = '\0'; + +cleanup: + free(buffer); + return ret; +} + +static int +nc_server_config_util_read_pubkey_openssl(FILE *f, char **pubkey) +{ + int ret = 0; + EVP_PKEY *pub_pkey = NULL; + + NC_CHECK_ARG_RET(NULL, f, pubkey, 1); + + /* 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())); + return 1; + } + + ret = nc_server_config_util_evp_pkey_to_ssh_pubkey(pub_pkey, pubkey); + + EVP_PKEY_free(pub_pkey); + return ret; +} + +static int +nc_server_config_util_read_pubkey_libssh(const char *pubkey_path, char **pubkey) +{ + int ret = 0; + ssh_key pub_sshkey = NULL; + + NC_CHECK_ARG_RET(NULL, pubkey_path, pubkey, 1); + + ret = ssh_pki_import_pubkey_file(pubkey_path, &pub_sshkey); + if (ret) { + ERR(NULL, "Importing public key from file \"%s\" failed.", pubkey_path); + return ret; + } + + ret = ssh_pki_export_pubkey_base64(pub_sshkey, pubkey); + if (ret) { + ERR(NULL, "Importing pubkey failed."); + goto cleanup; + } + +cleanup: + ssh_key_free(pub_sshkey); + return 0; +} + +int +nc_server_config_util_get_ssh_pubkey_file(const char *pubkey_path, char **pubkey) +{ + int ret = 0; + FILE *f = NULL; + char *header = NULL; + size_t len = 0; + + 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 header */ + if (getline(&header, &len, f) < 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); + } 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); + } else { + /* it's probably OpenSSH public key */ + ret = nc_server_config_util_read_pubkey_libssh(pubkey_path, pubkey); + } + if (ret) { + ERR(NULL, "Error getting public key from file \"%s\".", pubkey_path); + goto cleanup; + } + +cleanup: + if (f) { + fclose(f); + } + + free(header); + return ret; +} + +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; + + 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())); + return 1; + } + + ret = nc_server_config_util_evp_pkey_to_spki_pubkey(pub_pkey, pubkey); + if (ret) { + goto cleanup; + } + +cleanup: + if (f) { + fclose(f); + } + + EVP_PKEY_free(pub_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) +{ + char *privkey_header = NULL; + size_t len = 0; + + NC_CHECK_ARG_RET(NULL, f_privkey, privkey_path, 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))) { + /* 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))) { + /* 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))) { + /* 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))) { + /* 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); + 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) +{ + int ret = 0, len; + BIO *bio = NULL; + char *priv_b64 = NULL; + + NC_CHECK_ARG_RET(NULL, privkey_path, f_privkey, privkey, pkey, 1); + + bio = BIO_new(BIO_s_mem()); + if (!bio) { + ERR(NULL, "Creating new BIO failed (%s).", ERR_reason_error_string(ERR_get_error())); + ret = 1; + goto cleanup; + } + + /* read the privkey file, create EVP_PKEY */ + *pkey = PEM_read_PrivateKey(f_privkey, NULL, NULL, NULL); + if (!*pkey) { + ERR(NULL, "Getting private key from file \"%s\" failed (%s).", privkey_path, ERR_reason_error_string(ERR_get_error())); + ret = 1; + goto cleanup; + } + + /* write the privkey to bio */ + if (!PEM_write_bio_PrivateKey(bio, *pkey, NULL, NULL, 0, NULL, NULL)) { + ERR(NULL, "Writing private key to BIO failed (%s).", ERR_reason_error_string(ERR_get_error())); + ret = 1; + goto cleanup; + } + + /* 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 = strndup(priv_b64, len); + NC_CHECK_ERRMEM_GOTO(!*privkey, ret = 1, cleanup); + +cleanup: + /* priv_b64 is freed with BIO */ + BIO_free(bio); + return ret; +} + +static int +nc_server_config_util_get_privkey_libssh(const char *privkey_path, char **privkey, EVP_PKEY **pkey) +{ + int ret = 0; + BIO *bio = NULL; + char *priv_b64 = NULL; + ssh_key key = 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); + 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); + 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())); + 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; + +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) +{ + int ret = 0; + FILE *f_privkey = NULL; + char *priv = NULL; + + NC_CHECK_ARG_RET(NULL, privkey_path, privkey_format, privkey, pkey, 1); + + f_privkey = fopen(privkey_path, "r"); + if (!f_privkey) { + ERR(NULL, "Unable to open file \"%s\".", privkey_path); + ret = 1; + 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); + if (ret) { + ERR(NULL, "Getting private key format from file \"%s\" failed.", privkey_path); + goto cleanup; + } + + 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); + break; + case NC_PRIVKEY_FORMAT_OPENSSH: + /* need the help of libssh */ + ret = nc_server_config_util_get_privkey_libssh(privkey_path, &priv, pkey); + /* if the function returned successfully, the key is no longer OpenSSH, it was converted to x509 */ + *privkey_format = NC_PRIVKEY_FORMAT_X509; + break; + default: + ERR(NULL, "Private key format not recognized."); + ret = 1; + break; + } + if (ret) { + 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'; + +cleanup: + if (f_privkey) { + fclose(f_privkey); + } + + free(priv); + return ret; +} + +int +nc_server_config_util_get_asym_key_pair(const char *privkey_path, const char *pubkey_path, NC_PUBKEY_FORMAT wanted_pubkey_format, + char **privkey, NC_PRIVKEY_FORMAT *privkey_type, char **pubkey) +{ + int ret = 0; + EVP_PKEY *priv_pkey = NULL; + + NC_CHECK_ARG_RET(NULL, privkey_path, privkey, privkey_type, pubkey, 1); + + *privkey = NULL; + *pubkey = NULL; + + /* get private key base64 and EVP_PKEY */ + ret = nc_server_config_util_get_privkey(privkey_path, privkey_type, privkey, &priv_pkey); + if (ret) { + ERR(NULL, "Getting private key from file \"%s\" failed.", privkey_path); + goto cleanup; + } + + /* 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); + } else { + ret = nc_server_config_util_evp_pkey_to_spki_pubkey(priv_pkey, pubkey); + } + } else { + if (wanted_pubkey_format == NC_PUBKEY_FORMAT_SSH) { + ret = nc_server_config_util_get_ssh_pubkey_file(pubkey_path, pubkey); + } else { + ret = nc_server_config_util_get_spki_pubkey_file(pubkey_path, pubkey); + } + } + if (ret) { + if (pubkey_path) { + ERR(NULL, "Getting public key from file \"%s\" failed.", pubkey_path); + } else { + ERR(NULL, "Generating public key from private key failed."); + } + goto cleanup; + } + +cleanup: + EVP_PKEY_free(priv_pkey); + return ret; +} + +API int +nc_server_config_add_address_port(const struct ly_ctx *ctx, const char *endpt_name, NC_TRANSPORT_IMPL transport, + const char *address, uint16_t port, struct lyd_node **config) +{ + int ret = 0; + const char *address_fmt, *port_fmt; + char port_buf[6] = {0}; + + NC_CHECK_ARG_RET(NULL, ctx, endpt_name, address, config, 1); + + if (transport == NC_TI_LIBSSH) { + /* SSH path */ + address_fmt = "/ietf-netconf-server:netconf-server/listen/endpoint[name='%s']/ssh/tcp-server-parameters/local-address"; + port_fmt = "/ietf-netconf-server:netconf-server/listen/endpoint[name='%s']/ssh/tcp-server-parameters/local-port"; + } else if (transport == NC_TI_OPENSSL) { + /* TLS path */ + address_fmt = "/ietf-netconf-server:netconf-server/listen/endpoint[name='%s']/tls/tcp-server-parameters/local-address"; + port_fmt = "/ietf-netconf-server:netconf-server/listen/endpoint[name='%s']/tls/tcp-server-parameters/local-port"; + } else { + ERR(NULL, "Can not set address and port of a non SSH/TLS endpoint."); + ret = 1; + goto cleanup; + } + + ret = nc_server_config_create(ctx, config, address, address_fmt, endpt_name); + if (ret) { + goto cleanup; + } + + sprintf(port_buf, "%d", port); + ret = nc_server_config_create(ctx, config, port_buf, port_fmt, endpt_name); + if (ret) { + goto cleanup; + } + +cleanup: + return ret; +} + +API int +nc_server_config_add_ch_address_port(const struct ly_ctx *ctx, const char *client_name, const char *endpt_name, + NC_TRANSPORT_IMPL transport, const char *address, const char *port, struct lyd_node **config) +{ + int ret = 0; + const char *address_fmt, *port_fmt; + + NC_CHECK_ARG_RET(NULL, ctx, client_name, endpt_name, address, port, config, 1); + + if (transport == NC_TI_LIBSSH) { + /* 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) { + /* 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"; + } else { + ERR(NULL, "Transport not supported."); + ret = 1; + goto cleanup; + } + + ret = nc_server_config_create(ctx, config, address, address_fmt, client_name, endpt_name); + if (ret) { + goto cleanup; + } + + ret = nc_server_config_create(ctx, config, port, port_fmt, client_name, endpt_name); + if (ret) { + goto cleanup; + } + +cleanup: + return ret; +} + +API int +nc_server_config_del_endpt(const char *endpt_name, struct lyd_node **config) +{ + NC_CHECK_ARG_RET(NULL, config, 1); + + if (endpt_name) { + return nc_server_config_delete(config, "/ietf-netconf-server:netconf-server/listen/endpoint[name='%s']", endpt_name); + } else { + return nc_server_config_delete(config, "/ietf-netconf-server:netconf-server/listen/endpoint"); + } +} + +API int +nc_server_config_del_ch_client(const char *ch_client_name, struct lyd_node **config) +{ + NC_CHECK_ARG_RET(NULL, config, 1); + + if (ch_client_name) { + return nc_server_config_delete(config, "/ietf-netconf-server:netconf-server/call-home/netconf-client[name='%s']", ch_client_name); + } else { + return nc_server_config_delete(config, "/ietf-netconf-server:netconf-server/call-home/netconf-client"); + } +} + +API int +nc_server_config_del_ch_endpt(const char *client_name, const char *endpt_name, struct lyd_node **config) +{ + NC_CHECK_ARG_RET(NULL, client_name, config, 1); + + if (endpt_name) { + return nc_server_config_delete(config, "/ietf-netconf-server:netconf-server/call-home/netconf-client[name='%s']/" + "endpoints/endpoint[name='%s']", client_name, endpt_name); + } else { + return nc_server_config_delete(config, "/ietf-netconf-server:netconf-server/call-home/netconf-client[name='%s']/" + "endpoints/endpoint", client_name); + } +} + +API int +nc_server_config_add_keystore_asym_key(const struct ly_ctx *ctx, NC_TRANSPORT_IMPL ti, const char *asym_key_name, + const char *privkey_path, const char *pubkey_path, struct lyd_node **config) +{ + int ret = 0; + char *privkey = NULL, *pubkey = NULL; + NC_PRIVKEY_FORMAT privkey_type; + const char *privkey_format, *pubkey_format; + + 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) { + 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) { + ret = nc_server_config_util_get_asym_key_pair(privkey_path, pubkey_path, NC_PUBKEY_FORMAT_X509, &privkey, &privkey_type, &pubkey); + } else { + ERR(NULL, "Only SSH and TLS transports can be used to create an asymmetric key pair in the keystore."); + ret = 1; + goto cleanup; + } + if (ret) { + ERR(NULL, "Getting keys from file(s) failed."); + goto cleanup; + } + + /* get pubkey format str */ + if (ti == NC_TI_LIBSSH) { + pubkey_format = "ietf-crypto-types:ssh-public-key-format"; + } else { + pubkey_format = "ietf-crypto-types:subject-public-key-info-format"; + } + + /* get privkey identityref value */ + privkey_format = nc_server_config_util_privkey_format_to_identityref(privkey_type); + if (!privkey_format) { + ret = 1; + goto cleanup; + } + + ret = nc_server_config_create(ctx, config, pubkey_format, "/ietf-keystore:keystore/asymmetric-keys/" + "asymmetric-key[name='%s']/public-key-format", asym_key_name); + if (ret) { + goto cleanup; + } + + ret = nc_server_config_create(ctx, config, pubkey, "/ietf-keystore:keystore/asymmetric-keys/" + "asymmetric-key[name='%s']/public-key", asym_key_name); + if (ret) { + goto cleanup; + } + + ret = nc_server_config_create(ctx, config, privkey_format, "/ietf-keystore:keystore/asymmetric-keys/" + "asymmetric-key[name='%s']/private-key-format", asym_key_name); + if (ret) { + goto cleanup; + } + + ret = nc_server_config_create(ctx, config, privkey, "/ietf-keystore:keystore/asymmetric-keys/" + "asymmetric-key[name='%s']/cleartext-private-key", asym_key_name); + if (ret) { + goto cleanup; + } + +cleanup: + free(privkey); + free(pubkey); + return ret; +} + +API int +nc_server_config_del_keystore_asym_key(const char *asym_key_name, struct lyd_node **config) +{ + NC_CHECK_ARG_RET(NULL, config, 1); + + if (asym_key_name) { + return nc_server_config_delete(config, "/ietf-keystore:keystore/asymmetric-keys/asymmetric-key[name='%s']", asym_key_name); + } else { + return nc_server_config_delete(config, "/ietf-keystore:keystore/asymmetric-keys/asymmetric-key"); + } +} + +API int +nc_server_config_add_keystore_cert(const struct ly_ctx *ctx, const char *asym_key_name, const char *cert_name, + const char *cert_path, struct lyd_node **config) +{ + int ret = 0; + char *cert = NULL; + + NC_CHECK_ARG_RET(NULL, ctx, asym_key_name, cert_name, cert_path, config, 1); + + /* get cert data */ + ret = nc_server_config_util_read_certificate(cert_path, &cert); + if (ret) { + goto cleanup; + } + + ret = nc_server_config_create(ctx, config, cert, "/ietf-keystore:keystore/asymmetric-keys/" + "asymmetric-key[name='%s']/certificates/certificate[name='%s']/cert-data", asym_key_name, cert_name); + +cleanup: + free(cert); + return ret; +} + +API int +nc_server_config_del_keystore_cert(const char *asym_key_name, const char *cert_name, struct lyd_node **config) +{ + NC_CHECK_ARG_RET(NULL, asym_key_name, config, 1); + + if (cert_name) { + return nc_server_config_delete(config, "/ietf-keystore:keystore/asymmetric-keys/asymmetric-key[name='%s']/" + "certificates/certificate[name='%s']", asym_key_name, cert_name); + } else { + return nc_server_config_delete(config, "/ietf-keystore:keystore/asymmetric-keys/asymmetric-key[name='%s']/" + "certificates/certificate", asym_key_name); + } +} + +API int +nc_server_config_add_truststore_pubkey(const struct ly_ctx *ctx, const char *pub_bag_name, const char *pubkey_name, + const char *pubkey_path, struct lyd_node **config) +{ + int ret = 0; + char *pubkey = NULL; + const char *pubkey_format = "ietf-crypto-types:ssh-public-key-format"; + + NC_CHECK_ARG_RET(NULL, ctx, pub_bag_name, pubkey_name, pubkey_path, config, 1); + + ret = nc_server_config_util_get_ssh_pubkey_file(pubkey_path, &pubkey); + if (ret) { + goto cleanup; + } + + ret = nc_server_config_create(ctx, config, pubkey_format, "/ietf-truststore:truststore/public-key-bags/" + "public-key-bag[name='%s']/public-key[name='%s']/public-key-format", pub_bag_name, pubkey_name); + if (ret) { + goto cleanup; + } + + ret = nc_server_config_create(ctx, config, pubkey, "/ietf-truststore:truststore/public-key-bags/" + "public-key-bag[name='%s']/public-key[name='%s']/public-key", pub_bag_name, pubkey_name); + if (ret) { + goto cleanup; + } + +cleanup: + free(pubkey); + return ret; +} + +API int +nc_server_config_del_truststore_pubkey(const char *pub_bag_name, + const char *pubkey_name, struct lyd_node **config) +{ + NC_CHECK_ARG_RET(NULL, pub_bag_name, config, 1); + + if (pubkey_name) { + return nc_server_config_delete(config, "/ietf-truststore:truststore/public-key-bags/" + "public-key-bag[name='%s']/public-key[name='%s']", pub_bag_name, pubkey_name); + } else { + return nc_server_config_delete(config, "/ietf-truststore:truststore/public-key-bags/" + "public-key-bag[name='%s']/public-key", pub_bag_name); + } +} + +API int +nc_server_config_add_truststore_cert(const struct ly_ctx *ctx, const char *cert_bag_name, const char *cert_name, + const char *cert_path, struct lyd_node **config) +{ + int ret = 0; + char *cert = NULL; + + NC_CHECK_ARG_RET(NULL, ctx, cert_bag_name, cert_name, cert_path, config, 1); + + ret = nc_server_config_util_read_certificate(cert_path, &cert); + if (ret) { + goto cleanup; + } + + ret = nc_server_config_create(ctx, config, cert, "/ietf-truststore:truststore/certificate-bags/" + "certificate-bag[name='%s']/certificate[name='%s']/cert-data", cert_bag_name, cert_name); + if (ret) { + goto cleanup; + } + +cleanup: + free(cert); + return ret; +} + +API int +nc_server_config_del_truststore_cert(const char *cert_bag_name, + const char *cert_name, struct lyd_node **config) +{ + NC_CHECK_ARG_RET(NULL, cert_bag_name, config, 1); + + if (cert_name) { + return nc_server_config_delete(config, "/ietf-truststore:truststore/certificate-bags/" + "certificate-bag[name='%s']/certificate[name='%s']", cert_bag_name, cert_name); + } else { + return nc_server_config_delete(config, "/ietf-truststore:truststore/certificate-bags/" + "certificate-bag[name='%s']/certificate", cert_bag_name); + } +} + +#endif /* NC_ENABLED_SSH_TLS */ + +API int +nc_server_config_add_unix_socket(const struct ly_ctx *ctx, const char *endpt_name, const char *path, + mode_t mode, uid_t uid, gid_t gid, struct lyd_node **config) +{ + int ret = 0; + char *tree_path = NULL; + char buf[12] = {0}; + + NC_CHECK_ARG_RET(NULL, ctx, endpt_name, path, config, 1); + + ret = asprintf(&tree_path, "/ietf-netconf-server:netconf-server/listen/endpoint[name='%s']/libnetconf2-netconf-server:unix-socket", endpt_name); + NC_CHECK_ERRMEM_GOTO(ret == -1, tree_path = NULL; ret = 1, cleanup); + + /* path to unix socket */ + ret = nc_server_config_append(ctx, tree_path, "path", path, config); + if (ret) { + goto cleanup; + } + + /* mode */ + if (mode != (mode_t)-1) { + if (mode > 0777) { + ERR(NULL, "Invalid mode value (%o).", mode); + ret = 1; + goto cleanup; + } + + sprintf(buf, "%o", mode); + ret = nc_server_config_append(ctx, tree_path, "mode", buf, config); + if (ret) { + goto cleanup; + } + } + + /* uid */ + if (uid != (uid_t)-1) { + memset(buf, 0, 12); + sprintf(buf, "%u", uid); + ret = nc_server_config_append(ctx, tree_path, "uid", buf, config); + if (ret) { + goto cleanup; + } + } + + /* gid */ + if (gid != (gid_t)-1) { + memset(buf, 0, 12); + sprintf(buf, "%u", gid); + ret = nc_server_config_append(ctx, tree_path, "gid", buf, config); + if (ret) { + goto cleanup; + } + } + +cleanup: + free(tree_path); + return ret; +} + +API int +nc_server_config_add_ch_persistent(const struct ly_ctx *ctx, const char *ch_client_name, struct lyd_node **config) +{ + NC_CHECK_ARG_RET(NULL, ctx, ch_client_name, config, 1); + + /* delete periodic tree if exists */ + if (nc_server_config_check_delete(config, "/ietf-netconf-server:netconf-server/call-home/" + "netconf-client[name='%s']/connection-type/periodic", ch_client_name)) { + return 1; + } + + return nc_server_config_create(ctx, config, NULL, "/ietf-netconf-server:netconf-server/call-home/" + "netconf-client[name='%s']/connection-type/persistent", ch_client_name); +} + +API int +nc_server_config_add_ch_period(const struct ly_ctx *ctx, const char *ch_client_name, uint16_t period, + struct lyd_node **config) +{ + char buf[6] = {0}; + + NC_CHECK_ARG_RET(NULL, ctx, ch_client_name, config, 1); + + /* delete persistent tree if exists */ + if (nc_server_config_check_delete(config, "/ietf-netconf-server:netconf-server/call-home/" + "netconf-client[name='%s']/connection-type/persistent", ch_client_name)) { + return 1; + } + + sprintf(buf, "%" PRIu16, period); + return nc_server_config_create(ctx, config, buf, "/ietf-netconf-server:netconf-server/call-home/" + "netconf-client[name='%s']/connection-type/periodic/period", ch_client_name); +} + +API int +nc_server_config_del_ch_period(const char *ch_client_name, struct lyd_node **config) +{ + NC_CHECK_ARG_RET(NULL, ch_client_name, config, 1); + + return nc_server_config_delete(config, "/ietf-netconf-server:netconf-server/call-home/" + "netconf-client[name='%s']/connection-type/periodic/period", ch_client_name); +} + +API int +nc_server_config_add_ch_anchor_time(const struct ly_ctx *ctx, const char *ch_client_name, + const char *anchor_time, struct lyd_node **config) +{ + NC_CHECK_ARG_RET(NULL, ctx, ch_client_name, anchor_time, config, 1); + + /* delete persistent tree if exists */ + if (nc_server_config_check_delete(config, "/ietf-netconf-server:netconf-server/call-home/" + "netconf-client[name='%s']/connection-type/persistent", ch_client_name)) { + return 1; + } + + return nc_server_config_create(ctx, config, anchor_time, "/ietf-netconf-server:netconf-server/call-home/" + "netconf-client[name='%s']/connection-type/periodic/anchor-time", ch_client_name); +} + +API int +nc_server_config_del_ch_anchor_time(const char *ch_client_name, struct lyd_node **config) +{ + NC_CHECK_ARG_RET(NULL, ch_client_name, config, 1); + + return nc_server_config_delete(config, "/ietf-netconf-server:netconf-server/call-home/" + "netconf-client[name='%s']/connection-type/periodic/anchor-time", ch_client_name); +} + +API int +nc_server_config_add_ch_idle_timeout(const struct ly_ctx *ctx, const char *ch_client_name, + uint16_t idle_timeout, struct lyd_node **config) +{ + char buf[6] = {0}; + + NC_CHECK_ARG_RET(NULL, ctx, ch_client_name, config, 1); + + /* delete persistent tree if exists */ + if (nc_server_config_check_delete(config, "/ietf-netconf-server:netconf-server/call-home/" + "netconf-client[name='%s']/connection-type/persistent", ch_client_name)) { + return 1; + } + + sprintf(buf, "%" PRIu16, idle_timeout); + return nc_server_config_create(ctx, config, buf, "/ietf-netconf-server:netconf-server/call-home/" + "netconf-client[name='%s']/connection-type/periodic/idle-timeout", ch_client_name); +} + +API int +nc_server_config_del_ch_idle_timeout(const char *ch_client_name, struct lyd_node **config) +{ + NC_CHECK_ARG_RET(NULL, ch_client_name, config, 1); + + return nc_server_config_delete(config, "/ietf-netconf-server:netconf-server/call-home/" + "netconf-client[name='%s']/connection-type/periodic/idle-timeout", ch_client_name); +} + +API int +nc_server_config_add_ch_reconnect_strategy(const struct ly_ctx *ctx, const char *ch_client_name, + NC_CH_START_WITH start_with, uint16_t max_wait, uint8_t max_attempts, struct lyd_node **config) +{ + int ret = 0; + char *path = NULL; + char buf[6] = {0}; + const char *start_with_val; + + NC_CHECK_ARG_RET(NULL, ctx, ch_client_name, config, 1); + + /* prepared the path */ + ret = asprintf(&path, "/ietf-netconf-server:netconf-server/call-home/netconf-client[name='%s']/reconnect-strategy", ch_client_name); + NC_CHECK_ERRMEM_GOTO(ret == -1, path = NULL; ret = 1, cleanup); + + if (start_with) { + /* get string value from enum */ + if (start_with == NC_CH_FIRST_LISTED) { + start_with_val = "first-listed"; + } else if (start_with == NC_CH_LAST_CONNECTED) { + start_with_val = "last-connected"; + } else { + start_with_val = "random-selection"; + } + + ret = nc_server_config_append(ctx, path, "start-with", start_with_val, config); + if (ret) { + goto cleanup; + } + } + + if (max_attempts) { + sprintf(buf, "%" PRIu8, max_attempts); + ret = nc_server_config_append(ctx, path, "max-attempts", buf, config); + if (ret) { + goto cleanup; + } + memset(buf, 0, 6); + } + + if (max_wait) { + sprintf(buf, "%" PRIu16, max_wait); + ret = nc_server_config_append(ctx, path, "max-wait", buf, config); + if (ret) { + goto cleanup; + } + } + +cleanup: + free(path); + return ret; +} + +API int +nc_server_config_del_ch_reconnect_strategy(const char *ch_client_name, struct lyd_node **config) +{ + NC_CHECK_ARG_RET(NULL, ch_client_name, config, 1); + + return nc_server_config_delete(config, "/ietf-netconf-server:netconf-server/call-home/" + "netconf-client[name='%s']/reconnect-strategy", ch_client_name); +} diff --git a/src/server_config_util.h b/src/server_config_util.h new file mode 100644 index 00000000..6048fc4b --- /dev/null +++ b/src/server_config_util.h @@ -0,0 +1,163 @@ +/** + * @file server_config_util.h + * @author Roman Janota + * @brief libnetconf2 server configuration utlities header + * + * @copyright + * Copyright (c) 2023 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 NC_SERVER_CONFIG_UTIL_H_ +#define NC_SERVER_CONFIG_UTIL_H_ + +#include + +#include "session_p.h" + +#ifdef NC_ENABLED_SSH_TLS + +/* private key's pkcs8 header */ +#define NC_PKCS8_PRIVKEY_HEADER "-----BEGIN PRIVATE KEY-----\n" + +/* private key's pkcs8 footer */ +#define NC_PKCS8_PRIVKEY_FOOTER "\n-----END PRIVATE KEY-----\n" + +/* private key's openssh header */ +#define NC_OPENSSH_PRIVKEY_HEADER "-----BEGIN OPENSSH PRIVATE KEY-----\n" + +/* private key's openssh footer */ +#define NC_OPENSSH_PRIVKEY_FOOTER "\n-----END OPENSSH PRIVATE KEY-----\n" + +/* private key's pkcs1 rsa header */ +#define NC_PKCS1_RSA_PRIVKEY_HEADER "-----BEGIN RSA PRIVATE KEY-----\n" + +/* private key's sec1 ec header */ +#define NC_SEC1_EC_PRIVKEY_HEADER "-----BEGIN 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" + +/* private key's footer when getting an EC/RSA privkey from file using libssh */ +#define NC_LIBSSH_PRIVKEY_FOOTER "\n-----END PRIVATE KEY-----\n" + +/* public key's ssh2 header */ +#define NC_SSH2_PUBKEY_HEADER "---- BEGIN SSH2 PUBLIC KEY ----\n" + +/* public key's SubjectPublicKeyInfo format header */ +#define NC_SUBJECT_PUBKEY_INFO_HEADER "-----BEGIN PUBLIC KEY-----\n" + +/* public key's SubjectPublicKeyInfo format footer */ +#define NC_SUBJECT_PUBKEY_INFO_FOOTER "\n-----END PUBLIC KEY-----\n" + +/* certificate's PEM format header */ +#define NC_PEM_CERTIFICATE_HEADER "-----BEGIN CERTIFICATE-----\n" + +/* certificate's PEM format footer */ +#define NC_PEM_CERTIFICATE_FOOTER "\n-----END CERTIFICATE-----\n" + +typedef enum { + NC_ALG_HOSTKEY, + NC_ALG_KEY_EXCHANGE, + NC_ALG_ENCRYPTION, + NC_ALG_MAC +} NC_ALG_TYPE; + +/** + * @brief Gets asymmetric key pair from private key (and optionally public key) file(s). + * + * @param[in] privkey_path Path to private key. + * @param[in] pubkey_path Optional path to public key. If not set, PK will be generated from private key. + * @param[in] wanted_pubkey_type Wanted public key format to be generated (SPKI/SSH) + * @param[out] privkey Base64 encoded private key. + * @param[out] privkey_type Type of the private key. (RSA, EC, etc) + * @param[out] pubkey Base64 encoded public key. + * @return 0 on success, non-zero otherwise. + */ +int nc_server_config_util_get_asym_key_pair(const char *privkey_path, const char *pubkey_path, NC_PUBKEY_FORMAT wanted_pubkey_type, + char **privkey, NC_PRIVKEY_FORMAT *privkey_type, char **pubkey); + +/** + * @brief Gets public key from a file and converts it to the SSH format if need be. + * + * @param[in] pubkey_path Path to the public key. + * @param[out] pubkey Base64 encoded public key. + * + * @return 0 on success, non-zero otherwise. + */ +int nc_server_config_util_get_ssh_pubkey_file(const char *pubkey_path, char **pubkey); + +/** + * @brief Gets a certificate from a file. + * + * @param[in] cert_path Path to the certificate. + * @param[out] cert Base64 PEM encoded certificate data. + * + * @return 0 on success, non-zero otherwise. + */ +int nc_server_config_util_read_certificate(const char *cert_path, char **cert); + +/** + * @brief Converts private key format to its associated identityref value. + * + * @param[in] format Private key format. + * + * @return Identityref on success, NULL on failure. + */ +const char *nc_server_config_util_privkey_format_to_identityref(NC_PRIVKEY_FORMAT format); + +#endif /* NC_ENABLED_SSH_TLS */ + +/** + * @brief Creates YANG data nodes in a path and gives the final node a value. + * + * @param[in] ctx libyang context. + * @param[in, out] tree The YANG data tree where the insertion will happen. On success + * this is set to the top level container. + * @param[in] value Value assigned to the final node in the path. + * @param[in] path_fmt Format of the path. + * @param[in] ... Parameters for the path format, essentially representing the lists' keys. + * @return 0 on success, 1 otherwise. + */ +int nc_server_config_create(const struct ly_ctx *ctx, struct lyd_node **tree, const char *value, const char *path_fmt, ...); + +/** + * @brief Creates a YANG data node by appending it to a specified parent node. + * + * @param[in] ctx libyang context. + * @param[in] parent_path Path to the parent node. + * @param[in] child_name Name of the parent's child node to be created. + * @param[in] value Value given to the child node. + * @param[out] tree YANG data tree where the insertion will happen. On success + * this is set to the top level container. + * @return 0 on success, 1 otherwise. + */ +int nc_server_config_append(const struct ly_ctx *ctx, const char *parent_path, const char *child_name, + const char *value, struct lyd_node **tree); + +/** + * @brief Deletes a subtree from the YANG data. + * + * @param tree YANG data from which the subtree will be deleted. + * @param[in] path_fmt Format of the path. The last node will be the top level node of the deleted tree. + * @param[in] ... Parameters for the path format, essentially representing the lists' keys. + * @return 0 on success, non-zero otherwise. + */ +int nc_server_config_delete(struct lyd_node **tree, const char *path_fmt, ...); + +/** + * @brief Deletes a subtree from the YANG data, but doesn't return an error if the node doesn't exist. + * + * @param tree YANG data from which the subtree will be deleted. + * @param[in] path_fmt Format of the path. The last node will be the top level node of the deleted tree. + * @param[in] ... Parameters for the path format, essentially representing the lists' keys. + * @return 0 on success, non-zero otherwise. + */ +int nc_server_config_check_delete(struct lyd_node **tree, const char *path_fmt, ...); + +#endif /* NC_CONFIG_NEW_H_ */ diff --git a/src/server_config_util_ssh.c b/src/server_config_util_ssh.c new file mode 100644 index 00000000..8b162ee6 --- /dev/null +++ b/src/server_config_util_ssh.c @@ -0,0 +1,694 @@ +/** + * @file server_config_util_ssh.c + * @author Roman Janota + * @brief libnetconf2 server SSH configuration utilities + * + * @copyright + * Copyright (c) 2023 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 "server_config_util.h" + +#include +#include +#include +#include +#include +#include + +#include + +#include "compat.h" +#include "config.h" +#include "log_p.h" +#include "server_config.h" +#include "session_p.h" + +static int +_nc_server_config_add_ssh_hostkey(const struct ly_ctx *ctx, const char *tree_path, + const char *privkey_path, const char *pubkey_path, struct lyd_node **config) +{ + int ret = 0; + char *pubkey = NULL, *privkey = NULL; + NC_PRIVKEY_FORMAT privkey_type; + const char *privkey_format, *pubkey_format = "ietf-crypto-types:ssh-public-key-format"; + + NC_CHECK_ARG_RET(NULL, ctx, tree_path, privkey_path, config, 1); + + /* get the keys as a string from the given files */ + ret = nc_server_config_util_get_asym_key_pair(privkey_path, pubkey_path, NC_PUBKEY_FORMAT_SSH, &privkey, &privkey_type, &pubkey); + if (ret) { + ERR(NULL, "Getting keys from file(s) failed."); + goto cleanup; + } + + /* get privkey identityref value */ + privkey_format = nc_server_config_util_privkey_format_to_identityref(privkey_type); + if (!privkey_format) { + ret = 1; + goto cleanup; + } + + ret = nc_server_config_append(ctx, tree_path, "inline-definition/public-key-format", pubkey_format, config); + if (ret) { + goto cleanup; + } + + ret = nc_server_config_append(ctx, tree_path, "inline-definition/public-key", pubkey, config); + if (ret) { + goto cleanup; + } + + ret = nc_server_config_append(ctx, tree_path, "inline-definition/private-key-format", privkey_format, config); + if (ret) { + goto cleanup; + } + + ret = nc_server_config_append(ctx, tree_path, "inline-definition/cleartext-private-key", privkey, config); + if (ret) { + goto cleanup; + } + + /* delete keystore choice nodes if present */ + ret = nc_server_config_check_delete(config, "%s/keystore-reference", tree_path); + if (ret) { + goto cleanup; + } + +cleanup: + free(privkey); + free(pubkey); + return ret; +} + +API int +nc_server_config_add_ssh_hostkey(const struct ly_ctx *ctx, const char *endpt_name, const char *hostkey_name, + const char *privkey_path, const char *pubkey_path, struct lyd_node **config) +{ + int ret = 0; + char *path = NULL; + + NC_CHECK_ARG_RET(NULL, ctx, endpt_name, hostkey_name, privkey_path, config, 1); + + ret = asprintf(&path, "/ietf-netconf-server:netconf-server/listen/endpoint[name='%s']/ssh/ssh-server-parameters/" + "server-identity/host-key[name='%s']/public-key", endpt_name, hostkey_name); + NC_CHECK_ERRMEM_GOTO(ret == -1, path = NULL; ret = 1, cleanup); + + ret = _nc_server_config_add_ssh_hostkey(ctx, path, privkey_path, pubkey_path, config); + if (ret) { + ERR(NULL, "Creating new hostkey YANG data nodes failed."); + goto cleanup; + } + +cleanup: + free(path); + return ret; +} + +API int +nc_server_config_add_ch_ssh_hostkey(const struct ly_ctx *ctx, const char *client_name, const char *endpt_name, + const char *hostkey_name, const char *privkey_path, const char *pubkey_path, struct lyd_node **config) +{ + int ret = 0; + char *path = NULL; + + NC_CHECK_ARG_RET(NULL, ctx, client_name, endpt_name, hostkey_name, privkey_path, config, 1); + + ret = asprintf(&path, "/ietf-netconf-server:netconf-server/call-home/" + "netconf-client[name='%s']/endpoints/endpoint[name='%s']/ssh/ssh-server-parameters/server-identity/" + "host-key[name='%s']/public-key", client_name, endpt_name, hostkey_name); + NC_CHECK_ERRMEM_GOTO(ret == -1, path = NULL; ret = 1, cleanup); + + ret = _nc_server_config_add_ssh_hostkey(ctx, path, privkey_path, pubkey_path, config); + if (ret) { + ERR(NULL, "Creating new Call-Home hostkey YANG data nodes failed."); + goto cleanup; + } + +cleanup: + free(path); + return ret; +} + +API int +nc_server_config_del_ssh_hostkey(const struct ly_ctx *ctx, const char *endpt_name, const char *hostkey_name, + struct lyd_node **config) +{ + NC_CHECK_ARG_RET(NULL, ctx, endpt_name, config, 1); + + if (hostkey_name) { + return nc_server_config_delete(config, "/ietf-netconf-server:netconf-server/listen/endpoint[name='%s']/ssh/ssh-server-parameters/" + "server-identity/host-key[name='%s']", endpt_name, hostkey_name); + } else { + return nc_server_config_delete(config, "/ietf-netconf-server:netconf-server/listen/endpoint[name='%s']/ssh/ssh-server-parameters/" + "server-identity/host-key", endpt_name); + } +} + +API int +nc_server_config_del_ch_ssh_hostkey(const char *client_name, const char *endpt_name, + const char *hostkey_name, struct lyd_node **config) +{ + NC_CHECK_ARG_RET(NULL, client_name, endpt_name, config, 1); + + if (hostkey_name) { + return nc_server_config_delete(config, "/ietf-netconf-server:netconf-server/call-home/" + "netconf-client[name='%s']/endpoints/endpoint[name='%s']/ssh/ssh-server-parameters/server-identity/" + "host-key[name='%s']", client_name, endpt_name, hostkey_name); + } else { + return nc_server_config_delete(config, "/ietf-netconf-server:netconf-server/call-home/" + "netconf-client[name='%s']/endpoints/endpoint[name='%s']/ssh/ssh-server-parameters/server-identity/" + "host-key", client_name, endpt_name); + } +} + +API int +nc_server_config_add_ssh_keystore_ref(const struct ly_ctx *ctx, const char *endpt_name, const char *hostkey_name, + const char *keystore_reference, struct lyd_node **config) +{ + int ret = 0; + + NC_CHECK_ARG_RET(NULL, ctx, endpt_name, hostkey_name, keystore_reference, config, 1); + + ret = nc_server_config_create(ctx, config, keystore_reference, "/ietf-netconf-server:netconf-server/listen/" + "endpoint[name='%s']/ssh/ssh-server-parameters/server-identity/host-key[name='%s']/public-key/" + "keystore-reference", endpt_name, hostkey_name); + if (ret) { + goto cleanup; + } + + /* delete inline definition nodes if present */ + ret = nc_server_config_check_delete(config, "/ietf-netconf-server:netconf-server/listen/" + "endpoint[name='%s']/ssh/ssh-server-parameters/server-identity/host-key[name='%s']/public-key/" + "inline-definition", endpt_name, hostkey_name); + if (ret) { + goto cleanup; + } + +cleanup: + return ret; +} + +API int +nc_server_config_add_ch_ssh_keystore_ref(const struct ly_ctx *ctx, const char *client_name, + const char *endpt_name, const char *hostkey_name, const char *keystore_reference, struct lyd_node **config) +{ + int ret = 0; + + NC_CHECK_ARG_RET(NULL, ctx, client_name, endpt_name, hostkey_name, keystore_reference, config, 1); + + ret = nc_server_config_create(ctx, config, keystore_reference, "/ietf-netconf-server:netconf-server/call-home/" + "netconf-client[name='%s']/endpoints/endpoint[name='%s']/ssh/ssh-server-parameters/server-identity/" + "host-key[name='%s']/public-key/keystore-reference", client_name, endpt_name, hostkey_name); + if (ret) { + goto cleanup; + } + + /* delete inline definition nodes if present */ + ret = nc_server_config_check_delete(config, "/ietf-netconf-server:netconf-server/call-home/" + "netconf-client[name='%s']/endpoints/endpoint[name='%s']/ssh/ssh-server-parameters/server-identity/" + "host-key[name='%s']/public-key/inline-definition", client_name, endpt_name, hostkey_name); + if (ret) { + goto cleanup; + } + +cleanup: + return ret; +} + +API int +nc_server_config_del_ssh_keystore_ref(const char *endpt_name, const char *hostkey_name, + struct lyd_node **config) +{ + NC_CHECK_ARG_RET(NULL, endpt_name, config, 1); + + return nc_server_config_delete(config, "/ietf-netconf-server:netconf-server/listen/" + "endpoint[name='%s']/ssh/ssh-server-parameters/server-identity/host-key[name='%s']/public-key/" + "keystore-reference", endpt_name, hostkey_name); +} + +API int +nc_server_config_del_ch_ssh_keystore_ref(const char *client_name, const char *endpt_name, + const char *hostkey_name, struct lyd_node **config) +{ + NC_CHECK_ARG_RET(NULL, client_name, endpt_name, hostkey_name, config, 1); + + return nc_server_config_delete(config, "/ietf-netconf-server:netconf-server/call-home/" + "netconf-client[name='%s']/endpoints/endpoint[name='%s']/ssh/ssh-server-parameters/server-identity/" + "host-key[name='%s']/public-key/keystore-reference", client_name, endpt_name, hostkey_name); +} + +static int +_nc_server_config_add_ssh_user_pubkey(const struct ly_ctx *ctx, const char *tree_path, const char *pubkey_path, + struct lyd_node **config) +{ + int ret = 0; + char *pubkey = NULL; + const char *pubkey_format = "ietf-crypto-types:ssh-public-key-format"; + + /* get pubkey data */ + ret = nc_server_config_util_get_ssh_pubkey_file(pubkey_path, &pubkey); + if (ret) { + goto cleanup; + } + + ret = nc_server_config_append(ctx, tree_path, "public-key-format", pubkey_format, config); + if (ret) { + goto cleanup; + } + + ret = nc_server_config_append(ctx, tree_path, "public-key", pubkey, config); + if (ret) { + goto cleanup; + } + +cleanup: + free(pubkey); + return ret; +} + +API int +nc_server_config_add_ssh_user_pubkey(const struct ly_ctx *ctx, const char *endpt_name, + const char *user_name, const char *pubkey_name, const char *pubkey_path, struct lyd_node **config) +{ + int ret = 0; + char *path = NULL; + + NC_CHECK_ARG_RET(NULL, ctx, endpt_name, user_name, pubkey_name, pubkey_path, config, 1); + + ret = asprintf(&path, "/ietf-netconf-server:netconf-server/listen/endpoint[name='%s']/ssh/" + "ssh-server-parameters/client-authentication/users/user[name='%s']/public-keys/inline-definition/" + "public-key[name='%s']", endpt_name, user_name, pubkey_name); + NC_CHECK_ERRMEM_GOTO(ret == -1, path = NULL; ret = 1, cleanup); + + ret = _nc_server_config_add_ssh_user_pubkey(ctx, path, pubkey_path, config); + if (ret) { + ERR(NULL, "Creating new SSH user's public key failed."); + goto cleanup; + } + + /* delete truststore reference if present */ + ret = nc_server_config_check_delete(config, "/ietf-netconf-server:netconf-server/listen/endpoint[name='%s']/ssh/" + "ssh-server-parameters/client-authentication/users/user[name='%s']/public-keys/truststore-reference", + endpt_name, user_name); + if (ret) { + goto cleanup; + } + +cleanup: + free(path); + return ret; +} + +API int +nc_server_config_add_ch_ssh_user_pubkey(const struct ly_ctx *ctx, const char *client_name, const char *endpt_name, + const char *user_name, const char *pubkey_name, const char *pubkey_path, struct lyd_node **config) +{ + int ret = 0; + char *path = NULL; + + NC_CHECK_ARG_RET(NULL, ctx, client_name, endpt_name, user_name, pubkey_name, pubkey_path, config, 1); + + ret = asprintf(&path, "/ietf-netconf-server:netconf-server/call-home/netconf-client[name='%s']/endpoints/" + "endpoint[name='%s']/ssh/ssh-server-parameters/client-authentication/" + "users/user[name='%s']/public-keys/inline-definition/public-key[name='%s']", client_name, + endpt_name, user_name, pubkey_name); + NC_CHECK_ERRMEM_GOTO(ret == -1, path = NULL; ret = 1, cleanup); + + ret = _nc_server_config_add_ssh_user_pubkey(ctx, path, pubkey_path, config); + if (ret) { + ERR(NULL, "Creating new CH SSH user's public key failed."); + goto cleanup; + } + + /* delete truststore reference if present */ + ret = nc_server_config_check_delete(config, "/ietf-netconf-server:netconf-server/call-home/netconf-client[name='%s']/" + "endpoints/endpoint[name='%s']/ssh/ssh-server-parameters/client-authentication/users/user[name='%s']/" + "public-keys/truststore-reference", client_name, endpt_name, user_name); + if (ret) { + goto cleanup; + } + +cleanup: + free(path); + return ret; +} + +API int +nc_server_config_del_ssh_user_pubkey(const char *endpt_name, const char *user_name, + const char *pubkey_name, struct lyd_node **config) +{ + NC_CHECK_ARG_RET(NULL, endpt_name, user_name, config, 1); + + if (pubkey_name) { + return nc_server_config_delete(config, "/ietf-netconf-server:netconf-server/listen/endpoint[name='%s']/ssh/" + "ssh-server-parameters/client-authentication/users/user[name='%s']/public-keys/inline-definition/" + "public-key[name='%s']", endpt_name, user_name, pubkey_name); + } else { + return nc_server_config_delete(config, "/ietf-netconf-server:netconf-server/listen/endpoint[name='%s']/ssh/" + "ssh-server-parameters/client-authentication/users/user[name='%s']/public-keys/inline-definition/" + "public-key", endpt_name, user_name); + } +} + +API int +nc_server_config_del_ch_ssh_user_pubkey(const char *client_name, const char *endpt_name, + const char *user_name, const char *pubkey_name, struct lyd_node **config) +{ + NC_CHECK_ARG_RET(NULL, client_name, endpt_name, user_name, config, 1); + + if (pubkey_name) { + return nc_server_config_delete(config, "/ietf-netconf-server:netconf-server/call-home/" + "netconf-client[name='%s']/endpoints/endpoint[name='%s']/ssh/ssh-server-parameters/client-authentication/" + "users/user[name='%s']/public-keys/inline-definition/public-key[name='%s']", client_name, + endpt_name, user_name, pubkey_name); + } else { + return nc_server_config_delete(config, "/ietf-netconf-server:netconf-server/call-home/" + "netconf-client[name='%s']/endpoints/endpoint[name='%s']/ssh/ssh-server-parameters/client-authentication/" + "users/user[name='%s']/public-keys/inline-definition/public-key", client_name, + endpt_name, user_name); + } +} + +static int +_nc_server_config_add_ssh_user_password(const struct ly_ctx *ctx, const char *tree_path, + const char *password, struct lyd_node **config) +{ + int ret = 0; + char *hashed_pw = NULL; + const char *salt = "$6$idsizuippipk$"; + struct crypt_data cdata = {0}; + + NC_CHECK_ARG_RET(NULL, ctx, tree_path, password, config, 1); + + hashed_pw = crypt_r(password, salt, &cdata); + if (!hashed_pw) { + ERR(NULL, "Hashing password failed (%s).", strerror(errno)); + ret = 1; + goto cleanup; + } + + ret = nc_server_config_append(ctx, tree_path, "password", hashed_pw, config); + if (ret) { + goto cleanup; + } + +cleanup: + return ret; +} + +API int +nc_server_config_add_ssh_user_password(const struct ly_ctx *ctx, const char *endpt_name, + const char *user_name, const char *password, struct lyd_node **config) +{ + int ret = 0; + char *path = NULL; + + NC_CHECK_ARG_RET(NULL, ctx, endpt_name, user_name, password, config, 1); + + ret = asprintf(&path, "/ietf-netconf-server:netconf-server/listen/endpoint[name='%s']/ssh/ssh-server-parameters/" + "client-authentication/users/user[name='%s']", endpt_name, user_name); + NC_CHECK_ERRMEM_GOTO(ret == -1, path = NULL; ret = 1, cleanup); + + ret = _nc_server_config_add_ssh_user_password(ctx, path, password, config); + if (ret) { + ERR(NULL, "Creating new SSH user's password failed."); + goto cleanup; + } + +cleanup: + free(path); + return ret; +} + +API int +nc_server_config_add_ch_ssh_user_password(const struct ly_ctx *ctx, const char *client_name, const char *endpt_name, + const char *user_name, const char *password, struct lyd_node **config) +{ + int ret = 0; + char *path = NULL; + + NC_CHECK_ARG_RET(NULL, ctx, client_name, endpt_name, user_name, password, config, 1); + + ret = asprintf(&path, "/ietf-netconf-server:netconf-server/call-home/netconf-client[name='%s']/endpoints/" + "endpoint[name='%s']/ssh/ssh-server-parameters/client-authentication/" + "users/user[name='%s']", client_name, endpt_name, user_name); + NC_CHECK_ERRMEM_GOTO(ret == -1, path = NULL; ret = 1, cleanup); + + ret = _nc_server_config_add_ssh_user_password(ctx, path, password, config); + if (ret) { + ERR(NULL, "Creating new CH SSH user's password failed."); + goto cleanup; + } + +cleanup: + free(path); + return ret; +} + +API int +nc_server_config_del_ssh_user_password(const char *endpt_name, const char *user_name, struct lyd_node **config) +{ + NC_CHECK_ARG_RET(NULL, endpt_name, user_name, config, 1); + + return nc_server_config_delete(config, "/ietf-netconf-server:netconf-server/listen/endpoint[name='%s']/ssh/" + "ssh-server-parameters/client-authentication/users/user[name='%s']/password", endpt_name, user_name); +} + +API int +nc_server_config_del_ch_ssh_user_password(const char *client_name, const char *endpt_name, + const char *user_name, struct lyd_node **config) +{ + NC_CHECK_ARG_RET(NULL, client_name, endpt_name, user_name, config, 1); + + return nc_server_config_delete(config, "/ietf-netconf-server:netconf-server/call-home/" + "netconf-client[name='%s']/endpoints/endpoint[name='%s']/ssh/ssh-server-parameters/client-authentication/" + "users/user[name='%s']/password", client_name, endpt_name, user_name); +} + +static int +_nc_server_config_add_ssh_user_interactive(const struct ly_ctx *ctx, const char *tree_path, + const char *pam_config_name, const char *pam_config_dir, struct lyd_node **config) +{ + int ret = 0; + + ret = nc_server_config_append(ctx, tree_path, "pam-config-file-name", pam_config_name, config); + if (ret) { + goto cleanup; + } + + if (pam_config_dir) { + ret = nc_server_config_append(ctx, tree_path, "pam-config-file-dir", pam_config_dir, config); + if (ret) { + goto cleanup; + } + } + +cleanup: + return ret; +} + +API int +nc_server_config_add_ssh_user_interactive(const struct ly_ctx *ctx, const char *endpt_name, + const char *user_name, const char *pam_config_name, const char *pam_config_dir, struct lyd_node **config) +{ + int ret = 0; + char *path = NULL; + + NC_CHECK_ARG_RET(NULL, ctx, endpt_name, user_name, pam_config_name, config, 1); + + ret = asprintf(&path, "/ietf-netconf-server:netconf-server/listen/endpoint[name='%s']/ssh/ssh-server-parameters/" + "client-authentication/users/user[name='%s']/" + "libnetconf2-netconf-server:keyboard-interactive", endpt_name, user_name); + NC_CHECK_ERRMEM_GOTO(ret == -1, path = NULL; ret = 1, cleanup); + + ret = _nc_server_config_add_ssh_user_interactive(ctx, path, pam_config_name, pam_config_dir, config); + if (ret) { + ERR(NULL, "Creating new SSH user's keyboard interactive nodes failed."); + goto cleanup; + } + +cleanup: + free(path); + return ret; +} + +API int +nc_server_config_add_ch_ssh_user_interactive(const struct ly_ctx *ctx, const char *client_name, const char *endpt_name, + const char *user_name, const char *pam_config_name, const char *pam_config_dir, struct lyd_node **config) +{ + int ret = 0; + char *path = NULL; + + NC_CHECK_ARG_RET(NULL, ctx, client_name, endpt_name, user_name, pam_config_name, config, 1); + + ret = asprintf(&path, "/ietf-netconf-server:netconf-server/call-home/netconf-client[name='%s']/endpoints/" + "endpoint[name='%s']/ssh/ssh-server-parameters/client-authentication/users/user[name='%s']/" + "libnetconf2-netconf-server:keyboard-interactive", client_name, endpt_name, user_name); + NC_CHECK_ERRMEM_GOTO(ret == -1, path = NULL; ret = 1, cleanup); + + ret = _nc_server_config_add_ssh_user_interactive(ctx, path, pam_config_name, pam_config_dir, config); + if (ret) { + ERR(NULL, "Creating new CH SSH user's keyboard interactive nodes failed."); + goto cleanup; + } + +cleanup: + free(path); + return ret; +} + +API int +nc_server_config_del_ssh_user_interactive(const char *endpt_name, const char *user_name, struct lyd_node **config) +{ + NC_CHECK_ARG_RET(NULL, endpt_name, user_name, config, 1); + + return nc_server_config_delete(config, "/ietf-netconf-server:netconf-server/listen/endpoint[name='%s']/ssh/" + "ssh-server-parameters/client-authentication/users/user[name='%s']/" + "libnetconf2-netconf-server:keyboard-interactive", endpt_name, user_name); +} + +API int +nc_server_config_del_ch_ssh_user_interactive(const char *client_name, const char *endpt_name, + const char *user_name, struct lyd_node **config) +{ + NC_CHECK_ARG_RET(NULL, client_name, endpt_name, user_name, config, 1); + + return nc_server_config_delete(config, "/ietf-netconf-server:netconf-server/call-home/netconf-client[name='%s']/endpoints/" + "endpoint[name='%s']/ssh/ssh-server-parameters/client-authentication/users/user[name='%s']/" + "libnetconf2-netconf-server:keyboard-interactive", client_name, endpt_name, user_name); +} + +API int +nc_server_config_del_ssh_user(const char *endpt_name, + const char *user_name, struct lyd_node **config) +{ + NC_CHECK_ARG_RET(NULL, endpt_name, config, 1); + + if (user_name) { + return nc_server_config_delete(config, "/ietf-netconf-server:netconf-server/listen/endpoint[name='%s']/ssh/" + "ssh-server-parameters/client-authentication/users/user[name='%s']", endpt_name, user_name); + } else { + return nc_server_config_delete(config, "/ietf-netconf-server:netconf-server/listen/endpoint[name='%s']/ssh/" + "ssh-server-parameters/client-authentication/users/user", endpt_name); + } +} + +API int +nc_server_config_del_ch_ssh_user(const char *client_name, const char *endpt_name, + const char *user_name, struct lyd_node **config) +{ + NC_CHECK_ARG_RET(NULL, client_name, endpt_name, config, 1); + + if (user_name) { + return nc_server_config_delete(config, "/ietf-netconf-server:netconf-server/call-home/netconf-client[name='%s']/endpoints/" + "endpoint[name='%s']/ssh/ssh-server-parameters/client-authentication/users/user[name='%s']", client_name, + endpt_name, user_name); + } else { + return nc_server_config_delete(config, "/ietf-netconf-server:netconf-server/call-home/netconf-client[name='%s']/endpoints/" + "endpoint[name='%s']/ssh/ssh-server-parameters/client-authentication/users/user", client_name, endpt_name); + } +} + +API int +nc_server_config_add_ssh_endpoint_client_ref(const struct ly_ctx *ctx, const char *endpt_name, + const char *referenced_endpt, struct lyd_node **config) +{ + NC_CHECK_ARG_RET(NULL, ctx, endpt_name, referenced_endpt, config, 1); + + return nc_server_config_create(ctx, config, referenced_endpt, "/ietf-netconf-server:netconf-server/listen/endpoint[name='%s']/ssh/ssh-server-parameters/" + "client-authentication/libnetconf2-netconf-server:endpoint-reference", endpt_name); +} + +API int +nc_server_config_del_ssh_endpoint_client_ref(const char *endpt_name, struct lyd_node **config) +{ + NC_CHECK_ARG_RET(NULL, endpt_name, config, 1); + + return nc_server_config_delete(config, "/ietf-netconf-server:netconf-server/listen/endpoint[name='%s']/ssh/ssh-server-parameters/" + "client-authentication/libnetconf2-netconf-server:endpoint-reference", endpt_name); +} + +API int +nc_server_config_add_ssh_truststore_ref(const struct ly_ctx *ctx, const char *endpt_name, const char *user_name, + const char *truststore_reference, struct lyd_node **config) +{ + int ret = 0; + + NC_CHECK_ARG_RET(NULL, ctx, endpt_name, user_name, truststore_reference, config, 1); + + ret = nc_server_config_create(ctx, config, truststore_reference, "/ietf-netconf-server:netconf-server/listen/" + "endpoint[name='%s']/ssh/ssh-server-parameters/client-authentication/users/user[name='%s']/public-keys/" + "truststore-reference", endpt_name, user_name); + if (ret) { + goto cleanup; + } + + /* delete inline definition nodes if present */ + ret = nc_server_config_check_delete(config, "/ietf-netconf-server:netconf-server/listen/endpoint[name='%s']/ssh/" + "ssh-server-parameters/client-authentication/users/user[name='%s']/public-keys/inline-definition", + endpt_name, user_name); + if (ret) { + goto cleanup; + } + +cleanup: + return ret; +} + +API int +nc_server_config_add_ch_ssh_truststore_ref(const struct ly_ctx *ctx, const char *client_name, + const char *endpt_name, const char *user_name, const char *truststore_reference, struct lyd_node **config) +{ + int ret = 0; + + NC_CHECK_ARG_RET(NULL, ctx, client_name, endpt_name, user_name, truststore_reference, config, 1); + + ret = nc_server_config_create(ctx, config, truststore_reference, "/ietf-netconf-server:netconf-server/call-home/" + "netconf-client[name='%s']/endpoints/endpoint[name='%s']/ssh/ssh-server-parameters/client-authentication/" + "users/user[name='%s']/public-keys/truststore-reference", client_name, endpt_name, user_name); + if (ret) { + goto cleanup; + } + + /* delete inline definition nodes if present */ + ret = nc_server_config_check_delete(config, "/ietf-netconf-server:netconf-server/call-home/netconf-client[name='%s']/" + "endpoints/endpoint[name='%s']/ssh/ssh-server-parameters/client-authentication/users/user[name='%s']/" + "public-keys/inline-definition", client_name, endpt_name, user_name); + if (ret) { + goto cleanup; + } + +cleanup: + return ret; +} + +API int +nc_server_config_del_ssh_truststore_ref(const char *endpt_name, const char *user_name, + struct lyd_node **config) +{ + NC_CHECK_ARG_RET(NULL, endpt_name, user_name, config, 1); + + return nc_server_config_delete(config, "/ietf-netconf-server:netconf-server/listen/" + "endpoint[name='%s']/ssh/ssh-server-parameters/client-authentication/users/user[name='%s']/public-keys/" + "truststore-reference", endpt_name, user_name); +} + +API int +nc_server_config_del_ch_ssh_truststore_ref(const char *client_name, const char *endpt_name, + const char *user_name, struct lyd_node **config) +{ + NC_CHECK_ARG_RET(NULL, client_name, endpt_name, user_name, config, 1); + + return nc_server_config_delete(config, "/ietf-netconf-server:netconf-server/call-home/" + "netconf-client[name='%s']/endpoints/endpoint[name='%s']/ssh/ssh-server-parameters/client-authentication/" + "users/user[name='%s']/public-keys/truststore-reference", client_name, endpt_name, user_name); +} diff --git a/src/server_config_util_tls.c b/src/server_config_util_tls.c new file mode 100644 index 00000000..047a6d5e --- /dev/null +++ b/src/server_config_util_tls.c @@ -0,0 +1,790 @@ +/** + * @file server_config_util_tls.c + * @author Roman Janota + * @brief libnetconf2 server TLS configuration utilities + * + * @copyright + * Copyright (c) 2023 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 "server_config_util.h" + +#include +#include +#include +#include +#include + +#include + +#include "compat.h" +#include "config.h" +#include "log_p.h" +#include "server_config.h" +#include "session.h" +#include "session_p.h" + +static int +_nc_server_config_add_tls_server_cert(const struct ly_ctx *ctx, const char *tree_path, const char *privkey_path, + const char *pubkey_path, const char *cert_path, struct lyd_node **config) +{ + int ret = 0; + char *privkey = NULL, *pubkey = NULL, *cert = NULL; + NC_PRIVKEY_FORMAT privkey_type; + const char *privkey_format, *pubkey_format = "ietf-crypto-types:subject-public-key-info-format"; + + NC_CHECK_ARG_RET(NULL, ctx, tree_path, privkey_path, cert_path, config, 1); + + /* get the keys as a string from the given files */ + ret = nc_server_config_util_get_asym_key_pair(privkey_path, pubkey_path, NC_PUBKEY_FORMAT_X509, &privkey, &privkey_type, &pubkey); + if (ret) { + ERR(NULL, "Getting keys from file(s) failed."); + goto cleanup; + } + + /* get cert data from file */ + ret = nc_server_config_util_read_certificate(cert_path, &cert); + if (ret) { + ERR(NULL, "Getting certificate from file \"%s\" failed.", cert_path); + goto cleanup; + } + + /* get privkey identityref value */ + privkey_format = nc_server_config_util_privkey_format_to_identityref(privkey_type); + if (!privkey_format) { + ret = 1; + goto cleanup; + } + + ret = nc_server_config_append(ctx, tree_path, "inline-definition/public-key-format", pubkey_format, config); + if (ret) { + goto cleanup; + } + + ret = nc_server_config_append(ctx, tree_path, "inline-definition/public-key", pubkey, config); + if (ret) { + goto cleanup; + } + + ret = nc_server_config_append(ctx, tree_path, "inline-definition/private-key-format", privkey_format, config); + if (ret) { + goto cleanup; + } + + ret = nc_server_config_append(ctx, tree_path, "inline-definition/cleartext-private-key", privkey, config); + if (ret) { + goto cleanup; + } + + ret = nc_server_config_append(ctx, tree_path, "inline-definition/cert-data", cert, config); + if (ret) { + goto cleanup; + } + + /* delete keystore if present */ + ret = nc_server_config_check_delete(config, "%s/keystore-reference", tree_path); + if (ret) { + goto cleanup; + } + +cleanup: + free(privkey); + free(pubkey); + free(cert); + return ret; +} + +API int +nc_server_config_add_tls_server_cert(const struct ly_ctx *ctx, const char *endpt_name, const char *privkey_path, + const char *pubkey_path, const char *cert_path, struct lyd_node **config) +{ + int ret = 0; + char *path = NULL; + + NC_CHECK_ARG_RET(NULL, ctx, endpt_name, privkey_path, cert_path, config, 1); + + ret = asprintf(&path, "/ietf-netconf-server:netconf-server/listen/endpoint[name='%s']/" + "tls/tls-server-parameters/server-identity/certificate", endpt_name); + NC_CHECK_ERRMEM_GOTO(ret == -1, path = NULL; ret = 1, cleanup); + + ret = _nc_server_config_add_tls_server_cert(ctx, path, privkey_path, pubkey_path, + cert_path, config); + if (ret) { + ERR(NULL, "Creating new TLS server certificate YANG data failed."); + goto cleanup; + } + +cleanup: + free(path); + return ret; +} + +API int +nc_server_config_del_tls_server_cert(const char *endpt_name, struct lyd_node **config) +{ + NC_CHECK_ARG_RET(NULL, endpt_name, config, 1); + + return nc_server_config_delete(config, "/ietf-netconf-server:netconf-server/listen/endpoint[name='%s']/" + "tls/tls-server-parameters/server-identity/certificate/inline-definition", endpt_name); +} + +API int +nc_server_config_add_ch_tls_server_cert(const struct ly_ctx *ctx, const char *client_name, const char *endpt_name, + const char *privkey_path, const char *pubkey_path, const char *cert_path, struct lyd_node **config) +{ + int ret = 0; + char *path = NULL; + + NC_CHECK_ARG_RET(NULL, ctx, client_name, endpt_name, privkey_path, cert_path, config, 1); + + ret = asprintf(&path, "/ietf-netconf-server:netconf-server/call-home/" + "netconf-client[name='%s']/endpoints/endpoint[name='%s']/tls/tls-server-parameters/server-identity/" + "certificate", client_name, endpt_name); + NC_CHECK_ERRMEM_GOTO(ret == -1, path = NULL; ret = 1, cleanup); + + ret = _nc_server_config_add_tls_server_cert(ctx, path, privkey_path, pubkey_path, + cert_path, config); + if (ret) { + ERR(NULL, "Creating new CH TLS server certificate YANG data failed."); + goto cleanup; + } + +cleanup: + free(path); + return ret; +} + +API int +nc_server_config_del_ch_tls_server_cert(const char *client_name, const char *endpt_name, + struct lyd_node **config) +{ + NC_CHECK_ARG_RET(NULL, client_name, endpt_name, config, 1); + + return nc_server_config_delete(config, "/ietf-netconf-server:netconf-server/call-home/" + "netconf-client[name='%s']/endpoints/endpoint[name='%s']/tls/tls-server-parameters/server-identity/" + "certificate/inline-definition", client_name, endpt_name); +} + +static int +_nc_server_config_add_tls_keystore_ref(const struct ly_ctx *ctx, const char *tree_path, const char *asym_key_ref, + const char *cert_ref, struct lyd_node **config) +{ + int ret = 0; + + /* create asymmetric key pair reference */ + ret = nc_server_config_append(ctx, tree_path, "keystore-reference/asymmetric-key", asym_key_ref, config); + if (ret) { + goto cleanup; + } + + /* create cert reference, this cert has to belong to the asym key */ + ret = nc_server_config_append(ctx, tree_path, "keystore-reference/certificate", cert_ref, config); + if (ret) { + goto cleanup; + } + + /* delete inline definition if present */ + ret = nc_server_config_check_delete(config, "%s/inline-definition", tree_path); + if (ret) { + goto cleanup; + } + +cleanup: + return ret; +} + +API int +nc_server_config_add_tls_keystore_ref(const struct ly_ctx *ctx, const char *endpt_name, const char *asym_key_ref, + const char *cert_ref, struct lyd_node **config) +{ + int ret = 0; + char *path = NULL; + + NC_CHECK_ARG_RET(NULL, ctx, endpt_name, asym_key_ref, cert_ref, config, 1); + + ret = asprintf(&path, "/ietf-netconf-server:netconf-server/listen/endpoint[name='%s']/" + "tls/tls-server-parameters/server-identity/certificate", endpt_name); + NC_CHECK_ERRMEM_GOTO(ret == -1, path = NULL; ret = 1, cleanup); + + ret = _nc_server_config_add_tls_keystore_ref(ctx, path, asym_key_ref, cert_ref, config); + if (ret) { + goto cleanup; + } + +cleanup: + free(path); + return ret; +} + +API int +nc_server_config_del_tls_keystore_ref(const char *endpt_name, struct lyd_node **config) +{ + NC_CHECK_ARG_RET(NULL, endpt_name, config, 1); + + return nc_server_config_delete(config, "/ietf-netconf-server:netconf-server/listen/endpoint[name='%s']/" + "tls/tls-server-parameters/server-identity/certificate/keystore-reference", endpt_name); +} + +API int +nc_server_config_add_ch_tls_keystore_ref(const struct ly_ctx *ctx, const char *client_name, + const char *endpt_name, const char *asym_key_ref, const char *cert_ref, struct lyd_node **config) +{ + int ret = 0; + char *path = NULL; + + NC_CHECK_ARG_RET(NULL, ctx, client_name, endpt_name, asym_key_ref, cert_ref, config, 1); + + ret = asprintf(&path, "/ietf-netconf-server:netconf-server/call-home/netconf-client[name='%s']/endpoints/" + "endpoint[name='%s']/tls/tls-server-parameters/server-identity/certificate", client_name, endpt_name); + NC_CHECK_ERRMEM_GOTO(ret == -1, path = NULL; ret = 1, cleanup); + + ret = _nc_server_config_add_tls_keystore_ref(ctx, path, asym_key_ref, cert_ref, config); + if (ret) { + goto cleanup; + } + +cleanup: + free(path); + return ret; +} + +API int +nc_server_config_del_ch_tls_keystore_ref(const char *client_name, const char *endpt_name, + struct lyd_node **config) +{ + NC_CHECK_ARG_RET(NULL, client_name, endpt_name, config, 1); + + return nc_server_config_delete(config, "/ietf-netconf-server:netconf-server/call-home/netconf-client[name='%s']/" + "endpoints/endpoint[name='%s']/tls/tls-server-parameters/server-identity/certificate/" + "keystore-reference", client_name, endpt_name); +} + +static int +_nc_server_config_add_tls_client_cert(const struct ly_ctx *ctx, const char *tree_path, + const char *cert_path, struct lyd_node **config) +{ + int ret = 0; + char *cert = NULL; + + NC_CHECK_ARG_RET(NULL, ctx, tree_path, cert_path, config, 1); + + ret = nc_server_config_util_read_certificate(cert_path, &cert); + if (ret) { + ERR(NULL, "Getting certificate from file \"%s\" failed.", cert_path); + goto cleanup; + } + + ret = nc_server_config_append(ctx, tree_path, "cert-data", cert, config); + if (ret) { + goto cleanup; + } + +cleanup: + free(cert); + return ret; +} + +API int +nc_server_config_add_tls_client_cert(const struct ly_ctx *ctx, const char *endpt_name, const char *cert_name, + const char *cert_path, struct lyd_node **config) +{ + int ret = 0; + char *path = NULL; + + NC_CHECK_ARG_RET(NULL, ctx, endpt_name, cert_name, cert_path, config, 1); + + ret = asprintf(&path, "/ietf-netconf-server:netconf-server/listen/endpoint[name='%s']/tls/tls-server-parameters/" + "client-authentication/ee-certs/inline-definition/certificate[name='%s']", endpt_name, cert_name); + NC_CHECK_ERRMEM_GOTO(ret == -1, path = NULL; ret = 1, cleanup); + + ret = _nc_server_config_add_tls_client_cert(ctx, path, cert_path, config); + if (ret) { + ERR(NULL, "Creating new TLS client certificate YANG data failed."); + goto cleanup; + } + + /* delete truststore if present */ + ret = nc_server_config_check_delete(config, "/ietf-netconf-server:netconf-server/listen/endpoint[name='%s']/tls/tls-server-parameters/" + "client-authentication/ee-certs/truststore-reference", endpt_name); + if (ret) { + goto cleanup; + } + +cleanup: + free(path); + return ret; +} + +API int +nc_server_config_del_tls_client_cert(const char *endpt_name, const char *cert_name, struct lyd_node **config) +{ + NC_CHECK_ARG_RET(NULL, endpt_name, config, 1); + + if (cert_name) { + return nc_server_config_delete(config, "/ietf-netconf-server:netconf-server/listen/endpoint[name='%s']/tls/" + "tls-server-parameters/client-authentication/ee-certs/inline-definition/" + "certificate[name='%s']", endpt_name, cert_name); + } else { + return nc_server_config_delete(config, "/ietf-netconf-server:netconf-server/listen/endpoint[name='%s']/tls/" + "tls-server-parameters/client-authentication/ee-certs/inline-definition/" + "certificate", endpt_name); + } +} + +API int +nc_server_config_add_ch_tls_client_cert(const struct ly_ctx *ctx, const char *client_name, const char *endpt_name, + const char *cert_name, const char *cert_path, struct lyd_node **config) +{ + int ret = 0; + char *path = NULL; + + NC_CHECK_ARG_RET(NULL, ctx, client_name, endpt_name, cert_name, cert_path, config, 1); + + ret = asprintf(&path, "/ietf-netconf-server:netconf-server/call-home/netconf-client[name='%s']/" + "endpoints/endpoint[name='%s']/tls/tls-server-parameters/client-authentication/ee-certs/" + "inline-definition/certificate[name='%s']", client_name, endpt_name, cert_name) == -1; + NC_CHECK_ERRMEM_GOTO(ret == -1, path = NULL; ret = 1, cleanup); + + ret = _nc_server_config_add_tls_client_cert(ctx, path, cert_path, config); + if (ret) { + ERR(NULL, "Creating new CH TLS client certificate YANG data failed."); + goto cleanup; + } + + /* delete truststore if present */ + ret = nc_server_config_check_delete(config, "/ietf-netconf-server:netconf-server/call-home/netconf-client[name='%s']/" + "endpoints/endpoint[name='%s']/tls/tls-server-parameters/" + "client-authentication/ee-certs/truststore-reference", client_name, endpt_name); + if (ret) { + goto cleanup; + } + +cleanup: + free(path); + return ret; +} + +API int +nc_server_config_del_ch_tls_client_cert(const char *client_name, const char *endpt_name, + const char *cert_name, struct lyd_node **config) +{ + NC_CHECK_ARG_RET(NULL, client_name, endpt_name, config, 1); + + if (cert_name) { + return nc_server_config_delete(config, "/ietf-netconf-server:netconf-server/call-home/netconf-client[name='%s']/" + "endpoints/endpoint[name='%s']/tls/tls-server-parameters/client-authentication/ee-certs/" + "inline-definition/certificate[name='%s']", client_name, endpt_name, cert_name); + } else { + return nc_server_config_delete(config, "/ietf-netconf-server:netconf-server/call-home/netconf-client[name='%s']/" + "endpoints/endpoint[name='%s']/tls/tls-server-parameters/client-authentication/ee-certs/" + "inline-definition/certificate", client_name, endpt_name); + } +} + +API int +nc_server_config_add_tls_client_cert_truststore_ref(const struct ly_ctx *ctx, const char *endpt_name, + const char *cert_bag_ref, struct lyd_node **config) +{ + int ret = 0; + + NC_CHECK_ARG_RET(NULL, ctx, endpt_name, cert_bag_ref, config, 1); + + ret = nc_server_config_create(ctx, config, cert_bag_ref, "/ietf-netconf-server:netconf-server/listen/endpoint[name='%s']/tls/" + "tls-server-parameters/client-authentication/ee-certs/truststore-reference", endpt_name); + if (ret) { + goto cleanup; + } + + /* delete inline definition if present */ + ret = nc_server_config_check_delete(config, "/ietf-netconf-server:netconf-server/listen/endpoint[name='%s']/tls/" + "tls-server-parameters/client-authentication/ee-certs/inline-definition", endpt_name); + if (ret) { + goto cleanup; + } + +cleanup: + return ret; +} + +API int +nc_server_config_del_tls_client_cert_truststore_ref(const char *endpt_name, struct lyd_node **config) +{ + NC_CHECK_ARG_RET(NULL, endpt_name, config, 1); + + return nc_server_config_delete(config, "/ietf-netconf-server:netconf-server/listen/endpoint[name='%s']/tls/" + "tls-server-parameters/client-authentication/ee-certs/truststore-reference", endpt_name); +} + +API int +nc_server_config_add_ch_tls_client_cert_truststore_ref(const struct ly_ctx *ctx, const char *client_name, + const char *endpt_name, const char *cert_bag_ref, struct lyd_node **config) +{ + int ret = 0; + + NC_CHECK_ARG_RET(NULL, ctx, client_name, endpt_name, cert_bag_ref, config, 1); + + ret = nc_server_config_create(ctx, config, cert_bag_ref, "/ietf-netconf-server:netconf-server/call-home/" + "netconf-client[name='%s']/endpoints/endpoint[name='%s']/tls/tls-server-parameters/" + "client-authentication/ee-certs/truststore-reference", client_name, endpt_name); + if (ret) { + goto cleanup; + } + + /* delete inline definition if present */ + ret = nc_server_config_check_delete(config, "/ietf-netconf-server:netconf-server/call-home/" + "netconf-client[name='%s']/endpoints/endpoint[name='%s']/tls/" + "tls-server-parameters/client-authentication/ee-certs/inline-definition", client_name, endpt_name); + if (ret) { + goto cleanup; + } + +cleanup: + return ret; +} + +API int +nc_server_config_del_ch_tls_client_cert_truststore_ref(const char *client_name, const char *endpt_name, + struct lyd_node **config) +{ + NC_CHECK_ARG_RET(NULL, client_name, endpt_name, config, 1); + + return nc_server_config_delete(config, "/ietf-netconf-server:netconf-server/call-home/" + "netconf-client[name='%s']/endpoints/endpoint[name='%s']/tls/tls-server-parameters/" + "client-authentication/ee-certs/truststore-reference", client_name, endpt_name); +} + +API int +nc_server_config_add_tls_ca_cert(const struct ly_ctx *ctx, const char *endpt_name, const char *cert_name, + const char *cert_path, struct lyd_node **config) +{ + int ret = 0; + char *path = NULL; + + NC_CHECK_ARG_RET(NULL, ctx, endpt_name, cert_name, cert_path, config, 1); + + ret = asprintf(&path, "/ietf-netconf-server:netconf-server/listen/endpoint[name='%s']/tls/tls-server-parameters/" + "client-authentication/ca-certs/inline-definition/certificate[name='%s']", endpt_name, cert_name); + NC_CHECK_ERRMEM_GOTO(ret == -1, path = NULL; ret = 1, cleanup); + + ret = _nc_server_config_add_tls_client_cert(ctx, path, cert_path, config); + if (ret) { + ERR(NULL, "Creating new TLS client certificate authority YANG data failed."); + goto cleanup; + } + + /* delete truststore if present */ + ret = nc_server_config_check_delete(config, "/ietf-netconf-server:netconf-server/listen/endpoint[name='%s']/tls/tls-server-parameters/" + "client-authentication/ca-certs/truststore-reference", endpt_name); + if (ret) { + goto cleanup; + } + +cleanup: + free(path); + return ret; +} + +API int +nc_server_config_del_tls_ca_cert(const char *endpt_name, const char *cert_name, struct lyd_node **config) +{ + NC_CHECK_ARG_RET(NULL, endpt_name, config, 1); + + if (cert_name) { + return nc_server_config_delete(config, "/ietf-netconf-server:netconf-server/listen/endpoint[name='%s']/tls/" + "tls-server-parameters/client-authentication/ca-certs/inline-definition/" + "certificate[name='%s']", endpt_name, cert_name); + } else { + return nc_server_config_delete(config, "/ietf-netconf-server:netconf-server/listen/endpoint[name='%s']/tls/" + "tls-server-parameters/client-authentication/ca-certs/inline-definition/" + "certificate", endpt_name); + } +} + +API int +nc_server_config_add_ch_tls_ca_cert(const struct ly_ctx *ctx, const char *client_name, const char *endpt_name, + const char *cert_name, const char *cert_path, struct lyd_node **config) +{ + int ret = 0; + char *path = NULL; + + NC_CHECK_ARG_RET(NULL, ctx, client_name, endpt_name, cert_name, cert_path, config, 1); + + ret = asprintf(&path, "/ietf-netconf-server:netconf-server/call-home/netconf-client[name='%s']/" + "endpoints/endpoint[name='%s']/tls/tls-server-parameters/client-authentication/ca-certs/" + "inline-definition/certificate[name='%s']", client_name, endpt_name, cert_name); + NC_CHECK_ERRMEM_GOTO(ret == -1, path = NULL; ret = 1, cleanup); + + ret = _nc_server_config_add_tls_client_cert(ctx, path, cert_path, config); + if (ret) { + ERR(NULL, "Creating new CH TLS client certificate authority YANG data failed."); + goto cleanup; + } + + /* delete truststore if present */ + ret = nc_server_config_check_delete(config, "/ietf-netconf-server:netconf-server/call-home/netconf-client[name='%s']/" + "endpoints/endpoint[name='%s']/tls/tls-server-parameters/" + "client-authentication/ca-certs/truststore-reference", client_name, endpt_name); + if (ret) { + goto cleanup; + } + +cleanup: + free(path); + return ret; +} + +API int +nc_server_config_del_ch_tls_ca_cert(const char *client_name, const char *endpt_name, + const char *cert_name, struct lyd_node **config) +{ + NC_CHECK_ARG_RET(NULL, client_name, endpt_name, config, 1); + + if (cert_name) { + return nc_server_config_delete(config, "/ietf-netconf-server:netconf-server/call-home/netconf-client[name='%s']/" + "endpoints/endpoint[name='%s']/tls/tls-server-parameters/client-authentication/ca-certs/" + "inline-definition/certificate[name='%s']", client_name, endpt_name, cert_name); + } else { + return nc_server_config_delete(config, "/ietf-netconf-server:netconf-server/call-home/netconf-client[name='%s']/" + "endpoints/endpoint[name='%s']/tls/tls-server-parameters/client-authentication/ca-certs/" + "inline-definition/certificate", client_name, endpt_name); + } +} + +API int +nc_server_config_add_tls_ca_cert_truststore_ref(const struct ly_ctx *ctx, const char *endpt_name, + const char *cert_bag_ref, struct lyd_node **config) +{ + int ret = 0; + + NC_CHECK_ARG_RET(NULL, ctx, endpt_name, cert_bag_ref, config, 1); + + ret = nc_server_config_create(ctx, config, cert_bag_ref, "/ietf-netconf-server:netconf-server/listen/endpoint[name='%s']/tls/" + "tls-server-parameters/client-authentication/ca-certs/truststore-reference", endpt_name); + if (ret) { + goto cleanup; + } + + /* delete inline definition if present */ + ret = nc_server_config_check_delete(config, "/ietf-netconf-server:netconf-server/listen/endpoint[name='%s']/tls/" + "tls-server-parameters/client-authentication/ca-certs/inline-definition", endpt_name); + if (ret) { + goto cleanup; + } + +cleanup: + return ret; +} + +API int +nc_server_config_del_tls_ca_cert_truststore_ref(const char *endpt_name, struct lyd_node **config) +{ + NC_CHECK_ARG_RET(NULL, endpt_name, config, 1); + + return nc_server_config_delete(config, "/ietf-netconf-server:netconf-server/listen/endpoint[name='%s']/tls/" + "tls-server-parameters/client-authentication/ca-certs/truststore-reference", endpt_name); +} + +API int +nc_server_config_add_ch_tls_ca_cert_truststore_ref(const struct ly_ctx *ctx, const char *client_name, + const char *endpt_name, const char *cert_bag_ref, struct lyd_node **config) +{ + int ret = 0; + + NC_CHECK_ARG_RET(NULL, ctx, client_name, endpt_name, cert_bag_ref, config, 1); + + ret = nc_server_config_create(ctx, config, cert_bag_ref, "/ietf-netconf-server:netconf-server/call-home/" + "netconf-client[name='%s']/endpoints/endpoint[name='%s']/tls/tls-server-parameters/" + "client-authentication/ca-certs/truststore-reference", client_name, endpt_name); + if (ret) { + goto cleanup; + } + + /* delete inline definition if present */ + ret = nc_server_config_check_delete(config, "/ietf-netconf-server:netconf-server/call-home/" + "netconf-client[name='%s']/endpoints/endpoint[name='%s']/tls/tls-server-parameters/" + "client-authentication/ca-certs/inline-definition", client_name, endpt_name); + if (ret) { + goto cleanup; + } + +cleanup: + return ret; +} + +API int +nc_server_config_del_ch_tls_ca_cert_truststore_ref(const char *client_name, const char *endpt_name, + struct lyd_node **config) +{ + NC_CHECK_ARG_RET(NULL, client_name, endpt_name, config, 1); + + return nc_server_config_delete(config, "/ietf-netconf-server:netconf-server/call-home/" + "netconf-client[name='%s']/endpoints/endpoint[name='%s']/tls/tls-server-parameters/" + "client-authentication/ca-certs/truststore-reference", client_name, endpt_name); +} + +static const char * +nc_server_config_tls_maptype2str(NC_TLS_CTN_MAPTYPE map_type) +{ + switch (map_type) { + case NC_TLS_CTN_SPECIFIED: + return "ietf-x509-cert-to-name:specified"; + case NC_TLS_CTN_SAN_RFC822_NAME: + return "ietf-x509-cert-to-name:san-rfc822-name"; + case NC_TLS_CTN_SAN_DNS_NAME: + return "ietf-x509-cert-to-name:san-dns-name"; + case NC_TLS_CTN_SAN_IP_ADDRESS: + return "ietf-x509-cert-to-name:san-ip-address"; + case NC_TLS_CTN_SAN_ANY: + return "ietf-x509-cert-to-name:san-any"; + case NC_TLS_CTN_COMMON_NAME: + return "ietf-x509-cert-to-name:common-name"; + case NC_TLS_CTN_UNKNOWN: + default: + ERR(NULL, "Unknown CTN mapping type."); + return NULL; + } +} + +static int +_nc_server_config_add_tls_ctn(const struct ly_ctx *ctx, const char *tree_path, const char *fingerprint, + NC_TLS_CTN_MAPTYPE map_type, const char *name, struct lyd_node **config) +{ + int ret = 0; + const char *map; + + NC_CHECK_ARG_RET(NULL, ctx, tree_path, name, config, 1); + + if (fingerprint) { + /* optional */ + ret = nc_server_config_append(ctx, tree_path, "fingerprint", fingerprint, config); + if (ret) { + goto cleanup; + } + } + + /* get map str */ + map = nc_server_config_tls_maptype2str(map_type); + if (!map) { + ret = 1; + goto cleanup; + } + + ret = nc_server_config_append(ctx, tree_path, "map-type", map, config); + if (ret) { + goto cleanup; + } + + ret = nc_server_config_append(ctx, tree_path, "name", name, config); + if (ret) { + goto cleanup; + } + +cleanup: + return ret; +} + +API int +nc_server_config_add_tls_ctn(const struct ly_ctx *ctx, const char *endpt_name, uint32_t id, const char *fingerprint, + NC_TLS_CTN_MAPTYPE map_type, const char *name, struct lyd_node **config) +{ + int ret = 0; + char *path = NULL; + + NC_CHECK_ARG_RET(NULL, ctx, endpt_name, id, name, config, 1); + + ret = asprintf(&path, "/ietf-netconf-server:netconf-server/listen/endpoint[name='%s']/tls/netconf-server-parameters/" + "client-identity-mappings/cert-to-name[id='%u']", endpt_name, id); + NC_CHECK_ERRMEM_GOTO(ret == -1, path = NULL; ret = 1, cleanup); + + ret = _nc_server_config_add_tls_ctn(ctx, path, fingerprint, map_type, name, config); + if (ret) { + ERR(NULL, "Creating new TLS cert-to-name YANG data failed."); + goto cleanup; + } + +cleanup: + free(path); + return ret; +} + +API int +nc_server_config_del_tls_ctn(const char *endpt_name, uint32_t id, struct lyd_node **config) +{ + NC_CHECK_ARG_RET(NULL, endpt_name, config, 1); + + if (id) { + return nc_server_config_delete(config, "/ietf-netconf-server:netconf-server/listen/endpoint[name='%s']/tls/" + "netconf-server-parameters/client-identity-mappings/cert-to-name[id='%u']", endpt_name, id); + } else { + return nc_server_config_delete(config, "/ietf-netconf-server:netconf-server/listen/endpoint[name='%s']/tls/" + "netconf-server-parameters/client-identity-mappings/cert-to-name", endpt_name); + } +} + +API int +nc_server_config_add_ch_tls_ctn(const struct ly_ctx *ctx, const char *client_name, const char *endpt_name, + uint32_t id, const char *fingerprint, NC_TLS_CTN_MAPTYPE map_type, const char *name, struct lyd_node **config) +{ + int ret = 0; + char *path = NULL; + + NC_CHECK_ARG_RET(NULL, ctx, client_name, endpt_name, id, name, config, 1); + + ret = asprintf(&path, "/ietf-netconf-server:netconf-server/call-home/netconf-client[name='%s']/" + "endpoints/endpoint[name='%s']/tls/netconf-server-parameters/client-identity-mappings/" + "cert-to-name[id='%u']", client_name, endpt_name, id); + NC_CHECK_ERRMEM_GOTO(ret == -1, path = NULL; ret = 1, cleanup); + + ret = _nc_server_config_add_tls_ctn(ctx, path, fingerprint, map_type, name, config); + if (ret) { + ERR(NULL, "Creating new CH TLS cert-to-name YANG data failed."); + goto cleanup; + } + +cleanup: + free(path); + return ret; +} + +API int +nc_server_config_del_ch_tls_ctn(const char *client_name, const char *endpt_name, + uint32_t id, struct lyd_node **config) +{ + NC_CHECK_ARG_RET(NULL, client_name, endpt_name, config, 1); + + if (id) { + return nc_server_config_delete(config, "/ietf-netconf-server:netconf-server/call-home/netconf-client[name='%s']/" + "endpoints/endpoint[name='%s']/tls/netconf-server-parameters/client-identity-mappings/" + "cert-to-name[id='%u']", client_name, endpt_name, id); + } else { + return nc_server_config_delete(config, "/ietf-netconf-server:netconf-server/call-home/netconf-client[name='%s']/" + "endpoints/endpoint[name='%s']/tls/netconf-server-parameters/client-identity-mappings/" + "cert-to-name", client_name, endpt_name); + } +} + +API int +nc_server_config_add_tls_endpoint_client_ref(const struct ly_ctx *ctx, const char *endpt_name, const char *referenced_endpt, struct lyd_node **config) +{ + NC_CHECK_ARG_RET(NULL, ctx, endpt_name, referenced_endpt, config, 1); + + return nc_server_config_create(ctx, config, referenced_endpt, "/ietf-netconf-server:netconf-server/listen/endpoint[name='%s']/tls/tls-server-parameters/" + "client-authentication/libnetconf2-netconf-server:endpoint-reference", endpt_name); +} + +API int +nc_server_config_del_tls_endpoint_client_ref(const char *endpt_name, struct lyd_node **config) +{ + NC_CHECK_ARG_RET(NULL, endpt_name, config, 1); + + return nc_server_config_delete(config, "/ietf-netconf-server:netconf-server/listen/endpoint[name='%s']/tls/tls-server-parameters/" + "client-authentication/libnetconf2-netconf-server:endpoint-reference", endpt_name); +} diff --git a/src/session.c b/src/session.c index d148fcf8..ddf10ab5 100644 --- a/src/session.c +++ b/src/session.c @@ -18,8 +18,6 @@ #include #include #include -#include -#include #include #include #include @@ -30,22 +28,21 @@ #include #include "compat.h" -#include "libnetconf.h" -#include "session.h" -#include "session_server.h" +#include "config.h" +#include "log_p.h" +#include "netconf.h" +#include "session_p.h" -#ifdef NC_ENABLED_SSH +#ifdef NC_ENABLED_SSH_TLS -# include +#include +#include +#include +#include +#include +#include -#endif /* NC_ENABLED_SSH */ - -#if defined (NC_ENABLED_SSH) || defined (NC_ENABLED_TLS) - -# include -# include - -#endif /* NC_ENABLED_SSH || NC_ENABLED_TLS */ +#endif /* NC_ENABLED_SSH_TLS */ /* in seconds */ #define NC_CLIENT_HELLO_TIMEOUT 60 @@ -107,25 +104,123 @@ nc_realtime_get(struct timespec *ts) } } +#ifdef NC_ENABLED_SSH_TLS + const char * -nc_keytype2str(NC_SSH_KEY_TYPE type) +nc_privkey_format_to_str(NC_PRIVKEY_FORMAT format) { - switch (type) { - case NC_SSH_KEY_DSA: - return "DSA"; - case NC_SSH_KEY_RSA: + switch (format) { + case NC_PRIVKEY_FORMAT_RSA: return "RSA"; - case NC_SSH_KEY_ECDSA: + case NC_PRIVKEY_FORMAT_EC: return "EC"; + case NC_PRIVKEY_FORMAT_X509: + return NULL; + case NC_PRIVKEY_FORMAT_OPENSSH: + return "OPENSSH"; default: - break; + return NULL; } +} - return NULL; +int +nc_base64_to_bin(const char *base64, char **bin) +{ + BIO *bio, *bio64; + size_t used = 0, size = 0, r = 0; + void *tmp = NULL; + int nl_count, i, remainder; + 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())); + return -1; + } + + bio = BIO_new_mem_buf(b64, strlen(b64)); + if (!bio) { + ERR(NULL, "Error creating a bio (%s).", ERR_reason_error_string(ERR_get_error())); + return -1; + } + + BIO_push(bio64, bio); + + /* store the decoded base64 in bin */ + *bin = NULL; + do { + size += 64; + + tmp = realloc(*bin, size); + if (!tmp) { + free(*bin); + return -1; + } + *bin = tmp; + + r = BIO_read(bio64, *bin + used, 64); + used += r; + } while (r == 64); + + free(b64); + BIO_free_all(bio64); + return size; +} + +int +nc_is_pk_subject_public_key_info(const char *b64) +{ + int ret = 0; + long len; + char *bin = NULL, *tmp; + EVP_PKEY *pkey = NULL; + + /* base64 2 binary */ + len = nc_base64_to_bin(b64, &bin); + if (len == -1) { + ERR(NULL, "Decoding base64 public key to binary failed."); + ret = -1; + goto cleanup; + } + + /* 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 */ + ret = 1; + } else { + /* fail, it's most likely not SubjectPublicKeyInfo pubkey */ + ret = 0; + } + +cleanup: + EVP_PKEY_free(pkey); + free(bin); + return ret; } +#endif /* NC_ENABLED_SSH_TLS */ + int -nc_sock_enable_keepalive(int sock, struct nc_keepalives *ka) +nc_sock_configure_keepalive(int sock, struct nc_keepalives *ka) { int opt; @@ -439,10 +534,7 @@ nc_session_client_msgs_unlock(struct nc_session *session, const char *func) API NC_STATUS nc_session_get_status(const struct nc_session *session) { - if (!session) { - ERRARG("session"); - return NC_STATUS_ERR; - } + NC_CHECK_ARG_RET(session, session, NC_STATUS_ERR); return session->status; } @@ -450,10 +542,7 @@ nc_session_get_status(const struct nc_session *session) API NC_SESSION_TERM_REASON nc_session_get_term_reason(const struct nc_session *session) { - if (!session) { - ERRARG("session"); - return NC_SESSION_TERM_ERR; - } + NC_CHECK_ARG_RET(session, session, NC_SESSION_TERM_ERR); return session->term_reason; } @@ -461,10 +550,7 @@ nc_session_get_term_reason(const struct nc_session *session) API uint32_t nc_session_get_killed_by(const struct nc_session *session) { - if (!session) { - ERRARG("session"); - return 0; - } + NC_CHECK_ARG_RET(session, session, 0); return session->killed_by; } @@ -472,10 +558,7 @@ nc_session_get_killed_by(const struct nc_session *session) API uint32_t nc_session_get_id(const struct nc_session *session) { - if (!session) { - ERRARG("session"); - return 0; - } + NC_CHECK_ARG_RET(session, session, 0); return session->id; } @@ -483,10 +566,7 @@ nc_session_get_id(const struct nc_session *session) API int nc_session_get_version(const struct nc_session *session) { - if (!session) { - ERRARG("session"); - return -1; - } + NC_CHECK_ARG_RET(session, session, -1); return session->version == NC_VERSION_10 ? 0 : 1; } @@ -494,10 +574,7 @@ nc_session_get_version(const struct nc_session *session) API NC_TRANSPORT_IMPL nc_session_get_ti(const struct nc_session *session) { - if (!session) { - ERRARG("session"); - return 0; - } + NC_CHECK_ARG_RET(session, session, 0); return session->ti_type; } @@ -505,10 +582,7 @@ nc_session_get_ti(const struct nc_session *session) API const char * nc_session_get_username(const struct nc_session *session) { - if (!session) { - ERRARG("session"); - return NULL; - } + NC_CHECK_ARG_RET(session, session, NULL); return session->username; } @@ -516,10 +590,7 @@ nc_session_get_username(const struct nc_session *session) API const char * nc_session_get_host(const struct nc_session *session) { - if (!session) { - ERRARG("session"); - return NULL; - } + NC_CHECK_ARG_RET(session, session, NULL); return session->host; } @@ -527,10 +598,8 @@ nc_session_get_host(const struct nc_session *session) API const char * nc_session_get_path(const struct nc_session *session) { - if (!session) { - ERRARG("session"); - return NULL; - } + NC_CHECK_ARG_RET(session, session, NULL); + if (session->ti_type != NC_TI_UNIX) { return NULL; } @@ -541,10 +610,7 @@ nc_session_get_path(const struct nc_session *session) API uint16_t nc_session_get_port(const struct nc_session *session) { - if (!session) { - ERRARG("session"); - return 0; - } + NC_CHECK_ARG_RET(session, session, 0); return session->port; } @@ -552,10 +618,7 @@ nc_session_get_port(const struct nc_session *session) API const struct ly_ctx * nc_session_get_ctx(const struct nc_session *session) { - if (!session) { - ERRARG("session"); - return NULL; - } + NC_CHECK_ARG_RET(session, session, NULL); return session->ctx; } @@ -564,7 +627,7 @@ API void nc_session_set_data(struct nc_session *session, void *data) { if (!session) { - ERRARG("session"); + ERRARG(NULL, "session"); return; } @@ -574,10 +637,7 @@ nc_session_set_data(struct nc_session *session, void *data) API void * nc_session_get_data(const struct nc_session *session) { - if (!session) { - ERRARG("session"); - return NULL; - } + NC_CHECK_ARG_RET(session, session, NULL); return session->data; } @@ -585,10 +645,7 @@ nc_session_get_data(const struct nc_session *session) API int nc_session_is_callhome(const struct nc_session *session) { - if (!session) { - ERRARG("session"); - return 0; - } + NC_CHECK_ARG_RET(session, session, 0); if (session->flags & NC_SESSION_CALLHOME) { return 1; @@ -696,7 +753,7 @@ nc_session_free_transport(struct nc_session *session, int *multisession) (void)siter; break; -#ifdef NC_ENABLED_SSH +#ifdef NC_ENABLED_SSH_TLS case NC_TI_LIBSSH: { int r; @@ -754,24 +811,6 @@ nc_session_free_transport(struct nc_session *session, int *multisession) /* there are still multiple sessions, keep the ring list */ siter->ti.libssh.next = session->ti.libssh.next; } - - /* change nc_sshcb_msg() argument, we need a RUNNING session and this one will be freed */ - if (session->flags & NC_SESSION_SSH_MSG_CB) { - siter = session->ti.libssh.next; - while (siter && (siter->status != NC_STATUS_RUNNING)) { - if (siter->ti.libssh.next == session) { - ERRINT; - break; - } - siter = siter->ti.libssh.next; - } - /* siter may be NULL in case all the sessions terminated at the same time (socket was disconnected), - * we set session to NULL because we do not expect any new message to arrive */ - ssh_set_message_callback(session->ti.libssh.session, nc_sshcb_msg, siter); - if (siter) { - siter->flags |= NC_SESSION_SSH_MSG_CB; - } - } } /* SESSION IO UNLOCK */ @@ -780,9 +819,6 @@ nc_session_free_transport(struct nc_session *session, int *multisession) } break; } -#endif - -#ifdef NC_ENABLED_TLS case NC_TI_OPENSSL: /* remember sock so we can close it */ sock = SSL_get_fd(session->ti.tls); @@ -796,7 +832,7 @@ nc_session_free_transport(struct nc_session *session, int *multisession) X509_free(session->opts.server.client_cert); } break; -#endif +#endif /* NC_ENABLED_SSH_TLS */ case NC_TI_NONE: break; } @@ -896,7 +932,7 @@ nc_session_free(struct nc_session *session, void (*data_free)(void *)) } /* LY ext data */ -#ifdef NC_ENABLED_SSH +#ifdef NC_ENABLED_SSH_TLS struct nc_session *siter; if ((session->flags & NC_SESSION_SHAREDCTX) && session->ti.libssh.next) { @@ -910,7 +946,7 @@ nc_session_free(struct nc_session *session, void (*data_free)(void *)) } } } else -#endif +#endif /* NC_ENABLED_SSH_TLS */ { lyd_free_siblings(session->opts.client.ext_data); } @@ -1032,20 +1068,16 @@ nc_server_get_cpblts_version(const struct ly_ctx *ctx, LYS_VERSION version) uint32_t i, u; LY_ARRAY_COUNT_TYPE v; char *yl_content_id; + uint32_t wd_also_supported; + uint32_t wd_basic_mode; #define NC_CPBLT_BUF_LEN 4096 char str[NC_CPBLT_BUF_LEN]; - if (!ctx) { - ERRARG("ctx"); - return NULL; - } + NC_CHECK_ARG_RET(NULL, ctx, NULL); cpblts = malloc(size * sizeof *cpblts); - if (!cpblts) { - ERRMEM; - goto error; - } + NC_CHECK_ERRMEM_GOTO(!cpblts, , error); cpblts[0] = strdup("urn:ietf:params:netconf:base:1.0"); cpblts[1] = strdup("urn:ietf:params:netconf:base:1.1"); count = 2; @@ -1088,11 +1120,12 @@ nc_server_get_cpblts_version(const struct ly_ctx *ctx, LYS_VERSION version) mod = ly_ctx_get_module_implemented(ctx, "ietf-netconf-with-defaults"); if (mod) { - if (!server_opts.wd_basic_mode) { + wd_basic_mode = ATOMIC_LOAD_RELAXED(server_opts.wd_basic_mode); + if (!wd_basic_mode) { VRB(NULL, "with-defaults capability will not be advertised even though \"ietf-netconf-with-defaults\" model is present, unknown basic-mode."); } else { strcpy(str, "urn:ietf:params:netconf:capability:with-defaults:1.0"); - switch (server_opts.wd_basic_mode) { + switch (wd_basic_mode) { case NC_WD_ALL: strcat(str, "?basic-mode=report-all"); break; @@ -1107,18 +1140,19 @@ nc_server_get_cpblts_version(const struct ly_ctx *ctx, LYS_VERSION version) break; } - if (server_opts.wd_also_supported) { + wd_also_supported = ATOMIC_LOAD_RELAXED(server_opts.wd_also_supported); + if (wd_also_supported) { strcat(str, "&also-supported="); - if (server_opts.wd_also_supported & NC_WD_ALL) { + if (wd_also_supported & NC_WD_ALL) { strcat(str, "report-all,"); } - if (server_opts.wd_also_supported & NC_WD_ALL_TAG) { + if (wd_also_supported & NC_WD_ALL_TAG) { strcat(str, "report-all-tagged,"); } - if (server_opts.wd_also_supported & NC_WD_TRIM) { + if (wd_also_supported & NC_WD_TRIM) { strcat(str, "trim,"); } - if (server_opts.wd_also_supported & NC_WD_EXPLICIT) { + if (wd_also_supported & NC_WD_EXPLICIT) { strcat(str, "explicit,"); } str[strlen(str) - 1] = '\0'; @@ -1145,16 +1179,10 @@ nc_server_get_cpblts_version(const struct ly_ctx *ctx, LYS_VERSION version) /* get content-id */ if (server_opts.content_id_clb) { yl_content_id = server_opts.content_id_clb(server_opts.content_id_data); - if (!yl_content_id) { - ERRMEM; - goto error; - } + NC_CHECK_ERRMEM_GOTO(!yl_content_id, , error); } else { yl_content_id = malloc(11); - if (!yl_content_id) { - ERRMEM; - goto error; - } + NC_CHECK_ERRMEM_GOTO(!yl_content_id, , error); sprintf(yl_content_id, "%u", ly_ctx_get_change_count(ctx)); } @@ -1261,10 +1289,7 @@ parse_cpblts(struct lyd_node *capabilities, char ***list) } /* last item remains NULL */ *list = calloc(i + 1, sizeof **list); - if (!*list) { - ERRMEM; - return -1; - } + NC_CHECK_ERRMEM_RET(!*list, -1); i = 0; } @@ -1294,10 +1319,7 @@ parse_cpblts(struct lyd_node *capabilities, char ***list) /* store capabilities */ if (list) { (*list)[i] = strndup(cpb_start, cpb_end - cpb_start); - if (!(*list)[i]) { - ERRMEM; - return -1; - } + NC_CHECK_ERRMEM_RET(!(*list)[i], -1); i++; } } @@ -1320,10 +1342,7 @@ nc_send_hello_io(struct nc_session *session) if (session->side == NC_CLIENT) { /* client side hello - send only NETCONF base capabilities */ cpblts = malloc(3 * sizeof *cpblts); - if (!cpblts) { - ERRMEM; - return NC_MSG_ERROR; - } + NC_CHECK_ERRMEM_RET(!cpblts, NC_MSG_ERROR); cpblts[0] = strdup("urn:ietf:params:netconf:base:1.0"); cpblts[1] = strdup("urn:ietf:params:netconf:base:1.1"); cpblts[2] = NULL; @@ -1513,244 +1532,3 @@ nc_handshake_io(struct nc_session *session) return type; } - -#ifdef NC_ENABLED_SSH - -static void -nc_ssh_init(void) -{ -#if (LIBSSH_VERSION_INT < SSH_VERSION_INT(0, 8, 0)) - ssh_threads_set_callbacks(ssh_threads_get_pthread()); - ssh_init(); -#endif -} - -static void -nc_ssh_destroy(void) -{ -#if OPENSSL_VERSION_NUMBER < 0x10100000L // < 1.1.0 - FIPS_mode_set(0); - CONF_modules_unload(1); - nc_thread_destroy(); -#endif - -#if (LIBSSH_VERSION_INT < SSH_VERSION_INT(0, 8, 0)) - ssh_finalize(); -#endif -} - -#endif /* NC_ENABLED_SSH */ - -#ifdef NC_ENABLED_TLS - -#if OPENSSL_VERSION_NUMBER < 0x10100000L // < 1.1.0 - -struct CRYPTO_dynlock_value { - pthread_mutex_t lock; -}; - -static struct CRYPTO_dynlock_value * -tls_dyn_create_func(const char *UNUSED(file), int UNUSED(line)) -{ - struct CRYPTO_dynlock_value *value; - - value = malloc(sizeof *value); - if (!value) { - ERRMEM; - return NULL; - } - pthread_mutex_init(&value->lock, NULL); - - return value; -} - -static void -tls_dyn_lock_func(int mode, struct CRYPTO_dynlock_value *l, const char *UNUSED(file), int UNUSED(line)) -{ - /* mode can also be CRYPTO_READ or CRYPTO_WRITE, but all the examples - * I found ignored this fact, what do I know... */ - if (mode & CRYPTO_LOCK) { - pthread_mutex_lock(&l->lock); - } else { - pthread_mutex_unlock(&l->lock); - } -} - -static void -tls_dyn_destroy_func(struct CRYPTO_dynlock_value *l, const char *UNUSED(file), int UNUSED(line)) -{ - pthread_mutex_destroy(&l->lock); - free(l); -} - -#endif - -#endif /* NC_ENABLED_TLS */ - -#if defined (NC_ENABLED_TLS) && !defined (NC_ENABLED_SSH) - -#if OPENSSL_VERSION_NUMBER < 0x10100000L // < 1.1.0 -static pthread_mutex_t *tls_locks; - -static void -tls_thread_locking_func(int mode, int n, const char *UNUSED(file), int UNUSED(line)) -{ - if (mode & CRYPTO_LOCK) { - pthread_mutex_lock(tls_locks + n); - } else { - pthread_mutex_unlock(tls_locks + n); - } -} - -static void -tls_thread_id_func(CRYPTO_THREADID *tid) -{ - CRYPTO_THREADID_set_numeric(tid, (unsigned long)pthread_self()); -} - -#endif - -static void -nc_tls_init(void) -{ -#if OPENSSL_VERSION_NUMBER < 0x10100000L // < 1.1.0 - SSL_load_error_strings(); - ERR_load_BIO_strings(); - SSL_library_init(); - - int i; - - tls_locks = malloc(CRYPTO_num_locks() * sizeof *tls_locks); - if (!tls_locks) { - ERRMEM; - return; - } - for (i = 0; i < CRYPTO_num_locks(); ++i) { - pthread_mutex_init(tls_locks + i, NULL); - } - - CRYPTO_THREADID_set_callback(tls_thread_id_func); - CRYPTO_set_locking_callback(tls_thread_locking_func); - - CRYPTO_set_dynlock_create_callback(tls_dyn_create_func); - CRYPTO_set_dynlock_lock_callback(tls_dyn_lock_func); - CRYPTO_set_dynlock_destroy_callback(tls_dyn_destroy_func); -#endif -} - -static void -nc_tls_destroy(void) -{ -#if OPENSSL_VERSION_NUMBER < 0x10100000L // < 1.1.0 - FIPS_mode_set(0); - CRYPTO_cleanup_all_ex_data(); - nc_thread_destroy(); - EVP_cleanup(); - ERR_free_strings(); -#if OPENSSL_VERSION_NUMBER < 0x10002000L // < 1.0.2 - sk_SSL_COMP_free(SSL_COMP_get_compression_methods()); -#elif OPENSSL_VERSION_NUMBER < 0x10100000L // < 1.1.0 - SSL_COMP_free_compression_methods(); -#endif - - int i; - - CRYPTO_THREADID_set_callback(NULL); - CRYPTO_set_locking_callback(NULL); - for (i = 0; i < CRYPTO_num_locks(); ++i) { - pthread_mutex_destroy(tls_locks + i); - } - free(tls_locks); - - CRYPTO_set_dynlock_create_callback(NULL); - CRYPTO_set_dynlock_lock_callback(NULL); - CRYPTO_set_dynlock_destroy_callback(NULL); -#endif -} - -#endif /* NC_ENABLED_TLS && !NC_ENABLED_SSH */ - -#if defined (NC_ENABLED_SSH) && defined (NC_ENABLED_TLS) - -static void -nc_ssh_tls_init(void) -{ -#if OPENSSL_VERSION_NUMBER < 0x10100000L // < 1.1.0 - SSL_load_error_strings(); - ERR_load_BIO_strings(); - SSL_library_init(); -#endif - - nc_ssh_init(); - -#if OPENSSL_VERSION_NUMBER < 0x10100000L // < 1.1.0 - CRYPTO_set_dynlock_create_callback(tls_dyn_create_func); - CRYPTO_set_dynlock_lock_callback(tls_dyn_lock_func); - CRYPTO_set_dynlock_destroy_callback(tls_dyn_destroy_func); -#endif -} - -static void -nc_ssh_tls_destroy(void) -{ -#if OPENSSL_VERSION_NUMBER < 0x10100000L // < 1.1.0 - ERR_free_strings(); -# if OPENSSL_VERSION_NUMBER < 0x10002000L // < 1.0.2 - sk_SSL_COMP_free(SSL_COMP_get_compression_methods()); -# elif OPENSSL_VERSION_NUMBER < 0x10100000L // < 1.1.0 - SSL_COMP_free_compression_methods(); -# endif -#endif - - nc_ssh_destroy(); - -#if OPENSSL_VERSION_NUMBER < 0x10100000L // < 1.1.0 - CRYPTO_set_dynlock_create_callback(NULL); - CRYPTO_set_dynlock_lock_callback(NULL); - CRYPTO_set_dynlock_destroy_callback(NULL); -#endif -} - -#endif /* NC_ENABLED_SSH && NC_ENABLED_TLS */ - -#if defined (NC_ENABLED_SSH) || defined (NC_ENABLED_TLS) - -API void -nc_thread_destroy(void) -{ - /* caused data-races and seems not neccessary for avoiding valgrind reachable memory */ - // CRYPTO_cleanup_all_ex_data(); - -#if OPENSSL_VERSION_NUMBER < 0x10100000L // < 1.1.0 - CRYPTO_THREADID crypto_tid; - - CRYPTO_THREADID_current(&crypto_tid); - ERR_remove_thread_state(&crypto_tid); -#endif -} - -#endif /* NC_ENABLED_SSH || NC_ENABLED_TLS */ - -void -nc_init(void) -{ -#if defined (NC_ENABLED_SSH) && defined (NC_ENABLED_TLS) - nc_ssh_tls_init(); -#elif defined (NC_ENABLED_SSH) - nc_ssh_init(); -#elif defined (NC_ENABLED_TLS) - nc_tls_init(); -#endif -} - -void -nc_destroy(void) -{ -#if defined (NC_ENABLED_SSH) && defined (NC_ENABLED_TLS) - nc_ssh_tls_destroy(); -#elif defined (NC_ENABLED_SSH) - nc_ssh_destroy(); -#elif defined (NC_ENABLED_TLS) - nc_tls_destroy(); -#endif -} diff --git a/src/session.h b/src/session.h index a533477f..52626e8a 100644 --- a/src/session.h +++ b/src/session.h @@ -23,7 +23,7 @@ extern "C" { #include "netconf.h" -#ifdef NC_ENABLED_SSH +#ifdef NC_ENABLED_SSH_TLS /** * @brief Enumeration of NETCONF SSH authentication methods @@ -34,9 +34,16 @@ typedef enum { NC_SSH_AUTH_INTERACTIVE = 0x04 /**< interactive SSH authentication */ } NC_SSH_AUTH_TYPE; -#endif /* NC_ENABLED_SSH */ - -#ifdef NC_ENABLED_TLS +/** + * @brief Enumeration of host key checking and known_hosts entry adding modes + */ +typedef enum { + NC_SSH_KNOWNHOSTS_ASK = 0, /**< add a known_hosts entry, but with a prompt */ + NC_SSH_KNOWNHOSTS_STRICT, /**< do not add a known_hosts entry and the server's host key must be present in the configured known_hosts file */ + NC_SSH_KNOWNHOSTS_ACCEPT_NEW, /**< add a known_hosts entry without a prompt */ + NC_SSH_KNOWNHOSTS_ACCEPT, /**< add a known_hosts entry without a prompt and allow connections to servers which changed their host key */ + NC_SSH_KNOWNHOSTS_SKIP /**< do not add a known_hosts entry and skip all host key checks */ +} NC_SSH_KNOWNHOSTS_MODE; /** * @brief Enumeration of cert-to-name mapping types @@ -51,7 +58,17 @@ typedef enum { NC_TLS_CTN_COMMON_NAME /**< common name as username */ } NC_TLS_CTN_MAPTYPE; -#endif /* NC_ENABLED_TLS */ +/** + * @brief Enumeration of TLS versions. + */ +typedef enum { + NC_TLS_VERSION_10 = 1, /**< TLS1.0 */ + NC_TLS_VERSION_11 = 2, /**< TLS1.1 */ + NC_TLS_VERSION_12 = 4, /**< TLS1.2 */ + NC_TLS_VERSION_13 = 8 /**< TLS1.3 */ +} NC_TLS_VERSION; + +#endif /* NC_ENABLED_SSH_TLS */ /** * @brief Enumeration of possible session statuses @@ -72,12 +89,11 @@ typedef enum { NC_TI_FD, /**< file descriptors - use standard input/output, transport protocol is implemented outside the current application */ NC_TI_UNIX, /**< unix socket */ -#ifdef NC_ENABLED_SSH +#ifdef NC_ENABLED_SSH_TLS NC_TI_LIBSSH, /**< libssh - use libssh library, only for NETCONF over SSH transport */ -#endif -#ifdef NC_ENABLED_TLS + NC_TI_OPENSSL /**< OpenSSL - use OpenSSL library, only for NETCONF over TLS transport */ -#endif +#endif /* NC_ENABLED_SSH_TLS */ } NC_TRANSPORT_IMPL; /** @@ -98,16 +114,6 @@ typedef enum { NC_CH_RANDOM } NC_CH_START_WITH; -/** - * @brief Enumeration of SSH key types. - */ -typedef enum { - NC_SSH_KEY_UNKNOWN = 0, - NC_SSH_KEY_DSA, - NC_SSH_KEY_RSA, - NC_SSH_KEY_ECDSA -} NC_SSH_KEY_TYPE; - /** * @brief NETCONF session object */ @@ -233,20 +239,6 @@ int nc_session_is_callhome(const struct nc_session *session); */ void nc_session_free(struct nc_session *session, void (*data_free)(void *)); -#if defined (NC_ENABLED_SSH) || defined (NC_ENABLED_TLS) - -/** - * @brief Free all the dynamically allocated thread-specific libssl/libcrypto - * resources. - * - * This function should be called only if init (nc_client_init(), respectively nc_server_init()) was called. - * Call it in every thread your application creates just before the thread exits. In the last thread - * (usually the main one) call nc_client_destroy(), respectively nc_server_destroy(). - */ -void nc_thread_destroy(void); - -#endif /* NC_ENABLED_SSH || NC_ENABLED_TLS */ - #ifdef __cplusplus } #endif diff --git a/src/session_client.c b/src/session_client.c index 00d78db4..0b4571ad 100644 --- a/src/session_client.c +++ b/src/session_client.c @@ -42,21 +42,23 @@ #include #include "compat.h" -#include "libnetconf.h" -#include "messages_client.h" +#include "config.h" +#include "log_p.h" +#include "messages_p.h" #include "session_client.h" +#include "session_client_ch.h" +#include "session_p.h" #include "../modules/ietf_netconf@2013-09-29_yang.h" #include "../modules/ietf_netconf_monitoring@2010-10-04_yang.h" static const char *ncds2str[] = {NULL, "config", "url", "running", "startup", "candidate"}; -#ifdef NC_ENABLED_SSH -int sshauth_hostkey_check(const char *hostname, ssh_session session, void *priv); +#ifdef NC_ENABLED_SSH_TLS char *sshauth_password(const char *username, const char *hostname, void *priv); char *sshauth_interactive(const char *auth_name, const char *instruction, const char *prompt, int echo, void *priv); char *sshauth_privkey_passphrase(const char *privkey_path, void *priv); -#endif /* NC_ENABLED_SSH */ +#endif /* NC_ENABLED_SSH_TLS */ static pthread_once_t nc_client_context_once = PTHREAD_ONCE_INIT; static pthread_key_t nc_client_context_key; @@ -68,22 +70,20 @@ static struct nc_client_context context_main = { .max_probes = 10, .probe_interval = 5 }, -#ifdef NC_ENABLED_SSH +#ifdef NC_ENABLED_SSH_TLS .ssh_opts = { .auth_pref = {{NC_SSH_AUTH_INTERACTIVE, 1}, {NC_SSH_AUTH_PASSWORD, 2}, {NC_SSH_AUTH_PUBLICKEY, 3}}, - .auth_hostkey_check = sshauth_hostkey_check, .auth_password = sshauth_password, .auth_interactive = sshauth_interactive, .auth_privkey_passphrase = sshauth_privkey_passphrase }, .ssh_ch_opts = { .auth_pref = {{NC_SSH_AUTH_INTERACTIVE, 1}, {NC_SSH_AUTH_PASSWORD, 2}, {NC_SSH_AUTH_PUBLICKEY, 3}}, - .auth_hostkey_check = sshauth_hostkey_check, .auth_password = sshauth_password, .auth_interactive = sshauth_interactive, .auth_privkey_passphrase = sshauth_privkey_passphrase }, -#endif /* NC_ENABLED_SSH */ +#endif /* NC_ENABLED_SSH_TLS */ /* .tls_ structures zeroed */ .refcount = 0 }; @@ -110,7 +110,7 @@ nc_client_context_free(void *ptr) /* for the main thread the same is done in nc_client_destroy() */ free(c->opts.schema_searchpath); -#if defined (NC_ENABLED_SSH) || defined (NC_ENABLED_TLS) +#ifdef NC_ENABLED_SSH_TLS int i; for (i = 0; i < c->opts.ch_bind_count; ++i) { @@ -120,15 +120,13 @@ nc_client_context_free(void *ptr) free(c->opts.ch_binds); c->opts.ch_binds = NULL; c->opts.ch_bind_count = 0; -#endif -#ifdef NC_ENABLED_SSH + _nc_client_ssh_destroy_opts(&c->ssh_opts); _nc_client_ssh_destroy_opts(&c->ssh_ch_opts); -#endif -#ifdef NC_ENABLED_TLS + _nc_client_tls_destroy_opts(&c->tls_opts); _nc_client_tls_destroy_opts(&c->tls_ch_opts); -#endif +#endif /* NC_ENABLED_SSH_TLS */ free(c); } } @@ -162,14 +160,13 @@ nc_client_context_location(void) e = calloc(1, sizeof *e); /* set default values */ e->refcount = 1; -#ifdef NC_ENABLED_SSH +#ifdef NC_ENABLED_SSH_TLS e->ssh_opts.auth_pref[0].type = NC_SSH_AUTH_INTERACTIVE; e->ssh_opts.auth_pref[0].value = 1; e->ssh_opts.auth_pref[1].type = NC_SSH_AUTH_PASSWORD; e->ssh_opts.auth_pref[1].value = 2; e->ssh_opts.auth_pref[2].type = NC_SSH_AUTH_PUBLICKEY; e->ssh_opts.auth_pref[2].value = 3; - e->ssh_opts.auth_hostkey_check = sshauth_hostkey_check; e->ssh_opts.auth_password = sshauth_password; e->ssh_opts.auth_interactive = sshauth_interactive; e->ssh_opts.auth_privkey_passphrase = sshauth_privkey_passphrase; @@ -179,7 +176,7 @@ nc_client_context_location(void) e->ssh_ch_opts.auth_pref[0].value = 1; e->ssh_ch_opts.auth_pref[1].value = 2; e->ssh_ch_opts.auth_pref[2].value = 3; -#endif /* NC_ENABLED_SSH */ +#endif /* NC_ENABLED_SSH_TLS */ } pthread_setspecific(nc_client_context_key, e); } @@ -201,7 +198,7 @@ nc_client_set_thread_context(void *context) struct nc_client_context *old, *new; if (!context) { - ERRARG(context); + ERRARG(NULL, "context"); return; } @@ -282,10 +279,7 @@ nc_client_set_schema_searchpath(const char *path) if (path) { client_opts.schema_searchpath = strdup(path); - if (!client_opts.schema_searchpath) { - ERRMEM; - return 1; - } + NC_CHECK_ERRMEM_RET(!client_opts.schema_searchpath, 1); } else { client_opts.schema_searchpath = NULL; } @@ -923,11 +917,7 @@ build_module_info_yl(struct nc_session *session, int get_data_sup, int xpath_sup } (*result) = calloc(modules->count + 1, sizeof **result); - if (!(*result)) { - ERRMEM; - ret = -1; - goto cleanup; - } + NC_CHECK_ERRMEM_GOTO(!(*result), ret = -1, cleanup); for (u = 0; u < modules->count; ++u) { submodules_count = 0; @@ -950,13 +940,7 @@ build_module_info_yl(struct nc_session *session, int get_data_sup, int xpath_sup (*result)[u].implemented = !strcmp(lyd_get_value(iter), "implement"); } else if (!strcmp(iter->schema->name, "feature")) { (*result)[u].features = nc_realloc((*result)[u].features, (feature_count + 2) * sizeof *(*result)[u].features); - if (!(*result)[u].features) { - ERRMEM; - free_module_info(*result); - *result = NULL; - ret = -1; - goto cleanup; - } + NC_CHECK_ERRMEM_GOTO(!(*result)[u].features, free_module_info(*result); *result = NULL; ret = -1, cleanup); (*result)[u].features[feature_count] = strdup(lyd_get_value(iter)); (*result)[u].features[feature_count + 1] = NULL; ++feature_count; @@ -967,25 +951,18 @@ build_module_info_yl(struct nc_session *session, int get_data_sup, int xpath_sup if (submodules_count) { (*result)[u].submodules = calloc(submodules_count + 1, sizeof *(*result)[u].submodules); - if (!(*result)[u].submodules) { - ERRMEM; - free_module_info(*result); - *result = NULL; - ret = -1; - goto cleanup; - } else { - v = 0; - LY_LIST_FOR(lyd_child(modules->dnodes[u]), iter) { - mod = modules->dnodes[u]->schema->module; - if ((mod == iter->schema->module) && !strcmp(iter->schema->name, "submodule")) { - LY_LIST_FOR(lyd_child(iter), child) { - if (mod != child->schema->module) { - continue; - } else if (!strcmp(child->schema->name, "name")) { - (*result)[u].submodules[v].name = strdup(lyd_get_value(child)); - } else if (!strcmp(child->schema->name, "revision")) { - (*result)[u].submodules[v].revision = strdup(lyd_get_value(child)); - } + NC_CHECK_ERRMEM_GOTO(!(*result)[u].submodules, free_module_info(*result); *result = NULL; ret = -1, cleanup); + v = 0; + LY_LIST_FOR(lyd_child(modules->dnodes[u]), iter) { + mod = modules->dnodes[u]->schema->module; + if ((mod == iter->schema->module) && !strcmp(iter->schema->name, "submodule")) { + LY_LIST_FOR(lyd_child(iter), child) { + if (mod != child->schema->module) { + continue; + } else if (!strcmp(child->schema->name, "name")) { + (*result)[u].submodules[v].name = strdup(lyd_get_value(child)); + } else if (!strcmp(child->schema->name, "revision")) { + (*result)[u].submodules[v].revision = strdup(lyd_get_value(child)); } } } @@ -1015,10 +992,7 @@ build_module_info_cpblts(char **cpblts, struct module_info **result) for (u = 0; cpblts[u]; ++u) {} (*result) = calloc(u + 1, sizeof **result); - if (!(*result)) { - ERRMEM; - return -1; - } + NC_CHECK_ERRMEM_RET(!(*result), -1); for (u = v = 0; cpblts[u]; ++u) { module_cpblt = strstr(cpblts[u], "module="); @@ -1388,19 +1362,16 @@ nc_connect_inout(int fdin, int fdout, struct ly_ctx *ctx) struct nc_session *session; if (fdin < 0) { - ERRARG("fdin"); + ERRARG(NULL, "fdin"); return NULL; } else if (fdout < 0) { - ERRARG("fdout"); + ERRARG(NULL, "fdout"); return NULL; } /* prepare session structure */ session = nc_new_session(NC_CLIENT, 0); - if (!session) { - ERRMEM; - return NULL; - } + NC_CHECK_ERRMEM_RET(!session, NULL); session->status = NC_STATUS_STARTING; /* transport specific data */ @@ -1441,10 +1412,7 @@ nc_connect_unix(const char *address, struct ly_ctx *ctx) char *buf = NULL; size_t buf_size = 0; - if (address == NULL) { - ERRARG("address"); - return NULL; - } + NC_CHECK_ARG_RET(NULL, address, NULL); sock = socket(AF_UNIX, SOCK_STREAM, 0); if (sock < 0) { @@ -1468,10 +1436,7 @@ nc_connect_unix(const char *address, struct ly_ctx *ctx) /* prepare session structure */ session = nc_new_session(NC_CLIENT, 0); - if (!session) { - ERRMEM; - goto fail; - } + NC_CHECK_ERRMEM_GOTO(!session, , fail); session->status = NC_STATUS_STARTING; /* transport specific data */ @@ -1486,17 +1451,14 @@ nc_connect_unix(const char *address, struct ly_ctx *ctx) session->path = strdup(address); - pw = nc_getpwuid(geteuid(), &pw_buf, &buf, &buf_size); + pw = nc_getpw(geteuid(), NULL, &pw_buf, &buf, &buf_size); if (!pw) { ERR(NULL, "Failed to find username for UID %u.", (unsigned int)geteuid()); goto fail; } username = strdup(pw->pw_name); free(buf); - if (!username) { - ERRMEM; - goto fail; - } + NC_CHECK_ERRMEM_GOTO(!username, , fail); session->username = username; /* NETCONF handshake */ @@ -1538,10 +1500,7 @@ nc_saddr2str(const struct sockaddr *saddr, char **str_ip, uint16_t *port) str_len = (saddr->sa_family == AF_INET) ? INET_ADDRSTRLEN : INET6_ADDRSTRLEN; *str_ip = malloc(str_len); - if (!*str_ip) { - ERRMEM; - return -1; - } + NC_CHECK_ERRMEM_RET(!(*str_ip), -1); if (saddr->sa_family == AF_INET) { addr = &((struct sockaddr_in *)saddr)->sin_addr; @@ -1650,7 +1609,7 @@ sock_connect(int timeout_ms, int *sock_pending, struct addrinfo *res, struct nc_ } /* enable keep-alive */ - if (nc_sock_enable_keepalive(sock, ka)) { + if (nc_sock_configure_keepalive(sock, ka)) { goto cleanup; } @@ -1754,20 +1713,14 @@ nc_sock_connect(const char *host, uint16_t port, int timeout_ms, struct nc_keepa return -1; } -#if defined (NC_ENABLED_SSH) || defined (NC_ENABLED_TLS) +#ifdef NC_ENABLED_SSH_TLS int nc_client_ch_add_bind_listen(const char *address, uint16_t port, const char *hostname, NC_TRANSPORT_IMPL ti) { int sock; - if (!address) { - ERRARG("address"); - return -1; - } else if (!port) { - ERRARG("port"); - return -1; - } + NC_CHECK_ARG_RET(NULL, address, port, -1); sock = nc_sock_listen_inet(address, port, &client_opts.ka); if (sock == -1) { @@ -1859,32 +1812,25 @@ nc_accept_callhome(int timeout, struct ly_ctx *ctx, struct nc_session **session) char *host = NULL; uint16_t port, idx; + NC_CHECK_ARG_RET(NULL, session, -1); + if (!client_opts.ch_binds) { ERRINIT; return -1; - } else if (!session) { - ERRARG("session"); - return -1; } - sock = nc_sock_accept_binds(client_opts.ch_binds, client_opts.ch_bind_count, timeout, &host, &port, &idx); + sock = nc_sock_accept_binds(client_opts.ch_binds, client_opts.ch_bind_count, &client_opts.ch_bind_lock, timeout, &host, &port, &idx); if (sock < 1) { free(host); return sock; } -#ifdef NC_ENABLED_SSH if (client_opts.ch_binds_aux[idx].ti == NC_TI_LIBSSH) { *session = nc_accept_callhome_ssh_sock(sock, host, port, ctx, NC_TRANSPORT_TIMEOUT); - } else -#endif -#ifdef NC_ENABLED_TLS - if (client_opts.ch_binds_aux[idx].ti == NC_TI_OPENSSL) { + } else if (client_opts.ch_binds_aux[idx].ti == NC_TI_OPENSSL) { *session = nc_accept_callhome_tls_sock(sock, host, port, ctx, NC_TRANSPORT_TIMEOUT, client_opts.ch_binds_aux[idx].hostname); - } else -#endif - { + } else { close(sock); *session = NULL; } @@ -1898,15 +1844,12 @@ nc_accept_callhome(int timeout, struct ly_ctx *ctx, struct nc_session **session) return 1; } -#endif /* NC_ENABLED_SSH || NC_ENABLED_TLS */ +#endif /* NC_ENABLED_SSH_TLS */ API const char * const * nc_session_get_cpblts(const struct nc_session *session) { - if (!session) { - ERRARG("session"); - return NULL; - } + NC_CHECK_ARG_RET(session, session, NULL); return (const char * const *)session->opts.client.cpblts; } @@ -1916,13 +1859,7 @@ nc_session_cpblt(const struct nc_session *session, const char *capab) { int i, len; - if (!session) { - ERRARG("session"); - return NULL; - } else if (!capab) { - ERRARG("capab"); - return NULL; - } + NC_CHECK_ARG_RET(session, session, capab, NULL); len = strlen(capab); for (i = 0; session->opts.client.cpblts[i]; ++i) { @@ -1937,34 +1874,39 @@ nc_session_cpblt(const struct nc_session *session, const char *capab) API int nc_session_ntf_thread_running(const struct nc_session *session) { - if (!session || (session->side != NC_CLIENT)) { - ERRARG("session"); + NC_CHECK_ARG_RET(session, session, 0); + + if (session->side != NC_CLIENT) { + ERRARG(NULL, "session"); return 0; } return ATOMIC_LOAD_RELAXED(session->opts.client.ntf_thread_running); } -API void +API int nc_client_init(void) { - nc_init(); + int r; + + if ((r = pthread_mutex_init(&client_opts.ch_bind_lock, NULL))) { + ERR(NULL, "%s: failed to init bind lock(%s).", __func__, strerror(r)); + return -1; + } + + return 0; } API void nc_client_destroy(void) { + pthread_mutex_destroy(&client_opts.ch_bind_lock); nc_client_set_schema_searchpath(NULL); -#if defined (NC_ENABLED_SSH) || defined (NC_ENABLED_TLS) +#ifdef NC_ENABLED_SSH_TLS nc_client_ch_del_bind(NULL, 0, 0); -#endif -#ifdef NC_ENABLED_SSH nc_client_ssh_destroy_opts(); -#endif -#ifdef NC_ENABLED_TLS nc_client_tls_destroy_opts(); -#endif - nc_destroy(); +#endif /* NC_ENABLED_SSH_TLS */ } static NC_MSG_TYPE @@ -2133,11 +2075,7 @@ recv_msg(struct nc_session *session, int timeout, NC_MSG_TYPE expected, struct l cont_ptr = &((*cont_ptr)->next); } *cont_ptr = malloc(sizeof **cont_ptr); - if (!*cont_ptr) { - ERRMEM; - ret = NC_MSG_ERROR; - goto cleanup_unlock; - } + NC_CHECK_ERRMEM_GOTO(!*cont_ptr, ret = NC_MSG_ERROR, cleanup_unlock); (*cont_ptr)->msg = msg; msg = NULL; (*cont_ptr)->type = ret; @@ -2360,19 +2298,9 @@ nc_recv_reply(struct nc_session *session, struct nc_rpc *rpc, uint64_t msgid, in { NC_MSG_TYPE ret; - if (!session) { - ERRARG("session"); - return NC_MSG_ERROR; - } else if (!rpc) { - ERRARG("rpc"); - return NC_MSG_ERROR; - } else if (!envp) { - ERRARG("envp"); - return NC_MSG_ERROR; - } else if (!op) { - ERRARG("op"); - return NC_MSG_ERROR; - } else if ((session->status != NC_STATUS_RUNNING) || (session->side != NC_CLIENT)) { + NC_CHECK_ARG_RET(session, session, rpc, envp, op, NC_MSG_ERROR); + + if ((session->status != NC_STATUS_RUNNING) || (session->side != NC_CLIENT)) { ERR(session, "Invalid session to receive RPC replies."); return NC_MSG_ERROR; } @@ -2429,16 +2357,9 @@ recv_notif(struct nc_session *session, int timeout, struct lyd_node **envp, stru API NC_MSG_TYPE nc_recv_notif(struct nc_session *session, int timeout, struct lyd_node **envp, struct lyd_node **op) { - if (!session) { - ERRARG("session"); - return NC_MSG_ERROR; - } else if (!envp) { - ERRARG("envp"); - return NC_MSG_ERROR; - } else if (!op) { - ERRARG("op"); - return NC_MSG_ERROR; - } else if ((session->status != NC_STATUS_RUNNING) || (session->side != NC_CLIENT)) { + NC_CHECK_ARG_RET(session, session, envp, op, NC_MSG_ERROR); + + if ((session->status != NC_STATUS_RUNNING) || (session->side != NC_CLIENT)) { ERR(session, "Invalid session to receive Notifications."); return NC_MSG_ERROR; } @@ -2511,22 +2432,16 @@ nc_recv_notif_dispatch_data(struct nc_session *session, nc_notif_dispatch_clb no pthread_t tid; int ret; - if (!session) { - ERRARG("session"); - return -1; - } else if (!notif_clb) { - ERRARG("notif_clb"); - return -1; - } else if ((session->status != NC_STATUS_RUNNING) || (session->side != NC_CLIENT)) { + NC_CHECK_ARG_RET(session, session, notif_clb, -1); + + if ((session->status != NC_STATUS_RUNNING) || (session->side != NC_CLIENT)) { ERR(session, "Invalid session to receive Notifications."); return -1; } ntarg = malloc(sizeof *ntarg); - if (!ntarg) { - ERRMEM; - return -1; - } + NC_CHECK_ERRMEM_RET(!ntarg, -1); + ntarg->session = session; ntarg->notif_clb = notif_clb; ntarg->user_data = user_data; @@ -2603,16 +2518,9 @@ nc_send_rpc(struct nc_session *session, struct nc_rpc *rpc, int timeout, uint64_ char str[11]; uint64_t cur_msgid; - if (!session) { - ERRARG("session"); - return NC_MSG_ERROR; - } else if (!rpc) { - ERRARG("rpc"); - return NC_MSG_ERROR; - } else if (!msgid) { - ERRARG("msgid"); - return NC_MSG_ERROR; - } else if ((session->status != NC_STATUS_RUNNING) || (session->side != NC_CLIENT)) { + NC_CHECK_ARG_RET(session, session, rpc, msgid, NC_MSG_ERROR); + + if ((session->status != NC_STATUS_RUNNING) || (session->side != NC_CLIENT)) { ERR(session, "Invalid session to send RPCs."); return NC_MSG_ERROR; } @@ -3195,7 +3103,7 @@ API void nc_client_session_set_not_strict(struct nc_session *session) { if (session->side != NC_CLIENT) { - ERRARG("session"); + ERRARG(NULL, "session"); return; } diff --git a/src/session_client.h b/src/session_client.h index 6115fcbb..3ac3c509 100644 --- a/src/session_client.h +++ b/src/session_client.h @@ -22,13 +22,10 @@ extern "C" { #include -#ifdef NC_ENABLED_SSH +#ifdef NC_ENABLED_SSH_TLS # include -#endif - -#ifdef NC_ENABLED_TLS # include -#endif +#endif /* NC_ENABLED_SSH_TLS */ #include "messages_client.h" #include "netconf.h" @@ -116,9 +113,11 @@ void nc_client_set_thread_context(void *context); void *nc_client_get_thread_context(void); /** - * @brief Initialize libssh and/or libssl/libcrypto for use in the client. + * @brief Initialize client for establishing connections. + * + * @return 0 on success, -1 on error. */ -void nc_client_init(void); +int nc_client_init(void); /** * @brief Destroy all libssh and/or libssl/libcrypto dynamic memory and @@ -169,7 +168,7 @@ struct nc_session *nc_connect_unix(const char *address, struct ly_ctx *ctx); /** @} Client Session */ -#ifdef NC_ENABLED_SSH +#ifdef NC_ENABLED_SSH_TLS /** * @defgroup client_ssh Client SSH @@ -180,28 +179,23 @@ struct nc_session *nc_connect_unix(const char *address, struct ly_ctx *ctx); */ /** - * @brief Set SSH authentication hostkey check (knownhosts) callback. - * - * Repetitive calling causes replacing of the previous callback and its private data. Caller is responsible for - * freeing the private data when necessary (the private data can be obtained by - * nc_client_ssh_get_auth_hostkey_check_clb()). + * @brief Set the behaviour of checking the host key and adding/reading entries to/from the known_hosts file. * - * @param[in] auth_hostkey_check Function to call, returns 0 on success, non-zero in error. - * If NULL, the default callback is set. - * @param[in] priv Optional private data to be passed to the callback function. + * @param[in] mode Server host key checking mode. */ -void nc_client_ssh_set_auth_hostkey_check_clb(int (*auth_hostkey_check)(const char *hostname, ssh_session session, void *priv), - void *priv); +void nc_client_ssh_set_knownhosts_mode(NC_SSH_KNOWNHOSTS_MODE mode); /** - * @brief Get currently set SSH authentication hostkey check (knownhosts) callback and its private data previously set - * by nc_client_ssh_set_auth_hostkey_check_clb(). + * @brief Set the path to the known_hosts file. * - * @param[out] auth_hostkey_check Currently set callback, NULL in case of the default callback. - * @param[out] priv Currently set (optional) private data to be passed to the callback function. + * Repetetive calling replaces the value. If the given file doesn't exist and the process has sufficient + * rights, it gets created whenever the file is needed, otherwise an error occurs. If NULL is passed or the + * path isn't set, the default known_hosts file will be used. + * + * @param[in] path Path to the known_hosts file. + * @return 0 on success, 1 on error. */ -void nc_client_ssh_get_auth_hostkey_check_clb(int (**auth_hostkey_check)(const char *hostname, ssh_session session, void *priv), - void **priv); +int nc_client_ssh_set_knownhosts_path(const char *path); /** * @brief Set SSH password authentication callback. @@ -397,10 +391,6 @@ struct nc_session *nc_connect_ssh_channel(struct nc_session *session, struct ly_ /** @} Client SSH */ -#endif /* NC_ENABLED_SSH */ - -#ifdef NC_ENABLED_TLS - /** * @defgroup client_tls Client TLS * @ingroup client @@ -496,7 +486,7 @@ struct nc_session *nc_connect_libssl(SSL *tls, struct ly_ctx *ctx); /** @} Client TLS */ -#endif /* NC_ENABLED_TLS */ +#endif /* NC_ENABLED_SSH_TLS */ /** * @addtogroup client_session diff --git a/src/session_client_ch.h b/src/session_client_ch.h index 33ea39c3..916188c8 100644 --- a/src/session_client_ch.h +++ b/src/session_client_ch.h @@ -22,15 +22,15 @@ extern "C" { #include -#ifdef NC_ENABLED_SSH +#ifdef NC_ENABLED_SSH_TLS # include -#endif +#endif /* NC_ENABLED_SSH_TLS */ #include "messages_client.h" #include "netconf.h" #include "session.h" -#if defined (NC_ENABLED_SSH) || defined (NC_ENABLED_TLS) +#ifdef NC_ENABLED_SSH_TLS /** * @defgroup client_ch Client-side Call Home @@ -55,10 +55,6 @@ int nc_accept_callhome(int timeout, struct ly_ctx *ctx, struct nc_session **sess /** @} Client-side Call Home */ -#endif /* NC_ENABLED_SSH || NC_ENABLED_TLS */ - -#ifdef NC_ENABLED_SSH - /** * @defgroup client_ch_ssh Client-side Call Home on SSH * @ingroup client_ch @@ -67,30 +63,6 @@ int nc_accept_callhome(int timeout, struct ly_ctx *ctx, struct nc_session **sess * @{ */ -/** - * @brief Set SSH Call Home authentication hostkey check (knownhosts) callback. - * - * Repetitive calling causes replacing of the previous callback and its private data. Caller is responsible for - * freeing the private data when necessary (the private data can be obtained by - * nc_client_ssh_ch_get_auth_hostkey_check_clb()). - * - * @param[in] auth_hostkey_check Function to call, returns 0 on success, non-zero in error. - * If NULL, the default callback is set. - * @param[in] priv Optional private data to be passed to the callback function. - */ -void nc_client_ssh_ch_set_auth_hostkey_check_clb(int (*auth_hostkey_check)(const char *hostname, ssh_session session, void *priv), - void *priv); - -/** - * @brief Get currently set SSH Call Home authentication hostkey check (knownhosts) callback and its private data - * previously set by nc_client_ssh_ch_set_auth_hostkey_check_clb(). - * - * @param[out] auth_hostkey_check Currently set callback, NULL in case of the default callback. - * @param[out] priv Currently set (optional) private data to be passed to the callback function. - */ -void nc_client_ssh_ch_get_auth_hostkey_check_clb(int (**auth_hostkey_check)(const char *hostname, ssh_session session, void *priv), - void **priv); - /** * @brief Set SSH Call Home password authentication callback. * @@ -257,10 +229,6 @@ const char *nc_client_ssh_ch_get_username(void); /** @} Client-side Call Home on SSH */ -#endif /* NC_ENABLED_SSH */ - -#ifdef NC_ENABLED_TLS - /** * @defgroup client_ch_tls Client-side Call Home on TLS * @ingroup client_ch @@ -353,7 +321,7 @@ void nc_client_tls_ch_get_crl_paths(const char **crl_file, const char **crl_dir) /** @} Client-side Call Home on TLS */ -#endif /* NC_ENABLED_TLS */ +#endif /* NC_ENABLED_SSH_TLS */ #ifdef __cplusplus } diff --git a/src/session_client_ssh.c b/src/session_client_ssh.c index 647a526d..ff3c6255 100644 --- a/src/session_client_ssh.c +++ b/src/session_client_ssh.c @@ -45,9 +45,11 @@ #include #include "compat.h" -#include "libnetconf.h" +#include "config.h" +#include "log_p.h" #include "session_client.h" #include "session_client_ch.h" +#include "session_p.h" struct nc_client_context *nc_client_context_location(void); @@ -167,8 +169,11 @@ _nc_client_ssh_destroy_opts(struct nc_client_ssh_opts *opts) } free(opts->keys); free(opts->username); + free(opts->knownhosts_path); + opts->key_count = 0; opts->keys = NULL; opts->username = NULL; + opts->knownhosts_path = NULL; } void @@ -274,120 +279,163 @@ sshauth_hostkey_hash_dnssec_check(const char *hostname, const unsigned char *sha #endif /* ENABLE_DNSSEC */ -int -sshauth_hostkey_check(const char *hostname, ssh_session session, void *UNUSED(priv)) +static int +nc_client_ssh_update_known_hosts(ssh_session session, const char *hostname) { - char *hexa = NULL; + int ret; -#if (LIBSSH_VERSION_INT >= SSH_VERSION_INT(0, 9, 0)) - int c, ret; - enum ssh_known_hosts_e state; -#else - int c, state, ret; -#endif + ret = ssh_session_update_known_hosts(session); + if (ret != SSH_OK) { + WRN(NULL, "Adding the known host \"%s\" failed (%s).", hostname, ssh_get_error(session)); + } + + return ret; +} + +static int +nc_client_ssh_get_srv_pubkey_data(ssh_session session, enum ssh_keytypes_e *srv_pubkey_type, char **hexa, unsigned char **hash_sha1) +{ + int ret; ssh_key srv_pubkey; - unsigned char *hash_sha1 = NULL; size_t hlen; - enum ssh_keytypes_e srv_pubkey_type; - char answer[5]; - FILE *out = NULL, *in = NULL; -#if (LIBSSH_VERSION_INT >= SSH_VERSION_INT(0, 9, 0)) - state = ssh_session_is_known_server(session); -#else - state = ssh_is_server_known(session); -#endif + *hexa = NULL; + *hash_sha1 = NULL; -#if (LIBSSH_VERSION_INT >= SSH_VERSION_INT(0, 8, 0)) ret = ssh_get_server_publickey(session, &srv_pubkey); -#else - ret = ssh_get_publickey(session, &srv_pubkey); -#endif if (ret < 0) { - ERR(NULL, "Unable to get server public key."); + ERR(NULL, "Unable to get server's public key."); return -1; } - srv_pubkey_type = ssh_key_type(srv_pubkey); - ret = ssh_get_publickey_hash(srv_pubkey, SSH_PUBLICKEY_HASH_SHA1, &hash_sha1, &hlen); + *srv_pubkey_type = ssh_key_type(srv_pubkey); + ret = ssh_get_publickey_hash(srv_pubkey, SSH_PUBLICKEY_HASH_SHA1, hash_sha1, &hlen); ssh_key_free(srv_pubkey); if (ret < 0) { ERR(NULL, "Failed to calculate SHA1 hash of the server public key."); return -1; } - hexa = ssh_get_hexa(hash_sha1, hlen); + *hexa = ssh_get_hexa(*hash_sha1, hlen); + if (!*hexa) { + ERR(NULL, "Getting the hostkey's hex string failed."); + return -1; + } + + return 0; +} + +#ifdef ENABLE_DNSSEC +static int +nc_client_ssh_do_dnssec_sshfp_check(ssh_session session, enum ssh_keytypes_e srv_pubkey_type, const char *hostname, unsigned char *hash_sha1) +{ + int ret = 0; + + if ((srv_pubkey_type != SSH_KEYTYPE_UNKNOWN) && (srv_pubkey_type != SSH_KEYTYPE_RSA1)) { + if (srv_pubkey_type == SSH_KEYTYPE_DSS) { + ret = sshauth_hostkey_hash_dnssec_check(hostname, hash_sha1, 2, 1); + } else if (srv_pubkey_type == SSH_KEYTYPE_RSA) { + ret = sshauth_hostkey_hash_dnssec_check(hostname, hash_sha1, 1, 1); + } else if (srv_pubkey_type == SSH_KEYTYPE_ECDSA) { + ret = sshauth_hostkey_hash_dnssec_check(hostname, hash_sha1, 3, 1); + } else { + /* other key types not supported */ + ret = 1; + } + + /* DNSSEC SSHFP check successful, that's enough */ + if (!ret) { + VRB(NULL, "DNSSEC SSHFP check successful."); + ssh_session_update_known_hosts(session); + } + + return ret; + } + + return 1; +} - switch (state) { -#if (LIBSSH_VERSION_INT >= SSH_VERSION_INT(0, 9, 0)) - case SSH_KNOWN_HOSTS_OK: -#else - case SSH_SERVER_KNOWN_OK: #endif - break; /* ok */ -#if (LIBSSH_VERSION_INT >= SSH_VERSION_INT(0, 9, 0)) - case SSH_KNOWN_HOSTS_CHANGED: -#else - case SSH_SERVER_KNOWN_CHANGED: +static int +nc_client_ssh_auth_hostkey_check(const char *hostname, uint16_t port, ssh_session session) +{ + char *hexa = NULL; + unsigned char *hash_sha1 = NULL; + NC_SSH_KNOWNHOSTS_MODE knownhosts_mode = ssh_opts.knownhosts_mode; + enum ssh_keytypes_e srv_pubkey_type; + char answer[5]; + FILE *out = NULL, *in = NULL; + int c, state; + +#ifdef ENABLE_DNSSEC + int dnssec_ret; #endif - ERR(NULL, "Remote host key changed, the connection will be terminated!"); + + if (knownhosts_mode == NC_SSH_KNOWNHOSTS_SKIP) { + /* skip all hostkey checks */ + return 0; + } + + if (nc_client_ssh_get_srv_pubkey_data(session, &srv_pubkey_type, &hexa, &hash_sha1)) { goto error; + } -#if (LIBSSH_VERSION_INT >= SSH_VERSION_INT(0, 9, 0)) + state = ssh_session_is_known_server(session); + switch (state) { + case SSH_KNOWN_HOSTS_OK: + break; /* ok */ + case SSH_KNOWN_HOSTS_CHANGED: + if (knownhosts_mode == NC_SSH_KNOWNHOSTS_ACCEPT) { + /* is the mode is set to accept, then accept any connection even if the remote key changed */ + WRN(NULL, "Remote host key changed!"); + break; + } else { + ERR(NULL, "Remote host key changed, the connection will be terminated!"); + goto error; + } case SSH_KNOWN_HOSTS_OTHER: -#else - case SSH_SERVER_FOUND_OTHER: -#endif WRN(NULL, "Remote host key is not known, but a key of another type for this host is known. Continue with caution."); goto hostkey_not_known; - -#if (LIBSSH_VERSION_INT >= SSH_VERSION_INT(0, 9, 0)) case SSH_KNOWN_HOSTS_NOT_FOUND: -#else - case SSH_SERVER_FILE_NOT_FOUND: -#endif WRN(NULL, "Could not find the known hosts file."); goto hostkey_not_known; - -#if (LIBSSH_VERSION_INT >= SSH_VERSION_INT(0, 9, 0)) case SSH_KNOWN_HOSTS_UNKNOWN: -#else - case SSH_SERVER_NOT_KNOWN: -#endif hostkey_not_known: #ifdef ENABLE_DNSSEC - if ((srv_pubkey_type != SSH_KEYTYPE_UNKNOWN) && (srv_pubkey_type != SSH_KEYTYPE_RSA1)) { - if (srv_pubkey_type == SSH_KEYTYPE_DSS) { - ret = sshauth_hostkey_hash_dnssec_check(hostname, hash_sha1, 2, 1); - } else if (srv_pubkey_type == SSH_KEYTYPE_RSA) { - ret = sshauth_hostkey_hash_dnssec_check(hostname, hash_sha1, 1, 1); - } else if (srv_pubkey_type == SSH_KEYTYPE_ECDSA) { - ret = sshauth_hostkey_hash_dnssec_check(hostname, hash_sha1, 3, 1); - } - - /* DNSSEC SSHFP check successful, that's enough */ - if (!ret) { - VRB(NULL, "DNSSEC SSHFP check successful."); -#if (LIBSSH_VERSION_INT >= SSH_VERSION_INT(0, 9, 0)) - ssh_session_update_known_hosts(session); -#else - ssh_write_knownhost(session); -#endif - ssh_clean_pubkey_hash(&hash_sha1); - ssh_string_free_char(hexa); - return 0; - } + /* do dnssec check, if it's ok then we're done otherwise continue */ + dnssec_ret = nc_client_ssh_do_dnssec_sshfp_check(session, srv_pubkey_type, hostname, hash_sha1); + if (!dnssec_ret) { + ssh_clean_pubkey_hash(&hash_sha1); + ssh_string_free_char(hexa); + return 0; } #endif + /* open the files for reading/writing */ if (!(in = nc_open_in(1, NULL))) { goto error; } + if (!(out = nc_open_out())) { goto error; } + if (knownhosts_mode == NC_SSH_KNOWNHOSTS_STRICT) { + /* do not connect if the hostkey is not present in known_hosts file in this mode */ + ERR(NULL, "No %s host key is known for [%s]:%hu.\n", ssh_key_type_to_char(srv_pubkey_type), hostname, port); + goto error; + } else if ((knownhosts_mode == NC_SSH_KNOWNHOSTS_ACCEPT_NEW) || (knownhosts_mode == NC_SSH_KNOWNHOSTS_ACCEPT)) { + /* add a new entry to the known_hosts file without prompting */ + if (nc_client_ssh_update_known_hosts(session, hostname)) { + goto error; + } + + VRB(NULL, "Permanently added '[%s]:%hu' (%s) to the list of known hosts.", hostname, port, ssh_key_type_to_char(srv_pubkey_type)); + + break; + } + /* try to get result from user */ if (fprintf(out, "The authenticity of the host \'%s\' cannot be established.\n", hostname) < 1) { ERR(NULL, "Writing into output failed (%s).", feof(out) ? "EOF" : strerror(errno)); @@ -399,12 +447,12 @@ sshauth_hostkey_check(const char *hostname, ssh_session session, void *UNUSED(pr } #ifdef ENABLE_DNSSEC - if (ret == 2) { + if (dnssec_ret == 2) { if (fprintf(out, "No matching host key fingerprint found using DNS.\n") < 1) { ERR(NULL, "Writing into output failed (%s).", feof(out) ? "EOF" : strerror(errno)); goto error; } - } else if (ret == 1) { + } else if (dnssec_ret == 1) { if (fprintf(out, "Matching host key fingerprint found using DNS.\n") < 1) { ERR(NULL, "Writing into output failed (%s).", feof(out) ? "EOF" : strerror(errno)); goto error; @@ -427,15 +475,8 @@ sshauth_hostkey_check(const char *hostname, ssh_session session, void *UNUSED(pr fflush(in); if (!strcmp("yes", answer)) { - /* store the key into the host file */ -#if (LIBSSH_VERSION_INT >= SSH_VERSION_INT(0, 9, 0)) - ret = ssh_session_update_known_hosts(session); -#else - ret = ssh_write_knownhost(session); -#endif - if (ret != SSH_OK) { - WRN(NULL, "Adding the known host \"%s\" failed (%s).", hostname, ssh_get_error(session)); - } + /* store the key into the known_hosts file */ + nc_client_ssh_update_known_hosts(session, hostname); } else if (!strcmp("no", answer)) { goto error; } else { @@ -448,12 +489,7 @@ sshauth_hostkey_check(const char *hostname, ssh_session session, void *UNUSED(pr } while (strcmp(answer, "yes") && strcmp(answer, "no")); break; - -#if (LIBSSH_VERSION_INT >= SSH_VERSION_INT(0, 9, 0)) case SSH_KNOWN_HOSTS_ERROR: -#else - case SSH_SERVER_ERROR: -#endif ERR(NULL, "SSH error: %s", ssh_get_error(session)); goto error; } @@ -481,10 +517,7 @@ sshauth_password(const char *username, const char *hostname, void *UNUSED(priv)) FILE *in = NULL, *out = NULL; buf = malloc(buflen * sizeof *buf); - if (!buf) { - ERRMEM; - return NULL; - } + NC_CHECK_ERRMEM_RET(!buf, NULL); if (!(in = nc_open_in(0, &oldterm))) { goto error; @@ -504,10 +537,7 @@ sshauth_password(const char *username, const char *hostname, void *UNUSED(priv)) if (len >= buflen - 1) { buflen *= 2; buf = nc_realloc(buf, buflen * sizeof *buf); - if (!buf) { - ERRMEM; - goto error; - } + NC_CHECK_ERRMEM_GOTO(!buf, , error); } buf[len++] = (char)c; } @@ -536,10 +566,7 @@ sshauth_interactive(const char *auth_name, const char *instruction, const char * FILE *in = NULL, *out = NULL; buf = malloc(buflen * sizeof *buf); - if (!buf) { - ERRMEM; - return NULL; - } + NC_CHECK_ERRMEM_RET(!buf, NULL); if (!(in = nc_open_in(echo, &oldterm))) { goto error; @@ -567,10 +594,7 @@ sshauth_interactive(const char *auth_name, const char *instruction, const char * if (cur_len >= buflen - 1) { buflen *= 2; buf = nc_realloc(buf, buflen * sizeof *buf); - if (!buf) { - ERRMEM; - goto error; - } + NC_CHECK_ERRMEM_GOTO(!buf, , error); } buf[cur_len++] = (char)c; } @@ -599,10 +623,7 @@ sshauth_privkey_passphrase(const char *privkey_path, void *UNUSED(priv)) FILE *in = NULL, *out = NULL; buf = malloc(buflen * sizeof *buf); - if (!buf) { - ERRMEM; - return NULL; - } + NC_CHECK_ERRMEM_RET(!buf, NULL); if (!(in = nc_open_in(0, &oldterm))) { goto error; @@ -622,10 +643,7 @@ sshauth_privkey_passphrase(const char *privkey_path, void *UNUSED(priv)) if (len >= buflen - 1) { buflen *= 2; buf = nc_realloc(buf, buflen * sizeof *buf); - if (!buf) { - ERRMEM; - goto error; - } + NC_CHECK_ERRMEM_GOTO(!buf, , error); } buf[len++] = (char)c; } @@ -644,57 +662,26 @@ sshauth_privkey_passphrase(const char *privkey_path, void *UNUSED(priv)) return NULL; } -static void -_nc_client_ssh_set_auth_hostkey_check_clb(int (*auth_hostkey_check)(const char *hostname, ssh_session session, void *priv), - void *priv, struct nc_client_ssh_opts *opts) +API int +nc_client_ssh_set_knownhosts_path(const char *path) { - if (auth_hostkey_check) { - opts->auth_hostkey_check = auth_hostkey_check; - opts->auth_hostkey_check_priv = priv; - } else { - opts->auth_hostkey_check = sshauth_hostkey_check; - opts->auth_hostkey_check_priv = NULL; - } -} + free(ssh_opts.knownhosts_path); -static void -_nc_client_ssh_get_auth_hostkey_check_clb(int (**auth_hostkey_check)(const char *hostname, ssh_session session, void *priv), - void **priv, struct nc_client_ssh_opts *opts) -{ - if (auth_hostkey_check) { - (*auth_hostkey_check) = opts->auth_hostkey_check == sshauth_hostkey_check ? NULL : opts->auth_hostkey_check; - } - if (priv) { - (*priv) = opts->auth_hostkey_check_priv; + if (!path) { + ssh_opts.knownhosts_path = NULL; + return 0; } -} -API void -nc_client_ssh_set_auth_hostkey_check_clb(int (*auth_hostkey_check)(const char *hostname, ssh_session session, void *priv), - void *priv) -{ - _nc_client_ssh_set_auth_hostkey_check_clb(auth_hostkey_check, priv, &ssh_opts); -} + ssh_opts.knownhosts_path = strdup(path); + NC_CHECK_ERRMEM_RET(!ssh_opts.knownhosts_path, 1); -API void -nc_client_ssh_ch_set_auth_hostkey_check_clb(int (*auth_hostkey_check)(const char *hostname, ssh_session session, void *priv), - void *priv) -{ - _nc_client_ssh_set_auth_hostkey_check_clb(auth_hostkey_check, priv, &ssh_ch_opts); -} - -API void -nc_client_ssh_get_auth_hostkey_check_clb(int (**auth_hostkey_check)(const char *hostname, ssh_session session, void *priv), - void **priv) -{ - _nc_client_ssh_get_auth_hostkey_check_clb(auth_hostkey_check, priv, &ssh_opts); + return 0; } API void -nc_client_ssh_ch_get_auth_hostkey_check_clb(int (**auth_hostkey_check)(const char *hostname, ssh_session session, void *priv), - void **priv) +nc_client_ssh_set_knownhosts_mode(NC_SSH_KNOWNHOSTS_MODE mode) { - _nc_client_ssh_get_auth_hostkey_check_clb(auth_hostkey_check, priv, &ssh_ch_opts); + ssh_opts.knownhosts_mode = mode; } static void @@ -869,13 +856,7 @@ _nc_client_ssh_add_keypair(const char *pub_key, const char *priv_key, struct nc_ FILE *key; char line[128]; - if (!pub_key) { - ERRARG("pub_key"); - return -1; - } else if (!priv_key) { - ERRARG("priv_key"); - return -1; - } + NC_CHECK_ARG_RET(NULL, pub_key, priv_key, -1); for (i = 0; i < opts->key_count; ++i) { if (!strcmp(opts->keys[i].pubkey_path, pub_key) || !strcmp(opts->keys[i].privkey_path, priv_key)) { @@ -897,18 +878,12 @@ _nc_client_ssh_add_keypair(const char *pub_key, const char *priv_key, struct nc_ /* add the keys */ ++opts->key_count; opts->keys = nc_realloc(opts->keys, opts->key_count * sizeof *opts->keys); - if (!opts->keys) { - ERRMEM; - return -1; - } + NC_CHECK_ERRMEM_RET(!opts->keys, -1); opts->keys[opts->key_count - 1].pubkey_path = strdup(pub_key); opts->keys[opts->key_count - 1].privkey_path = strdup(priv_key); opts->keys[opts->key_count - 1].privkey_crypt = 0; - if (!opts->keys[opts->key_count - 1].pubkey_path || !opts->keys[opts->key_count - 1].privkey_path) { - ERRMEM; - return -1; - } + NC_CHECK_ERRMEM_RET(!opts->keys[opts->key_count - 1].pubkey_path || !opts->keys[opts->key_count - 1].privkey_path, -1); /* check encryption */ if ((key = fopen(priv_key, "r"))) { @@ -949,7 +924,7 @@ static int _nc_client_ssh_del_keypair(int idx, struct nc_client_ssh_opts *opts) { if (idx >= opts->key_count) { - ERRARG("idx"); + ERRARG(NULL, "idx"); return -1; } @@ -962,10 +937,7 @@ _nc_client_ssh_del_keypair(int idx, struct nc_client_ssh_opts *opts) } if (opts->key_count) { opts->keys = nc_realloc(opts->keys, opts->key_count * sizeof *opts->keys); - if (!opts->keys) { - ERRMEM; - return -1; - } + NC_CHECK_ERRMEM_RET(!opts->keys, -1); } else { free(opts->keys); opts->keys = NULL; @@ -1008,10 +980,10 @@ static int _nc_client_ssh_get_keypair(int idx, const char **pub_key, const char **priv_key, struct nc_client_ssh_opts *opts) { if (idx >= opts->key_count) { - ERRARG("idx"); + ERRARG(NULL, "idx"); return -1; } else if (!pub_key && !priv_key) { - ERRARG("pub_key and priv_key"); + ERRARG(NULL, "pub_key and priv_key"); return -1; } @@ -1101,10 +1073,7 @@ _nc_client_ssh_set_username(const char *username, struct nc_client_ssh_opts *opt } if (username) { opts->username = strdup(username); - if (!opts->username) { - ERRMEM; - return -1; - } + NC_CHECK_ERRMEM_RET(!opts->username, -1); } else { opts->username = NULL; } @@ -1192,7 +1161,7 @@ connect_ssh_session(struct nc_session *session, struct nc_client_ssh_opts *opts, return -1; } - if (opts->auth_hostkey_check(session->host, ssh_sess, opts->auth_hostkey_check_priv)) { + if (nc_client_ssh_auth_hostkey_check(session->host, session->port, ssh_sess)) { ERR(session, "Checking the host key failed."); return -1; } @@ -1213,7 +1182,6 @@ connect_ssh_session(struct nc_session *session, struct nc_client_ssh_opts *opts, ERR(session, "Authentication failed (%s).", ssh_get_error(ssh_sess)); return -1; } else if (ret_auth == SSH_AUTH_SUCCESS) { - WRN(session, "Server accepts \"none\" authentication method.") return 1; } @@ -1516,17 +1484,11 @@ _nc_connect_libssh(ssh_session ssh_session, struct ly_ctx *ctx, struct nc_keepal char *buf = NULL; size_t buf_len = 0; - if (!ssh_session) { - ERRARG("ssh_session"); - return NULL; - } + NC_CHECK_ARG_RET(NULL, ssh_session, NULL); /* prepare session structure */ session = nc_new_session(NC_CLIENT, 0); - if (!session) { - ERRMEM; - return NULL; - } + NC_CHECK_ERRMEM_RET(!session, NULL); session->status = NC_STATUS_STARTING; session->ti_type = NC_TI_LIBSSH; session->ti.libssh.session = ssh_session; @@ -1542,10 +1504,7 @@ _nc_connect_libssh(ssh_session ssh_session, struct ly_ctx *ctx, struct nc_keepal /* remember host */ host = strdup("localhost"); - if (!host) { - ERRMEM; - goto fail; - } + NC_CHECK_ERRMEM_GOTO(!host, , fail); ssh_options_set(session->ti.libssh.session, SSH_OPTIONS_HOST, host); /* create and connect socket */ @@ -1572,7 +1531,7 @@ _nc_connect_libssh(ssh_session ssh_session, struct ly_ctx *ctx, struct nc_keepal /* remember username */ if (!username) { if (!opts->username) { - pw = nc_getpwuid(getuid(), &pw_buf, &buf, &buf_len); + pw = nc_getpw(getuid(), NULL, &pw_buf, &buf, &buf_len); if (!pw) { ERR(NULL, "Unknown username for the SSH connection (%s).", strerror(errno)); goto fail; @@ -1582,10 +1541,7 @@ _nc_connect_libssh(ssh_session ssh_session, struct ly_ctx *ctx, struct nc_keepal } else { username = strdup(opts->username); } - if (!username) { - ERRMEM; - goto fail; - } + NC_CHECK_ERRMEM_GOTO(!username, , fail); ssh_options_set(session->ti.libssh.session, SSH_OPTIONS_USER, username); } @@ -1646,9 +1602,10 @@ nc_connect_ssh(const char *host, uint16_t port, struct ly_ctx *ctx) struct nc_session *session = NULL; char *buf = NULL; size_t buf_len = 0; + char *known_hosts_path = NULL; /* process parameters */ - if (!host || strisempty(host)) { + if (!host || (host[0] == '\0')) { host = "localhost"; } @@ -1658,7 +1615,7 @@ nc_connect_ssh(const char *host, uint16_t port, struct ly_ctx *ctx) port_uint = port; if (!ssh_opts.username) { - pw = nc_getpwuid(getuid(), &pw_buf, &buf, &buf_len); + pw = nc_getpw(getuid(), NULL, &pw_buf, &buf, &buf_len); if (!pw) { ERR(session, "Unknown username for the SSH connection (%s).", strerror(errno)); goto fail; @@ -1667,14 +1624,22 @@ nc_connect_ssh(const char *host, uint16_t port, struct ly_ctx *ctx) } } else { username = ssh_opts.username; + + pw = nc_getpw(0, username, &pw_buf, &buf, &buf_len); + } + + if (ssh_opts.knownhosts_path) { + /* known_hosts file path was set so use it */ + known_hosts_path = strdup(ssh_opts.knownhosts_path); + NC_CHECK_ERRMEM_GOTO(!known_hosts_path, , fail); + } else if (pw) { + /* path not set explicitly, but current user's username found in /etc/passwd, so create the path */ + NC_CHECK_ERRMEM_GOTO(asprintf(&known_hosts_path, "%s/.ssh/known_hosts", pw->pw_dir) == -1, , fail); } /* prepare session structure */ session = nc_new_session(NC_CLIENT, 0); - if (!session) { - ERRMEM; - goto fail; - } + NC_CHECK_ERRMEM_GOTO(!session, , fail); session->status = NC_STATUS_STARTING; /* transport-specific data */ @@ -1690,6 +1655,9 @@ nc_connect_ssh(const char *host, uint16_t port, struct ly_ctx *ctx) ssh_options_set(session->ti.libssh.session, SSH_OPTIONS_PORT, &port_uint); ssh_options_set(session->ti.libssh.session, SSH_OPTIONS_USER, username); ssh_options_set(session->ti.libssh.session, SSH_OPTIONS_TIMEOUT, &timeout); + if (known_hosts_path) { + ssh_options_set(session->ti.libssh.session, SSH_OPTIONS_KNOWNHOSTS, known_hosts_path); + } /* create and assign communication socket */ sock = nc_sock_connect(host, port, -1, &client_opts.ka, NULL, &ip_host); @@ -1703,6 +1671,7 @@ nc_connect_ssh(const char *host, uint16_t port, struct ly_ctx *ctx) /* store information for session connection */ session->host = strdup(host); session->username = strdup(username); + session->port = port; if ((connect_ssh_session(session, &ssh_opts, NC_TRANSPORT_TIMEOUT) != 1) || (open_netconf_channel(session, NC_TRANSPORT_TIMEOUT) != 1)) { goto fail; @@ -1729,10 +1698,12 @@ nc_connect_ssh(const char *host, uint16_t port, struct ly_ctx *ctx) session->port = port; free(buf); + free(known_hosts_path); return session; fail: free(buf); + free(known_hosts_path); free(ip_host); nc_session_free(session, NULL); return NULL; @@ -1749,17 +1720,11 @@ nc_connect_ssh_channel(struct nc_session *session, struct ly_ctx *ctx) { struct nc_session *new_session, *ptr; - if (!session) { - ERRARG("session"); - return NULL; - } + NC_CHECK_ARG_RET(session, session, NULL); /* prepare session structure */ new_session = nc_new_session(NC_CLIENT, 1); - if (!new_session) { - ERRMEM; - return NULL; - } + NC_CHECK_ERRMEM_RET(!new_session, NULL); new_session->status = NC_STATUS_STARTING; /* share some parameters including the IO lock (we are using one socket for both sessions) */ @@ -1838,7 +1803,7 @@ nc_accept_callhome_ssh_sock(int sock, const char *host, uint16_t port, struct ly ssh_options_set(sess, SSH_OPTIONS_PORT, &uint_port); ssh_options_set(sess, SSH_OPTIONS_TIMEOUT, &ssh_timeout); if (!ssh_ch_opts.username) { - pw = nc_getpwuid(getuid(), &pw_buf, &buf, &buf_len); + pw = nc_getpw(getuid(), NULL, &pw_buf, &buf, &buf_len); if (!pw) { ERR(NULL, "Unknown username for the SSH connection (%s).", strerror(errno)); ssh_free(sess); diff --git a/src/session_client_tls.c b/src/session_client_tls.c index f95fd460..bf5dcdb7 100644 --- a/src/session_client_tls.c +++ b/src/session_client_tls.c @@ -28,13 +28,11 @@ #include #include -#include "libnetconf.h" +#include "config.h" +#include "log_p.h" #include "session_client.h" #include "session_client_ch.h" - -#if OPENSSL_VERSION_NUMBER < 0x10100000L -#define X509_STORE_CTX_get_by_subject X509_STORE_get_by_subject -#endif +#include "session_p.h" struct nc_client_context *nc_client_context_location(void); @@ -44,8 +42,6 @@ struct nc_client_context *nc_client_context_location(void); static int tlsauth_ch; -#if OPENSSL_VERSION_NUMBER >= 0x10100000L // >= 1.1.0 - static int tlsauth_verify_callback(int preverify_ok, X509_STORE_CTX *x509_ctx) { @@ -140,102 +136,6 @@ tlsauth_verify_callback(int preverify_ok, X509_STORE_CTX *x509_ctx) return 1; /* success */ } -#else - -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; - 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 */ - memset((char *)&obj, 0, sizeof obj); - 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_cleanup(&store_ctx); - crl = obj.data.crl; - if ((rc > 0) && crl) { - next_update = X509_CRL_get_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_contents(&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_contents(&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_contents(&obj); - return 0; /* fail */ - } - X509_OBJECT_free_contents(&obj); - } - - /* try to retrieve a CRL corresponding to the _issuer_ of - * the current certificate in order to check for revocation */ - memset((char *)&obj, 0, sizeof obj); - 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_cleanup(&store_ctx); - crl = obj.data.crl; - 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(revoked->serialNumber, X509_get_serialNumber(cert)) == 0) { - ERR(NULL, "Certificate revoked!"); - X509_STORE_CTX_set_error(x509_ctx, X509_V_ERR_CERT_REVOKED); - X509_OBJECT_free_contents(&obj); - return 0; /* fail */ - } - } - X509_OBJECT_free_contents(&obj); - } - - return 1; /* success */ -} - -#endif - void _nc_client_tls_destroy_opts(struct nc_client_tls_opts *opts) { @@ -262,26 +162,17 @@ nc_client_tls_destroy_opts(void) static int _nc_client_tls_set_cert_key_paths(const char *client_cert, const char *client_key, struct nc_client_tls_opts *opts) { - if (!client_cert) { - ERRARG("client_cert"); - return -1; - } + NC_CHECK_ARG_RET(NULL, client_cert, -1); free(opts->cert_path); free(opts->key_path); opts->cert_path = strdup(client_cert); - if (!opts->cert_path) { - ERRMEM; - return -1; - } + NC_CHECK_ERRMEM_RET(!opts->cert_path, -1); if (client_key) { opts->key_path = strdup(client_key); - if (!opts->key_path) { - ERRMEM; - return -1; - } + NC_CHECK_ERRMEM_RET(!opts->key_path, -1); } else { opts->key_path = NULL; } @@ -307,7 +198,7 @@ static void _nc_client_tls_get_cert_key_paths(const char **client_cert, const char **client_key, struct nc_client_tls_opts *opts) { if (!client_cert && !client_key) { - ERRARG("client_cert and client_key"); + ERRARG(NULL, "client_cert and client_key"); return; } @@ -335,7 +226,7 @@ static int _nc_client_tls_set_trusted_ca_paths(const char *ca_file, const char *ca_dir, struct nc_client_tls_opts *opts) { if (!ca_file && !ca_dir) { - ERRARG("ca_file and ca_dir"); + ERRARG(NULL, "ca_file and ca_dir"); return -1; } @@ -344,20 +235,14 @@ _nc_client_tls_set_trusted_ca_paths(const char *ca_file, const char *ca_dir, str if (ca_file) { opts->ca_file = strdup(ca_file); - if (!opts->ca_file) { - ERRMEM; - return -1; - } + NC_CHECK_ERRMEM_RET(!opts->ca_file, -1); } else { opts->ca_file = NULL; } if (ca_dir) { opts->ca_dir = strdup(ca_dir); - if (!opts->ca_dir) { - ERRMEM; - return -1; - } + NC_CHECK_ERRMEM_RET(!opts->ca_dir, -1); } else { opts->ca_dir = NULL; } @@ -383,7 +268,7 @@ static void _nc_client_tls_get_trusted_ca_paths(const char **ca_file, const char **ca_dir, struct nc_client_tls_opts *opts) { if (!ca_file && !ca_dir) { - ERRARG("ca_file and ca_dir"); + ERRARG(NULL, "ca_file and ca_dir"); return; } @@ -411,7 +296,7 @@ static int _nc_client_tls_set_crl_paths(const char *crl_file, const char *crl_dir, struct nc_client_tls_opts *opts) { if (!crl_file && !crl_dir) { - ERRARG("crl_file and crl_dir"); + ERRARG(NULL, "crl_file and crl_dir"); return -1; } @@ -420,20 +305,14 @@ _nc_client_tls_set_crl_paths(const char *crl_file, const char *crl_dir, struct n if (crl_file) { opts->crl_file = strdup(crl_file); - if (!opts->crl_file) { - ERRMEM; - return -1; - } + NC_CHECK_ERRMEM_RET(!opts->crl_file, -1); } else { opts->crl_file = NULL; } if (crl_dir) { opts->crl_dir = strdup(crl_dir); - if (!opts->crl_dir) { - ERRMEM; - return -1; - } + NC_CHECK_ERRMEM_RET(!opts->crl_dir, -1); } else { opts->crl_dir = NULL; } @@ -459,7 +338,7 @@ static void _nc_client_tls_get_crl_paths(const char **crl_file, const char **crl_dir, struct nc_client_tls_opts *opts) { if (!crl_file && !crl_dir) { - ERRARG("crl_file and crl_dir"); + ERRARG(NULL, "crl_file and crl_dir"); return; } @@ -511,15 +390,8 @@ nc_client_tls_update_opts(struct nc_client_tls_opts *opts, const char *peername) if (!opts->tls_ctx || opts->tls_ctx_change) { SSL_CTX_free(opts->tls_ctx); - -#if OPENSSL_VERSION_NUMBER >= 0x10100000L // >= 1.1.0 /* prepare global SSL context, highest available method is negotiated autmatically */ - if (!(opts->tls_ctx = SSL_CTX_new(TLS_client_method()))) -#else - /* prepare global SSL context, allow only mandatory TLS 1.2 */ - if (!(opts->tls_ctx = SSL_CTX_new(TLSv1_2_client_method()))) -#endif - { + 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; @@ -554,7 +426,6 @@ nc_client_tls_update_opts(struct nc_client_tls_opts *opts, const char *peername) goto cleanup; } -#if OPENSSL_VERSION_NUMBER >= 0x10100000L // >= 1.1.0 if (peername) { /* server identity (hostname) verification */ vpm = X509_VERIFY_PARAM_new(); @@ -569,7 +440,6 @@ nc_client_tls_update_opts(struct nc_client_tls_opts *opts, const char *peername) goto cleanup; } } -#endif } if (opts->crl_store_change || (!opts->crl_store && (opts->crl_file || opts->crl_dir))) { @@ -583,11 +453,6 @@ nc_client_tls_update_opts(struct nc_client_tls_opts *opts, const char *peername) goto cleanup; } -#if OPENSSL_VERSION_NUMBER < 0x10100000L // < 1.1.0 - /* whaveter this does... */ - opts->crl_store->cache = 0; -#endif - 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."); @@ -669,7 +534,7 @@ nc_connect_tls(const char *host, unsigned short port, struct ly_ctx *ctx) } /* process parameters */ - if (!host || strisempty(host)) { + if (!host || (host[0] == '\0')) { host = "localhost"; } @@ -684,10 +549,7 @@ nc_connect_tls(const char *host, unsigned short port, struct ly_ctx *ctx) /* prepare session structure */ session = nc_new_session(NC_CLIENT, 0); - if (!session) { - ERRMEM; - return NULL; - } + NC_CHECK_ERRMEM_RET(!session, NULL); session->status = NC_STATUS_STARTING; /* fill the session */ @@ -757,20 +619,16 @@ nc_connect_libssl(SSL *tls, struct ly_ctx *ctx) { struct nc_session *session; - if (!tls) { - ERRARG("tls"); - return NULL; - } else if (!SSL_is_init_finished(tls)) { + NC_CHECK_ARG_RET(NULL, tls, NULL); + + if (!SSL_is_init_finished(tls)) { ERR(NULL, "Supplied TLS session is not fully connected!"); return NULL; } /* prepare session structure */ session = nc_new_session(NC_CLIENT, 0); - if (!session) { - ERRMEM; - return NULL; - } + NC_CHECK_ERRMEM_RET(!session, NULL); session->status = NC_STATUS_STARTING; session->ti_type = NC_TI_OPENSSL; session->ti.tls = tls; diff --git a/src/session_p.h b/src/session_p.h index b352cc44..7e740432 100644 --- a/src/session_p.h +++ b/src/session_p.h @@ -21,30 +21,306 @@ #include #include -#include #include #include "compat.h" -#include "libnetconf.h" -#include "messages_client.h" -#include "netconf.h" -#include "session.h" +#include "config.h" #include "session_client.h" +#include "session_server_ch.h" -#ifdef NC_ENABLED_SSH +/** + * Enumeration of diff operation types. + */ +typedef enum { + NC_OP_UNKNOWN = 0, + NC_OP_NONE, + NC_OP_CREATE, + NC_OP_DELETE, + NC_OP_REPLACE +} NC_OPERATION; + +/** + * Enumeration of key or certificate store type. + */ +typedef enum { + NC_STORE_LOCAL, /**< key/certificate is stored locally in the ietf-netconf-server YANG data */ + NC_STORE_KEYSTORE, /**< key/certificate is stored externally in a keystore module YANG data */ + NC_STORE_TRUSTSTORE /**< key/certificate is stored externally in a truststore module YANG data */ +} NC_STORE_TYPE; + +#ifdef NC_ENABLED_SSH_TLS -# include -# include -# include +#include +#include /* seconds */ -# define NC_SSH_TIMEOUT 10 +#define NC_SSH_TIMEOUT 10 + /* number of all supported authentication methods */ -# define NC_SSH_AUTH_COUNT 3 +#define NC_SSH_AUTH_COUNT 3 + +/** + * Enumeration of SSH public key formats. + */ +typedef enum { + NC_PUBKEY_FORMAT_SSH, /**< see RFC 4253, section 6.6 */ + NC_PUBKEY_FORMAT_X509 /**< see RFC 5280 sec. 4.1.2.7 */ +} NC_PUBKEY_FORMAT; + +/** + * Enumeration of private key file formats. + */ +typedef enum { + NC_PRIVKEY_FORMAT_RSA, /**< PKCS1 RSA format */ + NC_PRIVKEY_FORMAT_EC, /**< SEC1 EC format */ + NC_PRIVKEY_FORMAT_X509, /**< X509 (PKCS8) format */ + NC_PRIVKEY_FORMAT_OPENSSH, /**< OpenSSH format */ + NC_PRIVKEY_FORMAT_UNKNOWN /**< Unknown format */ +} NC_PRIVKEY_FORMAT; + +/** + * @brief A basic certificate. + */ +struct nc_certificate { + char *name; /**< Arbitrary name of the certificate. */ + char *data; /**< Base-64 encoded certificate. */ +}; + +struct nc_certificate_bag { + char *name; + struct nc_certificate *certs; + uint16_t cert_count; +}; + +/** + * @brief An asymmetric key. + */ +struct nc_asymmetric_key { + char *name; /**< Arbitrary name of the key. */ + + NC_PUBKEY_FORMAT pubkey_type; /**< Type of the public key. */ + char *pubkey_data; /**< Base-64 encoded public key. */ + NC_PRIVKEY_FORMAT privkey_type; /**< Type of the private key. */ + char *privkey_data; /**< Base-64 encoded private key. */ + + struct nc_certificate *certs; /**< The certificates associated with this key. */ + uint16_t cert_count; /**< Number of certificates associated with this key. */ +}; + +/** + * @brief A symmetric key. + */ +struct nc_symmetric_key { + char *name; /**< Arbitrary name of the key. */ + char *data; /**< Base-64 encoded key. */ +}; + +/** + * @brief A public key. + */ +struct nc_public_key { + char *name; /**< Arbitrary name of the public key. */ + NC_PUBKEY_FORMAT type; /**< Type of the public key. */ + char *data; /**< Base-64 encoded public key. */ +}; + +struct nc_public_key_bag { + char *name; + struct nc_public_key *pubkeys; + uint16_t pubkey_count; +}; + +struct nc_truststore { + struct nc_certificate_bag *cert_bags; + uint16_t cert_bag_count; + + struct nc_public_key_bag *pub_bags; + uint16_t pub_bag_count; +}; + +/** + * @brief Keystore YANG module representation. + */ +struct nc_keystore { + struct nc_asymmetric_key *asym_keys; /**< Stored asymmetric keys. */ + uint16_t asym_key_count; /**< Count of stored asymmetric keys. */ + + struct nc_symmetric_key *sym_keys; /**< Stored symmetric keys. */ + uint16_t sym_key_count; /**< Count of stored symmetric keys. */ +}; + +/** + * @brief Tracks the state of a client's authentication. + */ +struct nc_auth_state { + int auth_method_count; /**< The number of auth. methods that the user supports. */ + int auth_success_count; /**< The number of auth. methods that ended successfully. */ +}; + +/** + * @brief A server's authorized client. + */ +struct nc_auth_client { + char *username; /**< Arbitrary username. */ + + NC_STORE_TYPE store; /**< Specifies how/where the client's public key is stored. */ + union { + struct { + struct nc_public_key *pubkeys; /**< The client's public keys. */ + uint16_t pubkey_count; /**< The number of client's public keys. */ + }; + char *ts_ref; /**< Name of the referenced truststore key. */ + }; + + char *password; /**< Client's password */ + char *pam_config_name; /**< Client's PAM configuration file name. */ + char *pam_config_dir; /**< Client's PAM configuration file directory. */ + int supports_none; /**< Implies that the client supports the none authentication method. */ +}; + +/** + * @brief The server's hostkey. + */ +struct nc_hostkey { + char *name; /**< Arbitrary name of the host key. */ + + NC_STORE_TYPE store; /**< Specifies how/where the key is stored. */ + union { + struct nc_asymmetric_key key; /**< The server's hostkey. */ + char *ks_ref; /**< Name of the referenced key. */ + }; +}; + +/** + * @brief Server options for configuring the SSH transport protocol. + */ +struct nc_server_ssh_opts { + struct nc_hostkey *hostkeys; /**< Server's hostkeys. */ + uint16_t hostkey_count; /**< Number of server's hostkeys. */ + + struct nc_auth_client *auth_clients; /**< Server's authorized clients. */ + uint16_t client_count; /**< Number of server's authorized clients. */ + + char *referenced_endpt_name; /**< Reference to another endpoint (used for client authentication). */ + + char *hostkey_algs; /**< Hostkey algorithms supported by the server. */ + char *encryption_algs; /**< Encryption algorithms supported by the server. */ + char *kex_algs; /**< Key exchange algorithms supported by the server. */ + char *mac_algs; /**< MAC algorithms supported by the server. */ + + uint16_t auth_attempts; /**< Number of allowed authentication attempts. */ + uint16_t auth_timeout; /**< Authentication timeout. */ +}; + +/** + * @brief Certificate grouping (either local-definition or truststore reference). + */ +struct nc_cert_grouping { + NC_STORE_TYPE store; /**< Specifies how/where the certificates are stored. */ + union { + struct { + struct nc_certificate *certs; /**< Local-defined certificates. */ + uint16_t cert_count; /**< Certificate count. */ + }; + char *ts_ref; /**< Name of the referenced truststore certificate bag. */ + }; +}; + +/** + * @brief Storing downloaded data via CURL. + */ +struct nc_curl_data { + unsigned char *data; /**< Downloaded data */ + size_t size; /**< Size of downloaded data */ +}; + +/** + * @brief Cert-to-name entries. + */ +struct nc_ctn { + uint32_t id; /**< ID of the entry, the lower the higher priority */ + char *fingerprint; /**< Fingerprint of the entry */ + NC_TLS_CTN_MAPTYPE map_type; /**< Specifies how to get the username from the certificate */ + char *name; /**< Username for this entry */ + struct nc_ctn *next; /**< Linked-list reference to the next entry */ +}; + +/** + * @brief Server options for configuring the TLS transport protocol. + */ +struct nc_server_tls_opts { + NC_STORE_TYPE store; /**< Specifies how/where the server identity is stored. */ + union { + struct { + NC_PUBKEY_FORMAT pubkey_type; /**< Server public key type */ + char *pubkey_data; /**< Server's public key */ + + NC_PRIVKEY_FORMAT privkey_type; /**< Server private key type */ + char *privkey_data; /**< Server's private key */ + + char *cert_data; /**< Server's certificate */ + }; + + struct { + char *key_ref; /**< Reference to the server's key */ + char *cert_ref; /**< Reference to the concrete server's certificate */ + }; + }; + + 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 */ + + struct nc_ctn *ctn; /**< Cert-to-name entries */ +}; + +#endif /* NC_ENABLED_SSH_TLS */ + +/** + * @brief Keepalives configuration data. + */ +struct nc_keepalives { + int enabled; /**< Indicates that keepalives are enabled. */ + uint16_t idle_time; /**< Idle timeout. */ + uint16_t max_probes; /**< Maximum number of probes. */ + uint16_t probe_interval; /**< Probe interval. */ +}; + +/** + * @brief UNIX socket connection configuration. + */ +struct nc_server_unix_opts { + char *address; /**< Address of the socket. */ + mode_t mode; /**< Socket's mode. */ + uid_t uid; /**< Socket's uid. */ + gid_t gid; /**< Socket's gid. */ +}; + +/** + * @brief Stores information about a bind. + */ +struct nc_bind { + char *address; /**< Bind's address. */ + uint16_t port; /**< Bind's port. */ + int sock; /**< Bind's socket. */ + int pollin; /**< Specifies, which sockets to poll on. */ +}; + +#ifdef NC_ENABLED_SSH_TLS -/* ACCESS unlocked */ struct nc_client_ssh_opts { + char *knownhosts_path; /**< path to known_hosts file */ + NC_SSH_KNOWNHOSTS_MODE knownhosts_mode; /**< implies whether to check known_hosts or not */ + /* SSH authentication method preferences */ struct { NC_SSH_AUTH_TYPE type; @@ -60,13 +336,11 @@ struct nc_client_ssh_opts { uint16_t key_count; /* SSH authentication callbacks */ - int (*auth_hostkey_check)(const char *, ssh_session, void *); char *(*auth_password)(const char *, const char *, void *); char *(*auth_interactive)(const char *, const char *, const char *, int, void *); char *(*auth_privkey_passphrase)(const char *, void *); /* private data for the callbacks */ - void *auth_hostkey_check_priv; void *auth_password_priv; void *auth_interactive_priv; void *auth_privkey_passphrase_priv; @@ -74,25 +348,6 @@ struct nc_client_ssh_opts { char *username; }; -/* ACCESS locked, separate locks */ -struct nc_server_ssh_opts { - /* SSH bind options */ - char **hostkeys; - uint8_t hostkey_count; - - int auth_methods; - uint16_t auth_attempts; - uint16_t auth_timeout; -}; - -#endif /* NC_ENABLED_SSH */ - -#ifdef NC_ENABLED_TLS - -# include -# include - -/* ACCESS unlocked */ struct nc_client_tls_opts { char *cert_path; char *key_path; @@ -107,40 +362,7 @@ struct nc_client_tls_opts { X509_STORE *crl_store; }; -/* ACCESS locked, separate locks */ -struct nc_server_tls_opts { - char *server_cert; - char **trusted_cert_lists; - uint16_t trusted_cert_list_count; - char *trusted_ca_file; - char *trusted_ca_dir; - X509_STORE *crl_store; - - struct nc_ctn { - uint32_t id; - char *fingerprint; - NC_TLS_CTN_MAPTYPE map_type; - char *name; - struct nc_ctn *next; - } *ctn; -}; - -#endif /* NC_ENABLED_TLS */ - -/* ACCESS unlocked */ -struct nc_keepalives { - int enabled; - uint16_t idle_time; - uint16_t max_probes; - uint16_t probe_interval; -}; - -/* ACCESS unlocked */ -struct nc_server_unix_opts { - mode_t mode; - uid_t uid; - gid_t gid; -}; +#endif /* NC_ENABLED_SSH_TLS */ /* ACCESS unlocked */ struct nc_client_opts { @@ -150,12 +372,8 @@ struct nc_client_opts { void *schema_clb_data; struct nc_keepalives ka; - struct nc_bind { - char *address; - uint16_t port; - int sock; - int pollin; - } *ch_binds; + struct nc_bind *ch_binds; + pthread_mutex_t ch_bind_lock; /**< To avoid concurrent calls of poll and accept on the bound sockets **/ struct { NC_TRANSPORT_IMPL ti; @@ -169,20 +387,36 @@ struct nc_client_context { unsigned int refcount; struct nc_client_opts opts; -#ifdef NC_ENABLED_SSH +#ifdef NC_ENABLED_SSH_TLS struct nc_client_ssh_opts ssh_opts; struct nc_client_ssh_opts ssh_ch_opts; -#endif /* NC_ENABLED_SSH */ -#ifdef NC_ENABLED_TLS + struct nc_client_tls_opts tls_opts; struct nc_client_tls_opts tls_ch_opts; -#endif /* NC_ENABLED_TLS */ +#endif /* NC_ENABLED_SSH_TLS */ +}; + +/** + * @brief Call Home client thread data. + */ +struct nc_ch_client_thread_arg { + char *client_name; + + const struct ly_ctx *(*acquire_ctx_cb)(void *cb_data); /**< acquiring libyang context cb */ + void (*release_ctx_cb)(void *cb_data); /**< releasing libyang context cb */ + void *ctx_cb_data; /**< acq/rel cb data */ + int (*new_session_cb)(const char *client_name, struct nc_session *new_session, void *user_data); /**< creating new session cb */ + void *new_session_cb_data; /**< new session cb data */ + + int thread_running; /**< A boolean value that is truthy while the underlying Call Home thread is running */ + pthread_mutex_t cond_lock; /**< Condition's lock used for signalling the thread to terminate */ + pthread_cond_t cond; /**< Condition used for signalling the thread to terminate */ }; struct nc_server_opts { /* ACCESS unlocked */ - NC_WD_MODE wd_basic_mode; - int wd_also_supported; + ATOMIC_T wd_basic_mode; + ATOMIC_T wd_also_supported; uint32_t capabilities_count; char **capabilities; @@ -191,95 +425,57 @@ struct nc_server_opts { void (*content_id_data_free)(void *data); - /* ACCESS unlocked */ uint16_t hello_timeout; uint16_t idle_timeout; -#ifdef NC_ENABLED_SSH - int (*passwd_auth_clb)(const struct nc_session *session, const char *password, void *user_data); - void *passwd_auth_data; - void (*passwd_auth_data_free)(void *data); - - int (*pubkey_auth_clb)(const struct nc_session *session, ssh_key key, void *user_data); - void *pubkey_auth_data; - void (*pubkey_auth_data_free)(void *data); - - int (*interactive_auth_sess_clb)(const struct nc_session *session, ssh_session ssh_sess, ssh_message msg, void *user_data); - void *interactive_auth_sess_data; - void (*interactive_auth_sess_data_free)(void *data); - - int (*interactive_auth_clb)(const struct nc_session *session, ssh_message msg, void *user_data); +#ifdef NC_ENABLED_SSH_TLS + int (*interactive_auth_clb)(const struct nc_session *session, ssh_session ssh_sess, ssh_message msg, void *user_data); void *interactive_auth_data; void (*interactive_auth_data_free)(void *data); - char *conf_name; - char *conf_dir; -#endif -#ifdef NC_ENABLED_TLS - int (*user_verify_clb)(const struct nc_session *session); - int (*server_cert_clb)(const char *name, void *user_data, char **cert_path, char **cert_data, char **privkey_path, - char **privkey_data, NC_SSH_KEY_TYPE *privkey_type); - void *server_cert_data; - void (*server_cert_data_free)(void *data); + int (*user_verify_clb)(const struct nc_session *session); +#endif /* NC_ENABLED_SSH_TLS */ - int (*server_cert_chain_clb)(const char *name, void *user_data, char ***cert_paths, int *cert_path_count, - char ***cert_data, int *cert_data_count); - void *server_cert_chain_data; - void (*server_cert_chain_data_free)(void *data); + pthread_rwlock_t config_lock; - int (*trusted_cert_list_clb)(const char *name, void *user_data, char ***cert_paths, int *cert_path_count, - char ***cert_data, int *cert_data_count); - void *trusted_cert_list_data; - void (*trusted_cert_list_data_free)(void *data); -#endif +#ifdef NC_ENABLED_SSH_TLS + struct nc_keystore keystore; /**< store for server's keys/certificates */ + struct nc_truststore truststore; /**< store for server client's keys/certificates */ +#endif /* NC_ENABLED_SSH_TLS */ -#ifdef NC_ENABLED_SSH - /* ACCESS locked with authkey_lock */ - struct { - char *path; - char *base64; - NC_SSH_KEY_TYPE type; - char *username; - } *authkeys; - uint16_t authkey_count; - pthread_mutex_t authkey_lock; - - int (*hostkey_clb)(const char *name, void *user_data, char **privkey_path, char **privkey_data, - NC_SSH_KEY_TYPE *privkey_type); - void *hostkey_data; - void (*hostkey_data_free)(void *data); -#endif - - /* ACCESS locked, add/remove endpts/binds - bind_lock + WRITE endpt_lock (strict order!) - * modify endpts - WRITE endpt_lock - * access endpts - READ endpt_lock - * modify/poll binds - bind_lock */ struct nc_bind *binds; - pthread_mutex_t bind_lock; + pthread_mutex_t bind_lock; /**< To avoid concurrent calls of poll and accept on the bound sockets **/ struct nc_endpt { char *name; +#ifdef NC_ENABLED_SSH_TLS + char *referenced_endpt_name; +#endif /* NC_ENABLED_SSH_TLS */ NC_TRANSPORT_IMPL ti; struct nc_keepalives ka; union { -#ifdef NC_ENABLED_SSH +#ifdef NC_ENABLED_SSH_TLS struct nc_server_ssh_opts *ssh; -#endif -#ifdef NC_ENABLED_TLS + struct nc_server_tls_opts *tls; -#endif +#endif /* NC_ENABLED_SSH_TLS */ struct nc_server_unix_opts *unixsock; } opts; } *endpts; uint16_t endpt_count; - pthread_rwlock_t endpt_lock; /* ACCESS locked, add/remove CH clients - WRITE lock ch_client_lock * modify CH clients - READ lock ch_client_lock + ch_client_lock */ struct nc_ch_client { char *name; + pthread_t tid; /**< Call Home client's thread ID */ + struct nc_ch_client_thread_arg *thread_data; /**< Data of the Call Home client's thread */ + struct nc_ch_endpt { char *name; +#ifdef NC_ENABLED_SSH_TLS + char *referenced_endpt_name; +#endif /* NC_ENABLED_SSH_TLS */ NC_TRANSPORT_IMPL ti; char *address; uint16_t port; @@ -287,32 +483,41 @@ struct nc_server_opts { struct nc_keepalives ka; union { -#ifdef NC_ENABLED_SSH +#ifdef NC_ENABLED_SSH_TLS struct nc_server_ssh_opts *ssh; -#endif -#ifdef NC_ENABLED_TLS + struct nc_server_tls_opts *tls; -#endif +#endif /* NC_ENABLED_SSH_TLS */ } opts; } *ch_endpts; uint16_t ch_endpt_count; + NC_CH_CONN_TYPE conn_type; + struct { + uint16_t period; + time_t anchor_time; + uint16_t idle_timeout; + }; - union { - struct { - uint16_t period; - time_t anchor_time; - uint16_t idle_timeout; - } period; - } conn; NC_CH_START_WITH start_with; uint8_t max_attempts; + uint16_t max_wait; uint32_t id; pthread_mutex_t lock; } *ch_clients; uint16_t ch_client_count; pthread_rwlock_t ch_client_lock; +#ifdef NC_ENABLED_SSH_TLS + struct nc_ch_dispatch_data { + nc_server_ch_session_acquire_ctx_cb acquire_ctx_cb; + nc_server_ch_session_release_ctx_cb release_ctx_cb; + void *ctx_cb_data; + nc_server_ch_new_session_cb new_session_cb; + void *new_session_cb_data; + } ch_dispatch_data; +#endif /* NC_ENABLED_SSH_TLS */ + /* Atomic IDs */ ATOMIC_T new_session_id; ATOMIC_T new_client_id; @@ -350,6 +555,11 @@ struct nc_server_opts { */ #define NC_CH_NO_ENDPT_WAIT 1000 +/** + * Time slept in msec between Call Home thread session idle timeout checks. + */ +#define NC_CH_THREAD_IDLE_TIMEOUT_SLEEP 1000 + /** * Timeout in msec for a Call Home socket to establish its connection. */ @@ -414,7 +624,7 @@ struct nc_session { struct { int sock; /**< socket file descriptor */ } unixsock; /**< NC_TI_UNIX transport implementation structure */ -#ifdef NC_ENABLED_SSH +#ifdef NC_ENABLED_SSH_TLS struct { ssh_channel channel; ssh_session session; @@ -422,10 +632,9 @@ struct nc_session { SSH session, but different SSH channel. If no such session exists, it is NULL. otherwise there is a ring list of the NETCONF sessions */ } libssh; -#endif -#ifdef NC_ENABLED_TLS + SSL *tls; -#endif +#endif /* NC_ENABLED_SSH_TLS */ } ti; /**< transport implementation data */ char *username; char *host; @@ -457,8 +666,8 @@ struct nc_session { } client; struct { /* server side only data */ - time_t session_start; /**< real time the session was created */ - time_t last_rpc; /**< monotonic time (seconds) the last RPC was received on this session */ + struct timespec session_start; /**< real time the session was created */ + time_t last_rpc; /**< monotonic time (seconds) the last RPC was received on this session */ pthread_mutex_t ntf_status_lock; /**< lock for ntf_status */ uint32_t ntf_status; /**< flag (count) whether the session is subscribed to notifications */ @@ -472,21 +681,15 @@ struct nc_session { pthread_cond_t ch_cond; /**< Call Home thread condition */ /* server flags */ -#ifdef NC_ENABLED_SSH +#ifdef NC_ENABLED_SSH_TLS /* SSH session authenticated */ # define NC_SESSION_SSH_AUTHENTICATED 0x10 /* netconf subsystem requested */ # define NC_SESSION_SSH_SUBSYS_NETCONF 0x20 - /* new SSH message arrived */ -# define NC_SESSION_SSH_NEW_MSG 0x40 - /* this session is passed to nc_sshcb_msg() */ -# define NC_SESSION_SSH_MSG_CB 0x80 - uint16_t ssh_auth_attempts; /**< number of failed SSH authentication attempts */ -#endif -#ifdef NC_ENABLED_TLS + X509 *client_cert; /**< TLS client certificate if used for authentication */ -#endif +#endif /* NC_ENABLED_SSH_TLS */ } server; } opts; }; @@ -523,7 +726,7 @@ struct nc_ntf_thread_arg { void (*free_data)(void *); }; -#ifdef NC_ENABLED_SSH +#ifdef NC_ENABLED_SSH_TLS /** * @brief PAM callback arguments. @@ -531,13 +734,42 @@ struct nc_ntf_thread_arg { struct nc_pam_thread_arg { ssh_message msg; /**< libssh message */ struct nc_session *session; /**< NETCONF session */ + struct nc_server_ssh_opts *opts; /**< SSH server opts */ }; -#endif +/** + * @brief Converts private key format to a string. + * This string is the same, as in a PKCS#1 Private key format, meaning + * ---- BEGIN (string) PRIVATE KEY ----. The string can be empty for some types. + * + * @param[in] format Private key format. + * @return String representing the private key or NULL. + */ +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. + * + * @param[in] b64 Base64 encoded data. + * + * @return -1 on error, 0 if it is not SPKI public key, 1 if it is a public key in the SPKI format. + */ +int nc_is_pk_subject_public_key_info(const char *b64); + +#endif /* NC_ENABLED_SSH_TLS */ void *nc_realloc(void *ptr, size_t size); -struct passwd *nc_getpwuid(uid_t uid, struct passwd *pwd_buf, char **buf, size_t *buf_size); +struct passwd *nc_getpw(uid_t uid, const char *username, struct passwd *pwd_buf, char **buf, size_t *buf_size); NC_MSG_TYPE nc_send_msg_io(struct nc_session *session, int io_timeout, struct lyd_node *op); @@ -564,9 +796,7 @@ int32_t nc_timeouttime_cur_diff(const struct timespec *ts); */ void nc_realtime_get(struct timespec *ts); -const char *nc_keytype2str(NC_SSH_KEY_TYPE type); - -int nc_sock_enable_keepalive(int sock, struct nc_keepalives *ka); +int nc_sock_configure_keepalive(int sock, struct nc_keepalives *ka); struct nc_session *nc_new_session(NC_SIDE side, int shared_ti); @@ -682,24 +912,24 @@ int nc_sock_listen_inet(const char *address, uint16_t port, struct nc_keepalives /** * @brief Create a listening socket (AF_UNIX). * - * @param[in] address UNIX address to listen on. - * @param[in] opts The server options (unix permissions). + * @param[in] opts The server options (unix permissions and address of the socket). * @return Listening socket, -1 on error. */ -int nc_sock_listen_unix(const char *address, const struct nc_server_unix_opts *opts); +int nc_sock_listen_unix(const struct nc_server_unix_opts *opts); /** * @brief Accept a new connection on a listening socket. * * @param[in] binds Structure with the listening sockets. * @param[in] bind_count Number of @p binds. + * @param[in] bind_lock Lock for avoiding concurrent poll/accept on a single bind. * @param[in] timeout Timeout for accepting. * @param[out] host Host of the remote peer. Can be NULL. * @param[out] port Port of the new connection. Can be NULL. * @param[out] idx Index of the bind that was accepted. Can be NULL. * @return Accepted socket of the new connection, -1 on error. */ -int nc_sock_accept_binds(struct nc_bind *binds, uint16_t bind_count, int timeout, char **host, uint16_t *port, uint16_t *idx); +int nc_sock_accept_binds(struct nc_bind *binds, uint16_t bind_count, pthread_mutex_t *bind_lock, int timeout, char **host, uint16_t *port, uint16_t *idx); /** * @brief Lock endpoint structures for reading and the specific endpoint. @@ -730,6 +960,15 @@ struct nc_ch_endpt *nc_server_ch_client_lock(const char *name, const char *endpt */ void nc_server_ch_client_unlock(struct nc_ch_client *client); +/** + * @brief Gets an endpoint structure based on its name. + * + * @param[in] name The name of the endpoint. + * @param[out] endpt Pointer to the endpoint structure. + * @return 0 on success, 1 on failure. + */ +int nc_server_get_referenced_endpt(const char *name, struct nc_endpt **endpt); + /** * @brief Add a client Call Home bind, listen on it. * @@ -763,11 +1002,7 @@ int nc_client_ch_del_bind(const char *address, uint16_t port, NC_TRANSPORT_IMPL */ NC_MSG_TYPE nc_connect_callhome(const char *host, uint16_t port, NC_TRANSPORT_IMPL ti, struct nc_session **session); -void nc_init(void); - -void nc_destroy(void); - -#ifdef NC_ENABLED_SSH +#ifdef NC_ENABLED_SSH_TLS /** * @brief Accept a server Call Home connection on a socket. @@ -789,27 +1024,24 @@ struct nc_session *nc_accept_callhome_ssh_sock(int sock, const char *host, uint1 * @param[in] timeout Transport operations timeout in msec (not SSH authentication one). * @return 1 on success, 0 on timeout, -1 on error. */ -int nc_accept_ssh_session(struct nc_session *session, int sock, int timeout); +int nc_accept_ssh_session(struct nc_session *session, struct nc_server_ssh_opts *opts, int sock, int timeout); /** - * @brief Callback called when a new SSH message is received. + * @brief Process a SSH message. * - * @param[in] sshsession SSH session the message arrived on. + * @param[in] session Session structure of the connection. + * @param[in] opts Endpoint SSH options on which the session was created. * @param[in] msg SSH message itself. - * @param[in] data NETCONF session running on @p sshsession. + * @param[in] state State of the authentication. * @return 0 if the message was handled, 1 if it is left up to libssh. */ -int nc_sshcb_msg(ssh_session sshsession, ssh_message msg, void *data); +int nc_session_ssh_msg(struct nc_session *session, struct nc_server_ssh_opts *opts, ssh_message msg, struct nc_auth_state *state); void nc_server_ssh_clear_opts(struct nc_server_ssh_opts *opts); void nc_client_ssh_destroy_opts(void); void _nc_client_ssh_destroy_opts(struct nc_client_ssh_opts *opts); -#endif /* NC_ENABLED_SSH */ - -#ifdef NC_ENABLED_TLS - 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); @@ -821,14 +1053,14 @@ struct nc_session *nc_accept_callhome_tls_sock(int sock, const char *host, uint1 * @param[in] timeout Transport operations timeout in msec. * @return 1 on success, 0 on timeout, -1 on error. */ -int nc_accept_tls_session(struct nc_session *session, int sock, int timeout); +int nc_accept_tls_session(struct nc_session *session, struct nc_server_tls_opts *opts, int sock, int timeout); void nc_server_tls_clear_opts(struct nc_server_tls_opts *opts); void nc_client_tls_destroy_opts(void); void _nc_client_tls_destroy_opts(struct nc_client_tls_opts *opts); -#endif /* NC_ENABLED_TLS */ +#endif /* NC_ENABLED_SSH_TLS */ /** * Functions diff --git a/src/session_server.c b/src/session_server.c index 9f013036..581bb88f 100644 --- a/src/session_server.c +++ b/src/session_server.c @@ -29,23 +29,30 @@ #include #include #include +#include #include #include #include #include +#ifdef NC_ENABLED_SSH_TLS +#include +#endif + #include "compat.h" -#include "libnetconf.h" +#include "config.h" +#include "log_p.h" +#include "messages_p.h" +#include "messages_server.h" +#include "server_config_p.h" +#include "session.h" +#include "session_p.h" #include "session_server.h" #include "session_server_ch.h" struct nc_server_opts server_opts = { -#ifdef NC_ENABLED_SSH - .authkey_lock = PTHREAD_MUTEX_INITIALIZER, -#endif - .bind_lock = PTHREAD_MUTEX_INITIALIZER, - .endpt_lock = PTHREAD_RWLOCK_INITIALIZER, - .ch_client_lock = PTHREAD_RWLOCK_INITIALIZER + .config_lock = PTHREAD_RWLOCK_INITIALIZER, + .ch_client_lock = PTHREAD_RWLOCK_INITIALIZER, }; static nc_rpc_clb global_rpc_clb = NULL; @@ -56,13 +63,10 @@ nc_server_endpt_lock_get(const char *name, NC_TRANSPORT_IMPL ti, uint16_t *idx) uint16_t i; struct nc_endpt *endpt = NULL; - if (!name) { - ERRARG("endpt_name"); - return NULL; - } + NC_CHECK_ARG_RET(NULL, name, NULL); - /* WRITE LOCK */ - pthread_rwlock_wrlock(&server_opts.endpt_lock); + /* READ LOCK */ + pthread_rwlock_rdlock(&server_opts.config_lock); for (i = 0; i < server_opts.endpt_count; ++i) { if (!strcmp(server_opts.endpts[i].name, name) && (!ti || (server_opts.endpts[i].ti == ti))) { @@ -74,7 +78,7 @@ nc_server_endpt_lock_get(const char *name, NC_TRANSPORT_IMPL ti, uint16_t *idx) if (!endpt) { ERR(NULL, "Endpoint \"%s\" was not found.", name); /* UNLOCK */ - pthread_rwlock_unlock(&server_opts.endpt_lock); + pthread_rwlock_unlock(&server_opts.config_lock); return NULL; } @@ -94,16 +98,13 @@ nc_server_ch_client_lock(const char *name, const char *endpt_name, NC_TRANSPORT_ *client_p = NULL; - if (!name) { - ERRARG("client_name"); - return NULL; - } + NC_CHECK_ARG_RET(NULL, name, NULL); /* READ LOCK */ pthread_rwlock_rdlock(&server_opts.ch_client_lock); for (i = 0; i < server_opts.ch_client_count; ++i) { - if (!strcmp(server_opts.ch_clients[i].name, name)) { + if (server_opts.ch_clients[i].name && !strcmp(server_opts.ch_clients[i].name, name)) { client = &server_opts.ch_clients[i]; if (!endpt_name && !ti) { /* return only client */ @@ -121,7 +122,7 @@ nc_server_ch_client_lock(const char *name, const char *endpt_name, NC_TRANSPORT_ } if (!client) { - ERR(NULL, "Call Home client \"%s\" was not found.", name); + VRB(NULL, "Call Home client \"%s\" was not found.", name); /* READ UNLOCK */ pthread_rwlock_unlock(&server_opts.ch_client_lock); @@ -150,14 +151,30 @@ nc_server_ch_client_unlock(struct nc_ch_client *client) pthread_rwlock_unlock(&server_opts.ch_client_lock); } +int +nc_server_get_referenced_endpt(const char *name, struct nc_endpt **endpt) +{ + uint16_t i; + + for (i = 0; i < server_opts.endpt_count; i++) { + if (!strcmp(name, server_opts.endpts[i].name)) { + *endpt = &server_opts.endpts[i]; + return 0; + } + } + + ERR(NULL, "Referenced endpoint \"%s\" was not found.", name); + return 1; +} + API void nc_session_set_term_reason(struct nc_session *session, NC_SESSION_TERM_REASON reason) { if (!session) { - ERRARG("session"); + ERRARG(session, "session"); return; } else if (!reason) { - ERRARG("reason"); + ERRARG(session, "reason"); return; } @@ -171,10 +188,10 @@ API void nc_session_set_killed_by(struct nc_session *session, uint32_t sid) { if (!session || (session->term_reason != NC_SESSION_TERM_KILLED)) { - ERRARG("session"); + ERRARG(session, "session"); return; } else if (!sid) { - ERRARG("sid"); + ERRARG(session, "sid"); return; } @@ -185,16 +202,115 @@ API void nc_session_set_status(struct nc_session *session, NC_STATUS status) { if (!session) { - ERRARG("session"); + ERRARG(session, "session"); return; } else if (!status) { - ERRARG("status"); + ERRARG(session, "status"); return; } session->status = status; } +API int +nc_server_init_ctx(struct ly_ctx **ctx) +{ + int new_ctx = 0, i, ret = 0; + struct lys_module *module; + /* all features */ + const char *ietf_netconf_features[] = {"writable-running", "candidate", "rollback-on-error", "validate", "startup", "url", "xpath", "confirmed-commit", NULL}; + /* all features (module has no features) */ + const char *ietf_netconf_monitoring_features[] = {NULL}; + + NC_CHECK_ARG_RET(NULL, ctx, 1); + + if (!*ctx) { + /* context not given, create a new one */ + if (ly_ctx_new(NC_SERVER_SEARCH_DIR, 0, ctx)) { + ERR(NULL, "Couldn't create new libyang context.\n"); + ret = 1; + goto cleanup; + } + new_ctx = 1; + } + + if (new_ctx) { + /* new context created, implement both modules */ + if (!ly_ctx_load_module(*ctx, "ietf-netconf", NULL, ietf_netconf_features)) { + ERR(NULL, "Loading module \"ietf-netconf\" failed.\n"); + ret = 1; + goto cleanup; + } + + if (!ly_ctx_load_module(*ctx, "ietf-netconf-monitoring", NULL, ietf_netconf_monitoring_features)) { + ERR(NULL, "Loading module \"ietf-netconf-monitoring\" failed.\n"); + ret = 1; + goto cleanup; + } + + goto cleanup; + } + + module = ly_ctx_get_module_implemented(*ctx, "ietf-netconf"); + if (module) { + /* ietf-netconf module is present, check features */ + for (i = 0; ietf_netconf_features[i]; i++) { + if (lys_feature_value(module, ietf_netconf_features[i])) { + /* feature not found, enable all of them */ + if (!ly_ctx_load_module(*ctx, "ietf-netconf", NULL, ietf_netconf_features)) { + ERR(NULL, "Loading module \"ietf-netconf\" failed.\n"); + ret = 1; + goto cleanup; + } + + break; + } + } + } else { + /* ietf-netconf module not found, add it */ + if (!ly_ctx_load_module(*ctx, "ietf-netconf", NULL, ietf_netconf_features)) { + ERR(NULL, "Loading module \"ietf-netconf\" failed.\n"); + ret = 1; + goto cleanup; + } + } + + module = ly_ctx_get_module_implemented(*ctx, "ietf-netconf-monitoring"); + if (!module) { + /* ietf-netconf-monitoring module not found, add it */ + if (!ly_ctx_load_module(*ctx, "ietf-netconf-monitoring", NULL, ietf_netconf_monitoring_features)) { + ERR(NULL, "Loading module \"ietf-netconf-monitoring\" failed.\n"); + ret = 1; + goto cleanup; + } + } + +cleanup: + if (new_ctx && ret) { + ly_ctx_destroy(*ctx); + *ctx = NULL; + } + return ret; +} + +#ifdef NC_ENABLED_SSH_TLS + +API void +nc_server_ch_set_dispatch_data(nc_server_ch_session_acquire_ctx_cb acquire_ctx_cb, + nc_server_ch_session_release_ctx_cb release_ctx_cb, void *ctx_cb_data, nc_server_ch_new_session_cb new_session_cb, + void *new_session_cb_data) +{ + NC_CHECK_ARG_RET(NULL, acquire_ctx_cb, release_ctx_cb, new_session_cb, ); + + server_opts.ch_dispatch_data.acquire_ctx_cb = acquire_ctx_cb; + server_opts.ch_dispatch_data.release_ctx_cb = release_ctx_cb; + server_opts.ch_dispatch_data.ctx_cb_data = ctx_cb_data; + server_opts.ch_dispatch_data.new_session_cb = new_session_cb; + server_opts.ch_dispatch_data.new_session_cb_data = new_session_cb_data; +} + +#endif + int nc_sock_listen_inet(const char *address, uint16_t port, struct nc_keepalives *ka) { @@ -228,7 +344,7 @@ nc_sock_listen_inet(const char *address, uint16_t port, struct nc_keepalives *ka goto fail; } - if (nc_sock_enable_keepalive(sock, ka)) { + if (nc_sock_configure_keepalive(sock, ka)) { goto fail; } @@ -270,7 +386,6 @@ nc_sock_listen_inet(const char *address, uint16_t port, struct nc_keepalives *ka ERR(NULL, "Unable to start listening on \"%s\" port %d (%s).", address, port, strerror(errno)); goto fail; } - return sock; fail: @@ -282,13 +397,13 @@ nc_sock_listen_inet(const char *address, uint16_t port, struct nc_keepalives *ka } int -nc_sock_listen_unix(const char *address, const struct nc_server_unix_opts *opts) +nc_sock_listen_unix(const struct nc_server_unix_opts *opts) { struct sockaddr_un sun; int sock = -1; - if (strlen(address) > sizeof(sun.sun_path) - 1) { - ERR(NULL, "Socket path \"%s\" is longer than maximum length %d.", address, (int)(sizeof(sun.sun_path) - 1)); + if (strlen(opts->address) > sizeof(sun.sun_path) - 1) { + ERR(NULL, "Socket path \"%s\" is longer than maximum length %d.", opts->address, (int)(sizeof(sun.sun_path) - 1)); goto fail; } @@ -300,11 +415,11 @@ nc_sock_listen_unix(const char *address, const struct nc_server_unix_opts *opts) memset(&sun, 0, sizeof(sun)); sun.sun_family = AF_UNIX; - snprintf(sun.sun_path, sizeof(sun.sun_path) - 1, "%s", address); + snprintf(sun.sun_path, sizeof(sun.sun_path) - 1, "%s", opts->address); unlink(sun.sun_path); if (bind(sock, (struct sockaddr *)&sun, sizeof(sun)) == -1) { - ERR(NULL, "Could not bind \"%s\" (%s).", address, strerror(errno)); + ERR(NULL, "Could not bind \"%s\" (%s).", opts->address, strerror(errno)); goto fail; } @@ -323,7 +438,7 @@ nc_sock_listen_unix(const char *address, const struct nc_server_unix_opts *opts) } if (listen(sock, NC_REVERSE_QUEUE) == -1) { - ERR(NULL, "Unable to start listening on \"%s\" (%s).", address, strerror(errno)); + ERR(NULL, "Unable to start listening on \"%s\" (%s).", opts->address, strerror(errno)); goto fail; } @@ -366,10 +481,7 @@ sock_host_unix(int acc_sock_fd, char **host) return 0; } - if (!(*host = strdup(sun_path))) { - ERRMEM; - return -1; - } + NC_CHECK_ERRMEM_RET(!(*host = strdup(sun_path)), -1); return 0; } @@ -386,10 +498,7 @@ static int sock_host_inet(const struct sockaddr_in *addr, char **host, uint16_t *port) { *host = malloc(INET_ADDRSTRLEN); - if (!(*host)) { - ERRMEM; - return -1; - } + NC_CHECK_ERRMEM_RET(!(*host), -1); if (!inet_ntop(AF_INET, &addr->sin_addr, *host, INET_ADDRSTRLEN)) { ERR(NULL, "inet_ntop failed (%s).", strerror(errno)); @@ -415,10 +524,7 @@ static int sock_host_inet6(const struct sockaddr_in6 *addr, char **host, uint16_t *port) { *host = malloc(INET6_ADDRSTRLEN); - if (!(*host)) { - ERRMEM; - return -1; - } + NC_CHECK_ERRMEM_RET(!(*host), -1); if (!inet_ntop(AF_INET6, &addr->sin6_addr, *host, INET6_ADDRSTRLEN)) { ERR(NULL, "inet_ntop failed (%s).", strerror(errno)); @@ -433,7 +539,7 @@ sock_host_inet6(const struct sockaddr_in6 *addr, char **host, uint16_t *port) } int -nc_sock_accept_binds(struct nc_bind *binds, uint16_t bind_count, int timeout, char **host, uint16_t *port, uint16_t *idx) +nc_sock_accept_binds(struct nc_bind *binds, uint16_t bind_count, pthread_mutex_t *bind_lock, int timeout, char **host, uint16_t *port, uint16_t *idx) { sigset_t sigmask, origmask; uint16_t i, j, pfd_count, client_port; @@ -444,10 +550,10 @@ nc_sock_accept_binds(struct nc_bind *binds, uint16_t bind_count, int timeout, ch int ret, client_sock, sock = -1, flags; pfd = malloc(bind_count * sizeof *pfd); - if (!pfd) { - ERRMEM; - return -1; - } + NC_CHECK_ERRMEM_RET(!pfd, -1); + + /* LOCK */ + pthread_mutex_lock(bind_lock); for (i = 0, pfd_count = 0; i < bind_count; ++i) { if (binds[i].sock < 0) { @@ -477,10 +583,14 @@ nc_sock_accept_binds(struct nc_bind *binds, uint16_t bind_count, int timeout, ch if (!ret) { /* we timeouted */ free(pfd); + /* UNLOCK */ + pthread_mutex_unlock(bind_lock); return 0; } else if (ret == -1) { ERR(NULL, "Poll failed (%s).", strerror(errno)); free(pfd); + /* UNLOCK */ + pthread_mutex_unlock(bind_lock); return -1; } @@ -505,9 +615,10 @@ nc_sock_accept_binds(struct nc_bind *binds, uint16_t bind_count, int timeout, ch } } free(pfd); - if (sock == -1) { ERRINT; + /* UNLOCK */ + pthread_mutex_unlock(bind_lock); return -1; } @@ -515,6 +626,8 @@ nc_sock_accept_binds(struct nc_bind *binds, uint16_t bind_count, int timeout, ch client_sock = accept(sock, (struct sockaddr *)&saddr, &saddr_len); if (client_sock < 0) { ERR(NULL, "Accept failed (%s).", strerror(errno)); + /* UNLOCK */ + pthread_mutex_unlock(bind_lock); return -1; } @@ -560,10 +673,14 @@ nc_sock_accept_binds(struct nc_bind *binds, uint16_t bind_count, int timeout, ch if (idx) { *idx = i; } + /* UNLOCK */ + pthread_mutex_unlock(bind_lock); return client_sock; fail: close(client_sock); + /* UNLOCK */ + pthread_mutex_unlock(bind_lock); return -1; } @@ -675,7 +792,7 @@ nc_clb_default_close_session(struct lyd_node *UNUSED(rpc), struct nc_session *se * @param[in] ctx Context to initialize. */ static void -nc_server_init_ctx(const struct ly_ctx *ctx) +nc_server_init_cb_ctx(const struct ly_ctx *ctx) { struct lysc_node *rpc; @@ -706,8 +823,6 @@ nc_server_init(void) pthread_rwlockattr_t attr, *attr_p = NULL; int r; - nc_init(); - server_opts.new_session_id = 1; server_opts.new_client_id = 1; @@ -723,7 +838,7 @@ nc_server_init(void) } #endif - if ((r = pthread_rwlock_init(&server_opts.endpt_lock, attr_p))) { + if ((r = pthread_rwlock_init(&server_opts.config_lock, attr_p))) { ERR(NULL, "%s: failed to init rwlock(%s).", __func__, strerror(r)); goto error; } @@ -735,6 +850,19 @@ nc_server_init(void) if (attr_p) { pthread_rwlockattr_destroy(attr_p); } + +#ifdef NC_ENABLED_SSH_TLS + if (curl_global_init(CURL_GLOBAL_SSL | CURL_GLOBAL_ACK_EINTR)) { + ERR(NULL, "%s: failed to init CURL.", __func__); + goto error; + } +#endif + + if ((r = pthread_mutex_init(&server_opts.bind_lock, NULL))) { + ERR(NULL, "%s: failed to init bind lock(%s).", __func__, strerror(r)); + goto error; + } + return 0; error: @@ -759,77 +887,37 @@ nc_server_destroy(void) server_opts.content_id_data_free(server_opts.content_id_data); } -#if defined (NC_ENABLED_SSH) || defined (NC_ENABLED_TLS) - nc_server_del_endpt(NULL, 0); - nc_server_ch_del_client(NULL); -#endif -#ifdef NC_ENABLED_SSH - if (server_opts.passwd_auth_data && server_opts.passwd_auth_data_free) { - server_opts.passwd_auth_data_free(server_opts.passwd_auth_data); - } - server_opts.passwd_auth_data = NULL; - server_opts.passwd_auth_data_free = NULL; - - if (server_opts.pubkey_auth_data && server_opts.pubkey_auth_data_free) { - server_opts.pubkey_auth_data_free(server_opts.pubkey_auth_data); - } - server_opts.pubkey_auth_data = NULL; - server_opts.pubkey_auth_data_free = NULL; + nc_server_config_listen(NULL, NC_OP_DELETE); + nc_server_config_ch(NULL, NC_OP_DELETE); - if (server_opts.interactive_auth_sess_data && server_opts.interactive_auth_sess_data_free) { - server_opts.interactive_auth_sess_data_free(server_opts.interactive_auth_sess_data); - } - server_opts.interactive_auth_sess_data = NULL; - server_opts.interactive_auth_sess_data_free = NULL; + pthread_mutex_destroy(&server_opts.bind_lock); +#ifdef NC_ENABLED_SSH_TLS if (server_opts.interactive_auth_data && server_opts.interactive_auth_data_free) { server_opts.interactive_auth_data_free(server_opts.interactive_auth_data); } server_opts.interactive_auth_data = NULL; server_opts.interactive_auth_data_free = NULL; - nc_server_ssh_del_authkey(NULL, NULL, 0, NULL); - - if (server_opts.hostkey_data && server_opts.hostkey_data_free) { - server_opts.hostkey_data_free(server_opts.hostkey_data); - } - server_opts.hostkey_data = NULL; - server_opts.hostkey_data_free = NULL; - - /* PAM */ - free(server_opts.conf_name); - free(server_opts.conf_dir); - server_opts.conf_name = NULL; - server_opts.conf_dir = NULL; -#endif -#ifdef NC_ENABLED_TLS - if (server_opts.server_cert_data && server_opts.server_cert_data_free) { - server_opts.server_cert_data_free(server_opts.server_cert_data); - } - server_opts.server_cert_data = NULL; - server_opts.server_cert_data_free = NULL; - if (server_opts.trusted_cert_list_data && server_opts.trusted_cert_list_data_free) { - server_opts.trusted_cert_list_data_free(server_opts.trusted_cert_list_data); - } - server_opts.trusted_cert_list_data = NULL; - server_opts.trusted_cert_list_data_free = NULL; -#endif - nc_destroy(); + nc_server_config_ks_keystore(NULL, NC_OP_DELETE); + nc_server_config_ts_truststore(NULL, NC_OP_DELETE); + curl_global_cleanup(); +#endif /* NC_ENABLED_SSH_TLS */ } API int nc_server_set_capab_withdefaults(NC_WD_MODE basic_mode, int also_supported) { if (!basic_mode || (basic_mode == NC_WD_ALL_TAG)) { - ERRARG("basic_mode"); + ERRARG(NULL, "basic_mode"); return -1; } else if (also_supported && !(also_supported & (NC_WD_ALL | NC_WD_ALL_TAG | NC_WD_TRIM | NC_WD_EXPLICIT))) { - ERRARG("also_supported"); + ERRARG(NULL, "also_supported"); return -1; } - server_opts.wd_basic_mode = basic_mode; - server_opts.wd_also_supported = also_supported; + ATOMIC_STORE_RELAXED(server_opts.wd_basic_mode, basic_mode); + ATOMIC_STORE_RELAXED(server_opts.wd_also_supported, also_supported); return 0; } @@ -837,15 +925,15 @@ API void nc_server_get_capab_withdefaults(NC_WD_MODE *basic_mode, int *also_supported) { if (!basic_mode && !also_supported) { - ERRARG("basic_mode and also_supported"); + ERRARG(NULL, "basic_mode and also_supported"); return; } if (basic_mode) { - *basic_mode = server_opts.wd_basic_mode; + *basic_mode = ATOMIC_LOAD_RELAXED(server_opts.wd_basic_mode); } if (also_supported) { - *also_supported = server_opts.wd_also_supported; + *also_supported = ATOMIC_LOAD_RELAXED(server_opts.wd_also_supported); } } @@ -855,15 +943,12 @@ nc_server_set_capability(const char *value) void *mem; if (!value || !value[0]) { - ERRARG("value must not be empty"); + ERRARG(NULL, "value must not be empty"); return EXIT_FAILURE; } mem = realloc(server_opts.capabilities, (server_opts.capabilities_count + 1) * sizeof *server_opts.capabilities); - if (!mem) { - ERRMEM; - return EXIT_FAILURE; - } + NC_CHECK_ERRMEM_RET(!mem, EXIT_FAILURE); server_opts.capabilities = mem; server_opts.capabilities[server_opts.capabilities_count] = strdup(value); @@ -881,62 +966,28 @@ nc_server_set_content_id_clb(char *(*content_id_clb)(void *user_data), void *use server_opts.content_id_data_free = free_user_data; } -API void -nc_server_set_hello_timeout(uint16_t hello_timeout) -{ - server_opts.hello_timeout = hello_timeout; -} - -API uint16_t -nc_server_get_hello_timeout(void) -{ - return server_opts.hello_timeout; -} - -API void -nc_server_set_idle_timeout(uint16_t idle_timeout) -{ - server_opts.idle_timeout = idle_timeout; -} - -API uint16_t -nc_server_get_idle_timeout(void) -{ - return server_opts.idle_timeout; -} - API NC_MSG_TYPE nc_accept_inout(int fdin, int fdout, const char *username, const struct ly_ctx *ctx, struct nc_session **session) { NC_MSG_TYPE msgtype; struct timespec ts_cur; - if (!ctx) { - ERRARG("ctx"); - return NC_MSG_ERROR; - } else if (fdin < 0) { - ERRARG("fdin"); + NC_CHECK_ARG_RET(NULL, ctx, username, session, NC_MSG_ERROR); + + if (fdin < 0) { + ERRARG(NULL, "fdin"); return NC_MSG_ERROR; } else if (fdout < 0) { - ERRARG("fdout"); - return NC_MSG_ERROR; - } else if (!username) { - ERRARG("username"); - return NC_MSG_ERROR; - } else if (!session) { - ERRARG("session"); + ERRARG(NULL, "fdout"); return NC_MSG_ERROR; } /* init ctx as needed */ - nc_server_init_ctx(ctx); + nc_server_init_cb_ctx(ctx); /* prepare session structure */ *session = nc_new_session(NC_SERVER, 0); - if (!(*session)) { - ERRMEM; - return NC_MSG_ERROR; - } + NC_CHECK_ERRMEM_RET(!(*session), NC_MSG_ERROR); (*session)->status = NC_STATUS_STARTING; /* transport specific data */ @@ -962,7 +1013,7 @@ nc_accept_inout(int fdin, int fdout, const char *username, const struct ly_ctx * nc_timeouttime_get(&ts_cur, 0); (*session)->opts.server.last_rpc = ts_cur.tv_sec; nc_realtime_get(&ts_cur); - (*session)->opts.server.session_start = ts_cur.tv_sec; + (*session)->opts.server.session_start = ts_cur; (*session)->status = NC_STATUS_RUNNING; @@ -1131,10 +1182,7 @@ nc_ps_new(void) struct nc_pollsession *ps; ps = calloc(1, sizeof(struct nc_pollsession)); - if (!ps) { - ERRMEM; - return NULL; - } + NC_CHECK_ERRMEM_RET(!ps, NULL); pthread_cond_init(&ps->cond, NULL); pthread_mutex_init(&ps->lock, NULL); @@ -1170,13 +1218,7 @@ nc_ps_add_session(struct nc_pollsession *ps, struct nc_session *session) { uint8_t q_id; - if (!ps) { - ERRARG("ps"); - return -1; - } else if (!session) { - ERRARG("session"); - return -1; - } + NC_CHECK_ARG_RET(session, ps, session, -1); /* LOCK */ if (nc_ps_lock(ps, &q_id, __func__)) { @@ -1241,13 +1283,7 @@ nc_ps_del_session(struct nc_pollsession *ps, struct nc_session *session) uint8_t q_id; int ret, ret2; - if (!ps) { - ERRARG("ps"); - return -1; - } else if (!session) { - ERRARG("session"); - return -1; - } + NC_CHECK_ARG_RET(session, ps, session, -1); /* LOCK */ if (nc_ps_lock(ps, &q_id, __func__)) { @@ -1268,10 +1304,7 @@ nc_ps_get_session(const struct nc_pollsession *ps, uint16_t idx) uint8_t q_id; struct nc_session *ret = NULL; - if (!ps) { - ERRARG("ps"); - return NULL; - } + NC_CHECK_ARG_RET(NULL, ps, NULL); /* LOCK */ if (nc_ps_lock((struct nc_pollsession *)ps, &q_id, __func__)) { @@ -1295,10 +1328,7 @@ nc_ps_find_session(const struct nc_pollsession *ps, nc_ps_session_match_cb match uint16_t i; struct nc_session *ret = NULL; - if (!ps) { - ERRARG("ps"); - return NULL; - } + NC_CHECK_ARG_RET(NULL, ps, NULL); /* LOCK */ if (nc_ps_lock((struct nc_pollsession *)ps, &q_id, __func__)) { @@ -1324,10 +1354,7 @@ nc_ps_session_count(struct nc_pollsession *ps) uint8_t q_id; uint16_t session_count; - if (!ps) { - ERRARG("ps"); - return 0; - } + NC_CHECK_ARG_RET(NULL, ps, 0); /* LOCK (just for memory barrier so that we read the current value) */ if (nc_ps_lock((struct nc_pollsession *)ps, &q_id, __func__)) { @@ -1378,13 +1405,9 @@ nc_server_recv_rpc_io(struct nc_session *session, int io_timeout, struct nc_serv struct lyd_node *e; int r, ret = 0; - if (!session) { - ERRARG("session"); - return NC_PSPOLL_ERROR; - } else if (!rpc) { - ERRARG("rpc"); - return NC_PSPOLL_ERROR; - } else if ((session->status != NC_STATUS_RUNNING) || (session->side != NC_SERVER)) { + NC_CHECK_ARG_RET(session, session, rpc, NC_PSPOLL_ERROR); + + if ((session->status != NC_STATUS_RUNNING) || (session->side != NC_SERVER)) { ERR(session, "Invalid session to receive RPCs."); return NC_PSPOLL_ERROR; } @@ -1405,11 +1428,7 @@ nc_server_recv_rpc_io(struct nc_session *session, int io_timeout, struct nc_serv } *rpc = calloc(1, sizeof **rpc); - if (!*rpc) { - ERRMEM; - ret = NC_PSPOLL_ERROR; - goto cleanup; - } + NC_CHECK_ERRMEM_GOTO(!*rpc, ret = NC_PSPOLL_ERROR, cleanup); /* parse the RPC */ if (!lyd_parse_op(session->ctx, NULL, msg, LYD_XML, LYD_TYPE_RPC_NETCONF, &(*rpc)->envp, &(*rpc)->rpc)) { @@ -1476,10 +1495,10 @@ nc_server_notif_send(struct nc_session *session, struct nc_server_notif *notif, /* check parameters */ if (!session || (session->side != NC_SERVER) || !nc_session_get_notif_status(session)) { - ERRARG("session"); + ERRARG(NULL, "session"); return NC_MSG_ERROR; } else if (!notif || !notif->ntf || !notif->eventtime) { - ERRARG("notif"); + ERRARG(NULL, "notif"); return NC_MSG_ERROR; } @@ -1591,13 +1610,14 @@ nc_ps_poll_session_io(struct nc_session *session, int io_timeout, time_t now_mon struct pollfd pfd; int r, ret = 0; -#ifdef NC_ENABLED_SSH +#ifdef NC_ENABLED_SSH_TLS + ssh_message ssh_msg; struct nc_session *new; -#endif +#endif /* NC_ENABLED_SSH_TLS */ /* check timeout first */ if (!(session->flags & NC_SESSION_CALLHOME) && !nc_session_get_notif_status(session) && server_opts.idle_timeout && - (now_mono >= session->opts.server.last_rpc + server_opts.idle_timeout)) { + (now_mono >= session->opts.server.last_rpc + (unsigned) server_opts.idle_timeout)) { sprintf(msg, "session idle timeout elapsed"); session->status = NC_STATUS_INVALID; session->term_reason = NC_SESSION_TERM_TIMEOUT; @@ -1613,8 +1633,37 @@ nc_ps_poll_session_io(struct nc_session *session, int io_timeout, time_t now_mon } switch (session->ti_type) { -#ifdef NC_ENABLED_SSH +#ifdef NC_ENABLED_SSH_TLS case NC_TI_LIBSSH: + ssh_msg = ssh_message_get(session->ti.libssh.session); + if (ssh_msg) { + nc_session_ssh_msg(session, NULL, ssh_msg, NULL); + if (session->ti.libssh.next) { + for (new = session->ti.libssh.next; new != session; new = new->ti.libssh.next) { + if ((new->status == NC_STATUS_STARTING) && new->ti.libssh.channel && + (new->flags & NC_SESSION_SSH_SUBSYS_NETCONF)) { + /* new NETCONF SSH channel */ + ret = NC_PSPOLL_SSH_CHANNEL; + break; + } + } + if (new != session) { + ssh_message_free(ssh_msg); + break; + } + } + if (!ret) { + /* just some SSH message */ + ret = NC_PSPOLL_SSH_MSG; + } + ssh_message_free(ssh_msg); + + /* break because 1) we don't want to return anything here ORred with NC_PSPOLL_RPC + * and 2) we don't want to delay openning a new channel by waiting for a RPC to get processed + */ + break; + } + r = ssh_channel_poll_timeout(session->ti.libssh.channel, 0, 0); if (r == SSH_EOF) { sprintf(msg, "SSH channel unexpected EOF"); @@ -1627,35 +1676,13 @@ nc_ps_poll_session_io(struct nc_session *session, int io_timeout, time_t now_mon session->term_reason = NC_SESSION_TERM_OTHER; ret = NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR; } else if (!r) { - if (session->flags & NC_SESSION_SSH_NEW_MSG) { - /* new SSH message */ - session->flags &= ~NC_SESSION_SSH_NEW_MSG; - if (session->ti.libssh.next) { - for (new = session->ti.libssh.next; new != session; new = new->ti.libssh.next) { - if ((new->status == NC_STATUS_STARTING) && new->ti.libssh.channel && - (new->flags & NC_SESSION_SSH_SUBSYS_NETCONF)) { - /* new NETCONF SSH channel */ - ret = NC_PSPOLL_SSH_CHANNEL; - break; - } - } - if (new != session) { - break; - } - } - - /* just some SSH message */ - ret = NC_PSPOLL_SSH_MSG; - } else { - ret = NC_PSPOLL_TIMEOUT; - } + /* no application data received */ + ret = NC_PSPOLL_TIMEOUT; } else { /* we have some application data */ ret = NC_PSPOLL_RPC; } break; -#endif -#ifdef NC_ENABLED_TLS case NC_TI_OPENSSL: r = SSL_pending(session->ti.tls); if (!r) { @@ -1695,7 +1722,7 @@ nc_ps_poll_session_io(struct nc_session *session, int io_timeout, time_t now_mon ret = NC_PSPOLL_RPC; } break; -#endif +#endif /* NC_ENABLED_SSH_TLS */ case NC_TI_FD: case NC_TI_UNIX: pfd.fd = (session->ti_type == NC_TI_FD) ? session->ti.fd.in : session->ti.unixsock.sock; @@ -1747,10 +1774,7 @@ nc_ps_poll(struct nc_pollsession *ps, int timeout, struct nc_session **session) struct nc_ps_session *cur_ps_session; struct nc_server_rpc *rpc = NULL; - if (!ps) { - ERRARG("ps"); - return NC_PSPOLL_ERROR; - } + NC_CHECK_ARG_RET(NULL, ps, NC_PSPOLL_ERROR); /* PS LOCK */ if (nc_ps_lock(ps, &q_id, __func__)) { @@ -1803,10 +1827,10 @@ nc_ps_poll(struct nc_pollsession *ps, int timeout, struct nc_session **session) cur_ps_session->state = NC_PS_STATE_NONE; break; case NC_PSPOLL_TIMEOUT: -#ifdef NC_ENABLED_SSH +#ifdef NC_ENABLED_SSH_TLS case NC_PSPOLL_SSH_CHANNEL: case NC_PSPOLL_SSH_MSG: -#endif +#endif /* NC_ENABLED_SSH_TLS */ cur_ps_session->state = NC_PS_STATE_NONE; break; case NC_PSPOLL_RPC: @@ -1871,10 +1895,10 @@ nc_ps_poll(struct nc_pollsession *ps, int timeout, struct nc_session **session) case NC_PSPOLL_RPC: case NC_PSPOLL_SESSION_TERM: case NC_PSPOLL_SESSION_TERM | NC_PSPOLL_SESSION_ERROR: -#ifdef NC_ENABLED_SSH +#ifdef NC_ENABLED_SSH_TLS case NC_PSPOLL_SSH_CHANNEL: case NC_PSPOLL_SSH_MSG: -#endif +#endif /* NC_ENABLED_SSH_TLS */ if (session) { *session = cur_session; } @@ -1929,7 +1953,7 @@ nc_ps_clear(struct nc_pollsession *ps, int all, void (*data_free)(void *)) struct nc_session *session; if (!ps) { - ERRARG("ps"); + ERRARG(NULL, "ps"); return; } @@ -2006,7 +2030,7 @@ nc_accept_unix(struct nc_session *session, int sock) return -1; } - pw = nc_getpwuid(uid, &pw_buf, &buf, &buf_len); + pw = nc_getpw(uid, NULL, &pw_buf, &buf, &buf_len); if (pw == NULL) { ERR(NULL, "Failed to find username for uid=%u (%s).\n", uid, strerror(errno)); close(sock); @@ -2031,529 +2055,50 @@ nc_accept_unix(struct nc_session *session, int sock) } API int -nc_server_add_endpt(const char *name, NC_TRANSPORT_IMPL ti) +nc_server_endpt_count(void) { - uint16_t i; - int ret = 0; + return server_opts.endpt_count; +} - if (!name) { - ERRARG("name"); - return -1; - } +API NC_MSG_TYPE +nc_accept(int timeout, const struct ly_ctx *ctx, struct nc_session **session) +{ + NC_MSG_TYPE msgtype; + int sock, ret; + char *host = NULL; + uint16_t port, bind_idx; + struct timespec ts_cur; - /* BIND LOCK */ - pthread_mutex_lock(&server_opts.bind_lock); + NC_CHECK_ARG_RET(NULL, ctx, session, NC_MSG_ERROR); - /* ENDPT WRITE LOCK */ - pthread_rwlock_wrlock(&server_opts.endpt_lock); + /* init ctx as needed */ + nc_server_init_cb_ctx(ctx); - /* check name uniqueness */ - for (i = 0; i < server_opts.endpt_count; ++i) { - if (!strcmp(server_opts.endpts[i].name, name)) { - ERR(NULL, "Endpoint \"%s\" already exists.", name); - ret = -1; - goto cleanup; - } - } + /* CONFIG LOCK */ + pthread_rwlock_rdlock(&server_opts.config_lock); - server_opts.endpts = nc_realloc(server_opts.endpts, (server_opts.endpt_count + 1) * sizeof *server_opts.endpts); - if (!server_opts.endpts) { - ERRMEM; - ret = -1; - goto cleanup; + if (!server_opts.endpt_count) { + ERR(NULL, "No endpoints to accept sessions on."); + /* CONFIG UNLOCK */ + pthread_rwlock_unlock(&server_opts.config_lock); + return NC_MSG_ERROR; } - memset(&server_opts.endpts[server_opts.endpt_count], 0, sizeof *server_opts.endpts); - ++server_opts.endpt_count; - server_opts.endpts[server_opts.endpt_count - 1].name = strdup(name); - server_opts.endpts[server_opts.endpt_count - 1].ti = ti; - server_opts.endpts[server_opts.endpt_count - 1].ka.idle_time = 1; - server_opts.endpts[server_opts.endpt_count - 1].ka.max_probes = 10; - server_opts.endpts[server_opts.endpt_count - 1].ka.probe_interval = 5; - - server_opts.binds = nc_realloc(server_opts.binds, server_opts.endpt_count * sizeof *server_opts.binds); - if (!server_opts.binds) { - ERRMEM; - ret = -1; - goto cleanup; + ret = nc_sock_accept_binds(server_opts.binds, server_opts.endpt_count, &server_opts.bind_lock, timeout, &host, &port, &bind_idx); + if (ret < 1) { + free(host); + /* CONFIG UNLOCK */ + pthread_rwlock_unlock(&server_opts.config_lock); + if (!ret) { + return NC_MSG_WOULDBLOCK; + } + return NC_MSG_ERROR; } - memset(&server_opts.binds[server_opts.endpt_count - 1], 0, sizeof *server_opts.binds); - server_opts.binds[server_opts.endpt_count - 1].sock = -1; - - switch (ti) { -#ifdef NC_ENABLED_SSH - case NC_TI_LIBSSH: - server_opts.endpts[server_opts.endpt_count - 1].opts.ssh = calloc(1, sizeof(struct nc_server_ssh_opts)); - if (!server_opts.endpts[server_opts.endpt_count - 1].opts.ssh) { - ERRMEM; - ret = -1; - goto cleanup; - } - server_opts.endpts[server_opts.endpt_count - 1].opts.ssh->auth_methods = - NC_SSH_AUTH_PUBLICKEY | NC_SSH_AUTH_PASSWORD; - server_opts.endpts[server_opts.endpt_count - 1].opts.ssh->auth_attempts = 3; - server_opts.endpts[server_opts.endpt_count - 1].opts.ssh->auth_timeout = 30; - break; -#endif -#ifdef NC_ENABLED_TLS - case NC_TI_OPENSSL: - server_opts.endpts[server_opts.endpt_count - 1].opts.tls = calloc(1, sizeof(struct nc_server_tls_opts)); - if (!server_opts.endpts[server_opts.endpt_count - 1].opts.tls) { - ERRMEM; - ret = -1; - goto cleanup; - } - break; -#endif - case NC_TI_UNIX: - server_opts.endpts[server_opts.endpt_count - 1].opts.unixsock = calloc(1, sizeof(struct nc_server_unix_opts)); - if (!server_opts.endpts[server_opts.endpt_count - 1].opts.unixsock) { - ERRMEM; - ret = -1; - goto cleanup; - } - server_opts.endpts[server_opts.endpt_count - 1].opts.unixsock->mode = (mode_t)-1; - server_opts.endpts[server_opts.endpt_count - 1].opts.unixsock->uid = (uid_t)-1; - server_opts.endpts[server_opts.endpt_count - 1].opts.unixsock->gid = (gid_t)-1; - break; - default: - ERRINT; - ret = -1; - goto cleanup; - } - -cleanup: - /* ENDPT UNLOCK */ - pthread_rwlock_unlock(&server_opts.endpt_lock); - - /* BIND UNLOCK */ - pthread_mutex_unlock(&server_opts.bind_lock); - - return ret; -} - -API int -nc_server_del_endpt(const char *name, NC_TRANSPORT_IMPL ti) -{ - uint32_t i; - int ret = -1; - - /* BIND LOCK */ - pthread_mutex_lock(&server_opts.bind_lock); - - /* ENDPT WRITE LOCK */ - pthread_rwlock_wrlock(&server_opts.endpt_lock); - - if (!name && !ti) { - /* remove all endpoints */ - for (i = 0; i < server_opts.endpt_count; ++i) { - free(server_opts.endpts[i].name); - switch (server_opts.endpts[i].ti) { -#ifdef NC_ENABLED_SSH - case NC_TI_LIBSSH: - nc_server_ssh_clear_opts(server_opts.endpts[i].opts.ssh); - free(server_opts.endpts[i].opts.ssh); - break; -#endif -#ifdef NC_ENABLED_TLS - case NC_TI_OPENSSL: - nc_server_tls_clear_opts(server_opts.endpts[i].opts.tls); - free(server_opts.endpts[i].opts.tls); - break; -#endif - case NC_TI_UNIX: - free(server_opts.endpts[i].opts.unixsock); - break; - default: - ERRINT; - /* won't get here ...*/ - break; - } - ret = 0; - } - free(server_opts.endpts); - server_opts.endpts = NULL; - - /* remove all binds */ - for (i = 0; i < server_opts.endpt_count; ++i) { - free(server_opts.binds[i].address); - if (server_opts.binds[i].sock > -1) { - close(server_opts.binds[i].sock); - } - } - free(server_opts.binds); - server_opts.binds = NULL; - - server_opts.endpt_count = 0; - - } else { - /* remove one endpoint with bind(s) or all endpoints using one transport protocol */ - for (i = 0; i < server_opts.endpt_count; ++i) { - if ((name && !strcmp(server_opts.endpts[i].name, name)) || (!name && (server_opts.endpts[i].ti == ti))) { - /* remove endpt */ - free(server_opts.endpts[i].name); - switch (server_opts.endpts[i].ti) { -#ifdef NC_ENABLED_SSH - case NC_TI_LIBSSH: - nc_server_ssh_clear_opts(server_opts.endpts[i].opts.ssh); - free(server_opts.endpts[i].opts.ssh); - break; -#endif -#ifdef NC_ENABLED_TLS - case NC_TI_OPENSSL: - nc_server_tls_clear_opts(server_opts.endpts[i].opts.tls); - free(server_opts.endpts[i].opts.tls); - break; -#endif - case NC_TI_UNIX: - free(server_opts.endpts[i].opts.unixsock); - break; - default: - ERRINT; - break; - } - - /* remove bind(s) */ - free(server_opts.binds[i].address); - if (server_opts.binds[i].sock > -1) { - close(server_opts.binds[i].sock); - } - - /* move last endpt and bind(s) to the empty space */ - --server_opts.endpt_count; - if (!server_opts.endpt_count) { - free(server_opts.binds); - server_opts.binds = NULL; - free(server_opts.endpts); - server_opts.endpts = NULL; - } else if (i < server_opts.endpt_count) { - memcpy(&server_opts.binds[i], &server_opts.binds[server_opts.endpt_count], sizeof *server_opts.binds); - memcpy(&server_opts.endpts[i], &server_opts.endpts[server_opts.endpt_count], sizeof *server_opts.endpts); - } - - ret = 0; - if (name) { - break; - } - } - } - } - - /* ENDPT UNLOCK */ - pthread_rwlock_unlock(&server_opts.endpt_lock); - - /* BIND UNLOCK */ - pthread_mutex_unlock(&server_opts.bind_lock); - - return ret; -} - -API int -nc_server_endpt_count(void) -{ - return server_opts.endpt_count; -} - -API int -nc_server_is_endpt(const char *name) -{ - uint16_t i; - int found = 0; - - if (!name) { - return found; - } - - /* ENDPT READ LOCK */ - pthread_rwlock_rdlock(&server_opts.endpt_lock); - - /* check name uniqueness */ - for (i = 0; i < server_opts.endpt_count; ++i) { - if (!strcmp(server_opts.endpts[i].name, name)) { - found = 1; - break; - } - } - - /* ENDPT UNLOCK */ - pthread_rwlock_unlock(&server_opts.endpt_lock); - - return found; -} - -int -nc_server_endpt_set_address_port(const char *endpt_name, const char *address, uint16_t port) -{ - struct nc_endpt *endpt; - struct nc_bind *bind = NULL; - uint16_t i; - int sock = -1, set_addr, ret = 0; - - if (!endpt_name) { - ERRARG("endpt_name"); - return -1; - } else if ((!address && !port) || (address && port)) { - ERRARG("address and port"); - return -1; - } - - if (address) { - set_addr = 1; - } else { - set_addr = 0; - } - - /* BIND LOCK */ - pthread_mutex_lock(&server_opts.bind_lock); - - /* ENDPT LOCK */ - endpt = nc_server_endpt_lock_get(endpt_name, 0, &i); - if (!endpt) { - /* BIND UNLOCK */ - pthread_mutex_unlock(&server_opts.bind_lock); - return -1; - } - - bind = &server_opts.binds[i]; - - if (set_addr) { - port = bind->port; - } else { - address = bind->address; - } - - if (!set_addr && (endpt->ti == NC_TI_UNIX)) { - ret = -1; - goto cleanup; - } - - /* we have all the information we need to create a listening socket */ - if (address && (port || (endpt->ti == NC_TI_UNIX))) { - /* create new socket, close the old one */ - if (endpt->ti == NC_TI_UNIX) { - sock = nc_sock_listen_unix(address, endpt->opts.unixsock); - } else { - sock = nc_sock_listen_inet(address, port, &endpt->ka); - } - if (sock == -1) { - ret = -1; - goto cleanup; - } - - if (bind->sock > -1) { - close(bind->sock); - } - bind->sock = sock; - } /* else we are just setting address or port */ - - if (set_addr) { - free(bind->address); - bind->address = strdup(address); - } else { - bind->port = port; - } - - if (sock > -1) { - switch (endpt->ti) { - case NC_TI_UNIX: - VRB(NULL, "Listening on %s for UNIX connections.", address); - break; -#ifdef NC_ENABLED_SSH - case NC_TI_LIBSSH: - VRB(NULL, "Listening on %s:%u for SSH connections.", address, port); - break; -#endif -#ifdef NC_ENABLED_TLS - case NC_TI_OPENSSL: - VRB(NULL, "Listening on %s:%u for TLS connections.", address, port); - break; -#endif - default: - ERRINT; - break; - } - } - -cleanup: - /* ENDPT UNLOCK */ - pthread_rwlock_unlock(&server_opts.endpt_lock); - - /* BIND UNLOCK */ - pthread_mutex_unlock(&server_opts.bind_lock); - - return ret; -} - -API int -nc_server_endpt_set_address(const char *endpt_name, const char *address) -{ - return nc_server_endpt_set_address_port(endpt_name, address, 0); -} - -#if defined (NC_ENABLED_SSH) || defined (NC_ENABLED_TLS) - -API int -nc_server_endpt_set_port(const char *endpt_name, uint16_t port) -{ - return nc_server_endpt_set_address_port(endpt_name, NULL, port); -} - -#endif - -API int -nc_server_endpt_set_perms(const char *endpt_name, mode_t mode, uid_t uid, gid_t gid) -{ - struct nc_endpt *endpt; - uint16_t i; - int ret = 0; - - if (!endpt_name) { - ERRARG("endpt_name"); - return -1; - } else if (mode == 0) { - ERRARG("mode"); - return -1; - } - - /* ENDPT LOCK */ - endpt = nc_server_endpt_lock_get(endpt_name, 0, &i); - if (!endpt) { - return -1; - } - - if (endpt->ti != NC_TI_UNIX) { - ret = -1; - goto cleanup; - } - - endpt->opts.unixsock->mode = mode; - endpt->opts.unixsock->uid = uid; - endpt->opts.unixsock->gid = gid; - -cleanup: - /* ENDPT UNLOCK */ - pthread_rwlock_unlock(&server_opts.endpt_lock); - - return ret; -} - -API int -nc_server_endpt_enable_keepalives(const char *endpt_name, int enable) -{ - struct nc_endpt *endpt; - int ret = 0; - - if (!endpt_name) { - ERRARG("endpt_name"); - return -1; - } - - /* ENDPT LOCK */ - endpt = nc_server_endpt_lock_get(endpt_name, 0, NULL); - if (!endpt) { - return -1; - } - - endpt->ka.enabled = (enable ? 1 : 0); - - /* ENDPT UNLOCK */ - pthread_rwlock_unlock(&server_opts.endpt_lock); - - return ret; -} - -API int -nc_server_endpt_set_keepalives(const char *endpt_name, int idle_time, int max_probes, int probe_interval) -{ - struct nc_endpt *endpt; - int ret = 0; - - if (!endpt_name) { - ERRARG("endpt_name"); - return -1; - } - - /* ENDPT LOCK */ - endpt = nc_server_endpt_lock_get(endpt_name, 0, NULL); - if (!endpt) { - return -1; - } - - if (idle_time > -1) { - endpt->ka.idle_time = idle_time; - } - if (max_probes > -1) { - endpt->ka.max_probes = max_probes; - } - if (probe_interval > -1) { - endpt->ka.probe_interval = probe_interval; - } - - /* ENDPT UNLOCK */ - pthread_rwlock_unlock(&server_opts.endpt_lock); - - return ret; -} - -API NC_MSG_TYPE -nc_accept(int timeout, const struct ly_ctx *ctx, struct nc_session **session) -{ - NC_MSG_TYPE msgtype; - int sock, ret; - char *host = NULL; - uint16_t port, bind_idx; - struct timespec ts_cur; - - if (!ctx) { - ERRARG("ctx"); - return NC_MSG_ERROR; - } else if (!session) { - ERRARG("session"); - return NC_MSG_ERROR; - } - - /* init ctx as needed */ - nc_server_init_ctx(ctx); - - /* BIND LOCK */ - pthread_mutex_lock(&server_opts.bind_lock); - - if (!server_opts.endpt_count) { - ERR(NULL, "No endpoints to accept sessions on."); - /* BIND UNLOCK */ - pthread_mutex_unlock(&server_opts.bind_lock); - return NC_MSG_ERROR; - } - - ret = nc_sock_accept_binds(server_opts.binds, server_opts.endpt_count, timeout, &host, &port, &bind_idx); - if (ret < 1) { - /* BIND UNLOCK */ - pthread_mutex_unlock(&server_opts.bind_lock); - free(host); - if (!ret) { - return NC_MSG_WOULDBLOCK; - } - return NC_MSG_ERROR; - } - - /* switch bind_lock for endpt_lock, so that another thread can accept another session */ - /* ENDPT READ LOCK */ - pthread_rwlock_rdlock(&server_opts.endpt_lock); - - /* BIND UNLOCK */ - pthread_mutex_unlock(&server_opts.bind_lock); - - sock = ret; + sock = ret; *session = nc_new_session(NC_SERVER, 0); - if (!(*session)) { - ERRMEM; - close(sock); - free(host); - msgtype = NC_MSG_ERROR; - goto cleanup; - } + NC_CHECK_ERRMEM_GOTO(!(*session), close(sock); free(host); msgtype = NC_MSG_ERROR, cleanup); (*session)->status = NC_STATUS_STARTING; (*session)->ctx = (struct ly_ctx *)ctx; (*session)->flags = NC_SESSION_SHAREDCTX; @@ -2561,767 +2106,140 @@ nc_accept(int timeout, const struct ly_ctx *ctx, struct nc_session **session) (*session)->port = port; /* sock gets assigned to session or closed */ -#ifdef NC_ENABLED_SSH +#ifdef NC_ENABLED_SSH_TLS if (server_opts.endpts[bind_idx].ti == NC_TI_LIBSSH) { - (*session)->data = server_opts.endpts[bind_idx].opts.ssh; - ret = nc_accept_ssh_session(*session, sock, NC_TRANSPORT_TIMEOUT); + ret = nc_accept_ssh_session(*session, server_opts.endpts[bind_idx].opts.ssh, sock, NC_TRANSPORT_TIMEOUT); + if (ret < 0) { + msgtype = NC_MSG_ERROR; + goto cleanup; + } else if (!ret) { + msgtype = NC_MSG_WOULDBLOCK; + goto cleanup; + } + } else if (server_opts.endpts[bind_idx].ti == NC_TI_OPENSSL) { + (*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); + if (ret < 0) { + msgtype = NC_MSG_ERROR; + goto cleanup; + } else if (!ret) { + msgtype = NC_MSG_WOULDBLOCK; + goto cleanup; + } + } else +#endif /* NC_ENABLED_SSH_TLS */ + if (server_opts.endpts[bind_idx].ti == NC_TI_UNIX) { + (*session)->data = server_opts.endpts[bind_idx].opts.unixsock; + ret = nc_accept_unix(*session, sock); if (ret < 0) { msgtype = NC_MSG_ERROR; goto cleanup; - } else if (!ret) { - msgtype = NC_MSG_WOULDBLOCK; - goto cleanup; - } - } else -#endif -#ifdef NC_ENABLED_TLS - if (server_opts.endpts[bind_idx].ti == NC_TI_OPENSSL) { - (*session)->data = server_opts.endpts[bind_idx].opts.tls; - ret = nc_accept_tls_session(*session, sock, NC_TRANSPORT_TIMEOUT); - if (ret < 0) { - msgtype = NC_MSG_ERROR; - goto cleanup; - } else if (!ret) { - msgtype = NC_MSG_WOULDBLOCK; - goto cleanup; - } - } else -#endif - if (server_opts.endpts[bind_idx].ti == NC_TI_UNIX) { - (*session)->data = server_opts.endpts[bind_idx].opts.unixsock; - ret = nc_accept_unix(*session, sock); - if (ret < 0) { - msgtype = NC_MSG_ERROR; - goto cleanup; - } - } else { - ERRINT; - close(sock); - msgtype = NC_MSG_ERROR; - goto cleanup; - } - - (*session)->data = NULL; - - /* ENDPT UNLOCK */ - pthread_rwlock_unlock(&server_opts.endpt_lock); - - /* assign new SID atomically */ - (*session)->id = ATOMIC_INC_RELAXED(server_opts.new_session_id); - - /* NETCONF handshake */ - msgtype = nc_handshake_io(*session); - if (msgtype != NC_MSG_HELLO) { - nc_session_free(*session, NULL); - *session = NULL; - return msgtype; - } - - nc_timeouttime_get(&ts_cur, 0); - (*session)->opts.server.last_rpc = ts_cur.tv_sec; - nc_realtime_get(&ts_cur); - (*session)->opts.server.session_start = ts_cur.tv_sec; - (*session)->status = NC_STATUS_RUNNING; - - return msgtype; - -cleanup: - /* ENDPT UNLOCK */ - pthread_rwlock_unlock(&server_opts.endpt_lock); - - nc_session_free(*session, NULL); - *session = NULL; - return msgtype; -} - -#if defined (NC_ENABLED_SSH) || defined (NC_ENABLED_TLS) - -/* client is expected to be locked */ -static int -_nc_server_ch_client_del_endpt(struct nc_ch_client *client, const char *endpt_name, NC_TRANSPORT_IMPL ti) -{ - uint16_t i; - int ret = -1; - - if (!endpt_name) { - /* remove all endpoints */ - for (i = 0; i < client->ch_endpt_count; ++i) { - free(client->ch_endpts[i].name); - free(client->ch_endpts[i].address); - if (client->ch_endpts[i].sock_pending != -1) { - close(client->ch_endpts[i].sock_pending); - } - switch (client->ch_endpts[i].ti) { -#ifdef NC_ENABLED_SSH - case NC_TI_LIBSSH: - nc_server_ssh_clear_opts(client->ch_endpts[i].opts.ssh); - free(client->ch_endpts[i].opts.ssh); - break; -#endif -#ifdef NC_ENABLED_TLS - case NC_TI_OPENSSL: - nc_server_tls_clear_opts(client->ch_endpts[i].opts.tls); - free(client->ch_endpts[i].opts.tls); - break; -#endif - default: - ERRINT; - /* won't get here ...*/ - break; - } - } - free(client->ch_endpts); - client->ch_endpts = NULL; - client->ch_endpt_count = 0; - - ret = 0; - } else { - for (i = 0; i < client->ch_endpt_count; ++i) { - if (!strcmp(client->ch_endpts[i].name, endpt_name) && (!ti || (ti == client->ch_endpts[i].ti))) { - free(client->ch_endpts[i].name); - free(client->ch_endpts[i].address); - if (client->ch_endpts[i].sock_pending != -1) { - close(client->ch_endpts[i].sock_pending); - } - switch (client->ch_endpts[i].ti) { -#ifdef NC_ENABLED_SSH - case NC_TI_LIBSSH: - nc_server_ssh_clear_opts(client->ch_endpts[i].opts.ssh); - free(client->ch_endpts[i].opts.ssh); - break; -#endif -#ifdef NC_ENABLED_TLS - case NC_TI_OPENSSL: - nc_server_tls_clear_opts(client->ch_endpts[i].opts.tls); - free(client->ch_endpts[i].opts.tls); - break; -#endif - default: - ERRINT; - /* won't get here ...*/ - break; - } - - /* move last endpoint to the empty space */ - --client->ch_endpt_count; - if (i < client->ch_endpt_count) { - memcpy(&client->ch_endpts[i], &client->ch_endpts[client->ch_endpt_count], sizeof *client->ch_endpts); - } else if (!server_opts.ch_client_count) { - free(server_opts.ch_clients); - server_opts.ch_clients = NULL; - } - - ret = 0; - break; - } - } - } - - return ret; -} - -API int -nc_server_ch_add_client(const char *name) -{ - uint16_t i; - struct nc_ch_client *client; - - if (!name) { - ERRARG("name"); - return -1; - } - - /* WRITE LOCK */ - pthread_rwlock_wrlock(&server_opts.ch_client_lock); - - /* check name uniqueness */ - for (i = 0; i < server_opts.ch_client_count; ++i) { - if (!strcmp(server_opts.ch_clients[i].name, name)) { - ERR(NULL, "Call Home client \"%s\" already exists.", name); - /* WRITE UNLOCK */ - pthread_rwlock_unlock(&server_opts.ch_client_lock); - return -1; - } - } - - ++server_opts.ch_client_count; - server_opts.ch_clients = nc_realloc(server_opts.ch_clients, server_opts.ch_client_count * sizeof *server_opts.ch_clients); - if (!server_opts.ch_clients) { - ERRMEM; - /* WRITE UNLOCK */ - pthread_rwlock_unlock(&server_opts.ch_client_lock); - return -1; - } - client = &server_opts.ch_clients[server_opts.ch_client_count - 1]; - - client->name = strdup(name); - client->id = ATOMIC_INC_RELAXED(server_opts.new_client_id); - client->ch_endpts = NULL; - client->ch_endpt_count = 0; - client->conn_type = 0; - - /* set CH default options */ - client->start_with = NC_CH_FIRST_LISTED; - client->max_attempts = 3; - - pthread_mutex_init(&client->lock, NULL); - - /* WRITE UNLOCK */ - pthread_rwlock_unlock(&server_opts.ch_client_lock); - - return 0; -} - -API int -nc_server_ch_del_client(const char *name) -{ - uint16_t i; - int ret = -1; - - /* WRITE LOCK */ - pthread_rwlock_wrlock(&server_opts.ch_client_lock); - - if (!name) { - /* remove all CH clients with endpoints */ - for (i = 0; i < server_opts.ch_client_count; ++i) { - free(server_opts.ch_clients[i].name); - - /* remove all endpoints */ - _nc_server_ch_client_del_endpt(&server_opts.ch_clients[i], NULL, 0); - - pthread_mutex_destroy(&server_opts.ch_clients[i].lock); - ret = 0; - } - free(server_opts.ch_clients); - server_opts.ch_clients = NULL; - - server_opts.ch_client_count = 0; - - } else { - /* remove one client with endpoints */ - for (i = 0; i < server_opts.ch_client_count; ++i) { - if (!strcmp(server_opts.ch_clients[i].name, name)) { - free(server_opts.ch_clients[i].name); - - /* remove all endpoints */ - _nc_server_ch_client_del_endpt(&server_opts.ch_clients[i], NULL, 0); - - pthread_mutex_destroy(&server_opts.ch_clients[i].lock); - - /* move last client and endpoint(s) to the empty space */ - --server_opts.ch_client_count; - if (i < server_opts.ch_client_count) { - memcpy(&server_opts.ch_clients[i], &server_opts.ch_clients[server_opts.ch_client_count], - sizeof *server_opts.ch_clients); - } else if (!server_opts.ch_client_count) { - free(server_opts.ch_clients); - server_opts.ch_clients = NULL; - } - - ret = 0; - break; - } - } - } - - /* WRITE UNLOCK */ - pthread_rwlock_unlock(&server_opts.ch_client_lock); - - return ret; -} - -API int -nc_server_ch_is_client(const char *name) -{ - uint16_t i; - int found = 0; - - if (!name) { - return found; - } - - /* READ LOCK */ - pthread_rwlock_rdlock(&server_opts.ch_client_lock); - - /* check name uniqueness */ - for (i = 0; i < server_opts.ch_client_count; ++i) { - if (!strcmp(server_opts.ch_clients[i].name, name)) { - found = 1; - break; - } - } - - /* UNLOCK */ - pthread_rwlock_unlock(&server_opts.ch_client_lock); - - return found; -} - -API int -nc_server_ch_client_add_endpt(const char *client_name, const char *endpt_name, NC_TRANSPORT_IMPL ti) -{ - uint16_t i; - struct nc_ch_client *client; - struct nc_ch_endpt *endpt; - int ret = -1; - - if (!client_name) { - ERRARG("client_name"); - return -1; - } else if (!endpt_name) { - ERRARG("endpt_name"); - return -1; - } else if (!ti) { - ERRARG("ti"); - return -1; - } - - /* LOCK */ - nc_server_ch_client_lock(client_name, NULL, 0, &client); - if (!client) { - return -1; - } - - for (i = 0; i < client->ch_endpt_count; ++i) { - if (!strcmp(client->ch_endpts[i].name, endpt_name)) { - ERR(NULL, "Call Home client \"%s\" endpoint \"%s\" already exists.", client_name, endpt_name); - goto cleanup; - } - } - - ++client->ch_endpt_count; - client->ch_endpts = realloc(client->ch_endpts, client->ch_endpt_count * sizeof *client->ch_endpts); - if (!client->ch_endpts) { - ERRMEM; - goto cleanup; - } - endpt = &client->ch_endpts[client->ch_endpt_count - 1]; - - memset(endpt, 0, sizeof *client->ch_endpts); - endpt->name = strdup(endpt_name); - endpt->ti = ti; - endpt->sock_pending = -1; - endpt->ka.idle_time = 1; - endpt->ka.max_probes = 10; - endpt->ka.probe_interval = 5; - - switch (ti) { -#ifdef NC_ENABLED_SSH - case NC_TI_LIBSSH: - endpt->opts.ssh = calloc(1, sizeof(struct nc_server_ssh_opts)); - if (!endpt->opts.ssh) { - ERRMEM; - goto cleanup; - } - endpt->opts.ssh->auth_methods = NC_SSH_AUTH_PUBLICKEY | NC_SSH_AUTH_PASSWORD; - endpt->opts.ssh->auth_attempts = 3; - endpt->opts.ssh->auth_timeout = 30; - break; -#endif -#ifdef NC_ENABLED_TLS - case NC_TI_OPENSSL: - endpt->opts.tls = calloc(1, sizeof(struct nc_server_tls_opts)); - if (!endpt->opts.tls) { - ERRMEM; - goto cleanup; - } - break; -#endif - default: - ERRINT; - goto cleanup; - } - - /* success */ - ret = 0; - -cleanup: - /* UNLOCK */ - nc_server_ch_client_unlock(client); - - return ret; -} - -API int -nc_server_ch_client_del_endpt(const char *client_name, const char *endpt_name, NC_TRANSPORT_IMPL ti) -{ - int ret; - struct nc_ch_client *client; - - if (!client_name) { - ERRARG("client_name"); - return -1; - } - - /* LOCK */ - nc_server_ch_client_lock(client_name, NULL, 0, &client); - if (!client) { - return -1; - } - - ret = _nc_server_ch_client_del_endpt(client, endpt_name, ti); - - /* UNLOCK */ - nc_server_ch_client_unlock(client); - - return ret; -} - -API int -nc_server_ch_client_is_endpt(const char *client_name, const char *endpt_name) -{ - uint16_t i; - struct nc_ch_client *client = NULL; - int found = 0; - - if (!client_name || !endpt_name) { - return found; - } - - /* READ LOCK */ - pthread_rwlock_rdlock(&server_opts.ch_client_lock); - - for (i = 0; i < server_opts.ch_client_count; ++i) { - if (!strcmp(server_opts.ch_clients[i].name, client_name)) { - client = &server_opts.ch_clients[i]; - break; - } - } - - if (!client) { - goto cleanup; - } - - for (i = 0; i < client->ch_endpt_count; ++i) { - if (!strcmp(client->ch_endpts[i].name, endpt_name)) { - found = 1; - break; - } - } - -cleanup: - /* UNLOCK */ - pthread_rwlock_unlock(&server_opts.ch_client_lock); - return found; -} - -API int -nc_server_ch_client_endpt_set_address(const char *client_name, const char *endpt_name, const char *address) -{ - struct nc_ch_client *client; - struct nc_ch_endpt *endpt; - - if (!client_name) { - ERRARG("client_name"); - return -1; - } else if (!endpt_name) { - ERRARG("endpt_name"); - return -1; - } else if (!address) { - ERRARG("address"); - return -1; - } - - /* LOCK */ - endpt = nc_server_ch_client_lock(client_name, endpt_name, 0, &client); - if (!endpt) { - return -1; - } - - free(endpt->address); - endpt->address = strdup(address); - - /* UNLOCK */ - nc_server_ch_client_unlock(client); - - return 0; -} - -API int -nc_server_ch_client_endpt_set_port(const char *client_name, const char *endpt_name, uint16_t port) -{ - struct nc_ch_client *client; - struct nc_ch_endpt *endpt; - - if (!client_name) { - ERRARG("client_name"); - return -1; - } else if (!endpt_name) { - ERRARG("endpt_name"); - return -1; - } else if (!port) { - ERRARG("port"); - return -1; - } - - /* LOCK */ - endpt = nc_server_ch_client_lock(client_name, endpt_name, 0, &client); - if (!endpt) { - return -1; - } - - endpt->port = port; - - /* UNLOCK */ - nc_server_ch_client_unlock(client); - - return 0; -} - -API int -nc_server_ch_client_endpt_enable_keepalives(const char *client_name, const char *endpt_name, int enable) -{ - struct nc_ch_client *client; - struct nc_ch_endpt *endpt; - - if (!client_name) { - ERRARG("client_name"); - return -1; - } else if (!endpt_name) { - ERRARG("endpt_name"); - return -1; - } - - /* LOCK */ - endpt = nc_server_ch_client_lock(client_name, endpt_name, 0, &client); - if (!endpt) { - return -1; - } - - endpt->ka.enabled = (enable ? 1 : 0); - - /* UNLOCK */ - nc_server_ch_client_unlock(client); - - return 0; -} - -API int -nc_server_ch_client_endpt_set_keepalives(const char *client_name, const char *endpt_name, int idle_time, int max_probes, - int probe_interval) -{ - struct nc_ch_client *client; - struct nc_ch_endpt *endpt; - - if (!client_name) { - ERRARG("client_name"); - return -1; - } else if (!endpt_name) { - ERRARG("endpt_name"); - return -1; - } - - /* LOCK */ - endpt = nc_server_ch_client_lock(client_name, endpt_name, 0, &client); - if (!endpt) { - return -1; - } - - if (idle_time > -1) { - endpt->ka.idle_time = idle_time; - } - if (max_probes > -1) { - endpt->ka.max_probes = max_probes; - } - if (probe_interval > -1) { - endpt->ka.probe_interval = probe_interval; - } - - /* UNLOCK */ - nc_server_ch_client_unlock(client); - - return 0; -} - -API int -nc_server_ch_client_set_conn_type(const char *client_name, NC_CH_CONN_TYPE conn_type) -{ - struct nc_ch_client *client; - - if (!client_name) { - ERRARG("client_name"); - return -1; - } else if (!conn_type) { - ERRARG("conn_type"); - return -1; - } - - /* LOCK */ - nc_server_ch_client_lock(client_name, NULL, 0, &client); - if (!client) { - return -1; - } - - if (client->conn_type != conn_type) { - client->conn_type = conn_type; - - /* set default options */ - switch (conn_type) { - case NC_CH_PERSIST: - /* no options */ - break; - case NC_CH_PERIOD: - client->conn.period.period = 60; - client->conn.period.anchor_time = 0; - client->conn.period.idle_timeout = 120; - break; - default: - ERRINT; - break; } + } else { + ERRINT; + close(sock); + msgtype = NC_MSG_ERROR; + goto cleanup; } - /* UNLOCK */ - nc_server_ch_client_unlock(client); - - return 0; -} + (*session)->data = NULL; -API int -nc_server_ch_client_periodic_set_period(const char *client_name, uint16_t period) -{ - struct nc_ch_client *client; + /* CONFIG UNLOCK */ + pthread_rwlock_unlock(&server_opts.config_lock); - if (!client_name) { - ERRARG("client_name"); - return -1; - } else if (!period) { - ERRARG("period"); - return -1; - } + /* assign new SID atomically */ + (*session)->id = ATOMIC_INC_RELAXED(server_opts.new_session_id); - /* LOCK */ - nc_server_ch_client_lock(client_name, NULL, 0, &client); - if (!client) { - return -1; + /* NETCONF handshake */ + msgtype = nc_handshake_io(*session); + if (msgtype != NC_MSG_HELLO) { + nc_session_free(*session, NULL); + *session = NULL; + return msgtype; } - if (client->conn_type != NC_CH_PERIOD) { - ERR(NULL, "Call Home client \"%s\" is not of periodic connection type.", client_name); - /* UNLOCK */ - nc_server_ch_client_unlock(client); - return -1; - } + nc_timeouttime_get(&ts_cur, 0); + (*session)->opts.server.last_rpc = ts_cur.tv_sec; + nc_realtime_get(&ts_cur); + (*session)->opts.server.session_start = ts_cur; + (*session)->status = NC_STATUS_RUNNING; - client->conn.period.period = period; + return msgtype; - /* UNLOCK */ - nc_server_ch_client_unlock(client); +cleanup: + /* CONFIG UNLOCK */ + pthread_rwlock_unlock(&server_opts.config_lock); - return 0; + nc_session_free(*session, NULL); + *session = NULL; + return msgtype; } -API int -nc_server_ch_client_periodic_set_anchor_time(const char *client_name, time_t anchor_time) -{ - struct nc_ch_client *client; - - if (!client_name) { - ERRARG("client_name"); - return -1; - } - - /* LOCK */ - nc_server_ch_client_lock(client_name, NULL, 0, &client); - if (!client) { - return -1; - } - - if (client->conn_type != NC_CH_PERIOD) { - ERR(NULL, "Call Home client \"%s\" is not of periodic connection type.", client_name); - /* UNLOCK */ - nc_server_ch_client_unlock(client); - return -1; - } - - client->conn.period.anchor_time = anchor_time; - - /* UNLOCK */ - nc_server_ch_client_unlock(client); - - return 0; -} +#ifdef NC_ENABLED_SSH_TLS API int -nc_server_ch_client_periodic_set_idle_timeout(const char *client_name, uint16_t idle_timeout) +nc_server_ch_is_client(const char *name) { - struct nc_ch_client *client; + uint16_t i; + int found = 0; - if (!client_name) { - ERRARG("client_name"); - return -1; + if (!name) { + return found; } - /* LOCK */ - nc_server_ch_client_lock(client_name, NULL, 0, &client); - if (!client) { - return -1; - } + /* READ LOCK */ + pthread_rwlock_rdlock(&server_opts.ch_client_lock); - if (client->conn_type != NC_CH_PERIOD) { - ERR(NULL, "Call Home client \"%s\" is not of periodic connection type.", client_name); - /* UNLOCK */ - nc_server_ch_client_unlock(client); - return -1; + /* check name uniqueness */ + for (i = 0; i < server_opts.ch_client_count; ++i) { + if (!strcmp(server_opts.ch_clients[i].name, name)) { + found = 1; + break; + } } - client->conn.period.idle_timeout = idle_timeout; - /* UNLOCK */ - nc_server_ch_client_unlock(client); + pthread_rwlock_unlock(&server_opts.ch_client_lock); - return 0; + return found; } API int -nc_server_ch_client_set_start_with(const char *client_name, NC_CH_START_WITH start_with) +nc_server_ch_client_is_endpt(const char *client_name, const char *endpt_name) { - struct nc_ch_client *client; - - if (!client_name) { - ERRARG("client_name"); - return -1; - } + uint16_t i; + struct nc_ch_client *client = NULL; + int found = 0; - /* LOCK */ - nc_server_ch_client_lock(client_name, NULL, 0, &client); - if (!client) { - return -1; + if (!client_name || !endpt_name) { + return found; } - client->start_with = start_with; - - /* UNLOCK */ - nc_server_ch_client_unlock(client); - - return 0; -} - -API int -nc_server_ch_client_set_max_attempts(const char *client_name, uint8_t max_attempts) -{ - struct nc_ch_client *client; + /* READ LOCK */ + pthread_rwlock_rdlock(&server_opts.ch_client_lock); - if (!client_name) { - ERRARG("client_name"); - return -1; - } else if (!max_attempts) { - ERRARG("max_attempts"); - return -1; + for (i = 0; i < server_opts.ch_client_count; ++i) { + if (!strcmp(server_opts.ch_clients[i].name, client_name)) { + client = &server_opts.ch_clients[i]; + break; + } } - /* LOCK */ - nc_server_ch_client_lock(client_name, NULL, 0, &client); if (!client) { - return -1; + goto cleanup; } - client->max_attempts = max_attempts; + for (i = 0; i < client->ch_endpt_count; ++i) { + if (!strcmp(client->ch_endpts[i].name, endpt_name)) { + found = 1; + break; + } + } +cleanup: /* UNLOCK */ - nc_server_ch_client_unlock(client); - - return 0; + pthread_rwlock_unlock(&server_opts.ch_client_lock); + return found; } /** @@ -3360,15 +2278,12 @@ nc_connect_ch_endpt(struct nc_ch_endpt *endpt, nc_server_ch_session_acquire_ctx_ return NC_MSG_ERROR; } + /* init ctx as needed */ + nc_server_init_cb_ctx(ctx); + /* create session */ *session = nc_new_session(NC_SERVER, 0); - if (!(*session)) { - ERRMEM; - close(sock); - free(ip_host); - msgtype = NC_MSG_ERROR; - goto fail; - } + NC_CHECK_ERRMEM_GOTO(!(*session), close(sock); free(ip_host); msgtype = NC_MSG_ERROR, fail); (*session)->status = NC_STATUS_STARTING; (*session)->ctx = (struct ly_ctx *)ctx; (*session)->flags = NC_SESSION_SHAREDCTX | NC_SESSION_CALLHOME; @@ -3376,10 +2291,9 @@ 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 */ -#ifdef NC_ENABLED_SSH +#ifdef NC_ENABLED_SSH_TLS if (endpt->ti == NC_TI_LIBSSH) { - (*session)->data = endpt->opts.ssh; - ret = nc_accept_ssh_session(*session, sock, NC_TRANSPORT_TIMEOUT); + ret = nc_accept_ssh_session(*session, endpt->opts.ssh, sock, NC_TRANSPORT_TIMEOUT); (*session)->data = NULL; if (ret < 0) { @@ -3389,12 +2303,9 @@ nc_connect_ch_endpt(struct nc_ch_endpt *endpt, nc_server_ch_session_acquire_ctx_ msgtype = NC_MSG_WOULDBLOCK; goto fail; } - } else -#endif -#ifdef NC_ENABLED_TLS - if (endpt->ti == NC_TI_OPENSSL) { + } else if (endpt->ti == NC_TI_OPENSSL) { (*session)->data = endpt->opts.tls; - ret = nc_accept_tls_session(*session, sock, NC_TRANSPORT_TIMEOUT); + ret = nc_accept_tls_session(*session, endpt->opts.tls, sock, NC_TRANSPORT_TIMEOUT); (*session)->data = NULL; if (ret < 0) { @@ -3405,7 +2316,7 @@ nc_connect_ch_endpt(struct nc_ch_endpt *endpt, nc_server_ch_session_acquire_ctx_ goto fail; } } else -#endif +#endif /* NC_ENABLED_SSH_TLS */ { ERRINT; close(sock); @@ -3425,7 +2336,7 @@ nc_connect_ch_endpt(struct nc_ch_endpt *endpt, nc_server_ch_session_acquire_ctx_ nc_timeouttime_get(&ts_cur, 0); (*session)->opts.server.last_rpc = ts_cur.tv_sec; nc_realtime_get(&ts_cur); - (*session)->opts.server.session_start = ts_cur.tv_sec; + (*session)->opts.server.session_start = ts_cur; (*session)->status = NC_STATUS_RUNNING; return msgtype; @@ -3439,14 +2350,6 @@ nc_connect_ch_endpt(struct nc_ch_endpt *endpt, nc_server_ch_session_acquire_ctx_ return msgtype; } -struct nc_ch_client_thread_arg { - char *client_name; - nc_server_ch_session_acquire_ctx_cb acquire_ctx_cb; - nc_server_ch_session_release_ctx_cb release_ctx_cb; - void *ctx_cb_data; - nc_server_ch_new_session_cb new_session_cb; -}; - static struct nc_ch_client * nc_server_ch_client_with_endpt_lock(const char *name) { @@ -3486,7 +2389,7 @@ nc_server_ch_client_thread_session_cond_wait(struct nc_session *session, struct session->flags |= NC_SESSION_CH_THREAD; /* give the session to the user */ - if (data->new_session_cb(data->client_name, session)) { + if (data->new_session_cb(data->client_name, session, data->new_session_cb_data)) { /* something is wrong, free the session */ session->flags &= ~NC_SESSION_CH_THREAD; @@ -3500,8 +2403,7 @@ nc_server_ch_client_thread_session_cond_wait(struct nc_session *session, struct } do { - nc_timeouttime_get(&ts, NC_CH_NO_ENDPT_WAIT); - + nc_timeouttime_get(&ts, NC_CH_THREAD_IDLE_TIMEOUT_SLEEP); /* CH COND WAIT */ r = pthread_cond_clockwait(&session->opts.server.ch_cond, &session->opts.server.ch_lock, COMPAT_CLOCK_ID, &ts); if (!r) { @@ -3527,7 +2429,7 @@ nc_server_ch_client_thread_session_cond_wait(struct nc_session *session, struct } if (client->conn_type == NC_CH_PERIOD) { - idle_timeout = client->conn.period.idle_timeout; + idle_timeout = client->idle_timeout; } else { idle_timeout = 0; } @@ -3554,30 +2456,102 @@ nc_server_ch_client_thread_session_cond_wait(struct nc_session *session, struct return ret; } +/** + * @brief Waits for some amount of time while reacting to signals about terminating a Call Home thread. + * + * @param[in] session An established session. + * @param[in] data Call Home thread's data. + * @param[in] cond_wait_time Time in seconds to sleep for, after which a reconnect is attempted. + * + * @return 0 if the thread should stop running, 1 if it should continue. + */ +static int +nc_server_ch_client_thread_is_running_wait(struct nc_session *session, struct nc_ch_client_thread_arg *data, uint64_t cond_wait_time) +{ + struct timespec ts; + int ret = 0, thread_running; + + /* COND LOCK */ + pthread_mutex_lock(&data->cond_lock); + /* get reconnect timeout in ms */ + nc_timeouttime_get(&ts, cond_wait_time * 1000); + while (!ret && data->thread_running) { + ret = pthread_cond_clockwait(&data->cond, &data->cond_lock, COMPAT_CLOCK_ID, &ts); + } + + thread_running = data->thread_running; + /* COND UNLOCK */ + pthread_mutex_unlock(&data->cond_lock); + + if (!thread_running) { + /* thread is terminating */ + VRB(session, "Call Home thread signaled to exit, client \"%s\" probably removed.", data->client_name); + ret = 0; + } else if (ret == ETIMEDOUT) { + /* time to reconnect */ + VRB(session, "Call Home client \"%s\" timeout of %" PRIu64 " seconds expired, reconnecting.", data->client_name, cond_wait_time); + ret = 1; + } else if (ret) { + ERR(session, "Pthread condition timedwait failed (%s).", strerror(ret)); + ret = 0; + } + + return ret; +} + +/** + * @brief Checks if a Call Home thread should terminate. + * + * Checks the shared boolean variable thread_running. This should be done everytime + * before entering a critical section. + * + * @param[in] data Call Home thread's data. + * + * @return 0 if the thread should stop running, -1 if it can continue. + */ +static int +nc_server_ch_client_thread_is_running(struct nc_ch_client_thread_arg *data) +{ + int ret = -1; + + /* COND LOCK */ + pthread_mutex_lock(&data->cond_lock); + if (!data->thread_running) { + /* thread should stop running */ + ret = 0; + } + /* COND UNLOCK */ + pthread_mutex_unlock(&data->cond_lock); + + return ret; +} + static void * nc_ch_client_thread(void *arg) { struct nc_ch_client_thread_arg *data = (struct nc_ch_client_thread_arg *)arg; NC_MSG_TYPE msgtype; uint8_t cur_attempts = 0; - uint16_t next_endpt_index; + uint16_t next_endpt_index, max_wait; char *cur_endpt_name = NULL; struct nc_ch_endpt *cur_endpt; - struct nc_session *session; + struct nc_session *session = NULL; struct nc_ch_client *client; - uint32_t client_id, reconnect_in; + uint32_t reconnect_in; /* LOCK */ client = nc_server_ch_client_with_endpt_lock(data->client_name); - if (!client) { - goto cleanup; - } - client_id = client->id; + assert(client); cur_endpt = &client->ch_endpts[0]; cur_endpt_name = strdup(cur_endpt->name); while (1) { + if (!nc_server_ch_client_thread_is_running(data)) { + /* thread should stop running */ + break; + } + if (!cur_attempts) { VRB(NULL, "Call Home client \"%s\" endpoint \"%s\" connecting...", data->client_name, cur_endpt_name); } @@ -3587,49 +2561,50 @@ nc_ch_client_thread(void *arg) /* UNLOCK */ nc_server_ch_client_unlock(client); - VRB(NULL, "Call Home client \"%s\" session %u established.", data->client_name, session->id); - if (nc_server_ch_client_thread_session_cond_wait(session, data)) { + if (!nc_server_ch_client_thread_is_running(data)) { + /* thread should stop running */ goto cleanup; } - VRB(NULL, "Call Home client \"%s\" session terminated.", data->client_name); - /* LOCK */ - client = nc_server_ch_client_with_endpt_lock(data->client_name); - if (!client) { + /* run while the session is established */ + VRB(session, "Call Home client \"%s\" session %u established.", data->client_name, session->id); + if (nc_server_ch_client_thread_session_cond_wait(session, data)) { goto cleanup; } - if (client->id != client_id) { - nc_server_ch_client_unlock(client); + + VRB(session, "Call Home client \"%s\" session terminated.", data->client_name); + if (!nc_server_ch_client_thread_is_running(data)) { + /* thread should stop running */ goto cleanup; } + /* LOCK */ + client = nc_server_ch_client_with_endpt_lock(data->client_name); + assert(client); + /* session changed status -> it was disconnected for whatever reason, * persistent connection immediately tries to reconnect, periodic connects at specific times */ if (client->conn_type == NC_CH_PERIOD) { - if (client->conn.period.anchor_time) { + if (client->anchor_time) { /* anchored */ - reconnect_in = (time(NULL) - client->conn.period.anchor_time) % (client->conn.period.period * 60); + reconnect_in = (time(NULL) - client->anchor_time) % (client->period * 60); } else { /* fixed timeout */ - reconnect_in = client->conn.period.period * 60; + reconnect_in = client->period * 60; } /* UNLOCK */ nc_server_ch_client_unlock(client); - /* sleep until we should reconnect TODO wake up sometimes to check for new notifications */ - VRB(NULL, "Call Home client \"%s\" reconnecting in %" PRIu32 " seconds.", data->client_name, reconnect_in); - sleep(reconnect_in); + /* wait for the timeout to elapse, so we can try to reconnect */ + VRB(session, "Call Home client \"%s\" reconnecting in %" PRIu32 " seconds.", data->client_name, reconnect_in); + if (!nc_server_ch_client_thread_is_running_wait(session, data, reconnect_in)) { + goto cleanup; + } /* LOCK */ client = nc_server_ch_client_with_endpt_lock(data->client_name); - if (!client) { - goto cleanup; - } - if (client->id != client_id) { - nc_server_ch_client_unlock(client); - goto cleanup; - } + assert(client); } /* set next endpoint to try */ @@ -3652,21 +2627,21 @@ nc_ch_client_thread(void *arg) } } else { + /* session was not created, wait a little bit and try again */ + max_wait = client->max_wait; + /* UNLOCK */ nc_server_ch_client_unlock(client); - /* session was not created */ - sleep(NC_CH_ENDPT_BACKOFF_WAIT); + /* wait for max_wait seconds */ + if (!nc_server_ch_client_thread_is_running_wait(session, data, max_wait)) { + /* thread should stop running */ + goto cleanup; + } /* LOCK */ client = nc_server_ch_client_with_endpt_lock(data->client_name); - if (!client) { - goto cleanup; - } - if (client->id != client_id) { - nc_server_ch_client_unlock(client); - goto cleanup; - } + assert(client); ++cur_attempts; @@ -3679,12 +2654,12 @@ nc_ch_client_thread(void *arg) if (next_endpt_index >= client->ch_endpt_count) { /* endpoint was removed, start with the first one */ - VRB(NULL, "Call Home client \"%s\" endpoint \"%s\" removed.", data->client_name, cur_endpt_name); + VRB(session, "Call Home client \"%s\" endpoint \"%s\" removed.", data->client_name, cur_endpt_name); next_endpt_index = 0; cur_attempts = 0; } else if (cur_attempts == client->max_attempts) { /* we have tried to connect to this endpoint enough times */ - VRB(NULL, "Call Home client \"%s\" endpoint \"%s\" failed connection attempt limit %" PRIu8 " reached.", + VRB(session, "Call Home client \"%s\" endpoint \"%s\" failed connection attempt limit %" PRIu8 " reached.", data->client_name, cur_endpt_name, client->max_attempts); /* clear a pending socket, if any */ @@ -3709,9 +2684,11 @@ nc_ch_client_thread(void *arg) free(cur_endpt_name); cur_endpt_name = strdup(cur_endpt->name); } + /* UNLOCK if we break out of the loop */ + nc_server_ch_client_unlock(client); cleanup: - VRB(NULL, "Call Home client \"%s\" thread exit.", data->client_name); + VRB(session, "Call Home client \"%s\" thread exit.", data->client_name); free(cur_endpt_name); free(data->client_name); free(data); @@ -3720,31 +2697,31 @@ nc_ch_client_thread(void *arg) API int nc_connect_ch_client_dispatch(const char *client_name, nc_server_ch_session_acquire_ctx_cb acquire_ctx_cb, - nc_server_ch_session_release_ctx_cb release_ctx_cb, void *ctx_cb_data, nc_server_ch_new_session_cb new_session_cb) + nc_server_ch_session_release_ctx_cb release_ctx_cb, void *ctx_cb_data, nc_server_ch_new_session_cb new_session_cb, + void *new_session_cb_data) { int ret; pthread_t tid; struct nc_ch_client_thread_arg *arg; + uint16_t i; + struct nc_ch_client *ch_client; - if (!client_name) { - ERRARG("client_name"); - return -1; - } else if (!acquire_ctx_cb) { - ERRARG("acquire_ctx_cb"); - return -1; - } else if (!release_ctx_cb) { - ERRARG("release_ctx_cb"); - return -1; - } else if (!new_session_cb) { - ERRARG("new_session_cb"); - return -1; + NC_CHECK_ARG_RET(NULL, client_name, acquire_ctx_cb, release_ctx_cb, new_session_cb, -1); + + for (i = 0; i < server_opts.ch_client_count; i++) { + if (!strcmp(server_opts.ch_clients[i].name, client_name)) { + ch_client = &server_opts.ch_clients[i]; + break; + } } - arg = malloc(sizeof *arg); - if (!arg) { - ERRMEM; + if (i == server_opts.ch_client_count) { + ERR(NULL, "Client \"%s\" not found.", client_name); return -1; } + + arg = malloc(sizeof *arg); + NC_CHECK_ERRMEM_RET(!arg, -1); arg->client_name = strdup(client_name); if (!arg->client_name) { ERRMEM; @@ -3755,6 +2732,13 @@ nc_connect_ch_client_dispatch(const char *client_name, nc_server_ch_session_acqu arg->release_ctx_cb = release_ctx_cb; arg->ctx_cb_data = ctx_cb_data; arg->new_session_cb = new_session_cb; + arg->new_session_cb_data = new_session_cb_data; + /* thread is now running */ + arg->thread_running = 1; + /* initialize the condition */ + pthread_cond_init(&arg->cond, NULL); + /* initialize the mutex */ + pthread_mutex_init(&arg->cond_lock, NULL); ret = pthread_create(&tid, NULL, nc_ch_client_thread, arg); if (ret) { @@ -3764,20 +2748,24 @@ nc_connect_ch_client_dispatch(const char *client_name, nc_server_ch_session_acqu return -1; } /* the thread now manages arg */ - - pthread_detach(tid); + ch_client->tid = tid; + ch_client->thread_data = arg; return 0; } -#endif /* NC_ENABLED_SSH || NC_ENABLED_TLS */ +#endif /* NC_ENABLED_SSH_TLS */ -API time_t +API struct timespec nc_session_get_start_time(const struct nc_session *session) { - if (!session || (session->side != NC_SERVER)) { - ERRARG("session"); - return 0; + struct timespec fail = {0}; + + NC_CHECK_ARG_RET(session, session, fail); + + if (session->side != NC_SERVER) { + ERRARG(session, "session"); + return fail; } return session->opts.server.session_start; @@ -3787,7 +2775,7 @@ API void nc_session_inc_notif_status(struct nc_session *session) { if (!session || (session->side != NC_SERVER)) { - ERRARG("session"); + ERRARG(session, "session"); return; } @@ -3804,7 +2792,7 @@ API void nc_session_dec_notif_status(struct nc_session *session) { if (!session || (session->side != NC_SERVER)) { - ERRARG("session"); + ERRARG(session, "session"); return; } @@ -3825,7 +2813,7 @@ nc_session_get_notif_status(const struct nc_session *session) uint32_t ntf_status; if (!session || (session->side != NC_SERVER)) { - ERRARG("session"); + ERRARG(session, "session"); return 0; } diff --git a/src/session_server.h b/src/session_server.h index 64dadbcd..b7bab618 100644 --- a/src/session_server.h +++ b/src/session_server.h @@ -24,15 +24,13 @@ extern "C" { #include #include -#ifdef NC_ENABLED_TLS +#ifdef NC_ENABLED_SSH_TLS # include -#endif -#ifdef NC_ENABLED_SSH # include # include # include -#endif +#endif /* NC_ENABLED_SSH_TLS */ #include "netconf.h" #include "session.h" @@ -116,7 +114,8 @@ struct nc_server_reply *nc_clb_default_close_session(struct lyd_node *rpc, struc /** @} Server Session */ /** - * @addtogroup server + * @defgroup server_functions Server Functions + * @ingroup server * @{ */ @@ -135,6 +134,23 @@ int nc_server_init(void); */ void nc_server_destroy(void); +/** + * @brief Initialize a context which can serve as a default server context. + * + * Loads the default modules ietf-netconf and ietf-netconf-monitoring and their enabled features - ietf-netconf + * enabled features are : writable-running, candidate, rollback-on-error, validate, startup, url, xpath, confirmed-commit and + * ietf-netconf-monitoring has no features. + * + * If ctx is : + * - NULL: a new context will be created and if the call is successful you have to free it, + * - non NULL: context will be searched for the two modules and their features + * and if anything is missing, it will be implemented. + * + * @param[in,out] ctx Optional context in which the modules will be loaded. Created if ctx is null. + * @return 0 on success, -1 on error. + */ +int nc_server_init_ctx(struct ly_ctx **ctx); + /** * @brief Set the with-defaults capability extra parameters. * @@ -156,8 +172,8 @@ int nc_server_set_capab_withdefaults(NC_WD_MODE basic_mode, int also_supported); * * At least one argument must be non-NULL. * - * @param[in,out] basic_mode basic-mode parameter. - * @param[in,out] also_supported also-supported parameter. + * @param[out] basic_mode basic-mode parameter. + * @param[out] also_supported also-supported parameter. */ void nc_server_get_capab_withdefaults(NC_WD_MODE *basic_mode, int *also_supported); @@ -183,36 +199,6 @@ int nc_server_set_capability(const char *value); void nc_server_set_content_id_clb(char *(*content_id_clb)(void *user_data), void *user_data, void (*free_user_data)(void *user_data)); -/** - * @brief Set server timeout for receiving a hello message. - * - * @param[in] hello_timeout Hello message timeout. 0 for infinite waiting. - */ -void nc_server_set_hello_timeout(uint16_t hello_timeout); - -/** - * @brief get server timeout for receiving a hello message. - * - * @return Hello message timeout, 0 is infinite. - */ -uint16_t nc_server_get_hello_timeout(void); - -/** - * @brief Set server timeout for dropping an idle session. - * - * @param[in] idle_timeout Idle session timeout. 0 to never drop a session - * because of inactivity. - */ -void nc_server_set_idle_timeout(uint16_t idle_timeout); - -/** - * @brief Get server timeout for dropping an idle session. - * - * @return Idle session timeout, 0 for for never dropping - * a session because of inactivity. - */ -uint16_t nc_server_get_idle_timeout(void); - /** * @brief Get all the server capabilities including all the schemas. * @@ -237,7 +223,7 @@ char **nc_server_get_cpblts(const struct ly_ctx *ctx); */ char **nc_server_get_cpblts_version(const struct ly_ctx *ctx, LYS_VERSION version); -/** @} Server */ +/** @} Server Functions */ /** * @addtogroup server_session @@ -343,10 +329,10 @@ uint16_t nc_ps_session_count(struct nc_pollsession *ps); #define NC_PSPOLL_SESSION_ERROR 0x0040 /**< Some session was terminated incorrectly (not by a \ or \ RPC). */ #define NC_PSPOLL_ERROR 0x0080 /**< Other fatal errors (they are printed). */ -#ifdef NC_ENABLED_SSH +#ifdef NC_ENABLED_SSH_TLS # define NC_PSPOLL_SSH_MSG 0x00100 /**< SSH message received (and processed, if relevant, only with SSH support). */ # define NC_PSPOLL_SSH_CHANNEL 0x0200 /**< New SSH channel opened on an existing session (only with SSH support). */ -#endif +#endif /* NC_ENABLED_SSH_TLS */ /** * @brief Poll sessions and process any received RPCs. @@ -379,33 +365,10 @@ void nc_ps_clear(struct nc_pollsession *ps, int all, void (*data_free)(void *)); /** @} Server Session */ /** - * @addtogroup server + * @addtogroup server_functions * @{ */ -/** - * @brief Add a new endpoint. - * - * Before the endpoint can accept any connections, its address and port must - * be set via nc_server_endpt_set_address() and nc_server_endpt_set_port(). - * - * @param[in] name Arbitrary unique endpoint name. - * @param[in] ti Transport protocol to use. - * @return 0 on success, -1 on error. - */ -int nc_server_add_endpt(const char *name, NC_TRANSPORT_IMPL ti); - -/** - * @brief Stop listening on and remove an endpoint. - * - * @param[in] name Endpoint name. NULL matches all endpoints. - * @param[in] ti Endpoint transport protocol. NULL matches any protocol. - * Redundant to set if @p name is set, endpoint names are - * unique disregarding their protocol. - * @return 0 on success, -1 on not finding any match. - */ -int nc_server_del_endpt(const char *name, NC_TRANSPORT_IMPL ti); - /** * @brief Get the number of currently configured listening endpoints. * Note that an ednpoint without address and/or port will be included @@ -415,76 +378,7 @@ int nc_server_del_endpt(const char *name, NC_TRANSPORT_IMPL ti); */ int nc_server_endpt_count(void); -/** - * @brief Check if an endpoint exists. - * - * @param[in] name Endpoint name. - * @return 0 if does not exists, non-zero otherwise. - */ -int nc_server_is_endpt(const char *name); - -/** - * @brief Change endpoint listening address. - * - * On error the previous listening socket (if any) is left untouched. - * - * @param[in] endpt_name Existing endpoint name. - * @param[in] address New listening address. - * @return 0 on success, -1 on error. - */ -int nc_server_endpt_set_address(const char *endpt_name, const char *address); - -#if defined (NC_ENABLED_SSH) || defined (NC_ENABLED_TLS) - -/** - * @brief Change endpoint listening port. - * - * This is only valid on SSH/TLS transport endpoint. - * On error the previous listening socket (if any) is left untouched. - * - * @param[in] endpt_name Existing endpoint name. - * @param[in] port New listening port. - * @return 0 on success, -1 on error. - */ -int nc_server_endpt_set_port(const char *endpt_name, uint16_t port); - -#endif - -/** - * @brief Change endpoint permissions. - * - * This is only valid on UNIX transport endpoint. - * On error the previous listening socket (if any) is left untouched. - * - * @param[in] endpt_name Existing endpoint name. - * @param[in] mode New mode, -1 to use default. - * @param[in] uid New uid, -1 to use default. - * @param[in] gid New gid, -1 to use default. - * @return 0 on success, -1 on error. - */ -int nc_server_endpt_set_perms(const char *endpt_name, mode_t mode, uid_t uid, gid_t gid); - -/** - * @brief Change endpoint keepalives state. Affects only new connections. - * - * @param[in] endpt_name Existing endpoint name. - * @param[in] enable Whether to enable or disable keepalives. - * @return 0 on success, -1 on error. - */ -int nc_server_endpt_enable_keepalives(const char *endpt_name, int enable); - -/** - * @brief Change endpoint keepalives parameters. Affects only new connections. - * - * @param[in] endpt_name Existing endpoint name. - * @param[in] idle_time Keepalive idle time in seconds, 1 by default, -1 to keep previous value. - * @param[in] max_probes Keepalive max probes sent, 10 by default, -1 to keep previous value. - * @param[in] probe_interval Keepalive probe interval in seconds, 5 by default, -1 to keep previous value. - * @return 0 on success, -1 on error. - */ -int nc_server_endpt_set_keepalives(const char *endpt_name, int idle_time, int max_probes, int probe_interval); - -/** @} Server */ +/** @} */ /** * @addtogroup server_session @@ -499,7 +393,8 @@ int nc_server_endpt_set_keepalives(const char *endpt_name, int idle_time, int ma * for much longer that @p timeout, but only with slow/faulty/malicious clients. * * Server capabilities are generated based on the content of @p ctx. The context must - * not be destroyed before the accepted NETCONF session is freed. + * not be destroyed before the accepted NETCONF session is freed. Basic usable context may + * be created by calling ::nc_server_init_ctx(). * * Supported RPCs of models in the context are expected to have their callback * in the corresponding RPC schema node set to a nc_rpc_clb function callback using ::nc_set_rpc_callback(). @@ -516,7 +411,7 @@ int nc_server_endpt_set_keepalives(const char *endpt_name, int idle_time, int ma */ NC_MSG_TYPE nc_accept(int timeout, const struct ly_ctx *ctx, struct nc_session **session); -#ifdef NC_ENABLED_SSH +#ifdef NC_ENABLED_SSH_TLS /** * @brief Accept a new NETCONF session on an SSH session of a running NETCONF @p orig_session. @@ -552,192 +447,18 @@ NC_MSG_TYPE nc_ps_accept_ssh_channel(struct nc_pollsession *ps, struct nc_sessio */ /** - * @brief Add an authorized client SSH public key. This public key can be used for - * publickey authentication (for any SSH connection, even Call Home) afterwards. - * - * @param[in] pubkey_base64 Authorized public key binary content encoded in base64. - * @param[in] type Authorized public key SSH type. - * @param[in] username Username that the client with the public key must use. - * @return 0 on success, -1 on error. - */ -int nc_server_ssh_add_authkey(const char *pubkey_base64, NC_SSH_KEY_TYPE type, const char *username); - -/** - * @brief Add an authorized client SSH public key. This public key can be used for - * publickey authentication (for any SSH connection, even Call Home) afterwards. - * - * @param[in] pubkey_path Path to the public key. - * @param[in] username Username that the client with the public key must use. - * @return 0 on success, -1 on error. - */ -int nc_server_ssh_add_authkey_path(const char *pubkey_path, const char *username); - -/** - * @brief Remove an authorized client SSH public key. - * - * @param[in] pubkey_path Path to an authorized public key. NULL matches all the keys. - * @param[in] pubkey_base64 Authorized public key content. NULL matches any key. - * @param[in] type Authorized public key type. 0 matches all types. - * @param[in] username Username for an authorized public key. NULL matches all the usernames. - * @return 0 on success, -1 on not finding any match. - */ -int nc_server_ssh_del_authkey(const char *pubkey_path, const char *pubkey_base64, NC_SSH_KEY_TYPE type, - const char *username); - -/** - * @brief Set the callback for SSH password authentication. If none is set, local system users are used. - * - * @param[in] passwd_auth_clb Callback that should authenticate the user. Username can be directly obtained from @p session. - * Zero return indicates success, non-zero an error. - * @param[in] user_data Optional arbitrary user data that will be passed to @p passwd_auth_clb. - * @param[in] free_user_data Optional callback that will be called during cleanup to free any @p user_data. - */ -void nc_server_ssh_set_passwd_auth_clb(int (*passwd_auth_clb)(const struct nc_session *session, const char *password, - void *user_data), void *user_data, void (*free_user_data)(void *user_data)); - -/** - * @brief Set the callback for SSH interactive authentication. If none is set, local PAM-based authentication is used. + * @brief Set the callback for SSH interactive authentication. If not set, local PAM-based authentication is used. * - * @param[in] interactive_auth_sess_clb Callback that should authenticate the user. + * @param[in] interactive_auth_clb Callback that should authenticate the user. * Zero return indicates success, non-zero an error. - * @param[in] user_data Optional arbitrary user data that will be passed to @p interactive_auth_sess_clb. + * @param[in] user_data Optional arbitrary user data that will be passed to @p interactive_auth_clb. * @param[in] free_user_data Optional callback that will be called during cleanup to free any @p user_data. */ -void nc_server_ssh_set_interactive_auth_sess_clb(int (*interactive_auth_sess_clb)(const struct nc_session *session, - ssh_session ssh_sess, ssh_message msg, void *user_data), void *user_data, void (*free_user_data)(void *user_data)); - -/** - * @brief Deprecated, use ::nc_server_ssh_set_interactive_auth_sess_clb() instead. - */ void nc_server_ssh_set_interactive_auth_clb(int (*interactive_auth_clb)(const struct nc_session *session, - const ssh_message msg, void *user_data), void *user_data, void (*free_user_data)(void *user_data)); - -/** - * @brief Set the name and a path to a PAM configuration file. - * - * @p conf_name has to be set via this function prior to using PAM keyboard-interactive authentication method. - * - * @param[in] conf_name Name of the configuration file. - * @param[in] conf_dir Optional. The absolute path to the directory in which the configuration file - * with the name @p conf_name is located. A newer version (>= 1.4) of PAM library is required to be - * able to specify the path. If NULL is passed, - * then the PAM's system directories will be searched (usually /etc/pam.d/). - * @return 0 on success, -1 on error. - */ -int nc_server_ssh_set_pam_conf_path(const char *conf_name, const char *conf_dir); - -/** - * @brief Set the callback for SSH public key authentication. If none is set, local system users are used. - * - * @param[in] pubkey_auth_clb Callback that should authenticate the user. - * Zero return indicates success, non-zero an error. - * @param[in] user_data Optional arbitrary user data that will be passed to @p pubkey_auth_clb. - * @param[in] free_user_data Optional callback that will be called during cleanup to free any @p user_data. - */ -void nc_server_ssh_set_pubkey_auth_clb(int (*pubkey_auth_clb)(const struct nc_session *session, ssh_key key, - void *user_data), void *user_data, void (*free_user_data)(void *user_data)); - -/** - * @brief Set the callback for retrieving host keys. Any RSA, DSA, and ECDSA keys can be added. However, - * a maximum of one key of each type will be used during SSH authentication, later keys replacing - * the earlier ones. - * - * @param[in] hostkey_clb Callback that should return the key itself. Zero return indicates success, non-zero - * an error. On success exactly ONE of @p privkey_path or @p privkey_data is expected - * to be set. The one set will be freed. - * - @p privkey_path expects a PEM file, - * - @p privkey_data expects a base-64 encoded ANS.1 DER data, - * - @p privkey_type type of the key in @p privkey_data. Use ::NC_SSH_KEY_UNKNOWN for - * PKCS#8 key that includes the information about the key in its data. - * @param[in] user_data Optional arbitrary user data that will be passed to @p hostkey_clb. - * @param[in] free_user_data Optional callback that will be called during cleanup to free any @p user_data. - */ -void nc_server_ssh_set_hostkey_clb(int (*hostkey_clb)(const char *name, void *user_data, char **privkey_path, - char **privkey_data, NC_SSH_KEY_TYPE *privkey_type), void *user_data, void (*free_user_data)(void *user_data)); - -/** - * @brief Add endpoint SSH host keys the server will identify itself with. Only the name is set, the key itself - * wil be retrieved using a callback. - * - * @param[in] endpt_name Existing endpoint name. - * @param[in] name Arbitrary name of the host key. - * @param[in] idx Optional index where to add the key. -1 adds at the end. - * @return 0 on success, -1 on error. - */ -int nc_server_ssh_endpt_add_hostkey(const char *endpt_name, const char *name, int16_t idx); - -/** - * @brief Delete endpoint SSH host key. Their order is preserved. - * - * @param[in] endpt_name Existing endpoint name. - * @param[in] name Name of the host key. NULL matches all the keys, but if @p idx != -1 then this must be NULL. - * @param[in] idx Index of the hostkey. -1 matches all indices, but if @p name != NULL then this must be -1. - * @return 0 on success, -1 on error. - */ -int nc_server_ssh_endpt_del_hostkey(const char *endpt_name, const char *name, int16_t idx); - -/** - * @brief Move endpoint SSH host key. - * - * @param[in] endpt_name Exisitng endpoint name. - * @param[in] key_mov Name of the host key that will be moved. - * @param[in] key_after Name of the key that will preceed @p key_mov. NULL if @p key_mov is to be moved at the beginning. - * @return 0 on success, -1 on error. - */ -int nc_server_ssh_endpt_mov_hostkey(const char *endpt_name, const char *key_mov, const char *key_after); - -/** - * @brief Modify endpoint SSH host key. - * - * @param[in] endpt_name Exisitng endpoint name. - * @param[in] name Name of an existing host key. - * @param[in] new_name New name of the host key @p name. - * @return 0 on success, -1 on error. - */ -int nc_server_ssh_endpt_mod_hostkey(const char *endpt_name, const char *name, const char *new_name); - -/** - * @brief Set endpoint accepted SSH authentication methods. All (publickey, password, interactive) - * are supported by default. - * - * @param[in] endpt_name Existing endpoint name. - * @param[in] auth_methods Accepted authentication methods bit field of NC_SSH_AUTH_TYPE. - * @return 0 on success, -1 on error. - */ -int nc_server_ssh_endpt_set_auth_methods(const char *endpt_name, int auth_methods); - -/** - * @brief Get endpoint accepted SSH authentication methods. - * - * @param[in] endpt_name Existing endpoint name. - * @return Accepted authentication methods bit field of NC_SSH_AUTH_TYPE. - */ -int nc_server_ssh_endpt_get_auth_methods(const char *endpt_name); - -/** - * @brief Set endpoint SSH authentication attempts of every client. 3 by default. - * - * @param[in] endpt_name Existing endpoint name. - * @param[in] auth_attempts Failed authentication attempts before a client is dropped. - * @return 0 on success, -1 on error. - */ -int nc_server_ssh_endpt_set_auth_attempts(const char *endpt_name, uint16_t auth_attempts); - -/** - * @brief Set endpoint SSH authentication timeout. 30 seconds by default. - * - * @param[in] endpt_name Existing endpoint name. - * @param[in] auth_timeout Number of seconds before an unauthenticated client is dropped. - * @return 0 on success, -1 on error. - */ -int nc_server_ssh_endpt_set_auth_timeout(const char *endpt_name, uint16_t auth_timeout); + ssh_session ssh_sess, ssh_message msg, void *user_data), void *user_data, void (*free_user_data)(void *user_data)); /** @} Server SSH */ -#endif /* NC_ENABLED_SSH */ - -#ifdef NC_ENABLED_TLS - /** * @defgroup server_tls Server TLS * @ingroup server @@ -746,165 +467,6 @@ int nc_server_ssh_endpt_set_auth_timeout(const char *endpt_name, uint16_t auth_t * @{ */ -/** - * @brief Set the server TLS certificate. Only the name is set, the certificate itself - * wil be retrieved using a callback. - * - * @param[in] endpt_name Existing endpoint name. - * @param[in] name Arbitrary certificate name. - * @return 0 on success, -1 on error. - */ -int nc_server_tls_endpt_set_server_cert(const char *endpt_name, const char *name); - -/** - * @brief Set the callback for retrieving server certificate and matching private key. - * - * @param[in] cert_clb Callback that should return the certificate and the key itself. Zero return indicates success, - * non-zero an error. On success exactly ONE of @p cert_path or @p cert_data and ONE of - * @p privkey_path and @p privkey_data is expected to be set. Those set will be freed. - * - @p cert_path expects a PEM file, - * - @p cert_data expects a base-64 encoded ASN.1 DER data, - * - @p privkey_path expects a PEM file, - * - @p privkey_data expects a base-64 encoded ANS.1 DER data, - * - @p privkey_type type of the key in @p privkey_data. - * @param[in] user_data Optional arbitrary user data that will be passed to @p cert_clb. - * @param[in] free_user_data Optional callback that will be called during cleanup to free any @p user_data. - */ -void nc_server_tls_set_server_cert_clb(int (*cert_clb)(const char *name, void *user_data, char **cert_path, char **cert_data, - char **privkey_path, char **privkey_data, NC_SSH_KEY_TYPE *privkey_type), void *user_data, - void (*free_user_data)(void *user_data)); - -/** - * @brief Set the callback for retrieving server certificate chain - * - * @param[in] cert_chain_clb Callback that should return all the certificates of the chain. Zero return indicates success, - * non-zero an error. On success, @p cert_paths and @p cert_data are expected to be set or left - * NULL. Both will be (deeply) freed. - * - @p cert_paths expect an array of PEM files, - * - @p cert_path_count number of @p cert_paths array members, - * - @p cert_data expect an array of base-64 encoded ASN.1 DER cert data, - * - @p cert_data_count number of @p cert_data array members. - * @param[in] user_data Optional arbitrary user data that will be passed to @p cert_clb. - * @param[in] free_user_data Optional callback that will be called during cleanup to free any @p user_data. - */ -void nc_server_tls_set_server_cert_chain_clb(int (*cert_chain_clb)(const char *name, void *user_data, char ***cert_paths, - int *cert_path_count, char ***cert_data, int *cert_data_count), void *user_data, void (*free_user_data)(void *user_data)); - -/** - * @brief Add a trusted certificate list. Can be both a CA or a client one. Can be - * safely used together with nc_server_tls_endpt_set_trusted_ca_paths(). - * - * @param[in] endpt_name Existing endpoint name. - * @param[in] name Arbitary name identifying this certificate list. - * @return 0 on success, -1 on error. - */ -int nc_server_tls_endpt_add_trusted_cert_list(const char *endpt_name, const char *name); - -/** - * @brief Set the callback for retrieving trusted certificates. - * - * @param[in] cert_list_clb Callback that should return all the certificates of a list. Zero return indicates success, - * non-zero an error. On success, @p cert_paths and @p cert_data are expected to be set or left - * NULL. Both will be (deeply) freed. - * - @p cert_paths expect an array of PEM files, - * - @p cert_path_count number of @p cert_paths array members, - * - @p cert_data expect an array of base-64 encoded ASN.1 DER cert data, - * - @p cert_data_count number of @p cert_data array members. - * @param[in] user_data Optional arbitrary user data that will be passed to @p cert_clb. - * @param[in] free_user_data Optional callback that will be called during cleanup to free any @p user_data. - */ -void nc_server_tls_set_trusted_cert_list_clb(int (*cert_list_clb)(const char *name, void *user_data, char ***cert_paths, - int *cert_path_count, char ***cert_data, int *cert_data_count), void *user_data, void (*free_user_data)(void *user_data)); - -/** - * @brief Remove a trusted certificate. - * - * @param[in] endpt_name Existing endpoint name. - * @param[in] name Name of the certificate list to delete. NULL deletes all the lists. - * @return 0 on success, -1 on not found. - */ -int nc_server_tls_endpt_del_trusted_cert_list(const char *endpt_name, const char *name); - -/** - * @brief Set trusted Certificate Authority certificate locations. There can only be - * one file and one directory, they are replaced if already set. Can be safely - * used with nc_server_tls_endpt_add_trusted_cert() or its _path variant. - * - * @param[in] endpt_name Existing endpoint name. - * @param[in] ca_file Path to a trusted CA cert store file in PEM format. Can be NULL. - * @param[in] ca_dir Path to a trusted CA cert store hashed directory (c_rehash utility - * can be used to create hashes) with PEM files. Can be NULL. - * @return 0 on success, -1 on error. - */ -int nc_server_tls_endpt_set_trusted_ca_paths(const char *endpt_name, const char *ca_file, const char *ca_dir); - -/** - * @brief Set Certificate Revocation List locations. There can only be one file - * and one directory, they are replaced if already set. - * - * @param[in] endpt_name Existing endpoint name. - * @param[in] crl_file Path to a CRL store file in PEM format. Can be NULL. - * @param[in] crl_dir Path to a CRL store hashed directory (c_rehash utility - * can be used to create hashes) with PEM files. Can be NULL. - * @return 0 on success, -1 on error. - */ -int nc_server_tls_endpt_set_crl_paths(const char *endpt_name, const char *crl_file, const char *crl_dir); - -/** - * @brief Destroy and clean CRLs. Certificates, private keys, and CTN entries are - * not affected. - * - * @param[in] endpt_name Existing endpoint name. - */ -void nc_server_tls_endpt_clear_crls(const char *endpt_name); - -/** - * @brief Add a cert-to-name entry. - * - * It is possible to add an entry step-by-step, specifying first only @p ip and in later calls - * @p fingerprint, @p map_type, and optionally @p name spearately. - * - * @param[in] endpt_name Existing endpoint name. - * @param[in] id Priority of the entry. It must be unique. If already exists, the entry with this id - * is modified. - * @param[in] fingerprint Matching certificate fingerprint. If NULL, kept temporarily unset. - * @param[in] map_type Type of username-certificate mapping. If 0, kept temporarily unset. - * @param[in] name Specific username used only if @p map_type == NC_TLS_CTN_SPECIFED. - * @return 0 on success, -1 on error. - */ -int nc_server_tls_endpt_add_ctn(const char *endpt_name, uint32_t id, const char *fingerprint, - NC_TLS_CTN_MAPTYPE map_type, const char *name); - -/** - * @brief Remove a cert-to-name entry. - * - * @param[in] endpt_name Existing endpoint name. - * @param[in] id Priority of the entry. -1 matches all the priorities. - * @param[in] fingerprint Fingerprint fo the entry. NULL matches all the fingerprints. - * @param[in] map_type Mapping type of the entry. 0 matches all the mapping types. - * @param[in] name Specific username for the entry. NULL matches all the usernames. - * @return 0 on success, -1 on not finding any match. - */ -int nc_server_tls_endpt_del_ctn(const char *endpt_name, int64_t id, const char *fingerprint, - NC_TLS_CTN_MAPTYPE map_type, const char *name); - -/** - * @brief Get a cert-to-name entry. - * - * If a parameter is NULL, it is ignored. If its dereferenced value is NULL, - * it is filled and returned. If the value is set, it is used as a filter. - * Returns first matching entry. - * - * @param[in] endpt_name Existing endpoint name. - * @param[in,out] id Priority of the entry. - * @param[in,out] fingerprint Fingerprint fo the entry. - * @param[in,out] map_type Mapping type of the entry. - * @param[in,out] name Specific username for the entry. - * @return 0 on success, -1 on not finding any match. - */ -int nc_server_tls_endpt_get_ctn(const char *endpt_name, uint32_t *id, char **fingerprint, NC_TLS_CTN_MAPTYPE *map_type, - char **name); - /** * @brief Get client certificate. * @@ -926,7 +488,7 @@ void nc_server_tls_set_verify_clb(int (*verify_clb)(const struct nc_session *ses /** @} Server TLS */ -#endif /* NC_ENABLED_TLS */ +#endif /* NC_ENABLED_SSH_TLS */ /** * @addtogroup server_session @@ -939,7 +501,7 @@ void nc_server_tls_set_verify_clb(int (*verify_clb)(const struct nc_session *ses * @param[in] session Session to get the information from. * @return Session start time. */ -time_t nc_session_get_start_time(const struct nc_session *session); +struct timespec nc_session_get_start_time(const struct nc_session *session); /** * @brief Increase session notification subscription flag count. diff --git a/src/session_server_ch.h b/src/session_server_ch.h index e8c508ba..72191697 100644 --- a/src/session_server_ch.h +++ b/src/session_server_ch.h @@ -22,11 +22,12 @@ extern "C" { #include #include +#include #include "netconf.h" #include "session.h" -#if defined (NC_ENABLED_SSH) || defined (NC_ENABLED_TLS) +#ifdef NC_ENABLED_SSH_TLS /** * @defgroup server_ch Server-side Call Home @@ -36,21 +37,15 @@ extern "C" { * @{ */ -/** - * @brief Add a new Call Home client. - * - * @param[in] name Arbitrary unique client name. - * @return 0 on success, -1 on error. - */ -int nc_server_ch_add_client(const char *name); +/** @} Server-side Call Home */ /** - * @brief Drop any connections, stop connecting and remove a client. + * @defgroup server_ch_functions Server-side Call Home Functions + * @ingroup server_ch * - * @param[in] name Client name. NULL matches all the clients. - * @return 0 on success, -1 on not finding any match. + * @brief Server-side Call Home functions. + * @{ */ -int nc_server_ch_del_client(const char *name); /** * @brief Check if a Call Home client exists. @@ -60,28 +55,6 @@ int nc_server_ch_del_client(const char *name); */ int nc_server_ch_is_client(const char *name); -/** - * @brief Add a new Call Home client endpoint. - * - * @param[in] client_name Existing client name. - * @param[in] endpt_name Arbitrary unique (within the client) endpoint name. - * @param[in] ti Transport protocol to use. - * @return 0 on success, -1 on error. - */ -int nc_server_ch_client_add_endpt(const char *client_name, const char *endpt_name, NC_TRANSPORT_IMPL ti); - -/** - * @brief Remove a Call Home client endpoint. - * - * @param[in] client_name Existing client name. - * @param[in] endpt_name Existing endpoint of @p client_name. NULL matches all endpoints. - * @param[in] ti Client transport protocol. NULL matches any protocol. - * Redundant to set if @p endpt_name is set, client names are - * unique disregarding their protocol. - * @return 0 on success, -1 on error. - */ -int nc_server_ch_client_del_endpt(const char *client_name, const char *endpt_name, NC_TRANSPORT_IMPL ti); - /** * @brief Check if an endpoint of a Call Home client exists. * @@ -91,107 +64,6 @@ int nc_server_ch_client_del_endpt(const char *client_name, const char *endpt_nam */ int nc_server_ch_client_is_endpt(const char *client_name, const char *endpt_name); -/** - * @brief Change Call Home client endpoint listening address. - * - * On error the previous listening socket (if any) is left untouched. - * - * @param[in] client_name Existing Call Home client name. - * @param[in] endpt_name Existing endpoint name of @p client_name. - * @param[in] address New listening address. - * @return 0 on success, -1 on error. - */ -int nc_server_ch_client_endpt_set_address(const char *client_name, const char *endpt_name, const char *address); - -/** - * @brief Change Call Home client endpoint listening port. - * - * On error the previous listening socket (if any) is left untouched. - * - * @param[in] client_name Existing Call Home client name. - * @param[in] endpt_name Existing endpoint name of @p client_name. - * @param[in] port New listening port. - * @return 0 on success, -1 on error. - */ -int nc_server_ch_client_endpt_set_port(const char *client_name, const char *endpt_name, uint16_t port); - -/** - * @brief Change Call Home client endpoint keepalives state. Affects only new connections. - * - * @param[in] client_name Existing Call Home client name. - * @param[in] endpt_name Existing endpoint name of @p client_name. - * @param[in] enable Whether to enable or disable keepalives. - * @return 0 on success, -1 on error. - */ -int nc_server_ch_client_endpt_enable_keepalives(const char *client_name, const char *endpt_name, int enable); - -/** - * @brief Change Call Home client endpoint keepalives parameters. Affects only new connections. - * - * @param[in] client_name Existing Call Home client name. - * @param[in] endpt_name Existing endpoint name of @p client_name. - * @param[in] idle_time Keepalive idle time in seconds, 1 by default, -1 to keep previous value. - * @param[in] max_probes Keepalive max probes sent, 10 by default, -1 to keep previous value. - * @param[in] probe_interval Keepalive probe interval in seconds, 5 by default, -1 to keep previous value. - * @return 0 on success, -1 on error. - */ -int nc_server_ch_client_endpt_set_keepalives(const char *client_name, const char *endpt_name, int idle_time, - int max_probes, int probe_interval); - -/** - * @brief Set Call Home client connection type. - * - * @param[in] client_name Existing Call Home client name. - * @param[in] conn_type Call Home connection type. - * @return 0 on success, -1 on error. - */ -int nc_server_ch_client_set_conn_type(const char *client_name, NC_CH_CONN_TYPE conn_type); - -/** - * @brief Set Call Home client periodic connection period for reconnecting. - * - * @param[in] client_name Existing Call Home client name. - * @param[in] period Call Home periodic connection period in minutes. - * @return 0 on success, -1 on error. - */ -int nc_server_ch_client_periodic_set_period(const char *client_name, uint16_t period); - -/** - * @brief Set Call Home client periodic connection period anchor time. - * - * @param[in] client_name Existing Call Home client name. - * @param[in] anchor_time Call Home periodic connection anchor time for the period. - * @return 0 on success, -1 on error. - */ -int nc_server_ch_client_periodic_set_anchor_time(const char *client_name, time_t anchor_time); - -/** - * @brief Set Call Home client periodic connection idle timeout. - * - * @param[in] client_name Existing Call Home client name. - * @param[in] idle_timeout Call Home periodic idle timeout. - * @return 0 on success, -1 on error. - */ -int nc_server_ch_client_periodic_set_idle_timeout(const char *client_name, uint16_t idle_timeout); - -/** - * @brief Set Call Home client start-with policy. - * - * @param[in] client_name Existing Call Home client name. - * @param[in] start_with Call Home client start-with. - * @return 0 on success, -1 on error. - */ -int nc_server_ch_client_set_start_with(const char *client_name, NC_CH_START_WITH start_with); - -/** - * @brief Set Call Home client overall max attempts. - * - * @param[in] client_name Existing Call Home client name. - * @param[in] max_attempts Call Home overall max reconnect attempts. - * @return 0 on success, -1 on error. - */ -int nc_server_ch_client_set_max_attempts(const char *client_name, uint8_t max_attempts); - /** * @brief Callback for getting a locked context for new Call Home sessions. * @@ -213,10 +85,11 @@ typedef void (*nc_server_ch_session_release_ctx_cb)(void *cb_data); * * @param[in] client_name Name of the CH client which established the session. * @param[in] new_session New established CH session, the pointer is internally discarded afterwards. + * @param[in] user_data Arbitrary new session callback data. * @return 0 on success; * @return non-zero on error and @p new_session is freed. */ -typedef int (*nc_server_ch_new_session_cb)(const char *client_name, struct nc_session *new_session); +typedef int (*nc_server_ch_new_session_cb)(const char *client_name, struct nc_session *new_session, void *user_data); /** * @brief Dispatch a thread connecting to a listening NETCONF client and creating Call Home sessions. @@ -226,237 +99,31 @@ typedef int (*nc_server_ch_new_session_cb)(const char *client_name, struct nc_se * @param[in] release_ctx_cb Callback for releasing session context. * @param[in] ctx_cb_data Arbitrary user data passed to @p acquire_ctx_cb and @p release_ctx_cb. * @param[in] new_session_cb Callback called for every established session on the client. + * @param[in] new_session_cb_data Arbitrary user data passed to @p new_session_cb. * @return 0 if the thread was successfully created, -1 on error. */ int nc_connect_ch_client_dispatch(const char *client_name, nc_server_ch_session_acquire_ctx_cb acquire_ctx_cb, - nc_server_ch_session_release_ctx_cb release_ctx_cb, void *ctx_cb_data, nc_server_ch_new_session_cb new_session_cb); - -/** @} Server-side Call Home */ - -#endif /* NC_ENABLED_SSH || NC_ENABLED_TLS */ - -#ifdef NC_ENABLED_SSH + nc_server_ch_session_release_ctx_cb release_ctx_cb, void *ctx_cb_data, nc_server_ch_new_session_cb new_session_cb, + void *new_session_cb_data); /** - * @defgroup server_ch_ssh Server-side Call Home on SSH - * @ingroup server_ch + * @brief Set callbacks and their data for Call Home threads. * - * @brief SSH settings for the Call Home functionality - * @{ - */ - -/** - * @brief Add Call Home SSH host keys the server will identify itself with. Only the name is set, the key itself - * wil be retrieved using a callback. - * - * @param[in] client_name Existing Call Home client name. - * @param[in] endpt_name Existing endpoint name of the client. - * @param[in] name Arbitrary name of the host key. - * @param[in] idx Optional index where to add the key. -1 adds at the end. - * @return 0 on success, -1 on error. - */ -int nc_server_ssh_ch_client_endpt_add_hostkey(const char *client_name, const char *endpt_name, const char *name, int16_t idx); - -/** - * @brief Delete Call Home SSH host keys. Their order is preserved. + * If set, Call Home threads will be dispatched automatically upon creation of new Call Home clients. * - * @param[in] client_name Existing Call Home client name. - * @param[in] endpt_name Existing endpoint name of the client. - * @param[in] name Name of the host key. NULL matches all the keys, but if @p idx != -1 then this must be NULL. - * @param[in] idx Index of the hostkey. -1 matches all indices, but if @p name != NULL then this must be -1. - * @return 0 on success, -1 on error. - */ -int nc_server_ssh_ch_client_endpt_del_hostkey(const char *client_name, const char *endpt_name, const char *name, int16_t idx); - -/** - * @brief Move Call Home SSH host key. - * - * @param[in] client_name Exisitng Call Home client name. - * @param[in] endpt_name Existing endpoint name of the client. - * @param[in] key_mov Name of the host key that will be moved. - * @param[in] key_after Name of the key that will preceed @p key_mov. NULL if @p key_mov is to be moved at the beginning. - * @return 0 in success, -1 on error. - */ -int nc_server_ssh_ch_client_endpt_mov_hostkey(const char *client_name, const char *endpt_name, const char *key_mov, - const char *key_after); - -/** - * @brief Set accepted Call Home SSH authentication methods. All (publickey, password, interactive) - * are supported by default. - * - * @param[in] client_name Existing Call Home client name. - * @param[in] endpt_name Existing endpoint name of the client. - * @param[in] auth_methods Accepted authentication methods bit field of NC_SSH_AUTH_TYPE. - * @return 0 on success, -1 on error. - */ -int nc_server_ssh_ch_client_endpt_set_auth_methods(const char *client_name, const char *endpt_name, int auth_methods); - -/** - * @brief Get accepted Call Home SSH authentication methods. - * - * @param[in] client_name Existing Call Home client name. - * @param[in] endpt_name Existing endpoint name of the client. - * @return Accepted authentication methods bit field of NC_SSH_AUTH_TYPE. - */ -int nc_server_ssh_ch_client_endpt_get_auth_methods(const char *client_name, const char *endpt_name); - -/** - * @brief Set Call Home SSH authentication attempts of every client. 3 by default. - * - * @param[in] client_name Existing Call Home client name. - * @param[in] endpt_name Existing endpoint name of the client. - * @param[in] auth_attempts Failed authentication attempts before a client is dropped. - * @return 0 on success, -1 on error. - */ -int nc_server_ssh_ch_client_endpt_set_auth_attempts(const char *client_name, const char *endpt_name, uint16_t auth_attempts); - -/** - * @brief Set Call Home SSH authentication timeout. 30 seconds by default. - * - * @param[in] client_name Existing Call Home client name. - * @param[in] endpt_name Existing endpoint name of the client. - * @param[in] auth_timeout Number of seconds before an unauthenticated client is dropped. - * @return 0 on success, -1 on error. - */ -int nc_server_ssh_ch_client_endpt_set_auth_timeout(const char *client_name, const char *endpt_name, uint16_t auth_timeout); - -/** @} Server-side Call Home on SSH */ - -#endif /* NC_ENABLED_SSH */ - -#ifdef NC_ENABLED_TLS - -/** - * @defgroup server_ch_tls Server-side Call Home on TLS - * @ingroup server_ch - * - * @brief TLS settings for the Call Home functionality - * @{ - */ - -/** - * @brief Set the server Call Home TLS certificate. Only the name is set, the certificate itself - * wil be retrieved using a callback. - * - * @param[in] client_name Existing Call Home client name. - * @param[in] endpt_name Existing endpoint name of the client. - * @param[in] name Arbitrary certificate name. - * @return 0 on success, -1 on error. - */ -int nc_server_tls_ch_client_endpt_set_server_cert(const char *client_name, const char *endpt_name, const char *name); - -/** - * @brief Add a Call Home trusted certificate list. Can be both a CA or a client one. - * - * @param[in] client_name Existing Call Home client name. - * @param[in] endpt_name Existing endpoint name of the client. - * @param[in] name Arbitary name identifying this certificate list. - * @return 0 on success, -1 on error. - */ -int nc_server_tls_ch_client_endpt_add_trusted_cert_list(const char *client_name, const char *endpt_name, const char *name); - -/** - * @brief Remove a set Call Home trusted certificate list. CRLs and CTN entries are not affected. - * - * @param[in] client_name Existing Call Home client name. - * @param[in] endpt_name Existing endpoint name of the client. - * @param[in] name Name of the certificate list to delete. NULL deletes all the lists. - * @return 0 on success, -1 on not found. - */ -int nc_server_tls_ch_client_endpt_del_trusted_cert_list(const char *client_name, const char *endpt_name, const char *name); - -/** - * @brief Set trusted Call Home Certificate Authority certificate locations. There - * can only be one file and one directory, they are replaced if already set. - * - * @param[in] client_name Existing Call Home client name. - * @param[in] endpt_name Existing endpoint name of the client. - * @param[in] ca_file Path to a trusted CA cert store file in PEM format. - * Can be NULL. - * @param[in] ca_dir Path to a trusted CA cert store hashed directory - * (c_rehash utility can be used to create hashes) - * with PEM files. Can be NULL. - * @return 0 on success, -1 on error. - */ -int nc_server_tls_ch_client_endpt_set_trusted_ca_paths(const char *client_name, const char *endpt_name, const char *ca_file, - const char *ca_dir); - -/** - * @brief Set Call Home Certificate Revocation List locations. There can only be - * one file and one directory, they are replaced if already set. - * - * @param[in] client_name Existing Call Home client name. - * @param[in] endpt_name Existing endpoint name of the client. - * @param[in] crl_file Path to a CRL store file in PEM format. Can be NULL. - * @param[in] crl_dir Path to a CRL store hashed directory (c_rehash utility - * can be used to create hashes) with PEM files. Can be NULL. - * @return 0 on success, -1 on error. - */ -int nc_server_tls_ch_client_endpt_set_crl_paths(const char *client_name, const char *endpt_name, const char *crl_file, - const char *crl_dir); - -/** - * @brief Destroy and clean Call Home CRLs. Call Home certificates, private keys, - * and CTN entries are not affected. - * - * @param[in] client_name Existing Call Home client name. - * @param[in] endpt_name Existing endpoint name of the client. - */ -void nc_server_tls_ch_client_endpt_clear_crls(const char *client_name, const char *endpt_name); - -/** - * @brief Add a cert-to-name entry. - * - * It is possible to add an entry step-by-step, specifying first only @p ip and in later calls - * @p fingerprint, @p map_type, and optionally @p name spearately. - * - * @param[in] client_name Existing Call Home client name. - * @param[in] endpt_name Existing endpoint name of the client. - * @param[in] id Priority of the entry. It must be unique. If already exists, the entry with this id - * is modified. - * @param[in] fingerprint Matching certificate fingerprint. If NULL, kept temporarily unset. - * @param[in] map_type Type of username-certificate mapping. If 0, kept temporarily unset. - * @param[in] name Specific username used only if @p map_type == NC_TLS_CTN_SPECIFED. - * @return 0 on success, -1 on error. - */ -int nc_server_tls_ch_client_endpt_add_ctn(const char *client_name, const char *endpt_name, uint32_t id, - const char *fingerprint, NC_TLS_CTN_MAPTYPE map_type, const char *name); - -/** - * @brief Remove a Call Home cert-to-name entry. - * - * @param[in] client_name Existing Call Home client name. - * @param[in] endpt_name Existing endpoint name of the client. - * @param[in] id Priority of the entry. -1 matches all the priorities. - * @param[in] fingerprint Fingerprint fo the entry. NULL matches all the fingerprints. - * @param[in] map_type Mapping type of the entry. 0 matches all the mapping types. - * @param[in] name Specific username for the entry. NULL matches all the usernames. - * @return 0 on success, -1 on not finding any match. - */ -int nc_server_tls_ch_client_endpt_del_ctn(const char *client_name, const char *endpt_name, int64_t id, - const char *fingerprint, NC_TLS_CTN_MAPTYPE map_type, const char *name); - -/** - * @brief Get a Call Home cert-to-name entry. - * - * If a parameter is NULL, it is ignored. If its dereferenced value is NULL, - * it is filled and returned. If the value is set, it is used as a filter. - * Returns first matching entry. - * - * @param[in] client_name Existing Call Home client name. - * @param[in] endpt_name Existing endpoint name of the client. - * @param[in,out] id Priority of the entry. - * @param[in,out] fingerprint Fingerprint fo the entry. - * @param[in,out] map_type Mapping type of the entry. - * @param[in,out] name Specific username for the entry. - * @return 0 on success, -1 on not finding any match. + * @param[in] acquire_ctx_cb Callback for acquiring new session context. + * @param[in] release_ctx_cb Callback for releasing session context. + * @param[in] ctx_cb_data Arbitrary user data passed to @p acquire_ctx_cb and @p release_ctx_cb. + * @param[in] new_session_cb Callback called for every established Call Home session. + * @param[in] new_session_cb_data Arbitrary user data passed to @p new_session_cb. */ -int nc_server_tls_ch_client_endpt_get_ctn(const char *client_name, const char *endpt_name, uint32_t *id, char **fingerprint, - NC_TLS_CTN_MAPTYPE *map_type, char **name); +void nc_server_ch_set_dispatch_data(nc_server_ch_session_acquire_ctx_cb acquire_ctx_cb, + nc_server_ch_session_release_ctx_cb release_ctx_cb, void *ctx_cb_data, nc_server_ch_new_session_cb new_session_cb, + void *new_session_cb_data); -/** @} Server-side Call Home on TLS */ +/** @} Server-side Call Home Functions */ -#endif /* NC_ENABLED_TLS */ +#endif /* NC_ENABLED_SSH_TLS */ #ifdef __cplusplus } diff --git a/src/session_server_ssh.c b/src/session_server_ssh.c index c6b744d9..77953c61 100644 --- a/src/session_server_ssh.c +++ b/src/session_server_ssh.c @@ -15,21 +15,23 @@ #define _GNU_SOURCE -#include "config.h" /* Expose HAVE_SHADOW and HAVE_CRYPT */ +#include "config.h" /* Expose HAVE_LIBPAM */ -#ifdef HAVE_SHADOW - #include -#endif -#ifdef HAVE_CRYPT - #include -#endif #ifdef HAVE_LIBPAM - #include +# include #endif +#include +#include #include -#include +#include +#include +#include +#include +#include +#include #include +#include #include #include #include @@ -38,27 +40,22 @@ #include #include "compat.h" -#include "libnetconf.h" -#include "session_server.h" -#include "session_server_ch.h" - -#if !defined (HAVE_CRYPT_R) -pthread_mutex_t crypt_lock = PTHREAD_MUTEX_INITIALIZER; -#endif +#include "log_p.h" +#include "session.h" +#include "session_p.h" extern struct nc_server_opts server_opts; static char * -base64der_key_to_tmp_file(const char *in, const char *key_str) +base64der_privkey_to_tmp_file(const char *in, const char *privkey_format) { char path[12] = "/tmp/XXXXXX"; int fd, written; + unsigned len; mode_t umode; FILE *file; - if (in == NULL) { - return NULL; - } + NC_CHECK_ARG_RET(NULL, in, NULL); umode = umask(0177); fd = mkstemp(path); @@ -73,865 +70,111 @@ base64der_key_to_tmp_file(const char *in, const char *key_str) return NULL; } - /* write the key into the file */ - if (key_str) { - written = fwrite("-----BEGIN ", 1, 11, file); - written += fwrite(key_str, 1, strlen(key_str), file); + /* write header */ + written = fwrite("-----BEGIN ", 1, 11, file); + if (privkey_format) { + written += fwrite(privkey_format, 1, strlen(privkey_format), file); written += fwrite(" PRIVATE KEY-----\n", 1, 18, file); - written += fwrite(in, 1, strlen(in), file); - written += fwrite("\n-----END ", 1, 10, file); - written += fwrite(key_str, 1, strlen(key_str), file); - written += fwrite(" PRIVATE KEY-----", 1, 17, file); - - fclose(file); - if ((unsigned)written != 11 + strlen(key_str) + 18 + strlen(in) + 10 + strlen(key_str) + 17) { - unlink(path); - return NULL; - } } else { - written = fwrite("-----BEGIN PRIVATE KEY-----\n", 1, 28, file); - written += fwrite(in, 1, strlen(in), file); - written += fwrite("\n-----END PRIVATE KEY-----", 1, 26, file); - - fclose(file); - if ((unsigned)written != 28 + strlen(in) + 26) { - unlink(path); - return NULL; - } + written += fwrite("PRIVATE KEY-----\n", 1, 17, file); } - return strdup(path); -} + /* write data */ + written += fwrite(in, 1, strlen(in), file); -static int -nc_server_ssh_add_hostkey(const char *name, int16_t idx, struct nc_server_ssh_opts *opts) -{ - uint8_t i; - - if (!name) { - ERRARG("name"); - return -1; - } else if (idx > opts->hostkey_count) { - ERRARG("idx"); - return -1; - } - - for (i = 0; i < opts->hostkey_count; ++i) { - if (!strcmp(opts->hostkeys[i], name)) { - ERRARG("name"); - return -1; - } - } - - ++opts->hostkey_count; - opts->hostkeys = nc_realloc(opts->hostkeys, opts->hostkey_count * sizeof *opts->hostkeys); - if (!opts->hostkeys) { - ERRMEM; - return -1; - } - - if (idx < 0) { - idx = opts->hostkey_count - 1; - } - if (idx != opts->hostkey_count - 1) { - memmove(opts->hostkeys + idx + 1, opts->hostkeys + idx, opts->hostkey_count - idx); - } - opts->hostkeys[idx] = strdup(name); - - return 0; -} - -API int -nc_server_ssh_endpt_add_hostkey(const char *endpt_name, const char *name, int16_t idx) -{ - int ret; - struct nc_endpt *endpt; - - /* LOCK */ - endpt = nc_server_endpt_lock_get(endpt_name, NC_TI_LIBSSH, NULL); - if (!endpt) { - return -1; - } - - ret = nc_server_ssh_add_hostkey(name, idx, endpt->opts.ssh); - - /* UNLOCK */ - pthread_rwlock_unlock(&server_opts.endpt_lock); - - return ret; -} - -API void -nc_server_ssh_set_passwd_auth_clb(int (*passwd_auth_clb)(const struct nc_session *session, const char *password, void *user_data), - void *user_data, void (*free_user_data)(void *user_data)) -{ - server_opts.passwd_auth_clb = passwd_auth_clb; - server_opts.passwd_auth_data = user_data; - server_opts.passwd_auth_data_free = free_user_data; -} - -API void -nc_server_ssh_set_interactive_auth_sess_clb(int (*interactive_auth_sess_clb)(const struct nc_session *session, - ssh_session ssh_sess, ssh_message msg, void *user_data), void *user_data, void (*free_user_data)(void *user_data)) -{ - server_opts.interactive_auth_sess_clb = interactive_auth_sess_clb; - server_opts.interactive_auth_sess_data = user_data; - server_opts.interactive_auth_sess_data_free = free_user_data; -} - -API void -nc_server_ssh_set_interactive_auth_clb(int (*interactive_auth_clb)(const struct nc_session *session, ssh_message msg, void *user_data), - void *user_data, void (*free_user_data)(void *user_data)) -{ - server_opts.interactive_auth_clb = interactive_auth_clb; - server_opts.interactive_auth_data = user_data; - server_opts.interactive_auth_data_free = free_user_data; -} - -API int -nc_server_ssh_set_pam_conf_path(const char *conf_name, const char *conf_dir) -{ -#ifdef HAVE_LIBPAM - free(server_opts.conf_name); - free(server_opts.conf_dir); - server_opts.conf_name = NULL; - server_opts.conf_dir = NULL; - - if (conf_dir) { -# ifdef LIBPAM_HAVE_CONFDIR - server_opts.conf_dir = strdup(conf_dir); - if (!(server_opts.conf_dir)) { - ERRMEM; - return -1; - } -# else - ERR(NULL, "Failed to set PAM config directory because of old version of PAM. " - "Put the config file in the system directory (usually /etc/pam.d/)."); - return -1; -# endif - } - - if (conf_name) { - server_opts.conf_name = strdup(conf_name); - if (!(server_opts.conf_name)) { - ERRMEM; - return -1; - } - } - - return 0; -#else - (void)conf_name; - (void)conf_dir; - - ERR(NULL, "PAM-based SSH authentication is not supported."); - return -1; -#endif -} - -API void -nc_server_ssh_set_pubkey_auth_clb(int (*pubkey_auth_clb)(const struct nc_session *session, ssh_key key, void *user_data), - void *user_data, void (*free_user_data)(void *user_data)) -{ - server_opts.pubkey_auth_clb = pubkey_auth_clb; - server_opts.pubkey_auth_data = user_data; - server_opts.pubkey_auth_data_free = free_user_data; -} - -API int -nc_server_ssh_ch_client_endpt_add_hostkey(const char *client_name, const char *endpt_name, const char *name, int16_t idx) -{ - int ret; - struct nc_ch_client *client; - struct nc_ch_endpt *endpt; - - /* LOCK */ - endpt = nc_server_ch_client_lock(client_name, endpt_name, NC_TI_LIBSSH, &client); - if (!endpt) { - return -1; - } - - ret = nc_server_ssh_add_hostkey(name, idx, endpt->opts.ssh); - - /* UNLOCK */ - nc_server_ch_client_unlock(client); - - return ret; -} - -API void -nc_server_ssh_set_hostkey_clb(int (*hostkey_clb)(const char *name, void *user_data, char **privkey_path, - char **privkey_data, NC_SSH_KEY_TYPE *privkey_type), void *user_data, void (*free_user_data)(void *user_data)) -{ - if (!hostkey_clb) { - ERRARG("hostkey_clb"); - return; - } - - server_opts.hostkey_clb = hostkey_clb; - server_opts.hostkey_data = user_data; - server_opts.hostkey_data_free = free_user_data; -} - -static int -nc_server_ssh_del_hostkey(const char *name, int16_t idx, struct nc_server_ssh_opts *opts) -{ - uint8_t i; - - if (name && (idx > -1)) { - ERRARG("name and idx"); - return -1; - } else if (idx >= opts->hostkey_count) { - ERRARG("idx"); - } - - if (!name && (idx < 0)) { - for (i = 0; i < opts->hostkey_count; ++i) { - free(opts->hostkeys[i]); - } - free(opts->hostkeys); - opts->hostkeys = NULL; - opts->hostkey_count = 0; - } else if (name) { - for (i = 0; i < opts->hostkey_count; ++i) { - if (!strcmp(opts->hostkeys[i], name)) { - idx = i; - goto remove_idx; - } - } - - ERRARG("name"); - return -1; + /* write footer */ + written += fwrite("\n-----END ", 1, 10, file); + if (privkey_format) { + written += fwrite(privkey_format, 1, strlen(privkey_format), file); + written += fwrite(" PRIVATE KEY-----", 1, 17, file); } else { -remove_idx: - --opts->hostkey_count; - free(opts->hostkeys[idx]); - if (idx < opts->hostkey_count - 1) { - memmove(opts->hostkeys + idx, opts->hostkeys + idx + 1, (opts->hostkey_count - idx) * sizeof *opts->hostkeys); - } - if (!opts->hostkey_count) { - free(opts->hostkeys); - opts->hostkeys = NULL; - } - } - - return 0; -} - -API int -nc_server_ssh_endpt_del_hostkey(const char *endpt_name, const char *name, int16_t idx) -{ - int ret; - struct nc_endpt *endpt; - - /* LOCK */ - endpt = nc_server_endpt_lock_get(endpt_name, NC_TI_LIBSSH, NULL); - if (!endpt) { - return -1; - } - - ret = nc_server_ssh_del_hostkey(name, idx, endpt->opts.ssh); - - /* UNLOCK */ - pthread_rwlock_unlock(&server_opts.endpt_lock); - - return ret; -} - -API int -nc_server_ssh_ch_client_endpt_del_hostkey(const char *client_name, const char *endpt_name, const char *name, int16_t idx) -{ - int ret; - struct nc_ch_client *client; - struct nc_ch_endpt *endpt; - - /* LOCK */ - endpt = nc_server_ch_client_lock(client_name, endpt_name, NC_TI_LIBSSH, &client); - if (!endpt) { - return -1; - } - - ret = nc_server_ssh_del_hostkey(name, idx, endpt->opts.ssh); - - /* UNLOCK */ - nc_server_ch_client_unlock(client); - - return ret; -} - -static int -nc_server_ssh_mov_hostkey(const char *key_mov, const char *key_after, struct nc_server_ssh_opts *opts) -{ - uint8_t i; - int16_t mov_idx = -1, after_idx = -1; - char *bckup; - - if (!key_mov) { - ERRARG("key_mov"); - return -1; - } - - for (i = 0; i < opts->hostkey_count; ++i) { - if (key_after && (after_idx == -1) && !strcmp(opts->hostkeys[i], key_after)) { - after_idx = i; - } - if ((mov_idx == -1) && !strcmp(opts->hostkeys[i], key_mov)) { - mov_idx = i; - } - - if ((!key_after || (after_idx > -1)) && (mov_idx > -1)) { - break; - } + written += fwrite("PRIVATE KEY-----", 1, 16, file); } - if (key_after && (after_idx == -1)) { - ERRARG("key_after"); - return -1; - } - if (mov_idx == -1) { - ERRARG("key_mov"); - return -1; - } - if ((mov_idx == after_idx) || (mov_idx == after_idx + 1)) { - /* nothing to do */ - return 0; - } + fclose(file); - /* finally move the key */ - bckup = opts->hostkeys[mov_idx]; - if (mov_idx > after_idx) { - memmove(opts->hostkeys + after_idx + 2, opts->hostkeys + after_idx + 1, - ((mov_idx - after_idx) - 1) * sizeof *opts->hostkeys); - opts->hostkeys[after_idx + 1] = bckup; + /* checksum */ + if (privkey_format) { + len = 11 + strlen(privkey_format) + 18 + strlen(in) + 10 + strlen(privkey_format) + 17; } else { - memmove(opts->hostkeys + mov_idx, opts->hostkeys + mov_idx + 1, (after_idx - mov_idx) * sizeof *opts->hostkeys); - opts->hostkeys[after_idx] = bckup; - } - - return 0; -} - -API int -nc_server_ssh_endpt_mov_hostkey(const char *endpt_name, const char *key_mov, const char *key_after) -{ - int ret; - struct nc_endpt *endpt; - - /* LOCK */ - endpt = nc_server_endpt_lock_get(endpt_name, NC_TI_LIBSSH, NULL); - if (!endpt) { - return -1; - } - - ret = nc_server_ssh_mov_hostkey(key_mov, key_after, endpt->opts.ssh); - - /* UNLOCK */ - pthread_rwlock_unlock(&server_opts.endpt_lock); - - return ret; -} - -API int -nc_server_ssh_ch_client_endpt_mov_hostkey(const char *client_name, const char *endpt_name, const char *key_mov, - const char *key_after) -{ - int ret; - struct nc_ch_client *client; - struct nc_ch_endpt *endpt; - - /* LOCK */ - endpt = nc_server_ch_client_lock(client_name, endpt_name, NC_TI_LIBSSH, &client); - if (!endpt) { - return -1; - } - - ret = nc_server_ssh_mov_hostkey(key_mov, key_after, endpt->opts.ssh); - - /* UNLOCK */ - nc_server_ch_client_unlock(client); - - return ret; -} - -static int -nc_server_ssh_set_auth_methods(int auth_methods, struct nc_server_ssh_opts *opts) -{ - if ((auth_methods & NC_SSH_AUTH_INTERACTIVE) && !server_opts.conf_name && !server_opts.interactive_auth_clb && - !server_opts.interactive_auth_sess_clb) { - /* path to a configuration file not set */ - ERR(NULL, "To use Keyboard-Interactive authentication method, set the PAM configuration file or a callback."); - return 1; - } - opts->auth_methods = auth_methods; - return 0; -} - -API int -nc_server_ssh_endpt_set_auth_methods(const char *endpt_name, int auth_methods) -{ - int ret; - struct nc_endpt *endpt; - - /* LOCK */ - endpt = nc_server_endpt_lock_get(endpt_name, NC_TI_LIBSSH, NULL); - if (!endpt) { - return -1; - } - - ret = nc_server_ssh_set_auth_methods(auth_methods, endpt->opts.ssh); - - /* UNLOCK */ - pthread_rwlock_unlock(&server_opts.endpt_lock); - - return ret; -} - -API int -nc_server_ssh_ch_client_endpt_set_auth_methods(const char *client_name, const char *endpt_name, int auth_methods) -{ - int ret; - struct nc_ch_client *client; - struct nc_ch_endpt *endpt; - - /* LOCK */ - endpt = nc_server_ch_client_lock(client_name, endpt_name, NC_TI_LIBSSH, &client); - if (!endpt) { - return -1; - } - - ret = nc_server_ssh_set_auth_methods(auth_methods, endpt->opts.ssh); - - /* UNLOCK */ - nc_server_ch_client_unlock(client); - - return ret; -} - -API int -nc_server_ssh_endpt_get_auth_methods(const char *endpt_name) -{ - int ret; - struct nc_endpt *endpt; - - /* LOCK */ - endpt = nc_server_endpt_lock_get(endpt_name, NC_TI_LIBSSH, NULL); - if (!endpt) { - return -1; - } - - ret = endpt->opts.ssh->auth_methods; - - /* UNLOCK */ - pthread_rwlock_unlock(&server_opts.endpt_lock); - - return ret; -} - -API int -nc_server_ssh_ch_client_endpt_get_auth_methods(const char *client_name, const char *endpt_name) -{ - int ret; - struct nc_ch_client *client; - struct nc_ch_endpt *endpt; - - /* LOCK */ - endpt = nc_server_ch_client_lock(client_name, endpt_name, NC_TI_LIBSSH, &client); - if (!endpt) { - return -1; + len = 11 + 17 + strlen(in) + 10 + 16; } - ret = endpt->opts.ssh->auth_methods; - - /* UNLOCK */ - nc_server_ch_client_unlock(client); - - return ret; -} - -static int -nc_server_ssh_set_auth_attempts(uint16_t auth_attempts, struct nc_server_ssh_opts *opts) -{ - if (!auth_attempts) { - ERRARG("auth_attempts"); - return -1; - } - - opts->auth_attempts = auth_attempts; - return 0; -} - -API int -nc_server_ssh_endpt_set_auth_attempts(const char *endpt_name, uint16_t auth_attempts) -{ - int ret; - struct nc_endpt *endpt; - - /* LOCK */ - endpt = nc_server_endpt_lock_get(endpt_name, NC_TI_LIBSSH, NULL); - if (!endpt) { - return -1; - } - - ret = nc_server_ssh_set_auth_attempts(auth_attempts, endpt->opts.ssh); - - /* UNLOCK */ - pthread_rwlock_unlock(&server_opts.endpt_lock); - - return ret; -} - -API int -nc_server_ssh_ch_client_endpt_set_auth_attempts(const char *client_name, const char *endpt_name, uint16_t auth_attempts) -{ - int ret; - struct nc_ch_client *client; - struct nc_ch_endpt *endpt; - - /* LOCK */ - endpt = nc_server_ch_client_lock(client_name, endpt_name, NC_TI_LIBSSH, &client); - if (!endpt) { - return -1; - } - - ret = nc_server_ssh_set_auth_attempts(auth_attempts, endpt->opts.ssh); - - /* UNLOCK */ - nc_server_ch_client_unlock(client); - - return ret; -} - -static int -nc_server_ssh_set_auth_timeout(uint16_t auth_timeout, struct nc_server_ssh_opts *opts) -{ - if (!auth_timeout) { - ERRARG("auth_timeout"); - return -1; - } - - opts->auth_timeout = auth_timeout; - return 0; -} - -API int -nc_server_ssh_endpt_set_auth_timeout(const char *endpt_name, uint16_t auth_timeout) -{ - int ret; - struct nc_endpt *endpt; - - /* LOCK */ - endpt = nc_server_endpt_lock_get(endpt_name, NC_TI_LIBSSH, NULL); - if (!endpt) { - return -1; - } - - ret = nc_server_ssh_set_auth_timeout(auth_timeout, endpt->opts.ssh); - - /* UNLOCK */ - pthread_rwlock_unlock(&server_opts.endpt_lock); - - return ret; -} - -API int -nc_server_ssh_ch_client_endpt_set_auth_timeout(const char *client_name, const char *endpt_name, uint16_t auth_timeout) -{ - int ret; - struct nc_ch_client *client; - struct nc_ch_endpt *endpt; - - /* LOCK */ - endpt = nc_server_ch_client_lock(client_name, endpt_name, NC_TI_LIBSSH, &client); - if (!endpt) { - return -1; + if ((unsigned)written != len) { + unlink(path); + return NULL; } - ret = nc_server_ssh_set_auth_timeout(auth_timeout, endpt->opts.ssh); - - /* UNLOCK */ - nc_server_ch_client_unlock(client); - - return ret; + return strdup(path); } static int -_nc_server_ssh_add_authkey(const char *pubkey_path, const char *pubkey_base64, NC_SSH_KEY_TYPE type, const char *username) -{ - int ret = 0; - - /* LOCK */ - pthread_mutex_lock(&server_opts.authkey_lock); - - ++server_opts.authkey_count; - server_opts.authkeys = nc_realloc(server_opts.authkeys, server_opts.authkey_count * sizeof *server_opts.authkeys); - if (!server_opts.authkeys) { - ERRMEM; - ret = -1; - goto cleanup; - } - server_opts.authkeys[server_opts.authkey_count - 1].path = pubkey_path ? strdup(pubkey_path) : NULL; - server_opts.authkeys[server_opts.authkey_count - 1].base64 = pubkey_base64 ? strdup(pubkey_base64) : NULL; - server_opts.authkeys[server_opts.authkey_count - 1].type = type; - server_opts.authkeys[server_opts.authkey_count - 1].username = strdup(username); - -cleanup: - /* UNLOCK */ - pthread_mutex_unlock(&server_opts.authkey_lock); - return ret; -} - -API int -nc_server_ssh_add_authkey_path(const char *pubkey_path, const char *username) -{ - if (!pubkey_path) { - ERRARG("pubkey_path"); - return -1; - } else if (!username) { - ERRARG("username"); - return -1; - } - - return _nc_server_ssh_add_authkey(pubkey_path, NULL, 0, username); -} - -API int -nc_server_ssh_add_authkey(const char *pubkey_base64, NC_SSH_KEY_TYPE type, const char *username) -{ - if (!pubkey_base64) { - ERRARG("pubkey_base64"); - return -1; - } else if (!type) { - ERRARG("type"); - return -1; - } else if (!username) { - ERRARG("username"); - return -1; - } - - return _nc_server_ssh_add_authkey(NULL, pubkey_base64, type, username); -} - -API int -nc_server_ssh_del_authkey(const char *pubkey_path, const char *pubkey_base64, NC_SSH_KEY_TYPE type, - const char *username) -{ - uint32_t i; - int ret = -1; - - /* LOCK */ - pthread_mutex_lock(&server_opts.authkey_lock); - - if (!pubkey_path && !pubkey_base64 && !type && !username) { - for (i = 0; i < server_opts.authkey_count; ++i) { - free(server_opts.authkeys[i].path); - free(server_opts.authkeys[i].base64); - free(server_opts.authkeys[i].username); - - ret = 0; - } - free(server_opts.authkeys); - server_opts.authkeys = NULL; - server_opts.authkey_count = 0; - } else { - for (i = 0; i < server_opts.authkey_count; ++i) { - if ((!pubkey_path || !strcmp(server_opts.authkeys[i].path, pubkey_path)) && - (!pubkey_base64 || !strcmp(server_opts.authkeys[i].base64, pubkey_base64)) && - (!type || (server_opts.authkeys[i].type == type)) && - (!username || !strcmp(server_opts.authkeys[i].username, username))) { - free(server_opts.authkeys[i].path); - free(server_opts.authkeys[i].base64); - free(server_opts.authkeys[i].username); - - --server_opts.authkey_count; - if (i < server_opts.authkey_count) { - memcpy(&server_opts.authkeys[i], &server_opts.authkeys[server_opts.authkey_count], - sizeof *server_opts.authkeys); - } else if (!server_opts.authkey_count) { - free(server_opts.authkeys); - server_opts.authkeys = NULL; - } - - ret = 0; - } - } - } - - /* UNLOCK */ - pthread_mutex_unlock(&server_opts.authkey_lock); - - return ret; -} - -void -nc_server_ssh_clear_opts(struct nc_server_ssh_opts *opts) -{ - nc_server_ssh_del_hostkey(NULL, -1, opts); -} - -#ifdef HAVE_SHADOW - -/** - * @brief Get passwd entry for a user. - * - * @param[in] username Name of the user. - * @param[in] pwd_buf Passwd entry buffer. - * @param[in,out] buf Passwd entry string buffer. - * @param[in,out] buf_size Current @p buf size. - * @return Found passwd entry for the user, NULL if none found. - */ -static struct passwd * -auth_password_getpwnam(const char *username, struct passwd *pwd_buf, char **buf, size_t *buf_size) -{ - struct passwd *pwd = NULL; - char *mem; - int r = 0; - - do { - r = getpwnam_r(username, pwd_buf, *buf, *buf_size, &pwd); - if (pwd) { - /* entry found */ - break; - } - - if (r == ERANGE) { - /* small buffer, enlarge */ - *buf_size <<= 2; - mem = realloc(*buf, *buf_size); - if (!mem) { - ERRMEM; - return NULL; - } - *buf = mem; - } - } while (r == ERANGE); - - return pwd; -} - -/** - * @brief Get shadow entry for a user. - * - * @param[in] username Name of the user. - * @param[in] spwd_buf Shadow entry buffer. - * @param[in,out] buf Shadow entry string buffer. - * @param[in,out] buf_size Current @p buf size. - * @return Found shadow entry for the user, NULL if none found. - */ -static struct spwd * -auth_password_getspnam(const char *username, struct spwd *spwd_buf, char **buf, size_t *buf_size) +nc_server_ssh_ks_ref_get_key(const char *referenced_name, struct nc_asymmetric_key **askey) { - struct spwd *spwd = NULL; - char *mem; - int r = 0; - - do { -# ifndef __QNXNTO__ - r = getspnam_r(username, spwd_buf, *buf, *buf_size, &spwd); -# else - spwd = getspnam_r(username, spwd_buf, *buf, *buf_size); -# endif - if (spwd) { - /* entry found */ + uint16_t i; + struct nc_keystore *ks = &server_opts.keystore; + + *askey = NULL; + + /* lookup name */ + for (i = 0; i < ks->asym_key_count; i++) { + if (!strcmp(referenced_name, ks->asym_keys[i].name)) { break; } + } - if (r == ERANGE) { - /* small buffer, enlarge */ - *buf_size <<= 2; - mem = realloc(*buf, *buf_size); - if (!mem) { - ERRMEM; - return NULL; - } - *buf = mem; - } - } while (r == ERANGE); + if (i == ks->asym_key_count) { + ERR(NULL, "Keystore entry \"%s\" not found.", referenced_name); + return 1; + } + + *askey = &ks->asym_keys[i]; - return spwd; + /* check if the referenced public key is SubjectPublicKeyInfo */ + if ((*askey)->pubkey_data && nc_is_pk_subject_public_key_info((*askey)->pubkey_data)) { + ERR(NULL, "The public key of the referenced hostkey \"%s\" is in the SubjectPublicKeyInfo format, " + "which is not allowed in the SSH!", referenced_name); + return 1; + } + + return 0; } -/** - * @brief Get hashed system apssword for a user. - * - * @param[in] username Name of the user. - * @return Hashed password of @p username. - */ -static char * -auth_password_get_pwd_hash(const char *username) +static int +nc_server_ssh_ts_ref_get_keys(const char *referenced_name, struct nc_public_key **pubkeys, uint16_t *pubkey_count) { - struct passwd *pwd, pwd_buf; - struct spwd *spwd, spwd_buf; - char *pass_hash = NULL, *buf = NULL; - size_t buf_size = 256; - - buf = malloc(buf_size); - if (!buf) { - ERRMEM; - goto error; - } - - pwd = auth_password_getpwnam(username, &pwd_buf, &buf, &buf_size); - if (!pwd) { - VRB(NULL, "User \"%s\" not found locally.", username); - goto error; - } - - if (!strcmp(pwd->pw_passwd, "x")) { - spwd = auth_password_getspnam(username, &spwd_buf, &buf, &buf_size); - if (!spwd) { - VRB(NULL, "Failed to retrieve the shadow entry for \"%s\".", username); - goto error; - } else if ((spwd->sp_expire > -1) && (spwd->sp_expire <= (time(NULL) / (60 * 60 * 24)))) { - WRN(NULL, "User \"%s\" account has expired.", username); - goto error; - } + uint16_t i, j; + struct nc_truststore *ts = &server_opts.truststore; - pass_hash = spwd->sp_pwdp; - } else { - pass_hash = pwd->pw_passwd; - } + *pubkeys = NULL; + *pubkey_count = 0; - if (!pass_hash) { - ERR(NULL, "No password could be retrieved for \"%s\".", username); - goto error; + /* lookup name */ + for (i = 0; i < ts->pub_bag_count; i++) { + if (!strcmp(referenced_name, ts->pub_bags[i].name)) { + break; + } } - /* check the hash structure for special meaning */ - if (!strcmp(pass_hash, "*") || !strcmp(pass_hash, "!")) { - VRB(NULL, "User \"%s\" is not allowed to authenticate using a password.", username); - goto error; - } - if (!strcmp(pass_hash, "*NP*")) { - VRB(NULL, "Retrieving password for \"%s\" from a NIS+ server not supported.", username); - goto error; + if (i == ts->pub_bag_count) { + ERR(NULL, "Truststore entry \"%s\" not found.", referenced_name); + return 1; } - pass_hash = strdup(pass_hash); - free(buf); - return pass_hash; - -error: - free(buf); - return NULL; -} - -#else + /* check if any of the referenced public keys is SubjectPublicKeyInfo */ + for (j = 0; j < ts->pub_bags[i].pubkey_count; j++) { + if (nc_is_pk_subject_public_key_info(ts->pub_bags[i].pubkeys[j].data)) { + ERR(NULL, "A public key of the referenced public key bag \"%s\" is in the SubjectPublicKeyInfo format, " + "which is not allowed in the SSH!", referenced_name); + return 1; + } + } -/** - * @brief Get hashed system password for a user. - * - * @param[in] username Name of the user. - * @return Hashed password of @p username. - */ -static char * -auth_password_get_pwd_hash(const char *username) -{ - (void)username; - return strdup(""); + *pubkeys = ts->pub_bags[i].pubkeys; + *pubkey_count = ts->pub_bags[i].pubkey_count; + return 0; } -#endif - /** * @brief Compare hashed password with a cleartext password for a match. * @@ -941,16 +184,13 @@ auth_password_get_pwd_hash(const char *username) * @return non-zero if not a match. */ static int -auth_password_compare_pwd(const char *pass_hash, const char *pass_clear) +auth_password_compare_pwd(const char *stored_pw, const char *received_pw) { - char *new_pass_hash; - -#ifdef HAVE_CRYPT_R - struct crypt_data cdata; -#endif + char *received_pw_hash = NULL; + struct crypt_data cdata = {0}; - if (!pass_hash[0]) { - if (!pass_clear[0]) { + if (!stored_pw[0]) { + if (!received_pw[0]) { WRN(NULL, "User authentication successful with an empty password!"); return 0; } else { @@ -960,48 +200,35 @@ auth_password_compare_pwd(const char *pass_hash, const char *pass_clear) } } -#ifdef HAVE_CRYPT_R - cdata.initialized = 0; - new_pass_hash = crypt_r(pass_clear, pass_hash, &cdata); -#else - pthread_mutex_lock(&crypt_lock); - new_pass_hash = crypt(pass_clear, pass_hash); - pthread_mutex_unlock(&crypt_lock); -#endif + if (!strncmp(stored_pw, "$0$", 3)) { + /* cleartext password, simply compare the values */ + return strcmp(stored_pw + 3, received_pw); + } - if (!new_pass_hash) { + received_pw_hash = crypt_r(received_pw, stored_pw, &cdata); + if (!received_pw_hash) { + ERR(NULL, "Hashing the password failed (%s).", strerror(errno)); return 1; } - return strcmp(new_pass_hash, pass_hash); + return strcmp(received_pw_hash, stored_pw); } -static void -nc_sshcb_auth_password(struct nc_session *session, ssh_message msg) +static int +nc_sshcb_auth_password(struct nc_session *session, struct nc_auth_client *auth_client, ssh_message msg) { - char *pass_hash; int auth_ret = 1; - if (server_opts.passwd_auth_clb) { - auth_ret = server_opts.passwd_auth_clb(session, ssh_message_auth_password(msg), server_opts.passwd_auth_data); - } else { - pass_hash = auth_password_get_pwd_hash(session->username); - if (pass_hash) { - auth_ret = auth_password_compare_pwd(pass_hash, ssh_message_auth_password(msg)); - free(pass_hash); - } - } + auth_ret = auth_password_compare_pwd(auth_client->password, ssh_message_auth_password(msg)); - if (!auth_ret) { - session->flags |= NC_SESSION_SSH_AUTHENTICATED; - VRB(session, "User \"%s\" authenticated.", session->username); - ssh_message_auth_reply_success(msg, 0); - } else { + if (auth_ret) { ++session->opts.server.ssh_auth_attempts; VRB(session, "Failed user \"%s\" authentication attempt (#%d).", session->username, session->opts.server.ssh_auth_attempts); ssh_message_reply_default(msg); } + + return auth_ret; } #ifdef HAVE_LIBPAM @@ -1032,7 +259,7 @@ nc_pam_conv_clb(int n_messages, const struct pam_message **msg, struct pam_respo struct nc_server_ssh_opts *opts; libssh_session = clb_data->session->ti.libssh.session; - opts = clb_data->session->data; + opts = clb_data->opts; /* PAM_MAX_NUM_MSG == 32 by default */ if ((n_messages <= 0) || (n_messages >= PAM_MAX_NUM_MSG)) { @@ -1074,11 +301,7 @@ nc_pam_conv_clb(int n_messages, const struct pam_message **msg, struct pam_respo *resp = calloc(n_requests, sizeof **resp); prompts = calloc(n_requests, sizeof *prompts); echo = calloc(n_requests, sizeof *echo); - if (!(*resp) || !prompts || !echo) { - ERRMEM; - r = PAM_BUF_ERR; - goto cleanup; - } + NC_CHECK_ERRMEM_GOTO(!(*resp) || !prompts || !echo, r = PAM_BUF_ERR, cleanup); /* set the prompts for the user */ j = 0; @@ -1126,7 +349,7 @@ nc_pam_conv_clb(int n_messages, const struct pam_message **msg, struct pam_respo } usleep(NC_TIMEOUT_STEP); - } while ((opts->auth_timeout) && (nc_timeouttime_cur_diff(&ts_timeout) >= 1)); + } while (opts->auth_timeout && (nc_timeouttime_cur_diff(&ts_timeout) >= 1)); if (!reply) { ERR(NULL, "Authentication timeout."); @@ -1174,28 +397,49 @@ nc_pam_conv_clb(int n_messages, const struct pam_message **msg, struct pam_respo * @return PAM error otherwise. */ static int -nc_pam_auth(struct nc_session *session, ssh_message ssh_msg) +nc_pam_auth(struct nc_session *session, struct nc_server_ssh_opts *opts, ssh_message ssh_msg) { pam_handle_t *pam_h = NULL; int ret; struct nc_pam_thread_arg clb_data; struct pam_conv conv; + uint16_t i; /* structure holding callback's data */ clb_data.msg = ssh_msg; clb_data.session = session; + clb_data.opts = opts; /* PAM conversation structure holding the callback and it's data */ conv.conv = nc_pam_conv_clb; conv.appdata_ptr = &clb_data; + /* get the current client's configuration file */ + for (i = 0; i < opts->client_count; i++) { + if (!strcmp(opts->auth_clients[i].username, session->username)) { + break; + } + } + + if (i == opts->client_count) { + ERR(NULL, "User \"%s\" not found.", session->username); + ret = 1; + goto cleanup; + } + + if (!opts->auth_clients[i].pam_config_name) { + ERR(NULL, "User's \"%s\" PAM configuration filename not set."); + ret = 1; + goto cleanup; + } + /* initialize PAM and see if the given configuration file exists */ # ifdef LIBPAM_HAVE_CONFDIR /* PAM version >= 1.4 */ - ret = pam_start_confdir(server_opts.conf_name, session->username, &conv, server_opts.conf_dir, &pam_h); + ret = pam_start_confdir(opts->auth_clients[i].pam_config_name, session->username, &conv, opts->auth_clients[i].pam_config_dir, &pam_h); # else /* PAM version < 1.4 */ - ret = pam_start(server_opts.conf_name, session->username, &conv, &pam_h); + ret = pam_start(opts->auth_clients[i].pam_config_name, session->username, &conv, &pam_h); # endif if (ret != PAM_SUCCESS) { ERR(NULL, "PAM error occurred (%s).\n", pam_strerror(pam_h, ret)); @@ -1243,19 +487,16 @@ nc_pam_auth(struct nc_session *session, ssh_message ssh_msg) #endif -static void -nc_sshcb_auth_kbdint(struct nc_session *session, ssh_message msg) +static int +nc_sshcb_auth_kbdint(struct nc_session *session, struct nc_server_ssh_opts *opts, ssh_message msg) { int auth_ret = 1; - if (server_opts.interactive_auth_sess_clb) { - auth_ret = server_opts.interactive_auth_sess_clb(session, session->ti.libssh.session, msg, - server_opts.interactive_auth_data); - } else if (server_opts.interactive_auth_clb) { - auth_ret = server_opts.interactive_auth_clb(session, msg, server_opts.interactive_auth_data); + if (server_opts.interactive_auth_clb) { + auth_ret = server_opts.interactive_auth_clb(session, session->ti.libssh.session, msg, server_opts.interactive_auth_data); } else { #ifdef HAVE_LIBPAM - if (nc_pam_auth(session, msg) == PAM_SUCCESS) { + if (nc_pam_auth(session, opts, msg) == PAM_SUCCESS) { auth_ret = 0; } #else @@ -1263,22 +504,99 @@ nc_sshcb_auth_kbdint(struct nc_session *session, ssh_message msg) #endif } - /* We have already sent a reply */ - if (auth_ret == -1) { - return; - } - /* Authenticate message based on outcome */ - if (!auth_ret) { - session->flags |= NC_SESSION_SSH_AUTHENTICATED; - VRB(session, "User \"%s\" authenticated.", session->username); - ssh_message_auth_reply_success(msg, 0); - } else { + if (auth_ret) { ++session->opts.server.ssh_auth_attempts; VRB(session, "Failed user \"%s\" authentication attempt (#%d).", session->username, session->opts.server.ssh_auth_attempts); ssh_message_reply_default(msg); } + + return auth_ret; +} + +/* + * Get the public key type from binary data stored in buffer. + * The data is in the form of: 4 bytes = data length, then data of data length + * 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) +{ + uint32_t type_len; + + /* copy the 4 bytes */ + memcpy(&type_len, buffer, sizeof type_len); + /* type_len now stores the length of the key type */ + type_len = ntohl(type_len); + *len = type_len; + + /* move 4 bytes in the buffer, this is where the type should be */ + buffer += sizeof type_len; + return buffer; +} + +/** + * @brief Create ssh key from base64 pubkey data. + * + * @param[in] base64 base64 encoded public key. + * @param[out] key created ssh key. + * @return 0 on success, 1 otherwise. + */ +static int +nc_server_ssh_create_ssh_pubkey(const char *base64, ssh_key *key) +{ + int ret = 0; + char *bin = NULL; + const char *pub_type = NULL; + uint32_t pub_type_len = 0; + + if (!key && !base64) { + ERRINT; + ret = 1; + goto cleanup; + } + + *key = NULL; + + /* convert base64 to binary */ + if (nc_base64_to_bin(base64, &bin) == -1) { + ERR(NULL, "Unable to decode base64."); + ret = 1; + goto cleanup; + } + + /* get the key type and try to import it if possible */ + pub_type = nc_server_ssh_get_pubkey_type(bin, &pub_type_len); + if (!pub_type) { + ret = 1; + goto cleanup; + } else if (!strncmp(pub_type, "ssh-dss", pub_type_len)) { + ERR(NULL, "DSA keys are not supported."); + ret = 1; + goto cleanup; + } else if (!strncmp(pub_type, "ssh-rsa", pub_type_len)) { + ret = ssh_pki_import_pubkey_base64(base64, SSH_KEYTYPE_RSA, key); + } else if (!strncmp(pub_type, "ecdsa-sha2-nistp256", pub_type_len)) { + ret = ssh_pki_import_pubkey_base64(base64, SSH_KEYTYPE_ECDSA_P256, key); + } else if (!strncmp(pub_type, "ecdsa-sha2-nistp384", pub_type_len)) { + ret = ssh_pki_import_pubkey_base64(base64, SSH_KEYTYPE_ECDSA_P384, key); + } else if (!strncmp(pub_type, "ecdsa-sha2-nistp521", pub_type_len)) { + ret = ssh_pki_import_pubkey_base64(base64, SSH_KEYTYPE_ECDSA_P521, key); + } else if (!strncmp(pub_type, "ssh-ed25519", pub_type_len)) { + ret = ssh_pki_import_pubkey_base64(base64, SSH_KEYTYPE_ED25519, key); + } else { + ERR(NULL, "Public key type not recognised."); + ret = 1; + goto cleanup; + } + +cleanup: + if (ret != SSH_OK) { + ERR(NULL, "Error importing public key."); + } + free(bin); + return ret; } /** @@ -1287,96 +605,96 @@ nc_sshcb_auth_kbdint(struct nc_session *session, ssh_message msg) * @param[in] key Presented SSH key to compare. * @return Authorized key username, NULL if no match was found. */ -static const char * -auth_pubkey_compare_key(ssh_key key) +static int +auth_pubkey_compare_key(ssh_key key, struct nc_auth_client *auth_client) { - uint32_t i; - ssh_key pub_key; - const char *username = NULL; + uint16_t i, pubkey_count; int ret = 0; + ssh_key new_key = NULL; + struct nc_public_key *pubkeys; - /* LOCK */ - pthread_mutex_lock(&server_opts.authkey_lock); - - for (i = 0; i < server_opts.authkey_count; ++i) { - switch (server_opts.authkeys[i].type) { - case NC_SSH_KEY_UNKNOWN: - ret = ssh_pki_import_pubkey_file(server_opts.authkeys[i].path, &pub_key); - break; - case NC_SSH_KEY_DSA: - ret = ssh_pki_import_pubkey_base64(server_opts.authkeys[i].base64, SSH_KEYTYPE_DSS, &pub_key); - break; - case NC_SSH_KEY_RSA: - ret = ssh_pki_import_pubkey_base64(server_opts.authkeys[i].base64, SSH_KEYTYPE_RSA, &pub_key); - break; - case NC_SSH_KEY_ECDSA: - ret = ssh_pki_import_pubkey_base64(server_opts.authkeys[i].base64, SSH_KEYTYPE_ECDSA, &pub_key); - break; + /* get the correct public key storage */ + if (auth_client->store == NC_STORE_LOCAL) { + pubkeys = auth_client->pubkeys; + pubkey_count = auth_client->pubkey_count; + } else { + ret = nc_server_ssh_ts_ref_get_keys(auth_client->ts_ref, &pubkeys, &pubkey_count); + if (ret) { + ERR(NULL, "Error getting \"%s\"'s public keys from the truststore.", auth_client->username); + return ret; } + } - if (ret == SSH_EOF) { - WRN(NULL, "Failed to import a public key of \"%s\" (File access problem).", server_opts.authkeys[i].username); - continue; - } else if (ret == SSH_ERROR) { - WRN(NULL, "Failed to import a public key of \"%s\" (SSH error).", server_opts.authkeys[i].username); + /* try to compare all of the client's keys with the key received in the SSH message */ + for (i = 0; i < pubkey_count; i++) { + /* create the SSH key from the data */ + if (nc_server_ssh_create_ssh_pubkey(pubkeys[i].data, &new_key)) { + ssh_key_free(new_key); continue; } - if (!ssh_key_cmp(key, pub_key, SSH_KEY_CMP_PUBLIC)) { - ssh_key_free(pub_key); + /* compare the keys */ + ret = ssh_key_cmp(key, new_key, SSH_KEY_CMP_PUBLIC); + if (!ret) { break; + } else { + WRN(NULL, "User's \"%s\" public key doesn't match, trying another.", auth_client->username); + ssh_key_free(new_key); } - - ssh_key_free(pub_key); } - if (i < server_opts.authkey_count) { - username = server_opts.authkeys[i].username; + if (i == pubkey_count) { + ret = 1; } - /* UNLOCK */ - pthread_mutex_unlock(&server_opts.authkey_lock); + if (!ret) { + /* only free a key if everything was ok, it would have already been freed otherwise */ + ssh_key_free(new_key); + } - return username; + return ret; } static void -nc_sshcb_auth_pubkey(struct nc_session *session, ssh_message msg) +nc_sshcb_auth_none(struct nc_session *session, struct nc_auth_client *auth_client, ssh_message msg) +{ + if (auth_client->supports_none && !auth_client->password && !auth_client->pubkey_count && !auth_client->pam_config_name) { + /* only authenticate the client if he supports none and no other method */ + session->flags |= NC_SESSION_SSH_AUTHENTICATED; + VRB(session, "User \"%s\" authenticated.", session->username); + ssh_message_auth_reply_success(msg, 0); + } + + ssh_message_reply_default(msg); +} + +static int +nc_sshcb_auth_pubkey(struct nc_session *session, struct nc_auth_client *auth_client, ssh_message msg) { - const char *username; - int signature_state; + int signature_state, ret = 0; - if (server_opts.pubkey_auth_clb) { - if (server_opts.pubkey_auth_clb(session, ssh_message_auth_pubkey(msg), server_opts.pubkey_auth_data)) { - goto fail; - } - } else { - if ((username = auth_pubkey_compare_key(ssh_message_auth_pubkey(msg))) == NULL) { - VRB(session, "User \"%s\" tried to use an unknown (unauthorized) public key.", session->username); - goto fail; - } else if (strcmp(session->username, username)) { - VRB(session, "User \"%s\" is not the username identified with the presented public key.", session->username); - goto fail; - } + if (auth_pubkey_compare_key(ssh_message_auth_pubkey(msg), auth_client)) { + VRB(session, "User \"%s\" tried to use an unknown (unauthorized) public key.", session->username); + ret = 1; + goto fail; } signature_state = ssh_message_auth_publickey_state(msg); - if (signature_state == SSH_PUBLICKEY_STATE_VALID) { - VRB(session, "User \"%s\" authenticated.", session->username); - session->flags |= NC_SESSION_SSH_AUTHENTICATED; - ssh_message_auth_reply_success(msg, 0); - } else if (signature_state == SSH_PUBLICKEY_STATE_NONE) { + if (signature_state == SSH_PUBLICKEY_STATE_NONE) { /* accepting only the use of a public key */ ssh_message_auth_reply_pk_ok_simple(msg); + ret = 1; } - return; + return ret; fail: ++session->opts.server.ssh_auth_attempts; VRB(session, "Failed user \"%s\" authentication attempt (#%d).", session->username, session->opts.server.ssh_auth_attempts); ssh_message_reply_default(msg); + + return ret; } static int @@ -1435,10 +753,7 @@ nc_sshcb_channel_subsystem(struct nc_session *session, ssh_channel channel, cons } else { /* additional channel subsystem request, new session is ready as far as SSH is concerned */ new_session = nc_new_session(NC_SERVER, 1); - if (!new_session) { - ERRMEM; - return -1; - } + NC_CHECK_ERRMEM_RET(!new_session, -1); /* insert the new session */ if (!session->ti.libssh.next) { @@ -1464,11 +779,13 @@ nc_sshcb_channel_subsystem(struct nc_session *session, ssh_channel channel, cons } int -nc_sshcb_msg(ssh_session UNUSED(sshsession), ssh_message msg, void *data) +nc_session_ssh_msg(struct nc_session *session, struct nc_server_ssh_opts *opts, ssh_message msg, struct nc_auth_state *state) { const char *str_type, *str_subtype = NULL, *username; - int subtype, type; - struct nc_session *session = (struct nc_session *)data; + int subtype, type, libssh_auth_methods = 0, ret = 0; + uint16_t i; + struct nc_auth_client *auth_client = NULL; + struct nc_endpt *referenced_endpt; type = ssh_message_type(msg); subtype = ssh_message_subtype(msg); @@ -1590,7 +907,6 @@ nc_sshcb_msg(ssh_session UNUSED(sshsession), ssh_message msg, void *data) ssh_message_reply_default(msg); return 0; } - session->flags |= NC_SESSION_SSH_NEW_MSG; /* * process known messages @@ -1600,19 +916,73 @@ nc_sshcb_msg(ssh_session UNUSED(sshsession), ssh_message msg, void *data) ERR(session, "User \"%s\" authenticated, but requested another authentication.", session->username); ssh_message_reply_default(msg); return 0; + } else if (!state || !opts) { + /* these two parameters should always be set during an authentication, + * however do a check just in case something goes really wrong, since they + * are not needed for other types of messages + */ + ERRINT; + return 1; } /* save the username, do not let the client change it */ username = ssh_message_auth_user(msg); - if (!session->username) { - if (!username) { - ERR(session, "Denying an auth request without a username."); - return 1; + assert(username); + + for (i = 0; i < opts->client_count; i++) { + if (!strcmp(opts->auth_clients[i].username, username)) { + auth_client = &opts->auth_clients[i]; + break; + } + } + + if (!auth_client) { + if (opts->referenced_endpt_name) { + /* client not known by the endpt, but it references another one so try it */ + if (nc_server_get_referenced_endpt(opts->referenced_endpt_name, &referenced_endpt)) { + ERRINT; + return 1; + } + + return nc_session_ssh_msg(session, referenced_endpt->opts.ssh, msg, state); } + ERR(NULL, "User \"%s\" not known by the server.", username); + ssh_message_reply_default(msg); + return 0; + } + + if (!session->username) { session->username = strdup(username); - } else if (username) { + + /* configure and count accepted auth methods */ + if (auth_client->store == NC_STORE_LOCAL) { + if (auth_client->pubkey_count) { + libssh_auth_methods |= SSH_AUTH_METHOD_PUBLICKEY; + } + } else if (auth_client->ts_ref) { + libssh_auth_methods |= SSH_AUTH_METHOD_PUBLICKEY; + } + if (auth_client->password) { + state->auth_method_count++; + libssh_auth_methods |= SSH_AUTH_METHOD_PASSWORD; + } + if (auth_client->pam_config_name) { + state->auth_method_count++; + libssh_auth_methods |= SSH_AUTH_METHOD_INTERACTIVE; + } + if (auth_client->supports_none) { + libssh_auth_methods |= SSH_AUTH_METHOD_NONE; + } + + if (libssh_auth_methods & SSH_AUTH_METHOD_PUBLICKEY) { + state->auth_method_count++; + } + + ssh_set_auth_methods(session->ti.libssh.session, libssh_auth_methods); + } else { if (strcmp(username, session->username)) { + /* changing username not allowed */ ERR(session, "User \"%s\" changed its username to \"%s\".", session->username, username); session->status = NC_STATUS_INVALID; session->term_reason = NC_SESSION_TERM_OTHER; @@ -1620,19 +990,34 @@ nc_sshcb_msg(ssh_session UNUSED(sshsession), ssh_message msg, void *data) } } + /* try authenticating, the user must authenticate via all of his configured auth methods */ if (subtype == SSH_AUTH_METHOD_NONE) { - /* libssh will return the supported auth methods */ - return 1; + nc_sshcb_auth_none(session, auth_client, msg); + ret = 1; } else if (subtype == SSH_AUTH_METHOD_PASSWORD) { - nc_sshcb_auth_password(session, msg); - return 0; + ret = nc_sshcb_auth_password(session, auth_client, msg); } else if (subtype == SSH_AUTH_METHOD_PUBLICKEY) { - nc_sshcb_auth_pubkey(session, msg); - return 0; + ret = nc_sshcb_auth_pubkey(session, auth_client, msg); } else if (subtype == SSH_AUTH_METHOD_INTERACTIVE) { - nc_sshcb_auth_kbdint(session, msg); - return 0; + ret = nc_sshcb_auth_kbdint(session, opts, msg); + } + + if (!ret) { + state->auth_success_count++; + } + + if (!ret && (state->auth_success_count < state->auth_method_count)) { + /* success, but he needs to do another method */ + VRB(session, "User \"%s\" partially authenticated, but still needs to authenticate via the rest of his configured methods.", username); + ssh_message_auth_reply_success(msg, 1); + } else if (!ret && (state->auth_success_count == state->auth_method_count)) { + /* authenticated */ + ssh_message_auth_reply_success(msg, 0); + session->flags |= NC_SESSION_SSH_AUTHENTICATED; + VRB(session, "User \"%s\" authenticated.", username); } + + return 0; } else if (session->flags & NC_SESSION_SSH_AUTHENTICATED) { if ((type == SSH_REQUEST_CHANNEL_OPEN) && ((enum ssh_channel_type_e)subtype == SSH_CHANNEL_SESSION)) { if (nc_sshcb_channel_open(session, msg)) { @@ -1657,67 +1042,34 @@ nc_sshcb_msg(ssh_session UNUSED(sshsession), ssh_message msg, void *data) /* ret 1 on success, 0 on timeout, -1 on error */ static int -nc_accept_ssh_session_open_netconf_channel(struct nc_session *session, int timeout) +nc_accept_ssh_session_open_netconf_channel(struct nc_session *session, struct nc_server_ssh_opts *opts, int timeout) { - int ret; struct timespec ts_timeout; + ssh_message msg; - /* message callback is executed twice to give chance for the channel to be - * created if timeout == 0 (it takes 2 messages, channel-open, subsystem-request) */ - if (!timeout) { - if (!nc_session_is_connected(session)) { - ERR(session, "Communication socket unexpectedly closed (libssh)."); - return -1; - } - - ret = ssh_execute_message_callbacks(session->ti.libssh.session); - if (ret != SSH_OK) { - ERR(session, "Failed to receive SSH messages on a session (%s).", - ssh_get_error(session->ti.libssh.session)); - return -1; - } - - if (!session->ti.libssh.channel) { - return 0; - } - - ret = ssh_execute_message_callbacks(session->ti.libssh.session); - if (ret != SSH_OK) { - ERR(session, "Failed to receive SSH messages on a session (%s).", - ssh_get_error(session->ti.libssh.session)); - return -1; - } - - if (!(session->flags & NC_SESSION_SSH_SUBSYS_NETCONF)) { - /* we did not receive subsystem-request, timeout */ - return 0; - } - - return 1; - } - - if (timeout > -1) { - nc_timeouttime_get(&ts_timeout, timeout); + if (timeout) { + nc_timeouttime_get(&ts_timeout, timeout * 1000); } while (1) { if (!nc_session_is_connected(session)) { - ERR(session, "Communication socket unexpectedly closed (libssh)."); + ERR(session, "Communication SSH socket unexpectedly closed."); return -1; } - ret = ssh_execute_message_callbacks(session->ti.libssh.session); - if (ret != SSH_OK) { - ERR(session, "Failed to receive SSH messages on a session (%s).", - ssh_get_error(session->ti.libssh.session)); - return -1; + msg = ssh_message_get(session->ti.libssh.session); + if (msg) { + if (nc_session_ssh_msg(session, opts, msg, NULL)) { + ssh_message_reply_default(msg); + } + ssh_message_free(msg); } - if (session->ti.libssh.channel && (session->flags & NC_SESSION_SSH_SUBSYS_NETCONF)) { + if (session->ti.libssh.channel && session->flags & NC_SESSION_SSH_SUBSYS_NETCONF) { return 1; } usleep(NC_TIMEOUT_STEP); - if ((timeout > -1) && (nc_timeouttime_cur_diff(&ts_timeout) < 1)) { + if (opts->auth_timeout && (nc_timeouttime_cur_diff(&ts_timeout) < 1)) { /* timeout */ ERR(session, "Failed to start \"netconf\" SSH subsystem for too long, disconnecting."); break; @@ -1737,44 +1089,42 @@ nc_accept_ssh_session_open_netconf_channel(struct nc_session *session, int timeo * @return -1 on error. */ static int -nc_ssh_bind_add_hostkeys(ssh_bind sbind, char **hostkeys, uint8_t hostkey_count) +nc_ssh_bind_add_hostkeys(ssh_bind sbind, struct nc_server_ssh_opts *opts, uint16_t hostkey_count) { - uint8_t i; + uint16_t i; char *privkey_path, *privkey_data; int ret; - NC_SSH_KEY_TYPE privkey_type; - - if (!server_opts.hostkey_clb) { - ERR(NULL, "Callback for retrieving SSH host keys not set."); - return -1; - } + struct nc_asymmetric_key *key = NULL; for (i = 0; i < hostkey_count; ++i) { privkey_path = privkey_data = NULL; - if (server_opts.hostkey_clb(hostkeys[i], server_opts.hostkey_data, &privkey_path, &privkey_data, &privkey_type)) { - ERR(NULL, "Host key callback failed."); - return -1; - } - if (privkey_data) { - privkey_path = base64der_key_to_tmp_file(privkey_data, nc_keytype2str(privkey_type)); - if (!privkey_path) { - ERR(NULL, "Temporarily storing a host key into a file failed (%s).", strerror(errno)); - free(privkey_data); + /* get the asymmetric key */ + if (opts->hostkeys[i].store == NC_STORE_LOCAL) { + /* stored locally */ + key = &opts->hostkeys[i].key; + } else { + /* keystore reference, need to get it */ + if (nc_server_ssh_ks_ref_get_key(opts->hostkeys[i].ks_ref, &key)) { return -1; } } + privkey_path = base64der_privkey_to_tmp_file(key->privkey_data, nc_privkey_format_to_str(key->privkey_type)); + if (!privkey_path) { + ERR(NULL, "Temporarily storing a host key into a file failed (%s).", strerror(errno)); + return -1; + } + ret = ssh_bind_options_set(sbind, SSH_BIND_OPTIONS_HOSTKEY, privkey_path); /* cleanup */ if (privkey_data && unlink(privkey_path)) { WRN(NULL, "Removing a temporary host key file \"%s\" failed (%s).", privkey_path, strerror(errno)); } - free(privkey_data); if (ret != SSH_OK) { - ERR(NULL, "Failed to set hostkey \"%s\" (%s).", hostkeys[i], privkey_path); + ERR(NULL, "Failed to set hostkey \"%s\" (%s).", opts->hostkeys[i].name, privkey_path); } free(privkey_path); @@ -1787,23 +1137,11 @@ nc_ssh_bind_add_hostkeys(ssh_bind sbind, char **hostkeys, uint8_t hostkey_count) } static int -nc_accept_ssh_session_auth(struct nc_session *session, const struct nc_server_ssh_opts *opts) +nc_accept_ssh_session_auth(struct nc_session *session, struct nc_server_ssh_opts *opts) { struct timespec ts_timeout; ssh_message msg; - int libssh_auth_methods = 0; - - /* configure accepted auth methods */ - if (opts->auth_methods & NC_SSH_AUTH_PUBLICKEY) { - libssh_auth_methods |= SSH_AUTH_METHOD_PUBLICKEY; - } - if (opts->auth_methods & NC_SSH_AUTH_PASSWORD) { - libssh_auth_methods |= SSH_AUTH_METHOD_PASSWORD; - } - if (opts->auth_methods & NC_SSH_AUTH_INTERACTIVE) { - libssh_auth_methods |= SSH_AUTH_METHOD_INTERACTIVE; - } - ssh_set_auth_methods(session->ti.libssh.session, libssh_auth_methods); + struct nc_auth_state state = {0}; /* authenticate */ if (opts->auth_timeout) { @@ -1817,7 +1155,7 @@ nc_accept_ssh_session_auth(struct nc_session *session, const struct nc_server_ss msg = ssh_message_get(session->ti.libssh.session); if (msg) { - if (nc_sshcb_msg(session->ti.libssh.session, msg, (void *) session)) { + if (nc_session_ssh_msg(session, opts, msg, &state)) { ssh_message_reply_default(msg); } ssh_message_free(msg); @@ -1833,7 +1171,7 @@ nc_accept_ssh_session_auth(struct nc_session *session, const struct nc_server_ss } usleep(NC_TIMEOUT_STEP); - if ((opts->auth_timeout) && (nc_timeouttime_cur_diff(&ts_timeout) < 1)) { + if (opts->auth_timeout && (nc_timeouttime_cur_diff(&ts_timeout) < 1)) { /* timeout */ break; } @@ -1853,14 +1191,12 @@ nc_accept_ssh_session_auth(struct nc_session *session, const struct nc_server_ss } int -nc_accept_ssh_session(struct nc_session *session, int sock, int timeout) +nc_accept_ssh_session(struct nc_session *session, struct nc_server_ssh_opts *opts, int sock, int timeout) { ssh_bind sbind = NULL; - struct nc_server_ssh_opts *opts; int rc = 1, r; struct timespec ts_timeout; - - opts = session->data; + const char *err_msg; /* other transport-specific data */ session->ti_type = NC_TI_LIBSSH; @@ -1879,7 +1215,25 @@ nc_accept_ssh_session(struct nc_session *session, int sock, int timeout) } /* configure host keys */ - if (nc_ssh_bind_add_hostkeys(sbind, opts->hostkeys, opts->hostkey_count)) { + if (nc_ssh_bind_add_hostkeys(sbind, opts, opts->hostkey_count)) { + rc = -1; + goto cleanup; + } + + /* configure supported algorithms */ + if (opts->hostkey_algs && ssh_bind_options_set(sbind, SSH_BIND_OPTIONS_HOSTKEY_ALGORITHMS, opts->hostkey_algs)) { + rc = -1; + goto cleanup; + } + if (opts->encryption_algs && ssh_bind_options_set(sbind, SSH_BIND_OPTIONS_CIPHERS_S_C, opts->encryption_algs)) { + rc = -1; + goto cleanup; + } + if (opts->kex_algs && ssh_bind_options_set(sbind, SSH_BIND_OPTIONS_KEY_EXCHANGE, opts->kex_algs)) { + rc = -1; + goto cleanup; + } + if (opts->mac_algs && ssh_bind_options_set(sbind, SSH_BIND_OPTIONS_HMAC_S_C, opts->mac_algs)) { rc = -1; goto cleanup; } @@ -1892,6 +1246,7 @@ nc_accept_ssh_session(struct nc_session *session, int sock, int timeout) } sock = -1; + /* set to non-blocking */ ssh_set_blocking(session->ti.libssh.session, 0); if (timeout > -1) { @@ -1909,7 +1264,11 @@ nc_accept_ssh_session(struct nc_session *session, int sock, int timeout) rc = 0; goto cleanup; } else if (r != SSH_OK) { - ERR(session, "SSH key exchange error (%s).", ssh_get_error(session->ti.libssh.session)); + err_msg = ssh_get_error(session->ti.libssh.session); + if (err_msg[0] == '\0') { + err_msg = "hostkey algorithm generated from the hostkey most likely not found in the set of configured hostkey algorithms"; + } + ERR(session, "SSH key exchange error (%s).", err_msg); rc = -1; goto cleanup; } @@ -1919,20 +1278,11 @@ nc_accept_ssh_session(struct nc_session *session, int sock, int timeout) goto cleanup; } - /* set the message callback after a successful authentication */ - ssh_set_message_callback(session->ti.libssh.session, nc_sshcb_msg, session); - - /* remember that this session was just set as nc_sshcb_msg() parameter */ - session->flags |= NC_SESSION_SSH_MSG_CB; - /* open channel and request 'netconf' subsystem */ - if ((rc = nc_accept_ssh_session_open_netconf_channel(session, timeout)) != 1) { + if ((rc = nc_accept_ssh_session_open_netconf_channel(session, opts, timeout)) != 1) { goto cleanup; } - /* all SSH messages were processed */ - session->flags &= ~NC_SESSION_SSH_NEW_MSG; - cleanup: if (sock > -1) { close(sock); @@ -1948,13 +1298,7 @@ nc_session_accept_ssh_channel(struct nc_session *orig_session, struct nc_session struct nc_session *new_session = NULL; struct timespec ts_cur; - if (!orig_session) { - ERRARG("orig_session"); - return NC_MSG_ERROR; - } else if (!session) { - ERRARG("session"); - return NC_MSG_ERROR; - } + 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) && orig_session->ti.libssh.next) { @@ -1987,7 +1331,7 @@ nc_session_accept_ssh_channel(struct nc_session *orig_session, struct nc_session } nc_realtime_get(&ts_cur); - new_session->opts.server.session_start = ts_cur.tv_sec; + new_session->opts.server.session_start = ts_cur; nc_timeouttime_get(&ts_cur, 0); new_session->opts.server.last_rpc = ts_cur.tv_sec; new_session->status = NC_STATUS_RUNNING; @@ -2005,13 +1349,7 @@ nc_ps_accept_ssh_channel(struct nc_pollsession *ps, struct nc_session **session) struct timespec ts_cur; uint16_t i; - if (!ps) { - ERRARG("ps"); - return NC_MSG_ERROR; - } else if (!session) { - ERRARG("session"); - return NC_MSG_ERROR; - } + NC_CHECK_ARG_RET(NULL, ps, session, NC_MSG_ERROR); /* LOCK */ if (nc_ps_lock(ps, &q_id, __func__)) { @@ -2058,7 +1396,7 @@ nc_ps_accept_ssh_channel(struct nc_pollsession *ps, struct nc_session **session) } nc_realtime_get(&ts_cur); - new_session->opts.server.session_start = ts_cur.tv_sec; + new_session->opts.server.session_start = ts_cur; nc_timeouttime_get(&ts_cur, 0); new_session->opts.server.last_rpc = ts_cur.tv_sec; new_session->status = NC_STATUS_RUNNING; diff --git a/src/session_server_tls.c b/src/session_server_tls.c index 9b9b3f10..b0aa2b6b 100644 --- a/src/session_server_tls.c +++ b/src/session_server_tls.c @@ -1,10 +1,11 @@ /** * @file session_server_tls.c * @author Michal Vasko + * @author Roman Janota * @brief libnetconf2 TLS server session manipulation functions * * @copyright - * Copyright (c) 2015 - 2021 CESNET, z.s.p.o. + * Copyright (c) 2015 - 2023 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. @@ -16,9 +17,14 @@ #define _GNU_SOURCE #include +#include +#include +#include #include #include +#include +#include #include #include #include @@ -26,21 +32,14 @@ #include #include "compat.h" -#include "libnetconf.h" -#include "session_server.h" -#include "session_server_ch.h" - -#if OPENSSL_VERSION_NUMBER < 0x10100000L -#define X509_STORE_CTX_get_by_subject X509_STORE_get_by_subject -#endif +#include "config.h" +#include "log_p.h" +#include "session.h" +#include "session_p.h" struct nc_server_tls_opts tls_ch_opts; -pthread_mutex_t tls_ch_opts_lock = PTHREAD_MUTEX_INITIALIZER; extern struct nc_server_opts server_opts; -static pthread_key_t verify_key; -static pthread_once_t verify_once = PTHREAD_ONCE_INIT; - static char * asn1time_to_str(const ASN1_TIME *t) { @@ -51,10 +50,12 @@ asn1time_to_str(const ASN1_TIME *t) if (!t) { return NULL; } + bio = BIO_new(BIO_s_mem()); if (!bio) { return NULL; } + ASN1_TIME_print(bio, t); n = BIO_pending(bio); cp = malloc(n + 1); @@ -63,12 +64,14 @@ asn1time_to_str(const ASN1_TIME *t) BIO_free(bio); return NULL; } + n = BIO_read(bio, cp, n); if (n < 0) { BIO_free(bio); free(cp); return NULL; } + cp[n] = '\0'; BIO_free(bio); return cp; @@ -123,23 +126,6 @@ base64der_to_cert(const char *in) return out; } -/* return NULL - either errno or SSL error */ -static X509 * -pem_to_cert(const char *path) -{ - FILE *fp; - X509 *out; - - fp = fopen(path, "r"); - if (!fp) { - return NULL; - } - - out = PEM_read_X509(fp, NULL, NULL, NULL); - fclose(fp); - return out; -} - static EVP_PKEY * base64der_to_privatekey(const char *in, const char *key_str) { @@ -213,10 +199,7 @@ nc_tls_ctn_get_username_from_cert(X509 *client_cert, NC_TLS_CTN_MAPTYPE map_type *strchr(common_name, '/') = '\0'; } *username = strdup(common_name); - if (!*username) { - ERRMEM; - return -1; - } + NC_CHECK_ERRMEM_RET(!*username, 1); free(subject); } else { /* retrieve subjectAltName's rfc822Name (email), dNSName and iPAddress values */ @@ -233,30 +216,16 @@ nc_tls_ctn_get_username_from_cert(X509 *client_cert, NC_TLS_CTN_MAPTYPE map_type /* rfc822Name (email) */ if (((map_type == NC_TLS_CTN_SAN_ANY) || (map_type == NC_TLS_CTN_SAN_RFC822_NAME)) && (san_name->type == GEN_EMAIL)) { -#if OPENSSL_VERSION_NUMBER < 0x10100000L // < 1.1.0 - *username = strdup((char *)ASN1_STRING_data(san_name->d.rfc822Name)); -#else *username = strdup((char *)ASN1_STRING_get0_data(san_name->d.rfc822Name)); -#endif - if (!*username) { - ERRMEM; - return -1; - } + NC_CHECK_ERRMEM_RET(!*username, 1); break; } /* dNSName */ if (((map_type == NC_TLS_CTN_SAN_ANY) || (map_type == NC_TLS_CTN_SAN_DNS_NAME)) && (san_name->type == GEN_DNS)) { -#if OPENSSL_VERSION_NUMBER < 0x10100000L // < 1.1.0 - *username = strdup((char *)ASN1_STRING_data(san_name->d.dNSName)); -#else *username = strdup((char *)ASN1_STRING_get0_data(san_name->d.dNSName)); -#endif - if (!*username) { - ERRMEM; - return -1; - } + NC_CHECK_ERRMEM_RET(!*username, 1); break; } @@ -318,19 +287,17 @@ nc_tls_cert_to_name(struct nc_session *session, struct nc_ctn *ctn_first, X509 * { char *digest_md5 = NULL, *digest_sha1 = NULL, *digest_sha224 = NULL; char *digest_sha256 = NULL, *digest_sha384 = NULL, *digest_sha512 = NULL; - unsigned char *buf = malloc(64); + unsigned char *buf; unsigned int buf_len = 64; int ret = 0; struct nc_ctn *ctn; NC_TLS_CTN_MAPTYPE map_type; char *username = NULL; - if (!buf) { - ERRMEM; - return -1; - } + buf = malloc(buf_len); + NC_CHECK_ERRMEM_RET(!buf, -1); - if (!session || !ctn_first || !cert) { + if (!session || !cert) { free(buf); return -1; } @@ -340,16 +307,20 @@ nc_tls_cert_to_name(struct nc_session *session, struct nc_ctn *ctn_first, X509 * map_type = NC_TLS_CTN_UNKNOWN; /* first make sure the entry is valid */ - if (!ctn->fingerprint || !ctn->map_type || ((ctn->map_type == NC_TLS_CTN_SPECIFIED) && !ctn->name)) { - VRB(NULL, "Cert verify CTN: entry with id %u not valid, skipping.", ctn->id); + 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); continue; } - /* MD5 */ - if (!strncmp(ctn->fingerprint, "01", 2)) { + /* if ctn has no fingerprint, it will match any certificate */ + if (!ctn->fingerprint) { + map_type = ctn->map_type; + + /* MD5 */ + } else if (!strncmp(ctn->fingerprint, "01", 2)) { if (!digest_md5) { if (X509_digest(cert, EVP_md5(), buf, &buf_len) != 1) { - ERR(NULL, "Calculating MD5 digest failed (%s).", ERR_reason_error_string(ERR_get_error())); + ERR(session, "Calculating MD5 digest failed (%s).", ERR_reason_error_string(ERR_get_error())); ret = -1; goto cleanup; } @@ -358,15 +329,17 @@ nc_tls_cert_to_name(struct nc_session *session, struct nc_ctn *ctn_first, X509 * if (!strcasecmp(ctn->fingerprint + 3, digest_md5)) { /* we got ourselves a potential winner! */ - VRB(NULL, "Cert verify CTN: entry with a matching fingerprint found."); + VRB(session, "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(NULL, "Calculating SHA-1 digest failed (%s).", ERR_reason_error_string(ERR_get_error())); + ERR(session, "Calculating SHA-1 digest failed (%s).", ERR_reason_error_string(ERR_get_error())); ret = -1; goto cleanup; } @@ -375,15 +348,17 @@ nc_tls_cert_to_name(struct nc_session *session, struct nc_ctn *ctn_first, X509 * if (!strcasecmp(ctn->fingerprint + 3, digest_sha1)) { /* we got ourselves a potential winner! */ - VRB(NULL, "Cert verify CTN: entry with a matching fingerprint found."); + VRB(session, "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(NULL, "Calculating SHA-224 digest failed (%s).", ERR_reason_error_string(ERR_get_error())); + ERR(session, "Calculating SHA-224 digest failed (%s).", ERR_reason_error_string(ERR_get_error())); ret = -1; goto cleanup; } @@ -392,15 +367,17 @@ nc_tls_cert_to_name(struct nc_session *session, struct nc_ctn *ctn_first, X509 * if (!strcasecmp(ctn->fingerprint + 3, digest_sha224)) { /* we got ourselves a potential winner! */ - VRB(NULL, "Cert verify CTN: entry with a matching fingerprint found."); + VRB(session, "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(NULL, "Calculating SHA-256 digest failed (%s).", ERR_reason_error_string(ERR_get_error())); + ERR(session, "Calculating SHA-256 digest failed (%s).", ERR_reason_error_string(ERR_get_error())); ret = -1; goto cleanup; } @@ -409,15 +386,17 @@ nc_tls_cert_to_name(struct nc_session *session, struct nc_ctn *ctn_first, X509 * if (!strcasecmp(ctn->fingerprint + 3, digest_sha256)) { /* we got ourselves a potential winner! */ - VRB(NULL, "Cert verify CTN: entry with a matching fingerprint found."); + VRB(session, "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(NULL, "Calculating SHA-384 digest failed (%s).", ERR_reason_error_string(ERR_get_error())); + ERR(session, "Calculating SHA-384 digest failed (%s).", ERR_reason_error_string(ERR_get_error())); ret = -1; goto cleanup; } @@ -426,15 +405,17 @@ nc_tls_cert_to_name(struct nc_session *session, struct nc_ctn *ctn_first, X509 * if (!strcasecmp(ctn->fingerprint + 3, digest_sha384)) { /* we got ourselves a potential winner! */ - VRB(NULL, "Cert verify CTN: entry with a matching fingerprint found."); + VRB(session, "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(NULL, "Calculating SHA-512 digest failed (%s).", ERR_reason_error_string(ERR_get_error())); + ERR(session, "Calculating SHA-512 digest failed (%s).", ERR_reason_error_string(ERR_get_error())); ret = -1; goto cleanup; } @@ -443,13 +424,15 @@ nc_tls_cert_to_name(struct nc_session *session, struct nc_ctn *ctn_first, X509 * if (!strcasecmp(ctn->fingerprint + 3, digest_sha512)) { /* we got ourselves a potential winner! */ - VRB(NULL, "Cert verify CTN: entry with a matching fingerprint found."); + VRB(session, "Cert verify CTN: entry with a matching fingerprint found."); map_type = ctn->map_type; } + free(digest_sha512); + digest_sha512 = NULL; /* unknown */ } else { - WRN(NULL, "Unknown fingerprint algorithm used (%s), skipping.", ctn->fingerprint); + WRN(session, "Unknown fingerprint algorithm used (%s), skipping.", ctn->fingerprint); continue; } @@ -458,11 +441,7 @@ nc_tls_cert_to_name(struct nc_session *session, struct nc_ctn *ctn_first, X509 * if (map_type == NC_TLS_CTN_SPECIFIED) { /* specified -> get username from the ctn entry */ session->username = strdup(ctn->name); - if (!session->username) { - ERRMEM; - ret = -1; - goto cleanup; - } + 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); @@ -499,229 +478,210 @@ nc_tls_cert_to_name(struct nc_session *session, struct nc_ctn *ctn_first, X509 * return ret; } -#if OPENSSL_VERSION_NUMBER >= 0x10100000L // >= 1.1.0 - static int -nc_tlsclb_verify(int preverify_ok, X509_STORE_CTX *x509_ctx) +nc_server_tls_check_crl(X509_STORE *crl_store, X509_STORE_CTX *x509_ctx, X509 *cert, + const X509_NAME *subject, const X509_NAME *issuer) { - X509_STORE_CTX *store_ctx; - X509_OBJECT *obj; - X509_NAME *subject; - X509_NAME *issuer; - X509 *cert; + int n, i, ret = 0; + X509_STORE_CTX *store_ctx = NULL; + X509_OBJECT *obj = NULL; X509_CRL *crl; X509_REVOKED *revoked; - - STACK_OF(X509) * cert_stack; EVP_PKEY *pubkey; - struct nc_session *session; - struct nc_server_tls_opts *opts; const ASN1_INTEGER *serial; - int i, n, rc, depth; - char *cp; const ASN1_TIME *last_update = NULL, *next_update = NULL; + char *cp; - /* get the thread session */ - session = pthread_getspecific(verify_key); - if (!session) { - ERRINT; - return 0; - } + 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); - opts = session->data; + /* 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; + } - /* 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); + /* check date of CRL to make sure it's not expired */ + if (!next_update) { + ERR(NULL, "Cert verify CRL: invalid nextUpdate field."); + X509_STORE_CTX_set_error(x509_ctx, X509_V_ERR_ERROR_IN_CRL_NEXT_UPDATE_FIELD); + ret = -1; + goto cleanup; + } + + if (X509_cmp_current_time(next_update) < 0) { + ERR(NULL, "Cert verify CRL: expired - revoking all certificates."); + X509_STORE_CTX_set_error(x509_ctx, X509_V_ERR_CRL_HAS_EXPIRED); + ret = -1; + goto cleanup; + } } - /* standard certificate verification failed, so a trusted client cert must match to continue */ - if (!preverify_ok) { - subject = X509_get_subject_name(session->opts.server.client_cert); - cert_stack = X509_STORE_CTX_get1_certs(x509_ctx, subject); - if (cert_stack) { - for (i = 0; i < sk_X509_num(cert_stack); ++i) { - if (cert_pubkey_match(session->opts.server.client_cert, sk_X509_value(cert_stack, i))) { - /* 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(NULL, "Cert verify: fail (%s), but the client 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); - sk_X509_pop_free(cert_stack, X509_free); - 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; } - sk_X509_pop_free(cert_stack, X509_free); } - - ERR(NULL, "Cert verify: fail (%s).", X509_verify_cert_error_string(X509_STORE_CTX_get_error(x509_ctx))); - return 0; } - /* print cert verify info */ - depth = X509_STORE_CTX_get_error_depth(x509_ctx); - VRB(NULL, "Cert verify: depth %d.", depth); - - 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(NULL, "Cert verify: subject: %s.", cp); - OPENSSL_free(cp); - cp = X509_NAME_oneline(issuer, NULL, 0); - VRB(NULL, "Cert verify: issuer: %s.", cp); - OPENSSL_free(cp); +cleanup: + X509_STORE_CTX_free(store_ctx); + X509_OBJECT_free(obj); + return ret; +} - /* check for revocation if set */ - if (opts->crl_store) { - /* 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) { - 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_get_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); - X509_OBJECT_free(obj); - if (pubkey) { - EVP_PKEY_free(pubkey); - } - return 0; - } - if (pubkey) { - EVP_PKEY_free(pubkey); - } +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; - /* 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); - X509_OBJECT_free(obj); - return 0; - } - 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); - X509_OBJECT_free(obj); - return 0; - } - X509_OBJECT_free(obj); - } + *certs = NULL; + *cert_count = 0; - /* 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); - 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); - X509_OBJECT_free(obj); - return 0; - } - } - X509_OBJECT_free(obj); + /* lookup name */ + for (i = 0; i < ts->cert_bag_count; i++) { + if (!strcmp(referenced_name, ts->cert_bags[i].name)) { + break; } } - /* cert-to-name already successful */ - if (session->username) { - return 1; + if (i == ts->cert_bag_count) { + ERR(NULL, "Truststore entry \"%s\" not found.", referenced_name); + return -1; } - /* cert-to-name */ - rc = nc_tls_cert_to_name(session, opts->ctn, cert); - if (rc) { - if (rc == -1) { - /* fatal error */ - depth = 0; - } - /* rc == 1 is a normal CTN fail (no match found) */ - goto fail; - } + *certs = ts->cert_bags[i].certs; + *cert_count = ts->cert_bags[i].cert_count; + return 0; +} - VRB(NULL, "Cert verify CTN: new client username recognized as \"%s\".", session->username); +/* 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) +{ + X509_STORE *store; + struct nc_cert_grouping *ee_certs; + int i, ret; + X509 *cert; + struct nc_certificate *certs; + uint16_t cert_count; - if (server_opts.user_verify_clb && !server_opts.user_verify_clb(session)) { - VRB(NULL, "Cert verify: user verify callback revoked authorization."); - X509_STORE_CTX_set_error(x509_ctx, X509_V_ERR_APPLICATION_VERIFICATION); - return 0; + 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; } - 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; + } -fail: - if (depth > 0) { - VRB(NULL, "Cert verify CTN: cert fail, cert-to-name will continue on the next cert in chain."); - return 1; + if (ee_certs->store == NC_STORE_LOCAL) { + /* local definition */ + certs = ee_certs->certs; + cert_count = ee_certs->cert_count; + } else { + /* truststore reference */ + if (nc_server_tls_ts_ref_get_certs(ee_certs->ts_ref, &certs, &cert_count)) { + ERR(NULL, "Error getting end-entity certificates from the truststore reference \"%s\".", ee_certs->ts_ref); + return -1; + } + } + + for (i = 0; i < cert_count; i++) { + cert = base64der_to_cert(certs[i].data); + ret = cert_pubkey_match(session->opts.server.client_cert, cert); + X509_free(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; + } } - VRB(NULL, "Cert-to-name unsuccessful, dropping the new client."); - X509_STORE_CTX_set_error(x509_ctx, X509_V_ERR_APPLICATION_VERIFICATION); return 0; } -#else - static int nc_tlsclb_verify(int preverify_ok, X509_STORE_CTX *x509_ctx) { - X509_STORE_CTX store_ctx; - X509_OBJECT obj; X509_NAME *subject; X509_NAME *issuer; X509 *cert; - X509_CRL *crl; - X509_REVOKED *revoked; + char *cp; + X509_STORE *store; STACK_OF(X509) * cert_stack; - EVP_PKEY *pubkey; struct nc_session *session; struct nc_server_tls_opts *opts; - long serial; - int i, n, rc, depth; - char *cp; - ASN1_TIME *last_update = NULL, *next_update = NULL; + 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 the thread session */ - session = pthread_getspecific(verify_key); + /* get session from the store */ + session = X509_STORE_get_ex_data(store, 0); if (!session) { - ERRINT; + ERR(session, "Error getting session from store (%s).", ERR_reason_error_string(ERR_get_error())); return 0; } @@ -730,32 +690,33 @@ nc_tlsclb_verify(int preverify_ok, X509_STORE_CTX *x509_ctx) /* 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); - while ((cert = sk_X509_pop(cert_stack))) { - X509_free(session->opts.server.client_cert); - session->opts.server.client_cert = cert; - } + session->opts.server.client_cert = sk_X509_value(cert_stack, 0); + X509_up_ref(session->opts.server.client_cert); sk_X509_pop_free(cert_stack, X509_free); } - /* standard certificate verification failed, so a trusted client cert must match to continue */ + /* standard certificate verification failed, so an end-entity client cert must match to continue */ if (!preverify_ok) { - subject = X509_get_subject_name(session->opts.server.client_cert); - cert_stack = X509_STORE_get1_certs(x509_ctx, subject); - if (cert_stack) { - for (i = 0; i < sk_X509_num(cert_stack); ++i) { - if (cert_pubkey_match(session->opts.server.client_cert, sk_X509_value(cert_stack, i))) { - /* 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 client 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); - sk_X509_pop_free(cert_stack, X509_free); - return 1; - } + /* check current endpoint's end-entity certs */ + rc = nc_server_tls_do_preverify(session, x509_ctx, 1); + if (rc == -1) { + return 0; + } else if (rc == 1) { + return 1; + } + + /* no match, continue */ + if (opts->referenced_endpt_name) { + /* check referenced endpoint's end-entity certs */ + rc = nc_server_tls_do_preverify(session, x509_ctx, 2); + if (rc == -1) { + return 0; + } else if (rc == 1) { + return 1; } - sk_X509_pop_free(cert_stack, X509_free); } + /* no match, fail */ ERR(session, "Cert verify: fail (%s).", X509_verify_cert_error_string(X509_STORE_CTX_get_error(x509_ctx))); return 0; } @@ -775,84 +736,11 @@ nc_tlsclb_verify(int preverify_ok, X509_STORE_CTX *x509_ctx) VRB(session, "Cert verify: issuer: %s.", cp); OPENSSL_free(cp); - /* check for revocation if set */ + /* check if the current certificate is revoked if CRL is set */ if (opts->crl_store) { - /* try to retrieve a CRL corresponding to the _subject_ of - * the current certificate in order to verify it's integrity */ - memset((char *)&obj, 0, sizeof(obj)); - 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_cleanup(&store_ctx); - crl = obj.data.crl; - if ((rc > 0) && crl) { - cp = X509_NAME_oneline(subject, NULL, 0); - VRB(session, "Cert verify CRL: issuer: %s.", cp); - OPENSSL_free(cp); - - last_update = X509_CRL_get_lastUpdate(crl); - next_update = X509_CRL_get_nextUpdate(crl); - cp = asn1time_to_str(last_update); - VRB(session, "Cert verify CRL: last update: %s.", cp); - free(cp); - cp = asn1time_to_str(next_update); - VRB(session, "Cert verify CRL: next update: %s.", cp); - free(cp); - - /* verify the signature on this CRL */ - pubkey = X509_get_pubkey(cert); - if (X509_CRL_verify(crl, pubkey) <= 0) { - ERR(session, "Cert verify CRL: invalid signature."); - X509_STORE_CTX_set_error(x509_ctx, X509_V_ERR_CRL_SIGNATURE_FAILURE); - X509_OBJECT_free_contents(&obj); - if (pubkey) { - EVP_PKEY_free(pubkey); - } - return 0; - } - if (pubkey) { - EVP_PKEY_free(pubkey); - } - - /* check date of CRL to make sure it's not expired */ - if (!next_update) { - ERR(session, "Cert verify CRL: invalid nextUpdate field."); - X509_STORE_CTX_set_error(x509_ctx, X509_V_ERR_ERROR_IN_CRL_NEXT_UPDATE_FIELD); - X509_OBJECT_free_contents(&obj); - return 0; - } - if (X509_cmp_current_time(next_update) < 0) { - ERR(session, "Cert verify CRL: expired - revoking all certificates."); - X509_STORE_CTX_set_error(x509_ctx, X509_V_ERR_CRL_HAS_EXPIRED); - X509_OBJECT_free_contents(&obj); - return 0; - } - X509_OBJECT_free_contents(&obj); - } - - /* try to retrieve a CRL corresponding to the _issuer_ of - * the current certificate in order to check for revocation */ - memset((char *)&obj, 0, sizeof(obj)); - 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_cleanup(&store_ctx); - crl = obj.data.crl; - 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(revoked->serialNumber, X509_get_serialNumber(cert)) == 0) { - serial = ASN1_INTEGER_get(revoked->serialNumber); - cp = X509_NAME_oneline(issuer, NULL, 0); - ERR(session, "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); - X509_OBJECT_free_contents(&obj); - return 0; - } - } - X509_OBJECT_free_contents(&obj); + rc = nc_server_tls_check_crl(opts->crl_store, x509_ctx, cert, subject, issuer); + if (rc) { + return 0; } } @@ -863,13 +751,31 @@ nc_tlsclb_verify(int preverify_ok, X509_STORE_CTX *x509_ctx) /* cert-to-name */ rc = nc_tls_cert_to_name(session, opts->ctn, cert); - if (rc) { - if (rc == -1) { + 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 */ + if (nc_server_get_referenced_endpt(opts->referenced_endpt_name, &referenced_endpt)) { /* fatal error */ + ERRINT; depth = 0; + goto fail; + } + + 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; } - /* rc == 1 is a normal CTN fail (no match found) */ - goto fail; } VRB(session, "Cert verify CTN: new client username recognized as \"%s\".", session->username); @@ -893,975 +799,487 @@ nc_tlsclb_verify(int preverify_ok, X509_STORE_CTX *x509_ctx) return 0; } -#endif - -static int -nc_server_tls_set_server_cert(const char *name, struct nc_server_tls_opts *opts) +API const X509 * +nc_session_get_client_cert(const struct nc_session *session) { - if (!name) { - if (opts->server_cert) { - free(opts->server_cert); - } - opts->server_cert = NULL; - return 0; + if (!session || (session->side != NC_SERVER)) { + ERRARG(session, "session"); + return NULL; } - if (opts->server_cert) { - free(opts->server_cert); - } - opts->server_cert = strdup(name); + return session->opts.server.client_cert; +} - return 0; +API void +nc_server_tls_set_verify_clb(int (*verify_clb)(const struct nc_session *session)) +{ + server_opts.user_verify_clb = verify_clb; } -API int -nc_server_tls_endpt_set_server_cert(const char *endpt_name, const char *name) +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) { - int ret; - struct nc_endpt *endpt; + uint16_t i, j; + struct nc_keystore *ks = &server_opts.keystore; - if (!endpt_name) { - ERRARG("endpt_name"); + *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; } - /* LOCK */ - endpt = nc_server_endpt_lock_get(endpt_name, NC_TI_OPENSSL, NULL); - if (!endpt) { + 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; } - ret = nc_server_tls_set_server_cert(name, endpt->opts.tls); - /* UNLOCK */ - pthread_rwlock_unlock(&server_opts.endpt_lock); - return ret; + *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; } -API int -nc_server_tls_ch_client_endpt_set_server_cert(const char *client_name, const char *endpt_name, const char *name) +static int +nc_tls_ctx_set_server_cert_key(SSL_CTX *tls_ctx, struct nc_server_tls_opts *opts) { - int ret; - struct nc_ch_client *client; - struct nc_ch_endpt *endpt; - - /* LOCK */ - endpt = nc_server_ch_client_lock(client_name, endpt_name, NC_TI_OPENSSL, &client); - if (!endpt) { - return -1; - } + char *privkey_data = NULL, *cert_data = NULL; + int ret = 0; + NC_PRIVKEY_FORMAT privkey_type; + X509 *cert = NULL; + EVP_PKEY *pkey = NULL; - ret = nc_server_tls_set_server_cert(name, endpt->opts.tls); + NC_CHECK_ARG_RET(NULL, tls_ctx, opts, -1); - /* UNLOCK */ - nc_server_ch_client_unlock(client); + /* get data needed for setting the server cert */ + if (opts->store == NC_STORE_LOCAL) { + /* local definition */ + cert_data = opts->cert_data; + privkey_data = opts->privkey_data; + 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) { + ERR(NULL, "Getting server certificate from the keystore reference \"%s\" failed.", opts->key_ref); + return -1; + } + } - return ret; -} + /* load the cert */ + cert = base64der_to_cert(cert_data); + if (!cert) { + ERR(NULL, "Converting certificate data to certificate format failed."); + ret = -1; + goto cleanup; + } -API void -nc_server_tls_set_server_cert_clb(int (*cert_clb)(const char *name, void *user_data, char **cert_path, char **cert_data, - char **privkey_path, char **privkey_data, NC_SSH_KEY_TYPE *privkey_type), void *user_data, - void (*free_user_data)(void *user_data)) -{ - if (!cert_clb) { - ERRARG("cert_clb"); - return; + /* 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; } - server_opts.server_cert_clb = cert_clb; - server_opts.server_cert_data = user_data; - server_opts.server_cert_data_free = free_user_data; -} + /* load the private key */ + pkey = base64der_to_privatekey(privkey_data, nc_privkey_format_to_str(privkey_type)); + if (!pkey) { + ERR(NULL, "Converting private key data to private key format failed."); + ret = -1; + goto cleanup; + } -API void -nc_server_tls_set_server_cert_chain_clb(int (*cert_chain_clb)(const char *name, void *user_data, char ***cert_paths, - int *cert_path_count, char ***cert_data, int *cert_data_count), void *user_data, void (*free_user_data)(void *user_data)) -{ - if (!cert_chain_clb) { - ERRARG("cert_chain_clb"); - return; + /* 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; } - server_opts.server_cert_chain_clb = cert_chain_clb; - server_opts.server_cert_chain_data = user_data; - server_opts.server_cert_chain_data_free = free_user_data; + ret = 0; + +cleanup: + X509_free(cert); + EVP_PKEY_free(pkey); + return ret; } static int -nc_server_tls_add_trusted_cert_list(const char *name, struct nc_server_tls_opts *opts) +tls_store_add_trusted_cert(X509_STORE *cert_store, const char *cert_data) { - if (!name) { - ERRARG("name"); + 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; } - ++opts->trusted_cert_list_count; - opts->trusted_cert_lists = nc_realloc(opts->trusted_cert_lists, - opts->trusted_cert_list_count * sizeof *opts->trusted_cert_lists); - if (!opts->trusted_cert_lists) { - ERRMEM; + /* 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; } - opts->trusted_cert_lists[opts->trusted_cert_list_count - 1] = strdup(name); + X509_free(cert); return 0; } -API int -nc_server_tls_endpt_add_trusted_cert_list(const char *endpt_name, const char *name) +static int +nc_tls_store_set_trusted_certs(X509_STORE *cert_store, struct nc_cert_grouping *ca_certs) { - int ret; - struct nc_endpt *endpt; + uint16_t i; + struct nc_certificate *certs; + uint16_t cert_count; - if (!endpt_name) { - ERRARG("endpt_name"); - return -1; + 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; + } } - /* LOCK */ - endpt = nc_server_endpt_lock_get(endpt_name, NC_TI_OPENSSL, NULL); - if (!endpt) { - return -1; + for (i = 0; i < cert_count; i++) { + if (tls_store_add_trusted_cert(cert_store, certs[i].data)) { + return -1; + } } - ret = nc_server_tls_add_trusted_cert_list(name, endpt->opts.tls); - /* UNLOCK */ - pthread_rwlock_unlock(&server_opts.endpt_lock); - return ret; + return 0; } -API int -nc_server_tls_ch_client_endpt_add_trusted_cert_list(const char *client_name, const char *endpt_name, const char *name) +static int +nc_server_tls_crl_path(struct nc_session *session, const char *crl_path, X509_STORE *store) { - int ret; - struct nc_ch_client *client; - struct nc_ch_endpt *endpt; + int ret = 0; + X509_CRL *crl = NULL; + FILE *f; - /* LOCK */ - endpt = nc_server_ch_client_lock(client_name, endpt_name, NC_TI_OPENSSL, &client); - if (!endpt) { + f = fopen(crl_path, "r"); + if (!f) { + ERR(session, "Unable to open CRL file \"%s\".", crl_path); return -1; } - ret = nc_server_tls_add_trusted_cert_list(name, endpt->opts.tls); - - /* UNLOCK */ - nc_server_ch_client_unlock(client); - - return ret; -} - -API void -nc_server_tls_set_trusted_cert_list_clb(int (*cert_list_clb)(const char *name, void *user_data, char ***cert_paths, - int *cert_path_count, char ***cert_data, int *cert_data_count), - void *user_data, void (*free_user_data)(void *user_data)) -{ - if (!cert_list_clb) { - ERRARG("cert_list_clb"); - return; + /* try DER first */ + crl = d2i_X509_CRL_fp(f, NULL); + if (crl) { + /* success */ + goto ok; } - server_opts.trusted_cert_list_clb = cert_list_clb; - server_opts.trusted_cert_list_data = user_data; - server_opts.trusted_cert_list_data_free = free_user_data; -} - -static int -nc_server_tls_del_trusted_cert_list(const char *name, struct nc_server_tls_opts *opts) -{ - uint16_t i; - - if (!name) { - for (i = 0; i < opts->trusted_cert_list_count; ++i) { - free(opts->trusted_cert_lists[i]); - } - free(opts->trusted_cert_lists); - opts->trusted_cert_lists = NULL; - opts->trusted_cert_list_count = 0; - return 0; - } else { - for (i = 0; i < opts->trusted_cert_list_count; ++i) { - if (!strcmp(opts->trusted_cert_lists[i], name)) { - free(opts->trusted_cert_lists[i]); - - --opts->trusted_cert_list_count; - if (i < opts->trusted_cert_list_count - 1) { - memmove(opts->trusted_cert_lists + i, opts->trusted_cert_lists + i + 1, - (opts->trusted_cert_list_count - i) * sizeof *opts->trusted_cert_lists); - } - return 0; - } - } + /* 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; } - ERR(NULL, "Certificate list \"%s\" not found.", name); - return -1; -} - -API int -nc_server_tls_endpt_del_trusted_cert_list(const char *endpt_name, const char *name) -{ - int ret; - struct nc_endpt *endpt; - - if (!endpt_name) { - ERRARG("endpt_name"); - return -1; - } - - /* LOCK */ - endpt = nc_server_endpt_lock_get(endpt_name, NC_TI_OPENSSL, NULL); - if (!endpt) { - return -1; +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; } - ret = nc_server_tls_del_trusted_cert_list(name, endpt->opts.tls); - /* UNLOCK */ - pthread_rwlock_unlock(&server_opts.endpt_lock); + /* ok */ + ret = 0; +cleanup: + fclose(f); + X509_CRL_free(crl); return ret; } -API int -nc_server_tls_ch_client_endpt_del_trusted_cert_list(const char *client_name, const char *endpt_name, const char *name) +static size_t +nc_server_tls_curl_cb(char *ptr, size_t size, size_t nmemb, void *userdata) { - int ret; - struct nc_ch_client *client; - struct nc_ch_endpt *endpt; - - /* LOCK */ - endpt = nc_server_ch_client_lock(client_name, endpt_name, NC_TI_OPENSSL, &client); - if (!endpt) { - return -1; - } - - ret = nc_server_tls_del_trusted_cert_list(name, endpt->opts.tls); + struct nc_curl_data *data; - /* UNLOCK */ - nc_server_ch_client_unlock(client); + size = nmemb; - return ret; -} - -static int -nc_server_tls_set_trusted_ca_paths(const char *ca_file, const char *ca_dir, struct nc_server_tls_opts *opts) -{ - if (!ca_file && !ca_dir) { - ERRARG("ca_file and ca_dir"); - return -1; - } + data = (struct nc_curl_data *)userdata; - if (ca_file) { - free(opts->trusted_ca_file); - opts->trusted_ca_file = strdup(ca_file); - } + data->data = nc_realloc(data->data, data->size + size); + NC_CHECK_ERRMEM_RET(!data->data, 0); - if (ca_dir) { - free(opts->trusted_ca_dir); - opts->trusted_ca_dir = strdup(ca_dir); - } + memcpy(&data->data[data->size], ptr, size); + data->size += size; - return 0; -} - -API int -nc_server_tls_endpt_set_trusted_ca_paths(const char *endpt_name, const char *ca_file, const char *ca_dir) -{ - int ret; - struct nc_endpt *endpt; - - if (!endpt_name) { - ERRARG("endpt_name"); - return -1; - } - - /* LOCK */ - endpt = nc_server_endpt_lock_get(endpt_name, NC_TI_OPENSSL, NULL); - if (!endpt) { - return -1; - } - ret = nc_server_tls_set_trusted_ca_paths(ca_file, ca_dir, endpt->opts.tls); - /* UNLOCK */ - pthread_rwlock_unlock(&server_opts.endpt_lock); - - return ret; -} - -API int -nc_server_tls_ch_client_endpt_set_trusted_ca_paths(const char *client_name, const char *endpt_name, const char *ca_file, - const char *ca_dir) -{ - int ret; - struct nc_ch_client *client; - struct nc_ch_endpt *endpt; - - /* LOCK */ - endpt = nc_server_ch_client_lock(client_name, endpt_name, NC_TI_OPENSSL, &client); - if (!endpt) { - return -1; - } - - ret = nc_server_tls_set_trusted_ca_paths(ca_file, ca_dir, endpt->opts.tls); - - /* UNLOCK */ - nc_server_ch_client_unlock(client); - - return ret; + return size; } static int -nc_server_tls_set_crl_paths(const char *crl_file, const char *crl_dir, struct nc_server_tls_opts *opts) +nc_server_tls_curl_init(struct nc_session *session, CURL **handle, struct nc_curl_data *data) { - X509_LOOKUP *lookup; - - if (!crl_file && !crl_dir) { - ERRARG("crl_file and crl_dir"); - return -1; - } + NC_CHECK_ARG_RET(session, handle, data, -1); - if (!opts->crl_store) { - opts->crl_store = X509_STORE_new(); - } - - if (crl_file) { - lookup = X509_STORE_add_lookup(opts->crl_store, X509_LOOKUP_file()); - if (!lookup) { - ERR(NULL, "Failed to add a lookup method."); - goto fail; - } - - if (X509_LOOKUP_load_file(lookup, crl_file, X509_FILETYPE_PEM) != 1) { - ERR(NULL, "Failed to add a revocation lookup file (%s).", ERR_reason_error_string(ERR_get_error())); - goto fail; - } - } - - if (crl_dir) { - lookup = X509_STORE_add_lookup(opts->crl_store, X509_LOOKUP_hash_dir()); - if (!lookup) { - ERR(NULL, "Failed to add a lookup method."); - goto fail; - } - - if (X509_LOOKUP_add_dir(lookup, crl_dir, X509_FILETYPE_PEM) != 1) { - ERR(NULL, "Failed to add a revocation lookup directory (%s).", ERR_reason_error_string(ERR_get_error())); - goto fail; - } - } - - return 0; - -fail: - return -1; -} + *handle = NULL; -API int -nc_server_tls_endpt_set_crl_paths(const char *endpt_name, const char *crl_file, const char *crl_dir) -{ - int ret; - struct nc_endpt *endpt; - - if (!endpt_name) { - ERRARG("endpt_name"); + *handle = curl_easy_init(); + if (!*handle) { + ERR(session, "Initializing CURL failed."); return -1; } - /* LOCK */ - endpt = nc_server_endpt_lock_get(endpt_name, NC_TI_OPENSSL, NULL); - if (!endpt) { + if (curl_easy_setopt(*handle, CURLOPT_WRITEFUNCTION, nc_server_tls_curl_cb)) { + ERR(session, "Setting curl callback failed."); return -1; } - ret = nc_server_tls_set_crl_paths(crl_file, crl_dir, endpt->opts.tls); - /* UNLOCK */ - pthread_rwlock_unlock(&server_opts.endpt_lock); - - return ret; -} - -API int -nc_server_tls_ch_client_set_crl_paths(const char *client_name, const char *endpt_name, const char *crl_file, - const char *crl_dir) -{ - int ret; - struct nc_ch_client *client; - struct nc_ch_endpt *endpt; - /* LOCK */ - endpt = nc_server_ch_client_lock(client_name, endpt_name, NC_TI_OPENSSL, &client); - if (!endpt) { + if (curl_easy_setopt(*handle, CURLOPT_WRITEDATA, data)) { + ERR(session, "Setting curl callback data failed."); return -1; } - ret = nc_server_tls_set_crl_paths(crl_file, crl_dir, endpt->opts.tls); - - /* UNLOCK */ - nc_server_ch_client_unlock(client); - - return ret; -} - -static void -nc_server_tls_clear_crls(struct nc_server_tls_opts *opts) -{ - if (!opts->crl_store) { - return; - } - - X509_STORE_free(opts->crl_store); - opts->crl_store = NULL; -} - -API void -nc_server_tls_endpt_clear_crls(const char *endpt_name) -{ - struct nc_endpt *endpt; - - if (!endpt_name) { - ERRARG("endpt_name"); - return; - } - - /* LOCK */ - endpt = nc_server_endpt_lock_get(endpt_name, NC_TI_OPENSSL, NULL); - if (!endpt) { - return; - } - nc_server_tls_clear_crls(endpt->opts.tls); - /* UNLOCK */ - pthread_rwlock_unlock(&server_opts.endpt_lock); -} - -API void -nc_server_tls_ch_client_endpt_clear_crls(const char *client_name, const char *endpt_name) -{ - struct nc_ch_client *client; - struct nc_ch_endpt *endpt; - - /* LOCK */ - endpt = nc_server_ch_client_lock(client_name, endpt_name, NC_TI_OPENSSL, &client); - if (!endpt) { - return; - } - - nc_server_tls_clear_crls(endpt->opts.tls); - - /* UNLOCK */ - nc_server_ch_client_unlock(client); -} - -static int -nc_server_tls_add_ctn(uint32_t id, const char *fingerprint, NC_TLS_CTN_MAPTYPE map_type, const char *name, - struct nc_server_tls_opts *opts) -{ - struct nc_ctn *ctn, *new; - - if (!opts->ctn) { - /* the first item */ - opts->ctn = new = calloc(1, sizeof *new); - if (!new) { - ERRMEM; - return -1; - } - } else if (opts->ctn->id > id) { - /* insert at the beginning */ - new = calloc(1, sizeof *new); - if (!new) { - ERRMEM; - return -1; - } - new->next = opts->ctn; - opts->ctn = new; - } else { - for (ctn = opts->ctn; ctn->next && ctn->next->id <= id; ctn = ctn->next) {} - if (ctn->id == id) { - /* it exists already */ - new = ctn; - } else { - /* insert after ctn */ - new = calloc(1, sizeof *new); - if (!new) { - ERRMEM; - return -1; - } - new->next = ctn->next; - ctn->next = new; - } - } - - new->id = id; - if (fingerprint) { - free(new->fingerprint); - new->fingerprint = strdup(fingerprint); - } - if (map_type) { - new->map_type = map_type; - } - if (name) { - free(new->name); - new->name = strdup(name); - } - return 0; } -API int -nc_server_tls_endpt_add_ctn(const char *endpt_name, uint32_t id, const char *fingerprint, NC_TLS_CTN_MAPTYPE map_type, - const char *name) +static int +nc_server_tls_curl_fetch(struct nc_session *session, CURL *handle, const char *url) { - int ret; - struct nc_endpt *endpt; - - if (!endpt_name) { - ERRARG("endpt_name"); - return -1; - } + char err_buf[CURL_ERROR_SIZE]; - /* LOCK */ - endpt = nc_server_endpt_lock_get(endpt_name, NC_TI_OPENSSL, NULL); - if (!endpt) { + /* set uri */ + if (curl_easy_setopt(handle, CURLOPT_URL, url)) { + ERR(session, "Setting URI \"%s\" to download CRL from failed.", url); return -1; } - ret = nc_server_tls_add_ctn(id, fingerprint, map_type, name, endpt->opts.tls); - /* UNLOCK */ - pthread_rwlock_unlock(&server_opts.endpt_lock); - - return ret; -} -API int -nc_server_tls_ch_client_endpt_add_ctn(const char *client_name, const char *endpt_name, uint32_t id, - const char *fingerprint, NC_TLS_CTN_MAPTYPE map_type, const char *name) -{ - int ret; - struct nc_ch_client *client; - struct nc_ch_endpt *endpt; + /* set err buf */ + curl_easy_setopt(handle, CURLOPT_ERRORBUFFER, err_buf); - /* LOCK */ - endpt = nc_server_ch_client_lock(client_name, endpt_name, NC_TI_OPENSSL, &client); - if (!endpt) { + /* download */ + if (curl_easy_perform(handle)) { + ERR(session, "Downloading CRL from \"%s\" failed (%s).", url, err_buf); return -1; } - ret = nc_server_tls_add_ctn(id, fingerprint, map_type, name, endpt->opts.tls); - - /* UNLOCK */ - nc_server_ch_client_unlock(client); - - return ret; + return 0; } static int -nc_server_tls_del_ctn(int64_t id, const char *fingerprint, NC_TLS_CTN_MAPTYPE map_type, const char *name, - struct nc_server_tls_opts *opts) +nc_server_tls_add_crl_to_store(struct nc_session *session, struct nc_curl_data *downloaded, X509_STORE *store) { - struct nc_ctn *ctn, *next, *prev; - int ret = -1; - - if ((id < 0) && !fingerprint && !map_type && !name) { - ctn = opts->ctn; - while (ctn) { - free(ctn->fingerprint); - free(ctn->name); - - next = ctn->next; - free(ctn); - ctn = next; - - ret = 0; - } - opts->ctn = NULL; - } else { - prev = NULL; - ctn = opts->ctn; - while (ctn) { - if (((id < 0) || (ctn->id == id)) && - (!fingerprint || !strcmp(ctn->fingerprint, fingerprint)) && - (!map_type || (ctn->map_type == map_type)) && - (!name || (ctn->name && !strcmp(ctn->name, name)))) { - free(ctn->fingerprint); - free(ctn->name); - - if (prev) { - prev->next = ctn->next; - next = ctn->next; - } else { - opts->ctn = ctn->next; - next = ctn->next; - } - free(ctn); - ctn = next; + int ret = 0; + X509_CRL *crl = NULL; + BIO *bio = NULL; - ret = 0; - } else { - prev = ctn; - ctn = ctn->next; - } - } + /* try DER first */ + crl = d2i_X509_CRL(NULL, (const unsigned char **) &downloaded->data, downloaded->size); + if (crl) { + /* it was DER */ + goto ok; } - return ret; -} - -API int -nc_server_tls_endpt_del_ctn(const char *endpt_name, int64_t id, const char *fingerprint, NC_TLS_CTN_MAPTYPE map_type, - const char *name) -{ - int ret; - struct nc_endpt *endpt; - - if (!endpt_name) { - ERRARG("endpt_name"); - return -1; + /* 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; } - /* LOCK */ - endpt = nc_server_endpt_lock_get(endpt_name, NC_TI_OPENSSL, NULL); - if (!endpt) { - 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; } - ret = nc_server_tls_del_ctn(id, fingerprint, map_type, name, endpt->opts.tls); - /* UNLOCK */ - pthread_rwlock_unlock(&server_opts.endpt_lock); - - return ret; -} -API int -nc_server_tls_ch_client_endpt_del_ctn(const char *client_name, const char *endpt_name, int64_t id, - const char *fingerprint, NC_TLS_CTN_MAPTYPE map_type, const char *name) -{ - int ret; - struct nc_ch_client *client; - struct nc_ch_endpt *endpt; - - /* LOCK */ - endpt = nc_server_ch_client_lock(client_name, endpt_name, NC_TI_OPENSSL, &client); - if (!endpt) { - 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; } + /* ok */ + ret = 0; - ret = nc_server_tls_del_ctn(id, fingerprint, map_type, name, endpt->opts.tls); - - /* UNLOCK */ - nc_server_ch_client_unlock(client); - +cleanup: + X509_CRL_free(crl); + BIO_free(bio); return ret; } static int -nc_server_tls_get_ctn(uint32_t *id, char **fingerprint, NC_TLS_CTN_MAPTYPE *map_type, char **name, - struct nc_server_tls_opts *opts) +nc_server_tls_crl_url(struct nc_session *session, const char *url, X509_STORE *store) { - struct nc_ctn *ctn; - int ret = -1; - - for (ctn = opts->ctn; ctn; ctn = ctn->next) { - if (id && *id && (*id != ctn->id)) { - continue; - } - if (fingerprint && *fingerprint && (!ctn->fingerprint || strcmp(*fingerprint, ctn->fingerprint))) { - continue; - } - if (map_type && *map_type && (!ctn->map_type || (*map_type != ctn->map_type))) { - continue; - } - if (name && *name && (!ctn->name || strcmp(*name, ctn->name))) { - continue; - } - - /* first match, good enough */ - if (id && !(*id)) { - *id = ctn->id; - } - if (fingerprint && !(*fingerprint) && ctn->fingerprint) { - *fingerprint = strdup(ctn->fingerprint); - } - if (map_type && !(*map_type) && ctn->map_type) { - *map_type = ctn->map_type; - } - if (name && !(*name) && ctn->name) { - *name = strdup(ctn->name); - } + int ret = 0; + CURL *handle = NULL; + struct nc_curl_data downloaded = {0}; - ret = 0; - break; + /* init curl */ + ret = nc_server_tls_curl_init(session, &handle, &downloaded); + if (ret) { + goto cleanup; } - return ret; -} - -API int -nc_server_tls_endpt_get_ctn(const char *endpt_name, uint32_t *id, char **fingerprint, NC_TLS_CTN_MAPTYPE *map_type, - char **name) -{ - int ret; - struct nc_endpt *endpt; - - if (!endpt_name) { - ERRARG("endpt_name"); - return -1; - } + VRB(session, "Downloading CRL from \"%s\".", url); - /* LOCK */ - endpt = nc_server_endpt_lock_get(endpt_name, NC_TI_OPENSSL, NULL); - if (!endpt) { - return -1; + /* download the CRL */ + ret = nc_server_tls_curl_fetch(session, handle, url); + if (ret) { + goto cleanup; } - ret = nc_server_tls_get_ctn(id, fingerprint, map_type, name, endpt->opts.tls); - /* UNLOCK */ - pthread_rwlock_unlock(&server_opts.endpt_lock); - - return ret; -} -API int -nc_server_tls_ch_client_endpt_get_ctn(const char *client_name, const char *endpt_name, uint32_t *id, char **fingerprint, - NC_TLS_CTN_MAPTYPE *map_type, char **name) -{ - int ret; - struct nc_ch_client *client; - struct nc_ch_endpt *endpt; - - /* LOCK */ - endpt = nc_server_ch_client_lock(client_name, endpt_name, NC_TI_OPENSSL, &client); - if (!endpt) { - return -1; + /* convert the downloaded data to CRL and add it to the store */ + ret = nc_server_tls_add_crl_to_store(session, &downloaded, store); + if (ret) { + goto cleanup; } - ret = nc_server_tls_get_ctn(id, fingerprint, map_type, name, endpt->opts.tls); - - /* UNLOCK */ - nc_server_ch_client_unlock(client); - +cleanup: + curl_easy_cleanup(handle); return ret; } -API const X509 * -nc_session_get_client_cert(const struct nc_session *session) -{ - if (!session || (session->side != NC_SERVER)) { - ERRARG("session"); - return NULL; - } - - return session->opts.server.client_cert; -} - -API void -nc_server_tls_set_verify_clb(int (*verify_clb)(const struct nc_session *session)) -{ - server_opts.user_verify_clb = verify_clb; -} - -void -nc_server_tls_clear_opts(struct nc_server_tls_opts *opts) -{ - free(opts->server_cert); - nc_server_tls_del_trusted_cert_list(NULL, opts); - free(opts->trusted_ca_file); - free(opts->trusted_ca_dir); - nc_server_tls_clear_crls(opts); - nc_server_tls_del_ctn(-1, NULL, 0, NULL, opts); -} - -static void -nc_tls_make_verify_key(void) +static int +nc_server_tls_crl_cert_ext(struct nc_session *session, X509_STORE *cert_store, X509_STORE *crl_store) { - pthread_key_create(&verify_key, NULL); -} + int ret = 0, i, j, k, gtype; + CURL *handle = NULL; + struct nc_curl_data downloaded = {0}; -static X509 * -tls_load_cert(const char *cert_path, const char *cert_data) -{ + STACK_OF(X509_OBJECT) * objs; + X509_OBJECT *obj; X509 *cert; - if (cert_path) { - cert = pem_to_cert(cert_path); - } else { - cert = base64der_to_cert(cert_data); - } - - if (!cert) { - if (cert_path) { - ERR(NULL, "Loading a trusted certificate (path \"%s\") failed (%s).", cert_path, - ERR_reason_error_string(ERR_get_error())); - } else { - ERR(NULL, "Loading a trusted certificate (data \"%s\") failed (%s).", cert_data, - ERR_reason_error_string(ERR_get_error())); + STACK_OF(DIST_POINT) * dist_points; + DIST_POINT *dist_point; + GENERAL_NAMES *general_names; + GENERAL_NAME *general_name; + ASN1_STRING *asn_string_uri; + const char *crl_distpoint_uri; + + /* init curl */ + ret = nc_server_tls_curl_init(session, &handle, &downloaded); + if (ret) { + goto cleanup; + } + + /* treat all entries in the cert_store as X509_OBJECTs */ + objs = X509_STORE_get0_objects(cert_store); + if (!objs) { + ERR(session, "Getting certificates from store failed (%s).", ERR_reason_error_string(ERR_get_error())); + ret = -1; + goto cleanup; + } + + /* iterate over all the CAs */ + for (i = 0; i < sk_X509_OBJECT_num(objs); i++) { + obj = sk_X509_OBJECT_value(objs, i); + cert = X509_OBJECT_get0_X509(obj); + if (!cert) { + /* the object on this index was not a certificate */ + continue; } - } - return cert; -} - -static int -nc_tls_ctx_set_server_cert_chain(SSL_CTX *tls_ctx, const char *cert_name) -{ - char **cert_paths = NULL, **cert_data = NULL; - int cert_path_count = 0, cert_data_count = 0, ret = 0, i = 0; - X509 *cert = NULL; - - if (!server_opts.server_cert_chain_clb) { - /* This is optional, so return OK */ - return 0; - } - if (server_opts.server_cert_chain_clb(cert_name, server_opts.server_cert_chain_data, &cert_paths, - &cert_path_count, &cert_data, &cert_data_count)) { - ERR(NULL, "Server certificate chain callback failed."); - return -1; - } + /* get all the distribution points for this CA */ + dist_points = X509_get_ext_d2i(cert, NID_crl_distribution_points, NULL, NULL); - for (i = 0; i < cert_path_count; ++i) { - cert = tls_load_cert(cert_paths[i], NULL); - if (!cert || (SSL_CTX_add_extra_chain_cert(tls_ctx, cert) != 1)) { - ERR(NULL, "Loading the server certificate chain failed (%s).", ERR_reason_error_string(ERR_get_error())); - ret = -1; - goto cleanup; - } - } + /* 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; - for (i = 0; i < cert_data_count; ++i) { - cert = tls_load_cert(NULL, cert_data[i]); - if (!cert || (SSL_CTX_add_extra_chain_cert(tls_ctx, cert) != 1)) { - ERR(NULL, "Loading the server certificate chain failed (%s).", ERR_reason_error_string(ERR_get_error())); - ret = -1; - goto cleanup; - } - } -cleanup: - for (i = 0; i < cert_path_count; ++i) { - free(cert_paths[i]); - } - free(cert_paths); - for (i = 0; i < cert_data_count; ++i) { - free(cert_data[i]); - } - free(cert_data); - /* cert is owned by the SSL_CTX */ + /* 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); - return ret; -} + /* 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; + } -static int -nc_tls_ctx_set_server_cert_key(SSL_CTX *tls_ctx, const char *cert_name) -{ - char *cert_path = NULL, *cert_data = NULL, *privkey_path = NULL, *privkey_data = NULL; - int ret = 0; - NC_SSH_KEY_TYPE privkey_type; - X509 *cert = NULL; - EVP_PKEY *pkey = NULL; + crl_distpoint_uri = (const char *) ASN1_STRING_get0_data(asn_string_uri); - if (!cert_name) { - ERR(NULL, "Server certificate not set."); - return -1; - } else if (!server_opts.server_cert_clb) { - ERR(NULL, "Callback for retrieving the server certificate is not set."); - return -1; - } + VRB(session, "Downloading CRL from \"%s\".", crl_distpoint_uri); - if (server_opts.server_cert_clb(cert_name, server_opts.server_cert_data, &cert_path, &cert_data, &privkey_path, - &privkey_data, &privkey_type)) { - ERR(NULL, "Server certificate callback failed."); - return -1; - } + /* 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; + } - /* load the certificate */ - if (cert_path) { - if (SSL_CTX_use_certificate_file(tls_ctx, cert_path, SSL_FILETYPE_PEM) != 1) { - ERR(NULL, "Loading the server certificate failed (%s).", ERR_reason_error_string(ERR_get_error())); - ret = -1; - goto cleanup; - } - } else { - cert = base64der_to_cert(cert_data); - if (!cert || (SSL_CTX_use_certificate(tls_ctx, cert) != 1)) { - ERR(NULL, "Loading the server certificate failed (%s).", ERR_reason_error_string(ERR_get_error())); - ret = -1; - goto cleanup; - } - } + /* 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; + } - /* load the private key */ - if (privkey_path) { - if (SSL_CTX_use_PrivateKey_file(tls_ctx, privkey_path, SSL_FILETYPE_PEM) != 1) { - ERR(NULL, "Loading the server private key failed (%s).", ERR_reason_error_string(ERR_get_error())); - ret = -1; - goto cleanup; - } - } else { - pkey = base64der_to_privatekey(privkey_data, nc_keytype2str(privkey_type)); - if (!pkey || (SSL_CTX_use_PrivateKey(tls_ctx, pkey) != 1)) { - ERR(NULL, "Loading the server private key failed (%s).", ERR_reason_error_string(ERR_get_error())); - ret = -1; - goto cleanup; + /* the CRL was downloaded, no need to download it again using different protocol */ + break; + } } } - ret = nc_tls_ctx_set_server_cert_chain(tls_ctx, cert_name); - cleanup: - X509_free(cert); - EVP_PKEY_free(pkey); - free(cert_path); - free(cert_data); - free(privkey_path); - free(privkey_data); + curl_easy_cleanup(handle); return ret; } -static void -tls_store_add_trusted_cert(X509_STORE *cert_store, const char *cert_path, const char *cert_data) -{ - X509 *cert = tls_load_cert(cert_path, cert_data); - - if (!cert) { - return; - } - - /* 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; - } - X509_free(cert); -} - static int -nc_tls_store_set_trusted_certs(X509_STORE *cert_store, char **trusted_cert_lists, uint16_t trusted_cert_list_count) +nc_tls_store_set_crl(struct nc_session *session, struct nc_server_tls_opts *opts, X509_STORE *store) { - uint16_t i; - int j; - char **cert_paths, **cert_data; - int cert_path_count, cert_data_count; - - if (!server_opts.trusted_cert_list_clb) { - ERR(NULL, "Callback for retrieving trusted certificate lists is not set."); - return -1; + if (!opts->crl_store) { + /* first call on this endpoint */ + opts->crl_store = X509_STORE_new(); + NC_CHECK_ERRMEM_GOTO(!opts->crl_store, , fail); } - for (i = 0; i < trusted_cert_list_count; ++i) { - cert_paths = cert_data = NULL; - cert_path_count = cert_data_count = 0; - if (server_opts.trusted_cert_list_clb(trusted_cert_lists[i], server_opts.trusted_cert_list_data, - &cert_paths, &cert_path_count, &cert_data, &cert_data_count)) { - ERR(NULL, "Trusted certificate list callback for \"%s\" failed.", trusted_cert_lists[i]); - return -1; + if (opts->crl_path) { + if (nc_server_tls_crl_path(session, opts->crl_path, opts->crl_store)) { + goto fail; } - - for (j = 0; j < cert_path_count; ++j) { - tls_store_add_trusted_cert(cert_store, cert_paths[j], NULL); - free(cert_paths[j]); + } else if (opts->crl_url) { + if (nc_server_tls_crl_url(session, opts->crl_url, opts->crl_store)) { + goto fail; } - free(cert_paths); - - for (j = 0; j < cert_data_count; ++j) { - tls_store_add_trusted_cert(cert_store, NULL, cert_data[j]); - free(cert_data[j]); + } else { + if (nc_server_tls_crl_cert_ext(session, store, opts->crl_store)) { + goto fail; } - free(cert_data); } return 0; + +fail: + return -1; } static int @@ -1899,62 +1317,80 @@ nc_server_tls_accept_check(int accept_ret, struct nc_session *session) } int -nc_accept_tls_session(struct nc_session *session, int sock, int timeout) +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; - X509_LOOKUP *lookup; - struct nc_server_tls_opts *opts; int ret; struct timespec ts_timeout; - - opts = session->data; + struct nc_endpt *referenced_endpt; /* SSL_CTX */ -#if OPENSSL_VERSION_NUMBER >= 0x10100000L // >= 1.1.0 tls_ctx = SSL_CTX_new(TLS_server_method()); -#else - tls_ctx = SSL_CTX_new(TLSv1_2_server_method()); -#endif + if (!tls_ctx) { ERR(session, "Failed to create TLS context."); goto error; } + 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->server_cert)) { + if (nc_tls_ctx_set_server_cert_key(tls_ctx, opts)) { goto error; } /* X509_STORE, managed (freed) with the context */ cert_store = X509_STORE_new(); - SSL_CTX_set_cert_store(tls_ctx, cert_store); + if (!cert_store) { + ERR(session, "Creating certificate store failed (%s).", ERR_reason_error_string(ERR_get_error())); + goto error; + } - if (nc_tls_store_set_trusted_certs(cert_store, opts->trusted_cert_lists, opts->trusted_cert_list_count)) { + /* 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; } - if (opts->trusted_ca_file) { - lookup = X509_STORE_add_lookup(cert_store, X509_LOOKUP_file()); - if (!lookup) { - ERR(session, "Failed to add a lookup method."); + /* 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; + } + + /* do the same for referenced endpoint's end entity certs */ + if (opts->referenced_endpt_name) { + if (nc_server_get_referenced_endpt(opts->referenced_endpt_name, &referenced_endpt)) { + ERRINT; goto error; } - if (X509_LOOKUP_load_file(lookup, opts->trusted_ca_file, X509_FILETYPE_PEM) != 1) { - ERR(session, "Failed to add a trusted cert file (%s).", ERR_reason_error_string(ERR_get_error())); + ret = X509_STORE_set_ex_data(cert_store, 2, &referenced_endpt->opts.tls->ee_certs); + if (!ret) { + ERR(session, "Setting certificate store data failed (%s).", ERR_reason_error_string(ERR_get_error())); goto error; } } - if (opts->trusted_ca_dir) { - lookup = X509_STORE_add_lookup(cert_store, X509_LOOKUP_hash_dir()); - if (!lookup) { - ERR(session, "Failed to add a lookup method."); + /* 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; + } + + /* 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; } + } - if (X509_LOOKUP_add_dir(lookup, opts->trusted_ca_dir, X509_FILETYPE_PEM) != 1) { - ERR(session, "Failed to add a trusted cert directory (%s).", ERR_reason_error_string(ERR_get_error())); + /* 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; } } @@ -1971,14 +1407,34 @@ nc_accept_tls_session(struct nc_session *session, int sock, int timeout) goto error; } + /* set TLS versions for the current SSL session */ + if (opts->tls_versions) { + if (!(opts->tls_versions & NC_TLS_VERSION_10)) { + SSL_set_options(session->ti.tls, SSL_OP_NO_TLSv1); + } + if (!(opts->tls_versions & NC_TLS_VERSION_11)) { + SSL_set_options(session->ti.tls, SSL_OP_NO_TLSv1_1); + } + if (!(opts->tls_versions & NC_TLS_VERSION_12)) { + SSL_set_options(session->ti.tls, SSL_OP_NO_TLSv1_2); + } + if (!(opts->tls_versions & NC_TLS_VERSION_13)) { + SSL_set_options(session->ti.tls, SSL_OP_NO_TLSv1_3); + } + } + + /* set 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); + } + SSL_set_fd(session->ti.tls, sock); sock = -1; SSL_set_mode(session->ti.tls, SSL_MODE_AUTO_RETRY); - /* store session on per-thread basis */ - pthread_once(&verify_once, nc_tls_make_verify_key); - pthread_setspecific(verify_key, session); - if (timeout > -1) { nc_timeouttime_get(&ts_timeout, timeout); } diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 663c0c44..01c7db53 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -7,15 +7,26 @@ if(${SOURCE_FORMAT_ENABLED}) add_test(NAME format WORKING_DIRECTORY ${CMAKE_BINARY_DIR} COMMAND cmake --build ${CMAKE_BINARY_DIR} --target format-check) endif() -# list of all the tests in each directory -set(tests test_io test_fd_comm test_init_destroy_client test_init_destroy_server test_client_thread test_thread_messages) +# list of all the tests that don't require SSH and TLS +set(tests test_unix_socket test_client_thread test_fd_comm test_init_destroy_client test_init_destroy_server + test_io test_thread_messages test_client_messages) # only enable PAM tests if the version of PAM is greater than 1.4 if(LIBPAM_HAVE_CONFDIR) - list(APPEND tests test_pam) + list(APPEND tests test_auth) endif() -set(client_tests test_client test_client_messages) +#append tests depending on SSH/TLS +if(ENABLE_SSH_TLS) + list(APPEND tests test_two_channels test_ks_ts test_ec + test_ed25519 test_replace test_endpt_share_clients test_tls test_crl test_ch + test_runtime_changes test_client_ssh test_client_tls) +endif() + +foreach(src IN LISTS libsrc) + list(APPEND test_srcs "../${src}") +endforeach() +add_library(testobj OBJECT ${test_srcs} ${compatsrc}) # add -Wl,--wrap flags set(test test_client_ssh) @@ -35,23 +46,6 @@ foreach(mock_func IN LISTS ${test}_mock_funcs) set(${test}_wrap_link_flags "${${test}_wrap_link_flags},--wrap=${mock_func}") endforeach() -#append tests depending on SSH/TLS -if(ENABLE_SSH OR ENABLE_TLS) - list(APPEND tests test_server_thread) - if(ENABLE_SSH) - list(APPEND client_tests test_client_ssh) - endif() - - if(ENABLE_TLS) - list(APPEND client_tests test_client_tls) - endif() -endif() - -foreach(src IN LISTS libsrc) - list(APPEND test_srcs "../${src}") -endforeach() -add_library(testobj OBJECT ${test_srcs} ${compatsrc}) - foreach(test_name IN LISTS tests) add_executable(${test_name} $ ${test_name}.c) target_link_libraries(${test_name} ${CMOCKA_LIBRARIES} ${LIBYANG_LIBRARIES} netconf2) @@ -60,31 +54,17 @@ foreach(test_name IN LISTS tests) add_test(NAME ${test_name} COMMAND $) endforeach() -foreach(test_name IN LISTS client_tests) - add_executable(${test_name} $ ./client/${test_name}.c) - target_link_libraries(${test_name} ${CMOCKA_LIBRARIES} ${LIBYANG_LIBRARIES} netconf2) - target_include_directories(${test_name} PRIVATE ${CMOCKA_INCLUDE_DIR}) - set_target_properties(${test_name} PROPERTIES LINK_FLAGS "${${test_name}_wrap_link_flags}") - add_test(NAME ${test_name} COMMAND $) -endforeach() - if(ENABLE_VALGRIND_TESTS) foreach(test_name IN LISTS tests) add_test(${test_name}_valgrind valgrind --leak-check=full --show-leak-kinds=all --error-exitcode=1 --suppressions=${PROJECT_SOURCE_DIR}/tests/library_valgrind.supp ${CMAKE_BINARY_DIR}/tests/${test_name}) endforeach() - - foreach(test_name IN LISTS client_tests) - add_test(${test_name}_valgrind valgrind --leak-check=full --show-leak-kinds=all --error-exitcode=1 - --suppressions=${PROJECT_SOURCE_DIR}/tests/library_valgrind.supp ${CMAKE_BINARY_DIR}/tests/${test_name}) - endforeach() endif() include_directories(${CMAKE_SOURCE_DIR}/src ${PROJECT_BINARY_DIR}) configure_file("${PROJECT_SOURCE_DIR}/tests/config.h.in" "${PROJECT_BINARY_DIR}/tests/config.h" ESCAPE_QUOTES @ONLY) if(LIBPAM_HAVE_CONFDIR) - #compile PAM test module add_library(pam_netconf SHARED ${CMAKE_SOURCE_DIR}/tests/pam/pam_netconf.c) set_target_properties(pam_netconf PROPERTIES PREFIX "") diff --git a/tests/client/test_client.c b/tests/client/test_client.c deleted file mode 100644 index 722d0bf1..00000000 --- a/tests/client/test_client.c +++ /dev/null @@ -1,141 +0,0 @@ -/** - * @file test_client.c - * @author David Sedlák - * @brief client test - * - * Copyright (c) 2018 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 "tests/config.h" - -static int -setup_f(void **state) -{ - (void)state; - - nc_verbosity(NC_VERB_VERBOSE); - - return 0; -} - -static int -teardown_f(void **state) -{ - (void)state; - - return 0; -} - -static void -test_nc_client_setting_schema_searchpath(void **state) -{ - (void)state; - const char *path; - int ret; - - /* initiate client */ - nc_client_init(); - - path = nc_client_get_schema_searchpath(); - assert_null(path); - - ret = nc_client_set_schema_searchpath("path"); - assert_int_equal(ret, 0); - path = nc_client_get_schema_searchpath(); - assert_string_equal(path, "path"); - - ret = nc_client_set_schema_searchpath("path1"); - assert_int_equal(ret, 0); - path = nc_client_get_schema_searchpath(); - assert_string_equal(path, "path1"); -} - -LY_ERR -test_clb(const char *mod_name, const char *mod_rev, const char *submod_name, const char *sub_rev, void *user_data, - LYS_INFORMAT *format, const char **model_data, void (**free_module_data)(void *model_data, void *user_data)) -{ - (void)mod_name; - (void)mod_rev; - (void)submod_name; - (void)sub_rev; - (void)user_data; - (void)format; - (void)model_data; - (void)free_module_data; - - return LY_SUCCESS; -} - -LY_ERR -test_clb1(const char *mod_name, const char *mod_rev, const char *submod_name, const char *sub_rev, void *user_data, - LYS_INFORMAT *format, const char **model_data, void (**free_module_data)(void *model_data, void *user_data)) -{ - (void)mod_name; - (void)mod_rev; - (void)submod_name; - (void)sub_rev; - (void)user_data; - (void)format; - (void)model_data; - (void)free_module_data; - - return LY_SUCCESS; -} - -static void -test_nc_client_setting_schema_callback(void **state) -{ - (void)state; - ly_module_imp_clb ret_f; - char *data_ret; - int ret; - - ret_f = nc_client_get_schema_callback((void **)&data_ret); - assert_null(ret_f); - assert_null(data_ret); - - ret = nc_client_set_schema_callback(test_clb, "DATA"); - assert_int_equal(ret, 0); - ret_f = nc_client_get_schema_callback((void **)&data_ret); - assert_ptr_equal(test_clb, ret_f); - assert_string_equal("DATA", data_ret); - - ret = nc_client_set_schema_callback(test_clb1, "DATA1"); - assert_int_equal(ret, 0); - ret_f = nc_client_get_schema_callback((void **)&data_ret); - assert_ptr_equal(test_clb1, ret_f); - assert_string_equal("DATA1", data_ret); - - /* destroy client */ - nc_client_destroy(); -} - -int -main(void) -{ - const struct CMUnitTest tests[] = { - cmocka_unit_test_setup_teardown(test_nc_client_setting_schema_searchpath, setup_f, teardown_f), - cmocka_unit_test_setup_teardown(test_nc_client_setting_schema_callback, setup_f, teardown_f), - }; - - return cmocka_run_group_tests(tests, NULL, NULL); -} diff --git a/tests/config.h.in b/tests/config.h.in index 42a80787..79b5f573 100644 --- a/tests/config.h.in +++ b/tests/config.h.in @@ -19,7 +19,21 @@ #endif #define TESTS_DIR "@CMAKE_SOURCE_DIR@/tests" +#define MODULES_DIR "@CMAKE_SOURCE_DIR@/modules" #define BUILD_DIR "@CMAKE_BINARY_DIR@" @SSH_MACRO@ @TLS_MACRO@ + +/* nc_server.h local includes (not to use the installed ones) */ +#include "netconf.h" +#include "log.h" +#include "messages_server.h" +#include "server_config.h" +#include "session_server.h" +#include "session_server_ch.h" + +/* nc_client.h local includes (not to use the installed ones) */ +#include "messages_client.h" +#include "session_client.h" +#include "session_client_ch.h" diff --git a/tests/data/crl.pem b/tests/data/crl.pem new file mode 100644 index 00000000..0becd573 --- /dev/null +++ b/tests/data/crl.pem @@ -0,0 +1,12 @@ +-----BEGIN X509 CRL----- +MIIB2zCBxAIBATANBgkqhkiG9w0BAQsFADBjMQswCQYDVQQGEwJDWjETMBEGA1UE +CAwKU29tZS1TdGF0ZTENMAsGA1UEBwwEQnJubzEPMA0GA1UECgwGQ0VTTkVUMQww +CgYDVQQLDANUTUMxETAPBgNVBAMMCHNlcnZlcmNhFw0yMzA2MTUxMTMxMzBaFw0z +MzA2MTIxMTMxMzBaMBwwGgIJAJXrkmAO99aQFw0yMzA2MTIxNDAzMTJaoA8wDTAL +BgNVHRQEBAICEAUwDQYJKoZIhvcNAQELBQADggEBAMbxn/kkds6i60o31eQvaI49 +XXQiEnZ+15r8ehkeKv4VOQBeHbddrYuhoaESdVLeB2wzphbLLPAOsUvqHxtM4eO2 +faDDhpquHL3eKsHZA5UyAl9vEHyzONXiSoJvVNvC5dtQHflgUtqKwOnhrvxlUBqV +pcwVWfqE+opm19f8iNaA4OgFn9dFyaRnpgzWILGOykzW+aEzmCpkVYeUgR1RqBm5 +bgpzQBma2Mg9AjWIeTwGozoANt/Q7GJhdmeZEp41iDc1HElFMUyYspClaYfRKzuE +VvrHeKOFQIjhQcoPO6oH5QwYgXKUBFuTJD5who3hHbu+6u3upaJ6RZGcwVa+Ly8= +-----END X509 CRL----- diff --git a/tests/data/id_ecdsa256 b/tests/data/id_ecdsa256 new file mode 100644 index 00000000..6c01edb1 --- /dev/null +++ b/tests/data/id_ecdsa256 @@ -0,0 +1,9 @@ +-----BEGIN OPENSSH PRIVATE KEY----- +b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAaAAAABNlY2RzYS +1zaGEyLW5pc3RwMjU2AAAACG5pc3RwMjU2AAAAQQSGOtimPvmp8jbsK5kLto0HcgfaWGTB +hBsn+c5deY0SQuzhj6vkxqkYMBTqAcpWLbj6xVhpTPs5LalDJ9ffuaj6AAAAqCh4tAQoeL +QEAAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBIY62KY++anyNuwr +mQu2jQdyB9pYZMGEGyf5zl15jRJC7OGPq+TGqRgwFOoBylYtuPrFWGlM+zktqUMn19+5qP +oAAAAgBf3u7SBWmpDCm7esp1VnpoflXGytRAxp85nsb4Hhbd0AAAANcm9tYW5AcGN2YXNr +bwECAw== +-----END OPENSSH PRIVATE KEY----- diff --git a/tests/data/id_ecdsa256.pub b/tests/data/id_ecdsa256.pub new file mode 100644 index 00000000..169840d0 --- /dev/null +++ b/tests/data/id_ecdsa256.pub @@ -0,0 +1 @@ +ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBIY62KY++anyNuwrmQu2jQdyB9pYZMGEGyf5zl15jRJC7OGPq+TGqRgwFOoBylYtuPrFWGlM+zktqUMn19+5qPo= test@libnetconf2 diff --git a/tests/data/id_ecdsa384 b/tests/data/id_ecdsa384 new file mode 100644 index 00000000..38211c73 --- /dev/null +++ b/tests/data/id_ecdsa384 @@ -0,0 +1,10 @@ +-----BEGIN OPENSSH PRIVATE KEY----- +b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAiAAAABNlY2RzYS +1zaGEyLW5pc3RwMzg0AAAACG5pc3RwMzg0AAAAYQQSz1JFAWiAmtKwWSyCXdZUwNTS/m+c +CpiUPC3vHZ82S1g2ihbQpN4IAIAAfRTER+rnV5/qClDmCjBsOSIaw86VcIykwAqcNy0x0i +vGoyfpVL4/9CZfVSf/hwITxfbCK5MAAADYCMDKzgjAys4AAAATZWNkc2Etc2hhMi1uaXN0 +cDM4NAAAAAhuaXN0cDM4NAAAAGEEEs9SRQFogJrSsFksgl3WVMDU0v5vnAqYlDwt7x2fNk +tYNooW0KTeCACAAH0UxEfq51ef6gpQ5gowbDkiGsPOlXCMpMAKnDctMdIrxqMn6VS+P/Qm +X1Un/4cCE8X2wiuTAAAAMCpWDy26Bm4HFbLLgn/Jf8iJ/V5V5RXOnW994Jbh2w2b0gT9c5 +CfLHjENUhHF3zI9QAAAA1yb21hbkBwY3Zhc2tvAQID +-----END OPENSSH PRIVATE KEY----- diff --git a/tests/data/id_ecdsa384.pub b/tests/data/id_ecdsa384.pub new file mode 100644 index 00000000..5dbf598b --- /dev/null +++ b/tests/data/id_ecdsa384.pub @@ -0,0 +1 @@ +ecdsa-sha2-nistp384 AAAAE2VjZHNhLXNoYTItbmlzdHAzODQAAAAIbmlzdHAzODQAAABhBBLPUkUBaICa0rBZLIJd1lTA1NL+b5wKmJQ8Le8dnzZLWDaKFtCk3ggAgAB9FMRH6udXn+oKUOYKMGw5IhrDzpVwjKTACpw3LTHSK8ajJ+lUvj/0Jl9VJ/+HAhPF9sIrkw== test@libnetconf2 diff --git a/tests/data/id_ecdsa521 b/tests/data/id_ecdsa521 new file mode 100644 index 00000000..1c43f0a8 --- /dev/null +++ b/tests/data/id_ecdsa521 @@ -0,0 +1,12 @@ +-----BEGIN OPENSSH PRIVATE KEY----- +b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAArAAAABNlY2RzYS +1zaGEyLW5pc3RwNTIxAAAACG5pc3RwNTIxAAAAhQQBdWbNJ1cbOVb9jkjFVe3ef8hM2Jc3 +Fgrx1uScbWjzRyLFELbVxgMdQhVShobGyl28Sw4tQPXBPk2iJuZ1is8Jk0gA89GJqKLAbI +O6MVOc6Swx3jR9VP0jOxEWN0dt+swYkZYPNsmSCtVl49w+li+759b5jIWUKNX+jwLua0DG +oykzIy4AAAEQGOm95hjpveYAAAATZWNkc2Etc2hhMi1uaXN0cDUyMQAAAAhuaXN0cDUyMQ +AAAIUEAXVmzSdXGzlW/Y5IxVXt3n/ITNiXNxYK8dbknG1o80cixRC21cYDHUIVUoaGxspd +vEsOLUD1wT5NoibmdYrPCZNIAPPRiaiiwGyDujFTnOksMd40fVT9IzsRFjdHbfrMGJGWDz +bJkgrVZePcPpYvu+fW+YyFlCjV/o8C7mtAxqMpMyMuAAAAQgG2S26e7KJI+Old8/A2JPPz +9Lbtwgjb09LYZhkRzCELq/9yjY3HvBEOFF3c5WbEn+Opn+MJP1JmQ5UxEUPybDl+egAAAA +1yb21hbkBwY3Zhc2tvAQIDBAU= +-----END OPENSSH PRIVATE KEY----- diff --git a/tests/data/id_ecdsa521.pub b/tests/data/id_ecdsa521.pub new file mode 100644 index 00000000..f49132a9 --- /dev/null +++ b/tests/data/id_ecdsa521.pub @@ -0,0 +1 @@ +ecdsa-sha2-nistp521 AAAAE2VjZHNhLXNoYTItbmlzdHA1MjEAAAAIbmlzdHA1MjEAAACFBAF1Zs0nVxs5Vv2OSMVV7d5/yEzYlzcWCvHW5JxtaPNHIsUQttXGAx1CFVKGhsbKXbxLDi1A9cE+TaIm5nWKzwmTSADz0YmoosBsg7oxU5zpLDHeNH1U/SM7ERY3R236zBiRlg82yZIK1WXj3D6WL7vn1vmMhZQo1f6PAu5rQMajKTMjLg== test@libnetconf2 diff --git a/tests/data/id_ed25519 b/tests/data/id_ed25519 new file mode 100644 index 00000000..df71976d --- /dev/null +++ b/tests/data/id_ed25519 @@ -0,0 +1,7 @@ +-----BEGIN OPENSSH PRIVATE KEY----- +b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW +QyNTUxOQAAACDq+Oq6bYOgbFoTtSTKJrod3LgmJnrjuiXzlD7P2Dt+cAAAAJC1rL1gtay9 +YAAAAAtzc2gtZWQyNTUxOQAAACDq+Oq6bYOgbFoTtSTKJrod3LgmJnrjuiXzlD7P2Dt+cA +AAAEAQm84SEphEUZEbuCRmXrMcYyv70wNEVziE/SbBC6+trOr46rptg6BsWhO1JMomuh3c +uCYmeuO6JfOUPs/YO35wAAAADXJvbWFuQHBjdmFza28= +-----END OPENSSH PRIVATE KEY----- diff --git a/tests/data/id_ed25519.pub b/tests/data/id_ed25519.pub new file mode 100644 index 00000000..533f33f7 --- /dev/null +++ b/tests/data/id_ed25519.pub @@ -0,0 +1 @@ +ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOr46rptg6BsWhO1JMomuh3cuCYmeuO6JfOUPs/YO35w test@libnetconf2 diff --git a/tests/pam/pam_netconf.c b/tests/pam/pam_netconf.c index dd30fe07..835f4832 100644 --- a/tests/pam/pam_netconf.c +++ b/tests/pam/pam_netconf.c @@ -259,7 +259,7 @@ pam_sm_acct_mgmt(pam_handle_t *pam_h, int flags, int argc, const char *argv[]) if (r != PAM_SUCCESS) { return r; } - if (!strcmp((const char *)username, "test")) { + if (!strcmp((const char *)username, "test_int")) { return PAM_NEW_AUTHTOK_REQD; } return PAM_SYSTEM_ERR; @@ -291,7 +291,7 @@ pam_sm_chauthtok(pam_handle_t *pam_h, int flags, int argc, const char *argv[]) if (r != PAM_SUCCESS) { return r; } - if (!strcmp((const char *)username, "test")) { + if (!strcmp((const char *)username, "test_int")) { return PAM_SUCCESS; } else { return PAM_SYSTEM_ERR; @@ -299,7 +299,7 @@ pam_sm_chauthtok(pam_handle_t *pam_h, int flags, int argc, const char *argv[]) /* change the authentication token in the second call */ } else if (flags & PAM_UPDATE_AUTHTOK) { - r = pam_set_item(pam_h, PAM_AUTHTOK, "test"); + r = pam_set_item(pam_h, PAM_AUTHTOK, "test_int"); if (r == PAM_SUCCESS) { printf("[TEST #6] Passed.\n\n"); } else { diff --git a/tests/test_auth.c b/tests/test_auth.c new file mode 100644 index 00000000..70f5ba30 --- /dev/null +++ b/tests/test_auth.c @@ -0,0 +1,385 @@ +/** + * @file test_auth.c + * @author Roman Janota + * @brief libnetconf2 Linux PAM keyboard-interactive authentication test + * + * @copyright + * Copyright (c) 2022 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 "tests/config.h" + +#define NC_ACCEPT_TIMEOUT 2000 +#define NC_PS_POLL_TIMEOUT 2000 + +struct ly_ctx *ctx; + +struct test_state { + pthread_barrier_t barrier; +}; + +static void * +server_thread(void *arg) +{ + int ret; + NC_MSG_TYPE msgtype; + struct nc_session *session; + struct nc_pollsession *ps; + struct test_state *state = arg; + + (void) arg; + + ps = nc_ps_new(); + assert_non_null(ps); + + /* accept a session and add it to the poll session structure */ + pthread_barrier_wait(&state->barrier); + msgtype = nc_accept(NC_ACCEPT_TIMEOUT, ctx, &session); + assert_int_equal(msgtype, NC_MSG_HELLO); + + ret = nc_ps_add_session(ps, session); + assert_int_equal(ret, 0); + + do { + ret = nc_ps_poll(ps, NC_PS_POLL_TIMEOUT, NULL); + assert_int_equal(ret & NC_PSPOLL_RPC, NC_PSPOLL_RPC); + } while (!(ret & NC_PSPOLL_SESSION_TERM)); + + nc_ps_clear(ps, 1, NULL); + nc_ps_free(ps); + return NULL; +} + +static char * +auth_interactive(const char *auth_name, const char *instruction, const char *prompt, int echo, void *priv) +{ + (void) instruction; + (void) echo; + (void) auth_name; + (void) priv; + + /* send the replies to keyboard-interactive authentication */ + if (strstr(prompt, "backwards")) { + return strdup("tni_tset"); + } else if (strstr(prompt, "1+1")) { + return strdup("2"); + } else { + return NULL; + } +} + +static void * +client_thread_interactive(void *arg) +{ + int ret; + struct nc_session *session = NULL; + struct test_state *state = arg; + + /* skip all hostkey and known_hosts checks */ + nc_client_ssh_set_knownhosts_mode(NC_SSH_KNOWNHOSTS_SKIP); + + ret = nc_client_set_schema_searchpath(MODULES_DIR); + assert_int_equal(ret, 0); + + ret = nc_client_ssh_set_username("test_int"); + assert_int_equal(ret, 0); + + /* set keyboard-interactive authentication callback */ + nc_client_ssh_set_auth_interactive_clb(auth_interactive, NULL); + + nc_client_ssh_set_auth_pref(NC_SSH_AUTH_PUBLICKEY, -1); + nc_client_ssh_set_auth_pref(NC_SSH_AUTH_PASSWORD, -1); + nc_client_ssh_set_auth_pref(NC_SSH_AUTH_INTERACTIVE, 1); + + pthread_barrier_wait(&state->barrier); + session = nc_connect_ssh("127.0.0.1", 10005, NULL); + assert_non_null(session); + + nc_session_free(session, NULL); + return NULL; +} + +static void +test_nc_auth_interactive(void **state) +{ + int ret, i; + pthread_t tids[2]; + + assert_non_null(state); + + ret = pthread_create(&tids[0], NULL, client_thread_interactive, *state); + assert_int_equal(ret, 0); + ret = pthread_create(&tids[1], NULL, server_thread, *state); + assert_int_equal(ret, 0); + + for (i = 0; i < 2; i++) { + pthread_join(tids[i], NULL); + } +} + +static char * +auth_password(const char *username, const char *hostname, void *priv) +{ + (void) hostname; + (void) priv; + + /* set the reply to password authentication */ + if (!strcmp(username, "test_pw")) { + return strdup("testpw"); + } else { + return NULL; + } +} + +static void * +client_thread_password(void *arg) +{ + int ret; + struct nc_session *session = NULL; + struct test_state *state = arg; + + /* skip all hostkey and known_hosts checks */ + nc_client_ssh_set_knownhosts_mode(NC_SSH_KNOWNHOSTS_SKIP); + + ret = nc_client_set_schema_searchpath(MODULES_DIR); + assert_int_equal(ret, 0); + + ret = nc_client_ssh_set_username("test_pw"); + assert_int_equal(ret, 0); + + nc_client_ssh_set_auth_password_clb(auth_password, NULL); + + nc_client_ssh_set_auth_pref(NC_SSH_AUTH_PUBLICKEY, -1); + nc_client_ssh_set_auth_pref(NC_SSH_AUTH_PASSWORD, 1); + nc_client_ssh_set_auth_pref(NC_SSH_AUTH_INTERACTIVE, -1); + + pthread_barrier_wait(&state->barrier); + session = nc_connect_ssh("127.0.0.1", 10005, NULL); + assert_non_null(session); + + nc_session_free(session, NULL); + return NULL; +} + +static void +test_nc_auth_password(void **state) +{ + int ret, i; + pthread_t tids[2]; + + assert_non_null(state); + + ret = pthread_create(&tids[0], NULL, client_thread_password, *state); + assert_int_equal(ret, 0); + ret = pthread_create(&tids[1], NULL, server_thread, *state); + assert_int_equal(ret, 0); + + for (i = 0; i < 2; i++) { + pthread_join(tids[i], NULL); + } +} + +static void * +client_thread_pubkey(void *arg) +{ + int ret; + struct nc_session *session = NULL; + struct test_state *state = arg; + + /* skip all hostkey and known_hosts checks */ + nc_client_ssh_set_knownhosts_mode(NC_SSH_KNOWNHOSTS_SKIP); + + ret = nc_client_set_schema_searchpath(MODULES_DIR); + assert_int_equal(ret, 0); + + ret = nc_client_ssh_set_username("test_pk"); + assert_int_equal(ret, 0); + + nc_client_ssh_set_auth_pref(NC_SSH_AUTH_PUBLICKEY, 1); + nc_client_ssh_set_auth_pref(NC_SSH_AUTH_PASSWORD, -1); + nc_client_ssh_set_auth_pref(NC_SSH_AUTH_INTERACTIVE, -1); + + ret = nc_client_ssh_add_keypair(TESTS_DIR "/data/key_rsa.pub", TESTS_DIR "/data/key_rsa"); + assert_int_equal(ret, 0); + + pthread_barrier_wait(&state->barrier); + session = nc_connect_ssh("127.0.0.1", 10005, NULL); + assert_non_null(session); + + nc_session_free(session, NULL); + return NULL; +} + +static void +test_nc_auth_pubkey(void **state) +{ + int ret, i; + pthread_t tids[2]; + + assert_non_null(state); + + ret = pthread_create(&tids[0], NULL, client_thread_pubkey, *state); + assert_int_equal(ret, 0); + ret = pthread_create(&tids[1], NULL, server_thread, *state); + assert_int_equal(ret, 0); + + for (i = 0; i < 2; i++) { + pthread_join(tids[i], NULL); + } +} + +static void * +client_thread_none(void *arg) +{ + int ret; + struct nc_session *session = NULL; + struct test_state *state = arg; + + /* skip all hostkey and known_hosts checks */ + nc_client_ssh_set_knownhosts_mode(NC_SSH_KNOWNHOSTS_SKIP); + + ret = nc_client_set_schema_searchpath(MODULES_DIR); + assert_int_equal(ret, 0); + + ret = nc_client_ssh_set_username("test_none"); + assert_int_equal(ret, 0); + + pthread_barrier_wait(&state->barrier); + session = nc_connect_ssh("127.0.0.1", 10005, NULL); + assert_non_null(session); + + nc_session_free(session, NULL); + return NULL; +} + +static void +test_nc_auth_none(void **state) +{ + int ret, i; + pthread_t tids[2]; + + assert_non_null(state); + + ret = pthread_create(&tids[0], NULL, client_thread_none, *state); + assert_int_equal(ret, 0); + ret = pthread_create(&tids[1], NULL, server_thread, *state); + assert_int_equal(ret, 0); + + for (i = 0; i < 2; i++) { + pthread_join(tids[i], NULL); + } +} + +static int +setup_f(void **state) +{ + int ret; + struct lyd_node *tree = NULL; + struct test_state *test_state; + + nc_verbosity(NC_VERB_VERBOSE); + + /* init barrier */ + test_state = malloc(sizeof *test_state); + assert_non_null(test_state); + + ret = pthread_barrier_init(&test_state->barrier, NULL, 2); + assert_int_equal(ret, 0); + + *state = test_state; + + ret = ly_ctx_new(MODULES_DIR, 0, &ctx); + assert_int_equal(ret, 0); + + ret = nc_server_init_ctx(&ctx); + assert_int_equal(ret, 0); + + 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", 10005, &tree); + assert_int_equal(ret, 0); + + 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_ssh_user_pubkey(ctx, "endpt", "test_pk", "pubkey", TESTS_DIR "/data/key_rsa.pub", &tree); + assert_int_equal(ret, 0); + + ret = nc_server_config_add_ssh_user_interactive(ctx, "endpt", "test_int", "netconf.conf", BUILD_DIR "/tests", &tree); + assert_int_equal(ret, 0); + + ret = nc_server_config_add_ssh_user_password(ctx, "endpt", "test_pw", "testpw", &tree); + assert_int_equal(ret, 0); + + ret = lyd_new_path(tree, ctx, "/ietf-netconf-server:netconf-server/listen/endpoint[name='endpt']/ssh/ssh-server-parameters/" + "client-authentication/users/user[name='test_none']/none", NULL, 0, NULL); + assert_int_equal(ret, 0); + + /* configure the server based on the data */ + ret = nc_server_config_setup_data(tree); + assert_int_equal(ret, 0); + + ret = nc_server_init(); + assert_int_equal(ret, 0); + + /* initialize client */ + ret = nc_client_init(); + assert_int_equal(ret, 0); + + lyd_free_all(tree); + + return 0; +} + +static int +teardown_f(void **state) +{ + int ret = 0; + struct test_state *test_state; + + assert_non_null(state); + test_state = *state; + + ret = pthread_barrier_destroy(&test_state->barrier); + assert_int_equal(ret, 0); + + free(*state); + nc_client_destroy(); + nc_server_destroy(); + ly_ctx_destroy(ctx); + + return 0; +} + +int +main(void) +{ + const struct CMUnitTest tests[] = { + cmocka_unit_test_setup_teardown(test_nc_auth_interactive, setup_f, teardown_f), + cmocka_unit_test_setup_teardown(test_nc_auth_pubkey, setup_f, teardown_f), + cmocka_unit_test_setup_teardown(test_nc_auth_password, setup_f, teardown_f), + cmocka_unit_test_setup_teardown(test_nc_auth_none, setup_f, teardown_f) + }; + + setenv("CMOCKA_TEST_ABORT", "1", 1); + return cmocka_run_group_tests(tests, NULL, NULL); +} diff --git a/tests/test_ch.c b/tests/test_ch.c new file mode 100644 index 00000000..558e29a2 --- /dev/null +++ b/tests/test_ch.c @@ -0,0 +1,470 @@ +/** + * @file test_ch.c + * @author Roman Janota + * @brief libnetconf2 Call-home test + * + * @copyright + * Copyright (c) 2023 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 "tests/config.h" + +#define NC_PS_POLL_TIMEOUT 2000 + +#define NC_ACCEPT_TIMEOUT 2000 + +struct ly_ctx *ctx; + +struct test_state { + pthread_barrier_t barrier; + struct lyd_node *ssh_tree; + struct lyd_node *tls_tree; +}; + +char buffer[512]; +char expected[512]; + +static void +test_msg_callback(const struct nc_session *session, NC_VERB_LEVEL level, const char *msg) +{ + (void) level; + (void) session; + + if (strstr(msg, expected)) { + strcpy(buffer, msg); + } + + printf("%s\n", msg); +} + +/* acquire ctx cb for dispatch */ +const struct ly_ctx * +ch_session_acquire_ctx_cb(void *cb_data) +{ + (void) cb_data; + return ctx; +} + +/* release ctx cb for dispatch */ +void +ch_session_release_ctx_cb(void *cb_data) +{ + (void) cb_data; + return; +} + +/* new session cb for dispatch */ +int +ch_new_session_cb(const char *client_name, struct nc_session *new_session, void *user_data) +{ + int ret = 0; + struct nc_pollsession *ps = (struct nc_pollsession *)user_data; + + (void) client_name; + + ret = nc_ps_add_session(ps, new_session); + assert_int_equal(ret, 0); + return 0; +} + +static void * +server_thread_ssh(void *arg) +{ + int ret; + struct test_state *state = arg; + struct nc_pollsession *ps; + + /* set print clb so we get access to messages */ + nc_set_print_clb_session(test_msg_callback); + buffer[0] = '\0'; + strcpy(expected, "reconnecting in"); + + /* prepare data for deleting the call-home client */ + ret = nc_server_config_del_ch_client("ch_ssh", &state->ssh_tree); + assert_int_equal(ret, 0); + + /* new poll session */ + ps = nc_ps_new(); + assert_non_null(ps); + + pthread_barrier_wait(&state->barrier); + /* create the call-home client thread */ + ret = nc_connect_ch_client_dispatch("ch_ssh", ch_session_acquire_ctx_cb, + ch_session_release_ctx_cb, NULL, ch_new_session_cb, ps); + assert_int_equal(ret, 0); + + /* poll */ + do { + ret = nc_ps_poll(ps, NC_PS_POLL_TIMEOUT, NULL); + if (ret & (NC_PSPOLL_TIMEOUT | NC_PSPOLL_NOSESSIONS)) { + usleep(500); + } + } while (!strlen(buffer)); + + /* delete the call-home client, the thread should end */ + ret = nc_server_config_setup_data(state->ssh_tree); + assert_int_equal(ret, 0); + + nc_ps_clear(ps, 1, NULL); + nc_ps_free(ps); + nc_server_destroy(); + return NULL; +} + +static void * +client_thread_ssh(void *arg) +{ + int ret; + struct nc_session *session = NULL; + struct test_state *state = arg; + + /* skip all hostkey and known_hosts checks */ + nc_client_ssh_set_knownhosts_mode(NC_SSH_KNOWNHOSTS_SKIP); + + /* set directory where to search for modules */ + ret = nc_client_set_schema_searchpath(MODULES_DIR); + assert_int_equal(ret, 0); + + /* set ssh username */ + ret = nc_client_ssh_ch_set_username("test_ch_ssh"); + assert_int_equal(ret, 0); + + /* add client's key pair */ + ret = nc_client_ssh_ch_add_keypair(TESTS_DIR "/data/id_ed25519.pub", TESTS_DIR "/data/id_ed25519"); + assert_int_equal(ret, 0); + + /* add call-home bind */ + ret = nc_client_ssh_ch_add_bind_listen("127.0.0.1", 10009); + assert_int_equal(ret, 0); + + pthread_barrier_wait(&state->barrier); + /* connect */ + ret = nc_accept_callhome(NC_ACCEPT_TIMEOUT, NULL, &session); + assert_int_equal(ret, 1); + + ret = nc_client_ssh_ch_del_bind("127.0.0.1", 10009); + assert_int_equal(ret, 0); + + nc_session_free(session, NULL); + return NULL; +} + +static int +setup_ssh(void **state) +{ + int ret; + struct test_state *test_state; + + nc_verbosity(NC_VERB_VERBOSE); + + /* init barrier */ + test_state = malloc(sizeof *test_state); + assert_non_null(test_state); + + ret = pthread_barrier_init(&test_state->barrier, NULL, 2); + assert_int_equal(ret, 0); + + test_state->ssh_tree = NULL; + *state = test_state; + + /* create new context */ + ret = ly_ctx_new(MODULES_DIR, 0, &ctx); + assert_int_equal(ret, 0); + + /* load default modules into context */ + ret = nc_server_init_ctx(&ctx); + assert_int_equal(ret, 0); + + /* load ietf-netconf-server module and it's imports into context */ + ret = nc_server_config_load_modules(&ctx); + 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", "10009", &test_state->ssh_tree); + assert_int_equal(ret, 0); + + /* set connection type to persistent */ + ret = nc_server_config_add_ch_persistent(ctx, "ch_ssh", &test_state->ssh_tree); + assert_int_equal(ret, 0); + + /* set the period of the periodic connection type, this should remove the persistent connection type */ + ret = nc_server_config_add_ch_period(ctx, "ch_ssh", 3, &test_state->ssh_tree); + assert_int_equal(ret, 0); + + /* set call-home server hostkey */ + ret = nc_server_config_add_ch_ssh_hostkey(ctx, "ch_ssh", "endpt", "hostkey", TESTS_DIR "/data/key_ecdsa", NULL, &test_state->ssh_tree); + assert_int_equal(ret, 0); + + /* set call-home client's pubkey */ + ret = nc_server_config_add_ch_ssh_user_pubkey(ctx, "ch_ssh", "endpt", "test_ch_ssh", "pubkey", TESTS_DIR "/data/id_ed25519.pub", &test_state->ssh_tree); + assert_int_equal(ret, 0); + + /* configure the server based on the data */ + ret = nc_server_config_setup_data(test_state->ssh_tree); + assert_int_equal(ret, 0); + + /* initialize server */ + ret = nc_server_init(); + assert_int_equal(ret, 0); + + /* initialize client */ + ret = nc_client_init(); + assert_int_equal(ret, 0); + + return 0; +} + +static int +teardown_ssh(void **state) +{ + int ret = 0; + struct test_state *test_state; + + assert_non_null(state); + test_state = *state; + + ret = pthread_barrier_destroy(&test_state->barrier); + assert_int_equal(ret, 0); + + lyd_free_tree(test_state->ssh_tree); + + free(*state); + nc_client_destroy(); + ly_ctx_destroy(ctx); + + return 0; +} + +static void +test_nc_ch_ssh(void **state) +{ + int ret, i; + pthread_t tids[2]; + + assert_non_null(state); + + /* client */ + ret = pthread_create(&tids[0], NULL, client_thread_ssh, *state); + assert_int_equal(ret, 0); + + /* server */ + ret = pthread_create(&tids[1], NULL, server_thread_ssh, *state); + assert_int_equal(ret, 0); + + for (i = 0; i < 2; i++) { + pthread_join(tids[i], NULL); + } +} + +static void * +server_thread_tls(void *arg) +{ + int ret; + struct test_state *state = arg; + struct nc_pollsession *ps; + + /* prepare data for deleting the call-home client */ + ret = nc_server_config_del_ch_client("ch_tls", &state->tls_tree); + assert_int_equal(ret, 0); + + /* new poll session */ + ps = nc_ps_new(); + assert_non_null(ps); + + pthread_barrier_wait(&state->barrier); + /* create the call-home client thread */ + ret = nc_connect_ch_client_dispatch("ch_tls", ch_session_acquire_ctx_cb, + ch_session_release_ctx_cb, NULL, ch_new_session_cb, ps); + assert_int_equal(ret, 0); + + /* poll */ + do { + ret = nc_ps_poll(ps, NC_PS_POLL_TIMEOUT, NULL); + if (ret & (NC_PSPOLL_TIMEOUT | NC_PSPOLL_NOSESSIONS)) { + usleep(500); + } + } while (!(ret & NC_PSPOLL_SESSION_TERM)); + + /* delete the call-home client, the thread should end */ + ret = nc_server_config_setup_data(state->tls_tree); + assert_int_equal(ret, 0); + + nc_ps_clear(ps, 1, NULL); + nc_ps_free(ps); + nc_server_destroy(); + return NULL; +} + +static void * +client_thread_tls(void *arg) +{ + int ret; + struct nc_session *session = NULL; + struct test_state *state = arg; + + /* set directory where to search for modules */ + ret = nc_client_set_schema_searchpath(MODULES_DIR); + assert_int_equal(ret, 0); + + /* add client's cert */ + ret = nc_client_tls_ch_set_cert_key_paths(TESTS_DIR "/data/client.crt", TESTS_DIR "/data/client.key"); + assert_int_equal(ret, 0); + + /* set client ca */ + ret = nc_client_tls_ch_set_trusted_ca_paths(TESTS_DIR "/data/serverca.pem", NULL); + assert_int_equal(ret, 0); + + /* add call-home bind */ + ret = nc_client_tls_ch_add_bind_listen("127.0.0.1", 10010); + assert_int_equal(ret, 0); + + pthread_barrier_wait(&state->barrier); + /* connect */ + ret = nc_accept_callhome(NC_ACCEPT_TIMEOUT, NULL, &session); + assert_int_equal(ret, 1); + + ret = nc_client_tls_ch_del_bind("127.0.0.1", 10010); + assert_int_equal(ret, 0); + + nc_session_free(session, NULL); + return NULL; +} + +static void +test_nc_ch_tls(void **state) +{ + int ret, i; + pthread_t tids[2]; + + assert_non_null(state); + + /* client */ + ret = pthread_create(&tids[0], NULL, client_thread_tls, *state); + assert_int_equal(ret, 0); + + /* server */ + ret = pthread_create(&tids[1], NULL, server_thread_tls, *state); + assert_int_equal(ret, 0); + + for (i = 0; i < 2; i++) { + pthread_join(tids[i], NULL); + } +} + +static int +setup_tls(void **state) +{ + int ret; + struct test_state *test_state; + + nc_verbosity(NC_VERB_VERBOSE); + + /* init barrier */ + test_state = malloc(sizeof *test_state); + assert_non_null(test_state); + + ret = pthread_barrier_init(&test_state->barrier, NULL, 2); + assert_int_equal(ret, 0); + + test_state->tls_tree = NULL; + *state = test_state; + + /* create new context */ + ret = ly_ctx_new(MODULES_DIR, 0, &ctx); + assert_int_equal(ret, 0); + + /* load default modules into context */ + ret = nc_server_init_ctx(&ctx); + assert_int_equal(ret, 0); + + /* load ietf-netconf-server module and it's imports into context */ + ret = nc_server_config_load_modules(&ctx); + 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", "10010", &test_state->tls_tree); + assert_int_equal(ret, 0); + + /* set call-home server certificate */ + ret = nc_server_config_add_ch_tls_server_cert(ctx, "ch_tls", "endpt", TESTS_DIR "/data/server.key", NULL, TESTS_DIR "/data/server.crt", &test_state->tls_tree); + assert_int_equal(ret, 0); + + /* set call-home client end entity certificate */ + ret = nc_server_config_add_ch_tls_client_cert(ctx, "ch_tls", "endpt", "ee-cert", TESTS_DIR "/data/client.crt", &test_state->tls_tree); + assert_int_equal(ret, 0); + + /* set call-home client certificate authority certificate */ + ret = nc_server_config_add_ch_tls_ca_cert(ctx, "ch_tls", "endpt", "ca-cert", TESTS_DIR "/data/serverca.pem", &test_state->tls_tree); + assert_int_equal(ret, 0); + + /* set call-home CTN */ + ret = nc_server_config_add_ch_tls_ctn(ctx, "ch_tls", "endpt", 1, + "04:85:6B:75:D1:1A:86:E0:D8:FE:5B:BD:72:F5:73:1D:07:EA:32:BF:09:11:21:6A:6E:23:78:8E:B6:D5:73:C3:2D", + NC_TLS_CTN_SPECIFIED, "ch_client_tls", &test_state->tls_tree); + assert_int_equal(ret, 0); + + /* configure the server based on the data */ + ret = nc_server_config_setup_data(test_state->tls_tree); + assert_int_equal(ret, 0); + + /* initialize server */ + ret = nc_server_init(); + assert_int_equal(ret, 0); + + /* initialize client */ + ret = nc_client_init(); + assert_int_equal(ret, 0); + + return 0; +} + +static int +teardown_tls(void **state) +{ + int ret = 0; + struct test_state *test_state; + + assert_non_null(state); + test_state = *state; + + ret = pthread_barrier_destroy(&test_state->barrier); + assert_int_equal(ret, 0); + + lyd_free_tree(test_state->tls_tree); + + free(*state); + nc_client_destroy(); + ly_ctx_destroy(ctx); + + return 0; +} + +int +main(void) +{ + const struct CMUnitTest tests[] = { + cmocka_unit_test_setup_teardown(test_nc_ch_ssh, setup_ssh, teardown_ssh), + cmocka_unit_test_setup_teardown(test_nc_ch_tls, setup_tls, teardown_tls), + }; + + setenv("CMOCKA_TEST_ABORT", "1", 1); + return cmocka_run_group_tests(tests, NULL, NULL); +} diff --git a/tests/client/test_client_messages.c b/tests/test_client_messages.c similarity index 100% rename from tests/client/test_client_messages.c rename to tests/test_client_messages.c diff --git a/tests/client/test_client_ssh.c b/tests/test_client_ssh.c similarity index 93% rename from tests/client/test_client_ssh.c rename to tests/test_client_ssh.c index 8d50145d..b8fa940f 100644 --- a/tests/client/test_client_ssh.c +++ b/tests/test_client_ssh.c @@ -34,16 +34,6 @@ #include #include -static int -ssh_hostkey_check_clb(const char *hostname, ssh_session session, void *priv) -{ - (void)hostname; - (void)session; - (void)priv; - - return 0; -} - static int setup_f(void **state) { @@ -52,11 +42,15 @@ setup_f(void **state) nc_verbosity(NC_VERB_VERBOSE); + /* init client */ + nc_client_init(); + ret = nc_client_ssh_set_username("username"); assert_int_equal(ret, 0); ret = nc_client_ssh_ch_set_username("ch_username"); assert_int_equal(ret, 0); - nc_client_ssh_set_auth_hostkey_check_clb(ssh_hostkey_check_clb, NULL); + /* skip all hostkey and known_hosts checks */ + nc_client_ssh_set_knownhosts_mode(NC_SSH_KNOWNHOSTS_SKIP); return 0; } @@ -65,6 +59,8 @@ static int teardown_f(void **state) { (void)state; + /* destroy client */ + nc_client_destroy(); return 0; } @@ -216,10 +212,11 @@ __wrap_nc_sock_listen_inet(const char *address, uint16_t port, struct nc_keepali } MOCK int -__wrap_nc_sock_accept_binds(struct nc_bind *binds, uint16_t bind_count, int timeout, char **host, uint16_t *port, uint16_t *idx) +__wrap_nc_sock_accept_binds(struct nc_bind *binds, uint16_t bind_count, pthread_mutex_t *bind_lock, int timeout, char **host, uint16_t *port, uint16_t *idx) { (void)binds; (void)bind_count; + (void)bind_lock; (void)timeout; (void)host; (void)port; @@ -240,35 +237,6 @@ __wrap_nc_accept_callhome_ssh_sock(int sock, const char *host, uint16_t port, st return mock_ptr_type(struct nc_session *); } -static int -test_hostkey_clb(const char *hostname, ssh_session session, void *priv) -{ - (void)hostname; - (void)session; - (void)priv; - - return 0; -} - -static void -test_nc_client_ssh_setting_auth_hostkey_check_clb(void **state) -{ - (void)state; - int (*ret_f)(const char *hostname, ssh_session session, void *priv); - char *priv_data_ret; - - /* ssh_hostkey_check_clb is set in setup_f */ - nc_client_ssh_get_auth_hostkey_check_clb(&ret_f, (void **)&priv_data_ret); - assert_ptr_equal(ret_f, ssh_hostkey_check_clb); - assert_null(priv_data_ret); - - /* set different callback and private data */ - nc_client_ssh_set_auth_hostkey_check_clb(test_hostkey_clb, "DATA"); - nc_client_ssh_get_auth_hostkey_check_clb(&ret_f, (void **)&priv_data_ret); - assert_ptr_equal(ret_f, test_hostkey_clb); - assert_string_equal(priv_data_ret, "DATA"); -} - char * test_pwd_clb1(const char *username, const char *hostname, void *priv) { @@ -458,9 +426,6 @@ test_nc_client_ssh_setting_auth_pref(void **state) (void)state; int ret; - /* initiate client, must be called in first test */ - nc_client_init(); - /* check default prefference settings according to documentation */ ret = nc_client_ssh_get_auth_pref(NC_SSH_AUTH_INTERACTIVE); assert_int_equal(ret, 1); @@ -676,9 +641,6 @@ test_nc_connect_ssh_bad_hello(void **state) session = nc_connect_ssh("127.0.0.1", 8080, NULL); assert_null(session); - - /* destroy client, must be called in last test */ - nc_client_destroy(); } static void @@ -716,7 +678,7 @@ test_nc_client_ssh_ch_add_bind_listen(void **state) assert_int_equal(ret, -1); /* fake a successful CH ssh listening socket */ - will_return(__wrap_nc_sock_listen_inet, 1); + will_return(__wrap_nc_sock_listen_inet, 5); ret = nc_client_ssh_ch_add_bind_listen("127.0.0.1", 4334); assert_int_equal(ret, 0); @@ -808,7 +770,6 @@ main(void) { const struct CMUnitTest tests[] = { cmocka_unit_test_setup_teardown(test_nc_client_ssh_setting_auth_pref, setup_f, teardown_f), - cmocka_unit_test_setup_teardown(test_nc_client_ssh_setting_auth_hostkey_check_clb, setup_f, teardown_f), cmocka_unit_test_setup_teardown(test_nc_client_ssh_setting_auth_password_clb, setup_f, teardown_f), cmocka_unit_test_setup_teardown(test_nc_client_ssh_setting_auth_interactive_clb, setup_f, teardown_f), cmocka_unit_test_setup_teardown(test_nc_client_ssh_setting_auth_privkey_passphrase_clb, setup_f, teardown_f), diff --git a/tests/test_client_thread.c b/tests/test_client_thread.c index 9d0eda0e..bede794e 100644 --- a/tests/test_client_thread.c +++ b/tests/test_client_thread.c @@ -53,8 +53,6 @@ main(void) pthread_t t; int r; - nc_client_init(); - /* * TEST sharing the thread context */ diff --git a/tests/client/test_client_tls.c b/tests/test_client_tls.c similarity index 99% rename from tests/client/test_client_tls.c rename to tests/test_client_tls.c index a3fd4b74..2ead67b4 100644 --- a/tests/client/test_client_tls.c +++ b/tests/test_client_tls.c @@ -87,8 +87,6 @@ test_nc_client_tls_setting_cert_key_paths(void **state) const char *cert, *key; int ret; - nc_client_init(); - /* no certificats are set, nc_client_tls_get_cert_key_paths should output NULL */ nc_client_tls_get_cert_key_paths(&cert, &key); assert_null(cert); diff --git a/tests/test_config_new.c b/tests/test_config_new.c new file mode 100644 index 00000000..03ebb0bd --- /dev/null +++ b/tests/test_config_new.c @@ -0,0 +1,213 @@ +/** + * @file test_keystore.c + * @author Roman Janota + * @brief libnetconf2 Linux PAM keyboard-interactive authentication test + * + * @copyright + * Copyright (c) 2022 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 "tests/config.h" + +#define NC_ACCEPT_TIMEOUT 2000 +#define NC_PS_POLL_TIMEOUT 2000 + +struct ly_ctx *ctx; + +struct test_state { + pthread_barrier_t barrier; +}; + +static void * +server_thread(void *arg) +{ + int ret; + NC_MSG_TYPE msgtype; + struct nc_session *session; + struct nc_pollsession *ps; + struct test_state *state = arg; + + ps = nc_ps_new(); + assert_non_null(ps); + + /* accept a session and add it to the poll session structure */ + pthread_barrier_wait(&state->barrier); + msgtype = nc_accept(NC_ACCEPT_TIMEOUT, ctx, &session); + assert_int_equal(msgtype, NC_MSG_HELLO); + + ret = nc_ps_add_session(ps, session); + assert_int_equal(ret, 0); + + do { + ret = nc_ps_poll(ps, NC_PS_POLL_TIMEOUT, NULL); + assert_int_equal(ret & NC_PSPOLL_RPC, NC_PSPOLL_RPC); + } while (!(ret & NC_PSPOLL_SESSION_TERM)); + + nc_ps_clear(ps, 1, NULL); + nc_ps_free(ps); + return NULL; +} + +static char * +auth_password(const char *username, const char *hostname, void *priv) +{ + (void) username; + (void) hostname; + (void) priv; + + /* set the reply to password authentication */ + return strdup("testpassword123"); +} + +static void * +client_thread(void *arg) +{ + int ret; + struct nc_session *session = NULL; + struct test_state *state = arg; + + /* skip all hostkey and known_hosts checks */ + nc_client_ssh_set_knownhosts_mode(NC_SSH_KNOWNHOSTS_SKIP); + + ret = nc_client_set_schema_searchpath(MODULES_DIR); + assert_int_equal(ret, 0); + + ret = nc_client_ssh_set_username("client"); + assert_int_equal(ret, 0); + + nc_client_ssh_set_auth_password_clb(auth_password, NULL); + + pthread_barrier_wait(&state->barrier); + session = nc_connect_ssh("127.0.0.1", 10005, NULL); + assert_non_null(session); + + nc_session_free(session, NULL); + return NULL; +} + +static void +test_nc_config_new(void **state) +{ + int ret, i; + pthread_t tids[2]; + + assert_non_null(state); + + ret = pthread_create(&tids[0], NULL, client_thread, *state); + assert_int_equal(ret, 0); + ret = pthread_create(&tids[1], NULL, server_thread, *state); + assert_int_equal(ret, 0); + + for (i = 0; i < 2; i++) { + pthread_join(tids[i], NULL); + } +} + +static int +setup_f(void **state) +{ + int ret; + struct lyd_node *tree = NULL; + struct test_state *test_state; + + nc_verbosity(NC_VERB_VERBOSE); + + /* init barrier */ + test_state = malloc(sizeof *test_state); + assert_non_null(test_state); + + ret = pthread_barrier_init(&test_state->barrier, NULL, 2); + assert_int_equal(ret, 0); + + *state = test_state; + + /* new context */ + ret = ly_ctx_new(MODULES_DIR, 0, &ctx); + assert_int_equal(ret, 0); + + /* initialize the context by loading default modules */ + ret = nc_server_init_ctx(&ctx); + assert_int_equal(ret, 0); + + /* load ietf-netconf-server module and it's imports */ + ret = nc_server_config_load_modules(&ctx); + assert_int_equal(ret, 0); + + /* create new hostkey data */ + ret = nc_server_config_add_ssh_hostkey(ctx, "endpt", "hostkey", TESTS_DIR "/data/server.key", NULL, &tree); + 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", 10005, &tree); + assert_int_equal(ret, 0); + + /* create the host-key algorithms data */ + ret = nc_server_config_add_ssh_host_key_algs(ctx, "endpt", &tree, 1, "rsa-sha2-512"); + assert_int_equal(ret, 0); + + /* create the client authentication data, password only */ + ret = nc_server_config_add_ssh_user_password(ctx, "endpt", "client", "testpassword123", &tree); + assert_int_equal(ret, 0); + + /* configure the server based on the data */ + ret = nc_server_config_setup_data(tree); + assert_int_equal(ret, 0); + + ret = nc_server_init(); + assert_int_equal(ret, 0); + + /* initialize client */ + ret = nc_client_init(); + assert_int_equal(ret, 0); + + lyd_free_all(tree); + + return 0; +} + +static int +teardown_f(void **state) +{ + int ret = 0; + struct test_state *test_state; + + assert_non_null(state); + test_state = *state; + + ret = pthread_barrier_destroy(&test_state->barrier); + assert_int_equal(ret, 0); + + free(*state); + nc_client_destroy(); + nc_server_destroy(); + ly_ctx_destroy(ctx); + + return 0; +} + +int +main(void) +{ + const struct CMUnitTest tests[] = { + cmocka_unit_test_setup_teardown(test_nc_config_new, setup_f, teardown_f), + }; + + setenv("CMOCKA_TEST_ABORT", "1", 1); + return cmocka_run_group_tests(tests, NULL, NULL); +} diff --git a/tests/test_crl.c b/tests/test_crl.c new file mode 100644 index 00000000..6cd6dc77 --- /dev/null +++ b/tests/test_crl.c @@ -0,0 +1,218 @@ +/** + * @file test_crl.c + * @author Roman Janota + * @brief libnetconf2 TLS CRL test + * + * @copyright + * Copyright (c) 2023 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 "tests/config.h" + +#define NC_ACCEPT_TIMEOUT 2000 +#define NC_PS_POLL_TIMEOUT 2000 + +struct ly_ctx *ctx; + +struct test_state { + pthread_barrier_t barrier; +}; + +char buffer[512]; +char expected[512]; + +static void +test_msg_callback(const struct nc_session *session, NC_VERB_LEVEL level, const char *msg) +{ + (void) level; + (void) session; + + if (strstr(msg, expected)) { + strcpy(buffer, msg); + } + + printf("%s\n", msg); +} + +static void * +server_thread(void *arg) +{ + NC_MSG_TYPE msgtype; + struct nc_session *session; + struct test_state *state = 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"); + + /* accept a session and add it to the poll session structure */ + pthread_barrier_wait(&state->barrier); + msgtype = nc_accept(NC_ACCEPT_TIMEOUT, ctx, &session); + assert_int_equal(msgtype, NC_MSG_ERROR); + + assert_int_not_equal(strlen(buffer), 0); + + nc_session_free(session, NULL); + return NULL; +} + +static void * +client_thread(void *arg) +{ + int ret; + struct nc_session *session = NULL; + struct test_state *state = arg; + + ret = nc_client_set_schema_searchpath(MODULES_DIR); + assert_int_equal(ret, 0); + + /* set client cert */ + ret = nc_client_tls_set_cert_key_paths(TESTS_DIR "/data/client.crt", TESTS_DIR "/data/client.key"); + assert_int_equal(ret, 0); + + /* set client ca */ + ret = nc_client_tls_set_trusted_ca_paths(NULL, TESTS_DIR "/data"); + assert_int_equal(ret, 0); + + pthread_barrier_wait(&state->barrier); + session = nc_connect_tls("127.0.0.1", 10005, NULL); + + nc_session_free(session, NULL); + return NULL; +} + +static void +test_nc_tls(void **state) +{ + int ret, i; + pthread_t tids[2]; + + assert_non_null(state); + + ret = pthread_create(&tids[0], NULL, client_thread, *state); + assert_int_equal(ret, 0); + ret = pthread_create(&tids[1], NULL, server_thread, *state); + assert_int_equal(ret, 0); + + for (i = 0; i < 2; i++) { + pthread_join(tids[i], NULL); + } +} + +static int +setup_f(void **state) +{ + int ret; + struct lyd_node *tree = NULL; + struct test_state *test_state; + + nc_verbosity(NC_VERB_VERBOSE); + + /* init barrier */ + test_state = malloc(sizeof *test_state); + assert_non_null(test_state); + + ret = pthread_barrier_init(&test_state->barrier, NULL, 2); + assert_int_equal(ret, 0); + + *state = test_state; + + ret = ly_ctx_new(MODULES_DIR, 0, &ctx); + assert_int_equal(ret, 0); + + ret = nc_server_init_ctx(&ctx); + assert_int_equal(ret, 0); + + ret = nc_server_config_load_modules(&ctx); + 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", 10005, &tree); + assert_int_equal(ret, 0); + + /* create new server certificate data */ + ret = nc_server_config_add_tls_server_cert(ctx, "endpt", TESTS_DIR "/data/server.key", NULL, TESTS_DIR "/data/server.crt", &tree); + assert_int_equal(ret, 0); + + /* create new end entity client cert data */ + ret = nc_server_config_add_tls_client_cert(ctx, "endpt", "client_cert", TESTS_DIR "/data/client.crt", &tree); + assert_int_equal(ret, 0); + + /* create new client ca data */ + ret = nc_server_config_add_tls_ca_cert(ctx, "endpt", "client_ca", TESTS_DIR "/data/serverca.pem", &tree); + assert_int_equal(ret, 0); + + /* create new cert-to-name */ + ret = nc_server_config_add_tls_ctn(ctx, "endpt", 1, + "04:85:6B:75:D1:1A:86:E0:D8:FE:5B:BD:72:F5:73:1D:07:EA:32:BF:09:11:21:6A:6E:23:78:8E:B6:D5:73:C3:2D", + NC_TLS_CTN_SPECIFIED, "client", &tree); + assert_int_equal(ret, 0); + + /* set path to a CRL file */ + ret = lyd_new_path(tree, ctx, "/ietf-netconf-server:netconf-server/listen/endpoint[name='endpt']/tls/tls-server-parameters/" + "client-authentication/libnetconf2-netconf-server:crl-path", TESTS_DIR "/data/crl.pem", 0, NULL); + assert_int_equal(ret, 0); + + /* configure the server based on the data */ + ret = nc_server_config_setup_data(tree); + assert_int_equal(ret, 0); + + ret = nc_server_init(); + assert_int_equal(ret, 0); + + /* initialize client */ + ret = nc_client_init(); + assert_int_equal(ret, 0); + + lyd_free_all(tree); + + return 0; +} + +static int +teardown_f(void **state) +{ + int ret = 0; + struct test_state *test_state; + + assert_non_null(state); + test_state = *state; + + ret = pthread_barrier_destroy(&test_state->barrier); + assert_int_equal(ret, 0); + + free(*state); + nc_client_destroy(); + nc_server_destroy(); + ly_ctx_destroy(ctx); + + return 0; +} + +int +main(void) +{ + const struct CMUnitTest tests[] = { + cmocka_unit_test_setup_teardown(test_nc_tls, setup_f, teardown_f), + }; + + setenv("CMOCKA_TEST_ABORT", "1", 1); + return cmocka_run_group_tests(tests, NULL, NULL); +} diff --git a/tests/test_ec.c b/tests/test_ec.c new file mode 100644 index 00000000..415588d7 --- /dev/null +++ b/tests/test_ec.c @@ -0,0 +1,279 @@ +/** + * @file test_ec.c + * @author Roman Janota + * @brief libnetconf2 EC keys authentication test + * + * @copyright + * Copyright (c) 2023 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 "tests/config.h" + +#define NC_ACCEPT_TIMEOUT 2000 +#define NC_PS_POLL_TIMEOUT 2000 + +struct ly_ctx *ctx; + +struct test_state { + pthread_barrier_t barrier; + const char *client_username; + const char *client_privkey; + const char *client_pubkey; +}; + +static void * +server_thread(void *arg) +{ + int ret; + NC_MSG_TYPE msgtype; + struct nc_session *session; + struct nc_pollsession *ps; + struct test_state *state = arg; + + (void) arg; + + ps = nc_ps_new(); + assert_non_null(ps); + + /* accept a session and add it to the poll session structure */ + pthread_barrier_wait(&state->barrier); + msgtype = nc_accept(NC_ACCEPT_TIMEOUT, ctx, &session); + assert_int_equal(msgtype, NC_MSG_HELLO); + + ret = nc_ps_add_session(ps, session); + assert_int_equal(ret, 0); + + do { + ret = nc_ps_poll(ps, NC_PS_POLL_TIMEOUT, NULL); + assert_int_equal(ret & NC_PSPOLL_RPC, NC_PSPOLL_RPC); + } while (!(ret & NC_PSPOLL_SESSION_TERM)); + + nc_ps_clear(ps, 1, NULL); + nc_ps_free(ps); + return NULL; +} + +static void * +client_thread(void *arg) +{ + int ret; + struct nc_session *session = NULL; + struct test_state *state = arg; + + /* skip all hostkey and known_hosts checks */ + nc_client_ssh_set_knownhosts_mode(NC_SSH_KNOWNHOSTS_SKIP); + + /* set directory where to search for modules */ + ret = nc_client_set_schema_searchpath(MODULES_DIR); + assert_int_equal(ret, 0); + + /* set ssh username */ + ret = nc_client_ssh_set_username(state->client_username); + assert_int_equal(ret, 0); + + /* add client's key pair */ + ret = nc_client_ssh_add_keypair(state->client_pubkey, state->client_privkey); + assert_int_equal(ret, 0); + + pthread_barrier_wait(&state->barrier); + /* connect */ + session = nc_connect_ssh("127.0.0.1", 10009, NULL); + assert_non_null(session); + + nc_session_free(session, NULL); + return NULL; +} + +static void +test_nc_ec256(void **state) +{ + int ret, i; + pthread_t tids[2]; + struct test_state *client; + + /* set specific data for the client */ + assert_non_null(state); + client = *state; + + /* client */ + client->client_username = "test_ec256"; + client->client_pubkey = TESTS_DIR "/data/id_ecdsa256.pub"; + client->client_privkey = TESTS_DIR "/data/id_ecdsa256"; + ret = pthread_create(&tids[0], NULL, client_thread, client); + assert_int_equal(ret, 0); + + /* server */ + ret = pthread_create(&tids[1], NULL, server_thread, *state); + assert_int_equal(ret, 0); + + for (i = 0; i < 2; i++) { + pthread_join(tids[i], NULL); + } +} + +static void +test_nc_ec384(void **state) +{ + int ret, i; + pthread_t tids[2]; + struct test_state *client; + + /* set specific data for the client */ + assert_non_null(state); + client = *state; + + /* client */ + client->client_username = "test_ec384"; + client->client_pubkey = TESTS_DIR "/data/id_ecdsa384.pub"; + client->client_privkey = TESTS_DIR "/data/id_ecdsa384"; + ret = pthread_create(&tids[0], NULL, client_thread, client); + assert_int_equal(ret, 0); + + /* server */ + ret = pthread_create(&tids[1], NULL, server_thread, *state); + assert_int_equal(ret, 0); + + for (i = 0; i < 2; i++) { + pthread_join(tids[i], NULL); + } +} + +static void +test_nc_ec521(void **state) +{ + int ret, i; + pthread_t tids[2]; + struct test_state *client; + + /* set specific data for the client */ + assert_non_null(state); + client = *state; + + /* client */ + client->client_username = "test_ec521"; + client->client_pubkey = TESTS_DIR "/data/id_ecdsa521.pub"; + client->client_privkey = TESTS_DIR "/data/id_ecdsa521"; + ret = pthread_create(&tids[0], NULL, client_thread, client); + assert_int_equal(ret, 0); + + /* server */ + ret = pthread_create(&tids[1], NULL, server_thread, *state); + assert_int_equal(ret, 0); + + for (i = 0; i < 2; i++) { + pthread_join(tids[i], NULL); + } +} + +static int +setup_f(void **state) +{ + int ret; + struct lyd_node *tree = NULL; + struct test_state *test_state; + + nc_verbosity(NC_VERB_VERBOSE); + + /* init barrier */ + test_state = malloc(sizeof *test_state); + assert_non_null(test_state); + + ret = pthread_barrier_init(&test_state->barrier, NULL, 2); + assert_int_equal(ret, 0); + + *state = test_state; + + /* create new context */ + ret = ly_ctx_new(MODULES_DIR, 0, &ctx); + assert_int_equal(ret, 0); + + /* load default modules into context */ + ret = nc_server_init_ctx(&ctx); + assert_int_equal(ret, 0); + + /* load ietf-netconf-server module and it's imports into context */ + ret = nc_server_config_load_modules(&ctx); + assert_int_equal(ret, 0); + + 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", 10009, &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); + assert_int_equal(ret, 0); + + ret = nc_server_config_add_ssh_user_pubkey(ctx, "endpt", "test_ec384", "pubkey", TESTS_DIR "/data/id_ecdsa384.pub", &tree); + assert_int_equal(ret, 0); + + ret = nc_server_config_add_ssh_user_pubkey(ctx, "endpt", "test_ec521", "pubkey", TESTS_DIR "/data/id_ecdsa521.pub", &tree); + assert_int_equal(ret, 0); + + /* configure the server based on the data */ + ret = nc_server_config_setup_data(tree); + assert_int_equal(ret, 0); + + /* initialize server */ + ret = nc_server_init(); + assert_int_equal(ret, 0); + + /* initialize client */ + ret = nc_client_init(); + assert_int_equal(ret, 0); + + lyd_free_all(tree); + + return 0; +} + +static int +teardown_f(void **state) +{ + int ret = 0; + struct test_state *test_state; + + assert_non_null(state); + test_state = *state; + + ret = pthread_barrier_destroy(&test_state->barrier); + assert_int_equal(ret, 0); + + free(*state); + nc_client_destroy(); + nc_server_destroy(); + ly_ctx_destroy(ctx); + + return 0; +} + +int +main(void) +{ + const struct CMUnitTest tests[] = { + cmocka_unit_test_setup_teardown(test_nc_ec256, setup_f, teardown_f), + cmocka_unit_test_setup_teardown(test_nc_ec384, setup_f, teardown_f), + cmocka_unit_test_setup_teardown(test_nc_ec521, setup_f, teardown_f), + }; + + setenv("CMOCKA_TEST_ABORT", "1", 1); + return cmocka_run_group_tests(tests, NULL, NULL); +} diff --git a/tests/test_ed25519.c b/tests/test_ed25519.c new file mode 100644 index 00000000..714a88a8 --- /dev/null +++ b/tests/test_ed25519.c @@ -0,0 +1,208 @@ +/** + * @file test_ed25519.c + * @author Roman Janota + * @brief libnetconf2 ED25519 key authentication test + * + * @copyright + * Copyright (c) 2023 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 "tests/config.h" + +#define NC_ACCEPT_TIMEOUT 2000 +#define NC_PS_POLL_TIMEOUT 2000 + +struct ly_ctx *ctx; + +struct test_state { + pthread_barrier_t barrier; +}; + +static void * +server_thread(void *arg) +{ + int ret; + NC_MSG_TYPE msgtype; + struct nc_session *session; + struct nc_pollsession *ps; + struct test_state *state = arg; + + (void) arg; + + ps = nc_ps_new(); + assert_non_null(ps); + + /* accept a session and add it to the poll session structure */ + pthread_barrier_wait(&state->barrier); + msgtype = nc_accept(NC_ACCEPT_TIMEOUT, ctx, &session); + assert_int_equal(msgtype, NC_MSG_HELLO); + + ret = nc_ps_add_session(ps, session); + assert_int_equal(ret, 0); + + do { + ret = nc_ps_poll(ps, NC_PS_POLL_TIMEOUT, NULL); + assert_int_equal(ret & NC_PSPOLL_RPC, NC_PSPOLL_RPC); + } while (!(ret & NC_PSPOLL_SESSION_TERM)); + + nc_ps_clear(ps, 1, NULL); + nc_ps_free(ps); + return NULL; +} + +static void * +client_thread(void *arg) +{ + int ret; + struct nc_session *session = NULL; + struct test_state *state = arg; + + /* skip all hostkey and known_hosts checks */ + nc_client_ssh_set_knownhosts_mode(NC_SSH_KNOWNHOSTS_SKIP); + + /* set directory where to search for modules */ + ret = nc_client_set_schema_searchpath(MODULES_DIR); + assert_int_equal(ret, 0); + + /* set ssh username */ + ret = nc_client_ssh_set_username("test_ed25519"); + assert_int_equal(ret, 0); + + /* add client's key pair */ + ret = nc_client_ssh_add_keypair(TESTS_DIR "/data/id_ed25519.pub", TESTS_DIR "/data/id_ed25519"); + assert_int_equal(ret, 0); + + pthread_barrier_wait(&state->barrier); + /* connect */ + session = nc_connect_ssh("127.0.0.1", 10009, NULL); + assert_non_null(session); + + nc_session_free(session, NULL); + return NULL; +} + +static void +test_nc_ed25519(void **state) +{ + int ret, i; + pthread_t tids[2]; + + assert_non_null(state); + + /* client */ + ret = pthread_create(&tids[0], NULL, client_thread, *state); + assert_int_equal(ret, 0); + + /* server */ + ret = pthread_create(&tids[1], NULL, server_thread, *state); + assert_int_equal(ret, 0); + + for (i = 0; i < 2; i++) { + pthread_join(tids[i], NULL); + } +} + +static int +setup_f(void **state) +{ + int ret; + struct lyd_node *tree = NULL; + struct test_state *test_state; + + nc_verbosity(NC_VERB_VERBOSE); + + /* init barrier */ + test_state = malloc(sizeof *test_state); + assert_non_null(test_state); + + ret = pthread_barrier_init(&test_state->barrier, NULL, 2); + assert_int_equal(ret, 0); + + *state = test_state; + + /* create new context */ + ret = ly_ctx_new(MODULES_DIR, 0, &ctx); + assert_int_equal(ret, 0); + + /* load default modules into context */ + ret = nc_server_init_ctx(&ctx); + assert_int_equal(ret, 0); + + /* load ietf-netconf-server module and it's imports into context */ + ret = nc_server_config_load_modules(&ctx); + assert_int_equal(ret, 0); + + 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", 10009, &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); + assert_int_equal(ret, 0); + + /* configure the server based on the data */ + ret = nc_server_config_setup_data(tree); + assert_int_equal(ret, 0); + + /* initialize server */ + ret = nc_server_init(); + assert_int_equal(ret, 0); + + /* initialize client */ + ret = nc_client_init(); + assert_int_equal(ret, 0); + + lyd_free_all(tree); + + return 0; +} + +static int +teardown_f(void **state) +{ + int ret = 0; + struct test_state *test_state; + + assert_non_null(state); + test_state = *state; + + ret = pthread_barrier_destroy(&test_state->barrier); + assert_int_equal(ret, 0); + + free(*state); + nc_client_destroy(); + nc_server_destroy(); + ly_ctx_destroy(ctx); + + return 0; +} + +int +main(void) +{ + const struct CMUnitTest tests[] = { + cmocka_unit_test_setup_teardown(test_nc_ed25519, setup_f, teardown_f), + }; + + setenv("CMOCKA_TEST_ABORT", "1", 1); + return cmocka_run_group_tests(tests, NULL, NULL); +} diff --git a/tests/test_endpt_share_clients.c b/tests/test_endpt_share_clients.c new file mode 100644 index 00000000..7099d77e --- /dev/null +++ b/tests/test_endpt_share_clients.c @@ -0,0 +1,338 @@ +/** + * @file test_endpt_share_clients.c + * @author Roman Janota + * @brief libnetconf2 Sharing clients between endpoints test + * + * @copyright + * Copyright (c) 2023 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 "tests/config.h" + +#define NC_ACCEPT_TIMEOUT 2000 +#define NC_PS_POLL_TIMEOUT 2000 + +struct ly_ctx *ctx; + +struct test_state { + pthread_barrier_t barrier; +}; + +static void * +server_thread(void *arg) +{ + int ret; + NC_MSG_TYPE msgtype; + struct nc_session *session; + struct nc_pollsession *ps; + struct test_state *state = arg; + + ps = nc_ps_new(); + assert_non_null(ps); + + /* accept a session and add it to the poll session structure */ + pthread_barrier_wait(&state->barrier); + msgtype = nc_accept(NC_ACCEPT_TIMEOUT, ctx, &session); + assert_int_equal(msgtype, NC_MSG_HELLO); + + ret = nc_ps_add_session(ps, session); + assert_int_equal(ret, 0); + + do { + ret = nc_ps_poll(ps, NC_PS_POLL_TIMEOUT, NULL); + assert_int_equal(ret & NC_PSPOLL_RPC, NC_PSPOLL_RPC); + } while (!(ret & NC_PSPOLL_SESSION_TERM)); + + nc_ps_clear(ps, 1, NULL); + nc_ps_free(ps); + return NULL; +} + +static void * +client_thread_ssh(void *arg) +{ + int ret; + struct nc_session *session = NULL; + struct test_state *state = arg; + + /* skip all hostkey and known_hosts checks */ + nc_client_ssh_set_knownhosts_mode(NC_SSH_KNOWNHOSTS_SKIP); + + /* set directory where to search for modules */ + ret = nc_client_set_schema_searchpath(MODULES_DIR); + assert_int_equal(ret, 0); + + /* set ssh username */ + ret = nc_client_ssh_set_username("client"); + assert_int_equal(ret, 0); + + /* add client's key pair */ + ret = nc_client_ssh_add_keypair(TESTS_DIR "/data/key_rsa.pub", TESTS_DIR "/data/key_rsa"); + assert_int_equal(ret, 0); + + /* wait for the server to reach polling */ + pthread_barrier_wait(&state->barrier); + + /* connect */ + session = nc_connect_ssh("127.0.0.1", 10005, NULL); + assert_non_null(session); + + nc_session_free(session, NULL); + return NULL; +} + +static void +nc_test_endpt_share_clients_ssh(void **state) +{ + int ret, i; + pthread_t tids[2]; + + assert_non_null(state); + + ret = pthread_create(&tids[0], NULL, client_thread_ssh, *state); + assert_int_equal(ret, 0); + ret = pthread_create(&tids[1], NULL, server_thread, *state); + assert_int_equal(ret, 0); + + for (i = 0; i < 2; i++) { + pthread_join(tids[i], NULL); + } +} + +static void * +client_thread_tls(void *arg) +{ + int ret; + struct nc_session *session = NULL; + struct test_state *state = arg; + + /* set directory where to search for modules */ + ret = nc_client_set_schema_searchpath(MODULES_DIR); + assert_int_equal(ret, 0); + + /* set client cert */ + ret = nc_client_tls_set_cert_key_paths(TESTS_DIR "/data/client.crt", TESTS_DIR "/data/client.key"); + assert_int_equal(ret, 0); + + /* set client ca */ + ret = nc_client_tls_set_trusted_ca_paths(NULL, TESTS_DIR "/data"); + assert_int_equal(ret, 0); + + pthread_barrier_wait(&state->barrier); + session = nc_connect_tls("127.0.0.1", 10008, NULL); + assert_non_null(session); + + nc_session_free(session, NULL); + return NULL; +} + +static void +nc_test_endpt_share_clients_tls(void **state) +{ + int ret, i; + pthread_t tids[2]; + + assert_non_null(state); + + ret = pthread_create(&tids[0], NULL, client_thread_tls, *state); + assert_int_equal(ret, 0); + ret = pthread_create(&tids[1], NULL, server_thread, *state); + assert_int_equal(ret, 0); + + for (i = 0; i < 2; i++) { + pthread_join(tids[i], NULL); + } +} + +static int +setup_ssh(void **state) +{ + int ret; + struct lyd_node *tree = NULL; + struct test_state *test_state; + + nc_verbosity(NC_VERB_VERBOSE); + + /* init barrier */ + test_state = malloc(sizeof *test_state); + assert_non_null(test_state); + + ret = pthread_barrier_init(&test_state->barrier, NULL, 2); + assert_int_equal(ret, 0); + + *state = test_state; + + /* create new context */ + ret = ly_ctx_new(MODULES_DIR, 0, &ctx); + assert_int_equal(ret, 0); + + /* load default modules into context */ + ret = nc_server_init_ctx(&ctx); + assert_int_equal(ret, 0); + + /* load ietf-netconf-server module and it's imports into context */ + ret = nc_server_config_load_modules(&ctx); + assert_int_equal(ret, 0); + + /* create the first SSH endpoint with a client reference to the second endpoint */ + 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", 10005, &tree); + assert_int_equal(ret, 0); + + ret = nc_server_config_add_ssh_endpoint_client_ref(ctx, "SSH_endpt_1", "SSH_endpt_2", &tree); + assert_int_equal(ret, 0); + + /* create the second SSH endpoint with a single client */ + 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", 10006, &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); + assert_int_equal(ret, 0); + + /* configure the server based on the yang data */ + ret = nc_server_config_setup_data(tree); + assert_int_equal(ret, 0); + + /* initialize the server */ + ret = nc_server_init(); + assert_int_equal(ret, 0); + + /* initialize client */ + ret = nc_client_init(); + assert_int_equal(ret, 0); + + lyd_free_all(tree); + + return 0; +} + +static int +setup_tls(void **state) +{ + int ret; + struct lyd_node *tree = NULL; + struct test_state *test_state; + + nc_verbosity(NC_VERB_VERBOSE); + + /* init barrier */ + test_state = malloc(sizeof *test_state); + assert_non_null(test_state); + + ret = pthread_barrier_init(&test_state->barrier, NULL, 2); + assert_int_equal(ret, 0); + + *state = test_state; + + /* create new context */ + ret = ly_ctx_new(MODULES_DIR, 0, &ctx); + assert_int_equal(ret, 0); + + /* load default modules into context */ + ret = nc_server_init_ctx(&ctx); + assert_int_equal(ret, 0); + + /* load ietf-netconf-server module and it's imports into context */ + ret = nc_server_config_load_modules(&ctx); + assert_int_equal(ret, 0); + + /* create the first TLS endpoint with a single end entity client cert and a CTN entry */ + 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", 10007, &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); + assert_int_equal(ret, 0); + + ret = nc_server_config_add_tls_ca_cert(ctx, "TLS_endpt_1", "cert_ca", TESTS_DIR "/data/serverca.pem", &tree); + assert_int_equal(ret, 0); + + ret = nc_server_config_add_tls_ctn(ctx, "TLS_endpt_1", 1, + "04:85:6B:75:D1:1A:86:E0:D8:FE:5B:BD:72:F5:73:1D:07:EA:32:BF:09:11:21:6A:6E:23:78:8E:B6:D5:73:C3:2D", + NC_TLS_CTN_SPECIFIED, "client", &tree); + assert_int_equal(ret, 0); + + /* create the second TLS endpoint with a reference to the first endpoint */ + ret = nc_server_config_add_tls_server_cert(ctx, "TLS_endpt_2", + 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", 10008, &tree); + assert_int_equal(ret, 0); + + ret = nc_server_config_add_tls_endpoint_client_ref(ctx, "TLS_endpt_2", "TLS_endpt_1", &tree); + assert_int_equal(ret, 0); + + /* configure the server based on the yang data */ + ret = nc_server_config_setup_data(tree); + assert_int_equal(ret, 0); + + /* initialize the server */ + ret = nc_server_init(); + assert_int_equal(ret, 0); + + /* initialize client */ + ret = nc_client_init(); + assert_int_equal(ret, 0); + + lyd_free_all(tree); + + return 0; +} + +static int +teardown_f(void **state) +{ + int ret = 0; + struct test_state *test_state; + + assert_non_null(state); + test_state = *state; + + ret = pthread_barrier_destroy(&test_state->barrier); + assert_int_equal(ret, 0); + + free(*state); + nc_client_destroy(); + nc_server_destroy(); + ly_ctx_destroy(ctx); + + return 0; +} + +int +main(void) +{ + const struct CMUnitTest tests[] = { + cmocka_unit_test_setup_teardown(nc_test_endpt_share_clients_ssh, setup_ssh, teardown_f), + cmocka_unit_test_setup_teardown(nc_test_endpt_share_clients_tls, setup_tls, teardown_f), + }; + + setenv("CMOCKA_TEST_ABORT", "1", 1); + return cmocka_run_group_tests(tests, NULL, NULL); +} diff --git a/tests/test_ks_ts.c b/tests/test_ks_ts.c new file mode 100644 index 00000000..940f357c --- /dev/null +++ b/tests/test_ks_ts.c @@ -0,0 +1,330 @@ +/** + * @file test_ks_ts.c + * @author Roman Janota + * @brief libnetconf2 Keystore and trustore usage test. + * + * @copyright + * Copyright (c) 2023 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 "tests/config.h" + +#define NC_ACCEPT_TIMEOUT 2000 +#define NC_PS_POLL_TIMEOUT 2000 + +struct ly_ctx *ctx; + +struct test_state { + pthread_barrier_t barrier; +}; + +static void * +server_thread(void *arg) +{ + int ret; + NC_MSG_TYPE msgtype; + struct nc_session *session; + struct nc_pollsession *ps; + struct test_state *state = arg; + + ps = nc_ps_new(); + assert_non_null(ps); + + /* accept a session and add it to the poll session structure */ + pthread_barrier_wait(&state->barrier); + msgtype = nc_accept(NC_ACCEPT_TIMEOUT, ctx, &session); + assert_int_equal(msgtype, NC_MSG_HELLO); + + ret = nc_ps_add_session(ps, session); + assert_int_equal(ret, 0); + + do { + ret = nc_ps_poll(ps, NC_PS_POLL_TIMEOUT, NULL); + assert_int_equal(ret & NC_PSPOLL_RPC, NC_PSPOLL_RPC); + } while (!(ret & NC_PSPOLL_SESSION_TERM)); + + nc_ps_clear(ps, 1, NULL); + nc_ps_free(ps); + return NULL; +} + +static void * +client_thread_ssh(void *arg) +{ + int ret; + struct nc_session *session = NULL; + struct test_state *state = arg; + + /* skip all hostkey and known_hosts checks */ + nc_client_ssh_set_knownhosts_mode(NC_SSH_KNOWNHOSTS_SKIP); + + ret = nc_client_set_schema_searchpath(MODULES_DIR); + assert_int_equal(ret, 0); + + ret = nc_client_ssh_set_username("client"); + assert_int_equal(ret, 0); + + ret = nc_client_ssh_add_keypair(TESTS_DIR "/data/id_ed25519.pub", TESTS_DIR "/data/id_ed25519"); + assert_int_equal(ret, 0); + + pthread_barrier_wait(&state->barrier); + session = nc_connect_ssh("127.0.0.1", 10005, NULL); + assert_non_null(session); + + nc_session_free(session, NULL); + return NULL; +} + +static void +test_nc_ks_ts_ssh(void **state) +{ + int ret, i; + pthread_t tids[2]; + + assert_non_null(state); + + ret = pthread_create(&tids[0], NULL, client_thread_ssh, *state); + assert_int_equal(ret, 0); + ret = pthread_create(&tids[1], NULL, server_thread, *state); + assert_int_equal(ret, 0); + + for (i = 0; i < 2; i++) { + pthread_join(tids[i], NULL); + } +} + +static int +setup_ssh(void **state) +{ + int ret; + struct lyd_node *tree = NULL; + struct test_state *test_state; + + nc_verbosity(NC_VERB_VERBOSE); + + /* init barrier */ + test_state = malloc(sizeof *test_state); + assert_non_null(test_state); + + ret = pthread_barrier_init(&test_state->barrier, NULL, 2); + assert_int_equal(ret, 0); + + *state = test_state; + + ret = ly_ctx_new(MODULES_DIR, 0, &ctx); + assert_int_equal(ret, 0); + + ret = nc_server_init_ctx(&ctx); + assert_int_equal(ret, 0); + + 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", 10005, &tree); + assert_int_equal(ret, 0); + + ret = nc_server_config_add_ssh_keystore_ref(ctx, "endpt", "hostkey", "test_keystore", &tree); + assert_int_equal(ret, 0); + + 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); + assert_int_equal(ret, 0); + + ret = nc_server_config_add_truststore_pubkey(ctx, "test_truststore", "pubkey", TESTS_DIR "/data/id_ed25519.pub", &tree); + assert_int_equal(ret, 0); + + /* configure the server based on the data */ + ret = nc_server_config_setup_data(tree); + assert_int_equal(ret, 0); + + ret = nc_server_init(); + assert_int_equal(ret, 0); + + /* initialize client */ + ret = nc_client_init(); + assert_int_equal(ret, 0); + + lyd_free_all(tree); + + return 0; +} + +static void * +client_thread_tls(void *arg) +{ + int ret; + struct nc_session *session = NULL; + struct test_state *state = arg; + + ret = nc_client_set_schema_searchpath(MODULES_DIR); + assert_int_equal(ret, 0); + + /* set client cert */ + ret = nc_client_tls_set_cert_key_paths(TESTS_DIR "/data/client.crt", TESTS_DIR "/data/client.key"); + assert_int_equal(ret, 0); + + /* set client ca */ + ret = nc_client_tls_set_trusted_ca_paths(NULL, TESTS_DIR "/data"); + assert_int_equal(ret, 0); + + pthread_barrier_wait(&state->barrier); + session = nc_connect_tls("127.0.0.1", 10005, NULL); + assert_non_null(session); + + nc_session_free(session, NULL); + return NULL; +} + +static void +test_nc_ks_ts_tls(void **state) +{ + int ret, i; + pthread_t tids[2]; + + assert_non_null(state); + + ret = pthread_create(&tids[0], NULL, client_thread_tls, *state); + assert_int_equal(ret, 0); + ret = pthread_create(&tids[1], NULL, server_thread, *state); + assert_int_equal(ret, 0); + + for (i = 0; i < 2; i++) { + pthread_join(tids[i], NULL); + } +} + +static int +setup_tls(void **state) +{ + int ret; + struct lyd_node *tree = NULL; + struct test_state *test_state; + + nc_verbosity(NC_VERB_VERBOSE); + + /* init barrier */ + test_state = malloc(sizeof *test_state); + assert_non_null(test_state); + + ret = pthread_barrier_init(&test_state->barrier, NULL, 2); + assert_int_equal(ret, 0); + + *state = test_state; + + /* new ctx */ + ret = ly_ctx_new(MODULES_DIR, 0, &ctx); + assert_int_equal(ret, 0); + + /* init ctx */ + ret = nc_server_init_ctx(&ctx); + assert_int_equal(ret, 0); + + /* load ietf netconf server module and its requisities */ + ret = nc_server_config_load_modules(&ctx); + assert_int_equal(ret, 0); + + /* new tls bind */ + ret = nc_server_config_add_address_port(ctx, "endpt", NC_TI_OPENSSL, "127.0.0.1", 10005, &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); + assert_int_equal(ret, 0); + + /* new keystore cert belonging to the key pair */ + ret = nc_server_config_add_keystore_cert(ctx, "server_key", "server_cert", TESTS_DIR "/data/server.crt", &tree); + assert_int_equal(ret, 0); + + /* new truststore client cert */ + ret = nc_server_config_add_truststore_cert(ctx, "ee_cert_bag", "ee_cert", TESTS_DIR "/data/client.crt", &tree); + assert_int_equal(ret, 0); + + /* new truststore client CA cert */ + ret = nc_server_config_add_truststore_cert(ctx, "ca_cert_bag", "ca_cert", TESTS_DIR "/data/serverca.pem", &tree); + assert_int_equal(ret, 0); + + /* new keystore ref for the TLS server cert */ + ret = nc_server_config_add_tls_keystore_ref(ctx, "endpt", "server_key", "server_cert", &tree); + assert_int_equal(ret, 0); + + /* new truststore ref for the client cert */ + ret = nc_server_config_add_tls_client_cert_truststore_ref(ctx, "endpt", "ee_cert_bag", &tree); + assert_int_equal(ret, 0); + + /* new truststore ref for the client CA cert */ + ret = nc_server_config_add_tls_ca_cert_truststore_ref(ctx, "endpt", "ca_cert_bag", &tree); + assert_int_equal(ret, 0); + + /* new cert-to-name */ + ret = nc_server_config_add_tls_ctn(ctx, "endpt", 1, + "04:85:6B:75:D1:1A:86:E0:D8:FE:5B:BD:72:F5:73:1D:07:EA:32:BF:09:11:21:6A:6E:23:78:8E:B6:D5:73:C3:2D", + NC_TLS_CTN_SPECIFIED, "client", &tree); + assert_int_equal(ret, 0); + + /* configure the server based on the data */ + ret = nc_server_config_setup_data(tree); + assert_int_equal(ret, 0); + + ret = nc_server_init(); + assert_int_equal(ret, 0); + + /* initialize client */ + ret = nc_client_init(); + assert_int_equal(ret, 0); + + lyd_free_all(tree); + + return 0; +} + +static int +teardown_f(void **state) +{ + int ret = 0; + struct test_state *test_state; + + assert_non_null(state); + test_state = *state; + + ret = pthread_barrier_destroy(&test_state->barrier); + assert_int_equal(ret, 0); + + free(*state); + nc_client_destroy(); + nc_server_destroy(); + ly_ctx_destroy(ctx); + + return 0; +} + +int +main(void) +{ + const struct CMUnitTest tests[] = { + cmocka_unit_test_setup_teardown(test_nc_ks_ts_ssh, setup_ssh, teardown_f), + cmocka_unit_test_setup_teardown(test_nc_ks_ts_tls, setup_tls, teardown_f), + }; + + setenv("CMOCKA_TEST_ABORT", "1", 1); + return cmocka_run_group_tests(tests, NULL, NULL); +} diff --git a/tests/test_pam.c b/tests/test_pam.c deleted file mode 100644 index 8fce4782..00000000 --- a/tests/test_pam.c +++ /dev/null @@ -1,193 +0,0 @@ -/** - * @file test_pam.c - * @author Roman Janota - * @brief libnetconf2 Linux PAM keyboard-interactive authentication test - * - * @copyright - * Copyright (c) 2022 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 "tests/config.h" - -#define nc_assert(cond) if (!(cond)) { fprintf(stderr, "assert failed (%s:%d)\n", __FILE__, __LINE__); abort(); } - -#define NC_ACCEPT_TIMEOUT 5000 -#define NC_PS_POLL_TIMEOUT 5000 - -struct ly_ctx *ctx; - -static void * -server_thread(void *arg) -{ - int ret; - NC_MSG_TYPE msgtype; - struct nc_session *session; - struct nc_pollsession *ps; - - (void) arg; - ps = nc_ps_new(); - nc_assert(ps); - - /* accept a session and add it to the poll session structure */ - msgtype = nc_accept(NC_ACCEPT_TIMEOUT, ctx, &session); - nc_assert(msgtype == NC_MSG_HELLO); - ret = nc_ps_add_session(ps, session); - nc_assert(!ret); - ret = nc_ps_poll(ps, NC_PS_POLL_TIMEOUT, NULL); - nc_assert(ret & NC_PSPOLL_RPC); - ret = nc_ps_poll(ps, NC_PS_POLL_TIMEOUT, NULL); - nc_assert(ret & NC_PSPOLL_RPC); - nc_ps_clear(ps, 1, NULL); - - nc_ps_free(ps); - nc_thread_destroy(); - return NULL; -} - -static int -clb_hostkeys(const char *name, void *user_data, char **privkey_path, char **privkey_data, - NC_SSH_KEY_TYPE *privkey_type) -{ - (void) user_data; - (void) privkey_data; - (void) privkey_type; - - /* set the path to the testing private keys */ - if (!strcmp(name, "key_rsa")) { - *privkey_path = strdup(TESTS_DIR "/data/key_rsa"); - return 0; - } else if (!strcmp(name, "key_dsa")) { - *privkey_path = strdup(TESTS_DIR "/data/key_dsa"); - return 0; - } - - return 1; -} - -static char * -auth_interactive(const char *auth_name, const char *instruction, const char *prompt, int echo, void *priv) -{ - (void) instruction; - (void) echo; - (void) auth_name; - (void) priv; - - /* send the replies to keyboard-interactive authentication */ - if (strstr(prompt, "backwards")) { - return strdup("tset"); - } else if (strstr(prompt, "1+1")) { - return strdup("2"); - } else { - return NULL; - } -} - -static int -ssh_hostkey_check_clb(const char *hostname, ssh_session session, void *priv) -{ - (void)hostname; - (void)session; - (void)priv; - /* redundant in this test, nonetheless this callback has to be set */ - - return 0; -} - -static void * -client_thread(void *arg) -{ - (void) arg; - int ret; - struct nc_session *session = NULL; - - printf("SSH client started.\n"); - - /* initialize client */ - nc_client_init(); - ret = nc_client_set_schema_searchpath(TESTS_DIR "/data/modules"); - nc_assert(!ret); - /* skip the knownhost check */ - nc_client_ssh_set_auth_hostkey_check_clb(ssh_hostkey_check_clb, NULL); - - ret = nc_client_ssh_set_username("test"); - nc_assert(!ret); - - /* set keyboard-interactive authentication callback */ - nc_client_ssh_set_auth_interactive_clb(auth_interactive, NULL); - session = nc_connect_ssh("0.0.0.0", 6002, NULL); - nc_assert(session); - - printf("SSH client finished.\n"); - nc_client_destroy(); - - nc_session_free(session, NULL); - nc_thread_destroy(); - return NULL; -} - -int -main(void) -{ - int ret, i; - pthread_t tids[2]; - - ly_ctx_new(TESTS_DIR "/data/modules", 0, &ctx); - nc_assert(ctx); - ly_ctx_load_module(ctx, "ietf-netconf", NULL, NULL); - - nc_verbosity(NC_VERB_VERBOSE); - nc_server_init(); - - /* set callback */ - 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); - nc_assert(!ret); - ret = nc_server_endpt_set_address("main_ssh", "0.0.0.0"); - nc_assert(!ret); - ret = nc_server_endpt_set_port("main_ssh", 6002); - nc_assert(!ret); - ret = nc_server_ssh_endpt_add_hostkey("main_ssh", "key_rsa", -1); - nc_assert(!ret); - - /* in order to use the Linux PAM keyboard-interactive method, - * the PAM module has to know where to find the desired configuration file */ - ret = nc_server_ssh_set_pam_conf_path("netconf.conf", BUILD_DIR "/tests"); - nc_assert(!ret); - - /* only want to test keyboard-interactive auth method */ - ret = nc_server_ssh_endpt_set_auth_methods("main_ssh", NC_SSH_AUTH_INTERACTIVE); - nc_assert(!ret); - - ret = pthread_create(&tids[0], NULL, client_thread, NULL); - nc_assert(!ret); - ret = pthread_create(&tids[1], NULL, server_thread, NULL); - nc_assert(!ret); - - for (i = 0; i < 2; i++) { - pthread_join(tids[i], NULL); - } - - nc_server_destroy(); - ly_ctx_destroy(ctx); - return 0; -} diff --git a/tests/test_replace.c b/tests/test_replace.c new file mode 100644 index 00000000..1fd09371 --- /dev/null +++ b/tests/test_replace.c @@ -0,0 +1,307 @@ +/** + * @file test_replace.c + * @author Roman Janota + * @brief libnetconf2 Non-diff YANG data configuration test + * + * @copyright + * Copyright (c) 2023 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 "tests/config.h" + +#define NC_ACCEPT_TIMEOUT 2000 +#define NC_PS_POLL_TIMEOUT 2000 + +struct ly_ctx *ctx; + +struct test_state { + pthread_barrier_t barrier; +}; + +const char *old_data = + "\n" + " \n" + " 10\n" + " \n" + " old-ssh\n" + " \n" + " \n" + " 127.0.0.1\n" + " 10005\n" + " \n" + " \n" + " \n" + " \n" + " old_key\n" + " \n" + " \n" + " ct:ssh-public-key-format\n" + " MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA6ojtjfDmvyQP1ZkIwBpr97eKDuebvpoglRHRdvVuTpf/gU1VArAQmwGh05i6lm8TkVl1noMlIxLJDcWslaeVn6KyvsX0HhsQtXwqPqwka5UCv6alwf/ivAvcNpcX1j0t/uIGCI4dSiKnzQCyf0FTirzQkjrDZUd3meDhNQTruCalGV4gfNWIq3e1oGuwAn1tLlu9oTrE4HzMpgbNEU6wNmsSqpwGxUhYLoSaM7b0dLmqP+ZczSS0Uac0PFNkehGQ2CYIT80f580o4XGtoLCUUGkp6YCTL4Z2CeBEaJABWjDIDH+dKYIUBqUpz4Th12gXAP+h+3qI6+9eppeHrfrzARDsfLjwUNxQJse1QSArjAytf0FKtGHrORc7W0TiCFvR0zaoUNLTKk7enTiRQ9rfWZOAu44fUvPCaXDE6zXXeaVgoKCo4VHlho36erUcjlEBM+jk28IykbZGtBb6igKvYa1tPSgeYm/zJoFVjQcnr14uci/ft1+Na+hOIEoEEiKxcAPk2b2vBKNlRIW7WLJ3u7ZiuQEJTNm6+3cE4+lfwaBCBqBToE+dpzvoUXoMyFFReUFd1O5axu4fXgt00jMaOQxmE0v9OmR/pL/PWIflVF4Zz5yVONYaDVc7l+veY0oEZruEPJ0hlEgxuCzLrcMhjufl2qE2Q7fQIaav/1NqBVkCAwEAAQ==\n" + " ct:rsa-private-key-format\n" + " MIIJKAIBAAKCAgEA6ojtjfDmvyQP1ZkIwBpr97eKDuebvpoglRHRdvVuTpf/gU1VArAQmwGh05i6lm8TkVl1noMlIxLJDcWslaeVn6KyvsX0HhsQtXwqPqwka5UCv6alwf/ivAvcNpcX1j0t/uIGCI4dSiKnzQCyf0FTirzQkjrDZUd3meDhNQTruCalGV4gfNWIq3e1oGuwAn1tLlu9oTrE4HzMpgbNEU6wNmsSqpwGxUhYLoSaM7b0dLmqP+ZczSS0Uac0PFNkehGQ2CYIT80f580o4XGtoLCUUGkp6YCTL4Z2CeBEaJABWjDIDH+dKYIUBqUpz4Th12gXAP+h+3qI6+9eppeHrfrzARDsfLjwUNxQJse1QSArjAytf0FKtGHrORc7W0TiCFvR0zaoUNLTKk7enTiRQ9rfWZOAu44fUvPCaXDE6zXXeaVgoKCo4VHlho36erUcjlEBM+jk28IykbZGtBb6igKvYa1tPSgeYm/zJoFVjQcnr14uci/ft1+Na+hOIEoEEiKxcAPk2b2vBKNlRIW7WLJ3u7ZiuQEJTNm6+3cE4+lfwaBCBqBToE+dpzvoUXoMyFFReUFd1O5axu4fXgt00jMaOQxmE0v9OmR/pL/PWIflVF4Zz5yVONYaDVc7l+veY0oEZruEPJ0hlEgxuCzLrcMhjufl2qE2Q7fQIaav/1NqBVkCAwEAAQKCAgAeRZw75Oszoqj0jfMmMILdD3Cfad+dY3FvLESYESeyt0XAX8XoOed6ymQj1qPGxQGGkkBvPEgv1b3jrC8Rhfb3Ct39Z7mRpTar5iHhwwBUboBTUmQ0vR173iAHX8sw2Oa17mCO/CDlr8Fu4Xcom7r3vlVBepo72VSjpPYMjN0MANjwhEi3NCyWzTXBRgUK3TuZbzfzto0w2Irlpx0S7dAqxfk70jXBgwv2vSDWKfg1lL1X0BkMVX98xpMkcjMW2muSqp4KBtTma4GqT6z0f7Y1Bs3lGLZmvPlBXxQVVvkFtiQsENCtSd/h17Gk2mb4EbReaaBzwCYqJdRWtlpJ54kzy8U00co+Yn//ZS7sbbIDkqHPnXkpdIr+0rEDMlOw2Y3vRZCxqZFqfWCW0uzhwKqk2VoYqtDL+ORKG/aG/KTBQ4Y71Uh+7aabPwj5R+NaVMjbqmrVeH70eKjoNVgcNYY1C9rGVF1d+LQEm7UsqS0DPp4wN9QKLAqIfuarAhQBhZy1R7Sj1r5macD9DsGxsurM4mHZV0LNmYLZiFHjTUb6iRSPD5RBFW80vcNtxZ0cxmkLtxrj/DVyExV11Cl0SbZLLa9mScYvxdl/qZutXt3PQyab0NiYxGzCD2RnLkCyxkh1vuHHjhvIWYfbd2VgZB/qGr+o9T07FGfMCu23//fugQKCAQEA9UH38glH/rAjZ431sv6ryUEFY8I2FyLTijtvoj9CNGcQn8vJQAHvUPfMdyqDoum6wgcTmG+UXA6mZzpGQCiY8JW5CoItgXRoYgNzpvVVe2aLf51QGtNLLEFpNDMpCtI+I+COpAmGvWAukku0pZfRjm9eb1ydvTpHlFC9+VhVUsLzw3VtSC5PVW6r65mZcYcB6SFVPap+31ENP/9jOMFoymh57lSMZJMxTEA5b0l2miFb9Rp906Zqiud5zv2jIqF6gL70giW3ovVxR7LGKKTKIa9pxawHwB6Ithygs7YoJkjF2dm8pZTMZKsQN92K70XGj07SmYRLZpkVD7i+cqbbKQKCAQEA9M6580Rcw6W0twfcy0/iB4U5ZS52EcCjW8vHlL+MpUo7YvXadSgV1ZaM28zW/ZGk3wE0zy1YT5s30SQkm0NiWN3t/J0l19ccAOxlPWfjhF7vIQZr7XMo5HeaK0Ak5+68J6bx6KgcXmlJOup7INaE8DyGXB6vd4K6957IXyqs3/bfJAUmz49hnveCfLFdTVVT/Uq4IoPKfQSbSZc0BvPBsnBCF164l4jllGBaWS302dhgW4cgxzG0SZGgNwow4AhB+ygiiS8yvOa7UcHfUObVrzWeeq9mYSQ1PkvUTjkWR2/Y8xy7WP0TRBdJOVSs90H51lerEDGNQWvQvI97S9ZOsQKCAQB59u9lpuXtqwxAQCFyfSFSuQoEHR2nDcOjF4GhbtHum15yCPaw5QVs/33nuPWze4ZLXReKk9p0mTh5V0p+N3IvGlXl+uzEVu5d55eI7LIw5sLymHmwjWjxvimiMtrzLbCHSPHGc5JU9NLUH9/bBY/JxGpy+NzcsHHOOQTwTdRIjviIOAo7fgQn2RyX0k+zXE8/7zqjqvji9zyemdNu8we4uJICSntyvJwkbj/hrufTKEnBrwXpzfVn1EsH+6w32ZPBGLUhT75txJ8r56SRq7l1XPU9vxovmT+lSMFF/Y0j1MbHWnds5H1shoFPNtYTvWBL/gfPHjIc+H23zsiu3XlZAoIBAC2xB/Pnpoi9vOUMiqFH36AXtYa1DURy+AqCFlYlClMvb7YgvQ1w1eJvnwrHSLk7HdKhnwGsLPduuRRH8q0n/osnoOutSQroE0n41UyIv2ZNccRwNmSzQcairBu2dSz02hlsh2otNl5IuGpOqXyPjXBpW4qGD6n2tH7THALnLC0BHtTSQVQsJsRM3gX39LoiWvLDp2qJvplm6rTpi8Rgap6rZSqHe1yNKIxxD2vlr/WY9SMgLXYASO4SSBz9wfGOmQIPk6KXNJkdV4kC7nNjIi75iwLLCgjHgUiHTrDq5sWekpeNnUoWsinbTsdsjnv3zHG9GyiClyLGxMbs4M5eyYECggEBAKuC8ZMpdIrjk6tERYB6g0LnQ7mW8XYbDFAmLYMLs9yfG2jcjVbsW9Kugsr+3poUUv/q+hNO3jfY4HazhZDa0MalgNPoSwr/VNRnkck40x2ovFb989J7yl++zTrnIrax9XRH1V0cNu+Kj7OMwZ2RRfbNv5JBdOZPvkfqyIKFmbQgYbtD66rHuzNOfJpzqr/WVLO57/zzW8245NKG2B6B0oXkei/KqDY0DAbHR3i3EOj1NPtVI1FC/xX8R9BREaid458bqoHJKuInrGcBjaUI9Cvymv8TbstUgD6NPbJR4Sm6vrLeUqzjWZP3t1+Z6DjXmnpR2vvhMU/FWb//21p/88o=\n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " old_user\n" + " $6$xyz$lomVe5tZ2Gz9uSKKywzXuPcHhqjIByhBbqdUTx/jAwUnw7JRp7QHd4ORiEVqxeZg1NEJkHux.mETo9BFPSh1x.\n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + "\n"; + +const char *new_data = + "\n" + " \n" + " 10\n" + " \n" + " new-ssh\n" + " \n" + " \n" + " 127.0.0.1\n" + " 10005\n" + " \n" + " \n" + " \n" + " \n" + " new_key\n" + " \n" + " \n" + " ct:ssh-public-key-format\n" + " AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBDRIB2eNSRWU+HNWRUGKr76ghCLg8RaMlUCps9lBjnc6ggaJl2Q+TOLn8se2wAdK3lYBMz3dcqR+SlU7eB8wJAc=\n" + " ct:ec-private-key-format\n" + " MHcCAQEEICQ2fr9Jt2xluom0YQQ7HseE8YTo5reZRVcQENKUWOrooAoGCCqGSM49AwEHoUQDQgAENEgHZ41JFZT4c1ZFQYqvvqCEIuDxFoyVQKmz2UGOdzqCBomXZD5M4ufyx7bAB0reVgEzPd1ypH5KVTt4HzAkBw==\n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " new_user\n" + " \n" + " \n" + " \n" + " test\n" + " ct:ssh-public-key-format\n" + " AAAAB3NzaC1yc2EAAAADAQABAAABAQDPavVALiM7QwTIUAndO8E9GOkSDQWjuEwkzbJ3kOBPa7kkq71UOZFeecDjFb9eipkljfFys/JYHGQaYVF8/svT0KV5h7HlutRdF6yvqSEbjpbTORb27pdHX3iFEyDCwCIoq9vMeX+wyXnteyn01GpIL0ig0WAnvkqX/SPjuplX5ZItUSr0MhXM7fNSX50BD6G8IO0/djUcdMUcjTjGv73SxB9ZzLvxnhXuUJbzEJJJLj6qajyEIVaJSa73vA33JCD8qzarrsuITojVLPDFmeHwSAoB5dP86yop6e6ypuXzKxxef6yNXcE8oTj8UFYBIXsgIP2nBvWk41EaK0Vk3YFl\n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + "\n"; + +static void * +server_thread(void *arg) +{ + int ret; + NC_MSG_TYPE msgtype; + struct nc_session *session; + struct nc_pollsession *ps; + struct test_state *state = arg; + + ps = nc_ps_new(); + assert_non_null(ps); + + /* accept a session and add it to the poll session structure */ + pthread_barrier_wait(&state->barrier); + msgtype = nc_accept(NC_ACCEPT_TIMEOUT, ctx, &session); + assert_int_equal(msgtype, NC_MSG_HELLO); + + ret = nc_ps_add_session(ps, session); + assert_int_equal(ret, 0); + + do { + ret = nc_ps_poll(ps, NC_PS_POLL_TIMEOUT, NULL); + assert_int_equal(ret & NC_PSPOLL_RPC, NC_PSPOLL_RPC); + } while (!(ret & NC_PSPOLL_SESSION_TERM)); + + nc_ps_clear(ps, 1, NULL); + nc_ps_free(ps); + return NULL; +} + +static void * +client_thread(void *arg) +{ + int ret; + struct nc_session *session = NULL; + struct test_state *state = arg; + + /* skip all hostkey and known_hosts checks */ + nc_client_ssh_set_knownhosts_mode(NC_SSH_KNOWNHOSTS_SKIP); + + /* set directory where to search for modules */ + ret = nc_client_set_schema_searchpath(MODULES_DIR); + assert_int_equal(ret, 0); + + /* set ssh username */ + ret = nc_client_ssh_set_username("new_client"); + assert_int_equal(ret, 0); + + /* add client's key pair */ + ret = nc_client_ssh_add_keypair(TESTS_DIR "/data/key_rsa.pub", TESTS_DIR "/data/key_rsa"); + assert_int_equal(ret, 0); + + /* wait for the server to reach polling */ + pthread_barrier_wait(&state->barrier); + + /* connect */ + session = nc_connect_ssh("127.0.0.1", 10005, NULL); + assert_non_null(session); + + nc_session_free(session, NULL); + return NULL; +} + +static void +nc_test_replace(void **state) +{ + int ret, i; + pthread_t tids[2]; + + assert_non_null(state); + + ret = pthread_create(&tids[0], NULL, client_thread, *state); + assert_int_equal(ret, 0); + ret = pthread_create(&tids[1], NULL, server_thread, *state); + assert_int_equal(ret, 0); + + for (i = 0; i < 2; i++) { + pthread_join(tids[i], NULL); + } +} + +static int +setup_f(void **state) +{ + int ret; + struct lyd_node *old_tree = NULL, *new_tree = NULL; + struct test_state *test_state; + + nc_verbosity(NC_VERB_VERBOSE); + + /* init barrier */ + test_state = malloc(sizeof *test_state); + assert_non_null(test_state); + + ret = pthread_barrier_init(&test_state->barrier, NULL, 2); + assert_int_equal(ret, 0); + + *state = test_state; + + /* create new context */ + ret = ly_ctx_new(MODULES_DIR, 0, &ctx); + assert_int_equal(ret, 0); + + /* load default modules into context */ + ret = nc_server_init_ctx(&ctx); + assert_int_equal(ret, 0); + + /* load ietf-netconf-server module and it's imports into context */ + 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", 10005, &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); + assert_int_equal(ret, 0); + + ret = nc_server_config_add_ssh_user_password(ctx, "old", "old_client", "passwd", &old_tree); + assert_int_equal(ret, 0); + + /* configure the server based on the yang data, treat them as if every node had replace operation */ + 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", 10005, &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); + assert_int_equal(ret, 0); + + ret = nc_server_config_add_ssh_user_pubkey(ctx, "new", "new_client", "pubkey", TESTS_DIR "/data/key_rsa.pub", &new_tree); + assert_int_equal(ret, 0); + + /* configure the server based on the yang data, meaning + * everything configured will be deleted and only the new data applied + */ + ret = nc_server_config_setup_data(new_tree); + assert_int_equal(ret, 0); + + /* initialize the server */ + ret = nc_server_init(); + assert_int_equal(ret, 0); + + /* initialize client */ + ret = nc_client_init(); + assert_int_equal(ret, 0); + + lyd_free_all(old_tree); + lyd_free_all(new_tree); + + return 0; +} + +static int +teardown_f(void **state) +{ + int ret = 0; + struct test_state *test_state; + + assert_non_null(state); + test_state = *state; + + ret = pthread_barrier_destroy(&test_state->barrier); + assert_int_equal(ret, 0); + + free(*state); + nc_client_destroy(); + nc_server_destroy(); + ly_ctx_destroy(ctx); + + return 0; +} + +int +main(void) +{ + const struct CMUnitTest tests[] = { + cmocka_unit_test_setup_teardown(nc_test_replace, setup_f, teardown_f), + }; + + setenv("CMOCKA_TEST_ABORT", "1", 1); + return cmocka_run_group_tests(tests, NULL, NULL); +} diff --git a/tests/test_runtime_changes.c b/tests/test_runtime_changes.c new file mode 100644 index 00000000..53360c6d --- /dev/null +++ b/tests/test_runtime_changes.c @@ -0,0 +1,463 @@ +/** + * @file test_runtime_changes.c + * @author Roman Janota + * @brief libnetconf2 Runtime changes test. + * + * @copyright + * Copyright (c) 2023 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 "tests/config.h" + +#define NC_ACCEPT_TIMEOUT 2000 +#define NC_PS_POLL_TIMEOUT 2000 + +struct test_state { + pthread_barrier_t start_barrier; + pthread_barrier_t end_barrier; + struct lyd_node *tree; +}; + +typedef enum { + NC_TEST_EXPECT_FAIL, + NC_TEST_EXPECT_OK +} NC_TEST_EXPECT; + +typedef enum { + NC_TEST_STATE_END, + NC_TEST_STATE_RUN +} NC_TEST_STATE; + +struct ly_ctx *ctx; +int test_running; +int expect_ok; + +static void * +server_thread(void *arg) +{ + int ret; + NC_MSG_TYPE msgtype; + struct nc_session *session; + struct nc_pollsession *ps; + struct test_state *state = arg; + + ps = nc_ps_new(); + assert_non_null(ps); + + /* just to wait for when new data is configured */ + pthread_barrier_wait(&state->end_barrier); + + while (1) { + /* config ready, wait for client/server to be ready */ + pthread_barrier_wait(&state->start_barrier); + msgtype = nc_accept(NC_ACCEPT_TIMEOUT, ctx, &session); + + if (expect_ok) { + assert_int_equal(msgtype, NC_MSG_HELLO); + ret = nc_ps_add_session(ps, session); + assert_int_equal(ret, 0); + + do { + ret = nc_ps_poll(ps, NC_PS_POLL_TIMEOUT, NULL); + assert_int_equal(ret & NC_PSPOLL_RPC, NC_PSPOLL_RPC); + } while (!(ret & NC_PSPOLL_SESSION_TERM)); + nc_ps_clear(ps, 1, NULL); + } else { + assert_int_equal(msgtype, NC_MSG_ERROR); + } + + if (!test_running) { + break; + } + + /* wait for next config */ + pthread_barrier_wait(&state->end_barrier); + } + + nc_ps_free(ps); + return NULL; +} + +static void * +client_thread_tls(void *arg) +{ + int ret; + struct nc_session *session = NULL; + struct test_state *state = arg; + + ret = nc_client_set_schema_searchpath(MODULES_DIR); + assert_int_equal(ret, 0); + + /* set client cert */ + ret = nc_client_tls_set_cert_key_paths(TESTS_DIR "/data/client.crt", TESTS_DIR "/data/client.key"); + assert_int_equal(ret, 0); + + /* set client ca */ + ret = nc_client_tls_set_trusted_ca_paths(NULL, TESTS_DIR "/data"); + assert_int_equal(ret, 0); + + /* just to wait for when new data is configured */ + pthread_barrier_wait(&state->end_barrier); + + while (1) { + /* config ready, wait for client/server to be ready */ + pthread_barrier_wait(&state->start_barrier); + session = nc_connect_tls("127.0.0.1", 10005, NULL); + if (expect_ok) { + assert_non_null(session); + nc_session_free(session, NULL); + } else { + assert_null(session); + } + + if (!test_running) { + break; + } + + /* wait for next config */ + pthread_barrier_wait(&state->end_barrier); + } + + return NULL; +} + +static void * +client_thread_ssh(void *arg) +{ + int ret; + struct nc_session *session = NULL; + struct test_state *state = arg; + + ret = nc_client_set_schema_searchpath(MODULES_DIR); + assert_int_equal(ret, 0); + + /* skip all hostkey and known_hosts checks */ + nc_client_ssh_set_knownhosts_mode(NC_SSH_KNOWNHOSTS_SKIP); + + /* set ssh username */ + ret = nc_client_ssh_set_username("client"); + assert_int_equal(ret, 0); + + /* add client's key pair */ + ret = nc_client_ssh_add_keypair(TESTS_DIR "/data/id_ed25519.pub", TESTS_DIR "/data/id_ed25519"); + assert_int_equal(ret, 0); + + /* just to wait for when new data is configured */ + pthread_barrier_wait(&state->end_barrier); + + while (1) { + /* config ready, wait for client/server to be ready */ + pthread_barrier_wait(&state->start_barrier); + session = nc_connect_ssh("127.0.0.1", 10006, NULL); + if (expect_ok) { + assert_non_null(session); + nc_session_free(session, NULL); + } else { + assert_null(session); + } + + if (!test_running) { + break; + } + + /* wait for next config */ + pthread_barrier_wait(&state->end_barrier); + } + + return NULL; +} + +static inline void +configure(struct test_state *state, NC_TEST_EXPECT ok_or_fail, NC_TEST_STATE run_or_end) +{ + int ret = 0; + + /* lidl synchronization */ + pthread_barrier_wait(&state->end_barrier); + + /* apply new config */ + ret = nc_server_config_setup_data(state->tree); + assert_int_equal(ret, 0); + + /* set test params */ + expect_ok = ok_or_fail; + test_running = run_or_end; + + /* it just works */ + pthread_barrier_wait(&state->start_barrier); +} + +static void +init_test_create_threads_tls(pthread_t tids[2], void **state) +{ + int ret; + + /* so threads dont quit immediately */ + test_running = NC_TEST_STATE_RUN; + ret = pthread_create(&tids[0], NULL, client_thread_tls, *state); + assert_int_equal(ret, 0); + ret = pthread_create(&tids[1], NULL, server_thread, *state); + assert_int_equal(ret, 0); +} + +static void +init_test_create_threads_ssh(pthread_t tids[2], void **state) +{ + int ret; + + /* so threads dont quit immediately */ + test_running = NC_TEST_STATE_RUN; + ret = pthread_create(&tids[0], NULL, client_thread_ssh, *state); + assert_int_equal(ret, 0); + ret = pthread_create(&tids[1], NULL, server_thread, *state); + assert_int_equal(ret, 0); +} + +static void +test_nc_change_tls_srv_crt(void **state) +{ + int ret, i; + pthread_t tids[2]; + struct test_state *test_state; + + assert_non_null(state); + test_state = *state; + init_test_create_threads_tls(tids, state); + + ret = nc_server_config_add_tls_server_cert(ctx, "endpt_tls", TESTS_DIR "/data/client.key", NULL, TESTS_DIR "/data/client.crt", &test_state->tree); + assert_int_equal(ret, 0); + configure(test_state, NC_TEST_EXPECT_FAIL, NC_TEST_STATE_RUN); + + ret = nc_server_config_add_tls_server_cert(ctx, "endpt_tls", TESTS_DIR "/data/server.key", NULL, TESTS_DIR "/data/server.crt", &test_state->tree); + assert_int_equal(ret, 0); + configure(test_state, NC_TEST_EXPECT_OK, NC_TEST_STATE_END); + + for (i = 0; i < 2; i++) { + pthread_join(tids[i], NULL); + } +} + +static void +test_nc_change_tls_client_crt(void **state) +{ + int ret, i; + pthread_t tids[2]; + struct test_state *test_state; + + assert_non_null(state); + test_state = *state; + init_test_create_threads_tls(tids, state); + + ret = nc_server_config_add_tls_client_cert(ctx, "endpt_tls", "client_cert", TESTS_DIR "/data/server.crt", &test_state->tree); + assert_int_equal(ret, 0); + configure(test_state, NC_TEST_EXPECT_FAIL, NC_TEST_STATE_RUN); + + ret = nc_server_config_add_tls_client_cert(ctx, "endpt_tls", "client_cert", TESTS_DIR "/data/client.crt", &test_state->tree); + assert_int_equal(ret, 0); + configure(test_state, NC_TEST_EXPECT_OK, NC_TEST_STATE_END); + + for (i = 0; i < 2; i++) { + pthread_join(tids[i], NULL); + } +} + +static void +test_nc_change_tls_ctn(void **state) +{ + int ret, i; + pthread_t tids[2]; + struct test_state *test_state; + + assert_non_null(state); + test_state = *state; + init_test_create_threads_tls(tids, state); + + ret = nc_server_config_add_tls_ctn(ctx, "endpt_tls", 1, + "FF:FF:FF:FF:FF:FF:FF:FF:FF:FF:FF:FF:FF:FF:FF:FF:FF:FF:FF:FF:FF:FF:FF:FF:FF:FF:FF:FF:FF:FF:FF:FF:FF", + NC_TLS_CTN_SPECIFIED, "invalid-fingerprint", &test_state->tree); + assert_int_equal(ret, 0); + configure(test_state, NC_TEST_EXPECT_FAIL, NC_TEST_STATE_RUN); + + ret = nc_server_config_add_tls_ctn(ctx, "endpt_tls", 1, + "04:85:6B:75:D1:1A:86:E0:D8:FE:5B:BD:72:F5:73:1D:07:EA:32:BF:09:11:21:6A:6E:23:78:8E:B6:D5:73:C3:2D", + NC_TLS_CTN_SPECIFIED, "client", &test_state->tree); + assert_int_equal(ret, 0); + configure(test_state, NC_TEST_EXPECT_OK, NC_TEST_STATE_END); + + for (i = 0; i < 2; i++) { + pthread_join(tids[i], NULL); + } +} + +static void +test_nc_change_ssh_hostkey(void **state) +{ + int ret, i; + pthread_t tids[2]; + struct test_state *test_state; + + assert_non_null(state); + test_state = *state; + init_test_create_threads_ssh(tids, state); + + ret = nc_server_config_add_ssh_hostkey(ctx, "endpt_ssh", "hostkey", TESTS_DIR "/data/server.key", NULL, &test_state->tree); + 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); + 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); + configure(test_state, NC_TEST_EXPECT_OK, NC_TEST_STATE_END); + + for (i = 0; i < 2; i++) { + pthread_join(tids[i], NULL); + } +} + +static void +test_nc_change_ssh_usr_pubkey(void **state) +{ + int ret, i; + pthread_t tids[2]; + struct test_state *test_state; + + assert_non_null(state); + test_state = *state; + init_test_create_threads_ssh(tids, state); + + ret = nc_server_config_add_ssh_user_pubkey(ctx, "endpt_ssh", "client", "pubkey", TESTS_DIR "/data/id_ecdsa521.pub", &test_state->tree); + assert_int_equal(ret, 0); + configure(test_state, NC_TEST_EXPECT_FAIL, NC_TEST_STATE_RUN); + + ret = nc_server_config_add_ssh_user_pubkey(ctx, "endpt_ssh", "client", "pubkey", TESTS_DIR "/data/id_ed25519.pub", &test_state->tree); + assert_int_equal(ret, 0); + configure(test_state, NC_TEST_EXPECT_OK, NC_TEST_STATE_END); + + for (i = 0; i < 2; i++) { + pthread_join(tids[i], NULL); + } +} + +static int +setup_f(void **state) +{ + int ret; + struct test_state *test_state; + + nc_verbosity(NC_VERB_VERBOSE); + + test_state = malloc(sizeof *test_state); + assert_non_null(test_state); + + /* init barriers */ + ret = pthread_barrier_init(&test_state->start_barrier, NULL, 3); + assert_int_equal(ret, 0); + ret = pthread_barrier_init(&test_state->end_barrier, NULL, 3); + assert_int_equal(ret, 0); + + test_state->tree = NULL; + *state = test_state; + + ret = ly_ctx_new(MODULES_DIR, 0, &ctx); + assert_int_equal(ret, 0); + + ret = nc_server_init_ctx(&ctx); + assert_int_equal(ret, 0); + + ret = nc_server_config_load_modules(&ctx); + 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", 10005, &test_state->tree); + assert_int_equal(ret, 0); + + /* create new server certificate data */ + ret = nc_server_config_add_tls_server_cert(ctx, "endpt_tls", TESTS_DIR "/data/server.key", NULL, TESTS_DIR "/data/server.crt", &test_state->tree); + assert_int_equal(ret, 0); + + /* create new end entity client cert data */ + ret = nc_server_config_add_tls_client_cert(ctx, "endpt_tls", "client_cert", TESTS_DIR "/data/client.crt", &test_state->tree); + assert_int_equal(ret, 0); + + /* create new cert-to-name */ + ret = nc_server_config_add_tls_ctn(ctx, "endpt_tls", 1, + "04:85:6B:75:D1:1A:86:E0:D8:FE:5B:BD:72:F5:73:1D:07:EA:32:BF:09:11:21:6A:6E:23:78:8E:B6:D5:73:C3:2D", + NC_TLS_CTN_SPECIFIED, "client", &test_state->tree); + 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", 10006, &test_state->tree); + assert_int_equal(ret, 0); + + /* create new hostkey data */ + ret = nc_server_config_add_ssh_hostkey(ctx, "endpt_ssh", "hostkey", TESTS_DIR "/data/server.key", NULL, &test_state->tree); + assert_int_equal(ret, 0); + + /* create new ssh user pubkey data */ + ret = nc_server_config_add_ssh_user_pubkey(ctx, "endpt_ssh", "client", "pubkey", TESTS_DIR "/data/id_ed25519.pub", &test_state->tree); + assert_int_equal(ret, 0); + + ret = nc_server_init(); + assert_int_equal(ret, 0); + + /* initialize client */ + ret = nc_client_init(); + assert_int_equal(ret, 0); + + return 0; +} + +static int +teardown_f(void **state) +{ + int ret = 0; + struct test_state *test_state; + + assert_non_null(state); + test_state = *state; + + ret = pthread_barrier_destroy(&test_state->start_barrier); + assert_int_equal(ret, 0); + ret = pthread_barrier_destroy(&test_state->end_barrier); + assert_int_equal(ret, 0); + + lyd_free_all(test_state->tree); + free(*state); + nc_client_destroy(); + nc_server_destroy(); + ly_ctx_destroy(ctx); + + return 0; +} + +int +main(void) +{ + const struct CMUnitTest tests[] = { + cmocka_unit_test_setup_teardown(test_nc_change_tls_srv_crt, setup_f, teardown_f), + cmocka_unit_test_setup_teardown(test_nc_change_tls_client_crt, setup_f, teardown_f), + cmocka_unit_test_setup_teardown(test_nc_change_tls_ctn, setup_f, teardown_f), + cmocka_unit_test_setup_teardown(test_nc_change_ssh_hostkey, setup_f, teardown_f), + cmocka_unit_test_setup_teardown(test_nc_change_ssh_usr_pubkey, setup_f, teardown_f), + }; + + setenv("CMOCKA_TEST_ABORT", "1", 1); + return cmocka_run_group_tests(tests, NULL, NULL); +} diff --git a/tests/test_server_thread.c b/tests/test_server_thread.c index c1f2b575..2af23de7 100644 --- a/tests/test_server_thread.c +++ b/tests/test_server_thread.c @@ -618,7 +618,6 @@ client_fork(void) nc_assert(pipe(pipes + clients * 2) == 0); if (!(pids[clients] = fork())) { - nc_client_init(); ret = nc_client_set_schema_searchpath(TESTS_DIR "/data/modules"); nc_assert(!ret); @@ -640,8 +639,6 @@ client_fork(void) nc_assert(pipe(pipes + clients * 2) == 0); if (!(pids[clients] = fork())) { - nc_client_init(); - ret = nc_client_set_schema_searchpath(TESTS_DIR "/data/modules"); nc_assert(!ret); diff --git a/tests/test_tls.c b/tests/test_tls.c new file mode 100644 index 00000000..542f7bdb --- /dev/null +++ b/tests/test_tls.c @@ -0,0 +1,207 @@ +/** + * @file test_tls.c + * @author Roman Janota + * @brief libnetconf2 TLS authentication test + * + * @copyright + * Copyright (c) 2023 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 "tests/config.h" + +#define NC_ACCEPT_TIMEOUT 2000 +#define NC_PS_POLL_TIMEOUT 2000 + +struct ly_ctx *ctx; + +struct test_state { + pthread_barrier_t barrier; +}; + +static void * +server_thread(void *arg) +{ + int ret; + NC_MSG_TYPE msgtype; + struct nc_session *session; + struct nc_pollsession *ps; + struct test_state *state = arg; + + (void) arg; + + ps = nc_ps_new(); + assert_non_null(ps); + + /* accept a session and add it to the poll session structure */ + pthread_barrier_wait(&state->barrier); + msgtype = nc_accept(NC_ACCEPT_TIMEOUT, ctx, &session); + assert_int_equal(msgtype, NC_MSG_HELLO); + + ret = nc_ps_add_session(ps, session); + assert_int_equal(ret, 0); + + do { + ret = nc_ps_poll(ps, NC_PS_POLL_TIMEOUT, NULL); + assert_int_equal(ret & NC_PSPOLL_RPC, NC_PSPOLL_RPC); + } while (!(ret & NC_PSPOLL_SESSION_TERM)); + + nc_ps_clear(ps, 1, NULL); + nc_ps_free(ps); + return NULL; +} + +static void * +client_thread(void *arg) +{ + int ret; + struct nc_session *session = NULL; + struct test_state *state = arg; + + ret = nc_client_set_schema_searchpath(MODULES_DIR); + assert_int_equal(ret, 0); + + /* set client cert */ + ret = nc_client_tls_set_cert_key_paths(TESTS_DIR "/data/client.crt", TESTS_DIR "/data/client.key"); + assert_int_equal(ret, 0); + + /* set client ca */ + ret = nc_client_tls_set_trusted_ca_paths(NULL, TESTS_DIR "/data"); + assert_int_equal(ret, 0); + + pthread_barrier_wait(&state->barrier); + session = nc_connect_tls("127.0.0.1", 10005, NULL); + assert_non_null(session); + + nc_session_free(session, NULL); + return NULL; +} + +static void +test_nc_tls(void **state) +{ + int ret, i; + pthread_t tids[2]; + + assert_non_null(state); + + ret = pthread_create(&tids[0], NULL, client_thread, *state); + assert_int_equal(ret, 0); + ret = pthread_create(&tids[1], NULL, server_thread, *state); + assert_int_equal(ret, 0); + + for (i = 0; i < 2; i++) { + pthread_join(tids[i], NULL); + } +} + +static int +setup_f(void **state) +{ + int ret; + struct lyd_node *tree = NULL; + struct test_state *test_state; + + nc_verbosity(NC_VERB_VERBOSE); + + /* init barrier */ + test_state = malloc(sizeof *test_state); + assert_non_null(test_state); + + ret = pthread_barrier_init(&test_state->barrier, NULL, 2); + assert_int_equal(ret, 0); + + *state = test_state; + + ret = ly_ctx_new(MODULES_DIR, 0, &ctx); + assert_int_equal(ret, 0); + + ret = nc_server_init_ctx(&ctx); + assert_int_equal(ret, 0); + + ret = nc_server_config_load_modules(&ctx); + 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", 10005, &tree); + assert_int_equal(ret, 0); + + /* create new server certificate data */ + ret = nc_server_config_add_tls_server_cert(ctx, "endpt", TESTS_DIR "/data/server.key", NULL, TESTS_DIR "/data/server.crt", &tree); + assert_int_equal(ret, 0); + + /* create new end entity client cert data */ + ret = nc_server_config_add_tls_client_cert(ctx, "endpt", "client_cert", TESTS_DIR "/data/client.crt", &tree); + assert_int_equal(ret, 0); + + /* create new client ca data */ + ret = nc_server_config_add_tls_ca_cert(ctx, "endpt", "client_ca", TESTS_DIR "/data/serverca.pem", &tree); + assert_int_equal(ret, 0); + + /* create new cert-to-name */ + ret = nc_server_config_add_tls_ctn(ctx, "endpt", 1, + "04:85:6B:75:D1:1A:86:E0:D8:FE:5B:BD:72:F5:73:1D:07:EA:32:BF:09:11:21:6A:6E:23:78:8E:B6:D5:73:C3:2D", + NC_TLS_CTN_SPECIFIED, "client", &tree); + assert_int_equal(ret, 0); + + /* configure the server based on the data */ + ret = nc_server_config_setup_data(tree); + assert_int_equal(ret, 0); + + ret = nc_server_init(); + assert_int_equal(ret, 0); + + /* initialize client */ + ret = nc_client_init(); + assert_int_equal(ret, 0); + + lyd_free_all(tree); + + return 0; +} + +static int +teardown_f(void **state) +{ + int ret = 0; + struct test_state *test_state; + + assert_non_null(state); + test_state = *state; + + ret = pthread_barrier_destroy(&test_state->barrier); + assert_int_equal(ret, 0); + + free(*state); + nc_client_destroy(); + nc_server_destroy(); + ly_ctx_destroy(ctx); + + return 0; +} + +int +main(void) +{ + const struct CMUnitTest tests[] = { + cmocka_unit_test_setup_teardown(test_nc_tls, setup_f, teardown_f), + }; + + setenv("CMOCKA_TEST_ABORT", "1", 1); + return cmocka_run_group_tests(tests, NULL, NULL); +} diff --git a/tests/test_two_channels.c b/tests/test_two_channels.c new file mode 100644 index 00000000..24e7353a --- /dev/null +++ b/tests/test_two_channels.c @@ -0,0 +1,204 @@ +/** + * @file test_two_channels.c + * @author Roman Janota + * @brief libnetconf2 Openning a new session on an established SSH channel test. + * + * @copyright + * Copyright (c) 2023 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 "tests/config.h" + +#define NC_ACCEPT_TIMEOUT 2000 +#define NC_PS_POLL_TIMEOUT 2000 +#define BACKOFF_TIMEOUT_USECS 100 + +struct ly_ctx *ctx; + +static void * +server_thread(void *arg) +{ + int ret, del_session_count = 0, sleep_count = 0; + NC_MSG_TYPE msgtype; + struct nc_session *session, *new_session; + struct nc_pollsession *ps; + + (void) arg; + + ps = nc_ps_new(); + assert_non_null(ps); + + while (del_session_count < 2) { + msgtype = nc_accept(0, ctx, &session); + + if (msgtype == NC_MSG_HELLO) { + ret = nc_ps_add_session(ps, session); + assert_int_equal(ret, 0); + } + + ret = nc_ps_poll(ps, 0, &new_session); + + if (ret & NC_PSPOLL_SESSION_TERM) { + nc_ps_del_session(ps, new_session); + nc_session_free(new_session, NULL); + del_session_count++; + } else if (ret & NC_PSPOLL_SSH_CHANNEL) { + msgtype = nc_session_accept_ssh_channel(session, &new_session); + if (msgtype == NC_MSG_HELLO) { + ret = nc_ps_add_session(ps, new_session); + assert_int_equal(ret, 0); + } + } else if (ret & NC_PS_POLL_TIMEOUT) { + usleep(BACKOFF_TIMEOUT_USECS); + sleep_count++; + assert_int_not_equal(sleep_count, 50000); + } + } + + nc_ps_free(ps); + return NULL; +} + +static void * +client_thread(void *arg) +{ + (void) arg; + int ret; + struct nc_session *session_cl1, *session_cl2; + + /* skip all hostkey and known_hosts checks */ + nc_client_ssh_set_knownhosts_mode(NC_SSH_KNOWNHOSTS_SKIP); + + ret = nc_client_set_schema_searchpath(MODULES_DIR); + assert_int_equal(ret, 0); + + ret = nc_client_ssh_add_keypair(TESTS_DIR "/data/id_ed25519.pub", TESTS_DIR "/data/id_ed25519"); + assert_int_equal(ret, 0); + + ret = nc_client_ssh_set_username("client_1"); + assert_int_equal(ret, 0); + + session_cl1 = nc_connect_ssh("127.0.0.1", 10005, NULL); + assert_non_null(session_cl1); + + ret = nc_client_ssh_set_username("client_2"); + assert_int_equal(ret, 0); + + ret = nc_client_ssh_del_keypair(0); + assert_int_equal(ret, 0); + + ret = nc_client_ssh_add_keypair(TESTS_DIR "/data/id_ecdsa521.pub", TESTS_DIR "/data/id_ecdsa521"); + assert_int_equal(ret, 0); + + session_cl2 = nc_connect_ssh_channel(session_cl1, NULL); + assert_non_null(session_cl2); + + nc_client_destroy(); + nc_session_free(session_cl1, NULL); + nc_session_free(session_cl2, NULL); + return NULL; +} + +static void +test_nc_two_channels(void **state) +{ + int ret, i; + pthread_t tids[2]; + + (void) state; + + ret = pthread_create(&tids[0], NULL, client_thread, NULL); + assert_int_equal(ret, 0); + ret = pthread_create(&tids[1], NULL, server_thread, NULL); + assert_int_equal(ret, 0); + + for (i = 0; i < 2; i++) { + pthread_join(tids[i], NULL); + } +} + +static int +setup_f(void **state) +{ + int ret; + struct lyd_node *tree = NULL; + + (void) state; + + nc_verbosity(NC_VERB_VERBOSE); + + ret = ly_ctx_new(MODULES_DIR, 0, &ctx); + assert_int_equal(ret, 0); + + ret = nc_server_init_ctx(&ctx); + assert_int_equal(ret, 0); + + 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", 10005, &tree); + assert_int_equal(ret, 0); + + 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_ssh_user_pubkey(ctx, "endpt", "client_1", "pubkey", TESTS_DIR "/data/id_ed25519.pub", &tree); + assert_int_equal(ret, 0); + + ret = nc_server_config_add_ssh_user_pubkey(ctx, "endpt", "client_2", "pubkey", TESTS_DIR "/data/id_ecdsa521.pub", &tree); + assert_int_equal(ret, 0); + + ret = nc_server_config_setup_data(tree); + assert_int_equal(ret, 0); + + ret = nc_server_init(); + assert_int_equal(ret, 0); + + /* initialize client */ + ret = nc_client_init(); + assert_int_equal(ret, 0); + + lyd_free_all(tree); + + return 0; +} + +static int +teardown_f(void **state) +{ + (void) state; + + nc_server_destroy(); + ly_ctx_destroy(ctx); + + return 0; +} + +int +main(void) +{ + const struct CMUnitTest tests[] = { + cmocka_unit_test_setup_teardown(test_nc_two_channels, setup_f, teardown_f), + }; + + setenv("CMOCKA_TEST_ABORT", "1", 1); + return cmocka_run_group_tests(tests, NULL, NULL); +} diff --git a/tests/test_unix_socket.c b/tests/test_unix_socket.c new file mode 100644 index 00000000..351a6e23 --- /dev/null +++ b/tests/test_unix_socket.c @@ -0,0 +1,183 @@ +/** + * @file test_unix_socket.c + * @author Roman Janota + * @brief libnetconf2 UNIX socket test + * + * @copyright + * Copyright (c) 2022 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 "tests/config.h" + +#define NC_ACCEPT_TIMEOUT 2000 +#define NC_PS_POLL_TIMEOUT 2000 + +struct ly_ctx *ctx; + +struct test_state { + pthread_barrier_t barrier; +}; + +static void * +server_thread(void *arg) +{ + int ret; + NC_MSG_TYPE msgtype; + struct nc_session *session; + struct nc_pollsession *ps; + struct test_state *state = arg; + + ps = nc_ps_new(); + assert_non_null(ps); + + /* accept a session and add it to the poll session structure */ + pthread_barrier_wait(&state->barrier); + msgtype = nc_accept(NC_ACCEPT_TIMEOUT, ctx, &session); + assert_int_equal(msgtype, NC_MSG_HELLO); + + ret = nc_ps_add_session(ps, session); + assert_int_equal(ret, 0); + + do { + ret = nc_ps_poll(ps, NC_PS_POLL_TIMEOUT, NULL); + assert_int_equal(ret & NC_PSPOLL_RPC, NC_PSPOLL_RPC); + } while (!(ret & NC_PSPOLL_SESSION_TERM)); + + nc_ps_clear(ps, 1, NULL); + nc_ps_free(ps); + return NULL; +} + +static void * +client_thread(void *arg) +{ + int ret = 0; + struct nc_session *session = NULL; + struct test_state *state = arg; + + ret = nc_client_set_schema_searchpath(MODULES_DIR); + assert_int_equal(ret, 0); + + pthread_barrier_wait(&state->barrier); + session = nc_connect_unix("/tmp/nc2_test_unix_sock", NULL); + assert_non_null(session); + + nc_session_free(session, NULL); + return NULL; +} + +static void +test_nc_connect_unix_socket(void **state) +{ + int ret, i; + pthread_t tids[2]; + + assert_non_null(state); + + ret = pthread_create(&tids[0], NULL, client_thread, *state); + assert_int_equal(ret, 0); + ret = pthread_create(&tids[1], NULL, server_thread, *state); + assert_int_equal(ret, 0); + + for (i = 0; i < 2; i++) { + pthread_join(tids[i], NULL); + } +} + +static int +setup_f(void **state) +{ + int ret; + struct lyd_node *tree = NULL; + struct test_state *test_state; + + nc_verbosity(NC_VERB_VERBOSE); + + /* init barrier */ + test_state = malloc(sizeof *test_state); + assert_non_null(test_state); + + ret = pthread_barrier_init(&test_state->barrier, NULL, 2); + assert_int_equal(ret, 0); + + *state = test_state; + + ret = ly_ctx_new(MODULES_DIR, 0, &ctx); + assert_int_equal(ret, 0); + + /* initialize context */ + ret = nc_server_init_ctx(&ctx); + assert_int_equal(ret, 0); + + /* load ietf-netconf-server module and it's dependencies into context */ + ret = nc_server_config_load_modules(&ctx); + assert_int_equal(ret, 0); + + /* create the UNIX socket */ + ret = nc_server_config_add_unix_socket(ctx, "unix", "/tmp/nc2_test_unix_sock", 0700, -1, -1, &tree); + assert_int_equal(ret, 0); + + /* configure the server based on the data */ + ret = nc_server_config_setup_data(tree); + assert_int_equal(ret, 0); + + /* initialize server */ + ret = nc_server_init(); + assert_int_equal(ret, 0); + + /* initialize client */ + ret = nc_client_init(); + assert_int_equal(ret, 0); + + lyd_free_all(tree); + return 0; +} + +static int +teardown_f(void **state) +{ + int ret = 0; + struct test_state *test_state; + + assert_non_null(state); + test_state = *state; + + ret = pthread_barrier_destroy(&test_state->barrier); + assert_int_equal(ret, 0); + + free(*state); + nc_client_destroy(); + nc_server_destroy(); + ly_ctx_destroy(ctx); + + return 0; +} + +int +main(void) +{ + const struct CMUnitTest tests[] = { + cmocka_unit_test_setup_teardown(test_nc_connect_unix_socket, setup_f, teardown_f), + }; + + setenv("CMOCKA_TEST_ABORT", "1", 1); + return cmocka_run_group_tests(tests, NULL, NULL); +}