From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from aws-us-west-2-korg-lkml-1.web.codeaurora.org (localhost.localdomain [127.0.0.1]) by smtp.lore.kernel.org (Postfix) with ESMTP id 21B27E98FAC for ; Thu, 9 Apr 2026 04:25:29 +0000 (UTC) Subject: Re: [meta-oe][whinlatter][PATCH 3/3] libssh: Fix CVE-2026-0965 To: openembedded-devel@lists.openembedded.org From: "Deepak Rathore" X-Originating-Location: Mumbai, Maharashtra, IN (151.186.177.21) X-Originating-Platform: Windows Edge 146 User-Agent: GROUPS.IO Web Poster MIME-Version: 1.0 Date: Wed, 08 Apr 2026 21:25:22 -0700 References: <20260406115535.1242965-1-deeratho@cisco.com> In-Reply-To: Message-ID: <829031.1775708722131603063@lists.openembedded.org> Content-Type: multipart/alternative; boundary="JNqEUsod6vV0DnxfjZfZ" List-Id: X-Webhook-Received: from 45-33-107-173.ip.linodeusercontent.com [45.33.107.173] by aws-us-west-2-korg-lkml-1.web.codeaurora.org with HTTPS for ; Thu, 09 Apr 2026 04:25:29 -0000 X-Groupsio-URL: https://lists.openembedded.org/g/openembedded-devel/message/126110 --JNqEUsod6vV0DnxfjZfZ Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Hi Ankur, Thank you for your review and response. As previously indicated by the maintainer in this thread ( openembedded-cor= e@lists.openembedded.org | Inquiry Regarding Package Upgrade Approach vs. M= anual CVE Fixes in LTS Releases ( https://lists.openembedded.org/g/openembe= dded-core/topic/118275693 ) ), package upgrades are not considered for stab= le and LTS branches for stability. Only bug fixes and security fixes are ac= cepted. Accordingly, I have submitted CVE fix patches instead of performing= a package upgrade. Regards, Deepak On Wed, Apr 8, 2026 at 10:49 AM, Ankur Tyagi wrote: >=20 > I think we can upgrade to v0.11.4 >=20 > https://git.libssh.org/projects/libssh.git/tree/CHANGELOG?h=3Dstable-0.11 >=20 > version 0.11.4 (released 2026-02-10) > * Security: > * CVE-2025-14821: libssh loads configuration files from the C:\etc > directory > on Windows > * CVE-2026-0964: SCP Protocol Path Traversal in ssh_scp_pull_request() > * CVE-2026-0965: Possible Denial of Service when parsing unexpected > configuration files > * CVE-2026-0966: Buffer underflow in ssh_get_hexa() on invalid input > * CVE-2026-0967: Specially crafted patterns could cause DoS > * CVE-2026-0968: OOB Read in sftp_parse_longname() > * libssh-2026-sftp-extensions: Read buffer overrun when handling SFTP > extensions > * Stability and compatibility improvements of ProxyJum >=20 >=20 >=20 > On Mon, Apr 6, 2026 at 11:56=E2=80=AFPM Deepak Rathore via > lists.openembedded.org > wrote: >=20 >>=20 >> From: Deepak Rathore >>=20 >> Pick the patch [1] as mentioned in [2] >>=20 >> [1] https://git.libssh.org/projects/libssh.git/commit/?id=3Dbf390a042623= e02abc8f421c4c5fadc0429a8a76 >>=20 >> [2] https://security-tracker.debian.org/tracker/CVE-2026-0965 >>=20 >> Signed-off-by: Deepak Rathore >> --- >> .../libssh/libssh/CVE-2026-0965.patch | 286 ++++++++++++++++++ >> .../recipes-support/libssh/libssh_0.11.3.bb | 1 + >> 2 files changed, 287 insertions(+) >> create mode 100644 >> meta-oe/recipes-support/libssh/libssh/CVE-2026-0965.patch >>=20 >> diff --git a/meta-oe/recipes-support/libssh/libssh/CVE-2026-0965.patch >> b/meta-oe/recipes-support/libssh/libssh/CVE-2026-0965.patch >> new file mode 100644 >> index 0000000000..57cb9d6170 >> --- /dev/null >> +++ b/meta-oe/recipes-support/libssh/libssh/CVE-2026-0965.patch >> @@ -0,0 +1,286 @@ >> +From 5858a988942d2e25985b34b8c40ce2792cbbe853 Mon Sep 17 00:00:00 2001 >> +From: Jakub Jelen >> +Date: Thu, 11 Dec 2025 17:33:19 +0100 >> +Subject: [PATCH 4/4] CVE-2026-0965 config: Do not attempt to read >> non-regular >> + and too large configuration files >> + >> +Changes also the reading of known_hosts to use the new helper function >> + >> +CVE: CVE-2026-0965 >> +Upstream-Status: Backport [ https://git.libssh.org/projects/libssh.git/= commit/?id=3Dbf390a042623e02abc8f421c4c5fadc0429a8a76 >> ] >> + >> +Signed-off-by: Jakub Jelen >> +Reviewed-by: Andreas Schneider >> +(cherry picked from commit a5eb30dbfd8f3526b2d04bd9f0a3803b665f5798) >> +(cherry picked from commit bf390a042623e02abc8f421c4c5fadc0429a8a76) >> +Signed-off-by: Deepak Rathore >> +--- >> + include/libssh/misc.h | 3 ++ >> + include/libssh/priv.h | 3 ++ >> + src/bind_config.c | 4 +- >> + src/config.c | 8 ++-- >> + src/dh-gex.c | 4 +- >> + src/known_hosts.c | 2 +- >> + src/knownhosts.c | 2 +- >> + src/misc.c | 74 ++++++++++++++++++++++++++++++++ >> + tests/unittests/torture_config.c | 20 +++++++++ >> + 9 files changed, 110 insertions(+), 10 deletions(-) >> + >> +diff --git a/include/libssh/misc.h b/include/libssh/misc.h >> +index ab726a0e..8eab94ee 100644 >> +--- a/include/libssh/misc.h >> ++++ b/include/libssh/misc.h >> +@@ -36,6 +36,7 @@ >> + #include >> + #include >> + #endif /* _WIN32 */ >> ++#include >> + >> + #ifdef __cplusplus >> + extern "C" { >> +@@ -136,6 +137,8 @@ int ssh_check_username_syntax(const char *username)= ; >> + void ssh_proxyjumps_free(struct ssh_list *proxy_jump_list); >> + bool ssh_libssh_proxy_jumps(void); >> + >> ++FILE *ssh_strict_fopen(const char *filename, size_t max_file_size); >> ++ >> + #ifdef __cplusplus >> + } >> + #endif >> +diff --git a/include/libssh/priv.h b/include/libssh/priv.h >> +index 35fd8506..62069970 100644 >> +--- a/include/libssh/priv.h >> ++++ b/include/libssh/priv.h >> +@@ -473,6 +473,9 @@ char *ssh_strerror(int err_num, char *buf, size_t >> buflen); >> + #define SSH_TTY_MODES_MAX_BUFSIZE (55 * 5 + 1) >> + int encode_current_tty_opts(unsigned char *buf, size_t buflen); >> + >> ++/** The default maximum file size for a configuration file */ >> ++#define SSH_MAX_CONFIG_FILE_SIZE 16 * 1024 * 1024 >> ++ >> + #ifdef __cplusplus >> + } >> + #endif >> +diff --git a/src/bind_config.c b/src/bind_config.c >> +index 9e4a7fd4..c12f1003 100644 >> +--- a/src/bind_config.c >> ++++ b/src/bind_config.c >> +@@ -212,7 +212,7 @@ local_parse_file(ssh_bind bind, >> + return; >> + } >> + >> +- f =3D fopen(filename, "r"); >> ++ f =3D ssh_strict_fopen(filename, SSH_MAX_CONFIG_FILE_SIZE); >> + if (f =3D=3D NULL) { >> + SSH_LOG(SSH_LOG_RARE, "Cannot find file %s to load", >> + filename); >> +@@ -636,7 +636,7 @@ int ssh_bind_config_parse_file(ssh_bind bind, const >> char *filename) >> + * option to be redefined later by another file. */ >> + uint8_t seen[BIND_CFG_MAX] =3D {0}; >> + >> +- f =3D fopen(filename, "r"); >> ++ f =3D ssh_strict_fopen(filename, SSH_MAX_CONFIG_FILE_SIZE); >> + if (f =3D=3D NULL) { >> + return 0; >> + } >> +diff --git a/src/config.c b/src/config.c >> +index b4171efd..1ffad537 100644 >> +--- a/src/config.c >> ++++ b/src/config.c >> +@@ -223,10 +223,9 @@ local_parse_file(ssh_session session, >> + return; >> + } >> + >> +- f =3D fopen(filename, "r"); >> ++ f =3D ssh_strict_fopen(filename, SSH_MAX_CONFIG_FILE_SIZE); >> + if (f =3D=3D NULL) { >> +- SSH_LOG(SSH_LOG_RARE, "Cannot find file %s to load", >> +- filename); >> ++ /* The underlying function logs the reasons */ >> + return; >> + } >> + >> +@@ -1466,8 +1465,9 @@ int ssh_config_parse_file(ssh_session session, >> const char *filename) >> + int parsing, rv; >> + bool global =3D 0; >> + >> +- f =3D fopen(filename, "r"); >> ++ f =3D ssh_strict_fopen(filename, SSH_MAX_CONFIG_FILE_SIZE); >> + if (f =3D=3D NULL) { >> ++ /* The underlying function logs the reasons */ >> + return 0; >> + } >> + >> +diff --git a/src/dh-gex.c b/src/dh-gex.c >> +index 46ba934e..428a5655 100644 >> +--- a/src/dh-gex.c >> ++++ b/src/dh-gex.c >> +@@ -519,9 +519,9 @@ static int ssh_retrieve_dhgroup(char *moduli_file, >> + } >> + >> + if (moduli_file !=3D NULL) >> +- moduli =3D fopen(moduli_file, "r"); >> ++ moduli =3D ssh_strict_fopen(moduli_file, SSH_MAX_CONFIG_FILE_SIZE); >> + else >> +- moduli =3D fopen(MODULI_FILE, "r"); >> ++ moduli =3D ssh_strict_fopen(MODULI_FILE, SSH_MAX_CONFIG_FILE_SIZE); >> + >> + if (moduli =3D=3D NULL) { >> + char err_msg[SSH_ERRNO_MSG_MAX] =3D {0}; >> +diff --git a/src/known_hosts.c b/src/known_hosts.c >> +index 3ef83e21..701576ce 100644 >> +--- a/src/known_hosts.c >> ++++ b/src/known_hosts.c >> +@@ -83,7 +83,7 @@ static struct ssh_tokens_st >> *ssh_get_knownhost_line(FILE **file, >> + struct ssh_tokens_st *tokens =3D NULL; >> + >> + if (*file =3D=3D NULL) { >> +- *file =3D fopen(filename,"r"); >> ++ *file =3D ssh_strict_fopen(filename, SSH_MAX_CONFIG_FILE_SIZE); >> + if (*file =3D=3D NULL) { >> + return NULL; >> + } >> +diff --git a/src/knownhosts.c b/src/knownhosts.c >> +index a2d08a75..3ab468de 100644 >> +--- a/src/knownhosts.c >> ++++ b/src/knownhosts.c >> +@@ -232,7 +232,7 @@ static int ssh_known_hosts_read_entries(const char >> *match, >> + FILE *fp =3D NULL; >> + int rc; >> + >> +- fp =3D fopen(filename, "r"); >> ++ fp =3D ssh_strict_fopen(filename, SSH_MAX_CONFIG_FILE_SIZE); >> + if (fp =3D=3D NULL) { >> + char err_msg[SSH_ERRNO_MSG_MAX] =3D {0}; >> + SSH_LOG(SSH_LOG_TRACE, "Failed to open the known_hosts file '%s': %s", >> +diff --git a/src/misc.c b/src/misc.c >> +index 774211fb..3968e6bc 100644 >> +--- a/src/misc.c >> ++++ b/src/misc.c >> +@@ -37,6 +37,7 @@ >> + #endif /* _WIN32 */ >> + >> + #include >> ++#include >> + #include >> + #include >> + #include >> +@@ -2244,4 +2245,77 @@ ssh_libssh_proxy_jumps(void) >> + return !(t !=3D NULL && t[0] =3D=3D '1'); >> + } >> + >> ++/** >> ++ * @internal >> ++ * >> ++ * @brief Safely open a file containing some configuration. >> ++ * >> ++ * Runs checks if the file can be used as some configuration file (is >> regular >> ++ * file and is not too large). If so, returns the opened file (for >> reading). >> ++ * Otherwise logs error and returns `NULL`. >> ++ * >> ++ * @param filename The path to the file to open. >> ++ * @param max_file_size Maximum file size that is accepted. >> ++ * >> ++ * @returns the opened file or `NULL` on error. >> ++ */ >> ++FILE *ssh_strict_fopen(const char *filename, size_t max_file_size) >> ++{ >> ++ FILE *f =3D NULL; >> ++ struct stat sb; >> ++ char err_msg[SSH_ERRNO_MSG_MAX] =3D {0}; >> ++ int r, fd; >> ++ >> ++ /* open first to avoid TOCTOU */ >> ++ fd =3D open(filename, O_RDONLY); >> ++ if (fd =3D=3D -1) { >> ++ SSH_LOG(SSH_LOG_RARE, >> ++ "Failed to open a file %s for reading: %s", >> ++ filename, >> ++ ssh_strerror(errno, err_msg, SSH_ERRNO_MSG_MAX)); >> ++ return NULL; >> ++ } >> ++ >> ++ /* Check the file is sensible for a configuration file */ >> ++ r =3D fstat(fd, &sb); >> ++ if (r !=3D 0) { >> ++ SSH_LOG(SSH_LOG_RARE, >> ++ "Failed to stat %s: %s", >> ++ filename, >> ++ ssh_strerror(errno, err_msg, SSH_ERRNO_MSG_MAX)); >> ++ close(fd); >> ++ return NULL; >> ++ } >> ++ if ((sb.st_mode & S_IFMT) !=3D S_IFREG) { >> ++ SSH_LOG(SSH_LOG_RARE, >> ++ "The file %s is not a regular file: skipping", >> ++ filename); >> ++ close(fd); >> ++ return NULL; >> ++ } >> ++ >> ++ if ((size_t)sb.st_size > max_file_size) { >> ++ SSH_LOG(SSH_LOG_RARE, >> ++ "The file %s is too large (%jd MB > %zu MB): skipping", >> ++ filename, >> ++ (intmax_t)sb.st_size / 1024 / 1024, >> ++ max_file_size / 1024 / 1024); >> ++ close(fd); >> ++ return NULL; >> ++ } >> ++ >> ++ f =3D fdopen(fd, "r"); >> ++ if (f =3D=3D NULL) { >> ++ SSH_LOG(SSH_LOG_RARE, >> ++ "Failed to open a file %s for reading: %s", >> ++ filename, >> ++ ssh_strerror(r, err_msg, SSH_ERRNO_MSG_MAX)); >> ++ close(fd); >> ++ return NULL; >> ++ } >> ++ >> ++ /* the flcose() will close also the underlying fd */ >> ++ return f; >> ++} >> ++ >> + /** @} */ >> +diff --git a/tests/unittests/torture_config.c >> b/tests/unittests/torture_config.c >> +index fcfe8fbc..0cb31a76 100644 >> +--- a/tests/unittests/torture_config.c >> ++++ b/tests/unittests/torture_config.c >> +@@ -2675,6 +2675,23 @@ static void torture_config_match_complex(void >> **state) >> + ssh_string_free_char(v); >> + } >> + >> ++/* Invalid configuration files >> ++ */ >> ++static void torture_config_invalid(void **state) >> ++{ >> ++ ssh_session session =3D *state; >> ++ >> ++ ssh_options_set(session, SSH_OPTIONS_HOST, "Bar"); >> ++ >> ++ /* non-regular file -- ignored (or missing on non-unix) so OK */ >> ++ _parse_config(session, "/dev/random", NULL, SSH_OK); >> ++ >> ++#ifndef _WIN32 >> ++ /* huge file -- ignored (or missing on non-unix) so OK */ >> ++ _parse_config(session, "/proc/kcore", NULL, SSH_OK); >> ++#endif >> ++} >> ++ >> + int torture_run_tests(void) >> + { >> + int rc; >> +@@ -2771,6 +2788,9 @@ int torture_run_tests(void) >> + setup, teardown), >> + cmocka_unit_test_setup_teardown(torture_config_match_complex, >> + setup, teardown), >> ++ cmocka_unit_test_setup_teardown(torture_config_invalid, >> ++ setup, >> ++ teardown), >> + }; >> + >> + >> +-- >> +2.51.0 >> + >> diff --git a/meta-oe/recipes-support/libssh/libssh_0.11.3.bb >> b/meta-oe/recipes-support/libssh/libssh_0.11.3.bb >> index 193ff3512d..c552692bde 100644 >> --- a/meta-oe/recipes-support/libssh/libssh_0.11.3.bb >> +++ b/meta-oe/recipes-support/libssh/libssh_0.11.3.bb >> @@ -14,6 +14,7 @@ SRC_URI =3D >> "git://git.libssh.org/projects/libssh.git;protocol=3Dhttps;branch=3Dstab= le >> file://CVE-2026-0968_p1.patch \ >> file://CVE-2026-0968_p2.patch \ >> file://CVE-2026-0967.patch \ >> + file://CVE-2026-0965.patch \ >> " >>=20 >> SRC_URI:append:toolchain-clang =3D " >> file://0001-CompilerChecks.cmake-drop-Wunused-variable-flag.patch" >> -- >> 2.35.6 >>=20 >>=20 >>=20 >>=20 >=20 >=20 On Wed, Apr 8, 2026 at 10:49 AM, Ankur Tyagi wrote: >=20 > I think we can upgrade to v0.11.4 >=20 > https://git.libssh.org/projects/libssh.git/tree/CHANGELOG?h=3Dstable-0.11 >=20 > version 0.11.4 (released 2026-02-10) > * Security: > * CVE-2025-14821: libssh loads configuration files from the C:\etc > directory > on Windows > * CVE-2026-0964: SCP Protocol Path Traversal in ssh_scp_pull_request() > * CVE-2026-0965: Possible Denial of Service when parsing unexpected > configuration files > * CVE-2026-0966: Buffer underflow in ssh_get_hexa() on invalid input > * CVE-2026-0967: Specially crafted patterns could cause DoS > * CVE-2026-0968: OOB Read in sftp_parse_longname() > * libssh-2026-sftp-extensions: Read buffer overrun when handling SFTP > extensions > * Stability and compatibility improvements of ProxyJum >=20 >=20 >=20 > On Mon, Apr 6, 2026 at 11:56=E2=80=AFPM Deepak Rathore via > lists.openembedded.org > wrote: >=20 >>=20 >> From: Deepak Rathore >>=20 >> Pick the patch [1] as mentioned in [2] >>=20 >> [1] https://git.libssh.org/projects/libssh.git/commit/?id=3Dbf390a042623= e02abc8f421c4c5fadc0429a8a76 >>=20 >> [2] https://security-tracker.debian.org/tracker/CVE-2026-0965 >>=20 >> Signed-off-by: Deepak Rathore >> --- >> .../libssh/libssh/CVE-2026-0965.patch | 286 ++++++++++++++++++ >> .../recipes-support/libssh/libssh_0.11.3.bb | 1 + >> 2 files changed, 287 insertions(+) >> create mode 100644 >> meta-oe/recipes-support/libssh/libssh/CVE-2026-0965.patch >>=20 >> diff --git a/meta-oe/recipes-support/libssh/libssh/CVE-2026-0965.patch >> b/meta-oe/recipes-support/libssh/libssh/CVE-2026-0965.patch >> new file mode 100644 >> index 0000000000..57cb9d6170 >> --- /dev/null >> +++ b/meta-oe/recipes-support/libssh/libssh/CVE-2026-0965.patch >> @@ -0,0 +1,286 @@ >> +From 5858a988942d2e25985b34b8c40ce2792cbbe853 Mon Sep 17 00:00:00 2001 >> +From: Jakub Jelen >> +Date: Thu, 11 Dec 2025 17:33:19 +0100 >> +Subject: [PATCH 4/4] CVE-2026-0965 config: Do not attempt to read >> non-regular >> + and too large configuration files >> + >> +Changes also the reading of known_hosts to use the new helper function >> + >> +CVE: CVE-2026-0965 >> +Upstream-Status: Backport [ https://git.libssh.org/projects/libssh.git/= commit/?id=3Dbf390a042623e02abc8f421c4c5fadc0429a8a76 >> ] >> + >> +Signed-off-by: Jakub Jelen >> +Reviewed-by: Andreas Schneider >> +(cherry picked from commit a5eb30dbfd8f3526b2d04bd9f0a3803b665f5798) >> +(cherry picked from commit bf390a042623e02abc8f421c4c5fadc0429a8a76) >> +Signed-off-by: Deepak Rathore >> +--- >> + include/libssh/misc.h | 3 ++ >> + include/libssh/priv.h | 3 ++ >> + src/bind_config.c | 4 +- >> + src/config.c | 8 ++-- >> + src/dh-gex.c | 4 +- >> + src/known_hosts.c | 2 +- >> + src/knownhosts.c | 2 +- >> + src/misc.c | 74 ++++++++++++++++++++++++++++++++ >> + tests/unittests/torture_config.c | 20 +++++++++ >> + 9 files changed, 110 insertions(+), 10 deletions(-) >> + >> +diff --git a/include/libssh/misc.h b/include/libssh/misc.h >> +index ab726a0e..8eab94ee 100644 >> +--- a/include/libssh/misc.h >> ++++ b/include/libssh/misc.h >> +@@ -36,6 +36,7 @@ >> + #include >> + #include >> + #endif /* _WIN32 */ >> ++#include >> + >> + #ifdef __cplusplus >> + extern "C" { >> +@@ -136,6 +137,8 @@ int ssh_check_username_syntax(const char *username)= ; >> + void ssh_proxyjumps_free(struct ssh_list *proxy_jump_list); >> + bool ssh_libssh_proxy_jumps(void); >> + >> ++FILE *ssh_strict_fopen(const char *filename, size_t max_file_size); >> ++ >> + #ifdef __cplusplus >> + } >> + #endif >> +diff --git a/include/libssh/priv.h b/include/libssh/priv.h >> +index 35fd8506..62069970 100644 >> +--- a/include/libssh/priv.h >> ++++ b/include/libssh/priv.h >> +@@ -473,6 +473,9 @@ char *ssh_strerror(int err_num, char *buf, size_t >> buflen); >> + #define SSH_TTY_MODES_MAX_BUFSIZE (55 * 5 + 1) >> + int encode_current_tty_opts(unsigned char *buf, size_t buflen); >> + >> ++/** The default maximum file size for a configuration file */ >> ++#define SSH_MAX_CONFIG_FILE_SIZE 16 * 1024 * 1024 >> ++ >> + #ifdef __cplusplus >> + } >> + #endif >> +diff --git a/src/bind_config.c b/src/bind_config.c >> +index 9e4a7fd4..c12f1003 100644 >> +--- a/src/bind_config.c >> ++++ b/src/bind_config.c >> +@@ -212,7 +212,7 @@ local_parse_file(ssh_bind bind, >> + return; >> + } >> + >> +- f =3D fopen(filename, "r"); >> ++ f =3D ssh_strict_fopen(filename, SSH_MAX_CONFIG_FILE_SIZE); >> + if (f =3D=3D NULL) { >> + SSH_LOG(SSH_LOG_RARE, "Cannot find file %s to load", >> + filename); >> +@@ -636,7 +636,7 @@ int ssh_bind_config_parse_file(ssh_bind bind, const >> char *filename) >> + * option to be redefined later by another file. */ >> + uint8_t seen[BIND_CFG_MAX] =3D {0}; >> + >> +- f =3D fopen(filename, "r"); >> ++ f =3D ssh_strict_fopen(filename, SSH_MAX_CONFIG_FILE_SIZE); >> + if (f =3D=3D NULL) { >> + return 0; >> + } >> +diff --git a/src/config.c b/src/config.c >> +index b4171efd..1ffad537 100644 >> +--- a/src/config.c >> ++++ b/src/config.c >> +@@ -223,10 +223,9 @@ local_parse_file(ssh_session session, >> + return; >> + } >> + >> +- f =3D fopen(filename, "r"); >> ++ f =3D ssh_strict_fopen(filename, SSH_MAX_CONFIG_FILE_SIZE); >> + if (f =3D=3D NULL) { >> +- SSH_LOG(SSH_LOG_RARE, "Cannot find file %s to load", >> +- filename); >> ++ /* The underlying function logs the reasons */ >> + return; >> + } >> + >> +@@ -1466,8 +1465,9 @@ int ssh_config_parse_file(ssh_session session, >> const char *filename) >> + int parsing, rv; >> + bool global =3D 0; >> + >> +- f =3D fopen(filename, "r"); >> ++ f =3D ssh_strict_fopen(filename, SSH_MAX_CONFIG_FILE_SIZE); >> + if (f =3D=3D NULL) { >> ++ /* The underlying function logs the reasons */ >> + return 0; >> + } >> + >> +diff --git a/src/dh-gex.c b/src/dh-gex.c >> +index 46ba934e..428a5655 100644 >> +--- a/src/dh-gex.c >> ++++ b/src/dh-gex.c >> +@@ -519,9 +519,9 @@ static int ssh_retrieve_dhgroup(char *moduli_file, >> + } >> + >> + if (moduli_file !=3D NULL) >> +- moduli =3D fopen(moduli_file, "r"); >> ++ moduli =3D ssh_strict_fopen(moduli_file, SSH_MAX_CONFIG_FILE_SIZE); >> + else >> +- moduli =3D fopen(MODULI_FILE, "r"); >> ++ moduli =3D ssh_strict_fopen(MODULI_FILE, SSH_MAX_CONFIG_FILE_SIZE); >> + >> + if (moduli =3D=3D NULL) { >> + char err_msg[SSH_ERRNO_MSG_MAX] =3D {0}; >> +diff --git a/src/known_hosts.c b/src/known_hosts.c >> +index 3ef83e21..701576ce 100644 >> +--- a/src/known_hosts.c >> ++++ b/src/known_hosts.c >> +@@ -83,7 +83,7 @@ static struct ssh_tokens_st >> *ssh_get_knownhost_line(FILE **file, >> + struct ssh_tokens_st *tokens =3D NULL; >> + >> + if (*file =3D=3D NULL) { >> +- *file =3D fopen(filename,"r"); >> ++ *file =3D ssh_strict_fopen(filename, SSH_MAX_CONFIG_FILE_SIZE); >> + if (*file =3D=3D NULL) { >> + return NULL; >> + } >> +diff --git a/src/knownhosts.c b/src/knownhosts.c >> +index a2d08a75..3ab468de 100644 >> +--- a/src/knownhosts.c >> ++++ b/src/knownhosts.c >> +@@ -232,7 +232,7 @@ static int ssh_known_hosts_read_entries(const char >> *match, >> + FILE *fp =3D NULL; >> + int rc; >> + >> +- fp =3D fopen(filename, "r"); >> ++ fp =3D ssh_strict_fopen(filename, SSH_MAX_CONFIG_FILE_SIZE); >> + if (fp =3D=3D NULL) { >> + char err_msg[SSH_ERRNO_MSG_MAX] =3D {0}; >> + SSH_LOG(SSH_LOG_TRACE, "Failed to open the known_hosts file '%s': %s", >> +diff --git a/src/misc.c b/src/misc.c >> +index 774211fb..3968e6bc 100644 >> +--- a/src/misc.c >> ++++ b/src/misc.c >> +@@ -37,6 +37,7 @@ >> + #endif /* _WIN32 */ >> + >> + #include >> ++#include >> + #include >> + #include >> + #include >> +@@ -2244,4 +2245,77 @@ ssh_libssh_proxy_jumps(void) >> + return !(t !=3D NULL && t[0] =3D=3D '1'); >> + } >> + >> ++/** >> ++ * @internal >> ++ * >> ++ * @brief Safely open a file containing some configuration. >> ++ * >> ++ * Runs checks if the file can be used as some configuration file (is >> regular >> ++ * file and is not too large). If so, returns the opened file (for >> reading). >> ++ * Otherwise logs error and returns `NULL`. >> ++ * >> ++ * @param filename The path to the file to open. >> ++ * @param max_file_size Maximum file size that is accepted. >> ++ * >> ++ * @returns the opened file or `NULL` on error. >> ++ */ >> ++FILE *ssh_strict_fopen(const char *filename, size_t max_file_size) >> ++{ >> ++ FILE *f =3D NULL; >> ++ struct stat sb; >> ++ char err_msg[SSH_ERRNO_MSG_MAX] =3D {0}; >> ++ int r, fd; >> ++ >> ++ /* open first to avoid TOCTOU */ >> ++ fd =3D open(filename, O_RDONLY); >> ++ if (fd =3D=3D -1) { >> ++ SSH_LOG(SSH_LOG_RARE, >> ++ "Failed to open a file %s for reading: %s", >> ++ filename, >> ++ ssh_strerror(errno, err_msg, SSH_ERRNO_MSG_MAX)); >> ++ return NULL; >> ++ } >> ++ >> ++ /* Check the file is sensible for a configuration file */ >> ++ r =3D fstat(fd, &sb); >> ++ if (r !=3D 0) { >> ++ SSH_LOG(SSH_LOG_RARE, >> ++ "Failed to stat %s: %s", >> ++ filename, >> ++ ssh_strerror(errno, err_msg, SSH_ERRNO_MSG_MAX)); >> ++ close(fd); >> ++ return NULL; >> ++ } >> ++ if ((sb.st_mode & S_IFMT) !=3D S_IFREG) { >> ++ SSH_LOG(SSH_LOG_RARE, >> ++ "The file %s is not a regular file: skipping", >> ++ filename); >> ++ close(fd); >> ++ return NULL; >> ++ } >> ++ >> ++ if ((size_t)sb.st_size > max_file_size) { >> ++ SSH_LOG(SSH_LOG_RARE, >> ++ "The file %s is too large (%jd MB > %zu MB): skipping", >> ++ filename, >> ++ (intmax_t)sb.st_size / 1024 / 1024, >> ++ max_file_size / 1024 / 1024); >> ++ close(fd); >> ++ return NULL; >> ++ } >> ++ >> ++ f =3D fdopen(fd, "r"); >> ++ if (f =3D=3D NULL) { >> ++ SSH_LOG(SSH_LOG_RARE, >> ++ "Failed to open a file %s for reading: %s", >> ++ filename, >> ++ ssh_strerror(r, err_msg, SSH_ERRNO_MSG_MAX)); >> ++ close(fd); >> ++ return NULL; >> ++ } >> ++ >> ++ /* the flcose() will close also the underlying fd */ >> ++ return f; >> ++} >> ++ >> + /** @} */ >> +diff --git a/tests/unittests/torture_config.c >> b/tests/unittests/torture_config.c >> +index fcfe8fbc..0cb31a76 100644 >> +--- a/tests/unittests/torture_config.c >> ++++ b/tests/unittests/torture_config.c >> +@@ -2675,6 +2675,23 @@ static void torture_config_match_complex(void >> **state) >> + ssh_string_free_char(v); >> + } >> + >> ++/* Invalid configuration files >> ++ */ >> ++static void torture_config_invalid(void **state) >> ++{ >> ++ ssh_session session =3D *state; >> ++ >> ++ ssh_options_set(session, SSH_OPTIONS_HOST, "Bar"); >> ++ >> ++ /* non-regular file -- ignored (or missing on non-unix) so OK */ >> ++ _parse_config(session, "/dev/random", NULL, SSH_OK); >> ++ >> ++#ifndef _WIN32 >> ++ /* huge file -- ignored (or missing on non-unix) so OK */ >> ++ _parse_config(session, "/proc/kcore", NULL, SSH_OK); >> ++#endif >> ++} >> ++ >> + int torture_run_tests(void) >> + { >> + int rc; >> +@@ -2771,6 +2788,9 @@ int torture_run_tests(void) >> + setup, teardown), >> + cmocka_unit_test_setup_teardown(torture_config_match_complex, >> + setup, teardown), >> ++ cmocka_unit_test_setup_teardown(torture_config_invalid, >> ++ setup, >> ++ teardown), >> + }; >> + >> + >> +-- >> +2.51.0 >> + >> diff --git a/meta-oe/recipes-support/libssh/libssh_0.11.3.bb >> b/meta-oe/recipes-support/libssh/libssh_0.11.3.bb >> index 193ff3512d..c552692bde 100644 >> --- a/meta-oe/recipes-support/libssh/libssh_0.11.3.bb >> +++ b/meta-oe/recipes-support/libssh/libssh_0.11.3.bb >> @@ -14,6 +14,7 @@ SRC_URI =3D >> "git://git.libssh.org/projects/libssh.git;protocol=3Dhttps;branch=3Dstab= le >> file://CVE-2026-0968_p1.patch \ >> file://CVE-2026-0968_p2.patch \ >> file://CVE-2026-0967.patch \ >> + file://CVE-2026-0965.patch \ >> " >>=20 >> SRC_URI:append:toolchain-clang =3D " >> file://0001-CompilerChecks.cmake-drop-Wunused-variable-flag.patch" >> -- >> 2.35.6 >>=20 >>=20 >>=20 >>=20 >=20 > --JNqEUsod6vV0DnxfjZfZ Content-Type: text/html; charset="utf-8" Content-Transfer-Encoding: quoted-printable

Hi Ankur,

Thank you for your review and response.

As previously indicated by the maintainer in this thread (openembedd= ed-core@lists.openembedded.org | Inquiry Regarding Package Upgrade Approach= vs. Manual CVE Fixes in LTS Releases), package upgrades are not consid= ered for stable and LTS branches for stability. Only bug fixes and security= fixes are accepted. Accordingly, I have submitted CVE fix patches instead = of performing a package upgrade.

Regards,
Deepak

 
 
On Wed, Apr 8, 2026 at 10:49 AM, Ankur Tyagi wrote:
I think we can upgrade to v0.11.4

https://git.libssh.org/projects/libssh.git/tre= e/CHANGELOG?h=3Dstable-0.11

version 0.11.4 (released 2026-02= -10)
* Security:
* CVE-2025-14821: libssh loads configuration fil= es from the C:\etc directory
on Windows
* CVE-2026-0964: SCP Prot= ocol Path Traversal in ssh_scp_pull_request()
* CVE-2026-0965: Possibl= e Denial of Service when parsing unexpected
configuration files
*= CVE-2026-0966: Buffer underflow in ssh_get_hexa() on invalid input
* = CVE-2026-0967: Specially crafted patterns could cause DoS
* CVE-2026-0= 968: OOB Read in sftp_parse_longname()
* libssh-2026-sftp-extensions: = Read buffer overrun when handling SFTP
extensions
* Stability and= compatibility improvements of ProxyJum



On Mon, Apr = 6, 2026 at 11:56=E2=80=AFPM Deepak Rathore via
lists.openembedded.org = <deeratho=3Dcisco.com@lists.openembedded.org>
wrote:

From: Deepak Rathore <deeratho@cisco.com>

Pick the patch [1] as mentioned in [2]

[1] https://git.libssh.org/pr= ojects/libssh.git/commit/?id=3Dbf390a042623e02abc8f421c4c5fadc0429a8a76=
[2] https://security-tracker.debian.org= /tracker/CVE-2026-0965

Signed-off-by: Deepak Rathore <dee= ratho@cisco.com>
---
.../libssh/libssh/CVE-2026-0965.patch | 2= 86 ++++++++++++++++++
.../recipes-support/libssh/libssh_0.11.3.bb | 1 = +
2 files changed, 287 insertions(+)
create mode 100644 meta-oe/r= ecipes-support/libssh/libssh/CVE-2026-0965.patch

diff --git a/me= ta-oe/recipes-support/libssh/libssh/CVE-2026-0965.patch b/meta-oe/recipes-s= upport/libssh/libssh/CVE-2026-0965.patch
new file mode 100644
ind= ex 0000000000..57cb9d6170
--- /dev/null
+++ b/meta-oe/recipes-sup= port/libssh/libssh/CVE-2026-0965.patch
@@ -0,0 +1,286 @@
+From 58= 58a988942d2e25985b34b8c40ce2792cbbe853 Mon Sep 17 00:00:00 2001
+From:= Jakub Jelen <jjelen@redhat.com>
+Date: Thu, 11 Dec 2025 17:33:1= 9 +0100
+Subject: [PATCH 4/4] CVE-2026-0965 config: Do not attempt to = read non-regular
+ and too large configuration files
+
+Chan= ges also the reading of known_hosts to use the new helper function
++CVE: CVE-2026-0965
+Upstream-Status: Backport [https://git.libssh.org/p= rojects/libssh.git/commit/?id=3Dbf390a042623e02abc8f421c4c5fadc0429a8a76]
+
+Signed-off-by: Jakub Jelen <jjelen@redhat.com>
+= Reviewed-by: Andreas Schneider <asn@cryptomilk.org>
+(cherry pic= ked from commit a5eb30dbfd8f3526b2d04bd9f0a3803b665f5798)
+(cherry pic= ked from commit bf390a042623e02abc8f421c4c5fadc0429a8a76)
+Signed-off-= by: Deepak Rathore <deeratho@cisco.com>
+---
+ include/libs= sh/misc.h | 3 ++
+ include/libssh/priv.h | 3 ++
+ src/bind_config= .c | 4 +-
+ src/config.c | 8 ++--
+ src/dh-gex.c | 4 +-
+ sr= c/known_hosts.c | 2 +-
+ src/knownhosts.c | 2 +-
+ src/misc.c | 7= 4 ++++++++++++++++++++++++++++++++
+ tests/unittests/torture_config.c = | 20 +++++++++
+ 9 files changed, 110 insertions(+), 10 deletions(-)+
+diff --git a/include/libssh/misc.h b/include/libssh/misc.h
+index ab726a0e..8eab94ee 100644
+--- a/include/libssh/misc.h
++= ++ b/include/libssh/misc.h
+@@ -36,6 +36,7 @@
+ #include <sys/= types.h>
+ #include <stdbool.h>
+ #endif /* _WIN32 */++#include <stdio.h>
+
+ #ifdef __cplusplus
+ exter= n "C" {
+@@ -136,6 +137,8 @@ int ssh_check_username_syntax(const char = *username);
+ void ssh_proxyjumps_free(struct ssh_list *proxy_jump_lis= t);
+ bool ssh_libssh_proxy_jumps(void);
+
++FILE *ssh_stric= t_fopen(const char *filename, size_t max_file_size);
++
+ #ifdef = __cplusplus
+ }
+ #endif
+diff --git a/include/libssh/priv.h= b/include/libssh/priv.h
+index 35fd8506..62069970 100644
+--- a/= include/libssh/priv.h
++++ b/include/libssh/priv.h
+@@ -473,6 +47= 3,9 @@ char *ssh_strerror(int err_num, char *buf, size_t buflen);
+ #d= efine SSH_TTY_MODES_MAX_BUFSIZE (55 * 5 + 1)
+ int encode_current_tty_= opts(unsigned char *buf, size_t buflen);
+
++/** The default maxi= mum file size for a configuration file */
++#define SSH_MAX_CONFIG_FIL= E_SIZE 16 * 1024 * 1024
++
+ #ifdef __cplusplus
+ }
+ #= endif
+diff --git a/src/bind_config.c b/src/bind_config.c
+index = 9e4a7fd4..c12f1003 100644
+--- a/src/bind_config.c
++++ b/src/bin= d_config.c
+@@ -212,7 +212,7 @@ local_parse_file(ssh_bind bind,
+= return;
+ }
+
+- f =3D fopen(filename, "r");
++ f =3D = ssh_strict_fopen(filename, SSH_MAX_CONFIG_FILE_SIZE);
+ if (f =3D=3D N= ULL) {
+ SSH_LOG(SSH_LOG_RARE, "Cannot find file %s to load",
+ f= ilename);
+@@ -636,7 +636,7 @@ int ssh_bind_config_parse_file(ssh_bind= bind, const char *filename)
+ * option to be redefined later by anoth= er file. */
+ uint8_t seen[BIND_CFG_MAX] =3D {0};
+
+- f =3D= fopen(filename, "r");
++ f =3D ssh_strict_fopen(filename, SSH_MAX_CON= FIG_FILE_SIZE);
+ if (f =3D=3D NULL) {
+ return 0;
+ }
= +diff --git a/src/config.c b/src/config.c
+index b4171efd..1ffad537 10= 0644
+--- a/src/config.c
++++ b/src/config.c
+@@ -223,10 +22= 3,9 @@ local_parse_file(ssh_session session,
+ return;
+ }
+=
+- f =3D fopen(filename, "r");
++ f =3D ssh_strict_fopen(filenam= e, SSH_MAX_CONFIG_FILE_SIZE);
+ if (f =3D=3D NULL) {
+- SSH_LOG(S= SH_LOG_RARE, "Cannot find file %s to load",
+- filename);
++ /* T= he underlying function logs the reasons */
+ return;
+ }
++@@ -1466,8 +1465,9 @@ int ssh_config_parse_file(ssh_session session, c= onst char *filename)
+ int parsing, rv;
+ bool global =3D 0;
+
+- f =3D fopen(filename, "r");
++ f =3D ssh_strict_fopen(filen= ame, SSH_MAX_CONFIG_FILE_SIZE);
+ if (f =3D=3D NULL) {
++ /* The = underlying function logs the reasons */
+ return 0;
+ }
++diff --git a/src/dh-gex.c b/src/dh-gex.c
+index 46ba934e..428a5655= 100644
+--- a/src/dh-gex.c
++++ b/src/dh-gex.c
+@@ -519,9 += 519,9 @@ static int ssh_retrieve_dhgroup(char *moduli_file,
+ }
+=
+ if (moduli_file !=3D NULL)
+- moduli =3D fopen(moduli_file, "r= ");
++ moduli =3D ssh_strict_fopen(moduli_file, SSH_MAX_CONFIG_FILE_SI= ZE);
+ else
+- moduli =3D fopen(MODULI_FILE, "r");
++ moduli= =3D ssh_strict_fopen(MODULI_FILE, SSH_MAX_CONFIG_FILE_SIZE);
+
+= if (moduli =3D=3D NULL) {
+ char err_msg[SSH_ERRNO_MSG_MAX] =3D {0};<= br />+diff --git a/src/known_hosts.c b/src/known_hosts.c
+index 3ef83e= 21..701576ce 100644
+--- a/src/known_hosts.c
++++ b/src/known_hos= ts.c
+@@ -83,7 +83,7 @@ static struct ssh_tokens_st *ssh_get_knownhost= _line(FILE **file,
+ struct ssh_tokens_st *tokens =3D NULL;
+
+ if (*file =3D=3D NULL) {
+- *file =3D fopen(filename,"r");
++= *file =3D ssh_strict_fopen(filename, SSH_MAX_CONFIG_FILE_SIZE);
+ if = (*file =3D=3D NULL) {
+ return NULL;
+ }
+diff --git a/src/k= nownhosts.c b/src/knownhosts.c
+index a2d08a75..3ab468de 100644
+= --- a/src/knownhosts.c
++++ b/src/knownhosts.c
+@@ -232,7 +232,7 = @@ static int ssh_known_hosts_read_entries(const char *match,
+ FILE *= fp =3D NULL;
+ int rc;
+
+- fp =3D fopen(filename, "r");
++ fp =3D ssh_strict_fopen(filename, SSH_MAX_CONFIG_FILE_SIZE);
+ if= (fp =3D=3D NULL) {
+ char err_msg[SSH_ERRNO_MSG_MAX] =3D {0};
+ = SSH_LOG(SSH_LOG_TRACE, "Failed to open the known_hosts file '%s': %s",
+diff --git a/src/misc.c b/src/misc.c
+index 774211fb..3968e6bc 10064= 4
+--- a/src/misc.c
++++ b/src/misc.c
+@@ -37,6 +37,7 @@
+ #endif /* _WIN32 */
+
+ #include <errno.h>
++#incl= ude <fcntl.h>
+ #include <limits.h>
+ #include <st= dio.h>
+ #include <string.h>
+@@ -2244,4 +2245,77 @@ ssh= _libssh_proxy_jumps(void)
+ return !(t !=3D NULL && t[0] =3D= =3D '1');
+ }
+
++/**
++ * @internal
++ *
++ = * @brief Safely open a file containing some configuration.
++ *
+= + * Runs checks if the file can be used as some configuration file (is regu= lar
++ * file and is not too large). If so, returns the opened file (f= or reading).
++ * Otherwise logs error and returns `NULL`.
++ *++ * @param filename The path to the file to open.
++ * @param max= _file_size Maximum file size that is accepted.
++ *
++ * @returns= the opened file or `NULL` on error.
++ */
++FILE *ssh_strict_fop= en(const char *filename, size_t max_file_size)
++{
++ FILE *f =3D= NULL;
++ struct stat sb;
++ char err_msg[SSH_ERRNO_MSG_MAX] =3D = {0};
++ int r, fd;
++
++ /* open first to avoid TOCTOU */++ fd =3D open(filename, O_RDONLY);
++ if (fd =3D=3D -1) {
++ = SSH_LOG(SSH_LOG_RARE,
++ "Failed to open a file %s for reading: %s",++ filename,
++ ssh_strerror(errno, err_msg, SSH_ERRNO_MSG_MAX));<= br />++ return NULL;
++ }
++
++ /* Check the file is sensibl= e for a configuration file */
++ r =3D fstat(fd, &sb);
++ if = (r !=3D 0) {
++ SSH_LOG(SSH_LOG_RARE,
++ "Failed to stat %s: %s",=
++ filename,
++ ssh_strerror(errno, err_msg, SSH_ERRNO_MSG_MAX))= ;
++ close(fd);
++ return NULL;
++ }
++ if ((sb.st_mode= & S_IFMT) !=3D S_IFREG) {
++ SSH_LOG(SSH_LOG_RARE,
++ "The f= ile %s is not a regular file: skipping",
++ filename);
++ close(f= d);
++ return NULL;
++ }
++
++ if ((size_t)sb.st_size &= gt; max_file_size) {
++ SSH_LOG(SSH_LOG_RARE,
++ "The file %s is = too large (%jd MB > %zu MB): skipping",
++ filename,
++ (intma= x_t)sb.st_size / 1024 / 1024,
++ max_file_size / 1024 / 1024);
++= close(fd);
++ return NULL;
++ }
++
++ f =3D fdopen(fd,= "r");
++ if (f =3D=3D NULL) {
++ SSH_LOG(SSH_LOG_RARE,
++ "= Failed to open a file %s for reading: %s",
++ filename,
++ ssh_st= rerror(r, err_msg, SSH_ERRNO_MSG_MAX));
++ close(fd);
++ return N= ULL;
++ }
++
++ /* the flcose() will close also the underlyi= ng fd */
++ return f;
++}
++
+ /** @} */
+diff --g= it a/tests/unittests/torture_config.c b/tests/unittests/torture_config.c+index fcfe8fbc..0cb31a76 100644
+--- a/tests/unittests/torture_con= fig.c
++++ b/tests/unittests/torture_config.c
+@@ -2675,6 +2675,2= 3 @@ static void torture_config_match_complex(void **state)
+ ssh_stri= ng_free_char(v);
+ }
+
++/* Invalid configuration files
++ */
++static void torture_config_invalid(void **state)
++{
++ ssh_session session =3D *state;
++
++ ssh_options_set(sessio= n, SSH_OPTIONS_HOST, "Bar");
++
++ /* non-regular file -- ignored= (or missing on non-unix) so OK */
++ _parse_config(session, "/dev/ran= dom", NULL, SSH_OK);
++
++#ifndef _WIN32
++ /* huge file -- = ignored (or missing on non-unix) so OK */
++ _parse_config(session, "/= proc/kcore", NULL, SSH_OK);
++#endif
++}
++
+ int tortu= re_run_tests(void)
+ {
+ int rc;
+@@ -2771,6 +2788,9 @@ int = torture_run_tests(void)
+ setup, teardown),
+ cmocka_unit_test_se= tup_teardown(torture_config_match_complex,
+ setup, teardown),
++= cmocka_unit_test_setup_teardown(torture_config_invalid,
++ setup,
++ teardown),
+ };
+
+
+--
+2.51.0
+
d= iff --git a/meta-oe/recipes-support/libssh/libssh_0.11.3.bb b/meta-oe/recip= es-support/libssh/libssh_0.11.3.bb
index 193ff3512d..c552692bde 100644=
--- a/meta-oe/recipes-support/libssh/libssh_0.11.3.bb
+++ b/meta= -oe/recipes-support/libssh/libssh_0.11.3.bb
@@ -14,6 +14,7 @@ SRC_URI = =3D "git://git.libssh.org/projects/libssh.git;protocol=3Dhttps;branch=3Dsta= ble
file://CVE-2026-0968_p1.patch \
file://CVE-2026-0968_p2.patch= \
file://CVE-2026-0967.patch \
+ file://CVE-2026-0965.patch \"

SRC_URI:append:toolchain-clang =3D " file://0001-CompilerCh= ecks.cmake-drop-Wunused-variable-flag.patch"
--
2.35.6



On Wed, Apr 8, 2026 at 10:49 AM, Ankur Tyagi wrote:
I think we can upgrade to v0.11.4

https://git.libssh.org/projects/libssh.git/tre= e/CHANGELOG?h=3Dstable-0.11

version 0.11.4 (released 2026-02= -10)
* Security:
* CVE-2025-14821: libssh loads configuration fil= es from the C:\etc directory
on Windows
* CVE-2026-0964: SCP Prot= ocol Path Traversal in ssh_scp_pull_request()
* CVE-2026-0965: Possibl= e Denial of Service when parsing unexpected
configuration files
*= CVE-2026-0966: Buffer underflow in ssh_get_hexa() on invalid input
* = CVE-2026-0967: Specially crafted patterns could cause DoS
* CVE-2026-0= 968: OOB Read in sftp_parse_longname()
* libssh-2026-sftp-extensions: = Read buffer overrun when handling SFTP
extensions
* Stability and= compatibility improvements of ProxyJum



On Mon, Apr = 6, 2026 at 11:56=E2=80=AFPM Deepak Rathore via
lists.openembedded.org = <deeratho=3Dcisco.com@lists.openembedded.org>
wrote:

From: Deepak Rathore <deeratho@cisco.com>

Pick the patch [1] as mentioned in [2]

[1] https://git.libssh.org/pr= ojects/libssh.git/commit/?id=3Dbf390a042623e02abc8f421c4c5fadc0429a8a76=
[2] https://security-tracker.debian.org= /tracker/CVE-2026-0965

Signed-off-by: Deepak Rathore <dee= ratho@cisco.com>
---
.../libssh/libssh/CVE-2026-0965.patch | 2= 86 ++++++++++++++++++
.../recipes-support/libssh/libssh_0.11.3.bb | 1 = +
2 files changed, 287 insertions(+)
create mode 100644 meta-oe/r= ecipes-support/libssh/libssh/CVE-2026-0965.patch

diff --git a/me= ta-oe/recipes-support/libssh/libssh/CVE-2026-0965.patch b/meta-oe/recipes-s= upport/libssh/libssh/CVE-2026-0965.patch
new file mode 100644
ind= ex 0000000000..57cb9d6170
--- /dev/null
+++ b/meta-oe/recipes-sup= port/libssh/libssh/CVE-2026-0965.patch
@@ -0,0 +1,286 @@
+From 58= 58a988942d2e25985b34b8c40ce2792cbbe853 Mon Sep 17 00:00:00 2001
+From:= Jakub Jelen <jjelen@redhat.com>
+Date: Thu, 11 Dec 2025 17:33:1= 9 +0100
+Subject: [PATCH 4/4] CVE-2026-0965 config: Do not attempt to = read non-regular
+ and too large configuration files
+
+Chan= ges also the reading of known_hosts to use the new helper function
++CVE: CVE-2026-0965
+Upstream-Status: Backport [https://git.libssh.org/p= rojects/libssh.git/commit/?id=3Dbf390a042623e02abc8f421c4c5fadc0429a8a76]
+
+Signed-off-by: Jakub Jelen <jjelen@redhat.com>
+= Reviewed-by: Andreas Schneider <asn@cryptomilk.org>
+(cherry pic= ked from commit a5eb30dbfd8f3526b2d04bd9f0a3803b665f5798)
+(cherry pic= ked from commit bf390a042623e02abc8f421c4c5fadc0429a8a76)
+Signed-off-= by: Deepak Rathore <deeratho@cisco.com>
+---
+ include/libs= sh/misc.h | 3 ++
+ include/libssh/priv.h | 3 ++
+ src/bind_config= .c | 4 +-
+ src/config.c | 8 ++--
+ src/dh-gex.c | 4 +-
+ sr= c/known_hosts.c | 2 +-
+ src/knownhosts.c | 2 +-
+ src/misc.c | 7= 4 ++++++++++++++++++++++++++++++++
+ tests/unittests/torture_config.c = | 20 +++++++++
+ 9 files changed, 110 insertions(+), 10 deletions(-)+
+diff --git a/include/libssh/misc.h b/include/libssh/misc.h
+index ab726a0e..8eab94ee 100644
+--- a/include/libssh/misc.h
++= ++ b/include/libssh/misc.h
+@@ -36,6 +36,7 @@
+ #include <sys/= types.h>
+ #include <stdbool.h>
+ #endif /* _WIN32 */++#include <stdio.h>
+
+ #ifdef __cplusplus
+ exter= n "C" {
+@@ -136,6 +137,8 @@ int ssh_check_username_syntax(const char = *username);
+ void ssh_proxyjumps_free(struct ssh_list *proxy_jump_lis= t);
+ bool ssh_libssh_proxy_jumps(void);
+
++FILE *ssh_stric= t_fopen(const char *filename, size_t max_file_size);
++
+ #ifdef = __cplusplus
+ }
+ #endif
+diff --git a/include/libssh/priv.h= b/include/libssh/priv.h
+index 35fd8506..62069970 100644
+--- a/= include/libssh/priv.h
++++ b/include/libssh/priv.h
+@@ -473,6 +47= 3,9 @@ char *ssh_strerror(int err_num, char *buf, size_t buflen);
+ #d= efine SSH_TTY_MODES_MAX_BUFSIZE (55 * 5 + 1)
+ int encode_current_tty_= opts(unsigned char *buf, size_t buflen);
+
++/** The default maxi= mum file size for a configuration file */
++#define SSH_MAX_CONFIG_FIL= E_SIZE 16 * 1024 * 1024
++
+ #ifdef __cplusplus
+ }
+ #= endif
+diff --git a/src/bind_config.c b/src/bind_config.c
+index = 9e4a7fd4..c12f1003 100644
+--- a/src/bind_config.c
++++ b/src/bin= d_config.c
+@@ -212,7 +212,7 @@ local_parse_file(ssh_bind bind,
+= return;
+ }
+
+- f =3D fopen(filename, "r");
++ f =3D = ssh_strict_fopen(filename, SSH_MAX_CONFIG_FILE_SIZE);
+ if (f =3D=3D N= ULL) {
+ SSH_LOG(SSH_LOG_RARE, "Cannot find file %s to load",
+ f= ilename);
+@@ -636,7 +636,7 @@ int ssh_bind_config_parse_file(ssh_bind= bind, const char *filename)
+ * option to be redefined later by anoth= er file. */
+ uint8_t seen[BIND_CFG_MAX] =3D {0};
+
+- f =3D= fopen(filename, "r");
++ f =3D ssh_strict_fopen(filename, SSH_MAX_CON= FIG_FILE_SIZE);
+ if (f =3D=3D NULL) {
+ return 0;
+ }
= +diff --git a/src/config.c b/src/config.c
+index b4171efd..1ffad537 10= 0644
+--- a/src/config.c
++++ b/src/config.c
+@@ -223,10 +22= 3,9 @@ local_parse_file(ssh_session session,
+ return;
+ }
+=
+- f =3D fopen(filename, "r");
++ f =3D ssh_strict_fopen(filenam= e, SSH_MAX_CONFIG_FILE_SIZE);
+ if (f =3D=3D NULL) {
+- SSH_LOG(S= SH_LOG_RARE, "Cannot find file %s to load",
+- filename);
++ /* T= he underlying function logs the reasons */
+ return;
+ }
++@@ -1466,8 +1465,9 @@ int ssh_config_parse_file(ssh_session session, c= onst char *filename)
+ int parsing, rv;
+ bool global =3D 0;
+
+- f =3D fopen(filename, "r");
++ f =3D ssh_strict_fopen(filen= ame, SSH_MAX_CONFIG_FILE_SIZE);
+ if (f =3D=3D NULL) {
++ /* The = underlying function logs the reasons */
+ return 0;
+ }
++diff --git a/src/dh-gex.c b/src/dh-gex.c
+index 46ba934e..428a5655= 100644
+--- a/src/dh-gex.c
++++ b/src/dh-gex.c
+@@ -519,9 += 519,9 @@ static int ssh_retrieve_dhgroup(char *moduli_file,
+ }
+=
+ if (moduli_file !=3D NULL)
+- moduli =3D fopen(moduli_file, "r= ");
++ moduli =3D ssh_strict_fopen(moduli_file, SSH_MAX_CONFIG_FILE_SI= ZE);
+ else
+- moduli =3D fopen(MODULI_FILE, "r");
++ moduli= =3D ssh_strict_fopen(MODULI_FILE, SSH_MAX_CONFIG_FILE_SIZE);
+
+= if (moduli =3D=3D NULL) {
+ char err_msg[SSH_ERRNO_MSG_MAX] =3D {0};<= br />+diff --git a/src/known_hosts.c b/src/known_hosts.c
+index 3ef83e= 21..701576ce 100644
+--- a/src/known_hosts.c
++++ b/src/known_hos= ts.c
+@@ -83,7 +83,7 @@ static struct ssh_tokens_st *ssh_get_knownhost= _line(FILE **file,
+ struct ssh_tokens_st *tokens =3D NULL;
+
+ if (*file =3D=3D NULL) {
+- *file =3D fopen(filename,"r");
++= *file =3D ssh_strict_fopen(filename, SSH_MAX_CONFIG_FILE_SIZE);
+ if = (*file =3D=3D NULL) {
+ return NULL;
+ }
+diff --git a/src/k= nownhosts.c b/src/knownhosts.c
+index a2d08a75..3ab468de 100644
+= --- a/src/knownhosts.c
++++ b/src/knownhosts.c
+@@ -232,7 +232,7 = @@ static int ssh_known_hosts_read_entries(const char *match,
+ FILE *= fp =3D NULL;
+ int rc;
+
+- fp =3D fopen(filename, "r");
++ fp =3D ssh_strict_fopen(filename, SSH_MAX_CONFIG_FILE_SIZE);
+ if= (fp =3D=3D NULL) {
+ char err_msg[SSH_ERRNO_MSG_MAX] =3D {0};
+ = SSH_LOG(SSH_LOG_TRACE, "Failed to open the known_hosts file '%s': %s",
+diff --git a/src/misc.c b/src/misc.c
+index 774211fb..3968e6bc 10064= 4
+--- a/src/misc.c
++++ b/src/misc.c
+@@ -37,6 +37,7 @@
+ #endif /* _WIN32 */
+
+ #include <errno.h>
++#incl= ude <fcntl.h>
+ #include <limits.h>
+ #include <st= dio.h>
+ #include <string.h>
+@@ -2244,4 +2245,77 @@ ssh= _libssh_proxy_jumps(void)
+ return !(t !=3D NULL && t[0] =3D= =3D '1');
+ }
+
++/**
++ * @internal
++ *
++ = * @brief Safely open a file containing some configuration.
++ *
+= + * Runs checks if the file can be used as some configuration file (is regu= lar
++ * file and is not too large). If so, returns the opened file (f= or reading).
++ * Otherwise logs error and returns `NULL`.
++ *++ * @param filename The path to the file to open.
++ * @param max= _file_size Maximum file size that is accepted.
++ *
++ * @returns= the opened file or `NULL` on error.
++ */
++FILE *ssh_strict_fop= en(const char *filename, size_t max_file_size)
++{
++ FILE *f =3D= NULL;
++ struct stat sb;
++ char err_msg[SSH_ERRNO_MSG_MAX] =3D = {0};
++ int r, fd;
++
++ /* open first to avoid TOCTOU */++ fd =3D open(filename, O_RDONLY);
++ if (fd =3D=3D -1) {
++ = SSH_LOG(SSH_LOG_RARE,
++ "Failed to open a file %s for reading: %s",++ filename,
++ ssh_strerror(errno, err_msg, SSH_ERRNO_MSG_MAX));<= br />++ return NULL;
++ }
++
++ /* Check the file is sensibl= e for a configuration file */
++ r =3D fstat(fd, &sb);
++ if = (r !=3D 0) {
++ SSH_LOG(SSH_LOG_RARE,
++ "Failed to stat %s: %s",=
++ filename,
++ ssh_strerror(errno, err_msg, SSH_ERRNO_MSG_MAX))= ;
++ close(fd);
++ return NULL;
++ }
++ if ((sb.st_mode= & S_IFMT) !=3D S_IFREG) {
++ SSH_LOG(SSH_LOG_RARE,
++ "The f= ile %s is not a regular file: skipping",
++ filename);
++ close(f= d);
++ return NULL;
++ }
++
++ if ((size_t)sb.st_size &= gt; max_file_size) {
++ SSH_LOG(SSH_LOG_RARE,
++ "The file %s is = too large (%jd MB > %zu MB): skipping",
++ filename,
++ (intma= x_t)sb.st_size / 1024 / 1024,
++ max_file_size / 1024 / 1024);
++= close(fd);
++ return NULL;
++ }
++
++ f =3D fdopen(fd,= "r");
++ if (f =3D=3D NULL) {
++ SSH_LOG(SSH_LOG_RARE,
++ "= Failed to open a file %s for reading: %s",
++ filename,
++ ssh_st= rerror(r, err_msg, SSH_ERRNO_MSG_MAX));
++ close(fd);
++ return N= ULL;
++ }
++
++ /* the flcose() will close also the underlyi= ng fd */
++ return f;
++}
++
+ /** @} */
+diff --g= it a/tests/unittests/torture_config.c b/tests/unittests/torture_config.c+index fcfe8fbc..0cb31a76 100644
+--- a/tests/unittests/torture_con= fig.c
++++ b/tests/unittests/torture_config.c
+@@ -2675,6 +2675,2= 3 @@ static void torture_config_match_complex(void **state)
+ ssh_stri= ng_free_char(v);
+ }
+
++/* Invalid configuration files
++ */
++static void torture_config_invalid(void **state)
++{
++ ssh_session session =3D *state;
++
++ ssh_options_set(sessio= n, SSH_OPTIONS_HOST, "Bar");
++
++ /* non-regular file -- ignored= (or missing on non-unix) so OK */
++ _parse_config(session, "/dev/ran= dom", NULL, SSH_OK);
++
++#ifndef _WIN32
++ /* huge file -- = ignored (or missing on non-unix) so OK */
++ _parse_config(session, "/= proc/kcore", NULL, SSH_OK);
++#endif
++}
++
+ int tortu= re_run_tests(void)
+ {
+ int rc;
+@@ -2771,6 +2788,9 @@ int = torture_run_tests(void)
+ setup, teardown),
+ cmocka_unit_test_se= tup_teardown(torture_config_match_complex,
+ setup, teardown),
++= cmocka_unit_test_setup_teardown(torture_config_invalid,
++ setup,
++ teardown),
+ };
+
+
+--
+2.51.0
+
d= iff --git a/meta-oe/recipes-support/libssh/libssh_0.11.3.bb b/meta-oe/recip= es-support/libssh/libssh_0.11.3.bb
index 193ff3512d..c552692bde 100644=
--- a/meta-oe/recipes-support/libssh/libssh_0.11.3.bb
+++ b/meta= -oe/recipes-support/libssh/libssh_0.11.3.bb
@@ -14,6 +14,7 @@ SRC_URI = =3D "git://git.libssh.org/projects/libssh.git;protocol=3Dhttps;branch=3Dsta= ble
file://CVE-2026-0968_p1.patch \
file://CVE-2026-0968_p2.patch= \
file://CVE-2026-0967.patch \
+ file://CVE-2026-0965.patch \"

SRC_URI:append:toolchain-clang =3D " file://0001-CompilerCh= ecks.cmake-drop-Wunused-variable-flag.patch"
--
2.35.6



--JNqEUsod6vV0DnxfjZfZ--