From 34d0cc50229cf211b35bcba5b99b4cd1b6451931 Mon Sep 17 00:00:00 2001 From: Azure Linux Security Servicing Account Date: Tue, 28 Jul 2026 07:37:12 +0000 Subject: [PATCH 1/3] Patch libssh for CVE-2026-59850, CVE-2026-59847, CVE-2026-59845, CVE-2026-59844, CVE-2026-59843 --- SPECS/libssh/CVE-2026-59843.patch | 63 +++++++++++++++++++++++++++++ SPECS/libssh/CVE-2026-59844.patch | 46 +++++++++++++++++++++ SPECS/libssh/CVE-2026-59845.patch | 67 +++++++++++++++++++++++++++++++ SPECS/libssh/CVE-2026-59847.patch | 37 +++++++++++++++++ SPECS/libssh/CVE-2026-59850.patch | 33 +++++++++++++++ SPECS/libssh/libssh.spec | 10 ++++- 6 files changed, 255 insertions(+), 1 deletion(-) create mode 100644 SPECS/libssh/CVE-2026-59843.patch create mode 100644 SPECS/libssh/CVE-2026-59844.patch create mode 100644 SPECS/libssh/CVE-2026-59845.patch create mode 100644 SPECS/libssh/CVE-2026-59847.patch create mode 100644 SPECS/libssh/CVE-2026-59850.patch diff --git a/SPECS/libssh/CVE-2026-59843.patch b/SPECS/libssh/CVE-2026-59843.patch new file mode 100644 index 00000000000..ff3d45a8613 --- /dev/null +++ b/SPECS/libssh/CVE-2026-59843.patch @@ -0,0 +1,63 @@ +From 65a8b74257154acc6ba80e861c4d885cfa77afb0 Mon Sep 17 00:00:00 2001 +From: AllSpark +Date: Tue, 28 Jul 2026 06:56:03 +0000 +Subject: [PATCH] channels: Fail when receiving max packet size 0 + +Signed-off-by: Azure Linux Security Servicing Account +Upstream-reference: AI Backport of https://git.libssh.org/projects/libssh.git/patch/?id=44b186fa17aff497dae420c59c003222e438103c +--- + src/channels.c | 7 +++++++ + src/messages.c | 19 +++++++++++++++---- + 2 files changed, 22 insertions(+), 4 deletions(-) + +diff --git a/src/channels.c b/src/channels.c +index 52d001b..6276323 100644 +--- a/src/channels.c ++++ b/src/channels.c +@@ -192,6 +192,13 @@ SSH_PACKET_CALLBACK(ssh_packet_channel_open_conf){ + if (rc != SSH_OK) + goto error; + ++ if (channel->remote_maxpacket == 0) { ++ SSH_LOG(SSH_LOG_RARE, ++ "Invalid maximum packet size 0 in " ++ "SSH2_MSG_CHANNEL_OPEN_CONFIRMATION"); ++ goto error; ++ } ++ + SSH_LOG(SSH_LOG_PROTOCOL, + "Received a CHANNEL_OPEN_CONFIRMATION for channel %d:%d", + channel->local_channel, +diff --git a/src/messages.c b/src/messages.c +index 6dadabf..e79ecec 100644 +--- a/src/messages.c ++++ b/src/messages.c +@@ -1160,10 +1160,21 @@ SSH_PACKET_CALLBACK(ssh_packet_channel_open){ + SSH_LOG(SSH_LOG_PACKET, + "Clients wants to open a %s channel", type_c); + +- ssh_buffer_unpack(packet,"ddd", +- &msg->channel_request_open.sender, +- &msg->channel_request_open.window, +- &msg->channel_request_open.packet_size); ++ rc = ssh_buffer_unpack(packet, ++ "ddd", ++ &msg->channel_request_open.sender, ++ &msg->channel_request_open.window, ++ &msg->channel_request_open.packet_size); ++ if (rc != SSH_OK){ ++ goto error; ++ } ++ ++ if (msg->channel_request_open.packet_size == 0) { ++ ssh_set_error(session, ++ SSH_FATAL, ++ "Invalid maximum packet size 0 in SSH2_MSG_CHANNEL_OPEN"); ++ goto error; ++ } + + if (session->session_state != SSH_SESSION_STATE_AUTHENTICATED){ + ssh_set_error(session,SSH_FATAL, "Invalid state when receiving channel open request (must be authenticated)"); +-- +2.45.4 + diff --git a/SPECS/libssh/CVE-2026-59844.patch b/SPECS/libssh/CVE-2026-59844.patch new file mode 100644 index 00000000000..fe879c2f96c --- /dev/null +++ b/SPECS/libssh/CVE-2026-59844.patch @@ -0,0 +1,46 @@ +From 7f039ce9852b5ee29c1438137ada1db7f1a6a69e Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Pavol=20=C5=BD=C3=A1=C4=8Dik?= +Date: Fri, 6 Mar 2026 18:05:29 +0100 +Subject: [PATCH] CVE-2026-59844 sftpserver: cap accepted values of len in + SSH_FXP_READ +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +The client-provided length is directly used in +a malloc in process_read(), so not restricting it +leads to allocations bounded only by UINT32_MAX. + +The new cap is the same as the one currently used +by OpenSSH. + +Signed-off-by: Pavol Žáčik +Reviewed-by: Jakub Jelen +(cherry picked from commit 6dba2e06f0713c04ad5eca7d4315d0104be7e627) +Signed-off-by: Azure Linux Security Servicing Account +Upstream-reference: https://git.libssh.org/projects/libssh.git/patch/?id=2544f22733ffcd59a2e51e2950f80901d063b946 +--- + src/sftpserver.c | 7 +++++++ + 1 file changed, 7 insertions(+) + +diff --git a/src/sftpserver.c b/src/sftpserver.c +index 528ef6f..d2352c0 100644 +--- a/src/sftpserver.c ++++ b/src/sftpserver.c +@@ -143,6 +143,13 @@ sftp_client_message sftp_get_client_message(sftp_session sftp) { + sftp_client_message_free(msg); + return NULL; + } ++ if (msg->len > MAX_PACKET_LEN - 1024) { ++ ssh_set_error(sftp->session, ++ SSH_FATAL, ++ "Too large SSH_FXP_READ length: %" PRIu32, ++ msg->len); ++ goto error; ++ } + break; + case SSH_FXP_MKDIR: + case SSH_FXP_SETSTAT: +-- +2.45.4 + diff --git a/SPECS/libssh/CVE-2026-59845.patch b/SPECS/libssh/CVE-2026-59845.patch new file mode 100644 index 00000000000..5e77f35a9e8 --- /dev/null +++ b/SPECS/libssh/CVE-2026-59845.patch @@ -0,0 +1,67 @@ +From e27315bb88e155a8bdcab162f87b6cb7894403ec Mon Sep 17 00:00:00 2001 +From: Jakub Jelen +Date: Thu, 26 Mar 2026 16:32:24 +0100 +Subject: [PATCH] CVE-2026-59845 socket: Properly check fork() return code +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +During execution of proxy command, when fork() fails, its return value +is stored in pid and when the parent process attempts to kill it, +it sends the kill signal to all processes the calling application has +access to (except for init). + +This caused nard to debug issues when the system under the load was hitting +fork failures, which resulted in killing of all the system processes +(of given user). + +Reported and first patch iteration provided by: Halil Oktay (oblivionsage). + +This code missing fork return value check is in libssh since 2010 +(f31a14b7932ef4cc165ddd8f1f1a5b23eb21beb3), but this issue is exploitable only +since libssh 0.9.0 as previously there was no implementation of killing +ProxyCommand children. + +Signed-off-by: Jakub Jelen +Reviewed-by: Pavol Žáčik +(cherry picked from commit 92b6fb9c5e2d1606e8f809fd884ab6dd4d3b7d45) +Signed-off-by: Azure Linux Security Servicing Account +Upstream-reference: https://git.libssh.org/projects/libssh.git/patch/?id=53b8152623290c69657a6774d96888b876e6061f +--- + src/socket.c | 13 ++++++++++++- + 1 file changed, 12 insertions(+), 1 deletion(-) + +diff --git a/src/socket.c b/src/socket.c +index 99dcf8c..ba9ba52 100644 +--- a/src/socket.c ++++ b/src/socket.c +@@ -964,6 +964,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; +@@ -982,7 +983,17 @@ ssh_socket_connect_proxycommand(ssh_socket s, const char *command) + pid = fork(); + if (pid == 0) { + ssh_execute_command(command, pair[0], pair[0]); +- /* Does not return */ ++ /* child: Does not return */ ++ } ++ /* parent */ ++ if (pid == -1) { ++ close(pair[0]); ++ close(pair[1]); ++ ssh_set_error(s->session, ++ SSH_FATAL, ++ "fork failed: %s", ++ ssh_strerror(errno, err_msg, SSH_ERRNO_MSG_MAX)); ++ return SSH_ERROR; + } + s->proxy_pid = pid; + close(pair[0]); +-- +2.45.4 + diff --git a/SPECS/libssh/CVE-2026-59847.patch b/SPECS/libssh/CVE-2026-59847.patch new file mode 100644 index 00000000000..944a9b5bac0 --- /dev/null +++ b/SPECS/libssh/CVE-2026-59847.patch @@ -0,0 +1,37 @@ +From e8547607304c02bdd7191e8f3640b0eb99fa340f Mon Sep 17 00:00:00 2001 +From: AllSpark +Date: Tue, 28 Jul 2026 06:50:36 +0000 +Subject: [PATCH] CVE-2026-59847 libcrypto: Fix tag verification of AES-GCM + ciphers + +Signed-off-by: Azure Linux Security Servicing Account +Upstream-reference: AI Backport of https://git.libssh.org/projects/libssh.git/patch/?id=c483a187354dfd96b16d3309a74f6d1cf82c2074 +--- + src/libcrypto.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/src/libcrypto.c b/src/libcrypto.c +index aa48c67..c610f37 100644 +--- a/src/libcrypto.c ++++ b/src/libcrypto.c +@@ -585,7 +585,7 @@ evp_cipher_aead_encrypt(struct ssh_cipher_struct *cipher, + rc = EVP_EncryptFinal(cipher->ctx, + NULL, + &tmplen); +- if (rc < 0) { ++ if (rc != 1 || outlen != 0) { + SSH_LOG(SSH_LOG_WARNING, "EVP_EncryptFinal failed: Failed to create a tag"); + return; + } +@@ -673,7 +673,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_WARNING, "EVP_DecryptFinal failed: Failed authentication"); + return SSH_ERROR; + } +-- +2.45.4 + diff --git a/SPECS/libssh/CVE-2026-59850.patch b/SPECS/libssh/CVE-2026-59850.patch new file mode 100644 index 00000000000..82d0b950835 --- /dev/null +++ b/SPECS/libssh/CVE-2026-59850.patch @@ -0,0 +1,33 @@ +From fe0bd041bf6103575ed29649d584d26d8630ab63 Mon Sep 17 00:00:00 2001 +From: AllSpark +Date: Tue, 28 Jul 2026 06:50:24 +0000 +Subject: [PATCH] channels: Avoid processing DATA packets on closed channels + +Signed-off-by: Azure Linux Security Servicing Account +Upstream-reference: AI Backport of https://git.libssh.org/projects/libssh.git/patch/?id=7dfabb1fd213196c4912c314b418ff36c882ea54 +--- + src/channels.c | 8 ++++++++ + 1 file changed, 8 insertions(+) + +diff --git a/src/channels.c b/src/channels.c +index 8290dbd..52d001b 100644 +--- a/src/channels.c ++++ b/src/channels.c +@@ -568,6 +568,14 @@ 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; ++ } ++ + /* What shall we do in this case? Let's accept it anyway */ + if (len > channel->local_window) { + SSH_LOG(SSH_LOG_RARE, +-- +2.45.4 + diff --git a/SPECS/libssh/libssh.spec b/SPECS/libssh/libssh.spec index ccb6e32f259..a7142158278 100644 --- a/SPECS/libssh/libssh.spec +++ b/SPECS/libssh/libssh.spec @@ -2,7 +2,7 @@ Vendor: Microsoft Corporation Distribution: Azure Linux Name: libssh Version: 0.10.6 -Release: 8%{?dist} +Release: 9%{?dist} Summary: A library implementing the SSH protocol License: LGPLv2+ URL: http://www.libssh.org @@ -25,6 +25,11 @@ Patch9: CVE-2026-0965.patch Patch10: CVE-2026-0966.patch Patch11: CVE-2026-0967.patch Patch12: CVE-2026-0968.patch +Patch13: CVE-2026-59843.patch +Patch14: CVE-2026-59844.patch +Patch15: CVE-2026-59845.patch +Patch16: CVE-2026-59847.patch +Patch17: CVE-2026-59850.patch BuildRequires: cmake BuildRequires: gcc-c++ @@ -158,6 +163,9 @@ popd %attr(0644,root,root) %config(noreplace) %{_sysconfdir}/libssh/libssh_server.config %changelog +* Tue Jul 28 2026 Azure Linux Security Servicing Account - 0.10.6-9 +- Patch for CVE-2026-59850, CVE-2026-59847, CVE-2026-59845, CVE-2026-59844, CVE-2026-59843 + * Tue May 05 2026 Azure Linux Security Servicing Account - 0.10.6-8 - Patch for CVE-2026-0968 From a9ec7772be7749f17bfd57385bdb0216ad10c05c Mon Sep 17 00:00:00 2001 From: Azure Linux Security Servicing Account Date: Tue, 28 Jul 2026 08:18:47 +0000 Subject: [PATCH 2/3] Patch libssh for CVE-2026-59848 --- SPECS/libssh/CVE-2026-59848.patch | 418 ++++++++++++++++++++++++++++++ SPECS/libssh/libssh.spec | 3 +- 2 files changed, 420 insertions(+), 1 deletion(-) create mode 100644 SPECS/libssh/CVE-2026-59848.patch diff --git a/SPECS/libssh/CVE-2026-59848.patch b/SPECS/libssh/CVE-2026-59848.patch new file mode 100644 index 00000000000..11c2ab74059 --- /dev/null +++ b/SPECS/libssh/CVE-2026-59848.patch @@ -0,0 +1,418 @@ +From b3e1c7a31e19a6f5ae2351f817221b335fdf74af Mon Sep 17 00:00:00 2001 +From: AllSpark +Date: Tue, 28 Jul 2026 08:11:54 +0000 +Subject: [PATCH] sftp: track and validate outstanding request IDs + +Signed-off-by: Azure Linux Security Servicing Account +Upstream-reference: AI Backport of https://git.libssh.org/projects/libssh.git/patch/?id=9563afc950f473daa355ca594e2e5f4d520460ac https://git.libssh.org/projects/libssh.git/patch/?id=e3dc89de9754790e49b26f03b70e8e4acc88bde8 +--- + include/libssh/sftp.h | 1 + + src/sftp.c | 185 ++++++++++++++++++++++++++++++++++++------ + 2 files changed, 163 insertions(+), 23 deletions(-) + +diff --git a/include/libssh/sftp.h b/include/libssh/sftp.h +index c713466..984c4eb 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/src/sftp.c b/src/sftp.c +index 2291d69..0a01481 100644 +--- a/src/sftp.c ++++ b/src/sftp.c +@@ -130,6 +130,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; ++ } ++ + sftp->read_packet = calloc(1, sizeof(struct sftp_packet_struct)); + if (sftp->read_packet == NULL) { + ssh_set_error_oom(session); +@@ -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); +@@ -351,13 +365,20 @@ void sftp_server_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); + } + #endif /* WITH_SERVER */ + + 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; +@@ -384,6 +405,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); + } + +@@ -613,6 +640,35 @@ static sftp_message sftp_get_message(sftp_packet packet) + return NULL; + } + ++ { ++ struct ssh_iterator *it = NULL; ++ struct ssh_iterator *match = NULL; ++ uint32_t *stored_id = NULL; ++ ++ for (it = ssh_list_get_iterator(sftp->outstanding_ids); ++ it != NULL; ++ it = it->next) { ++ stored_id = (uint32_t *)it->data; ++ if (stored_id != NULL && *stored_id == msg->id) { ++ match = it; ++ break; ++ } ++ } ++ ++ if (match == NULL) { ++ ssh_set_error(packet->sftp->session, SSH_FATAL, ++ "Received message with unknown id %u", ++ msg->id); ++ sftp_message_free(msg); ++ sftp_set_error(packet->sftp, SSH_FX_BAD_MESSAGE); ++ return NULL; ++ } ++ ++ stored_id = (uint32_t *)match->data; ++ ssh_list_remove(sftp->outstanding_ids, match); ++ free(stored_id); ++ } ++ + SSH_LOG(SSH_LOG_PACKET, + "Packet with id %d type %d", + msg->id, +@@ -907,8 +963,30 @@ static sftp_message sftp_dequeue(sftp_session sftp, uint32_t id){ + * 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; ++static int sftp_get_new_id(sftp_session session, uint32_t *id) ++{ ++ uint32_t *new_id = NULL; ++ int rc; ++ ++ if (session == NULL || id == NULL) { ++ return SSH_ERROR; ++ } ++ ++ new_id = malloc(sizeof(uint32_t)); ++ if (new_id == NULL) { ++ return SSH_ERROR; ++ } ++ ++ *new_id = ++session->id_counter; ++ rc = ssh_list_append(session->outstanding_ids, new_id); ++ if (rc != SSH_OK) { ++ free(new_id); ++ return SSH_ERROR; ++ } ++ ++ *id = *new_id; ++ ++ return SSH_OK; + } + + static sftp_status_message parse_status_msg(sftp_message msg){ +@@ -1029,7 +1107,10 @@ sftp_dir sftp_opendir(sftp_session sftp, const char *path) + return NULL; + } + +- id = sftp_get_new_id(sftp); ++ if (sftp_get_new_id(sftp, &id) != SSH_OK) { ++ ssh_set_error_oom(session); ++ return NULL; ++ } + + rc = ssh_buffer_pack(payload, + "ds", +@@ -1571,7 +1652,10 @@ sftp_attributes sftp_readdir(sftp_session sftp, sftp_dir dir) + return NULL; + } + +- id = sftp_get_new_id(sftp); ++ if (sftp_get_new_id(sftp, &id) != SSH_OK) { ++ ssh_set_error_oom(session); ++ goto error; ++ } + + rc = ssh_buffer_pack(payload, + "dS", +@@ -1704,7 +1788,10 @@ static int sftp_handle_close(sftp_session sftp, ssh_string handle) + return -1; + } + +- id = sftp_get_new_id(sftp); ++ if (sftp_get_new_id(sftp, &id) != SSH_OK) { ++ ssh_set_error_oom(session); ++ goto error; ++ } + + rc = ssh_buffer_pack(buffer, + "dS", +@@ -1835,7 +1922,10 @@ 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); ++ if (sftp_get_new_id(sftp, &id) != SSH_OK) { ++ ssh_set_error_oom(session); ++ goto error; ++ } + + rc = ssh_buffer_pack(buffer, + "dsd", +@@ -1946,7 +2036,10 @@ ssize_t sftp_read(sftp_file handle, void *buf, size_t count) { + return -1; + } + +- id = sftp_get_new_id(handle->sftp); ++ if (sftp_get_new_id(handle->sftp, &id) != SSH_OK) { ++ ssh_set_error_oom(handle->sftp->session); ++ return SSH_ERROR; ++ } + + rc = ssh_buffer_pack(buffer, + "dSqd", +@@ -2047,7 +2140,11 @@ int sftp_async_read_begin(sftp_file file, uint32_t len){ + return -1; + } + +- id = sftp_get_new_id(sftp); ++ if (sftp_get_new_id(sftp, &id) != SSH_OK) { ++ ssh_set_error_oom(sftp->session); ++ sftp_set_error(sftp, SSH_FX_FAILURE); ++ return -1; ++ } + + rc = ssh_buffer_pack(buffer, + "dSqd", +@@ -2173,7 +2270,10 @@ ssize_t sftp_write(sftp_file file, const void *buf, size_t count) { + return -1; + } + +- id = sftp_get_new_id(file->sftp); ++ if (sftp_get_new_id(file->sftp, &id) != SSH_OK) { ++ ssh_set_error_oom(file->sftp->session); ++ return SSH_ERROR; ++ } + + rc = ssh_buffer_pack(buffer, + "dSqdP", +@@ -2291,7 +2391,10 @@ int sftp_unlink(sftp_session sftp, const char *file) { + return -1; + } + +- id = sftp_get_new_id(sftp); ++ if (sftp_get_new_id(sftp, &id) != SSH_OK) { ++ ssh_set_error_oom(sftp->session); ++ goto error; ++ } + + rc = ssh_buffer_pack(buffer, + "ds", +@@ -2366,7 +2469,10 @@ int sftp_rmdir(sftp_session sftp, const char *directory) { + return -1; + } + +- id = sftp_get_new_id(sftp); ++ if (sftp_get_new_id(sftp, &id) != SSH_OK) { ++ ssh_set_error_oom(sftp->session); ++ goto error; ++ } + + rc = ssh_buffer_pack(buffer, + "ds", +@@ -2443,7 +2549,10 @@ 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); ++ if (sftp_get_new_id(sftp, &id) != SSH_OK) { ++ ssh_set_error_oom(session); ++ goto error; ++ } + + rc = ssh_buffer_pack(buffer, + "ds", +@@ -2538,7 +2647,10 @@ int sftp_rename(sftp_session sftp, const char *original, const char *newname) { + return -1; + } + +- id = sftp_get_new_id(sftp); ++ if (sftp_get_new_id(sftp, &id) != SSH_OK) { ++ ssh_set_error_oom(sftp->session); ++ goto error; ++ } + + rc = ssh_buffer_pack(buffer, + "dss", +@@ -2622,7 +2734,10 @@ int sftp_setstat(sftp_session sftp, const char *file, sftp_attributes attr) + return -1; + } + +- id = sftp_get_new_id(sftp); ++ if (sftp_get_new_id(sftp, &id) != SSH_OK) { ++ ssh_set_error_oom(session); ++ goto error; ++ } + + rc = ssh_buffer_pack(buffer, + "ds", +@@ -2752,7 +2867,10 @@ int sftp_symlink(sftp_session sftp, const char *target, const char *dest) { + return -1; + } + +- id = sftp_get_new_id(sftp); ++ if (sftp_get_new_id(sftp, &id) != SSH_OK) { ++ ssh_set_error_oom(sftp->session); ++ goto error; ++ } + + /* TODO check for version number if they ever fix it. */ + if (ssh_get_openssh_version(sftp->session)) { +@@ -2850,7 +2968,10 @@ char *sftp_readlink(sftp_session sftp, const char *path) + return NULL; + } + +- id = sftp_get_new_id(sftp); ++ if (sftp_get_new_id(sftp, &id) != SSH_OK) { ++ ssh_set_error_oom(session); ++ goto error; ++ } + + rc = ssh_buffer_pack(buffer, + "ds", +@@ -2976,7 +3097,10 @@ sftp_statvfs_t sftp_statvfs(sftp_session sftp, const char *path) + return NULL; + } + +- id = sftp_get_new_id(sftp); ++ if (sftp_get_new_id(sftp, &id) != SSH_OK) { ++ ssh_set_error_oom(session); ++ goto error; ++ } + + rc = ssh_buffer_pack(buffer, + "dss", +@@ -3051,7 +3175,10 @@ int sftp_fsync(sftp_file file) + return -1; + } + +- id = sftp_get_new_id(sftp); ++ if (sftp_get_new_id(sftp, &id) != SSH_OK) { ++ ssh_set_error_oom(session); ++ goto error; ++ } + + rc = ssh_buffer_pack(buffer, + "dsS", +@@ -3151,7 +3278,10 @@ sftp_statvfs_t sftp_fstatvfs(sftp_file file) + return NULL; + } + +- id = sftp_get_new_id(sftp); ++ if (sftp_get_new_id(sftp, &id) != SSH_OK) { ++ ssh_set_error_oom(session); ++ goto error; ++ } + + rc = ssh_buffer_pack(buffer, + "dsS", +@@ -3238,7 +3368,10 @@ char *sftp_canonicalize_path(sftp_session sftp, const char *path) + return NULL; + } + +- id = sftp_get_new_id(sftp); ++ if (sftp_get_new_id(sftp, &id) != SSH_OK) { ++ ssh_set_error_oom(session); ++ goto error; ++ } + + rc = ssh_buffer_pack(buffer, + "ds", +@@ -3329,7 +3462,10 @@ static sftp_attributes sftp_xstat(sftp_session sftp, + return NULL; + } + +- id = sftp_get_new_id(sftp); ++ if (sftp_get_new_id(sftp, &id) != SSH_OK) { ++ ssh_set_error_oom(session); ++ goto error; ++ } + + rc = ssh_buffer_pack(buffer, + "ds", +@@ -3407,7 +3543,10 @@ sftp_attributes sftp_fstat(sftp_file file) + return NULL; + } + +- id = sftp_get_new_id(file->sftp); ++ if (sftp_get_new_id(file->sftp, &id) != SSH_OK) { ++ ssh_set_error_oom(file->sftp->session); ++ return NULL; ++ } + + rc = ssh_buffer_pack(buffer, + "dS", +-- +2.45.4 + diff --git a/SPECS/libssh/libssh.spec b/SPECS/libssh/libssh.spec index a7142158278..5e429ac8380 100644 --- a/SPECS/libssh/libssh.spec +++ b/SPECS/libssh/libssh.spec @@ -30,6 +30,7 @@ Patch14: CVE-2026-59844.patch Patch15: CVE-2026-59845.patch Patch16: CVE-2026-59847.patch Patch17: CVE-2026-59850.patch +Patch18: CVE-2026-59848.patch BuildRequires: cmake BuildRequires: gcc-c++ @@ -164,7 +165,7 @@ popd %changelog * Tue Jul 28 2026 Azure Linux Security Servicing Account - 0.10.6-9 -- Patch for CVE-2026-59850, CVE-2026-59847, CVE-2026-59845, CVE-2026-59844, CVE-2026-59843 +- Patch for CVE-2026-59850, CVE-2026-59847, CVE-2026-59845, CVE-2026-59844, CVE-2026-59843, CVE-2026-59848 * Tue May 05 2026 Azure Linux Security Servicing Account - 0.10.6-8 - Patch for CVE-2026-0968 From 2ebc43fbb0e5801dc89b694e28f948a99e6db8a9 Mon Sep 17 00:00:00 2001 From: Sushil Sati Date: Wed, 29 Jul 2026 01:45:16 +0000 Subject: [PATCH 3/3] Updated for CVE-2026-59844.patch, CVE-2026-59847.patch, CVE-2026-59848.patch --- SPECS/libssh/CVE-2026-59844.patch | 27 +- SPECS/libssh/CVE-2026-59847.patch | 15 +- SPECS/libssh/CVE-2026-59848.patch | 772 +++++++++++++++++++++++------- 3 files changed, 611 insertions(+), 203 deletions(-) diff --git a/SPECS/libssh/CVE-2026-59844.patch b/SPECS/libssh/CVE-2026-59844.patch index fe879c2f96c..a4e50a9d9ae 100644 --- a/SPECS/libssh/CVE-2026-59844.patch +++ b/SPECS/libssh/CVE-2026-59844.patch @@ -20,27 +20,28 @@ Reviewed-by: Jakub Jelen Signed-off-by: Azure Linux Security Servicing Account Upstream-reference: https://git.libssh.org/projects/libssh.git/patch/?id=2544f22733ffcd59a2e51e2950f80901d063b946 --- - src/sftpserver.c | 7 +++++++ - 1 file changed, 7 insertions(+) + src/sftpserver.c | 8 ++++++++ + 1 file changed, 8 insertions(+) diff --git a/src/sftpserver.c b/src/sftpserver.c -index 528ef6f..d2352c0 100644 +index 528ef6f..60f9071 100644 --- a/src/sftpserver.c +++ b/src/sftpserver.c -@@ -143,6 +143,13 @@ sftp_client_message sftp_get_client_message(sftp_session sftp) { +@@ -105,6 +105,14 @@ sftp_client_message sftp_get_client_message(sftp_session sftp) { sftp_client_message_free(msg); return NULL; } -+ if (msg->len > MAX_PACKET_LEN - 1024) { -+ ssh_set_error(sftp->session, -+ SSH_FATAL, -+ "Too large SSH_FXP_READ length: %" PRIu32, -+ msg->len); -+ 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); ++ sftp_client_message_free(msg); ++ return NULL; ++ } break; - case SSH_FXP_MKDIR: - case SSH_FXP_SETSTAT: + case SSH_FXP_WRITE: + rc = ssh_buffer_unpack(payload, -- 2.45.4 diff --git a/SPECS/libssh/CVE-2026-59847.patch b/SPECS/libssh/CVE-2026-59847.patch index 944a9b5bac0..7c315e62d57 100644 --- a/SPECS/libssh/CVE-2026-59847.patch +++ b/SPECS/libssh/CVE-2026-59847.patch @@ -7,22 +7,13 @@ Subject: [PATCH] CVE-2026-59847 libcrypto: Fix tag verification of AES-GCM Signed-off-by: Azure Linux Security Servicing Account Upstream-reference: AI Backport of https://git.libssh.org/projects/libssh.git/patch/?id=c483a187354dfd96b16d3309a74f6d1cf82c2074 --- - src/libcrypto.c | 4 ++-- - 1 file changed, 2 insertions(+), 2 deletions(-) + src/libcrypto.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libcrypto.c b/src/libcrypto.c -index aa48c67..c610f37 100644 +index aa48c67..81f5214 100644 --- a/src/libcrypto.c +++ b/src/libcrypto.c -@@ -585,7 +585,7 @@ evp_cipher_aead_encrypt(struct ssh_cipher_struct *cipher, - rc = EVP_EncryptFinal(cipher->ctx, - NULL, - &tmplen); -- if (rc < 0) { -+ if (rc != 1 || outlen != 0) { - SSH_LOG(SSH_LOG_WARNING, "EVP_EncryptFinal failed: Failed to create a tag"); - return; - } @@ -673,7 +673,7 @@ evp_cipher_aead_decrypt(struct ssh_cipher_struct *cipher, rc = EVP_DecryptFinal(cipher->ctx, NULL, diff --git a/SPECS/libssh/CVE-2026-59848.patch b/SPECS/libssh/CVE-2026-59848.patch index 11c2ab74059..e5c51dd6278 100644 --- a/SPECS/libssh/CVE-2026-59848.patch +++ b/SPECS/libssh/CVE-2026-59848.patch @@ -6,9 +6,13 @@ Subject: [PATCH] sftp: track and validate outstanding request IDs Signed-off-by: Azure Linux Security Servicing Account Upstream-reference: AI Backport of https://git.libssh.org/projects/libssh.git/patch/?id=9563afc950f473daa355ca594e2e5f4d520460ac https://git.libssh.org/projects/libssh.git/patch/?id=e3dc89de9754790e49b26f03b70e8e4acc88bde8 --- - include/libssh/sftp.h | 1 + - src/sftp.c | 185 ++++++++++++++++++++++++++++++++++++------ - 2 files changed, 163 insertions(+), 23 deletions(-) + include/libssh/sftp.h | 1 + + include/libssh/sftp_priv.h | 11 ++ + src/sftp.c | 232 ++++++++++++++++++++----- + tests/client/CMakeLists.txt | 1 + + tests/client/torture_sftp_request_id.c | 182 +++++++++++++++++++ + 5 files changed, 381 insertions(+), 46 deletions(-) + create mode 100644 tests/client/torture_sftp_request_id.c diff --git a/include/libssh/sftp.h b/include/libssh/sftp.h index c713466..984c4eb 100644 @@ -22,8 +26,30 @@ index c713466..984c4eb 100644 }; struct sftp_packet_struct { +diff --git a/include/libssh/sftp_priv.h b/include/libssh/sftp_priv.h +index 005bf85..6ba3d59 100644 +--- a/include/libssh/sftp_priv.h ++++ b/include/libssh/sftp_priv.h +@@ -32,6 +32,17 @@ int buffer_add_attributes(ssh_buffer buffer, sftp_attributes attr); + 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); + + #ifdef __cplusplus + } diff --git a/src/sftp.c b/src/sftp.c -index 2291d69..0a01481 100644 +index 2291d69..5e4d74e 100644 --- a/src/sftp.c +++ b/src/sftp.c @@ -130,6 +130,12 @@ sftp_session sftp_new(ssh_session session) @@ -68,19 +94,7 @@ index 2291d69..0a01481 100644 if (sftp->read_packet != NULL) { if (sftp->read_packet->payload != NULL) { SSH_BUFFER_FREE(sftp->read_packet->payload); -@@ -351,13 +365,20 @@ void sftp_server_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); - } - #endif /* WITH_SERVER */ +@@ -357,7 +371,8 @@ void sftp_server_free(sftp_session sftp) void sftp_free(sftp_session sftp) { @@ -90,329 +104,731 @@ index 2291d69..0a01481 100644 if (sftp == NULL) { return; -@@ -384,6 +405,12 @@ void sftp_free(sftp_session sftp) +@@ -383,6 +398,11 @@ void sftp_free(sftp_session sftp) + SAFE_FREE(sftp->read_packet); 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 +591,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; -@@ -613,6 +640,35 @@ static sftp_message sftp_get_message(sftp_packet packet) - return NULL; - } - -+ { -+ struct ssh_iterator *it = NULL; -+ struct ssh_iterator *match = NULL; -+ uint32_t *stored_id = NULL; -+ -+ for (it = ssh_list_get_iterator(sftp->outstanding_ids); -+ it != NULL; -+ it = it->next) { -+ stored_id = (uint32_t *)it->data; -+ if (stored_id != NULL && *stored_id == msg->id) { -+ match = it; -+ break; -+ } -+ } -+ -+ if (match == NULL) { -+ ssh_set_error(packet->sftp->session, SSH_FATAL, -+ "Received message with unknown id %u", -+ msg->id); -+ sftp_message_free(msg); -+ sftp_set_error(packet->sftp, SSH_FX_BAD_MESSAGE); -+ return NULL; + switch(packet->type) { +@@ -617,6 +639,27 @@ static sftp_message sftp_get_message(sftp_packet packet) + "Packet with id %d type %d", + 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; + } -+ -+ stored_id = (uint32_t *)match->data; -+ ssh_list_remove(sftp->outstanding_ids, match); -+ free(stored_id); + } + - SSH_LOG(SSH_LOG_PACKET, - "Packet with id %d type %d", - msg->id, -@@ -907,8 +963,30 @@ static sftp_message sftp_dequeue(sftp_session sftp, uint32_t id){ - * between them. - * Returns a new ID ready to use in a request ++ 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,15 +945,50 @@ 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 ++/** ++ * @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; -+static int sftp_get_new_id(sftp_session session, uint32_t *id) ++ ++int sftp_get_new_id(sftp_session sftp, uint32_t *id_out) +{ -+ uint32_t *new_id = NULL; ++ uint32_t *id = NULL; + int rc; + -+ if (session == NULL || id == NULL) { ++ if (id_out == NULL) { ++ ssh_set_error_invalid(sftp->session); ++ sftp_set_error(sftp, SSH_FX_FAILURE); + return SSH_ERROR; + } + -+ new_id = malloc(sizeof(uint32_t)); -+ if (new_id == NULL) { ++ id = malloc(sizeof(uint32_t)); ++ if (id == NULL) { ++ ssh_set_error_oom(sftp->session); ++ sftp_set_error(sftp, SSH_FX_FAILURE); + return SSH_ERROR; + } + -+ *new_id = ++session->id_counter; -+ rc = ssh_list_append(session->outstanding_ids, new_id); ++ *id = ++sftp->id_counter; ++ rc = ssh_list_append(sftp->outstanding_ids, id); + if (rc != SSH_OK) { -+ free(new_id); ++ free(id); ++ ssh_set_error_oom(sftp->session); ++ sftp_set_error(sftp, SSH_FX_FAILURE); + return SSH_ERROR; + } + -+ *id = *new_id; ++ *id_out = *id; + + return SSH_OK; } ++ static sftp_status_message parse_status_msg(sftp_message msg){ -@@ -1029,7 +1107,10 @@ sftp_dir sftp_opendir(sftp_session sftp, const char *path) + sftp_status_message status; + int rc; +@@ -1022,6 +1100,11 @@ sftp_dir sftp_opendir(sftp_session sftp, const char *path) return NULL; } -- id = sftp_get_new_id(sftp); -+ if (sftp_get_new_id(sftp, &id) != SSH_OK) { -+ ssh_set_error_oom(session); ++ 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 +1112,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", -@@ -1571,7 +1652,10 @@ sftp_attributes sftp_readdir(sftp_session sftp, sftp_dir dir) + id, +@@ -1564,6 +1645,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 +1657,6 @@ sftp_attributes sftp_readdir(sftp_session sftp, sftp_dir dir) return NULL; } - id = sftp_get_new_id(sftp); -+ if (sftp_get_new_id(sftp, &id) != SSH_OK) { -+ ssh_set_error_oom(session); -+ goto error; -+ } - +- rc = ssh_buffer_pack(payload, "dS", -@@ -1704,7 +1788,10 @@ static int sftp_handle_close(sftp_session sftp, ssh_string handle) + id, +@@ -1697,6 +1781,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 +1793,6 @@ static int sftp_handle_close(sftp_session sftp, ssh_string handle) return -1; } - id = sftp_get_new_id(sftp); -+ if (sftp_get_new_id(sftp, &id) != SSH_OK) { -+ ssh_set_error_oom(session); -+ goto error; -+ } - +- rc = ssh_buffer_pack(buffer, "dS", -@@ -1835,7 +1922,10 @@ sftp_file sftp_open(sftp_session sftp, + id, +@@ -1808,6 +1895,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); +@@ -1835,7 +1927,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); -+ if (sftp_get_new_id(sftp, &id) != SSH_OK) { -+ ssh_set_error_oom(session); -+ goto error; -+ } rc = ssh_buffer_pack(buffer, "dsd", -@@ -1946,7 +2036,10 @@ ssize_t sftp_read(sftp_file handle, void *buf, size_t count) { - return -1; +@@ -1940,14 +2031,17 @@ ssize_t sftp_read(sftp_file handle, void *buf, size_t count) { + return 0; } -- id = sftp_get_new_id(handle->sftp); -+ if (sftp_get_new_id(handle->sftp, &id) != SSH_OK) { -+ ssh_set_error_oom(handle->sftp->session); -+ return SSH_ERROR; ++ rc = sftp_get_new_id(handle->sftp, &id); ++ if (rc != SSH_OK) { ++ return -1; + } ++ + buffer = ssh_buffer_new(); + if (buffer == NULL) { + ssh_set_error_oom(sftp->session); + return -1; + } +- id = sftp_get_new_id(handle->sftp); +- rc = ssh_buffer_pack(buffer, "dSqd", -@@ -2047,7 +2140,11 @@ int sftp_async_read_begin(sftp_file file, uint32_t len){ + id, +@@ -2040,6 +2134,11 @@ int sftp_async_read_begin(sftp_file file, uint32_t len){ + 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); +@@ -2047,8 +2146,6 @@ int sftp_async_read_begin(sftp_file file, uint32_t len){ return -1; } - id = sftp_get_new_id(sftp); -+ if (sftp_get_new_id(sftp, &id) != SSH_OK) { -+ ssh_set_error_oom(sftp->session); -+ sftp_set_error(sftp, SSH_FX_FAILURE); -+ return -1; -+ } - +- rc = ssh_buffer_pack(buffer, "dSqd", -@@ -2173,7 +2270,10 @@ ssize_t sftp_write(sftp_file file, const void *buf, size_t count) { + id, +@@ -2166,6 +2263,11 @@ ssize_t sftp_write(sftp_file file, const void *buf, size_t count) { + size_t packetlen; + int rc; + ++ 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); +@@ -2173,8 +2275,6 @@ ssize_t sftp_write(sftp_file file, const void *buf, size_t count) { return -1; } - id = sftp_get_new_id(file->sftp); -+ if (sftp_get_new_id(file->sftp, &id) != SSH_OK) { -+ ssh_set_error_oom(file->sftp->session); -+ return SSH_ERROR; -+ } - +- rc = ssh_buffer_pack(buffer, "dSqdP", -@@ -2291,7 +2391,10 @@ int sftp_unlink(sftp_session sftp, const char *file) { + id, +@@ -2283,6 +2383,11 @@ int sftp_unlink(sftp_session sftp, const char *file) { + ssh_buffer buffer; + 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) { +@@ -2291,8 +2396,6 @@ int sftp_unlink(sftp_session sftp, const char *file) { return -1; } - id = sftp_get_new_id(sftp); -+ if (sftp_get_new_id(sftp, &id) != SSH_OK) { -+ ssh_set_error_oom(sftp->session); -+ goto error; -+ } - +- rc = ssh_buffer_pack(buffer, "ds", -@@ -2366,7 +2469,10 @@ int sftp_rmdir(sftp_session sftp, const char *directory) { + id, +@@ -2359,6 +2462,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); +@@ -2366,8 +2474,6 @@ int sftp_rmdir(sftp_session sftp, const char *directory) { return -1; } - id = sftp_get_new_id(sftp); -+ if (sftp_get_new_id(sftp, &id) != SSH_OK) { -+ ssh_set_error_oom(sftp->session); -+ goto error; -+ } - +- rc = ssh_buffer_pack(buffer, "ds", -@@ -2443,7 +2549,10 @@ int sftp_mkdir(sftp_session sftp, const char *directory, mode_t mode) + id, +@@ -2432,6 +2538,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); +@@ -2443,8 +2554,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); -+ if (sftp_get_new_id(sftp, &id) != SSH_OK) { -+ ssh_set_error_oom(session); -+ goto error; -+ } - +- rc = ssh_buffer_pack(buffer, "ds", -@@ -2538,7 +2647,10 @@ int sftp_rename(sftp_session sftp, const char *original, const char *newname) { + id, +@@ -2531,6 +2640,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); +@@ -2538,8 +2652,6 @@ int sftp_rename(sftp_session sftp, const char *original, const char *newname) { return -1; } - id = sftp_get_new_id(sftp); -+ if (sftp_get_new_id(sftp, &id) != SSH_OK) { -+ ssh_set_error_oom(sftp->session); -+ goto error; -+ } - +- rc = ssh_buffer_pack(buffer, "dss", -@@ -2622,7 +2734,10 @@ int sftp_setstat(sftp_session sftp, const char *file, sftp_attributes attr) + id, +@@ -2615,6 +2727,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); +@@ -2622,8 +2739,6 @@ int sftp_setstat(sftp_session sftp, const char *file, sftp_attributes attr) return -1; } - id = sftp_get_new_id(sftp); -+ if (sftp_get_new_id(sftp, &id) != SSH_OK) { -+ ssh_set_error_oom(session); -+ goto error; -+ } - +- rc = ssh_buffer_pack(buffer, "ds", -@@ -2752,7 +2867,10 @@ int sftp_symlink(sftp_session sftp, const char *target, const char *dest) { + id, +@@ -2744,6 +2859,11 @@ int sftp_symlink(sftp_session sftp, const char *target, const char *dest) { + sftp_set_error(sftp, SSH_FX_FAILURE); return -1; } - -- id = sftp_get_new_id(sftp); -+ if (sftp_get_new_id(sftp, &id) != SSH_OK) { -+ ssh_set_error_oom(sftp->session); -+ goto error; ++ ++ rc = sftp_get_new_id(sftp, &id); ++ if (rc != SSH_OK) { ++ return -1; + } + buffer = ssh_buffer_new(); + if (buffer == NULL) { +@@ -2752,8 +2872,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)) { -@@ -2850,7 +2968,10 @@ char *sftp_readlink(sftp_session sftp, const char *path) + rc = ssh_buffer_pack(buffer, +@@ -2843,6 +2961,12 @@ char *sftp_readlink(sftp_session sftp, const char *path) + sftp_set_error(sftp, SSH_FX_FAILURE); return NULL; } - -- id = sftp_get_new_id(sftp); -+ if (sftp_get_new_id(sftp, &id) != SSH_OK) { -+ ssh_set_error_oom(session); -+ goto error; ++ ++ 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); +@@ -2850,8 +2974,6 @@ char *sftp_readlink(sftp_session sftp, const char *path) + return NULL; + } +- id = sftp_get_new_id(sftp); +- rc = ssh_buffer_pack(buffer, "ds", -@@ -2976,7 +3097,10 @@ sftp_statvfs_t sftp_statvfs(sftp_session sftp, const char *path) + id, +@@ -2969,6 +3091,11 @@ sftp_statvfs_t sftp_statvfs(sftp_session sftp, const char *path) return NULL; } -- id = sftp_get_new_id(sftp); -+ if (sftp_get_new_id(sftp, &id) != SSH_OK) { -+ ssh_set_error_oom(session); -+ goto error; ++ 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); +@@ -2976,8 +3103,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", -@@ -3051,7 +3175,10 @@ int sftp_fsync(sftp_file file) - return -1; + id, +@@ -3044,6 +3169,11 @@ int sftp_fsync(sftp_file file) } + sftp = file->sftp; -- id = sftp_get_new_id(sftp); -+ if (sftp_get_new_id(sftp, &id) != SSH_OK) { -+ ssh_set_error_oom(session); -+ goto error; ++ 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); +@@ -3051,8 +3181,6 @@ int sftp_fsync(sftp_file file) + return -1; + } +- id = sftp_get_new_id(sftp); +- rc = ssh_buffer_pack(buffer, "dsS", -@@ -3151,7 +3278,10 @@ sftp_statvfs_t sftp_fstatvfs(sftp_file file) - return NULL; + id, +@@ -3144,6 +3272,11 @@ sftp_statvfs_t sftp_fstatvfs(sftp_file file) } + sftp = file->sftp; -- id = sftp_get_new_id(sftp); -+ if (sftp_get_new_id(sftp, &id) != SSH_OK) { -+ ssh_set_error_oom(session); -+ goto error; ++ 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); +@@ -3151,8 +3284,6 @@ sftp_statvfs_t sftp_fstatvfs(sftp_file file) + return NULL; + } +- id = sftp_get_new_id(sftp); +- rc = ssh_buffer_pack(buffer, "dsS", -@@ -3238,7 +3368,10 @@ char *sftp_canonicalize_path(sftp_session sftp, const char *path) + id, +@@ -3231,6 +3362,11 @@ char *sftp_canonicalize_path(sftp_session sftp, const char *path) return NULL; } -- id = sftp_get_new_id(sftp); -+ if (sftp_get_new_id(sftp, &id) != SSH_OK) { -+ ssh_set_error_oom(session); -+ goto error; ++ 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); +@@ -3238,8 +3374,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", -@@ -3329,7 +3462,10 @@ static sftp_attributes sftp_xstat(sftp_session sftp, + id, +@@ -3322,6 +3456,11 @@ static sftp_attributes sftp_xstat(sftp_session sftp, return NULL; } -- id = sftp_get_new_id(sftp); -+ if (sftp_get_new_id(sftp, &id) != SSH_OK) { -+ ssh_set_error_oom(session); -+ goto error; ++ 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); +@@ -3329,8 +3468,6 @@ static sftp_attributes sftp_xstat(sftp_session sftp, + return NULL; + } +- id = sftp_get_new_id(sftp); +- rc = ssh_buffer_pack(buffer, "ds", -@@ -3407,7 +3543,10 @@ sftp_attributes sftp_fstat(sftp_file file) + id, +@@ -3400,6 +3537,11 @@ sftp_attributes sftp_fstat(sftp_file file) return NULL; } -- id = sftp_get_new_id(file->sftp); -+ if (sftp_get_new_id(file->sftp, &id) != SSH_OK) { -+ ssh_set_error_oom(file->sftp->session); ++ 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); +@@ -3407,8 +3549,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 71e5182..864478a 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 0000000..5a7eab0 +--- /dev/null ++++ b/tests/client/torture_sftp_request_id.c +@@ -0,0 +1,182 @@ ++#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; ++ 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_read_and_dispatch(sftp); ++ assert_int_equal(rc, -1); ++} ++ ++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.45.4