Resolves: RHEL-213033 Resolves: RHEL-213041 Resolves: RHEL-213047 Resolves: RHEL-213081 Resolves: RHEL-213114 Resolves: RHEL-213119 Resolves: RHEL-213151
867 lines
24 KiB
Diff
867 lines
24 KiB
Diff
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
|
|
|