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:
parent
048ffad847
commit
9e3f4b36c8
81
CVE-2026-59843.patch
Normal file
81
CVE-2026-59843.patch
Normal 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
45
CVE-2026-59844.patch
Normal 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
66
CVE-2026-59845.patch
Normal 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
317
CVE-2026-59846.patch
Normal 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
69
CVE-2026-59847.patch
Normal 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
866
CVE-2026-59848.patch
Normal 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
37
CVE-2026-59850.patch
Normal 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
|
||||
|
||||
19
libssh.spec
19
libssh.spec
@ -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
|
||||
|
||||
Loading…
Reference in New Issue
Block a user