From 1ce8b1b1ba42faf9d44649fb1207f10778013fd8 Mon Sep 17 00:00:00 2001 From: deepin-ci-robot Date: Mon, 27 Jul 2026 12:10:33 +0800 Subject: [PATCH 01/10] fix(cve): CVE-2026-59844 - sftpserver: Check SSH_FXP_READ length to prevent DoS MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit CVE: CVE-2026-59844 (medium) - 远程认证攻击者可以发送任意长度的 SSH_FXP_READ 请求(最高 4 GiB),导致 libssh SFTP 服务器分配过多内存 Upstream: https://www.libssh.org/files/0.11/libssh-0.11.5.tar.xz Co-authored-by: hudeng Generated-By: qwen3.6-35b --- debian/changelog | 6 ++++++ debian/patches/CVE-2026-59844.patch | 32 +++++++++++++++++++++++++++++ debian/patches/series | 1 + 3 files changed, 39 insertions(+) create mode 100644 debian/patches/CVE-2026-59844.patch diff --git a/debian/changelog b/debian/changelog index 0edc6e7..2a0b5e1 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,9 @@ +libssh (0.11.3-1deepin6) unstable; urgency=medium + + * fix(cve): CVE-2026-59844 + + -- deepin-ci-robot Mon, 27 Jul 2026 12:10:33 +0800 + libssh (0.11.3-1deepin5) unstable; urgency=medium * fix(cve): CVE-2026-0965 diff --git a/debian/patches/CVE-2026-59844.patch b/debian/patches/CVE-2026-59844.patch new file mode 100644 index 0000000..d50307f --- /dev/null +++ b/debian/patches/CVE-2026-59844.patch @@ -0,0 +1,32 @@ +Description: CVE-2026-59844 - 安全修复 +Author: Pavol Zacik +Origin: https://www.libssh.org/files/0.11/libssh-0.11.5.tar.xz +Bug: https://nvd.nist.gov/vuln/detail/CVE-2026-59844 +Last-Update: 2026-07-21 +--- +diff --git a/src/sftpserver.c b/src/sftpserver.c +index 6c52e00..9a09dc3 100644 +--- a/src/sftpserver.c ++++ b/src/sftpserver.c +@@ -109,7 +109,6 @@ sftp_make_client_message(sftp_session sftp, sftp_packet packet) + printf("unpack init failed!\n"); + goto error; + } +- version = ntohl(version); + sftp->client_version = version; + break; + case SSH_FXP_CLOSE: +@@ -128,6 +127,13 @@ sftp_make_client_message(sftp_session sftp, sftp_packet packet) + if (rc != SSH_OK) { + goto error; + } ++ if (msg->len > MAX_PACKET_LEN - 1024) { ++ ssh_set_error(sftp->session, ++ SSH_FATAL, ++ "Too large SSH_FXP_READ length: %" PRIu32, ++ msg->len); ++ goto error; ++ } + break; + case SSH_FXP_WRITE: + rc = ssh_buffer_unpack(payload, diff --git a/debian/patches/series b/debian/patches/series index 7a34d0c..a691744 100644 --- a/debian/patches/series +++ b/debian/patches/series @@ -6,3 +6,4 @@ CVE-2026-0967.patch CVE-2026-0964.patch CVE-2026-0968.patch CVE-2026-0965.patch +CVE-2026-59844.patch From fd30fec9c5abdab5f0b7565b1baa873e352301ac Mon Sep 17 00:00:00 2001 From: deepin-ci-robot Date: Mon, 27 Jul 2026 12:25:10 +0800 Subject: [PATCH 02/10] fix(cve): CVE-2026-59847 - CVE-2026-59847 libcrypto: Fix symmetric issue during encryption CVE: CVE-2026-59847 (medium) - Fix symmetric issue during encryption and tag verification of AES-GCM ciphers Upstream: https://git.libssh.org/projects/libssh/commit/06c6082df966ef69db19387cf2064fa053fca1ed Co-authored-by: hudeng Generated-By: qwen3.6-35b --- debian/changelog | 6 ++++++ debian/patches/CVE-2026-59847.patch | 28 ++++++++++++++++++++++++++++ debian/patches/series | 1 + 3 files changed, 35 insertions(+) create mode 100644 debian/patches/CVE-2026-59847.patch diff --git a/debian/changelog b/debian/changelog index 2a0b5e1..0615674 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,9 @@ +libssh (0.11.3-1deepin7) unstable; urgency=medium + + * fix(cve): CVE-2026-59847 + + -- deepin-ci-robot Mon, 27 Jul 2026 12:25:10 +0800 + libssh (0.11.3-1deepin6) unstable; urgency=medium * fix(cve): CVE-2026-59844 diff --git a/debian/patches/CVE-2026-59847.patch b/debian/patches/CVE-2026-59847.patch new file mode 100644 index 0000000..129f86f --- /dev/null +++ b/debian/patches/CVE-2026-59847.patch @@ -0,0 +1,28 @@ +Description: CVE-2026-59847 - 安全修复 +Author: Jakub Jelen +Origin: https://git.libssh.org/projects/libssh/commit/06c6082df966ef69db19387cf2064fa053fca1ed +Bug: https://nvd.nist.gov/vuln/detail/CVE-2026-59847 +Last-Update: 2026-05-18 +--- +diff --git a/src/libcrypto.c b/src/libcrypto.c +index e21a82b..b928451 100644 +--- a/src/libcrypto.c ++++ b/src/libcrypto.c +@@ -646,7 +646,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; + } +@@ -734,7 +734,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; + } diff --git a/debian/patches/series b/debian/patches/series index a691744..0e2544b 100644 --- a/debian/patches/series +++ b/debian/patches/series @@ -7,3 +7,4 @@ CVE-2026-0964.patch CVE-2026-0968.patch CVE-2026-0965.patch CVE-2026-59844.patch +CVE-2026-59847.patch From ea761eb525d6f4cd2dd83a6435d3651ce385b4df Mon Sep 17 00:00:00 2001 From: deepin-ci-robot Date: Mon, 27 Jul 2026 12:38:46 +0800 Subject: [PATCH 03/10] fix(cve): CVE-2026-59848 - CVE-2026-59848 sftp: handle responses with unknown request IDs CVE: CVE-2026-59848 (medium) - 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. Upstream: https://gitlab.com/libssh/libssh-mirror/commit/26147eb4767937c797f97ff3b1b1663384232417 Co-authored-by: hudeng Generated-By: qwen3.6-35b --- debian/changelog | 6 + debian/patches/CVE-2026-59848.patch | 902 ++++++++++++++++++++++++++++ debian/patches/series | 1 + 3 files changed, 909 insertions(+) create mode 100644 debian/patches/CVE-2026-59848.patch diff --git a/debian/changelog b/debian/changelog index 0615674..d85532a 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,9 @@ +libssh (0.11.3-1deepin8) unstable; urgency=medium + + * fix(cve): CVE-2026-59848 + + -- deepin-ci-robot Mon, 27 Jul 2026 12:38:46 +0800 + libssh (0.11.3-1deepin7) unstable; urgency=medium * fix(cve): CVE-2026-59847 diff --git a/debian/patches/CVE-2026-59848.patch b/debian/patches/CVE-2026-59848.patch new file mode 100644 index 0000000..4cd2ce2 --- /dev/null +++ b/debian/patches/CVE-2026-59848.patch @@ -0,0 +1,902 @@ +Description: CVE-2026-59848 - 安全修复 +Author: Pavol Žáčik +Origin: https://gitlab.com/libssh/libssh-mirror/commit/26147eb4767937c797f97ff3b1b1663384232417 +Bug: https://nvd.nist.gov/vuln/detail/CVE-2026-59848 +Last-Update: 2026-06-01 +--- +diff --git a/include/libssh/sftp.h b/include/libssh/sftp.h +index cf4458c..3578f13 100644 +--- a/include/libssh/sftp.h ++++ b/include/libssh/sftp.h +@@ -93,6 +93,7 @@ struct sftp_session_struct { + sftp_ext ext; + sftp_packet read_packet; + sftp_limits_t limits; ++ struct ssh_list *outstanding_ids; + }; + + struct sftp_packet_struct { +diff --git a/include/libssh/sftp_priv.h b/include/libssh/sftp_priv.h +index 8470a8a..1a0eb65 100644 +--- a/include/libssh/sftp_priv.h ++++ b/include/libssh/sftp_priv.h +@@ -62,15 +62,17 @@ int sftp_read_and_dispatch(sftp_session sftp); + + sftp_message sftp_dequeue(sftp_session sftp, uint32_t id); + +-/* +- * 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 ++/** ++ * @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 + */ +-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); + + sftp_status_message parse_status_msg(sftp_message msg); + +diff --git a/src/sftp.c b/src/sftp.c +index cbcdb06..a3e0d18 100644 +--- a/src/sftp.c ++++ b/src/sftp.c +@@ -148,6 +148,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; ++ } ++ + return sftp; + error: + if (sftp->ext != NULL) { +@@ -156,6 +162,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); +@@ -187,6 +194,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); +@@ -208,6 +221,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); +@@ -321,7 +335,8 @@ 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) { + return; +@@ -349,6 +364,12 @@ void sftp_free(sftp_session sftp) + sftp_ext_free(sftp->ext); + sftp_limits_free(sftp->limits); + ++ 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); + } + +@@ -677,6 +698,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); +@@ -684,8 +710,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, +@@ -771,6 +795,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); +@@ -778,8 +807,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, +@@ -904,6 +931,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); +@@ -911,8 +943,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, +@@ -1019,6 +1049,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); +@@ -1047,7 +1082,6 @@ sftp_file sftp_open(sftp_session sftp, + } + SSH_LOG(SSH_LOG_PACKET, "Opening file %s with sftp flags %" PRIx32, + file, sftp_flags); +- id = sftp_get_new_id(sftp); + + rc = ssh_buffer_pack(buffer, + "dsd", +@@ -1411,7 +1445,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; ++ } + + /* + * limit the writes to the maximum specified in Section 3 of +@@ -1534,6 +1571,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); +@@ -1541,8 +1583,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, +@@ -1609,6 +1649,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); +@@ -1616,8 +1661,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, +@@ -1682,6 +1725,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); +@@ -1693,8 +1741,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, +@@ -1784,6 +1830,11 @@ int sftp_rename(sftp_session sftp, const char *original, const char *newname) + int request_type; + 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); +@@ -1791,8 +1842,6 @@ int sftp_rename(sftp_session sftp, const char *original, const char *newname) + return -1; + } + +- id = sftp_get_new_id(sftp); +- + /* + * posix-rename@openssh.com extension will be used + * if it is supported by sftp +@@ -1899,6 +1948,11 @@ int sftp_setstat(sftp_session sftp, const char *file, sftp_attributes attr) + 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); +@@ -1906,8 +1960,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, +@@ -1987,6 +2039,11 @@ sftp_lsetstat(sftp_session sftp, const char *file, sftp_attributes attr) + 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); +@@ -1994,8 +2051,6 @@ sftp_lsetstat(sftp_session sftp, const char *file, sftp_attributes attr) + return -1; + } + +- id = sftp_get_new_id(sftp); +- + rc = ssh_buffer_pack(buffer, "dss", id, extension_name, file); + if (rc != SSH_OK) { + ssh_set_error_oom(sftp->session); +@@ -2119,6 +2174,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); +@@ -2126,8 +2186,6 @@ int sftp_symlink(sftp_session sftp, const char *target, const char *dest) + return -1; + } + +- id = sftp_get_new_id(sftp); +- + /* The OpenSSH sftp server has order of the arguments reversed, see the + * section "4.1 sftp: Reversal of arguments to SSH_FXP_SYMLINK' in + * https://github.com/openssh/openssh-portable/blob/master/PROTOCOL +@@ -2220,6 +2278,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); +@@ -2227,8 +2291,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, +@@ -2310,6 +2372,11 @@ int sftp_hardlink(sftp_session sftp, const char *oldpath, const char *newpath) + 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); +@@ -2317,8 +2384,6 @@ int sftp_hardlink(sftp_session sftp, const char *oldpath, const char *newpath) + return -1; + } + +- id = sftp_get_new_id(sftp); +- + rc = ssh_buffer_pack(buffer, + "dsss", + id, +@@ -2434,6 +2499,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); +@@ -2441,8 +2511,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, +@@ -2509,6 +2577,11 @@ int sftp_fsync(sftp_file file) + } + sftp = file->sftp; + ++ rc = sftp_get_new_id(file->sftp, &id); ++ if (rc != SSH_OK) { ++ return -1; ++ } ++ + buffer = ssh_buffer_new(); + if (buffer == NULL) { + ssh_set_error_oom(sftp->session); +@@ -2516,8 +2589,6 @@ int sftp_fsync(sftp_file file) + return -1; + } + +- id = sftp_get_new_id(sftp); +- + rc = ssh_buffer_pack(buffer, + "dsS", + id, +@@ -2609,6 +2680,11 @@ sftp_statvfs_t sftp_fstatvfs(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); +@@ -2616,8 +2692,6 @@ sftp_statvfs_t sftp_fstatvfs(sftp_file file) + return NULL; + } + +- id = sftp_get_new_id(sftp); +- + rc = ssh_buffer_pack(buffer, + "dsS", + id, +@@ -2723,6 +2797,11 @@ static sftp_limits_t sftp_limits_use_extension(sftp_session sftp) + if (sftp == NULL) + 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); +@@ -2730,8 +2809,6 @@ static sftp_limits_t sftp_limits_use_extension(sftp_session sftp) + return NULL; + } + +- id = sftp_get_new_id(sftp); +- + rc = ssh_buffer_pack(buffer, + "ds", + id, +@@ -2873,6 +2950,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); +@@ -2880,8 +2962,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, +@@ -2964,6 +3044,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); +@@ -2971,8 +3056,6 @@ static sftp_attributes sftp_xstat(sftp_session sftp, + return NULL; + } + +- id = sftp_get_new_id(sftp); +- + rc = ssh_buffer_pack(buffer, + "ds", + id, +@@ -3042,6 +3125,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); +@@ -3049,8 +3137,6 @@ sftp_attributes sftp_fstat(sftp_file file) + return NULL; + } + +- id = sftp_get_new_id(file->sftp); +- + rc = ssh_buffer_pack(buffer, + "dS", + id, +@@ -3121,6 +3207,11 @@ char *sftp_expand_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); +@@ -3128,8 +3219,6 @@ char *sftp_expand_path(sftp_session sftp, const char *path) + return NULL; + } + +- id = sftp_get_new_id(sftp); +- + rc = ssh_buffer_pack(buffer, + "dss", + id, +@@ -3208,6 +3297,11 @@ sftp_home_directory(sftp_session sftp, const char *username) + 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); +@@ -3215,8 +3309,6 @@ sftp_home_directory(sftp_session sftp, const char *username) + return NULL; + } + +- id = sftp_get_new_id(sftp); +- + rc = ssh_buffer_pack(buffer, + "dss", + id, +diff --git a/src/sftp_aio.c b/src/sftp_aio.c +index c1c5456..9396132 100644 +--- a/src/sftp_aio.c ++++ b/src/sftp_aio.c +@@ -86,6 +86,11 @@ ssize_t sftp_aio_begin_read(sftp_file file, size_t len, sftp_aio *aio) + return SSH_ERROR; + } + ++ rc = sftp_get_new_id(sftp, &id); ++ if (rc != SSH_OK) { ++ return SSH_ERROR; ++ } ++ + buffer = ssh_buffer_new(); + if (buffer == NULL) { + ssh_set_error_oom(sftp->session); +@@ -93,8 +98,6 @@ ssize_t sftp_aio_begin_read(sftp_file file, size_t len, sftp_aio *aio) + return SSH_ERROR; + } + +- id = sftp_get_new_id(sftp); +- + rc = ssh_buffer_pack(buffer, + "dSqd", + id, +@@ -354,6 +357,11 @@ ssize_t sftp_aio_begin_write(sftp_file file, + return SSH_ERROR; + } + ++ rc = sftp_get_new_id(sftp, &id); ++ if (rc != SSH_OK) { ++ return SSH_ERROR; ++ } ++ + buffer = ssh_buffer_new(); + if (buffer == NULL) { + ssh_set_error_oom(sftp->session); +@@ -361,7 +369,6 @@ ssize_t sftp_aio_begin_write(sftp_file file, + return SSH_ERROR; + } + +- id = sftp_get_new_id(sftp); + rc = ssh_buffer_pack(buffer, + "dSqdP", + id, +diff --git a/src/sftp_common.c b/src/sftp_common.c +index d6189b7..66d19ef 100644 +--- a/src/sftp_common.c ++++ b/src/sftp_common.c +@@ -795,6 +795,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) { +@@ -842,9 +844,63 @@ 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; + } + ++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; ++} ++ + int sftp_read_and_dispatch(sftp_session sftp) + { + sftp_packet packet = NULL; +diff --git a/tests/client/torture_sftp_request_id.c b/tests/client/torture_sftp_request_id.c +new file mode 100644 +index 0000000..21f2774 +--- /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 ++#include ++ ++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; ++} diff --git a/debian/patches/series b/debian/patches/series index 0e2544b..92531d3 100644 --- a/debian/patches/series +++ b/debian/patches/series @@ -8,3 +8,4 @@ CVE-2026-0968.patch CVE-2026-0965.patch CVE-2026-59844.patch CVE-2026-59847.patch +CVE-2026-59848.patch From dfd092add291f545dd6f667f3137b45bcc803238 Mon Sep 17 00:00:00 2001 From: deepin-ci-robot Date: Mon, 27 Jul 2026 12:49:16 +0800 Subject: [PATCH 04/10] fix(cve): CVE-2026-15370 - fix: replace str(n)(cat|cpy) with strl(cat|cpy) CVE: CVE-2026-15370 (medium) - A flaw was found in libssh. During SFTP server directory listing, the longname field is constructed with unsafe concatenation into a fixed-size stack buffer. When a client causes the server to list attacker-controlled filenames, sufficiently long names can overflow that stack buffer and may lead to ... Upstream: https://gitlab.com/libssh/libssh-mirror/-/commit/b2065f71c905d347b5e8dd7f8e24856807d71e99 Co-authored-by: hudeng Generated-By: qwen3.6-35b --- debian/changelog | 6 + debian/patches/CVE-2026-15370.patch | 329 ++++++++++++++++++++++++++++ debian/patches/series | 1 + 3 files changed, 336 insertions(+) create mode 100644 debian/patches/CVE-2026-15370.patch diff --git a/debian/changelog b/debian/changelog index d85532a..1308860 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,9 @@ +libssh (0.11.3-1deepin9) unstable; urgency=medium + + * fix(cve): CVE-2026-15370 + + -- deepin-ci-robot Mon, 27 Jul 2026 12:49:16 +0800 + libssh (0.11.3-1deepin8) unstable; urgency=medium * fix(cve): CVE-2026-59848 diff --git a/debian/patches/CVE-2026-15370.patch b/debian/patches/CVE-2026-15370.patch new file mode 100644 index 0000000..b53e53f --- /dev/null +++ b/debian/patches/CVE-2026-15370.patch @@ -0,0 +1,329 @@ +Description: CVE-2026-15370 - 安全修复 +Author: YubarajDas +Origin: https://gitlab.com/libssh/libssh-mirror/-/commit/b2065f71c905d347b5e8dd7f8e24856807d71e99 +Bug: https://nvd.nist.gov/vuln/detail/CVE-2026-15370 +Last-Update: 2026-04-22 22:40:19 +0530 +--- +diff --git a/config.h.cmake b/config.h.cmake +index 8dce527..b97fb52 100644 +--- a/config.h.cmake ++++ b/config.h.cmake +@@ -135,6 +135,12 @@ + /* Define to 1 if you have the `strndup' function. */ + #cmakedefine HAVE_STRNDUP 1 + ++/* Define to 1 if you have the `strlcpy' function. */ ++#cmakedefine HAVE_STRLCPY 1 ++ ++/* Define to 1 if you have the `strlcat' function. */ ++#cmakedefine HAVE_STRLCAT 1 ++ + /* Define to 1 if you have the `cfmakeraw' function. */ + #cmakedefine HAVE_CFMAKERAW 1 + +diff --git a/include/libssh/priv.h b/include/libssh/priv.h +index 6206997..0f19c34 100644 +--- a/include/libssh/priv.h ++++ b/include/libssh/priv.h +@@ -59,6 +59,14 @@ extern "C" { + char *strndup(const char *s, size_t n); + #endif /* ! HAVE_STRNDUP */ + ++#ifndef HAVE_STRLCPY ++size_t strlcpy(char *dst, const char *src, size_t size); ++#endif ++ ++#ifndef HAVE_STRLCAT ++size_t strlcat(char *dst, const char *src, size_t size); ++#endif ++ + #ifdef HAVE_BYTESWAP_H + #include + #endif +diff --git a/src/gcrypt_missing.c b/src/gcrypt_missing.c +index 5f84e6b..dc06377 100644 +--- a/src/gcrypt_missing.c ++++ b/src/gcrypt_missing.c +@@ -61,7 +61,8 @@ char *ssh_gcry_bn2dec(bignum bn) { + } + + if (!gcry_mpi_cmp_ui(bn, 0)) { +- strcpy(ret, "0"); ++ ret[0] = '0'; ++ ret[1] = '\0'; + } else { + ten = bignum_new(); + if (ten == NULL) { +diff --git a/src/getpass.c b/src/getpass.c +index c19c4bc..384f06e 100644 +--- a/src/getpass.c ++++ b/src/getpass.c +@@ -74,7 +74,7 @@ static int ssh_gets(const char *prompt, char *buf, size_t len, int verify) + fprintf(stdout, "\n"); + + if (*tmp) { +- strncpy(buf, tmp, len); ++ strlcpy(buf, tmp, len); + } + + if (verify) { +diff --git a/src/knownhosts.c b/src/knownhosts.c +index 3ab468d..5f37c8e 100644 +--- a/src/knownhosts.c ++++ b/src/knownhosts.c +@@ -594,14 +594,10 @@ char *ssh_known_hosts_get_algorithms_names(ssh_session session) + } + + if (needcomma) { +- strncat(methods_buffer, +- ",", +- sizeof(methods_buffer) - strlen(methods_buffer) - 1); ++ strlcat(methods_buffer, ",", sizeof(methods_buffer)); + } + +- strncat(methods_buffer, +- algo, +- sizeof(methods_buffer) - strlen(methods_buffer) - 1); ++ strlcat(methods_buffer, algo, sizeof(methods_buffer)); + needcomma = true; + + ssh_knownhosts_entry_free(entry); +diff --git a/src/match.c b/src/match.c +index 771ee63..eb765ef 100644 +--- a/src/match.c ++++ b/src/match.c +@@ -384,7 +384,7 @@ match_cidr_address_list(const char *address, + } + + if (address != NULL) { +- strncpy(addr, address, NI_MAXHOST - 1); ++ strlcpy(addr, address, sizeof(addr)); + + /* Remove interface in case of IPv6 address: addr%interface */ + a = strchr(addr, '%'); +@@ -482,7 +482,7 @@ match_cidr_address_list(const char *address, + } + #undef VALID_CIDR_CHARS + +- strncpy(addr_buffer, cp, sizeof(addr_buffer) - 1); ++ strlcpy(addr_buffer, cp, sizeof(addr_buffer)); + sp = strchr(addr_buffer, '/'); + if (sp != NULL) { + *sp = '\0'; +diff --git a/src/misc.c b/src/misc.c +index 3968e6b..218fbbf 100644 +--- a/src/misc.c ++++ b/src/misc.c +@@ -952,8 +952,7 @@ char *ssh_dirname (const char *path) + return NULL; + } + +- strncpy(new, path, len); +- new[len] = '\0'; ++ strlcpy(new, path, len + 1); + + return new; + } +@@ -1010,8 +1009,7 @@ char *ssh_basename (const char *path) + return NULL; + } + +- strncpy(new, s, len); +- new[len] = '\0'; ++ strlcpy(new, s, len + 1); + + return new; + } +@@ -1316,7 +1314,7 @@ char *ssh_path_expand_escape(ssh_session session, const char *s) + return NULL; + } + l = strlen(buf); +- strncpy(buf + l, x, MAX_BUF_SIZE - l - 1); ++ strlcpy(buf + l, x, MAX_BUF_SIZE - l); + buf[i] = '\0'; + SAFE_FREE(x); + } +@@ -2318,4 +2316,47 @@ FILE *ssh_strict_fopen(const char *filename, size_t max_file_size) + return f; + } + ++/* ++ * BSD-compatible fallback implementations for systems without native ++ * strlcpy/strlcat. These are original implementations following the OpenBSD API ++ * specification. The strlcpy() and strlcat() API was designed by Todd C. ++ * Miller. See: https://man.openbsd.org/strlcpy.3 ++ */ ++ ++#ifndef HAVE_STRLCPY ++size_t strlcpy(char *dst, const char *src, size_t size) ++{ ++ size_t len = strlen(src); ++ ++ if (size != 0) { ++ size_t copy_len = (len >= size) ? size - 1 : len; ++ ++ memcpy(dst, src, copy_len); ++ dst[copy_len] = '\0'; ++ } ++ ++ return len; ++} ++#endif /* HAVE_STRLCPY */ ++ ++#ifndef HAVE_STRLCAT ++size_t strlcat(char *dst, const char *src, size_t size) ++{ ++ size_t dst_len; ++ const char *p = memchr(dst, '\0', size); ++ size_t src_len = strlen(src); ++ ++ dst_len = (p != NULL) ? (size_t)(p - dst) : size; ++ ++ if (dst_len < size) { ++ size_t copy_len = ++ (src_len >= size - dst_len) ? size - dst_len - 1 : src_len; ++ memcpy(dst + dst_len, src, copy_len); ++ dst[dst_len + copy_len] = '\0'; ++ } ++ ++ return dst_len + src_len; ++} ++#endif /* HAVE_STRLCAT */ ++ + /** @} */ +diff --git a/src/options.c b/src/options.c +index 785296d..f084b5a 100644 +--- a/src/options.c ++++ b/src/options.c +@@ -2663,7 +2663,7 @@ static char *ssh_bind_options_expand_escape(ssh_bind sshbind, const char *s) + return NULL; + } + l = strlen(buf); +- strncpy(buf + l, x, MAX_BUF_SIZE - l - 1); ++ strlcpy(buf + l, x, MAX_BUF_SIZE - l); + buf[i] = '\0'; + SAFE_FREE(x); + } +diff --git a/src/server.c b/src/server.c +index c6ad11f..9430c57 100644 +--- a/src/server.c ++++ b/src/server.c +@@ -619,24 +619,19 @@ int ssh_auth_reply_default(ssh_session session,int partial) { + session->auth.supported_methods = SSH_AUTH_METHOD_PUBLICKEY | SSH_AUTH_METHOD_PASSWORD; + } + if (session->auth.supported_methods & SSH_AUTH_METHOD_PUBLICKEY) { +- strncat(methods_c, "publickey,", +- sizeof(methods_c) - strlen(methods_c) - 1); ++ strlcat(methods_c, "publickey,", sizeof(methods_c)); + } + if (session->auth.supported_methods & SSH_AUTH_METHOD_GSSAPI_MIC){ +- strncat(methods_c,"gssapi-with-mic,", +- sizeof(methods_c) - strlen(methods_c) - 1); ++ strlcat(methods_c, "gssapi-with-mic,", sizeof(methods_c)); + } + if (session->auth.supported_methods & SSH_AUTH_METHOD_INTERACTIVE) { +- strncat(methods_c, "keyboard-interactive,", +- sizeof(methods_c) - strlen(methods_c) - 1); ++ strlcat(methods_c, "keyboard-interactive,", sizeof(methods_c)); + } + if (session->auth.supported_methods & SSH_AUTH_METHOD_PASSWORD) { +- strncat(methods_c, "password,", +- sizeof(methods_c) - strlen(methods_c) - 1); ++ strlcat(methods_c, "password,", sizeof(methods_c)); + } + if (session->auth.supported_methods & SSH_AUTH_METHOD_HOSTBASED) { +- strncat(methods_c, "hostbased,", +- sizeof(methods_c) - strlen(methods_c) - 1); ++ strlcat(methods_c, "hostbased,", sizeof(methods_c)); + } + + if (methods_c[0] == '\0' || methods_c[strlen(methods_c)-1] != ',') { +diff --git a/src/sftpserver.c b/src/sftpserver.c +index 9a09dc3..46b191f 100644 +--- a/src/sftpserver.c ++++ b/src/sftpserver.c +@@ -1200,16 +1200,20 @@ readdir_long_name(char *z_file_name, struct stat *z_st, char *z_long_name) + *ptr++ = ' '; + *ptr = '\0'; + +- snprintf(tmpbuf, sizeof(tmpbuf), "%3d %d %d %d", (int)z_st->st_nlink, +- (int)z_st->st_uid, (int)z_st->st_gid, (int)z_st->st_size); +- strcat(z_long_name, tmpbuf); +- + ctime_r(&z_st->st_mtime, time); + if ((ptr = strchr(time, '\n'))) { + *ptr = '\0'; + } +- snprintf(tmpbuf, sizeof(tmpbuf), " %s %s", time + 4, z_file_name); +- strcat(z_long_name, tmpbuf); ++ snprintf(tmpbuf, ++ sizeof(tmpbuf), ++ "%3d %d %d %d %s %s", ++ (int)z_st->st_nlink, ++ (int)z_st->st_uid, ++ (int)z_st->st_gid, ++ (int)z_st->st_size, ++ time + 4, ++ z_file_name); ++ strlcat(z_long_name, tmpbuf, MAX_LONG_NAME_LEN); + + return SSH_OK; + } +diff --git a/src/token.c b/src/token.c +index 6ce2b64..4c03256 100644 +--- a/src/token.c ++++ b/src/token.c +@@ -338,9 +338,9 @@ char *ssh_remove_duplicates(const char *list) + for (i = 0; i < num_tokens; i++) { + if (should_copy[i]) { + if (need_comma) { +- strncat(ret, ",", (max_len - strlen(ret) - 1)); ++ strlcat(ret, ",", max_len); + } +- strncat(ret, tok->tokens[i], (max_len - strlen(ret) - 1)); ++ strlcat(ret, tok->tokens[i], max_len); + need_comma = true; + } + } +diff --git a/tests/client/torture_auth.c b/tests/client/torture_auth.c +index 614d80f..7eb8852 100644 +--- a/tests/client/torture_auth.c ++++ b/tests/client/torture_auth.c +@@ -420,7 +420,7 @@ torture_auth_autopubkey_protected_auth_function (const char *prompt, char *buf, + ssh_string_free_char(expected_id); + + data->n_calls += 1; +- strncpy(buf, "secret", len); ++ strlcpy(buf, "secret", len); + return 0; + } + +diff --git a/tests/torture.c b/tests/torture.c +index 6bdd646..443d952 100644 +--- a/tests/torture.c ++++ b/tests/torture.c +@@ -1593,8 +1593,7 @@ static int recursive_rm_dir_content(const char *path) + int rc = 0; + BOOL removed; + +- strcpy(file_path, path); +- strcat(file_path, "\\*"); ++ snprintf(file_path, sizeof(file_path), "%s\\*", path); + + file_handle = FindFirstFile(file_path, &file_data); + +@@ -1624,9 +1623,11 @@ static int recursive_rm_dir_content(const char *path) + } + + /* Create full file path */ +- strcpy(file_path, path); +- strcat(file_path, "\\"); +- strcat(file_path, file_data.cFileName); ++ snprintf(file_path, ++ sizeof(file_path), ++ "%s\\%s", ++ path, ++ file_data.cFileName); + + attributes = GetFileAttributes(file_path); + if (attributes & FILE_ATTRIBUTE_DIRECTORY) { diff --git a/debian/patches/series b/debian/patches/series index 92531d3..29244bc 100644 --- a/debian/patches/series +++ b/debian/patches/series @@ -9,3 +9,4 @@ CVE-2026-0965.patch CVE-2026-59844.patch CVE-2026-59847.patch CVE-2026-59848.patch +CVE-2026-15370.patch From f4c8960a7ac2e61956fad68b2bd89a7c249b0e2c Mon Sep 17 00:00:00 2001 From: deepin-ci-robot Date: Mon, 27 Jul 2026 12:54:40 +0800 Subject: [PATCH 05/10] fix(cve): CVE-2026-59845 - CVE-2026-59845 socket: Properly check fork() return code CVE: CVE-2026-59845 (medium) - CVE-2026-59845 socket: Properly check fork() return code Upstream: https://git.libssh.org/projects/libssh/commit/92b6fb9c5e2d1606e8f809fd884ab6dd4d3b7d45 Co-authored-by: hudeng Generated-By: qwen3.6-35b --- debian/changelog | 6 +++++ debian/patches/CVE-2026-59845.patch | 37 +++++++++++++++++++++++++++++ debian/patches/series | 1 + 3 files changed, 44 insertions(+) create mode 100644 debian/patches/CVE-2026-59845.patch diff --git a/debian/changelog b/debian/changelog index 1308860..34c33d5 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,9 @@ +libssh (0.11.3-1deepin10) unstable; urgency=medium + + * fix(cve): CVE-2026-59845 + + -- deepin-ci-robot Mon, 27 Jul 2026 12:54:40 +0800 + libssh (0.11.3-1deepin9) unstable; urgency=medium * fix(cve): CVE-2026-15370 diff --git a/debian/patches/CVE-2026-59845.patch b/debian/patches/CVE-2026-59845.patch new file mode 100644 index 0000000..4f7e0ae --- /dev/null +++ b/debian/patches/CVE-2026-59845.patch @@ -0,0 +1,37 @@ +Description: CVE-2026-59845 - 安全修复 +Author: Jakub Jelen +Origin: https://git.libssh.org/projects/libssh/commit/92b6fb9c5e2d1606e8f809fd884ab6dd4d3b7d45 +Bug: https://nvd.nist.gov/vuln/detail/CVE-2026-59845 +Last-Update: 2026-03-26 16:32:24 +0100 +--- +diff --git a/src/socket.c b/src/socket.c +index abb2377..c309b72 100644 +--- a/src/socket.c ++++ b/src/socket.c +@@ -962,6 +962,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]; + ssh_poll_handle h = NULL; + int pid; +@@ -980,7 +981,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]); diff --git a/debian/patches/series b/debian/patches/series index 29244bc..178ba3f 100644 --- a/debian/patches/series +++ b/debian/patches/series @@ -10,3 +10,4 @@ CVE-2026-59844.patch CVE-2026-59847.patch CVE-2026-59848.patch CVE-2026-15370.patch +CVE-2026-59845.patch From 81d9104a779ced4c848ecd10e59f37fed1c380f4 Mon Sep 17 00:00:00 2001 From: deepin-ci-robot Date: Mon, 27 Jul 2026 13:03:37 +0800 Subject: [PATCH 06/10] fix(cve): CVE-2026-59850 - CVE-2026-59850 channels: Avoid processing DATA packets on closed channel CVE: CVE-2026-59850 (medium) - CVE-2026-59850: channels: Avoid processing DATA packets on closed channels Upstream: https://git.libssh.org/projects/libssh/commit/6edfb52b3b364577d2db0334c0514a977efceed2 Co-authored-by: hudeng Generated-By: qwen3.6-35b --- debian/changelog | 6 ++++++ debian/patches/CVE-2026-59850.patch | 24 ++++++++++++++++++++++++ debian/patches/series | 1 + 3 files changed, 31 insertions(+) create mode 100644 debian/patches/CVE-2026-59850.patch diff --git a/debian/changelog b/debian/changelog index 34c33d5..6aaa7ba 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,9 @@ +libssh (0.11.3-1deepin11) unstable; urgency=medium + + * fix(cve): CVE-2026-59850 + + -- deepin-ci-robot Mon, 27 Jul 2026 13:03:37 +0800 + libssh (0.11.3-1deepin10) unstable; urgency=medium * fix(cve): CVE-2026-59845 diff --git a/debian/patches/CVE-2026-59850.patch b/debian/patches/CVE-2026-59850.patch new file mode 100644 index 0000000..9ee6588 --- /dev/null +++ b/debian/patches/CVE-2026-59850.patch @@ -0,0 +1,24 @@ +Description: CVE-2026-59850 - 安全修复 +Author: Jakub Jelen +Origin: https://git.libssh.org/projects/libssh/commit/6edfb52b3b364577d2db0334c0514a977efceed2 +Bug: https://nvd.nist.gov/vuln/detail/CVE-2026-59850 +Last-Update: 2026-07-01 16:43:08 +0200 +--- +diff --git a/src/channels.c b/src/channels.c +index c8a5d8b..60eb27c 100644 +--- a/src/channels.c ++++ b/src/channels.c +@@ -628,6 +628,13 @@ SSH_PACKET_CALLBACK(channel_rcv_data) + channel->local_window, + channel->remote_window); + ++ 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) { + SSH_LOG(SSH_LOG_RARE, + "Data packet too big for our window(%" PRIu32 " vs %" PRIu32 ")", diff --git a/debian/patches/series b/debian/patches/series index 178ba3f..8b7a00c 100644 --- a/debian/patches/series +++ b/debian/patches/series @@ -11,3 +11,4 @@ CVE-2026-59847.patch CVE-2026-59848.patch CVE-2026-15370.patch CVE-2026-59845.patch +CVE-2026-59850.patch From 9d04177087c5b60d87f50dcea1eff5f03b1a9125 Mon Sep 17 00:00:00 2001 From: deepin-ci-robot Date: Mon, 27 Jul 2026 13:11:23 +0800 Subject: [PATCH 07/10] fix(cve): CVE-2026-59849 - CVE-2026-59849 auth: Fix state machine to aovid infinite loop MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit CVE: CVE-2026-59849 (low) - 认证过程中证书迭代器在失败时未正确移动,可能导致无限循环 Upstream: https://gitlab.com/libssh/libssh-mirror/-/commit/1e8f1c397019ca5a60e2c4b6d3e6869672436165 Co-authored-by: hudeng Generated-By: qwen3.6-35b --- debian/changelog | 6 +++ debian/patches/CVE-2026-59849.patch | 59 +++++++++++++++++++++++++++++ debian/patches/series | 1 + 3 files changed, 66 insertions(+) create mode 100644 debian/patches/CVE-2026-59849.patch diff --git a/debian/changelog b/debian/changelog index 6aaa7ba..a4729d3 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,9 @@ +libssh (0.11.3-1deepin12) unstable; urgency=medium + + * fix(cve): CVE-2026-59849 + + -- deepin-ci-robot Mon, 27 Jul 2026 13:11:23 +0800 + libssh (0.11.3-1deepin11) unstable; urgency=medium * fix(cve): CVE-2026-59850 diff --git a/debian/patches/CVE-2026-59849.patch b/debian/patches/CVE-2026-59849.patch new file mode 100644 index 0000000..7abd368 --- /dev/null +++ b/debian/patches/CVE-2026-59849.patch @@ -0,0 +1,59 @@ +Description: CVE-2026-59849 - 安全修复 +Author: Jakub Jelen +Origin: https://gitlab.com/libssh/libssh-mirror/-/commit/1e8f1c397019ca5a60e2c4b6d3e6869672436165 +Bug: https://nvd.nist.gov/vuln/detail/CVE-2026-59849 +Last-Update: Tue, 26 May 2026 12:13:47 +0200 +--- +diff --git a/src/auth.c b/src/auth.c +index d5983a6..ebd4793 100644 +--- a/src/auth.c ++++ b/src/auth.c +@@ -1188,6 +1188,7 @@ enum ssh_auth_auto_state_e { + SSH_AUTH_AUTO_STATE_PUBKEY, + SSH_AUTH_AUTO_STATE_KEY_IMPORTED, + SSH_AUTH_AUTO_STATE_CERTIFICATE_FILE, ++ SSH_AUTH_AUTO_STATE_CERTIFICATE_OPTION_INIT, + SSH_AUTH_AUTO_STATE_CERTIFICATE_OPTION, + SSH_AUTH_AUTO_STATE_PUBKEY_ACCEPTED + }; +@@ -1414,6 +1415,7 @@ int ssh_userauth_publickey_auto(ssh_session session, + } + if (state->state == SSH_AUTH_AUTO_STATE_KEY_IMPORTED || + state->state == SSH_AUTH_AUTO_STATE_CERTIFICATE_FILE || ++ state->state == SSH_AUTH_AUTO_STATE_CERTIFICATE_OPTION_INIT || + state->state == SSH_AUTH_AUTO_STATE_CERTIFICATE_OPTION) { + ssh_key k = state->pubkey; + if (state->state != SSH_AUTH_AUTO_STATE_KEY_IMPORTED) { +@@ -1464,14 +1466,15 @@ int ssh_userauth_publickey_auto(ssh_session session, + continue; + } + /* if the file does not exists, try configuration options */ +- state->state = SSH_AUTH_AUTO_STATE_CERTIFICATE_OPTION; ++ state->state = SSH_AUTH_AUTO_STATE_CERTIFICATE_OPTION_INIT; + } + /* Try certificate files loaded through options */ ++ if (state->state == SSH_AUTH_AUTO_STATE_CERTIFICATE_OPTION_INIT) { ++ state->cert_it = ssh_list_get_iterator(session->opts.certificate); ++ state->state = SSH_AUTH_AUTO_STATE_CERTIFICATE_OPTION; ++ } + if (state->state == SSH_AUTH_AUTO_STATE_CERTIFICATE_OPTION) { + SSH_KEY_FREE(state->cert); +- if (state->cert_it == NULL) { +- state->cert_it = ssh_list_get_iterator(session->opts.certificate); +- } + while (state->cert_it != NULL) { + const char *cert_file = state->cert_it->data; + ssh_key cert = NULL; +@@ -1495,10 +1498,12 @@ int ssh_userauth_publickey_auto(ssh_session session, + state->cert = cert; + cert = NULL; + state->state = SSH_AUTH_AUTO_STATE_CERTIFICATE_OPTION; ++ state->cert_it = state->cert_it->next; + /* try to authenticate with this identity */ + break; /* try this cert */ + } + /* continue with next identity */ ++ state->cert_it = state->cert_it->next; + } + if (state->cert != NULL) { + continue; /* retry with the certificate */ diff --git a/debian/patches/series b/debian/patches/series index 8b7a00c..c1195b5 100644 --- a/debian/patches/series +++ b/debian/patches/series @@ -12,3 +12,4 @@ CVE-2026-59848.patch CVE-2026-15370.patch CVE-2026-59845.patch CVE-2026-59850.patch +CVE-2026-59849.patch From 50a848d3c93bc3131fb84b1d13c979ef7c43784a Mon Sep 17 00:00:00 2001 From: deepin-ci-robot Date: Mon, 27 Jul 2026 13:20:56 +0800 Subject: [PATCH 08/10] fix(cve): CVE-2026-59846 - CVE-2026-59846 Block shell metacharacters from usernames MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit CVE: CVE-2026-59846 (low) - 当攻击者可以在用户名中插入美元符号或反斜杠时,可能导致打印包含秘密的环境变量。这是 CVE-2023-6004 的补充修复。 Upstream: https://git.libssh.org/projects/libssh.git/commit/56ce3c193eb06af5bf3b07ec0b4c7308b5c72130 Co-authored-by: hudeng Generated-By: qwen3.6-35b --- debian/changelog | 6 ++++++ debian/patches/CVE-2026-59846.patch | 32 +++++++++++++++++++++++++++++ debian/patches/series | 1 + 3 files changed, 39 insertions(+) create mode 100644 debian/patches/CVE-2026-59846.patch diff --git a/debian/changelog b/debian/changelog index a4729d3..55a7693 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,9 @@ +libssh (0.11.3-1deepin13) unstable; urgency=medium + + * fix(cve): CVE-2026-59846 + + -- deepin-ci-robot Mon, 27 Jul 2026 13:20:56 +0800 + libssh (0.11.3-1deepin12) unstable; urgency=medium * fix(cve): CVE-2026-59849 diff --git a/debian/patches/CVE-2026-59846.patch b/debian/patches/CVE-2026-59846.patch new file mode 100644 index 0000000..518102d --- /dev/null +++ b/debian/patches/CVE-2026-59846.patch @@ -0,0 +1,32 @@ +Description: CVE-2026-59846 - 安全修复 +Author: Jakub Jelen +Origin: https://git.libssh.org/projects/libssh.git/commit/56ce3c193eb06af5bf3b07ec0b4c7308b5c72130 +Bug: https://nvd.nist.gov/vuln/detail/CVE-2026-59846 +Last-Update: 2026-04-02 +--- +diff --git a/include/libssh/priv.h b/include/libssh/priv.h +index 0f19c34..aa1b00e 100644 +--- a/include/libssh/priv.h ++++ b/include/libssh/priv.h +@@ -253,6 +253,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 218fbbf..d57d81e 100644 +--- a/src/misc.c ++++ b/src/misc.c +@@ -2190,7 +2190,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++) { diff --git a/debian/patches/series b/debian/patches/series index c1195b5..9e02c06 100644 --- a/debian/patches/series +++ b/debian/patches/series @@ -13,3 +13,4 @@ CVE-2026-15370.patch CVE-2026-59845.patch CVE-2026-59850.patch CVE-2026-59849.patch +CVE-2026-59846.patch From 0ab005279e75f53c73a06970e7488427c87ec7ad Mon Sep 17 00:00:00 2001 From: hudeng Date: Mon, 27 Jul 2026 15:45:07 +0800 Subject: [PATCH 09/10] fix(CVE-2026-59848): update remaining sftp_get_new_id calls in sftp_read/sftp_async_read_begin --- debian/patches/CVE-2026-59848.patch | 175 ++++++++++++++++------------ 1 file changed, 99 insertions(+), 76 deletions(-) diff --git a/debian/patches/CVE-2026-59848.patch b/debian/patches/CVE-2026-59848.patch index 4cd2ce2..dcb7c99 100644 --- a/debian/patches/CVE-2026-59848.patch +++ b/debian/patches/CVE-2026-59848.patch @@ -4,10 +4,10 @@ Origin: https://gitlab.com/libssh/libssh-mirror/commit/26147eb4767937c797f97ff3b Bug: https://nvd.nist.gov/vuln/detail/CVE-2026-59848 Last-Update: 2026-06-01 --- -diff --git a/include/libssh/sftp.h b/include/libssh/sftp.h -index cf4458c..3578f13 100644 ---- a/include/libssh/sftp.h -+++ b/include/libssh/sftp.h +Index: libssh/include/libssh/sftp.h +=================================================================== +--- libssh.orig/include/libssh/sftp.h ++++ libssh/include/libssh/sftp.h @@ -93,6 +93,7 @@ struct sftp_session_struct { sftp_ext ext; sftp_packet read_packet; @@ -16,11 +16,11 @@ index cf4458c..3578f13 100644 }; struct sftp_packet_struct { -diff --git a/include/libssh/sftp_priv.h b/include/libssh/sftp_priv.h -index 8470a8a..1a0eb65 100644 ---- a/include/libssh/sftp_priv.h -+++ b/include/libssh/sftp_priv.h -@@ -62,15 +62,17 @@ int sftp_read_and_dispatch(sftp_session sftp); +Index: libssh/include/libssh/sftp_priv.h +=================================================================== +--- libssh.orig/include/libssh/sftp_priv.h ++++ libssh/include/libssh/sftp_priv.h +@@ -62,15 +62,17 @@ int sftp_read_and_dispatch(sftp_session sftp_message sftp_dequeue(sftp_session sftp, uint32_t id); @@ -46,11 +46,11 @@ index 8470a8a..1a0eb65 100644 sftp_status_message parse_status_msg(sftp_message msg); -diff --git a/src/sftp.c b/src/sftp.c -index cbcdb06..a3e0d18 100644 ---- a/src/sftp.c -+++ b/src/sftp.c -@@ -148,6 +148,12 @@ sftp_session sftp_new(ssh_session session) +Index: libssh/src/sftp.c +=================================================================== +--- libssh.orig/src/sftp.c ++++ libssh/src/sftp.c +@@ -148,6 +148,12 @@ sftp_session sftp_new(ssh_session sessio goto error; } @@ -71,7 +71,7 @@ index cbcdb06..a3e0d18 100644 if (sftp->read_packet != NULL) { if (sftp->read_packet->payload != NULL) { SSH_BUFFER_FREE(sftp->read_packet->payload); -@@ -187,6 +194,12 @@ sftp_new_channel(ssh_session session, ssh_channel channel) +@@ -187,6 +194,12 @@ sftp_new_channel(ssh_session session, ss goto error; } @@ -115,7 +115,7 @@ index cbcdb06..a3e0d18 100644 SAFE_FREE(sftp); } -@@ -677,6 +698,11 @@ sftp_dir sftp_opendir(sftp_session sftp, const char *path) +@@ -677,6 +698,11 @@ sftp_dir sftp_opendir(sftp_session sftp, return NULL; } @@ -127,7 +127,7 @@ index cbcdb06..a3e0d18 100644 payload = ssh_buffer_new(); if (payload == NULL) { ssh_set_error_oom(sftp->session); -@@ -684,8 +710,6 @@ sftp_dir sftp_opendir(sftp_session sftp, const char *path) +@@ -684,8 +710,6 @@ sftp_dir sftp_opendir(sftp_session sftp, return NULL; } @@ -136,7 +136,7 @@ index cbcdb06..a3e0d18 100644 rc = ssh_buffer_pack(payload, "ds", id, -@@ -771,6 +795,11 @@ sftp_attributes sftp_readdir(sftp_session sftp, sftp_dir dir) +@@ -771,6 +795,11 @@ sftp_attributes sftp_readdir(sftp_sessio int rc; if (dir->buffer == NULL) { @@ -148,7 +148,7 @@ index cbcdb06..a3e0d18 100644 payload = ssh_buffer_new(); if (payload == NULL) { ssh_set_error_oom(sftp->session); -@@ -778,8 +807,6 @@ sftp_attributes sftp_readdir(sftp_session sftp, sftp_dir dir) +@@ -778,8 +807,6 @@ sftp_attributes sftp_readdir(sftp_sessio return NULL; } @@ -157,7 +157,7 @@ index cbcdb06..a3e0d18 100644 rc = ssh_buffer_pack(payload, "dS", id, -@@ -904,6 +931,11 @@ static int sftp_handle_close(sftp_session sftp, ssh_string handle) +@@ -904,6 +931,11 @@ static int sftp_handle_close(sftp_sessio uint32_t id; int rc; @@ -169,7 +169,7 @@ index cbcdb06..a3e0d18 100644 buffer = ssh_buffer_new(); if (buffer == NULL) { ssh_set_error_oom(sftp->session); -@@ -911,8 +943,6 @@ static int sftp_handle_close(sftp_session sftp, ssh_string handle) +@@ -911,8 +943,6 @@ static int sftp_handle_close(sftp_sessio return -1; } @@ -198,7 +198,31 @@ index cbcdb06..a3e0d18 100644 rc = ssh_buffer_pack(buffer, "dsd", -@@ -1411,7 +1445,10 @@ ssize_t sftp_write(sftp_file file, const void *buf, size_t count) { +@@ -1179,7 +1213,10 @@ ssize_t sftp_read(sftp_file handle, void + 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", +@@ -1280,7 +1317,10 @@ int sftp_async_read_begin(sftp_file file + 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", +@@ -1411,7 +1451,10 @@ ssize_t sftp_write(sftp_file file, const return -1; } @@ -210,7 +234,7 @@ index cbcdb06..a3e0d18 100644 /* * limit the writes to the maximum specified in Section 3 of -@@ -1534,6 +1571,11 @@ int sftp_unlink(sftp_session sftp, const char *file) { +@@ -1534,6 +1577,11 @@ int sftp_unlink(sftp_session sftp, const uint32_t id; int rc; @@ -222,7 +246,7 @@ index cbcdb06..a3e0d18 100644 buffer = ssh_buffer_new(); if (buffer == NULL) { ssh_set_error_oom(sftp->session); -@@ -1541,8 +1583,6 @@ int sftp_unlink(sftp_session sftp, const char *file) { +@@ -1541,8 +1589,6 @@ int sftp_unlink(sftp_session sftp, const return -1; } @@ -231,7 +255,7 @@ index cbcdb06..a3e0d18 100644 rc = ssh_buffer_pack(buffer, "ds", id, -@@ -1609,6 +1649,11 @@ int sftp_rmdir(sftp_session sftp, const char *directory) { +@@ -1609,6 +1655,11 @@ int sftp_rmdir(sftp_session sftp, const uint32_t id; int rc; @@ -243,7 +267,7 @@ index cbcdb06..a3e0d18 100644 buffer = ssh_buffer_new(); if (buffer == NULL) { ssh_set_error_oom(sftp->session); -@@ -1616,8 +1661,6 @@ int sftp_rmdir(sftp_session sftp, const char *directory) { +@@ -1616,8 +1667,6 @@ int sftp_rmdir(sftp_session sftp, const return -1; } @@ -252,7 +276,7 @@ index cbcdb06..a3e0d18 100644 rc = ssh_buffer_pack(buffer, "ds", id, -@@ -1682,6 +1725,11 @@ int sftp_mkdir(sftp_session sftp, const char *directory, mode_t mode) +@@ -1682,6 +1731,11 @@ int sftp_mkdir(sftp_session sftp, const uint32_t id; int rc; @@ -264,7 +288,7 @@ index cbcdb06..a3e0d18 100644 buffer = ssh_buffer_new(); if (buffer == NULL) { ssh_set_error_oom(sftp->session); -@@ -1693,8 +1741,6 @@ int sftp_mkdir(sftp_session sftp, const char *directory, mode_t mode) +@@ -1693,8 +1747,6 @@ int sftp_mkdir(sftp_session sftp, const attr.permissions = mode; attr.flags = SSH_FILEXFER_ATTR_PERMISSIONS; @@ -273,7 +297,7 @@ index cbcdb06..a3e0d18 100644 rc = ssh_buffer_pack(buffer, "ds", id, -@@ -1784,6 +1830,11 @@ int sftp_rename(sftp_session sftp, const char *original, const char *newname) +@@ -1784,6 +1836,11 @@ int sftp_rename(sftp_session sftp, const int request_type; int rc; @@ -285,7 +309,7 @@ index cbcdb06..a3e0d18 100644 buffer = ssh_buffer_new(); if (buffer == NULL) { ssh_set_error_oom(sftp->session); -@@ -1791,8 +1842,6 @@ int sftp_rename(sftp_session sftp, const char *original, const char *newname) +@@ -1791,8 +1848,6 @@ int sftp_rename(sftp_session sftp, const return -1; } @@ -294,7 +318,7 @@ index cbcdb06..a3e0d18 100644 /* * posix-rename@openssh.com extension will be used * if it is supported by sftp -@@ -1899,6 +1948,11 @@ int sftp_setstat(sftp_session sftp, const char *file, sftp_attributes attr) +@@ -1899,6 +1954,11 @@ int sftp_setstat(sftp_session sftp, cons return -1; } @@ -306,7 +330,7 @@ index cbcdb06..a3e0d18 100644 buffer = ssh_buffer_new(); if (buffer == NULL) { ssh_set_error_oom(sftp->session); -@@ -1906,8 +1960,6 @@ int sftp_setstat(sftp_session sftp, const char *file, sftp_attributes attr) +@@ -1906,8 +1966,6 @@ int sftp_setstat(sftp_session sftp, cons return -1; } @@ -315,7 +339,7 @@ index cbcdb06..a3e0d18 100644 rc = ssh_buffer_pack(buffer, "ds", id, -@@ -1987,6 +2039,11 @@ sftp_lsetstat(sftp_session sftp, const char *file, sftp_attributes attr) +@@ -1987,6 +2045,11 @@ sftp_lsetstat(sftp_session sftp, const c return -1; } @@ -327,7 +351,7 @@ index cbcdb06..a3e0d18 100644 buffer = ssh_buffer_new(); if (buffer == NULL) { ssh_set_error_oom(sftp->session); -@@ -1994,8 +2051,6 @@ sftp_lsetstat(sftp_session sftp, const char *file, sftp_attributes attr) +@@ -1994,8 +2057,6 @@ sftp_lsetstat(sftp_session sftp, const c return -1; } @@ -336,7 +360,7 @@ index cbcdb06..a3e0d18 100644 rc = ssh_buffer_pack(buffer, "dss", id, extension_name, file); if (rc != SSH_OK) { ssh_set_error_oom(sftp->session); -@@ -2119,6 +2174,11 @@ int sftp_symlink(sftp_session sftp, const char *target, const char *dest) +@@ -2119,6 +2180,11 @@ int sftp_symlink(sftp_session sftp, cons return -1; } @@ -348,7 +372,7 @@ index cbcdb06..a3e0d18 100644 buffer = ssh_buffer_new(); if (buffer == NULL) { ssh_set_error_oom(sftp->session); -@@ -2126,8 +2186,6 @@ int sftp_symlink(sftp_session sftp, const char *target, const char *dest) +@@ -2126,8 +2192,6 @@ int sftp_symlink(sftp_session sftp, cons return -1; } @@ -357,7 +381,7 @@ index cbcdb06..a3e0d18 100644 /* The OpenSSH sftp server has order of the arguments reversed, see the * section "4.1 sftp: Reversal of arguments to SSH_FXP_SYMLINK' in * https://github.com/openssh/openssh-portable/blob/master/PROTOCOL -@@ -2220,6 +2278,12 @@ char *sftp_readlink(sftp_session sftp, const char *path) +@@ -2220,6 +2284,12 @@ char *sftp_readlink(sftp_session sftp, c sftp_set_error(sftp, SSH_FX_FAILURE); return NULL; } @@ -370,7 +394,7 @@ index cbcdb06..a3e0d18 100644 buffer = ssh_buffer_new(); if (buffer == NULL) { ssh_set_error_oom(sftp->session); -@@ -2227,8 +2291,6 @@ char *sftp_readlink(sftp_session sftp, const char *path) +@@ -2227,8 +2297,6 @@ char *sftp_readlink(sftp_session sftp, c return NULL; } @@ -379,7 +403,7 @@ index cbcdb06..a3e0d18 100644 rc = ssh_buffer_pack(buffer, "ds", id, -@@ -2310,6 +2372,11 @@ int sftp_hardlink(sftp_session sftp, const char *oldpath, const char *newpath) +@@ -2310,6 +2378,11 @@ int sftp_hardlink(sftp_session sftp, con return -1; } @@ -391,7 +415,7 @@ index cbcdb06..a3e0d18 100644 buffer = ssh_buffer_new(); if (buffer == NULL) { ssh_set_error_oom(sftp->session); -@@ -2317,8 +2384,6 @@ int sftp_hardlink(sftp_session sftp, const char *oldpath, const char *newpath) +@@ -2317,8 +2390,6 @@ int sftp_hardlink(sftp_session sftp, con return -1; } @@ -400,7 +424,7 @@ index cbcdb06..a3e0d18 100644 rc = ssh_buffer_pack(buffer, "dsss", id, -@@ -2434,6 +2499,11 @@ sftp_statvfs_t sftp_statvfs(sftp_session sftp, const char *path) +@@ -2434,6 +2505,11 @@ sftp_statvfs_t sftp_statvfs(sftp_session return NULL; } @@ -412,7 +436,7 @@ index cbcdb06..a3e0d18 100644 buffer = ssh_buffer_new(); if (buffer == NULL) { ssh_set_error_oom(sftp->session); -@@ -2441,8 +2511,6 @@ sftp_statvfs_t sftp_statvfs(sftp_session sftp, const char *path) +@@ -2441,8 +2517,6 @@ sftp_statvfs_t sftp_statvfs(sftp_session return NULL; } @@ -421,7 +445,7 @@ index cbcdb06..a3e0d18 100644 rc = ssh_buffer_pack(buffer, "dss", id, -@@ -2509,6 +2577,11 @@ int sftp_fsync(sftp_file file) +@@ -2509,6 +2583,11 @@ int sftp_fsync(sftp_file file) } sftp = file->sftp; @@ -433,7 +457,7 @@ index cbcdb06..a3e0d18 100644 buffer = ssh_buffer_new(); if (buffer == NULL) { ssh_set_error_oom(sftp->session); -@@ -2516,8 +2589,6 @@ int sftp_fsync(sftp_file file) +@@ -2516,8 +2595,6 @@ int sftp_fsync(sftp_file file) return -1; } @@ -442,7 +466,7 @@ index cbcdb06..a3e0d18 100644 rc = ssh_buffer_pack(buffer, "dsS", id, -@@ -2609,6 +2680,11 @@ sftp_statvfs_t sftp_fstatvfs(sftp_file file) +@@ -2609,6 +2686,11 @@ sftp_statvfs_t sftp_fstatvfs(sftp_file f } sftp = file->sftp; @@ -454,7 +478,7 @@ index cbcdb06..a3e0d18 100644 buffer = ssh_buffer_new(); if (buffer == NULL) { ssh_set_error_oom(sftp->session); -@@ -2616,8 +2692,6 @@ sftp_statvfs_t sftp_fstatvfs(sftp_file file) +@@ -2616,8 +2698,6 @@ sftp_statvfs_t sftp_fstatvfs(sftp_file f return NULL; } @@ -463,7 +487,7 @@ index cbcdb06..a3e0d18 100644 rc = ssh_buffer_pack(buffer, "dsS", id, -@@ -2723,6 +2797,11 @@ static sftp_limits_t sftp_limits_use_extension(sftp_session sftp) +@@ -2723,6 +2803,11 @@ static sftp_limits_t sftp_limits_use_ext if (sftp == NULL) return NULL; @@ -475,7 +499,7 @@ index cbcdb06..a3e0d18 100644 buffer = ssh_buffer_new(); if (buffer == NULL) { ssh_set_error_oom(sftp->session); -@@ -2730,8 +2809,6 @@ static sftp_limits_t sftp_limits_use_extension(sftp_session sftp) +@@ -2730,8 +2815,6 @@ static sftp_limits_t sftp_limits_use_ext return NULL; } @@ -484,7 +508,7 @@ index cbcdb06..a3e0d18 100644 rc = ssh_buffer_pack(buffer, "ds", id, -@@ -2873,6 +2950,11 @@ char *sftp_canonicalize_path(sftp_session sftp, const char *path) +@@ -2873,6 +2956,11 @@ char *sftp_canonicalize_path(sftp_sessio return NULL; } @@ -496,7 +520,7 @@ index cbcdb06..a3e0d18 100644 buffer = ssh_buffer_new(); if (buffer == NULL) { ssh_set_error_oom(sftp->session); -@@ -2880,8 +2962,6 @@ char *sftp_canonicalize_path(sftp_session sftp, const char *path) +@@ -2880,8 +2968,6 @@ char *sftp_canonicalize_path(sftp_sessio return NULL; } @@ -505,7 +529,7 @@ index cbcdb06..a3e0d18 100644 rc = ssh_buffer_pack(buffer, "ds", id, -@@ -2964,6 +3044,11 @@ static sftp_attributes sftp_xstat(sftp_session sftp, +@@ -2964,6 +3050,11 @@ static sftp_attributes sftp_xstat(sftp_s return NULL; } @@ -517,7 +541,7 @@ index cbcdb06..a3e0d18 100644 buffer = ssh_buffer_new(); if (buffer == NULL) { ssh_set_error_oom(sftp->session); -@@ -2971,8 +3056,6 @@ static sftp_attributes sftp_xstat(sftp_session sftp, +@@ -2971,8 +3062,6 @@ static sftp_attributes sftp_xstat(sftp_s return NULL; } @@ -526,7 +550,7 @@ index cbcdb06..a3e0d18 100644 rc = ssh_buffer_pack(buffer, "ds", id, -@@ -3042,6 +3125,11 @@ sftp_attributes sftp_fstat(sftp_file file) +@@ -3042,6 +3131,11 @@ sftp_attributes sftp_fstat(sftp_file fil return NULL; } @@ -538,7 +562,7 @@ index cbcdb06..a3e0d18 100644 buffer = ssh_buffer_new(); if (buffer == NULL) { ssh_set_error_oom(file->sftp->session); -@@ -3049,8 +3137,6 @@ sftp_attributes sftp_fstat(sftp_file file) +@@ -3049,8 +3143,6 @@ sftp_attributes sftp_fstat(sftp_file fil return NULL; } @@ -547,7 +571,7 @@ index cbcdb06..a3e0d18 100644 rc = ssh_buffer_pack(buffer, "dS", id, -@@ -3121,6 +3207,11 @@ char *sftp_expand_path(sftp_session sftp, const char *path) +@@ -3121,6 +3213,11 @@ char *sftp_expand_path(sftp_session sftp return NULL; } @@ -559,7 +583,7 @@ index cbcdb06..a3e0d18 100644 buffer = ssh_buffer_new(); if (buffer == NULL) { ssh_set_error_oom(sftp->session); -@@ -3128,8 +3219,6 @@ char *sftp_expand_path(sftp_session sftp, const char *path) +@@ -3128,8 +3225,6 @@ char *sftp_expand_path(sftp_session sftp return NULL; } @@ -568,7 +592,7 @@ index cbcdb06..a3e0d18 100644 rc = ssh_buffer_pack(buffer, "dss", id, -@@ -3208,6 +3297,11 @@ sftp_home_directory(sftp_session sftp, const char *username) +@@ -3208,6 +3303,11 @@ sftp_home_directory(sftp_session sftp, c return NULL; } @@ -580,7 +604,7 @@ index cbcdb06..a3e0d18 100644 buffer = ssh_buffer_new(); if (buffer == NULL) { ssh_set_error_oom(sftp->session); -@@ -3215,8 +3309,6 @@ sftp_home_directory(sftp_session sftp, const char *username) +@@ -3215,8 +3315,6 @@ sftp_home_directory(sftp_session sftp, c return NULL; } @@ -589,11 +613,11 @@ index cbcdb06..a3e0d18 100644 rc = ssh_buffer_pack(buffer, "dss", id, -diff --git a/src/sftp_aio.c b/src/sftp_aio.c -index c1c5456..9396132 100644 ---- a/src/sftp_aio.c -+++ b/src/sftp_aio.c -@@ -86,6 +86,11 @@ ssize_t sftp_aio_begin_read(sftp_file file, size_t len, sftp_aio *aio) +Index: libssh/src/sftp_aio.c +=================================================================== +--- libssh.orig/src/sftp_aio.c ++++ libssh/src/sftp_aio.c +@@ -86,6 +86,11 @@ ssize_t sftp_aio_begin_read(sftp_file fi return SSH_ERROR; } @@ -605,7 +629,7 @@ index c1c5456..9396132 100644 buffer = ssh_buffer_new(); if (buffer == NULL) { ssh_set_error_oom(sftp->session); -@@ -93,8 +98,6 @@ ssize_t sftp_aio_begin_read(sftp_file file, size_t len, sftp_aio *aio) +@@ -93,8 +98,6 @@ ssize_t sftp_aio_begin_read(sftp_file fi return SSH_ERROR; } @@ -614,7 +638,7 @@ index c1c5456..9396132 100644 rc = ssh_buffer_pack(buffer, "dSqd", id, -@@ -354,6 +357,11 @@ ssize_t sftp_aio_begin_write(sftp_file file, +@@ -354,6 +357,11 @@ ssize_t sftp_aio_begin_write(sftp_file f return SSH_ERROR; } @@ -626,7 +650,7 @@ index c1c5456..9396132 100644 buffer = ssh_buffer_new(); if (buffer == NULL) { ssh_set_error_oom(sftp->session); -@@ -361,7 +369,6 @@ ssize_t sftp_aio_begin_write(sftp_file file, +@@ -361,7 +369,6 @@ ssize_t sftp_aio_begin_write(sftp_file f return SSH_ERROR; } @@ -634,11 +658,11 @@ index c1c5456..9396132 100644 rc = ssh_buffer_pack(buffer, "dSqdP", id, -diff --git a/src/sftp_common.c b/src/sftp_common.c -index d6189b7..66d19ef 100644 ---- a/src/sftp_common.c -+++ b/src/sftp_common.c -@@ -795,6 +795,8 @@ static sftp_message sftp_get_message(sftp_packet packet) +Index: libssh/src/sftp_common.c +=================================================================== +--- libssh.orig/src/sftp_common.c ++++ libssh/src/sftp_common.c +@@ -795,6 +795,8 @@ static sftp_message sftp_get_message(sft { sftp_session sftp = packet->sftp; sftp_message msg = NULL; @@ -647,7 +671,7 @@ index d6189b7..66d19ef 100644 int rc; switch (packet->type) { -@@ -842,9 +844,63 @@ static sftp_message sftp_get_message(sftp_packet packet) +@@ -842,9 +844,63 @@ static sftp_message sftp_get_message(sft msg->id, msg->packet_type); @@ -711,11 +735,10 @@ index d6189b7..66d19ef 100644 int sftp_read_and_dispatch(sftp_session sftp) { sftp_packet packet = NULL; -diff --git a/tests/client/torture_sftp_request_id.c b/tests/client/torture_sftp_request_id.c -new file mode 100644 -index 0000000..21f2774 +Index: libssh/tests/client/torture_sftp_request_id.c +=================================================================== --- /dev/null -+++ b/tests/client/torture_sftp_request_id.c ++++ libssh/tests/client/torture_sftp_request_id.c @@ -0,0 +1,183 @@ +#include "config.h" + From bc3507e22f94358a48208eac8dc1d86e2bbc1f35 Mon Sep 17 00:00:00 2001 From: hudeng Date: Mon, 27 Jul 2026 15:54:35 +0800 Subject: [PATCH 10/10] fix(CVE-2026-59848): return NULL instead of -1 in sftp_fstatvfs (pointer return type) --- debian/patches/CVE-2026-59848.patch | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/debian/patches/CVE-2026-59848.patch b/debian/patches/CVE-2026-59848.patch index dcb7c99..b8aa513 100644 --- a/debian/patches/CVE-2026-59848.patch +++ b/debian/patches/CVE-2026-59848.patch @@ -472,7 +472,7 @@ Index: libssh/src/sftp.c + rc = sftp_get_new_id(sftp, &id); + if (rc != SSH_OK) { -+ return -1; ++ return NULL; + } + buffer = ssh_buffer_new();