Version in base suite: 0.9.7-0+deb11u1 Base version: libssh_0.9.7-0+deb11u1 Target version: libssh_0.9.8-0+deb11u1 Base file: /srv/ftp-master.debian.org/ftp/pool/main/libs/libssh/libssh_0.9.7-0+deb11u1.dsc Target file: /srv/ftp-master.debian.org/policy/pool/main/libs/libssh/libssh_0.9.8-0+deb11u1.dsc CMakeLists.txt | 4 ChangeLog | 5 debian/changelog | 15 debian/patches/0001-Fix-regression-in-IPv6-addresses-in-hostname-parsing.patch | 135 +++ debian/patches/0002-tests-Increase-test-coverage-for-IPv6-address-parsin.patch | 134 +++ debian/patches/series | 2 include/libssh/kex.h | 1 include/libssh/libcrypto.h | 5 include/libssh/libgcrypt.h | 1 include/libssh/libmbedcrypto.h | 1 include/libssh/misc.h | 2 include/libssh/packet.h | 1 include/libssh/session.h | 6 include/libssh/wrapper.h | 39 src/ABI/current | 2 src/ABI/libssh-4.8.9.symbols | 421 ++++++++++ src/CMakeLists.txt | 17 src/channels.c | 12 src/config_parser.c | 12 src/connect.c | 2 src/curve25519.c | 19 src/dh-gex.c | 7 src/dh.c | 17 src/ecdh.c | 8 src/ecdh_crypto.c | 12 src/ecdh_gcrypt.c | 10 src/ecdh_mbedcrypto.c | 11 src/kdf.c | 126 ++ src/kex.c | 88 +- src/libcrypto.c | 215 +++-- src/libgcrypt.c | 186 ++-- src/libmbedcrypto.c | 234 +++-- src/misc.c | 112 ++ src/options.c | 40 src/packet.c | 60 + src/packet_cb.c | 12 src/server.c | 8 src/session.c | 76 + tests/client/torture_proxycommand.c | 53 + tests/client/torture_rekey.c | 56 - tests/client/torture_session.c | 35 tests/keys/certauth/id_rsa | 54 - tests/keys/certauth/id_rsa-cert.pub | 2 tests/keys/certauth/id_rsa.pub | 2 tests/unittests/torture_config.c | 44 - tests/unittests/torture_misc.c | 119 ++ 46 files changed, 1884 insertions(+), 539 deletions(-) diff -Nru libssh-0.9.7/CMakeLists.txt libssh-0.9.8/CMakeLists.txt --- libssh-0.9.7/CMakeLists.txt 2023-05-04 11:41:35.000000000 +0000 +++ libssh-0.9.8/CMakeLists.txt 2023-12-18 16:57:27.000000000 +0000 @@ -10,7 +10,7 @@ include(DefineCMakeDefaults) include(DefineCompilerFlags) -project(libssh VERSION 0.9.7 LANGUAGES C) +project(libssh VERSION 0.9.8 LANGUAGES C) # global needed variable set(APPLICATION_NAME ${PROJECT_NAME}) @@ -22,7 +22,7 @@ # Increment AGE. Set REVISION to 0 # If the source code was changed, but there were no interface changes: # Increment REVISION. -set(LIBRARY_VERSION "4.8.8") +set(LIBRARY_VERSION "4.8.9") set(LIBRARY_SOVERSION "4") # where to look first for cmake modules, before ${CMAKE_ROOT}/Modules/ is checked diff -Nru libssh-0.9.7/ChangeLog libssh-0.9.8/ChangeLog --- libssh-0.9.7/ChangeLog 2023-05-04 11:41:09.000000000 +0000 +++ libssh-0.9.8/ChangeLog 2023-12-18 16:57:27.000000000 +0000 @@ -1,5 +1,10 @@ ChangeLog ========== +version 0.9.8 (released 2023-12-18) + * Fix CVE-2023-6004: Command injection using proxycommand + * Fix CVE-2023-48795: Potential downgrade attack using strict kex + * Fix CVE-2023-6918: Missing checks for return values of MD functions + * Allow @ in usernames when parsing from URI composes version 0.9.7 (released 2023-05-04) * Fix CVE-2023-1667: a NULL dereference during rekeying with algorithm guessing diff -Nru libssh-0.9.7/debian/changelog libssh-0.9.8/debian/changelog --- libssh-0.9.7/debian/changelog 2023-05-21 18:22:05.000000000 +0000 +++ libssh-0.9.8/debian/changelog 2023-12-25 10:40:33.000000000 +0000 @@ -1,3 +1,18 @@ +libssh (0.9.8-0+deb11u1) bullseye-security; urgency=medium + + * New upstream security release: + - Fix Command injection using ProxyCommand + (CVE-2023-6004, Closes: #1059061) + - Fix missing checks for return values of MD functions + (CVE-2023-6918, Closes: #1059059) + - Fix potential downgrade attack using strict kex + (CVE-2023-48795, Closes: #1059004) + * Fix regression in IPv6 addresses in hostname parsing from CVE-2023-6004 + fix. Patch and unit test backported from upstream stable-0.9 branch. + See https://gitlab.com/libssh/libssh-mirror/-/issues/227 + + -- Martin Pitt Mon, 25 Dec 2023 11:40:33 +0100 + libssh (0.9.7-0+deb11u1) bullseye-security; urgency=medium * New upstream security microrelease: diff -Nru libssh-0.9.7/debian/patches/0001-Fix-regression-in-IPv6-addresses-in-hostname-parsing.patch libssh-0.9.8/debian/patches/0001-Fix-regression-in-IPv6-addresses-in-hostname-parsing.patch --- libssh-0.9.7/debian/patches/0001-Fix-regression-in-IPv6-addresses-in-hostname-parsing.patch 1970-01-01 00:00:00.000000000 +0000 +++ libssh-0.9.8/debian/patches/0001-Fix-regression-in-IPv6-addresses-in-hostname-parsing.patch 2023-12-25 10:40:33.000000000 +0000 @@ -0,0 +1,135 @@ +From 72f59157e6ccbd4c0bb806690931413169a0886f Mon Sep 17 00:00:00 2001 +From: Jakub Jelen +Date: Fri, 22 Dec 2023 10:32:40 +0100 +Subject: [PATCH 1/2] Fix regression in IPv6 addresses in hostname parsing + +Signed-off-by: Jakub Jelen +Reviewed-by: Andreas Schneider +(cherry picked from commit 4f997aee7c7d7ea346b3e8ba505da0b7601ff318) +--- + include/libssh/config_parser.h | 11 ++++++++--- + src/config.c | 4 ++-- + src/config_parser.c | 16 +++++++++++----- + src/options.c | 10 ++-------- + 4 files changed, 23 insertions(+), 18 deletions(-) + +diff --git a/include/libssh/config_parser.h b/include/libssh/config_parser.h +index e974917c..ee647bfb 100644 +--- a/include/libssh/config_parser.h ++++ b/include/libssh/config_parser.h +@@ -26,6 +26,8 @@ + #ifndef CONFIG_PARSER_H_ + #define CONFIG_PARSER_H_ + ++#include ++ + char *ssh_config_get_cmd(char **str); + + char *ssh_config_get_token(char **str); +@@ -45,13 +47,16 @@ int ssh_config_get_yesno(char **str, int notfound); + * be stored or NULL if we do not care about the result. + * @param[out] port Pointer to the location, where the new port will + * be stored or NULL if we do not care about the result. ++ * @param[in] ignore_port Set to true if the we should not attempt to parse ++ * port number. + * + * @returns SSH_OK if the provided string is in format of SSH URI, + * SSH_ERROR on failure + */ + int ssh_config_parse_uri(const char *tok, +- char **username, +- char **hostname, +- char **port); ++ char **username, ++ char **hostname, ++ char **port, ++ bool ignore_port); + + #endif /* LIBSSH_CONFIG_H_ */ +diff --git a/src/config.c b/src/config.c +index 54ada276..a813568d 100644 +--- a/src/config.c ++++ b/src/config.c +@@ -324,7 +324,7 @@ ssh_config_parse_proxy_jump(ssh_session session, const char *s, bool do_parsing) + } + if (parse_entry) { + /* We actually care only about the first item */ +- rv = ssh_config_parse_uri(cp, &username, &hostname, &port); ++ rv = ssh_config_parse_uri(cp, &username, &hostname, &port, false); + /* The rest of the list needs to be passed on */ + if (endp != NULL) { + next = strdup(endp + 1); +@@ -335,7 +335,7 @@ ssh_config_parse_proxy_jump(ssh_session session, const char *s, bool do_parsing) + } + } else { + /* The rest is just sanity-checked to avoid failures later */ +- rv = ssh_config_parse_uri(cp, NULL, NULL, NULL); ++ rv = ssh_config_parse_uri(cp, NULL, NULL, NULL, false); + } + if (rv != SSH_OK) { + goto out; +diff --git a/src/config_parser.c b/src/config_parser.c +index 87bac5d4..a2da0a62 100644 +--- a/src/config_parser.c ++++ b/src/config_parser.c +@@ -134,9 +134,10 @@ int ssh_config_get_yesno(char **str, int notfound) + } + + int ssh_config_parse_uri(const char *tok, +- char **username, +- char **hostname, +- char **port) ++ char **username, ++ char **hostname, ++ char **port, ++ bool ignore_port) + { + char *endp = NULL; + long port_n; +@@ -182,12 +183,17 @@ int ssh_config_parse_uri(const char *tok, + if (endp == NULL) { + goto error; + } +- } else { +- /* Hostnames or aliases expand to the last colon or to the end */ ++ } else if (!ignore_port) { ++ /* Hostnames or aliases expand to the last colon (if port is requested) ++ * or to the end */ + endp = strrchr(tok, ':'); + if (endp == NULL) { + endp = strchr(tok, '\0'); + } ++ } else { ++ /* If no port is requested, expand to the end of line ++ * (to accommodate the IPv6 addresses) */ ++ endp = strchr(tok, '\0'); + } + if (tok == endp) { + /* Zero-length hostnames are not valid */ +diff --git a/src/options.c b/src/options.c +index 7c03e7ab..0890ff2e 100644 +--- a/src/options.c ++++ b/src/options.c +@@ -491,17 +491,11 @@ int ssh_options_set(ssh_session session, enum ssh_options_e type, + ssh_set_error_invalid(session); + return -1; + } else { +- char *username = NULL, *hostname = NULL, *port = NULL; +- rc = ssh_config_parse_uri(value, &username, &hostname, &port); ++ char *username = NULL, *hostname = NULL; ++ rc = ssh_config_parse_uri(value, &username, &hostname, NULL, true); + if (rc != SSH_OK) { + return -1; + } +- if (port != NULL) { +- SAFE_FREE(username); +- SAFE_FREE(hostname); +- SAFE_FREE(port); +- return -1; +- } + if (username != NULL) { + SAFE_FREE(session->opts.username); + session->opts.username = username; +-- +2.43.0 + diff -Nru libssh-0.9.7/debian/patches/0002-tests-Increase-test-coverage-for-IPv6-address-parsin.patch libssh-0.9.8/debian/patches/0002-tests-Increase-test-coverage-for-IPv6-address-parsin.patch --- libssh-0.9.7/debian/patches/0002-tests-Increase-test-coverage-for-IPv6-address-parsin.patch 1970-01-01 00:00:00.000000000 +0000 +++ libssh-0.9.8/debian/patches/0002-tests-Increase-test-coverage-for-IPv6-address-parsin.patch 2023-12-25 10:40:33.000000000 +0000 @@ -0,0 +1,134 @@ +From 5dc10ff63ca2e8db91abfdccf1d095f5b4261b8e Mon Sep 17 00:00:00 2001 +From: Jakub Jelen +Date: Fri, 22 Dec 2023 09:52:18 +0100 +Subject: [PATCH 2/2] tests: Increase test coverage for IPv6 address parsing as + hostnames + +This was an issue in cockpit: + +https://github.com/cockpit-project/cockpit/issues/19772 + +Signed-off-by: Jakub Jelen +Reviewed-by: Andreas Schneider +(cherry picked from commit 6f6e453d7b0ad4ee6a6f6a1c96a9a6b27821410d) +--- + tests/unittests/torture_config.c | 49 +++++++++++++++++++++++++++++++ + tests/unittests/torture_options.c | 22 ++++++++++++++ + 2 files changed, 71 insertions(+) + +diff --git a/tests/unittests/torture_config.c b/tests/unittests/torture_config.c +index 3a5a74bf..d8097e79 100644 +--- a/tests/unittests/torture_config.c ++++ b/tests/unittests/torture_config.c +@@ -2,6 +2,7 @@ + + #define LIBSSH_STATIC + ++#include + #include "torture.h" + #include "libssh/options.h" + #include "libssh/session.h" +@@ -997,6 +998,53 @@ static void torture_config_match_pattern(void **state) + + } + ++static void torture_config_parse_uri(void **state) ++{ ++ char *username = NULL; ++ char *hostname = NULL; ++ char *port = NULL; ++ int rc; ++ ++ (void)state; /* unused */ ++ ++ rc = ssh_config_parse_uri("localhost", &username, &hostname, &port, false); ++ assert_return_code(rc, errno); ++ assert_null(username); ++ assert_string_equal(hostname, "localhost"); ++ SAFE_FREE(hostname); ++ assert_null(port); ++ ++ rc = ssh_config_parse_uri("1.2.3.4", &username, &hostname, &port, false); ++ assert_return_code(rc, errno); ++ assert_null(username); ++ assert_string_equal(hostname, "1.2.3.4"); ++ SAFE_FREE(hostname); ++ assert_null(port); ++ ++ rc = ssh_config_parse_uri("1.2.3.4:2222", &username, &hostname, &port, false); ++ assert_return_code(rc, errno); ++ assert_null(username); ++ assert_string_equal(hostname, "1.2.3.4"); ++ SAFE_FREE(hostname); ++ assert_string_equal(port, "2222"); ++ SAFE_FREE(port); ++ ++ rc = ssh_config_parse_uri("[1:2:3::4]:2222", &username, &hostname, &port, false); ++ assert_return_code(rc, errno); ++ assert_null(username); ++ assert_string_equal(hostname, "1:2:3::4"); ++ SAFE_FREE(hostname); ++ assert_string_equal(port, "2222"); ++ SAFE_FREE(port); ++ ++ /* do not want port */ ++ rc = ssh_config_parse_uri("1:2:3::4", &username, &hostname, NULL, true); ++ assert_return_code(rc, errno); ++ assert_null(username); ++ assert_string_equal(hostname, "1:2:3::4"); ++ SAFE_FREE(hostname); ++} ++ + + int torture_run_tests(void) { + int rc; +@@ -1012,6 +1060,7 @@ int torture_run_tests(void) { + cmocka_unit_test(torture_config_rekey), + cmocka_unit_test(torture_config_pubkeyacceptedkeytypes), + cmocka_unit_test(torture_config_match_pattern), ++ cmocka_unit_test(torture_config_parse_uri), + }; + + +diff --git a/tests/unittests/torture_options.c b/tests/unittests/torture_options.c +index d0fdaed1..576ca9cd 100644 +--- a/tests/unittests/torture_options.c ++++ b/tests/unittests/torture_options.c +@@ -59,12 +59,34 @@ static void torture_options_set_host(void **state) { + assert_non_null(session->opts.host); + assert_string_equal(session->opts.host, "localhost"); + ++ /* IPv4 address */ ++ rc = ssh_options_set(session, SSH_OPTIONS_HOST, "127.1.1.1"); ++ assert_true(rc == 0); ++ assert_non_null(session->opts.host); ++ assert_string_equal(session->opts.host, "127.1.1.1"); ++ assert_null(session->opts.username); ++ ++ /* IPv6 address */ ++ rc = ssh_options_set(session, SSH_OPTIONS_HOST, "::1"); ++ assert_true(rc == 0); ++ assert_non_null(session->opts.host); ++ assert_string_equal(session->opts.host, "::1"); ++ assert_null(session->opts.username); ++ + rc = ssh_options_set(session, SSH_OPTIONS_HOST, "guru@meditation"); + assert_true(rc == 0); + assert_non_null(session->opts.host); + assert_string_equal(session->opts.host, "meditation"); + assert_non_null(session->opts.username); + assert_string_equal(session->opts.username, "guru"); ++ ++ /* more @ in uri is OK -- it should go to the username */ ++ rc = ssh_options_set(session, SSH_OPTIONS_HOST, "at@login@hostname"); ++ assert_true(rc == 0); ++ assert_non_null(session->opts.host); ++ assert_string_equal(session->opts.host, "hostname"); ++ assert_non_null(session->opts.username); ++ assert_string_equal(session->opts.username, "at@login"); + } + + static void torture_options_set_ciphers(void **state) { +-- +2.43.0 + diff -Nru libssh-0.9.7/debian/patches/series libssh-0.9.8/debian/patches/series --- libssh-0.9.7/debian/patches/series 2023-05-21 18:22:05.000000000 +0000 +++ libssh-0.9.8/debian/patches/series 2023-12-25 10:40:33.000000000 +0000 @@ -1,3 +1,5 @@ +0001-Fix-regression-in-IPv6-addresses-in-hostname-parsing.patch +0002-tests-Increase-test-coverage-for-IPv6-address-parsin.patch 1003-custom-lib-names.patch 2003-disable-expand_tilde_unix-test.patch 2004-install-static-lib.patch diff -Nru libssh-0.9.7/include/libssh/kex.h libssh-0.9.8/include/libssh/kex.h --- libssh-0.9.7/include/libssh/kex.h 2023-05-04 11:40:37.000000000 +0000 +++ libssh-0.9.8/include/libssh/kex.h 2023-12-18 16:57:27.000000000 +0000 @@ -36,6 +36,7 @@ int ssh_send_kex(ssh_session session); void ssh_list_kex(struct ssh_kex_struct *kex); int ssh_set_client_kex(ssh_session session); +int ssh_kex_append_extensions(ssh_session session, struct ssh_kex_struct *pkex); int ssh_kex_select_methods(ssh_session session); int ssh_verify_existing_algo(enum ssh_kex_types_e algo, const char *name); char *ssh_keep_known_algos(enum ssh_kex_types_e algo, const char *list); diff -Nru libssh-0.9.7/include/libssh/libcrypto.h libssh-0.9.8/include/libssh/libcrypto.h --- libssh-0.9.7/include/libssh/libcrypto.h 2022-05-23 13:08:11.000000000 +0000 +++ libssh-0.9.8/include/libssh/libcrypto.h 2023-12-18 16:57:27.000000000 +0000 @@ -39,11 +39,6 @@ typedef EVP_MD_CTX* SHA512CTX; typedef EVP_MD_CTX* MD5CTX; typedef HMAC_CTX* HMACCTX; -#ifdef HAVE_ECC -typedef EVP_MD_CTX *EVPCTX; -#else -typedef void *EVPCTX; -#endif #define SHA_DIGEST_LEN SHA_DIGEST_LENGTH #define SHA256_DIGEST_LEN SHA256_DIGEST_LENGTH diff -Nru libssh-0.9.7/include/libssh/libgcrypt.h libssh-0.9.8/include/libssh/libgcrypt.h --- libssh-0.9.7/include/libssh/libgcrypt.h 2022-05-23 13:08:11.000000000 +0000 +++ libssh-0.9.8/include/libssh/libgcrypt.h 2023-12-18 16:57:27.000000000 +0000 @@ -32,7 +32,6 @@ typedef gcry_md_hd_t SHA512CTX; typedef gcry_md_hd_t MD5CTX; typedef gcry_md_hd_t HMACCTX; -typedef gcry_md_hd_t EVPCTX; #define SHA_DIGEST_LENGTH 20 #define SHA_DIGEST_LEN SHA_DIGEST_LENGTH #define MD5_DIGEST_LEN 16 diff -Nru libssh-0.9.7/include/libssh/libmbedcrypto.h libssh-0.9.8/include/libssh/libmbedcrypto.h --- libssh-0.9.7/include/libssh/libmbedcrypto.h 2022-05-23 13:08:11.000000000 +0000 +++ libssh-0.9.8/include/libssh/libmbedcrypto.h 2023-12-18 16:57:27.000000000 +0000 @@ -41,7 +41,6 @@ typedef mbedtls_md_context_t *SHA512CTX; typedef mbedtls_md_context_t *MD5CTX; typedef mbedtls_md_context_t *HMACCTX; -typedef mbedtls_md_context_t *EVPCTX; #define SHA_DIGEST_LENGTH 20 #define SHA_DIGEST_LEN SHA_DIGEST_LENGTH diff -Nru libssh-0.9.7/include/libssh/misc.h libssh-0.9.8/include/libssh/misc.h --- libssh-0.9.7/include/libssh/misc.h 2022-05-23 13:08:11.000000000 +0000 +++ libssh-0.9.8/include/libssh/misc.h 2023-12-18 16:57:27.000000000 +0000 @@ -97,4 +97,6 @@ int ssh_quote_file_name(const char *file_name, char *buf, size_t buf_len); int ssh_newline_vis(const char *string, char *buf, size_t buf_len); +int ssh_check_hostname_syntax(const char *hostname); + #endif /* MISC_H_ */ diff -Nru libssh-0.9.7/include/libssh/packet.h libssh-0.9.8/include/libssh/packet.h --- libssh-0.9.7/include/libssh/packet.h 2022-05-23 13:08:11.000000000 +0000 +++ libssh-0.9.8/include/libssh/packet.h 2023-12-18 16:57:27.000000000 +0000 @@ -63,6 +63,7 @@ SSH_PACKET_CALLBACK(ssh_packet_kexdh_init); #endif +int ssh_packet_send_newkeys(ssh_session session); int ssh_packet_send_unimplemented(ssh_session session, uint32_t seqnum); int ssh_packet_parse_type(ssh_session session); //int packet_flush(ssh_session session, int enforce_blocking); diff -Nru libssh-0.9.7/include/libssh/session.h libssh-0.9.8/include/libssh/session.h --- libssh-0.9.7/include/libssh/session.h 2023-05-04 11:40:48.000000000 +0000 +++ libssh-0.9.8/include/libssh/session.h 2023-12-18 16:57:27.000000000 +0000 @@ -80,6 +80,12 @@ * sending it twice during key exchange to simplify the state machine. */ #define SSH_SESSION_FLAG_KEXINIT_SENT 4 +/* The current SSH2 session implements the "strict KEX" feature and should behave + * differently on SSH2_MSG_NEWKEYS. */ +#define SSH_SESSION_FLAG_KEX_STRICT 0x0010 +/* Unexpected packets have been sent while the session was still unencrypted */ +#define SSH_SESSION_FLAG_KEX_TAINTED 0x0020 + /* codes to use with ssh_handle_packets*() */ /* Infinite timeout */ #define SSH_TIMEOUT_INFINITE -1 diff -Nru libssh-0.9.7/include/libssh/wrapper.h libssh-0.9.8/include/libssh/wrapper.h --- libssh-0.9.7/include/libssh/wrapper.h 2022-05-23 13:08:11.000000000 +0000 +++ libssh-0.9.8/include/libssh/wrapper.h 2023-12-18 16:57:27.000000000 +0000 @@ -67,37 +67,38 @@ typedef struct ssh_mac_ctx_struct *ssh_mac_ctx; MD5CTX md5_init(void); -void md5_update(MD5CTX c, const void *data, unsigned long len); -void md5_final(unsigned char *md,MD5CTX c); +void md5_ctx_free(MD5CTX); +int md5_update(MD5CTX c, const void *data, unsigned long len); +int md5_final(unsigned char *md,MD5CTX c); SHACTX sha1_init(void); -void sha1_update(SHACTX c, const void *data, unsigned long len); -void sha1_final(unsigned char *md,SHACTX c); -void sha1(const unsigned char *digest,int len,unsigned char *hash); +void sha1_ctx_free(SHACTX); +int sha1_update(SHACTX c, const void *data, unsigned long len); +int sha1_final(unsigned char *md,SHACTX c); +int sha1(const unsigned char *digest,int len,unsigned char *hash); SHA256CTX sha256_init(void); -void sha256_update(SHA256CTX c, const void *data, unsigned long len); -void sha256_final(unsigned char *md,SHA256CTX c); -void sha256(const unsigned char *digest, int len, unsigned char *hash); +void sha256_ctx_free(SHA256CTX); +int sha256_update(SHA256CTX c, const void *data, unsigned long len); +int sha256_final(unsigned char *md,SHA256CTX c); +int sha256(const unsigned char *digest, int len, unsigned char *hash); SHA384CTX sha384_init(void); -void sha384_update(SHA384CTX c, const void *data, unsigned long len); -void sha384_final(unsigned char *md,SHA384CTX c); -void sha384(const unsigned char *digest, int len, unsigned char *hash); +void sha384_ctx_free(SHA384CTX); +int sha384_update(SHA384CTX c, const void *data, unsigned long len); +int sha384_final(unsigned char *md,SHA384CTX c); +int sha384(const unsigned char *digest, int len, unsigned char *hash); SHA512CTX sha512_init(void); -void sha512_update(SHA512CTX c, const void *data, unsigned long len); -void sha512_final(unsigned char *md,SHA512CTX c); -void sha512(const unsigned char *digest, int len, unsigned char *hash); - -void evp(int nid, unsigned char *digest, int len, unsigned char *hash, unsigned int *hlen); -EVPCTX evp_init(int nid); -void evp_update(EVPCTX ctx, const void *data, unsigned long len); -void evp_final(EVPCTX ctx, unsigned char *md, unsigned int *mdlen); +void sha512_ctx_free(SHA512CTX); +int sha512_update(SHA512CTX c, const void *data, unsigned long len); +int sha512_final(unsigned char *md,SHA512CTX c); +int sha512(const unsigned char *digest, int len, unsigned char *hash); HMACCTX hmac_init(const void *key,int len, enum ssh_hmac_e type); void hmac_update(HMACCTX c, const void *data, unsigned long len); void hmac_final(HMACCTX ctx,unsigned char *hashmacbuf,unsigned int *len); + size_t hmac_digest_len(enum ssh_hmac_e type); int ssh_kdf(struct ssh_crypto_struct *crypto, diff -Nru libssh-0.9.7/src/ABI/current libssh-0.9.8/src/ABI/current --- libssh-0.9.7/src/ABI/current 2023-05-04 11:41:41.000000000 +0000 +++ libssh-0.9.8/src/ABI/current 2023-12-18 16:57:27.000000000 +0000 @@ -1 +1 @@ -4.8.8 \ No newline at end of file +4.8.9 \ No newline at end of file diff -Nru libssh-0.9.7/src/ABI/libssh-4.8.9.symbols libssh-0.9.8/src/ABI/libssh-4.8.9.symbols --- libssh-0.9.7/src/ABI/libssh-4.8.9.symbols 1970-01-01 00:00:00.000000000 +0000 +++ libssh-0.9.8/src/ABI/libssh-4.8.9.symbols 2023-12-18 16:57:27.000000000 +0000 @@ -0,0 +1,421 @@ +_ssh_log +buffer_free +buffer_get +buffer_get_len +buffer_new +channel_accept_x11 +channel_change_pty_size +channel_close +channel_forward_accept +channel_forward_cancel +channel_forward_listen +channel_free +channel_get_exit_status +channel_get_session +channel_is_closed +channel_is_eof +channel_is_open +channel_new +channel_open_forward +channel_open_session +channel_poll +channel_read +channel_read_buffer +channel_read_nonblocking +channel_request_env +channel_request_exec +channel_request_pty +channel_request_pty_size +channel_request_send_signal +channel_request_sftp +channel_request_shell +channel_request_subsystem +channel_request_x11 +channel_select +channel_send_eof +channel_set_blocking +channel_write +channel_write_stderr +privatekey_free +privatekey_from_file +publickey_free +publickey_from_file +publickey_from_privatekey +publickey_to_string +sftp_async_read +sftp_async_read_begin +sftp_attributes_free +sftp_canonicalize_path +sftp_chmod +sftp_chown +sftp_client_message_free +sftp_client_message_get_data +sftp_client_message_get_filename +sftp_client_message_get_flags +sftp_client_message_get_submessage +sftp_client_message_get_type +sftp_client_message_set_filename +sftp_close +sftp_closedir +sftp_dir_eof +sftp_extension_supported +sftp_extensions_get_count +sftp_extensions_get_data +sftp_extensions_get_name +sftp_file_set_blocking +sftp_file_set_nonblocking +sftp_free +sftp_fstat +sftp_fstatvfs +sftp_fsync +sftp_get_client_message +sftp_get_error +sftp_handle +sftp_handle_alloc +sftp_handle_remove +sftp_init +sftp_lstat +sftp_mkdir +sftp_new +sftp_new_channel +sftp_open +sftp_opendir +sftp_read +sftp_readdir +sftp_readlink +sftp_rename +sftp_reply_attr +sftp_reply_data +sftp_reply_handle +sftp_reply_name +sftp_reply_names +sftp_reply_names_add +sftp_reply_status +sftp_rewind +sftp_rmdir +sftp_seek +sftp_seek64 +sftp_send_client_message +sftp_server_free +sftp_server_init +sftp_server_new +sftp_server_version +sftp_setstat +sftp_stat +sftp_statvfs +sftp_statvfs_free +sftp_symlink +sftp_tell +sftp_tell64 +sftp_unlink +sftp_utimes +sftp_write +ssh_accept +ssh_add_channel_callbacks +ssh_auth_list +ssh_basename +ssh_bind_accept +ssh_bind_accept_fd +ssh_bind_fd_toaccept +ssh_bind_free +ssh_bind_get_fd +ssh_bind_listen +ssh_bind_new +ssh_bind_options_parse_config +ssh_bind_options_set +ssh_bind_set_blocking +ssh_bind_set_callbacks +ssh_bind_set_fd +ssh_blocking_flush +ssh_buffer_add_data +ssh_buffer_free +ssh_buffer_get +ssh_buffer_get_data +ssh_buffer_get_len +ssh_buffer_new +ssh_buffer_reinit +ssh_channel_accept_forward +ssh_channel_accept_x11 +ssh_channel_cancel_forward +ssh_channel_change_pty_size +ssh_channel_close +ssh_channel_free +ssh_channel_get_exit_status +ssh_channel_get_session +ssh_channel_is_closed +ssh_channel_is_eof +ssh_channel_is_open +ssh_channel_listen_forward +ssh_channel_new +ssh_channel_open_auth_agent +ssh_channel_open_forward +ssh_channel_open_forward_unix +ssh_channel_open_reverse_forward +ssh_channel_open_session +ssh_channel_open_x11 +ssh_channel_poll +ssh_channel_poll_timeout +ssh_channel_read +ssh_channel_read_nonblocking +ssh_channel_read_timeout +ssh_channel_request_auth_agent +ssh_channel_request_env +ssh_channel_request_exec +ssh_channel_request_pty +ssh_channel_request_pty_size +ssh_channel_request_send_break +ssh_channel_request_send_exit_signal +ssh_channel_request_send_exit_status +ssh_channel_request_send_signal +ssh_channel_request_sftp +ssh_channel_request_shell +ssh_channel_request_subsystem +ssh_channel_request_x11 +ssh_channel_select +ssh_channel_send_eof +ssh_channel_set_blocking +ssh_channel_set_counter +ssh_channel_window_size +ssh_channel_write +ssh_channel_write_stderr +ssh_clean_pubkey_hash +ssh_connect +ssh_connector_free +ssh_connector_new +ssh_connector_set_in_channel +ssh_connector_set_in_fd +ssh_connector_set_out_channel +ssh_connector_set_out_fd +ssh_copyright +ssh_dirname +ssh_disconnect +ssh_dump_knownhost +ssh_event_add_connector +ssh_event_add_fd +ssh_event_add_session +ssh_event_dopoll +ssh_event_free +ssh_event_new +ssh_event_remove_connector +ssh_event_remove_fd +ssh_event_remove_session +ssh_execute_message_callbacks +ssh_finalize +ssh_forward_accept +ssh_forward_cancel +ssh_forward_listen +ssh_free +ssh_get_cipher_in +ssh_get_cipher_out +ssh_get_clientbanner +ssh_get_disconnect_message +ssh_get_error +ssh_get_error_code +ssh_get_fd +ssh_get_fingerprint_hash +ssh_get_hexa +ssh_get_hmac_in +ssh_get_hmac_out +ssh_get_issue_banner +ssh_get_kex_algo +ssh_get_log_callback +ssh_get_log_level +ssh_get_log_userdata +ssh_get_openssh_version +ssh_get_poll_flags +ssh_get_pubkey +ssh_get_pubkey_hash +ssh_get_publickey +ssh_get_publickey_hash +ssh_get_random +ssh_get_server_publickey +ssh_get_serverbanner +ssh_get_status +ssh_get_version +ssh_getpass +ssh_gssapi_get_creds +ssh_gssapi_set_creds +ssh_handle_key_exchange +ssh_init +ssh_is_blocking +ssh_is_connected +ssh_is_server_known +ssh_key_cmp +ssh_key_free +ssh_key_is_private +ssh_key_is_public +ssh_key_new +ssh_key_type +ssh_key_type_from_name +ssh_key_type_to_char +ssh_known_hosts_parse_line +ssh_knownhosts_entry_free +ssh_log +ssh_message_auth_interactive_request +ssh_message_auth_kbdint_is_response +ssh_message_auth_password +ssh_message_auth_pubkey +ssh_message_auth_publickey +ssh_message_auth_publickey_state +ssh_message_auth_reply_pk_ok +ssh_message_auth_reply_pk_ok_simple +ssh_message_auth_reply_success +ssh_message_auth_set_methods +ssh_message_auth_user +ssh_message_channel_request_channel +ssh_message_channel_request_command +ssh_message_channel_request_env_name +ssh_message_channel_request_env_value +ssh_message_channel_request_open_destination +ssh_message_channel_request_open_destination_port +ssh_message_channel_request_open_originator +ssh_message_channel_request_open_originator_port +ssh_message_channel_request_open_reply_accept +ssh_message_channel_request_open_reply_accept_channel +ssh_message_channel_request_pty_height +ssh_message_channel_request_pty_pxheight +ssh_message_channel_request_pty_pxwidth +ssh_message_channel_request_pty_term +ssh_message_channel_request_pty_width +ssh_message_channel_request_reply_success +ssh_message_channel_request_subsystem +ssh_message_channel_request_x11_auth_cookie +ssh_message_channel_request_x11_auth_protocol +ssh_message_channel_request_x11_screen_number +ssh_message_channel_request_x11_single_connection +ssh_message_free +ssh_message_get +ssh_message_global_request_address +ssh_message_global_request_port +ssh_message_global_request_reply_success +ssh_message_reply_default +ssh_message_retrieve +ssh_message_service_reply_success +ssh_message_service_service +ssh_message_subtype +ssh_message_type +ssh_mkdir +ssh_new +ssh_options_copy +ssh_options_get +ssh_options_get_port +ssh_options_getopt +ssh_options_parse_config +ssh_options_set +ssh_pcap_file_close +ssh_pcap_file_free +ssh_pcap_file_new +ssh_pcap_file_open +ssh_pki_copy_cert_to_privkey +ssh_pki_export_privkey_base64 +ssh_pki_export_privkey_file +ssh_pki_export_privkey_to_pubkey +ssh_pki_export_pubkey_base64 +ssh_pki_export_pubkey_file +ssh_pki_generate +ssh_pki_import_cert_base64 +ssh_pki_import_cert_file +ssh_pki_import_privkey_base64 +ssh_pki_import_privkey_file +ssh_pki_import_pubkey_base64 +ssh_pki_import_pubkey_file +ssh_pki_key_ecdsa_name +ssh_print_hash +ssh_print_hexa +ssh_privatekey_type +ssh_publickey_to_file +ssh_remove_channel_callbacks +ssh_scp_accept_request +ssh_scp_close +ssh_scp_deny_request +ssh_scp_free +ssh_scp_init +ssh_scp_leave_directory +ssh_scp_new +ssh_scp_pull_request +ssh_scp_push_directory +ssh_scp_push_file +ssh_scp_push_file64 +ssh_scp_read +ssh_scp_request_get_filename +ssh_scp_request_get_permissions +ssh_scp_request_get_size +ssh_scp_request_get_size64 +ssh_scp_request_get_warning +ssh_scp_write +ssh_select +ssh_send_debug +ssh_send_ignore +ssh_send_keepalive +ssh_server_init_kex +ssh_service_request +ssh_session_export_known_hosts_entry +ssh_session_get_known_hosts_entry +ssh_session_has_known_hosts_entry +ssh_session_is_known_server +ssh_session_update_known_hosts +ssh_set_agent_channel +ssh_set_agent_socket +ssh_set_auth_methods +ssh_set_blocking +ssh_set_callbacks +ssh_set_channel_callbacks +ssh_set_counters +ssh_set_fd_except +ssh_set_fd_toread +ssh_set_fd_towrite +ssh_set_log_callback +ssh_set_log_level +ssh_set_log_userdata +ssh_set_message_callback +ssh_set_pcap_file +ssh_set_server_callbacks +ssh_silent_disconnect +ssh_string_burn +ssh_string_copy +ssh_string_data +ssh_string_fill +ssh_string_free +ssh_string_free_char +ssh_string_from_char +ssh_string_get_char +ssh_string_len +ssh_string_new +ssh_string_to_char +ssh_threads_get_default +ssh_threads_get_noop +ssh_threads_get_pthread +ssh_threads_set_callbacks +ssh_try_publickey_from_file +ssh_userauth_agent +ssh_userauth_agent_pubkey +ssh_userauth_autopubkey +ssh_userauth_gssapi +ssh_userauth_kbdint +ssh_userauth_kbdint_getanswer +ssh_userauth_kbdint_getinstruction +ssh_userauth_kbdint_getname +ssh_userauth_kbdint_getnanswers +ssh_userauth_kbdint_getnprompts +ssh_userauth_kbdint_getprompt +ssh_userauth_kbdint_setanswer +ssh_userauth_list +ssh_userauth_none +ssh_userauth_offer_pubkey +ssh_userauth_password +ssh_userauth_privatekey_file +ssh_userauth_pubkey +ssh_userauth_publickey +ssh_userauth_publickey_auto +ssh_userauth_try_publickey +ssh_version +ssh_write_knownhost +string_burn +string_copy +string_data +string_fill +string_free +string_from_char +string_len +string_new +string_to_char \ No newline at end of file diff -Nru libssh-0.9.7/src/CMakeLists.txt libssh-0.9.8/src/CMakeLists.txt --- libssh-0.9.7/src/CMakeLists.txt 2022-05-23 13:08:11.000000000 +0000 +++ libssh-0.9.8/src/CMakeLists.txt 2023-12-18 16:57:27.000000000 +0000 @@ -9,13 +9,6 @@ ${LIBSSH_REQUIRED_LIBRARIES} ) -if (WIN32) - set(LIBSSH_LINK_LIBRARIES - ${LIBSSH_LINK_LIBRARIES} - ws2_32 - ) -endif (WIN32) - if (OPENSSL_CRYPTO_LIBRARIES) set(LIBSSH_PRIVATE_INCLUDE_DIRS ${LIBSSH_PRIVATE_INCLUDE_DIRS} @@ -93,6 +86,16 @@ ) endif() +# This needs to be last for mingw to build +# https://gitlab.com/libssh/libssh-mirror/-/issues/84 +if (WIN32) + set(LIBSSH_LINK_LIBRARIES + ${LIBSSH_LINK_LIBRARIES} + iphlpapi + ws2_32 + ) +endif (WIN32) + if (BUILD_STATIC_LIB) set(LIBSSH_STATIC_LIBRARY ssh_static diff -Nru libssh-0.9.7/src/channels.c libssh-0.9.8/src/channels.c --- libssh-0.9.7/src/channels.c 2022-05-23 13:08:11.000000000 +0000 +++ libssh-0.9.8/src/channels.c 2023-12-18 16:57:22.000000000 +0000 @@ -3403,9 +3403,15 @@ firstround=0; } while(1); - memcpy(readchans, rchans, (count_ptrs(rchans) + 1) * sizeof(ssh_channel )); - memcpy(writechans, wchans, (count_ptrs(wchans) + 1) * sizeof(ssh_channel )); - memcpy(exceptchans, echans, (count_ptrs(echans) + 1) * sizeof(ssh_channel )); + if (readchans != &dummy) { + memcpy(readchans, rchans, (count_ptrs(rchans) + 1) * sizeof(ssh_channel)); + } + if (writechans != &dummy) { + memcpy(writechans, wchans, (count_ptrs(wchans) + 1) * sizeof(ssh_channel)); + } + if (exceptchans != &dummy) { + memcpy(exceptchans, echans, (count_ptrs(echans) + 1) * sizeof(ssh_channel)); + } SAFE_FREE(rchans); SAFE_FREE(wchans); SAFE_FREE(echans); diff -Nru libssh-0.9.7/src/config_parser.c libssh-0.9.8/src/config_parser.c --- libssh-0.9.7/src/config_parser.c 2022-05-23 13:08:11.000000000 +0000 +++ libssh-0.9.8/src/config_parser.c 2023-12-18 16:57:27.000000000 +0000 @@ -30,6 +30,7 @@ #include "libssh/config_parser.h" #include "libssh/priv.h" +#include "libssh/misc.h" char *ssh_config_get_cmd(char **str) { @@ -139,6 +140,7 @@ { char *endp = NULL; long port_n; + int rc; /* Sanitize inputs */ if (username != NULL) { @@ -152,7 +154,7 @@ } /* Username part (optional) */ - endp = strchr(tok, '@'); + endp = strrchr(tok, '@'); if (endp != NULL) { /* Zero-length username is not valid */ if (tok == endp) { @@ -196,6 +198,14 @@ if (*hostname == NULL) { goto error; } + /* if not an ip, check syntax */ + rc = ssh_is_ipaddr(*hostname); + if (rc == 0) { + rc = ssh_check_hostname_syntax(*hostname); + if (rc != SSH_OK) { + goto error; + } + } } /* Skip also the closing bracket */ if (*endp == ']') { diff -Nru libssh-0.9.7/src/connect.c libssh-0.9.8/src/connect.c --- libssh-0.9.7/src/connect.c 2022-05-23 13:08:11.000000000 +0000 +++ libssh-0.9.8/src/connect.c 2023-12-18 16:57:27.000000000 +0000 @@ -136,7 +136,7 @@ #endif } - if (ssh_is_ipaddr(host)) { + if (ssh_is_ipaddr(host) == 1) { /* this is an IP address */ SSH_LOG(SSH_LOG_PACKET, "host %s matches an IP address", host); hints.ai_flags |= AI_NUMERICHOST; diff -Nru libssh-0.9.7/src/curve25519.c libssh-0.9.8/src/curve25519.c --- libssh-0.9.7/src/curve25519.c 2023-05-04 11:40:41.000000000 +0000 +++ libssh-0.9.8/src/curve25519.c 2023-12-18 16:57:27.000000000 +0000 @@ -335,16 +335,10 @@ } /* Send the MSG_NEWKEYS */ - if (ssh_buffer_add_u8(session->out_buffer, SSH2_MSG_NEWKEYS) < 0) { - goto error; - } - - rc=ssh_packet_send(session); + rc = ssh_packet_send_newkeys(session); if (rc == SSH_ERROR) { goto error; } - - SSH_LOG(SSH_LOG_PROTOCOL, "SSH_MSG_NEWKEYS sent"); session->dh_handshake_state = DH_STATE_NEWKEYS_SENT; return SSH_PACKET_USED; @@ -502,18 +496,13 @@ return SSH_ERROR; } - /* Send the MSG_NEWKEYS */ - rc = ssh_buffer_add_u8(session->out_buffer, SSH2_MSG_NEWKEYS); - if (rc < 0) { - goto error; - } - session->dh_handshake_state = DH_STATE_NEWKEYS_SENT; - rc = ssh_packet_send(session); + + /* Send the MSG_NEWKEYS */ + rc = ssh_packet_send_newkeys(session); if (rc == SSH_ERROR) { goto error; } - SSH_LOG(SSH_LOG_PROTOCOL, "SSH_MSG_NEWKEYS sent"); return SSH_PACKET_USED; error: diff -Nru libssh-0.9.7/src/dh-gex.c libssh-0.9.8/src/dh-gex.c --- libssh-0.9.7/src/dh-gex.c 2023-05-04 11:40:41.000000000 +0000 +++ libssh-0.9.8/src/dh-gex.c 2023-12-18 16:57:27.000000000 +0000 @@ -287,15 +287,10 @@ } /* Send the MSG_NEWKEYS */ - if (ssh_buffer_add_u8(session->out_buffer, SSH2_MSG_NEWKEYS) < 0) { - goto error; - } - - rc = ssh_packet_send(session); + rc = ssh_packet_send_newkeys(session); if (rc == SSH_ERROR) { goto error; } - SSH_LOG(SSH_LOG_PROTOCOL, "SSH_MSG_NEWKEYS sent"); session->dh_handshake_state = DH_STATE_NEWKEYS_SENT; return SSH_PACKET_USED; diff -Nru libssh-0.9.7/src/dh.c libssh-0.9.8/src/dh.c --- libssh-0.9.7/src/dh.c 2023-05-04 11:40:41.000000000 +0000 +++ libssh-0.9.8/src/dh.c 2023-12-18 16:57:27.000000000 +0000 @@ -386,16 +386,10 @@ } /* Send the MSG_NEWKEYS */ - if (ssh_buffer_add_u8(session->out_buffer, SSH2_MSG_NEWKEYS) < 0) { - goto error; - } - - rc=ssh_packet_send(session); + rc = ssh_packet_send_newkeys(session); if (rc == SSH_ERROR) { goto error; } - - SSH_LOG(SSH_LOG_PROTOCOL, "SSH_MSG_NEWKEYS sent"); session->dh_handshake_state = DH_STATE_NEWKEYS_SENT; return SSH_PACKET_USED; error: @@ -532,15 +526,12 @@ } SSH_LOG(SSH_LOG_DEBUG, "Sent KEX_DH_[GEX]_REPLY"); - if (ssh_buffer_add_u8(session->out_buffer, SSH2_MSG_NEWKEYS) < 0) { - ssh_buffer_reinit(session->out_buffer); - goto error; - } session->dh_handshake_state=DH_STATE_NEWKEYS_SENT; - if (ssh_packet_send(session) == SSH_ERROR) { + /* Send the MSG_NEWKEYS */ + rc = ssh_packet_send_newkeys(session); + if (rc == SSH_ERROR) { goto error; } - SSH_LOG(SSH_LOG_PACKET, "SSH_MSG_NEWKEYS sent"); return SSH_OK; error: diff -Nru libssh-0.9.7/src/ecdh.c libssh-0.9.8/src/ecdh.c --- libssh-0.9.7/src/ecdh.c 2023-05-04 11:40:41.000000000 +0000 +++ libssh-0.9.8/src/ecdh.c 2023-12-18 16:57:27.000000000 +0000 @@ -93,16 +93,10 @@ } /* Send the MSG_NEWKEYS */ - if (ssh_buffer_add_u8(session->out_buffer, SSH2_MSG_NEWKEYS) < 0) { - goto error; - } - - rc=ssh_packet_send(session); + rc = ssh_packet_send_newkeys(session); if (rc == SSH_ERROR) { goto error; } - - SSH_LOG(SSH_LOG_PROTOCOL, "SSH_MSG_NEWKEYS sent"); session->dh_handshake_state = DH_STATE_NEWKEYS_SENT; return SSH_PACKET_USED; diff -Nru libssh-0.9.7/src/ecdh_crypto.c libssh-0.9.8/src/ecdh_crypto.c --- libssh-0.9.7/src/ecdh_crypto.c 2022-05-23 13:08:11.000000000 +0000 +++ libssh-0.9.8/src/ecdh_crypto.c 2023-12-18 16:57:27.000000000 +0000 @@ -323,18 +323,12 @@ goto error; } - /* Send the MSG_NEWKEYS */ - rc = ssh_buffer_add_u8(session->out_buffer, SSH2_MSG_NEWKEYS); - if (rc < 0) { - goto error; - } - session->dh_handshake_state = DH_STATE_NEWKEYS_SENT; - rc = ssh_packet_send(session); - if (rc == SSH_ERROR){ + /* Send the MSG_NEWKEYS */ + rc = ssh_packet_send_newkeys(session); + if (rc == SSH_ERROR) { goto error; } - SSH_LOG(SSH_LOG_PROTOCOL, "SSH_MSG_NEWKEYS sent"); return SSH_PACKET_USED; error: diff -Nru libssh-0.9.7/src/ecdh_gcrypt.c libssh-0.9.8/src/ecdh_gcrypt.c --- libssh-0.9.7/src/ecdh_gcrypt.c 2022-05-23 13:08:11.000000000 +0000 +++ libssh-0.9.8/src/ecdh_gcrypt.c 2023-12-18 16:57:27.000000000 +0000 @@ -372,17 +372,13 @@ goto out; } - + session->dh_handshake_state = DH_STATE_NEWKEYS_SENT; /* Send the MSG_NEWKEYS */ - rc = ssh_buffer_add_u8(session->out_buffer, SSH2_MSG_NEWKEYS); - if (rc != SSH_OK) { + rc = ssh_packet_send_newkeys(session); + if (rc == SSH_ERROR) { goto out; } - session->dh_handshake_state = DH_STATE_NEWKEYS_SENT; - rc = ssh_packet_send(session); - SSH_LOG(SSH_LOG_PROTOCOL, "SSH_MSG_NEWKEYS sent"); - out: gcry_sexp_release(param); gcry_sexp_release(key); diff -Nru libssh-0.9.7/src/ecdh_mbedcrypto.c libssh-0.9.8/src/ecdh_mbedcrypto.c --- libssh-0.9.7/src/ecdh_mbedcrypto.c 2022-05-23 13:08:11.000000000 +0000 +++ libssh-0.9.8/src/ecdh_mbedcrypto.c 2023-12-18 16:57:27.000000000 +0000 @@ -300,16 +300,13 @@ goto out; } - rc = ssh_buffer_add_u8(session->out_buffer, SSH2_MSG_NEWKEYS); - if (rc < 0) { - rc = SSH_ERROR; + session->dh_handshake_state = DH_STATE_NEWKEYS_SENT; + /* Send the MSG_NEWKEYS */ + rc = ssh_packet_send_newkeys(session); + if (rc == SSH_ERROR) { goto out; } - session->dh_handshake_state = DH_STATE_NEWKEYS_SENT; - rc = ssh_packet_send(session); - SSH_LOG(SSH_LOG_PROTOCOL, "SSH_MSG_NEWKEYS sent"); - out: mbedtls_ecp_group_free(&grp); if (rc == SSH_ERROR) { diff -Nru libssh-0.9.7/src/kdf.c libssh-0.9.8/src/kdf.c --- libssh-0.9.7/src/kdf.c 2022-05-23 13:08:11.000000000 +0000 +++ libssh-0.9.8/src/kdf.c 2023-12-18 16:57:27.000000000 +0000 @@ -58,65 +58,102 @@ } ctx->digest_type = type; - switch(type){ + switch (type) { case SSH_KDF_SHA1: ctx->ctx.sha1_ctx = sha1_init(); + if (ctx->ctx.sha1_ctx == NULL) { + goto err; + } return ctx; case SSH_KDF_SHA256: ctx->ctx.sha256_ctx = sha256_init(); + if (ctx->ctx.sha256_ctx == NULL) { + goto err; + } return ctx; case SSH_KDF_SHA384: ctx->ctx.sha384_ctx = sha384_init(); + if (ctx->ctx.sha384_ctx == NULL) { + goto err; + } return ctx; case SSH_KDF_SHA512: ctx->ctx.sha512_ctx = sha512_init(); + if (ctx->ctx.sha512_ctx == NULL) { + goto err; + } return ctx; - default: - SAFE_FREE(ctx); - return NULL; } +err: + SAFE_FREE(ctx); + return NULL; } -static void ssh_mac_update(ssh_mac_ctx ctx, const void *data, size_t len) +static void ssh_mac_ctx_free(ssh_mac_ctx ctx) { - switch(ctx->digest_type){ + if (ctx == NULL) { + return; + } + + switch (ctx->digest_type) { case SSH_KDF_SHA1: - sha1_update(ctx->ctx.sha1_ctx, data, len); + sha1_ctx_free(ctx->ctx.sha1_ctx); break; case SSH_KDF_SHA256: - sha256_update(ctx->ctx.sha256_ctx, data, len); + sha256_ctx_free(ctx->ctx.sha256_ctx); break; case SSH_KDF_SHA384: - sha384_update(ctx->ctx.sha384_ctx, data, len); + sha384_ctx_free(ctx->ctx.sha384_ctx); break; case SSH_KDF_SHA512: - sha512_update(ctx->ctx.sha512_ctx, data, len); + sha512_ctx_free(ctx->ctx.sha512_ctx); break; } + SAFE_FREE(ctx); +} + +static int ssh_mac_update(ssh_mac_ctx ctx, const void *data, size_t len) +{ + switch (ctx->digest_type) { + case SSH_KDF_SHA1: + return sha1_update(ctx->ctx.sha1_ctx, data, len); + case SSH_KDF_SHA256: + return sha256_update(ctx->ctx.sha256_ctx, data, len); + case SSH_KDF_SHA384: + return sha384_update(ctx->ctx.sha384_ctx, data, len); + case SSH_KDF_SHA512: + return sha512_update(ctx->ctx.sha512_ctx, data, len); + } + return SSH_ERROR; } -static void ssh_mac_final(unsigned char *md, ssh_mac_ctx ctx) +static int ssh_mac_final(unsigned char *md, ssh_mac_ctx ctx) { - switch(ctx->digest_type){ + int rc = SSH_ERROR; + + switch (ctx->digest_type) { case SSH_KDF_SHA1: - sha1_final(md,ctx->ctx.sha1_ctx); + rc = sha1_final(md, ctx->ctx.sha1_ctx); break; case SSH_KDF_SHA256: - sha256_final(md,ctx->ctx.sha256_ctx); + rc = sha256_final(md, ctx->ctx.sha256_ctx); break; case SSH_KDF_SHA384: - sha384_final(md,ctx->ctx.sha384_ctx); + rc = sha384_final(md, ctx->ctx.sha384_ctx); break; case SSH_KDF_SHA512: - sha512_final(md,ctx->ctx.sha512_ctx); + rc = sha512_final(md, ctx->ctx.sha512_ctx); break; } SAFE_FREE(ctx); + return rc; } int sshkdf_derive_key(struct ssh_crypto_struct *crypto, - unsigned char *key, size_t key_len, - int key_type, unsigned char *output, + unsigned char *key, + size_t key_len, + int key_type, + unsigned char *output, size_t requested_len) { /* Can't use VLAs with Visual Studio, so allocate the biggest @@ -125,6 +162,7 @@ size_t output_len = crypto->digest_len; char letter = key_type; ssh_mac_ctx ctx; + int rc; if (DIGEST_MAX_LEN < crypto->digest_len) { return -1; @@ -135,11 +173,30 @@ return -1; } - ssh_mac_update(ctx, key, key_len); - ssh_mac_update(ctx, crypto->secret_hash, crypto->digest_len); - ssh_mac_update(ctx, &letter, 1); - ssh_mac_update(ctx, crypto->session_id, crypto->session_id_len); - ssh_mac_final(digest, ctx); + rc = ssh_mac_update(ctx, key, key_len); + if (rc != SSH_OK) { + ssh_mac_ctx_free(ctx); + return -1; + } + rc = ssh_mac_update(ctx, crypto->secret_hash, crypto->digest_len); + if (rc != SSH_OK) { + ssh_mac_ctx_free(ctx); + return -1; + } + rc = ssh_mac_update(ctx, &letter, 1); + if (rc != SSH_OK) { + ssh_mac_ctx_free(ctx); + return -1; + } + rc = ssh_mac_update(ctx, crypto->session_id, crypto->session_id_len); + if (rc != SSH_OK) { + ssh_mac_ctx_free(ctx); + return -1; + } + rc = ssh_mac_final(digest, ctx); + if (rc != SSH_OK) { + return -1; + } if (requested_len < output_len) { output_len = requested_len; @@ -151,10 +208,25 @@ if (ctx == NULL) { return -1; } - ssh_mac_update(ctx, key, key_len); - ssh_mac_update(ctx, crypto->secret_hash, crypto->digest_len); - ssh_mac_update(ctx, output, output_len); - ssh_mac_final(digest, ctx); + rc = ssh_mac_update(ctx, key, key_len); + if (rc != SSH_OK) { + ssh_mac_ctx_free(ctx); + return -1; + } + rc = ssh_mac_update(ctx, crypto->secret_hash, crypto->digest_len); + if (rc != SSH_OK) { + ssh_mac_ctx_free(ctx); + return -1; + } + rc = ssh_mac_update(ctx, output, output_len); + if (rc != SSH_OK) { + ssh_mac_ctx_free(ctx); + return -1; + } + rc = ssh_mac_final(digest, ctx); + if (rc != SSH_OK) { + return -1; + } if (requested_len < output_len + crypto->digest_len) { memcpy(output + output_len, digest, requested_len - output_len); } else { diff -Nru libssh-0.9.7/src/kex.c libssh-0.9.8/src/kex.c --- libssh-0.9.7/src/kex.c 2023-05-04 11:40:48.000000000 +0000 +++ libssh-0.9.8/src/kex.c 2023-12-18 16:57:27.000000000 +0000 @@ -163,6 +163,9 @@ /* RFC 8308 */ #define KEX_EXTENSION_CLIENT "ext-info-c" +/* Strict kex mitigation against CVE-2023-48795 */ +#define KEX_STRICT_CLIENT "kex-strict-c-v00@openssh.com" +#define KEX_STRICT_SERVER "kex-strict-s-v00@openssh.com" /* Allowed algorithms in FIPS mode */ #define FIPS_ALLOWED_CIPHERS "aes256-gcm@openssh.com,"\ @@ -491,6 +494,27 @@ session->first_kex_follows_guess_wrong ? "wrong" : "right"); } + /* + * handle the "strict KEX" feature. If supported by peer, then set up the + * flag and verify packet sequence numbers. + */ + if (server_kex) { + ok = ssh_match_group(crypto->client_kex.methods[SSH_KEX], + KEX_STRICT_CLIENT); + if (ok) { + SSH_LOG(SSH_LOG_DEBUG, "Client supports strict kex, enabling."); + session->flags |= SSH_SESSION_FLAG_KEX_STRICT; + } + } else { + /* client kex */ + ok = ssh_match_group(crypto->server_kex.methods[SSH_KEX], + KEX_STRICT_SERVER); + if (ok) { + SSH_LOG(SSH_LOG_DEBUG, "Server supports strict kex, enabling."); + session->flags |= SSH_SESSION_FLAG_KEX_STRICT; + } + } + if (server_kex) { /* * If client sent a ext-info-c message in the kex list, it supports @@ -714,11 +738,8 @@ { struct ssh_kex_struct *client = &session->next_crypto->client_kex; const char *wanted; - char *kex = NULL; - char *kex_tmp = NULL; int ok; int i; - size_t kex_len, len; /* Skip if already set, for example for the rekey or when we do the guessing * it could have been already used to make some protocol decisions. */ @@ -767,23 +788,52 @@ return SSH_OK; } - /* Here we append ext-info-c to the list of kex algorithms */ - kex = client->methods[SSH_KEX]; + ok = ssh_kex_append_extensions(session, client); + if (ok != SSH_OK){ + return ok; + } + + return SSH_OK; +} + +int ssh_kex_append_extensions(ssh_session session, struct ssh_kex_struct *pkex) +{ + char *kex = NULL; + char *kex_tmp = NULL; + size_t kex_len, len; + + /* Here we append ext-info-c and kex-strict-c-v00@openssh.com for client + * and kex-strict-s-v00@openssh.com for server to the list of kex algorithms + */ + kex = pkex->methods[SSH_KEX]; len = strlen(kex); - if (len + strlen(KEX_EXTENSION_CLIENT) + 2 < len) { + if (session->server) { + /* Comma, nul byte */ + kex_len = len + 1 + strlen(KEX_STRICT_SERVER) + 1; + } else { + /* Comma, comma, nul byte */ + kex_len = len + 1 + strlen(KEX_EXTENSION_CLIENT) + 1 + + strlen(KEX_STRICT_CLIENT) + 1; + } + if (kex_len >= MAX_PACKET_LEN) { /* Overflow */ return SSH_ERROR; } - kex_len = len + strlen(KEX_EXTENSION_CLIENT) + 2; /* comma, NULL */ kex_tmp = realloc(kex, kex_len); if (kex_tmp == NULL) { - free(kex); ssh_set_error_oom(session); return SSH_ERROR; } - snprintf(kex_tmp + len, kex_len - len, ",%s", KEX_EXTENSION_CLIENT); - client->methods[SSH_KEX] = kex_tmp; - + if (session->server){ + snprintf(kex_tmp + len, kex_len - len, ",%s", KEX_STRICT_SERVER); + } else { + snprintf(kex_tmp + len, + kex_len - len, + ",%s,%s", + KEX_EXTENSION_CLIENT, + KEX_STRICT_CLIENT); + } + pkex->methods[SSH_KEX] = kex_tmp; return SSH_OK; } @@ -886,11 +936,19 @@ enum ssh_key_exchange_e kex_type; int i; - /* Here we should drop the ext-info-c from the list so we avoid matching. + /* Here we should drop the extensions from the list so we avoid matching. * it. We added it to the end, so we can just truncate the string here */ - ext_start = strstr(client->methods[SSH_KEX], ","KEX_EXTENSION_CLIENT); - if (ext_start != NULL) { - ext_start[0] = '\0'; + if (session->client) { + ext_start = strstr(client->methods[SSH_KEX], "," KEX_EXTENSION_CLIENT); + if (ext_start != NULL) { + ext_start[0] = '\0'; + } + } + if (session->server) { + ext_start = strstr(server->methods[SSH_KEX], "," KEX_STRICT_SERVER); + if (ext_start != NULL) { + ext_start[0] = '\0'; + } } for (i = 0; i < SSH_KEX_METHODS; i++) { diff -Nru libssh-0.9.7/src/libcrypto.c libssh-0.9.8/src/libcrypto.c --- libssh-0.9.7/src/libcrypto.c 2022-05-23 13:08:11.000000000 +0000 +++ libssh-0.9.8/src/libcrypto.c 2023-12-18 16:57:27.000000000 +0000 @@ -126,81 +126,47 @@ return c; } -void sha1_update(SHACTX c, const void *data, unsigned long len) +void sha1_ctx_free(SHACTX c) { - EVP_DigestUpdate(c, data, len); -} - -void sha1_final(unsigned char *md, SHACTX c) -{ - unsigned int mdlen = 0; - - EVP_DigestFinal(c, md, &mdlen); EVP_MD_CTX_destroy(c); } -void sha1(const unsigned char *digest, int len, unsigned char *hash) -{ - SHACTX c = sha1_init(); - if (c != NULL) { - sha1_update(c, digest, len); - sha1_final(hash, c); - } -} - -#ifdef HAVE_OPENSSL_ECC -static const EVP_MD *nid_to_evpmd(int nid) +int sha1_update(SHACTX c, const void *data, unsigned long len) { - switch (nid) { - case NID_X9_62_prime256v1: - return EVP_sha256(); - case NID_secp384r1: - return EVP_sha384(); - case NID_secp521r1: - return EVP_sha512(); - default: - return NULL; + int rc = EVP_DigestUpdate(c, data, len); + if (rc != 1) { + return SSH_ERROR; } - - return NULL; -} - -void evp(int nid, unsigned char *digest, int len, unsigned char *hash, unsigned int *hlen) -{ - const EVP_MD *evp_md = nid_to_evpmd(nid); - EVP_MD_CTX *md = EVP_MD_CTX_new(); - - EVP_DigestInit(md, evp_md); - EVP_DigestUpdate(md, digest, len); - EVP_DigestFinal(md, hash, hlen); - EVP_MD_CTX_free(md); + return SSH_OK; } -EVPCTX evp_init(int nid) +int sha1_final(unsigned char *md, SHACTX c) { - const EVP_MD *evp_md = nid_to_evpmd(nid); + unsigned int mdlen = 0; + int rc = EVP_DigestFinal(c, md, &mdlen); - EVPCTX ctx = EVP_MD_CTX_new(); - if (ctx == NULL) { - return NULL; + EVP_MD_CTX_destroy(c); + if (rc != 1) { + return SSH_ERROR; } - - EVP_DigestInit(ctx, evp_md); - - return ctx; + return SSH_OK; } -void evp_update(EVPCTX ctx, const void *data, unsigned long len) +int sha1(const unsigned char *digest, int len, unsigned char *hash) { - EVP_DigestUpdate(ctx, data, len); -} + SHACTX c = sha1_init(); + int rc; -void evp_final(EVPCTX ctx, unsigned char *md, unsigned int *mdlen) -{ - EVP_DigestFinal(ctx, md, mdlen); - EVP_MD_CTX_free(ctx); + if (c == NULL) { + return SSH_ERROR; + } + rc = sha1_update(c, digest, len); + if (rc != SSH_OK) { + sha1_ctx_free(c); + return SSH_ERROR; + } + return sha1_final(hash, c); } -#endif SHA256CTX sha256_init(void) { @@ -218,26 +184,46 @@ return c; } -void sha256_update(SHA256CTX c, const void *data, unsigned long len) +void sha256_ctx_free(SHA256CTX c) +{ + EVP_MD_CTX_destroy(c); +} + +int sha256_update(SHA256CTX c, const void *data, unsigned long len) { - EVP_DigestUpdate(c, data, len); + int rc = EVP_DigestUpdate(c, data, len); + if (rc != 1) { + return SSH_ERROR; + } + return SSH_OK; } -void sha256_final(unsigned char *md, SHA256CTX c) +int sha256_final(unsigned char *md, SHA256CTX c) { unsigned int mdlen = 0; + int rc = EVP_DigestFinal(c, md, &mdlen); - EVP_DigestFinal(c, md, &mdlen); EVP_MD_CTX_destroy(c); + if (rc != 1) { + return SSH_ERROR; + } + return SSH_OK; } -void sha256(const unsigned char *digest, int len, unsigned char *hash) +int sha256(const unsigned char *digest, int len, unsigned char *hash) { SHA256CTX c = sha256_init(); - if (c != NULL) { - sha256_update(c, digest, len); - sha256_final(hash, c); + int rc; + + if (c == NULL) { + return SSH_ERROR; } + rc = sha256_update(c, digest, len); + if (rc != SSH_OK) { + sha256_ctx_free(c); + return SSH_ERROR; + } + return sha256_final(hash, c); } SHA384CTX sha384_init(void) @@ -256,26 +242,47 @@ return c; } -void sha384_update(SHA384CTX c, const void *data, unsigned long len) +void +sha384_ctx_free(SHA384CTX c) +{ + EVP_MD_CTX_destroy(c); +} + +int sha384_update(SHA384CTX c, const void *data, unsigned long len) { - EVP_DigestUpdate(c, data, len); + int rc = EVP_DigestUpdate(c, data, len); + if (rc != 1) { + return SSH_ERROR; + } + return SSH_OK; } -void sha384_final(unsigned char *md, SHA384CTX c) +int sha384_final(unsigned char *md, SHA384CTX c) { unsigned int mdlen = 0; + int rc = EVP_DigestFinal(c, md, &mdlen); - EVP_DigestFinal(c, md, &mdlen); EVP_MD_CTX_destroy(c); + if (rc != 1) { + return SSH_ERROR; + } + return SSH_OK; } -void sha384(const unsigned char *digest, int len, unsigned char *hash) +int sha384(const unsigned char *digest, int len, unsigned char *hash) { SHA384CTX c = sha384_init(); - if (c != NULL) { - sha384_update(c, digest, len); - sha384_final(hash, c); + int rc; + + if (c == NULL) { + return SSH_ERROR; } + rc = sha384_update(c, digest, len); + if (rc != SSH_OK) { + sha384_ctx_free(c); + return SSH_ERROR; + } + return sha384_final(hash, c); } SHA512CTX sha512_init(void) @@ -294,26 +301,46 @@ return c; } -void sha512_update(SHA512CTX c, const void *data, unsigned long len) +void sha512_ctx_free(SHA512CTX c) +{ + EVP_MD_CTX_destroy(c); +} + +int sha512_update(SHA512CTX c, const void *data, unsigned long len) { - EVP_DigestUpdate(c, data, len); + int rc = EVP_DigestUpdate(c, data, len); + if (rc != 1) { + return SSH_ERROR; + } + return SSH_OK; } -void sha512_final(unsigned char *md, SHA512CTX c) +int sha512_final(unsigned char *md, SHA512CTX c) { unsigned int mdlen = 0; + int rc = EVP_DigestFinal(c, md, &mdlen); - EVP_DigestFinal(c, md, &mdlen); EVP_MD_CTX_destroy(c); + if (rc != 1) { + return SSH_ERROR; + } + return SSH_OK; } -void sha512(const unsigned char *digest, int len, unsigned char *hash) +int sha512(const unsigned char *digest, int len, unsigned char *hash) { SHA512CTX c = sha512_init(); - if (c != NULL) { - sha512_update(c, digest, len); - sha512_final(hash, c); + int rc; + + if (c == NULL) { + return SSH_ERROR; } + rc = sha512_update(c, digest, len); + if (rc != SSH_OK) { + sha512_ctx_free(c); + return SSH_ERROR; + } + return sha512_final(hash, c); } MD5CTX md5_init(void) @@ -332,19 +359,33 @@ return c; } -void md5_update(MD5CTX c, const void *data, unsigned long len) +void md5_ctx_free(MD5CTX c) +{ + EVP_MD_CTX_destroy(c); +} + +int md5_update(MD5CTX c, const void *data, unsigned long len) { - EVP_DigestUpdate(c, data, len); + int rc = EVP_DigestUpdate(c, data, len); + if (rc != 1) { + return SSH_ERROR; + } + return SSH_OK; } -void md5_final(unsigned char *md, MD5CTX c) +int md5_final(unsigned char *md, MD5CTX c) { unsigned int mdlen = 0; + int rc = EVP_DigestFinal(c, md, &mdlen); - EVP_DigestFinal(c, md, &mdlen); EVP_MD_CTX_destroy(c); + if (rc != 1) { + return SSH_ERROR; + } + return SSH_OK; } + #ifdef HAVE_OPENSSL_EVP_KDF_CTX_NEW_ID static const EVP_MD *sshkdf_digest_to_md(enum ssh_kdf_digest digest_type) { diff -Nru libssh-0.9.7/src/libgcrypt.c libssh-0.9.8/src/libgcrypt.c --- libssh-0.9.7/src/libgcrypt.c 2022-05-23 13:08:11.000000000 +0000 +++ libssh-0.9.8/src/libgcrypt.c 2023-12-18 16:57:27.000000000 +0000 @@ -68,72 +68,37 @@ return ctx; } -void sha1_update(SHACTX c, const void *data, unsigned long len) { - gcry_md_write(c, data, len); -} - -void sha1_final(unsigned char *md, SHACTX c) { - gcry_md_final(c); - memcpy(md, gcry_md_read(c, 0), SHA_DIGEST_LEN); - gcry_md_close(c); -} - -void sha1(const unsigned char *digest, int len, unsigned char *hash) { - gcry_md_hash_buffer(GCRY_MD_SHA1, hash, digest, len); -} - -#ifdef HAVE_GCRYPT_ECC -static int nid_to_md_algo(int nid) +void +sha1_ctx_free(SHACTX c) { - switch (nid) { - case NID_gcrypt_nistp256: - return GCRY_MD_SHA256; - case NID_gcrypt_nistp384: - return GCRY_MD_SHA384; - case NID_gcrypt_nistp521: - return GCRY_MD_SHA512; - } - return GCRY_MD_NONE; + gcry_md_close(c); } -void evp(int nid, unsigned char *digest, int len, - unsigned char *hash, unsigned int *hlen) -{ - int algo = nid_to_md_algo(nid); - - /* Note: What gcrypt calls 'hash' is called 'digest' here and - vice-versa. */ - gcry_md_hash_buffer(algo, hash, digest, len); - *hlen = gcry_md_get_algo_dlen(algo); +int sha1_update(SHACTX c, const void *data, unsigned long len) { + gcry_md_write(c, data, len); + return SSH_OK; } -EVPCTX evp_init(int nid) +int sha1_final(unsigned char *md, SHACTX c) { - gcry_error_t err; - int algo = nid_to_md_algo(nid); - EVPCTX ctx; + unsigned char *tmp = NULL; - err = gcry_md_open(&ctx, algo, 0); - if (err) { - return NULL; + gcry_md_final(c); + tmp = gcry_md_read(c, 0); + if (tmp == NULL) { + gcry_md_close(c); + return SSH_ERROR; } - - return ctx; + memcpy(md, tmp, SHA_DIGEST_LEN); + gcry_md_close(c); + return SSH_OK; } -void evp_update(EVPCTX ctx, const void *data, unsigned long len) -{ - gcry_md_write(ctx, data, len); +int sha1(const unsigned char *digest, int len, unsigned char *hash) { + gcry_md_hash_buffer(GCRY_MD_SHA1, hash, digest, len); + return SSH_OK; } -void evp_final(EVPCTX ctx, unsigned char *md, unsigned int *mdlen) -{ - int algo = gcry_md_get_algo(ctx); - *mdlen = gcry_md_get_algo_dlen(algo); - memcpy(md, gcry_md_read(ctx, algo), *mdlen); - gcry_md_close(ctx); -} -#endif SHA256CTX sha256_init(void) { SHA256CTX ctx = NULL; @@ -142,18 +107,35 @@ return ctx; } -void sha256_update(SHACTX c, const void *data, unsigned long len) { +void +sha256_ctx_free(SHA256CTX c) +{ + gcry_md_close(c); +} + +int sha256_update(SHACTX c, const void *data, unsigned long len) { gcry_md_write(c, data, len); + return SSH_OK; } -void sha256_final(unsigned char *md, SHACTX c) { - gcry_md_final(c); - memcpy(md, gcry_md_read(c, 0), SHA256_DIGEST_LEN); - gcry_md_close(c); +int sha256_final(unsigned char *md, SHACTX c) +{ + unsigned char *tmp = NULL; + + gcry_md_final(c); + tmp = gcry_md_read(c, 0); + if (tmp == NULL) { + gcry_md_close(c); + return SSH_ERROR; + } + memcpy(md, tmp, SHA256_DIGEST_LEN); + gcry_md_close(c); + return SSH_OK; } -void sha256(const unsigned char *digest, int len, unsigned char *hash){ +int sha256(const unsigned char *digest, int len, unsigned char *hash){ gcry_md_hash_buffer(GCRY_MD_SHA256, hash, digest, len); + return SSH_OK; } SHA384CTX sha384_init(void) { @@ -163,18 +145,35 @@ return ctx; } -void sha384_update(SHACTX c, const void *data, unsigned long len) { +void +sha384_ctx_free(SHA384CTX c) +{ + gcry_md_close(c); +} + +int sha384_update(SHACTX c, const void *data, unsigned long len) { gcry_md_write(c, data, len); + return SSH_OK; } -void sha384_final(unsigned char *md, SHACTX c) { - gcry_md_final(c); - memcpy(md, gcry_md_read(c, 0), SHA384_DIGEST_LEN); - gcry_md_close(c); +int sha384_final(unsigned char *md, SHACTX c) +{ + unsigned char *tmp = NULL; + + gcry_md_final(c); + tmp = gcry_md_read(c, 0); + if (tmp == NULL) { + gcry_md_close(c); + return SSH_ERROR; + } + memcpy(md, tmp, SHA384_DIGEST_LEN); + gcry_md_close(c); + return SSH_OK; } -void sha384(const unsigned char *digest, int len, unsigned char *hash) { +int sha384(const unsigned char *digest, int len, unsigned char *hash) { gcry_md_hash_buffer(GCRY_MD_SHA384, hash, digest, len); + return SSH_OK; } SHA512CTX sha512_init(void) { @@ -184,18 +183,35 @@ return ctx; } -void sha512_update(SHACTX c, const void *data, unsigned long len) { +void +sha512_ctx_free(SHA512CTX c) +{ + gcry_md_close(c); +} + +int sha512_update(SHACTX c, const void *data, unsigned long len) { gcry_md_write(c, data, len); + return SSH_OK; } -void sha512_final(unsigned char *md, SHACTX c) { - gcry_md_final(c); - memcpy(md, gcry_md_read(c, 0), SHA512_DIGEST_LEN); - gcry_md_close(c); +int sha512_final(unsigned char *md, SHACTX c) +{ + unsigned char *tmp = NULL; + + gcry_md_final(c); + tmp = gcry_md_read(c, 0); + if (tmp == NULL) { + gcry_md_close(c); + return SSH_ERROR; + } + memcpy(md, tmp, SHA512_DIGEST_LEN); + gcry_md_close(c); + return SSH_OK; } -void sha512(const unsigned char *digest, int len, unsigned char *hash) { +int sha512(const unsigned char *digest, int len, unsigned char *hash) { gcry_md_hash_buffer(GCRY_MD_SHA512, hash, digest, len); + return SSH_OK; } MD5CTX md5_init(void) { @@ -205,14 +221,30 @@ return c; } -void md5_update(MD5CTX c, const void *data, unsigned long len) { +void +md5_ctx_free(MD5CTX c) +{ + gcry_md_close(c); +} + +int md5_update(MD5CTX c, const void *data, unsigned long len) { gcry_md_write(c,data,len); + return SSH_OK; } -void md5_final(unsigned char *md, MD5CTX c) { - gcry_md_final(c); - memcpy(md, gcry_md_read(c, 0), MD5_DIGEST_LEN); - gcry_md_close(c); +int md5_final(unsigned char *md, MD5CTX c) +{ + unsigned char *tmp = NULL; + + gcry_md_final(c); + tmp = gcry_md_read(c, 0); + if (tmp == NULL) { + gcry_md_close(c); + return SSH_ERROR; + } + memcpy(md, tmp, MD5_DIGEST_LEN); + gcry_md_close(c); + return SSH_OK; } int ssh_kdf(struct ssh_crypto_struct *crypto, diff -Nru libssh-0.9.7/src/libmbedcrypto.c libssh-0.9.8/src/libmbedcrypto.c --- libssh-0.9.7/src/libmbedcrypto.c 2022-05-23 13:08:11.000000000 +0000 +++ libssh-0.9.8/src/libmbedcrypto.c 2023-12-18 16:57:27.000000000 +0000 @@ -82,99 +82,46 @@ return ctx; } -void sha1_update(SHACTX c, const void *data, unsigned long len) +void +sha1_ctx_free(SHACTX c) { - mbedtls_md_update(c, data, len); -} - -void sha1_final(unsigned char *md, SHACTX c) -{ - mbedtls_md_finish(c, md); mbedtls_md_free(c); SAFE_FREE(c); } -void sha1(const unsigned char *digest, int len, unsigned char *hash) +int sha1_update(SHACTX c, const void *data, unsigned long len) { - const mbedtls_md_info_t *md_info = - mbedtls_md_info_from_type(MBEDTLS_MD_SHA1); - if (md_info != NULL) { - mbedtls_md(md_info, digest, len, hash); + int rc = mbedtls_md_update(c, data, len); + if (rc != 0) { + return SSH_ERROR; } + return SSH_OK; } -static mbedtls_md_type_t nid_to_md_algo(int nid) +int sha1_final(unsigned char *md, SHACTX c) { - switch (nid) { - case NID_mbedtls_nistp256: - return MBEDTLS_MD_SHA256; - case NID_mbedtls_nistp384: - return MBEDTLS_MD_SHA384; - case NID_mbedtls_nistp521: - return MBEDTLS_MD_SHA512; + int rc = mbedtls_md_finish(c, md); + sha1_ctx_free(c); + if (rc != 0) { + return SSH_ERROR; } - return MBEDTLS_MD_NONE; + return SSH_OK; } -void evp(int nid, unsigned char *digest, int len, - unsigned char *hash, unsigned int *hlen) +int sha1(const unsigned char *digest, int len, unsigned char *hash) { - mbedtls_md_type_t algo = nid_to_md_algo(nid); const mbedtls_md_info_t *md_info = - mbedtls_md_info_from_type(algo); - - - if (md_info != NULL) { - *hlen = mbedtls_md_get_size(md_info); - mbedtls_md(md_info, digest, len, hash); - } -} - -EVPCTX evp_init(int nid) -{ - EVPCTX ctx = NULL; + mbedtls_md_info_from_type(MBEDTLS_MD_SHA1); int rc; - mbedtls_md_type_t algo = nid_to_md_algo(nid); - const mbedtls_md_info_t *md_info = - mbedtls_md_info_from_type(algo); if (md_info == NULL) { - return NULL; - } - - ctx = malloc(sizeof(mbedtls_md_context_t)); - if (ctx == NULL) { - return NULL; - } - - mbedtls_md_init(ctx); - - rc = mbedtls_md_setup(ctx, md_info, 0); - if (rc != 0) { - SAFE_FREE(ctx); - return NULL; + return SSH_ERROR; } - - rc = mbedtls_md_starts(ctx); + rc = mbedtls_md(md_info, digest, len, hash); if (rc != 0) { - SAFE_FREE(ctx); - return NULL; + return SSH_ERROR; } - - return ctx; -} - -void evp_update(EVPCTX ctx, const void *data, unsigned long len) -{ - mbedtls_md_update(ctx, data, len); -} - -void evp_final(EVPCTX ctx, unsigned char *md, unsigned int *mdlen) -{ - *mdlen = mbedtls_md_get_size(ctx->md_info); - mbedtls_md_finish(ctx, md); - mbedtls_md_free(ctx); - SAFE_FREE(ctx); + return SSH_OK; } SHA256CTX sha256_init(void) @@ -210,25 +157,46 @@ return ctx; } -void sha256_update(SHA256CTX c, const void *data, unsigned long len) +void +sha256_ctx_free(SHA256CTX c) { - mbedtls_md_update(c, data, len); + mbedtls_md_free(c); + SAFE_FREE(c); } -void sha256_final(unsigned char *md, SHA256CTX c) +int sha256_update(SHA256CTX c, const void *data, unsigned long len) { - mbedtls_md_finish(c, md); - mbedtls_md_free(c); - SAFE_FREE(c); + int rc = mbedtls_md_update(c, data, len); + if (rc != 0) { + return SSH_ERROR; + } + return SSH_OK; +} + +int sha256_final(unsigned char *md, SHA256CTX c) +{ + int rc = mbedtls_md_finish(c, md); + sha256_ctx_free(c); + if (rc != 0) { + return SSH_ERROR; + } + return SSH_OK; } -void sha256(const unsigned char *digest, int len, unsigned char *hash) +int sha256(const unsigned char *digest, int len, unsigned char *hash) { const mbedtls_md_info_t *md_info = mbedtls_md_info_from_type(MBEDTLS_MD_SHA256); - if (md_info != NULL) { - mbedtls_md(md_info, digest, len, hash); + int rc; + + if (md_info == NULL) { + return SSH_ERROR; + } + rc = mbedtls_md(md_info, digest, len, hash); + if (rc != 0) { + return SSH_ERROR; } + return SSH_OK; } SHA384CTX sha384_init(void) @@ -264,25 +232,46 @@ return ctx; } -void sha384_update(SHA384CTX c, const void *data, unsigned long len) +void +sha384_ctx_free(SHA384CTX c) { - mbedtls_md_update(c, data, len); + mbedtls_md_free(c); + SAFE_FREE(c); } -void sha384_final(unsigned char *md, SHA384CTX c) +int sha384_update(SHA384CTX c, const void *data, unsigned long len) { - mbedtls_md_finish(c, md); - mbedtls_md_free(c); - SAFE_FREE(c); + int rc = mbedtls_md_update(c, data, len); + if (rc != 0) { + return SSH_ERROR; + } + return SSH_OK; } -void sha384(const unsigned char *digest, int len, unsigned char *hash) +int sha384_final(unsigned char *md, SHA384CTX c) +{ + int rc = mbedtls_md_finish(c, md); + sha384_ctx_free(c); + if (rc != 0) { + return SSH_ERROR; + } + return SSH_OK; +} + +int sha384(const unsigned char *digest, int len, unsigned char *hash) { const mbedtls_md_info_t *md_info = mbedtls_md_info_from_type(MBEDTLS_MD_SHA384); - if (md_info != NULL) { - mbedtls_md(md_info, digest, len, hash); + int rc; + + if (md_info == NULL) { + return SSH_ERROR; + } + rc = mbedtls_md(md_info, digest, len, hash); + if (rc != 0) { + return SSH_ERROR; } + return SSH_OK; } SHA512CTX sha512_init(void) @@ -317,25 +306,46 @@ return ctx; } -void sha512_update(SHA512CTX c, const void *data, unsigned long len) +void +sha512_ctx_free(SHA512CTX c) +{ + mbedtls_md_free(c); + SAFE_FREE(c); +} + +int sha512_update(SHA512CTX c, const void *data, unsigned long len) { - mbedtls_md_update(c, data, len); + int rc = mbedtls_md_update(c, data, len); + if (rc != 0) { + return SSH_ERROR; + } + return SSH_OK; } -void sha512_final(unsigned char *md, SHA512CTX c) +int sha512_final(unsigned char *md, SHA512CTX c) { - mbedtls_md_finish(c, md); - mbedtls_md_free(c); - SAFE_FREE(c); + int rc = mbedtls_md_finish(c, md); + sha512_ctx_free(c); + if (rc != 0) { + return SSH_ERROR; + } + return SSH_OK; } -void sha512(const unsigned char *digest, int len, unsigned char *hash) +int sha512(const unsigned char *digest, int len, unsigned char *hash) { const mbedtls_md_info_t *md_info = mbedtls_md_info_from_type(MBEDTLS_MD_SHA512); - if (md_info != NULL) { - mbedtls_md(md_info, digest, len, hash); + int rc; + + if (md_info == NULL) { + return SSH_ERROR; } + rc = mbedtls_md(md_info, digest, len, hash); + if (rc != 0) { + return SSH_ERROR; + } + return SSH_OK; } MD5CTX md5_init(void) @@ -370,16 +380,30 @@ return ctx; } +void +md5_ctx_free(MD5CTX c) +{ + mbedtls_md_free(c); + SAFE_FREE(c); +} -void md5_update(MD5CTX c, const void *data, unsigned long len) { - mbedtls_md_update(c, data, len); +int md5_update(MD5CTX c, const void *data, unsigned long len) +{ + int rc = mbedtls_md_update(c, data, len); + if (rc != 0) { + return SSH_ERROR; + } + return SSH_OK; } -void md5_final(unsigned char *md, MD5CTX c) +int md5_final(unsigned char *md, MD5CTX c) { - mbedtls_md_finish(c, md); - mbedtls_md_free(c); - SAFE_FREE(c); + int rc = mbedtls_md_finish(c, md); + md5_ctx_free(c); + if (rc != 0) { + return SSH_ERROR; + } + return SSH_OK; } int ssh_kdf(struct ssh_crypto_struct *crypto, diff -Nru libssh-0.9.7/src/misc.c libssh-0.9.8/src/misc.c --- libssh-0.9.7/src/misc.c 2023-05-04 11:39:39.000000000 +0000 +++ libssh-0.9.8/src/misc.c 2023-12-18 16:57:27.000000000 +0000 @@ -32,6 +32,7 @@ #include #include #include +#include #endif /* _WIN32 */ @@ -59,6 +60,7 @@ #include #include #include +#include #ifdef HAVE_IO_H #include @@ -94,6 +96,8 @@ #define ZLIB_STRING "" #endif +#define ARPA_DOMAIN_MAX_LEN 63 + /** * @defgroup libssh_misc The SSH helper functions. * @ingroup libssh @@ -214,22 +218,37 @@ int ssh_is_ipaddr(const char *str) { int rc = SOCKET_ERROR; + char *s = strdup(str); - if (strchr(str, ':')) { + if (s == NULL) { + return -1; + } + if (strchr(s, ':')) { struct sockaddr_storage ss; int sslen = sizeof(ss); + char *network_interface = strchr(s, '%'); - /* TODO link-local (IP:v6:addr%ifname). */ - rc = WSAStringToAddressA((LPSTR) str, + /* link-local (IP:v6:addr%ifname). */ + if (network_interface != NULL) { + rc = if_nametoindex(network_interface + 1); + if (rc == 0) { + free(s); + return 0; + } + *network_interface = '\0'; + } + rc = WSAStringToAddressA((LPSTR) s, AF_INET6, NULL, (struct sockaddr*)&ss, &sslen); if (rc == 0) { + free(s); return 1; } } + free(s); return ssh_is_ipaddr_v4(str); } #else /* _WIN32 */ @@ -333,17 +352,32 @@ int ssh_is_ipaddr(const char *str) { int rc = -1; + char *s = strdup(str); - if (strchr(str, ':')) { + if (s == NULL) { + return -1; + } + if (strchr(s, ':')) { struct in6_addr dest6; + char *network_interface = strchr(s, '%'); - /* TODO link-local (IP:v6:addr%ifname). */ - rc = inet_pton(AF_INET6, str, &dest6); + /* link-local (IP:v6:addr%ifname). */ + if (network_interface != NULL) { + rc = if_nametoindex(network_interface + 1); + if (rc == 0) { + free(s); + return 0; + } + *network_interface = '\0'; + } + rc = inet_pton(AF_INET6, s, &dest6); if (rc > 0) { + free(s); return 1; } } + free(s); return ssh_is_ipaddr_v4(str); } @@ -1734,4 +1768,70 @@ return out - buf; } +/** + * @brief Checks syntax of a domain name + * + * The check is made based on the RFC1035 section 2.3.1 + * Allowed characters are: hyphen, period, digits (0-9) and letters (a-zA-Z) + * + * The label should be no longer than 63 characters + * The label should start with a letter and end with a letter or number + * The label in this implementation can start with a number to allow virtual + * URLs to pass. Note that this will make IPv4 addresses to pass + * this check too. + * + * @param hostname The domain name to be checked, has to be null terminated + * + * @return SSH_OK if the hostname passes syntax check + * SSH_ERROR otherwise or if hostname is NULL or empty string + */ +int ssh_check_hostname_syntax(const char *hostname) +{ + char *it = NULL, *s = NULL, *buf = NULL; + size_t it_len; + char c; + + if (hostname == NULL || strlen(hostname) == 0) { + return SSH_ERROR; + } + + /* strtok_r writes into the string, keep the input clean */ + s = strdup(hostname); + if (s == NULL) { + return SSH_ERROR; + } + + it = strtok_r(s, ".", &buf); + /* if the token has 0 length */ + if (it == NULL) { + free(s); + return SSH_ERROR; + } + do { + it_len = strlen(it); + if (it_len > ARPA_DOMAIN_MAX_LEN || + /* the first char must be a letter, but some virtual urls start + * with a number */ + isalnum(it[0]) == 0 || + isalnum(it[it_len - 1]) == 0) { + free(s); + return SSH_ERROR; + } + while (*it != '\0') { + c = *it; + /* the "." is allowed too, but tokenization removes it from the + * string */ + if (isalnum(c) == 0 && c != '-') { + free(s); + return SSH_ERROR; + } + it++; + } + } while ((it = strtok_r(NULL, ".", &buf)) != NULL); + + free(s); + + return SSH_OK; +} + /** @} */ diff -Nru libssh-0.9.7/src/options.c libssh-0.9.8/src/options.c --- libssh-0.9.7/src/options.c 2022-05-23 13:08:11.000000000 +0000 +++ libssh-0.9.8/src/options.c 2023-12-18 16:57:27.000000000 +0000 @@ -36,6 +36,7 @@ #include "libssh/session.h" #include "libssh/misc.h" #include "libssh/options.h" +#include "libssh/config_parser.h" #ifdef WITH_SERVER #include "libssh/server.h" #include "libssh/bind.h" @@ -490,33 +491,24 @@ ssh_set_error_invalid(session); return -1; } else { - q = strdup(value); - if (q == NULL) { - ssh_set_error_oom(session); + char *username = NULL, *hostname = NULL, *port = NULL; + rc = ssh_config_parse_uri(value, &username, &hostname, &port); + if (rc != SSH_OK) { return -1; } - p = strchr(q, '@'); - - SAFE_FREE(session->opts.host); - - if (p) { - *p = '\0'; - session->opts.host = strdup(p + 1); - if (session->opts.host == NULL) { - SAFE_FREE(q); - ssh_set_error_oom(session); - return -1; - } - + if (port != NULL) { + SAFE_FREE(username); + SAFE_FREE(hostname); + SAFE_FREE(port); + return -1; + } + if (username != NULL) { SAFE_FREE(session->opts.username); - session->opts.username = strdup(q); - SAFE_FREE(q); - if (session->opts.username == NULL) { - ssh_set_error_oom(session); - return -1; - } - } else { - session->opts.host = q; + session->opts.username = username; + } + if (hostname != NULL) { + SAFE_FREE(session->opts.host); + session->opts.host = hostname; } } break; diff -Nru libssh-0.9.7/src/packet.c libssh-0.9.8/src/packet.c --- libssh-0.9.7/src/packet.c 2023-05-04 11:40:36.000000000 +0000 +++ libssh-0.9.8/src/packet.c 2023-12-18 16:57:27.000000000 +0000 @@ -1309,6 +1309,19 @@ } #endif /* WITH_ZLIB */ payloadsize = ssh_buffer_get_len(session->in_buffer); + if (session->recv_seq == UINT32_MAX) { + /* Overflowing sequence numbers is always fishy */ + if (crypto == NULL) { + /* don't allow sequence number overflow when unencrypted */ + ssh_set_error(session, + SSH_FATAL, + "Incoming sequence number overflow"); + goto error; + } else { + SSH_LOG(SSH_LOG_WARNING, + "Incoming sequence number overflow"); + } + } session->recv_seq++; if (crypto != NULL) { struct ssh_cipher_struct *cipher = NULL; @@ -1331,7 +1344,19 @@ SSH_LOG(SSH_LOG_PACKET, "packet: read type %hhd [len=%d,padding=%hhd,comp=%d,payload=%d]", session->in_packet.type, packet_len, padding, compsize, payloadsize); - + if (crypto == NULL) { + /* In strict kex, only a few packets are allowed. Taint the session + * if we received packets that are normally allowed but to be + * refused if we are in strict kex when KEX is over. + */ + uint8_t type = session->in_packet.type; + + if (type != SSH2_MSG_KEXINIT && type != SSH2_MSG_NEWKEYS && + (type < SSH2_MSG_KEXDH_INIT || + type > SSH2_MSG_KEX_DH_GEX_REQUEST)) { + session->flags |= SSH_SESSION_FLAG_KEX_TAINTED; + } + } /* Check if the packet is expected */ filter_result = ssh_packet_incoming_filter(session); @@ -1347,6 +1372,9 @@ session->in_packet.type); goto error; case SSH_PACKET_UNKNOWN: + if (crypto == NULL) { + session->flags |= SSH_SESSION_FLAG_KEX_TAINTED; + } ssh_packet_send_unimplemented(session, session->recv_seq - 1); break; } @@ -1521,7 +1549,33 @@ SSH_LOG(SSH_LOG_RARE, "Failed to send unimplemented: %s", ssh_get_error(session)); } + if (session->current_crypto == NULL) { + session->flags |= SSH_SESSION_FLAG_KEX_TAINTED; + } + } +} + +/** @internal + * @brief sends a SSH_MSG_NEWKEYS when enabling the new negotiated ciphers + * @param session the SSH session + * @return SSH_ERROR on error, else SSH_OK + */ +int ssh_packet_send_newkeys(ssh_session session) +{ + int rc; + + /* Send the MSG_NEWKEYS */ + rc = ssh_buffer_add_u8(session->out_buffer, SSH2_MSG_NEWKEYS); + if (rc < 0) { + return rc; } + + rc = ssh_packet_send(session); + if (rc == SSH_ERROR) { + return rc; + } + SSH_LOG(SSH_LOG_DEBUG, "SSH_MSG_NEWKEYS sent"); + return rc; } /** @internal @@ -1829,6 +1883,10 @@ if (rc == SSH_OK && type == SSH2_MSG_NEWKEYS) { struct ssh_iterator *it; + if (session->flags & SSH_SESSION_FLAG_KEX_STRICT) { + /* reset packet sequence number when running in strict kex mode */ + session->send_seq = 0; + } for (it = ssh_list_get_iterator(session->out_queue); it != NULL; it = ssh_list_get_iterator(session->out_queue)) { diff -Nru libssh-0.9.7/src/packet_cb.c libssh-0.9.8/src/packet_cb.c --- libssh-0.9.7/src/packet_cb.c 2023-05-04 11:40:23.000000000 +0000 +++ libssh-0.9.8/src/packet_cb.c 2023-12-18 16:57:27.000000000 +0000 @@ -110,6 +110,18 @@ goto error; } + if (session->flags & SSH_SESSION_FLAG_KEX_STRICT) { + /* reset packet sequence number when running in strict kex mode */ + session->recv_seq = 0; + /* Check that we aren't tainted */ + if (session->flags & SSH_SESSION_FLAG_KEX_TAINTED) { + ssh_set_error(session, + SSH_FATAL, + "Received unexpected packets in strict KEX mode."); + goto error; + } + } + if(session->server){ /* server things are done in server.c */ session->dh_handshake_state=DH_STATE_FINISHED; diff -Nru libssh-0.9.7/src/server.c libssh-0.9.8/src/server.c --- libssh-0.9.7/src/server.c 2023-05-04 11:40:47.000000000 +0000 +++ libssh-0.9.8/src/server.c 2023-12-18 16:57:27.000000000 +0000 @@ -195,7 +195,13 @@ } } - return 0; + /* Do not append the extensions during rekey */ + if (session->flags & SSH_SESSION_FLAG_AUTHENTICATED) { + return SSH_OK; + } + + rc = ssh_kex_append_extensions(session, server); + return rc; } int ssh_server_init_kex(ssh_session session) { diff -Nru libssh-0.9.7/src/session.c libssh-0.9.8/src/session.c --- libssh-0.9.7/src/session.c 2022-05-23 13:08:11.000000000 +0000 +++ libssh-0.9.8/src/session.c 2023-12-18 16:57:27.000000000 +0000 @@ -643,6 +643,10 @@ } spoll = ssh_socket_get_poll_handle(session->socket); + if (spoll == NULL) { + ssh_set_error_oom(session); + return SSH_ERROR; + } ssh_poll_add_events(spoll, POLLIN); ctx = ssh_poll_get_ctx(spoll); @@ -997,7 +1001,18 @@ *hash = NULL; if (session->current_crypto == NULL || session->current_crypto->server_pubkey == NULL) { - ssh_set_error(session,SSH_FATAL,"No current cryptographic context"); + ssh_set_error(session, SSH_FATAL, "No current cryptographic context"); + return SSH_ERROR; + } + + rc = ssh_get_server_publickey(session, &pubkey); + if (rc != SSH_OK) { + return SSH_ERROR; + } + + rc = ssh_pki_export_pubkey_blob(pubkey, &pubkey_blob); + ssh_key_free(pubkey); + if (rc != SSH_OK) { return SSH_ERROR; } @@ -1012,25 +1027,21 @@ return SSH_ERROR; } - rc = ssh_get_server_publickey(session, &pubkey); + rc = md5_update(ctx, + ssh_string_data(pubkey_blob), + ssh_string_len(pubkey_blob)); if (rc != SSH_OK) { - md5_final(h, ctx); + md5_ctx_free(ctx); SAFE_FREE(h); - return SSH_ERROR; + return rc; } - - rc = ssh_pki_export_pubkey_blob(pubkey, &pubkey_blob); - ssh_key_free(pubkey); + SSH_STRING_FREE(pubkey_blob); + rc = md5_final(h, ctx); if (rc != SSH_OK) { - md5_final(h, ctx); SAFE_FREE(h); - return SSH_ERROR; + return rc; } - md5_update(ctx, ssh_string_data(pubkey_blob), ssh_string_len(pubkey_blob)); - SSH_STRING_FREE(pubkey_blob); - md5_final(h, ctx); - *hash = h; return MD5_DIGEST_LEN; @@ -1149,8 +1160,17 @@ goto out; } - sha1_update(ctx, ssh_string_data(blob), ssh_string_len(blob)); - sha1_final(h, ctx); + rc = sha1_update(ctx, ssh_string_data(blob), ssh_string_len(blob)); + if (rc != SSH_OK) { + free(h); + sha1_ctx_free(ctx); + goto out; + } + rc = sha1_final(h, ctx); + if (rc != SSH_OK) { + free(h); + goto out; + } *hlen = SHA_DIGEST_LEN; } @@ -1172,8 +1192,17 @@ goto out; } - sha256_update(ctx, ssh_string_data(blob), ssh_string_len(blob)); - sha256_final(h, ctx); + rc = sha256_update(ctx, ssh_string_data(blob), ssh_string_len(blob)); + if (rc != SSH_OK) { + free(h); + sha256_ctx_free(ctx); + goto out; + } + rc = sha256_final(h, ctx); + if (rc != SSH_OK) { + free(h); + goto out; + } *hlen = SHA256_DIGEST_LEN; } @@ -1203,8 +1232,17 @@ goto out; } - md5_update(ctx, ssh_string_data(blob), ssh_string_len(blob)); - md5_final(h, ctx); + rc = md5_update(ctx, ssh_string_data(blob), ssh_string_len(blob)); + if (rc != SSH_OK) { + free(h); + md5_ctx_free(ctx); + goto out; + } + rc = md5_final(h, ctx); + if (rc != SSH_OK) { + free(h); + goto out; + } *hlen = MD5_DIGEST_LEN; } diff -Nru libssh-0.9.7/tests/client/torture_proxycommand.c libssh-0.9.8/tests/client/torture_proxycommand.c --- libssh-0.9.7/tests/client/torture_proxycommand.c 2022-05-23 13:08:11.000000000 +0000 +++ libssh-0.9.8/tests/client/torture_proxycommand.c 2023-12-18 16:57:27.000000000 +0000 @@ -161,6 +161,56 @@ assert_int_equal(rc & O_RDWR, O_RDWR); } +static void torture_options_proxycommand_injection(void **state) +{ + struct torture_state *s = *state; + struct passwd *pwd = NULL; + const char *malicious_host = "`echo foo > mfile`"; + const char *command = "nc %h %p"; + char *current_dir = NULL; + char *malicious_file_path = NULL; + int mfp_len; + int verbosity = torture_libssh_verbosity(); + struct stat sb; + int rc; + + pwd = getpwnam("bob"); + assert_non_null(pwd); + + rc = setuid(pwd->pw_uid); + assert_return_code(rc, errno); + + s->ssh.session = ssh_new(); + assert_non_null(s->ssh.session); + + ssh_options_set(s->ssh.session, SSH_OPTIONS_LOG_VERBOSITY, &verbosity); + // if we would be checking the rc, this should fail + ssh_options_set(s->ssh.session, SSH_OPTIONS_HOST, malicious_host); + + ssh_options_set(s->ssh.session, SSH_OPTIONS_USER, TORTURE_SSH_USER_ALICE); + + rc = ssh_options_set(s->ssh.session, SSH_OPTIONS_PROXYCOMMAND, command); + assert_int_equal(rc, 0); + rc = ssh_connect(s->ssh.session); + assert_ssh_return_code_equal(s->ssh.session, rc, SSH_ERROR); + + current_dir = torture_get_current_working_dir(); + assert_non_null(current_dir); + mfp_len = strlen(current_dir) + 6; + malicious_file_path = malloc(mfp_len); + assert_non_null(malicious_file_path); + rc = snprintf(malicious_file_path, mfp_len, + "%s/mfile", current_dir); + assert_int_equal(rc, mfp_len); + free(current_dir); + rc = stat(malicious_file_path, &sb); + assert_int_not_equal(rc, 0); + + // cleanup + remove(malicious_file_path); + free(malicious_file_path); +} + int torture_run_tests(void) { int rc; struct CMUnitTest tests[] = { @@ -176,6 +226,9 @@ cmocka_unit_test_setup_teardown(torture_options_set_proxycommand_ssh_stderr, session_setup, session_teardown), + cmocka_unit_test_setup_teardown(torture_options_proxycommand_injection, + NULL, + session_teardown), }; diff -Nru libssh-0.9.7/tests/client/torture_rekey.c libssh-0.9.8/tests/client/torture_rekey.c --- libssh-0.9.7/tests/client/torture_rekey.c 2023-05-04 11:40:51.000000000 +0000 +++ libssh-0.9.8/tests/client/torture_rekey.c 2023-12-18 16:57:27.000000000 +0000 @@ -148,6 +148,29 @@ ssh_disconnect(s->ssh.session); } +static void sanity_check_session(void **state) +{ + struct torture_state *s = *state; + struct ssh_crypto_struct *c = NULL; + + c = s->ssh.session->current_crypto; + assert_non_null(c); + assert_int_equal(c->in_cipher->max_blocks, + bytes / c->in_cipher->blocksize); + assert_int_equal(c->out_cipher->max_blocks, + bytes / c->out_cipher->blocksize); + /* when strict kex is used, the newkeys reset the sequence number */ + if ((s->ssh.session->flags & SSH_SESSION_FLAG_KEX_STRICT) != 0) { + assert_int_equal(c->out_cipher->packets, s->ssh.session->send_seq); + assert_int_equal(c->in_cipher->packets, s->ssh.session->recv_seq); + } else { + /* Otherwise we have less encrypted packets than transferred + * (first are not encrypted) */ + assert_true(c->out_cipher->packets < s->ssh.session->send_seq); + assert_true(c->in_cipher->packets < s->ssh.session->recv_seq); + } +} + /* We lower the rekey limits manually and check that the rekey * really happens when sending data */ @@ -166,16 +189,10 @@ rc = ssh_connect(s->ssh.session); assert_ssh_return_code(s->ssh.session, rc); - /* The blocks limit is set correctly */ - c = s->ssh.session->current_crypto; - assert_int_equal(c->in_cipher->max_blocks, - bytes / c->in_cipher->blocksize); - assert_int_equal(c->out_cipher->max_blocks, - bytes / c->out_cipher->blocksize); - /* We should have less encrypted packets than transfered (first are not encrypted) */ - assert_true(c->out_cipher->packets < s->ssh.session->send_seq); - assert_true(c->in_cipher->packets < s->ssh.session->recv_seq); + sanity_check_session(state); /* Copy the initial secret hash = session_id so we know we changed keys later */ + c = s->ssh.session->current_crypto; + assert_non_null(c); secret_hash = malloc(c->digest_len); assert_non_null(secret_hash); memcpy(secret_hash, c->secret_hash, c->digest_len); @@ -272,14 +289,10 @@ sftp_file file; mode_t mask; - /* The blocks limit is set correctly */ - c = s->ssh.session->current_crypto; - assert_int_equal(c->in_cipher->max_blocks, bytes / c->in_cipher->blocksize); - assert_int_equal(c->out_cipher->max_blocks, bytes / c->out_cipher->blocksize); - /* We should have less encrypted packets than transfered (first are not encrypted) */ - assert_true(c->out_cipher->packets < s->ssh.session->send_seq); - assert_true(c->in_cipher->packets < s->ssh.session->recv_seq); + sanity_check_session(state); /* Copy the initial secret hash = session_id so we know we changed keys later */ + c = s->ssh.session->current_crypto; + assert_non_null(c); secret_hash = malloc(c->digest_len); assert_non_null(secret_hash); memcpy(secret_hash, c->secret_hash, c->digest_len); @@ -464,15 +477,10 @@ assert_ssh_return_code(s->ssh.session, rc); /* The blocks limit is set correctly */ - c = s->ssh.session->current_crypto; - assert_int_equal(c->in_cipher->max_blocks, - bytes / c->in_cipher->blocksize); - assert_int_equal(c->out_cipher->max_blocks, - bytes / c->out_cipher->blocksize); - /* We should have less encrypted packets than transfered (first are not encrypted) */ - assert_true(c->out_cipher->packets < s->ssh.session->send_seq); - assert_true(c->in_cipher->packets < s->ssh.session->recv_seq); + sanity_check_session(state); /* Copy the initial secret hash = session_id so we know we changed keys later */ + c = s->ssh.session->current_crypto; + assert_non_null(c); secret_hash = malloc(c->digest_len); assert_non_null(secret_hash); memcpy(secret_hash, c->secret_hash, c->digest_len); diff -Nru libssh-0.9.7/tests/client/torture_session.c libssh-0.9.8/tests/client/torture_session.c --- libssh-0.9.7/tests/client/torture_session.c 2022-05-23 13:08:11.000000000 +0000 +++ libssh-0.9.8/tests/client/torture_session.c 2023-12-18 16:57:27.000000000 +0000 @@ -118,12 +118,47 @@ ssh_channel_free(channel); } +static void torture_pubkey_hash(void **state) +{ + struct torture_state *s = *state; + ssh_session session = s->ssh.session; + char *hash = NULL; + char *hexa = NULL; + int rc = 0; + + /* bad arguments */ + rc = ssh_get_pubkey_hash(session, NULL); + assert_int_equal(rc, SSH_ERROR); + + rc = ssh_get_pubkey_hash(NULL, (unsigned char **)&hash); + assert_int_equal(rc, SSH_ERROR); + + /* deprecated, but should be covered by tests! */ + rc = ssh_get_pubkey_hash(session, (unsigned char **)&hash); + if (ssh_fips_mode()) { + /* When in FIPS mode, expect the call to fail */ + assert_int_equal(rc, SSH_ERROR); + } else { + assert_int_equal(rc, MD5_DIGEST_LEN); + + hexa = ssh_get_hexa((unsigned char *)hash, rc); + SSH_STRING_FREE_CHAR(hash); + assert_string_equal(hexa, + "ee:80:7f:61:f9:d5:be:f1:96:86:cc:96:7a:db:7a:7b"); + + SSH_STRING_FREE_CHAR(hexa); + } +} + int torture_run_tests(void) { int rc; struct CMUnitTest tests[] = { cmocka_unit_test_setup_teardown(torture_channel_read_error, session_setup, session_teardown), + cmocka_unit_test_setup_teardown(torture_pubkey_hash, + session_setup, + session_teardown), }; ssh_init(); diff -Nru libssh-0.9.7/tests/keys/certauth/id_rsa libssh-0.9.8/tests/keys/certauth/id_rsa --- libssh-0.9.7/tests/keys/certauth/id_rsa 2022-05-23 13:08:11.000000000 +0000 +++ libssh-0.9.8/tests/keys/certauth/id_rsa 2023-12-18 16:57:27.000000000 +0000 @@ -1,27 +1,27 @@ ------BEGIN RSA PRIVATE KEY----- -MIIEiwIBAAKB/QMTSsCQqarOIauonYgjAt8E+lgSWBU/43ITyDDzLM4IS4wCcqXB -1Fagz386FU1B2AcUqlPZ1+7RlaXkqgKr4nGHv00U/GG+YAUgUAw1G12kI4cvrnWr -FIXwcq+VTJNej5pHxEqcRLw7ZBorpqm2UsY5KLr5R3uMNap7koj1Hbt9lKsvfDn6 -HjM4qY0ygx8hxf/4wCzIh5V4k9/UAMkqI2CM9c3yEE2aWh/4MDOnAFj+0T2sMAo8 -jyOZ6v+W7hmEtsUc9mEv+5B+hhVeYO/RwxketJAQRPYDSPSi1mjtv9fnzGk15q/l -Hb2V/HP/pyIpao19A4daR0a4ia9Hk4UCAwEAAQKB/QKEaPxjrKzlWoQSWRdUaQY5 -Idyy7yw9hiMa9BK1COh/u66XVlY86Fwb9puR5Fu/WF67WIuX1PpizJXkLBBRtuDs -lvY2BjrPQ/MONtc3JPYp4vbFXYxtAzh6zrTPhMVfcjV7Jr1XWZ+lEVOmhR2G4gvk -P2WDozIKWub3jMLTt4afgHCGaKfKEUpKjFkiAalz8oLVv8qV1FVPPDT2PWeKMuE3 -XfoN7YUaP6+aPlNnjIv/3BDsrPsiKZ+AKXcERdPvVQa/LypzW08cqC6sIJKWVmQI -3KgoYs9VvbDXfQ8jKfcsTApZkSDaLX6tf3Ei+76R0lbV4L1rpypa25qj9YECfwHP -N+v/6yObJFL5/1rEuT7CFbfP8g5J8qUVufcPRKv//ChluLuWNxgLJmIv2ZffWwhe -GKHlT98QPgFvsMSOyLeut4beZYKDSeVNvEt9eCBjOax2jOBGo3hv8j/Fs8yAfZOV -Ardv2qUszubM+DVwjJzb3vaZyEesRucJISqkJeUCfwGzGdMp0LXrZ7aaQGgHj/P2 -DKGq0E2gnj/EBapatjxKm4hMRn/vkTWjCDCryTnvJqkW/00tr4GqWXoeilBFD270 -RcvbOe9LQmGlHIYzgwc5nfLDBQyeNnHRmkeD9LQRUfdTdHj4jf+35pHlsVUT0Rnl -IMNoRA6V07bySFdI3SECfiF+1rbrxuhaCRIA0Ax3pL0eGuuTgksAm8VlbCMTgSiC -kF1CrXXgSAHOZb02C9Bf4cwEFfjh/KxM/4eXDa+Rfg7JQJxmVLivqEAlxIOvIxBp -xDnSWAljmrrllozyQnBsJDbbOm6BLf5+e5wIuryHvnP7vHNEU0J24g/78PxrrQJ/ -AVD4OzYzUfESzbUBFJBmyIZSmhJ0aOpwJOpniNvgLymI8zI/l22uhF/TQ/6HRbsV -sfcBmoA7YKzRx2ZHsIsLvN6p/4u1fsJGkuERCk5yt/HDhfPLwU321IeEeMaVia+w -T1/u4JF/SADhLTU69az3UJrHmQ7zRmh7I0DZDeB8gQJ+XqIqutPeerNtbqMjXGW8 -TdpqZAzAQAv6dPgaH0W0OzJe2hP9uy0D84H5f8Im/irJh/AXo/QL3obXqopyeLf0 -HfcUUnEZEBPlqsirZFtPClD+HL6Orf1je0oVV/aQssPkQl6/aXBNd+kS27U3NBML -LmRhC4+Q+/M5MlRggLtn ------END RSA PRIVATE KEY----- +-----BEGIN OPENSSH PRIVATE KEY----- +b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAABFwAAAAdzc2gtcn +NhAAAAAwEAAQAAAQEA0DehrU/ohoimMKojFXdo1uEAcqx4fS87AjDUz8t4s436ppP+0+U3 ++qrhOCE/mXZvXewjTHltmEtCHNSbsWhYTjwrEQUDRNOVahn2PEQTcX/9itvsv9PX79Imbv +ZsLR0f1FsmorkWpnDQmuga7hYBFBj4sV+VML5ieoK2OraUEq46ILsxqRgUTetkErlzX7S2 +SPE7vNM0ahmA+HNBuKNUD+BOtCzkqN54flGA9TZ7kapC7xqiRHK+ZzahQ2PFR4BxbVP1uT +DsanbjKOpBC4hISao3hi4iUnyj0gJ8itmkhQS+oI/2KWSGW01/k9W7jOUXDSt7LGUTSW6s +ILYHzmefCwAAA9B/6IFvf+iBbwAAAAdzc2gtcnNhAAABAQDQN6GtT+iGiKYwqiMVd2jW4Q +ByrHh9LzsCMNTPy3izjfqmk/7T5Tf6quE4IT+Zdm9d7CNMeW2YS0Ic1JuxaFhOPCsRBQNE +05VqGfY8RBNxf/2K2+y/09fv0iZu9mwtHR/UWyaiuRamcNCa6BruFgEUGPixX5UwvmJ6gr +Y6tpQSrjoguzGpGBRN62QSuXNftLZI8Tu80zRqGYD4c0G4o1QP4E60LOSo3nh+UYD1NnuR +qkLvGqJEcr5nNqFDY8VHgHFtU/W5MOxqduMo6kELiEhJqjeGLiJSfKPSAnyK2aSFBL6gj/ +YpZIZbTX+T1buM5RcNK3ssZRNJbqwgtgfOZ58LAAAAAwEAAQAAAQEAxjzxFU0LGWtortSN +apaxnkPCZWuHm8gn6kILm3shg/IdPhORfrSxw1qF6ybcooN8LHPyd5D0oxaj70cMpK+vw2 +zNo/qdzh2UF9x375Dw4hL1lgslMM3EvXPbW7IJ9DnSYCAYfLyzr+ug8JsjaKJSjIvp2xYh +uLLKl9FzJhtGhzDaJr9FCbSmd5R7Telz4En0Lwo/VxYvyzCoRwzhVUVqJZpdtF7/1du4tT +agfPzPYY9zM9muR7AawtzMc4UFvMzl1OtjOHYtqSMVBZx44fRpXT3/fy7A98+7erd0zTWj +s6gaz6I8VmRPk4iTdBH4KBzC8dGZQNMrY9SQ/ZANet8eYQAAAIBI8hS6bX00NpNXwOaEqr +jZKf/u1W71KHpYBAY1w3xanGqPVOX5PEsFH6NLjqSLF75Bk22pvJvZ8EAaoISyvLSqqO5t +1vvjCjVKALSaVIFDcA20NpCXRugmVT1HeQNKHCTt3yOoraL8Sh9wlRbxLmlxgISYS6uH6e +dEPa6qFshMVwAAAIEA7RFx7+mZJfrSJUu9pYiZJc6+Ns2WrSA2mgI+mIhWqreDK4Kw0a5g +akqD0mb9oPHySnf3lCe+17yqNxH2fcX0G3B5LxiRnFVNm2wC4ZGvb+yMU2+0uSI/Sf8L1N +sfWm+z4VC93Qhe0fIpuk8JAMNOwCvFcEFu5rr9sxHtPWjWtj0AAACBAODYXjs6jqsvW+3P +e0efFp9kIezi8CejSxVXX0/zmWMCpTw1laiUmK41coKTKBSDZcNgrsF/ns1uCrPg6C2u/y +evF8J+DeqU3vo1QhnRAJA2fLZk1Dr/GfAsp9mS9w6FdIQiQjQ0f/X9rYgAr9x4qMogYgrG +zkb7k6FUoGgD7sbnAAAAE2xpYnNzaF90b3J0dXJlX2F1dGgBAgMEBQYH +-----END OPENSSH PRIVATE KEY----- diff -Nru libssh-0.9.7/tests/keys/certauth/id_rsa-cert.pub libssh-0.9.8/tests/keys/certauth/id_rsa-cert.pub --- libssh-0.9.7/tests/keys/certauth/id_rsa-cert.pub 2022-05-23 13:08:11.000000000 +0000 +++ libssh-0.9.8/tests/keys/certauth/id_rsa-cert.pub 2023-12-18 16:57:27.000000000 +0000 @@ -1 +1 @@ -ssh-rsa-cert-v01@openssh.com AAAAHHNzaC1yc2EtY2VydC12MDFAb3BlbnNzaC5jb20AAAAgHZLan4ufbTFWr8Hl/8JvZTLYa0eNNm2qov9zPlK7qfwAAAADAQABAAAA/QMTSsCQqarOIauonYgjAt8E+lgSWBU/43ITyDDzLM4IS4wCcqXB1Fagz386FU1B2AcUqlPZ1+7RlaXkqgKr4nGHv00U/GG+YAUgUAw1G12kI4cvrnWrFIXwcq+VTJNej5pHxEqcRLw7ZBorpqm2UsY5KLr5R3uMNap7koj1Hbt9lKsvfDn6HjM4qY0ygx8hxf/4wCzIh5V4k9/UAMkqI2CM9c3yEE2aWh/4MDOnAFj+0T2sMAo8jyOZ6v+W7hmEtsUc9mEv+5B+hhVeYO/RwxketJAQRPYDSPSi1mjtv9fnzGk15q/lHb2V/HP/pyIpao19A4daR0a4ia9Hk4UAAAAAAAAAAAAAAAEAAAATdG9ydHVyZV9hdXRoX2NhcmxvcwAAAAkAAAAFYWxpY2UAAAAAAAAAAP//////////AAAAAAAAAIIAAAAVcGVybWl0LVgxMS1mb3J3YXJkaW5nAAAAAAAAABdwZXJtaXQtYWdlbnQtZm9yd2FyZGluZwAAAAAAAAAWcGVybWl0LXBvcnQtZm9yd2FyZGluZwAAAAAAAAAKcGVybWl0LXB0eQAAAAAAAAAOcGVybWl0LXVzZXItcmMAAAAAAAAAAAAAARcAAAAHc3NoLXJzYQAAAAMBAAEAAAEBAKcDafm8fNluz8a9GQaWgk1XUJcchLleeubTke6xQlJbI+rcjWIIwd1gDuh7Mdr0YIVhsh6dpg/L4bpRJBGNhDPxK8BmjTpIU14lKxrWQAirHN09P2QGtGtgrf09lA+xhV9E+pkF2Zz6PCt/P3sgUQnJcwjjsWhMaSASrt67fPanH+10hnfgjkevkMMHGJxmLiOW7JFQkd9I+gHHKEXs6Q9fhtiStzr3WN4hAPG5uXrnRZgseAV9p3TFPMEgUTpdRvnkOnkCBF169KiyjU97QgoXHExWk/rrgsJtgrTou/qRyi18WWm9S1HXLHyNOgZxKirmxLNPC9dIcJBD1kDWG8UAAAEPAAAAB3NzaC1yc2EAAAEAhNLOXT0jyz/Web0HUyrtPCvUZsLkDyBWCNoNTfsxGVoYsE4WCpNwqQO1A4NT5AtIE+R7rn9wfjvXM7sYh6hJyq3HVEWhts1SkQVU7sQBrImTIrj2cWKR3gmQ+ehsgNFGhcFZTK77ugw1fMfzZRvKVTkRWhe6v92wQOtkoINtf3f1fK6xY+vLwAA/E4VdaRJmhwAaNpy3PfMAJytkCLjcjUSWHYDha4hs98/EBPduGNNNiZdyG7lcpSvvq9HBDxzOiHBa/We9m38/Dk4TNVkZ/wrtBFQxH75if6SgGa/feGJrKQHBru7sPh8dO4R1AmZaoLmRzMnzZOtB0oEXmBqHmw== libssh_torture_auth +ssh-rsa-cert-v01@openssh.com AAAAHHNzaC1yc2EtY2VydC12MDFAb3BlbnNzaC5jb20AAAAgllK1Wz9hM1kUks5QXU/vXbDmzpQWMtFWObMvi9ymg38AAAADAQABAAABAQDQN6GtT+iGiKYwqiMVd2jW4QByrHh9LzsCMNTPy3izjfqmk/7T5Tf6quE4IT+Zdm9d7CNMeW2YS0Ic1JuxaFhOPCsRBQNE05VqGfY8RBNxf/2K2+y/09fv0iZu9mwtHR/UWyaiuRamcNCa6BruFgEUGPixX5UwvmJ6grY6tpQSrjoguzGpGBRN62QSuXNftLZI8Tu80zRqGYD4c0G4o1QP4E60LOSo3nh+UYD1NnuRqkLvGqJEcr5nNqFDY8VHgHFtU/W5MOxqduMo6kELiEhJqjeGLiJSfKPSAnyK2aSFBL6gj/YpZIZbTX+T1buM5RcNK3ssZRNJbqwgtgfOZ58LAAAAAAAAAAAAAAABAAAAE3RvcnR1cmVfYXV0aF9jYXJsb3MAAAAJAAAABWFsaWNlAAAAAAAAAAD//////////wAAAAAAAACCAAAAFXBlcm1pdC1YMTEtZm9yd2FyZGluZwAAAAAAAAAXcGVybWl0LWFnZW50LWZvcndhcmRpbmcAAAAAAAAAFnBlcm1pdC1wb3J0LWZvcndhcmRpbmcAAAAAAAAACnBlcm1pdC1wdHkAAAAAAAAADnBlcm1pdC11c2VyLXJjAAAAAAAAAAAAAAEXAAAAB3NzaC1yc2EAAAADAQABAAABAQCnA2n5vHzZbs/GvRkGloJNV1CXHIS5Xnrm05HusUJSWyPq3I1iCMHdYA7oezHa9GCFYbIenaYPy+G6USQRjYQz8SvAZo06SFNeJSsa1kAIqxzdPT9kBrRrYK39PZQPsYVfRPqZBdmc+jwrfz97IFEJyXMI47FoTGkgEq7eu3z2px/tdIZ34I5Hr5DDBxicZi4jluyRUJHfSPoBxyhF7OkPX4bYkrc691jeIQDxubl650WYLHgFfad0xTzBIFE6XUb55Dp5AgRdevSoso1Pe0IKFxxMVpP664LCbYK06Lv6kcotfFlpvUtR1yx8jToGcSoq5sSzTwvXSHCQQ9ZA1hvFAAABFAAAAAxyc2Etc2hhMi01MTIAAAEAeI9eUAWyL5DVWBj0vU2kKdGxMXQx1Y8R68DXwXhnxfLwilJPa6IFg+g988lpF5aZzvAiX6TgDtJAhzfuBU+ZREGfdclUQIpz3xwDG76Gmg/DpQHdmqU76n2Na32s6+4SsSmWWKx6cPPdjbCRS0VMSrMohLuDyPGMoC7RjLfDDxqW5TIbMtqQdOiPl/0PpR73Q0FjB50Ec12buQDkExlEOi2Y+yB830vuTJN3ds7bx6NXM1Jjftg/8D0SzNRAIYDQFnpyXKO6kNrN66o48E3mrnVHXuFBTf+kpdYrK+1LKQVk/hVLBHr+NuVmQltL0zcjJfiXj7i5ZqcsqR1UAU5hKg== libssh_torture_auth diff -Nru libssh-0.9.7/tests/keys/certauth/id_rsa.pub libssh-0.9.8/tests/keys/certauth/id_rsa.pub --- libssh-0.9.7/tests/keys/certauth/id_rsa.pub 2022-05-23 13:08:11.000000000 +0000 +++ libssh-0.9.8/tests/keys/certauth/id_rsa.pub 2023-12-18 16:57:27.000000000 +0000 @@ -1 +1 @@ -ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAA/QMTSsCQqarOIauonYgjAt8E+lgSWBU/43ITyDDzLM4IS4wCcqXB1Fagz386FU1B2AcUqlPZ1+7RlaXkqgKr4nGHv00U/GG+YAUgUAw1G12kI4cvrnWrFIXwcq+VTJNej5pHxEqcRLw7ZBorpqm2UsY5KLr5R3uMNap7koj1Hbt9lKsvfDn6HjM4qY0ygx8hxf/4wCzIh5V4k9/UAMkqI2CM9c3yEE2aWh/4MDOnAFj+0T2sMAo8jyOZ6v+W7hmEtsUc9mEv+5B+hhVeYO/RwxketJAQRPYDSPSi1mjtv9fnzGk15q/lHb2V/HP/pyIpao19A4daR0a4ia9Hk4U= libssh_torture_auth +ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDQN6GtT+iGiKYwqiMVd2jW4QByrHh9LzsCMNTPy3izjfqmk/7T5Tf6quE4IT+Zdm9d7CNMeW2YS0Ic1JuxaFhOPCsRBQNE05VqGfY8RBNxf/2K2+y/09fv0iZu9mwtHR/UWyaiuRamcNCa6BruFgEUGPixX5UwvmJ6grY6tpQSrjoguzGpGBRN62QSuXNftLZI8Tu80zRqGYD4c0G4o1QP4E60LOSo3nh+UYD1NnuRqkLvGqJEcr5nNqFDY8VHgHFtU/W5MOxqduMo6kELiEhJqjeGLiJSfKPSAnyK2aSFBL6gj/YpZIZbTX+T1buM5RcNK3ssZRNJbqwgtgfOZ58L libssh_torture_auth diff -Nru libssh-0.9.7/tests/unittests/torture_config.c libssh-0.9.8/tests/unittests/torture_config.c --- libssh-0.9.7/tests/unittests/torture_config.c 2022-05-23 13:08:11.000000000 +0000 +++ libssh-0.9.8/tests/unittests/torture_config.c 2023-12-18 16:57:27.000000000 +0000 @@ -671,24 +671,40 @@ assert_string_equal(session->opts.ProxyCommand, "ssh -W [%h]:%p 2620:52:0::fed"); - /* Try to create some invalid configurations */ - /* Non-numeric port */ + /* Multiple @ is allowed in second jump */ torture_write_file(LIBSSH_TESTCONFIG11, - "Host bad-port\n" - "\tProxyJump jumpbox:22bad22\n" + "Host allowed-hostname\n" + "\tProxyJump localhost,user@principal.com@jumpbox:22\n" ""); torture_reset_config(session); - ssh_options_set(session, SSH_OPTIONS_HOST, "bad-port"); + ssh_options_set(session, SSH_OPTIONS_HOST, "allowed-hostname"); ret = ssh_config_parse_file(session, LIBSSH_TESTCONFIG11); - assert_ssh_return_code_equal(session, ret, SSH_ERROR); + assert_ssh_return_code(session, ret); + assert_string_equal(session->opts.ProxyCommand, + "ssh -J user@principal.com@jumpbox:22 -W [%h]:%p localhost"); - /* Too many @ */ + /* Multiple @ is allowed */ torture_write_file(LIBSSH_TESTCONFIG11, - "Host bad-hostname\n" + "Host allowed-hostname\n" "\tProxyJump user@principal.com@jumpbox:22\n" ""); torture_reset_config(session); - ssh_options_set(session, SSH_OPTIONS_HOST, "bad-hostname"); + ssh_options_set(session, SSH_OPTIONS_HOST, "allowed-hostname"); + ret = ssh_config_parse_file(session, LIBSSH_TESTCONFIG11); + assert_ssh_return_code(session, ret); + assert_string_equal(session->opts.ProxyCommand, + "ssh -l user@principal.com -p 22 -W [%h]:%p jumpbox"); + + /* In this part, we try various other config files and strings. */ + + /* Try to create some invalid configurations */ + /* Non-numeric port */ + torture_write_file(LIBSSH_TESTCONFIG11, + "Host bad-port\n" + "\tProxyJump jumpbox:22bad22\n" + ""); + torture_reset_config(session); + ssh_options_set(session, SSH_OPTIONS_HOST, "bad-port"); ret = ssh_config_parse_file(session, LIBSSH_TESTCONFIG11); assert_ssh_return_code_equal(session, ret, SSH_ERROR); @@ -752,16 +768,6 @@ ret = ssh_config_parse_file(session, LIBSSH_TESTCONFIG11); assert_ssh_return_code_equal(session, ret, SSH_ERROR); - /* Too many @ in second jump */ - torture_write_file(LIBSSH_TESTCONFIG11, - "Host bad-hostname\n" - "\tProxyJump localhost,user@principal.com@jumpbox:22\n" - ""); - torture_reset_config(session); - ssh_options_set(session, SSH_OPTIONS_HOST, "bad-hostname"); - ret = ssh_config_parse_file(session, LIBSSH_TESTCONFIG11); - assert_ssh_return_code_equal(session, ret, SSH_ERROR); - /* Braces mismatch in second jump */ torture_write_file(LIBSSH_TESTCONFIG11, "Host mismatch\n" diff -Nru libssh-0.9.7/tests/unittests/torture_misc.c libssh-0.9.8/tests/unittests/torture_misc.c --- libssh-0.9.7/tests/unittests/torture_misc.c 2022-05-23 13:08:11.000000000 +0000 +++ libssh-0.9.8/tests/unittests/torture_misc.c 2023-12-18 16:57:27.000000000 +0000 @@ -17,7 +17,14 @@ #include "misc.c" #include "error.c" +#ifdef _WIN32 +#include +#else +#include +#endif + #define TORTURE_TEST_DIR "/usr/local/bin/truc/much/.." +#define TORTURE_IPV6_LOCAL_LINK "fe80::98e1:82ff:fe8d:28b3%%%s" const char template[] = "temp_dir_XXXXXX"; @@ -656,6 +663,116 @@ assert_string_equal(buffer, "a\\nb\\n"); } +static void torture_ssh_check_hostname_syntax(void **state) +{ + int rc; + (void)state; + + rc = ssh_check_hostname_syntax("duckduckgo.com"); + assert_int_equal(rc, SSH_OK); + rc = ssh_check_hostname_syntax("www.libssh.org"); + assert_int_equal(rc, SSH_OK); + rc = ssh_check_hostname_syntax("Some-Thing.com"); + assert_int_equal(rc, SSH_OK); + rc = ssh_check_hostname_syntax("amazon.a23456789012345678901234567890123456789012345678901234567890123"); + assert_int_equal(rc, SSH_OK); + rc = ssh_check_hostname_syntax("amazon.a23456789012345678901234567890123456789012345678901234567890123.a23456789012345678901234567890123456789012345678901234567890123.ok"); + assert_int_equal(rc, SSH_OK); + rc = ssh_check_hostname_syntax("amazon.a23456789012345678901234567890123456789012345678901234567890123.a23456789012345678901234567890123456789012345678901234567890123.a23456789012345678901234567890123456789012345678901234567890123"); + assert_int_equal(rc, SSH_OK); + rc = ssh_check_hostname_syntax("lavabo-inter.innocentes-manus-meas"); + assert_int_equal(rc, SSH_OK); + rc = ssh_check_hostname_syntax("localhost"); + assert_int_equal(rc, SSH_OK); + rc = ssh_check_hostname_syntax("a"); + assert_int_equal(rc, SSH_OK); + rc = ssh_check_hostname_syntax("a-0.b-b"); + assert_int_equal(rc, SSH_OK); + rc = ssh_check_hostname_syntax("libssh."); + assert_int_equal(rc, SSH_OK); + + rc = ssh_check_hostname_syntax(NULL); + assert_int_equal(rc, SSH_ERROR); + rc = ssh_check_hostname_syntax(""); + assert_int_equal(rc, SSH_ERROR); + rc = ssh_check_hostname_syntax("/"); + assert_int_equal(rc, SSH_ERROR); + rc = ssh_check_hostname_syntax("@"); + assert_int_equal(rc, SSH_ERROR); + rc = ssh_check_hostname_syntax("["); + assert_int_equal(rc, SSH_ERROR); + rc = ssh_check_hostname_syntax("`"); + assert_int_equal(rc, SSH_ERROR); + rc = ssh_check_hostname_syntax("{"); + assert_int_equal(rc, SSH_ERROR); + rc = ssh_check_hostname_syntax("&"); + assert_int_equal(rc, SSH_ERROR); + rc = ssh_check_hostname_syntax("|"); + assert_int_equal(rc, SSH_ERROR); + rc = ssh_check_hostname_syntax("\""); + assert_int_equal(rc, SSH_ERROR); + rc = ssh_check_hostname_syntax("`"); + assert_int_equal(rc, SSH_ERROR); + rc = ssh_check_hostname_syntax(" "); + assert_int_equal(rc, SSH_ERROR); + rc = ssh_check_hostname_syntax("*the+giant&\"rooks\".c0m"); + assert_int_equal(rc, SSH_ERROR); + rc = ssh_check_hostname_syntax("!www.libssh.org"); + assert_int_equal(rc, SSH_ERROR); + rc = ssh_check_hostname_syntax("--.--"); + assert_int_equal(rc, SSH_ERROR); + rc = ssh_check_hostname_syntax("libssh.a234567890123456789012345678901234567890123456789012345678901234"); + assert_int_equal(rc, SSH_ERROR); + rc = ssh_check_hostname_syntax("libssh.a234567890123456789012345678901234567890123456789012345678901234.a234567890123456789012345678901234567890123456789012345678901234"); + assert_int_equal(rc, SSH_ERROR); + rc = ssh_check_hostname_syntax("libssh-"); + assert_int_equal(rc, SSH_ERROR); + rc = ssh_check_hostname_syntax("fe80::9656:d028:8652:66b6"); + assert_int_equal(rc, SSH_ERROR); + rc = ssh_check_hostname_syntax("."); + assert_int_equal(rc, SSH_ERROR); + rc = ssh_check_hostname_syntax(".."); + assert_int_equal(rc, SSH_ERROR); +} + +static void torture_ssh_is_ipaddr(void **state) { + int rc; + char *interf = malloc(64); + char *test_interf = malloc(128); + (void)state; + + assert_non_null(interf); + assert_non_null(test_interf); + rc = ssh_is_ipaddr("201.255.3.69"); + assert_int_equal(rc, 1); + rc = ssh_is_ipaddr("::1"); + assert_int_equal(rc, 1); + rc = ssh_is_ipaddr("2001:0db8:85a3:0000:0000:8a2e:0370:7334"); + assert_int_equal(rc, 1); + if_indextoname(1, interf); + assert_non_null(interf); + rc = sprintf(test_interf, TORTURE_IPV6_LOCAL_LINK, interf); + /* the "%%s" is not written */ + assert_int_equal(rc, strlen(interf) + strlen(TORTURE_IPV6_LOCAL_LINK) - 3); + rc = ssh_is_ipaddr(test_interf); + assert_int_equal(rc, 1); + free(interf); + free(test_interf); + + rc = ssh_is_ipaddr(".."); + assert_int_equal(rc, 0); + rc = ssh_is_ipaddr(":::"); + assert_int_equal(rc, 0); + rc = ssh_is_ipaddr("1.1.1.1.1"); + assert_int_equal(rc, 0); + rc = ssh_is_ipaddr("1.1"); + assert_int_equal(rc, 0); + rc = ssh_is_ipaddr("caesar"); + assert_int_equal(rc, 0); + rc = ssh_is_ipaddr("::xa:1"); + assert_int_equal(rc, 0); +} + int torture_run_tests(void) { int rc; struct CMUnitTest tests[] = { @@ -678,6 +795,8 @@ cmocka_unit_test(torture_ssh_newline_vis), cmocka_unit_test(torture_ssh_mkdirs), cmocka_unit_test(torture_ssh_quote_file_name), + cmocka_unit_test(torture_ssh_check_hostname_syntax), + cmocka_unit_test(torture_ssh_is_ipaddr), }; ssh_init();