1
0
Fork 0

Adding upstream version 3.7.1.

Signed-off-by: Daniel Baumann <daniel@debian.org>
This commit is contained in:
Daniel Baumann 2025-05-02 04:10:16 +02:00
parent bf988a2857
commit 5c9c809cee
Signed by: daniel
GPG key ID: FBB4F0E80A80222F
26 changed files with 690 additions and 232 deletions

View file

@ -38,8 +38,13 @@
#include "netconf.h"
#include "session.h"
#include "session_p.h"
#ifdef NC_ENABLED_SSH_TLS
#include "session_wrapper.h"
#endif /* NC_ENABLED_SSH_TLS */
const char *nc_msgtype2str[] = {
"error",
"would block",
@ -938,6 +943,7 @@ nc_write_msg_io(struct nc_session *session, int io_timeout, int type, ...)
switch (reply->type) {
case NC_RPL_OK:
assert(rpc_envp != NULL);
if (lyd_new_opaq2(reply_envp, NULL, "ok", NULL, rpc_envp->name.prefix, rpc_envp->name.module_ns, NULL)) {
lyd_free_tree(reply_envp);

View file

@ -96,7 +96,7 @@ nc_libssh_thread_verbosity(int level)
#endif /* NC_ENABLED_SSH_TLS */
static void
void
nc_log_vprintf(const struct nc_session *session, NC_VERB_LEVEL level, const char *format, va_list args)
{
va_list args2;
@ -140,6 +140,7 @@ nc_log_vprintf(const struct nc_session *session, NC_VERB_LEVEL level, const char
cleanup:
free(msg);
va_end(args2);
}
void

View file

@ -35,6 +35,16 @@
*/
void nc_log_printf(const struct nc_session *session, NC_VERB_LEVEL level, const char *format, ...);
/**
* @brief Internal printing function with va_list
*
* @param[in] session Optional NETCONF session that generated the message
* @param[in] level Verbose level
* @param[in] format Formatting string
* @param[in] args va_list with arguments
*/
void nc_log_vprintf(const struct nc_session *session, NC_VERB_LEVEL level, const char *format, va_list args);
/**
* @brief Verbose level variable
*/

View file

@ -1,10 +1,11 @@
/**
* @file messages_p.h
* @author Radek Krejci <rkrejci@cesnet.cz>
* @author Michal Vasko <mvasko@cesnet.cz>
* @brief libnetconf2's private functions and structures of NETCONF messages.
*
* @copyright
* Copyright (c) 2021 CESNET, z.s.p.o.
* Copyright (c) 2021 - 2025 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.
@ -34,7 +35,7 @@ struct nc_server_reply {
struct nc_server_reply_data {
NC_RPL type;
struct lyd_node *data;
struct lyd_node *data; /**< always points to the operation, for both RPCs and actions */
int free;
NC_WD_MODE wd;
};

View file

@ -4,7 +4,7 @@
* @brief libnetconf2 - server NETCONF messages functions
*
* @copyright
* Copyright (c) 2015 CESNET, z.s.p.o.
* Copyright (c) 2015 - 2025 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.
@ -261,14 +261,21 @@ nc_err(const struct ly_ctx *ctx, NC_ERR tag, ...)
{
va_list ap;
struct lyd_node *err = NULL;
const struct lys_module *nc_mod;
NC_ERR_TYPE type;
const char *arg1, *arg2;
uint32_t sid;
NC_CHECK_ARG_RET(NULL, tag, NULL);
NC_CHECK_ARG_RET(NULL, tag, ctx, NULL);
nc_mod = ly_ctx_get_module_implemented(ctx, "ietf-netconf");
if (!nc_mod) {
ERR(NULL, "Module \"ietf-netconf\" missing in the context.");
return NULL;
}
/* rpc-error */
if (lyd_new_opaq2(NULL, ctx, "rpc-error", NULL, NULL, NC_NS_BASE, &err)) {
if (lyd_new_inner(NULL, nc_mod, "rpc-error", 0, &err)) {
return NULL;
}
@ -337,17 +344,17 @@ nc_err(const struct ly_ctx *ctx, NC_ERR tag, ...)
ERRARG(NULL, "tag");
goto fail;
}
if (lyd_new_opaq2(err, NULL, "error-type", nc_err_type2str(type), NULL, NC_NS_BASE, NULL)) {
if (lyd_new_term(err, NULL, "error-type", nc_err_type2str(type), 0, NULL)) {
goto fail;
}
/* error-tag */
if (lyd_new_opaq2(err, NULL, "error-tag", nc_err_tag2str(tag), NULL, NC_NS_BASE, NULL)) {
if (lyd_new_term(err, NULL, "error-tag", nc_err_tag2str(tag), 0, NULL)) {
goto fail;
}
/* error-severity */
if (lyd_new_opaq2(err, NULL, "error-severity", "error", NULL, NC_NS_BASE, NULL)) {
if (lyd_new_term(err, NULL, "error-severity", "error", 0, NULL)) {
goto fail;
}
@ -474,13 +481,17 @@ fail:
API NC_ERR_TYPE
nc_err_get_type(const struct lyd_node *err)
{
struct lyd_node *match;
const struct lysc_node *schema;
struct lyd_node *match = NULL;
NC_CHECK_ARG_RET(NULL, err, 0);
lyd_find_sibling_opaq_next(lyd_child(err), "error-type", &match);
schema = lys_find_path(NULL, err->schema, "error-type", 0);
if (schema) {
lyd_find_sibling_val(lyd_child(err), schema, NULL, 0, &match);
}
if (match) {
return nc_err_str2type(((struct lyd_node_opaq *)match)->value);
return nc_err_str2type(lyd_get_value(match));
}
return 0;
@ -489,13 +500,17 @@ nc_err_get_type(const struct lyd_node *err)
API NC_ERR
nc_err_get_tag(const struct lyd_node *err)
{
struct lyd_node *match;
const struct lysc_node *schema;
struct lyd_node *match = NULL;
NC_CHECK_ARG_RET(NULL, err, 0);
lyd_find_sibling_opaq_next(lyd_child(err), "error-tag", &match);
schema = lys_find_path(NULL, err->schema, "error-tag", 0);
if (schema) {
lyd_find_sibling_val(lyd_child(err), schema, NULL, 0, &match);
}
if (match) {
return nc_err_str2tag(((struct lyd_node_opaq *)match)->value);
return nc_err_str2tag(lyd_get_value(match));
}
return 0;
@ -504,17 +519,25 @@ nc_err_get_tag(const struct lyd_node *err)
API int
nc_err_set_app_tag(struct lyd_node *err, const char *error_app_tag)
{
const struct lysc_node *schema;
struct lyd_node *match;
NC_CHECK_ARG_RET(NULL, err, error_app_tag, -1);
/* find the schema node */
schema = lys_find_path(NULL, err->schema, "error-app-tag", 0);
if (!schema) {
return -1;
}
/* remove previous node */
lyd_find_sibling_opaq_next(lyd_child(err), "error-app-tag", &match);
lyd_find_sibling_val(lyd_child(err), schema, NULL, 0, &match);
if (match) {
lyd_free_tree(match);
}
if (lyd_new_opaq2(err, NULL, "error-app-tag", error_app_tag, NULL, NC_NS_BASE, NULL)) {
/* create the node */
if (lyd_new_term(err, NULL, "error-app-tag", error_app_tag, 0, &match)) {
return -1;
}
@ -524,13 +547,17 @@ nc_err_set_app_tag(struct lyd_node *err, const char *error_app_tag)
API const char *
nc_err_get_app_tag(const struct lyd_node *err)
{
struct lyd_node *match;
const struct lysc_node *schema;
struct lyd_node *match = NULL;
NC_CHECK_ARG_RET(NULL, err, NULL);
lyd_find_sibling_opaq_next(lyd_child(err), "error-app-tag", &match);
schema = lys_find_path(NULL, err->schema, "error-tag", 0);
if (schema) {
lyd_find_sibling_val(lyd_child(err), schema, NULL, 0, &match);
}
if (match) {
return ((struct lyd_node_opaq *)match)->value;
return lyd_get_value(match);
}
return NULL;
@ -539,17 +566,25 @@ nc_err_get_app_tag(const struct lyd_node *err)
API int
nc_err_set_path(struct lyd_node *err, const char *error_path)
{
const struct lysc_node *schema;
struct lyd_node *match;
NC_CHECK_ARG_RET(NULL, err, error_path, -1);
/* find the schema node */
schema = lys_find_path(NULL, err->schema, "error-path", 0);
if (!schema) {
return -1;
}
/* remove previous node */
lyd_find_sibling_opaq_next(lyd_child(err), "error-path", &match);
lyd_find_sibling_val(lyd_child(err), schema, NULL, 0, &match);
if (match) {
lyd_free_tree(match);
}
if (lyd_new_opaq2(err, NULL, "error-path", error_path, NULL, NC_NS_BASE, NULL)) {
/* create the node */
if (lyd_new_term(err, NULL, "error-path", error_path, 0, &match)) {
return -1;
}
@ -559,13 +594,17 @@ nc_err_set_path(struct lyd_node *err, const char *error_path)
API const char *
nc_err_get_path(const struct lyd_node *err)
{
struct lyd_node *match;
const struct lysc_node *schema;
struct lyd_node *match = NULL;
NC_CHECK_ARG_RET(NULL, err, NULL);
lyd_find_sibling_opaq_next(lyd_child(err), "error-path", &match);
schema = lys_find_path(NULL, err->schema, "error-path", 0);
if (schema) {
lyd_find_sibling_val(lyd_child(err), schema, NULL, 0, &match);
}
if (match) {
return ((struct lyd_node_opaq *)match)->value;
return lyd_get_value(match);
}
return NULL;
@ -574,21 +613,37 @@ nc_err_get_path(const struct lyd_node *err)
API int
nc_err_set_msg(struct lyd_node *err, const char *error_message, const char *lang)
{
struct lyd_node *match;
struct lyd_node *match, *prev_anchor;
struct lyd_attr *attr;
NC_CHECK_ARG_RET(NULL, err, error_message, -1);
/* remove previous node */
lyd_find_sibling_opaq_next(lyd_child(err), "error-message", &match);
if (match) {
/* Change the value of error-message and keep order of elements to comply with appendix-B in RFC 6241. */
lydict_remove(LYD_CTX(err), ((struct lyd_node_opaq *)match)->value);
lydict_insert(LYD_CTX(err), error_message, 0, &(((struct lyd_node_opaq *)match)->value));
return 0;
lyd_free_tree(match);
}
/* find the previous node anchor (last non-opaque node) */
prev_anchor = lyd_child(err);
while (prev_anchor && prev_anchor->next && prev_anchor->next->schema) {
prev_anchor = prev_anchor->next;
}
if (!prev_anchor->schema) {
prev_anchor = NULL;
}
/* create the node at the right place */
if (lyd_new_opaq2(err, NULL, "error-message", error_message, NULL, NC_NS_BASE, &match)) {
return -1;
}
if (prev_anchor) {
lyd_insert_after(prev_anchor, match);
} else if (match->prev != match) {
/* some opaque nodes existed, this must be the first */
lyd_insert_before(lyd_child(err), match);
}
if (lang && lyd_new_attr(match, NULL, "xml:lang", lang, &attr)) {
lyd_free_tree(match);
return -1;
@ -733,6 +788,16 @@ nc_server_rpc_free(struct nc_server_rpc *rpc)
free(rpc);
}
API NC_RPL
nc_server_reply_type(struct nc_server_reply *reply)
{
if (!reply) {
return 0;
}
return reply->type;
}
API void
nc_server_reply_free(struct nc_server_reply *reply)
{
@ -747,7 +812,7 @@ nc_server_reply_free(struct nc_server_reply *reply)
case NC_RPL_DATA:
data_rpl = (struct nc_server_reply_data *)reply;
if (data_rpl->free) {
lyd_free_siblings(data_rpl->data);
lyd_free_all(data_rpl->data);
}
break;
case NC_RPL_OK:
@ -766,7 +831,7 @@ nc_server_reply_free(struct nc_server_reply *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 nc_server_notif *ntf = NULL;
struct lyd_node *elem;
int found;
@ -791,9 +856,10 @@ nc_server_notif_new(struct lyd_node *event, char *eventtime, NC_PARAMTYPE paramt
if (paramtype == NC_PARAMTYPE_DUP_AND_FREE) {
ntf->eventtime = strdup(eventtime);
NC_CHECK_ERRMEM_GOTO(!ntf->eventtime, , error);
if (lyd_dup_single(event, NULL, LYD_DUP_RECURSIVE, &ntf->ntf)) {
free(ntf);
return NULL;
goto error;
}
} else {
ntf->eventtime = eventtime;
@ -802,6 +868,11 @@ nc_server_notif_new(struct lyd_node *event, char *eventtime, NC_PARAMTYPE paramt
ntf->free = (paramtype == NC_PARAMTYPE_CONST ? 0 : 1);
return ntf;
error:
free(ntf->eventtime);
free(ntf);
return NULL;
}
API void

View file

@ -210,7 +210,7 @@ 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\>.
* @param[in] error_path New value of \<error-path\> in JSON format.
* @return 0 on success, -1 on error.
*/
int nc_err_set_path(struct lyd_node *err, const char *error_path);
@ -286,6 +286,14 @@ int nc_err_add_bad_ns(struct lyd_node *err, const char *ns_name);
*/
int nc_err_add_info_other(struct lyd_node *err, struct lyd_node *other);
/**
* @brief Get server reply message type.
*
* @param[in] reply Server rpc-reply object to analyze.
* @return Reply type.
*/
NC_RPL nc_server_reply_type(struct nc_server_reply *reply);
/**
* @brief Free a server rpc-reply object.
*

View file

@ -1,10 +1,11 @@
/**
* @file netconf.h
* @author Radek Krejci <rkrejci@cesnet.cz>
* @author Michal Vasko <mvasko@cesnet.cz>
* @brief libnetconf2's general public functions and structures definitions.
*
* @copyright
* Copyright (c) 2015 - 2021 CESNET, z.s.p.o.
* Copyright (c) 2015 - 2025 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.
@ -51,7 +52,7 @@ extern "C" {
/**
* @brief Enumeration of reasons of the NETCONF session termination as defined in RFC 6470.
*/
typedef enum NC_SESSION_TERM_REASON {
typedef enum {
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 */
@ -65,7 +66,7 @@ typedef enum NC_SESSION_TERM_REASON {
/**
* @brief Enumeration of NETCONF message types.
*/
typedef enum NC_MSG_TYPE {
typedef enum {
NC_MSG_ERROR, /**< error return value */
NC_MSG_WOULDBLOCK, /**< timeout return value */
NC_MSG_NONE, /**< no message at input or message was processed internally */
@ -85,7 +86,7 @@ extern const char *nc_msgtype2str[];
/**
* @brief Enumeration of the supported types of datastores defined by NETCONF
*/
typedef enum NC_DATASTORE_TYPE {
typedef enum {
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 */
@ -97,7 +98,7 @@ typedef enum NC_DATASTORE_TYPE {
/**
* @brief Enumeration of NETCONF with-defaults capability modes.
*/
typedef enum NC_WITHDEFAULTS_MODE {
typedef enum {
NC_WD_UNKNOWN = 0, /**< invalid mode */
NC_WD_ALL, /**< report-all mode */
NC_WD_ALL_TAG, /**< report-all-tagged mode */
@ -108,7 +109,7 @@ typedef enum NC_WITHDEFAULTS_MODE {
/**
* @brief Enumeration of NETCONF (both server and client) rpc-reply types.
*/
typedef enum NC_REPLY {
typedef enum {
NC_RPL_OK, /**< OK rpc-reply */
NC_RPL_DATA, /**< DATA rpc-reply */
NC_RPL_ERROR, /**< ERROR rpc-reply */
@ -118,7 +119,7 @@ typedef enum NC_REPLY {
/**
* @brief Enumeration of function parameter treatments.
*/
typedef enum NC_PARAMTYPE {
typedef enum {
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 */

View file

@ -2617,6 +2617,14 @@ nc_server_config_encryption_alg(const struct lyd_node *node, enum nc_operation o
/* get the algorithm name and append it to supported algs */
alg = ((struct lyd_node_term *)node)->value.ident->name;
/* YANG IDs cannot begin with a number, need to convert them to the correct form */
if (!strcmp(alg, "triple-des-cbc")) {
alg = "3des-cbc";
} else if (!strcmp(alg, "triple-des-ctr")) {
alg = "3des-ctr";
}
if (nc_server_config_transport_params(alg, &opts->encryption_algs, op)) {
ret = 1;
goto cleanup;
@ -3224,7 +3232,7 @@ nc_server_config_create_cert_to_name(const struct lyd_node *node, struct nc_serv
assert(!strcmp(LYD_NAME(node), "cert-to-name"));
/* find the list's key */
/* find the list's key - ignore result using assert of reference argument instead */
lyd_find_path(node, "id", 0, &n);
assert(n);
id = ((struct lyd_node_term *)n)->value.uint32;
@ -4006,13 +4014,20 @@ nc_server_config_parse_netconf_server(const struct lyd_node *node, enum nc_opera
}
int
nc_server_config_ln2_netconf_server(const struct lyd_node *node, enum nc_operation op)
nc_server_config_ln2_netconf_server(const struct lyd_node *UNUSED(node), enum nc_operation op)
{
(void) node;
uint32_t i;
assert((op == NC_OP_CREATE) || (op == NC_OP_DELETE));
if (op == NC_OP_DELETE) {
/* delete ignored modules */
for (i = 0; i < server_opts.ignored_mod_count; ++i) {
free(server_opts.ignored_modules[i]);
}
free(server_opts.ignored_modules);
server_opts.ignored_modules = NULL;
server_opts.ignored_mod_count = 0;
#ifdef NC_ENABLED_SSH_TLS
/* delete the intervals */
@ -4155,6 +4170,45 @@ cleanup:
#endif /* NC_ENABLED_SSH_TLS */
static int
nc_server_config_ignored_module(const struct lyd_node *node, enum nc_operation op)
{
int ret = 0;
const char *mod_name;
uint16_t i;
assert(!strcmp(LYD_NAME(node), "ignored-hello-module"));
mod_name = lyd_get_value(node);
if (op == NC_OP_CREATE) {
/* add the module */
ret = nc_server_config_realloc(mod_name, (void **)&server_opts.ignored_modules,
sizeof *server_opts.ignored_modules, &server_opts.ignored_mod_count);
} else {
/* find the module */
for (i = 0; i < server_opts.ignored_mod_count; ++i) {
if (!strcmp(server_opts.ignored_modules[i], mod_name)) {
break;
}
}
assert(i < server_opts.ignored_mod_count);
/* remove the module by replacing it with the last */
free(server_opts.ignored_modules[i]);
if (i < server_opts.ignored_mod_count - 1) {
server_opts.ignored_modules[i] = server_opts.ignored_modules[server_opts.ignored_mod_count - 1];
}
--server_opts.ignored_mod_count;
if (!server_opts.ignored_mod_count) {
free(server_opts.ignored_modules);
server_opts.ignored_modules = NULL;
}
}
return ret;
}
static int
nc_server_config_parse_libnetconf2_netconf_server(const struct lyd_node *node, enum nc_operation op)
{
@ -4169,6 +4223,9 @@ nc_server_config_parse_libnetconf2_netconf_server(const struct lyd_node *node, e
ret = nc_server_config_interval(node, op);
}
#endif /* NC_ENABLED_SSH_TLS */
else if (!strcmp(name, "ignored-hello-module")) {
ret = nc_server_config_ignored_module(node, op);
}
if (ret) {
ERR(NULL, "Configuring node \"%s\" failed.", LYD_NAME(node));

View file

@ -29,8 +29,13 @@
#include "log_p.h"
#include "session.h"
#include "session_p.h"
#ifdef NC_ENABLED_SSH_TLS
#include "session_wrapper.h"
#endif /* NC_ENABLED_SSH_TLS */
int
nc_server_config_create(const struct ly_ctx *ctx, struct lyd_node **tree, const char *value, const char *path_fmt, ...)
{

View file

@ -498,11 +498,18 @@ _nc_server_config_add_ssh_user_password(const struct ly_ctx *ctx, const char *tr
int ret = 0;
char *hashed_pw = NULL;
const char *salt = "$6$idsizuippipk$";
struct crypt_data cdata = {0};
struct crypt_data *cdata = NULL;
NC_CHECK_ARG_RET(NULL, ctx, tree_path, password, config, 1);
hashed_pw = crypt_r(password, salt, &cdata);
cdata = (struct crypt_data *) calloc(sizeof(struct crypt_data), 1);
if (cdata == NULL) {
ERR(NULL, "Allocation of crypt_data struct failed.");
ret = 1;
goto cleanup;
}
hashed_pw = crypt_r(password, salt, cdata);
if (!hashed_pw) {
ERR(NULL, "Hashing password failed (%s).", strerror(errno));
ret = 1;
@ -515,6 +522,7 @@ _nc_server_config_add_ssh_user_password(const struct ly_ctx *ctx, const char *tr
}
cleanup:
free(cdata);
return ret;
}

View file

@ -37,9 +37,10 @@
#ifdef NC_ENABLED_SSH_TLS
#include "session_wrapper.h"
#include <curl/curl.h>
#include <libssh/libssh.h>
#include "session_wrapper.h"
#endif /* NC_ENABLED_SSH_TLS */
@ -880,8 +881,25 @@ nc_session_free(struct nc_session *session, void (*data_free)(void *))
struct ly_in *msg;
struct timespec ts;
void *p;
NC_STATUS status;
if (!session || (session->status == NC_STATUS_CLOSING)) {
if (!session) {
return;
}
if ((session->side == NC_SERVER) && (session->flags & NC_SESSION_CALLHOME)) {
/* CH LOCK */
pthread_mutex_lock(&session->opts.server.ch_lock);
}
status = session->status;
if ((session->side == NC_SERVER) && (session->flags & NC_SESSION_CALLHOME)) {
/* CH UNLOCK */
pthread_mutex_unlock(&session->opts.server.ch_lock);
}
if (status == NC_STATUS_CLOSING) {
return;
}
@ -948,7 +966,7 @@ nc_session_free(struct nc_session *session, void (*data_free)(void *))
nc_session_client_msgs_unlock(session, __func__);
}
if (session->status == NC_STATUS_RUNNING) {
if ((session->status == NC_STATUS_RUNNING) && nc_session_is_connected(session)) {
/* receive any leftover messages */
while (nc_read_msg_poll_io(session, 0, &msg) == 1) {
ly_in_free(msg, 1);
@ -1117,8 +1135,6 @@ nc_server_get_cpblts_version(const struct ly_ctx *ctx, LYS_VERSION version)
cpblts[1] = strdup("urn:ietf:params:netconf:base:1.1");
count = 2;
/* capabilities */
mod = ly_ctx_get_module_implemented(ctx, "ietf-netconf");
if (mod) {
if (lys_feature_value(mod, "writable-running") == LY_SUCCESS) {
@ -1153,11 +1169,15 @@ nc_server_get_cpblts_version(const struct ly_ctx *ctx, LYS_VERSION version)
}
}
/* HELLO LOCK */
pthread_rwlock_rdlock(&server_opts.hello_lock);
mod = ly_ctx_get_module_implemented(ctx, "ietf-netconf-with-defaults");
if (mod) {
wd_basic_mode = ATOMIC_LOAD_RELAXED(server_opts.wd_basic_mode);
wd_basic_mode = server_opts.wd_basic_mode;
if (!wd_basic_mode) {
VRB(NULL, "with-defaults capability will not be advertised even though \"ietf-netconf-with-defaults\" model is present, unknown basic-mode.");
VRB(NULL, "with-defaults capability will not be advertised even though \"ietf-netconf-with-defaults\" "
"model is present, unknown basic-mode.");
} else {
strcpy(str, "urn:ietf:params:netconf:capability:with-defaults:1.0");
switch (wd_basic_mode) {
@ -1172,10 +1192,10 @@ nc_server_get_cpblts_version(const struct ly_ctx *ctx, LYS_VERSION version)
break;
default:
ERRINT;
break;
goto unlock_error;
}
wd_also_supported = ATOMIC_LOAD_RELAXED(server_opts.wd_also_supported);
wd_also_supported = server_opts.wd_also_supported;
if (wd_also_supported) {
strcat(str, "&also-supported=");
if (wd_also_supported & NC_WD_ALL) {
@ -1205,10 +1225,15 @@ nc_server_get_cpblts_version(const struct ly_ctx *ctx, LYS_VERSION version)
/* models */
u = 0;
while ((mod = ly_ctx_get_module_iter(ctx, &u))) {
if (nc_server_is_mod_ignored(mod->name)) {
/* ignored, not part of the cababilities */
continue;
}
if (!strcmp(mod->name, "ietf-yang-library")) {
if (!mod->revision || (strcmp(mod->revision, "2016-06-21") && strcmp(mod->revision, "2019-01-04"))) {
ERR(NULL, "Unknown \"ietf-yang-library\" revision, only 2016-06-21 and 2019-01-04 are supported.");
goto error;
goto unlock_error;
}
/* get content-id */
@ -1293,11 +1318,18 @@ nc_server_get_cpblts_version(const struct ly_ctx *ctx, LYS_VERSION version)
add_cpblt(str, &cpblts, &size, &count);
}
/* HELLO UNLOCK */
pthread_rwlock_unlock(&server_opts.hello_lock);
/* ending NULL capability */
add_cpblt(NULL, &cpblts, &size, &count);
return cpblts;
unlock_error:
/* HELLO UNLOCK */
pthread_rwlock_unlock(&server_opts.hello_lock);
error:
free(cpblts);
return NULL;

View file

@ -1749,7 +1749,7 @@ nc_sock_connect(const char *src_addr, uint16_t src_port, const char *dst_addr, u
hints.ai_protocol = IPPROTO_TCP;
i = getaddrinfo(dst_addr, dst_port_str, &hints, &res_list);
if (i != 0) {
ERR(NULL, "Unable to translate the host address (%s).", gai_strerror(i));
ERR(NULL, "Unable to translate the host address \"%s\" (%s).", dst_addr, gai_strerror(i));
goto error;
}
@ -2006,6 +2006,10 @@ nc_client_init(void)
}
#ifdef NC_ENABLED_SSH_TLS
if (nc_tls_backend_init_wrap()) {
ERR(NULL, "%s: failed to init the SSL library backend.", __func__);
return -1;
}
if (ssh_init()) {
ERR(NULL, "%s: failed to init libssh.", __func__);
return -1;
@ -2024,6 +2028,7 @@ nc_client_destroy(void)
nc_client_ch_del_bind(NULL, 0, 0);
nc_client_ssh_destroy_opts();
nc_client_tls_destroy_opts();
nc_tls_backend_destroy_wrap();
ssh_finalize();
#endif /* NC_ENABLED_SSH_TLS */
}

View file

@ -52,29 +52,79 @@
#include <mbedtls/x509_crt.h>
/**
* @brief Converts mbedTLS error codes to a string.
* @brief Converts MbedTLS error code to a string and merges it with an arbitrary error message.
*
* Some mbedTLS functions may return 'high' and some 'low' level errors, try to handle both cases this way.
*
* @param[in] err MbedTLS error code.
* @return Error string.
* @param[in] session NETCONF session.
* @param[in] mbedtls_err_code MbedTLS error code.
* @param[in] orig_err_msg_fmt Original error message format string.
* @param[in] ... Additional arguments for the original error message.
*/
static const char *
nc_get_mbedtls_str_err(int err)
static void
nc_mbedtls_strerr(const struct nc_session *session, int mbedtls_err_code, const char *orig_err_msg_fmt, ...)
{
const char *err_str;
va_list args;
char *err_buf = NULL, *err_msg_fmt = NULL;
size_t err_buf_len = 0, err_msg_fmt_len = 0;
const char *high_err_str, *low_err_str;
err_str = mbedtls_high_level_strerr(err);
if (err_str) {
return err_str;
va_start(args, orig_err_msg_fmt);
/* get the length of the error strings */
high_err_str = mbedtls_high_level_strerr(mbedtls_err_code);
low_err_str = mbedtls_low_level_strerr(mbedtls_err_code);
if (high_err_str) {
err_buf_len += strlen(high_err_str);
}
if (low_err_str) {
err_buf_len += strlen(low_err_str);
}
if (!err_buf_len) {
/* just print the original error message */
nc_log_vprintf(session, NC_VERB_ERROR, orig_err_msg_fmt, args);
goto cleanup;
}
err_str = mbedtls_low_level_strerr(err);
if (err_str) {
return err_str;
if (high_err_str && low_err_str) {
/* for a colon and 2 spaces */
err_buf_len += 3;
}
return "unknown error";
/* allocate the mbedtls error buffer */
err_buf = malloc(err_buf_len + 1);
if (!err_buf) {
/* just print the original error message */
nc_log_vprintf(session, NC_VERB_ERROR, orig_err_msg_fmt, args);
goto cleanup;
}
/* fill the error buffer and print it */
if (high_err_str && low_err_str) {
snprintf(err_buf, err_buf_len + 1, "%s : %s", high_err_str, low_err_str);
} else if (high_err_str) {
snprintf(err_buf, err_buf_len + 1, "%s", high_err_str);
} else {
snprintf(err_buf, err_buf_len + 1, "%s", low_err_str);
}
/* allocate the new error format string buffer, err_msg = "orig_err_msg (MbedTLS err)." */
err_msg_fmt_len = strlen(orig_err_msg_fmt) + strlen(" (") + strlen(err_buf) + strlen(").");
err_msg_fmt = malloc(err_msg_fmt_len + 1);
if (!err_msg_fmt) {
/* just print the original error message */
nc_log_vprintf(session, NC_VERB_ERROR, orig_err_msg_fmt, args);
goto cleanup;
}
/* fill the new error format string */
snprintf(err_msg_fmt, err_msg_fmt_len + 1, "%s (%s).", orig_err_msg_fmt, err_buf);
/* print the error message */
nc_log_vprintf(session, NC_VERB_ERROR, err_msg_fmt, args);
cleanup:
va_end(args);
free(err_msg_fmt);
free(err_buf);
}
/**
@ -100,7 +150,7 @@ nc_server_tls_dn2str(const mbedtls_x509_name *dn)
}
if (r < 1) {
free(str);
ERR(NULL, "Failed to convert DN to string (%s).", nc_get_mbedtls_str_err(r));
nc_mbedtls_strerr(NULL, r, "Failed to convert DN to string");
return NULL;
}
@ -132,7 +182,7 @@ nc_tls_rng_new(mbedtls_ctr_drbg_context **ctr_drbg, mbedtls_entropy_context **en
rc = mbedtls_ctr_drbg_seed(*ctr_drbg, mbedtls_entropy_func, *entropy, NULL, 0);
if (rc) {
ERR(NULL, "Seeding ctr_drbg failed (%s).", nc_get_mbedtls_str_err(rc));
nc_mbedtls_strerr(NULL, rc, "Seeding ctr_drbg failed");
goto fail;
}
@ -194,6 +244,27 @@ nc_tls_get_verify_err_str(int err)
return err_buf;
}
int
nc_tls_backend_init_wrap(void)
{
int r;
r = psa_crypto_init();
if (r) {
nc_mbedtls_strerr(NULL, r, "Failed to initialize PSA crypto");
return -1;
}
return 0;
}
void
nc_tls_backend_destroy_wrap(void)
{
mbedtls_psa_crypto_free();
}
void *
nc_tls_session_new_wrap(void *tls_cfg)
{
@ -207,7 +278,7 @@ nc_tls_session_new_wrap(void *tls_cfg)
rc = mbedtls_ssl_setup(session, tls_cfg);
if (rc) {
ERR(NULL, "Setting up TLS session failed (%s).", nc_get_mbedtls_str_err(rc));
nc_mbedtls_strerr(NULL, rc, "Setting up TLS session failed");
mbedtls_ssl_free(session);
free(session);
return NULL;
@ -335,7 +406,7 @@ nc_tls_pem_to_cert_wrap(const char *cert_data)
rc = mbedtls_x509_crt_parse(cert, (const unsigned char *)cert_data, strlen(cert_data) + 1);
if (rc) {
ERR(NULL, "Parsing certificate data failed (%s).", nc_get_mbedtls_str_err(rc));
nc_mbedtls_strerr(NULL, rc, "Parsing certificate data failed");
nc_tls_cert_destroy_wrap(cert);
return NULL;
}
@ -379,7 +450,7 @@ nc_tls_pem_to_privkey_wrap(const char *privkey_data)
rc = mbedtls_pk_parse_key(pkey, (const unsigned char *)privkey_data, strlen(privkey_data) + 1, NULL, 0, mbedtls_ctr_drbg_random, ctr_drbg);
if (rc) {
ERR(NULL, "Parsing private key data failed (%s).", nc_get_mbedtls_str_err(rc));
nc_mbedtls_strerr(NULL, rc, "Parsing private key data failed");
goto cleanup;
}
@ -755,7 +826,7 @@ nc_server_tls_md5_wrap(void *cert, unsigned char *buf)
rc = mbedtls_md5(c->raw.p, c->raw.len, buf);
if (rc) {
ERR(NULL, "Calculating MD5 digest failed (%s).", nc_get_mbedtls_str_err(rc));
nc_mbedtls_strerr(NULL, rc, "Calculating MD5 digest failed");
return 1;
}
@ -770,7 +841,7 @@ nc_server_tls_sha1_wrap(void *cert, unsigned char *buf)
rc = mbedtls_sha1(c->raw.p, c->raw.len, buf);
if (rc) {
ERR(NULL, "Calculating SHA-1 digest failed (%s).", nc_get_mbedtls_str_err(rc));
nc_mbedtls_strerr(NULL, rc, "Calculating SHA-1 digest failed");
return 1;
}
@ -785,7 +856,7 @@ nc_server_tls_sha224_wrap(void *cert, unsigned char *buf)
rc = mbedtls_sha256(c->raw.p, c->raw.len, buf, 1);
if (rc) {
ERR(NULL, "Calculating SHA-224 digest failed (%s).", nc_get_mbedtls_str_err(rc));
nc_mbedtls_strerr(NULL, rc, "Calculating SHA-224 digest failed");
return 1;
}
@ -800,7 +871,7 @@ nc_server_tls_sha256_wrap(void *cert, unsigned char *buf)
rc = mbedtls_sha256(c->raw.p, c->raw.len, buf, 0);
if (rc) {
ERR(NULL, "Calculating SHA-256 digest failed (%s).", nc_get_mbedtls_str_err(rc));
nc_mbedtls_strerr(NULL, rc, "Calculating SHA-256 digest failed");
return 1;
}
@ -815,7 +886,7 @@ nc_server_tls_sha384_wrap(void *cert, unsigned char *buf)
rc = mbedtls_sha512(c->raw.p, c->raw.len, buf, 1);
if (rc) {
ERR(NULL, "Calculating SHA-384 digest failed (%s).", nc_get_mbedtls_str_err(rc));
nc_mbedtls_strerr(NULL, rc, "Calculating SHA-384 digest failed");
return 1;
}
@ -830,7 +901,7 @@ nc_server_tls_sha512_wrap(void *cert, unsigned char *buf)
rc = mbedtls_sha512(c->raw.p, c->raw.len, buf, 0);
if (rc) {
ERR(NULL, "Calculating SHA-512 digest failed (%s).", nc_get_mbedtls_str_err(rc));
nc_mbedtls_strerr(NULL, rc, "Calculating SHA-512 digest failed");
return 1;
}
@ -979,7 +1050,7 @@ nc_tls_import_privkey_file_wrap(const char *privkey_path)
rc = mbedtls_pk_parse_keyfile(pkey, privkey_path, NULL, mbedtls_ctr_drbg_random, ctr_drbg);
nc_tls_rng_destroy(ctr_drbg, entropy);
if (rc) {
ERR(NULL, "Parsing private key from file \"%s\" failed (%s).", privkey_path, nc_get_mbedtls_str_err(rc));
nc_mbedtls_strerr(NULL, rc, "Parsing private key from file \"%s\" failed", privkey_path);
nc_tls_privkey_destroy_wrap(pkey);
return NULL;
}
@ -1002,7 +1073,7 @@ nc_client_tls_load_cert_key_wrap(const char *cert_path, const char *key_path, vo
ret = mbedtls_x509_crt_parse_file(c, cert_path);
if (ret) {
ERR(NULL, "Parsing certificate from file \"%s\" failed (%s).", cert_path, nc_get_mbedtls_str_err(ret));
nc_mbedtls_strerr(NULL, ret, "Parsing certificate from file \"%s\" failed", cert_path);
goto cleanup;
}
@ -1027,12 +1098,12 @@ nc_client_tls_load_trusted_certs_wrap(void *cert_store, const char *file_path, c
int rc;
if (file_path && ((rc = mbedtls_x509_crt_parse_file(cert_store, file_path)) < 0)) {
ERR(NULL, "Loading CA certificate from file \"%s\" failed (%s).", file_path, nc_get_mbedtls_str_err(rc));
nc_mbedtls_strerr(NULL, rc, "Loading CA certificate from file \"%s\" failed", file_path);
return 1;
}
if (dir_path && ((rc = mbedtls_x509_crt_parse_path(cert_store, dir_path)) < 0)) {
ERR(NULL, "Loading CA certificate from directory \"%s\" failed (%s).", dir_path, nc_get_mbedtls_str_err(rc));
nc_mbedtls_strerr(NULL, rc, "Loading CA certificate from directory \"%s\" failed", dir_path);
return 1;
}
@ -1046,7 +1117,7 @@ nc_client_tls_set_hostname_wrap(void *tls_session, const char *hostname)
rc = mbedtls_ssl_set_hostname(tls_session, hostname);
if (rc) {
ERR(NULL, "Setting hostname failed (%s).", nc_get_mbedtls_str_err(rc));
nc_mbedtls_strerr(NULL, rc, "Setting hostname failed");
return 1;
}
@ -1083,7 +1154,7 @@ nc_tls_setup_config_from_ctx_wrap(struct nc_tls_ctx *tls_ctx, int side, void *tl
rc = mbedtls_ssl_config_defaults(tls_cfg, MBEDTLS_SSL_IS_CLIENT, MBEDTLS_SSL_TRANSPORT_STREAM, MBEDTLS_SSL_PRESET_DEFAULT);
}
if (rc) {
ERR(NULL, "Setting default TLS config failed (%s).", nc_get_mbedtls_str_err(rc));
nc_mbedtls_strerr(NULL, rc, "Setting default TLS config failed");
return 1;
}
@ -1111,25 +1182,17 @@ nc_tls_verify_error_string_wrap(uint32_t err_code)
void
nc_client_tls_print_connect_err_wrap(int connect_ret, const char *peername, void *UNUSED(tls_session))
{
const char *err = nc_get_mbedtls_str_err(connect_ret);
if (err) {
ERR(NULL, "TLS connection to \"%s\" failed (%s).", peername, err);
if (peername) {
nc_mbedtls_strerr(NULL, connect_ret, "TLS connect to host \"%s\" failed", peername);
} else {
ERR(NULL, "TLS connection to \"%s\" failed.", peername);
nc_mbedtls_strerr(NULL, connect_ret, "TLS connect to an unknown host failed");
}
}
void
nc_server_tls_print_accept_err_wrap(int accept_ret, void *UNUSED(tls_session))
{
const char *err = nc_get_mbedtls_str_err(accept_ret);
if (err) {
ERR(NULL, "TLS accept failed (%s).", err);
} else {
ERR(NULL, "TLS accept failed.");
}
nc_mbedtls_strerr(NULL, accept_ret, "TLS accept failed");
}
int
@ -1158,7 +1221,7 @@ nc_base64_decode_wrap(const char *base64, unsigned char **bin)
/* get the size of the decoded data */
rc = mbedtls_base64_decode(NULL, 0, &size, (const unsigned char *)base64, strlen(base64));
if (rc != MBEDTLS_ERR_BASE64_BUFFER_TOO_SMALL) {
ERR(NULL, "Base64 decoding failed (%s).", nc_get_mbedtls_str_err(rc));
nc_mbedtls_strerr(NULL, rc, "Base64 decoding failed");
return -1;
}
@ -1168,7 +1231,7 @@ nc_base64_decode_wrap(const char *base64, unsigned char **bin)
/* decode */
rc = mbedtls_base64_decode(*bin, size, &size, (const unsigned char *)base64, strlen(base64));
if (rc) {
ERR(NULL, "Base64 decoding failed (%s).", nc_get_mbedtls_str_err(rc));
nc_mbedtls_strerr(NULL, rc, "Base64 decoding failed");
free(*bin);
*bin = NULL;
return -1;
@ -1185,7 +1248,7 @@ nc_base64_encode_wrap(const unsigned char *bin, size_t len, char **base64)
rc = mbedtls_base64_encode(NULL, 0, &size, bin, len);
if (rc != MBEDTLS_ERR_BASE64_BUFFER_TOO_SMALL) {
ERR(NULL, "Base64 encoding failed (%s).", nc_get_mbedtls_str_err(rc));
nc_mbedtls_strerr(NULL, rc, "Base64 encoding failed");
return -1;
}
@ -1194,7 +1257,7 @@ nc_base64_encode_wrap(const unsigned char *bin, size_t len, char **base64)
rc = mbedtls_base64_encode((unsigned char *)*base64, size, &size, bin, len);
if (rc) {
ERR(NULL, "Base64 encoding failed (%s).", nc_get_mbedtls_str_err(rc));
nc_mbedtls_strerr(NULL, rc, "Base64 encoding failed");
free(*base64);
*base64 = NULL;
return -1;
@ -1217,13 +1280,13 @@ nc_tls_read_wrap(struct nc_session *session, unsigned char *buf, size_t size)
rc = 0;
break;
case MBEDTLS_ERR_SSL_PEER_CLOSE_NOTIFY:
ERR(session, "Communication socket unexpectedly closed (MbedTLS).");
nc_mbedtls_strerr(session, rc, "Communication socket unexpectedly closed");
session->status = NC_STATUS_INVALID;
session->term_reason = NC_SESSION_TERM_DROPPED;
rc = -1;
break;
default:
ERR(session, "TLS communication error occurred (%s).", nc_get_mbedtls_str_err(rc));
nc_mbedtls_strerr(session, rc, "TLS communication error occurred");
session->status = NC_STATUS_INVALID;
session->term_reason = NC_SESSION_TERM_OTHER;
rc = -1;
@ -1248,11 +1311,11 @@ nc_tls_write_wrap(struct nc_session *session, const unsigned char *buf, size_t s
rc = 0;
break;
case MBEDTLS_ERR_SSL_PEER_CLOSE_NOTIFY:
ERR(session, "TLS connection was properly closed.");
nc_mbedtls_strerr(session, rc, "TLS connection was properly closed.");
rc = -1;
break;
default:
ERR(session, "TLS communication error occurred (%s).", nc_get_mbedtls_str_err(rc));
nc_mbedtls_strerr(session, rc, "TLS communication error occurred");
rc = -1;
break;
}
@ -1281,7 +1344,7 @@ nc_tls_close_notify_wrap(void *tls_session)
while ((rc = mbedtls_ssl_close_notify(tls_session))) {
if ((rc != MBEDTLS_ERR_SSL_WANT_READ) && (rc != MBEDTLS_ERR_SSL_WANT_WRITE)) {
/* some error occurred */
ERR(NULL, "Sending TLS close notify failed (%s).", nc_get_mbedtls_str_err(rc));
nc_mbedtls_strerr(NULL, rc, "Sending TLS close notify failed");
return;
}
}
@ -1300,7 +1363,7 @@ nc_tls_import_cert_file_wrap(const char *cert_path)
rc = mbedtls_x509_crt_parse_file(c, cert_path);
if (rc) {
ERR(NULL, "Parsing certificate from file \"%s\" failed (%s).", cert_path, nc_get_mbedtls_str_err(rc));
nc_mbedtls_strerr(NULL, rc, "Parsing certificate from file \"%s\" failed", cert_path);
nc_tls_cert_destroy_wrap(c);
return NULL;
}
@ -1324,7 +1387,7 @@ nc_tls_export_privkey_pem_wrap(void *pkey)
NC_CHECK_ERRMEM_RET(!pem, NULL);
}
if (rc < 0) {
ERR(NULL, "Exporting private key to PEM format failed (%s).", nc_get_mbedtls_str_err(rc));
nc_mbedtls_strerr(NULL, rc, "Exporting private key to PEM format failed");
free(pem);
return NULL;
}
@ -1369,7 +1432,7 @@ nc_tls_export_pubkey_pem_wrap(void *pkey)
NC_CHECK_ERRMEM_RET(!pem, NULL);
}
if (rc < 0) {
ERR(NULL, "Exporting public key to PEM format failed (%s).", nc_get_mbedtls_str_err(rc));
nc_mbedtls_strerr(NULL, rc, "Exporting public key to PEM format failed");
free(pem);
return NULL;
}
@ -1399,7 +1462,7 @@ nc_tls_get_rsa_pubkey_params_wrap(void *pkey, void **e, void **n)
mbedtls_mpi_init(mod);
if ((rc = mbedtls_rsa_export(mbedtls_pk_rsa(*(mbedtls_pk_context *)pkey), mod, NULL, NULL, NULL, exp))) {
ERR(NULL, "Failed to export RSA public key parameters (%s).", nc_get_mbedtls_str_err(rc));
nc_mbedtls_strerr(NULL, rc, "Failed to export RSA public key parameters");
goto fail;
}
@ -1468,7 +1531,7 @@ nc_tls_get_ec_pubkey_params_wrap(void *pkey, void **q, void **q_grp)
/* get the group and public key */
ret = mbedtls_ecp_export(mbedtls_pk_ec(*(mbedtls_pk_context *)pkey), grp, d, p);
if (ret) {
ERR(NULL, "Failed to export EC public key parameters (%s).", nc_get_mbedtls_str_err(ret));
nc_mbedtls_strerr(NULL, ret, "Failed to export EC private key parameters");
ret = 1;
goto cleanup;
}
@ -1507,7 +1570,7 @@ nc_tls_ec_point_to_bin_wrap(void *q, void *q_grp, unsigned char **bin, int *bin_
NC_CHECK_ERRMEM_RET(!buf, 1);
}
if (rc) {
ERR(NULL, "Failed to write EC public key binary (%s).", nc_get_mbedtls_str_err(rc));
nc_mbedtls_strerr(NULL, rc, "Failed to write EC public key binary");
free(buf);
return 1;
}
@ -1544,7 +1607,7 @@ nc_tls_mpi2bin_wrap(void *mpi, unsigned char **bin, int *bin_len)
rc = mbedtls_mpi_write_binary(mpi, buf, buf_len);
if (rc) {
ERR(NULL, "Failed to convert MPI to binary (%s).", nc_get_mbedtls_str_err(rc));
nc_mbedtls_strerr(NULL, rc, "Failed to convert MPI to binary");
free(buf);
return 1;
}
@ -1567,7 +1630,7 @@ nc_tls_import_pubkey_file_wrap(const char *pubkey_path)
rc = mbedtls_pk_parse_public_keyfile(pk, pubkey_path);
if (rc) {
ERR(NULL, "Parsing public key from file \"%s\" failed (%s).", pubkey_path, nc_get_mbedtls_str_err(rc));
nc_mbedtls_strerr(NULL, rc, "Parsing public key from file \"%s\" failed", pubkey_path);
nc_tls_privkey_destroy_wrap(pk);
return NULL;
}
@ -1608,7 +1671,7 @@ nc_server_tls_parse_crl_dist_points(unsigned char **p, size_t len, char ***uris,
*/
ret = mbedtls_asn1_get_tag(p, end_crl_dist_points, &len, MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE);
if (ret) {
ERR(NULL, "Failed to parse CRL distribution points extension (%s).", nc_get_mbedtls_str_err(ret));
nc_mbedtls_strerr(NULL, ret, "Failed to parse CRL distribution points extension");
goto cleanup;
}
if (!len) {
@ -1631,7 +1694,7 @@ nc_server_tls_parse_crl_dist_points(unsigned char **p, size_t len, char ***uris,
ERR(NULL, "Failed to parse CRL distribution points extension (nameRelativeToCRLIssuer not yet supported).");
goto cleanup;
} else {
ERR(NULL, "Failed to parse CRL distribution points extension (%s).", nc_get_mbedtls_str_err(ret));
nc_mbedtls_strerr(NULL, ret, "Failed to parse CRL distribution points extension");
goto cleanup;
}
}
@ -1640,7 +1703,7 @@ nc_server_tls_parse_crl_dist_points(unsigned char **p, size_t len, char ***uris,
end_general_names = *p + len;
ret = mbedtls_asn1_get_tag(p, end_general_names, &name_len, MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE);
if (ret) {
ERR(NULL, "Failed to parse GeneralNames in CRL distribution points extension (%s)", nc_get_mbedtls_str_err(ret));
nc_mbedtls_strerr(NULL, ret, "Failed to parse CRL distribution points extension");
goto cleanup;
}
@ -1665,8 +1728,7 @@ nc_server_tls_parse_crl_dist_points(unsigned char **p, size_t len, char ***uris,
*/
ret = mbedtls_asn1_get_tag(p, end_names, &name_len, MBEDTLS_ASN1_CONTEXT_SPECIFIC | (tag & MBEDTLS_ASN1_CONSTRUCTED) | tag_value);
if (ret) {
ERR(NULL, "Failed to parse GeneralName in CRL distribution points extension (%s).",
nc_get_mbedtls_str_err(ret));
nc_mbedtls_strerr(NULL, ret, "Failed to parse GeneralName in CRL distribution points extension");
goto cleanup;
}
@ -1699,7 +1761,7 @@ nc_server_tls_parse_crl_dist_points(unsigned char **p, size_t len, char ***uris,
} else if (ret != MBEDTLS_ERR_ASN1_UNEXPECTED_TAG) {
/* failed to parse it, but not because it's optional */
ERR(NULL, "Failed to parse CRL distribution points extension (%s).", nc_get_mbedtls_str_err(ret));
nc_mbedtls_strerr(NULL, ret, "Failed to parse CRL distribution points extension");
goto cleanup;
}
}
@ -1754,7 +1816,7 @@ nc_server_tls_get_crl_distpoint_uris_wrap(void *leaf_cert, void *cert_store, cha
*/
ret = mbedtls_asn1_get_tag(&p, end_v3_ext, &len, MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE);
if (ret) {
ERR(NULL, "Failed to parse CRL distribution points extension (%s).", nc_get_mbedtls_str_err(ret));
nc_mbedtls_strerr(NULL, ret, "Failed to parse CRL distribution points extension");
goto cleanup;
}
@ -1767,7 +1829,7 @@ nc_server_tls_get_crl_distpoint_uris_wrap(void *leaf_cert, void *cert_store, cha
*/
ret = mbedtls_asn1_get_tag(&p, end_v3_ext, &len, MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE);
if (ret) {
ERR(NULL, "Failed to parse CRL distribution points extension (%s).", nc_get_mbedtls_str_err(ret));
nc_mbedtls_strerr(NULL, ret, "Failed to parse CRL distribution points extension");
goto cleanup;
}
@ -1776,7 +1838,7 @@ nc_server_tls_get_crl_distpoint_uris_wrap(void *leaf_cert, void *cert_store, cha
/* parse extnID */
ret = mbedtls_asn1_get_tag(&p, end_ext, &ext_oid.len, MBEDTLS_ASN1_OID);
if (ret) {
ERR(NULL, "Failed to parse CRL distribution points extension (%s).", nc_get_mbedtls_str_err(ret));
nc_mbedtls_strerr(NULL, ret, "Failed to parse CRL distribution points extension");
goto cleanup;
}
ext_oid.tag = MBEDTLS_ASN1_OID;
@ -1793,14 +1855,14 @@ nc_server_tls_get_crl_distpoint_uris_wrap(void *leaf_cert, void *cert_store, cha
/* parse optional critical */
ret = mbedtls_asn1_get_bool(&p, end_ext, &is_critical);
if (ret && (ret != MBEDTLS_ERR_ASN1_UNEXPECTED_TAG)) {
ERR(NULL, "Failed to parse CRL distribution points extension (%s).", nc_get_mbedtls_str_err(ret));
nc_mbedtls_strerr(NULL, ret, "Failed to parse CRL distribution points extension");
goto cleanup;
}
/* parse extnValue */
ret = mbedtls_asn1_get_tag(&p, end_ext, &len, MBEDTLS_ASN1_OCTET_STRING);
if (ret) {
ERR(NULL, "Failed to parse CRL distribution points extension (%s).", nc_get_mbedtls_str_err(ret));
nc_mbedtls_strerr(NULL, ret, "Failed to parse CRL distribution points extension");
goto cleanup;
}
@ -1813,12 +1875,12 @@ nc_server_tls_get_crl_distpoint_uris_wrap(void *leaf_cert, void *cert_store, cha
*/
ret = mbedtls_asn1_get_tag(&p, end_ext_octet, &len, MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE);
if (ret) {
ERR(NULL, "Failed to parse CRL distribution points extension (%s).", nc_get_mbedtls_str_err(ret));
nc_mbedtls_strerr(NULL, ret, "Failed to parse CRL distribution points extension");
goto cleanup;
}
if (p + len != end_ext_octet) {
/* length mismatch */
ERR(NULL, "Failed to parse CRL distribution points extension (%s).", nc_get_mbedtls_str_err(ret));
ERR(NULL, "Failed to parse CRL distribution points extension (length mismatch).");
goto cleanup;
} else if (!len) {
/* empty sequence, but size is 1..max */

View file

@ -44,6 +44,20 @@
#include <openssl/x509.h>
#include <openssl/x509v3.h>
int
nc_tls_backend_init_wrap(void)
{
/* nothing to do */
return 0;
}
void
nc_tls_backend_destroy_wrap(void)
{
/* nothing to do */
return;
}
void *
nc_tls_session_new_wrap(void *tls_cfg)
{

View file

@ -30,8 +30,13 @@
#include "session_client.h"
#include "session_server.h"
#include "session_server_ch.h"
#ifdef NC_ENABLED_SSH_TLS
#include "session_wrapper.h"
#endif /* NC_ENABLED_SSH_TLS */
extern struct nc_server_opts server_opts;
/**
@ -498,19 +503,27 @@ struct nc_ch_client_thread_arg {
};
struct nc_server_opts {
/* ACCESS unlocked */
ATOMIC_T wd_basic_mode;
ATOMIC_T wd_also_supported;
uint32_t capabilities_count;
/* ACCESS locked - hello lock - separate lock to not always hold config_lock */
char **ignored_modules; /**< Names of YANG modules that are not reported in the server <hello> message. */
uint16_t ignored_mod_count;
NC_WD_MODE wd_basic_mode; /**< With-defaults basic mode of the server. */
int wd_also_supported; /**< Bitmap of with-defaults modes that are also supported by the server. */
char **capabilities;
uint32_t capabilities_count;
char *(*content_id_clb)(void *user_data);
char *(*content_id_clb)(void *user_data); /**< Callback for generating content_id for ietf-yang-library data. */
void *content_id_data;
void (*content_id_data_free)(void *data);
pthread_rwlock_t hello_lock; /**< Needs to be held while the server <hello> message is being generated. */
/* ACCESS unlocked */
uint16_t idle_timeout;
/* ACCESS locked - options modified by YANG data/API - WRITE lock
* - options read when accepting sessions - READ lock */
pthread_rwlock_t config_lock;
#ifdef NC_ENABLED_SSH_TLS
char *authkey_path_fmt; /**< Path to users' public keys that may contain tokens with special meaning. */
char *pam_config_name; /**< PAM configuration file name. */
@ -521,13 +534,12 @@ struct nc_server_opts {
int (*user_verify_clb)(const struct nc_session *session);
#endif /* NC_ENABLED_SSH_TLS */
pthread_rwlock_t config_lock;
#ifdef NC_ENABLED_SSH_TLS
struct nc_keystore keystore; /**< store for server's keys/certificates */
struct nc_truststore truststore; /**< store for server client's keys/certificates */
struct nc_keystore keystore; /**< Server's keys/certificates. */
struct nc_truststore truststore; /**< Server client's keys/certificates. */
#endif /* NC_ENABLED_SSH_TLS */
/* ACCESS locked */
struct nc_bind *binds;
pthread_mutex_t bind_lock; /**< To avoid concurrent calls of poll and accept on the bound sockets **/
struct nc_endpt {
@ -607,11 +619,12 @@ struct nc_server_opts {
} ch_dispatch_data;
#endif /* NC_ENABLED_SSH_TLS */
/* Atomic IDs */
/* ACCESS unlocked */
ATOMIC_T new_session_id;
ATOMIC_T new_client_id;
#ifdef NC_ENABLED_SSH_TLS
/* ACCESS locked */
struct {
pthread_t tid; /**< Thread ID of the certificate expiration notification thread. */
int thread_running; /**< Flag representing the runningness of the cert exp notification thread. */
@ -628,7 +641,8 @@ struct nc_server_opts {
int interval_count; /**< Number of intervals. */
} cert_exp_notif;
FILE *tls_keylog_file; /**< File to log TLS secrets to. */
/* ACCESS unlocked */
FILE *tls_keylog_file; /**< File to log TLS secrets to. */
#endif
};
@ -1238,6 +1252,14 @@ int nc_session_tls_crl_from_cert_ext_fetch(void *leaf_cert, void *cert_store, vo
#endif /* NC_ENABLED_SSH_TLS */
/**
* @brief Check whether a module is not ignored by the server.
*
* @param[in] mod_name Module name to check.
* @return Whether the module is ignored.
*/
int nc_server_is_mod_ignored(const char *mod_name);
/**
* Functions
* - io.c

View file

@ -44,14 +44,18 @@
#include "session_p.h"
#include "session_server.h"
#include "session_server_ch.h"
#include "session_wrapper.h"
#ifdef NC_ENABLED_SSH_TLS
#include "session_wrapper.h"
#include <curl/curl.h>
#include <libssh/libssh.h>
#endif
#endif /* NC_ENABLED_SSH_TLS */
struct nc_server_opts server_opts = {
.hello_lock = PTHREAD_RWLOCK_INITIALIZER,
.config_lock = PTHREAD_RWLOCK_INITIALIZER,
.ch_client_lock = PTHREAD_RWLOCK_INITIALIZER,
.idle_timeout = 180, /**< default idle timeout (not in config for UNIX socket) */
@ -818,41 +822,62 @@ nc_server_keylog_file_open(void)
#endif
/**
* @brief Initialize a rwlock.
*
* @param[in] rwlock RW lock to initialize.
* @return errno.
*/
static int
nc_server_init_rwlock(pthread_rwlock_t *rwlock)
{
#ifdef HAVE_PTHREAD_RWLOCKATTR_SETKIND_NP
int rc = 0;
pthread_rwlockattr_t attr;
if ((rc = pthread_rwlockattr_init(&attr))) {
ERR(NULL, "%s: failed to init attribute (%s).", __func__, strerror(rc));
return rc;
}
if ((rc = pthread_rwlockattr_setkind_np(&attr, PTHREAD_RWLOCK_PREFER_WRITER_NONRECURSIVE_NP))) {
ERR(NULL, "%s: failed to set attribute (%s).", __func__, strerror(rc));
goto cleanup;
}
if ((rc = pthread_rwlock_init(rwlock, &attr))) {
ERR(NULL, "%s: failed to init rwlock (%s).", __func__, strerror(rc));
goto cleanup;
}
cleanup:
pthread_rwlockattr_destroy(&attr);
return rc;
#else
int rc = 0;
if ((rc = pthread_rwlock_init(rwlock, NULL))) {
ERR(NULL, "%s: failed to init rwlock (%s).", __func__, strerror(rc));
}
return rc;
#endif
}
API int
nc_server_init(void)
{
pthread_rwlockattr_t *attr_p = NULL;
int r;
ATOMIC_STORE_RELAXED(server_opts.new_session_id, 1);
ATOMIC_STORE_RELAXED(server_opts.new_client_id, 1);
#ifdef HAVE_PTHREAD_RWLOCKATTR_SETKIND_NP
pthread_rwlockattr_t attr;
if ((r = pthread_rwlockattr_init(&attr))) {
ERR(NULL, "%s: failed init attribute (%s).", __func__, strerror(r));
if (nc_server_init_rwlock(&server_opts.config_lock)) {
goto error;
}
attr_p = &attr;
if ((r = pthread_rwlockattr_setkind_np(&attr, PTHREAD_RWLOCK_PREFER_WRITER_NONRECURSIVE_NP))) {
ERR(NULL, "%s: failed set attribute (%s).", __func__, strerror(r));
if (nc_server_init_rwlock(&server_opts.ch_client_lock)) {
goto error;
}
#endif
if ((r = pthread_rwlock_init(&server_opts.config_lock, attr_p))) {
ERR(NULL, "%s: failed to init rwlock(%s).", __func__, strerror(r));
goto error;
}
if ((r = pthread_rwlock_init(&server_opts.ch_client_lock, attr_p))) {
ERR(NULL, "%s: failed to init rwlock(%s).", __func__, strerror(r));
goto error;
}
if (attr_p) {
pthread_rwlockattr_destroy(attr_p);
}
#ifdef NC_ENABLED_SSH_TLS
if (curl_global_init(CURL_GLOBAL_SSL | CURL_GLOBAL_ACK_EINTR)) {
@ -860,6 +885,11 @@ nc_server_init(void)
goto error;
}
if (nc_tls_backend_init_wrap()) {
ERR(NULL, "%s: failed to init the SSL library backend.", __func__);
return -1;
}
/* optional for dynamic library, mandatory for static */
if (ssh_init()) {
ERR(NULL, "%s: failed to init libssh.", __func__);
@ -889,9 +919,6 @@ nc_server_init(void)
return 0;
error:
if (attr_p) {
pthread_rwlockattr_destroy(attr_p);
}
return -1;
}
@ -942,6 +969,7 @@ nc_server_destroy(void)
nc_server_config_ks_keystore(NULL, NC_OP_DELETE);
nc_server_config_ts_truststore(NULL, NC_OP_DELETE);
curl_global_cleanup();
nc_tls_backend_destroy_wrap();
ssh_finalize();
/* close the TLS keylog file */
@ -962,8 +990,15 @@ nc_server_set_capab_withdefaults(NC_WD_MODE basic_mode, int also_supported)
return -1;
}
ATOMIC_STORE_RELAXED(server_opts.wd_basic_mode, basic_mode);
ATOMIC_STORE_RELAXED(server_opts.wd_also_supported, also_supported);
/* HELLO LOCK */
pthread_rwlock_wrlock(&server_opts.hello_lock);
server_opts.wd_basic_mode = basic_mode;
server_opts.wd_also_supported = also_supported;
/* HELLO UNLOCK */
pthread_rwlock_unlock(&server_opts.hello_lock);
return 0;
}
@ -975,12 +1010,18 @@ nc_server_get_capab_withdefaults(NC_WD_MODE *basic_mode, int *also_supported)
return;
}
/* HELLO LOCK */
pthread_rwlock_wrlock(&server_opts.hello_lock);
if (basic_mode) {
*basic_mode = ATOMIC_LOAD_RELAXED(server_opts.wd_basic_mode);
*basic_mode = server_opts.wd_basic_mode;
}
if (also_supported) {
*also_supported = ATOMIC_LOAD_RELAXED(server_opts.wd_also_supported);
*also_supported = server_opts.wd_also_supported;
}
/* HELLO UNLOCK */
pthread_rwlock_unlock(&server_opts.hello_lock);
}
API int
@ -993,6 +1034,9 @@ nc_server_set_capability(const char *value)
return EXIT_FAILURE;
}
/* HELLO LOCK */
pthread_rwlock_wrlock(&server_opts.hello_lock);
mem = realloc(server_opts.capabilities, (server_opts.capabilities_count + 1) * sizeof *server_opts.capabilities);
NC_CHECK_ERRMEM_RET(!mem, EXIT_FAILURE);
server_opts.capabilities = mem;
@ -1000,6 +1044,9 @@ nc_server_set_capability(const char *value)
server_opts.capabilities[server_opts.capabilities_count] = strdup(value);
server_opts.capabilities_count++;
/* HELLO UNLOCK */
pthread_rwlock_unlock(&server_opts.hello_lock);
return EXIT_SUCCESS;
}
@ -1007,9 +1054,15 @@ API void
nc_server_set_content_id_clb(char *(*content_id_clb)(void *user_data), void *user_data,
void (*free_user_data)(void *user_data))
{
/* HELLO LOCK */
pthread_rwlock_wrlock(&server_opts.hello_lock);
server_opts.content_id_clb = content_id_clb;
server_opts.content_id_data = user_data;
server_opts.content_id_data_free = free_user_data;
/* HELLO UNLOCK */
pthread_rwlock_unlock(&server_opts.hello_lock);
}
API NC_MSG_TYPE
@ -2731,7 +2784,7 @@ nc_connect_ch_endpt(struct nc_ch_endpt *endpt, nc_server_ch_session_acquire_ctx_
const struct ly_ctx *ctx = NULL;
int sock, ret;
struct timespec ts_cur;
char *ip_host;
char *ip_host = NULL;
sock = nc_sock_connect(endpt->src_addr, endpt->src_port, endpt->dst_addr, endpt->dst_port,
NC_CH_CONNECT_TIMEOUT, &endpt->ka, &endpt->sock_pending, &ip_host);
@ -4080,3 +4133,17 @@ nc_server_notif_cert_expiration_thread_stop(int wait)
}
#endif /* NC_ENABLED_SSH_TLS */
int
nc_server_is_mod_ignored(const char *mod_name)
{
uint16_t i;
for (i = 0; i < server_opts.ignored_mod_count; ++i) {
if (!strcmp(server_opts.ignored_modules[i], mod_name)) {
return 1;
}
}
return 0;
}

View file

@ -625,7 +625,8 @@ static int
nc_server_ssh_compare_password(const char *stored_pw, const char *received_pw)
{
char *received_pw_hash = NULL;
struct crypt_data cdata = {0};
struct crypt_data *cdata;
int ret;
NC_CHECK_ARG_RET(NULL, stored_pw, received_pw, 1);
@ -645,13 +646,23 @@ nc_server_ssh_compare_password(const char *stored_pw, const char *received_pw)
return strcmp(stored_pw + 3, received_pw);
}
received_pw_hash = crypt_r(received_pw, stored_pw, &cdata);
if (!received_pw_hash) {
ERR(NULL, "Hashing the password failed (%s).", strerror(errno));
cdata = (struct crypt_data *) calloc(sizeof(struct crypt_data), 1);
if (cdata == NULL) {
ERR(NULL, "Allocation of crypt_data struct failed.");
return 1;
}
return strcmp(received_pw_hash, stored_pw);
received_pw_hash = crypt_r(received_pw, stored_pw, cdata);
if (!received_pw_hash) {
ERR(NULL, "Hashing the password failed (%s).", strerror(errno));
free(cdata);
return 1;
}
ret = strcmp(received_pw_hash, stored_pw);
free(cdata);
return ret;
}
API int
@ -1946,17 +1957,29 @@ nc_accept_ssh_session(struct nc_session *session, struct nc_server_ssh_opts *opt
rc = -1;
goto cleanup;
}
if (opts->encryption_algs && ssh_bind_options_set(sbind, SSH_BIND_OPTIONS_CIPHERS_S_C, opts->encryption_algs)) {
rc = -1;
goto cleanup;
if (opts->encryption_algs) {
if (ssh_bind_options_set(sbind, SSH_BIND_OPTIONS_CIPHERS_S_C, opts->encryption_algs)) {
rc = -1;
goto cleanup;
}
if (ssh_bind_options_set(sbind, SSH_BIND_OPTIONS_CIPHERS_C_S, opts->encryption_algs)) {
rc = -1;
goto cleanup;
}
}
if (opts->kex_algs && ssh_bind_options_set(sbind, SSH_BIND_OPTIONS_KEY_EXCHANGE, opts->kex_algs)) {
rc = -1;
goto cleanup;
}
if (opts->mac_algs && ssh_bind_options_set(sbind, SSH_BIND_OPTIONS_HMAC_S_C, opts->mac_algs)) {
rc = -1;
goto cleanup;
if (opts->mac_algs) {
if (ssh_bind_options_set(sbind, SSH_BIND_OPTIONS_HMAC_S_C, opts->mac_algs)) {
rc = -1;
goto cleanup;
}
if (ssh_bind_options_set(sbind, SSH_BIND_OPTIONS_HMAC_C_S, opts->mac_algs)) {
rc = -1;
goto cleanup;
}
}
/* configure the ssh banner */

View file

@ -331,8 +331,8 @@ static int
nc_server_tls_cert_to_name(struct nc_ctn *ctn, void *cert_chain, char **username)
{
int ret = 1, i, cert_count, fingerprint_match;
char *digest_md5 = NULL, *digest_sha1 = NULL, *digest_sha224 = NULL;
char *digest_sha256 = NULL, *digest_sha384 = NULL, *digest_sha512 = NULL;
char *digest_md5, *digest_sha1, *digest_sha224;
char *digest_sha256, *digest_sha384, *digest_sha512;
void *cert;
/* first make sure the entry is valid */
@ -372,7 +372,6 @@ nc_server_tls_cert_to_name(struct nc_ctn *ctn, void *cert_chain, char **username
fingerprint_match = 1;
}
free(digest_md5);
digest_md5 = NULL;
/* SHA-1 */
} else if (!strncmp(ctn->fingerprint, "02", 2)) {
@ -388,7 +387,6 @@ nc_server_tls_cert_to_name(struct nc_ctn *ctn, void *cert_chain, char **username
fingerprint_match = 1;
}
free(digest_sha1);
digest_sha1 = NULL;
/* SHA-224 */
} else if (!strncmp(ctn->fingerprint, "03", 2)) {
@ -404,7 +402,6 @@ nc_server_tls_cert_to_name(struct nc_ctn *ctn, void *cert_chain, char **username
fingerprint_match = 1;
}
free(digest_sha224);
digest_sha224 = NULL;
/* SHA-256 */
} else if (!strncmp(ctn->fingerprint, "04", 2)) {
@ -420,7 +417,6 @@ nc_server_tls_cert_to_name(struct nc_ctn *ctn, void *cert_chain, char **username
fingerprint_match = 1;
}
free(digest_sha256);
digest_sha256 = NULL;
/* SHA-384 */
} else if (!strncmp(ctn->fingerprint, "05", 2)) {
@ -436,7 +432,6 @@ nc_server_tls_cert_to_name(struct nc_ctn *ctn, void *cert_chain, char **username
fingerprint_match = 1;
}
free(digest_sha384);
digest_sha384 = NULL;
/* SHA-512 */
} else if (!strncmp(ctn->fingerprint, "06", 2)) {
@ -452,7 +447,6 @@ nc_server_tls_cert_to_name(struct nc_ctn *ctn, void *cert_chain, char **username
fingerprint_match = 1;
}
free(digest_sha512);
digest_sha512 = NULL;
/* unknown */
} else {
@ -658,7 +652,13 @@ nc_session_get_client_cert(const struct nc_session *session)
API void
nc_server_tls_set_verify_clb(int (*verify_clb)(const struct nc_session *session))
{
/* CONFIG LOCK */
pthread_rwlock_wrlock(&server_opts.config_lock);
server_opts.user_verify_clb = verify_clb;
/* CONFIG UNLOCK */
pthread_rwlock_unlock(&server_opts.config_lock);
}
int

View file

@ -69,6 +69,20 @@ struct nc_tls_verify_cb_data {
void *chain; /**< Certificate chain used to verify the client cert. */
};
/**
* @brief Initializes the TLS backend.
*
* Does nothing for OpenSSL, required for MbedTLS version 3.6.0 and later.
*/
int nc_tls_backend_init_wrap(void);
/**
* @brief Destroys the TLS backend.
*
* Does nothing for OpenSSL, required for MbedTLS version 3.6.0 and later.
*/
void nc_tls_backend_destroy_wrap(void);
/**
* @brief Creates a new TLS session from the given configuration.
*
@ -267,8 +281,6 @@ void nc_tls_sans_destroy_wrap(void *sans);
*/
int nc_tls_get_num_sans_wrap(void *sans);
#ifdef NC_ENABLED_SSH_TLS
/**
* @brief Get the SAN value and type in the context of CTN.
*
@ -280,8 +292,6 @@ int nc_tls_get_num_sans_wrap(void *sans);
*/
int nc_tls_get_san_value_type_wrap(void *sans, int idx, char **san_value, NC_TLS_CTN_MAPTYPE *san_type);
#endif
/**
* @brief Get the number of certificates in a certificate chain.
*