1
0
Fork 0

Merging upstream version 3.5.5 (Closes: #1098233).

Signed-off-by: Daniel Baumann <daniel@debian.org>
This commit is contained in:
Daniel Baumann 2025-02-18 11:33:30 +01:00
parent c86ae7dcba
commit 6af28b7e8e
Signed by: daniel
GPG key ID: FBB4F0E80A80222F
144 changed files with 43534 additions and 11497 deletions

View file

@ -3,76 +3,100 @@ add_test(NAME headers
COMMAND ${CMAKE_SOURCE_DIR}/compat/check_includes.sh ${CMAKE_SOURCE_DIR}/src/)
# format
if (${SOURCE_FORMAT_ENABLED})
add_test(NAME format WORKING_DIRECTORY ${CMAKE_BINARY_DIR} COMMAND make format-check)
endif()
# list of all the tests in each directory
set(tests test_io test_fd_comm test_init_destroy_client test_init_destroy_server test_client_thread test_thread_messages)
set(client_tests test_client test_client_messages)
# add -Wl,--wrap flags
set(test test_client_ssh)
set(${test}_mock_funcs connect ssh_connect ssh_userauth_none ssh_userauth_kbdint ssh_is_connected
ssh_channel_open_session ssh_channel_request_subsystem ssh_channel_is_close ssh_channel_write
ssh_channel_poll_timeout ssh_userauth_password nc_handshake_io nc_ctx_check_and_fill
ssh_userauth_try_publickey ssh_userauth_publickey nc_sock_listen_inet nc_sock_accept_binds nc_accept_callhome_ssh_sock)
set(${test}_wrap_link_flags "-Wl")
foreach(mock_func IN LISTS ${test}_mock_funcs)
set(${test}_wrap_link_flags "${${test}_wrap_link_flags},--wrap=${mock_func}")
endforeach()
set(test test_client_tls)
set(${test}_mock_funcs connect SSL_connect nc_send_hello_io nc_handshake_io nc_ctx_check_and_fill)
set(${test}_wrap_link_flags "-Wl")
foreach(mock_func IN LISTS ${test}_mock_funcs)
set(${test}_wrap_link_flags "${${test}_wrap_link_flags},--wrap=${mock_func}")
endforeach()
#append tests depending on SSH/TLS
if(ENABLE_SSH OR ENABLE_TLS)
list(APPEND tests test_server_thread)
if(ENABLE_SSH)
list(APPEND client_tests test_client_ssh)
endif()
if(ENABLE_TLS)
list(APPEND client_tests test_client_tls)
endif()
if(${SOURCE_FORMAT_ENABLED})
add_test(NAME format WORKING_DIRECTORY ${CMAKE_BINARY_DIR} COMMAND cmake --build ${CMAKE_BINARY_DIR} --target format-check)
endif()
foreach(src IN LISTS libsrc)
list(APPEND test_srcs "../${src}")
endforeach()
add_library(testobj OBJECT ${test_srcs})
add_library(testobj OBJECT ${test_srcs} ${compatsrc})
foreach(test_name IN LISTS tests)
add_executable(${test_name} $<TARGET_OBJECTS:testobj> ${test_name}.c)
target_link_libraries(${test_name} ${CMOCKA_LIBRARIES} ${LIBYANG_LIBRARIES} netconf2)
target_include_directories(${test_name} PRIVATE ${CMOCKA_INCLUDE_DIR})
set_target_properties(${test_name} PROPERTIES LINK_FLAGS "${${test_name}_wrap_link_flags}")
add_test(NAME ${test_name} COMMAND $<TARGET_FILE:${test_name}>)
endforeach()
set(TEST_SRC "ln2_test.c")
set(NEXT_TEST_PORT 10050)
foreach(test_name IN LISTS client_tests)
add_executable(${test_name} $<TARGET_OBJECTS:testobj> ./client/${test_name}.c)
target_link_libraries(${test_name} ${CMOCKA_LIBRARIES} ${LIBYANG_LIBRARIES} netconf2)
target_include_directories(${test_name} PRIVATE ${CMOCKA_INCLUDE_DIR})
set_target_properties(${test_name} PROPERTIES LINK_FLAGS "${${test_name}_wrap_link_flags}")
add_test(NAME ${test_name} COMMAND $<TARGET_FILE:${test_name}>)
endforeach()
macro(get_test_ports PORT_COUNT PORT_DEFINITIONS)
if (NOT ${PORT_COUNT})
set(${PORT_COUNT} 1)
endif()
if(ENABLE_VALGRIND_TESTS)
foreach(test_name IN LISTS tests)
add_test(${test_name}_valgrind valgrind --leak-check=full --show-leak-kinds=all --error-exitcode=1
--suppressions=${PROJECT_SOURCE_DIR}/tests/ld.supp ${CMAKE_BINARY_DIR}/tests/${test_name})
endforeach()
SET(PORT_INDEX 0)
while(PORT_INDEX LESS ${${PORT_COUNT}})
list(APPEND ${PORT_DEFINITIONS} "TEST_PORT_${PORT_INDEX}=${NEXT_TEST_PORT}")
math(EXPR PORT_INDEX "${PORT_INDEX} + 1")
math(EXPR NEXT_TEST_PORT "${NEXT_TEST_PORT} + 1")
endwhile()
set(NEXT_TEST_PORT ${NEXT_TEST_PORT} PARENT_SCOPE)
endmacro()
foreach(test_name IN LISTS client_tests)
add_test(${test_name}_valgrind valgrind --leak-check=full --show-leak-kinds=all --error-exitcode=1
--suppressions=${PROJECT_SOURCE_DIR}/tests/ld.supp ${CMAKE_BINARY_DIR}/tests/${test_name})
endforeach()
function(libnetconf2_test)
cmake_parse_arguments(TEST "" "NAME;PORT_COUNT" "WRAP_FUNCS" ${ARGN})
add_executable(${TEST_NAME} $<TARGET_OBJECTS:testobj> ${TEST_SRC} ${TEST_NAME}.c)
target_link_libraries(${TEST_NAME} ${CMOCKA_LIBRARIES} ${LIBYANG_LIBRARIES} netconf2)
target_include_directories(${TEST_NAME} PRIVATE ${CMOCKA_INCLUDE_DIR})
# wrap functions
if(TEST_WRAP_FUNCS)
set(wrap_link_flags "-Wl")
foreach(mock_func IN LISTS TEST_WRAP_FUNCS)
set(wrap_link_flags "${wrap_link_flags},--wrap=${mock_func}")
endforeach()
set_target_properties(${TEST_NAME} PROPERTIES LINK_FLAGS "${wrap_link_flags}")
endif()
# create a test, generate port numbers and set them as env vars for the test
add_test(NAME ${TEST_NAME} COMMAND $<TARGET_FILE:${TEST_NAME}>)
get_test_ports(TEST_PORT_COUNT TEST_PORT_DEFINITIONS)
set_tests_properties(${TEST_NAME} PROPERTIES ENVIRONMENT "${TEST_PORT_DEFINITIONS}")
# do the same for valgrind tests
if(ENABLE_VALGRIND_TESTS)
add_test(${TEST_NAME}_valgrind valgrind --leak-check=full --show-leak-kinds=all --error-exitcode=1
--suppressions=${PROJECT_SOURCE_DIR}/tests/library_valgrind.supp ${CMAKE_BINARY_DIR}/tests/${TEST_NAME})
get_test_ports(TEST_PORT_COUNT VALGRIND_TEST_PORT_DEFINITIONS)
set_tests_properties(${TEST_NAME}_valgrind PROPERTIES ENVIRONMENT "${VALGRIND_TEST_PORT_DEFINITIONS}")
endif()
endfunction()
# all the tests that don't require SSH and TLS
libnetconf2_test(NAME test_client_messages)
libnetconf2_test(NAME test_client_thread)
libnetconf2_test(NAME test_fd_comm)
libnetconf2_test(NAME test_io)
libnetconf2_test(NAME test_thread_messages)
libnetconf2_test(NAME test_unix_socket)
# tests depending on SSH/TLS
if(ENABLE_SSH_TLS)
libnetconf2_test(NAME test_auth_ssh)
libnetconf2_test(NAME test_authkeys)
libnetconf2_test(NAME test_cert_exp_notif)
libnetconf2_test(NAME test_ch PORT_COUNT 2)
libnetconf2_test(NAME test_client_monitoring)
libnetconf2_test(NAME test_endpt_share_clients PORT_COUNT 4)
libnetconf2_test(NAME test_ks_ts)
if (LIBPAM_HAVE_CONFDIR)
libnetconf2_test(NAME test_pam WRAP_FUNCS pam_start)
endif()
libnetconf2_test(NAME test_replace)
libnetconf2_test(NAME test_runtime_changes PORT_COUNT 2)
libnetconf2_test(NAME test_tls)
libnetconf2_test(NAME test_two_channels)
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)
# 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"
)

View file

@ -1,125 +0,0 @@
#include <errno.h>
#include <setjmp.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <cmocka.h>
#include <libyang/libyang.h>
#include <log.h>
#include <session_client.h>
#include "tests/config.h"
static int
setup_f(void **state)
{
(void)state;
nc_verbosity(NC_VERB_VERBOSE);
return 0;
}
static int
teardown_f(void **state)
{
(void)state;
return 0;
}
static void
test_nc_client_setting_schema_searchpath(void **state)
{
(void)state;
const char *path;
int ret;
/* initiate client */
nc_client_init();
path = nc_client_get_schema_searchpath();
assert_null(path);
ret = nc_client_set_schema_searchpath("path");
assert_int_equal(ret, 0);
path = nc_client_get_schema_searchpath();
assert_string_equal(path, "path");
ret = nc_client_set_schema_searchpath("path1");
assert_int_equal(ret, 0);
path = nc_client_get_schema_searchpath();
assert_string_equal(path, "path1");
}
LY_ERR
test_clb(const char *mod_name, const char *mod_rev, const char *submod_name, const char *sub_rev, void *user_data,
LYS_INFORMAT *format, const char **model_data, void (**free_module_data)(void *model_data, void *user_data))
{
(void)mod_name;
(void)mod_rev;
(void)submod_name;
(void)sub_rev;
(void)user_data;
(void)format;
(void)model_data;
(void)free_module_data;
return LY_SUCCESS;
}
LY_ERR
test_clb1(const char *mod_name, const char *mod_rev, const char *submod_name, const char *sub_rev, void *user_data,
LYS_INFORMAT *format, const char **model_data, void (**free_module_data)(void *model_data, void *user_data))
{
(void)mod_name;
(void)mod_rev;
(void)submod_name;
(void)sub_rev;
(void)user_data;
(void)format;
(void)model_data;
(void)free_module_data;
return LY_SUCCESS;
}
static void
test_nc_client_setting_schema_callback(void **state)
{
(void)state;
ly_module_imp_clb ret_f;
char *data_ret;
int ret;
ret_f = nc_client_get_schema_callback((void **)&data_ret);
assert_null(ret_f);
assert_null(data_ret);
ret = nc_client_set_schema_callback(test_clb, "DATA");
assert_int_equal(ret, 0);
ret_f = nc_client_get_schema_callback((void **)&data_ret);
assert_ptr_equal(test_clb, ret_f);
assert_string_equal("DATA", data_ret);
ret = nc_client_set_schema_callback(test_clb1, "DATA1");
assert_int_equal(ret, 0);
ret_f = nc_client_get_schema_callback((void **)&data_ret);
assert_ptr_equal(test_clb1, ret_f);
assert_string_equal("DATA1", data_ret);
/* destroy client */
nc_client_destroy();
}
int
main(void)
{
const struct CMUnitTest tests[] = {
cmocka_unit_test_setup_teardown(test_nc_client_setting_schema_searchpath, setup_f, teardown_f),
cmocka_unit_test_setup_teardown(test_nc_client_setting_schema_callback, setup_f, teardown_f),
};
return cmocka_run_group_tests(tests, NULL, NULL);
}

View file

@ -1,813 +0,0 @@
#include <errno.h>
#include <setjmp.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <cmocka.h>
#include <config.h>
#include <libyang/libyang.h>
#include <log.h>
#include <session_client.h>
#include <session_client_ch.h>
#include <session_p.h>
#include "tests/config.h"
#include <libssh/callbacks.h>
#include <libssh/libssh.h>
#include <libssh/server.h>
static int
ssh_hostkey_check_clb(const char *hostname, ssh_session session, void *priv)
{
(void)hostname;
(void)session;
(void)priv;
return 0;
}
static int
setup_f(void **state)
{
(void)state;
int ret;
nc_verbosity(NC_VERB_VERBOSE);
ret = nc_client_ssh_set_username("username");
assert_int_equal(ret, 0);
ret = nc_client_ssh_ch_set_username("ch_username");
assert_int_equal(ret, 0);
nc_client_ssh_set_auth_hostkey_check_clb(ssh_hostkey_check_clb, NULL);
return 0;
}
static int
teardown_f(void **state)
{
(void)state;
return 0;
}
MOCK int
__wrap_connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen)
{
(void)sockfd;
(void)addr;
(void)addrlen;
return (int)mock();
}
MOCK int
__wrap_ssh_connect(ssh_session session)
{
(void)session;
/* set support of all authentication methods by fake server */
ssh_set_auth_methods(session, SSH_AUTH_METHOD_PUBLICKEY | SSH_AUTH_METHOD_PASSWORD | SSH_AUTH_METHOD_INTERACTIVE);
return (int)mock();
}
MOCK int
__wrap_ssh_userauth_none(ssh_session session, const char *username)
{
(void)session;
(void)username;
return (int)mock();
}
MOCK int
__wrap_ssh_userauth_kbdint(ssh_session session, const char *user, const char *submethods)
{
(void)session;
(void)user;
(void)submethods;
return (int)mock();
}
MOCK int
__wrap_ssh_is_connected(ssh_session session)
{
(void)session;
return (int)mock();
}
MOCK int
__wrap_ssh_channel_open_session(ssh_channel channel)
{
(void)channel;
return (int)mock();
}
MOCK int
__wrap_ssh_channel_request_subsystem(ssh_channel channel, const char *subsystem)
{
(void)channel;
(void)subsystem;
return (int)mock();
}
MOCK int
__wrap_ssh_channel_is_closed(ssh_channel channel)
{
(void)channel;
return 0;
}
MOCK int
__wrap_ssh_channel_write(ssh_channel channel, const void *data, uint32_t len)
{
(void)channel;
(void)data;
return len;
}
MOCK int
__wrap_ssh_channel_poll_timeout(ssh_channel channel, int timeout, int is_stderr)
{
(void)channel;
(void)timeout;
(void)is_stderr;
return (int)mock();
}
MOCK int
__wrap_ssh_userauth_password(ssh_session session, const char *username, const char *password)
{
(void)session;
check_expected(password);
check_expected(username);
return (int)mock();
}
MOCK int
__wrap_nc_handshake_io(struct nc_session *session)
{
(void)session;
return (int)mock();
}
MOCK int
__wrap_nc_ctx_check_and_fill(struct nc_session *session)
{
(void)session;
return (int)mock();
}
MOCK int
__wrap_ssh_userauth_try_publickey(ssh_session session, const char *username, const ssh_key pubkey)
{
(void)session;
(void)username;
(void)pubkey;
return (int)mock();
}
MOCK int
__wrap_ssh_userauth_publickey(ssh_session session, const char *username, const ssh_key privkey)
{
(void)session;
(void)username;
(void)privkey;
return (int)mock();
}
MOCK int
__wrap_nc_sock_listen_inet(const char *address, uint16_t port, struct nc_keepalives *ka)
{
(void)address;
(void)port;
(void)ka;
return (int)mock();
}
MOCK int
__wrap_nc_sock_accept_binds(struct nc_bind *binds, uint16_t bind_count, int timeout, char **host, uint16_t *port, uint16_t *idx)
{
(void)binds;
(void)bind_count;
(void)timeout;
(void)host;
(void)port;
*idx = 0;
return (int)mock();
}
MOCK struct nc_session *
__wrap_nc_accept_callhome_ssh_sock(int sock, const char *host, uint16_t port, struct ly_ctx *ctx, int timeout)
{
(void)sock;
(void)host;
(void)port;
(void)ctx;
(void)timeout;
return mock_ptr_type(struct nc_session *);
}
static int
test_hostkey_clb(const char *hostname, ssh_session session, void *priv)
{
(void)hostname;
(void)session;
(void)priv;
return 0;
}
static void
test_nc_client_ssh_setting_auth_hostkey_check_clb(void **state)
{
(void)state;
int (*ret_f)(const char *hostname, ssh_session session, void *priv);
char *priv_data_ret;
/* ssh_hostkey_check_clb is set in setup_f */
nc_client_ssh_get_auth_hostkey_check_clb(&ret_f, (void **)&priv_data_ret);
assert_ptr_equal(ret_f, ssh_hostkey_check_clb);
assert_null(priv_data_ret);
/* set different callback and private data */
nc_client_ssh_set_auth_hostkey_check_clb(test_hostkey_clb, "DATA");
nc_client_ssh_get_auth_hostkey_check_clb(&ret_f, (void **)&priv_data_ret);
assert_ptr_equal(ret_f, test_hostkey_clb);
assert_string_equal(priv_data_ret, "DATA");
}
char *
test_pwd_clb1(const char *username, const char *hostname, void *priv)
{
char *pass, *pass_to_return;
check_expected(username);
check_expected(hostname);
check_expected(priv);
pass = (char *)mock();
pass_to_return = malloc(sizeof *pass * (strlen(pass) + 1));
strcpy(pass_to_return, pass);
return pass_to_return;
}
char *
test_pwd_clb2(const char *username, const char *hostname, void *priv)
{
(void)username;
(void)hostname;
(void)priv;
return 0;
}
static void
test_nc_client_ssh_setting_auth_password_clb(void **state)
{
(void)state;
char *(*ret_f)(const char *username, const char *hostname, void *priv);
char *priv_data_ret;
/* set callback */
nc_client_ssh_set_auth_password_clb(test_pwd_clb1, "DATA");
nc_client_ssh_get_auth_password_clb(&ret_f, (void **)&priv_data_ret);
assert_ptr_equal(test_pwd_clb1, ret_f);
assert_string_equal("DATA", priv_data_ret);
/* set different callback */
nc_client_ssh_set_auth_password_clb(test_pwd_clb2, "NEW DATA");
nc_client_ssh_get_auth_password_clb(&ret_f, (void **)&priv_data_ret);
assert_ptr_equal(test_pwd_clb2, ret_f);
assert_string_equal("NEW DATA", priv_data_ret);
}
char *
test_inter_clb1(const char *auth_name, const char *instruction, const char *prompt, int echo, void *priv)
{
(void)auth_name;
(void)instruction;
(void)prompt;
(void)echo;
(void)priv;
return 0;
}
char *
test_inter_clb2(const char *auth_name, const char *instruction, const char *prompt, int echo, void *priv)
{
(void)auth_name;
(void)instruction;
(void)prompt;
(void)echo;
(void)priv;
return 0;
}
static void
test_nc_client_ssh_setting_auth_interactive_clb(void **state)
{
(void)state;
char *(*ret_f)(const char *auth_name, const char *instruction, const char *prompt, int echo, void *priv);
char *priv_data_ret;
/* set callback */
nc_client_ssh_set_auth_interactive_clb(test_inter_clb1, "DATA");
nc_client_ssh_get_auth_interactive_clb(&ret_f, (void **)&priv_data_ret);
assert_ptr_equal(test_inter_clb1, ret_f);
assert_string_equal("DATA", priv_data_ret);
/* set diferent callback */
nc_client_ssh_set_auth_interactive_clb(test_inter_clb2, "NEW DATA");
nc_client_ssh_get_auth_interactive_clb(&ret_f, (void **)&priv_data_ret);
assert_ptr_equal(test_inter_clb2, ret_f);
assert_string_equal("NEW DATA", priv_data_ret);
}
char *
test_passphrase_clb1(const char *privkey_path, void *priv)
{
(void)privkey_path;
(void)priv;
return 0;
}
char *
test_passphrase_clb2(const char *privkey_path, void *priv)
{
(void)privkey_path;
(void)priv;
return 0;
}
static void
test_nc_client_ssh_setting_auth_privkey_passphrase_clb(void **state)
{
(void)state;
char *(*ret_f)(const char *privkey_path, void *priv);
char *priv_data_ret;
/* set first callback */
nc_client_ssh_set_auth_privkey_passphrase_clb(test_passphrase_clb1, "DATA");
nc_client_ssh_get_auth_privkey_passphrase_clb(&ret_f, (void **)&priv_data_ret);
assert_ptr_equal(ret_f, test_passphrase_clb1);
assert_string_equal("DATA", priv_data_ret);
/* set different callback */
nc_client_ssh_set_auth_privkey_passphrase_clb(test_passphrase_clb2, "NEW DATA");
nc_client_ssh_get_auth_privkey_passphrase_clb(&ret_f, (void **)&priv_data_ret);
assert_ptr_equal(ret_f, test_passphrase_clb2);
assert_string_equal("NEW DATA", priv_data_ret);
}
static void
test_nc_client_ssh_adding_keypair(void **state)
{
(void)state;
int ret;
const char *pubkey1, *pubkey2;
/* at the beginning keypair count should be 0 */
ret = nc_client_ssh_get_keypair_count();
assert_int_equal(ret, 0);
/* add first key pair */
ret = nc_client_ssh_add_keypair(TESTS_DIR "/data/key_dsa.pub", TESTS_DIR "/data/key_dsa");
assert_int_equal(ret, 0);
ret = nc_client_ssh_get_keypair_count();
assert_int_equal(ret, 1);
/* add second keypair */
ret = nc_client_ssh_add_keypair("key_pub", "key_priv");
assert_int_equal(ret, 0);
ret = nc_client_ssh_get_keypair_count();
assert_int_equal(ret, 2);
ret = nc_client_ssh_get_keypair(1, &pubkey1, &pubkey2);
assert_int_equal(ret, 0);
assert_string_equal(pubkey1, "key_pub");
assert_string_equal(pubkey2, "key_priv");
/* delete first keypair */
ret = nc_client_ssh_del_keypair(0);
assert_int_equal(ret, 0);
ret = nc_client_ssh_get_keypair_count();
assert_int_equal(ret, 1);
/* try to get deleted keypair */
ret = nc_client_ssh_get_keypair(5, &pubkey1, &pubkey2);
assert_int_equal(ret, -1);
/* try to add keypair that is already set */
ret = nc_client_ssh_add_keypair("key_pub", "key_priv");
assert_int_equal(ret, -1);
ret = nc_client_ssh_get_keypair_count();
assert_int_equal(ret, 1);
/* try to delete keypair with id that is not used */
ret = nc_client_ssh_del_keypair(42);
assert_int_equal(ret, -1);
ret = nc_client_ssh_get_keypair_count();
assert_int_equal(ret, 1);
/* remove remaining keypairs */
ret = nc_client_ssh_del_keypair(0);
assert_int_equal(ret, 0);
ret = nc_client_ssh_get_keypair_count();
assert_int_equal(ret, 0);
}
static void
test_nc_client_ssh_setting_auth_pref(void **state)
{
(void)state;
int ret;
/* initiate client, must be called in first test */
nc_client_init();
/* check default prefference settings according to documentation */
ret = nc_client_ssh_get_auth_pref(NC_SSH_AUTH_INTERACTIVE);
assert_int_equal(ret, 3);
ret = nc_client_ssh_get_auth_pref(NC_SSH_AUTH_PASSWORD);
assert_int_equal(ret, 2);
ret = nc_client_ssh_get_auth_pref(NC_SSH_AUTH_PUBLICKEY);
assert_int_equal(ret, 1);
/* try to set prefetence of non existing method */
nc_client_ssh_set_auth_pref(42, 22);
/* try to get preference of non existing method */
ret = nc_client_ssh_get_auth_pref(42);
assert_int_equal(ret, 0);
/* change values of all methods and check if they actually changed */
nc_client_ssh_set_auth_pref(NC_SSH_AUTH_INTERACTIVE, 9);
ret = nc_client_ssh_get_auth_pref(NC_SSH_AUTH_INTERACTIVE);
assert_int_equal(ret, 9);
/* negative value should be set as -1 */
nc_client_ssh_set_auth_pref(NC_SSH_AUTH_PASSWORD, -5);
ret = nc_client_ssh_get_auth_pref(NC_SSH_AUTH_PASSWORD);
assert_int_equal(ret, -1);
nc_client_ssh_set_auth_pref(NC_SSH_AUTH_PUBLICKEY, 11);
ret = nc_client_ssh_get_auth_pref(NC_SSH_AUTH_PUBLICKEY);
assert_int_equal(ret, 11);
}
static void
test_nc_client_ssh_setting_username(void **state)
{
(void)state;
int ret;
const char *username_ret;
username_ret = nc_client_ssh_get_username();
/* username is set to "username" in setup_f */
assert_string_equal(username_ret, "username");
/* set new username and check if it changes */
ret = nc_client_ssh_set_username("new_username");
assert_int_equal(ret, 0);
username_ret = nc_client_ssh_get_username();
assert_string_equal(username_ret, "new_username");
}
static void
test_nc_connect_ssh_interactive_succesfull(void **state)
{
(void)state;
struct nc_session *session;
/* set authentication method to use interactive authentication */
nc_client_ssh_set_auth_pref(NC_SSH_AUTH_INTERACTIVE, 1);
nc_client_ssh_set_auth_pref(NC_SSH_AUTH_PASSWORD, -1);
nc_client_ssh_set_auth_pref(NC_SSH_AUTH_PUBLICKEY, -1);
nc_client_ssh_set_auth_pref(NC_SSH_AUTH_INTERACTIVE, 20);
/* prepare return values for functions used by nc_connect_ssh */
will_return(__wrap_connect, 0);
will_return(__wrap_ssh_connect, 0);
will_return(__wrap_ssh_userauth_none, 1);
will_return(__wrap_ssh_userauth_kbdint, 0);
will_return(__wrap_ssh_is_connected, 1);
will_return(__wrap_ssh_is_connected, 1);
will_return(__wrap_ssh_channel_open_session, 0);
will_return(__wrap_ssh_channel_request_subsystem, 0);
will_return(__wrap_nc_handshake_io, 3);
will_return(__wrap_nc_ctx_check_and_fill, 0);
session = nc_connect_ssh("127.0.0.1", 8080, NULL);
assert_non_null(session);
will_return(__wrap_ssh_channel_poll_timeout, 0);
nc_session_free(session, NULL);
}
static void
test_nc_connect_ssh_password_succesfull(void **state)
{
(void)state;
struct nc_session *session;
/* set authentication method to use password authentication */
nc_client_ssh_set_auth_pref(NC_SSH_AUTH_PASSWORD, 1);
nc_client_ssh_set_auth_pref(NC_SSH_AUTH_PUBLICKEY, -1);
nc_client_ssh_set_auth_pref(NC_SSH_AUTH_INTERACTIVE, -1);
/* set authentication callback */
nc_client_ssh_set_auth_password_clb(test_pwd_clb1, "private_data");
will_return(test_pwd_clb1, "secret password");
/* set values that are expected as parameters for authentication callback */
expect_string(test_pwd_clb1, username, "username");
expect_string(test_pwd_clb1, hostname, "127.0.0.1");
expect_string(test_pwd_clb1, priv, "private_data");
/* fake succesfull connection */
will_return(__wrap_connect, 0);
will_return(__wrap_ssh_connect, 0);
/* do not authenticate using no authentication method */
will_return(__wrap_ssh_userauth_none, 1);
/* succesfully authenticate via password authentication */
expect_string(__wrap_ssh_userauth_password, password, "secret password");
expect_string(__wrap_ssh_userauth_password, username, "username");
will_return(__wrap_ssh_userauth_password, 0);
/* fake ssh functions that are used to open netconf channel */
will_return(__wrap_ssh_channel_open_session, 0);
will_return(__wrap_ssh_channel_request_subsystem, 0);
/* fake that connection is still alive*/
will_return(__wrap_ssh_is_connected, 1);
/* fake ssh function for recieving hello message */
will_return(__wrap_ssh_is_connected, 1);
will_return(__wrap_nc_handshake_io, 3);
will_return(__wrap_nc_ctx_check_and_fill, 0);
session = nc_connect_ssh("127.0.0.1", 8080, NULL);
assert_non_null(session);
/* disconnect */
will_return(__wrap_ssh_channel_poll_timeout, 0);
nc_session_free(session, NULL);
}
static void
test_nc_connect_ssh_pubkey_succesfull(void **state)
{
(void)state;
struct nc_session *session;
int ret = 0;
/* set authentication method to use password authentication */
nc_client_ssh_set_auth_pref(NC_SSH_AUTH_PASSWORD, -1);
nc_client_ssh_set_auth_pref(NC_SSH_AUTH_PUBLICKEY, 1);
nc_client_ssh_set_auth_pref(NC_SSH_AUTH_INTERACTIVE, -1);
/* add keypair for authentication */
ret = nc_client_ssh_add_keypair(TESTS_DIR "/data/key_dsa.pub", TESTS_DIR "/data/key_dsa");
assert_int_equal(ret, 0);
/* fake succesfull connection */
will_return(__wrap_connect, 0);
will_return(__wrap_ssh_connect, 0);
/* do not authenticate using no authentication method */
will_return(__wrap_ssh_userauth_none, 1);
will_return(__wrap_ssh_userauth_try_publickey, 0);
will_return(__wrap_ssh_userauth_publickey, 0);
will_return(__wrap_ssh_is_connected, 1);
will_return(__wrap_ssh_channel_open_session, 0);
will_return(__wrap_ssh_channel_request_subsystem, 0);
/* fake ssh function for recieving hello message */
will_return(__wrap_ssh_is_connected, 1);
will_return(__wrap_nc_handshake_io, 3);
will_return(__wrap_nc_ctx_check_and_fill, 0);
session = nc_connect_ssh("127.0.0.1", 8080, NULL);
assert_non_null(session);
/* disconnect */
will_return(__wrap_ssh_channel_poll_timeout, 0);
nc_session_free(session, NULL);
}
static void
test_nc_connect_connection_failed(void **state)
{
(void)state;
struct nc_session *session;
errno = ECONNREFUSED;
will_return(__wrap_connect, -1);
will_return(__wrap_ssh_is_connected, 0);
session = nc_connect_ssh("127.0.0.1", 8080, NULL);
assert_null(session);
}
static void
test_nc_connect_ssh_bad_hello(void **state)
{
(void)state;
struct nc_session *session;
/* set authentication method to use interactive authentication */
nc_client_ssh_set_auth_pref(NC_SSH_AUTH_INTERACTIVE, 1);
nc_client_ssh_set_auth_pref(NC_SSH_AUTH_PASSWORD, -1);
nc_client_ssh_set_auth_pref(NC_SSH_AUTH_PUBLICKEY, 1);
nc_client_ssh_set_auth_password_clb(test_pwd_clb2, NULL);
will_return(__wrap_connect, 0);
will_return(__wrap_ssh_connect, 0);
will_return(__wrap_ssh_userauth_none, 1);
will_return(__wrap_ssh_userauth_kbdint, 0);
will_return(__wrap_ssh_is_connected, 1);
will_return(__wrap_ssh_is_connected, 1);
will_return(__wrap_ssh_channel_open_session, 0);
will_return(__wrap_ssh_channel_request_subsystem, 0);
will_return(__wrap_nc_handshake_io, 4);
session = nc_connect_ssh("127.0.0.1", 8080, NULL);
assert_null(session);
/* destroy client, must be called in last test */
nc_client_destroy();
}
static void
test_nc_client_ssh_ch_setting_username(void **state)
{
(void)state;
const char *username_ret;
int ret;
/* username is set to "ch_username" in setup_f */
username_ret = nc_client_ssh_ch_get_username();
assert_string_equal(username_ret, "ch_username");
/* set new username and check if it changes */
ret = nc_client_ssh_ch_set_username("new_ch_username");
assert_int_equal(ret, 0);
username_ret = nc_client_ssh_ch_get_username();
assert_string_equal(username_ret, "new_ch_username");
}
static void
test_nc_client_ssh_ch_add_bind_listen(void **state)
{
(void)state;
int ret;
/* invalid parameters, address NULL or port 0 */
ret = nc_client_ssh_ch_add_bind_listen(NULL, 4334);
assert_int_equal(ret, -1);
ret = nc_client_ssh_ch_add_bind_listen("127.0.0.1", 0);
assert_int_equal(ret, -1);
/* failed to create an ssh listening socket */
will_return(__wrap_nc_sock_listen_inet, -1);
ret = nc_client_ssh_ch_add_bind_listen("127.0.0.1", 4334);
assert_int_equal(ret, -1);
/* fake a successful CH ssh listening socket */
will_return(__wrap_nc_sock_listen_inet, 1);
ret = nc_client_ssh_ch_add_bind_listen("127.0.0.1", 4334);
assert_int_equal(ret, 0);
/* remove ssh listening client binds */
ret = nc_client_ssh_ch_del_bind("127.0.0.1", 4334);
assert_int_equal(ret, 0);
}
static void
test_nc_accept_callhome(void **state)
{
(void)state;
struct nc_session *session = NULL;
int timeout = 10;
int ret;
/* invalid parameter session */
ret = nc_accept_callhome(timeout, NULL, NULL);
assert_int_equal(ret, -1);
/* no client bind */
ret = nc_accept_callhome(timeout, NULL, &session);
assert_int_equal(ret, -1);
/* successfully add a client Call Home bind */
will_return(__wrap_nc_sock_listen_inet, 1);
ret = nc_client_ssh_ch_add_bind_listen("127.0.0.1", 4334);
assert_int_equal(ret, 0);
/* failed to accept a client bind */
will_return(__wrap_nc_sock_accept_binds, -1);
ret = nc_accept_callhome(timeout, NULL, &session);
assert_int_equal(ret, -1);
/* failed to accept a server Call Home connection */
will_return(__wrap_nc_accept_callhome_ssh_sock, NULL);
will_return(__wrap_nc_sock_accept_binds, 2);
ret = nc_accept_callhome(timeout, NULL, &session);
assert_int_equal(ret, -1);
/* create session structure to fake a successful server call home connection */
session = nc_new_session(NC_CLIENT, 0);
assert_non_null(session);
will_return(__wrap_nc_sock_accept_binds, 2);
will_return(__wrap_nc_accept_callhome_ssh_sock, session);
ret = nc_accept_callhome(timeout, NULL, &session);
assert_int_equal(ret, 1);
/* remove ssh listening client binds */
ret = nc_client_ssh_ch_del_bind("127.0.0.1", 4334);
assert_int_equal(ret, 0);
/* free session */
nc_session_free(session, NULL);
}
static void
test_nc_client_ssh_callhome_successful(void **state)
{
(void)state;
struct nc_session *session = NULL;
int timeout = 10;
int ret;
/* create session structure */
session = nc_new_session(NC_CLIENT, 0);
assert_non_null(session);
/* prepare to fake return values for functions used by nc_accept_callhome */
will_return(__wrap_nc_sock_listen_inet, 1);
will_return(__wrap_nc_sock_accept_binds, 2);
will_return(__wrap_nc_accept_callhome_ssh_sock, session);
ret = nc_client_ssh_ch_add_bind_listen("127.0.0.1", 4334);
assert_int_equal(ret, 0);
ret = nc_accept_callhome(timeout, NULL, &session);
assert_int_equal(ret, 1);
/* remove ssh listening client binds */
ret = nc_client_ssh_ch_del_bind("127.0.0.1", 4334);
assert_int_equal(ret, 0);
/* free session */
nc_session_free(session, NULL);
}
int
main(void)
{
const struct CMUnitTest tests[] = {
cmocka_unit_test_setup_teardown(test_nc_client_ssh_setting_auth_pref, setup_f, teardown_f),
cmocka_unit_test_setup_teardown(test_nc_client_ssh_setting_auth_hostkey_check_clb, setup_f, teardown_f),
cmocka_unit_test_setup_teardown(test_nc_client_ssh_setting_auth_password_clb, setup_f, teardown_f),
cmocka_unit_test_setup_teardown(test_nc_client_ssh_setting_auth_interactive_clb, setup_f, teardown_f),
cmocka_unit_test_setup_teardown(test_nc_client_ssh_setting_auth_privkey_passphrase_clb, setup_f, teardown_f),
cmocka_unit_test_setup_teardown(test_nc_client_ssh_adding_keypair, setup_f, teardown_f),
cmocka_unit_test_setup_teardown(test_nc_client_ssh_setting_username, setup_f, teardown_f),
cmocka_unit_test_setup_teardown(test_nc_connect_ssh_interactive_succesfull, setup_f, teardown_f),
cmocka_unit_test_setup_teardown(test_nc_connect_ssh_password_succesfull, setup_f, teardown_f),
cmocka_unit_test_setup_teardown(test_nc_connect_ssh_pubkey_succesfull, setup_f, teardown_f),
cmocka_unit_test_setup_teardown(test_nc_connect_connection_failed, setup_f, teardown_f),
cmocka_unit_test_setup_teardown(test_nc_connect_ssh_bad_hello, setup_f, teardown_f),
cmocka_unit_test_setup_teardown(test_nc_client_ssh_ch_setting_username, setup_f, teardown_f),
cmocka_unit_test_setup_teardown(test_nc_client_ssh_ch_add_bind_listen, setup_f, teardown_f),
cmocka_unit_test_setup_teardown(test_nc_accept_callhome, setup_f, teardown_f),
cmocka_unit_test_setup_teardown(test_nc_client_ssh_callhome_successful, setup_f, teardown_f),
};
return cmocka_run_group_tests(tests, NULL, NULL);
}

View file

@ -1,200 +0,0 @@
#include <errno.h>
#include <setjmp.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <cmocka.h>
#include <config.h>
#include <libyang/libyang.h>
#include <log.h>
#include <session_client.h>
#include "tests/config.h"
static int
setup_f(void **state)
{
(void)state;
nc_verbosity(NC_VERB_VERBOSE);
return 0;
}
static int
teardown_f(void **state)
{
(void)state;
return 0;
}
MOCK int
__wrap_connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen)
{
(void)sockfd;
(void)addr;
(void)addrlen;
return (int)mock();
}
MOCK int
__wrap_SSL_connect(SSL *ssl)
{
(void)ssl;
return (int)mock();
}
MOCK int
__wrap_nc_handshake_io(struct nc_session *session)
{
(void)session;
return (int)mock();
}
MOCK int
__wrap_nc_ctx_check_and_fill(struct nc_session *session)
{
(void)session;
return (int)mock();
}
static void
test_nc_client_tls_setting_cert_key_paths(void **state)
{
(void)state;
const char *cert, *key;
int ret;
nc_client_init();
/* no certificats are set, nc_client_tls_get_cert_key_paths should output NULL */
nc_client_tls_get_cert_key_paths(&cert, &key);
assert_null(cert);
assert_null(key);
/* set certificate path */
ret = nc_client_tls_set_cert_key_paths("cert_path", "key_path");
assert_int_equal(ret, 0);
nc_client_tls_get_cert_key_paths(&cert, &key);
assert_string_equal(cert, "cert_path");
assert_string_equal(key, "key_path");
/* override certificate path */
ret = nc_client_tls_set_cert_key_paths("cert_path1", "key_path1");
assert_int_equal(ret, 0);
nc_client_tls_get_cert_key_paths(&cert, &key);
assert_string_equal(cert, "cert_path1");
assert_string_equal(key, "key_path1");
}
static void
test_nc_client_tls_setting_trusted_ca_paths(void **state)
{
(void)state;
const char *file, *dir;
int ret;
ret = nc_client_tls_set_trusted_ca_paths("ca_file", "ca_dir");
assert_int_equal(ret, 0);
nc_client_tls_get_trusted_ca_paths(&file, &dir);
assert_string_equal("ca_file", file);
assert_string_equal("ca_dir", dir);
ret = nc_client_tls_set_trusted_ca_paths("ca_file1", "ca_dir1");
assert_int_equal(ret, 0);
nc_client_tls_get_trusted_ca_paths(&file, &dir);
assert_string_equal("ca_file1", file);
assert_string_equal("ca_dir1", dir);
}
static void
test_nc_connect_tls_succesfull(void **state)
{
(void)state;
int ret;
struct nc_session *session;
ret = nc_client_tls_set_cert_key_paths(TESTS_DIR "/data/client.crt", TESTS_DIR "/data/client.key");
assert_int_equal(ret, 0);
ret = nc_client_tls_set_trusted_ca_paths(NULL, TESTS_DIR "/data");
assert_int_equal(ret, 0);
will_return(__wrap_connect, 0);
will_return(__wrap_SSL_connect, 1);
/* fake succesfull handshake */
will_return(__wrap_nc_handshake_io, 3);
will_return(__wrap_nc_ctx_check_and_fill, 0);
session = nc_connect_tls("0.0.0.0", 6001, NULL);
assert_non_null(session);
nc_session_free(session, NULL);
}
static void
test_nc_client_tls_setting_crl_paths(void **state)
{
(void)state;
const char *file, *dir;
int ret;
nc_client_tls_get_crl_paths(&file, &dir);
assert_null(file);
assert_null(dir);
ret = nc_client_tls_set_crl_paths("file", "dir");
assert_int_equal(ret, 0);
nc_client_tls_get_crl_paths(&file, &dir);
assert_string_equal(file, "file");
assert_string_equal(dir, "dir");
ret = nc_client_tls_set_crl_paths("file1", "dir1");
assert_int_equal(ret, 0);
nc_client_tls_get_crl_paths(&file, &dir);
assert_string_equal(file, "file1");
assert_string_equal(dir, "dir1");
/* destroy client */
nc_client_destroy();
}
static void
test_nc_connect_tls_handshake_failed(void **state)
{
(void)state;
int ret;
struct nc_session *session;
ret = nc_client_tls_set_cert_key_paths(TESTS_DIR "/data/client.crt", TESTS_DIR "/data/client.key");
assert_int_equal(ret, 0);
ret = nc_client_tls_set_trusted_ca_paths(NULL, TESTS_DIR "/data");
assert_int_equal(ret, 0);
will_return(__wrap_connect, 0);
will_return(__wrap_SSL_connect, 1);
/* fake failed handshake */
will_return(__wrap_nc_handshake_io, 0);
session = nc_connect_tls("0.0.0.0", 6001, NULL);
assert_null(session);
}
int
main(void)
{
const struct CMUnitTest tests[] = {
cmocka_unit_test_setup_teardown(test_nc_client_tls_setting_cert_key_paths, setup_f, teardown_f),
cmocka_unit_test_setup_teardown(test_nc_connect_tls_handshake_failed, setup_f, teardown_f),
cmocka_unit_test_setup_teardown(test_nc_connect_tls_succesfull, setup_f, teardown_f),
cmocka_unit_test_setup_teardown(test_nc_client_tls_setting_trusted_ca_paths, setup_f, teardown_f),
cmocka_unit_test_setup_teardown(test_nc_client_tls_setting_crl_paths, setup_f, teardown_f),
};
return cmocka_run_group_tests(tests, NULL, NULL);
}

View file

@ -19,6 +19,24 @@
#endif
#define TESTS_DIR "@CMAKE_SOURCE_DIR@/tests"
#define MODULES_DIR "@CMAKE_SOURCE_DIR@/modules"
#define BUILD_DIR "@CMAKE_BINARY_DIR@"
#cmakedefine HAVE_MBEDTLS
@SSH_MACRO@
@TLS_MACRO@
/* nc_server.h local includes (not to use the installed ones) */
#include "netconf.h"
#include "log.h"
#include "messages_server.h"
#include "server_config.h"
#include "session_server.h"
#include "session_server_ch.h"
/* nc_client.h local includes (not to use the installed ones) */
#include "messages_client.h"
#include "session_client.h"
#include "session_client_ch.h"

1
tests/data/0b527f1f.0 Symbolic link
View file

@ -0,0 +1 @@
ec_serverca.pem

View file

@ -0,0 +1,2 @@
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOr46rptg6BsWhO1JMomuh3cuCYmeuO6JfOUPs/YO35w test@libnetconf2
ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBIY62KY++anyNuwrmQu2jQdyB9pYZMGEGyf5zl15jRJC7OGPq+TGqRgwFOoBylYtuPrFWGlM+zktqUMn19+5qPo= test@libnetconf2

12
tests/data/crl.pem Normal file
View file

@ -0,0 +1,12 @@
-----BEGIN X509 CRL-----
MIIB2zCBxAIBATANBgkqhkiG9w0BAQsFADBjMQswCQYDVQQGEwJDWjETMBEGA1UE
CAwKU29tZS1TdGF0ZTENMAsGA1UEBwwEQnJubzEPMA0GA1UECgwGQ0VTTkVUMQww
CgYDVQQLDANUTUMxETAPBgNVBAMMCHNlcnZlcmNhFw0yMzA2MTUxMTMxMzBaFw0z
MzA2MTIxMTMxMzBaMBwwGgIJAJXrkmAO99aQFw0yMzA2MTIxNDAzMTJaoA8wDTAL
BgNVHRQEBAICEAUwDQYJKoZIhvcNAQELBQADggEBAMbxn/kkds6i60o31eQvaI49
XXQiEnZ+15r8ehkeKv4VOQBeHbddrYuhoaESdVLeB2wzphbLLPAOsUvqHxtM4eO2
faDDhpquHL3eKsHZA5UyAl9vEHyzONXiSoJvVNvC5dtQHflgUtqKwOnhrvxlUBqV
pcwVWfqE+opm19f8iNaA4OgFn9dFyaRnpgzWILGOykzW+aEzmCpkVYeUgR1RqBm5
bgpzQBma2Mg9AjWIeTwGozoANt/Q7GJhdmeZEp41iDc1HElFMUyYspClaYfRKzuE
VvrHeKOFQIjhQcoPO6oH5QwYgXKUBFuTJD5who3hHbu+6u3upaJ6RZGcwVa+Ly8=
-----END X509 CRL-----

53
tests/data/ec_server.crt Normal file
View file

@ -0,0 +1,53 @@
Certificate:
Data:
Version: 1 (0x0)
Serial Number: 3 (0x3)
Signature Algorithm: sha256WithRSAEncryption
Issuer: C=CZ, ST=Some-State, O=CESNET, OU=TMC, CN=clientca
Validity
Not Before: Nov 13 09:26:01 2024 GMT
Not After : Nov 11 09:26:01 2034 GMT
Subject: C=CZ, ST=Some-State, O=CESNET, OU=TMC, CN=127.0.0.1
Subject Public Key Info:
Public Key Algorithm: id-ecPublicKey
Public-Key: (256 bit)
pub:
04:21:8f:4c:09:ed:6c:ef:8e:17:09:f6:71:15:df:
6b:bc:55:ee:62:b8:06:66:b0:83:d0:31:6a:58:eb:
ca:1d:ed:3a:d7:a5:35:f9:c1:83:e7:2a:e7:3a:0b:
a8:0d:8e:d8:48:91:44:f0:33:70:a2:a4:fa:14:b7:
6e:74:cf:e3:13
ASN1 OID: prime256v1
NIST CURVE: P-256
Signature Algorithm: sha256WithRSAEncryption
Signature Value:
c1:b3:2c:22:63:44:7e:ed:bc:59:0b:88:36:a6:a3:b5:9c:13:
25:e9:35:17:a8:ed:51:a6:54:98:46:fa:68:cf:82:af:85:1e:
66:ff:86:64:f6:b7:cb:2f:2a:7d:f1:f0:f0:5c:85:40:86:99:
0f:12:2f:7c:14:9b:27:25:ed:6b:5a:a7:80:8b:8e:e0:17:7b:
d0:a0:45:aa:d3:6d:b8:8b:cc:46:c7:b7:01:8b:fa:bc:2e:5d:
18:77:c0:87:9b:37:16:a1:b0:3c:cc:72:44:4a:3e:c3:0f:6f:
60:5e:ae:a1:0d:08:54:49:96:f0:aa:84:9a:00:da:63:bd:0a:
fb:d7:93:3e:8a:e5:c0:64:31:01:c7:14:47:0f:94:d4:4e:c9:
c2:3d:28:7b:18:60:64:c7:d1:1f:f8:47:86:f5:68:ea:bf:e6:
b5:f1:43:19:e1:55:c1:20:73:7e:71:9f:9e:08:9b:7c:4c:5c:
61:62:6f:3f:64:1d:d6:f2:52:42:fe:a6:c9:5d:ce:24:8a:f8:
d7:2b:a6:0f:ca:ec:4a:92:da:31:f3:d3:fd:01:5f:ea:2a:c5:
d6:0e:b0:04:43:f9:60:71:e4:42:6d:43:34:d2:9b:31:59:9e:
c8:b9:6b:b2:67:0a:ff:fb:f4:a7:27:ec:c0:2d:83:b0:1e:03:
9d:a0:05:f8
-----BEGIN CERTIFICATE-----
MIICUjCCAToCAQMwDQYJKoZIhvcNAQELBQAwVDELMAkGA1UEBhMCQ1oxEzARBgNV
BAgMClNvbWUtU3RhdGUxDzANBgNVBAoMBkNFU05FVDEMMAoGA1UECwwDVE1DMREw
DwYDVQQDDAhjbGllbnRjYTAeFw0yNDExMTMwOTI2MDFaFw0zNDExMTEwOTI2MDFa
MFUxCzAJBgNVBAYTAkNaMRMwEQYDVQQIDApTb21lLVN0YXRlMQ8wDQYDVQQKDAZD
RVNORVQxDDAKBgNVBAsMA1RNQzESMBAGA1UEAwwJMTI3LjAuMC4xMFkwEwYHKoZI
zj0CAQYIKoZIzj0DAQcDQgAEIY9MCe1s744XCfZxFd9rvFXuYrgGZrCD0DFqWOvK
He0616U1+cGD5yrnOguoDY7YSJFE8DNwoqT6FLdudM/jEzANBgkqhkiG9w0BAQsF
AAOCAQEAwbMsImNEfu28WQuINqajtZwTJek1F6jtUaZUmEb6aM+Cr4UeZv+GZPa3
yy8qffHw8FyFQIaZDxIvfBSbJyXta1qngIuO4Bd70KBFqtNtuIvMRse3AYv6vC5d
GHfAh5s3FqGwPMxyREo+ww9vYF6uoQ0IVEmW8KqEmgDaY70K+9eTPorlwGQxAccU
Rw+U1E7Jwj0oexhgZMfRH/hHhvVo6r/mtfFDGeFVwSBzfnGfngibfExcYWJvP2Qd
1vJSQv6myV3OJIr41yumD8rsSpLaMfPT/QFf6irF1g6wBEP5YHHkQm1DNNKbMVme
yLlrsmcK//v0pyfswC2DsB4DnaAF+A==
-----END CERTIFICATE-----

5
tests/data/ec_server.key Normal file
View file

@ -0,0 +1,5 @@
-----BEGIN EC PRIVATE KEY-----
MHcCAQEEIFIFZl3hkDpo1uqLRK8UeFo9Tm6tfgBjlvM1TcRZixy+oAoGCCqGSM49
AwEHoUQDQgAEIY9MCe1s744XCfZxFd9rvFXuYrgGZrCD0DFqWOvKHe0616U1+cGD
5yrnOguoDY7YSJFE8DNwoqT6FLdudM/jEw==
-----END EC PRIVATE KEY-----

View file

@ -0,0 +1,78 @@
Certificate:
Data:
Version: 3 (0x2)
Serial Number: 0 (0x0)
Signature Algorithm: sha256WithRSAEncryption
Issuer: C=CZ, ST=Some-State, O=CESNET, OU=TMC, CN=clientca
Validity
Not Before: Oct 25 11:00:37 2024 GMT
Not After : Oct 23 11:00:37 2034 GMT
Subject: C=CZ, ST=Some-State, O=CESNET, OU=TMC, CN=clientca
Subject Public Key Info:
Public Key Algorithm: rsaEncryption
Public-Key: (2048 bit)
Modulus:
00:d3:59:56:1b:a0:75:ce:50:66:ce:60:77:69:87:
8f:bc:6a:42:83:6c:64:49:89:10:54:59:2a:cd:c7:
85:83:a2:dd:66:33:72:e9:2f:46:54:9c:a0:8b:f7:
c7:76:01:d4:be:2a:54:6a:63:24:2a:65:a0:90:fe:
63:71:33:ce:76:76:37:fa:6f:1c:66:86:d9:19:1f:
2a:72:a4:ac:f9:56:95:58:d0:f5:c3:1d:c0:a9:c2:
fe:89:cb:ac:04:18:a0:fa:14:eb:18:42:46:7b:fc:
fe:a6:b6:26:70:c0:45:c4:79:9c:53:b9:0b:71:d4:
c8:74:93:86:80:a5:76:38:16:0e:7f:a5:2e:bc:c4:
4f:e5:7a:cd:ef:41:0b:02:9e:3d:f0:d8:62:aa:2c:
89:68:51:22:44:6a:c2:2f:bc:77:10:20:38:dd:f0:
5b:cb:31:a2:3c:9e:27:a2:3f:d1:61:25:14:35:05:
ab:10:0f:f1:f9:49:40:e5:16:8f:e3:69:32:51:f9:
01:20:ce:b1:18:e7:1f:11:76:ec:3c:74:f7:99:bd:
a1:4e:53:6f:89:a4:95:6a:73:ae:6d:9a:7e:f3:78:
11:df:bd:89:5b:e6:a1:c1:0b:92:57:ba:ba:6d:b0:
8e:d7:5c:60:c0:ae:ca:e0:6d:31:6b:07:f1:98:8a:
66:2f
Exponent: 65537 (0x10001)
X509v3 extensions:
X509v3 Basic Constraints: critical
CA:TRUE
X509v3 Key Usage: critical
Certificate Sign
X509v3 Subject Key Identifier:
CD:59:B8:BB:EB:BA:27:B2:66:3C:1C:05:76:9B:71:8A:68:EA:30:FD
Signature Algorithm: sha256WithRSAEncryption
Signature Value:
a6:2d:e1:9e:04:a5:0c:9d:6b:82:b9:f2:59:85:9f:ef:e4:ea:
eb:b9:ab:70:73:50:fa:1d:5d:0b:d7:7f:3e:32:f6:e5:27:01:
47:69:3a:a2:a2:d2:e0:4d:16:ad:9d:98:3c:ed:81:05:c6:12:
a1:92:85:95:7f:22:e7:d2:77:fe:53:be:fe:2c:74:2c:24:7b:
66:97:8c:0b:00:88:3e:96:87:1c:6a:0e:70:98:81:10:c1:84:
f4:98:4b:60:77:9c:24:a7:b2:a5:44:e8:05:da:a5:6c:62:77:
68:f9:2e:73:3c:c6:2c:ad:3a:ff:4a:67:a0:da:23:84:ea:bc:
d9:cb:f9:45:13:e3:38:26:c7:f1:60:95:f3:3f:2f:81:98:0b:
58:60:72:5f:c9:ef:1f:76:b2:05:03:8d:4f:3a:a8:eb:0a:c5:
a8:fd:a3:5f:a8:29:83:cb:9e:cb:13:24:a6:4a:33:95:22:fc:
26:90:dc:97:2c:53:ac:24:1a:60:d6:aa:e4:cd:14:12:84:61:
ea:15:28:5a:79:f3:18:1e:bb:77:03:61:2c:b4:b6:d5:c5:99:
7c:a7:7c:8a:1b:c8:a0:2c:50:53:5d:fb:b4:81:23:bf:0a:b1:
9f:f0:b0:d3:ed:08:e2:4d:a7:50:44:be:3a:a0:c0:2c:70:0c:
e4:c8:71:15
-----BEGIN CERTIFICATE-----
MIIDZTCCAk2gAwIBAgIBADANBgkqhkiG9w0BAQsFADBUMQswCQYDVQQGEwJDWjET
MBEGA1UECAwKU29tZS1TdGF0ZTEPMA0GA1UECgwGQ0VTTkVUMQwwCgYDVQQLDANU
TUMxETAPBgNVBAMMCGNsaWVudGNhMB4XDTI0MTAyNTExMDAzN1oXDTM0MTAyMzEx
MDAzN1owVDELMAkGA1UEBhMCQ1oxEzARBgNVBAgMClNvbWUtU3RhdGUxDzANBgNV
BAoMBkNFU05FVDEMMAoGA1UECwwDVE1DMREwDwYDVQQDDAhjbGllbnRjYTCCASIw
DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANNZVhugdc5QZs5gd2mHj7xqQoNs
ZEmJEFRZKs3HhYOi3WYzcukvRlScoIv3x3YB1L4qVGpjJCploJD+Y3EzznZ2N/pv
HGaG2RkfKnKkrPlWlVjQ9cMdwKnC/onLrAQYoPoU6xhCRnv8/qa2JnDARcR5nFO5
C3HUyHSThoCldjgWDn+lLrzET+V6ze9BCwKePfDYYqosiWhRIkRqwi+8dxAgON3w
W8sxojyeJ6I/0WElFDUFqxAP8flJQOUWj+NpMlH5ASDOsRjnHxF27Dx095m9oU5T
b4mklWpzrm2afvN4Ed+9iVvmocELkle6um2wjtdcYMCuyuBtMWsH8ZiKZi8CAwEA
AaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAgQwHQYDVR0OBBYE
FM1ZuLvruieyZjwcBXabcYpo6jD9MA0GCSqGSIb3DQEBCwUAA4IBAQCmLeGeBKUM
nWuCufJZhZ/v5Orruatwc1D6HV0L138+MvblJwFHaTqiotLgTRatnZg87YEFxhKh
koWVfyLn0nf+U77+LHQsJHtml4wLAIg+loccag5wmIEQwYT0mEtgd5wkp7KlROgF
2qVsYndo+S5zPMYsrTr/Smeg2iOE6rzZy/lFE+M4JsfxYJXzPy+BmAtYYHJfye8f
drIFA41POqjrCsWo/aNfqCmDy57LEySmSjOVIvwmkNyXLFOsJBpg1qrkzRQShGHq
FShaefMYHrt3A2EstLbVxZl8p3yKG8igLFBTXfu0gSO/CrGf8LDT7QjiTadQRL46
oMAscAzkyHEV
-----END CERTIFICATE-----

9
tests/data/id_ecdsa256 Normal file
View file

@ -0,0 +1,9 @@
-----BEGIN OPENSSH PRIVATE KEY-----
b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAaAAAABNlY2RzYS
1zaGEyLW5pc3RwMjU2AAAACG5pc3RwMjU2AAAAQQSGOtimPvmp8jbsK5kLto0HcgfaWGTB
hBsn+c5deY0SQuzhj6vkxqkYMBTqAcpWLbj6xVhpTPs5LalDJ9ffuaj6AAAAqCh4tAQoeL
QEAAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBIY62KY++anyNuwr
mQu2jQdyB9pYZMGEGyf5zl15jRJC7OGPq+TGqRgwFOoBylYtuPrFWGlM+zktqUMn19+5qP
oAAAAgBf3u7SBWmpDCm7esp1VnpoflXGytRAxp85nsb4Hhbd0AAAANcm9tYW5AcGN2YXNr
bwECAw==
-----END OPENSSH PRIVATE KEY-----

View file

@ -0,0 +1 @@
ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBIY62KY++anyNuwrmQu2jQdyB9pYZMGEGyf5zl15jRJC7OGPq+TGqRgwFOoBylYtuPrFWGlM+zktqUMn19+5qPo= test@libnetconf2

10
tests/data/id_ecdsa384 Normal file
View file

@ -0,0 +1,10 @@
-----BEGIN OPENSSH PRIVATE KEY-----
b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAiAAAABNlY2RzYS
1zaGEyLW5pc3RwMzg0AAAACG5pc3RwMzg0AAAAYQQSz1JFAWiAmtKwWSyCXdZUwNTS/m+c
CpiUPC3vHZ82S1g2ihbQpN4IAIAAfRTER+rnV5/qClDmCjBsOSIaw86VcIykwAqcNy0x0i
vGoyfpVL4/9CZfVSf/hwITxfbCK5MAAADYCMDKzgjAys4AAAATZWNkc2Etc2hhMi1uaXN0
cDM4NAAAAAhuaXN0cDM4NAAAAGEEEs9SRQFogJrSsFksgl3WVMDU0v5vnAqYlDwt7x2fNk
tYNooW0KTeCACAAH0UxEfq51ef6gpQ5gowbDkiGsPOlXCMpMAKnDctMdIrxqMn6VS+P/Qm
X1Un/4cCE8X2wiuTAAAAMCpWDy26Bm4HFbLLgn/Jf8iJ/V5V5RXOnW994Jbh2w2b0gT9c5
CfLHjENUhHF3zI9QAAAA1yb21hbkBwY3Zhc2tvAQID
-----END OPENSSH PRIVATE KEY-----

View file

@ -0,0 +1 @@
ecdsa-sha2-nistp384 AAAAE2VjZHNhLXNoYTItbmlzdHAzODQAAAAIbmlzdHAzODQAAABhBBLPUkUBaICa0rBZLIJd1lTA1NL+b5wKmJQ8Le8dnzZLWDaKFtCk3ggAgAB9FMRH6udXn+oKUOYKMGw5IhrDzpVwjKTACpw3LTHSK8ajJ+lUvj/0Jl9VJ/+HAhPF9sIrkw== test@libnetconf2

12
tests/data/id_ecdsa521 Normal file
View file

@ -0,0 +1,12 @@
-----BEGIN OPENSSH PRIVATE KEY-----
b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAArAAAABNlY2RzYS
1zaGEyLW5pc3RwNTIxAAAACG5pc3RwNTIxAAAAhQQBdWbNJ1cbOVb9jkjFVe3ef8hM2Jc3
Fgrx1uScbWjzRyLFELbVxgMdQhVShobGyl28Sw4tQPXBPk2iJuZ1is8Jk0gA89GJqKLAbI
O6MVOc6Swx3jR9VP0jOxEWN0dt+swYkZYPNsmSCtVl49w+li+759b5jIWUKNX+jwLua0DG
oykzIy4AAAEQGOm95hjpveYAAAATZWNkc2Etc2hhMi1uaXN0cDUyMQAAAAhuaXN0cDUyMQ
AAAIUEAXVmzSdXGzlW/Y5IxVXt3n/ITNiXNxYK8dbknG1o80cixRC21cYDHUIVUoaGxspd
vEsOLUD1wT5NoibmdYrPCZNIAPPRiaiiwGyDujFTnOksMd40fVT9IzsRFjdHbfrMGJGWDz
bJkgrVZePcPpYvu+fW+YyFlCjV/o8C7mtAxqMpMyMuAAAAQgG2S26e7KJI+Old8/A2JPPz
9Lbtwgjb09LYZhkRzCELq/9yjY3HvBEOFF3c5WbEn+Opn+MJP1JmQ5UxEUPybDl+egAAAA
1yb21hbkBwY3Zhc2tvAQIDBAU=
-----END OPENSSH PRIVATE KEY-----

View file

@ -0,0 +1 @@
ecdsa-sha2-nistp521 AAAAE2VjZHNhLXNoYTItbmlzdHA1MjEAAAAIbmlzdHA1MjEAAACFBAF1Zs0nVxs5Vv2OSMVV7d5/yEzYlzcWCvHW5JxtaPNHIsUQttXGAx1CFVKGhsbKXbxLDi1A9cE+TaIm5nWKzwmTSADz0YmoosBsg7oxU5zpLDHeNH1U/SM7ERY3R236zBiRlg82yZIK1WXj3D6WL7vn1vmMhZQo1f6PAu5rQMajKTMjLg== test@libnetconf2

7
tests/data/id_ed25519 Normal file
View file

@ -0,0 +1,7 @@
-----BEGIN OPENSSH PRIVATE KEY-----
b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW
QyNTUxOQAAACDq+Oq6bYOgbFoTtSTKJrod3LgmJnrjuiXzlD7P2Dt+cAAAAJC1rL1gtay9
YAAAAAtzc2gtZWQyNTUxOQAAACDq+Oq6bYOgbFoTtSTKJrod3LgmJnrjuiXzlD7P2Dt+cA
AAAEAQm84SEphEUZEbuCRmXrMcYyv70wNEVziE/SbBC6+trOr46rptg6BsWhO1JMomuh3c
uCYmeuO6JfOUPs/YO35wAAAADXJvbWFuQHBjdmFza28=
-----END OPENSSH PRIVATE KEY-----

View file

@ -0,0 +1 @@
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOr46rptg6BsWhO1JMomuh3cuCYmeuO6JfOUPs/YO35w test@libnetconf2

View file

@ -1,12 +0,0 @@
-----BEGIN DSA PRIVATE KEY-----
MIIBugIBAAKBgQC5UysgLVJXTBaY4hPuLBBmxsOkC9VzFKbH581ufLqKxBZlQhcn
OZ4DaQWXUvMXOmDTnG+ROpjbXBMxaYRmCwrUL9Gz0JeTYuqE413D1XYpQYZH5oJH
7SyIe4R+mlCdORekeSKdY2oJ9dREVVe0WXiAzrBV+Eg5Ve3uWQkYw3HAMwIVAMOo
rc88si96YqcYSPf//761FJtVAoGANwUJNODSvguHU6x55+UTGHLyWnbIqd/nbUn1
cnCRa4xeHSHq0rBayoHh3hJiqqpcdRpb0lVoYNe51HmFJHJUGLHtYcxgnqdvIF5K
QiP5h1ESxYqb4v4AXHl5+CVyC0Yp/hkowqaNfVqyOgol/IhNlJknZDCMxWUD9NUJ
iiV5oc0CgYACU3qrETmgEHbwx5kCKN1/Ly4pWzS5rNg764aYsU0wE714TfYs5nOf
yEvQYkfBDb+rEpGyKot6ZvDsHvL0WVVVx7mIDSKnzHAYwYGl1wKNHlLenOMZIDRT
43AKfTz03wRkCrzBl2fAmLLq7wFaXcDDxaBg4zN2CDbuHjmJgRuwzAIUBRb7QH4c
p2gVuWcmRuuI9Qexmzc=
-----END DSA PRIVATE KEY-----

View file

@ -1 +0,0 @@
ssh-dss AAAAB3NzaC1kc3MAAACBALlTKyAtUldMFpjiE+4sEGbGw6QL1XMUpsfnzW58uorEFmVCFyc5ngNpBZdS8xc6YNOcb5E6mNtcEzFphGYLCtQv0bPQl5Ni6oTjXcPVdilBhkfmgkftLIh7hH6aUJ05F6R5Ip1jagn11ERVV7RZeIDOsFX4SDlV7e5ZCRjDccAzAAAAFQDDqK3PPLIvemKnGEj3//++tRSbVQAAAIA3BQk04NK+C4dTrHnn5RMYcvJadsip3+dtSfVycJFrjF4dIerSsFrKgeHeEmKqqlx1GlvSVWhg17nUeYUkclQYse1hzGCep28gXkpCI/mHURLFipvi/gBceXn4JXILRin+GSjCpo19WrI6CiX8iE2UmSdkMIzFZQP01QmKJXmhzQAAAIACU3qrETmgEHbwx5kCKN1/Ly4pWzS5rNg764aYsU0wE714TfYs5nOfyEvQYkfBDb+rEpGyKot6ZvDsHvL0WVVVx7mIDSKnzHAYwYGl1wKNHlLenOMZIDRT43AKfTz03wRkCrzBl2fAmLLq7wFaXcDDxaBg4zN2CDbuHjmJgRuwzA== vasko@pcvasko

View file

@ -1,10 +0,0 @@
{
ld
Memcheck:Leak
match-leak-kinds: reachable
fun:calloc
fun:_dlerror_run
fun:dlopen@@GLIBC_2.2.5
fun:lyext_load_plugins
fun:ly_ctx_new
}

1
tests/library_lsan.supp Normal file
View file

@ -0,0 +1 @@
leak:libpam.so

View file

@ -0,0 +1,27 @@
{
ld
Memcheck:Leak
match-leak-kinds: reachable
fun:calloc
fun:_dlerror_run
fun:dlopen@@GLIBC_2.2.5
fun:lyext_load_plugins
fun:ly_ctx_new
}
{
CI:test_pam:__wrap_pam_start
Memcheck:Leak
match-leak-kinds: definite
fun:malloc
...
fun:ln2_glob_test_server_thread
fun:ln2_glob_test_server_thread
}
{
test_pam:__wrap_pam_start
Memcheck:Leak
match-leak-kinds: definite
fun:malloc
...
fun:ln2_glob_test_server_thread
}

182
tests/ln2_test.c Normal file
View file

@ -0,0 +1,182 @@
/**
* @file ln2_test.c
* @author Roman Janota <janota@cesnet.cz>
* @brief base source for libnetconf2 testing
*
* @copyright
* Copyright (c) 2024 CESNET, z.s.p.o.
*
* This source code is licensed under BSD 3-Clause License (the "License").
* You may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://opensource.org/licenses/BSD-3-Clause
*/
#define _GNU_SOURCE
#include <assert.h>
#include <pthread.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "ln2_test.h"
int
ln2_glob_test_get_ports(int port_count, ...)
{
va_list ap;
int i, ret = 0, *port_ptr;
const char **port_str_ptr, *env;
char *env_name = NULL;
va_start(ap, port_count);
for (i = 0; i < port_count; i++) {
port_ptr = va_arg(ap, int *);
port_str_ptr = va_arg(ap, const char **);
if (asprintf(&env_name, "TEST_PORT_%d", i) == -1) {
ret = 1;
goto cleanup;
}
/* try to get the env variable, which is set by CTest */
env = getenv(env_name);
free(env_name);
if (!env) {
/* the default value will be used instead */
continue;
}
*port_ptr = atoi(env);
*port_str_ptr = env;
}
cleanup:
va_end(ap);
return ret;
}
void *
ln2_glob_test_server_thread(void *arg)
{
int ret;
NC_MSG_TYPE msgtype;
struct nc_session *session = NULL;
struct nc_pollsession *ps = NULL;
struct ln2_test_ctx *test_ctx = arg;
ps = nc_ps_new();
assert(ps);
/* wait for the client to be ready to connect */
pthread_barrier_wait(&test_ctx->barrier);
/* accept a session and add it to the poll session structure */
msgtype = nc_accept(NC_ACCEPT_TIMEOUT, test_ctx->ctx, &session);
if (msgtype != NC_MSG_HELLO) {
SETUP_FAIL_LOG;
nc_ps_free(ps);
return NULL;
}
ret = nc_ps_add_session(ps, session);
assert(!ret);
/* poll until the session is terminated by the client */
do {
ret = nc_ps_poll(ps, NC_PS_POLL_TIMEOUT, NULL);
assert(ret & NC_PSPOLL_RPC);
} while (!(ret & NC_PSPOLL_SESSION_TERM));
nc_ps_clear(ps, 1, NULL);
nc_ps_free(ps);
return NULL;
}
int
ln2_glob_test_setup(struct ln2_test_ctx **test_ctx)
{
int ret;
*test_ctx = calloc(1, sizeof **test_ctx);
if (!*test_ctx) {
SETUP_FAIL_LOG;
ret = 1;
goto cleanup;
}
/* set verbosity */
nc_verbosity(NC_VERB_VERBOSE);
/* initialize server */
ret = nc_server_init();
if (ret) {
SETUP_FAIL_LOG;
goto cleanup;
}
/* initialize client */
ret = nc_client_init();
if (ret) {
SETUP_FAIL_LOG;
goto cleanup;
}
/* init barrier */
ret = pthread_barrier_init(&(*test_ctx)->barrier, NULL, 2);
if (ret) {
SETUP_FAIL_LOG;
goto cleanup;
}
/* create libyang context */
ret = ly_ctx_new(MODULES_DIR, 0, &(*test_ctx)->ctx);
if (ret) {
SETUP_FAIL_LOG;
goto cleanup;
}
/* load default yang modules */
ret = nc_server_init_ctx(&(*test_ctx)->ctx);
if (ret) {
SETUP_FAIL_LOG;
goto cleanup;
}
ret = nc_server_config_load_modules(&(*test_ctx)->ctx);
if (ret) {
SETUP_FAIL_LOG;
goto cleanup;
}
cleanup:
return ret;
}
int
ln2_glob_test_teardown(void **state)
{
struct ln2_test_ctx *test_ctx = *state;
nc_client_destroy();
nc_server_destroy();
if (test_ctx->free_test_data) {
test_ctx->free_test_data(test_ctx->test_data);
}
pthread_barrier_destroy(&test_ctx->barrier);
ly_ctx_destroy(test_ctx->ctx);
free(test_ctx);
return 0;
}
void
ln2_glob_test_free_test_data(void *test_data)
{
free(test_data);
}

80
tests/ln2_test.h Normal file
View file

@ -0,0 +1,80 @@
/**
* @file ln2_test.h
* @author Roman Janota <janota@cesnet.cz>
* @brief base header for libnetconf2 testing
*
* @copyright
* Copyright (c) 2024 CESNET, z.s.p.o.
*
* This source code is licensed under BSD 3-Clause License (the "License").
* You may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://opensource.org/licenses/BSD-3-Clause
*/
#ifndef _LN2_TEST_H_
#define _LN2_TEST_H_
#include <pthread.h>
#include <stdarg.h>
#include "tests/config.h"
#define NC_ACCEPT_TIMEOUT 2000
#define NC_PS_POLL_TIMEOUT 2000
#define SETUP_FAIL_LOG \
fprintf(stderr, "Setup fail in %s:%d.\n", __FILE__, __LINE__)
/**
* @brief Test context used for sharing data between the test and the server/client threads.
*/
struct ln2_test_ctx {
pthread_barrier_t barrier; /**< Barrier for synchronizing the client and the server. */
struct ly_ctx *ctx; /**< libyang context. */
void *test_data; /**< Arbitrary test data. */
void (*free_test_data)(void *); /**< Callback for freeing the test data. */
};
/**
* @brief Try to obtain ports from the TEST_PORT_X environment variables.
*
* @param[in] port_count Number of ports needed by the test.
* @param[in] ... @p port_count number of (int *, const char **) pairs, which will be filled with the port numbers.
* @return 0 on success, 1 on error.
*/
int ln2_glob_test_get_ports(int port_count, ...);
/**
* @brief Default server thread for the tests.
*
* @param[in] arg Test context.
* @return NULL.
*/
void * ln2_glob_test_server_thread(void *arg);
/**
* @brief Default setup of the test context (init server, client, libyang context and a barrier).
*
* @param[out] test_ctx Test context.
* @return 0 on success, non-zero on error.
*/
int ln2_glob_test_setup(struct ln2_test_ctx **test_ctx);
/**
* @brief Default teardown of the test context (destroy server, client, test data, libyang context and a barrier).
*
* @param[in] state Test context.
* @return 0.
*/
int ln2_glob_test_teardown(void **state);
/**
* @brief Default callback for freeing test data.
*
* @param[in] test_data Test data.
*/
void ln2_glob_test_free_test_data(void *test_data);
#endif

311
tests/pam/pam_netconf.c Normal file
View file

@ -0,0 +1,311 @@
/**
* @file pam_netconf.c
* @author Roman Janota <xjanot04@fit.vutbr.cz>
* @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 <security/pam_modules.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#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")) {
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")) {
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");
if (r == PAM_SUCCESS) {
printf("[TEST #6] Passed.\n\n");
} else {
fprintf(stderr, "[TEST #6] Failed.\n");
}
return r;
}
return PAM_SYSTEM_ERR;
}

354
tests/test_auth_ssh.c Normal file
View file

@ -0,0 +1,354 @@
/**
* @file test_auth_ssh.c
* @author Roman Janota <janota@cesnet.cz>
* @brief libnetconf2 SSH authentication test
*
* @copyright
* Copyright (c) 2023 - 2024 CESNET, z.s.p.o.
*
* This source code is licensed under BSD 3-Clause License (the "License").
* You may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://opensource.org/licenses/BSD-3-Clause
*/
#define _GNU_SOURCE
#include <errno.h>
#include <pthread.h>
#include <setjmp.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <cmocka.h>
#include "ln2_test.h"
struct test_auth_ssh_data {
const char *username;
const char *pubkey_path;
const char *privkey_path;
int check_banner;
};
int TEST_PORT = 10050;
const char *TEST_PORT_STR = "10050";
static char *
auth_password(const char *username, const char *hostname, void *priv)
{
(void) hostname;
(void) priv;
/* set the reply to password authentication */
if (!strcmp(username, "test_pw")) {
return strdup("testpw");
} else {
return NULL;
}
}
static void
check_banner(const struct nc_session *session)
{
const char *banner;
banner = nc_session_ssh_get_banner(session);
assert_non_null(banner);
assert_string_equal(banner, "SSH-2.0-test-banner");
}
static void *
client_thread_ssh(void *arg)
{
int ret;
struct nc_session *session = NULL;
struct ln2_test_ctx *test_ctx = arg;
struct test_auth_ssh_data *test_data = test_ctx->test_data;
/* 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_data->username);
assert_int_equal(ret, 0);
if (test_data->pubkey_path) {
ret = nc_client_ssh_add_keypair(test_data->pubkey_path, test_data->privkey_path);
assert_int_equal(ret, 0);
} else {
nc_client_ssh_set_auth_password_clb(auth_password, NULL);
}
pthread_barrier_wait(&test_ctx->barrier);
session = nc_connect_ssh("127.0.0.1", TEST_PORT, NULL);
assert_non_null(session);
if (test_data->check_banner) {
check_banner(session);
}
nc_session_free(session, NULL);
return NULL;
}
static void
test_nc_auth_ssh_password(void **state)
{
int ret, i;
pthread_t tids[2];
struct ln2_test_ctx *test_ctx = *state;
struct test_auth_ssh_data *test_data = test_ctx->test_data;
test_data->username = "test_pw";
ret = pthread_create(&tids[0], NULL, client_thread_ssh, *state);
assert_int_equal(ret, 0);
ret = pthread_create(&tids[1], NULL, ln2_glob_test_server_thread, *state);
assert_int_equal(ret, 0);
for (i = 0; i < 2; i++) {
pthread_join(tids[i], NULL);
}
}
static void
test_nc_auth_ssh_none(void **state)
{
int ret, i;
pthread_t tids[2];
struct ln2_test_ctx *test_ctx = *state;
struct test_auth_ssh_data *test_data = test_ctx->test_data;
test_data->username = "test_none";
ret = pthread_create(&tids[0], NULL, client_thread_ssh, *state);
assert_int_equal(ret, 0);
ret = pthread_create(&tids[1], NULL, ln2_glob_test_server_thread, *state);
assert_int_equal(ret, 0);
for (i = 0; i < 2; i++) {
pthread_join(tids[i], NULL);
}
}
static void
test_nc_auth_ssh_rsa_pubkey(void **state)
{
int ret, i;
pthread_t tids[2];
struct ln2_test_ctx *test_ctx = *state;
struct test_auth_ssh_data *test_data = test_ctx->test_data;
test_data->username = "test_pk";
test_data->pubkey_path = TESTS_DIR "/data/key_rsa.pub";
test_data->privkey_path = TESTS_DIR "/data/key_rsa";
ret = pthread_create(&tids[0], NULL, client_thread_ssh, *state);
assert_int_equal(ret, 0);
ret = pthread_create(&tids[1], NULL, ln2_glob_test_server_thread, *state);
assert_int_equal(ret, 0);
for (i = 0; i < 2; i++) {
pthread_join(tids[i], NULL);
}
}
static void
test_nc_auth_ssh_ec256_pubkey(void **state)
{
int ret, i;
pthread_t tids[2];
struct ln2_test_ctx *test_ctx = *state;
struct test_auth_ssh_data *test_data = test_ctx->test_data;
test_data->username = "test_ec256";
test_data->pubkey_path = TESTS_DIR "/data/id_ecdsa256.pub";
test_data->privkey_path = TESTS_DIR "/data/id_ecdsa256";
ret = pthread_create(&tids[0], NULL, client_thread_ssh, *state);
assert_int_equal(ret, 0);
ret = pthread_create(&tids[1], NULL, ln2_glob_test_server_thread, *state);
assert_int_equal(ret, 0);
for (i = 0; i < 2; i++) {
pthread_join(tids[i], NULL);
}
}
static void
test_nc_auth_ssh_ec384_pubkey(void **state)
{
int ret, i;
pthread_t tids[2];
struct ln2_test_ctx *test_ctx = *state;
struct test_auth_ssh_data *test_data = test_ctx->test_data;
test_data->username = "test_ec384";
test_data->pubkey_path = TESTS_DIR "/data/id_ecdsa384.pub";
test_data->privkey_path = TESTS_DIR "/data/id_ecdsa384";
ret = pthread_create(&tids[0], NULL, client_thread_ssh, *state);
assert_int_equal(ret, 0);
ret = pthread_create(&tids[1], NULL, ln2_glob_test_server_thread, *state);
assert_int_equal(ret, 0);
for (i = 0; i < 2; i++) {
pthread_join(tids[i], NULL);
}
}
static void
test_nc_auth_ssh_ec521_pubkey(void **state)
{
int ret, i;
pthread_t tids[2];
struct ln2_test_ctx *test_ctx = *state;
struct test_auth_ssh_data *test_data = test_ctx->test_data;
test_data->username = "test_ec521";
test_data->pubkey_path = TESTS_DIR "/data/id_ecdsa521.pub";
test_data->privkey_path = TESTS_DIR "/data/id_ecdsa521";
ret = pthread_create(&tids[0], NULL, client_thread_ssh, *state);
assert_int_equal(ret, 0);
ret = pthread_create(&tids[1], NULL, ln2_glob_test_server_thread, *state);
assert_int_equal(ret, 0);
for (i = 0; i < 2; i++) {
pthread_join(tids[i], NULL);
}
}
static void
test_nc_auth_ssh_ed25519_pubkey(void **state)
{
int ret, i;
pthread_t tids[2];
struct ln2_test_ctx *test_ctx = *state;
struct test_auth_ssh_data *test_data = test_ctx->test_data;
test_data->username = "test_ed25519";
test_data->pubkey_path = TESTS_DIR "/data/id_ed25519.pub";
test_data->privkey_path = TESTS_DIR "/data/id_ed25519";
ret = pthread_create(&tids[0], NULL, client_thread_ssh, *state);
assert_int_equal(ret, 0);
ret = pthread_create(&tids[1], NULL, ln2_glob_test_server_thread, *state);
assert_int_equal(ret, 0);
for (i = 0; i < 2; i++) {
pthread_join(tids[i], NULL);
}
}
static void
test_nc_auth_ssh_banner(void **state)
{
int ret, i;
pthread_t tids[2];
struct ln2_test_ctx *test_ctx = *state;
struct test_auth_ssh_data *test_data = test_ctx->test_data;
test_data->username = "test_ed25519";
test_data->pubkey_path = TESTS_DIR "/data/id_ed25519.pub";
test_data->privkey_path = TESTS_DIR "/data/id_ed25519";
test_data->check_banner = 1;
ret = pthread_create(&tids[0], NULL, client_thread_ssh, *state);
assert_int_equal(ret, 0);
ret = pthread_create(&tids[1], NULL, ln2_glob_test_server_thread, *state);
assert_int_equal(ret, 0);
for (i = 0; i < 2; i++) {
pthread_join(tids[i], NULL);
}
}
static int
setup_ssh(void **state)
{
int ret;
struct lyd_node *tree = NULL;
struct ln2_test_ctx *test_ctx;
struct test_auth_ssh_data *test_data;
ret = ln2_glob_test_setup(&test_ctx);
assert_int_equal(ret, 0);
test_data = calloc(1, sizeof *test_data);
assert_non_null(test_data);
test_ctx->test_data = test_data;
test_ctx->free_test_data = ln2_glob_test_free_test_data;
*state = test_ctx;
ret = nc_server_config_add_address_port(test_ctx->ctx, "endpt", NC_TI_SSH, "127.0.0.1", TEST_PORT, &tree);
assert_int_equal(ret, 0);
ret = nc_server_config_add_ssh_hostkey(test_ctx->ctx, "endpt", "hostkey", TESTS_DIR "/data/key_ecdsa", NULL, &tree);
assert_int_equal(ret, 0);
ret = nc_server_config_add_ssh_user_pubkey(test_ctx->ctx, "endpt", "test_pk", "pubkey", TESTS_DIR "/data/key_rsa.pub", &tree);
assert_int_equal(ret, 0);
ret = nc_server_config_add_ssh_user_pubkey(test_ctx->ctx, "endpt", "test_ec256", "pubkey", TESTS_DIR "/data/id_ecdsa256.pub", &tree);
assert_int_equal(ret, 0);
ret = nc_server_config_add_ssh_user_pubkey(test_ctx->ctx, "endpt", "test_ec384", "pubkey", TESTS_DIR "/data/id_ecdsa384.pub", &tree);
assert_int_equal(ret, 0);
ret = nc_server_config_add_ssh_user_pubkey(test_ctx->ctx, "endpt", "test_ec521", "pubkey", TESTS_DIR "/data/id_ecdsa521.pub", &tree);
assert_int_equal(ret, 0);
ret = nc_server_config_add_ssh_user_pubkey(test_ctx->ctx, "endpt", "test_ed25519", "pubkey", TESTS_DIR "/data/id_ed25519.pub", &tree);
assert_int_equal(ret, 0);
ret = nc_server_config_add_ssh_user_password(test_ctx->ctx, "endpt", "test_pw", "testpw", &tree);
assert_int_equal(ret, 0);
ret = lyd_new_path(tree, test_ctx->ctx, "/ietf-netconf-server:netconf-server/listen/endpoints/endpoint[name='endpt']/ssh/"
"ssh-server-parameters/server-identity/libnetconf2-netconf-server:banner", "test-banner", 0, NULL);
assert_int_equal(ret, 0);
ret = lyd_new_path(tree, test_ctx->ctx, "/ietf-netconf-server:netconf-server/listen/endpoints/endpoint[name='endpt']/ssh/"
"ssh-server-parameters/client-authentication/users/user[name='test_none']/none", NULL, 0, NULL);
assert_int_equal(ret, 0);
/* configure the server based on the data */
ret = nc_server_config_setup_data(tree);
assert_int_equal(ret, 0);
lyd_free_all(tree);
return 0;
}
int
main(void)
{
const struct CMUnitTest tests[] = {
cmocka_unit_test_setup_teardown(test_nc_auth_ssh_password, setup_ssh, ln2_glob_test_teardown),
cmocka_unit_test_setup_teardown(test_nc_auth_ssh_none, setup_ssh, ln2_glob_test_teardown),
cmocka_unit_test_setup_teardown(test_nc_auth_ssh_rsa_pubkey, setup_ssh, ln2_glob_test_teardown),
cmocka_unit_test_setup_teardown(test_nc_auth_ssh_ec256_pubkey, setup_ssh, ln2_glob_test_teardown),
cmocka_unit_test_setup_teardown(test_nc_auth_ssh_ec384_pubkey, setup_ssh, ln2_glob_test_teardown),
cmocka_unit_test_setup_teardown(test_nc_auth_ssh_ec521_pubkey, setup_ssh, ln2_glob_test_teardown),
cmocka_unit_test_setup_teardown(test_nc_auth_ssh_ed25519_pubkey, setup_ssh, ln2_glob_test_teardown),
cmocka_unit_test_setup_teardown(test_nc_auth_ssh_banner, setup_ssh, ln2_glob_test_teardown)
};
/* try to get ports from the environment, otherwise use the default */
if (ln2_glob_test_get_ports(1, &TEST_PORT, &TEST_PORT_STR)) {
return 1;
}
setenv("CMOCKA_TEST_ABORT", "1", 1);
return cmocka_run_group_tests(tests, NULL, NULL);
}

268
tests/test_authkeys.c Normal file
View file

@ -0,0 +1,268 @@
/**
* @file test_authkeys.c
* @author Roman Janota <janota@cesnet.cz>
* @brief libnetconf2 SSH authentication using mocked system authorized_keys
*
* @copyright
* 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.
* You may obtain a copy of the License at
*
* https://opensource.org/licenses/BSD-3-Clause
*/
#define _GNU_SOURCE
#include <errno.h>
#include <pthread.h>
#include <setjmp.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <cmocka.h>
#include "ln2_test.h"
struct test_authkey_data {
const char *pubkey_path;
const char *privkey_path;
int expect_ok;
};
int TEST_PORT = 10050;
const char *TEST_PORT_STR = "10050";
static void *
server_thread(void *arg)
{
int ret;
NC_MSG_TYPE msgtype;
struct nc_session *session;
struct nc_pollsession *ps;
struct ln2_test_ctx *test_ctx = arg;
struct test_authkey_data *test_data = test_ctx->test_data;
ps = nc_ps_new();
assert_non_null(ps);
/* accept a session and add it to the poll session structure */
pthread_barrier_wait(&test_ctx->barrier);
msgtype = nc_accept(NC_ACCEPT_TIMEOUT, test_ctx->ctx, &session);
/* only continue if we expect to authenticate successfully */
if (test_data->expect_ok) {
assert_int_equal(msgtype, NC_MSG_HELLO);
} else {
assert_int_equal(msgtype, NC_MSG_ERROR);
nc_ps_free(ps);
return NULL;
}
ret = nc_ps_add_session(ps, session);
assert_int_equal(ret, 0);
do {
ret = nc_ps_poll(ps, NC_PS_POLL_TIMEOUT, NULL);
assert_int_equal(ret & NC_PSPOLL_RPC, NC_PSPOLL_RPC);
} while (!(ret & NC_PSPOLL_SESSION_TERM));
nc_ps_clear(ps, 1, NULL);
nc_ps_free(ps);
return NULL;
}
static void *
client_thread(void *arg)
{
int ret;
struct nc_session *session = NULL;
struct ln2_test_ctx *test_ctx = arg;
struct test_authkey_data *test_data = test_ctx->test_data;
/* skip all hostkey and known_hosts checks */
nc_client_ssh_set_knownhosts_mode(NC_SSH_KNOWNHOSTS_SKIP);
/* set directory where to search for modules */
ret = nc_client_set_schema_searchpath(MODULES_DIR);
assert_int_equal(ret, 0);
/* add client's key pair */
ret = nc_client_ssh_add_keypair(test_data->pubkey_path, test_data->privkey_path);
assert_int_equal(ret, 0);
/* set ssh username */
ret = nc_client_ssh_set_username("test");
assert_int_equal(ret, 0);
pthread_barrier_wait(&test_ctx->barrier);
/* connect */
session = nc_connect_ssh("127.0.0.1", TEST_PORT, NULL);
if (test_data->expect_ok) {
assert_non_null(session);
} else {
assert_null(session);
}
nc_session_free(session, NULL);
return NULL;
}
static void
test_nc_authkey_ok(void **arg)
{
int ret, i;
pthread_t tids[2];
struct test_authkey_data *test_data;
test_data = (*(struct ln2_test_ctx **)arg)->test_data;
/* set the path to the test's authorized_keys file */
ret = nc_server_ssh_set_authkey_path_format(TESTS_DIR "/data/authorized_keys");
assert_int_equal(ret, 0);
/* set pubkey and privkey path, the pubkey matches the one in authorized keys */
test_data->pubkey_path = TESTS_DIR "/data/id_ed25519.pub";
test_data->privkey_path = TESTS_DIR "/data/id_ed25519";
/* expect ok result */
test_data->expect_ok = 1;
/* client */
ret = pthread_create(&tids[0], NULL, client_thread, *arg);
assert_int_equal(ret, 0);
/* server */
ret = pthread_create(&tids[1], NULL, server_thread, *arg);
assert_int_equal(ret, 0);
for (i = 0; i < 2; i++) {
pthread_join(tids[i], NULL);
}
}
static void
test_nc_authkey_bad_key(void **arg)
{
int ret, i;
pthread_t tids[2];
struct test_authkey_data *test_data;
test_data = (*(struct ln2_test_ctx **)arg)->test_data;
/* set the path to the test's authorized_keys file */
ret = nc_server_ssh_set_authkey_path_format(TESTS_DIR "/data/authorized_keys");
assert_int_equal(ret, 0);
/* set pubkey and privkey path, the pubkey doesn't match the one in authorized keys */
test_data->pubkey_path = TESTS_DIR "/data/id_ecdsa521.pub";
test_data->privkey_path = TESTS_DIR "/data/id_ecdsa521";
/* expect fail */
test_data->expect_ok = 0;
/* client */
ret = pthread_create(&tids[0], NULL, client_thread, *arg);
assert_int_equal(ret, 0);
/* server */
ret = pthread_create(&tids[1], NULL, server_thread, *arg);
assert_int_equal(ret, 0);
for (i = 0; i < 2; i++) {
pthread_join(tids[i], NULL);
}
}
static void
test_nc_authkey_bad_path(void **arg)
{
int ret, i;
pthread_t tids[2];
struct ln2_test_ctx *test_ctx;
struct test_authkey_data *test_data;
assert_non_null(arg);
test_ctx = *arg;
test_data = test_ctx->test_data;
/* set the path to the test's authorized_keys file */
ret = nc_server_ssh_set_authkey_path_format(TESTS_DIR "/some/bad/path");
assert_int_equal(ret, 0);
/* set pubkey and privkey path, the pubkey doesn't match the one in authorized keys */
test_data->pubkey_path = TESTS_DIR "/data/id_ed25519.pub";
test_data->privkey_path = TESTS_DIR "/data/id_ed25519";
/* expect fail */
test_data->expect_ok = 0;
/* client */
ret = pthread_create(&tids[0], NULL, client_thread, test_ctx);
assert_int_equal(ret, 0);
/* server */
ret = pthread_create(&tids[1], NULL, server_thread, test_ctx);
assert_int_equal(ret, 0);
for (i = 0; i < 2; i++) {
pthread_join(tids[i], NULL);
}
}
static int
setup_f(void **state)
{
int ret;
struct lyd_node *tree = NULL;
struct ln2_test_ctx *test_ctx;
struct test_authkey_data *test_data;
ret = ln2_glob_test_setup(&test_ctx);
assert_int_equal(ret, 0);
test_data = calloc(1, sizeof *test_data);
assert_non_null(test_data);
test_ctx->test_data = test_data;
test_ctx->free_test_data = ln2_glob_test_free_test_data;
*state = test_ctx;
ret = nc_server_config_add_ssh_hostkey(test_ctx->ctx, "endpt", "hostkey", TESTS_DIR "/data/server.key", NULL, &tree);
assert_int_equal(ret, 0);
ret = nc_server_config_add_address_port(test_ctx->ctx, "endpt", NC_TI_SSH, "127.0.0.1", TEST_PORT, &tree);
assert_int_equal(ret, 0);
ret = nc_server_config_add_ssh_user_authkey(test_ctx->ctx, "endpt", "test", &tree);
assert_int_equal(ret, 0);
/* configure the server based on the data */
ret = nc_server_config_setup_data(tree);
assert_int_equal(ret, 0);
lyd_free_all(tree);
return 0;
}
int
main(void)
{
const struct CMUnitTest tests[] = {
cmocka_unit_test_setup_teardown(test_nc_authkey_ok, setup_f, ln2_glob_test_teardown),
cmocka_unit_test_setup_teardown(test_nc_authkey_bad_key, setup_f, ln2_glob_test_teardown),
cmocka_unit_test_setup_teardown(test_nc_authkey_bad_path, setup_f, ln2_glob_test_teardown),
};
/* try to get ports from the environment, otherwise use the default */
if (ln2_glob_test_get_ports(1, &TEST_PORT, &TEST_PORT_STR)) {
return 1;
}
setenv("CMOCKA_TEST_ABORT", "1", 1);
return cmocka_run_group_tests(tests, NULL, NULL);
}

665
tests/test_cert_exp_notif.c Normal file
View file

@ -0,0 +1,665 @@
/**
* @file test_cert_exp_notif.c
* @author Roman Janota <janota@cesnet.cz>
* @brief libnetconf2 certificate expiration notification test
*
* @copyright
* Copyright (c) 2024 CESNET, z.s.p.o.
*
* This source code is licensed under BSD 3-Clause License (the "License").
* You may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://opensource.org/licenses/BSD-3-Clause
*/
#define _GNU_SOURCE
#include <errno.h>
#include <pthread.h>
#include <setjmp.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <cmocka.h>
#include "ln2_test.h"
#ifdef HAVE_MBEDTLS
#include <mbedtls/ctr_drbg.h>
#include <mbedtls/entropy.h>
#include <mbedtls/error.h>
#include <mbedtls/pk.h>
#include <mbedtls/x509.h>
#include <mbedtls/x509_crt.h>
#else
#include <openssl/asn1.h>
#include <openssl/bio.h>
#include <openssl/err.h>
#include <openssl/pem.h>
#include <openssl/x509.h>
#endif
#define VALID_CERT_ADD_SEC 15 /* should be enough even with valgrind on slower machines */
#define EXPIRED_CERT_ADD_SEC -10
/* leave a 2 second leeway at most for both sending and receiving */
#define NC_RECV_NOTIF_TIMEOUT (VALID_CERT_ADD_SEC + 2) * 1000
#define NC_SEND_NOTIF_TIMEOUT (VALID_CERT_ADD_SEC + 2) * 1000
struct ly_ctx *server_ctx, *client_ctx;
struct test_state {
pthread_barrier_t barrier;
pthread_barrier_t ntf_barrier;
struct nc_server_notif *ntf;
struct lyd_node *tree;
};
int TEST_PORT = 10050;
const char *TEST_PORT_STR = "10050";
#ifdef HAVE_MBEDTLS
static const char *
mbedtls_strerr(int err)
{
const char *err_str;
err_str = mbedtls_high_level_strerr(err);
if (err_str) {
return err_str;
}
err_str = mbedtls_low_level_strerr(err);
if (err_str) {
return err_str;
}
return "unknown error";
}
static int
custom_exp_date_cert_create(long offset_sec, char cert_path[12])
{
int ret = 0, fd;
mbedtls_pk_context pkey;
mbedtls_x509write_cert cert;
mbedtls_entropy_context entropy;
mbedtls_ctr_drbg_context ctr_drbg;
time_t exp_time;
struct tm *exp_tm;
const char *not_before = "20000101000000";
char not_after[15];
unsigned char output_buf[4096] = {0};
mode_t umode;
/* init */
mbedtls_ctr_drbg_init(&ctr_drbg);
mbedtls_entropy_init(&entropy);
mbedtls_pk_init(&pkey);
mbedtls_x509write_crt_init(&cert);
ret = mbedtls_ctr_drbg_seed(&ctr_drbg, mbedtls_entropy_func, &entropy, NULL, 0);
if (ret) {
fprintf(stderr, "mbedtls_ctr_drbg_seed() failed (%s)\n", mbedtls_strerr(ret));
goto cleanup;
}
/* parse the private key */
ret = mbedtls_pk_parse_keyfile(&pkey, TESTS_DIR "/data/client.key", NULL, mbedtls_ctr_drbg_random, &ctr_drbg);
if (ret) {
fprintf(stderr, "mbedtls_pk_parse_keyfile() failed (%s)\n", mbedtls_strerr(ret));
goto cleanup;
}
/* cert will be self signed, so the subject's and issuer's key can be the same */
mbedtls_x509write_crt_set_subject_key(&cert, &pkey);
mbedtls_x509write_crt_set_issuer_key(&cert, &pkey);
/* likewise for their CNs */
ret = mbedtls_x509write_crt_set_subject_name(&cert, "CN=cert_exp_test");
if (ret) {
fprintf(stderr, "mbedtls_x509write_crt_set_subject_name() failed (%s)\n", mbedtls_strerr(ret));
goto cleanup;
}
ret = mbedtls_x509write_crt_set_issuer_name(&cert, "CN=cert_exp_test");
if (ret) {
fprintf(stderr, "mbedtls_x509write_crt_set_issuer_name() failed (%s)\n", mbedtls_strerr(ret));
goto cleanup;
}
/* set SN to 1 */
ret = mbedtls_x509write_crt_set_serial_raw(&cert, (unsigned char *)"1", 1);
if (ret) {
fprintf(stderr, "mbedtls_x509write_crt_set_serial_raw() failed (%s)\n", mbedtls_strerr(ret));
goto cleanup;
}
/* generate the expiration date in GMT */
exp_time = time(NULL) + offset_sec;
exp_tm = gmtime(&exp_time);
ret = strftime(not_after, 15, "%Y%m%d%H%M%S", exp_tm);
if (ret != 14) {
fprintf(stderr, "strftime() failed (%s)\n", strerror(errno));
ret = 1;
goto cleanup;
}
/* set the validity dates */
ret = mbedtls_x509write_crt_set_validity(&cert, not_before, not_after);
if (ret) {
fprintf(stderr, "mbedtls_x509write_crt_set_validity() failed (%s)\n", mbedtls_strerr(ret));
goto cleanup;
}
mbedtls_x509write_crt_set_md_alg(&cert, MBEDTLS_MD_SHA256);
/* write the cert to mem */
ret = mbedtls_x509write_crt_pem(&cert, output_buf, 4096, mbedtls_ctr_drbg_random, &ctr_drbg);
if (ret < 0) {
fprintf(stderr, "mbedtls_x509write_crt_pem() failed (%s)\n", mbedtls_strerr(ret));
goto cleanup;
}
/* then create a tmp file and write it from mem to this file */
umode = umask(0177);
fd = mkstemp(cert_path);
if (fd < 0) {
fprintf(stderr, "mkstemp() failed (%s)\n", strerror(errno));
ret = 1;
goto cleanup;
}
umask(umode);
if (write(fd, output_buf, strlen((char *)output_buf)) < 0) {
fprintf(stderr, "write() failed (%s)\n", strerror(errno));
ret = 1;
goto cleanup;
}
cleanup:
mbedtls_x509write_crt_free(&cert);
mbedtls_pk_free(&pkey);
mbedtls_ctr_drbg_free(&ctr_drbg);
mbedtls_entropy_free(&entropy);
return ret;
}
#else
static const char *
openssl_strerr(void)
{
return ERR_reason_error_string(ERR_get_error());
}
static int
custom_exp_date_cert_create(long offset_sec, char cert_path[12])
{
int ret = 0;
EVP_PKEY *pkey = NULL;
X509 *cert = NULL;
X509_NAME *name;
FILE *f;
mode_t umode;
BIO *out_bio = NULL;
int fd;
/* get the private key */
f = fopen(TESTS_DIR "/data/client.key", "r");
if (!f) {
fprintf(stderr, "fopen() failed (%s)\n", strerror(errno));
ret = 1;
goto cleanup;
}
pkey = PEM_read_PrivateKey(f, NULL, NULL, NULL);
if (!pkey) {
fprintf(stderr, "PEM_read_PrivateKey() failed (%s)\n", openssl_strerr());
ret = 1;
goto cleanup;
}
/* new cert */
cert = X509_new();
if (!cert) {
fprintf(stderr, "X509_new() failed (%s)\n", openssl_strerr());
ret = 1;
goto cleanup;
}
/* set the public key */
if (!X509_set_pubkey(cert, pkey)) {
fprintf(stderr, "X509_set_pubkey() failed (%s)\n", openssl_strerr());
ret = 1;
goto cleanup;
}
/* set the issuer's CN */
name = X509_get_subject_name(cert);
if (!X509_NAME_add_entry_by_txt(name, "CN", MBSTRING_ASC, (unsigned char *)"cert_exp_test", -1, -1, 0)) {
fprintf(stderr, "X509_NAME_add_entry_by_txt() failed (%s)\n", openssl_strerr());
ret = 1;
goto cleanup;
}
if (!X509_set_issuer_name(cert, name)) {
fprintf(stderr, "X509_set_issuer_name() failed (%s)\n", openssl_strerr());
ret = 1;
goto cleanup;
}
/* set SN to 1 */
if (!ASN1_INTEGER_set(X509_get_serialNumber(cert), 1)) {
fprintf(stderr, "ASN1_INTEGER_set() failed (%s)\n", openssl_strerr());
ret = 1;
goto cleanup;
}
/* set the validity dates */
if (!X509_gmtime_adj(X509_get_notBefore(cert), 0) || !X509_gmtime_adj(X509_get_notAfter(cert), offset_sec)) {
fprintf(stderr, "X509_gmtime_adj() failed (%s)\n", openssl_strerr());
ret = 1;
goto cleanup;
}
/* sign it using the private key */
if (!X509_sign(cert, pkey, EVP_sha256())) {
fprintf(stderr, "X509_sign() failed (%s)\n", openssl_strerr());
ret = 1;
goto cleanup;
}
/* write the cert to a file */
umode = umask(0177);
fd = mkstemp(cert_path);
if (fd < 0) {
fprintf(stderr, "mkstemp() failed (%s)\n", strerror(errno));
ret = 1;
goto cleanup;
}
umask(umode);
out_bio = BIO_new_fd(fd, BIO_NOCLOSE);
if (!out_bio) {
fprintf(stderr, "BIO_new_fd() failed (%s)\n", openssl_strerr());
ret = 1;
goto cleanup;
}
if (!PEM_write_bio_X509(out_bio, cert)) {
fprintf(stderr, "PEM_write_bio_X509() failed (%s)\n", openssl_strerr());
ret = 1;
goto cleanup;
}
cleanup:
if (f) {
fclose(f);
}
X509_free(cert);
EVP_PKEY_free(pkey);
BIO_free(out_bio);
return ret;
}
#endif
static void *
server_thread(void *arg)
{
NC_MSG_TYPE msgtype;
struct nc_session *session;
struct test_state *state = arg;
struct nc_pollsession *ps;
int ret;
ps = nc_ps_new();
assert_non_null(ps);
/* wait until the client is ready to connect */
pthread_barrier_wait(&state->barrier);
msgtype = nc_accept(NC_ACCEPT_TIMEOUT, server_ctx, &session);
assert_int_equal(msgtype, NC_MSG_HELLO);
/* add sess to ps */
ret = nc_ps_add_session(ps, session);
assert_int_equal(ret, 0);
/* serve all the RPCs */
do {
ret = nc_ps_poll(ps, NC_PS_POLL_TIMEOUT, NULL);
} while (ret & NC_PSPOLL_RPC);
/* increase session notification subscription flag count */
nc_session_inc_notif_status(session);
/* wait until the notif is ready to be sent */
printf("Server waiting for the certificate to expire...\n");
pthread_barrier_wait(&state->ntf_barrier);
/* send the notif */
msgtype = nc_server_notif_send(session, state->ntf, NC_SEND_NOTIF_TIMEOUT);
assert_int_equal(msgtype, NC_MSG_NOTIF);
/* wait until the client has received the notif and closed the session */
pthread_barrier_wait(&state->barrier);
nc_session_dec_notif_status(session);
nc_server_notif_free(state->ntf);
nc_ps_clear(ps, 1, NULL);
nc_ps_free(ps);
return NULL;
}
static void *
client_thread(void *arg)
{
int ret;
struct nc_session *session = NULL;
struct test_state *state = arg;
NC_MSG_TYPE msgtype;
struct lyd_node *envp, *op, *node;
/* set schema search path */
ret = nc_client_set_schema_searchpath(MODULES_DIR);
assert_int_equal(ret, 0);
/* set client cert */
ret = nc_client_tls_set_cert_key_paths(TESTS_DIR "/data/client.crt", TESTS_DIR "/data/client.key");
assert_int_equal(ret, 0);
/* set client ca */
ret = nc_client_tls_set_trusted_ca_paths(NULL, TESTS_DIR "/data");
assert_int_equal(ret, 0);
/* wait until the server is ready to accept the connection */
pthread_barrier_wait(&state->barrier);
session = nc_connect_tls("127.0.0.1", TEST_PORT, client_ctx);
assert_non_null(session);
/* receive the notif */
msgtype = nc_recv_notif(session, NC_RECV_NOTIF_TIMEOUT, &envp, &op);
assert_int_equal(msgtype, NC_MSG_NOTIF);
/* check the notif content and print the expiration date */
ret = lyd_find_path(op, "expiration-date", 0, &node);
assert_int_equal(ret, 0);
printf("Certificate expires on :%s\n", lyd_get_value(node));
/* close the session and signal the server */
lyd_free_all(envp);
lyd_free_all(op);
nc_session_free(session, NULL);
pthread_barrier_wait(&state->barrier);
return NULL;
}
static void
nc_cert_exp_notif_cb(const char *exp_time, const char *xpath, void *user_data)
{
int ret;
struct nc_server_notif *ntf = NULL;
struct lyd_node *ntf_data = NULL;
time_t ntf_time;
char *ntf_time_str = NULL;
struct test_state *state = user_data;
/* create the notification data */
ret = lyd_new_path(NULL, server_ctx, xpath, exp_time, 0, &ntf_data);
assert_int_equal(ret, 0);
/* yang time str from time_t */
ntf_time = time(NULL);
ret = ly_time_time2str(ntf_time, NULL, &ntf_time_str);
assert_int_equal(ret, 0);
/* create the notification */
ntf = nc_server_notif_new(ntf_data, ntf_time_str, NC_PARAMTYPE_FREE);
assert_non_null(ntf);
state->ntf = ntf;
/* signal the server that the notif is ready to be sent */
pthread_barrier_wait(&state->ntf_barrier);
}
static void
test_nc_cert_exp_notif_valid_cert(void **state)
{
int ret, i;
pthread_t tids[2];
struct test_state *st = *state;
char cert_path[12] = "/tmp/XXXXXX";
assert_non_null(state);
/* create a soon expiring cert and get a path to it */
ret = custom_exp_date_cert_create(VALID_CERT_ADD_SEC, cert_path);
assert_int_equal(ret, 0);
/* create new end entity client cert data */
ret = nc_server_config_add_tls_client_cert(server_ctx, "endpt", "exp_cert_test", cert_path, &st->tree);
assert_int_equal(ret, 0);
/* configure the server based on the data */
ret = nc_server_config_setup_data(st->tree);
assert_int_equal(ret, 0);
/* start the cert exp notification thread */
ret = nc_server_notif_cert_expiration_thread_start(nc_cert_exp_notif_cb, st, NULL);
assert_int_equal(ret, 0);
/* start the client and server threads */
ret = pthread_create(&tids[0], NULL, client_thread, *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);
}
/* stop the cert exp notif thread */
nc_server_notif_cert_expiration_thread_stop(1);
}
static void
test_nc_cert_exp_notif_expired_cert(void **state)
{
int ret, i;
pthread_t tids[2];
struct test_state *st = *state;
char cert_path[12] = "/tmp/XXXXXX";
assert_non_null(state);
/* create an expired cert and get a path to it */
ret = custom_exp_date_cert_create(EXPIRED_CERT_ADD_SEC, cert_path);
assert_int_equal(ret, 0);
/* create new end entity client cert data from it */
ret = nc_server_config_add_tls_client_cert(server_ctx, "endpt", "exp_cert_test", cert_path, &st->tree);
assert_int_equal(ret, 0);
/* configure the server based on the data */
ret = nc_server_config_setup_data(st->tree);
assert_int_equal(ret, 0);
/* start the cert exp notification thread */
ret = nc_server_notif_cert_expiration_thread_start(nc_cert_exp_notif_cb, st, NULL);
assert_int_equal(ret, 0);
/* start the client and server threads */
ret = pthread_create(&tids[0], NULL, client_thread, *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);
}
/* stop the cert exp notif thread */
nc_server_notif_cert_expiration_thread_stop(1);
}
static void
test_nc_cert_exp_notif_bad_interval_period(void **state)
{
int ret;
struct lyd_node *tree = NULL;
const char *invalid_data =
"<ln2-netconf-server xmlns=\"urn:cesnet:libnetconf2-netconf-server\">\n"
" <certificate-expiration-notif-intervals>\n"
" <interval>\n"
" <anchor>5d</anchor>\n"
" <period>1w</period>\n"
" </interval>\n"
" </certificate-expiration-notif-intervals>\n"
"</ln2-netconf-server>";
(void) state;
/* validating this data should fail because of a must condition, the period
* must not be bigger than the anchor (at least unit wise) */
ret = lyd_parse_data_mem(server_ctx, invalid_data, LYD_XML, 0, LYD_VALIDATE_PRESENT, &tree);
assert_int_not_equal(ret, 0);
}
static void
init_test_ctx(struct ly_ctx **ctx)
{
int ret;
struct lys_module *mod;
const char *ietf_ct_features[] = {"cleartext-passwords", "cleartext-private-keys", "certificate-expiration-notification", NULL};
ret = ly_ctx_new(MODULES_DIR, 0, ctx);
assert_int_equal(ret, 0);
ret = nc_server_init_ctx(ctx);
assert_int_equal(ret, 0);
ret = nc_server_config_load_modules(ctx);
assert_int_equal(ret, 0);
mod = ly_ctx_get_module_implemented(*ctx, "ietf-crypto-types");
assert_non_null(mod);
/* enable the certificate-expiration-notification feature */
ret = lys_set_implemented(mod, ietf_ct_features);
assert_int_equal(ret, 0);
}
static int
setup_f(void **state)
{
int ret;
struct test_state *st;
nc_verbosity(NC_VERB_VERBOSE);
/* init barriers */
st = malloc(sizeof *st);
assert_non_null(st);
ret = pthread_barrier_init(&st->barrier, NULL, 2);
assert_int_equal(ret, 0);
ret = pthread_barrier_init(&st->ntf_barrier, NULL, 2);
assert_int_equal(ret, 0);
st->tree = NULL;
st->ntf = NULL;
*state = st;
/* init server */
ret = nc_server_init();
assert_int_equal(ret, 0);
/* init client */
ret = nc_client_init();
assert_int_equal(ret, 0);
/* init server ctx */
init_test_ctx(&server_ctx);
/* init client ctx to avoid the need to implement get-schema */
init_test_ctx(&client_ctx);
/* create new address and port data */
ret = nc_server_config_add_address_port(server_ctx, "endpt", NC_TI_TLS, "127.0.0.1", TEST_PORT, &st->tree);
assert_int_equal(ret, 0);
/* create new server certificate data */
ret = nc_server_config_add_tls_server_cert(server_ctx, "endpt", TESTS_DIR "/data/server.key", NULL, TESTS_DIR "/data/server.crt", &st->tree);
assert_int_equal(ret, 0);
/* create new end entity client cert data */
ret = nc_server_config_add_tls_client_cert(server_ctx, "endpt", "client_cert", TESTS_DIR "/data/client.crt", &st->tree);
assert_int_equal(ret, 0);
/* create new client ca data */
ret = nc_server_config_add_tls_ca_cert(server_ctx, "endpt", "client_ca", TESTS_DIR "/data/serverca.pem", &st->tree);
assert_int_equal(ret, 0);
/* create new cert-to-name */
ret = nc_server_config_add_tls_ctn(server_ctx, "endpt", 1,
"04:85:6B:75:D1:1A:86:E0:D8:FE:5B:BD:72:F5:73:1D:07:EA:32:BF:09:11:21:6A:6E:23:78:8E:B6:D5:73:C3:2D",
NC_TLS_CTN_SPECIFIED, "client", &st->tree);
assert_int_equal(ret, 0);
return 0;
}
static int
teardown_f(void **state)
{
int ret = 0;
struct test_state *test_state;
assert_non_null(state);
test_state = *state;
ret = pthread_barrier_destroy(&test_state->barrier);
assert_int_equal(ret, 0);
ret = pthread_barrier_destroy(&test_state->ntf_barrier);
assert_int_equal(ret, 0);
lyd_free_all(test_state->tree);
free(*state);
nc_client_destroy();
nc_server_destroy();
ly_ctx_destroy(server_ctx);
ly_ctx_destroy(client_ctx);
return 0;
}
int
main(void)
{
const struct CMUnitTest tests[] = {
cmocka_unit_test_setup_teardown(test_nc_cert_exp_notif_valid_cert, setup_f, teardown_f),
cmocka_unit_test_setup_teardown(test_nc_cert_exp_notif_expired_cert, setup_f, teardown_f),
cmocka_unit_test_setup_teardown(test_nc_cert_exp_notif_bad_interval_period, setup_f, teardown_f),
};
/* try to get ports from the environment, otherwise use the default */
if (ln2_glob_test_get_ports(1, &TEST_PORT, &TEST_PORT_STR)) {
return 1;
}
setenv("CMOCKA_TEST_ABORT", "1", 1);
return cmocka_run_group_tests(tests, NULL, NULL);
}

400
tests/test_ch.c Normal file
View file

@ -0,0 +1,400 @@
/**
* @file test_ch.c
* @author Roman Janota <janota@cesnet.cz>
* @brief libnetconf2 Call-home test
*
* @copyright
* Copyright (c) 2023 - 2024 CESNET, z.s.p.o.
*
* This source code is licensed under BSD 3-Clause License (the "License").
* You may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://opensource.org/licenses/BSD-3-Clause
*/
#define _GNU_SOURCE
#include <errno.h>
#include <pthread.h>
#include <setjmp.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <cmocka.h>
#include "ln2_test.h"
struct test_ch_data {
struct lyd_node *tree;
};
char buffer[512];
char expected[512];
int TEST_PORT = 10050, TEST_PORT_2 = 10051;
const char *TEST_PORT_STR = "10050", *TEST_PORT_2_STR = "10051";
static void
test_msg_callback(const struct nc_session *session, NC_VERB_LEVEL level, const char *msg)
{
(void) level;
(void) session;
if (strstr(msg, expected)) {
strncpy(buffer, msg, 511);
}
printf("%s\n", msg);
}
/* acquire ctx cb for dispatch */
const struct ly_ctx *
ch_session_acquire_ctx_cb(void *cb_data)
{
return ((struct ln2_test_ctx *)cb_data)->ctx;
}
/* release ctx cb for dispatch */
void
ch_session_release_ctx_cb(void *cb_data)
{
(void) cb_data;
return;
}
/* new session cb for dispatch */
int
ch_new_session_cb(const char *client_name, struct nc_session *new_session, void *user_data)
{
int ret = 0;
struct nc_pollsession *ps = (struct nc_pollsession *)user_data;
(void) client_name;
ret = nc_ps_add_session(ps, new_session);
assert_int_equal(ret, 0);
return 0;
}
static void *
server_thread_ssh(void *arg)
{
int ret;
struct nc_pollsession *ps;
struct ln2_test_ctx *test_ctx = arg;
struct test_ch_data *test_data = test_ctx->test_data;
/* set print clb so we get access to messages */
nc_set_print_clb_session(test_msg_callback);
buffer[0] = '\0';
strcpy(expected, "reconnecting in");
/* prepare data for deleting the call-home client */
ret = nc_server_config_del_ch_client("ch_ssh", &test_data->tree);
assert_int_equal(ret, 0);
/* new poll session */
ps = nc_ps_new();
assert_non_null(ps);
pthread_barrier_wait(&test_ctx->barrier);
/* create the call-home client thread */
ret = nc_connect_ch_client_dispatch("ch_ssh", ch_session_acquire_ctx_cb,
ch_session_release_ctx_cb, test_ctx, ch_new_session_cb, ps);
assert_int_equal(ret, 0);
/* poll */
do {
ret = nc_ps_poll(ps, NC_PS_POLL_TIMEOUT, NULL);
if (ret & (NC_PSPOLL_TIMEOUT | NC_PSPOLL_NOSESSIONS)) {
usleep(500);
}
} while (!strlen(buffer));
/* delete the call-home client, the thread should end */
ret = nc_server_config_setup_data(test_data->tree);
assert_int_equal(ret, 0);
nc_ps_clear(ps, 1, NULL);
nc_ps_free(ps);
return NULL;
}
static void *
client_thread_ssh(void *arg)
{
int ret;
struct nc_session *session = NULL;
struct ln2_test_ctx *test_ctx = arg;
/* skip all hostkey and known_hosts checks */
nc_client_ssh_ch_set_knownhosts_mode(NC_SSH_KNOWNHOSTS_SKIP);
/* set directory where to search for modules */
ret = nc_client_set_schema_searchpath(MODULES_DIR);
assert_int_equal(ret, 0);
/* set ssh username */
ret = nc_client_ssh_ch_set_username("test_ch_ssh");
assert_int_equal(ret, 0);
/* add client's key pair */
ret = nc_client_ssh_ch_add_keypair(TESTS_DIR "/data/id_ed25519.pub", TESTS_DIR "/data/id_ed25519");
assert_int_equal(ret, 0);
/* add call-home bind */
ret = nc_client_ssh_ch_add_bind_listen("127.0.0.1", TEST_PORT);
assert_int_equal(ret, 0);
pthread_barrier_wait(&test_ctx->barrier);
/* connect */
ret = nc_accept_callhome(NC_ACCEPT_TIMEOUT, NULL, &session);
assert_int_equal(ret, 1);
ret = nc_client_ssh_ch_del_bind("127.0.0.1", TEST_PORT);
assert_int_equal(ret, 0);
nc_session_free(session, NULL);
return NULL;
}
static void
test_nc_ch_free_test_data(void *test_data)
{
struct test_ch_data *test_ch_data;
test_ch_data = test_data;
lyd_free_tree(test_ch_data->tree);
free(test_ch_data);
}
static int
setup_ssh(void **state)
{
int ret;
struct lyd_node *tree = NULL;
struct ln2_test_ctx *test_ctx;
struct test_ch_data *test_data;
ret = ln2_glob_test_setup(&test_ctx);
assert_int_equal(ret, 0);
test_data = calloc(1, sizeof *test_data);
assert_non_null(test_data);
test_ctx->test_data = test_data;
test_ctx->free_test_data = test_nc_ch_free_test_data;
*state = test_ctx;
/* set call-home address and port */
ret = nc_server_config_add_ch_address_port(test_ctx->ctx, "ch_ssh", "endpt", NC_TI_SSH, "127.0.0.1", TEST_PORT_STR, &tree);
assert_int_equal(ret, 0);
/* set connection type to persistent */
ret = nc_server_config_add_ch_persistent(test_ctx->ctx, "ch_ssh", &tree);
assert_int_equal(ret, 0);
/* set the period of the periodic connection type, this should remove the persistent connection type */
ret = nc_server_config_add_ch_period(test_ctx->ctx, "ch_ssh", 3, &tree);
assert_int_equal(ret, 0);
/* set call-home server hostkey */
ret = nc_server_config_add_ch_ssh_hostkey(test_ctx->ctx, "ch_ssh", "endpt", "hostkey", TESTS_DIR "/data/key_ecdsa", NULL, &tree);
assert_int_equal(ret, 0);
/* set call-home client's pubkey */
ret = nc_server_config_add_ch_ssh_user_pubkey(test_ctx->ctx, "ch_ssh", "endpt", "test_ch_ssh", "pubkey", TESTS_DIR "/data/id_ed25519.pub", &tree);
assert_int_equal(ret, 0);
/* configure the server based on the data */
ret = nc_server_config_setup_data(tree);
assert_int_equal(ret, 0);
test_data->tree = tree;
return 0;
}
static void
test_nc_ch_ssh(void **state)
{
int ret, i;
pthread_t tids[2];
assert_non_null(state);
/* client */
ret = pthread_create(&tids[0], NULL, client_thread_ssh, *state);
assert_int_equal(ret, 0);
/* server */
ret = pthread_create(&tids[1], NULL, server_thread_ssh, *state);
assert_int_equal(ret, 0);
for (i = 0; i < 2; i++) {
pthread_join(tids[i], NULL);
}
}
static void *
server_thread_tls(void *arg)
{
int ret;
struct nc_pollsession *ps;
struct ln2_test_ctx *test_ctx = arg;
struct test_ch_data *test_data = test_ctx->test_data;
/* prepare data for deleting the call-home client */
ret = nc_server_config_del_ch_client("ch_tls", &test_data->tree);
assert_int_equal(ret, 0);
/* new poll session */
ps = nc_ps_new();
assert_non_null(ps);
pthread_barrier_wait(&test_ctx->barrier);
/* create the call-home client thread */
ret = nc_connect_ch_client_dispatch("ch_tls", ch_session_acquire_ctx_cb,
ch_session_release_ctx_cb, test_ctx, ch_new_session_cb, ps);
assert_int_equal(ret, 0);
/* poll */
do {
ret = nc_ps_poll(ps, NC_PS_POLL_TIMEOUT, NULL);
if (ret & (NC_PSPOLL_TIMEOUT | NC_PSPOLL_NOSESSIONS)) {
usleep(500);
}
} while (!(ret & NC_PSPOLL_SESSION_TERM));
/* delete the call-home client, the thread should end */
ret = nc_server_config_setup_data(test_data->tree);
assert_int_equal(ret, 0);
nc_ps_clear(ps, 1, NULL);
nc_ps_free(ps);
return NULL;
}
static void *
client_thread_tls(void *arg)
{
int ret;
struct nc_session *session = NULL;
struct ln2_test_ctx *test_ctx = arg;
/* set directory where to search for modules */
ret = nc_client_set_schema_searchpath(MODULES_DIR);
assert_int_equal(ret, 0);
/* add client's cert */
ret = nc_client_tls_ch_set_cert_key_paths(TESTS_DIR "/data/client.crt", TESTS_DIR "/data/client.key");
assert_int_equal(ret, 0);
/* set client ca */
ret = nc_client_tls_ch_set_trusted_ca_paths(TESTS_DIR "/data/serverca.pem", NULL);
assert_int_equal(ret, 0);
/* add call-home bind */
ret = nc_client_tls_ch_add_bind_listen("127.0.0.1", TEST_PORT_2);
assert_int_equal(ret, 0);
pthread_barrier_wait(&test_ctx->barrier);
/* connect */
ret = nc_accept_callhome(NC_ACCEPT_TIMEOUT, NULL, &session);
assert_int_equal(ret, 1);
ret = nc_client_tls_ch_del_bind("127.0.0.1", TEST_PORT_2);
assert_int_equal(ret, 0);
nc_session_free(session, NULL);
return NULL;
}
static void
test_nc_ch_tls(void **state)
{
int ret, i;
pthread_t tids[2];
assert_non_null(state);
/* client */
ret = pthread_create(&tids[0], NULL, client_thread_tls, *state);
assert_int_equal(ret, 0);
/* server */
ret = pthread_create(&tids[1], NULL, server_thread_tls, *state);
assert_int_equal(ret, 0);
for (i = 0; i < 2; i++) {
pthread_join(tids[i], NULL);
}
}
static int
setup_tls(void **state)
{
int ret;
struct lyd_node *tree = NULL;
struct ln2_test_ctx *test_ctx;
struct test_ch_data *test_data;
ret = ln2_glob_test_setup(&test_ctx);
assert_int_equal(ret, 0);
test_data = calloc(1, sizeof *test_data);
assert_non_null(test_data);
test_ctx->test_data = test_data;
test_ctx->free_test_data = test_nc_ch_free_test_data;
*state = test_ctx;
/* set call-home address and port */
ret = nc_server_config_add_ch_address_port(test_ctx->ctx, "ch_tls", "endpt", NC_TI_TLS, "127.0.0.1", TEST_PORT_2_STR, &tree);
assert_int_equal(ret, 0);
/* set call-home server certificate */
ret = nc_server_config_add_ch_tls_server_cert(test_ctx->ctx, "ch_tls", "endpt", TESTS_DIR "/data/server.key", NULL, TESTS_DIR "/data/server.crt", &tree);
assert_int_equal(ret, 0);
/* set call-home client end entity certificate */
ret = nc_server_config_add_ch_tls_client_cert(test_ctx->ctx, "ch_tls", "endpt", "ee-cert", TESTS_DIR "/data/client.crt", &tree);
assert_int_equal(ret, 0);
/* set call-home client certificate authority certificate */
ret = nc_server_config_add_ch_tls_ca_cert(test_ctx->ctx, "ch_tls", "endpt", "ca-cert", TESTS_DIR "/data/serverca.pem", &tree);
assert_int_equal(ret, 0);
/* set call-home CTN */
ret = nc_server_config_add_ch_tls_ctn(test_ctx->ctx, "ch_tls", "endpt", 1,
"04:85:6B:75:D1:1A:86:E0:D8:FE:5B:BD:72:F5:73:1D:07:EA:32:BF:09:11:21:6A:6E:23:78:8E:B6:D5:73:C3:2D",
NC_TLS_CTN_SPECIFIED, "ch_client_tls", &tree);
assert_int_equal(ret, 0);
/* configure the server based on the data */
ret = nc_server_config_setup_data(tree);
assert_int_equal(ret, 0);
test_data->tree = tree;
return 0;
}
int
main(void)
{
const struct CMUnitTest tests[] = {
cmocka_unit_test_setup_teardown(test_nc_ch_ssh, setup_ssh, ln2_glob_test_teardown),
cmocka_unit_test_setup_teardown(test_nc_ch_tls, setup_tls, ln2_glob_test_teardown),
};
/* try to get ports from the environment, otherwise use the default */
if (ln2_glob_test_get_ports(2, &TEST_PORT, &TEST_PORT_STR, &TEST_PORT_2, &TEST_PORT_2_STR)) {
return 1;
}
setenv("CMOCKA_TEST_ABORT", "1", 1);
return cmocka_run_group_tests(tests, NULL, NULL);
}

View file

@ -1,3 +1,19 @@
/**
* @file test_client_messages.c
* @author David Sedlák <xsedla1d@stud.fit.vutbr.cz>
* @brief client messages test
*
* Copyright (c) 2018 CESNET, z.s.p.o.
*
* This source code is licensed under BSD 3-Clause License (the "License").
* You may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://opensource.org/licenses/BSD-3-Clause
*/
#define _GNU_SOURCE
#include <errno.h>
#include <setjmp.h>
#include <stdio.h>
@ -7,12 +23,9 @@
#include <sys/types.h>
#include <cmocka.h>
#include <config.h>
#include <libyang/libyang.h>
#include <log.h>
#include <messages_p.h>
#include <session_client.h>
#include "tests/config.h"
#include "ln2_test.h"
static int
setup_f(void **state)

View file

@ -0,0 +1,201 @@
/**
* @file test_client_monitoring.c
* @author Roman Janota <janota@cesnet.cz>
* @brief libnetconf2 client monitoring thread test
*
* @copyright
* Copyright (c) 2024 CESNET, z.s.p.o.
*
* This source code is licensed under BSD 3-Clause License (the "License").
* You may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://opensource.org/licenses/BSD-3-Clause
*/
#define _GNU_SOURCE
#include <errno.h>
#include <pthread.h>
#include <setjmp.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <cmocka.h>
#include "ln2_test.h"
#include "session_p.h"
#include <libssh/libssh.h>
int TEST_PORT = 10050;
const char *TEST_PORT_STR = "10050";
void
monitoring_clb(struct nc_session *sess, void *user_data)
{
pthread_barrier_t *barrier = user_data;
/* signal the main thread that the monitoring callback was called */
pthread_barrier_wait(barrier);
printf("Session with ID %d disconnected by the server.\n", nc_session_get_id(sess));
nc_session_free(sess, NULL);
}
static void *
client_thread(void *arg)
{
int ret;
struct nc_session *session = NULL;
struct ln2_test_ctx *test_ctx = arg;
pthread_barrier_t monitoring_barrier;
/* initialize the barrier */
ret = pthread_barrier_init(&monitoring_barrier, NULL, 2);
assert_int_equal(ret, 0);
/* start the monitoring thread */
ret = nc_client_monitoring_thread_start(monitoring_clb, &monitoring_barrier, NULL);
assert_int_equal(ret, 0);
/* skip all hostkey and known_hosts checks */
nc_client_ssh_set_knownhosts_mode(NC_SSH_KNOWNHOSTS_SKIP);
/* set the search path for the schemas */
ret = nc_client_set_schema_searchpath(MODULES_DIR);
assert_int_equal(ret, 0);
/* set the client's username */
ret = nc_client_ssh_set_username("test_client_monitoring");
assert_int_equal(ret, 0);
/* add the client's key pair */
ret = nc_client_ssh_add_keypair(TESTS_DIR "/data/key_rsa.pub", TESTS_DIR "/data/key_rsa");
assert_int_equal(ret, 0);
/* wait for the server to be ready and connect */
pthread_barrier_wait(&test_ctx->barrier);
session = nc_connect_ssh("127.0.0.1", TEST_PORT, NULL);
assert_non_null(session);
/* wait for the monitoring thread callback to be called */
pthread_barrier_wait(&monitoring_barrier);
/* stop the monitoring thread */
nc_client_monitoring_thread_stop();
pthread_barrier_destroy(&monitoring_barrier);
return NULL;
}
void *
server_thread(void *arg)
{
int ret;
NC_MSG_TYPE msgtype;
struct nc_session *session = NULL;
struct nc_pollsession *ps = NULL;
struct ln2_test_ctx *test_ctx = arg;
int fd;
struct linger ling = {1, 0};
ps = nc_ps_new();
assert_non_null(ps);
/* wait for the client to be ready to connect */
pthread_barrier_wait(&test_ctx->barrier);
/* accept a session and add it to the poll session structure */
msgtype = nc_accept(NC_ACCEPT_TIMEOUT, test_ctx->ctx, &session);
assert_int_equal(msgtype, NC_MSG_HELLO);
/* get the session's fd */
fd = ssh_get_fd(session->ti.libssh.session);
assert_int_not_equal(fd, -1);
/* set the socket to close immediately */
ret = setsockopt(fd, SOL_SOCKET, SO_LINGER, &ling, sizeof(ling));
assert_int_equal(ret, 0);
/* add the session to the poll session */
ret = nc_ps_add_session(ps, session);
assert_int_equal(ret, 0);
/* poll until the client stops sending messages */
do {
ret = nc_ps_poll(ps, NC_PS_POLL_TIMEOUT, NULL);
} while ((ret & NC_PSPOLL_RPC));
/* free the session (it will close the socket -> client needs to detect this) */
nc_ps_clear(ps, 1, NULL);
nc_ps_free(ps);
return NULL;
}
static void
test_nc_client_monitoring(void **state)
{
int ret, i;
pthread_t tids[2];
ret = pthread_create(&tids[0], NULL, client_thread, *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 int
setup(void **state)
{
int ret;
struct lyd_node *tree = NULL;
struct ln2_test_ctx *test_ctx;
/* global setup */
ret = ln2_glob_test_setup(&test_ctx);
assert_int_equal(ret, 0);
*state = test_ctx;
/* add endpoint */
ret = nc_server_config_add_address_port(test_ctx->ctx, "endpt", NC_TI_SSH, "127.0.0.1", TEST_PORT, &tree);
assert_int_equal(ret, 0);
/* add hostkey */
ret = nc_server_config_add_ssh_hostkey(test_ctx->ctx, "endpt", "hostkey", TESTS_DIR "/data/key_ecdsa", NULL, &tree);
assert_int_equal(ret, 0);
/* add the test client */
ret = nc_server_config_add_ssh_user_pubkey(test_ctx->ctx, "endpt", "test_client_monitoring", "pubkey", TESTS_DIR "/data/key_rsa.pub", &tree);
assert_int_equal(ret, 0);
/* configure the server based on the data */
ret = nc_server_config_setup_data(tree);
assert_int_equal(ret, 0);
lyd_free_all(tree);
return 0;
}
int
main(void)
{
const struct CMUnitTest tests[] = {
cmocka_unit_test_setup_teardown(test_nc_client_monitoring, setup, ln2_glob_test_teardown)
};
/* try to get ports from the environment, otherwise use the default */
if (ln2_glob_test_get_ports(1, &TEST_PORT, &TEST_PORT_STR)) {
return 1;
}
setenv("CMOCKA_TEST_ABORT", "1", 1);
return cmocka_run_group_tests(tests, NULL, NULL);
}

View file

@ -12,6 +12,8 @@
* https://opensource.org/licenses/BSD-3-Clause
*/
#define _GNU_SOURCE
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
@ -22,8 +24,7 @@
#include <libyang/libyang.h>
#include <session_client.h>
#include "tests/config.h"
#include "ln2_test.h"
#define nc_assert(cond) if (!(cond)) { fprintf(stderr, "assert failed (%s:%d)\n", __FILE__, __LINE__); exit(1); }
@ -51,8 +52,6 @@ main(void)
pthread_t t;
int r;
nc_client_init();
/*
* TEST sharing the thread context
*/

View file

@ -0,0 +1,236 @@
/**
* @file test_endpt_share_clients.c
* @author Roman Janota <janota@cesnet.cz>
* @brief libnetconf2 Sharing clients between endpoints test
*
* @copyright
* Copyright (c) 2023 - 2024 CESNET, z.s.p.o.
*
* This source code is licensed under BSD 3-Clause License (the "License").
* You may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://opensource.org/licenses/BSD-3-Clause
*/
#define _GNU_SOURCE
#include <errno.h>
#include <pthread.h>
#include <setjmp.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <cmocka.h>
#include "ln2_test.h"
int TEST_PORT = 10050, TEST_PORT_2 = 10051, TEST_PORT_3 = 10052, TEST_PORT_4 = 10053;
const char *TEST_PORT_STR = "10050", *TEST_PORT_2_STR = "10051", *TEST_PORT_3_STR = "10052", *TEST_PORT_4_STR = "10053";
static void *
client_thread_ssh(void *arg)
{
int ret;
struct nc_session *session = NULL;
struct ln2_test_ctx *test_ctx = arg;
/* skip all hostkey and known_hosts checks */
nc_client_ssh_set_knownhosts_mode(NC_SSH_KNOWNHOSTS_SKIP);
/* set directory where to search for modules */
ret = nc_client_set_schema_searchpath(MODULES_DIR);
assert_int_equal(ret, 0);
/* set ssh username */
ret = nc_client_ssh_set_username("client");
assert_int_equal(ret, 0);
/* add client's key pair */
ret = nc_client_ssh_add_keypair(TESTS_DIR "/data/key_rsa.pub", TESTS_DIR "/data/key_rsa");
assert_int_equal(ret, 0);
/* wait for the server to reach polling */
pthread_barrier_wait(&test_ctx->barrier);
/* connect */
session = nc_connect_ssh("127.0.0.1", TEST_PORT, NULL);
assert_non_null(session);
nc_session_free(session, NULL);
return NULL;
}
static void
nc_test_endpt_share_clients_ssh(void **state)
{
int ret, i;
pthread_t tids[2];
assert_non_null(state);
ret = pthread_create(&tids[0], NULL, client_thread_ssh, *state);
assert_int_equal(ret, 0);
ret = pthread_create(&tids[1], NULL, ln2_glob_test_server_thread, *state);
assert_int_equal(ret, 0);
for (i = 0; i < 2; i++) {
pthread_join(tids[i], NULL);
}
}
static void *
client_thread_tls(void *arg)
{
int ret;
struct nc_session *session = NULL;
struct ln2_test_ctx *test_ctx = arg;
/* set directory where to search for modules */
ret = nc_client_set_schema_searchpath(MODULES_DIR);
assert_int_equal(ret, 0);
/* set client cert */
ret = nc_client_tls_set_cert_key_paths(TESTS_DIR "/data/client.crt", TESTS_DIR "/data/client.key");
assert_int_equal(ret, 0);
/* set client ca */
ret = nc_client_tls_set_trusted_ca_paths(NULL, TESTS_DIR "/data");
assert_int_equal(ret, 0);
pthread_barrier_wait(&test_ctx->barrier);
session = nc_connect_tls("127.0.0.1", TEST_PORT_4, NULL);
assert_non_null(session);
nc_session_free(session, NULL);
return NULL;
}
static void
nc_test_endpt_share_clients_tls(void **state)
{
int ret, i;
pthread_t tids[2];
assert_non_null(state);
ret = pthread_create(&tids[0], NULL, client_thread_tls, *state);
assert_int_equal(ret, 0);
ret = pthread_create(&tids[1], NULL, ln2_glob_test_server_thread, *state);
assert_int_equal(ret, 0);
for (i = 0; i < 2; i++) {
pthread_join(tids[i], NULL);
}
}
static int
setup_ssh(void **state)
{
int ret;
struct lyd_node *tree = NULL;
struct ln2_test_ctx *test_ctx;
ret = ln2_glob_test_setup(&test_ctx);
assert_int_equal(ret, 0);
*state = test_ctx;
/* create the first SSH endpoint with a client reference to the second endpoint */
ret = nc_server_config_add_ssh_hostkey(test_ctx->ctx, "SSH_endpt_1", "hostkey", TESTS_DIR "/data/key_rsa", NULL, &tree);
assert_int_equal(ret, 0);
ret = nc_server_config_add_address_port(test_ctx->ctx, "SSH_endpt_1", NC_TI_SSH, "127.0.0.1", TEST_PORT, &tree);
assert_int_equal(ret, 0);
ret = nc_server_config_add_ssh_endpoint_client_ref(test_ctx->ctx, "SSH_endpt_1", "SSH_endpt_2", &tree);
assert_int_equal(ret, 0);
/* create the second SSH endpoint with a single client */
ret = nc_server_config_add_ssh_hostkey(test_ctx->ctx, "SSH_endpt_2", "hostkey", TESTS_DIR "/data/key_rsa", NULL, &tree);
assert_int_equal(ret, 0);
ret = nc_server_config_add_address_port(test_ctx->ctx, "SSH_endpt_2", NC_TI_SSH, "127.0.0.1", TEST_PORT_2, &tree);
assert_int_equal(ret, 0);
ret = nc_server_config_add_ssh_user_pubkey(test_ctx->ctx, "SSH_endpt_2", "client", "pubkey", TESTS_DIR "/data/key_rsa.pub", &tree);
assert_int_equal(ret, 0);
/* configure the server based on the yang data */
ret = nc_server_config_setup_data(tree);
assert_int_equal(ret, 0);
lyd_free_all(tree);
return 0;
}
static int
setup_tls(void **state)
{
int ret;
struct lyd_node *tree = NULL;
struct ln2_test_ctx *test_ctx;
ret = ln2_glob_test_setup(&test_ctx);
assert_int_equal(ret, 0);
*state = test_ctx;
/* create the first TLS endpoint with a single end entity client cert and a CTN entry */
ret = nc_server_config_add_tls_server_cert(test_ctx->ctx, "TLS_endpt_1", TESTS_DIR "/data/server.key", NULL, TESTS_DIR "/data/server.crt", &tree);
assert_int_equal(ret, 0);
ret = nc_server_config_add_address_port(test_ctx->ctx, "TLS_endpt_1", NC_TI_TLS, "127.0.0.1", TEST_PORT_3, &tree);
assert_int_equal(ret, 0);
ret = nc_server_config_add_tls_client_cert(test_ctx->ctx, "TLS_endpt_1", "cert_client", TESTS_DIR "/data/client.crt", &tree);
assert_int_equal(ret, 0);
ret = nc_server_config_add_tls_ca_cert(test_ctx->ctx, "TLS_endpt_1", "cert_ca", TESTS_DIR "/data/serverca.pem", &tree);
assert_int_equal(ret, 0);
ret = nc_server_config_add_tls_ctn(test_ctx->ctx, "TLS_endpt_1", 1,
"04:85:6B:75:D1:1A:86:E0:D8:FE:5B:BD:72:F5:73:1D:07:EA:32:BF:09:11:21:6A:6E:23:78:8E:B6:D5:73:C3:2D",
NC_TLS_CTN_SPECIFIED, "client", &tree);
assert_int_equal(ret, 0);
/* create the second TLS endpoint with a reference to the first endpoint */
ret = nc_server_config_add_tls_server_cert(test_ctx->ctx, "TLS_endpt_2",
TESTS_DIR "/data/server.key", NULL, TESTS_DIR "/data/server.crt", &tree);
assert_int_equal(ret, 0);
ret = nc_server_config_add_address_port(test_ctx->ctx, "TLS_endpt_2", NC_TI_TLS, "127.0.0.1", TEST_PORT_4, &tree);
assert_int_equal(ret, 0);
ret = nc_server_config_add_tls_endpoint_client_ref(test_ctx->ctx, "TLS_endpt_2", "TLS_endpt_1", &tree);
assert_int_equal(ret, 0);
/* configure the server based on the yang data */
ret = nc_server_config_setup_data(tree);
assert_int_equal(ret, 0);
lyd_free_all(tree);
return 0;
}
int
main(void)
{
const struct CMUnitTest tests[] = {
cmocka_unit_test_setup_teardown(nc_test_endpt_share_clients_ssh, setup_ssh, ln2_glob_test_teardown),
cmocka_unit_test_setup_teardown(nc_test_endpt_share_clients_tls, setup_tls, ln2_glob_test_teardown),
};
/* try to get ports from the environment, otherwise use the default */
if (ln2_glob_test_get_ports(4, &TEST_PORT, &TEST_PORT_STR, &TEST_PORT_2, &TEST_PORT_2_STR,
&TEST_PORT_3, &TEST_PORT_3_STR, &TEST_PORT_4, &TEST_PORT_4_STR)) {
return 1;
}
setenv("CMOCKA_TEST_ABORT", "1", 1);
return cmocka_run_group_tests(tests, NULL, NULL);
}

View file

@ -12,6 +12,8 @@
* https://opensource.org/licenses/BSD-3-Clause
*/
#define _GNU_SOURCE
#include <errno.h>
#include <fcntl.h>
#include <pthread.h>
@ -30,10 +32,9 @@
#include <libyang/libyang.h>
#include <messages_p.h>
#include <session_client.h>
#include <session_p.h>
#include <session_server.h>
#include "tests/config.h"
#include "ln2_test.h"
struct nc_session *server_session;
struct nc_session *client_session;
@ -59,7 +60,7 @@ my_getconfig_rpc_clb(struct lyd_node *rpc, struct nc_session *session)
assert_string_equal(rpc->schema->name, "get-config");
assert_ptr_equal(session, server_session);
lyd_new_path(NULL, session->ctx, "/ietf-netconf:get-config/data", NULL, LYD_NEW_PATH_OUTPUT, &data);
lyd_new_path(NULL, session->ctx, "/ietf-netconf:get-config/data", NULL, LYD_NEW_VAL_OUTPUT, &data);
assert_non_null(data);
return nc_server_reply_data(data, NC_WD_EXPLICIT, NC_PARAMTYPE_FREE);
@ -90,6 +91,7 @@ static struct nc_session *
test_new_session(NC_SIDE side)
{
struct nc_session *sess;
struct timespec ts;
sess = calloc(1, sizeof *sess);
if (!sess) {
@ -99,9 +101,13 @@ test_new_session(NC_SIDE side)
sess->side = side;
if (side == NC_SERVER) {
pthread_mutex_init(&sess->opts.server.ntf_status_lock, NULL);
pthread_mutex_init(&sess->opts.server.rpc_lock, NULL);
pthread_cond_init(&sess->opts.server.rpc_cond, NULL);
sess->opts.server.rpc_inuse = 0;
nc_timeouttime_get(&ts, 0);
sess->opts.server.last_rpc = ts.tv_sec;
}
sess->io_lock = malloc(sizeof *sess->io_lock);
@ -495,6 +501,55 @@ test_send_recv_notif_11(void **state)
test_send_recv_notif();
}
static void
test_send_recv_malformed_10(void **state)
{
int ret;
struct nc_pollsession *ps;
struct nc_rpc *rpc;
struct lyd_node *envp, *op, *node;
NC_MSG_TYPE msgtype;
const char *msg;
(void)state;
server_session->version = NC_VERSION_10;
client_session->version = NC_VERSION_10;
/* write malformed message */
msg =
"<nc:rpc xmlns:nc=\"urn:ietf:params:xml:ns:netconf:base:1.0\">"
" <nc:commit/>"
"</nc:rpc>"
"]]>]]>";
assert_int_equal(write(client_session->ti.fd.out, msg, strlen(msg)), strlen(msg));
rpc = nc_rpc_commit(0, 0, NULL, NULL, 0);
assert_non_null(rpc);
/* server RPC, send reply */
ps = nc_ps_new();
assert_non_null(ps);
nc_ps_add_session(ps, server_session);
ret = nc_ps_poll(ps, 0, NULL);
assert_int_equal(ret, NC_PSPOLL_BAD_RPC | NC_PSPOLL_REPLY_ERROR);
/* server finished */
nc_ps_free(ps);
/* client reply */
msgtype = nc_recv_reply(client_session, rpc, 0, 0, &envp, &op);
assert_int_equal(msgtype, NC_MSG_REPLY_ERR_MSGID);
nc_rpc_free(rpc);
assert_string_equal(LYD_NAME(lyd_child(envp)), "rpc-error");
lyd_find_sibling_opaq_next(lyd_child(lyd_child(envp)), "error-tag", &node);
assert_non_null(node);
assert_string_equal(((struct lyd_node_opaq *)node)->value, "missing-attribute");
lyd_free_tree(envp);
assert_null(op);
}
int
main(void)
{
@ -532,13 +587,14 @@ main(void)
assert_non_null(node);
node->priv = my_commit_rpc_clb;
nc_server_init(ctx);
nc_server_init();
const struct CMUnitTest comm[] = {
cmocka_unit_test_setup_teardown(test_send_recv_ok_10, setup_sessions, teardown_sessions),
cmocka_unit_test_setup_teardown(test_send_recv_error_10, setup_sessions, teardown_sessions),
cmocka_unit_test_setup_teardown(test_send_recv_data_10, setup_sessions, teardown_sessions),
cmocka_unit_test_setup_teardown(test_send_recv_notif_10, setup_sessions, teardown_sessions),
cmocka_unit_test_setup_teardown(test_send_recv_malformed_10, setup_sessions, teardown_sessions),
cmocka_unit_test_setup_teardown(test_send_recv_ok_11, setup_sessions, teardown_sessions),
cmocka_unit_test_setup_teardown(test_send_recv_error_11, setup_sessions, teardown_sessions),
cmocka_unit_test_setup_teardown(test_send_recv_data_11, setup_sessions, teardown_sessions),

View file

@ -1,69 +0,0 @@
/**
* \file test_init_destroy_client.c
* \author Michal Vasko <mvasko@cesnet.cz>
* \brief libnetconf2 tests - init/destroy client
*
* Copyright (c) 2015 CESNET, z.s.p.o.
*
* This source code is licensed under BSD 3-Clause License (the "License").
* You may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://opensource.org/licenses/BSD-3-Clause
*/
#include <errno.h>
#include <fcntl.h>
#include <pthread.h>
#include <setjmp.h>
#include <stdarg.h>
#include <stddef.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <cmocka.h>
#include <libyang/libyang.h>
#include <session_client.h>
#include "tests/config.h"
static int
setup_client(void **state)
{
(void)state;
nc_client_init();
return 0;
}
static int
teardown_client(void **state)
{
(void)state;
nc_client_destroy();
return 0;
}
static void
test_dummy(void **state)
{
(void)state;
}
int
main(void)
{
const struct CMUnitTest init_destroy[] = {
cmocka_unit_test_setup_teardown(test_dummy, setup_client, teardown_client)
};
return cmocka_run_group_tests(init_destroy, NULL, NULL);
}

View file

@ -1,75 +0,0 @@
/**
* \file test_init_destroy_server.c
* \author Michal Vasko <mvasko@cesnet.cz>
* \brief libnetconf2 tests - init/destroy server
*
* Copyright (c) 2015 CESNET, z.s.p.o.
*
* This source code is licensed under BSD 3-Clause License (the "License").
* You may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://opensource.org/licenses/BSD-3-Clause
*/
#include <errno.h>
#include <fcntl.h>
#include <pthread.h>
#include <setjmp.h>
#include <stdarg.h>
#include <stddef.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <cmocka.h>
#include <libyang/libyang.h>
#include <session_server.h>
#include "tests/config.h"
struct ly_ctx *ctx;
static int
setup_server(void **state)
{
(void)state;
ly_ctx_new(NULL, 0, &ctx);
assert_non_null(ctx);
nc_server_init(ctx);
return 0;
}
static int
teardown_server(void **state)
{
(void)state;
nc_server_destroy();
ly_ctx_destroy(ctx);
return 0;
}
static void
test_dummy(void **state)
{
(void)state;
}
int
main(void)
{
const struct CMUnitTest init_destroy[] = {
cmocka_unit_test_setup_teardown(test_dummy, setup_server, teardown_server)
};
return cmocka_run_group_tests(init_destroy, NULL, NULL);
}

View file

@ -12,6 +12,8 @@
* https://opensource.org/licenses/BSD-3-Clause
*/
#define _GNU_SOURCE
#include <errno.h>
#include <fcntl.h>
#include <pthread.h>
@ -28,9 +30,9 @@
#include <libyang/libyang.h>
#include <messages_p.h>
#include <session_client.h>
#include <session_p.h>
#include "tests/config.h"
#include "ln2_test.h"
struct wr {
struct nc_session *session;

232
tests/test_ks_ts.c Normal file
View file

@ -0,0 +1,232 @@
/**
* @file test_ks_ts.c
* @author Roman Janota <janota@cesnet.cz>
* @brief libnetconf2 Keystore and trustore usage test.
*
* @copyright
* Copyright (c) 2023 - 2024 CESNET, z.s.p.o.
*
* This source code is licensed under BSD 3-Clause License (the "License").
* You may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://opensource.org/licenses/BSD-3-Clause
*/
#define _GNU_SOURCE
#include <errno.h>
#include <pthread.h>
#include <setjmp.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <cmocka.h>
#include "ln2_test.h"
int TEST_PORT = 10050;
const char *TEST_PORT_STR = "10050";
static void *
client_thread_ssh(void *arg)
{
int ret;
struct nc_session *session = NULL;
struct ln2_test_ctx *test_ctx = 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("client");
assert_int_equal(ret, 0);
ret = nc_client_ssh_add_keypair(TESTS_DIR "/data/id_ed25519.pub", TESTS_DIR "/data/id_ed25519");
assert_int_equal(ret, 0);
pthread_barrier_wait(&test_ctx->barrier);
session = nc_connect_ssh("127.0.0.1", TEST_PORT, NULL);
assert_non_null(session);
nc_session_free(session, NULL);
return NULL;
}
static void
test_nc_ks_ts_ssh(void **state)
{
int ret, i;
pthread_t tids[2];
assert_non_null(state);
ret = pthread_create(&tids[0], NULL, client_thread_ssh, *state);
assert_int_equal(ret, 0);
ret = pthread_create(&tids[1], NULL, ln2_glob_test_server_thread, *state);
assert_int_equal(ret, 0);
for (i = 0; i < 2; i++) {
pthread_join(tids[i], NULL);
}
}
static int
setup_ssh(void **state)
{
int ret;
struct lyd_node *tree = NULL;
struct ln2_test_ctx *test_ctx;
ret = ln2_glob_test_setup(&test_ctx);
assert_int_equal(ret, 0);
*state = test_ctx;
ret = nc_server_config_add_address_port(test_ctx->ctx, "endpt", NC_TI_SSH, "127.0.0.1", TEST_PORT, &tree);
assert_int_equal(ret, 0);
ret = nc_server_config_add_ssh_keystore_ref(test_ctx->ctx, "endpt", "hostkey", "test_keystore", &tree);
assert_int_equal(ret, 0);
ret = nc_server_config_add_ssh_truststore_ref(test_ctx->ctx, "endpt", "client", "test_truststore", &tree);
assert_int_equal(ret, 0);
ret = nc_server_config_add_keystore_asym_key(test_ctx->ctx, NC_TI_SSH, "test_keystore", TESTS_DIR "/data/key_rsa", NULL, &tree);
assert_int_equal(ret, 0);
ret = nc_server_config_add_truststore_pubkey(test_ctx->ctx, "test_truststore", "pubkey", TESTS_DIR "/data/id_ed25519.pub", &tree);
assert_int_equal(ret, 0);
/* configure the server based on the data */
ret = nc_server_config_setup_data(tree);
assert_int_equal(ret, 0);
lyd_free_all(tree);
return 0;
}
static void *
client_thread_tls(void *arg)
{
int ret;
struct nc_session *session = NULL;
struct ln2_test_ctx *test_ctx = arg;
ret = nc_client_set_schema_searchpath(MODULES_DIR);
assert_int_equal(ret, 0);
/* set client cert */
ret = nc_client_tls_set_cert_key_paths(TESTS_DIR "/data/client.crt", TESTS_DIR "/data/client.key");
assert_int_equal(ret, 0);
/* set client ca */
ret = nc_client_tls_set_trusted_ca_paths(NULL, TESTS_DIR "/data");
assert_int_equal(ret, 0);
pthread_barrier_wait(&test_ctx->barrier);
session = nc_connect_tls("127.0.0.1", TEST_PORT, NULL);
assert_non_null(session);
nc_session_free(session, NULL);
return NULL;
}
static void
test_nc_ks_ts_tls(void **state)
{
int ret, i;
pthread_t tids[2];
assert_non_null(state);
ret = pthread_create(&tids[0], NULL, client_thread_tls, *state);
assert_int_equal(ret, 0);
ret = pthread_create(&tids[1], NULL, ln2_glob_test_server_thread, *state);
assert_int_equal(ret, 0);
for (i = 0; i < 2; i++) {
pthread_join(tids[i], NULL);
}
}
static int
setup_tls(void **state)
{
int ret;
struct lyd_node *tree = NULL;
struct ln2_test_ctx *test_ctx;
ret = ln2_glob_test_setup(&test_ctx);
assert_int_equal(ret, 0);
*state = test_ctx;
/* new tls bind */
ret = nc_server_config_add_address_port(test_ctx->ctx, "endpt", NC_TI_TLS, "127.0.0.1", TEST_PORT, &tree);
assert_int_equal(ret, 0);
/* new keystore asym key pair */
ret = nc_server_config_add_keystore_asym_key(test_ctx->ctx, NC_TI_TLS, "server_key", TESTS_DIR "/data/server.key", NULL, &tree);
assert_int_equal(ret, 0);
/* new keystore cert belonging to the key pair */
ret = nc_server_config_add_keystore_cert(test_ctx->ctx, "server_key", "server_cert", TESTS_DIR "/data/server.crt", &tree);
assert_int_equal(ret, 0);
/* new truststore client cert */
ret = nc_server_config_add_truststore_cert(test_ctx->ctx, "ee_cert_bag", "ee_cert", TESTS_DIR "/data/client.crt", &tree);
assert_int_equal(ret, 0);
/* new truststore client CA cert */
ret = nc_server_config_add_truststore_cert(test_ctx->ctx, "ca_cert_bag", "ca_cert", TESTS_DIR "/data/serverca.pem", &tree);
assert_int_equal(ret, 0);
/* new keystore ref for the TLS server cert */
ret = nc_server_config_add_tls_keystore_ref(test_ctx->ctx, "endpt", "server_key", "server_cert", &tree);
assert_int_equal(ret, 0);
/* new truststore ref for the client cert */
ret = nc_server_config_add_tls_client_cert_truststore_ref(test_ctx->ctx, "endpt", "ee_cert_bag", &tree);
assert_int_equal(ret, 0);
/* new truststore ref for the client CA cert */
ret = nc_server_config_add_tls_ca_cert_truststore_ref(test_ctx->ctx, "endpt", "ca_cert_bag", &tree);
assert_int_equal(ret, 0);
/* new cert-to-name */
ret = nc_server_config_add_tls_ctn(test_ctx->ctx, "endpt", 1,
"04:85:6B:75:D1:1A:86:E0:D8:FE:5B:BD:72:F5:73:1D:07:EA:32:BF:09:11:21:6A:6E:23:78:8E:B6:D5:73:C3:2D",
NC_TLS_CTN_SPECIFIED, "client", &tree);
assert_int_equal(ret, 0);
/* configure the server based on the data */
ret = nc_server_config_setup_data(tree);
assert_int_equal(ret, 0);
lyd_free_all(tree);
return 0;
}
int
main(void)
{
const struct CMUnitTest tests[] = {
cmocka_unit_test_setup_teardown(test_nc_ks_ts_ssh, setup_ssh, ln2_glob_test_teardown),
cmocka_unit_test_setup_teardown(test_nc_ks_ts_tls, setup_tls, ln2_glob_test_teardown),
};
/* try to get ports from the environment, otherwise use the default */
if (ln2_glob_test_get_ports(1, &TEST_PORT, &TEST_PORT_STR)) {
return 1;
}
setenv("CMOCKA_TEST_ABORT", "1", 1);
return cmocka_run_group_tests(tests, NULL, NULL);
}

149
tests/test_pam.c Normal file
View file

@ -0,0 +1,149 @@
/**
* @file test_pam.c
* @author Roman Janota <janota@cesnet.cz>
* @brief libnetconf2 SSH Keyboard Interactive auth using PAM test
*
* @copyright
* Copyright (c) 2023 - 2024 CESNET, z.s.p.o.
*
* This source code is licensed under BSD 3-Clause License (the "License").
* You may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://opensource.org/licenses/BSD-3-Clause
*/
#define _GNU_SOURCE
#include <errno.h>
#include <pthread.h>
#include <setjmp.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <security/pam_appl.h>
#include <cmocka.h>
#include "ln2_test.h"
int TEST_PORT = 10050;
const char *TEST_PORT_STR = "10050";
/* mock pam_start to just call pam_start_confdir instead */
int __real_pam_start(const char *service_name, const char *user, const struct pam_conv *pam_conversation, pam_handle_t **pamh);
int
__wrap_pam_start(const char *service_name, const char *user, const struct pam_conv *pam_conversation, pam_handle_t **pamh)
{
return pam_start_confdir(service_name, user, pam_conversation, BUILD_DIR "/tests", pamh);
}
static char *
auth_interactive(const char *UNUSED(auth_name), const char *UNUSED(instruction),
const char *prompt, int UNUSED(echo), void *UNUSED(priv))
{
/* send the replies to keyboard-interactive authentication */
if (strstr(prompt, "backwards")) {
return strdup("tset");
} else if (strstr(prompt, "1+1")) {
return strdup("2");
} else {
return NULL;
}
}
static void *
client_thread(void *arg)
{
int ret;
struct nc_session *session = NULL;
struct ln2_test_ctx *test_ctx = 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");
assert_int_equal(ret, 0);
/* set keyboard-interactive authentication callback */
nc_client_ssh_set_auth_interactive_clb(auth_interactive, NULL);
pthread_barrier_wait(&test_ctx->barrier);
session = nc_connect_ssh("127.0.0.1", TEST_PORT, NULL);
assert_non_null(session);
nc_session_free(session, NULL);
return NULL;
}
static void
test_nc_pam(void **state)
{
int ret, i;
pthread_t tids[2];
assert_non_null(state);
ret = pthread_create(&tids[0], NULL, client_thread, *state);
assert_int_equal(ret, 0);
ret = pthread_create(&tids[1], NULL, ln2_glob_test_server_thread, *state);
assert_int_equal(ret, 0);
for (i = 0; i < 2; i++) {
pthread_join(tids[i], NULL);
}
}
static int
setup_f(void **state)
{
int ret;
struct lyd_node *tree = NULL;
struct ln2_test_ctx *test_ctx;
ret = ln2_glob_test_setup(&test_ctx);
assert_int_equal(ret, 0);
*state = test_ctx;
ret = nc_server_config_add_address_port(test_ctx->ctx, "endpt", NC_TI_SSH, "127.0.0.1", TEST_PORT, &tree);
assert_int_equal(ret, 0);
ret = nc_server_config_add_ssh_hostkey(test_ctx->ctx, "endpt", "hostkey", TESTS_DIR "/data/key_ecdsa", NULL, &tree);
assert_int_equal(ret, 0);
ret = nc_server_config_add_ssh_user_interactive(test_ctx->ctx, "endpt", "test", &tree);
assert_int_equal(ret, 0);
ret = nc_server_ssh_set_pam_conf_filename("netconf.conf");
assert_int_equal(ret, 0);
/* configure the server based on the data */
ret = nc_server_config_setup_data(tree);
assert_int_equal(ret, 0);
lyd_free_all(tree);
return 0;
}
int
main(void)
{
const struct CMUnitTest tests[] = {
cmocka_unit_test_setup_teardown(test_nc_pam, setup_f, ln2_glob_test_teardown)
};
/* try to get ports from the environment, otherwise use the default */
if (ln2_glob_test_get_ports(1, &TEST_PORT, &TEST_PORT_STR)) {
return 1;
}
setenv("CMOCKA_TEST_ABORT", "1", 1);
return cmocka_run_group_tests(tests, NULL, NULL);
}

144
tests/test_replace.c Normal file
View file

@ -0,0 +1,144 @@
/**
* @file test_replace.c
* @author Roman Janota <janota@cesnet.cz>
* @brief libnetconf2 Non-diff YANG data configuration test
*
* @copyright
* Copyright (c) 2023 - 2024 CESNET, z.s.p.o.
*
* This source code is licensed under BSD 3-Clause License (the "License").
* You may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://opensource.org/licenses/BSD-3-Clause
*/
#define _GNU_SOURCE
#include <errno.h>
#include <pthread.h>
#include <setjmp.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <cmocka.h>
#include "ln2_test.h"
int TEST_PORT = 10050;
const char *TEST_PORT_STR = "10050";
static void *
client_thread(void *arg)
{
int ret;
struct nc_session *session = NULL;
struct ln2_test_ctx *test_ctx = arg;
/* skip all hostkey and known_hosts checks */
nc_client_ssh_set_knownhosts_mode(NC_SSH_KNOWNHOSTS_SKIP);
/* set directory where to search for modules */
ret = nc_client_set_schema_searchpath(MODULES_DIR);
assert_int_equal(ret, 0);
/* set ssh username */
ret = nc_client_ssh_set_username("new_client");
assert_int_equal(ret, 0);
/* add client's key pair */
ret = nc_client_ssh_add_keypair(TESTS_DIR "/data/key_rsa.pub", TESTS_DIR "/data/key_rsa");
assert_int_equal(ret, 0);
/* wait for the server to reach polling */
pthread_barrier_wait(&test_ctx->barrier);
/* connect */
session = nc_connect_ssh("127.0.0.1", TEST_PORT, NULL);
assert_non_null(session);
nc_session_free(session, NULL);
return NULL;
}
static void
nc_test_replace(void **state)
{
int ret, i;
pthread_t tids[2];
assert_non_null(state);
ret = pthread_create(&tids[0], NULL, client_thread, *state);
assert_int_equal(ret, 0);
ret = pthread_create(&tids[1], NULL, ln2_glob_test_server_thread, *state);
assert_int_equal(ret, 0);
for (i = 0; i < 2; i++) {
pthread_join(tids[i], NULL);
}
}
static int
setup_f(void **state)
{
int ret;
struct lyd_node *old_tree = NULL, *new_tree = NULL;
struct ln2_test_ctx *test_ctx;
ret = ln2_glob_test_setup(&test_ctx);
assert_int_equal(ret, 0);
*state = test_ctx;
ret = nc_server_config_add_address_port(test_ctx->ctx, "old", NC_TI_SSH, "127.0.0.1", TEST_PORT, &old_tree);
assert_int_equal(ret, 0);
ret = nc_server_config_add_ssh_hostkey(test_ctx->ctx, "old", "old_key", TESTS_DIR "/data/key_rsa", NULL, &old_tree);
assert_int_equal(ret, 0);
ret = nc_server_config_add_ssh_user_password(test_ctx->ctx, "old", "old_client", "passwd", &old_tree);
assert_int_equal(ret, 0);
/* configure the server based on the yang data, treat them as if every node had replace operation */
ret = nc_server_config_setup_data(old_tree);
assert_int_equal(ret, 0);
ret = nc_server_config_add_address_port(test_ctx->ctx, "new", NC_TI_SSH, "127.0.0.1", TEST_PORT, &new_tree);
assert_int_equal(ret, 0);
ret = nc_server_config_add_ssh_hostkey(test_ctx->ctx, "new", "new_key", TESTS_DIR "/data/key_rsa", NULL, &new_tree);
assert_int_equal(ret, 0);
ret = nc_server_config_add_ssh_user_pubkey(test_ctx->ctx, "new", "new_client", "pubkey", TESTS_DIR "/data/key_rsa.pub", &new_tree);
assert_int_equal(ret, 0);
/* configure the server based on the yang data, meaning
* everything configured will be deleted and only the new data applied
*/
ret = nc_server_config_setup_data(new_tree);
assert_int_equal(ret, 0);
lyd_free_all(old_tree);
lyd_free_all(new_tree);
return 0;
}
int
main(void)
{
const struct CMUnitTest tests[] = {
cmocka_unit_test_setup_teardown(nc_test_replace, setup_f, ln2_glob_test_teardown),
};
/* try to get ports from the environment, otherwise use the default */
if (ln2_glob_test_get_ports(1, &TEST_PORT, &TEST_PORT_STR)) {
return 1;
}
setenv("CMOCKA_TEST_ABORT", "1", 1);
return cmocka_run_group_tests(tests, NULL, NULL);
}

View file

@ -0,0 +1,468 @@
/**
* @file test_runtime_changes.c
* @author Roman Janota <janota@cesnet.cz>
* @brief libnetconf2 Runtime changes test.
*
* @copyright
* Copyright (c) 2023 - 2024 CESNET, z.s.p.o.
*
* This source code is licensed under BSD 3-Clause License (the "License").
* You may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://opensource.org/licenses/BSD-3-Clause
*/
#define _GNU_SOURCE
#include <pthread.h>
#include <setjmp.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <cmocka.h>
#include "ln2_test.h"
struct test_state {
pthread_barrier_t start_barrier;
pthread_barrier_t end_barrier;
struct lyd_node *tree;
};
typedef enum {
NC_TEST_EXPECT_FAIL,
NC_TEST_EXPECT_OK
} NC_TEST_EXPECT;
typedef enum {
NC_TEST_STATE_END,
NC_TEST_STATE_RUN
} NC_TEST_STATE;
struct ly_ctx *ctx;
int test_running;
int expect_ok;
int TEST_PORT = 10050, TEST_PORT_2 = 10051;
const char *TEST_PORT_STR = "10050", *TEST_PORT_2_STR = "10051";
static void *
server_thread(void *arg)
{
int ret;
NC_MSG_TYPE msgtype;
struct nc_session *session;
struct nc_pollsession *ps;
struct test_state *state = arg;
ps = nc_ps_new();
assert_non_null(ps);
/* just to wait for when new data is configured */
pthread_barrier_wait(&state->end_barrier);
while (1) {
/* config ready, wait for client/server to be ready */
pthread_barrier_wait(&state->start_barrier);
msgtype = nc_accept(NC_ACCEPT_TIMEOUT, ctx, &session);
if (expect_ok) {
assert_int_equal(msgtype, NC_MSG_HELLO);
ret = nc_ps_add_session(ps, session);
assert_int_equal(ret, 0);
do {
ret = nc_ps_poll(ps, NC_PS_POLL_TIMEOUT, NULL);
assert_int_equal(ret & NC_PSPOLL_RPC, NC_PSPOLL_RPC);
} while (!(ret & NC_PSPOLL_SESSION_TERM));
nc_ps_clear(ps, 1, NULL);
} else {
assert_int_equal(msgtype, NC_MSG_ERROR);
}
if (!test_running) {
break;
}
/* wait for next config */
pthread_barrier_wait(&state->end_barrier);
}
nc_ps_free(ps);
return NULL;
}
static void *
client_thread_tls(void *arg)
{
int ret;
struct nc_session *session = NULL;
struct test_state *state = arg;
ret = nc_client_set_schema_searchpath(MODULES_DIR);
assert_int_equal(ret, 0);
/* set client cert */
ret = nc_client_tls_set_cert_key_paths(TESTS_DIR "/data/client.crt", TESTS_DIR "/data/client.key");
assert_int_equal(ret, 0);
/* set client ca */
ret = nc_client_tls_set_trusted_ca_paths(NULL, TESTS_DIR "/data");
assert_int_equal(ret, 0);
/* just to wait for when new data is configured */
pthread_barrier_wait(&state->end_barrier);
while (1) {
/* config ready, wait for client/server to be ready */
pthread_barrier_wait(&state->start_barrier);
session = nc_connect_tls("127.0.0.1", TEST_PORT, NULL);
if (expect_ok) {
assert_non_null(session);
nc_session_free(session, NULL);
} else {
assert_null(session);
}
if (!test_running) {
break;
}
/* wait for next config */
pthread_barrier_wait(&state->end_barrier);
}
return NULL;
}
static void *
client_thread_ssh(void *arg)
{
int ret;
struct nc_session *session = NULL;
struct test_state *state = arg;
ret = nc_client_set_schema_searchpath(MODULES_DIR);
assert_int_equal(ret, 0);
/* skip all hostkey and known_hosts checks */
nc_client_ssh_set_knownhosts_mode(NC_SSH_KNOWNHOSTS_SKIP);
/* set ssh username */
ret = nc_client_ssh_set_username("client");
assert_int_equal(ret, 0);
/* add client's key pair */
ret = nc_client_ssh_add_keypair(TESTS_DIR "/data/id_ed25519.pub", TESTS_DIR "/data/id_ed25519");
assert_int_equal(ret, 0);
/* just to wait for when new data is configured */
pthread_barrier_wait(&state->end_barrier);
while (1) {
/* config ready, wait for client/server to be ready */
pthread_barrier_wait(&state->start_barrier);
session = nc_connect_ssh("127.0.0.1", TEST_PORT_2, NULL);
if (expect_ok) {
assert_non_null(session);
nc_session_free(session, NULL);
} else {
assert_null(session);
}
if (!test_running) {
break;
}
/* wait for next config */
pthread_barrier_wait(&state->end_barrier);
}
return NULL;
}
static inline void
configure(struct test_state *state, NC_TEST_EXPECT ok_or_fail, NC_TEST_STATE run_or_end)
{
int ret = 0;
/* lidl synchronization */
pthread_barrier_wait(&state->end_barrier);
/* apply new config */
ret = nc_server_config_setup_data(state->tree);
assert_int_equal(ret, 0);
/* set test params */
expect_ok = ok_or_fail;
test_running = run_or_end;
/* it just works */
pthread_barrier_wait(&state->start_barrier);
}
static void
init_test_create_threads_tls(pthread_t tids[2], void **state)
{
int ret;
/* so threads dont quit immediately */
test_running = NC_TEST_STATE_RUN;
ret = pthread_create(&tids[0], NULL, client_thread_tls, *state);
assert_int_equal(ret, 0);
ret = pthread_create(&tids[1], NULL, server_thread, *state);
assert_int_equal(ret, 0);
}
static void
init_test_create_threads_ssh(pthread_t tids[2], void **state)
{
int ret;
/* so threads dont quit immediately */
test_running = NC_TEST_STATE_RUN;
ret = pthread_create(&tids[0], NULL, client_thread_ssh, *state);
assert_int_equal(ret, 0);
ret = pthread_create(&tids[1], NULL, server_thread, *state);
assert_int_equal(ret, 0);
}
static void
test_nc_change_tls_srv_crt(void **state)
{
int ret, i;
pthread_t tids[2];
struct test_state *test_state;
assert_non_null(state);
test_state = *state;
init_test_create_threads_tls(tids, state);
ret = nc_server_config_add_tls_server_cert(ctx, "endpt_tls", TESTS_DIR "/data/client.key", NULL, TESTS_DIR "/data/client.crt", &test_state->tree);
assert_int_equal(ret, 0);
configure(test_state, NC_TEST_EXPECT_FAIL, NC_TEST_STATE_RUN);
ret = nc_server_config_add_tls_server_cert(ctx, "endpt_tls", TESTS_DIR "/data/server.key", NULL, TESTS_DIR "/data/server.crt", &test_state->tree);
assert_int_equal(ret, 0);
configure(test_state, NC_TEST_EXPECT_OK, NC_TEST_STATE_END);
for (i = 0; i < 2; i++) {
pthread_join(tids[i], NULL);
}
}
static void
test_nc_change_tls_client_crt(void **state)
{
int ret, i;
pthread_t tids[2];
struct test_state *test_state;
assert_non_null(state);
test_state = *state;
init_test_create_threads_tls(tids, state);
ret = nc_server_config_add_tls_client_cert(ctx, "endpt_tls", "client_cert", TESTS_DIR "/data/server.crt", &test_state->tree);
assert_int_equal(ret, 0);
configure(test_state, NC_TEST_EXPECT_FAIL, NC_TEST_STATE_RUN);
ret = nc_server_config_add_tls_client_cert(ctx, "endpt_tls", "client_cert", TESTS_DIR "/data/client.crt", &test_state->tree);
assert_int_equal(ret, 0);
configure(test_state, NC_TEST_EXPECT_OK, NC_TEST_STATE_END);
for (i = 0; i < 2; i++) {
pthread_join(tids[i], NULL);
}
}
static void
test_nc_change_tls_ctn(void **state)
{
int ret, i;
pthread_t tids[2];
struct test_state *test_state;
assert_non_null(state);
test_state = *state;
init_test_create_threads_tls(tids, state);
ret = nc_server_config_add_tls_ctn(ctx, "endpt_tls", 1,
"FF:FF:FF:FF:FF:FF:FF:FF:FF:FF:FF:FF:FF:FF:FF:FF:FF:FF:FF:FF:FF:FF:FF:FF:FF:FF:FF:FF:FF:FF:FF:FF:FF",
NC_TLS_CTN_SPECIFIED, "invalid-fingerprint", &test_state->tree);
assert_int_equal(ret, 0);
configure(test_state, NC_TEST_EXPECT_FAIL, NC_TEST_STATE_RUN);
ret = nc_server_config_add_tls_ctn(ctx, "endpt_tls", 1,
"04:85:6B:75:D1:1A:86:E0:D8:FE:5B:BD:72:F5:73:1D:07:EA:32:BF:09:11:21:6A:6E:23:78:8E:B6:D5:73:C3:2D",
NC_TLS_CTN_SPECIFIED, "client", &test_state->tree);
assert_int_equal(ret, 0);
configure(test_state, NC_TEST_EXPECT_OK, NC_TEST_STATE_END);
for (i = 0; i < 2; i++) {
pthread_join(tids[i], NULL);
}
}
static void
test_nc_change_ssh_hostkey(void **state)
{
int ret, i;
pthread_t tids[2];
struct test_state *test_state;
assert_non_null(state);
test_state = *state;
init_test_create_threads_ssh(tids, state);
ret = nc_server_config_add_ssh_hostkey(ctx, "endpt_ssh", "hostkey", TESTS_DIR "/data/server.key", NULL, &test_state->tree);
assert_int_equal(ret, 0);
configure(test_state, NC_TEST_EXPECT_OK, NC_TEST_STATE_RUN);
ret = nc_server_config_add_keystore_asym_key(ctx, NC_TI_SSH, "keystore_hostkey", TESTS_DIR "/data/key_rsa", TESTS_DIR "/data/key_rsa.pub", &test_state->tree);
assert_int_equal(ret, 0);
ret = nc_server_config_add_ssh_keystore_ref(ctx, "endpt_ssh", "hostkey", "keystore_hostkey", &test_state->tree);
assert_int_equal(ret, 0);
configure(test_state, NC_TEST_EXPECT_OK, NC_TEST_STATE_END);
for (i = 0; i < 2; i++) {
pthread_join(tids[i], NULL);
}
}
static void
test_nc_change_ssh_usr_pubkey(void **state)
{
int ret, i;
pthread_t tids[2];
struct test_state *test_state;
assert_non_null(state);
test_state = *state;
init_test_create_threads_ssh(tids, state);
ret = nc_server_config_add_ssh_user_pubkey(ctx, "endpt_ssh", "client", "pubkey", TESTS_DIR "/data/id_ecdsa521.pub", &test_state->tree);
assert_int_equal(ret, 0);
configure(test_state, NC_TEST_EXPECT_FAIL, NC_TEST_STATE_RUN);
ret = nc_server_config_add_ssh_user_pubkey(ctx, "endpt_ssh", "client", "pubkey", TESTS_DIR "/data/id_ed25519.pub", &test_state->tree);
assert_int_equal(ret, 0);
configure(test_state, NC_TEST_EXPECT_OK, NC_TEST_STATE_END);
for (i = 0; i < 2; i++) {
pthread_join(tids[i], NULL);
}
}
static int
setup_f(void **state)
{
int ret;
struct test_state *test_state;
nc_verbosity(NC_VERB_VERBOSE);
test_state = malloc(sizeof *test_state);
assert_non_null(test_state);
/* init barriers */
ret = pthread_barrier_init(&test_state->start_barrier, NULL, 3);
assert_int_equal(ret, 0);
ret = pthread_barrier_init(&test_state->end_barrier, NULL, 3);
assert_int_equal(ret, 0);
test_state->tree = NULL;
*state = test_state;
ret = ly_ctx_new(MODULES_DIR, 0, &ctx);
assert_int_equal(ret, 0);
ret = nc_server_init_ctx(&ctx);
assert_int_equal(ret, 0);
ret = nc_server_config_load_modules(&ctx);
assert_int_equal(ret, 0);
/* create new address and port data */
ret = nc_server_config_add_address_port(ctx, "endpt_tls", NC_TI_TLS, "127.0.0.1", TEST_PORT, &test_state->tree);
assert_int_equal(ret, 0);
/* create new server certificate data */
ret = nc_server_config_add_tls_server_cert(ctx, "endpt_tls", TESTS_DIR "/data/server.key", NULL, TESTS_DIR "/data/server.crt", &test_state->tree);
assert_int_equal(ret, 0);
/* create new end entity client cert data */
ret = nc_server_config_add_tls_client_cert(ctx, "endpt_tls", "client_cert", TESTS_DIR "/data/client.crt", &test_state->tree);
assert_int_equal(ret, 0);
/* create new cert-to-name */
ret = nc_server_config_add_tls_ctn(ctx, "endpt_tls", 1,
"04:85:6B:75:D1:1A:86:E0:D8:FE:5B:BD:72:F5:73:1D:07:EA:32:BF:09:11:21:6A:6E:23:78:8E:B6:D5:73:C3:2D",
NC_TLS_CTN_SPECIFIED, "client", &test_state->tree);
assert_int_equal(ret, 0);
/* create new address and port data */
ret = nc_server_config_add_address_port(ctx, "endpt_ssh", NC_TI_SSH, "127.0.0.1", TEST_PORT_2, &test_state->tree);
assert_int_equal(ret, 0);
/* create new hostkey data */
ret = nc_server_config_add_ssh_hostkey(ctx, "endpt_ssh", "hostkey", TESTS_DIR "/data/server.key", NULL, &test_state->tree);
assert_int_equal(ret, 0);
/* create new ssh user pubkey data */
ret = nc_server_config_add_ssh_user_pubkey(ctx, "endpt_ssh", "client", "pubkey", TESTS_DIR "/data/id_ed25519.pub", &test_state->tree);
assert_int_equal(ret, 0);
ret = nc_server_init();
assert_int_equal(ret, 0);
/* initialize client */
ret = nc_client_init();
assert_int_equal(ret, 0);
return 0;
}
static int
teardown_f(void **state)
{
int ret = 0;
struct test_state *test_state;
assert_non_null(state);
test_state = *state;
ret = pthread_barrier_destroy(&test_state->start_barrier);
assert_int_equal(ret, 0);
ret = pthread_barrier_destroy(&test_state->end_barrier);
assert_int_equal(ret, 0);
lyd_free_all(test_state->tree);
free(*state);
nc_client_destroy();
nc_server_destroy();
ly_ctx_destroy(ctx);
return 0;
}
int
main(void)
{
const struct CMUnitTest tests[] = {
cmocka_unit_test_setup_teardown(test_nc_change_tls_srv_crt, setup_f, teardown_f),
cmocka_unit_test_setup_teardown(test_nc_change_tls_client_crt, setup_f, teardown_f),
cmocka_unit_test_setup_teardown(test_nc_change_tls_ctn, setup_f, teardown_f),
cmocka_unit_test_setup_teardown(test_nc_change_ssh_hostkey, setup_f, teardown_f),
cmocka_unit_test_setup_teardown(test_nc_change_ssh_usr_pubkey, setup_f, teardown_f),
};
/* try to get ports from the environment, otherwise use the default */
if (ln2_glob_test_get_ports(2, &TEST_PORT, &TEST_PORT_STR, &TEST_PORT_2, &TEST_PORT_2_STR)) {
return 1;
}
setenv("CMOCKA_TEST_ABORT", "1", 1);
return cmocka_run_group_tests(tests, NULL, NULL);
}

View file

@ -1,765 +0,0 @@
/**
* \file test_server_thread.c
* \author Michal Vasko <mvasko@cesnet.cz>
* \brief libnetconf2 tests - thread-safety of all server functions
*
* Copyright (c) 2017 CESNET, z.s.p.o.
*
* This source code is licensed under BSD 3-Clause License (the "License").
* You may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://opensource.org/licenses/BSD-3-Clause
*/
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <libyang/libyang.h>
#include <log.h>
#include <session_client.h>
#include <session_server.h>
#include "tests/config.h"
/* millisec */
#define NC_ACCEPT_TIMEOUT 5000
/* millisec */
#define NC_PS_POLL_TIMEOUT 5000
/* sec */
#define CLIENT_SSH_AUTH_TIMEOUT 10
#define nc_assert(cond) if (!(cond)) { fprintf(stderr, "assert failed (%s:%d)\n", __FILE__, __LINE__); exit(1); }
#if _POSIX_BARRIERS >= 200112L
pthread_barrier_t barrier;
#if defined (NC_ENABLED_SSH) || defined (NC_ENABLED_TLS)
static void *
server_thread(void *arg)
{
(void)arg;
NC_MSG_TYPE msgtype;
int ret;
struct nc_pollsession *ps;
struct nc_session *session;
ps = nc_ps_new();
nc_assert(ps);
pthread_barrier_wait(&barrier);
#if defined (NC_ENABLED_SSH) && defined (NC_ENABLED_TLS)
msgtype = nc_accept(NC_ACCEPT_TIMEOUT, &session);
nc_assert(msgtype == NC_MSG_HELLO);
nc_ps_add_session(ps, session);
ret = nc_ps_poll(ps, NC_PS_POLL_TIMEOUT, NULL);
nc_assert(ret & NC_PSPOLL_RPC);
nc_ps_clear(ps, 0, NULL);
#endif
msgtype = nc_accept(NC_ACCEPT_TIMEOUT, &session);
nc_assert(msgtype == NC_MSG_HELLO);
nc_ps_add_session(ps, session);
ret = nc_ps_poll(ps, NC_PS_POLL_TIMEOUT, NULL);
nc_assert(ret & NC_PSPOLL_RPC);
nc_ps_clear(ps, 0, NULL);
nc_ps_free(ps);
nc_thread_destroy();
return NULL;
}
#endif /* NC_ENABLED_SSH || NC_ENABLED_TLS */
#ifdef NC_ENABLED_SSH
static int
clb_hostkeys(const char *name, void *UNUSED(user_data), char **privkey_path, char **UNUSED(privkey_data),
NC_SSH_KEY_TYPE *UNUSED(privkey_type))
{
if (!strcmp(name, "key_rsa")) {
*privkey_path = strdup(TESTS_DIR "/data/key_rsa");
return 0;
} else if (!strcmp(name, "key_dsa")) {
*privkey_path = strdup(TESTS_DIR "/data/key_dsa");
return 0;
}
return 1;
}
static void *
add_endpt_thread(void *arg)
{
(void)arg;
int ret;
pthread_barrier_wait(&barrier);
ret = nc_server_add_endpt("tertiary", NC_TI_LIBSSH);
nc_assert(!ret);
return NULL;
}
static void *
del_endpt_thread(void *arg)
{
(void)arg;
int ret;
pthread_barrier_wait(&barrier);
ret = nc_server_del_endpt("secondary", 0);
nc_assert(!ret);
return NULL;
}
static void *
ssh_endpt_set_hostkey_thread(void *arg)
{
(void)arg;
int ret;
pthread_barrier_wait(&barrier);
ret = nc_server_ssh_endpt_add_hostkey("main_ssh", "key_dsa", -1);
nc_assert(!ret);
return NULL;
}
static void *
ssh_endpt_set_auth_methods_thread(void *arg)
{
(void)arg;
int ret;
pthread_barrier_wait(&barrier);
ret = nc_server_ssh_endpt_set_auth_methods("main_ssh", NC_SSH_AUTH_PUBLICKEY | NC_SSH_AUTH_PASSWORD | NC_SSH_AUTH_INTERACTIVE);
nc_assert(!ret);
return NULL;
}
static void *
ssh_endpt_set_auth_attempts_thread(void *arg)
{
(void)arg;
int ret;
pthread_barrier_wait(&barrier);
ret = nc_server_ssh_endpt_set_auth_attempts("main_ssh", 2);
nc_assert(!ret);
return NULL;
}
static void *
ssh_endpt_set_auth_timeout_thread(void *arg)
{
(void)arg;
int ret;
pthread_barrier_wait(&barrier);
ret = nc_server_ssh_endpt_set_auth_timeout("main_ssh", 5);
nc_assert(!ret);
return NULL;
}
static void *
ssh_endpt_add_authkey_thread(void *arg)
{
(void)arg;
int ret;
pthread_barrier_wait(&barrier);
ret = nc_server_ssh_add_authkey_path(TESTS_DIR "/data/key_rsa.pub", "test3");
nc_assert(!ret);
return NULL;
}
static void *
ssh_endpt_del_authkey_thread(void *arg)
{
(void)arg;
int ret;
pthread_barrier_wait(&barrier);
ret = nc_server_ssh_del_authkey(TESTS_DIR "/data/key_dsa.pub", NULL, 0, "test2");
nc_assert(!ret);
return NULL;
}
static int
ssh_hostkey_check_clb(const char *hostname, ssh_session session, void *priv)
{
(void)hostname;
(void)session;
(void)priv;
return 0;
}
static void *
ssh_client_thread(void *arg)
{
int ret, read_pipe = *(int *)arg;
char buf[9];
struct nc_session *session;
fprintf(stdout, "SSH client start.\n");
ret = read(read_pipe, buf, 9);
nc_assert(ret == 9);
nc_assert(!strncmp(buf, "ssh_ready", 9));
/* skip the knownhost check */
nc_client_ssh_set_auth_hostkey_check_clb(ssh_hostkey_check_clb, NULL);
ret = nc_client_ssh_set_username("test");
nc_assert(!ret);
ret = nc_client_ssh_add_keypair(TESTS_DIR "/data/key_ecdsa.pub", TESTS_DIR "/data/key_ecdsa");
nc_assert(!ret);
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);
session = nc_connect_ssh("127.0.0.1", 6001, NULL);
nc_assert(session);
nc_session_free(session, NULL);
fprintf(stdout, "SSH client finished.\n");
nc_thread_destroy();
return NULL;
}
#endif /* NC_ENABLED_SSH */
#ifdef NC_ENABLED_TLS
static int
clb_server_cert(const char *name, void *UNUSED(user_data), char **cert_path, char **cert_data, char **privkey_path,
char **privkey_data, NC_SSH_KEY_TYPE *privkey_type)
{
if (!strcmp(name, "server_cert1")) {
*cert_data = strdup("MIIEQDCCAygCCQCV65JgDvfWkTANBgkqhkiG9w0BAQsFADBjMQswCQYDVQQGEwJD\n"
"WjETMBEGA1UECAwKU29tZS1TdGF0ZTENMAsGA1UEBwwEQnJubzEPMA0GA1UECgwG\n"
"Q0VTTkVUMQwwCgYDVQQLDANUTUMxETAPBgNVBAMMCHNlcnZlcmNhMB4XDTE4MTEw\n"
"NTA3MzExMFoXDTI4MTEwMjA3MzExMFowYTELMAkGA1UEBhMCQ1oxEzARBgNVBAgM\n"
"ClNvbWUtU3RhdGUxDTALBgNVBAcMBEJybm8xDzANBgNVBAoMBkNFU05FVDEMMAoG\n"
"A1UECwwDVE1DMQ8wDQYDVQQDDAZzZXJ2ZXIwggIiMA0GCSqGSIb3DQEBAQUAA4IC\n"
"DwAwggIKAoICAQDqiO2N8Oa/JA/VmQjAGmv3t4oO55u+miCVEdF29W5Ol/+BTVUC\n"
"sBCbAaHTmLqWbxORWXWegyUjEskNxayVp5WforK+xfQeGxC1fCo+rCRrlQK/pqXB\n"
"/+K8C9w2lxfWPS3+4gYIjh1KIqfNALJ/QVOKvNCSOsNlR3eZ4OE1BOu4JqUZXiB8\n"
"1Yird7Wga7ACfW0uW72hOsTgfMymBs0RTrA2axKqnAbFSFguhJoztvR0uao/5lzN\n"
"JLRRpzQ8U2R6EZDYJghPzR/nzSjhca2gsJRQaSnpgJMvhnYJ4ERokAFaMMgMf50p\n"
"ghQGpSnPhOHXaBcA/6H7eojr716ml4et+vMBEOx8uPBQ3FAmx7VBICuMDK1/QUq0\n"
"Yes5FztbROIIW9HTNqhQ0tMqTt6dOJFD2t9Zk4C7jh9S88JpcMTrNdd5pWCgoKjh\n"
"UeWGjfp6tRyOUQEz6OTbwjKRtka0FvqKAq9hrW09KB5ib/MmgVWNByevXi5yL9+3\n"
"X41r6E4gSgQSIrFwA+TZva8Eo2VEhbtYsne7tmK5AQlM2br7dwTj6V/BoEIGoFOg\n"
"T52nO+hRegzIUVF5QV3U7lrG7h9eC3TSMxo5DGYTS/06ZH+kv89Yh+VUXhnPnJU4\n"
"1hoNVzuX695jSgRmu4Q8nSGUSDG4LMutwyGO5+XaoTZDt9Ahpq//U2oFWQIDAQAB\n"
"MA0GCSqGSIb3DQEBCwUAA4IBAQAXWHf/MG8RPCyA0rC3RwxmM70ndyKPIJoL4ggU\n"
"VgkN66BdpsE4UlWdlp0XL3aauMPxzLn9rq1yRtoHWT4/ucL9iEa6B295JBNjkgW+\n"
"ct9/y8060P9BUhY1DTv5DLzitsA4bjRaraIevjATDPfsbHFx9DTNrS5pXHIFbRcz\n"
"y3WniYXTKhpfM6m+1X8ogImE968DG8RqAW5YZZtrZW0VF/dhlQp20jEX/8Rv33Bp\n"
"RhNEIhPnYAquKCesMMclUtPW+5n2z8rgj5t/ETv4wc5QegpyPfdHNq09bGKB10Sy\n"
"sGvC6hP9GKU3R2Jhxih/t88O3WoisFQ8+Tf9s2LuSxUV0bzp");
*privkey_data = strdup("MIIJKAIBAAKCAgEA6ojtjfDmvyQP1ZkIwBpr97eKDuebvpoglRHRdvVuTpf/gU1V\n"
"ArAQmwGh05i6lm8TkVl1noMlIxLJDcWslaeVn6KyvsX0HhsQtXwqPqwka5UCv6al\n"
"wf/ivAvcNpcX1j0t/uIGCI4dSiKnzQCyf0FTirzQkjrDZUd3meDhNQTruCalGV4g\n"
"fNWIq3e1oGuwAn1tLlu9oTrE4HzMpgbNEU6wNmsSqpwGxUhYLoSaM7b0dLmqP+Zc\n"
"zSS0Uac0PFNkehGQ2CYIT80f580o4XGtoLCUUGkp6YCTL4Z2CeBEaJABWjDIDH+d\n"
"KYIUBqUpz4Th12gXAP+h+3qI6+9eppeHrfrzARDsfLjwUNxQJse1QSArjAytf0FK\n"
"tGHrORc7W0TiCFvR0zaoUNLTKk7enTiRQ9rfWZOAu44fUvPCaXDE6zXXeaVgoKCo\n"
"4VHlho36erUcjlEBM+jk28IykbZGtBb6igKvYa1tPSgeYm/zJoFVjQcnr14uci/f\n"
"t1+Na+hOIEoEEiKxcAPk2b2vBKNlRIW7WLJ3u7ZiuQEJTNm6+3cE4+lfwaBCBqBT\n"
"oE+dpzvoUXoMyFFReUFd1O5axu4fXgt00jMaOQxmE0v9OmR/pL/PWIflVF4Zz5yV\n"
"ONYaDVc7l+veY0oEZruEPJ0hlEgxuCzLrcMhjufl2qE2Q7fQIaav/1NqBVkCAwEA\n"
"AQKCAgAeRZw75Oszoqj0jfMmMILdD3Cfad+dY3FvLESYESeyt0XAX8XoOed6ymQj\n"
"1qPGxQGGkkBvPEgv1b3jrC8Rhfb3Ct39Z7mRpTar5iHhwwBUboBTUmQ0vR173iAH\n"
"X8sw2Oa17mCO/CDlr8Fu4Xcom7r3vlVBepo72VSjpPYMjN0MANjwhEi3NCyWzTXB\n"
"RgUK3TuZbzfzto0w2Irlpx0S7dAqxfk70jXBgwv2vSDWKfg1lL1X0BkMVX98xpMk\n"
"cjMW2muSqp4KBtTma4GqT6z0f7Y1Bs3lGLZmvPlBXxQVVvkFtiQsENCtSd/h17Gk\n"
"2mb4EbReaaBzwCYqJdRWtlpJ54kzy8U00co+Yn//ZS7sbbIDkqHPnXkpdIr+0rED\n"
"MlOw2Y3vRZCxqZFqfWCW0uzhwKqk2VoYqtDL+ORKG/aG/KTBQ4Y71Uh+7aabPwj5\n"
"R+NaVMjbqmrVeH70eKjoNVgcNYY1C9rGVF1d+LQEm7UsqS0DPp4wN9QKLAqIfuar\n"
"AhQBhZy1R7Sj1r5macD9DsGxsurM4mHZV0LNmYLZiFHjTUb6iRSPD5RBFW80vcNt\n"
"xZ0cxmkLtxrj/DVyExV11Cl0SbZLLa9mScYvxdl/qZutXt3PQyab0NiYxGzCD2Rn\n"
"LkCyxkh1vuHHjhvIWYfbd2VgZB/qGr+o9T07FGfMCu23//fugQKCAQEA9UH38glH\n"
"/rAjZ431sv6ryUEFY8I2FyLTijtvoj9CNGcQn8vJQAHvUPfMdyqDoum6wgcTmG+U\n"
"XA6mZzpGQCiY8JW5CoItgXRoYgNzpvVVe2aLf51QGtNLLEFpNDMpCtI+I+COpAmG\n"
"vWAukku0pZfRjm9eb1ydvTpHlFC9+VhVUsLzw3VtSC5PVW6r65mZcYcB6SFVPap+\n"
"31ENP/9jOMFoymh57lSMZJMxTEA5b0l2miFb9Rp906Zqiud5zv2jIqF6gL70giW3\n"
"ovVxR7LGKKTKIa9pxawHwB6Ithygs7YoJkjF2dm8pZTMZKsQN92K70XGj07SmYRL\n"
"ZpkVD7i+cqbbKQKCAQEA9M6580Rcw6W0twfcy0/iB4U5ZS52EcCjW8vHlL+MpUo7\n"
"YvXadSgV1ZaM28zW/ZGk3wE0zy1YT5s30SQkm0NiWN3t/J0l19ccAOxlPWfjhF7v\n"
"IQZr7XMo5HeaK0Ak5+68J6bx6KgcXmlJOup7INaE8DyGXB6vd4K6957IXyqs3/bf\n"
"JAUmz49hnveCfLFdTVVT/Uq4IoPKfQSbSZc0BvPBsnBCF164l4jllGBaWS302dhg\n"
"W4cgxzG0SZGgNwow4AhB+ygiiS8yvOa7UcHfUObVrzWeeq9mYSQ1PkvUTjkWR2/Y\n"
"8xy7WP0TRBdJOVSs90H51lerEDGNQWvQvI97S9ZOsQKCAQB59u9lpuXtqwxAQCFy\n"
"fSFSuQoEHR2nDcOjF4GhbtHum15yCPaw5QVs/33nuPWze4ZLXReKk9p0mTh5V0p+\n"
"N3IvGlXl+uzEVu5d55eI7LIw5sLymHmwjWjxvimiMtrzLbCHSPHGc5JU9NLUH9/b\n"
"BY/JxGpy+NzcsHHOOQTwTdRIjviIOAo7fgQn2RyX0k+zXE8/7zqjqvji9zyemdNu\n"
"8we4uJICSntyvJwkbj/hrufTKEnBrwXpzfVn1EsH+6w32ZPBGLUhT75txJ8r56SR\n"
"q7l1XPU9vxovmT+lSMFF/Y0j1MbHWnds5H1shoFPNtYTvWBL/gfPHjIc+H23zsiu\n"
"3XlZAoIBAC2xB/Pnpoi9vOUMiqFH36AXtYa1DURy+AqCFlYlClMvb7YgvQ1w1eJv\n"
"nwrHSLk7HdKhnwGsLPduuRRH8q0n/osnoOutSQroE0n41UyIv2ZNccRwNmSzQcai\n"
"rBu2dSz02hlsh2otNl5IuGpOqXyPjXBpW4qGD6n2tH7THALnLC0BHtTSQVQsJsRM\n"
"3gX39LoiWvLDp2qJvplm6rTpi8Rgap6rZSqHe1yNKIxxD2vlr/WY9SMgLXYASO4S\n"
"SBz9wfGOmQIPk6KXNJkdV4kC7nNjIi75iwLLCgjHgUiHTrDq5sWekpeNnUoWsinb\n"
"Tsdsjnv3zHG9GyiClyLGxMbs4M5eyYECggEBAKuC8ZMpdIrjk6tERYB6g0LnQ7mW\n"
"8XYbDFAmLYMLs9yfG2jcjVbsW9Kugsr+3poUUv/q+hNO3jfY4HazhZDa0MalgNPo\n"
"Swr/VNRnkck40x2ovFb989J7yl++zTrnIrax9XRH1V0cNu+Kj7OMwZ2RRfbNv5JB\n"
"dOZPvkfqyIKFmbQgYbtD66rHuzNOfJpzqr/WVLO57/zzW8245NKG2B6B0oXkei/K\n"
"qDY0DAbHR3i3EOj1NPtVI1FC/xX8R9BREaid458bqoHJKuInrGcBjaUI9Cvymv8T\n"
"bstUgD6NPbJR4Sm6vrLeUqzjWZP3t1+Z6DjXmnpR2vvhMU/FWb//21p/88o=");
*privkey_type = NC_SSH_KEY_RSA;
return 0;
} else if (!strcmp(name, "main_cert")) {
*cert_path = strdup(TESTS_DIR "/data/server.crt");
*privkey_path = strdup(TESTS_DIR "/data/server.key");
return 0;
}
return 1;
}
static int
clb_trusted_cert_lists(const char *name, void *UNUSED(user_data), char ***cert_paths, int *cert_path_count,
char ***cert_data, int *cert_data_count)
{
if (!strcmp(name, "trusted_cert_list1")) {
*cert_data = malloc(sizeof **cert_data);
(*cert_data)[0] = strdup("MIIDnDCCAoSgAwIBAgIJAIjf7UNx4uabMA0GCSqGSIb3DQEBCwUAMGMxCzAJBgNV\n"
"BAYTAkNaMRMwEQYDVQQIDApTb21lLVN0YXRlMQ0wCwYDVQQHDARCcm5vMQ8wDQYD\n"
"VQQKDAZDRVNORVQxDDAKBgNVBAsMA1RNQzERMA8GA1UEAwwIc2VydmVyY2EwHhcN\n"
"MTgxMTA1MDcyNjM5WhcNMjgxMTAyMDcyNjM5WjBjMQswCQYDVQQGEwJDWjETMBEG\n"
"A1UECAwKU29tZS1TdGF0ZTENMAsGA1UEBwwEQnJubzEPMA0GA1UECgwGQ0VTTkVU\n"
"MQwwCgYDVQQLDANUTUMxETAPBgNVBAMMCHNlcnZlcmNhMIIBIjANBgkqhkiG9w0B\n"
"AQEFAAOCAQ8AMIIBCgKCAQEAyMrKraqraFGklO2itRIEWxfuzWo1IwxZ22aJmeXD\n"
"Leomt6893NXelMLaC3swQ+hu49JjiIY81DXvbVgmIgLm7cAz5tHTHuJbfdI4Q6gy\n"
"ic4aOpy2s3s1/vYz+1TvEUFCiPXEsJrH72he/z9nBxL8vY6Eg8U8EG8NvKp9zyCK\n"
"A7vmNSgQOtuyF18fesYHAnvQjNXO5q6diPXdHOr2bjTRUvARGbWlv4Rvf81RwUkR\n"
"sWoF/0pglV/TxnW2MoHnn3apxb/kmH92CQ+GWKxq5SkhvbkYePlA87kgKnDtXl4w\n"
"EXIhYwM51kafRhhlAKN+qYeV9teBqGpjsZRYesrh3mXHlQIDAQABo1MwUTAdBgNV\n"
"HQ4EFgQU60nJ4q3ItcfaOOBjJSqadAPiMg8wHwYDVR0jBBgwFoAU60nJ4q3Itcfa\n"
"OOBjJSqadAPiMg8wDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEA\n"
"xIqIQ5SCXnKslZfrXiAEbB5dglxVOSa8me5a/70uHK/27JZ6veeIgRqZ4VgPHnBC\n"
"a3m6EHr+mnTjjqSUcGIUiKV3g2Dumw8paqZC+Qv+Ib/NKquS1lO2Ry1wHBtXzn5K\n"
"KHHyM1bWMDaDszirw2+pp22VdRrPZNA9NWXheEDYOLyQekyL2CfidhxhaXvUZyWg\n"
"alLyF2XRZ5/jAT+NjfWw39EmWPUGk13Jm83OaFc1VdrXNCiD0sGCQ+BTCllDinQv\n"
"R08yzd4fzA3YXthvX1dBu1SvqQAGOS7gssRCyv9uWI6MXta25X91eY1ZMz1euJ04\n"
"mB8EdyYiZc0kzrb9dv5d0g==");
*cert_data_count = 1;
return 0;
} else if (!strcmp(name, "client_cert_list")) {
*cert_paths = malloc(sizeof **cert_paths);
(*cert_paths)[0] = strdup(TESTS_DIR "/data/client.crt");
*cert_path_count = 1;
return 0;
}
return 1;
}
static void *
endpt_set_address_thread(void *arg)
{
(void)arg;
int ret;
pthread_barrier_wait(&barrier);
ret = nc_server_endpt_set_address("quaternary", "0.0.0.0");
nc_assert(!ret);
return NULL;
}
static void *
endpt_set_port_thread(void *arg)
{
(void)arg;
int ret;
pthread_barrier_wait(&barrier);
ret = nc_server_endpt_set_port("quaternary", 6003);
nc_assert(!ret);
return NULL;
}
static void *
tls_endpt_set_server_cert_thread(void *arg)
{
(void)arg;
int ret;
pthread_barrier_wait(&barrier);
ret = nc_server_tls_endpt_set_server_cert("quaternary", "server_cert1");
nc_assert(!ret);
nc_thread_destroy();
return NULL;
}
static void *
tls_endpt_add_trusted_cert_list_thread(void *arg)
{
(void)arg;
int ret;
pthread_barrier_wait(&barrier);
ret = nc_server_tls_endpt_add_trusted_cert_list("quaternary", "trusted_cert_list1");
nc_assert(!ret);
nc_thread_destroy();
return NULL;
}
static void *
tls_endpt_set_trusted_ca_paths_thread(void *arg)
{
(void)arg;
int ret;
pthread_barrier_wait(&barrier);
ret = nc_server_tls_endpt_set_trusted_ca_paths("quaternary", TESTS_DIR "/data/serverca.pem", "data");
nc_assert(!ret);
nc_thread_destroy();
return NULL;
}
static void *
tls_endpt_del_trusted_cert_list_thread(void *arg)
{
(void)arg;
pthread_barrier_wait(&barrier);
nc_server_tls_endpt_del_trusted_cert_list("quaternary", "trusted_cert_list1");
return NULL;
}
static void *
tls_endpt_set_crl_paths_thread(void *arg)
{
(void)arg;
int ret;
pthread_barrier_wait(&barrier);
ret = nc_server_tls_endpt_set_crl_paths("quaternary", NULL, "data");
nc_assert(!ret);
nc_thread_destroy();
return NULL;
}
static void *
tls_endpt_clear_crls_thread(void *arg)
{
(void)arg;
pthread_barrier_wait(&barrier);
nc_server_tls_endpt_clear_crls("quaternary");
return NULL;
}
static void *
tls_endpt_add_ctn_thread(void *arg)
{
(void)arg;
int ret;
pthread_barrier_wait(&barrier);
ret = nc_server_tls_endpt_add_ctn("main_tls", 2, "02:F0:F1:F2:F3:F4:F5:F6:F7:F8:F9:10:11:12:EE:FF:A0:A1:A2:A3",
NC_TLS_CTN_SAN_IP_ADDRESS, NULL);
nc_assert(!ret);
return NULL;
}
static void *
tls_endpt_del_ctn_thread(void *arg)
{
(void)arg;
int ret;
pthread_barrier_wait(&barrier);
ret = nc_server_tls_endpt_del_ctn("main_tls", -1, NULL, NC_TLS_CTN_SAN_ANY, NULL);
nc_assert(!ret);
return NULL;
}
static void *
tls_client_thread(void *arg)
{
int ret, read_pipe = *(int *)arg;
char buf[9];
struct nc_session *session;
fprintf(stdout, "TLS client start.\n");
ret = read(read_pipe, buf, 9);
nc_assert(ret == 9);
nc_assert(!strncmp(buf, "tls_ready", 9));
ret = nc_client_tls_set_cert_key_paths(TESTS_DIR "/data/client.crt", TESTS_DIR "/data/client.key");
nc_assert(!ret);
ret = nc_client_tls_set_trusted_ca_paths(NULL, TESTS_DIR "/data");
nc_assert(!ret);
session = nc_connect_tls("127.0.0.1", 6501, NULL);
nc_assert(session);
/* verify some capabilities */
nc_assert(nc_session_cpblt(session, "urn:jmu:params:xml:ns:yang:module-a?module=module-a&deviations=module-a-dv,module-a-dv2"));
nc_session_free(session, NULL);
fprintf(stdout, "TLS client finished.\n");
nc_thread_destroy();
return NULL;
}
#endif /* NC_ENABLED_TLS */
static void *(*thread_funcs[])(void *) = {
#if defined (NC_ENABLED_SSH) || defined (NC_ENABLED_TLS)
server_thread,
#endif
#ifdef NC_ENABLED_SSH
add_endpt_thread,
del_endpt_thread,
ssh_endpt_set_hostkey_thread,
ssh_endpt_set_auth_methods_thread,
ssh_endpt_set_auth_attempts_thread,
ssh_endpt_set_auth_timeout_thread,
ssh_endpt_add_authkey_thread,
ssh_endpt_del_authkey_thread,
#endif
#ifdef NC_ENABLED_TLS
endpt_set_address_thread,
endpt_set_port_thread,
tls_endpt_set_server_cert_thread,
tls_endpt_add_trusted_cert_list_thread,
tls_endpt_set_trusted_ca_paths_thread,
tls_endpt_del_trusted_cert_list_thread,
tls_endpt_set_crl_paths_thread,
tls_endpt_clear_crls_thread,
tls_endpt_add_ctn_thread,
tls_endpt_del_ctn_thread,
#endif
};
const int thread_count = sizeof thread_funcs / sizeof *thread_funcs;
#if defined (NC_ENABLED_SSH) && defined (NC_ENABLED_TLS)
const int client_count = 2;
pid_t pids[2];
int pipes[4];
#else
const int client_count = 1;
pid_t pids[1];
int pipes[2];
#endif
static void
client_fork(void)
{
int ret, clients = 0;
#ifdef NC_ENABLED_SSH
nc_assert(pipe(pipes + clients * 2) == 0);
if (!(pids[clients] = fork())) {
nc_client_init();
ret = nc_client_set_schema_searchpath(TESTS_DIR "/data/modules");
nc_assert(!ret);
/* close write */
close(pipes[clients * 2 + 1]);
ssh_client_thread(&pipes[clients * 2]);
close(pipes[clients * 2]);
nc_client_destroy();
exit(0);
}
/* close read */
close(pipes[clients * 2]);
++clients;
#endif
#ifdef NC_ENABLED_TLS
nc_assert(pipe(pipes + clients * 2) == 0);
if (!(pids[clients] = fork())) {
nc_client_init();
ret = nc_client_set_schema_searchpath(TESTS_DIR "/data/modules");
nc_assert(!ret);
/* close write */
close(pipes[clients * 2 + 1]);
tls_client_thread(&pipes[clients * 2]);
close(pipes[clients * 2]);
nc_client_destroy();
exit(0);
}
/* close read */
close(pipes[clients * 2]);
++clients;
#endif
}
#endif
int
main(void)
{
#if _POSIX_BARRIERS >= 200112L
struct ly_ctx *ctx;
int ret, i, clients = 0;
pthread_t tids[thread_count];
nc_verbosity(NC_VERB_VERBOSE);
client_fork();
ly_ctx_new(TESTS_DIR "/data/modules", 0, &ctx);
nc_assert(ctx);
ly_ctx_load_module(ctx, "ietf-netconf", NULL, NULL);
/* load some application models with deviations */
nc_assert(ly_ctx_load_module(ctx, "module-a", NULL, NULL));
nc_assert(ly_ctx_load_module(ctx, "module-a-dv", NULL, NULL));
nc_assert(ly_ctx_load_module(ctx, "module-a-dv2", NULL, NULL));
nc_server_init(ctx);
pthread_barrier_init(&barrier, NULL, thread_count);
#ifdef NC_ENABLED_SSH
/* set callback */
nc_server_ssh_set_hostkey_clb(clb_hostkeys, NULL, NULL);
/* do first, so that client can connect on SSH */
ret = nc_server_add_endpt("main_ssh", NC_TI_LIBSSH);
nc_assert(!ret);
ret = nc_server_endpt_set_address("main_ssh", "0.0.0.0");
nc_assert(!ret);
ret = nc_server_endpt_set_port("main_ssh", 6001);
nc_assert(!ret);
ret = nc_server_ssh_add_authkey_path(TESTS_DIR "/data/key_ecdsa.pub", "test");
nc_assert(!ret);
ret = nc_server_ssh_endpt_add_hostkey("main_ssh", "key_rsa", -1);
nc_assert(!ret);
/* client ready */
ret = write(pipes[clients * 2 + 1], "ssh_ready", 9);
nc_assert(ret == 9);
++clients;
/* for ssh_endpt_del_authkey */
ret = nc_server_ssh_add_authkey_path(TESTS_DIR "/data/key_dsa.pub", "test2");
nc_assert(!ret);
ret = nc_server_add_endpt("secondary", NC_TI_LIBSSH);
nc_assert(!ret);
#endif
#ifdef NC_ENABLED_TLS
/* set callbacks */
nc_server_tls_set_server_cert_clb(clb_server_cert, NULL, NULL);
nc_server_tls_set_trusted_cert_list_clb(clb_trusted_cert_lists, NULL, NULL);
/* do first, so that client can connect on TLS */
ret = nc_server_add_endpt("main_tls", NC_TI_OPENSSL);
nc_assert(!ret);
ret = nc_server_endpt_set_address("main_tls", "0.0.0.0");
nc_assert(!ret);
ret = nc_server_endpt_set_port("main_tls", 6501);
nc_assert(!ret);
ret = nc_server_tls_endpt_set_server_cert("main_tls", "main_cert");
nc_assert(!ret);
ret = nc_server_tls_endpt_add_trusted_cert_list("main_tls", "client_cert_list");
nc_assert(!ret);
ret = nc_server_tls_endpt_add_ctn("main_tls", 0, "02:B3:9F:26:65:76:6B:CC:FC:86:8E:D4:1A:81:64:0F:92:EB:18:AE:FF", NC_TLS_CTN_SPECIFIED, "test");
nc_assert(!ret);
/* client ready */
ret = write(pipes[clients * 2 + 1], "tls_ready", 9);
nc_assert(ret == 9);
++clients;
/* for tls_endpt_del_ctn */
ret = nc_server_tls_endpt_add_ctn("main_tls", 1, "02:00:11:22:33:44:55:66:77:88:99:AA:BB:CC:DD:EE:FF:A0:A1:A2:A3", NC_TLS_CTN_SAN_ANY, NULL);
nc_assert(!ret);
ret = nc_server_add_endpt("quaternary", NC_TI_OPENSSL);
nc_assert(!ret);
#endif
/* threads'n'stuff */
ret = 0;
for (i = 0; i < thread_count; ++i) {
ret += pthread_create(&tids[i], NULL, thread_funcs[i], NULL);
}
nc_assert(!ret);
/* cleanup */
for (i = 0; i < thread_count; ++i) {
pthread_join(tids[i], NULL);
}
for (i = 0; i < client_count; ++i) {
waitpid(pids[i], NULL, 0);
close(pipes[i * 2 + 1]);
}
pthread_barrier_destroy(&barrier);
nc_server_destroy();
ly_ctx_destroy(ctx);
#endif
return 0;
}

View file

@ -1,15 +0,0 @@
{
<custom_func_global_variables_check>
Helgrind:Race
fun:CRYPTO_malloc
}
{
<global_variable_always_set_with_default_method_when_getting>
Helgrind:Race
fun:DSA_get_default_method
}
{
<helgrind_ignores_rwlocks>
Helgrind:Race
fun:nc_server_endpt_set_address_port
}

View file

@ -13,6 +13,8 @@
* https://opensource.org/licenses/BSD-3-Clause
*/
#define _GNU_SOURCE
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
@ -23,17 +25,8 @@
#include <libyang/libyang.h>
#include <log.h>
#include <messages_p.h>
#include <messages_server.h>
#include <session_client.h>
#include <session_server.h>
#include "tests/config.h"
#include "ln2_test.h"
/* millisec */
#define NC_ACCEPT_TIMEOUT 5000
/* millisec */
#define NC_PS_POLL_TIMEOUT 5000
/* sec */
#define CLIENT_SSH_AUTH_TIMEOUT 10
@ -71,8 +64,8 @@ server_thread(void *arg)
const char *data;
int poll;
nc_assert(!nc_server_init(args.ctx));
nc_assert(nc_accept_inout(args.in, args.out, "test", &sess) == NC_MSG_HELLO);
nc_assert(!nc_server_init());
nc_assert(nc_accept_inout(args.in, args.out, "test", args.ctx, &sess) == NC_MSG_HELLO);
nc_session_inc_notif_status(sess);
data =
"<n1 xmlns=\"n1\">\n"
@ -90,9 +83,15 @@ server_thread(void *arg)
ps = nc_ps_new();
nc_assert(ps);
nc_ps_add_session(ps, sess);
/* get for ietf-yang-library data; delete-config in test */
poll = nc_ps_poll(ps, 1000, &sess);
nc_server_notif_send(sess, notif, 1000);
nc_assert(poll == NC_PSPOLL_RPC);
poll = nc_ps_poll(ps, 1000, &sess);
nc_assert(poll == NC_PSPOLL_RPC);
nc_server_notif_send(sess, notif, 1000);
nc_ps_clear(ps, 1, NULL);
nc_ps_free(ps);
@ -128,7 +127,7 @@ main(void)
int pipes[4];
struct nc_session *sess;
struct lyd_node *op, *envp;
struct ly_ctx *ctx;
struct ly_ctx *server_ctx, *client_ctx;
struct nc_rpc *rpc;
uint64_t msgid;
NC_MSG_TYPE msgtype;
@ -145,19 +144,23 @@ main(void)
thread_arg.in = pipes[0];
thread_arg.out = pipes[3];
/* Create context */
nc_assert(ly_ctx_new(TESTS_DIR "/data/modules", 0, &ctx) == LY_SUCCESS);
nc_assert(ly_ctx_load_module(ctx, "ietf-netconf", NULL, features));
nc_assert(ly_ctx_load_module(ctx, "notif1", NULL, NULL));
thread_arg.ctx = ctx;
/* Create both contexts */
nc_assert(ly_ctx_new(TESTS_DIR "/data/modules", 0, &server_ctx) == LY_SUCCESS);
nc_assert(ly_ctx_load_module(server_ctx, "ietf-netconf", NULL, features));
nc_assert(ly_ctx_load_module(server_ctx, "notif1", NULL, NULL));
thread_arg.ctx = server_ctx;
nc_set_global_rpc_clb(rpc_clb);
nc_assert(ly_ctx_new(TESTS_DIR "/data/modules", 0, &client_ctx) == LY_SUCCESS);
nc_assert(ly_ctx_load_module(client_ctx, "ietf-netconf", NULL, features));
nc_assert(ly_ctx_load_module(client_ctx, "notif1", NULL, NULL));
/* Start server thread */
pthread_create(&t[0], NULL, server_thread, &thread_arg);
nc_client_init();
/* Listen for notifications */
sess = nc_connect_inout(pipes[2], pipes[1], ctx);
sess = nc_connect_inout(pipes[2], pipes[1], client_ctx);
nc_assert(sess);
pthread_create(&t[1], NULL, notif_thread, sess);
@ -181,7 +184,8 @@ main(void)
/* Cleanup */
nc_session_free(sess, NULL);
ly_ctx_destroy(ctx);
ly_ctx_destroy(server_ctx);
ly_ctx_destroy(client_ctx);
for (uint8_t i = 0; i < 4; i++) {
close(pipes[i]);
}

237
tests/test_tls.c Normal file
View file

@ -0,0 +1,237 @@
/**
* @file test_tls.c
* @author Roman Janota <janota@cesnet.cz>
* @brief libnetconf2 TLS authentication test
*
* @copyright
* Copyright (c) 2023 - 2024 CESNET, z.s.p.o.
*
* This source code is licensed under BSD 3-Clause License (the "License").
* You may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://opensource.org/licenses/BSD-3-Clause
*/
#define _GNU_SOURCE
#include <pthread.h>
#include <setjmp.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <cmocka.h>
#include "ln2_test.h"
#define KEYLOG_FILENAME "ln2_test_tls_keylog.txt"
int TEST_PORT = 10050;
const char *TEST_PORT_STR = "10050";
static void *
client_thread(void *arg)
{
int ret;
struct nc_session *session = NULL;
struct ln2_test_ctx *test_ctx = arg;
ret = nc_client_set_schema_searchpath(MODULES_DIR);
assert_int_equal(ret, 0);
/* set client cert */
ret = nc_client_tls_set_cert_key_paths(TESTS_DIR "/data/client.crt", TESTS_DIR "/data/client.key");
assert_int_equal(ret, 0);
/* set client ca */
ret = nc_client_tls_set_trusted_ca_paths(NULL, TESTS_DIR "/data");
assert_int_equal(ret, 0);
pthread_barrier_wait(&test_ctx->barrier);
session = nc_connect_tls("127.0.0.1", TEST_PORT, NULL);
assert_non_null(session);
nc_session_free(session, NULL);
return NULL;
}
static void
test_nc_tls(void **state)
{
int ret, i;
pthread_t tids[2];
assert_non_null(state);
ret = pthread_create(&tids[0], NULL, client_thread, *state);
assert_int_equal(ret, 0);
ret = pthread_create(&tids[1], NULL, ln2_glob_test_server_thread, *state);
assert_int_equal(ret, 0);
for (i = 0; i < 2; i++) {
pthread_join(tids[i], NULL);
}
}
static void
test_nc_tls_ec_key(void **state)
{
int ret, i;
pthread_t tids[2];
struct ln2_test_ctx *test_ctx;
assert_non_null(state);
test_ctx = *state;
ret = nc_server_config_add_tls_server_cert(test_ctx->ctx, "endpt", TESTS_DIR "/data/ec_server.key",
NULL, TESTS_DIR "/data/ec_server.crt", (struct lyd_node **)&test_ctx->test_data);
assert_int_equal(ret, 0);
ret = nc_server_config_setup_data(test_ctx->test_data);
assert_int_equal(ret, 0);
ret = pthread_create(&tids[0], NULL, client_thread, *state);
assert_int_equal(ret, 0);
ret = pthread_create(&tids[1], NULL, ln2_glob_test_server_thread, *state);
assert_int_equal(ret, 0);
for (i = 0; i < 2; i++) {
pthread_join(tids[i], NULL);
}
}
static void
check_keylog_file(const char *filename)
{
char buf[256];
FILE *f;
int cli_random, cli_hs, cli_traffic, srv_hs, srv_traffic;
cli_random = cli_hs = cli_traffic = srv_hs = srv_traffic = 0;
f = fopen(filename, "r");
assert_non_null(f);
while (fgets(buf, sizeof(buf), f)) {
if (!strncmp(buf, "CLIENT_RANDOM", 13)) {
cli_random++;
} else if (!strncmp(buf, "CLIENT_HANDSHAKE_TRAFFIC_SECRET", 31)) {
cli_hs++;
} else if (!strncmp(buf, "CLIENT_TRAFFIC_SECRET_0", 23)) {
cli_traffic++;
} else if (!strncmp(buf, "SERVER_HANDSHAKE_TRAFFIC_SECRET", 31)) {
srv_hs++;
} else if (!strncmp(buf, "SERVER_TRAFFIC_SECRET_0", 23)) {
srv_traffic++;
}
}
fclose(f);
if (cli_random) {
/* tls 1.2 */
assert_int_equal(cli_random, 1);
assert_int_equal(cli_hs + cli_traffic + srv_hs + srv_traffic, 0);
} else {
/* tls 1.3 */
assert_int_equal(cli_hs + cli_traffic + srv_hs + srv_traffic, 4);
}
}
static void
test_nc_tls_keylog(void **state)
{
int ret, i;
pthread_t tids[2];
assert_non_null(state);
ret = pthread_create(&tids[0], NULL, client_thread, *state);
assert_int_equal(ret, 0);
ret = pthread_create(&tids[1], NULL, ln2_glob_test_server_thread, *state);
assert_int_equal(ret, 0);
for (i = 0; i < 2; i++) {
pthread_join(tids[i], NULL);
}
check_keylog_file(KEYLOG_FILENAME);
}
static void
test_nc_tls_free_test_data(void *test_data)
{
lyd_free_all(test_data);
}
static int
setup_f(void **state)
{
int ret;
struct lyd_node *tree = NULL;
struct ln2_test_ctx *test_ctx;
ret = ln2_glob_test_setup(&test_ctx);
assert_int_equal(ret, 0);
*state = test_ctx;
/* create new address and port data */
ret = nc_server_config_add_address_port(test_ctx->ctx, "endpt", NC_TI_TLS, "127.0.0.1", TEST_PORT, &tree);
assert_int_equal(ret, 0);
/* create new server certificate data */
ret = nc_server_config_add_tls_server_cert(test_ctx->ctx, "endpt", TESTS_DIR "/data/server.key", NULL, TESTS_DIR "/data/server.crt", &tree);
assert_int_equal(ret, 0);
/* create new end entity client cert data */
ret = nc_server_config_add_tls_client_cert(test_ctx->ctx, "endpt", "client_cert", TESTS_DIR "/data/client.crt", &tree);
assert_int_equal(ret, 0);
/* create new client ca data */
ret = nc_server_config_add_tls_ca_cert(test_ctx->ctx, "endpt", "client_ca", TESTS_DIR "/data/serverca.pem", &tree);
assert_int_equal(ret, 0);
/* create new cert-to-name */
ret = nc_server_config_add_tls_ctn(test_ctx->ctx, "endpt", 1,
"04:85:6B:75:D1:1A:86:E0:D8:FE:5B:BD:72:F5:73:1D:07:EA:32:BF:09:11:21:6A:6E:23:78:8E:B6:D5:73:C3:2D",
NC_TLS_CTN_SPECIFIED, "client", &tree);
assert_int_equal(ret, 0);
/* configure the server based on the data */
ret = nc_server_config_setup_data(tree);
assert_int_equal(ret, 0);
test_ctx->test_data = tree;
test_ctx->free_test_data = test_nc_tls_free_test_data;
return 0;
}
static int
keylog_setup_f(void **state)
{
unlink(KEYLOG_FILENAME);
setenv("SSLKEYLOGFILE", KEYLOG_FILENAME, 1);
return setup_f(state);
}
int
main(void)
{
const struct CMUnitTest tests[] = {
cmocka_unit_test_setup_teardown(test_nc_tls, setup_f, ln2_glob_test_teardown),
cmocka_unit_test_setup_teardown(test_nc_tls_ec_key, setup_f, ln2_glob_test_teardown),
cmocka_unit_test_setup_teardown(test_nc_tls_keylog, keylog_setup_f, ln2_glob_test_teardown)
};
/* try to get ports from the environment, otherwise use the default */
if (ln2_glob_test_get_ports(1, &TEST_PORT, &TEST_PORT_STR)) {
return 1;
}
setenv("CMOCKA_TEST_ABORT", "1", 1);
return cmocka_run_group_tests(tests, NULL, NULL);
}

180
tests/test_two_channels.c Normal file
View file

@ -0,0 +1,180 @@
/**
* @file test_two_channels.c
* @author Roman Janota <janota@cesnet.cz>
* @brief libnetconf2 Openning a new session on an established SSH channel test.
*
* @copyright
* Copyright (c) 2023 - 2024 CESNET, z.s.p.o.
*
* This source code is licensed under BSD 3-Clause License (the "License").
* You may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://opensource.org/licenses/BSD-3-Clause
*/
#define _GNU_SOURCE
#include <errno.h>
#include <pthread.h>
#include <setjmp.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <cmocka.h>
#include "ln2_test.h"
#define BACKOFF_TIMEOUT_USECS 100
int TEST_PORT = 10050;
const char *TEST_PORT_STR = "10050";
static void *
server_thread(void *arg)
{
int ret, del_session_count = 0, sleep_count = 0;
NC_MSG_TYPE msgtype;
struct nc_session *session, *new_session;
struct nc_pollsession *ps;
struct ln2_test_ctx *test_ctx = arg;
ps = nc_ps_new();
assert_non_null(ps);
while (del_session_count < 2) {
msgtype = nc_accept(0, test_ctx->ctx, &new_session);
if (msgtype == NC_MSG_HELLO) {
ret = nc_ps_add_session(ps, new_session);
assert_int_equal(ret, 0);
}
ret = nc_ps_poll(ps, 0, &session);
if (ret & NC_PSPOLL_SESSION_TERM) {
nc_ps_del_session(ps, session);
nc_session_free(session, NULL);
del_session_count++;
} else if (ret & NC_PSPOLL_SSH_CHANNEL) {
msgtype = nc_session_accept_ssh_channel(session, &new_session);
if (msgtype == NC_MSG_HELLO) {
ret = nc_ps_add_session(ps, new_session);
assert_int_equal(ret, 0);
}
} else if (ret & NC_PS_POLL_TIMEOUT) {
usleep(BACKOFF_TIMEOUT_USECS);
sleep_count++;
assert_int_not_equal(sleep_count, 50000);
}
}
nc_ps_free(ps);
return NULL;
}
static void *
client_thread(void *arg)
{
int ret;
struct nc_session *session_cl1, *session_cl2;
(void) 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_add_keypair(TESTS_DIR "/data/id_ed25519.pub", TESTS_DIR "/data/id_ed25519");
assert_int_equal(ret, 0);
ret = nc_client_ssh_set_username("client_1");
assert_int_equal(ret, 0);
session_cl1 = nc_connect_ssh("127.0.0.1", TEST_PORT, NULL);
assert_non_null(session_cl1);
ret = nc_client_ssh_set_username("client_2");
assert_int_equal(ret, 0);
ret = nc_client_ssh_del_keypair(0);
assert_int_equal(ret, 0);
ret = nc_client_ssh_add_keypair(TESTS_DIR "/data/id_ecdsa521.pub", TESTS_DIR "/data/id_ecdsa521");
assert_int_equal(ret, 0);
session_cl2 = nc_connect_ssh_channel(session_cl1, NULL);
assert_non_null(session_cl2);
nc_session_free(session_cl1, NULL);
nc_session_free(session_cl2, NULL);
return NULL;
}
static void
test_nc_two_channels(void **state)
{
int ret, i;
pthread_t tids[2];
ret = pthread_create(&tids[0], NULL, client_thread, *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 int
setup_f(void **state)
{
int ret;
struct lyd_node *tree = NULL;
struct ln2_test_ctx *test_ctx;
ret = ln2_glob_test_setup(&test_ctx);
assert_int_equal(ret, 0);
*state = test_ctx;
ret = nc_server_config_add_address_port(test_ctx->ctx, "endpt", NC_TI_SSH, "127.0.0.1", TEST_PORT, &tree);
assert_int_equal(ret, 0);
ret = nc_server_config_add_ssh_hostkey(test_ctx->ctx, "endpt", "hostkey", TESTS_DIR "/data/key_ecdsa", NULL, &tree);
assert_int_equal(ret, 0);
ret = nc_server_config_add_ssh_user_pubkey(test_ctx->ctx, "endpt", "client_1", "pubkey", TESTS_DIR "/data/id_ed25519.pub", &tree);
assert_int_equal(ret, 0);
ret = nc_server_config_add_ssh_user_pubkey(test_ctx->ctx, "endpt", "client_2", "pubkey", TESTS_DIR "/data/id_ecdsa521.pub", &tree);
assert_int_equal(ret, 0);
ret = nc_server_config_setup_data(tree);
assert_int_equal(ret, 0);
lyd_free_all(tree);
return 0;
}
int
main(void)
{
const struct CMUnitTest tests[] = {
cmocka_unit_test_setup_teardown(test_nc_two_channels, setup_f, ln2_glob_test_teardown),
};
/* try to get ports from the environment, otherwise use the default */
if (ln2_glob_test_get_ports(1, &TEST_PORT, &TEST_PORT_STR)) {
return 1;
}
setenv("CMOCKA_TEST_ABORT", "1", 1);
return cmocka_run_group_tests(tests, NULL, NULL);
}

95
tests/test_unix_socket.c Normal file
View file

@ -0,0 +1,95 @@
/**
* @file test_unix_socket.c
* @author Roman Janota <janota@cesnet.cz>
* @brief libnetconf2 UNIX socket test
*
* @copyright
* Copyright (c) 2022 - 2024 CESNET, z.s.p.o.
*
* This source code is licensed under BSD 3-Clause License (the "License").
* You may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://opensource.org/licenses/BSD-3-Clause
*/
#define _GNU_SOURCE
#include <errno.h>
#include <pthread.h>
#include <setjmp.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <cmocka.h>
#include "ln2_test.h"
static void *
client_thread(void *arg)
{
int ret = 0;
struct nc_session *session = NULL;
struct ln2_test_ctx *test_ctx = arg;
ret = nc_client_set_schema_searchpath(MODULES_DIR);
assert_int_equal(ret, 0);
pthread_barrier_wait(&test_ctx->barrier);
session = nc_connect_unix("/tmp/nc2_test_unix_sock", NULL);
assert_non_null(session);
nc_session_free(session, NULL);
return NULL;
}
static void
test_nc_connect_unix_socket(void **state)
{
int ret, i;
pthread_t tids[2];
assert_non_null(state);
ret = pthread_create(&tids[0], NULL, client_thread, *state);
assert_int_equal(ret, 0);
ret = pthread_create(&tids[1], NULL, ln2_glob_test_server_thread, *state);
assert_int_equal(ret, 0);
for (i = 0; i < 2; i++) {
pthread_join(tids[i], NULL);
}
}
static int
setup_f(void **state)
{
int ret;
struct lyd_node *tree = NULL;
struct ln2_test_ctx *test_ctx;
ret = ln2_glob_test_setup(&test_ctx);
assert_int_equal(ret, 0);
*state = test_ctx;
/* create the UNIX socket */
ret = nc_server_add_endpt_unix_socket_listen("unix", "/tmp/nc2_test_unix_sock", 0700, -1, -1);
assert_int_equal(ret, 0);
lyd_free_all(tree);
return 0;
}
int
main(void)
{
const struct CMUnitTest tests[] = {
cmocka_unit_test_setup_teardown(test_nc_connect_unix_socket, setup_f, ln2_glob_test_teardown),
};
setenv("CMOCKA_TEST_ABORT", "1", 1);
return cmocka_run_group_tests(tests, NULL, NULL);
}