Projects
Eulaceura:Factory
netopeer2
_service:obs_scm:netopeer2-1664351877.ff86482.o...
Sign Up
Log In
Username
Password
Overview
Repositories
Revisions
Requests
Users
Attributes
Meta
File _service:obs_scm:netopeer2-1664351877.ff86482.obscpio of Package netopeer2
07070100000000000081A40000000000000000000000016333FE8500000209000000000000000000000000000000000000002A00000000netopeer2-1664351877.ff86482/README.en.md# netopeer2 Netopeer2 is a server for implementing network configuration management based on the NETCONF Protocol. This is the second generation, originally available as the [Netopeer project](https://github.com/CESNET/netopeer). Netopeer2 is based on the new generation of the NETCONF and YANG libraries - [libyang](https://github.com/CESNET/libyang) and [libnetconf2](https://github.com/CESNET/libnetconf2). The Netopeer2 server uses [sysrepo](https://github.com/sysrepo/sysrepo) as a NETCONF datastore implementation. 07070100000001000081A40000000000000000000000016333FE85000001CB000000000000000000000000000000000000002700000000netopeer2-1664351877.ff86482/README.md# netopeer2 Netopeer2 是一个实现基于网络配置管理的服务器 关于 NETCONF 协议。这是第二代,原本 可用作 [Netopeer 项目](https://github.com/CESNET/netopeer)。网络对等2 基于新一代的 NETCONF 和 YANG 库 - [libyang](https://github.com/CESNET/libyang) 和 [libnetconf2](https://github.com/CESNET/libnetconf2)。 Netopeer2 服务器使用 [sysrepo](https://github.com/sysrepo/sysrepo) 作为 NETCONF 数据存储实现。 07070100000002000081A40000000000000000000000016333FE85000F7800000000000000000000000000000000000000003500000000netopeer2-1664351877.ff86482/netopeer2-1.1.70.tar.gznetopeer2-1.1.70/0000775000000000000000000000000014021433500012173 5ustar rootrootnetopeer2-1.1.70/CMakeLists.txt0000664000000000000000000002473614021433500014747 0ustar rootrootcmake_minimum_required(VERSION 2.8.12) project(netopeer2 C) set(NETOPEER2_DESC "NETCONF tools suite including a server and command-line client") # include custom Modules set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/CMakeModules/") if(POLICY CMP0075) cmake_policy(SET CMP0075 NEW) endif() include(GNUInstallDirs) include(CheckFunctionExists) include(CheckIncludeFile) include(UseCompat) # check the supported platform if(NOT UNIX) message(FATAL_ERROR "Only *nix like systems are supported.") endif() # set default build type if not specified by user if(NOT CMAKE_BUILD_TYPE) set(CMAKE_BUILD_TYPE debug) endif() add_compile_options(-Wall -Wextra -std=gnu99) set(CMAKE_C_FLAGS_RELEASE "-O2 -DNDEBUG") set(CMAKE_C_FLAGS_PACKAGE "-g -O2 -DNDEBUG") set(CMAKE_C_FLAGS_DEBUG "-g -O0 -DDEBUG") # 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(NP2SRV_VERSION 1.1.70) # libyang required SO version set(LIBYANG_DEP_SOVERSION_MAJOR 1) # build options if(CMAKE_BUILD_TYPE STREQUAL debug) option(BUILD_TESTS "Build tests" ON) option(VALGRIND_TESTS "Build tests with valgrind" ON) else() option(BUILD_TESTS "Build tests" OFF) option(VALGRIND_TESTS "Build tests with valgrind" OFF) endif() option(BUILD_CLI "Build and install neotpeer2-cli" ON) option(ENABLE_URL "Enable URL capability" ON) set(THREAD_COUNT 5 CACHE STRING "Number of threads accepting new sessions and handling requests") set(NACM_RECOVERY_UID 0 CACHE STRING "NACM recovery session UID that has unrestricted access") set(POLL_IO_TIMEOUT 10 CACHE STRING "Timeout in milliseconds of polling sessions for new data. It is also used for synchronization of low level IO such as sending a reply while a notification is being sent") set(YANG_MODULE_DIR "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_DATADIR}/yang/modules/netopeer2" CACHE STRING "Directory where to copy the YANG modules to") # script options option(INSTALL_MODULES "Install required modules into sysrepo" ON) option(GENERATE_HOSTKEY "Generate a new RSA host key in the keystore named \"genkey\"" ON) option(MERGE_LISTEN_CONFIG "Merge default server configuration for listening on all IPv4 interfaces" ON) set(MODULES_PERMS 600 CACHE STRING "File access permissions set for all the server modules") if(NOT MODULES_OWNER) execute_process(COMMAND id -un RESULT_VARIABLE RET OUTPUT_VARIABLE MODULES_OWNER OUTPUT_STRIP_TRAILING_WHITESPACE ERROR_VARIABLE ERROR_STR OUTPUT_STRIP_TRAILING_WHITESPACE) if(RET) message(WARNING "Learning server module user failed (${ERROR_STR}), the current user will be used.") endif() endif() set(MODULES_OWNER "${MODULES_OWNER}" CACHE STRING "System user that will become the owner of server modules, empty means the current user") if(NOT MODULES_GROUP AND MODULES_OWNER) execute_process(COMMAND id -gn ${MODULES_OWNER} RESULT_VARIABLE RET OUTPUT_VARIABLE MODULES_GROUP OUTPUT_STRIP_TRAILING_WHITESPACE ERROR_VARIABLE ERROR_STR OUTPUT_STRIP_TRAILING_WHITESPACE) if(RET) message(WARNING "Learning server module group failed (${ERROR_STR}), the current user group will be used.") endif() endif() set(MODULES_GROUP "${MODULES_GROUP}" CACHE STRING "System group that the server modules will belong to, empty means the current user group") # set prefix for the PID file if(NOT PIDFILE_PREFIX) set(PIDFILE_PREFIX "/var/run") endif() set(NP2SRV_SSH_AUTHORIZED_KEYS_PATTERN "%s/.ssh/authorized_keys" CACHE STRING "printf-like pattern for determining path to users' SSH authorized_keys file. Must contain exactly one '%s'.") set(NP2SRV_SSH_AUTHORIZED_KEYS_ARG_IS_USERNAME 0 CACHE STRING "If true, replace '%s' by username. If not set, replace '%s' by home directory. By default, unset.") if(NOT NP2SRV_SSH_AUTHORIZED_KEYS_PATTERN MATCHES "^[^%]*%s[^%]*$") message(FATAL_ERROR "Wrong format string given for NP2SRV_SSH_AUTHORIZED_KEYS_PATTERN: exactly one '%s' expected.") endif() # check that lnc2 supports np2srv thread count find_package(PkgConfig) if(PKG_CONFIG_FOUND) execute_process(COMMAND ${PKG_CONFIG_EXECUTABLE} "--variable=LNC2_MAX_THREAD_COUNT" "libnetconf2" OUTPUT_VARIABLE LNC2_THREAD_COUNT) if(LNC2_THREAD_COUNT) string(STRIP ${LNC2_THREAD_COUNT} LNC2_THREAD_COUNT) if(LNC2_THREAD_COUNT LESS THREAD_COUNT) message(FATAL_ERROR "libnetconf2 was compiled with support up to ${LNC2_THREAD_COUNT} threads, server is configured with ${THREAD_COUNT}.") else() message(STATUS "libnetconf2 was compiled with support of up to ${LNC2_THREAD_COUNT} threads") endif() else() message(STATUS "Unable to learn libnetconf2 thread support, check skipped") endif() else() message(STATUS "pkg-config not found, so it was not possible to check if libnetconf2 supports ${THREAD_COUNT} threads") endif() # put all binaries into one directory (even from subprojects) set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}) # dependencies - OpenSSL (required by later libnetconf2 checks and not really the server itself) find_package(OpenSSL) if(OPENSSL_FOUND) list(APPEND CMAKE_REQUIRED_INCLUDES ${OPENSSL_INCLUDE_DIRS}) list(APPEND CMAKE_REQUIRED_LIBRARIES ${OPENSSL_LIBRARIES}) endif() # dependencies - libssh (also required by libnetconf2 checks) find_package(LibSSH 0.7.1) if(LIBSSH_FOUND) list(APPEND CMAKE_REQUIRED_INCLUDES ${LIBSSH_INCLUDE_DIRS}) list(APPEND CMAKE_REQUIRED_LIBRARIES ${LIBSSH_LIBRARIES}) endif() # dependencies - libnetconf2 (now, because we need to configure outselves based on it) find_package(LibNETCONF2 REQUIRED) include_directories(${LIBNETCONF2_INCLUDE_DIRS}) list(APPEND CMAKE_REQUIRED_INCLUDES ${LIBNETCONF2_INCLUDE_DIRS}) list(APPEND CMAKE_REQUIRED_LIBRARIES ${LIBNETCONF2_LIBRARIES}) # source files set(SERVER_SRC src/common.c src/netconf.c src/netconf_monitoring.c src/netconf_acm.c src/netconf_nmda.c src/log.c) # at least some remote transport is enabled if(LIBNETCONF2_ENABLED_SSH OR LIBNETCONF2_ENABLED_TLS) list(APPEND SERVER_SRC src/netconf_server.c) endif() # SSH is enabled if(LIBNETCONF2_ENABLED_SSH) list(APPEND SERVER_SRC src/netconf_server_ssh.c) endif() # TLS is enabled if(LIBNETCONF2_ENABLED_TLS) list(APPEND SERVER_SRC src/netconf_server_tls.c) endif() # link compat use_compat() # netopeer2-server add_library(serverobj OBJECT ${SERVER_SRC}) add_executable(netopeer2-server $<TARGET_OBJECTS:serverobj> src/main.c $<TARGET_OBJECTS:compat>) # dependencies - libnetconf2 (was already found) target_link_libraries(netopeer2-server ${LIBNETCONF2_LIBRARIES}) # dependencies - libssh (was already found, if exists) if(LIBSSH_FOUND AND LIBNETCONF2_ENABLED_SSH) target_link_libraries(netopeer2-server ${LIBSSH_LIBRARIES}) include_directories(${LIBSSH_INCLUDE_DIRS}) endif() # dependencies - libcurl if(ENABLE_URL) find_package(CURL) if(CURL_FOUND) include_directories(${CURL_INCLUDE_DIRS}) target_link_libraries(netopeer2-server ${CURL_LIBRARIES}) set(NP2SRV_URL_CAPAB 1) else() message(STATUS "libcurl not found, url capability will not be supported") unset(NP2SRV_URL_CAPAB) endif() endif() # dependencies - pthread set(CMAKE_THREAD_PREFER_PTHREAD TRUE) find_package(Threads REQUIRED) target_link_libraries(netopeer2-server ${CMAKE_THREAD_LIBS_INIT}) list(APPEND CMAKE_REQUIRED_FLAGS ${CMAKE_THREAD_LIBS_INIT}) # dependencies - stdatomic check_include_file(stdatomic.h HAVE_STDATOMIC) # dependencies - libyang find_package(LibYANG REQUIRED) target_link_libraries(netopeer2-server ${LIBYANG_LIBRARIES}) include_directories(${LIBYANG_INCLUDE_DIRS}) list(APPEND CMAKE_REQUIRED_INCLUDES ${LIBYANG_INCLUDE_DIRS}) list(APPEND CMAKE_REQUIRED_LIBRARIES ${LIBYANG_LIBRARIES}) # dependencies - sysrepo find_package(Sysrepo REQUIRED) target_link_libraries(netopeer2-server ${SYSREPO_LIBRARIES}) include_directories(${SYSREPO_INCLUDE_DIRS}) list(APPEND CMAKE_REQUIRED_INCLUDES ${SYSREPO_INCLUDE_DIRS}) list(APPEND CMAKE_REQUIRED_LIBRARIES ${SYSREPO_LIBRARIES}) # generate files configure_file("${PROJECT_SOURCE_DIR}/src/config.h.in" "${PROJECT_BINARY_DIR}/config.h" ESCAPE_QUOTES @ONLY) include_directories(${PROJECT_BINARY_DIR}) # set script dir set(SCRIPT_DIR "${PROJECT_SOURCE_DIR}/scripts/") # packages add_subdirectory(packages) # install the module files install(DIRECTORY "${PROJECT_SOURCE_DIR}/modules/" DESTINATION ${YANG_MODULE_DIR}) # install the binary, required modules, and default configuration install(TARGETS netopeer2-server DESTINATION ${CMAKE_INSTALL_BINDIR}) if(INSTALL_MODULES) install(CODE " message(STATUS \"Installing missing sysrepo modules...\") set(ENV{NP2_MODULE_DIR} \"${YANG_MODULE_DIR}\") set(ENV{NP2_MODULE_PERMS} \"${MODULES_PERMS}\") set(ENV{NP2_MODULE_OWNER} \"${MODULES_OWNER}\") set(ENV{NP2_MODULE_GROUP} \"${MODULES_GROUP}\") execute_process(COMMAND \"${SCRIPT_DIR}/setup.sh\" RESULT_VARIABLE SETUP_RES) if(NOT SETUP_RES EQUAL \"0\") message(FATAL_ERROR \" scripts/setup.sh failed: \${SETUP_RES}\") endif() ") else() message(WARNING "Server will refuse to start if the modules are not installed!") endif() if(GENERATE_HOSTKEY) install(CODE " message(STATUS \"Generating a new RSA host key \\\"genkey\\\" if not already added...\") execute_process(COMMAND ${SCRIPT_DIR}/merge_hostkey.sh RESULT_VARIABLE MERGE_HOSTKEY_RES) if(NOT MERGE_HOSTKEY_RES EQUAL \"0\") message(FATAL_ERROR \" scripts/merge_hostkey.sh failed: \${MERGE_HOSTKEY_RES}\") endif() ") endif() if(MERGE_LISTEN_CONFIG) install(CODE " message(STATUS \"Merging default server listen configuration if there is none...\") execute_process(COMMAND ${SCRIPT_DIR}/merge_config.sh RESULT_VARIABLE MERGE_CONFIG_RES) if(NOT MERGE_CONFIG_RES EQUAL \"0\") message(FATAL_ERROR \" scripts/merge_config.sh failed: \${MERGE_CONFIG_RES}\") endif() ") endif() # cli if(BUILD_CLI) add_subdirectory(cli) endif() # 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} ) # uninstall add_custom_target(uninstall "${CMAKE_COMMAND}" -P "${CMAKE_MODULE_PATH}/uninstall.cmake") netopeer2-1.1.70/CMakeModules/0000775000000000000000000000000014021433500014504 5ustar rootrootnetopeer2-1.1.70/CMakeModules/FindCMocka.cmake0000664000000000000000000000272614021433500017453 0ustar rootroot# - Try to find CMocka # Once done this will define # # CMOCKA_ROOT_DIR - Set this variable to the root installation of CMocka # # Read-Only variables: # CMOCKA_FOUND - system has CMocka # CMOCKA_INCLUDE_DIR - the CMocka include directory # CMOCKA_LIBRARIES - Link these to use CMocka # CMOCKA_DEFINITIONS - Compiler switches required for using CMocka # #============================================================================= # Copyright (c) 2011-2012 Andreas Schneider <asn@cryptomilk.org> # # Distributed under the OSI-approved BSD License (the "License"); # see accompanying file Copyright.txt for details. # # This software is distributed WITHOUT ANY WARRANTY; without even the # implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. # See the License for more information. #============================================================================= # find_path(CMOCKA_INCLUDE_DIR NAMES cmocka.h PATHS ${CMOCKA_ROOT_DIR}/include ) find_library(CMOCKA_LIBRARY NAMES cmocka PATHS ${CMOCKA_ROOT_DIR}/include ) if (CMOCKA_LIBRARY) set(CMOCKA_LIBRARIES ${CMOCKA_LIBRARIES} ${CMOCKA_LIBRARY} ) endif (CMOCKA_LIBRARY) include(FindPackageHandleStandardArgs) find_package_handle_standard_args(CMocka DEFAULT_MSG CMOCKA_LIBRARIES CMOCKA_INCLUDE_DIR) # show the CMOCKA_INCLUDE_DIR and CMOCKA_LIBRARIES variables only in the advanced view mark_as_advanced(CMOCKA_INCLUDE_DIR CMOCKA_LIBRARIES) netopeer2-1.1.70/CMakeModules/FindLibNETCONF2.cmake0000664000000000000000000000645514021433500020126 0ustar rootroot# - Try to find LibNETCONF2 # Once done this will define # # LIBNETCONF2_FOUND - system has LibNETCONF2 # LIBNETCONF2_INCLUDE_DIRS - the LibNETCONF2 include directory # LIBNETCONF2_LIBRARIES - Link these to use LibNETCONF2 # LIBNETCONF2_ENABLED_SSH - LibNETCONF2 was compiled with SSH support # LIBNETCONF2_ENABLED_TLS - LibNETCONF2 was compiled with TLS support # # Author Michal Vasko <mvasko@cesnet.cz> # Copyright (c) 2020 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) include(CheckSymbolExists) if(LIBNETCONF2_LIBRARIES AND LIBNETCONF2_INCLUDE_DIRS) # in cache already set(LIBNETCONF2_FOUND TRUE) else() find_path(LIBNETCONF2_INCLUDE_DIR NAMES nc_client.h nc_server.h PATHS /usr/include /usr/local/include /opt/local/include /sw/include ${CMAKE_INCLUDE_PATH} ${CMAKE_INSTALL_PREFIX}/include ) find_library(LIBNETCONF2_LIBRARY NAMES netconf2 libnetconf2 PATHS /usr/lib /usr/lib64 /usr/local/lib /usr/local/lib64 /opt/local/lib /sw/lib ${CMAKE_LIBRARY_PATH} ${CMAKE_INSTALL_PREFIX}/lib ) 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) # check the configured options and make them available through cmake list(INSERT CMAKE_REQUIRED_INCLUDES 0 "${LIBNETCONF2_INCLUDE_DIR}") check_symbol_exists("NC_ENABLED_SSH" "nc_client.h" LIBNETCONF2_ENABLED_SSH) check_symbol_exists("NC_ENABLED_TLS" "nc_client.h" LIBNETCONF2_ENABLED_TLS) list(REMOVE_AT CMAKE_REQUIRED_INCLUDES 0) endif() netopeer2-1.1.70/CMakeModules/FindLibSSH.cmake0000664000000000000000000001122414021433500017373 0ustar rootroot# - Try to find LibSSH # Once done this will define # # LIBSSH_FOUND - system has LibSSH # LIBSSH_INCLUDE_DIRS - the LibSSH include directory # LIBSSH_LIBRARIES - link these to use LibSSH # LIBSSH_VERSION - # # Author Michal Vasko <mvasko@cesnet.cz> # Copyright (c) 2020 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(LIBSSH_LIBRARIES AND LIBSSH_INCLUDE_DIRS) # in cache already set(LIBSSH_FOUND TRUE) else() find_path(LIBSSH_INCLUDE_DIR NAMES libssh/libssh.h PATHS /usr/include /usr/local/include /opt/local/include /sw/include ${CMAKE_INCLUDE_PATH} ${CMAKE_INSTALL_PREFIX}/include ) find_library(LIBSSH_LIBRARY NAMES ssh.so libssh.so libssh.dylib PATHS /usr/lib /usr/local/lib /opt/local/lib /sw/lib ${CMAKE_LIBRARY_PATH} ${CMAKE_INSTALL_PREFIX}/lib ) if(LIBSSH_INCLUDE_DIR AND LIBSSH_LIBRARY) # learn libssh version if(EXISTS ${LIBSSH_INCLUDE_DIR}/libssh/libssh_version.h) set(LIBSSH_HEADER_PATH ${LIBSSH_INCLUDE_DIR}/libssh/libssh_version.h) else() set(LIBSSH_HEADER_PATH ${LIBSSH_INCLUDE_DIR}/libssh/libssh.h) endif() file(STRINGS ${LIBSSH_HEADER_PATH} LIBSSH_VERSION_MAJOR REGEX "#define[ ]+LIBSSH_VERSION_MAJOR[ ]+[0-9]+") if(NOT LIBSSH_VERSION_MAJOR) message(STATUS "LIBSSH_VERSION_MAJOR not found, assuming libssh is too old and cannot be used!") set(LIBSSH_INCLUDE_DIR "LIBSSH_INCLUDE_DIR-NOTFOUND") set(LIBSSH_LIBRARY "LIBSSH_LIBRARY-NOTFOUND") else() string(REGEX MATCH "[0-9]+" LIBSSH_VERSION_MAJOR ${LIBSSH_VERSION_MAJOR}) file(STRINGS ${LIBSSH_HEADER_PATH} LIBSSH_VERSION_MINOR REGEX "#define[ ]+LIBSSH_VERSION_MINOR[ ]+[0-9]+") string(REGEX MATCH "[0-9]+" LIBSSH_VERSION_MINOR ${LIBSSH_VERSION_MINOR}) file(STRINGS ${LIBSSH_HEADER_PATH} LIBSSH_VERSION_PATCH REGEX "#define[ ]+LIBSSH_VERSION_MICRO[ ]+[0-9]+") string(REGEX MATCH "[0-9]+" LIBSSH_VERSION_PATCH ${LIBSSH_VERSION_PATCH}) set(LIBSSH_VERSION ${LIBSSH_VERSION_MAJOR}.${LIBSSH_VERSION_MINOR}.${LIBSSH_VERSION_PATCH}) if(LIBSSH_VERSION VERSION_LESS 0.8.0) # libssh_threads also needs to be linked for these versions string(REPLACE "libssh.so" "libssh_threads.so" LIBSSH_THREADS_LIBRARY ${LIBSSH_LIBRARY} ) string(REPLACE "libssh.dylib" "libssh_threads.dylib" LIBSSH_THREADS_LIBRARY ${LIBSSH_THREADS_LIBRARY} ) string(REPLACE "ssh.so" "ssh_threads.so" LIBSSH_THREADS_LIBRARY ${LIBSSH_THREADS_LIBRARY} ) endif() endif() endif() set(LIBSSH_INCLUDE_DIRS ${LIBSSH_INCLUDE_DIR}) set(LIBSSH_LIBRARIES ${LIBSSH_LIBRARY} ${LIBSSH_THREADS_LIBRARY}) mark_as_advanced(LIBSSH_INCLUDE_DIRS LIBSSH_LIBRARIES) find_package_handle_standard_args(LibSSH FOUND_VAR LIBSSH_FOUND REQUIRED_VARS LIBSSH_INCLUDE_DIRS LIBSSH_LIBRARIES VERSION_VAR LIBSSH_VERSION) endif() netopeer2-1.1.70/CMakeModules/FindLibYANG.cmake0000664000000000000000000001077714021433500017510 0ustar rootroot# - Try to find LibYANG # Once done this will define # # LIBYANG_FOUND - system has LibYANG # LIBYANG_INCLUDE_DIRS - the LibYANG include directory # LIBYANG_LIBRARIES - Link these to use LibYANG # LIBYANG_VERSION - SO version of the found libyang library # # LIBYANG_CPP_FOUND - system has LibYANG C++ bindings # LIBYANG_CPP_INCLUDE_DIRS - the LibYANG C++ include directory # LIBYANG_CPP_LIBRARIES - Link these to use LibYANG C++ bindings # # Author Radek Krejci <rkrejci@cesnet.cz> # Copyright (c) 2015 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(LIBYANG_LIBRARIES AND LIBYANG_INCLUDE_DIRS) # in cache already set(LIBYANG_FOUND TRUE) else() find_path(LIBYANG_INCLUDE_DIR NAMES libyang/libyang.h PATHS /usr/include /usr/local/include /opt/local/include /sw/include ${CMAKE_INCLUDE_PATH} ${CMAKE_INSTALL_PREFIX}/include ) find_library(LIBYANG_LIBRARY NAMES yang libyang PATHS /usr/lib /usr/lib64 /usr/local/lib /usr/local/lib64 /opt/local/lib /sw/lib ${CMAKE_LIBRARY_PATH} ${CMAKE_INSTALL_PREFIX}/lib ) if(LIBYANG_INCLUDE_DIR) find_path(LY_HEADER_PATH "libyang/libyang.h" HINTS ${LIBYANG_INCLUDE_DIR}) file(READ "${LY_HEADER_PATH}/libyang/libyang.h" LY_HEADER) string(REGEX MATCH "#define LY_VERSION \"[0-9]+\\.[0-9]+\\.[0-9]+\"" LY_VERSION_MACRO "${LY_HEADER}") string(REGEX MATCH "[0-9]+\\.[0-9]+\\.[0-9]+" LIBYANG_VERSION "${LY_VERSION_MACRO}") endif() set(LIBYANG_INCLUDE_DIRS ${LIBYANG_INCLUDE_DIR}) set(LIBYANG_LIBRARIES ${LIBYANG_LIBRARY}) mark_as_advanced(LIBYANG_INCLUDE_DIRS LIBYANG_LIBRARIES) # handle the QUIETLY and REQUIRED arguments and set SYSREPO_FOUND to TRUE # if all listed variables are TRUE find_package_handle_standard_args(LibYANG FOUND_VAR LIBYANG_FOUND REQUIRED_VARS LIBYANG_LIBRARY LIBYANG_INCLUDE_DIR VERSION_VAR LIBYANG_VERSION) endif() #C++ bindings if (LIBYANG_CPP_LIBRARIES AND LIBYANG_CPP_INCLUDE_DIRS) # in cache already set(LIBYANG_CPP_FOUND TRUE) else () find_path(LIBYANG_CPP_INCLUDE_DIR NAMES libyang/Libyang.hpp PATHS /usr/include /usr/local/include /opt/local/include /sw/include ${CMAKE_INCLUDE_PATH} ${CMAKE_INSTALL_PREFIX}/include ) find_library(LIBYANG_CPP_LIBRARY NAMES yang-cpp libyang-cpp PATHS /usr/lib /usr/lib64 /usr/local/lib /usr/local/lib64 /opt/local/lib /sw/lib ${CMAKE_LIBRARY_PATH} ${CMAKE_INSTALL_PREFIX}/lib ) if (LIBYANG_CPP_INCLUDE_DIR AND LIBYANG_CPP_LIBRARY) set(LIBYANG_CPP_FOUND TRUE) else () set(LIBYANG_CPP_FOUND FALSE) endif () set(LIBYANG_CPP_INCLUDE_DIRS ${LIBYANG_CPP_INCLUDE_DIR}) set(LIBYANG_CPP_LIBRARIES ${LIBYANG_CPP_LIBRARY}) # show the LIBYANG_CPP_INCLUDE_DIRS and LIBYANG_CPP_LIBRARIES variables only in the advanced view mark_as_advanced(LIBYANG_CPP_INCLUDE_DIRS LIBYANG_CPP_LIBRARIES) endif() netopeer2-1.1.70/CMakeModules/FindSysrepo.cmake0000664000000000000000000000510214021433500017751 0ustar rootroot# Find Sysrepo # Once done, it will define # # SYSREPO_FOUND - System has SYSREPO # SYSREPO_INCLUDE_DIRS - The SYSREPO include directories # SYSREPO_LIBRARIES - The libraries needed to use SYSREPO # # Author Michal Vasko <mvasko@cesnet.cz> # Copyright (c) 2020 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(SYSREPO_LIBRARIES AND SYSREPO_INCLUDE_DIRS) # in cache already set(SYSREPO_FOUND TRUE) else() find_path(SYSREPO_INCLUDE_DIR NAMES sysrepo.h PATHS /usr/include /usr/local/include /opt/local/include /sw/include ${CMAKE_INCLUDE_PATH} ${CMAKE_INSTALL_PREFIX}/include ) find_library(SYSREPO_LIBRARY NAMES sysrepo libsysrepo PATHS /usr/lib /usr/lib64 /usr/local/lib /usr/local/lib64 /opt/local/lib /sw/lib ${CMAKE_LIBRARY_PATH} ${CMAKE_INSTALL_PREFIX}/lib ) set(SYSREPO_INCLUDE_DIRS ${SYSREPO_INCLUDE_DIR}) set(SYSREPO_LIBRARIES ${SYSREPO_LIBRARY}) mark_as_advanced(SYSREPO_INCLUDE_DIRS SYSREPO_LIBRARIES) # handle the QUIETLY and REQUIRED arguments and set SYSREPO_FOUND to TRUE # if all listed variables are TRUE find_package_handle_standard_args(Sysrepo DEFAULT_MSG SYSREPO_LIBRARY SYSREPO_INCLUDE_DIR) endif() netopeer2-1.1.70/CMakeModules/UseCompat.cmake0000664000000000000000000000216414021433500017411 0ustar rootrootinclude(CheckSymbolExists) include(TestBigEndian) macro(USE_COMPAT) # compatibility checks set(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) check_symbol_exists(vdprintf "stdio.h;stdarg.h" HAVE_VDPRINTF) check_symbol_exists(asprintf "stdio.h" HAVE_ASPRINTF) check_symbol_exists(vasprintf "stdio.h" HAVE_VASPRINTF) check_symbol_exists(get_current_dir_name "unistd.h" HAVE_GET_CURRENT_DIR_NAME) check_symbol_exists(strndup "string.h" HAVE_STRNDUP) check_symbol_exists(getline "stdio.h" HAVE_GETLINE) check_symbol_exists(strdupa "string.h" HAVE_STRDUPA) check_symbol_exists(strchrnul "string.h" HAVE_STRCHRNUL) TEST_BIG_ENDIAN(IS_BIG_ENDIAN) # header and object file configure_file(${PROJECT_SOURCE_DIR}/compat/compat.h.in ${PROJECT_BINARY_DIR}/compat.h @ONLY) include_directories(${PROJECT_BINARY_DIR}) add_library(compat OBJECT ${PROJECT_SOURCE_DIR}/compat/compat.c) set_property(TARGET compat PROPERTY POSITION_INDEPENDENT_CODE ON) endmacro() netopeer2-1.1.70/CMakeModules/uninstall.cmake0000664000000000000000000000133714021433500017523 0ustar rootrootcmake_minimum_required(VERSION 3.0.2) set(MANIFEST "${CMAKE_CURRENT_BINARY_DIR}/install_manifest.txt") if(NOT EXISTS ${MANIFEST}) message(FATAL_ERROR "Cannot find install manifest: ${MANIFEST}") endif() file(STRINGS ${MANIFEST} files) foreach(file ${files}) if(EXISTS ${file} OR IS_SYMLINK ${file}) message(STATUS "Removing: ${file}") execute_process(COMMAND rm -f ${file} RESULT_VARIABLE result OUTPUT_QUIET ERROR_VARIABLE stderr ERROR_STRIP_TRAILING_WHITESPACE ) if(NOT ${result} EQUAL 0) message(FATAL_ERROR "${stderr}") endif() else() message(STATUS "Does-not-exist: ${file}") endif() endforeach(file) netopeer2-1.1.70/CODINGSTYLE.md0000664000000000000000000001125314021433500014303 0ustar rootroot# Netopeer2 Coding Style This file describes the coding style used in most C files in the Netopeer2 tools. ## Basics - Use space instead of tabs for indentations. - There is no strict limit for the line length, However, try to keep lines in a reasonable length (120 characters). - Avoid trailing spaces on lines. - Put one blank line between function definitions. - Don't mix declarations and code within a block. Similarly, don't use declarations in iteration statements. ## Naming Use underscores to separate words in an identifier: `multi_word_name`. Use lowercase for most names. Use uppercase for macros, macro parameters and members of enumerations. Do not use names that begin with `_`. If you need a name for "internal use only", use `__` as a suffix instead of a prefix. ## Comments Avoid `//` comments. Use `/* ... */` comments, write block comments with the leading asterisk on each line. You may put the `/*` and `*/` on the same line as comment text if you prefer. ```c /* * comment text */ ``` ## Functions Put the return type, function name, and the braces that surround the function's code on separate lines, all starting in column 0. ```c static int foo(int arg) { ... } ``` When you need to put the function parameters on multiple lines, start new line at column after the opening parenthesis from the initial line. ```c static int my_function(struct my_struct *p1, struct another_struct *p2, int size) { ... } ``` In the absence of good reasons for another order, the following parameter order is preferred. One notable exception is that data parameters and their corresponding size parameters should be paired. 1. The primary object being manipulated, if any (equivalent to the "this" pointer in C++). 2. Input-only parameters. 3. Input/output parameters. 4. Output-only parameters. 5. Status parameter. Functions that destroy an instance of a dynamically-allocated type should accept and ignore a null pointer argument. Code that calls such a function (including the C standard library function `free()`) should omit a null-pointer check. We find that this usually makes code easier to read. ### Function Prototypes Put the return type and function name on the same line in a function prototype: ```c static const struct int foo(int arg); ``` ## Statements - Indent each level of code with 4 spaces. - Put single space between `if`, `while`, `for`, etc. statements and the expression that follow them. On the other hand, function calls has no space between the function name and opening parenthesis. - Opening code block brace is kept at the same line with the `if`, `while`, `for` or `switch` statements. ```c if (a) { x = exp(a); } else { return 1; } ``` - Start switch's cases at the same column as the switch. ```c switch (conn->state) { case 0: return "data found"; case 1: return "data not found"; default: return "unknown error"; } ``` - Do not put gratuitous parentheses around the expression in a return statement, that is, write `return 0;` and not `return(0);` ## Types Use typedefs sparingly. Code is clearer if the actual type is visible at the point of declaration. Do not, in general, declare a typedef for a struct, union, or enum. Do not declare a typedef for a pointer type, because this can be very confusing to the reader. Use the `int<N>_t` and `uint<N>_t` types from `<stdint.h>` for exact-width integer types. Use the `PRId<N>`, `PRIu<N>`, and `PRIx<N>` macros from `<inttypes.h>` for formatting them with `printf()` and related functions. Pointer declarators bind to the variable name, not the type name. Write `int *x`, not `int* x` and definitely not `int * x`. ## Expresions Put one space on each side of infix binary and ternary operators: ```c * / % + - << >> < <= > >= == != & ^ | && || ?: = += -= *= /= %= &= ^= |= <<= >>= ``` Do not put any white space around postfix, prefix, or grouping operators with one exception - `sizeof`, see the note below. ```c () [] -> . ! ~ ++ -- + - * & ``` The "sizeof" operator is unique among C operators in that it accepts two very different kinds of operands: an expression or a type. In general, prefer to specify an expression ```c int *x = calloc(1, sizeof *x); ``` When the operand of sizeof is an expression, there is no need to parenthesize that operand, and please don't. There is an exception to this rule when you need to work with partially compatible structures: ```c struct a_s { uint8_t type; } struct b_s { uint8_t type; char *str; } struct c_s { uint8_t type; uint8_t *u8; } ... struct a_s *a; switch (type) { case 1: a = (struct a_s *)calloc(1, sizeof(struct b_s)); break; case 2: a = (struct a_s *)calloc(1, sizeof(struct c_s)); break; ... ``` netopeer2-1.1.70/LICENSE0000664000000000000000000000271214021433500013202 0ustar rootrootCopyright (c) 2019, CESNET z.s.p.o. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of Netopeer2 nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT HOLDER OR CONTRIBUTORS 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. netopeer2-1.1.70/README.md0000664000000000000000000001341614021433500013457 0ustar rootroot# Netopeer2 – NETCONF Server [![BSD license](https://img.shields.io/badge/License-BSD-blue.svg)](https://opensource.org/licenses/BSD-3-Clause) [![Build Status](https://secure.travis-ci.org/CESNET/Netopeer2.png?branch=master)](http://travis-ci.org/CESNET/Netopeer2) [![Coverity Scan Build Status](https://scan.coverity.com/projects/8416/badge.svg)](https://scan.coverity.com/projects/8416) [![Ohloh Project Status](https://www.openhub.net/p/Netopeer2/widgets/project_thin_badge.gif)](https://www.openhub.net/p/Netopeer2) **Netopeer2** is a server for implementing network configuration management based on the NETCONF Protocol. This is the second generation, originally available as the [Netopeer project](https://github.com/CESNET/netopeer). Netopeer2 is based on the new generation of the NETCONF and YANG libraries - [libyang](https://github.com/CESNET/libyang) and [libnetconf2](https://github.com/CESNET/libnetconf2). The Netopeer2 server uses [sysrepo](https://github.com/sysrepo/sysrepo) as a NETCONF datastore implementation. **Netopeer2** is maintained and further developed by the [Tools for Monitoring and Configuration](https://www.liberouter.org/) department of [CESNET](http://www.ces.net/). Any feedback, testing or feature requests are welcome. Please contact us via the [issue tracker](https://github.com/CESNET/Netopeer2/issues). ## Branches The project uses 2 main branches `master` and `devel`. Other branches should not be cloned. In `master` there are files of the last official *release*. Any latest improvements and changes (of the server), which were tested at least briefly are found in `devel`. On every new *release*, `devel` is merged into `master`. This means that when only stable official releases are to be used, either `master` can be used or specific *releases* downloaded. If all the latest bugfixes should be applied, `devel` branch is the one to be used. Note that whenever **a new issue is created** and it occurs on the `master` branch, the **first response will likely be** to use `devel` before any further provided support. ## Packages We are using openSUSE Build Service to automaticaly prepare binary packages for number of GNU/Linux distros. The [netopeer2](https://software.opensuse.org//download.html?project=home%3Aliberouter&package=netopeer2) packages are always build from current `master` branch (latest release). If you are interested in any other packages (such as *src*), you can browse [all packages](https://download.opensuse.org/repositories/home:/liberouter/) from our repository. ## Requirements * [libyang](https://github.com/CESNET/libyang) * [libnetconf2](https://github.com/CESNET/libnetconf2) * [sysrepo](https://github.com/sysrepo/sysrepo) ## Compilation and installation ``` $ mkdir build; cd build $ cmake .. $ make # make install ``` ### Compilation options The `netopeer2-server` requires *ietf-netconf-server* and all connected YANG modules to be installed in *sysrepo* to work correctly. This is performed autmatically during the installation process. Moreover, default SSH configuration listening on all IPv4 interfaces and a newly generated SSH host key are imported so that it can be connected to the server out-of-the-box. However, it may not always be desired to perform all these steps even though the executed scripts check whether the modules/some configuration already exist and do not repeat/overwrite any modules/configuration. These are the options that affect the initial setup: ``` INSTALL_MODULES:ON GENERATE_HOSTKEY:ON MERGE_LISTEN_CONFIG:ON ``` If cross-compiling for a different architecture, you will likey want to turn all these options off and then run the scripts `setup.sh`, `merge_hostkey.sh`, and `merge_config.sh` manually. ### CLI A command-line NETCONF client `netopeer2-cli` is included and build/installed by default. This can be adjusted by an option: ``` BUILD_CLI:ON ``` ## NACM This NETCONF server implements full *ietf-netconf-acm* access control that **bypasses** *sysrepo* file system access control. NACM is enabled by default, so users other than `root` will not be allowed to *write* any data but should be granted *read* and *execute* permissions unless the access was modified by a NACM extension. When deploying this server, it is strongly advised to configure NACM properly. ## Server configuration Right after installation SSH listen and Call Home and TLS listen and Call Home are supported. By default, only SSH listen configuration is imported so to enable any other connection methods, they need to be configured manually. Example configuration XML files can be found in the `example_configuration` directory. These files can be easily modified to create configuration specific for a particular environment and configured authentication. ### SSH Call Home To enable SSH Call Home, only `ssh_callhome.xml` file needs to be imported to *sysrepo* provided that the default SSH host key `genkey` was imported into *ietf-keystore* configuration. ### TLS listen To support clients connecting using TLS, configuration files `tls_keystore.xml`, `tls_truststore.xml`, and `tls_listen.xml` needs to be merged into *sysrepo* configuration of modules *ietf-keystore*, *ietf-truststore*, and *ietf-netconf-server*, respectively. After doing so, a NETCONF client can connect using `client.crt` certificate and `client.key` private key and having `ca.pem` CA certificate set as trusted. These example certificates can be found in `example_configuration/tls_certs`. *netopeer2-cli* can easily be configured this way and the TLS connection tested. Once connected, the client will be identified with `tls-test` NETCONF username. ### TLS Call Home Using the same certificates and authorization options, a TLS client can be connected to using Call Home when `tls_callhome.xml` file is imported. But `tls_keystore.xml` and `tls_truststore.xml` need to be imported first. netopeer2-1.1.70/cli/0000775000000000000000000000000014021433500012742 5ustar rootrootnetopeer2-1.1.70/cli/CMakeLists.txt0000664000000000000000000000407014021433500015503 0ustar rootrootinclude(CheckFunctionExists) project(netopeer2-cli C) # set version set(NP2CLI_VERSION 2.0.62) configure_file("${CMAKE_CURRENT_SOURCE_DIR}/cli_version.h.in" "${PROJECT_BINARY_DIR}/cli_version.h" ESCAPE_QUOTES @ONLY) include_directories(${PROJECT_BINARY_DIR}) # source files set(CLI_SRC main.c commands.c completion.c configuration.c linenoise/linenoise.c) # netopeer2-cli target add_executable(netopeer2-cli ${CLI_SRC} $<TARGET_OBJECTS:compat>) # reuse server variables target_link_libraries(netopeer2-cli ${CMAKE_THREAD_LIBS_INIT}) target_link_libraries(netopeer2-cli ${LIBYANG_LIBRARIES}) target_link_libraries(netopeer2-cli ${LIBNETCONF2_LIBRARIES}) # dependencies - libssh if(LIBNETCONF2_ENABLED_SSH) if(NOT LIBSSH_FOUND) message(FATAL_ERROR "libnetconf2 supports SSH but libssh was not found, CLI compilation failed!") endif() target_link_libraries(netopeer2-cli ${LIBSSH_LIBRARIES}) include_directories(${LIBSSH_INCLUDE_DIRS}) endif() # dependencies - libssl if(LIBNETCONF2_ENABLED_TLS) if(NOT OPENSSL_FOUND) message(FATAL_ERROR "libnetconf2 supports TLS but OpenSSL was not found, CLI compilation failed!") endif() target_link_libraries(netopeer2-cli ${OPENSSL_LIBRARIES}) include_directories(${OPENSSL_INCLUDE_DIR}) endif() check_function_exists(eaccess HAVE_EACCESS) check_function_exists(mkstemps HAVE_MKSTEMPS) if(HAVE_MKSTEMPS) set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DHAVE_MKSTEMPS") endif(HAVE_MKSTEMPS) # install binary install(TARGETS netopeer2-cli DESTINATION ${CMAKE_INSTALL_BINDIR}) # man -> html find_program(ROFF2HTML roff2html) if(NOT ROFF2HTML) message(STATUS "roff2html not found, html version of man pages cannot be generated!") else() add_custom_target(doc ${ROFF2HTML} "${CMAKE_CURRENT_SOURCE_DIR}/doc/${PROJECT_NAME}.1" > "${CMAKE_CURRENT_SOURCE_DIR}/doc/${PROJECT_NAME}.1.html" DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/doc/${PROJECT_NAME}.1) endif() # install doc (man) install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/doc/${PROJECT_NAME}.1 DESTINATION ${CMAKE_INSTALL_MANDIR}/man1) netopeer2-1.1.70/cli/README.md0000664000000000000000000000240314021433500014220 0ustar rootroot# TLS support If TLS is enabled in libnetconf2, the TLS functionality is enabled. However, in order to make it working, you must perform a few initial configuration tasks. ## client certificate With every action that requires verification, you can specify paths to the client certificate to be used. Also, if you do not specify any certificate, the default one will be used. To set it up, use the `cert replaceown` command. ## server certificate verification In order to verify the certificate provided by the server, you need to specify the Certificate Authority certificates you find trustworthy and make them accessible to netopeer-cli. Again, you can explicitly specify the path to a Certificate Authority trusted store, or use the default directory. To add certificates to this directory, use the `cert add` command. ## Certificate Revocation Lists For netopeer-cli to check if a certificate was not revocated by its issuer, use the `crl add` command to provide CRLs of your trusted CAs for netopeer-cli. ## Certificates The `certs` directory includes copies of the needed example client certificates, which will work with the server example certificates. # Scripts The CLI supports some basic scripting and an example `sample_script.sh` is included for illustration. netopeer2-1.1.70/cli/certs/0000775000000000000000000000000014021433500014062 5ustar rootrootnetopeer2-1.1.70/cli/certs/ca.pem0000664000000000000000000000262014021433500015150 0ustar rootroot-----BEGIN CERTIFICATE----- MIID7TCCAtWgAwIBAgIJAMtE1NGAR5KoMA0GCSqGSIb3DQEBBQUAMIGMMQswCQYD VQQGEwJDWjEWMBQGA1UECAwNU291dGggTW9yYXZpYTENMAsGA1UEBwwEQnJubzEP MA0GA1UECgwGQ0VTTkVUMQwwCgYDVQQLDANUTUMxEzARBgNVBAMMCmV4YW1wbGUg Q0ExIjAgBgkqhkiG9w0BCQEWE2V4YW1wbGVjYUBsb2NhbGhvc3QwHhcNMTQwNzI0 MTQxOTAyWhcNMjQwNzIxMTQxOTAyWjCBjDELMAkGA1UEBhMCQ1oxFjAUBgNVBAgM DVNvdXRoIE1vcmF2aWExDTALBgNVBAcMBEJybm8xDzANBgNVBAoMBkNFU05FVDEM MAoGA1UECwwDVE1DMRMwEQYDVQQDDApleGFtcGxlIENBMSIwIAYJKoZIhvcNAQkB FhNleGFtcGxlY2FAbG9jYWxob3N0MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB CgKCAQEArD3TDHPAMT2Z84orK4lMlarbgooIUCcRZyLe+QM+8KY8Hn+mGaxPEOTS L3ywszqefB/Utm2hPKLHX684iRC14ID9WDGHxPjvoPArhgFhfV+qnPfxKTgxZC12 uOj4u1V9y+SkTCocFbRfXVBGpojrBuDHXkDMDEWNvr8/52YCv7bGaiBwUHolcLCU bmtKILCG0RNJyTaJpXQdAeq5Z1SJotpbfYFFtAXB32hVoLug1dzl2tjG9sb1wq3Q aDExcbC5w6P65qOkNoyym9ne6QlQagCqVDyFn3vcqkRaTjvZmxauCeUxXgJoXkyW cm0lM1KMHdoTArmchw2Dz0yHHSyDAQIDAQABo1AwTjAdBgNVHQ4EFgQUc1YQIqjZ sHVwlea0AB4N+ilNI2gwHwYDVR0jBBgwFoAUc1YQIqjZsHVwlea0AB4N+ilNI2gw DAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQUFAAOCAQEAI/1KH60qnw9Xs2RGfi0/ IKf5EynXt4bQX8EIyVKwSkYKe04zZxYfLIl/Q2HOPYoFmm3daj5ddr0ZS1i4p4fT UhstjsYWvXs3W/HhVmFUslakkn3PrswhP77fCk6eEJLxdfyJ1C7Uudq2m1isZbKi h+XF0mG1LxJaDMocSz4eAya7M5brwjy8DoOmA1TnLQFCVcpn+sCr7VC4wE/JqxyV hBCk/MuGqqM3B1j90bGFZ112ZOecyE0EDSr6IbiRBtmeNbEwOFjKXhNLYdxpBZ9D 8A/368OckZkCrVLGuJNxK9UwCVTe8IhotHUqU9EqFDmxdV8oIdU/OzUwwNPA/Bd/ 9g== -----END CERTIFICATE----- netopeer2-1.1.70/cli/certs/client.crt0000664000000000000000000000266414021433500016062 0ustar rootroot-----BEGIN CERTIFICATE----- MIIECTCCAvGgAwIBAgIBBzANBgkqhkiG9w0BAQsFADCBjDELMAkGA1UEBhMCQ1ox FjAUBgNVBAgMDVNvdXRoIE1vcmF2aWExDTALBgNVBAcMBEJybm8xDzANBgNVBAoM BkNFU05FVDEMMAoGA1UECwwDVE1DMRMwEQYDVQQDDApleGFtcGxlIENBMSIwIAYJ KoZIhvcNAQkBFhNleGFtcGxlY2FAbG9jYWxob3N0MB4XDTE1MDczMDA3MjcxOFoX DTM1MDcyNTA3MjcxOFowgYUxCzAJBgNVBAYTAkNaMRYwFAYDVQQIDA1Tb3V0aCBN b3JhdmlhMQ8wDQYDVQQKDAZDRVNORVQxDDAKBgNVBAsMA1RNQzEXMBUGA1UEAwwO ZXhhbXBsZSBjbGllbnQxJjAkBgkqhkiG9w0BCQEWF2V4YW1wbGVjbGllbnRAbG9j YWxob3N0MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAueCQaNQWoNmF K6LKu1p8U8ZWdWg/PvDdLsJyzfzl/Qw4UA68SfFNaY06zZl8QB9W02nr5kWeeMY0 VA3adrPgOlvfx3oWlFbkETnMaN4OT3WTQ0Wt6jAWZDzVfopwpJPAzRPxACDftIqF GagYcF32hZlVNqqnVdbXh0S0EViweqp/dbG4VDUHSNVbglc+u4UbEzNIFXMdEFsJ ZpkynOmSiTsIATqIhb+2srkVgLwhfkC2qkuHQwAHdubuB07ObM2z01UhyEdDvEYG HwtYAGDBL2TAcsI0oGeVkRyuOkV0QY0UN7UEFI1yTYw+xZ42HgFx3uGwApCImxhb j69GBYWFqwIDAQABo3sweTAJBgNVHRMEAjAAMCwGCWCGSAGG+EIBDQQfFh1PcGVu U1NMIEdlbmVyYXRlZCBDZXJ0aWZpY2F0ZTAdBgNVHQ4EFgQUXGpLeLnh2cSDARAV A7KrBxGYpo8wHwYDVR0jBBgwFoAUc1YQIqjZsHVwlea0AB4N+ilNI2gwDQYJKoZI hvcNAQELBQADggEBAJPV3RTXFRtNyOU4rjPpYeBAIAFp2aqGc4t2J1c7oPp/1n+l ZvjnwtlJpZHxMM783e2ryDQ6dkvXDf8kpwKlg3U3mkJ3xKkDdWrM4QwghXdCN519 aa9qmu0zdFL+jUAaWlQ5tsceOrvbusCcbMqiFGk/QfpHqPv52SVWbYyUx7IX7DE+ UjgsLHycfV/tlcx4ZE6soTzl9VdgSL/zmzG3rjsr58J80rXckLgBhvijgBlIAJvW fC7D0vaouvBInSFXymdPVoUDZ30cdGLf+hI/i/TfsEMOinLrXVdkSGNo6FXAHKSv XeB9oFKSzhQ7OPyRyqvEPycUSw/qD6FVr80oDDc= -----END CERTIFICATE----- netopeer2-1.1.70/cli/certs/client.key0000664000000000000000000000321714021433500016055 0ustar rootroot-----BEGIN RSA PRIVATE KEY----- MIIEpAIBAAKCAQEAueCQaNQWoNmFK6LKu1p8U8ZWdWg/PvDdLsJyzfzl/Qw4UA68 SfFNaY06zZl8QB9W02nr5kWeeMY0VA3adrPgOlvfx3oWlFbkETnMaN4OT3WTQ0Wt 6jAWZDzVfopwpJPAzRPxACDftIqFGagYcF32hZlVNqqnVdbXh0S0EViweqp/dbG4 VDUHSNVbglc+u4UbEzNIFXMdEFsJZpkynOmSiTsIATqIhb+2srkVgLwhfkC2qkuH QwAHdubuB07ObM2z01UhyEdDvEYGHwtYAGDBL2TAcsI0oGeVkRyuOkV0QY0UN7UE FI1yTYw+xZ42HgFx3uGwApCImxhbj69GBYWFqwIDAQABAoIBAQCZN9kR8DGu6V7y t0Ax68asL8O5B/OKaHWKQ9LqpVrXmikZJOxkbzoGldow/CIFoU+q+Zbwu9aDa65a 0wiP7Hoa4Py3q5XNNUrOQDyU/OYC7cI0I83WS0lJ2zOJGYj8wKae5Z81IeQFKGHK 4lsy1OGPAvPRGh7RjUUgRavA2MCwe07rWRuDb/OJFe4Oh56UMEjwMiNBtMNtncog j1vr/qgRJdf9tf0zlJmLvUJ9+HSFFV9I/97LJyFhb95gAfHkjdVroLVgT3Cho+4P WtZaKCIGD0OwfOG2nLV4leXvRUk62/LMlB8NI9+JF7Xm+HCKbaWHNWC7mvWSLV58 Zl4AbUWRAoGBANyJ6SFHFRHSPDY026SsdMzXR0eUxBAK7G70oSBKKhY+O1j0ocLE jI2krHJBhHbLlnvJVyMUaCUOTS5m0uDw9hgSsAqeSL3hL38kxVZw+KNG9Ouno1Fl KnE/xXHlPQyeGs/P8nAMzHZxQtEsQdQayJEhK2XXHTsy7Q3MxDisfVJ1AoGBANfD 34gB+OMx6pwj7zk3qWbYXSX8xjCZMR0ciko+h4xeMP2N8B0oyoqC+v1ABMAtJ3wG sGZd0hV9gwM7OUM3SEwkn6oeg1GemWLcn4rlSmTnZc4aeVwrEWlnSNFX3s4g9l4u k8Ugu4MVJYqH8HuDQ5Ggl6/QAwPzMSEdCW0O+jOfAoGAIBRbegC5+t6m7Yegz4Ja dxV1g98K6f58x+MDsQu4tYWV4mmrQgaPH2dtwizvlMwmdpkh+LNWNtWuumowkJHc akIFo3XExQIFg6wYnGtQb4e5xrGa2xMpKlIJaXjb+YLiCYqJDG2ALFZrTrvuU2kV 9a5qfqTc1qigvNolTM0iaaUCgYApmrZWhnLUdEKV2wP813PNxfioI4afxlpHD8LG sCn48gymR6E+Lihn7vuwq5B+8fYEH1ISWxLwW+RQUjIneNhy/jjfV8TgjyFqg7or 0Sy4KjpiNI6kLBXOakELRNNMkeSPopGR2E7v5rr3bGD9oAD+aqX1G7oJH/KgPPYd Vl7+ZwKBgQDcHyWYrimjyUgKaQD2GmoO9wdcJYQ59ke9K+OuGlp4ti5arsi7N1tP B4f09aeELM2ASIuk8Q/Mx0jQFnm8lzRFXdewgvdPoZW/7VufM9O7dGPOc41cm2Dh yrTcXx/VmUBb+/fnXVEgCv7gylp/wtdTGHQBQJHR81jFBz0lnLj+gg== -----END RSA PRIVATE KEY----- netopeer2-1.1.70/cli/cli_version.h.in0000664000000000000000000000066214021433500016040 0ustar rootroot/** * @file cli_version.h * @author Radek Krejci <rkrejci@cesnet.cz> * @brief netopeer2-cli version * * Copyright (c) 2016 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 CLI_VERSION "@NP2CLI_VERSION@" netopeer2-1.1.70/cli/commands.c0000664000000000000000000047642614021433500014732 0ustar rootroot/** * @file commands.c * @author Michal Vasko <mvasko@cesnet.cz> * @brief netopeer2-cli commands * * Copyright (c) 2017 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 <string.h> #include <stdio.h> #include <errno.h> #include <pthread.h> #include <sys/types.h> #include <sys/stat.h> #include <sys/mman.h> #include <pwd.h> #include <fcntl.h> #include <assert.h> #include <unistd.h> #include <getopt.h> #include <stdarg.h> #include <ctype.h> #include <libyang/libyang.h> #include <nc_client.h> #ifdef NC_ENABLED_TLS # include <openssl/pem.h> # include <openssl/x509v3.h> #endif #ifndef HAVE_EACCESS #define eaccess access #endif #include "compat.h" #include "commands.h" #include "configuration.h" #include "completion.h" #define CLI_CH_TIMEOUT 60 /* 1 minute */ #define CLI_RPC_REPLY_TIMEOUT 5 /* 5 seconds */ #define NC_CAP_WRITABLERUNNING_ID "urn:ietf:params:netconf:capability:writable-running" #define NC_CAP_CANDIDATE_ID "urn:ietf:params:netconf:capability:candidate" #define NC_CAP_CONFIRMEDCOMMIT_ID "urn:ietf:params:netconf:capability:confirmed-commit:1.1" #define NC_CAP_ROLLBACK_ID "urn:ietf:params:netconf:capability:rollback-on-error" #define NC_CAP_VALIDATE10_ID "urn:ietf:params:netconf:capability:validate:1.0" #define NC_CAP_VALIDATE11_ID "urn:ietf:params:netconf:capability:validate:1.1" #define NC_CAP_STARTUP_ID "urn:ietf:params:netconf:capability:startup" #define NC_CAP_URL_ID "urn:ietf:params:netconf:capability:url" #define NC_CAP_XPATH_ID "urn:ietf:params:netconf:capability:xpath" #define NC_CAP_WITHDEFAULTS_ID "urn:ietf:params:netconf:capability:with-defaults" #define NC_CAP_NOTIFICATION_ID "urn:ietf:params:netconf:capability:notification" #define NC_CAP_INTERLEAVE_ID "urn:ietf:params:netconf:capability:interleave" char some_msg[4096]; COMMAND commands[]; extern int done; LYD_FORMAT output_format = LYD_XML; int output_flag = LYP_FORMAT; char *config_editor; struct nc_session *session; volatile int interleave; int timed; int cmd_disconnect(const char *arg, char **tmp_config_file); struct arglist { char** list; int count; int size; }; static void init_arglist(struct arglist *args) { if (args != NULL) { args->list = NULL; args->count = 0; args->size = 0; } } static void clear_arglist(struct arglist *args) { int i = 0; if (args && args->list) { for (i = 0; i < args->count; i++) { if (args->list[i]) { free(args->list[i]); } } free(args->list); } init_arglist(args); } static int addargs(struct arglist *args, char *format, ...) { va_list arguments; char *aux = NULL, *aux1 = NULL, *prev_aux, quot; int spaces; if (args == NULL) { return EXIT_FAILURE; } /* store arguments to aux string */ va_start(arguments, format); if (vasprintf(&aux, format, arguments) == -1) { va_end(arguments); ERROR(__func__, "vasprintf() failed (%s)", strerror(errno)); return EXIT_FAILURE; } va_end(arguments); /* remember the begining of the aux string to free it after operations */ aux1 = aux; /* * get word by word from given string and store words separately into * the arglist */ prev_aux = NULL; quot = 0; for (aux = strtok(aux, " \n\t"); aux; prev_aux = aux, aux = strtok(NULL, " \n\t")) { if (!strcmp(aux, "")) { continue; } if (!args->list) { /* initial memory allocation */ if ((args->list = (char **)malloc(8 * sizeof(char *))) == NULL) { ERROR(__func__, "Memory allocation failed (%s:%d)", __FILE__, __LINE__); return EXIT_FAILURE; } args->size = 8; args->count = 0; } else if (!quot && (args->count + 2 >= args->size)) { /* * list is too short to add next to word so we have to * extend it */ args->size += 8; args->list = realloc(args->list, args->size * sizeof(char *)); } if (!quot) { /* add word at the end of the list */ if ((args->list[args->count] = malloc((strlen(aux) + 1) * sizeof(char))) == NULL) { ERROR(__func__, "Memory allocation failed (%s:%d)", __FILE__, __LINE__); return EXIT_FAILURE; } /* quoted argument */ if ((aux[0] == '\'') || (aux[0] == '\"')) { quot = aux[0]; ++aux; /* ...but without spaces */ if (aux[strlen(aux) - 1] == quot) { quot = 0; aux[strlen(aux) - 1] = '\0'; } } strcpy(args->list[args->count], aux); args->list[++args->count] = NULL; /* last argument */ } else { /* append another part of the argument */ spaces = aux - (prev_aux + strlen(prev_aux)); args->list[args->count - 1] = realloc(args->list[args->count - 1], strlen(args->list[args->count - 1]) + spaces + strlen(aux) + 1); /* end of quoted argument */ if (aux[strlen(aux) - 1] == quot) { quot = 0; aux[strlen(aux) - 1] = '\0'; } sprintf(args->list[args->count - 1] + strlen(args->list[args->count - 1]), "%*s%s", spaces, " ", aux); } } /* clean up */ free(aux1); return EXIT_SUCCESS; } static void cli_ntf_clb(struct nc_session *session, const struct nc_notif *notif) { FILE *output = nc_session_get_data(session); int was_rawmode = 0; if (output == stdout) { if (ls.rawmode) { was_rawmode = 1; linenoiseDisableRawMode(ls.ifd); printf("\n"); } else { was_rawmode = 0; } } fprintf(output, "notification (%s)\n", notif->datetime); lyd_print_file(output, notif->tree, output_format, LYP_WITHSIBLINGS | output_flag); fprintf(output, "\n"); fflush(output); if ((output == stdout) && was_rawmode) { linenoiseEnableRawMode(ls.ifd); linenoiseRefreshLine(); } if (!strcmp(notif->tree->schema->name, "notificationComplete") && !strcmp(notif->tree->schema->module->name, "nc-notifications")) { interleave = 1; } } static int cli_gettimespec(struct timespec *ts, int *mono) { errno = 0; #ifdef CLOCK_MONOTONIC_RAW *mono = 1; return clock_gettime(CLOCK_MONOTONIC_RAW, ts); #elif defined(CLOCK_MONOTONIC) *mono = 1; return clock_gettime(CLOCK_MONOTONIC, ts); #elif defined(CLOCK_REALTIME) /* no monotonic clock available, return realtime */ *mono = 0; return clock_gettime(CLOCK_REALTIME, ts); #else *mono = 0; 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 } /* returns milliseconds */ static int32_t cli_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); } static int cli_send_recv(struct nc_rpc *rpc, FILE *output, NC_WD_MODE wd_mode, int timeout_s) { char *str, *model_data; int ret = 0, ly_wd, mono; int32_t msec; uint16_t i, j; uint64_t msgid; struct lyd_node_anydata *any; NC_MSG_TYPE msgtype; struct nc_reply *reply; struct nc_reply_data *data_rpl; struct nc_reply_error *error; struct timespec ts_start, ts_stop; if (timed) { ret = cli_gettimespec(&ts_start, &mono); if (ret) { ERROR(__func__, "Getting current time failed (%s).", strerror(errno)); return ret; } } msgtype = nc_send_rpc(session, rpc, 1000, &msgid); if (msgtype == NC_MSG_ERROR) { ERROR(__func__, "Failed to send the RPC."); if (nc_session_get_status(session) != NC_STATUS_RUNNING) { cmd_disconnect(NULL, NULL); } return -1; } else if (msgtype == NC_MSG_WOULDBLOCK) { ERROR(__func__, "Timeout for sending the RPC expired."); return -1; } recv_reply: msgtype = nc_recv_reply(session, rpc, msgid, timeout_s * 1000, LYD_OPT_DESTRUCT | LYD_OPT_NOSIBLINGS, &reply); if (msgtype == NC_MSG_ERROR) { ERROR(__func__, "Failed to receive a reply."); if (nc_session_get_status(session) != NC_STATUS_RUNNING) { cmd_disconnect(NULL, NULL); } return -1; } else if (msgtype == NC_MSG_WOULDBLOCK) { ERROR(__func__, "Timeout for receiving a reply expired."); return -1; } else if (msgtype == NC_MSG_NOTIF) { /* read again */ goto recv_reply; } else if (msgtype == NC_MSG_REPLY_ERR_MSGID) { /* unexpected message, try reading again to get the correct reply */ ERROR(__func__, "Unexpected reply received - ignoring and waiting for the correct reply."); nc_reply_free(reply); goto recv_reply; } if (timed) { ret = cli_gettimespec(&ts_stop, &mono); if (ret) { ERROR(__func__, "Getting current time failed (%s).", strerror(errno)); nc_reply_free(reply); return ret; } } switch (reply->type) { case NC_RPL_OK: fprintf(output, "OK\n"); break; case NC_RPL_DATA: data_rpl = (struct nc_reply_data *)reply; /* special case */ if (nc_rpc_get_type(rpc) == NC_RPC_GETSCHEMA) { if (!data_rpl->data || (data_rpl->data->schema->nodetype != LYS_RPC) || (data_rpl->data->child == NULL) || (data_rpl->data->child->schema->nodetype != LYS_ANYXML)) { ERROR(__func__, "Unexpected data reply to <get-schema> RPC."); ret = -1; break; } if (output == stdout) { fprintf(output, "MODULE\n"); } any = (struct lyd_node_anydata *)data_rpl->data->child; switch (any->value_type) { case LYD_ANYDATA_CONSTSTRING: case LYD_ANYDATA_STRING: fputs(any->value.str, output); break; case LYD_ANYDATA_DATATREE: lyd_print_mem(&model_data, any->value.tree, LYD_XML, LYP_FORMAT | LYP_WITHSIBLINGS); fputs(model_data, output); free(model_data); break; case LYD_ANYDATA_XML: lyxml_print_mem(&model_data, any->value.xml, LYXML_PRINT_SIBLINGS); fputs(model_data, output); free(model_data); break; default: /* none of the others can appear here */ ERROR(__func__, "Unexpected anydata value format."); ret = -1; break; } if (ret == -1) { break; } if (output == stdout) { fprintf(output, "\n"); } break; } if (output == stdout) { fprintf(output, "DATA\n"); } else { switch (nc_rpc_get_type(rpc)) { case NC_RPC_GETCONFIG: fprintf(output, "<config xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\">\n"); break; case NC_RPC_GET: fprintf(output, "<data xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\">\n"); break; default: break; } } switch (wd_mode) { case NC_WD_ALL: ly_wd = LYP_WD_ALL; break; case NC_WD_ALL_TAG: ly_wd = LYP_WD_ALL_TAG; break; case NC_WD_TRIM: ly_wd = LYP_WD_TRIM; break; case NC_WD_EXPLICIT: ly_wd = LYP_WD_EXPLICIT; break; default: ly_wd = 0; break; } lyd_print_file(output, data_rpl->data, output_format, LYP_WITHSIBLINGS | LYP_NETCONF | ly_wd | output_flag); if (output == stdout) { fprintf(output, "\n"); } else { switch (nc_rpc_get_type(rpc)) { case NC_RPC_GETCONFIG: fprintf(output, "</config>\n"); break; case NC_RPC_GET: fprintf(output, "</data>\n"); break; default: break; } } break; case NC_RPL_ERROR: fprintf(output, "ERROR\n"); error = (struct nc_reply_error *)reply; for (i = 0; i < error->count; ++i) { if (error->err[i].type) { fprintf(output, "\ttype: %s\n", error->err[i].type); } if (error->err[i].tag) { fprintf(output, "\ttag: %s\n", error->err[i].tag); } if (error->err[i].severity) { fprintf(output, "\tseverity: %s\n", error->err[i].severity); } if (error->err[i].apptag) { fprintf(output, "\tapp-tag: %s\n", error->err[i].apptag); } if (error->err[i].path) { fprintf(output, "\tpath: %s\n", error->err[i].path); } if (error->err[i].message) { fprintf(output, "\tmessage: %s\n", error->err[i].message); } if (error->err[i].sid) { fprintf(output, "\tSID: %s\n", error->err[i].sid); } for (j = 0; j < error->err[i].attr_count; ++j) { fprintf(output, "\tbad-attr #%d: %s\n", j + 1, error->err[i].attr[j]); } for (j = 0; j < error->err[i].elem_count; ++j) { fprintf(output, "\tbad-elem #%d: %s\n", j + 1, error->err[i].elem[j]); } for (j = 0; j < error->err[i].ns_count; ++j) { fprintf(output, "\tbad-ns #%d: %s\n", j + 1, error->err[i].ns[j]); } for (j = 0; j < error->err[i].other_count; ++j) { lyxml_print_mem(&str, error->err[i].other[j], 0); fprintf(output, "\tother #%d:\n%s\n", j + 1, str); free(str); } fprintf(output, "\n"); } ret = 1; break; default: ERROR(__func__, "Internal error."); nc_reply_free(reply); return -1; } nc_reply_free(reply); if (msgtype == NC_MSG_REPLY_ERR_MSGID) { ERROR(__func__, "Trying to receive another message...\n"); goto recv_reply; } if (timed) { msec = cli_difftimespec(&ts_start, &ts_stop); fprintf(output, "%s %2dm%d.%03ds\n", mono ? "mono" : "real", msec / 60000, (msec % 60000) / 1000, msec % 1000); } return ret; } static char * trim_top_elem(char *data, const char *top_elem, const char *top_elem_ns) { char *ptr, *prefix = NULL, *buf; int pref_len = 0, state = 0, quote; /* state: -2 - syntax error, * -1 - top_elem not found, * 0 - start, * 1 - parsing prefix, * 2 - prefix just parsed, * 3 - top-elem found and parsed, looking for namespace, * 4 - top_elem and top_elem_ns found (success) */ if (!data) { return NULL; } while (isspace(data[0])) { ++data; } if (data[0] != '<') { return data; } for (ptr = data + 1; (ptr[0] != '\0') && (ptr[0] != '>'); ++ptr) { switch (state) { case 0: if (!strncmp(ptr, top_elem, strlen(top_elem))) { state = 3; ptr += strlen(top_elem); } else if ((ptr[0] != ':') && !isdigit(ptr[0])) { state = 1; prefix = ptr; pref_len = 1; } else { state = -1; } break; case 1: if (ptr[0] == ':') { /* prefix parsed */ state = 2; } else if (ptr[0] != ' ') { ++pref_len; } else { state = -1; } break; case 2: if (!strncmp(ptr, top_elem, strlen(top_elem))) { state = 3; ptr += strlen(top_elem); } else { state = -1; } break; case 3: if (!strncmp(ptr, "xmlns", 5)) { ptr += 5; if (prefix) { if ((ptr[0] != ':') || strncmp(ptr + 1, prefix, pref_len) || (ptr[1 + pref_len] != '=')) { /* it's not the right prefix, look further */ break; } /* we found our prefix, does the namespace match? */ ptr += 1 + pref_len; } if (ptr[0] != '=') { if (prefix) { /* fail for sure */ state = -1; } else { /* it may not be xmlns attribute, but something longer... */ } break; } ++ptr; if ((ptr[0] != '\"') && (ptr[0] != '\'')) { state = -2; break; } quote = ptr[0]; ++ptr; if (strncmp(ptr, top_elem_ns, strlen(top_elem_ns))) { if (prefix) { state = -1; } break; } ptr += strlen(top_elem_ns); if (ptr[0] != quote) { if (prefix) { state = -1; } break; } /* success */ ptr = strchrnul(ptr, '>'); state = 4; } break; } if ((state < 0) || (state == 4)) { break; } } if ((state == -2) || (ptr[0] == '\0')) { return NULL; } else if (state != 4) { return data; } /* skip the first elem, ... */ ++ptr; while (isspace(ptr[0])) { ++ptr; } data = ptr; /* ... but also its ending tag */ if (prefix) { asprintf(&buf, "</%.*s:%s>", pref_len, prefix, top_elem); } else { asprintf(&buf, "</%s>", top_elem); } ptr = strstr(data, buf); if (!ptr) { /* syntax error */ free(buf); return NULL; } else { /* reuse it */ prefix = ptr; } ptr += strlen(buf); free(buf); while (isspace(ptr[0])) { ++ptr; } if (ptr[0] != '\0') { /* there should be nothing more */ return NULL; } /* ending tag and all syntax seems fine, so cut off the ending tag */ while (isspace(prefix[-1]) && (prefix > data)) { --prefix; } prefix[0] = '\0'; return data; } void cmd_searchpath_help(void) { printf("searchpath [<model-dir-path>]\n"); } void cmd_outputformat_help(void) { printf("outputformat (xml | xml_noformat | json | json_noformat)\n"); } void cmd_verb_help(void) { printf("verb (error/0 | warning/1 | verbose/2 | debug/3)\n"); } void cmd_connect_help(void) { #if defined(NC_ENABLED_SSH) && defined(NC_ENABLED_TLS) printf("connect [--help] [--ssh] [--host <hostname>] [--port <num>] [--login <username>]\n"); printf("connect [--help] --tls [--host <hostname>] [--port <num>] [--cert <cert_path> [--key <key_path>]] [--trusted <trusted_CA_store.pem>]\n"); #elif defined(NC_ENABLED_SSH) printf("connect [--help] [--ssh] [--host <hostname>] [--port <num>] [--login <username>]\n"); #elif defined(NC_ENABLED_TLS) printf("connect [--help] [--tls] [--host <hostname>] [--port <num>] [--cert <cert_path> [--key <key_path>]] [--trusted <trusted_CA_store.pem>]\n"); #endif printf("connect [--help] --unix [--socket <path>]\n"); } void cmd_listen_help(void) { #if defined(NC_ENABLED_SSH) && defined(NC_ENABLED_TLS) printf("listen [--help] [--timeout <sec>] [--host <hostname>] [--port <num>]\n"); printf(" SSH [--ssh] [--login <username>]\n"); printf(" TLS --tls [--cert <cert_path> [--key <key_path>]] [--trusted <trusted_CA_store.pem>]\n"); #elif defined(NC_ENABLED_SSH) printf("listen [--help] [--ssh] [--timeout <sec>] [--host <hostname>] [--port <num>] [--login <username>]\n"); #elif defined(NC_ENABLED_TLS) printf("listen [--help] [--tls] [--timeout <sec>] [--host <hostname>] [--port <num>] [--cert <cert_path> [--key <key_path>]] [--trusted <trusted_CA_store.pem>]\n"); #endif } void cmd_editor_help(void) { printf("editor [--help] [<path/name-of-the-editor>]\n"); } void cmd_cancelcommit_help(void) { if (session && !nc_session_cpblt(session, NC_CAP_CONFIRMEDCOMMIT_ID)) { printf("cancel-commit is not supported by the current session.\n"); } else { printf("cancel-commit [--help] [--persist-id <commit-id>] [--rpc-timeout <seconds>]\n"); } } void cmd_commit_help(void) { const char *confirmed; if (session && !nc_session_cpblt(session, NC_CAP_CANDIDATE_ID)) { printf("commit is not supported by the current session.\n"); return; } if (!session || nc_session_cpblt(session, NC_CAP_CONFIRMEDCOMMIT_ID)) { confirmed = " [--confirmed] [--confirm-timeout <sec>] [--persist <new-commit-id>] [--persist-id <commit-id>]"; } else { confirmed = ""; } printf("commit [--help]%s [--rpc-timeout <seconds>]\n", confirmed); } void cmd_copyconfig_help(void) { int ds = 0; const char *running, *startup, *candidate, *url, *defaults; if (!session) { /* if session not established, print complete help for all capabilities */ running = "running"; startup = "|startup"; candidate = "|candidate"; url = "|url:<url>"; defaults = " [--defaults report-all|report-all-tagged|trim|explicit]"; } else { if (nc_session_cpblt(session, NC_CAP_WRITABLERUNNING_ID)) { running = "running"; ds = 1; } else { running = ""; } if (nc_session_cpblt(session, NC_CAP_STARTUP_ID)) { if (ds) { startup = "|startup"; } else { startup = "startup"; ds = 1; } } else { startup = ""; } if (nc_session_cpblt(session, NC_CAP_CANDIDATE_ID)) { if (ds) { candidate = "|candidate"; } else { candidate = "candidate"; ds = 1; } } else { candidate = ""; } if (nc_session_cpblt(session, NC_CAP_URL_ID)) { if (ds) { url = "|url:<url>"; } else { url = "url:<url>"; ds = 1; } } else { url = ""; } if (!ds) { printf("copy-config is not supported by the current session.\n"); return; } if (nc_session_cpblt(session, NC_CAP_WITHDEFAULTS_ID)) { defaults = " [--defaults report-all|report-all-tagged|trim|explicit]"; } else { defaults = ""; } } printf("copy-config [--help] --target %s%s%s%s (--source %s%s%s%s | --src-config[=<file>])%s [--rpc-timeout <seconds>]\n", running, startup, candidate, url, running, startup, candidate, url, defaults); } void cmd_deleteconfig_help(void) { const char *startup, *url; if (!session) { startup = "startup"; url = "|url:<url>"; } else { if (nc_session_cpblt(session, NC_CAP_STARTUP_ID)) { startup = "startup"; } else { startup = ""; } if (nc_session_cpblt(session, NC_CAP_URL_ID)) { url = strlen(startup) ? "|url:<url>" : "url:<url>"; } else { url = ""; } } if ((strlen(startup) + strlen(url)) == 0) { printf("delete-config is not supported by the current session.\n"); return; } printf("delete-config [--help] --target %s%s [--rpc-timeout <seconds>]\n", startup, url); } void cmd_discardchanges_help(void) { if (!session || nc_session_cpblt(session, NC_CAP_CANDIDATE_ID)) { printf("discard-changes [--help] [--rpc-timeout <seconds>]\n"); } else { printf("discard-changes is not supported by the current session.\n"); } } void cmd_editconfig_help(void) { const char *rollback, *validate, *running, *candidate, *url, *bracket; if (!session || nc_session_cpblt(session, NC_CAP_WRITABLERUNNING_ID)) { running = "running"; } else { running = ""; } if (!session || nc_session_cpblt(session, NC_CAP_CANDIDATE_ID)) { if (running[0]) { candidate = "|candidate"; } else { candidate = "candidate"; } } else { candidate = ""; } if (!running[0] && !candidate[0]) { printf("edit-config is not supported by the current session.\n"); return; } if (!session || nc_session_cpblt(session, NC_CAP_ROLLBACK_ID)) { rollback = "|rollback"; } else { rollback = ""; } if (!session || nc_session_cpblt(session, NC_CAP_VALIDATE11_ID)) { validate = "[--test set|test-only|test-then-set] "; } else if (!session || nc_session_cpblt(session, NC_CAP_VALIDATE10_ID)) { validate = "[--test set|test-then-set] "; } else { validate = ""; } if (!session || nc_session_cpblt(session, NC_CAP_URL_ID)) { url = " | --url <url>)"; bracket = "("; } else { url = ""; bracket = ""; } printf("edit-config [--help] --target %s%s %s--config[=<file>]%s [--defop merge|replace|none] " "%s[--error stop|continue%s] [--rpc-timeout <seconds>]\n", running, candidate, bracket, url, validate, rollback); } void cmd_get_help(void) { const char *defaults, *xpath; if (!session || nc_session_cpblt(session, NC_CAP_WITHDEFAULTS_ID)) { defaults = "[--defaults report-all|report-all-tagged|trim|explicit] "; } else { defaults = ""; } if (!session || nc_session_cpblt(session, NC_CAP_XPATH_ID)) { xpath = " | --filter-xpath <XPath>"; } else { xpath = ""; } fprintf(stdout, "get [--help] [--filter-subtree[=<file>]%s] %s[--out <file>] [--rpc-timeout <seconds>]\n", xpath, defaults); } void cmd_getconfig_help(void) { const char *defaults, *xpath, *candidate, *startup; /* if session not established, print complete help for all capabilities */ if (!session || nc_session_cpblt(session, NC_CAP_WITHDEFAULTS_ID)) { defaults = "[--defaults report-all|report-all-tagged|trim|explicit] "; } else { defaults = ""; } if (!session || nc_session_cpblt(session, NC_CAP_XPATH_ID)) { xpath = " | --filter-xpath <XPath>"; } else { xpath = ""; } if (!session || nc_session_cpblt(session, NC_CAP_STARTUP_ID)) { startup = "|startup"; } else { startup = ""; } if (!session || nc_session_cpblt(session, NC_CAP_CANDIDATE_ID)) { candidate = "|candidate"; } else { candidate = ""; } printf("get-config [--help] --source running%s%s [--filter-subtree[=<file>]%s] %s[--out <file>] [--rpc-timeout <seconds>]\n", startup, candidate, xpath, defaults); } void cmd_killsession_help(void) { printf("killsession [--help] --sid <sesion-ID> [--rpc-timeout <seconds>]\n"); } void cmd_lock_help(void) { const char *candidate, *startup; if (!session || nc_session_cpblt(session, NC_CAP_STARTUP_ID)) { startup = "|startup"; } else { startup = ""; } if (!session || nc_session_cpblt(session, NC_CAP_CANDIDATE_ID)) { candidate = "|candidate"; } else { candidate = ""; } printf("lock [--help] --target running%s%s [--rpc-timeout <seconds>]\n", startup, candidate); } void cmd_unlock_help(void) { const char *candidate, *startup; if (!session || nc_session_cpblt(session, NC_CAP_STARTUP_ID)) { startup = "|startup"; } else { startup = ""; } if (!session || nc_session_cpblt(session, NC_CAP_CANDIDATE_ID)) { candidate = "|candidate"; } else { candidate = ""; } printf("unlock [--help] --target running%s%s [--rpc-timeout <seconds>]\n", startup, candidate); } void cmd_validate_help(void) { const char *startup, *candidate, *url; if (session && !nc_session_cpblt(session, NC_CAP_VALIDATE10_ID) && !nc_session_cpblt(session, NC_CAP_VALIDATE11_ID)) { printf("validate is not supported by the current session.\n"); return; } if (!session) { /* if session not established, print complete help for all capabilities */ startup = "|startup"; candidate = "|candidate"; url = "|url:<url>"; } else { if (nc_session_cpblt(session, NC_CAP_STARTUP_ID)) { startup = "|startup"; } else { startup = ""; } if (nc_session_cpblt(session, NC_CAP_CANDIDATE_ID)) { candidate = "|candidate"; } else { candidate = ""; } if (nc_session_cpblt(session, NC_CAP_URL_ID)) { url = "|url:<dsturl>"; } else { url = ""; } } printf("validate [--help] (--source running%s%s%s | --src-config[=<file>]) [--rpc-timeout <seconds>]\n", startup, candidate, url); } void cmd_subscribe_help(void) { const char *xpath; if (session && !nc_session_cpblt(session, NC_CAP_NOTIFICATION_ID)) { printf("subscribe not supported by the current session.\n"); return; } if (!session || nc_session_cpblt(session, NC_CAP_XPATH_ID)) { xpath = " | --filter-xpath <XPath>"; } else { xpath = ""; } printf("subscribe [--help] [--filter-subtree[=<file>]%s] [--begin <time>] [--end <time>] [--stream <stream>] [--out <file>]" " [--rpc-timeout <seconds>]\n", xpath); printf("\t<time> has following format:\n"); printf("\t\t+<num> - current time plus the given number of seconds.\n"); printf("\t\t<num> - absolute time as number of seconds since 1970-01-01.\n"); printf("\t\t-<num> - current time minus the given number of seconds.\n"); } void cmd_getschema_help(void) { if (session && !ly_ctx_get_module(nc_session_get_ctx(session), "ietf-netconf-monitoring", NULL, 1)) { printf("get-schema is not supported by the current session.\n"); return; } printf("get-schema [--help] --model <identifier> [--version <version>] [--format <format>] [--out <file>] [--rpc-timeout <seconds>]\n"); } void cmd_getdata_help(void) { const struct lys_module *mod = NULL; const char *defaults, *xpath; int origin; if (session && !(mod = ly_ctx_get_module(nc_session_get_ctx(session), "ietf-netconf-nmda", NULL, 1))) { printf("get-data is not supported by the current session.\n"); return; } if (!session || nc_session_cpblt(session, NC_CAP_WITHDEFAULTS_ID)) { defaults = " [--defaults report-all|report-all-tagged|trim|explicit]"; } else { defaults = ""; } if (!session || nc_session_cpblt(session, NC_CAP_XPATH_ID)) { xpath = " | --filter-xpath <XPath>"; } else { xpath = ""; } if (mod && !lys_features_state(mod, "origin")) { origin = 0; } else { origin = 1; } fprintf(stdout, "get-data [--help] --datastore running|startup|candidate|operational [--filter-subtree[=<file>]%s]" " [--config true|false]%s [--depth <subtree-depth>]%s%s [--out <file>] [--rpc-timeout <seconds>]\n", xpath, origin ? " [--origin <origin>]* [--negated-origin]" : "", origin ? " [--with-origin]" : "", defaults); } void cmd_editdata_help(void) { const struct lys_module *mod; const char *url, *bracket; if (session && !(mod = ly_ctx_get_module(nc_session_get_ctx(session), "ietf-netconf-nmda", NULL, 1))) { printf("edit-data is not supported by the current session.\n"); return; } if (!session || nc_session_cpblt(session, NC_CAP_URL_ID)) { url = " | --url <url>)"; bracket = "("; } else { url = ""; bracket = ""; } fprintf(stdout, "edit-data [--help] --datastore running|startup|candidate %s--config[=<file>]%s" " [--defop merge|replace|none] [--rpc-timeout <seconds>]\n", bracket, url); } void cmd_userrpc_help(void) { printf("user-rpc [--help] [--content <file>] [--out <file>] [--rpc-timeout <seconds>]\n"); } void cmd_timed_help(void) { printf("timed [--help] [on | off]\n"); } #ifdef NC_ENABLED_SSH void cmd_auth_help(void) { printf("auth (--help | pref [(publickey | interactive | password) <preference>] | keys [add <public_key_path> <private_key_path>] [remove <key_index>])\n"); } void cmd_knownhosts_help(void) { printf("knownhosts [--help] [--del <key_index>]\n"); } #endif /* NC_ENABLED_SSH */ #ifdef NC_ENABLED_TLS void cmd_cert_help(void) { printf("cert [--help | display | add <cert_path> | remove <cert_name> | displayown | replaceown (<cert_path.pem> | <cert_path.crt> <key_path.key>)]\n"); } void cmd_crl_help(void) { printf("crl [--help | display | add <crl_path> | remove <crl_name>]\n"); } #endif /* NC_ENABLED_TLS */ #ifdef NC_ENABLED_SSH int cmd_auth(const char *arg, char **UNUSED(tmp_config_file)) { int i; short int pref; char *args = strdupa(arg); char *cmd = NULL, *ptr = NULL, *str; const char *pub_key, *priv_key; cmd = strtok_r(args, " ", &ptr); cmd = strtok_r(NULL, " ", &ptr); if (cmd == NULL || strcmp(cmd, "--help") == 0 || strcmp(cmd, "-h") == 0) { cmd_auth_help(); } else if (strcmp(cmd, "pref") == 0) { cmd = strtok_r(NULL, " ", &ptr); if (cmd == NULL) { printf("The SSH authentication method preferences:\n"); if ((pref = nc_client_ssh_get_auth_pref(NC_SSH_AUTH_PUBLICKEY)) < 0) { printf("\t'publickey': disabled\n"); } else { printf("\t'publickey': %d\n", pref); } if ((pref = nc_client_ssh_get_auth_pref(NC_SSH_AUTH_PASSWORD)) < 0) { printf("\t'password': disabled\n"); } else { printf("\t'password': %d\n", pref); } if ((pref = nc_client_ssh_get_auth_pref(NC_SSH_AUTH_INTERACTIVE)) < 0) { printf("\t'interactive': disabled\n"); } else { printf("\t'interactive': %d\n", pref); } } else if (strcmp(cmd, "publickey") == 0) { cmd = strtok_r(NULL, " ", &ptr); if (cmd == NULL) { ERROR("auth pref publickey", "Missing the preference argument"); return EXIT_FAILURE; } else { nc_client_ssh_set_auth_pref(NC_SSH_AUTH_PUBLICKEY, atoi(cmd)); } } else if (strcmp(cmd, "interactive") == 0) { cmd = strtok_r(NULL, " ", &ptr); if (cmd == NULL) { ERROR("auth pref interactive", "Missing the preference argument"); return EXIT_FAILURE; } else { nc_client_ssh_set_auth_pref(NC_SSH_AUTH_INTERACTIVE, atoi(cmd)); } } else if (strcmp(cmd, "password") == 0) { cmd = strtok_r(NULL, " ", &ptr); if (cmd == NULL) { ERROR("auth pref password", "Missing the preference argument"); return EXIT_FAILURE; } else { nc_client_ssh_set_auth_pref(NC_SSH_AUTH_PASSWORD, atoi(cmd)); } } else { ERROR("auth pref", "Unknown authentication method (%s)", cmd); return EXIT_FAILURE; } } else if (strcmp(cmd, "keys") == 0) { cmd = strtok_r(NULL, " ", &ptr); if (cmd == NULL) { printf("The keys used for SSH authentication:\n"); if (nc_client_ssh_get_keypair_count() == 0) { printf("(none)\n"); } else { for (i = 0; i < nc_client_ssh_get_keypair_count(); ++i) { nc_client_ssh_get_keypair(i, &pub_key, &priv_key); printf("#%d: %s (private %s)\n", i, pub_key, priv_key); } } } else if (strcmp(cmd, "add") == 0) { cmd = strtok_r(NULL, " ", &ptr); if (cmd == NULL) { ERROR("auth keys add", "Missing the private key path"); return EXIT_FAILURE; } str = cmd; cmd = strtok_r(NULL, " ", &ptr); if (cmd == NULL) { ERROR("auth keys add", "Missing the public key path"); return EXIT_FAILURE; } if (nc_client_ssh_ch_add_keypair(str, cmd) != EXIT_SUCCESS || nc_client_ssh_add_keypair(str, cmd) != EXIT_SUCCESS) { ERROR("auth keys add", "Failed to add keys"); return EXIT_FAILURE; } if (eaccess(cmd, R_OK) != 0) { ERROR("auth keys add", "The new private key is not accessible (%s), but added anyway", strerror(errno)); } if (eaccess(str, R_OK) != 0) { ERROR("auth keys add", "The public key for the new private key is not accessible (%s), but added anyway", strerror(errno)); } } else if (strcmp(cmd, "remove") == 0) { cmd = strtok_r(NULL, " ", &ptr); if (cmd == NULL) { ERROR("auth keys remove", "Missing the key index"); return EXIT_FAILURE; } i = strtol(cmd, &ptr, 10); if (ptr[0] || nc_client_ssh_ch_del_keypair(i) || nc_client_ssh_del_keypair(i)) { ERROR("auth keys remove", "Wrong index"); return EXIT_FAILURE; } } else { ERROR("auth keys", "Unknown argument %s", cmd); return EXIT_FAILURE; } } else { ERROR("auth", "Unknown argument %s", cmd); return EXIT_FAILURE; } return EXIT_SUCCESS; } int cmd_knownhosts(const char *arg, char **UNUSED(tmp_config_file)) { char* ptr, *kh_file, *line = NULL, **pkeys = NULL, *text; int del_idx = -1, i, j, pkey_len = 0, written, text_len; size_t line_len; FILE* file; struct passwd* pwd; struct arglist cmd; struct option long_options[] = { {"help", 0, 0, 'h'}, {"del", 1, 0, 'd'}, {0, 0, 0, 0} }; int option_index = 0, c; optind = 0; init_arglist(&cmd); if (addargs(&cmd, "%s", arg)) { return EXIT_FAILURE; } while ((c = getopt_long(cmd.count, cmd.list, "hd:", long_options, &option_index)) != -1) { switch (c) { case 'h': cmd_knownhosts_help(); clear_arglist(&cmd); return EXIT_SUCCESS; break; case 'd': del_idx = strtol(optarg, &ptr, 10); if (*ptr != '\0' || del_idx < 0) { ERROR("knownhosts", "Wrong index"); clear_arglist(&cmd); return EXIT_FAILURE; } break; default: ERROR("knownhosts", "Unknown option -%c", c); cmd_knownhosts_help(); clear_arglist(&cmd); return EXIT_FAILURE; } } clear_arglist(&cmd); errno = 0; pwd = getpwuid(getuid()); if (pwd == NULL) { if (errno == 0) { ERROR("knownhosts", "Failed to get the home directory of UID %d, it does not exist", getuid()); } else { ERROR("knownhosts", "Failed to get a pwd entry (%s)", strerror(errno)); } return EXIT_FAILURE; } asprintf(&kh_file, "%s/.ssh/known_hosts", pwd->pw_dir); if ((file = fopen(kh_file, "r+")) == NULL) { ERROR("knownhosts", "Cannot open \"%s\" (%s)", kh_file, strerror(errno)); free(kh_file); return EXIT_FAILURE; } free(kh_file); /* list */ if (del_idx == -1) { printf("ID Hostname Algorithm Key\n\n"); errno = 0; i = 0; while (getline(&line, &line_len, file) > 0) { /* host number */ printf("%d: ", i); /* host name */ ptr = strtok(line, " "); if (ptr == NULL) { printf("INVALID\n"); ++i; continue; } if (ptr[0] == '|' && ptr[2] == '|') { printf("(hashed hostname) "); } else { printf("%s ", ptr); } /* host key algorithm */ ptr = strtok(NULL, " "); if (ptr == NULL) { printf("INVALID\n"); ++i; continue; } printf("%s: ", ptr); /* host key */ ptr = strtok(NULL, " "); if (ptr == NULL) { printf("INVALID\n"); ++i; continue; } for (j = 0; j < pkey_len; ++j) { if (strcmp(ptr, pkeys[j]) == 0) { break; } } if (j == pkey_len) { ++pkey_len; pkeys = realloc(pkeys, pkey_len*sizeof(char*)); pkeys[j] = strdup(ptr); } printf("(key %d)\n", j); ++i; } if (i == 0) { printf("(none)\n"); } printf("\n"); for (j = 0; j < pkey_len; ++j) { free(pkeys[j]); } free(pkeys); free(line); /* delete */ } else { fseek(file, 0, SEEK_END); text_len = ftell(file); if (text_len < 0) { ERROR("knownhosts", "ftell on the known hosts file failed (%s)", strerror(errno)); fclose(file); return EXIT_FAILURE; } fseek(file, 0, SEEK_SET); text = malloc(text_len + 1); if (fread(text, 1, text_len, file) < (unsigned)text_len) { ERROR("knownhosts", "Cannot read known hosts file (%s)", strerror(ferror(file))); free(text); fclose(file); return EXIT_FAILURE; } text[text_len] = '\0'; fseek(file, 0, SEEK_SET); for (i = 0, ptr = text; (i < del_idx) && ptr; ++i, ptr = strchr(ptr + 1, '\n')); if (!ptr || (strlen(ptr) < 2)) { ERROR("knownhosts", "Key index %d does not exist", del_idx); free(text); fclose(file); return EXIT_FAILURE; } if (ptr[0] == '\n') { ++ptr; } /* write the old beginning */ written = fwrite(text, 1, ptr - text, file); if (written < ptr-text) { ERROR("knownhosts", "Failed to write to known hosts file (%s)", strerror(ferror(file))); free(text); fclose(file); return EXIT_FAILURE; } ptr = strchr(ptr, '\n'); if (ptr) { ++ptr; /* write the rest */ if (fwrite(ptr, 1, strlen(ptr), file) < strlen(ptr)) { ERROR("knownhosts", "Failed to write to known hosts file (%s)", strerror(ferror(file))); free(text); fclose(file); return EXIT_FAILURE; } written += strlen(ptr); } free(text); ftruncate(fileno(file), written); } fclose(file); return EXIT_SUCCESS; } static int cmd_connect_listen_ssh(struct arglist *cmd, int is_connect) { const char *func_name = (is_connect ? "cmd_connect" : "cmd_listen"); static unsigned short listening = 0; char *host = NULL, *user = NULL; struct passwd *pw; unsigned short port = 0; int c, timeout = 0, ret; int option_index = 0; struct option long_options[] = { {"ssh", 0, 0, 's'}, {"host", 1, 0, 'o'}, {"port", 1, 0, 'p'}, {"login", 1, 0, 'l'}, {"timeout", 1, 0, 'i'}, {0, 0, 0, 0} }; if (is_connect) { /* remove timeout option for use as connect command */ memset(&long_options[4], 0, sizeof long_options[4]); } /* set back to start to be able to use getopt() repeatedly */ optind = 0; while ((c = getopt_long(cmd->count, cmd->list, (is_connect ? "so:p:l:" : "si:o:p:l:"), long_options, &option_index)) != -1) { switch (c) { case 's': /* we know already */ break; case 'o': host = optarg; break; case 'i': timeout = atoi(optarg); break; case 'p': port = (unsigned short)atoi(optarg); if (!is_connect && listening && (listening != port)) { //nc_callhome_listen_stop(); listening = 0; } break; case 'l': user = optarg; break; default: ERROR(func_name, "Unknown option -%c.", c); if (is_connect) { cmd_connect_help(); } else { cmd_listen_help(); } return EXIT_FAILURE; } } /* default user */ if (!user) { pw = getpwuid(getuid()); if (pw) { user = pw->pw_name; } } if (is_connect) { /* default port */ if (!port) { port = NC_PORT_SSH; } /* default hostname */ if (!host) { host = "localhost"; } nc_client_ssh_set_username(user); /* create the session */ session = nc_connect_ssh(host, port, NULL); if (session == NULL) { ERROR(func_name, "Connecting to the %s:%d as user \"%s\" failed.", host, port, user); return EXIT_FAILURE; } } else { /* default port */ if (!port) { port = NC_PORT_CH_SSH; } /* default hostname */ if (!host) { host = "::0"; } /* default timeout */ if (!timeout) { timeout = CLI_CH_TIMEOUT; } /* create the session */ nc_client_ssh_ch_set_username(user); nc_client_ssh_ch_add_bind_listen(host, port); printf("Waiting %ds for an SSH Call Home connection on port %u...\n", timeout, port); ret = nc_accept_callhome(timeout * 1000, NULL, &session); nc_client_ssh_ch_del_bind(host, port); if (ret != 1) { if (ret == 0) { ERROR(func_name, "Receiving SSH Call Home on port %d as user \"%s\" timeout elapsed.", port, user); } else { ERROR(func_name, "Receiving SSH Call Home on port %d as user \"%s\" failed.", port, user); } return EXIT_FAILURE; } } return EXIT_SUCCESS; } #endif /* NC_ENABLED_SSH */ #ifdef NC_ENABLED_TLS static int cp(const char *to, const char *from) { int fd_to, fd_from; struct stat st; ssize_t from_len; int saved_errno; void *buf; fd_from = open(from, O_RDONLY); if (fd_from < 0) { return -1; } fd_to = open(to, O_WRONLY | O_CREAT | O_TRUNC, 0600); if (fd_to < 0) { goto out_error; } if (fstat(fd_from, &st) < 0) { goto out_error; } from_len = st.st_size; buf = malloc(from_len); if (read(fd_from, buf, from_len) < from_len) { goto out_error; } if (write(fd_to, buf, from_len) < from_len) { goto out_error; } free(buf); close(fd_from); close(fd_to); return 0; out_error: saved_errno = errno; close(fd_from); if (fd_to >= 0) { close(fd_to); } errno = saved_errno; return -1; } static void parse_cert(const char *name, const char *path) { int i, j, has_san, first_san; ASN1_OCTET_STRING *ip; ASN1_INTEGER *bs; BIO *bio_out; FILE *fp; X509 *cert; STACK_OF(GENERAL_NAME) *san_names = NULL; GENERAL_NAME *san_name; fp = fopen(path, "r"); if (fp == NULL) { ERROR("parse_cert", "Unable to open: %s", path); return; } cert = PEM_read_X509(fp, NULL, NULL, NULL); if (cert == NULL) { ERROR("parse_cert", "Unable to parse certificate: %s", path); fclose(fp); return; } bio_out = BIO_new_fp(stdout, BIO_NOCLOSE); bs = X509_get_serialNumber(cert); BIO_printf(bio_out, "-----%s----- serial: ", name); for (i = 0; i < bs->length; i++) { BIO_printf(bio_out, "%02x", bs->data[i]); } BIO_printf(bio_out, "\n"); BIO_printf(bio_out, "Subject: "); X509_NAME_print(bio_out, X509_get_subject_name(cert), 0); BIO_printf(bio_out, "\n"); BIO_printf(bio_out, "Issuer: "); X509_NAME_print(bio_out, X509_get_issuer_name(cert), 0); BIO_printf(bio_out, "\n"); BIO_printf(bio_out, "Valid until: "); #if OPENSSL_VERSION_NUMBER < 0x10100000L // < 1.1.0 ASN1_TIME_print(bio_out, X509_get_notAfter(cert)); #else ASN1_TIME_print(bio_out, X509_get0_notAfter(cert)); #endif BIO_printf(bio_out, "\n"); has_san = 0; first_san = 1; san_names = X509_get_ext_d2i(cert, NID_subject_alt_name, NULL, NULL); if (san_names != NULL) { for (i = 0; i < sk_GENERAL_NAME_num(san_names); ++i) { san_name = sk_GENERAL_NAME_value(san_names, i); if (san_name->type == GEN_EMAIL || san_name->type == GEN_DNS || san_name->type == GEN_IPADD) { if (!has_san) { BIO_printf(bio_out, "X509v3 Subject Alternative Name:\n\t"); has_san = 1; } if (!first_san) { BIO_printf(bio_out, ", "); } if (first_san) { first_san = 0; } if (san_name->type == GEN_EMAIL) { #if OPENSSL_VERSION_NUMBER < 0x10100000L // < 1.1.0 BIO_printf(bio_out, "RFC822:%s", (char*) ASN1_STRING_data(san_name->d.rfc822Name)); #else BIO_printf(bio_out, "RFC822:%s", (char*) ASN1_STRING_get0_data(san_name->d.rfc822Name)); #endif } if (san_name->type == GEN_DNS) { #if OPENSSL_VERSION_NUMBER < 0x10100000L // < 1.1.0 BIO_printf(bio_out, "DNS:%s", (char*) ASN1_STRING_data(san_name->d.dNSName)); #else BIO_printf(bio_out, "DNS:%s", (char*) ASN1_STRING_get0_data(san_name->d.dNSName)); #endif } if (san_name->type == GEN_IPADD) { BIO_printf(bio_out, "IP:"); ip = san_name->d.iPAddress; if (ip->length == 4) { BIO_printf(bio_out, "%d.%d.%d.%d", ip->data[0], ip->data[1], ip->data[2], ip->data[3]); } else if (ip->length == 16) { for (j = 0; j < ip->length; ++j) { if (j > 0 && j < 15 && j%2 == 1) { BIO_printf(bio_out, "%02x:", ip->data[j]); } else { BIO_printf(bio_out, "%02x", ip->data[j]); } } } } } } sk_GENERAL_NAME_pop_free(san_names, GENERAL_NAME_free); } if (has_san) { BIO_printf(bio_out, "\n"); } BIO_printf(bio_out, "\n"); X509_free(cert); BIO_vfree(bio_out); fclose(fp); } void parse_crl(const char *name, const char *path) { int i; BIO *bio_out; FILE *fp; X509_CRL *crl; const ASN1_INTEGER* bs; X509_REVOKED* rev; fp = fopen(path, "r"); if (fp == NULL) { ERROR("parse_crl", "Unable to open \"%s\": %s", path, strerror(errno)); return; } crl = PEM_read_X509_CRL(fp, NULL, NULL, NULL); if (crl == NULL) { ERROR("parse_crl", "Unable to parse certificate: %s", path); fclose(fp); return; } bio_out = BIO_new_fp(stdout, BIO_NOCLOSE); BIO_printf(bio_out, "-----%s-----\n", name); BIO_printf(bio_out, "Issuer: "); X509_NAME_print(bio_out, X509_CRL_get_issuer(crl), 0); BIO_printf(bio_out, "\n"); BIO_printf(bio_out, "Last update: "); #if OPENSSL_VERSION_NUMBER < 0x10100000L // < 1.1.0 ASN1_TIME_print(bio_out, X509_CRL_get_lastUpdate(crl)); #else ASN1_TIME_print(bio_out, X509_CRL_get0_lastUpdate(crl)); #endif BIO_printf(bio_out, "\n"); BIO_printf(bio_out, "Next update: "); #if OPENSSL_VERSION_NUMBER < 0x10100000L // < 1.1.0 ASN1_TIME_print(bio_out, X509_CRL_get_nextUpdate(crl)); #else ASN1_TIME_print(bio_out, X509_CRL_get0_nextUpdate(crl)); #endif BIO_printf(bio_out, "\n"); BIO_printf(bio_out, "REVOKED:\n"); if ((rev = sk_X509_REVOKED_pop(X509_CRL_get_REVOKED(crl))) == NULL) { BIO_printf(bio_out, "\tNone\n"); } while (rev != NULL) { #if OPENSSL_VERSION_NUMBER < 0x10100000L // < 1.1.0 bs = rev->serialNumber; #else bs = X509_REVOKED_get0_serialNumber(rev); #endif BIO_printf(bio_out, "\tSerial no.: "); for (i = 0; i < bs->length; i++) { BIO_printf(bio_out, "%02x", bs->data[i]); } BIO_printf(bio_out, " Date: "); #if OPENSSL_VERSION_NUMBER < 0x10100000L // < 1.1.0 ASN1_TIME_print(bio_out, rev->revocationDate); #else ASN1_TIME_print(bio_out, X509_REVOKED_get0_revocationDate(rev)); #endif BIO_printf(bio_out, "\n"); X509_REVOKED_free(rev); rev = sk_X509_REVOKED_pop(X509_CRL_get_REVOKED(crl)); } X509_CRL_free(crl); BIO_vfree(bio_out); fclose(fp); } int cmd_cert(const char *arg, char **UNUSED(tmp_config_file)) { int ret; char* args = strdupa(arg); char* cmd = NULL, *ptr = NULL, *path, *path2, *dest; char* trusted_dir, *netconf_dir, *c_rehash_cmd; DIR* dir = NULL; struct dirent *d; cmd = strtok_r(args, " ", &ptr); cmd = strtok_r(NULL, " ", &ptr); if (!cmd || !strcmp(cmd, "--help") || !strcmp(cmd, "-h")) { cmd_cert_help(); } else if (!strcmp(cmd, "display")) { int none = 1; char *name; if (!(trusted_dir = get_default_trustedCA_dir(NULL))) { ERROR("cert display", "Could not get the default trusted CA directory"); return EXIT_FAILURE; } dir = opendir(trusted_dir); while ((d = readdir(dir))) { if (!strcmp(d->d_name + strlen(d->d_name) - 4, ".pem")) { none = 0; name = strdup(d->d_name); name[strlen(name) - 4] = '\0'; asprintf(&path, "%s/%s", trusted_dir, d->d_name); parse_cert(name, path); free(name); free(path); } } closedir(dir); if (none) { printf("No certificates found in the default trusted CA directory.\n"); } free(trusted_dir); } else if (!strcmp(cmd, "add")) { path = strtok_r(NULL, " ", &ptr); if (!path || (strlen(path) < 5)) { ERROR("cert add", "Missing or wrong path to the certificate"); return EXIT_FAILURE; } if (eaccess(path, R_OK)) { ERROR("cert add", "Cannot access certificate \"%s\": %s", path, strerror(errno)); return EXIT_FAILURE; } trusted_dir = get_default_trustedCA_dir(NULL); if (!trusted_dir) { ERROR("cert add", "Could not get the default trusted CA directory"); return EXIT_FAILURE; } if ((asprintf(&dest, "%s/%s", trusted_dir, strrchr(path, '/') + 1) == -1) || (asprintf(&c_rehash_cmd, "c_rehash %s &> /dev/null", trusted_dir) == -1)) { ERROR("cert add", "Memory allocation failed"); free(trusted_dir); return EXIT_FAILURE; } free(trusted_dir); if (strcmp(dest + strlen(dest) - 4, ".pem")) { ERROR("cert add", "CA certificates are expected to be in *.pem format"); strcpy(dest + strlen(dest) - 4, ".pem"); } if (cp(dest, path)) { ERROR("cert add", "Could not copy the certificate: %s", strerror(errno)); free(dest); free(c_rehash_cmd); return EXIT_FAILURE; } free(dest); if (((ret = system(c_rehash_cmd)) == -1) || WEXITSTATUS(ret)) { ERROR("cert add", "c_rehash execution failed"); free(c_rehash_cmd); return EXIT_FAILURE; } free(c_rehash_cmd); } else if (!strcmp(cmd, "remove")) { path = strtok_r(NULL, " ", &ptr); if (!path) { ERROR("cert remove", "Missing the certificate name"); return EXIT_FAILURE; } /* delete ".pem" if the user unnecessarily included it */ if ((strlen(path) > 4) && !strcmp(path + strlen(path) - 4, ".pem")) { path[strlen(path) - 4] = '\0'; } trusted_dir = get_default_trustedCA_dir(NULL); if (!trusted_dir) { ERROR("cert remove", "Could not get the default trusted CA directory"); return EXIT_FAILURE; } if ((asprintf(&dest, "%s/%s.pem", trusted_dir, path) == -1) || (asprintf(&c_rehash_cmd, "c_rehash %s &> /dev/null", trusted_dir) == -1)) { ERROR("cert remove", "Memory allocation failed"); free(trusted_dir); return EXIT_FAILURE; } free(trusted_dir); if (remove(dest)) { ERROR("cert remove", "Cannot remove certificate \"%s\": %s (use the name from \"cert display\" output)", path, strerror(errno)); free(dest); free(c_rehash_cmd); return EXIT_FAILURE; } free(dest); if (((ret = system(c_rehash_cmd)) == -1) || WEXITSTATUS(ret)) { ERROR("cert remove", "c_rehash execution failed"); free(c_rehash_cmd); return EXIT_FAILURE; } free(c_rehash_cmd); } else if (!strcmp(cmd, "displayown")) { int crt = 0, key = 0, pem = 0; netconf_dir = get_netconf_dir(); if (!netconf_dir) { ERROR("cert displayown", "Could not get the client home directory"); return EXIT_FAILURE; } if (asprintf(&dest, "%s/client.pem", netconf_dir) == -1) { ERROR("cert displayown", "Memory allocation failed"); free(netconf_dir); return EXIT_FAILURE; } free(netconf_dir); if (!eaccess(dest, R_OK)) { pem = 1; } strcpy(dest + strlen(dest) - 4, ".key"); if (!eaccess(dest, R_OK)) { key = 1; } strcpy(dest + strlen(dest) - 4, ".crt"); if (!eaccess(dest, R_OK)) { crt = 1; } if (!crt && !key && !pem) { printf("FAIL: No client certificate found, use \"cert replaceown\" to set some.\n"); } else if (crt && !key && !pem) { printf("FAIL: Client *.crt certificate found, but is of no use without its private key *.key.\n"); } else if (!crt && key && !pem) { printf("FAIL: Private key *.key found, but is of no use without a certificate.\n"); } else if (!crt && !key && pem) { printf("OK: Using *.pem client certificate with the included private key.\n"); } else if (crt && key && !pem) { printf("OK: Using *.crt certificate with a separate private key.\n"); } else if (crt && !key && pem) { printf("WORKING: Using *.pem client certificate with the included private key (leftover certificate *.crt detected).\n"); } else if (!crt && key && pem) { printf("WORKING: Using *.pem client certificate with the included private key (leftover private key detected).\n"); } else if (crt && key && pem) { printf("WORKING: Using *.crt certificate with a separate private key (lower-priority *.pem certificate with a private key detected).\n"); } if (crt) { parse_cert("CRT", dest); } if (pem) { strcpy(dest + strlen(dest) - 4, ".pem"); parse_cert("PEM", dest); } free(dest); } else if (!strcmp(cmd, "replaceown")) { path = strtok_r(NULL, " ", &ptr); if (!path || (strlen(path) < 5)) { ERROR("cert replaceown", "Missing the certificate or invalid path."); return EXIT_FAILURE; } if (eaccess(path, R_OK)) { ERROR("cert replaceown", "Cannot access the certificate \"%s\": %s", path, strerror(errno)); return EXIT_FAILURE; } path2 = strtok_r(NULL, " ", &ptr); if (path2) { if (strlen(path2) < 5) { ERROR("cert replaceown", "Invalid private key path."); return EXIT_FAILURE; } if (eaccess(path2, R_OK)) { ERROR("cert replaceown", "Cannot access the private key \"%s\": %s", path2, strerror(errno)); return EXIT_FAILURE; } } netconf_dir = get_netconf_dir(); if (!netconf_dir) { ERROR("cert replaceown", "Could not get the client home directory"); return EXIT_FAILURE; } if (asprintf(&dest, "%s/client.XXX", netconf_dir) == -1) { ERROR("cert replaceown", "Memory allocation failed"); free(netconf_dir); return EXIT_FAILURE; } free(netconf_dir); if (path2) { /* CRT & KEY */ strcpy(dest + strlen(dest) - 4, ".pem"); errno = 0; if (remove(dest) && (errno == EACCES)) { ERROR("cert replaceown", "Could not remove old certificate (*.pem)"); } strcpy(dest + strlen(dest) - 4, ".crt"); if (cp(dest, path)) { ERROR("cert replaceown", "Could not copy the certificate \"%s\": %s", path, strerror(errno)); free(dest); return EXIT_FAILURE; } strcpy(dest + strlen(dest) - 4, ".key"); if (cp(dest, path2)) { ERROR("cert replaceown", "Could not copy the private key \"%s\": %s", path, strerror(errno)); free(dest); return EXIT_FAILURE; } } else { /* PEM */ strcpy(dest + strlen(dest) - 4, ".key"); errno = 0; if (remove(dest) && (errno == EACCES)) { ERROR("cert replaceown", "Could not remove old private key"); } strcpy(dest + strlen(dest) - 4, ".crt"); if (remove(dest) && (errno == EACCES)) { ERROR("cert replaceown", "Could not remove old certificate (*.crt)"); } strcpy(dest + strlen(dest) - 4, ".pem"); if (cp(dest, path)) { ERROR("cert replaceown", "Could not copy the certificate \"%s\": %s", path, strerror(errno)); free(dest); return EXIT_FAILURE; } } free(dest); } else { ERROR("cert", "Unknown argument %s", cmd); return EXIT_FAILURE; } return EXIT_SUCCESS; } int cmd_crl(const char *arg, char **UNUSED(tmp_config_file)) { int ret; char *args = strdupa(arg); char *cmd = NULL, *ptr = NULL, *path, *dest; char *crl_dir, *c_rehash_cmd; DIR *dir = NULL; struct dirent *d; cmd = strtok_r(args, " ", &ptr); cmd = strtok_r(NULL, " ", &ptr); if (!cmd || !strcmp(cmd, "--help") || !strcmp(cmd, "-h")) { cmd_crl_help(); } else if (!strcmp(cmd, "display")) { int none = 1; char *name; if (!(crl_dir = get_default_CRL_dir(NULL))) { ERROR("crl display", "Could not get the default CRL directory"); return EXIT_FAILURE; } dir = opendir(crl_dir); while ((d = readdir(dir))) { if (!strcmp(d->d_name + strlen(d->d_name) - 4, ".pem")) { none = 0; name = strdup(d->d_name); name[strlen(name) - 4] = '\0'; asprintf(&path, "%s/%s", crl_dir, d->d_name); parse_crl(name, path); free(name); free(path); } } closedir(dir); if (none) { printf("No CRLs found in the default CRL directory.\n"); } free(crl_dir); } else if (!strcmp(cmd, "add")) { path = strtok_r(NULL, " ", &ptr); if (!path || (strlen(path) < 5)) { ERROR("crl add", "Missing or wrong path to the certificate"); return EXIT_FAILURE; } if (eaccess(path, R_OK)) { ERROR("crl add", "Cannot access certificate \"%s\": %s", path, strerror(errno)); return EXIT_FAILURE; } crl_dir = get_default_CRL_dir(NULL); if (!crl_dir) { ERROR("crl add", "Could not get the default CRL directory"); return EXIT_FAILURE; } if ((asprintf(&dest, "%s/%s", crl_dir, strrchr(path, '/') + 1) == -1) || (asprintf(&c_rehash_cmd, "c_rehash %s &> /dev/null", crl_dir) == -1)) { ERROR("crl add", "Memory allocation failed"); free(crl_dir); return EXIT_FAILURE; } free(crl_dir); if (strcmp(dest + strlen(dest) - 4, ".pem")) { ERROR("crl add", "CRLs are expected to be in *.pem format"); strcpy(dest + strlen(dest) - 4, ".pem"); } if (cp(dest, path)) { ERROR("crl add", "Could not copy the CRL \"%s\": %s", path, strerror(errno)); free(dest); free(c_rehash_cmd); return EXIT_FAILURE; } free(dest); if (((ret = system(c_rehash_cmd)) == -1) || WEXITSTATUS(ret)) { ERROR("crl add", "c_rehash execution failed"); free(c_rehash_cmd); return EXIT_FAILURE; } free(c_rehash_cmd); } else if (!strcmp(cmd, "remove")) { path = strtok_r(NULL, " ", &ptr); if (!path) { ERROR("crl remove", "Missing the certificate name"); return EXIT_FAILURE; } // delete ".pem" if the user unnecessarily included it if ((strlen(path) > 4) && !strcmp(path + strlen(path) - 4, ".pem")) { path[strlen(path) - 4] = '\0'; } crl_dir = get_default_CRL_dir(NULL); if (!crl_dir) { ERROR("crl remove", "Could not get the default CRL directory"); return EXIT_FAILURE; } if ((asprintf(&dest, "%s/%s.pem", crl_dir, path) == -1) || (asprintf(&c_rehash_cmd, "c_rehash %s &> /dev/null", crl_dir) == -1)) { ERROR("crl remove", "Memory allocation failed"); free(crl_dir); return EXIT_FAILURE; } free(crl_dir); if (remove(dest)) { ERROR("crl remove", "Cannot remove CRL \"%s\": %s (use the name from \"crl display\" output)", path, strerror(errno)); free(dest); free(c_rehash_cmd); return EXIT_FAILURE; } free(dest); if (((ret = system(c_rehash_cmd)) == -1) || WEXITSTATUS(ret)) { ERROR("crl remove", "c_rehash execution failed"); free(c_rehash_cmd); return EXIT_FAILURE; } free(c_rehash_cmd); } else { ERROR("crl", "Unknown argument %s", cmd); return EXIT_FAILURE; } return EXIT_SUCCESS; } static int cmd_connect_listen_tls(struct arglist *cmd, int is_connect) { const char *func_name = (is_connect ? "cmd_connect" : "cmd_listen"); static unsigned short listening = 0; char *host = NULL; DIR *dir = NULL; struct dirent* d; int c, n, timeout = 0, ret = EXIT_FAILURE; char *cert = NULL, *key = NULL, *trusted_dir = NULL, *crl_dir = NULL, *trusted_store = NULL; unsigned short port = 0; int option_index = 0; struct option long_options[] = { {"tls", 0, 0, 't'}, {"host", 1, 0, 'o'}, {"port", 1, 0, 'p'}, {"cert", 1, 0, 'c'}, {"key", 1, 0, 'k'}, {"trusted", 1, 0, 'r'}, {"timeout", 1, 0, 'i'}, {0, 0, 0, 0} }; if (is_connect) { /* remove timeout option for use as connect command */ memset(&long_options[6], 0, sizeof long_options[6]); } /* set back to start to be able to use getopt() repeatedly */ optind = 0; while ((c = getopt_long(cmd->count, cmd->list, (is_connect ? "to:p:c:k:r:" : "ti:o:p:c:k:r:"), long_options, &option_index)) != -1) { switch (c) { case 't': /* we know already */ break; case 'o': host = optarg; break; case 'i': timeout = atoi(optarg); break; case 'p': port = (unsigned short)atoi(optarg); if (!is_connect && listening && (listening != port)) { //nc_callhome_listen_stop(); listening = 0; } break; case 'c': asprintf(&cert, "%s", optarg); break; case 'k': asprintf(&key, "%s", optarg); break; case 'r': trusted_store = optarg; break; default: ERROR(func_name, "Unknown option -%c.", c); if (is_connect) { cmd_connect_help(); } else { cmd_listen_help(); } return EXIT_FAILURE; } } if (!cert) { if (key) { ERROR(func_name, "Key specified without a certificate."); goto error_cleanup; } get_default_client_cert(&cert, &key); if (!cert) { ERROR(func_name, "Could not find the default client certificate, check with \"cert displayown\" command."); goto error_cleanup; } } if (!trusted_store) { trusted_dir = get_default_trustedCA_dir(NULL); if (!(dir = opendir(trusted_dir))) { ERROR(func_name, "Could not use the trusted CA directory."); goto error_cleanup; } /* check whether we have any trusted CA, verification should fail otherwise */ n = 0; while ((d = readdir(dir))) { if (++n > 2) { break; } } closedir(dir); if (n <= 2) { ERROR(func_name, "Trusted CA directory empty, use \"cert add\" command to add certificates."); } } else { if (eaccess(trusted_store, R_OK)) { ERROR(func_name, "Could not access trusted CA store \"%s\": %s", trusted_store, strerror(errno)); goto error_cleanup; } if ((strlen(trusted_store) < 5) || strcmp(trusted_store + strlen(trusted_store) - 4, ".pem")) { ERROR(func_name, "Trusted CA store in an unknown format."); goto error_cleanup; } } if (!(crl_dir = get_default_CRL_dir(NULL))) { ERROR(func_name, "Could not use the CRL directory."); goto error_cleanup; } if (is_connect) { nc_client_tls_set_cert_key_paths(cert, key); nc_client_tls_set_trusted_ca_paths(trusted_store, trusted_dir); nc_client_tls_set_crl_paths(NULL, crl_dir); /* default port */ if (!port) { port = NC_PORT_TLS; } /* default host */ if (!host) { host = "localhost"; } /* create the session */ session = nc_connect_tls(host, port, NULL); if (session == NULL) { ERROR(func_name, "Connecting to the %s:%d failed.", host, port); goto error_cleanup; } } else { nc_client_tls_ch_set_cert_key_paths(cert, key); nc_client_tls_ch_set_trusted_ca_paths(trusted_store, trusted_dir); nc_client_tls_ch_set_crl_paths(NULL, crl_dir); /* default timeout */ if (!timeout) { timeout = CLI_CH_TIMEOUT; } /* default port */ if (!port) { port = NC_PORT_CH_TLS; } /* default host */ if (!host) { host = "::0"; } /* create the session */ nc_client_tls_ch_add_bind_listen(host, port); ERROR(func_name, "Waiting %ds for a TLS Call Home connection on port %u...", timeout, port); ret = nc_accept_callhome(timeout * 1000, NULL, &session); nc_client_tls_ch_del_bind(host, port); if (ret != 1) { if (ret == 0) { ERROR(func_name, "Receiving TLS Call Home on port %d timeout elapsed.", port); } else { ERROR(func_name, "Receiving TLS Call Home on port %d failed.", port); } goto error_cleanup; } } ret = EXIT_SUCCESS; error_cleanup: free(trusted_dir); free(crl_dir); free(cert); free(key); return ret; } #endif /* NC_ENABLED_TLS */ static int cmd_connect_listen_unix(struct arglist *cmd, int is_connect) { const char *func_name = (is_connect ? "cmd_connect" : "cmd_listen"); const char *path = NULL; int c, ret = EXIT_FAILURE; int option_index = 0; struct option long_options[] = { {"unix", 0, 0, 'u'}, {"socket", 1, 0, 'S'}, {0, 0, 0, 0} }; if (!is_connect) { ERROR(func_name, "listen mode not supported for unix socket."); return EXIT_FAILURE; } /* set back to start to be able to use getopt() repeatedly */ optind = 0; while ((c = getopt_long(cmd->count, cmd->list, "uS:", long_options, &option_index)) != -1) { switch (c) { case 'u': /* we know already */ break; case 'S': path = optarg; break; default: ERROR(func_name, "Unknown option -%c.", c); cmd_connect_help(); return EXIT_FAILURE; } } if (!path) path = "/var/run/netopeer2-server.sock"; /* create the session */ session = nc_connect_unix(path, NULL); if (session == NULL) { ERROR(func_name, "Connecting to %s failed.", path); goto error_cleanup; } ret = EXIT_SUCCESS; error_cleanup: return ret; } int cmd_searchpath(const char *arg, char **UNUSED(tmp_config_file)) { const char *path; for (arg += 10; isspace(arg[0]); ++arg); if (!arg[0]) { path = nc_client_get_schema_searchpath(); fprintf(stdout, "%s\n", path && path[0] ? path : "<none>"); return 0; } if (!strcmp(arg, "-h") || !strcmp(arg, "--help")) { cmd_searchpath_help(); return 0; } nc_client_set_schema_searchpath(arg); return 0; } int cmd_outputformat(const char *arg, char **UNUSED(tmp_config_file)) { const char *format; if (strchr(arg, ' ') == NULL) { fprintf(stderr, "Missing the output format.\n"); return 1; } format = strchr(arg, ' ') + 1; if (!strncmp(format, "-h", 2) || !strncmp(format, "--help", 6)) { cmd_outputformat_help(); return 0; } if (!strncmp(format, "xml", 3) && ((format[3] == '\0') || (format[3] == ' '))) { output_format = LYD_XML; output_flag = LYP_FORMAT; } else if (!strncmp(format, "xml_noformat", 12) && ((format[12] == '\0') || (format[12] == ' '))) { output_format = LYD_XML; output_flag = 0; } else if (!strncmp(format, "json", 4) && ((format[4] == '\0') || (format[4] == ' '))) { output_format = LYD_JSON; output_flag = LYP_FORMAT; } else if (!strncmp(format, "json_noformat", 13) && ((format[13] == '\0') || (format[13] == ' '))) { output_format = LYD_JSON; output_flag = 0; } else { fprintf(stderr, "Unknown output format \"%s\".\n", format); return 1; } return 0; } int cmd_version(const char *UNUSED(arg), char **UNUSED(tmp_config_file)) { fprintf(stdout, "Netopeer2 CLI %s\n", CLI_VERSION); fprintf(stdout, "Compile time: %s, %s\n", __DATE__, __TIME__); return 0; } int cmd_verb(const char *arg, char **UNUSED(tmp_config_file)) { const char *verb; if (strlen(arg) < 5) { cmd_verb_help(); return 1; } verb = arg + 5; if (!strcmp(verb, "error") || !strcmp(verb, "0")) { nc_verbosity(0); #ifdef NC_ENABLED_SSH nc_libssh_thread_verbosity(0); #endif } else if (!strcmp(verb, "warning") || !strcmp(verb, "1")) { nc_verbosity(1); #ifdef NC_ENABLED_SSH nc_libssh_thread_verbosity(1); #endif } else if (!strcmp(verb, "verbose") || !strcmp(verb, "2")) { nc_verbosity(2); #ifdef NC_ENABLED_SSH nc_libssh_thread_verbosity(2); #endif } else if (!strcmp(verb, "debug") || !strcmp(verb, "3")) { nc_verbosity(3); #ifdef NC_ENABLED_SSH nc_libssh_thread_verbosity(3); #endif } else { fprintf(stderr, "Unknown verbosity \"%s\"\n", verb); return 1; } return 0; } int cmd_disconnect(const char *UNUSED(arg), char **UNUSED(tmp_config_file)) { if (session == NULL) { ERROR("disconnect", "Not connected to any NETCONF server."); } else { nc_session_free(session, NULL); session = NULL; } return EXIT_SUCCESS; } int cmd_status(const char *UNUSED(arg), char **UNUSED(tmp_config_file)) { const char *s; const char * const *cpblts; NC_TRANSPORT_IMPL transport; int i; if (!session) { printf("Client is not connected to any NETCONF server.\n"); } else { transport = nc_session_get_ti(session); printf("Current NETCONF session:\n"); printf(" ID : %u\n", nc_session_get_id(session)); switch (transport) { #ifdef NC_ENABLED_SSH case NC_TI_LIBSSH: s = "SSH"; printf(" Host : %s\n", nc_session_get_host(session)); printf(" Port : %u\n", nc_session_get_port(session)); break; #endif #ifdef NC_ENABLED_TLS case NC_TI_OPENSSL: s = "TLS"; printf(" Host : %s\n", nc_session_get_host(session)); printf(" Port : %u\n", nc_session_get_port(session)); break; #endif case NC_TI_FD: s = "FD"; break; case NC_TI_UNIX: s = "UNIX"; printf(" Path : %s\n", nc_session_get_path(session)); break; default: s = "Unknown"; break; } printf(" Transport : %s\n", s); printf(" Capabilities:\n"); cpblts = nc_session_get_cpblts(session); for (i = 0; cpblts[i]; ++i) { printf("\t%s\n", cpblts[i]); } } return EXIT_SUCCESS; } static int cmd_connect_listen(const char *arg, int is_connect) { const char *func_name = (is_connect ? "cmd_connect" : "cmd_listen"); int c, ret; const char *optstring; struct arglist cmd; struct option long_options[] = { {"help", 0, 0, 'h'}, #ifdef NC_ENABLED_SSH {"ssh", 0, 0, 's'}, {"timeout", 1, 0, 'i'}, {"host", 1, 0, 'o'}, {"port", 1, 0, 'p'}, {"login", 1, 0, 'l'}, #endif #ifdef NC_ENABLED_TLS {"tls", 0, 0, 't'}, {"timeout", 1, 0, 'i'}, {"host", 1, 0, 'o'}, {"port", 1, 0, 'p'}, {"cert", 1, 0, 'c'}, {"key", 1, 0, 'k'}, {"trusted", 1, 0, 'r'}, #endif {"unix", 0, 0, 'u'}, {"socket", 1, 0, 'S'}, {0, 0, 0, 0} }; int option_index = 0; /* set back to start to be able to use getopt() repeatedly */ optind = 0; if (session) { ERROR(func_name, "Already connected to %s.", nc_session_get_host(session) ? : nc_session_get_path(session)); return EXIT_FAILURE; } /* process given arguments */ init_arglist(&cmd); if (addargs(&cmd, "%s", arg)) { return EXIT_FAILURE; } ret = -1; #if defined(NC_ENABLED_SSH) && defined(NC_ENABLED_TLS) optstring = "hsti:o:p:l:c:k:r:uS:"; #elif defined(NC_ENABLED_SSH) optstring = "hsi:o:p:l:uS:"; #elif defined(NC_ENABLED_TLS) optstring = "hti:o:p:c:k:r:uS:"; #else optstring = "hi:o:p:c:k:r:uS:"; #endif while ((ret == -1) && ((c = getopt_long(cmd.count, cmd.list, optstring, long_options, &option_index)) != -1)) { switch (c) { case 'h': if (is_connect) { cmd_connect_help(); } else { cmd_listen_help(); } clear_arglist(&cmd); ret = EXIT_SUCCESS; break; #ifdef NC_ENABLED_SSH case 's': ret = cmd_connect_listen_ssh(&cmd, is_connect); break; #endif #ifdef NC_ENABLED_TLS case 't': ret = cmd_connect_listen_tls(&cmd, is_connect); break; #endif case 'u': ret = cmd_connect_listen_unix(&cmd, is_connect); break; default: break; } } if (ret == -1) { #ifdef NC_ENABLED_SSH ret = cmd_connect_listen_ssh(&cmd, is_connect); #elif defined(NC_ENABLED_TLS) ret = cmd_connect_listen_tls(&cmd, is_connect); #endif } if (!ret) { interleave = 1; } clear_arglist(&cmd); return ret; } int cmd_connect(const char *arg, char **UNUSED(tmp_config_file)) { return cmd_connect_listen(arg, 1); } int cmd_listen(const char *arg, char **UNUSED(tmp_config_file)) { return cmd_connect_listen(arg, 0); } int cmd_quit(const char *UNUSED(arg), char **UNUSED(tmp_config_file)) { done = 1; return 0; } int cmd_help(const char *arg, char **UNUSED(tmp_config_file)) { int i; char *args = strdupa(arg); char *cmd = NULL; strtok(args, " "); if ((cmd = strtok(NULL, " ")) == NULL) { generic_help: fprintf(stdout, "Available commands:\n"); for (i = 0; commands[i].name; i++) { if (commands[i].helpstring != NULL) { fprintf(stdout, " %-15s %s\n", commands[i].name, commands[i].helpstring); } } } else { /* print specific help for the selected command */ /* get the command of the specified name */ for (i = 0; commands[i].name; i++) { if (strcmp(cmd, commands[i].name) == 0) { break; } } /* execute the command's help if any valid command specified */ if (commands[i].name) { if (commands[i].help_func != NULL) { commands[i].help_func(); } else { printf("%s\n", commands[i].helpstring); } } else { /* if unknown command specified, print the list of commands */ printf("Unknown command \'%s\'\n", cmd); goto generic_help; } } return 0; } int cmd_editor(const char *arg, char **UNUSED(tmp_config_file)) { char *cmd, *args = strdupa(arg), *ptr = NULL; cmd = strtok_r(args, " ", &ptr); cmd = strtok_r(NULL, " ", &ptr); if (cmd == NULL) { printf("Current editor: "); printf("%s\n", config_editor); } else if (strcmp(cmd, "--help") == 0 || strcmp(cmd, "-h") == 0) { cmd_editor_help(); } else { free(config_editor); config_editor = strdup(cmd); } return EXIT_SUCCESS; } int cmd_cancelcommit(const char *arg, char **UNUSED(tmp_config_file)) { struct nc_rpc *rpc; int c, ret = EXIT_FAILURE, timeout = CLI_RPC_REPLY_TIMEOUT; const char *persist_id = NULL; struct arglist cmd; struct option long_options[] = { {"help", 0, 0, 'h'}, {"persist-id", 1, 0, 'i'}, {"rpc-timeout", 1, 0, 'r'}, {0, 0, 0, 0} }; int option_index = 0; /* set back to start to be able to use getopt() repeatedly */ optind = 0; /* process given arguments */ init_arglist(&cmd); if (addargs(&cmd, "%s", arg)) { return EXIT_FAILURE; } while ((c = getopt_long(cmd.count, cmd.list, "hi:r:", long_options, &option_index)) != -1) { switch (c) { case 'h': cmd_cancelcommit_help(); ret = EXIT_SUCCESS; goto fail; case 'i': persist_id = optarg; break; case 'r': timeout = atoi(optarg); if (!timeout) { ERROR(__func__, "Invalid timeout \"%s\".", optarg); goto fail; } break; default: ERROR(__func__, "Unknown option -%c.", c); cmd_cancelcommit_help(); goto fail; } } if (cmd.list[optind]) { ERROR(__func__, "Unparsed command arguments."); cmd_cancelcommit_help(); goto fail; } if (!session) { ERROR(__func__, "Not connected to a NETCONF server, no RPCs can be sent."); goto fail; } if (!interleave) { ERROR(__func__, "NETCONF server does not support interleaving RPCs and notifications."); goto fail; } rpc = nc_rpc_cancel(persist_id, NC_PARAMTYPE_CONST); if (!rpc) { ERROR(__func__, "RPC creation failed."); goto fail; } ret = cli_send_recv(rpc, stdout, 0, timeout); nc_rpc_free(rpc); fail: clear_arglist(&cmd); return ret; } int cmd_commit(const char *arg, char **UNUSED(tmp_config_file)) { struct nc_rpc *rpc; int c, ret = EXIT_FAILURE, confirmed = 0, timeout = CLI_RPC_REPLY_TIMEOUT; int32_t confirm_timeout = 0; char *persist = NULL, *persist_id = NULL; struct arglist cmd; struct option long_options[] = { {"help", 0, 0, 'h'}, {"confirmed", 0, 0, 'c'}, {"confirm-timeout", 1, 0, 't'}, {"persist", 1, 0, 'p'}, {"persist-id", 1, 0, 'i'}, {"rpc-timeout", 1, 0, 'r'}, {0, 0, 0, 0} }; int option_index = 0; /* set back to start to be able to use getopt() repeatedly */ optind = 0; /* process given arguments */ init_arglist(&cmd); if (addargs(&cmd, "%s", arg)) { return EXIT_FAILURE; } while ((c = getopt_long(cmd.count, cmd.list, "hct:p:i:r:", long_options, &option_index)) != -1) { switch (c) { case 'h': cmd_commit_help(); ret = EXIT_SUCCESS; goto fail; case 'c': confirmed = 1; break; case 't': confirm_timeout = atoi(optarg); break; case 'p': persist = optarg; break; case 'i': persist_id = optarg; break; case 'r': timeout = atoi(optarg); if (!timeout) { ERROR(__func__, "Invalid timeout \"%s\".", optarg); goto fail; } break; default: ERROR(__func__, "Unknown option -%c.", c); cmd_commit_help(); goto fail; } } if (cmd.list[optind]) { ERROR(__func__, "Unparsed command arguments."); cmd_commit_help(); goto fail; } if (!session) { ERROR(__func__, "Not connected to a NETCONF server, no RPCs can be sent."); goto fail; } if (!interleave) { ERROR(__func__, "NETCONF server does not support interleaving RPCs and notifications."); goto fail; } rpc = nc_rpc_commit(confirmed, confirm_timeout, persist, persist_id, NC_PARAMTYPE_CONST); if (!rpc) { ERROR(__func__, "RPC creation failed."); goto fail; } ret = cli_send_recv(rpc, stdout, 0, timeout); nc_rpc_free(rpc); fail: clear_arglist(&cmd); return ret; } int cmd_copyconfig(const char *arg, char **tmp_config_file) { int c, config_fd, ret = EXIT_FAILURE, timeout = CLI_RPC_REPLY_TIMEOUT; struct stat config_stat; char *src = NULL, *config_m = NULL, *src_start = NULL; const char *trg = NULL; NC_DATASTORE target = NC_DATASTORE_ERROR, source = NC_DATASTORE_ERROR; struct nc_rpc *rpc; NC_WD_MODE wd = NC_WD_UNKNOWN; struct arglist cmd; struct option long_options[] = { {"help", 0, 0, 'h'}, {"target", 1, 0, 't'}, {"source", 1, 0, 's'}, {"src-config", 2, 0, 'c'}, {"defaults", 1, 0, 'd'}, {"rpc-timeout", 1, 0, 'r'}, {0, 0, 0, 0} }; int option_index = 0; /* set back to start to be able to use getopt() repeatedly */ optind = 0; init_arglist(&cmd); if (addargs(&cmd, "%s", arg)) { return EXIT_FAILURE; } while ((c = getopt_long(cmd.count, cmd.list, "ht:s:c::d:r:", long_options, &option_index)) != -1) { switch (c) { case 'h': cmd_copyconfig_help(); ret = EXIT_SUCCESS; goto fail; case 't': /* validate argument */ if (!strcmp(optarg, "running")) { target = NC_DATASTORE_RUNNING; } else if (!strcmp(optarg, "startup")) { target = NC_DATASTORE_STARTUP; } else if (!strcmp(optarg, "candidate")) { target = NC_DATASTORE_CANDIDATE; } else if (!strncmp(optarg, "url:", 4)) { target = NC_DATASTORE_URL; trg = &(optarg[4]); } else { ERROR(__func__, "Invalid target datastore specified (%s).", optarg); goto fail; } break; case 's': /* check if -c was not used */ if (source != NC_DATASTORE_ERROR) { ERROR(__func__, "Mixing --source, and --src-config parameters is not allowed."); goto fail; } /* validate argument */ if (!strcmp(optarg, "running")) { source = NC_DATASTORE_RUNNING; } else if (!strcmp(optarg, "startup")) { source = NC_DATASTORE_STARTUP; } else if (!strcmp(optarg, "candidate")) { source = NC_DATASTORE_CANDIDATE; } else if (!strncmp(optarg, "url:", 4)) { source = NC_DATASTORE_URL; src = strdup(&(optarg[4])); } else { ERROR(__func__, "Invalid source datastore specified (%s).", optarg); goto fail; } break; case 'c': /* check if -s was not used */ if (source != NC_DATASTORE_ERROR) { ERROR(__func__, "Mixing --source and --src-config parameters is not allowed."); goto fail; } source = NC_DATASTORE_CONFIG; if (optarg) { /* open edit configuration data from the file */ config_fd = open(optarg, O_RDONLY); if (config_fd == -1) { ERROR(__func__, "Unable to open the local datastore file \"%s\" (%s).", optarg, strerror(errno)); goto fail; } /* map content of the file into the memory */ if (fstat(config_fd, &config_stat) != 0) { ERROR(__func__, "fstat failed (%s).", strerror(errno)); close(config_fd); goto fail; } config_m = mmap(NULL, config_stat.st_size, PROT_READ, MAP_PRIVATE, config_fd, 0); if (config_m == MAP_FAILED) { ERROR(__func__, "mmap of the local datastore file failed (%s).", strerror(errno)); close(config_fd); goto fail; } /* make a copy of the content to allow closing the file */ src = strdup(config_m); /* unmap local datastore file and close it */ munmap(config_m, config_stat.st_size); close(config_fd); } break; case 'd': if (!strcmp(optarg, "report-all")) { wd = NC_WD_ALL; } else if (!strcmp(optarg, "report-all-tagged")) { wd = NC_WD_ALL_TAG; } else if (!strcmp(optarg, "trim")) { wd = NC_WD_TRIM; } else if (!strcmp(optarg, "explicit")) { wd = NC_WD_EXPLICIT; } else { ERROR(__func__, "Unknown with-defaults mode \"%s\".", optarg); goto fail; } break; case 'r': timeout = atoi(optarg); if (!timeout) { ERROR(__func__, "Invalid timeout \"%s\".", optarg); goto fail; } break; default: ERROR(__func__, "Unknown option -%c.", c); cmd_copyconfig_help(); goto fail; } } if (cmd.list[optind]) { ERROR(__func__, "Unparsed command arguments."); cmd_copyconfig_help(); goto fail; } if (!source || !target) { ERROR(__func__, "Mandatory command arguments missing."); cmd_copyconfig_help(); goto fail; } if (!session) { ERROR(__func__, "Not connected to a NETCONF server, no RPCs can be sent."); goto fail; } if (!interleave) { ERROR(__func__, "NETCONF server does not support interleaving RPCs and notifications."); goto fail; } /* check if edit configuration data were specified */ if ((source == NC_DATASTORE_CONFIG) && !src) { /* let user write edit data interactively */ src = readinput("Type the content of a configuration datastore.", *tmp_config_file, tmp_config_file); if (!src) { ERROR(__func__, "Reading configuration data failed."); goto fail; } } if (src) { /* trim top-level element if needed */ src_start = trim_top_elem(src, "config", "urn:ietf:params:xml:ns:netconf:base:1.0"); if (!src_start) { ERROR(__func__, "Provided configuration content is invalid."); goto fail; } } /* create requests */ rpc = nc_rpc_copy(target, trg, source, src_start, wd, NC_PARAMTYPE_CONST); if (!rpc) { ERROR(__func__, "RPC creation failed."); goto fail; } ret = cli_send_recv(rpc, stdout, 0, timeout); nc_rpc_free(rpc); fail: free(src); clear_arglist(&cmd); return ret; } int cmd_deleteconfig(const char *arg, char **UNUSED(tmp_config_file)) { int c, ret = EXIT_FAILURE, timeout = CLI_RPC_REPLY_TIMEOUT; const char *trg = NULL; struct nc_rpc *rpc; NC_DATASTORE target = NC_DATASTORE_ERROR;; struct arglist cmd; struct option long_options[] = { {"help", 0, 0, 'h'}, {"target", 1, 0, 't'}, {"rpc-timeout", 1, 0, 'r'}, {0, 0, 0, 0} }; int option_index = 0; /* set back to start to be able to use getopt() repeatedly */ optind = 0; init_arglist(&cmd); if (addargs(&cmd, "%s", arg)) { return EXIT_FAILURE; } while ((c = getopt_long(cmd.count, cmd.list, "ht:r:", long_options, &option_index)) != -1) { switch (c) { case 'h': cmd_deleteconfig_help(); ret = EXIT_SUCCESS; goto fail; case 't': if (!strcmp(optarg, "startup")) { target = NC_DATASTORE_STARTUP; } else if (!strncmp(optarg, "url:", 4)) { target = NC_DATASTORE_URL; trg = &(optarg[4]); } else { ERROR(__func__, "Invalid source datastore specified (%s).", optarg); goto fail; } break; case 'r': timeout = atoi(optarg); if (!timeout) { ERROR(__func__, "Invalid timeout \"%s\".", optarg); goto fail; } break; default: ERROR(__func__, "Unknown option -%c.", c); cmd_deleteconfig_help(); goto fail; } } if (cmd.list[optind]) { ERROR(__func__, "Unparsed command arguments."); cmd_deleteconfig_help(); goto fail; } if (!target) { ERROR(__func__, "Mandatory command arguments missing."); cmd_deleteconfig_help(); goto fail; } if (!session) { ERROR(__func__, "Not connected to a NETCONF server, no RPCs can be sent."); goto fail; } if (!interleave) { ERROR(__func__, "NETCONF server does not support interleaving RPCs and notifications."); goto fail; } /* create requests */ rpc = nc_rpc_delete(target, trg, NC_PARAMTYPE_CONST); if (!rpc) { ERROR(__func__, "RPC creation failed."); goto fail; } ret = cli_send_recv(rpc, stdout, 0, timeout); nc_rpc_free(rpc); fail: clear_arglist(&cmd); return ret; } int cmd_discardchanges(const char *arg, char **UNUSED(tmp_config_file)) { struct nc_rpc *rpc; int c, ret = EXIT_FAILURE, timeout = CLI_RPC_REPLY_TIMEOUT; struct arglist cmd; struct option long_options[] = { {"help", 0, 0, 'h'}, {"rpc-timeout", 1, 0, 'r'}, {0, 0, 0, 0} }; int option_index = 0; /* set back to start to be able to use getopt() repeatedly */ optind = 0; /* process given arguments */ init_arglist(&cmd); if (addargs(&cmd, "%s", arg)) { return EXIT_FAILURE; } while ((c = getopt_long(cmd.count, cmd.list, "hr:", long_options, &option_index)) != -1) { switch (c) { case 'h': cmd_discardchanges_help(); ret = EXIT_SUCCESS; goto fail; case 'r': timeout = atoi(optarg); if (!timeout) { ERROR(__func__, "Invalid timeout \"%s\".", optarg); goto fail; } break; default: ERROR(__func__, "Unknown option -%c.", c); cmd_discardchanges_help(); goto fail; } } if (cmd.list[optind]) { ERROR(__func__, "Unparsed command arguments."); cmd_discardchanges_help(); goto fail; } if (!session) { ERROR(__func__, "Not connected to a NETCONF server, no RPCs can be sent."); goto fail; } if (!interleave) { ERROR(__func__, "NETCONF server does not support interleaving RPCs and notifications."); goto fail; } rpc = nc_rpc_discard(); if (!rpc) { ERROR(__func__, "RPC creation failed."); goto fail; } ret = cli_send_recv(rpc, stdout, 0, timeout); nc_rpc_free(rpc); fail: clear_arglist(&cmd); return ret; } int cmd_editconfig(const char *arg, char **tmp_config_file) { int c, config_fd, ret = EXIT_FAILURE, content_param = 0, timeout = CLI_RPC_REPLY_TIMEOUT; struct stat config_stat; char *content = NULL, *config_m = NULL, *cont_start; NC_DATASTORE target = NC_DATASTORE_ERROR; struct nc_rpc *rpc; NC_RPC_EDIT_DFLTOP op = NC_RPC_EDIT_DFLTOP_UNKNOWN; NC_RPC_EDIT_TESTOPT test = NC_RPC_EDIT_TESTOPT_UNKNOWN; NC_RPC_EDIT_ERROPT err = NC_RPC_EDIT_ERROPT_UNKNOWN; struct arglist cmd; struct option long_options[] = { {"help", 0, 0, 'h'}, {"target", 1, 0, 't'}, {"defop", 1, 0, 'o'}, {"test", 1, 0, 'e'}, {"error", 1, 0, 'E'}, {"config", 2, 0, 'c'}, {"url", 1, 0, 'u'}, {"rpc-timeout", 1, 0, 'r'}, {0, 0, 0, 0} }; int option_index = 0; /* set back to start to be able to use getopt() repeatedly */ optind = 0; init_arglist(&cmd); if (addargs(&cmd, "%s", arg)) { return EXIT_FAILURE; } while ((c = getopt_long(cmd.count, cmd.list, "ht:o:E:r:c::u:r:", long_options, &option_index)) != -1) { switch (c) { case 'h': cmd_editconfig_help(); ret = EXIT_SUCCESS; goto fail; case 't': /* validate argument */ if (!strcmp(optarg, "running")) { target = NC_DATASTORE_RUNNING; } else if (!strcmp(optarg, "candidate")) { target = NC_DATASTORE_CANDIDATE; } else { ERROR(__func__, "Invalid target datastore specified (%s).", optarg); goto fail; } break; case 'o': if (!strcmp(optarg, "merge")) { op = NC_RPC_EDIT_DFLTOP_MERGE; } else if (!strcmp(optarg, "replace")) { op = NC_RPC_EDIT_DFLTOP_REPLACE; } else if (!strcmp(optarg, "none")) { op = NC_RPC_EDIT_DFLTOP_NONE; } else { ERROR(__func__, "Invalid default operation specified (%s).", optarg); goto fail; } break; case 'e': if (!strcmp(optarg, "set")) { test = NC_RPC_EDIT_TESTOPT_SET; } else if (!strcmp(optarg, "test-only")) { test = NC_RPC_EDIT_TESTOPT_TEST; } else if (!strcmp(optarg, "test-then-set")) { test = NC_RPC_EDIT_TESTOPT_TESTSET; } else { ERROR(__func__, "Invalid test option specified (%s).", optarg); goto fail; } break; case 'E': if (!strcmp(optarg, "stop")) { err = NC_RPC_EDIT_ERROPT_STOP; } else if (!strcmp(optarg, "continue")) { err = NC_RPC_EDIT_ERROPT_CONTINUE; } else if (!strcmp(optarg, "rollback")) { err = NC_RPC_EDIT_ERROPT_ROLLBACK; } else { ERROR(__func__, "Invalid error option specified (%s).", optarg); goto fail; } break; case 'c': /* check if -u was not used */ if (content_param) { ERROR(__func__, "Mixing --url and --config parameters is not allowed."); goto fail; } content_param = 1; if (optarg) { /* open edit configuration data from the file */ config_fd = open(optarg, O_RDONLY); if (config_fd == -1) { ERROR(__func__, "Unable to open the local datastore file \"%s\" (%s).", optarg, strerror(errno)); goto fail; } /* map content of the file into the memory */ if (fstat(config_fd, &config_stat) != 0) { ERROR(__func__, "fstat failed (%s).", strerror(errno)); close(config_fd); goto fail; } config_m = mmap(NULL, config_stat.st_size, PROT_READ, MAP_PRIVATE, config_fd, 0); if (config_m == MAP_FAILED) { ERROR(__func__, "mmap of the local datastore file failed (%s).", strerror(errno)); close(config_fd); goto fail; } /* make a copy of the content to allow closing the file */ content = strdup(config_m); /* unmap local datastore file and close it */ munmap(config_m, config_stat.st_size); close(config_fd); } break; case 'u': /* check if -c was not used */ if (content_param) { ERROR(__func__, "Mixing --url and --config parameters is not allowed."); goto fail; } content_param = 1; content = strdup(optarg); break; case 'r': timeout = atoi(optarg); if (!timeout) { ERROR(__func__, "Invalid timeout \"%s\".", optarg); goto fail; } break; default: ERROR(__func__, "Unknown option -%c.", c); cmd_editconfig_help(); goto fail; } } if (cmd.list[optind]) { ERROR(__func__, "Unparsed command arguments."); cmd_editconfig_help(); goto fail; } if (!target || !content_param) { ERROR(__func__, "Mandatory command arguments missing."); cmd_editconfig_help(); goto fail; } if (!session) { ERROR(__func__, "Not connected to a NETCONF server, no RPCs can be sent."); goto fail; } if (!interleave) { ERROR(__func__, "NETCONF server does not support interleaving RPCs and notifications."); goto fail; } /* check if edit configuration data were specified */ if (!content) { /* let user write edit data interactively */ content = readinput("Type the content of the <edit-config>.", *tmp_config_file, tmp_config_file); if (!content) { ERROR(__func__, "Reading configuration data failed."); goto fail; } } /* trim top-level element if needed */ cont_start = trim_top_elem(content, "config", "urn:ietf:params:xml:ns:netconf:base:1.0"); if (!cont_start) { ERROR(__func__, "Provided configuration content is invalid."); goto fail; } rpc = nc_rpc_edit(target, op, test, err, cont_start, NC_PARAMTYPE_CONST); if (!rpc) { ERROR(__func__, "RPC creation failed."); goto fail; } ret = cli_send_recv(rpc, stdout, 0, timeout); nc_rpc_free(rpc); fail: clear_arglist(&cmd); free(content); return ret; } int cmd_get(const char *arg, char **tmp_config_file) { int c, config_fd, ret = EXIT_FAILURE, filter_param = 0, timeout = CLI_RPC_REPLY_TIMEOUT; struct stat config_stat; char *filter = NULL, *config_m = NULL; struct nc_rpc *rpc; NC_WD_MODE wd = NC_WD_UNKNOWN; FILE *output = NULL; struct arglist cmd; struct option long_options[] = { {"help", 0, 0, 'h'}, {"filter-subtree", 2, 0, 's'}, {"filter-xpath", 1, 0, 'x'}, {"defaults", 1, 0, 'd'}, {"out", 1, 0, 'o'}, {"rpc-timeout", 1, 0, 'r'}, {0, 0, 0, 0} }; int option_index = 0; /* set back to start to be able to use getopt() repeatedly */ optind = 0; init_arglist(&cmd); if (addargs(&cmd, "%s", arg)) { return EXIT_FAILURE; } while ((c = getopt_long(cmd.count, cmd.list, "hs::x:d:o:r:", long_options, &option_index)) != -1) { switch (c) { case 'h': cmd_get_help(); ret = EXIT_SUCCESS; goto fail; case 's': /* check if -x was not used */ if (filter_param) { ERROR(__func__, "Mixing --filter-subtree, and --filter-xpath parameters is not allowed."); goto fail; } filter_param = 1; if (optarg) { /* open edit configuration data from the file */ config_fd = open(optarg, O_RDONLY); if (config_fd == -1) { ERROR(__func__, "Unable to open the local datastore file \"%s\" (%s).", optarg, strerror(errno)); goto fail; } /* map content of the file into the memory */ if (fstat(config_fd, &config_stat) != 0) { ERROR(__func__, "fstat failed (%s).", strerror(errno)); close(config_fd); goto fail; } config_m = mmap(NULL, config_stat.st_size, PROT_READ, MAP_PRIVATE, config_fd, 0); if (config_m == MAP_FAILED) { ERROR(__func__, "mmap of the local datastore file failed (%s).", strerror(errno)); close(config_fd); goto fail; } /* make a copy of the content to allow closing the file */ filter = strdup(config_m); /* unmap local datastore file and close it */ munmap(config_m, config_stat.st_size); close(config_fd); } break; case 'x': /* check if -s was not used */ if (filter_param) { ERROR(__func__, "Mixing --filter-subtree, and --filter-xpath parameters is not allowed."); goto fail; } filter_param = 1; filter = strdup(optarg); break; case 'd': if (!strcmp(optarg, "report-all")) { wd = NC_WD_ALL; } else if (!strcmp(optarg, "report-all-tagged")) { wd = NC_WD_ALL_TAG; } else if (!strcmp(optarg, "trim")) { wd = NC_WD_TRIM; } else if (!strcmp(optarg, "explicit")) { wd = NC_WD_EXPLICIT; } else { ERROR(__func__, "Unknown with-defaults mode \"%s\".", optarg); goto fail; } break; case 'o': if (output) { ERROR(__func__, "Duplicated \"out\" option."); cmd_get_help(); goto fail; } output = fopen(optarg, "w"); if (!output) { ERROR(__func__, "Failed to open file \"%s\" (%s).", optarg, strerror(errno)); goto fail; } break; case 'r': timeout = atoi(optarg); if (!timeout) { ERROR(__func__, "Invalid timeout \"%s\".", optarg); goto fail; } break; default: ERROR(__func__, "Unknown option -%c.", c); cmd_get_help(); goto fail; } } if (cmd.list[optind]) { ERROR(__func__, "Unparsed command arguments."); cmd_get_help(); goto fail; } if (!session) { ERROR(__func__, "Not connected to a NETCONF server, no RPCs can be sent."); goto fail; } if (!interleave) { ERROR(__func__, "NETCONF server does not support interleaving RPCs and notifications."); goto fail; } /* check if edit configuration data were specified */ if (filter_param && !filter) { /* let user write edit data interactively */ filter = readinput("Type the content of the subtree filter.", *tmp_config_file, tmp_config_file); if (!filter) { ERROR(__func__, "Reading filter data failed."); goto fail; } } /* create requests */ rpc = nc_rpc_get(filter, wd, NC_PARAMTYPE_CONST); if (!rpc) { ERROR(__func__, "RPC creation failed."); goto fail; } if (output) { ret = cli_send_recv(rpc, output, wd, timeout); } else { ret = cli_send_recv(rpc, stdout, wd, timeout); } nc_rpc_free(rpc); fail: clear_arglist(&cmd); if (output) { fclose(output); } free(filter); return ret; } int cmd_getconfig(const char *arg, char **tmp_config_file) { int c, config_fd, ret = EXIT_FAILURE, filter_param = 0, timeout = CLI_RPC_REPLY_TIMEOUT; struct stat config_stat; char *filter = NULL, *config_m = NULL; struct nc_rpc *rpc; NC_WD_MODE wd = NC_WD_UNKNOWN; NC_DATASTORE source = NC_DATASTORE_ERROR; FILE *output = NULL; struct arglist cmd; struct option long_options[] = { {"help", 0, 0, 'h'}, {"source", 1, 0, 'u'}, {"filter-subtree", 2, 0, 's'}, {"filter-xpath", 1, 0, 'x'}, {"defaults", 1, 0, 'd'}, {"out", 1, 0, 'o'}, {"rpc-timeout", 1, 0, 'r'}, {0, 0, 0, 0} }; int option_index = 0; /* set back to start to be able to use getopt() repeatedly */ optind = 0; init_arglist(&cmd); if (addargs(&cmd, "%s", arg)) { return EXIT_FAILURE; } while ((c = getopt_long(cmd.count, cmd.list, "hu:s::x:d:o:r:", long_options, &option_index)) != -1) { switch (c) { case 'h': cmd_getconfig_help(); ret = EXIT_SUCCESS; goto fail; case 'u': if (!strcmp(optarg, "running")) { source = NC_DATASTORE_RUNNING; } else if (!strcmp(optarg, "startup")) { source = NC_DATASTORE_STARTUP; } else if (!strcmp(optarg, "candidate")) { source = NC_DATASTORE_CANDIDATE; } else { ERROR(__func__, "Invalid source datastore specified (%s).", optarg); goto fail; } break; case 's': /* check if -x was not used */ if (filter_param) { ERROR(__func__, "Mixing --filter-subtree, and --filter-xpath parameters is not allowed."); goto fail; } filter_param = 1; if (optarg) { /* open edit configuration data from the file */ config_fd = open(optarg, O_RDONLY); if (config_fd == -1) { ERROR(__func__, "Unable to open the local datastore file \"%s\" (%s).", optarg, strerror(errno)); goto fail; } /* map content of the file into the memory */ if (fstat(config_fd, &config_stat) != 0) { ERROR(__func__, "fstat failed (%s).", strerror(errno)); close(config_fd); goto fail; } config_m = mmap(NULL, config_stat.st_size, PROT_READ, MAP_PRIVATE, config_fd, 0); if (config_m == MAP_FAILED) { ERROR(__func__, "mmap of the local datastore file failed (%s).", strerror(errno)); close(config_fd); goto fail; } /* make a copy of the content to allow closing the file */ filter = strdup(config_m); /* unmap local datastore file and close it */ munmap(config_m, config_stat.st_size); close(config_fd); } break; case 'x': /* check if -s was not used */ if (filter_param) { ERROR(__func__, "Mixing --filter-subtree, and --filter-xpath parameters is not allowed."); goto fail; } filter_param = 1; filter = strdup(optarg); break; case 'd': if (!strcmp(optarg, "report-all")) { wd = NC_WD_ALL; } else if (!strcmp(optarg, "report-all-tagged")) { wd = NC_WD_ALL_TAG; } else if (!strcmp(optarg, "trim")) { wd = NC_WD_TRIM; } else if (!strcmp(optarg, "explicit")) { wd = NC_WD_EXPLICIT; } else { ERROR(__func__, "Unknown with-defaults mode \"%s\".", optarg); goto fail; } break; case 'o': if (output) { ERROR(__func__, "Duplicated \"out\" option."); cmd_getconfig_help(); goto fail; } output = fopen(optarg, "w"); if (!output) { ERROR(__func__, "Failed to open file \"%s\" (%s).", optarg, strerror(errno)); goto fail; } break; case 'r': timeout = atoi(optarg); if (!timeout) { ERROR(__func__, "Invalid timeout \"%s\".", optarg); goto fail; } break; default: ERROR(__func__, "Unknown option -%c.", c); cmd_getconfig_help(); goto fail; } } if (cmd.list[optind]) { ERROR(__func__, "Unparsed command arguments."); cmd_getconfig_help(); goto fail; } if (!source) { ERROR(__func__, "Mandatory command arguments missing."); cmd_getconfig_help(); goto fail; } if (!session) { ERROR(__func__, "Not connected to a NETCONF server, no RPCs can be sent."); goto fail; } if (!interleave) { ERROR(__func__, "NETCONF server does not support interleaving RPCs and notifications."); goto fail; } /* check if edit configuration data were specified */ if (filter_param && !filter) { /* let user write edit data interactively */ filter = readinput("Type the content of the subtree filter.", *tmp_config_file, tmp_config_file); if (!filter) { ERROR(__func__, "Reading filter data failed."); goto fail; } } /* create requests */ rpc = nc_rpc_getconfig(source, filter, wd, NC_PARAMTYPE_CONST); if (!rpc) { ERROR(__func__, "RPC creation failed."); goto fail; } if (output) { ret = cli_send_recv(rpc, output, wd, timeout); } else { ret = cli_send_recv(rpc, stdout, wd, timeout); } nc_rpc_free(rpc); fail: clear_arglist(&cmd); if (output) { fclose(output); } free(filter); return ret; } int cmd_killsession(const char *arg, char **UNUSED(tmp_config_file)) { struct nc_rpc *rpc; int c, ret = EXIT_FAILURE, timeout = CLI_RPC_REPLY_TIMEOUT; uint32_t sid = 0; struct arglist cmd; struct option long_options[] = { {"help", 0, 0, 'h'}, {"sid", 1, 0, 's'}, {"rpc-timeout", 1, 0, 'r'}, {0, 0, 0, 0} }; int option_index = 0; /* set back to start to be able to use getopt() repeatedly */ optind = 0; /* process given arguments */ init_arglist(&cmd); if (addargs(&cmd, "%s", arg)) { return EXIT_FAILURE; } while ((c = getopt_long(cmd.count, cmd.list, "hs:r:", long_options, &option_index)) != -1) { switch (c) { case 'h': cmd_killsession_help(); ret = EXIT_SUCCESS; goto fail; case 's': sid = atoi(optarg); break; case 'r': timeout = atoi(optarg); if (!timeout) { ERROR(__func__, "Invalid timeout \"%s\".", optarg); goto fail; } break; default: ERROR(__func__, "Unknown option -%c.", c); cmd_killsession_help(); goto fail; } } if (cmd.list[optind]) { ERROR(__func__, "Unparsed command arguments."); cmd_killsession_help(); goto fail; } if (!sid) { ERROR(__func__, "Mandatory command arguments missing."); cmd_killsession_help(); goto fail; } if (!session) { ERROR(__func__, "Not connected to a NETCONF server, no RPCs can be sent."); goto fail; } if (!interleave) { ERROR(__func__, "NETCONF server does not support interleaving RPCs and notifications."); goto fail; } if (!sid) { ERROR(__func__, "Session ID was not specififed or not a number."); goto fail; } rpc = nc_rpc_kill(sid); if (!rpc) { ERROR(__func__, "RPC creation failed."); goto fail; } ret = cli_send_recv(rpc, stdout, 0, timeout); nc_rpc_free(rpc); fail: clear_arglist(&cmd); return ret; } int cmd_lock(const char *arg, char **UNUSED(tmp_config_file)) { int c, ret = EXIT_FAILURE, timeout = CLI_RPC_REPLY_TIMEOUT; struct nc_rpc *rpc; NC_DATASTORE target = NC_DATASTORE_ERROR;; struct arglist cmd; struct option long_options[] = { {"help", 0, 0, 'h'}, {"target", 1, 0, 't'}, {"rpc-timeout", 1, 0, 'r'}, {0, 0, 0, 0} }; int option_index = 0; /* set back to start to be able to use getopt() repeatedly */ optind = 0; init_arglist(&cmd); if (addargs(&cmd, "%s", arg)) { return EXIT_FAILURE; } while ((c = getopt_long(cmd.count, cmd.list, "ht:r:", long_options, &option_index)) != -1) { switch (c) { case 'h': cmd_lock_help(); ret = EXIT_SUCCESS; goto fail; case 't': if (!strcmp(optarg, "running")) { target = NC_DATASTORE_RUNNING; } else if (!strcmp(optarg, "startup")) { target = NC_DATASTORE_STARTUP; } else if (!strcmp(optarg, "candidate")) { target = NC_DATASTORE_CANDIDATE; } else { ERROR(__func__, "Invalid source datastore specified (%s).", optarg); goto fail; } break; case 'r': timeout = atoi(optarg); if (!timeout) { ERROR(__func__, "Invalid timeout \"%s\".", optarg); goto fail; } break; default: ERROR(__func__, "Unknown option -%c.", c); cmd_lock_help(); goto fail; } } if (cmd.list[optind]) { ERROR(__func__, "Unparsed command arguments."); cmd_lock_help(); goto fail; } if (!target) { ERROR(__func__, "Mandatory command arguments missing."); cmd_lock_help(); goto fail; } if (!session) { ERROR(__func__, "Not connected to a NETCONF server, no RPCs can be sent."); goto fail; } if (!interleave) { ERROR(__func__, "NETCONF server does not support interleaving RPCs and notifications."); goto fail; } /* create requests */ rpc = nc_rpc_lock(target); if (!rpc) { ERROR(__func__, "RPC creation failed."); goto fail; } ret = cli_send_recv(rpc, stdout, 0, timeout); nc_rpc_free(rpc); fail: clear_arglist(&cmd); return ret; } int cmd_unlock(const char *arg, char **UNUSED(tmp_config_file)) { int c, ret = EXIT_FAILURE, timeout = CLI_RPC_REPLY_TIMEOUT; struct nc_rpc *rpc; NC_DATASTORE target = NC_DATASTORE_ERROR;; struct arglist cmd; struct option long_options[] = { {"help", 0, 0, 'h'}, {"target", 1, 0, 't'}, {"rpc-timeout", 1, 0, 'r'}, {0, 0, 0, 0} }; int option_index = 0; /* set back to start to be able to use getopt() repeatedly */ optind = 0; init_arglist(&cmd); if (addargs(&cmd, "%s", arg)) { return EXIT_FAILURE; } while ((c = getopt_long(cmd.count, cmd.list, "ht:r:", long_options, &option_index)) != -1) { switch (c) { case 'h': cmd_unlock_help(); ret = EXIT_SUCCESS; goto fail; case 't': if (!strcmp(optarg, "running")) { target = NC_DATASTORE_RUNNING; } else if (!strcmp(optarg, "startup")) { target = NC_DATASTORE_STARTUP; } else if (!strcmp(optarg, "candidate")) { target = NC_DATASTORE_CANDIDATE; } else { ERROR(__func__, "Invalid source datastore specified (%s).", optarg); goto fail; } break; case 'r': timeout = atoi(optarg); if (!timeout) { ERROR(__func__, "Invalid timeout \"%s\".", optarg); goto fail; } break; default: ERROR(__func__, "Unknown option -%c.", c); cmd_unlock_help(); goto fail; } } if (cmd.list[optind]) { ERROR(__func__, "Unparsed command arguments."); cmd_unlock_help(); goto fail; } if (!target) { ERROR(__func__, "Mandatory command arguments missing."); cmd_unlock_help(); goto fail; } if (!session) { ERROR(__func__, "Not connected to a NETCONF server, no RPCs can be sent."); goto fail; } if (!interleave) { ERROR(__func__, "NETCONF server does not support interleaving RPCs and notifications."); goto fail; } /* create requests */ rpc = nc_rpc_unlock(target); if (!rpc) { ERROR(__func__, "RPC creation failed."); goto fail; } ret = cli_send_recv(rpc, stdout, 0, timeout); nc_rpc_free(rpc); fail: clear_arglist(&cmd); return ret; } int cmd_validate(const char *arg, char **tmp_config_file) { int c, config_fd, ret = EXIT_FAILURE, timeout = CLI_RPC_REPLY_TIMEOUT; struct stat config_stat; char *src = NULL, *config_m = NULL, *src_start; NC_DATASTORE source = NC_DATASTORE_ERROR; struct nc_rpc *rpc; struct arglist cmd; struct option long_options[] = { {"help", 0, 0, 'h'}, {"source", 1, 0, 's'}, {"src-config", 2, 0, 'c'}, {"rpc-timeout", 1, 0, 'r'}, {0, 0, 0, 0} }; int option_index = 0; /* set back to start to be able to use getopt() repeatedly */ optind = 0; init_arglist(&cmd); if (addargs(&cmd, "%s", arg)) { return EXIT_FAILURE; } while ((c = getopt_long(cmd.count, cmd.list, "hs:c::r:", long_options, &option_index)) != -1) { switch (c) { case 'h': cmd_validate_help(); ret = EXIT_SUCCESS; goto fail; case 's': /* check if -c was not used */ if (source != NC_DATASTORE_ERROR) { ERROR(__func__, "Mixing --source, and --src-config parameters is not allowed."); goto fail; } /* validate argument */ if (!strcmp(optarg, "running")) { source = NC_DATASTORE_RUNNING; } else if (!strcmp(optarg, "startup")) { source = NC_DATASTORE_STARTUP; } else if (!strcmp(optarg, "candidate")) { source = NC_DATASTORE_CANDIDATE; } else if (!strncmp(optarg, "url:", 4)) { source = NC_DATASTORE_URL; src = strdup(&(optarg[4])); } else { ERROR(__func__, "Invalid source datastore specified (%s).", optarg); goto fail; } break; case 'c': /* check if -s was not used */ if (source != NC_DATASTORE_ERROR) { ERROR(__func__, "Mixing --source and --src-config parameters is not allowed."); goto fail; } source = NC_DATASTORE_CONFIG; if (optarg) { /* open edit configuration data from the file */ config_fd = open(optarg, O_RDONLY); if (config_fd == -1) { ERROR(__func__, "Unable to open the local datastore file \"%s\" (%s).", optarg, strerror(errno)); goto fail; } /* map content of the file into the memory */ if (fstat(config_fd, &config_stat) != 0) { ERROR(__func__, "fstat failed (%s).", strerror(errno)); close(config_fd); goto fail; } config_m = mmap(NULL, config_stat.st_size, PROT_READ, MAP_PRIVATE, config_fd, 0); if (config_m == MAP_FAILED) { ERROR(__func__, "mmap of the local datastore file failed (%s).", strerror(errno)); close(config_fd); goto fail; } /* make a copy of the content to allow closing the file */ src = strdup(config_m); /* unmap local datastore file and close it */ munmap(config_m, config_stat.st_size); close(config_fd); } break; case 'r': timeout = atoi(optarg); if (!timeout) { ERROR(__func__, "Invalid timeout \"%s\".", optarg); goto fail; } break; default: ERROR(__func__, "Unknown option -%c.", c); cmd_validate_help(); goto fail; } } if (cmd.list[optind]) { ERROR(__func__, "Unparsed command arguments."); cmd_validate_help(); goto fail; } if (!source) { ERROR(__func__, "Mandatory command arguments missing."); cmd_validate_help(); goto fail; } if (!session) { ERROR(__func__, "Not connected to a NETCONF server, no RPCs can be sent."); goto fail; } if (!interleave) { ERROR(__func__, "NETCONF server does not support interleaving RPCs and notifications."); goto fail; } /* check if edit configuration data were specified */ if ((source == NC_DATASTORE_CONFIG) && !src) { /* let user write edit data interactively */ src = readinput("Type the content of a configuration datastore.", *tmp_config_file, tmp_config_file); if (!src) { ERROR(__func__, "Reading configuration data failed."); goto fail; } } /* trim top-level element if needed */ if (src) { src_start = trim_top_elem(src, "config", "urn:ietf:params:xml:ns:netconf:base:1.0"); if (!src_start) { ERROR(__func__, "Provided configuration content is invalid."); goto fail; } } else { src_start = NULL; } /* create requests */ rpc = nc_rpc_validate(source, src_start, NC_PARAMTYPE_CONST); if (!rpc) { ERROR(__func__, "RPC creation failed."); goto fail; } ret = cli_send_recv(rpc, stdout, 0, timeout); nc_rpc_free(rpc); fail: clear_arglist(&cmd); free(src); return ret; } int cmd_subscribe(const char *arg, char **tmp_config_file) { int c, config_fd, ret = EXIT_FAILURE, filter_param = 0, timeout = CLI_RPC_REPLY_TIMEOUT; struct stat config_stat; char *filter = NULL, *config_m = NULL, *start = NULL, *stop = NULL; const char *stream = NULL; struct nc_rpc *rpc = NULL; time_t t; FILE *output = NULL; struct arglist cmd; struct option long_options[] = { {"help", 0, 0, 'h'}, {"filter-subtree", 2, 0, 's'}, {"filter-xpath", 1, 0, 'x'}, {"begin", 1, 0, 'b'}, {"end", 1, 0, 'e'}, {"stream", 1, 0, 't'}, {"out", 1, 0, 'o'}, {"rpc-timeout", 1, 0, 'r'}, {0, 0, 0, 0} }; int option_index = 0; /* set back to start to be able to use getopt() repeatedly */ optind = 0; init_arglist(&cmd); if (addargs(&cmd, "%s", arg)) { return EXIT_FAILURE; } while ((c = getopt_long(cmd.count, cmd.list, "hs::x:b:e:t:o:r:", long_options, &option_index)) != -1) { switch (c) { case 'h': cmd_subscribe_help(); ret = EXIT_SUCCESS; goto fail; case 's': /* check if -x was not used */ if (filter_param) { ERROR(__func__, "Mixing --filter-subtree, and --filter-xpath parameters is not allowed."); goto fail; } filter_param = 1; if (optarg) { /* open edit configuration data from the file */ config_fd = open(optarg, O_RDONLY); if (config_fd == -1) { ERROR(__func__, "Unable to open the local datastore file \"%s\" (%s).", optarg, strerror(errno)); goto fail; } /* map content of the file into the memory */ if (fstat(config_fd, &config_stat) != 0) { ERROR(__func__, "fstat failed (%s).", strerror(errno)); close(config_fd); goto fail; } config_m = mmap(NULL, config_stat.st_size, PROT_READ, MAP_PRIVATE, config_fd, 0); if (config_m == MAP_FAILED) { ERROR(__func__, "mmap of the local datastore file failed (%s).", strerror(errno)); close(config_fd); goto fail; } /* make a copy of the content to allow closing the file */ filter = strdup(config_m); /* unmap local datastore file and close it */ munmap(config_m, config_stat.st_size); close(config_fd); } break; case 'x': /* check if -s was not used */ if (filter_param) { ERROR(__func__, "Mixing --filter-subtree, and --filter-xpath parameters is not allowed."); goto fail; } filter_param = 1; filter = strdup(optarg); break; case 'b': case 'e': if (optarg[0] == '-' || optarg[0] == '+') { t = time(NULL); t += atol(optarg); } else { t = atol(optarg); } if (c == 'b') { if (t > time(NULL)) { /* begin time is in future */ ERROR(__func__, "Begin time cannot be set to future."); goto fail; } start = nc_time2datetime(t, NULL, NULL); } else { /* c == 'e' */ stop = nc_time2datetime(t, NULL, NULL); } break; case 't': stream = optarg; break; case 'o': if (output) { ERROR(__func__, "Duplicated \"out\" option."); cmd_subscribe_help(); goto fail; } output = fopen(optarg, "w"); if (!output) { ERROR(__func__, "Failed to open file \"%s\" (%s).", optarg, strerror(errno)); goto fail; } break; case 'r': timeout = atoi(optarg); if (!timeout) { ERROR(__func__, "Invalid timeout \"%s\".", optarg); goto fail; } break; default: ERROR(__func__, "Unknown option -%c.", c); cmd_subscribe_help(); goto fail; } } if (cmd.list[optind]) { ERROR(__func__, "Unparsed command arguments."); cmd_subscribe_help(); goto fail; } if (!session) { ERROR(__func__, "Not connected to a NETCONF server, no RPCs can be sent."); goto fail; } /* check if edit configuration data were specified */ if (filter_param && !filter) { /* let user write edit data interactively */ filter = readinput("Type the content of the subtree filter.", *tmp_config_file, tmp_config_file); if (!filter) { ERROR(__func__, "Reading filter data failed."); goto fail; } } /* create requests */ rpc = nc_rpc_subscribe(stream, filter, start, stop, NC_PARAMTYPE_CONST); if (!rpc) { ERROR(__func__, "RPC creation failed."); goto fail; } /* create notification thread so that notifications can immediately be received */ if (!nc_session_ntf_thread_running(session)) { if (!output) { output = stdout; } nc_session_set_data(session, output); ret = nc_recv_notif_dispatch(session, cli_ntf_clb); if (ret) { ERROR(__func__, "Failed to create notification thread."); goto fail; } } ret = cli_send_recv(rpc, stdout, 0, timeout); if (ret) { goto fail; } if (!nc_session_cpblt(session, NC_CAP_INTERLEAVE_ID)) { fprintf(output, "NETCONF server does not support interleave, you\n" "cannot issue any RPCs during the subscription.\n" "Close the session with \"disconnect\".\n"); interleave = 0; } fail: clear_arglist(&cmd); if (output && (output != stdout)) { fclose(output); } free(filter); free(start); free(stop); nc_rpc_free(rpc); return ret; } int cmd_getschema(const char *arg, char **UNUSED(tmp_config_file)) { int c, ret = EXIT_FAILURE, timeout = CLI_RPC_REPLY_TIMEOUT; const char *model = NULL, *version = NULL, *format = NULL; struct nc_rpc *rpc; FILE *output = NULL; struct arglist cmd; struct option long_options[] = { {"help", 0, 0, 'h'}, {"model", 1, 0, 'm'}, {"version", 1, 0, 'v'}, {"format", 1, 0, 'f'}, {"out", 1, 0, 'o'}, {"rpc-timeout", 1, 0, 'r'}, {0, 0, 0, 0} }; int option_index = 0; /* set back to start to be able to use getopt() repeatedly */ optind = 0; init_arglist(&cmd); if (addargs(&cmd, "%s", arg)) { return EXIT_FAILURE; } while ((c = getopt_long(cmd.count, cmd.list, "hm:v:f:o:r:", long_options, &option_index)) != -1) { switch (c) { case 'h': cmd_getschema_help(); ret = EXIT_SUCCESS; goto fail; case 'm': model = optarg; break; case 'v': version = optarg; break; case 'f': format = optarg; break; case 'o': if (output) { ERROR(__func__, "Duplicated \"out\" option."); cmd_getschema_help(); goto fail; } output = fopen(optarg, "w"); if (!output) { ERROR(__func__, "Failed to open file \"%s\" (%s).", optarg, strerror(errno)); goto fail; } break; case 'r': timeout = atoi(optarg); if (!timeout) { ERROR(__func__, "Invalid timeout \"%s\".", optarg); goto fail; } break; default: ERROR(__func__, "Unknown option -%c.", c); cmd_getschema_help(); goto fail; } } if (cmd.list[optind]) { ERROR(__func__, "Unparsed command arguments."); cmd_getschema_help(); goto fail; } if (!model) { ERROR(__func__, "Mandatory command arguments missing."); cmd_getschema_help(); goto fail; } if (!session) { ERROR(__func__, "Not connected to a NETCONF server, no RPCs can be sent."); goto fail; } if (!interleave) { ERROR(__func__, "NETCONF server does not support interleaving RPCs and notifications."); goto fail; } rpc = nc_rpc_getschema(model, version, format, NC_PARAMTYPE_CONST); if (!rpc) { ERROR(__func__, "RPC creation failed."); goto fail; } if (output) { ret = cli_send_recv(rpc, output, 0, timeout); } else { ret = cli_send_recv(rpc, stdout, 0, timeout); } nc_rpc_free(rpc); fail: clear_arglist(&cmd); if (output) { fclose(output); } return ret; } int cmd_getdata(const char *arg, char **tmp_config_file) { int c, config_fd, ret = EXIT_FAILURE; int filter_param = 0, origin_count = 0, negated_origin = 0, depth = 0, with_origin = 0, timeout = CLI_RPC_REPLY_TIMEOUT; struct stat config_stat; char *filter = NULL, *config_m = NULL, *datastore = NULL, *config = NULL, **origin = NULL, *ptr; struct nc_rpc *rpc; NC_WD_MODE wd = NC_WD_UNKNOWN; FILE *output = NULL; struct arglist cmd; struct option long_options[] = { {"help", 0, 0, 'h'}, {"datastore", 1, 0, 'd'}, {"filter-subtree", 2, 0, 's'}, {"filter-xpath", 1, 0, 'x'}, {"config", 1, 0, 'c'}, {"origin", 1, 0, 'O'}, {"negated-origin", 0, 0, 'n'}, {"depth", 1, 0, 'e'}, {"with-origin", 0, 0, 'w'}, {"defaults", 1, 0, 'f'}, {"out", 1, 0, 'o'}, {"rpc-timeout", 1, 0, 'r'}, {0, 0, 0, 0} }; int option_index = 0; /* set back to start to be able to use getopt() repeatedly */ optind = 0; init_arglist(&cmd); if (addargs(&cmd, "%s", arg)) { return EXIT_FAILURE; } while ((c = getopt_long(cmd.count, cmd.list, "hd:s::x:c:O:ne:wf:o:r:", long_options, &option_index)) != -1) { switch (c) { case 'h': cmd_getdata_help(); ret = EXIT_SUCCESS; goto fail; case 'd': if (datastore) { ERROR(__func__, "Datastore was already specified."); goto fail; } if (!strcmp(optarg, "running")) { datastore = "ietf-datastores:running"; } else if (!strcmp(optarg, "startup")) { datastore = "ietf-datastores:startup"; } else if (!strcmp(optarg, "candidate")) { datastore = "ietf-datastores:candidate"; } else if (!strcmp(optarg, "operational")) { datastore = "ietf-datastores:operational"; } else { ERROR(__func__, "Invalid datastore specified (%s).", optarg); goto fail; } break; case 's': /* check if -x was not used */ if (filter_param) { ERROR(__func__, "Mixing --filter-subtree, and --filter-xpath parameters is not allowed."); goto fail; } filter_param = 1; if (optarg) { /* open edit configuration data from the file */ config_fd = open(optarg, O_RDONLY); if (config_fd == -1) { ERROR(__func__, "Unable to open the local datastore file \"%s\" (%s).", optarg, strerror(errno)); goto fail; } /* map content of the file into the memory */ if (fstat(config_fd, &config_stat) != 0) { ERROR(__func__, "fstat failed (%s).", strerror(errno)); close(config_fd); goto fail; } config_m = mmap(NULL, config_stat.st_size, PROT_READ, MAP_PRIVATE, config_fd, 0); if (config_m == MAP_FAILED) { ERROR(__func__, "mmap of the local datastore file failed (%s).", strerror(errno)); close(config_fd); goto fail; } /* make a copy of the content to allow closing the file */ filter = strdup(config_m); /* unmap local datastore file and close it */ munmap(config_m, config_stat.st_size); close(config_fd); } break; case 'x': /* check if -s was not used */ if (filter_param) { ERROR(__func__, "Mixing --filter-subtree, and --filter-xpath parameters is not allowed."); goto fail; } filter_param = 1; filter = strdup(optarg); break; case 'c': if (config) { ERROR(__func__, "Config filter was already specified."); goto fail; } if (!strcmp(optarg, "true") || !strcmp(optarg, "false")) { config = optarg; } else { ERROR(__func__, "Invalid config filter specified (%s).", optarg); goto fail; } break; case 'O': origin = realloc(origin, (origin_count + 1) * sizeof *origin); asprintf(&origin[origin_count], "ietf-origin:%s", optarg); ++origin_count; break; case 'n': negated_origin = 1; break; case 'e': depth = strtoul(optarg, &ptr, 10); if (ptr[0]) { ERROR(__func__, "Invalid depth specified (%s).", optarg); goto fail; } break; case 'w': with_origin = 1; break; case 'f': if (!strcmp(optarg, "report-all")) { wd = NC_WD_ALL; } else if (!strcmp(optarg, "report-all-tagged")) { wd = NC_WD_ALL_TAG; } else if (!strcmp(optarg, "trim")) { wd = NC_WD_TRIM; } else if (!strcmp(optarg, "explicit")) { wd = NC_WD_EXPLICIT; } else { ERROR(__func__, "Unknown with-defaults mode \"%s\".", optarg); goto fail; } break; case 'o': if (output) { ERROR(__func__, "Duplicated \"out\" option."); cmd_getconfig_help(); goto fail; } output = fopen(optarg, "w"); if (!output) { ERROR(__func__, "Failed to open file \"%s\" (%s).", optarg, strerror(errno)); goto fail; } break; case 'r': timeout = atoi(optarg); if (!timeout) { ERROR(__func__, "Invalid timeout \"%s\".", optarg); goto fail; } break; default: ERROR(__func__, "Unknown option -%c.", c); cmd_getdata_help(); goto fail; } } if (cmd.list[optind]) { ERROR(__func__, "Unparsed command arguments."); cmd_getdata_help(); goto fail; } if (!datastore) { ERROR(__func__, "Mandatory command arguments missing."); cmd_getdata_help(); goto fail; } if (!session) { ERROR(__func__, "Not connected to a NETCONF server, no RPCs can be sent."); goto fail; } if (!interleave) { ERROR(__func__, "NETCONF server does not support interleaving RPCs and notifications."); goto fail; } /* check if edit configuration data were specified */ if (filter_param && !filter) { /* let user write edit data interactively */ filter = readinput("Type the content of the subtree filter.", *tmp_config_file, tmp_config_file); if (!filter) { ERROR(__func__, "Reading filter data failed."); goto fail; } } /* create requests */ rpc = nc_rpc_getdata(datastore, filter, config, origin, origin_count, negated_origin, depth, with_origin, wd, NC_PARAMTYPE_CONST); if (!rpc) { ERROR(__func__, "RPC creation failed."); goto fail; } if (output) { ret = cli_send_recv(rpc, output, wd, timeout); } else { ret = cli_send_recv(rpc, stdout, wd, timeout); } nc_rpc_free(rpc); fail: clear_arglist(&cmd); if (output) { fclose(output); } free(filter); for (c = 0; c < origin_count; ++c) { free(origin[c]); } free(origin); return ret; } int cmd_editdata(const char *arg, char **tmp_config_file) { int c, config_fd, ret = EXIT_FAILURE, content_param = 0, timeout = CLI_RPC_REPLY_TIMEOUT; struct stat config_stat; char *content = NULL, *config_m = NULL, *cont_start, *datastore = NULL; struct nc_rpc *rpc; NC_RPC_EDIT_DFLTOP op = NC_RPC_EDIT_DFLTOP_UNKNOWN; struct arglist cmd; struct option long_options[] = { {"help", 0, 0, 'h'}, {"datastore", 1, 0, 'd'}, {"defop", 1, 0, 'o'}, {"config", 2, 0, 'c'}, {"url", 1, 0, 'u'}, {"rpc-timeout", 1, 0, 'r'}, {0, 0, 0, 0} }; int option_index = 0; /* set back to start to be able to use getopt() repeatedly */ optind = 0; init_arglist(&cmd); if (addargs(&cmd, "%s", arg)) { return EXIT_FAILURE; } while ((c = getopt_long(cmd.count, cmd.list, "hd:o:c::u:r:", long_options, &option_index)) != -1) { switch (c) { case 'h': cmd_editdata_help(); ret = EXIT_SUCCESS; goto fail; case 'd': if (datastore) { ERROR(__func__, "Datastore was already specified."); goto fail; } if (!strcmp(optarg, "running")) { datastore = "ietf-datastores:running"; } else if (!strcmp(optarg, "startup")) { datastore = "ietf-datastores:startup"; } else if (!strcmp(optarg, "candidate")) { datastore = "ietf-datastores:candidate"; } else if (!strcmp(optarg, "operational")) { datastore = "ietf-datastores:operational"; } else { ERROR(__func__, "Invalid datastore specified (%s).", optarg); goto fail; } break; case 'o': if (!strcmp(optarg, "merge")) { op = NC_RPC_EDIT_DFLTOP_MERGE; } else if (!strcmp(optarg, "replace")) { op = NC_RPC_EDIT_DFLTOP_REPLACE; } else if (!strcmp(optarg, "none")) { op = NC_RPC_EDIT_DFLTOP_NONE; } else { ERROR(__func__, "Invalid default operation specified (%s).", optarg); goto fail; } break; case 'c': /* check if -u was not used */ if (content_param) { ERROR(__func__, "Mixing --url and --config parameters is not allowed."); goto fail; } content_param = 1; if (optarg) { /* open edit configuration data from the file */ config_fd = open(optarg, O_RDONLY); if (config_fd == -1) { ERROR(__func__, "Unable to open the local datastore file \"%s\" (%s).", optarg, strerror(errno)); goto fail; } /* map content of the file into the memory */ if (fstat(config_fd, &config_stat) != 0) { ERROR(__func__, "fstat failed (%s).", strerror(errno)); close(config_fd); goto fail; } config_m = mmap(NULL, config_stat.st_size, PROT_READ, MAP_PRIVATE, config_fd, 0); if (config_m == MAP_FAILED) { ERROR(__func__, "mmap of the local datastore file failed (%s).", strerror(errno)); close(config_fd); goto fail; } /* make a copy of the content to allow closing the file */ content = strdup(config_m); /* unmap local datastore file and close it */ munmap(config_m, config_stat.st_size); close(config_fd); } break; case 'u': /* check if -c was not used */ if (content_param) { ERROR(__func__, "Mixing --url and --config parameters is not allowed."); goto fail; } content_param = 1; content = strdup(optarg); break; case 'r': timeout = atoi(optarg); if (!timeout) { ERROR(__func__, "Invalid timeout \"%s\".", optarg); goto fail; } break; default: ERROR(__func__, "Unknown option -%c.", c); cmd_editdata_help(); goto fail; } } if (cmd.list[optind]) { ERROR(__func__, "Unparsed command arguments."); cmd_editdata_help(); goto fail; } if (!datastore || !content_param) { ERROR(__func__, "Mandatory command arguments missing."); cmd_editdata_help(); goto fail; } if (!session) { ERROR(__func__, "Not connected to a NETCONF server, no RPCs can be sent."); goto fail; } if (!interleave) { ERROR(__func__, "NETCONF server does not support interleaving RPCs and notifications."); goto fail; } /* check if edit configuration data were specified */ if (!content) { /* let user write edit data interactively */ content = readinput("Type the content of the <edit-data>.", *tmp_config_file, tmp_config_file); if (!content) { ERROR(__func__, "Reading configuration data failed."); goto fail; } } /* trim top-level element if needed */ cont_start = trim_top_elem(content, "config", "urn:ietf:params:xml:ns:netconf:base:1.0"); if (!cont_start) { ERROR(__func__, "Provided configuration content is invalid."); goto fail; } rpc = nc_rpc_editdata(datastore, op, cont_start, NC_PARAMTYPE_CONST); if (!rpc) { ERROR(__func__, "RPC creation failed."); goto fail; } ret = cli_send_recv(rpc, stdout, 0, timeout); nc_rpc_free(rpc); fail: clear_arglist(&cmd); free(content); return ret; } int cmd_userrpc(const char *arg, char **tmp_config_file) { int c, config_fd, ret = EXIT_FAILURE, timeout = CLI_RPC_REPLY_TIMEOUT; struct stat config_stat; char *content = NULL, *config_m = NULL; struct nc_rpc *rpc; FILE *output = NULL; struct arglist cmd; struct option long_options[] = { {"help", 0, 0, 'h'}, {"content", 1, 0, 'c'}, {"out", 1, 0, 'o'}, {"rpc-timeout", 1, 0, 'r'}, {0, 0, 0, 0} }; int option_index = 0; /* set back to start to be able to use getopt() repeatedly */ optind = 0; init_arglist(&cmd); if (addargs(&cmd, "%s", arg)) { return EXIT_FAILURE; } while ((c = getopt_long(cmd.count, cmd.list, "ht:s:c::d:r:", long_options, &option_index)) != -1) { switch (c) { case 'h': cmd_userrpc_help(); ret = EXIT_SUCCESS; goto fail; case 'c': if (content) { ERROR(__func__, "Duplicated \"content\" option."); cmd_userrpc_help(); goto fail; } /* open edit configuration data from the file */ config_fd = open(optarg, O_RDONLY); if (config_fd == -1) { ERROR(__func__, "Unable to open the local datastore file \"%s\" (%s).", optarg, strerror(errno)); goto fail; } /* map content of the file into the memory */ if (fstat(config_fd, &config_stat) != 0) { ERROR(__func__, "fstat failed (%s).", strerror(errno)); close(config_fd); goto fail; } config_m = mmap(NULL, config_stat.st_size, PROT_READ, MAP_PRIVATE, config_fd, 0); if (config_m == MAP_FAILED) { ERROR(__func__, "mmap of the local datastore file failed (%s).", strerror(errno)); close(config_fd); goto fail; } /* make a copy of the content to allow closing the file */ content = strdup(config_m); /* unmap local datastore file and close it */ munmap(config_m, config_stat.st_size); close(config_fd); break; case 'o': if (output) { ERROR(__func__, "Duplicated \"out\" option."); cmd_userrpc_help(); goto fail; } output = fopen(optarg, "w"); if (!output) { ERROR(__func__, "Failed to open file \"%s\" (%s).", optarg, strerror(errno)); goto fail; } break; case 'r': timeout = atoi(optarg); if (!timeout) { ERROR(__func__, "Invalid timeout \"%s\".", optarg); goto fail; } break; default: ERROR(__func__, "Unknown option -%c.", c); cmd_userrpc_help(); goto fail; } } if (cmd.list[optind]) { ERROR(__func__, "Unparsed command arguments."); cmd_userrpc_help(); goto fail; } if (!session) { ERROR(__func__, "Not connected to a NETCONF server, no RPCs can be sent."); goto fail; } if (!interleave) { ERROR(__func__, "NETCONF server does not support interleaving RPCs and notifications."); goto fail; } /* check if edit configuration data were specified */ if (!content) { /* let user write edit data interactively */ content = readinput("Type the content of a configuration datastore.", *tmp_config_file, tmp_config_file); if (!content) { ERROR(__func__, "Reading configuration data failed."); goto fail; } } /* create requests */ rpc = nc_rpc_act_generic_xml(content, NC_PARAMTYPE_CONST); if (!rpc) { ERROR(__func__, "RPC creation failed."); goto fail; } if (output) { ret = cli_send_recv(rpc, output, 0, timeout); } else { ret = cli_send_recv(rpc, stdout, 0, timeout); } nc_rpc_free(rpc); fail: clear_arglist(&cmd); if (output) { fclose(output); } free(content); return ret; } int cmd_timed(const char *arg, char **UNUSED(tmp_config_file)) { char *args = strdupa(arg); char *cmd = NULL; strtok(args, " "); if ((cmd = strtok(NULL, " ")) == NULL) { fprintf(stdout, "All commands will %sbe timed.\n", timed ? "" : "not "); } else { if (!strcmp(cmd, "on")) { timed = 1; } else if (!strcmp(cmd, "off")) { timed = 0; } else { ERROR(__func__, "Unknown option %s.", cmd); cmd_timed_help(); } } return 0; } COMMAND commands[] = { #ifdef NC_ENABLED_SSH {"auth", cmd_auth, cmd_auth_help, "Manage SSH authentication options"}, {"knownhosts", cmd_knownhosts, cmd_knownhosts_help, "Manage the user knownhosts file"}, #endif #ifdef NC_ENABLED_TLS {"cert", cmd_cert, cmd_cert_help, "Manage trusted or your own certificates"}, {"crl", cmd_crl, cmd_crl_help, "Manage Certificate Revocation List directory"}, #endif {"outputformat", cmd_outputformat, cmd_outputformat_help, "Set the output format of all the data"}, {"searchpath", cmd_searchpath, cmd_searchpath_help, "Set the search path for models"}, {"verb", cmd_verb, cmd_verb_help, "Change verbosity"}, {"version", cmd_version, NULL, "Print Netopeer2 CLI version"}, {"disconnect", cmd_disconnect, NULL, "Disconnect from a NETCONF server"}, {"status", cmd_status, NULL, "Display information about the current NETCONF session"}, {"connect", cmd_connect, cmd_connect_help, "Connect to a NETCONF server"}, {"listen", cmd_listen, cmd_listen_help, "Wait for a Call Home connection from a NETCONF server"}, {"quit", cmd_quit, NULL, "Quit the program"}, {"help", cmd_help, NULL, "Display commands description"}, {"editor", cmd_editor, cmd_editor_help, "Set the text editor for working with XML data"}, {"cancel-commit", cmd_cancelcommit, cmd_cancelcommit_help, "ietf-netconf <cancel-commit> operation"}, {"commit", cmd_commit, cmd_commit_help, "ietf-netconf <commit> operation"}, {"copy-config", cmd_copyconfig, cmd_copyconfig_help, "ietf-netconf <copy-config> operation"}, {"delete-config", cmd_deleteconfig, cmd_deleteconfig_help, "ietf-netconf <delete-config> operation"}, {"discard-changes", cmd_discardchanges, cmd_discardchanges_help, "ietf-netconf <discard-changes> operation"}, {"edit-config", cmd_editconfig, cmd_editconfig_help, "ietf-netconf <edit-config> operation"}, {"get", cmd_get, cmd_get_help, "ietf-netconf <get> operation"}, {"get-config", cmd_getconfig, cmd_getconfig_help, "ietf-netconf <get-config> operation"}, {"kill-session", cmd_killsession, cmd_killsession_help, "ietf-netconf <kill-session> operation"}, {"lock", cmd_lock, cmd_lock_help, "ietf-netconf <lock> operation"}, {"unlock", cmd_unlock, cmd_unlock_help, "ietf-netconf <unlock> operation"}, {"validate", cmd_validate, cmd_validate_help, "ietf-netconf <validate> operation"}, {"subscribe", cmd_subscribe, cmd_subscribe_help, "notifications <create-subscription> operation"}, {"get-schema", cmd_getschema, cmd_getschema_help, "ietf-netconf-monitoring <get-schema> operation"}, {"get-data", cmd_getdata, cmd_getdata_help, "ietf-netconf-nmda <get-data> operation"}, {"edit-data", cmd_editdata, cmd_editdata_help, "ietf-netconf-nmda <edit-data> operation"}, {"user-rpc", cmd_userrpc, cmd_userrpc_help, "Send your own content in an RPC envelope"}, {"timed", cmd_timed, cmd_timed_help, "Time all the commands (that communicate with a server) from issuing a RPC to getting a reply"}, /* synonyms for previous commands */ {"?", cmd_help, NULL, "Display commands description"}, {"exit", cmd_quit, NULL, "Quit the program"}, {NULL, NULL, NULL, NULL} }; netopeer2-1.1.70/cli/commands.h0000664000000000000000000000231214021433500014712 0ustar rootroot/** * @file commands.h * @author Michal Vasko <mvasko@cesnet.cz> * @brief netopeer2-cli commands header * * Copyright (c) 2015 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 COMMANDS_H_ #define COMMANDS_H_ #include "cli_version.h" extern char some_msg[]; #define INSTRUCTION(format,args...) {snprintf(some_msg,4095,format,##args);printf("\n %s",some_msg);} #define ERROR(function,format,args...) {snprintf(some_msg,4095,format,##args);fprintf(stderr,"%s: %s\n",function,some_msg);} #ifdef __GNUC__ # define UNUSED(x) UNUSED_ ## x __attribute__((__unused__)) #else # define UNUSED(x) UNUSED_ ## x #endif #include <stdlib.h> #define PROMPT "> " typedef struct { char *name; /* User printable name of the function. */ int (*func)(const char *, char **); /* Function to call to do the command. */ void (*help_func)(void); /* Display command help. */ char *helpstring; /* Documentation for this function. */ } COMMAND; extern COMMAND commands[]; #endif /* COMMANDS_H_ */ netopeer2-1.1.70/cli/completion.c0000664000000000000000000002036214021433500015262 0ustar rootroot/** * @file completion.c * @author Michal Vasko <mvasko@cesnet.cz> * @brief netopeer2-cli auto completion * * Copyright (c) 2015 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 <stdlib.h> #include <stdio.h> #include <sys/types.h> #include <sys/wait.h> #include <sys/stat.h> #include <unistd.h> #include <fcntl.h> #include <dirent.h> #include <errno.h> #include <string.h> #include <nc_client.h> #ifndef HAVE_EACCESS #define eaccess access #endif #include "compat.h" #include "commands.h" #include "linenoise/linenoise.h" extern struct ly_ctx *ctx; extern char *config_editor; static void get_cmd_completion(const char *hint, char ***matches, unsigned int *match_count) { int i; *match_count = 0; *matches = NULL; for (i = 0; commands[i].name; i++) { if (!strncmp(hint, commands[i].name, strlen(hint))) { ++(*match_count); *matches = realloc(*matches, *match_count * sizeof **matches); (*matches)[*match_count-1] = strdup(commands[i].name); } } } static int last_opt(const char *buf, const char *hint, const char *opt) { do { --hint; } while (hint[0] == ' '); if ((unsigned)(hint - buf) < strlen(opt) - 1) { return 0; } hint -= strlen(opt) - 1; if (!strncmp(hint, opt, strlen(opt))) { return 1; } return 0; } void complete_cmd(const char *buf, const char *hint, linenoiseCompletions *lc) { char **matches = NULL; unsigned int match_count = 0, i; if (!strncmp(buf, "searchpath ", 11) #ifdef NC_ENABLED_SSH || !strncmp(buf, "auth keys add ", 14) #endif #ifdef NC_ENABLED_TLS || !strncmp(buf, "cert add ", 9) || !strncmp(buf, "cert remove ", 12) || !strncmp(buf, "cert replaceown ", 16) || !strncmp(buf, "crl add ", 8) || !strncmp(buf, "crl remove ", 11) #endif ) { linenoisePathCompletion(buf, hint, lc); } else if ((!strncmp(buf, "copy-config ", 12) || !strncmp(buf, "validate ", 9)) && last_opt(buf, hint, "--src-config")) { linenoisePathCompletion(buf, hint, lc); } else if (!strncmp(buf, "edit-config ", 12) && last_opt(buf, hint, "--config")) { linenoisePathCompletion(buf, hint, lc); } else if ((!strncmp(buf, "get ", 4) || !strncmp(buf, "get-config ", 11) || !strncmp(buf, "subscribe ", 10)) && (last_opt(buf, hint, "--filter-subtree") || last_opt(buf, hint, "--out"))) { linenoisePathCompletion(buf, hint, lc); } else if (!strncmp(buf, "get-schema ", 11) && last_opt(buf, hint, "--out")) { linenoisePathCompletion(buf, hint, lc); } else if (!strncmp(buf, "user-rpc ", 9) && last_opt(buf, hint, "--content")) { linenoisePathCompletion(buf, hint, lc); } else if (!strchr(buf, ' ') && hint[0]) { get_cmd_completion(hint, &matches, &match_count); for (i = 0; i < match_count; ++i) { linenoiseAddCompletion(lc, matches[i]); free(matches[i]); } free(matches); } } char * readinput(const char *instruction, const char *old_tmp, char **new_tmp) { volatile int tmpfd = -1; int ret, size, oldfd; pid_t pid, wait_pid; char* volatile input = NULL, * volatile old_content = NULL; char *tmpname = NULL, *ptr, *ptr2; /* Create a unique temporary file */ #ifdef HAVE_MKSTEMPS if (asprintf(&tmpname, "/tmp/tmpXXXXXX.xml") == -1) { ERROR(__func__, "asprintf() failed (%s).", strerror(errno)); goto fail; } tmpfd = mkstemps(tmpname, 4); if (tmpfd == -1) { ERROR(__func__, "Failed to create a temporary file (%s).", strerror(errno)); goto fail; } #else if (asprintf(&tmpname, "/tmp/tmpXXXXXX") == -1) { ERROR(__func__, "asprintf() failed (%s).", strerror(errno)); goto fail; } /* cannot fail */ mktemp(tmpname); if (asprintf(&tmpname, ".xml") == -1) { ERROR(__func__, "asprintf() failed (%s).", strerror(errno)); goto fail; } tmpfd = open(tmpname, O_RDWR | O_CREAT | O_EXCL, 0600); if (tmpfd == -1) { ERROR(__func__, "Failed to create a temporary file (%s).", strerror(errno)); goto fail; } #endif /* #ifdef HAVE_MKSTEMPS */ /* Read the old content, if any */ if (old_tmp != NULL) { oldfd = open(old_tmp, O_RDONLY); if (oldfd != -1) { size = lseek(oldfd, 0, SEEK_END); lseek(oldfd, 0, SEEK_SET); if (size > 0) { old_content = malloc(size+1); ret = read(oldfd, old_content, size); if (ret != size) { free(old_content); old_content = NULL; } else { old_content[size] = '\0'; } } close(oldfd); } } if (old_content) { ret = write(tmpfd, old_content, strlen(old_content)); if ((unsigned)ret < strlen(old_content)) { ERROR(__func__, "Failed to write the previous content (%s).", strerror(errno)); goto fail; } } else if (instruction) { ret = write(tmpfd, "\n<!--#\n", 7); ret += write(tmpfd, instruction, strlen(instruction)); ret += write(tmpfd, "\n-->\n", 5); if ((unsigned)ret < 6+strlen(instruction)+5) { ERROR(__func__, "Failed to write the instruction (%s).", strerror(errno)); goto fail; } ret = lseek(tmpfd, 0, SEEK_SET); if (ret == -1) { ERROR(__func__, "Rewinding the temporary file failed (%s).", strerror(errno)); goto fail; } } if ((pid = vfork()) == -1) { ERROR(__func__, "Fork failed (%s).", strerror(errno)); goto fail; } else if (pid == 0) { /* child */ execlp(config_editor, config_editor, tmpname, (char *)NULL); ERROR(__func__, "Exec failed (%s).", strerror(errno)); exit(1); } else { /* parent */ wait_pid = wait(&ret); if (wait_pid != pid) { ERROR(__func__, "Child process other than the editor exited, weird."); goto fail; } if (!WIFEXITED(ret)) { ERROR(__func__, "Editor exited in a non-standard way."); goto fail; } } /* Get the size of the input */ size = lseek(tmpfd, 0, SEEK_END); if (size == -1) { ERROR(__func__, "Failed to get the size of the temporary file (%s).", strerror(errno)); goto fail; } else if (size == 0) { /* not a fail, just no input */ goto fail; } lseek(tmpfd, 0, SEEK_SET); /* Read the input */ input = malloc(size + 1); ret = read(tmpfd, input, size); if (ret < size) { ERROR(__func__, "Failed to read from the temporary file (%s).", strerror(errno)); goto fail; } input[size] = '\0'; /* Remove the instruction comment */ if (!old_content && instruction) { ptr = strstr(input, "\n<!--#\n"); if (!ptr) { goto cleanup; } ptr2 = strstr(ptr, "\n-->\n"); /* The user could have deleted or modified the comment, ignore it then */ if (ptr2) { ptr2 += 5; memmove(ptr, ptr2, strlen(ptr2) + 1); /* Save the modified content */ if (ftruncate(tmpfd, 0) == -1) { ERROR(__func__, "Failed to truncate the temporary file (%s).", strerror(errno)); goto fail; } lseek(tmpfd, 0, SEEK_SET); ret = write(tmpfd, input, strlen(input)); if ((unsigned)ret < strlen(input)) { ERROR(__func__, "Failed to write to the temporary file (%s).", strerror(errno)); goto fail; } } } if (new_tmp) { *new_tmp = tmpname; } else { unlink(tmpname); free(tmpname); } cleanup: close(tmpfd); free(old_content); return input; fail: if (tmpfd > -1) { close(tmpfd); } if (tmpname != NULL) { unlink(tmpname); } free(tmpname); free(old_content); free(input); return NULL; } netopeer2-1.1.70/cli/completion.h0000664000000000000000000000124414021433500015265 0ustar rootroot/** * @file completion.h * @author Michal Vasko <mvasko@cesnet.cz> * @brief netopeer2-cli auto completion header * * Copyright (c) 2015 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 COMPLETION_H_ #define COMPLETION_H_ #include "linenoise/linenoise.h" void complete_cmd(const char *buf, const char *hint, linenoiseCompletions *lc); char *readinput(const char *instruction, const char *old_tmp, char **new_tmp); #endif /* COMPLETION_H_ */ netopeer2-1.1.70/cli/configuration.c0000664000000000000000000004263314021433500015765 0ustar rootroot/** * @file configuration.c * @author Michal Vasko <mvasko@cesnet.cz> * @brief netopeer2-cli configuration * * Copyright (c) 2015 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 <assert.h> #include <stdlib.h> #include <stdio.h> #include <errno.h> #include <string.h> #include <sys/types.h> #include <sys/stat.h> #include <unistd.h> #include <pwd.h> #include <fcntl.h> #include <dirent.h> #include <libyang/libyang.h> #include <nc_client.h> #ifndef HAVE_EACCESS #define eaccess access #endif #include "compat.h" #include "configuration.h" #include "commands.h" #include "linenoise/linenoise.h" extern LYD_FORMAT output_format; extern int output_flag; extern char *config_editor; /* NetConf Client home (appended to ~/) */ #define NCC_DIR ".netopeer2-cli" /* all these appended to NCC_DIR */ #define CA_DIR "certs" #define CRL_DIR "crl" #define CERT_CRT "client.crt" #define CERT_PEM "client.pem" #define CERT_KEY "client.key" char * get_netconf_dir(void) { int ret; struct passwd *pw; char *user_home, *netconf_dir; if (!(pw = getpwuid(getuid()))) { ERROR("get_netconf_dir", "Determining home directory failed: getpwuid: %s.", strerror(errno)); return NULL; } user_home = pw->pw_dir; if (asprintf(&netconf_dir, "%s/%s", user_home, NCC_DIR) == -1) { ERROR("get_netconf_dir", "asprintf() failed (%s:%d).", __FILE__, __LINE__); return NULL; } ret = mkdir(netconf_dir, 00700); if (!ret) { ERROR("get_netconf_dir", "Configuration directory \"%s\" did not exist, created.", netconf_dir); } else if (errno != EEXIST) { ERROR("get_netconf_dir", "Configuration directory \"%s\" cannot be created: %s", netconf_dir, strerror(errno)); free(netconf_dir); return NULL; } return netconf_dir; } void get_default_client_cert(char **cert, char **key) { char *netconf_dir; struct stat st; int ret; assert(cert && !*cert); assert(key && !*key); if (!(netconf_dir = get_netconf_dir())) { return; } // trying to use *.crt and *.key format if (asprintf(cert, "%s/%s", netconf_dir, CERT_CRT) == -1 || asprintf(key, "%s/%s", netconf_dir, CERT_KEY) == -1) { ERROR("get_default_client_cert", "asprintf() failed (%s:%d).", __FILE__, __LINE__); ERROR("get_default_client_cert", "Unable to use the default client certificate due to the previous error."); free(netconf_dir); return; } if (eaccess(*cert, R_OK) == -1 || eaccess(*key, R_OK) == -1) { if (errno != ENOENT) { ERROR("get_default_client_cert", "Unable to access \"%s\" and \"%s\": %s", *cert, *key, strerror(errno)); free(*key); *key = NULL; free(*cert); *cert = NULL; free(netconf_dir); return; } // *.crt & *.key failed, trying to use *.pem format free(*key); *key = NULL; free(*cert); if (asprintf(cert, "%s/%s", netconf_dir, CERT_PEM) == -1) { ERROR("get_default_client_cert", "asprintf() failed (%s:%d).", __FILE__, __LINE__); ERROR("get_default_client_cert", "Unable to use the default client certificate due to the previous error."); free(netconf_dir); return; } ret = eaccess(*cert, R_OK); if (ret == -1) { if (errno != ENOENT) { ERROR("get_default_client_cert", "Unable to access \"%s\": %s", *cert, strerror(errno)); } else { // *.pem failed as well ERROR("get_default_client_cert", "Unable to find the default client certificate."); } free(*cert); *cert = NULL; free(netconf_dir); return; } else { /* check permissions on *.pem */ if (stat(*cert, &st) != 0) { ERROR("get_default_client_cert", "Stat on \"%s\" failed: %s", *cert, strerror(errno)); } else if (st.st_mode & 0066) { ERROR("get_default_client_cert", "Unsafe permissions on \"%s\"", *cert); } } } else { /* check permissions on *.key */ if (stat(*key, &st) != 0) { ERROR("get_default_client_cert", "Stat on \"%s\" failed: %s", *key, strerror(errno)); } else if (st.st_mode & 0066) { ERROR("get_default_client_cert", "Unsafe permissions on \"%s\"", *key); } } free(netconf_dir); } char * get_default_trustedCA_dir(DIR **ret_dir) { char *netconf_dir, *cert_dir; if (!(netconf_dir = get_netconf_dir())) { return NULL; } if (asprintf(&cert_dir, "%s/%s", netconf_dir, CA_DIR) == -1) { ERROR(__func__, "asprintf() failed (%s:%d).", __FILE__, __LINE__); ERROR(__func__, "Unable to use the trusted CA directory due to the previous error."); free(netconf_dir); return NULL; } free(netconf_dir); if (ret_dir) { if (!(*ret_dir = opendir(cert_dir))) { ERROR(__func__, "Unable to open the default trusted CA directory (%s).", strerror(errno)); free(cert_dir); return NULL; } return NULL; } errno = 0; if (eaccess(cert_dir, R_OK | W_OK | X_OK)) { if (errno == ENOENT) { ERROR(__func__, "Default trusted CA directory does not exist, creating it."); if (mkdir(cert_dir, 00777)) { ERROR(__func__, "Failed to create the default trusted CA directory (%s).", strerror(errno)); free(cert_dir); return NULL; } } else { ERROR(__func__, "Unable to access the default trusted CA directory (%s).", strerror(errno)); free(cert_dir); return NULL; } } return cert_dir; } char * get_default_CRL_dir(DIR **ret_dir) { char *netconf_dir, *crl_dir; if (!(netconf_dir = get_netconf_dir())) { return NULL; } if (asprintf(&crl_dir, "%s/%s", netconf_dir, CRL_DIR) == -1) { ERROR(__func__, "asprintf() failed (%s:%d).", __FILE__, __LINE__); ERROR(__func__, "Unable to use the trusted CA directory due to the previous error."); free(netconf_dir); return NULL; } free(netconf_dir); if (ret_dir) { if (!(*ret_dir = opendir(crl_dir))) { ERROR(__func__, "Unable to open the default CRL directory (%s).", strerror(errno)); free(crl_dir); return NULL; } return NULL; } errno = 0; if (eaccess(crl_dir, R_OK | W_OK | X_OK)) { if (errno == ENOENT) { ERROR(__func__, "Default CRL dir does not exist, creating it."); if (mkdir(crl_dir, 00777)) { ERROR(__func__, "Failed to create the default CRL directory (%s).", strerror(errno)); free(crl_dir); return NULL; } } else { ERROR(__func__, "Unable to access the default CRL directory (%s).", strerror(errno)); free(crl_dir); return NULL; } } return crl_dir; } void load_config(void) { char *netconf_dir, *history_file, *config_file = NULL; struct lyxml_elem *config_xml = NULL, *child; struct ly_ctx *ctx; #ifdef NC_ENABLED_SSH const char *key_pub, *key_priv; struct lyxml_elem *auth_child, *pref_child, *key_child, *pair_child; #endif if ((netconf_dir = get_netconf_dir()) == NULL) { return; } if (asprintf(&history_file, "%s/history", netconf_dir) == -1) { ERROR(__func__, "asprintf() failed (%s:%d).", __FILE__, __LINE__); ERROR(__func__, "Unable to load commands history due to the previous error."); history_file = NULL; } else { if (eaccess(history_file, F_OK) && (errno == ENOENT)) { ERROR(__func__, "No saved history."); } else if (linenoiseHistoryLoad(history_file)) { ERROR(__func__, "Failed to load history."); } } ctx = ly_ctx_new(NULL, 0); if (!ctx) { ERROR(__func__, "Failed to create context."); ERROR(__func__, "Unable to load configuration due to the previous error."); ctx = NULL; } else { if (asprintf(&config_file, "%s/config.xml", netconf_dir) == -1) { ERROR(__func__, "asprintf() failed (%s:%d).", __FILE__, __LINE__); ERROR(__func__, "Unable to load configuration due to the previous error."); config_file = NULL; } else if (eaccess(config_file, F_OK) && (errno == ENOENT)) { ERROR(__func__, "No saved configuration."); } else { if ((config_xml = lyxml_parse_path(ctx, config_file, 0)) == NULL) { ERROR(__func__, "Failed to load configuration of NETCONF client (lyxml_read_path failed)."); config_xml = NULL; } else { /* doc -> <netconf-client/>*/ if (!strcmp(config_xml->name, "netconf-client")) { LY_TREE_FOR(config_xml->child, child) { if (!strcmp(child->name, "editor")) { /* doc -> <netconf-client> -> <editor> */ if (config_editor) { free(config_editor); } config_editor = strdup(child->content); } else if (!strcmp(child->name, "searchpath")) { /* doc -> <netconf-client> -> <searchpath> */ errno = 0; if (!mkdir(child->content, 00700) || (errno == EEXIST)) { if (errno == 0) { ERROR(__func__, "Search path \"%s\" did not exist, created.", child->content); } nc_client_set_schema_searchpath(child->content); } else { ERROR(__func__, "Search path \"%s\" cannot be created: %s", child->content, strerror(errno)); } } else if (!strcmp(child->name, "output-format")) { /* doc -> <netconf-client> -> <output-format> */ if (!strcmp(child->content, "json")) { output_format = LYD_JSON; output_flag = LYP_FORMAT; } else if (!strcmp(child->content, "json_noformat")) { output_format = LYD_JSON; output_flag = 0; } else if (!strcmp(child->content, "xml_noformat")) { output_format = LYD_XML; output_flag = 0; } /* else default (formatted XML) */ } #ifdef NC_ENABLED_SSH else if (!strcmp(child->name, "authentication")) { /* doc -> <netconf-client> -> <authentication> */ LY_TREE_FOR(child->child, auth_child) { if (!strcmp(auth_child->name, "pref")) { LY_TREE_FOR(auth_child->child, pref_child) { if (!strcmp(pref_child->name, "publickey")) { nc_client_ssh_set_auth_pref(NC_SSH_AUTH_PUBLICKEY, atoi(pref_child->content)); } else if (!strcmp(pref_child->name, "interactive")) { nc_client_ssh_set_auth_pref(NC_SSH_AUTH_INTERACTIVE, atoi(pref_child->content)); } else if (!strcmp(pref_child->name, "password")) { nc_client_ssh_set_auth_pref(NC_SSH_AUTH_PASSWORD, atoi(pref_child->content)); } } } else if (!strcmp(auth_child->name, "keys")) { LY_TREE_FOR(auth_child->child, key_child) { if (!strcmp(key_child->name, "pair")) { key_pub = NULL; key_priv = NULL; LY_TREE_FOR(key_child->child, pair_child) { if (!strcmp(pair_child->name, "public")) { key_pub = pair_child->content; } else if (!strcmp(pair_child->name, "private")) { key_priv = pair_child->content; } } if (key_pub && key_priv) { nc_client_ssh_ch_add_keypair(key_pub, key_priv); nc_client_ssh_add_keypair(key_pub, key_priv); } } } } } } #endif /* ENABLE_SSH */ } } } } } lyxml_free(ctx, config_xml); ly_ctx_destroy(ctx, NULL); free(config_file); free(history_file); free(netconf_dir); } void store_config(void) { char *netconf_dir, *history_file, *config_file; int indent; FILE *config_f = NULL; if ((netconf_dir = get_netconf_dir()) == NULL) { return; } if (asprintf(&history_file, "%s/history", netconf_dir) == -1) { ERROR(__func__, "asprintf() failed (%s:%d).", __FILE__, __LINE__); ERROR(__func__, "Unable to store commands history due to the previous error."); history_file = NULL; } else { if (linenoiseHistorySave(history_file)) { ERROR(__func__, "Failed to save history."); } } if (asprintf(&config_file, "%s/config.xml", netconf_dir) == -1) { ERROR(__func__, "asprintf() failed (%s:%d).", __FILE__, __LINE__); ERROR(__func__, "Unable to store configuration due to the previous error."); config_file = NULL; } else if ((config_f = fopen(config_file, "w")) == NULL) { ERROR(__func__, "fopen failed (%s).", strerror(errno)); ERROR(__func__, "Unable to store configuration due to the previous error."); } else { indent = 0; fprintf(config_f, "%*.s<netconf-client>\n", indent, ""); ++indent; /* editor */ fprintf(config_f, "%*.s<editor>%s</editor>\n", indent, "", config_editor); /* search-path */ if (nc_client_get_schema_searchpath()) { fprintf(config_f, "%*.s<searchpath>%s</searchpath>\n", indent, "", nc_client_get_schema_searchpath()); } /* output-format */ fprintf(config_f, "%*.s<output-format>", indent, ""); if (output_format == LYD_JSON) { if (output_flag) { fprintf(config_f, "json"); } else { fprintf(config_f, "json_noformat"); } } else { if (output_flag) { fprintf(config_f, "xml"); } else { fprintf(config_f, "xml_noformat"); } } fprintf(config_f, "</output-format>\n"); #ifdef NC_ENABLED_SSH /* SSH authentication */ fprintf(config_f, "%*.s<authentication>\n", indent, ""); ++indent; /* pref */ fprintf(config_f, "%*.s<pref>\n", indent, ""); ++indent; fprintf(config_f, "%*.s<publickey>%d</publickey>\n", indent, "", nc_client_ssh_get_auth_pref(NC_SSH_AUTH_PUBLICKEY)); fprintf(config_f, "%*.s<password>%d</password>\n", indent, "", nc_client_ssh_get_auth_pref(NC_SSH_AUTH_PASSWORD)); fprintf(config_f, "%*.s<interactive>%d</interactive>\n", indent, "", nc_client_ssh_get_auth_pref(NC_SSH_AUTH_INTERACTIVE)); --indent; fprintf(config_f, "%*.s</pref>\n", indent, ""); /* keys */ if (nc_client_ssh_get_keypair_count()) { fprintf(config_f, "%*.s<keys>\n", indent, ""); ++indent; /* pair(s) */ for (int i = 0; i < nc_client_ssh_get_keypair_count(); ++i) { const char *priv_key, *pub_key; nc_client_ssh_get_keypair(i, &pub_key, &priv_key); fprintf(config_f, "%*.s<pair>\n", indent, ""); ++indent; fprintf(config_f, "%*.s<public>%s</public>\n", indent, "", pub_key); fprintf(config_f, "%*.s<private>%s</private>\n", indent, "", priv_key); --indent; fprintf(config_f, "%*.s</pair>\n", indent, ""); } --indent; fprintf(config_f, "%*.s</keys>\n", indent, ""); } --indent; fprintf(config_f, "%*.s</authentication>\n", indent, ""); #endif --indent; fprintf(config_f, "%*.s</netconf-client>\n", indent, ""); fclose(config_f); } free(history_file); free(netconf_dir); free(config_file); } netopeer2-1.1.70/cli/configuration.h0000664000000000000000000000367214021433500015772 0ustar rootroot/** * @file configuration.h * @author Michal Vasko <mvasko@cesnet.cz> * @brief netopeer2-cli configuration header * * Copyright (c) 2015 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 CONFIGURATION_H_ #define CONFIGURATION_H_ #include <dirent.h> /** * @brief The CLI XML config options. */ struct cli_opts { char* config_editor; }; /** * @brief Finds the current user's netconf dir * @return NULL on failure, dynamically allocated netconf dir path * otherwise */ char *get_netconf_dir(void); /** * @brief Finds the default certificate and optionally key file, * the supplied pointers must be empty (*cert == NULL) * @param[out] cert path to the certificate (and perhaps also key), * no change on error * @param[out] key path to the private key, no change if the key * is included in cert */ void get_default_client_cert(char **cert, char **key); /** * @brief Finds the default trusted CA certificate directory * @return ret_dir == NULL: NULL on failure, dynamically allocated trusted CA dir path * otherwise, ret_dir != NULL: always NULL, on success *ret_dir is opened trusted CA * dir, not modified on error */ char *get_default_trustedCA_dir(DIR **ret_dir); /** * @brief Finds the default CRL directory * @return ret_dir == NILL: NULL on failure, dynamically allocated CRL dir path otherwise, * ret_dir != NULL: always NULL, on success *ret_dir is opened CRL dir, not modified * on error */ char *get_default_CRL_dir(DIR **ret_dir); /** * @brief Checks all the relevant files and directories creating any * that are missing, sets the saved configuration */ void load_config(void); /** * @brief Saves the current configuration and command history */ void store_config(void); #endif /* CONFIGURATION_H_ */ netopeer2-1.1.70/cli/doc/0000775000000000000000000000000014021433500013507 5ustar rootrootnetopeer2-1.1.70/cli/doc/netopeer2-cli.10000664000000000000000000005365614021433500016260 0ustar rootroot.\" Process this file with .\" groff -man -Tascii netopeer2-cli.1 .\" .TH "netopeer2-cli" 1 "Wed February 24 2016" "Netopeer" .SH NAME netopeer2-cli \- NETCONF client with command line interface build on libnetconf2 .SH DESCRIPTION .B netopeer2-cli serves as a generic NETCONF client providing a simple interactive command line interface. It allows user to establish a NETCONF session with a NETCONF-enabled device on the network and to obtain and manipulate its configuration data. .B netopeer2-cli is limited to a single NETCONF connection at a time via a forward or a reverse (Call Home) connecting method. .PP Here is the list of NETCONF capabilities supported by .BR netopeer2-cli : .IP \(bu 2 NETCONF protocol version 1.0 and 1.1 (RFC 6241) .IP \(bu 2 SSH transport (RFC 6242) .IP \(bu 2 TLS transport (RFC 5539bis) .IP \(bu 2 Call Home (reverse SSH/TLS) .IP \(bu 2 Event Notifications (RFC 5277) .IP \(bu 2 Access Control (RFC 6536) .IP \(bu 2 :writable-running capability .IP \(bu 2 :startup capability .IP \(bu 2 :candidate capability .IP \(bu 2 :confirmed-commit capability .IP \(bu 2 :validate capability version 1.1 .IP \(bu 2 :with-defaults capability (RFC 6243) .IP \(bu 2 :url capability .IP \(bu 2 :xpath capability .SH FILES .I ~/.netopeer2-cli/config.xml .RS Per user configuration for NETCONF <hello> message (see RFC 6241 for further details). .RE .PP .I ~/.netopeer2-cli/history .RS Per user history of executed commands. .RE .PP .I ~/.netopeer2-cli/client.pem .RS Per user certificate with its private key that is sent to the server for verification. If present together with .B client.crt and .B client.key it is not utilized. Only with TLS support. .RE .PP .I ~/.netopeer2-cli/client.crt .RS Per user certificate that is sent to the server for verification. Needs a corresponding private key to be utilized. Only with TLS support. .RE .PP .I ~/.netopeer2-cli/client.key .RS Per user private key for the user certificate. Needs a corresponding certificate to be utilized. Only with TLS support. .RE .PP .I ~/.netopeer2-cli/certs .RS Per user trusted Certificate Authority directory that is searched when verifying a server certificate. Only with TLS support. .RE .PP .I ~/.netopeer2-cli/crl .RS Per user Certificate Revocation List directory that is searched when verifying a server certificate. Only with TLS support. .RE .PP .I ~/.ssh/netconf_known_hosts .RS Alternative known_hosts file if the original file contains libssh incompatible records. Only with SSH support. .SH TRANSPORT The supported transport protocols are detected automatically based on how was libnetconf2 compiled. Unsupported (sub)commands are then not available and are hidden in the help. This page, however, contains them all. .SH COMMANDS .SS help Display list of commands. \fI\-\-(h)elp\fR option is also accepted by all commands to show detailed information about the command. .SS connect Connect to a NETCONF server. .PP .B connect [\-\-help] [\-\-host \fIhostname\fR] [\-\-port \fInum\fR] [\-\-ssh] [\-\-login \fIusername\fR] \-\-tls [\-\-cert \fIcert_path\fR [\-\-key \fIkey_path\fR]] [\-\-trusted \fItrusted_CA_store.pem\fR] .PP .RS 4 .B \-\-h(o)st \fIhostname\fR .RS 4 Specifies the hostname of the NETCONF server to connect to. If not specified, 'localhost' is used. .RE .PP .B \-\-(p)ort \fInum\fR .RS 4 Port to connect to the NETCONF server on. By default, port \fI830\fR for SSH or \fI6513\fR for TLS transport is used. .RE .PP .B \-\-(s)sh .RS 4 Use NETCONF over SSH transport. This is the default, when both SSH and TLS are enabled. .RE .PP .B \-\-(l)ogin \fIusername\fR .RS 4 Specify the SSH username to connect with. By default, the current system user is used. .RE .PP .B \-\-(t)ls .RS 4 Use NETCONF over TLS transport. .RE .PP .B \-\-(c)ert \fIcert_path\fR .RS 4 Use a specific certificate for TLS handshake. \fIcert_path\fR specifies path to the client certificate in CRT format. If the .B \-\-key option is not specified, \fIcert_path\fR is expected to contain also the private key for the client certificate, in PEM format. .RE .PP .B \-\-(k)ey \fIkey_path\fR .RS 4 Specifies path to the private key for the client certificate in KEY format. If not specified, \fIcert_path\fR is expected to contain also the private key for the client certificate, in PEM format. .RE .PP .B \-\-t(r)usted \fItrusted_CA_store.pem\fR .RS 4 Specifies path to a trusted CA certificate bundle in PEM format to be used exclusively for server verification for this connection instead of the default CA directory. .RE .RE .SS listen Listen for a NETCONF Call Home. .PP .B listen [\-\-help] [\-\-timeout \fIsec\fR] [\-\-host \fIhostname\fR] [\-\-port \fInum\fR] [\-\-ssh] [\-\-login \fIusername\fR] \-\-tls [\-\-cert \fIcert_path\fR [\-\-key \fIkey_path\fR]] [\-\-trusted \fItrusted_CA_store.pem\fR] .PP .RS 4 .B \-\-t(i)meout \fIsec\fR .RS 4 Specifies the time for how long the CLI will listen for the Call Home connection. By default, 60 (a minute) is used. .RE .PP .B \-\-h(o)st \fIhostname\fR .RS 4 Specifies the hostname (interface) to listen on. By default, ::0 is used. .RE .PP .B \fIother options\fR .RS 4 The rest of the options have similar meaning as in .B connect. .RE .RE .SS disconnect Disconnect from a NETCONF server. .RE .RE .SS commit Perform NETCONF <commit> operation. For more details see \fIRFC 6241 section 8.3.4.1\fR. .PP .B commit [\-\-help] [\-\-confirmed] [\-\-confirm-timeout \fIsec\fR] [\-\-persist \fInew-commit-id\fR] [\-\-persist-id \fIcommit-id\fR] .PP .RS 4 .B \-\-(c)onfirmed .RS 4 Make the <commit> a confirmed commit. See \fIRFC 6421 section 8.4\fR. .RE .PP .B \-\-confirm-(t)imeout \fIsec\fR .RS 4 Specify the confirmed commit timeout. Can be used only with .B \-\-confirmed. .RE .PP .B \-\-(p)ersist \fInew-commit-id\fR .RS 4 Make the commit a persistent commit. \fInew-commit-id\fR is the identificator of the commit. .RE .PP .B \-\-persist-(i)d \fIcommit-id\fR .RS 4 Confirm a persistent commit with the \fIcommit-id\fR identificator. .RE .RE .SS cancel-commit Cancel a pending confirmed commit. .PP .B cancel-commit [\-\-help] [\-\-persist\-id \fIcommit-id\fR] .PP .RS 4 .B \-\-persist-(i)d \fIcommit-id\fR .RS 4 Cancel a specific \fIcommit-id\fR pending confirmed commit. .RE .RE .SS copy-config Perform NETCONF <copy-config> operation. For more details see \fIRFC 6241 section 7.3\fR. .PP .B copy-config [\-\-help] \-\-target running|startup|candidate|url:\fIurl\fR (\-\-source running|startup|candidate|url:\fIurl\fR | \-\-src-config [\fIfile\fR]) [\-\-defaults report-all|report-all-tagged|trim|explicit] .PP .RS 4 .B \-\-(t)arget running|startup|candidate|url:\fIurl\fR .RS 4 Specifies the target of the <copy-config> operation. Either a datastore or a specific URL can be specified. .RE .PP .B \-\-(s)ource running|startup|candidate|url:\fIurl\fR .RS 4 Specifies the source of the <copy-config> operation. Either a datastore or a specific URL can be specified. .RE .PP .B \-\-src-(c)onfig [\fIfile\fR] .RS 4 Specifies the source of the <copy-config> operation. If a \fIfile\fR with the configuration is not specified, the configuration is read from the standard input. .RE .PP .B \-\-(d)efaults report\-all|report\-all\-tagged|trim|explicit .RS 4 Use :with-defaults capability with the specified retrieval mode. For more details see \fIRFC 6243 section 3\fR. .RE .RE .SS delete-config Perform NETCONF <delete-config> operation. For more details see \fIRFC 6241 section 7.4\fR. .PP .B delete\-config [\-\-help] \-\-target startup|url:\fIurl\fR .PP .RS 4 .B \-\-(t)arget startup|url:\fIurl\fR .RS 4 Specifies the target of the <delete-config> operation. Either the startup datastore or a specific URL can be specified. .RE .RE .SS discard-changes Perform NETCONF <discard-changes> operation. It reverts the .I candidate configuration to the current .I running configuration. For more details see \fIRFC 6241 section 8.3.4.2\fR. .RE .RE .SS edit-config Perform NETCONF <edit-config> operation. For more details see \fIRFC 6241 section 7.2\fR. .PP .B edit-config [\-\-help] \-\-target running|candidate (\-\-config [\fIfile\fR] | \-\-url \fIurl\fR) [\-\-defop merge|replace|none] [\-\-test set|test-only|test-then-set] [\-\-error stop|continue|rollback] .PP .RS 4 .B \-\-(t)arget running|candidate .RS 4 Specifies the target of the <edit-config> operation. .RE .PP .B \-\-(c)onfig [\fIfile\fR] .RS 4 Specifies the content of the <edit-config> operation. If a \fIfile\fR with the configuration is not specified, the configuration is read from the standard input. .RE .PP .B \-\-(u)rl \fIur\fR .RS 4 Specifies the content of the <edit-config> operation. It is retrieved from that specific URL. .RE .PP .B \-\-def(o)p merge|replace|none .RS 4 Specifies default operation for applying configuration data. .IP merge Merge configuration data at the corresponding level. This is the default value. .IP replace Edit configuration data completely replaces the configuration in the target datastore. .IP none The target datastore is unaffected by the edit configuration data, unless and until the edit configuration data contains the .I operation attribute to request a different operation. .RE .PP .B \-\-t(e)st set|test-only|test-then-set .RS 4 Perform validation of the modified configuration data. This option is available only if the server supports :validate:1.1 capability (see \fIRFC 6241 section 8.6\fR). .IP set Do not perform validation test. .IP test-only Do not apply the modified data, only perform the validation test. .IP test-then-set Perform a validation test before attempting to apply modified configuration data. This is the default value. .RE .PP .B \-\-e(r)ror stop|continue|rollback .RS 4 Set reaction to an error. .IP stop Abort the operation on first error. This is the default value. .IP continue Continue to process configuration data on error. The error is recorded and negative response is returned. .IP rollback Stop the operation processing on error and restore the configuration to its complete state at the start of this operation. This \fIaction\fR is available only if the server supports :rollback-on-error capability (see \fIRFC 6241 section 8.5\fR). .RE .RE .SS get Perform NETCONF <get> operation. Retrieves both status as well as configuration data from the current running datastore. For more details see \fIRFC 6241 section 7.7\fR. .PP .B get [\-\-help] [\-\-filter-subtree [\fIfile\fR] | \-\-filter-xpath \fIXPath\fR] [\-\-defaults report-all|report-all-tagged|trim|explicit] [\-\-out \fIfile\fR] .PP .RS 4 .B \-\-filter-(s)ubtree [\fIfile\fR] .RS 4 Specifies if the request will contain a subtree filter (\fIRFC 6241 section 6\fR). The \fIfile\fR is expected to contain the filter specification. If it is not specified, it is read from standard input. .RE .PP .B \-\-filter-(x)path \fIXPath\fR .RS 4 Specififes is the request will contain an XPath filter. \fIXPath\fR is the expression used for filtering. .RE .PP .B \-\-(d)efaults report-all|report-all-tagged|trim|explicit .RS 4 Use :with-defaults capability with specified retrieval mode. For more details see \fIRFC 6243 section 3\fR. .RE .PP .B \-\-(o)ut \fIfile\fR .RS 4 Print the result of the command into a file rather than to the standard output. .RE .RE .SS get-config Perform NETCONF <get-config> operation. Retrieves only configuration data from the specified source. For more details see \fIRFC 6241 section 7.1\fR. .PP .B get-config [\-\-help] \-\-source running|startup|candidate [\-\-filter-subtree [\fIfile\fR] | \-\-filter-xpath \fIXPath\fR] [\-\-defaults report-all|report-all-tagged|trim|explicit] [\-\-out \fIfile\fR] .PP .RS 4 .B \-\-(s)ource running|startup|candidate .RS 4 Specifies the source of the <get-config> operation. .RE .PP .B \-\-filter-(s)ubtree [\fIfile\fR] .RS 4 Specifies if the request will contain a subtree filter (\fIRFC 6241 section 6\fR). The \fIfile\fR is expected to contain the filter specification. If it is not specified, it is read from standard input. .RE .PP .B \-\-filter-(x)path \fIXPath\fR .RS 4 Specififes is the request will contain an XPath filter. \fIXPath\fR is the expression used for filtering. .RE .PP .B \-\-(d)efaults report-all|report-all-tagged|trim|explicit .RS 4 Use :with-defaults capability with specified retrieval mode. For more details see \fIRFC 6243 section 3\fR. .RE .PP .B \-\-(o)ut \fIfile\fR .RS 4 Print the result of the command into a file rather than to the standard output. .RE .RE .SS get-schema Perform NETCONF <get-schema> operation that retrieves specified data model used by the server. This operation is available only if the server implements the YANG Module for NETCONF Monitoring. The list of available schemas can be retrieved from .I /netconf-state/schemas subtree via the <get> operation. For more details see \fIRFC 6022 sections 3.1 and 4\fR. .PP .B get-schema [\-\-help] \-\-model \fIidentifier\fR [\-\-version \fIversion\fR] [\-\-format \fIformat\fR] [\-\-out \fIfile\fR] .PP .RS 4 .B \-\-(m)odel \fIidentifier\fR .RS 4 Identifier for the schema list entry. .RE .PP .B \-\-(v)ersion \fIversion\fR .RS 4 Version of the requested schema. .RE .PP .B \-\-(f)ormat \fIformat\fR .RS 4 The data modeling language (format) of the requested schema. Default value is .IR yang . .RE .PP .B \-\-(o)ut \fIfile\fR .RS 4 Print the result of the command into a file rather than to the standard output. .RE .RE .SS kill-session Perform NETCONF <kill-session> operation to terminate specified NETCONF session. To terminate the current session, use the .B disconnect command. For more details see \fIRFC 6241 section 7.9\fR. .PP .B kill-session [\-\-help] \-\-sid \fIsesion-ID\fR .PP .RS 4 .B \-\-(s)id \fIsession-ID\fR .RS 4 Session identifier of the NETCONF session to be terminated. .RE .RE .SS lock Perform the NETCONF <lock> operation to lock the entire configuration datastore of a server. For more details see \fIRFC 6241 section 7.5\fR. .PP .B lock [\-\-help] \-\-target running|startup|candidate .PP .RS 4 .B \-\-(t)arget running|startup|candidate .RS 4 Target datastore to lock.. .RE .RE .SS unlock Perform the NETCONF <unlock> operation to release a configuration lock, previously obtained with the <lock> operation. For more details see \fIRFC 6241 section 7.6\fR. .PP .B lock [\-\-help] \-\-target running|startup|candidate .PP .RS 4 .B \-\-(t)arget running|startup|candidate .RS 4 Target datastore to unlock. .RE .RE .SS validate Perform the NETCONF <validate> operation to validate configuration data. For more details see \fIRFC 6241 section 8.6.4.1\fR. .PP .B validate [\-\-help] (\-\-source running|startup|candidate|url:\fIurl\fR | \-\-src-config [\fIfile\fR]) .PP .RS 4 .B \-\-(s)ource running|startup|candidate|url:\fIurl\fR .RS 4 Specifies the source of the <validate> operation. Either a datastore or a specific URL can be specified. .RE .PP .B \-\-src-(c)onfig [\fIfile\fR] .RS 4 Specifies the source of the <validate> operation. If a \fIfile\fR with the configuration is not specified, the configuration is read from the standard input. .RE .RE .SS subscribe Perform NETCONF Event Notifications <create-subscription> operation. For more details see \fIRFC 5277 section 2.1.1\fR. .PP .B subscribe [\-\-help] [\-\-filter-subtree [\fIfile\fR] | \-\-filter-xpath \fIXPath\fR] [\-\-begin \fItime\fR] [\-\-end \fItime\fR] [\-\-stream \fIstream\fR] [\-\-out \fIfile\fR] .PP .RS 4 .B \-\-filter-(s)ubtree [\fIfile\fR] .RS 4 Specifies if the request will contain a subtree filter (\fIRFC 6241 section 6\fR). The \fIfile\fR is expected to contain the filter specification. If it is not specified, it is read from standard input. .RE .PP .B \-\-filter-(x)path \fIXPath\fR .RS 4 Specififes is the request will contain an XPath filter. \fIXPath\fR is the expression used for filtering. .RE .PP .B \-\-(b)egin \fItime\fR .RS 4 Start to replay event notifications from past (future time is not valid). If the start time is not specified, replay feature is not activated and only new event notifications are received. Format of the \fItime\fR parameter is described below. .RE .PP .B \-\-(e)nd \fItime\fR .RS 4 Specifies when the event replay stops. Format of the \fItime\fR parameter is as follows: .IP +\fItime\fR Current time plus the given number of seconds. .IP \fItime\fR Absolute time as number of seconds since 1970-01-01. .IP -\fItime\fR Current time minus the given number of seconds. .RE .PP .B \-\-s(t)ream \fIstream\fR .RS 4 Specifies which event stream is of the interest. If not specified, default NETCONF stream is received. The list of available streams can be retrieved from .I /netconf/streams subtree via the <get> operation. .RE .PP .B \-\-(o)ut \fIfile\fR .RS 4 Print the received notifications into a file rather than to the standard output. .RE .RE .SS cert Manage client and trusted Certificate Authority certificates that are used for TLS verification stored in \fI~/.netopeer2-cli/client.*\fR and \fI~/.netopeer2-cli/certs\fR respectively. .PP This command is available with TLS support. .PP .B cert [\-\-help] [display] [add \fIcert_path\fR] [remove \fIcert_name\fR] [displayown] [replaceown (\fIcert_path.pem\fR | \fIcert_path.crt\fR \fIkey_path.key\fR)] .PP .RS 4 .B display .RS 4 Displays all the recognized certificates in \fI~/.netopeer2-cli/certs\fR. First the file name and serial number, then subject, issuer and validity are shown for each certificate. .RE .PP .B add \fIcert_path\fR .RS 4 Adds the \fIcert_path\fR certificate to the trusted CA dir \fI~/.netopeer2-cli/certs\fR and recalculates hashes of all the certificates. .RE .PP .B remove \fIcert_name\fR .RS 4 Removes the \fIcert_name\fR certificate from the trusted CA dir \fI~/.netopeer2-cli/certs\fR and recalculates hashes of all the certificates. \fIcert_name\fR is the certificate file name, as displayed in the .B cert display command output. .RE .PP .B displayown .RS 4 Shows information about the status of the default client certificates and displays information about any relevant found client certificates \fI~/.netopeer2-cli/client.crt\fR and \fI~/.netopeer2-cli/client.pem\fR. .RE .PP .B replaceown \fIcert_path\fR [\fIkey_path\fR] .RS 4 Replaces the default client certificates with the PEM certificate in \fIcert_path\fR or the CRT certificate \fIcert_path\fR and private key \fIkey_path\fR. .B All previous client certificates are deleted. .RE .PP .SS crl Manage Certificate Revocation List certificates that are stored in the \fI~/.netopeer2-cli/crl\fR directory. .PP This command is available only with TLS support. .PP .B crl [\-\-help] [display] [add \fIcrl_path\fR] [remove \fIcrl_name\fR] .PP .RS 4 .B display .RS 4 Displays all the recognized CRLs in \fI~/.netopeer2-cli/crl\fR. First the file name, then issuer, last and next update dates are shown for each CRL followed by the serial numbers and revocation dates of all the revocated certificates. .RE .PP .B add \fIcrl_path\fR .RS 4 Adds the \fIcrl_path\fR CRL to the \fI~/.netopeer2-cli/crl\fR dir and recalculates hashes of all the CRLs. .RE .PP .B remove \fIcrl_name\fR .RS 4 Removes the \fIcert_name\fR CRL from the \fI~/.netopeer2-cli/crl\fR dir and recalculates hashes of all the CRLs. \fIcrl_name\fR is the CRL file name, as displayed in the .B crl display command output. .RE .RE .SS auth Manage SSH authentication options - authentication preferences and private keys. .PP This command is available with SSH support. .PP .B auth (\-\-help | pref [(publickey | interactive | password) \fIpreference\fR] | keys [add \fIprivate_key_path\fR] [remove \fIkey_index\fR]) .PP .RS 4 .B pref .RS 4 Print the current authentication method preferences or change a specific method preference. .RE .PP \fIpreference\fR .RS 4 The new preference of the authentication method. Negative values disable the authentication method. .RE .PP .B keys .RS 4 Manage keys used for authentication. .RE .PP \fIpath_to_the_key\fR .RS 4 If specified, .B add or .B remove the keys. If not, show the current keys. .RE .PP .SS editor Manage the external editor used for modifying raw input data. .PP .B editor [\-\-help] [\fIpath/name_of_the_editor\fR] .PP .RS 4 \fIpath/name_of_the_editor\fR .RS 4 The full path or just the name of the editor to be used. .RE .RE .SS status Print information about the current NETCONF session. .RE .RE .SS user-rpc Send your own content in an RPC envelope. .PP .B user-rpc [\-\-help] [\-\-content \fIfile\fR] [\-\-out \fIfile\fR] .PP .RS 4 .B \-\-(c)ontent \fIfile\fR .RS 4 Specifies a file containing NETCONF RPC operation in XML format. Only the NETCONF <rpc> envelope is added to the file content and then it is sent to a server. If the file is omitted, the RPC content is read from the standard input. .RE .PP .B \-\-(o)ut \fIfile\fR .RS 4 Print the command result into a file rather than to the standard output. .RE .RE .SS searchpath Set the directory, which will be used when searching for modules. Modules are always needed to be able to work with the same data as a NETCONF server. They are searched locally (in this directory) only if the server does not support the <get-schema> NETCONF operation. If it does, all the modules (except \fIietf-netconf-monitoring\fR, which includes the definition of the <get-schema> RPC itself) are downloaded from the server. .PP .B searchpath \fImodel-dir-path\fR .RE .RE .SS outputformat Set the format for all the output data. XML is the default. .PP .B outputformat (xml | xml_noformat | json) .RE .RE .SS version Print the CLI version. .RE .RE .SS verb Set CLI verbosity. Only errors are printed by default. .PP .B verb (error/0 | warning/1 | verbose/2 | debug/3) .RE .RE .SS quit Quit the program. .SH "SEE ALSO" RFC 5277 (Event Notifications) .br RFC 6241 (NETCONF v1.1) .br RFC 6242 (NETCONF over SSH) .br RFC 6243 (With-defaults capability) .br RFC 6536 (NETCONF Access Control) .br .na http://tools.ietf.org/html/draft-ietf-netconf-rfc5539bis-05 (NETCONF over TLS) .br http://tools.ietf.org/html/draft-ietf-netconf-reverse-ssh-05 (Call Home) .br https://github.com/CESNET/netopeer2 (Netopeer homepage) .ad .SH MAILING LIST To discuss various topics about .B netopeer2-cli, look at Netopeer2 issue tracker at <https://github.com/CESNET/netopeer2/issues>. .SH REPORTING BUGS Report bugs to the issue tracker at the same URL. .SH AUTHORS Radek Krejci <rkrejci@cesnet.cz> .SH COPYRIGHT Copyright \(co 2012-2016 CESNET, z.s.p.o. netopeer2-1.1.70/cli/linenoise/0000775000000000000000000000000014021433500014727 5ustar rootrootnetopeer2-1.1.70/cli/linenoise/LICENSE0000664000000000000000000000260014021433500015732 0ustar rootrootCopyright (c) 2010-2014, Salvatore Sanfilippo <antirez at gmail dot com> Copyright (c) 2010-2013, Pieter Noordhuis <pcnoordhuis at gmail dot com> All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT OWNER OR CONTRIBUTORS 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. netopeer2-1.1.70/cli/linenoise/linenoise.c0000664000000000000000000011466014021433500017070 0ustar rootroot/* linenoise.c -- VERSION 1.0 * * Guerrilla line editing library against the idea that a line editing lib * needs to be 20,000 lines of C code. * * You can find the latest source code at: * * http://github.com/antirez/linenoise * * Does a number of crazy assumptions that happen to be true in 99.9999% of * the 2010 UNIX computers around. * * ------------------------------------------------------------------------ * * Copyright (c) 2010-2014, Salvatore Sanfilippo <antirez at gmail dot com> * Copyright (c) 2010-2013, Pieter Noordhuis <pcnoordhuis at gmail dot com> * * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "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 COPYRIGHT * HOLDER OR CONTRIBUTORS 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. * * ------------------------------------------------------------------------ * * References: * - http://invisible-island.net/xterm/ctlseqs/ctlseqs.html * - http://www.3waylabs.com/nw/WWW/products/wizcon/vt220.html * * Todo list: * - Filter bogus Ctrl+<char> combinations. * - Win32 support * * Bloat: * - History search like Ctrl+r in readline? * * List of escape sequences used by this program, we do everything just * with three sequences. In order to be so cheap we may have some * flickering effect with some slow terminal, but the lesser sequences * the more compatible. * * EL (Erase Line) * Sequence: ESC [ n K * Effect: if n is 0 or missing, clear from cursor to end of line * Effect: if n is 1, clear from beginning of line to cursor * Effect: if n is 2, clear entire line * * CUF (CUrsor Forward) * Sequence: ESC [ n C * Effect: moves cursor forward n chars * * CUB (CUrsor Backward) * Sequence: ESC [ n D * Effect: moves cursor backward n chars * * The following is used to get the terminal width if getting * the width with the TIOCGWINSZ ioctl fails * * DSR (Device Status Report) * Sequence: ESC [ 6 n * Effect: reports the current cusor position as ESC [ n ; m R * where n is the row and m is the column * * When multi line mode is enabled, we also use an additional escape * sequence. However multi line editing is disabled by default. * * CUU (Cursor Up) * Sequence: ESC [ n A * Effect: moves cursor up of n chars. * * CUD (Cursor Down) * Sequence: ESC [ n B * Effect: moves cursor down of n chars. * * When linenoiseClearScreen() is called, two additional escape sequences * are used in order to clear the screen and position the cursor at home * position. * * CUP (Cursor position) * Sequence: ESC [ H * Effect: moves the cursor to upper left corner * * ED (Erase display) * Sequence: ESC [ 2 J * Effect: clear the whole screen * */ #include <termios.h> #include <unistd.h> #include <stdlib.h> #include <stdio.h> #include <errno.h> #include <string.h> #include <stdlib.h> #include <ctype.h> #include <dirent.h> #include <sys/types.h> #include <sys/ioctl.h> #include <sys/stat.h> #include <unistd.h> #include "linenoise.h" #define LINENOISE_DEFAULT_HISTORY_MAX_LEN 100 #define LINENOISE_MAX_LINE 4096 static char *unsupported_term[] = {"dumb","cons25","emacs",NULL}; static linenoiseCompletionCallback *completionCallback = NULL; static struct termios orig_termios; /* In order to restore at exit.*/ static int mlmode = 0; /* Multi line mode. Default is single line. */ static int atexit_registered = 0; /* Register atexit just 1 time. */ static int history_max_len = LINENOISE_DEFAULT_HISTORY_MAX_LEN; /* The linenoiseState structure represents the state during line editing. * We pass this state to functions implementing specific editing * functionalities. */ struct linenoiseState ls = {0}; enum KEY_ACTION{ KEY_NULL = 0, /* NULL */ CTRL_A = 1, /* Ctrl+a */ CTRL_B = 2, /* Ctrl-b */ CTRL_C = 3, /* Ctrl-c */ CTRL_D = 4, /* Ctrl-d */ CTRL_E = 5, /* Ctrl-e */ CTRL_F = 6, /* Ctrl-f */ CTRL_H = 8, /* Ctrl-h */ TAB = 9, /* Tab */ CTRL_K = 11, /* Ctrl+k */ CTRL_L = 12, /* Ctrl+l */ ENTER = 13, /* Enter */ CTRL_N = 14, /* Ctrl-n */ CTRL_P = 16, /* Ctrl-p */ CTRL_T = 20, /* Ctrl-t */ CTRL_U = 21, /* Ctrl+u */ CTRL_W = 23, /* Ctrl+w */ ESC = 27, /* Escape */ BACKSPACE = 127 /* Backspace */ }; static void linenoiseAtExit(void); int linenoiseHistoryAdd(const char *line, void *data); /* Debugging macro. */ #if 0 FILE *lndebug_fp = NULL; #define lndebug(...) \ do { \ if (lndebug_fp == NULL) { \ lndebug_fp = fopen("/tmp/lndebug.txt","a"); \ fprintf(lndebug_fp, \ "[%d %d %d] p: %d, rows: %d, rpos: %d, max: %d, oldmax: %d\n", \ (int)l->len,(int)l->pos,(int)l->oldpos,plen,rows,rpos, \ (int)l->maxrows,old_rows); \ } \ fprintf(lndebug_fp, ", " __VA_ARGS__); \ fflush(lndebug_fp); \ } while (0) #else #define lndebug(fmt, ...) #endif /* ======================= Low level terminal handling ====================== */ /* Set if to use or not the multi line mode. */ void linenoiseSetMultiLine(int ml) { mlmode = ml; } /* Return true if the terminal name is in the list of terminals we know are * not able to understand basic escape sequences. */ static int isUnsupportedTerm(void) { char *term = getenv("TERM"); int j; if (term == NULL) return 0; for (j = 0; unsupported_term[j]; j++) if (!strcasecmp(term,unsupported_term[j])) return 1; return 0; } /* Raw mode: 1960 magic shit. */ int linenoiseEnableRawMode(int fd) { struct termios raw; if (!isatty(STDIN_FILENO)) goto fatal; if (!atexit_registered) { atexit(linenoiseAtExit); atexit_registered = 1; } if (tcgetattr(fd,&orig_termios) == -1) goto fatal; raw = orig_termios; /* modify the original mode */ /* input modes: no break, no CR to NL, no parity check, no strip char, * no start/stop output control. */ raw.c_iflag &= ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON); /* output modes - disable post processing */ raw.c_oflag &= ~(OPOST); /* control modes - set 8 bit chars */ raw.c_cflag |= (CS8); /* local modes - choing off, canonical off, no extended functions, * no signal chars (^Z,^C) */ raw.c_lflag &= ~(ECHO | ICANON | IEXTEN | ISIG); /* control chars - set return condition: min number of bytes and timer. * We want read to return every single byte, without timeout. */ raw.c_cc[VMIN] = 1; raw.c_cc[VTIME] = 0; /* 1 byte, no timer */ /* put terminal in raw mode after flushing */ if (tcsetattr(fd,TCSAFLUSH,&raw) < 0) goto fatal; ls.rawmode = 1; return 0; fatal: errno = ENOTTY; return -1; } void linenoiseDisableRawMode(int fd) { /* Don't even check the return value as it's too late. */ if (ls.rawmode && tcsetattr(fd,TCSAFLUSH,&orig_termios) != -1) ls.rawmode = 0; } /* Use the ESC [6n escape sequence to query the horizontal cursor position * and return it. On error -1 is returned, on success the position of the * cursor. */ static int getCursorPosition(int ifd, int ofd) { char buf[32]; int cols, rows; unsigned int i = 0; /* Report cursor location */ if (write(ofd, "\x1b[6n", 4) != 4) return -1; /* Read the response: ESC [ rows ; cols R */ while (i < sizeof(buf)-1) { if (read(ifd,buf+i,1) != 1) break; if (buf[i] == 'R') break; i++; } buf[i] = '\0'; /* Parse it. */ if (buf[0] != ESC || buf[1] != '[') return -1; if (sscanf(buf+2,"%d;%d",&rows,&cols) != 2) return -1; return cols; } /* Try to get the number of columns in the current terminal, or assume 80 * if it fails. */ static int getColumns(int ifd, int ofd) { struct winsize ws; if (ioctl(1, TIOCGWINSZ, &ws) == -1 || ws.ws_col == 0) { /* ioctl() failed. Try to query the terminal itself. */ int start, cols; /* Get the initial position so we can restore it later. */ start = getCursorPosition(ifd,ofd); if (start == -1) goto failed; /* Go to right margin and get position. */ if (write(ofd,"\x1b[999C",6) != 6) goto failed; cols = getCursorPosition(ifd,ofd); if (cols == -1) goto failed; /* Restore position. */ if (cols > start) { char seq[32]; snprintf(seq,32,"\x1b[%dD",cols-start); if (write(ofd,seq,strlen(seq)) == -1) { /* Can't recover... */ } } return cols; } else { return ws.ws_col; } failed: return 80; } /* Clear the screen. Used to handle ctrl+l */ void linenoiseClearScreen(void) { if (write(STDOUT_FILENO,"\x1b[H\x1b[2J",7) <= 0) { /* nothing to do, just to avoid warning. */ } } /* Beep, used for completion when there is nothing to complete or when all * the choices were already shown. */ static void linenoiseBeep(void) { fprintf(stderr, "\x7"); fflush(stderr); } /* ============================== Completion ================================ */ /* Free a list of completion option populated by linenoiseAddCompletion(). */ static void freeCompletions(linenoiseCompletions *lc) { size_t i; for (i = 0; i < lc->len; i++) free(lc->cvec[i]); if (lc->cvec != NULL) free(lc->cvec); } /* This is an helper function for linenoiseEdit() and is called when the * user types the <tab> key in order to complete the string currently in the * input. * * The state of the editing is encapsulated into the pointed linenoiseState * structure as described in the structure definition. */ static int completeLine(struct linenoiseState *ls) { linenoiseCompletions lc = {0, 0, NULL}; int nread, nwritten, hint_len, hint_line_count, char_count; char c = 0, *common, *hint; struct winsize w; /* Hint is only the string after the last space */ hint = strrchr(ls->buf, ' '); if (!hint) { hint = ls->buf; } else { ++hint; } completionCallback(ls->buf, hint, &lc); if (lc.len == 0) { linenoiseBeep(); } else { unsigned int i, j; /* Learn the longest common part */ common = strdup(lc.cvec[0]); for (i = 1; i < lc.len; ++i) { for (j = 0; j < strlen(lc.cvec[i]); ++j) { if (lc.cvec[i][j] != common[j]) { break; } } common[j] = '\0'; } /* Path completions have a different hint */ if (lc.path && strrchr(hint, '/')) { hint = strrchr(hint, '/'); ++hint; } /* Show completion */ if ((lc.len == 1) && (common[strlen(common) - 1] != '/')) { nwritten = snprintf(hint, ls->buflen - (hint - ls->buf), "%s ", common); } else { nwritten = snprintf(hint, ls->buflen - (hint - ls->buf), "%s", common); } free(common); ls->len = ls->pos = (hint - ls->buf) + nwritten; linenoiseRefreshLine(); /* A single hint */ if (lc.len == 1) { freeCompletions(&lc); return 0; } /* Read a char */ nread = read(ls->ifd,&c,1); if (nread <= 0) { freeCompletions(&lc); return -1; } /* Not a tab */ if (c != 9) { freeCompletions(&lc); return c; } /* Learn terminal window size */ ioctl(ls->ifd, TIOCGWINSZ, &w); /* Learn the longest hint */ hint_len = strlen(lc.cvec[0]); for (i = 1; i < lc.len; ++i) { if (strlen(lc.cvec[i]) > (unsigned)hint_len) { hint_len = strlen(lc.cvec[i]); } } /* Learn the number of hints that fit a line */ hint_line_count = 0; while (1) { char_count = 0; if (hint_line_count) { char_count += hint_line_count * (hint_len + 2); } char_count += hint_len; /* Too much */ if (char_count > w.ws_col) { break; } /* Still fits */ ++hint_line_count; } /* No hint fits, too bad */ if (!hint_line_count) { freeCompletions(&lc); return c; } while (c == 9) { /* Second tab */ linenoiseDisableRawMode(ls->ifd); printf("\n"); for (i = 0; i < lc.len; ++i) { printf("%-*s", hint_len, lc.cvec[i]); /* Line full or last hint */ if (((i + 1) % hint_line_count == 0) || (i == lc.len - 1)) { printf("\n"); } else { printf(" "); } } linenoiseEnableRawMode(ls->ifd); linenoiseRefreshLine(); /* Read a char */ nread = read(ls->ifd,&c,1); if (nread <= 0) { freeCompletions(&lc); return -1; } } } freeCompletions(&lc); return c; /* Return last read character */ } /* Register a callback function to be called for tab-completion. */ void linenoiseSetCompletionCallback(linenoiseCompletionCallback *fn) { completionCallback = fn; } /* This function can be called in user completion callback to fill * path completion for them. hint parameter is actually the whole path * and buf is unused, but included to match the completion callback prototype. */ void linenoisePathCompletion(const char *buf, const char *hint, linenoiseCompletions *lc) { const char *ptr; char *full_path, *hint_ptr, match[FILENAME_MAX + 2]; DIR *dir; struct dirent *ent; struct stat st; (void)buf; lc->path = 1; ptr = strrchr(hint, '/'); /* new relative path */ if (ptr == NULL) { full_path = malloc(2 + FILENAME_MAX + 1); strcpy(full_path, "./"); ptr = hint; } else { full_path = malloc((int)(ptr - hint) + FILENAME_MAX + 1); ++ptr; sprintf(full_path, "%.*s", (int)(ptr - hint), hint); } hint_ptr = full_path + strlen(full_path); dir = opendir(full_path); if (dir == NULL) { free(full_path); return; } while ((ent = readdir(dir))) { if (ent->d_name[0] == '.') { continue; } if (!strncmp(ptr, ent->d_name, strlen(ptr))) { /* is it a directory? */ strcpy(hint_ptr, ent->d_name); if (stat(full_path, &st)) { /* skip this item */ continue; } strcpy(match, ent->d_name); if (S_ISDIR(st.st_mode)) { strcat(match, "/"); } linenoiseAddCompletion(lc, match); } } free(full_path); closedir(dir); } /* This function is used by the callback function registered by the user * in order to add completion options given the input string when the * user typed <tab>. See the example.c source code for a very easy to * understand example. */ void linenoiseAddCompletion(linenoiseCompletions *lc, const char *str) { size_t len = strlen(str); char *copy, **cvec; copy = malloc(len+1); if (copy == NULL) return; memcpy(copy,str,len+1); cvec = realloc(lc->cvec,sizeof(char*)*(lc->len+1)); if (cvec == NULL) { free(copy); return; } lc->cvec = cvec; lc->cvec[lc->len++] = copy; } /* =========================== Line editing ================================= */ /* We define a very simple "append buffer" structure, that is an heap * allocated string where we can append to. This is useful in order to * write all the escape sequences in a buffer and flush them to the standard * output in a single call, to avoid flickering effects. */ struct abuf { char *b; int len; }; static void abInit(struct abuf *ab) { ab->b = NULL; ab->len = 0; } static void abAppend(struct abuf *ab, const char *s, int len) { char *new = realloc(ab->b,ab->len+len); if (new == NULL) return; memcpy(new+ab->len,s,len); ab->b = new; ab->len += len; } static void abFree(struct abuf *ab) { free(ab->b); } /* Single line low level line refresh. * * Rewrite the currently edited line accordingly to the buffer content, * cursor position, and number of columns of the terminal. */ static void refreshSingleLine(struct linenoiseState *l) { char seq[64]; size_t plen = strlen(l->prompt); int fd = l->ofd; char *buf = l->buf; size_t len = l->len; size_t pos = l->pos; struct abuf ab; while((plen+pos) >= l->cols) { buf++; len--; pos--; } while (plen+len > l->cols) { len--; } abInit(&ab); /* Cursor to left edge */ snprintf(seq,64,"\r"); abAppend(&ab,seq,strlen(seq)); /* Write the prompt and the current buffer content */ abAppend(&ab,l->prompt,strlen(l->prompt)); abAppend(&ab,buf,len); /* Erase to right */ snprintf(seq,64,"\x1b[0K"); abAppend(&ab,seq,strlen(seq)); /* Move cursor to original position. */ snprintf(seq,64,"\r\x1b[%dC", (int)(pos+plen)); abAppend(&ab,seq,strlen(seq)); if (write(fd,ab.b,ab.len) == -1) {} /* Can't recover from write error. */ abFree(&ab); } /* Multi line low level line refresh. * * Rewrite the currently edited line accordingly to the buffer content, * cursor position, and number of columns of the terminal. */ static void refreshMultiLine(struct linenoiseState *l) { char seq[64]; int plen = strlen(l->prompt); int rows = (plen+l->len+l->cols-1)/l->cols; /* rows used by current buf. */ int rpos = (plen+l->oldpos+l->cols)/l->cols; /* cursor relative row. */ int rpos2; /* rpos after refresh. */ int col; /* colum position, zero-based. */ int old_rows = l->maxrows; int fd = l->ofd, j; struct abuf ab; /* Update maxrows if needed. */ if (rows > (int)l->maxrows) l->maxrows = rows; /* First step: clear all the lines used before. To do so start by * going to the last row. */ abInit(&ab); if (old_rows-rpos > 0) { lndebug("go down %d", old_rows-rpos); snprintf(seq,64,"\x1b[%dB", old_rows-rpos); abAppend(&ab,seq,strlen(seq)); } /* Now for every row clear it, go up. */ for (j = 0; j < old_rows-1; j++) { lndebug("clear+up"); snprintf(seq,64,"\r\x1b[0K\x1b[1A"); abAppend(&ab,seq,strlen(seq)); } /* Clean the top line. */ lndebug("clear"); snprintf(seq,64,"\r\x1b[0K"); abAppend(&ab,seq,strlen(seq)); /* Write the prompt and the current buffer content */ abAppend(&ab,l->prompt,strlen(l->prompt)); abAppend(&ab,l->buf,l->len); /* If we are at the very end of the screen with our prompt, we need to * emit a newline and move the prompt to the first column. */ if (l->pos && l->pos == l->len && (l->pos+plen) % l->cols == 0) { lndebug("<newline>"); abAppend(&ab,"\n",1); snprintf(seq,64,"\r"); abAppend(&ab,seq,strlen(seq)); rows++; if (rows > (int)l->maxrows) l->maxrows = rows; } /* Move cursor to right position. */ rpos2 = (plen+l->pos+l->cols)/l->cols; /* current cursor relative row. */ lndebug("rpos2 %d", rpos2); /* Go up till we reach the expected positon. */ if (rows-rpos2 > 0) { lndebug("go-up %d", rows-rpos2); snprintf(seq,64,"\x1b[%dA", rows-rpos2); abAppend(&ab,seq,strlen(seq)); } /* Set column. */ col = (plen+(int)l->pos) % (int)l->cols; lndebug("set col %d", 1+col); if (col) snprintf(seq,64,"\r\x1b[%dC", col); else snprintf(seq,64,"\r"); abAppend(&ab,seq,strlen(seq)); lndebug("\n"); l->oldpos = l->pos; if (write(fd,ab.b,ab.len) == -1) {} /* Can't recover from write error. */ abFree(&ab); } /* Calls the two low level functions refreshSingleLine() or * refreshMultiLine() according to the selected mode. */ void linenoiseRefreshLine(void) { if (mlmode) refreshMultiLine(&ls); else refreshSingleLine(&ls); } /* Insert the character 'c' at cursor current position. * * On error writing to the terminal -1 is returned, otherwise 0. */ int linenoiseEditInsert(struct linenoiseState *l, char c) { if (l->len < l->buflen) { if (l->len == l->pos) { l->buf[l->pos] = c; l->pos++; l->len++; l->buf[l->len] = '\0'; if ((!mlmode && l->plen+l->len < l->cols) /* || mlmode */) { /* Avoid a full update of the line in the * trivial case. */ if (write(l->ofd,&c,1) == -1) return -1; } else { linenoiseRefreshLine(); } } else { memmove(l->buf+l->pos+1,l->buf+l->pos,l->len-l->pos); l->buf[l->pos] = c; l->len++; l->pos++; l->buf[l->len] = '\0'; linenoiseRefreshLine(); } } return 0; } /* Move cursor on the left. */ void linenoiseEditMoveLeft(struct linenoiseState *l) { if (l->pos > 0) { l->pos--; linenoiseRefreshLine(); } } /* Move cursor on the right. */ void linenoiseEditMoveRight(struct linenoiseState *l) { if (l->pos != l->len) { l->pos++; linenoiseRefreshLine(); } } /* Move cursor to the start of the line. */ void linenoiseEditMoveHome(struct linenoiseState *l) { if (l->pos != 0) { l->pos = 0; linenoiseRefreshLine(); } } /* Move cursor to the end of the line. */ void linenoiseEditMoveEnd(struct linenoiseState *l) { if (l->pos != l->len) { l->pos = l->len; linenoiseRefreshLine(); } } /* Substitute the currently edited line with the next or previous history * entry as specified by 'dir'. */ #define LINENOISE_HISTORY_NEXT 0 #define LINENOISE_HISTORY_PREV 1 void linenoiseEditHistoryNext(struct linenoiseState *l, int dir) { if (l->history_len > 1) { /* Update the current history entry before to * overwrite it with the next one. */ free(l->history[l->history_len - 1 - l->history_index].line); l->history[l->history_len - 1 - l->history_index].line = strdup(l->buf); /* Show the new entry */ l->history_index += (dir == LINENOISE_HISTORY_PREV) ? 1 : -1; if (l->history_index < 0) { l->history_index = 0; return; } else if (l->history_index >= l->history_len) { l->history_index = l->history_len-1; return; } strncpy(l->buf,l->history[l->history_len - 1 - l->history_index].line,l->buflen); l->buf[l->buflen-1] = '\0'; l->len = l->pos = strlen(l->buf); linenoiseRefreshLine(); } } /* Callback used for freeing user history data. */ void linenoiseHistoryDataFree(void (*hist_data_free)(void *data)) { ls.hist_data_free = hist_data_free; } static void linenoiseHistItemFree(int hist_idx) { free(ls.history[hist_idx].line); if (ls.hist_data_free) { ls.hist_data_free(ls.history[hist_idx].data); } } /* Delete the character at the right of the cursor without altering the cursor * position. Basically this is what happens with the "Delete" keyboard key. */ void linenoiseEditDelete(struct linenoiseState *l) { if (l->len > 0 && l->pos < l->len) { memmove(l->buf+l->pos,l->buf+l->pos+1,l->len-l->pos-1); l->len--; l->buf[l->len] = '\0'; linenoiseRefreshLine(); } } /* Backspace implementation. */ void linenoiseEditBackspace(struct linenoiseState *l) { if (l->pos > 0 && l->len > 0) { memmove(l->buf+l->pos-1,l->buf+l->pos,l->len-l->pos); l->pos--; l->len--; l->buf[l->len] = '\0'; linenoiseRefreshLine(); } } /* Delete the previosu word, maintaining the cursor at the start of the * current word. */ void linenoiseEditDeletePrevWord(struct linenoiseState *l) { size_t old_pos = l->pos; size_t diff; while (l->pos > 0 && l->buf[l->pos-1] == ' ') l->pos--; while (l->pos > 0 && l->buf[l->pos-1] != ' ') l->pos--; diff = old_pos - l->pos; memmove(l->buf+l->pos,l->buf+old_pos,l->len-old_pos+1); l->len -= diff; linenoiseRefreshLine(); } /* This function is the core of the line editing capability of linenoise. * It expects 'fd' to be already in "raw mode" so that every key pressed * will be returned ASAP to read(). * * The resulting string is put into 'buf' when the user type enter, or * when ctrl+d is typed. * * The function returns the length of the current buffer. */ static int linenoiseEdit(int stdin_fd, int stdout_fd, char *buf, size_t buflen, const char *prompt) { /* Populate the linenoise state that we pass to functions implementing * specific editing functionalities. */ ls.ifd = stdin_fd; ls.ofd = stdout_fd; ls.buf = buf; ls.buflen = buflen; ls.prompt = prompt; ls.plen = strlen(prompt); ls.oldpos = ls.pos = 0; ls.len = 0; ls.cols = getColumns(stdin_fd, stdout_fd); ls.maxrows = 0; ls.history_index = 0; /* Buffer starts empty. */ ls.buf[0] = '\0'; ls.buflen--; /* Make sure there is always space for the nulterm */ /* The latest history entry is always our current buffer, that * initially is just an empty string. */ linenoiseHistoryAdd("", NULL); if (write(ls.ofd,prompt,ls.plen) == -1) return -1; while(1) { char c; int nread; char seq[3]; nread = read(ls.ifd,&c,1); if (nread <= 0) return ls.len; /* Only autocomplete when the callback is set. It returns < 0 when * there was an error reading from fd. Otherwise it will return the * character that should be handled next. */ if (c == 9 && completionCallback != NULL) { c = completeLine(&ls); /* Return on errors */ if (c < 0) return ls.len; /* Read next character when 0 */ if (c == 0) continue; } switch(c) { case ENTER: /* enter */ ls.history_len--; linenoiseHistItemFree(ls.history_len); if (mlmode) linenoiseEditMoveEnd(&ls); return (int)ls.len; case CTRL_C: /* ctrl-c */ errno = EAGAIN; return -1; case BACKSPACE: /* backspace */ case 8: /* ctrl-h */ linenoiseEditBackspace(&ls); break; case CTRL_D: /* ctrl-d, remove char at right of cursor, or if the line is empty, act as end-of-file. */ if (ls.len > 0) { linenoiseEditDelete(&ls); } else { ls.history_len--; linenoiseHistItemFree(ls.history_len); return -1; } break; case CTRL_T: /* ctrl-t, swaps current character with previous. */ if (ls.pos > 0 && ls.pos < ls.len) { int aux = buf[ls.pos-1]; buf[ls.pos-1] = buf[ls.pos]; buf[ls.pos] = aux; if (ls.pos != ls.len-1) ls.pos++; linenoiseRefreshLine(); } break; case CTRL_B: /* ctrl-b */ linenoiseEditMoveLeft(&ls); break; case CTRL_F: /* ctrl-f */ linenoiseEditMoveRight(&ls); break; case CTRL_P: /* ctrl-p */ linenoiseEditHistoryNext(&ls, LINENOISE_HISTORY_PREV); break; case CTRL_N: /* ctrl-n */ linenoiseEditHistoryNext(&ls, LINENOISE_HISTORY_NEXT); break; case ESC: /* escape sequence */ /* Read the next two bytes representing the escape sequence. * Use two calls to handle slow terminals returning the two * chars at different times. */ if (read(ls.ifd,seq,1) == -1) break; if (read(ls.ifd,seq+1,1) == -1) break; /* ESC [ sequences. */ if (seq[0] == '[') { if (seq[1] >= '0' && seq[1] <= '9') { /* Extended escape, read additional byte. */ if (read(ls.ifd,seq+2,1) == -1) break; if (seq[2] == '~') { switch(seq[1]) { case '3': /* Delete key. */ linenoiseEditDelete(&ls); break; } } } else { switch(seq[1]) { case 'A': /* Up */ linenoiseEditHistoryNext(&ls, LINENOISE_HISTORY_PREV); break; case 'B': /* Down */ linenoiseEditHistoryNext(&ls, LINENOISE_HISTORY_NEXT); break; case 'C': /* Right */ linenoiseEditMoveRight(&ls); break; case 'D': /* Left */ linenoiseEditMoveLeft(&ls); break; case 'H': /* Home */ linenoiseEditMoveHome(&ls); break; case 'F': /* End*/ linenoiseEditMoveEnd(&ls); break; } } } /* ESC O sequences. */ else if (seq[0] == 'O') { switch(seq[1]) { case 'H': /* Home */ linenoiseEditMoveHome(&ls); break; case 'F': /* End*/ linenoiseEditMoveEnd(&ls); break; } } break; default: if (linenoiseEditInsert(&ls,c)) return -1; break; case CTRL_U: /* Ctrl+u, delete the whole line. */ buf[0] = '\0'; ls.pos = ls.len = 0; linenoiseRefreshLine(); break; case CTRL_K: /* Ctrl+k, delete from current to end of line. */ buf[ls.pos] = '\0'; ls.len = ls.pos; linenoiseRefreshLine(); break; case CTRL_A: /* Ctrl+a, go to the start of the line */ linenoiseEditMoveHome(&ls); break; case CTRL_E: /* ctrl+e, go to the end of the line */ linenoiseEditMoveEnd(&ls); break; case CTRL_L: /* ctrl+l, clear screen */ linenoiseClearScreen(); linenoiseRefreshLine(); break; case CTRL_W: /* ctrl+w, delete previous word */ linenoiseEditDeletePrevWord(&ls); break; } } return ls.len; } /* This special mode is used by linenoise in order to print scan codes * on screen for debugging / development purposes. It is implemented * by the linenoise_example program using the --keycodes option. */ void linenoisePrintKeyCodes(void) { char quit[4]; printf("Linenoise key codes debugging mode.\n" "Press keys to see scan codes. Type 'quit' at any time to exit.\n"); if (linenoiseEnableRawMode(STDIN_FILENO) == -1) return; memset(quit,' ',4); while(1) { char c; int nread; nread = read(STDIN_FILENO,&c,1); if (nread <= 0) continue; memmove(quit,quit+1,sizeof(quit)-1); /* shift string to left. */ quit[sizeof(quit)-1] = c; /* Insert current char on the right. */ if (memcmp(quit,"quit",sizeof(quit)) == 0) break; printf("'%c' %02x (%d) (type quit to exit)\n", isprint(c) ? c : '?', (int)c, (int)c); printf("\r"); /* Go left edge manually, we are in raw mode. */ fflush(stdout); } linenoiseDisableRawMode(STDIN_FILENO); } /* This function calls the line editing function linenoiseEdit() using * the STDIN file descriptor set in raw mode. */ static int linenoiseRaw(char *buf, size_t buflen, const char *prompt) { int count; if (buflen == 0) { errno = EINVAL; return -1; } if (!isatty(STDIN_FILENO)) { /* Not a tty: read from file / pipe. */ if (fgets(buf, buflen, stdin) == NULL) return -1; count = strlen(buf); if (count && buf[count-1] == '\n') { count--; buf[count] = '\0'; } } else { /* Interactive editing. */ if (linenoiseEnableRawMode(STDIN_FILENO) == -1) return -1; count = linenoiseEdit(STDIN_FILENO, STDOUT_FILENO, buf, buflen, prompt); linenoiseDisableRawMode(STDIN_FILENO); printf("\n"); } return count; } /* The high level function that is the main API of the linenoise library. * This function checks if the terminal has basic capabilities, just checking * for a blacklist of stupid terminals, and later either calls the line * editing function or uses dummy fgets() so that you will be able to type * something even in the most desperate of the conditions. */ char *linenoise(const char *prompt) { char buf[LINENOISE_MAX_LINE]; int count; if (isUnsupportedTerm()) { size_t len; printf("%s",prompt); fflush(stdout); if (fgets(buf,LINENOISE_MAX_LINE,stdin) == NULL) return NULL; len = strlen(buf); while(len && (buf[len-1] == '\n' || buf[len-1] == '\r')) { len--; buf[len] = '\0'; } return strdup(buf); } else { count = linenoiseRaw(buf,LINENOISE_MAX_LINE,prompt); if (count == -1) return NULL; return strdup(buf); } } /* ================================ History ================================= */ /* Free the history, but does not reset it. Only used when we have to * exit() to avoid memory leaks are reported by valgrind & co. */ static void freeHistory(void) { if (ls.history) { int i; for (i = 0; i < ls.history_len; i++) { linenoiseHistItemFree(i); } free(ls.history); } } /* At exit we'll try to fix the terminal to the initial conditions. */ static void linenoiseAtExit(void) { linenoiseDisableRawMode(STDIN_FILENO); freeHistory(); } /* This is the API call to add a new entry in the linenoise history. * It uses a fixed array of char pointers that are shifted (memmoved) * when the history max length is reached in order to remove the older * entry and make room for the new one, so it is not exactly suitable for huge * histories, but will work well for a few hundred of entries. * * Using a circular buffer is smarter, but a bit more complex to handle. */ int linenoiseHistoryAdd(const char *line, void *data) { char *linecopy; if (history_max_len == 0) return 0; /* Initialization on first call. */ if (ls.history == NULL) { ls.history = calloc(history_max_len, sizeof *ls.history); if (ls.history == NULL) return 0; } /* Don't add duplicated lines. */ if (ls.history_len && !strcmp(ls.history[ls.history_len-1].line, line)) { if (ls.hist_data_free) { ls.hist_data_free(ls.history[ls.history_len-1].data); } ls.history[ls.history_len-1].data = data; return 0; } /* Add an heap allocated copy of the line in the history. * If we reached the max length, remove the older line. */ linecopy = strdup(line); if (!linecopy) return 0; if (ls.history_len == history_max_len) { linenoiseHistItemFree(0); memmove(ls.history,ls.history+1,(history_max_len-1)*sizeof *ls.history); ls.history_len--; } ls.history[ls.history_len].line = linecopy; ls.history[ls.history_len].data = data; ls.history_len++; return 1; } /* Set the maximum length for the history. This function can be called even * if there is already some history, the function will make sure to retain * just the latest 'len' elements if the new history length value is smaller * than the amount of items already inside the history. */ int linenoiseHistorySetMaxLen(int len) { struct linenoiseHistItem *new; if (len < 1) return 0; if (ls.history) { int tocopy = ls.history_len; new = malloc(len*sizeof *new); if (new == NULL) return 0; /* If we can't copy everything, free the elements we'll not use. */ if (len < tocopy) { int j; for (j = 0; j < tocopy-len; j++) linenoiseHistItemFree(j); tocopy = len; } memset(new,0,len*sizeof *new); memcpy(new,ls.history+(ls.history_len-tocopy), tocopy*sizeof *ls.history); free(ls.history); ls.history = new; } history_max_len = len; if (ls.history_len > history_max_len) ls.history_len = history_max_len; return 1; } /* Save the history in the specified file. On success 0 is returned * otherwise -1 is returned. */ int linenoiseHistorySave(const char *filename) { FILE *fp = fopen(filename,"w"); int j; if (fp == NULL) return -1; for (j = 0; j < ls.history_len; j++) fprintf(fp,"%s\n",ls.history[j].line); fclose(fp); return 0; } /* Load the history from the specified file. If the file does not exist * zero is returned and no operation is performed. * * If the file exists and the operation succeeded 0 is returned, otherwise * on error -1 is returned. */ int linenoiseHistoryLoad(const char *filename) { FILE *fp = fopen(filename,"r"); char buf[LINENOISE_MAX_LINE]; if (fp == NULL) return -1; while (fgets(buf,LINENOISE_MAX_LINE,fp) != NULL) { char *p; p = strchr(buf,'\r'); if (!p) p = strchr(buf,'\n'); if (p) *p = '\0'; linenoiseHistoryAdd(buf,NULL); } fclose(fp); return 0; } netopeer2-1.1.70/cli/linenoise/linenoise.h0000664000000000000000000000731314021433500017071 0ustar rootroot/* linenoise.h -- VERSION 1.0 * * Guerrilla line editing library against the idea that a line editing lib * needs to be 20,000 lines of C code. * * See linenoise.c for more information. * * ------------------------------------------------------------------------ * * Copyright (c) 2010-2014, Salvatore Sanfilippo <antirez at gmail dot com> * Copyright (c) 2010-2013, Pieter Noordhuis <pcnoordhuis at gmail dot com> * * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "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 COPYRIGHT * HOLDER OR CONTRIBUTORS 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. */ #ifndef __LINENOISE_H #define __LINENOISE_H #ifdef __cplusplus extern "C" { #endif struct linenoiseState { int ifd; /* Terminal stdin file descriptor. */ int ofd; /* Terminal stdout file descriptor. */ char *buf; /* Edited line buffer. */ size_t buflen; /* Edited line buffer size. */ const char *prompt; /* Prompt to display. */ size_t plen; /* Prompt length. */ size_t pos; /* Current cursor position. */ size_t oldpos; /* Previous refresh cursor position. */ size_t len; /* Current edited line length. */ size_t cols; /* Number of columns in terminal. */ size_t maxrows; /* Maximum num of rows used so far (multiline mode) */ int rawmode; int history_index; /* The history index we are currently editing. */ struct linenoiseHistItem { char *line; void *data; } *history; int history_len; void (*hist_data_free)(void *data); }; extern struct linenoiseState ls; typedef struct linenoiseCompletions { int path; size_t len; char **cvec; } linenoiseCompletions; typedef void(linenoiseCompletionCallback)(const char *, const char *, linenoiseCompletions *); void linenoiseSetCompletionCallback(linenoiseCompletionCallback *); void linenoiseAddCompletion(linenoiseCompletions *, const char *); char *linenoise(const char *prompt); int linenoiseHistoryAdd(const char *line, void *data); int linenoiseHistorySetMaxLen(int len); int linenoiseHistorySave(const char *filename); int linenoiseHistoryLoad(const char *filename); void linenoiseHistoryDataFree(void (*hist_data_free)(void *data)); void linenoiseClearScreen(void); void linenoiseSetMultiLine(int ml); void linenoisePrintKeyCodes(void); void linenoisePathCompletion(const char *, const char *, linenoiseCompletions *); void linenoiseRefreshLine(void); int linenoiseEnableRawMode(int fd); void linenoiseDisableRawMode(int fd); #ifdef __cplusplus } #endif #endif /* __LINENOISE_H */ netopeer2-1.1.70/cli/main.c0000664000000000000000000001302114021433500014027 0ustar rootroot/** * @file main.c * @author Michal Vasko <mvasko@cesnet.cz> * @brief netopeer2-cli tool * * Copyright (c) 2015 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 <stdio.h> #include <stdlib.h> #include <sys/types.h> #include <sys/stat.h> #include <getopt.h> #include <errno.h> #include <fcntl.h> #include <sys/mman.h> #include <unistd.h> #include <sys/times.h> #include <string.h> #include <signal.h> #include <libyang/libyang.h> #include <nc_client.h> #include "compat.h" #include "commands.h" #include "completion.h" #include "configuration.h" #include "linenoise/linenoise.h" int done; extern char *config_editor; extern struct nc_session *session; void lnc2_print_clb(NC_VERB_LEVEL level, const char *msg) { int was_rawmode = 0; if (ls.rawmode) { was_rawmode = 1; linenoiseDisableRawMode(ls.ifd); printf("\n"); } switch (level) { case NC_VERB_ERROR: fprintf(stderr, "nc ERROR: %s\n", msg); break; case NC_VERB_WARNING: fprintf(stderr, "nc WARNING: %s\n", msg); break; case NC_VERB_VERBOSE: fprintf(stderr, "nc VERBOSE: %s\n", msg); break; case NC_VERB_DEBUG: case NC_VERB_DEBUG_LOWLVL: fprintf(stderr, "nc DEBUG: %s\n", msg); break; } if (was_rawmode) { linenoiseEnableRawMode(ls.ifd); linenoiseRefreshLine(); } } void ly_print_clb(LY_LOG_LEVEL level, const char *msg, const char *path) { int was_rawmode = 0; if (ls.rawmode) { was_rawmode = 1; linenoiseDisableRawMode(ls.ifd); printf("\n"); } switch (level) { case LY_LLERR: if (path) { fprintf(stderr, "ly ERROR: %s (%s)\n", msg, path); } else { fprintf(stderr, "ly ERROR: %s\n", msg); } break; case LY_LLWRN: if (path) { fprintf(stderr, "ly WARNING: %s (%s)\n", msg, path); } else { fprintf(stderr, "ly WARNING: %s\n", msg); } break; case LY_LLVRB: if (path) { fprintf(stderr, "ly VERBOSE: %s (%s)\n", msg, path); } else { fprintf(stderr, "ly VERBOSE: %s\n", msg); } break; case LY_LLDBG: if (path) { fprintf(stderr, "ly DEBUG: %s (%s)\n", msg, path); } else { fprintf(stderr, "ly DEBUG: %s\n", msg); } break; default: /* silent, just to cover enum, shouldn't be here in real world */ return; } if (was_rawmode) { linenoiseEnableRawMode(ls.ifd); linenoiseRefreshLine(); } } int main(void) { char *cmd, *cmdline, *cmdstart, *tmp_config_file = NULL; int i, j; struct sigaction action; nc_client_init(); /* ignore SIGPIPE */ memset(&action, 0, sizeof action); action.sa_handler = SIG_IGN; sigaction(SIGPIPE, &action, NULL); nc_set_print_clb(lnc2_print_clb); ly_set_log_clb(ly_print_clb, 1); linenoiseSetCompletionCallback(complete_cmd); linenoiseHistoryDataFree(free); load_config(); if (!config_editor) { config_editor = getenv("EDITOR"); if (config_editor) { config_editor = strdup(config_editor); } } if (!config_editor) { config_editor = strdup("vi"); } while (!done) { /* get the command from user */ cmdline = linenoise(PROMPT); /* EOF -> exit */ if (cmdline == NULL) { done = 1; cmdline = strdup("quit"); } /* empty line -> wait for another command */ if (*cmdline == '\0') { free(cmdline); continue; } /* isolate the command word. */ for (i = 0; cmdline[i] && (cmdline[i] == ' '); i++); cmdstart = cmdline + i; for (j = 0; cmdline[i] && (cmdline[i] != ' '); i++, j++); cmd = strndup(cmdstart, j); /* parse the command line */ for (i = 0; commands[i].name; i++) { if (strcmp(cmd, commands[i].name) == 0) { break; } } /* execute the command if any valid specified */ if (commands[i].name) { /* display help */ if ((strchr(cmdstart, ' ') != NULL) && ((strncmp(strchr(cmdstart, ' ') + 1, "-h", 2) == 0) || (strncmp(strchr(cmdstart, ' ') + 1, "--help", 6) == 0))) { if (commands[i].help_func != NULL) { commands[i].help_func(); } else { printf("%s\n", commands[i].helpstring); } } else { if (ls.history_index) { tmp_config_file = (char *)ls.history[ls.history_len - ls.history_index].data; } commands[i].func((const char *)cmdstart, &tmp_config_file); } } else { /* if unknown command specified, tell it to user */ fprintf(stderr, "%s: No such command, type 'help' for more information.\n", cmd); } if (!done) { linenoiseHistoryAdd(cmdline, tmp_config_file); } tmp_config_file = NULL; free(cmd); free(cmdline); } store_config(); free(config_editor); if (session) { nc_session_free(session, NULL); } nc_client_destroy(); return 0; } netopeer2-1.1.70/cli/sample_script.sh0000664000000000000000000000016014021433500016140 0ustar rootroot#!/bin/bash netopeer2-cli <<END connect get-config --source running --out out.xml disconnect END echo "" exit 0 netopeer2-1.1.70/compat/0000775000000000000000000000000014021433500013456 5ustar rootrootnetopeer2-1.1.70/compat/compat.c0000664000000000000000000000547714021433500015122 0ustar rootroot/** * @file compat.c * @author Michal Vasko <mvasko@cesnet.cz> * @brief compatibility functions * * Copyright (c) 2020 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 _POSIX_C_SOURCE 1 /* fdopen, _POSIX_PATH_MAX */ #define _ISOC99_SOURCE /* vsnprintf */ #define _XOPEN_SOURCE 500 /* strdup */ #include <errno.h> #include <limits.h> #include <string.h> #include <stdarg.h> #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include "compat.h" #ifndef HAVE_VDPRINTF int vdprintf(int fd, const char *format, va_list ap) { FILE *stream; int count; stream = fdopen(dup(fd), "a+"); if (stream) { count = vfprintf(stream, format, ap); fclose(stream); } return count; } #endif #ifndef HAVE_ASPRINTF int asprintf(char **strp, const char *fmt, ...) { int ret; va_list ap; va_start(ap, fmt); ret = vasprintf(strp, fmt, ap); va_end(ap); return ret; } #endif #ifndef HAVE_VASPRINTF int vasprintf(char **strp, const char *fmt, va_list ap) { va_list ap2; va_copy(ap2, ap); int l = vsnprintf(0, 0, fmt, ap2); va_end(ap2); if (l < 0 || !(*strp = malloc(l + 1U))) { return -1; } return vsnprintf(*strp, l + 1U, fmt, ap); } #endif #ifndef HAVE_STRNDUP char * strndup(const char *s, size_t n) { char *buf; size_t len = 0; /* strnlen */ for (; (len < n) && (s[len] != '\0'); ++len); if (!(buf = malloc(len + 1U))) { return NULL; } memcpy(buf, s, len); buf[len] = '\0'; return buf; } #endif #ifndef HAVE_GETLINE ssize_t getline(char **lineptr, size_t *n, FILE *stream) { static char line[256]; char *ptr; unsigned int len; if (!lineptr || !n) { errno = EINVAL; return -1; } if (ferror(stream) || feof(stream)) { return -1; } if (!fgets(line, 256, stream)) { return -1; } ptr = strchr(line, '\n'); if (ptr) { *ptr = '\0'; } len = strlen(line); if (len + 1 < 256) { ptr = realloc(*lineptr, 256); if (!ptr) { return -1; } *lineptr = ptr; *n = 256; } strcpy(*lineptr, line); return len; } #endif #ifndef HAVE_GET_CURRENT_DIR_NAME char * get_current_dir_name(void) { char tmp[_POSIX_PATH_MAX]; char *retval = NULL; if (getcwd(tmp, sizeof(tmp))) { retval = strdup(tmp); if (!retval) { errno = ENOMEM; } } return retval; } #endif #ifndef HAVE_STRCHRNUL char * strchrnul(const char *s, int c) { char * p = strchr(s, c); return p == NULL ? (char *) s + strlen(s) : p; } #endif netopeer2-1.1.70/compat/compat.h.in0000664000000000000000000000501314021433500015516 0ustar rootroot/** * @file compat.h * @author Michal Vasko <mvasko@cesnet.cz> * @brief compatibility functions header * * Copyright (c) 2020 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 _COMPAT_H_ #define _COMPAT_H_ #include <sys/types.h> #include <stdarg.h> #include <stdio.h> #ifndef __WORDSIZE # if defined __x86_64__ && !defined __ILP32__ # define __WORDSIZE 64 # else # define __WORDSIZE 32 # endif #endif #ifndef __INT64_C # if __WORDSIZE == 64 # define __INT64_C(c) c ## L # define __UINT64_C(c) c ## UL # else # define __INT64_C(c) c ## LL # define __UINT64_C(c) c ## ULL # endif #endif #cmakedefine HAVE_VDPRINTF #cmakedefine HAVE_ASPRINTF #cmakedefine HAVE_VASPRINTF #cmakedefine HAVE_STRNDUP #cmakedefine HAVE_GETLINE #cmakedefine HAVE_GET_CURRENT_DIR_NAME #cmakedefine HAVE_STRDUPA #cmakedefine HAVE_STRCHRNUL #define bswap64(val) \ ( (((val) >> 56) & 0x00000000000000FF) | (((val) >> 40) & 0x000000000000FF00) | \ (((val) >> 24) & 0x0000000000FF0000) | (((val) >> 8) & 0x00000000FF000000) | \ (((val) << 8) & 0x000000FF00000000) | (((val) << 24) & 0x0000FF0000000000) | \ (((val) << 40) & 0x00FF000000000000) | (((val) << 56) & 0xFF00000000000000) ) #undef le64toh #undef htole64 #cmakedefine IS_BIG_ENDIAN #ifdef IS_BIG_ENDIAN # define le64toh(x) bswap64(x) # define htole64(x) bswap64(x) #else # define le64toh(x) (x) # define htole64(x) (x) #endif #ifndef MAP_ANONYMOUS #define MAP_ANONYMOUS MAP_ANON #endif /* !MAP_ANONYMOUS */ #ifndef HAVE_STRDUPA #define strdupa(s) ( \ { \ char * buf; \ size_t len = strlen(s); \ buf = alloca(len + 1); \ buf[len] = '\0'; \ (char*) memcpy(buf, s, len); \ }) #endif #ifndef HAVE_VDPRINTF int vdprintf(int fd, const char *format, va_list ap); #endif #ifndef HAVE_ASPRINTF int asprintf(char **strp, const char *fmt, ...); #endif #ifndef HAVE_VASPRINTF int vasprintf(char **strp, const char *fmt, va_list ap); #endif #ifndef HAVE_STRNDUP char *strndup(const char *s, size_t n); #endif #ifndef HAVE_GETLINE ssize_t getline(char **lineptr, size_t *n, FILE *stream); #endif #ifndef HAVE_GET_CURRENT_DIR_NAME char *get_current_dir_name(void); #endif #ifndef HAVE_STRCHRNUL char *strchrnul(const char *s, int c); #endif #endif /* _COMPAT_H_ */ netopeer2-1.1.70/example_configuration/0000775000000000000000000000000014021433500016555 5ustar rootrootnetopeer2-1.1.70/example_configuration/ssh_callhome.xml0000664000000000000000000000356614021433500021752 0ustar rootroot<netconf-server xmlns="urn:ietf:params:xml:ns:yang:ietf-netconf-server"> <call-home> <netconf-client> <name>default-client</name> <endpoints> <endpoint> <name>default-ssh</name> <ssh> <tcp-client-parameters> <remote-address>localhost</remote-address> <keepalives> <idle-time>1</idle-time> <max-probes>10</max-probes> <probe-interval>5</probe-interval> </keepalives> </tcp-client-parameters> <ssh-server-parameters> <server-identity> <host-key> <name>default-key</name> <public-key> <keystore-reference>genkey</keystore-reference> </public-key> </host-key> </server-identity> <client-authentication> <supported-authentication-methods> <publickey/> <passsword/> <other>interactive</other> </supported-authentication-methods> <users/> </client-authentication> </ssh-server-parameters> </ssh> </endpoint> </endpoints> <connection-type> <persistent/> </connection-type> </netconf-client> </call-home> </netconf-server> netopeer2-1.1.70/example_configuration/tls_callhome.xml0000664000000000000000000000432114021433500021745 0ustar rootroot<netconf-server xmlns="urn:ietf:params:xml:ns:yang:ietf-netconf-server"> <call-home> <netconf-client> <name>default-client</name> <endpoints> <endpoint> <name>default-tls</name> <tls> <tcp-client-parameters> <remote-address>localhost</remote-address> <keepalives> <idle-time>1</idle-time> <max-probes>10</max-probes> <probe-interval>5</probe-interval> </keepalives> </tcp-client-parameters> <tls-server-parameters> <server-identity> <keystore-reference> <asymmetric-key>serverkey</asymmetric-key> <certificate>servercert</certificate> </keystore-reference> </server-identity> <client-authentication> <required/> <ca-certs>cacerts</ca-certs> <client-certs>clientcerts</client-certs> <cert-maps> <cert-to-name> <id>1</id> <fingerprint>02:E9:38:1F:F6:8B:62:DE:0A:0B:C5:03:81:A8:03:49:A0:00:7F:8B:F3</fingerprint> <map-type xmlns:x509c2n="urn:ietf:params:xml:ns:yang:ietf-x509-cert-to-name">x509c2n:specified</map-type> <name>tls-test</name> </cert-to-name> </cert-maps> </client-authentication> </tls-server-parameters> </tls> </endpoint> </endpoints> <connection-type> <persistent/> </connection-type> </netconf-client> </call-home> </netconf-server> netopeer2-1.1.70/example_configuration/tls_certs/0000775000000000000000000000000014021433500020557 5ustar rootrootnetopeer2-1.1.70/example_configuration/tls_certs/ca.key0000664000000000000000000000321314021433500021653 0ustar rootroot-----BEGIN RSA PRIVATE KEY----- MIIEogIBAAKCAQEArD3TDHPAMT2Z84orK4lMlarbgooIUCcRZyLe+QM+8KY8Hn+m GaxPEOTSL3ywszqefB/Utm2hPKLHX684iRC14ID9WDGHxPjvoPArhgFhfV+qnPfx KTgxZC12uOj4u1V9y+SkTCocFbRfXVBGpojrBuDHXkDMDEWNvr8/52YCv7bGaiBw UHolcLCUbmtKILCG0RNJyTaJpXQdAeq5Z1SJotpbfYFFtAXB32hVoLug1dzl2tjG 9sb1wq3QaDExcbC5w6P65qOkNoyym9ne6QlQagCqVDyFn3vcqkRaTjvZmxauCeUx XgJoXkyWcm0lM1KMHdoTArmchw2Dz0yHHSyDAQIDAQABAoIBAFH65y3hFhQZxuHU 3LFPG0WNWgdq3YQQ5EaboVcSRW3TIYA+r3c+vS9ESgpSJeRYvUBFAkCGM50huRWA 177dVkPyASNuB7on5h5K0dxpYdaDpzgpBv7ggRm2TfC66lB343UdcVnTHSTzggRv BgGT35GZgSsKWlRo8otcifUAZ8SJWRv6UxmX0zuvqSj3Q49ucevb57/CmYdgGxCP 5flP/fqAdKen8/A03KPaltMERdo5xXbs5f7iBjcCZM1427Ta5cjiBW1zDWU4zbzn 9+unwWPEuuPaGGtS6500qBJy6mIgM/9nYP9LKz9sJMEVJJZLZc1/2pjbJSNEiNJV SlJrvbECgYEA2u+xyfMsc2iE+dC8913NvIJnK7v7ixh5eu86SjJoYvflBvnEpPoX XxWlWUkO5jR1Hk2v1Z4U4hD/OR1kUwAqbN3MdCDd00hkhgVns8AgZtH0aXmdz+xq M0CKGXU7L/XS5mpiI8g24im1+1/rQjLxxUQjv0nfMxixa/ENmmtuisUCgYEAyWZ2 CzAROlhxjaCbe+WopjG0AevCcrAPAeRgqIOm9sJ51q0cg2B6E/Zn27CvzKxsFzgM +Vu3MoC0vVMK+Dc3o7idaQ0ew7kY5KO6LY8wDu5s3EGiS0KGJum2iIStE3lee/dd TDcX6yE/3WYvvNf7w7uN7nme3s3EuSDDKpPFCw0CgYAaSnpxI/CMk1qUnUpz8iHI p2g4SkS0uWWtK5k2W8NJTzeDlO7WWOoBkxneFPXjEx2VXALnhio/04aylyL7DKQL mr74mxHIU4MuzOtdHI9HiaLuH5qh42QFb5Sl5fwLkFuZK+FJJrvggN3HqAcaVf/O jpY0XGyfODHmInZdutT1eQKBgBAVoMPP+PBB8/+tnf1NICT1vzyQCZ2DNg+en6GV shXu/jAI70gGwnkpqq2+9KtR8egAz/hyPLVJ1iVwpmWgc08eBWRIafaTp8tK0Cmn T91BaWxFyaJdE72z2KIahoARp1wbK1ZU6BIdO66A5LsePLsrFXDAQdHleRqX5T5X QttZAoGASjKxjcbfZN9Q4jfvof2tacpdKbof9K03tbcRRkcBBF/mtiK4ER+c7dPU YcGJwOCT9YofASM0Qnq3F118Ic7DJAB332R1/UMY1krkCLAGfjAndL5XguFLpDQx c3dqfZ38rGw4GIr/rGJsPInYzkLxTfoYit/9dZjLLoxxvwlLTLs= -----END RSA PRIVATE KEY----- netopeer2-1.1.70/example_configuration/tls_certs/ca.pem0000664000000000000000000000262014021433500021645 0ustar rootroot-----BEGIN CERTIFICATE----- MIID7TCCAtWgAwIBAgIJAMtE1NGAR5KoMA0GCSqGSIb3DQEBBQUAMIGMMQswCQYD VQQGEwJDWjEWMBQGA1UECAwNU291dGggTW9yYXZpYTENMAsGA1UEBwwEQnJubzEP MA0GA1UECgwGQ0VTTkVUMQwwCgYDVQQLDANUTUMxEzARBgNVBAMMCmV4YW1wbGUg Q0ExIjAgBgkqhkiG9w0BCQEWE2V4YW1wbGVjYUBsb2NhbGhvc3QwHhcNMTQwNzI0 MTQxOTAyWhcNMjQwNzIxMTQxOTAyWjCBjDELMAkGA1UEBhMCQ1oxFjAUBgNVBAgM DVNvdXRoIE1vcmF2aWExDTALBgNVBAcMBEJybm8xDzANBgNVBAoMBkNFU05FVDEM MAoGA1UECwwDVE1DMRMwEQYDVQQDDApleGFtcGxlIENBMSIwIAYJKoZIhvcNAQkB FhNleGFtcGxlY2FAbG9jYWxob3N0MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB CgKCAQEArD3TDHPAMT2Z84orK4lMlarbgooIUCcRZyLe+QM+8KY8Hn+mGaxPEOTS L3ywszqefB/Utm2hPKLHX684iRC14ID9WDGHxPjvoPArhgFhfV+qnPfxKTgxZC12 uOj4u1V9y+SkTCocFbRfXVBGpojrBuDHXkDMDEWNvr8/52YCv7bGaiBwUHolcLCU bmtKILCG0RNJyTaJpXQdAeq5Z1SJotpbfYFFtAXB32hVoLug1dzl2tjG9sb1wq3Q aDExcbC5w6P65qOkNoyym9ne6QlQagCqVDyFn3vcqkRaTjvZmxauCeUxXgJoXkyW cm0lM1KMHdoTArmchw2Dz0yHHSyDAQIDAQABo1AwTjAdBgNVHQ4EFgQUc1YQIqjZ sHVwlea0AB4N+ilNI2gwHwYDVR0jBBgwFoAUc1YQIqjZsHVwlea0AB4N+ilNI2gw DAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQUFAAOCAQEAI/1KH60qnw9Xs2RGfi0/ IKf5EynXt4bQX8EIyVKwSkYKe04zZxYfLIl/Q2HOPYoFmm3daj5ddr0ZS1i4p4fT UhstjsYWvXs3W/HhVmFUslakkn3PrswhP77fCk6eEJLxdfyJ1C7Uudq2m1isZbKi h+XF0mG1LxJaDMocSz4eAya7M5brwjy8DoOmA1TnLQFCVcpn+sCr7VC4wE/JqxyV hBCk/MuGqqM3B1j90bGFZ112ZOecyE0EDSr6IbiRBtmeNbEwOFjKXhNLYdxpBZ9D 8A/368OckZkCrVLGuJNxK9UwCVTe8IhotHUqU9EqFDmxdV8oIdU/OzUwwNPA/Bd/ 9g== -----END CERTIFICATE----- netopeer2-1.1.70/example_configuration/tls_certs/client.crt0000664000000000000000000000266414021433500022557 0ustar rootroot-----BEGIN CERTIFICATE----- MIIECTCCAvGgAwIBAgIBBzANBgkqhkiG9w0BAQsFADCBjDELMAkGA1UEBhMCQ1ox FjAUBgNVBAgMDVNvdXRoIE1vcmF2aWExDTALBgNVBAcMBEJybm8xDzANBgNVBAoM BkNFU05FVDEMMAoGA1UECwwDVE1DMRMwEQYDVQQDDApleGFtcGxlIENBMSIwIAYJ KoZIhvcNAQkBFhNleGFtcGxlY2FAbG9jYWxob3N0MB4XDTE1MDczMDA3MjcxOFoX DTM1MDcyNTA3MjcxOFowgYUxCzAJBgNVBAYTAkNaMRYwFAYDVQQIDA1Tb3V0aCBN b3JhdmlhMQ8wDQYDVQQKDAZDRVNORVQxDDAKBgNVBAsMA1RNQzEXMBUGA1UEAwwO ZXhhbXBsZSBjbGllbnQxJjAkBgkqhkiG9w0BCQEWF2V4YW1wbGVjbGllbnRAbG9j YWxob3N0MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAueCQaNQWoNmF K6LKu1p8U8ZWdWg/PvDdLsJyzfzl/Qw4UA68SfFNaY06zZl8QB9W02nr5kWeeMY0 VA3adrPgOlvfx3oWlFbkETnMaN4OT3WTQ0Wt6jAWZDzVfopwpJPAzRPxACDftIqF GagYcF32hZlVNqqnVdbXh0S0EViweqp/dbG4VDUHSNVbglc+u4UbEzNIFXMdEFsJ ZpkynOmSiTsIATqIhb+2srkVgLwhfkC2qkuHQwAHdubuB07ObM2z01UhyEdDvEYG HwtYAGDBL2TAcsI0oGeVkRyuOkV0QY0UN7UEFI1yTYw+xZ42HgFx3uGwApCImxhb j69GBYWFqwIDAQABo3sweTAJBgNVHRMEAjAAMCwGCWCGSAGG+EIBDQQfFh1PcGVu U1NMIEdlbmVyYXRlZCBDZXJ0aWZpY2F0ZTAdBgNVHQ4EFgQUXGpLeLnh2cSDARAV A7KrBxGYpo8wHwYDVR0jBBgwFoAUc1YQIqjZsHVwlea0AB4N+ilNI2gwDQYJKoZI hvcNAQELBQADggEBAJPV3RTXFRtNyOU4rjPpYeBAIAFp2aqGc4t2J1c7oPp/1n+l ZvjnwtlJpZHxMM783e2ryDQ6dkvXDf8kpwKlg3U3mkJ3xKkDdWrM4QwghXdCN519 aa9qmu0zdFL+jUAaWlQ5tsceOrvbusCcbMqiFGk/QfpHqPv52SVWbYyUx7IX7DE+ UjgsLHycfV/tlcx4ZE6soTzl9VdgSL/zmzG3rjsr58J80rXckLgBhvijgBlIAJvW fC7D0vaouvBInSFXymdPVoUDZ30cdGLf+hI/i/TfsEMOinLrXVdkSGNo6FXAHKSv XeB9oFKSzhQ7OPyRyqvEPycUSw/qD6FVr80oDDc= -----END CERTIFICATE----- netopeer2-1.1.70/example_configuration/tls_certs/client.key0000664000000000000000000000321714021433500022552 0ustar rootroot-----BEGIN RSA PRIVATE KEY----- MIIEpAIBAAKCAQEAueCQaNQWoNmFK6LKu1p8U8ZWdWg/PvDdLsJyzfzl/Qw4UA68 SfFNaY06zZl8QB9W02nr5kWeeMY0VA3adrPgOlvfx3oWlFbkETnMaN4OT3WTQ0Wt 6jAWZDzVfopwpJPAzRPxACDftIqFGagYcF32hZlVNqqnVdbXh0S0EViweqp/dbG4 VDUHSNVbglc+u4UbEzNIFXMdEFsJZpkynOmSiTsIATqIhb+2srkVgLwhfkC2qkuH QwAHdubuB07ObM2z01UhyEdDvEYGHwtYAGDBL2TAcsI0oGeVkRyuOkV0QY0UN7UE FI1yTYw+xZ42HgFx3uGwApCImxhbj69GBYWFqwIDAQABAoIBAQCZN9kR8DGu6V7y t0Ax68asL8O5B/OKaHWKQ9LqpVrXmikZJOxkbzoGldow/CIFoU+q+Zbwu9aDa65a 0wiP7Hoa4Py3q5XNNUrOQDyU/OYC7cI0I83WS0lJ2zOJGYj8wKae5Z81IeQFKGHK 4lsy1OGPAvPRGh7RjUUgRavA2MCwe07rWRuDb/OJFe4Oh56UMEjwMiNBtMNtncog j1vr/qgRJdf9tf0zlJmLvUJ9+HSFFV9I/97LJyFhb95gAfHkjdVroLVgT3Cho+4P WtZaKCIGD0OwfOG2nLV4leXvRUk62/LMlB8NI9+JF7Xm+HCKbaWHNWC7mvWSLV58 Zl4AbUWRAoGBANyJ6SFHFRHSPDY026SsdMzXR0eUxBAK7G70oSBKKhY+O1j0ocLE jI2krHJBhHbLlnvJVyMUaCUOTS5m0uDw9hgSsAqeSL3hL38kxVZw+KNG9Ouno1Fl KnE/xXHlPQyeGs/P8nAMzHZxQtEsQdQayJEhK2XXHTsy7Q3MxDisfVJ1AoGBANfD 34gB+OMx6pwj7zk3qWbYXSX8xjCZMR0ciko+h4xeMP2N8B0oyoqC+v1ABMAtJ3wG sGZd0hV9gwM7OUM3SEwkn6oeg1GemWLcn4rlSmTnZc4aeVwrEWlnSNFX3s4g9l4u k8Ugu4MVJYqH8HuDQ5Ggl6/QAwPzMSEdCW0O+jOfAoGAIBRbegC5+t6m7Yegz4Ja dxV1g98K6f58x+MDsQu4tYWV4mmrQgaPH2dtwizvlMwmdpkh+LNWNtWuumowkJHc akIFo3XExQIFg6wYnGtQb4e5xrGa2xMpKlIJaXjb+YLiCYqJDG2ALFZrTrvuU2kV 9a5qfqTc1qigvNolTM0iaaUCgYApmrZWhnLUdEKV2wP813PNxfioI4afxlpHD8LG sCn48gymR6E+Lihn7vuwq5B+8fYEH1ISWxLwW+RQUjIneNhy/jjfV8TgjyFqg7or 0Sy4KjpiNI6kLBXOakELRNNMkeSPopGR2E7v5rr3bGD9oAD+aqX1G7oJH/KgPPYd Vl7+ZwKBgQDcHyWYrimjyUgKaQD2GmoO9wdcJYQ59ke9K+OuGlp4ti5arsi7N1tP B4f09aeELM2ASIuk8Q/Mx0jQFnm8lzRFXdewgvdPoZW/7VufM9O7dGPOc41cm2Dh yrTcXx/VmUBb+/fnXVEgCv7gylp/wtdTGHQBQJHR81jFBz0lnLj+gg== -----END RSA PRIVATE KEY----- netopeer2-1.1.70/example_configuration/tls_certs/server.crt0000664000000000000000000000266414021433500022607 0ustar rootroot-----BEGIN CERTIFICATE----- MIIECTCCAvGgAwIBAgIBCDANBgkqhkiG9w0BAQsFADCBjDELMAkGA1UEBhMCQ1ox FjAUBgNVBAgMDVNvdXRoIE1vcmF2aWExDTALBgNVBAcMBEJybm8xDzANBgNVBAoM BkNFU05FVDEMMAoGA1UECwwDVE1DMRMwEQYDVQQDDApleGFtcGxlIENBMSIwIAYJ KoZIhvcNAQkBFhNleGFtcGxlY2FAbG9jYWxob3N0MB4XDTE1MDczMDA3MjU1MFoX DTM1MDcyNTA3MjU1MFowgYUxCzAJBgNVBAYTAkNaMRYwFAYDVQQIDA1Tb3V0aCBN b3JhdmlhMQ8wDQYDVQQKDAZDRVNORVQxDDAKBgNVBAsMA1RNQzEXMBUGA1UEAwwO ZXhhbXBsZSBzZXJ2ZXIxJjAkBgkqhkiG9w0BCQEWF2V4YW1wbGVzZXJ2ZXJAbG9j YWxob3N0MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsdI1TBjzX1Pg QXFuPCw5/kQwU7qkrhirMcFAXhI8EoXepPa9fKAVuMjHW32P6nNzDpnhFe0YGdNl oIEN3hJJ87cVOqj4o7zZMbq3zVG2L8As7MTA8tYXm2fSC/0rIxxRRemcGUXM0q+4 LEACjZj2pOKonaivF5VbhgNjPCO1Jj/TamUc0aViE577C9L9EiObGM+bGbabWk/K WKLsvxUc+sKZXaJ7psTVgpggJAkUszlmwOQgFiMSR53E9/CAkQYhzGVCmH44Vs6H zs3RZjOTbce4wr4ongiA5LbPeSNSCFjy9loKpaE1rtOjkNBVdiNPCQTmLuODXUTK gkeL+9v/OwIDAQABo3sweTAJBgNVHRMEAjAAMCwGCWCGSAGG+EIBDQQfFh1PcGVu U1NMIEdlbmVyYXRlZCBDZXJ0aWZpY2F0ZTAdBgNVHQ4EFgQU83qEtQDFzDvLoaII vqiU6k7j1uswHwYDVR0jBBgwFoAUc1YQIqjZsHVwlea0AB4N+ilNI2gwDQYJKoZI hvcNAQELBQADggEBAJ+QOLi4gPWGofMkLTqSsbv5xRvTw0xa/sJnEeiejtygAu3o McAsyevSH9EYVPCANxzISPzd9SFaO56HxWgcxLn9vi8ZNvo2wIp9zucNu285ced1 K/2nDZfBmvBxXnj/n7spwqOyuoIc8sR7P7YyI806Qsfhk3ybNZE5UHJFZKDRQKvR J1t4nk9saeo87kIuNEDfYNdwYZzRfXoGJ5qIJQK+uJJv9noaIhfFowDW/G14Ji5p Vh/YtvnOPh7aBjOj8jmzk8MqzK+TZgT7GWu48Nd/NaV8g/DNg9hlN047LaNsJly3 NX3+VBlpMnA4rKwl1OnmYSirIVh9RJqNwqe6k/k= -----END CERTIFICATE----- netopeer2-1.1.70/example_configuration/tls_certs/server.key0000664000000000000000000000321314021433500022576 0ustar rootroot-----BEGIN RSA PRIVATE KEY----- MIIEowIBAAKCAQEAsdI1TBjzX1PgQXFuPCw5/kQwU7qkrhirMcFAXhI8EoXepPa9 fKAVuMjHW32P6nNzDpnhFe0YGdNloIEN3hJJ87cVOqj4o7zZMbq3zVG2L8As7MTA 8tYXm2fSC/0rIxxRRemcGUXM0q+4LEACjZj2pOKonaivF5VbhgNjPCO1Jj/TamUc 0aViE577C9L9EiObGM+bGbabWk/KWKLsvxUc+sKZXaJ7psTVgpggJAkUszlmwOQg FiMSR53E9/CAkQYhzGVCmH44Vs6Hzs3RZjOTbce4wr4ongiA5LbPeSNSCFjy9loK paE1rtOjkNBVdiNPCQTmLuODXUTKgkeL+9v/OwIDAQABAoIBAG/4MG1JbL4C/7vV pBcpth7Aaznd1eJ2UB4VVOWnT8JOH2L6p1h5KRRhAP9AMkXsCnAQPyZiVAG3FlAZ 01SZaY2YJDr6uQ3JVW4155TWtgSdWux//Ass+lJ17lJ0SRxjsV13ez6CsDWeRjc+ 2xy0S+KJgqk71XzhJG9fZLYyuddp3U/i3xFPUAcQM9xXKxcaD7g6LJf+a9pt6rim Eqq/pjJxDgTsRLARsazYuxrlOB445mvnLiYhOf2/MvI80jIUKaj8BeAhg49UIg/k mIh0xdevkcxBFer/BjBjscWaFjx14D6nkFMw7vtCum5KfalLN2edZKAzByOudGD4 5KnRp3ECgYEA6vnSoNGg9Do80JOpXRGYWhcR1lIDO5yRW5rVagncCcW5Pn/GMtNd x2q6k1ks8mXKR9CxZrxZGqeYObZ9a/5SLih7ZkpiVWXG8ZiBIPhP6lnwm5OeIqLa hr0BYWcRfrGg1phj5uySZgsVBE+D8jH42O9ccdvrWv1OiryAHfKIcwMCgYEAwbs+ HfQtvHOQXSYNhtOeA7IetkGy3cKVg2oILNcROvI96hS0MZKt1Rko0UAapx96eCIr el7vfdT0eUzNqt2wTKp1zmiG+SnX3fMDJNzMwu/jb/b4wQ20IHWNDnqcqTUVRUnL iksLFoHbTxsN5NpEQExcSt/zzP4qi1W2Bmo18WkCgYEAnhrk16LVux9ohiulHONW 8N9u+BeM51JtGAcxrDzgGo85Gs2czdwc0K6GxdiN/rfxCKtqgqcfCWlVaxfYgo7I OxiwF17blXx7BVrJICcUlqpX1Ebac5HCmkCYqjJQuj/I6jv1lI7/3rt8M79RF+j5 +PXt7Qq97SZd78nwJrZni4MCgYAiPjZ8lOyAouyhilhZvI3xmUpUbMhw6jQDRnqr clhZUvgeqAoxuPuA7zGHywzq/WVoVqHYv28Vjs6noiu4R/chlf+8vD0fTYYadRnZ Ki4HRt+sqrrNZN6x3hVQudt3DSr1VFXl293Z3JonIWETUoE93EFz+qHdWg+rETtb ZuqiAQKBgD+HI/syLECyO8UynuEaDD7qPl87PJ/CmZLMxa2/ZZUjhaXAW7CJMaS6 9PIzsLk33y3O4Qer0wx/tEdfnxMTBJrgGt/lFFdAKhSJroZ45l5apiavg1oZYp89 jSd0lVxWSmrBjBZLnqOl336gzaBVkBD5ND+XUPdR1UuVQExJlem4 -----END RSA PRIVATE KEY----- netopeer2-1.1.70/example_configuration/tls_keystore.xml0000664000000000000000000000753614021433500022041 0ustar rootroot<keystore xmlns="urn:ietf:params:xml:ns:yang:ietf-keystore"> <asymmetric-keys> <asymmetric-key> <name>serverkey</name> <algorithm>rsa2048</algorithm> <public-key>MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsdI1TBjzX1PgQXFuPCw5 /kQwU7qkrhirMcFAXhI8EoXepPa9fKAVuMjHW32P6nNzDpnhFe0YGdNloIEN3hJJ 87cVOqj4o7zZMbq3zVG2L8As7MTA8tYXm2fSC/0rIxxRRemcGUXM0q+4LEACjZj2 pOKonaivF5VbhgNjPCO1Jj/TamUc0aViE577C9L9EiObGM+bGbabWk/KWKLsvxUc +sKZXaJ7psTVgpggJAkUszlmwOQgFiMSR53E9/CAkQYhzGVCmH44Vs6Hzs3RZjOT bce4wr4ongiA5LbPeSNSCFjy9loKpaE1rtOjkNBVdiNPCQTmLuODXUTKgkeL+9v/ OwIDAQAB</public-key> <private-key>MIIEowIBAAKCAQEAsdI1TBjzX1PgQXFuPCw5/kQwU7qkrhirMcFAXhI8EoXepPa9 fKAVuMjHW32P6nNzDpnhFe0YGdNloIEN3hJJ87cVOqj4o7zZMbq3zVG2L8As7MTA 8tYXm2fSC/0rIxxRRemcGUXM0q+4LEACjZj2pOKonaivF5VbhgNjPCO1Jj/TamUc 0aViE577C9L9EiObGM+bGbabWk/KWKLsvxUc+sKZXaJ7psTVgpggJAkUszlmwOQg FiMSR53E9/CAkQYhzGVCmH44Vs6Hzs3RZjOTbce4wr4ongiA5LbPeSNSCFjy9loK paE1rtOjkNBVdiNPCQTmLuODXUTKgkeL+9v/OwIDAQABAoIBAG/4MG1JbL4C/7vV pBcpth7Aaznd1eJ2UB4VVOWnT8JOH2L6p1h5KRRhAP9AMkXsCnAQPyZiVAG3FlAZ 01SZaY2YJDr6uQ3JVW4155TWtgSdWux//Ass+lJ17lJ0SRxjsV13ez6CsDWeRjc+ 2xy0S+KJgqk71XzhJG9fZLYyuddp3U/i3xFPUAcQM9xXKxcaD7g6LJf+a9pt6rim Eqq/pjJxDgTsRLARsazYuxrlOB445mvnLiYhOf2/MvI80jIUKaj8BeAhg49UIg/k mIh0xdevkcxBFer/BjBjscWaFjx14D6nkFMw7vtCum5KfalLN2edZKAzByOudGD4 5KnRp3ECgYEA6vnSoNGg9Do80JOpXRGYWhcR1lIDO5yRW5rVagncCcW5Pn/GMtNd x2q6k1ks8mXKR9CxZrxZGqeYObZ9a/5SLih7ZkpiVWXG8ZiBIPhP6lnwm5OeIqLa hr0BYWcRfrGg1phj5uySZgsVBE+D8jH42O9ccdvrWv1OiryAHfKIcwMCgYEAwbs+ HfQtvHOQXSYNhtOeA7IetkGy3cKVg2oILNcROvI96hS0MZKt1Rko0UAapx96eCIr el7vfdT0eUzNqt2wTKp1zmiG+SnX3fMDJNzMwu/jb/b4wQ20IHWNDnqcqTUVRUnL iksLFoHbTxsN5NpEQExcSt/zzP4qi1W2Bmo18WkCgYEAnhrk16LVux9ohiulHONW 8N9u+BeM51JtGAcxrDzgGo85Gs2czdwc0K6GxdiN/rfxCKtqgqcfCWlVaxfYgo7I OxiwF17blXx7BVrJICcUlqpX1Ebac5HCmkCYqjJQuj/I6jv1lI7/3rt8M79RF+j5 +PXt7Qq97SZd78nwJrZni4MCgYAiPjZ8lOyAouyhilhZvI3xmUpUbMhw6jQDRnqr clhZUvgeqAoxuPuA7zGHywzq/WVoVqHYv28Vjs6noiu4R/chlf+8vD0fTYYadRnZ Ki4HRt+sqrrNZN6x3hVQudt3DSr1VFXl293Z3JonIWETUoE93EFz+qHdWg+rETtb ZuqiAQKBgD+HI/syLECyO8UynuEaDD7qPl87PJ/CmZLMxa2/ZZUjhaXAW7CJMaS6 9PIzsLk33y3O4Qer0wx/tEdfnxMTBJrgGt/lFFdAKhSJroZ45l5apiavg1oZYp89 jSd0lVxWSmrBjBZLnqOl336gzaBVkBD5ND+XUPdR1UuVQExJlem4</private-key> <certificates> <certificate> <name>servercert</name> <cert>MIIECTCCAvGgAwIBAgIBCDANBgkqhkiG9w0BAQsFADCBjDELMAkGA1UEBhMCQ1ox FjAUBgNVBAgMDVNvdXRoIE1vcmF2aWExDTALBgNVBAcMBEJybm8xDzANBgNVBAoM BkNFU05FVDEMMAoGA1UECwwDVE1DMRMwEQYDVQQDDApleGFtcGxlIENBMSIwIAYJ KoZIhvcNAQkBFhNleGFtcGxlY2FAbG9jYWxob3N0MB4XDTE1MDczMDA3MjU1MFoX DTM1MDcyNTA3MjU1MFowgYUxCzAJBgNVBAYTAkNaMRYwFAYDVQQIDA1Tb3V0aCBN b3JhdmlhMQ8wDQYDVQQKDAZDRVNORVQxDDAKBgNVBAsMA1RNQzEXMBUGA1UEAwwO ZXhhbXBsZSBzZXJ2ZXIxJjAkBgkqhkiG9w0BCQEWF2V4YW1wbGVzZXJ2ZXJAbG9j YWxob3N0MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsdI1TBjzX1Pg QXFuPCw5/kQwU7qkrhirMcFAXhI8EoXepPa9fKAVuMjHW32P6nNzDpnhFe0YGdNl oIEN3hJJ87cVOqj4o7zZMbq3zVG2L8As7MTA8tYXm2fSC/0rIxxRRemcGUXM0q+4 LEACjZj2pOKonaivF5VbhgNjPCO1Jj/TamUc0aViE577C9L9EiObGM+bGbabWk/K WKLsvxUc+sKZXaJ7psTVgpggJAkUszlmwOQgFiMSR53E9/CAkQYhzGVCmH44Vs6H zs3RZjOTbce4wr4ongiA5LbPeSNSCFjy9loKpaE1rtOjkNBVdiNPCQTmLuODXUTK gkeL+9v/OwIDAQABo3sweTAJBgNVHRMEAjAAMCwGCWCGSAGG+EIBDQQfFh1PcGVu U1NMIEdlbmVyYXRlZCBDZXJ0aWZpY2F0ZTAdBgNVHQ4EFgQU83qEtQDFzDvLoaII vqiU6k7j1uswHwYDVR0jBBgwFoAUc1YQIqjZsHVwlea0AB4N+ilNI2gwDQYJKoZI hvcNAQELBQADggEBAJ+QOLi4gPWGofMkLTqSsbv5xRvTw0xa/sJnEeiejtygAu3o McAsyevSH9EYVPCANxzISPzd9SFaO56HxWgcxLn9vi8ZNvo2wIp9zucNu285ced1 K/2nDZfBmvBxXnj/n7spwqOyuoIc8sR7P7YyI806Qsfhk3ybNZE5UHJFZKDRQKvR J1t4nk9saeo87kIuNEDfYNdwYZzRfXoGJ5qIJQK+uJJv9noaIhfFowDW/G14Ji5p Vh/YtvnOPh7aBjOj8jmzk8MqzK+TZgT7GWu48Nd/NaV8g/DNg9hlN047LaNsJly3 NX3+VBlpMnA4rKwl1OnmYSirIVh9RJqNwqe6k/k=</cert> </certificate> </certificates> </asymmetric-key> </asymmetric-keys> </keystore> netopeer2-1.1.70/example_configuration/tls_listen.xml0000664000000000000000000000332014021433500021455 0ustar rootroot<netconf-server xmlns="urn:ietf:params:xml:ns:yang:ietf-netconf-server"> <listen> <endpoint> <name>default-tls</name> <tls> <tcp-server-parameters> <local-address>0.0.0.0</local-address> <keepalives> <idle-time>1</idle-time> <max-probes>10</max-probes> <probe-interval>5</probe-interval> </keepalives> </tcp-server-parameters> <tls-server-parameters> <server-identity> <keystore-reference> <asymmetric-key>serverkey</asymmetric-key> <certificate>servercert</certificate> </keystore-reference> </server-identity> <client-authentication> <required/> <ca-certs>cacerts</ca-certs> <client-certs>clientcerts</client-certs> <cert-maps> <cert-to-name> <id>1</id> <fingerprint>02:E9:38:1F:F6:8B:62:DE:0A:0B:C5:03:81:A8:03:49:A0:00:7F:8B:F3</fingerprint> <map-type xmlns:x509c2n="urn:ietf:params:xml:ns:yang:ietf-x509-cert-to-name">x509c2n:specified</map-type> <name>tls-test</name> </cert-to-name> </cert-maps> </client-authentication> </tls-server-parameters> </tls> </endpoint> </listen> </netconf-server> netopeer2-1.1.70/example_configuration/tls_truststore.xml0000664000000000000000000000620314021433500022420 0ustar rootroot<truststore xmlns="urn:ietf:params:xml:ns:yang:ietf-truststore"> <certificates> <name>clientcerts</name> <certificate> <name>clientcert</name> <cert>MIIECTCCAvGgAwIBAgIBBzANBgkqhkiG9w0BAQsFADCBjDELMAkGA1UEBhMCQ1ox FjAUBgNVBAgMDVNvdXRoIE1vcmF2aWExDTALBgNVBAcMBEJybm8xDzANBgNVBAoM BkNFU05FVDEMMAoGA1UECwwDVE1DMRMwEQYDVQQDDApleGFtcGxlIENBMSIwIAYJ KoZIhvcNAQkBFhNleGFtcGxlY2FAbG9jYWxob3N0MB4XDTE1MDczMDA3MjcxOFoX DTM1MDcyNTA3MjcxOFowgYUxCzAJBgNVBAYTAkNaMRYwFAYDVQQIDA1Tb3V0aCBN b3JhdmlhMQ8wDQYDVQQKDAZDRVNORVQxDDAKBgNVBAsMA1RNQzEXMBUGA1UEAwwO ZXhhbXBsZSBjbGllbnQxJjAkBgkqhkiG9w0BCQEWF2V4YW1wbGVjbGllbnRAbG9j YWxob3N0MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAueCQaNQWoNmF K6LKu1p8U8ZWdWg/PvDdLsJyzfzl/Qw4UA68SfFNaY06zZl8QB9W02nr5kWeeMY0 VA3adrPgOlvfx3oWlFbkETnMaN4OT3WTQ0Wt6jAWZDzVfopwpJPAzRPxACDftIqF GagYcF32hZlVNqqnVdbXh0S0EViweqp/dbG4VDUHSNVbglc+u4UbEzNIFXMdEFsJ ZpkynOmSiTsIATqIhb+2srkVgLwhfkC2qkuHQwAHdubuB07ObM2z01UhyEdDvEYG HwtYAGDBL2TAcsI0oGeVkRyuOkV0QY0UN7UEFI1yTYw+xZ42HgFx3uGwApCImxhb j69GBYWFqwIDAQABo3sweTAJBgNVHRMEAjAAMCwGCWCGSAGG+EIBDQQfFh1PcGVu U1NMIEdlbmVyYXRlZCBDZXJ0aWZpY2F0ZTAdBgNVHQ4EFgQUXGpLeLnh2cSDARAV A7KrBxGYpo8wHwYDVR0jBBgwFoAUc1YQIqjZsHVwlea0AB4N+ilNI2gwDQYJKoZI hvcNAQELBQADggEBAJPV3RTXFRtNyOU4rjPpYeBAIAFp2aqGc4t2J1c7oPp/1n+l ZvjnwtlJpZHxMM783e2ryDQ6dkvXDf8kpwKlg3U3mkJ3xKkDdWrM4QwghXdCN519 aa9qmu0zdFL+jUAaWlQ5tsceOrvbusCcbMqiFGk/QfpHqPv52SVWbYyUx7IX7DE+ UjgsLHycfV/tlcx4ZE6soTzl9VdgSL/zmzG3rjsr58J80rXckLgBhvijgBlIAJvW fC7D0vaouvBInSFXymdPVoUDZ30cdGLf+hI/i/TfsEMOinLrXVdkSGNo6FXAHKSv XeB9oFKSzhQ7OPyRyqvEPycUSw/qD6FVr80oDDc=</cert> </certificate> </certificates> <certificates> <name>cacerts</name> <certificate> <name>cacert</name> <cert>MIID7TCCAtWgAwIBAgIJAMtE1NGAR5KoMA0GCSqGSIb3DQEBBQUAMIGMMQswCQYD VQQGEwJDWjEWMBQGA1UECAwNU291dGggTW9yYXZpYTENMAsGA1UEBwwEQnJubzEP MA0GA1UECgwGQ0VTTkVUMQwwCgYDVQQLDANUTUMxEzARBgNVBAMMCmV4YW1wbGUg Q0ExIjAgBgkqhkiG9w0BCQEWE2V4YW1wbGVjYUBsb2NhbGhvc3QwHhcNMTQwNzI0 MTQxOTAyWhcNMjQwNzIxMTQxOTAyWjCBjDELMAkGA1UEBhMCQ1oxFjAUBgNVBAgM DVNvdXRoIE1vcmF2aWExDTALBgNVBAcMBEJybm8xDzANBgNVBAoMBkNFU05FVDEM MAoGA1UECwwDVE1DMRMwEQYDVQQDDApleGFtcGxlIENBMSIwIAYJKoZIhvcNAQkB FhNleGFtcGxlY2FAbG9jYWxob3N0MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB CgKCAQEArD3TDHPAMT2Z84orK4lMlarbgooIUCcRZyLe+QM+8KY8Hn+mGaxPEOTS L3ywszqefB/Utm2hPKLHX684iRC14ID9WDGHxPjvoPArhgFhfV+qnPfxKTgxZC12 uOj4u1V9y+SkTCocFbRfXVBGpojrBuDHXkDMDEWNvr8/52YCv7bGaiBwUHolcLCU bmtKILCG0RNJyTaJpXQdAeq5Z1SJotpbfYFFtAXB32hVoLug1dzl2tjG9sb1wq3Q aDExcbC5w6P65qOkNoyym9ne6QlQagCqVDyFn3vcqkRaTjvZmxauCeUxXgJoXkyW cm0lM1KMHdoTArmchw2Dz0yHHSyDAQIDAQABo1AwTjAdBgNVHQ4EFgQUc1YQIqjZ sHVwlea0AB4N+ilNI2gwHwYDVR0jBBgwFoAUc1YQIqjZsHVwlea0AB4N+ilNI2gw DAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQUFAAOCAQEAI/1KH60qnw9Xs2RGfi0/ IKf5EynXt4bQX8EIyVKwSkYKe04zZxYfLIl/Q2HOPYoFmm3daj5ddr0ZS1i4p4fT UhstjsYWvXs3W/HhVmFUslakkn3PrswhP77fCk6eEJLxdfyJ1C7Uudq2m1isZbKi h+XF0mG1LxJaDMocSz4eAya7M5brwjy8DoOmA1TnLQFCVcpn+sCr7VC4wE/JqxyV hBCk/MuGqqM3B1j90bGFZ112ZOecyE0EDSr6IbiRBtmeNbEwOFjKXhNLYdxpBZ9D 8A/368OckZkCrVLGuJNxK9UwCVTe8IhotHUqU9EqFDmxdV8oIdU/OzUwwNPA/Bd/ 9g==</cert> </certificate> </certificates> </truststore> netopeer2-1.1.70/modules/0000775000000000000000000000000014021433500013643 5ustar rootrootnetopeer2-1.1.70/modules/iana-crypt-hash@2014-08-06.yang0000664000000000000000000001064614021433500020601 0ustar rootroot 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 12025 Waterfront Drive, Suite 300 Los Angeles, CA 90094-2536 United States Tel: +1 310 301 5800 E-Mail: iana@iana.org>"; description "This YANG module defines a type 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 7317; see the RFC itself for full legal notices."; revision 2014-08-06 { description "Initial revision."; reference "RFC 7317: 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$<clear text password> $<id>$<salt>$<password hash> $<id>$<parameter>$<salt>$<password hash> 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 '$<id>$<salt>$' or $<id>$<parameter>$<salt>$ is prepended to the result. This value is stored in the configuration data store. If a value starting with '$<id>$', where <id> 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-4.2012: Secure Hash Standard (SHS)"; } 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-4.2012: Secure Hash Standard (SHS)"; } feature crypt-hash-sha-512 { description "Indicates that the device supports the SHA-512 hash function in 'crypt-hash' values."; reference "FIPS.180-4.2012: Secure Hash Standard (SHS)"; } } netopeer2-1.1.70/modules/ietf-crypto-types@2019-07-02.yang0000664000000000000000000022723014021433500021217 0ustar rootroot 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: <http://datatracker.ietf.org/wg/netconf/> WG List: <mailto:netconf@ietf.org> Author: Kent Watsen <mailto:kent+ietf@watsen.net> Author: Wang Haiguang <wang.haiguang.shieldlab@huawei.com>"; description "This module defines common YANG types for cryptographic applications. Copyright (c) 2019 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 (https://trustee.ietf.org/license-info). This version of this YANG module is part of RFC XXXX (https://www.rfc-editor.org/info/rfcXXXX); 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 2019-07-02 { description "Initial version"; reference "RFC XXXX: Common YANG Data Types for Cryptography"; } /**************************************/ /* Identities for Hash Algorithms */ /**************************************/ typedef hash-algorithm-t { type union { type uint16; type enumeration { enum NONE { value 0; description "Hash algorithm is NULL."; } enum sha1 { value 1; status obsolete; description "The SHA1 algorithm."; reference "RFC 3174: US Secure Hash Algorithms 1 (SHA1)."; } enum sha-224 { value 2; description "The SHA-224 algorithm."; reference "RFC 6234: US Secure Hash Algorithms."; } enum sha-256 { value 3; description "The SHA-256 algorithm."; reference "RFC 6234: US Secure Hash Algorithms."; } enum sha-384 { value 4; description "The SHA-384 algorithm."; reference "RFC 6234: US Secure Hash Algorithms."; } enum sha-512 { value 5; description "The SHA-512 algorithm."; reference "RFC 6234: US Secure Hash Algorithms."; } enum shake-128 { value 6; description "The SHA3 algorithm with 128-bits output."; reference "National Institute of Standards and Technology, SHA-3 Standard: Permutation-Based Hash and Extendable-Output Functions, FIPS PUB 202, DOI 10.6028/NIST.FIPS.202, August 2015."; } enum shake-224 { value 7; description "The SHA3 algorithm with 224-bits output."; reference "National Institute of Standards and Technology, SHA-3 Standard: Permutation-Based Hash and Extendable-Output Functions, FIPS PUB 202, DOI 10.6028/NIST.FIPS.202, August 2015."; } enum shake-256 { value 8; description "The SHA3 algorithm with 256-bits output."; reference "National Institute of Standards and Technology, SHA-3 Standard: Permutation-Based Hash and Extendable-Output Functions, FIPS PUB 202, DOI 10.6028/NIST.FIPS.202, August 2015."; } enum shake-384 { value 9; description "The SHA3 algorithm with 384-bits output."; reference "National Institute of Standards and Technology, SHA-3 Standard: Permutation-Based Hash and Extendable-Output Functions, FIPS PUB 202, DOI 10.6028/NIST.FIPS.202, August 2015."; } enum shake-512 { value 10; description "The SHA3 algorithm with 384-bits output."; reference "National Institute of Standards and Technology, SHA-3 Standard: Permutation-Based Hash and Extendable-Output Functions, FIPS PUB 202, DOI 10.6028/NIST.FIPS.202, August 2015."; } } } default "0"; description "The uint16 filed shall be set by individual protocol families according to the hash algorithm value assigned by IANA. The setting is optional and by default is 0. The enumeration filed is set to the selected hash algorithm."; } /***********************************************/ /* Identities for Asymmetric Key Algorithms */ /***********************************************/ typedef asymmetric-key-algorithm-t { type union { type uint16; type enumeration { enum NONE { value 0; description "Asymetric key algorithm is NULL."; } enum rsa1024 { value 1; description "The RSA algorithm using a 1024-bit key."; reference "RFC 8017: PKCS #1: RSA Cryptography Specifications Version 2.2."; } enum rsa2048 { value 2; description "The RSA algorithm using a 2048-bit key."; reference "RFC 8017: PKCS #1: RSA Cryptography Specifications Version 2.2."; } enum rsa3072 { value 3; description "The RSA algorithm using a 3072-bit key."; reference "RFC 8017: PKCS #1: RSA Cryptography Specifications Version 2.2."; } enum rsa4096 { value 4; description "The RSA algorithm using a 4096-bit key."; reference "RFC 8017: PKCS #1: RSA Cryptography Specifications Version 2.2."; } enum rsa7680 { value 5; description "The RSA algorithm using a 7680-bit key."; reference "RFC 8017: PKCS #1: RSA Cryptography Specifications Version 2.2."; } enum rsa15360 { value 6; description "The RSA algorithm using a 15360-bit key."; reference "RFC 8017: PKCS #1: RSA Cryptography Specifications Version 2.2."; } enum secp192r1 { value 7; description "The asymmetric algorithm using a NIST P192 Curve."; reference "RFC 6090: Fundamental Elliptic Curve Cryptography Algorithms. RFC 5480: Elliptic Curve Cryptography Subject Public Key Information."; } enum secp224r1 { value 8; description "The asymmetric algorithm using a NIST P224 Curve."; reference "RFC 6090: Fundamental Elliptic Curve Cryptography Algorithms. RFC 5480: Elliptic Curve Cryptography Subject Public Key Information."; } enum secp256r1 { value 9; description "The asymmetric algorithm using a NIST P256 Curve."; reference "RFC 6090: Fundamental Elliptic Curve Cryptography Algorithms. RFC 5480: Elliptic Curve Cryptography Subject Public Key Information."; } enum secp384r1 { value 10; description "The asymmetric algorithm using a NIST P384 Curve."; reference "RFC 6090: Fundamental Elliptic Curve Cryptography Algorithms. RFC 5480: Elliptic Curve Cryptography Subject Public Key Information."; } enum secp521r1 { value 11; description "The asymmetric algorithm using a NIST P521 Curve."; reference "RFC 6090: Fundamental Elliptic Curve Cryptography Algorithms. RFC 5480: Elliptic Curve Cryptography Subject Public Key Information."; } enum x25519 { value 12; description "The asymmetric algorithm using a x.25519 Curve."; reference "RFC 7748: Elliptic Curves for Security."; } enum x448 { value 13; description "The asymmetric algorithm using a x.448 Curve."; reference "RFC 7748: Elliptic Curves for Security."; } } } default "0"; description "The uint16 filed shall be set by individual protocol families according to the asymmetric key algorithm value assigned by IANA. The setting is optional and by default is 0. The enumeration filed is set to the selected asymmetric key algorithm."; } /*************************************/ /* Identities for MAC Algorithms */ /*************************************/ typedef mac-algorithm-t { type union { type uint16; type enumeration { enum NONE { value 0; description "mac algorithm is NULL."; } enum hmac-sha1 { value 1; description "Generating MAC using SHA1 hash function"; reference "RFC 3174: US Secure Hash Algorithm 1 (SHA1)"; } enum hmac-sha1-96 { value 2; description "Generating MAC using SHA1 hash function"; reference "RFC 2404: The Use of HMAC-SHA-1-96 within ESP and AH"; } enum hmac-sha2-224 { value 3; description "Generating MAC using SHA2 hash function"; reference "RFC 6234: US Secure Hash Algorithms (SHA and SHA-based HMAC and HKDF)"; } enum hmac-sha2-256 { value 4; description "Generating MAC using SHA2 hash function"; reference "RFC 6234: US Secure Hash Algorithms (SHA and SHA-based HMAC and HKDF)"; } enum hmac-sha2-256-128 { value 5; description "Generating a 256 bits MAC using SHA2 hash function and truncate it to 128 bits"; reference "RFC 4868: Using HMAC-SHA-256, HMAC-SHA-384, and HMAC-SHA-512 with IPsec"; } enum hmac-sha2-384 { value 6; description "Generating a 384 bits MAC using SHA2 hash function"; reference "RFC 6234: US Secure Hash Algorithms (SHA and SHA-based HMAC and HKDF)"; } enum hmac-sha2-384-192 { value 7; description "Generating a 384 bits MAC using SHA2 hash function and truncate it to 192 bits"; reference "RFC 4868: Using HMAC-SHA-256, HMAC-SHA-384, and HMAC-SHA-512 with IPsec"; } enum hmac-sha2-512 { value 8; description "Generating a 512 bits MAC using SHA2 hash function"; reference "RFC 6234: US Secure Hash Algorithms (SHA and SHA-based HMAC and HKDF)"; } enum hmac-sha2-512-256 { value 9; description "Generating a 512 bits MAC using SHA2 hash function and truncate it to 256 bits"; reference "RFC 4868: Using HMAC-SHA-256, HMAC-SHA-384, and HMAC-SHA-512 with IPsec"; } enum aes-128-gmac { value 10; description "Generating 128-bit MAC using the Advanced Encryption Standard (AES) Galois Message Authentication Code (GMAC) as a mechanism to provide data origin authentication."; reference "RFC 4543: The Use of Galois Message Authentication Code (GMAC) in IPsec ESP and AH"; } enum aes-192-gmac { value 11; description "Generating 192-bit MAC using the Advanced Encryption Standard (AES) Galois Message Authentication Code (GMAC) as a mechanism to provide data origin authentication."; reference "RFC 4543: The Use of Galois Message Authentication Code (GMAC) in IPsec ESP and AH"; } enum aes-256-gmac { value 12; description "Generating 256-bit MAC using the Advanced Encryption Standard (AES) Galois Message Authentication Code (GMAC) as a mechanism to provide data origin authentication."; reference "RFC 4543: The Use of Galois Message Authentication Code (GMAC) in IPsec ESP and AH"; } enum aes-cmac-96 { value 13; description "Generating 96-bit MAC using Advanced Encryption Standard (AES) Cipher-based Message Authentication Code (CMAC)"; reference "RFC 4494: The AES-CMAC Algorithm and its Use with IPsec"; } enum aes-cmac-128 { value 14; description "Generating 128-bit MAC using Advanced Encryption Standard (AES) Cipher-based Message Authentication Code (CMAC)"; reference "RFC 4494: The AES-CMAC Algorithm and its Use with IPsec"; } enum sha1-des3-kd { value 15; description "Generating MAC using triple DES encryption function"; reference "RFC 3961: Encryption and Checksum Specifications for Kerberos 5"; } } } default "0"; description "The uint16 filed shall be set by individual protocol families according to the mac algorithm value assigned by IANA. The setting is optional and by default is 0. The enumeration filed is set to the selected mac algorithm."; } /********************************************/ /* Identities for Encryption Algorithms */ /********************************************/ typedef encryption-algorithm-t { type union { type uint16; type enumeration { enum NONE { value 0; description "Encryption algorithm is NULL."; } enum aes-128-cbc { value 1; description "Encrypt message with AES algorithm in CBC mode with a key length of 128 bits."; reference "RFC 3565: Use of the Advanced Encryption Standard (AES) Encryption Algorithm in Cryptographic Message Syntax (CMS)"; } enum aes-192-cbc { value 2; description "Encrypt message with AES algorithm in CBC mode with a key length of 192 bits"; reference "RFC 3565: Use of the Advanced Encryption Standard (AES) Encryption Algorithm in Cryptographic Message Syntax (CMS)"; } enum aes-256-cbc { value 3; description "Encrypt message with AES algorithm in CBC mode with a key length of 256 bits"; reference "RFC 3565: Use of the Advanced Encryption Standard (AES) Encryption Algorithm in Cryptographic Message Syntax (CMS)"; } enum aes-128-ctr { value 4; description "Encrypt message with AES algorithm in CTR mode with a key length of 128 bits"; reference "RFC 3686: Using Advanced Encryption Standard (AES) Counter Mode with IPsec Encapsulating Security Payload (ESP)"; } enum aes-192-ctr { value 5; description "Encrypt message with AES algorithm in CTR mode with a key length of 192 bits"; reference "RFC 3686: Using Advanced Encryption Standard (AES) Counter Mode with IPsec Encapsulating Security Payload (ESP)"; } enum aes-256-ctr { value 6; description "Encrypt message with AES algorithm in CTR mode with a key length of 256 bits"; reference "RFC 3686: Using Advanced Encryption Standard (AES) Counter Mode with IPsec Encapsulating Security Payload (ESP)"; } enum des3-cbc-sha1-kd { value 7; description "Encrypt message with 3DES algorithm in CBC mode with sha1 function for key derivation"; reference "RFC 3961: Encryption and Checksum Specifications for Kerberos 5"; } enum rc4-hmac { value 8; description "Encrypt message with rc4 algorithm"; reference "RFC 4757: The RC4-HMAC Kerberos Encryption Types Used by Microsoft Windows"; } enum rc4-hmac-exp { value 9; description "Encrypt message with rc4 algorithm that is exportable"; reference "RFC 4757: The RC4-HMAC Kerberos Encryption Types Used by Microsoft Windows"; } } } default "0"; description "The uint16 filed shall be set by individual protocol families according to the encryption algorithm value assigned by IANA. The setting is optional and by default is 0. The enumeration filed is set to the selected encryption algorithm."; } /****************************************************/ /* Identities for Encryption and MAC Algorithms */ /****************************************************/ typedef encryption-and-mac-algorithm-t { type union { type uint16; type enumeration { enum NONE { value 0; description "Encryption and MAC algorithm is NULL."; reference "None"; } enum aes-128-ccm { value 1; description "Encrypt message with AES algorithm in CCM mode with a key length of 128 bits; it can also be used for generating MAC"; reference "RFC 4309: Using Advanced Encryption Standard (AES) CCM Mode with IPsec Encapsulating Security Payload (ESP)"; } enum aes-192-ccm { value 2; description "Encrypt message with AES algorithm in CCM mode with a key length of 192 bits; it can also be used for generating MAC"; reference "RFC 4309: Using Advanced Encryption Standard (AES) CCM Mode with IPsec Encapsulating Security Payload (ESP)"; } enum aes-256-ccm { value 3; description "Encrypt message with AES algorithm in CCM mode with a key length of 256 bits; it can also be used for generating MAC"; reference "RFC 4309: Using Advanced Encryption Standard (AES) CCM Mode with IPsec Encapsulating Security Payload (ESP)"; } enum aes-128-gcm { value 4; description "Encrypt message with AES algorithm in GCM mode with a key length of 128 bits; it can also be used for generating MAC"; reference "RFC 4106: The Use of Galois/Counter Mode (GCM) in IPsec Encapsulating Security Payload (ESP)"; } enum aes-192-gcm { value 5; description "Encrypt message with AES algorithm in GCM mode with a key length of 192 bits; it can also be used for generating MAC"; reference "RFC 4106: The Use of Galois/Counter Mode (GCM) in IPsec Encapsulating Security Payload (ESP)"; } enum aes-256-gcm { value 6; description "Encrypt message with AES algorithm in GCM mode with a key length of 256 bits; it can also be used for generating MAC"; reference "RFC 4106: The Use of Galois/Counter Mode (GCM) in IPsec Encapsulating Security Payload (ESP)"; } enum chacha20-poly1305 { value 7; description "Encrypt message with chacha20 algorithm and generate MAC with POLY1305; it can also be used for generating MAC"; reference "RFC 8439: ChaCha20 and Poly1305 for IETF Protocols"; } } } default "0"; description "The uint16 filed shall be set by individual protocol families according to the encryption and mac algorithm value assigned by IANA. The setting is optional and by default is 0. The enumeration filed is set to the selected encryption and mac algorithm."; } /******************************************/ /* Identities for signature algorithm */ /******************************************/ typedef signature-algorithm-t { type union { type uint16; type enumeration { enum NONE { value 0; description "Signature algorithm is NULL"; } enum dsa-sha1 { value 1; description "The signature algorithm using DSA algorithm with SHA1 hash algorithm"; reference "RFC 4253: The Secure Shell (SSH) Transport Layer Protocol"; } enum rsassa-pkcs1-sha1 { value 2; description "The signature algorithm using RSASSA-PKCS1-v1_5 with the SHA1 hash algorithm."; reference "RFC 4253: The Secure Shell (SSH) Transport Layer Protocol"; } enum rsassa-pkcs1-sha256 { value 3; description "The signature algorithm using RSASSA-PKCS1-v1_5 with the SHA256 hash algorithm."; reference "RFC 8332: Use of RSA Keys with SHA-256 and SHA-512 in the Secure Shell (SSH) Protocol RFC 8446: The Transport Layer Security (TLS) Protocol Version 1.3"; } enum rsassa-pkcs1-sha384 { value 4; description "The signature algorithm using RSASSA-PKCS1-v1_5 with the SHA384 hash algorithm."; reference "RFC 8446: The Transport Layer Security (TLS) Protocol Version 1.3"; } enum rsassa-pkcs1-sha512 { value 5; description "The signature algorithm using RSASSA-PKCS1-v1_5 with the SHA512 hash algorithm."; reference "RFC 8332: Use of RSA Keys with SHA-256 and SHA-512 in the Secure Shell (SSH) Protocol RFC 8446: The Transport Layer Security (TLS) Protocol Version 1.3"; } enum rsassa-pss-rsae-sha256 { value 6; description "The signature algorithm using RSASSA-PSS with mask generation function 1 and SHA256 hash algorithm. If the public key is carried in an X.509 certificate, it MUST use the rsaEncryption OID"; reference "RFC 8446: The Transport Layer Security (TLS) Protocol Version 1.3"; } enum rsassa-pss-rsae-sha384 { value 7; description "The signature algorithm using RSASSA-PSS with mask generation function 1 and SHA384 hash algorithm. If the public key is carried in an X.509 certificate, it MUST use the rsaEncryption OID"; reference "RFC 8446: The Transport Layer Security (TLS) Protocol Version 1.3"; } enum rsassa-pss-rsae-sha512 { value 8; description "The signature algorithm using RSASSA-PSS with mask generation function 1 and SHA512 hash algorithm. If the public key is carried in an X.509 certificate, it MUST use the rsaEncryption OID"; reference "RFC 8446: The Transport Layer Security (TLS) Protocol Version 1.3"; } enum rsassa-pss-pss-sha256 { value 9; description "The signature algorithm using RSASSA-PSS with mask generation function 1 and SHA256 hash algorithm. If the public key is carried in an X.509 certificate, it MUST use the rsaEncryption OID"; reference "RFC 8446: The Transport Layer Security (TLS) Protocol Version 1.3"; } enum rsassa-pss-pss-sha384 { value 10; description "The signature algorithm using RSASSA-PSS with mask generation function 1 and SHA384 hash algorithm. If the public key is carried in an X.509 certificate, it MUST use the rsaEncryption OID"; reference "RFC 8446: The Transport Layer Security (TLS) Protocol Version 1.3"; } enum rsassa-pss-pss-sha512 { value 11; description "The signature algorithm using RSASSA-PSS with mask generation function 1 and SHA512 hash algorithm. If the public key is carried in an X.509 certificate, it MUST use the rsaEncryption OID"; reference "RFC 8446: The Transport Layer Security (TLS) Protocol Version 1.3"; } enum ecdsa-secp256r1-sha256 { value 12; description "The signature algorithm using ECDSA with curve name secp256r1 and SHA256 hash algorithm."; reference "RFC 5656: Elliptic Curve Algorithm Integration in the Secure Shell Transport Layer RFC 8446: The Transport Layer Security (TLS) Protocol Version 1.3"; } enum ecdsa-secp384r1-sha384 { value 13; description "The signature algorithm using ECDSA with curve name secp384r1 and SHA384 hash algorithm."; reference "RFC 5656: Elliptic Curve Algorithm Integration in the Secure Shell Transport Layer RFC 8446: The Transport Layer Security (TLS) Protocol Version 1.3"; } enum ecdsa-secp521r1-sha512 { value 14; description "The signature algorithm using ECDSA with curve name secp521r1 and SHA512 hash algorithm."; reference "RFC 5656: Elliptic Curve Algorithm Integration in the Secure Shell Transport Layer RFC 8446: The Transport Layer Security (TLS) Protocol Version 1.3"; } enum ed25519 { value 15; description "The signature algorithm using EdDSA with curve x25519"; reference "RFC 8032: Edwards-Curve Digital Signature Algorithm (EdDSA)"; } enum ed25519-cts { value 16; description "The signature algorithm using EdDSA with curve x25519 with phflag = 0"; reference "RFC 8032: Edwards-Curve Digital Signature Algorithm (EdDSA)"; } enum ed25519-ph { value 17; description "The signature algorithm using EdDSA with curve x25519 with phflag = 1"; reference "RFC 8032: Edwards-Curve Digital Signature Algorithm (EdDSA)"; } enum ed25519-sha512 { value 18; description "The signature algorithm using EdDSA with curve x25519 and SHA-512 function"; reference "RFC 8419: Use of Edwards-Curve Digital Signature Algorithm (EdDSA) Signatures in the Cryptographic Message Syntax (CMS)"; } enum ed448 { value 19; description "The signature algorithm using EdDSA with curve x448"; reference "RFC 8032: Edwards-Curve Digital Signature Algorithm (EdDSA)"; } enum ed448-ph { value 20; description "The signature algorithm using EdDSA with curve x448 and with PH being SHAKE256(x, 64) and phflag being 1"; reference "RFC 8032: Edwards-Curve Digital Signature Algorithm (EdDSA)"; } enum ed448-shake256 { value 21; description "The signature algorithm using EdDSA with curve x448 and SHAKE-256 function"; reference "RFC 8419: Use of Edwards-Curve Digital Signature Algorithm (EdDSA) Signatures in the Cryptographic Message Syntax (CMS)"; } enum ed448-shake256-len { value 22; description "The signature algorithm using EdDSA with curve x448 and SHAKE-256 function and a customized hash output"; reference "RFC 8419: Use of Edwards-Curve Digital Signature Algorithm (EdDSA) Signatures in the Cryptographic Message Syntax (CMS)"; } enum rsa-sha2-256 { value 23; description "The signature algorithm using RSA with SHA2 function for SSH protocol"; reference "RFC 8332: Use of RSA Keys with SHA-256 and SHA-512 in the Secure Shell (SSH) Protocol"; } enum rsa-sha2-512 { value 24; description "The signature algorithm using RSA with SHA2 function for SSH protocol"; reference "RFC 8332: Use of RSA Keys with SHA-256 and SHA-512 in the Secure Shell (SSH) Protocol"; } enum eccsi { value 25; description "The signature algorithm using ECCSI signature as defined in RFC 6507."; reference "RFC 6507: Elliptic Curve-Based Certificateless Signatures for Identity-based Encryption (ECCSI)"; } } } default "0"; description "The uint16 filed shall be set by individual protocol families according to the signature algorithm value assigned by IANA. The setting is optional and by default is 0. The enumeration filed is set to the selected signature algorithm."; } /**********************************************/ /* Identities for key exchange algorithms */ /**********************************************/ typedef key-exchange-algorithm-t { type union { type uint16; type enumeration { enum NONE { value 0; description "Key exchange algorithm is NULL."; } enum psk-only { value 1; description "Using Pre-shared key for authentication and key exchange"; reference "RFC 4279: Pre-Shared Key cipher suites for Transport Layer Security (TLS)"; } enum dhe-ffdhe2048 { value 2; description "Ephemeral Diffie Hellman key exchange with 2048 bit finite field"; reference "RFC 7919: Negotiated Finite Field Diffie-Hellman Ephemeral Parameters for Transport Layer Security (TLS)"; } enum dhe-ffdhe3072 { value 3; description "Ephemeral Diffie Hellman key exchange with 3072 bit finite field"; reference "RFC 7919: Negotiated Finite Field Diffie-Hellman Ephemeral Parameters for Transport Layer Security (TLS)"; } enum dhe-ffdhe4096 { value 4; description "Ephemeral Diffie Hellman key exchange with 4096 bit finite field"; reference "RFC 7919: Negotiated Finite Field Diffie-Hellman Ephemeral Parameters for Transport Layer Security (TLS)"; } enum dhe-ffdhe6144 { value 5; description "Ephemeral Diffie Hellman key exchange with 6144 bit finite field"; reference "RFC 7919: Negotiated Finite Field Diffie-Hellman Ephemeral Parameters for Transport Layer Security (TLS)"; } enum dhe-ffdhe8192 { value 6; description "Ephemeral Diffie Hellman key exchange with 8192 bit finite field"; reference "RFC 7919: Negotiated Finite Field Diffie-Hellman Ephemeral Parameters for Transport Layer Security (TLS)"; } enum psk-dhe-ffdhe2048 { value 7; description "Key exchange using pre-shared key with Diffie-Hellman key generation mechanism, where the DH group is FFDHE2048"; reference "RFC 8446: The Transport Layer Security (TLS) Protocol Version 1.3"; } enum psk-dhe-ffdhe3072 { value 8; description "Key exchange using pre-shared key with Diffie-Hellman key generation mechanism, where the DH group is FFDHE3072"; reference "RFC 8446: The Transport Layer Security (TLS) Protocol Version 1.3"; } enum psk-dhe-ffdhe4096 { value 9; description "Key exchange using pre-shared key with Diffie-Hellman key generation mechanism, where the DH group is FFDHE4096"; reference "RFC 8446: The Transport Layer Security (TLS) Protocol Version 1.3"; } enum psk-dhe-ffdhe6144 { value 10; description "Key exchange using pre-shared key with Diffie-Hellman key generation mechanism, where the DH group is FFDHE6144"; reference "RFC 8446: The Transport Layer Security (TLS) Protocol Version 1.3"; } enum psk-dhe-ffdhe8192 { value 11; description "Key exchange using pre-shared key with Diffie-Hellman key generation mechanism, where the DH group is FFDHE8192"; reference "RFC 8446: The Transport Layer Security (TLS) Protocol Version 1.3"; } enum ecdhe-secp256r1 { value 12; description "Ephemeral Diffie Hellman key exchange with elliptic group over curve secp256r1"; reference "RFC 8422: Elliptic Curve Cryptography (ECC) Cipher Suites for Transport Layer Security (TLS) Versions 1.2 and Earlier"; } enum ecdhe-secp384r1 { value 13; description "Ephemeral Diffie Hellman key exchange with elliptic group over curve secp384r1"; reference "RFC 8422: Elliptic Curve Cryptography (ECC) Cipher Suites for Transport Layer Security (TLS) Versions 1.2 and Earlier"; } enum ecdhe-secp521r1 { value 14; description "Ephemeral Diffie Hellman key exchange with elliptic group over curve secp521r1"; reference "RFC 8422: Elliptic Curve Cryptography (ECC) Cipher Suites for Transport Layer Security (TLS) Versions 1.2 and Earlier"; } enum ecdhe-x25519 { value 15; description "Ephemeral Diffie Hellman key exchange with elliptic group over curve x25519"; reference "RFC 8422: Elliptic Curve Cryptography (ECC) Cipher Suites for Transport Layer Security (TLS) Versions 1.2 and Earlier"; } enum ecdhe-x448 { value 16; description "Ephemeral Diffie Hellman key exchange with elliptic group over curve x448"; reference "RFC 8422: Elliptic Curve Cryptography (ECC) Cipher Suites for Transport Layer Security (TLS) Versions 1.2 and Earlier"; } enum psk-ecdhe-secp256r1 { value 17; description "Key exchange using pre-shared key with elliptic group-based Ephemeral Diffie Hellman key exchange over curve secp256r1"; reference "RFC 8446: The Transport Layer Security (TLS) Protocol Version 1.3"; } enum psk-ecdhe-secp384r1 { value 18; description "Key exchange using pre-shared key with elliptic group-based Ephemeral Diffie Hellman key exchange over curve secp384r1"; reference "RFC 8446: The Transport Layer Security (TLS) Protocol Version 1.3"; } enum psk-ecdhe-secp521r1 { value 19; description "Key exchange using pre-shared key with elliptic group-based Ephemeral Diffie Hellman key exchange over curve secp521r1"; reference "RFC 8446: The Transport Layer Security (TLS) Protocol Version 1.3"; } enum psk-ecdhe-x25519 { value 20; description "Key exchange using pre-shared key with elliptic group-based Ephemeral Diffie Hellman key exchange over curve x25519"; reference "RFC 8446: The Transport Layer Security (TLS) Protocol Version 1.3"; } enum psk-ecdhe-x448 { value 21; description "Key exchange using pre-shared key with elliptic group-based Ephemeral Diffie Hellman key exchange over curve x448"; reference "RFC 8446: The Transport Layer Security (TLS) Protocol Version 1.3"; } enum diffie-hellman-group14-sha1 { value 22; description "Using DH group14 and SHA1 for key exchange"; reference "RFC 4253: The Secure Shell (SSH) Transport Layer Protocol"; } enum diffie-hellman-group14-sha256 { value 23; description "Using DH group14 and SHA-256 for key exchange"; reference "RFC 8268: More Modular Exponentiation (MODP) Diffie-Hellman (DH) Key Exchange (KEX) Groups for Secure Shell (SSH)"; } enum diffie-hellman-group15-sha512 { value 24; description "Using DH group15 and SHA-512 for key exchange"; reference "RFC 8268: More Modular Exponentiation (MODP) Diffie-Hellman (DH) Key Exchange (KEX) Groups for Secure Shell (SSH)"; } enum diffie-hellman-group16-sha512 { value 25; description "Using DH group16 and SHA-512 for key exchange"; reference "RFC 8268: More Modular Exponentiation (MODP) Diffie-Hellman (DH) Key Exchange (KEX) Groups for Secure Shell (SSH)"; } enum diffie-hellman-group17-sha512 { value 26; description "Using DH group17 and SHA-512 for key exchange"; reference "RFC 8268: More Modular Exponentiation (MODP) Diffie-Hellman (DH) Key Exchange (KEX) Groups for Secure Shell (SSH)"; } enum diffie-hellman-group18-sha512 { value 27; description "Using DH group18 and SHA-512 for key exchange"; reference "RFC 8268: More Modular Exponentiation (MODP) Diffie-Hellman (DH) Key Exchange (KEX) Groups for Secure Shell (SSH)"; } enum ecdh-sha2-secp256r1 { value 28; description "Elliptic curve-based Diffie Hellman key exchange over curve ecp256r1 and using SHA2 for MAC generation"; reference "RFC 6239: Suite B Cryptographic Suites for Secure Shell (SSH)"; } enum ecdh-sha2-secp384r1 { value 29; description "Elliptic curve-based Diffie Hellman key exchange over curve ecp384r1 and using SHA2 for MAC generation"; reference "RFC 6239: Suite B Cryptographic Suites for Secure Shell (SSH)"; } enum ecdh-x25519-x9.63-sha256 { value 30; description "Elliptic curve-based Diffie Hellman key exchange over curve x.25519 and using ANSI x9.63 with SHA256 as KDF"; reference "RFC 8418: Use of the Elliptic Curve Diffie-Hellman Key Agreement Algorithm with X25519 and X448 in the Cryptographic Message Syntax (CMS)"; } enum ecdh-x25519-x9.63-sha384 { value 31; description "Elliptic curve-based Diffie Hellman key exchange over curve x.25519 and using ANSI x9.63 with SHA384 as KDF"; reference "RFC 8418: Use of the Elliptic Curve Diffie-Hellman Key Agreement Algorithm with X25519 and X448 in the Cryptographic Message Syntax (CMS)"; } enum ecdh-x25519-x9.63-sha512 { value 32; description "Elliptic curve-based Diffie Hellman key exchange over curve x.25519 and using ANSI x9.63 with SHA512 as KDF"; reference "RFC 8418: Use of the Elliptic Curve Diffie-Hellman Key Agreement Algorithm with X25519 and X448 in the Cryptographic Message Syntax (CMS)"; } enum ecdh-x25519-hkdf-sha256 { value 33; description "Elliptic curve-based Diffie Hellman key exchange over curve x.25519 and using HKDF with SHA256 as KDF"; reference "RFC 8418: Use of the Elliptic Curve Diffie-Hellman Key Agreement Algorithm with X25519 and X448 in the Cryptographic Message Syntax (CMS)"; } enum ecdh-x25519-hkdf-sha384 { value 34; description "Elliptic curve-based Diffie Hellman key exchange over curve x.25519 and using HKDF with SHA384 as KDF"; reference "RFC 8418: Use of the Elliptic Curve Diffie-Hellman Key Agreement Algorithm with X25519 and X448 in the Cryptographic Message Syntax (CMS)"; } enum ecdh-x25519-hkdf-sha512 { value 35; description "Elliptic curve-based Diffie Hellman key exchange over curve x.25519 and using HKDF with SHA512 as KDF"; reference "RFC 8418: Use of the Elliptic Curve Diffie-Hellman Key Agreement Algorithm with X25519 and X448 in the Cryptographic Message Syntax (CMS)"; } enum ecdh-x448-x9.63-sha256 { value 36; description "Elliptic curve-based Diffie Hellman key exchange over curve x.448 and using ANSI x9.63 with SHA256 as KDF"; reference "RFC 8418: Use of the Elliptic Curve Diffie-Hellman Key Agreement Algorithm with X25519 and X448 in the Cryptographic Message Syntax (CMS)"; } enum ecdh-x448-x9.63-sha384 { value 37; description "Elliptic curve-based Diffie Hellman key exchange over curve x.448 and using ANSI x9.63 with SHA384 as KDF"; reference "RFC 8418: Use of the Elliptic Curve Diffie-Hellman Key Agreement Algorithm with X25519 and X448 in the Cryptographic Message Syntax (CMS)"; } enum ecdh-x448-x9.63-sha512 { value 38; description "Elliptic curve-based Diffie Hellman key exchange over curve x.448 and using ANSI x9.63 with SHA512 as KDF"; reference "RFC 8418: Use of the Elliptic Curve Diffie-Hellman Key Agreement Algorithm with X25519 and X448 in the Cryptographic Message Syntax (CMS)"; } enum ecdh-x448-hkdf-sha256 { value 39; description "Elliptic curve-based Diffie Hellman key exchange over curve x.448 and using HKDF with SHA256 as KDF"; reference "RFC 8418: Use of the Elliptic Curve Diffie-Hellman Key Agreement Algorithm with X25519 and X448 in the Cryptographic Message Syntax (CMS)"; } enum ecdh-x448-hkdf-sha384 { value 40; description "Elliptic curve-based Diffie Hellman key exchange over curve x.448 and using HKDF with SHA384 as KDF"; reference "RFC 8418: Use of the Elliptic Curve Diffie-Hellman Key Agreement Algorithm with X25519 and X448 in the Cryptographic Message Syntax (CMS)"; } enum ecdh-x448-hkdf-sha512 { value 41; description "Elliptic curve-based Diffie Hellman key exchange over curve x.448 and using HKDF with SHA512 as KDF"; reference "RFC 8418: Use of the Elliptic Curve Diffie-Hellman Key Agreement Algorithm with X25519 and X448 in the Cryptographic Message Syntax (CMS)"; } enum rsaes-oaep { value 42; description "RSAES-OAEP combines the RSAEP and RSADP primitives with the EME-OAEP encoding method"; reference "RFC 8017: PKCS #1: RSA Cryptography Specifications Version 2.2."; } enum rsaes-pkcs1-v1_5 { value 43; description "RSAES-PKCS1-v1_5 combines the RSAEP and RSADP primitives with the EME-PKCS1-v1_5 encoding method"; reference "RFC 8017: PKCS #1: RSA Cryptography Specifications Version 2.2."; } } } default "0"; description "The uint16 filed shall be set by individual protocol families according to the key exchange algorithm value assigned by IANA. The setting is optional and by default is 0. The enumeration filed is set to the selected key exchange algorithm."; } /***************************************************/ /* 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)."; } 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)."; } /***********************************************/ /* 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)."; } 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 structures related to RFC 4253 */ /***************************************************/ typedef ssh-host-key { type binary; description "The binary public key data for this SSH 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"; } /*********************************************************/ /* 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 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 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."; } 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 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."; } /**********************************************/ /* Groupings for keys and/or certificates */ /**********************************************/ grouping symmetric-key-grouping { description "A symmetric key and algorithm."; leaf algorithm { type encryption-algorithm-t; mandatory true; description "The algorithm to be used when generating the key."; reference "RFC CCCC: Common YANG Data Types for Cryptography"; } choice key-type { mandatory true; description "Choice between key types."; leaf key { nacm:default-deny-all; type binary; description "The binary value of the key. The interpretation of the value is defined by 'algorithm'. For example, FIXME."; reference "RFC XXXX: FIXME"; } leaf hidden-key { nacm:default-deny-write; type empty; description "A permanently hidden key. How such keys are created is outside the scope of this module."; } } } grouping public-key-grouping { description "A public key and its associated algorithm."; leaf algorithm { nacm:default-deny-write; type asymmetric-key-algorithm-t; mandatory true; description "Identifies the key's algorithm."; reference "RFC CCCC: Common YANG Data Types for Cryptography"; } 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 'algorithm'. For example, a DSA key is an integer, an RSA key is represented as RSAPublicKey per RFC 8017, and an ECC key is represented using the 'publicKey' described in RFC 5915."; reference "RFC 8017: Public-Key Cryptography Standards (PKCS) #1: RSA Cryptography Specifications Version 2.2. RFC 5915: Elliptic Curve Private Key Structure."; } } grouping asymmetric-key-pair-grouping { description "A private key and its associated public key and algorithm."; uses public-key-grouping; choice private-key-type { mandatory true; description "Choice between key types."; leaf private-key { nacm:default-deny-all; type binary; description "The value of the binary key. The key's value is interpreted by the 'algorithm'. For example, a DSA key is an integer, an RSA key is represented as RSAPrivateKey as defined in RFC 8017, and an ECC key is represented as ECPrivateKey as defined in RFC 5915."; reference "RFC 8017: Public-Key Cryptography Standards (PKCS) #1: RSA Cryptography Specifications Version 2.2. RFC 5915: Elliptic Curve Private Key Structure."; } leaf hidden-private-key { nacm:default-deny-write; type empty; description "A permanently hidden key. How such keys are created is outside the scope of this module."; } } } 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 { nacm:default-deny-write; type trust-anchor-cert-cms; description "The binary certificate data for this certificate."; reference "RFC YYYY: Common YANG Data Types for Cryptography"; } notification certificate-expiration { 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-certs-grouping { description "A list of trust anchor certificates, and a notification for when one is about to (or already has) expire."; leaf-list cert { nacm:default-deny-write; type trust-anchor-cert-cms; description "The binary certificate data for this certificate."; reference "RFC YYYY: Common YANG Data Types for Cryptography"; } notification certificate-expiration { 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 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 { nacm:default-deny-write; type end-entity-cert-cms; description "The binary certificate data for this certificate."; reference "RFC YYYY: Common YANG Data Types for Cryptography"; } notification certificate-expiration { 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 end-entity-certs-grouping { description "A list of end entity certificates, and a notification for when one is about to (or already has) expire."; leaf-list cert { nacm:default-deny-write; type end-entity-cert-cms; description "The binary certificate data for this certificate."; reference "RFC YYYY: Common YANG Data Types for Cryptography"; } notification certificate-expiration { 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 asymmetric-key-pair-with-cert-grouping { description "A private/public key pair and an associated certificate. Implementations SHOULD assert that certificates contain the matching public key."; uses asymmetric-key-pair-grouping; uses end-entity-cert-grouping; action generate-certificate-signing-request { nacm:default-deny-all; description "Generates a certificate signing request structure for the associated asymmetric key using the passed subject and attribute values. The specified assertions need to be appropriate for the certificate's use. For example, an entity certificate for a TLS server SHOULD have values that enable clients to satisfy RFC 6125 processing."; input { leaf subject { type binary; mandatory true; description "The 'subject' field per the CertificationRequestInfo structure as specified by RFC 2986, Section 4.1 encoded using the 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)."; } leaf attributes { type binary; // FIXME: does this need to be mandatory? description "The 'attributes' field from the structure CertificationRequestInfo as specified by RFC 2986, Section 4.1 encoded using the 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)."; } } output { leaf certificate-signing-request { type binary; mandatory true; description "A CertificationRequest structure as specified by RFC 2986, Section 4.2 encoded using the 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)."; } } } // generate-certificate-signing-request } // asymmetric-key-pair-with-cert-grouping grouping asymmetric-key-pair-with-certs-grouping { description "A private/public key pair and 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. More than one certificate supports, for instance, a TPM-protected asymmetric key that has both IDevID and LDevID certificates associated."; list certificate { key "name"; description "A certificate for this asymmetric key."; leaf name { type string; description "An arbitrary name for the certificate. If the name matches the name of a certificate that exists independently in <operational> (i.e., an IDevID), then the 'cert' node MUST NOT be configured."; } uses end-entity-cert-grouping; } } // certificates action generate-certificate-signing-request { nacm:default-deny-all; description "Generates a certificate signing request structure for the associated asymmetric key using the passed subject and attribute values. The specified assertions need to be appropriate for the certificate's use. For example, an entity certificate for a TLS server SHOULD have values that enable clients to satisfy RFC 6125 processing."; input { leaf subject { type binary; mandatory true; description "The 'subject' field per the CertificationRequestInfo structure as specified by RFC 2986, Section 4.1 encoded using the 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)."; } leaf attributes { type binary; // FIXME: does this need to be mandatory? description "The 'attributes' field from the structure CertificationRequestInfo as specified by RFC 2986, Section 4.1 encoded using the 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)."; } } output { leaf certificate-signing-request { type binary; mandatory true; description "A CertificationRequest structure as specified by RFC 2986, Section 4.2 encoded using the 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)."; } } } // generate-certificate-signing-request } // asymmetric-key-pair-with-certs-grouping } netopeer2-1.1.70/modules/ietf-datastores@2017-08-17.yang0000664000000000000000000000527614021433500020717 0ustar rootrootmodule ietf-datastores { yang-version 1.1; namespace "urn:ietf:params:xml:ns:yang:ietf-datastores"; prefix ds; organization "IETF Network Modeling (NETMOD) Working Group"; contact "WG Web: <https://datatracker.ietf.org/wg/netmod/> WG List: <mailto:netmod@ietf.org> Author: Martin Bjorklund <mailto:mbj@tail-f.com> Author: Juergen Schoenwaelder <mailto:j.schoenwaelder@jacobs-university.de> Author: Phil Shafer <mailto:phil@juniper.net> Author: Kent Watsen <mailto:kwatsen@juniper.net> Author: Rob Wilton <rwilton@cisco.com>"; description "This YANG module defines two sets of identities for datastores. The first identifies the datastores themselves, the second identifies datastore properties. Copyright (c) 2017 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 XXXX (http://www.rfc-editor.org/info/rfcxxxx); see the RFC itself for full legal notices."; revision 2017-08-17 { description "Initial revision."; reference "RFC XXXX: Network Management Datastore Architecture"; } /* * Identities */ identity datastore { description "Abstract base identity for datastore identities."; } identity conventional { base datastore; description "Abstract base identity for conventional configuration datastores."; } identity running { base conventional; description "The running configuration datastore."; } identity candidate { base conventional; description "The candidate configuration datastore."; } identity startup { base conventional; description "The startup configuration datastore."; } identity intended { base conventional; description "The intended configuration datastore."; } identity dynamic { base datastore; description "Abstract base identity for dynamic configuration datastores."; } identity operational { base datastore; description "The operational state datastore."; } /* * Type definitions */ typedef datastore-ref { type identityref { base datastore; } description "A datastore identity reference."; } } netopeer2-1.1.70/modules/ietf-keystore@2019-07-02.yang0000664000000000000000000003703514021433500020404 0ustar rootroot module ietf-keystore { yang-version 1.1; namespace "urn:ietf:params:xml:ns:yang:ietf-keystore"; prefix ks; import ietf-crypto-types { prefix ct; reference "RFC CCCC: Common YANG Data Types for Cryptography"; } import ietf-netconf-acm { prefix nacm; reference "RFC 8341: Network Configuration Access Control Model"; } organization "IETF NETCONF (Network Configuration) Working Group"; contact "WG Web: <http://datatracker.ietf.org/wg/netconf/> WG List: <mailto:netconf@ietf.org> Author: Kent Watsen <mailto:kent+ietf@watsen.net>"; description "This module defines a keystore to centralize management of security credentials. Copyright (c) 2019 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 (https://trustee.ietf.org/license-info). This version of this YANG module is part of RFC XXXX (https://www.rfc-editor.org/info/rfcXXXX); 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 2019-07-02 { description "Initial version"; reference "RFC VVVV: A YANG Data Model for a Keystore"; } /****************/ /* Features */ /****************/ feature keystore-supported { description "The 'keystore-supported' feature indicates that the server supports the keystore."; } feature local-definitions-supported { description "The 'local-definitions-supported' feature indicates that the server supports locally-defined keys."; } feature key-generation { description "Indicates that the server supports the actions related to the life cycling keys in <operational>. To be used by configuration, keys in <operational> must be copied to <running>."; } /****************/ /* Typedefs */ /****************/ typedef asymmetric-key-ref { type leafref { path "/ks:keystore/ks:asymmetric-keys/ks:asymmetric-key" + "/ks:name"; } description "This typedef enables modules to easily define a reference to an asymmetric key stored in the keystore."; } /*****************/ /* Groupings */ /*****************/ grouping key-reference-type-grouping { description "A reusable grouping for a choice for the type of key referenced in the keystore."; choice key-type { mandatory true; description "A choice between a reference to a symmetric or asymmetric key in the keystore."; leaf symmetric-key-ref { if-feature "keystore-supported"; type leafref { path "/ks:keystore/ks:symmetric-keys/ks:symmetric-key/" + "ks:name"; } description "Identifies a symmetric key used to encrypt this key."; } leaf asymmetric-key-ref { if-feature "keystore-supported"; type leafref { path "/ks:keystore/ks:asymmetric-keys/ks:asymmetric-key/" + "ks:name"; } description "Identifies an asymmetric key used to encrypt this key."; } } } grouping encrypted-value-grouping { description "A reusable grouping for a value that has been encrypted by a symmetric or asymmetric key in the keystore."; uses "key-reference-type-grouping"; leaf value { type binary; description "The private key, encrypted using the specified symmetric or asymmetric key."; } } grouping symmetric-key-grouping { description "This grouping is identical to the one in ietf-crypt-types except that it adds a couple case statements enabling the key value to be encrypted by a symmetric or an asymmetric key known to the keystore."; uses ct:symmetric-key-grouping { augment "key-type" { description "Augments a new 'case' statement into the 'choice' statement defined by the ietf-crypto-types module."; container encrypted-key { description "A container for the encrypted symmetric key value."; uses encrypted-value-grouping; } } } } grouping asymmetric-key-pair-grouping { description "This grouping is identical to the one in ietf-crypt-types except that it adds a couple case statements enabling the key value to be encrypted by a symmetric or an asymmetric key known to the keystore."; uses ct:asymmetric-key-pair-grouping { augment "private-key-type" { description "Augments a new 'case' statement into the 'choice' statement defined by the ietf-crypto-types module."; container encrypted-private-key { description "A container for the encrypted asymmetric private key value."; uses encrypted-value-grouping; } } } } grouping asymmetric-key-pair-with-cert-grouping { description "This grouping is identical to the one in ietf-crypt-types except that it adds a couple case statements enabling the key value to be encrypted by a symmetric or an asymmetric key known to the keystore."; uses ct:asymmetric-key-pair-with-cert-grouping { augment "private-key-type" { description "Augments a new 'case' statement into the 'choice' statement defined by the ietf-crypto-types module."; container encrypted-private-key { description "A container for the encrypted asymmetric private key value."; uses encrypted-value-grouping; } } } } grouping asymmetric-key-pair-with-certs-grouping { description "This grouping is identical to the one in ietf-crypt-types except that it adds a couple case statements enabling the key value to be encrypted by a symmetric or an asymmetric key known to the keystore."; uses ct:asymmetric-key-pair-with-certs-grouping { augment "private-key-type" { description "Augments a new 'case' statement into the 'choice' statement defined by the ietf-crypto-types module."; container encrypted-private-key { description "A container for the encrypted asymmetric private key value."; uses encrypted-value-grouping; } } } } grouping asymmetric-key-certificate-ref-grouping { leaf asymmetric-key { type ks:asymmetric-key-ref; must '../certificate'; description "A reference to an asymmetric key in the keystore."; } leaf certificate { 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."; } description "This grouping defines a reference to a specific certificate associated with an asymmetric key stored in the keystore."; } grouping local-or-keystore-asymmetric-key-grouping { description "A grouping that expands to allow the asymmetric key to be either stored locally, within the using data model, or be a reference to an asymmetric key stored in the keystore."; choice local-or-keystore { mandatory true; case local { if-feature "local-definitions-supported"; container local-definition { description "Container to hold the local key definition."; uses asymmetric-key-pair-grouping; } } case keystore { if-feature "keystore-supported"; leaf keystore-reference { type ks:asymmetric-key-ref; description "A reference to an asymmetric key that exists in the keystore. The intent is to reference just the asymmetric key, not any certificates that may also be associated with the asymmetric key."; } } description "A choice between an inlined definition and a definition that exists in the keystore."; } } grouping local-or-keystore-asymmetric-key-with-certs-grouping { description "A grouping that expands to allow an asymmetric key and its associated certificates to be either stored locally, within the using data model, or be a reference to an asymmetric key (and its associated certificates) stored in the keystore."; choice local-or-keystore { mandatory true; case local { if-feature "local-definitions-supported"; container local-definition { description "Container to hold the local key definition."; uses asymmetric-key-pair-with-certs-grouping; } } case keystore { if-feature "keystore-supported"; leaf keystore-reference { type ks:asymmetric-key-ref; description "A reference to an asymmetric-key (and all of its associated certificates) in the keystore."; } } description "A choice between an inlined definition and a definition that exists in the keystore."; } } grouping local-or-keystore-end-entity-cert-with-key-grouping { description "A grouping that expands to allow an end-entity certificate (and its associated private key) to be either stored locally, within the using data model, or be a reference to a specific certificate in the keystore."; choice local-or-keystore { mandatory true; case local { if-feature "local-definitions-supported"; container local-definition { description "Container to hold the local key definition."; uses asymmetric-key-pair-with-cert-grouping; } } case keystore { if-feature "keystore-supported"; container keystore-reference { uses asymmetric-key-certificate-ref-grouping; description "A reference to a specific certificate (and its associated private key) in the keystore."; } } description "A choice between an inlined definition and a definition that exists in the keystore."; } } grouping keystore-grouping { description "Grouping definition enables use in other contexts. If ever done, implementations SHOULD augment new 'case' statements into local-or-keystore 'choice' statements to supply leafrefs to the new location."; container 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 ks:asymmetric-key-pair-with-certs-grouping; } } container 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 ks:symmetric-key-grouping; } } } // grouping keystore-grouping /*********************************/ /* Protocol accessible nodes */ /*********************************/ container keystore { nacm:default-deny-write; description "The keystore contains a list of keys."; uses keystore-grouping; } rpc generate-symmetric-key { //nacm:default-deny-all; description "Requests the device to generate an symmetric key using the specified key algorithm, optionally encrypted using a key in the keystore. The output is this RPC can be used as input to a subsequent configuration request."; input { leaf algorithm { type ct:encryption-algorithm-t; mandatory true; description "The algorithm to be used when generating the key."; reference "RFC CCCC: Common YANG Data Types for Cryptography"; } container encrypt-with { presence "Indicates that the key should be encrypted using the specified symmetric or asymmetric key. If not specified, then the private key is not encrypted when returned."; description "A container for the 'key-type' choice."; uses key-reference-type-grouping; } } output { uses ks:symmetric-key-grouping; } } // end generate-symmetric-key rpc generate-asymmetric-key { //nacm:default-deny-all; description "Requests the device to generate an asymmetric key using the specified key algorithm, optionally encrypted using a key in the keystore. The output is this RPC can be used as input to a subsequent configuration request."; input { leaf algorithm { type ct:asymmetric-key-algorithm-t; mandatory true; description "The algorithm to be used when generating the key."; reference "RFC CCCC: Common YANG Data Types for Cryptography"; } container encrypt-with { presence "Indicates that the key should be encrypted using the specified symmetric or asymmetric key. If not specified, then the private key is not encrypted when returned."; description "A container for the 'key-type' choice."; uses key-reference-type-grouping; } } output { uses ks:asymmetric-key-pair-grouping; } } // end generate-asymmetric-key } netopeer2-1.1.70/modules/ietf-netconf-acm@2018-02-14.yang0000664000000000000000000003174314021433500020726 0ustar rootrootmodule ietf-netconf-acm { namespace "urn:ietf:params:xml:ns:yang:ietf-netconf-acm"; prefix nacm; import ietf-yang-types { prefix yang; } organization "IETF NETCONF (Network Configuration) Working Group"; contact "WG Web: <https://datatracker.ietf.org/wg/netconf/> WG List: <mailto:netconf@ietf.org> Author: Andy Bierman <mailto:andy@yumaworks.com> Author: Martin Bjorklund <mailto:mbj@tail-f.com>"; description "Network Configuration Access Control Model. Copyright (c) 2012 - 2018 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 (https://trustee.ietf.org/license-info). This version of this YANG module is part of RFC 8341; see the RFC itself for full legal notices."; revision "2018-02-14" { description "Added support for YANG 1.1 actions and notifications tied to data nodes. Clarified how NACM extensions can be used by other data models."; reference "RFC 8341: Network Configuration Access Control Model"; } revision "2012-02-22" { description "Initial version."; reference "RFC 6536: Network Configuration Protocol (NETCONF) Access Control Model"; } /* * Extension statements */ extension default-deny-write { description "Used to indicate that the data model node represents a sensitive security system parameter. If present, the NETCONF server will only allow the designated 'recovery session' to have write access to the node. An explicit access control rule is required for all other users. If the NACM module is used, then it must be enabled (i.e., /nacm/enable-nacm object equals 'true'), or this extension is ignored. The 'default-deny-write' extension MAY appear within a data definition statement. It is ignored otherwise."; } extension default-deny-all { description "Used to indicate that the data model node controls a very sensitive security system parameter. If present, the NETCONF server will only allow the designated 'recovery session' to have read, write, or execute access to the node. An explicit access control rule is required for all other users. If the NACM module is used, then it must be enabled (i.e., /nacm/enable-nacm object equals 'true'), or this extension is ignored. The 'default-deny-all' extension MAY appear within a data definition statement, 'rpc' statement, or 'notification' statement. It is ignored otherwise."; } /* * Derived types */ typedef user-name-type { type string { length "1..max"; } description "General-purpose username string."; } typedef matchall-string-type { type string { pattern '\*'; } description "The string containing a single asterisk '*' is used to conceptually represent all possible values for the particular leaf using this data type."; } typedef access-operations-type { type bits { bit create { description "Any protocol operation that creates a new data node."; } bit read { description "Any protocol operation or notification that returns the value of a data node."; } bit update { description "Any protocol operation that alters an existing data node."; } bit delete { description "Any protocol operation that removes a data node."; } bit exec { description "Execution access to the specified protocol operation."; } } description "Access operation."; } typedef group-name-type { type string { length "1..max"; pattern '[^\*].*'; } description "Name of administrative group to which users can be assigned."; } typedef action-type { type enumeration { enum permit { description "Requested action is permitted."; } enum deny { description "Requested action is denied."; } } description "Action taken by the server when a particular rule matches."; } typedef node-instance-identifier { type yang:xpath1.0; description "Path expression used to represent a special data node, action, or notification instance-identifier string. A node-instance-identifier value is an unrestricted YANG instance-identifier expression. All the same rules as an instance-identifier apply, except that predicates for keys are optional. If a key predicate is missing, then the node-instance-identifier represents all possible server instances for that key. This XML Path Language (XPath) expression is evaluated in the following context: o The set of namespace declarations are those in scope on the leaf element where this type is used. o The set of variable bindings contains one variable, 'USER', which contains the name of the user of the current session. o The function library is the core function library, but note that due to the syntax restrictions of an instance-identifier, no functions are allowed. o The context node is the root node in the data tree. The accessible tree includes actions and notifications tied to data nodes."; } /* * Data definition statements */ container nacm { nacm:default-deny-all; description "Parameters for NETCONF access control model."; leaf enable-nacm { type boolean; default "true"; description "Enables or disables all NETCONF access control enforcement. If 'true', then enforcement is enabled. If 'false', then enforcement is disabled."; } leaf read-default { type action-type; default "permit"; description "Controls whether read access is granted if no appropriate rule is found for a particular read request."; } leaf write-default { type action-type; default "deny"; description "Controls whether create, update, or delete access is granted if no appropriate rule is found for a particular write request."; } leaf exec-default { type action-type; default "permit"; description "Controls whether exec access is granted if no appropriate rule is found for a particular protocol operation request."; } leaf enable-external-groups { type boolean; default "true"; description "Controls whether the server uses the groups reported by the NETCONF transport layer when it assigns the user to a set of NACM groups. If this leaf has the value 'false', any group names reported by the transport layer are ignored by the server."; } leaf denied-operations { type yang:zero-based-counter32; config false; mandatory true; description "Number of times since the server last restarted that a protocol operation request was denied."; } leaf denied-data-writes { type yang:zero-based-counter32; config false; mandatory true; description "Number of times since the server last restarted that a protocol operation request to alter a configuration datastore was denied."; } leaf denied-notifications { type yang:zero-based-counter32; config false; mandatory true; description "Number of times since the server last restarted that a notification was dropped for a subscription because access to the event type was denied."; } container groups { description "NETCONF access control groups."; list group { key name; description "One NACM group entry. This list will only contain configured entries, not any entries learned from any transport protocols."; leaf name { type group-name-type; description "Group name associated with this entry."; } leaf-list user-name { type user-name-type; description "Each entry identifies the username of a member of the group associated with this entry."; } } } list rule-list { key name; ordered-by user; description "An ordered collection of access control rules."; leaf name { type string { length "1..max"; } description "Arbitrary name assigned to the rule-list."; } leaf-list group { type union { type matchall-string-type; type group-name-type; } description "List of administrative groups that will be assigned the associated access rights defined by the 'rule' list. The string '*' indicates that all groups apply to the entry."; } list rule { key name; ordered-by user; description "One access control rule. Rules are processed in user-defined order until a match is found. A rule matches if 'module-name', 'rule-type', and 'access-operations' match the request. If a rule matches, the 'action' leaf determines whether or not access is granted."; leaf name { type string { length "1..max"; } description "Arbitrary name assigned to the rule."; } leaf module-name { type union { type matchall-string-type; type string; } default "*"; description "Name of the module associated with this rule. This leaf matches if it has the value '*' or if the object being accessed is defined in the module with the specified module name."; } choice rule-type { description "This choice matches if all leafs present in the rule match the request. If no leafs are present, the choice matches all requests."; case protocol-operation { leaf rpc-name { type union { type matchall-string-type; type string; } description "This leaf matches if it has the value '*' or if its value equals the requested protocol operation name."; } } case notification { leaf notification-name { type union { type matchall-string-type; type string; } description "This leaf matches if it has the value '*' or if its value equals the requested notification name."; } } case data-node { leaf path { type node-instance-identifier; mandatory true; description "Data node instance-identifier associated with the data node, action, or notification controlled by this rule. Configuration data or state data instance-identifiers start with a top-level data node. A complete instance-identifier is required for this type of path value. The special value '/' refers to all possible datastore contents."; } } } leaf access-operations { type union { type matchall-string-type; type access-operations-type; } default "*"; description "Access operations associated with this rule. This leaf matches if it has the value '*' or if the bit corresponding to the requested operation is set."; } leaf action { type action-type; mandatory true; description "The access control action associated with the rule. If a rule has been determined to match a particular request, then this object is used to determine whether to permit or deny the request."; } leaf comment { type string; description "A textual description of the access rule."; } } } } } netopeer2-1.1.70/modules/ietf-netconf-monitoring@2010-10-04.yang0000664000000000000000000004170214021433500022335 0ustar rootrootmodule ietf-netconf-monitoring { namespace "urn:ietf:params:xml:ns:yang:ietf-netconf-monitoring"; prefix ncm; import ietf-yang-types { prefix yang; } import ietf-inet-types { prefix inet; } organization "IETF NETCONF (Network Configuration) Working Group"; contact "WG Web: <http://tools.ietf.org/wg/netconf/> WG List: <mailto:netconf@ietf.org> WG Chair: Mehmet Ersue <mailto:mehmet.ersue@nsn.com> WG Chair: Bert Wijnen <mailto:bertietf@bwijnen.net> Editor: Mark Scott <mailto:mark.scott@ericsson.com> Editor: Martin Bjorklund <mailto:mbj@tail-f.com>"; description "NETCONF Monitoring Module. All elements in this module are read-only. Copyright (c) 2010 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 6022; see the RFC itself for full legal notices."; revision 2010-10-04 { description "Initial revision."; reference "RFC 6022: YANG Module for NETCONF Monitoring"; } typedef netconf-datastore-type { type enumeration { enum "running"; enum "candidate"; enum "startup"; } description "Enumeration of possible NETCONF datastore types."; reference "RFC 4741: NETCONF Configuration Protocol"; } identity transport { description "Base identity for NETCONF transport types."; } identity netconf-ssh { base transport; description "NETCONF over Secure Shell (SSH)."; reference "RFC 4742: Using the NETCONF Configuration Protocol over Secure SHell (SSH)"; } identity netconf-soap-over-beep { base transport; description "NETCONF over Simple Object Access Protocol (SOAP) over Blocks Extensible Exchange Protocol (BEEP)."; reference "RFC 4743: Using NETCONF over the Simple Object Access Protocol (SOAP)"; } identity netconf-soap-over-https { base transport; description "NETCONF over Simple Object Access Protocol (SOAP) over Hypertext Transfer Protocol Secure (HTTPS)."; reference "RFC 4743: Using NETCONF over the Simple Object Access Protocol (SOAP)"; } identity netconf-beep { base transport; description "NETCONF over Blocks Extensible Exchange Protocol (BEEP)."; reference "RFC 4744: Using the NETCONF Protocol over the Blocks Extensible Exchange Protocol (BEEP)"; } identity netconf-tls { base transport; description "NETCONF over Transport Layer Security (TLS)."; reference "RFC 5539: NETCONF over Transport Layer Security (TLS)"; } identity schema-format { description "Base identity for data model schema languages."; } identity xsd { base schema-format; description "W3C XML Schema Definition."; reference "W3C REC REC-xmlschema-1-20041028: XML Schema Part 1: Structures"; } identity yang { base schema-format; description "The YANG data modeling language for NETCONF."; reference "RFC 6020: YANG - A Data Modeling Language for the Network Configuration Protocol (NETCONF)"; } identity yin { base schema-format; description "The YIN syntax for YANG."; reference "RFC 6020: YANG - A Data Modeling Language for the Network Configuration Protocol (NETCONF)"; } identity rng { base schema-format; description "Regular Language for XML Next Generation (RELAX NG)."; reference "ISO/IEC 19757-2:2008: RELAX NG"; } identity rnc { base schema-format; description "Relax NG Compact Syntax"; reference "ISO/IEC 19757-2:2008: RELAX NG"; } grouping common-counters { description "Counters that exist both per session, and also globally, accumulated from all sessions."; leaf in-rpcs { type yang:zero-based-counter32; description "Number of correct <rpc> messages received."; } leaf in-bad-rpcs { type yang:zero-based-counter32; description "Number of messages received when an <rpc> message was expected, that were not correct <rpc> messages. This includes XML parse errors and errors on the rpc layer."; } leaf out-rpc-errors { type yang:zero-based-counter32; description "Number of <rpc-reply> messages sent that contained an <rpc-error> element."; } leaf out-notifications { type yang:zero-based-counter32; description "Number of <notification> messages sent."; } } container netconf-state { config false; description "The netconf-state container is the root of the monitoring data model."; container capabilities { description "Contains the list of NETCONF capabilities supported by the server."; leaf-list capability { type inet:uri; description "List of NETCONF capabilities supported by the server."; } } container datastores { description "Contains the list of NETCONF configuration datastores."; list datastore { key "name"; description "List of NETCONF configuration datastores supported by the NETCONF server and related information."; leaf name { type netconf-datastore-type; description "Name of the datastore associated with this list entry."; } container locks { presence "This container is present only if the datastore is locked."; description "The NETCONF <lock> and <partial-lock> operations allow a client to lock specific resources in a datastore. The NETCONF server will prevent changes to the locked resources by all sessions except the one that acquired the lock(s). Monitoring information is provided for each datastore entry including details such as the session that acquired the lock, the type of lock (global or partial) and the list of locked resources. Multiple locks per datastore are supported."; grouping lock-info { description "Lock related parameters, common to both global and partial locks."; leaf locked-by-session { type uint32; mandatory true; description "The session ID of the session that has locked this resource. Both a global lock and a partial lock MUST contain the NETCONF session-id. If the lock is held by a session that is not managed by the NETCONF server (e.g., a CLI session), a session id of 0 (zero) is reported."; reference "RFC 4741: NETCONF Configuration Protocol"; } leaf locked-time { type yang:date-and-time; mandatory true; description "The date and time of when the resource was locked."; } } choice lock-type { description "Indicates if a global lock or a set of partial locks are set."; container global-lock { description "Present if the global lock is set."; uses lock-info; } list partial-lock { key "lock-id"; description "List of partial locks."; reference "RFC 5717: Partial Lock Remote Procedure Call (RPC) for NETCONF"; leaf lock-id { type uint32; description "This is the lock id returned in the <partial-lock> response."; } uses lock-info; leaf-list select { type yang:xpath1.0; min-elements 1; description "The xpath expression that was used to request the lock. The select expression indicates the original intended scope of the lock."; } leaf-list locked-node { type instance-identifier; description "The list of instance-identifiers (i.e., the locked nodes). The scope of the partial lock is defined by the list of locked nodes."; } } } } } } container schemas { description "Contains the list of data model schemas supported by the server."; list schema { key "identifier version format"; description "List of data model schemas supported by the server."; leaf identifier { type string; description "Identifier to uniquely reference the schema. The identifier is used in the <get-schema> operation and may be used for other purposes such as file retrieval. For modeling languages that support or require a data model name (e.g., YANG module name) the identifier MUST match that name. For YANG data models, the identifier is the name of the module or submodule. In other cases, an identifier such as a filename MAY be used instead."; } leaf version { type string; description "Version of the schema supported. Multiple versions MAY be supported simultaneously by a NETCONF server. Each version MUST be reported individually in the schema list, i.e., with same identifier, possibly different location, but different version. For YANG data models, version is the value of the most recent YANG 'revision' statement in the module or submodule, or the empty string if no 'revision' statement is present."; } leaf format { type identityref { base schema-format; } description "The data modeling language the schema is written in (currently xsd, yang, yin, rng, or rnc). For YANG data models, 'yang' format MUST be supported and 'yin' format MAY also be provided."; } leaf namespace { type inet:uri; mandatory true; description "The XML namespace defined by the data model. For YANG data models, this is the module's namespace. If the list entry describes a submodule, this field contains the namespace of the module to which the submodule belongs."; } leaf-list location { type union { type enumeration { enum "NETCONF"; } type inet:uri; } description "One or more locations from which the schema can be retrieved. This list SHOULD contain at least one entry per schema. A schema entry may be located on a remote file system (e.g., reference to file system for ftp retrieval) or retrieved directly from a server supporting the <get-schema> operation (denoted by the value 'NETCONF')."; } } } container sessions { description "The sessions container includes session-specific data for NETCONF management sessions. The session list MUST include all currently active NETCONF sessions."; list session { key "session-id"; description "All NETCONF sessions managed by the NETCONF server MUST be reported in this list."; leaf session-id { type uint32 { range "1..max"; } description "Unique identifier for the session. This value is the NETCONF session identifier, as defined in RFC 4741."; reference "RFC 4741: NETCONF Configuration Protocol"; } leaf transport { type identityref { base transport; } mandatory true; description "Identifies the transport for each session, e.g., 'netconf-ssh', 'netconf-soap', etc."; } leaf username { type string; mandatory true; description "The username is the client identity that was authenticated by the NETCONF transport protocol. The algorithm used to derive the username is NETCONF transport protocol specific and in addition specific to the authentication mechanism used by the NETCONF transport protocol."; } leaf source-host { type inet:host; description "Host identifier of the NETCONF client. The value returned is implementation specific (e.g., hostname, IPv4 address, IPv6 address)"; } leaf login-time { type yang:date-and-time; mandatory true; description "Time at the server at which the session was established."; } uses common-counters { description "Per-session counters. Zero based with following reset behaviour: - at start of a session - when max value is reached"; } } } container statistics { description "Statistical data pertaining to the NETCONF server."; leaf netconf-start-time { type yang:date-and-time; description "Date and time at which the management subsystem was started."; } leaf in-bad-hellos { type yang:zero-based-counter32; description "Number of sessions silently dropped because an invalid <hello> message was received. This includes <hello> messages with a 'session-id' attribute, bad namespace, and bad capability declarations."; } leaf in-sessions { type yang:zero-based-counter32; description "Number of sessions started. This counter is incremented when a <hello> message with a <session-id> is sent. 'in-sessions' - 'in-bad-hellos' = 'number of correctly started netconf sessions'"; } leaf dropped-sessions { type yang:zero-based-counter32; description "Number of sessions that were abnormally terminated, e.g., due to idle timeout or transport close. This counter is not incremented when a session is properly closed by a <close-session> operation, or killed by a <kill-session> operation."; } uses common-counters { description "Global counters, accumulated from all sessions. Zero based with following reset behaviour: - re-initialization of NETCONF server - when max value is reached"; } } } rpc get-schema { description "This operation is used to retrieve a schema from the NETCONF server. Positive Response: The NETCONF server returns the requested schema. Negative Response: If requested schema does not exist, the <error-tag> is 'invalid-value'. If more than one schema matches the requested parameters, the <error-tag> is 'operation-failed', and <error-app-tag> is 'data-not-unique'."; input { leaf identifier { type string; mandatory true; description "Identifier for the schema list entry."; } leaf version { type string; description "Version of the schema requested. If this parameter is not present, and more than one version of the schema exists on the server, a 'data-not-unique' error is returned, as described above."; } leaf format { type identityref { base schema-format; } description "The data modeling language of the schema. If this parameter is not present, and more than one formats of the schema exists on the server, a 'data-not-unique' error is returned, as described above."; } } output { anyxml data { description "Contains the schema content."; } } } } netopeer2-1.1.70/modules/ietf-netconf-nmda@2019-01-07.yang0000664000000000000000000003234014021433500021101 0ustar rootroot module ietf-netconf-nmda { yang-version 1.1; namespace "urn:ietf:params:xml:ns:yang:ietf-netconf-nmda"; prefix ncds; import ietf-yang-types { prefix yang; reference "RFC 6991: Common YANG Data Types"; } import ietf-inet-types { prefix inet; reference "RFC 6991: Common YANG Data Types"; } import ietf-datastores { prefix ds; reference "RFC 8342: Network Management Datastore Architecture (NMDA)"; } import ietf-origin { prefix or; reference "RFC 8342: Network Management Datastore Architecture (NMDA)"; } import ietf-netconf { prefix nc; reference "RFC 6241: Network Configuration Protocol (NETCONF)"; } import ietf-netconf-with-defaults { prefix ncwd; reference "RFC 6243: With-defaults Capability for NETCONF"; } organization "IETF NETCONF Working Group"; contact "WG Web: <https://datatracker.ietf.org/wg/netconf/> WG List: <mailto:netconf@ietf.org> Author: Martin Bjorklund <mailto:mbj@tail-f.com> Author: Juergen Schoenwaelder <mailto:j.schoenwaelder@jacobs-university.de> Author: Phil Shafer <mailto:phil@juniper.net> Author: Kent Watsen <mailto:kent+ietf@watsen.net> Author: Robert Wilton <mailto:rwilton@cisco.com>"; description "This YANG module defines a set of NETCONF operations to support the Network Management Datastore Architecture (NMDA). 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. Copyright (c) 2019 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 (https://trustee.ietf.org/license-info). This version of this YANG module is part of RFC 8526; see the RFC itself for full legal notices."; revision 2019-01-07 { description "Initial revision."; reference "RFC 8526: NETCONF Extensions to Support the Network Management Datastore Architecture"; } feature origin { description "Indicates that the server supports the 'origin' annotation."; reference "RFC 8342: Network Management Datastore Architecture (NMDA)"; } feature with-defaults { description "NETCONF :with-defaults capability. If the server advertises the :with-defaults capability for a session, then this feature must also be enabled for that session. Otherwise, this feature must not be enabled."; reference "RFC 6243: With-defaults Capability for NETCONF, Section 4; and RFC 8526: NETCONF Extensions to Support the Network Management Datastore Architecture, Section 3.1.1.2"; } rpc get-data { description "Retrieve data from an NMDA datastore. The content returned by get-data must satisfy all filters, i.e., the filter criteria are logically ANDed. Any ancestor nodes (including list keys) of nodes selected by the filters are included in the response. The 'with-origin' parameter is only valid for an operational datastore. If 'with-origin' is used with an invalid datastore, then the server MUST return an <rpc-error> element with an <error-tag> value of 'invalid-value'. The 'with-defaults' parameter only applies to the operational datastore if the NETCONF :with-defaults and :with-operational-defaults capabilities are both advertised. If the 'with-defaults' parameter is present in a request for which it is not supported, then the server MUST return an <rpc-error> element with an <error-tag> value of 'invalid-value'."; input { leaf datastore { type ds:datastore-ref; mandatory true; description "Datastore from which to retrieve data. If the datastore is not supported by the server, then the server MUST return an <rpc-error> element with an <error-tag> value of 'invalid-value'."; } choice filter-spec { description "The content filter specification for this request."; anydata subtree-filter { description "This parameter identifies the portions of the target datastore to retrieve."; reference "RFC 6241: Network Configuration Protocol (NETCONF), Section 6"; } leaf xpath-filter { if-feature "nc:xpath"; type yang:xpath1.0; description "This parameter contains an XPath expression identifying the portions of the target datastore to retrieve. If the expression returns a node-set, all nodes in the node-set are selected by the filter. Otherwise, if the expression does not return a node-set, then the <get-data> operation fails. The expression is evaluated in the following XPath context: o The set of namespace declarations are those in scope on the 'xpath-filter' leaf element. o The set of variable bindings is empty. o The function library is the core function library, and the XPath functions are defined in Section 10 of RFC 7950. o The context node is the root node of the target datastore."; } } leaf config-filter { type boolean; description "Filter for nodes with the given value for their 'config' property. When this leaf is set to 'true', only 'config true' nodes are selected, and when set to 'false', only 'config false' nodes are selected. If this leaf is not present, no nodes are filtered."; } choice origin-filters { when 'derived-from-or-self(datastore, "ds:operational")'; if-feature "origin"; description "Filters configuration nodes based on the 'origin' annotation. Configuration nodes that do not have an 'origin' annotation are treated as if they have the 'origin' annotation 'or:unknown'. System state nodes are not affected by origin-filters and thus not filtered. Note that system state nodes can be filtered with the 'config-filter' leaf."; leaf-list origin-filter { type or:origin-ref; description "Filter based on the 'origin' annotation. A configuration node matches the filter if its 'origin' annotation is derived from or equal to any of the given filter values."; } leaf-list negated-origin-filter { type or:origin-ref; description "Filter based on the 'origin' annotation. A configuration node matches the filter if its 'origin' annotation is neither derived from nor equal to any of the given filter values."; } } leaf max-depth { type union { type uint16 { range "1..65535"; } type enumeration { enum unbounded { description "All descendant nodes are included."; } } } default "unbounded"; description "For each node selected by the filters, this parameter selects how many conceptual subtree levels should be returned in the reply. If the depth is 1, the reply includes just the selected nodes but no children. If the depth is 'unbounded', all descendant nodes are included."; } leaf with-origin { when 'derived-from-or-self(../datastore, "ds:operational")'; if-feature "origin"; type empty; description "If this parameter is present, the server will return the 'origin' annotation for the nodes that have one."; } uses ncwd:with-defaults-parameters { if-feature "with-defaults"; } } output { anydata data { description "Copy of the source datastore subset that matched the filter criteria (if any). An empty data container indicates that the request did not produce any results."; } } } rpc edit-data { description "Edit data in an NMDA datastore. If an error condition occurs such that an error severity <rpc-error> element is generated, the server will stop processing the <edit-data> operation and restore the specified configuration to its complete state at the start of this <edit-data> operation."; input { leaf datastore { type ds:datastore-ref; mandatory true; description "Datastore that is the target of the <edit-data> operation. If the target datastore is not writable, or is not supported by the server, then the server MUST return an <rpc-error> element with an <error-tag> value of 'invalid-value'."; } leaf default-operation { type enumeration { enum merge { description "The default operation is merge."; } enum replace { description "The default operation is replace."; } enum none { description "There is no default operation."; } } default "merge"; description "The default operation to use."; } choice edit-content { mandatory true; description "The content for the edit operation."; anydata config { description "Inline config content."; } leaf url { if-feature "nc:url"; type inet:uri; description "URL-based config content."; } } } } /* * Augment the <lock> and <unlock> operations with a * "datastore" parameter. */ augment "/nc:lock/nc:input/nc:target/nc:config-target" { description "Add NMDA datastore as target."; leaf datastore { type ds:datastore-ref; description "Datastore to lock. The <lock> operation is only supported on writable datastores. If the <lock> operation is not supported by the server on the specified target datastore, then the server MUST return an <rpc-error> element with an <error-tag> value of 'invalid-value'."; } } augment "/nc:unlock/nc:input/nc:target/nc:config-target" { description "Add NMDA datastore as target."; leaf datastore { type ds:datastore-ref; description "Datastore to unlock. The <unlock> operation is only supported on writable datastores. If the <unlock> operation is not supported by the server on the specified target datastore, then the server MUST return an <rpc-error> element with an <error-tag> value of 'invalid-value'."; } } /* * Augment the <validate> operation with a * "datastore" parameter. */ augment "/nc:validate/nc:input/nc:source/nc:config-source" { description "Add NMDA datastore as source."; leaf datastore { type ds:datastore-ref; description "Datastore to validate. The <validate> operation is supported only on configuration datastores. If the <validate> operation is not supported by the server on the specified target datastore, then the server MUST return an <rpc-error> element with an <error-tag> value of 'invalid-value'."; } } } netopeer2-1.1.70/modules/ietf-netconf-server@2019-07-02.yang0000664000000000000000000005216414021433500021477 0ustar rootroot 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 AAAA: YANG Groupings for TCP Clients and TCP Servers"; } import ietf-tcp-server { prefix tcps; reference "RFC AAAA: YANG Groupings for TCP Clients and TCP Servers"; } import ietf-ssh-server { prefix sshs; revision-date 2019-07-02; // stable grouping definitions reference "RFC BBBB: YANG Groupings for SSH Clients and SSH Servers"; } import ietf-tls-server { prefix tlss; revision-date 2019-07-02; // stable grouping definitions reference "RFC CCCC: YANG Groupings for TLS Clients and TLS Servers"; } organization "IETF NETCONF (Network Configuration) Working Group"; contact "WG Web: <http://datatracker.ietf.org/wg/netconf/> WG List: <mailto:netconf@ietf.org> Author: Kent Watsen <mailto:kent+ietf@watsen.net> Author: Gary Wu <mailto:garywu@cisco.com> Author: Juergen Schoenwaelder <mailto:j.schoenwaelder@jacobs-university.de>"; description "This module contains a collection of YANG definitions for configuring NETCONF servers. Copyright (c) 2019 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 (https://trustee.ietf.org/license-info). This version of this YANG module is part of RFC XXXX (https://www.rfc-editor.org/info/rfcXXXX); 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 2019-07-02 { description "Initial version"; reference "RFC XXXX: 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"; } // Groupings grouping netconf-server-grouping { description "Top-level grouping for NETCONF server configuration."; container listen { if-feature "ssh-listen or tls-listen"; presence "Enables server to listen for NETCONF client connections."; description "Configures listen behavior"; leaf idle-timeout { type uint16; units "seconds"; default 3600; // one hour 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. Sessions that have a notification subscription active are never dropped."; } 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."; } choice transport { mandatory true; description "Selects between available transports."; case ssh { if-feature "ssh-listen"; container ssh { description "SSH-specific listening configuration for inbound connections."; container tcp-server-parameters { description "A wrapper around the TCP client parameters to avoid name collisions."; uses tcps:tcp-server-grouping { refine "local-port" { default "830"; description "The NETCONF server will listen on the IANA-assigned well-known port value for 'netconf-ssh' (830) if no value is specified."; } } } container ssh-server-parameters { description "A wrapper around the SSH server parameters to avoid name collisions."; uses sshs:ssh-server-grouping; } } } case tls { if-feature "tls-listen"; container tls { description "TLS-specific listening configuration for inbound connections."; container tcp-server-parameters { description "A wrapper around the TCP client parameters to avoid name collisions."; uses tcps:tcp-server-grouping { refine "local-port" { default "6513"; description "The NETCONF server will listen on the IANA-assigned well-known port value for 'netconf-tls' (6513) if no value is specified."; } } } container tls-server-parameters { description "A wrapper around the TLS server parameters to avoid name collisions."; uses tlss:tls-server-grouping { refine "client-authentication" { //must 'ca-certs or client-certs'; description "NETCONF/TLS servers MUST validate client certificates."; } augment "client-authentication" { description "Augments in the cert-to-name structure."; container cert-maps { uses x509c2n:cert-to-name; description "The cert-maps container is used by a TLS- based NETCONF server to map the NETCONF client's presented X.509 certificate to a NETCONF username. 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."; reference "RFC WWWW: NETCONF over TLS, Section 7"; } } } } } } } } } container call-home { if-feature "ssh-call-home or tls-call-home"; presence "Enables the NETCONF server to initiate the underlying transport connection to NETCONF clients."; description "Configures call home behavior."; list netconf-client { key "name"; min-elements 1; description "List of NETCONF clients the NETCONF server is to initiate call-home connections to in parallel."; 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."; } choice transport { mandatory true; description "Selects between available transports."; case ssh { if-feature "ssh-call-home"; container ssh { description "Specifies SSH-specific call-home transport configuration."; container tcp-client-parameters { description "A wrapper around the TCP client parameters to avoid name collisions."; uses tcpc:tcp-client-grouping { refine "remote-port" { default "4334"; description "The NETCONF server will attempt to connect to the IANA-assigned well-known port for 'netconf-ch-tls' (4334) if no value is specified."; } } } container ssh-server-parameters { description "A wrapper around the SSH server parameters to avoid name collisions."; uses sshs:ssh-server-grouping; } } } case tls { if-feature "tls-call-home"; container tls { description "Specifies TLS-specific call-home transport configuration."; container tcp-client-parameters { description "A wrapper around the TCP client parameters to avoid name collisions."; uses tcpc:tcp-client-grouping { refine "remote-port" { default "4335"; description "The NETCONF server will attempt to connect to the IANA-assigned well-known port for 'netconf-ch-tls' (4335) if no value is specified."; } } } container tls-server-parameters { description "A wrapper around the TLS server parameters to avoid name collisions."; uses tlss:tls-server-grouping { refine "client-authentication" { /* commented out since auth could be external must 'ca-certs or client-certs'; */ description "NETCONF/TLS servers MUST validate client certificates."; } augment "client-authentication" { description "Augments in the cert-to-name structure."; container cert-maps { uses x509c2n:cert-to-name; description "The cert-maps container is used by a TLS-based NETCONF server to map the NETCONF client's presented X.509 certificate to a NETCONF username. 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."; reference "RFC WWWW: NETCONF over TLS, Section 7"; } } } } } } // tls } // choice } // endpoint } // endpoints 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."; } // container persistent } // case persistent-connection 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 increases resource utilization, albeit with increased delay in NETCONF client to NETCONF client interactions. The NETCONF client SHOULD gracefully close the connection using <close-session> upon completing planned activities. If the NETCONF session is not closed gracefully, the NETCONF server MUST immediately attempt to reestablish the connection. 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 '\d{4}-\d{2}-\d{2}T\d{2}:\d{2}' + '(Z|[\+\-]\d{2}:\d{2})'; } 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. 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 120; // two 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 periodic } // 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-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-grouping // Protocol accessible node, for servers that implement this // module. container netconf-server { uses netconf-server-grouping; description "Top-level container for NETCONF server configuration."; } } netopeer2-1.1.70/modules/ietf-netconf@2013-09-29.yang0000664000000000000000000006454214021433500020203 0ustar rootrootmodule ietf-netconf { // the namespace for NETCONF XML definitions is unchanged // from RFC 4741, which this document replaces namespace "urn:ietf:params:xml:ns:netconf:base:1.0"; prefix nc; import ietf-inet-types { prefix inet; } import ietf-netconf-acm { prefix nacm; } organization "IETF NETCONF (Network Configuration) Working Group"; contact "WG Web: <http://tools.ietf.org/wg/netconf/> WG List: <netconf@ietf.org> WG Chair: Bert Wijnen <bertietf@bwijnen.net> WG Chair: Mehmet Ersue <mehmet.ersue@nsn.com> Editor: Martin Bjorklund <mbj@tail-f.com> Editor: Juergen Schoenwaelder <j.schoenwaelder@jacobs-university.de> Editor: Andy Bierman <andy.bierman@brocade.com>"; description "NETCONF Protocol Data Types and Protocol Operations. Copyright (c) 2011 IETF Trust and the persons identified as the document authors. 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 6241; see the RFC itself for full legal notices."; revision 2013-09-29 { description "Updated to include NACM attributes"; reference "RFC 6536: sec 3.2.5 and 3.2.8"; } revision 2011-06-01 { description "Initial revision"; reference "RFC 6241: Network Configuration Protocol"; } extension get-filter-element-attributes { description "If this extension is present within an 'anyxml' statement named 'filter', which must be conceptually defined within the RPC input section for the <get> and <get-config> protocol operations, then the following unqualified XML attribute is supported within the <filter> element, within a <get> or <get-config> protocol operation: type : optional attribute with allowed value strings 'subtree' and 'xpath'. If missing, the default value is 'subtree'. If the 'xpath' feature is supported, then the following unqualified XML attribute is also supported: select: optional attribute containing a string representing an XPath expression. The 'type' attribute must be equal to 'xpath' if this attribute is present."; } // NETCONF capabilities defined as features feature writable-running { description "NETCONF :writable-running capability; If the server advertises the :writable-running capability for a session, then this feature must also be enabled for that session. Otherwise, this feature must not be enabled."; reference "RFC 6241, Section 8.2"; } feature candidate { description "NETCONF :candidate capability; If the server advertises the :candidate capability for a session, then this feature must also be enabled for that session. Otherwise, this feature must not be enabled."; reference "RFC 6241, Section 8.3"; } feature confirmed-commit { if-feature candidate; description "NETCONF :confirmed-commit:1.1 capability; If the server advertises the :confirmed-commit:1.1 capability for a session, then this feature must also be enabled for that session. Otherwise, this feature must not be enabled."; reference "RFC 6241, Section 8.4"; } feature rollback-on-error { description "NETCONF :rollback-on-error capability; If the server advertises the :rollback-on-error capability for a session, then this feature must also be enabled for that session. Otherwise, this feature must not be enabled."; reference "RFC 6241, Section 8.5"; } feature validate { description "NETCONF :validate:1.1 capability; If the server advertises the :validate:1.1 capability for a session, then this feature must also be enabled for that session. Otherwise, this feature must not be enabled."; reference "RFC 6241, Section 8.6"; } feature startup { description "NETCONF :startup capability; If the server advertises the :startup capability for a session, then this feature must also be enabled for that session. Otherwise, this feature must not be enabled."; reference "RFC 6241, Section 8.7"; } feature url { description "NETCONF :url capability; If the server advertises the :url capability for a session, then this feature must also be enabled for that session. Otherwise, this feature must not be enabled."; reference "RFC 6241, Section 8.8"; } feature xpath { description "NETCONF :xpath capability; If the server advertises the :xpath capability for a session, then this feature must also be enabled for that session. Otherwise, this feature must not be enabled."; reference "RFC 6241, Section 8.9"; } // NETCONF Simple Types typedef session-id-type { type uint32 { range "1..max"; } description "NETCONF Session Id"; } typedef session-id-or-zero-type { type uint32; description "NETCONF Session Id or Zero to indicate none"; } typedef error-tag-type { type enumeration { enum in-use { description "The request requires a resource that already is in use."; } enum invalid-value { description "The request specifies an unacceptable value for one or more parameters."; } enum too-big { description "The request or response (that would be generated) is too large for the implementation to handle."; } enum missing-attribute { description "An expected attribute is missing."; } enum bad-attribute { description "An attribute value is not correct; e.g., wrong type, out of range, pattern mismatch."; } enum unknown-attribute { description "An unexpected attribute is present."; } enum missing-element { description "An expected element is missing."; } enum bad-element { description "An element value is not correct; e.g., wrong type, out of range, pattern mismatch."; } enum unknown-element { description "An unexpected element is present."; } enum unknown-namespace { description "An unexpected namespace is present."; } enum access-denied { description "Access to the requested protocol operation or data model is denied because authorization failed."; } enum lock-denied { description "Access to the requested lock is denied because the lock is currently held by another entity."; } enum resource-denied { description "Request could not be completed because of insufficient resources."; } enum rollback-failed { description "Request to roll back some configuration change (via rollback-on-error or <discard-changes> operations) was not completed for some reason."; } enum data-exists { description "Request could not be completed because the relevant data model content already exists. For example, a 'create' operation was attempted on data that already exists."; } enum data-missing { description "Request could not be completed because the relevant data model content does not exist. For example, a 'delete' operation was attempted on data that does not exist."; } enum operation-not-supported { description "Request could not be completed because the requested operation is not supported by this implementation."; } enum operation-failed { description "Request could not be completed because the requested operation failed for some reason not covered by any other error condition."; } enum partial-operation { description "This error-tag is obsolete, and SHOULD NOT be sent by servers conforming to this document."; } enum malformed-message { description "A message could not be handled because it failed to be parsed correctly. For example, the message is not well-formed XML or it uses an invalid character set."; } } description "NETCONF Error Tag"; reference "RFC 6241, Appendix A"; } typedef error-severity-type { type enumeration { enum error { description "Error severity"; } enum warning { description "Warning severity"; } } description "NETCONF Error Severity"; reference "RFC 6241, Section 4.3"; } typedef edit-operation-type { type enumeration { enum merge { description "The configuration data identified by the element containing this attribute is merged with the configuration at the corresponding level in the configuration datastore identified by the target parameter."; } enum replace { description "The configuration data identified by the element containing this attribute replaces any related configuration in the configuration datastore identified by the target parameter. If no such configuration data exists in the configuration datastore, it is created. Unlike a <copy-config> operation, which replaces the entire target configuration, only the configuration actually present in the config parameter is affected."; } enum create { description "The configuration data identified by the element containing this attribute is added to the configuration if and only if the configuration data does not already exist in the configuration datastore. If the configuration data exists, an <rpc-error> element is returned with an <error-tag> value of 'data-exists'."; } enum delete { description "The configuration data identified by the element containing this attribute is deleted from the configuration if and only if the configuration data currently exists in the configuration datastore. If the configuration data does not exist, an <rpc-error> element is returned with an <error-tag> value of 'data-missing'."; } enum remove { description "The configuration data identified by the element containing this attribute is deleted from the configuration if the configuration data currently exists in the configuration datastore. If the configuration data does not exist, the 'remove' operation is silently ignored by the server."; } } default "merge"; description "NETCONF 'operation' attribute values"; reference "RFC 6241, Section 7.2"; } // NETCONF Standard Protocol Operations rpc get-config { description "Retrieve all or part of a specified configuration."; reference "RFC 6241, Section 7.1"; input { container source { description "Particular configuration to retrieve."; choice config-source { mandatory true; description "The configuration to retrieve."; leaf candidate { if-feature candidate; type empty; description "The candidate configuration is the config source."; } leaf running { type empty; description "The running configuration is the config source."; } leaf startup { if-feature startup; type empty; description "The startup configuration is the config source. This is optional-to-implement on the server because not all servers will support filtering for this datastore."; } } } anyxml filter { description "Subtree or XPath filter to use."; nc:get-filter-element-attributes; } } output { anyxml data { description "Copy of the source datastore subset that matched the filter criteria (if any). An empty data container indicates that the request did not produce any results."; } } } rpc edit-config { description "The <edit-config> operation loads all or part of a specified configuration to the specified target configuration."; reference "RFC 6241, Section 7.2"; input { container target { description "Particular configuration to edit."; choice config-target { mandatory true; description "The configuration target."; leaf candidate { if-feature candidate; type empty; description "The candidate configuration is the config target."; } leaf running { if-feature writable-running; type empty; description "The running configuration is the config source."; } } } leaf default-operation { type enumeration { enum merge { description "The default operation is merge."; } enum replace { description "The default operation is replace."; } enum none { description "There is no default operation."; } } default "merge"; description "The default operation to use."; } leaf test-option { if-feature validate; type enumeration { enum test-then-set { description "The server will test and then set if no errors."; } enum set { description "The server will set without a test first."; } enum test-only { description "The server will only test and not set, even if there are no errors."; } } default "test-then-set"; description "The test option to use."; } leaf error-option { type enumeration { enum stop-on-error { description "The server will stop on errors."; } enum continue-on-error { description "The server may continue on errors."; } enum rollback-on-error { description "The server will roll back on errors. This value can only be used if the 'rollback-on-error' feature is supported."; } } default "stop-on-error"; description "The error option to use."; } choice edit-content { mandatory true; description "The content for the edit operation."; anyxml config { description "Inline Config content."; } leaf url { if-feature url; type inet:uri; description "URL-based config content."; } } } } rpc copy-config { description "Create or replace an entire configuration datastore with the contents of another complete configuration datastore."; reference "RFC 6241, Section 7.3"; input { container target { description "Particular configuration to copy to."; choice config-target { mandatory true; description "The configuration target of the copy operation."; leaf candidate { if-feature candidate; type empty; description "The candidate configuration is the config target."; } leaf running { if-feature writable-running; type empty; description "The running configuration is the config target. This is optional-to-implement on the server."; } leaf startup { if-feature startup; type empty; description "The startup configuration is the config target."; } leaf url { if-feature url; type inet:uri; description "The URL-based configuration is the config target."; } } } container source { description "Particular configuration to copy from."; choice config-source { mandatory true; description "The configuration source for the copy operation."; leaf candidate { if-feature candidate; type empty; description "The candidate configuration is the config source."; } leaf running { type empty; description "The running configuration is the config source."; } leaf startup { if-feature startup; type empty; description "The startup configuration is the config source."; } leaf url { if-feature url; type inet:uri; description "The URL-based configuration is the config source."; } anyxml config { description "Inline Config content: <config> element. Represents an entire configuration datastore, not a subset of the running datastore."; } } } } } rpc delete-config { nacm:default-deny-all; description "Delete a configuration datastore."; reference "RFC 6241, Section 7.4"; input { container target { description "Particular configuration to delete."; choice config-target { mandatory true; description "The configuration target to delete."; leaf startup { if-feature startup; type empty; description "The startup configuration is the config target."; } leaf url { if-feature url; type inet:uri; description "The URL-based configuration is the config target."; } } } } } rpc lock { description "The lock operation allows the client to lock the configuration system of a device."; reference "RFC 6241, Section 7.5"; input { container target { description "Particular configuration to lock."; choice config-target { mandatory true; description "The configuration target to lock."; leaf candidate { if-feature candidate; type empty; description "The candidate configuration is the config target."; } leaf running { type empty; description "The running configuration is the config target."; } leaf startup { if-feature startup; type empty; description "The startup configuration is the config target."; } } } } } rpc unlock { description "The unlock operation is used to release a configuration lock, previously obtained with the 'lock' operation."; reference "RFC 6241, Section 7.6"; input { container target { description "Particular configuration to unlock."; choice config-target { mandatory true; description "The configuration target to unlock."; leaf candidate { if-feature candidate; type empty; description "The candidate configuration is the config target."; } leaf running { type empty; description "The running configuration is the config target."; } leaf startup { if-feature startup; type empty; description "The startup configuration is the config target."; } } } } } rpc get { description "Retrieve running configuration and device state information."; reference "RFC 6241, Section 7.7"; input { anyxml filter { description "This parameter specifies the portion of the system configuration and state data to retrieve."; nc:get-filter-element-attributes; } } output { anyxml data { description "Copy of the running datastore subset and/or state data that matched the filter criteria (if any). An empty data container indicates that the request did not produce any results."; } } } rpc close-session { description "Request graceful termination of a NETCONF session."; reference "RFC 6241, Section 7.8"; } rpc kill-session { nacm:default-deny-all; description "Force the termination of a NETCONF session."; reference "RFC 6241, Section 7.9"; input { leaf session-id { type session-id-type; mandatory true; description "Particular session to kill."; } } } rpc commit { if-feature candidate; description "Commit the candidate configuration as the device's new current configuration."; reference "RFC 6241, Section 8.3.4.1"; input { leaf confirmed { if-feature confirmed-commit; type empty; description "Requests a confirmed commit."; reference "RFC 6241, Section 8.3.4.1"; } leaf confirm-timeout { if-feature confirmed-commit; type uint32 { range "1..max"; } units "seconds"; default "600"; // 10 minutes description "The timeout interval for a confirmed commit."; reference "RFC 6241, Section 8.3.4.1"; } leaf persist { if-feature confirmed-commit; type string; description "This parameter is used to make a confirmed commit persistent. A persistent confirmed commit is not aborted if the NETCONF session terminates. The only way to abort a persistent confirmed commit is to let the timer expire, or to use the <cancel-commit> operation. The value of this parameter is a token that must be given in the 'persist-id' parameter of <commit> or <cancel-commit> operations in order to confirm or cancel the persistent confirmed commit. The token should be a random string."; reference "RFC 6241, Section 8.3.4.1"; } leaf persist-id { if-feature confirmed-commit; type string; description "This parameter is given in order to commit a persistent confirmed commit. The value must be equal to the value given in the 'persist' parameter to the <commit> operation. If it does not match, the operation fails with an 'invalid-value' error."; reference "RFC 6241, Section 8.3.4.1"; } } } rpc discard-changes { if-feature candidate; description "Revert the candidate configuration to the current running configuration."; reference "RFC 6241, Section 8.3.4.2"; } rpc cancel-commit { if-feature confirmed-commit; description "This operation is used to cancel an ongoing confirmed commit. If the confirmed commit is persistent, the parameter 'persist-id' must be given, and it must match the value of the 'persist' parameter."; reference "RFC 6241, Section 8.4.4.1"; input { leaf persist-id { type string; description "This parameter is given in order to cancel a persistent confirmed commit. The value must be equal to the value given in the 'persist' parameter to the <commit> operation. If it does not match, the operation fails with an 'invalid-value' error."; } } } rpc validate { if-feature validate; description "Validates the contents of the specified configuration."; reference "RFC 6241, Section 8.6.4.1"; input { container source { description "Particular configuration to validate."; choice config-source { mandatory true; description "The configuration source to validate."; leaf candidate { if-feature candidate; type empty; description "The candidate configuration is the config source."; } leaf running { type empty; description "The running configuration is the config source."; } leaf startup { if-feature startup; type empty; description "The startup configuration is the config source."; } leaf url { if-feature url; type inet:uri; description "The URL-based configuration is the config source."; } anyxml config { description "Inline Config content: <config> element. Represents an entire configuration datastore, not a subset of the running datastore."; } } } } } } netopeer2-1.1.70/modules/ietf-ssh-common@2019-07-02.yang0000664000000000000000000003550714021433500020624 0ustar rootroot module ietf-ssh-common { yang-version 1.1; namespace "urn:ietf:params:xml:ns:yang:ietf-ssh-common"; prefix sshcmn; organization "IETF NETCONF (Network Configuration) Working Group"; contact "WG Web: <http://datatracker.ietf.org/wg/netconf/> WG List: <mailto:netconf@ietf.org> Author: Kent Watsen <mailto:kent+ietf@watsen.net> Author: Gary Wu <mailto:garywu@cisco.com>"; description "This module defines a common features, identities, and groupings for Secure Shell (SSH). Copyright (c) 2019 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 (https://trustee.ietf.org/license-info). This version of this YANG module is part of RFC XXXX (https://www.rfc-editor.org/info/rfcXXXX); 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 2019-07-02 { description "Initial version"; reference "RFC XXXX: YANG Groupings for SSH Clients and SSH Servers"; } // Features feature ssh-ecc { description "Elliptic Curve Cryptography is supported for SSH."; reference "RFC 5656: Elliptic Curve Algorithm Integration in the Secure Shell Transport Layer"; } feature ssh-x509-certs { description "X.509v3 certificates are supported for SSH per RFC 6187."; reference "RFC 6187: X.509v3 Certificates for Secure Shell Authentication"; } feature ssh-dh-group-exchange { description "Diffie-Hellman Group Exchange is supported for SSH."; reference "RFC 4419: Diffie-Hellman Group Exchange for the Secure Shell (SSH) Transport Layer Protocol"; } feature ssh-ctr { description "SDCTR encryption mode is supported for SSH."; reference "RFC 4344: The Secure Shell (SSH) Transport Layer Encryption Modes"; } feature ssh-sha2 { description "The SHA2 family of cryptographic hash functions is supported for SSH."; reference "FIPS PUB 180-4: Secure Hash Standard (SHS)"; } // Identities identity public-key-alg-base { description "Base identity used to identify public key algorithms."; } identity ssh-dss { base public-key-alg-base; description "Digital Signature Algorithm using SHA-1 as the hashing algorithm."; reference "RFC 4253: The Secure Shell (SSH) Transport Layer Protocol"; } identity ssh-rsa { base public-key-alg-base; description "RSASSA-PKCS1-v1_5 signature scheme using SHA-1 as the hashing algorithm."; reference "RFC 4253: The Secure Shell (SSH) Transport Layer Protocol"; } identity ecdsa-sha2-nistp256 { base public-key-alg-base; if-feature "ssh-ecc and ssh-sha2"; description "Elliptic Curve Digital Signature Algorithm (ECDSA) using the nistp256 curve and the SHA2 family of hashing algorithms."; reference "RFC 5656: Elliptic Curve Algorithm Integration in the Secure Shell Transport Layer"; } identity ecdsa-sha2-nistp384 { base public-key-alg-base; if-feature "ssh-ecc and ssh-sha2"; description "Elliptic Curve Digital Signature Algorithm (ECDSA) using the nistp384 curve and the SHA2 family of hashing algorithms."; reference "RFC 5656: Elliptic Curve Algorithm Integration in the Secure Shell Transport Layer"; } identity ecdsa-sha2-nistp521 { base public-key-alg-base; if-feature "ssh-ecc and ssh-sha2"; description "Elliptic Curve Digital Signature Algorithm (ECDSA) using the nistp521 curve and the SHA2 family of hashing algorithms."; reference "RFC 5656: Elliptic Curve Algorithm Integration in the Secure Shell Transport Layer"; } identity x509v3-ssh-rsa { base public-key-alg-base; if-feature "ssh-x509-certs"; description "RSASSA-PKCS1-v1_5 signature scheme using a public key stored in an X.509v3 certificate and using SHA-1 as the hashing algorithm."; reference "RFC 6187: X.509v3 Certificates for Secure Shell Authentication"; } identity x509v3-rsa2048-sha256 { base public-key-alg-base; if-feature "ssh-x509-certs and ssh-sha2"; description "RSASSA-PKCS1-v1_5 signature scheme using a public key stored in an X.509v3 certificate and using SHA-256 as the hashing algorithm. RSA keys conveyed using this format MUST have a modulus of at least 2048 bits."; reference "RFC 6187: X.509v3 Certificates for Secure Shell Authentication"; } identity x509v3-ecdsa-sha2-nistp256 { base public-key-alg-base; if-feature "ssh-ecc and ssh-x509-certs and ssh-sha2"; description "Elliptic Curve Digital Signature Algorithm (ECDSA) using the nistp256 curve with a public key stored in an X.509v3 certificate and using the SHA2 family of hashing algorithms."; reference "RFC 6187: X.509v3 Certificates for Secure Shell Authentication"; } identity x509v3-ecdsa-sha2-nistp384 { base public-key-alg-base; if-feature "ssh-ecc and ssh-x509-certs and ssh-sha2"; description "Elliptic Curve Digital Signature Algorithm (ECDSA) using the nistp384 curve with a public key stored in an X.509v3 certificate and using the SHA2 family of hashing algorithms."; reference "RFC 6187: X.509v3 Certificates for Secure Shell Authentication"; } identity x509v3-ecdsa-sha2-nistp521 { base public-key-alg-base; if-feature "ssh-ecc and ssh-x509-certs and ssh-sha2"; description "Elliptic Curve Digital Signature Algorithm (ECDSA) using the nistp521 curve with a public key stored in an X.509v3 certificate and using the SHA2 family of hashing algorithms."; reference "RFC 6187: X.509v3 Certificates for Secure Shell Authentication"; } identity key-exchange-alg-base { description "Base identity used to identify key exchange algorithms."; } identity diffie-hellman-group14-sha1 { base key-exchange-alg-base; description "Diffie-Hellman key exchange with SHA-1 as HASH and Oakley Group 14 (2048-bit MODP Group)."; reference "RFC 4253: The Secure Shell (SSH) Transport Layer Protocol"; } identity diffie-hellman-group-exchange-sha1 { base key-exchange-alg-base; if-feature "ssh-dh-group-exchange"; description "Diffie-Hellman Group and Key Exchange with SHA-1 as HASH."; 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; if-feature "ssh-dh-group-exchange and ssh-sha2"; description "Diffie-Hellman Group and Key Exchange with SHA-256 as HASH."; reference "RFC 4419: Diffie-Hellman Group Exchange for the Secure Shell (SSH) Transport Layer Protocol"; } identity ecdh-sha2-nistp256 { base key-exchange-alg-base; if-feature "ssh-ecc and ssh-sha2"; description "Elliptic Curve Diffie-Hellman (ECDH) key exchange using the nistp256 curve and the SHA2 family of hashing algorithms."; reference "RFC 5656: Elliptic Curve Algorithm Integration in the Secure Shell Transport Layer"; } identity ecdh-sha2-nistp384 { base key-exchange-alg-base; if-feature "ssh-ecc and ssh-sha2"; description "Elliptic Curve Diffie-Hellman (ECDH) key exchange using the nistp384 curve and the SHA2 family of hashing algorithms."; reference "RFC 5656: Elliptic Curve Algorithm Integration in the Secure Shell Transport Layer"; } identity ecdh-sha2-nistp521 { base key-exchange-alg-base; if-feature "ssh-ecc and ssh-sha2"; description "Elliptic Curve Diffie-Hellman (ECDH) key exchange using the nistp521 curve and the SHA2 family of hashing algorithms."; reference "RFC 5656: Elliptic Curve Algorithm Integration in the Secure Shell Transport Layer"; } identity encryption-alg-base { description "Base identity used to identify encryption algorithms."; } identity triple-des-cbc { base encryption-alg-base; description "Three-key 3DES in CBC mode."; reference "RFC 4253: The Secure Shell (SSH) Transport Layer Protocol"; } identity aes128-cbc { base encryption-alg-base; description "AES in CBC mode, with a 128-bit key."; reference "RFC 4253: The Secure Shell (SSH) Transport Layer Protocol"; } identity aes192-cbc { base encryption-alg-base; description "AES in CBC mode, with a 192-bit key."; reference "RFC 4253: The Secure Shell (SSH) Transport Layer Protocol"; } identity aes256-cbc { base encryption-alg-base; description "AES in CBC mode, with a 256-bit key."; reference "RFC 4253: The Secure Shell (SSH) Transport Layer Protocol"; } identity aes128-ctr { base encryption-alg-base; if-feature "ssh-ctr"; description "AES in SDCTR mode, with 128-bit key."; reference "RFC 4344: The Secure Shell (SSH) Transport Layer Encryption Modes"; } identity aes192-ctr { base encryption-alg-base; if-feature "ssh-ctr"; description "AES in SDCTR mode, with 192-bit key."; reference "RFC 4344: The Secure Shell (SSH) Transport Layer Encryption Modes"; } identity aes256-ctr { base encryption-alg-base; if-feature "ssh-ctr"; description "AES in SDCTR mode, with 256-bit key."; reference "RFC 4344: The Secure Shell (SSH) Transport Layer Encryption Modes"; } 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-sha2-256 { base mac-alg-base; if-feature "ssh-sha2"; 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; if-feature "ssh-sha2"; description "HMAC-SHA2-512"; reference "RFC 6668: SHA-2 Data Integrity Verification for the Secure Shell (SSH) Transport Layer Protocol"; } // 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 public-key-alg-base; } ordered-by user; description "Acceptable host key algorithms 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 XXXX for valid combinations. If this leaf-list is not configured (has zero elements) the acceptable host key algorithms are implementation- defined."; reference "RFC XXXX: YANG Groupings for SSH Clients and SSH Servers"; } } container key-exchange { description "Parameters regarding key exchange."; leaf-list key-exchange-alg { type identityref { base key-exchange-alg-base; } ordered-by user; description "Acceptable key exchange algorithms in order of descending 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 encryption-alg-base; } ordered-by user; description "Acceptable encryption algorithms in order of descending 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 mac-alg-base; } ordered-by user; description "Acceptable MAC algorithms in order of descending preference. If this leaf-list is not configured (has zero elements) the acceptable MAC algorithms are implementation- defined."; } } } } netopeer2-1.1.70/modules/ietf-ssh-server@2019-07-02.yang0000664000000000000000000003432414021433500020636 0ustar rootroot module ietf-ssh-server { yang-version 1.1; namespace "urn:ietf:params:xml:ns:yang:ietf-ssh-server"; prefix sshs; import ietf-ssh-common { prefix sshcmn; revision-date 2019-07-02; // stable grouping definitions reference "RFC XXXX: YANG Groupings for SSH Clients and SSH Servers"; } /* import ietf-truststore { prefix ta; reference "RFC YYYY: A YANG Data Model for a Truststore"; } */ import ietf-keystore { prefix ks; reference "RFC ZZZZ: A YANG Data Model for a Keystore"; } 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"; } organization "IETF NETCONF (Network Configuration) Working Group"; contact "WG Web: <http://datatracker.ietf.org/wg/netconf/> WG List: <mailto:netconf@ietf.org> Author: Kent Watsen <mailto:kent+ietf@watsen.net> Author: Gary Wu <mailto:garywu@cisco.com>"; description "This module defines reusable groupings for SSH servers that can be used as a basis for specific SSH server instances. Copyright (c) 2019 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 (https://trustee.ietf.org/license-info). This version of this YANG module is part of RFC XXXX (https://www.rfc-editor.org/info/rfcXXXX); 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 2019-07-02 { description "Initial version"; reference "RFC XXXX: YANG Groupings for SSH Clients and SSH Servers"; } // Features feature ssh-server-transport-params-config { description "SSH transport layer parameters are configurable on an SSH server."; } feature ssh-server-keepalives { description "Per socket SSH keepalive parameters are configurable for SSH servers on the server implementing this feature."; } feature local-client-auth-supported { description "Indicates that the SSH server supports local configuration of client credentials."; } feature external-client-auth-supported { description "Indicates that the SSH server supports external configuration of client credentials."; } // 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 descendent 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 '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 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 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 { uses ks:local-or-keystore-asymmetric-key-grouping; description "A locally-defined or referenced asymmetric key pair to be used for the SSH server's host key."; reference "RFC ZZZZ: YANG Data Model for a Centralized Keystore Mechanism"; } container certificate { if-feature "sshcmn:ssh-x509-certs"; uses ks:local-or-keystore-end-entity-cert-with-key-grouping; description "A locally-defined or referenced end-entity certificate to be used for the SSH server's host key."; reference "RFC ZZZZ: YANG Data Model for a Centralized Keystore Mechanism"; } } } } // container server-identity container client-authentication { nacm:default-deny-write; description "Specifies if SSH client authentication is required or optional, and specifies if the SSH client authentication credentials are configured locally or externally."; container supported-authentication-methods { description "Indicates which authentication methods the server supports."; leaf publickey { type empty; description "Indicates that the 'publickey' method is supported. Note that RFC 6187 X.509v3 Certificates for SSH uses the 'publickey' method name."; reference "RFC 4252: The Secure Shell (SSH) Authentication Protocol. RFC 6187: X.509v3 Certificates for Secure Shell Authentication."; } leaf passsword { type empty; description "Indicates that the 'password' method is supported."; reference "RFC 4252: The Secure Shell (SSH) Authentication Protocol."; } leaf hostbased { type empty; description "Indicates that the 'hostbased' method is supported."; reference "RFC 4252: The Secure Shell (SSH) Authentication Protocol."; } leaf none { type empty; description "Indicates that the 'none' method is supported."; reference "RFC 4252: The Secure Shell (SSH) Authentication Protocol."; } leaf-list other { type string; description "Indicates a supported method name not defined by RFC 4253."; reference "RFC 4252: The Secure Shell (SSH) Authentication Protocol."; } } choice local-or-external { mandatory true; description "Indicates if the client credentials are configured locally or externally."; case local { if-feature "local-client-auth-supported"; description "Client credentials are configured locally."; container users { description "A list of locally configured users."; list user { key name; description "The list of local users configured on this device."; leaf name { type string; description "The user name string identifying this entry."; } leaf password { type ianach:crypt-hash; description "The password for this entry."; } list authorized-key { key name; description "A list of public SSH keys for this user. These keys are allowed for SSH authentication, as described in RFC 4253."; reference "RFC 4253: The Secure Shell (SSH) Transport Layer Protocol"; leaf name { type string; description "An arbitrary name for the SSH key."; } leaf algorithm { type string; mandatory true; description "The public key algorithm name for this SSH key. Valid values are the values in the IANA 'Secure Shell (SSH) Protocol Parameters' registry, Public Key Algorithm Names."; reference "IANA 'Secure Shell (SSH) Protocol Parameters' registry, Public Key Algorithm Names"; } leaf key-data { type binary; mandatory true; description "The binary public key data for this SSH 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"; } } } // list user /* if-feature "sshcmn:ssh-x509-certs"; description "A reference to a list of certificate authority (CA) certificates and a reference to a list of client certificates."; leaf ca-certs { if-feature "ts:x509-certificates"; type ts:certificates-ref; // local or remote description "A reference to a list 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 YYYY: YANG Data Model for Global Trust Anchors"; } leaf client-certs { if-feature "ts:x509-certificates"; type ts:certificates-ref; // local or remote description "A reference to a list of client certificates used by the SSH server to authenticate SSH client certificates. A clients certificate is authenticated if it is an exact match to a configured client certificate."; reference "RFC YYYY: YANG Data Model for Global Trust Anchors"; } */ } // container users } // case local case external { if-feature "external-client-auth-supported"; description "Client credentials are configured externally, such as via RADIUS, RFC 7317, or another mechanism."; leaf client-auth-defined-elsewhere { type empty; description "Indicates that client credentials are configured elsewhere."; } } } // choice local-or-external } // container client-authentication container transport-params { nacm:default-deny-write; if-feature "ssh-server-transport-params-config"; 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 keepalives are enabled."; description "Configures the keep-alive policy, to proactively test the aliveness of the SSL client. An unresponsive SSL 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 SSL client, a SSL-level message will be sent to test the aliveness of the SSL 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 SSL client before assuming the SSL client is no longer alive."; } } // container keepalives } // grouping server-identity-grouping } netopeer2-1.1.70/modules/ietf-tcp-client@2019-07-02.yang0000664000000000000000000001254014021433500020573 0ustar rootroot 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-tcp-common { prefix tcpcmn; reference "RFC XXXX: 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: <http://datatracker.ietf.org/wg/netconf/> <http://datatracker.ietf.org/wg/tcpm/> WG List: <mailto:netconf@ietf.org> <mailto:tcpm@ietf.org> Authors: Kent Watsen <mailto:kent+ietf@watsen.net> Michael Scharf <mailto:michael.scharf@hs-esslingen.de>"; description "This module defines reusable groupings for TCP clients that can be used as a basis for specific TCP client instances. Copyright (c) 2019 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 (https://trustee.ietf.org/license-info). This version of this YANG module is part of RFC XXXX (https://www.rfc-editor.org/info/rfcXXXX); 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 2019-07-02 { description "Initial version"; reference "RFC XXXX: 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."; } // Groupings grouping tcp-client-grouping { description "A reusable grouping for configuring a TCP client. Note that this grouping uses fairly typical descendent 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 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 (0) is used (instead of 'mandatory true') so that as application level data model may 'refine' it with an application specific default port number value."; } leaf local-address { if-feature "local-binding-supported"; type inet:ip-address; description "The local IP address/interface (VRF?) 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."; } uses tcpcmn:tcp-connection-grouping { augment "keepalives" { if-feature "tcp-client-keepalives"; description "Add an if-feature statement so that implementations can choose to support TCP client keepalives."; } } } } netopeer2-1.1.70/modules/ietf-tcp-common@2019-07-02.yang0000664000000000000000000001062114021433500020603 0ustar rootroot 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: <http://datatracker.ietf.org/wg/netconf/> <http://datatracker.ietf.org/wg/tcpm/> WG List: <mailto:netconf@ietf.org> <mailto:tcpm@ietf.org> Authors: Kent Watsen <mailto:kent+ietf@watsen.net> Michael Scharf <mailto:michael.scharf@hs-esslingen.de>"; description "This module defines reusable groupings for TCP commons that can be used as a basis for specific TCP common instances. Copyright (c) 2019 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 (https://trustee.ietf.org/license-info). This version of this YANG module is part of RFC XXXX (https://www.rfc-editor.org/info/rfcXXXX); 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 2019-07-02 { description "Initial version"; reference "RFC XXXX: YANG Groupings for TCP Clients and TCP Servers"; } // Features feature keepalives-supported { description "Indicates that keepalives are supported."; } // Groupings grouping tcp-common-grouping { description "A reusable grouping for configuring TCP parameters common to TCP connections as well as the operating system as a whole."; container keepalives { if-feature "keepalives-supported"; presence "Indicates that keepalives are enabled."; 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 * 60) + (max-probes * probe-interval) seconds."; leaf idle-time { type uint16 { range "1..max"; } units "seconds"; mandatory true; description "Sets the amount of time after which if no data has been received from the TCP peer, a TCP-level probe message will be sent to test the aliveness of the TCP peer."; } leaf max-probes { type uint16 { range "1..max"; } mandatory true; description "Sets the maximum number of sequential keep-alive probes that can fail to obtain a response from the TCP peer before assuming the TCP peer is no longer alive."; } leaf probe-interval { type uint16 { range "1..max"; } units "seconds"; mandatory true; description "Sets the time interval between failed probes."; } } // container keepalives } // grouping tcp-common-grouping grouping tcp-connection-grouping { description "A reusable grouping for configuring TCP parameters common to TCP connections."; uses tcp-common-grouping; } /* The following is for a future bis... This comment is here now so as support discussion with TCPM. This comment will be removed before publication. Should future system-level parameters be defined as a grouping or a container? grouping tcp-system-grouping { description "A reusable grouping for configuring TCP parameters common to the operating system as a whole."; // currently just a placeholder } */ } netopeer2-1.1.70/modules/ietf-tcp-server@2019-07-02.yang0000664000000000000000000001013514021433500020621 0ustar rootroot 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 XXXX: 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: <http://datatracker.ietf.org/wg/netconf/> <http://datatracker.ietf.org/wg/tcpm/> WG List: <mailto:netconf@ietf.org> <mailto:tcpm@ietf.org> Authors: Kent Watsen <mailto:kent+ietf@watsen.net> Michael Scharf <mailto:michael.scharf@hs-esslingen.de>"; description "This module defines reusable groupings for TCP servers that can be used as a basis for specific TCP server instances. Copyright (c) 2019 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 (https://trustee.ietf.org/license-info). This version of this YANG module is part of RFC XXXX (https://www.rfc-editor.org/info/rfcXXXX); 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 2019-07-02 { description "Initial version"; reference "RFC XXXX: 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."; } // Groupings grouping tcp-server-grouping { description "A reusable grouping for configuring a TCP server. Note that this grouping uses fairly typical descendent 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-connection-grouping { augment "keepalives" { if-feature "tcp-server-keepalives"; description "Add an if-feature statement so that implementations can choose to support TCP server keepalives."; } } } } netopeer2-1.1.70/modules/ietf-tls-common@2019-07-02.yang0000664000000000000000000003134314021433500020623 0ustar rootroot module ietf-tls-common { yang-version 1.1; namespace "urn:ietf:params:xml:ns:yang:ietf-tls-common"; prefix tlscmn; organization "IETF NETCONF (Network Configuration) Working Group"; contact "WG Web: <http://datatracker.ietf.org/wg/netconf/> WG List: <mailto:netconf@ietf.org> Author: Kent Watsen <mailto:kent+ietf@watsen.net> Author: Gary Wu <mailto:garywu@cisco.com>"; description "This module defines a common features, identities, and groupings for Transport Layer Security (TLS). Copyright (c) 2019 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 (https://trustee.ietf.org/license-info). This version of this YANG module is part of RFC XXXX (https://www.rfc-editor.org/info/rfcXXXX); 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 2019-07-02 { description "Initial version"; reference "RFC XXXX: YANG Groupings for TLS Clients and TLS Servers"; } // Features feature tls-1_0 { description "TLS Protocol Version 1.0 is supported."; reference "RFC 2246: The TLS Protocol Version 1.0"; } feature tls-1_1 { description "TLS Protocol Version 1.1 is supported."; reference "RFC 4346: The Transport Layer Security (TLS) Protocol Version 1.1"; } feature tls-1_2 { description "TLS Protocol Version 1.2 is supported."; reference "RFC 5246: The Transport Layer Security (TLS) Protocol Version 1.2"; } feature tls-1_3 { description "TLS Protocol Version 1.2 is supported."; reference "RFC 8446: The Transport Layer Security (TLS) Protocol Version 1.3"; } feature tls-ecc { description "Elliptic Curve Cryptography (ECC) is supported for TLS."; reference "RFC 8422: Elliptic Curve Cryptography (ECC) Cipher Suites for Transport Layer Security (TLS)"; } feature tls-dhe { description "Ephemeral Diffie-Hellman key exchange is supported for TLS."; reference "RFC 5246: The Transport Layer Security (TLS) Protocol Version 1.2"; } feature tls-3des { description "The Triple-DES block cipher is supported for TLS."; reference "RFC 5246: The Transport Layer Security (TLS) Protocol Version 1.2"; } feature tls-gcm { description "The Galois/Counter Mode authenticated encryption mode is supported for TLS."; reference "RFC 5288: AES Galois Counter Mode (GCM) Cipher Suites for TLS"; } feature tls-sha2 { description "The SHA2 family of cryptographic hash functions is supported for TLS."; reference "FIPS PUB 180-4: Secure Hash Standard (SHS)"; } // Identities identity tls-version-base { description "Base identity used to identify TLS protocol versions."; } identity tls-1.0 { base tls-version-base; if-feature "tls-1_0"; description "TLS Protocol Version 1.0."; reference "RFC 2246: The TLS Protocol Version 1.0"; } identity tls-1.1 { base tls-version-base; if-feature "tls-1_1"; description "TLS Protocol Version 1.1."; reference "RFC 4346: The Transport Layer Security (TLS) Protocol Version 1.1"; } identity tls-1.2 { base tls-version-base; if-feature "tls-1_2"; description "TLS Protocol Version 1.2."; reference "RFC 5246: The Transport Layer Security (TLS) Protocol Version 1.2"; } identity cipher-suite-base { description "Base identity used to identify TLS cipher suites."; } identity rsa-with-aes-128-cbc-sha { base cipher-suite-base; description "Cipher suite TLS_RSA_WITH_AES_128_CBC_SHA."; reference "RFC 5246: The Transport Layer Security (TLS) Protocol Version 1.2"; } identity rsa-with-aes-256-cbc-sha { base cipher-suite-base; description "Cipher suite TLS_RSA_WITH_AES_256_CBC_SHA."; reference "RFC 5246: The Transport Layer Security (TLS) Protocol Version 1.2"; } identity rsa-with-aes-128-cbc-sha256 { base cipher-suite-base; if-feature "tls-sha2"; description "Cipher suite TLS_RSA_WITH_AES_128_CBC_SHA256."; reference "RFC 5246: The Transport Layer Security (TLS) Protocol Version 1.2"; } identity rsa-with-aes-256-cbc-sha256 { base cipher-suite-base; if-feature "tls-sha2"; description "Cipher suite TLS_RSA_WITH_AES_256_CBC_SHA256."; reference "RFC 5246: The Transport Layer Security (TLS) Protocol Version 1.2"; } identity dhe-rsa-with-aes-128-cbc-sha { base cipher-suite-base; if-feature "tls-dhe"; description "Cipher suite TLS_DHE_RSA_WITH_AES_128_CBC_SHA."; reference "RFC 5246: The Transport Layer Security (TLS) Protocol Version 1.2"; } identity dhe-rsa-with-aes-256-cbc-sha { base cipher-suite-base; if-feature "tls-dhe"; description "Cipher suite TLS_DHE_RSA_WITH_AES_256_CBC_SHA."; reference "RFC 5246: The Transport Layer Security (TLS) Protocol Version 1.2"; } identity dhe-rsa-with-aes-128-cbc-sha256 { base cipher-suite-base; if-feature "tls-dhe and tls-sha2"; description "Cipher suite TLS_DHE_RSA_WITH_AES_128_CBC_SHA256."; reference "RFC 5246: The Transport Layer Security (TLS) Protocol Version 1.2"; } identity dhe-rsa-with-aes-256-cbc-sha256 { base cipher-suite-base; if-feature "tls-dhe and tls-sha2"; description "Cipher suite TLS_DHE_RSA_WITH_AES_256_CBC_SHA256."; reference "RFC 5246: The Transport Layer Security (TLS) Protocol Version 1.2"; } identity ecdhe-ecdsa-with-aes-128-cbc-sha256 { base cipher-suite-base; if-feature "tls-ecc and tls-sha2"; description "Cipher suite 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 (GCM)"; } identity ecdhe-ecdsa-with-aes-256-cbc-sha384 { base cipher-suite-base; if-feature "tls-ecc and tls-sha2"; description "Cipher suite 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 (GCM)"; } identity ecdhe-rsa-with-aes-128-cbc-sha256 { base cipher-suite-base; if-feature "tls-ecc and tls-sha2"; description "Cipher suite 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 (GCM)"; } identity ecdhe-rsa-with-aes-256-cbc-sha384 { base cipher-suite-base; if-feature "tls-ecc and tls-sha2"; description "Cipher suite 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 (GCM)"; } identity ecdhe-ecdsa-with-aes-128-gcm-sha256 { base cipher-suite-base; if-feature "tls-ecc and tls-gcm and tls-sha2"; description "Cipher suite 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 (GCM)"; } identity ecdhe-ecdsa-with-aes-256-gcm-sha384 { base cipher-suite-base; if-feature "tls-ecc and tls-gcm and tls-sha2"; description "Cipher suite 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 (GCM)"; } identity ecdhe-rsa-with-aes-128-gcm-sha256 { base cipher-suite-base; if-feature "tls-ecc and tls-gcm and tls-sha2"; description "Cipher suite 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 (GCM)"; } identity ecdhe-rsa-with-aes-256-gcm-sha384 { base cipher-suite-base; if-feature "tls-ecc and tls-gcm and tls-sha2"; description "Cipher suite 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 (GCM)"; } identity rsa-with-3des-ede-cbc-sha { base cipher-suite-base; if-feature "tls-3des"; description "Cipher suite TLS_RSA_WITH_3DES_EDE_CBC_SHA."; reference "RFC 5246: The Transport Layer Security (TLS) Protocol Version 1.2"; } identity ecdhe-rsa-with-3des-ede-cbc-sha { base cipher-suite-base; if-feature "tls-ecc and tls-3des"; description "Cipher suite TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA."; reference "RFC 8422: Elliptic Curve Cryptography (ECC) Cipher Suites for Transport Layer Security (TLS)"; } identity ecdhe-rsa-with-aes-128-cbc-sha { base cipher-suite-base; if-feature "tls-ecc"; description "Cipher suite TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA."; reference "RFC 8422: Elliptic Curve Cryptography (ECC) Cipher Suites for Transport Layer Security (TLS)"; } identity ecdhe-rsa-with-aes-256-cbc-sha { base cipher-suite-base; if-feature "tls-ecc"; description "Cipher suite TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA."; reference "RFC 8422: Elliptic Curve Cryptography (ECC) Cipher Suites for Transport Layer Security (TLS)"; } // Groupings grouping hello-params-grouping { description "A reusable grouping for TLS hello message parameters."; reference "RFC 5246: The Transport Layer Security (TLS) Protocol Version 1.2"; container tls-versions { description "Parameters regarding TLS versions."; leaf-list tls-version { type identityref { base tls-version-base; } description "Acceptable TLS protocol versions. If this leaf-list is not configured (has zero elements) the acceptable TLS protocol versions are implementation- defined."; } } container cipher-suites { description "Parameters regarding cipher suites."; leaf-list cipher-suite { type identityref { base cipher-suite-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 XXXX for valid combinations. If this leaf-list is not configured (has zero elements) the acceptable cipher suites are implementation- defined."; reference "RFC XXXX: YANG Groupings for TLS Clients and TLS Servers"; } } } } netopeer2-1.1.70/modules/ietf-tls-server@2019-07-02.yang0000664000000000000000000002356314021433500020646 0ustar rootroot module ietf-tls-server { yang-version 1.1; namespace "urn:ietf:params:xml:ns:yang:ietf-tls-server"; prefix tlss; import ietf-tls-common { prefix tlscmn; revision-date 2019-07-02; // stable grouping definitions reference "RFC XXXX: YANG Groupings for TLS Clients and TLS Servers"; } import ietf-truststore { prefix ts; reference "RFC YYYY: A YANG Data Model for a Truststore"; } import ietf-keystore { prefix ks; reference "RFC ZZZZ: A YANG Data Model for a Keystore"; } import ietf-netconf-acm { prefix nacm; reference "RFC 8341: Network Configuration Access Control Model"; } organization "IETF NETCONF (Network Configuration) Working Group"; contact "WG Web: <http://datatracker.ietf.org/wg/netconf/> WG List: <mailto:netconf@ietf.org> Author: Kent Watsen <mailto:kent+ietf@watsen.net> Author: Gary Wu <mailto:garywu@cisco.com>"; description "This module defines reusable groupings for TLS servers that can be used as a basis for specific TLS server instances. Copyright (c) 2019 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 (https://trustee.ietf.org/license-info). This version of this YANG module is part of RFC XXXX (https://www.rfc-editor.org/info/rfcXXXX); 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 2019-07-02 { description "Initial version"; reference "RFC XXXX: YANG Groupings for TLS Clients and TLS Servers"; } // Features feature tls-server-hello-params-config { description "TLS hello message parameters are configurable on a TLS server."; } feature tls-server-keepalives { description "Per socket TLS keepalive parameters are configurable for TLS servers on the server implementing this feature."; } feature local-client-auth-supported { description "Indicates that the TLS server supports local configuration of client credentials."; } feature external-client-auth-supported { description "Indicates that the TLS server supports external configuration of client credentials."; } // 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 descendent 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 { // FIXME: what about PSKs? 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."; reference "RFC 5246: The Transport Layer Security (TLS) Protocol Version 1.2 RFC ZZZZ: YANG Data Model for a 'Keystore' Mechanism"; uses ks:local-or-keystore-end-entity-cert-with-key-grouping; } // container server-identity container client-authentication { // FIXME: what about PSKs? nacm:default-deny-write; presence "Indicates that certificate based client authentication is supported (i.e., the server will request that the client send a certificate)."; description "Specifies if TLS client authentication is required or optional, and specifies if the certificates needed to authenticate the TLS client are configured locally or externally. If configured locally, the data model enables both trust anchors and end-entity certificate to be set."; choice required-or-optional { mandatory true; // or default to 'required' ? description "Indicates if TLS-level client authentication is required or optional. This is necessary for some protocols (e.g., RESTCONF) the may optionally authenticate a client via TLS-level authentication, HTTP-level authentication, or both simultaneously)."; leaf required { type empty; description "Indicates that TLS-level client authentication is required."; } leaf optional { type empty; description "Indicates that TLS-level client authentication is optional."; } } choice local-or-external { mandatory true; description "Indicates if the certificates needed to authenticate the client are configured locally or externally. The need to support external configuration for client authentication stems from the desire to support consuming data models that prefer to place client authentication with client definitions, rather then in a data model principally concerned with configuring the transport."; case local { if-feature "local-client-auth-supported"; description "The certificates needed to authenticate the clients are configured locally."; leaf ca-certs { if-feature "ts:x509-certificates"; type ts:certificates-ref;//FIXME: local-or-remote? description "A reference to a list 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 YYYY: YANG Data Model for Global Trust Anchors"; } leaf client-certs { if-feature "ts:x509-certificates"; type ts:certificates-ref;//FIXME: local-or-remote? description "A reference to a list of client certificates used by the TLS server to authenticate TLS client certificates. A clients certificate is authenticated if it is an exact match to a configured client certificate."; reference "RFC YYYY: YANG Data Model for Global Trust Anchors"; } } case external { if-feature "external-client-auth-supported"; description "The certificates needed to authenticate the clients are configured externally."; leaf client-auth-defined-elsewhere { type empty; description "Indicates that certificates needed to authenticate clients are configured elsewhere."; } } } // choice local-or-external } // container client-authentication container hello-params { nacm:default-deny-write; if-feature "tls-server-hello-params-config"; 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"; presence "Indicates that keepalives are enabled."; 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 } netopeer2-1.1.70/modules/ietf-truststore@2019-07-02.yang0000664000000000000000000002340614021433500020772 0ustar rootroot 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 YYYY: Common YANG Data Types for Cryptography"; } organization "IETF NETCONF (Network Configuration) Working Group"; contact "WG Web: <http://datatracker.ietf.org/wg/netconf/> WG List: <mailto:netconf@ietf.org> Author: Kent Watsen <mailto:kent+ietf@watsen.net>"; description "This module defines a truststore to centralize management of trust anchors including both X.509 certificates and SSH host keys. Copyright (c) 2019 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 (https://trustee.ietf.org/license-info). This version of this YANG module is part of RFC XXXX (https://www.rfc-editor.org/info/rfcXXXX); 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 2019-07-02 { description "Initial version"; reference "RFC XXXX: A YANG Data Model for a Truststore"; } /****************/ /* Features */ /****************/ feature truststore-supported { description "The 'truststore-supported' feature indicates that the server supports the truststore."; } feature local-definitions-supported { description "The 'local-definitions-supported' feature indicates that the server supports locally-defined trust anchors."; } feature x509-certificates { description "The 'x509-certificates' feature indicates that the server implements the /truststore/certificates subtree."; } feature ssh-host-keys { description "The 'ssh-host-keys' feature indicates that the server implements the /truststore/host-keys subtree."; } /****************/ /* Typedefs */ /****************/ typedef certificates-ref { type leafref { path "/ts:truststore/ts:certificates/ts:name"; } description "This typedef enables modules to easily define a reference to a set of certificates defined in the truststore."; } typedef host-keys-ref { type leafref { path "/ts:truststore/ts:host-keys/ts:name"; } description "This typedef enables modules to easily define a reference to a set of host keys defined in the truststore."; } /*****************/ /* Groupings */ /*****************/ grouping local-or-truststore-certs-grouping { description "A grouping that expands to allow trust anchors to be either stored locally, within the using data model, or be a reference to trust anchors stored in the truststore."; choice local-or-truststore { mandatory true; case local { if-feature "local-definitions-supported"; container local-definition { description "Container to hold the local trust anchor definitions. A list is defined so as to be symmetric with the truststore definition."; uses ct:trust-anchor-certs-grouping; } } case truststore { if-feature "truststore-supported"; if-feature "x509-certificates"; leaf truststore-reference { type ts:certificates-ref; description "A reference to a set of trust anchors that exists in the truststore."; } } description "A choice between an inlined definition and a definition that exists in the truststore."; } } grouping local-or-truststore-host-keys-grouping { description "A grouping that expands to allow trust anchors to be either stored locally, within the using data model, or be a reference to trust anchors stored in the truststore."; choice local-or-truststore { mandatory true; case local { if-feature "local-definitions-supported"; container local-definition { description "Container to hold the local trust anchor definitions. A list is defined so as to be symmetric with the truststore definition."; leaf-list host-key { nacm:default-deny-write; type ct:ssh-host-key; description "The binary data for this host key."; reference "RFC YYYY: Common YANG Data Types for Cryptography"; } uses ct:trust-anchor-certs-grouping; } } case truststore { if-feature "truststore-supported"; if-feature "ssh-host-keys"; leaf truststore-reference { type ts:host-keys-ref; description "A reference to a set of trust anchors that exists in the truststore."; } } description "A choice between an inlined definition and a definition that exists in the truststore."; } } grouping truststore-grouping { description "Grouping definition enables use in other contexts. If ever done, implementations SHOULD augment new 'case' statements into local-or-keystore 'choice' statements to supply leafrefs to the new location."; list certificates { if-feature "x509-certificates"; key "name"; description "A list of certificates. These certificates can be used by a server to authenticate clients, or by a client to authenticate servers. Each list of certificates SHOULD be specific to a purpose, as the list as a whole may be referenced by other modules. For instance, a RESTCONF server's configuration might use a specific list of certificates for when authenticating RESTCONF client connections."; leaf name { type string; description "An arbitrary name for this list of certificates."; } leaf description { type string; description "An arbitrary description for this list of certificates."; } list certificate { key "name"; description "A certificate."; leaf name { type string; description "An arbitrary name for this certificate. The name must be unique across all lists of certificates (not just this list) so that leafrefs from another module can resolve to unique values."; } uses ct:trust-anchor-cert-grouping { refine "cert" { mandatory true; } } } } list host-keys { if-feature "ssh-host-keys"; key "name"; description "A list of host keys. These host-keys can be used by clients to authenticate SSH servers. Each list of host keys SHOULD be specific to a purpose, so the list as a whole may be referenced by other modules. For instance, a NETCONF client's configuration might point to a specific list of host keys for when authenticating specific SSH servers."; leaf name { type string; description "An arbitrary name for this list of SSH host keys."; } leaf description { type string; description "An arbitrary description for this list of SSH host keys."; } list host-key { key "name"; description "A host key."; leaf name { type string; description "An arbitrary name for this host-key. Must be unique across all lists of host-keys (not just this list) so that a leafref to it from another module can resolve to unique values."; } leaf host-key { type ct:ssh-host-key; mandatory true; description "The binary public key data for this host key."; reference "RFC YYYY: Common YANG Data Types for Cryptography"; } } } } /*********************************/ /* Protocol accessible nodes */ /*********************************/ container truststore { nacm:default-deny-write; description "The truststore contains sets of X.509 certificates and SSH host keys."; uses truststore-grouping; } } netopeer2-1.1.70/modules/ietf-x509-cert-to-name@2014-12-10.yang0000664000000000000000000002704514021433500021523 0ustar rootroot module ietf-x509-cert-to-name { 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: <http://tools.ietf.org/wg/netmod/> WG List: <mailto:netmod@ietf.org> WG Chair: Thomas Nadeau <mailto:tnadeau@lucidvision.com> WG Chair: Juergen Schoenwaelder <mailto:j.schoenwaelder@jacobs-university.de> Editor: Martin Bjorklund <mailto:mbj@tail-f.com> Editor: Juergen Schoenwaelder <mailto:j.schoenwaelder@jacobs-university.de>"; 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"; } /* Identities */ 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"; } /* * Groupings */ 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 x509c2n: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"; } } } } netopeer2-1.1.70/modules/nc-notifications@2008-07-14.yang0000664000000000000000000000531514021433500021056 0ustar rootrootmodule nc-notifications { namespace "urn:ietf:params:xml:ns:netmod:notification"; prefix "manageEvent"; import ietf-yang-types { prefix yang; } import notifications { prefix ncEvent; } organization "IETF NETCONF WG"; contact "netconf@ietf.org"; description "Conversion of the 'manageEvent' XSD in the NETCONF Notifications RFC."; reference "RFC 5277"; revision 2008-07-14 { description "RFC 5277 version."; } container netconf { description "Top-level element in the notification namespace"; config false; container streams { description "The list of event streams supported by the system. When a query is issued, the returned set of streams is determined based on user privileges."; list stream { description "Stream name, description and other information."; key name; min-elements 1; leaf name { description "The name of the event stream. If this is the default NETCONF stream, this must have the value 'NETCONF'."; type ncEvent:streamNameType; } leaf description { description "A description of the event stream, including such information as the type of events that are sent over this stream."; type string; mandatory true; } leaf replaySupport { description "A description of the event stream, including such information as the type of events that are sent over this stream."; type boolean; mandatory true; } leaf replayLogCreationTime { description "The timestamp of the creation of the log used to support the replay function on this stream. Note that this might be earlier then the earliest available notification in the log. This object is updated if the log resets for some reason. This object MUST be present if replay is supported."; type yang:date-and-time; // xsd:dateTime is wrong! } } } } notification replayComplete { description "This notification is sent to signal the end of a replay portion of a subscription."; } notification notificationComplete { description "This notification is sent to signal the end of a notification subscription. It is sent in the case that stopTime was specified during the creation of the subscription.."; } } netopeer2-1.1.70/modules/notifications@2008-07-14.yang0000664000000000000000000000535014021433500020457 0ustar rootrootmodule notifications { namespace "urn:ietf:params:xml:ns:netconf:notification:1.0"; prefix "ncEvent"; import ietf-yang-types { prefix yang; } organization "IETF NETCONF WG"; contact "netconf@ops.ietf.org"; description "Conversion of the 'ncEvent' XSD in the NETCONF Notifications RFC."; reference "RFC 5277."; revision 2008-07-14 { description "RFC 5277 version."; } typedef streamNameType { description "The name of an event stream."; type string; } rpc create-subscription { description "The command to create a notification subscription. It takes as argument the name of the notification stream and filter. Both of those options limit the content of the subscription. In addition, there are two time-related parameters, startTime and stopTime, which can be used to select the time interval of interest to the notification replay feature."; input { leaf stream { description "An optional parameter that indicates which stream of events is of interest. If not present, then events in the default NETCONF stream will be sent."; type streamNameType; default "NETCONF"; } anyxml filter { description "An optional parameter that indicates which subset of all possible events is of interest. The format of this parameter is the same as that of the filter parameter in the NETCONF protocol operations. If not present, all events not precluded by other parameters will be sent."; } leaf startTime { description "A parameter used to trigger the replay feature and indicates that the replay should start at the time specified. If start time is not present, this is not a replay subscription."; type yang:date-and-time; } leaf stopTime { // must ". >= ../startTime"; description "An optional parameter used with the optional replay feature to indicate the newest notifications of interest. If stop time is not present, the notifications will continue until the subscription is terminated. Must be used with startTime."; type yang:date-and-time; } } } /*container notification { description "internal struct to start a notification"; config false; leaf eventTime { mandatory true; type yang:date-and-time; } // eventType and any data content goes here }*/ } netopeer2-1.1.70/packages/0000775000000000000000000000000014021433500013751 5ustar rootrootnetopeer2-1.1.70/packages/CMakeLists.txt0000664000000000000000000000567214021433500016523 0ustar rootrootset(PACKAGE "netopeer2") set(PACKAGE_BRIEF "NETCONF tools") set(PACKAGE_DESC ${NETOPEER2_DESC}) set(PACKAGE_DBG_DESC "Debug symbols for the server and client.") set(GITHUB_URL "https://github.com/cesnet/netopeer2") # read install scripts into variables file(READ "${SCRIPT_DIR}/setup.sh" SETUP_SH) file(READ "${SCRIPT_DIR}/merge_hostkey.sh" MERGE_HOSTKEY_SH) file(READ "${SCRIPT_DIR}/merge_config.sh" MERGE_CONFIG_SH) # prepend required environment variables for setup set(SETUP_SH "NP2_MODULE_DIR=/usr/share/yang/modules/netopeer2 NP2_MODULE_PERMS=660 NP2_MODULE_OWNER=root NP2_MODULE_GROUP=netconf\n${SETUP_SH}") set(POSTINST_CMD1 "groupadd -f netconf") set(POSTINST_CMD2 ${SETUP_SH}) set(POSTINST_CMD3 ${MERGE_HOSTKEY_SH}) set(POSTINST_CMD4 ${MERGE_CONFIG_SH}) find_program(DEB_BUILDER NAMES debuild) find_program(RPM_BUILDER NAMES rpmbuild) # setup package build configure_file(${PROJECT_SOURCE_DIR}/packages/${PACKAGE}.spec.in ${PROJECT_BINARY_DIR}/build-packages/${PACKAGE}.spec) configure_file(${PROJECT_SOURCE_DIR}/packages/${PACKAGE}.dsc.in ${PROJECT_BINARY_DIR}/build-packages/${PACKAGE}.dsc) configure_file(${PROJECT_SOURCE_DIR}/packages/debian.control.in ${PROJECT_BINARY_DIR}/build-packages/debian.control @ONLY) configure_file(${PROJECT_SOURCE_DIR}/packages/debian.rules.in ${PROJECT_BINARY_DIR}/build-packages/debian.rules) configure_file(${PROJECT_SOURCE_DIR}/packages/debian.compat ${PROJECT_BINARY_DIR}/build-packages/debian.compat COPYONLY) configure_file(${PROJECT_SOURCE_DIR}/packages/debian.copyright ${PROJECT_BINARY_DIR}/build-packages/debian.copyright COPYONLY) configure_file(${PROJECT_SOURCE_DIR}/packages/debian.${PACKAGE}.install ${PROJECT_BINARY_DIR}/build-packages/debian.${PACKAGE}.install COPYONLY) configure_file(${PROJECT_SOURCE_DIR}/packages/debian.postinst.in ${PROJECT_BINARY_DIR}/build-packages/debian.postinst @ONLY) if(NOT DEB_BUILDER) message(STATUS "Missing tools (devscripts, debhelper package) for building DEB package.") else() # target for local build deb package message(STATUS "To build local DEB package, use \"build-deb\" target.") add_custom_target(build-deb WORKING_DIRECTORY ${PROJECT_BINARY_DIR} COMMAND build-packages/local-deb.sh ) configure_file(${PROJECT_SOURCE_DIR}/packages/local-deb.sh.in ${PROJECT_BINARY_DIR}/build-packages/local-deb.sh @ONLY) endif() if(NOT RPM_BUILDER) message(STATUS "Missing tools (rpm package) for building RPM package.") else() # target for local build rpm package message(STATUS "To build local RPM package, use \"build-rpm\" target.") string(REPLACE ${PROJECT_SOURCE_DIR} "." EXCLUDE_BUILD_DIR ${PROJECT_BINARY_DIR}) add_custom_target(build-rpm WORKING_DIRECTORY ${PROJECT_BINARY_DIR} COMMAND build-packages/local-rpm.sh ) configure_file(${PROJECT_SOURCE_DIR}/packages/local-rpm.sh.in ${PROJECT_BINARY_DIR}/build-packages/local-rpm.sh @ONLY) endif() netopeer2-1.1.70/packages/create-package.sh0000775000000000000000000000404214021433500017144 0ustar rootroot#!/usr/bin/env bash package="netopeer2" url="https://github.com/cesnet/netopeer2" if [ "$TRAVIS_PULL_REQUEST" == "true" -o "$TRAVIS_EVENT_TYPE" != "cron" ] ; then exit 0 fi #install OSC sudo apt-get install -y osc # check osb_user and osb_pass if [ -z "${osb_user}" -o -z "${osb_pass}" ]; then exit 0 fi # fill username and password for opensuse build and downlaod last package information echo -e "[general]\napiurl = https://api.opensuse.org\n\n[https://api.opensuse.org]\nuser = ${osb_user}\npass = ${osb_pass}" >~/.oscrc cd ./build osc checkout home:liberouter cp home:liberouter/$package/$package.spec home:liberouter/$package/debian.changelog home:liberouter cp build-packages/debian* build-packages/$package* home:liberouter/$package cd home:liberouter/$package # check versions VERSION=$(cat $package.spec | grep "Version: " | awk '{print $NF}') OLDVERSION=$(cat ../$package.spec | grep "Version: " | awk '{print $NF}') if [ -z "$FORCEVERSION" -a "$VERSION" == "$OLDVERSION" ]; then exit 0 fi # create new changelog and paste old changelog if [ "$VERSION" != "$OLDVERSION" ]; then logtime=$(git log -i --grep="VERSION .* $OLDVERSION" | grep "Date: " | sed 's/Date:[ ]*//') echo -e "$package ($VERSION) stable; urgency=low\n" >debian.changelog git log --since="$logtime" --pretty=format:" * %s (%aN)%n" | grep "BUGFIX\|CHANGE\|FEATURE" >>debian.changelog git log -1 --pretty=format:"%n -- %aN <%aE> %aD%n" >>debian.changelog echo -e "\n" >>debian.changelog cat ../debian.changelog >>debian.changelog fi if [ "$VERSION" != "$OLDVERSION" ]; then git log -1 --date=format:'%a %b %d %Y' --pretty=format:"* %ad %aN <%aE>" | tr -d "\n" >>$package.spec echo " $VERSION" >>$package.spec git log --since="$logtime" --pretty=format:"- %s (%aN)" | grep "BUGFIX\|CHANGE\|FEATURE" >>$package.spec echo -e "\n" >>$package.spec fi cat ../$package.spec | sed -e '1,/%changelog/d' >>$package.spec # download source and update to opensuse build wget "${url}/archive/master.tar.gz" -O master.tar.gz osc commit -m travis-update netopeer2-1.1.70/packages/debian.compat0000664000000000000000000000000214021433500016370 0ustar rootroot9 netopeer2-1.1.70/packages/debian.control.in0000664000000000000000000000077314021433500017211 0ustar rootrootSource: @PACKAGE@ Maintainer: CESNET <mvasko@cesnet.cz> Priority: extra Standards-Version: 3.8.2 Build-Depends: debhelper (>= 9), gcc Homepage: @GITHUB_URL@ Package: @PACKAGE@ Depends: libyang@LIBYANG_DEP_SOVERSION_MAJOR@, sysrepo (>= 1.4.122), libnetconf2 (>= 1.1.43), libssh-4 (>= 0.7.1), openssl, ${shlibs:Depends} Section: libs Architecture: any Description: @PACKAGE_DESC@ Package: @PACKAGE@-dbg Depends: @PACKAGE@ (=@NP2SRV_VERSION@) Section: debug Architecture: any Description: @PACKAGE_DBG_DESC@ netopeer2-1.1.70/packages/debian.copyright0000664000000000000000000000271014021433500017125 0ustar rootrootCopyright (c) 2015-2020, CESNET All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of libnetconf2 nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT HOLDER OR CONTRIBUTORS 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. netopeer2-1.1.70/packages/debian.netopeer2.install0000664000000000000000000000012114021433500020457 0ustar rootrootusr/bin/netopeer2-server usr/share/yang usr/bin/netopeer2-cli usr/share/man/man1 netopeer2-1.1.70/packages/debian.postinst.in0000775000000000000000000000011414021433500017404 0ustar rootroot#!/bin/bash @POSTINST_CMD1@ @POSTINST_CMD2@ @POSTINST_CMD3@ @POSTINST_CMD4@ netopeer2-1.1.70/packages/debian.rules.in0000664000000000000000000000056714021433500016664 0ustar rootroot#!/usr/bin/make -f # -*- makefile -*- # Uncomment this to turn on verbose mode. export DH_VERBOSE=1 %: dh $@ override_dh_strip: dh_strip -p@PACKAGE@ --dbg-package=@PACKAGE@-dbg override_dh_auto_configure: cmake -DCMAKE_INSTALL_PREFIX=/usr -DCMAKE_BUILD_TYPE="Package" -DINSTALL_MODULES=OFF -DGENERATE_HOSTKEY=OFF \ -DMERGE_LISTEN_CONFIG=OFF . override_dh_auto_test: netopeer2-1.1.70/packages/local-deb.sh.in0000775000000000000000000000203014021433500016532 0ustar rootroot#!/usr/bin/env bash LOCAL_LC_TIME=$LC_TIME export LC_TIME="en_US.UTF-8" mkdir -p debian/source mkdir -p debs cp build-packages/debian.rules debian/rules cp build-packages/debian.control debian/control echo '9' >debian/compat echo '3.0 (quilt)' >debian/source/format cp "@PROJECT_SOURCE_DIR@/LICENSE" debian/copyright cp "@PROJECT_SOURCE_DIR@/packages/debian.@PACKAGE@.install" debian/@PACKAGE@.install cp "@PROJECT_SOURCE_DIR@/packages/debian.@PACKAGE@-dev.install" debian/@PACKAGE@-dev.install cp "@PROJECT_SOURCE_DIR@/packages/debian.@PYTHON_PACKAGE@.install" debian/@PYTHON_PACKAGE@.install echo -e "@PACKAGE@ (@LIBNETCONF2_VERSION@) stable; urgency=low\n" >debian/changelog git log -10 --pretty=format:' * %s (%aN)%n' 2>/dev/null >>debian/changelog || echo -e " * unknown changes \n" >>debian/changelog git log -1 --pretty=format:'%n -- %aN <%aE> %aD%n' >>debian/changelog 2>/dev/null || echo " -- ${USER} <${USER}@`hostname`> `date -R`" >>debian/changelog debuild --no-lintian -i -b -us -uc mv ../*.deb debs export LC_TIME=$LOCAL_LC_TIME netopeer2-1.1.70/packages/local-rpm.sh.in0000775000000000000000000000164314021433500016607 0ustar rootroot#!/usr/bin/env bash LOCAL_LC_TIME=$LC_TIME export LC_TIME="en_US.UTF-8" cd "@PROJECT_SOURCE_DIR@" && tar --exclude="@EXCLUDE_BUILD_DIR@" --exclude=./.git* -zcvf "@PROJECT_BINARY_DIR@/master.tar.gz" . --transform 's/./@PACKAGE@-master/' cd @PROJECT_BINARY_DIR@ mkdir -p rpms/{BUILD,RPMS,SOURCES,SPECS,SRPMS,tmp} mv master.tar.gz rpms/SOURCES sed -e '7d' build-packages/@PACKAGE@.spec > rpms/SPECS/@PACKAGE@.spec git log -1 --date=format:'%a %b %d %Y' --pretty=format:"* %ad %aN <%aE>" 2>/dev/null >>rpms/SPECS/@PACKAGE@.spec || printf "* `date +"%a %b %d %Y"` ${USER} <${USER}@`hostname`>" >>rpms/SPECS/@PACKAGE@.spec echo " @LIBNETCONF2_VERSION@" >>rpms/SPECS/@PACKAGE@.spec git log -10 --pretty=format:"- %s (%aN)" >>rpms/SPECS/@PACKAGE@.spec 2>/dev/null || echo "- unknown changes" >>rpms/SPECS/@PACKAGE@.spec rpmbuild --ba rpms/SPECS/@PACKAGE@.spec --define "%_topdir @PROJECT_BINARY_DIR@/rpms" export LC_TIME=$LOCAL_LC_TIME netopeer2-1.1.70/packages/netopeer2.dsc.in0000664000000000000000000000057614021433500016764 0ustar rootrootFormat: 3.0 (quilt) Source: @PACKAGE@ Binary: @PACKAGE@, @PACKAGE@-dbg Maintainer: CESNET <mvasko@cesnet.cz> Version: @NP2SRV_VERSION@ Architecture: any Standards-Version: 3.8.2 Homepage: @GITHUB_URL@ Vcs-Git: @GITHUB_URL@ Build-Depends: debhelper (>= 9), make, gcc, cmake, libyang-dev (>= 1.0), sysrepo-dev (>= 1.4.122), libnetconf2-dev (>= 1.1.43), libssh-dev (>= 0.7.1), openssl netopeer2-1.1.70/packages/netopeer2.spec.in0000664000000000000000000000233214021433500017135 0ustar rootrootName: @PACKAGE@ Version: @NP2SRV_VERSION@ Release: 0 Summary: @PACKAGE_BRIEF@ Url: @GITHUB_URL@ Source: @GITHUB_URL@/archive/master.tar.gz License: BSD-3-Clause BuildRoot: %{_tmppath}/%{name}-%{version}-%{release} %if 0%{?suse_version} Requires: libssh4 >= 0.7.1 %else Requires: libssh >= 0.7.1 %endif Requires: libyang@LIBYANG_DEP_SOVERSION_MAJOR@ Requires: sysrepo >= 1.4.122 Requires: libnetconf2 >= 1.1.43 Requires: openssl BuildRequires: cmake BuildRequires: gcc BuildRequires: libssh-devel >= 0.7.1 BuildRequires: libyang-devel >= 1.0 BuildRequires: sysrepo-devel >= 1.4.122 BuildRequires: libnetconf2-devel >= 1.1.43 BuildRequires: openssl-devel %if 0%{?suse_version} BuildRequires: timezone %endif %description @PACKAGE_DESC@ %prep %setup -n @PACKAGE@-master mkdir build %build cd build cmake -DCMAKE_INSTALL_PREFIX=/usr \ -DCMAKE_BUILD_TYPE="Package" \ -DINSTALL_MODULES=OFF \ -DGENERATE_HOSTKEY=OFF \ -DMERGE_LISTEN_CONFIG=OFF .. make %install cd build make DESTDIR=%{buildroot} install %post #!/bin/bash @POSTINST_CMD1@ @POSTINST_CMD2@ @POSTINST_CMD3@ @POSTINST_CMD4@ %files %defattr(-,root,root) %{_bindir}/netopeer2-server %{_datadir}/yang %{_bindir}/netopeer2-cli %{_mandir}/man1 %changelog netopeer2-1.1.70/packages/upload-package.sh0000775000000000000000000000106514021433500017167 0ustar rootroot#!/usr/bin/env bash GIT_PATH=`git rev-parse --show-cdup` if [ "$GIT_PATH" != "" ]; then echo "Must be run from repository root" exit fi if [ $# -ne 1 ]; then echo "Usage $0 <osb-user>" exit fi # for correct git log command output used in changelog LC_TIME=en_US.UTF-8 # upload package even if the version is not newer FORCEVERSION=1 # needed so that the package is even created TRAVIS_EVENT_TYPE=cron # OSB login osb_user=$1 # OSB password echo -n Password: read -s osb_pass echo # create and upload the package . ./packages/create-package.sh netopeer2-1.1.70/scripts/0000775000000000000000000000000014021433500013662 5ustar rootrootnetopeer2-1.1.70/scripts/merge_config.sh0000775000000000000000000000435614021433500016655 0ustar rootroot#!/usr/bin/env bash set -e # optional env variable override if [ -n "$SYSREPOCFG_EXECUTABLE" ]; then SYSREPOCFG="$SYSREPOCFG_EXECUTABLE" # avoid problems with sudo PATH elif [ `id -u` -eq 0 ]; then SYSREPOCFG=`su -c 'which sysrepocfg' -l $USER` else SYSREPOCFG=`which sysrepocfg` fi KS_KEY_NAME=genkey # check that there is no listen/Call Home configuration yet SERVER_CONFIG=`$SYSREPOCFG -X -x "/ietf-netconf-server:netconf-server/listen/endpoint[1]/name | /ietf-netconf-server:netconf-server/call-home/netconf-client[1]/name"` if [ -z "$SERVER_CONFIG" ]; then # import default config CONFIG="<netconf-server xmlns=\"urn:ietf:params:xml:ns:yang:ietf-netconf-server\"> <listen> <endpoint> <name>default-ssh</name> <ssh> <tcp-server-parameters> <local-address>0.0.0.0</local-address> <keepalives> <idle-time>1</idle-time> <max-probes>10</max-probes> <probe-interval>5</probe-interval> </keepalives> </tcp-server-parameters> <ssh-server-parameters> <server-identity> <host-key> <name>default-key</name> <public-key> <keystore-reference>$KS_KEY_NAME</keystore-reference> </public-key> </host-key> </server-identity> <client-authentication> <supported-authentication-methods> <publickey/> <passsword/> <other>interactive</other> </supported-authentication-methods> <users/> </client-authentication> </ssh-server-parameters> </ssh> </endpoint> </listen> </netconf-server>" TMPFILE=`mktemp -u` printf -- "$CONFIG" > $TMPFILE # apply it to startup and running $SYSREPOCFG --edit=$TMPFILE -d startup -f xml -m ietf-netconf-server -v2 $SYSREPOCFG -C startup -m ietf-netconf-server -v2 # remove the tmp file rm $TMPFILE fi netopeer2-1.1.70/scripts/merge_hostkey.sh0000775000000000000000000000307014021433500017066 0ustar rootroot#!/usr/bin/env bash set -e # optional env variable override if [ -n "$SYSREPOCFG_EXECUTABLE" ]; then SYSREPOCFG="$SYSREPOCFG_EXECUTABLE" # avoid problems with sudo PATH elif [ `id -u` -eq 0 ]; then SYSREPOCFG=`su -c 'which sysrepocfg' -l $USER` else SYSREPOCFG=`which sysrepocfg` fi # avoid problems with sudo PATH if [ `id -u` -eq 0 ]; then OPENSSL=`su -c 'which openssl' -l $USER` else OPENSSL=`which openssl` fi # check that there is no SSH key with this name yet KEYSTORE_KEY=`$SYSREPOCFG -X -x "/ietf-keystore:keystore/asymmetric-keys/asymmetric-key[name='genkey']/name"` if [ -z "$KEYSTORE_KEY" ]; then # generate a new key PRIVPEM=`$OPENSSL genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:2048 -outform PEM 2>/dev/null` # remove header/footer PRIVKEY=`grep -v -- "-----" - <<STDIN $PRIVPEM STDIN` # get public key PUBPEM=`$OPENSSL rsa -pubout 2>/dev/null <<STDIN $PRIVPEM STDIN` # remove header/footer PUBKEY=`grep -v -- "-----" - <<STDIN $PUBPEM STDIN` # generate edit config CONFIG="<keystore xmlns=\"urn:ietf:params:xml:ns:yang:ietf-keystore\"> <asymmetric-keys> <asymmetric-key> <name>genkey</name> <algorithm>rsa2048</algorithm> <public-key>$PUBKEY</public-key> <private-key>$PRIVKEY</private-key> </asymmetric-key> </asymmetric-keys> </keystore>" TMPFILE=`mktemp -u` printf -- "$CONFIG" > $TMPFILE # apply it to startup and running $SYSREPOCFG --edit=$TMPFILE -d startup -f xml -m ietf-keystore -v2 $SYSREPOCFG -C startup -m ietf-keystore -v2 # remove the tmp file rm $TMPFILE fi netopeer2-1.1.70/scripts/setup.sh0000775000000000000000000000726114021433500015367 0ustar rootroot#!/usr/bin/env bash # env variables NP2_MODULE_DIR, NP2_MODULE_PERMS must be defined and NP2_MODULE_OWNER, NP2_MODULE_GROUP will be used if # defined when executing this script! if [ -z "$NP2_MODULE_DIR" -o -z "$NP2_MODULE_PERMS" ]; then echo "Required environment variables not defined!" exit 1 fi # optional env variable override if [ -n "$SYSREPOCTL_EXECUTABLE" ]; then SYSREPOCTL="$SYSREPOCTL_EXECUTABLE" # avoid problems with sudo PATH elif [ `id -u` -eq 0 ]; then SYSREPOCTL=`su -c 'which sysrepoctl' -l $USER` else SYSREPOCTL=`which sysrepoctl` fi MODDIR=${DESTDIR}${NP2_MODULE_DIR} PERMS=${NP2_MODULE_PERMS} OWNER=${NP2_MODULE_OWNER} GROUP=${NP2_MODULE_GROUP} # array of modules to install MODULES=( "ietf-netconf-acm@2018-02-14.yang" "ietf-netconf@2013-09-29.yang -e writable-running -e candidate -e rollback-on-error -e validate -e startup -e url -e xpath" "ietf-netconf-monitoring@2010-10-04.yang" "ietf-netconf-nmda@2019-01-07.yang -e origin -e with-defaults" "nc-notifications@2008-07-14.yang" "notifications@2008-07-14.yang" "ietf-x509-cert-to-name@2014-12-10.yang" "ietf-crypto-types@2019-07-02.yang" "ietf-keystore@2019-07-02.yang -e keystore-supported" "ietf-truststore@2019-07-02.yang -e truststore-supported -e x509-certificates" "ietf-tcp-common@2019-07-02.yang -e keepalives-supported" "ietf-ssh-server@2019-07-02.yang -e local-client-auth-supported" "ietf-tls-server@2019-07-02.yang -e local-client-auth-supported" "ietf-netconf-server@2019-07-02.yang -e ssh-listen -e tls-listen -e ssh-call-home -e tls-call-home" ) # functions INSTALL_MODULE() { CMD="'$SYSREPOCTL' -a -i $MODDIR/$1 -s '$MODDIR' -p '$PERMS' -v2" if [ ! -z ${OWNER} ]; then CMD="$CMD -o '$OWNER'" fi if [ ! -z ${GROUP} ]; then CMD="$CMD -g '$GROUP'" fi eval $CMD local rc=$? if [ $rc -ne 0 ]; then exit $rc fi } UPDATE_MODULE() { CMD="'$SYSREPOCTL' -a -U $MODDIR/$1 -s '$MODDIR' -p '$PERMS' -v2" if [ ! -z ${OWNER} ]; then CMD="$CMD -o '$OWNER'" fi if [ ! -z ${GROUP} ]; then CMD="$CMD -g '$GROUP'" fi eval $CMD local rc=$? if [ $rc -ne 0 ]; then exit $rc fi } ENABLE_FEATURE() { "$SYSREPOCTL" -a -c $1 -e $2 -v2 local rc=$? if [ $rc -ne 0 ]; then exit $rc fi } # get current modules SCTL_MODULES=`$SYSREPOCTL -l` for i in "${MODULES[@]}"; do name=`echo "$i" | sed 's/\([^@]*\).*/\1/'` SCTL_MODULE=`echo "$SCTL_MODULES" | grep "^$name \+|[^|]*| I"` if [ -z "$SCTL_MODULE" ]; then # install module with all its features INSTALL_MODULE "$i" continue fi sctl_revision=`echo "$SCTL_MODULE" | sed 's/[^|]*| \([^ ]*\).*/\1/'` revision=`echo "$i" | sed 's/[^@]*@\([^\.]*\).*/\1/'` if [ "$sctl_revision" \< "$revision" ]; then # update module without any features file=`echo "$i" | cut -d' ' -f 1` UPDATE_MODULE "$file" fi # parse sysrepoctl features and add extra space at the end for easier matching sctl_features="`echo "$SCTL_MODULE" | sed 's/\([^|]*|\)\{6\}\(.*\)/\2/'` " # parse features we want to enable features=`echo "$i" | sed 's/[^ ]* \(.*\)/\1/'` while [ "${features:0:3}" = "-e " ]; do # skip "-e " features=${features:3} # parse feature feature=`echo "$features" | sed 's/\([^[:space:]]*\).*/\1/'` # enable feature if not already sctl_feature=`echo "$sctl_features" | grep " ${feature} "` if [ -z "$sctl_feature" ]; then # enable feature ENABLE_FEATURE $name $feature fi # next iteration, skip this feature features=`echo "$features" | sed 's/[^[:space:]]* \(.*\)/\1/'` done done netopeer2-1.1.70/src/0000775000000000000000000000000014021433500012762 5ustar rootrootnetopeer2-1.1.70/src/common.c0000664000000000000000000007233014021433500014423 0ustar rootroot/** * @file common.c * @author Michal Vasko <mvasko@cesnet.cz> * @brief netopeer2-server common routines * * Copyright (c) 2019 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 "config.h" #include <stdlib.h> #include <string.h> #include <ctype.h> #include <unistd.h> #include <errno.h> #ifdef NP2SRV_URL_CAPAB # include <curl/curl.h> # ifdef CURL_GLOBAL_ACK_EINTR # define URL_INIT_FLAGS CURL_GLOBAL_SSL | CURL_GLOBAL_ACK_EINTR # else # define URL_INIT_FLAGS CURL_GLOBAL_SSL # endif #endif #include <libyang/libyang.h> #include <nc_server.h> #include "common.h" #include "log.h" #include "netconf_acm.h" #include "netconf_monitoring.h" struct np2srv np2srv = {.unix_mode = -1, .unix_uid = -1, .unix_gid = -1}; int np_sleep(uint32_t ms) { struct timespec ts; ts.tv_sec = ms / 1000; ts.tv_nsec = (ms % 1000) * 1000000; return nanosleep(&ts, NULL); } const char * np_get_nc_sess_user(sr_session_ctx_t *session) { struct nc_session *nc_sess = NULL; uint32_t nc_sid, i; nc_sid = sr_session_get_event_nc_id(session); for (i = 0; (nc_sess = nc_ps_get_session(np2srv.nc_ps, i)); ++i) { if (nc_session_get_id(nc_sess) == nc_sid) { break; } } if (!nc_sess) { return NULL; } return nc_session_get_username(nc_sess); } sr_session_ctx_t * np_get_user_sess(sr_session_ctx_t *ev_sess) { struct nc_session *nc_sess = NULL; uint32_t nc_sid, i; nc_sid = sr_session_get_event_nc_id(ev_sess); for (i = 0; (nc_sess = nc_ps_get_session(np2srv.nc_ps, i)); ++i) { if (nc_session_get_id(nc_sess) == nc_sid) { break; } } if (!nc_sess) { return NULL; } return nc_session_get_data(nc_sess); } void np2srv_ntf_new_cb(sr_session_ctx_t *UNUSED(session), const sr_ev_notif_type_t notif_type, const struct lyd_node *notif, time_t timestamp, void *private_data) { struct nc_server_notif *nc_ntf = NULL; struct nc_session *ncs = (struct nc_session *)private_data; struct lyd_node *ly_ntf = NULL; NC_MSG_TYPE msg_type; char buf[26], *datetime; /* create these notifications, sysrepo only emulates them */ if (notif_type == SR_EV_NOTIF_REPLAY_COMPLETE) { ly_ntf = lyd_new_path(NULL, sr_get_context(np2srv.sr_conn), "/nc-notifications:replayComplete", NULL, 0, 0); notif = ly_ntf; } else if (notif_type == SR_EV_NOTIF_STOP) { ly_ntf = lyd_new_path(NULL, sr_get_context(np2srv.sr_conn), "/nc-notifications:notificationComplete", NULL, 0, 0); notif = ly_ntf; } /* find the top-level node */ while (notif->parent) { notif = notif->parent; } /* check NACM */ if (ncac_check_operation(notif, nc_session_get_username(ncs))) { goto cleanup; } /* create the notification object */ datetime = nc_time2datetime(timestamp, NULL, buf); nc_ntf = nc_server_notif_new((struct lyd_node *)notif, datetime, NC_PARAMTYPE_CONST); /* send the notification */ msg_type = nc_server_notif_send(ncs, nc_ntf, NP2SRV_NOTIF_SEND_TIMEOUT); if ((msg_type == NC_MSG_ERROR) || (msg_type == NC_MSG_WOULDBLOCK)) { ERR("Sending a notification to session %d %s.", nc_session_get_id(ncs), msg_type == NC_MSG_ERROR ? "failed" : "timed out"); goto cleanup; } ncm_session_notification(ncs); if (notif_type == SR_EV_NOTIF_STOP) { /* subscription finished */ nc_session_set_notif_status(ncs, 0); } cleanup: nc_server_notif_free(nc_ntf); lyd_free_withsiblings(ly_ntf); } void np2srv_new_session_cb(const char *UNUSED(client_name), struct nc_session *new_session) { int c; sr_val_t *event_data; sr_session_ctx_t *sr_sess = NULL; const struct lys_module *mod; char *host = NULL; /* start sysrepo session for every NETCONF session (so that it can be used for notification subscriptions) */ c = sr_session_start(np2srv.sr_conn, SR_DS_RUNNING, &sr_sess); if (c != SR_ERR_OK) { ERR("Failed to start a sysrepo session (%s).", sr_strerror(c)); nc_session_free(new_session, NULL); return; } nc_session_set_data(new_session, sr_sess); sr_session_set_nc_id(sr_sess, nc_session_get_id(new_session)); ncm_session_add(new_session); c = 0; while ((c < 3) && nc_ps_add_session(np2srv.nc_ps, new_session)) { /* presumably timeout, give it a shot 2 times */ np_sleep(NP2SRV_PS_BACKOFF_SLEEP); ++c; } if (c == 3) { /* there is some serious problem in synchronization/system planner */ EINT; goto error; } if ((mod = ly_ctx_get_module(sr_get_context(np2srv.sr_conn), "ietf-netconf-notifications", NULL, 1))) { /* generate ietf-netconf-notification's netconf-session-start event for sysrepo */ if (nc_session_get_ti(new_session) != NC_TI_UNIX) { host = (char *)nc_session_get_host(new_session); } event_data = calloc(3, sizeof *event_data); event_data[0].xpath = "/ietf-netconf-notifications:netconf-session-start/username"; event_data[0].type = SR_STRING_T; event_data[0].data.string_val = (char *)nc_session_get_username(new_session); event_data[1].xpath = "/ietf-netconf-notifications:netconf-session-start/session-id"; event_data[1].type = SR_UINT32_T; event_data[1].data.uint32_val = nc_session_get_id(new_session); if (host) { event_data[2].xpath = "/ietf-netconf-notifications:netconf-session-start/source-host"; event_data[2].type = SR_STRING_T; event_data[2].data.string_val = host; } c = sr_event_notif_send(np2srv.sr_sess, "/ietf-netconf-notifications:netconf-session-start", event_data, host ? 3 : 2); if (c != SR_ERR_OK) { WRN("Failed to send a notification (%s).", sr_strerror(c)); } else { VRB("Generated new event (netconf-session-start)."); } free(event_data); } return; error: ncm_session_del(new_session); sr_session_stop(sr_sess); nc_session_free(new_session, NULL); } #ifdef NP2SRV_URL_CAPAB int np2srv_url_setcap(void) { uint32_t i, j; char *cpblt; int first = 1, cur_prot, sup_prot = 0; curl_version_info_data *curl_data; const char *url_protocol_str[] = {"scp", "http", "https", "ftp", "sftp", "ftps", "file", NULL}; const char *main_cpblt = "urn:ietf:params:netconf:capability:url:1.0?scheme="; curl_data = curl_version_info(CURLVERSION_NOW); for (i = 0; curl_data->protocols[i]; ++i) { for (j = 0; url_protocol_str[j]; ++j) { if (!strcmp(curl_data->protocols[i], url_protocol_str[j])) { sup_prot |= (1 << j); break; } } } if (!sup_prot) { /* no protocols supported */ return EXIT_SUCCESS; } /* get max capab string size and allocate it */ j = strlen(main_cpblt) + 1; for (i = 0; url_protocol_str[i]; ++i) { j += strlen(url_protocol_str[i]) + 1; } cpblt = malloc(j); if (!cpblt) { EMEM; return EXIT_FAILURE; } /* main capability */ strcpy(cpblt, main_cpblt); /* supported protocols */ for (i = 0, cur_prot = 1; i < (sizeof url_protocol_str / sizeof *url_protocol_str); ++i, cur_prot <<= 1) { if (cur_prot & sup_prot) { sprintf(cpblt + strlen(cpblt), "%s%s", first ? "" : ",", url_protocol_str[i]); first = 0; } } nc_server_set_capability(cpblt); free(cpblt); return EXIT_SUCCESS; } struct np2srv_url_mem { char *memory; size_t size; }; static size_t url_writedata(char *ptr, size_t size, size_t nmemb, void *userdata) { int *fd = (int *)userdata; return write(*fd, ptr, size * nmemb); } static size_t url_readdata(void *ptr, size_t size, size_t nmemb, void *userdata) { size_t copied = 0, aux_size = size * nmemb; struct np2srv_url_mem *data = (struct np2srv_url_mem *)userdata; if (aux_size < 1 || data->size == 0) { /* no space or nothing left */ return 0; } copied = (data->size > aux_size) ? aux_size : data->size; memcpy(ptr, data->memory, copied); data->memory = data->memory + copied; /* move pointer */ data->size = data->size - copied; /* decrease amount of data left */ return copied; } static int url_open(const char *url) { CURL *curl; CURLcode res; char curl_buffer[CURL_ERROR_SIZE]; char url_tmp_name[(sizeof P_tmpdir / sizeof(char)) + 15] = P_tmpdir "/np2srv-XXXXXX"; int url_tmpfile; /* prepare temporary file ... */ if ((url_tmpfile = mkstemp(url_tmp_name)) < 0) { ERR("Failed to create a temporary file (%s).", strerror(errno)); return -1; } /* and hide it from the file system */ unlink(url_tmp_name); DBG("Getting file from URL: %s (via curl)", url); /* set up libcurl */ curl_global_init(URL_INIT_FLAGS); curl = curl_easy_init(); curl_easy_setopt(curl, CURLOPT_URL, url); curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, url_writedata); curl_easy_setopt(curl, CURLOPT_WRITEDATA, &url_tmpfile); curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, curl_buffer); res = curl_easy_perform(curl); if (res != CURLE_OK) { ERR("Failed to download data (curl: %s).", curl_buffer); close(url_tmpfile); url_tmpfile = -1; } else { /* move back to the beginning of the output file */ lseek(url_tmpfile, 0, SEEK_SET); } /* cleanup */ curl_easy_cleanup(curl); curl_global_cleanup(); return url_tmpfile; } struct lyd_node * op_parse_url(const char *url, int options, int *rc, sr_session_ctx_t *sr_sess) { struct lyd_node *config, *data; struct ly_ctx *ly_ctx; int fd; ly_ctx = (struct ly_ctx *)sr_get_context(np2srv.sr_conn); fd = url_open(url); if (fd == -1) { *rc = SR_ERR_INVAL_ARG; sr_set_error(sr_sess, NULL, "Could not open URL."); return NULL; } /* do not validate the whole context, we just want to load the config anyxml */ config = lyd_parse_fd(ly_ctx, fd, LYD_XML, LYD_OPT_CONFIG | LYD_OPT_TRUSTED); if (ly_errno) { *rc = SR_ERR_LY; sr_set_error(sr_sess, ly_errpath(ly_ctx), ly_errmsg(ly_ctx)); return NULL; } data = op_parse_config((struct lyd_node_anydata *)config, options, rc, sr_sess); lyd_free_withsiblings(config); return data; } int op_export_url(const char *url, struct lyd_node *data, int options, int *rc, sr_session_ctx_t *sr_sess) { CURL *curl; CURLcode res; struct np2srv_url_mem mem_data; char curl_buffer[CURL_ERROR_SIZE], *str_data; struct lyd_node *config; struct ly_ctx *ly_ctx; ly_ctx = (struct ly_ctx *)sr_get_context(np2srv.sr_conn); config = lyd_new_path(NULL, ly_ctx, "/ietf-netconf:config", data, data ? LYD_ANYDATA_DATATREE : 0, 0); if (!config) { *rc = SR_ERR_LY; sr_set_error(sr_sess, ly_errpath(ly_ctx), ly_errmsg(ly_ctx)); return -1; } lyd_print_mem(&str_data, config, LYD_XML, options); /* do not free data */ ((struct lyd_node_anydata *)config)->value.tree = NULL; lyd_free_withsiblings(config); DBG("Uploading file to URL: %s (via curl)", url); /* fill the structure for libcurl's READFUNCTION */ mem_data.memory = str_data; mem_data.size = strlen(str_data); /* set up libcurl */ curl_global_init(URL_INIT_FLAGS); curl = curl_easy_init(); curl_easy_setopt(curl, CURLOPT_URL, url); curl_easy_setopt(curl, CURLOPT_UPLOAD, 1L); curl_easy_setopt(curl, CURLOPT_READDATA, &mem_data); curl_easy_setopt(curl, CURLOPT_READFUNCTION, url_readdata); curl_easy_setopt(curl, CURLOPT_INFILESIZE, (long)mem_data.size); curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, curl_buffer); res = curl_easy_perform(curl); free(str_data); if (res != CURLE_OK) { ERR("Failed to upload data (curl: %s).", curl_buffer); *rc = SR_ERR_SYS; sr_set_error(sr_sess, NULL, curl_buffer); return -1; } /* cleanup */ curl_easy_cleanup(curl); curl_global_cleanup(); return 0; } #else int np2srv_url_setcap(void) { return EXIT_SUCCESS; } #endif struct lyd_node * op_parse_config(struct lyd_node_anydata *config, int options, int *rc, sr_session_ctx_t *sr_sess) { struct ly_ctx *ly_ctx; struct lyd_node *root = NULL; ly_ctx = lyd_node_module((struct lyd_node *)config)->ctx; switch (config->value_type) { case LYD_ANYDATA_CONSTSTRING: case LYD_ANYDATA_STRING: case LYD_ANYDATA_SXML: root = lyd_parse_mem(ly_ctx, config->value.str, LYD_XML, options); break; case LYD_ANYDATA_DATATREE: root = lyd_dup_withsiblings(config->value.tree, LYD_DUP_OPT_RECURSIVE); break; case LYD_ANYDATA_XML: root = lyd_parse_xml(ly_ctx, &config->value.xml, options); break; case LYD_ANYDATA_LYB: root = lyd_parse_mem(ly_ctx, config->value.mem, LYD_LYB, options); break; case LYD_ANYDATA_JSON: case LYD_ANYDATA_JSOND: case LYD_ANYDATA_SXMLD: case LYD_ANYDATA_LYBD: EINT; *rc = SR_ERR_INTERNAL; return NULL; } if (ly_errno != LY_SUCCESS) { *rc = SR_ERR_LY; sr_set_error(sr_sess, ly_errpath(ly_ctx), ly_errmsg(ly_ctx)); } return root; } static int strws(const char *str) { while (*str) { if (!isspace(*str)) { return 0; } ++str; } return 1; } static int op_filter_xpath_add_filter(const char *new_filter, int selection, struct np2_filter *filter) { void *mem; mem = realloc(filter->filters, (filter->count + 1) * sizeof *filter->filters); if (!mem) { EMEM; return -1; } filter->filters = mem; filter->filters[filter->count].str = strdup(new_filter); filter->filters[filter->count].selection = selection; ++filter->count; return 0; } static int filter_xpath_buf_append_attrs(struct ly_ctx *ctx, const struct lyxml_attr *attr, char **buf, int size) { const struct lys_module *module; const struct lyxml_attr *next; int new_size; char *buf_new; LY_TREE_FOR(attr, next) { if (next->type == LYXML_ATTR_STD) { module = NULL; if (next->ns) { module = ly_ctx_get_module_by_ns(ctx, next->ns->value, NULL, 1); } if (!module) { /* attribute without namespace or with unknown one will not match anything anyway */ continue; } new_size = size + 2 + strlen(module->name) + 1 + strlen(next->name) + 2 + strlen(next->value) + 2; buf_new = realloc(*buf, new_size * sizeof(char)); if (!buf_new) { EMEM; return -1; } *buf = buf_new; sprintf((*buf) + (size - 1), "[@%s:%s='%s']", module->name, next->name, next->value); size = new_size; } } return size; } static char * filter_xpath_buf_get_content(struct ly_ctx *ctx, const struct lyxml_elem *elem) { const char *start; size_t len; char *ret; /* skip leading and trailing whitespaces */ for (start = elem->content; isspace(*start); ++start); for (len = strlen(start); isspace(start[len - 1]); --len); start = lydict_insert(ctx, start, len); ly_log_options(0); ret = ly_path_xml2json(ctx, start, (struct lyxml_elem *)elem); ly_log_options(LY_LOLOG | LY_LOSTORE_LAST); if (!ret) { ret = strdup(start); } lydict_remove(ctx, start); return ret; } /* top-level content node with optional namespace and attributes */ static int filter_xpath_buf_add_top_content(struct ly_ctx *ctx, const struct lyxml_elem *elem, const char *elem_module_name, struct np2_filter *filter) { int size; char *buf, *content; content = filter_xpath_buf_get_content(ctx, elem); size = 1 + strlen(elem_module_name) + 1 + strlen(elem->name) + 9 + strlen(content) + 3; buf = malloc(size); if (!buf) { EMEM; free(content); return -1; } sprintf(buf, "/%s:%s[text()='%s']", elem_module_name, elem->name, content); free(content); size = filter_xpath_buf_append_attrs(ctx, elem->attr, &buf, size); if (!size) { free(buf); return 0; } else if (size < 1) { free(buf); return -1; } if (op_filter_xpath_add_filter(buf, 0, filter)) { free(buf); return -1; } free(buf); return 0; } /* content node with optional namespace and attributes */ static int filter_xpath_buf_append_content(struct ly_ctx *ctx, const struct lyxml_elem *elem, const char *elem_module_name, const char *last_ns, char **buf, int size) { const struct lys_module *module; int new_size; char *buf_new, *content, quot; if (!elem_module_name && elem->ns && (elem->ns->value != last_ns) && strcmp(elem->ns->value, "urn:ietf:params:xml:ns:netconf:base:1.0")) { module = ly_ctx_get_module_by_ns(ctx, elem->ns->value, NULL, 1); if (!module) { /* not really an error */ return 0; } elem_module_name = module->name; } new_size = size + 1 + (elem_module_name ? strlen(elem_module_name) + 1 : 0) + strlen(elem->name); buf_new = realloc(*buf, new_size); if (!buf_new) { EMEM; return -1; } *buf = buf_new; sprintf((*buf) + (size - 1), "[%s%s%s", (elem_module_name ? elem_module_name : ""), (elem_module_name ? ":" : ""), elem->name); size = new_size; size = filter_xpath_buf_append_attrs(ctx, elem->attr, buf, size); if (size < 1) { return size; } content = filter_xpath_buf_get_content(ctx, elem); new_size = size + 2 + strlen(content) + 2; buf_new = realloc(*buf, new_size); if (!buf_new) { EMEM; free(content); return -1; } *buf = buf_new; if (strchr(content, '\'')) { quot = '\"'; } else { quot = '\''; } sprintf((*buf) + (size - 1), "=%c%s%c]", quot, content, quot); free(content); return new_size; } /* containment/selection node with optional namespace and attributes */ static int filter_xpath_buf_append_node(struct ly_ctx *ctx, const struct lyxml_elem *elem, const char *elem_module_name, const char *last_ns, char **buf, int size) { const struct lys_module *module; int new_size; char *buf_new; if (!elem_module_name && elem->ns && (elem->ns->value != last_ns) && strcmp(elem->ns->value, "urn:ietf:params:xml:ns:netconf:base:1.0")) { module = ly_ctx_get_module_by_ns(ctx, elem->ns->value, NULL, 1); if (!module) { /* not really an error */ return 0; } elem_module_name = module->name; } new_size = size + 1 + (elem_module_name ? strlen(elem_module_name) + 1 : 0) + strlen(elem->name); buf_new = realloc(*buf, new_size); if (!buf_new) { EMEM; return -1; } *buf = buf_new; sprintf((*buf) + (size - 1), "/%s%s%s", (elem_module_name ? elem_module_name : ""), (elem_module_name ? ":" : ""), elem->name); size = new_size; size = filter_xpath_buf_append_attrs(ctx, elem->attr, buf, size); return size; } static int filter_xpath_buf_add_r(struct ly_ctx *ctx, const struct lyxml_elem *elem, const char *elem_module_name, const char *last_ns, char **buf, int size, struct np2_filter *filter) { struct lyxml_elem *child; int s, only_content_match; /* containment node or selection node */ size = filter_xpath_buf_append_node(ctx, elem, elem_module_name, last_ns, buf, size); if (size < 1) { return size; } if (!elem->child) { /* just a selection node */ if (op_filter_xpath_add_filter(*buf, 1, filter)) { return -1; } return 0; } /* append child content match nodes */ only_content_match = 1; LY_TREE_FOR(elem->child, child) { if (!child->child && child->content && !strws(child->content)) { /* there is a content filter, append all of them */ size = filter_xpath_buf_append_content(ctx, child, elem_module_name, last_ns, buf, size); if (size < 1) { return size; } } else { only_content_match = 0; } } if (only_content_match) { /* there are only content match nodes so we retrieve this filter as a subtree */ if (op_filter_xpath_add_filter(*buf, 0, filter)) { return -1; } return 0; } /* else there are some other filters so the current filter just restricts all the nested ones, is not retrieved * as a standalone subtree */ /* that is it for this filter depth, now we branch with every new node */ LY_TREE_FOR(elem->child, child) { if (child->child) { /* child containment node */ filter_xpath_buf_add_r(ctx, child, NULL, last_ns, buf, size, filter); } else if (!child->content || strws(child->content)) { /* child selection node */ s = filter_xpath_buf_append_node(ctx, child, NULL, last_ns, buf, size); if (!s) { continue; } else if (s < 0) { return s; } if (op_filter_xpath_add_filter(*buf, 1, filter)) { return -1; } } /* else child content node, already handled */ } return 0; } static int op_filter_build_xpath_from_subtree(struct ly_ctx *ctx, const struct lyxml_elem *elem, struct np2_filter *filter) { const struct lys_module *module, **modules, **modules_new; const struct lys_node *node; const struct lyxml_elem *iter; char *buf = NULL; uint32_t i, module_count; LY_TREE_FOR(elem, iter) { /* first filter node, it must always have a namespace */ modules = NULL; module_count = 0; if (iter->ns && strcmp(iter->ns->value, "urn:ietf:params:xml:ns:netconf:base:1.0")) { modules = malloc(sizeof *modules); if (!modules) { EMEM; goto error; } module_count = 1; modules[0] = ly_ctx_get_module_by_ns(ctx, iter->ns->value, NULL, 1); if (!modules[0]) { /* not really an error */ free(modules); continue; } } else { i = 0; while ((module = ly_ctx_get_module_iter(ctx, &i))) { node = NULL; while ((node = lys_getnext(node, NULL, module, 0))) { if (!strcmp(node->name, iter->name)) { modules_new = realloc(modules, (module_count + 1) * sizeof *modules); if (!modules_new) { EMEM; goto error; } ++module_count; modules = modules_new; modules[module_count - 1] = module; break; } } } } for (i = 0; i < module_count; ++i) { if (!iter->child && iter->content && !strws(iter->content)) { /* special case of top-level content match node */ if (filter_xpath_buf_add_top_content(ctx, iter, modules[i]->name, filter)) { goto error; } } else { /* containment or selection node */ if (filter_xpath_buf_add_r(ctx, iter, modules[i]->name, modules[i]->ns, &buf, 1, filter)) { goto error; } } } free(modules); } free(buf); return 0; error: free(buf); free(modules); op_filter_erase(filter); return -1; } void op_filter_erase(struct np2_filter *filter) { int i; for (i = 0; i < filter->count; ++i) { free(filter->filters[i].str); } free(filter->filters); filter->filters = NULL; filter->count = 0; } int op_filter_create(struct lyd_node *filter_node, struct np2_filter *filter) { struct lyd_attr *attr; struct lyxml_elem *subtree_filter; struct ly_ctx *ly_ctx; int free_filter, ret; ly_ctx = lyd_node_module(filter_node)->ctx; LY_TREE_FOR(filter_node->attr, attr) { if (!strcmp(attr->name, "type")) { if (!strcmp(attr->value_str, "xpath")) { LY_TREE_FOR(filter_node->attr, attr) { if (!strcmp(attr->name, "select")) { break; } } if (!attr) { ERR("RPC with an XPath filter without the \"select\" attribute."); return -1; } break; } else if (!strcmp(attr->value_str, "subtree")) { attr = NULL; break; } } } if (!attr) { /* subtree */ if (!((struct lyd_node_anydata *)filter_node)->value.str || (((struct lyd_node_anydata *)filter_node)->value_type <= LYD_ANYDATA_STRING && !((struct lyd_node_anydata *)filter_node)->value.str[0])) { /* empty filter, fair enough */ return 0; } switch (((struct lyd_node_anydata *)filter_node)->value_type) { case LYD_ANYDATA_CONSTSTRING: case LYD_ANYDATA_STRING: subtree_filter = lyxml_parse_mem(ly_ctx, ((struct lyd_node_anydata *)filter_node)->value.str, LYXML_PARSE_MULTIROOT); free_filter = 1; break; case LYD_ANYDATA_XML: subtree_filter = ((struct lyd_node_anydata *)filter_node)->value.xml; free_filter = 0; break; default: /* filter cannot be parsed as lyd_node tree */ return -1; } if (!subtree_filter) { return -1; } ret = op_filter_build_xpath_from_subtree(ly_ctx, subtree_filter, filter); if (free_filter) { lyxml_free_withsiblings(ly_ctx, subtree_filter); } if (ret) { return -1; } } else { /* xpath */ if (!attr->value_str || !attr->value_str[0]) { /* empty select, okay, I guess... */ return 0; } if (op_filter_xpath_add_filter(attr->value_str, 1, filter)) { return -1; } } return 0; } int op_filter_data_get(sr_session_ctx_t *session, uint32_t max_depth, sr_get_oper_options_t get_opts, const struct np2_filter *filter, sr_session_ctx_t *ev_sess, struct lyd_node **data) { const sr_error_info_t *err_info; struct lyd_node *node; int i, rc; for (i = 0; i < filter->count; ++i) { /* get the selected data */ rc = sr_get_data(session, filter->filters[i].str, max_depth, np2srv.sr_timeout, get_opts, &node); if (rc) { ERR("Getting data \"%s\" from sysrepo failed (%s).", filter->filters[i].str, sr_strerror(rc)); sr_get_error(session, &err_info); sr_set_error(ev_sess, err_info->err[0].xpath, err_info->err[0].message); return rc; } /* merge */ if (!*data) { *data = node; } else if (node && lyd_merge(*data, node, LYD_OPT_DESTRUCT | LYD_OPT_EXPLICIT)) { lyd_free_withsiblings(node); return SR_ERR_LY; } } return SR_ERR_OK; } int op_filter_data_filter(struct lyd_node **data, const struct np2_filter *filter, int with_selection, struct lyd_node **filtered_data) { struct lyd_node *node; int i, has_filter = 0, rc = SR_ERR_OK; struct ly_set *set = NULL; uint32_t j; if (!*data) { /* nothing to filter */ return SR_ERR_OK; } for (i = 0; i < filter->count; i++) { if (!with_selection && filter->filters[i].selection) { continue; } has_filter = 1; /* apply content (or even selection) filter */ set = lyd_find_path(*data, filter->filters[i].str); if (!set) { rc = SR_ERR_LY; goto cleanup; } for (j = 0; j < set->number; ++j) { node = lyd_dup(set->set.d[j], LYD_DUP_OPT_RECURSIVE | LYD_DUP_OPT_WITH_PARENTS | LYD_DUP_OPT_WITH_KEYS | LYD_DUP_OPT_WITH_WHEN); if (!node) { rc = SR_ERR_LY; goto cleanup; } /* always find parent */ while (node->parent) { node = node->parent; } /* merge */ if (!*filtered_data) { *filtered_data = node; } else if (node && lyd_merge(*filtered_data, node, LYD_OPT_DESTRUCT | LYD_OPT_EXPLICIT)) { lyd_free_withsiblings(node); rc = SR_ERR_LY; goto cleanup; } } ly_set_free(set); set = NULL; } if (!has_filter) { /* no filter, just use all the data */ *filtered_data = *data; *data = NULL; } cleanup: ly_set_free(set); return rc; } netopeer2-1.1.70/src/common.h0000664000000000000000000000623514021433500014431 0ustar rootroot/** * @file common.h * @author Michal Vasko <mvasko@cesnet.cz> * @brief netopeer2-server common structures and functions * * Copyright (c) 2019 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 NP2SRV_COMMON_H_ #define NP2SRV_COMMON_H_ #include <stdint.h> #include <sys/types.h> #include <time.h> #include <pthread.h> #include <nc_server.h> #include <sysrepo.h> #include "compat.h" #include "config.h" /* server internal data */ struct np2srv { sr_conn_ctx_t *sr_conn; /**< sysrepo connection */ sr_session_ctx_t *sr_sess; /**< sysrepo server session */ sr_subscription_ctx_t *sr_rpc_sub; /**< sysrepo RPC subscription context */ sr_subscription_ctx_t *sr_data_sub; /**< sysrepo data subscription context */ sr_subscription_ctx_t *sr_notif_sub; /**< sysrepo notification subscription context */ const char *unix_path; /**< path to the UNIX socket to listen on, if any */ mode_t unix_mode; /**< UNIX socket mode */ uid_t unix_uid; /**< UNIX socket UID */ gid_t unix_gid; /**< UNIX socket GID */ uint32_t sr_timeout; /**< timeout in ms for all sysrepo functions */ struct nc_pollsession *nc_ps; /**< libnetconf2 pollsession structure */ pthread_t workers[NP2SRV_THREAD_COUNT]; /**< worker threads handling sessions */ }; extern struct np2srv np2srv; extern ATOMIC_T skip_nacm_sr_sid; int np_sleep(uint32_t ms); const char *np_get_nc_sess_user(sr_session_ctx_t *session); sr_session_ctx_t *np_get_user_sess(sr_session_ctx_t *ev_sess); void np2srv_ntf_new_cb(sr_session_ctx_t *session, const sr_ev_notif_type_t notif_type, const struct lyd_node *notif, time_t timestamp, void *private_data); void np2srv_new_session_cb(const char *client_name, struct nc_session *new_session); int np2srv_url_setcap(void); #ifdef NP2SRV_URL_CAPAB struct lyd_node *op_parse_url(const char *url, int options, int *rc, sr_session_ctx_t *sr_sess); int op_export_url(const char *url, struct lyd_node *data, int options, int *rc, sr_session_ctx_t *sr_sess); #endif struct lyd_node *op_parse_config(struct lyd_node_anydata *config, int options, int *rc, sr_session_ctx_t *sr_sess); struct np2_filter { struct { char *str; int selection; /**< selection or content filter */ } *filters; int count; }; void op_filter_erase(struct np2_filter *filter); int op_filter_create(struct lyd_node *filter_node, struct np2_filter *filter); /** * @brief Get all data matching the selection filters. */ int op_filter_data_get(sr_session_ctx_t *session, uint32_t max_depth, sr_get_oper_options_t get_opts, const struct np2_filter *filter, sr_session_ctx_t *ev_sess, struct lyd_node **data); /** * @brief Filter out only the data matching the content filters. */ int op_filter_data_filter(struct lyd_node **data, const struct np2_filter *filter, int with_selection, struct lyd_node **filtered_data); #endif /* NP2SRV_COMMON_H_ */ netopeer2-1.1.70/src/config.h.in0000664000000000000000000000636314021433500015015 0ustar rootroot/** * @file config.h * @author Michal Vasko <mvasko@cesnet.cz> * @brief Various configuration constants for netopeer2-server * * Copyright (c) 2019 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 NP2SRV_CONFIG_H_ #define NP2SRV_CONFIG_H_ /** @brief Netopeer2 Server version */ #define NP2SRV_VERSION "@NP2SRV_VERSION@" /** @brief Netopeer2 Server PID file path * The default path /var/run/netopeer2-server.pid follows * the Filesystem Hierarchy Standard */ #define NP2SRV_PID_FILE_PATH "@PIDFILE_PREFIX@/netopeer2-server.pid" /** @brief Netopeer2 Server UNIX socket file path * The default path /var/run/netopeer2-server.sock follows * the Filesystem Hierarchy Standard */ #define NP2SRV_UNIX_SOCK_PATH "@PIDFILE_PREFIX@/netopeer2-server.sock" /** @brief Maximum number of threads handling session requests */ #ifndef NP2SRV_THREAD_COUNT # define NP2SRV_THREAD_COUNT @THREAD_COUNT@ #endif /** @brief NACM recovery session UID */ #define NP2SRV_NACM_RECOVERY_UID @NACM_RECOVERY_UID@ /** @brief Timeout for nc_ps_poll() call */ #define NP2SRV_POLL_IO_TIMEOUT @POLL_IO_TIMEOUT@ /** @brief Starting allocated length for a message */ #define NP2SRV_MSG_LEN_START 128 /** @brief Timeout for sending notifications (ms) * Should never be needed to be increased, libnetconf2 * handles concurrency well. */ #define NP2SRV_NOTIF_SEND_TIMEOUT 1000 /** @brief Timeout for PS structure accessing in * case there is too much contention (ms). */ #define NP2SRV_PS_BACKOFF_SLEEP 200 /** @brief URL capability support */ #cmakedefine NP2SRV_URL_CAPAB /** @brief availability of atomic variables */ #cmakedefine HAVE_STDATOMIC #ifdef HAVE_STDATOMIC # include <stdatomic.h> # define ATOMIC_T atomic_uint_fast32_t # define ATOMIC_STORE_FENCE(var, x) atomic_store_explicit(&(var), x, memory_order_release) # define ATOMIC_INC_FENCE(var) atomic_fetch_add_explicit(&(var), 1, memory_order_release) # define ATOMIC_DEC_FENCE(var) atomic_fetch_sub_explicit(&(var), 1, memory_order_release) # define ATOMIC_LOAD_FENCE(var) atomic_load_explicit(&(var), memory_order_acquire) # 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) #else # define ATOMIC_T uint32_t # define ATOMIC_STORE_FENCE(var, x) ((var) = (x)) # define ATOMIC_INC_FENCE(var) __sync_add_and_fetch(&(var), 1) # define ATOMIC_DEC_FENCE(var) __sync_sub_and_fetch(&(var), 1) # define ATOMIC_LOAD_FENCE(var) (var) # define ATOMIC_STORE_RELAXED(var, x) ATOMIC_STORE_FENCE(var, x) # define ATOMIC_LOAD_RELAXED(var) ATOMIC_LOAD_FENCE(var) #endif /** @brief unused compiler attribute */ #define UNUSED(x) UNUSED_ ## x __attribute__((__unused__)) /** @brief printf-like pattern for path to the authorized_keys file */ #define NP2SRV_SSH_AUTHORIZED_KEYS_PATTERN "@NP2SRV_SSH_AUTHORIZED_KEYS_PATTERN@" /** @brief Replace %s in NP2SRV_SSH_AUTHORIZED_KEYS_PATTERN by username (1), or by the home dir (0) */ #cmakedefine01 NP2SRV_SSH_AUTHORIZED_KEYS_ARG_IS_USERNAME #endif /* NP2SRV_CONFIG_H_ */ netopeer2-1.1.70/src/log.c0000664000000000000000000001527714021433500013723 0ustar rootroot/** * @file log.c * @author Michal Vasko <mvasko@cesnet.cz> * @brief netopeer2-server log functions * * Copyright (c) 2019 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 _DEFAULT_SOURCE #include <errno.h> #include <stdarg.h> #include <string.h> #include <stdio.h> #include <sys/types.h> #include <syslog.h> #include <unistd.h> #include <nc_server.h> #include <libyang/libyang.h> #include <sysrepo.h> #include "common.h" #include "log.h" volatile uint8_t np2_verbose_level; uint8_t np2_libssh_verbose_level; uint8_t np2_sr_verbose_level; uint8_t np2_stderr_log; static void np2log(int priority, const char *src, const char *fmt, ...) { char *format; va_list ap; va_start(ap, fmt); vsyslog(priority, fmt, ap); va_end(ap); if (np2_stderr_log) { format = malloc(11 + strlen(fmt) + 2); if (!format) { fprintf(stderr, "[ERR]: Memory allocation failed (%s:%d), src: %s, fmt: %s\n", __FILE__, __LINE__, src, fmt); return; } switch (priority) { case LOG_ERR: sprintf(format, "[ERR]: %s: %s\n", src, fmt); break; case LOG_WARNING: sprintf(format, "[WRN]: %s: %s\n", src, fmt); break; case LOG_INFO: sprintf(format, "[INF]: %s: %s\n", src, fmt); break; case LOG_DEBUG: sprintf(format, "[DBG]: %s: %s\n", src, fmt); break; default: sprintf(format, "[UNK]: %s: %s\n", src, fmt); break; } va_start(ap, fmt); vfprintf(stderr, format, ap); va_end(ap); free(format); } } /** * @brief Encode message characters (% -> %%) to avoid printf arg problems. */ static const char * np2log_encode(const char *msg, char **buf) { const char *ptr1, *ptr2; size_t buf_len, buf_size = 1; void *mem; *buf = NULL; if ((ptr2 = strchr(msg, '%'))) { /* something to encode */ ptr1 = msg; do { /* enlarge buffer */ buf_len = buf_size - 1; buf_size += (ptr2 - ptr1) + 2; mem = realloc(*buf, buf_size * sizeof **buf); if (!mem) { EMEM; return ""; } *buf = mem; /* copy preceding message */ strncpy(*buf + buf_len, ptr1, ptr2 - ptr1); buf_len += ptr2 - ptr1; /* copy % */ strcpy(*buf + buf_len, "%%"); /* next iter */ ptr1 = ptr2 + 1; } while ((ptr2 = strchr(ptr1, '%'))); /* copy remaining message */ buf_len = buf_size - 1; buf_size += strlen(ptr1); mem = realloc(*buf, buf_size * sizeof **buf); if (!mem) { EMEM; return ""; } *buf = mem; strcpy(*buf + buf_len, ptr1); } return (*buf ? *buf : msg); } /** * @brief printer callback for libnetconf2 */ void np2log_cb_nc2(NC_VERB_LEVEL level, const char *msg) { int priority = LOG_ERR; const char *log_msg; char *buf; if (level > np2_verbose_level) { return; } switch (level) { case NC_VERB_ERROR: priority = LOG_ERR;; break; case NC_VERB_WARNING: priority = LOG_WARNING; break; case NC_VERB_VERBOSE: priority = LOG_INFO; break; case NC_VERB_DEBUG: case NC_VERB_DEBUG_LOWLVL: priority = LOG_DEBUG; break; } log_msg = np2log_encode(msg, &buf); np2log(priority, "LN", log_msg); free(buf); } /** * @brief printer callback for libyang */ void np2log_cb_ly(LY_LOG_LEVEL level, const char *msg, const char *path) { int priority; const char *log_msg; char *buf; if (level > np2_verbose_level) { return; } switch (level) { case LY_LLERR: priority = LOG_ERR; break; case LY_LLWRN: priority = LOG_WARNING; break; case LY_LLVRB: priority = LOG_INFO; break; case LY_LLDBG: priority = LOG_DEBUG; break; default: /* silent, just to cover enum, shouldn't be here in real world */ return; } if (path) { np2log(priority, "LY", "%s (%s)", msg, path); } else { log_msg = np2log_encode(msg, &buf); np2log(priority, "LY", log_msg); free(buf); } } void np2log_cb_sr(sr_log_level_t level, const char *msg) { int priority = LOG_ERR; const char *log_msg; char *buf; if (level > np2_sr_verbose_level) { return; } switch (level) { case SR_LL_ERR: priority = LOG_ERR; break; case SR_LL_WRN: priority = LOG_WARNING; break; case SR_LL_INF: priority = LOG_INFO; break; case SR_LL_DBG: priority = LOG_DEBUG; break; case SR_LL_NONE: return; } log_msg = np2log_encode(msg, &buf); np2log(priority, "SR", log_msg); free(buf); } /** * @brief Internal printing function, follows the levels from libnetconf2 * @param[in] level Verbose level * @param[in] format Formatting string */ void np2log_printf(NC_VERB_LEVEL level, const char *format, ...) { va_list ap, ap2; ssize_t msg_len = NP2SRV_MSG_LEN_START, req_len; char *msg, *mem; int priority = LOG_ERR; if (level > np2_verbose_level) { return; } va_start(ap, format); va_copy(ap2, ap); /* initial length */ msg = malloc(msg_len); if (!msg) { goto cleanup; } /* learn how much bytes are needed */ req_len = vsnprintf(msg, msg_len, format, ap); if (req_len == -1) { goto cleanup; } else if (req_len >= NP2SRV_MSG_LEN_START) { /* the intial size was not enough */ msg_len = req_len + 1; mem = realloc(msg, msg_len); if (!mem) { free(msg); goto cleanup; } msg = mem; /* now print the full message */ req_len = vsnprintf(msg, msg_len, format, ap2); if (req_len == -1) { goto cleanup; } } switch (level) { case NC_VERB_ERROR: priority = LOG_ERR;; break; case NC_VERB_WARNING: priority = LOG_WARNING; break; case NC_VERB_VERBOSE: priority = LOG_INFO; break; case NC_VERB_DEBUG: case NC_VERB_DEBUG_LOWLVL: priority = LOG_DEBUG; break; } /* no need to encode in this case */ np2log(priority, "NP", msg); cleanup: free(msg); va_end(ap); va_end(ap2); } netopeer2-1.1.70/src/log.h0000664000000000000000000000360014021433500013713 0ustar rootroot/** * @file log.h * @author Michal Vasko <mvasko@cesnet.cz> * @brief netopeer2-server log functions * * Copyright (c) 2019 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 NP2SRV_LOG_H_ #define NP2SRV_LOG_H_ #include <nc_server.h> #include <sysrepo.h> /** * @brief Verbose level variable */ extern volatile uint8_t np2_verbose_level; /** * @brief libssh verbose level variable */ extern uint8_t np2_libssh_verbose_level; /** * @brief libsysrepo verbose level variable */ extern uint8_t np2_sr_verbose_level; /** * @brief netopeer2 flag whether to print messages to stderr (only if not daemon). */ extern uint8_t np2_stderr_log; /** * @brief internal printing function, follows the levels from libnetconf2 * @param[in] level Verbose level * @param[in] format Formatting string */ void np2log_printf(NC_VERB_LEVEL level, const char *format, ...); /* * Verbose printing macros */ #define ERR(format,args...) np2log_printf(NC_VERB_ERROR,format,##args) #define WRN(format,args...) np2log_printf(NC_VERB_WARNING,format,##args) #define VRB(format,args...) np2log_printf(NC_VERB_VERBOSE,format,##args) #define DBG(format,args...) np2log_printf(NC_VERB_DEBUG,format,##args) #define EMEM ERR("Memory allocation failed (%s:%d)", __FILE__, __LINE__) #define EINT ERR("Internal error (%s:%d)", __FILE__, __LINE__) /** * @brief printer callback for libnetconf2 */ void np2log_cb_nc2(NC_VERB_LEVEL level, const char *msg); /** * @brief printer callback for libyang */ void np2log_cb_ly(LY_LOG_LEVEL level, const char *msg, const char *path); /** * @brief printer callback for sysrepo */ void np2log_cb_sr(sr_log_level_t level, const char *msg); #endif /* NP2SRV_LOG_H_ */ netopeer2-1.1.70/src/main.c0000664000000000000000000013202214021433500014052 0ustar rootroot/** * @file main.c * @author Michal Vasko <mvasko@cesnet.cz> * @brief netopeer2-server - NETCONF server * * Copyright (c) 2019 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 #define _POSIX_C_SOUCRE 199309L #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <unistd.h> #include <string.h> #include <signal.h> #include <syslog.h> #include <errno.h> #include <stdio.h> #include <pwd.h> #include <grp.h> #include <libyang/libyang.h> #include <nc_server.h> #include <sysrepo.h> #include "config.h" #include "common.h" #include "log.h" #include "netconf.h" #if defined(NC_ENABLED_SSH) || defined(NC_ENABLED_TLS) # include "netconf_server.h" #endif #ifdef NC_ENABLED_SSH # include "netconf_server_ssh.h" #endif #ifdef NC_ENABLED_TLS # include "netconf_server_tls.h" #endif #include "netconf_acm.h" #include "netconf_monitoring.h" #include "netconf_nmda.h" /** @brief flag for main loop */ ATOMIC_T loop_continue = 1; /* SR SID of session to skip diff check for */ ATOMIC_T skip_nacm_sr_sid; static void *worker_thread(void *arg); static int np2srv_state_data_cb(sr_session_ctx_t *session, const char *module_name, const char *path, const char *request_xpath, uint32_t request_id, struct lyd_node **parent, void *private_data); /** * @brief Signal handler to control the process */ static void signal_handler(int sig) { static int quit = 0; switch (sig) { case SIGINT: case SIGTERM: case SIGQUIT: case SIGABRT: case SIGHUP: /* stop the process */ if (quit == 0) { /* first attempt */ quit = 1; } else { /* second attempt */ exit(EXIT_FAILURE); } ATOMIC_STORE_RELAXED(loop_continue, 0); break; default: exit(EXIT_FAILURE); } } static struct lyd_node * np2srv_ntf_get_data(sr_conn_ctx_t *sr_conn) { struct lyd_node *root, *stream, *sr_data = NULL, *sr_mod, *rep_sup; struct ly_set *set; const struct ly_ctx *ly_ctx; const char *mod_name; char buf[26]; int rc; ly_ctx = sr_get_context(sr_conn); root = lyd_new_path(NULL, ly_ctx, "/nc-notifications:netconf/streams", NULL, 0, 0); if (!root || !root->child) { goto error; } /* generic stream */ stream = lyd_new_path(root, ly_ctx, "/nc-notifications:netconf/streams/stream[name='NETCONF']", NULL, 0, 0); if (!stream) { goto error; } if (!lyd_new_leaf(stream, stream->schema->module, "description", "Default NETCONF stream containing notifications from all the modules." " Replays only notifications for modules that support replay.")) { goto error; } if (!lyd_new_leaf(stream, stream->schema->module, "replaySupport", "true")) { goto error; } /* go through all the sysrepo modules */ rc = sr_get_module_info(sr_conn, &sr_data); if (rc != SR_ERR_OK) { ERR("Failed to get sysrepo module info data (%s).", sr_strerror(rc)); goto error; } LY_TREE_FOR(sr_data->child, sr_mod) { mod_name = ((struct lyd_node_leaf_list *)sr_mod->child)->value_str; /* generate information about the stream/module */ stream = lyd_new(root->child, NULL, "stream"); if (!stream) { goto error; } if (!lyd_new_leaf(stream, NULL, "name", mod_name)) { goto error; } if (!lyd_new_leaf(stream, NULL, "description", "Stream with all notifications of a module.")) { goto error; } set = lyd_find_path(sr_mod, "replay-support"); if (!set) { EINT; goto error; } if (set->number == 1) { rep_sup = set->set.d[0]; } else { rep_sup = NULL; } ly_set_free(set); if (!lyd_new_leaf(stream, NULL, "replaySupport", rep_sup ? "true" : "false")) { goto error; } if (rep_sup) { nc_time2datetime(((struct lyd_node_leaf_list *)rep_sup)->value.uint64, NULL, buf); if (!lyd_new_leaf(stream, NULL, "replayLogCreationTime", buf)) { goto error; } } } lyd_free_withsiblings(sr_data); return root; error: lyd_free(root); lyd_free_withsiblings(sr_data); return NULL; } static int np2srv_state_data_cb(sr_session_ctx_t *UNUSED(session), const char *module_name, const char *path, const char *UNUSED(request_xpath), uint32_t UNUSED(request_id), struct lyd_node **parent, void *UNUSED(private_data)) { struct lyd_node *data = NULL, *node; struct ly_set *set = NULL; int ret = SR_ERR_OK; /* get the full module state data tree */ if (!strcmp(module_name, "ietf-netconf-monitoring")) { data = ncm_get_data(np2srv.sr_conn); } else if (!strcmp(module_name, "nc-notifications")) { data = np2srv_ntf_get_data(np2srv.sr_conn); } else { EINT; ret = SR_ERR_OPERATION_FAILED; goto cleanup; } /* find the requested top-level subtree */ set = lyd_find_path(data, path); if (!set || !set->number) { ret = SR_ERR_OPERATION_FAILED; goto cleanup; } node = set->set.d[0]; if (node->parent || *parent) { EINT; ret = SR_ERR_OPERATION_FAILED; goto cleanup; } /* return the subtree */ if (node == data) { data = data->next; } lyd_unlink(node); *parent = node; /* success */ cleanup: ly_set_free(set); lyd_free_withsiblings(data); return ret; } #if defined(NC_ENABLED_SSH) || defined(NC_ENABLED_TLS) static int np2srv_dummy_cb(sr_session_ctx_t *UNUSED(session), const char *UNUSED(module_name), const char *UNUSED(xpath), sr_event_t UNUSED(event), uint32_t UNUSED(request_id), void *UNUSED(private_data)) { return SR_ERR_OK; } #endif static void np2srv_del_session_cb(struct nc_session *session) { int i, rc; char *host = NULL; sr_val_t *event_data; sr_session_ctx_t *sr_sess; const struct lys_module *mod; if (nc_ps_del_session(np2srv.nc_ps, session)) { ERR("Removing session from ps failed."); } /* stop sysrepo session (also stop any sysrepo notification subscriptions) */ sr_sess = nc_session_get_data(session); sr_session_stop(sr_sess); ncm_session_del(session); if ((mod = ly_ctx_get_module(sr_get_context(np2srv.sr_conn), "ietf-netconf-notifications", NULL, 1))) { /* generate ietf-netconf-notification's netconf-session-end event for sysrepo */ if (nc_session_get_ti(session) != NC_TI_UNIX) { host = (char *)nc_session_get_host(session); } event_data = calloc(5, sizeof *event_data); i = 0; event_data[i].xpath = "/ietf-netconf-notifications:netconf-session-end/username"; event_data[i].type = SR_STRING_T; event_data[i++].data.string_val = (char*)nc_session_get_username(session); event_data[i].xpath = "/ietf-netconf-notifications:netconf-session-end/session-id"; event_data[i].type = SR_UINT32_T; event_data[i++].data.uint32_val = nc_session_get_id(session); if (host) { event_data[i].xpath = "/ietf-netconf-notifications:netconf-session-end/source-host"; event_data[i].type = SR_STRING_T; event_data[i++].data.string_val = host; } if (nc_session_get_killed_by(session)) { event_data[i].xpath = "/ietf-netconf-notifications:netconf-session-end/killed-by"; event_data[i].type = SR_UINT32_T; event_data[i++].data.uint32_val = nc_session_get_killed_by(session); } event_data[i].xpath = "/ietf-netconf-notifications:netconf-session-end/termination-reason"; event_data[i].type = SR_ENUM_T; switch (nc_session_get_term_reason(session)) { case NC_SESSION_TERM_CLOSED: event_data[i++].data.enum_val = "closed"; break; case NC_SESSION_TERM_KILLED: event_data[i++].data.enum_val = "killed"; break; case NC_SESSION_TERM_DROPPED: event_data[i++].data.enum_val = "dropped"; break; case NC_SESSION_TERM_TIMEOUT: event_data[i++].data.enum_val = "timeout"; break; default: event_data[i++].data.enum_val = "other"; break; } rc = sr_event_notif_send(np2srv.sr_sess, "/ietf-netconf-notifications:netconf-session-end", event_data, i); if (rc != SR_ERR_OK) { WRN("Failed to send a notification (%s).", sr_strerror(rc)); } else { VRB("Generated new event (netconf-session-end)."); } free(event_data); } nc_session_free(session, NULL); } static struct nc_server_error * np2srv_err_sr(int err_code, const char *message, const char *xpath) { struct nc_server_error *e; const char *ptr; switch (err_code) { case SR_ERR_LOCKED: err_lock_denied: ptr = strstr(message, "NC SID "); if (!ptr) { EINT; return NULL; } ptr += 7; e = nc_err(NC_ERR_LOCK_DENIED, atoi(ptr)); nc_err_set_msg(e, message, "en"); break; case SR_ERR_UNAUTHORIZED: err_access_denied: e = nc_err(NC_ERR_ACCESS_DENIED, NC_ERR_TYPE_PROT); nc_err_set_msg(e, message, "en"); if (xpath) { nc_err_set_path(e, xpath); } break; case SR_ERR_VALIDATION_FAILED: if (!strncmp(message, "When condition", 14)) { if (xpath) { EINT; return NULL; } e = nc_err(NC_ERR_UNKNOWN_ELEM, NC_ERR_TYPE_APP, xpath); nc_err_set_msg(e, message, "en"); break; } /* fallthrough */ default: if (strstr(message, "authorization failed")) { // access-denied goto err_access_denied; } else if (strstr(message, "is already locked") || strstr(message, "Module \"yang\" is locked by session")) { // lock-denied goto err_lock_denied; } else if (strstr(message, "is locked by session")) { // in-use e = nc_err(NC_ERR_IN_USE, NC_ERR_TYPE_PROT); nc_err_set_msg(e, message, "en"); if (xpath) { nc_err_set_path(e, xpath); } break; } else if (strstr(message, "already exists")) { // data-exists e = nc_err(NC_ERR_DATA_EXISTS); nc_err_set_msg(e, message, "en"); if (xpath) { nc_err_set_path(e, xpath); } break; } else if (strstr(message, "Source and target")) { e = nc_err(NC_ERR_INVALID_VALUE, NC_ERR_TYPE_PROT); nc_err_set_msg(e, message, "en"); break; } else if (strstr(message, "already subscribed")) { e = nc_err(NC_ERR_OP_FAILED, NC_ERR_TYPE_PROT); nc_err_set_msg(e, message, "en"); break; } e = nc_err(NC_ERR_OP_FAILED, NC_ERR_TYPE_APP); nc_err_set_msg(e, message, "en"); if (xpath) { nc_err_set_path(e, xpath); } break; } return e; } static struct nc_server_reply * np2srv_err_reply_sr(const sr_error_info_t *err_info) { struct nc_server_reply *reply = NULL; struct nc_server_error *e; size_t i; for (i = 0; i < err_info->err_count; ++i) { e = np2srv_err_sr(err_info->err_code, err_info->err[i].message, err_info->err[i].xpath); if (!e) { nc_server_reply_free(reply); return NULL; } if (reply) { nc_server_reply_add_err(reply, e); } else { reply = nc_server_reply_err(e); } e = NULL; } return reply; } static struct nc_server_reply * np2srv_rpc_cb(struct lyd_node *rpc, struct nc_session *ncs) { sr_session_ctx_t *sr_sess = NULL; const struct lyd_node *node; struct lyd_node_leaf_list *leaf; const sr_error_info_t *err_info; struct nc_server_reply *reply = NULL; struct lyd_node *output, *child = NULL; NC_WD_MODE nc_wd; struct ly_set *nodeset; struct nc_server_error *e; char *str; int rc; /* check NACM */ if ((node = ncac_check_operation(rpc, nc_session_get_username(ncs)))) { e = nc_err(NC_ERR_ACCESS_DENIED, NC_ERR_TYPE_APP); /* set path */ str = lys_data_path(node->schema); nc_err_set_path(e, str); free(str); /* set message */ asprintf(&str, "Executing the operation is denied because \"%s\" NACM authorization failed.", nc_session_get_username(ncs)); nc_err_set_msg(e, str, "en"); free(str); reply = nc_server_reply_err(e); goto cleanup; } /* get this user session with its NC id (but not user name) */ sr_sess = nc_session_get_data(ncs); /* sysrepo API, use the default timeout or slightly higher than the configured one */ rc = sr_rpc_send_tree(sr_sess, rpc, np2srv.sr_timeout ? np2srv.sr_timeout + 2000 : 0, &output); if (rc != SR_ERR_OK) { ERR("Failed to send an RPC (%s).", sr_strerror(rc)); goto cleanup; } /* build RPC Reply */ if (output) { LY_TREE_FOR(output->child, child) { if (!child->dflt) { break; } } } if (child) { /* get with-defaults mode */ if (!strcmp(lyd_node_module(rpc)->name, "ietf-netconf")) { /* augment */ nodeset = lyd_find_path(rpc, "ietf-netconf-with-defaults:with-defaults"); } else { /* grouping */ nodeset = lyd_find_path(rpc, "with-defaults"); } if (nodeset->number) { leaf = (struct lyd_node_leaf_list *)nodeset->set.d[0]; if (!strcmp(leaf->value_str, "report-all")) { nc_wd = NC_WD_ALL; } else if (!strcmp(leaf->value_str, "report-all-tagged")) { nc_wd = NC_WD_ALL_TAG; } else if (!strcmp(leaf->value_str, "trim")) { nc_wd = NC_WD_TRIM; } else { nc_wd = NC_WD_EXPLICIT; } } else { nc_server_get_capab_withdefaults(&nc_wd, NULL); } ly_set_free(nodeset); reply = nc_server_reply_data(output, nc_wd, NC_PARAMTYPE_FREE); } else { lyd_free_withsiblings(output); reply = nc_server_reply_ok(); } cleanup: if (!reply) { if (sr_sess) { sr_get_error(sr_sess, &err_info); reply = np2srv_err_reply_sr(err_info); } else { e = nc_err(NC_ERR_OP_FAILED, NC_ERR_TYPE_APP); reply = nc_server_reply_err(e); } } return reply; } static int np2srv_diff_check_cb(sr_session_ctx_t *session, const struct lyd_node *diff) { const struct lyd_node *node; char *path; const char *user; user = np_get_nc_sess_user(session); if (!user) { EINT; return SR_ERR_INTERNAL; } if (ATOMIC_LOAD_RELAXED(skip_nacm_sr_sid) == sr_session_get_event_sr_id(session)) { /* skip the NACM check */ return SR_ERR_OK; } if ((node = ncac_check_diff(diff, user))) { /* access denied */ path = lys_data_path(node->schema); sr_set_error(session, path, "Access to the data model \"%s\" is denied because \"%s\" NACM authorization failed.", lyd_node_module(node)->name, user); free(path); return SR_ERR_UNAUTHORIZED; } return SR_ERR_OK; } static int np2srv_check_schemas(sr_session_ctx_t *sr_sess) { const char *mod_name; const struct lys_module *mod; const struct ly_ctx *ly_ctx; ly_ctx = sr_get_context(sr_session_get_connection(sr_sess)); /* check that internally used schemas are implemented and with required features: ietf-netconf, ... */ mod_name = "ietf-netconf"; mod = ly_ctx_get_module(ly_ctx, mod_name, NULL, 1); if (!mod || !mod->implemented) { ERR("Module \"%s\" not implemented in sysrepo.", mod_name); return -1; } if (lys_features_state(mod, "writable-running") != 1) { ERR("Module \"%s\" feature \"writable-running\" not enabled in sysrepo.", mod_name); return -1; } if (lys_features_state(mod, "candidate") != 1) { ERR("Module \"%s\" feature \"candidate\" not enabled in sysrepo.", mod_name); return -1; } if (lys_features_state(mod, "rollback-on-error") != 1) { ERR("Module \"%s\" feature \"rollback-on-error\" not enabled in sysrepo.", mod_name); return -1; } if (lys_features_state(mod, "validate") != 1) { ERR("Module \"%s\" feature \"validate\" not enabled in sysrepo.", mod_name); return -1; } if (lys_features_state(mod, "startup") != 1) { ERR("Module \"%s\" feature \"startup\" not enabled in sysrepo.", mod_name); return -1; } #ifdef NP2SRV_URL_CAPAB if (lys_features_state(mod, "url") != 1) { ERR("Module \"%s\" feature \"url\" not enabled in sysrepo.", mod_name); return -1; } #endif if (lys_features_state(mod, "xpath") != 1) { ERR("Module \"%s\" feature \"xpath\" not enabled in sysrepo.", mod_name); return -1; } /* ... ietf-netconf-acm, */ mod_name = "ietf-netconf-acm"; mod = ly_ctx_get_module(ly_ctx, mod_name, NULL, 1); if (!mod || !mod->implemented) { ERR("Module \"%s\" not implemented in sysrepo.", mod_name); return -1; } /* ... ietf-netconf-monitoring (leave get-schema RPC empty, libnetconf2 will use its callback), */ mod_name = "ietf-netconf-monitoring"; mod = ly_ctx_get_module(ly_ctx, mod_name, NULL, 1); if (!mod || !mod->implemented) { ERR("Module \"%s\" not implemented in sysrepo.", mod_name); return -1; } /* ... ietf-netconf-with-defaults, */ mod_name = "ietf-netconf-with-defaults"; mod = ly_ctx_get_module(ly_ctx, mod_name, NULL, 1); if (!mod || !mod->implemented) { ERR("Module \"%s\" not implemented in sysrepo.", mod_name); return -1; } /* ... ietf-netconf-notifications (must be implemented in sysrepo), */ mod_name = "ietf-netconf-notifications"; mod = ly_ctx_get_module(ly_ctx, mod_name, NULL, 1); if (!mod || !mod->implemented) { ERR("Module \"%s\" not implemented in sysrepo.", mod_name); return -1; } mod_name = "nc-notifications"; mod = ly_ctx_get_module(ly_ctx, mod_name, NULL, 1); if (!mod || !mod->implemented) { ERR("Module \"%s\" not implemented in sysrepo.", mod_name); return -1; } mod_name = "notifications"; mod = ly_ctx_get_module(ly_ctx, mod_name, NULL, 1); if (!mod || !mod->implemented) { ERR("Module \"%s\" not implemented in sysrepo.", mod_name); return -1; } mod_name = "ietf-yang-library"; mod = ly_ctx_get_module(ly_ctx, mod_name, NULL, 1); if (!mod || !mod->implemented) { ERR("Module \"%s\" not implemented in sysrepo.", mod_name); return -1; } /* .. ietf-netconf-server */ mod_name = "ietf-netconf-server"; mod = ly_ctx_get_module(ly_ctx, mod_name, NULL, 1); if (!mod || !mod->implemented) { ERR("Module \"%s\" not implemented in sysrepo.", mod_name); return -1; } if (lys_features_state(mod, "ssh-listen") != 1) { ERR("Module \"%s\" feature \"ssh-listen\" not enabled in sysrepo.", mod_name); return -1; } if (lys_features_state(mod, "ssh-call-home") != 1) { ERR("Module \"%s\" feature \"ssh-call-home\" not enabled in sysrepo.", mod_name); return -1; } return 0; } static int server_init(void) { const struct ly_ctx *ly_ctx; int rc; /* connect to the sysrepo and set edit-config NACM diff check callback */ rc = sr_connect(SR_CONN_CACHE_RUNNING, &np2srv.sr_conn); if (rc != SR_ERR_OK) { ERR("Connecting to sysrepo failed (%s).", sr_strerror(rc)); goto error; } sr_set_diff_check_callback(np2srv.sr_conn, np2srv_diff_check_cb); ly_ctx = sr_get_context(np2srv.sr_conn); /* server session */ rc = sr_session_start(np2srv.sr_conn, SR_DS_RUNNING, &np2srv.sr_sess); if (rc != SR_ERR_OK) { ERR("Creating sysrepo session failed (%s).", sr_strerror(rc)); goto error; } /* check libyang context */ if (np2srv_check_schemas(np2srv.sr_sess)) { goto error; } /* init monitoring */ ncm_init(); /* init NACM */ ncac_init(); /* init libnetconf2 (it modifies only the dictionary) */ if (nc_server_init((struct ly_ctx *)ly_ctx)) { goto error; } /* prepare poll session structure for libnetconf2 */ np2srv.nc_ps = nc_ps_new(); /* set with-defaults capability basic-mode */ nc_server_set_capab_withdefaults(NC_WD_EXPLICIT, NC_WD_ALL | NC_WD_ALL_TAG | NC_WD_TRIM | NC_WD_EXPLICIT); /* set capabilities for the NETCONF Notifications */ nc_server_set_capability("urn:ietf:params:netconf:capability:notification:1.0"); nc_server_set_capability("urn:ietf:params:netconf:capability:interleave:1.0"); /* set URL capability */ if (np2srv_url_setcap()) { goto error; } /* set libnetconf2 global PRC callback */ nc_set_global_rpc_clb(np2srv_rpc_cb); #ifdef NC_ENABLED_SSH /* set libnetconf2 SSH callbacks */ nc_server_ssh_set_hostkey_clb(np2srv_hostkey_cb, NULL, NULL); nc_server_ssh_set_pubkey_auth_clb(np2srv_pubkey_auth_cb, NULL, NULL); #endif #ifdef NC_ENABLED_TLS /* set libnetconf2 TLS callbacks */ nc_server_tls_set_server_cert_clb(np2srv_cert_cb, NULL, NULL); nc_server_tls_set_trusted_cert_list_clb(np2srv_cert_list_cb, NULL, NULL); #endif /* UNIX socket */ if (np2srv.unix_path) { if (nc_server_add_endpt("unix", NC_TI_UNIX)) { goto error; } if (nc_server_endpt_set_perms("unix", np2srv.unix_mode, np2srv.unix_uid, np2srv.unix_gid)) { goto error; } if (nc_server_endpt_set_address("unix", np2srv.unix_path)) { goto error; } } return 0; error: ERR("Server init failed."); return -1; } static int server_rpc_subscribe(void) { int rc; #define SR_RPC_SUBSCR(xpath, cb) \ rc = sr_rpc_subscribe_tree(np2srv.sr_sess, xpath, cb, NULL, 0, SR_SUBSCR_CTX_REUSE, &np2srv.sr_rpc_sub); \ if (rc != SR_ERR_OK) { \ ERR("Subscribing for \"%s\" RPC failed (%s).", xpath, sr_strerror(rc)); \ goto error; \ } /* subscribe to standard supported RPCs */ if (np2srv.sr_rpc_sub) { EINT; goto error; } SR_RPC_SUBSCR("/ietf-netconf:get-config", np2srv_rpc_get_cb); SR_RPC_SUBSCR("/ietf-netconf:edit-config", np2srv_rpc_editconfig_cb); SR_RPC_SUBSCR("/ietf-netconf:copy-config", np2srv_rpc_copyconfig_cb); SR_RPC_SUBSCR("/ietf-netconf:delete-config", np2srv_rpc_deleteconfig_cb); SR_RPC_SUBSCR("/ietf-netconf:lock", np2srv_rpc_un_lock_cb); SR_RPC_SUBSCR("/ietf-netconf:unlock", np2srv_rpc_un_lock_cb); SR_RPC_SUBSCR("/ietf-netconf:get", np2srv_rpc_get_cb); /* keep close-session empty so that internal lnc2 callback is used */ SR_RPC_SUBSCR("/ietf-netconf:kill-session", np2srv_rpc_kill_cb); SR_RPC_SUBSCR("/ietf-netconf:commit", np2srv_rpc_commit_cb); SR_RPC_SUBSCR("/ietf-netconf:discard-changes", np2srv_rpc_discard_cb); SR_RPC_SUBSCR("/ietf-netconf:validate", np2srv_rpc_validate_cb); /* subscribe to create-subscription */ SR_RPC_SUBSCR("/notifications:create-subscription", np2srv_rpc_subscribe_cb); /* subscribe to NMDA RPCs */ SR_RPC_SUBSCR("/ietf-netconf-nmda:get-data", np2srv_rpc_getdata_cb); SR_RPC_SUBSCR("/ietf-netconf-nmda:edit-data", np2srv_rpc_editdata_cb); return 0; error: ERR("Server RPC subscribe failed."); return -1; } static int server_data_subscribe(void) { const char *mod_name, *xpath; int rc; #define SR_OPER_SUBSCR(mod_name, xpath, cb) \ rc = sr_oper_get_items_subscribe(np2srv.sr_sess, mod_name, xpath, cb, NULL, SR_SUBSCR_CTX_REUSE, &np2srv.sr_data_sub); \ if (rc != SR_ERR_OK) { \ ERR("Subscribing for providing \"%s\" state data failed (%s).", mod_name, sr_strerror(rc)); \ goto error; \ } #define SR_CONFIG_SUBSCR(mod_name, xpath, cb) \ rc = sr_module_change_subscribe(np2srv.sr_sess, mod_name, xpath, cb, NULL, 0, \ SR_SUBSCR_CTX_REUSE | SR_SUBSCR_DONE_ONLY | SR_SUBSCR_ENABLED, &np2srv.sr_data_sub); \ if (rc != SR_ERR_OK) { \ ERR("Subscribing for \"%s\" data changes failed (%s).", mod_name, sr_strerror(rc)); \ goto error; \ } /* subscribe for providing state data */ if (np2srv.sr_data_sub) { EINT; goto error; } mod_name = "ietf-netconf-monitoring"; SR_OPER_SUBSCR(mod_name, "/ietf-netconf-monitoring:netconf-state", np2srv_state_data_cb); mod_name = "nc-notifications"; SR_OPER_SUBSCR(mod_name, "/nc-notifications:netconf", np2srv_state_data_cb); /* * ietf-netconf-server */ mod_name = "ietf-netconf-server"; #if defined(NC_ENABLED_SSH) || defined(NC_ENABLED_TLS) xpath = "/ietf-netconf-server:netconf-server/listen/idle-timeout"; SR_CONFIG_SUBSCR(mod_name, xpath, np2srv_idle_timeout_cb); #endif #ifdef NC_ENABLED_SSH /* subscribe for server SSH listen configuration changes */ xpath = "/ietf-netconf-server:netconf-server/listen/endpoint/ssh"; SR_CONFIG_SUBSCR(mod_name, xpath, np2srv_endpt_ssh_cb); xpath = "/ietf-netconf-server:netconf-server/listen/endpoint/ssh/tcp-server-parameters"; SR_CONFIG_SUBSCR(mod_name, xpath, np2srv_endpt_tcp_params_cb); xpath = "/ietf-netconf-server:netconf-server/listen/endpoint/ssh/ssh-server-parameters/server-identity/host-key/" "public-key/keystore-reference"; SR_CONFIG_SUBSCR(mod_name, xpath, np2srv_endpt_ssh_hostkey_cb); xpath = "/ietf-netconf-server:netconf-server/listen/endpoint/ssh/ssh-server-parameters/client-authentication/" "supported-authentication-methods"; SR_CONFIG_SUBSCR(mod_name, xpath, np2srv_endpt_ssh_auth_methods_cb); /* subscribe for providing SSH operational data */ xpath = "/ietf-netconf-server:netconf-server/listen/endpoint/ssh/ssh-server-parameters/client-authentication/users"; SR_OPER_SUBSCR(mod_name, xpath, np2srv_endpt_ssh_auth_users_oper_cb); #endif #ifdef NC_ENABLED_TLS /* subscribe for server TLS listen configuration changes */ xpath = "/ietf-netconf-server:netconf-server/listen/endpoint/tls"; SR_CONFIG_SUBSCR(mod_name, xpath, np2srv_endpt_tls_cb); xpath = "/ietf-netconf-server:netconf-server/listen/endpoint/tls/tcp-server-parameters"; SR_CONFIG_SUBSCR(mod_name, xpath, np2srv_endpt_tcp_params_cb); xpath = "/ietf-netconf-server:netconf-server/listen/endpoint/tls/tls-server-parameters/server-identity/keystore-reference"; SR_CONFIG_SUBSCR(mod_name, xpath, np2srv_endpt_tls_servercert_cb); xpath = "/ietf-netconf-server:netconf-server/listen/endpoint/tls/tls-server-parameters/client-authentication"; SR_CONFIG_SUBSCR(mod_name, xpath, np2srv_endpt_tls_client_auth_cb); xpath = "/ietf-netconf-server:netconf-server/listen/endpoint/tls/tls-server-parameters/client-authentication/cert-maps"; SR_CONFIG_SUBSCR(mod_name, xpath, np2srv_endpt_tls_client_ctn_cb); #endif #if defined(NC_ENABLED_SSH) || defined(NC_ENABLED_TLS) /* subscribe for generic Call Home configuration changes */ xpath = "/ietf-netconf-server:netconf-server/call-home/netconf-client"; SR_CONFIG_SUBSCR(mod_name, xpath, np2srv_ch_client_cb); xpath = "/ietf-netconf-server:netconf-server/call-home/netconf-client/connection-type"; SR_CONFIG_SUBSCR(mod_name, xpath, np2srv_ch_connection_type_cb); xpath = "/ietf-netconf-server:netconf-server/call-home/netconf-client/reconnect-strategy"; SR_CONFIG_SUBSCR(mod_name, xpath, np2srv_ch_reconnect_strategy_cb); #endif #ifdef NC_ENABLED_SSH /* subscribe for server SSH Call Home configuration changes */ xpath = "/ietf-netconf-server:netconf-server/call-home/netconf-client/endpoints/endpoint/ssh"; SR_CONFIG_SUBSCR(mod_name, xpath, np2srv_ch_client_endpt_ssh_cb); xpath = "/ietf-netconf-server:netconf-server/call-home/netconf-client/endpoints/endpoint/ssh/tcp-client-parameters"; SR_CONFIG_SUBSCR(mod_name, xpath, np2srv_ch_client_endpt_tcp_params_cb); xpath = "/ietf-netconf-server:netconf-server/call-home/netconf-client/endpoints/endpoint/ssh/ssh-server-parameters/" "server-identity/host-key/public-key/keystore-reference"; SR_CONFIG_SUBSCR(mod_name, xpath, np2srv_ch_endpt_ssh_hostkey_cb); xpath = "/ietf-netconf-server:netconf-server/call-home/netconf-client/endpoints/endpoint/ssh/ssh-server-parameters/" "client-authentication/supported-authentication-methods"; SR_CONFIG_SUBSCR(mod_name, xpath, np2srv_ch_endpt_ssh_auth_methods_cb); #endif #ifdef NC_ENABLED_TLS /* subscribe for TLS Call Home configuration changes */ xpath = "/ietf-netconf-server:netconf-server/call-home/netconf-client/endpoints/endpoint/tls"; SR_CONFIG_SUBSCR(mod_name, xpath, np2srv_ch_client_endpt_tls_cb); xpath = "/ietf-netconf-server:netconf-server/call-home/netconf-client/endpoints/endpoint/tls/tcp-client-parameters"; SR_CONFIG_SUBSCR(mod_name, xpath, np2srv_ch_client_endpt_tcp_params_cb); xpath = "/ietf-netconf-server:netconf-server/call-home/netconf-client/endpoints/endpoint/tls/tls-server-parameters/" "server-identity/keystore-reference"; SR_CONFIG_SUBSCR(mod_name, xpath, np2srv_ch_client_endpt_tls_servercert_cb); xpath = "/ietf-netconf-server:netconf-server/call-home/netconf-client/endpoints/endpoint/tls/tls-server-parameters/" "client-authentication"; SR_CONFIG_SUBSCR(mod_name, xpath, np2srv_ch_client_endpt_tls_client_auth_cb); xpath = "/ietf-netconf-server:netconf-server/call-home/netconf-client/endpoints/endpoint/tls/tls-server-parameters/" "client-authentication/cert-maps"; SR_CONFIG_SUBSCR(mod_name, xpath, np2srv_ch_client_endpt_tls_client_ctn_cb); #endif #if defined(NC_ENABLED_SSH) || defined(NC_ENABLED_TLS) /* * ietf-keystore (just for in-use operational data) */ mod_name = "ietf-keystore"; xpath = "/ietf-keystore:keystore/asymmetric-keys"; SR_CONFIG_SUBSCR(mod_name, xpath, np2srv_dummy_cb); /* * ietf-truststore (just for in-use operational data) */ mod_name = "ietf-truststore"; xpath = "/ietf-truststore:truststore/certificates"; SR_CONFIG_SUBSCR(mod_name, xpath, np2srv_dummy_cb); #endif /* * ietf-netconf-acm */ mod_name = "ietf-netconf-acm"; xpath = "/ietf-netconf-acm:nacm"; SR_CONFIG_SUBSCR(mod_name, xpath, ncac_nacm_params_cb); xpath = "/ietf-netconf-acm:nacm/groups/group"; SR_CONFIG_SUBSCR(mod_name, xpath, ncac_group_cb); xpath = "/ietf-netconf-acm:nacm/rule-list"; SR_CONFIG_SUBSCR(mod_name, xpath, ncac_rule_list_cb); xpath = "/ietf-netconf-acm:nacm/rule-list/rule"; SR_CONFIG_SUBSCR(mod_name, xpath, ncac_rule_cb); /* state data */ xpath = "/ietf-netconf-acm:nacm/denied-operations"; SR_OPER_SUBSCR(mod_name, xpath, ncac_state_data_cb); xpath = "/ietf-netconf-acm:nacm/denied-data-writes"; SR_OPER_SUBSCR(mod_name, xpath, ncac_state_data_cb); xpath = "/ietf-netconf-acm:nacm/denied-notifications"; SR_OPER_SUBSCR(mod_name, xpath, ncac_state_data_cb); return 0; error: ERR("Server data subscribe failed."); return -1; } static void * worker_thread(void *arg) { NC_MSG_TYPE msgtype; int rc, idx = *((int *)arg); struct nc_session *ncs; #ifdef NC_ENABLED_SSH nc_libssh_thread_verbosity(np2_libssh_verbose_level); #endif while (ATOMIC_LOAD_RELAXED(loop_continue)) { /* try to accept new NETCONF sessions */ if (nc_server_endpt_count()) { msgtype = nc_accept(0, &ncs); if (msgtype == NC_MSG_HELLO) { np2srv_new_session_cb(NULL, ncs); } } /* listen for incoming requests on active NETCONF sessions */ rc = nc_ps_poll(np2srv.nc_ps, NP2SRV_POLL_IO_TIMEOUT, &ncs); if ((rc & (NC_PSPOLL_NOSESSIONS | NC_PSPOLL_TIMEOUT | NC_PSPOLL_ERROR)) && !(rc & NC_PSPOLL_SESSION_TERM)) { /* if there is no active session, timeout, or an error, rest for a while */ np_sleep(NP2SRV_PS_BACKOFF_SLEEP); continue; } /* process the result of nc_ps_poll(), increase counters */ if (rc & NC_PSPOLL_BAD_RPC) { ncm_session_bad_rpc(ncs); VRB("Session %d: thread %d event bad RPC.", nc_session_get_id(ncs), idx); } if (rc & NC_PSPOLL_RPC) { ncm_session_rpc(ncs); VRB("Session %d: thread %d event new RPC.", nc_session_get_id(ncs), idx); } if (rc & NC_PSPOLL_REPLY_ERROR) { ncm_session_rpc_reply_error(ncs); VRB("Session %d: thread %d event reply error.", nc_session_get_id(ncs), idx); } if (rc & NC_PSPOLL_SESSION_TERM) { VRB("Session %d: thread %d event session terminated.", nc_session_get_id(ncs), idx); np2srv_del_session_cb(ncs); } #ifdef NC_ENABLED_SSH else if (rc & NC_PSPOLL_SSH_CHANNEL) { /* a new SSH channel on existing session was created */ VRB("Session %d: thread %d event new SSH channel.", nc_session_get_id(ncs), idx); msgtype = nc_session_accept_ssh_channel(ncs, &ncs); if (msgtype == NC_MSG_HELLO) { np2srv_new_session_cb(NULL, ncs); } else if (msgtype == NC_MSG_BAD_HELLO) { ncm_bad_hello(ncs); } } #endif } /* cleanup */ #if defined(NC_ENABLED_SSH) || defined(NC_ENABLED_TLS) nc_thread_destroy(); #endif free(arg); return NULL; } static void print_version(void) { fprintf(stdout, "netopeer2-server %s\n", NP2SRV_VERSION); fprintf(stdout, "compile time: %s, %s\n", __DATE__, __TIME__); } static void print_usage(char* progname) { fprintf(stdout, "Usage: %s [-dhV] [-p path] [-U (path)] [-m mode] [-u uid] [-g gid] [-t timeout] [-v level] [-c category]\n", progname); fprintf(stdout, " -d debug mode (do not daemonize and print verbose messages to stderr instead of syslog)\n"); fprintf(stdout, " -h display help\n"); fprintf(stdout, " -V show program version\n"); fprintf(stdout, " -p path path to pidfile (default path is \"%s\")\n", NP2SRV_PID_FILE_PATH); fprintf(stdout, " -U[path] listen on a local UNIX socket (default path is \"%s\")\n", NP2SRV_UNIX_SOCK_PATH); fprintf(stdout, " -m mode set mode for the listening UNIX socket\n"); fprintf(stdout, " -u uid set UID/user for the listening UNIX socket\n"); fprintf(stdout, " -g gid set GID/group for the listening UNIX socket\n"); fprintf(stdout, " -t timeout timeout in seconds of all sysrepo functions (applying edit-config, reading data, ...),\n"); fprintf(stdout, " if 0 (default), the default sysrepo timeouts are used\n"); fprintf(stdout, " -v level verbose output level:\n"); fprintf(stdout, " 0 - errors\n"); fprintf(stdout, " 1 - errors and warnings\n"); fprintf(stdout, " 2 - errors, warnings, and verbose messages\n"); #ifndef NDEBUG fprintf(stdout, " -c category[,category]*\n"); fprintf(stdout, " verbose debug level, print only these debug message categories\n"); # ifdef NC_ENABLED_SSH fprintf(stdout, " categories: DICT, YANG, YIN, XPATH, DIFF, MSG, LN2DBG, SSH, SYSREPO\n"); # else fprintf(stdout, " categories: DICT, YANG, YIN, XPATH, DIFF, MSG, LN2DBG, SYSREPO\n"); # endif #else fprintf(stdout, " -c category[,category]*\n"); fprintf(stdout, " verbose debug level, NOT SUPPORTED in release build type\n"); #endif fprintf(stdout, "\n"); } int main(int argc, char *argv[]) { int ret = EXIT_SUCCESS; int c, *idx, i; int daemonize = 1, verb = 0; int pidfd; const char *pidfile = NP2SRV_PID_FILE_PATH; char pid[8]; char *ptr; struct passwd *pwd; struct group *grp; struct nc_session *sess; struct sigaction action; sigset_t block_mask; /* until daemonized, write messages to both syslog and stderr */ openlog("netopeer2-server", LOG_PID, LOG_DAEMON); np2_stderr_log = 1; /* process command line options */ while ((c = getopt(argc, argv, "dhVp:U::m:u:g:t:v:c:")) != -1) { switch (c) { case 'd': daemonize = 0; break; case 'h': print_usage(argv[0]); return EXIT_SUCCESS; case 'v': if (verb) { ERR("Do not combine -v and -c parameters."); return EXIT_FAILURE; } verb = 1; c = atoi(optarg); /* normalize verbose level */ np2_verbose_level = (c > NC_VERB_ERROR) ? ((c > NC_VERB_VERBOSE) ? NC_VERB_VERBOSE : c) : NC_VERB_ERROR; switch (np2_verbose_level) { case NC_VERB_ERROR: np2_sr_verbose_level = SR_LL_ERR; np2_libssh_verbose_level = 0; break; case NC_VERB_WARNING: np2_sr_verbose_level = SR_LL_WRN; np2_libssh_verbose_level = 1; break; case NC_VERB_VERBOSE: np2_sr_verbose_level = SR_LL_INF; np2_libssh_verbose_level = 1; break; } nc_verbosity(np2_verbose_level); #ifdef NC_ENABLED_SSH nc_libssh_thread_verbosity(np2_libssh_verbose_level); #endif break; case 'V': print_version(); return EXIT_SUCCESS; case 'p': pidfile = optarg; break; case 'U': np2srv.unix_path = optarg ? optarg : NP2SRV_UNIX_SOCK_PATH; break; case 'm': np2srv.unix_mode = strtoul(optarg, &ptr, 8); if (*ptr || (np2srv.unix_mode > 0777)) { ERR("Invalid UNIX socket mode \"%s\".", optarg); return EXIT_FAILURE; } break; case 'u': np2srv.unix_uid = strtoul(optarg, &ptr, 10); if (*ptr) { pwd = getpwnam(optarg); if (!pwd) { ERR("Invalid UNIX socket UID/user \"%s\".", optarg); return EXIT_FAILURE; } np2srv.unix_uid = pwd->pw_uid; } break; case 'g': np2srv.unix_gid = strtoul(optarg, &ptr, 10); if (*ptr) { grp = getgrnam(optarg); if (!grp) { ERR("Invalid UNIX socket GID/group \"%s\".", optarg); return EXIT_FAILURE; } np2srv.unix_gid = grp->gr_gid; } break; case 't': np2srv.sr_timeout = strtoul(optarg, &ptr, 10); if (*ptr) { ERR("Invalid timeout value \"%s\".", optarg); return EXIT_FAILURE; } /* make ms from s */ np2srv.sr_timeout *= 1000; break; case 'c': #ifndef NDEBUG if (verb) { ERR("Do not combine -v and -c parameters."); return EXIT_FAILURE; } /* set verbose for all, we change to debug later if requested */ np2_verbose_level = NC_VERB_VERBOSE; np2_libssh_verbose_level = 1; ptr = strtok(optarg, ","); do { if (!strcmp(ptr, "DICT")) { verb |= LY_LDGDICT; } else if (!strcmp(ptr, "YANG")) { verb |= LY_LDGYANG; } else if (!strcmp(ptr, "YIN")) { verb |= LY_LDGYIN; } else if (!strcmp(ptr, "XPATH")) { verb |= LY_LDGXPATH; } else if (!strcmp(ptr, "DIFF")) { verb |= LY_LDGDIFF; } else if (!strcmp(ptr, "MSG")) { /* NETCONF messages - only lnc2 debug verbosity */ np2_verbose_level = NC_VERB_DEBUG; } else if (!strcmp(ptr, "LN2DBG")) { np2_verbose_level = NC_VERB_DEBUG_LOWLVL; # ifdef NC_ENABLED_SSH } else if (!strcmp(ptr, "SSH")) { /* 2 should be always enough, 3 is too much useless info */ np2_libssh_verbose_level = 2; # endif } else if (!strcmp(ptr, "SYSREPO")) { np2_sr_verbose_level = SR_LL_DBG; } else { ERR("Unknown debug message category \"%s\", use -h.", ptr); return EXIT_FAILURE; } } while ((ptr = strtok(NULL, ","))); /* set final verbosity */ nc_verbosity(np2_verbose_level); # ifdef NC_ENABLED_SSH nc_libssh_thread_verbosity(np2_libssh_verbose_level); # endif if (verb) { ly_verb(LY_LLDBG); ly_verb_dbg(verb); } verb = 1; break; #else WRN("-c parameter not supported in release build type."); break; #endif default: print_usage(argv[0]); return EXIT_SUCCESS; } } /* daemonize */ if (daemonize == 1) { if (daemon(0, 0) != 0) { ERR("Daemonizing the server failed (%s).", strerror(errno)); return EXIT_FAILURE; } /* from now print only to syslog, not stderr */ np2_stderr_log = 0; } /* make sure we are the only instance - lock the PID file and write the PID */ pidfd = open(pidfile, O_RDWR | O_CREAT, 0640); if (pidfd < 0) { ERR("Unable to open the PID file \"%s\" (%s).", pidfile, strerror(errno)); return EXIT_FAILURE; } if (lockf(pidfd, F_TLOCK, 0) < 0) { close(pidfd); if (errno == EACCES || errno == EAGAIN) { ERR("Another instance of the Netopeer2 server is running."); } else { ERR("Unable to lock the PID file \"%s\" (%s).", pidfile, strerror(errno)); } return EXIT_FAILURE; } if (ftruncate(pidfd, 0)) { ERR("Failed to truncate PID file (%s).", strerror(errno)); close(pidfd); return EXIT_FAILURE; } c = snprintf(pid, sizeof(pid), "%d\n", getpid()); if (write(pidfd, pid, c) < c) { ERR("Failed to write into PID file."); close(pidfd); return EXIT_FAILURE; } close(pidfd); /* set the signal handler */ sigfillset(&block_mask); action.sa_handler = signal_handler; action.sa_mask = block_mask; action.sa_flags = 0; sigaction(SIGINT, &action, NULL); sigaction(SIGQUIT, &action, NULL); sigaction(SIGABRT, &action, NULL); sigaction(SIGTERM, &action, NULL); sigaction(SIGHUP, &action, NULL); /* ignore SIGPIPE */ action.sa_handler = SIG_IGN; sigaction(SIGPIPE, &action, NULL); /* set printer callbacks for the used libraries and set proper log levels */ nc_set_print_clb(np2log_cb_nc2); /* libnetconf2 */ ly_set_log_clb(np2log_cb_ly, 1); /* libyang */ sr_log_set_cb(np2log_cb_sr); /* sysrepo, log level is checked by callback */ /* initiate NETCONF server */ if (server_init()) { ret = EXIT_FAILURE; goto cleanup; } /* subscribe to sysrepo */ if (server_rpc_subscribe()) { ret = EXIT_FAILURE; goto cleanup; } if (server_data_subscribe()) { ret = EXIT_FAILURE; goto cleanup; } /* start additional worker threads */ for (i = 1; i < NP2SRV_THREAD_COUNT; ++i) { idx = malloc(sizeof *idx); *idx = i; pthread_create(&np2srv.workers[*idx], NULL, worker_thread, idx); } /* one worker will use this thread */ np2srv.workers[0] = pthread_self(); idx = malloc(sizeof *idx); *idx = 0; worker_thread(idx); /* wait for other worker threads to finish */ for (i = 1; i < NP2SRV_THREAD_COUNT; ++i) { c = pthread_join(np2srv.workers[i], NULL); if (c) { ERR("Failed to join worker thread %d (%s).", i, strerror(c)); } } cleanup: VRB("Server terminated."); /* stop subscriptions */ sr_unsubscribe(np2srv.sr_rpc_sub); sr_unsubscribe(np2srv.sr_data_sub); sr_unsubscribe(np2srv.sr_notif_sub); #if defined(NC_ENABLED_SSH) || defined(NC_ENABLED_TLS) /* remove all CH clients so they do not reconnect */ nc_server_ch_del_client(NULL); #endif /* close all open sessions */ if (np2srv.nc_ps) { while (nc_ps_session_count(np2srv.nc_ps)) { sess = nc_ps_get_session(np2srv.nc_ps, 0); nc_session_set_term_reason(sess, NC_SESSION_TERM_OTHER); np2srv_del_session_cb(sess); } nc_ps_free(np2srv.nc_ps); } /* libnetconf2 cleanup */ nc_server_destroy(); /* monitoring cleanup */ ncm_destroy(); /* NACM cleanup */ ncac_destroy(); /* removes the context and clears all the sessions */ sr_disconnect(np2srv.sr_conn); return ret; } netopeer2-1.1.70/src/netconf.c0000664000000000000000000010210014021433500014554 0ustar rootroot/** * @file netconf.c * @author Michal Vasko <mvasko@cesnet.cz> * @brief ietf-netconf callbacks * * Copyright (c) 2019 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 #define _DEFAULT_SOURCE #include <stdio.h> #include <assert.h> #include <inttypes.h> #include <unistd.h> #include <string.h> #include <pthread.h> #include <errno.h> #include <ctype.h> #include <sysrepo.h> #include <libyang/libyang.h> #include "common.h" #include "log.h" #include "netconf_acm.h" static int np2srv_get_first_ns(const char *expr, const char **start, int *len) { int i; if (expr[0] != '/') { return -1; } if (expr[1] == '/') { expr += 2; } else { ++expr; } if (!isalpha(expr[0]) && (expr[0] != '_')) { return -1; } for (i = 1; expr[i] && (isalnum(expr[i]) || (expr[i] == '_') || (expr[i] == '-') || (expr[i] == '.')); ++i); if (expr[i] != ':') { return -1; } *start = expr; *len = i; return 0; } /** * @brief Get generic filters in the form of "/module:*" from exact xpath filters. */ static int np2srv_get_rpc_module_filters(const struct np2_filter *filter, struct np2_filter *mod_filter) { int i, j, len, selection; const char *start; char *str; for (i = 0; i < filter->count; ++i) { if (np2srv_get_first_ns(filter->filters[i].str, &start, &len)) { /* not the simple format, use it as it is */ str = strdup(filter->filters[i].str); selection = filter->filters[i].selection; } else { /* get all the data of a module */ if (asprintf(&str, "/%.*s:*", len, start) == -1) { str = NULL; } selection = 1; } if (!str) { EMEM; return SR_ERR_NOMEM; } /* check for a duplicity */ for (j = 0; j < mod_filter->count; ++j) { if (!strcmp(str, mod_filter->filters[j].str)) { break; } } if (j < mod_filter->count) { free(str); continue; } /* add a new module filter */ mod_filter->filters = realloc(mod_filter->filters, (mod_filter->count + 1) * sizeof *mod_filter->filters); mod_filter->filters[mod_filter->count].str = str; mod_filter->filters[mod_filter->count].selection = selection; ++mod_filter->count; } return SR_ERR_OK; } /** * @brief Get data for a get RPC. */ static int np2srv_get_rpc_data(sr_session_ctx_t *session, const struct np2_filter *filter, sr_session_ctx_t *ev_sess, struct lyd_node **data) { struct lyd_node *all_data = NULL; sr_datastore_t ds; sr_get_oper_options_t get_opts = 0; struct np2_filter mod_filter = {0}; int rc = SR_ERR_OK; struct ly_set *set = NULL; /* get generic filters to allow retrieving all possibly needed data first, which are then filtered again * (once we have merged config and state data) */ rc = np2srv_get_rpc_module_filters(filter, &mod_filter); if (rc) { goto cleanup; } /* get data from running first */ ds = SR_DS_RUNNING; get_sr_data: sr_session_switch_ds(session, ds); if ((rc = op_filter_data_get(session, 0, get_opts, &mod_filter, ev_sess, &all_data))) { goto cleanup; } if (ds == SR_DS_RUNNING) { /* we have running data, now append state data */ ds = SR_DS_OPERATIONAL; get_opts = SR_OPER_NO_CONFIG; goto get_sr_data; } /* now filter only the requested data from the created running data + state data */ if ((rc = op_filter_data_filter(&all_data, filter, 1, data))) { goto cleanup; } cleanup: ly_set_free(set); lyd_free_withsiblings(all_data); op_filter_erase(&mod_filter); return rc; } /** * @brief get data for a get-config RPC. */ static int np2srv_getconfig_rpc_data(sr_session_ctx_t *session, const struct np2_filter *filter, sr_datastore_t ds, sr_session_ctx_t *ev_sess, struct lyd_node **data) { struct lyd_node *select_data = NULL; int rc = SR_ERR_OK; /* update sysrepo session datastore */ sr_session_switch_ds(session, ds); /* * create the data tree for the data reply */ if ((rc = op_filter_data_get(session, 0, 0, filter, ev_sess, &select_data))) { goto cleanup; } if ((rc = op_filter_data_filter(&select_data, filter, 0, data))) { goto cleanup; } cleanup: lyd_free_withsiblings(select_data); return rc; } int np2srv_rpc_get_cb(sr_session_ctx_t *session, const char *op_path, const struct lyd_node *input, sr_event_t event, uint32_t UNUSED(request_id), struct lyd_node *output, void *UNUSED(private_data)) { struct lyd_node *node, *data_get = NULL; struct np2_filter filter = {0}; int rc = SR_ERR_OK; sr_session_ctx_t *user_sess; struct ly_set *nodeset; sr_datastore_t ds = 0; char *username = NULL; if (event == SR_EV_ABORT) { /* ignore in this case */ return SR_ERR_OK; } /* Get username. It is assumed that right now the NETCONF session cannot end * due to the RPC lock held while np2srv_rpc_cb() is executing (which called this callback). */ if ((username = (char *)np_get_nc_sess_user(session))) { if (!(username = strdup(username))) { EMEM; rc = SR_ERR_NOMEM; goto cleanup; } } else { EINT; rc = SR_ERR_INTERNAL; goto cleanup; } /* get know which datastore is being affected for get-config */ if (!strcmp(op_path, "/ietf-netconf:get-config")) { nodeset = lyd_find_path(input, "source/*"); if (!strcmp(nodeset->set.d[0]->schema->name, "running")) { ds = SR_DS_RUNNING; } else if (!strcmp(nodeset->set.d[0]->schema->name, "startup")) { ds = SR_DS_STARTUP; } else { assert(!strcmp(nodeset->set.d[0]->schema->name, "candidate")); ds = SR_DS_CANDIDATE; } ly_set_free(nodeset); } /* create filters */ nodeset = lyd_find_path(input, "filter"); if (nodeset->number) { node = nodeset->set.d[0]; ly_set_free(nodeset); if (op_filter_create(node, &filter)) { rc = SR_ERR_INTERNAL; goto cleanup; } } else { ly_set_free(nodeset); filter.filters = malloc(sizeof *filter.filters); if (!filter.filters) { EMEM; rc = SR_ERR_NOMEM; goto cleanup; } filter.count = 1; filter.filters[0].str = strdup("/*"); filter.filters[0].selection = 1; if (!filter.filters[0].str) { EMEM; rc = SR_ERR_NOMEM; goto cleanup; } } /* we do not care here about with-defaults mode, it does not change anything */ /* get the user session */ user_sess = np_get_user_sess(session); if (!user_sess) { EINT; rc = SR_ERR_INTERNAL; goto cleanup; } /* get filtered data */ if (!strcmp(op_path, "/ietf-netconf:get-config")) { rc = np2srv_getconfig_rpc_data(user_sess, &filter, ds, session, &data_get); } else { rc = np2srv_get_rpc_data(user_sess, &filter, session, &data_get); } if (rc) { goto cleanup; } /* perform correct NACM filtering */ ncac_check_data_read_filter(&data_get, username); /* add output */ node = lyd_new_output_anydata(output, NULL, "data", data_get, LYD_ANYDATA_DATATREE); if (!node) { goto cleanup; } data_get = NULL; /* success */ cleanup: op_filter_erase(&filter); lyd_free_withsiblings(data_get); free(username); return rc; } int np2srv_rpc_editconfig_cb(sr_session_ctx_t *session, const char *UNUSED(op_path), const struct lyd_node *input, sr_event_t event, uint32_t UNUSED(request_id), struct lyd_node *UNUSED(output), void *UNUSED(private_data)) { sr_datastore_t ds = 0; struct ly_set *nodeset; struct lyd_node *config = NULL; const sr_error_info_t *err_info; sr_session_ctx_t *user_sess = NULL; const char *str, *defop = "merge", *testop = "test-then-set"; int rc = SR_ERR_OK; if (event == SR_EV_ABORT) { /* ignore in this case */ return SR_ERR_OK; } /* get know which datastore is being affected */ nodeset = lyd_find_path(input, "target/*"); if (!strcmp(nodeset->set.d[0]->schema->name, "running")) { ds = SR_DS_RUNNING; } else { assert(!strcmp(nodeset->set.d[0]->schema->name, "candidate")); ds = SR_DS_CANDIDATE; } ly_set_free(nodeset); /* default-operation */ nodeset = lyd_find_path(input, "default-operation"); if (nodeset->number) { defop = ((struct lyd_node_leaf_list *)nodeset->set.d[0])->value_str; } ly_set_free(nodeset); /* test-option */ nodeset = lyd_find_path(input, "test-option"); if (nodeset->number) { testop = ((struct lyd_node_leaf_list *)nodeset->set.d[0])->value_str; if (!strcmp(testop, "set")) { VRB("edit-config test-option \"set\" not supported, validation will be performed."); testop = "test-then-set"; } } ly_set_free(nodeset); /* error-option */ nodeset = lyd_find_path(input, "error-option"); if (nodeset->number) { str = ((struct lyd_node_leaf_list *)nodeset->set.d[0])->value_str; if (strcmp(str, "rollback-on-error")) { VRB("edit-config error-option \"%s\" not supported, rollback-on-error will be performed.", str); } } ly_set_free(nodeset); /* config */ nodeset = lyd_find_path(input, "config | url"); if (!strcmp(nodeset->set.d[0]->schema->name, "config")) { config = op_parse_config((struct lyd_node_anydata *)nodeset->set.d[0], LYD_OPT_EDIT | LYD_OPT_STRICT, &rc, session); if (rc) { ly_set_free(nodeset); goto cleanup; } } else { assert(!strcmp(nodeset->set.d[0]->schema->name, "url")); #ifdef NP2SRV_URL_CAPAB config = op_parse_url(((struct lyd_node_leaf_list *)nodeset->set.d[0])->value_str, LYD_OPT_EDIT | LYD_OPT_STRICT | LYD_OPT_TRUSTED, &rc, session); if (rc) { ly_set_free(nodeset); goto cleanup; } #else ly_set_free(nodeset); rc = SR_ERR_UNSUPPORTED; sr_set_error(session, NULL, "URL not supported."); goto cleanup; #endif } ly_set_free(nodeset); /* get the user session */ user_sess = np_get_user_sess(session); if (!user_sess) { EINT; rc = SR_ERR_INTERNAL; goto cleanup; } /* update sysrepo session datastore */ sr_session_switch_ds(user_sess, ds); /* sysrepo API */ if (config) { rc = sr_edit_batch(user_sess, config, defop); if (rc != SR_ERR_OK) { goto cleanup; } } if (!strcmp(testop, "test-then-set")) { rc = sr_apply_changes(user_sess, np2srv.sr_timeout, 1); } else { assert(!strcmp(testop, "test-only")); rc = sr_validate(user_sess, NULL, 0); } if (rc != SR_ERR_OK) { sr_get_error(user_sess, &err_info); sr_set_error(session, err_info->err[0].xpath, err_info->err[0].message); goto cleanup; } /* success */ cleanup: if (user_sess) { /* discard any changes that possibly failed to be applied */ sr_discard_changes(user_sess); } lyd_free_withsiblings(config); return rc; } int np2srv_rpc_copyconfig_cb(sr_session_ctx_t *session, const char *UNUSED(op_path), const struct lyd_node *input, sr_event_t event, uint32_t UNUSED(request_id), struct lyd_node *UNUSED(output), void *UNUSED(private_data)) { sr_datastore_t ds = SR_DS_OPERATIONAL, sds = SR_DS_OPERATIONAL; struct ly_set *nodeset; const sr_error_info_t *err_info; struct lyd_node *config = NULL; int rc = SR_ERR_OK, run_to_start = 0; sr_session_ctx_t *user_sess; char *username = NULL; #ifdef NP2SRV_URL_CAPAB struct lyd_node_leaf_list *leaf; const char *trg_url = NULL; int lyp_wd_flag; #endif if (event == SR_EV_ABORT) { /* ignore in this case */ return SR_ERR_OK; } /* Get username. It is assumed that right now the NETCONF session cannot end * due to the RPC lock held while np2srv_rpc_cb() is executing (which called this callback). */ if ((username = (char *)np_get_nc_sess_user(session))) { if (!(username = strdup(username))) { EMEM; rc = SR_ERR_NOMEM; goto cleanup; } } else { EINT; rc = SR_ERR_INTERNAL; goto cleanup; } /* get know which datastores are affected */ nodeset = lyd_find_path(input, "target/*"); if (!strcmp(nodeset->set.d[0]->schema->name, "running")) { ds = SR_DS_RUNNING; } else if (!strcmp(nodeset->set.d[0]->schema->name, "startup")) { ds = SR_DS_STARTUP; } else if (!strcmp(nodeset->set.d[0]->schema->name, "candidate")) { ds = SR_DS_CANDIDATE; } else { assert(!strcmp(nodeset->set.d[0]->schema->name, "url")); #ifdef NP2SRV_URL_CAPAB trg_url = ((struct lyd_node_leaf_list *)nodeset->set.d[0])->value_str; #else ly_set_free(nodeset); rc = SR_ERR_UNSUPPORTED; sr_set_error(session, NULL, "URL not supported."); goto cleanup; #endif } ly_set_free(nodeset); nodeset = lyd_find_path(input, "source/*"); if (!strcmp(nodeset->set.d[0]->schema->name, "running")) { sds = SR_DS_RUNNING; if (ds == SR_DS_STARTUP) { /* special copy-config from running to startup that bypasses NACM */ run_to_start = 1; } } else if (!strcmp(nodeset->set.d[0]->schema->name, "startup")) { sds = SR_DS_STARTUP; } else if (!strcmp(nodeset->set.d[0]->schema->name, "candidate")) { sds = SR_DS_CANDIDATE; } else if (!strcmp(nodeset->set.d[0]->schema->name, "config")) { config = op_parse_config((struct lyd_node_anydata *)nodeset->set.d[0], LYD_OPT_CONFIG | LYD_OPT_STRICT, &rc, session); if (rc) { ly_set_free(nodeset); goto cleanup; } } else { assert(!strcmp(nodeset->set.d[0]->schema->name, "url")); #ifdef NP2SRV_URL_CAPAB if (trg_url && !strcmp(trg_url, ((struct lyd_node_leaf_list *)nodeset->set.d[0])->value_str)) { rc = SR_ERR_INVAL_ARG; sr_set_error(session, NULL, "Source and target URLs are the same."); goto cleanup; } config = op_parse_url(((struct lyd_node_leaf_list *)nodeset->set.d[0])->value_str, LYD_OPT_CONFIG | LYD_OPT_STRICT | LYD_OPT_TRUSTED, &rc, session); if (rc) { ly_set_free(nodeset); goto cleanup; } #else ly_set_free(nodeset); rc = SR_ERR_UNSUPPORTED; sr_set_error(session, NULL, "URL not supported."); goto cleanup; #endif } ly_set_free(nodeset); if (ds == sds) { rc = SR_ERR_INVAL_ARG; sr_set_error(session, NULL, "Source and target datastores are the same."); goto cleanup; } /* NACM checks */ if (!config && !run_to_start) { /* get source datastore data and filter them */ sr_session_switch_ds(session, sds); rc = sr_get_data(session, "/*", 0, np2srv.sr_timeout, 0, &config); if (rc != SR_ERR_OK) { goto cleanup; } ncac_check_data_read_filter(&config, username); } /* get the user session */ user_sess = np_get_user_sess(session); if (!user_sess) { EINT; rc = SR_ERR_INTERNAL; goto cleanup; } /* update sysrepo session datastore */ sr_session_switch_ds(user_sess, ds); /* sysrepo API/URL handling */ #ifdef NP2SRV_URL_CAPAB if (trg_url) { /* we need with-defaults flag in this case */ nodeset = lyd_find_path(input, "ietf-netconf-with-defaults:with-defaults"); lyp_wd_flag = 0; if (nodeset->number) { leaf = (struct lyd_node_leaf_list *)nodeset->set.d[0]; if (!strcmp(leaf->value_str, "report-all")) { lyp_wd_flag = LYP_WD_ALL; } else if (!strcmp(leaf->value_str, "report-all-tagged")) { lyp_wd_flag = LYP_WD_ALL_TAG; } else if (!strcmp(leaf->value_str, "trim")) { lyp_wd_flag = LYP_WD_TRIM; } else { assert(!strcmp(leaf->value_str, "explicit")); lyp_wd_flag = LYP_WD_EXPLICIT; } } ly_set_free(nodeset); if (op_export_url(trg_url, config, LYP_FORMAT | LYP_WITHSIBLINGS | lyp_wd_flag, &rc, session)) { goto cleanup; } } else #endif { if (config) { /* config is spent */ rc = sr_replace_config(user_sess, NULL, config, np2srv.sr_timeout, 1); config = NULL; } else { assert(run_to_start); /* set SID to skip NACM check, only one copy-config can be executed at once */ ATOMIC_STORE_RELAXED(skip_nacm_sr_sid, sr_session_get_id(user_sess)); rc = sr_copy_config(user_sess, NULL, sds, np2srv.sr_timeout, 1); ATOMIC_STORE_RELAXED(skip_nacm_sr_sid, 0); } if (rc != SR_ERR_OK) { sr_get_error(user_sess, &err_info); sr_set_error(session, err_info->err[0].xpath, err_info->err[0].message); goto cleanup; } } /* success */ cleanup: lyd_free_withsiblings(config); free(username); return rc; } int np2srv_rpc_deleteconfig_cb(sr_session_ctx_t *session, const char *UNUSED(op_path), const struct lyd_node *input, sr_event_t event, uint32_t UNUSED(request_id), struct lyd_node *UNUSED(output), void *UNUSED(private_data)) { sr_datastore_t ds = 0; struct ly_set *nodeset; int rc = SR_ERR_OK; sr_session_ctx_t *user_sess; const sr_error_info_t *err_info; #ifdef NP2SRV_URL_CAPAB struct lyd_node *config; const char *trg_url = NULL; #endif if (event == SR_EV_ABORT) { /* ignore in this case */ return SR_ERR_OK; } /* get know which datastore is affected */ nodeset = lyd_find_path(input, "target/*"); if (!strcmp(nodeset->set.d[0]->schema->name, "startup")) { ds = SR_DS_STARTUP; } else { assert(!strcmp(nodeset->set.d[0]->schema->name, "url")); #ifdef NP2SRV_URL_CAPAB trg_url = ((struct lyd_node_leaf_list *)nodeset->set.d[0])->value_str; #else ly_set_free(nodeset); rc = SR_ERR_UNSUPPORTED; sr_set_error(session, NULL, "URL not supported."); goto cleanup; #endif } ly_set_free(nodeset); /* get the user session */ user_sess = np_get_user_sess(session); if (!user_sess) { EINT; rc = SR_ERR_INTERNAL; goto cleanup; } /* update sysrepo session datastore */ sr_session_switch_ds(user_sess, ds); /* sysrepo API/URL handling */ #ifdef NP2SRV_URL_CAPAB if (trg_url) { /* import URL to check its validity */ config = op_parse_url(trg_url, LYD_OPT_CONFIG | LYD_OPT_STRICT | LYD_OPT_TRUSTED, &rc, session); if (rc) { goto cleanup; } lyd_free_withsiblings(config); /* upload empty config */ if (op_export_url(trg_url, NULL, 0, &rc, session)) { goto cleanup; } } else #endif { rc = sr_replace_config(user_sess, NULL, NULL, np2srv.sr_timeout, 1); if (rc != SR_ERR_OK) { sr_get_error(user_sess, &err_info); sr_set_error(session, err_info->err[0].xpath, err_info->err[0].message); goto cleanup; } } /* success */ cleanup: return rc; } int np2srv_rpc_un_lock_cb(sr_session_ctx_t *session, const char *UNUSED(op_path), const struct lyd_node *input, sr_event_t event, uint32_t UNUSED(request_id), struct lyd_node *UNUSED(output), void *UNUSED(private_data)) { sr_datastore_t ds = 0; struct ly_set *nodeset; sr_session_ctx_t *user_sess; const sr_error_info_t *err_info; int rc = SR_ERR_OK; if (event == SR_EV_ABORT) { /* ignore in this case */ return SR_ERR_OK; } /* get know which datastore is being affected */ nodeset = lyd_find_path(input, "target/*"); if (!strcmp(nodeset->set.d[0]->schema->name, "running")) { ds = SR_DS_RUNNING; } else if (!strcmp(nodeset->set.d[0]->schema->name, "startup")) { ds = SR_DS_STARTUP; } else { assert(!strcmp(nodeset->set.d[0]->schema->name, "candidate")); ds = SR_DS_CANDIDATE; } ly_set_free(nodeset); /* get the user session */ user_sess = np_get_user_sess(session); if (!user_sess) { EINT; rc = SR_ERR_INTERNAL; goto cleanup; } /* update sysrepo session datastore */ sr_session_switch_ds(user_sess, ds); /* sysrepo API */ if (!strcmp(input->schema->name, "lock")) { rc = sr_lock(user_sess, NULL); } else if (!strcmp(input->schema->name, "unlock")) { rc = sr_unlock(user_sess, NULL); } if (rc != SR_ERR_OK) { sr_get_error(user_sess, &err_info); sr_set_error(session, err_info->err[0].xpath, err_info->err[0].message); goto cleanup; } /* success */ cleanup: return rc; } int np2srv_rpc_kill_cb(sr_session_ctx_t *session, const char *UNUSED(op_path), const struct lyd_node *input, sr_event_t event, uint32_t UNUSED(request_id), struct lyd_node *UNUSED(output), void *UNUSED(private_data)) { struct nc_session *kill_sess; struct ly_set *nodeset; uint32_t kill_sid, i; int rc = SR_ERR_OK; if (event == SR_EV_ABORT) { /* ignore in this case */ return SR_ERR_OK; } nodeset = lyd_find_path(input, "session-id"); kill_sid = ((struct lyd_node_leaf_list *)nodeset->set.d[0])->value.uint32; ly_set_free(nodeset); if (kill_sid == sr_session_get_event_nc_id(session)) { rc = SR_ERR_INVAL_ARG; sr_set_error(session, NULL, "It is forbidden to kill own session."); goto cleanup; } for (i = 0; (kill_sess = nc_ps_get_session(np2srv.nc_ps, i)); ++i) { if (nc_session_get_id(kill_sess) == kill_sid) { break; } } if (!kill_sess) { rc = SR_ERR_INVAL_ARG; sr_set_error(session, NULL, "Session with the specified \"session-id\" not found."); goto cleanup; } /* kill the session */ nc_session_set_status(kill_sess, NC_STATUS_INVALID); nc_session_set_term_reason(kill_sess, NC_SESSION_TERM_KILLED); nc_session_set_killed_by(kill_sess, kill_sid); /* success */ cleanup: return rc; } int np2srv_rpc_commit_cb(sr_session_ctx_t *session, const char *UNUSED(op_path), const struct lyd_node *UNUSED(input), sr_event_t event, uint32_t UNUSED(request_id), struct lyd_node *UNUSED(output), void *UNUSED(private_data)) { int rc = SR_ERR_OK; sr_session_ctx_t *user_sess; const sr_error_info_t *err_info; if (event == SR_EV_ABORT) { /* ignore in this case */ return SR_ERR_OK; } /* get the user session */ user_sess = np_get_user_sess(session); if (!user_sess) { EINT; rc = SR_ERR_INTERNAL; goto cleanup; } /* update sysrepo session datastore */ sr_session_switch_ds(user_sess, SR_DS_RUNNING); /* sysrepo API */ rc = sr_copy_config(user_sess, NULL, SR_DS_CANDIDATE, np2srv.sr_timeout, 1); if (rc != SR_ERR_OK) { sr_get_error(user_sess, &err_info); sr_set_error(session, err_info->err[0].xpath, err_info->err[0].message); goto cleanup; } /* success */ cleanup: return rc; } int np2srv_rpc_discard_cb(sr_session_ctx_t *session, const char *UNUSED(op_path), const struct lyd_node *UNUSED(input), sr_event_t event, uint32_t UNUSED(request_id), struct lyd_node *UNUSED(output), void *UNUSED(private_data)) { int rc = SR_ERR_OK; sr_session_ctx_t *user_sess; const sr_error_info_t *err_info; if (event == SR_EV_ABORT) { /* ignore in this case */ return SR_ERR_OK; } /* get the user session */ user_sess = np_get_user_sess(session); if (!user_sess) { EINT; rc = SR_ERR_INTERNAL; goto cleanup; } /* update sysrepo session datastore */ sr_session_switch_ds(user_sess, SR_DS_CANDIDATE); /* sysrepo API */ rc = sr_copy_config(user_sess, NULL, SR_DS_RUNNING, np2srv.sr_timeout, 1); if (rc != SR_ERR_OK) { sr_get_error(user_sess, &err_info); sr_set_error(session, err_info->err[0].xpath, err_info->err[0].message); goto cleanup; } /* success */ cleanup: return rc; } int np2srv_rpc_validate_cb(sr_session_ctx_t *session, const char *UNUSED(op_path), const struct lyd_node *input, sr_event_t event, uint32_t UNUSED(request_id), struct lyd_node *UNUSED(output), void *UNUSED(private_data)) { sr_datastore_t ds; struct lyd_node *config = NULL; struct ly_set *nodeset; sr_session_ctx_t *user_sess; int rc = SR_ERR_OK; const sr_error_info_t *err_info; if (event == SR_EV_ABORT) { /* ignore in this case */ return SR_ERR_OK; } /* get know which datastore is affected */ nodeset = lyd_find_path(input, "source/*"); if (nodeset->number) { if (!strcmp(nodeset->set.d[0]->schema->name, "running")) { ds = SR_DS_RUNNING; } else if (!strcmp(nodeset->set.d[0]->schema->name, "startup")) { ds = SR_DS_STARTUP; } else if (!strcmp(nodeset->set.d[0]->schema->name, "candidate")) { ds = SR_DS_CANDIDATE; } else if (!strcmp(nodeset->set.d[0]->schema->name, "config")) { /* config is also validated now */ config = op_parse_config((struct lyd_node_anydata *)nodeset->set.d[0], LYD_OPT_CONFIG | LYD_OPT_STRICT, &rc, session); if (rc) { ly_set_free(nodeset); goto cleanup; } } else { assert(!strcmp(nodeset->set.d[0]->schema->name, "url")); #ifdef NP2SRV_URL_CAPAB config = op_parse_url(((struct lyd_node_leaf_list *)nodeset->set.d[0])->value_str, LYD_OPT_CONFIG | LYD_OPT_STRICT | LYD_OPT_TRUSTED, &rc, session); if (rc) { ly_set_free(nodeset); goto cleanup; } #else ly_set_free(nodeset); rc = SR_ERR_UNSUPPORTED; sr_set_error(session, NULL, "URL not supported."); goto cleanup; #endif } } ly_set_free(nodeset); if (!config) { /* get the user session */ user_sess = np_get_user_sess(session); if (!user_sess) { EINT; rc = SR_ERR_INTERNAL; goto cleanup; } /* update sysrepo session datastore */ sr_session_switch_ds(user_sess, ds); /* sysrepo API */ rc = sr_validate(user_sess, NULL, 0); if (rc != SR_ERR_OK) { sr_get_error(user_sess, &err_info); sr_set_error(session, err_info->err[0].xpath, err_info->err[0].message); goto cleanup; } } /* success */ cleanup: lyd_free_withsiblings(config); return rc; } static int np2srv_rpc_subscribe_append_str(const char *str, char **ret) { void *mem; int len; if (!*ret) { *ret = strdup(str); if (!*ret) { EMEM; return SR_ERR_NOMEM; } } else { len = strlen(*ret); mem = realloc(*ret, len + strlen(str) + 1); if (!mem) { EMEM; return SR_ERR_NOMEM; } *ret = mem; strcat(*ret + len, str); } return SR_ERR_OK; } int np2srv_rpc_subscribe_cb(sr_session_ctx_t *session, const char *UNUSED(op_path), const struct lyd_node *input, sr_event_t event, uint32_t UNUSED(request_id), struct lyd_node *UNUSED(output), void *UNUSED(private_data)) { struct ly_set *nodeset; const struct lys_module *ly_mod; struct lys_node *root, *next, *elem, *parent; struct nc_session *ncs; const char *stream; struct np2_filter filter = {0}; char *xp = NULL; time_t start = 0, stop = 0; int rc = SR_ERR_OK, i; uint32_t idx; if (event == SR_EV_ABORT) { /* ignore in this case (not supported) */ return SR_ERR_OK; } /* find this NETCONF session */ for (i = 0; (ncs = nc_ps_get_session(np2srv.nc_ps, i)); ++i) { if (nc_session_get_id(ncs) == sr_session_get_event_nc_id(session)) { break; } } if (!ncs) { ERR("Failed to find NETCONF session SID %u.", sr_session_get_event_nc_id(session)); rc = SR_ERR_INTERNAL; goto cleanup; } /* RFC 5277 section 6.5 */ if (nc_session_get_notif_status(ncs)) { sr_set_error(session, NULL, "Session already subscribed."); return SR_ERR_EXISTS; } /* learn stream */ nodeset = lyd_find_path(input, "stream"); stream = ((struct lyd_node_leaf_list *)nodeset->set.d[0])->value_str; ly_set_free(nodeset); /* filter, join all into one xpath */ nodeset = lyd_find_path(input, "filter"); if (nodeset->number) { if (op_filter_create(nodeset->set.d[0], &filter)) { rc = SR_ERR_INTERNAL; goto cleanup; } /* all selection filters first */ for (i = 0; i < filter.count; ++i) { if (!filter.filters[i].selection) { continue; } /* put all selection filters into parentheses */ if (!xp) { if ((rc = np2srv_rpc_subscribe_append_str("(", &xp))) { goto cleanup; } if ((rc = np2srv_rpc_subscribe_append_str(filter.filters[i].str, &xp))) { goto cleanup; } } else { if ((rc = np2srv_rpc_subscribe_append_str(" or ", &xp))) { goto cleanup; } if ((rc = np2srv_rpc_subscribe_append_str(filter.filters[i].str, &xp))) { goto cleanup; } } } if (xp) { /* finish parentheses */ if ((rc = np2srv_rpc_subscribe_append_str(")", &xp))) { goto cleanup; } } /* now append all content filters */ for (i = 0; i < filter.count; ++i) { if (filter.filters[i].selection) { continue; } if (xp) { if ((rc = np2srv_rpc_subscribe_append_str(" and ", &xp))) { goto cleanup; } } if ((rc = np2srv_rpc_subscribe_append_str(filter.filters[i].str, &xp))) { goto cleanup; } } } ly_set_free(nodeset); /* start time */ nodeset = lyd_find_path(input, "startTime"); if (nodeset->number) { start = nc_datetime2time(((struct lyd_node_leaf_list *)nodeset->set.d[0])->value_str); } ly_set_free(nodeset); /* stop time */ nodeset = lyd_find_path(input, "stopTime"); if (nodeset->number) { stop = nc_datetime2time(((struct lyd_node_leaf_list *)nodeset->set.d[0])->value_str); } ly_set_free(nodeset); /* set ongoing notifications flag */ nc_session_set_notif_status(ncs, 1); /* sysrepo API */ if (!strcmp(stream, "NETCONF")) { /* subscribe to all modules with notifications */ idx = 0; while ((ly_mod = ly_ctx_get_module_iter(lyd_node_module(input)->ctx, &idx))) { rc = SR_ERR_OK; LY_TREE_FOR(ly_mod->data, root) { LY_TREE_DFS_BEGIN(root, next, elem) { if (elem->nodetype == LYS_NOTIF) { /* check that we are not in a grouping */ parent = lys_parent(elem); while (parent && (parent->nodetype != LYS_GROUPING)) { parent = lys_parent(parent); } if (!parent) { rc = sr_event_notif_subscribe_tree(nc_session_get_data(ncs), ly_mod->name, xp, start, stop, np2srv_ntf_new_cb, ncs, np2srv.sr_notif_sub ? SR_SUBSCR_CTX_REUSE : 0, &np2srv.sr_notif_sub); break; } } LY_TREE_DFS_END(root, next, elem); } if (elem && (elem->nodetype == LYS_NOTIF)) { break; } } if (rc != SR_ERR_OK) { goto cleanup; } } } else { rc = sr_event_notif_subscribe_tree(nc_session_get_data(ncs), stream, xp, start, stop, np2srv_ntf_new_cb, ncs, np2srv.sr_notif_sub ? SR_SUBSCR_CTX_REUSE : 0, &np2srv.sr_notif_sub); } if (rc != SR_ERR_OK) { goto cleanup; } /* success */ cleanup: op_filter_erase(&filter); free(xp); if (ncs && rc) { nc_session_set_notif_status(ncs, 0); } return rc; } netopeer2-1.1.70/src/netconf.h0000664000000000000000000000472714021433500014601 0ustar rootroot/** * @file netconf.h * @author Michal Vasko <mvasko@cesnet.cz> * @brief ietf-netconf callbacks header * * Copyright (c) 2019 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 NP2SRV_NETCONF_H_ #define NP2SRV_NETCONF_H_ #include <libyang/libyang.h> #include <sysrepo.h> int np2srv_rpc_get_cb(sr_session_ctx_t *session, const char *op_path, const struct lyd_node *input, sr_event_t event, uint32_t request_id, struct lyd_node *output, void *private_data); int np2srv_rpc_editconfig_cb(sr_session_ctx_t *session, const char *op_path, const struct lyd_node *input, sr_event_t event, uint32_t request_id, struct lyd_node *output, void *private_data); int np2srv_rpc_copyconfig_cb(sr_session_ctx_t *session, const char *op_path, const struct lyd_node *input, sr_event_t event, uint32_t request_id, struct lyd_node *output, void *private_data); int np2srv_rpc_deleteconfig_cb(sr_session_ctx_t *session, const char *op_path, const struct lyd_node *input, sr_event_t event, uint32_t request_id, struct lyd_node *output, void *private_data); int np2srv_rpc_un_lock_cb(sr_session_ctx_t *session, const char *op_path, const struct lyd_node *input, sr_event_t event, uint32_t request_id, struct lyd_node *output, void *private_data); int np2srv_rpc_kill_cb(sr_session_ctx_t *session, const char *op_path, const struct lyd_node *input, sr_event_t event, uint32_t request_id, struct lyd_node *output, void *private_data); int np2srv_rpc_commit_cb(sr_session_ctx_t *session, const char *op_path, const struct lyd_node *input, sr_event_t event, uint32_t request_id, struct lyd_node *output, void *private_data); int np2srv_rpc_discard_cb(sr_session_ctx_t *session, const char *op_path, const struct lyd_node *input, sr_event_t event, uint32_t request_id, struct lyd_node *output, void *private_data); int np2srv_rpc_validate_cb(sr_session_ctx_t *session, const char *op_path, const struct lyd_node *input, sr_event_t event, uint32_t request_id, struct lyd_node *output, void *private_data); int np2srv_rpc_subscribe_cb(sr_session_ctx_t *session, const char *op_path, const struct lyd_node *input, sr_event_t event, uint32_t request_id, struct lyd_node *output, void *private_data); #endif /* NP2SRV_NETCONF_H_ */ netopeer2-1.1.70/src/netconf_acm.c0000664000000000000000000013655114021433500015415 0ustar rootroot/** * @file netconf_acm.c * @author Michal Vasko <mvasko@cesnet.cz> * @brief NACM and ietf-netconf-acm callbacks * * Copyright (c) 2019 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 #define _DEFAULT_SOURCE #include <stdlib.h> #include <string.h> #include <assert.h> #include <unistd.h> #include <grp.h> #include <pwd.h> #include <libyang/libyang.h> #include <sysrepo.h> #include "common.h" #include "log.h" #include "netconf_acm.h" static struct ncac nacm; /* /ietf-netconf-acm:nacm */ int ncac_nacm_params_cb(sr_session_ctx_t *session, const char *UNUSED(module_name), const char *xpath, sr_event_t UNUSED(event), uint32_t UNUSED(request_id), void *UNUSED(private_data)) { sr_change_iter_t *iter; sr_change_oper_t op; const struct lyd_node *node; const char *prev_val, *prev_list; char *xpath2; bool prev_dflt; int rc; if (asprintf(&xpath2, "%s/*", xpath) == -1) { EMEM; return SR_ERR_NOMEM; } rc = sr_get_changes_iter(session, xpath2, &iter); free(xpath2); if (rc != SR_ERR_OK) { ERR("Getting changes iter failed (%s).", sr_strerror(rc)); return rc; } pthread_mutex_lock(&nacm.lock); while ((rc = sr_get_change_tree_next(session, iter, &op, &node, &prev_val, &prev_list, &prev_dflt)) == SR_ERR_OK) { if (!strcmp(node->schema->name, "enable-nacm")) { if ((op == SR_OP_CREATED) || (op == SR_OP_MODIFIED)) { if (((struct lyd_node_leaf_list *)node)->value.bln) { nacm.enabled = 1; } else { nacm.enabled = 0; } } } else if (!strcmp(node->schema->name, "read-default")) { if ((op == SR_OP_CREATED) || (op == SR_OP_MODIFIED)) { if (!strcmp(((struct lyd_node_leaf_list *)node)->value_str, "permit")) { nacm.default_read_deny = 0; } else { nacm.default_read_deny = 1; } } } else if (!strcmp(node->schema->name, "write-default")) { if ((op == SR_OP_CREATED) || (op == SR_OP_MODIFIED)) { if (!strcmp(((struct lyd_node_leaf_list *)node)->value_str, "permit")) { nacm.default_write_deny = 0; } else { nacm.default_write_deny = 1; } } } else if (!strcmp(node->schema->name, "exec-default")) { if ((op == SR_OP_CREATED) || (op == SR_OP_MODIFIED)) { if (!strcmp(((struct lyd_node_leaf_list *)node)->value_str, "permit")) { nacm.default_exec_deny = 0; } else { nacm.default_exec_deny = 1; } } } else if (!strcmp(node->schema->name, "enable-external-groups")) { if ((op == SR_OP_CREATED) || (op == SR_OP_MODIFIED)) { if (((struct lyd_node_leaf_list *)node)->value.bln) { nacm.enable_external_groups = 1; } else { nacm.enable_external_groups = 0; } } } } pthread_mutex_unlock(&nacm.lock); sr_free_change_iter(iter); if (rc != SR_ERR_NOT_FOUND) { ERR("Getting next change failed (%s).", sr_strerror(rc)); return rc; } return SR_ERR_OK; } /* /ietf-netconf-acm:nacm/denied-* */ int ncac_state_data_cb(sr_session_ctx_t *UNUSED(session), const char *UNUSED(module_name), const char *path, const char *UNUSED(request_xpath), uint32_t UNUSED(request_id), struct lyd_node **parent, void *UNUSED(private_data)) { struct lyd_node *node; char num_str[11]; assert(*parent); pthread_mutex_lock(&nacm.lock); if (!strcmp(path, "/ietf-netconf-acm:nacm/denied-operations")) { sprintf(num_str, "%u", nacm.denied_operations); node = lyd_new_path(*parent, NULL, "denied-operations", num_str, 0, 0); } else if (!strcmp(path, "/ietf-netconf-acm:nacm/denied-data-writes")) { sprintf(num_str, "%u", nacm.denied_data_writes); node = lyd_new_path(*parent, NULL, "denied-data-writes", num_str, 0, 0); } else { assert(!strcmp(path, "/ietf-netconf-acm:nacm/denied-notifications")); sprintf(num_str, "%u", nacm.denied_notifications); node = lyd_new_path(*parent, NULL, "denied-notifications", num_str, 0, 0); } pthread_mutex_unlock(&nacm.lock); if (!node) { return SR_ERR_INTERNAL; } return SR_ERR_OK; } /* /ietf-netconf-acm:nacm/groups/group */ int ncac_group_cb(sr_session_ctx_t *session, const char *UNUSED(module_name), const char *xpath, sr_event_t UNUSED(event), uint32_t UNUSED(request_id), void *UNUSED(private_data)) { sr_change_iter_t *iter; sr_change_oper_t op; const struct lyd_node *node; const char *prev_val, *prev_list, *group_name, *user_name; struct ncac_group *group = NULL; struct ly_ctx *ly_ctx; uint32_t i, j; char *xpath2; bool prev_dflt; int rc; void *mem; ly_ctx = (struct ly_ctx *)sr_get_context(np2srv.sr_conn); if (asprintf(&xpath2, "%s//.", xpath) == -1) { EMEM; return SR_ERR_NOMEM; } rc = sr_get_changes_iter(session, xpath2, &iter); free(xpath2); if (rc != SR_ERR_OK) { ERR("Getting changes iter failed (%s).", sr_strerror(rc)); return rc; } pthread_mutex_lock(&nacm.lock); while ((rc = sr_get_change_tree_next(session, iter, &op, &node, &prev_val, &prev_list, &prev_dflt)) == SR_ERR_OK) { if (!strcmp(node->schema->name, "group")) { /* name must be present */ assert(!strcmp(node->child->schema->name, "name")); group_name = ((struct lyd_node_leaf_list *)node->child)->value_str; switch (op) { case SR_OP_CREATED: /* add new group */ mem = realloc(nacm.groups, (nacm.group_count + 1) * sizeof *nacm.groups); if (!mem) { EMEM; pthread_mutex_unlock(&nacm.lock); return SR_ERR_NOMEM; } nacm.groups = mem; group = &nacm.groups[nacm.group_count]; ++nacm.group_count; group->name = lydict_insert(ly_ctx, group_name, 0); group->users = NULL; group->user_count = 0; break; case SR_OP_DELETED: /* find it */ for (i = 0; i < nacm.group_count; ++i) { /* both in dictionary */ if (nacm.groups[i].name == group_name) { group = &nacm.groups[i]; break; } } assert(i < nacm.group_count); /* delete it */ lydict_remove(ly_ctx, group->name); for (j = 0; j < group->user_count; ++j) { lydict_remove(ly_ctx, group->users[j]); } free(group->users); --nacm.group_count; if (i < nacm.group_count) { memcpy(group, &nacm.groups[nacm.group_count], sizeof *group); } if (!nacm.group_count) { free(nacm.groups); nacm.groups = NULL; } group = NULL; break; default: EINT; pthread_mutex_unlock(&nacm.lock); return SR_ERR_INTERNAL; } } else { /* name must be present */ assert(!strcmp(node->parent->child->schema->name, "name")); group_name = ((struct lyd_node_leaf_list *)node->parent->child)->value_str; group = NULL; for (i = 0; i < nacm.group_count; ++i) { /* both in dictionary */ if (nacm.groups[i].name == group_name) { group = &nacm.groups[i]; break; } } if (!strcmp(node->schema->name, "user-name")) { if ((op == SR_OP_DELETED) && !group) { continue; } assert(group); user_name = ((struct lyd_node_leaf_list *)node)->value_str; if (op == SR_OP_CREATED) { mem = realloc(group->users, (group->user_count + 1) * sizeof *group->users); if (!mem) { EMEM; pthread_mutex_unlock(&nacm.lock); return SR_ERR_NOMEM; } group->users = mem; group->users[group->user_count] = (char *)lydict_insert(ly_ctx, user_name, 0); ++group->user_count; } else { assert(op == SR_OP_DELETED); for (i = 0; i < group->user_count; ++i) { /* both in dictionary */ if (group->users[i] == user_name) { break; } } assert(i < group->user_count); /* delete it */ lydict_remove(ly_ctx, group->users[i]); --group->user_count; if (i < group->user_count) { group->users[i] = group->users[group->user_count]; } if (!group->user_count) { free(group->users); group->users = NULL; } } } } } pthread_mutex_unlock(&nacm.lock); sr_free_change_iter(iter); if (rc != SR_ERR_NOT_FOUND) { ERR("Getting next change failed (%s).", sr_strerror(rc)); return rc; } return SR_ERR_OK; } static void ncac_remove_rules(struct ncac_rule_list *list) { struct ncac_rule *rule, *tmp; struct ly_ctx *ly_ctx; ly_ctx = (struct ly_ctx *)sr_get_context(np2srv.sr_conn); LY_TREE_FOR_SAFE(list->rules, tmp, rule) { lydict_remove(ly_ctx, rule->name); lydict_remove(ly_ctx, rule->module_name); lydict_remove(ly_ctx, rule->target); lydict_remove(ly_ctx, rule->comment); free(rule); } list->rules = NULL; } /* /ietf-netconf-acm:nacm/rule-list */ int ncac_rule_list_cb(sr_session_ctx_t *session, const char *UNUSED(module_name), const char *xpath, sr_event_t UNUSED(event), uint32_t UNUSED(request_id), void *UNUSED(private_data)) { sr_change_iter_t *iter; sr_change_oper_t op; const struct lyd_node *node; struct ly_ctx *ly_ctx; const char *prev_val, *prev_list, *rlist_name, *group_name; struct ncac_rule_list *rlist = NULL, *prev_rlist; char *xpath2; bool prev_dflt; int rc, len; uint32_t i; void *mem; ly_ctx = (struct ly_ctx *)sr_get_context(np2srv.sr_conn); if (asprintf(&xpath2, "%s//.", xpath) == -1) { EMEM; return SR_ERR_NOMEM; } rc = sr_get_changes_iter(session, xpath2, &iter); free(xpath2); if (rc != SR_ERR_OK) { ERR("Getting changes iter failed (%s).", sr_strerror(rc)); return rc; } pthread_mutex_lock(&nacm.lock); while ((rc = sr_get_change_tree_next(session, iter, &op, &node, &prev_val, &prev_list, &prev_dflt)) == SR_ERR_OK) { if (!strcmp(node->schema->name, "rule-list")) { /* name must be present */ assert(!strcmp(node->child->schema->name, "name")); rlist_name = ((struct lyd_node_leaf_list *)node->child)->value_str; switch (op) { case SR_OP_MOVED: /* find it */ prev_rlist = NULL; for (rlist = nacm.rule_lists; rlist && (rlist->name != rlist_name); rlist = rlist->next) { prev_rlist = rlist; } assert(rlist); /* unlink it */ if (prev_rlist) { prev_rlist->next = rlist->next; } else { nacm.rule_lists = rlist->next; } /* fallthrough */ case SR_OP_CREATED: if (op == SR_OP_CREATED) { /* create new rule list */ rlist = calloc(1, sizeof *rlist); if (!rlist) { EMEM; pthread_mutex_unlock(&nacm.lock); return SR_ERR_NOMEM; } rlist->name = lydict_insert(ly_ctx, rlist_name, 0); } /* find previous list */ assert(prev_list); if (prev_list[0]) { assert(strchr(prev_list, '\'')); prev_list = strchr(prev_list, '\'') + 1; len = strchr(prev_list, '\'') - prev_list; prev_rlist = nacm.rule_lists; while (prev_rlist && strncmp(prev_rlist->name, prev_list, len)) { prev_rlist = prev_rlist->next; } assert(prev_rlist); } else { prev_rlist = NULL; } /* insert after previous list */ if (prev_rlist) { rlist->next = prev_rlist->next; prev_rlist->next = rlist; } else { rlist->next = nacm.rule_lists; nacm.rule_lists = rlist; } break; case SR_OP_DELETED: /* find it */ prev_rlist = NULL; for (rlist = nacm.rule_lists; rlist && (rlist->name != rlist_name); rlist = rlist->next) { prev_rlist = rlist; } assert(rlist); /* delete it */ lydict_remove(ly_ctx, rlist->name); for (i = 0; i < rlist->group_count; ++i) { lydict_remove(ly_ctx, rlist->groups[i]); } free(rlist->groups); ncac_remove_rules(rlist); if (prev_rlist) { prev_rlist->next = rlist->next; } else { nacm.rule_lists = rlist->next; } free(rlist); rlist = NULL; break; default: EINT; pthread_mutex_unlock(&nacm.lock); return SR_ERR_INTERNAL; } } else { /* name must be present */ assert(!strcmp(node->parent->child->schema->name, "name")); rlist_name = ((struct lyd_node_leaf_list *)node->parent->child)->value_str; for (rlist = nacm.rule_lists; rlist && (rlist->name != rlist_name); rlist = rlist->next); if (!strcmp(node->schema->name, "group")) { if ((op == SR_OP_DELETED) && !rlist) { continue; } assert(rlist); group_name = ((struct lyd_node_leaf_list *)node)->value_str; if (op == SR_OP_CREATED) { mem = realloc(rlist->groups, (rlist->group_count + 1) * sizeof *rlist->groups); if (!mem) { EMEM; pthread_mutex_unlock(&nacm.lock); return SR_ERR_NOMEM; } rlist->groups = mem; rlist->groups[rlist->group_count] = (char *)lydict_insert(ly_ctx, group_name, 0); ++rlist->group_count; } else { assert(op == SR_OP_DELETED); for (i = 0; i < rlist->group_count; ++i) { /* both in dictionary */ if (rlist->groups[i] == group_name) { break; } } assert(i < rlist->group_count); /* delete it */ lydict_remove(ly_ctx, rlist->groups[i]); --rlist->group_count; if (i < rlist->group_count) { rlist->groups[i] = rlist->groups[rlist->group_count]; } if (!rlist->group_count) { free(rlist->groups); rlist->groups = NULL; } } } } } pthread_mutex_unlock(&nacm.lock); sr_free_change_iter(iter); if (rc != SR_ERR_NOT_FOUND) { ERR("Getting next change failed (%s).", sr_strerror(rc)); return rc; } return SR_ERR_OK; } /* /ietf-netconf-acm:nacm/rule-list/rule */ int ncac_rule_cb(sr_session_ctx_t *session, const char *UNUSED(module_name), const char *xpath, sr_event_t UNUSED(event), uint32_t UNUSED(request_id), void *UNUSED(private_data)) { sr_change_iter_t *iter; sr_change_oper_t op; const struct lyd_node *node; struct ly_ctx *ly_ctx; const char *prev_val, *prev_list, *rule_name, *rlist_name, *str; struct ncac_rule_list *rlist; struct ncac_rule *rule = NULL, *prev_rule; char *xpath2; bool prev_dflt; int rc, len; ly_ctx = (struct ly_ctx *)sr_get_context(np2srv.sr_conn); if (asprintf(&xpath2, "%s//.", xpath) == -1) { EMEM; return SR_ERR_NOMEM; } rc = sr_get_changes_iter(session, xpath2, &iter); free(xpath2); if (rc != SR_ERR_OK) { ERR("Getting changes iter failed (%s).", sr_strerror(rc)); return rc; } pthread_mutex_lock(&nacm.lock); while ((rc = sr_get_change_tree_next(session, iter, &op, &node, &prev_val, &prev_list, &prev_dflt)) == SR_ERR_OK) { if (!strcmp(node->schema->name, "rule")) { /* find parent rule list */ assert(!strcmp(node->parent->child->schema->name, "name")); rlist_name = ((struct lyd_node_leaf_list *)node->parent->child)->value_str; for (rlist = nacm.rule_lists; rlist && (rlist->name != rlist_name); rlist = rlist->next); if ((op == SR_OP_DELETED) && !rlist) { /* even parent rule-list was deleted */ continue; } assert(rlist); /* name must be present */ assert(!strcmp(node->child->schema->name, "name")); rule_name = ((struct lyd_node_leaf_list *)node->child)->value_str; switch (op) { case SR_OP_MOVED: /* find it */ prev_rule = NULL; for (rule = rlist->rules; rule && (rule->name != rule_name); rule = rule->next) { prev_rule = rule; } assert(rule); /* unlink it */ if (prev_rule) { prev_rule->next = rule->next; } else { rlist->rules = rule->next; } /* fallthrough */ case SR_OP_CREATED: if (op == SR_OP_CREATED) { /* create new rule */ rule = calloc(1, sizeof *rule); if (!rule) { EMEM; pthread_mutex_unlock(&nacm.lock); return SR_ERR_NOMEM; } rule->name = lydict_insert(ly_ctx, rule_name, 0); rule->target_type = NCAC_TARGET_ANY; } assert(rule); /* find previous rule */ assert(prev_list); if (prev_list[0]) { assert(strchr(prev_list, '\'')); prev_list = strchr(prev_list, '\'') + 1; len = strchr(prev_list, '\'') - prev_list; prev_rule = rlist->rules; while (prev_rule && strncmp(prev_rule->name, prev_list, len)) { prev_rule = prev_rule->next; } assert(prev_rule); } else { prev_rule = NULL; } /* insert after previous rule */ if (prev_rule) { rule->next = prev_rule->next; prev_rule->next = rule; } else { rule->next = rlist->rules; rlist->rules = rule; } break; case SR_OP_DELETED: /* find it */ prev_rule = NULL; for (rule = rlist->rules; rule && (rule->name != rule_name); rule = rule->next) { prev_rule = rule; } assert(rule); /* delete it */ lydict_remove(ly_ctx, rule->name); lydict_remove(ly_ctx, rule->module_name); lydict_remove(ly_ctx, rule->target); lydict_remove(ly_ctx, rule->comment); if (prev_rule) { prev_rule->next = rule->next; } else { rlist->rules = rule->next; } free(rule); break; default: EINT; pthread_mutex_unlock(&nacm.lock); return SR_ERR_INTERNAL; } } else { /* find parent rule list */ assert(!strcmp(node->parent->parent->child->schema->name, "name")); rlist_name = ((struct lyd_node_leaf_list *)node->parent->parent->child)->value_str; for (rlist = nacm.rule_lists; rlist && (rlist->name != rlist_name); rlist = rlist->next); if ((op == SR_OP_DELETED) && !rlist) { /* even parent rule-list was deleted */ continue; } assert(rlist); /* name must be present */ assert(!strcmp(node->parent->child->schema->name, "name")); rule_name = ((struct lyd_node_leaf_list *)node->parent->child)->value_str; for (rule = rlist->rules; rule && (rule->name != rule_name); rule = rule->next); if ((op == SR_OP_DELETED) && !rule) { /* even parent rule was deleted */ continue; } assert(rule); if (!strcmp(node->schema->name, "module-name")) { str = ((struct lyd_node_leaf_list *)node)->value_str; lydict_remove(ly_ctx, rule->module_name); if (!strcmp(str, "*")) { rule->module_name = NULL; } else { rule->module_name = lydict_insert(ly_ctx, str, 0); } } else if (!strcmp(node->schema->name, "rpc-name") || !strcmp(node->schema->name, "notification-name") || !strcmp(node->schema->name, "path")) { if (op == SR_OP_DELETED) { lydict_remove(ly_ctx, rule->target); rule->target = NULL; rule->target_type = NCAC_TARGET_ANY; } else { str = ((struct lyd_node_leaf_list *)node)->value_str; lydict_remove(ly_ctx, rule->target); if (!strcmp(str, "*")) { rule->target = NULL; } else { rule->target = lydict_insert(ly_ctx, str, 0); } if (!strcmp(node->schema->name, "rpc-name")) { rule->target_type = NCAC_TARGET_RPC; } else if (!strcmp(node->schema->name, "notification-name")) { rule->target_type = NCAC_TARGET_NOTIF; } else { assert(!strcmp(node->schema->name, "path")); rule->target_type = NCAC_TARGET_DATA; } } } else if (!strcmp(node->schema->name, "access-operations")) { str = ((struct lyd_node_leaf_list *)node)->value_str; rule->operations = 0; if (!strcmp(str, "*")) { rule->operations = NCAC_OP_ALL; } else { if (strstr(str, "create")) { rule->operations |= NCAC_OP_CREATE; } if (strstr(str, "read")) { rule->operations |= NCAC_OP_READ; } if (strstr(str, "update")) { rule->operations |= NCAC_OP_UPDATE; } if (strstr(str, "delete")) { rule->operations |= NCAC_OP_DELETE; } if (strstr(str, "exec")) { rule->operations |= NCAC_OP_EXEC; } } } else if (!strcmp(node->schema->name, "action")) { if (!strcmp(((struct lyd_node_leaf_list *)node)->value_str, "permit")) { rule->action_deny = 0; } else { rule->action_deny = 1; } } else if (!strcmp(node->schema->name, "comment")) { if (op == SR_OP_DELETED) { lydict_remove(ly_ctx, rule->comment); rule->comment = NULL; } else { assert((op == SR_OP_MODIFIED) || (op == SR_OP_CREATED)); lydict_remove(ly_ctx, rule->comment); rule->comment = lydict_insert(ly_ctx, ((struct lyd_node_leaf_list *)node)->value_str, 0); } } } } pthread_mutex_unlock(&nacm.lock); sr_free_change_iter(iter); if (rc != SR_ERR_NOT_FOUND) { ERR("Getting next change failed (%s).", sr_strerror(rc)); return rc; } return SR_ERR_OK; } enum ncac_access { NCAC_ACCESS_DENY = 1, /**< access to the node is denied */ NCAC_ACCESS_PARTIAL_DENY = 2, /**< access to the node is denied but it is a prefix of a matching rule */ NCAC_ACCESS_PARTIAL_PERMIT = 3, /**< access to the node is permitted but any children must still be checked */ NCAC_ACCESS_PERMIT = 4 /**< access to the node is permitted with any children */ }; #define NCAC_ACCESS_IS_NODE_PERMIT(x) ((x) > 2) void ncac_init(void) { pthread_mutex_init(&nacm.lock, NULL); } void ncac_destroy(void) { struct ncac_group *group; struct ncac_rule_list *rule_list, *tmp; struct ly_ctx *ly_ctx; uint32_t i, j; ly_ctx = (struct ly_ctx *)sr_get_context(np2srv.sr_conn); for (i = 0; i < nacm.group_count; ++i) { group = &nacm.groups[i]; lydict_remove(ly_ctx, group->name); for (j = 0; j < group->user_count; ++j) { lydict_remove(ly_ctx, group->users[j]); } free(group->users); } free(nacm.groups); LY_TREE_FOR_SAFE(nacm.rule_lists, tmp, rule_list) { lydict_remove(ly_ctx, rule_list->name); for (i = 0; i < rule_list->group_count; ++i) { lydict_remove(ly_ctx, rule_list->groups[i]); } free(rule_list->groups); ncac_remove_rules(rule_list); free(rule_list); } pthread_mutex_destroy(&nacm.lock); } /** * @brief Get passwd entry of a user, specifically its UID and GID. * * @param[in] user User to learn about. * @param[out] uid User UID, if set. * @param[out] gid User GID, if set. * @return 0 on success, 1 on user not found, -1 on error. */ static int ncac_getpwnam(const char *user, uid_t *uid, gid_t *gid) { struct passwd pwd, *pwd_p; char *buf = NULL; ssize_t buflen; int ret; assert(user); buflen = sysconf(_SC_GETPW_R_SIZE_MAX); if (buflen == -1) { buflen = 2048; } buf = malloc(buflen); if (!buf) { EMEM; return -1; } ret = getpwnam_r(user, &pwd, buf, buflen, &pwd_p); if (ret) { ERR("Getting user \"%s\" pwd entry failed (%s).", user, strerror(ret)); free(buf); return -1; } else if (!pwd_p) { free(buf); return 1; } if (uid) { *uid = pwd.pw_uid; } if (gid) { *gid = pwd.pw_gid; } free(buf); return 0; } /** * @brief Check NACM acces for the data tree. If this check passes, no other check is necessary. * If not, each node must be checked separately to decide. * * @param[in] top_node Top-level node of the data. * @param[in] user User, whose access to check. * @return non-zero if access allowed, 0 if more checks are required. */ static int ncac_allowed_tree(const struct lys_node *top_node, const char *user) { struct lys_node *parent; uid_t user_uid; for (parent = lys_parent(top_node); parent && (parent->nodetype & (LYS_USES | LYS_CASE | LYS_CHOICE)); parent = lys_parent(parent)); if (parent) { EINT; return 0; } /* 1) NACM is off */ if (!nacm.enabled) { return 1; } /* 2) recovery session allowed */ if (!ncac_getpwnam(user, &user_uid, NULL) && (user_uid == NP2SRV_NACM_RECOVERY_UID)) { return 1; } /* 3) <close-session> and notifications <replayComplete>, <notificationComplete> always allowed */ if ((top_node->nodetype == LYS_RPC) && !strcmp(top_node->name, "close-session") && !strcmp(lys_node_module(top_node)->name, "ietf-netconf")) { return 1; } else if ((top_node->nodetype == LYS_NOTIF) && !strcmp(lys_node_module(top_node)->name, "nc-notifications")) { return 1; } return 0; } /** * @brief Collect all NACM groups for a user. If enabled, even system ones. * * @param[in] ly_ctx libyang context for dictionary. * @param[in] user User to collect groups for. * @param[out] groups Array of collected groups. * @param[out] group_count Number of collected groups. * @return 0 on success, -1 on error. */ static int ncac_collect_groups(struct ly_ctx *ly_ctx, const char *user, char ***groups, uint32_t *group_count) { struct group grp, *grp_p; gid_t user_gid; const char *user_dict = NULL, *grp_dict; char *buf = NULL; gid_t *gids = NULL; ssize_t buflen; uint32_t i, j; void *mem; int gid_count = 0, ret, rc = -1; user_dict = lydict_insert(ly_ctx, user, 0); *groups = NULL; *group_count = 0; /* collect NACM groups */ for (i = 0; i < nacm.group_count; ++i) { for (j = 0; j < nacm.groups[i].user_count; ++j) { if (nacm.groups[i].users[j] == user_dict) { mem = realloc(*groups, (*group_count + 1) * sizeof **groups); if (!mem) { EMEM; goto cleanup; } *groups = mem; (*groups)[*group_count] = (char *)lydict_insert(ly_ctx, nacm.groups[i].name, 0); ++(*group_count); } } } /* collect system groups */ if (nacm.enable_external_groups) { ret = ncac_getpwnam(user, NULL, &user_gid); if (ret) { if (ret == 1) { /* no user, no more groups */ rc = 0; } goto cleanup; } /* get all GIDs */ getgrouplist(user, user_gid, gids, &gid_count); gids = malloc(gid_count * sizeof *gids); if (!gids) { EMEM; goto cleanup; } ret = getgrouplist(user, user_gid, gids, &gid_count); if (ret == -1) { ERR("Getting system groups of user \"%s\" failed.", user); goto cleanup; } /* add all GIDs group names */ buflen = sysconf(_SC_GETGR_R_SIZE_MAX); if (buflen == -1) { buflen = 2048; } free(buf); buf = malloc(buflen); if (!buf) { EMEM; goto cleanup; } for (i = 0; i < (unsigned)gid_count; ++i) { ret = getgrgid_r(gids[i], &grp, buf, buflen, &grp_p); if (ret) { ERR("Getting GID grp entry failed (%s).", strerror(ret)); goto cleanup; } else if (!grp_p) { ERR("Getting GID grp entry failed (Group not found)."); goto cleanup; } grp_dict = lydict_insert(ly_ctx, grp.gr_name, 0); /* check for duplicates */ for (j = 0; j < *group_count; ++j) { if ((*groups)[j] == grp_dict) { break; } } if (j < *group_count) { /* duplicate */ lydict_remove(ly_ctx, grp_dict); } else { mem = realloc(*groups, (*group_count + 1) * sizeof *groups); if (!mem) { EMEM; goto cleanup; } *groups = mem; (*groups)[*group_count] = (char *)grp_dict; ++(*group_count); } } } /* success */ rc = 0; cleanup: free(gids); free(buf); lydict_remove(ly_ctx, user_dict); return rc; } /** * @brief Check NACM match of a node path and specific rule target. * * Details on matching in description of typedef ietf-netconf-acm:node-instance-identifier. * * @param[in] rule_target Rule target instance-identifier. * @param[in] node_path Node data path. * @return 0 if does not match. * @return 1 if the rule path matches. * @return 2 if the path is a partial match. */ static int ncac_allowed_path(const char *rule_target, const char *node_path) { const char *rule_ptr, *node_ptr; rule_ptr = rule_target; node_ptr = node_path; while (rule_ptr[0] && node_ptr[0]) { if (rule_ptr[0] == node_ptr[0]) { ++rule_ptr; ++node_ptr; } else if ((rule_ptr[0] == '/') && (node_ptr[0] == '[')) { /* target has no predicate, skip it in path as well because it matches any value */ while (node_ptr[0] != ']') { if (node_ptr[0] == '\'') { do { ++node_ptr; } while (node_ptr[0] != '\''); } ++node_ptr; } ++node_ptr; } else { /* not a match */ return 0; } } if (!rule_ptr[0] && !node_ptr[0]) { /* full match */ return 1; } else if (rule_ptr[0]) { assert(!node_ptr[0]); /* rule continues, it is a partial match */ return 2; } else { assert(!rule_ptr[0]); /* node continues, prefix (descendant) match */ return 1; } } /** * @brief Check NACM access for a single node. * * @param[in] node Node to check. * @param[in] user User, whose access to check. * @param[in] oper Operation to check. * @return NCAC access enum. */ static enum ncac_access ncac_allowed_node(const struct lyd_node *node, const char *user, uint8_t oper) { struct ncac_rule_list *rlist; struct ncac_rule *rule; struct ly_ctx *ly_ctx; char **groups, *path; uint32_t i, j, group_count; enum ncac_access access = NCAC_ACCESS_DENY, partial_access = NCAC_ACCESS_DENY; int path_match; assert(oper); ly_ctx = lyd_node_module(node)->ctx; /* * ref https://tools.ietf.org/html/rfc8341#section-3.4.4 */ /* 4) collect groups */ if (ncac_collect_groups(ly_ctx, user, &groups, &group_count)) { goto cleanup; } /* 5) no groups */ if (!group_count) { goto step10; } /* 6) find matching rule lists */ for (rlist = nacm.rule_lists; rlist; rlist = rlist->next) { for (i = 0; i < rlist->group_count; ++i) { if (strcmp(rlist->groups[i], "*")) { for (j = 0; j < group_count; ++j) { if (rlist->groups[i] == groups[j]) { break; } } if (j < group_count) { /* match */ break; } } else { /* match for all groups */ break; } } if (i == rlist->group_count) { /* no match */ continue; } /* 7) find matching rules */ for (rule = rlist->rules; rule; rule = rule->next) { /* module name matching */ if (rule->module_name && (rule->module_name != lyd_node_module(node)->name)) { continue; } /* access operation matching */ if (!(rule->operations & oper)) { continue; } /* target (rule) type matching */ switch (rule->target_type) { case NCAC_TARGET_RPC: if (node->schema->nodetype != LYS_RPC) { continue; } if (rule->target && (rule->target != node->schema->name)) { /* exact match needed */ continue; } break; case NCAC_TARGET_NOTIF: /* only top-level notification */ if (lys_parent(node->schema) || (node->schema->nodetype != LYS_NOTIF)) { continue; } if (rule->target && (rule->target != node->schema->name)) { /* exact match needed */ continue; } break; case NCAC_TARGET_DATA: if (node->schema->nodetype & (LYS_RPC | LYS_NOTIF)) { continue; } /* fallthrough */ case NCAC_TARGET_ANY: if (rule->target) { path = lyd_path(node); /* exact match or is a descendant (specified in RFC 8341 page 27) for full tree access */ path_match = ncac_allowed_path(rule->target, path); free(path); if (!path_match) { continue; } else if (path_match == 2) { /* partial match, continue searching for a full match (only partial permit has some meaning, * do not overwrite it) */ partial_access = rule->action_deny ? partial_access : NCAC_ACCESS_PARTIAL_PERMIT; continue; } } break; } /* 8) rule matched */ access = rule->action_deny ? NCAC_ACCESS_DENY : NCAC_ACCESS_PERMIT; goto cleanup; } } /* 9) no matching rule found */ step10: /* 10) check default-deny-all extension */ for (i = 0; i < node->schema->ext_size; ++i) { if (!strcmp(node->schema->ext[i]->def->module->name, "ietf-netconf-acm")) { if (!strcmp(node->schema->ext[i]->def->name, "default-deny-all")) { goto cleanup; } if ((oper & (NCAC_OP_CREATE | NCAC_OP_UPDATE | NCAC_OP_DELETE)) && !strcmp(node->schema->ext[i]->def->name, "default-deny-write")) { goto cleanup; } } } /* 11) was already covered in 10) */ /* 12) check defaults */ switch (oper) { case NCAC_OP_READ: if (nacm.default_read_deny) { access = NCAC_ACCESS_DENY; } else { /* permit, but not by an explicit rule */ access = NCAC_ACCESS_PARTIAL_PERMIT; } break; case NCAC_OP_CREATE: case NCAC_OP_UPDATE: case NCAC_OP_DELETE: if (nacm.default_write_deny) { access = NCAC_ACCESS_DENY; } else { /* permit, but not by an explicit rule */ access = NCAC_ACCESS_PARTIAL_PERMIT; } break; case NCAC_OP_EXEC: if (nacm.default_exec_deny) { access = NCAC_ACCESS_DENY; } else { /* permit, but not by an explicit rule */ access = NCAC_ACCESS_PARTIAL_PERMIT; } break; default: EINT; goto cleanup; } if ((access == NCAC_ACCESS_DENY) && (partial_access == NCAC_ACCESS_PARTIAL_PERMIT)) { /* node itself is not allowed but a rule allows access to some descendants so it may be allowed at the end */ access = NCAC_ACCESS_PARTIAL_DENY; } cleanup: for (i = 0; i < group_count; ++i) { lydict_remove(ly_ctx, groups[i]); } free(groups); return access; } const struct lyd_node * ncac_check_operation(const struct lyd_node *data, const char *user) { const struct lyd_node *op; int allowed = 0; pthread_mutex_lock(&nacm.lock); /* check access for the whole data tree first */ if (ncac_allowed_tree(data->schema, user)) { allowed = 1; goto cleanup; } op = data; while (op) { if (op->schema->nodetype & (LYS_RPC | LYS_ACTION | LYS_NOTIF)) { /* we found the desired node */ break; } switch (op->schema->nodetype) { case LYS_CONTAINER: case LYS_LIST: if (!op->child) { /* list/container without children, invalid */ op = NULL; } else { op = op->child; } break; case LYS_LEAF: assert(lys_is_key((struct lys_node_leaf *)op->schema, NULL)); if (!op->next) { /* last key of the last in-depth list, invalid */ op = NULL; } else { op = op->next; } break; default: op = NULL; break; } } if (!op) { EINT; goto cleanup; } if (op->schema->nodetype & (LYS_RPC | LYS_ACTION)) { /* check X access on the RPC/action */ if (!NCAC_ACCESS_IS_NODE_PERMIT(ncac_allowed_node(op, user, NCAC_OP_EXEC))) { goto cleanup; } } else { assert(op->schema->nodetype == LYS_NOTIF); /* check R access on the notification */ if (!NCAC_ACCESS_IS_NODE_PERMIT(ncac_allowed_node(op, user, NCAC_OP_READ))) { goto cleanup; } } if (op->parent) { /* check R access on the parents, the last parent must be enough */ if (!NCAC_ACCESS_IS_NODE_PERMIT(ncac_allowed_node(op->parent, user, NCAC_OP_READ))) { goto cleanup; } } allowed = 1; cleanup: if (allowed) { op = NULL; } else { if (op->schema->nodetype & (LYS_RPC | LYS_ACTION)) { ++nacm.denied_operations; } else { ++nacm.denied_notifications; } } pthread_mutex_unlock(&nacm.lock); return op; } /** * @brief Filter out any siblings for which the user does not have R access, recursively. * * @param[in,out] first First sibling to filter. * @param[in] user User for the NACM filtering. * @return Highest access among descendants (recursively), permit is the highest. */ static enum ncac_access ncac_check_data_read_filter_r(struct lyd_node **first, const char *user) { struct lyd_node *next, *elem; enum ncac_access node_access, ret_access = NCAC_ACCESS_DENY; LY_TREE_FOR_SAFE(*first, next, elem) { /* check access of the node */ node_access = ncac_allowed_node(elem, user, NCAC_OP_READ); if (node_access == NCAC_ACCESS_PARTIAL_DENY) { /* only partial deny access, we must check children recursively to learn whether this node is allowed or not */ if (!(elem->schema->nodetype & (LYS_LEAF | LYS_LEAFLIST | LYS_ANYDATA))) { node_access = ncac_check_data_read_filter_r(&elem->child, user); } if (node_access != NCAC_ACCESS_PERMIT) { /* none of the descendants are actually permitted, access denied */ node_access = NCAC_ACCESS_DENY; } } else if (node_access == NCAC_ACCESS_PARTIAL_PERMIT) { /* partial permit, the node will be included in the reply but we must check children as well */ if (!(elem->schema->nodetype & (LYS_LEAF | LYS_LEAFLIST | LYS_ANYDATA))) { ncac_check_data_read_filter_r(&elem->child, user); } node_access = NCAC_ACCESS_PERMIT; } /* access denied, free the subtree */ if (node_access == NCAC_ACCESS_DENY) { /* never free keys */ if (!lys_is_key((struct lys_node_leaf *)elem->schema, NULL)) { if ((elem == *first) && !(*first)->parent) { *first = (*first)->next; } lyd_free(elem); } continue; } /* access is permitted, update return access and check the next sibling */ ret_access = NCAC_ACCESS_PERMIT; } return ret_access; } void ncac_check_data_read_filter(struct lyd_node **data, const char *user) { assert(data); pthread_mutex_lock(&nacm.lock); if (*data && !ncac_allowed_tree((*data)->schema, user)) { ncac_check_data_read_filter_r(data, user); } pthread_mutex_unlock(&nacm.lock); } /** * @brief Check whether diff node siblings can be applied by a user, recursively with children. * * @param[in] diff First diff sibling. * @param[in] user User for the NACM check. * @param[in] parent_op Inherited parent operation. * @return NULL if access allowed, otherwise the denied access data node. */ static const struct lyd_node * ncac_check_diff_r(const struct lyd_node *diff, const char *user, const char *parent_op) { const char *op; struct lyd_attr *attr; const struct lyd_node *node = NULL; uint8_t oper; LY_TREE_FOR(diff, diff) { /* find operation */ LY_TREE_FOR(diff->attr, attr) { if (!strcmp(attr->name, "operation")) { assert(!strcmp(attr->annotation->module->name, "ietf-netconf") || !strcmp(attr->annotation->module->name, "sysrepo")); break; } } if (attr) { op = attr->value_str; } else { op = parent_op; } assert(op); /* get required access operation */ switch (op[0]) { case 'n': /* "none" */ oper = 0; break; case 'r': /* "replace" */ assert(!strcmp(op, "replace")); oper = NCAC_OP_UPDATE; break; case 'c': /* "create" */ oper = NCAC_OP_CREATE; break; case 'd': /* "delete" */ oper = NCAC_OP_DELETE; break; default: EINT; return NULL; } /* check access for the node, none operation is always allowed, and partial access is relevant only for read operation */ if (oper && !NCAC_ACCESS_IS_NODE_PERMIT(ncac_allowed_node(diff, user, oper))) { node = diff; break; } /* go recursively */ if (!(diff->schema->nodetype & (LYS_LEAF | LYS_LEAFLIST | LYS_ANYDATA)) && diff->child) { node = ncac_check_diff_r(diff->child, user, op); } } return node; } const struct lyd_node * ncac_check_diff(const struct lyd_node *diff, const char *user) { const struct lyd_node *node = NULL; pthread_mutex_lock(&nacm.lock); /* any node can be used in this case */ if (!ncac_allowed_tree(diff->schema, user)) { node = ncac_check_diff_r(diff, user, NULL); if (node) { ++nacm.denied_data_writes; } } pthread_mutex_unlock(&nacm.lock); return node; } netopeer2-1.1.70/src/netconf_acm.h0000664000000000000000000001373514021433500015420 0ustar rootroot/** * @file netconf_acm.h * @author Michal Vasko <mvasko@cesnet.cz> * @brief NACM and ietf-netconf-acm callbacks header * * Copyright (c) 2019 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 NP2SRV_NETCONF_ACM_H_ #define NP2SRV_NETCONF_ACM_H_ #include <pthread.h> #include <stdint.h> #include <libyang/libyang.h> #define NCAC_OP_CREATE 0x01 /**< NACM operation create */ #define NCAC_OP_READ 0x02 /**< NACM operation read */ #define NCAC_OP_UPDATE 0x04 /**< NACM operation update */ #define NCAC_OP_DELETE 0x08 /**< NACM operation delete */ #define NCAC_OP_EXEC 0x10 /**< NACM operation exec */ #define NCAC_OP_ALL 0x1F /**< All NACM operations */ /** * @brief Rule target node type. */ typedef enum ncac_target_type { NCAC_TARGET_RPC, /**< Rule target is an RPC. */ NCAC_TARGET_NOTIF, /**< Rule target is a notification. */ NCAC_TARGET_DATA, /**< Rule target is a data node, action, or a nested notification. */ NCAC_TARGET_ANY, /**< Rule target is any node. */ } NCAC_TARGET_TYPE; /** * @brief Main NACM container structure. */ struct ncac { char enabled; /**< Whether NACM is enabled. */ char default_read_deny; /**< Whether default NACM read action is "deny" (otherwise "permit"). */ char default_write_deny; /**< Whether default NACM write action is "deny" (otherwise "permit"). */ char default_exec_deny; /**< Whether default NACM exec action is "deny" (otherwise "permit"). */ char enable_external_groups; /**< Whether external (system) groups are taken into consideration for NACM. */ uint32_t denied_operations; /**< Counter of denied operations (RPC or action). */ uint32_t denied_data_writes; /**< Counter of denied data writes. */ uint32_t denied_notifications; /**< Counter of denied notifications. */ /** * @brief NACM group. */ struct ncac_group { const char *name; /**< Group name. */ char **users; /**< Array of users belonging to this group. */ uint32_t user_count; /**< Number of users. */ } *groups; /**< Array of existing groups. */ uint32_t group_count; /**< Number of groups. */ /** * @brief NACM rule list. */ struct ncac_rule_list { const char *name; /**< Rule list name. */ char **groups; /**< All groups associated with this rule list. */ uint32_t group_count; /**< Number of groups. */ /** * @brief NACM rule. */ struct ncac_rule { const char *name; /**< Rule name. */ const char *module_name; /**< Rule module name. */ const char *target; /**< Rule target. */ NCAC_TARGET_TYPE target_type; /**< Rule target type. */ uint8_t operations; /**< Rule operations associated with it. */ char action_deny; /**< Whether the rule action is "deny" (otherwise "permit"). */ const char *comment; /**< Rule comment. */ struct ncac_rule *next; /**< Pointer to the next rule. */ } *rules; /**< List of rules in the rule list. */ struct ncac_rule_list *next; /**< Pointer to the next rule list. */ } *rule_lists; /**< List of all the rule lists. */ pthread_mutex_t lock; }; int ncac_nacm_params_cb(sr_session_ctx_t *session, const char *module_name, const char *xpath, sr_event_t event, uint32_t request_id, void *private_data); int ncac_state_data_cb(sr_session_ctx_t *session, const char *module_name, const char *path, const char *request_xpath, uint32_t request_id, struct lyd_node **parent, void *private_data); int ncac_group_cb(sr_session_ctx_t *session, const char *module_name, const char *xpath, sr_event_t event, uint32_t request_id, void *private_data); int ncac_rule_list_cb(sr_session_ctx_t *session, const char *module_name, const char *xpath, sr_event_t event, uint32_t request_id, void *private_data); int ncac_rule_cb(sr_session_ctx_t *session, const char *module_name, const char *xpath, sr_event_t event, uint32_t request_id, void *private_data); void ncac_init(void); void ncac_destroy(void); /** * @brief Check whether an operation is allowed for a user. * * According to https://tools.ietf.org/html/rfc8341#section-3.1.3 * RPC must have X access (except close-session), action additional R access on parent nodes. * Notification must have R access on itself and any parent nodes. * Recovery session is allowed by default. * * @param[in] data Top-level node of the operation. * @param[in] user User for the NACM check. * @return NULL if access allowed, otherwise the denied access data node. */ const struct lyd_node *ncac_check_operation(const struct lyd_node *data, const char *user); /** * @brief Filter out any data for which the user does not have R access. * * According to https://tools.ietf.org/html/rfc8341#section-3.2.4 * Recovery session is allowed all nodes by default. * * @param[in,out] data Data to filter. * @param[in] user User for the NACM filtering. */ void ncac_check_data_read_filter(struct lyd_node **data, const char *user); /** * @brief Check whether a diff (simplified edit-config tree) can be * applied by a user. * * According to https://tools.ietf.org/html/rfc8341#section-3.2.5 * Check C access for created nodes, D access for deleted nodes, * and U access for changed nodes. * Recovery session is allowed by default. * * @param[in] diff Diff tree to check. * @param[in] user User for the NACM check. * @return NULL if access allowed, otherwise the denied access data node. */ const struct lyd_node *ncac_check_diff(const struct lyd_node *diff, const char *user); #endif /* NP2SRV_NETCONF_ACM_H_ */ netopeer2-1.1.70/src/netconf_monitoring.c0000664000000000000000000002563614021433500017043 0ustar rootroot/** * @file netconf_monitoring.c * @author Michal Vasko <mvasko@cesnet.cz> * @brief ietf-netconf-monitoring statistics and counters * * Copyright (c) 2019 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 <time.h> #include <stdint.h> #include <stdlib.h> #include <string.h> #include <pthread.h> #include <libyang/libyang.h> #include <nc_server.h> #include "common.h" #include "log.h" #include "netconf_monitoring.h" #define NCM_TIMEZONE "CET" struct ncm stats; void ncm_init(void) { stats.netconf_start_time = time(NULL); pthread_mutex_init(&stats.lock, NULL); } void ncm_destroy(void) { free(stats.sessions); free(stats.session_stats); pthread_mutex_destroy(&stats.lock); } static uint32_t find_session_idx(struct nc_session *session) { uint32_t i; for (i = 0; i < stats.session_count; ++i) { if (nc_session_get_id(stats.sessions[i]) == nc_session_get_id(session)) { return i; } } EINT; return 0; } static int ncm_is_monitored(struct nc_session *session) { switch (nc_session_get_ti(session)) { #ifdef NC_ENABLED_SSH case NC_TI_LIBSSH: #endif #ifdef NC_ENABLED_TLS case NC_TI_OPENSSL: #endif #if defined(NC_ENABLED_SSH) || defined(NC_ENABLED_TLS) return 1; #endif default: break; } return 0; } void ncm_session_rpc(struct nc_session *session) { if (!ncm_is_monitored(session)) { return; } pthread_mutex_lock(&stats.lock); ++stats.session_stats[find_session_idx(session)].in_rpcs; ++stats.global_stats.in_rpcs; pthread_mutex_unlock(&stats.lock); } void ncm_session_bad_rpc(struct nc_session *session) { if (!ncm_is_monitored(session)) { return; } pthread_mutex_lock(&stats.lock); ++stats.session_stats[find_session_idx(session)].in_bad_rpcs; ++stats.global_stats.in_bad_rpcs; pthread_mutex_unlock(&stats.lock); } void ncm_session_rpc_reply_error(struct nc_session *session) { if (!ncm_is_monitored(session)) { return; } pthread_mutex_lock(&stats.lock); ++stats.session_stats[find_session_idx(session)].out_rpc_errors; ++stats.global_stats.out_rpc_errors; pthread_mutex_unlock(&stats.lock); } void ncm_session_notification(struct nc_session *session) { if (!ncm_is_monitored(session)) { return; } pthread_mutex_lock(&stats.lock); ++stats.session_stats[find_session_idx(session)].out_notifications; ++stats.global_stats.out_notifications; pthread_mutex_unlock(&stats.lock); } void ncm_session_add(struct nc_session *session) { void *new; if (!ncm_is_monitored(session)) { WRN("Session %d uses a transport protocol not supported by ietf-netconf-monitoring, will not be monitored.", nc_session_get_id(session)); return; } pthread_mutex_lock(&stats.lock); ++stats.in_sessions; ++stats.session_count; new = realloc(stats.sessions, stats.session_count * sizeof *stats.sessions); if (!new) { EMEM; return; } stats.sessions = new; new = realloc(stats.session_stats, stats.session_count * sizeof *stats.session_stats); if (!new) { EMEM; return; } stats.session_stats = new; stats.sessions[stats.session_count - 1] = session; memset(&stats.session_stats[stats.session_count - 1], 0, sizeof *stats.session_stats); pthread_mutex_unlock(&stats.lock); } void ncm_session_del(struct nc_session *session) { uint32_t i; if (!ncm_is_monitored(session)) { return; } pthread_mutex_lock(&stats.lock); if (!nc_session_get_term_reason(session)) { EINT; } if (nc_session_get_term_reason(session) != NC_SESSION_TERM_CLOSED) { ++stats.dropped_sessions; } i = find_session_idx(session); --stats.session_count; if (stats.session_count && (i < stats.session_count)) { memmove(&stats.sessions[i], &stats.sessions[i + 1], (stats.session_count - i) * sizeof *stats.sessions); memmove(&stats.session_stats[i], &stats.session_stats[i + 1], (stats.session_count - i) * sizeof *stats.session_stats); } pthread_mutex_unlock(&stats.lock); } void ncm_bad_hello(struct nc_session *session) { if (!ncm_is_monitored(session)) { return; } pthread_mutex_lock(&stats.lock); ++stats.in_bad_hellos; pthread_mutex_unlock(&stats.lock); } struct lyd_node * ncm_get_data(sr_conn_ctx_t *conn) { struct lyd_node *root = NULL, *cont, *list, *cont2, *cont3; const struct lys_module *mod; struct ly_ctx *ly_ctx; const char **cpblts; char buf[26]; uint32_t i, nc_id; int rc, is_locked; time_t ts; ly_ctx = (struct ly_ctx *)sr_get_context(conn); root = lyd_new_path(NULL, ly_ctx, "/ietf-netconf-monitoring:netconf-state", NULL, 0, 0); if (!root) { goto error; } /* capabilities */ cont = lyd_new(root, NULL, "capabilities"); cpblts = nc_server_get_cpblts_version(ly_ctx, LYS_VERSION_1); if (!cpblts) { goto error; } for (i = 0; cpblts[i]; ++i) { lyd_new_leaf(cont, NULL, "capability", cpblts[i]); lydict_remove(ly_ctx, cpblts[i]); } free(cpblts); cont = lyd_new(root, NULL, "datastores"); list = lyd_new(cont, NULL, "datastore"); lyd_new_leaf(list, NULL, "name", "running"); rc = sr_get_lock(conn, SR_DS_RUNNING, NULL, &is_locked, NULL, &nc_id, &ts); if (rc != SR_ERR_OK) { WRN("Failed to learn about running lock (%s).", sr_strerror(rc)); } else if (is_locked) { cont2 = lyd_new(list, NULL, "locks"); cont3 = lyd_new(cont2, NULL, "global-lock"); sprintf(buf, "%u", nc_id); lyd_new_leaf(cont3, NULL, "locked-by-session", buf); nc_time2datetime(ts, NCM_TIMEZONE, buf); lyd_new_leaf(cont3, NULL, "locked-time", buf); } list = lyd_new(cont, NULL, "datastore"); lyd_new_leaf(list, NULL, "name", "startup"); rc = sr_get_lock(conn, SR_DS_STARTUP, NULL, &is_locked, NULL, &nc_id, &ts); if (rc != SR_ERR_OK) { WRN("Failed to learn about startup lock (%s).", sr_strerror(rc)); } else if (is_locked) { cont2 = lyd_new(list, NULL, "locks"); cont3 = lyd_new(cont2, NULL, "global-lock"); sprintf(buf, "%u", nc_id); lyd_new_leaf(cont3, NULL, "locked-by-session", buf); nc_time2datetime(ts, NCM_TIMEZONE, buf); lyd_new_leaf(cont3, NULL, "locked-time", buf); } list = lyd_new(cont, NULL, "datastore"); lyd_new_leaf(list, NULL, "name", "candidate"); rc = sr_get_lock(conn, SR_DS_CANDIDATE, NULL, &is_locked, NULL, &nc_id, &ts); if (rc != SR_ERR_OK) { WRN("Failed to learn about candidate lock (%s).", sr_strerror(rc)); } else if (is_locked) { cont2 = lyd_new(list, NULL, "locks"); cont3 = lyd_new(cont2, NULL, "global-lock"); sprintf(buf, "%u", nc_id); lyd_new_leaf(cont3, NULL, "locked-by-session", buf); nc_time2datetime(ts, NCM_TIMEZONE, buf); lyd_new_leaf(cont3, NULL, "locked-time", buf); } /* schemas */ cont = lyd_new(root, NULL, "schemas"); i = 0; while ((mod = ly_ctx_get_module_iter(ly_ctx, &i))) { list = lyd_new(cont, NULL, "schema"); lyd_new_leaf(list, NULL, "identifier", mod->name); lyd_new_leaf(list, NULL, "version", (mod->rev ? mod->rev[0].date : NULL)); lyd_new_leaf(list, NULL, "format", "yang"); lyd_new_leaf(list, NULL, "namespace", lys_main_module(mod)->ns); lyd_new_leaf(list, NULL, "location", "NETCONF"); list = lyd_new(cont, NULL, "schema"); lyd_new_leaf(list, NULL, "identifier", mod->name); lyd_new_leaf(list, NULL, "version", (mod->rev ? mod->rev[0].date : NULL)); lyd_new_leaf(list, NULL, "format", "yin"); lyd_new_leaf(list, NULL, "namespace", lys_main_module(mod)->ns); lyd_new_leaf(list, NULL, "location", "NETCONF"); } /* sessions */ pthread_mutex_lock(&stats.lock); if (stats.session_count) { cont = lyd_new(root, NULL, "sessions"); for (i = 0; i < stats.session_count; ++i) { list = lyd_new(cont, NULL, "session"); sprintf(buf, "%u", nc_session_get_id(stats.sessions[i])); lyd_new_leaf(list, NULL, "session-id", buf); switch (nc_session_get_ti(stats.sessions[i])) { #ifdef NC_ENABLED_SSH case NC_TI_LIBSSH: lyd_new_leaf(list, NULL, "transport", "netconf-ssh"); break; #endif #ifdef NC_ENABLED_TLS case NC_TI_OPENSSL: lyd_new_leaf(list, NULL, "transport", "netconf-tls"); break; #endif default: /* NC_TI_FD, NC_TI_NONE */ ERR("ietf-netconf-monitoring unsupported session transport type."); pthread_mutex_unlock(&stats.lock); goto error; } lyd_new_leaf(list, NULL, "username", nc_session_get_username(stats.sessions[i])); lyd_new_leaf(list, NULL, "source-host", nc_session_get_host(stats.sessions[i])); nc_time2datetime(nc_session_get_start_time(stats.sessions[i]), NCM_TIMEZONE, buf); lyd_new_leaf(list, NULL, "login-time", buf); sprintf(buf, "%u", stats.session_stats[i].in_rpcs); lyd_new_leaf(list, NULL, "in-rpcs", buf); sprintf(buf, "%u", stats.session_stats[i].in_bad_rpcs); lyd_new_leaf(list, NULL, "in-bad-rpcs", buf); sprintf(buf, "%u", stats.session_stats[i].out_rpc_errors); lyd_new_leaf(list, NULL, "out-rpc-errors", buf); sprintf(buf, "%u", stats.session_stats[i].out_notifications); lyd_new_leaf(list, NULL, "out-notifications", buf); } } /* statistics */ cont = lyd_new(root, NULL, "statistics"); nc_time2datetime(stats.netconf_start_time, NCM_TIMEZONE, buf); lyd_new_leaf(cont, NULL, "netconf-start-time", buf); sprintf(buf, "%u", stats.in_bad_hellos); lyd_new_leaf(cont, NULL, "in-bad-hellos", buf); sprintf(buf, "%u", stats.in_sessions); lyd_new_leaf(cont, NULL, "in-sessions", buf); sprintf(buf, "%u", stats.dropped_sessions); lyd_new_leaf(cont, NULL, "dropped-sessions", buf); sprintf(buf, "%u", stats.global_stats.in_rpcs); lyd_new_leaf(cont, NULL, "in-rpcs", buf); sprintf(buf, "%u", stats.global_stats.in_bad_rpcs); lyd_new_leaf(cont, NULL, "in-bad-rpcs", buf); sprintf(buf, "%u", stats.global_stats.out_rpc_errors); lyd_new_leaf(cont, NULL, "out-rpc-errors", buf); sprintf(buf, "%u", stats.global_stats.out_notifications); lyd_new_leaf(cont, NULL, "out-notifications", buf); pthread_mutex_unlock(&stats.lock); if (lyd_validate(&root, LYD_OPT_NOSIBLINGS, NULL)) { goto error; } return root; error: lyd_free(root); return NULL; } netopeer2-1.1.70/src/netconf_monitoring.h0000664000000000000000000000300614021433500017033 0ustar rootroot/** * @file netconf_monitoring.h * @author Michal Vasko <mvasko@cesnet.cz> * @brief ietf-netconf-monitoring statistics and counters header * * Copyright (c) 2019 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 NP2SRV_NETCONF_MONITORING_H_ #define NP2SRV_NETCONF_MONITORING_H_ #include <pthread.h> #include <nc_server.h> #include <sysrepo.h> struct ncm_session_stats { uint32_t in_rpcs; uint32_t in_bad_rpcs; uint32_t out_rpc_errors; uint32_t out_notifications; }; struct ncm { struct nc_session **sessions; struct ncm_session_stats *session_stats; uint32_t session_count; time_t netconf_start_time; uint32_t in_bad_hellos; uint32_t in_sessions; uint32_t dropped_sessions; struct ncm_session_stats global_stats; pthread_mutex_t lock; }; void ncm_init(void); void ncm_destroy(void); void ncm_session_rpc(struct nc_session *session); void ncm_session_bad_rpc(struct nc_session *session); void ncm_session_rpc_reply_error(struct nc_session *session); void ncm_session_notification(struct nc_session *session); void ncm_session_add(struct nc_session *session); void ncm_session_del(struct nc_session *session); void ncm_bad_hello(struct nc_session *session); struct lyd_node *ncm_get_data(sr_conn_ctx_t *conn); #endif /* NP2SRV_NETCONF_MONITORING_H_ */ netopeer2-1.1.70/src/netconf_nmda.c0000664000000000000000000002464014021433500015567 0ustar rootroot/** * @file netconf_nmda.c * @author Michal Vasko <mvasko@cesnet.cz> * @brief ietf-netconf-nmda callbacks * * Copyright (c) 2019 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 <stdio.h> #include <assert.h> #include <inttypes.h> #include <unistd.h> #include <string.h> #include <sysrepo.h> #include <libyang/libyang.h> #include "config.h" #include "common.h" #include "log.h" #include "netconf_acm.h" /** * @brief Perform origin filtering. * * @param[in,out] data Data to filter. * @param[in] filter Origin filter identity. * @param[in] negated Whether the filter is negated. * @return Sysrepo error value. */ static int op_data_filter_origin(struct lyd_node **data, const struct lys_ident *filter, int negated) { struct ly_set *set; struct lyd_node *node; char *xpath; int ret; uint32_t i; if (!*data) { return SR_ERR_OK; } if (negated) { ret = asprintf(&xpath, "//*[@origin and derived-from-or-self(@origin, '%s:%s')]", filter->module->name, filter->name); } else { ret = asprintf(&xpath, "//*[@origin and not(derived-from-or-self(@origin, '%s:%s'))]", filter->module->name, filter->name); } if (ret == -1) { EMEM; return SR_ERR_NOMEM; } set = lyd_find_path(*data, xpath); free(xpath); if (!set) { return SR_ERR_INTERNAL; } if (set->number) { /* go backwards to allow safe node freeing */ i = set->number; do { --i; node = set->set.d[i]; if (node->schema->flags & LYS_CONFIG_R) { /* state nodes are not affected */ continue; } /* free non-matching subtree */ if (node == *data) { *data = (*data)->next; } lyd_free(node); } while (i); } ly_set_free(set); return SR_ERR_OK; } int np2srv_rpc_getdata_cb(sr_session_ctx_t *session, const char *UNUSED(op_path), const struct lyd_node *input, sr_event_t event, uint32_t UNUSED(request_id), struct lyd_node *output, void *UNUSED(private_data)) { struct lyd_node_leaf_list *leaf; struct lyd_node *node, *select_data = NULL, *data = NULL; struct np2_filter filter = {0}; int i, rc = SR_ERR_OK; sr_session_ctx_t *user_sess; uint32_t max_depth = 0; struct ly_set *nodeset; sr_datastore_t ds; NC_WD_MODE nc_wd; sr_get_oper_options_t get_opts = 0; char *username = NULL; if (event == SR_EV_ABORT) { /* ignore in this case */ return SR_ERR_OK; } /* Get username. It is assumed that right now the NETCONF session cannot end * due to the RPC lock held while np2srv_rpc_cb() is executing (which called this callback). */ if ((username = (char *)np_get_nc_sess_user(session))) { if (!(username = strdup(username))) { EMEM; rc = SR_ERR_NOMEM; goto cleanup; } } else { EINT; rc = SR_ERR_INTERNAL; goto cleanup; } /* get default value for with-defaults */ nc_server_get_capab_withdefaults(&nc_wd, NULL); /* get know which datastore is being affected */ nodeset = lyd_find_path(input, "datastore"); leaf = (struct lyd_node_leaf_list *)nodeset->set.d[0]; ly_set_free(nodeset); if (!strcmp(leaf->value.ident->name, "running")) { ds = SR_DS_RUNNING; } else if (!strcmp(leaf->value.ident->name, "startup")) { ds = SR_DS_STARTUP; } else if (!strcmp(leaf->value.ident->name, "candidate")) { ds = SR_DS_CANDIDATE; } else if (!strcmp(leaf->value.ident->name, "operational")) { ds = SR_DS_OPERATIONAL; } else { rc = SR_ERR_INVAL_ARG; sr_set_error(session, NULL, "Datastore \"%s\" is not supported.", leaf->value_str); goto cleanup; } /* create filters */ nodeset = lyd_find_path(input, "subtree-filter | xpath-filter"); node = nodeset->number ? nodeset->set.d[0] : NULL; ly_set_free(nodeset); if (node && !strcmp(node->schema->name, "subtree-filter")) { if (op_filter_create(node, &filter)) { rc = SR_ERR_INTERNAL; goto cleanup; } } else { filter.filters = malloc(sizeof *filter.filters); if (!filter.filters) { EMEM; rc = SR_ERR_NOMEM; goto cleanup; } filter.count = 1; filter.filters[0].str = node ? strdup(((struct lyd_node_leaf_list *)node)->value_str) : strdup("/*"); if (!filter.filters[0].str) { EMEM; rc = SR_ERR_NOMEM; goto cleanup; } filter.filters[0].selection = 1; } /* config filter */ nodeset = lyd_find_path(input, "config-filter"); leaf = nodeset->number ? (struct lyd_node_leaf_list *)nodeset->set.d[0] : NULL; ly_set_free(nodeset); if (leaf) { if (!strcmp(leaf->value_str, "false")) { get_opts |= SR_OPER_NO_CONFIG; } else { get_opts |= SR_OPER_NO_STATE; } } /* depth */ nodeset = lyd_find_path(input, "max-depth"); leaf = (struct lyd_node_leaf_list *)nodeset->set.d[0]; ly_set_free(nodeset); if (leaf && strcmp(leaf->value_str, "unbounded")) { max_depth = leaf->value.uint16; } /* origin */ nodeset = lyd_find_path(input, "with-origin"); leaf = nodeset->number ? (struct lyd_node_leaf_list *)nodeset->set.d[0] : NULL; ly_set_free(nodeset); if (leaf) { get_opts |= SR_OPER_WITH_ORIGIN; } /* get with-defaults mode */ nodeset = lyd_find_path(input, "ietf-netconf-with-defaults:with-defaults"); leaf = nodeset->number ? (struct lyd_node_leaf_list *)nodeset->set.d[0] : NULL; ly_set_free(nodeset); if (leaf) { if (!strcmp(leaf->value_str, "report-all")) { nc_wd = NC_WD_ALL; } else if (!strcmp(leaf->value_str, "report-all-tagged")) { nc_wd = NC_WD_ALL_TAG; } else if (!strcmp(leaf->value_str, "trim")) { nc_wd = NC_WD_TRIM; } else { assert(!strcmp(leaf->value_str, "explicit")); nc_wd = NC_WD_EXPLICIT; } } /* get the user session */ user_sess = np_get_user_sess(session); if (!user_sess) { EINT; rc = SR_ERR_INTERNAL; goto cleanup; } /* update sysrepo session datastore */ sr_session_switch_ds(user_sess, ds); /* * create the data tree for the data reply */ if ((rc = op_filter_data_get(user_sess, max_depth, get_opts, &filter, session, &select_data))) { goto cleanup; } if ((rc = op_filter_data_filter(&select_data, &filter, 0, &data))) { goto cleanup; } /* origin filter */ nodeset = lyd_find_path(input, "origin-filter | negated-origin-filter"); for (i = 0; i < (signed)nodeset->number; ++i) { leaf = (struct lyd_node_leaf_list *)nodeset->set.d[i]; op_data_filter_origin(&data, leaf->value.ident, strcmp(leaf->schema->name, "origin-filter")); } ly_set_free(nodeset); /* perform correct NACM filtering */ ncac_check_data_read_filter(&data, username); /* add output */ node = lyd_new_output_anydata(output, NULL, "data", data, LYD_ANYDATA_DATATREE); if (!node) { goto cleanup; } data = NULL; /* success */ cleanup: op_filter_erase(&filter); lyd_free_withsiblings(select_data); lyd_free_withsiblings(data); free(username); return rc; } int np2srv_rpc_editdata_cb(sr_session_ctx_t *session, const char *UNUSED(op_path), const struct lyd_node *input, sr_event_t event, uint32_t UNUSED(request_id), struct lyd_node *UNUSED(output), void *UNUSED(private_data)) { sr_datastore_t ds; struct ly_set *nodeset; struct lyd_node_leaf_list *leaf; struct lyd_node *node, *config = NULL; const sr_error_info_t *err_info; sr_session_ctx_t *user_sess; const char *defop; int rc = SR_ERR_OK; if (event == SR_EV_ABORT) { /* ignore in this case */ return SR_ERR_OK; } /* get know which datastore is being affected */ nodeset = lyd_find_path(input, "datastore"); leaf = (struct lyd_node_leaf_list *)nodeset->set.d[0]; ly_set_free(nodeset); if (!strcmp(leaf->value.ident->name, "running")) { ds = SR_DS_RUNNING; } else if (!strcmp(leaf->value.ident->name, "startup")) { ds = SR_DS_STARTUP; } else if (!strcmp(leaf->value.ident->name, "candidate")) { ds = SR_DS_CANDIDATE; } else { rc = SR_ERR_INVAL_ARG; sr_set_error(session, NULL, "Datastore \"%s\" is not supported or writable.", leaf->value_str); goto cleanup; } /* default-operation */ nodeset = lyd_find_path(input, "default-operation"); leaf = (struct lyd_node_leaf_list *)nodeset->set.d[0]; ly_set_free(nodeset); defop = leaf->value_str; /* config */ nodeset = lyd_find_path(input, "config | url"); node = nodeset->set.d[0]; ly_set_free(nodeset); if (!strcmp(node->schema->name, "config")) { config = op_parse_config((struct lyd_node_anydata *)node, LYD_OPT_EDIT | LYD_OPT_STRICT, &rc, session); if (rc) { goto cleanup; } } else { assert(!strcmp(node->schema->name, "url")); #ifdef NP2SRV_URL_CAPAB config = op_parse_url(((struct lyd_node_leaf_list *)node)->value_str, LYD_OPT_EDIT | LYD_OPT_STRICT, &rc, session); if (rc) { goto cleanup; } #else rc = SR_ERR_UNSUPPORTED; sr_set_error(session, NULL, "URL not supported."); goto cleanup; #endif } /* get the user session */ user_sess = np_get_user_sess(session); if (!user_sess) { EINT; rc = SR_ERR_INTERNAL; goto cleanup; } /* update sysrepo session datastore */ sr_session_switch_ds(user_sess, ds); /* sysrepo API */ rc = sr_edit_batch(user_sess, config, defop); if (rc != SR_ERR_OK) { goto cleanup; } rc = sr_apply_changes(user_sess, np2srv.sr_timeout, 1); if (rc != SR_ERR_OK) { sr_get_error(user_sess, &err_info); sr_set_error(session, err_info->err[0].xpath, err_info->err[0].message); goto cleanup; } /* success */ cleanup: lyd_free_withsiblings(config); return rc; } netopeer2-1.1.70/src/netconf_nmda.h0000664000000000000000000000166314021433500015574 0ustar rootroot/** * @file netconf_nmda.h * @author Michal Vasko <mvasko@cesnet.cz> * @brief ietf-netconf-nmda callbacks header * * Copyright (c) 2019 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 NP2SRV_NETCONF_NMDA_H_ #define NP2SRV_NETCONF_NMDA_H_ #include <libyang/libyang.h> #include <sysrepo.h> int np2srv_rpc_getdata_cb(sr_session_ctx_t *session, const char *op_path, const struct lyd_node *input, sr_event_t event, uint32_t request_id, struct lyd_node *output, void *private_data); int np2srv_rpc_editdata_cb(sr_session_ctx_t *session, const char *op_path, const struct lyd_node *input, sr_event_t event, uint32_t request_id, struct lyd_node *output, void *private_data); #endif /* NP2SRV_NMDA_H_ */ netopeer2-1.1.70/src/netconf_server.c0000664000000000000000000004610214021433500016153 0ustar rootroot/** * @file netconf_server.c * @author Michal Vasko <mvasko@cesnet.cz> * @brief ietf-netconf-server callbacks * * Copyright (c) 2019 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 /* asprintf() */ #include <stdbool.h> #include <stdint.h> #include <string.h> #include <assert.h> #include <nc_server.h> #include <libyang/libyang.h> #include <sysrepo.h> #include "common.h" #include "log.h" int np2srv_sr_get_privkey(const struct lyd_node *asym_key, char **privkey_data, NC_SSH_KEY_TYPE *privkey_type) { struct lyd_node_leaf_list *alg = NULL, *privkey = NULL; struct lyd_node *node; /* find the nodes */ LY_TREE_FOR(asym_key->child, node) { if (!strcmp(node->schema->name, "algorithm")) { alg = (struct lyd_node_leaf_list *)node; } else if (!strcmp(node->schema->name, "private-key")) { privkey = (struct lyd_node_leaf_list *)node; } } if (!alg || !privkey) { ERR("Failed to find asymmetric key information."); return -1; } /* set algorithm */ if (!strncmp(alg->value.ident->name, "rsa", 3)) { *privkey_type = NC_SSH_KEY_RSA; } else if (!strncmp(alg->value.ident->name, "secp", 4)) { *privkey_type = NC_SSH_KEY_ECDSA; } else { ERR("Unknown private key algorithm \"%s\".", alg->value_str); return -1; } /* set data */ *privkey_data = strdup(privkey->value_str); if (!*privkey_data) { EMEM; return -1; } return 0; } /* /ietf-netconf-server:netconf-server/listen/idle-timeout */ int np2srv_idle_timeout_cb(sr_session_ctx_t *session, const char *UNUSED(module_name), const char *xpath, sr_event_t UNUSED(event), uint32_t UNUSED(request_id), void *UNUSED(private_data)) { sr_change_iter_t *iter; sr_change_oper_t op; const struct lyd_node *node; const char *prev_val, *prev_list; bool prev_dflt; int rc; rc = sr_get_changes_iter(session, xpath, &iter); if (rc != SR_ERR_OK) { ERR("Getting changes iter failed (%s).", sr_strerror(rc)); return rc; } while ((rc = sr_get_change_tree_next(session, iter, &op, &node, &prev_val, &prev_list, &prev_dflt)) == SR_ERR_OK) { /* ignore other operations */ if ((op == SR_OP_CREATED) || (op == SR_OP_MODIFIED)) { nc_server_set_idle_timeout(((struct lyd_node_leaf_list *)node)->value.uint16); } } sr_free_change_iter(iter); if (rc != SR_ERR_NOT_FOUND) { ERR("Getting next change failed (%s).", sr_strerror(rc)); return rc; } return SR_ERR_OK; } static int np2srv_tcp_keepalives(const char *client_name, const char *endpt_name, sr_session_ctx_t *session, const char *xpath) { sr_change_iter_t *iter; sr_change_oper_t op; const struct lyd_node *node; const char *prev_val, *prev_list; bool prev_dflt; int rc, idle_time = -1, max_probes = -1, probe_interval = -1; rc = sr_get_changes_iter(session, xpath, &iter); if (rc != SR_ERR_OK) { ERR("Getting changes iter failed (%s).", sr_strerror(rc)); return rc; } while ((rc = sr_get_change_tree_next(session, iter, &op, &node, &prev_val, &prev_list, &prev_dflt)) == SR_ERR_OK) { if (!strcmp(node->schema->name, "idle-time")) { if (op == SR_OP_DELETED) { idle_time = 1; } else { idle_time = ((struct lyd_node_leaf_list *)node)->value.uint16; } } else if (!strcmp(node->schema->name, "max-probes")) { if (op == SR_OP_DELETED) { max_probes = 10; } else { max_probes = ((struct lyd_node_leaf_list *)node)->value.uint16; } } else if (!strcmp(node->schema->name, "probe-interval")) { if (op == SR_OP_DELETED) { probe_interval = 5; } else { probe_interval = ((struct lyd_node_leaf_list *)node)->value.uint16; } } } sr_free_change_iter(iter); if (rc != SR_ERR_NOT_FOUND) { ERR("Getting next change failed (%s).", sr_strerror(rc)); return rc; } rc = 0; /* set new keepalive parameters */ if (!client_name) { if (nc_server_is_endpt(endpt_name)) { rc = nc_server_endpt_set_keepalives(endpt_name, idle_time, max_probes, probe_interval); } } else { if (nc_server_ch_client_is_endpt(client_name, endpt_name)) { rc = nc_server_ch_client_endpt_set_keepalives(client_name, endpt_name, idle_time, max_probes, probe_interval); } } if (rc) { ERR("Keepalives configuration failed (%d).", rc); return SR_ERR_INTERNAL; } return SR_ERR_OK; } /* /ietf-netconf-server:netconf-server/listen/endpoint/ * /tcp-server-parameters */ int np2srv_endpt_tcp_params_cb(sr_session_ctx_t *session, const char *UNUSED(module_name), const char *xpath, sr_event_t UNUSED(event), uint32_t UNUSED(request_id), void *UNUSED(private_data)) { sr_change_iter_t *iter; sr_change_oper_t op; const struct lyd_node *node; const char *prev_val, *prev_list, *endpt_name; char *xpath2; bool prev_dflt; int rc, failed = 0; if (asprintf(&xpath2, "%s/*", xpath) == -1) { EMEM; return SR_ERR_NOMEM; } rc = sr_get_changes_iter(session, xpath2, &iter); free(xpath2); if (rc != SR_ERR_OK) { ERR("Getting changes iter failed (%s).", sr_strerror(rc)); return rc; } while ((rc = sr_get_change_tree_next(session, iter, &op, &node, &prev_val, &prev_list, &prev_dflt)) == SR_ERR_OK) { /* find name */ endpt_name = ((struct lyd_node_leaf_list *)node->parent->parent->parent->child)->value_str; if (!strcmp(node->schema->name, "local-address")) { if ((op == SR_OP_CREATED) || (op == SR_OP_MODIFIED)) { if (nc_server_endpt_set_address(endpt_name, ((struct lyd_node_leaf_list *)node)->value_str)) { failed = 1; } } } else if (!strcmp(node->schema->name, "local-port")) { if ((op == SR_OP_CREATED) || (op == SR_OP_MODIFIED)) { if (nc_server_endpt_set_port(endpt_name, ((struct lyd_node_leaf_list *)node)->value.uint16)) { failed = 1; } } } else if (!strcmp(node->schema->name, "keepalives")) { if (op == SR_OP_CREATED) { if (nc_server_endpt_enable_keepalives(endpt_name, 1)) { failed = 1; } } else if (op == SR_OP_DELETED) { if (nc_server_is_endpt(endpt_name)) { if (nc_server_endpt_enable_keepalives(endpt_name, 0)) { failed = 1; } } } /* set specific parameters */ if (asprintf(&xpath2, "%s/keepalives/*", xpath) == -1) { EMEM; return SR_ERR_NOMEM; } if (np2srv_tcp_keepalives(NULL, endpt_name, session, xpath2)) { failed = 1; } free(xpath2); } } sr_free_change_iter(iter); if (rc != SR_ERR_NOT_FOUND) { ERR("Getting next change failed (%s).", sr_strerror(rc)); return rc; } return failed ? SR_ERR_CALLBACK_FAILED : SR_ERR_OK; } static int np2srv_ch_periodic_connection_params(const char *client_name, sr_session_ctx_t *session, const char *xpath) { sr_change_iter_t *iter; sr_change_oper_t op; const struct lyd_node *node; const char *prev_val, *prev_list; bool prev_dflt; int rc; rc = sr_get_changes_iter(session, xpath, &iter); if (rc != SR_ERR_OK) { ERR("Getting changes iter failed (%s).", sr_strerror(rc)); return rc; } while ((rc = sr_get_change_tree_next(session, iter, &op, &node, &prev_val, &prev_list, &prev_dflt)) == SR_ERR_OK) { if (!strcmp(node->schema->name, "period")) { if (op == SR_OP_DELETED) { if (nc_server_ch_is_client(client_name)) { /* set default */ rc = nc_server_ch_client_periodic_set_period(client_name, 60); } } else { rc = nc_server_ch_client_periodic_set_period(client_name, ((struct lyd_node_leaf_list *)node)->value.uint16); } } else if (!strcmp(node->schema->name, "anchor-time")) { if (op == SR_OP_DELETED) { if (nc_server_ch_is_client(client_name)) { /* set default */ rc = nc_server_ch_client_periodic_set_anchor_time(client_name, 0); } } else { rc = nc_server_ch_client_periodic_set_anchor_time(client_name, nc_datetime2time(((struct lyd_node_leaf_list *)node)->value.string)); } } else if (!strcmp(node->schema->name, "idle-timeout")) { if (op == SR_OP_DELETED) { if (nc_server_ch_is_client(client_name)) { /* set default */ rc = nc_server_ch_client_periodic_set_idle_timeout(client_name, 120); } } else { rc = nc_server_ch_client_periodic_set_idle_timeout(client_name, ((struct lyd_node_leaf_list *)node)->value.uint16); } } if (rc) { sr_free_change_iter(iter); return SR_ERR_INTERNAL; } } sr_free_change_iter(iter); if (rc != SR_ERR_NOT_FOUND) { ERR("Getting next change failed (%s).", sr_strerror(rc)); return rc; } return SR_ERR_OK; } /* /ietf-netconf-server:netconf-server/call-home/netconf-client */ int np2srv_ch_client_cb(sr_session_ctx_t *session, const char *UNUSED(module_name), const char *xpath, sr_event_t UNUSED(event), uint32_t UNUSED(request_id), void *UNUSED(private_data)) { sr_change_iter_t *iter; sr_change_oper_t op; const struct lyd_node *node; const char *prev_val, *prev_list, *client_name; bool prev_dflt; int rc; rc = sr_get_changes_iter(session, xpath, &iter); if (rc != SR_ERR_OK) { ERR("Getting changes iter failed (%s).", sr_strerror(rc)); return rc; } while ((rc = sr_get_change_tree_next(session, iter, &op, &node, &prev_val, &prev_list, &prev_dflt)) == SR_ERR_OK) { /* get name */ client_name = ((struct lyd_node_leaf_list *)node->child)->value_str; /* ignore other operations */ if (op == SR_OP_CREATED) { rc = nc_server_ch_add_client(client_name); if (!rc) { rc = nc_connect_ch_client_dispatch(client_name, np2srv_new_session_cb); } } else if (op == SR_OP_DELETED) { rc = nc_server_ch_del_client(client_name); } if (rc) { sr_free_change_iter(iter); return SR_ERR_INTERNAL; } } sr_free_change_iter(iter); if (rc != SR_ERR_NOT_FOUND) { ERR("Getting next change failed (%s).", sr_strerror(rc)); return rc; } return SR_ERR_OK; } /* /ietf-netconf-server:netconf-server/call-home/netconf-client/endpoints/endpoint/ * /tcp-client-parameters */ int np2srv_ch_client_endpt_tcp_params_cb(sr_session_ctx_t *session, const char *UNUSED(module_name), const char *xpath, sr_event_t UNUSED(event), uint32_t UNUSED(request_id), void *UNUSED(private_data)) { sr_change_iter_t *iter; sr_change_oper_t op; const struct lyd_node *node; const char *prev_val, *prev_list, *endpt_name, *client_name; char *xpath2; bool prev_dflt; int rc; if (asprintf(&xpath2, "%s/*", xpath) == -1) { EMEM; return SR_ERR_NOMEM; } rc = sr_get_changes_iter(session, xpath2, &iter); free(xpath2); if (rc != SR_ERR_OK) { ERR("Getting changes iter failed (%s).", sr_strerror(rc)); return rc; } while ((rc = sr_get_change_tree_next(session, iter, &op, &node, &prev_val, &prev_list, &prev_dflt)) == SR_ERR_OK) { /* find names */ endpt_name = ((struct lyd_node_leaf_list *)node->parent->parent->parent->child)->value_str; client_name = ((struct lyd_node_leaf_list *)node->parent->parent->parent->parent->parent->child)->value_str; if (!strcmp(node->schema->name, "remote-address")) { if ((op == SR_OP_CREATED) || (op == SR_OP_MODIFIED)) { if (nc_server_ch_client_endpt_set_address(client_name, endpt_name, ((struct lyd_node_leaf_list *)node)->value_str)) { sr_free_change_iter(iter); return SR_ERR_INTERNAL; } } } else if (!strcmp(node->schema->name, "remote-port")) { if ((op == SR_OP_CREATED) || (op == SR_OP_MODIFIED)) { if (nc_server_ch_client_endpt_set_port(client_name, endpt_name, ((struct lyd_node_leaf_list *)node)->value.uint16)) { sr_free_change_iter(iter); return SR_ERR_INTERNAL; } } } else if (!strcmp(node->schema->name, "keepalives")) { if (op == SR_OP_CREATED) { rc = nc_server_ch_client_endpt_enable_keepalives(client_name, endpt_name, 1); } else if (op == SR_OP_DELETED) { if (nc_server_ch_client_is_endpt(client_name, endpt_name)) { rc = nc_server_ch_client_endpt_enable_keepalives(client_name, endpt_name, 0); } } if (rc) { sr_free_change_iter(iter); return SR_ERR_INTERNAL; } /* set specific parameters */ if (asprintf(&xpath2, "%s/keepalives/*", xpath) == -1) { EMEM; return SR_ERR_NOMEM; } rc = np2srv_tcp_keepalives(client_name, endpt_name, session, xpath2); free(xpath2); if (rc != SR_ERR_OK) { sr_free_change_iter(iter); return rc; } } } sr_free_change_iter(iter); if (rc != SR_ERR_NOT_FOUND) { ERR("Getting next change failed (%s).", sr_strerror(rc)); return rc; } return SR_ERR_OK; } /* /ietf-netconf-server:netconf-server/call-home/netconf-client/connection-type */ int np2srv_ch_connection_type_cb(sr_session_ctx_t *session, const char *UNUSED(module_name), const char *xpath, sr_event_t UNUSED(event), uint32_t UNUSED(request_id), void *UNUSED(private_data)) { sr_change_iter_t *iter; sr_change_oper_t op; const struct lyd_node *node; const char *prev_val, *prev_list, *client_name; char *xpath2; bool prev_dflt; int rc; if (asprintf(&xpath2, "%s/*", xpath) == -1) { EMEM; return SR_ERR_NOMEM; } rc = sr_get_changes_iter(session, xpath2, &iter); free(xpath2); if (rc != SR_ERR_OK) { ERR("Getting changes iter failed (%s).", sr_strerror(rc)); return rc; } while ((rc = sr_get_change_tree_next(session, iter, &op, &node, &prev_val, &prev_list, &prev_dflt)) == SR_ERR_OK) { /* find names */ client_name = ((struct lyd_node_leaf_list *)node->parent->parent->child)->value_str; /* connection type */ if (op == SR_OP_CREATED) { if (!strcmp(node->schema->name, "persistent")) { if (nc_server_ch_client_set_conn_type(client_name, NC_CH_PERSIST)) { sr_free_change_iter(iter); return SR_ERR_INTERNAL; } } else if (!strcmp(node->schema->name, "periodic")) { if (nc_server_ch_client_set_conn_type(client_name, NC_CH_PERIOD)) { sr_free_change_iter(iter); return SR_ERR_INTERNAL; } } } /* periodic connection type params */ if (!strcmp(node->schema->name, "periodic")) { if (asprintf(&xpath2, "%s/periodic/*", xpath) == -1) { EMEM; return SR_ERR_NOMEM; } rc = np2srv_ch_periodic_connection_params(client_name, session, xpath2); free(xpath2); if (rc != SR_ERR_OK) { return rc; } } } sr_free_change_iter(iter); if (rc != SR_ERR_NOT_FOUND) { ERR("Getting next change failed (%s).", sr_strerror(rc)); return rc; } return SR_ERR_OK; } /* /ietf-netconf-server:netconf-server/call-home/netconf-client/reconnect-strategy */ int np2srv_ch_reconnect_strategy_cb(sr_session_ctx_t *session, const char *UNUSED(module_name), const char *xpath, sr_event_t UNUSED(event), uint32_t UNUSED(request_id), void *UNUSED(private_data)) { sr_change_iter_t *iter; sr_change_oper_t op; const struct lyd_node *node; const char *prev_val, *prev_list, *client_name, *str; char *xpath2; bool prev_dflt; int rc; if (asprintf(&xpath2, "%s/*", xpath) == -1) { EMEM; return SR_ERR_NOMEM; } rc = sr_get_changes_iter(session, xpath2, &iter); free(xpath2); if (rc != SR_ERR_OK) { ERR("Getting changes iter failed (%s).", sr_strerror(rc)); return rc; } while ((rc = sr_get_change_tree_next(session, iter, &op, &node, &prev_val, &prev_list, &prev_dflt)) == SR_ERR_OK) { /* find name */ client_name = ((struct lyd_node_leaf_list *)node->parent->parent->child)->value_str; if (!strcmp(node->schema->name, "start-with")) { if (op == SR_OP_DELETED) { if (nc_server_ch_is_client(client_name)) { /* set default */ rc = nc_server_ch_client_set_start_with(client_name, NC_CH_FIRST_LISTED); } } else { str = ((struct lyd_node_leaf_list *)node)->value_str; if (!strcmp(str, "first-listed")) { rc = nc_server_ch_client_set_start_with(client_name, NC_CH_FIRST_LISTED); } else if (!strcmp(str, "last-connected")) { rc = nc_server_ch_client_set_start_with(client_name, NC_CH_LAST_CONNECTED); } else if (!strcmp(str, "random-selection")) { rc = nc_server_ch_client_set_start_with(client_name, NC_CH_RANDOM); } } } else if (!strcmp(node->schema->name, "max-attempts")) { if (op == SR_OP_DELETED) { if (nc_server_ch_is_client(client_name)) { /* set default */ rc = nc_server_ch_client_set_max_attempts(client_name, 3); } } else { rc = nc_server_ch_client_set_max_attempts(client_name, ((struct lyd_node_leaf_list *)node)->value.uint8); } } if (rc) { sr_free_change_iter(iter); return SR_ERR_INTERNAL; } } sr_free_change_iter(iter); if (rc != SR_ERR_NOT_FOUND) { ERR("Getting next change failed (%s).", sr_strerror(rc)); return rc; } return SR_ERR_OK; } netopeer2-1.1.70/src/netconf_server.h0000664000000000000000000000322414021433500016156 0ustar rootroot/** * @file netconf_server.h * @author Michal Vasko <mvasko@cesnet.cz> * @brief ietf-netconf-server callbacks header * * Copyright (c) 2019 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 NP2SRV_NETCONF_SERVER_H_ #define NP2SRV_NETCONF_SERVER_H_ #include <sysrepo.h> int np2srv_sr_get_privkey(const struct lyd_node *asym_key, char **privkey_data, NC_SSH_KEY_TYPE *privkey_type); int np2srv_idle_timeout_cb(sr_session_ctx_t *session, const char *module_name, const char *xpath, sr_event_t event, uint32_t request_id, void *private_data); int np2srv_endpt_tcp_params_cb(sr_session_ctx_t *session, const char *module_name, const char *xpath, uint32_t request_id, sr_event_t event, void *private_data); int np2srv_ch_client_cb(sr_session_ctx_t *session, const char *module_name, const char *xpath, sr_event_t event, uint32_t request_id, void *private_data); int np2srv_ch_client_endpt_tcp_params_cb(sr_session_ctx_t *session, const char *module_name, const char *xpath, uint32_t request_id, sr_event_t event, void *private_data); int np2srv_ch_connection_type_cb(sr_session_ctx_t *session, const char *module_name, const char *xpath, uint32_t request_id, sr_event_t event, void *private_data); int np2srv_ch_reconnect_strategy_cb(sr_session_ctx_t *session, const char *module_name, const char *xpath, uint32_t request_id, sr_event_t event, void *private_data); #endif /* NP2SRV_NETCONF_SERVER_H_ */ netopeer2-1.1.70/src/netconf_server_ssh.c0000664000000000000000000005103514021433500017031 0ustar rootroot/** * @file netconf_server_ssh.c * @author Michal Vasko <mvasko@cesnet.cz> * @brief ietf-netconf-server SSH callbacks * * Copyright (c) 2019 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 /* asprintf() */ #define _DEFAULT_SOURCE /* getpwent() */ #define _POSIX_C_SOURCE 200809L /* getline() */ #include <stdio.h> #include <stdint.h> #include <string.h> #include <assert.h> #include <errno.h> #include <sys/types.h> #include <pwd.h> #include <ctype.h> #include <libssh/libssh.h> #include <nc_server.h> #include <libyang/libyang.h> #include <sysrepo.h> #include "config.h" #include "common.h" #include "log.h" #include "netconf_server.h" int np2srv_hostkey_cb(const char *name, void *UNUSED(user_data), char **UNUSED(privkey_path), char **privkey_data, NC_SSH_KEY_TYPE *privkey_type) { sr_session_ctx_t *sr_sess; char *xpath; struct lyd_node *data = NULL; int r, rc = -1; r = sr_session_start(np2srv.sr_conn, SR_DS_RUNNING, &sr_sess); if (r != SR_ERR_OK) { return -1; } /* get hostkey data from sysrepo */ if (asprintf(&xpath, "/ietf-keystore:keystore/asymmetric-keys/asymmetric-key[name='%s']", name) == -1) { EMEM; goto cleanup; } r = sr_get_subtree(sr_sess, xpath, 0, &data); free(xpath); if (r != SR_ERR_OK) { goto cleanup; } else if (!data) { ERR("Hostkey \"%s\" not found.", name); goto cleanup; } /* parse private key values */ if (np2srv_sr_get_privkey(data, privkey_data, privkey_type)) { goto cleanup; } /* success */ rc = 0; cleanup: lyd_free_withsiblings(data); sr_session_stop(sr_sess); return rc; } int np2srv_pubkey_auth_cb(const struct nc_session *session, ssh_key key, void *UNUSED(user_data)) { FILE *f = NULL; struct passwd *pwd; ssh_key pub_key = NULL; enum ssh_keytypes_e ktype; const char *username; char *line = NULL, *ptr, *ptr2; size_t n; int r, ret = 1, line_num = 0; username = nc_session_get_username(session); errno = 0; pwd = getpwnam(username); if (!pwd) { ERR("Failed to find user entry for \"%s\" (%s).", username, errno ? strerror(errno) : "User not found"); goto cleanup; } /* check any authorized keys */ r = asprintf(&line, NP2SRV_SSH_AUTHORIZED_KEYS_PATTERN, NP2SRV_SSH_AUTHORIZED_KEYS_ARG_IS_USERNAME ? pwd->pw_name : pwd->pw_dir); if (r == -1) { EMEM; line = NULL; goto cleanup; } n = r; f = fopen(line, "r"); if (!f) { if (errno == ENOENT) { VRB("User \"%s\" has no authorized_keys file.", username); } else { ERR("Failed to open \"%s\" authorized_keys file (%s).", line, strerror(errno)); } goto cleanup; } while (getline(&line, &n, f) > -1) { ++line_num; /* separate key type */ ptr = line; for (ptr2 = ptr; !isspace(ptr2[0]); ++ptr2); if (ptr2[0] == '\0') { WRN("Invalid authorized key format of \"%s\" (line %d).", username, line_num); continue; } ptr2[0] = '\0'; /* detect key type */ ktype = ssh_key_type_from_name(ptr); if (ktype == SSH_KEYTYPE_UNKNOWN) { WRN("Unknown key type \"%s\" (line %d).", ptr, line_num); continue; } /* separate key data */ ptr = ptr2 + 1; for (ptr2 = ptr; !isspace(ptr2[0]); ++ptr2); ptr2[0] = '\0'; r = ssh_pki_import_pubkey_base64(ptr, ktype, &pub_key); if (r != SSH_OK) { WRN("Failed to import authorized key of \"%s\" (%s, line %d).", username, r == SSH_EOF ? "Unexpected end-of-file" : "SSH error", line_num); continue; } /* compare public keys */ if (!ssh_key_cmp(key, pub_key, SSH_KEY_CMP_PUBLIC)) { /* key matches */ ret = 0; goto cleanup; } /* not a match, next key */ ssh_key_free(pub_key); pub_key = NULL; } if (!feof(f)) { WRN("Failed reading from authorized_keys file of \"%s\".", username); goto cleanup; } /* no match */ cleanup: if (f) { fclose(f); } free(line); ssh_key_free(pub_key); return ret; } /* /ietf-netconf-server:netconf-server/listen/endpoint/ssh */ int np2srv_endpt_ssh_cb(sr_session_ctx_t *session, const char *UNUSED(module_name), const char *xpath, sr_event_t UNUSED(event), uint32_t UNUSED(request_id), void *UNUSED(private_data)) { sr_change_iter_t *iter; sr_change_oper_t op; const struct lyd_node *node; const char *prev_val, *prev_list, *endpt_name; bool prev_dflt; int rc, failed = 0; rc = sr_get_changes_iter(session, xpath, &iter); if (rc != SR_ERR_OK) { ERR("Getting changes iter failed (%s).", sr_strerror(rc)); return rc; } while ((rc = sr_get_change_tree_next(session, iter, &op, &node, &prev_val, &prev_list, &prev_dflt)) == SR_ERR_OK) { /* get name */ endpt_name = ((struct lyd_node_leaf_list *)node->parent->child)->value_str; /* ignore other operations */ if (op == SR_OP_CREATED) { if (nc_server_add_endpt(endpt_name, NC_TI_LIBSSH)) { failed = 1; } /* turn off all auth methods by default */ nc_server_ssh_endpt_set_auth_methods(endpt_name, 0); } else if (op == SR_OP_DELETED) { if (nc_server_del_endpt(endpt_name, NC_TI_LIBSSH)) { failed = 1; } } } sr_free_change_iter(iter); if (rc != SR_ERR_NOT_FOUND) { ERR("Getting next change failed (%s).", sr_strerror(rc)); return rc; } return failed ? SR_ERR_INTERNAL : SR_ERR_OK; } /* /ietf-netconf-server:netconf-server/listen/endpoint/ssh/ssh-server-parameters/server-identity/host-key/public-key/ * keystore-reference */ int np2srv_endpt_ssh_hostkey_cb(sr_session_ctx_t *session, const char *UNUSED(module_name), const char *xpath, sr_event_t UNUSED(event), uint32_t UNUSED(request_id), void *UNUSED(private_data)) { sr_change_iter_t *iter; sr_change_oper_t op; const struct lyd_node *node; const char *prev_val, *prev_list, *endpt_name; bool prev_dflt; int rc; rc = sr_get_changes_iter(session, xpath, &iter); if (rc != SR_ERR_OK) { ERR("Getting changes iter failed (%s).", sr_strerror(rc)); return rc; } while ((rc = sr_get_change_tree_next(session, iter, &op, &node, &prev_val, &prev_list, &prev_dflt)) == SR_ERR_OK) { /* find name */ endpt_name = ((struct lyd_node_leaf_list *)node->parent->parent->parent->parent->parent->parent->child)->value_str; /* ignore other operations */ if (op == SR_OP_CREATED) { rc = nc_server_ssh_endpt_add_hostkey(endpt_name, ((struct lyd_node_leaf_list *)node)->value_str, -1); } else if (op == SR_OP_DELETED) { if (nc_server_is_endpt(endpt_name)) { rc = nc_server_ssh_endpt_del_hostkey(endpt_name, ((struct lyd_node_leaf_list *)node)->value_str, -1); } } else if (op == SR_OP_MOVED) { rc = nc_server_ssh_endpt_mov_hostkey(endpt_name, ((struct lyd_node_leaf_list *)node)->value_str, prev_val); } if (rc) { sr_free_change_iter(iter); return SR_ERR_INTERNAL; } } sr_free_change_iter(iter); if (rc != SR_ERR_NOT_FOUND) { ERR("Getting next change failed (%s).", sr_strerror(rc)); return rc; } return SR_ERR_OK; } static int np2srv_ssh_update_auth_method(const struct lyd_node *node, sr_change_oper_t op, int cur_auth) { struct lyd_node_leaf_list *leaf; int auth; auth = cur_auth; if (!strcmp(node->schema->name, "publickey")) { if (op == SR_OP_CREATED) { auth |= NC_SSH_AUTH_PUBLICKEY; } else if (op == SR_OP_DELETED) { auth &= ~NC_SSH_AUTH_PUBLICKEY; } } else if (!strcmp(node->schema->name, "passsword")) { if (op == SR_OP_CREATED) { auth |= NC_SSH_AUTH_PASSWORD; } else if (op == SR_OP_DELETED) { auth &= ~NC_SSH_AUTH_PASSWORD; } } else if (!strcmp(node->schema->name, "hostbased") || !strcmp(node->schema->name, "none")) { WRN("SSH authentication \"%s\" not supported.", node->schema->name); } else if (!strcmp(node->schema->name, "other")) { leaf = (struct lyd_node_leaf_list *)node; if (!strcmp(leaf->value_str, "interactive")) { if (op == SR_OP_CREATED) { auth |= NC_SSH_AUTH_INTERACTIVE; } else if (op == SR_OP_DELETED) { auth &= ~NC_SSH_AUTH_INTERACTIVE; } } else { WRN("SSH authentication \"%s\" not supported.", leaf->value_str); } } return auth; } /* /ietf-netconf-server:netconf-server/listen/endpoint/ssh/ssh-server-parameters/client-authentication/ * supported-authentication-methods */ int np2srv_endpt_ssh_auth_methods_cb(sr_session_ctx_t *session, const char *UNUSED(module_name), const char *xpath, sr_event_t UNUSED(event), uint32_t UNUSED(request_id), void *UNUSED(private_data)) { sr_change_iter_t *iter; sr_change_oper_t op; const struct lyd_node *node; const char *prev_val, *prev_list, *endpt_name; char *xpath2; bool prev_dflt; int rc, auth; if (asprintf(&xpath2, "%s/*", xpath) == -1) { EMEM; return SR_ERR_NOMEM; } rc = sr_get_changes_iter(session, xpath2, &iter); free(xpath2); if (rc != SR_ERR_OK) { ERR("Getting changes iter failed (%s).", sr_strerror(rc)); return rc; } while ((rc = sr_get_change_tree_next(session, iter, &op, &node, &prev_val, &prev_list, &prev_dflt)) == SR_ERR_OK) { /* find name */ endpt_name = ((struct lyd_node_leaf_list *)node->parent->parent->parent->parent->parent->child)->value_str; if ((op == SR_OP_DELETED) && !nc_server_is_endpt(endpt_name)) { /* endpt deleted */ continue; } /* current methods */ auth = nc_server_ssh_endpt_get_auth_methods(endpt_name); auth = np2srv_ssh_update_auth_method(node, op, auth); /* updated methods */ if (nc_server_ssh_endpt_set_auth_methods(endpt_name, auth)) { sr_free_change_iter(iter); return SR_ERR_INTERNAL; } } sr_free_change_iter(iter); if (rc != SR_ERR_NOT_FOUND) { ERR("Getting next change failed (%s).", sr_strerror(rc)); return rc; } return SR_ERR_OK; } static int np2srv_user_add_auth_key(const char *alg, size_t alg_len, const char *key, size_t key_len, struct lyd_node *user, uint8_t *key_idx) { char name[7], *str; struct lyd_node *authkey; authkey = lyd_new(user, NULL, "authorized-key"); if (!authkey) { return -1; } /* name */ sprintf(name, "key%d", (*key_idx)++); if (!lyd_new_leaf(authkey, NULL, "name", name)) { return -1; } /* algorithm */ str = strndup(alg, alg_len); if (!str) { EMEM; return -1; } lyd_new_leaf(authkey, NULL, "algorithm", str); free(str); /* key-data */ str = strndup(key, key_len); if (!str) { EMEM; return -1; } lyd_new_leaf(authkey, NULL, "key-data", str); free(str); return 0; } /* /ietf-netconf-server:netconf-server/listen/endpoint/ssh/ssh-server-parameters/client-authentication/users */ /* /ietf-netconf-server:netconf-server/call-home/netconf-client/endpoints/endpoint/ssh/ssh-server-parameters/ * client-authentication/users */ int np2srv_endpt_ssh_auth_users_oper_cb(sr_session_ctx_t *UNUSED(session), const char *UNUSED(module_name), const char *UNUSED(path), const char *UNUSED(request_xpath), uint32_t UNUSED(request_id), struct lyd_node **parent, void *UNUSED(private_data)) { struct passwd *pwd; struct lyd_node *users, *user; char *path, *line = NULL, *ptr, *alg, *data; size_t line_len = 0; FILE *f = NULL; int rc = SR_ERR_INTERNAL; uint8_t key_idx; users = lyd_new(*parent, NULL, "users"); if (!users) { return SR_ERR_INTERNAL; } while ((pwd = getpwent())) { /* create user with name */ user = lyd_new(users, NULL, "user"); if (!user) { return SR_ERR_INTERNAL; } lyd_new_leaf(user, NULL, "name", pwd->pw_name); /* check any authorized keys */ if (asprintf(&path, NP2SRV_SSH_AUTHORIZED_KEYS_PATTERN, NP2SRV_SSH_AUTHORIZED_KEYS_ARG_IS_USERNAME ? pwd->pw_name : pwd->pw_dir) == -1) { EMEM; goto cleanup; } f = fopen(path, "r"); if (!f) { if (errno != ENOENT && errno != ENOTDIR && errno != EACCES) { ERR("Opening \"%s\" authorized key file failed (%s).", path, strerror(errno)); free(path); goto cleanup; } if (errno == EACCES) { VRB("Skipping \"%s\" authorized key file (%s).", path, strerror(errno)); } free(path); continue; } free(path); /* create authorized keys */ key_idx = 1; while (getline(&line, &line_len, f) != -1) { if ((line[0] == '\0') || (line[0] == '#')) { continue; } /* find algorithm */ ptr = line; while (strncmp(ptr, "ssh-dss", 7) && strncmp(ptr, "ssh-rsa", 7) && strncmp(ptr, "ecdsa", 5)) { ptr = strchr(ptr, ' '); if (!ptr) { break; } ++ptr; } if (!ptr) { /* unrecognized line */ continue; } alg = ptr; /* find data */ ptr = strchr(ptr, ' '); if (!ptr) { /* unrecognized line */ continue; } ++ptr; data = ptr; if (!(ptr = strchr(data, ' ')) && !(ptr = strchr(data, '\n'))) { ptr = data + strlen(data); } /* create new authorized key */ if (np2srv_user_add_auth_key(alg, strchr(alg, ' ') - alg, data, ptr - data, user, &key_idx)) { goto cleanup; } } if (ferror(f)) { ERR("Reading from an authorized keys file failed (%s).", strerror(errno)); goto cleanup; } fclose(f); f = NULL; } /* success */ rc = SR_ERR_OK; cleanup: free(line); if (f) { fclose(f); } endpwent(); return rc; } /* /ietf-netconf-server:netconf-server/call-home/netconf-client/endpoints/endpoint/ssh */ int np2srv_ch_client_endpt_ssh_cb(sr_session_ctx_t *session, const char *UNUSED(module_name), const char *xpath, sr_event_t UNUSED(event), uint32_t UNUSED(request_id), void *UNUSED(private_data)) { sr_change_iter_t *iter; sr_change_oper_t op; const struct lyd_node *node; const char *prev_val, *prev_list, *endpt_name, *client_name; bool prev_dflt; int rc; rc = sr_get_changes_iter(session, xpath, &iter); if (rc != SR_ERR_OK) { ERR("Getting changes iter failed (%s).", sr_strerror(rc)); return rc; } while ((rc = sr_get_change_tree_next(session, iter, &op, &node, &prev_val, &prev_list, &prev_dflt)) == SR_ERR_OK) { /* get names */ endpt_name = ((struct lyd_node_leaf_list *)node->parent->child)->value_str; client_name = ((struct lyd_node_leaf_list *)node->parent->parent->parent->child)->value_str; /* ignore other operations */ if (op == SR_OP_CREATED) { rc = nc_server_ch_client_add_endpt(client_name, endpt_name, NC_TI_LIBSSH); /* turn off all auth methods by default */ nc_server_ssh_ch_client_endpt_set_auth_methods(client_name, endpt_name, 0); } else if (op == SR_OP_DELETED) { rc = nc_server_ch_client_del_endpt(client_name, endpt_name, NC_TI_LIBSSH); } if (rc) { sr_free_change_iter(iter); return SR_ERR_INTERNAL; } } sr_free_change_iter(iter); if (rc != SR_ERR_NOT_FOUND) { ERR("Getting next change failed (%s).", sr_strerror(rc)); return rc; } return SR_ERR_OK; } /* /ietf-netconf-server:netconf-server/call-home/netconf-client/endpoints/endpoint/ssh/ssh-server-parameters/ * server-identity/host-key/public-key/keystore-reference */ int np2srv_ch_endpt_ssh_hostkey_cb(sr_session_ctx_t *session, const char *UNUSED(module_name), const char *xpath, sr_event_t UNUSED(event), uint32_t UNUSED(request_id), void *UNUSED(private_data)) { sr_change_iter_t *iter; sr_change_oper_t op; const struct lyd_node *node; const char *prev_val, *prev_list, *endpt_name, *client_name; bool prev_dflt; int rc; rc = sr_get_changes_iter(session, xpath, &iter); if (rc != SR_ERR_OK) { ERR("Getting changes iter failed (%s).", sr_strerror(rc)); return rc; } while ((rc = sr_get_change_tree_next(session, iter, &op, &node, &prev_val, &prev_list, &prev_dflt)) == SR_ERR_OK) { /* find name */ endpt_name = ((struct lyd_node_leaf_list *)node->parent->parent->parent->parent->parent->parent->child)->value_str; client_name = ((struct lyd_node_leaf_list *)node->parent->parent->parent->parent->parent->parent->parent->parent->child)->value_str; /* ignore other operations */ if (op == SR_OP_CREATED) { rc = nc_server_ssh_ch_client_endpt_add_hostkey(client_name, endpt_name, ((struct lyd_node_leaf_list *)node)->value_str, -1); } else if (op == SR_OP_DELETED) { if (nc_server_ch_client_is_endpt(client_name, endpt_name)) { rc = nc_server_ssh_ch_client_endpt_del_hostkey(client_name, endpt_name, ((struct lyd_node_leaf_list *)node)->value_str, -1); } } else if (op == SR_OP_MOVED) { rc = nc_server_ssh_ch_client_endpt_mov_hostkey(client_name, endpt_name, ((struct lyd_node_leaf_list *)node)->value_str, prev_val); } if (rc) { sr_free_change_iter(iter); return SR_ERR_INTERNAL; } } sr_free_change_iter(iter); if (rc != SR_ERR_NOT_FOUND) { ERR("Getting next change failed (%s).", sr_strerror(rc)); return rc; } return SR_ERR_OK; } /* /ietf-netconf-server:netconf-server/call-home/netconf-client/endpoints/endpoint/ssh/ssh-server-parameters/ * client-authentication/supported-authentication-methods */ int np2srv_ch_endpt_ssh_auth_methods_cb(sr_session_ctx_t *session, const char *UNUSED(module_name), const char *xpath, sr_event_t UNUSED(event), uint32_t UNUSED(request_id), void *UNUSED(private_data)) { sr_change_iter_t *iter; sr_change_oper_t op; const struct lyd_node *node; const char *prev_val, *prev_list, *endpt_name, *client_name; char *xpath2; bool prev_dflt; int rc, auth; if (asprintf(&xpath2, "%s/*", xpath) == -1) { EMEM; return SR_ERR_NOMEM; } rc = sr_get_changes_iter(session, xpath2, &iter); free(xpath2); if (rc != SR_ERR_OK) { ERR("Getting changes iter failed (%s).", sr_strerror(rc)); return rc; } while ((rc = sr_get_change_tree_next(session, iter, &op, &node, &prev_val, &prev_list, &prev_dflt)) == SR_ERR_OK) { /* find names */ endpt_name = ((struct lyd_node_leaf_list *)node->parent->parent->parent->parent->parent->child)->value_str; client_name = ((struct lyd_node_leaf_list *)node->parent->parent->parent->parent->parent->parent->parent->child)->value_str; if ((op == SR_OP_DELETED) && !nc_server_ch_client_is_endpt(client_name, endpt_name)) { continue; } /* current methods */ auth = nc_server_ssh_ch_client_endpt_get_auth_methods(client_name, endpt_name); auth = np2srv_ssh_update_auth_method(node, op, auth); /* updated methods */ if (nc_server_ssh_ch_client_endpt_set_auth_methods(client_name, endpt_name, auth)) { sr_free_change_iter(iter); return SR_ERR_INTERNAL; } } sr_free_change_iter(iter); if (rc != SR_ERR_NOT_FOUND) { ERR("Getting next change failed (%s).", sr_strerror(rc)); return rc; } return SR_ERR_OK; } netopeer2-1.1.70/src/netconf_server_ssh.h0000664000000000000000000000403214021433500017031 0ustar rootroot/** * @file netconf_server_ssh.h * @author Michal Vasko <mvasko@cesnet.cz> * @brief ietf-netconf-server SSH callbacks header * * Copyright (c) 2019 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 NP2SRV_NETCONF_SERVER_SSH_H_ #define NP2SRV_NETCONF_SERVER_SSH_H_ #include <libssh/libssh.h> #include <sysrepo.h> int np2srv_hostkey_cb(const char *name, void *user_data, char **privkey_path, char **privkey_data, NC_SSH_KEY_TYPE *privkey_type); int np2srv_pubkey_auth_cb(const struct nc_session *session, ssh_key key, void *user_data); int np2srv_endpt_ssh_cb(sr_session_ctx_t *session, const char *module_name, const char *xpath, sr_event_t event, uint32_t request_id, void *private_data); int np2srv_endpt_ssh_hostkey_cb(sr_session_ctx_t *session, const char *module_name, const char *xpath, uint32_t request_id, sr_event_t event, void *private_data); int np2srv_endpt_ssh_auth_methods_cb(sr_session_ctx_t *session, const char *module_name, const char *xpath, uint32_t request_id, sr_event_t event, void *private_data); int np2srv_endpt_ssh_auth_users_oper_cb(sr_session_ctx_t *session, const char *module_name, const char *path, const char *request_xpath, uint32_t request_id, struct lyd_node **parent, void *private_data); int np2srv_ch_client_endpt_ssh_cb(sr_session_ctx_t *session, const char *module_name, const char *xpath, uint32_t request_id, sr_event_t event, void *private_data); int np2srv_ch_endpt_ssh_hostkey_cb(sr_session_ctx_t *session, const char *module_name, const char *xpath, uint32_t request_id, sr_event_t event, void *private_data); int np2srv_ch_endpt_ssh_auth_methods_cb(sr_session_ctx_t *session, const char *module_name, const char *xpath, uint32_t request_id, sr_event_t event, void *private_data); #endif /* NP2SRV_NETCONF_SERVER_SSH_H_ */ netopeer2-1.1.70/src/netconf_server_tls.c0000664000000000000000000005542414021433500017044 0ustar rootroot/** * @file netconf_server_ssh.c * @author Michal Vasko <mvasko@cesnet.cz> * @brief ietf-netconf-server SSH callbacks * * Copyright (c) 2019 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 /* asprintf() */ #include <stdint.h> #include <stdlib.h> #include <string.h> #include <assert.h> #include <nc_server.h> #include <libyang/libyang.h> #include <sysrepo.h> #include "common.h" #include "log.h" #include "netconf_server.h" int np2srv_cert_cb(const char *name, void *UNUSED(user_data), char **UNUSED(cert_path), char **cert_data, char **UNUSED(privkey_path), char **privkey_data, NC_SSH_KEY_TYPE *privkey_type) { sr_session_ctx_t *sr_sess; char *xpath; struct lyd_node *data = NULL; int r, rc = -1; r = sr_session_start(np2srv.sr_conn, SR_DS_RUNNING, &sr_sess); if (r != SR_ERR_OK) { return -1; } /* get private key data from sysrepo */ if (asprintf(&xpath, "/ietf-keystore:keystore/asymmetric-keys/asymmetric-key[certificates/certificate/name='%s']", name) == -1) { EMEM; goto cleanup; } r = sr_get_subtree(sr_sess, xpath, 0, &data); free(xpath); if (r != SR_ERR_OK) { goto cleanup; } else if (!data) { ERR("Server certificate \"%s\" not found.", name); goto cleanup; } /* parse private key values */ if (np2srv_sr_get_privkey(data, privkey_data, privkey_type)) { goto cleanup; } /* get cert data from sysrepo */ if (asprintf(&xpath, "/ietf-keystore:keystore/asymmetric-keys/asymmetric-key/certificates/" "certificate[name='%s']/cert", name) == -1) { EMEM; goto cleanup; } lyd_free_withsiblings(data); r = sr_get_subtree(sr_sess, xpath, 0, &data); free(xpath); if (r != SR_ERR_OK) { goto cleanup; } else if (!data) { ERR("Server certificate \"%s\" not found.", name); goto cleanup; } /* set cert data */ *cert_data = strdup(((struct lyd_node_leaf_list *)data)->value_str); if (!*cert_data) { EMEM; goto cleanup; } /* success */ rc = 0; cleanup: lyd_free_withsiblings(data); sr_session_stop(sr_sess); return rc; } int np2srv_cert_list_cb(const char *name, void *UNUSED(user_data), char ***UNUSED(cert_paths), int *UNUSED(cert_path_count), char ***cert_data, int *cert_data_count) { sr_session_ctx_t *sr_sess; char *xpath; struct lyd_node *data = NULL; struct ly_set *set = NULL; int r, rc = -1; uint32_t i, j; r = sr_session_start(np2srv.sr_conn, SR_DS_RUNNING, &sr_sess); if (r != SR_ERR_OK) { return -1; } /* get cert list data from sysrepo */ if (asprintf(&xpath, "/ietf-truststore:truststore/certificates[name='%s']", name) == -1) { EMEM; goto cleanup; } r = sr_get_subtree(sr_sess, xpath, 0, &data); free(xpath); if (r != SR_ERR_OK) { goto cleanup; } else if (!data) { ERR("Certificate list \"%s\" not found.", name); goto cleanup; } /* find all certificates */ set = lyd_find_path(data, "certificate/cert"); if (!set) { /* libyang error printed */ goto cleanup; } else if (!set->number) { WRN("Certificate list \"%s\" does not define any actual certificates."); rc = 0; goto cleanup; } *cert_data = malloc(set->number * sizeof **cert_data); if (!*cert_data) { EMEM; goto cleanup; } /* collect all cert data */ for (i = 0; i < set->number; ++i) { (*cert_data)[i] = strdup(((struct lyd_node_leaf_list *)set->set.d[i])->value_str); if (!(*cert_data)[i]) { EMEM; for (j = 0; j < i - 1; ++j) { free((*cert_data)[i]); } free(*cert_data); goto cleanup; } } *cert_data_count = set->number; /* success */ rc = 0; cleanup: lyd_free_withsiblings(data); ly_set_free(set); sr_session_stop(sr_sess); return rc; } /* /ietf-netconf-server:netconf-server/listen/endpoint/tls */ int np2srv_endpt_tls_cb(sr_session_ctx_t *session, const char *UNUSED(module_name), const char *xpath, sr_event_t UNUSED(event), uint32_t UNUSED(request_id), void *UNUSED(private_data)) { sr_change_iter_t *iter; sr_change_oper_t op; const struct lyd_node *node; const char *prev_val, *prev_list, *endpt_name; bool prev_dflt; int rc; rc = sr_get_changes_iter(session, xpath, &iter); if (rc != SR_ERR_OK) { ERR("Getting changes iter failed (%s).", sr_strerror(rc)); return rc; } while ((rc = sr_get_change_tree_next(session, iter, &op, &node, &prev_val, &prev_list, &prev_dflt)) == SR_ERR_OK) { /* get name */ endpt_name = ((struct lyd_node_leaf_list *)node->parent->child)->value_str; /* ignore other operations */ if (op == SR_OP_CREATED) { rc = nc_server_add_endpt(endpt_name, NC_TI_OPENSSL); } else if (op == SR_OP_DELETED) { rc = nc_server_del_endpt(endpt_name, NC_TI_OPENSSL); } if (rc) { sr_free_change_iter(iter); return SR_ERR_INTERNAL; } } sr_free_change_iter(iter); if (rc != SR_ERR_NOT_FOUND) { ERR("Getting next change failed (%s).", sr_strerror(rc)); return rc; } return SR_ERR_OK; } /* /ietf-netconf-server:netconf-server/listen/endpoint/tls/tls-server-parameters/server-identity/keystore-reference */ int np2srv_endpt_tls_servercert_cb(sr_session_ctx_t *session, const char *UNUSED(module_name), const char *xpath, sr_event_t UNUSED(event), uint32_t UNUSED(request_id), void *UNUSED(private_data)) { sr_change_iter_t *iter; sr_change_oper_t op; const struct lyd_node *node; const char *prev_val, *prev_list, *endpt_name; char *xpath2; bool prev_dflt; int rc; if (asprintf(&xpath2, "%s/*", xpath) == -1) { EMEM; return SR_ERR_NOMEM; } rc = sr_get_changes_iter(session, xpath2, &iter); free(xpath2); if (rc != SR_ERR_OK) { ERR("Getting changes iter failed (%s).", sr_strerror(rc)); return rc; } while ((rc = sr_get_change_tree_next(session, iter, &op, &node, &prev_val, &prev_list, &prev_dflt)) == SR_ERR_OK) { /* find name */ endpt_name = ((struct lyd_node_leaf_list *)node->parent->parent->parent->parent->parent->child)->value_str; /* we do not care about the "asymmetric-key", the certificate is enough */ if (!strcmp(node->schema->name, "certificate")) { if ((op == SR_OP_CREATED) || (op == SR_OP_MODIFIED)) { rc = nc_server_tls_endpt_set_server_cert(endpt_name, ((struct lyd_node_leaf_list *)node)->value_str); } else if (op == SR_OP_DELETED) { if (nc_server_is_endpt(endpt_name)) { rc = nc_server_tls_endpt_set_server_cert(endpt_name, NULL); } } if (rc) { sr_free_change_iter(iter); return SR_ERR_INTERNAL; } } } sr_free_change_iter(iter); if (rc != SR_ERR_NOT_FOUND) { ERR("Getting next change failed (%s).", sr_strerror(rc)); return rc; } return SR_ERR_OK; } /* /ietf-netconf-server:netconf-server/listen/endpoint/tls/tls-server-parameters/client-authentication */ int np2srv_endpt_tls_client_auth_cb(sr_session_ctx_t *session, const char *UNUSED(module_name), const char *xpath, sr_event_t UNUSED(event), uint32_t UNUSED(request_id), void *UNUSED(private_data)) { sr_change_iter_t *iter; sr_change_oper_t op; const struct lyd_node *node; const char *prev_val, *prev_list, *endpt_name; char *xpath2; bool prev_dflt; int rc; if (asprintf(&xpath2, "%s/*", xpath) == -1) { EMEM; return SR_ERR_NOMEM; } rc = sr_get_changes_iter(session, xpath2, &iter); free(xpath2); if (rc != SR_ERR_OK) { ERR("Getting changes iter failed (%s).", sr_strerror(rc)); return rc; } while ((rc = sr_get_change_tree_next(session, iter, &op, &node, &prev_val, &prev_list, &prev_dflt)) == SR_ERR_OK) { /* find name */ endpt_name = ((struct lyd_node_leaf_list *)node->parent->parent->parent->parent->child)->value_str; if (!strcmp(node->schema->name, "optional")) { /* it is always required */ ERR("TLS client authentication is always required."); sr_free_change_iter(iter); return SR_ERR_UNSUPPORTED; } else if (!strcmp(node->schema->name, "ca-certs") || !strcmp(node->schema->name, "client-certs")) { if (op == SR_OP_CREATED) { rc = nc_server_tls_endpt_add_trusted_cert_list(endpt_name, ((struct lyd_node_leaf_list *)node)->value_str); } else if (op == SR_OP_DELETED) { if (nc_server_is_endpt(endpt_name)) { rc = nc_server_tls_endpt_del_trusted_cert_list(endpt_name, ((struct lyd_node_leaf_list *)node)->value_str); } } else if (op == SR_OP_MODIFIED) { nc_server_tls_endpt_del_trusted_cert_list(endpt_name, prev_val); rc = nc_server_tls_endpt_add_trusted_cert_list(endpt_name, ((struct lyd_node_leaf_list *)node)->value_str); } if (rc) { sr_free_change_iter(iter); return SR_ERR_INTERNAL; } } } sr_free_change_iter(iter); if (rc != SR_ERR_NOT_FOUND) { ERR("Getting next change failed (%s).", sr_strerror(rc)); return rc; } return SR_ERR_OK; } static NC_TLS_CTN_MAPTYPE np2srv_tls_ctn_str2map_type(const char *map_type) { NC_TLS_CTN_MAPTYPE ret = 0; if (strncmp(map_type, "ietf-x509-cert-to-name:", 23)) { return ret; } map_type += 23; if (!strcmp(map_type, "specified")) { ret = NC_TLS_CTN_SPECIFIED; } else if (!strcmp(map_type, "san-rfc822-name")) { ret = NC_TLS_CTN_SAN_RFC822_NAME; } else if (!strcmp(map_type, "san-dns-name")) { ret = NC_TLS_CTN_SAN_DNS_NAME; } else if (!strcmp(map_type, "san-ip-address")) { ret = NC_TLS_CTN_SAN_IP_ADDRESS; } else if (!strcmp(map_type, "san-any")) { ret = NC_TLS_CTN_SAN_ANY; } else if (!strcmp(map_type, "common-name")) { ret = NC_TLS_CTN_COMMON_NAME; } return ret; } /* /ietf-netconf-server:netconf-server/listen/endpoint/tls/tls-server-parameters/client-authentication/cert-maps */ int np2srv_endpt_tls_client_ctn_cb(sr_session_ctx_t *session, const char *UNUSED(module_name), const char *xpath, sr_event_t UNUSED(event), uint32_t UNUSED(request_id), void *UNUSED(private_data)) { sr_change_iter_t *iter; sr_change_oper_t op; const struct lyd_node *node, *child; const char *prev_val, *prev_list, *endpt_name, *fingerprint, *name; char *xpath2; bool prev_dflt; int rc; uint32_t id; NC_TLS_CTN_MAPTYPE map_type; if (asprintf(&xpath2, "%s/*", xpath) == -1) { EMEM; return SR_ERR_NOMEM; } rc = sr_get_changes_iter(session, xpath2, &iter); free(xpath2); if (rc != SR_ERR_OK) { ERR("Getting changes iter failed (%s).", sr_strerror(rc)); return rc; } while ((rc = sr_get_change_tree_next(session, iter, &op, &node, &prev_val, &prev_list, &prev_dflt)) == SR_ERR_OK) { /* find name */ endpt_name = ((struct lyd_node_leaf_list *)node->parent->parent->parent->parent->parent->child)->value_str; /* collect all attributes */ id = 0; fingerprint = NULL; map_type = 0; name = NULL; LY_TREE_FOR(node->child, child) { if (!strcmp(child->schema->name, "id")) { id = ((struct lyd_node_leaf_list *)child)->value.uint32; } else if (!strcmp(child->schema->name, "fingerprint")) { fingerprint = ((struct lyd_node_leaf_list *)child)->value_str; } else if (!strcmp(child->schema->name, "map-type")) { map_type = np2srv_tls_ctn_str2map_type(((struct lyd_node_leaf_list *)child)->value_str); } else if (!strcmp(child->schema->name, "name")) { name = ((struct lyd_node_leaf_list *)child)->value_str; } } /* it was validated */ assert(fingerprint && map_type); if (op == SR_OP_CREATED) { rc = nc_server_tls_endpt_add_ctn(endpt_name, id, fingerprint, map_type, name); } else if (op == SR_OP_DELETED) { if (nc_server_is_endpt(endpt_name)) { rc = nc_server_tls_endpt_del_ctn(endpt_name, id, fingerprint, map_type, name); } } else if (op == SR_OP_MODIFIED) { nc_server_tls_endpt_del_ctn(endpt_name, id, NULL, 0, NULL); rc = nc_server_tls_endpt_add_ctn(endpt_name, id, fingerprint, map_type, name); } if (rc) { sr_free_change_iter(iter); return SR_ERR_INTERNAL; } } sr_free_change_iter(iter); if (rc != SR_ERR_NOT_FOUND) { ERR("Getting next change failed (%s).", sr_strerror(rc)); return rc; } return SR_ERR_OK; } /* /ietf-netconf-server:netconf-server/call-home/netconf-client/endpoints/endpoint/tls */ int np2srv_ch_client_endpt_tls_cb(sr_session_ctx_t *session, const char *UNUSED(module_name), const char *xpath, sr_event_t UNUSED(event), uint32_t UNUSED(request_id), void *UNUSED(private_data)) { sr_change_iter_t *iter; sr_change_oper_t op; const struct lyd_node *node; const char *prev_val, *prev_list, *endpt_name, *client_name; bool prev_dflt; int rc; rc = sr_get_changes_iter(session, xpath, &iter); if (rc != SR_ERR_OK) { ERR("Getting changes iter failed (%s).", sr_strerror(rc)); return rc; } while ((rc = sr_get_change_tree_next(session, iter, &op, &node, &prev_val, &prev_list, &prev_dflt)) == SR_ERR_OK) { /* get names */ endpt_name = ((struct lyd_node_leaf_list *)node->parent->child)->value_str; client_name = ((struct lyd_node_leaf_list *)node->parent->parent->parent->child)->value_str; /* ignore other operations */ if (op == SR_OP_CREATED) { rc = nc_server_ch_client_add_endpt(client_name, endpt_name, NC_TI_OPENSSL); } else if (op == SR_OP_DELETED) { if (nc_server_ch_is_client(client_name)) { rc = nc_server_ch_client_del_endpt(client_name, endpt_name, NC_TI_OPENSSL); } } if (rc) { sr_free_change_iter(iter); return SR_ERR_INTERNAL; } } sr_free_change_iter(iter); if (rc != SR_ERR_NOT_FOUND) { ERR("Getting next change failed (%s).", sr_strerror(rc)); return rc; } return SR_ERR_OK; } /* /ietf-netconf-server:netconf-server/call-home/netconf-client/endpoints/endpoint/tls/tls-server-parameters/" * server-identity/keystore-reference */ int np2srv_ch_client_endpt_tls_servercert_cb(sr_session_ctx_t *session, const char *UNUSED(module_name), const char *xpath, sr_event_t UNUSED(event), uint32_t UNUSED(request_id), void *UNUSED(private_data)) { sr_change_iter_t *iter; sr_change_oper_t op; const struct lyd_node *node; const char *prev_val, *prev_list, *endpt_name, *client_name; char *xpath2; bool prev_dflt; int rc; if (asprintf(&xpath2, "%s/*", xpath) == -1) { EMEM; return SR_ERR_NOMEM; } rc = sr_get_changes_iter(session, xpath2, &iter); free(xpath2); if (rc != SR_ERR_OK) { ERR("Getting changes iter failed (%s).", sr_strerror(rc)); return rc; } while ((rc = sr_get_change_tree_next(session, iter, &op, &node, &prev_val, &prev_list, &prev_dflt)) == SR_ERR_OK) { /* get names */ endpt_name = ((struct lyd_node_leaf_list *)node->parent->parent->parent->parent->parent->child)->value_str; client_name = ((struct lyd_node_leaf_list *)node->parent->parent->parent->parent->parent->parent->parent->child)->value_str; /* we do not care about the "asymmetric-key", the certificate is enough */ if (!strcmp(node->schema->name, "certificate")) { if ((op == SR_OP_CREATED) || (op == SR_OP_MODIFIED)) { rc = nc_server_tls_ch_client_endpt_set_server_cert(client_name, endpt_name, ((struct lyd_node_leaf_list *)node)->value_str); } else if (op == SR_OP_DELETED) { if (nc_server_ch_client_is_endpt(client_name, endpt_name)) { rc = nc_server_tls_ch_client_endpt_set_server_cert(client_name, endpt_name, NULL); } } if (rc) { sr_free_change_iter(iter); return SR_ERR_INTERNAL; } } } sr_free_change_iter(iter); if (rc != SR_ERR_NOT_FOUND) { ERR("Getting next change failed (%s).", sr_strerror(rc)); return rc; } return SR_ERR_OK; } /* /ietf-netconf-server:netconf-server/call-home/netconf-client/endpoints/endpoint/tls/tls-server-parameters/" * client-authentication */ int np2srv_ch_client_endpt_tls_client_auth_cb(sr_session_ctx_t *session, const char *UNUSED(module_name), const char *xpath, sr_event_t UNUSED(event), uint32_t UNUSED(request_id), void *UNUSED(private_data)) { sr_change_iter_t *iter; sr_change_oper_t op; const struct lyd_node *node; const char *prev_val, *prev_list, *endpt_name, *client_name; char *xpath2; bool prev_dflt; int rc; if (asprintf(&xpath2, "%s/*", xpath) == -1) { EMEM; return SR_ERR_NOMEM; } rc = sr_get_changes_iter(session, xpath2, &iter); free(xpath2); if (rc != SR_ERR_OK) { ERR("Getting changes iter failed (%s).", sr_strerror(rc)); return rc; } while ((rc = sr_get_change_tree_next(session, iter, &op, &node, &prev_val, &prev_list, &prev_dflt)) == SR_ERR_OK) { /* get names */ endpt_name = ((struct lyd_node_leaf_list *)node->parent->parent->parent->parent->child)->value_str; client_name = ((struct lyd_node_leaf_list *)node->parent->parent->parent->parent->parent->parent->child)->value_str; if (!strcmp(node->schema->name, "optional")) { /* it is always required */ ERR("TLS client authentication is always required."); sr_free_change_iter(iter); return SR_ERR_UNSUPPORTED; } else if (!strcmp(node->schema->name, "ca-certs") || !strcmp(node->schema->name, "client-certs")) { if (op == SR_OP_CREATED) { rc = nc_server_tls_ch_client_endpt_add_trusted_cert_list(client_name, endpt_name, ((struct lyd_node_leaf_list *)node)->value_str); } else if (op == SR_OP_DELETED) { if (nc_server_ch_client_is_endpt(client_name, endpt_name)) { rc = nc_server_tls_ch_client_endpt_del_trusted_cert_list(client_name, endpt_name, ((struct lyd_node_leaf_list *)node)->value_str); } } else if (op == SR_OP_MODIFIED) { nc_server_tls_ch_client_endpt_del_trusted_cert_list(client_name, endpt_name, prev_val); rc = nc_server_tls_ch_client_endpt_add_trusted_cert_list(client_name, endpt_name, ((struct lyd_node_leaf_list *)node)->value_str); } if (rc) { sr_free_change_iter(iter); return SR_ERR_INTERNAL; } } } sr_free_change_iter(iter); if (rc != SR_ERR_NOT_FOUND) { ERR("Getting next change failed (%s).", sr_strerror(rc)); return rc; } return SR_ERR_OK; } /* /ietf-netconf-server:netconf-server/call-home/netconf-client/endpoints/endpoint/tls/tls-server-parameters/" * client-authentication/cert-maps */ int np2srv_ch_client_endpt_tls_client_ctn_cb(sr_session_ctx_t *session, const char *UNUSED(module_name), const char *xpath, sr_event_t UNUSED(event), uint32_t UNUSED(request_id), void *UNUSED(private_data)) { sr_change_iter_t *iter; sr_change_oper_t op; const struct lyd_node *node, *child; const char *prev_val, *prev_list, *endpt_name, *client_name, *fingerprint, *name; char *xpath2; bool prev_dflt; int rc; uint32_t id; NC_TLS_CTN_MAPTYPE map_type; if (asprintf(&xpath2, "%s/*", xpath) == -1) { EMEM; return SR_ERR_NOMEM; } rc = sr_get_changes_iter(session, xpath2, &iter); free(xpath2); if (rc != SR_ERR_OK) { ERR("Getting changes iter failed (%s).", sr_strerror(rc)); return rc; } while ((rc = sr_get_change_tree_next(session, iter, &op, &node, &prev_val, &prev_list, &prev_dflt)) == SR_ERR_OK) { /* get names */ endpt_name = ((struct lyd_node_leaf_list *)node->parent->parent->parent->parent->parent->child)->value_str; client_name = ((struct lyd_node_leaf_list *)node->parent->parent->parent->parent->parent->parent->parent->child)->value_str; /* collect all attributes */ id = 0; fingerprint = NULL; map_type = 0; name = NULL; LY_TREE_FOR(node->child, child) { if (!strcmp(child->schema->name, "id")) { id = ((struct lyd_node_leaf_list *)child)->value.uint32; } else if (!strcmp(child->schema->name, "fingerprint")) { fingerprint = ((struct lyd_node_leaf_list *)child)->value_str; } else if (!strcmp(child->schema->name, "map-type")) { map_type = np2srv_tls_ctn_str2map_type(((struct lyd_node_leaf_list *)child)->value_str); } else if (!strcmp(child->schema->name, "name")) { name = ((struct lyd_node_leaf_list *)child)->value_str; } } /* it was validated */ assert(fingerprint && map_type); if (op == SR_OP_CREATED) { rc = nc_server_tls_ch_client_endpt_add_ctn(client_name, endpt_name, id, fingerprint, map_type, name); } else if (op == SR_OP_DELETED) { if (nc_server_ch_client_is_endpt(client_name, endpt_name)) { rc = nc_server_tls_ch_client_endpt_del_ctn(client_name, endpt_name, id, fingerprint, map_type, name); } } else if (op == SR_OP_MODIFIED) { nc_server_tls_ch_client_endpt_del_ctn(client_name, endpt_name, id, NULL, 0, NULL); rc = nc_server_tls_ch_client_endpt_add_ctn(client_name, endpt_name, id, fingerprint, map_type, name); } if (rc) { sr_free_change_iter(iter); return SR_ERR_INTERNAL; } } sr_free_change_iter(iter); if (rc != SR_ERR_NOT_FOUND) { ERR("Getting next change failed (%s).", sr_strerror(rc)); return rc; } return SR_ERR_OK; } netopeer2-1.1.70/src/netconf_server_tls.h0000664000000000000000000000440014021433500017035 0ustar rootroot/** * @file netconf_server_tks.h * @author Michal Vasko <mvasko@cesnet.cz> * @brief ietf-netconf-server TLS callbacks header * * Copyright (c) 2019 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 NP2SRV_NETCONF_SERVER_TLS_H_ #define NP2SRV_NETCONF_SERVER_TLS_H_ #include <sysrepo.h> int np2srv_cert_cb(const char *name, void *user_data, char **cert_path, char **cert_data, char **privkey_path, char **privkey_data, NC_SSH_KEY_TYPE *privkey_type); int np2srv_cert_list_cb(const char *name, void *user_data, char ***cert_paths, int *cert_path_count, char ***cert_data, int *cert_data_count); int np2srv_endpt_tls_cb(sr_session_ctx_t *session, const char *module_name, const char *xpath, sr_event_t event, uint32_t request_id, void *private_data); int np2srv_endpt_tls_servercert_cb(sr_session_ctx_t *session, const char *module_name, const char *xpath, sr_event_t event, uint32_t request_id, void *private_data); int np2srv_endpt_tls_client_auth_cb(sr_session_ctx_t *session, const char *module_name, const char *xpath, sr_event_t event, uint32_t request_id, void *private_data); int np2srv_endpt_tls_client_ctn_cb(sr_session_ctx_t *session, const char *module_name, const char *xpath, sr_event_t event, uint32_t request_id, void *private_data); int np2srv_ch_client_endpt_tls_cb(sr_session_ctx_t *session, const char *module_name, const char *xpath, sr_event_t event, uint32_t request_id, void *private_data); int np2srv_ch_client_endpt_tls_servercert_cb(sr_session_ctx_t *session, const char *module_name, const char *xpath, sr_event_t event, uint32_t request_id, void *private_data); int np2srv_ch_client_endpt_tls_client_auth_cb(sr_session_ctx_t *session, const char *module_name, const char *xpath, sr_event_t event, uint32_t request_id, void *private_data); int np2srv_ch_client_endpt_tls_client_ctn_cb(sr_session_ctx_t *session, const char *module_name, const char *xpath, sr_event_t event, uint32_t request_id, void *private_data); #endif /* NP2SRV_NETCONF_SERVER_TLS_H_ */ 07070100000003000081A40000000000000000000000016333FE8500004829000000000000000000000000000000000000002C00000000netopeer2-1664351877.ff86482/netopeer2.specName: netopeer2 Version: 1.1.70 Release: 1.2 Summary: NETCONF tools Url: https://github.com/cesnet/netopeer2 Source: https://github.com/cesnet/netopeer2/archive/netopeer2-1.1.70.tar.gz License: BSD-3-Clause BuildRoot: %{_tmppath}/%{name}-%{version}-%{release} %if 0%{?suse_version} Requires: libssh4 >= 0.7.1 %else Requires: libssh >= 0.7.1 %endif Requires: libyang1 Requires: sysrepo >= 1.4.122 Requires: libnetconf2 >= 1.1.43 Requires: openssl BuildRequires: cmake BuildRequires: gcc BuildRequires: libssh-devel >= 0.7.1 BuildRequires: libyang-devel >= 1.0 BuildRequires: sysrepo-devel >= 1.4.122 BuildRequires: libnetconf2-devel >= 1.1.43 BuildRequires: openssl-devel %if 0%{?suse_version} BuildRequires: timezone %endif %description NETCONF tools suite including a server and command-line client %prep %setup -n netopeer2-1.1.70 mkdir build %build cd build cmake -DCMAKE_INSTALL_PREFIX=/usr \ -DCMAKE_BUILD_TYPE="Package" \ -DINSTALL_MODULES=OFF \ -DGENERATE_HOSTKEY=OFF \ -DMERGE_LISTEN_CONFIG=OFF .. make %install cd build make DESTDIR=%{buildroot} install %post #!/bin/bash groupadd -f netconf NP2_MODULE_DIR=/usr/share/yang/modules/netopeer2 NP2_MODULE_PERMS=660 NP2_MODULE_OWNER=root NP2_MODULE_GROUP=netconf #!/usr/bin/env bash # env variables NP2_MODULE_DIR, NP2_MODULE_PERMS must be defined and NP2_MODULE_OWNER, NP2_MODULE_GROUP will be used if # defined when executing this script! if [ -z "$NP2_MODULE_DIR" -o -z "$NP2_MODULE_PERMS" ]; then echo "Required environment variables not defined!" exit 1 fi # optional env variable override if [ -n "$SYSREPOCTL_EXECUTABLE" ]; then SYSREPOCTL="$SYSREPOCTL_EXECUTABLE" # avoid problems with sudo PATH elif [ `id -u` -eq 0 ]; then SYSREPOCTL=`su -c 'which sysrepoctl' -l $USER` else SYSREPOCTL=`which sysrepoctl` fi MODDIR=${DESTDIR}${NP2_MODULE_DIR} PERMS=${NP2_MODULE_PERMS} OWNER=${NP2_MODULE_OWNER} GROUP=${NP2_MODULE_GROUP} # array of modules to install MODULES=( "ietf-netconf-acm@2018-02-14.yang" "ietf-netconf@2013-09-29.yang -e writable-running -e candidate -e rollback-on-error -e validate -e startup -e url -e xpath" "ietf-netconf-monitoring@2010-10-04.yang" "ietf-netconf-nmda@2019-01-07.yang -e origin -e with-defaults" "nc-notifications@2008-07-14.yang" "notifications@2008-07-14.yang" "ietf-x509-cert-to-name@2014-12-10.yang" "ietf-crypto-types@2019-07-02.yang" "ietf-keystore@2019-07-02.yang -e keystore-supported" "ietf-truststore@2019-07-02.yang -e truststore-supported -e x509-certificates" "ietf-tcp-common@2019-07-02.yang -e keepalives-supported" "ietf-ssh-server@2019-07-02.yang -e local-client-auth-supported" "ietf-tls-server@2019-07-02.yang -e local-client-auth-supported" "ietf-netconf-server@2019-07-02.yang -e ssh-listen -e tls-listen -e ssh-call-home -e tls-call-home" ) # functions INSTALL_MODULE() { CMD="'$SYSREPOCTL' -a -i $MODDIR/$1 -s '$MODDIR' -p '$PERMS' -v2" if [ ! -z ${OWNER} ]; then CMD="$CMD -o '$OWNER'" fi if [ ! -z ${GROUP} ]; then CMD="$CMD -g '$GROUP'" fi eval $CMD local rc=$? if [ $rc -ne 0 ]; then exit $rc fi } UPDATE_MODULE() { CMD="'$SYSREPOCTL' -a -U $MODDIR/$1 -s '$MODDIR' -p '$PERMS' -v2" if [ ! -z ${OWNER} ]; then CMD="$CMD -o '$OWNER'" fi if [ ! -z ${GROUP} ]; then CMD="$CMD -g '$GROUP'" fi eval $CMD local rc=$? if [ $rc -ne 0 ]; then exit $rc fi } ENABLE_FEATURE() { "$SYSREPOCTL" -a -c $1 -e $2 -v2 local rc=$? if [ $rc -ne 0 ]; then exit $rc fi } # get current modules SCTL_MODULES=`$SYSREPOCTL -l` for i in "${MODULES[@]}"; do name=`echo "$i" | sed 's/\([^@]*\).*/\1/'` SCTL_MODULE=`echo "$SCTL_MODULES" | grep "^$name \+|[^|]*| I"` if [ -z "$SCTL_MODULE" ]; then # install module with all its features INSTALL_MODULE "$i" continue fi sctl_revision=`echo "$SCTL_MODULE" | sed 's/[^|]*| \([^ ]*\).*/\1/'` revision=`echo "$i" | sed 's/[^@]*@\([^\.]*\).*/\1/'` if [ "$sctl_revision" \< "$revision" ]; then # update module without any features file=`echo "$i" | cut -d' ' -f 1` UPDATE_MODULE "$file" fi # parse sysrepoctl features and add extra space at the end for easier matching sctl_features="`echo "$SCTL_MODULE" | sed 's/\([^|]*|\)\{6\}\(.*\)/\2/'` " # parse features we want to enable features=`echo "$i" | sed 's/[^ ]* \(.*\)/\1/'` while [ "${features:0:3}" = "-e " ]; do # skip "-e " features=${features:3} # parse feature feature=`echo "$features" | sed 's/\([^[:space:]]*\).*/\1/'` # enable feature if not already sctl_feature=`echo "$sctl_features" | grep " ${feature} "` if [ -z "$sctl_feature" ]; then # enable feature ENABLE_FEATURE $name $feature fi # next iteration, skip this feature features=`echo "$features" | sed 's/[^[:space:]]* \(.*\)/\1/'` done done #!/usr/bin/env bash set -e # optional env variable override if [ -n "$SYSREPOCFG_EXECUTABLE" ]; then SYSREPOCFG="$SYSREPOCFG_EXECUTABLE" # avoid problems with sudo PATH elif [ `id -u` -eq 0 ]; then SYSREPOCFG=`su -c 'which sysrepocfg' -l $USER` else SYSREPOCFG=`which sysrepocfg` fi # avoid problems with sudo PATH if [ `id -u` -eq 0 ]; then OPENSSL=`su -c 'which openssl' -l $USER` else OPENSSL=`which openssl` fi # check that there is no SSH key with this name yet KEYSTORE_KEY=`$SYSREPOCFG -X -x "/ietf-keystore:keystore/asymmetric-keys/asymmetric-key[name='genkey']/name"` if [ -z "$KEYSTORE_KEY" ]; then # generate a new key PRIVPEM=`$OPENSSL genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:2048 -outform PEM 2>/dev/null` # remove header/footer PRIVKEY=`grep -v -- "-----" - <<STDIN $PRIVPEM STDIN` # get public key PUBPEM=`$OPENSSL rsa -pubout 2>/dev/null <<STDIN $PRIVPEM STDIN` # remove header/footer PUBKEY=`grep -v -- "-----" - <<STDIN $PUBPEM STDIN` # generate edit config CONFIG="<keystore xmlns=\"urn:ietf:params:xml:ns:yang:ietf-keystore\"> <asymmetric-keys> <asymmetric-key> <name>genkey</name> <algorithm>rsa2048</algorithm> <public-key>$PUBKEY</public-key> <private-key>$PRIVKEY</private-key> </asymmetric-key> </asymmetric-keys> </keystore>" TMPFILE=`mktemp -u` printf -- "$CONFIG" > $TMPFILE # apply it to startup and running $SYSREPOCFG --edit=$TMPFILE -d startup -f xml -m ietf-keystore -v2 $SYSREPOCFG -C startup -m ietf-keystore -v2 # remove the tmp file rm $TMPFILE fi #!/usr/bin/env bash set -e # optional env variable override if [ -n "$SYSREPOCFG_EXECUTABLE" ]; then SYSREPOCFG="$SYSREPOCFG_EXECUTABLE" # avoid problems with sudo PATH elif [ `id -u` -eq 0 ]; then SYSREPOCFG=`su -c 'which sysrepocfg' -l $USER` else SYSREPOCFG=`which sysrepocfg` fi KS_KEY_NAME=genkey # check that there is no listen/Call Home configuration yet SERVER_CONFIG=`$SYSREPOCFG -X -x "/ietf-netconf-server:netconf-server/listen/endpoint[1]/name | /ietf-netconf-server:netconf-server/call-home/netconf-client[1]/name"` if [ -z "$SERVER_CONFIG" ]; then # import default config CONFIG="<netconf-server xmlns=\"urn:ietf:params:xml:ns:yang:ietf-netconf-server\"> <listen> <endpoint> <name>default-ssh</name> <ssh> <tcp-server-parameters> <local-address>0.0.0.0</local-address> <keepalives> <idle-time>1</idle-time> <max-probes>10</max-probes> <probe-interval>5</probe-interval> </keepalives> </tcp-server-parameters> <ssh-server-parameters> <server-identity> <host-key> <name>default-key</name> <public-key> <keystore-reference>$KS_KEY_NAME</keystore-reference> </public-key> </host-key> </server-identity> <client-authentication> <supported-authentication-methods> <publickey/> <passsword/> <other>interactive</other> </supported-authentication-methods> <users/> </client-authentication> </ssh-server-parameters> </ssh> </endpoint> </listen> </netconf-server>" TMPFILE=`mktemp -u` printf -- "$CONFIG" > $TMPFILE # apply it to startup and running $SYSREPOCFG --edit=$TMPFILE -d startup -f xml -m ietf-netconf-server -v2 $SYSREPOCFG -C startup -m ietf-netconf-server -v2 # remove the tmp file rm $TMPFILE fi %files %defattr(-,root,root) %{_bindir}/netopeer2-server %{_datadir}/yang %{_bindir}/netopeer2-cli %{_mandir}/man1 %changelog * Mon Mar 08 2021 Michal Vasko <mvasko@cesnet.cz> 1.1.70 - common BUGFIX set error to event session (Michal Vasko) * Mon Mar 08 2021 Michal Vasko <mvasko@cesnet.cz> 1.1.69 - common BUGFIX set error to event session (Michal Vasko) - netconf server BUGFIX set periodic connection parameters (Michal Vasko) - nacm BUGFIX partial access overwriting default access (Michal Vasko) - netconf BUGFIX discard non-applied changes (Michal Vasko) - netconf BUGFIX do not unsubscribe on subsc exists error (Michal Vasko) - netconf BUGFIX all RPC callbacks ignore abort event (Michal Vasko) - netconf BUGFIX always use user SR sessions in RPC callbacks (Michal Vasko) - netconf BUGFIX forbid more subscriptions on a session (Michal Vasko) - netconf BUGFIX use global sessions for locking (Michal Vasko) - netconf BUGFIX Clb func ignore the ABORT event (aPiecek) - main BUGFIX use event session ID (Michal Vasko) - common BUGFIX content match node handling (Michal Vasko) - netconf BUGFIX server crash protection (aPiecek) - nacm BUGFIX differentiate explicit and implicit permit (Michal Vasko) - netconf BUGFIX apply selection filters for get (Michal Vasko) - netconf BUGFIX store username locally (Michal Vasko) - build BUGFIX remove obsolete cmake option (Michal Vasko) - netconf BUGFIX graceful handling of NULL username (Michal Vasko) - common BUGFIX subtree filter selection and content filters (Michal Vasko) - common BUGFIX memory leak (Michal Vasko) - packages CHANGE update required sysrepo version (Michal Vasko) - packages CHANGE increase dependency versions (Michal Vasko) * Tue Jan 26 2021 Michal Vasko <mvasko@cesnet.cz> 1.1.53 - nacm BUGFIX proper read access filtering (Michal Vasko) - build FEATURE options for specifying custom paths of authorized_keys (#812) (Jan Kundrát) - log BUGFIX shorten unknown priority flag (#806) (gwendlan) - netconf BUGFIX avoid invalid argument error (Michal Vasko) - netconf BUGFIX uninitialized variable (Michal Vasko) - main BUGFIX configure timeout in seconds as intended (Michal Vasko) - build FEATURE increase required cmake version (Michal Vasko) - cli BUGFIX start notification thread before subscribing (Michal Vasko) - netconf BUGFIX get data filtering (Michal Vasko) - main BUGFIX handle both grouping and augment with-defaults (Michal Vasko) - main FEATURE custom PID file path (#764) (Václav Kubernát) - nacm BUGFIX odr violation (#765) (Václav Kubernát) - nacm BUGFIX proper path matching (Michal Vasko) - server ssh BUGFIX missing param (Michal Vasko) - server ssh BUGFIX missing param (Michal Vasko) - cmake BUGFIX <pkg>_FOUND variable names (#731) (Robin Jarry) - log BUGFIX encode % only when using the string as format (Michal Vasko) - netconf monitoring BUGFIX creation of locks container missing (Michal Vasko) - build BUGFIX non-standard paths of lnc2 lib/headers (Michal Vasko) - build BUGFIX do not require openssl and libssh (Michal Vasko) - server ssh FEATURE ignore auth key file access error (#708) (Václav Kubernát) - build FEATURE add env var sysrepo tools override (Václav Kubernát) - main BUGFIX correct help string for an optional argument (Michal Vasko) - scripts FEATURE support using current user for SR modules (Michal Vasko) - server ssh BUGFIX missing param (Michal Vasko) - netopeer2 FEATURE support for any transport combination (Michal Vasko) - main BUGFIX standard NC error tags lock-denied, in-use and data-exists (#657) (jkmuller) * Tue Jul 21 2020 Radek Krejci <rkrejci@cesnet.cz> 1.1.39 - netconf acm BUGFIX notification and rpc name matching (Michal Vasko) - netconf BUGFIX copy-config invalid-value error (Michal Vasko) - netconf BUGFIX proper lock error tag (Michal Vasko) - netconf BUGFIX set nc session notif flag sooner (Michal Vasko) - main BUGFIX lnc2 and libssh logging level fixed (Michal Vasko) - scripts CHANGE use su only for effective root (Michal Vasko) - scripts BUGFIX issues with shell variables containing spaces (#646) (Andrei Pavel) - build BUGFIX take DESTDIR into account (#649) (Heiko Thiery) * Tue Jun 02 2020 Michal Vasko <mvasko@cesnet.cz> 1.1.34 - build BUGFIX vasprintf detection on BSD (Michal Vasko) - netconf BUGFIX forward get_data errors to client (#628) (#631) (Robin Jarry) - server CHANGE continue applying changes on error (#633) (Robin Jarry) - build CHANGE rename timeout so that it is accurate (Michal Vasko) - netconf FEATURE use data change timeout for sr_get_data (#632) (Robin Jarry) - build BUGFIX revert changes supported only in cmake 3.1 and newer (Michal Vasko) - cmake BUGFIX ssh_threads linking (Michal Vasko) - build BUGFIX set PIC for compat (Michal Vasko) - main FEATURE improve op exec NACM fail message (Michal Vasko) - cli BUGFIX define variables only in sources (Michal Vasko) - build FEATURE support for latest libssh (Michal Vasko) - log BUGFIX header variables must be extern (Michal Vasko) - nacm BUGFIX all groups match (Michal Vasko) - readme CHANGE include links to the packages (Michal Vasko) - packages FEATURE initial working script commit (Michal Vasko) - packages FEATURE initial working script commit (Michal Vasko) - build FEATURE allow setting specific module permissions (Michal Vasko) * Tue May 12 2020 Michal Vasko <mvasko@cesnet.cz> 1.1.30 - build FEATURE allow setting specific module permissions (Michal Vasko) - netconf_nmda BUGFIX get username based on nc session (Michal Vasko) - build CHANGE do not generate scripts (Michal Vasko) - build CHANGE do not use compile-time tool paths (Michal Vasko) - build BUGFIX specify supported target flags (Michal Vasko) - fixup! build BUGFIX openssl must be found for libnetconf2 checks (Michal Vasko) - build BUGFIX openssl must be found for libnetconf2 checks (Michal Vasko) - build CHANGE do not exit whole script if nothing to do (Michal Vasko) * Mon May 11 2020 Michal Vasko <mvasko@cesnet.cz> 1.1.29 - nacm BUGFIX match descendant nodes too (Michal Vasko) - build CHANGE install YANG modules into system (Michal Vasko) - nacm BUGFIX match only exact paths (Michal Vasko) - nacm BUGFIX match nested nodes too (Michal Vasko) - build BUGFIX put all binaries into one directory (Michal Vasko) - build CHANGE find libyang module c++ bindings (Michal Vasko) - netconf CHANGE reflect latest sysrepo API changes (Michal Vasko) - server BUGFIX check log level for messages from all sources (Michal Vasko) - server BUGFIX only load any <config> data (Michal Vasko) - server BUGFIX script return (Michal Vasko) - server CHANGE merge configuration only if there is none (Michal Vasko) - server CHANGE enable setting modules group from cmake (Michal Vasko) - server BUGFIX fix partial processing of tcp params changes (#606) (Robin Jarry) - server CHANGE make installing modules much more robust (Michal Vasko) - build CHANGE make sure compat.h is always generated (Michal Vasko) - build CHANGE do not call use_compat() twice (Michal Vasko) - cmake BUGFIX no dot wrap long strings (Michal Vasko) - server FEATURE customizable poll timeout (Michal Vasko) - cmake BUGFIX duplicate definition (Michal Vasko) - compat CHANGE added more functions (#600) (apropp-molex) - compat BUGFIX avoid macro collision (Michal Vasko) - build CHANGE use compat library (Michal Vasko) - server CHANGE do not require libcrypt as it is not used (Michal Vasko) - server CHANGE cover new libnetconf2 log level (Michal Vasko) - server BUGFIX propagate custom copy-config and commit errors (Michal Vasko) - server BUGFIX handle applying emtpy config correctly (Michal Vasko) - server BUGFIX manually remove all callhome clients (Michal Vasko) - server CHANGE set proper required cmake variables (Michal Vasko) - cli CHANGE use . instead of , as floating point (Michal Vasko) - server CHANGE no need to install ietf-datastores (Michal Vasko) - server CHANGE subscribe for all used data (Michal Vasko) - server BUGFIX comment redundant notification container (Michal Vasko) - server BUGFIX sending nested notifications (Michal Vasko) - server CHANGE support for more authorized keys in file (Michal Vasko) - server CHANGE set owner for all the server modules (Michal Vasko) - build CHANGE new compile time variable for RPC timeout (K.Sriram) - server CHANGE use small timeout for polling (Michal Vasko) - server BUGFIX use correct wd mode in reply (Michal Vasko) - server BUGFIX do not collect wd mode (Michal Vasko) - server CHANGE skip NACM check for copy from running to startup (Michal Vasko) - cli CHANGE include a sample script (Michal Vasko) - server BUGFIX use seconds for data timeout as intended (Michal Vasko) - server BUGFIX use proper username for NACM check (Michal Vasko) - server BUGFIX leaked session (Michal Vasko) - server CHANGE improve NACM denied error (Michal Vasko) - server CHANGE remove ietf-yang-library oper callback (Michal Vasko) - server CHANGE compile options for sysrepo timeout and wait (Michal Vasko) - server CHANGE adjust based on latest sysrepo API changes (Michal Vasko) - server CHANGE adjust for latest sysrepo API changes (Michal Vasko) - server BUGFIX do not modify deleted endpoints (Michal Vasko) - server CHANGE configurable edit-config timeout (Michal Vasko) - server BUGFIX double free (Michal Vasko) - cli BUGFIX endless argument processing loop (Michal Vasko) - cli CHANGE timeout param for all commands (Michal Vasko) - server BUGFIX nmda params handling (Michal Vasko) - cli CHANGE native support for NMDA RPCs (Michal Vasko) - cli BUGFIX create subscription thread only if not running (Michal Vasko) 07070100000004000081A40000000000000000000000016333FE85000000C2000000000000000000000000000000000000002C00000000netopeer2-1664351877.ff86482/netopeer2.yamlname: netopeer2 description: NETCONF tools suite including a server and command-line client upstream: https://github.com/cesnet/netopeer2 branches: - name: master type: protected type: public 07070100000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000B00000000TRAILER!!!2021 blocks
Locations
Projects
Search
Status Monitor
Help
Open Build Service
OBS Manuals
API Documentation
OBS Portal
Reporting a Bug
Contact
Mailing List
Forums
Chat (IRC)
Twitter
Open Build Service (OBS)
is an
openSUSE project
.
浙ICP备2022010568号-2