From 6af28b7e8ea493c1aef9bd51127da55ad9a92e4f Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Tue, 18 Feb 2025 11:33:30 +0100 Subject: [PATCH] Merging upstream version 3.5.5 (Closes: #1098233). Signed-off-by: Daniel Baumann --- .github/workflows/ci.yml | 178 +- .github/workflows/devel-push.yml | 12 +- .gitignore | 2 + .lgtm.yml | 19 - CMakeLists.txt | 250 +- CMakeModules/FindLibPAM.cmake | 86 + CMakeModules/FindMbedTLS.cmake | 110 + CMakeModules/SourceFormat.cmake | 6 +- CMakeModules/UseCompat.cmake | 52 +- Doxyfile.in | 6 +- FAQ.md | 45 +- FindLibNETCONF2.cmake | 21 +- README.md | 144 +- SECURITY.md | 30 + compat/compat.c | 261 +- compat/compat.h.in | 80 +- distro/README.md | 17 + distro/config/apkg.toml | 4 +- distro/pkg/deb/control | 17 +- distro/pkg/deb/libnetconf2-2.install | 1 - distro/pkg/deb/libnetconf2-dev.install | 1 + distro/pkg/deb/libnetconf4.install | 2 + distro/pkg/rpm/libnetconf2.spec | 29 +- distro/scripts/make-archive.sh | 5 +- distro/tests/control | 1 + distro/tests/test-pkg-config.sh | 5 + src/libnetconf.h => doc/libnetconf.doc | 424 +- examples/CMakeLists.txt | 21 + examples/README.md | 134 + examples/admin_key | 7 + examples/admin_key.pub | 1 + examples/client.c | 266 + examples/config.json | 93 + examples/example.h.in | 41 + examples/server.c | 380 ++ libnetconf2.pc.in | 4 +- modules/iana-crypt-hash@2014-04-04.yang | 124 + .../iana-ssh-encryption-algs@2022-06-16.yang | 389 ++ ...iana-ssh-key-exchange-algs@2022-06-16.yang | 2217 ++++++++ modules/iana-ssh-mac-algs@2022-06-16.yang | 162 + .../iana-ssh-public-key-algs@2022-06-16.yang | 436 ++ ...iana-tls-cipher-suite-algs@2022-06-16.yang | 3777 +++++++++++++ modules/ietf-crypto-types@2023-12-28.yang | 1091 ++++ modules/ietf-keystore@2023-12-28.yang | 407 ++ modules/ietf-netconf-server@2023-12-28.yang | 685 +++ modules/ietf-ssh-common@2023-12-28.yang | 261 + modules/ietf-ssh-server@2023-12-28.yang | 425 ++ modules/ietf-tcp-client@2023-12-28.yang | 326 ++ modules/ietf-tcp-common@2023-12-28.yang | 117 + modules/ietf-tcp-server@2023-12-28.yang | 116 + modules/ietf-tls-common@2023-12-28.yang | 316 ++ modules/ietf-tls-server@2023-12-28.yang | 527 ++ modules/ietf-truststore@2023-12-28.yang | 391 ++ .../ietf-x509-cert-to-name@2014-12-10.yang | 314 ++ ...libnetconf2-netconf-server@2024-07-09.yang | 475 ++ nc_client.h.in | 12 +- nc_server.h.in | 13 +- nc_version.h.in | 32 + src/config.h.in | 33 +- src/io.c | 403 +- src/log.c | 113 +- src/log.h | 16 +- src/log_p.h | 69 +- src/messages_client.c | 306 +- src/messages_client.h | 1 + src/messages_p.h | 7 + src/messages_server.c | 190 +- src/messages_server.h | 20 +- src/netconf.h | 3 +- src/server_config.c | 4707 +++++++++++++++++ src/server_config.h | 1442 +++++ src/server_config_ks.c | 445 ++ src/server_config_p.h | 169 + src/server_config_ts.c | 600 +++ src/server_config_util.c | 1366 +++++ src/server_config_util.h | 169 + src/server_config_util_ssh.c | 798 +++ src/server_config_util_tls.c | 790 +++ src/session.c | 1319 ++--- src/session.h | 94 +- src/session_client.c | 1923 ++++--- src/session_client.h | 271 +- src/session_client_ch.h | 100 +- src/session_client_ssh.c | 750 +-- src/session_client_tls.c | 781 +-- src/session_mbedtls.c | 2014 +++++++ src/session_openssl.c | 1494 ++++++ src/session_p.h | 1012 +++- src/session_server.c | 4131 ++++++++------- src/session_server.h | 676 +-- src/session_server_ch.h | 394 +- src/session_server_ssh.c | 2481 +++++---- src/session_server_tls.c | 2409 +++------ src/session_wrapper.h | 742 +++ tests/CMakeLists.txt | 144 +- tests/client/test_client.c | 125 - tests/client/test_client_ssh.c | 813 --- tests/client/test_client_tls.c | 200 - tests/config.h.in | 18 + tests/data/0b527f1f.0 | 1 + tests/data/authorized_keys | 2 + tests/data/crl.pem | 12 + tests/data/ec_server.crt | 53 + tests/data/ec_server.key | 5 + tests/data/ec_serverca.pem | 78 + tests/data/id_ecdsa256 | 9 + tests/data/id_ecdsa256.pub | 1 + tests/data/id_ecdsa384 | 10 + tests/data/id_ecdsa384.pub | 1 + tests/data/id_ecdsa521 | 12 + tests/data/id_ecdsa521.pub | 1 + tests/data/id_ed25519 | 7 + tests/data/id_ed25519.pub | 1 + tests/data/key_dsa | 12 - tests/data/key_dsa.pub | 1 - tests/ld.supp | 10 - tests/library_lsan.supp | 1 + tests/library_valgrind.supp | 27 + tests/ln2_test.c | 182 + tests/ln2_test.h | 80 + tests/pam/pam_netconf.c | 311 ++ tests/test_auth_ssh.c | 354 ++ tests/test_authkeys.c | 268 + tests/test_cert_exp_notif.c | 665 +++ tests/test_ch.c | 400 ++ tests/{client => }/test_client_messages.c | 23 +- tests/test_client_monitoring.c | 201 + tests/test_client_thread.c | 7 +- tests/test_endpt_share_clients.c | 236 + tests/test_fd_comm.c | 66 +- tests/test_init_destroy_client.c | 69 - tests/test_init_destroy_server.c | 75 - tests/test_io.c | 6 +- tests/test_ks_ts.c | 232 + tests/test_pam.c | 149 + tests/test_replace.c | 144 + tests/test_runtime_changes.c | 468 ++ tests/test_server_thread.c | 765 --- tests/test_server_thread.supp | 15 - tests/test_thread_messages.c | 46 +- tests/test_tls.c | 237 + tests/test_two_channels.c | 180 + tests/test_unix_socket.c | 95 + uncrustify.cfg | 607 ++- 144 files changed, 43534 insertions(+), 11497 deletions(-) delete mode 100644 .lgtm.yml create mode 100644 CMakeModules/FindLibPAM.cmake create mode 100644 CMakeModules/FindMbedTLS.cmake create mode 100644 SECURITY.md create mode 100644 distro/README.md delete mode 100644 distro/pkg/deb/libnetconf2-2.install create mode 100644 distro/pkg/deb/libnetconf4.install create mode 100644 distro/tests/control create mode 100755 distro/tests/test-pkg-config.sh rename src/libnetconf.h => doc/libnetconf.doc (54%) create mode 100644 examples/CMakeLists.txt create mode 100644 examples/README.md create mode 100644 examples/admin_key create mode 100644 examples/admin_key.pub create mode 100644 examples/client.c create mode 100644 examples/config.json create mode 100644 examples/example.h.in create mode 100644 examples/server.c create mode 100644 modules/iana-crypt-hash@2014-04-04.yang create mode 100644 modules/iana-ssh-encryption-algs@2022-06-16.yang create mode 100644 modules/iana-ssh-key-exchange-algs@2022-06-16.yang create mode 100644 modules/iana-ssh-mac-algs@2022-06-16.yang create mode 100644 modules/iana-ssh-public-key-algs@2022-06-16.yang create mode 100644 modules/iana-tls-cipher-suite-algs@2022-06-16.yang create mode 100644 modules/ietf-crypto-types@2023-12-28.yang create mode 100644 modules/ietf-keystore@2023-12-28.yang create mode 100644 modules/ietf-netconf-server@2023-12-28.yang create mode 100644 modules/ietf-ssh-common@2023-12-28.yang create mode 100644 modules/ietf-ssh-server@2023-12-28.yang create mode 100644 modules/ietf-tcp-client@2023-12-28.yang create mode 100644 modules/ietf-tcp-common@2023-12-28.yang create mode 100644 modules/ietf-tcp-server@2023-12-28.yang create mode 100644 modules/ietf-tls-common@2023-12-28.yang create mode 100644 modules/ietf-tls-server@2023-12-28.yang create mode 100644 modules/ietf-truststore@2023-12-28.yang create mode 100644 modules/ietf-x509-cert-to-name@2014-12-10.yang create mode 100644 modules/libnetconf2-netconf-server@2024-07-09.yang create mode 100644 nc_version.h.in create mode 100644 src/server_config.c create mode 100644 src/server_config.h create mode 100644 src/server_config_ks.c create mode 100644 src/server_config_p.h create mode 100644 src/server_config_ts.c create mode 100644 src/server_config_util.c create mode 100644 src/server_config_util.h create mode 100644 src/server_config_util_ssh.c create mode 100644 src/server_config_util_tls.c create mode 100644 src/session_mbedtls.c create mode 100644 src/session_openssl.c create mode 100644 src/session_wrapper.h delete mode 100644 tests/client/test_client.c delete mode 100644 tests/client/test_client_ssh.c delete mode 100644 tests/client/test_client_tls.c create mode 120000 tests/data/0b527f1f.0 create mode 100644 tests/data/authorized_keys create mode 100644 tests/data/crl.pem create mode 100644 tests/data/ec_server.crt create mode 100644 tests/data/ec_server.key create mode 100644 tests/data/ec_serverca.pem create mode 100644 tests/data/id_ecdsa256 create mode 100644 tests/data/id_ecdsa256.pub create mode 100644 tests/data/id_ecdsa384 create mode 100644 tests/data/id_ecdsa384.pub create mode 100644 tests/data/id_ecdsa521 create mode 100644 tests/data/id_ecdsa521.pub create mode 100644 tests/data/id_ed25519 create mode 100644 tests/data/id_ed25519.pub delete mode 100644 tests/data/key_dsa delete mode 100644 tests/data/key_dsa.pub delete mode 100644 tests/ld.supp create mode 100644 tests/library_lsan.supp create mode 100644 tests/library_valgrind.supp create mode 100644 tests/ln2_test.c create mode 100644 tests/ln2_test.h create mode 100644 tests/pam/pam_netconf.c create mode 100644 tests/test_auth_ssh.c create mode 100644 tests/test_authkeys.c create mode 100644 tests/test_cert_exp_notif.c create mode 100644 tests/test_ch.c rename tests/{client => }/test_client_messages.c (98%) create mode 100644 tests/test_client_monitoring.c create mode 100644 tests/test_endpt_share_clients.c delete mode 100644 tests/test_init_destroy_client.c delete mode 100644 tests/test_init_destroy_server.c create mode 100644 tests/test_ks_ts.c create mode 100644 tests/test_pam.c create mode 100644 tests/test_replace.c create mode 100644 tests/test_runtime_changes.c delete mode 100644 tests/test_server_thread.c delete mode 100644 tests/test_server_thread.supp create mode 100644 tests/test_tls.c create mode 100644 tests/test_two_channels.c create mode 100644 tests/test_unix_socket.c diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b8bbc67..fc36cb7 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -10,12 +10,12 @@ on: - devel env: - DEFAULT_PACKAGES: libcmocka-dev zlib1g-dev libssh-dev libssl-dev + DEFAULT_PACKAGES: libcmocka-dev zlib1g-dev libssh-dev libssl-dev libpam0g-dev libcurl4-openssl-dev jobs: git-branch: name: Get git branch - runs-on: ubuntu-18.04 + runs-on: ubuntu-latest outputs: branch-name: ${{ steps.get-git-branch.outputs.branch-name }} steps: @@ -26,7 +26,7 @@ jobs: else export GIT_BRANCH=${{ github.base_ref }} fi - echo "::set-output name=branch-name::$GIT_BRANCH" + echo "branch-name=$GIT_BRANCH" >> $GITHUB_OUTPUT build: name: ${{ matrix.config.name }} @@ -37,96 +37,118 @@ jobs: matrix: config: - { - name: "Release, Ubuntu 18.04, gcc", - os: "ubuntu-18.04", + name: "Release, gcc, OpenSSL", + os: "ubuntu-22.04", build-type: "Release", dep-build-type: "Release", cc: "gcc", options: "-DENABLE_TESTS=ON -DENABLE_DNSSEC=ON", + tls-lib: "OpenSSL", packages: "", snaps: "", make-prepend: "", make-target: "" } - { - name: "Release, Ubuntu 18.04, clang", - os: "ubuntu-18.04", + name: "Release, gcc, MbedTLS", + os: "ubuntu-22.04", build-type: "Release", dep-build-type: "Release", - cc: "clang", + cc: "gcc", options: "-DENABLE_TESTS=ON -DENABLE_DNSSEC=ON", + tls-lib: "MbedTLS", packages: "", snaps: "", make-prepend: "", make-target: "" } - { - name: "Debug, Ubuntu 18.04, gcc", - os: "ubuntu-18.04", + name: "Release, clang", + os: "ubuntu-22.04", + build-type: "Release", + dep-build-type: "Release", + cc: "clang", + options: "-DENABLE_TESTS=ON -DENABLE_DNSSEC=ON", + tls-lib: "OpenSSL", + packages: "", + snaps: "", + make-prepend: "", + make-target: "" + } + - { + name: "Debug, gcc, OpenSSL", + os: "ubuntu-22.04", build-type: "Debug", dep-build-type: "Release", cc: "gcc", options: "-DENABLE_DNSSEC=ON", + tls-lib: "OpenSSL", packages: "valgrind", snaps: "", make-prepend: "", make-target: "" } - { - name: "Debug, Ubuntu 18.04, clang", - os: "ubuntu-18.04", + name: "Debug, gcc, MbedTLS", + os: "ubuntu-22.04", + build-type: "Debug", + dep-build-type: "Release", + cc: "gcc", + options: "-DENABLE_DNSSEC=ON", + tls-lib: "MbedTLS", + packages: "valgrind", + snaps: "", + make-prepend: "", + make-target: "" + } + - { + name: "Debug, clang", + os: "ubuntu-22.04", build-type: "Debug", dep-build-type: "Release", cc: "clang", options: "-DENABLE_DNSSEC=ON", - packages: "valgrind", - snaps: "", - make-prepend: "", - make-target: "" - } - - { - name: "SSH Only", - os: "ubuntu-18.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-18.04", - build-type: "Debug", - dep-build-type: "Release", - cc: "gcc", - options: "-DENABLE_TLS=ON -DENABLE_SSH=OFF", - packages: "valgrind", + tls-lib: "OpenSSL", + # no valgrind because it does not support DWARF5 yet generated by clang 14 + packages: "", snaps: "", make-prepend: "", make-target: "" } - { name: "No SSH nor TLS", - os: "ubuntu-18.04", + 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", + tls-lib: "", packages: "valgrind", snaps: "", make-prepend: "", make-target: "" } - { - name: "ASAN and UBSAN", - os: "ubuntu-18.04", + name: "ASAN and UBSAN, OpenSSL", + os: "ubuntu-22.04", build-type: "Debug", dep-build-type: "Release", cc: "clang", options: "-DCMAKE_C_FLAGS=-fsanitize=address,undefined -DENABLE_VALGRIND_TESTS=OFF", + tls-lib: "OpenSSL", + packages: "", + snaps: "", + make-prepend: "", + make-target: "" + } + - { + name: "ASAN and UBSAN, MbedTLS", + os: "ubuntu-22.04", + build-type: "Debug", + dep-build-type: "Release", + cc: "clang", + options: "-DCMAKE_C_FLAGS=-fsanitize=address,undefined -DENABLE_VALGRIND_TESTS=OFF", + tls-lib: "MbedTLS", packages: "", snaps: "", make-prepend: "", @@ -134,53 +156,85 @@ jobs: } - { name: "ABI Check", - os: "ubuntu-latest", + os: "ubuntu-22.04", build-type: "ABICheck", dep-build-type: "Debug", cc: "gcc", options: "", + tls-lib: "OpenSSL", packages: "abi-dumper abi-compliance-checker snap", snaps: "core universal-ctags", make-prepend: "", make-target: "abi-check" } + - { + name: "DEB Package", + os: "ubuntu-22.04", + build-type: "Release", + dep-build-type: "Release", + cc: "gcc", + options: "", + tls-lib: "OpenSSL", + packages: "cmake debhelper valgrind python3-pip", + snaps: "", + make-prepend: "", + make-target: "" + } steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@main + with: + fetch-depth: 100 - name: Deps-packages shell: bash run: | - sudo add-apt-repository ppa:kedazo/libssh-0.7.x -y sudo apt-get update sudo apt-get install $DEFAULT_PACKAGES ${{ matrix.config.packages }} if ${{ matrix.config.snaps != '' }} then sudo snap refresh; sudo snap install ${{ matrix.config.snaps }} fi + if ${{ matrix.config.name == 'DEB Package' }}; then + pip install apkg + apkg system-setup + fi - name: Deps-uncrustify shell: bash working-directory: ${{ github.workspace }} run: | - git clone --branch uncrustify-0.71.0 https://github.com/uncrustify/uncrustify + git clone --branch uncrustify-0.77.1 https://github.com/uncrustify/uncrustify cd uncrustify mkdir build cd build CC=${{ matrix.config.cc }} cmake .. make sudo make install - if: ${{ matrix.config.name == 'Debug, Ubuntu 18.04, gcc' }} + if: ${{ matrix.config.name == 'Debug, gcc, OpenSSL' || matrix.config.name == 'Debug, gcc, MbedTLS' }} - name: Deps-libyang shell: bash run: | - git clone -b ${{needs.git-branch.outputs.branch-name}} https://github.com/CESNET/libyang.git + git clone -b ${{ needs.git-branch.outputs.branch-name }} https://github.com/CESNET/libyang.git cd libyang - mkdir build - cd build - CC=${{ matrix.config.cc }} cmake -DCMAKE_BUILD_TYPE=${{ matrix.config.dep-build-type }} -DENABLE_BUILD_TESTS=OFF .. - make -j2 - sudo make install + if ${{ matrix.config.name == 'DEB Package' }}; then + apkg build + apkg install + else + mkdir build + cd build + CC=${{ matrix.config.cc }} cmake -DCMAKE_BUILD_TYPE=${{ matrix.config.dep-build-type }} -DENABLE_TESTS=OFF .. + make -j2 + sudo make install + fi + + - name: Build-and-install-package + shell: bash + working-directory: ${{ github.workspace }} + run: | + apkg build + apkg install + if: ${{ matrix.config.name == 'DEB Package' }} - name: Deps-libval shell: bash @@ -190,6 +244,19 @@ jobs: ./configure make -j2 sudo make install + if: ${{ matrix.config.name != 'DEB Package' }} + + - name: Deps-MbedTLS + shell: bash + run: | + git clone -b mbedtls-3.5.2 https://github.com/Mbed-TLS/mbedtls.git + cd mbedtls + mkdir build + cd build + CC=${{ matrix.config.cc }} cmake -DUSE_SHARED_MBEDTLS_LIBRARY=On -DENABLE_TESTING=Off .. + make -j2 + sudo make install + if: ${{ matrix.config.tls-lib == 'MbedTLS' }} - name: Configure shell: bash @@ -198,6 +265,7 @@ jobs: mkdir build cd build CC=${{ matrix.config.cc }} cmake -DCMAKE_BUILD_TYPE=${{ matrix.config.build-type }} ${{ matrix.config.options }} .. + if: ${{ matrix.config.name != 'DEB Package' }} - name: Build shell: bash @@ -206,8 +274,12 @@ jobs: export LC_ALL=C.UTF-8 export PATH=/snap/bin:${{ github.workspace }}/coverity-tools/bin:$PATH ${{ matrix.config.make-prepend }} make ${{ matrix.config.make-target }} + if: ${{ matrix.config.name != 'DEB Package' }} - name: Test shell: bash working-directory: ${{ github.workspace }}/build - run: ctest --output-on-failure + run: | + export LSAN_OPTIONS=suppressions=${{ github.workspace }}/tests/library_lsan.supp + ctest -j4 --output-on-failure + if: ${{ matrix.config.name != 'DEB Package' }} diff --git a/.github/workflows/devel-push.yml b/.github/workflows/devel-push.yml index 49918a8..0946af3 100644 --- a/.github/workflows/devel-push.yml +++ b/.github/workflows/devel-push.yml @@ -5,13 +5,13 @@ on: - devel env: - DEFAULT_PACKAGES: libcmocka-dev zlib1g-dev libssh-dev libssl-dev + DEFAULT_PACKAGES: libcmocka-dev zlib1g-dev libssh-dev libssl-dev libpam0g-dev libcurl4-openssl-dev COVERITY_PROJECT: CESNET%2Flibnetconf2 jobs: git-branch: name: Get git branch - runs-on: ubuntu-18.04 + runs-on: ubuntu-latest outputs: branch-name: ${{ steps.get-git-branch.outputs.branch-name }} steps: @@ -22,7 +22,7 @@ jobs: else export GIT_BRANCH=${{ github.base_ref }} fi - echo "::set-output name=branch-name::$GIT_BRANCH" + echo "branch-name=$GIT_BRANCH" >> $GITHUB_OUTPUT build: name: ${{ matrix.config.name }} @@ -58,7 +58,7 @@ jobs: } steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@main - name: Deps-packages shell: bash @@ -84,11 +84,11 @@ jobs: - name: Deps-libyang shell: bash run: | - git clone -b ${{needs.git-branch.outputs.branch-name}} https://github.com/CESNET/libyang.git + git clone -b ${{ needs.git-branch.outputs.branch-name }} https://github.com/CESNET/libyang.git cd libyang mkdir build cd build - CC=${{ matrix.config.cc }} cmake -DCMAKE_BUILD_TYPE=${{ matrix.config.dep-build-type }} -DENABLE_BUILD_TESTS=OFF .. + CC=${{ matrix.config.cc }} cmake -DCMAKE_BUILD_TYPE=${{ matrix.config.dep-build-type }} -DENABLE_TESTS=OFF .. make -j2 sudo make install diff --git a/.gitignore b/.gitignore index fc5bd90..117119f 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,3 @@ /pkg +/build +/doc/html diff --git a/.lgtm.yml b/.lgtm.yml deleted file mode 100644 index 2794096..0000000 --- a/.lgtm.yml +++ /dev/null @@ -1,19 +0,0 @@ -extraction: - cpp: - prepare: - packages: libpcre2-dev - after_prepare: - - cd $LGTM_WORKSPACE - - git clone -b devel https://github.com/CESNET/libyang.git - - cd libyang; mkdir build; cd build - - cmake -DCMAKE_INSTALL_PREFIX=$LGTM_WORKSPACE -DENABLE_BUILD_TESTS=OFF .. - - make -j2 - - make install - configure: - command: - - mkdir build; cd build - - cmake -DCMAKE_INCLUDE_PATH=$LGTM_WORKSPACE/include -DCMAKE_LIBRARY_PATH=$LGTM_WORKSPACE/lib .. - index: - build_command: - - cd build - - make -j2 diff --git a/CMakeLists.txt b/CMakeLists.txt index c659ab4..ea28902 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,9 +1,9 @@ -cmake_minimum_required(VERSION 2.8.12) +cmake_minimum_required(VERSION 3.5...3.28.1) project(libnetconf2 C) # include custom Modules -list(APPEND CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/CMakeModules/") +list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/CMakeModules/") include(GNUInstallDirs) include(CheckFunctionExists) @@ -19,16 +19,6 @@ if(POLICY CMP0075) cmake_policy(SET CMP0075 NEW) endif() -set(LIBNETCONF2_DESCRIPTION "NETCONF server and client library in C.") - -# check the supported platform -if(NOT UNIX) - message(FATAL_ERROR "Only *nix like systems are supported.") -endif() - -# osx specific -set(CMAKE_MACOSX_RPATH TRUE) - # set default build type if not specified by user if(NOT CMAKE_BUILD_TYPE) set(CMAKE_BUILD_TYPE Debug) @@ -53,28 +43,40 @@ elseif("${BUILD_TYPE_UPPER}" STREQUAL "DOCONLY") set(CMAKE_BUILD_TYPE "DocOnly" CACHE STRING "Build Type" FORCE) endif() +# +# variables +# + +set(LIBNETCONF2_DESCRIPTION "NETCONF server and client library in C.") + +# osx specific +set(CMAKE_MACOSX_RPATH TRUE) + # Version of the project # 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 0) -set(LIBNETCONF2_MICRO_VERSION 24) +set(LIBNETCONF2_MAJOR_VERSION 3) +set(LIBNETCONF2_MINOR_VERSION 5) +set(LIBNETCONF2_MICRO_VERSION 5) 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 +# Major version is changed with every backward non-compatible API/ABI change in the library, minor version changes # with backward compatible change and micro version is connected with any internal change of the library. -set(LIBNETCONF2_MAJOR_SOVERSION 2) -set(LIBNETCONF2_MINOR_SOVERSION 1) -set(LIBNETCONF2_MICRO_SOVERSION 20) +set(LIBNETCONF2_MAJOR_SOVERSION 4) +set(LIBNETCONF2_MINOR_SOVERSION 4) +set(LIBNETCONF2_MICRO_SOVERSION 5) set(LIBNETCONF2_SOVERSION_FULL ${LIBNETCONF2_MAJOR_SOVERSION}.${LIBNETCONF2_MINOR_SOVERSION}.${LIBNETCONF2_MICRO_SOVERSION}) set(LIBNETCONF2_SOVERSION ${LIBNETCONF2_MAJOR_SOVERSION}) -# libyang SO version required -set(LIBYANG_DEP_SOVERSION_MAJOR 2) +# Version of libyang library that this project depends on +set(LIBYANG_DEP_VERSION 2.0.0) +set(LIBYANG_DEP_SOVERSION 3.0.0) +set(LIBYANG_DEP_SOVERSION_MAJOR 3) -set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Wextra -fvisibility=hidden -std=gnu99") +# global C flags +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Wextra -fvisibility=hidden -std=c99") # # options @@ -86,15 +88,18 @@ else() option(ENABLE_TESTS "Build tests" OFF) option(ENABLE_VALGRIND_TESTS "Build tests with valgrind" OFF) 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) +option(ENABLE_COMMON_TARGETS "Define common custom target names such as 'doc' or 'uninstall', may cause conflicts when using add_subdirectory() to build this project" ON) +option(BUILD_SHARED_LIBS "By default, shared libs are enabled. Turn off for a static build." ON) 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") set(MAX_PSPOLL_THREAD_COUNT 6 CACHE STRING "Maximum number of threads that could simultaneously access a ps_poll structure") set(TIMEOUT_STEP 100 CACHE STRING "Number of microseconds tasks are repeated until timeout elapses") -set(YANG_MODULE_DIR "${CMAKE_INSTALL_PREFIX}/share/yang/modules" CACHE STRING "Directory with common YANG modules") +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") # # sources @@ -106,20 +111,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 @@ -131,11 +137,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 @@ -144,21 +151,23 @@ 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 compat/*.c compat/*.h* + examples/*.c + examples/*.h* src/*.c src/*.h - tests/*.c - tests/client/*.c) + tests/*.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() @@ -186,7 +195,7 @@ if(ENABLE_COVERAGE) endif() if ("${BUILD_TYPE_UPPER}" STREQUAL "DEBUG") - source_format_enable() + source_format_enable(0.77) endif() if("${BUILD_TYPE_UPPER}" STREQUAL "DOCONLY") @@ -201,12 +210,15 @@ endif() # use compat use_compat() -# netconf2 target -add_library(netconf2 SHARED ${libsrc} ${compatsrc}) +# netconf2 sourceless target - need it for linking libs, but the required sources will be added later +add_library(netconf2) + +# set the shared library version set_target_properties(netconf2 PROPERTIES VERSION ${LIBNETCONF2_SOVERSION_FULL} SOVERSION ${LIBNETCONF2_SOVERSION}) # include repository files with highest priority include_directories(${PROJECT_BINARY_DIR}/src) +include_directories(${PROJECT_BINARY_DIR}/include) # dependencies - pthread set(CMAKE_THREAD_PREFER_PTHREAD TRUE) @@ -217,41 +229,75 @@ 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() - - 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") +# header file compatibility +check_include_file("shadow.h" HAVE_SHADOW) +check_include_file("termios.h" HAVE_TERMIOS) + +if(ENABLE_SSH_TLS) + # dependencies - mbedTLS (higher preference) or OpenSSL + find_package(MbedTLS 3.5.0) + if (MBEDTLS_FOUND) + # dependencies - mbedtls + set(HAVE_MBEDTLS TRUE) + list(APPEND libsrc src/session_mbedtls.c) + include_directories(${MBEDTLS_INCLUDE_DIRS}) + target_link_libraries(netconf2 ${MBEDTLS_LIBRARIES}) + list(APPEND CMAKE_REQUIRED_LIBRARIES ${MBEDTLS_LIBRARIES}) + else() + # dependencies - openssl + find_package(OpenSSL 3.0.0 REQUIRED) + list(APPEND libsrc src/session_openssl.c) + include_directories(${OPENSSL_INCLUDE_DIR}) + target_link_libraries(netconf2 ${OPENSSL_LIBRARIES}) + list(APPEND CMAKE_REQUIRED_LIBRARIES ${OPENSSL_LIBRARIES}) endif() + # dependencies - libssh + find_package(LibSSH 0.9.5 REQUIRED) 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") - # crypt - if(NOT ${CMAKE_SYSTEM_NAME} MATCHES "QNX") - target_link_libraries(netconf2 -lcrypt) - list(APPEND CMAKE_REQUIRED_LIBRARIES crypt) + # dependencies - libcurl + find_package(CURL 7.30.0 REQUIRED) + if(TARGET CURL::libcurl) + target_link_libraries(netconf2 CURL::libcurl) else() - target_link_libraries(netconf2 -llogin) - list(APPEND CMAKE_REQUIRED_LIBRARIES login) + target_link_libraries(netconf2 ${CURL_LIBRARIES}) + list(APPEND CMAKE_REQUIRED_LIBRARIES ${CURL_LIBRARY}) + include_directories(${CURL_INCLUDE_DIRS}) endif() + + # crypt (if not found, assume no library needs to be linked) + if(${CMAKE_SYSTEM_NAME} MATCHES "QNX") + set(LIBCRYPT login) + else() + set(LIBCRYPT crypt) + endif() + check_library_exists(${LIBCRYPT} crypt "" HAVE_CRYPT) + if(HAVE_CRYPT) + target_link_libraries(netconf2 ${LIBCRYPT}) + list(APPEND CMAKE_REQUIRED_LIBRARIES ${LIBCRYPT}) + endif() + + # libpam + find_package(LibPAM) + if(LibPAM_FOUND) + set(HAVE_LIBPAM TRUE) + + target_link_libraries(netconf2 ${LIBPAM_LIBRARIES}) + list(APPEND CMAKE_REQUIRED_LIBRARIES ${LIBPAM_LIBRARIES}) + include_directories(${LIBPAM_INCLUDE_DIRS}) + + message(STATUS "SSH Keyboard Interactive system method: Linux PAM") + elseif(HAVE_SHADOW) + message(STATUS "SSH Keyboard Interactive system method: local users") + else() + message(WARNING "SSH Keyboard Interactive system method: disabled") + endif() + + # set compiler flag + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DNC_ENABLED_SSH_TLS") endif() # dependencies - libval @@ -263,14 +309,10 @@ if(ENABLE_DNSSEC) endif() # dependencies - libyang -find_package(LibYANG ${LIBYANG_DEP_SOVERSION_MAJOR} REQUIRED) +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) @@ -281,24 +323,35 @@ if(${CMAKE_SYSTEM_NAME} MATCHES "QNX") list(REMOVE_ITEM CMAKE_REQUIRED_DEFINITIONS -D_QNX_SOURCE) endif() -# generate files +# set sources +target_sources(netconf2 PRIVATE ${libsrc} ${compatsrc}) + +# generate config file configure_file("${PROJECT_SOURCE_DIR}/src/config.h.in" "${PROJECT_BINARY_DIR}/src/config.h" ESCAPE_QUOTES @ONLY) -configure_file(nc_client.h.in nc_client.h) -configure_file(nc_server.h.in nc_server.h) + +# generate and copy public header files +configure_file("${PROJECT_SOURCE_DIR}/nc_client.h.in" "${PROJECT_BINARY_DIR}/include/nc_client.h") +configure_file("${PROJECT_SOURCE_DIR}/nc_server.h.in" "${PROJECT_BINARY_DIR}/include/nc_server.h") +configure_file("${PROJECT_SOURCE_DIR}/nc_version.h.in" "${PROJECT_BINARY_DIR}/include/nc_version.h") +file(COPY ${headers} DESTINATION "${PROJECT_BINARY_DIR}/include/libnetconf2") + +# install YANG modules +install(DIRECTORY "${PROJECT_SOURCE_DIR}/modules/" DESTINATION ${YANG_MODULE_DIR} FILES_MATCHING PATTERN "*.yang") # install library install(TARGETS netconf2 DESTINATION ${CMAKE_INSTALL_LIBDIR}) # install headers -install(FILES ${CMAKE_CURRENT_BINARY_DIR}/nc_client.h DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) -install(FILES ${CMAKE_CURRENT_BINARY_DIR}/nc_server.h DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) -install(FILES ${headers} ${PROJECT_BINARY_DIR}/src/config.h DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/libnetconf2) +install(FILES ${PROJECT_BINARY_DIR}/include/nc_client.h DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) +install(FILES ${PROJECT_BINARY_DIR}/include/nc_server.h DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) +install(FILES ${PROJECT_BINARY_DIR}/include/nc_version.h DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) +install(DIRECTORY ${PROJECT_BINARY_DIR}/include/libnetconf2 DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) # install pkg-config file find_package(PkgConfig) if(PKG_CONFIG_FOUND) configure_file("libnetconf2.pc.in" "libnetconf2.pc" @ONLY) - install(FILES "${CMAKE_CURRENT_BINARY_DIR}/libnetconf2.pc" DESTINATION "${CMAKE_INSTALL_LIBDIR}/pkgconfig") + install(FILES "${PROJECT_BINARY_DIR}/libnetconf2.pc" DESTINATION "${CMAKE_INSTALL_LIBDIR}/pkgconfig") # check that pkg-config includes the used path execute_process(COMMAND ${PKG_CONFIG_EXECUTABLE} --variable pc_path pkg-config RESULT_VARIABLE RETURN OUTPUT_VARIABLE PC_PATH ERROR_QUIET) if(RETURN EQUAL 0) @@ -310,6 +363,15 @@ if(PKG_CONFIG_FOUND) endif() endif() +# examples +if(ENABLE_EXAMPLES) + if(NOT ENABLE_SSH_TLS) + message(WARNING "Examples will not be compiled because SSH and TLS are disabled.") + else() + add_subdirectory(examples) + endif() +endif() + # tests if(ENABLE_TESTS) enable_testing() @@ -320,22 +382,28 @@ endif() gen_coverage("test_.*" "test_.*_valgrind") # generate doxygen documentation for libnetconf2 API -gen_doc("${doxy_files}" ${LIBNETCONF2_VERSION} ${LIBNETCONF2_DESCRIPTION} "") +if(ENABLE_COMMON_TARGETS) + gen_doc("${doxy_files}" ${LIBNETCONF2_VERSION} ${LIBNETCONF2_DESCRIPTION} "") +endif() # generate API/ABI report if ("${BUILD_TYPE_UPPER}" STREQUAL "ABICHECK") - lib_abi_check(netconf2 "${headers}" ${LIBNETCONF2_SOVERSION_FULL} e7402149e5b36de7acab2e38970a3a9d6a8165d5) + lib_abi_check(netconf2 "${headers}" ${LIBNETCONF2_SOVERSION_FULL} 15fbc59efa5e6f1f7bea19ab561d03f8852caabb) endif() # source files to be covered by the 'format' target and a test with 'format-check' target source_format(${format_sources}) # clean cmake cache -add_custom_target(cleancache - COMMAND make clean - COMMAND find . -iname '*cmake*' -not -name CMakeLists.txt -exec rm -rf {} + - COMMAND rm -rf Makefile Doxyfile - WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}) +if(ENABLE_COMMON_TARGETS) + add_custom_target(cleancache + COMMAND make clean + COMMAND find . -iname '*cmake*' -not -name CMakeLists.txt -exec rm -rf {} + + COMMAND rm -rf Makefile Doxyfile + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}) +endif() # uninstall -add_custom_target(uninstall "${CMAKE_COMMAND}" -P "${CMAKE_MODULE_PATH}/uninstall.cmake") +if(ENABLE_COMMON_TARGETS) + add_custom_target(uninstall "${CMAKE_COMMAND}" -P "${CMAKE_MODULE_PATH}/uninstall.cmake") +endif() diff --git a/CMakeModules/FindLibPAM.cmake b/CMakeModules/FindLibPAM.cmake new file mode 100644 index 0000000..acf0475 --- /dev/null +++ b/CMakeModules/FindLibPAM.cmake @@ -0,0 +1,86 @@ +# - Try to find LibPAM +# Once done this will define +# +# LIBPAM_FOUND - system has LibPAM +# LIBPAM_INCLUDE_DIRS - the LibPAM include directory +# LIBPAM_LIBRARIES - link these to use LibPAM +# +# Author Roman Janota +# Copyright (c) 2022 CESNET, z.s.p.o. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# 1. Redistributions of source code must retain the copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. The name of the author may not be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# + +if(LIBPAM_LIBRARIES AND LIBPAM_INCLUDE_DIRS) + # in cache already + set(LIBPAM_FOUND TRUE) +else() + + find_path(LIBPAM_INCLUDE_DIR + NAMES + security/pam_appl.h + security/pam_modules.h + PATHS + /opt/local/include + /sw/include + ${CMAKE_INCLUDE_PATH} + ${CMAKE_INSTALL_PREFIX}/include + ) + + find_library(LIBPAM_LIBRARY + NAMES + pam + PATHS + /usr/lib + /usr/lib64 + /opt/local/lib + /sw/lib + ${CMAKE_LIBRARY_PATH} + ${CMAKE_INSTALL_PREFIX}/lib + ) + + if(LIBPAM_INCLUDE_DIR AND LIBPAM_LIBRARY) + set(LIBPAM_FOUND TRUE) + + # check if the function pam_start_confdir is in pam_appl.h header (added in PAM 1.4) + file(STRINGS ${LIBPAM_INCLUDE_DIR}/security/pam_appl.h PAM_CONFDIR REGEX "pam_start_confdir") + if ("${PAM_CONFDIR}" STREQUAL "") + set(LIBPAM_HAVE_CONFDIR FALSE) + else() + set(LIBPAM_HAVE_CONFDIR TRUE) + endif() + else() + set(LIBPAM_FOUND FALSE) + endif() + + set(LIBPAM_INCLUDE_DIRS ${LIBPAM_INCLUDE_DIR}) + set(LIBPAM_LIBRARIES ${LIBPAM_LIBRARY}) + + include(FindPackageHandleStandardArgs) + find_package_handle_standard_args(LibPAM DEFAULT_MSG LIBPAM_LIBRARIES LIBPAM_INCLUDE_DIRS) + + # show the LIBPAM_INCLUDE_DIRS and LIBPAM_LIBRARIES variables only in the advanced view + mark_as_advanced(LIBPAM_INCLUDE_DIRS LIBPAM_LIBRARIES) + +endif() diff --git a/CMakeModules/FindMbedTLS.cmake b/CMakeModules/FindMbedTLS.cmake new file mode 100644 index 0000000..17b96d7 --- /dev/null +++ b/CMakeModules/FindMbedTLS.cmake @@ -0,0 +1,110 @@ +# - Try to find MbedTLS +# Once done this will define +# +# MBEDTLS_FOUND - MbedTLS was found +# MBEDTLS_INCLUDE_DIRS - MbedTLS include directories +# MBEDTLS_LIBRARIES - link these to use MbedTLS +# MBEDTLS_VERSION - version of MbedTLS +# +# Author Roman Janota +# Copyright (c) 2024 CESNET, z.s.p.o. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# 1. Redistributions of source code must retain the copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. The name of the author may not be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# +include(FindPackageHandleStandardArgs) + +if(MBEDTLS_LIBRARIES AND MBEDTLS_INCLUDE_DIRS) + # in cache already + set(MBEDTLS_FOUND TRUE) +else() + find_path(MBEDTLS_INCLUDE_DIR + NAMES + mbedtls/ssl.h + PATHS + /opt/local/include + /sw/include + ${CMAKE_INCLUDE_PATH} + ${CMAKE_INSTALL_PREFIX}/include + ) + + find_library(MBEDTLS_LIBRARY + NAMES + libmbedtls.so + PATHS + /usr/lib + /usr/lib64 + /opt/local/lib + /sw/lib + ${CMAKE_LIBRARY_PATH} + ${CMAKE_INSTALL_PREFIX}/lib + ) + + find_library(MBEDX509_LIBRARY + NAMES + libmbedx509.so + PATHS + /usr/lib + /usr/lib64 + /opt/local/lib + /sw/lib + ${CMAKE_LIBRARY_PATH} + ${CMAKE_INSTALL_PREFIX}/lib + ) + + find_library(MBEDCRYPTO_LIBRARY + NAMES + libmbedcrypto.so + PATHS + /usr/lib + /usr/lib64 + /opt/local/lib + /sw/lib + ${CMAKE_LIBRARY_PATH} + ${CMAKE_INSTALL_PREFIX}/lib + ) + + if(MBEDTLS_INCLUDE_DIR AND MBEDTLS_LIBRARY AND MBEDX509_LIBRARY AND MBEDCRYPTO_LIBRARY) + # learn MbedTLS version + if(EXISTS "${MBEDTLS_INCLUDE_DIR}/mbedtls/build_info.h") + file(STRINGS "${MBEDTLS_INCLUDE_DIR}/mbedtls/build_info.h" MBEDTLS_VERSION + REGEX "#define[ \t]+MBEDTLS_VERSION_STRING[ \t]+\"([0-9]+\.[0-9]+\.[0-9]+)\"") + string(REGEX MATCH "[0-9]+\\.[0-9]+\\.[0-9]+" MBEDTLS_VERSION ${MBEDTLS_VERSION}) + endif() + if(NOT MBEDTLS_VERSION) + message(STATUS "MBEDTLS_VERSION not found, assuming MbedTLS is too old and cannot be used!") + set(MBEDTLS_INCLUDE_DIR "MBEDTLS_INCLUDE_DIR-NOTFOUND") + set(MBEDTLS_LIBRARY "MBEDTLS_LIBRARY-NOTFOUND") + endif() + endif() + + set(MBEDTLS_INCLUDE_DIRS ${MBEDTLS_INCLUDE_DIR}) + set(MBEDTLS_LIBRARIES ${MBEDTLS_LIBRARY} ${MBEDX509_LIBRARY} ${MBEDCRYPTO_LIBRARY}) + + find_package_handle_standard_args(MbedTLS FOUND_VAR MBEDTLS_FOUND + REQUIRED_VARS MBEDTLS_INCLUDE_DIRS MBEDTLS_LIBRARIES + VERSION_VAR MBEDTLS_VERSION) + + # show the MBEDTLS_INCLUDE_DIR and MBEDTLS_LIBRARIES variables only in the advanced view + mark_as_advanced(MBEDTLS_INCLUDE_DIRS MBEDTLS_LIBRARIES) +endif() diff --git a/CMakeModules/SourceFormat.cmake b/CMakeModules/SourceFormat.cmake index 76132a4..fd1e63b 100644 --- a/CMakeModules/SourceFormat.cmake +++ b/CMakeModules/SourceFormat.cmake @@ -2,7 +2,11 @@ # check that format checking is available - always use before SOURCE_FORMAT macro(SOURCE_FORMAT_ENABLE) - find_package(Uncrustify 0.71) + if(NOT ${ARGC} EQUAL 1) + message(FATAL_ERROR "source_format_enable() needs the required Uncrustify version!") + endif() + + find_package(Uncrustify ${ARGV0}) if(UNCRUSTIFY_FOUND) set(SOURCE_FORMAT_ENABLED TRUE) else() diff --git a/CMakeModules/UseCompat.cmake b/CMakeModules/UseCompat.cmake index 7d7a338..9a42123 100644 --- a/CMakeModules/UseCompat.cmake +++ b/CMakeModules/UseCompat.cmake @@ -6,7 +6,7 @@ # Additionally, "compat.h" include directory is added and can be included. # # Author Michal Vasko -# Copyright (c) 2021 CESNET, z.s.p.o. +# Copyright (c) 2021 - 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. @@ -15,7 +15,6 @@ # https://opensource.org/licenses/BSD-3-Clause # include(CheckSymbolExists) -include(CheckFunctionExists) include(CheckIncludeFile) include(TestBigEndian) if(POLICY CMP0075) @@ -24,10 +23,35 @@ endif() macro(USE_COMPAT) # compatibility checks - set(CMAKE_REQUIRED_DEFINITIONS -D_POSIX_C_SOURCE=200809L) + list(APPEND CMAKE_REQUIRED_DEFINITIONS -D_POSIX_C_SOURCE=200809L) list(APPEND CMAKE_REQUIRED_DEFINITIONS -D_GNU_SOURCE) list(APPEND CMAKE_REQUIRED_DEFINITIONS -D__BSD_VISIBLE=1) - set(CMAKE_REQUIRED_LIBRARIES pthread) + + check_symbol_exists(_POSIX_TIMERS "unistd.h" HAVE_CLOCK) + if(NOT HAVE_CLOCK) + message(FATAL_ERROR "Missing support for clock_gettime() and similar functions!") + endif() + + set(CMAKE_THREAD_PREFER_PTHREAD TRUE) + find_package(Threads) + list(APPEND CMAKE_REQUIRED_LIBRARIES ${CMAKE_THREAD_LIBS_INIT}) + + check_symbol_exists(pthread_mutex_timedlock "pthread.h" HAVE_PTHREAD_MUTEX_TIMEDLOCK) + check_symbol_exists(pthread_mutex_clocklock "pthread.h" HAVE_PTHREAD_MUTEX_CLOCKLOCK) + check_symbol_exists(pthread_rwlock_timedrdlock "pthread.h" HAVE_PTHREAD_RWLOCK_TIMEDRDLOCK) + check_symbol_exists(pthread_rwlock_clockrdlock "pthread.h" HAVE_PTHREAD_RWLOCK_CLOCKRDLOCK) + check_symbol_exists(pthread_rwlock_timedwrlock "pthread.h" HAVE_PTHREAD_RWLOCK_TIMEDWRLOCK) + check_symbol_exists(pthread_rwlock_clockwrlock "pthread.h" HAVE_PTHREAD_RWLOCK_CLOCKWRLOCK) + check_symbol_exists(pthread_cond_clockwait "pthread.h" HAVE_PTHREAD_COND_CLOCKWAIT) + if(HAVE_PTHREAD_MUTEX_CLOCKLOCK) + # can use CLOCK_MONOTONIC only if we have pthread_mutex_clocklock() + check_symbol_exists(_POSIX_MONOTONIC_CLOCK "unistd.h" HAVE_CLOCK_MONOTONIC) + endif() + if(HAVE_CLOCK_MONOTONIC) + set(COMPAT_CLOCK_ID "CLOCK_MONOTONIC") + else() + set(COMPAT_CLOCK_ID "CLOCK_REALTIME") + endif() check_symbol_exists(vdprintf "stdio.h;stdarg.h" HAVE_VDPRINTF) check_symbol_exists(asprintf "stdio.h" HAVE_ASPRINTF) @@ -41,14 +65,26 @@ macro(USE_COMPAT) check_symbol_exists(get_current_dir_name "unistd.h" HAVE_GET_CURRENT_DIR_NAME) - check_function_exists(pthread_mutex_timedlock HAVE_PTHREAD_MUTEX_TIMEDLOCK) + check_function_exists(timegm HAVE_TIMEGM) - TEST_BIG_ENDIAN(IS_BIG_ENDIAN) + # 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) - unset(CMAKE_REQUIRED_DEFINITIONS) - unset(CMAKE_REQUIRED_LIBRARIES) + list(REMOVE_ITEM CMAKE_REQUIRED_DEFINITIONS -D_POSIX_C_SOURCE=200809L) + list(REMOVE_ITEM CMAKE_REQUIRED_DEFINITIONS -D_GNU_SOURCE) + list(REMOVE_ITEM CMAKE_REQUIRED_DEFINITIONS -D__BSD_VISIBLE=1) + list(REMOVE_ITEM CMAKE_REQUIRED_LIBRARIES ${CMAKE_THREAD_LIBS_INIT}) # header and source file (adding the source directly allows for hiding its symbols) configure_file(${PROJECT_SOURCE_DIR}/compat/compat.h.in ${PROJECT_BINARY_DIR}/compat/compat.h @ONLY) diff --git a/Doxyfile.in b/Doxyfile.in index 324e670..551c59b 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/FAQ.md b/FAQ.md index 3acafb6..a30b19c 100644 --- a/FAQ.md +++ b/FAQ.md @@ -7,7 +7,7 @@ __Q: Having a fresh installation of *netopeer2-server*, when I connect to it I s __A:__ You are using *libssh* that was compiled with *gcrypt* library as the crypto backend. It does not support default SSH keys generated - during *netopeer2-server* installation. To fix, disable suport for this + during *netopeer2-server* installation. To fix, disable support for this backend when compiling *libssh* so that some other one is used. __Q: When a new NETCONF session is being created, I see the error:__ @@ -17,8 +17,9 @@ Starting the SSH session failed () __A:__ The most likely reason for this is that the SSH key that is used for this session authentication uses an algorithm not supported by - your system. The supported algorithms are automatically loaded by *libssh* - from OpenSSH configuration files (more info in `ssh_config(5)` and `sshd_config(5)`). + your system. The supported algorithms can be configured but if not, they + are automatically loaded by *libssh* from OpenSSH configuration files + (more info in `ssh_config(5)` and `sshd_config(5)`). __Q: When I try to connect to a server I immediately get a timeout after authenticating:__ @@ -36,3 +37,41 @@ __A:__ There are 2 most common reasons for this error. Either you are not using To fix, use a NETCONF client instead. Another reason may be that you are using *libssh* version 0.9.4. It includes a [regression bug](https://gitlab.com/libssh/libssh-mirror/-/merge_requests/101) that causes this problem and you must use another version to fix it. + +__Q: When I try to enter authentication tokens, they always echo back even though I set echo off:__ + +__A:__ You are most likely using an older version of *libssh* which contains a bug. + The bug was fixed in *libssh* 0.9.0, so you must use at least that version. + +__Q: When connecting over SSH and using publickey authentication, can I use a certificate:__ + +__A:__ No, it is not possible. There are currently 2 main types of certificates - *X.509v3* and *OpenSSH*. + *X.509v3* certificates for Secure Shell Authentication are a part of *NETCONF* specification + according to [RFC 6187](https://datatracker.ietf.org/doc/html/rfc6187), however using them + is currently not supported by *libssh* (version 0.9.6 as of writing this), which *libnetconf2* depends on. + As per the RFC mentioned before there are currently these `publickey` algorithms for *X.509v3* + supported by *NETCONF*: `x509v3-ssh-dss`, `x509v3-ssh-rsa`, `x509v3-rsa2048-sha256` and the family of + Elliptic Curve Digital Signature Algorithms `x509v3-ecdsa-sha2-*`. *libssh* 0.9.6 supports + these certificate publickey algorithms: `ssh-ed25519-cert-v01@openssh.com`, + `ecdsa-sha2-nistp521-cert-v01@openssh.com`, `ecdsa-sha2-nistp384-cert-v01@openssh.com`, + `ecdsa-sha2-nistp256-cert-v01@openssh.com`, `rsa-sha2-512-cert-v01@openssh.com`, + `rsa-sha2-256-cert-v01@openssh.com`, `ssh-rsa-cert-v01@openssh.com` and `ssh-dss-cert-v01@openssh.com`. + + + On the other hand there is a basic support for *OpenSSH* certificates in *libssh*. + The problem is that they are very minimalistic compared to *X.509v3* certificates + as per this [document](https://cvsweb.openbsd.org/src/usr.bin/ssh/PROTOCOL.certkeys?annotate=HEAD). + So when `publickey` authentication happens only the client's `publickey`, + which is extracted from the certificate, is sent to the server instead of the whole certificate. + This means that the `cert-to-name` process required by *NETCONF* can not take place. Specifically, + OpenSSH certificates are missing important fields such as `Common Name`, `Subject Alternative Name` and so on. + +__Q: I have client-side keepalives and monitoring enabled, but it takes a long time for the client to detect that the connection was terminated:__ +__A:__ Assuming that the network connection is fine or is loopback, then this is the standard TCP behavior. + The client will not immediately detect that the connection was terminated unless + it tries to send some data or unless a specific timeout occurs. + + Even though the server was terminated, its socket remains in a lingering state for some time and continues to reply to incoming + TCP keepalive packets. In particular, this timeout you're encountering is most likely affected by the `tcp_fin_timeout` kernel parameter, + which controls how long the TCP stack waits before timing out a half-closed connection after receiving a FIN packet. + The default value is typically 60 seconds, but it can be configured based on your needs. diff --git a/FindLibNETCONF2.cmake b/FindLibNETCONF2.cmake index ebacf63..b400dbb 100644 --- a/FindLibNETCONF2.cmake +++ b/FindLibNETCONF2.cmake @@ -4,11 +4,12 @@ # LIBNETCONF2_FOUND - system has LibNETCONF2 # LIBNETCONF2_INCLUDE_DIRS - the LibNETCONF2 include directory # LIBNETCONF2_LIBRARIES - Link these to use LibNETCONF2 +# LIBNETCONF2_VERSION - SO version of the found libNETCONF2 library # LIBNETCONF2_ENABLED_SSH - LibNETCONF2 was compiled with SSH support # LIBNETCONF2_ENABLED_TLS - LibNETCONF2 was compiled with TLS support # # Author Michal Vasko -# Copyright (c) 2020 CESNET, z.s.p.o. +# Copyright (c) 2021 CESNET, z.s.p.o. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions @@ -34,7 +35,6 @@ # THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # include(FindPackageHandleStandardArgs) -include(CheckSymbolExists) if(LIBNETCONF2_LIBRARIES AND LIBNETCONF2_INCLUDE_DIRS) # in cache already @@ -68,13 +68,28 @@ else() ${CMAKE_INSTALL_PREFIX}/lib ) + if(LIBNETCONF2_INCLUDE_DIR) + find_path(NC_VERSION_PATH "nc_version.h" HINTS ${LIBNETCONF2_INCLUDE_DIR}) + if(NOT NC_VERSION_PATH) + message(STATUS "libnetconf2 version header not found, assuming libnetconf2 is too old and cannot be used!") + set(LIBNETCONF2_INCLUDE_DIR "LIBNETCONF2_INCLUDE_DIR-NOTFOUND") + set(LIBNETCONF2_LIBRARY "LIBNETCONF2_LIBRARY-NOTFOUND") + else() + file(READ "${NC_VERSION_PATH}/nc_version.h" NC_VERSION_FILE) + string(REGEX MATCH "#define NC_VERSION \"[0-9]+\\.[0-9]+\\.[0-9]+\"" NC_VERSION_MACRO "${NC_VERSION_FILE}") + string(REGEX MATCH "[0-9]+\\.[0-9]+\\.[0-9]+" LIBNETCONF2_VERSION "${NC_VERSION_MACRO}") + endif() + endif() + set(LIBNETCONF2_INCLUDE_DIRS ${LIBNETCONF2_INCLUDE_DIR}) set(LIBNETCONF2_LIBRARIES ${LIBNETCONF2_LIBRARY}) mark_as_advanced(LIBNETCONF2_INCLUDE_DIRS LIBNETCONF2_LIBRARIES) # handle the QUIETLY and REQUIRED arguments and set SYSREPO_FOUND to TRUE # if all listed variables are TRUE - find_package_handle_standard_args(LibNETCONF2 DEFAULT_MSG LIBNETCONF2_LIBRARY LIBNETCONF2_INCLUDE_DIR) + find_package_handle_standard_args(LibNETCONF2 FOUND_VAR LIBNETCONF2_FOUND + REQUIRED_VARS LIBNETCONF2_LIBRARY LIBNETCONF2_INCLUDE_DIR + VERSION_VAR LIBNETCONF2_VERSION) # check the configured options and make them available through cmake list(INSERT CMAKE_REQUIRED_INCLUDES 0 "${LIBNETCONF2_INCLUDE_DIR}") diff --git a/README.md b/README.md index 245ce20..ab05cc1 100644 --- a/README.md +++ b/README.md @@ -22,13 +22,14 @@ 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 [CESNET](http://www.ces.net/). Any testing or improving/fixing the library is welcome. Please inform us about your experiences with using **libnetconf2** -via the [issue tracker](https://github.com/CESNET/libnetconf/issues). +via the [issue tracker](https://github.com/CESNET/libnetconf2/issues). Besides the [**libyang**](https://github.com/CESNET/libyang), **libnetconf2** is another basic building block for the [**Netopeer2** toolset](https://github.com/CESNET/Netopeer2). @@ -51,79 +52,41 @@ and it occurs on the `master` branch, the **first response will likely be** to u of the [**libnetconf**](https://github.com/CESNET/libnetconf) library, which is now obsolete and should not be used. -# Installation +## Packages -## Required Dependencies +Binary RPM or DEB packages of the latest release can be built locally using `apkg`, look into `README` in +the `distro` directory. -Install the following libraries and tools the libnetconf2 depends on. +## Requirements -### libyang -Follow the [libyang instructions](https://github.com/CESNET/libyang/blob/master/README.md), -in short: -``` -$ git clone https://github.com/CESNET/libyang.git -$ cd libyang; mkdir build; cd build -$ cmake .. -$ make -# make install -``` +* C compiler (gcc >= 4.8.4, clang >= 3.0, ...) +* cmake >= 3.5.0 +* crypt(3) +* [libyang](https://github.com/CESNET/libyang) +* libssh >= 0.9.5 (for SSH support) +* OpenSSL >= 3.0.0 or MbedTLS >= 3.5.0 (for TLS support) +* curl >= 7.30.0 -### libssh -Required version is at least 0.7.1. This dependency can be removed by disabling -SSH support (see the [Build Options](#build-options) section). Below si the basic -sequence of commands for compiling and installing it from source. However, there -are packages for certain Linux distributions available [here](https://www.libssh.org/get-it/). -``` -$ git clone http://git.libssh.org/projects/libssh.git -$ cd libssh; mkdir build; cd build -$ cmake .. -$ make -# make install -``` +#### Optional -### OpenSSL -This dependency is required when the TLS support is enabled, which it is by -default but libssh requires it too. So, to remove this dependency, you need -to disable both SSH and TLS (see the [Build Options](#build-options) section). +* libpam (for PAM-based SSH `keyboard-interactive` authentication method) +* libval (only for DNSSEC SSHFP retrieval) + * [DNSSEC-Tools/dnssec-tools/validator](https://github.com/DNSSEC-Tools/DNSSEC-Tools/tree/master/dnssec-tools/validator) + part of the DNSSEC-Tools suite +* doxygen (for generating documentation) +* cmocka >= 1.0.1 (for tests only, see [Tests](#Tests)) +* valgrind (for enhanced testing) +* gcov (for code coverage) +* lcov (for code coverage) +* genhtml (for code coverage) -OpenSSL is a standard part of the most distribution, so ask your package -manager for OpenSSL package including the necessary development files -(usually -dev or -devel package). - -## Optional Dependencies - -### libval (part of the DNSSEC-Tools suite) -It is required only if DNSSEC SSHFP retrieval is enabled (it is disabled by -default, see the [Build Options](#build-options) section). - -The easier way of installing it is as the libval-dev package (or a part of -the dnssec-tools package), if you can find it for your distribution. Otherwise, -compile and install it from [source](https://github.com/DNSSEC-Tools/DNSSEC-Tools/). -Only the validator component (`DNSSEC-Tools/dnssec-tools/validator`) is needed. - -### cmocka -For running the tests cmocka 1.0.1 is required (see the [Tests](#tests) section for more information). -``` -$ sudo apt-get install libcmocka-dev -``` - -### Doxygen -For building the library documentation. - -Doxygen is a standard part of the most distribution, so ask your package -manager for doxygen package. - -### gcov - -For code coverage, `gcov`, `lcov`, and `genhtml` are needed. - -## Building libnetconf2 +## Building ``` $ mkdir build; cd build $ cmake .. $ make -# install +# make install ``` The library documentation can be generated directly from the source codes using @@ -161,7 +124,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 @@ -229,10 +192,7 @@ $ make $ make coverage ``` -Note that `gcc` compiler is required for this option and additional tools are required: -* gcov -* lcov -* genhtml +Note that `gcc` compiler is required for this option. ### CMake Notes @@ -248,7 +208,7 @@ All public functions are available via 2 headers: #include ``` -You need to include either one if imeplementing a NETCONF server or a NETCONF client, +You need to include either one if implementing a NETCONF server or a NETCONF client, respectively. To compile your program with libnetconf2, it is necessary to link it with it using the @@ -257,6 +217,10 @@ following linker parameters: -lnetconf2 ``` +## Examples + +See [examples](examples) directory for an example client and server. + ## Tests The repository includes several tests built with [cmocka](https://cmocka.org/). @@ -283,3 +247,43 @@ Tests can be run by the make's `test` target: $ make test ``` +## Supported YANG modules + +### Server + +The *libnetconf2* NETCONF server has two APIs that load YANG modules into the context. The first API is [nc_server_init_ctx](https://netopeer.liberouter.org/doc/libnetconf2/master/html/group__server__functions.html#ga35cccf2dbe9204abe01ccb4b93db7438), which loads the following YANG modules with their features: + +- **ietf-netconf**: writable-running, candidate, rollback-on-error, validate, startup, url, xpath, confirmed-commit, +- **ietf-netconf-monitoring**: no features. + +The second API is [nc_server_config_load_modules](https://netopeer.liberouter.org/doc/libnetconf2/master/html/group__server__config__functions.html#ga3760b87e3ab4309514e9ad82c4c09cdb). Supported features (marked by ✔) are loaded into the context by this API. + +- **iana-crypt-hash**: crypt-hash-md5 ✔, crypt-hash-sha-256 ✔, crypt-hash-sha-512 ✔, +- **ietf-netconf-server**: ssh-listen ✔, tls-listen ✔, ssh-call-home ✔, tls-call-home ✔, central-netconf-server-supported ✔, +- **iana-ssh-encryption-algs**: no features, +- **iana-ssh-key-exchange-algs**: no features, +- **iana-ssh-mac-algs**: no features, +- **iana-ssh-public-key-algs**: no features, +- **iana-tls-cipher-suite-algs**: no features, +- **ietf-crypto-types**: cleartext-passwords ✔, cleartext-private-keys ✔, 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 ✘, +- **ietf-keystore**: central-keystore-supported ✔, inline-definitions-supported ✔, asymmetric-keys ✔, symmetric-keys ✘, +- **ietf-netconf-server**: ssh-listen ✔, tls-listen ✔, ssh-call-home ✔, tls-call-home ✔, central-netconf-server-supported ✔, +- **ietf-ssh-common**: transport-params ✔, ssh-x509-certs ✘, public-key-generation ✘, +- **ietf-ssh-server**: local-users-supported **?**, local-user-auth-publickey ✔, local-user-auth-password ✔, local-user-auth-none ✔, ssh-server-keepalives ✘, local-user-auth-hostbased ✘, +- **ietf-tcp-client**: tcp-client-keepalives ✔, proxy-connect ✘, socks5-gss-api ✘, socks5-username-password ✘, local-binding-supported ✔, +- **ietf-tcp-common**: transport-params ✔, ssh-x509-certs ✘, public-key-generation ✘, +- **ietf-tcp-server**: tcp-server-keepalives ✔, +- **ietf-tls-common**: tls10 ✔, tls11 ✔, tls12 ✔, tls13 ✔, hello-params ✔, public-key-generation ✘, +- **ietf-tls-server**: server-ident-x509-cert ✔, client-auth-supported ✔, client-auth-x509-cert ✔, 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 ✘, +- **ietf-truststore**: central-truststore-supported ✔, inline-definitions-supported ✔, certificates ✔, public-keys ✔, +- **ietf-x509-cert-to-name**: no features, +- **libnetconf2-netconf-server**: no features. + +The following features can be enabled/disabled to influence the behaviour of the `libnetconf2` NETCONF server: + +- `local-users-supported` - enabled by default, disable to change the behaviour of the SSH authentication (see the *libnetconf2* [documentation](https://netopeer.liberouter.org/doc/libnetconf2/master/html/howtoserver.html)). +- `certificate-expiration-notification` - disabled by default, but certificate expiration notifications are supported and you can enable this feature to create such YANG data (see the *libnetconf2* documentation). + +### Client + +Currently no client specific YANG modules are supported. diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 0000000..8a0d573 --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,30 @@ +# Security Policy + +If you discover a security-related issue, please report it based on the instructions below. + +## Reporting a Vulnerability + +Please **DO NOT** file a public issue, instead report the vulnerability on the relevant +[GitHub security](https://github.com/CESNET/libnetconf2/security) page. If you do not receive any reaction within 48 hours, +please also send an email to [mvasko@cesnet.cz]. + +## Review Process + +After receiving the report, an initial triage and technical analysis is performed to confirm the report and determine +its scope. We may request additional information in this stage of the process. + +Once a reviewer has confirmed the relevance of the report, a draft security advisory will be created on GitHub. The +draft advisory will be used to discuss the issue with maintainers, the reporter(s), and where applicable, other affected +parties under embargo. + +If the vulnerability is accepted, a timeline for developing a patch, public disclosure, and patch release will be +determined. If there is an embargo period on public disclosure before the patch release, the reporter(s) are expected to +participate in the discussion of the timeline and abide by agreed upon dates for public disclosure. + +Usually, the reasonably complex issues are fixed within hours of being reported. + +## Supported Versions + +After an issue is fixed, it **WILL NOT** be backported to any released version. Instead, it is kept in the public `devel` +branch, which is periodically merged into the main branch when a new release is due. So, the issue will be fixed in the +next release after it is fixed. diff --git a/compat/compat.c b/compat/compat.c index bf75c74..e7f747f 100644 --- a/compat/compat.c +++ b/compat/compat.c @@ -3,7 +3,7 @@ * @author Michal Vasko * @brief compatibility functions * - * Copyright (c) 2021 CESNET, z.s.p.o. + * Copyright (c) 2021 - 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,6 +16,7 @@ #include "compat.h" +#include #include #include #include @@ -28,6 +29,179 @@ #include #include +#ifndef HAVE_PTHREAD_MUTEX_TIMEDLOCK +int +pthread_mutex_timedlock(pthread_mutex_t *mutex, const struct timespec *abstime) +{ + int64_t nsec_diff; + struct timespec cur, dur; + int rc; + + /* try to acquire the lock and, if we fail, sleep for 5ms. */ + while ((rc = pthread_mutex_trylock(mutex)) == EBUSY) { + /* get time */ + clock_gettime(COMPAT_CLOCK_ID, &cur); + + /* get time diff */ + nsec_diff = 0; + nsec_diff += (((int64_t)abstime->tv_sec) - ((int64_t)cur.tv_sec)) * 1000000000L; + nsec_diff += ((int64_t)abstime->tv_nsec) - ((int64_t)cur.tv_nsec); + + if (nsec_diff <= 0) { + /* timeout */ + rc = ETIMEDOUT; + break; + } else if (nsec_diff < 5000000) { + /* sleep until timeout */ + dur.tv_sec = 0; + dur.tv_nsec = nsec_diff; + } else { + /* sleep 5 ms */ + dur.tv_sec = 0; + dur.tv_nsec = 5000000; + } + + nanosleep(&dur, NULL); + } + + return rc; +} + +#endif + +#ifndef HAVE_PTHREAD_MUTEX_CLOCKLOCK +int +pthread_mutex_clocklock(pthread_mutex_t *mutex, clockid_t clockid, const struct timespec *abstime) +{ + /* only real time supported without this function */ + if (clockid != CLOCK_REALTIME) { + return EINVAL; + } + + return pthread_mutex_timedlock(mutex, abstime); +} + +#endif + +#ifndef HAVE_PTHREAD_RWLOCK_TIMEDRDLOCK +int +pthread_rwlock_timedrdlock(pthread_rwlock_t *rwlock, const struct timespec *abstime) +{ + int64_t nsec_diff; + struct timespec cur, dur; + int rc; + + /* try to acquire the lock and, if we fail, sleep for 5ms. */ + while ((rc = pthread_rwlock_tryrdlock(rwlock)) == EBUSY) { + /* get time */ + clock_gettime(COMPAT_CLOCK_ID, &cur); + + /* get time diff */ + nsec_diff = 0; + nsec_diff += (((int64_t)abstime->tv_sec) - ((int64_t)cur.tv_sec)) * 1000000000L; + nsec_diff += ((int64_t)abstime->tv_nsec) - ((int64_t)cur.tv_nsec); + + if (nsec_diff <= 0) { + /* timeout */ + rc = ETIMEDOUT; + break; + } else if (nsec_diff < 5000000) { + /* sleep until timeout */ + dur.tv_sec = 0; + dur.tv_nsec = nsec_diff; + } else { + /* sleep 5 ms */ + dur.tv_sec = 0; + dur.tv_nsec = 5000000; + } + + nanosleep(&dur, NULL); + } + + return rc; +} + +#endif + +#ifndef HAVE_PTHREAD_RWLOCK_CLOCKRDLOCK +int +pthread_rwlock_clockrdlock(pthread_rwlock_t *rwlock, clockid_t clockid, const struct timespec *abstime) +{ + /* only real time supported without this function */ + if (clockid != CLOCK_REALTIME) { + return EINVAL; + } + + return pthread_rwlock_timedrdlock(rwlock, abstime); +} + +#endif + +#ifndef HAVE_PTHREAD_RWLOCK_TIMEDWRLOCK +int +pthread_rwlock_timedwrlock(pthread_rwlock_t *rwlock, const struct timespec *abstime) +{ + int64_t nsec_diff; + struct timespec cur, dur; + int rc; + + /* try to acquire the lock and, if we fail, sleep for 5ms. */ + while ((rc = pthread_rwlock_trywrlock(rwlock)) == EBUSY) { + /* get time */ + clock_gettime(COMPAT_CLOCK_ID, &cur); + + /* get time diff */ + nsec_diff = 0; + nsec_diff += (((int64_t)abstime->tv_sec) - ((int64_t)cur.tv_sec)) * 1000000000L; + nsec_diff += ((int64_t)abstime->tv_nsec) - ((int64_t)cur.tv_nsec); + + if (nsec_diff <= 0) { + /* timeout */ + rc = ETIMEDOUT; + break; + } else if (nsec_diff < 5000000) { + /* sleep until timeout */ + dur.tv_sec = 0; + dur.tv_nsec = nsec_diff; + } else { + /* sleep 5 ms */ + dur.tv_sec = 0; + dur.tv_nsec = 5000000; + } + + nanosleep(&dur, NULL); + } + + return rc; +} + +#endif + +#ifndef HAVE_PTHREAD_RWLOCK_CLOCKWRLOCK +int +pthread_rwlock_clockwrlock(pthread_rwlock_t *rwlock, clockid_t clockid, const struct timespec *abstime) +{ + /* only real time supported without this function */ + if (clockid != CLOCK_REALTIME) { + return EINVAL; + } + + return pthread_rwlock_timedwrlock(rwlock, abstime); +} + +#endif + +#ifndef HAVE_PTHREAD_COND_CLOCKWAIT +int +pthread_cond_clockwait(pthread_cond_t *cond, pthread_mutex_t *mutex, clockid_t UNUSED(clockid), + const struct timespec *abstime) +{ + /* assume the correct clock is set during cond init */ + return pthread_cond_timedwait(cond, mutex, abstime); +} + +#endif + #ifndef HAVE_VDPRINTF int vdprintf(int fd, const char *format, va_list ap) @@ -200,51 +374,54 @@ get_current_dir_name(void) #endif -#ifndef HAVE_PTHREAD_MUTEX_TIMEDLOCK -int -pthread_mutex_timedlock(pthread_mutex_t *mutex, const struct timespec *abstime) +#ifndef HAVE_CRYPT_R +char * +crypt_r(const char *phrase, const char *setting, struct crypt_data *data) { - int64_t nsec_diff; - int32_t diff; - struct timespec cur, dur; - int rc; + static pthread_mutex_t crypt_lock = PTHREAD_MUTEX_INITIALIZER; + char *hash; - /* try to acquire the lock and, if we fail, sleep for 5ms. */ - while ((rc = pthread_mutex_trylock(mutex)) == EBUSY) { - /* get real time */ -#ifdef CLOCK_REALTIME - clock_gettime(CLOCK_REALTIME, &cur); -#else - struct timeval tv; + (void) data; - gettimeofday(&tv, NULL); - cur.tv_sec = (time_t)tv.tv_sec; - cur.tv_nsec = 1000L * (long)tv.tv_usec; -#endif + pthread_mutex_lock(&crypt_lock); + hash = crypt(phrase, setting); + pthread_mutex_unlock(&crypt_lock); - /* get time diff */ - nsec_diff = 0; - nsec_diff += (((int64_t)abstime->tv_sec) - ((int64_t)cur.tv_sec)) * 1000000000L; - nsec_diff += ((int64_t)abstime->tv_nsec) - ((int64_t)cur.tv_nsec); - diff = (nsec_diff ? nsec_diff / 1000000L : 0); - - if (diff < 1) { - /* timeout */ - break; - } else if (diff < 5) { - /* sleep until timeout */ - dur.tv_sec = 0; - dur.tv_nsec = (long)diff * 1000000; - } else { - /* sleep 5 ms */ - dur.tv_sec = 0; - dur.tv_nsec = 5000000; - } - - nanosleep(&dur, NULL); - } - - return rc; + return hash; +} + +#endif + +#ifndef HAVE_TIMEGM +time_t +timegm(struct tm *tm) +{ + pthread_mutex_t tz_lock = PTHREAD_MUTEX_INITIALIZER; + time_t ret; + char *tz; + + pthread_mutex_lock(&tz_lock); + + tz = getenv("TZ"); + if (tz) { + tz = strdup(tz); + } + setenv("TZ", "", 1); + tzset(); + + ret = mktime(tm); + + if (tz) { + setenv("TZ", tz, 1); + free(tz); + } else { + unsetenv("TZ"); + } + tzset(); + + pthread_mutex_unlock(&tz_lock); + + return ret; } #endif diff --git a/compat/compat.h.in b/compat/compat.h.in index acc5fc7..45829c5 100644 --- a/compat/compat.h.in +++ b/compat/compat.h.in @@ -3,7 +3,7 @@ * @author Michal Vasko * @brief compatibility functions header * - * Copyright (c) 2021 CESNET, z.s.p.o. + * Copyright (c) 2021 - 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. @@ -15,12 +15,22 @@ #ifndef _COMPAT_H_ #define _COMPAT_H_ +#define _GNU_SOURCE /* pthread_rwlock_t */ + +#cmakedefine HAVE_CRYPT_H + +#ifdef HAVE_CRYPT_H +# include +#endif + +#include #include #include #include #include #include #include +#include #ifndef __WORDSIZE # if defined __x86_64__ && !defined __ILP32__ @@ -48,6 +58,15 @@ # define _PACKED #endif +#define COMPAT_CLOCK_ID @COMPAT_CLOCK_ID@ +#cmakedefine HAVE_PTHREAD_MUTEX_TIMEDLOCK +#cmakedefine HAVE_PTHREAD_MUTEX_CLOCKLOCK +#cmakedefine HAVE_PTHREAD_RWLOCK_TIMEDRDLOCK +#cmakedefine HAVE_PTHREAD_RWLOCK_CLOCKRDLOCK +#cmakedefine HAVE_PTHREAD_RWLOCK_TIMEDWRLOCK +#cmakedefine HAVE_PTHREAD_RWLOCK_CLOCKWRLOCK +#cmakedefine HAVE_PTHREAD_COND_CLOCKWAIT + #cmakedefine HAVE_VDPRINTF #cmakedefine HAVE_ASPRINTF #cmakedefine HAVE_VASPRINTF @@ -57,7 +76,8 @@ #cmakedefine HAVE_STRDUPA #cmakedefine HAVE_STRCHRNUL #cmakedefine HAVE_GET_CURRENT_DIR_NAME -#cmakedefine HAVE_PTHREAD_MUTEX_TIMEDLOCK +#cmakedefine HAVE_CRYPT_R +#cmakedefine HAVE_TIMEGM #ifndef bswap64 #define bswap64(val) \ @@ -87,6 +107,10 @@ # define ATOMIC_T atomic_uint_fast32_t # define ATOMIC_T_MAX UINT_FAST32_MAX +# define ATOMIC64_T atomic_uint_fast64_t +# define ATOMIC64_T_MAX UINT_FAST64_MAX + +# define ATOMIC_PTR_T atomic_uintptr_t # define ATOMIC_STORE_RELAXED(var, x) atomic_store_explicit(&(var), x, memory_order_relaxed) # define ATOMIC_LOAD_RELAXED(var) atomic_load_explicit(&(var), memory_order_relaxed) @@ -94,11 +118,20 @@ # define ATOMIC_ADD_RELAXED(var, x) atomic_fetch_add_explicit(&(var), x, memory_order_relaxed) # define ATOMIC_DEC_RELAXED(var) atomic_fetch_sub_explicit(&(var), 1, memory_order_relaxed) # define ATOMIC_SUB_RELAXED(var, x) atomic_fetch_sub_explicit(&(var), x, memory_order_relaxed) +# define ATOMIC_COMPARE_EXCHANGE_RELAXED(var, exp, des, result) \ + result = atomic_compare_exchange_strong_explicit(&(var), &(exp), des, memory_order_relaxed, memory_order_relaxed) + +# define ATOMIC_PTR_STORE_RELAXED(var, x) atomic_store_explicit(&(var), (uintptr_t)(x), memory_order_relaxed) +# define ATOMIC_PTR_LOAD_RELAXED(var) ((void *)atomic_load_explicit(&(var), memory_order_relaxed)) #else # include # define ATOMIC_T uint32_t # define ATOMIC_T_MAX UINT32_MAX +# define ATOMIC64_T uint64_t +# define ATOMIC64_T_MAX UINT64_MAX + +# define ATOMIC_PTR_T void * # define ATOMIC_STORE_RELAXED(var, x) ((var) = (x)) # define ATOMIC_LOAD_RELAXED(var) (var) @@ -106,6 +139,35 @@ # define ATOMIC_ADD_RELAXED(var, x) __sync_fetch_and_add(&(var), x) # define ATOMIC_DEC_RELAXED(var) __sync_fetch_and_sub(&(var), 1) # define ATOMIC_SUB_RELAXED(var, x) __sync_fetch_and_sub(&(var), x) +# define ATOMIC_COMPARE_EXCHANGE_RELAXED(var, exp, des, result) \ + { \ + ATOMIC_T __old = __sync_val_compare_and_swap(&(var), exp, des); \ + result = ATOMIC_LOAD_RELAXED(__old) == ATOMIC_LOAD_RELAXED(exp) ? 1 : 0; \ + ATOMIC_STORE_RELAXED(exp, ATOMIC_LOAD_RELAXED(__old)); \ + } + +# define ATOMIC_PTR_STORE_RELAXED(var, x) ((var) = (x)) +# define ATOMIC_PTR_LOAD_RELAXED(var) (var) +#endif + +#ifndef HAVE_PTHREAD_MUTEX_TIMEDLOCK +int pthread_mutex_timedlock(pthread_mutex_t *mutex, const struct timespec *abstime); +#endif + +#ifndef HAVE_PTHREAD_MUTEX_CLOCKLOCK +int pthread_mutex_clocklock(pthread_mutex_t *mutex, clockid_t clockid, const struct timespec *abstime); +#endif + +#ifndef HAVE_PTHREAD_RWLOCK_CLOCKRDLOCK +int pthread_rwlock_clockrdlock(pthread_rwlock_t *rwlock, clockid_t clockid, const struct timespec *abstime); +#endif + +#ifndef HAVE_PTHREAD_RWLOCK_CLOCKWRLOCK +int pthread_rwlock_clockwrlock(pthread_rwlock_t *rwlock, clockid_t clockid, const struct timespec *abstime); +#endif + +#ifndef HAVE_PTHREAD_COND_CLOCKWAIT +int pthread_cond_clockwait(pthread_cond_t *cond, pthread_mutex_t *mutex, clockid_t clockid, const struct timespec *abstime); #endif #ifndef HAVE_VDPRINTF @@ -151,8 +213,18 @@ char *strchrnul(const char *s, int c); char *get_current_dir_name(void); #endif -#ifndef HAVE_PTHREAD_MUTEX_TIMEDLOCK -int pthread_mutex_timedlock(pthread_mutex_t *mutex, const struct timespec *abstime); +#ifndef HAVE_CRYPT_R + +/* unused anyway */ +struct crypt_data { + char a; +}; + +char *crypt_r(const char *phrase, const char *setting, struct crypt_data *data); +#endif + +#ifndef HAVE_TIMEGM +time_t timegm(struct tm *tm); #endif #endif /* _COMPAT_H_ */ diff --git a/distro/README.md b/distro/README.md new file mode 100644 index 0000000..70dba2d --- /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/config/apkg.toml b/distro/config/apkg.toml index 49a5ead..cbcd96d 100644 --- a/distro/config/apkg.toml +++ b/distro/config/apkg.toml @@ -3,8 +3,8 @@ name = "libnetconf2" make_archive_script = "distro/scripts/make-archive.sh" [upstream] -archive_url = "https://github.com/CESNET/libnetconf2/archive/refs/tags/v{{ version }}.tar.gz" +archive_url = "https://github.com/CESNET/libnetconf2/archive/v{{ version }}/libnetconf2-{{ version }}.tar.gz" version_script = "distro/scripts/upstream-version.sh" [apkg] -compat = 1 +compat = 2 diff --git a/distro/pkg/deb/control b/distro/pkg/deb/control index 34324c5..61f7672 100644 --- a/distro/pkg/deb/control +++ b/distro/pkg/deb/control @@ -6,14 +6,16 @@ Priority: optional Standards-Version: 4.5.0 Build-Depends: cmake, debhelper (>= 10), - libyang2-dev, - libssl-dev, - libssh-dev (>= 0.7.1), - pkg-config + libyang-dev, + libssl-dev (>= 3.0.0), + libssh-dev (>= 0.9.5), + libpam0g-dev, + 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-2 +Package: libnetconf4 Depends: ${misc:Depends}, ${shlibs:Depends} Architecture: any @@ -27,8 +29,8 @@ Description: library implementing NETCONF protocol - runtime It is implemented in C. Package: libnetconf2-dev -Depends: libyang2-dev, - libnetconf2-2 (= ${binary:Version}), +Depends: libyang-dev, + libnetconf4 (= ${binary:Version}), ${misc:Depends} Section: libdevel Architecture: any @@ -41,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-2.install b/distro/pkg/deb/libnetconf2-2.install deleted file mode 100644 index 09074c4..0000000 --- a/distro/pkg/deb/libnetconf2-2.install +++ /dev/null @@ -1 +0,0 @@ -usr/lib/*/libnetconf2.so.* diff --git a/distro/pkg/deb/libnetconf2-dev.install b/distro/pkg/deb/libnetconf2-dev.install index 74785f7..c8a621f 100644 --- a/distro/pkg/deb/libnetconf2-dev.install +++ b/distro/pkg/deb/libnetconf2-dev.install @@ -3,3 +3,4 @@ usr/lib/*/pkgconfig/libnetconf2.pc usr/include/libnetconf2/* usr/include/nc_client.h usr/include/nc_server.h +usr/include/nc_version.h diff --git a/distro/pkg/deb/libnetconf4.install b/distro/pkg/deb/libnetconf4.install new file mode 100644 index 0000000..10ffdc6 --- /dev/null +++ b/distro/pkg/deb/libnetconf4.install @@ -0,0 +1,2 @@ +usr/lib/*/libnetconf2.so.* +usr/share/yang/modules/libnetconf2 diff --git a/distro/pkg/rpm/libnetconf2.spec b/distro/pkg/rpm/libnetconf2.spec index b491e59..96d13c5 100644 --- a/distro/pkg/rpm/libnetconf2.spec +++ b/distro/pkg/rpm/libnetconf2.spec @@ -3,20 +3,22 @@ Version: {{ version }} Release: {{ release }}%{?dist} Summary: NETCONF protocol library Url: https://github.com/CESNET/libnetconf2 -Source: libnetconf2-%{version}.tar.gz +Source: %{url}/archive/v%{version}/%{name}-%{version}.tar.gz License: BSD BuildRequires: cmake -BuildRequires: make BuildRequires: gcc BuildRequires: libssh-devel BuildRequires: openssl-devel +BuildRequires: pam-devel BuildRequires: pkgconfig(libyang) >= 2 +BuildRequires: libcurl-devel %package devel Summary: Headers of libnetconf2 library Conflicts: libnetconf-devel Requires: %{name}%{?_isa} = %{version}-%{release} +Requires: pkgconfig %description devel Headers of libnetconf library. @@ -28,27 +30,24 @@ servers. NETCONF is the NETwork CONFiguration protocol introduced by IETF. %prep %autosetup -p1 -mkdir build %build -cd build -cmake \ - -DCMAKE_INSTALL_PREFIX:PATH=%{_prefix} \ - -DCMAKE_BUILD_TYPE:String="Release" \ - -DCMAKE_C_FLAGS="${RPM_OPT_FLAGS}" \ - -DCMAKE_CXX_FLAGS="${RPM_OPT_FLAGS}" \ - .. -make +%cmake -DCMAKE_BUILD_TYPE=RELWITHDEBINFO -DENABLE_TESTS=OFF +%cmake_build %install -cd build -make DESTDIR=%{buildroot} install +%cmake_install + %files %license LICENSE -%{_libdir}/libnetconf2.so.2* +%doc README.md FAQ.md +%{_libdir}/libnetconf2.so.* +%{_datadir}/yang/modules/libnetconf2/*.yang +%dir %{_datadir}/yang/modules/libnetconf2/ %files devel +%doc CODINGSTYLE.md %{_libdir}/libnetconf2.so %{_libdir}/pkgconfig/libnetconf2.pc %{_includedir}/*.h @@ -57,5 +56,5 @@ make DESTDIR=%{buildroot} install %changelog -* Tue Oct 12 2021 Jakub Ružička - {{ version }}-{{ release }} +* {{ now }} Jakub Ružička - {{ version }}-{{ release }} - upstream package diff --git a/distro/scripts/make-archive.sh b/distro/scripts/make-archive.sh index d518845..2ea7c5a 100755 --- a/distro/scripts/make-archive.sh +++ b/distro/scripts/make-archive.sh @@ -1,10 +1,7 @@ #!/bin/bash # create archive from current source using git -VERSION=$(git describe --tags --always) -# skip "v" from start of version number (if it exists) and replace - with . -VERSION=${VERSION#v} -VERSION=${VERSION//[-]/.} +VERSION=$(git log --oneline -n1 --grep="^VERSION" | rev | cut -d' ' -f1 | rev) NAMEVER=libnetconf2-$VERSION ARCHIVE=$NAMEVER.tar.gz diff --git a/distro/tests/control b/distro/tests/control new file mode 100644 index 0000000..3f1aeba --- /dev/null +++ b/distro/tests/control @@ -0,0 +1 @@ +Tests: test-pkg-config.sh diff --git a/distro/tests/test-pkg-config.sh b/distro/tests/test-pkg-config.sh new file mode 100755 index 0000000..abad2be --- /dev/null +++ b/distro/tests/test-pkg-config.sh @@ -0,0 +1,5 @@ +#!/bin/bash +set -ex + +version=`pkg-config --modversion libnetconf2` +echo "$version" | grep '2\.[0-9.]\+' diff --git a/src/libnetconf.h b/doc/libnetconf.doc similarity index 54% rename from src/libnetconf.h rename to doc/libnetconf.doc index 9a8f74a..620b76e 100644 --- a/src/libnetconf.h +++ b/doc/libnetconf.doc @@ -1,31 +1,3 @@ -/** - * @file libnetconf.h - * @author Radek Krejci - * @author Michal Vasko - * @brief libnetconf2 main internal header. - * - * 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 * @@ -45,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 * @@ -82,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 @@ -103,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. * @@ -115,8 +86,9 @@ * a case, be careful and avoid concurrent execution of the mentioned setters/getters and functions * creating connection (no matter if it is a standard NETCONF connection or Call Home). * - * In the client, it is thread-safe to work with distinguish NETCONF sessions since the client - * settings are thread-specific as described above. + * In the client, it is always thread-safe to work with a NETCONF session in a single thread since the client + * settings are thread-specific as described above. Generally, one can access a session in several threads + * as well but there is little incentive to do so. * * Server * ------ @@ -148,10 +120,6 @@ * * - ::nc_server_init() * - ::nc_server_destroy() - * - * Available in both __nc_client.h__ and __nc_server.h__. - * - * - ::nc_thread_destroy() */ /** @@ -196,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() @@ -240,8 +206,6 @@ * - ::nc_client_tls_get_cert_key_paths() * - ::nc_client_tls_set_trusted_ca_paths() * - ::nc_client_tls_get_trusted_ca_paths() - * - ::nc_client_tls_set_crl_paths() - * - ::nc_client_tls_get_crl_paths() * * - ::nc_connect_tls() * - ::nc_connect_libssl() @@ -279,7 +243,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() @@ -300,8 +263,6 @@ * - ::nc_client_tls_ch_get_cert_key_paths() * - ::nc_client_tls_ch_set_trusted_ca_paths() * - ::nc_client_tls_ch_get_trusted_ca_paths() - * - ::nc_client_tls_ch_set_crl_paths() - * - ::nc_client_tls_ch_get_crl_paths() * * - ::nc_accept_callhome() * @@ -319,14 +280,10 @@ * Init * ==== * - * Server takes an argument for its [initialization function](@ref howtoinit). - * In it, you set the server context, which determines what modules it - * supports and what capabilities to advertise. Few capabilities that + * Server must start with [initialization](@ref howtoinit). Its capabilities are + * 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, @@ -337,13 +294,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 * -------------- @@ -352,46 +303,141 @@ * * - ::nc_server_set_capab_withdefaults() * - ::nc_server_set_capability() - * - ::nc_server_set_hello_timeout() - * - ::nc_server_set_idle_timeout() + * - ::nc_server_endpt_count() + * - ::nc_server_add_endpt_unix_socket_listen() + * - ::nc_server_del_endpt_unix_socket() * - * - ::nc_server_add_endpt() - * - ::nc_server_del_endpt() - * - ::nc_server_endpt_set_address() - * - ::nc_server_endpt_set_port() - * - ::nc_server_endpt_set_perms() - * - * - * SSH + * Server Configuration * === * - * 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 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. * - * There are also some other optional settings. Note that authorized - * public keys are set for the server as a whole, not endpoint-specifically. + * 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. + * 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_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_config_load_modules() + * - ::nc_server_config_setup_diff() + * - ::nc_server_config_setup_data() + * - ::nc_server_config_setup_path() * - * - ::nc_server_ssh_set_hostkey_clb() + * - ::nc_server_config_add_address_port() + * - ::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() * - * - ::nc_server_ssh_add_authkey() - * - ::nc_server_ssh_add_authkey_path() - * - ::nc_server_ssh_del_authkey() + * SSH + * === * + * 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(). + * + * It is important to decide whether the users that can connect to the SSH server should be obtained from the configuration or from the system. + * If the YANG feature *local-users-supported* is enabled (the default), then the authorized users are derived from the configuration. + * When a client connects to the server, he must be found in the configuration and he must authenticate to **all** of his configured authentication methods. + * If the feature is disabled, then the system will be used to try to authenticate the client via one of the three + * methods - publickey, keyboard-interactive or password (only one of them has to succeed). + * + * If the local users are supported then 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()). + * + * \anchor ln2doc_pubkey + * The Public Key authentication method is supported. If you wish to use this method, you need to specify the given user's + * public keys, which will be compared with the key(s) presented by the SSH client when authenticating. One option is to configure + * the public keys directly in the ietf-netconf-server YANG data (inline-definition). Other option is to configure the keys' data + * in the ietf-trustore module's YANG data and then reference them (truststore-reference). The final option is to set the global + * path to file with public keys. This path may contain special tokens, see ::nc_server_ssh_set_authkey_path_format(). + * If the path is set and the use-system-keys container is present in the data for the client wishing to authenticate, + * then the keys from the file will be used for authentication. If the YANG feature *local-users-supported* is disabled, + * then it's neccessary to set the path format using ::nc_server_ssh_set_authkey_path_format(). + * + * \anchor ln2doc_kbdint + * The Keyboard Interactive authentication method is also supported. It can be done in three ways. + * If libpam is found, Linux PAM is used to handle the authentication. You need to specify the service name using ::nc_server_ssh_set_pam_conf_filename(). + * Else if the standard functions for accessing local users are found on the system, they are used. The only Keyboard Interactive challenge will be the given + * user's password (that is if he's found on the system). + * Either way, you can always define your own callback to perform the authentication, see ::nc_server_ssh_set_interactive_auth_clb(). + * The callback has a higher priority than the other two methods. + * + * There are also some other optional settings. + * + * Functions List + * -------------- + * + * Available in __nc_server.h__. + * + * - ::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() + * + * - ::nc_server_ssh_set_authkey_path_format() + * - ::nc_server_ssh_set_pam_conf_filename() + * - ::nc_server_ssh_set_interactive_auth_clb() * * TLS * === @@ -399,47 +445,53 @@ * 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. These lists + * are downloaded from the URIs specified in the x509 CRLDistributionPoints extensions. + * Be mindful that if any CRL is successfully downloaded and set, then at least one of them has to belong + * to the peer (e.g. the client) certificate (in other words it has to be issued by peer's CA). * * 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_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_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_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 * == @@ -461,54 +513,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_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_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_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 * ====================== @@ -588,6 +650,13 @@ * this request with ::nc_ps_accept_ssh_channel() or ::nc_session_accept_ssh_channel() * depending on the structure you want to use as the argument. * + * The server-side notifications are also supported. You can create a new notification + * with ::nc_server_notif_new() and send it via ::nc_server_notif_send() to subscribed clients. + * Keep in mind that the session you wish to send a notification on has to have at least one + * subscriber, see ::nc_session_inc_notif_status(). + * Currently, only notifications about certificate expiration are implemented, + * see ::nc_server_notif_cert_expiration_thread_start(). + * * Functions List * -------------- * @@ -603,6 +672,18 @@ * - ::nc_ps_clear() * - ::nc_ps_accept_ssh_channel() * - ::nc_session_accept_ssh_channel() + * + * - ::nc_server_notif_new() + * - ::nc_server_notif_send() + * - ::nc_server_notif_free() + * - ::nc_server_notif_get_time() + + * - ::nc_session_inc_notif_status() + * - ::nc_session_dec_notif_status() + * - ::nc_session_get_notif_status() + + * - ::nc_server_notif_cert_expiration_thread_start() + * - ::nc_server_notif_cert_expiration_thread_stop() */ /** @@ -610,7 +691,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. @@ -621,8 +702,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 @@ -632,23 +713,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. */ /** @@ -664,6 +732,6 @@ /** * @defgroup server Server * @brief NETCONF server functionality. + * @{ + * @} Server */ - -#endif /* NC_LIBNETCONF_H_ */ diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt new file mode 100644 index 0000000..012e73b --- /dev/null +++ b/examples/CMakeLists.txt @@ -0,0 +1,21 @@ +if(NOT LIBNETCONF2_VERSION) + message(FATAL_ERROR "Please use the root CMakeLists file instead.") +endif() + +# correct RPATH usage on OS X +set(CMAKE_MACOSX_RPATH TRUE) + +# include all the library headers +include_directories(BEFORE "${CMAKE_SOURCE_DIR}/src") + +# generate example header +include_directories(BEFORE ${CMAKE_CURRENT_BINARY_DIR}) +configure_file(example.h.in example.h) + +# list of all the examples +set(examples server client) + +foreach(app_name IN LISTS examples) + add_executable(${app_name} ${app_name}.c) + target_link_libraries(${app_name} netconf2) +endforeach(app_name) diff --git a/examples/README.md b/examples/README.md new file mode 100644 index 0000000..d79fdbf --- /dev/null +++ b/examples/README.md @@ -0,0 +1,134 @@ +# 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 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 two SSH endpoints - they differ in port and in how clients get authenticated. +You can modify this configuration in any way you want, however, configuring the server may fail if the configuration is not valid. + +## Example usage +### Server +First start the server: +``` +$ server -u ./example-socket +``` +The server will be started and configured per YANG data stored in the file `config.json`. +Two SSH endpoints with the addresses `127.0.0.1:10000` and `127.0.0.1:10001` will start listening for new connections. +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`). +The `-u` option specifies that a UNIX socket endpoint will be created and `./example-socket` is the path to where the socket will be listening. + +### 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']" +``` +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. +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 +``` +Listening for new connections! <-- server created +Connection established <-- client joined +Received RPC: + get-schema <-- name of the RPC + identifier = "ietf-datastores" <-- name of the requested YANG module + format = "ietf-netconf-monitoring:yang" <-- format of the requested YANG module +Received RPC: + get-schema + identifier = "ietf-netconf-nmda" + format = "ietf-netconf-monitoring:yang" +Received RPC: + get + filter = "(null)" <-- XPath filter has no value in the anyxml + type = "xpath" <-- defines XPath filter type (which may also be subtree) + select = "/ietf-yang-library:*" <-- contains a string representing the XPath filter +Received RPC: + get + filter = "(null)" + type = "xpath" + select = "/ietf-yang-library:yang-library/module-set/module[name='ietf-netconf']" +Received RPC: + close-session <-- communication with client terminated +``` +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 +``` + + + + + complete + + ietf-netconf <-- requested name of a module + 2013-09-29 + urn:ietf:params:xml:ns:netconf:base:1.0 + file:///home/roman/libnetconf2/modules/ietf-netconf@2013-09-29.yang + writable-running + candidate + confirmed-commit + rollback-on-error + validate + startup + url + xpath + + + + + + +``` +The client received a single `ietf-yang-library` module based on the used filter. + +#### SSH +After the server has been run, in another terminal instance, with the default configuration: +``` +$ client -p 10000 get-config startup +``` +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 `startup` is the source datastore for the retrieved data of the get-config RPC. + +##### Server output +``` +Using SSH! +Connection established +Received RPC: + get-schema + identifier = "ietf-datastores" + format = "ietf-netconf-monitoring:yang" +Received RPC: + get-schema + identifier = "ietf-netconf-nmda" + format = "ietf-netconf-monitoring:yang" +Received RPC: + get + filter = "(null)" + type = "xpath" + select = "/ietf-yang-library:*" +Received RPC: + get-config <-- name of the RPC + candidate = "" <-- source datastore, which is of type empty +Received RPC: + close-session +``` + +##### Client output +``` +admin@127.0.0.1 password: <-- prompts for password, type in 'admin' + + + +``` +No `startup` configuration is returned, because the example server lacks this functionality. +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: +``` +$ client -p 10001 -P ~/libnetconf2/examples/admin_key.pub -i ~/libnetconf2/examples/admin_key get +``` diff --git a/examples/admin_key b/examples/admin_key new file mode 100644 index 0000000..df71976 --- /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 0000000..533f33f --- /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 new file mode 100644 index 0000000..8295fb7 --- /dev/null +++ b/examples/client.c @@ -0,0 +1,266 @@ +/** + * @file client.c + * @author Roman Janota + * @brief libnetconf2 client example + * + * @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 + */ + +#include "example.h" + +#include +#include +#include +#include +#include +#include + +#include + +#include "log.h" +#include "messages_client.h" +#include "netconf.h" +#include "session_client.h" +#include "session_client_ch.h" + +static void +help_print() +{ + printf("Example usage:\n" + " client get\n" + "\n" + " Available options:\n" + " -h, --help\t \tPrint usage help.\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"); +} + +static enum NC_DATASTORE_TYPE +string2datastore(const char *str) +{ + if (!str) { + return NC_DATASTORE_RUNNING; + } + + if (!strcmp(str, "candidate")) { + return NC_DATASTORE_CANDIDATE; + } else if (!strcmp(str, "running")) { + return NC_DATASTORE_RUNNING; + } else if (!strcmp(str, "startup")) { + return NC_DATASTORE_STARTUP; + } else { + return 0; + } +} + +static int +send_rpc(struct nc_session *session, NC_RPC_TYPE rpc_type, const char *param1, const char *param2) +{ + enum NC_DATASTORE_TYPE datastore; + int r = 0, rc = 0; + uint64_t msg_id = 0; + struct lyd_node *envp = NULL, *op = NULL; + struct nc_rpc *rpc = NULL; + + /* decide which type of RPC to send */ + switch (rpc_type) { + case NC_RPC_GET: + /* create get RPC with an optional filter */ + rpc = nc_rpc_get(param1, NC_WD_UNKNOWN, NC_PARAMTYPE_CONST); + break; + + case NC_RPC_GETCONFIG: + /* create get-config RPC with a source datastore and an optional filter */ + datastore = string2datastore(param1); + if (!datastore) { + ERR_MSG_CLEANUP("Invalid name of a datastore. Use candidate, running, startup or neither.\n"); + } + rpc = nc_rpc_getconfig(datastore, param2, NC_WD_UNKNOWN, NC_PARAMTYPE_CONST); + break; + + default: + break; + } + if (!rpc) { + ERR_MSG_CLEANUP("Error while creating a RPC\n"); + } + + /* send the RPC on the session and remember NETCONF message ID */ + r = nc_send_rpc(session, rpc, 100, &msg_id); + if (r != NC_MSG_RPC) { + ERR_MSG_CLEANUP("Couldn't send a RPC\n"); + } + + /* receive the server's reply with the expected message ID + * as separate rpc-reply NETCONF envelopes and the parsed YANG output itself, if any */ + r = nc_recv_reply(session, rpc, msg_id, 100, &envp, &op); + if (r != NC_MSG_REPLY) { + ERR_MSG_CLEANUP("Couldn't receive a reply from the server\n"); + } + + /* print the whole reply */ + if (!op) { + r = lyd_print_file(stdout, envp, LYD_XML, 0); + } else { + r = lyd_print_file(stdout, op, LYD_XML, 0); + if (r) { + ERR_MSG_CLEANUP("Couldn't print the RPC to stdout\n"); + } + r = lyd_print_file(stdout, envp, LYD_XML, 0); + } + if (r) { + ERR_MSG_CLEANUP("Couldn't print the RPC to stdout\n"); + } + +cleanup: + lyd_free_all(envp); + lyd_free_all(op); + nc_rpc_free(rpc); + return rc; +} + +int +main(int argc, char **argv) +{ + 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'}, + {"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) { + help_print(); + goto cleanup; + } + + /* set the path to search for schemas */ + nc_client_set_schema_searchpath(MODULES_DIR); + + opterr = 0; + + 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; + break; + + 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: + ERR_MSG_CLEANUP("Invalid option or missing argument\n"); + } + } + + if (optind == argc) { + 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 */ + if (unix_socket_path) { + /* it's UNIX socket */ + session = nc_connect_unix(unix_socket_path, NULL); + } 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"); + } + + 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"); + } + } + + /* 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"); + } + + /* sending a get RPC */ + if (!strcmp(argv[optind], "get")) { + if (optind + 1 < argc) { + /* use the specified XPath filter */ + rpc_parameter_1 = argv[optind + 1]; + } + if (send_rpc(session, NC_RPC_GET, rpc_parameter_1, rpc_parameter_2)) { + rc = 1; + goto cleanup; + } + /* sending a get-config RPC */ + } else if (!strcmp(argv[optind], "get-config")) { + /* use the specified datastore and optional XPath filter */ + if (optind + 2 < argc) { + rpc_parameter_1 = argv[optind + 1]; + rpc_parameter_2 = argv[optind + 2]; + } else if (optind + 1 < argc) { + rpc_parameter_1 = argv[optind + 1]; + } + if (send_rpc(session, NC_RPC_GETCONFIG, rpc_parameter_1, rpc_parameter_2)) { + rc = 1; + goto cleanup; + } + } else { + ERR_MSG_CLEANUP("Invalid name of a RPC\n"); + } + +cleanup: + nc_session_free(session, NULL); + nc_client_destroy(); + return rc; +} diff --git a/examples/config.json b/examples/config.json new file mode 100644 index 0000000..165579b --- /dev/null +++ b/examples/config.json @@ -0,0 +1,93 @@ +{ + "ietf-netconf-server:netconf-server": { + "listen": { + "idle-timeout": 10, + "endpoints": { + "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" + } + ] + } + } + } + ] + } + } + } + } + } + ] + } + } + } +} diff --git a/examples/example.h.in b/examples/example.h.in new file mode 100644 index 0000000..1a97f17 --- /dev/null +++ b/examples/example.h.in @@ -0,0 +1,41 @@ +/** + * @file example.h + * @author Roman Janota + * @brief libnetconf2 example header + * + * @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 + */ + +#ifndef _EXAMPLE_H_ +#define _EXAMPLE_H_ + +#include + +/* 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 'password' authentication exptected username and password */ +#define SSH_USERNAME "admin" + +/* time in microseconds to sleep for if there are no new RPCs and no new sessions */ +#define BACKOFF_TIMEOUT_USECS 100 + +#define ERR_MSG_CLEANUP(msg) \ + rc = 1; \ + fprintf(stderr, "%s", msg); \ + goto cleanup + +#endif diff --git a/examples/server.c b/examples/server.c new file mode 100644 index 0000000..83e71c7 --- /dev/null +++ b/examples/server.c @@ -0,0 +1,380 @@ +/** + * @file server.c + * @author Roman Janota + * @brief libnetconf2 server example + * + * @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 "example.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#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) +{ + (void) signum; + /* notify the main loop if we should exit */ + exit_application = 1; +} + +static struct nc_server_reply * +get_rpc(struct lyd_node *rpc, struct nc_session *session) +{ + const struct ly_ctx *ctx; + const char *xpath; + struct lyd_node *root = NULL, *root2 = NULL, *duplicate = NULL; + 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); + + /* load the ietf-yang-library data of the session, which represent this server's state data */ + if (ly_ctx_get_yanglib_data(ctx, &root, "%u", ly_ctx_get_change_count(ctx))) { + err = nc_err(ctx, NC_ERR_OP_FAILED, NC_ERR_TYPE_APP); + goto error; + } + + /* search for the optional filter in the RPC */ + 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; + } + + if (filter) { + /* look for the expected filter attributes type and select */ + LY_LIST_FOR(filter->meta, m) { + if (!strcmp(m->name, "type")) { + type = m; + } + if (!strcmp(m->name, "select")) { + select = m; + } + } + + /* only XPath filter is supported */ + if (!type || strcmp(lyd_get_meta_value(type), "xpath") || !select) { + err = nc_err(ctx, NC_ERR_OP_NOT_SUPPORTED, NC_ERR_TYPE_APP); + goto error; + } + xpath = lyd_get_meta_value(select); + + /* find all the subtrees matching the filter */ + if (lyd_find_xpath(root, xpath, &set)) { + err = nc_err(ctx, NC_ERR_OP_FAILED, NC_ERR_TYPE_APP); + goto error; + } + + root2 = NULL; + for (uint32_t i = 0; i < set->count; i++) { + /* create a copy of the subtree with its parent nodes */ + if (lyd_dup_single(set->dnodes[i], NULL, LYD_DUP_RECURSIVE | LYD_DUP_WITH_PARENTS, &duplicate)) { + err = nc_err(ctx, NC_ERR_OP_FAILED, NC_ERR_TYPE_APP); + goto error; + } + + /* merge another top-level filtered subtree into the result */ + while (duplicate->parent) { + duplicate = lyd_parent(duplicate); + } + if (lyd_merge_tree(&root2, duplicate, LYD_MERGE_DESTRUCT)) { + err = nc_err(ctx, NC_ERR_OP_FAILED, NC_ERR_TYPE_APP); + goto error; + } + duplicate = NULL; + } + + /* replace the original full data with only the filtered data */ + lyd_free_siblings(root); + root = root2; + root2 = NULL; + } + + /* duplicate the rpc node without its input nodes so the output nodes can be appended */ + if (lyd_dup_single(rpc, NULL, 0, &duplicate)) { + err = nc_err(ctx, NC_ERR_OP_FAILED, NC_ERR_TYPE_APP); + goto error; + } + + /* create the get RPC anyxml "data" output node with the requested data */ + if (lyd_new_any(duplicate, NULL, "data", root, LYD_ANYDATA_DATATREE, LYD_NEW_ANY_USE_VALUE | LYD_NEW_VAL_OUTPUT, NULL)) { + err = nc_err(ctx, NC_ERR_OP_FAILED, NC_ERR_TYPE_APP); + goto error; + } + + ly_set_free(set, NULL); + + /* send data reply with the RPC output data */ + return nc_server_reply_data(duplicate, NC_WD_UNKNOWN, NC_PARAMTYPE_FREE); + +error: + ly_set_free(set, NULL); + lyd_free_siblings(root); + lyd_free_siblings(duplicate); + lyd_free_siblings(root2); + + /* send error reply with the specific NETCONF error */ + return nc_server_reply_err(err); +} + +static struct nc_server_reply * +glob_rpc(struct lyd_node *rpc, struct nc_session *session) +{ + struct lyd_node *iter; + struct lyd_meta *m; + + printf("Received RPC:\n"); + + /* iterate over all the nodes in the RPC */ + LYD_TREE_DFS_BEGIN(rpc, iter) { + /* if the node has a value, then print its name and value */ + if (iter->schema->nodetype & (LYD_NODE_TERM | LYD_NODE_ANY)) { + printf(" %s = \"%s\"\n", LYD_NAME(iter), lyd_get_value(iter)); + /* then iterate through all the metadata, which may include the XPath filter */ + LY_LIST_FOR(iter->meta, m) { + printf(" %s = \"%s\"\n", m->name, lyd_get_meta_value(m)); + } + /* else print just the name */ + } else if (iter->schema->nodetype == LYS_RPC) { + printf(" %s\n", LYD_NAME(iter)); + } + + LYD_TREE_DFS_END(rpc, iter); + } + + /* if close-session RPC is received, then call library's default function to properly close the session */ + if (!strcmp(LYD_NAME(rpc), "close-session") && !strcmp(lyd_owner_module(rpc)->name, "ietf-netconf")) { + return nc_clb_default_close_session(rpc, session); + } + + /* if get-schema RPC is received, then use the library implementation of this RPC */ + if (!strcmp(LYD_NAME(rpc), "get-schema") && !strcmp(lyd_owner_module(rpc)->name, "ietf-netconf-monitoring")) { + return nc_clb_default_get_schema(rpc, session); + } + + if (!strcmp(LYD_NAME(rpc), "get") && !strcmp(lyd_owner_module(rpc)->name, "ietf-netconf")) { + return get_rpc(rpc, session); + } + + /* return an okay reply to every other RPC */ + return nc_server_reply_ok(); +} + +static void +help_print() +{ + printf("Example usage:\n" + " server -u ./unix_socket\n" + "\n" + " Available options:\n" + " -h, --help\t \tPrint usage help.\n" + " -u, --unix\t\tCreate a UNIX socket endpoint at the place specified by .\n\n"); +} + +static int +init(const char *unix_socket_path, struct ly_ctx **context, struct nc_pollsession **ps) +{ + int rc = 0; + struct lyd_node *config = NULL; + + /* 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"); + } + + /* implement the base NETCONF modules */ + rc = nc_server_init_ctx(context); + if (rc) { + ERR_MSG_CLEANUP("Error while initializing 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"); + } + + /* 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"); + } + + /* initialize the server */ + if (nc_server_init()) { + ERR_MSG_CLEANUP("Error occurred while initializing the server.\n"); + } + + /* create unix socket endpoint if path was set */ + if (unix_socket_path) { + rc = nc_server_add_endpt_unix_socket_listen("unix-socket-endpt", unix_socket_path, -1, -1, -1); + if (rc) { + ERR_MSG_CLEANUP("Creating UNIX socket endpoint failed.\n"); + } + } + + /* create a new poll session structure, which is used for polling RPCs sent by clients */ + *ps = nc_ps_new(); + if (!*ps) { + ERR_MSG_CLEANUP("Couldn't create a poll session\n"); + } + + /* set the global RPC callback, which is called every time a new RPC is received */ + nc_set_global_rpc_clb(glob_rpc); + + /* upon receiving SIGINT the handler will notify the program that is should terminate */ + signal(SIGINT, sigint_handler); + +cleanup: + lyd_free_all(config); + return rc; +} + +int +main(int argc, char **argv) +{ + int r, opt, no_new_sessions, rc = 0; + struct ly_ctx *context = NULL; + struct nc_session *session, *new_session; + struct nc_pollsession *ps = NULL; + const char *unix_socket_path = NULL; + + struct option options[] = { + {"help", no_argument, NULL, 'h'}, + {"debug", no_argument, NULL, 'd'}, + {"unix", required_argument, NULL, 'u'}, + {NULL, 0, NULL, 0} + }; + + opterr = 0; + + while ((opt = getopt_long(argc, argv, "hdu:", options, NULL)) != -1) { + switch (opt) { + case 'h': + help_print(); + goto cleanup; + + case 'd': + nc_verbosity(NC_VERB_DEBUG); + break; + + case 'u': + unix_socket_path = optarg; + break; + + default: + ERR_MSG_CLEANUP("Invalid option or missing argument\n"); + } + } + + /* initialize the server */ + r = init(unix_socket_path, &context, &ps); + if (r) { + ERR_MSG_CLEANUP("Initializing the server failed."); + } + + printf("Listening for new connections!\n"); + + while (!exit_application) { + no_new_sessions = 0; + + /* try to accept new NETCONF sessions on all configured endpoints */ + r = nc_accept(0, context, &session); + + switch (r) { + + /* session accepted and its hello message received */ + case NC_MSG_HELLO: + printf("Connection established\n"); + + /* add the new session to the poll structure */ + if (nc_ps_add_session(ps, session)) { + ERR_MSG_CLEANUP("Couldn't add session to poll\n"); + } + break; + + /* there were no new sessions */ + case NC_MSG_WOULDBLOCK: + no_new_sessions = 1; + break; + + /* session accepted, but its hello message was invalid */ + case NC_MSG_BAD_HELLO: + printf("Parsing client hello message error.\n"); + break; + + /* something else went wrong */ + case NC_MSG_ERROR: + /* accepting a session failed, but the server should continue handling RPCs on established sessions */ + printf("Error while accepting a hello message.\n"); + rc = 1; + break; + } + + /* poll all the sessions in the structure and process a single event on a session which is then returned, + * in case it is a new RPC then the global RPC callback is also called */ + r = nc_ps_poll(ps, 0, &new_session); + + /* a fatal error occurred */ + if (r & NC_PSPOLL_ERROR) { + ERR_MSG_CLEANUP("Error polling RPCs\n"); + } + + /* a session was terminated, so remove it from the ps structure and free it */ + if (r & NC_PSPOLL_SESSION_TERM) { + r = nc_ps_del_session(ps, new_session); + assert(!r); + nc_session_free(new_session, NULL); + } + + /* there were no new sessions and no new events on any established sessions, + * prevent active waiting by sleeping for a short period of time */ + if (no_new_sessions && (r & (NC_PSPOLL_TIMEOUT | NC_PSPOLL_NOSESSIONS))) { + usleep(BACKOFF_TIMEOUT_USECS); + } + + /* other set bits of the return value of nc_ps_poll() are not interesting in this example */ + } + +cleanup: + /* free all the remaining sessions in the ps structure before destroying the context */ + if (ps) { + nc_ps_clear(ps, 1, NULL); + } + 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 66da469..10926a5 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 0000000..eaf6258 --- /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 0000000..516396b --- /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 0000000..73b1895 --- /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 0000000..9cf3ae0 --- /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 0000000..ae25bbc --- /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 0000000..2b914d8 --- /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-12-28.yang b/modules/ietf-crypto-types@2023-12-28.yang new file mode 100644 index 0000000..38dc215 --- /dev/null +++ b/modules/ietf-crypto-types@2023-12-28.yang @@ -0,0 +1,1091 @@ +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-12-28 { + 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; + } + mandatory true; + 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; + mandatory true; + description + "The binary value of the public key. The interpretation + of the value is defined by 'public-key-format' field."; + } + } + + grouping private-key-grouping { + description + "A private key."; + 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 asymmetric-key-pair-grouping { + description + "A private key and, optionally, its associated public key. + Implementations SHOULD ensure that the two keys, when both + are specified, are a matching pair."; + uses public-key-grouping { + refine public-key-format { + mandatory false; + } + refine public-key { + mandatory false; + } + } + uses private-key-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-12-28.yang b/modules/ietf-keystore@2023-12-28.yang new file mode 100644 index 0000000..b492c77 --- /dev/null +++ b/modules/ietf-keystore@2023-12-28.yang @@ -0,0 +1,407 @@ +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-12-28 { + 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 central keystore (i.e., fully + 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 central keystore."; + } + + 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 central keystore."; + } + + /*****************/ + /* Groupings */ + /*****************/ + + grouping encrypted-by-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 central keystore."; + choice encrypted-by { + 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."; + } + } + } + } + + // *-ref groupings + + grouping asymmetric-key-certificate-ref-grouping { + description + "Grouping for the reference to a certificate associated + with an asymmetric key stored in the central keystore."; + 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 for the configuration of a symmetric key. The + symmetric key may be defined inline or as a reference to + a symmetric key stored in the central keystore. + + Servers that do not define the 'central-keystore-supported' + feature SHOULD augment in custom 'case' statements enabling + references to 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 central-keystore { + if-feature "central-keystore-supported"; + if-feature "symmetric-keys"; + leaf central-keystore-reference { + type ks:symmetric-key-ref; + description + "A reference to an symmetric key that exists in + the central keystore."; + } + } + } + } + + grouping inline-or-keystore-asymmetric-key-grouping { + description + "A grouping for the configuration of an asymmetric key. The + asymmetric key may be defined inline or as a reference to + an asymmetric key stored in the central keystore. + + Servers that do not define the 'central-keystore-supported' + feature SHOULD augment in custom 'case' statements enabling + references to 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 central-keystore { + if-feature "central-keystore-supported"; + if-feature "asymmetric-keys"; + leaf central-keystore-reference { + type ks:asymmetric-key-ref; + description + "A reference to an asymmetric key that exists in + the central keystore. 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 for the configuration of an asymmetric key and + its associated certificates. The asymmetric key and its + associated certificates may be defined inline or as a + reference to an asymmetric key (and its associated + certificates) in the central keystore. + + Servers that do not define the 'central-keystore-supported' + feature SHOULD augment in custom 'case' statements enabling + references to 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 central-keystore { + if-feature "central-keystore-supported"; + if-feature "asymmetric-keys"; + leaf central-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 for the configuration of an asymmetric key and + its associated end-entity certificate. The asymmetric key + and its associated end-entity certificate may be defined + inline or as a reference to an asymmetric key (and its + associated end-entity certificate) in the central keystore. + + Servers that do not define the 'central-keystore-supported' + feature SHOULD augment in custom 'case' statements enabling + references to 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 central-keystore { + if-feature "central-keystore-supported"; + if-feature "asymmetric-keys"; + container central-keystore-reference { + uses asymmetric-key-certificate-ref-grouping; + description + "A reference to a specific certificate associated with + an asymmetric key stored in the central keystore."; + } + } + } + } + + // the keystore grouping + + 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-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-grouping; + } + } + } +} diff --git a/modules/ietf-netconf-server@2023-12-28.yang b/modules/ietf-netconf-server@2023-12-28.yang new file mode 100644 index 0000000..f6f7eed --- /dev/null +++ b/modules/ietf-netconf-server@2023-12-28.yang @@ -0,0 +1,685 @@ +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-12-28 { + 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 listening on a single port."; + choice transport { + mandatory true; + description + "Selects between available transports."; + case ssh { + if-feature "ssh-listen"; + container ssh { + description + "TCP, SSH, and NETCONF configuration to listen + for NETCONF over SSH connections."; + container tcp-server-parameters { + description + "TCP-level server parameters to listen + for NETCONF over SSH connections."; + 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 + "SSH-level server parameters to listen + for NETCONF over SSH connections."; + uses sshs:ssh-server-grouping; + } + container netconf-server-parameters { + description + "NETCONF-level server parameters to listen + for NETCONF over SSH connections."; + uses ncs:netconf-server-grouping { + refine "client-identity-mappings" { + if-feature "sshcmn:ssh-x509-certs"; + description + "Adds 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 + "TCP, TLS, and NETCONF configuration to listen + for NETCONF over TLS connections."; + container tcp-server-parameters { + description + "TCP-level server parameters to listen + for NETCONF over TLS connections."; + 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 + "TLS-level server parameters to listen + for NETCONF over TLS connections."; + 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 + "NETCONF-level server parameters to listen + for NETCONF over TLS connections."; + 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 outbound + connection."; + choice transport { + mandatory true; + description + "Selects between available transports."; + case ssh { + if-feature "ssh-call-home"; + container ssh { + description + "TCP, SSH, and NETCONF configuration to initiate + a NETCONF over SSH Call Home connection."; + container tcp-client-parameters { + description + "TCP-level client parameters to initiate a + NETCONF over SSH Call Home connection."; + 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 + "SSH-level server parameters to initiate a + NETCONF over SSH Call Home connection."; + uses sshs:ssh-server-grouping; + } + container netconf-server-parameters { + description + "NETCONF-level server parameters to initiate a + NETCONF over SSH Call Home connection."; + uses ncs:netconf-server-grouping { + refine "client-identity-mappings" { + if-feature "sshcmn:ssh-x509-certs"; + description + "Adds 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 + "TCP, TLS, and NETCONF configuration to initiate + a NETCONF over TLS Call Home connection."; + container tcp-client-parameters { + description + "TCP-level client parameters to initiate a + NETCONF over TLS Call Home connection."; + 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 + "TLS-level server parameters to initiate a + NETCONF over TLS Call Home connection."; + 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 + "NETCONF-level server parameters to initiate a + NETCONF over TLS Call Home connection."; + 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."; + } + container endpoints { + description + "Container for a list of endpoints."; + 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-12-28.yang b/modules/ietf-ssh-common@2023-12-28.yang new file mode 100644 index 0000000..968fd69 --- /dev/null +++ b/modules/ietf-ssh-common@2023-12-28.yang @@ -0,0 +1,261 @@ +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-12-28 { + 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 num-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 + 'num-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 + thus the 'num-bits' value is not specified."; + reference + "FIPS 186-6: Digital Signature Standard (DSS)"; + } + container private-key-encoding { + description + "Indicates how the private key is to be encoded."; + choice private-key-encoding { + mandatory true; + 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 encrypted { + if-feature "ct:encrypted-private-keys"; + container encrypted { + description + "Indicates that the private key is to be encrypted + using the specified symmetric or asymmetric key."; + uses ks:encrypted-by-grouping; + } + } + case hidden { + if-feature "ct:hidden-private-keys"; + leaf hidden { + 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-12-28.yang b/modules/ietf-ssh-server@2023-12-28.yang new file mode 100644 index 0000000..ecbb47a --- /dev/null +++ b/modules/ietf-ssh-server@2023-12-28.yang @@ -0,0 +1,425 @@ +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-12-28 { + 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 allow 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 'not(public-key-format) or derived-from-or-self' + + '(public-key-format, "ct:ssh-public-key-format")'; + + } + refine "inline-or-keystore/central-keystore/" + + "central-keystore-reference" { + must 'not(deref(.)/../ks:public-key-format) or ' + + '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 'not(public-key-format) or derived-from-or-self' + + '(public-key-format, "ct:subject-public-key-' + + 'info-format")'; + } + refine "inline-or-keystore/central-keystore/" + + "central-keystore-reference/asymmetric-key" { + must 'not(deref(.)/../ks:public-key-format) or ' + + '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/central-truststore/" + + "central-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/central-truststore/" + + "central-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."; + } + } + } // users + 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-12-28.yang b/modules/ietf-tcp-client@2023-12-28.yang new file mode 100644 index 0000000..567fb5d --- /dev/null +++ b/modules/ietf-tcp-client@2023-12-28.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-12-28 { + 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 { + refine "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-12-28.yang b/modules/ietf-tcp-common@2023-12-28.yang new file mode 100644 index 0000000..f9a291e --- /dev/null +++ b/modules/ietf-tcp-common@2023-12-28.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 define a reusable 'grouping' that is common + to both TCP-clients and TCP-servers. This grouping statement + is used by both the 'ietf-tcp-client' and 'ietf-tcp-server' + modules. + + 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-12-28 { + 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"; + 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. Further guidance can be found + in Section 2.1.5 of RFC DDDD."; + reference + "RFC 9293: + Transmission Control Protocol (TCP), Section 3.8.4.."; + leaf idle-time { + type uint16 { + range "1..max"; + } + units "seconds"; + default 7200; + 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"; + } + default 9; + 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"; + default 75; + 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-12-28.yang b/modules/ietf-tcp-server@2023-12-28.yang new file mode 100644 index 0000000..ee6c657 --- /dev/null +++ b/modules/ietf-tcp-server@2023-12-28.yang @@ -0,0 +1,116 @@ +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-12-28 { + 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 { + refine "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-12-28.yang b/modules/ietf-tls-common@2023-12-28.yang new file mode 100644 index 0000000..79107ac --- /dev/null +++ b/modules/ietf-tls-common@2023-12-28.yang @@ -0,0 +1,316 @@ +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-12-28 { + 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"; + } + + // Typedefs + + 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"; + } + + + // 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; + } + ordered-by user; + 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 num-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 'num-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 thus the 'num-bits' + value is not specified."; + } + container private-key-encoding { + description + "Indicates how the private key is to be encoded."; + choice private-key-encoding { + mandatory true; + 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 encrypted { + if-feature "ct:encrypted-private-keys"; + container encrypted { + description + "Indicates that the key is to be encrypted using + the specified symmetric or asymmetric key."; + uses ks:encrypted-by-grouping; + } + } + case hidden { + if-feature "ct:hidden-private-keys"; + leaf hidden { + 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-12-28.yang b/modules/ietf-tls-server@2023-12-28.yang new file mode 100644 index 0000000..4b33cf3 --- /dev/null +++ b/modules/ietf-tls-server@2023-12-28.yang @@ -0,0 +1,527 @@ +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-12-28 { + 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 { + if-feature "tlscmn:tls12"; + 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 { + if-feature "tlscmn:tls13"; + 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 'not(public-key-format) or derived-from-or-self' + + '(public-key-format,' + ' "ct:subject-public-' + + 'key-info-format")'; + } + refine "inline-or-keystore/central-keystore/" + + "central-keystore-reference/asymmetric-key" { + must 'not(deref(.)/../ks:public-key-format) or ' + + '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 'not(public-key-format) or derived-from-or-self' + + '(public-key-format,' + ' "ct:subject-public-' + + 'key-info-format")'; + } + refine "inline-or-keystore/central-keystore/" + + "central-keystore-reference" { + must 'not(deref(.)/../ks:public-key-format) or ' + + '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 Section 6 in + RFC 9257, and the EPSK input fields detailed in + Section 5.1 in RFC 9258. 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 + RFC 9257: Guidance for External Pre-Shared Key + (PSK) Usage in TLS + RFC 9258: Importing External Pre-Shared Keys + (PSKs) for TLS 1.3"; + 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 RFC 9257, 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 + RFC 9257: Guidance for External Pre-Shared Key + (PSK) Usage in TLS"; + } + leaf hash { + type tlscmn:epsk-supported-hash; + default sha-256; + 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 + "Per Section 5.1 of RFC 9258, context MUST include + the context used to determine the EPSK, if + any exists. For example, context may include + information about peer roles or identities + to mitigate Selfie-style reflection attacks. + Since the EPSK is a key derived from an external + protocol or sequence of protocols, context MUST + include a channel binding for the deriving + protocols [RFC5056]. The details of this + binding are protocol specfic and out of scope + for this document."; + reference + "RFC 9258: Importing External Pre-Shared Keys + (PSKs) for TLS 1.3"; + } + leaf target-protocol { + type uint16; + description + "As per Section 3.1 of RFC 9258, the protocol + for which a PSK is imported for use."; + reference + "RFC 9258: Importing External Pre-Shared Keys + (PSKs) for TLS 1.3"; + } + leaf target-kdf { + type uint16; + description + "As per Section 3 of RFC 9258, the KDF for + which a PSK is imported for use."; + reference + "RFC 9258: Importing External Pre-Shared Keys + (PSKs) for TLS 1.3"; + } + } + } + } + } // 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/central-truststore/" + + "central-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-12-28.yang b/modules/ietf-truststore@2023-12-28.yang new file mode 100644 index 0000000..540ea41 --- /dev/null +++ b/modules/ietf-truststore@2023-12-28.yang @@ -0,0 +1,391 @@ +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-12-28 { + 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 central truststore."; + } + + typedef certificate-ref { + type leafref { + path "/ts:truststore/ts:certificate-bags/ts:certificate-bag" + + "[ts:name = current()/../certificate-bag]/" + + "ts:certificate/ts:name"; + } + description + "This typedef defines a reference to a specific certificate + in a certificate bag in the central truststore. 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 central truststore."; + } + + typedef public-key-ref { + type leafref { + path "/ts:truststore/ts:public-key-bags/ts:public-key-bag" + + "[ts:name = current()/../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. This typedef + requires that there exist a sibling 'leaf' node called + 'public-key-bag' that SHOULD have the typedef + 'public-key-bag-ref'."; + } + + /*****************/ + /* Groupings */ + /*****************/ + + // *-ref groupings + + grouping certificate-ref-grouping { + description + "Grouping for the reference to a certificate in a + certificate-bag in the central truststore."; + leaf certificate-bag { + nacm:default-deny-write; + if-feature "central-truststore-supported"; + if-feature "certificates"; + type ts:certificate-bag-ref; + must "../certificate"; + description + "Reference to a certificate-bag in the truststore."; + } + leaf certificate { + nacm:default-deny-write; + type ts:certificate-ref; + must "../certificate-bag"; + description + "Reference to a specific certificate in the + referenced certificate-bag."; + } + } + + grouping public-key-ref-grouping { + description + "Grouping for the reference to a public key in a + public-key-bag in the central truststore."; + leaf public-key-bag { + nacm:default-deny-write; + if-feature "central-truststore-supported"; + if-feature "public-keys"; + type ts:public-key-bag-ref; + description + "Reference of a public key bag in the truststore inlucding + the certificate to authenticate the TLS client."; + } + leaf public-key { + nacm:default-deny-write; + type ts:public-key-ref; + description + "Reference to a specific public key in the + referenced public-key-bag."; + } + } + + // inline-or-truststore-* groupings + + grouping inline-or-truststore-certs-grouping { + description + "A grouping for the configuration of a list of certificates. + The list of certificate may be defined inline or as a + reference to a certificate bag in the central truststore. + + Servers that do not define the 'central-truststore-supported' + feature SHOULD augment in custom 'case' statements enabling + references to 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 central-truststore { + if-feature "central-truststore-supported"; + if-feature "certificates"; + leaf central-truststore-reference { + type ts:certificate-bag-ref; + description + "A reference to a certificate bag that exists in the + central truststore."; + } + } + } + } + + 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 define the 'central-truststore-supported' + feature SHOULD augment in custom 'case' statements enabling + references to 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 central-truststore { + if-feature "central-truststore-supported"; + if-feature "public-keys"; + leaf central-truststore-reference { + type ts:public-key-bag-ref; + description + "A reference to a bag of public keys that exists + in the central truststore."; + } + } + } + } + + + // the truststore grouping + + 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 0000000..53b5484 --- /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@2024-07-09.yang b/modules/libnetconf2-netconf-server@2024-07-09.yang new file mode 100644 index 0000000..01acb77 --- /dev/null +++ b/modules/libnetconf2-netconf-server@2024-07-09.yang @@ -0,0 +1,475 @@ +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 "2024-07-09" { + description "Second revision."; + } + + // Identities + +/* + 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"; + } + + // Typedefs + + typedef time-period { + type string { + pattern '(1[0-2]|[1-9])m|[1-4]w|[1-7]d|(2[0-4]|1[0-9]|[1-9])h'; + } + + description + "The time-period type allows to specify time in either months, weeks, days, or hours. + Its purpose is to create time intervals for the certificate expiration notifications."; + } + + // Groupings + + grouping ssh-authentication-params-grouping { + description + "Grouping for SSH authentication parameters."; + + leaf auth-timeout { + type uint16; + default 30; + units "seconds"; + description + "Represents the maximum amount of seconds an authentication can go on for."; + } + } + + grouping ssh-server-banner-grouping { + description + "Grouping for the SSH server banner."; + + leaf banner { + type string { + length "1..247"; + } + description + "The banner that will be sent to the client when connecting to the server. + If not set, the libnetconf2 default with its version will be used."; + + reference + "RFC 4253: The Secure Shell (SSH) Transport Layer Protocol, section 4.2."; + } + } + + grouping system-auth-public-keys-grouping { + description + "Grouping for using the system configured keys in the SSH public key authentication method."; + + container use-system-keys { + presence + "Indicates that the given user will be authenticated using the system's configured public keys."; + + description + "Authentication is done using the system's mechanisms."; + + reference + "libnetconf2 documentation: + Section SSH"; + } + } + + grouping keyboard-interactive-grouping { + description + "Grouping for the SSH Keyboard interactive authentication method."; + + container keyboard-interactive { + presence "Indicates that the given client supports the SSH Keyboard Interactive authentication method."; + description + "Keyboard interactive SSH authentication method."; + + reference + "RFC 4256: + Generic Message Exchange Authentication for + the Secure Shell Protocol (SSH)"; + + choice method { + mandatory true; + description + "Method to perform the authentication with."; + + container use-system-auth { + presence + "Indicates that the system will handle the authentication."; + + description + "Authentication is done using the system's mechanisms."; + + reference + "libnetconf2 documentation: + Section SSH"; + } + } + } + } + + grouping endpoint-reference-grouping { + description + "Grouping for the endpoint reference."; + + leaf endpoint-reference { + type leafref { + path "/ncs:netconf-server/ncs:listen/ncs:endpoints/ncs:endpoint/ncs:name"; + } + 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."; + } + } + + // Augments + + augment "/ncs:netconf-server/ncs:listen/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: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:endpoints/ncs:endpoint/ncs:transport/ncs:ssh" + + "/ncs:ssh/ncs:ssh-server-parameters/ncs:server-identity" { + uses ssh-server-banner-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:server-identity" { + uses ssh-authentication-params-grouping; + } + + augment "/ncs:netconf-server/ncs:listen/ncs:endpoints/ncs:endpoint/ncs:transport/ncs:ssh/ncs:ssh/ncs:ssh-server-parameters" + + "/ncs:client-authentication/ncs:users/ncs:user/ncs:public-keys/ncs:inline-or-truststore" { + case system-auth-public-keys { + uses system-auth-public-keys-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/ncs:public-keys/ncs:inline-or-truststore" { + case system-auth-public-keys { + uses system-auth-public-keys-grouping; + } + } + + augment "/ncs:netconf-server/ncs:listen/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: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: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: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: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: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; + } + + // Protocol-accessible Nodes + + container ln2-netconf-server { + container certificate-expiration-notif-intervals { + if-feature "ct:certificate-expiration-notification"; + + description + "Container for the certificate expiration notification intervals. + Its child nodes describe the ability to set the time intervals for the certificate + expiration notifications. These intervals are given in the form of an anchor and a period. + By default, these notifications are generated 3, 2, and 1 month; 2 weeks; 7, 6, 5, 4, 3, 2 and 1 day before a certificate expires. + Additionally, notifications are generated on the day of expiration and every day thereafter. + + Simplified example of YANG data that describe the default intervals: + + Anchor Period + 3m ... 1m + 2w ... 1w + 7d ... 1d + "; + + list interval { + key "anchor period"; + + leaf anchor { + type time-period; + + description + "The time anchor for the notification. The anchor is the time + before the certificate expiration when a notification will be sent. + It is essentially the lower bound of the given interval."; + } + leaf period { + type time-period; + + // Require the period to be smaller than the anchor (only units are checked for simplicity) + must "(contains(., 'm') and contains(../anchor, 'm')) or + (contains(., 'w') and (contains(../anchor, 'm') or contains(../anchor, 'w'))) or + (contains(., 'd') and (contains(../anchor, 'm') or contains(../anchor, 'w') or contains(../anchor, 'd'))) or + contains(., 'h')" { + error-message + "Certificate expiration notification period must be smaller than the anchor."; + } + + description + "The period of the notification. The period is the time + between two notifications within the given time interval."; + } + } + } + } +} diff --git a/nc_client.h.in b/nc_client.h.in index 40306e9..471b599 100644 --- a/nc_client.h.in +++ b/nc_client.h.in @@ -1,8 +1,9 @@ /** - * \file nc_client.h - * \author Radek Krejci - * \brief libnetconf2's main public header for NETCONF clients. + * @file nc_client.h + * @author Radek Krejci + * @brief libnetconf2's main public header for NETCONF clients. * + * @copyright * Copyright (c) 2015 - 2021 CESNET, z.s.p.o. * * This source code is licensed under BSD 3-Clause License (the "License"). @@ -15,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 859150c..a4ad8ca 100644 --- a/nc_server.h.in +++ b/nc_server.h.in @@ -1,8 +1,9 @@ /** - * \file nc_server.h - * \author Radek Krejci - * \brief libnetconf2's main public header for NETCONF servers. + * @file nc_server.h + * @author Radek Krejci + * @brief libnetconf2's main public header for NETCONF servers. * + * @copyright * Copyright (c) 2015 - 2021 CESNET, z.s.p.o. * * This source code is licensed under BSD 3-Clause License (the "License"). @@ -15,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/nc_version.h.in b/nc_version.h.in new file mode 100644 index 0000000..7d08b29 --- /dev/null +++ b/nc_version.h.in @@ -0,0 +1,32 @@ +/** + * @file nc_version.h + * @author Michal Vasko + * @brief libnetconf2 version information + * + * @copyright + * Copyright (c) 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_VERSION_H_ +#define NC_VERSION_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#define NC_VERSION_MAJOR @LIBNETCONF2_MAJOR_SOVERSION@ /**< libnetconf2 major version number */ +#define NC_VERSION_MINOR @LIBNETCONF2_MINOR_SOVERSION@ /**< libnetconf2 minor version number */ +#define NC_VERSION_MICRO @LIBNETCONF2_MICRO_SOVERSION@ /**< libnetconf2 micro version number */ +#define NC_VERSION "@LIBNETCONF2_SOVERSION_FULL@" /**< libnetconf2 version string */ + +#ifdef __cplusplus +} +#endif + +#endif /* NC_VERSION_H_ */ diff --git a/src/config.h.in b/src/config.h.in index 966050c..d366e0d 100644 --- a/src/config.h.in +++ b/src/config.h.in @@ -1,9 +1,11 @@ /** - * \file config.h - * \author Radek Krejci - * \brief libnetconf2 various configuration settings. + * @file config.h + * @author Radek Krejci + * @author Michal Vasko + * @brief libnetconf2 various configuration settings. * - * Copyright (c) 2015 - 2017 CESNET, z.s.p.o. + * @copyright + * Copyright (c) 2015 - 2024 CESNET, z.s.p.o. * * This source code is licensed under BSD 3-Clause License (the "License"). * You may not use this file except in compliance with the License. @@ -33,14 +35,29 @@ #cmakedefine HAVE_SHADOW /* - * Support for crypt.h + * Support for terminal in/out */ -#cmakedefine HAVE_CRYPT +#cmakedefine HAVE_TERMIOS /* - * Location of installed basic YANG modules on the system + * Support for keyboard-interactive SSH authentication method */ -#define NC_YANG_DIR "@YANG_MODULE_DIR@" +#cmakedefine HAVE_LIBPAM + +/* + * Use MbedTLS as TLS back-end + */ +#cmakedefine HAVE_MBEDTLS + +/* + * Location of installed YANG modules on the system + */ +#define NC_SERVER_SEARCH_DIR "@YANG_MODULE_DIR@" + +/* + * Location of installed YANG modules on the system + */ +#define NC_CLIENT_SEARCH_DIR "@CLIENT_SEARCH_DIR@" /* * Inactive read timeout diff --git a/src/io.c b/src/io.c index f2a6451..bf7a5b2 100644 --- a/src/io.c +++ b/src/io.c @@ -1,9 +1,11 @@ /** - * \file io.c - * \author Radek Krejci - * \brief libnetconf2 - input/output functions + * @file io.c + * @author Radek Krejci + * @author Michal Vasko + * @brief libnetconf2 - input/output functions * - * Copyright (c) 2015 CESNET, z.s.p.o. + * @copyright + * Copyright (c) 2015 - 2024 CESNET, z.s.p.o. * * This source code is licensed under BSD 3-Clause License (the "License"). * You may not use this file except in compliance with the License. @@ -13,27 +15,30 @@ */ #define _GNU_SOURCE /* asprintf, signals */ + #include #include #include -#include +#include #include -#include #include +#include #include #include #include #include #include -#ifdef NC_ENABLED_TLS -# include -#endif - #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" +#include "session_wrapper.h" const char *nc_msgtype2str[] = { "error", @@ -49,49 +54,13 @@ const char *nc_msgtype2str[] = { #define BUFFERSIZE 512 -#ifdef NC_ENABLED_TLS - -static char * -nc_ssl_error_get_reasons(void) -{ - unsigned int e; - int reason_size, reason_len; - char *reasons = NULL; - - reason_size = 1; - reason_len = 0; - while ((e = ERR_get_error())) { - if (reason_len) { - /* add "; " */ - reason_size += 2; - reasons = nc_realloc(reasons, reason_size); - if (!reasons) { - ERRMEM; - return 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; - } - reason_len += sprintf(reasons + reason_len, "%s", ERR_reason_error_string(e)); - } - - return reasons; -} - -#endif - static ssize_t -nc_read(struct nc_session *session, char *buf, size_t count, uint32_t inact_timeout, struct timespec *ts_act_timeout) +nc_read(struct nc_session *session, char *buf, uint32_t count, uint32_t inact_timeout, struct timespec *ts_act_timeout) { - size_t readd = 0; + uint32_t readd = 0; ssize_t r = -1; int fd, interrupted; - struct timespec ts_cur, ts_inact_timeout; + struct timespec ts_inact_timeout; assert(session); assert(buf); @@ -104,8 +73,7 @@ nc_read(struct nc_session *session, char *buf, size_t count, uint32_t inact_time return 0; } - nc_gettimespec_mono(&ts_inact_timeout); - nc_addtimespec(&ts_inact_timeout, inact_timeout); + nc_timeouttime_get(&ts_inact_timeout, inact_timeout); do { interrupted = 0; switch (session->ti_type) { @@ -139,8 +107,8 @@ nc_read(struct nc_session *session, char *buf, size_t count, uint32_t inact_time } break; -#ifdef NC_ENABLED_SSH - case NC_TI_LIBSSH: +#ifdef NC_ENABLED_SSH_TLS + case NC_TI_SSH: /* read via libssh */ r = ssh_channel_read(session->ti.libssh.channel, buf + readd, count - readd, 0); if (r == SSH_AGAIN) { @@ -161,48 +129,15 @@ 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(); - r = SSL_read(session->ti.tls, buf + readd, count - readd); - if (r <= 0) { - int e; - char *reasons; - - switch (e = SSL_get_error(session->ti.tls, r)) { - case SSL_ERROR_WANT_READ: - case SSL_ERROR_WANT_WRITE: - r = 0; - break; - case SSL_ERROR_ZERO_RETURN: - ERR(session, "Communication socket unexpectedly closed (OpenSSL)."); - session->status = NC_STATUS_INVALID; - session->term_reason = NC_SESSION_TERM_DROPPED; - return -1; - case SSL_ERROR_SYSCALL: - ERR(session, "SSL socket error (%s).", errno ? strerror(errno) : "unexpected EOF"); - session->status = NC_STATUS_INVALID; - session->term_reason = NC_SESSION_TERM_OTHER; - return -1; - case SSL_ERROR_SSL: - reasons = nc_ssl_error_get_reasons(); - ERR(session, "SSL error (%s).", reasons); - free(reasons); - session->status = NC_STATUS_INVALID; - session->term_reason = NC_SESSION_TERM_OTHER; - return -1; - default: - ERR(session, "Unknown SSL error occured (err code %d).", e); - session->status = NC_STATUS_INVALID; - session->term_reason = NC_SESSION_TERM_OTHER; - return -1; - } + case NC_TI_TLS: + r = nc_tls_read_wrap(session, (unsigned char *)buf + readd, count - readd); + if (r < 0) { + /* non-recoverable error */ + return r; } break; -#endif +#endif /* NC_ENABLED_SSH_TLS */ } if (r == 0) { @@ -210,9 +145,8 @@ nc_read(struct nc_session *session, char *buf, size_t count, uint32_t inact_time if (!interrupted) { usleep(NC_TIMEOUT_STEP); } - nc_gettimespec_mono(&ts_cur); - if ((nc_difftimespec(&ts_cur, &ts_inact_timeout) < 1) || (nc_difftimespec(&ts_cur, ts_act_timeout) < 1)) { - if (nc_difftimespec(&ts_cur, &ts_inact_timeout) < 1) { + if ((nc_timeouttime_cur_diff(&ts_inact_timeout) < 1) || (nc_timeouttime_cur_diff(ts_act_timeout) < 1)) { + if (nc_timeouttime_cur_diff(&ts_inact_timeout) < 1) { ERR(session, "Inactive read timeout elapsed."); } else { ERR(session, "Active read timeout elapsed."); @@ -226,8 +160,7 @@ nc_read(struct nc_session *session, char *buf, size_t count, uint32_t inact_time readd += r; /* reset inactive timeout */ - nc_gettimespec_mono(&ts_inact_timeout); - nc_addtimespec(&ts_inact_timeout, inact_timeout); + nc_timeouttime_get(&ts_inact_timeout, inact_timeout); } } while (readd < count); @@ -249,10 +182,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) { @@ -282,10 +212,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) { @@ -301,10 +228,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 */ @@ -362,8 +286,7 @@ nc_read_msg_io(struct nc_session *session, int io_timeout, struct ly_in **msg, i goto cleanup; } - nc_gettimespec_mono(&ts_act_timeout); - nc_addtimespec(&ts_act_timeout, NC_READ_ACT_TIMEOUT * 1000); + nc_timeouttime_get(&ts_act_timeout, NC_READ_ACT_TIMEOUT * 1000); if (!io_locked) { /* SESSION IO LOCK */ @@ -428,11 +351,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'; @@ -469,7 +388,6 @@ cleanup: static int nc_read_poll(struct nc_session *session, int io_timeout) { - sigset_t sigmask, origmask; int ret = -2; struct pollfd fds; @@ -479,8 +397,13 @@ nc_read_poll(struct nc_session *session, int io_timeout) } switch (session->ti_type) { -#ifdef NC_ENABLED_SSH - case NC_TI_LIBSSH: +#ifdef NC_ENABLED_SSH_TLS + case NC_TI_SSH: + if (io_timeout == -1) { + /* BUG libssh 0.11.0 replaces timeout -1 with 0 for non-blocking sessions */ + io_timeout = INT_MAX; + } + /* EINTR is handled, it resumes waiting */ ret = ssh_channel_poll_timeout(session->ti.libssh.channel, io_timeout, 0); if (ret == SSH_ERROR) { @@ -501,10 +424,8 @@ 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); + case NC_TI_TLS: + ret = nc_tls_get_num_pending_bytes_wrap(session->ti.tls.session); if (ret) { /* some buffered TLS data available */ ret = 1; @@ -512,8 +433,8 @@ nc_read_poll(struct nc_session *session, int io_timeout) break; } - fds.fd = SSL_get_fd(session->ti.tls); -#endif + fds.fd = nc_tls_get_fd_wrap(session); +#endif /* NC_ENABLED_SSH_TLS */ /* fallthrough */ case NC_TI_FD: case NC_TI_UNIX: @@ -526,11 +447,7 @@ nc_read_poll(struct nc_session *session, int io_timeout) fds.events = POLLIN; fds.revents = 0; - sigfillset(&sigmask); - pthread_sigmask(SIG_SETMASK, &sigmask, &origmask); - ret = poll(&fds, 1, io_timeout); - pthread_sigmask(SIG_SETMASK, &origmask, NULL); - + ret = nc_poll(&fds, 1, io_timeout); break; default: @@ -600,7 +517,7 @@ nc_read_msg_poll_io(struct nc_session *session, int io_timeout, struct ly_in **m /* does not really log, only fatal errors */ int -nc_session_is_connected(struct nc_session *session) +nc_session_is_connected(const struct nc_session *session) { int ret; struct pollfd fds; @@ -612,15 +529,13 @@ nc_session_is_connected(struct nc_session *session) case NC_TI_UNIX: fds.fd = session->ti.unixsock.sock; break; -#ifdef NC_ENABLED_SSH - case NC_TI_LIBSSH: +#ifdef NC_ENABLED_SSH_TLS + case NC_TI_SSH: 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); + case NC_TI_TLS: + fds.fd = nc_tls_get_fd_wrap(session); break; -#endif +#endif /* NC_ENABLED_SSH_TLS */ default: return 0; } @@ -632,11 +547,8 @@ nc_session_is_connected(struct nc_session *session) fds.events = POLLIN; fds.revents = 0; - errno = 0; - while (((ret = poll(&fds, 1, 0)) == -1) && (errno == EINTR)) {} - + ret = nc_poll(&fds, 1, 0); if (ret == -1) { - ERR(session, "poll failed (%s).", strerror(errno)); return 0; } else if ((ret > 0) && (fds.revents & (POLLHUP | POLLERR))) { return 0; @@ -646,21 +558,26 @@ nc_session_is_connected(struct nc_session *session) } #define WRITE_BUFSIZE (2 * BUFFERSIZE) -struct wclb_arg { +struct nc_wclb_arg { struct nc_session *session; char buf[WRITE_BUFSIZE]; - size_t len; + uint32_t len; }; +/** + * @brief Write to a NETCONF session. + * + * @param[in] session Session to write to. + * @param[in] buf Buffer to write. + * @param[in] count Count of bytes from @p buf to write. + * @return Number of bytes written. + * @return -1 on error. + */ static int -nc_write(struct nc_session *session, const void *buf, size_t count) +nc_write(struct nc_session *session, const void *buf, uint32_t count) { int c, fd, interrupted; - size_t written = 0; - -#ifdef NC_ENABLED_TLS - unsigned long e; -#endif + uint32_t written = 0; if ((session->status != NC_STATUS_RUNNING) && (session->status != NC_STATUS_STARTING)) { return -1; @@ -674,7 +591,7 @@ nc_write(struct nc_session *session, const void *buf, size_t count) return -1; } - DBG(session, "Sending message:\n%.*s\n", count, buf); + DBG(session, "Sending message:\n%.*s\n", (int)count, buf); do { interrupted = 0; @@ -689,13 +606,13 @@ nc_write(struct nc_session *session, const void *buf, size_t count) c = 0; interrupted = 1; } else if (c < 0) { - ERR(session, "socket error (%s).", strerror(errno)); + ERR(session, "Socket error (%s).", strerror(errno)); return -1; } break; -#ifdef NC_ENABLED_SSH - case NC_TI_LIBSSH: +#ifdef NC_ENABLED_SSH_TLS + case NC_TI_SSH: if (ssh_channel_is_closed(session->ti.libssh.channel) || ssh_channel_is_eof(session->ti.libssh.channel)) { if (ssh_channel_is_closed(session->ti.libssh.channel)) { ERR(session, "SSH channel unexpectedly closed."); @@ -712,36 +629,14 @@ 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) { - char *reasons; - - switch ((e = SSL_get_error(session->ti.tls, c))) { - case SSL_ERROR_ZERO_RETURN: - ERR(session, "SSL connection was properly closed."); - return -1; - case SSL_ERROR_WANT_WRITE: - case SSL_ERROR_WANT_READ: - c = 0; - break; - case SSL_ERROR_SYSCALL: - ERR(session, "SSL socket error (%s).", strerror(errno)); - return -1; - case SSL_ERROR_SSL: - reasons = nc_ssl_error_get_reasons(); - ERR(session, "SSL error (%s).", reasons); - free(reasons); - return -1; - default: - ERR(session, "Unknown SSL error occured (err code %d).", e); - return -1; - } + case NC_TI_TLS: + c = nc_tls_write_wrap(session, (const unsigned char *)(buf + written), count - written); + if (c < 0) { + /* possible client dc, or some socket/TLS communication error */ + return -1; } break; -#endif +#endif /* NC_ENABLED_SSH_TLS */ default: ERRINT; return -1; @@ -758,30 +653,47 @@ nc_write(struct nc_session *session, const void *buf, size_t count) return written; } +/** + * @brief Write the start tag and the message part of a chunked-framing NETCONF message. + * + * @param[in] session Session to write to. + * @param[in] buf Message buffer to write. + * @param[in] count Count of bytes from @p buf to write. + * @return Number of bytes written. + * @return -1 on error. + */ static int -nc_write_starttag_and_msg(struct nc_session *session, const void *buf, size_t count) +nc_write_starttag_and_msg(struct nc_session *session, const void *buf, uint32_t count) { - int ret = 0, c; + int ret = 0, r; char chunksize[24]; - // warning: ‘%zu’ directive writing between 4 and 20 bytes into a region of size 18 [-Wformat-overflow=] if (session->version == NC_VERSION_11) { - sprintf(chunksize, "\n#%zu\n", count); - ret = nc_write(session, chunksize, strlen(chunksize)); - if (ret == -1) { + r = sprintf(chunksize, "\n#%" PRIu32 "\n", count); + + r = nc_write(session, chunksize, r); + if (r == -1) { return -1; } + ret += r; } - c = nc_write(session, buf, count); - if (c == -1) { + r = nc_write(session, buf, count); + if (r == -1) { return -1; } - ret += c; + ret += r; return ret; } +/** + * @brief Write the end tag part of a chunked-framing NETCONF message. + * + * @param[in] session Session to write to. + * @return Number of bytes written. + * @return -1 on error. + */ static int nc_write_endtag(struct nc_session *session) { @@ -796,8 +708,15 @@ nc_write_endtag(struct nc_session *session) return ret; } +/** + * @brief Flush all the data buffered for writing. + * + * @param[in] warg Write callback structure to flush. + * @return Number of written bytes. + * @return -1 on error. + */ static int -nc_write_clb_flush(struct wclb_arg *warg) +nc_write_clb_flush(struct nc_wclb_arg *warg) { int ret = 0; @@ -810,12 +729,22 @@ nc_write_clb_flush(struct wclb_arg *warg) return ret; } +/** + * @brief Write callback buffering the data in a write structure. + * + * @param[in] arg Write structure used for buffering. + * @param[in] buf Buffer to write. + * @param[in] count Count of bytes to write from @p buf. + * @param[in] xmlcontent Whether the data are actually printed as part of an XML in which case they need to be encoded. + * @return Number of written bytes. + * @return -1 on error. + */ static ssize_t -nc_write_clb(void *arg, const void *buf, size_t count, int xmlcontent) +nc_write_clb(void *arg, const void *buf, uint32_t count, int xmlcontent) { - int ret = 0, c; - size_t l; - struct wclb_arg *warg = (struct wclb_arg *)arg; + ssize_t ret = 0, c; + uint32_t l; + struct nc_wclb_arg *warg = arg; if (!buf) { c = nc_write_clb_flush(warg); @@ -895,6 +824,9 @@ nc_write_clb(void *arg, const void *buf, size_t count, int xmlcontent) return ret; } +/** + * @brief Write print callback used by libyang. + */ static ssize_t nc_write_xmlclb(void *arg, const void *buf, size_t count) { @@ -921,7 +853,7 @@ nc_write_msg_io(struct nc_session *session, int io_timeout, int type, ...) struct nc_server_notif *notif; struct nc_server_reply *reply; char *buf; - struct wclb_arg arg; + struct nc_wclb_arg arg; const char **capabilities; uint32_t *sid = NULL, i, wd = 0; LY_ERR lyrc; @@ -954,11 +886,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); @@ -969,7 +897,7 @@ nc_write_msg_io(struct nc_session *session, int io_timeout, int type, ...) } /* rpc data */ - if (lyd_print_clb(nc_write_xmlclb, (void *)&arg, op, LYD_XML, LYD_PRINT_SHRINK)) { + if (lyd_print_clb(nc_write_xmlclb, (void *)&arg, op, LYD_XML, LYD_PRINT_SHRINK | LYD_PRINT_KEEPEMPTYCONT)) { ret = NC_MSG_ERROR; goto cleanup; } @@ -991,27 +919,21 @@ nc_write_msg_io(struct nc_session *session, int io_timeout, int type, ...) rpc_envp = va_arg(ap, struct lyd_node_opaq *); reply = va_arg(ap, struct nc_server_reply *); - if (!rpc_envp) { - /* can be NULL if replying with a malformed-message error */ - nc_write_clb((void *)&arg, "", 18 + strlen(NC_NS_BASE) + 2, 0); - - assert(reply->type == NC_RPL_ERROR); - if (lyd_print_clb(nc_write_xmlclb, (void *)&arg, ((struct nc_server_reply_error *)reply)->err, LYD_XML, - LYD_PRINT_SHRINK | LYD_PRINT_WITHSIBLINGS)) { + /* build a rpc-reply opaque node that can be simply printed */ + if (rpc_envp) { + if (lyd_new_opaq2(NULL, session->ctx, "rpc-reply", NULL, rpc_envp->name.prefix, rpc_envp->name.module_ns, + &reply_envp)) { + ERRINT; + ret = NC_MSG_ERROR; + goto cleanup; + } + } else { + if (lyd_new_opaq2(NULL, session->ctx, "rpc-reply", NULL, NULL, NC_NS_BASE, + &reply_envp)) { + ERRINT; ret = NC_MSG_ERROR; goto cleanup; } - - nc_write_clb((void *)&arg, "", 12, 0); - break; - } - - /* build a rpc-reply opaque node that can be simply printed */ - if (lyd_new_opaq2(NULL, session->ctx, "rpc-reply", NULL, rpc_envp->name.prefix, rpc_envp->name.module_ns, - &reply_envp)) { - ERRINT; - ret = NC_MSG_ERROR; - goto cleanup; } switch (reply->type) { @@ -1060,7 +982,9 @@ nc_write_msg_io(struct nc_session *session, int io_timeout, int type, ...) } /* temporary */ - ((struct lyd_node_opaq *)reply_envp)->attr = rpc_envp->attr; + if (rpc_envp) { + ((struct lyd_node_opaq *)reply_envp)->attr = rpc_envp->attr; + } /* print */ lyrc = lyd_print_clb(nc_write_xmlclb, (void *)&arg, reply_envp, LYD_XML, LYD_PRINT_SHRINK | wd); @@ -1117,11 +1041,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++) { @@ -1130,12 +1050,8 @@ nc_write_msg_io(struct nc_session *session, int io_timeout, int type, ...) nc_write_clb((void *)&arg, "", 13, 0); } if (sid) { - count = asprintf(&buf, "%u", *sid); - if (count == -1) { - ERRMEM; - ret = NC_MSG_ERROR; - goto cleanup; - } + count = asprintf(&buf, "%" PRIu32 "", *sid); + NC_CHECK_ERRMEM_GOTO(count == -1, ret = NC_MSG_ERROR, cleanup); nc_write_clb((void *)&arg, buf, count, 0); free(buf); } else { @@ -1179,7 +1095,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; @@ -1197,16 +1113,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 int)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 73074c4..16ada94 100644 --- a/src/log.c +++ b/src/log.c @@ -1,9 +1,11 @@ /** - * \file log.c - * \author Radek Krejci - * \brief libnetconf2 - log functions + * @file log.c + * @author Radek Krejci + * @author Michal Vasko + * @brief libnetconf2 - log functions * - * Copyright (c) 2015 - 2021 CESNET, z.s.p.o. + * @copyright + * Copyright (c) 2015 - 2024 CESNET, z.s.p.o. * * This source code is licensed under BSD 3-Clause License (the "License"). * You may not use this file except in compliance with the License. @@ -12,31 +14,38 @@ * https://opensource.org/licenses/BSD-3-Clause */ +#define _GNU_SOURCE /* pthread_rwlock_t */ + +#include "log_p.h" + #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" + +#define NC_MSG_SIZE 256 /** * @brief libnetconf verbose level variable */ -volatile uint8_t verbose_level = 0; +ATOMIC_T verbose_level = 0; -void (*depr_print_clb)(NC_VERB_LEVEL level, const char *msg); void (*print_clb)(const struct nc_session *session, NC_VERB_LEVEL level, const char *msg); API void nc_verbosity(NC_VERB_LEVEL level) { - verbose_level = level; + ATOMIC_STORE_RELAXED(verbose_level, level); ly_log_level((LY_LOG_LEVEL)level); } @@ -51,98 +60,100 @@ struct { {NC_VERB_DEBUG_LOWLVL, "[DBL]"} }; -#ifdef NC_ENABLED_SSH +#ifdef NC_ENABLED_SSH_TLS + +static void +nc_libssh_log_cb(int priority, const char *UNUSED(function), const char *buffer, void *UNUSED(userdata)) +{ + static char last_msg[NC_MSG_SIZE] = {0}; + static struct timespec last_print = {0}, cur_time; + + /* check for repeated messages and do not print them */ + if (!strncmp(last_msg, buffer, NC_MSG_SIZE - 1)) { + nc_realtime_get(&cur_time); + if (last_print.tv_sec && (nc_time_diff(&cur_time, &last_print) < 1000)) { + /* print another repeated message only after 1s */ + return; + } + + last_print = cur_time; + } else { + /* store the last message */ + strncpy(last_msg, buffer, NC_MSG_SIZE - 1); + memset(&last_print, 0, sizeof last_print); + } + + /* print the message */ + nc_log_printf(NULL, priority, "SSH: %s", buffer); +} API void nc_libssh_thread_verbosity(int level) { + ssh_set_log_callback(nc_libssh_log_cb); 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) +nc_log_vprintf(const struct nc_session *session, NC_VERB_LEVEL level, const char *format, va_list args) { -#define PRV_MSG_INIT_SIZE 256 va_list args2; - char *prv_msg; + char *msg; void *mem; int req_len; - prv_msg = malloc(PRV_MSG_INIT_SIZE); - if (!prv_msg) { + msg = malloc(NC_MSG_SIZE); + if (!msg) { return; } va_copy(args2, args); - req_len = vsnprintf(prv_msg, PRV_MSG_INIT_SIZE - 1, format, args); + req_len = vsnprintf(msg, NC_MSG_SIZE - 1, format, args); if (req_len == -1) { goto cleanup; - } else if (req_len >= PRV_MSG_INIT_SIZE - 1) { + } else if (req_len >= NC_MSG_SIZE - 1) { /* the length is not enough */ ++req_len; - mem = realloc(prv_msg, req_len); + mem = realloc(msg, req_len); if (!mem) { goto cleanup; } - prv_msg = mem; + msg = mem; /* now print the full message */ - req_len = vsnprintf(prv_msg, req_len, format, args2); + req_len = vsnprintf(msg, req_len, format, args2); if (req_len == -1) { goto cleanup; } } if (print_clb) { - print_clb(session, level, prv_msg); - } else if (depr_print_clb) { - depr_print_clb(level, prv_msg); + print_clb(session, level, msg); } else if (session && session->id) { - fprintf(stderr, "Session %u %s: %s\n", session->id, verb[level].label, prv_msg); + fprintf(stderr, "Session %" PRIu32 " %s: %s\n", session->id, verb[level].label, msg); } else { - fprintf(stderr, "%s: %s\n", verb[level].label, prv_msg); + fprintf(stderr, "%s: %s\n", verb[level].label, msg); } cleanup: - free(prv_msg); -#undef PRV_MSG_INIT_SIZE + free(msg); } void -prv_printf(const struct nc_session *session, NC_VERB_LEVEL level, const char *format, ...) +nc_log_printf(const struct nc_session *session, NC_VERB_LEVEL level, const char *format, ...) { va_list ap; va_start(ap, format); - prv_vprintf(session, level, format, ap); + nc_log_vprintf(session, level, format, ap); va_end(ap); } -static void -nc_ly_log_clb(LY_LOG_LEVEL lvl, const char *msg, const char *UNUSED(path)) -{ - if (print_clb) { - print_clb(NULL, (NC_VERB_LEVEL)lvl, msg); - } else if (depr_print_clb) { - depr_print_clb((NC_VERB_LEVEL)lvl, msg); - } -} - -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 *)) { print_clb = clb; - depr_print_clb = NULL; - ly_set_log_clb(nc_ly_log_clb, 1); } diff --git a/src/log.h b/src/log.h index 85f8663..b4365cc 100644 --- a/src/log.h +++ b/src/log.h @@ -3,6 +3,7 @@ * @author Radek Krejci * @brief libnetconf2 logger * + * @copyright * Copyright (c) 2015 - 2021 CESNET, z.s.p.o. * * This source code is licensed under BSD 3-Clause License (the "License"). @@ -49,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. @@ -68,20 +69,13 @@ 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. * - * This callback is set for libnetconf2 and also libyang that is used internally. libyang - * callback can be set explicitly, but must be done so after calling this function. + * The callback is not set per-session, it is a global resource. It might be called with + * a NULL session parameter. * * @param[in] clb Callback that is called for every message. */ diff --git a/src/log_p.h b/src/log_p.h index 0a899cd..42549eb 100644 --- a/src/log_p.h +++ b/src/log_p.h @@ -1,9 +1,11 @@ /** - * @file log.h + * @file log_p.h * @author Radek Krejci + * @author Michal Vasko * @brief libnetconf2 logger * - * Copyright (c) 2015 - 2021 CESNET, z.s.p.o. + * @copyright + * Copyright (c) 2015 - 2024 CESNET, z.s.p.o. * * This source code is licensed under BSD 3-Clause License (the "License"). * You may not use this file except in compliance with the License. @@ -15,8 +17,9 @@ #ifndef NC_LOG_PRIVATE_H_ #define NC_LOG_PRIVATE_H_ -#include +#include +#include "compat.h" #include "log.h" /* @@ -30,25 +33,67 @@ * @param[in] level Verbose level * @param[in] format Formatting string */ -void prv_printf(const struct nc_session *session, NC_VERB_LEVEL level, const char *format, ...); +void nc_log_printf(const struct nc_session *session, NC_VERB_LEVEL level, const char *format, ...); /** * @brief Verbose level variable */ -extern volatile uint8_t verbose_level; +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(verbose_level>=NC_VERB_WARNING){prv_printf(session,NC_VERB_WARNING,format,##args);} -#define VRB(session, format, args ...) if(verbose_level>=NC_VERB_VERBOSE){prv_printf(session,NC_VERB_VERBOSE,format,##args);} -#define DBG(session, format, args ...) if(verbose_level>=NC_VERB_DEBUG){prv_printf(session,NC_VERB_DEBUG,format,##args);} -#define DBL(session, format, args ...) if(verbose_level>=NC_VERB_DEBUG_LOWLVL){prv_printf(session,NC_VERB_DEBUG_LOWLVL,format,##args);} +#define ERR(session, ...) nc_log_printf(session, NC_VERB_ERROR, __VA_ARGS__) +#define WRN(session, ...) if(ATOMIC_LOAD_RELAXED(verbose_level)>=NC_VERB_WARNING){nc_log_printf(session, NC_VERB_WARNING, __VA_ARGS__);} +#define VRB(session, ...) if(ATOMIC_LOAD_RELAXED(verbose_level)>=NC_VERB_VERBOSE){nc_log_printf(session, NC_VERB_VERBOSE, __VA_ARGS__);} +#define DBG(session, ...) if(ATOMIC_LOAD_RELAXED(verbose_level)>=NC_VERB_DEBUG){nc_log_printf(session, NC_VERB_DEBUG, __VA_ARGS__);} +#define DBL(session, ...) if(ATOMIC_LOAD_RELAXED(verbose_level)>=NC_VERB_DEBUG_LOWLVL){nc_log_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 ERRINITSRV ERR(NULL, "%s: server 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_SRV_INIT_RET(RET) if (!ATOMIC_LOAD_RELAXED(server_opts.new_session_id)) {ERRINITSRV; return (RET);} +#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 6274a84..f8cc0fb 100644 --- a/src/messages_client.c +++ b/src/messages_client.c @@ -1,8 +1,9 @@ /** - * \file messages.c - * \author Radek Krejci - * \brief libnetconf2 - NETCONF messages functions + * @file messages.c + * @author Radek Krejci + * @brief libnetconf2 - NETCONF messages functions * + * @copyright * Copyright (c) 2015 - 2021 CESNET, z.s.p.o. * * This source code is licensed under BSD 3-Clause License (the "License"). @@ -12,15 +13,21 @@ * https://opensource.org/licenses/BSD-3-Clause */ +#define _GNU_SOURCE /* pthread_rwlock_t, strdup */ + #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"}; @@ -29,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; } @@ -42,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; @@ -73,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; @@ -101,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]); @@ -112,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; @@ -136,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]); @@ -150,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; @@ -176,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]); @@ -190,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; @@ -219,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; @@ -247,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; @@ -269,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; @@ -297,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)) { @@ -319,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; @@ -343,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; @@ -372,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; @@ -388,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)) { @@ -409,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]); @@ -420,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; @@ -442,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) { @@ -486,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)) { @@ -525,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; @@ -558,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 { @@ -591,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]); @@ -605,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) { @@ -633,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').", @@ -645,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)) { @@ -686,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').", @@ -698,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; @@ -725,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; @@ -747,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; @@ -770,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').", @@ -785,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) { @@ -829,11 +698,9 @@ nc_rpc_establishpush_onchange(const char *datastore, const char *filter, const c { struct nc_rpc_establishpush *rpc; uint32_t i; + void *tmp; - 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').", @@ -842,10 +709,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) { @@ -874,7 +738,15 @@ nc_rpc_establishpush_onchange(const char *datastore, const char *filter, const c if (excluded_change && (paramtype == NC_PARAMTYPE_DUP_AND_FREE)) { rpc->excluded_change = NULL; for (i = 0; excluded_change[i]; ++i) { - rpc->excluded_change = realloc(rpc->excluded_change, (i + 2) * sizeof *rpc->excluded_change); + tmp = realloc(rpc->excluded_change, (i + 2) * sizeof *rpc->excluded_change); + if (!tmp) { + /* in case we fail to alloc, just free all the excluded changes, but return the rpc anyways */ + ERRMEM; + free(rpc->excluded_change); + rpc->excluded_change = NULL; + break; + } + rpc->excluded_change = tmp; rpc->excluded_change[i] = strdup(excluded_change[i]); rpc->excluded_change[i + 1] = NULL; } @@ -892,13 +764,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').", @@ -907,10 +773,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; @@ -947,13 +810,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').", @@ -962,10 +819,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; @@ -996,16 +850,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_client.h b/src/messages_client.h index dda44dc..89d794c 100644 --- a/src/messages_client.h +++ b/src/messages_client.h @@ -4,6 +4,7 @@ * @author Michal Vasko * @brief libnetconf2's public functions and structures of NETCONF client messages. * + * @copyright * Copyright (c) 2015 - 2021 CESNET, z.s.p.o. * * This source code is licensed under BSD 3-Clause License (the "License"). diff --git a/src/messages_p.h b/src/messages_p.h index 177331f..5bf3d3a 100644 --- a/src/messages_p.h +++ b/src/messages_p.h @@ -3,6 +3,7 @@ * @author Radek Krejci * @brief libnetconf2's private functions and structures of NETCONF messages. * + * @copyright * Copyright (c) 2021 CESNET, z.s.p.o. * * This source code is licensed under BSD 3-Clause License (the "License"). @@ -15,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[]; @@ -65,6 +69,7 @@ struct nc_rpc { struct nc_rpc_act_generic { NC_RPC_TYPE type; /**< NC_RPC_ACT_GENERIC */ int has_data; /**< 1 for content.data, 0 for content.xml_str */ + union { struct lyd_node *data; /**< parsed RPC data */ char *xml_str; /**< raw XML string */ @@ -220,6 +225,7 @@ struct nc_rpc_establishpush { char *stop; char *encoding; int periodic; + union { struct { uint32_t period; @@ -241,6 +247,7 @@ struct nc_rpc_modifypush { char *filter; /**< XML subtree (starts with '<'), an XPath (starts with '/'), or reference (start with alpha) */ char *stop; int periodic; + union { struct { uint32_t period; diff --git a/src/messages_server.c b/src/messages_server.c index ec722be..8444203 100644 --- a/src/messages_server.c +++ b/src/messages_server.c @@ -1,8 +1,9 @@ /** - * \file messages_server.c - * \author Michal Vasko - * \brief libnetconf2 - server NETCONF messages functions + * @file messages_server.c + * @author Michal Vasko + * @brief libnetconf2 - server NETCONF messages functions * + * @copyright * Copyright (c) 2015 CESNET, z.s.p.o. * * This source code is licensed under BSD 3-Clause License (the "License"). @@ -12,6 +13,8 @@ * https://opensource.org/licenses/BSD-3-Clause */ +#define _GNU_SOURCE /* pthread_rwlock_t, strdup */ + #include #include #include @@ -21,10 +24,11 @@ #include #include "compat.h" -#include "libnetconf.h" -#include "session_server.h" - -extern struct nc_server_opts server_opts; +#include "config.h" +#include "log_p.h" +#include "messages_p.h" +#include "messages_server.h" +#include "netconf.h" API struct nc_server_reply * nc_server_reply_ok(void) @@ -32,10 +36,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; @@ -46,16 +47,15 @@ nc_server_reply_data(struct lyd_node *data, NC_WD_MODE wd, NC_PARAMTYPE paramtyp { struct nc_server_reply_data *ret; - if (!data) { - 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; @@ -80,16 +80,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; @@ -101,11 +95,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; } @@ -119,8 +112,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; } @@ -270,10 +265,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)) { @@ -291,7 +283,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; @@ -304,7 +296,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; @@ -313,14 +305,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; @@ -334,7 +326,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; @@ -342,7 +334,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)) { @@ -419,7 +411,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; } @@ -466,7 +458,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; } @@ -484,10 +476,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) { @@ -502,10 +491,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) { @@ -520,13 +506,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); @@ -546,10 +526,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) { @@ -564,13 +541,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); @@ -590,10 +561,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) { @@ -609,20 +577,15 @@ 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); if (match) { - lyd_free_tree(match); + /* Change the value of error-message and keep order of elements to comply with appendix-B in RFC 6241. */ + lydict_remove(LYD_CTX(err), ((struct lyd_node_opaq *)match)->value); + lydict_insert(LYD_CTX(err), error_message, 0, &(((struct lyd_node_opaq *)match)->value)); + return 0; } - if (lyd_new_opaq2(err, NULL, "error-message", error_message, NULL, NC_NS_BASE, &match)) { return -1; } @@ -639,10 +602,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) { @@ -658,10 +618,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); @@ -688,13 +645,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); @@ -714,13 +665,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); @@ -740,13 +685,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); @@ -766,13 +705,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); @@ -837,13 +770,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; @@ -855,11 +782,13 @@ 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); + NC_CHECK_ERRMEM_RET(!ntf, NULL); + if (paramtype == NC_PARAMTYPE_DUP_AND_FREE) { ntf->eventtime = strdup(eventtime); if (lyd_dup_single(event, NULL, LYD_DUP_RECURSIVE, &ntf->ntf)) { @@ -892,10 +821,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 44659d0..63f6ef1 100644 --- a/src/messages_server.h +++ b/src/messages_server.h @@ -3,6 +3,7 @@ * @author Michal Vasko * @brief libnetconf2's functions and structures of server NETCONF messages. * + * @copyright * Copyright (c) 2015-2021 CESNET, z.s.p.o. * * This source code is licensed under BSD 3-Clause License (the "License"). @@ -19,9 +20,11 @@ extern "C" { #endif -#include +#include #include +#include + #include "netconf.h" #include "session.h" @@ -95,7 +98,7 @@ struct nc_server_reply *nc_server_reply_ok(void); /** * @brief Create a DATA rpc-reply object. * - * @param[in] data Reply data tree. This tree must be valid according to + * @param[in] data Reply data tree pointing to the RPC/action itself. This tree must be valid according to * the RPC output of the RPC this is a reply to. * @param[in] wd with-default mode if applicable * @param[in] paramtype Determines how the @p data parameter is treated. @@ -106,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 as opaque data node tree. 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); @@ -115,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 as opaque data node tree. 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); @@ -165,6 +168,7 @@ const struct lyd_node *nc_server_reply_get_last_err(const struct nc_server_reply * - #NC_ERR_DATA_MISSING * - #NC_ERR_MALFORMED_MSG * - no additional arguments + * @param[in] ... Additional arguments depending on the @p tag used. * @return Opaque data node tree representing the error. */ struct lyd_node *nc_err(const struct ly_ctx *ctx, NC_ERR tag, ...); @@ -293,11 +297,11 @@ void nc_server_reply_free(struct nc_server_reply *reply); * @brief Create Event Notification object to be sent to the subscribed client(s). * * @param[in] event Notification data tree (valid as LYD_OPT_NOTIF) from libyang. The tree is directly used in created - * object, so the caller is supposed to not free the tree on its own, but only via freeng the created object. + * object, so the caller is supposed to not free the tree on its own, but only via freeing the created object. * @param[in] eventtime YANG dateTime format value of the time when the event was generated by the event source. * Caller can use nc_timespec2datetime() to create the value from a timespec value. * @param[in] paramtype How to further manage data parameters. - * @return Newly created structure of the Event Notification object to be sent to the clients via nc_server_send_notif() + * @return Newly created structure of the Event Notification object to be sent to the clients via nc_server_notif_send() * and freed using nc_server_notif_free(). */ struct nc_server_notif *nc_server_notif_new(struct lyd_node *event, char *eventtime, NC_PARAMTYPE paramtype); @@ -306,8 +310,8 @@ struct nc_server_notif *nc_server_notif_new(struct lyd_node *event, char *eventt * @brief Send NETCONF Event Notification via the session. * * @param[in] session NETCONF session where the Event Notification will be written. - * @param[in] notif NETCOFN Notification object to send via specified session. Object can be created by - * nc_notif_new() function. + * @param[in] notif NETCONF Notification object to send via specified session. Object can be created by + * nc_server_notif_new() function. * @param[in] timeout Timeout for writing in milliseconds. Use negative value for infinite * waiting and 0 for return if data cannot be sent immediately. * @return #NC_MSG_NOTIF on success, diff --git a/src/netconf.h b/src/netconf.h index de497ff..1e25cce 100644 --- a/src/netconf.h +++ b/src/netconf.h @@ -3,6 +3,7 @@ * @author Radek Krejci * @brief libnetconf2's general public functions and structures definitions. * + * @copyright * Copyright (c) 2015 - 2021 CESNET, z.s.p.o. * * This source code is licensed under BSD 3-Clause License (the "License"). @@ -19,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 0000000..158d84d --- /dev/null +++ b/src/server_config.c @@ -0,0 +1,4707 @@ +/** + * @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 + +#include "compat.h" +#include "config.h" +#include "log_p.h" +#include "server_config.h" +#include "server_config_p.h" +#include "session_p.h" + +/* returns a parent node of 'node' that matches the name 'name' */ +static const struct lyd_node * +nc_server_config_get_parent(const struct lyd_node *node, const char *name) +{ + NC_CHECK_ARG_RET(NULL, node, name, NULL); + + while (node) { + if (!strcmp(LYD_NAME(node), name)) { + return node; + } + node = lyd_parent(node); + } + + return NULL; +} + +/* returns a parent list node of 'node' that matches the name 'name' */ +static const struct lyd_node * +nc_server_config_get_parent_list(const struct lyd_node *node, const char *name) +{ + NC_CHECK_ARG_RET(NULL, node, name, NULL); + + while (node) { + /* check if the node is a list and its name matches the param */ + if ((node->schema->nodetype == LYS_LIST) && (!strcmp(LYD_NAME(node), name))) { + return node; + } + node = lyd_parent(node); + } + + return NULL; +} + +/* returns the key of a list node with the name 'name' */ +static const char * +nc_server_config_get_parent_list_key_value(const struct lyd_node *node, const char *name, const char *key_name) +{ + const char *original_name; + + NC_CHECK_ARG_RET(NULL, node, name, key_name, NULL); + original_name = LYD_NAME(node); + + /* get the supposed parent list */ + node = nc_server_config_get_parent_list(node, name); + if (!node) { + ERR(NULL, "Node \"%s\" not contained in \"%s\" subtree.", original_name, name); + return NULL; + } + + /* child should be the key */ + node = lyd_child(node); + if (!node) { + ERR(NULL, "Node \"%s\" has no child nodes.", name); + return NULL; + } + if (strcmp(LYD_NAME(node), key_name)) { + ERR(NULL, "Node \"%s\" child names mismatch (found:\"%s\", expected:\"%s\").", original_name, LYD_NAME(node), key_name); + return NULL; + } + + return lyd_get_value(node); +} + +/* returns true if a node is a part of the listen subtree */ +static int +is_listen(const struct lyd_node *node) +{ + node = nc_server_config_get_parent(node, "listen"); + 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) +{ + node = nc_server_config_get_parent(node, "call-home"); + 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) +{ + node = nc_server_config_get_parent(node, "ssh"); + return node != NULL; +} + +/* returns true if a node is a part of the tls subtree */ +static int +is_tls(const struct lyd_node *node) +{ + node = nc_server_config_get_parent(node, "tls"); + 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; + + NC_CHECK_ARG_RET(NULL, node, endpt, 1); + + name = nc_server_config_get_parent_list_key_value(node, "endpoint", "name"); + if (!name) { + return 1; + } + + 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 + * THE ch_client_lock HAS TO BE LOCKED PRIOR TO CALLING THIS + */ +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; + + NC_CHECK_ARG_RET(NULL, node, ch_client, 1); + + name = nc_server_config_get_parent_list_key_value(node, "netconf-client", "name"); + if (!name) { + return 1; + } + + 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]; + return 0; + } + } + + 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, + * ch_client has to be locked + */ +static int +nc_server_config_get_ch_endpt(const struct lyd_node *node, const struct nc_ch_client *ch_client, + struct nc_ch_endpt **ch_endpt) +{ + uint16_t i; + const char *name; + + NC_CHECK_ARG_RET(NULL, node, ch_client, ch_endpt, 1); + + name = nc_server_config_get_parent_list_key_value(node, "endpoint", "name"); + if (!name) { + return 1; + } + + 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]; + return 0; + } + } + + 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, const struct nc_ch_client *ch_client, + struct nc_server_ssh_opts **opts) +{ + struct nc_endpt *endpt; + struct nc_ch_endpt *ch_endpt; + + NC_CHECK_ARG_RET(NULL, node, opts, 1); + + 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_client, &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, const struct nc_ch_client *ch_client, + struct nc_hostkey **hostkey) +{ + uint16_t i; + const char *name; + struct nc_server_ssh_opts *opts; + + NC_CHECK_ARG_RET(NULL, node, hostkey, 1); + + if (nc_server_config_get_ssh_opts(node, ch_client, &opts)) { + return 1; + } + + name = nc_server_config_get_parent_list_key_value(node, "host-key", "name"); + if (!name) { + 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, const struct nc_ch_client *ch_client, + struct nc_auth_client **auth_client) +{ + uint16_t i; + const char *name; + struct nc_server_ssh_opts *opts; + + NC_CHECK_ARG_RET(NULL, node, auth_client, 1); + + if (nc_server_config_get_ssh_opts(node, ch_client, &opts)) { + return 1; + } + + name = nc_server_config_get_parent_list_key_value(node, "user", "name"); + if (!name) { + 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, const struct nc_ch_client *ch_client, + struct nc_public_key **pubkey) +{ + uint16_t i; + const char *name; + struct nc_auth_client *auth_client; + + NC_CHECK_ARG_RET(NULL, node, pubkey, 1); + + if (nc_server_config_get_auth_client(node, ch_client, &auth_client)) { + return 1; + } + + name = nc_server_config_get_parent_list_key_value(node, "public-key", "name"); + if (!name) { + 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, const struct nc_ch_client *ch_client, + struct nc_server_tls_opts **opts) +{ + struct nc_endpt *endpt; + struct nc_ch_endpt *ch_endpt; + + NC_CHECK_ARG_RET(NULL, node, opts, 1); + + 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_client, &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, const struct nc_ch_client *ch_client, + struct nc_certificate **cert) +{ + uint16_t i; + const char *name; + struct nc_cert_grouping *certs; + struct nc_server_tls_opts *opts; + int is_cert_end_entity; + const struct lyd_node *tmp; + + NC_CHECK_ARG_RET(NULL, node, cert, 1); + + if (nc_server_config_get_tls_opts(node, ch_client, &opts)) { + return 1; + } + + name = nc_server_config_get_parent_list_key_value(node, "certificate", "name"); + if (!name) { + return 1; + } + + /* it's in certificate subtree, now check if it's end entity or certificate authority */ + tmp = nc_server_config_get_parent(node, "ee-certs"); + if (tmp) { + is_cert_end_entity = 1; + } else { + tmp = nc_server_config_get_parent(node, "ca-certs"); + if (!tmp) { + ERR(NULL, "Node \"%s\" is not contained in ee-certs nor ca-certs subtree.", name); + return 1; + } + is_cert_end_entity = 0; + } + + /* get the right cert stack, either ee or ca */ + if (is_cert_end_entity) { + certs = &opts->ee_certs; + } else { + certs = &opts->ca_certs; + } + + for (i = 0; i < certs->cert_count; i++) { + if (!strcmp(certs->certs[i].name, name)) { + *cert = &certs->certs[i]; + return 0; + } + } + + ERR(NULL, "%s certificate \"%s\" was not found.", is_cert_end_entity ? "End-entity" : "Certificate authority", 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, const struct nc_ch_client *ch_client, + struct nc_ctn **ctn) +{ + uint32_t id; + struct nc_ctn *iter; + struct nc_server_tls_opts *opts; + const char *name; + + NC_CHECK_ARG_RET(NULL, node, ctn, 1); + + if (nc_server_config_get_tls_opts(node, ch_client, &opts)) { + return 1; + } + + name = LYD_NAME(node); + node = nc_server_config_get_parent_list(node, "cert-to-name"); + 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; + + 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; +} + +enum 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; + + NC_CHECK_ARG_RET(NULL, node, ch_client, 1); + + name = nc_server_config_get_parent_list_key_value(node, "netconf-client", "name"); + if (!name) { + return 1; + } + + /* 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_pubkeys(struct nc_auth_client *auth_client) +{ + uint16_t i, pubkey_count; + + 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 if (auth_client->store == NC_STORE_TRUSTSTORE) { + free(auth_client->ts_ref); + auth_client->ts_ref = NULL; + } +} + +static void +nc_server_config_del_auth_client(struct nc_server_ssh_opts *opts, struct nc_auth_client *auth_client) +{ + free(auth_client->username); + + nc_server_config_del_auth_client_pubkeys(auth_client); + + free(auth_client->password); + + 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->banner); + + free(opts); +} + +/* delete references to endpoint with the name 'referenced_endpt_name' from other endpts */ +static void +nc_server_config_del_endpt_references(const char *referenced_endpt_name) +{ + uint16_t i, j; + + /* first go through listen endpoints */ + 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_SSH) { + 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); + /* next go through ch endpoints */ + 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_SSH) { + server_opts.ch_clients[i].ch_endpts[j].opts.ssh->referenced_endpt_name = NULL; + } else { + server_opts.ch_clients[i].ch_endpts[j].opts.tls->referenced_endpt_name = NULL; + } + } + } + } + /* 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); + } +} + +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); + } + certs_grp->cert_count = 0; + free(certs_grp->certs); + certs_grp->certs = NULL; + } else if (certs_grp->store == NC_STORE_TRUSTSTORE) { + free(certs_grp->ts_ref); + certs_grp->ts_ref = NULL; + } + + /* reset to the default */ + certs_grp->store = NC_STORE_LOCAL; +} + +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; + } + } + + if (!iter) { + ERRINT; + return; + } + + 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 if (opts->store == NC_STORE_KEYSTORE) { + free(opts->key_ref); + free(opts->cert_ref); + } + + nc_server_config_del_certs(&opts->ca_certs); + nc_server_config_del_certs(&opts->ee_certs); + + 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 */ + +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->src_addr); + free(ch_endpt->dst_addr); + 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_SSH: + nc_server_config_del_ssh_opts(NULL, ch_endpt->opts.ssh); + break; + case NC_TI_TLS: + 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_destroy_ch_client(struct nc_ch_client *ch_client) +{ + pthread_t tid; + uint16_t i, ch_endpt_count; + + /* CH COND LOCK */ + pthread_mutex_lock(&ch_client->thread_data->cond_lock); + if (ch_client->thread_data->thread_running) { + ch_client->thread_data->thread_running = 0; + pthread_cond_signal(&ch_client->thread_data->cond); + /* CH COND UNLOCK */ + pthread_mutex_unlock(&ch_client->thread_data->cond_lock); + + /* get tid */ + tid = ch_client->tid; + + /* wait for the thread to terminate */ + pthread_join(tid, NULL); + } else { + /* CH COND UNLOCK */ + pthread_mutex_unlock(&ch_client->thread_data->cond_lock); + } + + /* free its members */ + free(ch_client->name); + + ch_endpt_count = ch_client->ch_endpt_count; + for (i = 0; i < ch_endpt_count; i++) { + nc_server_config_ch_del_endpt(ch_client, &ch_client->ch_endpts[i]); + } +} + +static void +nc_server_config_ch_del_client(const struct lyd_node *node) +{ + struct nc_ch_client client, *ch_client; + + /* WR LOCK */ + pthread_rwlock_wrlock(&server_opts.ch_client_lock); + + if (nc_server_config_get_ch_client(node, &ch_client)) { + /* WR UNLOCK */ + pthread_rwlock_unlock(&server_opts.ch_client_lock); + ERR(NULL, "Call-home client \"%s\" not found.", lyd_get_value(lyd_child(node))); + return; + } + + /* copy the client we want to delete into a local variable */ + memcpy(&client, ch_client, sizeof *ch_client); + + /* 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); + + nc_server_config_destroy_ch_client(&client); +} + +/* presence container */ +int +nc_server_config_listen(const struct lyd_node *node, enum 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_SSH: + nc_server_config_del_endpt_ssh(&server_opts.endpts[i], &server_opts.binds[i]); + break; + case NC_TI_TLS: + nc_server_config_del_endpt_tls(&server_opts.endpts[i], &server_opts.binds[i]); + break; +#endif /* NC_ENABLED_SSH_TLS */ + case NC_TI_UNIX: + break; + case NC_TI_NONE: + case NC_TI_FD: + ERRINT; + return 1; + } + } + } + + return 0; +} + +int +nc_server_config_ch(const struct lyd_node *node, enum nc_operation op) +{ + uint16_t i, ch_client_count; + struct nc_ch_client *ch_clients; + + (void) node; + + /* don't do anything if we're not deleting */ + if (op != NC_OP_DELETE) { + return 0; + } + + /* WR LOCK */ + pthread_rwlock_wrlock(&server_opts.ch_client_lock); + + ch_client_count = server_opts.ch_client_count; + ch_clients = server_opts.ch_clients; + + /* remove them from the server opts */ + server_opts.ch_client_count = 0; + server_opts.ch_clients = NULL; + + /* UNLOCK */ + pthread_rwlock_unlock(&server_opts.ch_client_lock); + + for (i = 0; i < ch_client_count; i++) { + /* now destroy each client */ + nc_server_config_destroy_ch_client(&ch_clients[i]); + } + + free(ch_clients); + return 0; +} + +/* default leaf */ +static int +nc_server_config_idle_timeout(const struct lyd_node *node, enum nc_operation op) +{ + struct nc_ch_client *ch_client = NULL; + + 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 { + /* listen 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 = 180; + } + } + + 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, enum 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 = NULL; + + 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_SSH: + nc_server_config_del_endpt_ssh(endpt, bind); + break; + case NC_TI_TLS: + nc_server_config_del_endpt_tls(endpt, bind); + break; +#endif /* NC_ENABLED_SSH_TLS */ + case NC_TI_UNIX: + 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_client, &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_SSH; + 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_SSH; + 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, enum nc_operation op) +{ + struct nc_endpt *endpt; + struct nc_bind *bind; + struct nc_ch_endpt *ch_endpt; + struct nc_ch_client *ch_client = NULL; + 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_client, &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_TLS; + 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_TLS; + 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, enum nc_operation op) +{ + struct nc_endpt *endpt; + struct nc_bind *bind; + struct nc_ch_endpt *ch_endpt; + struct nc_ch_client *ch_client = NULL; + 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_client, &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; +} + +/* leaf */ +static int +nc_server_config_local_address(const struct lyd_node *node, enum nc_operation op) +{ + int ret = 0; + struct nc_endpt *endpt; + struct nc_bind *bind; + struct nc_ch_client *ch_client = NULL; + struct nc_ch_endpt *ch_endpt; + + assert(!strcmp(LYD_NAME(node), "local-address")); + + 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; + } + + free(bind->address); + bind->address = strdup(lyd_get_value(node)); + NC_CHECK_ERRMEM_GOTO(!bind->address, ret = 1, cleanup); + + ret = nc_server_set_address_port(endpt, bind, lyd_get_value(node), 0); + 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_client, &ch_endpt)) { + ret = 1; + goto cleanup; + } + + if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) { + free(ch_endpt->src_addr); + ch_endpt->src_addr = strdup(lyd_get_value(node)); + NC_CHECK_ERRMEM_GOTO(!ch_endpt->src_addr, ret = 1, cleanup); + } else if (op == NC_OP_DELETE) { + free(ch_endpt->src_addr); + ch_endpt->src_addr = NULL; + } + } + +cleanup: + if (is_ch(node) && equal_parent_name(node, 1, "tcp-client-parameters")) { + /* UNLOCK */ + nc_ch_client_unlock(ch_client); + } + return ret; +} + +/* leaf with default value */ +static int +nc_server_config_local_port(const struct lyd_node *node, enum nc_operation op) +{ + int ret = 0; + struct nc_endpt *endpt; + struct nc_bind *bind; + struct nc_ch_client *ch_client = NULL; + struct nc_ch_endpt *ch_endpt; + + assert(!strcmp(LYD_NAME(node), "local-port")); + + 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) || (op == NC_OP_REPLACE)) { + bind->port = ((struct lyd_node_term *)node)->value.uint16; + } else { + /* delete -> set to default */ + bind->port = 0; + } + + ret = nc_server_set_address_port(endpt, bind, NULL, bind->port); + 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_client, &ch_endpt)) { + ret = 1; + goto cleanup; + } + + if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) { + ch_endpt->src_port = ((struct lyd_node_term *)node)->value.uint16; + } else if (op == NC_OP_DELETE) { + /* delete -> set to default */ + ch_endpt->src_port = 0; + } + } + +cleanup: + if (is_ch(node) && equal_parent_name(node, 1, "tcp-client-parameters")) { + /* UNLOCK */ + nc_ch_client_unlock(ch_client); + } + return ret; +} + +/* NP container */ +static int +nc_server_config_keepalives(const struct lyd_node *node, enum 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 = NULL; + + 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; + } + } 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_client, &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; +} + +/* leaf with default value */ +static int +nc_server_config_idle_time(const struct lyd_node *node, enum 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 = NULL; + + 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 { + /* delete -> set to default */ + endpt->ka.idle_time = 7200; + } + } 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_client, &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 { + /* delete -> set to default */ + ch_endpt->ka.idle_time = 7200; + } + } + +cleanup: + if (is_ch(node) && equal_parent_name(node, 2, "tcp-client-parameters")) { + /* UNLOCK */ + nc_ch_client_unlock(ch_client); + } + return ret; +} + +/* leaf with default value */ +static int +nc_server_config_max_probes(const struct lyd_node *node, enum 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 = NULL; + + 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 { + /* delete -> set to default */ + endpt->ka.max_probes = 9; + } + } 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_client, &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 { + /* delete -> set to default */ + ch_endpt->ka.max_probes = 9; + } + } + +cleanup: + if (is_ch(node) && equal_parent_name(node, 2, "tcp-client-parameters")) { + /* UNLOCK */ + nc_ch_client_unlock(ch_client); + } + return ret; +} + +/* leaf with default value */ +static int +nc_server_config_probe_interval(const struct lyd_node *node, enum 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 = NULL; + + 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 { + /* delete -> set to default */ + endpt->ka.probe_interval = 75; + } + } 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_client, &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 { + /* delete -> set to default */ + ch_endpt->ka.probe_interval = 75; + } + } + +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, enum nc_operation op) +{ + int ret = 0; + struct nc_hostkey *hostkey; + struct nc_server_ssh_opts *opts; + struct nc_ch_client *ch_client = NULL; + + assert(!strcmp(LYD_NAME(node), "host-key")); + + 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 (nc_server_config_get_ssh_opts(node, ch_client, &opts)) { + ret = 1; + goto cleanup; + } + + 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, ch_client, &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, enum nc_operation op) +{ + int ret = 0; + const char *format; + enum 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 = NULL; + + assert(!strcmp(LYD_NAME(node), "public-key-format")); + + /* LOCK */ + if (is_ch(node) && nc_server_config_get_ch_client_with_lock(node, &ch_client)) { + /* to avoid unlock on fail */ + return 1; + } + + 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; + } + + if (is_ssh(node) && equal_parent_name(node, 4, "server-identity")) { + /* SSH hostkey public key fmt */ + if (nc_server_config_get_hostkey(node, ch_client, &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, ch_client, &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, ch_client, &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, enum 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 = NULL; + + 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, ch_client, &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, ch_client, &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, ch_client, &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, ch_client, &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, ch_client, &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, enum nc_operation op) +{ + int ret = 0; + const char *format; + enum nc_privkey_format privkey_type; + struct nc_hostkey *hostkey; + struct nc_server_tls_opts *opts; + struct nc_ch_client *ch_client = NULL; + + (void) op; + + assert(!strcmp(LYD_NAME(node), "private-key-format")); + + /* LOCK */ + if (is_ch(node) && nc_server_config_get_ch_client_with_lock(node, &ch_client)) { + /* to avoid unlock on fail */ + return 1; + } + + 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; + } + + if (is_ssh(node)) { + /* ssh */ + if (nc_server_config_get_hostkey(node, ch_client, &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, ch_client, &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, enum nc_operation op) +{ + int ret = 0; + struct nc_hostkey *hostkey; + struct nc_server_tls_opts *opts; + struct nc_ch_client *ch_client = NULL; + + 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, ch_client, &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, ch_client, &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, enum nc_operation op) +{ + int ret = 0; + struct nc_hostkey *hostkey; + struct nc_ch_client *ch_client = NULL; + + assert(!strcmp(LYD_NAME(node), "central-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, ch_client, &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, enum nc_operation op) +{ + int ret = 0; + struct nc_auth_client *auth_client; + struct nc_server_ssh_opts *opts; + struct nc_ch_client *ch_client = NULL; + + 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, ch_client, &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, ch_client, &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_timeout(const struct lyd_node *node, enum nc_operation op) +{ + int ret = 0; + struct nc_server_ssh_opts *opts; + struct nc_ch_client *ch_client = NULL; + + 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, ch_client, &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, enum nc_operation op) +{ + int ret = 0; + struct nc_auth_client *auth_client; + struct nc_ch_client *ch_client = NULL; + struct nc_server_tls_opts *opts; + struct nc_cert_grouping *certs_grp; + + assert(!strcmp(LYD_NAME(node), "central-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, ch_client, &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, ch_client, &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; +} + +static int +nc_server_config_use_system_keys(const struct lyd_node *node, enum nc_operation op) +{ + int ret = 0; + struct nc_auth_client *auth_client; + struct nc_ch_client *ch_client = NULL; + + assert(!strcmp(LYD_NAME(node), "use-system-keys")); + + /* 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, ch_client, &auth_client)) { + ret = 1; + goto cleanup; + } + + if (op == NC_OP_CREATE) { + auth_client->store = NC_STORE_SYSTEM; + } + +cleanup: + if (is_ch(node)) { + /* UNLOCK */ + nc_ch_client_unlock(ch_client); + } + return ret; +} + +static int +nc_server_config_public_keys(const struct lyd_node *node, enum nc_operation op) +{ + int ret = 0; + struct nc_auth_client *auth_client; + struct nc_ch_client *ch_client = NULL; + + assert(!strcmp(LYD_NAME(node), "public-keys")); + + /* only do something on delete */ + if (op != NC_OP_DELETE) { + return 0; + } + + /* 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, ch_client, &auth_client)) { + ret = 1; + goto cleanup; + } + + nc_server_config_del_auth_client_pubkeys(auth_client); + +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, enum nc_operation op) +{ + int ret = 0; + struct nc_auth_client *auth_client; + struct nc_ch_client *ch_client = NULL; + + 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, ch_client, &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_use_system_auth(const struct lyd_node *node, enum nc_operation op) +{ + int ret = 0; + struct nc_auth_client *auth_client; + struct nc_ch_client *ch_client = NULL; + + assert(!strcmp(LYD_NAME(node), "use-system-auth")); + + /* 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, ch_client, &auth_client)) { + ret = 1; + goto cleanup; + } + + if (op == NC_OP_CREATE) { + auth_client->kb_int_enabled = 1; + } else { + auth_client->kb_int_enabled = 0; + } + +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, enum nc_operation op) +{ + int ret = 0; + struct nc_auth_client *auth_client; + struct nc_ch_client *ch_client = NULL; + + 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, ch_client, &auth_client)) { + ret = 1; + goto cleanup; + } + + if (op == NC_OP_CREATE) { + auth_client->none_enabled = 1; + } else { + auth_client->none_enabled = 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, enum 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, enum nc_operation op) +{ + int ret = 0; + const char *alg; + struct nc_server_ssh_opts *opts; + struct nc_ch_client *ch_client = NULL; + + 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, ch_client, &opts)) { + ret = 1; + goto cleanup; + } + + /* get the algorithm name and append it to supported algs */ + alg = ((struct lyd_node_term *)node)->value.ident->name; + if (nc_server_config_transport_params(alg, &opts->hostkey_algs, op)) { + ret = 1; + goto cleanup; + } + +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, enum nc_operation op) +{ + int ret = 0; + const char *alg; + struct nc_server_ssh_opts *opts; + struct nc_ch_client *ch_client = NULL; + + 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, ch_client, &opts)) { + ret = 1; + goto cleanup; + } + + /* get the algorithm name and append it to supported algs */ + alg = ((struct lyd_node_term *)node)->value.ident->name; + if (nc_server_config_transport_params(alg, &opts->kex_algs, op)) { + ret = 1; + goto cleanup; + } + +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, enum nc_operation op) +{ + int ret = 0; + const char *alg; + struct nc_server_ssh_opts *opts; + struct nc_ch_client *ch_client = NULL; + + 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, ch_client, &opts)) { + ret = 1; + goto cleanup; + } + + /* get the algorithm name and append it to supported algs */ + alg = ((struct lyd_node_term *)node)->value.ident->name; + if (nc_server_config_transport_params(alg, &opts->encryption_algs, op)) { + ret = 1; + goto cleanup; + } + +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, enum nc_operation op) +{ + int ret = 0; + const char *alg; + struct nc_server_ssh_opts *opts; + struct nc_ch_client *ch_client = NULL; + + 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, ch_client, &opts)) { + ret = 1; + goto cleanup; + } + + /* get the algorithm name and append it to supported algs */ + alg = ((struct lyd_node_term *)node)->value.ident->name; + if (nc_server_config_transport_params(alg, &opts->mac_algs, op)) { + ret = 1; + goto cleanup; + } + +cleanup: + if (is_ch(node)) { + /* UNLOCK */ + nc_ch_client_unlock(ch_client); + } + return ret; +} + +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_SSH) && (referenced_endpt->ti != NC_TI_TLS)) { + ERR(NULL, "Endpoint \"%s\" referenced by endpoint \"%s\" has unsupported transport type.", + server_opts.endpts[i].referenced_endpt_name, server_opts.endpts[i].name); + return 1; + } + + /* 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_SSH) { + server_opts.endpts[i].opts.ssh->referenced_endpt_name = referenced_endpt->name; + } else { + server_opts.endpts[i].opts.tls->referenced_endpt_name = referenced_endpt->name; + } + } + } + + /* 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_SSH) && (referenced_endpt->ti != NC_TI_TLS)) { + ERR(NULL, "Endpoint \"%s\" referenced by call home endpoint \"%s\" has unsupported transport type.", + server_opts.ch_clients[i].ch_endpts[j].referenced_endpt_name, server_opts.ch_clients[i].ch_endpts[j].name); + goto ch_fail; + } + + /* 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_SSH) { + server_opts.ch_clients[i].ch_endpts[j].opts.ssh->referenced_endpt_name = referenced_endpt->name; + } else { + server_opts.ch_clients[i].ch_endpts[j].opts.tls->referenced_endpt_name = referenced_endpt->name; + } + } + } + /* 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, enum nc_operation op) +{ + int ret = 0; + struct nc_endpt *endpt = NULL; + struct nc_ch_client *ch_client = NULL; + 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_client, &ch_endpt); + } + if (ret) { + goto cleanup; + } + + if (op == NC_OP_DELETE) { + if (endpt) { + /* listen */ + free(endpt->referenced_endpt_name); + endpt->referenced_endpt_name = NULL; + } else { + /* call home */ + free(ch_endpt->referenced_endpt_name); + ch_endpt->referenced_endpt_name = NULL; + } + if (is_ssh(node)) { + if (nc_server_config_get_ssh_opts(node, ch_client, &ssh)) { + ret = 1; + goto cleanup; + } + + ssh->referenced_endpt_name = NULL; + } else { + if (nc_server_config_get_tls_opts(node, ch_client, &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, enum nc_operation op) +{ + int ret = 0; + struct nc_certificate *cert; + struct nc_server_tls_opts *opts; + struct nc_ch_client *ch_client = NULL; + + 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, ch_client, &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, ch_client, &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, enum nc_operation op) +{ + int ret = 0; + struct nc_server_tls_opts *opts; + struct nc_ch_client *ch_client = NULL; + + 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, ch_client, &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_banner(const struct lyd_node *node, enum nc_operation op) +{ + int ret = 0; + struct nc_server_ssh_opts *opts; + struct nc_ch_client *ch_client = NULL; + + assert(!strcmp(LYD_NAME(node), "banner")); + + 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, ch_client, &opts)) { + ret = 1; + goto cleanup; + } + + if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) { + free(opts->banner); + opts->banner = strdup(lyd_get_value(node)); + NC_CHECK_ERRMEM_GOTO(!opts->banner, ret = 1, cleanup); + } else { + free(opts->banner); + opts->banner = NULL; + } + +cleanup: + if (is_ch(node)) { + /* UNLOCK */ + nc_ch_client_unlock(ch_client); + } + + return ret; +} + +static int +nc_server_config_client_authentication(const struct lyd_node *node, enum nc_operation op) +{ + int ret = 0; + struct nc_server_tls_opts *opts; + struct nc_ch_client *ch_client = NULL; + + assert(!strcmp(LYD_NAME(node), "client-authentication")); + + /* only do something on delete and if we're in the TLS subtree, + * because this is a presence container unlike its SSH counterpart */ + if (!is_tls(node) || (op != NC_OP_DELETE)) { + return 0; + } + + /* 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, ch_client, &opts)) { + ret = 1; + goto cleanup; + } + + nc_server_config_del_certs(&opts->ca_certs); + nc_server_config_del_certs(&opts->ee_certs); + +cleanup: + if (is_ch(node)) { + /* UNLOCK */ + nc_ch_client_unlock(ch_client); + } + return ret; +} + +static int +nc_server_config_ca_certs(const struct lyd_node *node, enum nc_operation op) +{ + int ret = 0; + struct nc_server_tls_opts *opts; + struct nc_ch_client *ch_client = NULL; + + assert(!strcmp(LYD_NAME(node), "ca-certs")); + + /* only do something on delete and if we're in the TLS subtree, + * because SSH certs are not yet supported */ + if (!is_tls(node) || (op != NC_OP_DELETE)) { + return 0; + } + + /* 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, ch_client, &opts)) { + ret = 1; + goto cleanup; + } + + nc_server_config_del_certs(&opts->ca_certs); + +cleanup: + if (is_ch(node)) { + /* UNLOCK */ + nc_ch_client_unlock(ch_client); + } + return ret; +} + +static int +nc_server_config_ee_certs(const struct lyd_node *node, enum nc_operation op) +{ + int ret = 0; + struct nc_server_tls_opts *opts; + struct nc_ch_client *ch_client = NULL; + + assert(!strcmp(LYD_NAME(node), "ee-certs")); + + /* only do something on delete and if we're in the TLS subtree, + * because SSH certs are not yet supported */ + if (!is_tls(node) || (op != NC_OP_DELETE)) { + return 0; + } + + /* 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, ch_client, &opts)) { + ret = 1; + goto cleanup; + } + + nc_server_config_del_certs(&opts->ee_certs); + +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, enum nc_operation op) +{ + int ret = 0; + struct nc_server_tls_opts *opts; + struct nc_ch_client *ch_client = NULL; + 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, ch_client, &opts)) { + ret = 1; + goto cleanup; + } + + if (equal_parent_name(node, 1, "central-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, ch_client, &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, ch_client, &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 = NULL; + uint32_t id; + NC_TLS_CTN_MAPTYPE m_type; + + assert(!strcmp(LYD_NAME(node), "cert-to-name")); + + /* find the list's key */ + lyd_find_path(node, "id", 0, &n); + assert(n); + id = ((struct lyd_node_term *)n)->value.uint32; + + /* get CTN map-type */ + if (lyd_find_path(node, "map-type", 0, &n)) { + ERR(NULL, "Missing CTN map-type."); + ret = 1; + goto cleanup; + } + map_type = ((struct lyd_node_term *)n)->value.ident->name; + if (!strcmp(map_type, "specified")) { + m_type = NC_TLS_CTN_SPECIFIED; + + /* get CTN name */ + if (lyd_find_path(node, "name", 0, &n)) { + ERR(NULL, "Missing CTN \"specified\" user name."); + ret = 1; + goto cleanup; + } + name = lyd_get_value(n); + } 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, "CTN map-type \"%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, replace */ + free(new); + new = iter; + free(new->name); + new->name = NULL; + } else { + new->next = iter->next; + iter->next = new; + } + } + + /* insert the right data */ + new->id = id; + if (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, enum nc_operation op) +{ + int ret = 0; + struct nc_server_tls_opts *opts; + struct nc_ctn *ctn; + struct nc_ch_client *ch_client = NULL; + + 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, ch_client, &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, ch_client, &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, enum nc_operation op) +{ + int ret = 0; + struct nc_ctn *ctn; + struct nc_ch_client *ch_client = NULL; + + 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, ch_client, &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, enum nc_operation op) +{ + int ret = 0; + struct nc_server_tls_opts *opts; + const char *version = NULL; + struct nc_ch_client *ch_client = NULL; + 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, ch_client, &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 *processed_cipher = NULL; + + ret = nc_tls_process_cipher_suite_wrap(cipher, &processed_cipher); + if (ret) { + ERR(NULL, "Failed to process the cipher suite \"%s\".", cipher); + goto cleanup; + } + + ret = nc_tls_append_cipher_suite_wrap(opts, processed_cipher); + if (ret) { + ERR(NULL, "Failed to append the cipher suite \"%s\".", cipher); + goto cleanup; + } + +cleanup: + free(processed_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, enum nc_operation op) +{ + int ret = 0; + struct nc_server_tls_opts *opts; + const char *cipher = NULL; + struct nc_ch_client *ch_client = NULL; + + 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, ch_client, &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; +} + +#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, enum nc_operation op) +{ + int ret = 0; + + 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) { + nc_server_config_ch_del_client(node); + } + +cleanup: + return ret; +} + +#ifdef NC_ENABLED_SSH_TLS + +static int +nc_server_config_remote_address(const struct lyd_node *node, enum nc_operation op) +{ + int ret = 0; + struct nc_ch_endpt *ch_endpt; + struct nc_ch_client *ch_client = NULL; + + 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_client, &ch_endpt)) { + ret = 1; + goto cleanup; + } + + if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) { + free(ch_endpt->dst_addr); + ch_endpt->dst_addr = strdup(lyd_get_value(node)); + NC_CHECK_ERRMEM_GOTO(!ch_endpt->dst_addr, ret = 1, cleanup); + } else { + free(ch_endpt->dst_addr); + ch_endpt->dst_addr = NULL; + } + +cleanup: + /* UNLOCK */ + nc_ch_client_unlock(ch_client); + return ret; +} + +static int +nc_server_config_remote_port(const struct lyd_node *node, enum nc_operation op) +{ + int ret = 0; + struct nc_ch_endpt *ch_endpt; + struct nc_ch_client *ch_client = NULL; + + 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_client, &ch_endpt)) { + ret = 1; + goto cleanup; + } + + if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) { + ch_endpt->dst_port = ((struct lyd_node_term *)node)->value.uint16; + } else { + ch_endpt->dst_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, enum nc_operation op) +{ + int ret = 0; + struct nc_ch_client *ch_client = NULL; + + assert(!strcmp(LYD_NAME(node), "persistent")); + + /* switch to periodic, don't do anything */ + if (op == NC_OP_DELETE) { + return 0; + } + + /* 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, enum nc_operation op) +{ + int ret = 0; + struct nc_ch_client *ch_client = NULL; + + assert(!strcmp(LYD_NAME(node), "periodic")); + + /* switch to persistent, don't do anything */ + if (op == NC_OP_DELETE) { + return 0; + } + + /* 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, enum nc_operation op) +{ + int ret = 0; + struct nc_ch_client *ch_client = NULL; + + 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, enum nc_operation op) +{ + int ret = 0; + struct nc_ch_client *ch_client = NULL; + 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, enum nc_operation op) +{ + int ret = 0; + struct nc_ch_client *ch_client = NULL; + + 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, enum nc_operation op) +{ + int ret = 0; + struct nc_ch_client *ch_client = NULL; + 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, enum nc_operation op) +{ + int ret = 0; + struct nc_ch_client *ch_client = NULL; + + 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, enum nc_operation op) +{ + int ret = 0; + struct nc_ch_client *ch_client = NULL; + + 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, enum nc_operation op) +{ + const char *name = LYD_NAME(node); + int ret = 0; + + if (!strcmp(name, "anchor-time")) { + ret = nc_server_config_anchor_time(node, op); + } else if (!strcmp(name, "call-home")) { + ret = nc_server_config_ch(node, op); + } else if (!strcmp(name, "endpoint")) { + ret = nc_server_config_endpoint(node, op); + } else if (!strcmp(name, "idle-timeout")) { + ret = nc_server_config_idle_timeout(node, op); + } else if (!strcmp(name, "listen")) { + ret = nc_server_config_listen(node, op); + } else if (!strcmp(name, "max-attempts")) { + ret = nc_server_config_max_attempts(node, op); + } else if (!strcmp(name, "max-wait")) { + ret = nc_server_config_max_wait(node, op); + } else if (!strcmp(name, "netconf-client")) { + ret = nc_server_config_netconf_client(node, op); + } else if (!strcmp(name, "period")) { + ret = nc_server_config_period(node, op); + } else if (!strcmp(name, "periodic")) { + ret = nc_server_config_periodic(node, op); + } else if (!strcmp(name, "persistent")) { + ret = nc_server_config_persistent(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); + } +#ifdef NC_ENABLED_SSH_TLS + else if (!strcmp(name, "auth-timeout")) { + ret = nc_server_config_auth_timeout(node, op); + } else if (!strcmp(name, "asymmetric-key")) { + ret = nc_server_config_asymmetric_key(node, op); + } else if (!strcmp(name, "banner")) { + ret = nc_server_config_banner(node, op); + } else if (!strcmp(name, "ca-certs")) { + ret = nc_server_config_ca_certs(node, op); + } else if (!strcmp(name, "central-keystore-reference")) { + ret = nc_server_config_keystore_reference(node, op); + } else if (!strcmp(name, "central-truststore-reference")) { + ret = nc_server_config_truststore_reference(node, op); + } else if (!strcmp(name, "cert-data")) { + ret = nc_server_config_cert_data(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, "cipher-suite")) { + ret = nc_server_config_cipher_suite(node, op); + } else if (!strcmp(name, "cleartext-private-key")) { + ret = nc_server_config_cleartext_private_key(node, op); + } else if (!strcmp(name, "client-authentication")) { + ret = nc_server_config_client_authentication(node, op); + } else if (!strcmp(name, "ee-certs")) { + ret = nc_server_config_ee_certs(node, op); + } else if (!strcmp(name, "encryption-alg")) { + ret = nc_server_config_encryption_alg(node, op); + } else if (!strcmp(name, "endpoint-reference")) { + ret = nc_server_config_endpoint_reference(node, op); + } else if (!strcmp(name, "fingerprint")) { + ret = nc_server_config_fingerprint(node, op); + } else if (!strcmp(name, "host-key")) { + ret = nc_server_config_host_key(node, op); + } else if (!strcmp(name, "host-key-alg")) { + ret = nc_server_config_host_key_alg(node, op); + } else if (!strcmp(name, "idle-time")) { + ret = nc_server_config_idle_time(node, op); + } else if (!strcmp(name, "keepalives")) { + ret = nc_server_config_keepalives(node, op); + } else if (!strcmp(name, "key-exchange-alg")) { + ret = nc_server_config_kex_alg(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, "mac-alg")) { + ret = nc_server_config_mac_alg(node, op); + } else if (!strcmp(name, "max-probes")) { + ret = nc_server_config_max_probes(node, op); + } else if (!strcmp(name, "none")) { + ret = nc_server_config_none(node, op); + } else if (!strcmp(name, "password")) { + ret = nc_server_config_password(node, op); + } else if (!strcmp(name, "private-key-format")) { + ret = nc_server_config_private_key_format(node, op); + } else if (!strcmp(name, "probe-interval")) { + ret = nc_server_config_probe_interval(node, op); + } else if (!strcmp(name, "public-key")) { + ret = nc_server_config_public_key(node, op); + } else if (!strcmp(name, "public-key-format")) { + ret = nc_server_config_public_key_format(node, op); + } else if (!strcmp(name, "public-keys")) { + ret = nc_server_config_public_keys(node, op); + } 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); + } else if (!strcmp(name, "ssh")) { + ret = nc_server_config_ssh(node, op); + } else if (!strcmp(name, "tls")) { + ret = nc_server_config_tls(node, op); + } else if (!strcmp(name, "tls-version")) { + ret = nc_server_config_tls_version(node, op); + } else if (!strcmp(name, "user")) { + ret = nc_server_config_user(node, op); + } else if (!strcmp(name, "use-system-auth")) { + ret = nc_server_config_use_system_auth(node, op); + } else if (!strcmp(name, "use-system-keys")) { + ret = nc_server_config_use_system_keys(node, op); + } +#endif /* NC_ENABLED_SSH_TLS */ + + if (ret) { + ERR(NULL, "Configuring node \"%s\" failed.", LYD_NAME(node)); + return 1; + } + + return 0; +} + +int +nc_server_config_ln2_netconf_server(const struct lyd_node *node, enum nc_operation op) +{ + (void) node; + + assert((op == NC_OP_CREATE) || (op == NC_OP_DELETE)); + + if (op == NC_OP_DELETE) { + +#ifdef NC_ENABLED_SSH_TLS + /* delete the intervals */ + pthread_mutex_lock(&server_opts.cert_exp_notif.lock); + free(server_opts.cert_exp_notif.intervals); + server_opts.cert_exp_notif.intervals = NULL; + server_opts.cert_exp_notif.interval_count = 0; + pthread_mutex_unlock(&server_opts.cert_exp_notif.lock); +#endif /* NC_ENABLED_SSH_TLS */ + + } + + return 0; +} + +#ifdef NC_ENABLED_SSH_TLS + +static int +nc_server_config_yang_value2cert_exp_time(const char *value, struct nc_cert_exp_time *cert_exp_time) +{ + char unit; + long val; + + unit = value[strlen(value) - 1]; + val = strtol(value, NULL, 10); + switch (unit) { + case 'm': + cert_exp_time->months = val; + break; + case 'w': + cert_exp_time->weeks = val; + break; + case 'd': + cert_exp_time->days = val; + break; + case 'h': + cert_exp_time->hours = val; + break; + default: + ERR(NULL, "Unexpected unit in the certificate expiration time \"%s\".", value); + return 1; + } + + return 0; +} + +static int +nc_server_config_create_interval(const char *anchor, const char *period) +{ + int ret = 0; + struct nc_cert_exp_time anchor_time = {0}, period_time = {0}; + + server_opts.cert_exp_notif.intervals = nc_realloc(server_opts.cert_exp_notif.intervals, + (server_opts.cert_exp_notif.interval_count + 1) * sizeof *server_opts.cert_exp_notif.intervals); + NC_CHECK_ERRMEM_RET(!server_opts.cert_exp_notif.intervals, 1); + + /* convert and set the anchor */ + ret = nc_server_config_yang_value2cert_exp_time(anchor, &anchor_time); + if (ret) { + goto cleanup; + } + server_opts.cert_exp_notif.intervals[server_opts.cert_exp_notif.interval_count].anchor = anchor_time; + + /* convert and set the period */ + ret = nc_server_config_yang_value2cert_exp_time(period, &period_time); + if (ret) { + goto cleanup; + } + server_opts.cert_exp_notif.intervals[server_opts.cert_exp_notif.interval_count].period = period_time; + + ++server_opts.cert_exp_notif.interval_count; + +cleanup: + return ret; +} + +static void +nc_server_config_del_interval(const char *anchor, const char *period) +{ + int i; + struct nc_cert_exp_time anchor_time = {0}, period_time = {0}; + + if (nc_server_config_yang_value2cert_exp_time(anchor, &anchor_time)) { + return; + } + if (nc_server_config_yang_value2cert_exp_time(period, &period_time)) { + return; + } + + for (i = 0; i < server_opts.cert_exp_notif.interval_count; ++i) { + if (!memcmp(&server_opts.cert_exp_notif.intervals[i].anchor, &anchor_time, sizeof anchor_time) && + !memcmp(&server_opts.cert_exp_notif.intervals[i].period, &period_time, sizeof period_time)) { + break; + } + } + if (i == server_opts.cert_exp_notif.interval_count) { + ERR(NULL, "Interval \"%s %s\" not found.", anchor, period); + return; + } + + server_opts.cert_exp_notif.interval_count--; + if (!server_opts.cert_exp_notif.interval_count) { + free(server_opts.cert_exp_notif.intervals); + server_opts.cert_exp_notif.intervals = NULL; + } else if (i != server_opts.cert_exp_notif.interval_count) { + server_opts.cert_exp_notif.intervals[i] = + server_opts.cert_exp_notif.intervals[server_opts.cert_exp_notif.interval_count]; + } +} + +static int +nc_server_config_interval(const struct lyd_node *node, enum nc_operation op) +{ + int ret = 0; + struct lyd_node *anchor, *period; + + assert(!strcmp(LYD_NAME(node), "interval")); + + anchor = lyd_child(node); + assert(anchor); + period = anchor->next; + assert(period); + + /* LOCK */ + pthread_mutex_lock(&server_opts.cert_exp_notif.lock); + + if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) { + ret = nc_server_config_create_interval(lyd_get_value(anchor), lyd_get_value(period)); + if (ret) { + goto cleanup; + } + } else { + nc_server_config_del_interval(lyd_get_value(anchor), lyd_get_value(period)); + } + +cleanup: + pthread_mutex_unlock(&server_opts.cert_exp_notif.lock); + return ret; +} + +#endif /* NC_ENABLED_SSH_TLS */ + +static int +nc_server_config_parse_libnetconf2_netconf_server(const struct lyd_node *node, enum nc_operation op) +{ + const char *name = LYD_NAME(node); + int ret = 0; + + if (!strcmp(name, "ln2-netconf-server")) { + ret = nc_server_config_ln2_netconf_server(node, op); + } +#ifdef NC_ENABLED_SSH_TLS + else if (!strcmp(name, "interval")) { + ret = nc_server_config_interval(node, op); + } +#endif /* NC_ENABLED_SSH_TLS */ + + 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, enum nc_operation parent_op, NC_MODULE module) +{ + struct lyd_node *child; + struct lyd_meta *m; + enum 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 */ + if (module == NC_MODULE_LIBNETCONF2_NETCONF_SERVER) { + ret = nc_server_config_parse_libnetconf2_netconf_server(node, current_op); + } else if (module == NC_MODULE_NETCONF_SERVER) { + ret = nc_server_config_parse_netconf_server(node, current_op); + } else { + ERRINT; + ret = 1; + } + 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 */ + const char *ietf_tcp_client[] = {"local-binding-supported", "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_netconf_server(const struct lyd_node *data, enum 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; +} + +static int +nc_server_config_fill_libnetconf2_netconf_server(const struct lyd_node *data, enum nc_operation op) +{ + int ret = 0; + struct lyd_node *tree; + uint32_t log_options = 0; + + /* silently search for ln2-netconf-server, it may not be present */ + ly_temp_log_options(&log_options); + + ret = lyd_find_path(data, "/libnetconf2-netconf-server:ln2-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_LIBNETCONF2_NETCONF_SERVER)) { + ret = 1; + goto cleanup; + } + +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, "Applying ietf-keystore configuration failed."); + goto cleanup; + } + + /* configure truststore */ + ret = nc_server_config_fill_truststore(data, NC_OP_UNKNOWN); + if (ret) { + ERR(NULL, "Applying ietf-truststore configuration failed."); + goto cleanup; + } +#endif /* NC_ENABLED_SSH_TLS */ + + /* configure netconf-server */ + ret = nc_server_config_fill_netconf_server(data, NC_OP_UNKNOWN); + if (ret) { + ERR(NULL, "Applying ietf-netconf-server configuration failed."); + goto cleanup; + } + + /* configure libnetconf2-netconf-server */ + ret = nc_server_config_fill_libnetconf2_netconf_server(data, NC_OP_UNKNOWN); + if (ret) { + ERR(NULL, "Applying libnetconf2-netconf-server configuration failed."); + goto cleanup; + } + +#ifdef NC_ENABLED_SSH_TLS + /* wake up the cert expiration notif thread if it's running */ + pthread_mutex_lock(&server_opts.cert_exp_notif.lock); + if (server_opts.cert_exp_notif.thread_running) { + pthread_cond_signal(&server_opts.cert_exp_notif.cond); + } + pthread_mutex_unlock(&server_opts.cert_exp_notif.lock); +#endif /* NC_ENABLED_SSH_TLS */ + +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, "Applying ietf-keystore configuration failed."); + goto cleanup; + } + + /* configure truststore */ + ret = nc_server_config_fill_truststore(data, NC_OP_CREATE); + if (ret) { + ERR(NULL, "Applying ietf-truststore configuration failed."); + goto cleanup; + } +#endif /* NC_ENABLED_SSH_TLS */ + + /* configure netconf-server */ + ret = nc_server_config_fill_netconf_server(data, NC_OP_CREATE); + if (ret) { + ERR(NULL, "Applying ietf-netconf-server configuration failed."); + goto cleanup; + } + + /* configure libnetconf2-netconf-server */ + ret = nc_server_config_fill_libnetconf2_netconf_server(data, NC_OP_CREATE); + if (ret) { + ERR(NULL, "Applying libnetconf2-netconf-server configuration failed."); + goto cleanup; + } + +#ifdef NC_ENABLED_SSH_TLS + /* wake up the cert expiration notif thread if it's running */ + pthread_mutex_lock(&server_opts.cert_exp_notif.lock); + if (server_opts.cert_exp_notif.thread_running) { + pthread_cond_signal(&server_opts.cert_exp_notif.cond); + } + pthread_mutex_unlock(&server_opts.cert_exp_notif.lock); +#endif /* NC_ENABLED_SSH_TLS */ + +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; +} + +#ifdef NC_ENABLED_SSH_TLS + +static int +nc_server_config_oper_get_algs(const struct ly_ctx *ctx, const char *mod_name, const char *ln2_algs[], + const char *mod_algs[], struct lyd_node **algs) +{ + int ret, r, i; + struct lyd_node *parent = NULL; + char *path = NULL; + + NC_CHECK_ARG_RET(NULL, ctx, mod_name, ln2_algs, mod_algs, algs, 1); + + *algs = NULL; + + r = asprintf(&path, "/%s:supported-algorithms", mod_name); + NC_CHECK_ERRMEM_RET(r == -1, 1); + + /* create supported algorithms container */ + ret = lyd_new_path(NULL, ctx, path, NULL, 0, &parent); + free(path); + if (ret) { + ERR(NULL, "Creating supported algorithms container failed."); + goto cleanup; + } + + /* append algs from libnetconf2-netconf-server */ + for (i = 0; ln2_algs[i]; i++) { + r = asprintf(&path, "libnetconf2-netconf-server:%s", ln2_algs[i]); + NC_CHECK_ERRMEM_GOTO(r == -1, ret = 1, cleanup); + ret = lyd_new_term(parent, NULL, "supported-algorithm", path, 0, NULL); + free(path); + if (ret) { + ERR(NULL, "Creating new supported algorithm failed."); + goto cleanup; + } + } + + /* append algs from mod_name module */ + for (i = 0; mod_algs[i]; i++) { + r = asprintf(&path, "%s:%s", mod_name, mod_algs[i]); + NC_CHECK_ERRMEM_GOTO(r == -1, ret = 1, cleanup); + ret = lyd_new_term(parent, NULL, "supported-algorithm", path, 0, NULL); + free(path); + if (ret) { + ERR(NULL, "Creating new supported algorithm failed."); + goto cleanup; + } + } + +cleanup: + if (ret) { + lyd_free_tree(parent); + } else { + *algs = parent; + } + return ret; +} + +API int +nc_server_config_oper_get_hostkey_algs(const struct ly_ctx *ctx, struct lyd_node **hostkey_algs) +{ + /* identities of hostkey algs supported by libssh (v0.10.5) defined in libnetconf2-netconf-server yang module */ + const char *libnetconf2_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", NULL + }; + + /* identities of hostkey algs supported by libssh (v0.10.5) defined in iana-ssh-public-key-algs yang module */ + const char *iana_hostkey_algs[] = { + "ssh-ed25519", "ecdsa-sha2-nistp521", "ecdsa-sha2-nistp384", "ecdsa-sha2-nistp256", + "rsa-sha2-512", "rsa-sha2-256", "ssh-rsa", "ssh-dss", NULL + }; + + NC_CHECK_ARG_RET(NULL, ctx, hostkey_algs, 1); + + return nc_server_config_oper_get_algs(ctx, "iana-ssh-public-key-algs", libnetconf2_hostkey_algs, + iana_hostkey_algs, hostkey_algs); +} + +API int +nc_server_config_oper_get_kex_algs(const struct ly_ctx *ctx, struct lyd_node **kex_algs) +{ + /* identities of kex algs supported by libssh (v0.10.5) defined in libnetconf2-netconf-server yang module */ + const char *libnetconf2_kex_algs[] = { + "libssh-curve25519-sha256", NULL + }; + + /* identities of kex algs supported by libssh (v0.10.5) defined in iana-ssh-key-exchange-algs yang module */ + const char *iana_kex_algs[] = { + "diffie-hellman-group-exchange-sha1", "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 + }; + + NC_CHECK_ARG_RET(NULL, ctx, kex_algs, 1); + + return nc_server_config_oper_get_algs(ctx, "iana-ssh-key-exchange-algs", libnetconf2_kex_algs, + iana_kex_algs, kex_algs); +} + +API int +nc_server_config_oper_get_encryption_algs(const struct ly_ctx *ctx, struct lyd_node **encryption_algs) +{ + /* identities of encryption algs supported by libssh (v0.10.5) defined in libnetconf2-netconf-server yang module */ + const char *libnetconf2_encryption_algs[] = { + "openssh-chacha20-poly1305", "openssh-aes256-gcm", "openssh-aes128-gcm", NULL + }; + + /* identities of encryption algs supported by libssh (v0.10.5) defined in iana-ssh-encryption-algs yang module */ + const char *iana_encryption_algs[] = { + "aes256-ctr", "aes192-ctr", "aes128-ctr", "aes256-cbc", "aes192-cbc", "aes128-cbc", + "blowfish-cbc", "triple-des-cbc", "none", NULL + }; + + NC_CHECK_ARG_RET(NULL, ctx, encryption_algs, 1); + + return nc_server_config_oper_get_algs(ctx, "iana-ssh-encryption-algs", libnetconf2_encryption_algs, + iana_encryption_algs, encryption_algs); +} + +API int +nc_server_config_oper_get_mac_algs(const struct ly_ctx *ctx, struct lyd_node **mac_algs) +{ + /* identities of mac algs supported by libssh (v0.10.5) defined in libnetconf2-netconf-server yang module */ + const char *libnetconf2_mac_algs[] = { + "openssh-hmac-sha2-256-etm", "openssh-hmac-sha2-512-etm", "openssh-hmac-sha1-etm", NULL + }; + + /* identities of mac algs supported by libssh (v0.10.5) defined in iana-ssh-mac-algs yang module */ + const char *iana_mac_algs[] = { + "hmac-sha2-256", "hmac-sha2-512", "hmac-sha1", NULL + }; + + NC_CHECK_ARG_RET(NULL, ctx, mac_algs, 1); + + return nc_server_config_oper_get_algs(ctx, "iana-ssh-mac-algs", libnetconf2_mac_algs, + iana_mac_algs, mac_algs); +} + +#endif /* NC_ENABLED_SSH_TLS */ diff --git a/src/server_config.h b/src/server_config.h new file mode 100644 index 0000000..63d4784 --- /dev/null +++ b/src/server_config.h @@ -0,0 +1,1442 @@ +/** + * @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. + * + * Note that the SSH authentication depends on the value of the 'local-users-supported' feature in the ietf-ssh-server module. + * If the feature, and its dependent if-features, are disabled, the SSH authentication will use the system users. + * Otherwise, the SSH authentication will use the local users from the configuration (the default). + * + * @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 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); + +/** + * @brief Gets the hostkey algorithms supported by the server from the 'iana-ssh-public-key-algs' YANG module. + * + * @param[in] ctx libyang context. + * @param[out] hostkey_algs Container with leaf-lists containing the supported algorithms. + * @return 0 on success, non-zero otherwise. + */ +int nc_server_config_oper_get_hostkey_algs(const struct ly_ctx *ctx, struct lyd_node **hostkey_algs); + +/** + * @brief Gets the key exchange algorithms supported by the server from the 'iana-ssh-key-exchange-algs' YANG module. + * + * @param[in] ctx libyang context. + * @param[out] kex_algs Container with leaf-lists containing the supported algorithms. + * @return 0 on success, non-zero otherwise. + */ +int nc_server_config_oper_get_kex_algs(const struct ly_ctx *ctx, struct lyd_node **kex_algs); + +/** + * @brief Gets the encryption algorithms supported by the server from the 'iana-ssh-encryption-algs' YANG module. + * + * @param[in] ctx libyang context. + * @param[out] encryption_algs Container with leaf-lists containing the supported algorithms. + * @return 0 on success, non-zero otherwise. + */ +int nc_server_config_oper_get_encryption_algs(const struct ly_ctx *ctx, struct lyd_node **encryption_algs); + +/** + * @brief Gets the MAC algorithms supported by the server from the 'iana-ssh-mac-algs' YANG module. + * + * @param[in] ctx libyang context. + * @param[out] mac_algs Container with leaf-lists containing the supported algorithms. + * @return 0 on success, non-zero otherwise. + */ +int nc_server_config_oper_get_mac_algs(const struct ly_ctx *ctx, struct lyd_node **mac_algs); + +/** + * @} 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 that will use system's authorized_keys to authenticate. + * + * The path to the authorized_keys file must be configured to successfully + * authenticate, see ::nc_server_ssh_set_authkey_path_format(). + * + * @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,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_authkey(const struct ly_ctx *ctx, const char *endpt_name, + const char *user_name, struct lyd_node **config); + +/** + * @brief Deletes an SSH user's authorized_keys method 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_authkey(const char *endpt_name, const char *user_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. + * + * One of Linux PAM, local users, or user callback is used to authenticate users with this SSH method (see \ref ln2doc_kbdint "the documentation"). + * + * @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,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, 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. + * + * A client certificate is authenticated if it is an exact match to a configured client certificate. + * + * @param[in] ctx libyang context. + * @param[in] endpt_name Arbitrary identifier of the endpoint. + * If an endpoint with this identifier already exists, its contents will be changed. + * @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. + * + * A client certificate is authenticated if it has a valid chain of trust to any configured CA cert. + * The configured CA cert, up to which the valid chain of trust can be built, does not have to be + * self-signed (the root CA). That means that the chain may be incomplete, yet the client will be authenticated. + * + * For example assume a certificate chain + * A <- B <- C, + * where A is the root CA, then the client certificate C will be authenticated either + * if solely B is configured, or if both A and B are configured. C will not be authenticated + * if just A is configured as a CA certificate. + * + * @param[in] ctx libyang context. + * @param[in] endpt_name Arbitrary identifier of the endpoint. + * If an endpoint with this identifier already exists, its contents will be changed. + * @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 configuration data nodes for a Call Home SSH user that will use system's authorized_keys to authenticate. + * + * The path to the authorized_keys file must be configured to successfully + * authenticate, see ::nc_server_ssh_set_authkey_path_format(). + * + * @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,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_authkey(const struct ly_ctx *ctx, const char *client_name, + const char *endpt_name, const char *user_name, struct lyd_node **config); + +/** + * @brief Deletes a Call Home SSH user's authorized_keys method 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 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_ch_del_ssh_user_authkey(const char *client_name, const char *endpt_name, + const char *user_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. + * + * One of Linux PAM, local users, or user callback is used to authenticate users with this SSH method (see \ref ln2doc_kbdint "the documentation"). + * + * @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,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, 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 0000000..0194c70 --- /dev/null +++ b/src/server_config_ks.c @@ -0,0 +1,445 @@ +/** + * @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" + +/** + * @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, enum 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, enum 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, enum 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, enum 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, enum 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, enum nc_operation op) +{ + struct nc_asymmetric_key *key; + const char *format; + enum 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, enum 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, enum 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, enum 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, enum 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, enum 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 0000000..d52f4dc --- /dev/null +++ b/src/server_config_p.h @@ -0,0 +1,169 @@ +/** + * @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_LIBNETCONF2_NETCONF_SERVER +} 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. + */ +enum 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, enum 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, enum 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, enum 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, enum 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, enum 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, enum 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, enum 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, enum 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, enum nc_operation op); + +/** LIBNETCONF2-NETCONF-SERVER **/ + +/** + * @brief Configures the ln2-netconf-server subtree in the libnetconf2-netconf-server module. + * + * @param[in] node Optional ln2-netconf-server 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_ln2_netconf_server(const struct lyd_node *node, enum 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 0000000..62528d7 --- /dev/null +++ b/src/server_config_ts.c @@ -0,0 +1,600 @@ +/** + * @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" + +/** + * @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, enum 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, enum 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, enum 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, enum 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, enum 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, enum 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, enum 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, enum 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, enum 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, enum 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, enum 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 0000000..3938c47 --- /dev/null +++ b/src/server_config_util.c @@ -0,0 +1,1366 @@ +/** + * @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 + +#include "compat.h" +#include "log_p.h" +#include "session.h" +#include "session_p.h" +#include "session_wrapper.h" + +int +nc_server_config_create(const struct ly_ctx *ctx, struct lyd_node **tree, const char *value, const char *path_fmt, ...) +{ + 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(enum 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_rsa_pubkey_param_to_bin(void *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; + + /* convert to binary */ + if (nc_tls_mpi2bin_wrap(bn, &bin_tmp, bin_len)) { + ret = 1; + goto cleanup; + } + + /* if the highest bit in the MSB is set a byte with the value 0 has to be prepended */ + if (bin_tmp[0] & 0x80) { + *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_pkey_to_ssh_pubkey(void *pkey, char **pubkey) +{ + int ret = 0, e_len, n_len, p_len, bin_len; + void *e = NULL, *n = NULL, *p = NULL, *p_grp = NULL; + 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; + + NC_CHECK_ARG_RET(NULL, pkey, pubkey, 1); + + if (nc_tls_privkey_is_rsa_wrap(pkey)) { + /* RSA key */ + algorithm_name = "ssh-rsa"; + + /* get the public key params */ + if (nc_tls_get_rsa_pubkey_params_wrap(pkey, &e, &n)) { + ret = 1; + goto cleanup; + } + + /* BIGNUM to bin */ + if (nc_server_config_util_rsa_pubkey_param_to_bin(e, &e_bin, &e_len) || + nc_server_config_util_rsa_pubkey_param_to_bin(n, &n_bin, &n_len)) { + ret = 1; + goto cleanup; + } + + 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 (nc_tls_privkey_is_ec_wrap(pkey)) { + /* EC Private key, get it's group first */ + ec_group = nc_tls_get_ec_group_wrap(pkey); + if (!ec_group) { + 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 = nc_tls_get_ec_pubkey_params_wrap(pkey, &p, &p_grp); + if (ret) { + ERR(NULL, "Getting public key point from the EC private key failed."); + ret = 1; + goto cleanup; + } + + /* EC point to bin */ + ret = nc_tls_ec_point_to_bin_wrap(p, p_grp, &p_bin, &p_len); + if (ret) { + ERR(NULL, "Converting EC public key point to binary failed."); + ret = 1; + goto cleanup; + } + + alg_name_len = strlen(algorithm_name); + curve_name_len = strlen(curve_name); + /* buffer for public key in binary, which looks like so: + * 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 { + 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_base64_encode_wrap(bin, bin_len, pubkey); + if (ret) { + ERR(NULL, "Converting public key from binary to base64 failed."); + goto cleanup; + } + +cleanup: + nc_tls_destroy_mpi_wrap(e); + nc_tls_destroy_mpi_wrap(n); + nc_tls_ec_point_destroy_wrap(p); + nc_tls_ec_group_destroy_wrap(p_grp); + free(bin); + free(e_bin); + free(n_bin); + free(ec_group); + free(p_bin); + return ret; +} + +/* spki = subject public key info */ +static int +nc_server_config_util_pkey_to_spki_pubkey(void *pkey, char **pubkey) +{ + int ret = 0; + char *pub_pem = NULL; + + NC_CHECK_ARG_RET(NULL, pkey, pubkey, 1); + + pub_pem = nc_tls_export_pubkey_pem_wrap(pkey); + if (!pub_pem) { + ret = 1; + goto cleanup; + } + + /* copy the public key without the header and footer */ + *pubkey = strndup(pub_pem + strlen(NC_SUBJECT_PUBKEY_INFO_HEADER), + strlen(pub_pem) - strlen(NC_SUBJECT_PUBKEY_INFO_HEADER) - strlen(NC_SUBJECT_PUBKEY_INFO_FOOTER)); + NC_CHECK_ERRMEM_GOTO(!*pubkey, ret = 1, cleanup); + +cleanup: + free(pub_pem); + return ret; +} + +int +nc_server_config_util_read_certificate(const char *cert_path, char **cert) +{ + int ret = 0; + void *crt = NULL; + char *pem = NULL; + + NC_CHECK_ARG_RET(NULL, cert_path, cert, 1); + + crt = nc_tls_import_cert_file_wrap(cert_path); + if (!crt) { + return 1; + } + + pem = nc_tls_export_cert_pem_wrap(crt); + if (!pem) { + ret = 1; + goto cleanup; + } + + /* copy the cert without its header and footer */ + *cert = strndup(pem + strlen(NC_PEM_CERTIFICATE_HEADER), + strlen(pem) - strlen(NC_PEM_CERTIFICATE_HEADER) - strlen(NC_PEM_CERTIFICATE_FOOTER)); + NC_CHECK_ERRMEM_GOTO(!*cert, ret = 1, cleanup); + +cleanup: + free(pem); + nc_tls_cert_destroy_wrap(crt); + return ret; +} + +static int +nc_server_config_util_read_ssh2_pubkey(const char *pubkey_path, char **pubkey) +{ + char *buffer = NULL; + size_t size = 0, pubkey_len = 0; + void *tmp; + ssize_t read; + int ret = 0; + FILE *f = NULL; + + NC_CHECK_ARG_RET(NULL, pubkey_path, pubkey, 1); + + f = fopen(pubkey_path, "r"); + if (!f) { + ERR(NULL, "Failed to open file \"%s\".", pubkey_path); + ret = 1; + goto cleanup; + } + + /* read lines from the file and create the public key without NL from it */ + while ((read = getline(&buffer, &size, f)) > 0) { + 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: + if (f) { + fclose(f); + } + free(buffer); + return ret; +} + +static int +nc_server_config_util_read_spki_pubkey(const char *pubkey_path, char **pubkey) +{ + int ret = 0; + void *pub_pkey = NULL; + + NC_CHECK_ARG_RET(NULL, pubkey_path, pubkey, 1); + + /* read the pubkey from file */ + pub_pkey = nc_tls_import_pubkey_file_wrap(pubkey_path); + if (!pub_pkey) { + return 1; + } + + ret = nc_server_config_util_pkey_to_ssh_pubkey(pub_pkey, pubkey); + nc_tls_privkey_destroy_wrap(pub_pkey); + return ret; +} + +static int +nc_server_config_util_read_openssh_pubkey(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 */ + ret = getline(&header, &len, f); + fclose(f); + if (ret < 0) { + ERR(NULL, "Error reading header from file \"%s\".", pubkey_path); + ret = 1; + goto cleanup; + } + + 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_spki_pubkey(pubkey_path, pubkey); + } else if (!strncmp(header, NC_SSH2_PUBKEY_HEADER, strlen(NC_SSH2_PUBKEY_HEADER))) { + /* it's ssh2 public key */ + ret = nc_server_config_util_read_ssh2_pubkey(pubkey_path, pubkey); + } else { + /* it's probably OpenSSH public key */ + ret = nc_server_config_util_read_openssh_pubkey(pubkey_path, pubkey); + } + if (ret) { + ERR(NULL, "Error getting public key from file \"%s\".", pubkey_path); + goto cleanup; + } + +cleanup: + free(header); + return ret; +} + +int +nc_server_config_util_get_spki_pubkey_file(const char *pubkey_path, char **pubkey) +{ + int ret = 0; + void *pkey = NULL; + + NC_CHECK_ARG_RET(NULL, pubkey_path, pubkey, 1); + + *pubkey = NULL; + + pkey = nc_tls_import_pubkey_file_wrap(pubkey_path); + if (!pkey) { + return 1; + } + + ret = nc_server_config_util_pkey_to_spki_pubkey(pkey, pubkey); + if (ret) { + goto cleanup; + } + +cleanup: + nc_tls_privkey_destroy_wrap(pkey); + return ret; +} + +static int +nc_server_config_util_get_privkey_format(const char *privkey, enum nc_privkey_format *privkey_format) +{ + NC_CHECK_ARG_RET(NULL, privkey, privkey_format, 1); + + if (!strncmp(privkey, NC_PKCS8_PRIVKEY_HEADER, strlen(NC_PKCS8_PRIVKEY_HEADER))) { + /* it's PKCS8 (X.509) private key */ + *privkey_format = NC_PRIVKEY_FORMAT_X509; + } else if (!strncmp(privkey, NC_OPENSSH_PRIVKEY_HEADER, strlen(NC_OPENSSH_PRIVKEY_HEADER))) { + /* it's OpenSSH private key */ + *privkey_format = NC_PRIVKEY_FORMAT_OPENSSH; + } else if (!strncmp(privkey, NC_PKCS1_RSA_PRIVKEY_HEADER, strlen(NC_PKCS1_RSA_PRIVKEY_HEADER))) { + /* it's RSA privkey in PKCS1 format */ + *privkey_format = NC_PRIVKEY_FORMAT_RSA; + } else if (!strncmp(privkey, 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 { + /* not supported */ + return 1; + } + + return 0; +} + +static int +nc_server_config_util_get_privkey_libtls(const char *privkey_path, char **privkey, void **pkey) +{ + void *pkey_tmp; + char *privkey_tmp; + + NC_CHECK_ARG_RET(NULL, privkey_path, privkey, pkey, 1); + + *privkey = *pkey = NULL; + + pkey_tmp = nc_tls_import_privkey_file_wrap(privkey_path); + if (!pkey_tmp) { + return 1; + } + + privkey_tmp = nc_tls_export_privkey_pem_wrap(pkey_tmp); + if (!privkey_tmp) { + nc_tls_privkey_destroy_wrap(pkey_tmp); + return 1; + } + + *privkey = privkey_tmp; + *pkey = pkey_tmp; + return 0; +} + +static int +nc_server_config_util_get_privkey_libssh(const char *privkey_path, char **privkey, void **pkey) +{ + int ret = 0; + ssh_key key = NULL; + void *pkey_tmp = NULL; + char *privkey_tmp = NULL; + + NC_CHECK_ARG_RET(NULL, privkey_path, privkey, pkey, 1); + + ret = ssh_pki_import_privkey_file(privkey_path, NULL, NULL, NULL, &key); + if (ret) { + ERR(NULL, "Importing privkey from file \"%s\" failed.", privkey_path); + ret = 1; + goto cleanup; + } + + /* export the key in PEM */ + ret = ssh_pki_export_privkey_base64(key, NULL, NULL, NULL, &privkey_tmp); + if (ret) { + ERR(NULL, "Exporting privkey to base64 failed."); + goto cleanup; + } + + pkey_tmp = nc_tls_pem_to_privkey_wrap(privkey_tmp); + if (!pkey_tmp) { + free(privkey_tmp); + ret = 1; + goto cleanup; + } + + *privkey = privkey_tmp; + *pkey = pkey_tmp; + +cleanup: + ssh_key_free(key); + return ret; +} + +static int +nc_server_config_util_pem_strip_header_footer(const char *pem, char **privkey) +{ + const char *header, *footer; + + if (!strncmp(pem, NC_PKCS8_PRIVKEY_HEADER, strlen(NC_PKCS8_PRIVKEY_HEADER))) { + /* it's PKCS8 (X.509) private key */ + header = NC_PKCS8_PRIVKEY_HEADER; + footer = NC_PKCS8_PRIVKEY_FOOTER; + } else if (!strncmp(pem, NC_OPENSSH_PRIVKEY_HEADER, strlen(NC_OPENSSH_PRIVKEY_HEADER))) { + /* it's OpenSSH private key */ + header = NC_OPENSSH_PRIVKEY_HEADER; + footer = NC_OPENSSH_PRIVKEY_FOOTER; + } else if (!strncmp(pem, NC_PKCS1_RSA_PRIVKEY_HEADER, strlen(NC_PKCS1_RSA_PRIVKEY_HEADER))) { + /* it's RSA privkey in PKCS1 format */ + header = NC_PKCS1_RSA_PRIVKEY_HEADER; + footer = NC_PKCS1_RSA_PRIVKEY_FOOTER; + } else if (!strncmp(pem, NC_SEC1_EC_PRIVKEY_HEADER, strlen(NC_SEC1_EC_PRIVKEY_HEADER))) { + /* it's EC privkey in SEC1 format */ + header = NC_SEC1_EC_PRIVKEY_HEADER; + footer = NC_SEC1_EC_PRIVKEY_FOOTER; + } else { + return 1; + } + + /* make a copy without the header and footer */ + *privkey = strndup(pem + strlen(header), strlen(pem) - strlen(header) - strlen(footer)); + NC_CHECK_ERRMEM_RET(!*privkey, 1); + + return 0; +} + +static int +nc_server_config_util_get_privkey(const char *privkey_path, enum nc_privkey_format *privkey_format, char **privkey, void **pkey) +{ + int ret = 0; + FILE *f_privkey = NULL; + char *priv = NULL; + char *privkey_header = NULL; + size_t header_len = 0; + + NC_CHECK_ARG_RET(NULL, privkey_path, privkey_format, privkey, pkey, 1); + + f_privkey = fopen(privkey_path, "r"); + if (!f_privkey) { + ERR(NULL, "Unable to open file \"%s\".", privkey_path); + ret = 1; + goto cleanup; + } + + /* read privkey header */ + if (getline(&privkey_header, &header_len, f_privkey) < 0) { + ERR(NULL, "Error reading header from file \"%s\".", privkey_path); + ret = 1; + goto cleanup; + } + + /* get privkey format */ + ret = nc_server_config_util_get_privkey_format(privkey_header, privkey_format); + if (ret) { + ERR(NULL, "Private key format \"%s\" not supported.", privkey_header); + goto cleanup; + } + + /* decide how to parse it based on the format */ + switch (*privkey_format) { + /* fall-through */ + case NC_PRIVKEY_FORMAT_RSA: + case NC_PRIVKEY_FORMAT_EC: + case NC_PRIVKEY_FORMAT_X509: + /* the TLS lib can do this */ + ret = nc_server_config_util_get_privkey_libtls(privkey_path, &priv, pkey); + break; + case NC_PRIVKEY_FORMAT_OPENSSH: + /* need the help of libssh */ + 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; + } + + /* parsing may have changed its type, get it again */ + ret = nc_server_config_util_get_privkey_format(priv, privkey_format); + if (ret) { + ERR(NULL, "Getting private key format from file \"%s\" failed.", privkey_path); + goto cleanup; + } + + /* strip private key's header and footer */ + ret = nc_server_config_util_pem_strip_header_footer(priv, privkey); + if (ret) { + ERR(NULL, "Stripping header and footer from private key \"%s\" failed.", privkey_path); + goto cleanup; + } + +cleanup: + if (f_privkey) { + fclose(f_privkey); + } + + free(privkey_header); + free(priv); + return ret; +} + +int +nc_server_config_util_get_asym_key_pair(const char *privkey_path, const char *pubkey_path, enum nc_pubkey_format wanted_pubkey_format, + char **privkey, enum nc_privkey_format *privkey_type, char **pubkey) +{ + int ret = 0; + void *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, &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_pkey_to_ssh_pubkey(pkey, pubkey); + } else { + ret = nc_server_config_util_pkey_to_spki_pubkey(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: + nc_tls_privkey_destroy_wrap(pkey); + return ret; +} + +API int +nc_server_config_add_address_port(const struct ly_ctx *ctx, const char *endpt_name, NC_TRANSPORT_IMPL transport, + const char *address, uint16_t port, struct lyd_node **config) +{ + 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_SSH) { + /* SSH path */ + address_fmt = "/ietf-netconf-server:netconf-server/listen/endpoints/endpoint[name='%s']/ssh/tcp-server-parameters/local-address"; + port_fmt = "/ietf-netconf-server:netconf-server/listen/endpoints/endpoint[name='%s']/ssh/tcp-server-parameters/local-port"; + } else if (transport == NC_TI_TLS) { + /* TLS path */ + address_fmt = "/ietf-netconf-server:netconf-server/listen/endpoints/endpoint[name='%s']/tls/tcp-server-parameters/local-address"; + port_fmt = "/ietf-netconf-server:netconf-server/listen/endpoints/endpoint[name='%s']/tls/tcp-server-parameters/local-port"; + } 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_SSH) { + /* SSH path */ + address_fmt = "/ietf-netconf-server:netconf-server/call-home/netconf-client[name='%s']/endpoints/endpoint[name='%s']/ssh/tcp-client-parameters/remote-address"; + port_fmt = "/ietf-netconf-server:netconf-server/call-home/netconf-client[name='%s']/endpoints/endpoint[name='%s']/ssh/tcp-client-parameters/remote-port"; + } else if (transport == NC_TI_TLS) { + /* TLS path */ + address_fmt = "/ietf-netconf-server:netconf-server/call-home/netconf-client[name='%s']/endpoints/endpoint[name='%s']/tls/tcp-client-parameters/remote-address"; + port_fmt = "/ietf-netconf-server:netconf-server/call-home/netconf-client[name='%s']/endpoints/endpoint[name='%s']/tls/tcp-client-parameters/remote-port"; + } 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/endpoints/endpoint[name='%s']", endpt_name); + } else { + return nc_server_config_delete(config, "/ietf-netconf-server:netconf-server/listen/endpoints/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; + enum 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_SSH) { + ret = nc_server_config_util_get_asym_key_pair(privkey_path, pubkey_path, NC_PUBKEY_FORMAT_SSH, &privkey, + &privkey_type, &pubkey); + } else if (ti == NC_TI_TLS) { + 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) { + goto cleanup; + } + + /* get pubkey format str */ + if (ti == NC_TI_SSH) { + pubkey_format = "ietf-crypto-types:ssh-public-key-format"; + } else { + pubkey_format = "ietf-crypto-types:subject-public-key-info-format"; + } + + /* 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_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); + + /* 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 if (start_with == NC_CH_RANDOM) { + start_with_val = "random-selection"; + } else { + ERR(NULL, "Unknown reconnect strategy."); + goto cleanup; + } + + 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 0000000..e38366a --- /dev/null +++ b/src/server_config_util.h @@ -0,0 +1,169 @@ +/** + * @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 pkcs1 rsa footer */ +#define NC_PKCS1_RSA_PRIVKEY_FOOTER "\n-----END RSA PRIVATE KEY-----\n" + +/* private key's sec1 ec header */ +#define NC_SEC1_EC_PRIVKEY_HEADER "-----BEGIN EC PRIVATE KEY-----\n" + +/* private key's sec1 ec footer */ +#define NC_SEC1_EC_PRIVKEY_FOOTER "\n-----END EC PRIVATE KEY-----\n" + +/* private key's header when getting an EC/RSA privkey from file using libssh */ +#define NC_LIBSSH_PRIVKEY_HEADER "-----BEGIN PRIVATE KEY-----\n" + +/* 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, enum nc_pubkey_format wanted_pubkey_type, + char **privkey, enum 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(enum 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 0000000..58e9678 --- /dev/null +++ b/src/server_config_util_ssh.c @@ -0,0 +1,798 @@ +/** + * @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; + enum 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) { + 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/central-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/endpoints/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) { + 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) { + 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/endpoints/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/endpoints/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/" + "endpoints/endpoint[name='%s']/ssh/ssh-server-parameters/server-identity/host-key[name='%s']/public-key/" + "central-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/" + "endpoints/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/central-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/endpoints/" + "endpoint[name='%s']/ssh/ssh-server-parameters/server-identity/host-key[name='%s']/public-key/" + "central-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/central-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/endpoints/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) { + goto cleanup; + } + + /* delete truststore reference if present */ + ret = nc_server_config_check_delete(config, "/ietf-netconf-server:netconf-server/listen/endpoints/endpoint[name='%s']/ssh/" + "ssh-server-parameters/client-authentication/users/user[name='%s']/public-keys/central-truststore-reference", + endpt_name, user_name); + if (ret) { + goto cleanup; + } + + /* delete use system auth if present */ + ret = nc_server_config_check_delete(config, "/ietf-netconf-server:netconf-server/listen/endpoints/endpoint[name='%s']/ssh/" + "ssh-server-parameters/client-authentication/users/user[name='%s']/public-keys/" + "libnetconf2-netconf-server:use-system-keys", 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) { + 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/central-truststore-reference", client_name, endpt_name, user_name); + if (ret) { + goto cleanup; + } + + /* delete use system auth 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/" + "libnetconf2-netconf-server:use-system-keys", 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/endpoints/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/endpoints/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); + } +} + +API int +nc_server_config_add_ssh_user_authkey(const struct ly_ctx *ctx, const char *endpt_name, + const char *user_name, struct lyd_node **config) +{ + int ret = 0; + char *path = NULL; + + NC_CHECK_ARG_RET(NULL, ctx, endpt_name, user_name, config, 1); + + ret = asprintf(&path, "/ietf-netconf-server:netconf-server/listen/endpoints/endpoint[name='%s']/ssh/ssh-server-parameters/" + "client-authentication/users/user[name='%s']/public-keys", endpt_name, user_name); + NC_CHECK_ERRMEM_GOTO(ret == -1, path = NULL; ret = 1, cleanup); + + ret = nc_server_config_append(ctx, path, "libnetconf2-netconf-server:use-system-keys", NULL, config); + if (ret) { + goto cleanup; + } + + /* delete inline definition nodes if present */ + ret = nc_server_config_check_delete(config, "/ietf-netconf-server:netconf-server/listen/endpoints/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; + } + + /* delete truststore reference if present */ + ret = nc_server_config_check_delete(config, "/ietf-netconf-server:netconf-server/listen/endpoints/endpoint[name='%s']/ssh/" + "ssh-server-parameters/client-authentication/users/user[name='%s']/public-keys/central-truststore-reference", + endpt_name, user_name); + if (ret) { + goto cleanup; + } + +cleanup: + free(path); + return ret; +} + +API int +nc_server_config_add_ch_ssh_user_authkey(const struct ly_ctx *ctx, const char *client_name, + const char *endpt_name, const char *user_name, struct lyd_node **config) +{ + int ret = 0; + char *path = NULL; + + NC_CHECK_ARG_RET(NULL, ctx, client_name, endpt_name, user_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']/public-keys", client_name, endpt_name, user_name); + NC_CHECK_ERRMEM_GOTO(ret == -1, path = NULL; ret = 1, cleanup); + + ret = nc_server_config_append(ctx, path, "libnetconf2-netconf-server:use-system-keys", NULL, config); + 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; + } + + /* 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/central-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_authkey(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/endpoints/endpoint[name='%s']/ssh/" + "ssh-server-parameters/client-authentication/users/user[name='%s']/" + "public-keys/libnetconf2-netconf-server:use-system-keys", endpt_name, user_name); +} + +API int +nc_server_config_ch_del_ssh_user_authkey(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/libnetconf2-netconf-server:use-system-keys", 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/endpoints/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) { + 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) { + 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/endpoints/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); +} + +API int +nc_server_config_add_ssh_user_interactive(const struct ly_ctx *ctx, const char *endpt_name, + const char *user_name, struct lyd_node **config) +{ + int ret = 0; + char *path = NULL; + + NC_CHECK_ARG_RET(NULL, ctx, endpt_name, user_name, config, 1); + + ret = asprintf(&path, "/ietf-netconf-server:netconf-server/listen/endpoints/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_append(ctx, path, "use-system-auth", NULL, config); + if (ret) { + 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, struct lyd_node **config) +{ + int ret = 0; + char *path = NULL; + + NC_CHECK_ARG_RET(NULL, ctx, client_name, endpt_name, user_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_append(ctx, path, "use-system-auth", NULL, config); + if (ret) { + 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/endpoints/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/endpoints/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/endpoints/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/endpoints/" + "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/endpoints/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/" + "endpoints/endpoint[name='%s']/ssh/ssh-server-parameters/client-authentication/users/user[name='%s']/public-keys/" + "central-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/endpoints/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; + } + + /* delete use system auth if present */ + ret = nc_server_config_check_delete(config, "/ietf-netconf-server:netconf-server/listen/endpoints/endpoint[name='%s']/ssh/" + "ssh-server-parameters/client-authentication/users/user[name='%s']/public-keys/" + "libnetconf2-netconf-server:use-system-keys", 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/central-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; + } + + /* delete use system auth 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/" + "libnetconf2-netconf-server:use-system-keys", 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/" + "central-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/central-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 0000000..2f99d6c --- /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; + enum 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/central-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/endpoints/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/endpoints/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, "central-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, "central-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/endpoints/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/endpoints/endpoint[name='%s']/" + "tls/tls-server-parameters/server-identity/certificate/central-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/" + "central-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/endpoints/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/endpoints/endpoint[name='%s']/" + "tls/tls-server-parameters/client-authentication/ee-certs/central-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/endpoints/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/endpoints/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/central-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/endpoints/endpoint[name='%s']/tls/" + "tls-server-parameters/client-authentication/ee-certs/central-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/endpoints/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/endpoints/endpoint[name='%s']/tls/" + "tls-server-parameters/client-authentication/ee-certs/central-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/central-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/central-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/endpoints/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/endpoints/endpoint[name='%s']/" + "tls/tls-server-parameters/client-authentication/ca-certs/central-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/endpoints/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/endpoints/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/central-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/endpoints/endpoint[name='%s']/tls/" + "tls-server-parameters/client-authentication/ca-certs/central-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/endpoints/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/endpoints/endpoint[name='%s']/tls/" + "tls-server-parameters/client-authentication/ca-certs/central-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/central-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/central-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/endpoints/endpoint[name='%s']/tls/netconf-server-parameters/" + "client-identity-mappings/cert-to-name[id='%" PRIu32 "']", 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/endpoints/endpoint[name='%s']/tls/" + "netconf-server-parameters/client-identity-mappings/cert-to-name[id='%" PRIu32 "']", endpt_name, id); + } else { + return nc_server_config_delete(config, "/ietf-netconf-server:netconf-server/listen/endpoints/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='%" PRIu32 "']", 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/endpoints/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/endpoints/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 2f9fd92..4b7cfd6 100644 --- a/src/session.c +++ b/src/session.c @@ -1,9 +1,10 @@ /** - * \file session.c - * \author Michal Vasko - * \brief libnetconf2 - general session functions + * @file session.c + * @author Michal Vasko + * @brief libnetconf2 - general session functions * - * Copyright (c) 2015 - 2018 CESNET, z.s.p.o. + * @copyright + * 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. @@ -29,82 +30,42 @@ #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 "session_wrapper.h" -#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 -#define NC_SERVER_HELLO_TIMEOUT 60 +#define NC_SERVER_CH_HELLO_TIMEOUT 180 /* in milliseconds */ #define NC_CLOSE_REPLY_TIMEOUT 200 -extern struct nc_server_opts server_opts; - -int -nc_gettimespec_mono(struct timespec *ts) -{ -#ifdef CLOCK_MONOTONIC_RAW - return clock_gettime(CLOCK_MONOTONIC_RAW, ts); -#elif defined (CLOCK_MONOTONIC) - return clock_gettime(CLOCK_MONOTONIC, ts); -#else - /* no monotonic clock available, return realtime */ - return nc_gettimespec_real(ts); -#endif -} - -int -nc_gettimespec_real(struct timespec *ts) -{ -#ifdef CLOCK_REALTIME - return clock_gettime(CLOCK_REALTIME, ts); -#else - int rc; - struct timeval tv; - - rc = gettimeofday(&tv, NULL); - if (!rc) { - ts->tv_sec = (time_t)tv.tv_sec; - ts->tv_nsec = 1000L * (long)tv.tv_usec; - } - return rc; -#endif -} - -/* ts1 < ts2 -> +, ts1 > ts2 -> -, returns milliseconds */ -int32_t -nc_difftimespec(const struct timespec *ts1, const struct timespec *ts2) -{ - int64_t nsec_diff = 0; - - nsec_diff += (((int64_t)ts2->tv_sec) - ((int64_t)ts1->tv_sec)) * 1000000000L; - nsec_diff += ((int64_t)ts2->tv_nsec) - ((int64_t)ts1->tv_nsec); - - return nsec_diff ? nsec_diff / 1000000L : 0; -} - void -nc_addtimespec(struct timespec *ts, uint32_t msec) +nc_timeouttime_get(struct timespec *ts, uint32_t add_ms) { + if (clock_gettime(COMPAT_CLOCK_ID, ts) == -1) { + ERR(NULL, "clock_gettime() failed (%s).", strerror(errno)); + return; + } + + if (!add_ms) { + return; + } + assert((ts->tv_nsec >= 0) && (ts->tv_nsec < 1000000000L)); - ts->tv_sec += msec / 1000; - ts->tv_nsec += (msec % 1000) * 1000000L; + ts->tv_sec += add_ms / 1000; + ts->tv_nsec += (add_ms % 1000) * 1000000L; if (ts->tv_nsec >= 1000000000L) { ++ts->tv_sec; @@ -117,33 +78,150 @@ nc_addtimespec(struct timespec *ts, uint32_t msec) assert((ts->tv_nsec >= 0) && (ts->tv_nsec < 1000000000L)); } -const char * -nc_keytype2str(NC_SSH_KEY_TYPE type) +int32_t +nc_time_diff(const struct timespec *ts1, const struct timespec *ts2) { - switch (type) { - case NC_SSH_KEY_DSA: - return "DSA"; - case NC_SSH_KEY_RSA: - return "RSA"; - case NC_SSH_KEY_ECDSA: - return "EC"; - default: - break; - } + int64_t nsec_diff = 0; - return NULL; + nsec_diff += (((int64_t)ts1->tv_sec) - ((int64_t)ts2->tv_sec)) * 1000000000L; + nsec_diff += ((int64_t)ts1->tv_nsec) - ((int64_t)ts2->tv_nsec); + + return nsec_diff / 1000000L; +} + +int32_t +nc_timeouttime_cur_diff(const struct timespec *ts) +{ + struct timespec cur; + + nc_timeouttime_get(&cur, 0); + + return nc_time_diff(ts, &cur); +} + +void +nc_realtime_get(struct timespec *ts) +{ + if (clock_gettime(CLOCK_REALTIME, ts)) { + ERR(NULL, "clock_gettime() failed (%s).", strerror(errno)); + return; + } } int -nc_sock_enable_keepalive(int sock, struct nc_keepalives *ka) +nc_poll(struct pollfd *pfd, uint16_t pfd_count, int timeout_ms) +{ + int rc; + struct timespec start_ts; + + if (timeout_ms > 0) { + /* get current time */ + nc_timeouttime_get(&start_ts, 0); + } + + do { + /* poll */ + rc = poll(pfd, pfd_count, timeout_ms); + + if (timeout_ms > 0) { + /* adjust the timeout by subtracting the elapsed time (relevant in case of EINTR) */ + timeout_ms += nc_timeouttime_cur_diff(&start_ts); + if (timeout_ms < 0) { + /* manual timeout */ + rc = 0; + errno = 0; + break; + } + } + } while ((rc == -1) && (errno == EINTR)); + + if (rc == -1) { + ERR(NULL, "Poll failed (%s).", strerror(errno)); + } + return rc; +} + +#ifdef NC_ENABLED_SSH_TLS + +void * +nc_base64der_to_cert(const char *data) +{ + char *buf = NULL; + void *cert; + + NC_CHECK_ARG_RET(NULL, data, NULL); + + if (asprintf(&buf, "%s%s%s", "-----BEGIN CERTIFICATE-----\n", data, "\n-----END CERTIFICATE-----") == -1) { + ERRMEM; + return NULL; + } + + cert = nc_tls_pem_to_cert_wrap(buf); + free(buf); + return cert; +} + +const char * +nc_privkey_format_to_str(enum nc_privkey_format format) +{ + switch (format) { + case NC_PRIVKEY_FORMAT_RSA: + return " RSA "; + case NC_PRIVKEY_FORMAT_EC: + return " EC "; + case NC_PRIVKEY_FORMAT_X509: + return " "; + case NC_PRIVKEY_FORMAT_OPENSSH: + return " OPENSSH "; + default: + return NULL; + } +} + +int +nc_is_pk_subject_public_key_info(const char *b64) +{ + int ret = 0; + long len; + unsigned char *bin = NULL, *tmp; + + /* decode base64 */ + len = nc_base64_decode_wrap(b64, &bin); + if (len == -1) { + ret = -1; + goto cleanup; + } + + /* for deallocation later */ + tmp = bin; + + /* try to parse the supposed SubjectPublicKeyInfo binary data */ + if (nc_tls_is_der_subpubkey_wrap(tmp, len)) { + /* success, it's most likely SubjectPublicKeyInfo */ + ret = 1; + } else { + /* it's most likely not SubjectPublicKeyInfo */ + ret = 0; + } + +cleanup: + free(bin); + return ret; +} + +#endif /* NC_ENABLED_SSH_TLS */ + +int +nc_sock_configure_ka(int sock, const struct nc_keepalives *ka) { int opt; opt = ka->enabled; if (setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, &opt, sizeof opt) == -1) { - ERR(NULL, "Could not set SO_KEEPALIVE option (%s).", strerror(errno)); + ERR(NULL, "Failed to set SO_KEEPALIVE (%s).", strerror(errno)); return -1; } + if (!ka->enabled) { return 0; } @@ -151,7 +229,7 @@ nc_sock_enable_keepalive(int sock, struct nc_keepalives *ka) #ifdef TCP_KEEPIDLE opt = ka->idle_time; if (setsockopt(sock, IPPROTO_TCP, TCP_KEEPIDLE, &opt, sizeof opt) == -1) { - ERR(NULL, "Setsockopt failed (%s).", strerror(errno)); + ERR(NULL, "Failed to set TCP_KEEPIDLE (%s).", strerror(errno)); return -1; } #endif @@ -159,7 +237,7 @@ nc_sock_enable_keepalive(int sock, struct nc_keepalives *ka) #ifdef TCP_KEEPCNT opt = ka->max_probes; if (setsockopt(sock, IPPROTO_TCP, TCP_KEEPCNT, &opt, sizeof opt) == -1) { - ERR(NULL, "Setsockopt failed (%s).", strerror(errno)); + ERR(NULL, "Failed to set TCP_KEEPCNT (%s).", strerror(errno)); return -1; } #endif @@ -167,7 +245,7 @@ nc_sock_enable_keepalive(int sock, struct nc_keepalives *ka) #ifdef TCP_KEEPINTVL opt = ka->probe_interval; if (setsockopt(sock, IPPROTO_TCP, TCP_KEEPINTVL, &opt, sizeof opt) == -1) { - ERR(NULL, "Setsockopt failed (%s).", strerror(errno)); + ERR(NULL, "Failed to set TCP_KEEPINTVL (%s).", strerror(errno)); return -1; } #endif @@ -179,6 +257,7 @@ struct nc_session * nc_new_session(NC_SIDE side, int shared_ti) { struct nc_session *sess; + struct timespec ts_cur; sess = calloc(1, sizeof *sess); if (!sess) { @@ -188,12 +267,16 @@ nc_new_session(NC_SIDE side, int shared_ti) sess->side = side; if (side == NC_SERVER) { + pthread_mutex_init(&sess->opts.server.ntf_status_lock, NULL); pthread_mutex_init(&sess->opts.server.rpc_lock, NULL); pthread_cond_init(&sess->opts.server.rpc_cond, NULL); - sess->opts.server.rpc_inuse = 0; pthread_mutex_init(&sess->opts.server.ch_lock, NULL); pthread_cond_init(&sess->opts.server.ch_cond, NULL); + + /* initialize last_rpc for idle_timeout */ + nc_timeouttime_get(&ts_cur, 0); + sess->opts.server.last_rpc = ts_cur.tv_sec; } else { pthread_mutex_init(&sess->opts.client.msgs_lock, NULL); } @@ -230,14 +313,14 @@ nc_session_rpc_lock(struct nc_session *session, int timeout, const char *func) } if (timeout > 0) { - nc_gettimespec_real(&ts_timeout); - nc_addtimespec(&ts_timeout, timeout); + nc_timeouttime_get(&ts_timeout, timeout); /* LOCK */ - ret = pthread_mutex_timedlock(&session->opts.server.rpc_lock, &ts_timeout); + ret = pthread_mutex_clocklock(&session->opts.server.rpc_lock, COMPAT_CLOCK_ID, &ts_timeout); if (!ret) { while (session->opts.server.rpc_inuse) { - ret = pthread_cond_timedwait(&session->opts.server.rpc_cond, &session->opts.server.rpc_lock, &ts_timeout); + ret = pthread_cond_clockwait(&session->opts.server.rpc_cond, &session->opts.server.rpc_lock, + COMPAT_CLOCK_ID, &ts_timeout); if (ret) { pthread_mutex_unlock(&session->opts.server.rpc_lock); break; @@ -307,11 +390,10 @@ nc_session_rpc_unlock(struct nc_session *session, int timeout, const char *func) assert(session->opts.server.rpc_inuse); if (timeout > 0) { - nc_gettimespec_real(&ts_timeout); - nc_addtimespec(&ts_timeout, timeout); + nc_timeouttime_get(&ts_timeout, timeout); /* LOCK */ - ret = pthread_mutex_timedlock(&session->opts.server.rpc_lock, &ts_timeout); + ret = pthread_mutex_clocklock(&session->opts.server.rpc_lock, COMPAT_CLOCK_ID, &ts_timeout); } else if (!timeout) { /* LOCK */ ret = pthread_mutex_trylock(&session->opts.server.rpc_lock); @@ -325,7 +407,7 @@ nc_session_rpc_unlock(struct nc_session *session, int timeout, const char *func) ERR(session, "%s: failed to RPC lock a session (%s).", func, strerror(ret)); return -1; } else if (ret) { - WRN(session, "%s: session RPC lock timeout, should not happen."); + WRN(session, "%s: session RPC lock timeout, should not happen.", func); } session->opts.server.rpc_inuse = 0; @@ -351,10 +433,9 @@ nc_session_io_lock(struct nc_session *session, int timeout, const char *func) struct timespec ts_timeout; if (timeout > 0) { - nc_gettimespec_real(&ts_timeout); - nc_addtimespec(&ts_timeout, timeout); + nc_timeouttime_get(&ts_timeout, timeout); - ret = pthread_mutex_timedlock(session->io_lock, &ts_timeout); + ret = pthread_mutex_clocklock(session->io_lock, COMPAT_CLOCK_ID, &ts_timeout); } else if (!timeout) { ret = pthread_mutex_trylock(session->io_lock); } else { /* timeout == -1 */ @@ -395,22 +476,20 @@ nc_session_client_msgs_lock(struct nc_session *session, int *timeout, const char { int ret; int32_t diff_msec; - struct timespec ts_timeout, ts_start, ts_end; + struct timespec ts_timeout, ts_start; assert(session->side == NC_CLIENT); if (*timeout > 0) { /* get current time */ - nc_gettimespec_real(&ts_start); + nc_timeouttime_get(&ts_start, 0); - nc_gettimespec_real(&ts_timeout); - nc_addtimespec(&ts_timeout, *timeout); + nc_timeouttime_get(&ts_timeout, *timeout); - ret = pthread_mutex_timedlock(&session->opts.client.msgs_lock, &ts_timeout); + ret = pthread_mutex_clocklock(&session->opts.client.msgs_lock, COMPAT_CLOCK_ID, &ts_timeout); if (!ret) { /* update timeout based on what was elapsed */ - nc_gettimespec_real(&ts_end); - diff_msec = nc_difftimespec(&ts_start, &ts_end); + diff_msec = nc_timeouttime_cur_diff(&ts_start); *timeout -= diff_msec; } } else if (!*timeout) { @@ -453,10 +532,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; } @@ -464,10 +540,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; } @@ -475,10 +548,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; } @@ -486,10 +556,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; } @@ -497,10 +564,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; } @@ -508,10 +572,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; } @@ -519,10 +580,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; } @@ -530,10 +588,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; } @@ -541,10 +596,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; } @@ -555,22 +608,39 @@ 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; } -API struct ly_ctx * -nc_session_get_ctx(const struct nc_session *session) +#ifdef NC_ENABLED_SSH_TLS + +API const char * +nc_session_ssh_get_banner(const struct nc_session *session) { - if (!session) { - ERRARG("session"); + NC_CHECK_ARG_RET(NULL, session, NULL); + + if (session->ti_type != NC_TI_SSH) { + ERR(NULL, "Cannot get the SSH banner of a non-SSH session."); return NULL; } + if (session->side == NC_SERVER) { + /* get the banner sent by the client */ + return ssh_get_clientbanner(session->ti.libssh.session); + } else { + /* get the banner received from the server */ + return ssh_get_serverbanner(session->ti.libssh.session); + } +} + +#endif + +API const struct ly_ctx * +nc_session_get_ctx(const struct nc_session *session) +{ + NC_CHECK_ARG_RET(session, session, NULL); + return session->ctx; } @@ -578,7 +648,7 @@ API void nc_session_set_data(struct nc_session *session, void *data) { if (!session) { - ERRARG("session"); + ERRARG(NULL, "session"); return; } @@ -588,14 +658,23 @@ 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; } +API int +nc_session_is_callhome(const struct nc_session *session) +{ + NC_CHECK_ARG_RET(session, session, 0); + + if (session->flags & NC_SESSION_CALLHOME) { + return 1; + } + + return 0; +} + NC_MSG_TYPE nc_send_msg_io(struct nc_session *session, int io_timeout, struct lyd_node *op) { @@ -607,36 +686,222 @@ nc_send_msg_io(struct nc_session *session, int io_timeout, struct lyd_node *op) return nc_write_msg_io(session, io_timeout, NC_MSG_RPC, op, NULL); } -API void -nc_session_free(struct nc_session *session, void (*data_free)(void *)) +/** + * @brief Send \ and read the reply on a session. + * + * @param[in] session Closing NETCONF session. + */ +static void +nc_session_free_close_session(struct nc_session *session) { - int r, i, rpc_locked = 0, msgs_locked = 0, sock = -1, timeout; - int connected; /* flag to indicate whether the transport socket is still connected */ - int multisession = 0; /* flag for more NETCONF sessions on a single SSH session */ - struct nc_session *siter; - struct nc_msg_cont *contiter; struct ly_in *msg; struct lyd_node *close_rpc, *envp; const struct lys_module *ietfnc; - struct timespec ts, ts_cur; + + ietfnc = ly_ctx_get_module_implemented(session->ctx, "ietf-netconf"); + if (!ietfnc) { + WRN(session, "Missing ietf-netconf module in context, unable to send ."); + return; + } + if (lyd_new_inner(NULL, ietfnc, "close-session", 0, &close_rpc)) { + WRN(session, "Failed to create RPC."); + return; + } + + /* send the RPC */ + nc_send_msg_io(session, NC_SESSION_FREE_LOCK_TIMEOUT, close_rpc); + +read_msg: + switch (nc_read_msg_poll_io(session, NC_CLOSE_REPLY_TIMEOUT, &msg)) { + case 1: + if (!strncmp(ly_in_memory(msg, NULL), "ctx, close_rpc, msg, LYD_XML, LYD_TYPE_REPLY_NETCONF, &envp, NULL)) { + WRN(session, "Failed to parse reply."); + } else if (!lyd_child(envp) || strcmp(LYD_NAME(lyd_child(envp)), "ok")) { + WRN(session, "Reply to was not as expected."); + } + lyd_free_tree(envp); + ly_in_free(msg, 1); + break; + case 0: + WRN(session, "Timeout for receiving a reply to elapsed."); + break; + case -1: + ERR(session, "Failed to receive a reply to ."); + break; + default: + /* cannot happen */ + break; + } + lyd_free_tree(close_rpc); +} + +/** + * @brief Free transport implementation members of a session. + * + * @param[in] session Session to free. + * @param[out] multisession Whether there are other NC sessions on the same SSH sessions. + */ +static void +nc_session_free_transport(struct nc_session *session, int *multisession) +{ + int connected; /* flag to indicate whether the transport socket is still connected */ + int sock = -1; + struct nc_session *siter; + + *multisession = 0; + connected = nc_session_is_connected(session); + + /* transport implementation cleanup */ + switch (session->ti_type) { + case NC_TI_FD: + /* nothing needed - file descriptors were provided by caller, + * so it is up to the caller to close them correctly + * TODO use callbacks + */ + /* just to avoid compiler warning */ + (void)connected; + (void)siter; + break; + + case NC_TI_UNIX: + sock = session->ti.unixsock.sock; + (void)connected; + (void)siter; + break; + +#ifdef NC_ENABLED_SSH_TLS + case NC_TI_SSH: { + int r; + + if (connected) { + ssh_channel_send_eof(session->ti.libssh.channel); + ssh_channel_free(session->ti.libssh.channel); + } + /* There can be multiple NETCONF sessions on the same SSH session (NETCONF session maps to + * SSH channel). So destroy the SSH session only if there is no other NETCONF session using + * it. Also, avoid concurrent free by multiple threads of sessions that share the SSH session. + */ + /* SESSION IO LOCK */ + r = nc_session_io_lock(session, NC_SESSION_FREE_LOCK_TIMEOUT, __func__); + + if (session->ti.libssh.next) { + for (siter = session->ti.libssh.next; siter != session; siter = siter->ti.libssh.next) { + if (siter->status != NC_STATUS_STARTING) { + *multisession = 1; + break; + } + } + } + + if (!*multisession) { + /* it's not multisession yet, but we still need to free the starting sessions */ + if (session->ti.libssh.next) { + do { + siter = session->ti.libssh.next; + session->ti.libssh.next = siter->ti.libssh.next; + + /* free starting SSH NETCONF session (channel will be freed in ssh_free()) */ + free(siter->username); + free(siter->host); + if (!(siter->flags & NC_SESSION_SHAREDCTX)) { + ly_ctx_destroy((struct ly_ctx *)siter->ctx); + } + + free(siter); + } while (session->ti.libssh.next != session); + } + /* remember sock so we can close it */ + sock = ssh_get_fd(session->ti.libssh.session); + if (connected) { + /* does not close sock */ + ssh_disconnect(session->ti.libssh.session); + } + ssh_free(session->ti.libssh.session); + } else { + /* remove the session from the list */ + for (siter = session->ti.libssh.next; siter->ti.libssh.next != session; siter = siter->ti.libssh.next) {} + if (session->ti.libssh.next == siter) { + /* there will be only one session */ + siter->ti.libssh.next = NULL; + } else { + /* there are still multiple sessions, keep the ring list */ + siter->ti.libssh.next = session->ti.libssh.next; + } + } + + /* SESSION IO UNLOCK */ + if (r == 1) { + nc_session_io_unlock(session, __func__); + } + break; + } + case NC_TI_TLS: + sock = nc_tls_get_fd_wrap(session); + + if (connected) { + /* notify the peer that we're shutting down */ + nc_tls_close_notify_wrap(session->ti.tls.session); + } + + nc_tls_ctx_destroy_wrap(&session->ti.tls.ctx); + memset(&session->ti.tls.ctx, 0, sizeof session->ti.tls.ctx); + nc_tls_session_destroy_wrap(session->ti.tls.session); + session->ti.tls.session = NULL; + nc_tls_config_destroy_wrap(session->ti.tls.config); + session->ti.tls.config = NULL; + + if (session->side == NC_SERVER) { + nc_tls_cert_destroy_wrap(session->opts.server.client_cert); + } + + break; +#endif /* NC_ENABLED_SSH_TLS */ + case NC_TI_NONE: + break; + } + + /* close socket separately */ + if (sock > -1) { + close(sock); + } +} + +API void +nc_session_free(struct nc_session *session, void (*data_free)(void *)) +{ + int r, i, rpc_locked = 0, msgs_locked = 0, timeout; + int multisession = 0; /* flag for more NETCONF sessions on a single SSH session */ + struct nc_msg_cont *contiter; + struct ly_in *msg; + struct timespec ts; void *p; if (!session || (session->status == NC_STATUS_CLOSING)) { return; } - /* stop notifications thread if any */ - if ((session->side == NC_CLIENT) && ATOMIC_LOAD_RELAXED(session->opts.client.ntf_thread)) { - /* let the thread know it should quit */ - ATOMIC_STORE_RELAXED(session->opts.client.ntf_thread, 2); + if (session->side == NC_CLIENT) { + if (session->flags & NC_SESSION_CLIENT_MONITORED) { + /* remove the session from the monitored list */ + nc_client_monitoring_session_stop(session, 1); + } + } - /* wait for it */ - nc_gettimespec_mono(&ts); - nc_addtimespec(&ts, NC_SESSION_FREE_LOCK_TIMEOUT); - while (ATOMIC_LOAD_RELAXED(session->opts.client.ntf_thread)) { + /* stop notification threads if any */ + if ((session->side == NC_CLIENT) && ATOMIC_LOAD_RELAXED(session->opts.client.ntf_thread_running)) { + /* let the threads know they should quit */ + ATOMIC_STORE_RELAXED(session->opts.client.ntf_thread_running, 0); + + /* wait for them */ + nc_timeouttime_get(&ts, NC_SESSION_FREE_LOCK_TIMEOUT); + while (ATOMIC_LOAD_RELAXED(session->opts.client.ntf_thread_count)) { usleep(NC_TIMEOUT_STEP); - nc_gettimespec_mono(&ts_cur); - if (nc_difftimespec(&ts_cur, &ts) < 1) { + if (nc_timeouttime_cur_diff(&ts) < 1) { ERR(session, "Waiting for notification thread exit failed (timed out)."); break; } @@ -690,33 +955,7 @@ nc_session_free(struct nc_session *session, void (*data_free)(void *)) } /* send closing info to the other side */ - ietfnc = ly_ctx_get_module_implemented(session->ctx, "ietf-netconf"); - if (!ietfnc) { - WRN(session, "Missing ietf-netconf schema in context, unable to send ."); - } else if (!lyd_new_inner(NULL, ietfnc, "close-session", 0, &close_rpc)) { - nc_send_msg_io(session, NC_SESSION_FREE_LOCK_TIMEOUT, close_rpc); - switch (nc_read_msg_poll_io(session, NC_CLOSE_REPLY_TIMEOUT, &msg)) { - case 1: - if (lyd_parse_op(session->ctx, close_rpc, msg, LYD_XML, LYD_TYPE_REPLY_NETCONF, &envp, NULL)) { - WRN(session, "Failed to parse reply."); - } else if (!lyd_child(envp) || strcmp(LYD_NAME(lyd_child(envp)), "ok")) { - WRN(session, "Reply to was not as expected."); - } - lyd_free_tree(envp); - ly_in_free(msg, 1); - break; - case 0: - WRN(session, "Timeout for receiving a reply to elapsed."); - break; - case -1: - ERR(session, "Failed to receive a reply to ."); - break; - default: - /* cannot happen */ - break; - } - lyd_free_tree(close_rpc); - } + nc_session_free_close_session(session); } /* list of server's capabilities */ @@ -726,6 +965,26 @@ nc_session_free(struct nc_session *session, void (*data_free)(void *)) } free(session->opts.client.cpblts); } + + /* LY ext data */ +#ifdef NC_ENABLED_SSH_TLS + struct nc_session *siter; + + if ((session->flags & NC_SESSION_SHAREDCTX) && (session->ti_type == NC_TI_SSH) && session->ti.libssh.next) { + for (siter = session->ti.libssh.next; siter != session; siter = siter->ti.libssh.next) { + if (siter->status != NC_STATUS_STARTING) { + /* move LY ext data to this session */ + assert(!siter->opts.client.ext_data); + siter->opts.client.ext_data = session->opts.client.ext_data; + session->opts.client.ext_data = NULL; + break; + } + } + } else +#endif /* NC_ENABLED_SSH_TLS */ + { + lyd_free_siblings(session->opts.client.ext_data); + } } if (session->data && data_free) { @@ -740,158 +999,36 @@ nc_session_free(struct nc_session *session, void (*data_free)(void *)) /* mark session for closing */ session->status = NC_STATUS_CLOSING; - if ((session->side == NC_SERVER) && (session->flags & NC_SESSION_CALLHOME)) { + if ((session->side == NC_SERVER) && (session->flags & NC_SESSION_CH_THREAD)) { pthread_cond_signal(&session->opts.server.ch_cond); - nc_gettimespec_real(&ts); - nc_addtimespec(&ts, NC_SESSION_FREE_LOCK_TIMEOUT); + nc_timeouttime_get(&ts, NC_SESSION_FREE_LOCK_TIMEOUT); /* wait for CH thread to actually wake up and terminate */ r = 0; - while (!r && (session->flags & NC_SESSION_CALLHOME)) { - r = pthread_cond_timedwait(&session->opts.server.ch_cond, &session->opts.server.ch_lock, &ts); + while (!r && (session->flags & NC_SESSION_CH_THREAD)) { + r = pthread_cond_clockwait(&session->opts.server.ch_cond, &session->opts.server.ch_lock, COMPAT_CLOCK_ID, &ts); } - - /* CH UNLOCK */ - pthread_mutex_unlock(&session->opts.server.ch_lock); - if (r) { ERR(session, "Waiting for Call Home thread failed (%s).", strerror(r)); } } - connected = nc_session_is_connected(session); + if ((session->side == NC_SERVER) && (session->flags & NC_SESSION_CALLHOME)) { + /* CH UNLOCK */ + pthread_mutex_unlock(&session->opts.server.ch_lock); + } /* transport implementation cleanup */ - switch (session->ti_type) { - case NC_TI_FD: - /* nothing needed - file descriptors were provided by caller, - * so it is up to the caller to close them correctly - * TODO use callbacks - */ - /* just to avoid compiler warning */ - (void)connected; - (void)siter; - break; - - case NC_TI_UNIX: - sock = session->ti.unixsock.sock; - (void)connected; - (void)siter; - break; - -#ifdef NC_ENABLED_SSH - case NC_TI_LIBSSH: - if (connected) { - ssh_channel_free(session->ti.libssh.channel); - } - /* There can be multiple NETCONF sessions on the same SSH session (NETCONF session maps to - * SSH channel). So destroy the SSH session only if there is no other NETCONF session using - * it. Also, avoid concurrent free by multiple threads of sessions that share the SSH session. - */ - /* SESSION IO LOCK */ - r = nc_session_io_lock(session, NC_SESSION_FREE_LOCK_TIMEOUT, __func__); - - multisession = 0; - if (session->ti.libssh.next) { - for (siter = session->ti.libssh.next; siter != session; siter = siter->ti.libssh.next) { - if (siter->status != NC_STATUS_STARTING) { - multisession = 1; - break; - } - } - } - - if (!multisession) { - /* it's not multisession yet, but we still need to free the starting sessions */ - if (session->ti.libssh.next) { - do { - siter = session->ti.libssh.next; - session->ti.libssh.next = siter->ti.libssh.next; - - /* free starting SSH NETCONF session (channel will be freed in ssh_free()) */ - lydict_remove(session->ctx, session->username); - lydict_remove(session->ctx, session->host); - if (!(session->flags & NC_SESSION_SHAREDCTX)) { - ly_ctx_destroy(session->ctx); - } - - free(siter); - } while (session->ti.libssh.next != session); - } - /* remember sock so we can close it */ - sock = ssh_get_fd(session->ti.libssh.session); - if (connected) { - ssh_disconnect(session->ti.libssh.session); - sock = -1; - } - ssh_free(session->ti.libssh.session); - } else { - /* remove the session from the list */ - for (siter = session->ti.libssh.next; siter->ti.libssh.next != session; siter = siter->ti.libssh.next) {} - if (session->ti.libssh.next == siter) { - /* there will be only one session */ - siter->ti.libssh.next = NULL; - } else { - /* 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 */ - if (r == 1) { - nc_session_io_unlock(session, __func__); - } - break; -#endif - -#ifdef NC_ENABLED_TLS - case NC_TI_OPENSSL: - /* remember sock so we can close it */ - sock = SSL_get_fd(session->ti.tls); - - if (connected) { - SSL_shutdown(session->ti.tls); - } - SSL_free(session->ti.tls); - - if (session->side == NC_SERVER) { - X509_free(session->opts.server.client_cert); - } - break; -#endif - case NC_TI_NONE: - break; - } - - /* close socket separately */ - if (sock > -1) { - close(sock); - } - - lydict_remove(session->ctx, session->username); - lydict_remove(session->ctx, session->host); - lydict_remove(session->ctx, session->path); + nc_session_free_transport(session, &multisession); /* final cleanup */ + free(session->username); + free(session->host); + free(session->path); + if (session->side == NC_SERVER) { + pthread_mutex_destroy(&session->opts.server.ntf_status_lock); if (rpc_locked) { nc_session_rpc_unlock(session, NC_SESSION_LOCK_TIMEOUT, __func__); } @@ -905,7 +1042,7 @@ nc_session_free(struct nc_session *session, void (*data_free)(void *)) } if (!(session->flags & NC_SESSION_SHAREDCTX)) { - ly_ctx_destroy(session->ctx); + ly_ctx_destroy((struct ly_ctx *)session->ctx); } if (session->side == NC_SERVER) { @@ -920,7 +1057,7 @@ nc_session_free(struct nc_session *session, void (*data_free)(void *)) } static void -add_cpblt(struct ly_ctx *ctx, const char *capab, const char ***cpblts, int *size, int *count) +add_cpblt(const char *capab, char ***cpblts, int *size, int *count) { size_t len; int i; @@ -952,40 +1089,32 @@ add_cpblt(struct ly_ctx *ctx, const char *capab, const char ***cpblts, int *size } } - if (capab) { - lydict_insert(ctx, capab, 0, &(*cpblts)[*count]); - } else { - (*cpblts)[*count] = NULL; - } + (*cpblts)[*count] = capab ? strdup(capab) : NULL; ++(*count); } -API const char ** -nc_server_get_cpblts_version(struct ly_ctx *ctx, LYS_VERSION version) +API char ** +nc_server_get_cpblts_version(const struct ly_ctx *ctx, LYS_VERSION version) { - const char **cpblts; + char **cpblts; const struct lys_module *mod; struct lysp_feature *feat; int size = 10, count, features_count = 0, dev_count = 0, str_len, len; 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; - } - lydict_insert(ctx, "urn:ietf:params:netconf:base:1.0", 0, &cpblts[0]); - lydict_insert(ctx, "urn:ietf:params:netconf:base:1.1", 0, &cpblts[1]); + 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; /* capabilities */ @@ -993,22 +1122,22 @@ nc_server_get_cpblts_version(struct ly_ctx *ctx, LYS_VERSION version) mod = ly_ctx_get_module_implemented(ctx, "ietf-netconf"); if (mod) { if (lys_feature_value(mod, "writable-running") == LY_SUCCESS) { - add_cpblt(ctx, "urn:ietf:params:netconf:capability:writable-running:1.0", &cpblts, &size, &count); + add_cpblt("urn:ietf:params:netconf:capability:writable-running:1.0", &cpblts, &size, &count); } if (lys_feature_value(mod, "candidate") == LY_SUCCESS) { - add_cpblt(ctx, "urn:ietf:params:netconf:capability:candidate:1.0", &cpblts, &size, &count); + add_cpblt("urn:ietf:params:netconf:capability:candidate:1.0", &cpblts, &size, &count); if (lys_feature_value(mod, "confirmed-commit") == LY_SUCCESS) { - add_cpblt(ctx, "urn:ietf:params:netconf:capability:confirmed-commit:1.1", &cpblts, &size, &count); + add_cpblt("urn:ietf:params:netconf:capability:confirmed-commit:1.1", &cpblts, &size, &count); } } if (lys_feature_value(mod, "rollback-on-error") == LY_SUCCESS) { - add_cpblt(ctx, "urn:ietf:params:netconf:capability:rollback-on-error:1.0", &cpblts, &size, &count); + add_cpblt("urn:ietf:params:netconf:capability:rollback-on-error:1.0", &cpblts, &size, &count); } if (lys_feature_value(mod, "validate") == LY_SUCCESS) { - add_cpblt(ctx, "urn:ietf:params:netconf:capability:validate:1.1", &cpblts, &size, &count); + add_cpblt("urn:ietf:params:netconf:capability:validate:1.1", &cpblts, &size, &count); } if (lys_feature_value(mod, "startup") == LY_SUCCESS) { - add_cpblt(ctx, "urn:ietf:params:netconf:capability:startup:1.0", &cpblts, &size, &count); + add_cpblt("urn:ietf:params:netconf:capability:startup:1.0", &cpblts, &size, &count); } /* The URL capability must be set manually using nc_server_set_capability() @@ -1016,21 +1145,22 @@ nc_server_get_cpblts_version(struct ly_ctx *ctx, LYS_VERSION version) * https://tools.ietf.org/html/rfc6241#section-8.8.3 */ // if (lys_feature_value(mod, "url") == LY_SUCCESS) { - // add_cpblt(ctx, "urn:ietf:params:netconf:capability:url:1.0", &cpblts, &size, &count); + // add_cpblt("urn:ietf:params:netconf:capability:url:1.0", &cpblts, &size, &count); // } if (lys_feature_value(mod, "xpath") == LY_SUCCESS) { - add_cpblt(ctx, "urn:ietf:params:netconf:capability:xpath:1.0", &cpblts, &size, &count); + add_cpblt("urn:ietf:params:netconf:capability:xpath:1.0", &cpblts, &size, &count); } } 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; @@ -1045,30 +1175,31 @@ nc_server_get_cpblts_version(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'; - add_cpblt(ctx, str, &cpblts, &size, &count); + add_cpblt(str, &cpblts, &size, &count); } } } /* other capabilities */ for (u = 0; u < server_opts.capabilities_count; u++) { - add_cpblt(ctx, server_opts.capabilities[u], &cpblts, &size, &count); + add_cpblt(server_opts.capabilities[u], &cpblts, &size, &count); } /* models */ @@ -1083,16 +1214,10 @@ nc_server_get_cpblts_version(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)); } @@ -1100,20 +1225,20 @@ nc_server_get_cpblts_version(struct ly_ctx *ctx, LYS_VERSION version) /* new one (capab defined in RFC 8526 section 2) */ sprintf(str, "urn:ietf:params:netconf:capability:yang-library:1.1?revision=%s&content-id=%s", mod->revision, yl_content_id); - add_cpblt(ctx, str, &cpblts, &size, &count); + add_cpblt(str, &cpblts, &size, &count); } else { /* old one (capab defined in RFC 7950 section 5.6.4) */ sprintf(str, "urn:ietf:params:netconf:capability:yang-library:1.0?revision=%s&module-set-id=%s", mod->revision, yl_content_id); - add_cpblt(ctx, str, &cpblts, &size, &count); + add_cpblt(str, &cpblts, &size, &count); } free(yl_content_id); continue; } else if ((version == LYS_VERSION_1_0) && (mod->parsed->version > version)) { - /* skip YANG 1.1 schemas */ + /* skip YANG 1.1 modules */ continue; } else if ((version == LYS_VERSION_1_1) && (mod->parsed->version != version)) { - /* skip YANG 1.0 schemas */ + /* skip YANG 1.0 modules */ continue; } @@ -1165,11 +1290,11 @@ nc_server_get_cpblts_version(struct ly_ctx *ctx, LYS_VERSION version) } } - add_cpblt(ctx, str, &cpblts, &size, &count); + add_cpblt(str, &cpblts, &size, &count); } /* ending NULL capability */ - add_cpblt(ctx, NULL, &cpblts, &size, &count); + add_cpblt(NULL, &cpblts, &size, &count); return cpblts; @@ -1178,8 +1303,8 @@ error: return NULL; } -API const char ** -nc_server_get_cpblts(struct ly_ctx *ctx) +API char ** +nc_server_get_cpblts(const struct ly_ctx *ctx) { return nc_server_get_cpblts_version(ctx, LYS_VERSION_UNDEF); } @@ -1199,10 +1324,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; } @@ -1232,10 +1354,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++; } } @@ -1251,22 +1370,19 @@ static NC_MSG_TYPE nc_send_hello_io(struct nc_session *session) { NC_MSG_TYPE ret; - int i, io_timeout; - const char **cpblts; + int i, timeout_io; + char **cpblts; uint32_t *sid; 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; - } - lydict_insert(session->ctx, "urn:ietf:params:netconf:base:1.0", 0, &cpblts[0]); - lydict_insert(session->ctx, "urn:ietf:params:netconf:base:1.1", 0, &cpblts[1]); + 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; - io_timeout = NC_CLIENT_HELLO_TIMEOUT * 1000; + timeout_io = NC_CLIENT_HELLO_TIMEOUT * 1000; sid = NULL; } else { cpblts = nc_server_get_cpblts_version(session->ctx, LYS_VERSION_1_0); @@ -1274,29 +1390,39 @@ nc_send_hello_io(struct nc_session *session) return NC_MSG_ERROR; } - io_timeout = NC_SERVER_HELLO_TIMEOUT * 1000; + if (session->flags & NC_SESSION_CALLHOME) { + timeout_io = NC_SERVER_CH_HELLO_TIMEOUT * 1000; + } else { + timeout_io = server_opts.idle_timeout ? server_opts.idle_timeout * 1000 : -1; + } sid = &session->id; } - ret = nc_write_msg_io(session, io_timeout, NC_MSG_HELLO, cpblts, sid); + ret = nc_write_msg_io(session, timeout_io, NC_MSG_HELLO, cpblts, sid); for (i = 0; cpblts[i]; ++i) { - lydict_remove(session->ctx, cpblts[i]); + free(cpblts[i]); } free(cpblts); return ret; } +/** + * @brief Receive server hello message on the client. + * + * @param[in] session Client session to use. + * @return Received message type. + */ static NC_MSG_TYPE -nc_recv_client_hello_io(struct nc_session *session) +nc_client_recv_hello_io(struct nc_session *session) { struct ly_in *msg; struct lyd_node *hello = NULL, *iter; struct lyd_node_opaq *node; int r, ver = -1, flag = 0; char *str; - long long int id; + long long id; NC_MSG_TYPE rc = NC_MSG_HELLO; r = nc_read_msg_poll_io(session, NC_CLIENT_HELLO_TIMEOUT * 1000, &msg); @@ -1371,8 +1497,14 @@ cleanup: return rc; } +/** + * @brief Receive client hello message on the server. + * + * @param[in] session Server session to use. + * @return Received message type. + */ static NC_MSG_TYPE -nc_recv_server_hello_io(struct nc_session *session) +nc_server_recv_hello_io(struct nc_session *session) { struct ly_in *msg; struct lyd_node *hello = NULL, *iter; @@ -1380,7 +1512,12 @@ nc_recv_server_hello_io(struct nc_session *session) NC_MSG_TYPE rc = NC_MSG_HELLO; int r, ver = -1, flag = 0, timeout_io; - timeout_io = server_opts.hello_timeout ? server_opts.hello_timeout * 1000 : NC_SERVER_HELLO_TIMEOUT * 1000; + if (session->flags & NC_SESSION_CALLHOME) { + timeout_io = NC_SERVER_CH_HELLO_TIMEOUT * 1000; + } else { + timeout_io = server_opts.idle_timeout ? server_opts.idle_timeout * 1000 : -1; + } + r = nc_read_msg_poll_io(session, timeout_io, &msg); switch (r) { case 1: @@ -1444,251 +1581,175 @@ nc_handshake_io(struct nc_session *session) } if (session->side == NC_CLIENT) { - type = nc_recv_client_hello_io(session); + type = nc_client_recv_hello_io(session); } else { - type = nc_recv_server_hello_io(session); + type = nc_server_recv_hello_io(session); } return type; } -#ifdef NC_ENABLED_SSH +#ifdef NC_ENABLED_SSH_TLS -static void -nc_ssh_init(void) +/** + * @brief CURL callback for downloading data. + * + * @param[in] ptr Downloaded data. + * @param[in] size Size of one element. + * @param[in] nmemb Number of elements. + * @param[in,out] userdata Storage the downloaded data. + * @return Number of bytes processed. + */ +static size_t +nc_session_curl_cb(char *ptr, size_t size, size_t nmemb, void *userdata) { -#if (LIBSSH_VERSION_INT < SSH_VERSION_INT(0, 8, 0)) - ssh_threads_set_callbacks(ssh_threads_get_pthread()); - ssh_init(); -#endif + struct nc_curl_data *data; + + size = nmemb; + + data = (struct nc_curl_data *)userdata; + + data->data = nc_realloc(data->data, data->size + size); + NC_CHECK_ERRMEM_RET(!data->data, 0); + + memcpy(&data->data[data->size], ptr, size); + data->size += size; + + return size; } -static void -nc_ssh_destroy(void) +/** + * @brief Download data using CURL. + * + * @param[in] handle CURL handle. + * @param[in] url URL to download the data from. + * @return 0 on success, 1 on failure. + */ +static int +nc_session_curl_fetch(CURL *handle, const char *url) { -#if OPENSSL_VERSION_NUMBER < 0x10100000L // < 1.1.0 - FIPS_mode_set(0); - CONF_modules_unload(1); - nc_thread_destroy(); -#endif + char err_buf[CURL_ERROR_SIZE]; -#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); + /* set uri */ + if (curl_easy_setopt(handle, CURLOPT_URL, url)) { + ERR(NULL, "Setting URI \"%s\" to download CRL from failed.", url); + return 1; } - 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); + /* set err buf */ + if (curl_easy_setopt(handle, CURLOPT_ERRORBUFFER, err_buf)) { + ERR(NULL, "Setting CURL error buffer option failed."); + return 1; } - free(tls_locks); - CRYPTO_set_dynlock_create_callback(NULL); - CRYPTO_set_dynlock_lock_callback(NULL); - CRYPTO_set_dynlock_destroy_callback(NULL); -#endif + /* download */ + if (curl_easy_perform(handle)) { + ERR(NULL, "Downloading CRL from \"%s\" failed (%s).", url, err_buf); + return 1; + } + + return 0; } -#endif /* NC_ENABLED_TLS && !NC_ENABLED_SSH */ - -#if defined (NC_ENABLED_SSH) && defined (NC_ENABLED_TLS) - -static void -nc_ssh_tls_init(void) +/** + * @brief Initialize CURL handle for downloading CRL. + * + * @param[out] handle CURL handle. + * @param[out] data Stores the downloaded data. + * @return 0 on success, 1 on failure. + */ +static int +nc_session_curl_init(CURL **handle, struct nc_curl_data *data) { -#if OPENSSL_VERSION_NUMBER < 0x10100000L // < 1.1.0 - SSL_load_error_strings(); - ERR_load_BIO_strings(); - SSL_library_init(); -#endif + NC_CHECK_ARG_RET(NULL, handle, data, -1); - nc_ssh_init(); + *handle = NULL; -#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 + *handle = curl_easy_init(); + if (!*handle) { + ERR(NULL, "Initializing CURL failed."); + return 1; + } + + if (curl_easy_setopt(*handle, CURLOPT_WRITEFUNCTION, nc_session_curl_cb)) { + ERR(NULL, "Setting curl callback failed."); + return 1; + } + + if (curl_easy_setopt(*handle, CURLOPT_WRITEDATA, data)) { + ERR(NULL, "Setting curl callback data failed."); + return 1; + } + + return 0; } -static void -nc_ssh_tls_destroy(void) +int +nc_session_tls_crl_from_cert_ext_fetch(void *leaf_cert, void *cert_store, void **crl_store) { -#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 + int ret = 0, uri_count = 0, i; + CURL *handle = NULL; + struct nc_curl_data downloaded = {0}; + char **uris = NULL; + void *crl_store_aux = NULL; - nc_ssh_destroy(); + *crl_store = NULL; -#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 + crl_store_aux = nc_tls_crl_store_new_wrap(); + if (!crl_store_aux) { + goto cleanup; + } + + /* init curl */ + ret = nc_session_curl_init(&handle, &downloaded); + if (ret) { + goto cleanup; + } + + /* get all the uris we can, even though some may point to the same CRL */ + ret = nc_server_tls_get_crl_distpoint_uris_wrap(leaf_cert, cert_store, &uris, &uri_count); + if (ret) { + goto cleanup; + } + + if (!uri_count) { + /* no CRL distribution points, nothing to download */ + goto cleanup; + } + + for (i = 0; i < uri_count; i++) { + VRB(NULL, "Downloading CRL from \"%s\".", uris[i]); + ret = nc_session_curl_fetch(handle, uris[i]); + if (ret) { + /* failed to download the CRL from this entry, try the next entry */ + WRN(NULL, "Failed to fetch CRL from \"%s\".", uris[i]); + continue; + } + + /* convert the downloaded data to CRL and add it to the store */ + ret = nc_server_tls_add_crl_to_store_wrap(downloaded.data, downloaded.size, crl_store_aux); + + /* free the downloaded data */ + free(downloaded.data); + downloaded.data = NULL; + downloaded.size = 0; + + if (ret) { + goto cleanup; + } + } + + *crl_store = crl_store_aux; + crl_store_aux = NULL; + +cleanup: + for (i = 0; i < uri_count; i++) { + free(uris[i]); + } + free(uris); + curl_easy_cleanup(handle); + nc_tls_crl_store_destroy_wrap(crl_store_aux); + return ret; } -#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 76c7cc7..adeea6f 100644 --- a/src/session.h +++ b/src/session.h @@ -1,9 +1,11 @@ /** - * \file session.h - * \author Radek Krejci - * \brief libnetconf2 session manipulation + * @file session.h + * @author Radek Krejci + * @author Michal Vasko + * @brief libnetconf2 session manipulation * - * Copyright (c) 2015 CESNET, z.s.p.o. + * @copyright + * 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. @@ -21,7 +23,7 @@ extern "C" { #include "netconf.h" -#ifdef NC_ENABLED_SSH +#ifdef NC_ENABLED_SSH_TLS /** * @brief Enumeration of NETCONF SSH authentication methods @@ -32,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 @@ -49,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 @@ -70,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 - 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 +#ifdef NC_ENABLED_SSH_TLS + NC_TI_SSH, /**< SSH - use libssh library, only for NETCONF over SSH transport */ + + NC_TI_TLS /**< TLS - use either OpenSSL or MbedTLS library, only for NETCONF over TLS transport */ +#endif /* NC_ENABLED_SSH_TLS */ } NC_TRANSPORT_IMPL; /** @@ -96,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 */ @@ -183,6 +191,18 @@ const char *nc_session_get_host(const struct nc_session *session); */ uint16_t nc_session_get_port(const struct nc_session *session); +#ifdef NC_ENABLED_SSH_TLS + +/** + * @brief Get the SSH banner sent by the peer. + * + * @param[in] session Session to get the banner from. + * @return SSH banner on success, NULL on error. + */ +const char *nc_session_ssh_get_banner(const struct nc_session *session); + +#endif + /** * @brief Get session path (unix socket only). * @@ -197,7 +217,7 @@ const char *nc_session_get_path(const struct nc_session *session); * @param[in] session Session to get the information from. * @return Session context. */ -struct ly_ctx *nc_session_get_ctx(const struct nc_session *session); +const struct ly_ctx *nc_session_get_ctx(const struct nc_session *session); /** * @brief Assign arbitrary data to a session. @@ -215,6 +235,14 @@ void nc_session_set_data(struct nc_session *session, void *data); */ void *nc_session_get_data(const struct nc_session *session); +/** + * @brief Learn whether a session was created using Call Home or not. + * + * @param[in] session Session to get the information from. + * @return 0 if a standard session, non-zero if a Call Home session. + */ +int nc_session_is_callhome(const struct nc_session *session); + /** * @brief Free the NETCONF session object. * @@ -223,20 +251,6 @@ void *nc_session_get_data(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 f6ce856..a5d9b47 100644 --- a/src/session_client.c +++ b/src/session_client.c @@ -1,9 +1,10 @@ /** - * \file session_client.c - * \author Michal Vasko - * \brief libnetconf2 session client functions + * @file session_client.c + * @author Michal Vasko + * @brief libnetconf2 session client functions * - * Copyright (c) 2015 CESNET, z.s.p.o. + * @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. @@ -38,24 +39,29 @@ #include #include +#ifdef NC_ENABLED_SSH_TLS +#include +#endif #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; @@ -67,22 +73,23 @@ static struct nc_client_context context_main = { .max_probes = 10, .probe_interval = 5 }, -#ifdef NC_ENABLED_SSH + .opts.monitoring_thread_data.lock = PTHREAD_MUTEX_INITIALIZER, +#ifdef NC_ENABLED_SSH_TLS .ssh_opts = { - .auth_pref = {{NC_SSH_AUTH_INTERACTIVE, 3}, {NC_SSH_AUTH_PASSWORD, 2}, {NC_SSH_AUTH_PUBLICKEY, 1}}, - .auth_hostkey_check = sshauth_hostkey_check, + .auth_pref = {{NC_SSH_AUTH_INTERACTIVE, 1}, {NC_SSH_AUTH_PASSWORD, 2}, {NC_SSH_AUTH_PUBLICKEY, 3}}, .auth_password = sshauth_password, .auth_interactive = sshauth_interactive, - .auth_privkey_passphrase = sshauth_privkey_passphrase + .auth_privkey_passphrase = sshauth_privkey_passphrase, + .knownhosts_mode = NC_SSH_KNOWNHOSTS_ASK }, .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 + .auth_privkey_passphrase = sshauth_privkey_passphrase, + .knownhosts_mode = NC_SSH_KNOWNHOSTS_ASK }, -#endif /* NC_ENABLED_SSH */ +#endif /* NC_ENABLED_SSH_TLS */ /* .tls_ structures zeroed */ .refcount = 0 }; @@ -109,8 +116,9 @@ 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) { close(c->opts.ch_binds[i].sock); free((char *)c->opts.ch_binds[i].address); @@ -118,15 +126,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); } } @@ -160,25 +166,37 @@ nc_client_context_location(void) e = calloc(1, sizeof *e); /* set default values */ e->refcount = 1; -#ifdef NC_ENABLED_SSH + e->opts.ka.enabled = 1; + e->opts.ka.idle_time = 1; + e->opts.ka.max_probes = 10; + e->opts.ka.probe_interval = 5; +#ifdef NC_ENABLED_SSH_TLS +# ifdef HAVE_TERMIOS + e->ssh_opts.knownhosts_mode = NC_SSH_KNOWNHOSTS_ASK; +# else + e->ssh_opts.knownhosts_mode = NC_SSH_KNOWNHOSTS_ACCEPT; +# endif e->ssh_opts.auth_pref[0].type = NC_SSH_AUTH_INTERACTIVE; - e->ssh_opts.auth_pref[0].value = 3; + 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 = 1; - e->ssh_opts.auth_hostkey_check = sshauth_hostkey_check; + e->ssh_opts.auth_pref[2].value = 3; e->ssh_opts.auth_password = sshauth_password; e->ssh_opts.auth_interactive = sshauth_interactive; e->ssh_opts.auth_privkey_passphrase = sshauth_privkey_passphrase; - /* callhome settings are the same except the inverted auth methods preferences */ + /* callhome settings are the same */ memcpy(&e->ssh_ch_opts, &e->ssh_opts, sizeof e->ssh_ch_opts); 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 */ } + + /* init the monitoring thread data lock */ + pthread_mutex_init(&e->opts.monitoring_thread_data.lock, NULL); + pthread_setspecific(nc_client_context_key, e); } @@ -199,7 +217,7 @@ nc_client_set_thread_context(void *context) struct nc_client_context *old, *new; if (!context) { - ERRARG(context); + ERRARG(NULL, "context"); return; } @@ -216,8 +234,32 @@ nc_client_set_thread_context(void *context) pthread_setspecific(nc_client_context_key, new); } +/** + * @brief Ext data callback for a context to provide schema mount data. + */ +static LY_ERR +nc_ly_ext_data_clb(const struct lysc_ext_instance *ext, void *user_data, void **ext_data, ly_bool *ext_data_free) +{ + struct nc_session *session = user_data; + + if (strcmp(ext->def->module->name, "ietf-yang-schema-mount") || strcmp(ext->def->name, "mount-point")) { + return LY_EINVAL; + } + + if (!session->opts.client.ext_data) { + ERR(session, "Unable to parse mounted data, no operational schema-mounts data received from the server."); + return LY_ENOTFOUND; + } + + /* return ext data */ + *ext_data = session->opts.client.ext_data; + *ext_data_free = 0; + + return LY_SUCCESS; +} + int -nc_session_new_ctx(struct nc_session *session, struct ly_ctx *ctx) +nc_client_session_new_ctx(struct nc_session *session, struct ly_ctx *ctx) { /* assign context (dicionary needed for handshake) */ if (!ctx) { @@ -225,15 +267,19 @@ nc_session_new_ctx(struct nc_session *session, struct ly_ctx *ctx) return EXIT_FAILURE; } - /* user path must be first, the first path is used to store schemas retreived via get-schema */ + /* user path must be first, the first path is used to store modules retreived via get-schema */ if (client_opts.schema_searchpath) { ly_ctx_set_searchdir(ctx, client_opts.schema_searchpath); - } else if (!access(NC_YANG_DIR, F_OK)) { - ly_ctx_set_searchdir(ctx, NC_YANG_DIR); + } + if (!access(NC_CLIENT_SEARCH_DIR, F_OK)) { + ly_ctx_set_searchdir(ctx, NC_CLIENT_SEARCH_DIR); } - /* set callback for getting schemas, if provided */ + /* set callback for getting modules, if provided */ ly_ctx_set_module_imp_clb(ctx, client_opts.schema_clb, client_opts.schema_clb_data); + + /* set ext data callback to avoid errors that no callback is set, the data are stored later, if any */ + ly_ctx_set_ext_data_clb(ctx, nc_ly_ext_data_clb, session); } else { session->flags |= NC_SESSION_SHAREDCTX; } @@ -252,10 +298,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; } @@ -291,9 +334,16 @@ nc_client_get_schema_callback(void **user_data) return client_opts.schema_clb; } -struct schema_info { +API void +nc_client_set_new_session_context_autofill(int enabled) +{ + client_opts.auto_context_fill_disabled = !enabled; +} + +struct module_info { char *name; char *revision; + struct { char *name; char *revision; @@ -305,83 +355,94 @@ struct schema_info { struct clb_data_s { void *user_data; ly_module_imp_clb user_clb; - struct schema_info *schemas; + struct module_info *modules; struct nc_session *session; int has_get_schema; }; /** - * @brief Retrieve YANG schema content from a local file. + * @brief Retrieve YANG module content from a local file. * - * @param[in] name Schema name. - * @param[in] rev Schema revision. + * @param[in] name Module name. + * @param[in] rev Module revision. * @param[in] clb_data get-schema callback data. - * @param[out] format Schema format. - * @return Schema content. + * @param[out] format Module format. + * @return Module content. */ static char * -retrieve_schema_data_localfile(const char *name, const char *rev, struct clb_data_s *clb_data, +retrieve_module_data_localfile(const char *name, const char *rev, struct clb_data_s *clb_data, LYS_INFORMAT *format) { - char *localfile = NULL; + char *localfile = NULL, *model_data = NULL; + const char *ptr; FILE *f; long length, l; - char *model_data = NULL; if (lys_search_localfile(ly_ctx_get_searchdirs(clb_data->session->ctx), !(ly_ctx_get_options(clb_data->session->ctx) & LY_CTX_DISABLE_SEARCHDIR_CWD), name, rev, &localfile, format)) { return NULL; } - if (localfile) { - VRB(clb_data->session, "Reading schema from localfile \"%s\".", localfile); - f = fopen(localfile, "r"); - if (!f) { - ERR(clb_data->session, "Unable to open \"%s\" file to get schema (%s).", localfile, strerror(errno)); - free(localfile); - return NULL; + if (localfile && rev) { + ptr = strrchr(localfile, '/'); + if (!strchr(ptr, '@')) { + /* we do not know the revision of the module and we require a specific one, so ignore this module */ + localfile = NULL; } - - fseek(f, 0, SEEK_END); - length = ftell(f); - if (length < 0) { - ERR(clb_data->session, "Unable to get size of schema file \"%s\".", localfile); - free(localfile); - fclose(f); - return NULL; - } - fseek(f, 0, SEEK_SET); - - model_data = malloc(length + 1); - if (!model_data) { - ERRMEM; - } else if ((l = fread(model_data, 1, length, f)) != length) { - ERR(clb_data->session, "Reading schema from \"%s\" failed (%d bytes read, but %d expected).", localfile, l, - length); - free(model_data); - model_data = NULL; - } else { - /* terminating NULL byte */ - model_data[length] = '\0'; - } - fclose(f); - free(localfile); } + if (!localfile) { + return NULL; + } + + VRB(clb_data->session, "Reading module \"%s@%s\" from local file \"%s\".", name, rev ? rev : "", + localfile); + f = fopen(localfile, "r"); + if (!f) { + ERR(clb_data->session, "Unable to open file \"%s\" (%s).", localfile, strerror(errno)); + free(localfile); + return NULL; + } + + fseek(f, 0, SEEK_END); + length = ftell(f); + if (length < 0) { + ERR(clb_data->session, "Unable to get the size of module file \"%s\".", localfile); + free(localfile); + fclose(f); + return NULL; + } + fseek(f, 0, SEEK_SET); + + model_data = malloc(length + 1); + if (!model_data) { + ERRMEM; + } else if ((l = fread(model_data, 1, length, f)) != length) { + ERR(clb_data->session, "Reading module from \"%s\" failed (%d bytes read, but %d expected).", localfile, l, + length); + free(model_data); + model_data = NULL; + } else { + /* terminating NULL byte */ + model_data[length] = '\0'; + } + fclose(f); + free(localfile); + return model_data; } /** - * @brief Retrieve YANG schema content from a reply to get-schema RPC. + * @brief Retrieve YANG module content from a reply to get-schema RPC. * - * @param[in] name Schema name. - * @param[in] rev Schema revision. + * @param[in] name Module name. + * @param[in] rev Module revision. * @param[in] clb_data get-schema callback data. - * @param[out] format Schema format. - * @return Schema content. + * @param[out] format Module format. + * @return Module content. */ static char * -retrieve_schema_data_getschema(const char *name, const char *rev, struct clb_data_s *clb_data, +retrieve_module_data_getschema(const char *name, const char *rev, struct clb_data_s *clb_data, LYS_INFORMAT *format) { struct nc_rpc *rpc; @@ -389,11 +450,10 @@ retrieve_schema_data_getschema(const char *name, const char *rev, struct clb_dat struct lyd_node_any *get_schema_data; NC_MSG_TYPE msg; uint64_t msgid; - char *localfile = NULL; + char *localfile = NULL, *envp_str = NULL, *model_data = NULL; FILE *f; - char *model_data = NULL; - VRB(clb_data->session, "Reading schema from server via get-schema."); + VRB(clb_data->session, "Reading module \"%s@%s\" from server via get-schema.", name, rev ? rev : ""); rpc = nc_rpc_getschema(name, rev, "yang", NC_PARAMTYPE_CONST); while ((msg = nc_send_rpc(clb_data->session, rpc, 0, &msgid)) == NC_MSG_WOULDBLOCK) { @@ -412,9 +472,15 @@ retrieve_schema_data_getschema(const char *name, const char *rev, struct clb_dat if (msg == NC_MSG_WOULDBLOCK) { ERR(clb_data->session, "Timeout for receiving reply to a expired."); goto cleanup; - } else if ((msg == NC_MSG_ERROR) || !op) { + } else if (msg == NC_MSG_ERROR) { ERR(clb_data->session, "Failed to receive a reply to ."); goto cleanup; + } else if (!op) { + assert(envp); + lyd_print_mem(&envp_str, envp, LYD_XML, 0); + WRN(clb_data->session, "Received an unexpected reply to :\n%s", envp_str); + free(envp_str); + goto cleanup; } if (!lyd_child(op) || (lyd_child(op)->schema->nodetype != LYS_ANYXML)) { @@ -441,27 +507,31 @@ retrieve_schema_data_getschema(const char *name, const char *rev, struct clb_dat free(model_data); model_data = NULL; } + if (!model_data) { + goto cleanup; + } - /* try to store the model_data into local schema repository */ - if (model_data) { - *format = LYS_IN_YANG; - if (client_opts.schema_searchpath) { - if (asprintf(&localfile, "%s/%s%s%s.yang", client_opts.schema_searchpath, name, rev ? "@" : "", - rev ? rev : "") == -1) { - ERRMEM; + /* set format */ + *format = LYS_IN_YANG; + + /* try to store the model_data into local module repository */ + lys_search_localfile(ly_ctx_get_searchdirs(clb_data->session->ctx), 0, name, rev, &localfile, NULL); + if (client_opts.schema_searchpath && !localfile) { + if (asprintf(&localfile, "%s/%s%s%s.yang", client_opts.schema_searchpath, name, rev ? "@" : "", + rev ? rev : "") == -1) { + ERRMEM; + } else { + f = fopen(localfile, "w"); + if (!f) { + WRN(clb_data->session, "Unable to store \"%s\" as a local copy of module retrieved via (%s).", + localfile, strerror(errno)); } else { - f = fopen(localfile, "w"); - if (!f) { - WRN(clb_data->session, "Unable to store \"%s\" as a local copy of schema retrieved via (%s).", - localfile, strerror(errno)); - } else { - fputs(model_data, f); - fclose(f); - } - free(localfile); + fputs(model_data, f); + fclose(f); } } } + free(localfile); cleanup: lyd_free_tree(envp); @@ -477,45 +547,83 @@ free_with_user_data(void *data, void *user_data) } /** - * @brief Retrieve YANG schema content. + * @brief Retrieve YANG module content. * - * @param[in] mod_name Schema name. - * @param[in] mod_rev Schema revision. - * @param[in] submod_name Optional submodule name. - * @param[in] sub_rev Submodule revision. + * @param[in] mod_name Module name. + * @param[in] mod_rev Module revision. * @param[in] user_data get-schema callback data. - * @param[out] format Schema format. - * @param[out] module_data Schema content. + * @param[out] format Module format. + * @param[out] module_data Module content. * @param[out] free_module_data Callback for freeing @p module_data. * @return LY_ERR value. */ static LY_ERR -retrieve_schema_data(const char *mod_name, const char *mod_rev, const char *submod_name, const char *sub_rev, +retrieve_module_data(const char *mod_name, const char *mod_rev, void *user_data, LYS_INFORMAT *format, + const char **module_data, void (**free_module_data)(void *model_data, void *user_data)) +{ + struct clb_data_s *clb_data = (struct clb_data_s *)user_data; + char *model_data = NULL; + + /* 1. try to get data locally */ + model_data = retrieve_module_data_localfile(mod_name, mod_rev, clb_data, format); + + /* 2. try to use */ + if (!model_data && clb_data->has_get_schema) { + model_data = retrieve_module_data_getschema(mod_name, mod_rev, clb_data, format); + } + + /* 3. try to use user callback */ + if (!model_data && clb_data->user_clb) { + VRB(clb_data->session, "Reading module \"%s@%s\" via user callback.", mod_name, mod_rev ? mod_rev : ""); + clb_data->user_clb(mod_name, mod_rev, NULL, NULL, clb_data->user_data, format, (const char **)&model_data, + free_module_data); + } + + *free_module_data = free_with_user_data; + *module_data = model_data; + return *module_data ? LY_SUCCESS : LY_ENOTFOUND; +} + +/** + * @brief Retrieve YANG import module content. + * + * @param[in] mod_name Module name. + * @param[in] mod_rev Module revision. + * @param[in] submod_name Optional submodule name. + * @param[in] sub_rev Submodule revision. + * @param[in] user_data get-schema callback data. + * @param[out] format Module format. + * @param[out] module_data Module content. + * @param[out] free_module_data Callback for freeing @p module_data. + * @return LY_ERR value. + */ +static LY_ERR +retrieve_module_data_imp(const char *mod_name, const char *mod_rev, const char *submod_name, const char *sub_rev, void *user_data, LYS_INFORMAT *format, const char **module_data, void (**free_module_data)(void *model_data, void *user_data)) { struct clb_data_s *clb_data = (struct clb_data_s *)user_data; - unsigned int u, v, match = 1; + uint32_t u, v, match = 1; const char *name = NULL, *rev = NULL; char *model_data = NULL; - /* get and check the final name and revision of the schema to be retrieved */ + /* get and check the final name and revision of the module to be retrieved */ if (!mod_rev || !mod_rev[0]) { /* newest revision requested - get the newest revision from the list of available modules on server */ match = 0; - for (u = 0; clb_data->schemas[u].name; ++u) { - if (strcmp(mod_name, clb_data->schemas[u].name)) { + for (u = 0; clb_data->modules[u].name; ++u) { + if (strcmp(mod_name, clb_data->modules[u].name)) { continue; } - if (!match || (strcmp(mod_rev, clb_data->schemas[u].revision) > 0)) { - mod_rev = clb_data->schemas[u].revision; + if (!match || (strcmp(mod_rev, clb_data->modules[u].revision) > 0)) { + mod_rev = clb_data->modules[u].revision; } match = u + 1; } if (!match) { - /* valid situation if we are retrieving YANG 1.1 schema and have only capabilities for now + /* valid situation if we are retrieving YANG 1.1 module and have only capabilities for now * (when loading ietf-datastore for ietf-yang-library) */ - VRB(clb_data->session, "Unable to identify revision of the schema \"%s\" from " + VRB(clb_data->session, "Unable to identify revision of the import module \"%s\" from " "the available server side information.", mod_name); } } @@ -524,17 +632,17 @@ retrieve_schema_data(const char *mod_name, const char *mod_rev, const char *subm if (sub_rev) { rev = sub_rev; } else if (match) { - if (!clb_data->schemas[match - 1].submodules) { + if (!clb_data->modules[match - 1].submodules) { VRB(clb_data->session, "Unable to identify revision of the requested submodule \"%s\", " - "in schema \"%s\", from the available server side information.", submod_name, mod_name); + "in import module \"%s\", from the available server side information.", submod_name, mod_name); } else { - for (v = 0; clb_data->schemas[match - 1].submodules[v].name; ++v) { - if (!strcmp(submod_name, clb_data->schemas[match - 1].submodules[v].name)) { - rev = sub_rev = clb_data->schemas[match - 1].submodules[v].revision; + for (v = 0; clb_data->modules[match - 1].submodules[v].name; ++v) { + if (!strcmp(submod_name, clb_data->modules[match - 1].submodules[v].name)) { + rev = sub_rev = clb_data->modules[match - 1].submodules[v].revision; } } if (!rev) { - ERR(clb_data->session, "Requested submodule \"%s\" is not known for schema \"%s\" on server side.", + ERR(clb_data->session, "Requested submodule \"%s\" is not found in import module \"%s\" on server side.", submod_name, mod_name); return LY_ENOTFOUND; } @@ -545,21 +653,18 @@ retrieve_schema_data(const char *mod_name, const char *mod_rev, const char *subm rev = mod_rev; } - VRB(clb_data->session, "Retrieving data for schema \"%s\", revision \"%s\".", name, rev ? rev : ""); - if (match) { - /* we have enough information to avoid communication with server and try to get - * the schema locally */ + /* we have enough information to avoid communication with server and try to get the module locally */ /* 1. try to get data locally */ - model_data = retrieve_schema_data_localfile(name, rev, clb_data, format); + model_data = retrieve_module_data_localfile(name, rev, clb_data, format); /* 2. try to use */ if (!model_data && clb_data->has_get_schema) { - model_data = retrieve_schema_data_getschema(name, rev, clb_data, format); + model_data = retrieve_module_data_getschema(name, rev, clb_data, format); } } else { - /* we are unsure which revision of the schema we should load, so first try to get + /* we are unsure which revision of the module we should load, so first try to get * the newest revision from the server via get-schema and only if the server does not * implement get-schema, try to load the newest revision locally. This is imperfect * solution, but there are situation when a client does not know what revision is @@ -567,18 +672,18 @@ retrieve_schema_data(const char *mod_name, const char *mod_rev, const char *subm /* 1. try to use */ if (clb_data->has_get_schema) { - model_data = retrieve_schema_data_getschema(name, rev, clb_data, format); + model_data = retrieve_module_data_getschema(name, rev, clb_data, format); } /* 2. try to get data locally */ if (!model_data) { - model_data = retrieve_schema_data_localfile(name, rev, clb_data, format); + model_data = retrieve_module_data_localfile(name, rev, clb_data, format); } } /* 3. try to use user callback */ if (!model_data && clb_data->user_clb) { - VRB(clb_data->session, "Reading schema via user callback."); + VRB(clb_data->session, "Reading module \"%s@%s\" via user callback.", name, rev ? rev : ""); clb_data->user_clb(mod_name, mod_rev, submod_name, sub_rev, clb_data->user_data, format, (const char **)&model_data, free_module_data); } @@ -589,13 +694,14 @@ retrieve_schema_data(const char *mod_name, const char *mod_rev, const char *subm } /** - * @brief Load a YANG schema into context. + * @brief Load a YANG module into context. * * @param[in] session NC session. - * @param[in] name Schema name. - * @param[in] revision Schema revision. - * @param[in] schemas Server schema info built from capabilities. - * @param[in] user_clb User callback for retireving schema data. + * @param[in] name Module name. + * @param[in] revision Module revision. + * @param[in] features Enabled module features. + * @param[in] modules Server module info built from capabilities. + * @param[in] user_clb User callback for retrieving module data. * @param[in] user_data User data for @p user_clb. * @param[in] has_get_schema Whether the server supports get-schema. * @param[out] mod Loaded module. @@ -603,81 +709,92 @@ retrieve_schema_data(const char *mod_name, const char *mod_rev, const char *subm * @return -1 on error. */ static int -nc_ctx_load_module(struct nc_session *session, const char *name, const char *revision, struct schema_info *schemas, - ly_module_imp_clb user_clb, void *user_data, int has_get_schema, struct lys_module **mod) +nc_ctx_load_module(struct nc_session *session, const char *name, const char *revision, const char **features, + struct module_info *modules, ly_module_imp_clb user_clb, void *user_data, int has_get_schema, struct lys_module **mod) { int ret = 0; - struct ly_err_item *eitem; + const struct ly_err_item *eitem; const char *module_data = NULL; + struct ly_in *in; LYS_INFORMAT format; + uint32_t temp_lo = LY_LOSTORE, *prev_lo; void (*free_module_data)(void *, void *) = NULL; struct clb_data_s clb_data; - *mod = NULL; - if (revision) { - *mod = ly_ctx_get_module(session->ctx, name, revision); + /* try to use a module from the context */ + *mod = ly_ctx_get_module_implemented(session->ctx, name); + if (!*mod) { + if (revision) { + *mod = ly_ctx_get_module(session->ctx, name, revision); + } else { + *mod = ly_ctx_get_module_latest(session->ctx, name); + } + } else if (revision && (!(*mod)->revision || strcmp((*mod)->revision, revision))) { + WRN(session, "Server implements module \"%s\" in revision \"%s\" but revision \"%s\" is already implemented" + " and will be used instead.", name, revision, (*mod)->revision ? (*mod)->revision : ""); } + if (*mod) { - if (!(*mod)->implemented) { - /* make the present module implemented */ - if (lys_set_implemented(*mod, NULL)) { - ERR(session, "Failed to implement model \"%s\".", (*mod)->name); - ret = -1; - } + /* make the present module implemented and/or enable all its features */ + if (lys_set_implemented(*mod, features)) { + ERR(session, "Failed to implement module \"%s\".", (*mod)->name); + return -1; } + return 0; + } + + /* missing implemented module, load it ... */ + clb_data.has_get_schema = has_get_schema; + clb_data.modules = modules; + clb_data.session = session; + clb_data.user_clb = user_clb; + clb_data.user_data = user_data; + + /* clear all the errors and just collect them for now */ + ly_err_clean(session->ctx, NULL); + prev_lo = ly_temp_log_options(&temp_lo); + + /* get module data */ + if (!retrieve_module_data(name, revision, &clb_data, &format, &module_data, &free_module_data)) { + /* set import callback */ + ly_ctx_set_module_imp_clb(session->ctx, retrieve_module_data_imp, &clb_data); + + /* parse the module */ + ly_in_new_memory(module_data, &in); + lys_parse(session->ctx, in, format, features, mod); + ly_in_free(in, 0); + if (free_module_data) { + free_module_data((char *)module_data, user_data); + } + + ly_ctx_set_module_imp_clb(session->ctx, NULL, NULL); + } + + /* restore logging options, then print errors on definite failure */ + ly_temp_log_options(prev_lo); + if (!(*mod)) { + for (eitem = ly_err_first(session->ctx); eitem; eitem = eitem->next) { + ly_err_print(session->ctx, eitem); + } + ret = -1; } else { - /* missing implemented module, load it ... */ - clb_data.has_get_schema = has_get_schema; - clb_data.schemas = schemas; - clb_data.session = session; - clb_data.user_clb = user_clb; - clb_data.user_data = user_data; - - /* clear all the errors and just collect them for now */ - ly_err_clean(session->ctx, NULL); - ly_log_options(LY_LOSTORE); - - /* get module data */ - retrieve_schema_data(name, revision, NULL, NULL, &clb_data, &format, &module_data, &free_module_data); - - if (module_data) { - /* parse the schema */ - ly_ctx_set_module_imp_clb(session->ctx, retrieve_schema_data, &clb_data); - - lys_parse_mem(session->ctx, module_data, format, mod); - if (*free_module_data) { - (*free_module_data)((char *)module_data, user_data); - } - - ly_ctx_set_module_imp_clb(session->ctx, NULL, NULL); - } - - /* restore logging options, then print errors on definite failure */ - ly_log_options(LY_LOLOG | LY_LOSTORE_LAST); - if (!(*mod)) { - for (eitem = ly_err_first(session->ctx); eitem && eitem->next; eitem = eitem->next) { + /* print only warnings */ + for (eitem = ly_err_first(session->ctx); eitem; eitem = eitem->next) { + if (eitem->level == LY_LLWRN) { ly_err_print(session->ctx, eitem); } - ret = -1; - } else { - /* print only warnings */ - for (eitem = ly_err_first(session->ctx); eitem && eitem->next; eitem = eitem->next) { - if (eitem->level == LY_LLWRN) { - ly_err_print(session->ctx, eitem); - } - } } - - /* clean the errors */ - ly_err_clean(session->ctx, NULL); } + /* clean the errors */ + ly_err_clean(session->ctx, NULL); + return ret; } static void -free_schema_info(struct schema_info *list) +free_module_info(struct module_info *list) { uint32_t u, v; @@ -706,48 +823,33 @@ free_schema_info(struct schema_info *list) } /** - * @brief Build server schema info from ietf-yang-library data. + * @brief Retrieve yang-library and schema-mounts operational data from the server. * * @param[in] session NC session. * @param[in] has_get_data Whether get-data RPC is available or only get. - * @param[out] result Server schemas. + * @param[in] filter Filter to use. + * @param[out] oper_data Received data. * @return 0 on success. * @return -1 on error. */ static int -build_schema_info_yl(struct nc_session *session, int has_get_data, struct schema_info **result) +get_oper_data(struct nc_session *session, int has_get_data, const char *filter, struct lyd_node **oper_data) { struct nc_rpc *rpc = NULL; struct lyd_node *op = NULL, *envp = NULL; struct lyd_node_any *data; NC_MSG_TYPE msg; uint64_t msgid; - struct ly_set *modules = NULL; - uint32_t u, v, submodules_count, feature_count; - struct lyd_node *iter, *child; - struct lys_module *mod; int ret = 0; const char *rpc_name; - /* get yang-library data from the server */ + /* get data from the server */ if (has_get_data) { - rpc_name = "get-data"; - if (nc_session_cpblt(session, "urn:ietf:params:netconf:capability:xpath:1.0")) { - rpc = nc_rpc_getdata("ietf-datastores:operational", "/ietf-yang-library:*", "false", NULL, 0, 0, 0, 0, 0, - NC_PARAMTYPE_CONST); - } else { - rpc = nc_rpc_getdata("ietf-datastores:operational", - "", "false", NULL, 0, 0, 0, 0, - 0, NC_PARAMTYPE_CONST); - } + rpc_name = ""; + rpc = nc_rpc_getdata("ietf-datastores:operational", filter, "false", NULL, 0, 0, 0, 0, 0, NC_PARAMTYPE_CONST); } else { - rpc_name = "get"; - if (nc_session_cpblt(session, "urn:ietf:params:netconf:capability:xpath:1.0")) { - rpc = nc_rpc_get("/ietf-yang-library:*", 0, NC_PARAMTYPE_CONST); - } else { - rpc = nc_rpc_get("", 0, - NC_PARAMTYPE_CONST); - } + rpc_name = ""; + rpc = nc_rpc_get(filter, 0, NC_PARAMTYPE_CONST); } if (!rpc) { goto cleanup; @@ -757,7 +859,7 @@ build_schema_info_yl(struct nc_session *session, int has_get_data, struct schema usleep(1000); } if (msg == NC_MSG_ERROR) { - WRN(session, "Failed to send request for yang-library data."); + WRN(session, "Failed to send %s RPC.", rpc_name); goto cleanup; } @@ -768,36 +870,85 @@ build_schema_info_yl(struct nc_session *session, int has_get_data, struct schema msg = nc_recv_reply(session, rpc, msgid, NC_READ_ACT_TIMEOUT * 1000, &envp, &op); } while (msg == NC_MSG_NOTIF || msg == NC_MSG_REPLY_ERR_MSGID); if (msg == NC_MSG_WOULDBLOCK) { - WRN(session, "Timeout for receiving reply to a <%s> yang-library data expired.", rpc_name); + WRN(session, "Timeout for receiving reply to a %s RPC expired.", rpc_name); goto cleanup; } else if (msg == NC_MSG_ERROR) { - WRN(session, "Failed to receive a reply to <%s> of yang-library data.", rpc_name); + WRN(session, "Failed to receive a reply to %s RPC.", rpc_name); goto cleanup; - } else if (!op || !lyd_child(op) || strcmp(lyd_child(op)->schema->name, "data")) { - WRN(session, "Unexpected reply without data to a yang-library <%s> RPC.", rpc_name); + } else if (!op || !lyd_child(op) || !lyd_child(op)->schema || strcmp(lyd_child(op)->schema->name, "data")) { + WRN(session, "Unexpected reply without data to a %s RPC.", rpc_name); goto cleanup; } data = (struct lyd_node_any *)lyd_child(op); if (data->value_type != LYD_ANYDATA_DATATREE) { - WRN(session, "Unexpected data in reply to a yang-library <%s> RPC.", rpc_name); + WRN(session, "Unexpected data in reply to a %s RPC.", rpc_name); goto cleanup; } else if (!data->value.tree) { - WRN(session, "No data in reply to a yang-library <%s> RPC.", rpc_name); + WRN(session, "No data in reply to a %s RPC.", rpc_name); goto cleanup; } - if (lyd_find_xpath(data->value.tree, "/ietf-yang-library:modules-state/module", &modules)) { - WRN(session, "No module information in reply to a yang-library <%s> RPC.", rpc_name); + *oper_data = data->value.tree; + data->value.tree = NULL; + +cleanup: + nc_rpc_free(rpc); + lyd_free_tree(envp); + lyd_free_tree(op); + + if (session->status != NC_STATUS_RUNNING) { + /* something bad happened, discard the session */ + ERR(session, "Invalid session, discarding."); + ret = -1; + } + + return ret; +} + +/** + * @brief Build server module info from ietf-yang-library data. + * + * @param[in] session NC session. + * @param[in] get_data_sup Whether get-data RPC is available or only get. + * @param[in] xpath_sup Whether XPath filter is supported or only subtree filter. + * @param[out] result Server modules. + * @return 0 on success. + * @return -1 on error. + */ +static int +build_module_info_yl(struct nc_session *session, int get_data_sup, int xpath_sup, struct module_info **result) +{ + struct ly_set *modules = NULL; + uint32_t u, v, submodules_count, feature_count; + struct lyd_node *iter, *child, *oper_data = NULL; + struct lys_module *mod; + int ret = 0; + uint8_t notifications_found = 0; + uint8_t nc_notifications_found = 0; + + /* get yang-library operational data */ + if (xpath_sup) { + if (get_oper_data(session, get_data_sup, "/ietf-yang-library:*", &oper_data)) { + goto cleanup; + } + } else { + if (get_oper_data(session, get_data_sup, + "", &oper_data)) { + goto cleanup; + } + } + if (!oper_data) { + goto cleanup; + } + + if (lyd_find_xpath(oper_data, "/ietf-yang-library:modules-state/module", &modules)) { + WRN(NULL, "No yang-library module information found."); goto cleanup; } (*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; @@ -814,19 +965,18 @@ build_schema_info_yl(struct nc_session *session, int has_get_data, struct schema } if (!strcmp(iter->schema->name, "name")) { (*result)[u].name = strdup(lyd_get_value(iter)); + if (!strcmp((*result)[u].name, "notifications")) { + notifications_found = 1; + } else if (!strcmp((*result)[u].name, "nc-notifications")) { + nc_notifications_found = 1; + } } else if (!strcmp(iter->schema->name, "revision")) { (*result)[u].revision = strdup(lyd_get_value(iter)); } else if (!strcmp(iter->schema->name, "conformance-type")) { (*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_schema_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; @@ -837,25 +987,18 @@ build_schema_info_yl(struct nc_session *session, int has_get_data, struct schema if (submodules_count) { (*result)[u].submodules = calloc(submodules_count + 1, sizeof *(*result)[u].submodules); - if (!(*result)[u].submodules) { - ERRMEM; - free_schema_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)); } } } @@ -863,41 +1006,49 @@ build_schema_info_yl(struct nc_session *session, int has_get_data, struct schema } } -cleanup: - nc_rpc_free(rpc); - lyd_free_tree(envp); - lyd_free_tree(op); - ly_set_free(modules, NULL); + /* If NETCONF server supports RFC5277 notification capability and libnetconf2 + * required notifications and nc-notifications are not present on the NETCONF + * server (which it is not obligated to support), then the libyang context + * needs to be initialized using client side local YANG schema files */ + if (nc_session_cpblt(session, "urn:ietf:params:netconf:capability:notification:1.0") && + !notifications_found && !nc_notifications_found) { - if (session->status != NC_STATUS_RUNNING) { - /* something bad happened, discard the session */ - ERR(session, "Invalid session, discarding."); - ret = -1; + (*result) = nc_realloc(*result, (modules->count + 3) * sizeof **result); + NC_CHECK_ERRMEM_GOTO(!(*result), ret = -1, cleanup); + + (*result)[u].name = strdup("notifications"); + (*result)[u].revision = strdup("2008-07-14"); + (*result)[u].implemented = 1; + u++; + + (*result)[u].name = strdup("nc-notifications"); + (*result)[u].revision = strdup("2008-07-14"); + (*result)[u].implemented = 1; } +cleanup: + lyd_free_siblings(oper_data); + ly_set_free(modules, NULL); return ret; } /** - * @brief Build server schema info from received capabilities. + * @brief Build server module info from received capabilities. * * @param[in] cpblts Server capabilities. - * @param[out] result Server schemas. + * @param[out] result Server modules. * @return 0 on success. * @return -1 on error. */ static int -build_schema_info_cpblts(char **cpblts, struct schema_info **result) +build_module_info_cpblts(char **cpblts, struct module_info **result) { uint32_t u, v, feature_count; char *module_cpblt, *ptr, *ptr2; 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="); @@ -956,18 +1107,18 @@ build_schema_info_cpblts(char **cpblts, struct schema_info **result) } /** - * @brief Fill client context based on server schema info. + * @brief Fill client context based on server modules info. * * @param[in] session NC session with the context to modify. - * @param[in] modules Server schema info. - * @param[in] user_clb User callback for retrieving specific schemas. + * @param[in] modules Server modules info. + * @param[in] user_clb User callback for retrieving specific modules. * @param[in] user_data User data for @p user_clb. * @param[in] has_get_schema Whether server supports get-schema RPC. * @return 0 on success. * @return -1 on error. */ static int -nc_ctx_fill(struct nc_session *session, struct schema_info *modules, ly_module_imp_clb user_clb, void *user_data, +nc_ctx_fill(struct nc_session *session, struct module_info *modules, ly_module_imp_clb user_clb, void *user_data, int has_get_schema) { int ret = -1; @@ -981,7 +1132,8 @@ nc_ctx_fill(struct nc_session *session, struct schema_info *modules, ly_module_i } /* we can continue even if it fails */ - nc_ctx_load_module(session, modules[u].name, modules[u].revision, modules, user_clb, user_data, has_get_schema, &mod); + nc_ctx_load_module(session, modules[u].name, modules[u].revision, (const char **)modules[u].features, modules, + user_clb, user_data, has_get_schema, &mod); if (!mod) { if (session->status != NC_STATUS_RUNNING) { @@ -990,13 +1142,10 @@ nc_ctx_fill(struct nc_session *session, struct schema_info *modules, ly_module_i goto cleanup; } - /* all loading ways failed, the schema will be ignored in the received data */ - WRN(session, "Failed to load schema \"%s@%s\".", modules[u].name, modules[u].revision ? + /* all loading ways failed, the module will be ignored in the received data */ + WRN(session, "Failed to load module \"%s@%s\".", modules[u].name, modules[u].revision ? modules[u].revision : ""); session->flags |= NC_SESSION_CLIENT_NOT_STRICT; - } else { - /* set the features */ - lys_set_implemented(mod, (const char **)modules[u].features); } } @@ -1008,64 +1157,145 @@ cleanup: } /** - * @brief Fill client context with ietf-netconf schema. + * @brief Fill client context with ietf-netconf module. * * @param[in] session NC session with the context to modify. - * @param[in] modules Server schema info. - * @param[in] user_clb User callback for retrieving specific schemas. + * @param[in] modules Server module info. + * @param[in] user_clb User callback for retrieving specific modules. * @param[in] user_data User data for @p user_clb. * @param[in] has_get_schema Whether server supports get-schema RPC. * @return 0 on success. * @return -1 on error. */ static int -nc_ctx_fill_ietf_netconf(struct nc_session *session, struct schema_info *modules, ly_module_imp_clb user_clb, +nc_ctx_fill_ietf_netconf(struct nc_session *session, struct module_info *modules, ly_module_imp_clb user_clb, void *user_data, int has_get_schema) { uint32_t u; + const char **features = NULL; + struct ly_in *in; struct lys_module *ietfnc; - ietfnc = ly_ctx_get_module_implemented(session->ctx, "ietf-netconf"); - if (!ietfnc) { - nc_ctx_load_module(session, "ietf-netconf", NULL, modules, user_clb, user_data, has_get_schema, &ietfnc); - if (!ietfnc) { - lys_parse_mem(session->ctx, ietf_netconf_2013_09_29_yang, LYS_IN_YANG, &ietfnc); + /* find supported features (capabilities) in ietf-netconf */ + for (u = 0; modules[u].name; ++u) { + if (!strcmp(modules[u].name, "ietf-netconf")) { + assert(modules[u].implemented); + features = (const char **)modules[u].features; + break; } } - if (!ietfnc) { - ERR(session, "Loading base NETCONF schema failed."); + if (!modules[u].name) { + ERR(session, "Base NETCONF module not supported by the server."); return -1; } - /* set supported capabilities from ietf-netconf */ - for (u = 0; modules[u].name; ++u) { - if (strcmp(modules[u].name, "ietf-netconf") || !modules[u].implemented) { - continue; + ietfnc = ly_ctx_get_module_implemented(session->ctx, "ietf-netconf"); + if (ietfnc) { + /* make sure to enable all the features if already loaded */ + lys_set_implemented(ietfnc, features); + } else { + /* load the module */ + nc_ctx_load_module(session, "ietf-netconf", NULL, features, modules, user_clb, user_data, has_get_schema, &ietfnc); + if (!ietfnc) { + ly_in_new_memory(ietf_netconf_2013_09_29_yang, &in); + lys_parse(session->ctx, in, LYS_IN_YANG, features, &ietfnc); + ly_in_free(in, 0); } - - lys_set_implemented(ietfnc, (const char **)modules[u].features); + } + if (!ietfnc) { + ERR(session, "Loading base NETCONF module failed."); + return -1; } return 0; } +API int +nc_client_set_new_session_context_schema_mount(struct nc_session *session) +{ + int rc = 0, yanglib_support = 0, xpath_support = 0, nmda_support = 0; + struct lyd_node *oper_data = NULL; + const struct lys_module *mod; + + if (session->flags & NC_SESSION_SHAREDCTX) { + /* context is already fully set up */ + goto cleanup; + } + + /* check all useful capabilities */ + if (ly_ctx_get_module_implemented(session->ctx, "ietf-yang-library")) { + yanglib_support = 1; + } + if ((mod = ly_ctx_get_module_implemented(session->ctx, "ietf-netconf")) && !lys_feature_value(mod, "xpath")) { + xpath_support = 1; + } + if (ly_ctx_get_module_implemented(session->ctx, "ietf-netconf-nmda")) { + nmda_support = 1; + } + + if (!yanglib_support) { + ERR(session, "Module \"ietf-yang-library\" missing to retrieve schema-mount data."); + rc = -1; + goto cleanup; + } + + /* get yang-library and schema-mounts operational data */ + if (xpath_support) { + if ((rc = get_oper_data(session, nmda_support, "/ietf-yang-library:* | /ietf-yang-schema-mount:*", &oper_data))) { + goto cleanup; + } + } else { + if ((rc = get_oper_data(session, nmda_support, + "" + "", &oper_data))) { + goto cleanup; + } + } + + if (!oper_data || lyd_find_path(oper_data, "/ietf-yang-schema-mount:schema-mounts", 0, NULL)) { + /* no schema-mounts operational data */ + goto cleanup; + } + + /* validate the data for the parent reference prefixes to be resolved */ + if (lyd_validate_all(&oper_data, NULL, LYD_VALIDATE_PRESENT, NULL)) { + ERR(session, "Invalid operational data received from the server (%s).", ly_err_last(LYD_CTX(oper_data))->msg); + rc = -1; + goto cleanup; + } + + /* store the data in the session */ + lyd_free_siblings(session->opts.client.ext_data); + session->opts.client.ext_data = oper_data; + oper_data = NULL; + +cleanup: + lyd_free_siblings(oper_data); + return rc; +} + int nc_ctx_check_and_fill(struct nc_session *session) { - int i, get_schema_support = 0, yanglib_support = 0, get_data_support = 0, ret = -1; + int i, get_schema_support = 0, yanglib_support = 0, xpath_support = 0, nmda_support = 0, ret = -1; ly_module_imp_clb old_clb = NULL; void *old_data = NULL; struct lys_module *mod = NULL; char *revision; - struct schema_info *server_modules = NULL, *sm = NULL; + struct module_info *server_modules = NULL, *sm = NULL; assert(session->opts.client.cpblts && session->ctx); + if (client_opts.auto_context_fill_disabled) { + VRB(session, "Context of the new session is left only with the default YANG modules."); + return 0; + } + /* store the original user's callback, we will be switching between local search, get-schema and user callback */ old_clb = ly_ctx_get_module_imp_clb(session->ctx, &old_data); /* switch off default searchpath to use only our callback integrating modifying searchpath algorithm to limit - * schemas only to those present on the server side */ + * modules only to those present on the server side */ ly_ctx_set_options(session->ctx, LY_CTX_DISABLE_SEARCHDIRS); /* our callback is set later with appropriate data */ @@ -1075,36 +1305,26 @@ nc_ctx_check_and_fill(struct nc_session *session) for (i = 0; session->opts.client.cpblts[i]; ++i) { if (!strncmp(session->opts.client.cpblts[i], "urn:ietf:params:xml:ns:yang:ietf-netconf-monitoring?", 52)) { get_schema_support = 1 + i; - if (yanglib_support) { - break; - } } else if (!strncmp(session->opts.client.cpblts[i], "urn:ietf:params:netconf:capability:yang-library:", 48)) { yanglib_support = 1 + i; - if (get_schema_support) { - break; - } + } else if (!strncmp(session->opts.client.cpblts[i], "urn:ietf:params:netconf:capability:xpath:1.0", 44)) { + xpath_support = 1 + i; } + /* NMDA is YANG 1.1 module, which is not present in the capabilities */ } - if (get_schema_support) { - VRB(session, "Capability for support found."); - } else { - VRB(session, "Capability for support not found."); - } - if (yanglib_support) { - VRB(session, "Capability for yang-library support found."); - } else { - VRB(session, "Capability for yang-library support not found."); - } + VRB(session, "Capability for support%s found.", get_schema_support ? "" : " not"); + VRB(session, "Capability for yang-library support%s found.", yanglib_support ? "" : " not"); + VRB(session, "Capability for XPath filter support%s found.", xpath_support ? "" : " not"); - /* get information about server's schemas from capabilities list until we will have yang-library */ - if (build_schema_info_cpblts(session->opts.client.cpblts, &server_modules) || !server_modules) { - ERR(session, "Unable to get server's schema information from the 's capabilities."); + /* get information about server's modules from capabilities list until we will have yang-library */ + if (build_module_info_cpblts(session->opts.client.cpblts, &server_modules) || !server_modules) { + ERR(session, "Unable to get server module information from the 's capabilities."); goto cleanup; } /* get-schema is supported, load local ietf-netconf-monitoring so we can create RPCs */ if (get_schema_support && lys_parse_mem(session->ctx, ietf_netconf_monitoring_2010_10_04_yang, LYS_IN_YANG, NULL)) { - WRN(session, "Loading NETCONF monitoring schema failed, cannot use ."); + WRN(session, "Loading NETCONF monitoring module failed, cannot use ."); get_schema_support = 0; } @@ -1115,72 +1335,94 @@ nc_ctx_check_and_fill(struct nc_session *session) /* get correct version of ietf-yang-library into context */ if (yanglib_support) { - /* use get schema to get server's ietf-yang-library */ + /* use get-schema to get server's ietf-yang-library */ revision = strstr(session->opts.client.cpblts[yanglib_support - 1], "revision="); if (!revision) { - WRN(session, "Loading NETCONF ietf-yang-library schema failed, missing revision in NETCONF message."); + WRN(session, "Loading NETCONF ietf-yang-library module failed, missing revision in NETCONF message."); WRN(session, "Unable to automatically use ."); yanglib_support = 0; } else { revision = strndup(&revision[9], 10); - if (nc_ctx_load_module(session, "ietf-yang-library", revision, server_modules, old_clb, old_data, + if (nc_ctx_load_module(session, "ietf-yang-library", revision, NULL, server_modules, old_clb, old_data, get_schema_support, &mod)) { - WRN(session, "Loading NETCONF ietf-yang-library schema failed, unable to use it to learn all " + WRN(session, "Loading NETCONF ietf-yang-library module failed, unable to use it to learn all " "the supported modules."); yanglib_support = 0; } if (strcmp(revision, "2019-01-04") >= 0) { /* we also need ietf-datastores to be implemented */ - if (nc_ctx_load_module(session, "ietf-datastores", NULL, server_modules, old_clb, old_data, + if (nc_ctx_load_module(session, "ietf-datastores", NULL, NULL, server_modules, old_clb, old_data, get_schema_support, &mod)) { - WRN(session, "Loading NETCONF ietf-datastores schema failed, unable to use yang-library " + WRN(session, "Loading NETCONF ietf-datastores module failed, unable to use yang-library " "to learn all the supported modules."); yanglib_support = 0; } } free(revision); - - /* ietf-netconf-nmda is needed to issue get-data */ - if (!nc_ctx_load_module(session, "ietf-netconf-nmda", NULL, server_modules, old_clb, old_data, - get_schema_support, &mod)) { - VRB(session, "Support for from ietf-netcon-nmda found."); - get_data_support = 1; - } } } - /* prepare structured information about server's schemas */ + /* prepare structured information about server's modules */ if (yanglib_support) { - if (build_schema_info_yl(session, get_data_support, &sm)) { + if (build_module_info_yl(session, 0, xpath_support, &sm)) { goto cleanup; } else if (!sm) { VRB(session, "Trying to use capabilities instead of ietf-yang-library data."); } else { /* prefer yang-library information, currently we have it from capabilities used for getting correct - * yang-library schema */ - free_schema_info(server_modules); + * yang-library module */ + free_module_info(server_modules); server_modules = sm; + + /* check for NMDA support */ + for (i = 0; server_modules[i].name; ++i) { + if (!strcmp(server_modules[i].name, "ietf-netconf-nmda") && server_modules[i].implemented) { + nmda_support = 1; + break; + } + } + + /* ietf-netconf-nmda is needed to issue get-data */ + if (nmda_support && nc_ctx_load_module(session, "ietf-netconf-nmda", NULL, NULL, server_modules, old_clb, + old_data, get_schema_support, &mod)) { + WRN(session, "Loading NMDA module failed, unable to use ."); + } } } + /* compile all modules at once to avoid invalid errors or warnings */ + ly_ctx_set_options(session->ctx, LY_CTX_EXPLICIT_COMPILE); + + /* fill the context */ if (nc_ctx_fill(session, server_modules, old_clb, old_data, get_schema_support)) { goto cleanup; } - /* succsess */ + /* compile it */ + if (ly_ctx_compile(session->ctx)) { + goto cleanup; + } + + /* set support for schema-mount, if possible (requires ietf-yang-library support) */ + if (yanglib_support && nc_client_set_new_session_context_schema_mount(session)) { + goto cleanup; + } + + /* success */ ret = 0; if (session->flags & NC_SESSION_CLIENT_NOT_STRICT) { - WRN(session, "Some models failed to be loaded, any data from these models (and any other unknown) will " + WRN(session, "Some modules failed to be loaded, any data from these modules (and any other unknown) will " "be ignored."); } cleanup: - free_schema_info(server_modules); + free_module_info(server_modules); /* set user callback back */ ly_ctx_set_module_imp_clb(session->ctx, old_clb, old_data); ly_ctx_unset_options(session->ctx, LY_CTX_DISABLE_SEARCHDIRS); + ly_ctx_unset_options(session->ctx, LY_CTX_EXPLICIT_COMPILE); return ret; } @@ -1191,19 +1433,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 */ @@ -1211,7 +1450,7 @@ nc_connect_inout(int fdin, int fdout, struct ly_ctx *ctx) session->ti.fd.in = fdin; session->ti.fd.out = fdout; - if (nc_session_new_ctx(session, ctx) != EXIT_SUCCESS) { + if (nc_client_session_new_ctx(session, ctx) != EXIT_SUCCESS) { goto fail; } ctx = session->ctx; @@ -1226,6 +1465,11 @@ nc_connect_inout(int fdin, int fdout, struct ly_ctx *ctx) goto fail; } + /* start monitoring the session if the monitoring thread is running */ + if (nc_client_monitoring_session_start(session)) { + goto fail; + } + return session; fail: @@ -1244,10 +1488,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) { @@ -1271,10 +1512,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 */ @@ -1282,25 +1520,22 @@ nc_connect_unix(const char *address, struct ly_ctx *ctx) session->ti.unixsock.sock = sock; sock = -1; /* do not close sock in fail label anymore */ - if (nc_session_new_ctx(session, ctx) != EXIT_SUCCESS) { + if (nc_client_session_new_ctx(session, ctx) != EXIT_SUCCESS) { goto fail; } ctx = session->ctx; - lydict_insert(ctx, address, 0, &session->path); + 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; - } - lydict_insert_zc(ctx, username, &session->username); + NC_CHECK_ERRMEM_GOTO(!username, , fail); + session->username = username; /* NETCONF handshake */ if (nc_handshake_io(session) != NC_MSG_HELLO) { @@ -1312,6 +1547,11 @@ nc_connect_unix(const char *address, struct ly_ctx *ctx) goto fail; } + /* start monitoring the session if the monitoring thread is running */ + if (nc_client_monitoring_session_start(session)) { + goto fail; + } + return session; fail: @@ -1322,39 +1562,80 @@ fail: return NULL; } -/* - Helper for a non-blocking connect (which is required because of the locking - concept for e.g. call home settings). For more details see nc_sock_connect(). +/** + * @brief Convert socket IP address to string. + * + * @param[in] saddr Sockaddr to convert. + * @param[out] str_ip String IP address. + * @param[out] port Optional port. + * @return 0 on success. + * @return -1 on error. */ static int -_non_blocking_connect(int timeout, int *sock_pending, struct addrinfo *res, struct nc_keepalives *ka) +nc_saddr2str(const struct sockaddr *saddr, char **str_ip, uint16_t *port) { - int flags, ret, error; + void *addr; + socklen_t str_len; + + assert((saddr->sa_family == AF_INET) || (saddr->sa_family == AF_INET6)); + + str_len = (saddr->sa_family == AF_INET) ? INET_ADDRSTRLEN : INET6_ADDRSTRLEN; + *str_ip = malloc(str_len); + NC_CHECK_ERRMEM_RET(!(*str_ip), -1); + + if (saddr->sa_family == AF_INET) { + addr = &((struct sockaddr_in *)saddr)->sin_addr; + if (port) { + *port = ntohs(((struct sockaddr_in *)saddr)->sin_port); + } + } else { + addr = &((struct sockaddr_in6 *)saddr)->sin6_addr; + if (port) { + *port = ntohs(((struct sockaddr_in6 *)saddr)->sin6_port); + } + } + if (!inet_ntop(saddr->sa_family, addr, *str_ip, str_len)) { + ERR(NULL, "Converting host to IP address failed (%s).", strerror(errno)); + free(*str_ip); + return -1; + } + + return 0; +} + +/** + * @brief Try to connect a socket, optionally a pending one from a previous attempt. + * + * @param[in] src_addr Specific source address to bind to, used only for CH. + * @param[in] src_port Specific source port to bind to, used only for CH. + * @param[in] timeout_ms Timeout in ms to wait for the connection to be fully established, -1 to block. + * @param[in,out] sock_pending Optional previously created socked that was not fully connected yet. If provided and + * connected, is set to -1. + * @param[in] res Addrinfo resource to use when creating a new socket. + * @param[in] ka Keepalives to set. + * @return Connected socket or -1 on error. + */ +static int +sock_connect(const char *src_addr, uint16_t src_port, int timeout_ms, int *sock_pending, struct addrinfo *res, + const struct nc_keepalives *ka) +{ + int flags, ret, error, opt; int sock = -1; - fd_set wset; - struct timeval ts; + struct pollfd fds = {0}; socklen_t len = sizeof(int); - struct in_addr *addr; uint16_t port; - char str[INET6_ADDRSTRLEN]; + char *str; if (sock_pending && (*sock_pending != -1)) { VRB(NULL, "Trying to connect the pending socket %d.", *sock_pending); sock = *sock_pending; } else { assert(res); - if (res->ai_family == AF_INET6) { - addr = (struct in_addr *) &((struct sockaddr_in6 *)res->ai_addr)->sin6_addr; - port = ntohs(((struct sockaddr_in6 *)res->ai_addr)->sin6_port); - } else { - addr = &((struct sockaddr_in *)res->ai_addr)->sin_addr; - port = ntohs(((struct sockaddr_in *)res->ai_addr)->sin_port); - } - if (!inet_ntop(res->ai_family, addr, str, res->ai_addrlen)) { - WRN(NULL, "inet_ntop() failed (%s).", strerror(errno)); - } else { - VRB(NULL, "Trying to connect via %s to %s:%u.", (res->ai_family == AF_INET6) ? "IPv6" : "IPv4", str, port); + if (nc_saddr2str(res->ai_addr, &str, &port)) { + return -1; } + VRB(NULL, "Trying to connect via %s to %s:%u.", (res->ai_family == AF_INET6) ? "IPv6" : "IPv4", str, port); + free(str); /* connect to a server */ sock = socket(res->ai_family, res->ai_socktype, res->ai_protocol); @@ -1367,6 +1648,21 @@ _non_blocking_connect(int timeout, int *sock_pending, struct addrinfo *res, stru ERR(NULL, "fcntl() failed (%s).", strerror(errno)); goto cleanup; } + + /* bind the socket to a specific address/port to make the connection from (CH only) */ + if (src_addr || src_port) { + /* enable address reuse, so that we're able to bind this address again when the CH conn is dropped and retried */ + opt = 1; + if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof opt) == -1) { + ERR(NULL, "Could not set SO_REUSEADDR socket option (%s).", strerror(errno)); + goto cleanup; + } + + if (nc_sock_bind_inet(sock, src_addr, src_port, (res->ai_family == AF_INET) ? 1 : 0)) { + goto cleanup; + } + } + /* non-blocking connect! */ if (connect(sock, res->ai_addr, res->ai_addrlen) < 0) { if (errno != EINPROGRESS) { @@ -1376,20 +1672,19 @@ _non_blocking_connect(int timeout, int *sock_pending, struct addrinfo *res, stru } } } - ts.tv_sec = timeout; - ts.tv_usec = 0; - FD_ZERO(&wset); - FD_SET(sock, &wset); + fds.fd = sock; + fds.events = POLLOUT; - if ((ret = select(sock + 1, NULL, &wset, NULL, (timeout != -1) ? &ts : NULL)) < 0) { - ERR(NULL, "select() failed (%s).", strerror(errno)); + /* wait until we can write data to the socket */ + ret = poll(&fds, 1, timeout_ms); + if (ret == -1) { + /* error */ + ERR(NULL, "poll() failed (%s).", strerror(errno)); goto cleanup; - } - - if (ret == 0) { + } else if (ret == 0) { /* there was a timeout */ - VRB(NULL, "Timed out after %ds (%s).", timeout, strerror(errno)); + VRB(NULL, "Timed out after %d ms (%s).", timeout_ms, strerror(errno)); if (sock_pending) { /* no sock-close, we'll try it again */ *sock_pending = sock; @@ -1407,16 +1702,20 @@ _non_blocking_connect(int timeout, int *sock_pending, struct addrinfo *res, stru } if (error) { /* network connection failed, try another resource */ - VRB(NULL, "getsockopt() error (%s).", strerror(error)); + VRB(NULL, "Connection error (%s).", strerror(error)); errno = error; goto cleanup; } - /* enable keep-alive */ - if (nc_sock_enable_keepalive(sock, ka)) { + /* configure keepalives */ + if (nc_sock_configure_ka(sock, ka)) { goto cleanup; } + /* connected */ + if (sock_pending) { + *sock_pending = -1; + } return sock; cleanup: @@ -1427,41 +1726,35 @@ cleanup: return -1; } -/* A given timeout value limits the time how long the function blocks. If it has to block - only for some seconds, a socket connection might not yet have been fully established. - Therefore the active (pending) socket will be stored in *sock_pending, but the return - value will be -1. In such a case a subsequent invokation is required, by providing the - stored sock_pending, again. - In general, if this function returns -1, when a timeout has been given, this function - has to be invoked, until it returns a valid socket. - */ int -nc_sock_connect(const char *host, uint16_t port, int timeout, struct nc_keepalives *ka, int *sock_pending, char **ip_host) +nc_sock_connect(const char *src_addr, uint16_t src_port, const char *dst_addr, uint16_t dst_port, int timeout_ms, + struct nc_keepalives *ka, int *sock_pending, char **ip_host) { int i, opt; int sock = sock_pending ? *sock_pending : -1; struct addrinfo hints, *res_list = NULL, *res; - char *buf, port_s[6]; /* length of string representation of short int */ - void *addr; + char dst_port_str[6]; /* length of string representation of short int */ + struct sockaddr_storage saddr; + socklen_t addr_len = sizeof saddr; - DBG(NULL, "nc_sock_connect(%s, %u, %d, %d)", host, port, timeout, sock); + *ip_host = NULL; /* no pending socket */ if (sock == -1) { /* connect to a server */ - snprintf(port_s, 6, "%u", port); + snprintf(dst_port_str, 6, "%u", dst_port); memset(&hints, 0, sizeof hints); hints.ai_family = AF_UNSPEC; hints.ai_socktype = SOCK_STREAM; hints.ai_protocol = IPPROTO_TCP; - i = getaddrinfo(host, port_s, &hints, &res_list); + i = getaddrinfo(dst_addr, dst_port_str, &hints, &res_list); if (i != 0) { ERR(NULL, "Unable to translate the host address (%s).", gai_strerror(i)); goto error; } for (res = res_list; res != NULL; res = res->ai_next) { - sock = _non_blocking_connect(timeout, sock_pending, res, ka); + sock = sock_connect(src_addr, src_port, timeout_ms, sock_pending, res, ka); if (sock == -1) { if (!sock_pending || (*sock_pending == -1)) { /* try the next resource */ @@ -1471,7 +1764,12 @@ nc_sock_connect(const char *host, uint16_t port, int timeout, struct nc_keepaliv break; } } - VRB(NULL, "Successfully connected to %s:%s over %s.", host, port_s, (res->ai_family == AF_INET6) ? "IPv6" : "IPv4"); + + if (res->ai_family == AF_INET) { + VRB(NULL, "Successfully connected to %s:%s over IPv4.", dst_addr, dst_port_str); + } else { + VRB(NULL, "Successfully connected to [%s]:%s over IPv6.", dst_addr, dst_port_str); + } opt = 1; if (setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, &opt, sizeof opt) == -1) { @@ -1479,24 +1777,8 @@ nc_sock_connect(const char *host, uint16_t port, int timeout, struct nc_keepaliv goto error; } - if (ip_host && ((res->ai_family == AF_INET6) || (res->ai_family == AF_INET))) { - buf = malloc(INET6_ADDRSTRLEN); - if (!buf) { - ERRMEM; - goto error; - } - if (res->ai_family == AF_INET) { - addr = &((struct sockaddr_in *)res->ai_addr)->sin_addr; - } else { - addr = &((struct sockaddr_in6 *)res->ai_addr)->sin6_addr; - } - if (!inet_ntop(res->ai_family, addr, buf, INET6_ADDRSTRLEN)) { - ERR(NULL, "Converting host to IP address failed (%s).", strerror(errno)); - free(buf); - goto error; - } - - *ip_host = buf; + if (nc_saddr2str(res->ai_addr, ip_host, NULL)) { + goto error; } break; } @@ -1505,7 +1787,18 @@ nc_sock_connect(const char *host, uint16_t port, int timeout, struct nc_keepaliv } else { /* try to get a connection with the pending socket */ assert(sock_pending); - sock = _non_blocking_connect(timeout, sock_pending, NULL, ka); + sock = sock_connect(src_addr, src_port, timeout_ms, sock_pending, NULL, ka); + + if (sock > 0) { + if (getpeername(sock, (struct sockaddr *)&saddr, &addr_len)) { + ERR(NULL, "getpeername failed (%s).", strerror(errno)); + goto error; + } + + if (nc_saddr2str((struct sockaddr *)&saddr, ip_host, NULL)) { + goto error; + } + } } return sock; @@ -1523,22 +1816,16 @@ error: 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, NC_TRANSPORT_IMPL ti) +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); + sock = nc_sock_listen_inet(address, port); if (sock == -1) { return -1; } @@ -1551,20 +1838,16 @@ nc_client_ch_add_bind_listen(const char *address, uint16_t port, NC_TRANSPORT_IM return -1; } - client_opts.ch_bind_ti = nc_realloc(client_opts.ch_bind_ti, client_opts.ch_bind_count * sizeof *client_opts.ch_bind_ti); - if (!client_opts.ch_bind_ti) { + client_opts.ch_binds_aux = nc_realloc(client_opts.ch_binds_aux, client_opts.ch_bind_count * sizeof *client_opts.ch_binds_aux); + if (!client_opts.ch_binds_aux) { ERRMEM; close(sock); return -1; } - client_opts.ch_bind_ti[client_opts.ch_bind_count - 1] = ti; + client_opts.ch_binds_aux[client_opts.ch_bind_count - 1].ti = ti; + client_opts.ch_binds_aux[client_opts.ch_bind_count - 1].hostname = hostname ? strdup(hostname) : NULL; client_opts.ch_binds[client_opts.ch_bind_count - 1].address = strdup(address); - if (!client_opts.ch_binds[client_opts.ch_bind_count - 1].address) { - ERRMEM; - close(sock); - return -1; - } client_opts.ch_binds[client_opts.ch_bind_count - 1].port = port; client_opts.ch_binds[client_opts.ch_bind_count - 1].sock = sock; client_opts.ch_binds[client_opts.ch_bind_count - 1].pollin = 0; @@ -1581,28 +1864,40 @@ nc_client_ch_del_bind(const char *address, uint16_t port, NC_TRANSPORT_IMPL ti) if (!address && !port && !ti) { for (i = 0; i < client_opts.ch_bind_count; ++i) { close(client_opts.ch_binds[i].sock); - free((char *)client_opts.ch_binds[i].address); + free(client_opts.ch_binds[i].address); + + free(client_opts.ch_binds_aux[i].hostname); ret = 0; } + client_opts.ch_bind_count = 0; + free(client_opts.ch_binds); client_opts.ch_binds = NULL; - client_opts.ch_bind_count = 0; + + free(client_opts.ch_binds_aux); + client_opts.ch_binds_aux = NULL; } else { for (i = 0; i < client_opts.ch_bind_count; ++i) { if ((!address || !strcmp(client_opts.ch_binds[i].address, address)) && (!port || (client_opts.ch_binds[i].port == port)) && - (!ti || (client_opts.ch_bind_ti[i] == ti))) { + (!ti || (client_opts.ch_binds_aux[i].ti == ti))) { close(client_opts.ch_binds[i].sock); - free((char *)client_opts.ch_binds[i].address); + free(client_opts.ch_binds[i].address); --client_opts.ch_bind_count; if (!client_opts.ch_bind_count) { free(client_opts.ch_binds); client_opts.ch_binds = NULL; + + free(client_opts.ch_binds_aux); + client_opts.ch_binds_aux = NULL; } else if (i < client_opts.ch_bind_count) { - memcpy(&client_opts.ch_binds[i], &client_opts.ch_binds[client_opts.ch_bind_count], sizeof *client_opts.ch_binds); - client_opts.ch_bind_ti[i] = client_opts.ch_bind_ti[client_opts.ch_bind_count]; + memcpy(&client_opts.ch_binds[i], &client_opts.ch_binds[client_opts.ch_bind_count], + sizeof *client_opts.ch_binds); + + memcpy(&client_opts.ch_binds_aux[i], &client_opts.ch_binds_aux[client_opts.ch_bind_count], + sizeof *client_opts.ch_binds_aux); } ret = 0; @@ -1616,36 +1911,37 @@ nc_client_ch_del_bind(const char *address, uint16_t port, NC_TRANSPORT_IMPL ti) API int nc_accept_callhome(int timeout, struct ly_ctx *ctx, struct nc_session **session) { - int sock; + int ret, sock; 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"); + ERR(NULL, "Call-Home binds not set."); return -1; } - sock = nc_sock_accept_binds(client_opts.ch_binds, client_opts.ch_bind_count, timeout, &host, &port, &idx); - - if (sock < 1) { + ret = nc_sock_accept_binds(client_opts.ch_binds, client_opts.ch_bind_count, &client_opts.ch_bind_lock, timeout, + &host, &port, &idx, &sock); + if (ret < 1) { free(host); - return sock; + return ret; } -#ifdef NC_ENABLED_SSH - if (client_opts.ch_bind_ti[idx] == NC_TI_LIBSSH) { + /* configure keepalives */ + if (nc_sock_configure_ka(sock, &client_opts.ka)) { + free(host); + close(sock); + return -1; + } + + if (client_opts.ch_binds_aux[idx].ti == NC_TI_SSH) { *session = nc_accept_callhome_ssh_sock(sock, host, port, ctx, NC_TRANSPORT_TIMEOUT); - } else -#endif -#ifdef NC_ENABLED_TLS - if (client_opts.ch_bind_ti[idx] == NC_TI_OPENSSL) { - *session = nc_accept_callhome_tls_sock(sock, host, port, ctx, NC_TRANSPORT_TIMEOUT); - } else -#endif - { + } else if (client_opts.ch_binds_aux[idx].ti == NC_TI_TLS) { + *session = nc_accept_callhome_tls_sock(sock, host, port, ctx, NC_TRANSPORT_TIMEOUT, + client_opts.ch_binds_aux[idx].hostname); + } else { close(sock); *session = NULL; } @@ -1659,15 +1955,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; } @@ -1677,13 +1970,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) { @@ -1698,34 +1985,47 @@ 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) ? 1 : 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; + } + +#ifdef NC_ENABLED_SSH_TLS + if (ssh_init()) { + ERR(NULL, "%s: failed to init libssh.", __func__); + return -1; + } +#endif + + 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(); + ssh_finalize(); +#endif /* NC_ENABLED_SSH_TLS */ } static NC_MSG_TYPE @@ -1826,7 +2126,7 @@ get_msg_type(struct nc_session *session, struct ly_in *msg) * @param[out] message If receiving a message succeeded this is the message, NULL otherwise. * @return NC_MSG_REPLY If a rpc-reply was received; * @return NC_MSG_NOTIF If a notification was received; - * @return NC_MSG_ERROR If any error occured; + * @return NC_MSG_ERROR If any error occurred; * @return NC_MSG_WOULDBLOCK If the timeout was reached. */ static NC_MSG_TYPE @@ -1894,11 +2194,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; @@ -1924,6 +2220,7 @@ recv_reply(struct nc_session *session, int timeout, struct lyd_node *op, uint64_ LY_ERR lyrc; struct ly_in *msg = NULL; NC_MSG_TYPE ret = NC_MSG_ERROR; + uint32_t temp_lo = LY_LOSTORE, *prev_lo; assert(op && (op->schema->nodetype & (LYS_RPC | LYS_ACTION))); @@ -1936,12 +2233,21 @@ recv_reply(struct nc_session *session, int timeout, struct lyd_node *op, uint64_ } /* parse */ + prev_lo = ly_temp_log_options(&temp_lo); lyrc = lyd_parse_op(NULL, op, msg, LYD_XML, LYD_TYPE_REPLY_NETCONF, envp, NULL); - if (!lyrc) { + ly_temp_log_options(prev_lo); + + if (*envp) { + /* if the envelopes were parsed, check the message-id, even on error */ ret = recv_reply_check_msgid(session, *envp, msgid); goto cleanup; - } else { - ERR(session, "Received an invalid message (%s).", ly_errmsg(LYD_CTX(op))); + } + + if (lyrc) { + /* parsing error */ + ERR(session, "Received an invalid message (%s).", ly_err_last(LYD_CTX(op))->msg); + lyd_free_tree(*envp); + *envp = NULL; ret = NC_MSG_ERROR; goto cleanup; } @@ -1981,6 +2287,7 @@ recv_reply_dup_rpc(struct nc_session *session, struct nc_rpc *rpc, struct lyd_no lyrc = lyd_parse_op(session->ctx, NULL, in, LYD_XML, LYD_TYPE_RPC_YANG, &tree, &op2); ly_in_free(in, 0); if (lyrc) { + lyd_free_tree(tree); return -1; } @@ -2092,7 +2399,7 @@ recv_reply_dup_rpc(struct nc_session *session, struct nc_rpc *rpc, struct lyd_no if (module_name && rpc_name) { mod = ly_ctx_get_module_implemented(session->ctx, module_name); if (!mod) { - ERR(session, "Missing \"%s\" schema in the context.", module_name); + ERR(session, "Missing \"%s\" module in the context.", module_name); return -1; } @@ -2101,7 +2408,7 @@ recv_reply_dup_rpc(struct nc_session *session, struct nc_rpc *rpc, struct lyd_no } if (module_check) { if (!ly_ctx_get_module_implemented(session->ctx, module_check)) { - ERR(session, "Missing \"%s\" schema in the context.", module_check); + ERR(session, "Missing \"%s\" module in the context.", module_check); return -1; } } @@ -2118,22 +2425,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 (!msgid) { - ERRARG("msgid"); - 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; } @@ -2175,7 +2469,9 @@ recv_notif(struct nc_session *session, int timeout, struct lyd_node **envp, stru if (!lyrc) { goto cleanup; } else { - ERR(session, "Received an invalid message (%s).", ly_errmsg(session->ctx)); + ERR(session, "Received an invalid message (%s).", ly_err_last(session->ctx)->msg); + lyd_free_tree(*envp); + *envp = NULL; ret = NC_MSG_ERROR; goto cleanup; } @@ -2188,16 +2484,9 @@ cleanup: 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; } @@ -2211,8 +2500,10 @@ nc_recv_notif_thread(void *arg) { struct nc_ntf_thread_arg *ntarg; struct nc_session *session; + nc_notif_dispatch_clb notif_clb; + void *user_data; - void (*notif_clb)(struct nc_session *session, const struct lyd_node *envp, const struct lyd_node *op); + void (*free_data)(void *); struct lyd_node *envp, *op; NC_MSG_TYPE msgtype; @@ -2222,19 +2513,21 @@ nc_recv_notif_thread(void *arg) ntarg = (struct nc_ntf_thread_arg *)arg; session = ntarg->session; notif_clb = ntarg->notif_clb; + user_data = ntarg->user_data; + free_data = ntarg->free_data; free(ntarg); - while (ATOMIC_LOAD_RELAXED(session->opts.client.ntf_thread) == 1) { + while (ATOMIC_LOAD_RELAXED(session->opts.client.ntf_thread_running)) { msgtype = nc_recv_notif(session, NC_CLIENT_NOTIF_THREAD_SLEEP / 1000, &envp, &op); if (msgtype == NC_MSG_NOTIF) { - notif_clb(session, envp, op); + notif_clb(session, envp, op, user_data); if (!strcmp(op->schema->name, "notificationComplete") && !strcmp(op->schema->module->name, "nc-notifications")) { - lyd_free_tree(envp); - lyd_free_tree(op); + lyd_free_all(envp); + lyd_free_all(op); break; } - lyd_free_tree(envp); - lyd_free_tree(op); + lyd_free_all(envp); + lyd_free_all(op); } else if ((msgtype == NC_MSG_ERROR) && (session->status != NC_STATUS_RUNNING)) { /* quit this thread once the session is broken */ break; @@ -2244,48 +2537,54 @@ nc_recv_notif_thread(void *arg) } VRB(session, "Notification thread exit."); - ATOMIC_STORE_RELAXED(session->opts.client.ntf_thread, 0); + ATOMIC_DEC_RELAXED(session->opts.client.ntf_thread_count); + if (free_data) { + free_data(user_data); + } + return NULL; } API int -nc_recv_notif_dispatch(struct nc_session *session, void (*notif_clb)(struct nc_session *session, - const struct lyd_node *envp, const struct lyd_node *op)) +nc_recv_notif_dispatch(struct nc_session *session, nc_notif_dispatch_clb notif_clb) +{ + return nc_recv_notif_dispatch_data(session, notif_clb, NULL, NULL); +} + +API int +nc_recv_notif_dispatch_data(struct nc_session *session, nc_notif_dispatch_clb notif_clb, void *user_data, + void (*free_data)(void *)) { struct nc_ntf_thread_arg *ntarg; 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; - } else if (ATOMIC_LOAD_RELAXED(session->opts.client.ntf_thread)) { - ERR(session, "Separate notification thread is already running."); - 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; + ntarg->free_data = free_data; + ATOMIC_INC_RELAXED(session->opts.client.ntf_thread_count); /* just so that nc_recv_notif_thread() does not immediately exit */ - ATOMIC_STORE_RELAXED(session->opts.client.ntf_thread, 1); + ATOMIC_STORE_RELAXED(session->opts.client.ntf_thread_running, 1); ret = pthread_create(&tid, NULL, nc_recv_notif_thread, ntarg); if (ret) { ERR(session, "Failed to create a new thread (%s).", strerror(errno)); free(ntarg); - ATOMIC_STORE_RELAXED(session->opts.client.ntf_thread, 0); + if (ATOMIC_DEC_RELAXED(session->opts.client.ntf_thread_count) == 1) { + ATOMIC_STORE_RELAXED(session->opts.client.ntf_thread_running, 0); + } return -1; } @@ -2346,16 +2645,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; } @@ -2378,21 +2670,21 @@ nc_send_rpc(struct nc_session *session, struct nc_rpc *rpc, int timeout, uint64_ case NC_RPC_VALIDATE: mod = ly_ctx_get_module_implemented(session->ctx, "ietf-netconf"); if (!mod) { - ERR(session, "Missing \"ietf-netconf\" schema in the context."); + ERR(session, "Missing \"ietf-netconf\" module in the context."); return NC_MSG_ERROR; } break; case NC_RPC_GETSCHEMA: mod = ly_ctx_get_module_implemented(session->ctx, "ietf-netconf-monitoring"); if (!mod) { - ERR(session, "Missing \"ietf-netconf-monitoring\" schema in the context."); + ERR(session, "Missing \"ietf-netconf-monitoring\" module in the context."); return NC_MSG_ERROR; } break; case NC_RPC_SUBSCRIBE: mod = ly_ctx_get_module_implemented(session->ctx, "notifications"); if (!mod) { - ERR(session, "Missing \"notifications\" schema in the context."); + ERR(session, "Missing \"notifications\" module in the context."); return NC_MSG_ERROR; } break; @@ -2400,7 +2692,7 @@ nc_send_rpc(struct nc_session *session, struct nc_rpc *rpc, int timeout, uint64_ case NC_RPC_EDITDATA: mod = ly_ctx_get_module_implemented(session->ctx, "ietf-netconf-nmda"); if (!mod) { - ERR(session, "Missing \"ietf-netconf-nmda\" schema in the context."); + ERR(session, "Missing \"ietf-netconf-nmda\" module in the context."); return NC_MSG_ERROR; } break; @@ -2410,7 +2702,7 @@ nc_send_rpc(struct nc_session *session, struct nc_rpc *rpc, int timeout, uint64_ case NC_RPC_KILLSUB: mod = ly_ctx_get_module_implemented(session->ctx, "ietf-subscribed-notifications"); if (!mod) { - ERR(session, "Missing \"ietf-subscribed-notifications\" schema in the context."); + ERR(session, "Missing \"ietf-subscribed-notifications\" module in the context."); return NC_MSG_ERROR; } break; @@ -2418,19 +2710,19 @@ nc_send_rpc(struct nc_session *session, struct nc_rpc *rpc, int timeout, uint64_ case NC_RPC_MODIFYPUSH: mod = ly_ctx_get_module_implemented(session->ctx, "ietf-subscribed-notifications"); if (!mod) { - ERR(session, "Missing \"ietf-subscribed-notifications\" schema in the context."); + ERR(session, "Missing \"ietf-subscribed-notifications\" module in the context."); return NC_MSG_ERROR; } mod2 = ly_ctx_get_module_implemented(session->ctx, "ietf-yang-push"); if (!mod2) { - ERR(session, "Missing \"ietf-yang-push\" schema in the context."); + ERR(session, "Missing \"ietf-yang-push\" module in the context."); return NC_MSG_ERROR; } break; case NC_RPC_RESYNCSUB: mod = ly_ctx_get_module_implemented(session->ctx, "ietf-yang-push"); if (!mod) { - ERR(session, "Missing \"ietf-yang-push\" schema in the context."); + ERR(session, "Missing \"ietf-yang-push\" module in the context."); return NC_MSG_ERROR; } break; @@ -2466,10 +2758,10 @@ nc_send_rpc(struct nc_session *session, struct nc_rpc *rpc, int timeout, uint64_ CHECK_LYRC_BREAK(lyd_new_term(cont, mod, ncds2str[rpc_gc->source], NULL, 0, NULL)); if (rpc_gc->filter) { if (!rpc_gc->filter[0] || (rpc_gc->filter[0] == '<')) { - CHECK_LYRC_BREAK(lyd_new_any(data, mod, "filter", rpc_gc->filter, 0, LYD_ANYDATA_XML, 0, &node)); + CHECK_LYRC_BREAK(lyd_new_any(data, mod, "filter", rpc_gc->filter, LYD_ANYDATA_XML, 0, &node)); CHECK_LYRC_BREAK(lyd_new_meta(NULL, node, NULL, "ietf-netconf:type", "subtree", 0, NULL)); } else { - CHECK_LYRC_BREAK(lyd_new_any(data, mod, "filter", NULL, 0, LYD_ANYDATA_STRING, 0, &node)); + CHECK_LYRC_BREAK(lyd_new_any(data, mod, "filter", NULL, LYD_ANYDATA_STRING, 0, &node)); CHECK_LYRC_BREAK(lyd_new_meta(NULL, node, NULL, "ietf-netconf:type", "xpath", 0, NULL)); CHECK_LYRC_BREAK(lyd_new_meta(NULL, node, NULL, "ietf-netconf:select", rpc_gc->filter, 0, NULL)); } @@ -2478,7 +2770,7 @@ nc_send_rpc(struct nc_session *session, struct nc_rpc *rpc, int timeout, uint64_ if (rpc_gc->wd_mode) { ietfncwd = ly_ctx_get_module_implemented(session->ctx, "ietf-netconf-with-defaults"); if (!ietfncwd) { - ERR(session, "Missing \"ietf-netconf-with-defaults\" schema in the context.", session->id); + ERR(session, "Missing \"ietf-netconf-with-defaults\" module in the context."); lyrc = LY_ENOTFOUND; break; } @@ -2503,7 +2795,7 @@ nc_send_rpc(struct nc_session *session, struct nc_rpc *rpc, int timeout, uint64_ CHECK_LYRC_BREAK(lyd_new_term(data, mod, "error-option", rpcedit_erropt2str[rpc_e->error_opt], 0, NULL)); } if (!rpc_e->edit_cont[0] || (rpc_e->edit_cont[0] == '<')) { - CHECK_LYRC_BREAK(lyd_new_any(data, mod, "config", rpc_e->edit_cont, 0, LYD_ANYDATA_XML, 0, NULL)); + CHECK_LYRC_BREAK(lyd_new_any(data, mod, "config", rpc_e->edit_cont, LYD_ANYDATA_XML, 0, NULL)); } else { CHECK_LYRC_BREAK(lyd_new_term(data, mod, "url", rpc_e->edit_cont, 0, NULL)); } @@ -2523,7 +2815,7 @@ nc_send_rpc(struct nc_session *session, struct nc_rpc *rpc, int timeout, uint64_ CHECK_LYRC_BREAK(lyd_new_inner(data, mod, "source", 0, &cont)); if (rpc_cp->url_config_src) { if (!rpc_cp->url_config_src[0] || (rpc_cp->url_config_src[0] == '<')) { - CHECK_LYRC_BREAK(lyd_new_any(cont, mod, "config", rpc_cp->url_config_src, 0, LYD_ANYDATA_XML, 0, NULL)); + CHECK_LYRC_BREAK(lyd_new_any(cont, mod, "config", rpc_cp->url_config_src, LYD_ANYDATA_XML, 0, NULL)); } else { CHECK_LYRC_BREAK(lyd_new_term(cont, mod, "url", rpc_cp->url_config_src, 0, NULL)); } @@ -2534,7 +2826,7 @@ nc_send_rpc(struct nc_session *session, struct nc_rpc *rpc, int timeout, uint64_ if (rpc_cp->wd_mode) { ietfncwd = ly_ctx_get_module_implemented(session->ctx, "ietf-netconf-with-defaults"); if (!ietfncwd) { - ERR(session, "Missing \"ietf-netconf-with-defaults\" schema in the context.", session->id); + ERR(session, "Missing \"ietf-netconf-with-defaults\" module in the context."); lyrc = LY_ENOTFOUND; break; } @@ -2576,10 +2868,10 @@ nc_send_rpc(struct nc_session *session, struct nc_rpc *rpc, int timeout, uint64_ CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "get", 0, &data)); if (rpc_g->filter) { if (!rpc_g->filter[0] || (rpc_g->filter[0] == '<')) { - CHECK_LYRC_BREAK(lyd_new_any(data, mod, "filter", rpc_g->filter, 0, LYD_ANYDATA_XML, 0, &node)); + CHECK_LYRC_BREAK(lyd_new_any(data, mod, "filter", rpc_g->filter, LYD_ANYDATA_XML, 0, &node)); CHECK_LYRC_BREAK(lyd_new_meta(NULL, node, NULL, "ietf-netconf:type", "subtree", 0, NULL)); } else { - CHECK_LYRC_BREAK(lyd_new_any(data, mod, "filter", NULL, 0, LYD_ANYDATA_STRING, 0, &node)); + CHECK_LYRC_BREAK(lyd_new_any(data, mod, "filter", NULL, LYD_ANYDATA_STRING, 0, &node)); CHECK_LYRC_BREAK(lyd_new_meta(NULL, node, NULL, "ietf-netconf:type", "xpath", 0, NULL)); CHECK_LYRC_BREAK(lyd_new_meta(NULL, node, NULL, "ietf-netconf:select", rpc_g->filter, 0, NULL)); } @@ -2588,7 +2880,7 @@ nc_send_rpc(struct nc_session *session, struct nc_rpc *rpc, int timeout, uint64_ if (rpc_g->wd_mode) { ietfncwd = ly_ctx_get_module_implemented(session->ctx, "ietf-netconf-with-defaults"); if (!ietfncwd) { - ERR(session, "Missing \"ietf-netconf-with-defaults\" schema in the context.", session->id); + ERR(session, "Missing \"ietf-netconf-with-defaults\" module in the context."); lyrc = LY_ENOTFOUND; break; } @@ -2600,7 +2892,7 @@ nc_send_rpc(struct nc_session *session, struct nc_rpc *rpc, int timeout, uint64_ rpc_k = (struct nc_rpc_kill *)rpc; CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "kill-session", 0, &data)); - sprintf(str, "%u", rpc_k->sid); + sprintf(str, "%" PRIu32, rpc_k->sid); CHECK_LYRC_BREAK(lyd_new_term(data, mod, "session-id", str, 0, NULL)); break; @@ -2613,7 +2905,7 @@ nc_send_rpc(struct nc_session *session, struct nc_rpc *rpc, int timeout, uint64_ } if (rpc_com->confirm_timeout) { - sprintf(str, "%u", rpc_com->confirm_timeout); + sprintf(str, "%" PRIu32, rpc_com->confirm_timeout); CHECK_LYRC_BREAK(lyd_new_term(data, mod, "confirm-timeout", str, 0, NULL)); } if (rpc_com->persist) { @@ -2644,7 +2936,7 @@ nc_send_rpc(struct nc_session *session, struct nc_rpc *rpc, int timeout, uint64_ CHECK_LYRC_BREAK(lyd_new_inner(data, mod, "source", 0, &cont)); if (rpc_val->url_config_src) { if (!rpc_val->url_config_src[0] || (rpc_val->url_config_src[0] == '<')) { - CHECK_LYRC_BREAK(lyd_new_any(cont, mod, "config", rpc_val->url_config_src, 0, LYD_ANYDATA_XML, 0, NULL)); + CHECK_LYRC_BREAK(lyd_new_any(cont, mod, "config", rpc_val->url_config_src, LYD_ANYDATA_XML, 0, NULL)); } else { CHECK_LYRC_BREAK(lyd_new_term(cont, mod, "url", rpc_val->url_config_src, 0, NULL)); } @@ -2676,10 +2968,10 @@ nc_send_rpc(struct nc_session *session, struct nc_rpc *rpc, int timeout, uint64_ if (rpc_sub->filter) { if (!rpc_sub->filter[0] || (rpc_sub->filter[0] == '<')) { - CHECK_LYRC_BREAK(lyd_new_any(data, mod, "filter", rpc_sub->filter, 0, LYD_ANYDATA_XML, 0, &node)); + CHECK_LYRC_BREAK(lyd_new_any(data, mod, "filter", rpc_sub->filter, LYD_ANYDATA_XML, 0, &node)); CHECK_LYRC_BREAK(lyd_new_meta(NULL, node, NULL, "ietf-netconf:type", "subtree", 0, NULL)); } else { - CHECK_LYRC_BREAK(lyd_new_any(data, mod, "filter", NULL, 0, LYD_ANYDATA_STRING, 0, &node)); + CHECK_LYRC_BREAK(lyd_new_any(data, mod, "filter", NULL, LYD_ANYDATA_STRING, 0, &node)); CHECK_LYRC_BREAK(lyd_new_meta(NULL, node, NULL, "ietf-netconf:type", "xpath", 0, NULL)); CHECK_LYRC_BREAK(lyd_new_meta(NULL, node, NULL, "ietf-netconf:select", rpc_sub->filter, 0, NULL)); } @@ -2700,7 +2992,7 @@ nc_send_rpc(struct nc_session *session, struct nc_rpc *rpc, int timeout, uint64_ if (rpc_getd->filter) { if (!rpc_getd->filter[0] || (rpc_getd->filter[0] == '<')) { - CHECK_LYRC_BREAK(lyd_new_any(data, mod, "subtree-filter", rpc_getd->filter, 0, LYD_ANYDATA_XML, 0, NULL)); + CHECK_LYRC_BREAK(lyd_new_any(data, mod, "subtree-filter", rpc_getd->filter, LYD_ANYDATA_XML, 0, NULL)); } else { CHECK_LYRC_BREAK(lyd_new_term(data, mod, "xpath-filter", rpc_getd->filter, 0, NULL)); } @@ -2737,7 +3029,7 @@ nc_send_rpc(struct nc_session *session, struct nc_rpc *rpc, int timeout, uint64_ NULL)); } if (!rpc_editd->edit_cont[0] || (rpc_editd->edit_cont[0] == '<')) { - CHECK_LYRC_BREAK(lyd_new_any(data, mod, "config", rpc_editd->edit_cont, 0, LYD_ANYDATA_XML, 0, NULL)); + CHECK_LYRC_BREAK(lyd_new_any(data, mod, "config", rpc_editd->edit_cont, LYD_ANYDATA_XML, 0, NULL)); } else { CHECK_LYRC_BREAK(lyd_new_term(data, mod, "url", rpc_editd->edit_cont, 0, NULL)); } @@ -2750,7 +3042,7 @@ nc_send_rpc(struct nc_session *session, struct nc_rpc *rpc, int timeout, uint64_ if (rpc_estsub->filter) { if (!rpc_estsub->filter[0] || (rpc_estsub->filter[0] == '<')) { - CHECK_LYRC_BREAK(lyd_new_any(data, mod, "stream-subtree-filter", rpc_estsub->filter, 0, LYD_ANYDATA_XML, + CHECK_LYRC_BREAK(lyd_new_any(data, mod, "stream-subtree-filter", rpc_estsub->filter, LYD_ANYDATA_XML, 0, NULL)); } else if (rpc_estsub->filter[0] == '/') { CHECK_LYRC_BREAK(lyd_new_term(data, mod, "stream-xpath-filter", rpc_estsub->filter, 0, NULL)); @@ -2776,12 +3068,12 @@ nc_send_rpc(struct nc_session *session, struct nc_rpc *rpc, int timeout, uint64_ CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "modify-subscription", 0, &data)); - sprintf(str, "%u", rpc_modsub->id); + sprintf(str, "%" PRIu32, rpc_modsub->id); CHECK_LYRC_BREAK(lyd_new_term(data, mod, "id", str, 0, NULL)); if (rpc_modsub->filter) { if (!rpc_modsub->filter[0] || (rpc_modsub->filter[0] == '<')) { - CHECK_LYRC_BREAK(lyd_new_any(data, mod, "stream-subtree-filter", rpc_modsub->filter, 0, LYD_ANYDATA_XML, + CHECK_LYRC_BREAK(lyd_new_any(data, mod, "stream-subtree-filter", rpc_modsub->filter, LYD_ANYDATA_XML, 0, NULL)); } else if (rpc_modsub->filter[0] == '/') { CHECK_LYRC_BREAK(lyd_new_term(data, mod, "stream-xpath-filter", rpc_modsub->filter, 0, NULL)); @@ -2799,7 +3091,7 @@ nc_send_rpc(struct nc_session *session, struct nc_rpc *rpc, int timeout, uint64_ CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "delete-subscription", 0, &data)); - sprintf(str, "%u", rpc_delsub->id); + sprintf(str, "%" PRIu32, rpc_delsub->id); CHECK_LYRC_BREAK(lyd_new_term(data, mod, "id", str, 0, NULL)); break; @@ -2808,7 +3100,7 @@ nc_send_rpc(struct nc_session *session, struct nc_rpc *rpc, int timeout, uint64_ CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "kill-subscription", 0, &data)); - sprintf(str, "%u", rpc_killsub->id); + sprintf(str, "%" PRIu32, rpc_killsub->id); CHECK_LYRC_BREAK(lyd_new_term(data, mod, "id", str, 0, NULL)); break; @@ -2820,7 +3112,7 @@ nc_send_rpc(struct nc_session *session, struct nc_rpc *rpc, int timeout, uint64_ if (rpc_estpush->filter) { if (!rpc_estpush->filter[0] || (rpc_estpush->filter[0] == '<')) { - CHECK_LYRC_BREAK(lyd_new_any(data, mod2, "datastore-subtree-filter", rpc_estpush->filter, 0, + CHECK_LYRC_BREAK(lyd_new_any(data, mod2, "datastore-subtree-filter", rpc_estpush->filter, LYD_ANYDATA_XML, 0, NULL)); } else if (rpc_estpush->filter[0] == '/') { CHECK_LYRC_BREAK(lyd_new_term(data, mod2, "datastore-xpath-filter", rpc_estpush->filter, 0, NULL)); @@ -2865,13 +3157,13 @@ nc_send_rpc(struct nc_session *session, struct nc_rpc *rpc, int timeout, uint64_ CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "modify-subscription", 0, &data)); - sprintf(str, "%u", rpc_modpush->id); + sprintf(str, "%" PRIu32, rpc_modpush->id); CHECK_LYRC_BREAK(lyd_new_term(data, mod, "id", str, 0, NULL)); CHECK_LYRC_BREAK(lyd_new_term(data, mod2, "datastore", rpc_modpush->datastore, 0, NULL)); if (rpc_modpush->filter) { if (!rpc_modpush->filter[0] || (rpc_modpush->filter[0] == '<')) { - CHECK_LYRC_BREAK(lyd_new_any(data, mod2, "datastore-subtree-filter", rpc_modpush->filter, 0, + CHECK_LYRC_BREAK(lyd_new_any(data, mod2, "datastore-subtree-filter", rpc_modpush->filter, LYD_ANYDATA_XML, 0, NULL)); } else if (rpc_modpush->filter[0] == '/') { CHECK_LYRC_BREAK(lyd_new_term(data, mod2, "datastore-xpath-filter", rpc_modpush->filter, 0, NULL)); @@ -2903,7 +3195,7 @@ nc_send_rpc(struct nc_session *session, struct nc_rpc *rpc, int timeout, uint64_ rpc_resyncsub = (struct nc_rpc_resyncsub *)rpc; CHECK_LYRC_BREAK(lyd_new_inner(NULL, mod, "resync-subscription", 0, &data)); - sprintf(str, "%u", rpc_resyncsub->id); + sprintf(str, "%" PRIu32, rpc_resyncsub->id); CHECK_LYRC_BREAK(lyd_new_term(data, mod, "id", str, 0, NULL)); break; @@ -2938,9 +3230,414 @@ API void nc_client_session_set_not_strict(struct nc_session *session) { if (session->side != NC_CLIENT) { - ERRARG("session"); + ERRARG(NULL, "session"); return; } session->flags |= NC_SESSION_CLIENT_NOT_STRICT; } + +/** + * @brief Get the file descriptor of a session. + * + * @param[in] session Session. + * + * @return File descriptor of the session or -1 in case of an error. + */ +static int +nc_client_monitoring_get_session_fd(struct nc_session *session) +{ + int fd = -1; + + switch (session->ti_type) { + case NC_TI_FD: + fd = session->ti.fd.in; + break; + case NC_TI_UNIX: + fd = session->ti.unixsock.sock; + break; +#ifdef NC_ENABLED_SSH_TLS + case NC_TI_SSH: + fd = ssh_get_fd(session->ti.libssh.session); + break; + case NC_TI_TLS: + fd = nc_tls_get_fd_wrap(session); + break; +#endif /* NC_ENABLED_SSH_TLS */ + case NC_TI_NONE: + /* invalid */ + break; + } + + return fd; +} + +/** + * @brief Lock the client monitoring data. + * + * @param[in] timeout Timeout in msec to use. + * + * @return 1 on success; + * @return 0 on timeout; + * @return -1 on error. + */ +static int +nc_client_monitoring_lock(int timeout) +{ + int r; + struct timespec ts; + + if (timeout > 0) { + nc_timeouttime_get(&ts, timeout); + r = pthread_mutex_clocklock(&client_opts.monitoring_thread_data.lock, COMPAT_CLOCK_ID, &ts); + } else if (!timeout) { + r = pthread_mutex_trylock(&client_opts.monitoring_thread_data.lock); + } else { + r = pthread_mutex_lock(&client_opts.monitoring_thread_data.lock); + } + + if (r) { + ERR(NULL, "Failed to lock the client monitoring data lock (%s).", strerror(r)); + + if ((r == EBUSY) || (r == ETIMEDOUT)) { + /* timeout */ + return 0; + } + + /* error */ + return -1; + } + + return 1; +} + +int +nc_client_monitoring_session_start(struct nc_session *session) +{ + int ret = 0, fd, r; + struct nc_client_monitoring_thread_arg *mtarg; + void *tmp, *tmp2; + + /* LOCK */ + r = nc_client_monitoring_lock(NC_CLIENT_MONITORING_LOCK_TIMEOUT); + if (r < 1) { + return 1; + } + + /* check if the monitoring thread is running */ + if (!client_opts.monitoring_thread_data.thread_running) { + goto cleanup; + } + + mtarg = &client_opts.monitoring_thread_data; + + /* get the session's file descriptor */ + fd = nc_client_monitoring_get_session_fd(session); + assert(fd != -1); + + /* if realloc fails, keep the original sessions without adding the new one */ + tmp = realloc(mtarg->sessions, (mtarg->session_count + 1) * sizeof *mtarg->sessions); + NC_CHECK_ERRMEM_GOTO(!tmp, ret = 1, cleanup); + tmp2 = realloc(mtarg->pfds, (mtarg->pfd_count + 1) * sizeof *mtarg->pfds); + NC_CHECK_ERRMEM_GOTO(!tmp2, free(tmp); ret = 1, cleanup); + + mtarg->sessions = tmp; + mtarg->sessions[mtarg->session_count] = session; + mtarg->session_count++; + + /* we are only interested in POLLHUP or POLLNVAL */ + mtarg->pfds = tmp2; + mtarg->pfds[mtarg->pfd_count].fd = fd; + mtarg->pfds[mtarg->pfd_count].events = 0; + mtarg->pfds[mtarg->pfd_count].revents = 0; + mtarg->pfd_count++; + + session->flags |= NC_SESSION_CLIENT_MONITORED; + +cleanup: + /* UNLOCK */ + pthread_mutex_unlock(&client_opts.monitoring_thread_data.lock); + return ret; +} + +void +nc_client_monitoring_session_stop(struct nc_session *session, int lock) +{ + int i, r; + struct nc_client_monitoring_thread_arg *mtarg; + + if (lock) { + /* LOCK */ + r = nc_client_monitoring_lock(NC_CLIENT_MONITORING_LOCK_TIMEOUT); + if (r < 1) { + return; + } + } + + mtarg = &client_opts.monitoring_thread_data; + + /* session is no longer monitored (is being freed) */ + if (!(session->flags & NC_SESSION_CLIENT_MONITORED)) { + goto cleanup; + } + + /* find the session */ + for (i = 0; i < mtarg->session_count; i++) { + if (mtarg->sessions[i] == session) { + break; + } + } + if (i == mtarg->session_count) { + /* not found */ + ERRINT; + goto cleanup; + } + + /* remove the session */ + mtarg->session_count--; + mtarg->pfd_count--; + if (!mtarg->session_count) { + free(mtarg->sessions); + mtarg->sessions = NULL; + free(mtarg->pfds); + mtarg->pfds = NULL; + } else if (mtarg->sessions[i] != mtarg->sessions[mtarg->session_count]) { + mtarg->sessions[i] = mtarg->sessions[mtarg->session_count]; + mtarg->pfds[i] = mtarg->pfds[mtarg->session_count]; + } + + session->flags &= ~NC_SESSION_CLIENT_MONITORED; + +cleanup: + if (lock) { + /* UNLOCK */ + pthread_mutex_unlock(&mtarg->lock); + } +} + +/** + * @brief Monitoring thread for client sessions. + * + * @param[in] arg Thread context of the creating thread. + * @return NULL. + */ +static void * +nc_client_monitoring_thread(void *arg) +{ + int r, locked = 0; + struct nc_client_monitoring_thread_arg *mtarg; + nfds_t i; + struct nc_session *session = NULL; + + /* set this thread's context to the one from the thread that started the monitoring thread */ + nc_client_set_thread_context(arg); + + /* LOCK */ + r = nc_client_monitoring_lock(NC_CLIENT_MONITORING_LOCK_TIMEOUT); + if (r < 1) { + goto cleanup; + } + locked = 1; + + mtarg = &client_opts.monitoring_thread_data; + + while (mtarg->thread_running) { + if (!mtarg->session_count) { + goto next_iter; + } + + /* poll for either POLLIN or POLLHUP events */ + r = poll(mtarg->pfds, mtarg->pfd_count, 0); + if (r == -1) { + if (errno == EINTR) { + continue; + } + + ERR(NULL, "Client monitoring thread: poll failed (%s).", strerror(errno)); + goto cleanup; + } else if (!r) { + /* no events */ + goto next_iter; + } + + for (i = 0; i < mtarg->pfd_count; i++) { + if (mtarg->pfds[i].revents & (POLLHUP | POLLNVAL)) { + /* save the session and stop monitoring it, callback will be called outside of the lock */ + session = mtarg->sessions[i]; + session->status = NC_STATUS_INVALID; + session->term_reason = NC_SESSION_TERM_DROPPED; + nc_client_monitoring_session_stop(session, 0); + break; + } + } + +next_iter: + /* UNLOCK */ + pthread_mutex_unlock(&mtarg->lock); + locked = 0; + + /* if an event occurred, call the callback */ + if (session) { + mtarg->clb(session, mtarg->clb_data); + } + session = NULL; + + usleep(NC_CLIENT_MONITORING_BACKOFF * 1000); + + /* LOCK */ + r = nc_client_monitoring_lock(NC_CLIENT_MONITORING_LOCK_TIMEOUT); + if (r < 1) { + goto cleanup; + } + locked = 1; + } + +cleanup: + if (locked) { + /* UNLOCK */ + pthread_mutex_unlock(&mtarg->lock); + } + VRB(NULL, "Client monitoring thread exit."); + return NULL; +} + +API int +nc_client_monitoring_thread_start(nc_client_monitoring_clb monitoring_clb, void *user_data, void (*free_data)(void *)) +{ + int ret = 0, r; + void *ctx; + + NC_CHECK_ARG_RET(NULL, monitoring_clb, 1); + + /* LOCK */ + r = nc_client_monitoring_lock(NC_CLIENT_MONITORING_LOCK_TIMEOUT); + if (r < 1) { + return 1; + } + + if (client_opts.monitoring_thread_data.thread_running) { + ERR(NULL, "Client monitoring thread is already running."); + ret = 1; + goto cleanup; + } + + client_opts.monitoring_thread_data.clb = monitoring_clb; + client_opts.monitoring_thread_data.clb_data = user_data; + client_opts.monitoring_thread_data.clb_free_data = free_data; + + /* get the current thread context, so that the monitoring thread can use it */ + ctx = nc_client_get_thread_context(); + if (!ctx) { + ERR(NULL, "Failed to get the client thread context."); + ret = 1; + goto cleanup; + } + + client_opts.monitoring_thread_data.thread_running = 1; + + r = pthread_create(&client_opts.monitoring_thread_data.tid, NULL, + nc_client_monitoring_thread, ctx); + if (r) { + ERR(NULL, "Failed to create the client monitoring thread (%s).", strerror(r)); + ret = 1; + goto cleanup; + } + +cleanup: + /* UNLOCK */ + pthread_mutex_unlock(&client_opts.monitoring_thread_data.lock); + return ret; +} + +API void +nc_client_monitoring_thread_stop(void) +{ + int r, i; + pthread_t tid; + struct nc_client_monitoring_thread_arg *mtarg; + + /* LOCK */ + r = nc_client_monitoring_lock(NC_CLIENT_MONITORING_LOCK_TIMEOUT); + if (r < 1) { + return; + } + + if (!client_opts.monitoring_thread_data.thread_running) { + ERR(NULL, "Client monitoring thread is not running."); + + /* UNLOCK */ + pthread_mutex_unlock(&client_opts.monitoring_thread_data.lock); + return; + } + + mtarg = &client_opts.monitoring_thread_data; + + mtarg->thread_running = 0; + tid = mtarg->tid; + + /* remove the flag from the session while the lock is held */ + for (i = 0; i < mtarg->session_count; i++) { + mtarg->sessions[i]->flags &= ~NC_SESSION_CLIENT_MONITORED; + } + + /* UNLOCK */ + pthread_mutex_unlock(&mtarg->lock); + + r = pthread_join(tid, NULL); + if (r) { + ERR(NULL, "Failed to join the client monitoring thread (%s).", strerror(r)); + } + + /* LOCK */ + r = nc_client_monitoring_lock(NC_CLIENT_MONITORING_LOCK_TIMEOUT); + if (r < 1) { + return; + } + + if (mtarg->clb_free_data) { + mtarg->clb_free_data(mtarg->clb_data); + } + mtarg->clb = NULL; + mtarg->clb_data = NULL; + mtarg->clb_free_data = NULL; + + free(mtarg->sessions); + mtarg->sessions = NULL; + + free(mtarg->pfds); + mtarg->pfds = NULL; + + /* UNLOCK */ + pthread_mutex_unlock(&mtarg->lock); +} + +API void +nc_client_enable_tcp_keepalives(int enable) +{ + client_opts.ka.enabled = enable; +} + +API void +nc_client_set_tcp_keepalives(uint16_t idle_time, uint16_t max_probes, uint16_t probe_interval) +{ + if (!idle_time) { + /* default */ + client_opts.ka.idle_time = 1; + } else { + client_opts.ka.idle_time = idle_time; + } + + if (!max_probes) { + /* default */ + client_opts.ka.max_probes = 10; + } else { + client_opts.ka.max_probes = max_probes; + } + + if (!probe_interval) { + /* default */ + client_opts.ka.probe_interval = 5; + } else { + client_opts.ka.probe_interval = probe_interval; + } +} diff --git a/src/session_client.h b/src/session_client.h index 6003bc9..c6df04d 100644 --- a/src/session_client.h +++ b/src/session_client.h @@ -3,6 +3,7 @@ * @author Michal Vasko * @brief libnetconf2 session client manipulation * + * @copyright * Copyright (c) 2015 - 2021 CESNET, z.s.p.o. * * This source code is licensed under BSD 3-Clause License (the "License"). @@ -21,18 +22,14 @@ extern "C" { #include -#ifdef NC_ENABLED_SSH -# include -#endif - -#ifdef NC_ENABLED_TLS -# include -#endif - #include "messages_client.h" #include "netconf.h" #include "session.h" +#ifdef NC_ENABLED_SSH_TLS +# include +#endif /* NC_ENABLED_SSH_TLS */ + /** * @addtogroup client * @{ @@ -82,6 +79,26 @@ int nc_client_set_schema_callback(ly_module_imp_clb clb, void *user_data); */ ly_module_imp_clb nc_client_get_schema_callback(void **user_data); +/** + * @brief Enable/disable loading of all the YANG modules supported by + * the server when a new session is created. If disabled, it is expected + * that users update the context themselves and load the YANG modules + * that are planned to be used. Otherwise even basic RPCs may fail to be sent! + * + * @param[in] enabled Whether context autofill is enabled or disabled. + */ +void nc_client_set_new_session_context_autofill(int enabled); + +/** + * @brief Set client session context to support schema-mount, if possible. + * + * Performed automatically unless ::nc_client_set_new_session_context_autofill() was disabled. + * + * @param[in] session NETCONF session to update. + * @return 0 on success, -1 on error. + */ +int nc_client_set_new_session_context_schema_mount(struct nc_session *session); + /** * @brief Use the provided thread-specific client's context in the current thread. * @@ -105,13 +122,15 @@ 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 - * the client options, for both SSH and TLS, and for Call Home too. + * the client options, for both SSH and TLS, and for Call Home too. */ void nc_client_destroy(void); @@ -134,13 +153,9 @@ void nc_client_destroy(void); * * @param[in] fdin Input file descriptor for reading (clear) data from NETCONF server. * @param[in] fdout Output file descriptor for writing (clear) data for NETCONF server. - * @param[in] ctx Optional parameter. If set, provides strict YANG context for the session - * (ignoring what is actually supported by the server side). If not set, - * YANG context is created for the session using \ (if supported - * by the server side) or/and by searching for YANG schemas in the searchpath - * (see nc_client_schema_searchpath()). In every case except not providing context - * to connect to a server supporting \ it is possible that - * the session context will not include all the models supported by the server. + * @param[in,out] ctx Optional custom context to use for the session. If not set, a default context is created. + * Any YANG modules not present in the context and supported by the server are loaded using \ + * (if supported) and/or by searching the searchpath (see ::nc_client_set_schema_searchpath()). * @return Created NETCONF session object or NULL in case of error. */ struct nc_session *nc_connect_inout(int fdin, int fdout, struct ly_ctx *ctx); @@ -153,20 +168,16 @@ struct nc_session *nc_connect_inout(int fdin, int fdout, struct ly_ctx *ctx); * by sending and processing NETCONF \ messages. * * @param[in] address Path to the unix socket. - * @param[in] ctx Optional parameter. If set, provides strict YANG context for the session - * (ignoring what is actually supported by the server side). If not set, - * YANG context is created for the session using \ (if supported - * by the server side) or/and by searching for YANG schemas in the searchpath - * (see nc_client_schema_searchpath()). In every case except not providing context - * to connect to a server supporting \ it is possible that - * the session context will not include all the models supported by the server. + * @param[in,out] ctx Optional custom context to use for the session. If not set, a default context is created. + * Any YANG modules not present in the context and supported by the server are loaded using \ + * (if supported) and/or by searching the searchpath (see ::nc_client_set_schema_searchpath()). * @return Created NETCONF session object or NULL in case of error. */ 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 @@ -177,28 +188,25 @@ struct nc_session *nc_connect_unix(const char *address, struct ly_ctx *ctx); */ /** - * @brief Set SSH authentication hostkey check (knownhosts) callback. + * @brief Set the behaviour of checking the host key and adding/reading entries to/from the known_hosts file. * - * 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()). + * The default mode is ::NC_SSH_KNOWNHOSTS_ASK. * - * @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. @@ -354,15 +362,11 @@ const char *nc_client_ssh_get_username(void); * they are supposed to use nc_connect_libssh(). * * @param[in] host Hostname or address (both Ipv4 and IPv6 are accepted) of the target server. - * 'localhost' is used by default if NULL is specified. + * 'localhost' is used by default if NULL is specified. * @param[in] port Port number of the target server. Default value 830 is used if 0 is specified. - * @param[in] ctx Optional parameter. If set, provides strict YANG context for the session - * (ignoring what is actually supported by the server side). If not set, - * YANG context is created for the session using \ (if supported - * by the server side) or/and by searching for YANG schemas in the searchpath - * (see nc_client_schema_searchpath()). In every case except not providing context - * to connect to a server supporting \ it is possible that - * the session context will not include all the models supported by the server. + * @param[in,out] ctx Optional custom context to use for the session. If not set, a default context is created. + * Any YANG modules not present in the context and supported by the server are loaded using \ + * (if supported) and/or by searching the searchpath (see ::nc_client_set_schema_searchpath()). * @return Created NETCONF session object or NULL on error. */ struct nc_session *nc_connect_ssh(const char *host, uint16_t port, struct ly_ctx *ctx); @@ -375,15 +379,11 @@ struct nc_session *nc_connect_ssh(const char *host, uint16_t port, struct ly_ctx * set and connected only the host and the username must be set/is detected. Or the @p ssh_session * can already be authenticated in which case it is used directly. * - * @param[in] ssh_session libssh structure representing SSH session object. After passing it - * to libnetconf2 this way, it is fully managed by it (including freeing!). - * @param[in] ctx Optional parameter. If set, provides strict YANG context for the session - * (ignoring what is actually supported by the server side). If not set, - * YANG context is created for the session using \ (if supported - * by the server side) or/and by searching for YANG schemas in the searchpath - * (see nc_client_schema_searchpath()). In every case except not providing context - * to connect to a server supporting \ it is possible that - * the session context will not include all the models supported by the server. + * @param[in] ssh_session libssh structure representing SSH session object. It is fully managed by the created session + * including freeing it. + * @param[in,out] ctx Optional custom context to use for the session. If not set, a default context is created. + * Any YANG modules not present in the context and supported by the server are loaded using \ + * (if supported) and/or by searching the searchpath (see ::nc_client_set_schema_searchpath()). * @return Created NETCONF session object or NULL on error. */ struct nc_session *nc_connect_libssh(ssh_session ssh_session, struct ly_ctx *ctx); @@ -392,24 +392,16 @@ struct nc_session *nc_connect_libssh(ssh_session ssh_session, struct ly_ctx *ctx * @brief Create another NETCONF session on existing SSH session using separated SSH channel. * * @param[in] session Existing NETCONF session. The session has to be created on SSH transport layer using libssh - - * it has to be created by nc_connect_ssh(), nc_connect_libssh() or nc_connect_ssh_channel(). - * @param[in] ctx Optional parameter. If set, provides strict YANG context for the session - * (ignoring what is actually supported by the server side). If not set, - * YANG context is created for the session using \ (if supported - * by the server side) or/and by searching for YANG schemas in the searchpath - * (see nc_client_schema_searchpath()). In every case except not providing context - * to connect to a server supporting \ it is possible that - * the session context will not include all the models supported by the server. + * it has to be created by nc_connect_ssh(), nc_connect_libssh() or nc_connect_ssh_channel(). + * @param[in,out] ctx Optional custom context to use for the session. If not set, a default context is created. + * Any YANG modules not present in the context and supported by the server are loaded using \ + * (if supported) and/or by searching the searchpath (see ::nc_client_set_schema_searchpath()). * @return Created NETCONF session object or NULL on error. */ struct nc_session *nc_connect_ssh_channel(struct nc_session *session, struct ly_ctx *ctx); /** @} Client SSH */ -#endif /* NC_ENABLED_SSH */ - -#ifdef NC_ENABLED_TLS - /** * @defgroup client_tls Client TLS * @ingroup client @@ -458,19 +450,12 @@ int nc_client_tls_set_trusted_ca_paths(const char *ca_file, const char *ca_dir); void nc_client_tls_get_trusted_ca_paths(const char **ca_file, const char **ca_dir); /** - * @brief Set client Certificate Revocation List paths. - * - * @param[in] crl_file Location of the CRL certificate file used to check for revocated certificates. - * @param[in] crl_dir Location of the CRL certificate directory used to check for revocated certificates. - * @return 0 on success, -1 on error. + * @brief Deprecated. */ int nc_client_tls_set_crl_paths(const char *crl_file, const char *crl_dir); /** - * @brief Get client Certificate Revocation List paths. - * - * @param[out] crl_file Location of the CRL certificate file used to check for revocated certificates. - * @param[out] crl_dir Location of the CRL certificate directory used to check for revocated certificates. + * @brief Deprecated. */ void nc_client_tls_get_crl_paths(const char **crl_file, const char **crl_dir); @@ -478,42 +463,26 @@ void nc_client_tls_get_crl_paths(const char **crl_file, const char **crl_dir); * @brief Connect to the NETCONF server using TLS transport (via libssl) * * TLS session is created with the certificates set using nc_client_tls_* functions, which must be called beforehand! - * If the caller needs to use specific TLS session properties, they are supposed to use nc_connect_libssl(). + * If the caller needs to use specific TLS session properties, they are supposed to use ::nc_connect_libssl(). * * @param[in] host Hostname or address (both Ipv4 and IPv6 are accepted) of the target server. - * 'localhost' is used by default if NULL is specified. + * 'localhost' is used by default if NULL is specified. It is verified by TLS when connecting to it. * @param[in] port Port number of the target server. Default value 6513 is used if 0 is specified. - * @param[in] ctx Optional parameter. If set, provides strict YANG context for the session - * (ignoring what is actually supported by the server side). If not set, - * YANG context is created for the session using \ (if supported - * by the server side) or/and by searching for YANG schemas in the searchpath - * (see nc_client_schema_searchpath()). In every case except not providing context - * to connect to a server supporting \ it is possible that - * the session context will not include all the models supported by the server. + * @param[in,out] ctx Optional custom context to use for the session. If not set, a default context is created. + * Any YANG modules not present in the context and supported by the server are loaded using \ + * (if supported) and/or by searching the searchpath (see ::nc_client_set_schema_searchpath()). * @return Created NETCONF session object or NULL on error. */ struct nc_session *nc_connect_tls(const char *host, uint16_t port, struct ly_ctx *ctx); /** - * @brief Connect to the NETCONF server using the provided TLS (libssl) session. - * - * The TLS session supplied is expected to be fully connected and authenticated! - * - * @param[in] tls libssl structure representing the TLS session object. - * @param[in] ctx Optional parameter. If set, provides strict YANG context for the session - * (ignoring what is actually supported by the server side). If not set, - * YANG context is created for the session using \ (if supported - * by the server side) or/and by searching for YANG schemas in the searchpath - * (see nc_client_schema_searchpath()). In every case except not providing context - * to connect to a server supporting \ it is possible that - * the session context will not include all the models supported by the server. - * @return Created NETCONF session object or NULL on error. + * @brief Deprecated. Should not be needed. */ -struct nc_session *nc_connect_libssl(SSL *tls, struct ly_ctx *ctx); +struct nc_session *nc_connect_libssl(void *tls, struct ly_ctx *ctx); /** @} Client TLS */ -#endif /* NC_ENABLED_TLS */ +#endif /* NC_ENABLED_SSH_TLS */ /** * @addtogroup client_session @@ -572,9 +541,9 @@ NC_MSG_TYPE nc_recv_reply(struct nc_session *session, struct nc_rpc *rpc, uint64 * @brief Receive NETCONF Notification. * * @param[in] session NETCONF session from which the function gets data. It must be the - * client side session object. + * client side session object. * @param[in] timeout Timeout for reading in milliseconds. Use negative value for infinite - * waiting and 0 for immediate return if data are not available on the wire. + * waiting and 0 for immediate return if data are not available on the wire. * @param[out] envp NETCONF notification XML envelopes. * @param[out] op Parsed NETCONF notification data. * @return #NC_MSG_NOTIF for success, @@ -584,18 +553,44 @@ NC_MSG_TYPE nc_recv_reply(struct nc_session *session, struct nc_rpc *rpc, uint64 */ NC_MSG_TYPE nc_recv_notif(struct nc_session *session, int timeout, struct lyd_node **envp, struct lyd_node **op); +/** + * @brief Callback for receiving notifications in a separate thread. + * + * @param[in] session NC session that received the notification. + * @param[in] envp Notification envelope data tree. + * @param[in] op Notification body data tree. + * @param[in] user_data Arbitrary user data passed to the callback. + */ +typedef void (*nc_notif_dispatch_clb)(struct nc_session *session, const struct lyd_node *envp, const struct lyd_node *op, + void *user_data); + /** * @brief Receive NETCONF Notifications in a separate thread until the session is terminated - * or \ is received. + * or \ is received. * * @param[in] session Netconf session to read notifications from. * @param[in] notif_clb Function that is called for every received notification (including - * \). Parameters are the session the notification was received on - * and the notification data. + * \). Parameters are the session the notification was received on + * and the notification data. * @return 0 if the thread was successfully created, -1 on error. */ -int nc_recv_notif_dispatch(struct nc_session *session, - void (*notif_clb)(struct nc_session *session, const struct lyd_node *envp, const struct lyd_node *op)); +int nc_recv_notif_dispatch(struct nc_session *session, nc_notif_dispatch_clb notif_clb); + +/** + * @brief Receive NETCONF Notifications in a separate thread until the session is terminated + * or \ is received. Similar to ::nc_recv_notif_dispatch() but allows + * to set arbitrary user data that can be freed as well. + * + * @param[in] session Netconf session to read notifications from. + * @param[in] notif_clb Callback that is called for every received notification (including + * \). Parameters are the session the notification was received on + * and the notification data. + * @param[in] user_data Arbitrary user data. + * @param[in] free_data Callback for freeing the user data after notif thread exit. + * @return 0 if the thread was successfully created, -1 on error. + */ +int nc_recv_notif_dispatch_data(struct nc_session *session, nc_notif_dispatch_clb notif_clb, void *user_data, + void (*free_data)(void *)); /** * @brief Send NETCONF RPC message via the session. @@ -603,7 +598,7 @@ int nc_recv_notif_dispatch(struct nc_session *session, * @param[in] session NETCONF session where the RPC will be written. * @param[in] rpc NETCONF RPC object to send via the specified session. * @param[in] timeout Timeout for writing in milliseconds. Use negative value for infinite - * waiting and 0 for return if data cannot be sent immediately. + * waiting and 0 for return if data cannot be sent immediately. * @param[out] msgid If RPC was successfully sent, this is it's message ID. * @return #NC_MSG_RPC on success, * #NC_MSG_WOULDBLOCK in case of a busy session, and @@ -613,7 +608,7 @@ NC_MSG_TYPE nc_send_rpc(struct nc_session *session, struct nc_rpc *rpc, int time /** * @brief Make a session not strict when sending RPCs and receiving RPC replies. In other words, - * it will silently skip unknown nodes without an error. + * it will silently skip unknown nodes without an error. * * Generally, no such data should be worked with, so use this function only when you know what you * are doing and you understand the consequences. @@ -622,6 +617,58 @@ NC_MSG_TYPE nc_send_rpc(struct nc_session *session, struct nc_rpc *rpc, int time */ void nc_client_session_set_not_strict(struct nc_session *session); +/** + * @brief Callback for monitoring client sessions. + * + * This callback is called whenever the client finds out that a session was disconnected by the server. + * + * @param[in] session Disconnected session. The session will not be freed, so it is safe to call nc_session_free() on it. + * @param[in] user_data Arbitrary user data passed to the callback. + */ +typedef void (*nc_client_monitoring_clb)(struct nc_session *session, void *user_data); + +/** + * @brief Start a thread that monitors client sessions. + * + * Once the thread is running, new sessions will be monitored automatically. + * + * Note that once you start the monitoring thread, any other client thread that + * calls ::nc_session_free() needs to share the same thread context (or be the same thread) + * as the thread that called this function (see ::nc_client_set_thread_context()). + * + * @param[in] monitoring_clb Callback called whenever a session is terminated. + * @param[in] user_data Arbitrary user data passed to the callback. + * @param[in] free_data Callback for freeing the user data after monitoring thread exits. + * @return 0 on success, 1 on error. + */ +int nc_client_monitoring_thread_start(nc_client_monitoring_clb monitoring_clb, void *user_data, void (*free_data)(void *)); + +/** + * @brief Stop the client session monitoring thread. + */ +void nc_client_monitoring_thread_stop(void); + +/** + * @brief Enable or disable TCP keepalives. Only affects new sessions. + * + * Client-side TCP keepalives have the following default values: + * - idle time: 1 second + * - max probes: 10 + * - probe interval: 5 seconds + * + * @param[in] enable Whether to enable or disable TCP keepalives. + */ +void nc_client_enable_tcp_keepalives(int enable); + +/** + * @brief Set TCP keepalive options. + * + * @param[in] idle_time Time in seconds before the first keepalive probe is sent. If 0, the default value 1 is used. + * @param[in] max_probes Maximum number of keepalive probes to send before considering the connection dead. If 0, the default value 10 is used. + * @param[in] probe_interval Time in seconds between individual keepalive probes. If 0, the default value 5 is used. + */ +void nc_client_set_tcp_keepalives(uint16_t idle_time, uint16_t max_probes, uint16_t probe_interval); + /** @} Client Session */ #ifdef __cplusplus diff --git a/src/session_client_ch.h b/src/session_client_ch.h index d8db141..c4d28c0 100644 --- a/src/session_client_ch.h +++ b/src/session_client_ch.h @@ -3,7 +3,8 @@ * @author Michal Vasko * @brief libnetconf2 Call Home session client manipulation * - * Copyright (c) 2015 - 2021 CESNET, z.s.p.o. + * @copyright + * 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. @@ -21,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 @@ -43,8 +44,10 @@ extern "C" { * @brief Accept a Call Home connection on any of the listening binds. * * @param[in] timeout Timeout for receiving a new connection in milliseconds, 0 for - * non-blocking call, -1 for infinite waiting. - * @param[in] ctx Session context to use. Can be NULL. + * non-blocking call, -1 for infinite waiting. + * @param[in,out] ctx Optional custom context to use for the session. If not set, a default context is created. + * Any YANG modules not present in the context and supported by the server are loaded using \ + * (if supported) and/or by searching the searchpath (see ::nc_client_set_schema_searchpath()). * @param[out] session New session. * @return 1 on success, 0 on timeout, -1 on error. */ @@ -52,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 @@ -65,28 +64,26 @@ int nc_accept_callhome(int timeout, struct ly_ctx *ctx, struct nc_session **sess */ /** - * @brief Set SSH Call Home authentication hostkey check (knownhosts) callback. + * @brief Set SSH Call Home behaviour of checking the host key and adding/reading entries to/from the known_hosts file. * - * 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()). + * The default mode is ::NC_SSH_KNOWNHOSTS_ASK. * - * @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_ch_set_auth_hostkey_check_clb(int (*auth_hostkey_check)(const char *hostname, ssh_session session, void *priv), - void *priv); +void nc_client_ssh_ch_set_knownhosts_mode(NC_SSH_KNOWNHOSTS_MODE mode); /** - * @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(). + * @brief Set SSH Call Home path to the known_hosts file for connections. * - * @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_ch_get_auth_hostkey_check_clb(int (**auth_hostkey_check)(const char *hostname, ssh_session session, void *priv), - void **priv); +int nc_client_ssh_ch_set_knownhosts_path(const char *path); + /** * @brief Set SSH Call Home password authentication callback. * @@ -95,7 +92,7 @@ void nc_client_ssh_ch_get_auth_hostkey_check_clb(int (**auth_hostkey_check)(cons * nc_client_ssh_ch_get_auth_password_clb()). * * @param[in] auth_password Function to call, returns the password for username\@hostname. - * If NULL, the default callback is set. + * 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_password_clb(char *(*auth_password)(const char *username, const char *hostname, void *priv), @@ -119,8 +116,7 @@ void nc_client_ssh_ch_get_auth_password_clb(char *(**auth_password)(const char * * nc_client_ssh_ch_get_auth_interactive_clb()). * * @param[in] auth_interactive Function to call for every question, returns the answer for - * authentication name with instruction and echoing prompt. - * If NULL, the default callback is set. + * authentication name with instruction and echoing prompt. 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_interactive_clb(char *(*auth_interactive)(const char *auth_name, const char *instruction, @@ -145,8 +141,8 @@ void nc_client_ssh_ch_get_auth_interactive_clb(char *(**auth_interactive)(const * freeing the private data when necessary (the private data can be obtained by * nc_client_ssh_ch_get_auth_privkey_passphrase_clb()). * - * @param[in] auth_privkey_passphrase Function to call for every question, returns - * the passphrase for the specific private key. + * @param[in] auth_privkey_passphrase Function to call for every question, returns the passphrase for the specific + * private key. * @param[in] priv Optional private data to be passed to the callback function. */ void nc_client_ssh_ch_set_auth_privkey_passphrase_clb(char *(*auth_privkey_passphrase)(const char *privkey_path, void *priv), @@ -254,10 +250,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 @@ -275,6 +267,16 @@ const char *nc_client_ssh_ch_get_username(void); */ int nc_client_tls_ch_add_bind_listen(const char *address, uint16_t port); +/** + * @brief Add a new client bind and start listening on it for TLS Call Home connections coming from the specified hostname. + * + * @param[in] address IP address to bind to. + * @param[in] port Port to bind to. + * @param[in] hostname Expected server hostname, verified by TLS when connecting to it. If NULL, the check is skipped. + * @return 0 on success, -1 on error. + */ +int nc_client_tls_ch_add_bind_hostname_listen(const char *address, uint16_t port, const char *hostname); + /** * @brief Remove a TLS listening client bind. * @@ -289,7 +291,7 @@ int nc_client_tls_ch_del_bind(const char *address, uint16_t port); * * @param[in] client_cert Path to the file containing the client certificate. * @param[in] client_key Path to the file containing the private key for the @p client_cert. - * If NULL, key is expected to be stored with @p client_cert. + * If NULL, key is expected to be stored with @p client_cert. * @return 0 on success, -1 on error. */ int nc_client_tls_ch_set_cert_key_paths(const char *client_cert, const char *client_key); @@ -298,8 +300,7 @@ int nc_client_tls_ch_set_cert_key_paths(const char *client_cert, const char *cli * @brief Get client Call Home authentication identity - a certificate and a private key. * * @param[out] client_cert Path to the file containing the client certificate. Can be NULL. - * @param[out] client_key Path to the file containing the private key for the @p client_cert. - * Can be NULL. + * @param[out] client_key Path to the file containing the private key for the @p client_cert. Can be NULL. */ void nc_client_tls_ch_get_cert_key_paths(const char **client_cert, const char **client_key); @@ -307,9 +308,9 @@ void nc_client_tls_ch_get_cert_key_paths(const char **client_cert, const char ** * @brief Set client Call Home trusted CA certificates. * * @param[in] ca_file Location of the CA certificate file used to verify server certificates. - * For more info, see the documentation for SSL_CTX_load_verify_locations() from OpenSSL. + * For more info, see the documentation for SSL_CTX_load_verify_locations() from OpenSSL. * @param[in] ca_dir Location of the CA certificates directory used to verify the server certificates. - * For more info, see the documentation for SSL_CTX_load_verify_locations() from OpenSSL. + * For more info, see the documentation for SSL_CTX_load_verify_locations() from OpenSSL. * @return 0 on success, -1 on error. */ int nc_client_tls_ch_set_trusted_ca_paths(const char *ca_file, const char *ca_dir); @@ -317,35 +318,24 @@ int nc_client_tls_ch_set_trusted_ca_paths(const char *ca_file, const char *ca_di /** * @brief Get client Call Home trusted CA certificates. * - * @param[out] ca_file Location of the CA certificate file used to verify server certificates. - * Can be NULL. - * @param[out] ca_dir Location of the CA certificates directory used to verify the server certificates. - * Can be NULL. + * @param[out] ca_file Location of the CA certificate file used to verify server certificates. Can be NULL. + * @param[out] ca_dir Location of the CA certificates directory used to verify the server certificates. Can be NULL. */ void nc_client_tls_ch_get_trusted_ca_paths(const char **ca_file, const char **ca_dir); /** - * @brief Set client Call Home Certificate Revocation Lists. - * - * @param[in] crl_file Location of the CRL certificate file used to check for revocated certificates. - * @param[in] crl_dir Location of the CRL certificate directory used to check for revocated certificates. - * @return 0 on success, -1 on error. + * @brief Deprecated. */ int nc_client_tls_ch_set_crl_paths(const char *crl_file, const char *crl_dir); /** - * @brief Get client Call Home Certificate Revocation Lists. - * - * @param[out] crl_file Location of the CRL certificate file used to check for revocated certificates. - * Can be NULL. - * @param[out] crl_dir Location of the CRL certificate directory used to check for revocated certificates. - * Can be NULL. + * @brief Deprecated. */ 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 bd25b68..bd11f56 100644 --- a/src/session_client_ssh.c +++ b/src/session_client_ssh.c @@ -1,11 +1,12 @@ /** - * \file session_client_ssh.c - * \author Radek Krejci - * \author Michal Vasko - * \brief libnetconf2 - SSH specific client session transport functions + * @file session_client_ssh.c + * @author Radek Krejci + * @author Michal Vasko + * @brief libnetconf2 - SSH specific client session transport functions * * This source is compiled only with libssh. * + * @copyright * Copyright (c) 2015 - 2021 CESNET, z.s.p.o. * * This source code is licensed under BSD 3-Clause License (the "License"). @@ -20,6 +21,7 @@ #include #include #include +#include #include #include #include @@ -28,35 +30,50 @@ #include #include #include -#include #include #include #ifdef ENABLE_DNSSEC -# include -# include -# include +# include +# include +# include -# include +# include #endif #include #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" + +/* must be after config.h */ +#ifdef HAVE_TERMIOS +# include +#endif struct nc_client_context *nc_client_context_location(void); -int nc_session_new_ctx(struct nc_session *session, struct ly_ctx *ctx); #define client_opts nc_client_context_location()->opts #define ssh_opts nc_client_context_location()->ssh_opts #define ssh_ch_opts nc_client_context_location()->ssh_ch_opts +#ifdef HAVE_TERMIOS + +/** + * @brief Open a terminal FILE with no echo. + * + * @param[in] path Filesystem terminal path. + * @param[in] oldterm Old terminal options. + * @return Opened terminal; + * @return NULL on error. + */ static FILE * -open_tty_noecho(const char *path, struct termios *oldterm) +nc_open_tty_noecho(const char *path, struct termios *oldterm) { struct termios newterm; FILE *ret; @@ -73,9 +90,13 @@ open_tty_noecho(const char *path, struct termios *oldterm) } newterm = *oldterm; + + /* turn off echo */ newterm.c_lflag &= ~ECHO; - newterm.c_lflag &= ~ICANON; + + /* get rid of any leftover characters */ tcflush(fileno(ret), TCIFLUSH); + if (tcsetattr(fileno(ret), TCSANOW, &newterm)) { ERR(NULL, "Unable to change terminal \"%s\" settings for hiding password (%s).", path, strerror(errno)); fclose(ret); @@ -85,6 +106,14 @@ open_tty_noecho(const char *path, struct termios *oldterm) return ret; } +/** + * @brief Open an input terminal FILE. + * + * @param[in] echo Whether to turn echo on or off. + * @param[in] oldterm Old terminal options. + * @return Opened terminal; + * @return NULL on error. + */ static FILE * nc_open_in(int echo, struct termios *oldterm) { @@ -93,7 +122,7 @@ nc_open_in(int echo, struct termios *oldterm) FILE *in; if (!echo) { - in = open_tty_noecho("/dev/tty", oldterm); + in = nc_open_tty_noecho("/dev/tty", oldterm); } else { in = fopen("/dev/tty", "r"); if (!in) { @@ -108,7 +137,7 @@ nc_open_in(int echo, struct termios *oldterm) } if (!echo) { - in = open_tty_noecho(buf, oldterm); + in = nc_open_tty_noecho(buf, oldterm); } else { in = fopen(buf, "r"); if (!in) { @@ -120,6 +149,12 @@ nc_open_in(int echo, struct termios *oldterm) return in; } +/** + * @brief Open an output terminal FILE. + * + * @return Opened terminal; + * @return NULL on error. + */ static FILE * nc_open_out(void) { @@ -145,6 +180,15 @@ nc_open_out(void) return out; } +/** + * @brief Close an input/output terminal FILE. + * + * @param[in] inout Terminal FILE to close. + * @param[in] echo Whether echo was turned on or off. + * @param[in] oldterm Old terminal options. + * @return Opened terminal; + * @return NULL on error. + */ static void nc_close_inout(FILE *inout, int echo, struct termios *oldterm) { @@ -156,6 +200,8 @@ nc_close_inout(FILE *inout, int echo, struct termios *oldterm) } } +#endif + void _nc_client_ssh_destroy_opts(struct nc_client_ssh_opts *opts) { @@ -167,8 +213,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,116 +323,210 @@ finish: #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; + } - 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 */ + 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; +} -#if (LIBSSH_VERSION_INT >= SSH_VERSION_INT(0, 9, 0)) - case SSH_KNOWN_HOSTS_CHANGED: -#else - case SSH_SERVER_KNOWN_CHANGED: #endif - ERR(NULL, "Remote host key changed, the connection will be terminated!"); + +/** + * @brief Convert knownhosts mode to string. + * + * @param[in] knownhosts_mode Knownhosts mode. + * @return Knownhosts mode string. + */ +static const char * +nc_client_ssh_knownhosts_mode2str(NC_SSH_KNOWNHOSTS_MODE knownhosts_mode) +{ + const char *mode_str; + + switch (knownhosts_mode) { + case NC_SSH_KNOWNHOSTS_ASK: + mode_str = "ask"; + break; + case NC_SSH_KNOWNHOSTS_STRICT: + mode_str = "strict"; + break; + case NC_SSH_KNOWNHOSTS_ACCEPT_NEW: + mode_str = "accept-new"; + break; + case NC_SSH_KNOWNHOSTS_ACCEPT: + mode_str = "accept"; + break; + case NC_SSH_KNOWNHOSTS_SKIP: + mode_str = "skip"; + break; + default: + mode_str = "unknown"; + break; + } + + return mode_str; +} + +/** + * @brief Perform the hostkey check. + * + * @param[in] hostname Expected hostname. + * @param[in] port Expected port. + * @param[in] knownhosts_mode Knownhosts mode. + * @param[in] session libssh session. + * @return 0 on success, -1 on error. + */ +static int +nc_client_ssh_auth_hostkey_check(const char *hostname, uint16_t port, + NC_SSH_KNOWNHOSTS_MODE knownhosts_mode, ssh_session session) +{ + char *hexa = NULL; + unsigned char *hash_sha1 = NULL; + enum ssh_keytypes_e srv_pubkey_type; + int state; + +#ifdef HAVE_TERMIOS + int c; + char answer[5]; + FILE *out = NULL, *in = NULL; +#endif + +#ifdef ENABLE_DNSSEC + int dnssec_ret; +#endif + + VRB(NULL, "Server hostkey check mode: %s.", nc_client_ssh_knownhosts_mode2str(knownhosts_mode)); + + 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 + 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; + } + +#ifdef HAVE_TERMIOS + /* open the files for reading/writing */ if (!(in = nc_open_in(1, NULL))) { goto error; } + if (!(out = nc_open_out())) { goto error; } @@ -398,19 +541,19 @@ hostkey_not_known: goto error; } -#ifdef ENABLE_DNSSEC - if (ret == 2) { +# ifdef ENABLE_DNSSEC + 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; } } -#endif +# endif if (fprintf(out, "Are you sure you want to continue connecting (yes/no)? ") < 1) { ERR(NULL, "Writing into output failed (%s).", feof(out) ? "EOF" : strerror(errno)); @@ -427,15 +570,8 @@ hostkey_not_known: 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 { @@ -446,27 +582,30 @@ hostkey_not_known: fflush(out); } } while (strcmp(answer, "yes") && strcmp(answer, "no")); +#else + ERR(NULL, "Unable to get input from user, terminate the connection."); + goto error; +#endif 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; } +#ifdef HAVE_TERMIOS nc_close_inout(in, 1, NULL); nc_close_inout(out, 1, NULL); +#endif ssh_clean_pubkey_hash(&hash_sha1); ssh_string_free_char(hexa); return 0; error: +#ifdef HAVE_TERMIOS nc_close_inout(in, 1, NULL); nc_close_inout(out, 1, NULL); +#endif ssh_clean_pubkey_hash(&hash_sha1); ssh_string_free_char(hexa); return -1; @@ -475,16 +614,14 @@ error: char * sshauth_password(const char *username, const char *hostname, void *UNUSED(priv)) { +#ifdef HAVE_TERMIOS char *buf = NULL; int c, buflen = 1024, len; struct termios oldterm; 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 +641,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; } @@ -524,22 +658,27 @@ error: nc_close_inout(out, 1, NULL); free(buf); return NULL; +#else + (void)username; + (void)hostname; + + ERR(NULL, "Unable to get input from user, authentication failed."); + return NULL; +#endif } char * sshauth_interactive(const char *auth_name, const char *instruction, const char *prompt, int echo, void *UNUSED(priv)) { - unsigned int buflen = 64, cur_len; +#ifdef HAVE_TERMIOS + uint32_t buflen = 64, cur_len; int c; struct termios oldterm; char *buf = NULL; 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 +706,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; } @@ -588,21 +724,28 @@ error: nc_close_inout(out, 1, NULL); free(buf); return NULL; +#else + (void)auth_name; + (void)instruction; + (void)prompt; + (void)echo; + + ERR(NULL, "Unable to get input from user, authentication failed."); + return NULL; +#endif } char * sshauth_privkey_passphrase(const char *privkey_path, void *UNUSED(priv)) { +#ifdef HAVE_TERMIOS char *buf = NULL; int c, buflen = 1024, len; struct termios oldterm; 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 +765,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; } @@ -642,59 +782,52 @@ error: nc_close_inout(out, 1, NULL); free(buf); return NULL; +#else + (void)privkey_path; + + ERR(NULL, "Unable to get input from user, encrypted private key unusable."); + return NULL; +#endif } -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) +static int +_nc_client_ssh_set_knownhosts_path(const char *path, struct nc_client_ssh_opts *opts) { - 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(opts->knownhosts_path); + + if (!path) { + opts->knownhosts_path = NULL; + return 0; } + + opts->knownhosts_path = strdup(path); + NC_CHECK_ERRMEM_RET(!opts->knownhosts_path, 1); + + return 0; } -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) +API int +nc_client_ssh_set_knownhosts_path(const char *path) { - 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; - } + return _nc_client_ssh_set_knownhosts_path(path, &ssh_opts); +} + +API int +nc_client_ssh_ch_set_knownhosts_path(const char *path) +{ + return _nc_client_ssh_set_knownhosts_path(path, &ssh_ch_opts); } 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_knownhosts_mode(NC_SSH_KNOWNHOSTS_MODE mode) { - _nc_client_ssh_set_auth_hostkey_check_clb(auth_hostkey_check, priv, &ssh_opts); + ssh_opts.knownhosts_mode = mode; } 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_ch_set_knownhosts_mode(NC_SSH_KNOWNHOSTS_MODE mode) { - _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); -} - -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_get_auth_hostkey_check_clb(auth_hostkey_check, priv, &ssh_ch_opts); + ssh_ch_opts.knownhosts_mode = mode; } static void @@ -869,13 +1002,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)) { @@ -895,20 +1022,25 @@ _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; - } - 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; + opts->keys = nc_realloc(opts->keys, (opts->key_count + 1) * sizeof *opts->keys); + NC_CHECK_ERRMEM_RET(!opts->keys, -1); - if (!opts->keys[opts->key_count - 1].pubkey_path || !opts->keys[opts->key_count - 1].privkey_path) { - ERRMEM; + opts->keys[opts->key_count].pubkey_path = realpath(pub_key, NULL); + if (!opts->keys[opts->key_count].pubkey_path) { + ERR(NULL, "Invalid public key path \"%s\" (%s).", pub_key, strerror(errno)); return -1; } + opts->keys[opts->key_count].privkey_path = realpath(priv_key, NULL); + if (!opts->keys[opts->key_count].privkey_path) { + ERR(NULL, "Invalid private key path \"%s\" (%s).", priv_key, strerror(errno)); + free(opts->keys[opts->key_count].pubkey_path); + return -1; + } + opts->keys[opts->key_count].privkey_crypt = 0; + ++opts->key_count; + + /* use normalized path */ + priv_key = opts->keys[opts->key_count - 1].privkey_path; /* check encryption */ if ((key = fopen(priv_key, "r"))) { @@ -949,7 +1081,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 +1094,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 +1137,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 +1230,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; } @@ -1145,13 +1271,13 @@ nc_client_ssh_ch_get_username(void) API int nc_client_ssh_ch_add_bind_listen(const char *address, uint16_t port) { - return nc_client_ch_add_bind_listen(address, port, NC_TI_LIBSSH); + return nc_client_ch_add_bind_listen(address, port, NULL, NC_TI_SSH); } API int nc_client_ssh_ch_del_bind(const char *address, uint16_t port) { - return nc_client_ch_del_bind(address, port, NC_TI_LIBSSH); + return nc_client_ch_del_bind(address, port, NC_TI_SSH); } /* Establish a secure SSH connection and authenticate. @@ -1172,16 +1298,14 @@ connect_ssh_session(struct nc_session *session, struct nc_client_ssh_opts *opts, char *s, *answer, echo; ssh_key pubkey, privkey; ssh_session ssh_sess; - struct timespec ts_timeout, ts_cur; + struct timespec ts_timeout; ssh_sess = session->ti.libssh.session; - nc_gettimespec_mono(&ts_timeout); - nc_addtimespec(&ts_timeout, NC_TRANSPORT_TIMEOUT); + nc_timeouttime_get(&ts_timeout, NC_TRANSPORT_TIMEOUT); while ((ret = ssh_connect(ssh_sess)) == SSH_AGAIN) { usleep(NC_TIMEOUT_STEP); - nc_gettimespec_mono(&ts_cur); - if (nc_difftimespec(&ts_cur, &ts_timeout) < 1) { + if (nc_timeouttime_cur_diff(&ts_timeout) < 1) { break; } } @@ -1194,22 +1318,18 @@ 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, opts->knownhosts_mode, ssh_sess)) { ERR(session, "Checking the host key failed."); return -1; } if (timeout > -1) { - nc_gettimespec_mono(&ts_timeout); - nc_addtimespec(&ts_timeout, timeout); + nc_timeouttime_get(&ts_timeout, timeout); } while ((ret_auth = ssh_userauth_none(ssh_sess, NULL)) == SSH_AUTH_AGAIN) { usleep(NC_TIMEOUT_STEP); - if (timeout > -1) { - nc_gettimespec_mono(&ts_cur); - if (nc_difftimespec(&ts_cur, &ts_timeout) < 1) { - break; - } + if ((timeout > -1) && (nc_timeouttime_cur_diff(&ts_timeout) < 1)) { + break; } } if (ret_auth == SSH_AUTH_AGAIN) { @@ -1219,7 +1339,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; } @@ -1278,16 +1397,12 @@ connect_ssh_session(struct nc_session *session, struct nc_client_ssh_opts *opts, } if (timeout > -1) { - nc_gettimespec_mono(&ts_timeout); - nc_addtimespec(&ts_timeout, timeout); + nc_timeouttime_get(&ts_timeout, timeout); } while ((ret_auth = ssh_userauth_password(ssh_sess, session->username, s)) == SSH_AUTH_AGAIN) { usleep(NC_TIMEOUT_STEP); - if (timeout > -1) { - nc_gettimespec_mono(&ts_cur); - if (nc_difftimespec(&ts_cur, &ts_timeout) < 1) { - break; - } + if ((timeout > -1) && (nc_timeouttime_cur_diff(&ts_timeout) < 1)) { + break; } } memset(s, 0, strlen(s)); @@ -1300,18 +1415,14 @@ connect_ssh_session(struct nc_session *session, struct nc_client_ssh_opts *opts, VRB(session, "Keyboard-interactive authentication."); if (timeout > -1) { - nc_gettimespec_mono(&ts_timeout); - nc_addtimespec(&ts_timeout, timeout); + nc_timeouttime_get(&ts_timeout, timeout); } while (((ret_auth = ssh_userauth_kbdint(ssh_sess, NULL, NULL)) == SSH_AUTH_INFO) || (ret_auth == SSH_AUTH_AGAIN)) { if (ret_auth == SSH_AUTH_AGAIN) { usleep(NC_TIMEOUT_STEP); - if (timeout > -1) { - nc_gettimespec_mono(&ts_cur); - if (nc_difftimespec(&ts_cur, &ts_timeout) < 1) { - break; - } + if ((timeout > -1) && (nc_timeouttime_cur_diff(&ts_timeout) < 1)) { + break; } continue; } @@ -1323,9 +1434,6 @@ connect_ssh_session(struct nc_session *session, struct nc_client_ssh_opts *opts, break; } - /* libssh BUG - echo is always 1 for some reason, assume always 0 */ - echo = 0; - answer = opts->auth_interactive(ssh_userauth_kbdint_getname(ssh_sess), ssh_userauth_kbdint_getinstruction(ssh_sess), prompt, echo, opts->auth_interactive_priv); @@ -1340,8 +1448,7 @@ connect_ssh_session(struct nc_session *session, struct nc_client_ssh_opts *opts, break; } if (timeout > -1) { - nc_gettimespec_mono(&ts_timeout); - nc_addtimespec(&ts_timeout, timeout); + nc_timeouttime_get(&ts_timeout, timeout); } } break; @@ -1372,16 +1479,12 @@ connect_ssh_session(struct nc_session *session, struct nc_client_ssh_opts *opts, } if (timeout > -1) { - nc_gettimespec_mono(&ts_timeout); - nc_addtimespec(&ts_timeout, timeout); + nc_timeouttime_get(&ts_timeout, timeout); } while ((ret_auth = ssh_userauth_try_publickey(ssh_sess, NULL, pubkey)) == SSH_AUTH_AGAIN) { usleep(NC_TIMEOUT_STEP); - if (timeout > -1) { - nc_gettimespec_mono(&ts_cur); - if (nc_difftimespec(&ts_cur, &ts_timeout) < 1) { - break; - } + if ((timeout > -1) && (nc_timeouttime_cur_diff(&ts_timeout) < 1)) { + break; } } ssh_key_free(pubkey); @@ -1412,16 +1515,12 @@ connect_ssh_session(struct nc_session *session, struct nc_client_ssh_opts *opts, } if (timeout > -1) { - nc_gettimespec_mono(&ts_timeout); - nc_addtimespec(&ts_timeout, timeout); + nc_timeouttime_get(&ts_timeout, timeout); } while ((ret_auth = ssh_userauth_publickey(ssh_sess, NULL, privkey)) == SSH_AUTH_AGAIN) { usleep(NC_TIMEOUT_STEP); - if (timeout > -1) { - nc_gettimespec_mono(&ts_cur); - if (nc_difftimespec(&ts_cur, &ts_timeout) < 1) { - break; - } + if ((timeout > -1) && (nc_timeouttime_cur_diff(&ts_timeout) < 1)) { + break; } } ssh_key_free(privkey); @@ -1468,7 +1567,7 @@ open_netconf_channel(struct nc_session *session, int timeout) { ssh_session ssh_sess; int ret; - struct timespec ts_timeout, ts_cur; + struct timespec ts_timeout; ssh_sess = session->ti.libssh.session; @@ -1484,17 +1583,13 @@ open_netconf_channel(struct nc_session *session, int timeout) /* open a channel */ if (timeout > -1) { - nc_gettimespec_mono(&ts_timeout); - nc_addtimespec(&ts_timeout, timeout); + nc_timeouttime_get(&ts_timeout, timeout); } session->ti.libssh.channel = ssh_channel_new(ssh_sess); while ((ret = ssh_channel_open_session(session->ti.libssh.channel)) == SSH_AGAIN) { usleep(NC_TIMEOUT_STEP); - if (timeout > -1) { - nc_gettimespec_mono(&ts_cur); - if (nc_difftimespec(&ts_cur, &ts_timeout) < 1) { - break; - } + if ((timeout > -1) && (nc_timeouttime_cur_diff(&ts_timeout) < 1)) { + break; } } if (ret == SSH_AGAIN) { @@ -1511,16 +1606,12 @@ open_netconf_channel(struct nc_session *session, int timeout) /* execute the NETCONF subsystem on the channel */ if (timeout > -1) { - nc_gettimespec_mono(&ts_timeout); - nc_addtimespec(&ts_timeout, timeout); + nc_timeouttime_get(&ts_timeout, timeout); } while ((ret = ssh_channel_request_subsystem(session->ti.libssh.channel, "netconf")) == SSH_AGAIN) { usleep(NC_TIMEOUT_STEP); - if (timeout > -1) { - nc_gettimespec_mono(&ts_cur); - if (nc_difftimespec(&ts_cur, &ts_timeout) < 1) { - break; - } + if ((timeout > -1) && (nc_timeouttime_cur_diff(&ts_timeout) < 1)) { + break; } } if (ret == SSH_AGAIN) { @@ -1550,19 +1641,13 @@ _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_type = NC_TI_SSH; session->ti.libssh.session = ssh_session; /* was port set? */ @@ -1576,16 +1661,19 @@ _nc_connect_libssh(ssh_session ssh_session, struct ly_ctx *ctx, struct nc_keepal /* remember host */ host = strdup("localhost"); - if (!host) { - ERRMEM; + NC_CHECK_ERRMEM_GOTO(!host, , fail); + + if (ssh_options_set(session->ti.libssh.session, SSH_OPTIONS_HOST, host) != SSH_OK) { + ERR(NULL, "Failed to use hostname \"%s\".", host); + free(host); goto fail; } - ssh_options_set(session->ti.libssh.session, SSH_OPTIONS_HOST, host); /* create and connect socket */ - sock = nc_sock_connect(host, port, -1, ka, NULL, &ip_host); + sock = nc_sock_connect(NULL, 0, host, port, -1, ka, NULL, &ip_host); if (sock == -1) { ERR(NULL, "Unable to connect to %s:%u (%s).", host, port, strerror(errno)); + free(host); goto fail; } ssh_options_set(session->ti.libssh.session, SSH_OPTIONS_FD, &sock); @@ -1606,7 +1694,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; @@ -1616,10 +1704,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); } @@ -1642,7 +1727,7 @@ _nc_connect_libssh(ssh_session ssh_session, struct ly_ctx *ctx, struct nc_keepal * SSH session is established and netconf channel opened, create a NETCONF session. (Application layer) */ - if (nc_session_new_ctx(session, ctx) != EXIT_SUCCESS) { + if (nc_client_session_new_ctx(session, ctx) != EXIT_SUCCESS) { goto fail; } ctx = session->ctx; @@ -1657,24 +1742,19 @@ _nc_connect_libssh(ssh_session ssh_session, struct ly_ctx *ctx, struct nc_keepal goto fail; } - /* store information into the dictionary */ - if (host) { - lydict_insert_zc(ctx, host, &session->host); - } - if (port) { - session->port = port; - } - if (username) { - lydict_insert_zc(ctx, username, &session->username); + /* start monitoring the session if the monitoring thread is running */ + if (nc_client_monitoring_session_start(session)) { + goto fail; } + /* store information if not previously */ + session->host = host; + session->port = port; + session->username = username; + return session; fail: - free(host); - session->host = NULL; - free(username); - session->username = NULL; nc_session_free(session, NULL); return NULL; } @@ -1692,7 +1772,7 @@ nc_connect_ssh(const char *host, uint16_t port, struct ly_ctx *ctx) size_t buf_len = 0; /* process parameters */ - if (!host || strisempty(host)) { + if (!host || (host[0] == '\0')) { host = "localhost"; } @@ -1702,7 +1782,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; @@ -1711,18 +1791,17 @@ 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); } /* 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 */ - session->ti_type = NC_TI_LIBSSH; + session->ti_type = NC_TI_SSH; session->ti.libssh.session = ssh_new(); if (!session->ti.libssh.session) { ERR(session, "Unable to initialize SSH session."); @@ -1730,13 +1809,19 @@ nc_connect_ssh(const char *host, uint16_t port, struct ly_ctx *ctx) } /* set some basic SSH session options */ - ssh_options_set(session->ti.libssh.session, SSH_OPTIONS_HOST, host); + if (ssh_options_set(session->ti.libssh.session, SSH_OPTIONS_HOST, host) != SSH_OK) { + ERR(session, "Failed to use hostname \"%s\".", host); + goto fail; + } 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 (ssh_opts.knownhosts_path) { + ssh_options_set(session->ti.libssh.session, SSH_OPTIONS_KNOWNHOSTS, ssh_opts.knownhosts_path); + } /* create and assign communication socket */ - sock = nc_sock_connect(host, port, -1, &client_opts.ka, NULL, &ip_host); + sock = nc_sock_connect(NULL, 0, host, port, -1, &client_opts.ka, NULL, &ip_host); if (sock == -1) { ERR(session, "Unable to connect to %s:%u (%s).", host, port, strerror(errno)); goto fail; @@ -1744,15 +1829,16 @@ nc_connect_ssh(const char *host, uint16_t port, struct ly_ctx *ctx) ssh_options_set(session->ti.libssh.session, SSH_OPTIONS_FD, &sock); ssh_set_blocking(session->ti.libssh.session, 0); - /* temporarily, for session connection */ - session->host = host; - session->username = username; + /* 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; } - if (nc_session_new_ctx(session, ctx) != EXIT_SUCCESS) { + if (nc_client_session_new_ctx(session, ctx) != EXIT_SUCCESS) { goto fail; } ctx = session->ctx; @@ -1767,10 +1853,15 @@ nc_connect_ssh(const char *host, uint16_t port, struct ly_ctx *ctx) goto fail; } - /* store information into the dictionary */ - lydict_insert_zc(ctx, ip_host, &session->host); + /* start monitoring the session if the monitoring thread is running */ + if (nc_client_monitoring_session_start(session)) { + goto fail; + } + + /* update information */ + free(session->host); + session->host = ip_host; session->port = port; - lydict_insert(ctx, username, 0, &session->username); free(buf); return session; @@ -1793,21 +1884,15 @@ 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) */ - new_session->ti_type = NC_TI_LIBSSH; + new_session->ti_type = NC_TI_SSH; new_session->ti.libssh.session = session->ti.libssh.session; new_session->io_lock = session->io_lock; @@ -1830,7 +1915,7 @@ nc_connect_ssh_channel(struct nc_session *session, struct ly_ctx *ctx) } nc_session_io_unlock(new_session, __func__); - if (nc_session_new_ctx(new_session, ctx) != EXIT_SUCCESS) { + if (nc_client_session_new_ctx(new_session, ctx) != EXIT_SUCCESS) { goto fail; } ctx = session->ctx; @@ -1845,10 +1930,15 @@ nc_connect_ssh_channel(struct nc_session *session, struct ly_ctx *ctx) goto fail; } - /* store information into session and the dictionary */ - lydict_insert(ctx, session->host, 0, &new_session->host); + /* start monitoring the session if the monitoring thread is running */ + if (nc_client_monitoring_session_start(session)) { + goto fail; + } + + /* store information into session */ + new_session->host = strdup(session->host); new_session->port = session->port; - lydict_insert(ctx, session->username, 0, &new_session->username); + new_session->username = strdup(session->username); return new_session; @@ -1877,12 +1967,17 @@ nc_accept_callhome_ssh_sock(int sock, const char *host, uint16_t port, struct ly ssh_options_set(sess, SSH_OPTIONS_FD, &sock); ssh_set_blocking(sess, 0); - ssh_options_set(sess, SSH_OPTIONS_HOST, host); + if (ssh_options_set(sess, SSH_OPTIONS_HOST, host) != SSH_OK) { + ERR(NULL, "Failed to use hostname \"%s\".", host); + ssh_free(sess); + return NULL; + } uint_port = port; 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); @@ -1893,6 +1988,11 @@ nc_accept_callhome_ssh_sock(int sock, const char *host, uint16_t port, struct ly } else { ssh_options_set(sess, SSH_OPTIONS_USER, ssh_ch_opts.username); } + + if (ssh_ch_opts.knownhosts_path) { + ssh_options_set(sess, SSH_OPTIONS_KNOWNHOSTS, ssh_ch_opts.knownhosts_path); + } + ssh_options_set(sess, SSH_OPTIONS_HOSTKEYS, "ssh-ed25519,ecdsa-sha2-nistp256," "ecdsa-sha2-nistp384,ecdsa-sha2-nistp521,ssh-rsa,rsa-sha2-512,rsa-sha2-256,ssh-dss"); #ifdef HAVE_LIBSSH_OPTIONS_PUBLICKEY_ACCEPTED_TYPES diff --git a/src/session_client_tls.c b/src/session_client_tls.c index 7cb59f7..04486e1 100644 --- a/src/session_client_tls.c +++ b/src/session_client_tls.c @@ -1,11 +1,12 @@ /** - * \file session_client_tls.c - * \author Radek Krejci - * \author Michal Vasko - * \brief libnetconf2 - TLS specific session client transport functions + * @file session_client_tls.c + * @author Radek Krejci + * @author Michal Vasko + * @brief libnetconf2 - TLS specific session client transport functions * * This source is compiled only with libssl. * + * @copyright * Copyright (c) 2015 CESNET, z.s.p.o. * * This source code is licensed under BSD 3-Clause License (the "License"). @@ -15,225 +16,30 @@ * https://opensource.org/licenses/BSD-3-Clause */ +#define _GNU_SOURCE /* pthread_rwlock_t, strdup */ + #include #include +#include #include #include #include -#include -#include -#include -#include "libnetconf.h" +#include "compat.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" +#include "session_wrapper.h" struct nc_client_context *nc_client_context_location(void); -int nc_session_new_ctx(struct nc_session *session, struct ly_ctx *ctx); #define client_opts nc_client_context_location()->opts #define tls_opts nc_client_context_location()->tls_opts #define tls_ch_opts nc_client_context_location()->tls_ch_opts -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) -{ - X509_STORE_CTX *store_ctx; - X509_OBJECT *obj; - X509_NAME *subject, *issuer; - X509 *cert; - X509_CRL *crl; - X509_REVOKED *revoked; - EVP_PKEY *pubkey; - int i, n, rc; - const ASN1_TIME *next_update = NULL; - struct nc_client_tls_opts *opts; - - if (!preverify_ok) { - return 0; - } - - opts = (tlsauth_ch ? &tls_ch_opts : &tls_opts); - - if (!opts->crl_store) { - /* nothing to check */ - return 1; - } - - cert = X509_STORE_CTX_get_current_cert(x509_ctx); - subject = X509_get_subject_name(cert); - issuer = X509_get_issuer_name(cert); - - /* try to retrieve a CRL corresponding to the _subject_ of - * the current certificate in order to verify it's integrity */ - store_ctx = X509_STORE_CTX_new(); - obj = X509_OBJECT_new(); - X509_STORE_CTX_init(store_ctx, opts->crl_store, NULL, NULL); - rc = X509_STORE_CTX_get_by_subject(store_ctx, X509_LU_CRL, subject, obj); - X509_STORE_CTX_free(store_ctx); - crl = X509_OBJECT_get0_X509_CRL(obj); - if ((rc > 0) && crl) { - next_update = X509_CRL_get0_nextUpdate(crl); - - /* verify the signature on this CRL */ - pubkey = X509_get_pubkey(cert); - if (X509_CRL_verify(crl, pubkey) <= 0) { - X509_STORE_CTX_set_error(x509_ctx, X509_V_ERR_CRL_SIGNATURE_FAILURE); - X509_OBJECT_free(obj); - if (pubkey) { - EVP_PKEY_free(pubkey); - } - return 0; /* fail */ - } - if (pubkey) { - EVP_PKEY_free(pubkey); - } - - /* check date of CRL to make sure it's not expired */ - if (!next_update) { - X509_STORE_CTX_set_error(x509_ctx, X509_V_ERR_ERROR_IN_CRL_NEXT_UPDATE_FIELD); - X509_OBJECT_free(obj); - return 0; /* fail */ - } - if (X509_cmp_current_time(next_update) < 0) { - X509_STORE_CTX_set_error(x509_ctx, X509_V_ERR_CRL_HAS_EXPIRED); - X509_OBJECT_free(obj); - return 0; /* fail */ - } - X509_OBJECT_free(obj); - } - - /* try to retrieve a CRL corresponding to the _issuer_ of - * the current certificate in order to check for revocation */ - store_ctx = X509_STORE_CTX_new(); - obj = X509_OBJECT_new(); - X509_STORE_CTX_init(store_ctx, opts->crl_store, NULL, NULL); - rc = X509_STORE_CTX_get_by_subject(store_ctx, X509_LU_CRL, issuer, obj); - X509_STORE_CTX_free(store_ctx); - crl = X509_OBJECT_get0_X509_CRL(obj); - if ((rc > 0) && crl) { - /* check if the current certificate is revoked by this CRL */ - n = sk_X509_REVOKED_num(X509_CRL_get_REVOKED(crl)); - for (i = 0; i < n; i++) { - revoked = sk_X509_REVOKED_value(X509_CRL_get_REVOKED(crl), i); - if (ASN1_INTEGER_cmp(X509_REVOKED_get0_serialNumber(revoked), X509_get_serialNumber(cert)) == 0) { - ERR(NULL, "Certificate revoked!"); - X509_STORE_CTX_set_error(x509_ctx, X509_V_ERR_CERT_REVOKED); - X509_OBJECT_free(obj); - return 0; /* fail */ - } - } - X509_OBJECT_free(obj); - } - - return 1; /* success */ -} - -#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) { @@ -241,12 +47,6 @@ _nc_client_tls_destroy_opts(struct nc_client_tls_opts *opts) free(opts->key_path); free(opts->ca_file); free(opts->ca_dir); - SSL_CTX_free(opts->tls_ctx); - - free(opts->crl_file); - free(opts->crl_dir); - X509_STORE_free(opts->crl_store); - memset(opts, 0, sizeof *opts); } @@ -260,32 +60,21 @@ 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; } - opts->tls_ctx_change = 1; - return 0; } @@ -305,7 +94,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; } @@ -333,7 +122,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; } @@ -342,26 +131,18 @@ _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; } - opts->tls_ctx_change = 1; - return 0; } @@ -381,7 +162,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; } @@ -405,200 +186,197 @@ nc_client_tls_ch_get_trusted_ca_paths(const char **ca_file, const char **ca_dir) _nc_client_tls_get_trusted_ca_paths(ca_file, ca_dir, &tls_ch_opts); } -static int -_nc_client_tls_set_crl_paths(const char *crl_file, const char *crl_dir, struct nc_client_tls_opts *opts) +API int +nc_client_tls_set_crl_paths(const char *UNUSED(crl_file), const char *UNUSED(crl_dir)) { - if (!crl_file && !crl_dir) { - ERRARG("crl_file and crl_dir"); - return -1; - } - - free(opts->crl_file); - free(opts->crl_dir); - - if (crl_file) { - opts->crl_file = strdup(crl_file); - if (!opts->crl_file) { - ERRMEM; - return -1; - } - } else { - opts->crl_file = NULL; - } - - if (crl_dir) { - opts->crl_dir = strdup(crl_dir); - if (!opts->crl_dir) { - ERRMEM; - return -1; - } - } else { - opts->crl_dir = NULL; - } - - opts->crl_store_change = 1; - - return 0; + ERR(NULL, "nc_client_tls_set_crl_paths() is deprecated, do not use it."); + return -1; } API int -nc_client_tls_set_crl_paths(const char *crl_file, const char *crl_dir) +nc_client_tls_ch_set_crl_paths(const char *UNUSED(crl_file), const char *UNUSED(crl_dir)) { - return _nc_client_tls_set_crl_paths(crl_file, crl_dir, &tls_opts); -} - -API int -nc_client_tls_ch_set_crl_paths(const char *crl_file, const char *crl_dir) -{ - return _nc_client_tls_set_crl_paths(crl_file, crl_dir, &tls_ch_opts); -} - -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"); - return; - } - - if (crl_file) { - *crl_file = opts->crl_file; - } - if (crl_dir) { - *crl_dir = opts->crl_dir; - } + ERR(NULL, "nc_client_tls_ch_set_crl_paths() is deprecated, do not use it."); + return -1; } API void -nc_client_tls_get_crl_paths(const char **crl_file, const char **crl_dir) +nc_client_tls_get_crl_paths(const char **UNUSED(crl_file), const char **UNUSED(crl_dir)) { - _nc_client_tls_get_crl_paths(crl_file, crl_dir, &tls_opts); + ERR(NULL, "nc_client_tls_get_crl_paths() is deprecated, do not use it."); + return; } API void -nc_client_tls_ch_get_crl_paths(const char **crl_file, const char **crl_dir) +nc_client_tls_ch_get_crl_paths(const char **UNUSED(crl_file), const char **UNUSED(crl_dir)) { - _nc_client_tls_get_crl_paths(crl_file, crl_dir, &tls_ch_opts); + ERR(NULL, "nc_client_tls_ch_get_crl_paths() is deprecated, do not use it."); + return; } API int nc_client_tls_ch_add_bind_listen(const char *address, uint16_t port) { - return nc_client_ch_add_bind_listen(address, port, NC_TI_OPENSSL); + return nc_client_ch_add_bind_listen(address, port, NULL, NC_TI_TLS); +} + +API int +nc_client_tls_ch_add_bind_hostname_listen(const char *address, uint16_t port, const char *hostname) +{ + return nc_client_ch_add_bind_listen(address, port, hostname, NC_TI_TLS); } API int nc_client_tls_ch_del_bind(const char *address, uint16_t port) { - return nc_client_ch_del_bind(address, port, NC_TI_OPENSSL); + return nc_client_ch_del_bind(address, port, NC_TI_TLS); } static int -nc_client_tls_update_opts(struct nc_client_tls_opts *opts) +nc_client_tls_connect_check(int connect_ret, void *tls_session, const char *peername) { - char *key; - X509_LOOKUP *lookup; + uint32_t verify; + char *err; - if (!opts->tls_ctx || opts->tls_ctx_change) { - SSL_CTX_free(opts->tls_ctx); + /* check certificate verification result */ + verify = nc_tls_get_verify_result_wrap(tls_session); + if (!verify && (connect_ret == 1)) { + VRB(NULL, "Server certificate verified (domain \"%s\").", peername); + } else if (verify) { + err = nc_tls_verify_error_string_wrap(verify); + ERR(NULL, "Server certificate error (%s).", err); + free(err); + } -#if 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 - { - ERR(NULL, "Unable to create OpenSSL context (%s).", ERR_reason_error_string(ERR_get_error())); - return -1; - } - SSL_CTX_set_verify(opts->tls_ctx, SSL_VERIFY_PEER, tlsauth_verify_callback); + /* check TLS connection result */ + if (connect_ret != 1) { + nc_client_tls_print_connect_err_wrap(connect_ret, peername, tls_session); + } - /* get peer certificate */ - if (SSL_CTX_use_certificate_file(opts->tls_ctx, opts->cert_path, SSL_FILETYPE_PEM) != 1) { - ERR(NULL, "Loading the client certificate from \'%s\' failed (%s).", opts->cert_path, - ERR_reason_error_string(ERR_get_error())); - return -1; - } + return connect_ret; +} - /* if the file with private key not specified, expect that the private key is stored with the certificate */ - if (!opts->key_path) { - key = opts->cert_path; - } else { - key = opts->key_path; - } - if (SSL_CTX_use_PrivateKey_file(opts->tls_ctx, key, SSL_FILETYPE_PEM) != 1) { - ERR(NULL, "Loading the client priavte key from \'%s\' failed (%s).", key, - ERR_reason_error_string(ERR_get_error())); - return -1; - } +static void * +nc_client_tls_session_new(int sock, const char *host, int timeout, struct nc_client_tls_opts *opts, void **out_tls_cfg, struct nc_tls_ctx *tls_ctx) +{ + int ret = 0, sock_tmp = sock; + struct timespec ts_timeout; + void *tls_session, *tls_cfg, *cli_cert, *cli_pkey, *cert_store, *crl_store; - if (!SSL_CTX_load_verify_locations(opts->tls_ctx, opts->ca_file, opts->ca_dir)) { - ERR(NULL, "Failed to load the locations of trusted CA certificates (%s).", - ERR_reason_error_string(ERR_get_error())); - return -1; + tls_session = tls_cfg = cli_cert = cli_pkey = cert_store = crl_store = NULL; + + /* prepare TLS context from which a session will be created */ + tls_cfg = nc_tls_config_new_wrap(NC_CLIENT); + if (!tls_cfg) { + goto fail; + } + + /* opaque CA/CRL certificate store */ + cert_store = nc_tls_cert_store_new_wrap(); + if (!cert_store) { + goto fail; + } + + /* load client's key and certificate */ + if (nc_client_tls_load_cert_key_wrap(opts->cert_path, opts->key_path, &cli_cert, &cli_pkey)) { + goto fail; + } + + /* load trusted CA certificates */ + if (nc_client_tls_load_trusted_certs_wrap(cert_store, opts->ca_file, opts->ca_dir)) { + goto fail; + } + + /* load CRLs from set certificates' extensions */ + if (nc_session_tls_crl_from_cert_ext_fetch(cli_cert, cert_store, &crl_store)) { + goto fail; + } + + /* set client's verify mode flags */ + nc_client_tls_set_verify_wrap(tls_cfg); + + /* init TLS context and store data which may be needed later in it */ + if (nc_tls_init_ctx_wrap(cli_cert, cli_pkey, cert_store, crl_store, tls_ctx)) { + goto fail; + } + + /* memory is managed by context now */ + cli_cert = cli_pkey = cert_store = crl_store = NULL; + + /* setup config from ctx */ + if (nc_tls_setup_config_from_ctx_wrap(tls_ctx, NC_CLIENT, tls_cfg)) { + goto fail; + } + + /* session from config */ + tls_session = nc_tls_session_new_wrap(tls_cfg); + if (!tls_session) { + goto fail; + } + + /* set session fd */ + nc_tls_set_fd_wrap(tls_session, sock, tls_ctx); + + sock = -1; + + /* set session hostname to check against in the server cert */ + if (nc_client_tls_set_hostname_wrap(tls_session, host)) { + goto fail; + } + + /* handshake */ + if (timeout > -1) { + nc_timeouttime_get(&ts_timeout, timeout); + } + while ((ret = nc_client_tls_handshake_step_wrap(tls_session, sock_tmp)) == 0) { + usleep(NC_TIMEOUT_STEP); + if ((timeout > -1) && (nc_timeouttime_cur_diff(&ts_timeout) < 1)) { + ERR(NULL, "SSL connect timeout."); + goto fail; } } - if (opts->crl_store_change || (!opts->crl_store && (opts->crl_file || opts->crl_dir))) { - /* set the revocation store with the correct paths for the callback */ - X509_STORE_free(opts->crl_store); - - opts->crl_store = X509_STORE_new(); - if (!opts->crl_store) { - ERR(NULL, "Unable to create a certificate store (%s).", ERR_reason_error_string(ERR_get_error())); - return -1; - } - -#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."); - return -1; - } - if (X509_LOOKUP_add_dir(lookup, opts->crl_file, X509_FILETYPE_PEM) != 1) { - ERR(NULL, "Failed to add the revocation lookup file \"%s\".", opts->crl_file); - return -1; - } - } - - if (opts->crl_dir) { - if (!(lookup = X509_STORE_add_lookup(opts->crl_store, X509_LOOKUP_hash_dir()))) { - ERR(NULL, "Failed to add lookup method to CRL checking."); - return -1; - } - if (X509_LOOKUP_add_dir(lookup, opts->crl_dir, X509_FILETYPE_PEM) != 1) { - ERR(NULL, "Failed to add the revocation lookup directory \"%s\".", opts->crl_dir); - return -1; - } - } + /* check if handshake was ok */ + if (nc_client_tls_connect_check(ret, tls_session, host) != 1) { + goto fail; } - return 0; + *out_tls_cfg = tls_cfg; + return tls_session; + +fail: + if (sock > -1) { + close(sock); + } + + nc_tls_session_destroy_wrap(tls_session); + nc_tls_cert_destroy_wrap(cli_cert); + nc_tls_privkey_destroy_wrap(cli_pkey); + nc_tls_cert_store_destroy_wrap(cert_store); + nc_tls_crl_store_destroy_wrap(crl_store); + nc_tls_config_destroy_wrap(tls_cfg); + return NULL; } API struct nc_session * nc_connect_tls(const char *host, unsigned short port, struct ly_ctx *ctx) { struct nc_session *session = NULL; - int sock, verify, ret; - unsigned long tls_err; - struct timespec ts_timeout, ts_cur; - const char *peername; + int sock; char *ip_host = NULL; + void *tls_cfg = NULL; + struct nc_tls_ctx tls_ctx = {0}; - if (!tls_opts.cert_path || (!tls_opts.ca_file && !tls_opts.ca_dir)) { - ERRINIT; + if (!tls_opts.cert_path) { + ERR(NULL, "Client certificate not set."); + return NULL; + } else if (!tls_opts.ca_file && !tls_opts.ca_dir) { + ERR(NULL, "Certificate authority certificates not set."); return NULL; } /* process parameters */ - if (!host || strisempty(host)) { + if (!host || (host[0] == '\0')) { host = "localhost"; } @@ -606,94 +384,33 @@ nc_connect_tls(const char *host, unsigned short port, struct ly_ctx *ctx) port = NC_PORT_TLS; } - /* create/update TLS structures */ - if (nc_client_tls_update_opts(&tls_opts)) { - 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; - /* fill the session */ - session->ti_type = NC_TI_OPENSSL; - if (!(session->ti.tls = SSL_new(tls_opts.tls_ctx))) { - ERR(NULL, "Failed to create a new TLS session structure (%s).", ERR_reason_error_string(ERR_get_error())); - goto fail; - } - /* create and assign socket */ - sock = nc_sock_connect(host, port, -1, &client_opts.ka, NULL, &ip_host); + sock = nc_sock_connect(NULL, 0, host, port, -1, &client_opts.ka, NULL, &ip_host); if (sock == -1) { ERR(NULL, "Unable to connect to %s:%u (%s).", host, port, strerror(errno)); goto fail; } - SSL_set_fd(session->ti.tls, sock); - /* set the SSL_MODE_AUTO_RETRY flag to allow OpenSSL perform re-handshake automatically */ - SSL_set_mode(session->ti.tls, SSL_MODE_AUTO_RETRY); - -#if OPENSSL_VERSION_NUMBER >= 0x10100000L // >= 1.1.0 - /* server identity (hostname) verification */ - if (!SSL_set1_host(session->ti.tls, host)) { - ERR(NULL, "Failed to set expected server hostname."); + /* fill the session */ + session->ti_type = NC_TI_TLS; + if (!(session->ti.tls.session = nc_client_tls_session_new(sock, host, NC_TRANSPORT_TIMEOUT, &tls_opts, &tls_cfg, &tls_ctx))) { goto fail; } -#endif + session->ti.tls.config = tls_cfg; - /* connect and perform the handshake */ - nc_gettimespec_mono(&ts_timeout); - nc_addtimespec(&ts_timeout, NC_TRANSPORT_TIMEOUT); - tlsauth_ch = 0; - while (((ret = SSL_connect(session->ti.tls)) != 1) && (SSL_get_error(session->ti.tls, ret) == SSL_ERROR_WANT_READ)) { - usleep(NC_TIMEOUT_STEP); - nc_gettimespec_mono(&ts_cur); - if (nc_difftimespec(&ts_cur, &ts_timeout) < 1) { - ERR(NULL, "SSL_connect timeout."); - goto fail; - } - } - if (ret != 1) { - switch (SSL_get_error(session->ti.tls, ret)) { - case SSL_ERROR_SYSCALL: - ERR(NULL, "SSL_connect failed (%s).", errno ? strerror(errno) : "unexpected EOF"); - break; - case SSL_ERROR_SSL: - tls_err = ERR_get_error(); - ERR(NULL, "SSL_connect failed (%s).", ERR_reason_error_string(tls_err)); - break; - default: - ERR(NULL, "SSL_connect failed."); - break; - } + /* memory belongs to session */ + memcpy(&session->ti.tls.ctx, &tls_ctx, sizeof tls_ctx); + memset(&tls_ctx, 0, sizeof tls_ctx); + + if (nc_client_session_new_ctx(session, ctx) != EXIT_SUCCESS) { goto fail; } - /* check certificate verification result */ - verify = SSL_get_verify_result(session->ti.tls); - switch (verify) { - case X509_V_OK: -#if OPENSSL_VERSION_NUMBER >= 0x10100000L // >= 1.1.0 - peername = SSL_get0_peername(session->ti.tls); - VRB(NULL, "Server certificate successfully verified (domain \"%s\").", peername ? peername : ""); -#else - (void)peername; - VRB(NULL, "Server certificate successfully verified."); -#endif - break; - default: - WRN(NULL, "Server certificate verification problem (%s).", X509_verify_cert_error_string(verify)); - } - - if (nc_session_new_ctx(session, ctx) != EXIT_SUCCESS) { - goto fail; - } - ctx = session->ctx; - /* NETCONF handshake */ if (nc_handshake_io(session) != NC_MSG_HELLO) { goto fail; @@ -704,46 +421,58 @@ nc_connect_tls(const char *host, unsigned short port, struct ly_ctx *ctx) goto fail; } - /* store information into session and the dictionary */ - lydict_insert_zc(ctx, ip_host, &session->host); + /* start monitoring the session if the monitoring thread is running */ + if (nc_client_monitoring_session_start(session)) { + goto fail; + } + + /* store information into session */ + session->host = ip_host; session->port = port; - lydict_insert(ctx, "certificate-based", 0, &session->username); + session->username = strdup("certificate-based"); return session; fail: free(ip_host); nc_session_free(session, NULL); + nc_tls_ctx_destroy_wrap(&tls_ctx); return NULL; } API struct nc_session * -nc_connect_libssl(SSL *tls, struct ly_ctx *ctx) +nc_connect_libssl(void *UNUSED(tls), struct ly_ctx *UNUSED(ctx)) { - struct nc_session *session; + ERR(NULL, "nc_connect_libssl() is deprecated, do not use it."); + return NULL; +} - if (!tls) { - ERRARG("tls"); - return NULL; - } else if (!SSL_is_init_finished(tls)) { - ERR(NULL, "Supplied TLS session is not fully connected!"); - return NULL; - } +struct nc_session * +nc_accept_callhome_tls_sock(int sock, const char *host, uint16_t port, struct ly_ctx *ctx, int timeout, const char *peername) +{ + struct nc_session *session = NULL; + void *tls_cfg = NULL; + struct nc_tls_ctx tls_ctx = {0}; /* prepare session structure */ session = nc_new_session(NC_CLIENT, 0); - 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; - if (nc_session_new_ctx(session, ctx) != EXIT_SUCCESS) { + /* fill the session */ + session->ti_type = NC_TI_TLS; + if (!(session->ti.tls.session = nc_client_tls_session_new(sock, peername, timeout, &tls_ch_opts, &tls_cfg, &tls_ctx))) { + goto fail; + } + session->ti.tls.config = tls_cfg; + + /* memory belongs to session */ + memcpy(&session->ti.tls.ctx, &tls_ctx, sizeof tls_ctx); + memset(&tls_ctx, 0, sizeof tls_ctx); + + if (nc_client_session_new_ctx(session, ctx) != EXIT_SUCCESS) { goto fail; } - ctx = session->ctx; /* NETCONF handshake */ if (nc_handshake_io(session) != NC_MSG_HELLO) { @@ -755,94 +484,22 @@ nc_connect_libssl(SSL *tls, struct ly_ctx *ctx) goto fail; } + session->flags |= NC_SESSION_CALLHOME; + + /* start monitoring the session if the monitoring thread is running */ + if (nc_client_monitoring_session_start(session)) { + goto fail; + } + + /* store information into session */ + session->host = strdup(host); + session->port = port; + session->username = strdup("certificate-based"); + return session; fail: - session->ti.tls = NULL; nc_session_free(session, NULL); + nc_tls_ctx_destroy_wrap(&tls_ctx); return NULL; } - -struct nc_session * -nc_accept_callhome_tls_sock(int sock, const char *host, uint16_t port, struct ly_ctx *ctx, int timeout) -{ - int verify, ret; - SSL *tls = NULL; - struct nc_session *session = NULL; - struct timespec ts_timeout, ts_cur; - - if (nc_client_tls_update_opts(&tls_ch_opts)) { - goto cleanup; - } - - if (!(tls = SSL_new(tls_ch_opts.tls_ctx))) { - ERR(NULL, "Failed to create new TLS session structure (%s).", ERR_reason_error_string(ERR_get_error())); - goto cleanup; - } - - SSL_set_fd(tls, sock); - - /* set the SSL_MODE_AUTO_RETRY flag to allow OpenSSL perform re-handshake automatically */ - SSL_set_mode(tls, SSL_MODE_AUTO_RETRY); - - /* connect and perform the handshake */ - if (timeout > -1) { - nc_gettimespec_mono(&ts_timeout); - nc_addtimespec(&ts_timeout, timeout); - } - tlsauth_ch = 1; - while (((ret = SSL_connect(tls)) == -1) && (SSL_get_error(tls, ret) == SSL_ERROR_WANT_READ)) { - usleep(NC_TIMEOUT_STEP); - if (timeout > -1) { - nc_gettimespec_mono(&ts_cur); - if (nc_difftimespec(&ts_cur, &ts_timeout) < 1) { - ERR(NULL, "SSL_connect timeout."); - goto cleanup; - } - } - } - if (ret != 1) { - switch (SSL_get_error(tls, ret)) { - case SSL_ERROR_SYSCALL: - ERR(NULL, "SSL_connect failed (%s).", strerror(errno)); - break; - case SSL_ERROR_SSL: - ERR(NULL, "SSL_connect failed (%s).", ERR_reason_error_string(ERR_get_error())); - break; - default: - ERR(NULL, "SSL_connect failed."); - break; - } - goto cleanup; - } - - /* check certificate verification result */ - verify = SSL_get_verify_result(tls); - switch (verify) { - case X509_V_OK: - VRB(NULL, "Server certificate successfully verified."); - break; - default: - WRN(NULL, "Server certificate verification problem (%s).", X509_verify_cert_error_string(verify)); - } - - /* connect */ - session = nc_connect_libssl(tls, ctx); - if (!session) { - goto cleanup; - } - - session->flags |= NC_SESSION_CALLHOME; - - /* store information into session and the dictionary */ - lydict_insert(session->ctx, host, 0, &session->host); - session->port = port; - lydict_insert(session->ctx, "certificate-based", 0, &session->username); - -cleanup: - if (!session) { - SSL_free(tls); - close(sock); - } - return session; -} diff --git a/src/session_mbedtls.c b/src/session_mbedtls.c new file mode 100644 index 0000000..560d85f --- /dev/null +++ b/src/session_mbedtls.c @@ -0,0 +1,2014 @@ +/** + * @file session_mbedtls.c + * @author Roman Janota + * @brief libnetconf2 - wrapped MbedTLS function calls for TLS/asymmetric cryptography support + * + * This file is a wrapper for MbedTLS function calls. The implementation is done + * in such a way that the original libnetconf2 code is not dependent on MbedTLS. + * This file is included in the build process only if MbedTLS is being used. + * + * @copyright + * Copyright (c) 2024 CESNET, z.s.p.o. + * + * This source code is licensed under BSD 3-Clause License (the "License"). + * You may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + */ + +#define _GNU_SOURCE + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "compat.h" +#include "config.h" +#include "log_p.h" +#include "session.h" +#include "session_p.h" +#include "session_wrapper.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/** + * @brief Converts mbedTLS error codes to a string. + * + * Some mbedTLS functions may return 'high' and some 'low' level errors, try to handle both cases this way. + * + * @param[in] err MbedTLS error code. + * @return Error string. + */ +static const char * +nc_get_mbedtls_str_err(int err) +{ + const char *err_str; + + err_str = mbedtls_high_level_strerr(err); + if (err_str) { + return err_str; + } + + err_str = mbedtls_low_level_strerr(err); + if (err_str) { + return err_str; + } + + return "unknown error"; +} + +/** + * @brief Converts DN to a string. + * + * @param[in] dn Internal DN representation. + * @return DN string on success, NULL of fail. + */ +static char * +nc_server_tls_dn2str(const mbedtls_x509_name *dn) +{ + char *str; + size_t len = 64; + int r; + + str = malloc(len); + NC_CHECK_ERRMEM_RET(!str, NULL); + + while ((r = mbedtls_x509_dn_gets(str, len, dn)) == MBEDTLS_ERR_X509_BUFFER_TOO_SMALL) { + len <<= 1; + str = nc_realloc(str, len); + NC_CHECK_ERRMEM_RET(!str, NULL); + } + if (r < 1) { + free(str); + ERR(NULL, "Failed to convert DN to string (%s).", nc_get_mbedtls_str_err(r)); + return NULL; + } + + return str; +} + +/** + * @brief Create a new random number generator context. + * + * @param[out] ctr_drbg Random bit generator context. + * @param[out] entropy Entropy context. + * @return 0 on success, 1 on failure. + */ +static int +nc_tls_rng_new(mbedtls_ctr_drbg_context **ctr_drbg, mbedtls_entropy_context **entropy) +{ + int rc; + + *ctr_drbg = NULL; + *entropy = NULL; + + *entropy = malloc(sizeof **entropy); + NC_CHECK_ERRMEM_GOTO(!*entropy, , fail); + *ctr_drbg = malloc(sizeof **ctr_drbg); + NC_CHECK_ERRMEM_GOTO(!*ctr_drbg, , fail); + + mbedtls_entropy_init(*entropy); + mbedtls_ctr_drbg_init(*ctr_drbg); + + rc = mbedtls_ctr_drbg_seed(*ctr_drbg, mbedtls_entropy_func, *entropy, NULL, 0); + if (rc) { + ERR(NULL, "Seeding ctr_drbg failed (%s).", nc_get_mbedtls_str_err(rc)); + goto fail; + } + + return 0; + +fail: + mbedtls_ctr_drbg_free(*ctr_drbg); + free(*ctr_drbg); + if (*entropy) { + mbedtls_entropy_free(*entropy); + free(*entropy); + } + *ctr_drbg = NULL; + *entropy = NULL; + return 1; +} + +/** + * @brief Destroy the random number generator context. + * + * @param[in] ctr_drbg Random bit generator context. + * @param[in] entropy Entropy context. + */ +static void +nc_tls_rng_destroy(mbedtls_ctr_drbg_context *ctr_drbg, mbedtls_entropy_context *entropy) +{ + mbedtls_ctr_drbg_free(ctr_drbg); + free(ctr_drbg); + if (entropy) { + mbedtls_entropy_free(entropy); + free(entropy); + } +} + +/** + * @brief Get a string representation of the verification error. + * + * @param[in] err Verification error code. + * @return String representation of the error. Caller is responsible for freeing it. + */ +static char * +nc_tls_get_verify_err_str(int err) +{ + int ret; + char *err_buf = NULL; + + err_buf = malloc(256); + NC_CHECK_ERRMEM_RET(!err_buf, NULL); + + ret = mbedtls_x509_crt_verify_info(err_buf, 256, "", err); + if (ret < 0) { + free(err_buf); + return NULL; + } + + /* strip the NL */ + err_buf[ret - 1] = '\0'; + + return err_buf; +} + +void * +nc_tls_session_new_wrap(void *tls_cfg) +{ + int rc; + mbedtls_ssl_context *session; + + session = malloc(sizeof *session); + NC_CHECK_ERRMEM_RET(!session, NULL); + + mbedtls_ssl_init(session); + + rc = mbedtls_ssl_setup(session, tls_cfg); + if (rc) { + ERR(NULL, "Setting up TLS session failed (%s).", nc_get_mbedtls_str_err(rc)); + mbedtls_ssl_free(session); + free(session); + return NULL; + } + + return session; +} + +void +nc_tls_session_destroy_wrap(void *tls_session) +{ + mbedtls_ssl_free(tls_session); + free(tls_session); +} + +void * +nc_tls_config_new_wrap(int UNUSED(side)) +{ + mbedtls_ssl_config *tls_cfg; + + tls_cfg = malloc(sizeof *tls_cfg); + NC_CHECK_ERRMEM_RET(!tls_cfg, NULL); + + mbedtls_ssl_config_init(tls_cfg); + return tls_cfg; +} + +void +nc_tls_config_destroy_wrap(void *tls_cfg) +{ + if (!tls_cfg) { + return; + } + + mbedtls_ssl_config_free(tls_cfg); + free(tls_cfg); +} + +void * +nc_tls_cert_new_wrap(void) +{ + mbedtls_x509_crt *cert; + + cert = malloc(sizeof *cert); + NC_CHECK_ERRMEM_RET(!cert, NULL); + + mbedtls_x509_crt_init(cert); + return cert; +} + +void +nc_tls_cert_destroy_wrap(void *cert) +{ + mbedtls_x509_crt_free(cert); + free(cert); +} + +/** + * @brief Create a new private key context. + * + * @return New private key context or NULL. + */ +static void * +nc_tls_pkey_new_wrap(void) +{ + mbedtls_pk_context *pkey; + + pkey = malloc(sizeof *pkey); + NC_CHECK_ERRMEM_RET(!pkey, NULL); + + mbedtls_pk_init(pkey); + return pkey; +} + +void +nc_tls_privkey_destroy_wrap(void *pkey) +{ + mbedtls_pk_free(pkey); + free(pkey); +} + +void * +nc_tls_cert_store_new_wrap(void) +{ + /* certificate is the same as a certificate store in MbedTLS */ + return nc_tls_cert_new_wrap(); +} + +void +nc_tls_cert_store_destroy_wrap(void *cert_store) +{ + /* certificate is the same as a certificate store in MbedTLS */ + nc_tls_cert_destroy_wrap(cert_store); +} + +void * +nc_tls_crl_store_new_wrap(void) +{ + mbedtls_x509_crl *crl; + + crl = malloc(sizeof *crl); + NC_CHECK_ERRMEM_RET(!crl, NULL); + + mbedtls_x509_crl_init(crl); + return crl; +} + +void +nc_tls_crl_store_destroy_wrap(void *crl_store) +{ + mbedtls_x509_crl_free(crl_store); + free(crl_store); +} + +void * +nc_tls_pem_to_cert_wrap(const char *cert_data) +{ + int rc; + mbedtls_x509_crt *cert; + + cert = nc_tls_cert_new_wrap(); + if (!cert) { + return NULL; + } + + rc = mbedtls_x509_crt_parse(cert, (const unsigned char *)cert_data, strlen(cert_data) + 1); + if (rc) { + ERR(NULL, "Parsing certificate data failed (%s).", nc_get_mbedtls_str_err(rc)); + nc_tls_cert_destroy_wrap(cert); + return NULL; + } + + return cert; +} + +int +nc_tls_add_cert_to_store_wrap(void *cert, void *cert_store) +{ + mbedtls_x509_crt *iter; + + /* store is a linked list */ + iter = cert_store; + while (iter->next) { + iter = iter->next; + } + iter->next = cert; + + return 0; +} + +void * +nc_tls_pem_to_privkey_wrap(const char *privkey_data) +{ + int rc = 0; + mbedtls_pk_context *pkey = NULL; + mbedtls_ctr_drbg_context *ctr_drbg = NULL; + mbedtls_entropy_context *entropy = NULL; + + rc = nc_tls_rng_new(&ctr_drbg, &entropy); + if (rc) { + goto cleanup; + } + + pkey = nc_tls_pkey_new_wrap(); + if (!pkey) { + rc = 1; + goto cleanup; + } + + rc = mbedtls_pk_parse_key(pkey, (const unsigned char *)privkey_data, strlen(privkey_data) + 1, NULL, 0, mbedtls_ctr_drbg_random, ctr_drbg); + if (rc) { + ERR(NULL, "Parsing private key data failed (%s).", nc_get_mbedtls_str_err(rc)); + goto cleanup; + } + +cleanup: + if (rc) { + nc_tls_privkey_destroy_wrap(pkey); + pkey = NULL; + } + nc_tls_rng_destroy(ctr_drbg, entropy); + return pkey; +} + +int +nc_server_tls_add_crl_to_store_wrap(const unsigned char *crl_data, size_t size, void *crl_store) +{ + int rc; + + /* try DER first */ + rc = mbedtls_x509_crl_parse_der(crl_store, crl_data, size); + if (!rc) { + /* success, it was DER */ + return 0; + } + + /* DER failed, try PEM */ + rc = mbedtls_x509_crl_parse(crl_store, crl_data, size + 1); + if (!rc) { + /* success, it was PEM */ + return 0; + } + + /* failed to parse it */ + ERR(NULL, "Reading downloaded CRL failed."); + return 1; +} + +int +nc_server_tls_set_tls_versions_wrap(void *tls_cfg, unsigned int tls_versions) +{ + if ((tls_versions & NC_TLS_VERSION_10) || ((tls_versions & NC_TLS_VERSION_11))) { + /* skip TLS versions 1.0 and 1.1 */ + WRN(NULL, "mbedTLS does not support TLS1.0 and TLS1.1"); + } + + /* first set the minimum version */ + if (tls_versions & NC_TLS_VERSION_12) { + mbedtls_ssl_conf_min_tls_version(tls_cfg, MBEDTLS_SSL_VERSION_TLS1_2); + } else if (tls_versions & NC_TLS_VERSION_13) { + mbedtls_ssl_conf_min_tls_version(tls_cfg, MBEDTLS_SSL_VERSION_TLS1_3); + } + + /* then set the maximum version */ + if (tls_versions & NC_TLS_VERSION_13) { + mbedtls_ssl_conf_max_tls_version(tls_cfg, MBEDTLS_SSL_VERSION_TLS1_3); + } else if (tls_versions & NC_TLS_VERSION_12) { + mbedtls_ssl_conf_max_tls_version(tls_cfg, MBEDTLS_SSL_VERSION_TLS1_2); + } + + return 0; +} + +/** + * @brief Duplicates a certificate. + * + * @param[in] cert Certificate to duplicate. + * @return Duplicated certificate or NULL. + */ +static mbedtls_x509_crt * +nc_tls_cert_dup(const mbedtls_x509_crt *cert) +{ + mbedtls_x509_crt *new_cert; + + new_cert = nc_tls_cert_new_wrap(); + if (!new_cert) { + return NULL; + } + + if (mbedtls_x509_crt_parse_der(new_cert, cert->raw.p, cert->raw.len)) { + free(new_cert); + return NULL; + } + + return new_cert; +} + +/** + * @brief Duplicate a certificate and append it to a chain. + * + * @param[in] cert Certificate to duplicate and append. + * @param[in,out] chain Chain to append the certificate to. + * @return 0 on success, -1 on error. + */ +static int +nc_server_tls_append_cert_to_chain(mbedtls_x509_crt *cert, mbedtls_x509_crt **chain) +{ + mbedtls_x509_crt *iter, *copy; + + copy = nc_tls_cert_dup(cert); + if (!copy) { + return -1; + } + + if (!*chain) { + /* first in the list */ + *chain = copy; + } else { + /* find the last cert */ + iter = *chain; + while (iter->next) { + iter = iter->next; + } + iter->next = copy; + } + + return 0; +} + +/** + * @brief Verify a certificate. + * + * @param[in] cb_data Callback data (session, opts, data for CTN). + * @param[in] cert Certificate to verify. + * @param[in] depth Certificate depth in the chain. + * @param[in,out] flags Verification flags. Used to propagate errors. + * @return 0 on success (verification result is based on the value of flags), non-zero on fatal-error. + */ +static int +nc_server_tls_verify_cb(void *cb_data, mbedtls_x509_crt *cert, int depth, uint32_t *flags) +{ + int ret = 0; + struct nc_tls_verify_cb_data *data = cb_data; + char *err; + + /* append to the chain we're building */ + ret = nc_server_tls_append_cert_to_chain(cert, (mbedtls_x509_crt **)&data->chain); + if (ret) { + nc_tls_cert_destroy_wrap(data->chain); + return MBEDTLS_ERR_X509_ALLOC_FAILED; + } + + if (!*flags) { + /* in-built verification was successful */ + ret = nc_server_tls_verify_cert(cert, depth, 1, data); + } else { + /* in-built verification failed, but the client still may be authenticated if: + * 1) the peer cert matches any configured end-entity cert + * 2) the peer cert has a valid chain of trust to any configured certificate authority cert + * otherwise just continue until we reach the peer cert (depth = 0) + */ + if ((depth == 0) && (*flags == MBEDTLS_X509_BADCERT_NOT_TRUSTED)) { + /* not trusted self-signed peer certificate, case 1) */ + ret = nc_server_tls_verify_cert(cert, depth, 0, data); + if (!ret) { + *flags &= ~MBEDTLS_X509_BADCERT_NOT_TRUSTED; + } + } else if (*flags == MBEDTLS_X509_BADCERT_MISSING) { + /* full chain of trust is invalid, but it may be valid partially, case 2) */ + ret = nc_server_tls_verify_cert(cert, depth, 0, data); + if (!ret) { + *flags &= ~MBEDTLS_X509_BADCERT_MISSING; + } + } else { + err = nc_tls_get_verify_err_str(*flags); + ERR(data->session, "Cert verify: fail (%s).", err); + free(err); + ret = 1; + } + } + + if ((ret == -1) || (depth == 0)) { + /* free the chain */ + nc_tls_cert_destroy_wrap(data->chain); + } + + if (ret == -1) { + /* fatal error */ + return MBEDTLS_ERR_X509_ALLOC_FAILED; + } else if (!ret) { + /* success */ + if ((depth == 0) && (!data->session->opts.server.client_cert)) { + /* copy the client cert */ + data->session->opts.server.client_cert = nc_tls_cert_dup(cert); + if (!data->session->opts.server.client_cert) { + return MBEDTLS_ERR_X509_ALLOC_FAILED; + } + } + return 0; + } else { + if (depth > 0) { + /* chain verify failed, but peer cert can still match */ + return 0; + } else { + /* failed to verify peer cert, but return 0 so that we can propagate the error via the flags */ + if (!*flags) { + *flags |= MBEDTLS_X509_BADCERT_OTHER; + } + return 0; + } + } +} + +void +nc_server_tls_set_verify_wrap(void *tls_cfg, struct nc_tls_verify_cb_data *cb_data) +{ + mbedtls_ssl_conf_authmode(tls_cfg, MBEDTLS_SSL_VERIFY_REQUIRED); + mbedtls_ssl_conf_verify(tls_cfg, nc_server_tls_verify_cb, cb_data); +} + +void +nc_client_tls_set_verify_wrap(void *tls_cfg) +{ + mbedtls_ssl_conf_authmode(tls_cfg, MBEDTLS_SSL_VERIFY_REQUIRED); +} + +char * +nc_server_tls_get_subject_wrap(void *cert) +{ + return nc_server_tls_dn2str(&(((mbedtls_x509_crt *)cert)->subject)); +} + +char * +nc_server_tls_get_issuer_wrap(void *cert) +{ + return nc_server_tls_dn2str(&(((mbedtls_x509_crt *)cert)->issuer)); +} + +void * +nc_tls_get_sans_wrap(void *cert) +{ + return &(((mbedtls_x509_crt *)cert)->subject_alt_names); +} + +void +nc_tls_sans_destroy_wrap(void *UNUSED(sans)) +{ + return; +} + +int +nc_tls_get_num_sans_wrap(void *sans) +{ + mbedtls_x509_sequence *iter; + int n = 0; + + /* sans are a linked list */ + iter = sans; + while (iter) { + ++n; + iter = iter->next; + } + + return n; +} + +int +nc_tls_get_san_value_type_wrap(void *sans, int idx, char **san_value, NC_TLS_CTN_MAPTYPE *san_type) +{ + int i, rc, ret = 0; + mbedtls_x509_sequence *iter; + mbedtls_x509_subject_alternative_name san = {0}; + const mbedtls_x509_buf *ip; + + *san_value = NULL; + *san_type = NC_TLS_CTN_UNKNOWN; + + /* find the SAN */ + iter = sans; + for (i = 0; i < idx; i++) { + iter = iter->next; + } + + /* parse it */ + rc = mbedtls_x509_parse_subject_alt_name(&iter->buf, &san); + if (rc && (rc != MBEDTLS_ERR_X509_FEATURE_UNAVAILABLE)) { + return -1; + } + + /* get its type and value */ + switch (san.type) { + case MBEDTLS_X509_SAN_DNS_NAME: + *san_type = NC_TLS_CTN_SAN_DNS_NAME; + *san_value = strndup((const char *)san.san.unstructured_name.p, san.san.unstructured_name.len); + NC_CHECK_ERRMEM_GOTO(!*san_value, ret = -1, cleanup); + break; + case MBEDTLS_X509_SAN_RFC822_NAME: + *san_type = NC_TLS_CTN_SAN_RFC822_NAME; + *san_value = strndup((const char *)san.san.unstructured_name.p, san.san.unstructured_name.len); + NC_CHECK_ERRMEM_GOTO(!*san_value, ret = -1, cleanup); + break; + case MBEDTLS_X509_SAN_IP_ADDRESS: + *san_type = NC_TLS_CTN_SAN_IP_ADDRESS; + ip = &san.san.unstructured_name; + if (ip->len == 4) { + rc = asprintf(san_value, "%d.%d.%d.%d", ip->p[0], ip->p[1], ip->p[2], ip->p[3]) == -1; + NC_CHECK_ERRMEM_GOTO(rc == -1, ret = -1, cleanup); + } else if (ip->len == 16) { + rc = asprintf(san_value, "%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x", + ip->p[0], ip->p[1], ip->p[2], ip->p[3], ip->p[4], ip->p[5], + ip->p[6], ip->p[7], ip->p[8], ip->p[9], ip->p[10], ip->p[11], + ip->p[12], ip->p[13], ip->p[14], ip->p[15]); + NC_CHECK_ERRMEM_GOTO(rc == -1, ret = -1, cleanup); + } else { + WRN(NULL, "SAN IP address in an unknown format (length is %d).", ip->len); + ret = 1; + } + break; + default: + /* we dont care about other types */ + *san_type = NC_TLS_CTN_UNKNOWN; + ret = 1; + break; + } + +cleanup: + mbedtls_x509_free_subject_alt_name(&san); + return ret; +} + +int +nc_tls_get_num_certs_wrap(void *chain) +{ + mbedtls_x509_crt *iter; + int n = 0; + + /* chain is a linked list */ + iter = chain; + while (iter) { + ++n; + iter = iter->next; + } + + return n; +} + +void +nc_tls_get_cert_wrap(void *chain, int idx, void **cert) +{ + int i; + mbedtls_x509_crt *iter; + + iter = chain; + for (i = 0; i < idx; i++) { + iter = iter->next; + } + + *cert = iter; +} + +int +nc_server_tls_certs_match_wrap(void *cert1, void *cert2) +{ + mbedtls_x509_crt *c1 = cert1; + mbedtls_x509_crt *c2 = cert2; + + if (!c1 || !c2) { + return 0; + } + + /* compare raw DER encoded data */ + if (!c1->raw.p || !c2->raw.p || (c1->raw.len != c2->raw.len) || + memcmp(c1->raw.p, c2->raw.p, c1->raw.len)) { + return 0; + } + + return 1; +} + +int +nc_server_tls_md5_wrap(void *cert, unsigned char *buf) +{ + int rc; + mbedtls_x509_crt *c = cert; + + rc = mbedtls_md5(c->raw.p, c->raw.len, buf); + if (rc) { + ERR(NULL, "Calculating MD5 digest failed (%s).", nc_get_mbedtls_str_err(rc)); + return 1; + } + + return 0; +} + +int +nc_server_tls_sha1_wrap(void *cert, unsigned char *buf) +{ + int rc; + mbedtls_x509_crt *c = cert; + + rc = mbedtls_sha1(c->raw.p, c->raw.len, buf); + if (rc) { + ERR(NULL, "Calculating SHA-1 digest failed (%s).", nc_get_mbedtls_str_err(rc)); + return 1; + } + + return 0; +} + +int +nc_server_tls_sha224_wrap(void *cert, unsigned char *buf) +{ + int rc; + mbedtls_x509_crt *c = cert; + + rc = mbedtls_sha256(c->raw.p, c->raw.len, buf, 1); + if (rc) { + ERR(NULL, "Calculating SHA-224 digest failed (%s).", nc_get_mbedtls_str_err(rc)); + return 1; + } + + return 0; +} + +int +nc_server_tls_sha256_wrap(void *cert, unsigned char *buf) +{ + int rc; + mbedtls_x509_crt *c = cert; + + rc = mbedtls_sha256(c->raw.p, c->raw.len, buf, 0); + if (rc) { + ERR(NULL, "Calculating SHA-256 digest failed (%s).", nc_get_mbedtls_str_err(rc)); + return 1; + } + + return 0; +} + +int +nc_server_tls_sha384_wrap(void *cert, unsigned char *buf) +{ + int rc; + mbedtls_x509_crt *c = cert; + + rc = mbedtls_sha512(c->raw.p, c->raw.len, buf, 1); + if (rc) { + ERR(NULL, "Calculating SHA-384 digest failed (%s).", nc_get_mbedtls_str_err(rc)); + return 1; + } + + return 0; +} + +int +nc_server_tls_sha512_wrap(void *cert, unsigned char *buf) +{ + int rc; + mbedtls_x509_crt *c = cert; + + rc = mbedtls_sha512(c->raw.p, c->raw.len, buf, 0); + if (rc) { + ERR(NULL, "Calculating SHA-512 digest failed (%s).", nc_get_mbedtls_str_err(rc)); + return 1; + } + + return 0; +} + +/** + * @brief Callback for sending data. + * + * @param[in] ctx Socket. + * @param[in] buf Data to send. + * @param[in] len Length of the data. + * @return Number of bytes sent or negative value on error. + */ +static int +nc_server_tls_send(void *ctx, const unsigned char *buf, size_t len) +{ + int sock, ret; + + NC_CHECK_ARG_RET(NULL, ctx, MBEDTLS_ERR_NET_INVALID_CONTEXT); + + sock = *(int *)ctx; + + ret = send(sock, buf, len, MSG_NOSIGNAL); + if (ret < 0) { + if ((errno == EWOULDBLOCK) || (errno = EAGAIN) || (errno == EINTR)) { + return MBEDTLS_ERR_SSL_WANT_WRITE; + } else if ((errno == EPIPE) || (errno == ECONNRESET)) { + return MBEDTLS_ERR_NET_CONN_RESET; + } else { + return MBEDTLS_ERR_NET_SEND_FAILED; + } + } + + return ret; +} + +/** + * @brief Callback for receiving data. + * + * @param[in] ctx Socket. + * @param[out] buf Buffer to store the received data. + * @param[in] len Length of the buffer. + * @return Number of bytes received or negative value on error. + */ +static int +nc_server_tls_recv(void *ctx, unsigned char *buf, size_t len) +{ + int sock, ret; + + NC_CHECK_ARG_RET(NULL, ctx, MBEDTLS_ERR_NET_INVALID_CONTEXT); + + sock = *(int *)ctx; + + ret = recv(sock, buf, len, 0); + if (ret < 0) { + if ((errno == EWOULDBLOCK) || (errno = EAGAIN) || (errno == EINTR)) { + return MBEDTLS_ERR_SSL_WANT_READ; + } else if ((errno == EPIPE) || (errno == ECONNRESET)) { + return MBEDTLS_ERR_NET_CONN_RESET; + } else { + return MBEDTLS_ERR_NET_RECV_FAILED; + } + } + + return ret; +} + +void +nc_tls_set_fd_wrap(void *tls_session, int sock, struct nc_tls_ctx *tls_ctx) +{ + /* mbedtls sets a pointer to the sock, which is stored in tls_ctx */ + *tls_ctx->sock = sock; + mbedtls_ssl_set_bio(tls_session, tls_ctx->sock, nc_server_tls_send, nc_server_tls_recv, NULL); +} + +int +nc_server_tls_handshake_step_wrap(void *tls_session) +{ + int rc = 0; + + rc = mbedtls_ssl_handshake(tls_session); + if (!rc) { + return 1; + } else if ((rc == MBEDTLS_ERR_SSL_WANT_READ) || (rc == MBEDTLS_ERR_SSL_WANT_WRITE)) { + return 0; + } else { + return rc; + } +} + +int +nc_client_tls_handshake_step_wrap(void *tls_session, int sock) +{ + int rc = 0; + struct pollfd pfd = {sock, 0, 0}; + + rc = mbedtls_ssl_handshake(tls_session); + if (!rc) { + return 1; + } else if ((rc == MBEDTLS_ERR_SSL_WANT_READ) || (rc == MBEDTLS_ERR_SSL_WANT_WRITE)) { + /* check for EPIPE */ + if (poll(&pfd, 1, 0) < 0) { + return -1; + } else { + if (pfd.revents & (POLLERR | POLLHUP | POLLNVAL)) { + return -1; + } else { + return 0; + } + } + } else { + return rc; + } +} + +void +nc_tls_ctx_destroy_wrap(struct nc_tls_ctx *tls_ctx) +{ + nc_tls_rng_destroy(tls_ctx->ctr_drbg, tls_ctx->entropy); + nc_tls_cert_destroy_wrap(tls_ctx->cert); + nc_tls_privkey_destroy_wrap(tls_ctx->pkey); + nc_tls_cert_store_destroy_wrap(tls_ctx->cert_store); + nc_tls_crl_store_destroy_wrap(tls_ctx->crl_store); + free(tls_ctx->sock); +} + +void * +nc_tls_import_privkey_file_wrap(const char *privkey_path) +{ + int rc; + mbedtls_pk_context *pkey; + mbedtls_ctr_drbg_context *ctr_drbg; + mbedtls_entropy_context *entropy; + + if (nc_tls_rng_new(&ctr_drbg, &entropy)) { + return NULL; + } + + pkey = nc_tls_pkey_new_wrap(); + if (!pkey) { + nc_tls_rng_destroy(ctr_drbg, entropy); + return NULL; + } + + rc = mbedtls_pk_parse_keyfile(pkey, privkey_path, NULL, mbedtls_ctr_drbg_random, ctr_drbg); + nc_tls_rng_destroy(ctr_drbg, entropy); + if (rc) { + ERR(NULL, "Parsing private key from file \"%s\" failed (%s).", privkey_path, nc_get_mbedtls_str_err(rc)); + nc_tls_privkey_destroy_wrap(pkey); + return NULL; + } + return pkey; +} + +int +nc_client_tls_load_cert_key_wrap(const char *cert_path, const char *key_path, void **cert, void **pkey) +{ + int ret = 0; + mbedtls_x509_crt *c; + mbedtls_pk_context *pk; + + NC_CHECK_ARG_RET(NULL, cert_path, key_path, cert, pkey, 1); + + c = nc_tls_cert_new_wrap(); + if (!c) { + return 1; + } + + ret = mbedtls_x509_crt_parse_file(c, cert_path); + if (ret) { + ERR(NULL, "Parsing certificate from file \"%s\" failed (%s).", cert_path, nc_get_mbedtls_str_err(ret)); + goto cleanup; + } + + pk = nc_tls_import_privkey_file_wrap(key_path); + if (!pk) { + ret = 1; + goto cleanup; + } + + *cert = c; + c = NULL; + *pkey = pk; + +cleanup: + nc_tls_cert_destroy_wrap(c); + return ret; +} + +int +nc_client_tls_load_trusted_certs_wrap(void *cert_store, const char *file_path, const char *dir_path) +{ + int rc; + + if (file_path && ((rc = mbedtls_x509_crt_parse_file(cert_store, file_path)) < 0)) { + ERR(NULL, "Loading CA certificate from file \"%s\" failed (%s).", file_path, nc_get_mbedtls_str_err(rc)); + return 1; + } + + if (dir_path && ((rc = mbedtls_x509_crt_parse_path(cert_store, dir_path)) < 0)) { + ERR(NULL, "Loading CA certificate from directory \"%s\" failed (%s).", dir_path, nc_get_mbedtls_str_err(rc)); + return 1; + } + + return 0; +} + +int +nc_client_tls_set_hostname_wrap(void *tls_session, const char *hostname) +{ + int rc; + + rc = mbedtls_ssl_set_hostname(tls_session, hostname); + if (rc) { + ERR(NULL, "Setting hostname failed (%s).", nc_get_mbedtls_str_err(rc)); + return 1; + } + + return 0; +} + +int +nc_tls_init_ctx_wrap(void *cert, void *pkey, void *cert_store, void *crl_store, struct nc_tls_ctx *tls_ctx) +{ + /* setup rng */ + if (nc_tls_rng_new(&tls_ctx->ctr_drbg, &tls_ctx->entropy)) { + return 1; + } + + /* fill the context */ + tls_ctx->sock = malloc(sizeof *tls_ctx->sock); + NC_CHECK_ERRMEM_RET(!tls_ctx->sock, 1); + tls_ctx->cert = cert; + tls_ctx->pkey = pkey; + tls_ctx->cert_store = cert_store; + tls_ctx->crl_store = crl_store; + return 0; +} + +int +nc_tls_setup_config_from_ctx_wrap(struct nc_tls_ctx *tls_ctx, int side, void *tls_cfg) +{ + int rc; + + /* set default config data */ + if (side == NC_SERVER) { + rc = mbedtls_ssl_config_defaults(tls_cfg, MBEDTLS_SSL_IS_SERVER, MBEDTLS_SSL_TRANSPORT_STREAM, MBEDTLS_SSL_PRESET_DEFAULT); + } else { + rc = mbedtls_ssl_config_defaults(tls_cfg, MBEDTLS_SSL_IS_CLIENT, MBEDTLS_SSL_TRANSPORT_STREAM, MBEDTLS_SSL_PRESET_DEFAULT); + } + if (rc) { + ERR(NULL, "Setting default TLS config failed (%s).", nc_get_mbedtls_str_err(rc)); + return 1; + } + + /* set config's rng */ + mbedtls_ssl_conf_rng(tls_cfg, mbedtls_ctr_drbg_random, tls_ctx->ctr_drbg); + /* set config's cert and key */ + mbedtls_ssl_conf_own_cert(tls_cfg, tls_ctx->cert, tls_ctx->pkey); + /* set config's CA and CRL cert store */ + mbedtls_ssl_conf_ca_chain(tls_cfg, tls_ctx->cert_store, tls_ctx->crl_store); + return 0; +} + +uint32_t +nc_tls_get_verify_result_wrap(void *tls_session) +{ + return mbedtls_ssl_get_verify_result(tls_session); +} + +char * +nc_tls_verify_error_string_wrap(uint32_t err_code) +{ + return nc_tls_get_verify_err_str(err_code); +} + +void +nc_client_tls_print_connect_err_wrap(int connect_ret, const char *peername, void *UNUSED(tls_session)) +{ + const char *err = nc_get_mbedtls_str_err(connect_ret); + + if (err) { + ERR(NULL, "TLS connection to \"%s\" failed (%s).", peername, err); + } else { + ERR(NULL, "TLS connection to \"%s\" failed.", peername); + } +} + +void +nc_server_tls_print_accept_err_wrap(int accept_ret, void *UNUSED(tls_session)) +{ + const char *err = nc_get_mbedtls_str_err(accept_ret); + + if (err) { + ERR(NULL, "TLS accept failed (%s).", err); + } else { + ERR(NULL, "TLS accept failed."); + } +} + +int +nc_tls_is_der_subpubkey_wrap(unsigned char *der, long len) +{ + int ret; + mbedtls_pk_context *pkey; + + pkey = nc_tls_pkey_new_wrap(); + if (!pkey) { + return -1; + } + + ret = mbedtls_pk_parse_subpubkey(&der, der + len, pkey); + nc_tls_privkey_destroy_wrap(pkey); + + return !ret; +} + +int +nc_base64_decode_wrap(const char *base64, unsigned char **bin) +{ + size_t size; + int rc; + + /* get the size of the decoded data */ + rc = mbedtls_base64_decode(NULL, 0, &size, (const unsigned char *)base64, strlen(base64)); + if (rc != MBEDTLS_ERR_BASE64_BUFFER_TOO_SMALL) { + ERR(NULL, "Base64 decoding failed (%s).", nc_get_mbedtls_str_err(rc)); + return -1; + } + + *bin = malloc(size); + NC_CHECK_ERRMEM_RET(!*bin, -1); + + /* decode */ + rc = mbedtls_base64_decode(*bin, size, &size, (const unsigned char *)base64, strlen(base64)); + if (rc) { + ERR(NULL, "Base64 decoding failed (%s).", nc_get_mbedtls_str_err(rc)); + free(*bin); + *bin = NULL; + return -1; + } + + return size; +} + +int +nc_base64_encode_wrap(const unsigned char *bin, size_t len, char **base64) +{ + size_t size; + int rc; + + rc = mbedtls_base64_encode(NULL, 0, &size, bin, len); + if (rc != MBEDTLS_ERR_BASE64_BUFFER_TOO_SMALL) { + ERR(NULL, "Base64 encoding failed (%s).", nc_get_mbedtls_str_err(rc)); + return -1; + } + + *base64 = malloc(size); + NC_CHECK_ERRMEM_RET(!*base64, -1); + + rc = mbedtls_base64_encode((unsigned char *)*base64, size, &size, bin, len); + if (rc) { + ERR(NULL, "Base64 encoding failed (%s).", nc_get_mbedtls_str_err(rc)); + free(*base64); + *base64 = NULL; + return -1; + } + + return 0; +} + +int +nc_tls_read_wrap(struct nc_session *session, unsigned char *buf, size_t size) +{ + int rc; + mbedtls_ssl_context *tls_session = session->ti.tls.session; + + rc = mbedtls_ssl_read(tls_session, buf, size); + if (rc <= 0) { + switch (rc) { + case MBEDTLS_ERR_SSL_WANT_READ: + case MBEDTLS_ERR_SSL_WANT_WRITE: + rc = 0; + break; + case MBEDTLS_ERR_SSL_PEER_CLOSE_NOTIFY: + ERR(session, "Communication socket unexpectedly closed (MbedTLS)."); + session->status = NC_STATUS_INVALID; + session->term_reason = NC_SESSION_TERM_DROPPED; + rc = -1; + break; + default: + ERR(session, "TLS communication error occurred (%s).", nc_get_mbedtls_str_err(rc)); + session->status = NC_STATUS_INVALID; + session->term_reason = NC_SESSION_TERM_OTHER; + rc = -1; + break; + } + } + + return rc; +} + +int +nc_tls_write_wrap(struct nc_session *session, const unsigned char *buf, size_t size) +{ + int rc = 0; + mbedtls_ssl_context *tls_session = session->ti.tls.session; + + rc = mbedtls_ssl_write(tls_session, buf, size); + if (rc < 0) { + switch (rc) { + case MBEDTLS_ERR_SSL_WANT_READ: + case MBEDTLS_ERR_SSL_WANT_WRITE: + rc = 0; + break; + case MBEDTLS_ERR_SSL_PEER_CLOSE_NOTIFY: + ERR(session, "TLS connection was properly closed."); + rc = -1; + break; + default: + ERR(session, "TLS communication error occurred (%s).", nc_get_mbedtls_str_err(rc)); + rc = -1; + break; + } + } + + return rc; +} + +int +nc_tls_get_num_pending_bytes_wrap(void *tls_session) +{ + return mbedtls_ssl_get_bytes_avail(tls_session); +} + +int +nc_tls_get_fd_wrap(const struct nc_session *session) +{ + return session->ti.tls.ctx.sock ? *session->ti.tls.ctx.sock : -1; +} + +void +nc_tls_close_notify_wrap(void *tls_session) +{ + int rc; + + while ((rc = mbedtls_ssl_close_notify(tls_session))) { + if ((rc != MBEDTLS_ERR_SSL_WANT_READ) && (rc != MBEDTLS_ERR_SSL_WANT_WRITE)) { + /* some error occurred */ + ERR(NULL, "Sending TLS close notify failed (%s).", nc_get_mbedtls_str_err(rc)); + return; + } + } +} + +void * +nc_tls_import_cert_file_wrap(const char *cert_path) +{ + int rc; + mbedtls_x509_crt *c; + + c = nc_tls_cert_new_wrap(); + if (!c) { + return NULL; + } + + rc = mbedtls_x509_crt_parse_file(c, cert_path); + if (rc) { + ERR(NULL, "Parsing certificate from file \"%s\" failed (%s).", cert_path, nc_get_mbedtls_str_err(rc)); + nc_tls_cert_destroy_wrap(c); + return NULL; + } + + return c; +} + +char * +nc_tls_export_privkey_pem_wrap(void *pkey) +{ + int rc; + char *pem; + size_t size = 128; + + pem = malloc(size); + NC_CHECK_ERRMEM_RET(!pem, NULL); + + while ((rc = mbedtls_pk_write_key_pem(pkey, (unsigned char *)pem, size)) == MBEDTLS_ERR_BASE64_BUFFER_TOO_SMALL) { + size <<= 1; + pem = nc_realloc(pem, size); + NC_CHECK_ERRMEM_RET(!pem, NULL); + } + if (rc < 0) { + ERR(NULL, "Exporting private key to PEM format failed (%s).", nc_get_mbedtls_str_err(rc)); + free(pem); + return NULL; + } + + return pem; +} + +char * +nc_tls_export_cert_pem_wrap(void *cert) +{ + char *b64 = NULL, *pem = NULL; + + /* encode the certificate */ + if (nc_base64_encode_wrap(((mbedtls_x509_crt *)cert)->raw.p, ((mbedtls_x509_crt *)cert)->raw.len, &b64)) { + goto cleanup; + } + + if (asprintf(&pem, "-----BEGIN CERTIFICATE-----\n%s\n-----END CERTIFICATE-----\n", b64) == -1) { + ERRMEM; + pem = NULL; + goto cleanup; + } + +cleanup: + free(b64); + return pem; +} + +char * +nc_tls_export_pubkey_pem_wrap(void *pkey) +{ + int rc; + char *pem; + size_t size = 128; + + pem = malloc(size); + NC_CHECK_ERRMEM_RET(!pem, NULL); + + while ((rc = mbedtls_pk_write_pubkey_pem(pkey, (unsigned char *)pem, size)) == MBEDTLS_ERR_BASE64_BUFFER_TOO_SMALL) { + size <<= 1; + pem = nc_realloc(pem, size); + NC_CHECK_ERRMEM_RET(!pem, NULL); + } + if (rc < 0) { + ERR(NULL, "Exporting public key to PEM format failed (%s).", nc_get_mbedtls_str_err(rc)); + free(pem); + return NULL; + } + + return pem; +} + +int +nc_tls_privkey_is_rsa_wrap(void *pkey) +{ + return mbedtls_pk_get_type(pkey) == MBEDTLS_PK_RSA; +} + +int +nc_tls_get_rsa_pubkey_params_wrap(void *pkey, void **e, void **n) +{ + int rc; + mbedtls_mpi *exp = NULL, *mod = NULL; + + exp = malloc(sizeof *exp); + mod = malloc(sizeof *mod); + if (!exp || !mod) { + ERRMEM; + goto fail; + } + mbedtls_mpi_init(exp); + mbedtls_mpi_init(mod); + + if ((rc = mbedtls_rsa_export(mbedtls_pk_rsa(*(mbedtls_pk_context *)pkey), mod, NULL, NULL, NULL, exp))) { + ERR(NULL, "Failed to export RSA public key parameters (%s).", nc_get_mbedtls_str_err(rc)); + goto fail; + } + + *e = exp; + *n = mod; + return 0; + +fail: + mbedtls_mpi_free(exp); + mbedtls_mpi_free(mod); + free(exp); + free(mod); + return 1; +} + +void +nc_tls_destroy_mpi_wrap(void *mpi) +{ + mbedtls_mpi_free(mpi); + free(mpi); +} + +int +nc_tls_privkey_is_ec_wrap(void *pkey) +{ + return mbedtls_pk_get_type(pkey) == MBEDTLS_PK_ECKEY; +} + +char * +nc_tls_get_ec_group_wrap(void *pkey) +{ + const mbedtls_ecp_curve_info *curve_info; + mbedtls_ecp_group_id group_id; + mbedtls_ecp_keypair *ec; + + /* get the group ID from the EC key */ + ec = mbedtls_pk_ec(*(mbedtls_pk_context *)pkey); + group_id = ec->private_grp.id; + + /* get the group name based on the id */ + curve_info = mbedtls_ecp_curve_info_from_grp_id(group_id); + return strdup(curve_info->name); +} + +int +nc_tls_get_ec_pubkey_params_wrap(void *pkey, void **q, void **q_grp) +{ + int ret; + mbedtls_ecp_group *grp = NULL; + mbedtls_ecp_point *p = NULL; + mbedtls_mpi *d = NULL; + + /* init group, mpi and point */ + grp = malloc(sizeof *grp); + d = malloc(sizeof *d); + p = malloc(sizeof *p); + if (!grp || !p || !d) { + ERRMEM; + ret = 1; + goto cleanup; + } + mbedtls_ecp_group_init(grp); + mbedtls_mpi_init(d); + mbedtls_ecp_point_init(p); + + /* get the group and public key */ + ret = mbedtls_ecp_export(mbedtls_pk_ec(*(mbedtls_pk_context *)pkey), grp, d, p); + if (ret) { + ERR(NULL, "Failed to export EC public key parameters (%s).", nc_get_mbedtls_str_err(ret)); + ret = 1; + goto cleanup; + } + + *q_grp = grp; + grp = NULL; + *q = p; + p = NULL; + +cleanup: + mbedtls_ecp_group_free(grp); + free(grp); + mbedtls_mpi_free(d); + free(d); + mbedtls_ecp_point_free(p); + free(p); + return ret; +} + +int +nc_tls_ec_point_to_bin_wrap(void *q, void *q_grp, unsigned char **bin, int *bin_len) +{ + int rc; + unsigned char *buf; + size_t buf_len = 32, out_len; + + buf = malloc(buf_len); + NC_CHECK_ERRMEM_RET(!buf, 1); + + while ((rc = (mbedtls_ecp_point_write_binary(q_grp, q, MBEDTLS_ECP_PF_COMPRESSED, &out_len, buf, buf_len)))) { + if (rc != MBEDTLS_ERR_ECP_BUFFER_TOO_SMALL) { + break; + } + buf_len <<= 1; + buf = nc_realloc(buf, buf_len); + NC_CHECK_ERRMEM_RET(!buf, 1); + } + if (rc) { + ERR(NULL, "Failed to write EC public key binary (%s).", nc_get_mbedtls_str_err(rc)); + free(buf); + return 1; + } + + *bin = buf; + *bin_len = out_len; + return 0; +} + +void +nc_tls_ec_point_destroy_wrap(void *p) +{ + mbedtls_ecp_point_free(p); + free(p); +} + +void +nc_tls_ec_group_destroy_wrap(void *grp) +{ + mbedtls_ecp_group_free(grp); + free(grp); +} + +int +nc_tls_mpi2bin_wrap(void *mpi, unsigned char **bin, int *bin_len) +{ + int rc; + unsigned char *buf; + int buf_len; + + buf_len = mbedtls_mpi_size(mpi); + buf = malloc(buf_len); + NC_CHECK_ERRMEM_RET(!buf, 1); + + rc = mbedtls_mpi_write_binary(mpi, buf, buf_len); + if (rc) { + ERR(NULL, "Failed to convert MPI to binary (%s).", nc_get_mbedtls_str_err(rc)); + free(buf); + return 1; + } + + *bin = buf; + *bin_len = buf_len; + return 0; +} + +void * +nc_tls_import_pubkey_file_wrap(const char *pubkey_path) +{ + int rc = 0; + mbedtls_pk_context *pk = NULL; + + pk = nc_tls_pkey_new_wrap(); + if (!pk) { + return NULL; + } + + rc = mbedtls_pk_parse_public_keyfile(pk, pubkey_path); + if (rc) { + ERR(NULL, "Parsing public key from file \"%s\" failed (%s).", pubkey_path, nc_get_mbedtls_str_err(rc)); + nc_tls_privkey_destroy_wrap(pk); + return NULL; + } + + return pk; +} + +/** + * @brief Parse the CRL distribution points X509v3 extension and obtain the URIs. + * + * @param[in,out] p Pointer to the DER encoded extension. When the function gets called, this should + * point to the first byte in the value of CRLDistributionPoints. + * @param[in] len Length of the CRLDistributionPoints ASN.1 encoded value. + * @param[out] uris Array of URIs found in the extension. + * @param[out] uri_count Number of URIs found in the extension. + * @return 0 on success, non-zero on error. + */ +static int +nc_server_tls_parse_crl_dist_points(unsigned char **p, size_t len, char ***uris, int *uri_count) +{ + char **tmp_uris, *uri; + int ret = 0; + size_t name_len; + unsigned char *end_crl_dist_points, *end_general_names, *end_names, tag, tag_class, tag_value; + + /* + * parsing the value of CRLDistributionPoints + * + * CRLDistributionPoints ::= SEQUENCE SIZE (1..MAX) OF DistributionPoint + */ + end_crl_dist_points = *p + len; + while (*p < end_crl_dist_points) { + /* + * DistributionPoint ::= SEQUENCE { + * distributionPoint [0] DistributionPointName OPTIONAL, + * reasons [1] ReasonFlags OPTIONAL, + * cRLIssuer [2] GeneralNames OPTIONAL } + */ + ret = mbedtls_asn1_get_tag(p, end_crl_dist_points, &len, MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE); + if (ret) { + ERR(NULL, "Failed to parse CRL distribution points extension (%s).", nc_get_mbedtls_str_err(ret)); + goto cleanup; + } + if (!len) { + /* empty sequence */ + continue; + } + + /* parse distributionPoint */ + ret = mbedtls_asn1_get_tag(p, end_crl_dist_points, &len, MBEDTLS_ASN1_CONTEXT_SPECIFIC | MBEDTLS_ASN1_CONSTRUCTED | 0); + if (!ret) { + /* + * DistributionPointName ::= CHOICE { + * fullName [0] GeneralNames, + * nameRelativeToCRLIssuer [1] RelativeDistinguishedName } + */ + ret = mbedtls_asn1_get_tag(p, end_crl_dist_points, &len, MBEDTLS_ASN1_CONTEXT_SPECIFIC | MBEDTLS_ASN1_CONSTRUCTED | 0); + if (ret) { + if ((ret == MBEDTLS_ERR_ASN1_UNEXPECTED_TAG) && (**p == (MBEDTLS_ASN1_CONTEXT_SPECIFIC | MBEDTLS_ASN1_CONSTRUCTED | 1))) { + /* it's nameRelativeToCRLIssuer, but we don't support it */ + ERR(NULL, "Failed to parse CRL distribution points extension (nameRelativeToCRLIssuer not yet supported)."); + goto cleanup; + } else { + ERR(NULL, "Failed to parse CRL distribution points extension (%s).", nc_get_mbedtls_str_err(ret)); + goto cleanup; + } + } + + /* GeneralNames ::= SEQUENCE SIZE (1..MAX) OF GeneralName */ + end_general_names = *p + len; + ret = mbedtls_asn1_get_tag(p, end_general_names, &name_len, MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE); + if (ret) { + ERR(NULL, "Failed to parse GeneralNames in CRL distribution points extension (%s)", nc_get_mbedtls_str_err(ret)); + goto cleanup; + } + + end_names = *p + name_len; + while (*p < end_names) { + tag = **p; + tag_class = tag & MBEDTLS_ASN1_TAG_CLASS_MASK; + tag_value = tag & MBEDTLS_ASN1_TAG_VALUE_MASK; + /* + * read the GeneralName tag and length + * + * GeneralName ::= CHOICE { + * otherName [0] AnotherName, + * rfc822Name [1] IA5String, + * dNSName [2] IA5String, + * x400Address [3] ORAddress, + * directoryName [4] Name, + * ediPartyName [5] EDIPartyName, + * uniformResourceIdentifier [6] IA5String, + * iPAddress [7] OCTET STRING, + * registeredID [8] OBJECT IDENTIFIER } + */ + ret = mbedtls_asn1_get_tag(p, end_names, &name_len, MBEDTLS_ASN1_CONTEXT_SPECIFIC | (tag & MBEDTLS_ASN1_CONSTRUCTED) | tag_value); + if (ret) { + ERR(NULL, "Failed to parse GeneralName in CRL distribution points extension (%s).", + nc_get_mbedtls_str_err(ret)); + goto cleanup; + } + + /* uniformResourceIdentifier [6] IA5String */ + if ((tag_class == MBEDTLS_ASN1_CONTEXT_SPECIFIC) && + (tag_value == MBEDTLS_X509_SAN_UNIFORM_RESOURCE_IDENTIFIER)) { + uri = strndup((char *)*p, name_len); + if (!uri) { + ERRMEM; + ret = 1; + goto cleanup; + } + + tmp_uris = realloc(*uris, (*uri_count + 1) * sizeof **uris); + if (!tmp_uris) { + ERRMEM; + free(uri); + ret = 1; + goto cleanup; + } + *uris = tmp_uris; + + (*uris)[*uri_count] = uri; + ++(*uri_count); + } + + /* Move to the next GeneralName */ + *p += name_len; + } + + } else if (ret != MBEDTLS_ERR_ASN1_UNEXPECTED_TAG) { + /* failed to parse it, but not because it's optional */ + ERR(NULL, "Failed to parse CRL distribution points extension (%s).", nc_get_mbedtls_str_err(ret)); + goto cleanup; + } + } + +cleanup: + return ret; +} + +int +nc_server_tls_get_crl_distpoint_uris_wrap(void *leaf_cert, void *cert_store, char ***uris, int *uri_count) +{ + int ret = 0, is_critical = 0, cert_count, i; + mbedtls_x509_crt *cert; + unsigned char *p, *end_v3_ext, *end_ext, *end_ext_octet; + size_t len; + mbedtls_x509_buf ext_oid = {0}; + + NC_CHECK_ARG_RET(NULL, cert_store, uris, uri_count, 1); + + *uris = NULL; + *uri_count = 0; + + /* get the number of certs in the store */ + cert = cert_store; + cert_count = 0; + while (cert) { + ++cert_count; + cert = cert->next; + } + + /* iterate over all the certs */ + for (i = -1; i < cert_count; i++) { + if (i == -1) { + cert = leaf_cert; + } else if (i == 0) { + cert = cert_store; + } else { + cert = cert->next; + } + + if (!cert->v3_ext.len) { + /* no extensions, skip this cert */ + continue; + } + + /* go over all the extensions and try to find the CRL distribution points */ + p = cert->v3_ext.p; + end_v3_ext = p + cert->v3_ext.len; + + /* + * Extensions ::= SEQUENCE SIZE (1..MAX) OF Extension + */ + ret = mbedtls_asn1_get_tag(&p, end_v3_ext, &len, MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE); + if (ret) { + ERR(NULL, "Failed to parse CRL distribution points extension (%s).", nc_get_mbedtls_str_err(ret)); + goto cleanup; + } + + while (p < end_v3_ext) { + /* + * Extension ::= SEQUENCE { + * extnID OBJECT IDENTIFIER, + * critical BOOLEAN DEFAULT FALSE, + * extnValue OCTET STRING } + */ + ret = mbedtls_asn1_get_tag(&p, end_v3_ext, &len, MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE); + if (ret) { + ERR(NULL, "Failed to parse CRL distribution points extension (%s).", nc_get_mbedtls_str_err(ret)); + goto cleanup; + } + + end_ext = p + len; + + /* parse extnID */ + ret = mbedtls_asn1_get_tag(&p, end_ext, &ext_oid.len, MBEDTLS_ASN1_OID); + if (ret) { + ERR(NULL, "Failed to parse CRL distribution points extension (%s).", nc_get_mbedtls_str_err(ret)); + goto cleanup; + } + ext_oid.tag = MBEDTLS_ASN1_OID; + ext_oid.p = p; + + if (memcmp(ext_oid.p, MBEDTLS_OID_CRL_DISTRIBUTION_POINTS, ext_oid.len)) { + /* not the extension we are looking for */ + p = end_ext; + continue; + } + + p += ext_oid.len; + + /* parse optional critical */ + ret = mbedtls_asn1_get_bool(&p, end_ext, &is_critical); + if (ret && (ret != MBEDTLS_ERR_ASN1_UNEXPECTED_TAG)) { + ERR(NULL, "Failed to parse CRL distribution points extension (%s).", nc_get_mbedtls_str_err(ret)); + goto cleanup; + } + + /* parse extnValue */ + ret = mbedtls_asn1_get_tag(&p, end_ext, &len, MBEDTLS_ASN1_OCTET_STRING); + if (ret) { + ERR(NULL, "Failed to parse CRL distribution points extension (%s).", nc_get_mbedtls_str_err(ret)); + goto cleanup; + } + + end_ext_octet = p + len; + + /* + * parse extnValue, that is CRLDistributionPoints + * + * CRLDistributionPoints ::= SEQUENCE SIZE (1..MAX) OF DistributionPoint + */ + ret = mbedtls_asn1_get_tag(&p, end_ext_octet, &len, MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE); + if (ret) { + ERR(NULL, "Failed to parse CRL distribution points extension (%s).", nc_get_mbedtls_str_err(ret)); + goto cleanup; + } + if (p + len != end_ext_octet) { + /* length mismatch */ + ERR(NULL, "Failed to parse CRL distribution points extension (%s).", nc_get_mbedtls_str_err(ret)); + goto cleanup; + } else if (!len) { + /* empty sequence, but size is 1..max */ + ERR(NULL, "Failed to parse CRL distribution points extension (empty sequence)."); + goto cleanup; + } + + /* parse the distribution points and obtain the uris */ + ret = nc_server_tls_parse_crl_dist_points(&p, len, uris, uri_count); + if (ret) { + goto cleanup; + } + } + } + +cleanup: + return ret; +} + +int +nc_tls_process_cipher_suite_wrap(const char *cipher, char **out) +{ + const char *begin, *ptr; + + /* check if it's a TLS 1.3 cipher suite */ + if (!strcmp(cipher, "tls-aes-256-gcm-sha384") || !strcmp(cipher, "tls-aes-128-gcm-sha256") || + !strcmp(cipher, "tls-chacha20-poly1305-sha256") || !strcmp(cipher, "tls-aes-128-ccm-sha256") || + !strcmp(cipher, "tls-aes-128-ccm-8-sha256")) { + /* + 3 because mbedtls has "TLS1-3" prefix for 1.3 suites */ + *out = malloc(strlen(cipher) + 3 + 1); + NC_CHECK_ERRMEM_RET(!*out, 1); + sprintf(*out, "TLS1-3"); + begin = cipher + 4; + } else { + *out = malloc(strlen(cipher) + 1); + NC_CHECK_ERRMEM_RET(!*out, 1); + begin = cipher; + } + + /* convert to uppercase */ + for (ptr = begin; *ptr; ptr++) { + (*out)[ptr - begin] = toupper(*ptr); + } + + (*out)[ptr - begin] = '\0'; + return 0; +} + +int +nc_tls_append_cipher_suite_wrap(struct nc_server_tls_opts *opts, const char *cipher_suite) +{ + int cipher_id; + + cipher_id = mbedtls_ssl_get_ciphersuite_id(cipher_suite); + if (!cipher_id) { + return 1; + } + + /* append the cipher suite to a zero terminated array */ + if (!opts->ciphers) { + /* first entry, account for terminating 0 */ + opts->ciphers = malloc(2 * sizeof *opts->ciphers); + NC_CHECK_ERRMEM_RET(!opts->ciphers, 1); + ((int *)opts->ciphers)[0] = cipher_id; + opts->cipher_count = 1; + } else { + /* +2 because of terminating 0 */ + opts->ciphers = nc_realloc(opts->ciphers, (opts->cipher_count + 2) * sizeof *opts->ciphers); + NC_CHECK_ERRMEM_RET(!opts->ciphers, 1); + ((int *)opts->ciphers)[opts->cipher_count] = cipher_id; + opts->cipher_count++; + } + + /* terminate the array */ + ((int *)opts->ciphers)[opts->cipher_count] = 0; + return 0; +} + +void +nc_server_tls_set_cipher_suites_wrap(void *tls_cfg, void *cipher_suites) +{ + mbedtls_ssl_conf_ciphersuites(tls_cfg, cipher_suites); +} + +time_t +nc_tls_get_cert_exp_time_wrap(void *cert) +{ + struct tm t = {0}; + mbedtls_x509_time *valid_to; + + valid_to = &((mbedtls_x509_crt *)cert)->valid_to; + + t.tm_sec = valid_to->sec; + t.tm_min = valid_to->min; + t.tm_hour = valid_to->hour; + + t.tm_mday = valid_to->day; + t.tm_mon = valid_to->mon - 1; + t.tm_year = valid_to->year - 1900; + + /* let system figure out the DST */ + t.tm_isdst = -1; + + return timegm(&t); +} + +/** + * @brief Convert the MbedTLS key export type to a label for the keylog file. + * + * @param[in] type MbedTLS key export type. + * @return Label for the keylog file or NULL if the type is not supported. + */ +static const char * +nc_tls_keylog_type2label(mbedtls_ssl_key_export_type type) +{ + switch (type) { + case MBEDTLS_SSL_KEY_EXPORT_TLS12_MASTER_SECRET: + return "CLIENT_RANDOM"; +#ifdef MBEDTLS_SSL_PROTO_TLS1_3 + case MBEDTLS_SSL_KEY_EXPORT_TLS1_3_CLIENT_HANDSHAKE_TRAFFIC_SECRET: + return "CLIENT_HANDSHAKE_TRAFFIC_SECRET"; + case MBEDTLS_SSL_KEY_EXPORT_TLS1_3_SERVER_HANDSHAKE_TRAFFIC_SECRET: + return "SERVER_HANDSHAKE_TRAFFIC_SECRET"; + case MBEDTLS_SSL_KEY_EXPORT_TLS1_3_CLIENT_APPLICATION_TRAFFIC_SECRET: + return "CLIENT_TRAFFIC_SECRET_0"; + case MBEDTLS_SSL_KEY_EXPORT_TLS1_3_SERVER_APPLICATION_TRAFFIC_SECRET: + return "SERVER_TRAFFIC_SECRET_0"; +#endif + default: + return NULL; + } +} + +/** + * @brief Callback for writing a line in the keylog file. + */ +static void +nc_tls_keylog_write_line(void *UNUSED(p_expkey), mbedtls_ssl_key_export_type type, const unsigned char *secret, + size_t secret_len, const unsigned char client_random[32], + const unsigned char UNUSED(server_random[32]), mbedtls_tls_prf_types UNUSED(tls_prf_type)) +{ + size_t linelen, len = 0, i, client_random_len; + char buf[256]; + const char *label; + + if (!server_opts.tls_keylog_file) { + return; + } + + label = nc_tls_keylog_type2label(type); + if (!label) { + /* type not supported */ + return; + } + + /*