From abe222e1e86865b506d5663dfe5ca86cb3bf7339 Mon Sep 17 00:00:00 2001 From: Norbert Pocs Date: Thu, 10 Nov 2022 10:50:52 +0100 Subject: [PATCH] torture_config.c: Add test for +,-,^ config feature It should be possible to use features to add,remove,prioritize algorithms in the algorithm list from the config file. Signed-off-by: Norbert Pocs Reviewed-by: Jakub Jelen --- tests/unittests/torture_config.c | 393 +++++++++++++++++++++++++++++++ 1 file changed, 393 insertions(+) diff --git a/tests/unittests/torture_config.c b/tests/unittests/torture_config.c index 9cc20f04..cbde56a4 100644 --- a/tests/unittests/torture_config.c +++ b/tests/unittests/torture_config.c @@ -40,6 +40,9 @@ extern LIBSSH_THREAD int ssh_log_level; #define LIBSSH_TESTCONFIG10 "libssh_testconfig10.tmp" #define LIBSSH_TESTCONFIG11 "libssh_testconfig11.tmp" #define LIBSSH_TESTCONFIG12 "libssh_testconfig12.tmp" +#define LIBSSH_TESTCONFIG14 "libssh_testconfig14.tmp" +#define LIBSSH_TESTCONFIG15 "libssh_testconfig15.tmp" +#define LIBSSH_TESTCONFIG16 "libssh_testconfig16.tmp" #define LIBSSH_TESTCONFIGGLOB "libssh_testc*[36].tmp" #define LIBSSH_TEST_PUBKEYTYPES "libssh_test_PubkeyAcceptedKeyTypes.tmp" #define LIBSSH_TEST_PUBKEYALGORITHMS "libssh_test_PubkeyAcceptedAlgorithms.tmp" @@ -181,6 +184,27 @@ extern LIBSSH_THREAD int ssh_log_level; "IdentityFile id_rsa_one\n" \ "IdentityFile id_ecdsa_two\n" +/* +,-,^ features for all supported list */ +/* kex won't work in fips */ +#define LIBSSH_TESTCONFIG_STRING14 \ + "HostKeyAlgorithms +ssh-rsa\n" \ + "Ciphers +aes128-cbc,aes256-cbc\n" \ + "KexAlgorithms +diffie-hellman-group14-sha1,diffie-hellman-group1-sha1\n" \ + "MACs +hmac-sha1,hmac-sha1-etm@openssh.com\n" + +/* have to be algorithms which are in the default list */ +#define LIBSSH_TESTCONFIG_STRING15 \ + "HostKeyAlgorithms -rsa-sha2-512,rsa-sha2-256\n" \ + "Ciphers -aes256-ctr\n" \ + "KexAlgorithms -diffie-hellman-group18-sha512,diffie-hellman-group16-sha512\n" \ + "MACs -hmac-sha2-256-etm@openssh.com\n" + +#define LIBSSH_TESTCONFIG_STRING16 \ + "HostKeyAlgorithms ^rsa-sha2-512,rsa-sha2-256\n" \ + "Ciphers ^aes256-cbc\n" \ + "KexAlgorithms ^diffie-hellman-group18-sha512,diffie-hellman-group16-sha512\n" \ + "MACs ^hmac-sha1\n" + #define LIBSSH_TEST_PUBKEYTYPES_STRING \ "PubkeyAcceptedKeyTypes "PUBKEYACCEPTEDTYPES"\n" @@ -238,6 +262,9 @@ static int setup_config_files(void **state) unlink(LIBSSH_TESTCONFIG10); unlink(LIBSSH_TESTCONFIG11); unlink(LIBSSH_TESTCONFIG12); + unlink(LIBSSH_TESTCONFIG14); + unlink(LIBSSH_TESTCONFIG15); + unlink(LIBSSH_TESTCONFIG16); unlink(LIBSSH_TEST_PUBKEYTYPES); unlink(LIBSSH_TEST_PUBKEYALGORITHMS); unlink(LIBSSH_TEST_NONEWLINEEND); @@ -285,6 +312,14 @@ static int setup_config_files(void **state) torture_write_file(LIBSSH_TESTCONFIG12, LIBSSH_TESTCONFIG_STRING12); + /* +,-,^ feature */ + torture_write_file(LIBSSH_TESTCONFIG14, + LIBSSH_TESTCONFIG_STRING14); + torture_write_file(LIBSSH_TESTCONFIG15, + LIBSSH_TESTCONFIG_STRING15); + torture_write_file(LIBSSH_TESTCONFIG16, + LIBSSH_TESTCONFIG_STRING16); + torture_write_file(LIBSSH_TEST_PUBKEYTYPES, LIBSSH_TEST_PUBKEYTYPES_STRING); @@ -316,6 +351,9 @@ static int teardown_config_files(void **state) unlink(LIBSSH_TESTCONFIG10); unlink(LIBSSH_TESTCONFIG11); unlink(LIBSSH_TESTCONFIG12); + unlink(LIBSSH_TESTCONFIG14); + unlink(LIBSSH_TESTCONFIG15); + unlink(LIBSSH_TESTCONFIG16); unlink(LIBSSH_TEST_PUBKEYTYPES); unlink(LIBSSH_TEST_PUBKEYALGORITHMS); @@ -1267,6 +1305,349 @@ static void torture_config_rekey_string(void **state) torture_config_rekey(state, NULL, LIBSSH_TESTCONFIG_STRING12); } +/** + * @brief Remove substring from a string + * + * @param occurrence 0 means "remove the first occurrence" + * 1 means "remove the second occurrence" and so on + */ +static void helper_remove_substring(char *s, const char *subs, int occurrence) { + char *p; + /* remove the substring from the defaults */ + p = strstr(s, subs); + assert_non_null(p); + /* look for second occurrence */ + for (int i = 0; i < occurrence; i++) { + p = strstr(p + 1, subs); + assert_non_null(p); + } + memmove(p, p + strlen(subs), strlen(p + strlen(subs)) + 1); +} + +/** + * @brief test that openssh style '+' feature works + */ +static void torture_config_plus(void **state, + const char *file, const char *string) +{ + ssh_session session = *state; + const char *def_hostkeys = ssh_kex_get_default_methods(SSH_HOSTKEYS); + const char *fips_hostkeys = ssh_kex_get_fips_methods(SSH_HOSTKEYS); + const char *def_ciphers = ssh_kex_get_default_methods(SSH_CRYPT_C_S); + const char *fips_ciphers = ssh_kex_get_fips_methods(SSH_CRYPT_C_S); + const char *def_kex = ssh_kex_get_default_methods(SSH_KEX); + const char *fips_kex = ssh_kex_get_fips_methods(SSH_KEX); + const char *def_mac = ssh_kex_get_default_methods(SSH_MAC_C_S); + const char *fips_mac = ssh_kex_get_fips_methods(SSH_MAC_C_S); + const char *hostkeys_added = ",ssh-rsa"; + const char *ciphers_added = "aes128-cbc,aes256-cbc"; + const char *kex_added = ",diffie-hellman-group14-sha1,diffie-hellman-group1-sha1"; + const char *mac_added = ",hmac-sha1,hmac-sha1-etm@openssh.com"; + char *awaited = NULL; + int rc; + + _parse_config(session, file, string, SSH_OK); + + /* check hostkeys */ + if (ssh_fips_mode()) { + /* ssh-rsa is disabled in fips */ + assert_string_equal(session->opts.wanted_methods[SSH_HOSTKEYS], fips_hostkeys); + } else { + awaited = calloc(strlen(def_hostkeys) + strlen(hostkeys_added) + 1, 1); + rc = snprintf(awaited, strlen(def_hostkeys) + strlen(hostkeys_added) + 1, + "%s%s", def_hostkeys, hostkeys_added); + assert_int_equal(rc, strlen(def_hostkeys) + strlen(hostkeys_added)); + + assert_string_equal(session->opts.wanted_methods[SSH_HOSTKEYS], awaited); + free(awaited); + } + + /* check ciphers */ + if (ssh_fips_mode()) { + /* already all supported is in the list */ + assert_string_equal(session->opts.wanted_methods[SSH_CRYPT_C_S], fips_ciphers); + } else { + awaited = calloc(strlen(def_ciphers) + strlen(ciphers_added) + 1, 1); + rc = snprintf(awaited, strlen(def_ciphers) + strlen(ciphers_added) + 1, + "%s%s", def_ciphers, ciphers_added); + assert_int_equal(rc, strlen(def_ciphers) + strlen(ciphers_added)); + assert_string_equal(session->opts.wanted_methods[SSH_CRYPT_C_S], awaited); + free(awaited); + } + + /* check kex */ + if (ssh_fips_mode()) { + /* sha1 is disabled in fips */ + assert_string_equal(session->opts.wanted_methods[SSH_KEX], fips_kex); + } else { + awaited = calloc(strlen(def_kex) + strlen(kex_added) + 1, 1); + rc = snprintf(awaited, strlen(def_kex) + strlen(kex_added) + 1, + "%s%s", def_kex, kex_added); + assert_int_equal(rc, strlen(def_kex) + strlen(kex_added)); + assert_string_equal(session->opts.wanted_methods[SSH_KEX], awaited); + free(awaited); + } + + /* check mac */ + if (ssh_fips_mode()) { + /* the added algos are already in the fips_methods */ + assert_string_equal(session->opts.wanted_methods[SSH_MAC_C_S], fips_mac); + } else { + awaited = calloc(strlen(def_mac) + strlen(mac_added) + 1, 1); + rc = snprintf(awaited, strlen(def_mac) + strlen(mac_added) + 1, + "%s%s", def_mac, mac_added); + assert_int_equal(rc, strlen(def_mac) + strlen(mac_added)); + assert_string_equal(session->opts.wanted_methods[SSH_MAC_C_S], awaited); + free(awaited); + } +} + +/** + * @brief test that openssh style '+' feature works from file + */ +static void torture_config_plus_file(void **state) +{ + torture_config_plus(state, LIBSSH_TESTCONFIG14, NULL); +} + +/** + * @brief test that openssh style '+' feature works from string + */ +static void torture_config_plus_string(void **state) +{ + torture_config_plus(state, NULL, LIBSSH_TESTCONFIG_STRING14); +} + +/** + * @brief test that openssh style '-' feature works from string + */ +static void torture_config_minus(void **state, + const char *file, const char *string) +{ + ssh_session session = *state; + const char *def_hostkeys = ssh_kex_get_default_methods(SSH_HOSTKEYS); + const char *fips_hostkeys = ssh_kex_get_fips_methods(SSH_HOSTKEYS); + const char *def_ciphers = ssh_kex_get_default_methods(SSH_CRYPT_C_S); + const char *fips_ciphers = ssh_kex_get_fips_methods(SSH_CRYPT_C_S); + const char *def_kex = ssh_kex_get_default_methods(SSH_KEX); + const char *fips_kex = ssh_kex_get_fips_methods(SSH_KEX); + const char *def_mac = ssh_kex_get_default_methods(SSH_MAC_C_S); + const char *fips_mac = ssh_kex_get_fips_methods(SSH_MAC_C_S); + const char *hostkeys_removed = ",rsa-sha2-512,rsa-sha2-256"; + const char *ciphers_removed = ",aes256-ctr"; + const char *kex_removed = ",diffie-hellman-group18-sha512,diffie-hellman-group16-sha512"; + const char *fips_kex_removed = ",diffie-hellman-group16-sha512,diffie-hellman-group18-sha512"; + const char *mac_removed = "hmac-sha2-256-etm@openssh.com,"; + char *awaited = NULL; + int rc; + + _parse_config(session, file, string, SSH_OK); + + /* check hostkeys */ + if (ssh_fips_mode()) { + awaited = calloc(strlen(fips_hostkeys) + 1, 1); + rc = snprintf(awaited, strlen(fips_hostkeys) + 1, "%s", fips_hostkeys); + assert_int_equal(rc, strlen(fips_hostkeys)); + } else { + awaited = calloc(strlen(def_hostkeys) + 1, 1); + rc = snprintf(awaited, strlen(def_hostkeys) + 1, "%s", def_hostkeys); + assert_int_equal(rc, strlen(def_hostkeys)); + } + /* remove the substring from the defaults */ + helper_remove_substring(awaited, hostkeys_removed, 0); + assert_string_equal(session->opts.wanted_methods[SSH_HOSTKEYS], awaited); + free(awaited); + + /* check ciphers */ + if (ssh_fips_mode()) { + awaited = calloc(strlen(fips_ciphers) + 1, 1); + rc = snprintf(awaited, strlen(fips_ciphers) + 1, "%s", fips_ciphers); + assert_int_equal(rc, strlen(fips_ciphers)); + } else { + awaited = calloc(strlen(def_ciphers) + 1, 1); + rc = snprintf(awaited, strlen(def_ciphers) + 1, "%s", def_ciphers); + assert_int_equal(rc, strlen(def_ciphers)); + /* remove the comma at the end of the list */ + awaited[strlen(awaited) - 1] = '\0'; + } + /* remove the substring from the defaults */ + helper_remove_substring(awaited, ciphers_removed, 0); + assert_string_equal(session->opts.wanted_methods[SSH_CRYPT_C_S], awaited); + free(awaited); + + /* check kex */ + if (ssh_fips_mode()) { + awaited = calloc(strlen(fips_kex) + 1, 1); + rc = snprintf(awaited, strlen(fips_kex) + 1, "%s", fips_kex); + assert_int_equal(rc, strlen(fips_kex)); + /* remove the substring from the defaults */ + helper_remove_substring(awaited, fips_kex_removed, 0); + } else { + awaited = calloc(strlen(def_kex) + 1, 1); + rc = snprintf(awaited, strlen(def_kex) + 1, "%s", def_kex); + assert_int_equal(rc, strlen(def_kex)); + /* remove the substring from the defaults */ + helper_remove_substring(awaited, kex_removed, 0); + } + assert_string_equal(session->opts.wanted_methods[SSH_KEX], awaited); + free(awaited); + + /* check mac */ + if (ssh_fips_mode()) { + awaited = calloc(strlen(fips_mac) + 1, 1); + rc = snprintf(awaited, strlen(fips_mac) + 1, "%s", fips_mac); + assert_int_equal(rc, strlen(fips_mac)); + } else { + awaited = calloc(strlen(def_mac) + 1, 1); + rc = snprintf(awaited, strlen(def_mac) + 1, "%s", def_mac); + assert_int_equal(rc, strlen(def_mac)); + } + /* remove the substring from the defaults */ + helper_remove_substring(awaited, mac_removed, 0); + assert_string_equal(session->opts.wanted_methods[SSH_MAC_C_S], awaited); + free(awaited); +} + +/** + * @brief test that openssh style '-' feature works from file + */ +static void torture_config_minus_file(void **state) +{ + torture_config_minus(state, LIBSSH_TESTCONFIG15, NULL); +} + +/** + * @brief test that openssh style '-' feature works from string + */ +static void torture_config_minus_string(void **state) +{ + torture_config_minus(state, NULL, LIBSSH_TESTCONFIG_STRING15); +} + +/** + * @brief test that openssh style '^' feature works from string + */ +static void torture_config_caret(void **state, + const char *file, const char *string) +{ + ssh_session session = *state; + const char *def_hostkeys = ssh_kex_get_default_methods(SSH_HOSTKEYS); + const char *fips_hostkeys = ssh_kex_get_fips_methods(SSH_HOSTKEYS); + const char *def_ciphers = ssh_kex_get_default_methods(SSH_CRYPT_C_S); + const char *fips_ciphers = ssh_kex_get_fips_methods(SSH_CRYPT_C_S); + const char *def_kex = ssh_kex_get_default_methods(SSH_KEX); + const char *fips_kex = ssh_kex_get_fips_methods(SSH_KEX); + const char *def_mac = ssh_kex_get_default_methods(SSH_MAC_C_S); + const char *fips_mac = ssh_kex_get_fips_methods(SSH_MAC_C_S); + const char *hostkeys_prio = "rsa-sha2-512,rsa-sha2-256"; + const char *ciphers_prio = "aes256-cbc,"; + const char *kex_prio = "diffie-hellman-group18-sha512,diffie-hellman-group16-sha512,"; + const char *fips_kex_prio = ",diffie-hellman-group16-sha512,diffie-hellman-group18-sha512"; + const char *mac_prio = "hmac-sha1,"; + char *awaited = NULL; + int rc; + + _parse_config(session, file, string, SSH_OK); + + /* check hostkeys */ + /* +2 for the added comma and the \0 */ + if (ssh_fips_mode()) { + awaited = calloc(strlen(hostkeys_prio) + strlen(fips_hostkeys) + 2, 1); + rc = snprintf(awaited, strlen(hostkeys_prio) + strlen(fips_hostkeys) + 2, + "%s,%s", hostkeys_prio, fips_hostkeys); + assert_int_equal(rc, strlen(hostkeys_prio) + strlen(fips_hostkeys) + 1); + } else { + awaited = calloc(strlen(def_hostkeys) + strlen(hostkeys_prio) + 2, 1); + rc = snprintf(awaited, strlen(hostkeys_prio) + strlen(def_hostkeys) + 2, + "%s,%s", hostkeys_prio, def_hostkeys); + assert_int_equal(rc, strlen(hostkeys_prio) + strlen(def_hostkeys) + 1); + } + + /* remove the substring from the defaults */ + helper_remove_substring(awaited, hostkeys_prio, 1); + /* remove the comma at the end of the list */ + awaited[strlen(awaited) - 1] = '\0'; + + assert_string_equal(session->opts.wanted_methods[SSH_HOSTKEYS], awaited); + free(awaited); + + /* check ciphers */ + if (ssh_fips_mode()) { + awaited = calloc(strlen(ciphers_prio) + strlen(fips_ciphers) + 1, 1); + rc = snprintf(awaited, strlen(ciphers_prio) + strlen(fips_ciphers) + 1, + "%s%s", ciphers_prio, fips_ciphers); + assert_int_equal(rc, strlen(ciphers_prio) + strlen(fips_ciphers)); + /* remove the substring from the defaults */ + helper_remove_substring(awaited, ciphers_prio, 1); + } else { + /* + 2 because the '\0' and the comma */ + awaited = calloc(strlen(ciphers_prio) + strlen(def_ciphers) + 1, 1); + rc = snprintf(awaited, strlen(ciphers_prio) + strlen(def_ciphers) + 1, + "%s%s", ciphers_prio, def_ciphers); + assert_int_equal(rc, strlen(ciphers_prio) + strlen(def_ciphers)); + /* remove the comma at the end of the list */ + awaited[strlen(awaited) - 1] = '\0'; + } + + assert_string_equal(session->opts.wanted_methods[SSH_CRYPT_C_S], awaited); + free(awaited); + + /* check kex */ + if (ssh_fips_mode()) { + awaited = calloc(strlen(kex_prio) + strlen(fips_kex) + 1, 1); + rc = snprintf(awaited, strlen(kex_prio) + strlen(fips_kex) + 1, + "%s%s", kex_prio, fips_kex); + assert_int_equal(rc, strlen(kex_prio) + strlen(fips_kex)); + /* remove the substring from the defaults */ + /* the default list has different order of these two algos than the fips + * and because here is a braindead string substitution being done, + * change the order and remove the first occurrence of it */ + helper_remove_substring(awaited, fips_kex_prio, 0); + } else { + awaited = calloc(strlen(kex_prio) + strlen(def_kex) + 1, 1); + rc = snprintf(awaited, strlen(kex_prio) + strlen(def_kex) + 1, + "%s%s", kex_prio, def_kex); + assert_int_equal(rc, strlen(def_kex) + strlen(kex_prio)); + /* remove the substring from the defaults */ + helper_remove_substring(awaited, kex_prio, 1); + } + + assert_string_equal(session->opts.wanted_methods[SSH_KEX], awaited); + free(awaited); + + /* check mac */ + if (ssh_fips_mode()) { + awaited = calloc(strlen(mac_prio) + strlen(fips_mac) + 1, 1); + rc = snprintf(awaited, strlen(mac_prio) + strlen(fips_mac) + 1, "%s%s", mac_prio, fips_mac); + assert_int_equal(rc, strlen(mac_prio) + strlen(fips_mac)); + /* the fips list contains hmac-sha1 algo */ + helper_remove_substring(awaited, mac_prio, 1); + } else { + awaited = calloc(strlen(mac_prio) + strlen(def_mac) + 1, 1); + /* the mac is not in default; it is added to the list */ + rc = snprintf(awaited, strlen(mac_prio) + strlen(def_mac) + 1, "%s%s", mac_prio, def_mac); + assert_int_equal(rc, strlen(mac_prio) + strlen(def_mac)); + } + assert_string_equal(session->opts.wanted_methods[SSH_MAC_C_S], awaited); + free(awaited); +} + +/** + * @brief test that openssh style '^' feature works from file + */ +static void torture_config_caret_file(void **state) +{ + torture_config_caret(state, LIBSSH_TESTCONFIG16, NULL); +} + +/** + * @brief test that openssh style '^' feature works from string + */ +static void torture_config_caret_string(void **state) +{ + torture_config_caret(state, NULL, LIBSSH_TESTCONFIG_STRING16); +} + /** * @brief test PubkeyAcceptedKeyTypes helper function */ @@ -1848,6 +2229,18 @@ int torture_run_tests(void) setup, teardown), cmocka_unit_test_setup_teardown(torture_config_rekey_string, setup, teardown), + cmocka_unit_test_setup_teardown(torture_config_plus_file, + setup, teardown), + cmocka_unit_test_setup_teardown(torture_config_plus_string, + setup, teardown), + cmocka_unit_test_setup_teardown(torture_config_minus_file, + setup, teardown), + cmocka_unit_test_setup_teardown(torture_config_minus_string, + setup, teardown), + cmocka_unit_test_setup_teardown(torture_config_caret_file, + setup, teardown), + cmocka_unit_test_setup_teardown(torture_config_caret_string, + setup, teardown), cmocka_unit_test_setup_teardown(torture_config_pubkeytypes_file, setup, teardown), cmocka_unit_test_setup_teardown(torture_config_pubkeytypes_string,