From 2a22cfde645a9637dac0034f3cfb03480d485ff6 Mon Sep 17 00:00:00 2001 From: Azure Linux Security Servicing Account Date: Thu, 26 Mar 2026 08:03:28 +0000 Subject: [PATCH] Patch rpm-ostree for CVE-2026-33056 --- SPECS/rpm-ostree/CVE-2026-33056.patch | 150 ++++++++++++++++++++++++++ SPECS/rpm-ostree/rpm-ostree.spec | 6 +- 2 files changed, 155 insertions(+), 1 deletion(-) create mode 100644 SPECS/rpm-ostree/CVE-2026-33056.patch diff --git a/SPECS/rpm-ostree/CVE-2026-33056.patch b/SPECS/rpm-ostree/CVE-2026-33056.patch new file mode 100644 index 00000000000..3622b6c4921 --- /dev/null +++ b/SPECS/rpm-ostree/CVE-2026-33056.patch @@ -0,0 +1,150 @@ +From 41625e3304b0564ed10579c908ca1375fcf9656b Mon Sep 17 00:00:00 2001 +From: Alex Crichton +Date: Thu, 19 Mar 2026 16:58:05 -0500 +Subject: [PATCH] archive: Prevent symlink-directory collision chmod attack + (#442) + +When unpacking a tarball containing a symlink followed by a directory +entry with the same path, unpack_dir previously used fs::metadata() +which follows symlinks. This allowed an attacker to modify permissions +on arbitrary directories outside the extraction path. + +The fix uses fs::symlink_metadata() to detect symlinks and refuse to +treat them as valid existing directories. + +Document more exhaustively+consistently security caveats. + +Reported-by: Sergei Zimmerman +Assisted-by: OpenCode (Claude claude-opus-4-5) + +Signed-off-by: Colin Walters +Co-authored-by: Colin Walters +Signed-off-by: Azure Linux Security Servicing Account +Upstream-reference: https://github.com/alexcrichton/tar-rs/commit/17b1fd84e632071cb8eef9d3709bf347bd266446.patch +--- + vendor/tar/src/archive.rs | 18 ++++++++++--- + vendor/tar/src/entry.rs | 7 ++--- + vendor/tar/tests/entry.rs | 56 +++++++++++++++++++++++++++++++++++++++ + 3 files changed, 75 insertions(+), 6 deletions(-) + +diff --git a/vendor/tar/src/archive.rs b/vendor/tar/src/archive.rs +index 1bed512..056eb71 100644 +--- a/vendor/tar/src/archive.rs ++++ b/vendor/tar/src/archive.rs +@@ -88,9 +88,21 @@ impl Archive { + /// extracting each file in turn to the location specified by the entry's + /// path name. + /// +- /// This operation is relatively sensitive in that it will not write files +- /// outside of the path specified by `dst`. Files in the archive which have +- /// a '..' in their path are skipped during the unpacking process. ++ /// # Security ++ /// ++ /// A best-effort is made to prevent writing files outside `dst` (paths ++ /// containing `..` are skipped, symlinks are validated). However, there ++ /// have been historical bugs in this area, and more may exist. For this ++ /// reason, when processing untrusted archives, stronger sandboxing is ++ /// encouraged: e.g. the [`cap-std`] crate and/or OS-level ++ /// containerization/virtualization. ++ /// ++ /// If `dst` does not exist, it is created. Unpacking into an existing ++ /// directory merges content. This function assumes `dst` is not ++ /// concurrently modified by untrusted processes. Protecting against ++ /// TOCTOU races is out of scope for this crate. ++ /// ++ /// [`cap-std`]: https://docs.rs/cap-std/ + /// + /// # Examples + /// +diff --git a/vendor/tar/src/entry.rs b/vendor/tar/src/entry.rs +index 8f0b62a..287d689 100644 +--- a/vendor/tar/src/entry.rs ++++ b/vendor/tar/src/entry.rs +@@ -210,8 +210,9 @@ impl<'a, R: Read> Entry<'a, R> { + /// also be propagated to the path `dst`. Any existing file at the location + /// `dst` will be overwritten. + /// +- /// This function carefully avoids writing outside of `dst`. If the file has +- /// a '..' in its path, this function will skip it and return false. ++ /// # Security ++ /// ++ /// See [`Archive::unpack`]. + /// + /// # Examples + /// +@@ -430,7 +431,7 @@ impl<'a> EntryFields<'a> { + // If the directory already exists just let it slide + fs::create_dir(dst).or_else(|err| { + if err.kind() == ErrorKind::AlreadyExists { +- let prev = fs::metadata(dst); ++ let prev = fs::symlink_metadata(dst); + if prev.map(|m| m.is_dir()).unwrap_or(false) { + return Ok(()); + } +diff --git a/vendor/tar/tests/entry.rs b/vendor/tar/tests/entry.rs +index fa8eeae..5e874fa 100644 +--- a/vendor/tar/tests/entry.rs ++++ b/vendor/tar/tests/entry.rs +@@ -377,3 +377,59 @@ fn modify_symlink_just_created() { + t!(t!(File::open(&test)).read_to_end(&mut contents)); + assert_eq!(contents.len(), 0); + } ++ ++/// Test that unpacking a tarball with a symlink followed by a directory entry ++/// with the same name does not allow modifying permissions of arbitrary directories ++/// outside the extraction path. ++#[test] ++#[cfg(unix)] ++fn symlink_dir_collision_does_not_modify_external_dir_permissions() { ++ use ::std::fs; ++ use ::std::os::unix::fs::PermissionsExt; ++ ++ let td = Builder::new().prefix("tar").tempdir().unwrap(); ++ ++ let target_dir = td.path().join("target-dir"); ++ fs::create_dir(&target_dir).unwrap(); ++ fs::set_permissions(&target_dir, fs::Permissions::from_mode(0o700)).unwrap(); ++ let before_mode = fs::metadata(&target_dir).unwrap().permissions().mode() & 0o7777; ++ assert_eq!(before_mode, 0o700); ++ ++ let extract_dir = td.path().join("extract-dir"); ++ fs::create_dir(&extract_dir).unwrap(); ++ ++ let mut ar = tar::Builder::new(Vec::new()); ++ ++ let mut header = tar::Header::new_gnu(); ++ header.set_size(0); ++ header.set_entry_type(tar::EntryType::Symlink); ++ header.set_path("foo").unwrap(); ++ header.set_link_name(&target_dir).unwrap(); ++ header.set_mode(0o777); ++ header.set_cksum(); ++ ar.append(&header, &[][..]).unwrap(); ++ ++ let mut header = tar::Header::new_gnu(); ++ header.set_size(0); ++ header.set_entry_type(tar::EntryType::Directory); ++ header.set_path("foo").unwrap(); ++ header.set_mode(0o777); ++ header.set_cksum(); ++ ar.append(&header, &[][..]).unwrap(); ++ ++ let bytes = ar.into_inner().unwrap(); ++ let mut ar = tar::Archive::new(&bytes[..]); ++ ++ let result = ar.unpack(&extract_dir); ++ assert!(result.is_err()); ++ ++ let symlink_path = extract_dir.join("foo"); ++ assert!(symlink_path ++ .symlink_metadata() ++ .unwrap() ++ .file_type() ++ .is_symlink()); ++ ++ let after_mode = fs::metadata(&target_dir).unwrap().permissions().mode() & 0o7777; ++ assert_eq!(after_mode, 0o700); ++} +-- +2.45.4 + diff --git a/SPECS/rpm-ostree/rpm-ostree.spec b/SPECS/rpm-ostree/rpm-ostree.spec index 856c776c648..9f6de654398 100644 --- a/SPECS/rpm-ostree/rpm-ostree.spec +++ b/SPECS/rpm-ostree/rpm-ostree.spec @@ -1,7 +1,7 @@ Summary: Commit RPMs to an OSTree repository Name: rpm-ostree Version: 2024.4 -Release: 8%{?dist} +Release: 9%{?dist} License: LGPLv2+ Vendor: Microsoft Corporation Distribution: Azure Linux @@ -12,6 +12,7 @@ Patch1: rpm-ostree-libdnf-build.patch Patch2: CVE-2024-2905.patch Patch3: CVE-2026-25541.patch Patch4: CVE-2025-58160.patch +Patch5: CVE-2026-33056.patch BuildRequires: attr-devel BuildRequires: autoconf @@ -180,6 +181,9 @@ make check %{_datadir}/gir-1.0/*-1.0.gir %changelog +* Thu Mar 26 2026 Azure Linux Security Servicing Account - 2024.4-9 +- Patch for CVE-2026-33056 + * Tue Feb 17 2026 Azure Linux Security Servicing Account - 2024.4-8 - Patch for CVE-2026-25541 & CVE-2025-58160