diff --git a/tests/unittests/torture_config.c b/tests/unittests/torture_config.c index 4b225fdd..e18cf9cb 100644 --- a/tests/unittests/torture_config.c +++ b/tests/unittests/torture_config.c @@ -17,6 +17,7 @@ extern LIBSSH_THREAD int ssh_log_level; #define LIBSSH_TESTCONFIG7 "libssh_testconfig7.tmp" #define LIBSSH_TESTCONFIG8 "libssh_testconfig8.tmp" #define LIBSSH_TESTCONFIG9 "libssh_testconfig9.tmp" +#define LIBSSH_TESTCONFIG10 "libssh_testconfig10.tmp" #define LIBSSH_TESTCONFIGGLOB "libssh_testc*[36].tmp" #define USERNAME "testuser" @@ -106,6 +107,22 @@ static int setup_config_files(void **state) "VisualHostkey yes\n" /* SOC_UNSUPPORTED */ ""); + /* Match keyword */ + torture_write_file(LIBSSH_TESTCONFIG10, + "Match host example\n" + "\tHostName example.com\n" + "Match host example1,example2\n" + "\tHostName exampleN\n" + "Match user guest\n" + "\tHostName guest.com\n" + "Match user tester host testhost\n" + "\tHostName testhost.com\n" + "Match !user tester host testhost\n" + "\tHostName nonuser-testhost.com\n" + "Match all\n" + "\tHostName all-matched.com\n" + ""); + session = ssh_new(); verbosity = torture_libssh_verbosity(); @@ -127,6 +144,7 @@ static int teardown(void **state) unlink(LIBSSH_TESTCONFIG7); unlink(LIBSSH_TESTCONFIG8); unlink(LIBSSH_TESTCONFIG9); + unlink(LIBSSH_TESTCONFIG10); ssh_free(*state); @@ -303,6 +321,61 @@ static void torture_config_unknown(void **state) { assert_true(ret == 0); } + +/** + * @brief Verify the configuration parser accepts Match keyword with + * full OpenSSH syntax. + */ +static void torture_config_match(void **state) +{ + ssh_session session = *state; + int ret = 0; + + /* Without any settings we should get all-matched.com hostname */ + ssh_options_set(session, SSH_OPTIONS_HOST, "unmatched"); + ret = ssh_config_parse_file(session, LIBSSH_TESTCONFIG10); + assert_true(ret == 0); + assert_string_equal(session->opts.host, "all-matched.com"); + + /* Hostname example does simple hostname matching */ + ssh_options_set(session, SSH_OPTIONS_HOST, "example"); + ret = ssh_config_parse_file(session, LIBSSH_TESTCONFIG10); + assert_true(ret == 0); + assert_string_equal(session->opts.host, "example.com"); + + /* We can match also both hosts from a comma separated list */ + ssh_options_set(session, SSH_OPTIONS_HOST, "example1"); + ret = ssh_config_parse_file(session, LIBSSH_TESTCONFIG10); + assert_true(ret == 0); + assert_string_equal(session->opts.host, "exampleN"); + + ssh_options_set(session, SSH_OPTIONS_HOST, "example2"); + ret = ssh_config_parse_file(session, LIBSSH_TESTCONFIG10); + assert_true(ret == 0); + assert_string_equal(session->opts.host, "exampleN"); + + /* We can match by user */ + ssh_options_set(session, SSH_OPTIONS_USER, "guest"); + ret = ssh_config_parse_file(session, LIBSSH_TESTCONFIG10); + assert_true(ret == 0); + assert_string_equal(session->opts.host, "guest.com"); + + /* We can combine two options on a single line to match both of them */ + ssh_options_set(session, SSH_OPTIONS_USER, "tester"); + ssh_options_set(session, SSH_OPTIONS_HOST, "testhost"); + ret = ssh_config_parse_file(session, LIBSSH_TESTCONFIG10); + assert_true(ret == 0); + assert_string_equal(session->opts.host, "testhost.com"); + + /* We can also negate conditions */ + ssh_options_set(session, SSH_OPTIONS_USER, "not-tester"); + ssh_options_set(session, SSH_OPTIONS_HOST, "testhost"); + ret = ssh_config_parse_file(session, LIBSSH_TESTCONFIG10); + assert_true(ret == 0); + assert_string_equal(session->opts.host, "nonuser-testhost.com"); + +} + int torture_run_tests(void) { int rc; struct CMUnitTest tests[] = { @@ -324,6 +397,9 @@ int torture_run_tests(void) { cmocka_unit_test_setup_teardown(torture_config_unknown, setup_config_files, teardown), + cmocka_unit_test_setup_teardown(torture_config_match, + setup_config_files, + teardown), }; diff --git a/tests/unittests/torture_options.c b/tests/unittests/torture_options.c index 06a12e81..a4e83285 100644 --- a/tests/unittests/torture_options.c +++ b/tests/unittests/torture_options.c @@ -400,6 +400,133 @@ static void torture_options_config_host(void **state) { unlink("test_config"); } +static void torture_options_config_match(void **state) +{ + ssh_session session = *state; + FILE *config = NULL; + int rv; + + /* Required for options_parse_config() */ + ssh_options_set(session, SSH_OPTIONS_HOST, "testhost1"); + + /* The Match keyword requires argument */ + config = fopen("test_config", "w"); + assert_non_null(config); + fputs("Match\n", + config); + fclose(config); + + rv = ssh_options_parse_config(session, "test_config"); + assert_ssh_return_code_equal(session, rv, SSH_ERROR); + + /* The Match all keyword needs to be the only one (start) */ + config = fopen("test_config", "w"); + assert_non_null(config); + fputs("Match all host local\n", + config); + fclose(config); + + rv = ssh_options_parse_config(session, "test_config"); + assert_ssh_return_code_equal(session, rv, SSH_ERROR); + + /* The Match all keyword needs to be the only one (end) */ + config = fopen("test_config", "w"); + assert_non_null(config); + fputs("Match host local all\n", + config); + fclose(config); + + rv = ssh_options_parse_config(session, "test_config"); + assert_ssh_return_code_equal(session, rv, SSH_ERROR); + + /* The Match host keyword requires an argument */ + config = fopen("test_config", "w"); + assert_non_null(config); + fputs("Match host\n", + config); + fclose(config); + + rv = ssh_options_parse_config(session, "test_config"); + assert_ssh_return_code_equal(session, rv, SSH_ERROR); + + /* The Match user keyword requires an argument */ + config = fopen("test_config", "w"); + assert_non_null(config); + fputs("Match user\n", + config); + fclose(config); + + rv = ssh_options_parse_config(session, "test_config"); + assert_ssh_return_code_equal(session, rv, SSH_ERROR); + + /* The Match canonical keyword is ignored */ + config = fopen("test_config", "w"); + assert_non_null(config); + fputs("Match canonical\n" + "\tPort 33\n" + "Match all\n" + "\tPort 34\n", + config); + fclose(config); + + rv = ssh_options_parse_config(session, "test_config"); + assert_ssh_return_code_equal(session, rv, SSH_OK); + assert_int_equal(session->opts.port, 34); + + session->opts.port = 0; + + /* The Match originalhost keyword is ignored */ + config = fopen("test_config", "w"); + assert_non_null(config); + fputs("Match originalhost origin\n" + "\tPort 33\n" + "Match all\n" + "\tPort 34\n", + config); + fclose(config); + + rv = ssh_options_parse_config(session, "test_config"); + assert_ssh_return_code(session, rv); + assert_int_equal(session->opts.port, 34); + + session->opts.port = 0; + + /* The Match localuser keyword is ignored */ + config = fopen("test_config", "w"); + assert_non_null(config); + fputs("Match originalhost origin\n" + "\tPort 33\n" + "Match all\n" + "\tPort 34\n", + config); + fclose(config); + + rv = ssh_options_parse_config(session, "test_config"); + assert_ssh_return_code(session, rv); + assert_int_equal(session->opts.port, 34); + + session->opts.port = 0; + + /* The Match exec keyword is ignored */ + config = fopen("test_config", "w"); + assert_non_null(config); + fputs("Match exec /bin/true\n" + "\tPort 33\n" + "Match all\n" + "\tPort 34\n", + config); + fclose(config); + + rv = ssh_options_parse_config(session, "test_config"); + assert_ssh_return_code(session, rv); + assert_int_equal(session->opts.port, 34); + + session->opts.port = 0; + + unlink("test_config"); +} + + #ifdef WITH_SERVER /* sshbind options */ @@ -472,7 +599,9 @@ int torture_run_tests(void) { cmocka_unit_test_setup_teardown(torture_options_set_hostkey, setup, teardown), cmocka_unit_test_setup_teardown(torture_options_set_pubkey_accepted_types, setup, teardown), cmocka_unit_test_setup_teardown(torture_options_set_macs, setup, teardown), - cmocka_unit_test_setup_teardown(torture_options_config_host, setup, teardown) + cmocka_unit_test_setup_teardown(torture_options_config_host, setup, teardown), + cmocka_unit_test_setup_teardown(torture_options_config_match, + setup, teardown) }; #ifdef WITH_SERVER