From 715fadbbace7b0b2e739edca1968cc35b7b61a07 Mon Sep 17 00:00:00 2001 From: CentOS Sources Date: Tue, 3 Nov 2020 06:48:28 -0500 Subject: [PATCH] import libssh-0.9.4-2.el8 --- .gitignore | 2 +- .libssh.metadata | 2 +- ...0.9.0-do-not-ignore-known-hosts-keys.patch | 1240 ----------------- SOURCES/libssh-0.9.0-run-sshd-confined.patch | 67 - ....9.0-skip-1k-rsa-key-generation-test.patch | 59 - SOURCES/libssh-0.9.0.tar.xz.asc | 16 - ...libssh-0.9.4-add-cve-2019-14889-test.patch | 125 ++ ...9.4-do-not-parse-config-during-tests.patch | 58 + ...t-return-error-server-closed-channel.patch | 43 + ...sh-0.9.4-enable-sshd-sha1-algorithms.patch | 18 + SOURCES/libssh-0.9.4-fix-version.patch | 11 + SOURCES/libssh-0.9.4.tar.xz.asc | 16 + SOURCES/libssh_server.config | 2 - SPECS/libssh.spec | 26 +- 14 files changed, 294 insertions(+), 1391 deletions(-) delete mode 100644 SOURCES/libssh-0.9.0-do-not-ignore-known-hosts-keys.patch delete mode 100644 SOURCES/libssh-0.9.0-run-sshd-confined.patch delete mode 100644 SOURCES/libssh-0.9.0-skip-1k-rsa-key-generation-test.patch delete mode 100644 SOURCES/libssh-0.9.0.tar.xz.asc create mode 100644 SOURCES/libssh-0.9.4-add-cve-2019-14889-test.patch create mode 100644 SOURCES/libssh-0.9.4-do-not-parse-config-during-tests.patch create mode 100644 SOURCES/libssh-0.9.4-do-not-return-error-server-closed-channel.patch create mode 100644 SOURCES/libssh-0.9.4-enable-sshd-sha1-algorithms.patch create mode 100644 SOURCES/libssh-0.9.4-fix-version.patch create mode 100644 SOURCES/libssh-0.9.4.tar.xz.asc diff --git a/.gitignore b/.gitignore index 9130fdb..bfd0bb1 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,2 @@ -SOURCES/libssh-0.9.0.tar.xz +SOURCES/libssh-0.9.4.tar.xz SOURCES/libssh.keyring diff --git a/.libssh.metadata b/.libssh.metadata index 2c4bf88..c453642 100644 --- a/.libssh.metadata +++ b/.libssh.metadata @@ -1,2 +1,2 @@ -570bffef68af6c1211673bc9a8036c9265935b2b SOURCES/libssh-0.9.0.tar.xz +93289b77379263328c843fa85ba5ed4b274b689f SOURCES/libssh-0.9.4.tar.xz 3f2ab0bca02893402ba0ad172a6bd44456a65f86 SOURCES/libssh.keyring diff --git a/SOURCES/libssh-0.9.0-do-not-ignore-known-hosts-keys.patch b/SOURCES/libssh-0.9.0-do-not-ignore-known-hosts-keys.patch deleted file mode 100644 index c007677..0000000 --- a/SOURCES/libssh-0.9.0-do-not-ignore-known-hosts-keys.patch +++ /dev/null @@ -1,1240 +0,0 @@ -From b040856ccfde1a5d4c21791f46ca6ee00c21a47b Mon Sep 17 00:00:00 2001 -From: Anderson Toshiyuki Sasaki -Date: Tue, 2 Jul 2019 10:21:15 +0200 -Subject: [PATCH 1/6] knownhosts: Fix possible memory leak - -The memory allocated for host_port can leak if the global knownhosts -file is unaccessible. - -Found by address sanitizer build in CI. - -Signed-off-by: Anderson Toshiyuki Sasaki -Reviewed-by: Jakub Jelen -(cherry picked from commit fe248414fec1e654e4ee1259927d68777dd870ae) ---- - src/knownhosts.c | 4 +++- - 1 file changed, 3 insertions(+), 1 deletion(-) - -diff --git a/src/knownhosts.c b/src/knownhosts.c -index 8a4a8ba7..9383cc97 100644 ---- a/src/knownhosts.c -+++ b/src/knownhosts.c -@@ -706,13 +706,15 @@ enum ssh_known_hosts_e ssh_session_has_known_hosts_entry(ssh_session session) - rc = ssh_known_hosts_read_entries(host_port, - session->opts.global_knownhosts, - &entry_list); -- SAFE_FREE(host_port); - if (rc != 0) { -+ SAFE_FREE(host_port); - ssh_list_free(entry_list); - return SSH_KNOWN_HOSTS_ERROR; - } - } - -+ SAFE_FREE(host_port); -+ - if (ssh_list_count(entry_list) == 0) { - ssh_list_free(entry_list); - return SSH_KNOWN_HOSTS_UNKNOWN; --- -2.21.0 - - -From 7ff0af75436ee6e549907dd563e968b92f7f8db2 Mon Sep 17 00:00:00 2001 -From: Anderson Toshiyuki Sasaki -Date: Fri, 28 Jun 2019 13:19:51 +0200 -Subject: [PATCH 2/6] tests: Check if known_hosts works with single - unaccessible file - -Make sure known hosts check works when local known_hosts file is -unaccessible, but the host is present in global known_hosts file. - -Remove double return value check in previous existing test. - -Signed-off-by: Anderson Toshiyuki Sasaki -Reviewed-by: Jakub Jelen -(cherry picked from commit ad68de7271e6ccda261df4d9fc827321e7d90fd0) ---- - tests/unittests/torture_knownhosts_parsing.c | 19 +++++++++++-------- - 1 file changed, 11 insertions(+), 8 deletions(-) - -diff --git a/tests/unittests/torture_knownhosts_parsing.c b/tests/unittests/torture_knownhosts_parsing.c -index ac8d7f31..a087caef 100644 ---- a/tests/unittests/torture_knownhosts_parsing.c -+++ b/tests/unittests/torture_knownhosts_parsing.c -@@ -384,22 +384,19 @@ static void torture_knownhosts_host_exists(void **state) - - /* This makes sure the system's known_hosts are not used */ - ssh_options_set(session, SSH_OPTIONS_GLOBAL_KNOWNHOSTS, "/dev/null"); -- - found = ssh_session_has_known_hosts_entry(session); - assert_int_equal(found, SSH_KNOWN_HOSTS_OK); -- assert_true(found == SSH_KNOWN_HOSTS_OK); - - /* This makes sure the check will not fail when the system's known_hosts is - * not accessible*/ - ssh_options_set(session, SSH_OPTIONS_GLOBAL_KNOWNHOSTS, "./unaccessible"); -- - found = ssh_session_has_known_hosts_entry(session); - assert_int_equal(found, SSH_KNOWN_HOSTS_OK); -- assert_true(found == SSH_KNOWN_HOSTS_OK); - -+ /* This makes sure the check will fail for an unknown host */ - ssh_options_set(session, SSH_OPTIONS_HOST, "wurstbrot"); - found = ssh_session_has_known_hosts_entry(session); -- assert_true(found == SSH_KNOWN_HOSTS_UNKNOWN); -+ assert_int_equal(found, SSH_KNOWN_HOSTS_UNKNOWN); - - ssh_free(session); - } -@@ -414,17 +411,23 @@ static void torture_knownhosts_host_exists_global(void **state) - assert_non_null(session); - - ssh_options_set(session, SSH_OPTIONS_HOST, "localhost"); -+ ssh_options_set(session, SSH_OPTIONS_GLOBAL_KNOWNHOSTS, knownhosts_file); -+ - /* This makes sure the user's known_hosts are not used */ - ssh_options_set(session, SSH_OPTIONS_KNOWNHOSTS, "/dev/null"); -- ssh_options_set(session, SSH_OPTIONS_GLOBAL_KNOWNHOSTS, knownhosts_file); -+ found = ssh_session_has_known_hosts_entry(session); -+ assert_int_equal(found, SSH_KNOWN_HOSTS_OK); - -+ /* This makes sure the check will not fail when the user's known_hosts is -+ * not accessible*/ -+ ssh_options_set(session, SSH_OPTIONS_KNOWNHOSTS, "./unaccessible"); - found = ssh_session_has_known_hosts_entry(session); - assert_int_equal(found, SSH_KNOWN_HOSTS_OK); -- assert_true(found == SSH_KNOWN_HOSTS_OK); - -+ /* This makes sure the check will fail for an unknown host */ - ssh_options_set(session, SSH_OPTIONS_HOST, "wurstbrot"); - found = ssh_session_has_known_hosts_entry(session); -- assert_true(found == SSH_KNOWN_HOSTS_UNKNOWN); -+ assert_int_equal(found, SSH_KNOWN_HOSTS_UNKNOWN); - - ssh_free(session); - } --- -2.21.0 - - -From b9530cedbeb169762307096dfeb485ab94e09740 Mon Sep 17 00:00:00 2001 -From: Anderson Toshiyuki Sasaki -Date: Fri, 28 Jun 2019 13:27:34 +0200 -Subject: [PATCH 3/6] knownhosts: Read knownhosts file only if found - -Avoid trying to open the files if they are not accessible. This was -already treated as a non-error, but with this we save one function call. - -Signed-off-by: Anderson Toshiyuki Sasaki -Reviewed-by: Jakub Jelen -(cherry picked from commit e5a64a3d6b1b601cbaf207468a6658d1a4fa0031) ---- - src/knownhosts.c | 4 ++-- - 1 file changed, 2 insertions(+), 2 deletions(-) - -diff --git a/src/knownhosts.c b/src/knownhosts.c -index 9383cc97..8040a0c0 100644 ---- a/src/knownhosts.c -+++ b/src/knownhosts.c -@@ -691,7 +691,7 @@ enum ssh_known_hosts_e ssh_session_has_known_hosts_entry(ssh_session session) - return SSH_KNOWN_HOSTS_ERROR; - } - -- if (session->opts.knownhosts != NULL) { -+ if (known_hosts_found) { - rc = ssh_known_hosts_read_entries(host_port, - session->opts.knownhosts, - &entry_list); -@@ -702,7 +702,7 @@ enum ssh_known_hosts_e ssh_session_has_known_hosts_entry(ssh_session session) - } - } - -- if (session->opts.global_knownhosts != NULL) { -+ if (global_known_hosts_found) { - rc = ssh_known_hosts_read_entries(host_port, - session->opts.global_knownhosts, - &entry_list); --- -2.21.0 - - -From aaa978ad06ebdeff88a39b9a894696254263162c Mon Sep 17 00:00:00 2001 -From: Anderson Toshiyuki Sasaki -Date: Fri, 28 Jun 2019 22:35:38 +0200 -Subject: [PATCH 4/6] token: Added function to remove duplicates - -Added a function to remove duplicates from lists. This function is used -in a new provided function to append lists removing duplicates. - -Signed-off-by: Anderson Toshiyuki Sasaki -Reviewed-by: Jakub Jelen -(cherry picked from commit 548753b3389518ebce98a7ddbf0640db3ad72de8) ---- - include/libssh/token.h | 6 +- - src/token.c | 152 ++++++++++++++++++++++++++++++- - tests/unittests/torture_tokens.c | 122 +++++++++++++++++++++++++ - 3 files changed, 278 insertions(+), 2 deletions(-) - -diff --git a/include/libssh/token.h b/include/libssh/token.h -index 7b244189..9896fb06 100644 ---- a/include/libssh/token.h -+++ b/include/libssh/token.h -@@ -38,7 +38,11 @@ void ssh_tokens_free(struct ssh_tokens_st *tokens); - char *ssh_find_matching(const char *available_d, - const char *preferred_d); - -- - char *ssh_find_all_matching(const char *available_d, - const char *preferred_d); -+ -+char *ssh_remove_duplicates(const char *list); -+ -+char *ssh_append_without_duplicates(const char *list, -+ const char *appended_list); - #endif /* TOKEN_H_ */ -diff --git a/src/token.c b/src/token.c -index aee235ac..0924d3bd 100644 ---- a/src/token.c -+++ b/src/token.c -@@ -26,6 +26,7 @@ - - #include - #include -+#include - - #include "libssh/priv.h" - #include "libssh/token.h" -@@ -175,7 +176,7 @@ char *ssh_find_matching(const char *available_list, - - for (i = 0; p_tok->tokens[i]; i++) { - for (j = 0; a_tok->tokens[j]; j++) { -- if (strcmp(a_tok->tokens[j], p_tok->tokens[i]) == 0){ -+ if (strcmp(a_tok->tokens[j], p_tok->tokens[i]) == 0) { - ret = strdup(a_tok->tokens[j]); - goto out; - } -@@ -260,3 +261,152 @@ out: - ssh_tokens_free(p_tok); - return ret; - } -+ -+/** -+ * @internal -+ * -+ * @brief Given a string containing a list of elements, remove all duplicates -+ * and return in a newly allocated string. -+ * -+ * @param[in] list The list to be freed of duplicates -+ * -+ * @return A newly allocated copy of the string free of duplicates; NULL in -+ * case of error. -+ */ -+char *ssh_remove_duplicates(const char *list) -+{ -+ struct ssh_tokens_st *tok = NULL; -+ -+ size_t i, j, num_tokens, max_len; -+ char *ret = NULL; -+ bool *should_copy = NULL, need_comma = false; -+ -+ if (list == NULL) { -+ return NULL; -+ } -+ -+ /* The maximum number of tokens is the size of the list */ -+ max_len = strlen(list); -+ if (max_len == 0) { -+ return NULL; -+ } -+ -+ /* Add space for ending '\0' */ -+ max_len++; -+ -+ tok = ssh_tokenize(list, ','); -+ if ((tok == NULL) || (tok->tokens == NULL) || (tok->tokens[0] == NULL)) { -+ goto out; -+ } -+ -+ should_copy = calloc(1, max_len); -+ if (should_copy == NULL) { -+ goto out; -+ } -+ -+ if (strlen(tok->tokens[0]) > 0) { -+ should_copy[0] = true; -+ } -+ -+ for (i = 1; tok->tokens[i]; i++) { -+ for (j = 0; j < i; j++) { -+ if (strcmp(tok->tokens[i], tok->tokens[j]) == 0) { -+ /* Found a duplicate; do not copy */ -+ should_copy[i] = false; -+ break; -+ } -+ } -+ -+ /* No matching token before */ -+ if (j == i) { -+ /* Only copy if it is not an empty string */ -+ if (strlen(tok->tokens[i]) > 0) { -+ should_copy[i] = true; -+ } else { -+ should_copy[i] = false; -+ } -+ } -+ } -+ -+ num_tokens = i; -+ -+ ret = calloc(1, max_len); -+ if (ret == NULL) { -+ goto out; -+ } -+ -+ for (i = 0; i < num_tokens; i++) { -+ if (should_copy[i]) { -+ if (need_comma) { -+ strncat(ret, ",", (max_len - strlen(ret) - 1)); -+ } -+ strncat(ret, tok->tokens[i], (max_len - strlen(ret) - 1)); -+ need_comma = true; -+ } -+ } -+ -+ /* If no comma is needed, nothing was copied */ -+ if (!need_comma) { -+ SAFE_FREE(ret); -+ } -+ -+out: -+ SAFE_FREE(should_copy); -+ ssh_tokens_free(tok); -+ return ret; -+} -+ -+/** -+ * @internal -+ * -+ * @brief Given two strings containing lists of tokens, return a newly -+ * allocated string containing all the elements of the first list appended with -+ * all the elements of the second list, without duplicates. The order of the -+ * elements will be preserved. -+ * -+ * @param[in] list The first list -+ * @param[in] appended_list The list to be appended -+ * -+ * @return A newly allocated copy list containing all the elements of the -+ * kept_list appended with the elements of the appended_list without duplicates; -+ * NULL in case of error. -+ */ -+char *ssh_append_without_duplicates(const char *list, -+ const char *appended_list) -+{ -+ size_t concat_len = 0; -+ char *ret = NULL, *concat = NULL; -+ -+ if (list != NULL) { -+ concat_len = strlen(list); -+ } -+ -+ if (appended_list != NULL) { -+ concat_len += strlen(appended_list); -+ } -+ -+ if (concat_len == 0) { -+ return NULL; -+ } -+ -+ /* Add room for ending '\0' and for middle ',' */ -+ concat_len += 2; -+ concat = calloc(1, concat_len); -+ if (concat == NULL) { -+ return NULL; -+ } -+ -+ if (list != NULL) { -+ strcpy(concat, list); -+ strncat(concat, ",", concat_len - strlen(concat) - 1); -+ } -+ if (appended_list != NULL) { -+ strncat(concat, appended_list, concat_len - strlen(concat) - 1); -+ } -+ -+ ret = ssh_remove_duplicates(concat); -+ -+ SAFE_FREE(concat); -+ -+ return ret; -+} -diff --git a/tests/unittests/torture_tokens.c b/tests/unittests/torture_tokens.c -index e192842f..6b52b847 100644 ---- a/tests/unittests/torture_tokens.c -+++ b/tests/unittests/torture_tokens.c -@@ -146,6 +146,126 @@ static void torture_tokens_sanity(UNUSED_PARAM(void **state)) - tokenize_compare_expected(",", single_colon, 1); - } - -+static void torture_remove_duplicate(UNUSED_PARAM(void **state)) -+{ -+ -+ const char *simple[] = {"a,a,b,b,c,c", -+ "a,b,c,a,b,c", -+ "a,b,c,c,b,a", -+ "a,a,,b,b,,c,c", -+ ",a,a,b,b,c,c", -+ "a,a,b,b,c,c,"}; -+ const char *empty[] = {"", -+ ",,,,,,,,,", -+ NULL}; -+ char *ret = NULL; -+ int i; -+ -+ for (i = 0; i < 6; i++) { -+ ret = ssh_remove_duplicates(simple[i]); -+ assert_non_null(ret); -+ assert_string_equal("a,b,c", ret); -+ printf("simple[%d] resulted in '%s'\n", i, ret); -+ SAFE_FREE(ret); -+ } -+ -+ for (i = 0; i < 3; i++) { -+ ret = ssh_remove_duplicates(empty[i]); -+ if (ret != NULL) { -+ printf("empty[%d] resulted in '%s'\n", i, ret); -+ } -+ assert_null(ret); -+ } -+ -+ ret = ssh_remove_duplicates("a"); -+ assert_non_null(ret); -+ assert_string_equal("a", ret); -+ SAFE_FREE(ret); -+} -+ -+static void torture_append_without_duplicate(UNUSED_PARAM(void **state)) -+{ -+ const char *s1[] = {"a,a,b,b,c,c", -+ "a,b,c,a,b,c", -+ "a,b,c,c,b,a", -+ "a,a,,b,b,,c,c", -+ ",a,a,b,b,c,c", -+ "a,a,b,b,c,c,"}; -+ const char *s2[] = {"a,a,b,b,c,c,d,d", -+ "a,b,c,d,a,b,c,d", -+ "a,b,c,d,d,c,b,a", -+ "a,a,,b,b,,c,c,,d,d", -+ ",a,a,b,b,c,c,d,d", -+ "a,a,b,b,c,c,d,d,", -+ "d"}; -+ const char *empty[] = {"", -+ ",,,,,,,,,", -+ NULL, -+ NULL}; -+ char *ret = NULL; -+ int i, j; -+ -+ ret = ssh_append_without_duplicates("a", "a"); -+ assert_non_null(ret); -+ assert_string_equal("a", ret); -+ SAFE_FREE(ret); -+ -+ ret = ssh_append_without_duplicates("a", "b"); -+ assert_non_null(ret); -+ assert_string_equal("a,b", ret); -+ SAFE_FREE(ret); -+ -+ ret = ssh_append_without_duplicates("a", NULL); -+ assert_non_null(ret); -+ assert_string_equal("a", ret); -+ SAFE_FREE(ret); -+ -+ ret = ssh_append_without_duplicates(NULL, "b"); -+ assert_non_null(ret); -+ assert_string_equal("b", ret); -+ SAFE_FREE(ret); -+ -+ for (i = 0; i < 6; i++) { -+ for (j = 0; j < 7; j++) { -+ ret = ssh_append_without_duplicates(s1[i], s2[j]); -+ assert_non_null(ret); -+ printf("s1[%d] + s2[%d] resulted in '%s'\n", i, j, ret); -+ assert_string_equal("a,b,c,d", ret); -+ SAFE_FREE(ret); -+ } -+ } -+ -+ for (i = 0; i < 6; i++) { -+ for (j = 0; j < 3; j++) { -+ ret = ssh_append_without_duplicates(s1[i], empty[j]); -+ assert_non_null(ret); -+ printf("s1[%d] + empty[%d] resulted in '%s'\n", i, j, ret); -+ assert_string_equal("a,b,c", ret); -+ SAFE_FREE(ret); -+ } -+ } -+ -+ for (i = 0; i < 3; i++) { -+ for (j = 0; j < 6; j++) { -+ ret = ssh_append_without_duplicates(empty[i], s1[j]); -+ assert_non_null(ret); -+ printf("empty[%d] + s1[%d] resulted in '%s'\n", i, j, ret); -+ assert_string_equal("a,b,c", ret); -+ SAFE_FREE(ret); -+ } -+ } -+ for (i = 0; i < 4; i++) { -+ for (j = 0; j < 4; j++) { -+ ret = ssh_append_without_duplicates(empty[i], empty[j]); -+ if (ret != NULL) { -+ printf("empty[%d] + empty[%d] resulted in '%s'\n", i, j, ret); -+ } -+ assert_null(ret); -+ } -+ } -+} -+ -+ - int torture_run_tests(void) - { - int rc; -@@ -153,6 +273,8 @@ int torture_run_tests(void) - cmocka_unit_test(torture_tokens_sanity), - cmocka_unit_test(torture_find_matching), - cmocka_unit_test(torture_find_all_matching), -+ cmocka_unit_test(torture_remove_duplicate), -+ cmocka_unit_test(torture_append_without_duplicate), - }; - - ssh_init(); --- -2.21.0 - - -From fa3caa61fdb39269cd78c4919df515b71991d231 Mon Sep 17 00:00:00 2001 -From: Anderson Toshiyuki Sasaki -Date: Tue, 2 Jul 2019 13:48:17 +0200 -Subject: [PATCH 5/6] knownhosts: Introduced - ssh_known_hosts_get_algorithms_names() - -The added internal function obtain a newly allocated string containing a -list of the signature types that can be generated by the keys present in -the known_hosts files, separated by commas. - -Signed-off-by: Anderson Toshiyuki Sasaki -Reviewed-by: Jakub Jelen -(cherry picked from commit 65a38759ca872e8bec0158ab3676e74b6afd336f) ---- - include/libssh/knownhosts.h | 1 + - src/knownhosts.c | 141 +++++++++++++++++++ - tests/unittests/torture_knownhosts_parsing.c | 28 ++++ - 3 files changed, 170 insertions(+) - -diff --git a/include/libssh/knownhosts.h b/include/libssh/knownhosts.h -index dcaa6c24..44e434c0 100644 ---- a/include/libssh/knownhosts.h -+++ b/include/libssh/knownhosts.h -@@ -23,6 +23,7 @@ - #define SSH_KNOWNHOSTS_H_ - - struct ssh_list *ssh_known_hosts_get_algorithms(ssh_session session); -+char *ssh_known_hosts_get_algorithms_names(ssh_session session); - enum ssh_known_hosts_e - ssh_session_get_known_hosts_entry_file(ssh_session session, - const char *filename, -diff --git a/src/knownhosts.c b/src/knownhosts.c -index 8040a0c0..cf9d8a6b 100644 ---- a/src/knownhosts.c -+++ b/src/knownhosts.c -@@ -42,6 +42,7 @@ - #include "libssh/pki.h" - #include "libssh/dh.h" - #include "libssh/knownhosts.h" -+#include "libssh/token.h" - - /** - * @addtogroup libssh_session -@@ -451,6 +452,146 @@ error: - return NULL; - } - -+/** -+ * @internal -+ * -+ * @brief Returns a static string containing a list of the signature types the -+ * given key type can generate. -+ * -+ * @returns A static cstring containing the signature types the key is able to -+ * generate separated by commas; NULL in case of error -+ */ -+static const char *ssh_known_host_sigs_from_hostkey_type(enum ssh_keytypes_e type) -+{ -+ switch (type) { -+ case SSH_KEYTYPE_RSA: -+ return "rsa-sha2-512,rsa-sha2-256,ssh-rsa"; -+ case SSH_KEYTYPE_ED25519: -+ return "ssh-ed25519"; -+#ifdef HAVE_DSA -+ case SSH_KEYTYPE_DSS: -+ return "ssh-dss"; -+#endif -+#ifdef HAVE_ECDH -+ case SSH_KEYTYPE_ECDSA_P256: -+ return "ecdsa-sha2-nistp256"; -+ case SSH_KEYTYPE_ECDSA_P384: -+ return "ecdsa-sha2-nistp384"; -+ case SSH_KEYTYPE_ECDSA_P521: -+ return "ecdsa-sha2-nistp521"; -+#endif -+ case SSH_KEYTYPE_UNKNOWN: -+ default: -+ SSH_LOG(SSH_LOG_WARN, "The given type %d is not a base private key type " -+ "or is unsupported", type); -+ return NULL; -+ } -+} -+ -+/** -+ * @internal -+ * @brief Get the host keys algorithms identifiers from the known_hosts files -+ * -+ * This expands the signatures types that can be generated from the keys types -+ * present in the known_hosts files -+ * -+ * @param[in] session The ssh session to use. -+ * -+ * @return A newly allocated cstring containing a list of signature algorithms -+ * that can be generated by the host using the keys listed in the known_hosts -+ * files, NULL on error. -+ */ -+char *ssh_known_hosts_get_algorithms_names(ssh_session session) -+{ -+ char methods_buffer[256 + 1] = {0}; -+ struct ssh_list *entry_list = NULL; -+ struct ssh_iterator *it = NULL; -+ char *host_port = NULL; -+ size_t count; -+ bool needcomma = false; -+ char *names; -+ -+ int rc; -+ -+ if (session->opts.knownhosts == NULL || -+ session->opts.global_knownhosts == NULL) { -+ if (ssh_options_apply(session) < 0) { -+ ssh_set_error(session, -+ SSH_REQUEST_DENIED, -+ "Can't find a known_hosts file"); -+ -+ return NULL; -+ } -+ } -+ -+ host_port = ssh_session_get_host_port(session); -+ if (host_port == NULL) { -+ return NULL; -+ } -+ -+ rc = ssh_known_hosts_read_entries(host_port, -+ session->opts.knownhosts, -+ &entry_list); -+ if (rc != 0) { -+ SAFE_FREE(host_port); -+ ssh_list_free(entry_list); -+ return NULL; -+ } -+ -+ rc = ssh_known_hosts_read_entries(host_port, -+ session->opts.global_knownhosts, -+ &entry_list); -+ SAFE_FREE(host_port); -+ if (rc != 0) { -+ ssh_list_free(entry_list); -+ return NULL; -+ } -+ -+ if (entry_list == NULL) { -+ return NULL; -+ } -+ -+ count = ssh_list_count(entry_list); -+ if (count == 0) { -+ ssh_list_free(entry_list); -+ return NULL; -+ } -+ -+ for (it = ssh_list_get_iterator(entry_list); -+ it != NULL; -+ it = ssh_list_get_iterator(entry_list)) -+ { -+ struct ssh_knownhosts_entry *entry = NULL; -+ const char *algo = NULL; -+ -+ entry = ssh_iterator_value(struct ssh_knownhosts_entry *, it); -+ algo = ssh_known_host_sigs_from_hostkey_type(entry->publickey->type); -+ if (algo == NULL) { -+ continue; -+ } -+ -+ if (needcomma) { -+ strncat(methods_buffer, -+ ",", -+ sizeof(methods_buffer) - strlen(methods_buffer) - 1); -+ } -+ -+ strncat(methods_buffer, -+ algo, -+ sizeof(methods_buffer) - strlen(methods_buffer) - 1); -+ needcomma = true; -+ -+ ssh_knownhosts_entry_free(entry); -+ ssh_list_remove(entry_list, it); -+ } -+ -+ ssh_list_free(entry_list); -+ -+ names = ssh_remove_duplicates(methods_buffer); -+ -+ return names; -+} -+ - /** - * @brief Parse a line from a known_hosts entry into a structure - * -diff --git a/tests/unittests/torture_knownhosts_parsing.c b/tests/unittests/torture_knownhosts_parsing.c -index a087caef..6952e858 100644 ---- a/tests/unittests/torture_knownhosts_parsing.c -+++ b/tests/unittests/torture_knownhosts_parsing.c -@@ -369,6 +369,31 @@ static void torture_knownhosts_read_file(void **state) - ssh_list_free(entry_list); - } - -+static void torture_knownhosts_get_algorithms_names(void **state) -+{ -+ const char *knownhosts_file = *state; -+ ssh_session session; -+ const char *expect = "ssh-ed25519,rsa-sha2-512,rsa-sha2-256,ssh-rsa"; -+ char *names = NULL; -+ bool process_config = false; -+ -+ session = ssh_new(); -+ assert_non_null(session); -+ -+ /* This makes sure the global configuration file is not processed */ -+ ssh_options_set(session, SSH_OPTIONS_PROCESS_CONFIG, &process_config); -+ -+ ssh_options_set(session, SSH_OPTIONS_HOST, "localhost"); -+ ssh_options_set(session, SSH_OPTIONS_KNOWNHOSTS, knownhosts_file); -+ -+ names = ssh_known_hosts_get_algorithms_names(session); -+ assert_non_null(names); -+ assert_string_equal(names, expect); -+ -+ SAFE_FREE(names); -+ ssh_free(session); -+} -+ - #ifndef _WIN32 /* There is no /dev/null on Windows */ - static void torture_knownhosts_host_exists(void **state) - { -@@ -510,6 +535,9 @@ int torture_run_tests(void) { - cmocka_unit_test_setup_teardown(torture_knownhosts_read_file, - setup_knownhosts_file_duplicate, - teardown_knownhosts_file), -+ cmocka_unit_test_setup_teardown(torture_knownhosts_get_algorithms_names, -+ setup_knownhosts_file, -+ teardown_knownhosts_file), - #ifndef _WIN32 - cmocka_unit_test_setup_teardown(torture_knownhosts_host_exists, - setup_knownhosts_file, --- -2.21.0 - - -From 1fd68ec732214a12ba3f59ca23f80463411e22dd Mon Sep 17 00:00:00 2001 -From: Anderson Toshiyuki Sasaki -Date: Mon, 1 Jul 2019 19:39:07 +0200 -Subject: [PATCH 6/6] kex: Do not ignore keys in known_hosts files - -Previously, if the SSH_OPTIONS_HOSTKEYS option was set by any mean, -including the client configuration file, the keys in known_hosts files -wouldn't be considered before advertising the list of wanted host keys. - -This could result in the client requesting the server to provide a -signature using a key not present in the known_hosts files (e.g. when -the first wanted algorithm in SSH_OPTIONS_HOSTKEYS is not present in the -known_hosts files), causing a host key mismatch and possible key -rejection. - -Now, the keys present in the known_hosts files are prioritized over the -other wanted keys. This do not change the fact that only keys of types -present in the list set in SSH_OPTIONS_HOSTKEYS will be accepted and -prioritized following the order defined by such list. - -The new wanted list of hostkeys is given by: - - The keys present in known_hosts files, ordered by preference defined - in SSH_OPTIONS_HOSTKEYS. If the option is not set, a default order - of preference is used. - - The other keys present in the same option are appended without adding - duplicates. If the option is not set, the default list of keys is - used. - -Fixes: T156 - -Signed-off-by: Anderson Toshiyuki Sasaki -Reviewed-by: Jakub Jelen -(cherry picked from commit f18a7cc17e399ae7bc92f707da3a676c52fd948e) ---- - src/kex.c | 165 +++++++++---------- - tests/unittests/torture_knownhosts_parsing.c | 148 ++++++++++++++++- - 2 files changed, 223 insertions(+), 90 deletions(-) - -diff --git a/src/kex.c b/src/kex.c -index 6ea5e8ba..0d4cad6d 100644 ---- a/src/kex.c -+++ b/src/kex.c -@@ -561,103 +561,94 @@ void ssh_list_kex(struct ssh_kex_struct *kex) { - - /** - * @internal -+ * - * @brief selects the hostkey mechanisms to be chosen for the key exchange, -- * as some hostkey mechanisms may be present in known_hosts file and preferred -+ * as some hostkey mechanisms may be present in known_hosts files. -+ * - * @returns a cstring containing a comma-separated list of hostkey methods. - * NULL if no method matches - */ - char *ssh_client_select_hostkeys(ssh_session session) - { -- char methods_buffer[128]={0}; -- char tail_buffer[128]={0}; -+ const char *wanted = NULL; -+ char *wanted_without_certs = NULL; -+ char *known_hosts_algorithms = NULL; -+ char *known_hosts_ordered = NULL; - char *new_hostkeys = NULL; -- static const char *preferred_hostkeys[] = { -- "ssh-ed25519", -- "ecdsa-sha2-nistp521", -- "ecdsa-sha2-nistp384", -- "ecdsa-sha2-nistp256", -- "rsa-sha2-512", -- "rsa-sha2-256", -- "ssh-rsa", --#ifdef HAVE_DSA -- "ssh-dss", --#endif -- NULL -- }; -- struct ssh_list *algo_list = NULL; -- struct ssh_iterator *it = NULL; -- size_t algo_count; -- int needcomma = 0; -- size_t i, len; -- -- algo_list = ssh_known_hosts_get_algorithms(session); -- if (algo_list == NULL) { -- return NULL; -+ char *fips_hostkeys = NULL; -+ -+ wanted = session->opts.wanted_methods[SSH_HOSTKEYS]; -+ if (wanted == NULL) { -+ if (ssh_fips_mode()) { -+ wanted = ssh_kex_get_fips_methods(SSH_HOSTKEYS); -+ } else { -+ wanted = ssh_kex_get_default_methods(SSH_HOSTKEYS); -+ } - } - -- algo_count = ssh_list_count(algo_list); -- if (algo_count == 0) { -- ssh_list_free(algo_list); -+ /* This removes the certificate types, unsupported for now */ -+ wanted_without_certs = ssh_find_all_matching(HOSTKEYS, wanted); -+ if (wanted_without_certs == NULL) { -+ SSH_LOG(SSH_LOG_WARNING, -+ "List of allowed host key algorithms is empty or contains only " -+ "unsupported algorithms"); - return NULL; - } - -- for (i = 0; preferred_hostkeys[i] != NULL; ++i) { -- bool found = false; -- /* This is a signature type: We list also the SHA2 extensions */ -- enum ssh_keytypes_e base_preferred = -- ssh_key_type_from_signature_name(preferred_hostkeys[i]); -- -- for (it = ssh_list_get_iterator(algo_list); -- it != NULL; -- it = it->next) { -- const char *algo = ssh_iterator_value(const char *, it); -- /* This is always key type so we do not have to care for the -- * SHA2 extension */ -- enum ssh_keytypes_e base_algo = ssh_key_type_from_name(algo); -- -- if (base_preferred == base_algo) { -- /* Matching the keys already verified it is a known type */ -- if (needcomma) { -- strncat(methods_buffer, -- ",", -- sizeof(methods_buffer) - strlen(methods_buffer) - 1); -- } -- strncat(methods_buffer, -- preferred_hostkeys[i], -- sizeof(methods_buffer) - strlen(methods_buffer) - 1); -- needcomma = 1; -- found = true; -- } -- } -- /* Collect the rest of the algorithms in other buffer, that will -- * follow the preferred buffer. This will signalize all the algorithms -- * we are willing to accept. -- */ -- if (!found) { -- snprintf(tail_buffer + strlen(tail_buffer), -- sizeof(tail_buffer) - strlen(tail_buffer), -- ",%s", preferred_hostkeys[i]); -- } -+ SSH_LOG(SSH_LOG_DEBUG, -+ "Order of wanted host keys: \"%s\"", -+ wanted_without_certs); -+ -+ known_hosts_algorithms = ssh_known_hosts_get_algorithms_names(session); -+ if (known_hosts_algorithms == NULL) { -+ SSH_LOG(SSH_LOG_DEBUG, -+ "No key found in known_hosts; " -+ "changing host key method to \"%s\"", -+ wanted_without_certs); -+ -+ return wanted_without_certs; - } -- ssh_list_free(algo_list); - -- if (strlen(methods_buffer) == 0) { -+ SSH_LOG(SSH_LOG_DEBUG, -+ "Algorithms found in known_hosts files: \"%s\"", -+ known_hosts_algorithms); -+ -+ /* Filter and order the keys from known_hosts according to wanted list */ -+ known_hosts_ordered = ssh_find_all_matching(known_hosts_algorithms, -+ wanted_without_certs); -+ SAFE_FREE(known_hosts_algorithms); -+ if (known_hosts_ordered == NULL) { - SSH_LOG(SSH_LOG_DEBUG, -- "No supported kex method for existing key in known_hosts file"); -- return NULL; -+ "No key found in known_hosts is allowed; " -+ "changing host key method to \"%s\"", -+ wanted_without_certs); -+ -+ return wanted_without_certs; - } - -- /* Append the supported list to the preferred. -- * The length is maximum 128 + 128 + 1, which will not overflow -- */ -- len = strlen(methods_buffer) + strlen(tail_buffer) + 1; -- new_hostkeys = malloc(len); -+ /* Append the other supported keys after the preferred ones -+ * This function tolerates NULL pointers in parameters */ -+ new_hostkeys = ssh_append_without_duplicates(known_hosts_ordered, -+ wanted_without_certs); -+ SAFE_FREE(known_hosts_ordered); -+ SAFE_FREE(wanted_without_certs); - if (new_hostkeys == NULL) { - ssh_set_error_oom(session); - return NULL; - } -- snprintf(new_hostkeys, len, -- "%s%s", methods_buffer, tail_buffer); -+ -+ if (ssh_fips_mode()) { -+ /* Filter out algorithms not allowed in FIPS mode */ -+ fips_hostkeys = ssh_keep_fips_algos(SSH_HOSTKEYS, new_hostkeys); -+ SAFE_FREE(new_hostkeys); -+ if (fips_hostkeys == NULL) { -+ SSH_LOG(SSH_LOG_WARNING, -+ "None of the wanted host keys or keys in known_hosts files " -+ "is allowed in FIPS mode."); -+ return NULL; -+ } -+ new_hostkeys = fips_hostkeys; -+ } - - SSH_LOG(SSH_LOG_DEBUG, - "Changing host key method to \"%s\"", -@@ -672,7 +663,7 @@ char *ssh_client_select_hostkeys(ssh_session session) - */ - int ssh_set_client_kex(ssh_session session) - { -- struct ssh_kex_struct *client= &session->next_crypto->client_kex; -+ struct ssh_kex_struct *client = &session->next_crypto->client_kex; - const char *wanted; - char *kex = NULL; - char *kex_tmp = NULL; -@@ -687,14 +678,22 @@ int ssh_set_client_kex(ssh_session session) - } - - memset(client->methods, 0, KEX_METHODS_SIZE * sizeof(char **)); -- /* first check if we have specific host key methods */ -- if (session->opts.wanted_methods[SSH_HOSTKEYS] == NULL) { -- /* Only if no override */ -- session->opts.wanted_methods[SSH_HOSTKEYS] = -- ssh_client_select_hostkeys(session); -- } - -+ /* Set the list of allowed algorithms in order of preference, if it hadn't -+ * been set yet. */ - for (i = 0; i < KEX_METHODS_SIZE; i++) { -+ if (i == SSH_HOSTKEYS) { -+ /* Set the hostkeys in the following order: -+ * - First: keys present in known_hosts files ordered by preference -+ * - Next: other wanted algorithms ordered by preference */ -+ client->methods[i] = ssh_client_select_hostkeys(session); -+ if (client->methods[i] == NULL) { -+ ssh_set_error_oom(session); -+ return SSH_ERROR; -+ } -+ continue; -+ } -+ - wanted = session->opts.wanted_methods[i]; - if (wanted == NULL) { - if (ssh_fips_mode()) { -diff --git a/tests/unittests/torture_knownhosts_parsing.c b/tests/unittests/torture_knownhosts_parsing.c -index 6952e858..1c2ccc10 100644 ---- a/tests/unittests/torture_knownhosts_parsing.c -+++ b/tests/unittests/torture_knownhosts_parsing.c -@@ -28,6 +28,8 @@ - - #define TMP_FILE_NAME "/tmp/known_hosts_XXXXXX" - -+const char template[] = "temp_dir_XXXXXX"; -+ - static int setup_knownhosts_file(void **state) - { - char *tmp_file = NULL; -@@ -394,6 +396,115 @@ static void torture_knownhosts_get_algorithms_names(void **state) - ssh_free(session); - } - -+static void torture_knownhosts_algorithms_wanted(void **state) -+{ -+ const char *knownhosts_file = *state; -+ char *algo_list = NULL; -+ ssh_session session; -+ bool process_config = false; -+ const char *wanted = "ecdsa-sha2-nistp384,ecdsa-sha2-nistp256," -+ "rsa-sha2-256,ecdsa-sha2-nistp521"; -+ const char *expect = "rsa-sha2-256,ecdsa-sha2-nistp384," -+ "ecdsa-sha2-nistp256,ecdsa-sha2-nistp521"; -+ int verbose = 4; -+ -+ session = ssh_new(); -+ assert_non_null(session); -+ -+ ssh_options_set(session, SSH_OPTIONS_LOG_VERBOSITY, &verbose); -+ -+ /* This makes sure the global configuration file is not processed */ -+ ssh_options_set(session, SSH_OPTIONS_PROCESS_CONFIG, &process_config); -+ -+ /* Set the wanted list of hostkeys, ordered by preference */ -+ ssh_options_set(session, SSH_OPTIONS_HOSTKEYS, wanted); -+ -+ ssh_options_set(session, SSH_OPTIONS_HOST, "localhost"); -+ ssh_options_set(session, SSH_OPTIONS_KNOWNHOSTS, knownhosts_file); -+ -+ algo_list = ssh_client_select_hostkeys(session); -+ assert_non_null(algo_list); -+ assert_string_equal(algo_list, expect); -+ free(algo_list); -+ -+ ssh_free(session); -+} -+ -+static void torture_knownhosts_algorithms_negative(UNUSED_PARAM(void **state)) -+{ -+ const char *wanted = NULL; -+ const char *expect = NULL; -+ -+ char *algo_list = NULL; -+ -+ char *cwd = NULL; -+ char *tmp_dir = NULL; -+ -+ bool process_config = false; -+ int verbose = 4; -+ int rc = 0; -+ -+ ssh_session session; -+ /* Create temporary directory */ -+ cwd = torture_get_current_working_dir(); -+ assert_non_null(cwd); -+ -+ tmp_dir = torture_make_temp_dir(template); -+ assert_non_null(tmp_dir); -+ -+ rc = torture_change_dir(tmp_dir); -+ assert_int_equal(rc, 0); -+ -+ session = ssh_new(); -+ assert_non_null(session); -+ -+ ssh_options_set(session, SSH_OPTIONS_LOG_VERBOSITY, &verbose); -+ ssh_options_set(session, SSH_OPTIONS_PROCESS_CONFIG, &process_config); -+ ssh_options_set(session, SSH_OPTIONS_HOST, "localhost"); -+ -+ /* Test with unknown key type in known_hosts */ -+ wanted = "rsa-sha2-256"; -+ ssh_options_set(session, SSH_OPTIONS_HOSTKEYS, wanted); -+ torture_write_file("unknown_key_type", "localhost unknown AAAABBBBCCCC"); -+ ssh_options_set(session, SSH_OPTIONS_KNOWNHOSTS, "unknown_key_type"); -+ algo_list = ssh_client_select_hostkeys(session); -+ assert_non_null(algo_list); -+ assert_string_equal(algo_list, wanted); -+ SAFE_FREE(algo_list); -+ -+ /* Test with unsupported, but existing types */ -+ wanted = "rsa-sha2-256-cert-v01@openssh.com," -+ "rsa-sha2-512-cert-v01@openssh.com"; -+ ssh_options_set(session, SSH_OPTIONS_HOSTKEYS, wanted); -+ algo_list = ssh_client_select_hostkeys(session); -+ assert_null(algo_list); -+ -+ /* In FIPS mode, test filtering keys not allowed */ -+ if (ssh_fips_mode()) { -+ wanted = "ssh-ed25519,rsa-sha2-256,ssh-rsa"; -+ expect = "rsa-sha2-256"; -+ ssh_options_set(session, SSH_OPTIONS_HOSTKEYS, wanted); -+ torture_write_file("no_fips", LOCALHOST_DEFAULT_ED25519); -+ ssh_options_set(session, SSH_OPTIONS_KNOWNHOSTS, "no_fips"); -+ algo_list = ssh_client_select_hostkeys(session); -+ assert_non_null(algo_list); -+ assert_string_equal(algo_list, expect); -+ SAFE_FREE(algo_list); -+ } -+ -+ ssh_free(session); -+ -+ /* Teardown */ -+ rc = torture_change_dir(cwd); -+ assert_int_equal(rc, 0); -+ -+ rc = torture_rmdirs(tmp_dir); -+ assert_int_equal(rc, 0); -+ -+ SAFE_FREE(tmp_dir); -+ SAFE_FREE(cwd); -+} -+ - #ifndef _WIN32 /* There is no /dev/null on Windows */ - static void torture_knownhosts_host_exists(void **state) - { -@@ -457,12 +568,12 @@ static void torture_knownhosts_host_exists_global(void **state) - ssh_free(session); - } - --static void --torture_knownhosts_algorithms(void **state) -+static void torture_knownhosts_algorithms(void **state) - { - const char *knownhosts_file = *state; - char *algo_list = NULL; - ssh_session session; -+ bool process_config = false; - const char *expect = "ssh-ed25519,rsa-sha2-512,rsa-sha2-256,ssh-rsa," - "ecdsa-sha2-nistp521,ecdsa-sha2-nistp384," - "ecdsa-sha2-nistp256" -@@ -470,10 +581,15 @@ torture_knownhosts_algorithms(void **state) - ",ssh-dss" - #endif - ; -+ const char *expect_fips = "rsa-sha2-512,rsa-sha2-256,ecdsa-sha2-nistp521," -+ "ecdsa-sha2-nistp384,ecdsa-sha2-nistp256"; - - session = ssh_new(); - assert_non_null(session); - -+ /* This makes sure the global configuration file is not processed */ -+ ssh_options_set(session, SSH_OPTIONS_PROCESS_CONFIG, &process_config); -+ - ssh_options_set(session, SSH_OPTIONS_HOST, "localhost"); - ssh_options_set(session, SSH_OPTIONS_KNOWNHOSTS, knownhosts_file); - /* This makes sure the system's known_hosts are not used */ -@@ -481,18 +597,22 @@ torture_knownhosts_algorithms(void **state) - - algo_list = ssh_client_select_hostkeys(session); - assert_non_null(algo_list); -- assert_string_equal(algo_list, expect); -+ if (ssh_fips_mode()) { -+ assert_string_equal(algo_list, expect_fips); -+ } else { -+ assert_string_equal(algo_list, expect); -+ } - free(algo_list); - - ssh_free(session); - } - --static void --torture_knownhosts_algorithms_global(void **state) -+static void torture_knownhosts_algorithms_global(void **state) - { - const char *knownhosts_file = *state; - char *algo_list = NULL; - ssh_session session; -+ bool process_config = false; - const char *expect = "ssh-ed25519,rsa-sha2-512,rsa-sha2-256,ssh-rsa," - "ecdsa-sha2-nistp521,ecdsa-sha2-nistp384," - "ecdsa-sha2-nistp256" -@@ -500,10 +620,15 @@ torture_knownhosts_algorithms_global(void **state) - ",ssh-dss" - #endif - ; -+ const char *expect_fips = "rsa-sha2-512,rsa-sha2-256,ecdsa-sha2-nistp521," -+ "ecdsa-sha2-nistp384,ecdsa-sha2-nistp256"; - - session = ssh_new(); - assert_non_null(session); - -+ /* This makes sure the global configuration file is not processed */ -+ ssh_options_set(session, SSH_OPTIONS_PROCESS_CONFIG, &process_config); -+ - ssh_options_set(session, SSH_OPTIONS_HOST, "localhost"); - /* This makes sure the current-user's known hosts are not used */ - ssh_options_set(session, SSH_OPTIONS_KNOWNHOSTS, "/dev/null"); -@@ -511,12 +636,17 @@ torture_knownhosts_algorithms_global(void **state) - - algo_list = ssh_client_select_hostkeys(session); - assert_non_null(algo_list); -- assert_string_equal(algo_list, expect); -+ if (ssh_fips_mode()) { -+ assert_string_equal(algo_list, expect_fips); -+ } else { -+ assert_string_equal(algo_list, expect); -+ } - free(algo_list); - - ssh_free(session); - } --#endif -+ -+#endif /* _WIN32 There is no /dev/null on Windows */ - - int torture_run_tests(void) { - int rc; -@@ -538,6 +668,10 @@ int torture_run_tests(void) { - cmocka_unit_test_setup_teardown(torture_knownhosts_get_algorithms_names, - setup_knownhosts_file, - teardown_knownhosts_file), -+ cmocka_unit_test_setup_teardown(torture_knownhosts_algorithms_wanted, -+ setup_knownhosts_file, -+ teardown_knownhosts_file), -+ cmocka_unit_test(torture_knownhosts_algorithms_negative), - #ifndef _WIN32 - cmocka_unit_test_setup_teardown(torture_knownhosts_host_exists, - setup_knownhosts_file, --- -2.21.0 - diff --git a/SOURCES/libssh-0.9.0-run-sshd-confined.patch b/SOURCES/libssh-0.9.0-run-sshd-confined.patch deleted file mode 100644 index 4bc62bb..0000000 --- a/SOURCES/libssh-0.9.0-run-sshd-confined.patch +++ /dev/null @@ -1,67 +0,0 @@ ---- a/tests/torture.c 2019-06-28 14:01:52.936462964 +0200 -+++ b/tests/torture.c 2019-06-28 14:03:52.324325029 +0200 -@@ -854,7 +854,7 @@ - s = *state; - - snprintf(sshd_start_cmd, sizeof(sshd_start_cmd), -- "/usr/sbin/sshd -r -f %s -E %s/sshd/daemon.log 2> %s/sshd/cwrap.log", -+ "runcon -t sshd_t -u system_u -r system_r /usr/sbin/sshd -r -f %s -E %s/sshd/daemon.log 2> %s/sshd/cwrap.log", - s->srv_config, s->socket_dir, s->socket_dir); - - rc = system(sshd_start_cmd); -@@ -864,7 +864,7 @@ - unsetenv("PAM_WRAPPER"); - - /* Wait until the sshd is ready to accept connections */ -- rc = torture_wait_for_daemon(5); -+ rc = torture_wait_for_daemon(10); - assert_int_equal(rc, 0); - } - -@@ -904,27 +904,32 @@ - torture_reload_sshd_server(void **state) - { - struct torture_state *s = *state; -- pid_t pid; -+ char sshd_start_cmd[1024]; - int rc; - -- /* read the pidfile */ -- pid = torture_read_pidfile(s->srv_pidfile); -- assert_int_not_equal(pid, -1); -+ rc = torture_terminate_process(s->srv_pidfile); -+ if (rc != 0) { -+ fprintf(stderr, "XXXXXX Failed to terminate sshd\n"); -+ } - -- kill(pid, SIGHUP); -+ usleep(100 * 1000); - -- /* 10 ms */ -- usleep(10 * 1000); -+ /* Set the default interface for the server */ -+ setenv("SOCKET_WRAPPER_DEFAULT_IFACE", "10", 1); -+ setenv("PAM_WRAPPER", "1", 1); - -- rc = kill(pid, 0); -- if (rc != 0) { -- fprintf(stderr, -- "ERROR: SSHD process %u died during reload!\n", pid); -- return SSH_ERROR; -- } -+ snprintf(sshd_start_cmd, sizeof(sshd_start_cmd), -+ "runcon -t sshd_t -u system_u -r system_r /usr/sbin/sshd -r -f %s -E %s/sshd/daemon.log 2> %s/sshd/cwrap.log", -+ s->srv_config, s->socket_dir, s->socket_dir); -+ -+ rc = system(sshd_start_cmd); -+ assert_return_code(rc, errno); -+ -+ setenv("SOCKET_WRAPPER_DEFAULT_IFACE", "21", 1); -+ unsetenv("PAM_WRAPPER"); - - /* Wait until the sshd is ready to accept connections */ -- rc = torture_wait_for_daemon(5); -+ rc = torture_wait_for_daemon(10); - assert_int_equal(rc, 0); - return SSH_OK; - } diff --git a/SOURCES/libssh-0.9.0-skip-1k-rsa-key-generation-test.patch b/SOURCES/libssh-0.9.0-skip-1k-rsa-key-generation-test.patch deleted file mode 100644 index f45a27d..0000000 --- a/SOURCES/libssh-0.9.0-skip-1k-rsa-key-generation-test.patch +++ /dev/null @@ -1,59 +0,0 @@ -From bf2ed2ca929e5e12279f85c930f8fbb452ada888 Mon Sep 17 00:00:00 2001 -From: Anderson Toshiyuki Sasaki -Date: Tue, 30 Jul 2019 18:22:30 +0200 -Subject: [PATCH] tests: Skip testing 1024 bits key generation in FIPS mode - -In torture_threads_pki_rsa, skip the test which generates 1024 bits RSA -key pair when in FIPS mode. - -Signed-off-by: Anderson Toshiyuki Sasaki ---- - tests/unittests/torture_threads_pki_rsa.c | 28 ++++++++++++----------- - 1 file changed, 15 insertions(+), 13 deletions(-) - -diff --git a/tests/unittests/torture_threads_pki_rsa.c b/tests/unittests/torture_threads_pki_rsa.c -index 5a841ee9..03d526cd 100644 ---- a/tests/unittests/torture_threads_pki_rsa.c -+++ b/tests/unittests/torture_threads_pki_rsa.c -@@ -571,23 +571,25 @@ static void *thread_pki_rsa_generate_key(void *threadid) - session = ssh_new(); - assert_non_null(session); - -- rc = ssh_pki_generate(SSH_KEYTYPE_RSA, 1024, &key); -- assert_ssh_return_code(session, rc); -- assert_non_null(key); -+ if (!ssh_fips_mode()) { -+ rc = ssh_pki_generate(SSH_KEYTYPE_RSA, 1024, &key); -+ assert_ssh_return_code(session, rc); -+ assert_non_null(key); - -- rc = ssh_pki_export_privkey_to_pubkey(key, &pubkey); -- assert_int_equal(rc, SSH_OK); -- assert_non_null(pubkey); -+ rc = ssh_pki_export_privkey_to_pubkey(key, &pubkey); -+ assert_int_equal(rc, SSH_OK); -+ assert_non_null(pubkey); - -- sign = pki_do_sign(key, RSA_HASH, 20, SSH_DIGEST_SHA256); -- assert_non_null(sign); -+ sign = pki_do_sign(key, RSA_HASH, 20, SSH_DIGEST_SHA256); -+ assert_non_null(sign); - -- rc = pki_signature_verify(session, sign, pubkey, RSA_HASH, 20); -- assert_ssh_return_code(session, rc); -+ rc = pki_signature_verify(session, sign, pubkey, RSA_HASH, 20); -+ assert_ssh_return_code(session, rc); - -- ssh_signature_free(sign); -- SSH_KEY_FREE(key); -- SSH_KEY_FREE(pubkey); -+ ssh_signature_free(sign); -+ SSH_KEY_FREE(key); -+ SSH_KEY_FREE(pubkey); -+ } - - rc = ssh_pki_generate(SSH_KEYTYPE_RSA, 2048, &key); - assert_ssh_return_code(session, rc); --- -2.21.0 - diff --git a/SOURCES/libssh-0.9.0.tar.xz.asc b/SOURCES/libssh-0.9.0.tar.xz.asc deleted file mode 100644 index 880c31b..0000000 --- a/SOURCES/libssh-0.9.0.tar.xz.asc +++ /dev/null @@ -1,16 +0,0 @@ ------BEGIN PGP SIGNATURE----- - -iQIzBAABCAAdFiEEjf9T4Y8qvI2PPJIjfuD8TcwBTj0FAl0VtfEACgkQfuD8TcwB -Tj0cthAApYglGlE2+f06uKvPUc8LvzVkRFJA5ycm7uMddrMbSJ+ElScEu1a6q/qV -xPK3w49WdDpbYcz3s/GtdhGkSTye49Zav2PJzDFWvac+PoXVOwt7C5JzlPSq7kfa -0i6k1/7YSiPcFCo7467v2fpg0t80OHgS+Tbc1mwI0KtRzPA2pjqHsnu+RVNc6EA1 -paUf1QMhjPFIQwcqJcQGAHZBqpx4JBspzBpC7wDxQKFh5FfMbVOxPG3qvgeOYDqd -cbfsJkdOJQ0rAxyNBB6xbz8XktJyK4Bjg5eT9GEz+zpsjh8jtfJMhNEemaBLXkug -+4wb4DU2uhEuOoOFh0YOgLcRLm2INNhVSTcizasmA8X1YcvAUmKB7fUMuYwOQOPZ -bsSkJ8kg7v76Y8w/pUger99pVYg0iiLi9KI6a2t7qTSibZewv38IV6eSJqORnZja -SLeswZUAAtHK/nTf7ohZ8Blnhx3UE5M6vyuli+KMmHAxTOzHhyWJvYDYJ2oJ7+tR -N49U1O77VE0WYY7HoyEXYkvSwWLb+MYK6ueaJTVBEbq7ZdpzQBQtPLoyCXUdQwZ2 -DyZaaZnhkn9FYvsJp/twHra3XlQ359EXdMwflISaKgFUpfaluLIu1xRGrYH4XPNm -FHZRPCj34PY1uDOKHXiRl/xUdaR4CSIKPgofhxzy/mLZepJR9vU= -=xGD9 ------END PGP SIGNATURE----- diff --git a/SOURCES/libssh-0.9.4-add-cve-2019-14889-test.patch b/SOURCES/libssh-0.9.4-add-cve-2019-14889-test.patch new file mode 100644 index 0000000..ce149b4 --- /dev/null +++ b/SOURCES/libssh-0.9.4-add-cve-2019-14889-test.patch @@ -0,0 +1,125 @@ +From 1694606e12d8950b003ff86248883732ef05e00c Mon Sep 17 00:00:00 2001 +From: Anderson Toshiyuki Sasaki +Date: Fri, 19 Jun 2020 11:59:33 +0200 +Subject: [PATCH] tests: Add test for CVE-2019-14889 + +The test checks if a command appended to the file path is not executed. + +Signed-off-by: Anderson Toshiyuki Sasaki +Reviewed-by: Andreas Schneider +--- + tests/client/torture_scp.c | 84 ++++++++++++++++++++++++++++++++++++++ + 1 file changed, 84 insertions(+) + +diff --git a/tests/client/torture_scp.c b/tests/client/torture_scp.c +index 8f080af3..59a00bae 100644 +--- a/tests/client/torture_scp.c ++++ b/tests/client/torture_scp.c +@@ -37,6 +37,7 @@ + #define BUF_SIZE 1024 + + #define TEMPLATE BINARYDIR "/tests/home/alice/temp_dir_XXXXXX" ++#define ALICE_HOME BINARYDIR "/tests/home/alice" + + struct scp_st { + struct torture_state *s; +@@ -540,6 +541,86 @@ static void torture_scp_upload_newline(void **state) + fclose(file); + } + ++static void torture_scp_upload_appended_command(void **state) ++{ ++ struct scp_st *ts = NULL; ++ struct torture_state *s = NULL; ++ ++ ssh_session session = NULL; ++ ssh_scp scp = NULL; ++ ++ FILE *file = NULL; ++ ++ char buf[1024]; ++ char *rs = NULL; ++ int rc; ++ ++ assert_non_null(state); ++ ts = *state; ++ ++ assert_non_null(ts->s); ++ s = ts->s; ++ ++ session = s->ssh.session; ++ assert_non_null(session); ++ ++ assert_non_null(ts->tmp_dir_basename); ++ assert_non_null(ts->tmp_dir); ++ ++ /* Upload a file path with a command appended */ ++ ++ /* Append a command to the file path */ ++ snprintf(buf, BUF_SIZE, "%s" ++ "/;touch hack", ++ ts->tmp_dir); ++ ++ /* When writing the file_name must be the directory name */ ++ scp = ssh_scp_new(session, SSH_SCP_WRITE | SSH_SCP_RECURSIVE, ++ buf); ++ assert_non_null(scp); ++ ++ rc = ssh_scp_init(scp); ++ assert_ssh_return_code(session, rc); ++ ++ /* Push directory where the new file will be copied */ ++ rc = ssh_scp_push_directory(scp, ";touch hack", 0755); ++ assert_ssh_return_code(session, rc); ++ ++ /* Try to push file */ ++ rc = ssh_scp_push_file(scp, "original", 8, 0644); ++ assert_ssh_return_code(session, rc); ++ ++ rc = ssh_scp_write(scp, "original", 8); ++ assert_ssh_return_code(session, rc); ++ ++ /* Leave the directory */ ++ rc = ssh_scp_leave_directory(scp); ++ assert_ssh_return_code(session, rc); ++ ++ /* Cleanup */ ++ ssh_scp_close(scp); ++ ssh_scp_free(scp); ++ ++ /* Make sure the command was not executed */ ++ snprintf(buf, BUF_SIZE, ALICE_HOME "/hack"); ++ file = fopen(buf, "r"); ++ assert_null(file); ++ ++ /* Open the file and check content */ ++ snprintf(buf, BUF_SIZE, "%s" ++ "/;touch hack/original", ++ ts->tmp_dir); ++ ++ file = fopen(buf, "r"); ++ assert_non_null(file); ++ ++ rs = fgets(buf, 1024, file); ++ assert_non_null(rs); ++ assert_string_equal(buf, "original"); ++ ++ fclose(file); ++} ++ + int torture_run_tests(void) + { + int rc; +@@ -559,6 +640,9 @@ int torture_run_tests(void) + cmocka_unit_test_setup_teardown(torture_scp_upload_newline, + session_setup, + session_teardown), ++ cmocka_unit_test_setup_teardown(torture_scp_upload_appended_command, ++ session_setup, ++ session_teardown), + }; + + ssh_init(); +-- +2.26.2 + diff --git a/SOURCES/libssh-0.9.4-do-not-parse-config-during-tests.patch b/SOURCES/libssh-0.9.4-do-not-parse-config-during-tests.patch new file mode 100644 index 0000000..ac5ee0d --- /dev/null +++ b/SOURCES/libssh-0.9.4-do-not-parse-config-during-tests.patch @@ -0,0 +1,58 @@ +From f10d80047c660e33f5c365bf3cf436a0c2a300f1 Mon Sep 17 00:00:00 2001 +From: Anderson Toshiyuki Sasaki +Date: Tue, 23 Jun 2020 18:31:47 +0200 +Subject: [PATCH] tests: Do not parse configuration file in torture_knownhosts + +The test might fail if there is a local configuration file that changes +the location of the known_hosts file. The test should not be affected +by configuration files present in the testing environment. + +Signed-off-by: Anderson Toshiyuki Sasaki +Reviewed-by: Jakub Jelen +--- + tests/client/torture_knownhosts.c | 8 ++++++++ + 1 file changed, 8 insertions(+) + +diff --git a/tests/client/torture_knownhosts.c b/tests/client/torture_knownhosts.c +index fcc54846..55aee217 100644 +--- a/tests/client/torture_knownhosts.c ++++ b/tests/client/torture_knownhosts.c +@@ -307,6 +307,7 @@ static void torture_knownhosts_other_auto(void **state) { + char tmp_file[1024] = {0}; + char *known_hosts_file = NULL; + int rc; ++ bool process_config = false; + + snprintf(tmp_file, + sizeof(tmp_file), +@@ -344,6 +345,9 @@ static void torture_knownhosts_other_auto(void **state) { + + s->ssh.session = session; + ++ rc = ssh_options_set(session, SSH_OPTIONS_PROCESS_CONFIG, &process_config); ++ assert_ssh_return_code(session, rc); ++ + rc = ssh_options_set(session, SSH_OPTIONS_HOST, TORTURE_SSH_SERVER); + assert_ssh_return_code(session, rc); + +@@ -368,6 +372,7 @@ static void torture_knownhosts_conflict(void **state) { + char *known_hosts_file = NULL; + FILE *file; + int rc; ++ bool process_config = false; + + snprintf(tmp_file, + sizeof(tmp_file), +@@ -411,6 +416,9 @@ static void torture_knownhosts_conflict(void **state) { + + s->ssh.session = session; + ++ rc = ssh_options_set(session, SSH_OPTIONS_PROCESS_CONFIG, &process_config); ++ assert_ssh_return_code(session, rc); ++ + ssh_options_set(session, SSH_OPTIONS_HOST, TORTURE_SSH_SERVER); + ssh_options_set(session, SSH_OPTIONS_KNOWNHOSTS, known_hosts_file); + rc = ssh_options_set(session, SSH_OPTIONS_HOSTKEYS, "rsa-sha2-256"); +-- +2.26.2 + diff --git a/SOURCES/libssh-0.9.4-do-not-return-error-server-closed-channel.patch b/SOURCES/libssh-0.9.4-do-not-return-error-server-closed-channel.patch new file mode 100644 index 0000000..387b9c0 --- /dev/null +++ b/SOURCES/libssh-0.9.4-do-not-return-error-server-closed-channel.patch @@ -0,0 +1,43 @@ +From 750e4f3f9d3ec879929801d65a500ec3ad84ff67 Mon Sep 17 00:00:00 2001 +From: Anderson Toshiyuki Sasaki +Date: Thu, 18 Jun 2020 19:08:54 +0200 +Subject: [PATCH] channel: Do not return error if the server closed the channel + +If the server properly closed the channel, the client should not return +error if it finds the channel closed. + +Fixes T231 + +Signed-off-by: Anderson Toshiyuki Sasaki +Reviewed-by: Jakub Jelen +--- + src/channels.c | 7 ++++--- + 1 file changed, 4 insertions(+), 3 deletions(-) + +diff --git a/src/channels.c b/src/channels.c +index 9fe309d0..607bd568 100644 +--- a/src/channels.c ++++ b/src/channels.c +@@ -2932,15 +2932,16 @@ int ssh_channel_read_timeout(ssh_channel channel, + if (session->session_state == SSH_SESSION_STATE_ERROR) { + return SSH_ERROR; + } ++ /* If the server closed the channel properly, there is nothing to do */ ++ if (channel->remote_eof && ssh_buffer_get_len(stdbuf) == 0) { ++ return 0; ++ } + if (channel->state == SSH_CHANNEL_STATE_CLOSED) { + ssh_set_error(session, + SSH_FATAL, + "Remote channel is closed."); + return SSH_ERROR; + } +- if (channel->remote_eof && ssh_buffer_get_len(stdbuf) == 0) { +- return 0; +- } + len = ssh_buffer_get_len(stdbuf); + /* Read count bytes if len is greater, everything otherwise */ + len = (len > count ? count : len); +-- +2.26.2 + diff --git a/SOURCES/libssh-0.9.4-enable-sshd-sha1-algorithms.patch b/SOURCES/libssh-0.9.4-enable-sshd-sha1-algorithms.patch new file mode 100644 index 0000000..a821223 --- /dev/null +++ b/SOURCES/libssh-0.9.4-enable-sshd-sha1-algorithms.patch @@ -0,0 +1,18 @@ +--- a/tests/torture.c 2020-04-09 16:16:07.691894761 +0200 ++++ b/tests/torture.c 2020-04-09 20:11:50.577962771 +0200 +@@ -636,6 +636,15 @@ + # else /* HAVE_DSA */ + "HostKeyAlgorithms +ssh-rsa\n" + # endif /* HAVE_DSA */ ++/* Add back algorithms removed from default in OpenSSH-8.2 due to SHA1 ++ * deprecation*/ ++# if (OPENSSH_VERSION_MAJOR == 8 && OPENSSH_VERSION_MINOR >= 2) ++ "KexAlgorithms +diffie-hellman-group14-sha1," ++ "diffie-hellman-group-exchange-sha1," ++ "diffie-hellman-group1-sha1\n" ++ "HostKeyAlgorithms +ssh-rsa\n" ++ "CASignatureAlgorithms +ssh-rsa\n" ++#endif + # if (OPENSSH_VERSION_MAJOR == 7 && OPENSSH_VERSION_MINOR < 6) + "Ciphers +3des-cbc,aes128-cbc,aes192-cbc,aes256-cbc,blowfish-cbc\n" + # else /* OPENSSH_VERSION 7.0 - 7.5 */ diff --git a/SOURCES/libssh-0.9.4-fix-version.patch b/SOURCES/libssh-0.9.4-fix-version.patch new file mode 100644 index 0000000..143e972 --- /dev/null +++ b/SOURCES/libssh-0.9.4-fix-version.patch @@ -0,0 +1,11 @@ +--- a/include/libssh/libssh.h 2020-04-15 13:38:32.899177005 +0200 ++++ b/include/libssh/libssh.h 2020-04-15 13:38:57.406454427 +0200 +@@ -79,7 +79,7 @@ + /* libssh version */ + #define LIBSSH_VERSION_MAJOR 0 + #define LIBSSH_VERSION_MINOR 9 +-#define LIBSSH_VERSION_MICRO 3 ++#define LIBSSH_VERSION_MICRO 4 + + #define LIBSSH_VERSION_INT SSH_VERSION_INT(LIBSSH_VERSION_MAJOR, \ + LIBSSH_VERSION_MINOR, \ diff --git a/SOURCES/libssh-0.9.4.tar.xz.asc b/SOURCES/libssh-0.9.4.tar.xz.asc new file mode 100644 index 0000000..84b673c --- /dev/null +++ b/SOURCES/libssh-0.9.4.tar.xz.asc @@ -0,0 +1,16 @@ +-----BEGIN PGP SIGNATURE----- + +iQIzBAABCgAdFiEEjf9T4Y8qvI2PPJIjfuD8TcwBTj0FAl6O0BgACgkQfuD8TcwB +Tj0dCQ/+J0pjZU6uu7h6gkc4BbRciCpYDIv66Lw9iCc2bQmLLhPrukWjz6/PDV+U +iL/1dlwxG8rOlXdtCEFGyDvm0y4E8NaQCcgjU9jA8nsXo+SyyJAeWT7BeI3m2hPi +tjbLAjQVHCW1jIite1dJeoPIPg15LChc08t+HWVI3pwQviwlJWTPmHgMaT3uwa1X +fD66hjgB2UFo5eYnbION3L/jpA0vsI4o4F5CFPEhgbz3H6KmrgQbKLPM3H/103zU +XjtHEw7gy/85OmjpcskMrUVAMbw9EZ5ESFOrKyuQaFBY57L//tAdUaEloxsMKt+5 +nmYunmlGmDLT6rHfjSg5X1S+NsQaXhGelc0TLVgvlzs4kR+QbApR1ewKTcsYlVwr +jYG+PuAiROqc18xM/fQYh8UqohluDBmUpEDmVOEKT2tg/S7R5RJtOxdmcZPsLO+W +EOoP+OeUvQqNlzqu6kBRI4v2lwVU4QwDzKCNRzwQHJOH+azH/3FRJBDF1ZAQvgxy +w/NqlpFO6P76e0SLzBjHCDyqwbAzfq4WK3f5oE0RAA5RlndWusovTAWaYrAbVaoz +emkt/guiHHsbLy6S2ELJu4BI9TGGtDMJoo1ScMMQzFqijUISCBgK/+6mUVUlMli0 +lTH6VE+MvpElADE+IYSXWOLHrspTxVa/jVun3iYE8Nexn6G0XE0= +=xSu8 +-----END PGP SIGNATURE----- diff --git a/SOURCES/libssh_server.config b/SOURCES/libssh_server.config index 279b5a3..d3ef5c2 100644 --- a/SOURCES/libssh_server.config +++ b/SOURCES/libssh_server.config @@ -1,4 +1,2 @@ # Parse system-wide crypto configuration file Include /etc/crypto-policies/back-ends/libssh.config -# Parse OpenSSH configuration file for consistency -Include /etc/ssh/sshd_config diff --git a/SPECS/libssh.spec b/SPECS/libssh.spec index 925aa8f..c61df8b 100644 --- a/SPECS/libssh.spec +++ b/SPECS/libssh.spec @@ -1,6 +1,6 @@ Name: libssh -Version: 0.9.0 -Release: 4%{?dist} +Version: 0.9.4 +Release: 2%{?dist} Summary: A library implementing the SSH protocol License: LGPLv2+ URL: http://www.libssh.org @@ -11,9 +11,11 @@ Source2: https://cryptomilk.org/gpgkey-8DFF53E18F2ABC8D8F3C92237EE0FC4DCC Source3: libssh_client.config Source4: libssh_server.config -Patch0: libssh-0.9.0-run-sshd-confined.patch -Patch1: libssh-0.9.0-do-not-ignore-known-hosts-keys.patch -Patch2: libssh-0.9.0-skip-1k-rsa-key-generation-test.patch +Patch0: libssh-0.9.4-enable-sshd-sha1-algorithms.patch +Patch1: libssh-0.9.4-fix-version.patch +Patch2: libssh-0.9.4-do-not-return-error-server-closed-channel.patch +Patch3: libssh-0.9.4-add-cve-2019-14889-test.patch +Patch4: libssh-0.9.4-do-not-parse-config-during-tests.patch BuildRequires: cmake BuildRequires: doxygen @@ -133,6 +135,20 @@ popd %attr(0644,root,root) %config(noreplace) %{_sysconfdir}/libssh/libssh_server.config %changelog +* Wed Jun 24 2020 Anderson Sasaki - 0.9.4-2 +- Do not return error when server properly closed the channel (#1849071) +- Add a test for CVE-2019-14889 +- Do not parse configuration file in torture_knownhosts test + +* Tue May 26 2020 Anderson Sasaki - 0.9.4-1 +- Update to version 0.9.4 + https://www.libssh.org/2020/04/09/libssh-0-9-4-and-libssh-0-8-9-security-release/ +- Fixed CVE-2019-14889 (#1781782) +- Fixed CVE-2020-1730 (#1802422) +- Create missing directories in the path provided for known_hosts files (#1733914) +- Removed inclusion of OpenSSH server configuration file from + libssh_server.config (#1821339) + * Mon Aug 05 2019 Anderson Sasaki - 0.9.0-4 - Skip 1024 bits RSA key generation test in FIPS mode (#1734485)