1
0
Fork 0

Adding upstream version 2.0.24.

Signed-off-by: Daniel Baumann <daniel@debian.org>
This commit is contained in:
Daniel Baumann 2025-02-18 11:18:17 +01:00
parent e508fcfeb9
commit afb0a8fea7
Signed by: daniel
GPG key ID: FBB4F0E80A80222F
118 changed files with 45084 additions and 0 deletions

69
src/config.h.in Normal file
View file

@ -0,0 +1,69 @@
/**
* \file config.h
* \author Radek Krejci <rkrejci@cesnet.cz>
* \brief libnetconf2 various configuration settings.
*
* Copyright (c) 2015 - 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
*/
#ifndef NC_CONFIG_H_
#define NC_CONFIG_H_
/*
* Mark all objects as hidden and export only objects explicitly marked to be part of the public API or
* those marked as mock objects for testing purpose
*/
#define API __attribute__((visibility("default")))
#define MOCK __attribute__((visibility("default")))
/*
* Support for getpeereid
*/
#cmakedefine HAVE_GETPEEREID
/*
* Support for shadow file manipulation
*/
#cmakedefine HAVE_SHADOW
/*
* Support for crypt.h
*/
#cmakedefine HAVE_CRYPT
/*
* Location of installed basic YANG modules on the system
*/
#define NC_YANG_DIR "@YANG_MODULE_DIR@"
/*
* Inactive read timeout
*/
#define NC_READ_INACT_TIMEOUT @READ_INACTIVE_TIMEOUT@
/*
* Active read timeout in seconds
* (also used for internal <get-schema> RPC reply timeout)
*/
#define NC_READ_ACT_TIMEOUT @READ_ACTIVE_TIMEOUT@
/*
* pspoll structure queue size (also found in nc_server.h)
*/
#define NC_PS_QUEUE_SIZE @MAX_PSPOLL_THREAD_COUNT@
/* Microseconds after which tasks are repeated until the full timeout elapses.
* A millisecond (1000) should be divisible by this number without remain.
*/
#define NC_TIMEOUT_STEP @TIMEOUT_STEP@
/* Portability feature-check macros. */
#cmakedefine HAVE_PTHREAD_RWLOCKATTR_SETKIND_NP
#endif /* NC_CONFIG_H_ */

1212
src/io.c Normal file

File diff suppressed because it is too large Load diff

669
src/libnetconf.h Normal file
View file

@ -0,0 +1,669 @@
/**
* @file libnetconf.h
* @author Radek Krejci <rkrejci@cesnet.cz>
* @author Michal Vasko <mvasko@cesnet.cz>
* @brief libnetconf2 main internal header.
*
* Copyright (c) 2015 - 2021 CESNET, z.s.p.o.
*
* This source code is licensed under BSD 3-Clause License (the "License").
* You may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://opensource.org/licenses/BSD-3-Clause
*/
#ifndef NC_LIBNETCONF_H_
#define NC_LIBNETCONF_H_
#include "config.h"
#include "log_p.h"
#include "messages_p.h"
#include "netconf.h"
#include "session_p.h"
/* Tests whether string is empty or non-empty. */
#define strisempty(str) ((str)[0] == '\0')
#define strnonempty(str) ((str)[0] != '\0')
/**
* @mainpage About
*
* libnetconf2 is a NETCONF library in C handling NETCONF authentication and all NETCONF
* RPC communication both server and client-side. Note that NETCONF datastore implementation
* is not a part of this library. The library supports both NETCONF 1.0
* ([RFC 4741](https://tools.ietf.org/html/rfc4741)) as well as NETCONF 1.1
* ([RFC 6241](https://tools.ietf.org/html/rfc6241)).
*
* @section about-features Main Features
*
* - Creating SSH ([RFC 4742](https://tools.ietf.org/html/rfc4742), [RFC 6242](https://tools.ietf.org/html/rfc6242)),
* using [libssh](https://www.libssh.org/), or TLS ([RFC 7589](https://tools.ietf.org/html/rfc7589)),
* using [OpenSSL](https://www.openssl.org/), authenticated NETCONF sessions.
* - Creating NETCONF sessions with a pre-established transport protocol
* (using this mechanism the communication can be tunneled through sshd(8), for instance).
* - Creating NETCONF Call Home sessions ([RFC 8071](https://tools.ietf.org/html/rfc8071)).
* - Creating, sending, receiving, and replying to RPCs ([RFC 4741](https://tools.ietf.org/html/rfc4741),
* [RFC 6241](https://tools.ietf.org/html/rfc6241)).
* - Creating, sending and receiving NETCONF Event Notifications ([RFC 5277](https://tools.ietf.org/html/rfc5277)),
*
* @section about-license License
*
* Copyright (c) 2015-2021 CESNET, z.s.p.o.
*
* (The BSD 3-Clause License)
*
* 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 above copyright
* notice, this list of conditions and the following disclaimer.
* 2. 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.
* 3. Neither the name of the Company nor the names of its contributors
* may be used to endorse or promote products derived from this
* software without specific prior written permission.
*/
/**
* @page howto How To ...
*
* - @subpage howtoinit
* - @subpage howtoclient
* - @subpage howtoserver
* - @subpage howtoclientcomm
* - @subpage howtoservercomm
* - @subpage howtotimeouts
*/
/**
* @page howtoinit Init and Thread-safety Information
*
* Before working with the library, it must be initialized using ::nc_client_init()
* or ::nc_server_init(). Based on how the library was compiled, also _libssh_ and/or
* _libssh_/_libcrypto_ are initialized (for multi-threaded use) too. To prevent
* any reachable memory at the end of your application, there are complementary
* destroy functions (::nc_server_destroy() and ::nc_client_destroy() available. If your
* application is multi-threaded, call the destroy functions in the main thread,
* after all the other threads have ended. In every other thread you should call
* ::nc_thread_destroy() just before it exits.
*
* If _libnetconf2_ is used in accordance with this information, there should
* not be memory leaks of any kind at program exit. For thread-safety details
* of _libssh_, _libssl_, and _libcrypto_, please refer to the corresponding project
* documentation. _libnetconf2_ thread-safety information is below.
*
* Client
* ------
*
* Optionally, a client can specify two alternative ways to get schemas needed when connecting
* with a server. The primary way is to read local files in searchpath (and its subdirectories)
* specified via ::nc_client_set_schema_searchpath(). Alternatively, _libnetconf2_ can use callback
* provided via ::nc_client_set_schema_callback(). If these ways do not succeed and the server
* implements NETCONF \<get-schema\> operation, the schema is retrieved from the server and stored
* localy into the searchpath (if specified) for a future use. If none of these methods succeed to
* load particular schema, the data from this schema are ignored during the communication with the
* server.
*
* Besides the mentioned setters, there are many other @ref howtoclientssh "SSH", @ref howtoclienttls "TLS"
* and @ref howtoclientch "Call Home" getter/setter functions to manipulate with various settings. All these
* settings are internally placed in a thread-specific context so they are independent and
* initialized to the default values within each new thread. However, the context can be shared among
* the threads using ::nc_client_get_thread_context() and ::nc_client_set_thread_context() functions. In such
* a case, be careful and avoid concurrent execution of the mentioned setters/getters and functions
* creating connection (no matter if it is a standard NETCONF connection or Call Home).
*
* In the client, it is thread-safe to work with distinguish NETCONF sessions since the client
* settings are thread-specific as described above.
*
* Server
* ------
*
* Server is __FULLY__ thread-safe meaning you can set all the (thread-shared in contrast to
* client) options simultaneously while listening for or accepting new sessions or
* polling the existing ones. It is even safe to poll one session in several
* pollsession structures or one pollsession structure in several threads. Generally,
* servers can use more threads without any problems as long as they keep their workflow sane
* (behavior such as freeing sessions only after no thread uses them or similar).
*
* Functions List
* --------------
*
* Available in __nc_client.h__.
*
* - ::nc_client_init()
* - ::nc_client_destroy()
*
* - ::nc_client_set_schema_searchpath()
* - ::nc_client_get_schema_searchpath()
* - ::nc_client_set_schema_callback()
* - ::nc_client_get_schema_callback()
*
* - ::nc_client_set_thread_context()
* - ::nc_client_get_thread_context()
*
* Available in __nc_server.h__.
*
* - ::nc_server_init()
* - ::nc_server_destroy()
*
* Available in both __nc_client.h__ and __nc_server.h__.
*
* - ::nc_thread_destroy()
*/
/**
* @page howtoclient Client sessions
*
* To connect to a NETCONF server, a NETCONF session must be established,
* which requires a working transport session. It is possible to create
* NETCONF sessions with SSH (using _libssh_) or TLS (using _libssl/libcrypto_)
* as the underlying transport protocol. It is also possible to establish
* the transport protocol outside _libnetconf2_ and then provide these file
* descriptors (FD) for full NETCONF session creation.
*
* There are a lot of options for both an SSH and a TLS client. All of them
* have setters and getters so that there is no need to duplicate them in
* a client.
*
* @anchor howtoclientssh
* SSH
* ===
*
* Connecting to a server using SSH does not strictly require to set any
* options, there are sensible default values for all the basic ones.
* Except all the SSH options, optionally some authetication callbacks can be set,
* which are particulary useful in automated clients (passwords cannot be
* asked a user) or simply if any additional information is retrieved some
* other way than from standard terminal input.
*
* Having the default options or changing any unsuitable ones, there are 2 functions
* to use for a new server connection. ::nc_connect_ssh() is the standard function
* that creates sessions using the set options. If there are some options, which
* cannot be changed with the provided API, there is ::nc_connect_libssh() available.
* It requires a _libssh_ session, in which all the SSH options can be modified
* and even the connection established. This allows for full customization and
* should fit any specific situation.
*
* New NETCONF sessions can also be created on existing authenticated SSH sessions.
* There is a new SSH channel needed, on which the NETCONF session is then created.
* Use ::nc_connect_ssh_channel() for this purpose.
*
* Functions List
* --------------
*
* Available in __nc_client.h__.
*
* - ::nc_client_ssh_set_auth_hostkey_check_clb()
* - ::nc_client_ssh_get_auth_hostkey_check_clb()
* - ::nc_client_ssh_set_auth_password_clb()
* - ::nc_client_ssh_get_auth_password_clb()
* - ::nc_client_ssh_set_auth_interactive_clb()
* - ::nc_client_ssh_get_auth_interactive_clb()
* - ::nc_client_ssh_set_auth_privkey_passphrase_clb()
* - ::nc_client_ssh_get_auth_privkey_passphrase_clb()
* - ::nc_client_ssh_add_keypair()
* - ::nc_client_ssh_del_keypair()
* - ::nc_client_ssh_get_keypair_count()
* - ::nc_client_ssh_get_keypair()
* - ::nc_client_ssh_set_auth_pref()
* - ::nc_client_ssh_get_auth_pref()
* - ::nc_client_ssh_set_username()
* - ::nc_client_ssh_get_username()
*
* - ::nc_connect_ssh()
* - ::nc_connect_libssh()
* - ::nc_connect_ssh_channel()
*
* @anchor howtoclienttls
* TLS
* ===
*
* To connect to a server using TLS, there must be some client identification
* options set. Client must specify its certificate with a private key using
* ::nc_client_tls_set_cert_key_paths(). Also, the Certificate Authority of
* a server certificate must be considered trusted. Paths to all the trusted
* CA certificates can be set by ::nc_client_tls_set_trusted_ca_paths().
*
* Then there are again 2 functions for connecting, ::nc_connect_tls() being
* the standard way of connecting. ::nc_connect_libssl() again enables
* to customize the TLS session in every way _libssl_ allows.
*
* Functions List
* --------------
*
* Available in __nc_client.h__.
*
* - ::nc_client_tls_set_cert_key_paths()
* - ::nc_client_tls_get_cert_key_paths()
* - ::nc_client_tls_set_trusted_ca_paths()
* - ::nc_client_tls_get_trusted_ca_paths()
* - ::nc_client_tls_set_crl_paths()
* - ::nc_client_tls_get_crl_paths()
*
* - ::nc_connect_tls()
* - ::nc_connect_libssl()
*
*
* FD and UNIX socket
* ==================
*
* If you authenticated the connection using some tunneling software, you
* can pass its file descriptors to _libnetconf2_ using ::nc_connect_inout(),
* which will continue to establish a full NETCONF session. To connect locally
* on a UNIX socket avoiding all cryptography use ::nc_connect_unix().
*
* Funtions List
* -------------
*
* Available in __nc_client.h__.
*
* - ::nc_connect_inout()
* - ::nc_connect_unix()
*
*
* @anchor howtoclientch
* Call Home
* =========
*
* Call Home needs the same options set as standard SSH or TLS and the functions
* reflect it exactly. However, to accept a connection, the client must first
* specify addresses and ports, which to listen on by ::nc_client_ssh_ch_add_bind_listen()
* and ::nc_client_tls_ch_add_bind_listen(). Then connections can be
* accepted using ::nc_accept_callhome().
*
* Functions List
* --------------
*
* Available in __nc_client.h__.
*
* - ::nc_client_ssh_ch_set_auth_hostkey_check_clb()
* - ::nc_client_ssh_ch_set_auth_password_clb()
* - ::nc_client_ssh_ch_set_auth_interactive_clb()
* - ::nc_client_ssh_ch_set_auth_privkey_passphrase_clb()
* - ::nc_client_ssh_ch_add_bind_listen()
* - ::nc_client_ssh_ch_del_bind()
* - ::nc_client_ssh_ch_add_keypair()
* - ::nc_client_ssh_ch_del_keypair()
* - ::nc_client_ssh_ch_get_keypair_count()
* - ::nc_client_ssh_ch_get_keypair()
* - ::nc_client_ssh_ch_set_auth_pref()
* - ::nc_client_ssh_ch_get_auth_pref()
* - ::nc_client_ssh_ch_set_username()
* - ::nc_client_ssh_ch_get_username()
*
* - ::nc_client_tls_ch_add_bind_listen()
* - ::nc_client_tls_ch_del_bind()
* - ::nc_client_tls_ch_set_cert_key_paths()
* - ::nc_client_tls_ch_get_cert_key_paths()
* - ::nc_client_tls_ch_set_trusted_ca_paths()
* - ::nc_client_tls_ch_get_trusted_ca_paths()
* - ::nc_client_tls_ch_set_crl_paths()
* - ::nc_client_tls_ch_get_crl_paths()
*
* - ::nc_accept_callhome()
*
*
* Cleanup
* =======
*
* These options and the schema searchpath are stored in dynamically
* allocated memory. They are freed as a part of [destroying the client](@ref howtoinit).
*/
/**
* @page howtoserver Server sessions
*
* Init
* ====
*
* Server takes an argument for its [initialization function](@ref howtoinit).
* In it, you set the server context, which determines what modules it
* supports and what capabilities to advertise. Few capabilities that
* cannot be learnt from the context are set with separate functions
* ::nc_server_set_capab_withdefaults() and generally ::nc_server_set_capability().
* Timeout for receiving the _hello_ message on a new session can be set
* by ::nc_server_set_hello_timeout() and the timeout for disconnecting
* an inactive session by ::nc_server_set_idle_timeout().
*
* Context does not only determine server modules, but its overall
* functionality as well. For every RPC the server should support,
* an nc_rpc_clb callback should be set on that node in the context using ::nc_set_rpc_callback().
* Server then calls these as appropriate [during poll](@ref howtoservercomm).
*
* Just like in the [client](@ref howtoclient), you can let _libnetconf2_
* establish SSH or TLS transport or do it yourself and only provide the file
* descriptors of the connection.
*
* Server options can be only set, there are no getters.
*
* To be able to accept any connections, endpoints must first be added
* with ::nc_server_add_endpt() and configured with ::nc_server_endpt_set_address()
* and ::nc_server_endpt_set_port(). For unix sockets, ::nc_server_endpt_set_perms()
* is available to set the unix socket file permissions, and ::nc_server_endpt_set_port()
* is invalid.
*
* Functions List
* --------------
*
* Available in __nc_server.h__.
*
* - ::nc_server_set_capab_withdefaults()
* - ::nc_server_set_capability()
* - ::nc_server_set_hello_timeout()
* - ::nc_server_set_idle_timeout()
*
* - ::nc_server_add_endpt()
* - ::nc_server_del_endpt()
* - ::nc_server_endpt_set_address()
* - ::nc_server_endpt_set_port()
* - ::nc_server_endpt_set_perms()
*
*
* SSH
* ===
*
* To successfully accept an SSH session you must set at least the host key using
* ::nc_server_ssh_endpt_add_hostkey(), which are ordered. This way you simply add
* some hostkey identifier, but the key itself will be retrieved always when needed
* by calling the callback set by ::nc_server_ssh_set_hostkey_clb().
*
* There are also some other optional settings. Note that authorized
* public keys are set for the server as a whole, not endpoint-specifically.
*
* Functions List
* --------------
*
* Available in __nc_server.h__.
*
* - ::nc_server_ssh_endpt_add_hostkey()
* - ::nc_server_ssh_endpt_del_hostkey()
* - ::nc_server_ssh_endpt_mov_hostkey()
* - ::nc_server_ssh_endpt_mod_hostkey()
* - ::nc_server_ssh_endpt_set_auth_methods()
* - ::nc_server_ssh_endpt_set_auth_attempts()
* - ::nc_server_ssh_endpt_set_auth_timeout()
*
* - ::nc_server_ssh_set_hostkey_clb()
*
* - ::nc_server_ssh_add_authkey()
* - ::nc_server_ssh_add_authkey_path()
* - ::nc_server_ssh_del_authkey()
*
*
* TLS
* ===
*
* TLS works with endpoints too, but its options differ
* significantly from the SSH ones, especially in the _cert-to-name_
* options that TLS uses to derive usernames from client certificates.
* So, after starting listening on an endpoint you need to set the server
* certificate (::nc_server_tls_endpt_set_server_cert()). Its actual content
* together with the matching private key will be loaded using a callback
* from ::nc_server_tls_set_server_cert_clb(). Additional certificates needed
* for the client to verify the server's certificate chain can be loaded using
* a callback from ::nc_server_tls_set_server_cert_chain_clb().
*
* To accept client certificates, they must first be considered trusted,
* which you have three ways of achieving. You can add each of their Certificate Authority
* certificates to the trusted ones or mark a specific client certificate
* as trusted. Lastly, you can set paths with all the trusted CA certificates
* with ::nc_server_tls_endpt_set_trusted_ca_paths(). Adding specific certificates
* is also performed only as an arbitrary identificator and later retrieved from
* callback set by ::nc_server_tls_set_trusted_cert_list_clb(). But, you can add
* certficates as whole lists, not one-by-one.
*
* Then, from each trusted client certificate a username must be derived
* for the NETCONF session. This is accomplished by finding a matching
* _cert-to-name_ entry. They are added using ::nc_server_tls_endpt_add_ctn().
*
* If you need to remove trusted certificates, you can do so with ::nc_server_tls_endpt_del_trusted_cert_list().
* To clear all Certificate Revocation Lists use ::nc_server_tls_endpt_clear_crls().
*
* Functions List
* --------------
*
* Available in __nc_server.h__.
*
* - ::nc_server_tls_endpt_set_server_cert()
* - ::nc_server_tls_endpt_add_trusted_cert_list()
* - ::nc_server_tls_endpt_del_trusted_cert_list()
* - ::nc_server_tls_endpt_set_trusted_ca_paths()
* - ::nc_server_tls_endpt_set_crl_paths()
* - ::nc_server_tls_endpt_clear_crls()
* - ::nc_server_tls_endpt_add_ctn()
* - ::nc_server_tls_endpt_del_ctn()
* - ::nc_server_tls_endpt_get_ctn()
*
* - ::nc_server_tls_set_server_cert_clb()
* - ::nc_server_tls_set_server_cert_chain_clb()
* - ::nc_server_tls_set_trusted_cert_list_clb()
*
* FD
* ==
*
* If you used a tunneling software, which does its own authentication,
* you can accept a NETCONF session on its file descriptors with
* ::nc_accept_inout().
*
* Functions List
* --------------
*
* Available in __nc_server.h__.
*
* - ::nc_accept_inout()
*
*
* Call Home
* =========
*
* _Call Home_ works with endpoints just like standard sessions, but
* the options are organized a bit differently and endpoints are added
* for CH clients. However, one important difference is that
* once all the mandatory options are set, _libnetconf2_ __will not__
* immediately start connecting to a client. It will do so only after
* calling ::nc_connect_ch_client_dispatch() in a separate thread.
*
* Lastly, monitoring of these sessions is up to the application.
*
* Functions List
* --------------
*
* Available in __nc_server.h__.
*
* - ::nc_server_ch_add_client()
* - ::nc_server_ch_del_client()
* - ::nc_server_ch_is_client()
* - ::nc_server_ch_client_add_endpt()
* - ::nc_server_ch_client_del_endpt()
* - ::nc_server_ch_client_is_endpt()
* - ::nc_server_ch_client_endpt_set_address()
* - ::nc_server_ch_client_endpt_set_port()
* - ::nc_server_ch_client_endpt_enable_keepalives()
* - ::nc_server_ch_client_endpt_set_keepalives()
* - ::nc_server_ch_client_set_conn_type()
* - ::nc_server_ch_client_periodic_set_period()
* - ::nc_server_ch_client_periodic_set_anchor_time()
* - ::nc_server_ch_client_periodic_set_idle_timeout()
* - ::nc_server_ch_client_set_start_with()
* - ::nc_server_ch_client_set_max_attempts()
* - ::nc_connect_ch_client_dispatch()
*
* - ::nc_server_ssh_ch_client_endpt_add_hostkey()
* - ::nc_server_ssh_ch_client_endpt_del_hostkey()
* - ::nc_server_ssh_ch_client_endpt_mov_hostkey()
* - ::nc_server_ssh_ch_client_endpt_set_auth_methods()
* - ::nc_server_ssh_ch_client_endpt_get_auth_methods()
* - ::nc_server_ssh_ch_client_endpt_set_auth_attempts()
* - ::nc_server_ssh_ch_client_endpt_set_auth_timeout()
*
* - ::nc_server_tls_ch_client_endpt_set_server_cert()
* - ::nc_server_tls_ch_client_endpt_add_trusted_cert_list()
* - ::nc_server_tls_ch_client_endpt_del_trusted_cert_list()
* - ::nc_server_tls_ch_client_endpt_set_trusted_ca_paths()
* - ::nc_server_tls_ch_client_endpt_set_crl_paths()
* - ::nc_server_tls_ch_client_endpt_clear_crls()
* - ::nc_server_tls_ch_client_endpt_add_ctn()
* - ::nc_server_tls_ch_client_endpt_del_ctn()
* - ::nc_server_tls_ch_client_endpt_get_ctn()
*
*
* Connecting And Cleanup
* ======================
*
* When accepting connections with ::nc_accept(), all the endpoints are examined
* and the first with a pending connection is used. To remove all CH clients,
* endpoints, and free any used dynamic memory, [destroy](@ref howtoinit) the server.
*
* Functions List
* --------------
*
* Available in __nc_server.h__.
*
* - ::nc_accept()
*/
/**
* @page howtoclientcomm Client communication
*
* To send RPCs on a session, you simply create an RPC, send it using ::nc_send_rpc(),
* and then wait for a reply using ::nc_recv_reply(). If you are subscribed, there are 2 ways
* of receiving notifications. Either you wait for them the same way
* as for standard replies with ::nc_recv_notif() or you create a dispatcher
* with ::nc_recv_notif_dispatch() that asynchronously (in a separate thread)
* reads notifications and passes them to your callback.
*
* Functions List
* --------------
*
* Available in __nc_client.h__.
*
* - ::nc_rpc_act_generic()
* - ::nc_rpc_act_generic_xml()
* - ::nc_rpc_getconfig()
* - ::nc_rpc_edit()
* - ::nc_rpc_copy()
* - ::nc_rpc_delete()
* - ::nc_rpc_lock()
* - ::nc_rpc_unlock()
* - ::nc_rpc_get()
* - ::nc_rpc_kill()
* - ::nc_rpc_commit()
* - ::nc_rpc_discard()
* - ::nc_rpc_cancel()
* - ::nc_rpc_validate()
* - ::nc_rpc_getschema()
* - ::nc_rpc_subscribe()
* - ::nc_rpc_getdata()
* - ::nc_rpc_editdata()
* - ::nc_rpc_establishsub()
* - ::nc_rpc_modifysub()
* - ::nc_rpc_deletesub()
* - ::nc_rpc_killsub()
* - ::nc_rpc_establishpush_periodic()
* - ::nc_rpc_establishpush_onchange()
* - ::nc_rpc_modifypush_periodic()
* - ::nc_rpc_modifypush_onchange()
* - ::nc_rpc_resyncsub()
*
* - ::nc_send_rpc()
* - ::nc_recv_reply()
* - ::nc_recv_notif()
* - ::nc_recv_notif_dispatch()
*/
/**
* @page howtoservercomm Server communication
*
* Once at least one session is established, an nc_pollsession structure
* should be created with ::nc_ps_new(), filled with the session using
* ::nc_ps_add_session() and finally polled with ::nc_ps_poll(). Based on
* the return value from the poll, further actions can be taken. More
* sessions can be polled at the same time and any requests received on
* the sessions are [handled internally](@ref howtoserver).
*
* If an SSH NETCONF session asks for a new channel, you can accept
* this request with ::nc_ps_accept_ssh_channel() or ::nc_session_accept_ssh_channel()
* depending on the structure you want to use as the argument.
*
* Functions List
* --------------
*
* Available in __nc_server.h__.
*
* - ::nc_ps_new()
* - ::nc_ps_add_session()
* - ::nc_ps_del_session()
* - ::nc_ps_session_count()
* - ::nc_ps_free()
*
* - ::nc_ps_poll()
* - ::nc_ps_clear()
* - ::nc_ps_accept_ssh_channel()
* - ::nc_session_accept_ssh_channel()
*/
/**
* @page howtotimeouts Timeouts
*
* There are several timeouts which are used throughout _libnetconf2_ to
* assure that it will never indefinitely hang on any operation. Normally,
* you should not need to worry about them much necause they are set by
* default to reasonable values for common systems. However, if your
* platform is not common (embedded, ...), adjusting these timeouts may
* save a lot of debugging and time.
*
* Compile Options
* ---------------
*
* You can adjust active and inactive read timeout using `cmake` variables.
* For details look into `README.md`.
*
* API Functions
* -------------
*
* Once a new connection is established including transport protocol negotiations,
* _hello_ message is exchanged. You can set how long will the server wait for
* receiving this message from a client before dropping it.
*
* Having a NETCONF session working, it may not communicate for a longer time.
* To free up some resources, it is possible to adjust the maximum idle period
* of a session before it is disconnected. In _Call Home_, for both a persistent
* and periodic connection can this idle timeout be specified separately for each
* client using corresponding functions.
*
* Lastly, SSH user authentication timeout can be also modified. It is the time
* a client has to successfully authenticate after connecting before it is disconnected.
*
* Functions List
* --------------
*
* Available in __nc_server.h__.
*
* - ::nc_server_set_hello_timeout()
* - ::nc_server_get_hello_timeout()
* - ::nc_server_set_idle_timeout()
* - ::nc_server_get_idle_timeout()
* - ::nc_server_ch_client_periodic_set_idle_timeout()
* - ::nc_server_ssh_ch_client_endpt_set_auth_timeout()
* - ::nc_server_ssh_ch_client_endpt_set_auth_timeout()
*/
/**
* @defgroup misc Miscellaneous
* @brief Miscellaneous macros, types, structure and functions for a generic use by both server and client applications.
*/
/**
* @defgroup client Client
* @brief NETCONF client functionality.
*/
/**
* @defgroup server Server
* @brief NETCONF server functionality.
*/
#endif /* NC_LIBNETCONF_H_ */

148
src/log.c Normal file
View file

@ -0,0 +1,148 @@
/**
* \file log.c
* \author Radek Krejci <rkrejci@cesnet.cz>
* \brief libnetconf2 - log functions
*
* Copyright (c) 2015 - 2021 CESNET, z.s.p.o.
*
* This source code is licensed under BSD 3-Clause License (the "License").
* You may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://opensource.org/licenses/BSD-3-Clause
*/
#include <stdarg.h>
#include <stdio.h>
#include <libyang/libyang.h>
#ifdef NC_ENABLED_SSH
#include <libssh/libssh.h>
#endif
#include "compat.h"
#include "libnetconf.h"
#include "log.h"
/**
* @brief libnetconf verbose level variable
*/
volatile uint8_t verbose_level = 0;
void (*depr_print_clb)(NC_VERB_LEVEL level, const char *msg);
void (*print_clb)(const struct nc_session *session, NC_VERB_LEVEL level, const char *msg);
API void
nc_verbosity(NC_VERB_LEVEL level)
{
verbose_level = level;
ly_log_level((LY_LOG_LEVEL)level);
}
struct {
NC_VERB_LEVEL level;
const char *label;
} verb[] = {
{NC_VERB_ERROR, "[ERR]"},
{NC_VERB_WARNING, "[WRN]"},
{NC_VERB_VERBOSE, "[INF]"},
{NC_VERB_DEBUG, "[DBG]"},
{NC_VERB_DEBUG_LOWLVL, "[DBL]"}
};
#ifdef NC_ENABLED_SSH
API void
nc_libssh_thread_verbosity(int level)
{
ssh_set_log_level(level);
}
#endif
static void
prv_vprintf(const struct nc_session *session, NC_VERB_LEVEL level, const char *format, va_list args)
{
#define PRV_MSG_INIT_SIZE 256
va_list args2;
char *prv_msg;
void *mem;
int req_len;
prv_msg = malloc(PRV_MSG_INIT_SIZE);
if (!prv_msg) {
return;
}
va_copy(args2, args);
req_len = vsnprintf(prv_msg, PRV_MSG_INIT_SIZE - 1, format, args);
if (req_len == -1) {
goto cleanup;
} else if (req_len >= PRV_MSG_INIT_SIZE - 1) {
/* the length is not enough */
++req_len;
mem = realloc(prv_msg, req_len);
if (!mem) {
goto cleanup;
}
prv_msg = mem;
/* now print the full message */
req_len = vsnprintf(prv_msg, req_len, format, args2);
if (req_len == -1) {
goto cleanup;
}
}
if (print_clb) {
print_clb(session, level, prv_msg);
} else if (depr_print_clb) {
depr_print_clb(level, prv_msg);
} else if (session && session->id) {
fprintf(stderr, "Session %u %s: %s\n", session->id, verb[level].label, prv_msg);
} else {
fprintf(stderr, "%s: %s\n", verb[level].label, prv_msg);
}
cleanup:
free(prv_msg);
#undef PRV_MSG_INIT_SIZE
}
void
prv_printf(const struct nc_session *session, NC_VERB_LEVEL level, const char *format, ...)
{
va_list ap;
va_start(ap, format);
prv_vprintf(session, level, format, ap);
va_end(ap);
}
static void
nc_ly_log_clb(LY_LOG_LEVEL lvl, const char *msg, const char *UNUSED(path))
{
if (print_clb) {
print_clb(NULL, (NC_VERB_LEVEL)lvl, msg);
} else if (depr_print_clb) {
depr_print_clb((NC_VERB_LEVEL)lvl, msg);
}
}
API void
nc_set_print_clb(void (*clb)(NC_VERB_LEVEL, const char *))
{
print_clb = NULL;
depr_print_clb = clb;
ly_set_log_clb(nc_ly_log_clb, 1);
}
API void
nc_set_print_clb_session(void (*clb)(const struct nc_session *, NC_VERB_LEVEL, const char *))
{
print_clb = clb;
depr_print_clb = NULL;
ly_set_log_clb(nc_ly_log_clb, 1);
}

96
src/log.h Normal file
View file

@ -0,0 +1,96 @@
/**
* @file log.h
* @author Radek Krejci <rkrejci@cesnet.cz>
* @brief libnetconf2 logger
*
* Copyright (c) 2015 - 2021 CESNET, z.s.p.o.
*
* This source code is licensed under BSD 3-Clause License (the "License").
* You may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://opensource.org/licenses/BSD-3-Clause
*/
#ifndef NC_LOG_H_
#define NC_LOG_H_
struct nc_session;
#ifdef __cplusplus
extern "C" {
#endif
/**
* @addtogroup misc
* @{
*/
/**
* @brief Verbosity levels.
*/
typedef enum NC_VERB_LEVEL {
NC_VERB_ERROR = 0, /**< Print only error messages. */
NC_VERB_WARNING = 1, /**< Print error and warning messages. */
NC_VERB_VERBOSE = 2, /**< Besides errors and warnings, print some other verbose messages. */
NC_VERB_DEBUG = 3, /**< Print almost all messages including some development debug messages. */
NC_VERB_DEBUG_LOWLVL = 4 /**< Print all messages including low level debug messages. */
} NC_VERB_LEVEL;
/**
* @brief Set libnetconf's verbosity level.
*
* This level is set for libnetconf2 and alo libyang that is used internally. libyang
* verbose level can be set explicitly, but must be done so after calling this function.
* However, if debug verbosity is used, selecting displayed libyang debug message groups
* must be done explicitly.
*
* @param[in] level Enabled verbosity level (includes all the levels with higher priority).
*/
void nc_verbosity(NC_VERB_LEVEL level);
#ifdef NC_ENABLED_SSH
/**
* @brief Set libssh verbosity level.
*
* libssh verbosity is set separately because it defines more verbose levels than libnetconf2.
* Also, you need to set this for every thread unlike libnetconf verbosity.
*
* Values:
* - 0 - no logging,
* - 1 - rare conditions or warnings,
* - 2 - API-accessible entrypoints,
* - 3 - packet id and size,
* - 4 - functions entering and leaving.
*
* @param[in] level libssh verbosity level.
*/
void nc_libssh_thread_verbosity(int level);
#endif
/**
* @brief Deprecated, use ::nc_set_print_clb_session() instead.
*
* @param[in] clb Callback that is called for every message.
*/
void nc_set_print_clb(void (*clb)(NC_VERB_LEVEL, const char *));
/**
* @brief Set libnetconf print callback.
*
* This callback is set for libnetconf2 and also libyang that is used internally. libyang
* callback can be set explicitly, but must be done so after calling this function.
*
* @param[in] clb Callback that is called for every message.
*/
void nc_set_print_clb_session(void (*clb)(const struct nc_session *, NC_VERB_LEVEL, const char *));
/** @} */
#ifdef __cplusplus
}
#endif
#endif /* NC_LOG_H_ */

54
src/log_p.h Normal file
View file

@ -0,0 +1,54 @@
/**
* @file log.h
* @author Radek Krejci <rkrejci@cesnet.cz>
* @brief libnetconf2 logger
*
* Copyright (c) 2015 - 2021 CESNET, z.s.p.o.
*
* This source code is licensed under BSD 3-Clause License (the "License").
* You may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://opensource.org/licenses/BSD-3-Clause
*/
#ifndef NC_LOG_PRIVATE_H_
#define NC_LOG_PRIVATE_H_
#include <stdint.h>
#include "log.h"
/*
* libnetconf's message printing
*/
/**
* @brief Internal printing function
*
* @param[in] session Optional NETCONF session that generated the message
* @param[in] level Verbose level
* @param[in] format Formatting string
*/
void prv_printf(const struct nc_session *session, NC_VERB_LEVEL level, const char *format, ...);
/**
* @brief Verbose level variable
*/
extern volatile uint8_t verbose_level;
/*
* Verbose printing macros
*/
#define ERR(session, format, args ...) prv_printf(session,NC_VERB_ERROR,format,##args)
#define WRN(session, format, args ...) if(verbose_level>=NC_VERB_WARNING){prv_printf(session,NC_VERB_WARNING,format,##args);}
#define VRB(session, format, args ...) if(verbose_level>=NC_VERB_VERBOSE){prv_printf(session,NC_VERB_VERBOSE,format,##args);}
#define DBG(session, format, args ...) if(verbose_level>=NC_VERB_DEBUG){prv_printf(session,NC_VERB_DEBUG,format,##args);}
#define DBL(session, format, args ...) if(verbose_level>=NC_VERB_DEBUG_LOWLVL){prv_printf(session,NC_VERB_DEBUG_LOWLVL,format,##args);}
#define ERRMEM ERR(NULL, "%s: memory reallocation failed (%s:%d).", __func__, __FILE__, __LINE__)
#define ERRARG(arg) ERR(NULL, "%s: invalid argument (%s).", __func__, arg)
#define ERRINIT ERR(NULL, "%s: libnetconf2 not initialized.", __func__)
#define ERRINT ERR(NULL, "%s: internal error (%s:%d).", __func__, __FILE__, __LINE__)
#endif /* NC_LOG_PRIVATE_H_ */

1234
src/messages_client.c Normal file

File diff suppressed because it is too large Load diff

555
src/messages_client.h Normal file
View file

@ -0,0 +1,555 @@
/**
* @file messages_client.h
* @author Radek Krejci <rkrejci@cesnet.cz>
* @author Michal Vasko <mvasko@cesnet.cz>
* @brief libnetconf2's public functions and structures of NETCONF client messages.
*
* Copyright (c) 2015 - 2021 CESNET, z.s.p.o.
*
* This source code is licensed under BSD 3-Clause License (the "License").
* You may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://opensource.org/licenses/BSD-3-Clause
*/
#ifndef NC_MESSAGES_CLIENT_H_
#define NC_MESSAGES_CLIENT_H_
#ifdef __cplusplus
extern "C" {
#endif
#include <stdint.h>
#include "netconf.h"
/**
* @defgroup client_msg Client Messages
* @ingroup client
*
* @brief Functions to create NETCONF RPCs (or actions) and process replies received from the server.
* @{
*/
/**
* @brief Enumeration of RPC types
*
* Note that NC_RPC_CLOSE is not defined since sending \<close-session\> is done implicitly by nc_session_free()
*/
typedef enum {
NC_RPC_UNKNOWN = 0, /**< invalid RPC. */
NC_RPC_ACT_GENERIC, /**< user-defined generic RPC/action. */
/* ietf-netconf */
NC_RPC_GETCONFIG, /**< \<get-config\> RPC. */
NC_RPC_EDIT, /**< \<edit-config\> RPC. */
NC_RPC_COPY, /**< \<copy-config\> RPC. */
NC_RPC_DELETE, /**< \<delete-config\> RPC. */
NC_RPC_LOCK, /**< \<lock\> RPC. */
NC_RPC_UNLOCK, /**< \<unlock\> RPC. */
NC_RPC_GET, /**< \<get\> RPC. */
NC_RPC_KILL, /**< \<kill-session\> RPC. */
NC_RPC_COMMIT, /**< \<commit\> RPC. */
NC_RPC_DISCARD, /**< \<discard-changes\> RPC. */
NC_RPC_CANCEL, /**< \<cancel-commit\> RPC. */
NC_RPC_VALIDATE, /**< \<validate\> RPC. */
/* ietf-netconf-monitoring */
NC_RPC_GETSCHEMA, /**< \<get-schema\> RPC. */
/* notifications */
NC_RPC_SUBSCRIBE, /**< \<create-subscription\> RPC. */
/* ietf-netconf-nmda */
NC_RPC_GETDATA, /**< \<get-data\> RPC. */
NC_RPC_EDITDATA, /**< \<edit-data\> RPC. */
/* ietf-subscribed-notifications */
NC_RPC_ESTABLISHSUB, /**< \<establish-subscription\> RPC. */
NC_RPC_MODIFYSUB, /**< \<modify-subscription\> RPC. */
NC_RPC_DELETESUB, /**< \<delete-subscription\> RPC. */
NC_RPC_KILLSUB, /**< \<kill-subscription\> RPC. */
/* ietf-yang-push */
NC_RPC_ESTABLISHPUSH, /**< \<establish-subscription\> RPC with augments. */
NC_RPC_MODIFYPUSH, /**< \<modify-subscription\> RPC with augments. */
NC_RPC_RESYNCSUB /**< \<resync-subscription\> RPC. */
} NC_RPC_TYPE;
/**
* @brief Enumeration of \<edit-config\> default operation
*/
typedef enum {
NC_RPC_EDIT_DFLTOP_UNKNOWN = 0, /**< unknown default operation */
NC_RPC_EDIT_DFLTOP_MERGE, /**< default operation merge */
NC_RPC_EDIT_DFLTOP_REPLACE, /**< default operation replace */
NC_RPC_EDIT_DFLTOP_NONE /**< default operation none */
} NC_RPC_EDIT_DFLTOP;
/**
* @brief Enumeration of \<edit-config\> test option
*/
typedef enum {
NC_RPC_EDIT_TESTOPT_UNKNOWN = 0, /**< unknown test option */
NC_RPC_EDIT_TESTOPT_TESTSET, /**< test-then-set option */
NC_RPC_EDIT_TESTOPT_SET, /**< set option */
NC_RPC_EDIT_TESTOPT_TEST /**< test-only option */
} NC_RPC_EDIT_TESTOPT;
/**
* @brief Enumeration of \<edit-config\> error option
*/
typedef enum {
NC_RPC_EDIT_ERROPT_UNKNOWN = 0, /**< unknown error option */
NC_RPC_EDIT_ERROPT_STOP, /**< stop-on-error option */
NC_RPC_EDIT_ERROPT_CONTINUE, /**< continue-on-error option */
NC_RPC_EDIT_ERROPT_ROLLBACK /**< rollback-on-error option */
} NC_RPC_EDIT_ERROPT;
/**
* @brief NETCONF error structure representation
*/
struct nc_err {
/** @brief \<error-type\>, error layer where the error occurred. */
const char *type;
/** @brief \<error-tag\>. */
const char *tag;
/** @brief \<error-severity\>. */
const char *severity;
/** @brief \<error-app-tag\>, the data-model-specific or implementation-specific error condition, if one exists. */
const char *apptag;
/** @brief \<error-path\>, XPATH expression identifying the element with the error. */
const char *path;
/** @brief \<error-message\>, Human-readable description of the error. */
const char *message;
/** @brief xml:lang attribute of the error-message. */
const char *message_lang;
/* <error-info> */
/** @brief \<session-id\>, session ID of the session holding the requested lock. Part of \<error-info\>. */
const char *sid;
/** @brief \<bad-attr\>, array of the names of the data-model-specific XML attributes that caused the error. Part of \<error-info\>. */
const char **attr;
/** @brief \<bad-element\>, array of the names of the data-model-specific XML element that caused the error. Part of \<error-info\>. */
const char **elem;
/** @brief \<bad-namespace\>, array of the unexpected XML namespaces that caused the error. Part of \<error-info\>. */
const char **ns;
/** @brief List of the remaining non-standard opaque nodes. */
struct lyd_node *other;
/** @brief Number of items in the attr array */
uint16_t attr_count;
/** @brief Number of items in the elem array */
uint16_t elem_count;
/** @brief Number of items in the ns array */
uint16_t ns_count;
/** @brief Number of items in the other array */
uint16_t other_count;
};
/**
* @struct nc_rpc
* @brief NETCONF client RPC object
*
* Note that any stored parameters are not checked for validity because it is performed later,
* while sending the RPC via a specific NETCONF session (::nc_send_rpc()) since the NETCONF
* capabilities of the session are needed for such a check. An RPC object can be sent via any
* NETCONF session which supports all the needed NETCONF capabilities for the RPC.
*/
struct nc_rpc;
/**
* @brief Get the type of the RPC
*
* @param[in] rpc RPC to check the type of.
* @return Type of @p rpc.
*/
NC_RPC_TYPE nc_rpc_get_type(const struct nc_rpc *rpc);
/**
* @brief Create a generic NETCONF RPC or action
*
* Note that created object can be sent via any NETCONF session that shares the context
* of the @p data.
*
* @note In case of action, the \<action\> element is added automatically and should not be in @p data.
*
* @param[in] data NETCONF RPC data as a data tree.
* @param[in] paramtype How to further manage data parameters.
* @return Created RPC object to send via a NETCONF session or NULL in case of (memory allocation) error.
*/
struct nc_rpc *nc_rpc_act_generic(const struct lyd_node *data, NC_PARAMTYPE paramtype);
/**
* @brief Create a generic NETCONF RPC or action from an XML string
*
* For details, see ::nc_rpc.
*
* @note In case of action, the \<action\> element is added automatically and should not be in @p xml_str.
*
* @param[in] xml_str NETCONF RPC data as an XML string.
* @param[in] paramtype How to further manage data parameters.
* @return Created RPC object to send via a NETCONF session or NULL in case of (memory allocation) error.
*/
struct nc_rpc *nc_rpc_act_generic_xml(const char *xml_str, NC_PARAMTYPE paramtype);
/**
* @brief Create NETCONF RPC \<get-config\>
*
* For details, see ::nc_rpc.
*
* @param[in] source Source datastore being queried.
* @param[in] filter Optional filter data, an XML subtree or XPath expression (with JSON prefixes).
* @param[in] wd_mode Optional with-defaults capability mode.
* @param[in] paramtype How to further manage data parameters.
* @return Created RPC object to send via a NETCONF session or NULL in case of (memory allocation) error.
*/
struct nc_rpc *nc_rpc_getconfig(NC_DATASTORE source, const char *filter, NC_WD_MODE wd_mode,
NC_PARAMTYPE paramtype);
/**
* @brief Create NETCONF RPC \<edit-config\>
*
* For details, see ::nc_rpc.
*
* @param[in] target Target datastore being edited.
* @param[in] default_op Optional default operation.
* @param[in] test_opt Optional test option.
* @param[in] error_opt Optional error option.
* @param[in] edit_content Config or URL where the config to perform is to be found.
* @param[in] paramtype How to further manage data parameters.
* @return Created RPC object to send via a NETCONF session or NULL in case of (memory allocation) error.
*/
struct nc_rpc *nc_rpc_edit(NC_DATASTORE target, NC_RPC_EDIT_DFLTOP default_op, NC_RPC_EDIT_TESTOPT test_opt,
NC_RPC_EDIT_ERROPT error_opt, const char *edit_content, NC_PARAMTYPE paramtype);
/**
* @brief Create NETCONF RPC \<copy-config\>
*
* For details, see ::nc_rpc.
*
* @param[in] target Target datastore.
* @param[in] url_trg Used instead @p target if the target is an URL.
* @param[in] source Source datastore.
* @param[in] url_or_config_src Used instead @p source if the source is an URL or a config.
* @param[in] wd_mode Optional with-defaults capability mode.
* @param[in] paramtype How to further manage data parameters.
* @return Created RPC object to send via a NETCONF session or NULL in case of (memory allocation) error.
*/
struct nc_rpc *nc_rpc_copy(NC_DATASTORE target, const char *url_trg, NC_DATASTORE source,
const char *url_or_config_src, NC_WD_MODE wd_mode, NC_PARAMTYPE paramtype);
/**
* @brief Create NETCONF RPC \<delete-config\>
*
* For details, see ::nc_rpc.
*
* @param[in] target Target datastore to delete.
* @param[in] url Used instead @p target if the target is an URL.
* @param[in] paramtype How to further manage data parameters.
* @return Created RPC object to send via a NETCONF session or NULL in case of (memory allocation) error.
*/
struct nc_rpc *nc_rpc_delete(NC_DATASTORE target, const char *url, NC_PARAMTYPE paramtype);
/**
* @brief Create NETCONF RPC \<lock\>
*
* For details, see ::nc_rpc.
*
* @param[in] target Target datastore of the operation.
* @return Created RPC object to send via a NETCONF session or NULL in case of (memory allocation) error.
*/
struct nc_rpc *nc_rpc_lock(NC_DATASTORE target);
/**
* @brief Create NETCONF RPC \<unlock\>
*
* For details, see ::nc_rpc.
*
* @param[in] target Target datastore of the operation.
* @return Created RPC object to send via a NETCONF session or NULL in case of (memory allocation) error.
*/
struct nc_rpc *nc_rpc_unlock(NC_DATASTORE target);
/**
* @brief Create NETCONF RPC \<get\>
*
* For details, see ::nc_rpc.
*
* @param[in] filter Optional filter data, an XML subtree or XPath expression (with JSON prefixes).
* @param[in] wd_mode Optional with-defaults capability mode.
* @param[in] paramtype How to further manage data parameters.
* @return Created RPC object to send via a NETCONF session or NULL in case of (memory allocation) error.
*/
struct nc_rpc *nc_rpc_get(const char *filter, NC_WD_MODE wd_mode, NC_PARAMTYPE paramtype);
/**
* @brief Create NETCONF RPC \<kill-session\>
*
* For details, see ::nc_rpc.
*
* @param[in] session_id Session ID of the session to kill.
* @return Created RPC object to send via a NETCONF session or NULL in case of (memory allocation) error.
*/
struct nc_rpc *nc_rpc_kill(uint32_t session_id);
/**
* @brief Create NETCONF RPC \<commit\>
*
* For details, see ::nc_rpc.
*
* @param[in] confirmed Whether the commit is to be confirmed.
* @param[in] confirm_timeout Optional confirm timeout.
* @param[in] persist Optional identification string of a new persistent confirmed commit.
* @param[in] persist_id Optional identification string of a persistent confirmed commit to be commited.
* @param[in] paramtype How to further manage data parameters.
* @return Created RPC object to send via a NETCONF session or NULL in case of (memory allocation) error.
*/
struct nc_rpc *nc_rpc_commit(int confirmed, uint32_t confirm_timeout, const char *persist, const char *persist_id,
NC_PARAMTYPE paramtype);
/**
* @brief Create NETCONF RPC \<discard-changes\>
*
* For details, see ::nc_rpc.
*
* @return Created RPC object to send via a NETCONF session or NULL in case of (memory allocation) error.
*/
struct nc_rpc *nc_rpc_discard(void);
/**
* @brief Create NETCONF RPC \<cancel-commit\>
*
* For details, see ::nc_rpc.
*
* @param[in] persist_id Optional identification string of a persistent confirmed commit.
* @param[in] paramtype How to further manage data parameters.
* @return Created RPC object to send via a NETCONF session or NULL in case of (memory allocation) error.
*/
struct nc_rpc *nc_rpc_cancel(const char *persist_id, NC_PARAMTYPE paramtype);
/**
* @brief Create NETCONF RPC \<validate\>
*
* For details, see ::nc_rpc.
*
* @param[in] source Source datastore being validated.
* @param[in] url_or_config Used instead @p source if the source is an URL or a config.
* @param[in] paramtype How to further manage data parameters.
* @return Created RPC object to send via a NETCONF session or NULL in case of (memory allocation) error.
*/
struct nc_rpc *nc_rpc_validate(NC_DATASTORE source, const char *url_or_config, NC_PARAMTYPE paramtype);
/**
* @brief Create NETCONF RPC \<get-schema\>
*
* For details, see ::nc_rpc.
*
* @param[in] identifier Requested model identifier.
* @param[in] version Optional model version, either YANG version (1.0/1.1) or revision date.
* @param[in] format Optional format of the model (default is YANG).
* @param[in] paramtype How to further manage data parameters.
* @return Created RPC object to send via a NETCONF session or NULL in case of (memory allocation) error.
*/
struct nc_rpc *nc_rpc_getschema(const char *identifier, const char *version, const char *format, NC_PARAMTYPE paramtype);
/**
* @brief Create NETCONF RPC \<create-subscription\>
*
* For details, see ::nc_rpc.
*
* @param[in] stream_name Optional name of a NETCONF stream to subscribe to.
* @param[in] filter Optional filter data, an XML subtree or XPath expression (with JSON prefixes).
* @param[in] start_time Optional YANG datetime identifying the start of the subscription.
* @param[in] stop_time Optional YANG datetime identifying the end of the subscription.
* @param[in] paramtype How to further manage data parameters.
* @return Created RPC object to send via a NETCONF session or NULL in case of (memory allocation) error.
*/
struct nc_rpc *nc_rpc_subscribe(const char *stream_name, const char *filter, const char *start_time,
const char *stop_time, NC_PARAMTYPE paramtype);
/**
* @brief Create NETCONF RPC \<get-data\>
*
* For details, see ::nc_rpc.
*
* @param[in] datastore Source datastore, foreign identity so a module name prefix is required.
* @param[in] filter Optional filter data, an XML subtree or XPath expression (with JSON prefixes).
* @param[in] config_filter Optional config filter, "true" for config-only data, "false" for state-only data.
* @param[in] origin_filter Optional origin filter array, selects only nodes of this or derived origin.
* @param[in] origin_filter_count Count of filters is @p origin_filter.
* @param[in] neg_origin_filter Whether origin filters are negated or not.
* @param[in] max_depth Maximum depth of returned subtrees, 0 for unlimited.
* @param[in] with_origin Whether return data origin.
* @param[in] wd_mode Optional with-defaults capability mode.
* @param[in] paramtype How to further manage data parameters.
* @return Created RPC object to send via a NETCONF session or NULL in case of (memory allocation) error.
*/
struct nc_rpc *nc_rpc_getdata(const char *datastore, const char *filter, const char *config_filter, char **origin_filter,
int origin_filter_count, int neg_origin_filter, uint16_t max_depth, int with_origin, NC_WD_MODE wd_mode,
NC_PARAMTYPE paramtype);
/**
* @brief Create NETCONF RPC \<get-data\>
*
* For details, see ::nc_rpc.
*
* @param[in] datastore Source datastore, foreign identity so a module name prefix is required.
* @param[in] default_op Optional default operation.
* @param[in] edit_content Config or URL where the config to perform is to be found.
* @param[in] paramtype How to further manage data parameters.
* @return Created RPC object to send via a NETCONF session or NULL in case of (memory allocation) error.
*/
struct nc_rpc *nc_rpc_editdata(const char *datastore, NC_RPC_EDIT_DFLTOP default_op, const char *edit_content,
NC_PARAMTYPE paramtype);
/**
* @brief Create NETCONF RPC \<establish-subscription\>
*
* For details, see ::nc_rpc.
*
* @param[in] filter Optional filter data, an XML subtree, XPath expression (with JSON prefixes),
* or filter reference, selected based on the first character.
* @param[in] stream_name Name of a NETCONF stream to subscribe to.
* @param[in] start_time Optional YANG datetime identifying the start of the subscription.
* @param[in] stop_time Optional YANG datetime identifying the end of the subscription.
* @param[in] encoding Optional specific encoding to use.
* @param[in] paramtype How to further manage data parameters.
* @return Created RPC object to send via a NETCONF session or NULL in case of (memory allocation) error.
*/
struct nc_rpc *nc_rpc_establishsub(const char *filter, const char *stream_name, const char *start_time,
const char *stop_time, const char *encoding, NC_PARAMTYPE paramtype);
/**
* @brief Create NETCONF RPC \<modify-subscription\>
*
* For details, see ::nc_rpc.
*
* @param[in] id Subscription ID to modify.
* @param[in] filter Optional new filter data, an XML subtree, XPath expression (with JSON prefixes),
* or filter reference, selected based on the first character.
* @param[in] stop_time Optional new YANG datetime identifying the end of the subscription.
* @param[in] paramtype How to further manage data parameters.
* @return Created RPC object to send via a NETCONF session or NULL in case of (memory allocation) error.
*/
struct nc_rpc *nc_rpc_modifysub(uint32_t id, const char *filter, const char *stop_time, NC_PARAMTYPE paramtype);
/**
* @brief Create NETCONF RPC \<delete-subscription\>
*
* For details, see ::nc_rpc.
*
* @param[in] id Subscription ID to delete.
* @return Created RPC object to send via a NETCONF session or NULL in case of (memory allocation) error.
*/
struct nc_rpc *nc_rpc_deletesub(uint32_t id);
/**
* @brief Create NETCONF RPC \<kill-subscription\>
*
* For details, see ::nc_rpc.
*
* @param[in] id Subscription ID to kill.
* @return Created RPC object to send via a NETCONF session or NULL in case of (memory allocation) error.
*/
struct nc_rpc *nc_rpc_killsub(uint32_t id);
/**
* @brief Create NETCONF RPC \<establish-subscription\> with augments from ietf-yang-push for a periodic subscription
*
* For details, see ::nc_rpc.
*
* @param[in] datastore Source datastore, foreign identity so a module name prefix is required.
* @param[in] filter Optional filter data, an XML subtree, XPath expression (with JSON prefixes),
* or filter reference, selected based on the first character.
* @param[in] stop_time Optional YANG datetime identifying the end of the subscription.
* @param[in] encoding Optional specific encoding to use.
* @param[in] period Subscription period in centiseconds (0.01s).
* @param[in] anchor_time Optional anchor datetime for the period.
* @param[in] paramtype How to further manage data parameters.
* @return Created RPC object to send via a NETCONF session or NULL in case of (memory allocation) error.
*/
struct nc_rpc *nc_rpc_establishpush_periodic(const char *datastore, const char *filter, const char *stop_time,
const char *encoding, uint32_t period, const char *anchor_time, NC_PARAMTYPE paramtype);
/**
* @brief Create NETCONF RPC \<establish-subscription\> with augments from ietf-yang-push for an on-change subscription
*
* For details, see ::nc_rpc.
*
* @param[in] datastore Source datastore, foreign identity so a module name prefix is required.
* @param[in] filter Optional filter data, an XML subtree, XPath expression (with JSON prefixes),
* or filter reference, selected based on the first character.
* @param[in] stop_time Optional YANG datetime identifying the end of the subscription.
* @param[in] encoding Optional specific encoding to use.
* @param[in] dampening_period Optional dampening period of the notifications.
* @param[in] sync_on_start Whether to send a full push-update notification on subscription start.
* @param[in] excluded_change Optional NULL-terminated array of excluded changes.
* @param[in] paramtype How to further manage data parameters.
* @return Created RPC object to send via a NETCONF session or NULL in case of (memory allocation) error.
*/
struct nc_rpc *nc_rpc_establishpush_onchange(const char *datastore, const char *filter, const char *stop_time,
const char *encoding, uint32_t dampening_period, int sync_on_start, const char **excluded_change,
NC_PARAMTYPE paramtype);
/**
* @brief Create NETCONF RPC \<modify-subscription\> with augments from ietf-yang-push for a periodic subscription
*
* For details, see ::nc_rpc.
*
* @param[in] id Subscription ID to modify.
* @param[in] datastore Source datastore, foreign identity so a module name prefix is required.
* @param[in] filter Optional filter data, an XML subtree, XPath expression (with JSON prefixes),
* or filter reference, selected based on the first character.
* @param[in] stop_time Optional YANG datetime identifying the end of the subscription.
* @param[in] period Subscription period in centiseconds (0.01s).
* @param[in] anchor_time Optional anchor datetime for the period.
* @param[in] paramtype How to further manage data parameters.
* @return Created RPC object to send via a NETCONF session or NULL in case of (memory allocation) error.
*/
struct nc_rpc *nc_rpc_modifypush_periodic(uint32_t id, const char *datastore, const char *filter, const char *stop_time,
uint32_t period, const char *anchor_time, NC_PARAMTYPE paramtype);
/**
* @brief Create NETCONF RPC \<modify-subscription\> with augments from ietf-yang-push for an on-change subscription
*
* For details, see ::nc_rpc.
*
* @param[in] id Subscription ID to modify.
* @param[in] datastore Source datastore, foreign identity so a module name prefix is required.
* @param[in] filter Optional filter data, an XML subtree, XPath expression (with JSON prefixes),
* or filter reference, selected based on the first character.
* @param[in] stop_time Optional YANG datetime identifying the end of the subscription.
* @param[in] dampening_period Optional dampening period of the notifications.
* @param[in] paramtype How to further manage data parameters.
* @return Created RPC object to send via a NETCONF session or NULL in case of (memory allocation) error.
*/
struct nc_rpc *nc_rpc_modifypush_onchange(uint32_t id, const char *datastore, const char *filter, const char *stop_time,
uint32_t dampening_period, NC_PARAMTYPE paramtype);
/**
* @brief Create NETCONF RPC \<resync-subscription\>
*
* For details, see ::nc_rpc.
*
* @param[in] id Subscription ID to resync.
* @return Created RPC object to send via a NETCONF session or NULL in case of (memory allocation) error.
*/
struct nc_rpc *nc_rpc_resyncsub(uint32_t id);
/**
* @brief Free the NETCONF RPC object.
*
* @param[in] rpc Object to free.
*/
void nc_rpc_free(struct nc_rpc *rpc);
/** @} Client Messages */
#ifdef __cplusplus
}
#endif
#endif /* NC_MESSAGES_CLIENT_H_ */

263
src/messages_p.h Normal file
View file

@ -0,0 +1,263 @@
/**
* @file messages_p.h
* @author Radek Krejci <rkrejci@cesnet.cz>
* @brief libnetconf2's private functions and structures of NETCONF messages.
*
* Copyright (c) 2021 CESNET, z.s.p.o.
*
* This source code is licensed under BSD 3-Clause License (the "License").
* You may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://opensource.org/licenses/BSD-3-Clause
*/
#ifndef NC_MESSAGES_P_H_
#define NC_MESSAGES_P_H_
#include <libyang/libyang.h>
#include "messages_client.h"
#include "messages_server.h"
extern const char *rpcedit_dfltop2str[];
extern const char *rpcedit_testopt2str[];
extern const char *rpcedit_erropt2str[];
struct nc_server_reply {
NC_RPL type;
};
struct nc_server_reply_data {
NC_RPL type;
struct lyd_node *data;
int free;
NC_WD_MODE wd;
};
struct nc_server_reply_error {
NC_RPL type;
struct lyd_node *err;
};
struct nc_server_rpc {
struct lyd_node *envp; /**< NETCONF-specific RPC envelopes */
struct lyd_node *rpc; /**< RPC data tree */
};
struct nc_server_notif {
char *eventtime; /**< eventTime of the notification */
struct lyd_node *ntf; /**< notification data tree of the message */
int free;
};
struct nc_client_reply_error {
NC_RPL type;
struct nc_err *err;
uint32_t count;
struct ly_ctx *ctx;
};
struct nc_rpc {
NC_RPC_TYPE type;
};
struct nc_rpc_act_generic {
NC_RPC_TYPE type; /**< NC_RPC_ACT_GENERIC */
int has_data; /**< 1 for content.data, 0 for content.xml_str */
union {
struct lyd_node *data; /**< parsed RPC data */
char *xml_str; /**< raw XML string */
} content;
char free;
};
struct nc_rpc_getconfig {
NC_RPC_TYPE type; /**< NC_RPC_GETCONFIG */
NC_DATASTORE source; /**< NETCONF datastore being queried */
char *filter; /**< either XML subtree (starts with '<') or an XPath (starts with '/' or an alpha) */
NC_WD_MODE wd_mode;
char free;
};
struct nc_rpc_edit {
NC_RPC_TYPE type; /**< NC_RPC_EDIT */
NC_DATASTORE target;
NC_RPC_EDIT_DFLTOP default_op;
NC_RPC_EDIT_TESTOPT test_opt;
NC_RPC_EDIT_ERROPT error_opt;
char *edit_cont; /**< either URL (starts with aplha) or config (starts with '<') */
char free;
};
struct nc_rpc_copy {
NC_RPC_TYPE type; /**< NC_RPC_COPY */
NC_DATASTORE target;
char *url_trg;
NC_DATASTORE source;
char *url_config_src; /**< either URL (starts with aplha) or config (starts with '<') */
NC_WD_MODE wd_mode;
char free;
};
struct nc_rpc_delete {
NC_RPC_TYPE type; /**< NC_RPC_DELETE */
NC_DATASTORE target;
char *url;
char free;
};
struct nc_rpc_lock {
NC_RPC_TYPE type; /**< NC_RPC_LOCK or NC_RPC_UNLOCK */
NC_DATASTORE target;
};
struct nc_rpc_get {
NC_RPC_TYPE type; /**< NC_RPC_GET */
char *filter; /**< either XML subtree (starts with '<') or an XPath (starts with '/' or an alpha) */
NC_WD_MODE wd_mode;
char free;
};
struct nc_rpc_kill {
NC_RPC_TYPE type; /**< NC_RPC_KILL */
uint32_t sid;
};
struct nc_rpc_commit {
NC_RPC_TYPE type; /**< NC_RPC_COMMIT */
int confirmed;
uint32_t confirm_timeout;
char *persist;
char *persist_id;
char free;
};
struct nc_rpc_cancel {
NC_RPC_TYPE type; /**< NC_RPC_CANCEL */
char *persist_id;
char free;
};
struct nc_rpc_validate {
NC_RPC_TYPE type; /**< NC_RPC_VALIDATE */
NC_DATASTORE source;
char *url_config_src; /**< either URL (starts with alpha) or config (starts with '<') */
char free;
};
struct nc_rpc_getschema {
NC_RPC_TYPE type; /**< NC_RPC_GETSCHEMA */
char *identifier; /**< requested model identifier */
char *version; /**< either YANG version (1.0/1.1) or revision date */
char *format; /**< model format */
char free;
};
struct nc_rpc_subscribe {
NC_RPC_TYPE type; /**< NC_RPC_SUBSCRIBE */
char *stream; /**< stream name */
char *filter; /**< either XML subtree (starts with '<') or an XPath (starts with '/' or an alpha) */
char *start;
char *stop;
char free;
};
struct nc_rpc_getdata {
NC_RPC_TYPE type; /**< NC_RPC_GETDATA */
char *datastore; /**< target datastore identity */
char *filter; /**< either XML subtree (starts with '<') or an XPath (starts with '/' or an alpha) */
char *config_filter; /**< config filter ("true"/"false") */
char **origin_filter; /**< origin filters */
int origin_filter_count; /**< origin filter count */
int negated_origin_filter; /**< whether origin filter is negated or not */
int max_depth; /**< max depth of returned subtrees, 0 for unlimited */
int with_origin; /**< whether to return origin of data */
NC_WD_MODE wd_mode;
char free;
};
struct nc_rpc_editdata {
NC_RPC_TYPE type; /**< NC_RPC_EDITDATA */
char *datastore; /**< target datastore identity */
NC_RPC_EDIT_DFLTOP default_op;
char *edit_cont; /**< either URL (starts with aplha) or config (starts with '<') */
char free;
};
struct nc_rpc_establishsub {
NC_RPC_TYPE type; /**< NC_RPC_ESTABLISHSUB */
char *filter; /**< XML subtree (starts with '<'), an XPath (starts with '/'), or reference (start with alpha) */
char *stream; /**< stream name */
char *start;
char *stop;
char *encoding;
char free;
};
struct nc_rpc_modifysub {
NC_RPC_TYPE type; /**< NC_RPC_MODIFYSUB */
uint32_t id;
char *filter; /**< XML subtree (starts with '<'), an XPath (starts with '/'), or reference (start with alpha) */
char *stop;
char free;
};
struct nc_rpc_deletesub {
NC_RPC_TYPE type; /**< NC_RPC_DELETESUB */
uint32_t id;
};
struct nc_rpc_killsub {
NC_RPC_TYPE type; /**< NC_RPC_KILLSUB */
uint32_t id;
};
struct nc_rpc_establishpush {
NC_RPC_TYPE type; /**< NC_RPC_ESTABLISHPUSH */
char *datastore;
char *filter; /**< XML subtree (starts with '<'), an XPath (starts with '/'), or reference (start with alpha) */
char *stop;
char *encoding;
int periodic;
union {
struct {
uint32_t period;
char *anchor_time;
};
struct {
uint32_t dampening_period;
int sync_on_start;
char **excluded_change;
};
};
char free;
};
struct nc_rpc_modifypush {
NC_RPC_TYPE type; /**< NC_RPC_MODIFYPUSH */
uint32_t id;
char *datastore;
char *filter; /**< XML subtree (starts with '<'), an XPath (starts with '/'), or reference (start with alpha) */
char *stop;
int periodic;
union {
struct {
uint32_t period;
char *anchor_time;
};
uint32_t dampening_period;
};
char free;
};
struct nc_rpc_resyncsub {
NC_RPC_TYPE type; /**< NC_RPC_RESYNCSUB */
uint32_t id;
};
void nc_server_rpc_free(struct nc_server_rpc *rpc);
void nc_client_err_clean(struct nc_err *err, struct ly_ctx *ctx);
#endif /* NC_MESSAGES_P_H_ */

901
src/messages_server.c Normal file
View file

@ -0,0 +1,901 @@
/**
* \file messages_server.c
* \author Michal Vasko <mvasko@cesnet.cz>
* \brief libnetconf2 - server NETCONF messages functions
*
* 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
*/
#include <ctype.h>
#include <inttypes.h>
#include <stdarg.h>
#include <stdlib.h>
#include <string.h>
#include <libyang/libyang.h>
#include "compat.h"
#include "libnetconf.h"
#include "session_server.h"
extern struct nc_server_opts server_opts;
API struct nc_server_reply *
nc_server_reply_ok(void)
{
struct nc_server_reply *ret;
ret = malloc(sizeof *ret);
if (!ret) {
ERRMEM;
return NULL;
}
ret->type = NC_RPL_OK;
return ret;
}
API struct nc_server_reply *
nc_server_reply_data(struct lyd_node *data, NC_WD_MODE wd, NC_PARAMTYPE paramtype)
{
struct nc_server_reply_data *ret;
if (!data) {
ERRARG("data");
return NULL;
}
ret = malloc(sizeof *ret);
if (!ret) {
ERRMEM;
return NULL;
}
ret->type = NC_RPL_DATA;
ret->wd = wd;
if (paramtype == NC_PARAMTYPE_DUP_AND_FREE) {
if (lyd_dup_single(data, NULL, LYD_DUP_RECURSIVE, &ret->data)) {
free(ret);
return NULL;
}
} else {
ret->data = data;
}
if (paramtype != NC_PARAMTYPE_CONST) {
ret->free = 1;
} else {
ret->free = 0;
}
return (struct nc_server_reply *)ret;
}
API struct nc_server_reply *
nc_server_reply_err(struct lyd_node *err)
{
struct nc_server_reply_error *ret;
if (!err) {
ERRARG("err");
return NULL;
}
ret = malloc(sizeof *ret);
if (!ret) {
ERRMEM;
return NULL;
}
ret->type = NC_RPL_ERROR;
ret->err = err;
return (struct nc_server_reply *)ret;
}
API int
nc_server_reply_add_err(struct nc_server_reply *reply, struct lyd_node *err)
{
struct nc_server_reply_error *err_rpl;
if (!reply || (reply->type != NC_RPL_ERROR)) {
ERRARG("reply");
return -1;
} else if (!err) {
ERRARG("err");
return -1;
}
err_rpl = (struct nc_server_reply_error *)reply;
lyd_insert_sibling(err_rpl->err, err, &err_rpl->err);
return 0;
}
API const struct lyd_node *
nc_server_reply_get_last_err(const struct nc_server_reply *reply)
{
struct nc_server_reply_error *err_rpl;
if (!reply || (reply->type != NC_RPL_ERROR)) {
ERRARG("reply");
return NULL;
}
err_rpl = (struct nc_server_reply_error *)reply;
if (!err_rpl->err) {
return NULL;
}
return err_rpl->err->prev;
}
static const char *
nc_err_tag2str(NC_ERR tag)
{
switch (tag) {
case NC_ERR_IN_USE:
return "in-use";
case NC_ERR_INVALID_VALUE:
return "invalid-value";
case NC_ERR_ACCESS_DENIED:
return "access-denied";
case NC_ERR_ROLLBACK_FAILED:
return "rollback-failed";
case NC_ERR_OP_NOT_SUPPORTED:
return "operation-not-supported";
case NC_ERR_TOO_BIG:
return "too-big";
case NC_ERR_RES_DENIED:
return "resource-denied";
case NC_ERR_MISSING_ATTR:
return "missing-attribute";
case NC_ERR_BAD_ATTR:
return "bad-attribute";
case NC_ERR_UNKNOWN_ATTR:
return "unknown-attribute";
case NC_ERR_MISSING_ELEM:
return "missing-element";
case NC_ERR_BAD_ELEM:
return "bad-element";
case NC_ERR_UNKNOWN_ELEM:
return "unknown-element";
case NC_ERR_UNKNOWN_NS:
return "unknown-namespace";
case NC_ERR_LOCK_DENIED:
return "lock-denied";
case NC_ERR_DATA_EXISTS:
return "data-exists";
case NC_ERR_DATA_MISSING:
return "data-missing";
case NC_ERR_OP_FAILED:
return "operation-failed";
case NC_ERR_MALFORMED_MSG:
return "malformed-message";
default:
break;
}
return NULL;
}
static NC_ERR
nc_err_str2tag(const char *str)
{
if (!strcmp(str, "in-use")) {
return NC_ERR_IN_USE;
} else if (!strcmp(str, "invalid-value")) {
return NC_ERR_INVALID_VALUE;
} else if (!strcmp(str, "access-denied")) {
return NC_ERR_ACCESS_DENIED;
} else if (!strcmp(str, "rollback-failed")) {
return NC_ERR_ROLLBACK_FAILED;
} else if (!strcmp(str, "operation-not-supported")) {
return NC_ERR_OP_NOT_SUPPORTED;
} else if (!strcmp(str, "too-big")) {
return NC_ERR_TOO_BIG;
} else if (!strcmp(str, "resource-denied")) {
return NC_ERR_RES_DENIED;
} else if (!strcmp(str, "missing-attribute")) {
return NC_ERR_MISSING_ATTR;
} else if (!strcmp(str, "bad-attribute")) {
return NC_ERR_BAD_ATTR;
} else if (!strcmp(str, "unknown-attribute")) {
return NC_ERR_UNKNOWN_ATTR;
} else if (!strcmp(str, "missing-element")) {
return NC_ERR_MISSING_ELEM;
} else if (!strcmp(str, "bad-element")) {
return NC_ERR_BAD_ELEM;
} else if (!strcmp(str, "unknown-element")) {
return NC_ERR_UNKNOWN_ELEM;
} else if (!strcmp(str, "unknown-namespace")) {
return NC_ERR_UNKNOWN_NS;
} else if (!strcmp(str, "lock-denied")) {
return NC_ERR_LOCK_DENIED;
} else if (!strcmp(str, "data-exists")) {
return NC_ERR_DATA_EXISTS;
} else if (!strcmp(str, "data-missing")) {
return NC_ERR_DATA_MISSING;
} else if (!strcmp(str, "operation-failed")) {
return NC_ERR_OP_FAILED;
} else if (!strcmp(str, "malformed-message")) {
return NC_ERR_MALFORMED_MSG;
}
return 0;
}
static const char *
nc_err_type2str(NC_ERR_TYPE type)
{
switch (type) {
case NC_ERR_TYPE_TRAN:
return "transport";
case NC_ERR_TYPE_RPC:
return "rpc";
case NC_ERR_TYPE_PROT:
return "protocol";
case NC_ERR_TYPE_APP:
return "application";
default:
break;
}
return NULL;
}
static NC_ERR_TYPE
nc_err_str2type(const char *str)
{
if (!strcmp(str, "transport")) {
return NC_ERR_TYPE_TRAN;
} else if (!strcmp(str, "rpc")) {
return NC_ERR_TYPE_RPC;
} else if (!strcmp(str, "protocol")) {
return NC_ERR_TYPE_PROT;
} else if (!strcmp(str, "application")) {
return NC_ERR_TYPE_APP;
}
return 0;
}
API struct lyd_node *
nc_err(const struct ly_ctx *ctx, NC_ERR tag, ...)
{
va_list ap;
struct lyd_node *err = NULL;
NC_ERR_TYPE type;
const char *arg1, *arg2;
uint32_t sid;
if (!tag) {
ERRARG("tag");
return NULL;
}
/* rpc-error */
if (lyd_new_opaq2(NULL, ctx, "rpc-error", NULL, NULL, NC_NS_BASE, &err)) {
return NULL;
}
va_start(ap, tag);
/* error-type */
switch (tag) {
case NC_ERR_IN_USE:
case NC_ERR_INVALID_VALUE:
case NC_ERR_ACCESS_DENIED:
case NC_ERR_ROLLBACK_FAILED:
case NC_ERR_OP_NOT_SUPPORTED:
type = (NC_ERR_TYPE)va_arg(ap, int); /* NC_ERR_TYPE enum is automatically promoted to int */
if ((type != NC_ERR_TYPE_PROT) && (type != NC_ERR_TYPE_APP)) {
ERRARG("type");
goto fail;
}
break;
case NC_ERR_TOO_BIG:
case NC_ERR_RES_DENIED:
type = (NC_ERR_TYPE)va_arg(ap, int);
break;
case NC_ERR_MISSING_ATTR:
case NC_ERR_BAD_ATTR:
case NC_ERR_UNKNOWN_ATTR:
type = (NC_ERR_TYPE)va_arg(ap, int);
if (type == NC_ERR_TYPE_TRAN) {
ERRARG("type");
goto fail;
}
break;
case NC_ERR_MISSING_ELEM:
case NC_ERR_BAD_ELEM:
case NC_ERR_UNKNOWN_ELEM:
type = (NC_ERR_TYPE)va_arg(ap, int);
if ((type != NC_ERR_TYPE_PROT) && (type != NC_ERR_TYPE_APP)) {
ERRARG("type");
goto fail;
}
break;
case NC_ERR_UNKNOWN_NS:
type = (NC_ERR_TYPE)va_arg(ap, int);
if ((type != NC_ERR_TYPE_PROT) && (type != NC_ERR_TYPE_APP)) {
ERRARG("type");
goto fail;
}
break;
case NC_ERR_LOCK_DENIED:
type = NC_ERR_TYPE_PROT;
break;
case NC_ERR_DATA_EXISTS:
case NC_ERR_DATA_MISSING:
type = NC_ERR_TYPE_APP;
break;
case NC_ERR_OP_FAILED:
type = (NC_ERR_TYPE)va_arg(ap, int);
if (type == NC_ERR_TYPE_TRAN) {
ERRARG("type");
goto fail;
}
break;
case NC_ERR_MALFORMED_MSG:
type = NC_ERR_TYPE_RPC;
break;
default:
ERRARG("tag");
goto fail;
}
if (lyd_new_opaq2(err, NULL, "error-type", nc_err_type2str(type), NULL, NC_NS_BASE, NULL)) {
goto fail;
}
/* error-tag */
if (lyd_new_opaq2(err, NULL, "error-tag", nc_err_tag2str(tag), NULL, NC_NS_BASE, NULL)) {
goto fail;
}
/* error-severity */
if (lyd_new_opaq2(err, NULL, "error-severity", "error", NULL, NC_NS_BASE, NULL)) {
goto fail;
}
/* error-message */
switch (tag) {
case NC_ERR_IN_USE:
nc_err_set_msg(err, "The request requires a resource that already is in use.", "en");
break;
case NC_ERR_INVALID_VALUE:
nc_err_set_msg(err, "The request specifies an unacceptable value for one or more parameters.", "en");
break;
case NC_ERR_TOO_BIG:
nc_err_set_msg(err, "The request or response (that would be generated) is too large for the implementation to handle.", "en");
break;
case NC_ERR_MISSING_ATTR:
nc_err_set_msg(err, "An expected attribute is missing.", "en");
break;
case NC_ERR_BAD_ATTR:
nc_err_set_msg(err, "An attribute value is not correct.", "en");
break;
case NC_ERR_UNKNOWN_ATTR:
nc_err_set_msg(err, "An unexpected attribute is present.", "en");
break;
case NC_ERR_MISSING_ELEM:
nc_err_set_msg(err, "An expected element is missing.", "en");
break;
case NC_ERR_BAD_ELEM:
nc_err_set_msg(err, "An element value is not correct.", "en");
break;
case NC_ERR_UNKNOWN_ELEM:
nc_err_set_msg(err, "An unexpected element is present.", "en");
break;
case NC_ERR_UNKNOWN_NS:
nc_err_set_msg(err, "An unexpected namespace is present.", "en");
break;
case NC_ERR_ACCESS_DENIED:
nc_err_set_msg(err, "Access to the requested protocol operation or data model is denied because authorization failed.", "en");
break;
case NC_ERR_LOCK_DENIED:
nc_err_set_msg(err, "Access to the requested lock is denied because the lock is currently held by another entity.", "en");
break;
case NC_ERR_RES_DENIED:
nc_err_set_msg(err, "Request could not be completed because of insufficient resources.", "en");
break;
case NC_ERR_ROLLBACK_FAILED:
nc_err_set_msg(err, "Request to roll back some configuration change was not completed for some reason.", "en");
break;
case NC_ERR_DATA_EXISTS:
nc_err_set_msg(err, "Request could not be completed because the relevant data model content already exists.", "en");
break;
case NC_ERR_DATA_MISSING:
nc_err_set_msg(err, "Request could not be completed because the relevant data model content does not exist.", "en");
break;
case NC_ERR_OP_NOT_SUPPORTED:
nc_err_set_msg(err, "Request could not be completed because the requested operation is not supported by this implementation.", "en");
break;
case NC_ERR_OP_FAILED:
nc_err_set_msg(err, "Request could not be completed because the requested operation failed for a non-specific reason.", "en");
break;
case NC_ERR_MALFORMED_MSG:
nc_err_set_msg(err, "A message could not be handled because it failed to be parsed correctly.", "en");
break;
default:
ERRARG("tag");
goto fail;
}
/* error-info */
switch (tag) {
case NC_ERR_IN_USE:
case NC_ERR_INVALID_VALUE:
case NC_ERR_ACCESS_DENIED:
case NC_ERR_ROLLBACK_FAILED:
case NC_ERR_OP_NOT_SUPPORTED:
case NC_ERR_TOO_BIG:
case NC_ERR_RES_DENIED:
case NC_ERR_DATA_EXISTS:
case NC_ERR_DATA_MISSING:
case NC_ERR_OP_FAILED:
case NC_ERR_MALFORMED_MSG:
break;
case NC_ERR_MISSING_ATTR:
case NC_ERR_BAD_ATTR:
case NC_ERR_UNKNOWN_ATTR:
arg1 = va_arg(ap, const char *);
arg2 = va_arg(ap, const char *);
nc_err_add_bad_attr(err, arg1);
nc_err_add_bad_elem(err, arg2);
break;
case NC_ERR_MISSING_ELEM:
case NC_ERR_BAD_ELEM:
case NC_ERR_UNKNOWN_ELEM:
arg1 = va_arg(ap, const char *);
nc_err_add_bad_elem(err, arg1);
break;
case NC_ERR_UNKNOWN_NS:
arg1 = va_arg(ap, const char *);
arg2 = va_arg(ap, const char *);
nc_err_add_bad_elem(err, arg1);
nc_err_add_bad_ns(err, arg2);
break;
case NC_ERR_LOCK_DENIED:
sid = va_arg(ap, uint32_t);
nc_err_set_sid(err, sid);
break;
default:
ERRARG("tag");
goto fail;
}
va_end(ap);
return err;
fail:
va_end(ap);
lyd_free_siblings(err);
return NULL;
}
API NC_ERR_TYPE
nc_err_get_type(const struct lyd_node *err)
{
struct lyd_node *match;
if (!err) {
ERRARG("err");
return 0;
}
lyd_find_sibling_opaq_next(lyd_child(err), "error-type", &match);
if (match) {
return nc_err_str2type(((struct lyd_node_opaq *)match)->value);
}
return 0;
}
API NC_ERR
nc_err_get_tag(const struct lyd_node *err)
{
struct lyd_node *match;
if (!err) {
ERRARG("err");
return 0;
}
lyd_find_sibling_opaq_next(lyd_child(err), "error-tag", &match);
if (match) {
return nc_err_str2tag(((struct lyd_node_opaq *)match)->value);
}
return 0;
}
API int
nc_err_set_app_tag(struct lyd_node *err, const char *error_app_tag)
{
struct lyd_node *match;
if (!err) {
ERRARG("err");
return -1;
} else if (!error_app_tag) {
ERRARG("error_app_tag");
return -1;
}
/* remove previous node */
lyd_find_sibling_opaq_next(lyd_child(err), "error-app-tag", &match);
if (match) {
lyd_free_tree(match);
}
if (lyd_new_opaq2(err, NULL, "error-app-tag", error_app_tag, NULL, NC_NS_BASE, NULL)) {
return -1;
}
return 0;
}
API const char *
nc_err_get_app_tag(const struct lyd_node *err)
{
struct lyd_node *match;
if (!err) {
ERRARG("err");
return NULL;
}
lyd_find_sibling_opaq_next(lyd_child(err), "error-app-tag", &match);
if (match) {
return ((struct lyd_node_opaq *)match)->value;
}
return NULL;
}
API int
nc_err_set_path(struct lyd_node *err, const char *error_path)
{
struct lyd_node *match;
if (!err) {
ERRARG("err");
return -1;
} else if (!error_path) {
ERRARG("error_path");
return -1;
}
/* remove previous node */
lyd_find_sibling_opaq_next(lyd_child(err), "error-path", &match);
if (match) {
lyd_free_tree(match);
}
if (lyd_new_opaq2(err, NULL, "error-path", error_path, NULL, NC_NS_BASE, NULL)) {
return -1;
}
return 0;
}
API const char *
nc_err_get_path(const struct lyd_node *err)
{
struct lyd_node *match;
if (!err) {
ERRARG("err");
return 0;
}
lyd_find_sibling_opaq_next(lyd_child(err), "error-path", &match);
if (match) {
return ((struct lyd_node_opaq *)match)->value;
}
return NULL;
}
API int
nc_err_set_msg(struct lyd_node *err, const char *error_message, const char *lang)
{
struct lyd_node *match;
struct lyd_attr *attr;
if (!err) {
ERRARG("err");
return -1;
} else if (!error_message) {
ERRARG("error_message");
return -1;
}
/* remove previous message */
lyd_find_sibling_opaq_next(lyd_child(err), "error-message", &match);
if (match) {
lyd_free_tree(match);
}
if (lyd_new_opaq2(err, NULL, "error-message", error_message, NULL, NC_NS_BASE, &match)) {
return -1;
}
if (lang && lyd_new_attr(match, NULL, "xml:lang", lang, &attr)) {
lyd_free_tree(match);
return -1;
}
return 0;
}
API const char *
nc_err_get_msg(const struct lyd_node *err)
{
struct lyd_node *match;
if (!err) {
ERRARG("err");
return NULL;
}
lyd_find_sibling_opaq_next(lyd_child(err), "error-message", &match);
if (match) {
return ((struct lyd_node_opaq *)match)->value;
}
return NULL;
}
API int
nc_err_set_sid(struct lyd_node *err, uint32_t session_id)
{
struct lyd_node *match, *info;
char buf[22];
if (!err) {
ERRARG("err");
return -1;
}
/* find error-info */
lyd_find_sibling_opaq_next(lyd_child(err), "error-info", &info);
if (!info && lyd_new_opaq2(err, NULL, "error-info", NULL, NULL, NC_NS_BASE, &info)) {
return -1;
}
/* remove previous node */
lyd_find_sibling_opaq_next(lyd_child(info), "session-id", &match);
if (match) {
lyd_free_tree(match);
}
sprintf(buf, "%" PRIu32, session_id);
if (lyd_new_opaq2(info, NULL, "session-id", buf, NULL, NC_NS_BASE, NULL)) {
return -1;
}
return 0;
}
API int
nc_err_add_bad_attr(struct lyd_node *err, const char *attr_name)
{
struct lyd_node *info;
if (!err) {
ERRARG("err");
return -1;
} else if (!attr_name) {
ERRARG("attr_name");
return -1;
}
/* find error-info */
lyd_find_sibling_opaq_next(lyd_child(err), "error-info", &info);
if (!info && lyd_new_opaq2(err, NULL, "error-info", NULL, NULL, NC_NS_BASE, &info)) {
return -1;
}
if (lyd_new_opaq2(info, NULL, "bad-attribute", attr_name, NULL, NC_NS_BASE, NULL)) {
return -1;
}
return 0;
}
API int
nc_err_add_bad_elem(struct lyd_node *err, const char *elem_name)
{
struct lyd_node *info;
if (!err) {
ERRARG("err");
return -1;
} else if (!elem_name) {
ERRARG("elem_name");
return -1;
}
/* find error-info */
lyd_find_sibling_opaq_next(lyd_child(err), "error-info", &info);
if (!info && lyd_new_opaq2(err, NULL, "error-info", NULL, NULL, NC_NS_BASE, &info)) {
return -1;
}
if (lyd_new_opaq2(info, NULL, "bad-element", elem_name, NULL, NC_NS_BASE, NULL)) {
return -1;
}
return 0;
}
API int
nc_err_add_bad_ns(struct lyd_node *err, const char *ns_name)
{
struct lyd_node *info;
if (!err) {
ERRARG("err");
return -1;
} else if (!ns_name) {
ERRARG("ns_name");
return -1;
}
/* find error-info */
lyd_find_sibling_opaq_next(lyd_child(err), "error-info", &info);
if (!info && lyd_new_opaq2(err, NULL, "error-info", NULL, NULL, NC_NS_BASE, &info)) {
return -1;
}
if (lyd_new_opaq2(info, NULL, "bad-namespace", ns_name, NULL, NC_NS_BASE, NULL)) {
return -1;
}
return 0;
}
API int
nc_err_add_info_other(struct lyd_node *err, struct lyd_node *other)
{
struct lyd_node *info;
if (!err) {
ERRARG("err");
return -1;
} else if (!other) {
ERRARG("other");
return -1;
}
/* find error-info */
lyd_find_sibling_opaq_next(lyd_child(err), "error-info", &info);
if (!info && lyd_new_opaq2(err, NULL, "error-info", NULL, NULL, NC_NS_BASE, &info)) {
return -1;
}
lyd_insert_child(info, other);
return 0;
}
void
nc_server_rpc_free(struct nc_server_rpc *rpc)
{
if (!rpc) {
return;
}
lyd_free_tree(rpc->envp);
/* may be action */
lyd_free_all(rpc->rpc);
free(rpc);
}
API void
nc_server_reply_free(struct nc_server_reply *reply)
{
struct nc_server_reply_data *data_rpl;
struct nc_server_reply_error *error_rpl;
if (!reply) {
return;
}
switch (reply->type) {
case NC_RPL_DATA:
data_rpl = (struct nc_server_reply_data *)reply;
if (data_rpl->free) {
lyd_free_siblings(data_rpl->data);
}
break;
case NC_RPL_OK:
/* nothing to free */
break;
case NC_RPL_ERROR:
error_rpl = (struct nc_server_reply_error *)reply;
lyd_free_siblings(error_rpl->err);
break;
default:
break;
}
free(reply);
}
API struct nc_server_notif *
nc_server_notif_new(struct lyd_node *event, char *eventtime, NC_PARAMTYPE paramtype)
{
struct nc_server_notif *ntf;
struct lyd_node *elem;
int found;
if (!event) {
ERRARG("event");
return NULL;
} else if (!eventtime) {
ERRARG("eventtime");
return NULL;
}
/* check that there is a notification */
found = 0;
LYD_TREE_DFS_BEGIN(event, elem) {
if (elem->schema->nodetype == LYS_NOTIF) {
found = 1;
break;
}
LYD_TREE_DFS_END(event, elem);
}
if (!found) {
ERRARG("event");
return NULL;
}
ntf = malloc(sizeof *ntf);
if (paramtype == NC_PARAMTYPE_DUP_AND_FREE) {
ntf->eventtime = strdup(eventtime);
if (lyd_dup_single(event, NULL, LYD_DUP_RECURSIVE, &ntf->ntf)) {
free(ntf);
return NULL;
}
} else {
ntf->eventtime = eventtime;
ntf->ntf = event;
}
ntf->free = (paramtype == NC_PARAMTYPE_CONST ? 0 : 1);
return ntf;
}
API void
nc_server_notif_free(struct nc_server_notif *notif)
{
if (!notif) {
return;
}
if (notif->free) {
lyd_free_tree(notif->ntf);
free(notif->eventtime);
}
free(notif);
}
API const char *
nc_server_notif_get_time(const struct nc_server_notif *notif)
{
if (!notif) {
ERRARG("notif");
return NULL;
}
return notif->eventtime;
}

340
src/messages_server.h Normal file
View file

@ -0,0 +1,340 @@
/**
* @file messages_server.h
* @author Michal Vasko <mvasko@cesnet.cz>
* @brief libnetconf2's functions and structures of server NETCONF messages.
*
* Copyright (c) 2015-2021 CESNET, z.s.p.o.
*
* This source code is licensed under BSD 3-Clause License (the "License").
* You may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://opensource.org/licenses/BSD-3-Clause
*/
#ifndef NC_MESSAGES_SERVER_H_
#define NC_MESSAGES_SERVER_H_
#ifdef __cplusplus
extern "C" {
#endif
#include <libyang/libyang.h>
#include <stdint.h>
#include "netconf.h"
#include "session.h"
/**
* @defgroup server_msg Server Messages
* @ingroup server
*
* @brief Functions to create NETCONF Event notifications and replies to the NETCONF RPCs (or actions).
* @{
*/
/**
* @brief Enumeration of NETCONF errors
*/
typedef enum NC_ERROR {
NC_ERR_UNKNOWN = 0, /**< unknown error */
NC_ERR_IN_USE, /**< in-use error */
NC_ERR_INVALID_VALUE, /**< invalid-value error */
NC_ERR_TOO_BIG, /**< too-big error */
NC_ERR_MISSING_ATTR, /**< missing-attribute error */
NC_ERR_BAD_ATTR, /**< bad-attribute error */
NC_ERR_UNKNOWN_ATTR, /**< unknown-attribute error */
NC_ERR_MISSING_ELEM, /**< missing-element error */
NC_ERR_BAD_ELEM, /**< bad-element error */
NC_ERR_UNKNOWN_ELEM, /**< unknown-element error */
NC_ERR_UNKNOWN_NS, /**< unknown-namespace error */
NC_ERR_ACCESS_DENIED, /**< access-denied error */
NC_ERR_LOCK_DENIED, /**< lock-denied error */
NC_ERR_RES_DENIED, /**< resource-denied error */
NC_ERR_ROLLBACK_FAILED, /**< rollback-failed error */
NC_ERR_DATA_EXISTS, /**< data-exists error */
NC_ERR_DATA_MISSING, /**< data-missing error */
NC_ERR_OP_NOT_SUPPORTED, /**< operation-not-supported error */
NC_ERR_OP_FAILED, /**< operation-failed error */
NC_ERR_MALFORMED_MSG /**< malformed-message error */
} NC_ERR;
/**
* @brief Enumeration of NETCONF error type (layer)
*/
typedef enum NC_ERROR_TYPE {
NC_ERR_TYPE_UNKNOWN = 0, /**< unknown layer */
NC_ERR_TYPE_TRAN, /**< transport layer */
NC_ERR_TYPE_RPC, /**< RPC layer */
NC_ERR_TYPE_PROT, /**< protocol layer */
NC_ERR_TYPE_APP /**< application layer */
} NC_ERR_TYPE;
/**
* @brief NETCONF server rpc-reply object
*/
struct nc_server_reply;
/**
* @brief NETCONF server Event Notification object
*/
struct nc_server_notif;
/**
* @brief NETCONF server error structure
*/
struct nc_server_error;
/**
* @brief Create an OK rpc-reply object.
*
* @return rpc-reply object, NULL on error.
*/
struct nc_server_reply *nc_server_reply_ok(void);
/**
* @brief Create a DATA rpc-reply object.
*
* @param[in] data Reply data tree. This tree must be valid according to
* the RPC output of the RPC this is a reply to.
* @param[in] wd with-default mode if applicable
* @param[in] paramtype Determines how the @p data parameter is treated.
* @return rpc-reply object, NULL on error.
*/
struct nc_server_reply *nc_server_reply_data(struct lyd_node *data, NC_WD_MODE wd, NC_PARAMTYPE paramtype);
/**
* @brief Create an ERROR rpc-reply object.
*
* @param[in] err Errors as opaque data node tree. It will be freed with the returned object.
* @return rpc-reply object, NULL on error.
*/
struct nc_server_reply *nc_server_reply_err(struct lyd_node *err);
/**
* @brief Add another error opaque data node tree to an ERROR rpc-reply object.
*
* @param[in] reply ERROR reply to add to.
* @param[in] err Error as opaque data node tree. It will be freed with the returned object.
* @return 0 on success, -1 on errror.
*/
int nc_server_reply_add_err(struct nc_server_reply *reply, struct lyd_node *err);
/**
* @brief Get last error from an ERROR rpc-reply object.
*
* @param[in] reply ERROR reply to read from.
* @return Last error opaque data tree, NULL on failure.
*/
const struct lyd_node *nc_server_reply_get_last_err(const struct nc_server_reply *reply);
/**
* @brief Create a server error structure. Its \<error-message\> is filled with
* a general description of the specific error.
*
* @param[in] ctx libyang context to use.
* @param[in] tag \<error-tag\> of the server error specified as #NC_ERR value. According to the tag, the
* specific additional parameters are required:
* - #NC_ERR_IN_USE
* - #NC_ERR_INVALID_VALUE
* - #NC_ERR_ACCESS_DENIED
* - #NC_ERR_ROLLBACK_FAILED
* - #NC_ERR_OP_NOT_SUPPORTED
* - #NC_ERR_TOO_BIG
* - #NC_ERR_RES_DENIED
* - #NC_ERR_OP_FAILED
* - `NC_ERR_TYPE type;` - type (layer) of the error.
* - #NC_ERR_MISSING_ATTR
* - #NC_ERR_BAD_ATTR
* - #NC_ERR_UNKNOWN_ATTR
* - `NC_ERR_TYPE type;` - type (layer) of the error.
* - `const char *attr_name;` - error \<bad-attribute\> value.
* - `const char *elem_name;` - error \<bad-element\> value.
* - #NC_ERR_MISSING_ELEM
* - #NC_ERR_BAD_ELEM
* - #NC_ERR_UNKNOWN_ELEM
* - `NC_ERR_TYPE type;` - type (layer) of the error.
* - `const char *elem_name;` - error \<bad-element\> value.
* - #NC_ERR_UNKNOWN_NS
* - `NC_ERR_TYPE type;` - type (layer) of the error.
* - `const char *elem_name;` - error \<bad-element\> value.
* - `const char *nc_name;` - error \<bad-namespace\> value.
* - #NC_ERR_LOCK_DENIED
* - `uint32_t session_id;` - error \<session-id\> value.
* - #NC_ERR_DATA_EXISTS
* - #NC_ERR_DATA_MISSING
* - #NC_ERR_MALFORMED_MSG
* - no additional arguments
* @return Opaque data node tree representing the error.
*/
struct lyd_node *nc_err(const struct ly_ctx *ctx, NC_ERR tag, ...);
/**
* @brief Get the \<error-type\> of a server error.
*
* @param[in] err Error opaque data node tree to read from.
* @return Server error type, 0 on error.
*/
NC_ERR_TYPE nc_err_get_type(const struct lyd_node *err);
/**
* @brief Get the \<error-tag\> of a server error.
*
* @param[in] err Error opaque data node tree to read from.
* @return Server error tag, 0 on error.
*/
NC_ERR nc_err_get_tag(const struct lyd_node *err);
/**
* @brief Set the \<error-app-tag\> element of an error. Any previous value will be overwritten.
*
* @param[in] err Error opaque data node tree to modify.
* @param[in] error_app_tag New value of \<error-app-tag\>.
* @return 0 on success, -1 on error.
*/
int nc_err_set_app_tag(struct lyd_node *err, const char *error_app_tag);
/**
* @brief Get the \<error-app-tag\> of a server error.
*
* @param[in] err Error opaque data node tree to read from.
* @return Server error app tag, NULL on error.
*/
const char *nc_err_get_app_tag(const struct lyd_node *err);
/**
* @brief Set the \<error-path\> element of an error. Any previous value will be overwritten.
*
* @param[in] err Error opaque data node tree to modify.
* @param[in] error_path New value of \<error-path\>.
* @return 0 on success, -1 on error.
*/
int nc_err_set_path(struct lyd_node *err, const char *error_path);
/**
* @brief Get the \<error-path\> of a server error.
*
* @param[in] err Error opaque data node tree to read from.
* @return Server error path, NULL on error.
*/
const char *nc_err_get_path(const struct lyd_node *err);
/**
* @brief Set the \<error-message\> element of an error. Any previous value will be overwritten.
*
* @param[in] err Error opaque data node tree to modify.
* @param[in] error_message New value of \<error-message\>.
* @param[in] lang Optional language of @p error_message.
* @return 0 on success, -1 on error.
*/
int nc_err_set_msg(struct lyd_node *err, const char *error_message, const char *lang);
/**
* @brief Get the \<error-message\> of a server error.
*
* @param[in] err Error opaque data node tree to read from.
* @return Server error message, NULL on error.
*/
const char *nc_err_get_msg(const struct lyd_node *err);
/**
* @brief Set the \<session-id\> element of an error. Any previous value will be overwritten.
*
* @param[in] err Error opaque data node tree to modify.
* @param[in] session_id New value of \<session-id\>.
* @return 0 on success, -1 on error.
*/
int nc_err_set_sid(struct lyd_node *err, uint32_t session_id);
/**
* @brief Add a \<bad-attribute\> element to an error.
*
* @param[in] err Error opaque data node tree to modify.
* @param[in] attr_name Value of the new \<bad-attribute\> element.
* @return 0 on success, -1 on error.
*/
int nc_err_add_bad_attr(struct lyd_node *err, const char *attr_name);
/**
* @brief Add a \<bad-element\> element to an error.
*
* @param[in] err Error opaque data node tree to modify.
* @param[in] elem_name Value of the new \<bad-element\> element.
* @return 0 on success, -1 on error.
*/
int nc_err_add_bad_elem(struct lyd_node *err, const char *elem_name);
/**
* @brief Add a \<bad-namespace\> element to an error.
*
* @param[in] err Error opaque data node tree to modify.
* @param[in] ns_name Value of the new \<bad-namespace\> element.
* @return 0 on success, -1 on error.
*/
int nc_err_add_bad_ns(struct lyd_node *err, const char *ns_name);
/**
* @brief Add an additional custom element to an error.
*
* @param[in] err Error opaque data node tree to modify.
* @param[in] other Other error opaque data node tree.
* @return 0 on success, -1 on error.
*/
int nc_err_add_info_other(struct lyd_node *err, struct lyd_node *other);
/**
* @brief Free a server rpc-reply object.
*
* @param[in] reply Server rpc-reply object to free.
*/
void nc_server_reply_free(struct nc_server_reply *reply);
/**
* @brief Create Event Notification object to be sent to the subscribed client(s).
*
* @param[in] event Notification data tree (valid as LYD_OPT_NOTIF) from libyang. The tree is directly used in created
* object, so the caller is supposed to not free the tree on its own, but only via freeng the created object.
* @param[in] eventtime YANG dateTime format value of the time when the event was generated by the event source.
* Caller can use nc_timespec2datetime() to create the value from a timespec value.
* @param[in] paramtype How to further manage data parameters.
* @return Newly created structure of the Event Notification object to be sent to the clients via nc_server_send_notif()
* and freed using nc_server_notif_free().
*/
struct nc_server_notif *nc_server_notif_new(struct lyd_node *event, char *eventtime, NC_PARAMTYPE paramtype);
/**
* @brief Send NETCONF Event Notification via the session.
*
* @param[in] session NETCONF session where the Event Notification will be written.
* @param[in] notif NETCOFN Notification object to send via specified session. Object can be created by
* nc_notif_new() function.
* @param[in] timeout Timeout for writing in milliseconds. Use negative value for infinite
* waiting and 0 for return if data cannot be sent immediately.
* @return #NC_MSG_NOTIF on success,
* #NC_MSG_WOULDBLOCK in case of a busy session, and
* #NC_MSG_ERROR on error.
*/
NC_MSG_TYPE nc_server_notif_send(struct nc_session *session, struct nc_server_notif *notif, int timeout);
/**
* @brief Free a server Event Notification object.
*
* @param[in] notif Server Event Notification object to free.
*/
void nc_server_notif_free(struct nc_server_notif *notif);
/**
* @brief Get the notification timestamp.
*
* @param[in] notif Server notification to read from.
* @return Datetime timestamp of the notification, NULL on error.
*/
const char *nc_server_notif_get_time(const struct nc_server_notif *notif);
/** @} Client Messages */
#ifdef __cplusplus
}
#endif
#endif /* NC_MESSAGES_SERVER_H_ */

134
src/netconf.h Normal file
View file

@ -0,0 +1,134 @@
/**
* @file netconf.h
* @author Radek Krejci <rkrejci@cesnet.cz>
* @brief libnetconf2's general public functions and structures definitions.
*
* Copyright (c) 2015 - 2021 CESNET, z.s.p.o.
*
* This source code is licensed under BSD 3-Clause License (the "License").
* You may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://opensource.org/licenses/BSD-3-Clause
*/
#ifndef NC_NETCONF_H_
#define NC_NETCONF_H_
#ifdef __cplusplus
extern "C" {
#endif
#include <time.h>
/**
* @addtogroup misc
* @{
*/
/** @brief Base NETCONF namespace */
#define NC_NS_BASE "urn:ietf:params:xml:ns:netconf:base:1.0"
/** @brief Notifications namespace */
#define NC_NS_NOTIF "urn:ietf:params:xml:ns:netconf:notification:1.0"
/** @brief Default NETCONF over SSH port */
#define NC_PORT_SSH 830
/** @brief Default NETCONF over SSH Call Home port */
#define NC_PORT_CH_SSH 4334
/** @brief Default NETCONF over TLS port */
#define NC_PORT_TLS 6513
/** @brief Default NETCONF over TLS Call Home port */
#define NC_PORT_CH_TLS 4335
/**
* @brief Set RPC callback to a schema node.
*
* @param[in] node const struct lysc_node *node
* @param[in] cb nc_rpc_clb cb
*/
#define nc_set_rpc_callback(node, cb) (node->priv = cb)
/**
* @brief Enumeration of reasons of the NETCONF session termination as defined in RFC 6470.
*/
typedef enum NC_SESSION_TERM_REASON {
NC_SESSION_TERM_ERR = -1, /**< error return code for function getting the session termination reason */
NC_SESSION_TERM_NONE = 0, /**< session still running */
NC_SESSION_TERM_CLOSED, /**< closed by client in a normal fashion */
NC_SESSION_TERM_KILLED, /**< session was terminated by \<kill-session\> operation */
NC_SESSION_TERM_DROPPED, /**< transport layer connection was unexpectedly closed */
NC_SESSION_TERM_TIMEOUT, /**< terminated because of inactivity */
NC_SESSION_TERM_BADHELLO, /**< \<hello\> message was invalid */
NC_SESSION_TERM_OTHER /**< terminated for some other reason */
} NC_SESSION_TERM_REASON;
/**
* @brief Enumeration of NETCONF message types.
*/
typedef enum NC_MSG_TYPE {
NC_MSG_ERROR, /**< error return value */
NC_MSG_WOULDBLOCK, /**< timeout return value */
NC_MSG_NONE, /**< no message at input or message was processed internally */
NC_MSG_HELLO, /**< \<hello\> message */
NC_MSG_BAD_HELLO, /**< \<hello\> message parsing failed */
NC_MSG_RPC, /**< \<rpc\> message */
NC_MSG_REPLY, /**< \<rpc-reply\> message */
NC_MSG_REPLY_ERR_MSGID, /**< \<rpc-reply\> message with missing or wrong message-id attribute value */
NC_MSG_NOTIF /**< \<notification\> message */
} NC_MSG_TYPE;
/**
* @brief Messages of NETCONF message type enum.
*/
extern const char *nc_msgtype2str[];
/**
* @brief Enumeration of the supported types of datastores defined by NETCONF
*/
typedef enum NC_DATASTORE_TYPE {
NC_DATASTORE_ERROR = 0, /**< error state of functions returning the datastore type */
NC_DATASTORE_CONFIG, /**< value describing that the datastore is set as config */
NC_DATASTORE_URL, /**< value describing that the datastore data should be given from the URL */
NC_DATASTORE_RUNNING, /**< base NETCONF's datastore containing the current device configuration */
NC_DATASTORE_STARTUP, /**< separated startup datastore as defined in Distinct Startup Capability */
NC_DATASTORE_CANDIDATE /**< separated working datastore as defined in Candidate Configuration Capability */
} NC_DATASTORE;
/**
* @brief Enumeration of NETCONF with-defaults capability modes.
*/
typedef enum NC_WITHDEFAULTS_MODE {
NC_WD_UNKNOWN = 0, /**< invalid mode */
NC_WD_ALL, /**< report-all mode */
NC_WD_ALL_TAG, /**< report-all-tagged mode */
NC_WD_TRIM, /**< trim mode */
NC_WD_EXPLICIT /**< explicit mode */
} NC_WD_MODE;
/**
* @brief Enumeration of NETCONF (both server and client) rpc-reply types.
*/
typedef enum NC_REPLY {
NC_RPL_OK, /**< OK rpc-reply */
NC_RPL_DATA, /**< DATA rpc-reply */
NC_RPL_ERROR, /**< ERROR rpc-reply */
NC_RPL_NOTIF /**< notification (client-only) */
} NC_RPL;
/**
* @brief Enumeration of function parameter treatments.
*/
typedef enum NC_PARAMTYPE {
NC_PARAMTYPE_CONST, /**< use the parameter directly, do not free */
NC_PARAMTYPE_FREE, /**< use the parameter directly, free afterwards */
NC_PARAMTYPE_DUP_AND_FREE /**< make a copy of the argument, free afterwards */
} NC_PARAMTYPE;
/** @} Miscellaneous */
#ifdef __cplusplus
}
#endif
#endif /* NC_NETCONF_H_ */

1694
src/session.c Normal file

File diff suppressed because it is too large Load diff

244
src/session.h Normal file
View file

@ -0,0 +1,244 @@
/**
* \file session.h
* \author Radek Krejci <rkrejci@cesnet.cz>
* \brief libnetconf2 session manipulation
*
* 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 NC_SESSION_H_
#define NC_SESSION_H_
#ifdef __cplusplus
extern "C" {
#endif
#include "netconf.h"
#ifdef NC_ENABLED_SSH
/**
* @brief Enumeration of NETCONF SSH authentication methods
*/
typedef enum {
NC_SSH_AUTH_PUBLICKEY = 0x01, /**< publickey SSH authentication */
NC_SSH_AUTH_PASSWORD = 0x02, /**< password SSH authentication */
NC_SSH_AUTH_INTERACTIVE = 0x04 /**< interactive SSH authentication */
} NC_SSH_AUTH_TYPE;
#endif /* NC_ENABLED_SSH */
#ifdef NC_ENABLED_TLS
/**
* @brief Enumeration of cert-to-name mapping types
*/
typedef enum {
NC_TLS_CTN_UNKNOWN = 0, /**< unknown mapping */
NC_TLS_CTN_SPECIFIED, /**< username explicitly specified */
NC_TLS_CTN_SAN_RFC822_NAME, /**< email address as username */
NC_TLS_CTN_SAN_DNS_NAME, /**< DNS name as username */
NC_TLS_CTN_SAN_IP_ADDRESS, /**< IP address as username */
NC_TLS_CTN_SAN_ANY, /**< any certificate Subject Alternative Name as username */
NC_TLS_CTN_COMMON_NAME /**< common name as username */
} NC_TLS_CTN_MAPTYPE;
#endif /* NC_ENABLED_TLS */
/**
* @brief Enumeration of possible session statuses
*/
typedef enum {
NC_STATUS_ERR = -1, /**< error return code for function getting the session status */
NC_STATUS_STARTING = 0, /**< session is not yet fully initiated */
NC_STATUS_CLOSING, /**< session is being closed */
NC_STATUS_INVALID, /**< session is not running and is supposed to be closed (nc_session_free()) */
NC_STATUS_RUNNING /**< up and running */
} NC_STATUS;
/**
* @brief Enumeration of transport implementations (ways how libnetconf implements NETCONF transport protocol)
*/
typedef enum {
NC_TI_NONE = 0, /**< none - session is not connected yet */
NC_TI_FD, /**< file descriptors - use standard input/output, transport protocol is implemented
outside the current application */
NC_TI_UNIX, /**< unix socket */
#ifdef NC_ENABLED_SSH
NC_TI_LIBSSH, /**< libssh - use libssh library, only for NETCONF over SSH transport */
#endif
#ifdef NC_ENABLED_TLS
NC_TI_OPENSSL /**< OpenSSL - use OpenSSL library, only for NETCONF over TLS transport */
#endif
} NC_TRANSPORT_IMPL;
/**
* @brief Enumeration of Call Home connection types.
*/
typedef enum {
NC_CH_CT_NOT_SET = 0,
NC_CH_PERSIST,
NC_CH_PERIOD
} NC_CH_CONN_TYPE;
/**
* @brief Enumeration of Call Home client priority policy.
*/
typedef enum {
NC_CH_FIRST_LISTED = 0, // default
NC_CH_LAST_CONNECTED,
NC_CH_RANDOM
} NC_CH_START_WITH;
/**
* @brief Enumeration of SSH key types.
*/
typedef enum {
NC_SSH_KEY_UNKNOWN = 0,
NC_SSH_KEY_DSA,
NC_SSH_KEY_RSA,
NC_SSH_KEY_ECDSA
} NC_SSH_KEY_TYPE;
/**
* @brief NETCONF session object
*/
struct nc_session;
/**
* @brief Get session status.
*
* @param[in] session Session to get the information from.
* @return Session status.
*/
NC_STATUS nc_session_get_status(const struct nc_session *session);
/**
* @brief Get session termination reason.
*
* @param[in] session Session to get the information from.
* @return Session termination reason enum value.
*/
NC_SESSION_TERM_REASON nc_session_get_term_reason(const struct nc_session *session);
/**
* @brief Get session killer session ID.
*
* @param[in] session Session to get the information from.
* @return Session killer ID.
*/
uint32_t nc_session_get_killed_by(const struct nc_session *session);
/**
* @brief Get session ID.
*
* @param[in] session Session to get the information from.
* @return Session ID.
*/
uint32_t nc_session_get_id(const struct nc_session *session);
/**
* @brief Get session NETCONF version.
*
* @param[in] session Session to get the information from.
* @return 0 for version 1.0, non-zero for version 1.1.
*/
int nc_session_get_version(const struct nc_session *session);
/**
* @brief Get session transport used.
*
* @param[in] session Session to get the information from.
* @return Session transport.
*/
NC_TRANSPORT_IMPL nc_session_get_ti(const struct nc_session *session);
/**
* @brief Get session username.
*
* @param[in] session Session to get the information from.
* @return Session username.
*/
const char *nc_session_get_username(const struct nc_session *session);
/**
* @brief Get session host.
*
* @param[in] session Session to get the information from.
* @return Session host.
*/
const char *nc_session_get_host(const struct nc_session *session);
/**
* @brief Get session port.
*
* @param[in] session Session to get the information from.
* @return Session port.
*/
uint16_t nc_session_get_port(const struct nc_session *session);
/**
* @brief Get session path (unix socket only).
*
* @param[in] session Session to get the information from.
* @return Session unix socket path.
*/
const char *nc_session_get_path(const struct nc_session *session);
/**
* @brief Get session context.
*
* @param[in] session Session to get the information from.
* @return Session context.
*/
struct ly_ctx *nc_session_get_ctx(const struct nc_session *session);
/**
* @brief Assign arbitrary data to a session.
*
* @param[in] session Session to modify.
* @param[in] data Data to be stored in the session.
*/
void nc_session_set_data(struct nc_session *session, void *data);
/**
* @brief Get the data assigned to a session.
*
* @param[in] session Session to get the data from.
* @return Session-specific data.
*/
void *nc_session_get_data(const struct nc_session *session);
/**
* @brief Free the NETCONF session object.
*
* @param[in] session Object to free.
* @param[in] data_free Session user data destructor.
*/
void nc_session_free(struct nc_session *session, void (*data_free)(void *));
#if defined (NC_ENABLED_SSH) || defined (NC_ENABLED_TLS)
/**
* @brief Free all the dynamically allocated thread-specific libssl/libcrypto
* resources.
*
* This function should be called only if init (nc_client_init(), respectively nc_server_init()) was called.
* Call it in every thread your application creates just before the thread exits. In the last thread
* (usually the main one) call nc_client_destroy(), respectively nc_server_destroy().
*/
void nc_thread_destroy(void);
#endif /* NC_ENABLED_SSH || NC_ENABLED_TLS */
#ifdef __cplusplus
}
#endif
#endif /* NC_SESSION_H_ */

2946
src/session_client.c Normal file

File diff suppressed because it is too large Load diff

631
src/session_client.h Normal file
View file

@ -0,0 +1,631 @@
/**
* @file session_client.h
* @author Michal Vasko <mvasko@cesnet.cz>
* @brief libnetconf2 session client manipulation
*
* Copyright (c) 2015 - 2021 CESNET, z.s.p.o.
*
* This source code is licensed under BSD 3-Clause License (the "License").
* You may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://opensource.org/licenses/BSD-3-Clause
*/
#ifndef NC_SESSION_CLIENT_H_
#define NC_SESSION_CLIENT_H_
#ifdef __cplusplus
extern "C" {
#endif
#include <libyang/libyang.h>
#ifdef NC_ENABLED_SSH
# include <libssh/libssh.h>
#endif
#ifdef NC_ENABLED_TLS
# include <openssl/ssl.h>
#endif
#include "messages_client.h"
#include "netconf.h"
#include "session.h"
/**
* @addtogroup client
* @{
*/
/**
* @brief Set location where libnetconf tries to search for YANG/YIN schemas.
*
* The location is searched when connecting to a NETCONF server and building
* YANG context for further processing of the NETCONF messages and data.
*
* The searchpath is also used to store schemas retreived via \<get-schema\>
* operation - if the schema is not found in searchpath neither via schema
* callback provided via nc_client_set_schema_callback() and server supports
* the NETCONF \<get-schema\> operation, the schema is retrieved this way and
* stored into the searchpath (if specified).
*
* @param[in] path Directory where to search for YANG/YIN schemas.
* @return 0 on success, 1 on (memory allocation) failure.
*/
int nc_client_set_schema_searchpath(const char *path);
/**
* @brief Get schema searchpath that was set by nc_client_set_schema_searchpath().
*
* @return Schema searchpath directory, NULL if not set.
*/
const char *nc_client_get_schema_searchpath(void);
/**
* @brief Set callback function to get missing schemas.
*
* @param[in] clb Callback responsible for returning the missing model.
* @param[in] user_data Arbitrary data that will always be passed to the callback @p clb.
* @return 0 on success, 1 on (memory allocation) failure.
*/
int nc_client_set_schema_callback(ly_module_imp_clb clb, void *user_data);
/**
* @brief Get callback function used to get missing schemas.
*
* @param[out] user_data Optionally return the private data set with the callback.
* Note that the caller is responsible for freeing the private data, so before
* changing the callback, private data used for the previous callback should be
* freed.
* @return Pointer to the set callback, NULL if no such callback was set.
*/
ly_module_imp_clb nc_client_get_schema_callback(void **user_data);
/**
* @brief Use the provided thread-specific client's context in the current thread.
*
* Note that from this point the context is shared with the thread from which the context was taken and any
* nc_client_*set* functions and functions creating connection in these threads should be protected from the
* concurrent execution.
*
* Context contains schema searchpath/callback, call home binds, TLS and SSH authentication data (username, keys,
* various certificates and callbacks).
*
* @param[in] context Client's thread-specific context provided by nc_client_get_thread_context().
*/
void nc_client_set_thread_context(void *context);
/**
* @brief Get thread-specific client context for sharing with some other thread using
* nc_client_set_thread_context().
*
* @return Pointer to the client's context of the current thread.
*/
void *nc_client_get_thread_context(void);
/**
* @brief Initialize libssh and/or libssl/libcrypto for use in the client.
*/
void nc_client_init(void);
/**
* @brief Destroy all libssh and/or libssl/libcrypto dynamic memory and
* the client options, for both SSH and TLS, and for Call Home too.
*/
void nc_client_destroy(void);
/** @} Client */
/**
* @defgroup client_session Client Session
* @ingroup client
*
* @brief Client-side NETCONF session manipulation.
* @{
*/
/**
* @brief Connect to the NETCONF server via proviaded input/output file descriptors.
*
* Transport layer is supposed to be already set. Function do not cover authentication
* or any other manipulation with the transport layer, it only establish NETCONF session
* by sending and processing NETCONF \<hello\> messages.
*
* @param[in] fdin Input file descriptor for reading (clear) data from NETCONF server.
* @param[in] fdout Output file descriptor for writing (clear) data for NETCONF server.
* @param[in] ctx Optional parameter. If set, provides strict YANG context for the session
* (ignoring what is actually supported by the server side). If not set,
* YANG context is created for the session using \<get-schema\> (if supported
* by the server side) or/and by searching for YANG schemas in the searchpath
* (see nc_client_schema_searchpath()). In every case except not providing context
* to connect to a server supporting \<get-schema\> it is possible that
* the session context will not include all the models supported by the server.
* @return Created NETCONF session object or NULL in case of error.
*/
struct nc_session *nc_connect_inout(int fdin, int fdout, struct ly_ctx *ctx);
/**
* @brief Connect to the NETCONF server via unix socket.
*
* Connect to netconf server via an unix socket. Function do not cover authentication
* or any other manipulation with the transport layer, it only establish NETCONF session
* by sending and processing NETCONF \<hello\> messages.
*
* @param[in] address Path to the unix socket.
* @param[in] ctx Optional parameter. If set, provides strict YANG context for the session
* (ignoring what is actually supported by the server side). If not set,
* YANG context is created for the session using \<get-schema\> (if supported
* by the server side) or/and by searching for YANG schemas in the searchpath
* (see nc_client_schema_searchpath()). In every case except not providing context
* to connect to a server supporting \<get-schema\> it is possible that
* the session context will not include all the models supported by the server.
* @return Created NETCONF session object or NULL in case of error.
*/
struct nc_session *nc_connect_unix(const char *address, struct ly_ctx *ctx);
/** @} Client Session */
#ifdef NC_ENABLED_SSH
/**
* @defgroup client_ssh Client SSH
* @ingroup client
*
* @brief Client-side settings for SSH connections.
* @{
*/
/**
* @brief Set SSH authentication hostkey check (knownhosts) callback.
*
* Repetitive calling causes replacing of the previous callback and its private data. Caller is responsible for
* freeing the private data when necessary (the private data can be obtained by
* nc_client_ssh_get_auth_hostkey_check_clb()).
*
* @param[in] auth_hostkey_check Function to call, returns 0 on success, non-zero in error.
* If NULL, the default callback is set.
* @param[in] priv Optional private data to be passed to the callback function.
*/
void nc_client_ssh_set_auth_hostkey_check_clb(int (*auth_hostkey_check)(const char *hostname, ssh_session session, void *priv),
void *priv);
/**
* @brief Get currently set SSH authentication hostkey check (knownhosts) callback and its private data previously set
* by nc_client_ssh_set_auth_hostkey_check_clb().
*
* @param[out] auth_hostkey_check Currently set callback, NULL in case of the default callback.
* @param[out] priv Currently set (optional) private data to be passed to the callback function.
*/
void nc_client_ssh_get_auth_hostkey_check_clb(int (**auth_hostkey_check)(const char *hostname, ssh_session session, void *priv),
void **priv);
/**
* @brief Set SSH password authentication callback.
*
* Repetitive calling causes replacing of the previous callback and its private data. Caller is responsible for
* freeing the private data when necessary (the private data can be obtained by
* nc_client_ssh_get_auth_password_clb()).
*
* @param[in] auth_password Function to call, returns the password for username\@hostname.
* If NULL, the default callback is set.
* @param[in] priv Optional private data to be passed to the callback function.
*/
void nc_client_ssh_set_auth_password_clb(char *(*auth_password)(const char *username, const char *hostname, void *priv),
void *priv);
/**
* @brief Get currently set SSH password authentication callback and its private data previously set
* by nc_client_ssh_set_auth_password_clb().
*
* @param[out] auth_password Currently set callback, NULL in case of the default callback.
* @param[out] priv Currently set (optional) private data to be passed to the callback function.
*/
void nc_client_ssh_get_auth_password_clb(char *(**auth_password)(const char *username, const char *hostname, void *priv),
void **priv);
/**
* @brief Set SSH interactive authentication callback.
*
* Repetitive calling causes replacing of the previous callback and its private data. Caller is responsible for
* freeing the private data when necessary (the private data can be obtained by
* nc_client_ssh_get_auth_interactive_clb()).
*
* @param[in] auth_interactive Function to call for every question, returns the answer for
* authentication name with instruction and echoing prompt.
* If NULL, the default callback is set.
* @param[in] priv Optional private data to be passed to the callback function.
*/
void nc_client_ssh_set_auth_interactive_clb(char *(*auth_interactive)(const char *auth_name, const char *instruction,
const char *prompt, int echo, void *priv),
void *priv);
/**
* @brief Get currently set SSH interactive authentication callback and its private data previously set
* by nc_client_ssh_set_auth_interactive_clb().
*
* @param[out] auth_interactive Currently set callback, NULL in case of the default callback.
* @param[out] priv Currently set (optional) private data to be passed to the callback function.
*/
void nc_client_ssh_get_auth_interactive_clb(char *(**auth_interactive)(const char *auth_name, const char *instruction,
const char *prompt, int echo, void *priv),
void **priv);
/**
* @brief Set SSH publickey authentication encrypted private key passphrase callback.
*
* Repetitive calling causes replacing of the previous callback and its private data. Caller is responsible for
* freeing the private data when necessary (the private data can be obtained by
* nc_client_ssh_get_auth_privkey_passphrase_clb()).
*
* @param[in] auth_privkey_passphrase Function to call for every question, returns
* the passphrase for the specific private key.
* @param[in] priv Optional private data to be passed to the callback function.
*/
void nc_client_ssh_set_auth_privkey_passphrase_clb(char *(*auth_privkey_passphrase)(const char *privkey_path, void *priv),
void *priv);
/**
* @brief Get currently set SSH publickey authentication encrypted private key passphrase callback and its private data
* previously set by nc_client_ssh_set_auth_privkey_passphrase_clb().
*
* @param[out] auth_privkey_passphrase Currently set callback, NULL in case of the default callback.
* @param[out] priv Currently set (optional) private data to be passed to the callback function.
*/
void nc_client_ssh_get_auth_privkey_passphrase_clb(char *(**auth_privkey_passphrase)(const char *privkey_path, void *priv),
void **priv);
/**
* @brief Add an SSH public and private key pair to be used for client authentication.
*
* Private key can be encrypted, the passphrase will be asked for before using it.
*
* @param[in] pub_key Path to the public key.
* @param[in] priv_key Path to the private key.
* @return 0 on success, -1 on error.
*/
int nc_client_ssh_add_keypair(const char *pub_key, const char *priv_key);
/**
* @brief Remove an SSH public and private key pair that was used for client authentication.
*
* @param[in] idx Index of the keypair starting with 0.
* @return 0 on success, -1 on error.
*/
int nc_client_ssh_del_keypair(int idx);
/**
* @brief Get the number of public an private key pairs set to be used for client authentication.
*
* @return Keypair count.
*/
int nc_client_ssh_get_keypair_count(void);
/**
* @brief Get a specific keypair set to be used for client authentication.
*
* @param[in] idx Index of the specific keypair.
* @param[out] pub_key Path to the public key.
* @param[out] priv_key Path to the private key.
* @return 0 on success, -1 on error.
*/
int nc_client_ssh_get_keypair(int idx, const char **pub_key, const char **priv_key);
/**
* @brief Set SSH authentication method preference.
*
* The default preference is as follows:
* - interactive authentication (3)
* - password authentication (2)
* - public key authentication (1)
*
* @param[in] auth_type Authentication method to modify the preference of.
* @param[in] pref Preference of @p auth_type. Higher number increases priority, negative values disable the method.
*/
void nc_client_ssh_set_auth_pref(NC_SSH_AUTH_TYPE auth_type, int16_t pref);
/**
* @brief Get SSH authentication method preference.
*
* @param[in] auth_type Authentication method to retrieve the prefrence of.
* @return Preference of the @p auth_type.
*/
int16_t nc_client_ssh_get_auth_pref(NC_SSH_AUTH_TYPE auth_type);
/**
* @brief Set client SSH username used for authentication.
*
* @param[in] username Username to use.
* @return 0 on success, -1 on error.
*/
int nc_client_ssh_set_username(const char *username);
/**
* @brief Get client SSH username used for authentication.
*
* @return Username used.
*/
const char *nc_client_ssh_get_username(void);
/**
* @brief Connect to the NETCONF server using SSH transport (via libssh).
*
* SSH session is created with default options. If the caller needs to use specific SSH session properties,
* they are supposed to use nc_connect_libssh().
*
* @param[in] host Hostname or address (both Ipv4 and IPv6 are accepted) of the target server.
* 'localhost' is used by default if NULL is specified.
* @param[in] port Port number of the target server. Default value 830 is used if 0 is specified.
* @param[in] ctx Optional parameter. If set, provides strict YANG context for the session
* (ignoring what is actually supported by the server side). If not set,
* YANG context is created for the session using \<get-schema\> (if supported
* by the server side) or/and by searching for YANG schemas in the searchpath
* (see nc_client_schema_searchpath()). In every case except not providing context
* to connect to a server supporting \<get-schema\> it is possible that
* the session context will not include all the models supported by the server.
* @return Created NETCONF session object or NULL on error.
*/
struct nc_session *nc_connect_ssh(const char *host, uint16_t port, struct ly_ctx *ctx);
/**
* @brief Connect to the NETCONF server using the provided SSH (libssh) session.
*
* SSH session can have any options set, they will not be modified. If no options were set,
* host 'localhost', port 22, and the username detected from the EUID is used. If socket is
* set and connected only the host and the username must be set/is detected. Or the @p ssh_session
* can already be authenticated in which case it is used directly.
*
* @param[in] ssh_session libssh structure representing SSH session object. After passing it
* to libnetconf2 this way, it is fully managed by it (including freeing!).
* @param[in] ctx Optional parameter. If set, provides strict YANG context for the session
* (ignoring what is actually supported by the server side). If not set,
* YANG context is created for the session using \<get-schema\> (if supported
* by the server side) or/and by searching for YANG schemas in the searchpath
* (see nc_client_schema_searchpath()). In every case except not providing context
* to connect to a server supporting \<get-schema\> it is possible that
* the session context will not include all the models supported by the server.
* @return Created NETCONF session object or NULL on error.
*/
struct nc_session *nc_connect_libssh(ssh_session ssh_session, struct ly_ctx *ctx);
/**
* @brief Create another NETCONF session on existing SSH session using separated SSH channel.
*
* @param[in] session Existing NETCONF session. The session has to be created on SSH transport layer using libssh -
* it has to be created by nc_connect_ssh(), nc_connect_libssh() or nc_connect_ssh_channel().
* @param[in] ctx Optional parameter. If set, provides strict YANG context for the session
* (ignoring what is actually supported by the server side). If not set,
* YANG context is created for the session using \<get-schema\> (if supported
* by the server side) or/and by searching for YANG schemas in the searchpath
* (see nc_client_schema_searchpath()). In every case except not providing context
* to connect to a server supporting \<get-schema\> it is possible that
* the session context will not include all the models supported by the server.
* @return Created NETCONF session object or NULL on error.
*/
struct nc_session *nc_connect_ssh_channel(struct nc_session *session, struct ly_ctx *ctx);
/** @} Client SSH */
#endif /* NC_ENABLED_SSH */
#ifdef NC_ENABLED_TLS
/**
* @defgroup client_tls Client TLS
* @ingroup client
*
* @brief Client-side settings for TLS connections.
* @{
*/
/**
* @brief Set client authentication identity - a certificate and a private key.
*
* @param[in] client_cert Path to the file containing the client certificate.
* @param[in] client_key Path to the file containing the private key for the @p client_cert.
* If NULL, key is expected to be stored with @p client_cert.
* @return 0 on success, -1 on error.
*/
int nc_client_tls_set_cert_key_paths(const char *client_cert, const char *client_key);
/**
* @brief Get client authentication identity - a certificate and a private key.
*
* @param[out] client_cert Path to the file containing the client certificate. Can be NULL.
* @param[out] client_key Path to the file containing the private key for the @p client_cert. Can be NULL.
*/
void nc_client_tls_get_cert_key_paths(const char **client_cert, const char **client_key);
/**
* @brief Set client trusted CA certificates paths.
*
* @param[in] ca_file Location of the CA certificate file used to verify server certificates.
* For more info, see the documentation for SSL_CTX_load_verify_locations() from OpenSSL.
* @param[in] ca_dir Location of the CA certificates directory used to verify the server certificates.
* For more info, see the documentation for SSL_CTX_load_verify_locations() from OpenSSL.
* @return 0 on success, -1 on error.
*/
int nc_client_tls_set_trusted_ca_paths(const char *ca_file, const char *ca_dir);
/**
* @brief Get client trusted CA certificates paths.
*
* @param[out] ca_file Location of the CA certificate file used to verify server certificates.
* Can be NULL.
* @param[out] ca_dir Location of the CA certificates directory used to verify the server certificates.
* Can be NULL.
*/
void nc_client_tls_get_trusted_ca_paths(const char **ca_file, const char **ca_dir);
/**
* @brief Set client Certificate Revocation List paths.
*
* @param[in] crl_file Location of the CRL certificate file used to check for revocated certificates.
* @param[in] crl_dir Location of the CRL certificate directory used to check for revocated certificates.
* @return 0 on success, -1 on error.
*/
int nc_client_tls_set_crl_paths(const char *crl_file, const char *crl_dir);
/**
* @brief Get client Certificate Revocation List paths.
*
* @param[out] crl_file Location of the CRL certificate file used to check for revocated certificates.
* @param[out] crl_dir Location of the CRL certificate directory used to check for revocated certificates.
*/
void nc_client_tls_get_crl_paths(const char **crl_file, const char **crl_dir);
/**
* @brief Connect to the NETCONF server using TLS transport (via libssl)
*
* TLS session is created with the certificates set using nc_client_tls_* functions, which must be called beforehand!
* If the caller needs to use specific TLS session properties, they are supposed to use nc_connect_libssl().
*
* @param[in] host Hostname or address (both Ipv4 and IPv6 are accepted) of the target server.
* 'localhost' is used by default if NULL is specified.
* @param[in] port Port number of the target server. Default value 6513 is used if 0 is specified.
* @param[in] ctx Optional parameter. If set, provides strict YANG context for the session
* (ignoring what is actually supported by the server side). If not set,
* YANG context is created for the session using \<get-schema\> (if supported
* by the server side) or/and by searching for YANG schemas in the searchpath
* (see nc_client_schema_searchpath()). In every case except not providing context
* to connect to a server supporting \<get-schema\> it is possible that
* the session context will not include all the models supported by the server.
* @return Created NETCONF session object or NULL on error.
*/
struct nc_session *nc_connect_tls(const char *host, uint16_t port, struct ly_ctx *ctx);
/**
* @brief Connect to the NETCONF server using the provided TLS (libssl) session.
*
* The TLS session supplied is expected to be fully connected and authenticated!
*
* @param[in] tls libssl structure representing the TLS session object.
* @param[in] ctx Optional parameter. If set, provides strict YANG context for the session
* (ignoring what is actually supported by the server side). If not set,
* YANG context is created for the session using \<get-schema\> (if supported
* by the server side) or/and by searching for YANG schemas in the searchpath
* (see nc_client_schema_searchpath()). In every case except not providing context
* to connect to a server supporting \<get-schema\> it is possible that
* the session context will not include all the models supported by the server.
* @return Created NETCONF session object or NULL on error.
*/
struct nc_session *nc_connect_libssl(SSL *tls, struct ly_ctx *ctx);
/** @} Client TLS */
#endif /* NC_ENABLED_TLS */
/**
* @addtogroup client_session
* @{
*/
/**
* @brief Get session capabilities.
*
* @param[in] session Session to get the information from.
* @return NULL-terminated array of the @p session capabilities.
*/
const char * const *nc_session_get_cpblts(const struct nc_session *session);
/**
* @brief Check capability presence in a session.
*
* @param[in] session Session to check.
* @param[in] capab Capability to look for, capability with any additional suffix will match.
* @return Matching capability, NULL if none found.
*/
const char *nc_session_cpblt(const struct nc_session *session, const char *capab);
/**
* @brief Check whether the session has a notification thread running.
*
* @param[in] session Session to check.
* @return 1 if notfication thread is running, 0 otherwise.
*/
int nc_session_ntf_thread_running(const struct nc_session *session);
/**
* @brief Receive NETCONF RPC reply.
*
* @note This function can be called in a single thread only.
*
* @param[in] session NETCONF session from which the function gets data. It must be the
* client side session object.
* @param[in] rpc Original RPC this should be the reply to.
* @param[in] msgid Expected message ID of the reply.
* @param[in] timeout Timeout for reading in milliseconds. Use negative value for infinite
* waiting and 0 for immediate return if data are not available on the wire.
* @param[out] envp NETCONF rpc-reply XML envelopes.
* @param[out] op Parsed NETCONF reply data, if any (none for \<ok\> or error replies). Set only on #NC_MSG_REPLY
* and #NC_MSG_REPLY_ERR_MSGID return.
* @return #NC_MSG_REPLY for success,
* #NC_MSG_WOULDBLOCK if @p timeout has elapsed,
* #NC_MSG_ERROR if reading has failed,
* #NC_MSG_NOTIF if a notification was read instead (call this function again to get the reply), and
* #NC_MSG_REPLY_ERR_MSGID if a reply with missing or wrong message-id was received.
*/
NC_MSG_TYPE nc_recv_reply(struct nc_session *session, struct nc_rpc *rpc, uint64_t msgid, int timeout,
struct lyd_node **envp, struct lyd_node **op);
/**
* @brief Receive NETCONF Notification.
*
* @param[in] session NETCONF session from which the function gets data. It must be the
* client side session object.
* @param[in] timeout Timeout for reading in milliseconds. Use negative value for infinite
* waiting and 0 for immediate return if data are not available on the wire.
* @param[out] envp NETCONF notification XML envelopes.
* @param[out] op Parsed NETCONF notification data.
* @return #NC_MSG_NOTIF for success,
* #NC_MSG_WOULDBLOCK if @p timeout has elapsed,
* #NC_MSG_ERROR if reading has failed, and
* #NC_MSG_REPLY if a reply was read instead (call this function again to get a notification).
*/
NC_MSG_TYPE nc_recv_notif(struct nc_session *session, int timeout, struct lyd_node **envp, struct lyd_node **op);
/**
* @brief Receive NETCONF Notifications in a separate thread until the session is terminated
* or \<notificationComplete\> is received.
*
* @param[in] session Netconf session to read notifications from.
* @param[in] notif_clb Function that is called for every received notification (including
* \<notificationComplete\>). Parameters are the session the notification was received on
* and the notification data.
* @return 0 if the thread was successfully created, -1 on error.
*/
int nc_recv_notif_dispatch(struct nc_session *session,
void (*notif_clb)(struct nc_session *session, const struct lyd_node *envp, const struct lyd_node *op));
/**
* @brief Send NETCONF RPC message via the session.
*
* @param[in] session NETCONF session where the RPC will be written.
* @param[in] rpc NETCONF RPC object to send via the specified session.
* @param[in] timeout Timeout for writing in milliseconds. Use negative value for infinite
* waiting and 0 for return if data cannot be sent immediately.
* @param[out] msgid If RPC was successfully sent, this is it's message ID.
* @return #NC_MSG_RPC on success,
* #NC_MSG_WOULDBLOCK in case of a busy session, and
* #NC_MSG_ERROR on error.
*/
NC_MSG_TYPE nc_send_rpc(struct nc_session *session, struct nc_rpc *rpc, int timeout, uint64_t *msgid);
/**
* @brief Make a session not strict when sending RPCs and receiving RPC replies. In other words,
* it will silently skip unknown nodes without an error.
*
* Generally, no such data should be worked with, so use this function only when you know what you
* are doing and you understand the consequences.
*
* @param[in] session NETCONF client session.
*/
void nc_client_session_set_not_strict(struct nc_session *session);
/** @} Client Session */
#ifdef __cplusplus
}
#endif
#endif /* NC_SESSION_CLIENT_H_ */

354
src/session_client_ch.h Normal file
View file

@ -0,0 +1,354 @@
/**
* @file session_client_ch.h
* @author Michal Vasko <mvasko@cesnet.cz>
* @brief libnetconf2 Call Home session client manipulation
*
* Copyright (c) 2015 - 2021 CESNET, z.s.p.o.
*
* This source code is licensed under BSD 3-Clause License (the "License").
* You may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://opensource.org/licenses/BSD-3-Clause
*/
#ifndef NC_SESSION_CLIENT_CH_H_
#define NC_SESSION_CLIENT_CH_H_
#ifdef __cplusplus
extern "C" {
#endif
#include <libyang/libyang.h>
#ifdef NC_ENABLED_SSH
# include <libssh/libssh.h>
#endif
#include "messages_client.h"
#include "netconf.h"
#include "session.h"
#if defined (NC_ENABLED_SSH) || defined (NC_ENABLED_TLS)
/**
* @defgroup client_ch Client-side Call Home
* @ingroup client
*
* @brief Call Home functionality for client-side applications.
* @{
*/
/**
* @brief Accept a Call Home connection on any of the listening binds.
*
* @param[in] timeout Timeout for receiving a new connection in milliseconds, 0 for
* non-blocking call, -1 for infinite waiting.
* @param[in] ctx Session context to use. Can be NULL.
* @param[out] session New session.
* @return 1 on success, 0 on timeout, -1 on error.
*/
int nc_accept_callhome(int timeout, struct ly_ctx *ctx, struct nc_session **session);
/** @} Client-side Call Home */
#endif /* NC_ENABLED_SSH || NC_ENABLED_TLS */
#ifdef NC_ENABLED_SSH
/**
* @defgroup client_ch_ssh Client-side Call Home on SSH
* @ingroup client_ch
*
* @brief SSH settings for the Call Home functionality
* @{
*/
/**
* @brief Set SSH Call Home authentication hostkey check (knownhosts) callback.
*
* Repetitive calling causes replacing of the previous callback and its private data. Caller is responsible for
* freeing the private data when necessary (the private data can be obtained by
* nc_client_ssh_ch_get_auth_hostkey_check_clb()).
*
* @param[in] auth_hostkey_check Function to call, returns 0 on success, non-zero in error.
* If NULL, the default callback is set.
* @param[in] priv Optional private data to be passed to the callback function.
*/
void nc_client_ssh_ch_set_auth_hostkey_check_clb(int (*auth_hostkey_check)(const char *hostname, ssh_session session, void *priv),
void *priv);
/**
* @brief Get currently set SSH Call Home authentication hostkey check (knownhosts) callback and its private data
* previously set by nc_client_ssh_ch_set_auth_hostkey_check_clb().
*
* @param[out] auth_hostkey_check Currently set callback, NULL in case of the default callback.
* @param[out] priv Currently set (optional) private data to be passed to the callback function.
*/
void nc_client_ssh_ch_get_auth_hostkey_check_clb(int (**auth_hostkey_check)(const char *hostname, ssh_session session, void *priv),
void **priv);
/**
* @brief Set SSH Call Home password authentication callback.
*
* Repetitive calling causes replacing of the previous callback and its private data. Caller is responsible for
* freeing the private data when necessary (the private data can be obtained by
* nc_client_ssh_ch_get_auth_password_clb()).
*
* @param[in] auth_password Function to call, returns the password for username\@hostname.
* If NULL, the default callback is set.
* @param[in] priv Optional private data to be passed to the callback function.
*/
void nc_client_ssh_ch_set_auth_password_clb(char *(*auth_password)(const char *username, const char *hostname, void *priv),
void *priv);
/**
* @brief Get currently set SSH Call Home password authentication callback and its private data
* previously set by nc_client_ssh_ch_set_auth_password_clb().
*
* @param[out] auth_password Currently set callback, NULL in case of the default callback.
* @param[out] priv Currently set (optional) private data to be passed to the callback function.
*/
void nc_client_ssh_ch_get_auth_password_clb(char *(**auth_password)(const char *username, const char *hostname, void *priv),
void **priv);
/**
* @brief Set SSH Call Home interactive authentication callback.
*
* Repetitive calling causes replacing of the previous callback and its private data. Caller is responsible for
* freeing the private data when necessary (the private data can be obtained by
* nc_client_ssh_ch_get_auth_interactive_clb()).
*
* @param[in] auth_interactive Function to call for every question, returns the answer for
* authentication name with instruction and echoing prompt.
* If NULL, the default callback is set.
* @param[in] priv Optional private data to be passed to the callback function.
*/
void nc_client_ssh_ch_set_auth_interactive_clb(char *(*auth_interactive)(const char *auth_name, const char *instruction,
const char *prompt, int echo, void *priv),
void *priv);
/**
* @brief Get currently set SSH Call Home interactive authentication callback and its private data
* previously set by nc_client_ssh_ch_set_auth_interactive_clb().
*
* @param[out] auth_interactive Currently set callback, NULL in case of the default callback.
* @param[out] priv Currently set (optional) private data to be passed to the callback function.
*/
void nc_client_ssh_ch_get_auth_interactive_clb(char *(**auth_interactive)(const char *auth_name, const char *instruction,
const char *prompt, int echo, void *priv),
void **priv);
/**
* @brief Set SSH Call Home publickey authentication encrypted private key passphrase callback.
*
* Repetitive calling causes replacing of the previous callback and its private data. Caller is responsible for
* freeing the private data when necessary (the private data can be obtained by
* nc_client_ssh_ch_get_auth_privkey_passphrase_clb()).
*
* @param[in] auth_privkey_passphrase Function to call for every question, returns
* the passphrase for the specific private key.
* @param[in] priv Optional private data to be passed to the callback function.
*/
void nc_client_ssh_ch_set_auth_privkey_passphrase_clb(char *(*auth_privkey_passphrase)(const char *privkey_path, void *priv),
void *priv);
/**
* @brief Get currently set SSH Call Home publickey authentication encrypted private key passphrase callback and its
* private data previously set by nc_client_ssh_ch_set_auth_privkey_passphrase_clb().
*
* @param[out] auth_privkey_passphrase Currently set callback, NULL in case of the default callback.
* @param[out] priv Currently set (optional) private data to be passed to the callback function.
*/
void nc_client_ssh_ch_get_auth_privkey_passphrase_clb(char *(**auth_privkey_passphrase)(const char *privkey_path, void *priv),
void **priv);
/**
* @brief Add a new client bind and start listening on it for SSH Call Home connections.
*
* @param[in] address IP address to bind to.
* @param[in] port Port to bind to.
* @return 0 on success, -1 on error.
*/
int nc_client_ssh_ch_add_bind_listen(const char *address, uint16_t port);
/**
* @brief Remove an SSH listening client bind.
*
* @param[in] address IP address the socket was bound to. NULL matches all.
* @param[in] port Port the socket was bound to. 0 matches all.
* @return 0 on success, -1 on not found.
*/
int nc_client_ssh_ch_del_bind(const char *address, uint16_t port);
/**
* @brief Add an SSH public and private key pair to be used for Call Home client authentication.
*
* Private key can be encrypted, the passphrase will be asked for before using it.
*
* @param[in] pub_key Path to the public key.
* @param[in] priv_key Path to the private key.
* @return 0 on success, -1 on error.
*/
int nc_client_ssh_ch_add_keypair(const char *pub_key, const char *priv_key);
/**
* @brief Remove an SSH public and private key pair that was used for Call Home client authentication.
*
* @param[in] idx Index of the keypair starting with 0.
* @return 0 on success, -1 on error.
*/
int nc_client_ssh_ch_del_keypair(int idx);
/**
* @brief Get the number of public an private key pairs set to be used for Call Home client authentication.
*
* @return Keypair count.
*/
int nc_client_ssh_ch_get_keypair_count(void);
/**
* @brief Get a specific keypair set to be used for Call Home client authentication.
*
* @param[in] idx Index of the specific keypair.
* @param[out] pub_key Path to the public key.
* @param[out] priv_key Path to the private key.
* @return 0 on success, -1 on error.
*/
int nc_client_ssh_ch_get_keypair(int idx, const char **pub_key, const char **priv_key);
/**
* @brief Set SSH Call Home authentication method preference.
*
* The default preference is as follows:
* - public key authentication (3)
* - password authentication (2)
* - interactive authentication (1)
*
* @param[in] auth_type Authentication method to modify the preference of.
* @param[in] pref Preference of @p auth_type. Higher number increases priority, negative values disable the method.
*/
void nc_client_ssh_ch_set_auth_pref(NC_SSH_AUTH_TYPE auth_type, int16_t pref);
/**
* @brief Get SSH Call Home authentication method preference.
*
* @param[in] auth_type Authentication method to retrieve the prefrence of.
* @return Preference of the @p auth_type.
*/
int16_t nc_client_ssh_ch_get_auth_pref(NC_SSH_AUTH_TYPE auth_type);
/**
* @brief Set client Call Home SSH username used for authentication.
*
* @param[in] username Username to use.
* @return 0 on success, -1 on error.
*/
int nc_client_ssh_ch_set_username(const char *username);
/**
* @brief Get client Call Home SSH username used for authentication.
*
* @return Username used.
*/
const char *nc_client_ssh_ch_get_username(void);
/** @} Client-side Call Home on SSH */
#endif /* NC_ENABLED_SSH */
#ifdef NC_ENABLED_TLS
/**
* @defgroup client_ch_tls Client-side Call Home on TLS
* @ingroup client_ch
*
* @brief TLS settings for the Call Home functionality
* @{
*/
/**
* @brief Add a new client bind and start listening on it for TLS Call Home connections.
*
* @param[in] address IP address to bind to.
* @param[in] port Port to bind to.
* @return 0 on success, -1 on error.
*/
int nc_client_tls_ch_add_bind_listen(const char *address, uint16_t port);
/**
* @brief Remove a TLS listening client bind.
*
* @param[in] address IP address the socket was bound to. NULL matches all.
* @param[in] port Port the socket was bound to. 0 matches all.
* @return 0 on success, -1 on not found.
*/
int nc_client_tls_ch_del_bind(const char *address, uint16_t port);
/**
* @brief Set client Call Home authentication identity - a certificate and a private key.
*
* @param[in] client_cert Path to the file containing the client certificate.
* @param[in] client_key Path to the file containing the private key for the @p client_cert.
* If NULL, key is expected to be stored with @p client_cert.
* @return 0 on success, -1 on error.
*/
int nc_client_tls_ch_set_cert_key_paths(const char *client_cert, const char *client_key);
/**
* @brief Get client Call Home authentication identity - a certificate and a private key.
*
* @param[out] client_cert Path to the file containing the client certificate. Can be NULL.
* @param[out] client_key Path to the file containing the private key for the @p client_cert.
* Can be NULL.
*/
void nc_client_tls_ch_get_cert_key_paths(const char **client_cert, const char **client_key);
/**
* @brief Set client Call Home trusted CA certificates.
*
* @param[in] ca_file Location of the CA certificate file used to verify server certificates.
* For more info, see the documentation for SSL_CTX_load_verify_locations() from OpenSSL.
* @param[in] ca_dir Location of the CA certificates directory used to verify the server certificates.
* For more info, see the documentation for SSL_CTX_load_verify_locations() from OpenSSL.
* @return 0 on success, -1 on error.
*/
int nc_client_tls_ch_set_trusted_ca_paths(const char *ca_file, const char *ca_dir);
/**
* @brief Get client Call Home trusted CA certificates.
*
* @param[out] ca_file Location of the CA certificate file used to verify server certificates.
* Can be NULL.
* @param[out] ca_dir Location of the CA certificates directory used to verify the server certificates.
* Can be NULL.
*/
void nc_client_tls_ch_get_trusted_ca_paths(const char **ca_file, const char **ca_dir);
/**
* @brief Set client Call Home Certificate Revocation Lists.
*
* @param[in] crl_file Location of the CRL certificate file used to check for revocated certificates.
* @param[in] crl_dir Location of the CRL certificate directory used to check for revocated certificates.
* @return 0 on success, -1 on error.
*/
int nc_client_tls_ch_set_crl_paths(const char *crl_file, const char *crl_dir);
/**
* @brief Get client Call Home Certificate Revocation Lists.
*
* @param[out] crl_file Location of the CRL certificate file used to check for revocated certificates.
* Can be NULL.
* @param[out] crl_dir Location of the CRL certificate directory used to check for revocated certificates.
* Can be NULL.
*/
void nc_client_tls_ch_get_crl_paths(const char **crl_file, const char **crl_dir);
/** @} Client-side Call Home on TLS */
#endif /* NC_ENABLED_TLS */
#ifdef __cplusplus
}
#endif
#endif /* NC_SESSION_CLIENT_CH_H_ */

1911
src/session_client_ssh.c Normal file

File diff suppressed because it is too large Load diff

848
src/session_client_tls.c Normal file
View file

@ -0,0 +1,848 @@
/**
* \file session_client_tls.c
* \author Radek Krejci <rkrejci@cesnet.cz>
* \author Michal Vasko <mvasko@cesnet.cz>
* \brief libnetconf2 - TLS specific session client transport functions
*
* This source is compiled only with libssl.
*
* 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
*/
#include <assert.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <libyang/libyang.h>
#include <openssl/err.h>
#include <openssl/ossl_typ.h>
#include <openssl/x509.h>
#include "libnetconf.h"
#include "session_client.h"
#include "session_client_ch.h"
#if OPENSSL_VERSION_NUMBER < 0x10100000L
#define X509_STORE_CTX_get_by_subject X509_STORE_get_by_subject
#endif
struct nc_client_context *nc_client_context_location(void);
int nc_session_new_ctx(struct nc_session *session, struct ly_ctx *ctx);
#define client_opts nc_client_context_location()->opts
#define tls_opts nc_client_context_location()->tls_opts
#define tls_ch_opts nc_client_context_location()->tls_ch_opts
static int tlsauth_ch;
#if OPENSSL_VERSION_NUMBER >= 0x10100000L // >= 1.1.0
static int
tlsauth_verify_callback(int preverify_ok, X509_STORE_CTX *x509_ctx)
{
X509_STORE_CTX *store_ctx;
X509_OBJECT *obj;
X509_NAME *subject, *issuer;
X509 *cert;
X509_CRL *crl;
X509_REVOKED *revoked;
EVP_PKEY *pubkey;
int i, n, rc;
const ASN1_TIME *next_update = NULL;
struct nc_client_tls_opts *opts;
if (!preverify_ok) {
return 0;
}
opts = (tlsauth_ch ? &tls_ch_opts : &tls_opts);
if (!opts->crl_store) {
/* nothing to check */
return 1;
}
cert = X509_STORE_CTX_get_current_cert(x509_ctx);
subject = X509_get_subject_name(cert);
issuer = X509_get_issuer_name(cert);
/* try to retrieve a CRL corresponding to the _subject_ of
* the current certificate in order to verify it's integrity */
store_ctx = X509_STORE_CTX_new();
obj = X509_OBJECT_new();
X509_STORE_CTX_init(store_ctx, opts->crl_store, NULL, NULL);
rc = X509_STORE_CTX_get_by_subject(store_ctx, X509_LU_CRL, subject, obj);
X509_STORE_CTX_free(store_ctx);
crl = X509_OBJECT_get0_X509_CRL(obj);
if ((rc > 0) && crl) {
next_update = X509_CRL_get0_nextUpdate(crl);
/* verify the signature on this CRL */
pubkey = X509_get_pubkey(cert);
if (X509_CRL_verify(crl, pubkey) <= 0) {
X509_STORE_CTX_set_error(x509_ctx, X509_V_ERR_CRL_SIGNATURE_FAILURE);
X509_OBJECT_free(obj);
if (pubkey) {
EVP_PKEY_free(pubkey);
}
return 0; /* fail */
}
if (pubkey) {
EVP_PKEY_free(pubkey);
}
/* check date of CRL to make sure it's not expired */
if (!next_update) {
X509_STORE_CTX_set_error(x509_ctx, X509_V_ERR_ERROR_IN_CRL_NEXT_UPDATE_FIELD);
X509_OBJECT_free(obj);
return 0; /* fail */
}
if (X509_cmp_current_time(next_update) < 0) {
X509_STORE_CTX_set_error(x509_ctx, X509_V_ERR_CRL_HAS_EXPIRED);
X509_OBJECT_free(obj);
return 0; /* fail */
}
X509_OBJECT_free(obj);
}
/* try to retrieve a CRL corresponding to the _issuer_ of
* the current certificate in order to check for revocation */
store_ctx = X509_STORE_CTX_new();
obj = X509_OBJECT_new();
X509_STORE_CTX_init(store_ctx, opts->crl_store, NULL, NULL);
rc = X509_STORE_CTX_get_by_subject(store_ctx, X509_LU_CRL, issuer, obj);
X509_STORE_CTX_free(store_ctx);
crl = X509_OBJECT_get0_X509_CRL(obj);
if ((rc > 0) && crl) {
/* check if the current certificate is revoked by this CRL */
n = sk_X509_REVOKED_num(X509_CRL_get_REVOKED(crl));
for (i = 0; i < n; i++) {
revoked = sk_X509_REVOKED_value(X509_CRL_get_REVOKED(crl), i);
if (ASN1_INTEGER_cmp(X509_REVOKED_get0_serialNumber(revoked), X509_get_serialNumber(cert)) == 0) {
ERR(NULL, "Certificate revoked!");
X509_STORE_CTX_set_error(x509_ctx, X509_V_ERR_CERT_REVOKED);
X509_OBJECT_free(obj);
return 0; /* fail */
}
}
X509_OBJECT_free(obj);
}
return 1; /* success */
}
#else
static int
tlsauth_verify_callback(int preverify_ok, X509_STORE_CTX *x509_ctx)
{
X509_STORE_CTX store_ctx;
X509_OBJECT obj;
X509_NAME *subject, *issuer;
X509 *cert;
X509_CRL *crl;
X509_REVOKED *revoked;
EVP_PKEY *pubkey;
int i, n, rc;
ASN1_TIME *next_update = NULL;
struct nc_client_tls_opts *opts;
if (!preverify_ok) {
return 0;
}
opts = (tlsauth_ch ? &tls_ch_opts : &tls_opts);
if (!opts->crl_store) {
/* nothing to check */
return 1;
}
cert = X509_STORE_CTX_get_current_cert(x509_ctx);
subject = X509_get_subject_name(cert);
issuer = X509_get_issuer_name(cert);
/* try to retrieve a CRL corresponding to the _subject_ of
* the current certificate in order to verify it's integrity */
memset((char *)&obj, 0, sizeof obj);
X509_STORE_CTX_init(&store_ctx, opts->crl_store, NULL, NULL);
rc = X509_STORE_CTX_get_by_subject(&store_ctx, X509_LU_CRL, subject, &obj);
X509_STORE_CTX_cleanup(&store_ctx);
crl = obj.data.crl;
if ((rc > 0) && crl) {
next_update = X509_CRL_get_nextUpdate(crl);
/* verify the signature on this CRL */
pubkey = X509_get_pubkey(cert);
if (X509_CRL_verify(crl, pubkey) <= 0) {
X509_STORE_CTX_set_error(x509_ctx, X509_V_ERR_CRL_SIGNATURE_FAILURE);
X509_OBJECT_free_contents(&obj);
if (pubkey) {
EVP_PKEY_free(pubkey);
}
return 0; /* fail */
}
if (pubkey) {
EVP_PKEY_free(pubkey);
}
/* check date of CRL to make sure it's not expired */
if (!next_update) {
X509_STORE_CTX_set_error(x509_ctx, X509_V_ERR_ERROR_IN_CRL_NEXT_UPDATE_FIELD);
X509_OBJECT_free_contents(&obj);
return 0; /* fail */
}
if (X509_cmp_current_time(next_update) < 0) {
X509_STORE_CTX_set_error(x509_ctx, X509_V_ERR_CRL_HAS_EXPIRED);
X509_OBJECT_free_contents(&obj);
return 0; /* fail */
}
X509_OBJECT_free_contents(&obj);
}
/* try to retrieve a CRL corresponding to the _issuer_ of
* the current certificate in order to check for revocation */
memset((char *)&obj, 0, sizeof obj);
X509_STORE_CTX_init(&store_ctx, opts->crl_store, NULL, NULL);
rc = X509_STORE_CTX_get_by_subject(&store_ctx, X509_LU_CRL, issuer, &obj);
X509_STORE_CTX_cleanup(&store_ctx);
crl = obj.data.crl;
if ((rc > 0) && crl) {
/* check if the current certificate is revoked by this CRL */
n = sk_X509_REVOKED_num(X509_CRL_get_REVOKED(crl));
for (i = 0; i < n; i++) {
revoked = sk_X509_REVOKED_value(X509_CRL_get_REVOKED(crl), i);
if (ASN1_INTEGER_cmp(revoked->serialNumber, X509_get_serialNumber(cert)) == 0) {
ERR(NULL, "Certificate revoked!");
X509_STORE_CTX_set_error(x509_ctx, X509_V_ERR_CERT_REVOKED);
X509_OBJECT_free_contents(&obj);
return 0; /* fail */
}
}
X509_OBJECT_free_contents(&obj);
}
return 1; /* success */
}
#endif
void
_nc_client_tls_destroy_opts(struct nc_client_tls_opts *opts)
{
free(opts->cert_path);
free(opts->key_path);
free(opts->ca_file);
free(opts->ca_dir);
SSL_CTX_free(opts->tls_ctx);
free(opts->crl_file);
free(opts->crl_dir);
X509_STORE_free(opts->crl_store);
memset(opts, 0, sizeof *opts);
}
void
nc_client_tls_destroy_opts(void)
{
_nc_client_tls_destroy_opts(&tls_opts);
_nc_client_tls_destroy_opts(&tls_ch_opts);
}
static int
_nc_client_tls_set_cert_key_paths(const char *client_cert, const char *client_key, struct nc_client_tls_opts *opts)
{
if (!client_cert) {
ERRARG("client_cert");
return -1;
}
free(opts->cert_path);
free(opts->key_path);
opts->cert_path = strdup(client_cert);
if (!opts->cert_path) {
ERRMEM;
return -1;
}
if (client_key) {
opts->key_path = strdup(client_key);
if (!opts->key_path) {
ERRMEM;
return -1;
}
} else {
opts->key_path = NULL;
}
opts->tls_ctx_change = 1;
return 0;
}
API int
nc_client_tls_set_cert_key_paths(const char *client_cert, const char *client_key)
{
return _nc_client_tls_set_cert_key_paths(client_cert, client_key, &tls_opts);
}
API int
nc_client_tls_ch_set_cert_key_paths(const char *client_cert, const char *client_key)
{
return _nc_client_tls_set_cert_key_paths(client_cert, client_key, &tls_ch_opts);
}
static void
_nc_client_tls_get_cert_key_paths(const char **client_cert, const char **client_key, struct nc_client_tls_opts *opts)
{
if (!client_cert && !client_key) {
ERRARG("client_cert and client_key");
return;
}
if (client_cert) {
*client_cert = opts->cert_path;
}
if (client_key) {
*client_key = opts->key_path;
}
}
API void
nc_client_tls_get_cert_key_paths(const char **client_cert, const char **client_key)
{
_nc_client_tls_get_cert_key_paths(client_cert, client_key, &tls_opts);
}
API void
nc_client_tls_ch_get_cert_key_paths(const char **client_cert, const char **client_key)
{
_nc_client_tls_get_cert_key_paths(client_cert, client_key, &tls_ch_opts);
}
static int
_nc_client_tls_set_trusted_ca_paths(const char *ca_file, const char *ca_dir, struct nc_client_tls_opts *opts)
{
if (!ca_file && !ca_dir) {
ERRARG("ca_file and ca_dir");
return -1;
}
free(opts->ca_file);
free(opts->ca_dir);
if (ca_file) {
opts->ca_file = strdup(ca_file);
if (!opts->ca_file) {
ERRMEM;
return -1;
}
} else {
opts->ca_file = NULL;
}
if (ca_dir) {
opts->ca_dir = strdup(ca_dir);
if (!opts->ca_dir) {
ERRMEM;
return -1;
}
} else {
opts->ca_dir = NULL;
}
opts->tls_ctx_change = 1;
return 0;
}
API int
nc_client_tls_set_trusted_ca_paths(const char *ca_file, const char *ca_dir)
{
return _nc_client_tls_set_trusted_ca_paths(ca_file, ca_dir, &tls_opts);
}
API int
nc_client_tls_ch_set_trusted_ca_paths(const char *ca_file, const char *ca_dir)
{
return _nc_client_tls_set_trusted_ca_paths(ca_file, ca_dir, &tls_ch_opts);
}
static void
_nc_client_tls_get_trusted_ca_paths(const char **ca_file, const char **ca_dir, struct nc_client_tls_opts *opts)
{
if (!ca_file && !ca_dir) {
ERRARG("ca_file and ca_dir");
return;
}
if (ca_file) {
*ca_file = opts->ca_file;
}
if (ca_dir) {
*ca_dir = opts->ca_dir;
}
}
API void
nc_client_tls_get_trusted_ca_paths(const char **ca_file, const char **ca_dir)
{
_nc_client_tls_get_trusted_ca_paths(ca_file, ca_dir, &tls_opts);
}
API void
nc_client_tls_ch_get_trusted_ca_paths(const char **ca_file, const char **ca_dir)
{
_nc_client_tls_get_trusted_ca_paths(ca_file, ca_dir, &tls_ch_opts);
}
static int
_nc_client_tls_set_crl_paths(const char *crl_file, const char *crl_dir, struct nc_client_tls_opts *opts)
{
if (!crl_file && !crl_dir) {
ERRARG("crl_file and crl_dir");
return -1;
}
free(opts->crl_file);
free(opts->crl_dir);
if (crl_file) {
opts->crl_file = strdup(crl_file);
if (!opts->crl_file) {
ERRMEM;
return -1;
}
} else {
opts->crl_file = NULL;
}
if (crl_dir) {
opts->crl_dir = strdup(crl_dir);
if (!opts->crl_dir) {
ERRMEM;
return -1;
}
} else {
opts->crl_dir = NULL;
}
opts->crl_store_change = 1;
return 0;
}
API int
nc_client_tls_set_crl_paths(const char *crl_file, const char *crl_dir)
{
return _nc_client_tls_set_crl_paths(crl_file, crl_dir, &tls_opts);
}
API int
nc_client_tls_ch_set_crl_paths(const char *crl_file, const char *crl_dir)
{
return _nc_client_tls_set_crl_paths(crl_file, crl_dir, &tls_ch_opts);
}
static void
_nc_client_tls_get_crl_paths(const char **crl_file, const char **crl_dir, struct nc_client_tls_opts *opts)
{
if (!crl_file && !crl_dir) {
ERRARG("crl_file and crl_dir");
return;
}
if (crl_file) {
*crl_file = opts->crl_file;
}
if (crl_dir) {
*crl_dir = opts->crl_dir;
}
}
API void
nc_client_tls_get_crl_paths(const char **crl_file, const char **crl_dir)
{
_nc_client_tls_get_crl_paths(crl_file, crl_dir, &tls_opts);
}
API void
nc_client_tls_ch_get_crl_paths(const char **crl_file, const char **crl_dir)
{
_nc_client_tls_get_crl_paths(crl_file, crl_dir, &tls_ch_opts);
}
API int
nc_client_tls_ch_add_bind_listen(const char *address, uint16_t port)
{
return nc_client_ch_add_bind_listen(address, port, NC_TI_OPENSSL);
}
API int
nc_client_tls_ch_del_bind(const char *address, uint16_t port)
{
return nc_client_ch_del_bind(address, port, NC_TI_OPENSSL);
}
static int
nc_client_tls_update_opts(struct nc_client_tls_opts *opts)
{
char *key;
X509_LOOKUP *lookup;
if (!opts->tls_ctx || opts->tls_ctx_change) {
SSL_CTX_free(opts->tls_ctx);
#if OPENSSL_VERSION_NUMBER >= 0x10100000L // >= 1.1.0
/* prepare global SSL context, highest available method is negotiated autmatically */
if (!(opts->tls_ctx = SSL_CTX_new(TLS_client_method())))
#else
/* prepare global SSL context, allow only mandatory TLS 1.2 */
if (!(opts->tls_ctx = SSL_CTX_new(TLSv1_2_client_method())))
#endif
{
ERR(NULL, "Unable to create OpenSSL context (%s).", ERR_reason_error_string(ERR_get_error()));
return -1;
}
SSL_CTX_set_verify(opts->tls_ctx, SSL_VERIFY_PEER, tlsauth_verify_callback);
/* get peer certificate */
if (SSL_CTX_use_certificate_file(opts->tls_ctx, opts->cert_path, SSL_FILETYPE_PEM) != 1) {
ERR(NULL, "Loading the client certificate from \'%s\' failed (%s).", opts->cert_path,
ERR_reason_error_string(ERR_get_error()));
return -1;
}
/* if the file with private key not specified, expect that the private key is stored with the certificate */
if (!opts->key_path) {
key = opts->cert_path;
} else {
key = opts->key_path;
}
if (SSL_CTX_use_PrivateKey_file(opts->tls_ctx, key, SSL_FILETYPE_PEM) != 1) {
ERR(NULL, "Loading the client priavte key from \'%s\' failed (%s).", key,
ERR_reason_error_string(ERR_get_error()));
return -1;
}
if (!SSL_CTX_load_verify_locations(opts->tls_ctx, opts->ca_file, opts->ca_dir)) {
ERR(NULL, "Failed to load the locations of trusted CA certificates (%s).",
ERR_reason_error_string(ERR_get_error()));
return -1;
}
}
if (opts->crl_store_change || (!opts->crl_store && (opts->crl_file || opts->crl_dir))) {
/* set the revocation store with the correct paths for the callback */
X509_STORE_free(opts->crl_store);
opts->crl_store = X509_STORE_new();
if (!opts->crl_store) {
ERR(NULL, "Unable to create a certificate store (%s).", ERR_reason_error_string(ERR_get_error()));
return -1;
}
#if OPENSSL_VERSION_NUMBER < 0x10100000L // < 1.1.0
/* whaveter this does... */
opts->crl_store->cache = 0;
#endif
if (opts->crl_file) {
if (!(lookup = X509_STORE_add_lookup(opts->crl_store, X509_LOOKUP_file()))) {
ERR(NULL, "Failed to add lookup method to CRL checking.");
return -1;
}
if (X509_LOOKUP_add_dir(lookup, opts->crl_file, X509_FILETYPE_PEM) != 1) {
ERR(NULL, "Failed to add the revocation lookup file \"%s\".", opts->crl_file);
return -1;
}
}
if (opts->crl_dir) {
if (!(lookup = X509_STORE_add_lookup(opts->crl_store, X509_LOOKUP_hash_dir()))) {
ERR(NULL, "Failed to add lookup method to CRL checking.");
return -1;
}
if (X509_LOOKUP_add_dir(lookup, opts->crl_dir, X509_FILETYPE_PEM) != 1) {
ERR(NULL, "Failed to add the revocation lookup directory \"%s\".", opts->crl_dir);
return -1;
}
}
}
return 0;
}
API struct nc_session *
nc_connect_tls(const char *host, unsigned short port, struct ly_ctx *ctx)
{
struct nc_session *session = NULL;
int sock, verify, ret;
unsigned long tls_err;
struct timespec ts_timeout, ts_cur;
const char *peername;
char *ip_host = NULL;
if (!tls_opts.cert_path || (!tls_opts.ca_file && !tls_opts.ca_dir)) {
ERRINIT;
return NULL;
}
/* process parameters */
if (!host || strisempty(host)) {
host = "localhost";
}
if (!port) {
port = NC_PORT_TLS;
}
/* create/update TLS structures */
if (nc_client_tls_update_opts(&tls_opts)) {
return NULL;
}
/* prepare session structure */
session = nc_new_session(NC_CLIENT, 0);
if (!session) {
ERRMEM;
return NULL;
}
session->status = NC_STATUS_STARTING;
/* fill the session */
session->ti_type = NC_TI_OPENSSL;
if (!(session->ti.tls = SSL_new(tls_opts.tls_ctx))) {
ERR(NULL, "Failed to create a new TLS session structure (%s).", ERR_reason_error_string(ERR_get_error()));
goto fail;
}
/* create and assign socket */
sock = nc_sock_connect(host, port, -1, &client_opts.ka, NULL, &ip_host);
if (sock == -1) {
ERR(NULL, "Unable to connect to %s:%u (%s).", host, port, strerror(errno));
goto fail;
}
SSL_set_fd(session->ti.tls, sock);
/* set the SSL_MODE_AUTO_RETRY flag to allow OpenSSL perform re-handshake automatically */
SSL_set_mode(session->ti.tls, SSL_MODE_AUTO_RETRY);
#if OPENSSL_VERSION_NUMBER >= 0x10100000L // >= 1.1.0
/* server identity (hostname) verification */
if (!SSL_set1_host(session->ti.tls, host)) {
ERR(NULL, "Failed to set expected server hostname.");
goto fail;
}
#endif
/* connect and perform the handshake */
nc_gettimespec_mono(&ts_timeout);
nc_addtimespec(&ts_timeout, NC_TRANSPORT_TIMEOUT);
tlsauth_ch = 0;
while (((ret = SSL_connect(session->ti.tls)) != 1) && (SSL_get_error(session->ti.tls, ret) == SSL_ERROR_WANT_READ)) {
usleep(NC_TIMEOUT_STEP);
nc_gettimespec_mono(&ts_cur);
if (nc_difftimespec(&ts_cur, &ts_timeout) < 1) {
ERR(NULL, "SSL_connect timeout.");
goto fail;
}
}
if (ret != 1) {
switch (SSL_get_error(session->ti.tls, ret)) {
case SSL_ERROR_SYSCALL:
ERR(NULL, "SSL_connect failed (%s).", errno ? strerror(errno) : "unexpected EOF");
break;
case SSL_ERROR_SSL:
tls_err = ERR_get_error();
ERR(NULL, "SSL_connect failed (%s).", ERR_reason_error_string(tls_err));
break;
default:
ERR(NULL, "SSL_connect failed.");
break;
}
goto fail;
}
/* check certificate verification result */
verify = SSL_get_verify_result(session->ti.tls);
switch (verify) {
case X509_V_OK:
#if OPENSSL_VERSION_NUMBER >= 0x10100000L // >= 1.1.0
peername = SSL_get0_peername(session->ti.tls);
VRB(NULL, "Server certificate successfully verified (domain \"%s\").", peername ? peername : "<unknown>");
#else
(void)peername;
VRB(NULL, "Server certificate successfully verified.");
#endif
break;
default:
WRN(NULL, "Server certificate verification problem (%s).", X509_verify_cert_error_string(verify));
}
if (nc_session_new_ctx(session, ctx) != EXIT_SUCCESS) {
goto fail;
}
ctx = session->ctx;
/* NETCONF handshake */
if (nc_handshake_io(session) != NC_MSG_HELLO) {
goto fail;
}
session->status = NC_STATUS_RUNNING;
if (nc_ctx_check_and_fill(session) == -1) {
goto fail;
}
/* store information into session and the dictionary */
lydict_insert_zc(ctx, ip_host, &session->host);
session->port = port;
lydict_insert(ctx, "certificate-based", 0, &session->username);
return session;
fail:
free(ip_host);
nc_session_free(session, NULL);
return NULL;
}
API struct nc_session *
nc_connect_libssl(SSL *tls, struct ly_ctx *ctx)
{
struct nc_session *session;
if (!tls) {
ERRARG("tls");
return NULL;
} else if (!SSL_is_init_finished(tls)) {
ERR(NULL, "Supplied TLS session is not fully connected!");
return NULL;
}
/* prepare session structure */
session = nc_new_session(NC_CLIENT, 0);
if (!session) {
ERRMEM;
return NULL;
}
session->status = NC_STATUS_STARTING;
session->ti_type = NC_TI_OPENSSL;
session->ti.tls = tls;
if (nc_session_new_ctx(session, ctx) != EXIT_SUCCESS) {
goto fail;
}
ctx = session->ctx;
/* NETCONF handshake */
if (nc_handshake_io(session) != NC_MSG_HELLO) {
goto fail;
}
session->status = NC_STATUS_RUNNING;
if (nc_ctx_check_and_fill(session) == -1) {
goto fail;
}
return session;
fail:
session->ti.tls = NULL;
nc_session_free(session, NULL);
return NULL;
}
struct nc_session *
nc_accept_callhome_tls_sock(int sock, const char *host, uint16_t port, struct ly_ctx *ctx, int timeout)
{
int verify, ret;
SSL *tls = NULL;
struct nc_session *session = NULL;
struct timespec ts_timeout, ts_cur;
if (nc_client_tls_update_opts(&tls_ch_opts)) {
goto cleanup;
}
if (!(tls = SSL_new(tls_ch_opts.tls_ctx))) {
ERR(NULL, "Failed to create new TLS session structure (%s).", ERR_reason_error_string(ERR_get_error()));
goto cleanup;
}
SSL_set_fd(tls, sock);
/* set the SSL_MODE_AUTO_RETRY flag to allow OpenSSL perform re-handshake automatically */
SSL_set_mode(tls, SSL_MODE_AUTO_RETRY);
/* connect and perform the handshake */
if (timeout > -1) {
nc_gettimespec_mono(&ts_timeout);
nc_addtimespec(&ts_timeout, timeout);
}
tlsauth_ch = 1;
while (((ret = SSL_connect(tls)) == -1) && (SSL_get_error(tls, ret) == SSL_ERROR_WANT_READ)) {
usleep(NC_TIMEOUT_STEP);
if (timeout > -1) {
nc_gettimespec_mono(&ts_cur);
if (nc_difftimespec(&ts_cur, &ts_timeout) < 1) {
ERR(NULL, "SSL_connect timeout.");
goto cleanup;
}
}
}
if (ret != 1) {
switch (SSL_get_error(tls, ret)) {
case SSL_ERROR_SYSCALL:
ERR(NULL, "SSL_connect failed (%s).", strerror(errno));
break;
case SSL_ERROR_SSL:
ERR(NULL, "SSL_connect failed (%s).", ERR_reason_error_string(ERR_get_error()));
break;
default:
ERR(NULL, "SSL_connect failed.");
break;
}
goto cleanup;
}
/* check certificate verification result */
verify = SSL_get_verify_result(tls);
switch (verify) {
case X509_V_OK:
VRB(NULL, "Server certificate successfully verified.");
break;
default:
WRN(NULL, "Server certificate verification problem (%s).", X509_verify_cert_error_string(verify));
}
/* connect */
session = nc_connect_libssl(tls, ctx);
if (!session) {
goto cleanup;
}
session->flags |= NC_SESSION_CALLHOME;
/* store information into session and the dictionary */
lydict_insert(session->ctx, host, 0, &session->host);
session->port = port;
lydict_insert(session->ctx, "certificate-based", 0, &session->username);
cleanup:
if (!session) {
SSL_free(tls);
close(sock);
}
return session;
}

861
src/session_p.h Normal file
View file

@ -0,0 +1,861 @@
/**
* @file session_p.h
* @author Radek Krejci <rkrejci@cesnet.cz>
* @author Michal Vasko <mvasko@cesnet.cz>
* @brief libnetconf2 session manipulation
*
* Copyright (c) 2017 - 2021 CESNET, z.s.p.o.
*
* This source code is licensed under BSD 3-Clause License (the "License").
* You may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://opensource.org/licenses/BSD-3-Clause
*/
#ifndef NC_SESSION_PRIVATE_H_
#define NC_SESSION_PRIVATE_H_
#include <pthread.h>
#include <stdint.h>
#include <sys/stat.h>
#include <libyang/libyang.h>
#include "compat.h"
#include "libnetconf.h"
#include "messages_client.h"
#include "netconf.h"
#include "session.h"
#ifdef NC_ENABLED_SSH
# include <libssh/callbacks.h>
# include <libssh/libssh.h>
# include <libssh/server.h>
/* seconds */
# define NC_SSH_TIMEOUT 10
/* number of all supported authentication methods */
# define NC_SSH_AUTH_COUNT 3
/* ACCESS unlocked */
struct nc_client_ssh_opts {
/* SSH authentication method preferences */
struct {
NC_SSH_AUTH_TYPE type;
int16_t value;
} auth_pref[NC_SSH_AUTH_COUNT];
/* SSH key pairs */
struct {
char *pubkey_path;
char *privkey_path;
int8_t privkey_crypt;
} *keys;
uint16_t key_count;
/* SSH authentication callbacks */
int (*auth_hostkey_check)(const char *, ssh_session, void *);
char *(*auth_password)(const char *, const char *, void *);
char *(*auth_interactive)(const char *, const char *, const char *, int, void *);
char *(*auth_privkey_passphrase)(const char *, void *);
/* private data for the callbacks */
void *auth_hostkey_check_priv;
void *auth_password_priv;
void *auth_interactive_priv;
void *auth_privkey_passphrase_priv;
char *username;
};
/* ACCESS locked, separate locks */
struct nc_server_ssh_opts {
/* SSH bind options */
const char **hostkeys;
uint8_t hostkey_count;
int auth_methods;
uint16_t auth_attempts;
uint16_t auth_timeout;
};
#endif /* NC_ENABLED_SSH */
#ifdef NC_ENABLED_TLS
# include <openssl/bio.h>
# include <openssl/ssl.h>
/* ACCESS unlocked */
struct nc_client_tls_opts {
char *cert_path;
char *key_path;
char *ca_file;
char *ca_dir;
int8_t tls_ctx_change;
SSL_CTX *tls_ctx;
char *crl_file;
char *crl_dir;
int8_t crl_store_change;
X509_STORE *crl_store;
};
/* ACCESS locked, separate locks */
struct nc_server_tls_opts {
const char *server_cert;
const char **trusted_cert_lists;
uint16_t trusted_cert_list_count;
const char *trusted_ca_file;
const char *trusted_ca_dir;
X509_STORE *crl_store;
struct nc_ctn {
uint32_t id;
const char *fingerprint;
NC_TLS_CTN_MAPTYPE map_type;
const char *name;
struct nc_ctn *next;
} *ctn;
};
#endif /* NC_ENABLED_TLS */
/* ACCESS unlocked */
struct nc_keepalives {
int enabled;
uint16_t idle_time;
uint16_t max_probes;
uint16_t probe_interval;
};
/* ACCESS unlocked */
struct nc_server_unix_opts {
mode_t mode;
uid_t uid;
gid_t gid;
};
/* ACCESS unlocked */
struct nc_client_opts {
char *schema_searchpath;
ly_module_imp_clb schema_clb;
void *schema_clb_data;
struct nc_keepalives ka;
struct nc_bind {
const char *address;
uint16_t port;
int sock;
int pollin;
} *ch_binds;
NC_TRANSPORT_IMPL *ch_bind_ti;
uint16_t ch_bind_count;
};
/* ACCESS unlocked */
struct nc_client_context {
unsigned int refcount;
struct nc_client_opts opts;
#ifdef NC_ENABLED_SSH
struct nc_client_ssh_opts ssh_opts;
struct nc_client_ssh_opts ssh_ch_opts;
#endif /* NC_ENABLED_SSH */
#ifdef NC_ENABLED_TLS
struct nc_client_tls_opts tls_opts;
struct nc_client_tls_opts tls_ch_opts;
#endif /* NC_ENABLED_TLS */
};
struct nc_server_opts {
/* ACCESS unlocked (dictionary locked internally in libyang) */
struct ly_ctx *ctx;
/* ACCESS unlocked */
NC_WD_MODE wd_basic_mode;
int wd_also_supported;
uint32_t capabilities_count;
const char **capabilities;
char *(*content_id_clb)(void *user_data);
void *content_id_data;
void (*content_id_data_free)(void *data);
/* ACCESS unlocked */
uint16_t hello_timeout;
uint16_t idle_timeout;
#ifdef NC_ENABLED_SSH
int (*passwd_auth_clb)(const struct nc_session *session, const char *password, void *user_data);
void *passwd_auth_data;
void (*passwd_auth_data_free)(void *data);
int (*pubkey_auth_clb)(const struct nc_session *session, ssh_key key, void *user_data);
void *pubkey_auth_data;
void (*pubkey_auth_data_free)(void *data);
int (*interactive_auth_clb)(const struct nc_session *session, ssh_message msg, void *user_data);
void *interactive_auth_data;
void (*interactive_auth_data_free)(void *data);
#endif
#ifdef NC_ENABLED_TLS
int (*user_verify_clb)(const struct nc_session *session);
int (*server_cert_clb)(const char *name, void *user_data, char **cert_path, char **cert_data, char **privkey_path,
char **privkey_data, NC_SSH_KEY_TYPE *privkey_type);
void *server_cert_data;
void (*server_cert_data_free)(void *data);
int (*server_cert_chain_clb)(const char *name, void *user_data, char ***cert_paths, int *cert_path_count,
char ***cert_data, int *cert_data_count);
void *server_cert_chain_data;
void (*server_cert_chain_data_free)(void *data);
int (*trusted_cert_list_clb)(const char *name, void *user_data, char ***cert_paths, int *cert_path_count,
char ***cert_data, int *cert_data_count);
void *trusted_cert_list_data;
void (*trusted_cert_list_data_free)(void *data);
#endif
#ifdef NC_ENABLED_SSH
/* ACCESS locked with authkey_lock */
struct {
const char *path;
const char *base64;
NC_SSH_KEY_TYPE type;
const char *username;
} *authkeys;
uint16_t authkey_count;
pthread_mutex_t authkey_lock;
int (*hostkey_clb)(const char *name, void *user_data, char **privkey_path, char **privkey_data, NC_SSH_KEY_TYPE *privkey_type);
void *hostkey_data;
void (*hostkey_data_free)(void *data);
#endif
/* ACCESS locked, add/remove endpts/binds - bind_lock + WRITE endpt_lock (strict order!)
* modify endpts - WRITE endpt_lock
* access endpts - READ endpt_lock
* modify/poll binds - bind_lock */
struct nc_bind *binds;
pthread_mutex_t bind_lock;
struct nc_endpt {
const char *name;
NC_TRANSPORT_IMPL ti;
struct nc_keepalives ka;
union {
#ifdef NC_ENABLED_SSH
struct nc_server_ssh_opts *ssh;
#endif
#ifdef NC_ENABLED_TLS
struct nc_server_tls_opts *tls;
#endif
struct nc_server_unix_opts *unixsock;
} opts;
} *endpts;
uint16_t endpt_count;
pthread_rwlock_t endpt_lock;
/* ACCESS locked, add/remove CH clients - WRITE lock ch_client_lock
* modify CH clients - READ lock ch_client_lock + ch_client_lock */
struct nc_ch_client {
const char *name;
struct nc_ch_endpt {
const char *name;
NC_TRANSPORT_IMPL ti;
const char *address;
uint16_t port;
int sock_pending;
int sock_retries;
struct nc_keepalives ka;
union {
#ifdef NC_ENABLED_SSH
struct nc_server_ssh_opts *ssh;
#endif
#ifdef NC_ENABLED_TLS
struct nc_server_tls_opts *tls;
#endif
} opts;
} *ch_endpts;
uint16_t ch_endpt_count;
NC_CH_CONN_TYPE conn_type;
union {
struct {
uint16_t period;
time_t anchor_time;
uint16_t idle_timeout;
} period;
} conn;
NC_CH_START_WITH start_with;
uint8_t max_attempts;
uint32_t id;
pthread_mutex_t lock;
} *ch_clients;
uint16_t ch_client_count;
pthread_rwlock_t ch_client_lock;
/* Atomic IDs */
ATOMIC_T new_session_id;
ATOMIC_T new_client_id;
};
/**
* Sleep time in usec to wait between nc_recv_notif() calls.
*/
#define NC_CLIENT_NOTIF_THREAD_SLEEP 10000
/**
* Timeout in msec for transport-related data to arrive (ssh_handle_key_exchange(), SSL_accept(), SSL_connect()).
* It can be quite a lot on slow machines (waiting for TLS cert-to-name resolution, ...).
*/
#define NC_TRANSPORT_TIMEOUT 10000
/**
* Timeout in msec for acquiring a lock of a session (used with a condition, so higher numbers could be required
* only in case of extreme concurrency).
*/
#define NC_SESSION_LOCK_TIMEOUT 500
/**
* Timeout in msec for acquiring a lock of a session that is supposed to be freed.
*/
#define NC_SESSION_FREE_LOCK_TIMEOUT 1000
/**
* Timeout in msec for acquiring a lock of a pollsession structure.
*/
#define NC_PS_LOCK_TIMEOUT 200
/**
* Timeout in msec for a thread to wait for its turn to work with a pollsession structure.
*/
#define NC_PS_QUEUE_TIMEOUT 5000
/**
* Time slept in msec if no endpoint was created for a running Call Home client.
*/
#define NC_CH_NO_ENDPT_WAIT 1000
/**
* Time slept in msec after a failed Call Home endpoint session creation.
*/
#define NC_CH_ENDPT_FAIL_WAIT 1000
/**
* Number of sockets kept waiting to be accepted.
*/
#define NC_REVERSE_QUEUE 5
/**
* Timeout for connecting Call Home socket to a client (s).
*/
#define NC_SOCKET_CH_TIMEOUT 5
/**
* Number of retires of connection Call Home socket to a client.
*/
#define NC_SOCKET_CH_RETRIES 5
/**
* @brief Type of the session
*/
typedef enum {
NC_CLIENT, /**< client side */
NC_SERVER /**< server side */
} NC_SIDE;
/**
* @brief Enumeration of the supported NETCONF protocol versions
*/
typedef enum {
NC_VERSION_10 = 0, /**< NETCONF 1.0 - RFC 4741, 4742 */
NC_VERSION_11 = 1 /**< NETCONF 1.1 - RFC 6241, 6242 */
} NC_VERSION;
#define NC_VERSION_10_ENDTAG "]]>]]>"
#define NC_VERSION_10_ENDTAG_LEN 6
/**
* @brief Container to serialize RPC messages
*/
struct nc_msg_cont {
struct ly_in *msg;
NC_MSG_TYPE type; /**< can be either NC_MSG_REPLY or NC_MSG_NOTIF */
struct nc_msg_cont *next;
};
/**
* @brief NETCONF session structure
*/
struct nc_session {
NC_STATUS status; /**< status of the session */
NC_SESSION_TERM_REASON term_reason; /**< reason of termination, if status is NC_STATUS_INVALID */
uint32_t killed_by; /**< session responsible for termination, if term_reason is NC_SESSION_TERM_KILLED */
NC_SIDE side; /**< side of the session: client or server */
/* NETCONF data */
uint32_t id; /**< NETCONF session ID (session-id-type) */
NC_VERSION version; /**< NETCONF protocol version */
/* Transport implementation */
NC_TRANSPORT_IMPL ti_type; /**< transport implementation type to select items from ti union */
pthread_mutex_t *io_lock; /**< input/output lock, note that in case of libssh TI, it will be shared with
other NETCONF sessions on the same SSH session (but different SSH channel) */
union {
struct {
int in; /**< input file descriptor */
int out; /**< output file descriptor */
} fd; /**< NC_TI_FD transport implementation structure */
struct {
int sock; /**< socket file descriptor */
} unixsock; /**< NC_TI_UNIX transport implementation structure */
#ifdef NC_ENABLED_SSH
struct {
ssh_channel channel;
ssh_session session;
struct nc_session *next; /**< pointer to the next NETCONF session on the same
SSH session, but different SSH channel. If no such session exists, it is NULL.
otherwise there is a ring list of the NETCONF sessions */
} libssh;
#endif
#ifdef NC_ENABLED_TLS
SSL *tls;
#endif
} ti; /**< transport implementation data */
const char *username;
const char *host;
uint16_t port;
const char *path; /**< socket path in case of unix socket */
/* other */
struct ly_ctx *ctx; /**< libyang context of the session */
void *data; /**< arbitrary user data */
uint8_t flags; /**< various flags of the session */
#define NC_SESSION_SHAREDCTX 0x01
#define NC_SESSION_CALLHOME 0x02
union {
struct {
/* client side only data */
uint64_t msgid;
char **cpblts; /**< list of server's capabilities on client side */
pthread_mutex_t msgs_lock; /**< lock for the msgs buffer */
struct nc_msg_cont *msgs; /**< queue for messages received of different type than expected */
ATOMIC_T ntf_thread; /**< flag whether notification thread for this session is running or not,
2 means it should quit */
/* client flags */
/* some server modules failed to load so the data from them will be ignored - not use strict flag for parsing */
# define NC_SESSION_CLIENT_NOT_STRICT 0x40
} client;
struct {
/* server side only data */
time_t session_start; /**< real time the session was created */
time_t last_rpc; /**< monotonic time (seconds) the last RPC was received on this session */
int ntf_status; /**< flag (count) whether the session is subscribed to notifications */
pthread_mutex_t rpc_lock; /**< lock indicating RPC processing, this lock is always locked before io_lock!! */
pthread_cond_t rpc_cond; /**< RPC condition (tied with rpc_lock and rpc_inuse) */
int rpc_inuse; /**< variable indicating whether there is RPC being processed or not (tied with
rpc_cond and rpc_lock) */
pthread_mutex_t ch_lock; /**< Call Home thread lock */
pthread_cond_t ch_cond; /**< Call Home thread condition */
/* server flags */
#ifdef NC_ENABLED_SSH
/* SSH session authenticated */
# define NC_SESSION_SSH_AUTHENTICATED 0x04
/* netconf subsystem requested */
# define NC_SESSION_SSH_SUBSYS_NETCONF 0x08
/* new SSH message arrived */
# define NC_SESSION_SSH_NEW_MSG 0x10
/* this session is passed to nc_sshcb_msg() */
# define NC_SESSION_SSH_MSG_CB 0x20
uint16_t ssh_auth_attempts; /**< number of failed SSH authentication attempts */
#endif
#ifdef NC_ENABLED_TLS
X509 *client_cert; /**< TLS client certificate if used for authentication */
#endif
} server;
} opts;
};
enum nc_ps_session_state {
NC_PS_STATE_NONE = 0, /**< session is not being worked with */
NC_PS_STATE_BUSY, /**< session is being polled or communicated on (and locked) */
NC_PS_STATE_INVALID /**< session is invalid and was already returned by another poll */
};
struct nc_ps_session {
struct nc_session *session;
enum nc_ps_session_state state;
};
/* ACCESS locked */
struct nc_pollsession {
struct nc_ps_session **sessions;
uint16_t session_count;
uint16_t last_event_session;
pthread_cond_t cond;
pthread_mutex_t lock;
uint8_t queue[NC_PS_QUEUE_SIZE]; /**< round buffer, queue is empty when queue_len == 0 */
uint8_t queue_begin; /**< queue starts on queue[queue_begin] */
uint8_t queue_len; /**< queue ends on queue[(queue_begin + queue_len - 1) % NC_PS_QUEUE_SIZE] */
};
struct nc_ntf_thread_arg {
struct nc_session *session;
void (*notif_clb)(struct nc_session *session, const struct lyd_node *envp, const struct lyd_node *op);
};
void *nc_realloc(void *ptr, size_t size);
struct passwd *nc_getpwuid(uid_t uid, struct passwd *pwd_buf, char **buf, size_t *buf_size);
NC_MSG_TYPE nc_send_msg_io(struct nc_session *session, int io_timeout, struct lyd_node *op);
int nc_gettimespec_mono(struct timespec *ts);
int nc_gettimespec_real(struct timespec *ts);
int32_t nc_difftimespec(const struct timespec *ts1, const struct timespec *ts2);
void nc_addtimespec(struct timespec *ts, uint32_t msec);
const char *nc_keytype2str(NC_SSH_KEY_TYPE type);
int nc_sock_enable_keepalive(int sock, struct nc_keepalives *ka);
struct nc_session *nc_new_session(NC_SIDE side, int shared_ti);
int nc_session_rpc_lock(struct nc_session *session, int timeout, const char *func);
int nc_session_rpc_unlock(struct nc_session *session, int timeout, const char *func);
/**
* @brief Lock IO lock on a session.
*
* @param[in] session Session to lock.
* @param[in] timeout Timeout in msec to use.
* @param[in] func Caller function for logging.
* @return 1 on success;
* @return 0 on timeout;
* @return -1 on error.
*/
int nc_session_io_lock(struct nc_session *session, int timeout, const char *func);
/**
* @brief Unlock IO lock on a session.
*
* @param[in] session Session to unlock.
* @param[in] func Caller function for logging.
* @return 1 on success;
* @return -1 on error.
*/
int nc_session_io_unlock(struct nc_session *session, const char *func);
/**
* @brief Lock MSGS lock on a session.
*
* @param[in] session Session to lock.
* @param[in,out] timeout Timeout in msec to use. If positive and on successful lock, is updated based on what was elapsed.
* @param[in] func Caller function for logging.
* @return 1 on success;
* @return 0 on timeout;
* @return -1 on error.
*/
int nc_session_client_msgs_lock(struct nc_session *session, int *timeout, const char *func);
/**
* @brief Unlock MSGS lock on a session.
*
* @param[in] session Session to unlock.
* @param[in] func Caller function for logging.
* @return 1 on success;
* @return -1 on error.
*/
int nc_session_client_msgs_unlock(struct nc_session *session, const char *func);
int nc_ps_lock(struct nc_pollsession *ps, uint8_t *id, const char *func);
int nc_ps_unlock(struct nc_pollsession *ps, uint8_t id, const char *func);
/**
* @brief Fill libyang context in @p session. Context models are based on the stored session
* capabilities. If the server does not support \<get-schema\>, the models are searched
* for in the directory set using nc_client_schema_searchpath().
*
* @param[in] session Session to create the context for.
* @return 0 on success, 1 on some missing schemas, -1 on error.
*/
int nc_ctx_check_and_fill(struct nc_session *session);
/**
* @brief Perform NETCONF handshake on @p session.
*
* @param[in] session NETCONF session to use.
* @return NC_MSG_HELLO on success, NC_MSG_BAD_HELLO on client \<hello\> message parsing fail
* (server-side only), NC_MSG_WOULDBLOCK on timeout, NC_MSG_ERROR on other error.
*/
NC_MSG_TYPE nc_handshake_io(struct nc_session *session);
/**
* @brief Create a socket connection.
*
* @param[in] host Hostname to connect to.
* @param[in] port Port to connect on.
* @param[in] timeout for blocking the connect+select call (-1 for infinite).
* @param[in] ka Keepalives parameters.
* @param[in,out] sock_pending for exchanging the pending socket, if the blocking timeout was != -1
* @param[out] ip_host Optional parameter with string IP address of the connected host.
* @return Connected socket or -1 on error.
*/
int nc_sock_connect(const char *host, uint16_t port, int timeout, struct nc_keepalives *ka, int *sock_pending, char **ip_host);
/**
* @brief Accept a new socket connection.
*
* @param[in] sock Listening socket.
* @param[in] timeout Timeout in milliseconds.
* @param[out] peer_host Host the new connection was initiated from. Can be NULL.
* @param[out] peer_port Port the new connection is connected on. Can be NULL.
* @return Connected socket with the new connection, -1 on error.
*/
int nc_sock_accept(int sock, int timeout, char **peer_host, uint16_t *peer_port);
/**
* @brief Create a listening socket (AF_INET or AF_INET6).
*
* @param[in] address IP address to listen on.
* @param[in] port Port to listen on.
* @param[in] ka Keepalives parameters.
* @return Listening socket, -1 on error.
*/
int nc_sock_listen_inet(const char *address, uint16_t port, struct nc_keepalives *ka);
/**
* @brief Create a listening socket (AF_UNIX).
*
* @param[in] address UNIX address to listen on.
* @param[in] opts The server options (unix permissions).
* @return Listening socket, -1 on error.
*/
int nc_sock_listen_unix(const char *address, const struct nc_server_unix_opts *opts);
/**
* @brief Accept a new connection on a listening socket.
*
* @param[in] binds Structure with the listening sockets.
* @param[in] bind_count Number of @p binds.
* @param[in] timeout Timeout for accepting.
* @param[out] host Host of the remote peer. Can be NULL.
* @param[out] port Port of the new connection. Can be NULL.
* @param[out] idx Index of the bind that was accepted. Can be NULL.
* @return Accepted socket of the new connection, -1 on error.
*/
int nc_sock_accept_binds(struct nc_bind *binds, uint16_t bind_count, int timeout, char **host, uint16_t *port, uint16_t *idx);
/**
* @brief Lock endpoint structures for reading and the specific endpoint.
*
* @param[in] name Name of the endpoint.
* @param[in] ti Expected transport.
* @param[out] idx Index of the endpoint. Optional.
* @return Endpoint structure.
*/
struct nc_endpt *nc_server_endpt_lock_get(const char *name, NC_TRANSPORT_IMPL ti, uint16_t *idx);
/**
* @brief Lock CH client structures for reading and lock the specific client.
*
* @param[in] name Name of the CH client.
* @param[in] endpt_name Endpoint of the CH client.
* @param[in] ti Expected transport.
* @param[out] client_p Pointer to the CH client.
* @return CH endpoint structure.
*/
struct nc_ch_endpt *nc_server_ch_client_lock(const char *name, const char *endpt_name, NC_TRANSPORT_IMPL ti,
struct nc_ch_client **client_p);
/**
* @brief Unlock CH client strcutures and the specific client.
*
* @param[in] endpt Locked CH client structure.
*/
void nc_server_ch_client_unlock(struct nc_ch_client *client);
/**
* @brief Add a client Call Home bind, listen on it.
*
* @param[in] address Address to bind to.
* @param[in] port Port to bind to.
* @param[in] ti Transport to use.
* @return 0 on success, -1 on error.
*/
int nc_client_ch_add_bind_listen(const char *address, uint16_t port, NC_TRANSPORT_IMPL ti);
/**
* @brief Remove a client Call Home bind, stop listening on it.
*
* @param[in] address Address of the bind. NULL matches any address.
* @param[in] port Port of the bind. 0 matches all ports.
* @param[in] ti Transport of the bind. 0 matches all transports.
* @return 0 on success, -1 on no matches found.
*/
int nc_client_ch_del_bind(const char *address, uint16_t port, NC_TRANSPORT_IMPL ti);
/**
* @brief Connect to a listening NETCONF client using Call Home.
*
* @param[in] host Hostname to connect to.
* @param[in] port Port to connect to.
* @param[in] ti Transport fo the connection.
* @param[out] session New Call Home session.
* @return NC_MSG_HELLO on success, NC_MSG_BAD_HELLO on client \<hello\> message
* parsing fail, NC_MSG_WOULDBLOCK on timeout, NC_MSG_ERROR on other errors.
*/
NC_MSG_TYPE nc_connect_callhome(const char *host, uint16_t port, NC_TRANSPORT_IMPL ti, struct nc_session **session);
void nc_init(void);
void nc_destroy(void);
#ifdef NC_ENABLED_SSH
/**
* @brief Accept a server Call Home connection on a socket.
*
* @param[in] sock Socket with a new connection.
* @param[in] host Hostname of the server.
* @param[in] port Port of the server.
* @param[in] ctx Context for the session. Can be NULL.
* @param[in] timeout Transport operations timeout in msec.
* @return New session, NULL on error.
*/
struct nc_session *nc_accept_callhome_ssh_sock(int sock, const char *host, uint16_t port, struct ly_ctx *ctx, int timeout);
/**
* @brief Establish SSH transport on a socket.
*
* @param[in] session Session structure of the new connection.
* @param[in] sock Socket of the new connection.
* @param[in] timeout Transport operations timeout in msec (not SSH authentication one).
* @return 1 on success, 0 on timeout, -1 on error.
*/
int nc_accept_ssh_session(struct nc_session *session, int sock, int timeout);
/**
* @brief Callback called when a new SSH message is received.
*
* @param[in] sshsession SSH session the message arrived on.
* @param[in] msg SSH message itself.
* @param[in] data NETCONF session running on @p sshsession.
* @return 0 if the message was handled, 1 if it is left up to libssh.
*/
int nc_sshcb_msg(ssh_session sshsession, ssh_message msg, void *data);
void nc_server_ssh_clear_opts(struct nc_server_ssh_opts *opts);
void nc_client_ssh_destroy_opts(void);
void _nc_client_ssh_destroy_opts(struct nc_client_ssh_opts *opts);
#endif /* NC_ENABLED_SSH */
#ifdef NC_ENABLED_TLS
struct nc_session *nc_accept_callhome_tls_sock(int sock, const char *host, uint16_t port, struct ly_ctx *ctx, int timeout);
/**
* @brief Establish TLS transport on a socket.
*
* @param[in] session Session structure of the new connection.
* @param[in] sock Socket of the new connection.
* @param[in] timeout Transport operations timeout in msec.
* @return 1 on success, 0 on timeout, -1 on error.
*/
int nc_accept_tls_session(struct nc_session *session, int sock, int timeout);
void nc_server_tls_clear_opts(struct nc_server_tls_opts *opts);
void nc_client_tls_destroy_opts(void);
void _nc_client_tls_destroy_opts(struct nc_client_tls_opts *opts);
#endif /* NC_ENABLED_TLS */
/**
* Functions
* - io.c
*/
/**
* @brief Read message from the wire.
*
* Accepts hello, rpc, rpc-reply and notification. Received string is transformed into
* libyang XML tree and the message type is detected from the top level element.
*
* @param[in] session NETCONF session from which the message is being read.
* @param[in] io_timeout Timeout in milliseconds. Negative value means infinite timeout,
* zero value causes to return immediately.
* @param[out] msg Input handled with the NETCONF message (application layer data).
* @return 1 on success.
* @return 0 on timeout.
* @return -1 on error.
* @return -2 on malformed message error.
*/
int nc_read_msg_poll_io(struct nc_session *session, int io_timeout, struct ly_in **msg);
/**
* @brief Read a message from the wire.
*
* @param[in] session NETCONF session from which the message is being read.
* @param[in] io_timeout Timeout in milliseconds. Negative value means infinite timeout,
* zero value causes to return immediately.
* @param[out] msg Input handled with the NETCONF message (application layer data).
* @param[in] passing_io_lock True if @p session IO lock is already held. This function always unlocks
* it before returning!
* @return 1 on success.
* @return 0 on timeout.
* @return -1 on error.
* @return -2 on malformed message error.
*/
int nc_read_msg_io(struct nc_session *session, int io_timeout, struct ly_in **msg, int passing_io_lock);
/**
* @brief Write message into wire.
*
* @param[in] session NETCONF session to which the message will be written.
* @param[in] io_timeout Timeout in milliseconds. Negative value means infinite timeout,
* zero value causes to return immediately.
* @param[in] type The type of the message to write, specified as #NC_MSG_TYPE value. According to the type, the
* specific additional parameters are required or accepted:
* - #NC_MSG_RPC
* - `struct lyd_node *op;` - operation (content of the \<rpc/\> to be sent. Required parameter.
* - `const char *attrs;` - additional attributes to be added into the \<rpc/\> element. Required parameter.
* - #NC_MSG_REPLY
* - `struct lyd_node_opaq *rpc_envp;` - parsed envelopes of the RPC to reply to. Required parameter.
* - `struct nc_server_reply *reply;` - RPC reply. Required parameter.
* - #NC_MSG_NOTIF
* - `struct nc_server_notif *notif;` - notification object. Required parameter.
* - #NC_MSG_HELLO
* - `const char **capabs;` - capabilities array ended with NULL. Required parameter.
* - `uint32_t *sid;` - session ID to be included in the hello message. Optional parameter.
*
* @return Type of the written message. #NC_MSG_WOULDBLOCK is returned if timeout is positive
* (or zero) value and IO lock could not be acquired in that time. #NC_MSG_ERROR is
* returned on error and #NC_MSG_NONE is never returned by this function.
*/
NC_MSG_TYPE nc_write_msg_io(struct nc_session *session, int io_timeout, int type, ...);
/**
* @brief Check whether a session is still connected (on transport layer).
*
* @param[in] session Session to check.
* @return 1 if connected, 0 if not.
*/
int nc_session_is_connected(struct nc_session *session);
#endif /* NC_SESSION_PRIVATE_H_ */

3665
src/session_server.c Normal file

File diff suppressed because it is too large Load diff

930
src/session_server.h Normal file
View file

@ -0,0 +1,930 @@
/**
* @file session_server.h
* @author Michal Vasko <mvasko@cesnet.cz>
* @brief libnetconf2 session server manipulation
*
* Copyright (c) 2015 - 2021 CESNET, z.s.p.o.
*
* This source code is licensed under BSD 3-Clause License (the "License").
* You may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://opensource.org/licenses/BSD-3-Clause
*/
#ifndef NC_SESSION_SERVER_H_
#define NC_SESSION_SERVER_H_
#ifdef __cplusplus
extern "C" {
#endif
#include <libyang/libyang.h>
#include <stdint.h>
#ifdef NC_ENABLED_TLS
# include <openssl/x509.h>
#endif
#ifdef NC_ENABLED_SSH
# include <libssh/callbacks.h>
# include <libssh/libssh.h>
# include <libssh/server.h>
#endif
#include "netconf.h"
#include "session.h"
/**
* @defgroup server_session Server Session
* @ingroup server
*
* @brief Server-side NETCONF session manipulation.
* @{
*/
/**
* @brief Prototype of callbacks that are called if some RPCs are received.
*
* If @p session termination reason is changed in the callback, one last reply
* is sent and then the session is considered invalid.
*
* The callback is set via nc_set_global_rpc_clb().
*
* @param[in] rpc Parsed client RPC request.
* @param[in] session Session the RPC arrived on.
* @return Server reply. If NULL, an operation-failed error will be sent to the client.
*/
typedef struct nc_server_reply *(*nc_rpc_clb)(struct lyd_node *rpc, struct nc_session *session);
/**
* @brief Set the termination reason for a session. Use only in #nc_rpc_clb callbacks.
*
* @param[in] session Session to modify.
* @param[in] reason Reason of termination.
*/
void nc_session_set_term_reason(struct nc_session *session, NC_SESSION_TERM_REASON reason);
/**
* @brief Set the session-id of the session responsible for this session's termination.
*
* @param[in] session Session to modify. Must have term_reason set to #NC_SESSION_TERM_KILLED.
* @param[in] sid SID of the killing session.
*/
void nc_session_set_killed_by(struct nc_session *session, uint32_t sid);
/**
* @brief Set the status of a session.
*
* @param[in] session Session to modify.
* @param[in] status Status of the session.
*/
void nc_session_set_status(struct nc_session *session, NC_STATUS status);
/**
* @brief Set a global nc_rpc_clb that is called if the particular RPC request is
* received and the private field in the corresponding RPC schema node is NULL.
*
* @param[in] clb An user-defined nc_rpc_clb function callback, NULL to default.
*/
void nc_set_global_rpc_clb(nc_rpc_clb clb);
/** @} Server Session */
/**
* @addtogroup server
* @{
*/
/**
* @brief Initialize libssh and/or libssl/libcrypto and the server using a libyang context.
*
* The context is not modified internally, only its dictionary is used for holding
* all the strings, which is thread-safe. Reading models is considered thread-safe
* as models cannot be removed and are rarely modified (augments or deviations).
*
* If the RPC callbacks on schema nodes (mentioned in @ref howtoserver) are modified after
* server initialization with that particular context, they will be called (changes
* will take effect). However, there could be race conditions as the access to
* these callbacks is not thread-safe.
*
* Server capabilities are generated based on its content. Changing the context
* in ways that result in changed capabilities (adding models, changing features)
* is discouraged after sessions are established as it is not possible to change
* capabilities of a session.
*
* This context can safely be destroyed only after calling the last libnetconf2
* function in an application.
*
* Supported RPCs of models in the context are expected to have their callback
* in the corresponding RPC schema node set to a nc_rpc_clb function callback using nc_set_rpc_callback().
* This callback is called by nc_ps_poll() if the particular RPC request is
* received. Callbacks for ietf-netconf:get-schema (supporting YANG and YIN format
* only) and ietf-netconf:close-session are set internally if left unset.
*
* @param[in] ctx Core NETCONF server context.
* @return 0 on success, -1 on error.
*/
int nc_server_init(struct ly_ctx *ctx);
/**
* @brief Destroy any dynamically allocated libssh and/or libssl/libcrypto and
* server resources.
*/
void nc_server_destroy(void);
/**
* @brief Set the with-defaults capability extra parameters.
*
* For the capability to be actually advertised, the server context must also
* include the ietf-netconf-with-defaults model.
*
* Changing this option has the same ill effects as changing capabilities while
* sessions are already established.
*
* @param[in] basic_mode basic-mode with-defaults parameter.
* @param[in] also_supported NC_WD_MODE bit array, also-supported with-defaults
* parameter.
* @return 0 on success, -1 on error.
*/
int nc_server_set_capab_withdefaults(NC_WD_MODE basic_mode, int also_supported);
/**
* @brief Get with-defaults capability extra parameters.
*
* At least one argument must be non-NULL.
*
* @param[in,out] basic_mode basic-mode parameter.
* @param[in,out] also_supported also-supported parameter.
*/
void nc_server_get_capab_withdefaults(NC_WD_MODE *basic_mode, int *also_supported);
/**
* @brief Set capability of the server.
*
* Capability can be used when some behavior or extension of the server is not defined
* as a YANG module. The provided value will be advertised in the server's \<hello\>
* messages. Note, that libnetconf only checks that the provided value is non-empty
* string.
*
* @param[in] value Capability string to be advertised in server's \<hello\> messages.
*/
int nc_server_set_capability(const char *value);
/**
* @brief Set the callback for getting yang-library capability identifier. If none is set, libyang context change count is used.
*
* @param[in] content_id_clb Callback that should return the yang-library content identifier.
* @param[in] user_data Optional arbitrary user data that will be passed to @p content_id_clb.
* @param[in] free_user_data Optional callback that will be called during cleanup to free any @p user_data.
*/
void nc_server_set_content_id_clb(char *(*content_id_clb)(void *user_data), void *user_data,
void (*free_user_data)(void *user_data));
/**
* @brief Set server timeout for receiving a hello message.
*
* @param[in] hello_timeout Hello message timeout. 0 for infinite waiting.
*/
void nc_server_set_hello_timeout(uint16_t hello_timeout);
/**
* @brief get server timeout for receiving a hello message.
*
* @return Hello message timeout, 0 is infinite.
*/
uint16_t nc_server_get_hello_timeout(void);
/**
* @brief Set server timeout for dropping an idle session.
*
* @param[in] idle_timeout Idle session timeout. 0 to never drop a session
* because of inactivity.
*/
void nc_server_set_idle_timeout(uint16_t idle_timeout);
/**
* @brief Get server timeout for dropping an idle session.
*
* @return Idle session timeout, 0 for for never dropping
* a session because of inactivity.
*/
uint16_t nc_server_get_idle_timeout(void);
/**
* @brief Get all the server capabilities including all the schemas.
*
* A few capabilities (with-defaults, interleave) depend on the current
* server options.
*
* @param[in] ctx Context to read most capabilities from.
* @return Array of capabilities stored in the @p ctx dictionary, NULL on error.
*/
const char **nc_server_get_cpblts(struct ly_ctx *ctx);
/**
* @brief Get the server capabilities including the schemas with the specified YANG version.
*
* A few capabilities (with-defaults, interleave) depend on the current
* server options.
*
* @param[in] ctx Context to read most capabilities from.
* @param[in] version YANG version of the schemas to be included in result, with
* LYS_VERSION_UNDEF the result is the same as from nc_server_get_cpblts().
* @return Array of capabilities stored in the @p ctx dictionary, NULL on error.
*/
const char **nc_server_get_cpblts_version(struct ly_ctx *ctx, LYS_VERSION version);
/** @} Server */
/**
* @addtogroup server_session
* @{
*/
/**
* @brief Accept a new session on a pre-established transport session.
*
* @param[in] fdin File descriptor to read (unencrypted) XML data from.
* @param[in] fdout File descriptor to write (unencrypted) XML data to.
* @param[in] username NETCONF username as provided by the transport protocol.
* @param[out] session New session on success.
* @return NC_MSG_HELLO on success, NC_MSG_BAD_HELLO on client \<hello\> message
* parsing fail, NC_MSG_WOULDBLOCK on timeout, NC_MSG_ERROR on other errors.
*/
NC_MSG_TYPE nc_accept_inout(int fdin, int fdout, const char *username, struct nc_session **session);
/**
* @brief Create an empty structure for polling sessions.
*
* @return Empty pollsession structure, NULL on error.
*/
struct nc_pollsession *nc_ps_new(void);
/**
* @brief Free a pollsession structure.
*
* !IMPORTANT! Make sure that @p ps is not accessible (is not used)
* by any thread before and after this call!
*
* @param[in] ps Pollsession structure to free.
*/
void nc_ps_free(struct nc_pollsession *ps);
/**
* @brief Add a session to a pollsession structure.
*
* @param[in] ps Pollsession structure to modify.
* @param[in] session Session to add to @p ps.
* @return 0 on success, -1 on error.
*/
int nc_ps_add_session(struct nc_pollsession *ps, struct nc_session *session);
/**
* @brief Remove a session from a pollsession structure.
*
* @param[in] ps Pollsession structure to modify.
* @param[in] session Session to remove from @p ps.
* @return 0 on success, -1 on not found.
*/
int nc_ps_del_session(struct nc_pollsession *ps, struct nc_session *session);
/**
* @brief Get a session from a pollsession structure matching the session ID.
*
* @param[in] ps Pollsession structure to read from.
* @param[in] idx Index of the session.
* @return Session on index, NULL if out-of-bounds.
*/
struct nc_session *nc_ps_get_session(const struct nc_pollsession *ps, uint16_t idx);
/**
* @brief Learn the number of sessions in a pollsession structure.
*
* Does not lock @p ps structure for efficiency.
*
* @param[in] ps Pollsession structure to check.
* @return Number of sessions (even invalid ones) in @p ps, -1 on error.
*/
uint16_t nc_ps_session_count(struct nc_pollsession *ps);
#define NC_PSPOLL_NOSESSIONS 0x0001 /**< No sessions to poll. */
#define NC_PSPOLL_TIMEOUT 0x0002 /**< Timeout elapsed. */
#define NC_PSPOLL_RPC 0x0004 /**< RPC was correctly parsed and processed. */
#define NC_PSPOLL_BAD_RPC 0x0008 /**< RPC was received, but failed to be parsed. */
#define NC_PSPOLL_REPLY_ERROR 0x0010 /**< Response to the RPC was a \<rpc-reply\> of type error. */
#define NC_PSPOLL_SESSION_TERM 0x0020 /**< Some session was terminated. */
#define NC_PSPOLL_SESSION_ERROR 0x0040 /**< Some session was terminated incorrectly (not by a \<close-session\> or \<kill-session\> RPC). */
#define NC_PSPOLL_ERROR 0x0080 /**< Other fatal errors (they are printed). */
#ifdef NC_ENABLED_SSH
# define NC_PSPOLL_SSH_MSG 0x00100 /**< SSH message received (and processed, if relevant, only with SSH support). */
# define NC_PSPOLL_SSH_CHANNEL 0x0200 /**< New SSH channel opened on an existing session (only with SSH support). */
#endif
/**
* @brief Poll sessions and process any received RPCs.
*
* Only one event on one session is handled in one function call. If this event
* is a session termination (#NC_PSPOLL_SESSION_TERM returned), the session
* should be removed from @p ps.
*
* @param[in] ps Pollsession structure to use.
* @param[in] timeout Poll timeout in milliseconds. 0 for non-blocking call, -1 for
* infinite waiting.
* @param[in] session Session that was processed and that specific return bits concern.
* Can be NULL.
* @return Bitfield of NC_PSPOLL_* macros.
*/
int nc_ps_poll(struct nc_pollsession *ps, int timeout, struct nc_session **session);
/**
* @brief Remove sessions from a pollsession structure and
* call nc_session_free() on them.
*
* Calling this function with @p all false makes sense if nc_ps_poll() returned #NC_PSPOLL_SESSION_TERM.
*
* @param[in] ps Pollsession structure to clear.
* @param[in] all Whether to free all sessions, or only the invalid ones.
* @param[in] data_free Session user data destructor.
*/
void nc_ps_clear(struct nc_pollsession *ps, int all, void (*data_free)(void *));
/** @} Server Session */
/**
* @addtogroup server
* @{
*/
/**
* @brief Add a new endpoint.
*
* Before the endpoint can accept any connections, its address and port must
* be set via nc_server_endpt_set_address() and nc_server_endpt_set_port().
*
* @param[in] name Arbitrary unique endpoint name.
* @param[in] ti Transport protocol to use.
* @return 0 on success, -1 on error.
*/
int nc_server_add_endpt(const char *name, NC_TRANSPORT_IMPL ti);
/**
* @brief Stop listening on and remove an endpoint.
*
* @param[in] name Endpoint name. NULL matches all endpoints.
* @param[in] ti Endpoint transport protocol. NULL matches any protocol.
* Redundant to set if @p name is set, endpoint names are
* unique disregarding their protocol.
* @return 0 on success, -1 on not finding any match.
*/
int nc_server_del_endpt(const char *name, NC_TRANSPORT_IMPL ti);
/**
* @brief Get the number of currently configured listening endpoints.
* Note that an ednpoint without address and/or port will be included
* even though it is not, in fact, listening.
*
* @return Number of added listening endpoints.
*/
int nc_server_endpt_count(void);
/**
* @brief Check if an endpoint exists.
*
* @param[in] name Endpoint name.
* @return 0 if does not exists, non-zero otherwise.
*/
int nc_server_is_endpt(const char *name);
/**
* @brief Change endpoint listening address.
*
* On error the previous listening socket (if any) is left untouched.
*
* @param[in] endpt_name Existing endpoint name.
* @param[in] address New listening address.
* @return 0 on success, -1 on error.
*/
int nc_server_endpt_set_address(const char *endpt_name, const char *address);
#if defined (NC_ENABLED_SSH) || defined (NC_ENABLED_TLS)
/**
* @brief Change endpoint listening port.
*
* This is only valid on SSH/TLS transport endpoint.
* On error the previous listening socket (if any) is left untouched.
*
* @param[in] endpt_name Existing endpoint name.
* @param[in] port New listening port.
* @return 0 on success, -1 on error.
*/
int nc_server_endpt_set_port(const char *endpt_name, uint16_t port);
#endif
/**
* @brief Change endpoint permissions.
*
* This is only valid on UNIX transport endpoint.
* On error the previous listening socket (if any) is left untouched.
*
* @param[in] endpt_name Existing endpoint name.
* @param[in] mode New mode, -1 to use default.
* @param[in] uid New uid, -1 to use default.
* @param[in] gid New gid, -1 to use default.
* @return 0 on success, -1 on error.
*/
int nc_server_endpt_set_perms(const char *endpt_name, mode_t mode, uid_t uid, gid_t gid);
/**
* @brief Change endpoint keepalives state. Affects only new connections.
*
* @param[in] endpt_name Existing endpoint name.
* @param[in] enable Whether to enable or disable keepalives.
* @return 0 on success, -1 on error.
*/
int nc_server_endpt_enable_keepalives(const char *endpt_name, int enable);
/**
* @brief Change endpoint keepalives parameters. Affects only new connections.
*
* @param[in] endpt_name Existing endpoint name.
* @param[in] idle_time Keepalive idle time in seconds, 1 by default, -1 to keep previous value.
* @param[in] max_probes Keepalive max probes sent, 10 by default, -1 to keep previous value.
* @param[in] probe_interval Keepalive probe interval in seconds, 5 by default, -1 to keep previous value.
* @return 0 on success, -1 on error.
*/
int nc_server_endpt_set_keepalives(const char *endpt_name, int idle_time, int max_probes, int probe_interval);
/** @} Server */
/**
* @addtogroup server_session
* @{
*/
/**
* @brief Accept new sessions on all the listening endpoints.
*
* Once a new (TCP/IP) conection is established a different (quite long) timeout
* is used for waiting for transport-related data, which means this call can block
* for much longer that @p timeout, but only with slow/faulty/malicious clients.
*
* @param[in] timeout Timeout for receiving a new connection in milliseconds, 0 for
* non-blocking call, -1 for infinite waiting.
* @param[out] session New session.
* @return NC_MSG_HELLO on success, NC_MSG_BAD_HELLO on client \<hello\> message
* parsing fail, NC_MSG_WOULDBLOCK on timeout, NC_MSG_ERROR on other errors.
*/
NC_MSG_TYPE nc_accept(int timeout, struct nc_session **session);
#ifdef NC_ENABLED_SSH
/**
* @brief Accept a new NETCONF session on an SSH session of a running NETCONF @p orig_session.
* Call this function only when nc_ps_poll() returns #NC_PSPOLL_SSH_CHANNEL on @p orig_session.
*
* @param[in] orig_session Session that has a new SSH channel ready.
* @param[out] session New session.
* @return NC_MSG_HELLO on success, NC_MSG_BAD_HELLO on client \<hello\> message
* parsing fail, NC_MSG_WOULDBLOCK on timeout, NC_MSG_ERROR on other errors.
*/
NC_MSG_TYPE nc_session_accept_ssh_channel(struct nc_session *orig_session, struct nc_session **session);
/**
* @brief Accept a new NETCONF session on an SSH session of a running NETCONF session
* that was polled in @p ps. Call this function only when nc_ps_poll() on @p ps returns #NC_PSPOLL_SSH_CHANNEL.
* The new session is only returned in @p session, it is not added to @p ps.
*
* @param[in] ps Unmodified pollsession structure from the previous nc_ps_poll() call.
* @param[out] session New session.
* @return NC_MSG_HELLO on success, NC_MSG_BAD_HELLO on client \<hello\> message
* parsing fail, NC_MSG_WOULDBLOCK on timeout, NC_MSG_ERROR on other errors.
*/
NC_MSG_TYPE nc_ps_accept_ssh_channel(struct nc_pollsession *ps, struct nc_session **session);
/** @} Server Session */
/**
* @defgroup server_ssh Server SSH
* @ingroup server
*
* @brief Server-side settings for SSH connections.
* @{
*/
/**
* @brief Add an authorized client SSH public key. This public key can be used for
* publickey authentication (for any SSH connection, even Call Home) afterwards.
*
* @param[in] pubkey_base64 Authorized public key binary content encoded in base64.
* @param[in] type Authorized public key SSH type.
* @param[in] username Username that the client with the public key must use.
* @return 0 on success, -1 on error.
*/
int nc_server_ssh_add_authkey(const char *pubkey_base64, NC_SSH_KEY_TYPE type, const char *username);
/**
* @brief Add an authorized client SSH public key. This public key can be used for
* publickey authentication (for any SSH connection, even Call Home) afterwards.
*
* @param[in] pubkey_path Path to the public key.
* @param[in] username Username that the client with the public key must use.
* @return 0 on success, -1 on error.
*/
int nc_server_ssh_add_authkey_path(const char *pubkey_path, const char *username);
/**
* @brief Remove an authorized client SSH public key.
*
* @param[in] pubkey_path Path to an authorized public key. NULL matches all the keys.
* @param[in] pubkey_base64 Authorized public key content. NULL matches any key.
* @param[in] type Authorized public key type. 0 matches all types.
* @param[in] username Username for an authorized public key. NULL matches all the usernames.
* @return 0 on success, -1 on not finding any match.
*/
int nc_server_ssh_del_authkey(const char *pubkey_path, const char *pubkey_base64, NC_SSH_KEY_TYPE type,
const char *username);
/**
* @brief Set the callback for SSH password authentication. If none is set, local system users are used.
*
* @param[in] passwd_auth_clb Callback that should authenticate the user. Username can be directly obtained from @p session.
* Zero return indicates success, non-zero an error.
* @param[in] user_data Optional arbitrary user data that will be passed to @p passwd_auth_clb.
* @param[in] free_user_data Optional callback that will be called during cleanup to free any @p user_data.
*/
void nc_server_ssh_set_passwd_auth_clb(int (*passwd_auth_clb)(const struct nc_session *session, const char *password,
void *user_data), void *user_data, void (*free_user_data)(void *user_data));
/**
* @brief Set the callback for SSH interactive authentication. If none is set, local system users are used.
*
* @param[in] interactive_auth_clb Callback that should authenticate the user.
* Zero return indicates success, non-zero an error.
* @param[in] user_data Optional arbitrary user data that will be passed to @p passwd_auth_clb.
* @param[in] free_user_data Optional callback that will be called during cleanup to free any @p user_data.
*/
void nc_server_ssh_set_interactive_auth_clb(int (*interactive_auth_clb)(const struct nc_session *session,
const ssh_message msg, void *user_data), void *user_data, void (*free_user_data)(void *user_data));
/**
* @brief Set the callback for SSH public key authentication. If none is set, local system users are used.
*
* @param[in] pubkey_auth_clb Callback that should authenticate the user.
* Zero return indicates success, non-zero an error.
* @param[in] user_data Optional arbitrary user data that will be passed to @p passwd_auth_clb.
* @param[in] free_user_data Optional callback that will be called during cleanup to free any @p user_data.
*/
void nc_server_ssh_set_pubkey_auth_clb(int (*pubkey_auth_clb)(const struct nc_session *session, ssh_key key,
void *user_data), void *user_data, void (*free_user_data)(void *user_data));
/**
* @brief Set the callback for retrieving host keys. Any RSA, DSA, and ECDSA keys can be added. However,
* a maximum of one key of each type will be used during SSH authentication, later keys replacing
* the earlier ones.
*
* @param[in] hostkey_clb Callback that should return the key itself. Zero return indicates success, non-zero
* an error. On success exactly ONE of @p privkey_path or @p privkey_data is expected
* to be set. The one set will be freed.
* - @p privkey_path expects a PEM file,
* - @p privkey_data expects a base-64 encoded ANS.1 DER data,
* - @p privkey_type type of the key in @p privkey_data. Use ::NC_SSH_KEY_UNKNOWN for
* PKCS#8 key that includes the information about the key in its data.
* @param[in] user_data Optional arbitrary user data that will be passed to @p hostkey_clb.
* @param[in] free_user_data Optional callback that will be called during cleanup to free any @p user_data.
*/
void nc_server_ssh_set_hostkey_clb(int (*hostkey_clb)(const char *name, void *user_data, char **privkey_path,
char **privkey_data, NC_SSH_KEY_TYPE *privkey_type), void *user_data, void (*free_user_data)(void *user_data));
/**
* @brief Add endpoint SSH host keys the server will identify itself with. Only the name is set, the key itself
* wil be retrieved using a callback.
*
* @param[in] endpt_name Existing endpoint name.
* @param[in] name Arbitrary name of the host key.
* @param[in] idx Optional index where to add the key. -1 adds at the end.
* @return 0 on success, -1 on error.
*/
int nc_server_ssh_endpt_add_hostkey(const char *endpt_name, const char *name, int16_t idx);
/**
* @brief Delete endpoint SSH host key. Their order is preserved.
*
* @param[in] endpt_name Existing endpoint name.
* @param[in] name Name of the host key. NULL matches all the keys, but if @p idx != -1 then this must be NULL.
* @param[in] idx Index of the hostkey. -1 matches all indices, but if @p name != NULL then this must be -1.
* @return 0 on success, -1 on error.
*/
int nc_server_ssh_endpt_del_hostkey(const char *endpt_name, const char *name, int16_t idx);
/**
* @brief Move endpoint SSH host key.
*
* @param[in] endpt_name Exisitng endpoint name.
* @param[in] key_mov Name of the host key that will be moved.
* @param[in] key_after Name of the key that will preceed @p key_mov. NULL if @p key_mov is to be moved at the beginning.
* @return 0 in success, -1 on error.
*/
int nc_server_ssh_endpt_mov_hostkey(const char *endpt_name, const char *key_mov, const char *key_after);
/**
* @brief Modify endpoint SSH host key.
*
* @param[in] endpt_name Exisitng endpoint name.
* @param[in] name Name of an existing host key.
* @param[in] new_name New name of the host key @p name.
* @return 0 in success, -1 on error.
*/
int nc_server_ssh_endpt_mod_hostkey(const char *endpt_name, const char *name, const char *new_name);
/**
* @brief Set endpoint accepted SSH authentication methods. All (publickey, password, interactive)
* are supported by default.
*
* @param[in] endpt_name Existing endpoint name.
* @param[in] auth_methods Accepted authentication methods bit field of NC_SSH_AUTH_TYPE.
* @return 0 on success, -1 on error.
*/
int nc_server_ssh_endpt_set_auth_methods(const char *endpt_name, int auth_methods);
/**
* @brief Get endpoint accepted SSH authentication methods.
*
* @param[in] endpt_name Existing endpoint name.
* @return Accepted authentication methods bit field of NC_SSH_AUTH_TYPE.
*/
int nc_server_ssh_endpt_get_auth_methods(const char *endpt_name);
/**
* @brief Set endpoint SSH authentication attempts of every client. 3 by default.
*
* @param[in] endpt_name Existing endpoint name.
* @param[in] auth_attempts Failed authentication attempts before a client is dropped.
* @return 0 on success, -1 on error.
*/
int nc_server_ssh_endpt_set_auth_attempts(const char *endpt_name, uint16_t auth_attempts);
/**
* @brief Set endpoint SSH authentication timeout. 30 seconds by default.
*
* @param[in] endpt_name Existing endpoint name.
* @param[in] auth_timeout Number of seconds before an unauthenticated client is dropped.
* @return 0 on success, -1 on error.
*/
int nc_server_ssh_endpt_set_auth_timeout(const char *endpt_name, uint16_t auth_timeout);
/** @} Server SSH */
#endif /* NC_ENABLED_SSH */
#ifdef NC_ENABLED_TLS
/**
* @defgroup server_tls Server TLS
* @ingroup server
*
* @brief Server-side settings for TLS connections.
* @{
*/
/**
* @brief Set the server TLS certificate. Only the name is set, the certificate itself
* wil be retrieved using a callback.
*
* @param[in] endpt_name Existing endpoint name.
* @param[in] name Arbitrary certificate name.
* @return 0 on success, -1 on error.
*/
int nc_server_tls_endpt_set_server_cert(const char *endpt_name, const char *name);
/**
* @brief Set the callback for retrieving server certificate and matching private key.
*
* @param[in] cert_clb Callback that should return the certificate and the key itself. Zero return indicates success,
* non-zero an error. On success exactly ONE of @p cert_path or @p cert_data and ONE of
* @p privkey_path and @p privkey_data is expected to be set. Those set will be freed.
* - @p cert_path expects a PEM file,
* - @p cert_data expects a base-64 encoded ASN.1 DER data,
* - @p privkey_path expects a PEM file,
* - @p privkey_data expects a base-64 encoded ANS.1 DER data,
* - @p privkey_type type of the key in @p privkey_data.
* @param[in] user_data Optional arbitrary user data that will be passed to @p cert_clb.
* @param[in] free_user_data Optional callback that will be called during cleanup to free any @p user_data.
*/
void nc_server_tls_set_server_cert_clb(int (*cert_clb)(const char *name, void *user_data, char **cert_path, char **cert_data,
char **privkey_path, char **privkey_data, NC_SSH_KEY_TYPE *privkey_type), void *user_data,
void (*free_user_data)(void *user_data));
/**
* @brief Set the callback for retrieving server certificate chain
*
* @param[in] cert_chain_clb Callback that should return all the certificates of the chain. Zero return indicates success,
* non-zero an error. On success, @p cert_paths and @p cert_data are expected to be set or left
* NULL. Both will be (deeply) freed.
* - @p cert_paths expect an array of PEM files,
* - @p cert_path_count number of @p cert_paths array members,
* - @p cert_data expect an array of base-64 encoded ASN.1 DER cert data,
* - @p cert_data_count number of @p cert_data array members.
* @param[in] user_data Optional arbitrary user data that will be passed to @p cert_clb.
* @param[in] free_user_data Optional callback that will be called during cleanup to free any @p user_data.
*/
void nc_server_tls_set_server_cert_chain_clb(int (*cert_chain_clb)(const char *name, void *user_data, char ***cert_paths,
int *cert_path_count, char ***cert_data, int *cert_data_count), void *user_data, void (*free_user_data)(void *user_data));
/**
* @brief Add a trusted certificate list. Can be both a CA or a client one. Can be
* safely used together with nc_server_tls_endpt_set_trusted_ca_paths().
*
* @param[in] endpt_name Existing endpoint name.
* @param[in] name Arbitary name identifying this certificate list.
* @return 0 on success, -1 on error.
*/
int nc_server_tls_endpt_add_trusted_cert_list(const char *endpt_name, const char *name);
/**
* @brief Set the callback for retrieving trusted certificates.
*
* @param[in] cert_list_clb Callback that should return all the certificates of a list. Zero return indicates success,
* non-zero an error. On success, @p cert_paths and @p cert_data are expected to be set or left
* NULL. Both will be (deeply) freed.
* - @p cert_paths expect an array of PEM files,
* - @p cert_path_count number of @p cert_paths array members,
* - @p cert_data expect an array of base-64 encoded ASN.1 DER cert data,
* - @p cert_data_count number of @p cert_data array members.
* @param[in] user_data Optional arbitrary user data that will be passed to @p cert_clb.
* @param[in] free_user_data Optional callback that will be called during cleanup to free any @p user_data.
*/
void nc_server_tls_set_trusted_cert_list_clb(int (*cert_list_clb)(const char *name, void *user_data, char ***cert_paths,
int *cert_path_count, char ***cert_data, int *cert_data_count), void *user_data, void (*free_user_data)(void *user_data));
/**
* @brief Remove a trusted certificate.
*
* @param[in] endpt_name Existing endpoint name.
* @param[in] name Name of the certificate list to delete. NULL deletes all the lists.
* @return 0 on success, -1 on not found.
*/
int nc_server_tls_endpt_del_trusted_cert_list(const char *endpt_name, const char *name);
/**
* @brief Set trusted Certificate Authority certificate locations. There can only be
* one file and one directory, they are replaced if already set. Can be safely
* used with nc_server_tls_endpt_add_trusted_cert() or its _path variant.
*
* @param[in] endpt_name Existing endpoint name.
* @param[in] ca_file Path to a trusted CA cert store file in PEM format. Can be NULL.
* @param[in] ca_dir Path to a trusted CA cert store hashed directory (c_rehash utility
* can be used to create hashes) with PEM files. Can be NULL.
* @return 0 on success, -1 on error.
*/
int nc_server_tls_endpt_set_trusted_ca_paths(const char *endpt_name, const char *ca_file, const char *ca_dir);
/**
* @brief Set Certificate Revocation List locations. There can only be one file
* and one directory, they are replaced if already set.
*
* @param[in] endpt_name Existing endpoint name.
* @param[in] crl_file Path to a CRL store file in PEM format. Can be NULL.
* @param[in] crl_dir Path to a CRL store hashed directory (c_rehash utility
* can be used to create hashes) with PEM files. Can be NULL.
* @return 0 on success, -1 on error.
*/
int nc_server_tls_endpt_set_crl_paths(const char *endpt_name, const char *crl_file, const char *crl_dir);
/**
* @brief Destroy and clean CRLs. Certificates, private keys, and CTN entries are
* not affected.
*
* @param[in] endpt_name Existing endpoint name.
*/
void nc_server_tls_endpt_clear_crls(const char *endpt_name);
/**
* @brief Add a cert-to-name entry.
*
* It is possible to add an entry step-by-step, specifying first only @p ip and in later calls
* @p fingerprint, @p map_type, and optionally @p name spearately.
*
* @param[in] endpt_name Existing endpoint name.
* @param[in] id Priority of the entry. It must be unique. If already exists, the entry with this id
* is modified.
* @param[in] fingerprint Matching certificate fingerprint. If NULL, kept temporarily unset.
* @param[in] map_type Type of username-certificate mapping. If 0, kept temporarily unset.
* @param[in] name Specific username used only if @p map_type == NC_TLS_CTN_SPECIFED.
* @return 0 on success, -1 on error.
*/
int nc_server_tls_endpt_add_ctn(const char *endpt_name, uint32_t id, const char *fingerprint,
NC_TLS_CTN_MAPTYPE map_type, const char *name);
/**
* @brief Remove a cert-to-name entry.
*
* @param[in] endpt_name Existing endpoint name.
* @param[in] id Priority of the entry. -1 matches all the priorities.
* @param[in] fingerprint Fingerprint fo the entry. NULL matches all the fingerprints.
* @param[in] map_type Mapping type of the entry. 0 matches all the mapping types.
* @param[in] name Specific username for the entry. NULL matches all the usernames.
* @return 0 on success, -1 on not finding any match.
*/
int nc_server_tls_endpt_del_ctn(const char *endpt_name, int64_t id, const char *fingerprint,
NC_TLS_CTN_MAPTYPE map_type, const char *name);
/**
* @brief Get a cert-to-name entry.
*
* If a parameter is NULL, it is ignored. If its dereferenced value is NULL,
* it is filled and returned. If the value is set, it is used as a filter.
* Returns first matching entry.
*
* @param[in] endpt_name Existing endpoint name.
* @param[in,out] id Priority of the entry.
* @param[in,out] fingerprint Fingerprint fo the entry.
* @param[in,out] map_type Mapping type of the entry.
* @param[in,out] name Specific username for the entry.
* @return 0 on success, -1 on not finding any match.
*/
int nc_server_tls_endpt_get_ctn(const char *endpt_name, uint32_t *id, char **fingerprint, NC_TLS_CTN_MAPTYPE *map_type,
char **name);
/**
* @brief Get client certificate.
*
* @param[in] session Session to get the information from.
* @return Const session client certificate.
*/
const X509 *nc_session_get_client_cert(const struct nc_session *session);
/**
* @brief Set TLS authentication additional verify callback.
*
* Server will always perform cert-to-name based on its configuration. Only after it passes
* and this callback is set, it is also called. It should return exactly what OpenSSL
* verify callback meaning 1 for success, 0 to deny the user.
*
* @param[in] verify_clb Additional user verify callback.
*/
void nc_server_tls_set_verify_clb(int (*verify_clb)(const struct nc_session *session));
/** @} Server TLS */
#endif /* NC_ENABLED_TLS */
/**
* @addtogroup server_session
* @{
*/
/**
* @brief Get session start time.
*
* @param[in] session Session to get the information from.
* @return Session start time.
*/
time_t nc_session_get_start_time(const struct nc_session *session);
/**
* @brief Increase session notification subscription flag count.
* Supports multiple subscriptions on one session.
*
* It is used only to ignore timeouts, because they are
* ignored for sessions with active subscriptions.
*
* @param[in] session Session to modify.
*/
void nc_session_inc_notif_status(struct nc_session *session);
/**
* @brief Decrease session notification subscription flag count.
* Supports multiple subscriptions on one session.
*
* @param[in] session Session to modify.
*/
void nc_session_dec_notif_status(struct nc_session *session);
/**
* @brief Get session notification subscription flag.
*
* @param[in] session Session to get the information from.
* @return 0 for no active subscription, non-zero for an active subscription.
*/
int nc_session_get_notif_status(const struct nc_session *session);
/**
* @brief Learn whether a session was created using Call Home or not.
* Works only for server sessions.
*
* @param[in] session Session to get the information from.
* @return 0 if a standard session, non-zero if a Call Home session.
*/
int nc_session_is_callhome(const struct nc_session *session);
/** @} Server Session */
#ifdef __cplusplus
}
#endif
#endif /* NC_SESSION_SERVER_H_ */

436
src/session_server_ch.h Normal file
View file

@ -0,0 +1,436 @@
/**
* @file session_server_ch.h
* @author Michal Vasko <mvasko@cesnet.cz>
* @brief libnetconf2 Call Home session server manipulation
*
* Copyright (c) 2015 - 2021 CESNET, z.s.p.o.
*
* This source code is licensed under BSD 3-Clause License (the "License").
* You may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://opensource.org/licenses/BSD-3-Clause
*/
#ifndef NC_SESSION_SERVER_CH_H_
#define NC_SESSION_SERVER_CH_H_
#ifdef __cplusplus
extern "C" {
#endif
#include <libyang/libyang.h>
#include <stdint.h>
#include "netconf.h"
#include "session.h"
#if defined (NC_ENABLED_SSH) || defined (NC_ENABLED_TLS)
/**
* @defgroup server_ch Server-side Call Home
* @ingroup server
*
* @brief Call Home functionality for server-side applications.
* @{
*/
/**
* @brief Add a new Call Home client.
*
* @param[in] name Arbitrary unique client name.
* @return 0 on success, -1 on error.
*/
int nc_server_ch_add_client(const char *name);
/**
* @brief Drop any connections, stop connecting and remove a client.
*
* @param[in] name Client name. NULL matches all the clients.
* @return 0 on success, -1 on not finding any match.
*/
int nc_server_ch_del_client(const char *name);
/**
* @brief Check if a Call Home client exists.
*
* @param[in] name Client name.
* @return 0 if does not exists, non-zero otherwise.
*/
int nc_server_ch_is_client(const char *name);
/**
* @brief Add a new Call Home client endpoint.
*
* @param[in] client_name Existing client name.
* @param[in] endpt_name Arbitrary unique (within the client) endpoint name.
* @param[in] ti Transport protocol to use.
* @return 0 on success, -1 on error.
*/
int nc_server_ch_client_add_endpt(const char *client_name, const char *endpt_name, NC_TRANSPORT_IMPL ti);
/**
* @brief Remove a Call Home client endpoint.
*
* @param[in] client_name Existing client name.
* @param[in] endpt_name Existing endpoint of @p client_name. NULL matches all endpoints.
* @param[in] ti Client transport protocol. NULL matches any protocol.
* Redundant to set if @p endpt_name is set, client names are
* unique disregarding their protocol.
* @return 0 on success, -1 on error.
*/
int nc_server_ch_client_del_endpt(const char *client_name, const char *endpt_name, NC_TRANSPORT_IMPL ti);
/**
* @brief Check if an endpoint of a Call Home client exists.
*
* @param[in] client_name Client name.
* @param[in] endpt_name Endpoint name.
* @return 0 if does not exists, non-zero otherwise.
*/
int nc_server_ch_client_is_endpt(const char *client_name, const char *endpt_name);
/**
* @brief Change Call Home client endpoint listening address.
*
* On error the previous listening socket (if any) is left untouched.
*
* @param[in] client_name Existing Call Home client name.
* @param[in] endpt_name Existing endpoint name of @p client_name.
* @param[in] address New listening address.
* @return 0 on success, -1 on error.
*/
int nc_server_ch_client_endpt_set_address(const char *client_name, const char *endpt_name, const char *address);
/**
* @brief Change Call Home client endpoint listening port.
*
* On error the previous listening socket (if any) is left untouched.
*
* @param[in] client_name Existing Call Home client name.
* @param[in] endpt_name Existing endpoint name of @p client_name.
* @param[in] port New listening port.
* @return 0 on success, -1 on error.
*/
int nc_server_ch_client_endpt_set_port(const char *client_name, const char *endpt_name, uint16_t port);
/**
* @brief Change Call Home client endpoint keepalives state. Affects only new connections.
*
* @param[in] client_name Existing Call Home client name.
* @param[in] endpt_name Existing endpoint name of @p client_name.
* @param[in] enable Whether to enable or disable keepalives.
* @return 0 on success, -1 on error.
*/
int nc_server_ch_client_endpt_enable_keepalives(const char *client_name, const char *endpt_name, int enable);
/**
* @brief Change Call Home client endpoint keepalives parameters. Affects only new connections.
*
* @param[in] client_name Existing Call Home client name.
* @param[in] endpt_name Existing endpoint name of @p client_name.
* @param[in] idle_time Keepalive idle time in seconds, 1 by default, -1 to keep previous value.
* @param[in] max_probes Keepalive max probes sent, 10 by default, -1 to keep previous value.
* @param[in] probe_interval Keepalive probe interval in seconds, 5 by default, -1 to keep previous value.
* @return 0 on success, -1 on error.
*/
int nc_server_ch_client_endpt_set_keepalives(const char *client_name, const char *endpt_name, int idle_time,
int max_probes, int probe_interval);
/**
* @brief Set Call Home client connection type.
*
* @param[in] client_name Existing Call Home client name.
* @param[in] conn_type Call Home connection type.
* @return 0 on success, -1 on error.
*/
int nc_server_ch_client_set_conn_type(const char *client_name, NC_CH_CONN_TYPE conn_type);
/**
* @brief Set Call Home client periodic connection period for reconnecting.
*
* @param[in] client_name Existing Call Home client name.
* @param[in] period Call Home periodic connection period in minutes.
* @return 0 on success, -1 on error.
*/
int nc_server_ch_client_periodic_set_period(const char *client_name, uint16_t period);
/**
* @brief Set Call Home client periodic connection period anchor time.
*
* @param[in] client_name Existing Call Home client name.
* @param[in] anchor_time Call Home periodic connection anchor time for the period.
* @return 0 on success, -1 on error.
*/
int nc_server_ch_client_periodic_set_anchor_time(const char *client_name, time_t anchor_time);
/**
* @brief Set Call Home client periodic connection idle timeout.
*
* @param[in] client_name Existing Call Home client name.
* @param[in] idle_timeout Call Home periodic idle timeout.
* @return 0 on success, -1 on error.
*/
int nc_server_ch_client_periodic_set_idle_timeout(const char *client_name, uint16_t idle_timeout);
/**
* @brief Set Call Home client start-with policy.
*
* @param[in] client_name Existing Call Home client name.
* @param[in] start_with Call Home client start-with.
* @return 0 on success, -1 on error.
*/
int nc_server_ch_client_set_start_with(const char *client_name, NC_CH_START_WITH start_with);
/**
* @brief Set Call Home client overall max attempts.
*
* @param[in] client_name Existing Call Home client name.
* @param[in] max_attempts Call Home overall max reconnect attempts.
* @return 0 on success, -1 on error.
*/
int nc_server_ch_client_set_max_attempts(const char *client_name, uint8_t max_attempts);
/**
* @brief Establish a Call Home connection with a listening NETCONF client.
*
* @param[in] client_name Existing client name.
* @param[out] session_clb Function that is called for every established session on the client. @p new_session
* pointer is internally discarded afterwards. If the callback returns non-zero, the @p new_session is freed.
* @return 0 if the thread was successfully created, -1 on error.
*/
int nc_connect_ch_client_dispatch(const char *client_name,
int (*session_clb)(const char *client_name, struct nc_session *new_session));
/** @} Server-side Call Home */
#endif /* NC_ENABLED_SSH || NC_ENABLED_TLS */
#ifdef NC_ENABLED_SSH
/**
* @defgroup server_ch_ssh Server-side Call Home on SSH
* @ingroup server_ch
*
* @brief SSH settings for the Call Home functionality
* @{
*/
/**
* @brief Add Call Home SSH host keys the server will identify itself with. Only the name is set, the key itself
* wil be retrieved using a callback.
*
* @param[in] client_name Existing Call Home client name.
* @param[in] endpt_name Existing endpoint name of the client.
* @param[in] name Arbitrary name of the host key.
* @param[in] idx Optional index where to add the key. -1 adds at the end.
* @return 0 on success, -1 on error.
*/
int nc_server_ssh_ch_client_endpt_add_hostkey(const char *client_name, const char *endpt_name, const char *name, int16_t idx);
/**
* @brief Delete Call Home SSH host keys. Their order is preserved.
*
* @param[in] client_name Existing Call Home client name.
* @param[in] endpt_name Existing endpoint name of the client.
* @param[in] name Name of the host key. NULL matches all the keys, but if @p idx != -1 then this must be NULL.
* @param[in] idx Index of the hostkey. -1 matches all indices, but if @p name != NULL then this must be -1.
* @return 0 on success, -1 on error.
*/
int nc_server_ssh_ch_client_endpt_del_hostkey(const char *client_name, const char *endpt_name, const char *name, int16_t idx);
/**
* @brief Move Call Home SSH host key.
*
* @param[in] client_name Exisitng Call Home client name.
* @param[in] endpt_name Existing endpoint name of the client.
* @param[in] key_mov Name of the host key that will be moved.
* @param[in] key_after Name of the key that will preceed @p key_mov. NULL if @p key_mov is to be moved at the beginning.
* @return 0 in success, -1 on error.
*/
int nc_server_ssh_ch_client_endpt_mov_hostkey(const char *client_name, const char *endpt_name, const char *key_mov,
const char *key_after);
/**
* @brief Set accepted Call Home SSH authentication methods. All (publickey, password, interactive)
* are supported by default.
*
* @param[in] client_name Existing Call Home client name.
* @param[in] endpt_name Existing endpoint name of the client.
* @param[in] auth_methods Accepted authentication methods bit field of NC_SSH_AUTH_TYPE.
* @return 0 on success, -1 on error.
*/
int nc_server_ssh_ch_client_endpt_set_auth_methods(const char *client_name, const char *endpt_name, int auth_methods);
/**
* @brief Get accepted Call Home SSH authentication methods.
*
* @param[in] client_name Existing Call Home client name.
* @param[in] endpt_name Existing endpoint name of the client.
* @return Accepted authentication methods bit field of NC_SSH_AUTH_TYPE.
*/
int nc_server_ssh_ch_client_endpt_get_auth_methods(const char *client_name, const char *endpt_name);
/**
* @brief Set Call Home SSH authentication attempts of every client. 3 by default.
*
* @param[in] client_name Existing Call Home client name.
* @param[in] endpt_name Existing endpoint name of the client.
* @param[in] auth_attempts Failed authentication attempts before a client is dropped.
* @return 0 on success, -1 on error.
*/
int nc_server_ssh_ch_client_endpt_set_auth_attempts(const char *client_name, const char *endpt_name, uint16_t auth_attempts);
/**
* @brief Set Call Home SSH authentication timeout. 30 seconds by default.
*
* @param[in] client_name Existing Call Home client name.
* @param[in] endpt_name Existing endpoint name of the client.
* @param[in] auth_timeout Number of seconds before an unauthenticated client is dropped.
* @return 0 on success, -1 on error.
*/
int nc_server_ssh_ch_client_endpt_set_auth_timeout(const char *client_name, const char *endpt_name, uint16_t auth_timeout);
/** @} Server-side Call Home on SSH */
#endif /* NC_ENABLED_SSH */
#ifdef NC_ENABLED_TLS
/**
* @defgroup server_ch_tls Server-side Call Home on TLS
* @ingroup server_ch
*
* @brief TLS settings for the Call Home functionality
* @{
*/
/**
* @brief Set the server Call Home TLS certificate. Only the name is set, the certificate itself
* wil be retrieved using a callback.
*
* @param[in] client_name Existing Call Home client name.
* @param[in] endpt_name Existing endpoint name of the client.
* @param[in] name Arbitrary certificate name.
* @return 0 on success, -1 on error.
*/
int nc_server_tls_ch_client_endpt_set_server_cert(const char *client_name, const char *endpt_name, const char *name);
/**
* @brief Add a Call Home trusted certificate list. Can be both a CA or a client one.
*
* @param[in] client_name Existing Call Home client name.
* @param[in] endpt_name Existing endpoint name of the client.
* @param[in] name Arbitary name identifying this certificate list.
* @return 0 on success, -1 on error.
*/
int nc_server_tls_ch_client_endpt_add_trusted_cert_list(const char *client_name, const char *endpt_name, const char *name);
/**
* @brief Remove a set Call Home trusted certificate list. CRLs and CTN entries are not affected.
*
* @param[in] client_name Existing Call Home client name.
* @param[in] endpt_name Existing endpoint name of the client.
* @param[in] name Name of the certificate list to delete. NULL deletes all the lists.
* @return 0 on success, -1 on not found.
*/
int nc_server_tls_ch_client_endpt_del_trusted_cert_list(const char *client_name, const char *endpt_name, const char *name);
/**
* @brief Set trusted Call Home Certificate Authority certificate locations. There
* can only be one file and one directory, they are replaced if already set.
*
* @param[in] client_name Existing Call Home client name.
* @param[in] endpt_name Existing endpoint name of the client.
* @param[in] ca_file Path to a trusted CA cert store file in PEM format.
* Can be NULL.
* @param[in] ca_dir Path to a trusted CA cert store hashed directory
* (c_rehash utility can be used to create hashes)
* with PEM files. Can be NULL.
* @return 0 on success, -1 on error.
*/
int nc_server_tls_ch_client_endpt_set_trusted_ca_paths(const char *client_name, const char *endpt_name, const char *ca_file,
const char *ca_dir);
/**
* @brief Set Call Home Certificate Revocation List locations. There can only be
* one file and one directory, they are replaced if already set.
*
* @param[in] client_name Existing Call Home client name.
* @param[in] endpt_name Existing endpoint name of the client.
* @param[in] crl_file Path to a CRL store file in PEM format. Can be NULL.
* @param[in] crl_dir Path to a CRL store hashed directory (c_rehash utility
* can be used to create hashes) with PEM files. Can be NULL.
* @return 0 on success, -1 on error.
*/
int nc_server_tls_ch_client_endpt_set_crl_paths(const char *client_name, const char *endpt_name, const char *crl_file,
const char *crl_dir);
/**
* @brief Destroy and clean Call Home CRLs. Call Home certificates, private keys,
* and CTN entries are not affected.
*
* @param[in] client_name Existing Call Home client name.
* @param[in] endpt_name Existing endpoint name of the client.
*/
void nc_server_tls_ch_client_endpt_clear_crls(const char *client_name, const char *endpt_name);
/**
* @brief Add a cert-to-name entry.
*
* It is possible to add an entry step-by-step, specifying first only @p ip and in later calls
* @p fingerprint, @p map_type, and optionally @p name spearately.
*
* @param[in] client_name Existing Call Home client name.
* @param[in] endpt_name Existing endpoint name of the client.
* @param[in] id Priority of the entry. It must be unique. If already exists, the entry with this id
* is modified.
* @param[in] fingerprint Matching certificate fingerprint. If NULL, kept temporarily unset.
* @param[in] map_type Type of username-certificate mapping. If 0, kept temporarily unset.
* @param[in] name Specific username used only if @p map_type == NC_TLS_CTN_SPECIFED.
* @return 0 on success, -1 on error.
*/
int nc_server_tls_ch_client_endpt_add_ctn(const char *client_name, const char *endpt_name, uint32_t id,
const char *fingerprint, NC_TLS_CTN_MAPTYPE map_type, const char *name);
/**
* @brief Remove a Call Home cert-to-name entry.
*
* @param[in] client_name Existing Call Home client name.
* @param[in] endpt_name Existing endpoint name of the client.
* @param[in] id Priority of the entry. -1 matches all the priorities.
* @param[in] fingerprint Fingerprint fo the entry. NULL matches all the fingerprints.
* @param[in] map_type Mapping type of the entry. 0 matches all the mapping types.
* @param[in] name Specific username for the entry. NULL matches all the usernames.
* @return 0 on success, -1 on not finding any match.
*/
int nc_server_tls_ch_client_endpt_del_ctn(const char *client_name, const char *endpt_name, int64_t id,
const char *fingerprint, NC_TLS_CTN_MAPTYPE map_type, const char *name);
/**
* @brief Get a Call Home cert-to-name entry.
*
* If a parameter is NULL, it is ignored. If its dereferenced value is NULL,
* it is filled and returned. If the value is set, it is used as a filter.
* Returns first matching entry.
*
* @param[in] client_name Existing Call Home client name.
* @param[in] endpt_name Existing endpoint name of the client.
* @param[in,out] id Priority of the entry.
* @param[in,out] fingerprint Fingerprint fo the entry.
* @param[in,out] map_type Mapping type of the entry.
* @param[in,out] name Specific username for the entry.
* @return 0 on success, -1 on not finding any match.
*/
int nc_server_tls_ch_client_endpt_get_ctn(const char *client_name, const char *endpt_name, uint32_t *id, char **fingerprint,
NC_TLS_CTN_MAPTYPE *map_type, char **name);
/** @} Server-side Call Home on TLS */
#endif /* NC_ENABLED_TLS */
#ifdef __cplusplus
}
#endif
#endif /* NC_SESSION_SERVER_CH_H_ */

1709
src/session_server_ssh.c Normal file

File diff suppressed because it is too large Load diff

2005
src/session_server_tls.c Normal file

File diff suppressed because it is too large Load diff