From 49491589a511d5b1915867d92283f0b3fc37e983 Mon Sep 17 00:00:00 2001 From: roman Date: Thu, 23 Nov 2023 16:01:04 +0100 Subject: [PATCH 01/12] server config UPDATE keyboard interactive These changes include: - PAM filename is set globally for all clients - PAM directory not configurable (security reasons) - Removal of the ln2 PAM module and its use in a test - Description added to the ln2 YANG module - Change of add_user_interactive API to only enable kbint for a user --- CMakeLists.txt | 10 +- CMakeModules/FindLibPAM.cmake | 9 - ...libnetconf2-netconf-server@2023-09-07.yang | 32 +- src/config.h.in | 5 - src/server_config.c | 64 +--- src/server_config.h | 25 +- src/server_config_util_ssh.c | 43 +-- src/session_p.h | 12 +- src/session_server.c | 2 + src/session_server.h | 13 + src/session_server_ssh.c | 94 +++--- tests/CMakeLists.txt | 23 +- tests/pam/pam_netconf.c | 311 ------------------ tests/test_auth.c | 75 +---- 14 files changed, 131 insertions(+), 587 deletions(-) delete mode 100644 tests/pam/pam_netconf.c diff --git a/CMakeLists.txt b/CMakeLists.txt index 5cb8b0e6..53c4724f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -160,8 +160,7 @@ set(format_sources examples/*.h* src/*.c src/*.h - tests/*.c - tests/pam/*.c) + tests/*.c) # # checks @@ -255,13 +254,6 @@ if(ENABLE_SSH_TLS) if(LibPAM_FOUND) set(HAVE_LIBPAM TRUE) - # enable PAM test if a PAM header file contains a declaration of a function pam_start_confdir - if (LIBPAM_HAVE_CONFDIR) - message(STATUS "LibPAM found, version >= 1.4, enabled PAM tests") - else() - message(STATUS "LibPAM found, version < 1.4, disabled PAM tests") - endif() - target_link_libraries(netconf2 ${LIBPAM_LIBRARIES}) list(APPEND CMAKE_REQUIRED_LIBRARIES ${LIBPAM_LIBRARIES}) include_directories(${LIBPAM_INCLUDE_DIRS}) diff --git a/CMakeModules/FindLibPAM.cmake b/CMakeModules/FindLibPAM.cmake index 252b116f..ff78fbe0 100644 --- a/CMakeModules/FindLibPAM.cmake +++ b/CMakeModules/FindLibPAM.cmake @@ -4,7 +4,6 @@ # LIBPAM_FOUND - system has LibPAM # LIBPAM_INCLUDE_DIRS - the LibPAM include directory # LIBPAM_LIBRARIES - link these to use LibPAM -# LIBPAM_HAVE_CONFDIR - LibPAM version >= 1.4 # # Author Roman Janota # Copyright (c) 2022 CESNET, z.s.p.o. @@ -63,14 +62,6 @@ else() if(LIBPAM_INCLUDE_DIR AND LIBPAM_LIBRARY) set(LIBPAM_FOUND TRUE) - # there is no reliable way to check the version of PAM, so see if the declaration of a function pam_start_confdir - # is in the header file (added in PAM 1.4) - file(STRINGS ${LIBPAM_INCLUDE_DIR}/security/pam_appl.h PAM_CONFDIR REGEX "pam_start_confdir") - if ("${PAM_CONFDIR}" STREQUAL "") - set(LIBPAM_HAVE_CONFDIR FALSE) - else() - set(LIBPAM_HAVE_CONFDIR TRUE) - endif() else() set(LIBPAM_FOUND FALSE) endif() diff --git a/modules/libnetconf2-netconf-server@2023-09-07.yang b/modules/libnetconf2-netconf-server@2023-09-07.yang index 0ef941f0..cd64d1a5 100644 --- a/modules/libnetconf2-netconf-server@2023-09-07.yang +++ b/modules/libnetconf2-netconf-server@2023-09-07.yang @@ -265,35 +265,33 @@ module libnetconf2-netconf-server { "Grouping for the SSH Keyboard interactive authentication method."; container keyboard-interactive { - presence "Indicates that PAM configuration file name has been configured. - This statement is present so the mandatory descendant - nodes do not imply that this node must be - configured."; + presence "Indicates that PAM configuration file name has been configured and that + the given client supportsthe SSH Keyboard Interactive authentication method."; description "Keyboard interactive SSH authentication method."; - leaf pam-config-file-name { - type string; - mandatory true; - } - leaf pam-config-file-dir { - type string; - } + + reference + "RFC 4256: + Generic Message Exchange Authentication for + the Secure Shell Protocol (SSH)"; } } grouping endpoint-reference-grouping { description - "Reference to another endpoint. The purpose is to use the referenced endpoint's authentication mechanisms. - If a connection occurs on an endpoint, the connecting user will be tried to be authenticated - using the given endpoint's defined methods. If the user wasn't authenticated and the endpoint - references another endpoint, the authentication will be tried again. However, this time - using the referenced endpoint's mechanisms. The references can be - multiple, however there must not be a cycle."; + "Grouping for the endpoint reference."; leaf endpoint-reference { type leafref { path "/ncs:netconf-server/ncs:listen/ncs:endpoint/ncs:name"; } + description + "Reference to another endpoint. The purpose is to use the referenced endpoint's authentication mechanisms. + If a connection occurs on an endpoint, the connecting user will be tried to be authenticated + using the given endpoint's defined methods. If the user wasn't authenticated and the endpoint + references another endpoint, the authentication will be tried again. However, this time + using the referenced endpoint's mechanisms. The references can be + multiple, however there must not be a cycle."; } } diff --git a/src/config.h.in b/src/config.h.in index 6dc9fa16..7cf3323c 100644 --- a/src/config.h.in +++ b/src/config.h.in @@ -44,11 +44,6 @@ */ #cmakedefine HAVE_LIBPAM -/* -* Support for older PAM versions -*/ -#cmakedefine LIBPAM_HAVE_CONFDIR - /* * Location of installed YANG modules on the system */ diff --git a/src/server_config.c b/src/server_config.c index 6ab79c92..1f1c8143 100644 --- a/src/server_config.c +++ b/src/server_config.c @@ -735,8 +735,6 @@ nc_server_config_del_auth_client(struct nc_server_ssh_opts *opts, struct nc_auth } free(auth_client->password); - free(auth_client->pam_config_name); - free(auth_client->pam_config_dir); opts->client_count--; if (!opts->client_count) { @@ -2502,50 +2500,13 @@ nc_server_config_password(const struct lyd_node *node, NC_OPERATION op) } static int -nc_server_config_pam_name(const struct lyd_node *node, NC_OPERATION op) +nc_server_config_kb_int(const struct lyd_node *node, NC_OPERATION op) { int ret = 0; struct nc_auth_client *auth_client; - struct nc_ch_client *ch_client; - - assert(!strcmp(LYD_NAME(node), "pam-config-file-name")); - - /* LOCK */ - if (is_ch(node) && nc_server_config_get_ch_client_with_lock(node, &ch_client)) { - /* to avoid unlock on fail */ - return 1; - } - - if (nc_server_config_get_auth_client(node, &auth_client)) { - ret = 1; - goto cleanup; - } - - if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) { - free(auth_client->pam_config_name); - auth_client->pam_config_name = strdup(lyd_get_value(node)); - NC_CHECK_ERRMEM_GOTO(!auth_client->pam_config_name, ret = 1, cleanup); - } else { - free(auth_client->pam_config_name); - auth_client->pam_config_name = NULL; - } - -cleanup: - if (is_ch(node)) { - /* UNLOCK */ - nc_ch_client_unlock(ch_client); - } - return ret; -} - -static int -nc_server_config_pam_dir(const struct lyd_node *node, NC_OPERATION op) -{ - int ret = 0; - struct nc_auth_client *auth_client; - struct nc_ch_client *ch_client; + struct nc_ch_client *ch_client = NULL; - assert(!strcmp(LYD_NAME(node), "pam-config-file-dir")); + assert(!strcmp(LYD_NAME(node), "keyboard-interactive")); /* LOCK */ if (is_ch(node) && nc_server_config_get_ch_client_with_lock(node, &ch_client)) { @@ -2558,13 +2519,10 @@ nc_server_config_pam_dir(const struct lyd_node *node, NC_OPERATION op) goto cleanup; } - if ((op == NC_OP_CREATE) || (op == NC_OP_REPLACE)) { - free(auth_client->pam_config_dir); - auth_client->pam_config_dir = strdup(lyd_get_value(node)); - NC_CHECK_ERRMEM_GOTO(!auth_client->pam_config_dir, ret = 1, cleanup); + if (op == NC_OP_CREATE) { + auth_client->kb_int_enabled = 1; } else { - free(auth_client->pam_config_dir); - auth_client->pam_config_dir = NULL; + auth_client->kb_int_enabled = 0; } cleanup: @@ -2597,9 +2555,9 @@ nc_server_config_none(const struct lyd_node *node, NC_OPERATION op) } if (op == NC_OP_CREATE) { - auth_client->supports_none = 1; + auth_client->none_enabled = 1; } else { - auth_client->supports_none = 0; + auth_client->none_enabled = 0; } cleanup: @@ -4204,10 +4162,8 @@ nc_server_config_parse_netconf_server(const struct lyd_node *node, NC_OPERATION ret = nc_server_config_truststore_reference(node, op); } else if (!strcmp(name, "password")) { ret = nc_server_config_password(node, op); - } else if (!strcmp(name, "pam-config-file-name")) { - ret = nc_server_config_pam_name(node, op); - } else if (!strcmp(name, "pam-config-file-dir")) { - ret = nc_server_config_pam_dir(node, op); + } else if (!strcmp(name, "keyboard-interactive")) { + ret = nc_server_config_kb_int(node, op); } else if (!strcmp(name, "none")) { ret = nc_server_config_none(node, op); } else if (!strcmp(name, "host-key-alg")) { diff --git a/src/server_config.h b/src/server_config.h index f0bc71cd..f8fb973f 100644 --- a/src/server_config.h +++ b/src/server_config.h @@ -413,8 +413,23 @@ int nc_server_config_del_ssh_user_password(const char *endpt_name, const char *u * Otherwise the new YANG data will be added to the previous data and may override it. * @return 0 on success, non-zero otherwise. */ + +/** + * @brief Creates new YANG configuration data nodes for an SSH user's keyboard interactive authentication method. + * + * To set the PAM configuration filename, see ::nc_server_ssh_set_pam_conf_filename(). + * + * @param[in] ctx libyang context. + * @param[in] endpt_name Arbitrary identifier of the endpoint. + * If an endpoint with this identifier already exists, its user might be changed. + * @param[in] user_name Arbitrary identifier of the user. + * If an user with this identifier already exists, its contents will be changed. + * @param[in,out] config Configuration YANG data tree. If *config is NULL, it will be created. + * Otherwise the new YANG data will be added to the previous data and may override it. + * @return 0 on success, non-zero otherwise. + */ int nc_server_config_add_ssh_user_interactive(const struct ly_ctx *ctx, const char *endpt_name, - const char *user_name, const char *pam_config_name, const char *pam_config_dir, struct lyd_node **config); + const char *user_name, struct lyd_node **config); /** * @brief Deletes an SSH user's keyboard interactive authentication from the YANG data. @@ -1045,6 +1060,8 @@ int nc_server_config_del_ch_ssh_user_password(const char *client_name, const cha /** * @brief Creates new YANG configuration data nodes for a Call Home SSH user's keyboard interactive authentication method. * + * To set the PAM configuration filename, see ::nc_server_ssh_set_pam_conf_filename(). + * * @param[in] ctx libyang context. * @param[in] client_name Arbitrary identifier of the Call Home client. * If a Call Home client with this identifier already exists, its contents will be changed. @@ -1052,16 +1069,12 @@ int nc_server_config_del_ch_ssh_user_password(const char *client_name, const cha * If the client's endpoint with this identifier already exists, its contents will be changed. * @param[in] user_name Arbitrary identifier of the endpoint's user. * If the endpoint's user with this identifier already exists, its contents will be changed. - * @param[in] pam_config_name Name of the PAM configuration file. - * @param[in] pam_config_dir Optional. The absolute path to the directory in which the configuration file - * with the name pam_config_name is located. A newer version (>= 1.4) of PAM library is required to be able to specify - * the path. If NULL is passed, then the PAM's system directories will be searched (usually /etc/pam.d/). * @param[in,out] config Configuration YANG data tree. If *config is NULL, it will be created. * Otherwise the new YANG data will be added to the previous data and may override it. * @return 0 on success, non-zero otherwise. */ int nc_server_config_add_ch_ssh_user_interactive(const struct ly_ctx *ctx, const char *client_name, const char *endpt_name, - const char *user_name, const char *pam_config_name, const char *pam_config_dir, struct lyd_node **config); + const char *user_name, struct lyd_node **config); /** * @brief Deletes a Call Home SSH user's keyboard interactive authentication from the YANG data. diff --git a/src/server_config_util_ssh.c b/src/server_config_util_ssh.c index 8b162ee6..166a9012 100644 --- a/src/server_config_util_ssh.c +++ b/src/server_config_util_ssh.c @@ -474,45 +474,21 @@ nc_server_config_del_ch_ssh_user_password(const char *client_name, const char *e "users/user[name='%s']/password", client_name, endpt_name, user_name); } -static int -_nc_server_config_add_ssh_user_interactive(const struct ly_ctx *ctx, const char *tree_path, - const char *pam_config_name, const char *pam_config_dir, struct lyd_node **config) -{ - int ret = 0; - - ret = nc_server_config_append(ctx, tree_path, "pam-config-file-name", pam_config_name, config); - if (ret) { - goto cleanup; - } - - if (pam_config_dir) { - ret = nc_server_config_append(ctx, tree_path, "pam-config-file-dir", pam_config_dir, config); - if (ret) { - goto cleanup; - } - } - -cleanup: - return ret; -} - API int nc_server_config_add_ssh_user_interactive(const struct ly_ctx *ctx, const char *endpt_name, - const char *user_name, const char *pam_config_name, const char *pam_config_dir, struct lyd_node **config) + const char *user_name, struct lyd_node **config) { int ret = 0; char *path = NULL; - NC_CHECK_ARG_RET(NULL, ctx, endpt_name, user_name, pam_config_name, config, 1); + NC_CHECK_ARG_RET(NULL, ctx, endpt_name, user_name, config, 1); ret = asprintf(&path, "/ietf-netconf-server:netconf-server/listen/endpoint[name='%s']/ssh/ssh-server-parameters/" - "client-authentication/users/user[name='%s']/" - "libnetconf2-netconf-server:keyboard-interactive", endpt_name, user_name); + "client-authentication/users/user[name='%s']", endpt_name, user_name); NC_CHECK_ERRMEM_GOTO(ret == -1, path = NULL; ret = 1, cleanup); - ret = _nc_server_config_add_ssh_user_interactive(ctx, path, pam_config_name, pam_config_dir, config); + ret = nc_server_config_append(ctx, path, "libnetconf2-netconf-server:keyboard-interactive", NULL, config); if (ret) { - ERR(NULL, "Creating new SSH user's keyboard interactive nodes failed."); goto cleanup; } @@ -523,21 +499,20 @@ nc_server_config_add_ssh_user_interactive(const struct ly_ctx *ctx, const char * API int nc_server_config_add_ch_ssh_user_interactive(const struct ly_ctx *ctx, const char *client_name, const char *endpt_name, - const char *user_name, const char *pam_config_name, const char *pam_config_dir, struct lyd_node **config) + const char *user_name, struct lyd_node **config) { int ret = 0; char *path = NULL; - NC_CHECK_ARG_RET(NULL, ctx, client_name, endpt_name, user_name, pam_config_name, config, 1); + NC_CHECK_ARG_RET(NULL, ctx, client_name, endpt_name, user_name, config, 1); ret = asprintf(&path, "/ietf-netconf-server:netconf-server/call-home/netconf-client[name='%s']/endpoints/" - "endpoint[name='%s']/ssh/ssh-server-parameters/client-authentication/users/user[name='%s']/" - "libnetconf2-netconf-server:keyboard-interactive", client_name, endpt_name, user_name); + "endpoint[name='%s']/ssh/ssh-server-parameters/client-authentication/users/user[name='%s']", + client_name, endpt_name, user_name); NC_CHECK_ERRMEM_GOTO(ret == -1, path = NULL; ret = 1, cleanup); - ret = _nc_server_config_add_ssh_user_interactive(ctx, path, pam_config_name, pam_config_dir, config); + ret = nc_server_config_append(ctx, path, "libnetconf2-netconf-server:keyboard-interactive", NULL, config); if (ret) { - ERR(NULL, "Creating new CH SSH user's keyboard interactive nodes failed."); goto cleanup; } diff --git a/src/session_p.h b/src/session_p.h index 11f8f5d0..fc5e713b 100644 --- a/src/session_p.h +++ b/src/session_p.h @@ -174,9 +174,8 @@ struct nc_auth_client { }; char *password; /**< Client's password */ - char *pam_config_name; /**< Client's PAM configuration file name. */ - char *pam_config_dir; /**< Client's PAM configuration file directory. */ - int supports_none; /**< Implies that the client supports the none authentication method. */ + int kb_int_enabled; /**< Indicates that the client supports keyboard-interactive authentication. */ + int none_enabled; /**< Implies that the client supports the none authentication method. */ }; /** @@ -429,6 +428,7 @@ struct nc_server_opts { uint16_t idle_timeout; #ifdef NC_ENABLED_SSH_TLS + char *pam_config_name; /**< PAM configuration file name. */ int (*interactive_auth_clb)(const struct nc_session *session, ssh_session ssh_sess, ssh_message msg, void *user_data); void *interactive_auth_data; void (*interactive_auth_data_free)(void *data); @@ -732,9 +732,9 @@ struct nc_ntf_thread_arg { * @brief PAM callback arguments. */ struct nc_pam_thread_arg { - ssh_message msg; /**< libssh message */ - struct nc_session *session; /**< NETCONF session */ - struct nc_server_ssh_opts *opts; /**< SSH server opts */ + ssh_message msg; /**< libssh message */ + struct nc_session *session; /**< NETCONF session */ + uint16_t auth_timeout; /**< Authentication timeout. */ }; /** diff --git a/src/session_server.c b/src/session_server.c index aed8820e..dd2c4e3c 100644 --- a/src/session_server.c +++ b/src/session_server.c @@ -850,6 +850,8 @@ nc_server_destroy(void) pthread_mutex_destroy(&server_opts.bind_lock); #ifdef NC_ENABLED_SSH_TLS + free(server_opts.pam_config_name); + server_opts.pam_config_name = NULL; if (server_opts.interactive_auth_data && server_opts.interactive_auth_data_free) { server_opts.interactive_auth_data_free(server_opts.interactive_auth_data); } diff --git a/src/session_server.h b/src/session_server.h index b7bab618..3083562d 100644 --- a/src/session_server.h +++ b/src/session_server.h @@ -457,6 +457,19 @@ NC_MSG_TYPE nc_ps_accept_ssh_channel(struct nc_pollsession *ps, struct nc_sessio void nc_server_ssh_set_interactive_auth_clb(int (*interactive_auth_clb)(const struct nc_session *session, ssh_session ssh_sess, ssh_message msg, void *user_data), void *user_data, void (*free_user_data)(void *user_data)); +/** + * @brief Set the name of the PAM configuration file. + * + * This filename will be set globally for all clients wishing to authenticate via the + * SSH Keyboard Interactive authentication method. + * + * @param[in] filename Name of the PAM configuration file. The file needs to be located in + * the default PAM directory (usually /etc/pam.d/). + * + * @return 0 on success, 1 on error. + */ +int nc_server_ssh_set_pam_conf_filename(const char *filename); + /** @} Server SSH */ /** diff --git a/src/session_server_ssh.c b/src/session_server_ssh.c index 3ea0b81a..39b3a82a 100644 --- a/src/session_server_ssh.c +++ b/src/session_server_ssh.c @@ -240,9 +240,7 @@ nc_sshcb_auth_password(struct nc_session *session, struct nc_auth_client *auth_c * @param[in] msg PAM module's messages. * @param[out] resp User responses. * @param[in] appdata_ptr Callback's data. - * @return PAM_SUCCESS on success; - * @return PAM_BUF_ERR on memory allocation error; - * @return PAM_CONV_ERR otherwise. + * @return PAM_SUCCESS on success, PAM_BUF_ERR on memory allocation error, PAM_CONV_ERR otherwise. */ static int nc_pam_conv_clb(int n_messages, const struct pam_message **msg, struct pam_response **resp, void *appdata_ptr) @@ -256,10 +254,10 @@ nc_pam_conv_clb(int n_messages, const struct pam_message **msg, struct pam_respo struct nc_pam_thread_arg *clb_data = appdata_ptr; ssh_session libssh_session; struct timespec ts_timeout; - struct nc_server_ssh_opts *opts; + uint16_t auth_timeout; libssh_session = clb_data->session->ti.libssh.session; - opts = clb_data->opts; + auth_timeout = clb_data->auth_timeout; /* PAM_MAX_NUM_MSG == 32 by default */ if ((n_messages <= 0) || (n_messages >= PAM_MAX_NUM_MSG)) { @@ -331,8 +329,8 @@ nc_pam_conv_clb(int n_messages, const struct pam_message **msg, struct pam_respo goto cleanup; } - if (opts->auth_timeout) { - nc_timeouttime_get(&ts_timeout, opts->auth_timeout * 1000); + if (auth_timeout) { + nc_timeouttime_get(&ts_timeout, auth_timeout * 1000); } /* get user's replies */ @@ -349,7 +347,7 @@ nc_pam_conv_clb(int n_messages, const struct pam_message **msg, struct pam_respo } usleep(NC_TIMEOUT_STEP); - } while (opts->auth_timeout && (nc_timeouttime_cur_diff(&ts_timeout) >= 1)); + } while (auth_timeout && (nc_timeouttime_cur_diff(&ts_timeout) >= 1)); if (!reply) { ERR(NULL, "Authentication timeout."); @@ -397,50 +395,30 @@ nc_pam_conv_clb(int n_messages, const struct pam_message **msg, struct pam_respo * @return PAM error otherwise. */ static int -nc_pam_auth(struct nc_session *session, struct nc_server_ssh_opts *opts, ssh_message ssh_msg) +nc_pam_auth(struct nc_session *session, struct nc_auth_client *client, uint16_t auth_timeout, ssh_message ssh_msg) { pam_handle_t *pam_h = NULL; int ret; struct nc_pam_thread_arg clb_data; struct pam_conv conv; - uint16_t i; /* structure holding callback's data */ clb_data.msg = ssh_msg; clb_data.session = session; - clb_data.opts = opts; + clb_data.auth_timeout = auth_timeout; /* PAM conversation structure holding the callback and it's data */ conv.conv = nc_pam_conv_clb; conv.appdata_ptr = &clb_data; - /* get the current client's configuration file */ - for (i = 0; i < opts->client_count; i++) { - if (!strcmp(opts->auth_clients[i].username, session->username)) { - break; - } - } - - if (i == opts->client_count) { - ERR(NULL, "User \"%s\" not found.", session->username); - ret = 1; - goto cleanup; - } - - if (!opts->auth_clients[i].pam_config_name) { - ERR(NULL, "User's \"%s\" PAM configuration filename not set."); + if (!server_opts.pam_config_name) { + ERR(NULL, "PAM configuration filename not set."); ret = 1; goto cleanup; } /* initialize PAM and see if the given configuration file exists */ -# ifdef LIBPAM_HAVE_CONFDIR - /* PAM version >= 1.4 */ - ret = pam_start_confdir(opts->auth_clients[i].pam_config_name, session->username, &conv, opts->auth_clients[i].pam_config_dir, &pam_h); -# else - /* PAM version < 1.4 */ - ret = pam_start(opts->auth_clients[i].pam_config_name, session->username, &conv, &pam_h); -# endif + ret = pam_start(server_opts.pam_config_name, client->username, &conv, &pam_h); if (ret != PAM_SUCCESS) { ERR(NULL, "PAM error occurred (%s).\n", pam_strerror(pam_h, ret)); goto cleanup; @@ -470,7 +448,7 @@ nc_pam_auth(struct nc_session *session, struct nc_server_ssh_opts *opts, ssh_mes VRB(NULL, "PAM warning occurred (%s).\n", pam_strerror(pam_h, ret)); ret = pam_chauthtok(pam_h, PAM_CHANGE_EXPIRED_AUTHTOK); if (ret == PAM_SUCCESS) { - VRB(NULL, "The authentication token of user \"%s\" updated successfully.", session->username); + VRB(NULL, "The authentication token of user \"%s\" updated successfully.", client->username); } else { ERR(NULL, "PAM error occurred (%s).\n", pam_strerror(pam_h, ret)); goto cleanup; @@ -485,10 +463,10 @@ nc_pam_auth(struct nc_session *session, struct nc_server_ssh_opts *opts, ssh_mes return ret; } -#endif +#endif /* HAVE_LIBPAM */ static int -nc_sshcb_auth_kbdint(struct nc_session *session, struct nc_server_ssh_opts *opts, ssh_message msg) +nc_sshcb_auth_kbdint(struct nc_session *session, struct nc_auth_client *client, uint16_t auth_timeout, ssh_message msg) { int auth_ret = 1; @@ -496,7 +474,7 @@ nc_sshcb_auth_kbdint(struct nc_session *session, struct nc_server_ssh_opts *opts auth_ret = server_opts.interactive_auth_clb(session, session->ti.libssh.session, msg, server_opts.interactive_auth_data); } else { #ifdef HAVE_LIBPAM - if (nc_pam_auth(session, opts, msg) == PAM_SUCCESS) { + if (nc_pam_auth(session, client, auth_timeout, msg) == PAM_SUCCESS) { auth_ret = 0; } #else @@ -515,6 +493,40 @@ nc_sshcb_auth_kbdint(struct nc_session *session, struct nc_server_ssh_opts *opts return auth_ret; } +API void +nc_server_ssh_set_interactive_auth_clb(int (*interactive_auth_clb)(const struct nc_session *session, ssh_session ssh_sess, ssh_message msg, void *user_data), + void *user_data, void (*free_user_data)(void *user_data)) +{ + server_opts.interactive_auth_clb = interactive_auth_clb; + server_opts.interactive_auth_data = user_data; + server_opts.interactive_auth_data_free = free_user_data; +} + +#ifdef HAVE_LIBPAM + +API int +nc_server_ssh_set_pam_conf_filename(const char *filename) +{ + NC_CHECK_ARG_RET(NULL, filename, 1); + + free(server_opts.pam_config_name); + server_opts.pam_config_name = strdup(filename); + NC_CHECK_ERRMEM_RET(!server_opts.pam_config_name, 1); + return 0; +} + +#else + +API int +nc_server_ssh_set_pam_conf_filename(const char *filename) +{ + (void) filename; + ERR("LibPAM not found."); + return 1; +} + +#endif /* HAVE_LIBPAM */ + /* * Get the public key type from binary data stored in buffer. * The data is in the form of: 4 bytes = data length, then data of data length @@ -654,7 +666,7 @@ auth_pubkey_compare_key(ssh_key key, struct nc_auth_client *auth_client) static void nc_sshcb_auth_none(struct nc_session *session, struct nc_auth_client *auth_client, ssh_message msg) { - if (auth_client->supports_none && !auth_client->password && !auth_client->pubkey_count && !auth_client->pam_config_name) { + if (auth_client->none_enabled && !auth_client->password && !auth_client->pubkey_count && !auth_client->kb_int_enabled) { /* only authenticate the client if he supports none and no other method */ session->flags |= NC_SESSION_SSH_AUTHENTICATED; VRB(session, "User \"%s\" authenticated.", session->username); @@ -963,11 +975,11 @@ nc_session_ssh_msg(struct nc_session *session, struct nc_server_ssh_opts *opts, state->auth_method_count++; libssh_auth_methods |= SSH_AUTH_METHOD_PASSWORD; } - if (auth_client->pam_config_name) { + if (auth_client->kb_int_enabled) { state->auth_method_count++; libssh_auth_methods |= SSH_AUTH_METHOD_INTERACTIVE; } - if (auth_client->supports_none) { + if (auth_client->none_enabled) { libssh_auth_methods |= SSH_AUTH_METHOD_NONE; } @@ -995,7 +1007,7 @@ nc_session_ssh_msg(struct nc_session *session, struct nc_server_ssh_opts *opts, } else if (subtype == SSH_AUTH_METHOD_PUBLICKEY) { ret = nc_sshcb_auth_pubkey(session, auth_client, msg); } else if (subtype == SSH_AUTH_METHOD_INTERACTIVE) { - ret = nc_sshcb_auth_kbdint(session, opts, msg); + ret = nc_sshcb_auth_kbdint(session, auth_client, opts->auth_timeout, msg); } if (!ret) { diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 01c7db53..0f9b4d17 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -11,14 +11,9 @@ endif() set(tests test_unix_socket test_client_thread test_fd_comm test_init_destroy_client test_init_destroy_server test_io test_thread_messages test_client_messages) -# only enable PAM tests if the version of PAM is greater than 1.4 -if(LIBPAM_HAVE_CONFDIR) - list(APPEND tests test_auth) -endif() - #append tests depending on SSH/TLS if(ENABLE_SSH_TLS) - list(APPEND tests test_two_channels test_ks_ts test_ec + list(APPEND tests test_auth test_two_channels test_ks_ts test_ec test_ed25519 test_replace test_endpt_share_clients test_tls test_crl test_ch test_runtime_changes test_client_ssh test_client_tls) endif() @@ -63,19 +58,3 @@ endif() include_directories(${CMAKE_SOURCE_DIR}/src ${PROJECT_BINARY_DIR}) configure_file("${PROJECT_SOURCE_DIR}/tests/config.h.in" "${PROJECT_BINARY_DIR}/tests/config.h" ESCAPE_QUOTES @ONLY) - -if(LIBPAM_HAVE_CONFDIR) - #compile PAM test module - add_library(pam_netconf SHARED ${CMAKE_SOURCE_DIR}/tests/pam/pam_netconf.c) - set_target_properties(pam_netconf PROPERTIES PREFIX "") - target_link_libraries(pam_netconf ${LIBPAM_LIBRARIES}) - - #generate PAM configuration file - file(WRITE ${CMAKE_CURRENT_BINARY_DIR}/netconf.conf - "#%PAM-1.4\n" - "auth required ${CMAKE_CURRENT_BINARY_DIR}/pam_netconf.so\n" - "account required ${CMAKE_CURRENT_BINARY_DIR}/pam_netconf.so\n" - "password required ${CMAKE_CURRENT_BINARY_DIR}/pam_netconf.so\n" - ) - -endif() diff --git a/tests/pam/pam_netconf.c b/tests/pam/pam_netconf.c deleted file mode 100644 index 835f4832..00000000 --- a/tests/pam/pam_netconf.c +++ /dev/null @@ -1,311 +0,0 @@ -/** - * @file pam_netconf.c - * @author Roman Janota - * @brief libnetconf2 Linux PAM test module - * - * @copyright - * Copyright (c) 2022 CESNET, z.s.p.o. - * - * This source code is licensed under BSD 3-Clause License (the "License"). - * You may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://opensource.org/licenses/BSD-3-Clause - */ - -#include -#include -#include -#include - -#include "config.h" - -#define N_MESSAGES 2 -#define N_REQUESTS 2 - -/** - * @brief Exchange module's messages for user's replies. - * - * @param[in] pam_h PAM handle. - * @param[in] n_messages Number of messages. - * @param[in] msg Module's messages for the user. - * @param[out] resp User's responses. - * @return PAM_SUCCESS on success; - * @return PAM error otherwise. - */ -static int -nc_pam_mod_call_clb(pam_handle_t *pam_h, int n_messages, const struct pam_message **msg, struct pam_response **resp) -{ - struct pam_conv *conv; - int r; - - /* the callback can be accessed through the handle */ - r = pam_get_item(pam_h, PAM_CONV, (void *) &conv); - if (r != PAM_SUCCESS) { - return r; - } - return conv->conv(n_messages, msg, resp, conv->appdata_ptr); -} - -/** - * @brief Validate the user's responses. - * - * @param[in] username Username. - * @param[in] reversed_username User's response to the first challenge. - * @param[in] eq_ans User's response to the second challenge. - * @return PAM_SUCCESS on success; - * @return PAM_AUTH_ERR whenever the user's replies are incorrect. - */ -static int -nc_pam_mod_auth(const char *username, char *reversed_username, char *eq_ans) -{ - int i, j, r; - size_t len; - char *buffer; - - len = strlen(reversed_username); - buffer = calloc(len + 1, sizeof *buffer); - if (!buffer) { - fprintf(stderr, "Memory allocation error.\n"); - return PAM_BUF_ERR; - } - - /* reverse the user's response */ - for (i = len - 1, j = 0; i >= 0; i--) { - buffer[j++] = reversed_username[i]; - } - buffer[j] = '\0'; - - if (!strcmp(username, buffer) && !strcmp(eq_ans, "2")) { - /* it's a match */ - r = PAM_SUCCESS; - } else { - r = PAM_AUTH_ERR; - } - - free(buffer); - return r; -} - -/** - * @brief Free the user's responses. - * - * @param[in] resp Responses. - * @param[in] n Number of responses to be freed. - */ -static void -nc_pam_mod_resp_free(struct pam_response *resp, int n) -{ - int i; - - if (!resp) { - return; - } - - for (i = 0; i < n; i++) { - free((resp + i)->resp); - } - free(resp); -} - -/** - * @brief Test module's implementation of "auth" service. - * - * Prepare prompts for the client and decide based on his - * answers whether to allow or disallow access. - * - * @param[in] pam_h PAM handle. - * @param[in] flags Flags. - * @param[in] argc Count of module options defined in the PAM configuration file. - * @param[in] argv Module options. - * @return PAM_SUCCESS on success; - * @return PAM error otherwise. - */ -API int -pam_sm_authenticate(pam_handle_t *pam_h, int flags, int argc, const char **argv) -{ - int r; - const char *username; - char *reversed_username = NULL, *eq_ans = NULL; - struct pam_message echo_msg, no_echo_msg, unexpected_type_msg, info_msg, err_msg; - const struct pam_message *msg[N_MESSAGES]; - struct pam_response *resp = NULL; - - (void) flags; - (void) argc; - (void) argv; - - /* get the username and if it's not known then the user will be prompted to enter it */ - r = pam_get_user(pam_h, &username, NULL); - if (r != PAM_SUCCESS) { - fprintf(stderr, "Unable to get username.\n"); - r = PAM_AUTHINFO_UNAVAIL; - goto cleanup; - } - - /* prepare the messages */ - echo_msg.msg_style = PAM_PROMPT_ECHO_ON; - echo_msg.msg = "Enter your username backwards: "; - no_echo_msg.msg_style = PAM_PROMPT_ECHO_OFF; - no_echo_msg.msg = "Enter the result to 1+1: "; - unexpected_type_msg.msg_style = PAM_AUTH_ERR; - unexpected_type_msg.msg = "Arbitrary test message"; - info_msg.msg_style = PAM_TEXT_INFO; - info_msg.msg = "Test info message"; - err_msg.msg_style = PAM_ERROR_MSG; - err_msg.msg = "Test error message"; - - /* tests */ - printf("[TEST #1] Too many PAM messages. Output:\n"); - r = nc_pam_mod_call_clb(pam_h, 500, msg, &resp); - if (r == PAM_SUCCESS) { - fprintf(stderr, "[TEST #1] Failed.\n"); - r = PAM_AUTH_ERR; - goto cleanup; - } - printf("[TEST #1] Passed.\n\n"); - - printf("[TEST #2] Negative number of PAM messages. Output:\n"); - r = nc_pam_mod_call_clb(pam_h, -1, msg, &resp); - if (r == PAM_SUCCESS) { - fprintf(stderr, "[TEST #2] Failed.\n"); - r = PAM_AUTH_ERR; - goto cleanup; - } - printf("[TEST #2] Passed.\n\n"); - - printf("[TEST #3] 0 PAM messages. Output:\n"); - r = nc_pam_mod_call_clb(pam_h, 0, msg, &resp); - if (r == PAM_SUCCESS) { - fprintf(stderr, "[TEST #3] Failed.\n"); - r = PAM_AUTH_ERR; - goto cleanup; - } - printf("[TEST #3] Passed.\n\n"); - - printf("[TEST #4] Unexpected message type. Output:\n"); - msg[0] = &unexpected_type_msg; - r = nc_pam_mod_call_clb(pam_h, N_MESSAGES, msg, &resp); - if (r == PAM_SUCCESS) { - fprintf(stderr, "[TEST #4] Failed.\n"); - r = PAM_AUTH_ERR; - goto cleanup; - } - printf("[TEST #4] Passed.\n\n"); - - printf("[TEST #5] Info and error messages. Output:\n"); - msg[0] = &info_msg; - msg[1] = &err_msg; - r = nc_pam_mod_call_clb(pam_h, N_MESSAGES, msg, &resp); - if (r == PAM_SUCCESS) { - fprintf(stderr, "[TEST #5] Failed.\n"); - r = PAM_AUTH_ERR; - goto cleanup; - } - printf("[TEST #5] Passed.\n\n"); - - printf("[TEST #6] Authentication attempt with an expired token. Output:\n"); - /* store the correct messages */ - msg[0] = &echo_msg; - msg[1] = &no_echo_msg; - - /* get responses */ - r = nc_pam_mod_call_clb(pam_h, N_MESSAGES, msg, &resp); - if (r != PAM_SUCCESS) { - fprintf(stderr, "[TEST #6] Failed.\n"); - goto cleanup; - } - - reversed_username = resp[0].resp; - eq_ans = resp[1].resp; - - /* validate the responses */ - r = nc_pam_mod_auth(username, reversed_username, eq_ans); - - /* not authenticated */ - if (r != PAM_SUCCESS) { - fprintf(stderr, "[TEST #6] Failed.\n"); - r = PAM_AUTH_ERR; - } - -cleanup: - /* free the responses */ - nc_pam_mod_resp_free(resp, N_REQUESTS); - return r; -} - -/** - * @brief Test module's silly implementation of "account" service. - * - * @param[in] pam_h PAM handle. - * @param[in] flags Flags. - * @param[in] argc The count of module options defined in the PAM configuration file. - * @param[in] argv Module options. - * @return PAM_NEW_AUTHTOK_REQD on success; - * @return PAM error otherwise. - */ -API int -pam_sm_acct_mgmt(pam_handle_t *pam_h, int flags, int argc, const char *argv[]) -{ - int r; - const void *username; - - (void) flags; - (void) argc; - (void) argv; - - /* get and check the username */ - r = pam_get_item(pam_h, PAM_USER, &username); - if (r != PAM_SUCCESS) { - return r; - } - if (!strcmp((const char *)username, "test_int")) { - return PAM_NEW_AUTHTOK_REQD; - } - return PAM_SYSTEM_ERR; -} - -/** - * @brief Test module's silly implementation of "password" service. - * - * @param[in] pam_h PAM handle. - * @param[in] flags Flags. - * @param[in] argc The count of module options defined in the PAM configuration file. - * @param[in] argv Module options. - * @return PAM_SUCCESS on success; - * @return PAM error otherwise. - */ -API int -pam_sm_chauthtok(pam_handle_t *pam_h, int flags, int argc, const char *argv[]) -{ - int r; - const void *username; - - (void) argc; - (void) argv; - - /* the function is called twice, each time with a different flag, - * in the first call just check the username if it matches */ - if (flags & PAM_PRELIM_CHECK) { - r = pam_get_item(pam_h, PAM_USER, &username); - if (r != PAM_SUCCESS) { - return r; - } - if (!strcmp((const char *)username, "test_int")) { - return PAM_SUCCESS; - } else { - return PAM_SYSTEM_ERR; - } - - /* change the authentication token in the second call */ - } else if (flags & PAM_UPDATE_AUTHTOK) { - r = pam_set_item(pam_h, PAM_AUTHTOK, "test_int"); - if (r == PAM_SUCCESS) { - printf("[TEST #6] Passed.\n\n"); - } else { - fprintf(stderr, "[TEST #6] Failed.\n"); - } - return r; - } - return PAM_SYSTEM_ERR; -} diff --git a/tests/test_auth.c b/tests/test_auth.c index 70f5ba30..8635167f 100644 --- a/tests/test_auth.c +++ b/tests/test_auth.c @@ -1,10 +1,10 @@ /** * @file test_auth.c * @author Roman Janota - * @brief libnetconf2 Linux PAM keyboard-interactive authentication test + * @brief libnetconf2 SSH authentication methods test * * @copyright - * Copyright (c) 2022 CESNET, z.s.p.o. + * Copyright (c) 2023 CESNET, z.s.p.o. * * This source code is licensed under BSD 3-Clause License (the "License"). * You may not use this file except in compliance with the License. @@ -68,73 +68,6 @@ server_thread(void *arg) return NULL; } -static char * -auth_interactive(const char *auth_name, const char *instruction, const char *prompt, int echo, void *priv) -{ - (void) instruction; - (void) echo; - (void) auth_name; - (void) priv; - - /* send the replies to keyboard-interactive authentication */ - if (strstr(prompt, "backwards")) { - return strdup("tni_tset"); - } else if (strstr(prompt, "1+1")) { - return strdup("2"); - } else { - return NULL; - } -} - -static void * -client_thread_interactive(void *arg) -{ - int ret; - struct nc_session *session = NULL; - struct test_state *state = arg; - - /* skip all hostkey and known_hosts checks */ - nc_client_ssh_set_knownhosts_mode(NC_SSH_KNOWNHOSTS_SKIP); - - ret = nc_client_set_schema_searchpath(MODULES_DIR); - assert_int_equal(ret, 0); - - ret = nc_client_ssh_set_username("test_int"); - assert_int_equal(ret, 0); - - /* set keyboard-interactive authentication callback */ - nc_client_ssh_set_auth_interactive_clb(auth_interactive, NULL); - - nc_client_ssh_set_auth_pref(NC_SSH_AUTH_PUBLICKEY, -1); - nc_client_ssh_set_auth_pref(NC_SSH_AUTH_PASSWORD, -1); - nc_client_ssh_set_auth_pref(NC_SSH_AUTH_INTERACTIVE, 1); - - pthread_barrier_wait(&state->barrier); - session = nc_connect_ssh("127.0.0.1", 10005, NULL); - assert_non_null(session); - - nc_session_free(session, NULL); - return NULL; -} - -static void -test_nc_auth_interactive(void **state) -{ - int ret, i; - pthread_t tids[2]; - - assert_non_null(state); - - ret = pthread_create(&tids[0], NULL, client_thread_interactive, *state); - assert_int_equal(ret, 0); - ret = pthread_create(&tids[1], NULL, server_thread, *state); - assert_int_equal(ret, 0); - - for (i = 0; i < 2; i++) { - pthread_join(tids[i], NULL); - } -} - static char * auth_password(const char *username, const char *hostname, void *priv) { @@ -324,9 +257,6 @@ setup_f(void **state) ret = nc_server_config_add_ssh_user_pubkey(ctx, "endpt", "test_pk", "pubkey", TESTS_DIR "/data/key_rsa.pub", &tree); assert_int_equal(ret, 0); - ret = nc_server_config_add_ssh_user_interactive(ctx, "endpt", "test_int", "netconf.conf", BUILD_DIR "/tests", &tree); - assert_int_equal(ret, 0); - ret = nc_server_config_add_ssh_user_password(ctx, "endpt", "test_pw", "testpw", &tree); assert_int_equal(ret, 0); @@ -374,7 +304,6 @@ int main(void) { const struct CMUnitTest tests[] = { - cmocka_unit_test_setup_teardown(test_nc_auth_interactive, setup_f, teardown_f), cmocka_unit_test_setup_teardown(test_nc_auth_pubkey, setup_f, teardown_f), cmocka_unit_test_setup_teardown(test_nc_auth_password, setup_f, teardown_f), cmocka_unit_test_setup_teardown(test_nc_auth_none, setup_f, teardown_f) From eada13e99d7b30b04db8841c77f0672e84621932 Mon Sep 17 00:00:00 2001 From: roman Date: Thu, 23 Nov 2023 16:05:11 +0100 Subject: [PATCH 02/12] ln2 yang module REFACTOR add descriptions and NLs --- ...libnetconf2-netconf-server@2023-09-07.yang | 40 ++++++++++++++----- 1 file changed, 29 insertions(+), 11 deletions(-) diff --git a/modules/libnetconf2-netconf-server@2023-09-07.yang b/modules/libnetconf2-netconf-server@2023-09-07.yang index cd64d1a5..bbf57113 100644 --- a/modules/libnetconf2-netconf-server@2023-09-07.yang +++ b/modules/libnetconf2-netconf-server@2023-09-07.yang @@ -355,19 +355,23 @@ module libnetconf2-netconf-server { } } - augment "/ncs:netconf-server/ncs:listen/ncs:endpoint/ncs:transport/ncs:ssh/ncs:ssh/ncs:ssh-server-parameters/ncs:client-authentication" { + augment "/ncs:netconf-server/ncs:listen/ncs:endpoint/ncs:transport/ncs:ssh" + + "/ncs:ssh/ncs:ssh-server-parameters/ncs:client-authentication" { uses ssh-authentication-params-grouping; } - augment "/ncs:netconf-server/ncs:call-home/ncs:netconf-client/ncs:endpoints/ncs:endpoint/ncs:transport/ncs:ssh/ncs:ssh/ncs:ssh-server-parameters/ncs:client-authentication" { + augment "/ncs:netconf-server/ncs:call-home/ncs:netconf-client/ncs:endpoints" + + "/ncs:endpoint/ncs:transport/ncs:ssh/ncs:ssh/ncs:ssh-server-parameters/ncs:client-authentication" { uses ssh-authentication-params-grouping; } - augment "/ncs:netconf-server/ncs:listen/ncs:endpoint/ncs:transport/ncs:ssh/ncs:ssh/ncs:ssh-server-parameters/ncs:client-authentication/ncs:users/ncs:user" { + augment "/ncs:netconf-server/ncs:listen/ncs:endpoint/ncs:transport/ncs:ssh" + + "/ncs:ssh/ncs:ssh-server-parameters/ncs:client-authentication/ncs:users/ncs:user" { uses keyboard-interactive-grouping; } - augment "/ncs:netconf-server/ncs:call-home/ncs:netconf-client/ncs:endpoints/ncs:endpoint/ncs:transport/ncs:ssh/ncs:ssh/ncs:ssh-server-parameters/ncs:client-authentication/ncs:users/ncs:user" { + augment "/ncs:netconf-server/ncs:call-home/ncs:netconf-client/ncs:endpoints" + + "/ncs:endpoint/ncs:transport/ncs:ssh/ncs:ssh/ncs:ssh-server-parameters/ncs:client-authentication/ncs:users/ncs:user" { uses keyboard-interactive-grouping; } @@ -375,47 +379,61 @@ module libnetconf2-netconf-server { case unix-socket { container unix-socket { description - "Defines a new transport called UNIX socket."; + "UNIX socket listening configuration for inbound connections."; leaf path { type string; mandatory true; + description + "Path to the socket on which the communication will occur."; } leaf mode { type string { pattern '[0124567]{3}'; } + description + "Mode of the socket."; } leaf uid { type uint16; + description + "User ID of the socket."; } leaf gid { type uint16; + description + "Group ID of the socket."; } } } } - augment "/ncs:netconf-server/ncs:listen/ncs:endpoint/ncs:transport/ncs:ssh/ncs:ssh/ncs:ssh-server-parameters/ncs:client-authentication" { + augment "/ncs:netconf-server/ncs:listen/ncs:endpoint/ncs:transport/ncs:ssh" + + "/ncs:ssh/ncs:ssh-server-parameters/ncs:client-authentication" { uses endpoint-reference-grouping; } - augment "/ncs:netconf-server/ncs:call-home/ncs:netconf-client/ncs:endpoints/ncs:endpoint/ncs:transport/ncs:ssh/ncs:ssh/ncs:ssh-server-parameters/ncs:client-authentication" { + augment "/ncs:netconf-server/ncs:call-home/ncs:netconf-client/ncs:endpoints" + + "/ncs:endpoint/ncs:transport/ncs:ssh/ncs:ssh/ncs:ssh-server-parameters/ncs:client-authentication" { uses endpoint-reference-grouping; } - augment "/ncs:netconf-server/ncs:listen/ncs:endpoint/ncs:transport/ncs:tls/ncs:tls/ncs:tls-server-parameters/ncs:client-authentication" { + augment "/ncs:netconf-server/ncs:listen/ncs:endpoint/ncs:transport/ncs:tls" + + "/ncs:tls/ncs:tls-server-parameters/ncs:client-authentication" { uses endpoint-reference-grouping; } - augment "/ncs:netconf-server/ncs:call-home/ncs:netconf-client/ncs:endpoints/ncs:endpoint/ncs:transport/ncs:tls/ncs:tls/ncs:tls-server-parameters/ncs:client-authentication" { + augment "/ncs:netconf-server/ncs:call-home/ncs:netconf-client/ncs:endpoints" + + "/ncs:endpoint/ncs:transport/ncs:tls/ncs:tls/ncs:tls-server-parameters/ncs:client-authentication" { uses endpoint-reference-grouping; } - augment "/ncs:netconf-server/ncs:listen/ncs:endpoint/ncs:transport/ncs:tls/ncs:tls/ncs:tls-server-parameters/ncs:client-authentication" { + augment "/ncs:netconf-server/ncs:listen/ncs:endpoint/ncs:transport/ncs:tls" + + "/ncs:tls/ncs:tls-server-parameters/ncs:client-authentication" { uses certificate-revocation-list-grouping; } - augment "/ncs:netconf-server/ncs:call-home/ncs:netconf-client/ncs:endpoints/ncs:endpoint/ncs:transport/ncs:tls/ncs:tls/ncs:tls-server-parameters/ncs:client-authentication" { + augment "/ncs:netconf-server/ncs:call-home/ncs:netconf-client/ncs:endpoints" + + "/ncs:endpoint/ncs:transport/ncs:tls/ncs:tls/ncs:tls-server-parameters/ncs:client-authentication" { uses certificate-revocation-list-grouping; } } From 1ed958e65ff5ac370ceef789470fab489f83355d Mon Sep 17 00:00:00 2001 From: roman Date: Thu, 23 Nov 2023 16:12:42 +0100 Subject: [PATCH 03/12] server config UPDATE fix ch locking, refactor --- src/server_config.c | 737 ++++++++++++++++++++----------------------- src/session_server.c | 21 +- 2 files changed, 354 insertions(+), 404 deletions(-) diff --git a/src/server_config.c b/src/server_config.c index 1f1c8143..04281ae8 100644 --- a/src/server_config.c +++ b/src/server_config.c @@ -74,35 +74,82 @@ static const char *supported_mac_algs[] = { extern struct nc_server_opts server_opts; -/* returns true if a node is a part of the listen subtree */ -static int -is_listen(const struct lyd_node *node) +/* returns a parent node of 'node' that matches the name 'name' */ +static const struct lyd_node * +nc_server_config_get_parent(const struct lyd_node *node, const char *name) { - assert(node); + NC_CHECK_ARG_RET(NULL, node, name, NULL); while (node) { - if (!strcmp(LYD_NAME(node), "listen")) { - break; + if (!strcmp(LYD_NAME(node), name)) { + return node; } node = lyd_parent(node); } - return node != NULL; + return NULL; } -/* returns true if a node is a part of the Call Home subtree */ -static int -is_ch(const struct lyd_node *node) +/* returns a parent list node of 'node' that matches the name 'name' */ +static const struct lyd_node * +nc_server_config_get_parent_list(const struct lyd_node *node, const char *name) { - assert(node); + NC_CHECK_ARG_RET(NULL, node, name, NULL); while (node) { - if (!strcmp(LYD_NAME(node), "call-home")) { - break; + /* check if the node is a list and its name matches the param */ + if ((node->schema->nodetype == LYS_LIST) && (!strcmp(LYD_NAME(node), name))) { + return node; } node = lyd_parent(node); } + return NULL; +} + +/* returns the key of a list node with the name 'name' */ +static const char * +nc_server_config_get_parent_list_key_value(const struct lyd_node *node, const char *name, const char *key_name) +{ + const char *original_name; + + NC_CHECK_ARG_RET(NULL, node, name, key_name, NULL); + original_name = LYD_NAME(node); + + /* get the supposed parent list */ + node = nc_server_config_get_parent_list(node, name); + if (!node) { + ERR(NULL, "Node \"%s\" not contained in \"%s\" subtree.", original_name, name); + return NULL; + } + + /* child should be the key */ + node = lyd_child(node); + if (!node) { + ERR(NULL, "Node \"%s\" has no child nodes.", name); + return NULL; + } + if (strcmp(LYD_NAME(node), key_name)) { + ERR(NULL, "Node \"%s\" child names mismatch (found:\"%s\", expected:\"%s\").", original_name, LYD_NAME(node), key_name); + return NULL; + } + + return lyd_get_value(node); +} + +/* returns true if a node is a part of the listen subtree */ +static int +is_listen(const struct lyd_node *node) +{ + node = nc_server_config_get_parent(node, "listen"); + return node != NULL; +} + +/* returns true if a node is a part of the Call Home subtree */ +static int +is_ch(const struct lyd_node *node) +{ + node = nc_server_config_get_parent(node, "call-home"); return node != NULL; } @@ -112,15 +159,7 @@ is_ch(const struct lyd_node *node) static int is_ssh(const struct lyd_node *node) { - assert(node); - - while (node) { - if (!strcmp(LYD_NAME(node), "ssh")) { - break; - } - node = lyd_parent(node); - } - + node = nc_server_config_get_parent(node, "ssh"); return node != NULL; } @@ -128,15 +167,7 @@ is_ssh(const struct lyd_node *node) static int is_tls(const struct lyd_node *node) { - assert(node); - - while (node) { - if (!strcmp(LYD_NAME(node), "tls")) { - break; - } - node = lyd_parent(node); - } - + node = nc_server_config_get_parent(node, "tls"); return node != NULL; } @@ -149,25 +180,13 @@ nc_server_config_get_endpt(const struct lyd_node *node, struct nc_endpt **endpt, uint16_t i; const char *name; - assert(node && endpt); - name = LYD_NAME(node); - - while (node) { - if (!strcmp(LYD_NAME(node), "endpoint")) { - break; - } - node = lyd_parent(node); - } + NC_CHECK_ARG_RET(NULL, node, endpt, 1); - if (!node) { - ERR(NULL, "Node \"%s\" is not contained in an endpoint subtree.", name); + name = nc_server_config_get_parent_list_key_value(node, "endpoint", "name"); + if (!name) { return 1; } - node = lyd_child(node); - assert(!strcmp(LYD_NAME(node), "name")); - name = lyd_get_value(node); - for (i = 0; i < server_opts.endpt_count; i++) { if (!strcmp(server_opts.endpts[i].name, name)) { *endpt = &server_opts.endpts[i]; @@ -182,93 +201,57 @@ nc_server_config_get_endpt(const struct lyd_node *node, struct nc_endpt **endpt, return 1; } -/* gets the ch_client struct based on node's location in the YANG data tree */ +/* gets the ch_client struct based on node's location in the YANG data tree + * THE ch_client_lock HAS TO BE LOCKED PRIOR TO CALLING THIS + */ static int nc_server_config_get_ch_client(const struct lyd_node *node, struct nc_ch_client **ch_client) { uint16_t i; const char *name; - assert(node && ch_client); - name = LYD_NAME(node); + NC_CHECK_ARG_RET(NULL, node, ch_client, 1); - while (node) { - if (!strcmp(LYD_NAME(node), "netconf-client")) { - break; - } - node = lyd_parent(node); - } - - if (!node) { - ERR(NULL, "Node \"%s\" is not contained in a netconf-client subtree.", name); + name = nc_server_config_get_parent_list_key_value(node, "netconf-client", "name"); + if (!name) { return 1; } - node = lyd_child(node); - assert(!strcmp(LYD_NAME(node), "name")); - name = lyd_get_value(node); - - /* LOCK */ - pthread_rwlock_rdlock(&server_opts.ch_client_lock); for (i = 0; i < server_opts.ch_client_count; i++) { if (!strcmp(server_opts.ch_clients[i].name, name)) { *ch_client = &server_opts.ch_clients[i]; - /* UNLOCK */ - pthread_rwlock_unlock(&server_opts.ch_client_lock); return 0; } } - /* UNLOCK */ - pthread_rwlock_unlock(&server_opts.ch_client_lock); ERR(NULL, "Call-home client \"%s\" was not found.", name); return 1; } -/* gets the ch_endpt struct based on node's location in the YANG data tree */ +/* gets the ch_endpt struct based on node's location in the YANG data tree, + * ch_client has to be locked + */ static int -nc_server_config_get_ch_endpt(const struct lyd_node *node, struct nc_ch_endpt **ch_endpt) +nc_server_config_get_ch_endpt(const struct lyd_node *node, const struct nc_ch_client *ch_client, + struct nc_ch_endpt **ch_endpt) { uint16_t i; const char *name; - struct nc_ch_client *ch_client; - assert(node && ch_endpt); - name = LYD_NAME(node); + NC_CHECK_ARG_RET(NULL, node, ch_client, ch_endpt, 1); - if (nc_server_config_get_ch_client(node, &ch_client)) { + name = nc_server_config_get_parent_list_key_value(node, "endpoint", "name"); + if (!name) { return 1; } - while (node) { - if (!strcmp(LYD_NAME(node), "endpoint")) { - break; - } - node = lyd_parent(node); - } - - if (!node) { - ERR(NULL, "Node \"%s\" is not contained in a call-home endpoint subtree.", name); - return 1; - } - - node = lyd_child(node); - assert(!strcmp(LYD_NAME(node), "name")); - name = lyd_get_value(node); - - /* LOCK */ - pthread_rwlock_rdlock(&server_opts.ch_client_lock); for (i = 0; i < ch_client->ch_endpt_count; i++) { if (!strcmp(ch_client->ch_endpts[i].name, name)) { *ch_endpt = &ch_client->ch_endpts[i]; - /* UNLOCK */ - pthread_rwlock_unlock(&server_opts.ch_client_lock); return 0; } } - /* UNLOCK */ - pthread_rwlock_unlock(&server_opts.ch_client_lock); ERR(NULL, "Call-home client's \"%s\" endpoint \"%s\" was not found.", ch_client->name, name); return 1; } @@ -277,12 +260,13 @@ nc_server_config_get_ch_endpt(const struct lyd_node *node, struct nc_ch_endpt ** /* gets the ssh_opts struct based on node's location in the YANG data tree */ static int -nc_server_config_get_ssh_opts(const struct lyd_node *node, struct nc_server_ssh_opts **opts) +nc_server_config_get_ssh_opts(const struct lyd_node *node, const struct nc_ch_client *ch_client, + struct nc_server_ssh_opts **opts) { struct nc_endpt *endpt; struct nc_ch_endpt *ch_endpt; - assert(node && opts); + NC_CHECK_ARG_RET(NULL, node, opts, 1); if (is_listen(node)) { if (nc_server_config_get_endpt(node, &endpt, NULL)) { @@ -290,7 +274,7 @@ nc_server_config_get_ssh_opts(const struct lyd_node *node, struct nc_server_ssh_ } *opts = endpt->opts.ssh; } else { - if (nc_server_config_get_ch_endpt(node, &ch_endpt)) { + if (nc_server_config_get_ch_endpt(node, ch_client, &ch_endpt)) { return 1; } *opts = ch_endpt->opts.ssh; @@ -301,34 +285,24 @@ nc_server_config_get_ssh_opts(const struct lyd_node *node, struct nc_server_ssh_ /* gets the hostkey struct based on node's location in the YANG data tree */ static int -nc_server_config_get_hostkey(const struct lyd_node *node, struct nc_hostkey **hostkey) +nc_server_config_get_hostkey(const struct lyd_node *node, const struct nc_ch_client *ch_client, + struct nc_hostkey **hostkey) { uint16_t i; const char *name; struct nc_server_ssh_opts *opts; - assert(node && hostkey); - name = LYD_NAME(node); + NC_CHECK_ARG_RET(NULL, node, hostkey, 1); - while (node) { - if (!strcmp(LYD_NAME(node), "host-key")) { - break; - } - node = lyd_parent(node); - } - - if (!node) { - ERR(NULL, "Node \"%s\" is not contained in a host-key subtree.", name); + if (nc_server_config_get_ssh_opts(node, ch_client, &opts)) { return 1; } - node = lyd_child(node); - assert(!strcmp(LYD_NAME(node), "name")); - name = lyd_get_value(node); - - if (nc_server_config_get_ssh_opts(node, &opts)) { + name = nc_server_config_get_parent_list_key_value(node, "host-key", "name"); + if (!name) { return 1; } + for (i = 0; i < opts->hostkey_count; i++) { if (!strcmp(opts->hostkeys[i].name, name)) { *hostkey = &opts->hostkeys[i]; @@ -342,34 +316,24 @@ nc_server_config_get_hostkey(const struct lyd_node *node, struct nc_hostkey **ho /* gets the client_auth struct based on node's location in the YANG data tree */ static int -nc_server_config_get_auth_client(const struct lyd_node *node, struct nc_auth_client **auth_client) +nc_server_config_get_auth_client(const struct lyd_node *node, const struct nc_ch_client *ch_client, + struct nc_auth_client **auth_client) { uint16_t i; const char *name; struct nc_server_ssh_opts *opts; - assert(node && auth_client); - name = LYD_NAME(node); - - while (node) { - if (!strcmp(LYD_NAME(node), "user")) { - break; - } - node = lyd_parent(node); - } + NC_CHECK_ARG_RET(NULL, node, auth_client, 1); - if (!node) { - ERR(NULL, "Node \"%s\" is not contained in a client-authentication subtree.", name); + if (nc_server_config_get_ssh_opts(node, ch_client, &opts)) { return 1; } - node = lyd_child(node); - assert(!strcmp(LYD_NAME(node), "name")); - name = lyd_get_value(node); - - if (nc_server_config_get_ssh_opts(node, &opts)) { + name = nc_server_config_get_parent_list_key_value(node, "user", "name"); + if (!name) { return 1; } + for (i = 0; i < opts->client_count; i++) { if (!strcmp(opts->auth_clients[i].username, name)) { *auth_client = &opts->auth_clients[i]; @@ -383,35 +347,24 @@ nc_server_config_get_auth_client(const struct lyd_node *node, struct nc_auth_cli /* gets the pubkey struct based on node's location in the YANG data tree */ static int -nc_server_config_get_pubkey(const struct lyd_node *node, struct nc_public_key **pubkey) +nc_server_config_get_pubkey(const struct lyd_node *node, const struct nc_ch_client *ch_client, + struct nc_public_key **pubkey) { uint16_t i; const char *name; struct nc_auth_client *auth_client; - assert(node && pubkey); - name = LYD_NAME(node); + NC_CHECK_ARG_RET(NULL, node, pubkey, 1); - node = lyd_parent(node); - while (node) { - if (!strcmp(LYD_NAME(node), "public-key")) { - break; - } - node = lyd_parent(node); - } - - if (!node) { - ERR(NULL, "Node \"%s\" is not contained in a public-key subtree.", name); + if (nc_server_config_get_auth_client(node, ch_client, &auth_client)) { return 1; } - node = lyd_child(node); - assert(!strcmp(LYD_NAME(node), "name")); - name = lyd_get_value(node); - - if (nc_server_config_get_auth_client(node, &auth_client)) { + name = nc_server_config_get_parent_list_key_value(node, "public-key", "name"); + if (!name) { return 1; } + for (i = 0; i < auth_client->pubkey_count; i++) { if (!strcmp(auth_client->pubkeys[i].name, name)) { *pubkey = &auth_client->pubkeys[i]; @@ -425,12 +378,13 @@ nc_server_config_get_pubkey(const struct lyd_node *node, struct nc_public_key ** /* gets the tls_opts struct based on node's location in the YANG data tree */ static int -nc_server_config_get_tls_opts(const struct lyd_node *node, struct nc_server_tls_opts **opts) +nc_server_config_get_tls_opts(const struct lyd_node *node, const struct nc_ch_client *ch_client, + struct nc_server_tls_opts **opts) { struct nc_endpt *endpt; struct nc_ch_endpt *ch_endpt; - assert(node && opts); + NC_CHECK_ARG_RET(NULL, node, opts, 1); if (is_listen(node)) { if (nc_server_config_get_endpt(node, &endpt, NULL)) { @@ -438,7 +392,7 @@ nc_server_config_get_tls_opts(const struct lyd_node *node, struct nc_server_tls_ } *opts = endpt->opts.tls; } else { - if (nc_server_config_get_ch_endpt(node, &ch_endpt)) { + if (nc_server_config_get_ch_endpt(node, ch_client, &ch_endpt)) { return 1; } *opts = ch_endpt->opts.tls; @@ -449,92 +403,76 @@ nc_server_config_get_tls_opts(const struct lyd_node *node, struct nc_server_tls_ /* gets the cert struct based on node's location in the YANG data tree */ static int -nc_server_config_get_cert(const struct lyd_node *node, struct nc_certificate **cert) +nc_server_config_get_cert(const struct lyd_node *node, const struct nc_ch_client *ch_client, + struct nc_certificate **cert) { uint16_t i; const char *name; - struct nc_cert_grouping *auth_client; + struct nc_cert_grouping *certs; struct nc_server_tls_opts *opts; int is_cert_end_entity; - struct lyd_node *name_node; + const struct lyd_node *tmp; - assert(node && cert); - name = LYD_NAME(node); + NC_CHECK_ARG_RET(NULL, node, cert, 1); - /* check if node is in certificate subtree */ - while (node) { - if (!strcmp(LYD_NAME(node), "certificate")) { - break; - } - node = lyd_parent(node); - } - if (!node) { - ERR(NULL, "Node \"%s\" is not contained in a certificate subtree.", name); + if (nc_server_config_get_tls_opts(node, ch_client, &opts)) { return 1; } - /* it's child should be the list's key, check later */ - name_node = lyd_child(node); - - /* it's in certificate node, now check if it's end entity or certificate authority */ - while (node) { - if (!strcmp(LYD_NAME(node), "ee-certs")) { - is_cert_end_entity = 1; - break; - } else if (!strcmp(LYD_NAME(node), "ca-certs")) { - is_cert_end_entity = 0; - break; - } - node = lyd_parent(node); - } - if (!node) { - ERR(NULL, "Node \"%s\" is not contained in ee-certs nor ca-certs subtree.", name); + name = nc_server_config_get_parent_list_key_value(node, "certificate", "name"); + if (!name) { return 1; } - assert(!strcmp(LYD_NAME(name_node), "name")); - name = lyd_get_value(name_node); - - if (nc_server_config_get_tls_opts(node, &opts)) { - return 1; + /* it's in certificate subtree, now check if it's end entity or certificate authority */ + tmp = nc_server_config_get_parent(node, "ee-certs"); + if (tmp) { + is_cert_end_entity = 1; + } else { + tmp = nc_server_config_get_parent(node, "ca-certs"); + if (!tmp) { + ERR(NULL, "Node \"%s\" is not contained in ee-certs nor ca-certs subtree.", name); + return 1; + } + is_cert_end_entity = 0; } + + /* get the right cert stack, either ee or ca */ if (is_cert_end_entity) { - auth_client = &opts->ee_certs; + certs = &opts->ee_certs; } else { - auth_client = &opts->ca_certs; + certs = &opts->ca_certs; } - for (i = 0; i < auth_client->cert_count; i++) { - if (!strcmp(auth_client->certs[i].name, name)) { - *cert = &auth_client->certs[i]; + for (i = 0; i < certs->cert_count; i++) { + if (!strcmp(certs->certs[i].name, name)) { + *cert = &certs->certs[i]; return 0; } } - ERR(NULL, "Certificate \"%s\" was not found.", name); + ERR(NULL, "%s certificate \"%s\" was not found.", is_cert_end_entity ? "End-entity" : "Certificate authority", name); return 1; } /* gets the ctn struct based on node's location in the YANG data tree */ static int -nc_server_config_get_ctn(const struct lyd_node *node, struct nc_ctn **ctn) +nc_server_config_get_ctn(const struct lyd_node *node, const struct nc_ch_client *ch_client, + struct nc_ctn **ctn) { uint32_t id; struct nc_ctn *iter; struct nc_server_tls_opts *opts; const char *name; - assert(node && ctn); - name = LYD_NAME(node); + NC_CHECK_ARG_RET(NULL, node, ctn, 1); - node = lyd_parent(node); - while (node) { - if (!strcmp(LYD_NAME(node), "cert-to-name")) { - break; - } - node = lyd_parent(node); + if (nc_server_config_get_tls_opts(node, ch_client, &opts)) { + return 1; } + name = LYD_NAME(node); + node = nc_server_config_get_parent_list(node, "cert-to-name"); if (!node) { ERR(NULL, "Node \"%s\" is not contained in a cert-to-name subtree.", name); return 1; @@ -544,10 +482,6 @@ nc_server_config_get_ctn(const struct lyd_node *node, struct nc_ctn **ctn) assert(!strcmp(LYD_NAME(node), "id")); id = ((struct lyd_node_term *)node)->value.uint32; - if (nc_server_config_get_tls_opts(node, &opts)) { - return 1; - } - iter = opts->ctn; while (iter) { if (iter->id == id) { @@ -588,25 +522,13 @@ nc_server_config_get_ch_client_with_lock(const struct lyd_node *node, struct nc_ uint16_t i; const char *name; - assert(node && ch_client); - name = LYD_NAME(node); + NC_CHECK_ARG_RET(NULL, node, ch_client, 1); - while (node) { - if (!strcmp(LYD_NAME(node), "netconf-client")) { - break; - } - node = lyd_parent(node); - } - - if (!node) { - ERR(NULL, "Node \"%s\" is not contained in a netconf-client subtree.", name); + name = nc_server_config_get_parent_list_key_value(node, "netconf-client", "name"); + if (!name) { return 1; } - node = lyd_child(node); - assert(!strcmp(LYD_NAME(node), "name")); - name = lyd_get_value(node); - /* LOCK */ pthread_rwlock_rdlock(&server_opts.ch_client_lock); for (i = 0; i < server_opts.ch_client_count; i++) { @@ -776,11 +698,13 @@ nc_server_config_del_ssh_opts(struct nc_bind *bind, struct nc_server_ssh_opts *o free(opts); } +/* delete references to endpoint with the name 'referenced_endpt_name' from other endpts */ static void nc_server_config_del_endpt_references(const char *referenced_endpt_name) { uint16_t i, j; + /* first go through listen endpoints */ for (i = 0; i < server_opts.endpt_count; i++) { if (server_opts.endpts[i].referenced_endpt_name) { if (!strcmp(server_opts.endpts[i].referenced_endpt_name, referenced_endpt_name)) { @@ -798,6 +722,7 @@ nc_server_config_del_endpt_references(const char *referenced_endpt_name) /* LOCK */ pthread_rwlock_rdlock(&server_opts.ch_client_lock); + /* next go through ch endpoints */ for (i = 0; i < server_opts.ch_client_count; i++) { /* LOCK */ pthread_mutex_lock(&server_opts.ch_clients[i].lock); @@ -1015,42 +940,6 @@ nc_server_config_del_endpt_tls(struct nc_endpt *endpt, struct nc_bind *bind) #endif /* NC_ENABLED_SSH_TLS */ -/* presence container */ -int -nc_server_config_listen(const struct lyd_node *node, NC_OPERATION op) -{ - uint16_t i, endpt_count; - - (void) node; - - assert(op == NC_OP_CREATE || op == NC_OP_DELETE); - - if (op == NC_OP_DELETE) { - endpt_count = server_opts.endpt_count; - for (i = 0; i < endpt_count; i++) { - switch (server_opts.endpts[i].ti) { -#ifdef NC_ENABLED_SSH_TLS - case NC_TI_LIBSSH: - nc_server_config_del_endpt_ssh(&server_opts.endpts[i], &server_opts.binds[i]); - break; - case NC_TI_OPENSSL: - nc_server_config_del_endpt_tls(&server_opts.endpts[i], &server_opts.binds[i]); - break; -#endif /* NC_ENABLED_SSH_TLS */ - case NC_TI_UNIX: - nc_server_config_del_endpt_unix_socket(&server_opts.endpts[i], &server_opts.binds[i]); - break; - case NC_TI_NONE: - case NC_TI_FD: - ERRINT; - return 1; - } - } - } - - return 0; -} - static void nc_server_config_ch_del_endpt(struct nc_ch_client *ch_client, struct nc_ch_endpt *ch_endpt) { @@ -1087,19 +976,51 @@ nc_server_config_ch_del_endpt(struct nc_ch_client *ch_client, struct nc_ch_endpt } static void -nc_server_config_ch_del_client(struct nc_ch_client *ch_client) +nc_server_config_destroy_ch_client(struct nc_ch_client *ch_client) { - uint16_t i, ch_endpt_count; - struct nc_ch_client client; pthread_t tid; + uint16_t i, ch_endpt_count; + + if (ch_client->thread_data->thread_running) { + /* get tid */ + tid = ch_client->tid; + /* CH COND LOCK */ + pthread_mutex_lock(&ch_client->thread_data->cond_lock); + ch_client->thread_data->thread_running = 0; + pthread_cond_signal(&ch_client->thread_data->cond); + /* CH COND UNLOCK */ + pthread_mutex_unlock(&ch_client->thread_data->cond_lock); + + /* wait for the thread to terminate */ + pthread_join(tid, NULL); + } + + /* free its members */ + free(ch_client->name); + + ch_endpt_count = ch_client->ch_endpt_count; + for (i = 0; i < ch_endpt_count; i++) { + nc_server_config_ch_del_endpt(ch_client, &ch_client->ch_endpts[i]); + } +} + +static void +nc_server_config_ch_del_client(const struct lyd_node *node) +{ + struct nc_ch_client client, *ch_client; /* WR LOCK */ pthread_rwlock_wrlock(&server_opts.ch_client_lock); + if (nc_server_config_get_ch_client(node, &ch_client)) { + /* WR UNLOCK */ + pthread_rwlock_unlock(&server_opts.ch_client_lock); + ERR(NULL, "Call-home client \"%s\" not found.", lyd_get_value(lyd_child(node))); + return; + } + /* copy the client we want to delete into a local variable */ memcpy(&client, ch_client, sizeof *ch_client); - /* get his tid */ - tid = client.tid; /* delete the client */ server_opts.ch_client_count--; @@ -1113,51 +1034,77 @@ nc_server_config_ch_del_client(struct nc_ch_client *ch_client) /* WR UNLOCK */ pthread_rwlock_unlock(&server_opts.ch_client_lock); - /* RD LOCK */ - pthread_rwlock_rdlock(&server_opts.ch_client_lock); - /* MUTEX LOCK */ - pthread_mutex_lock(&client.lock); - - if (client.thread_data->thread_running) { - /* CH COND LOCK */ - pthread_mutex_lock(&client.thread_data->cond_lock); - client.thread_data->thread_running = 0; - pthread_cond_signal(&client.thread_data->cond); - /* CH COND UNLOCK */ - pthread_mutex_unlock(&client.thread_data->cond_lock); + nc_server_config_destroy_ch_client(&client); +} - /* MUTEX UNLOCK */ - pthread_mutex_unlock(&client.lock); - /* RD UNLOCK */ - pthread_rwlock_unlock(&server_opts.ch_client_lock); +/* presence container */ +int +nc_server_config_listen(const struct lyd_node *node, NC_OPERATION op) +{ + uint16_t i, endpt_count; - /* wait for the thread to terminate */ - pthread_join(tid, NULL); - } + (void) node; - /* free its members */ - free(client.name); + assert(op == NC_OP_CREATE || op == NC_OP_DELETE); - ch_endpt_count = client.ch_endpt_count; - for (i = 0; i < ch_endpt_count; i++) { - nc_server_config_ch_del_endpt(&client, &client.ch_endpts[i]); + if (op == NC_OP_DELETE) { + endpt_count = server_opts.endpt_count; + for (i = 0; i < endpt_count; i++) { + switch (server_opts.endpts[i].ti) { +#ifdef NC_ENABLED_SSH_TLS + case NC_TI_LIBSSH: + nc_server_config_del_endpt_ssh(&server_opts.endpts[i], &server_opts.binds[i]); + break; + case NC_TI_OPENSSL: + nc_server_config_del_endpt_tls(&server_opts.endpts[i], &server_opts.binds[i]); + break; +#endif /* NC_ENABLED_SSH_TLS */ + case NC_TI_UNIX: + nc_server_config_del_endpt_unix_socket(&server_opts.endpts[i], &server_opts.binds[i]); + break; + case NC_TI_NONE: + case NC_TI_FD: + ERRINT; + return 1; + } + } } + + return 0; } int nc_server_config_ch(const struct lyd_node *node, NC_OPERATION op) { uint16_t i, ch_client_count; + struct nc_ch_client *ch_clients; (void) node; - if (op == NC_OP_DELETE) { - ch_client_count = server_opts.ch_client_count; - for (i = 0; i < ch_client_count; i++) { - nc_server_config_ch_del_client(&server_opts.ch_clients[i]); - } + /* don't do anything if we're not deleting */ + if (op != NC_OP_DELETE) { + return 0; } + /* WR LOCK */ + pthread_rwlock_wrlock(&server_opts.ch_client_lock); + + ch_client_count = server_opts.ch_client_count; + ch_clients = server_opts.ch_clients; + + /* remove them from the server opts */ + server_opts.ch_client_count = 0; + server_opts.ch_clients = NULL; + + /* UNLOCK */ + pthread_rwlock_unlock(&server_opts.ch_client_lock); + + for (i = 0; i < ch_client_count; i++) { + /* now destroy each client */ + nc_server_config_destroy_ch_client(&ch_clients[i]); + } + + free(ch_clients); return 0; } @@ -1181,7 +1128,7 @@ nc_server_config_hello_timeout(const struct lyd_node *node, NC_OPERATION op) static int nc_server_config_idle_timeout(const struct lyd_node *node, NC_OPERATION op) { - struct nc_ch_client *ch_client; + struct nc_ch_client *ch_client = NULL; assert(!strcmp(LYD_NAME(node), "idle-timeout")); @@ -1259,7 +1206,7 @@ nc_server_config_endpoint(const struct lyd_node *node, NC_OPERATION op) struct nc_endpt *endpt; struct nc_bind *bind; struct nc_ch_endpt *ch_endpt; - struct nc_ch_client *ch_client; + struct nc_ch_client *ch_client = NULL; assert(!strcmp(LYD_NAME(node), "endpoint")); @@ -1312,7 +1259,7 @@ nc_server_config_endpoint(const struct lyd_node *node, NC_OPERATION op) /* init ch sock */ ch_client->ch_endpts[ch_client->ch_endpt_count - 1].sock_pending = -1; } else if (op == NC_OP_DELETE) { - if (nc_server_config_get_ch_endpt(node, &ch_endpt)) { + if (nc_server_config_get_ch_endpt(node, ch_client, &ch_endpt)) { ret = 1; goto cleanup; } @@ -1358,7 +1305,7 @@ nc_server_config_ssh(const struct lyd_node *node, NC_OPERATION op) struct nc_endpt *endpt; struct nc_bind *bind; struct nc_ch_endpt *ch_endpt; - struct nc_ch_client *ch_client; + struct nc_ch_client *ch_client = NULL; int ret = 0; assert(!strcmp(LYD_NAME(node), "ssh")); @@ -1384,7 +1331,7 @@ nc_server_config_ssh(const struct lyd_node *node, NC_OPERATION op) return 1; } - if (nc_server_config_get_ch_endpt(node, &ch_endpt)) { + if (nc_server_config_get_ch_endpt(node, ch_client, &ch_endpt)) { ret = 1; goto cleanup; } @@ -1433,7 +1380,7 @@ nc_server_config_tls(const struct lyd_node *node, NC_OPERATION op) struct nc_endpt *endpt; struct nc_bind *bind; struct nc_ch_endpt *ch_endpt; - struct nc_ch_client *ch_client; + struct nc_ch_client *ch_client = NULL; int ret = 0; assert(!strcmp(LYD_NAME(node), "tls")); @@ -1459,7 +1406,7 @@ nc_server_config_tls(const struct lyd_node *node, NC_OPERATION op) return 1; } - if (nc_server_config_get_ch_endpt(node, &ch_endpt)) { + if (nc_server_config_get_ch_endpt(node, ch_client, &ch_endpt)) { ret = 1; goto cleanup; } @@ -1648,7 +1595,7 @@ nc_server_config_keepalives(const struct lyd_node *node, NC_OPERATION op) return 1; } - if (nc_server_config_get_ch_endpt(node, &ch_endpt)) { + if (nc_server_config_get_ch_endpt(node, ch_client, &ch_endpt)) { ret = 1; goto cleanup; } @@ -1676,7 +1623,7 @@ nc_server_config_idle_time(const struct lyd_node *node, NC_OPERATION op) struct nc_endpt *endpt; struct nc_bind *bind; struct nc_ch_endpt *ch_endpt; - struct nc_ch_client *ch_client; + struct nc_ch_client *ch_client = NULL; assert(!strcmp(LYD_NAME(node), "idle-time")); @@ -1702,7 +1649,7 @@ nc_server_config_idle_time(const struct lyd_node *node, NC_OPERATION op) return 1; } - if (nc_server_config_get_ch_endpt(node, &ch_endpt)) { + if (nc_server_config_get_ch_endpt(node, ch_client, &ch_endpt)) { ret = 1; goto cleanup; } @@ -1730,7 +1677,7 @@ nc_server_config_max_probes(const struct lyd_node *node, NC_OPERATION op) struct nc_endpt *endpt; struct nc_bind *bind; struct nc_ch_endpt *ch_endpt; - struct nc_ch_client *ch_client; + struct nc_ch_client *ch_client = NULL; assert(!strcmp(LYD_NAME(node), "max-probes")); @@ -1756,7 +1703,7 @@ nc_server_config_max_probes(const struct lyd_node *node, NC_OPERATION op) return 1; } - if (nc_server_config_get_ch_endpt(node, &ch_endpt)) { + if (nc_server_config_get_ch_endpt(node, ch_client, &ch_endpt)) { ret = 1; goto cleanup; } @@ -1784,7 +1731,7 @@ nc_server_config_probe_interval(const struct lyd_node *node, NC_OPERATION op) struct nc_endpt *endpt; struct nc_bind *bind; struct nc_ch_endpt *ch_endpt; - struct nc_ch_client *ch_client; + struct nc_ch_client *ch_client = NULL; assert(!strcmp(LYD_NAME(node), "probe-interval")); @@ -1810,7 +1757,7 @@ nc_server_config_probe_interval(const struct lyd_node *node, NC_OPERATION op) return 1; } - if (nc_server_config_get_ch_endpt(node, &ch_endpt)) { + if (nc_server_config_get_ch_endpt(node, ch_client, &ch_endpt)) { ret = 1; goto cleanup; } @@ -1846,7 +1793,7 @@ nc_server_config_host_key(const struct lyd_node *node, NC_OPERATION op) int ret = 0; struct nc_hostkey *hostkey; struct nc_server_ssh_opts *opts; - struct nc_ch_client *ch_client; + struct nc_ch_client *ch_client = NULL; assert(!strcmp(LYD_NAME(node), "host-key")); @@ -1857,7 +1804,7 @@ nc_server_config_host_key(const struct lyd_node *node, NC_OPERATION op) return 1; } - if (nc_server_config_get_ssh_opts(node, &opts)) { + if (nc_server_config_get_ssh_opts(node, ch_client, &opts)) { ret = 1; goto cleanup; } @@ -1868,7 +1815,7 @@ nc_server_config_host_key(const struct lyd_node *node, NC_OPERATION op) goto cleanup; } } else if (op == NC_OP_DELETE) { - if (nc_server_config_get_hostkey(node, &hostkey)) { + if (nc_server_config_get_hostkey(node, ch_client, &hostkey)) { ret = 1; goto cleanup; } @@ -1894,7 +1841,7 @@ nc_server_config_public_key_format(const struct lyd_node *node, NC_OPERATION op) struct nc_public_key *pubkey; struct nc_hostkey *hostkey; struct nc_server_tls_opts *opts; - struct nc_ch_client *ch_client; + struct nc_ch_client *ch_client = NULL; assert(!strcmp(LYD_NAME(node), "public-key-format")); @@ -1917,7 +1864,7 @@ nc_server_config_public_key_format(const struct lyd_node *node, NC_OPERATION op) if (is_ssh(node) && equal_parent_name(node, 4, "server-identity")) { /* SSH hostkey public key fmt */ - if (nc_server_config_get_hostkey(node, &hostkey)) { + if (nc_server_config_get_hostkey(node, ch_client, &hostkey)) { ret = 1; goto cleanup; } @@ -1927,7 +1874,7 @@ nc_server_config_public_key_format(const struct lyd_node *node, NC_OPERATION op) } } else if (is_ssh(node) && equal_parent_name(node, 6, "client-authentication")) { /* SSH client auth public key fmt */ - if (nc_server_config_get_pubkey(node, &pubkey)) { + if (nc_server_config_get_pubkey(node, ch_client, &pubkey)) { ret = 1; goto cleanup; } @@ -1937,7 +1884,7 @@ nc_server_config_public_key_format(const struct lyd_node *node, NC_OPERATION op) } } else if (is_tls(node) && equal_parent_name(node, 3, "server-identity")) { /* TLS server-identity */ - if (nc_server_config_get_tls_opts(node, &opts)) { + if (nc_server_config_get_tls_opts(node, ch_client, &opts)) { ret = 1; goto cleanup; } @@ -2004,7 +1951,7 @@ nc_server_config_public_key(const struct lyd_node *node, NC_OPERATION op) struct nc_auth_client *auth_client; struct nc_public_key *pubkey; struct nc_server_tls_opts *opts; - struct nc_ch_client *ch_client; + struct nc_ch_client *ch_client = NULL; assert(!strcmp(LYD_NAME(node), "public-key")); @@ -2016,7 +1963,7 @@ nc_server_config_public_key(const struct lyd_node *node, NC_OPERATION op) if (is_ssh(node) && equal_parent_name(node, 3, "host-key")) { /* server's public-key, mandatory leaf */ - if (nc_server_config_get_hostkey(node, &hostkey)) { + if (nc_server_config_get_hostkey(node, ch_client, &hostkey)) { ret = 1; goto cleanup; } @@ -2039,7 +1986,7 @@ nc_server_config_public_key(const struct lyd_node *node, NC_OPERATION op) } } else if (is_ssh(node) && equal_parent_name(node, 5, "client-authentication")) { /* client auth pubkeys, list */ - if (nc_server_config_get_auth_client(node, &auth_client)) { + if (nc_server_config_get_auth_client(node, ch_client, &auth_client)) { ret = 1; goto cleanup; } @@ -2053,7 +2000,7 @@ nc_server_config_public_key(const struct lyd_node *node, NC_OPERATION op) goto cleanup; } } else if (op == NC_OP_DELETE) { - if (nc_server_config_get_pubkey(node, &pubkey)) { + if (nc_server_config_get_pubkey(node, ch_client, &pubkey)) { ret = 1; goto cleanup; } @@ -2062,7 +2009,7 @@ nc_server_config_public_key(const struct lyd_node *node, NC_OPERATION op) } } else if (is_ssh(node) && equal_parent_name(node, 6, "client-authentication")) { /* client auth pubkey, leaf */ - if (nc_server_config_get_pubkey(node, &pubkey)) { + if (nc_server_config_get_pubkey(node, ch_client, &pubkey)) { ret = 1; goto cleanup; } @@ -2085,7 +2032,7 @@ nc_server_config_public_key(const struct lyd_node *node, NC_OPERATION op) } } else if (is_tls(node) && equal_parent_name(node, 3, "server-identity")) { /* TLS server-identity */ - if (nc_server_config_get_tls_opts(node, &opts)) { + if (nc_server_config_get_tls_opts(node, ch_client, &opts)) { ret = 1; goto cleanup; } @@ -2125,7 +2072,7 @@ nc_server_config_private_key_format(const struct lyd_node *node, NC_OPERATION op NC_PRIVKEY_FORMAT privkey_type; struct nc_hostkey *hostkey; struct nc_server_tls_opts *opts; - struct nc_ch_client *ch_client; + struct nc_ch_client *ch_client = NULL; (void) op; @@ -2152,7 +2099,7 @@ nc_server_config_private_key_format(const struct lyd_node *node, NC_OPERATION op if (is_ssh(node)) { /* ssh */ - if (nc_server_config_get_hostkey(node, &hostkey)) { + if (nc_server_config_get_hostkey(node, ch_client, &hostkey)) { ret = 1; goto cleanup; } @@ -2160,7 +2107,7 @@ nc_server_config_private_key_format(const struct lyd_node *node, NC_OPERATION op hostkey->key.privkey_type = privkey_type; } else if (is_tls(node)) { /* tls */ - if (nc_server_config_get_tls_opts(node, &opts)) { + if (nc_server_config_get_tls_opts(node, ch_client, &opts)) { ret = 1; goto cleanup; } @@ -2182,7 +2129,7 @@ nc_server_config_cleartext_private_key(const struct lyd_node *node, NC_OPERATION int ret = 0; struct nc_hostkey *hostkey; struct nc_server_tls_opts *opts; - struct nc_ch_client *ch_client; + struct nc_ch_client *ch_client = NULL; assert(!strcmp(LYD_NAME(node), "cleartext-private-key")); @@ -2194,7 +2141,7 @@ nc_server_config_cleartext_private_key(const struct lyd_node *node, NC_OPERATION if (is_ssh(node)) { /* ssh */ - if (nc_server_config_get_hostkey(node, &hostkey)) { + if (nc_server_config_get_hostkey(node, ch_client, &hostkey)) { ret = 1; goto cleanup; } @@ -2209,7 +2156,7 @@ nc_server_config_cleartext_private_key(const struct lyd_node *node, NC_OPERATION } } else if (is_tls(node)) { /* tls */ - if (nc_server_config_get_tls_opts(node, &opts)) { + if (nc_server_config_get_tls_opts(node, ch_client, &opts)) { ret = 1; goto cleanup; } @@ -2238,7 +2185,7 @@ nc_server_config_keystore_reference(const struct lyd_node *node, NC_OPERATION op { int ret = 0; struct nc_hostkey *hostkey; - struct nc_ch_client *ch_client; + struct nc_ch_client *ch_client = NULL; assert(!strcmp(LYD_NAME(node), "keystore-reference")); @@ -2249,7 +2196,7 @@ nc_server_config_keystore_reference(const struct lyd_node *node, NC_OPERATION op } if (is_ssh(node) && equal_parent_name(node, 3, "server-identity")) { - if (nc_server_config_get_hostkey(node, &hostkey)) { + if (nc_server_config_get_hostkey(node, ch_client, &hostkey)) { ret = 1; goto cleanup; } @@ -2291,7 +2238,7 @@ nc_server_config_user(const struct lyd_node *node, NC_OPERATION op) int ret = 0; struct nc_auth_client *auth_client; struct nc_server_ssh_opts *opts; - struct nc_ch_client *ch_client; + struct nc_ch_client *ch_client = NULL; assert(!strcmp(LYD_NAME(node), "user")); @@ -2301,7 +2248,7 @@ nc_server_config_user(const struct lyd_node *node, NC_OPERATION op) return 1; } - if (nc_server_config_get_ssh_opts(node, &opts)) { + if (nc_server_config_get_ssh_opts(node, ch_client, &opts)) { ret = 1; goto cleanup; } @@ -2312,7 +2259,7 @@ nc_server_config_user(const struct lyd_node *node, NC_OPERATION op) goto cleanup; } } else if (op == NC_OP_DELETE) { - if (nc_server_config_get_auth_client(node, &auth_client)) { + if (nc_server_config_get_auth_client(node, ch_client, &auth_client)) { ret = 1; goto cleanup; } @@ -2333,7 +2280,7 @@ nc_server_config_auth_attempts(const struct lyd_node *node, NC_OPERATION op) { int ret = 0; struct nc_server_ssh_opts *opts; - struct nc_ch_client *ch_client; + struct nc_ch_client *ch_client = NULL; assert(!strcmp(LYD_NAME(node), "auth-attempts")); @@ -2343,7 +2290,7 @@ nc_server_config_auth_attempts(const struct lyd_node *node, NC_OPERATION op) return 1; } - if (nc_server_config_get_ssh_opts(node, &opts)) { + if (nc_server_config_get_ssh_opts(node, ch_client, &opts)) { ret = 1; goto cleanup; } @@ -2365,7 +2312,7 @@ nc_server_config_auth_timeout(const struct lyd_node *node, NC_OPERATION op) { int ret = 0; struct nc_server_ssh_opts *opts; - struct nc_ch_client *ch_client; + struct nc_ch_client *ch_client = NULL; assert(!strcmp(LYD_NAME(node), "auth-timeout")); @@ -2375,7 +2322,7 @@ nc_server_config_auth_timeout(const struct lyd_node *node, NC_OPERATION op) return 1; } - if (nc_server_config_get_ssh_opts(node, &opts)) { + if (nc_server_config_get_ssh_opts(node, ch_client, &opts)) { ret = 1; goto cleanup; } @@ -2398,7 +2345,7 @@ nc_server_config_truststore_reference(const struct lyd_node *node, NC_OPERATION { int ret = 0; struct nc_auth_client *auth_client; - struct nc_ch_client *ch_client; + struct nc_ch_client *ch_client = NULL; struct nc_server_tls_opts *opts; struct nc_cert_grouping *certs_grp; @@ -2411,7 +2358,7 @@ nc_server_config_truststore_reference(const struct lyd_node *node, NC_OPERATION } if (is_ssh(node) && equal_parent_name(node, 1, "public-keys")) { - if (nc_server_config_get_auth_client(node, &auth_client)) { + if (nc_server_config_get_auth_client(node, ch_client, &auth_client)) { ret = 1; goto cleanup; } @@ -2429,7 +2376,7 @@ nc_server_config_truststore_reference(const struct lyd_node *node, NC_OPERATION } } else if (is_tls(node) && (equal_parent_name(node, 1, "ca-certs") || equal_parent_name(node, 1, "ee-certs"))) { /* ee-certs or ca-certs */ - if (nc_server_config_get_tls_opts(node, &opts)) { + if (nc_server_config_get_tls_opts(node, ch_client, &opts)) { ret = 1; goto cleanup; } @@ -2467,7 +2414,7 @@ nc_server_config_password(const struct lyd_node *node, NC_OPERATION op) { int ret = 0; struct nc_auth_client *auth_client; - struct nc_ch_client *ch_client; + struct nc_ch_client *ch_client = NULL; assert(!strcmp(LYD_NAME(node), "password")); @@ -2477,7 +2424,7 @@ nc_server_config_password(const struct lyd_node *node, NC_OPERATION op) return 1; } - if (nc_server_config_get_auth_client(node, &auth_client)) { + if (nc_server_config_get_auth_client(node, ch_client, &auth_client)) { ret = 1; goto cleanup; } @@ -2514,7 +2461,7 @@ nc_server_config_kb_int(const struct lyd_node *node, NC_OPERATION op) return 1; } - if (nc_server_config_get_auth_client(node, &auth_client)) { + if (nc_server_config_get_auth_client(node, ch_client, &auth_client)) { ret = 1; goto cleanup; } @@ -2539,7 +2486,7 @@ nc_server_config_none(const struct lyd_node *node, NC_OPERATION op) { int ret = 0; struct nc_auth_client *auth_client; - struct nc_ch_client *ch_client; + struct nc_ch_client *ch_client = NULL; assert(!strcmp(LYD_NAME(node), "none")); @@ -2549,7 +2496,7 @@ nc_server_config_none(const struct lyd_node *node, NC_OPERATION op) return 1; } - if (nc_server_config_get_auth_client(node, &auth_client)) { + if (nc_server_config_get_auth_client(node, ch_client, &auth_client)) { ret = 1; goto cleanup; } @@ -2651,7 +2598,7 @@ nc_server_config_host_key_alg(const struct lyd_node *node, NC_OPERATION op) const char *alg; uint8_t i; struct nc_server_ssh_opts *opts; - struct nc_ch_client *ch_client; + struct nc_ch_client *ch_client = NULL; assert(!strcmp(LYD_NAME(node), "host-key-alg")); @@ -2661,7 +2608,7 @@ nc_server_config_host_key_alg(const struct lyd_node *node, NC_OPERATION op) return 1; } - if (nc_server_config_get_ssh_opts(node, &opts)) { + if (nc_server_config_get_ssh_opts(node, ch_client, &opts)) { ret = 1; goto cleanup; } @@ -2701,7 +2648,7 @@ nc_server_config_kex_alg(const struct lyd_node *node, NC_OPERATION op) const char *alg; uint8_t i; struct nc_server_ssh_opts *opts; - struct nc_ch_client *ch_client; + struct nc_ch_client *ch_client = NULL; assert(!strcmp(LYD_NAME(node), "key-exchange-alg")); @@ -2711,7 +2658,7 @@ nc_server_config_kex_alg(const struct lyd_node *node, NC_OPERATION op) return 1; } - if (nc_server_config_get_ssh_opts(node, &opts)) { + if (nc_server_config_get_ssh_opts(node, ch_client, &opts)) { ret = 1; goto cleanup; } @@ -2751,7 +2698,7 @@ nc_server_config_encryption_alg(const struct lyd_node *node, NC_OPERATION op) const char *alg; uint8_t i; struct nc_server_ssh_opts *opts; - struct nc_ch_client *ch_client; + struct nc_ch_client *ch_client = NULL; assert(!strcmp(LYD_NAME(node), "encryption-alg")); @@ -2761,7 +2708,7 @@ nc_server_config_encryption_alg(const struct lyd_node *node, NC_OPERATION op) return 1; } - if (nc_server_config_get_ssh_opts(node, &opts)) { + if (nc_server_config_get_ssh_opts(node, ch_client, &opts)) { ret = 1; goto cleanup; } @@ -2801,7 +2748,7 @@ nc_server_config_mac_alg(const struct lyd_node *node, NC_OPERATION op) const char *alg; uint8_t i; struct nc_server_ssh_opts *opts; - struct nc_ch_client *ch_client; + struct nc_ch_client *ch_client = NULL; assert(!strcmp(LYD_NAME(node), "mac-alg")); @@ -2811,7 +2758,7 @@ nc_server_config_mac_alg(const struct lyd_node *node, NC_OPERATION op) return 1; } - if (nc_server_config_get_ssh_opts(node, &opts)) { + if (nc_server_config_get_ssh_opts(node, ch_client, &opts)) { ret = 1; goto cleanup; } @@ -3065,7 +3012,7 @@ nc_server_config_endpoint_reference(const struct lyd_node *node, NC_OPERATION op { int ret = 0; struct nc_endpt *endpt = NULL; - struct nc_ch_client *ch_client; + struct nc_ch_client *ch_client = NULL; struct nc_ch_endpt *ch_endpt = NULL; struct nc_server_ssh_opts *ssh = NULL; struct nc_server_tls_opts *tls = NULL; @@ -3081,7 +3028,7 @@ nc_server_config_endpoint_reference(const struct lyd_node *node, NC_OPERATION op if (is_listen(node)) { ret = nc_server_config_get_endpt(node, &endpt, NULL); } else { - ret = nc_server_config_get_ch_endpt(node, &ch_endpt); + ret = nc_server_config_get_ch_endpt(node, ch_client, &ch_endpt); } if (ret) { goto cleanup; @@ -3098,14 +3045,14 @@ nc_server_config_endpoint_reference(const struct lyd_node *node, NC_OPERATION op ch_endpt->referenced_endpt_name = NULL; } if (is_ssh(node)) { - if (nc_server_config_get_ssh_opts(node, &ssh)) { + if (nc_server_config_get_ssh_opts(node, ch_client, &ssh)) { ret = 1; goto cleanup; } ssh->referenced_endpt_name = NULL; } else { - if (nc_server_config_get_tls_opts(node, &tls)) { + if (nc_server_config_get_tls_opts(node, ch_client, &tls)) { ret = 1; goto cleanup; } @@ -3144,7 +3091,7 @@ nc_server_config_cert_data(const struct lyd_node *node, NC_OPERATION op) int ret = 0; struct nc_certificate *cert; struct nc_server_tls_opts *opts; - struct nc_ch_client *ch_client; + struct nc_ch_client *ch_client = NULL; assert(!strcmp(LYD_NAME(node), "cert-data")); @@ -3155,7 +3102,7 @@ nc_server_config_cert_data(const struct lyd_node *node, NC_OPERATION op) } if (equal_parent_name(node, 3, "server-identity")) { - if (nc_server_config_get_tls_opts(node, &opts)) { + if (nc_server_config_get_tls_opts(node, ch_client, &opts)) { ret = 1; goto cleanup; } @@ -3166,7 +3113,7 @@ nc_server_config_cert_data(const struct lyd_node *node, NC_OPERATION op) NC_CHECK_ERRMEM_GOTO(!opts->cert_data, ret = 1, cleanup); } } else if (equal_parent_name(node, 3, "ca-certs") || equal_parent_name(node, 3, "ee-certs")) { - if (nc_server_config_get_cert(node, &cert)) { + if (nc_server_config_get_cert(node, ch_client, &cert)) { ret = 1; goto cleanup; } @@ -3194,7 +3141,7 @@ nc_server_config_asymmetric_key(const struct lyd_node *node, NC_OPERATION op) { int ret = 0; struct nc_server_tls_opts *opts; - struct nc_ch_client *ch_client; + struct nc_ch_client *ch_client = NULL; assert(!strcmp(LYD_NAME(node), "asymmetric-key")); @@ -3204,7 +3151,7 @@ nc_server_config_asymmetric_key(const struct lyd_node *node, NC_OPERATION op) return 1; } - if (nc_server_config_get_tls_opts(node, &opts)) { + if (nc_server_config_get_tls_opts(node, ch_client, &opts)) { ret = 1; goto cleanup; } @@ -3256,7 +3203,7 @@ nc_server_config_certificate(const struct lyd_node *node, NC_OPERATION op) { int ret = 0; struct nc_server_tls_opts *opts; - struct nc_ch_client *ch_client; + struct nc_ch_client *ch_client = NULL; struct nc_certificate *cert; assert(!strcmp(LYD_NAME(node), "certificate")); @@ -3267,7 +3214,7 @@ nc_server_config_certificate(const struct lyd_node *node, NC_OPERATION op) return 1; } - if (nc_server_config_get_tls_opts(node, &opts)) { + if (nc_server_config_get_tls_opts(node, ch_client, &opts)) { ret = 1; goto cleanup; } @@ -3293,7 +3240,7 @@ nc_server_config_certificate(const struct lyd_node *node, NC_OPERATION op) goto cleanup; } } else { - if (nc_server_config_get_cert(node, &cert)) { + if (nc_server_config_get_cert(node, ch_client, &cert)) { ret = 1; goto cleanup; } @@ -3307,7 +3254,7 @@ nc_server_config_certificate(const struct lyd_node *node, NC_OPERATION op) goto cleanup; } } else { - if (nc_server_config_get_cert(node, &cert)) { + if (nc_server_config_get_cert(node, ch_client, &cert)) { ret = 1; goto cleanup; } @@ -3410,7 +3357,7 @@ nc_server_config_cert_to_name(const struct lyd_node *node, NC_OPERATION op) int ret = 0; struct nc_server_tls_opts *opts; struct nc_ctn *ctn; - struct nc_ch_client *ch_client; + struct nc_ch_client *ch_client = NULL; assert(!strcmp(LYD_NAME(node), "cert-to-name")); @@ -3420,7 +3367,7 @@ nc_server_config_cert_to_name(const struct lyd_node *node, NC_OPERATION op) return 1; } - if (nc_server_config_get_tls_opts(node, &opts)) { + if (nc_server_config_get_tls_opts(node, ch_client, &opts)) { ret = 1; goto cleanup; } @@ -3432,7 +3379,7 @@ nc_server_config_cert_to_name(const struct lyd_node *node, NC_OPERATION op) } } else { /* find the given ctn entry */ - if (nc_server_config_get_ctn(node, &ctn)) { + if (nc_server_config_get_ctn(node, ch_client, &ctn)) { ret = 1; goto cleanup; } @@ -3452,7 +3399,7 @@ nc_server_config_fingerprint(const struct lyd_node *node, NC_OPERATION op) { int ret = 0; struct nc_ctn *ctn; - struct nc_ch_client *ch_client; + struct nc_ch_client *ch_client = NULL; assert(!strcmp(LYD_NAME(node), "fingerprint")); @@ -3462,7 +3409,7 @@ nc_server_config_fingerprint(const struct lyd_node *node, NC_OPERATION op) return 1; } - if (nc_server_config_get_ctn(node, &ctn)) { + if (nc_server_config_get_ctn(node, ch_client, &ctn)) { ret = 1; goto cleanup; } @@ -3490,7 +3437,7 @@ nc_server_config_tls_version(const struct lyd_node *node, NC_OPERATION op) int ret = 0; struct nc_server_tls_opts *opts; const char *version = NULL; - struct nc_ch_client *ch_client; + struct nc_ch_client *ch_client = NULL; NC_TLS_VERSION tls_version; assert(!strcmp(LYD_NAME(node), "tls-version")); @@ -3501,7 +3448,7 @@ nc_server_config_tls_version(const struct lyd_node *node, NC_OPERATION op) return 1; } - if (nc_server_config_get_tls_opts(node, &opts)) { + if (nc_server_config_get_tls_opts(node, ch_client, &opts)) { ret = 1; goto cleanup; } @@ -3598,7 +3545,7 @@ nc_server_config_cipher_suite(const struct lyd_node *node, NC_OPERATION op) int ret = 0; struct nc_server_tls_opts *opts; const char *cipher = NULL; - struct nc_ch_client *ch_client; + struct nc_ch_client *ch_client = NULL; assert(!strcmp(LYD_NAME(node), "cipher-suite")); @@ -3608,7 +3555,7 @@ nc_server_config_cipher_suite(const struct lyd_node *node, NC_OPERATION op) return 1; } - if (nc_server_config_get_tls_opts(node, &opts)) { + if (nc_server_config_get_tls_opts(node, ch_client, &opts)) { ret = 1; goto cleanup; } @@ -3639,7 +3586,7 @@ nc_server_config_crl_url(const struct lyd_node *node, NC_OPERATION op) { int ret = 0; struct nc_server_tls_opts *opts; - struct nc_ch_client *ch_client; + struct nc_ch_client *ch_client = NULL; assert(!strcmp(LYD_NAME(node), "crl-url")); @@ -3649,7 +3596,7 @@ nc_server_config_crl_url(const struct lyd_node *node, NC_OPERATION op) return 1; } - if (nc_server_config_get_tls_opts(node, &opts)) { + if (nc_server_config_get_tls_opts(node, ch_client, &opts)) { ret = 1; goto cleanup; } @@ -3676,7 +3623,7 @@ nc_server_config_crl_path(const struct lyd_node *node, NC_OPERATION op) { int ret = 0; struct nc_server_tls_opts *opts; - struct nc_ch_client *ch_client; + struct nc_ch_client *ch_client = NULL; assert(!strcmp(LYD_NAME(node), "crl-path")); @@ -3686,7 +3633,7 @@ nc_server_config_crl_path(const struct lyd_node *node, NC_OPERATION op) return 1; } - if (nc_server_config_get_tls_opts(node, &opts)) { + if (nc_server_config_get_tls_opts(node, ch_client, &opts)) { ret = 1; goto cleanup; } @@ -3713,7 +3660,7 @@ nc_server_config_crl_cert_ext(const struct lyd_node *node, NC_OPERATION op) { int ret = 0; struct nc_server_tls_opts *opts; - struct nc_ch_client *ch_client; + struct nc_ch_client *ch_client = NULL; assert(!strcmp(LYD_NAME(node), "crl-cert-ext")); @@ -3723,7 +3670,7 @@ nc_server_config_crl_cert_ext(const struct lyd_node *node, NC_OPERATION op) return 1; } - if (nc_server_config_get_tls_opts(node, &opts)) { + if (nc_server_config_get_tls_opts(node, ch_client, &opts)) { ret = 1; goto cleanup; } @@ -3776,7 +3723,6 @@ static int nc_server_config_netconf_client(const struct lyd_node *node, NC_OPERATION op) { int ret = 0; - struct nc_ch_client *ch_client; assert(!strcmp(LYD_NAME(node), "netconf-client")); @@ -3800,12 +3746,7 @@ nc_server_config_netconf_client(const struct lyd_node *node, NC_OPERATION op) } #endif /* NC_ENABLED_SSH_TLS */ } else if (op == NC_OP_DELETE) { - if (nc_server_config_get_ch_client(node, &ch_client)) { - ret = 1; - goto cleanup; - } - - nc_server_config_ch_del_client(ch_client); + nc_server_config_ch_del_client(node); } cleanup: @@ -3819,7 +3760,7 @@ nc_server_config_remote_address(const struct lyd_node *node, NC_OPERATION op) { int ret = 0; struct nc_ch_endpt *ch_endpt; - struct nc_ch_client *ch_client; + struct nc_ch_client *ch_client = NULL; assert(!strcmp(LYD_NAME(node), "remote-address")); @@ -3829,7 +3770,7 @@ nc_server_config_remote_address(const struct lyd_node *node, NC_OPERATION op) return 1; } - if (nc_server_config_get_ch_endpt(node, &ch_endpt)) { + if (nc_server_config_get_ch_endpt(node, ch_client, &ch_endpt)) { ret = 1; goto cleanup; } @@ -3854,7 +3795,7 @@ nc_server_config_remote_port(const struct lyd_node *node, NC_OPERATION op) { int ret = 0; struct nc_ch_endpt *ch_endpt; - struct nc_ch_client *ch_client; + struct nc_ch_client *ch_client = NULL; assert(!strcmp(LYD_NAME(node), "remote-port")); @@ -3864,7 +3805,7 @@ nc_server_config_remote_port(const struct lyd_node *node, NC_OPERATION op) return 1; } - if (nc_server_config_get_ch_endpt(node, &ch_endpt)) { + if (nc_server_config_get_ch_endpt(node, ch_client, &ch_endpt)) { ret = 1; goto cleanup; } @@ -3887,7 +3828,7 @@ static int nc_server_config_persistent(const struct lyd_node *node, NC_OPERATION op) { int ret = 0; - struct nc_ch_client *ch_client; + struct nc_ch_client *ch_client = NULL; assert(!strcmp(LYD_NAME(node), "persistent")); @@ -3911,7 +3852,7 @@ static int nc_server_config_periodic(const struct lyd_node *node, NC_OPERATION op) { int ret = 0; - struct nc_ch_client *ch_client; + struct nc_ch_client *ch_client = NULL; assert(!strcmp(LYD_NAME(node), "periodic")); @@ -3939,7 +3880,7 @@ static int nc_server_config_period(const struct lyd_node *node, NC_OPERATION op) { int ret = 0; - struct nc_ch_client *ch_client; + struct nc_ch_client *ch_client = NULL; assert(!strcmp(LYD_NAME(node), "period")); @@ -3965,7 +3906,7 @@ static int nc_server_config_anchor_time(const struct lyd_node *node, NC_OPERATION op) { int ret = 0; - struct nc_ch_client *ch_client; + struct nc_ch_client *ch_client = NULL; struct lyd_value_date_and_time *anchor_time; assert(!strcmp(LYD_NAME(node), "anchor-time")); @@ -3994,7 +3935,7 @@ static int nc_server_config_reconnect_strategy(const struct lyd_node *node, NC_OPERATION op) { int ret = 0; - struct nc_ch_client *ch_client; + struct nc_ch_client *ch_client = NULL; assert(!strcmp(LYD_NAME(node), "reconnect-strategy")); @@ -4021,7 +3962,7 @@ static int nc_server_config_start_with(const struct lyd_node *node, NC_OPERATION op) { int ret = 0; - struct nc_ch_client *ch_client; + struct nc_ch_client *ch_client = NULL; const char *value; assert(!strcmp(LYD_NAME(node), "start-with")); @@ -4060,7 +4001,7 @@ static int nc_server_config_max_wait(const struct lyd_node *node, NC_OPERATION op) { int ret = 0; - struct nc_ch_client *ch_client; + struct nc_ch_client *ch_client = NULL; assert(!strcmp(LYD_NAME(node), "max-wait")); @@ -4086,7 +4027,7 @@ static int nc_server_config_max_attempts(const struct lyd_node *node, NC_OPERATION op) { int ret = 0; - struct nc_ch_client *ch_client; + struct nc_ch_client *ch_client = NULL; assert(!strcmp(LYD_NAME(node), "max-attempts")); diff --git a/src/session_server.c b/src/session_server.c index dd2c4e3c..601030c9 100644 --- a/src/session_server.c +++ b/src/session_server.c @@ -2320,14 +2320,14 @@ nc_connect_ch_endpt(struct nc_ch_endpt *endpt, nc_server_ch_session_acquire_ctx_ /** * @brief Wait for any event after a NC session was established on a CH client. * - * @param[in] session New NC session. * @param[in] data CH client thread argument. + * @param[in] session New NC session. The session is invalid upon being freed (= function exit). * @return 0 if session was terminated normally, * @return 1 if the CH client was removed, * @return -1 on error. */ static int -nc_server_ch_client_thread_session_cond_wait(struct nc_session *session, struct nc_ch_client_thread_arg *data) +nc_server_ch_client_thread_session_cond_wait(struct nc_ch_client_thread_arg *data, struct nc_session *session) { int rc = 0, r; uint32_t idle_timeout; @@ -2531,7 +2531,10 @@ nc_ch_client_thread(void *arg) /* LOCK */ client = nc_server_ch_client_with_endpt_lock(data->client_name); - assert(client); + if (!client) { + VRB(NULL, "Call Home client \"%s\" removed.", data->client_name); + goto cleanup; + } cur_endpt = &client->ch_endpts[0]; cur_endpt_name = strdup(cur_endpt->name); @@ -2553,11 +2556,12 @@ nc_ch_client_thread(void *arg) /* run while the session is established */ VRB(session, "Call Home client \"%s\" session %u established.", data->client_name, session->id); - if (nc_server_ch_client_thread_session_cond_wait(session, data)) { + if (nc_server_ch_client_thread_session_cond_wait(data, session)) { goto cleanup; } + session = NULL; - VRB(session, "Call Home client \"%s\" session terminated.", data->client_name); + VRB(NULL, "Call Home client \"%s\" session terminated.", data->client_name); if (!nc_server_ch_client_thread_is_running(data)) { /* thread should stop running */ goto cleanup; @@ -2565,7 +2569,10 @@ nc_ch_client_thread(void *arg) /* LOCK */ client = nc_server_ch_client_with_endpt_lock(data->client_name); - assert(client); + if (!client) { + VRB(NULL, "Call Home client \"%s\" removed.", data->client_name); + goto cleanup; + } /* session changed status -> it was disconnected for whatever reason, * persistent connection immediately tries to reconnect, periodic connects at specific times */ @@ -2677,6 +2684,8 @@ nc_ch_client_thread(void *arg) VRB(session, "Call Home client \"%s\" thread exit.", data->client_name); free(cur_endpt_name); free(data->client_name); + pthread_cond_destroy(&data->cond); + pthread_mutex_destroy(&data->cond_lock); free(data); return NULL; } From 8d6703a4ab096c7a5f092102e4070137a26c58da Mon Sep 17 00:00:00 2001 From: roman Date: Fri, 24 Nov 2023 11:29:47 +0100 Subject: [PATCH 04/12] session server REFACTOR move ifdefs --- src/session_server.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/session_server.c b/src/session_server.c index 601030c9..002ea9bb 100644 --- a/src/session_server.c +++ b/src/session_server.c @@ -57,6 +57,7 @@ struct nc_server_opts server_opts = { static nc_rpc_clb global_rpc_clb = NULL; +#ifdef NC_ENABLED_SSH_TLS /** * @brief Lock CH client structures for reading and lock the specific client. * @@ -107,6 +108,8 @@ nc_server_ch_client_unlock(struct nc_ch_client *client) pthread_rwlock_unlock(&server_opts.ch_client_lock); } +#endif /* NC_ENABLED_SSH_TLS */ + int nc_server_get_referenced_endpt(const char *name, struct nc_endpt **endpt) { @@ -2258,7 +2261,6 @@ nc_connect_ch_endpt(struct nc_ch_endpt *endpt, nc_server_ch_session_acquire_ctx_ (*session)->port = endpt->port; /* sock gets assigned to session or closed */ -#ifdef NC_ENABLED_SSH_TLS if (endpt->ti == NC_TI_LIBSSH) { ret = nc_accept_ssh_session(*session, endpt->opts.ssh, sock, NC_TRANSPORT_TIMEOUT); (*session)->data = NULL; @@ -2282,9 +2284,7 @@ nc_connect_ch_endpt(struct nc_ch_endpt *endpt, nc_server_ch_session_acquire_ctx_ msgtype = NC_MSG_WOULDBLOCK; goto fail; } - } else -#endif /* NC_ENABLED_SSH_TLS */ - { + } else { ERRINT; close(sock); msgtype = NC_MSG_ERROR; From 12d1053c0e59841658cac7f04363921d0055a538 Mon Sep 17 00:00:00 2001 From: roman Date: Fri, 24 Nov 2023 11:30:32 +0100 Subject: [PATCH 05/12] session server ssh REFACTOR remove redundant NLs --- src/session_server_ssh.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/session_server_ssh.c b/src/session_server_ssh.c index 39b3a82a..f79f7887 100644 --- a/src/session_server_ssh.c +++ b/src/session_server_ssh.c @@ -420,7 +420,7 @@ nc_pam_auth(struct nc_session *session, struct nc_auth_client *client, uint16_t /* initialize PAM and see if the given configuration file exists */ ret = pam_start(server_opts.pam_config_name, client->username, &conv, &pam_h); if (ret != PAM_SUCCESS) { - ERR(NULL, "PAM error occurred (%s).\n", pam_strerror(pam_h, ret)); + ERR(NULL, "PAM error occurred (%s).", pam_strerror(pam_h, ret)); goto cleanup; } @@ -428,10 +428,10 @@ nc_pam_auth(struct nc_session *session, struct nc_auth_client *client, uint16_t ret = pam_authenticate(pam_h, 0); if (ret != PAM_SUCCESS) { if (ret == PAM_ABORT) { - ERR(NULL, "PAM error occurred (%s).\n", pam_strerror(pam_h, ret)); + ERR(NULL, "PAM error occurred (%s).", pam_strerror(pam_h, ret)); goto cleanup; } else { - VRB(NULL, "PAM error occurred (%s).\n", pam_strerror(pam_h, ret)); + VRB(NULL, "PAM error occurred (%s).", pam_strerror(pam_h, ret)); goto cleanup; } } @@ -439,18 +439,18 @@ nc_pam_auth(struct nc_session *session, struct nc_auth_client *client, uint16_t /* correct token entered, check other requirements(the time of the day, expired token, ...) */ ret = pam_acct_mgmt(pam_h, 0); if ((ret != PAM_SUCCESS) && (ret != PAM_NEW_AUTHTOK_REQD)) { - VRB(NULL, "PAM error occurred (%s).\n", pam_strerror(pam_h, ret)); + VRB(NULL, "PAM error occurred (%s).", pam_strerror(pam_h, ret)); goto cleanup; } /* if a token has expired a new one will be generated */ if (ret == PAM_NEW_AUTHTOK_REQD) { - VRB(NULL, "PAM warning occurred (%s).\n", pam_strerror(pam_h, ret)); + VRB(NULL, "PAM warning occurred (%s).", pam_strerror(pam_h, ret)); ret = pam_chauthtok(pam_h, PAM_CHANGE_EXPIRED_AUTHTOK); if (ret == PAM_SUCCESS) { VRB(NULL, "The authentication token of user \"%s\" updated successfully.", client->username); } else { - ERR(NULL, "PAM error occurred (%s).\n", pam_strerror(pam_h, ret)); + ERR(NULL, "PAM error occurred (%s).", pam_strerror(pam_h, ret)); goto cleanup; } } @@ -458,7 +458,7 @@ nc_pam_auth(struct nc_session *session, struct nc_auth_client *client, uint16_t cleanup: /* destroy the PAM context */ if (pam_h && (pam_end(pam_h, ret) != PAM_SUCCESS)) { - ERR(NULL, "PAM error occurred (%s).\n", pam_strerror(pam_h, ret)); + ERR(NULL, "PAM error occurred (%s).", pam_strerror(pam_h, ret)); } return ret; } From 67b1c7737950d489f9e917269ed6d77f299f6290 Mon Sep 17 00:00:00 2001 From: roman Date: Thu, 30 Nov 2023 16:10:09 +0100 Subject: [PATCH 06/12] session server UPDATE add UNIX socket api Removed unix socket from configuration and moved it back to API. --- doc/libnetconf.doc | 3 + ...libnetconf2-netconf-server@2023-09-07.yang | 32 --- src/server_config.c | 196 +---------------- src/server_config.h | 17 -- src/server_config_util.c | 59 ------ src/session_p.h | 19 ++ src/session_server.c | 199 ++++++++++++++++++ src/session_server.h | 21 ++ tests/test_unix_socket.c | 10 +- 9 files changed, 247 insertions(+), 309 deletions(-) diff --git a/doc/libnetconf.doc b/doc/libnetconf.doc index c91b46fe..f756364a 100644 --- a/doc/libnetconf.doc +++ b/doc/libnetconf.doc @@ -307,6 +307,9 @@ * * - ::nc_server_set_capab_withdefaults() * - ::nc_server_set_capability() + * - ::nc_server_endpt_count() + * - ::nc_server_add_endpt_unix_socket_listen() + * - ::nc_server_del_endpt_unix_socket() * * Server Configuration * === diff --git a/modules/libnetconf2-netconf-server@2023-09-07.yang b/modules/libnetconf2-netconf-server@2023-09-07.yang index bbf57113..8d04b3a3 100644 --- a/modules/libnetconf2-netconf-server@2023-09-07.yang +++ b/modules/libnetconf2-netconf-server@2023-09-07.yang @@ -375,38 +375,6 @@ module libnetconf2-netconf-server { uses keyboard-interactive-grouping; } - augment "/ncs:netconf-server/ncs:listen/ncs:endpoint/ncs:transport" { - case unix-socket { - container unix-socket { - description - "UNIX socket listening configuration for inbound connections."; - leaf path { - type string; - mandatory true; - description - "Path to the socket on which the communication will occur."; - } - leaf mode { - type string { - pattern '[0124567]{3}'; - } - description - "Mode of the socket."; - } - leaf uid { - type uint16; - description - "User ID of the socket."; - } - leaf gid { - type uint16; - description - "Group ID of the socket."; - } - } - } - } - augment "/ncs:netconf-server/ncs:listen/ncs:endpoint/ncs:transport/ncs:ssh" + "/ncs:ssh/ncs:ssh-server-parameters/ncs:client-authentication" { uses endpoint-reference-grouping; diff --git a/src/server_config.c b/src/server_config.c index 04281ae8..781afb4c 100644 --- a/src/server_config.c +++ b/src/server_config.c @@ -770,42 +770,6 @@ nc_server_config_del_endpt_ssh(struct nc_endpt *endpt, struct nc_bind *bind) } } -#endif /* NC_ENABLED_SSH_TLS */ - -void -nc_server_config_del_unix_socket_opts(struct nc_bind *bind, struct nc_server_unix_opts *opts) -{ - if (bind->sock > -1) { - close(bind->sock); - } - - unlink(bind->address); - free(bind->address); - free(opts->address); - - free(opts); -} - -void -nc_server_config_del_endpt_unix_socket(struct nc_endpt *endpt, struct nc_bind *bind) -{ - free(endpt->name); - nc_server_config_del_unix_socket_opts(bind, endpt->opts.unixsock); - - server_opts.endpt_count--; - if (!server_opts.endpt_count) { - free(server_opts.endpts); - free(server_opts.binds); - server_opts.endpts = NULL; - server_opts.binds = NULL; - } else if (endpt != &server_opts.endpts[server_opts.endpt_count]) { - memcpy(endpt, &server_opts.endpts[server_opts.endpt_count], sizeof *server_opts.endpts); - memcpy(bind, &server_opts.binds[server_opts.endpt_count], sizeof *server_opts.binds); - } -} - -#ifdef NC_ENABLED_SSH_TLS - static void nc_server_config_del_cert(struct nc_cert_grouping *certs, struct nc_certificate *cert) { @@ -1060,7 +1024,7 @@ nc_server_config_listen(const struct lyd_node *node, NC_OPERATION op) break; #endif /* NC_ENABLED_SSH_TLS */ case NC_TI_UNIX: - nc_server_config_del_endpt_unix_socket(&server_opts.endpts[i], &server_opts.binds[i]); + _nc_server_del_endpt_unix_socket(&server_opts.endpts[i], &server_opts.binds[i]); break; case NC_TI_NONE: case NC_TI_FD: @@ -1234,7 +1198,7 @@ nc_server_config_endpoint(const struct lyd_node *node, NC_OPERATION op) break; #endif /* NC_ENABLED_SSH_TLS */ case NC_TI_UNIX: - nc_server_config_del_endpt_unix_socket(endpt, bind); + _nc_server_del_endpt_unix_socket(endpt, bind); break; case NC_TI_NONE: case NC_TI_FD: @@ -1429,73 +1393,6 @@ nc_server_config_tls(const struct lyd_node *node, NC_OPERATION op) return ret; } -#endif /* NC_ENABLED_SSH_TLS */ - -static int -nc_server_config_set_address_port(struct nc_endpt *endpt, struct nc_bind *bind, const char *address, uint16_t port) -{ - int sock = -1, set_addr, ret = 0; - - assert((address && !port) || (!address && port) || (endpt->ti == NC_TI_UNIX)); - - if (address) { - set_addr = 1; - } else { - set_addr = 0; - } - - if (set_addr) { - port = bind->port; - } else { - address = bind->address; - } - - /* we have all the information we need to create a listening socket */ - if ((address && port) || (endpt->ti == NC_TI_UNIX)) { - /* create new socket, close the old one */ - if (endpt->ti == NC_TI_UNIX) { - sock = nc_sock_listen_unix(endpt->opts.unixsock); - } else { - sock = nc_sock_listen_inet(address, port, &endpt->ka); - } - - if (sock == -1) { - ret = 1; - goto cleanup; - } - - if (bind->sock > -1) { - close(bind->sock); - } - bind->sock = sock; - } - - if (sock > -1) { - switch (endpt->ti) { - case NC_TI_UNIX: - VRB(NULL, "Listening on %s for UNIX connections.", endpt->opts.unixsock->address); - break; -#ifdef NC_ENABLED_SSH_TLS - case NC_TI_LIBSSH: - VRB(NULL, "Listening on %s:%u for SSH connections.", address, port); - break; - case NC_TI_OPENSSL: - VRB(NULL, "Listening on %s:%u for TLS connections.", address, port); - break; -#endif /* NC_ENABLED_SSH_TLS */ - default: - ERRINT; - ret = 1; - break; - } - } - -cleanup: - return ret; -} - -#ifdef NC_ENABLED_SSH_TLS - /* mandatory leaf */ static int nc_server_config_local_address(const struct lyd_node *node, NC_OPERATION op) @@ -1518,7 +1415,7 @@ nc_server_config_local_address(const struct lyd_node *node, NC_OPERATION op) bind->address = strdup(lyd_get_value(node)); NC_CHECK_ERRMEM_GOTO(!bind->address, ret = 1, cleanup); - ret = nc_server_config_set_address_port(endpt, bind, lyd_get_value(node), 0); + ret = nc_server_set_address_port(endpt, bind, lyd_get_value(node), 0); if (ret) { goto cleanup; } @@ -1551,7 +1448,7 @@ nc_server_config_local_port(const struct lyd_node *node, NC_OPERATION op) bind->port = 0; } - ret = nc_server_config_set_address_port(endpt, bind, NULL, bind->port); + ret = nc_server_set_address_port(endpt, bind, NULL, bind->port); if (ret) { goto cleanup; } @@ -2790,89 +2687,6 @@ nc_server_config_mac_alg(const struct lyd_node *node, NC_OPERATION op) return ret; } -#endif /* NC_ENABLED_SSH_TLS */ - -static int -nc_server_config_create_unix_socket(struct nc_endpt *endpt) -{ - endpt->ti = NC_TI_UNIX; - endpt->opts.unixsock = calloc(1, sizeof *endpt->opts.unixsock); - NC_CHECK_ERRMEM_RET(!endpt->opts.unixsock, 1); - - /* set default values */ - endpt->opts.unixsock->mode = -1; - endpt->opts.unixsock->uid = -1; - endpt->opts.unixsock->gid = -1; - - return 0; -} - -static int -nc_server_config_unix_socket(const struct lyd_node *node, NC_OPERATION op) -{ - int ret = 0; - uint32_t log_options = 0; - struct nc_endpt *endpt; - struct nc_bind *bind; - struct nc_server_unix_opts *opts; - struct lyd_node *data = NULL; - - assert(!strcmp(LYD_NAME(node), "unix-socket")); - - if (nc_server_config_get_endpt(node, &endpt, &bind)) { - ret = 1; - goto cleanup; - } - - if (op == NC_OP_CREATE) { - if (nc_server_config_create_unix_socket(endpt)) { - ret = 1; - goto cleanup; - } - - opts = endpt->opts.unixsock; - - lyd_find_path(node, "path", 0, &data); - assert(data); - - opts->address = strdup(lyd_get_value(data)); - bind->address = strdup(lyd_get_value(data)); - NC_CHECK_ERRMEM_GOTO(!opts->address || !bind->address, ret = 1, cleanup); - - /* silently search for non-mandatory parameters */ - ly_temp_log_options(&log_options); - ret = lyd_find_path(node, "mode", 0, &data); - if (!ret) { - opts->mode = strtol(lyd_get_value(data), NULL, 8); - } - - ret = lyd_find_path(node, "uid", 0, &data); - if (!ret) { - opts->uid = strtol(lyd_get_value(data), NULL, 10); - } - - ret = lyd_find_path(node, "gid", 0, &data); - if (!ret) { - opts->gid = strtol(lyd_get_value(data), NULL, 10); - } - - /* reset the logging options */ - ly_temp_log_options(NULL); - - ret = nc_server_config_set_address_port(endpt, bind, NULL, 0); - if (ret) { - goto cleanup; - } - } else if (op == NC_OP_DELETE) { - nc_server_config_del_unix_socket_opts(bind, endpt->opts.unixsock); - } - -cleanup: - return ret; -} - -#ifdef NC_ENABLED_SSH_TLS - static int nc_server_config_check_endpt_reference_cycle(struct nc_endpt *original, struct nc_endpt *next) { @@ -4063,8 +3877,6 @@ nc_server_config_parse_netconf_server(const struct lyd_node *node, NC_OPERATION ret = nc_server_config_hello_timeout(node, op); } else if (!strcmp(name, "endpoint")) { ret = nc_server_config_endpoint(node, op); - } else if (!strcmp(name, "unix-socket")) { - ret = nc_server_config_unix_socket(node, op); } #ifdef NC_ENABLED_SSH_TLS else if (!strcmp(name, "ssh")) { diff --git a/src/server_config.h b/src/server_config.h index f8fb973f..32f9805e 100644 --- a/src/server_config.h +++ b/src/server_config.h @@ -122,23 +122,6 @@ int nc_server_config_add_address_port(const struct ly_ctx *ctx, const char *endp #endif /* NC_ENABLED_SSH_TLS */ -/** - * @brief Creates new YANG data nodes for a UNIX socket. - * - * @param[in] ctx libyang context. - * @param[in] endpt_name Arbitrary identifier of the endpoint. - * If an endpoint with this identifier already exists, its contents might be changed. - * @param[in] path Path to the socket. - * @param[in] mode New mode, use -1 for default. - * @param[in] uid New uid, use -1 for default - * @param[in] gid New gid, use -1 for default - * @param[in,out] config Configuration YANG data tree. If *config is NULL, it will be created. - * Otherwise the new YANG data will be added to the previous data and may override it. - * @return 0 on success, non-zero otherwise. - */ -int nc_server_config_add_unix_socket(const struct ly_ctx *ctx, const char *endpt_name, const char *path, - mode_t mode, uid_t uid, gid_t gid, struct lyd_node **config); - /** * @brief Deletes an endpoint from the YANG data. * diff --git a/src/server_config_util.c b/src/server_config_util.c index 2b0c53c3..ef98b3d8 100644 --- a/src/server_config_util.c +++ b/src/server_config_util.c @@ -1325,65 +1325,6 @@ nc_server_config_del_truststore_cert(const char *cert_bag_name, #endif /* NC_ENABLED_SSH_TLS */ -API int -nc_server_config_add_unix_socket(const struct ly_ctx *ctx, const char *endpt_name, const char *path, - mode_t mode, uid_t uid, gid_t gid, struct lyd_node **config) -{ - int ret = 0; - char *tree_path = NULL; - char buf[12] = {0}; - - NC_CHECK_ARG_RET(NULL, ctx, endpt_name, path, config, 1); - - ret = asprintf(&tree_path, "/ietf-netconf-server:netconf-server/listen/endpoint[name='%s']/libnetconf2-netconf-server:unix-socket", endpt_name); - NC_CHECK_ERRMEM_GOTO(ret == -1, tree_path = NULL; ret = 1, cleanup); - - /* path to unix socket */ - ret = nc_server_config_append(ctx, tree_path, "path", path, config); - if (ret) { - goto cleanup; - } - - /* mode */ - if (mode != (mode_t)-1) { - if (mode > 0777) { - ERR(NULL, "Invalid mode value (%o).", mode); - ret = 1; - goto cleanup; - } - - sprintf(buf, "%o", mode); - ret = nc_server_config_append(ctx, tree_path, "mode", buf, config); - if (ret) { - goto cleanup; - } - } - - /* uid */ - if (uid != (uid_t)-1) { - memset(buf, 0, 12); - sprintf(buf, "%u", uid); - ret = nc_server_config_append(ctx, tree_path, "uid", buf, config); - if (ret) { - goto cleanup; - } - } - - /* gid */ - if (gid != (gid_t)-1) { - memset(buf, 0, 12); - sprintf(buf, "%u", gid); - ret = nc_server_config_append(ctx, tree_path, "gid", buf, config); - if (ret) { - goto cleanup; - } - } - -cleanup: - free(tree_path); - return ret; -} - API int nc_server_config_add_ch_persistent(const struct ly_ctx *ctx, const char *ch_client_name, struct lyd_node **config) { diff --git a/src/session_p.h b/src/session_p.h index fc5e713b..3c50d87c 100644 --- a/src/session_p.h +++ b/src/session_p.h @@ -769,6 +769,25 @@ int nc_is_pk_subject_public_key_info(const char *b64); void *nc_realloc(void *ptr, size_t size); +/** + * @brief Set the andress and port of an endpoint. + * + * @param[in] endpt Endpoint to set the address/port for. + * @param[in] bind Bind to set the address/port for. + * @param[in] address Address to set, can be a path to a UNIX socket. + * @param[in] port Port to set, invalid for UNIX socket endpoint. + * @return 0 on success, 1 on error. + */ +int nc_server_set_address_port(struct nc_endpt *endpt, struct nc_bind *bind, const char *address, uint16_t port); + +/** + * @brief Frees memory allocated by a UNIX socket endpoint. + * + * @param[in] endpt UNIX socket endpoint. + * @param[in] bind UNIX socket bind. + */ +void _nc_server_del_endpt_unix_socket(struct nc_endpt *endpt, struct nc_bind *bind); + struct passwd *nc_getpw(uid_t uid, const char *username, struct passwd *pwd_buf, char **buf, size_t *buf_size); NC_MSG_TYPE nc_send_msg_io(struct nc_session *session, int io_timeout, struct lyd_node *op); diff --git a/src/session_server.c b/src/session_server.c index 002ea9bb..1967c79e 100644 --- a/src/session_server.c +++ b/src/session_server.c @@ -1950,6 +1950,69 @@ nc_ps_clear(struct nc_pollsession *ps, int all, void (*data_free)(void *)) nc_ps_unlock(ps, q_id, __func__); } +int +nc_server_set_address_port(struct nc_endpt *endpt, struct nc_bind *bind, const char *address, uint16_t port) +{ + int sock = -1, set_addr, ret = 0; + + assert((address && !port) || (!address && port) || (endpt->ti == NC_TI_UNIX)); + + if (address) { + set_addr = 1; + } else { + set_addr = 0; + } + + if (set_addr) { + port = bind->port; + } else { + address = bind->address; + } + + /* we have all the information we need to create a listening socket */ + if ((address && port) || (endpt->ti == NC_TI_UNIX)) { + /* create new socket, close the old one */ + if (endpt->ti == NC_TI_UNIX) { + sock = nc_sock_listen_unix(endpt->opts.unixsock); + } else { + sock = nc_sock_listen_inet(address, port, &endpt->ka); + } + + if (sock == -1) { + ret = 1; + goto cleanup; + } + + if (bind->sock > -1) { + close(bind->sock); + } + bind->sock = sock; + } + + if (sock > -1) { + switch (endpt->ti) { + case NC_TI_UNIX: + VRB(NULL, "Listening on %s for UNIX connections.", endpt->opts.unixsock->address); + break; +#ifdef NC_ENABLED_SSH_TLS + case NC_TI_LIBSSH: + VRB(NULL, "Listening on %s:%u for SSH connections.", address, port); + break; + case NC_TI_OPENSSL: + VRB(NULL, "Listening on %s:%u for TLS connections.", address, port); + break; +#endif /* NC_ENABLED_SSH_TLS */ + default: + ERRINT; + ret = 1; + break; + } + } + +cleanup: + return ret; +} + /** * @brief Get UID of the owner of a socket. * @@ -2024,6 +2087,142 @@ nc_accept_unix(struct nc_session *session, int sock) #endif } +API int +nc_server_add_endpt_unix_socket_listen(const char *endpt_name, const char *unix_socket_path, mode_t mode, uid_t uid, gid_t gid) +{ + int ret = 0; + void *tmp; + uint16_t i; + + NC_CHECK_ARG_RET(NULL, endpt_name, unix_socket_path, 1); + + /* CONFIG LOCK */ + pthread_rwlock_wrlock(&server_opts.config_lock); + + /* check name uniqueness */ + for (i = 0; i < server_opts.endpt_count; i++) { + if (!strcmp(endpt_name, server_opts.endpts[i].name)) { + ERR(NULL, "Endpoint \"%s\" already exists.", endpt_name); + ret = 1; + goto cleanup; + } + } + + /* alloc a new endpoint */ + tmp = nc_realloc(server_opts.endpts, (server_opts.endpt_count + 1) * sizeof *server_opts.endpts); + NC_CHECK_ERRMEM_GOTO(!tmp, ret = 1, cleanup); + server_opts.endpts = tmp; + memset(&server_opts.endpts[server_opts.endpt_count], 0, sizeof *server_opts.endpts); + + /* alloc a new bind */ + tmp = nc_realloc(server_opts.binds, (server_opts.endpt_count + 1) * sizeof *server_opts.binds); + NC_CHECK_ERRMEM_GOTO(!tmp, ret = 1, cleanup); + server_opts.binds = tmp; + memset(&server_opts.binds[server_opts.endpt_count], 0, sizeof *server_opts.binds); + server_opts.binds[server_opts.endpt_count].sock = -1; + server_opts.endpt_count++; + + /* set name and ti */ + server_opts.endpts[server_opts.endpt_count - 1].name = strdup(endpt_name); + NC_CHECK_ERRMEM_GOTO(!server_opts.endpts[server_opts.endpt_count - 1].name, ret = 1, cleanup); + server_opts.endpts[server_opts.endpt_count - 1].ti = NC_TI_UNIX; + + /* set the bind data */ + server_opts.binds[server_opts.endpt_count - 1].address = strdup(unix_socket_path); + NC_CHECK_ERRMEM_GOTO(!server_opts.binds[server_opts.endpt_count - 1].address, ret = 1, cleanup); + + /* alloc unix opts */ + server_opts.endpts[server_opts.endpt_count - 1].opts.unixsock = calloc(1, sizeof(struct nc_server_unix_opts)); + NC_CHECK_ERRMEM_GOTO(!server_opts.endpts[server_opts.endpt_count - 1].opts.unixsock, ret = 1, cleanup); + + /* set the opts data */ + server_opts.endpts[server_opts.endpt_count - 1].opts.unixsock->address = strdup(unix_socket_path); + NC_CHECK_ERRMEM_GOTO(!server_opts.endpts[server_opts.endpt_count - 1].opts.unixsock->address, ret = 1, cleanup); + server_opts.endpts[server_opts.endpt_count - 1].opts.unixsock->mode = (mode == (mode_t) -1) ? (mode_t) -1 : mode; + server_opts.endpts[server_opts.endpt_count - 1].opts.unixsock->uid = (uid == (uid_t) -1) ? (uid_t) -1 : uid; + server_opts.endpts[server_opts.endpt_count - 1].opts.unixsock->gid = (gid == (gid_t) -1) ? (gid_t) -1 : gid; + + /* start listening */ + ret = nc_server_set_address_port(&server_opts.endpts[server_opts.endpt_count - 1], + &server_opts.binds[server_opts.endpt_count - 1], NULL, 0); + if (ret) { + ERR(NULL, "Listening on UNIX socket \"%s\" failed.", unix_socket_path); + goto cleanup; + } + +cleanup: + /* CONFIG UNLOCK */ + pthread_rwlock_unlock(&server_opts.config_lock); + return ret; +} + +static void +nc_server_del_endpt_unix_socket_opts(struct nc_bind *bind, struct nc_server_unix_opts *opts) +{ + if (bind->sock > -1) { + close(bind->sock); + } + + unlink(bind->address); + free(bind->address); + free(opts->address); + + free(opts); +} + +void +_nc_server_del_endpt_unix_socket(struct nc_endpt *endpt, struct nc_bind *bind) +{ + free(endpt->name); + nc_server_del_endpt_unix_socket_opts(bind, endpt->opts.unixsock); + + server_opts.endpt_count--; + if (!server_opts.endpt_count) { + free(server_opts.endpts); + free(server_opts.binds); + server_opts.endpts = NULL; + server_opts.binds = NULL; + } else if (endpt != &server_opts.endpts[server_opts.endpt_count]) { + memcpy(endpt, &server_opts.endpts[server_opts.endpt_count], sizeof *server_opts.endpts); + memcpy(bind, &server_opts.binds[server_opts.endpt_count], sizeof *server_opts.binds); + } +} + +API void +nc_server_del_endpt_unix_socket(const char *endpt_name) +{ + uint16_t i; + struct nc_endpt *endpt = NULL; + struct nc_bind *bind; + + /* CONFIG LOCK */ + pthread_rwlock_wrlock(&server_opts.config_lock); + + NC_CHECK_ARG_RET(NULL, endpt_name, ); + + for (i = 0; i < server_opts.endpt_count; i++) { + if (!strcmp(server_opts.endpts[i].name, endpt_name)) { + endpt = &server_opts.endpts[i]; + bind = &server_opts.binds[i]; + break; + } + } + if (!endpt) { + ERR(NULL, "Endpoint \"%s\" not found.", endpt_name); + goto end; + } + if (endpt->ti != NC_TI_UNIX) { + ERR(NULL, "Endpoint \"%s\" is not a UNIX socket endpoint.", endpt_name); + goto end; + } + + _nc_server_del_endpt_unix_socket(endpt, bind); + +end: + /* CONFIG UNLOCK */ + pthread_rwlock_unlock(&server_opts.config_lock); +} + API int nc_server_endpt_count(void) { diff --git a/src/session_server.h b/src/session_server.h index 3083562d..5e7d13c3 100644 --- a/src/session_server.h +++ b/src/session_server.h @@ -378,6 +378,27 @@ void nc_ps_clear(struct nc_pollsession *ps, int all, void (*data_free)(void *)); */ int nc_server_endpt_count(void); +/** + * @brief Create a new UNIX socket endpoint and start listening. + * + * @param[in] endpt_name Arbitrary unique identifier of the endpoint. + * @param[in] unix_socket_path Path to the listening socket. + * @param[in] mode New mode, -1 to use default. + * @param[in] uid New uid, -1 to use default. + * @param[in] gid New gid, -1 to use default. + * + * @return 0 on success, 1 on error. + */ +int nc_server_add_endpt_unix_socket_listen(const char *endpt_name, const char *unix_socket_path, mode_t mode, uid_t uid, gid_t gid); + +/** + * @brief Deletes a UNIX socket endpoint. + * + * @param[in] endpt_name Identifier of the endpoint. + * Has no effect if the endpoint doesn't exist or if its transport is not UNIX socket. + */ +void nc_server_del_endpt_unix_socket(const char *endpt_name); + /** @} */ /** diff --git a/tests/test_unix_socket.c b/tests/test_unix_socket.c index 351a6e23..a6c81d73 100644 --- a/tests/test_unix_socket.c +++ b/tests/test_unix_socket.c @@ -127,16 +127,8 @@ setup_f(void **state) ret = nc_server_init_ctx(&ctx); assert_int_equal(ret, 0); - /* load ietf-netconf-server module and it's dependencies into context */ - ret = nc_server_config_load_modules(&ctx); - assert_int_equal(ret, 0); - /* create the UNIX socket */ - ret = nc_server_config_add_unix_socket(ctx, "unix", "/tmp/nc2_test_unix_sock", 0700, -1, -1, &tree); - assert_int_equal(ret, 0); - - /* configure the server based on the data */ - ret = nc_server_config_setup_data(tree); + ret = nc_server_add_endpt_unix_socket_listen("unix", "/tmp/nc2_test_unix_sock", 0700, -1, -1); assert_int_equal(ret, 0); /* initialize server */ From 5da1e214aaad9ed126b78a88b10253c5ea7d5857 Mon Sep 17 00:00:00 2001 From: roman Date: Thu, 30 Nov 2023 16:39:00 +0100 Subject: [PATCH 07/12] session server UPDATE add system authentication --- CMakeLists.txt | 3 + ...libnetconf2-netconf-server@2023-09-07.yang | 17 +- src/server_config.c | 8 +- src/server_config.h | 21 +- src/server_config_util_ssh.c | 10 +- src/session_server_ssh.c | 324 +++++++++++++++--- 6 files changed, 304 insertions(+), 79 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 53c4724f..b028d5e5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -278,6 +278,9 @@ find_package(LibYANG ${LIBYANG_DEP_SOVERSION} REQUIRED) target_link_libraries(netconf2 ${LIBYANG_LIBRARIES}) include_directories(${LIBYANG_INCLUDE_DIRS}) +# header file compatibility - shadow.h +check_include_file("shadow.h" HAVE_SHADOW) + # function compatibility - getpeereid on QNX if(${CMAKE_SYSTEM_NAME} MATCHES "QNX") target_link_libraries(netconf2 -lsocket) diff --git a/modules/libnetconf2-netconf-server@2023-09-07.yang b/modules/libnetconf2-netconf-server@2023-09-07.yang index 8d04b3a3..cffad1c2 100644 --- a/modules/libnetconf2-netconf-server@2023-09-07.yang +++ b/modules/libnetconf2-netconf-server@2023-09-07.yang @@ -265,8 +265,7 @@ module libnetconf2-netconf-server { "Grouping for the SSH Keyboard interactive authentication method."; container keyboard-interactive { - presence "Indicates that PAM configuration file name has been configured and that - the given client supportsthe SSH Keyboard Interactive authentication method."; + presence "Indicates that the given client supports the SSH Keyboard Interactive authentication method."; description "Keyboard interactive SSH authentication method."; @@ -274,6 +273,20 @@ module libnetconf2-netconf-server { "RFC 4256: Generic Message Exchange Authentication for the Secure Shell Protocol (SSH)"; + + choice method { + mandatory true; + description + "Method to perform the authentication with."; + + container use-system-auth { + presence + "Indicates that the system will handle the authentication."; + + description + "Authentication is done using the system's mechanisms."; + } + } } } diff --git a/src/server_config.c b/src/server_config.c index 781afb4c..ce8cb102 100644 --- a/src/server_config.c +++ b/src/server_config.c @@ -2344,13 +2344,13 @@ nc_server_config_password(const struct lyd_node *node, NC_OPERATION op) } static int -nc_server_config_kb_int(const struct lyd_node *node, NC_OPERATION op) +nc_server_config_use_system_auth(const struct lyd_node *node, NC_OPERATION op) { int ret = 0; struct nc_auth_client *auth_client; struct nc_ch_client *ch_client = NULL; - assert(!strcmp(LYD_NAME(node), "keyboard-interactive")); + assert(!strcmp(LYD_NAME(node), "use-system-auth")); /* LOCK */ if (is_ch(node) && nc_server_config_get_ch_client_with_lock(node, &ch_client)) { @@ -3915,8 +3915,8 @@ nc_server_config_parse_netconf_server(const struct lyd_node *node, NC_OPERATION ret = nc_server_config_truststore_reference(node, op); } else if (!strcmp(name, "password")) { ret = nc_server_config_password(node, op); - } else if (!strcmp(name, "keyboard-interactive")) { - ret = nc_server_config_kb_int(node, op); + } else if (!strcmp(name, "use-system-auth")) { + ret = nc_server_config_use_system_auth(node, op); } else if (!strcmp(name, "none")) { ret = nc_server_config_none(node, op); } else if (!strcmp(name, "host-key-alg")) { diff --git a/src/server_config.h b/src/server_config.h index 32f9805e..07584d2c 100644 --- a/src/server_config.h +++ b/src/server_config.h @@ -383,24 +383,7 @@ int nc_server_config_del_ssh_user_password(const char *endpt_name, const char *u /** * @brief Creates new YANG configuration data nodes for an SSH user's keyboard interactive authentication method. * - * @param[in] ctx libyang context. - * @param[in] endpt_name Arbitrary identifier of the endpoint. - * If an endpoint with this identifier already exists, its user might be changed. - * @param[in] user_name Arbitrary identifier of the user. - * If an user with this identifier already exists, its contents will be changed. - * @param[in] pam_config_name Name of the PAM configuration file. - * @param[in] pam_config_dir Optional. The absolute path to the directory in which the configuration file - * with the name pam_config_name is located. A newer version (>= 1.4) of PAM library is required to be able to specify - * the path. If NULL is passed, then the PAM's system directories will be searched (usually /etc/pam.d/). - * @param[in,out] config Configuration YANG data tree. If *config is NULL, it will be created. - * Otherwise the new YANG data will be added to the previous data and may override it. - * @return 0 on success, non-zero otherwise. - */ - -/** - * @brief Creates new YANG configuration data nodes for an SSH user's keyboard interactive authentication method. - * - * To set the PAM configuration filename, see ::nc_server_ssh_set_pam_conf_filename(). + * One of Linux PAM, local users, or user callback is used to authenticate users with this SSH method (see \ref ln2doc_kbdint "the documentation"). * * @param[in] ctx libyang context. * @param[in] endpt_name Arbitrary identifier of the endpoint. @@ -1043,7 +1026,7 @@ int nc_server_config_del_ch_ssh_user_password(const char *client_name, const cha /** * @brief Creates new YANG configuration data nodes for a Call Home SSH user's keyboard interactive authentication method. * - * To set the PAM configuration filename, see ::nc_server_ssh_set_pam_conf_filename(). + * One of Linux PAM, local users, or user callback is used to authenticate users with this SSH method (see \ref ln2doc_kbdint "the documentation"). * * @param[in] ctx libyang context. * @param[in] client_name Arbitrary identifier of the Call Home client. diff --git a/src/server_config_util_ssh.c b/src/server_config_util_ssh.c index 166a9012..88185845 100644 --- a/src/server_config_util_ssh.c +++ b/src/server_config_util_ssh.c @@ -484,10 +484,10 @@ nc_server_config_add_ssh_user_interactive(const struct ly_ctx *ctx, const char * NC_CHECK_ARG_RET(NULL, ctx, endpt_name, user_name, config, 1); ret = asprintf(&path, "/ietf-netconf-server:netconf-server/listen/endpoint[name='%s']/ssh/ssh-server-parameters/" - "client-authentication/users/user[name='%s']", endpt_name, user_name); + "client-authentication/users/user[name='%s']/libnetconf2-netconf-server:keyboard-interactive", endpt_name, user_name); NC_CHECK_ERRMEM_GOTO(ret == -1, path = NULL; ret = 1, cleanup); - ret = nc_server_config_append(ctx, path, "libnetconf2-netconf-server:keyboard-interactive", NULL, config); + ret = nc_server_config_append(ctx, path, "use-system-auth", NULL, config); if (ret) { goto cleanup; } @@ -507,11 +507,11 @@ nc_server_config_add_ch_ssh_user_interactive(const struct ly_ctx *ctx, const cha NC_CHECK_ARG_RET(NULL, ctx, client_name, endpt_name, user_name, config, 1); ret = asprintf(&path, "/ietf-netconf-server:netconf-server/call-home/netconf-client[name='%s']/endpoints/" - "endpoint[name='%s']/ssh/ssh-server-parameters/client-authentication/users/user[name='%s']", - client_name, endpt_name, user_name); + "endpoint[name='%s']/ssh/ssh-server-parameters/client-authentication/users/user[name='%s']/" + "libnetconf2-netconf-server:keyboard-interactive", client_name, endpt_name, user_name); NC_CHECK_ERRMEM_GOTO(ret == -1, path = NULL; ret = 1, cleanup); - ret = nc_server_config_append(ctx, path, "libnetconf2-netconf-server:keyboard-interactive", NULL, config); + ret = nc_server_config_append(ctx, path, "use-system-auth", NULL, config); if (ret) { goto cleanup; } diff --git a/src/session_server_ssh.c b/src/session_server_ssh.c index f79f7887..91fb17a7 100644 --- a/src/session_server_ssh.c +++ b/src/session_server_ssh.c @@ -15,11 +15,14 @@ #define _GNU_SOURCE -#include "config.h" /* Expose HAVE_LIBPAM */ +#include "config.h" /* Expose HAVE_LIBPAM and HAVE_SHADOW */ #ifdef HAVE_LIBPAM # include #endif +#ifdef HAVE_SHADOW +# include +#endif #include #include @@ -231,6 +234,46 @@ nc_sshcb_auth_password(struct nc_session *session, struct nc_auth_client *auth_c return auth_ret; } +/* get answers to kbdint prompts on the given libssh session and return the number of them, -1 on timeout/dc */ +static int +nc_server_ssh_kbdint_get_nanswers(struct nc_session *session, ssh_session libssh_session, uint16_t auth_timeout) +{ + int ret = 0; + struct timespec ts_timeout = {0}; + ssh_message reply; + + if (auth_timeout) { + nc_timeouttime_get(&ts_timeout, auth_timeout * 1000); + } + + /* wait for answers from the client */ + do { + if (!nc_session_is_connected(session)) { + ERR(NULL, "SSH communication socket unexpectedly closed."); + ret = -1; + goto cleanup; + } + + reply = ssh_message_get(libssh_session); + if (reply) { + break; + } + + usleep(NC_TIMEOUT_STEP); + } while (auth_timeout && (nc_timeouttime_cur_diff(&ts_timeout) >= 1)); + if (!reply) { + ERR(NULL, "Authentication timeout."); + ret = -1; + goto cleanup; + } + + ret = ssh_userauth_kbdint_getnanswers(libssh_session); + +cleanup: + ssh_message_free(reply); + return ret; +} + #ifdef HAVE_LIBPAM /** @@ -253,7 +296,6 @@ nc_pam_conv_clb(int n_messages, const struct pam_message **msg, struct pam_respo ssh_message reply = NULL; struct nc_pam_thread_arg *clb_data = appdata_ptr; ssh_session libssh_session; - struct timespec ts_timeout; uint16_t auth_timeout; libssh_session = clb_data->session->ti.libssh.session; @@ -261,7 +303,7 @@ nc_pam_conv_clb(int n_messages, const struct pam_message **msg, struct pam_respo /* PAM_MAX_NUM_MSG == 32 by default */ if ((n_messages <= 0) || (n_messages >= PAM_MAX_NUM_MSG)) { - ERR(NULL, "Bad number of PAM messages (#%d).", n_messages); + ERR(clb_data->session, "Bad number of PAM messages (#%d).", n_messages); r = PAM_CONV_ERR; goto cleanup; } @@ -270,7 +312,7 @@ nc_pam_conv_clb(int n_messages, const struct pam_message **msg, struct pam_respo for (i = 0; i < n_messages; i++) { t = msg[i]->msg_style; if ((t != PAM_PROMPT_ECHO_OFF) && (t != PAM_PROMPT_ECHO_ON) && (t != PAM_TEXT_INFO) && (t != PAM_ERROR_MSG)) { - ERR(NULL, "PAM conversation callback received an unexpected type of message."); + ERR(clb_data->session, "PAM conversation callback received an unexpected type of message."); r = PAM_CONV_ERR; goto cleanup; } @@ -279,11 +321,11 @@ nc_pam_conv_clb(int n_messages, const struct pam_message **msg, struct pam_respo /* display messages with errors and/or some information and count the amount of actual authentication challenges */ for (i = 0; i < n_messages; i++) { if (msg[i]->msg_style == PAM_TEXT_INFO) { - VRB(NULL, "PAM conversation callback received a message with some information for the client (%s).", msg[i]->msg); + VRB(clb_data->session, "PAM conversation callback received a message with some information for the client (%s).", msg[i]->msg); n_requests--; } if (msg[i]->msg_style == PAM_ERROR_MSG) { - ERR(NULL, "PAM conversation callback received an error message (%s).", msg[i]->msg); + ERR(clb_data->session, "PAM conversation callback received an error message (%s).", msg[i]->msg); r = PAM_CONV_ERR; goto cleanup; } @@ -324,41 +366,19 @@ nc_pam_conv_clb(int n_messages, const struct pam_message **msg, struct pam_respo /* print all the keyboard-interactive challenges to the user */ r = ssh_message_auth_interactive_request(clb_data->msg, name, instruction, n_requests, prompts, echo); if (r != SSH_OK) { - ERR(NULL, "Failed to send an authentication request."); + ERR(clb_data->session, "Failed to send an authentication request."); r = PAM_CONV_ERR; goto cleanup; } - if (auth_timeout) { - nc_timeouttime_get(&ts_timeout, auth_timeout * 1000); - } - - /* get user's replies */ - do { - if (!nc_session_is_connected(clb_data->session)) { - ERR(NULL, "Communication SSH socket unexpectedly closed."); - r = PAM_CONV_ERR; - goto cleanup; - } - - reply = ssh_message_get(libssh_session); - if (reply) { - break; - } - - usleep(NC_TIMEOUT_STEP); - } while (auth_timeout && (nc_timeouttime_cur_diff(&ts_timeout) >= 1)); - - if (!reply) { - ERR(NULL, "Authentication timeout."); + n_answers = nc_server_ssh_kbdint_get_nanswers(clb_data->session, libssh_session, auth_timeout); + if (n_answers < 0) { + /* timeout or dc */ r = PAM_CONV_ERR; goto cleanup; - } - - /* check if the amount of replies matches the amount of requests */ - n_answers = ssh_userauth_kbdint_getnanswers(libssh_session); - if (n_answers != n_requests) { - ERR(NULL, "Expected %d response(s), got %d.", n_requests, n_answers); + } else if (n_answers != n_requests) { + /* check if the number of answers and requests matches */ + ERR(clb_data->session, "Expected %d response(s), got %d.", n_requests, n_answers); r = PAM_CONV_ERR; goto cleanup; } @@ -412,7 +432,7 @@ nc_pam_auth(struct nc_session *session, struct nc_auth_client *client, uint16_t conv.appdata_ptr = &clb_data; if (!server_opts.pam_config_name) { - ERR(NULL, "PAM configuration filename not set."); + ERR(session, "PAM configuration filename not set."); ret = 1; goto cleanup; } @@ -420,7 +440,7 @@ nc_pam_auth(struct nc_session *session, struct nc_auth_client *client, uint16_t /* initialize PAM and see if the given configuration file exists */ ret = pam_start(server_opts.pam_config_name, client->username, &conv, &pam_h); if (ret != PAM_SUCCESS) { - ERR(NULL, "PAM error occurred (%s).", pam_strerror(pam_h, ret)); + ERR(session, "PAM error occurred (%s).", pam_strerror(pam_h, ret)); goto cleanup; } @@ -428,10 +448,10 @@ nc_pam_auth(struct nc_session *session, struct nc_auth_client *client, uint16_t ret = pam_authenticate(pam_h, 0); if (ret != PAM_SUCCESS) { if (ret == PAM_ABORT) { - ERR(NULL, "PAM error occurred (%s).", pam_strerror(pam_h, ret)); + ERR(session, "PAM error occurred (%s).", pam_strerror(pam_h, ret)); goto cleanup; } else { - VRB(NULL, "PAM error occurred (%s).", pam_strerror(pam_h, ret)); + VRB(session, "PAM error occurred (%s).", pam_strerror(pam_h, ret)); goto cleanup; } } @@ -439,18 +459,18 @@ nc_pam_auth(struct nc_session *session, struct nc_auth_client *client, uint16_t /* correct token entered, check other requirements(the time of the day, expired token, ...) */ ret = pam_acct_mgmt(pam_h, 0); if ((ret != PAM_SUCCESS) && (ret != PAM_NEW_AUTHTOK_REQD)) { - VRB(NULL, "PAM error occurred (%s).", pam_strerror(pam_h, ret)); + VRB(session, "PAM error occurred (%s).", pam_strerror(pam_h, ret)); goto cleanup; } /* if a token has expired a new one will be generated */ if (ret == PAM_NEW_AUTHTOK_REQD) { - VRB(NULL, "PAM warning occurred (%s).", pam_strerror(pam_h, ret)); + VRB(session, "PAM warning occurred (%s).", pam_strerror(pam_h, ret)); ret = pam_chauthtok(pam_h, PAM_CHANGE_EXPIRED_AUTHTOK); if (ret == PAM_SUCCESS) { - VRB(NULL, "The authentication token of user \"%s\" updated successfully.", client->username); + VRB(session, "The authentication token of user \"%s\" updated successfully.", client->username); } else { - ERR(NULL, "PAM error occurred (%s).", pam_strerror(pam_h, ret)); + ERR(session, "PAM error occurred (%s).", pam_strerror(pam_h, ret)); goto cleanup; } } @@ -463,7 +483,189 @@ nc_pam_auth(struct nc_session *session, struct nc_auth_client *client, uint16_t return ret; } -#endif /* HAVE_LIBPAM */ +#elif defined (HAVE_SHADOW) + +static struct passwd * +nc_server_ssh_getpwnam(const char *username, struct passwd *pwd_buf, char **buf, size_t *buf_size) +{ + struct passwd *pwd = NULL; + char *mem; + int r = 0; + + do { + r = getpwnam_r(username, pwd_buf, *buf, *buf_size, &pwd); + if (pwd) { + /* entry found */ + break; + } + + if (r == ERANGE) { + /* small buffer, enlarge */ + *buf_size <<= 2; + mem = realloc(*buf, *buf_size); + if (!mem) { + ERRMEM; + return NULL; + } + *buf = mem; + } + } while (r == ERANGE); + + return pwd; +} + +static struct spwd * +nc_server_ssh_getspnam(const char *username, struct spwd *spwd_buf, char **buf, size_t *buf_size) +{ + struct spwd *spwd = NULL; + char *mem; + int r = 0; + + do { +# ifndef __QNXNTO__ + r = getspnam_r(username, spwd_buf, *buf, *buf_size, &spwd); +# else + spwd = getspnam_r(username, spwd_buf, *buf, *buf_size); +# endif + if (spwd) { + /* entry found */ + break; + } + + if (r == ERANGE) { + /* small buffer, enlarge */ + *buf_size <<= 2; + mem = realloc(*buf, *buf_size); + if (!mem) { + ERRMEM; + return NULL; + } + *buf = mem; + } + } while (r == ERANGE); + + return spwd; +} + +static char * +nc_server_ssh_get_pwd_hash(const char *username) +{ + struct passwd *pwd, pwd_buf; + struct spwd *spwd, spwd_buf; + char *pass_hash = NULL, *buf = NULL; + size_t buf_size = 256; + + buf = malloc(buf_size); + NC_CHECK_ERRMEM_GOTO(!buf, , error); + + pwd = nc_server_ssh_getpwnam(username, &pwd_buf, &buf, &buf_size); + if (!pwd) { + VRB(NULL, "User \"%s\" not found locally.", username); + goto error; + } + + if (!strcmp(pwd->pw_passwd, "x")) { + spwd = nc_server_ssh_getspnam(username, &spwd_buf, &buf, &buf_size); + if (!spwd) { + VRB(NULL, "Failed to retrieve the shadow entry for \"%s\".", username); + goto error; + } else if ((spwd->sp_expire > -1) && (spwd->sp_expire <= (time(NULL) / (60 * 60 * 24)))) { + WRN(NULL, "User \"%s\" account has expired.", username); + goto error; + } + + pass_hash = spwd->sp_pwdp; + } else { + pass_hash = pwd->pw_passwd; + } + + if (!pass_hash) { + ERR(NULL, "No password could be retrieved for \"%s\".", username); + goto error; + } + + /* check the hash structure for special meaning */ + if (!strcmp(pass_hash, "*") || !strcmp(pass_hash, "!")) { + VRB(NULL, "User \"%s\" is not allowed to authenticate using a password.", username); + goto error; + } + if (!strcmp(pass_hash, "*NP*")) { + VRB(NULL, "Retrieving password for \"%s\" from a NIS+ server not supported.", username); + goto error; + } + + pass_hash = strdup(pass_hash); + free(buf); + return pass_hash; + +error: + free(buf); + return NULL; +} + +/** + * @brief Authenticate using locally stored credentials. + * + * @param[in] session Session to authenticate on. + * @param[in] client Client to authenticate. + * @param[in] auth_timeout Authentication timeout. + * @param[in] msg SSH message that originally requested kbdint authentication. + * + * @return 0 on success, non-zero otherwise. + */ +static int +nc_server_ssh_system_auth(struct nc_session *session, struct nc_auth_client *client, uint16_t auth_timeout, ssh_message msg) +{ + int ret = 0, n_answers; + const char *name = "Keyboard-Interactive Authentication"; + const char *instruction = "Please enter your authentication token"; + char *prompt = NULL, *local_pw = NULL, *received_pw = NULL; + char echo[] = {0}; + + /* try to get the client's locally stored pw hash */ + local_pw = nc_server_ssh_get_pwd_hash(client->username); + if (!local_pw) { + ERR(session, "Unable to get %s's credentials.", client->username); + ret = 1; + goto cleanup; + } + + ret = asprintf(&prompt, "%s's password:", client->username); + NC_CHECK_ERRMEM_GOTO(ret == -1, prompt = NULL; ret = 1, cleanup); + + /* send the password prompt to the client */ + ret = ssh_message_auth_interactive_request(msg, name, instruction, 1, (const char **) &prompt, echo); + if (ret) { + ERR(session, "Failed to send an authentication request to client \"%s\".", client->username); + goto cleanup; + } + + /* get the reply */ + n_answers = nc_server_ssh_kbdint_get_nanswers(session, session->ti.libssh.session, auth_timeout); + if (n_answers < 0) { + /* timeout or dc */ + ret = 1; + goto cleanup; + } else if (n_answers != 1) { + /* only expecting a single answer */ + ERR(session, "Unexpected amount of answers in system auth. Expected 1, got \"%d\".", n_answers); + ret = 1; + goto cleanup; + } + received_pw = strdup(ssh_userauth_kbdint_getanswer(session->ti.libssh.session, 0)); + NC_CHECK_ERRMEM_GOTO(!received_pw, ret = 1, cleanup); + + /* cmp the pw hashes */ + ret = auth_password_compare_pwd(local_pw, received_pw); + +cleanup: + free(local_pw); + free(received_pw); + free(prompt); + return ret; +} + +#endif static int nc_sshcb_auth_kbdint(struct nc_session *session, struct nc_auth_client *client, uint16_t auth_timeout, ssh_message msg) @@ -474,18 +676,25 @@ nc_sshcb_auth_kbdint(struct nc_session *session, struct nc_auth_client *client, auth_ret = server_opts.interactive_auth_clb(session, session->ti.libssh.session, msg, server_opts.interactive_auth_data); } else { #ifdef HAVE_LIBPAM - if (nc_pam_auth(session, client, auth_timeout, msg) == PAM_SUCCESS) { + /* authenticate using PAM */ + if (!nc_pam_auth(session, client, auth_timeout, msg)) { + auth_ret = 0; + } +#elif defined (HAVE_SHADOW) + /* authenticate using locally configured users */ + if (!nc_server_ssh_system_auth(session, client, auth_timeout, msg)) { auth_ret = 0; } #else - ERR(session, "PAM-based SSH authentication is not supported."); + (void) auth_timeout; + ERR(NULL, "Keyboard-interactive method not supported."); #endif } /* Authenticate message based on outcome */ if (auth_ret) { ++session->opts.server.ssh_auth_attempts; - VRB(session, "Failed user \"%s\" authentication attempt (#%d).", session->username, + VRB(session, "Failed user \"%s\" authentication attempt (#%d).", client->username, session->opts.server.ssh_auth_attempts); ssh_message_reply_default(msg); } @@ -497,9 +706,15 @@ API void nc_server_ssh_set_interactive_auth_clb(int (*interactive_auth_clb)(const struct nc_session *session, ssh_session ssh_sess, ssh_message msg, void *user_data), void *user_data, void (*free_user_data)(void *user_data)) { + /* CONFIG LOCK */ + pthread_rwlock_wrlock(&server_opts.config_lock); + server_opts.interactive_auth_clb = interactive_auth_clb; server_opts.interactive_auth_data = user_data; server_opts.interactive_auth_data_free = free_user_data; + + /* CONFIG UNLOCK */ + pthread_rwlock_unlock(&server_opts.config_lock); } #ifdef HAVE_LIBPAM @@ -507,12 +722,23 @@ nc_server_ssh_set_interactive_auth_clb(int (*interactive_auth_clb)(const struct API int nc_server_ssh_set_pam_conf_filename(const char *filename) { + int ret = 0; + NC_CHECK_ARG_RET(NULL, filename, 1); + /* CONFIG LOCK */ + pthread_rwlock_wrlock(&server_opts.config_lock); + free(server_opts.pam_config_name); server_opts.pam_config_name = strdup(filename); - NC_CHECK_ERRMEM_RET(!server_opts.pam_config_name, 1); - return 0; + if (!server_opts.pam_config_name) { + ERRMEM; + ret = 1; + } + + /* CONFIG UNLOCK */ + pthread_rwlock_unlock(&server_opts.config_lock); + return ret; } #else From 37a6830b03d99f7c4929aa7f34ecaeef34a76426 Mon Sep 17 00:00:00 2001 From: roman Date: Thu, 30 Nov 2023 16:39:40 +0100 Subject: [PATCH 08/12] config UPDATE remove obsolete crypt macro --- src/config.h.in | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/config.h.in b/src/config.h.in index 7cf3323c..97f55b5c 100644 --- a/src/config.h.in +++ b/src/config.h.in @@ -34,11 +34,6 @@ */ #cmakedefine HAVE_SHADOW -/* - * Support for crypt.h - */ -#cmakedefine HAVE_CRYPT - /* * Support for keyboard-interactive SSH authentication method */ From b2cb584f2a7c42d421601d9b0fb3f41286dfa9be Mon Sep 17 00:00:00 2001 From: roman Date: Thu, 30 Nov 2023 16:41:25 +0100 Subject: [PATCH 09/12] doc UPDATE add kbdint paragraph to the docs --- doc/libnetconf.doc | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/doc/libnetconf.doc b/doc/libnetconf.doc index f756364a..feac6c9f 100644 --- a/doc/libnetconf.doc +++ b/doc/libnetconf.doc @@ -351,8 +351,7 @@ * * If you wish not to create the __YANG data__ yourself, you may use the library's functions to do this for you. * For example ::nc_server_config_add_address_port() creates __YANG data__ corresponding to an SSH/TLS endpoint. - * The variant for UNIX socket is ::nc_server_config_add_unix_socket(). You can then apply this data - * by calling ::nc_server_config_setup_data() (or ::nc_server_config_setup_diff() for diff). + * You can then apply this data by calling ::nc_server_config_setup_data() (or ::nc_server_config_setup_diff() for diff). * See *examples/server.c* for a simple example. * * You may also create entries in the keystore or truststore. For example the asymmetric key and certificate entries @@ -371,7 +370,6 @@ * - ::nc_server_config_setup_path() * * - ::nc_server_config_add_address_port() - * - ::nc_server_config_add_unix_socket() * - ::nc_server_config_del_endpt() * - ::nc_server_config_add_keystore_asym_key() * - ::nc_server_config_del_keystore_asym_key() @@ -393,6 +391,14 @@ * Another option for authorized clients is to reference another endpoint's clients, however be careful not to create a cyclic reference * (see ::nc_server_config_add_ssh_endpoint_client_ref()). An authorized client MUST authenticate to all of it's configured authentication methods. * + * \anchor ln2doc_kbdint + * The Keyboard Interactive authentication method is also supported. It can be done in three ways. + * If libpam is found, Linux PAM is used to handle the authentication. You need to specify the service name using ::nc_server_ssh_set_pam_conf_filename(). + * Else if the standard functions for accessing local users are found on the system, they are used. The only Keyboard Interactive challenge will be the given + * user's password (that is if he's found on the system). + * Either way, you can always define your own callback to perform the authentication, see ::nc_server_ssh_set_interactive_auth_clb(). + * The callback has a higher priority than the other two methods. + * * There are also some other optional settings. * * Functions List @@ -417,6 +423,9 @@ * - ::nc_server_config_add_ssh_endpoint_client_ref() * - ::nc_server_config_del_ssh_endpoint_client_ref() * + * - ::nc_server_ssh_set_pam_conf_filename() + * - ::nc_server_ssh_set_interactive_auth_clb() + * * TLS * === * From 908700927cc13952fd1e4a5631ea082238da36c5 Mon Sep 17 00:00:00 2001 From: roman Date: Fri, 1 Dec 2023 11:28:42 +0100 Subject: [PATCH 10/12] cmake UPDATE add kbdint method message --- CMakeLists.txt | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index b028d5e5..cacf4de7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -224,6 +224,9 @@ target_link_libraries(netconf2 ${CMAKE_THREAD_LIBS_INIT}) set(CMAKE_REQUIRED_LIBRARIES pthread) check_function_exists(pthread_rwlockattr_setkind_np HAVE_PTHREAD_RWLOCKATTR_SETKIND_NP) +# header file compatibility - shadow.h +check_include_file("shadow.h" HAVE_SHADOW) + if(ENABLE_SSH_TLS) # dependencies - openssl find_package(OpenSSL 3.0.0 REQUIRED) @@ -257,8 +260,12 @@ if(ENABLE_SSH_TLS) target_link_libraries(netconf2 ${LIBPAM_LIBRARIES}) list(APPEND CMAKE_REQUIRED_LIBRARIES ${LIBPAM_LIBRARIES}) include_directories(${LIBPAM_INCLUDE_DIRS}) + + message(STATUS "SSH Keyboard Interactive system method: Linux PAM") + elseif(HAVE_SHADOW) + message(STATUS "SSH Keyboard Interactive system method: local users") else() - message(WARNING "LibPAM not found, PAM-based keyboard-interactive SSH server authentication method is disabled") + message(WARNING "SSH Keyboard Interactive system method: disabled") endif() # set compiler flag @@ -278,9 +285,6 @@ find_package(LibYANG ${LIBYANG_DEP_SOVERSION} REQUIRED) target_link_libraries(netconf2 ${LIBYANG_LIBRARIES}) include_directories(${LIBYANG_INCLUDE_DIRS}) -# header file compatibility - shadow.h -check_include_file("shadow.h" HAVE_SHADOW) - # function compatibility - getpeereid on QNX if(${CMAKE_SYSTEM_NAME} MATCHES "QNX") target_link_libraries(netconf2 -lsocket) From 392debee75aa4342266981ace48666694d9c2cab Mon Sep 17 00:00:00 2001 From: Roytak Date: Wed, 6 Dec 2023 11:04:02 +0100 Subject: [PATCH 11/12] SOVERSION bump to version 4.1.0 --- CMakeLists.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index cacf4de7..c7c0b12b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -66,8 +66,8 @@ set(LIBNETCONF2_VERSION ${LIBNETCONF2_MAJOR_VERSION}.${LIBNETCONF2_MINOR_VERSION # Major version is changed with every backward non-compatible API/ABI change in libyang, 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 0) -set(LIBNETCONF2_MICRO_SOVERSION 2) +set(LIBNETCONF2_MINOR_SOVERSION 1) +set(LIBNETCONF2_MICRO_SOVERSION 0) set(LIBNETCONF2_SOVERSION_FULL ${LIBNETCONF2_MAJOR_SOVERSION}.${LIBNETCONF2_MINOR_SOVERSION}.${LIBNETCONF2_MICRO_SOVERSION}) set(LIBNETCONF2_SOVERSION ${LIBNETCONF2_MAJOR_SOVERSION}) From 94ac467c0e25be72efddd3ff38d92b56f6c08513 Mon Sep 17 00:00:00 2001 From: Roytak Date: Wed, 6 Dec 2023 11:04:27 +0100 Subject: [PATCH 12/12] VERSION bump to version 3.0.3 --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index c7c0b12b..4e03a7ea 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -59,7 +59,7 @@ endif() # 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 0) -set(LIBNETCONF2_MICRO_VERSION 2) +set(LIBNETCONF2_MICRO_VERSION 3) set(LIBNETCONF2_VERSION ${LIBNETCONF2_MAJOR_VERSION}.${LIBNETCONF2_MINOR_VERSION}.${LIBNETCONF2_MICRO_VERSION}) # Version of the library