Backport CVE patches from 0.11.5

Resolves: RHEL-213033
Resolves: RHEL-213041
Resolves: RHEL-213047
Resolves: RHEL-213081
Resolves: RHEL-213114
Resolves: RHEL-213119
Resolves: RHEL-213151
This commit is contained in:
Pavol Žáčik 2026-07-30 10:04:06 +02:00
parent 048ffad847
commit 9e3f4b36c8
No known key found for this signature in database
GPG Key ID: 4EE16C6E333F70A8
8 changed files with 1499 additions and 1 deletions

81
CVE-2026-59843.patch Normal file
View File

@ -0,0 +1,81 @@
From 7e70cb3bbe43a7edd786995a5434812545827f27 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Pavol=20=C5=BD=C3=A1=C4=8Dik?= <pzacik@redhat.com>
Date: Fri, 6 Mar 2026 13:58:30 +0100
Subject: [PATCH 01/12] CVE-2026-59843 channels: Fail when receiving max packet
size 0
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Do this both for SSH2_MSG_CHANNEL_OPEN and for
SSH2_MSG_CHANNEL_OPEN_CONFIRMATION. Using the
max packet size 0 would lead to an infinite loop
in channel_write_common.
Originally reported by Rinku Das on on 23th February.
Independently reported by Yi Lin on 26th February and
Haruto Kimura on 22nd March.
We do not consider this as a security issue as connecting
to untrusted servers on the internet brings much worse
security consequences than hanging your clinet.
Signed-off-by: Pavol Žáčik <pzacik@redhat.com>
Reviewed-by: Jakub Jelen <jjelen@redhat.com>
(cherry picked from commit 44b186fa17aff497dae420c59c003222e438103c)
---
src/channels.c | 7 +++++++
src/messages.c | 19 +++++++++++++++----
2 files changed, 22 insertions(+), 4 deletions(-)
diff --git a/src/channels.c b/src/channels.c
index 9cc8ab2f..2b84352f 100644
--- a/src/channels.c
+++ b/src/channels.c
@@ -192,6 +192,13 @@ SSH_PACKET_CALLBACK(ssh_packet_channel_open_conf){
if (rc != SSH_OK)
goto error;
+ if (channel->remote_maxpacket == 0) {
+ SSH_LOG(SSH_LOG_RARE,
+ "Invalid maximum packet size 0 in "
+ "SSH2_MSG_CHANNEL_OPEN_CONFIRMATION");
+ goto error;
+ }
+
SSH_LOG(SSH_LOG_DEBUG,
"Received a CHANNEL_OPEN_CONFIRMATION for channel %d:%d",
channel->local_channel,
diff --git a/src/messages.c b/src/messages.c
index ceb42b2c..04e95690 100644
--- a/src/messages.c
+++ b/src/messages.c
@@ -1160,10 +1160,21 @@ SSH_PACKET_CALLBACK(ssh_packet_channel_open){
SSH_LOG(SSH_LOG_PACKET,
"Clients wants to open a %s channel", type_c);
- ssh_buffer_unpack(packet,"ddd",
- &msg->channel_request_open.sender,
- &msg->channel_request_open.window,
- &msg->channel_request_open.packet_size);
+ rc = ssh_buffer_unpack(packet,
+ "ddd",
+ &msg->channel_request_open.sender,
+ &msg->channel_request_open.window,
+ &msg->channel_request_open.packet_size);
+ if (rc != SSH_OK){
+ goto error;
+ }
+
+ if (msg->channel_request_open.packet_size == 0) {
+ ssh_set_error(session,
+ SSH_FATAL,
+ "Invalid maximum packet size 0 in SSH2_MSG_CHANNEL_OPEN");
+ goto error;
+ }
if (session->session_state != SSH_SESSION_STATE_AUTHENTICATED){
ssh_set_error(session,SSH_FATAL, "Invalid state when receiving channel open request (must be authenticated)");
--
2.54.0

45
CVE-2026-59844.patch Normal file
View File

@ -0,0 +1,45 @@
From 7baf12bd38832bf7425de45e8f32d48af3e7fb7c Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Pavol=20=C5=BD=C3=A1=C4=8Dik?= <pzacik@redhat.com>
Date: Fri, 6 Mar 2026 18:05:29 +0100
Subject: [PATCH 02/12] CVE-2026-59844 sftpserver: cap accepted values of len
in SSH_FXP_READ
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
The client-provided length is directly used in
a malloc in process_read(), so not restricting it
leads to allocations bounded only by UINT32_MAX.
The new cap is the same as the one currently used
by OpenSSH.
Signed-off-by: Pavol Žáčik <pzacik@redhat.com>
Reviewed-by: Jakub Jelen <jjelen@redhat.com>
(cherry picked from commit 6dba2e06f0713c04ad5eca7d4315d0104be7e627)
---
src/sftpserver.c | 8 ++++++++
1 file changed, 8 insertions(+)
diff --git a/src/sftpserver.c b/src/sftpserver.c
index 528ef6f9..60f90714 100644
--- a/src/sftpserver.c
+++ b/src/sftpserver.c
@@ -105,6 +105,14 @@ sftp_client_message sftp_get_client_message(sftp_session sftp) {
sftp_client_message_free(msg);
return NULL;
}
+ if (msg->len > MAX_PACKET_LEN - 1024) {
+ ssh_set_error(sftp->session,
+ SSH_FATAL,
+ "Too large SSH_FXP_READ length: %" PRIu32,
+ msg->len);
+ sftp_client_message_free(msg);
+ return NULL;
+ }
break;
case SSH_FXP_WRITE:
rc = ssh_buffer_unpack(payload,
--
2.54.0

66
CVE-2026-59845.patch Normal file
View File

@ -0,0 +1,66 @@
From 1ebfc368a59d4fe72cbca2a0813e1166fa8b95ca Mon Sep 17 00:00:00 2001
From: Jakub Jelen <jjelen@redhat.com>
Date: Thu, 26 Mar 2026 16:32:24 +0100
Subject: [PATCH 03/12] CVE-2026-59845 socket: Properly check fork() return
code
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
During execution of proxy command, when fork() fails, its return value
is stored in pid and when the parent process attempts to kill it,
it sends the kill signal to all processes the calling application has
access to (except for init).
This caused nard to debug issues when the system under the load was hitting
fork failures, which resulted in killing of all the system processes
(of given user).
Reported and first patch iteration provided by: Halil Oktay (oblivionsage).
This code missing fork return value check is in libssh since 2010
(f31a14b7932ef4cc165ddd8f1f1a5b23eb21beb3), but this issue is exploitable only
since libssh 0.9.0 as previously there was no implementation of killing
ProxyCommand children.
Signed-off-by: Jakub Jelen <jjelen@redhat.com>
Reviewed-by: Pavol Žáčik <pzacik@redhat.com>
(cherry picked from commit 92b6fb9c5e2d1606e8f809fd884ab6dd4d3b7d45)
---
src/socket.c | 13 ++++++++++++-
1 file changed, 12 insertions(+), 1 deletion(-)
diff --git a/src/socket.c b/src/socket.c
index 35838e86..6300fcb2 100644
--- a/src/socket.c
+++ b/src/socket.c
@@ -942,6 +942,7 @@ ssh_execute_command(const char *command, socket_t in, socket_t out)
int
ssh_socket_connect_proxycommand(ssh_socket s, const char *command)
{
+ char err_msg[SSH_ERRNO_MSG_MAX] = {0};
socket_t pair[2];
int pid;
int rc;
@@ -959,7 +960,17 @@ ssh_socket_connect_proxycommand(ssh_socket s, const char *command)
pid = fork();
if (pid == 0) {
ssh_execute_command(command, pair[0], pair[0]);
- /* Does not return */
+ /* child: Does not return */
+ }
+ /* parent */
+ if (pid == -1) {
+ close(pair[0]);
+ close(pair[1]);
+ ssh_set_error(s->session,
+ SSH_FATAL,
+ "fork failed: %s",
+ ssh_strerror(errno, err_msg, SSH_ERRNO_MSG_MAX));
+ return SSH_ERROR;
}
s->proxy_pid = pid;
close(pair[0]);
--
2.54.0

317
CVE-2026-59846.patch Normal file
View File

@ -0,0 +1,317 @@
From d18c05327e8a6e9e8f7ddb26021a4fb7860e4afd Mon Sep 17 00:00:00 2001
From: Norbert Pocs <norbertpocs0@gmail.com>
Date: Wed, 27 Dec 2023 20:32:18 +0100
Subject: [PATCH 04/12] misc: Add function to check username syntax
Malicious code can be injected using the username with metacharacters,
therefore the username must be validated before using it with any %u.
Signed-off-by: Norbert Pocs <norbertpocs0@gmail.com>
Reviewed-by: Jakub Jelen <jjelen@redhat.com>
---
include/libssh/misc.h | 1 +
src/misc.c | 32 ++++++++++++++++++++++++++++++++
tests/unittests/torture_misc.c | 34 ++++++++++++++++++++++++++++++++++
3 files changed, 67 insertions(+)
diff --git a/include/libssh/misc.h b/include/libssh/misc.h
index 3452251d..06c62678 100644
--- a/include/libssh/misc.h
+++ b/include/libssh/misc.h
@@ -103,6 +103,7 @@ int ssh_tmpname(char *template);
char *ssh_strreplace(const char *src, const char *pattern, const char *repl);
int ssh_check_hostname_syntax(const char *hostname);
+int ssh_check_username_syntax(const char *username);
FILE *ssh_strict_fopen(const char *filename, size_t max_file_size);
diff --git a/src/misc.c b/src/misc.c
index 0cca373a..0389ee70 100644
--- a/src/misc.c
+++ b/src/misc.c
@@ -2148,4 +2148,36 @@ FILE *ssh_strict_fopen(const char *filename, size_t max_file_size)
return f;
}
+/**
+ * @brief Checks syntax of a username
+ *
+ * This check disallows metacharacters in the username
+ *
+ * @param username The username to be checked, has to be null terminated
+ *
+ * @return SSH_OK if the username passes syntax check
+ * SSH_ERROR otherwise or if username is NULL or empty string
+ */
+int ssh_check_username_syntax(const char *username)
+{
+ size_t username_len;
+
+ if (username == NULL || *username == '-') {
+ return SSH_ERROR;
+ }
+
+ username_len = strlen(username);
+ if (username_len == 0 || username[username_len - 1] == '\\' ||
+ strpbrk(username, "'`\";&<>|(){}") != NULL) {
+ return SSH_ERROR;
+ }
+ for (size_t i = 0; i < username_len; i++) {
+ if (isspace(username[i]) != 0 && username[i + 1] == '-') {
+ return SSH_ERROR;
+ }
+ }
+
+ return SSH_OK;
+}
+
/** @} */
diff --git a/tests/unittests/torture_misc.c b/tests/unittests/torture_misc.c
index 4470c358..7c134304 100644
--- a/tests/unittests/torture_misc.c
+++ b/tests/unittests/torture_misc.c
@@ -839,6 +839,39 @@ static void torture_ssh_check_hostname_syntax(void **state)
assert_int_equal(rc, SSH_ERROR);
}
+static void torture_ssh_check_username_syntax(void **state) {
+ int rc;
+ (void)state;
+
+ rc = ssh_check_username_syntax("username");
+ assert_int_equal(rc, SSH_OK);
+ rc = ssh_check_username_syntax("Alice");
+ assert_int_equal(rc, SSH_OK);
+ rc = ssh_check_username_syntax("Alice and Bob");
+ assert_int_equal(rc, SSH_OK);
+ rc = ssh_check_username_syntax("n4me?");
+ assert_int_equal(rc, SSH_OK);
+
+ rc = ssh_check_username_syntax("alice&bob");
+ assert_int_equal(rc, SSH_ERROR);
+ rc = ssh_check_username_syntax("backslash\\");
+ assert_int_equal(rc, SSH_ERROR);
+ rc = ssh_check_username_syntax("&var|()us\"<ha`r{}'");
+ assert_int_equal(rc, SSH_ERROR);
+ rc = ssh_check_username_syntax(" -");
+ assert_int_equal(rc, SSH_ERROR);
+ rc = ssh_check_username_syntax("me and -");
+ assert_int_equal(rc, SSH_ERROR);
+ rc = ssh_check_username_syntax("los -santos");
+ assert_int_equal(rc, SSH_ERROR);
+ rc = ssh_check_username_syntax("- who?");
+ assert_int_equal(rc, SSH_ERROR);
+ rc = ssh_check_username_syntax(NULL);
+ assert_int_equal(rc, SSH_ERROR);
+ rc = ssh_check_username_syntax("");
+ assert_int_equal(rc, SSH_ERROR);
+}
+
static void torture_ssh_is_ipaddr(void **state) {
int rc;
char *interf = malloc(64);
@@ -932,6 +965,7 @@ int torture_run_tests(void) {
cmocka_unit_test(torture_ssh_strreplace),
cmocka_unit_test(torture_ssh_strerror),
cmocka_unit_test(torture_ssh_check_hostname_syntax),
+ cmocka_unit_test(torture_ssh_check_username_syntax),
cmocka_unit_test(torture_ssh_is_ipaddr),
cmocka_unit_test(torture_ssh_get_hexa),
};
--
2.54.0
From b32217c6ed3a5a47e42f89fb68fe7ffec2c8f4bb Mon Sep 17 00:00:00 2001
From: Norbert Pocs <norbertpocs0@gmail.com>
Date: Thu, 28 Dec 2023 12:16:29 +0100
Subject: [PATCH 05/12] Check any input username for validity
Check possible inputs of username for malicious code.
Signed-off-by: Norbert Pocs <norbertpocs0@gmail.com>
Reviewed-by: Jakub Jelen <jjelen@redhat.com>
---
src/config_parser.c | 4 ++++
src/misc.c | 10 ++++++++--
src/options.c | 5 +++++
3 files changed, 17 insertions(+), 2 deletions(-)
diff --git a/src/config_parser.c b/src/config_parser.c
index 5555f6fd..9163d374 100644
--- a/src/config_parser.c
+++ b/src/config_parser.c
@@ -203,6 +203,10 @@ int ssh_config_parse_uri(const char *tok,
if (*username == NULL) {
goto error;
}
+ rc = ssh_check_username_syntax(*username);
+ if (rc != SSH_OK) {
+ goto error;
+ }
}
tok = endp + 1;
/* If there is second @ character, this does not look like our URI */
diff --git a/src/misc.c b/src/misc.c
index 0389ee70..b63b5521 100644
--- a/src/misc.c
+++ b/src/misc.c
@@ -183,6 +183,7 @@ char *ssh_get_local_username(void)
{
DWORD size = 0;
char *user;
+ int rc;
/* get the size */
GetUserName(NULL, &size);
@@ -193,7 +194,10 @@ char *ssh_get_local_username(void)
}
if (GetUserName(user, &size)) {
- return user;
+ rc = ssh_check_username_syntax(user);
+ if (rc == SSH_OK) {
+ return user;
+ }
}
return NULL;
@@ -337,8 +341,10 @@ char *ssh_get_local_username(void)
}
name = strdup(pwd.pw_name);
+ rc = ssh_check_username_syntax(name);
- if (name == NULL) {
+ if (rc != SSH_OK) {
+ free(name);
return NULL;
}
diff --git a/src/options.c b/src/options.c
index a8023fbf..a40459ff 100644
--- a/src/options.c
+++ b/src/options.c
@@ -662,6 +662,11 @@ int ssh_options_set(ssh_session session, enum ssh_options_e type,
ssh_set_error_oom(session);
return -1;
}
+ rc = ssh_check_username_syntax(session->opts.username);
+ if (rc != SSH_OK) {
+ ssh_set_error_invalid(session);
+ return -1;
+ }
}
break;
case SSH_OPTIONS_SSH_DIR:
--
2.54.0
From 26ae62df976dfd381b72f201ed4281530cd63d86 Mon Sep 17 00:00:00 2001
From: Norbert Pocs <norbertpocs0@gmail.com>
Date: Thu, 28 Dec 2023 12:27:31 +0100
Subject: [PATCH 06/12] torture: Add cases for username checks
Signed-off-by: Norbert Pocs <norbertpocs0@gmail.com>
Reviewed-by: Jakub Jelen <jjelen@redhat.com>
---
tests/unittests/torture_config.c | 3 +++
tests/unittests/torture_options.c | 6 ++++++
2 files changed, 9 insertions(+)
diff --git a/tests/unittests/torture_config.c b/tests/unittests/torture_config.c
index 6dc59e48..09c769b9 100644
--- a/tests/unittests/torture_config.c
+++ b/tests/unittests/torture_config.c
@@ -2293,6 +2293,9 @@ static void torture_config_parse_uri(void **state)
assert_null(username);
assert_string_equal(hostname, "1:2:3::4");
SAFE_FREE(hostname);
+
+ rc = ssh_config_parse_uri("user -name@", &username, NULL, NULL, true);
+ assert_int_equal(rc, SSH_ERROR);
}
/* Invalid configuration files
diff --git a/tests/unittests/torture_options.c b/tests/unittests/torture_options.c
index 2a7a04de..5c3d3f2c 100644
--- a/tests/unittests/torture_options.c
+++ b/tests/unittests/torture_options.c
@@ -89,6 +89,9 @@ static void torture_options_set_host(void **state) {
assert_non_null(session->opts.username);
assert_string_equal(session->opts.username, "at@login");
+ /* disallow metacharacters in the username */
+ rc = ssh_options_set(session, SSH_OPTIONS_HOST, "shallN()tP4ss -@hostname");
+ assert_ssh_return_code_equal(session, rc, SSH_ERROR);
}
static void torture_options_set_ciphers(void **state) {
@@ -396,6 +399,9 @@ static void torture_options_set_user(void **state) {
assert_true(rc == 0);
#endif /* _WIN32 */
+ rc = ssh_options_set(session, SSH_OPTIONS_USER, "&shallN()tP4ss");
+ assert_ssh_return_code_equal(session, rc, SSH_ERROR);
+
rc = ssh_options_set(session, SSH_OPTIONS_USER, "guru");
assert_true(rc == 0);
assert_string_equal(session->opts.username, "guru");
--
2.54.0
From 08a4f4c5151e75eda6d2105db493aa8c8df0dee7 Mon Sep 17 00:00:00 2001
From: Jakub Jelen <jjelen@redhat.com>
Date: Thu, 2 Apr 2026 15:39:25 +0200
Subject: [PATCH 07/12] CVE-2026-59846 Block shell metacharacters from
usernames
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
When an attacker could sneak the dollar sign or backslash into the username
expanded for example in proxy command, it can result in printing environment
variables that might contain secrets.
This is a fixup of CVE-2023-6004 which fixed this for hostnames, but these
two metacharacters were left out from the username filter.
This keeps the list in one place to simplify maintenance.
Signed-off-by: Jakub Jelen <jjelen@redhat.com>
Reviewed-by: Pavol Žáčik <pzacik@redhat.com>
(cherry picked from commit 6309df220e3431deb41946f892f4bb5af8b59dba)
---
include/libssh/priv.h | 2 ++
src/misc.c | 2 +-
2 files changed, 3 insertions(+), 1 deletion(-)
diff --git a/include/libssh/priv.h b/include/libssh/priv.h
index 876ea4ed..22f1e6da 100644
--- a/include/libssh/priv.h
+++ b/include/libssh/priv.h
@@ -223,6 +223,8 @@ int ssh_gettimeofday(struct timeval *__p, void *__t);
# define LIBSSH_MEM_PROTECTION
#endif
+#define SSH_DANGEROUS_SHELL_CHARS "'`\";&<>|(){}$\\,"
+
/* forward declarations */
struct ssh_common_struct;
struct ssh_kex_struct;
diff --git a/src/misc.c b/src/misc.c
index b63b5521..f4f48a37 100644
--- a/src/misc.c
+++ b/src/misc.c
@@ -2174,7 +2174,7 @@ int ssh_check_username_syntax(const char *username)
username_len = strlen(username);
if (username_len == 0 || username[username_len - 1] == '\\' ||
- strpbrk(username, "'`\";&<>|(){}") != NULL) {
+ strpbrk(username, SSH_DANGEROUS_SHELL_CHARS) != NULL) {
return SSH_ERROR;
}
for (size_t i = 0; i < username_len; i++) {
--
2.54.0

69
CVE-2026-59847.patch Normal file
View File

@ -0,0 +1,69 @@
From ee32a1916a5d5545d4ac470646c0e023a152a406 Mon Sep 17 00:00:00 2001
From: Jakub Jelen <jjelen@redhat.com>
Date: Fri, 15 May 2026 17:01:21 +0200
Subject: [PATCH 08/12] CVE-2026-59847 libcrypto: Fix tag verification of
AES-GCM ciphers
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
EVP_DecryptFinal() returns 0 errors, which was wrongly checked since
its introduction.
Reported by Ben Smyth discuss@bensmyth.com
Signed-off-by: Jakub Jelen <jjelen@redhat.com>
Reviewed-by: Pavol Žáčik <pzacik@redhat.com>
(cherry picked from commit 6d6cb6cb4d1684bb3f2b9fd23f3635e79d1e5495)
---
src/libcrypto.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/libcrypto.c b/src/libcrypto.c
index 658bbcd0..4353d006 100644
--- a/src/libcrypto.c
+++ b/src/libcrypto.c
@@ -680,7 +680,7 @@ evp_cipher_aead_decrypt(struct ssh_cipher_struct *cipher,
rc = EVP_DecryptFinal(cipher->ctx,
NULL,
&outlen);
- if (rc < 0) {
+ if (rc != 1 || outlen != 0) {
SSH_LOG(SSH_LOG_TRACE, "EVP_DecryptFinal failed: Failed authentication");
return SSH_ERROR;
}
--
2.54.0
From a7f55e18f9f7cb5ba3f71d8701168aea9d5a670f Mon Sep 17 00:00:00 2001
From: Jakub Jelen <jjelen@redhat.com>
Date: Mon, 18 May 2026 08:56:31 +0200
Subject: [PATCH 09/12] CVE-2026-59847 libcrypto: Fix symmetric issue during
encryption
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Signed-off-by: Jakub Jelen <jjelen@redhat.com>
Reviewed-by: Pavol Žáčik <pzacik@redhat.com>
(cherry picked from commit a5173c6ad249f7960bc7c1cc75a6a05ead8e3eba)
---
src/libcrypto.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/libcrypto.c b/src/libcrypto.c
index 4353d006..2f11caca 100644
--- a/src/libcrypto.c
+++ b/src/libcrypto.c
@@ -592,7 +592,7 @@ evp_cipher_aead_encrypt(struct ssh_cipher_struct *cipher,
rc = EVP_EncryptFinal(cipher->ctx,
NULL,
&tmplen);
- if (rc < 0) {
+ if (rc != 1) {
SSH_LOG(SSH_LOG_TRACE, "EVP_EncryptFinal failed: Failed to create a tag");
return;
}
--
2.54.0

866
CVE-2026-59848.patch Normal file
View File

@ -0,0 +1,866 @@
From 215615da6a174beaeafeccd69ae952d4106c6412 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Pavol=20=C5=BD=C3=A1=C4=8Dik?= <pzacik@redhat.com>
Date: Mon, 1 Jun 2026 16:33:03 +0200
Subject: [PATCH 10/12] CVE-2026-59848 sftp: handle responses with unknown
request IDs
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
This adds a new field to sftp_session_struct,
containing a list of outstanding request IDs.
An ID is added to the list when a request
is constructed and removed when the corresponding
request is received. If a client receives a response
with an unknown request ID, it reports an error.
Storing responses with unknown request IDs in
the response queue could be abused by a malicious
SFTP server which could deplete client memory
this way.
Signed-off-by: Pavol Žáčik <pzacik@redhat.com>
Reviewed-by: Jakub Jelen <jjelen@redhat.com>
(cherry picked from commit 26147eb4767937c797f97ff3b1b1663384232417)
---
include/libssh/sftp.h | 1 +
include/libssh/sftp_priv.h | 12 ++
src/sftp.c | 216 ++++++++++++++++++++-----
tests/client/CMakeLists.txt | 1 +
tests/client/torture_sftp_request_id.c | 183 +++++++++++++++++++++
5 files changed, 370 insertions(+), 43 deletions(-)
create mode 100644 tests/client/torture_sftp_request_id.c
diff --git a/include/libssh/sftp.h b/include/libssh/sftp.h
index c855df8a..ef3a61a5 100644
--- a/include/libssh/sftp.h
+++ b/include/libssh/sftp.h
@@ -90,6 +90,7 @@ struct sftp_session_struct {
void **handles;
sftp_ext ext;
sftp_packet read_packet;
+ struct ssh_list *outstanding_ids;
};
struct sftp_packet_struct {
diff --git a/include/libssh/sftp_priv.h b/include/libssh/sftp_priv.h
index ccde008a..56a1725c 100644
--- a/include/libssh/sftp_priv.h
+++ b/include/libssh/sftp_priv.h
@@ -29,4 +29,16 @@ sftp_attributes sftp_parse_attr(sftp_session session,
ssh_buffer buf,
int expectname);
+/**
+ * @brief Assigns a new SFTP ID for new requests and assures there is no
+ * collision between them.
+ *
+ * @param sftp The sftp session handle.
+ * @param id_out Pointer to store the new ID.
+ *
+ * @returns SSH_OK on success with the new ID stored in *id
+ * @returns SSH_ERROR on failure with the sftp and ssh errors set
+ */
+int sftp_get_new_id(sftp_session sftp, uint32_t *id_out);
+
#endif /* SFTP_PRIV_H */
diff --git a/src/sftp.c b/src/sftp.c
index 70f9ed15..39f0639c 100644
--- a/src/sftp.c
+++ b/src/sftp.c
@@ -149,6 +149,12 @@ sftp_session sftp_new(ssh_session session)
goto error;
}
+ sftp->outstanding_ids = ssh_list_new();
+ if (sftp->outstanding_ids == NULL) {
+ ssh_set_error_oom(session);
+ goto error;
+ }
+
if (ssh_channel_open_session(sftp->channel)) {
goto error;
}
@@ -165,6 +171,7 @@ error:
if (sftp->channel != NULL) {
ssh_channel_free(sftp->channel);
}
+ ssh_list_free(sftp->outstanding_ids);
if (sftp->read_packet != NULL) {
if (sftp->read_packet->payload != NULL) {
SSH_BUFFER_FREE(sftp->read_packet->payload);
@@ -196,6 +203,12 @@ sftp_new_channel(ssh_session session, ssh_channel channel)
goto error;
}
+ sftp->outstanding_ids = ssh_list_new();
+ if (sftp->outstanding_ids == NULL) {
+ ssh_set_error_oom(session);
+ goto error;
+ }
+
sftp->read_packet = calloc(1, sizeof(struct sftp_packet_struct));
if (sftp->read_packet == NULL) {
ssh_set_error_oom(session);
@@ -217,6 +230,7 @@ error:
if (sftp->ext != NULL) {
sftp_ext_free(sftp->ext);
}
+ ssh_list_free(sftp->outstanding_ids);
if (sftp->read_packet != NULL) {
if (sftp->read_packet->payload != NULL) {
SSH_BUFFER_FREE(sftp->read_packet->payload);
@@ -358,6 +372,7 @@ void sftp_server_free(sftp_session sftp)
void sftp_free(sftp_session sftp)
{
sftp_request_queue ptr;
+ struct ssh_iterator *id_it = NULL;
if (sftp == NULL) {
return;
@@ -384,6 +399,12 @@ void sftp_free(sftp_session sftp)
sftp_ext_free(sftp->ext);
+ id_it = ssh_list_get_iterator(sftp->outstanding_ids);
+ for (; id_it != NULL; id_it = id_it->next) {
+ free((uint32_t *)id_it->data);
+ }
+ ssh_list_free(sftp->outstanding_ids);
+
SAFE_FREE(sftp);
}
@@ -571,6 +592,8 @@ static sftp_message sftp_get_message(sftp_packet packet)
{
sftp_session sftp = packet->sftp;
sftp_message msg = NULL;
+ struct ssh_iterator *id_it = NULL;
+ bool id_found = false;
int rc;
switch(packet->type) {
@@ -618,6 +641,28 @@ static sftp_message sftp_get_message(sftp_packet packet)
msg->id,
msg->packet_type);
+ /* Validate that this ID is in our outstanding requests list */
+ id_it = ssh_list_get_iterator(sftp->outstanding_ids);
+ for (; id_it != NULL; id_it = id_it->next) {
+ uint32_t *stored_id = (uint32_t *)id_it->data;
+ if (*stored_id == msg->id) {
+ id_found = true;
+ ssh_list_remove(sftp->outstanding_ids, id_it);
+ free(stored_id);
+ break;
+ }
+ }
+
+ if (!id_found) {
+ ssh_set_error(packet->sftp->session,
+ SSH_FATAL,
+ "Unknown request ID %" PRIu32,
+ msg->id);
+ sftp_message_free(msg);
+ sftp_set_error(packet->sftp, SSH_FX_FAILURE);
+ return NULL;
+ }
+
return msg;
}
@@ -902,13 +947,36 @@ static sftp_message sftp_dequeue(sftp_session sftp, uint32_t id){
return NULL;
}
-/*
- * Assigns a new SFTP ID for new requests and assures there is no collision
- * between them.
- * Returns a new ID ready to use in a request
- */
-static inline uint32_t sftp_get_new_id(sftp_session session) {
- return ++session->id_counter;
+int sftp_get_new_id(sftp_session sftp, uint32_t *id_out)
+{
+ uint32_t *id = NULL;
+ int rc;
+
+ if (id_out == NULL) {
+ ssh_set_error_invalid(sftp->session);
+ sftp_set_error(sftp, SSH_FX_FAILURE);
+ return SSH_ERROR;
+ }
+
+ id = malloc(sizeof(uint32_t));
+ if (id == NULL) {
+ ssh_set_error_oom(sftp->session);
+ sftp_set_error(sftp, SSH_FX_FAILURE);
+ return SSH_ERROR;
+ }
+
+ *id = ++sftp->id_counter;
+ rc = ssh_list_append(sftp->outstanding_ids, id);
+ if (rc != SSH_OK) {
+ free(id);
+ ssh_set_error_oom(sftp->session);
+ sftp_set_error(sftp, SSH_FX_FAILURE);
+ return SSH_ERROR;
+ }
+
+ *id_out = *id;
+
+ return SSH_OK;
}
static sftp_status_message parse_status_msg(sftp_message msg){
@@ -1022,6 +1090,11 @@ sftp_dir sftp_opendir(sftp_session sftp, const char *path)
return NULL;
}
+ rc = sftp_get_new_id(sftp, &id);
+ if (rc != SSH_OK) {
+ return NULL;
+ }
+
payload = ssh_buffer_new();
if (payload == NULL) {
ssh_set_error_oom(sftp->session);
@@ -1029,8 +1102,6 @@ sftp_dir sftp_opendir(sftp_session sftp, const char *path)
return NULL;
}
- id = sftp_get_new_id(sftp);
-
rc = ssh_buffer_pack(payload,
"ds",
id,
@@ -1564,6 +1635,11 @@ sftp_attributes sftp_readdir(sftp_session sftp, sftp_dir dir)
int rc;
if (dir->buffer == NULL) {
+ rc = sftp_get_new_id(sftp, &id);
+ if (rc != SSH_OK) {
+ return NULL;
+ }
+
payload = ssh_buffer_new();
if (payload == NULL) {
ssh_set_error_oom(sftp->session);
@@ -1571,8 +1647,6 @@ sftp_attributes sftp_readdir(sftp_session sftp, sftp_dir dir)
return NULL;
}
- id = sftp_get_new_id(sftp);
-
rc = ssh_buffer_pack(payload,
"dS",
id,
@@ -1697,6 +1771,11 @@ static int sftp_handle_close(sftp_session sftp, ssh_string handle)
uint32_t id;
int rc;
+ rc = sftp_get_new_id(sftp, &id);
+ if (rc != SSH_OK) {
+ return -1;
+ }
+
buffer = ssh_buffer_new();
if (buffer == NULL) {
ssh_set_error_oom(sftp->session);
@@ -1704,8 +1783,6 @@ static int sftp_handle_close(sftp_session sftp, ssh_string handle)
return -1;
}
- id = sftp_get_new_id(sftp);
-
rc = ssh_buffer_pack(buffer,
"dS",
id,
@@ -1812,6 +1889,11 @@ sftp_file sftp_open(sftp_session sftp,
uint32_t id;
int rc;
+ rc = sftp_get_new_id(sftp, &id);
+ if (rc != SSH_OK) {
+ return NULL;
+ }
+
buffer = ssh_buffer_new();
if (buffer == NULL) {
ssh_set_error_oom(sftp->session);
@@ -1839,7 +1921,6 @@ sftp_file sftp_open(sftp_session sftp,
sftp_flags |= SSH_FXF_APPEND;
}
SSH_LOG(SSH_LOG_PACKET,"Opening file %s with sftp flags %x",file,sftp_flags);
- id = sftp_get_new_id(sftp);
rc = ssh_buffer_pack(buffer,
"dsd",
@@ -1955,7 +2036,10 @@ ssize_t sftp_read(sftp_file handle, void *buf, size_t count) {
return -1;
}
- id = sftp_get_new_id(handle->sftp);
+ rc = sftp_get_new_id(handle->sftp, &id);
+ if (rc != SSH_OK) {
+ return -1;
+ }
rc = ssh_buffer_pack(buffer,
"dSqd",
@@ -2056,7 +2140,10 @@ int sftp_async_read_begin(sftp_file file, uint32_t len){
return -1;
}
- id = sftp_get_new_id(sftp);
+ rc = sftp_get_new_id(sftp, &id);
+ if (rc != SSH_OK) {
+ return -1;
+ }
rc = ssh_buffer_pack(buffer,
"dSqd",
@@ -2187,7 +2274,10 @@ ssize_t sftp_write(sftp_file file, const void *buf, size_t count) {
return -1;
}
- id = sftp_get_new_id(file->sftp);
+ rc = sftp_get_new_id(file->sftp, &id);
+ if (rc != SSH_OK) {
+ return -1;
+ }
rc = ssh_buffer_pack(buffer,
"dSqdP",
@@ -2298,6 +2388,11 @@ int sftp_unlink(sftp_session sftp, const char *file) {
uint32_t id;
int rc;
+ rc = sftp_get_new_id(sftp, &id);
+ if (rc != SSH_OK) {
+ return -1;
+ }
+
buffer = ssh_buffer_new();
if (buffer == NULL) {
ssh_set_error_oom(sftp->session);
@@ -2305,8 +2400,6 @@ int sftp_unlink(sftp_session sftp, const char *file) {
return -1;
}
- id = sftp_get_new_id(sftp);
-
rc = ssh_buffer_pack(buffer,
"ds",
id,
@@ -2373,6 +2466,11 @@ int sftp_rmdir(sftp_session sftp, const char *directory) {
uint32_t id;
int rc;
+ rc = sftp_get_new_id(sftp, &id);
+ if (rc != SSH_OK) {
+ return -1;
+ }
+
buffer = ssh_buffer_new();
if (buffer == NULL) {
ssh_set_error_oom(sftp->session);
@@ -2380,8 +2478,6 @@ int sftp_rmdir(sftp_session sftp, const char *directory) {
return -1;
}
- id = sftp_get_new_id(sftp);
-
rc = ssh_buffer_pack(buffer,
"ds",
id,
@@ -2446,6 +2542,11 @@ int sftp_mkdir(sftp_session sftp, const char *directory, mode_t mode)
uint32_t id;
int rc;
+ rc = sftp_get_new_id(sftp, &id);
+ if (rc != SSH_OK) {
+ return -1;
+ }
+
buffer = ssh_buffer_new();
if (buffer == NULL) {
ssh_set_error_oom(sftp->session);
@@ -2457,8 +2558,6 @@ int sftp_mkdir(sftp_session sftp, const char *directory, mode_t mode)
attr.permissions = mode;
attr.flags = SSH_FILEXFER_ATTR_PERMISSIONS;
- id = sftp_get_new_id(sftp);
-
rc = ssh_buffer_pack(buffer,
"ds",
id,
@@ -2545,6 +2644,11 @@ int sftp_rename(sftp_session sftp, const char *original, const char *newname) {
uint32_t id;
int rc;
+ rc = sftp_get_new_id(sftp, &id);
+ if (rc != SSH_OK) {
+ return -1;
+ }
+
buffer = ssh_buffer_new();
if (buffer == NULL) {
ssh_set_error_oom(sftp->session);
@@ -2552,8 +2656,6 @@ int sftp_rename(sftp_session sftp, const char *original, const char *newname) {
return -1;
}
- id = sftp_get_new_id(sftp);
-
rc = ssh_buffer_pack(buffer,
"dss",
id,
@@ -2629,6 +2731,11 @@ int sftp_setstat(sftp_session sftp, const char *file, sftp_attributes attr)
sftp_status_message status = NULL;
int rc;
+ rc = sftp_get_new_id(sftp, &id);
+ if (rc != SSH_OK) {
+ return -1;
+ }
+
buffer = ssh_buffer_new();
if (buffer == NULL) {
ssh_set_error_oom(sftp->session);
@@ -2636,8 +2743,6 @@ int sftp_setstat(sftp_session sftp, const char *file, sftp_attributes attr)
return -1;
}
- id = sftp_get_new_id(sftp);
-
rc = ssh_buffer_pack(buffer,
"ds",
id,
@@ -2759,6 +2864,11 @@ int sftp_symlink(sftp_session sftp, const char *target, const char *dest) {
return -1;
}
+ rc = sftp_get_new_id(sftp, &id);
+ if (rc != SSH_OK) {
+ return -1;
+ }
+
buffer = ssh_buffer_new();
if (buffer == NULL) {
ssh_set_error_oom(sftp->session);
@@ -2766,8 +2876,6 @@ int sftp_symlink(sftp_session sftp, const char *target, const char *dest) {
return -1;
}
- id = sftp_get_new_id(sftp);
-
/* TODO check for version number if they ever fix it. */
if (ssh_get_openssh_version(sftp->session)) {
rc = ssh_buffer_pack(buffer,
@@ -2857,6 +2965,12 @@ char *sftp_readlink(sftp_session sftp, const char *path)
sftp_set_error(sftp, SSH_FX_FAILURE);
return NULL;
}
+
+ rc = sftp_get_new_id(sftp, &id);
+ if (rc != SSH_OK) {
+ return NULL;
+ }
+
buffer = ssh_buffer_new();
if (buffer == NULL) {
ssh_set_error_oom(sftp->session);
@@ -2864,8 +2978,6 @@ char *sftp_readlink(sftp_session sftp, const char *path)
return NULL;
}
- id = sftp_get_new_id(sftp);
-
rc = ssh_buffer_pack(buffer,
"ds",
id,
@@ -2983,6 +3095,11 @@ sftp_statvfs_t sftp_statvfs(sftp_session sftp, const char *path)
return NULL;
}
+ rc = sftp_get_new_id(sftp, &id);
+ if (rc != SSH_OK) {
+ return NULL;
+ }
+
buffer = ssh_buffer_new();
if (buffer == NULL) {
ssh_set_error_oom(sftp->session);
@@ -2990,8 +3107,6 @@ sftp_statvfs_t sftp_statvfs(sftp_session sftp, const char *path)
return NULL;
}
- id = sftp_get_new_id(sftp);
-
rc = ssh_buffer_pack(buffer,
"dss",
id,
@@ -3058,6 +3173,11 @@ int sftp_fsync(sftp_file file)
}
sftp = file->sftp;
+ rc = sftp_get_new_id(sftp, &id);
+ if (rc != SSH_OK) {
+ return -1;
+ }
+
buffer = ssh_buffer_new();
if (buffer == NULL) {
ssh_set_error_oom(sftp->session);
@@ -3065,8 +3185,6 @@ int sftp_fsync(sftp_file file)
return -1;
}
- id = sftp_get_new_id(sftp);
-
rc = ssh_buffer_pack(buffer,
"dsS",
id,
@@ -3158,6 +3276,11 @@ sftp_statvfs_t sftp_fstatvfs(sftp_file file)
}
sftp = file->sftp;
+ rc = sftp_get_new_id(sftp, &id);
+ if (rc != SSH_OK) {
+ return NULL;
+ }
+
buffer = ssh_buffer_new();
if (buffer == NULL) {
ssh_set_error_oom(sftp->session);
@@ -3165,8 +3288,6 @@ sftp_statvfs_t sftp_fstatvfs(sftp_file file)
return NULL;
}
- id = sftp_get_new_id(sftp);
-
rc = ssh_buffer_pack(buffer,
"dsS",
id,
@@ -3245,6 +3366,11 @@ char *sftp_canonicalize_path(sftp_session sftp, const char *path)
return NULL;
}
+ rc = sftp_get_new_id(sftp, &id);
+ if (rc != SSH_OK) {
+ return NULL;
+ }
+
buffer = ssh_buffer_new();
if (buffer == NULL) {
ssh_set_error_oom(sftp->session);
@@ -3252,8 +3378,6 @@ char *sftp_canonicalize_path(sftp_session sftp, const char *path)
return NULL;
}
- id = sftp_get_new_id(sftp);
-
rc = ssh_buffer_pack(buffer,
"ds",
id,
@@ -3336,6 +3460,11 @@ static sftp_attributes sftp_xstat(sftp_session sftp,
return NULL;
}
+ rc = sftp_get_new_id(sftp, &id);
+ if (rc != SSH_OK) {
+ return NULL;
+ }
+
buffer = ssh_buffer_new();
if (buffer == NULL) {
ssh_set_error_oom(sftp->session);
@@ -3343,8 +3472,6 @@ static sftp_attributes sftp_xstat(sftp_session sftp,
return NULL;
}
- id = sftp_get_new_id(sftp);
-
rc = ssh_buffer_pack(buffer,
"ds",
id,
@@ -3414,6 +3541,11 @@ sftp_attributes sftp_fstat(sftp_file file)
return NULL;
}
+ rc = sftp_get_new_id(file->sftp, &id);
+ if (rc != SSH_OK) {
+ return NULL;
+ }
+
buffer = ssh_buffer_new();
if (buffer == NULL) {
ssh_set_error_oom(file->sftp->session);
@@ -3421,8 +3553,6 @@ sftp_attributes sftp_fstat(sftp_file file)
return NULL;
}
- id = sftp_get_new_id(file->sftp);
-
rc = ssh_buffer_pack(buffer,
"dS",
id,
diff --git a/tests/client/CMakeLists.txt b/tests/client/CMakeLists.txt
index 71e5182e..864478a7 100644
--- a/tests/client/CMakeLists.txt
+++ b/tests/client/CMakeLists.txt
@@ -49,6 +49,7 @@ if (WITH_SFTP)
torture_sftp_dir
torture_sftp_read
torture_sftp_fsync
+ torture_sftp_request_id
${SFTP_BENCHMARK_TESTS})
endif (WITH_SFTP)
diff --git a/tests/client/torture_sftp_request_id.c b/tests/client/torture_sftp_request_id.c
new file mode 100644
index 00000000..21f2774a
--- /dev/null
+++ b/tests/client/torture_sftp_request_id.c
@@ -0,0 +1,183 @@
+#include "config.h"
+
+#define LIBSSH_STATIC
+
+#include "sftp.c"
+#include "torture.h"
+
+#include <pwd.h>
+#include <sys/types.h>
+
+static int sshd_setup(void **state)
+{
+ torture_setup_sshd_server(state, false);
+
+ return 0;
+}
+
+static int sshd_teardown(void **state)
+{
+ torture_teardown_sshd_server(state);
+
+ return 0;
+}
+
+static int session_setup(void **state)
+{
+ struct torture_state *s = *state;
+ struct passwd *pwd = NULL;
+ int rc;
+
+ pwd = getpwnam("bob");
+ assert_non_null(pwd);
+
+ rc = setuid(pwd->pw_uid);
+ assert_return_code(rc, errno);
+
+ s->ssh.session = torture_ssh_session(s,
+ TORTURE_SSH_SERVER,
+ NULL,
+ TORTURE_SSH_USER_ALICE,
+ NULL);
+ assert_non_null(s->ssh.session);
+
+ s->ssh.tsftp = torture_sftp_session(s->ssh.session);
+ assert_non_null(s->ssh.tsftp);
+
+ return 0;
+}
+
+static int session_teardown(void **state)
+{
+ struct torture_state *s = *state;
+
+ torture_rmdirs(s->ssh.tsftp->testdir);
+ torture_sftp_close(s->ssh.tsftp);
+ ssh_disconnect(s->ssh.session);
+ ssh_free(s->ssh.session);
+
+ return 0;
+}
+
+static void torture_sftp_request_id_null(void **state)
+{
+ struct torture_state *s = *state;
+ struct torture_sftp *t = s->ssh.tsftp;
+ sftp_session sftp = t->sftp;
+ int rc;
+
+ rc = sftp_get_new_id(sftp, NULL);
+ assert_int_equal(rc, SSH_ERROR);
+}
+
+static void torture_sftp_request_id_add(void **state)
+{
+ struct torture_state *s = *state;
+ struct torture_sftp *t = s->ssh.tsftp;
+ sftp_session sftp = t->sftp;
+ uint32_t id1, id2;
+ int rc;
+ size_t count;
+
+ /* The list of IDs should be empty at first */
+ count = ssh_list_count(sftp->outstanding_ids);
+ assert_int_equal(count, 0);
+
+ /* Request a new ID */
+ rc = sftp_get_new_id(sftp, &id1);
+ assert_int_equal(rc, SSH_OK);
+
+ /* Check that the list has one ID now */
+ count = ssh_list_count(sftp->outstanding_ids);
+ assert_int_equal(count, 1);
+
+ /* Request another ID */
+ rc = sftp_get_new_id(sftp, &id2);
+ assert_int_equal(rc, SSH_OK);
+
+ /* Check that the IDs differ */
+ assert_int_not_equal(id1, id2);
+
+ /* Check that the list has two IDs now */
+ count = ssh_list_count(sftp->outstanding_ids);
+ assert_int_equal(count, 2);
+}
+
+static void torture_sftp_request_id_remove(void **state)
+{
+ struct torture_state *s = *state;
+ struct torture_sftp *t = s->ssh.tsftp;
+ sftp_session sftp = t->sftp;
+ sftp_attributes attr = NULL;
+ size_t count;
+
+ count = ssh_list_count(sftp->outstanding_ids);
+ assert_int_equal(count, 0);
+
+ /* We send a request and receive a response */
+ attr = sftp_stat(sftp, SSH_EXECUTABLE);
+ assert_non_null(attr);
+
+ /* The number of outstanding requests should be back to 0 */
+ count = ssh_list_count(sftp->outstanding_ids);
+ assert_int_equal(count, 0);
+
+ sftp_attributes_free(attr);
+}
+
+static void torture_sftp_request_id_unknown(void **state)
+{
+ struct torture_state *s = *state;
+ struct torture_sftp *t = s->ssh.tsftp;
+ sftp_session sftp = t->sftp;
+ ssh_buffer buffer = NULL;
+ sftp_message msg = NULL;
+ uint32_t id = 0;
+ int rc;
+ size_t count;
+
+ count = ssh_list_count(sftp->outstanding_ids);
+ assert_int_equal(count, 0);
+
+ buffer = ssh_buffer_new();
+ assert_non_null(buffer);
+
+ rc = ssh_buffer_pack(buffer, "ds", id, "/tmp");
+ assert_int_equal(rc, SSH_OK);
+
+ /* Send a request without saving the request ID */
+ rc = sftp_packet_write(sftp, SSH_FXP_OPENDIR, buffer);
+ assert_int_not_equal(rc, -1);
+ SSH_BUFFER_FREE(buffer);
+
+ /* An attempt to receive the response should fail */
+ rc = sftp_recv_response_msg(sftp, id, true, &msg);
+ assert_int_equal(rc, SSH_ERROR);
+}
+
+int torture_run_tests(void)
+{
+ int rc;
+ struct CMUnitTest tests[] = {
+ cmocka_unit_test_setup_teardown(torture_sftp_request_id_null,
+ session_setup,
+ session_teardown),
+ cmocka_unit_test_setup_teardown(torture_sftp_request_id_add,
+ session_setup,
+ session_teardown),
+ cmocka_unit_test_setup_teardown(torture_sftp_request_id_remove,
+ session_setup,
+ session_teardown),
+ cmocka_unit_test_setup_teardown(torture_sftp_request_id_unknown,
+ session_setup,
+ session_teardown),
+ };
+
+ ssh_init();
+
+ torture_filter_tests(tests);
+ rc = cmocka_run_group_tests(tests, sshd_setup, sshd_teardown);
+ ssh_finalize();
+
+ return rc;
+}
--
2.54.0
From 2887a8e4cab51f9c04f1fcd956cd5cb9ba27049f Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Pavol=20=C5=BD=C3=A1=C4=8Dik?= <pzacik@redhat.com>
Date: Wed, 3 Jun 2026 12:56:09 +0200
Subject: [PATCH 11/12] CVE-2026-59848 sftp: Initialize sftp_request_queue ptr
in sftp_free
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Signed-off-by: Pavol Žáčik <pzacik@redhat.com>
Reviewed-by: Jakub Jelen <jjelen@redhat.com>
(cherry picked from commit 00876f7658fd265682708572122502188fa22076)
---
src/sftp.c | 2 +-
tests/client/torture_sftp_request_id.c | 3 +--
2 files changed, 2 insertions(+), 3 deletions(-)
diff --git a/src/sftp.c b/src/sftp.c
index 39f0639c..91dc4474 100644
--- a/src/sftp.c
+++ b/src/sftp.c
@@ -371,7 +371,7 @@ void sftp_server_free(sftp_session sftp)
void sftp_free(sftp_session sftp)
{
- sftp_request_queue ptr;
+ sftp_request_queue ptr = NULL;
struct ssh_iterator *id_it = NULL;
if (sftp == NULL) {
diff --git a/tests/client/torture_sftp_request_id.c b/tests/client/torture_sftp_request_id.c
index 21f2774a..30bff573 100644
--- a/tests/client/torture_sftp_request_id.c
+++ b/tests/client/torture_sftp_request_id.c
@@ -131,7 +131,6 @@ static void torture_sftp_request_id_unknown(void **state)
struct torture_sftp *t = s->ssh.tsftp;
sftp_session sftp = t->sftp;
ssh_buffer buffer = NULL;
- sftp_message msg = NULL;
uint32_t id = 0;
int rc;
size_t count;
@@ -151,7 +150,7 @@ static void torture_sftp_request_id_unknown(void **state)
SSH_BUFFER_FREE(buffer);
/* An attempt to receive the response should fail */
- rc = sftp_recv_response_msg(sftp, id, true, &msg);
+ rc = sftp_read_and_dispatch(sftp);
assert_int_equal(rc, SSH_ERROR);
}
--
2.54.0

37
CVE-2026-59850.patch Normal file
View File

@ -0,0 +1,37 @@
From 2b2dad066dbee0077e1214a8ec40386fc39f44f3 Mon Sep 17 00:00:00 2001
From: Jakub Jelen <jjelen@redhat.com>
Date: Wed, 1 Jul 2026 16:43:08 +0200
Subject: [PATCH 12/12] CVE-2026-59850 channels: Avoid processing DATA packets
on closed channels
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Signed-off-by: Jakub Jelen <jjelen@redhat.com>
Reviewed-by: Pavol Žáčik <pzacik@redhat.com>
(cherry picked from commit a8a3fa352bb5213e08a35e4494c6e44360e2e38a)
---
src/channels.c | 7 +++++++
1 file changed, 7 insertions(+)
diff --git a/src/channels.c b/src/channels.c
index 2b84352f..1c86fde8 100644
--- a/src/channels.c
+++ b/src/channels.c
@@ -590,6 +590,13 @@ SSH_PACKET_CALLBACK(channel_rcv_data){
return SSH_PACKET_USED;
}
+ if (channel->flags & SSH_CHANNEL_FLAG_CLOSED_REMOTE) {
+ SSH_LOG(SSH_LOG_WARNING, "Received data on (remotely) closed channel");
+ ssh_set_error(session, SSH_FATAL, "Received data on (remotely) closed channel");
+ SSH_STRING_FREE(str);
+ return SSH_PACKET_USED;
+ }
+
if (len <= channel->local_window) {
channel->local_window -= len;
} else {
--
2.54.0

View File

@ -1,6 +1,6 @@
Name: libssh
Version: 0.10.4
Release: 18%{?dist}
Release: 19%{?dist}
Summary: A library implementing the SSH protocol
License: LGPLv2+
URL: http://www.libssh.org
@ -68,6 +68,13 @@ Patch26: CVE-2026-0965.patch
Patch27: CVE-2026-0966.patch
Patch28: CVE-2026-0967.patch
Patch29: CVE-2026-0968.patch
Patch30: CVE-2026-59843.patch
Patch31: CVE-2026-59844.patch
Patch32: CVE-2026-59845.patch
Patch33: CVE-2026-59846.patch
Patch34: CVE-2026-59847.patch
Patch35: CVE-2026-59848.patch
Patch36: CVE-2026-59850.patch
%description
The ssh library was designed to be used by programmers needing a working SSH
@ -160,6 +167,16 @@ popd
%attr(0644,root,root) %config(noreplace) %{_sysconfdir}/libssh/libssh_server.config
%changelog
* Thu Jul 30 2026 Pavol Žáčik <pzacik@redhat.com> - 0.10.4-19
- Backport CVE patches from 0.11.5
Resolves: RHEL-213033
Resolves: RHEL-213041
Resolves: RHEL-213047
Resolves: RHEL-213081
Resolves: RHEL-213114
Resolves: RHEL-213119
Resolves: RHEL-213151
* Fri Feb 13 2026 Pavol Žáčik <pzacik@redhat.com> - 0.10.4-18
- Resolves: RHEL-150661
- Resolves: CVE-2025-4877