From patchwork Mon Apr 6 11:54:54 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "Deepak Rathore -X (deeratho - E INFOCHIPS PRIVATE LIMITED at Cisco)" X-Patchwork-Id: 85314 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 00CEAEF4ECD for ; Mon, 6 Apr 2026 11:55:16 +0000 (UTC) Received: from alln-iport-8.cisco.com (alln-iport-8.cisco.com [173.37.142.95]) by mx.groups.io with SMTP id smtpd.msgproc02-g2.53310.1775476512375310499 for ; Mon, 06 Apr 2026 04:55:12 -0700 Authentication-Results: mx.groups.io; dkim=fail reason="dkim: message contains an insecure body length tag" header.i=@cisco.com header.s=iport01 header.b=EMZxZ4KG; spf=pass (domain: cisco.com, ip: 173.37.142.95, mailfrom: deeratho@cisco.com) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=cisco.com; i=@cisco.com; l=8117; q=dns/txt; s=iport01; t=1775476512; x=1776686112; h=from:to:subject:date:message-id:mime-version: content-transfer-encoding; bh=f4M9MndZ8I9TFYunn/lbfFEgFvS/cLegaH6efCciirI=; b=EMZxZ4KGDn0qVwi19pitnFvF5Jg3gv5BrQw20IhGJ6tWKK3fOnuAIcTG ftrFScY4en5V8UfYYrN0vUZVwu00pqZyQFYEGj7axY1P7keyIYxluNhLn GFcXZuJZcrxnI3iZqXKltD0ft17BU2U83y1PfGiiMpj+LFvJr+lgkbwed IkZTN2Bop/DVAMopk9Bt+K8QKHK5tRf9qnEQbEwCGK4qgvtD1hPeOjSdH UIr8QOvUwlzkWgYffSJ6PDRJH9Rw8Nz1je++Vo3h6mVS0Tymq4XtkZ1aj 4DK1+xQIvTmMrEHbkkfxqi4mZQBBwkBBzMJo/3FVmPir3auBATHq7sUhL w==; X-CSE-ConnectionGUID: LciztflzRvS0Cc0rVixyPw== X-CSE-MsgGUID: YRU1Xa0aS3effd7b0iA5Ug== X-IPAS-Result: A0DHAgD2ndNp/43/Ja1aglmCV3FfQkkDlCeCIZ4dgX8PAQEBD0QNBAEBkjMCJjUIDgECBAEBAQEDAgMBAQEBAQEBAQEBAQsBAQUBAQECAQcFgQ4Thk8NhloBOAEYAVkDAQJaIyGDAgGCcwIBEbYSgiyBAYMoATEFCQICQAFP2yYBCxQBgTiFPogcWxgBhHonGxuBcoEVg2iBBYFcAgIBF4gKBIIigQ6BYR6BaQaETYdfSIEeA1ksAVUTDQoLBwWBZgM1EioVbjIdgSM+F4EMGwcFgUuGSnRtgRODd2YDCxgNSBEsNxQbBD5uB4shJ4I2ZycBExgXgX0YERGlUqEOCiiDdIwelToaM6oEZ5kGjgmWUIRogWoBOYFZcBWDIglJGQ+OLQsLiHHAbyM1AgkDMAEHAgcOAoFzkX0BAQ IronPort-Data: A9a23:3MnHSqqb5uPjH4iGyXQrLum4rxNeBmJIZBIvgKrLsJaIsI4StFCzt garIBnUM/ffamWmKt53YYy/9UxUvZOHmoJkHgA//HhgFHxGpePIVI+TRqvS04x+DSFioGZPt Zh2hgzodZhsJpPkjk7zdOCn9j8kif3gqoPUUIbsIjp2SRJvVBAvgBdin/9RqoNziLBVOSvV0 T/Ji5OZYgTNNwJcaDpOtfrf8E035ZwehRtB1rAATaET1LPhvyF94KI3fcmZM3b+S49IKe+2L 86r5K255G7Q4yA2AdqjlLvhGmVSKlIFFVHT4pb+c/HKbilq/kTe4I5iXBYvQRs/ZwGyojxE4 I4lWapc5useFvakdOw1C3G0GszlVEFM0OevzXOX6aR/w6BaGpfh660GMa04AWEX0sJ8AHNS+ /gKEi42NjuI3/iIwaKqcvY506zPLOGzVG8ekmtrwTecCbMtRorOBv2Wo9RZxzw3wMtJGJ4yZ eJANmEpN0uGOUASfA5LUfrSn8/w7pX7WzFVpUicuaowy2PS1wd2lrPqNbI5f/TVHJ0Ixh7I/ T6uE2LRMysnbeyWkTO+qX+iiOOQl3v6cd8vG+jtnhJtqBjJroAJMzURTVa9rPyzh0KyVt4aJ 0EK9y4Gqakp6FftScHwWRC9qnOIshMQHd1KHIUHBBql0KHY5UOdQ2MDVDMEMIBgv84tTjts3 ViM9z/0OQFSXHSuYSr13t+pQfmaY0D58Udqifc4cDY4 IronPort-HdrOrdr: A9a23:pckt1qv3qhZ1UWvC929roaIM7skDRtV00zEX/kB9WHVpm6uj5q KTdZsguyMc5Ax9ZJhCo6HiBED/exLhHPdOiOF7V4tKNzOIhILHFu1fBPPZowHIKmnZ6vNX07 tmfuxVDd39CkU/sOPBiTPIdurJBLK8gceVbSC09QYIcT1X X-Talos-CUID: 9a23:Ofh7DW+tVg94bJ1+tCGVv2ofG9I5fHnP9mnre2SzUXxXSa2EcUDFrQ== X-Talos-MUID: 9a23:tZx4aQqzRo5P+MsBgfwezwBYCZ5Y6vynMxsAn5sK5MPcNAUtBjjI2Q== X-IronPort-Anti-Spam-Filtered: true X-IronPort-AV: E=Sophos;i="6.23,163,1770595200"; d="scan'208";a="710500834" Received: from rcdn-l-core-04.cisco.com ([173.37.255.141]) by alln-iport-8.cisco.com with ESMTP/TLS/TLS_AES_256_GCM_SHA384; 06 Apr 2026 11:55:11 +0000 Received: from sjc-ads-3552.cisco.com (sjc-ads-3552.cisco.com [171.68.249.250]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by rcdn-l-core-04.cisco.com (Postfix) with ESMTPS id 3E12F180001B5 for ; Mon, 6 Apr 2026 11:55:11 +0000 (GMT) Received: by sjc-ads-3552.cisco.com (Postfix, from userid 1795984) id DFCCBCC12B5; Mon, 6 Apr 2026 04:55:10 -0700 (PDT) From: "Deepak Rathore -X (deeratho - E INFOCHIPS PRIVATE LIMITED at Cisco)" To: openembedded-devel@lists.openembedded.org Subject: [oe][meta-oe][whinlatter][PATCH 1/3] libssh: Fix CVE-2026-0968 Date: Mon, 6 Apr 2026 04:54:54 -0700 Message-Id: <20260406115454.1241643-1-deeratho@cisco.com> X-Mailer: git-send-email 2.35.6 MIME-Version: 1.0 X-Outbound-Client-TLS: ANONYMOUS;sjc-ads-3552.cisco.com [171.68.249.250];TLSv1.3;TLS_AES_256_GCM_SHA384;256 X-Outbound-SMTP-Client: 171.68.249.250, sjc-ads-3552.cisco.com X-Outbound-Node: rcdn-l-core-04.cisco.com 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 ; Mon, 06 Apr 2026 11:55:15 -0000 X-Groupsio-URL: https://lists.openembedded.org/g/openembedded-devel/message/126035 From: Deepak Rathore Pick the patch [1] and [2] as mentioned in [3] [1] https://git.libssh.org/projects/libssh.git/commit/?id=796d85f786dff62bd4bcc4408d9b7bbc855841e9 [2] https://git.libssh.org/projects/libssh.git/commit/?id=212121971fb26e1e00b72bd5402c0454a4d84c03 [3] https://security-tracker.debian.org/tracker/CVE-2026-0968 Signed-off-by: Deepak Rathore --- .../libssh/libssh/CVE-2026-0968_p1.patch | 64 +++++++++ .../libssh/libssh/CVE-2026-0968_p2.patch | 132 ++++++++++++++++++ .../recipes-support/libssh/libssh_0.11.3.bb | 2 + 3 files changed, 198 insertions(+) create mode 100644 meta-oe/recipes-support/libssh/libssh/CVE-2026-0968_p1.patch create mode 100644 meta-oe/recipes-support/libssh/libssh/CVE-2026-0968_p2.patch diff --git a/meta-oe/recipes-support/libssh/libssh/CVE-2026-0968_p1.patch b/meta-oe/recipes-support/libssh/libssh/CVE-2026-0968_p1.patch new file mode 100644 index 0000000000..97ae88b2be --- /dev/null +++ b/meta-oe/recipes-support/libssh/libssh/CVE-2026-0968_p1.patch @@ -0,0 +1,64 @@ +From 14a1c80ce06cd2c3e4798ec08b25a55ddaf95076 Mon Sep 17 00:00:00 2001 +From: Jakub Jelen +Date: Mon, 22 Dec 2025 20:59:11 +0100 +Subject: [PATCH 1/4] CVE-2026-0968: sftp: Sanitize input handling in + sftp_parse_longname() + +CVE: CVE-2026-0968 +Upstream-Status: Backport [https://git.libssh.org/projects/libssh.git/commit/?id=796d85f786dff62bd4bcc4408d9b7bbc855841e9] + +Signed-off-by: Jakub Jelen +Reviewed-by: Andreas Schneider +(cherry picked from commit 20856f44c146468c830da61dcbbbaa8ce71e390b) +(cherry picked from commit 796d85f786dff62bd4bcc4408d9b7bbc855841e9) +Signed-off-by: Deepak Rathore +--- + src/sftp_common.c | 16 +++++++++++++--- + 1 file changed, 13 insertions(+), 3 deletions(-) + +diff --git a/src/sftp_common.c b/src/sftp_common.c +index 13512b8d..b05597d8 100644 +--- a/src/sftp_common.c ++++ b/src/sftp_common.c +@@ -461,16 +461,21 @@ static char * sftp_parse_longname(const char *longname, + const char *p, *q; + size_t len, field = 0; + ++ if (longname == NULL || longname_field < SFTP_LONGNAME_PERM || ++ longname_field > SFTP_LONGNAME_NAME) { ++ return NULL; ++ } ++ + p = longname; + /* + * Find the beginning of the field which is specified + * by sftp_longname_field_e. + */ +- while (field != longname_field) { ++ while (*p != '\0' && field != longname_field) { + if (isspace(*p)) { + field++; + p++; +- while (*p && isspace(*p)) { ++ while (*p != '\0' && isspace(*p)) { + p++; + } + } else { +@@ -478,8 +483,13 @@ static char * sftp_parse_longname(const char *longname, + } + } + ++ /* If we reached NULL before we got our field fail */ ++ if (field != longname_field) { ++ return NULL; ++ } ++ + q = p; +- while (! isspace(*q)) { ++ while (*q != '\0' && !isspace(*q)) { + q++; + } + +-- +2.51.0 + diff --git a/meta-oe/recipes-support/libssh/libssh/CVE-2026-0968_p2.patch b/meta-oe/recipes-support/libssh/libssh/CVE-2026-0968_p2.patch new file mode 100644 index 0000000000..6de0d6cb3d --- /dev/null +++ b/meta-oe/recipes-support/libssh/libssh/CVE-2026-0968_p2.patch @@ -0,0 +1,132 @@ +From 5ad81f0514bf547055fd17dd4ca05121f1e512c9 Mon Sep 17 00:00:00 2001 +From: Jakub Jelen +Date: Mon, 22 Dec 2025 21:00:03 +0100 +Subject: [PATCH 2/4] CVE-2026-0968 tests: Reproducer for invalid longname data + +CVE: CVE-2026-0968 +Upstream-Status: Backport [https://git.libssh.org/projects/libssh.git/commit/?id=212121971fb26e1e00b72bd5402c0454a4d84c03] + +Signed-off-by: Jakub Jelen +Reviewed-by: Andreas Schneider +(cherry picked from commit 90a5d8f47399e8db61b56793cd21476ff6a528e0) +(cherry picked from commit 212121971fb26e1e00b72bd5402c0454a4d84c03) +Signed-off-by: Deepak Rathore +--- + tests/unittests/CMakeLists.txt | 7 +++ + tests/unittests/torture_unit_sftp.c | 86 +++++++++++++++++++++++++++++ + 2 files changed, 93 insertions(+) + create mode 100644 tests/unittests/torture_unit_sftp.c + +diff --git a/tests/unittests/CMakeLists.txt b/tests/unittests/CMakeLists.txt +index 79f3856c..53478af9 100644 +--- a/tests/unittests/CMakeLists.txt ++++ b/tests/unittests/CMakeLists.txt +@@ -98,6 +98,13 @@ if (UNIX AND NOT WIN32) + endif (WITH_SERVER) + endif (UNIX AND NOT WIN32) + ++if (WITH_SFTP) ++ set(LIBSSH_UNIT_TESTS ++ ${LIBSSH_UNIT_TESTS} ++ torture_unit_sftp ++ ) ++endif (WITH_SFTP) ++ + foreach(_UNIT_TEST ${LIBSSH_UNIT_TESTS}) + add_cmocka_test(${_UNIT_TEST} + SOURCES ${_UNIT_TEST}.c +diff --git a/tests/unittests/torture_unit_sftp.c b/tests/unittests/torture_unit_sftp.c +new file mode 100644 +index 00000000..12940039 +--- /dev/null ++++ b/tests/unittests/torture_unit_sftp.c +@@ -0,0 +1,86 @@ ++#include "config.h" ++ ++#include "sftp_common.c" ++#include "torture.h" ++ ++#define LIBSSH_STATIC ++ ++static void test_sftp_parse_longname(void **state) ++{ ++ const char *lname = NULL; ++ char *value = NULL; ++ ++ /* state not used */ ++ (void)state; ++ ++ /* Valid example from SFTP draft, page 18: ++ * https://datatracker.ietf.org/doc/draft-spaghetti-sshm-filexfer/ ++ */ ++ lname = "-rwxr-xr-x 1 mjos staff 348911 Mar 25 14:29 t-filexfer"; ++ value = sftp_parse_longname(lname, SFTP_LONGNAME_PERM); ++ assert_string_equal(value, "-rwxr-xr-x"); ++ free(value); ++ value = sftp_parse_longname(lname, SFTP_LONGNAME_OWNER); ++ assert_string_equal(value, "mjos"); ++ free(value); ++ value = sftp_parse_longname(lname, SFTP_LONGNAME_GROUP); ++ assert_string_equal(value, "staff"); ++ free(value); ++ value = sftp_parse_longname(lname, SFTP_LONGNAME_SIZE); ++ assert_string_equal(value, "348911"); ++ free(value); ++ /* This function is broken further as the date contains space which breaks ++ * the parsing altogether */ ++ value = sftp_parse_longname(lname, SFTP_LONGNAME_DATE); ++ assert_string_equal(value, "Mar"); ++ free(value); ++ value = sftp_parse_longname(lname, SFTP_LONGNAME_TIME); ++ assert_string_equal(value, "25"); ++ free(value); ++ value = sftp_parse_longname(lname, SFTP_LONGNAME_NAME); ++ assert_string_equal(value, "14:29"); ++ free(value); ++} ++ ++static void test_sftp_parse_longname_invalid(void **state) ++{ ++ const char *lname = NULL; ++ char *value = NULL; ++ ++ /* state not used */ ++ (void)state; ++ ++ /* Invalid inputs should not crash ++ */ ++ lname = NULL; ++ value = sftp_parse_longname(lname, SFTP_LONGNAME_PERM); ++ assert_null(value); ++ value = sftp_parse_longname(lname, SFTP_LONGNAME_NAME); ++ assert_null(value); ++ ++ lname = ""; ++ value = sftp_parse_longname(lname, SFTP_LONGNAME_PERM); ++ assert_string_equal(value, ""); ++ free(value); ++ value = sftp_parse_longname(lname, SFTP_LONGNAME_NAME); ++ assert_null(value); ++ ++ lname = "-rwxr-xr-x 1"; ++ value = sftp_parse_longname(lname, SFTP_LONGNAME_PERM); ++ assert_string_equal(value, "-rwxr-xr-x"); ++ free(value); ++ value = sftp_parse_longname(lname, SFTP_LONGNAME_NAME); ++ assert_null(value); ++} ++ ++int torture_run_tests(void) ++{ ++ int rc; ++ const struct CMUnitTest tests[] = { ++ cmocka_unit_test(test_sftp_parse_longname), ++ cmocka_unit_test(test_sftp_parse_longname_invalid), ++ }; ++ ++ rc = cmocka_run_group_tests(tests, NULL, NULL); ++ return rc; ++} +-- +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 ab47931fa3..1d4fd637d9 100644 --- a/meta-oe/recipes-support/libssh/libssh_0.11.3.bb +++ b/meta-oe/recipes-support/libssh/libssh_0.11.3.bb @@ -11,6 +11,8 @@ SRC_URI = "git://git.libssh.org/projects/libssh.git;protocol=https;branch=stable file://run-ptest \ file://CVE-2026-3731_p1.patch \ file://CVE-2026-3731_p2.patch \ + file://CVE-2026-0968_p1.patch \ + file://CVE-2026-0968_p2.patch \ " SRC_URI:append:toolchain-clang = " file://0001-CompilerChecks.cmake-drop-Wunused-variable-flag.patch" From patchwork Mon Apr 6 11:55:22 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: "Deepak Rathore -X (deeratho - E INFOCHIPS PRIVATE LIMITED at Cisco)" X-Patchwork-Id: 85315 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 DEFDCEF4ED0 for ; Mon, 6 Apr 2026 11:55:35 +0000 (UTC) Received: from alln-iport-3.cisco.com (alln-iport-3.cisco.com [173.37.142.90]) by mx.groups.io with SMTP id smtpd.msgproc02-g2.53314.1775476529734718028 for ; Mon, 06 Apr 2026 04:55:29 -0700 Authentication-Results: mx.groups.io; dkim=fail reason="dkim: message contains an insecure body length tag" header.i=@cisco.com header.s=iport01 header.b=BCGFaO1Z; spf=pass (domain: cisco.com, ip: 173.37.142.90, mailfrom: deeratho@cisco.com) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=cisco.com; i=@cisco.com; l=13940; q=dns/txt; s=iport01; t=1775476529; x=1776686129; h=from:to:subject:date:message-id:mime-version: content-transfer-encoding; bh=85v8U8uGGQkYlbmzYgUM5tTBZpcMT388Gy7WpFFc+T8=; b=BCGFaO1Zq3MJw+pfQvoCHlR2fyL+EhyuXXrEms6Ochr9lO7JSEpIi+Ht LjK/cfM8LqATY1WjshI39AltElR+aoIi1Wka6trh6ixeD3yV5YpMHH3FM 3IGWYCNQqNWNuGedGEaR7/3L03CZW6OArBOTYgyXM5tOPLxyapziUHUbr Mik7oYNrQo7bhy+P5U/8imAiL7qVN3999OGWSUzDeDNmpQps4VJAaiONg Ens71YtpolNFOKJ957JDfCxcp7qVEu0Ac+4ImnQYoN6yM4Dg//Wj1gQ11 ROcJMX7Fex98tBKTvhKrlcIioxAMuNzr41um+lsX5vCZ2cCZhsizM7VvT w==; X-CSE-ConnectionGUID: t7z0HGGcSNyepK3MHti5Tw== X-CSE-MsgGUID: azITTG7pQAiduLYb/Uxv5A== X-IPAS-Result: A0ChAwCwnNNp/5L/Ja1RCYI0EBqCU3FfQkkDhFSPU4IhgRaaKIReDwEBAQ9EDQQBAZIzAiY3Bg4BAgQBAQEBAwIDAQEBAQEBAQEBAQEBCgEBBQEBAQIBBwWBDhOGTw2GWgEpDwEYAUYTAwECAwImAi0jGAmDAgGCcwIBEZhZm0R6gTKBAYMoATEFCQJDT7UIAQsUAYEKLoI3gweDGQGFAlsYAUSENicbG4FygRWCcnaBBYFcAgIYgRoEhAOCaQSCIoEOgWGCBwaCTYIAgUqGFUiBAhwDWSwBVRMNCgsHBYFmAzUSKhVuMh2BIz4XNFgbBwWBS4ZKdG2BE4N3ZgMLGA1IESw3FBsEPQFuB4shJ4FzPAcBLBA3GgErIFsKWQ4fCRgRkwAdBwEJCpIrgTWdFYJECiiDdIwelToaM6prmQaOCZVoaIRogX4mgVlwFYMiCUkZD44tCwuDXoUTwS0jNQIJAzABBwIHAQEKAQOBc5ABgXwBAQ IronPort-Data: A9a23:DXwM/aMAxh7d73bvrR3ilsFynXyQoLVcMsEvi/4bfWQNrUom3mYOy jFODz2AOveMYDGnKt8ibYrgphgEsZPXxoRjHXM5pCpnJ55oRWUpJjg4wmPYZX76whjrFRo/h ykmQoCeap1yFjmA9knF3oHJ9RFUzbuPSqf3FNnKMyVwQR4MYCo6gHqPocZh6mJTqYb/WVjlV e/a+ZWFZgf5g2Asawr41orawP9RlKWq0N8nlgRWicBj5Df2i3QTBZQDEqC9R1OQrl58R7PSq 07rldlVz0uBl/sfIorNfoXTLiXmdoXv0T2m0RK6bUQNbi9q/UTe2o5jXBYVhNw+Zz+hx7idw /0V3XC8pJtA0qDkwIwgvxdk/y5WIfJUoJ/oDnOEl92e126dVlvwwqhSExRjVWEY0r4f7WBm7 /cULnUJKxuEne/zmOP9Qeh3jcNlJ87uVG8dkig/lneCUrB8HM2FGvmUjTNb9G9YasRmEfvTf cMFaT1HZxXbaBoJMVASYH47tLjy2CejI20J9Tp5o4I+8Unv8CMuyIHEMdbIXu2AAvhFgBux8 zeuE2PRR0ty2Mak4T2d/3Shg+XCkS/2VMceGaO18tZugUaP3SoUEBAQWF6xrPW1h0L4XMhQQ 3H44QI0pqQ0sUjuRd7nUljg8TiPvwUXXJxbFOhSBByx95c4Kj2xXgAsJgOtovR/3CPqbVTGD mO0ou4= IronPort-HdrOrdr: A9a23:5C5y7a8ecTisnau2BCZuk+DuI+orL9Y04lQ7vn2ZLiYlF/Bw9v re/sjzuiWbtN98YhwdcLO7Scq9qA3nlKKdiLN5VdzJYOCMggSVxe9ZgbcKuweBJwTOsshAyK xnb69yTPf0DVR8kILGxTPQKadF/DFCm5rY49s3CBxWPGZXV50= X-Talos-CUID: 9a23:PFtCRWuFxA3EysuSrnPSx2dp6Is5LW+F7Fj/KHSeGGtxa+awaUORpZ97xp8= X-Talos-MUID: 9a23:ael0nwmZ3JOX7OrV1Ao2dnprJuZU34vxEHkkkJkbn/iOchFgJWq02WE= X-IronPort-Anti-Spam-Filtered: true X-IronPort-AV: E=Sophos;i="6.23,163,1770595200"; d="scan'208";a="726648895" Received: from rcdn-l-core-09.cisco.com ([173.37.255.146]) by alln-iport-3.cisco.com with ESMTP/TLS/TLS_AES_256_GCM_SHA384; 06 Apr 2026 11:55:28 +0000 Received: from sjc-ads-3552.cisco.com (sjc-ads-3552.cisco.com [171.68.249.250]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by rcdn-l-core-09.cisco.com (Postfix) with ESMTPS id 7683618000481 for ; Mon, 6 Apr 2026 11:55:28 +0000 (GMT) Received: by sjc-ads-3552.cisco.com (Postfix, from userid 1795984) id 24D05CC12B5; Mon, 6 Apr 2026 04:55:28 -0700 (PDT) From: "Deepak Rathore -X (deeratho - E INFOCHIPS PRIVATE LIMITED at Cisco)" To: openembedded-devel@lists.openembedded.org Subject: [oe][meta-oe][whinlatter][PATCH 2/3] libssh: Fix CVE-2026-0967 Date: Mon, 6 Apr 2026 04:55:22 -0700 Message-Id: <20260406115522.1242484-1-deeratho@cisco.com> X-Mailer: git-send-email 2.35.6 MIME-Version: 1.0 X-Outbound-Client-TLS: ANONYMOUS;sjc-ads-3552.cisco.com [171.68.249.250];TLSv1.3;TLS_AES_256_GCM_SHA384;256 X-Outbound-SMTP-Client: 171.68.249.250, sjc-ads-3552.cisco.com X-Outbound-Node: rcdn-l-core-09.cisco.com 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 ; Mon, 06 Apr 2026 11:55:35 -0000 X-Groupsio-URL: https://lists.openembedded.org/g/openembedded-devel/message/126036 From: Deepak Rathore Pick the patch [1] as mentioned in [2] [1] https://git.libssh.org/projects/libssh.git/commit/?id=6d74aa6138895b3662bade9bd578338b0c4f8a15 [2] https://security-tracker.debian.org/tracker/CVE-2026-0967 Signed-off-by: Deepak Rathore --- .../libssh/libssh/CVE-2026-0967.patch | 362 ++++++++++++++++++ .../recipes-support/libssh/libssh_0.11.3.bb | 1 + 2 files changed, 363 insertions(+) create mode 100644 meta-oe/recipes-support/libssh/libssh/CVE-2026-0967.patch diff --git a/meta-oe/recipes-support/libssh/libssh/CVE-2026-0967.patch b/meta-oe/recipes-support/libssh/libssh/CVE-2026-0967.patch new file mode 100644 index 0000000000..0b32dfb97f --- /dev/null +++ b/meta-oe/recipes-support/libssh/libssh/CVE-2026-0967.patch @@ -0,0 +1,362 @@ +From a2de885e93b39d5d834f2e6d93bdc62dd9c0322d Mon Sep 17 00:00:00 2001 +From: Jakub Jelen +Date: Wed, 17 Dec 2025 18:48:34 +0100 +Subject: [PATCH 3/4] CVE-2026-0967 match: Avoid recursive matching (ReDoS) +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +The specially crafted patterns (from configuration files) could cause +exhaustive search or timeouts. + +Previous attempts to fix this by limiting recursion to depth 16 avoided +stack overflow, but not timeouts. This is due to the backtracking, +which caused the exponential time complexity O(N^16) of existing algorithm. + +This is code comes from the same function from OpenSSH, where this code +originates from, which is not having this issue (due to not limiting the number +of recursion), but will also easily exhaust stack due to unbound recursion: + +https://github.com/openssh/openssh-portable/commit/05bcd0cadf160fd4826a2284afa7cba6ec432633 + +This is an attempt to simplify the algorithm by preventing the backtracking +to previous wildcard, which should keep the same behavior for existing inputs +while reducing the complexity to linear O(N*M). + +This fixes the long-term issue we had with fuzzing as well as recently reported +security issue by Kang Yang. + +CVE: CVE-2026-0967 +Upstream-Status: Backport [https://git.libssh.org/projects/libssh.git/commit/?id=6d74aa6138895b3662bade9bd578338b0c4f8a15] + +Signed-off-by: Jakub Jelen +Reviewed-by: Pavol Žáčik +(cherry picked from commit a411de5ce806e3ea24d088774b2f7584d6590b5f) +(cherry picked from commit 6d74aa6138895b3662bade9bd578338b0c4f8a15) +Signed-off-by: Deepak Rathore +--- + src/match.c | 111 +++++++++++++---------------- + tests/unittests/torture_config.c | 116 +++++++++++++++++++++++-------- + 2 files changed, 135 insertions(+), 92 deletions(-) + +diff --git a/src/match.c b/src/match.c +index 2c004c98..771ee63c 100644 +--- a/src/match.c ++++ b/src/match.c +@@ -53,85 +53,70 @@ + + #include "libssh/priv.h" + +-#define MAX_MATCH_RECURSION 16 +- +-/* +- * Returns true if the given string matches the pattern (which may contain ? +- * and * as wildcards), and zero if it does not match. ++/** ++ * @brief Compare a string with a pattern containing wildcards `*` and `?` ++ * ++ * This function is an iterative replacement for the previously recursive ++ * implementation to avoid exponential complexity (DoS) with specific patterns. ++ * ++ * @param[in] s The string to match. ++ * @param[in] pattern The pattern to match against. ++ * ++ * @return 1 if the pattern matches, 0 otherwise. + */ +-static int match_pattern(const char *s, const char *pattern, size_t limit) ++static int match_pattern(const char *s, const char *pattern) + { +- bool had_asterisk = false; ++ const char *s_star = NULL; /* Position in s when last `*` was met */ ++ const char *p_star = NULL; /* Position in pattern after last `*` */ + +- if (s == NULL || pattern == NULL || limit <= 0) { ++ if (s == NULL || pattern == NULL) { + return 0; + } + +- for (;;) { +- /* If at end of pattern, accept if also at end of string. */ +- if (*pattern == '\0') { +- return (*s == '\0'); +- } +- +- /* Skip all the asterisks and adjacent question marks */ +- while (*pattern == '*' || (had_asterisk && *pattern == '?')) { +- if (*pattern == '*') { +- had_asterisk = true; +- } ++ while (*s) { ++ /* Case 1: Exact match or '?' wildcard */ ++ if (*pattern == *s || *pattern == '?') { ++ s++; + pattern++; ++ continue; + } + +- if (had_asterisk) { +- /* If at end of pattern, accept immediately. */ +- if (!*pattern) +- return 1; +- +- /* If next character in pattern is known, optimize. */ +- if (*pattern != '?') { +- /* +- * Look instances of the next character in +- * pattern, and try to match starting from +- * those. +- */ +- for (; *s; s++) +- if (*s == *pattern && match_pattern(s + 1, pattern + 1, limit - 1)) { +- return 1; +- } +- /* Failed. */ +- return 0; +- } +- /* +- * Move ahead one character at a time and try to +- * match at each position. ++ /* Case 2: '*' wildcard */ ++ if (*pattern == '*') { ++ /* Record the position of the star and the current string position. ++ * We optimistically assume * matches 0 characters first. + */ +- for (; *s; s++) { +- if (match_pattern(s, pattern, limit - 1)) { +- return 1; +- } +- } +- /* Failed. */ +- return 0; +- } +- /* +- * There must be at least one more character in the string. +- * If we are at the end, fail. +- */ +- if (!*s) { +- return 0; ++ p_star = ++pattern; ++ s_star = s; ++ continue; + } + +- /* Check if the next character of the string is acceptable. */ +- if (*pattern != '?' && *pattern != *s) { +- return 0; ++ /* Case 3: Mismatch */ ++ if (p_star) { ++ /* If we have seen a star previously, backtrack. ++ * We restore the pattern to just after the star, ++ * but advance the string position (consume one more char for the ++ * star). ++ * No need to backtrack to previous stars as any match of the last ++ * star could be eaten the same way by the previous star. ++ */ ++ pattern = p_star; ++ s = ++s_star; ++ continue; + } + +- /* Move to the next character, both in string and in pattern. */ +- s++; ++ /* Case 4: Mismatch and no star to backtrack to */ ++ return 0; ++ } ++ ++ /* Handle trailing stars in the pattern ++ * (e.g., pattern "abc*" matching "abc") */ ++ while (*pattern == '*') { + pattern++; + } + +- /* NOTREACHED */ +- return 0; ++ /* If we reached the end of the pattern, it's a match */ ++ return (*pattern == '\0'); + } + + /* +@@ -182,7 +167,7 @@ int match_pattern_list(const char *string, const char *pattern, + sub[subi] = '\0'; + + /* Try to match the subpattern against the string. */ +- if (match_pattern(string, sub, MAX_MATCH_RECURSION)) { ++ if (match_pattern(string, sub)) { + if (negated) { + return -1; /* Negative */ + } else { +diff --git a/tests/unittests/torture_config.c b/tests/unittests/torture_config.c +index ada0ce8c..fcfe8fbc 100644 +--- a/tests/unittests/torture_config.c ++++ b/tests/unittests/torture_config.c +@@ -2342,80 +2342,138 @@ static void torture_config_match_pattern(void **state) + (void) state; + + /* Simple test "a" matches "a" */ +- rv = match_pattern("a", "a", MAX_MATCH_RECURSION); ++ rv = match_pattern("a", "a"); + assert_int_equal(rv, 1); + + /* Simple test "a" does not match "b" */ +- rv = match_pattern("a", "b", MAX_MATCH_RECURSION); ++ rv = match_pattern("a", "b"); + assert_int_equal(rv, 0); + + /* NULL arguments are correctly handled */ +- rv = match_pattern("a", NULL, MAX_MATCH_RECURSION); ++ rv = match_pattern("a", NULL); + assert_int_equal(rv, 0); +- rv = match_pattern(NULL, "a", MAX_MATCH_RECURSION); ++ rv = match_pattern(NULL, "a"); + assert_int_equal(rv, 0); + + /* Simple wildcard ? is handled in pattern */ +- rv = match_pattern("a", "?", MAX_MATCH_RECURSION); ++ rv = match_pattern("a", "?"); + assert_int_equal(rv, 1); +- rv = match_pattern("aa", "?", MAX_MATCH_RECURSION); ++ rv = match_pattern("aa", "?"); + assert_int_equal(rv, 0); + /* Wildcard in search string */ +- rv = match_pattern("?", "a", MAX_MATCH_RECURSION); ++ rv = match_pattern("?", "a"); + assert_int_equal(rv, 0); +- rv = match_pattern("?", "?", MAX_MATCH_RECURSION); ++ rv = match_pattern("?", "?"); + assert_int_equal(rv, 1); + + /* Simple wildcard * is handled in pattern */ +- rv = match_pattern("a", "*", MAX_MATCH_RECURSION); ++ rv = match_pattern("a", "*"); + assert_int_equal(rv, 1); +- rv = match_pattern("aa", "*", MAX_MATCH_RECURSION); ++ rv = match_pattern("aa", "*"); + assert_int_equal(rv, 1); + /* Wildcard in search string */ +- rv = match_pattern("*", "a", MAX_MATCH_RECURSION); ++ rv = match_pattern("*", "a"); + assert_int_equal(rv, 0); +- rv = match_pattern("*", "*", MAX_MATCH_RECURSION); ++ rv = match_pattern("*", "*"); + assert_int_equal(rv, 1); + + /* More complicated patterns */ +- rv = match_pattern("a", "*a", MAX_MATCH_RECURSION); ++ rv = match_pattern("a", "*a"); + assert_int_equal(rv, 1); +- rv = match_pattern("a", "a*", MAX_MATCH_RECURSION); ++ rv = match_pattern("a", "a*"); + assert_int_equal(rv, 1); +- rv = match_pattern("abababc", "*abc", MAX_MATCH_RECURSION); ++ rv = match_pattern("abababc", "*abc"); + assert_int_equal(rv, 1); +- rv = match_pattern("ababababca", "*abc", MAX_MATCH_RECURSION); ++ rv = match_pattern("ababababca", "*abc"); + assert_int_equal(rv, 0); +- rv = match_pattern("ababababca", "*abc*", MAX_MATCH_RECURSION); ++ rv = match_pattern("ababababca", "*abc*"); + assert_int_equal(rv, 1); + + /* Multiple wildcards in row */ +- rv = match_pattern("aa", "??", MAX_MATCH_RECURSION); ++ rv = match_pattern("aa", "??"); + assert_int_equal(rv, 1); +- rv = match_pattern("bba", "??a", MAX_MATCH_RECURSION); ++ rv = match_pattern("bba", "??a"); + assert_int_equal(rv, 1); +- rv = match_pattern("aaa", "**a", MAX_MATCH_RECURSION); ++ rv = match_pattern("aaa", "**a"); + assert_int_equal(rv, 1); +- rv = match_pattern("bbb", "**a", MAX_MATCH_RECURSION); ++ rv = match_pattern("bbb", "**a"); + assert_int_equal(rv, 0); + + /* Consecutive asterisks do not make sense and do not need to recurse */ +- rv = match_pattern("hostname", "**********pattern", 5); ++ rv = match_pattern("hostname", "**********pattern"); + assert_int_equal(rv, 0); +- rv = match_pattern("hostname", "pattern**********", 5); ++ rv = match_pattern("hostname", "pattern**********"); + assert_int_equal(rv, 0); +- rv = match_pattern("pattern", "***********pattern", 5); ++ rv = match_pattern("pattern", "***********pattern"); + assert_int_equal(rv, 1); +- rv = match_pattern("pattern", "pattern***********", 5); ++ rv = match_pattern("pattern", "pattern***********"); + assert_int_equal(rv, 1); + +- /* Limit the maximum recursion */ +- rv = match_pattern("hostname", "*p*a*t*t*e*r*n*", 5); ++ rv = match_pattern("hostname", "*p*a*t*t*e*r*n*"); + assert_int_equal(rv, 0); +- /* Too much recursion */ +- rv = match_pattern("pattern", "*p*a*t*t*e*r*n*", 5); ++ rv = match_pattern("pattern", "*p*a*t*t*e*r*n*"); ++ assert_int_equal(rv, 1); ++ ++ /* Regular Expression Denial of Service */ ++ rv = match_pattern("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", ++ "*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a"); ++ assert_int_equal(rv, 1); ++ rv = match_pattern("ababababababababababababababababababababab", ++ "*a*b*a*b*a*b*a*b*a*b*a*b*a*b*a*b"); ++ assert_int_equal(rv, 1); ++ ++ /* A lot of backtracking */ ++ rv = match_pattern("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaax", ++ "a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*a*ax"); ++ assert_int_equal(rv, 1); ++ ++ /* Test backtracking: *a matches first 'a', fails on 'b', must backtrack */ ++ rv = match_pattern("axaxaxb", "*a*b"); ++ assert_int_equal(rv, 1); ++ ++ /* Test greedy consumption with suffix */ ++ rv = match_pattern("foo_bar_baz_bar", "*bar"); ++ assert_int_equal(rv, 1); ++ ++ /* Test exact suffix requirement (ensure no partial match acceptance) */ ++ rv = match_pattern("foobar_extra", "*bar"); ++ assert_int_equal(rv, 0); ++ ++ /* Test multiple distinct wildcards */ ++ rv = match_pattern("a_very_long_string_with_a_pattern", "*long*pattern"); ++ assert_int_equal(rv, 1); ++ ++ /* ? inside a * sequence */ ++ rv = match_pattern("abcdefg", "a*c?e*g"); ++ assert_int_equal(rv, 1); ++ ++ /* Consecutive mixed wildcards */ ++ rv = match_pattern("abc", "*?c"); ++ assert_int_equal(rv, 1); ++ ++ /* ? at the very end after * */ ++ rv = match_pattern("abc", "ab?"); ++ assert_int_equal(rv, 1); ++ rv = match_pattern("abc", "ab*?"); ++ assert_int_equal(rv, 1); ++ ++ /* Consecutive stars should be collapsed or handled gracefully */ ++ rv = match_pattern("abc", "a**c"); ++ assert_int_equal(rv, 1); ++ rv = match_pattern("abc", "***"); ++ assert_int_equal(rv, 1); ++ ++ /* Empty string handling */ ++ rv = match_pattern("", "*"); ++ assert_int_equal(rv, 1); ++ rv = match_pattern("", "?"); + assert_int_equal(rv, 0); ++ rv = match_pattern("", ""); ++ assert_int_equal(rv, 1); + ++ /* Pattern longer than string */ ++ rv = match_pattern("short", "short_but_longer"); ++ assert_int_equal(rv, 0); + } + + /* Identity file can be specified multiple times in the configuration +-- +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 1d4fd637d9..193ff3512d 100644 --- a/meta-oe/recipes-support/libssh/libssh_0.11.3.bb +++ b/meta-oe/recipes-support/libssh/libssh_0.11.3.bb @@ -13,6 +13,7 @@ SRC_URI = "git://git.libssh.org/projects/libssh.git;protocol=https;branch=stable file://CVE-2026-3731_p2.patch \ file://CVE-2026-0968_p1.patch \ file://CVE-2026-0968_p2.patch \ + file://CVE-2026-0967.patch \ " SRC_URI:append:toolchain-clang = " file://0001-CompilerChecks.cmake-drop-Wunused-variable-flag.patch" From patchwork Mon Apr 6 11:55:35 2026 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "Deepak Rathore -X (deeratho - E INFOCHIPS PRIVATE LIMITED at Cisco)" X-Patchwork-Id: 85316 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 D8274EF4ED2 for ; Mon, 6 Apr 2026 11:56:05 +0000 (UTC) Received: from alln-iport-2.cisco.com (alln-iport-2.cisco.com [173.37.142.89]) by mx.groups.io with SMTP id smtpd.msgproc01-g2.53334.1775476558545718420 for ; Mon, 06 Apr 2026 04:55:58 -0700 Authentication-Results: mx.groups.io; dkim=fail reason="dkim: message contains an insecure body length tag" header.i=@cisco.com header.s=iport01 header.b=dDWUB3oa; spf=pass (domain: cisco.com, ip: 173.37.142.89, mailfrom: deeratho@cisco.com) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=cisco.com; i=@cisco.com; l=10073; q=dns/txt; s=iport01; t=1775476558; x=1776686158; h=from:to:subject:date:message-id:mime-version: content-transfer-encoding; bh=SvX1jIZzimyxNsKAbXB/9WbvK1kgPk6MpLqRKhzAuIw=; b=dDWUB3oaOXDXMeD39FJ2zG7sQWxg3v59NY4X59GkE9kdVYjwrhBp8mlO xYIP9kiQcqB3LSMKt6XC5DJ2/Xq2x9SS8YavJypy5yoIEo3ElOJxJJ6l1 jR3RCYQL9VzmDYiWayHi6YQpxaMFsJB7BvSheq8c46puN0Iiqv8V/UMR2 aNIf/HH0NDeczBzJsHCHh5VCP6UDd584S8FVuVcQf1vJaCj08hYsSNaqs ZsbMxRgcaqe0TsXr1ddZbcHYahRbxL0SNH5XZP4neW8xrylAAg87BACTo uC0f9oOmRouHfbwvGWk5foe+UCljaKlNRI1cPI4JMUF3H0sZHAB/Ec3U9 Q==; X-CSE-ConnectionGUID: +fA6xGM/TwOa7Fum/xXuLw== X-CSE-MsgGUID: SGL3ycyHQnyvLdOXQzSzMg== X-IPAS-Result: A0BFAgBtntNp/4//Ja1aglmCV3FfQkkDlCeCIYEWnQeBfw8BAQEPRA0EAQGSMwImNAkOAQIEAQEBAQMCAwEBAQEBAQEBAQEBCwEBBQEBAQIBBwWBDhOGTw2GWgE4ARgBWQMBAlojIYMCAYJzAgERthOCLIEBgygBMQUJAkNP2yYBCxQBgTiFPogcWxgBRIQ2JxsbgXKBFYJydoEFgVwEGIgKBIIigQ6BYYIHBoJNggCHX0iBHgNZLAFVEw0KCwcFgWYDNRIqFW4yHYEjPheBDBsHBYFLhkp0bYETg3dmAwsYDUgRLDcUGwQ+bgeLISeCNgF6EwErF3pULxgOAwySdB2SRoE1n1kKKIN0jB6VOhozhASUFYshhzGZBo4JlXNdhGiBaDyBWXAVgyIJSRkPji0LC4hxwG8jNQIJAzABBwIHDgKBc5F9AQE IronPort-Data: A9a23:riUUZaAob4/uVRVW/3niw5YqxClBgxIJ4kV8jS/XYbTApDIjhGBRm GJMWmuOMqrcN2H1fYonaYjloU4AucLcm9IwOVdlrnsFo1CmBibm6XV1Cm+qYkt+++WaFBoPA /02M4eGdIZvCCeA+n9BC5C5xVFkz6aEW7HgP+DNPyF1VGdMRTwo4f5Zs7ZRbrVA357jWGthh fuo+5eBYAP9hWYtWo4pw/vrRC1H7ayaVAww5jTSVdgT1HfCmn8cCo4oJK3ZBxPQXolOE+emc P3Ixbe/83mx109F5gSNy+uTnuUiG9Y+DCDW4pZkc/HKbitq+kTe5p0G2M80Mi+7vdkmc+dZk 72hvbToIesg0zaldO41C3G0GAkmVUFKFSOuzXWX6aSuI0P6n3TEgPtHUlEZGMok5cF+Xk9T8 LsIGC0pcUXW7w626OrTpuhEnM8vKozveYgYoHwllW+fBvc9SpeFSKLPjTNa9G5v3YYVQrCEO pdfMGY0BPjDS0Un1lM/BJ8zhu60hn7XeDxDo1XTrq0yi4TW5FMpj+C8aICIJrRmQ+1usWLDt 2nX313/Ew8+OPuv8Ci+4Fej07qncSTTHdh6+KeD3vlyjVuew2YeBBEbWR63rOe0jma6WslDM AoT4icooK04+UCnQ9W7WAe3yENopTYGUNZWVul/4waXx++MvUCSB3MPSXhKb9lOWNIKeAHGH 2Shx7vBbQGDepXMIZ5B3t94dQ+PBBU= IronPort-HdrOrdr: A9a23:Rh33OK05hBZDvL8GQ+kBgAqjBIIkLtp133Aq2lEZdPUzSL37qy nAppomPHPP5Qr5O0tQ+uxoRpPgfZq0z/cciuMs1NyZMzUO1lHFEGgb1+vf6gylPTHi/ehA0q olWa1/BNrsSWVet6/BkWyF+xJK+qjhzEhu7t2uq0tQcQ== X-Talos-CUID: 9a23:eVL1Hmk84I8EYM6kIEoh6UFnfA3XOT7T5SnADGr/NXlOQeK7VFrX6JN5t9U7zg== X-Talos-MUID: 9a23:1XMqDwweT81bAwSL+2uoYVucG/+aqJ+BLBscmoQmgs+VKwh8HAekng+lZ4Byfw== X-IronPort-Anti-Spam-Filtered: true X-IronPort-AV: E=Sophos;i="6.23,163,1770595200"; d="scan'208";a="707090254" Received: from rcdn-l-core-06.cisco.com ([173.37.255.143]) by alln-iport-2.cisco.com with ESMTP/TLS/TLS_AES_256_GCM_SHA384; 06 Apr 2026 11:55:57 +0000 Received: from sjc-ads-3552.cisco.com (sjc-ads-3552.cisco.com [171.68.249.250]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by rcdn-l-core-06.cisco.com (Postfix) with ESMTPS id 8416918000244 for ; Mon, 6 Apr 2026 11:55:57 +0000 (GMT) Received: by sjc-ads-3552.cisco.com (Postfix, from userid 1795984) id 30D53CC12B5; Mon, 6 Apr 2026 04:55:57 -0700 (PDT) From: "Deepak Rathore -X (deeratho - E INFOCHIPS PRIVATE LIMITED at Cisco)" To: openembedded-devel@lists.openembedded.org Subject: [oe][meta-oe][whinlatter][PATCH 3/3] libssh: Fix CVE-2026-0965 Date: Mon, 6 Apr 2026 04:55:35 -0700 Message-Id: <20260406115535.1242965-1-deeratho@cisco.com> X-Mailer: git-send-email 2.35.6 MIME-Version: 1.0 X-Outbound-Client-TLS: ANONYMOUS;sjc-ads-3552.cisco.com [171.68.249.250];TLSv1.3;TLS_AES_256_GCM_SHA384;256 X-Outbound-SMTP-Client: 171.68.249.250, sjc-ads-3552.cisco.com X-Outbound-Node: rcdn-l-core-06.cisco.com 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 ; Mon, 06 Apr 2026 11:56:05 -0000 X-Groupsio-URL: https://lists.openembedded.org/g/openembedded-devel/message/126037 From: Deepak Rathore Pick the patch [1] as mentioned in [2] [1] https://git.libssh.org/projects/libssh.git/commit/?id=bf390a042623e02abc8f421c4c5fadc0429a8a76 [2] https://security-tracker.debian.org/tracker/CVE-2026-0965 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 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=bf390a042623e02abc8f421c4c5fadc0429a8a76] + +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 = fopen(filename, "r"); ++ f = ssh_strict_fopen(filename, SSH_MAX_CONFIG_FILE_SIZE); + if (f == 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] = {0}; + +- f = fopen(filename, "r"); ++ f = ssh_strict_fopen(filename, SSH_MAX_CONFIG_FILE_SIZE); + if (f == 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 = fopen(filename, "r"); ++ f = ssh_strict_fopen(filename, SSH_MAX_CONFIG_FILE_SIZE); + if (f == 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 = 0; + +- f = fopen(filename, "r"); ++ f = ssh_strict_fopen(filename, SSH_MAX_CONFIG_FILE_SIZE); + if (f == 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 != NULL) +- moduli = fopen(moduli_file, "r"); ++ moduli = ssh_strict_fopen(moduli_file, SSH_MAX_CONFIG_FILE_SIZE); + else +- moduli = fopen(MODULI_FILE, "r"); ++ moduli = ssh_strict_fopen(MODULI_FILE, SSH_MAX_CONFIG_FILE_SIZE); + + if (moduli == NULL) { + char err_msg[SSH_ERRNO_MSG_MAX] = {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 = NULL; + + if (*file == NULL) { +- *file = fopen(filename,"r"); ++ *file = ssh_strict_fopen(filename, SSH_MAX_CONFIG_FILE_SIZE); + if (*file == 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 = NULL; + int rc; + +- fp = fopen(filename, "r"); ++ fp = ssh_strict_fopen(filename, SSH_MAX_CONFIG_FILE_SIZE); + if (fp == NULL) { + char err_msg[SSH_ERRNO_MSG_MAX] = {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 != NULL && t[0] == '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 = NULL; ++ struct stat sb; ++ char err_msg[SSH_ERRNO_MSG_MAX] = {0}; ++ int r, fd; ++ ++ /* open first to avoid TOCTOU */ ++ fd = open(filename, O_RDONLY); ++ if (fd == -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 = fstat(fd, &sb); ++ if (r != 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) != 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 = fdopen(fd, "r"); ++ if (f == 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 = *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 = "git://git.libssh.org/projects/libssh.git;protocol=https;branch=stable 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 = " file://0001-CompilerChecks.cmake-drop-Wunused-variable-flag.patch"