diff --git a/CMakeLists.txt b/CMakeLists.txt index ea28902..287e6bc 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -57,22 +57,22 @@ set(CMAKE_MACOSX_RPATH TRUE) # minor version changes with added functionality (new tool, functionality of the tool or library, ...) and # micro version is changed with a set of small changes or bugfixes anywhere in the project. set(LIBNETCONF2_MAJOR_VERSION 3) -set(LIBNETCONF2_MINOR_VERSION 5) -set(LIBNETCONF2_MICRO_VERSION 5) +set(LIBNETCONF2_MINOR_VERSION 7) +set(LIBNETCONF2_MICRO_VERSION 1) set(LIBNETCONF2_VERSION ${LIBNETCONF2_MAJOR_VERSION}.${LIBNETCONF2_MINOR_VERSION}.${LIBNETCONF2_MICRO_VERSION}) # Version of the library # Major version is changed with every backward non-compatible API/ABI change in the library, minor version changes # with backward compatible change and micro version is connected with any internal change of the library. set(LIBNETCONF2_MAJOR_SOVERSION 4) -set(LIBNETCONF2_MINOR_SOVERSION 4) -set(LIBNETCONF2_MICRO_SOVERSION 5) +set(LIBNETCONF2_MINOR_SOVERSION 6) +set(LIBNETCONF2_MICRO_SOVERSION 1) set(LIBNETCONF2_SOVERSION_FULL ${LIBNETCONF2_MAJOR_SOVERSION}.${LIBNETCONF2_MINOR_SOVERSION}.${LIBNETCONF2_MICRO_SOVERSION}) set(LIBNETCONF2_SOVERSION ${LIBNETCONF2_MAJOR_SOVERSION}) # Version of libyang library that this project depends on -set(LIBYANG_DEP_VERSION 2.0.0) -set(LIBYANG_DEP_SOVERSION 3.0.0) +set(LIBYANG_DEP_VERSION 3.12.0) +set(LIBYANG_DEP_SOVERSION 3.9.0) set(LIBYANG_DEP_SOVERSION_MAJOR 3) # global C flags @@ -388,7 +388,7 @@ endif() # generate API/ABI report if ("${BUILD_TYPE_UPPER}" STREQUAL "ABICHECK") - lib_abi_check(netconf2 "${headers}" ${LIBNETCONF2_SOVERSION_FULL} 15fbc59efa5e6f1f7bea19ab561d03f8852caabb) + lib_abi_check(netconf2 "${headers}" ${LIBNETCONF2_SOVERSION_FULL} 5c72504e9a6fc578ed32fcfb15f59f50d2aab234) endif() # source files to be covered by the 'format' target and a test with 'format-check' target diff --git a/examples/client.c b/examples/client.c index 8295fb7..c780bbf 100644 --- a/examples/client.c +++ b/examples/client.c @@ -47,7 +47,7 @@ help_print() " get-config [datastore] [xpath-filter]\t\t send a RPC with optional XPath filter and datastore, the default datastore is \"running\" \n\n"); } -static enum NC_DATASTORE_TYPE +static NC_DATASTORE string2datastore(const char *str) { if (!str) { @@ -68,7 +68,7 @@ string2datastore(const char *str) static int send_rpc(struct nc_session *session, NC_RPC_TYPE rpc_type, const char *param1, const char *param2) { - enum NC_DATASTORE_TYPE datastore; + NC_DATASTORE datastore; int r = 0, rc = 0; uint64_t msg_id = 0; struct lyd_node *envp = NULL, *op = NULL; diff --git a/modules/libnetconf2-netconf-server@2024-07-09.yang b/modules/libnetconf2-netconf-server@2025-01-23.yang similarity index 95% rename from modules/libnetconf2-netconf-server@2024-07-09.yang rename to modules/libnetconf2-netconf-server@2025-01-23.yang index 01acb77..e47ac30 100644 --- a/modules/libnetconf2-netconf-server@2024-07-09.yang +++ b/modules/libnetconf2-netconf-server@2025-01-23.yang @@ -31,6 +31,10 @@ module libnetconf2-netconf-server { prefix tlss; } + revision "2025-01-23" { + description "Added a list of YANG modules skipped in the server message."; + } + revision "2024-07-09" { description "Second revision."; } @@ -428,11 +432,11 @@ module libnetconf2-netconf-server { if-feature "ct:certificate-expiration-notification"; description - "Container for the certificate expiration notification intervals. - Its child nodes describe the ability to set the time intervals for the certificate - expiration notifications. These intervals are given in the form of an anchor and a period. - By default, these notifications are generated 3, 2, and 1 month; 2 weeks; 7, 6, 5, 4, 3, 2 and 1 day before a certificate expires. - Additionally, notifications are generated on the day of expiration and every day thereafter. + "Container for the certificate expiration notification intervals. Its child nodes describe the ability to set + the time intervals for the certificate expiration notifications. These intervals are given in the form of an + anchor and a period. By default, these notifications are generated 3, 2, and 1 month; 2 weeks; 7, 6, 5, 4, 3, + 2 and 1 day before a certificate expires. Additionally, notifications are generated on the day of expiration + and every day thereafter. Simplified example of YANG data that describe the default intervals: @@ -471,5 +475,12 @@ module libnetconf2-netconf-server { } } } + + leaf-list ignored-hello-module { + type string; + + description + "List of implemented sysrepo YANG modules that will not be reported the NETCONF server in its messages."; + } } } diff --git a/src/io.c b/src/io.c index bf7a5b2..e7c851d 100644 --- a/src/io.c +++ b/src/io.c @@ -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); diff --git a/src/log.c b/src/log.c index 16ada94..52dc58f 100644 --- a/src/log.c +++ b/src/log.c @@ -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 diff --git a/src/log_p.h b/src/log_p.h index 42549eb..3d79031 100644 --- a/src/log_p.h +++ b/src/log_p.h @@ -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 */ diff --git a/src/messages_p.h b/src/messages_p.h index 5bf3d3a..46cb360 100644 --- a/src/messages_p.h +++ b/src/messages_p.h @@ -1,10 +1,11 @@ /** * @file messages_p.h * @author Radek Krejci + * @author Michal Vasko * @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; }; diff --git a/src/messages_server.c b/src/messages_server.c index 8444203..220243d 100644 --- a/src/messages_server.c +++ b/src/messages_server.c @@ -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 diff --git a/src/messages_server.h b/src/messages_server.h index 63f6ef1..32d9368 100644 --- a/src/messages_server.h +++ b/src/messages_server.h @@ -210,7 +210,7 @@ const char *nc_err_get_app_tag(const struct lyd_node *err); * @brief Set the \ 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 \. + * @param[in] error_path New value of \ 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. * diff --git a/src/netconf.h b/src/netconf.h index 1e25cce..5ce5faf 100644 --- a/src/netconf.h +++ b/src/netconf.h @@ -1,10 +1,11 @@ /** * @file netconf.h * @author Radek Krejci + * @author Michal Vasko * @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 */ diff --git a/src/server_config.c b/src/server_config.c index 158d84d..9da1092 100644 --- a/src/server_config.c +++ b/src/server_config.c @@ -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)); diff --git a/src/server_config_util.c b/src/server_config_util.c index 3938c47..39cfab9 100644 --- a/src/server_config_util.c +++ b/src/server_config_util.c @@ -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, ...) { diff --git a/src/server_config_util_ssh.c b/src/server_config_util_ssh.c index 58e9678..7f8f138 100644 --- a/src/server_config_util_ssh.c +++ b/src/server_config_util_ssh.c @@ -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; } diff --git a/src/session.c b/src/session.c index 4b7cfd6..c20a926 100644 --- a/src/session.c +++ b/src/session.c @@ -37,9 +37,10 @@ #ifdef NC_ENABLED_SSH_TLS +#include "session_wrapper.h" + #include #include -#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; diff --git a/src/session_client.c b/src/session_client.c index a5d9b47..de82568 100644 --- a/src/session_client.c +++ b/src/session_client.c @@ -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 */ } diff --git a/src/session_mbedtls.c b/src/session_mbedtls.c index 560d85f..48156a1 100644 --- a/src/session_mbedtls.c +++ b/src/session_mbedtls.c @@ -52,29 +52,79 @@ #include /** - * @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 */ diff --git a/src/session_openssl.c b/src/session_openssl.c index 6093a78..d6bc347 100644 --- a/src/session_openssl.c +++ b/src/session_openssl.c @@ -44,6 +44,20 @@ #include #include +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) { diff --git a/src/session_p.h b/src/session_p.h index e86eb0f..10b16e8 100644 --- a/src/session_p.h +++ b/src/session_p.h @@ -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 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 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 diff --git a/src/session_server.c b/src/session_server.c index f9c8501..ea99485 100644 --- a/src/session_server.c +++ b/src/session_server.c @@ -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 #include -#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; +} diff --git a/src/session_server_ssh.c b/src/session_server_ssh.c index 7214ac3..d8c3ad4 100644 --- a/src/session_server_ssh.c +++ b/src/session_server_ssh.c @@ -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 */ diff --git a/src/session_server_tls.c b/src/session_server_tls.c index ead84ad..5e7fe3f 100644 --- a/src/session_server_tls.c +++ b/src/session_server_tls.c @@ -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 diff --git a/src/session_wrapper.h b/src/session_wrapper.h index 107bb67..1e55659 100644 --- a/src/session_wrapper.h +++ b/src/session_wrapper.h @@ -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. * diff --git a/tests/test_client_messages.c b/tests/test_client_messages.c index d5ae2f7..a38c1c1 100644 --- a/tests/test_client_messages.c +++ b/tests/test_client_messages.c @@ -105,7 +105,7 @@ test_nc_rpc_act_generic(void **state) /* function to check if values of getconfig rpc are set correctly */ void -check_getconfig(struct nc_rpc *rpc, enum NC_DATASTORE_TYPE source, char *filter, NC_WD_MODE wd_mode) +check_getconfig(struct nc_rpc *rpc, NC_DATASTORE source, char *filter, NC_WD_MODE wd_mode) { assert_int_equal(nc_rpc_get_type(rpc), NC_RPC_GETCONFIG); struct nc_rpc_getconfig *getconfig_rpc = (struct nc_rpc_getconfig *)rpc; diff --git a/tests/test_fd_comm.c b/tests/test_fd_comm.c index 31e3293..a453c0e 100644 --- a/tests/test_fd_comm.c +++ b/tests/test_fd_comm.c @@ -240,6 +240,7 @@ test_send_recv_error(void) NC_MSG_TYPE msgtype; struct nc_rpc *rpc; struct lyd_node *envp, *op, *node; + const struct lysc_node *schema; struct nc_pollsession *ps; /* client RPC */ @@ -266,9 +267,10 @@ test_send_recv_error(void) nc_rpc_free(rpc); assert_string_equal(LYD_NAME(lyd_child(envp)), "rpc-error"); - lyd_find_sibling_opaq_next(lyd_child(lyd_child(envp)), "error-tag", &node); + schema = lys_find_path(LYD_CTX(envp), NULL, "/ietf-netconf:rpc-error/error-tag", 0); + lyd_find_sibling_val(lyd_child(lyd_child(envp)), schema, NULL, 0, &node); assert_non_null(node); - assert_string_equal(((struct lyd_node_opaq *)node)->value, "operation-not-supported"); + assert_string_equal(lyd_get_value(node), "operation-not-supported"); lyd_free_tree(envp); assert_null(op); } @@ -508,6 +510,7 @@ test_send_recv_malformed_10(void **state) struct nc_pollsession *ps; struct nc_rpc *rpc; struct lyd_node *envp, *op, *node; + const struct lysc_node *schema; NC_MSG_TYPE msgtype; const char *msg; @@ -543,9 +546,10 @@ test_send_recv_malformed_10(void **state) nc_rpc_free(rpc); assert_string_equal(LYD_NAME(lyd_child(envp)), "rpc-error"); - lyd_find_sibling_opaq_next(lyd_child(lyd_child(envp)), "error-tag", &node); + schema = lys_find_path(LYD_CTX(envp), NULL, "/ietf-netconf:rpc-error/error-tag", 0); + lyd_find_sibling_val(lyd_child(lyd_child(envp)), schema, NULL, 0, &node); assert_non_null(node); - assert_string_equal(((struct lyd_node_opaq *)node)->value, "missing-attribute"); + assert_string_equal(lyd_get_value(node), "missing-attribute"); lyd_free_tree(envp); assert_null(op); } diff --git a/tests/test_io.c b/tests/test_io.c index 8e059c3..6767b68 100644 --- a/tests/test_io.c +++ b/tests/test_io.c @@ -53,6 +53,7 @@ setup_write(void **state) /* ietf-netconf */ fd = open(TESTS_DIR "/data/modules/ietf-netconf.yin", O_RDONLY); if (fd == -1) { + free(w->session); free(w); return -1; } diff --git a/tests/test_thread_messages.c b/tests/test_thread_messages.c index 0930cc6..8b4e3db 100644 --- a/tests/test_thread_messages.c +++ b/tests/test_thread_messages.c @@ -46,8 +46,19 @@ typedef struct arg { struct nc_server_reply * rpc_clb(struct lyd_node *rpc, struct nc_session *session) { - (void)rpc; (void)session; - return nc_server_reply_ok(); + struct lyd_node *e; + + (void)session; + + if (!strcmp(LYD_NAME(rpc), "get") || !strcmp(LYD_NAME(rpc), "delete-config")) { + return nc_server_reply_ok(); + } else if (!strcmp(LYD_NAME(rpc), "commit")) { + e = nc_err(LYD_CTX(rpc), NC_ERR_RES_DENIED, NC_ERR_TYPE_APP); + nc_err_set_path(e, "/module-a:top/name"); + return nc_server_reply_err(e); + } else { + nc_assert(0); + } } static void * @@ -92,10 +103,14 @@ server_thread(void *arg) nc_server_notif_send(sess, notif, 1000); + /* commit in test */ + poll = nc_ps_poll(ps, 1000, &sess); + nc_assert(poll == (NC_PSPOLL_RPC | NC_PSPOLL_REPLY_ERROR)); + nc_ps_clear(ps, 1, NULL); nc_ps_free(ps); - /* Waiting for end of test */ + /* waiting for end of test */ pthread_barrier_wait(&barrier); nc_server_notif_free(notif); @@ -110,7 +125,7 @@ notif_thread(void *arg) struct lyd_node *op; NC_MSG_TYPE msgtype; - /* Sync threads for receiving message to increase chance of datarace */ + /* sync threads for receiving message to increase chance of datarace */ pthread_barrier_wait(&barrier_msg); do { msgtype = nc_recv_notif(sess, 1000, &envp, &op); @@ -131,44 +146,47 @@ main(void) struct nc_rpc *rpc; uint64_t msgid; NC_MSG_TYPE msgtype; - const char *features[] = {"startup", NULL}; + const char *features[] = {"startup", "candidate", NULL}; arg_t thread_arg; pthread_t t[2]; + char *str; pthread_barrier_init(&barrier, NULL, 2); pthread_barrier_init(&barrier_msg, NULL, 2); - /* Create a two pipes */ + /* create a two pipes */ nc_assert(pipe(pipes) != -1); nc_assert(pipe(pipes + 2) != -1); thread_arg.in = pipes[0]; thread_arg.out = pipes[3]; - /* Create both contexts */ + /* create both contexts */ nc_assert(ly_ctx_new(TESTS_DIR "/data/modules", 0, &server_ctx) == LY_SUCCESS); nc_assert(ly_ctx_load_module(server_ctx, "ietf-netconf", NULL, features)); nc_assert(ly_ctx_load_module(server_ctx, "notif1", NULL, NULL)); + nc_assert(ly_ctx_load_module(server_ctx, "module-a", NULL, NULL)); thread_arg.ctx = server_ctx; nc_set_global_rpc_clb(rpc_clb); nc_assert(ly_ctx_new(TESTS_DIR "/data/modules", 0, &client_ctx) == LY_SUCCESS); nc_assert(ly_ctx_load_module(client_ctx, "ietf-netconf", NULL, features)); nc_assert(ly_ctx_load_module(client_ctx, "notif1", NULL, NULL)); + nc_assert(ly_ctx_load_module(client_ctx, "module-a", NULL, NULL)); - /* Start server thread */ + /* start server thread */ pthread_create(&t[0], NULL, server_thread, &thread_arg); nc_client_init(); - /* Listen for notifications */ + /* listen for notifications */ sess = nc_connect_inout(pipes[2], pipes[1], client_ctx); nc_assert(sess); pthread_create(&t[1], NULL, notif_thread, sess); - /* Send rpc */ + /* send delete-config rpc */ rpc = nc_rpc_delete(NC_DATASTORE_STARTUP, NULL, NC_PARAMTYPE_CONST); nc_assert(nc_send_rpc(sess, rpc, 1000, &msgid) == NC_MSG_RPC); - /* Sync threads for receiving message to increase chance of datarace */ + /* sync threads for receiving message to increase chance of datarace */ pthread_barrier_wait(&barrier_msg); do { msgtype = nc_recv_reply(sess, rpc, msgid, 1000, &envp, &op); @@ -177,12 +195,33 @@ main(void) nc_rpc_free(rpc); lyd_free_tree(envp); - /* Waiting of end of test */ + /* send commit rpc */ + rpc = nc_rpc_commit(0, 0, NULL, NULL, NC_PARAMTYPE_CONST); + nc_assert(nc_send_rpc(sess, rpc, 1000, &msgid) == NC_MSG_RPC); + do { + msgtype = nc_recv_reply(sess, rpc, msgid, 1000, &envp, &op); + } while (msgtype == NC_MSG_NOTIF); + nc_assert(msgtype == NC_MSG_REPLY); + nc_rpc_free(rpc); + + lyd_print_mem(&str, envp, LYD_XML, LYD_PRINT_SHRINK); + nc_assert(!strcmp(str, + "" + "application" + "resource-denied" + "error" + "/a:top/a:name" + "Request could not be completed because of insufficient resources." + "")); + free(str); + lyd_free_tree(envp); + + /* waiting of end of test */ pthread_barrier_wait(&barrier); pthread_join(t[0], NULL); pthread_join(t[1], NULL); - /* Cleanup */ + /* cleanup */ nc_session_free(sess, NULL); ly_ctx_destroy(server_ctx); ly_ctx_destroy(client_ctx);