Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
150 changes: 150 additions & 0 deletions SPECS/rpm-ostree/CVE-2026-33056.patch
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
From 41625e3304b0564ed10579c908ca1375fcf9656b Mon Sep 17 00:00:00 2001
From: Alex Crichton <alex@alexcrichton.com>
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 <https://github.com/xokdvium>
Assisted-by: OpenCode (Claude claude-opus-4-5)

Signed-off-by: Colin Walters <walters@verbum.org>
Co-authored-by: Colin Walters <walters@verbum.org>
Signed-off-by: Azure Linux Security Servicing Account <azurelinux-security@microsoft.com>
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<R: Read> Archive<R> {
/// 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

6 changes: 5 additions & 1 deletion SPECS/rpm-ostree/rpm-ostree.spec
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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
Expand Down Expand Up @@ -180,6 +181,9 @@ make check
%{_datadir}/gir-1.0/*-1.0.gir

%changelog
* Thu Mar 26 2026 Azure Linux Security Servicing Account <azurelinux-security@microsoft.com> - 2024.4-9
- Patch for CVE-2026-33056

* Tue Feb 17 2026 Azure Linux Security Servicing Account <azurelinux-security@microsoft.com> - 2024.4-8
- Patch for CVE-2026-25541 & CVE-2025-58160

Expand Down
Loading