Blame SOURCES/libarchive-3.3.3-CVE-2021-31566.patch

d6b572
From b837c72c423b744a2e6c554742877173406dbfa0 Mon Sep 17 00:00:00 2001
d6b572
From: Martin Matuska <martin@matuska.org>
d6b572
Date: Sat, 25 May 2019 23:46:59 +0200
d6b572
Subject: [PATCH] archive_write_disk_posix: open a fd when processing fixup
d6b572
 entries
d6b572
d6b572
---
d6b572
 libarchive/archive_write_disk_posix.c | 25 ++++++++++++++++++++-----
d6b572
 1 file changed, 20 insertions(+), 5 deletions(-)
d6b572
d6b572
diff --git a/libarchive/archive_write_disk_posix.c b/libarchive/archive_write_disk_posix.c
d6b572
index 70b27b50..0583fbd1 100644
d6b572
--- a/libarchive/archive_write_disk_posix.c
d6b572
+++ b/libarchive/archive_write_disk_posix.c
d6b572
@@ -182,6 +182,7 @@ struct fixup_entry {
d6b572
 	void			*mac_metadata;
d6b572
 	int			 fixup; /* bitmask of what needs fixing */
d6b572
 	char			*name;
d6b572
+	int			 fd;
d6b572
 };
d6b572
 
d6b572
 /*
d6b572
@@ -2354,20 +2355,31 @@ _archive_write_disk_close(struct archive *_a)
d6b572
 
d6b572
 	while (p != NULL) {
d6b572
 		a->pst = NULL; /* Mark stat cache as out-of-date. */
d6b572
+		if (p->fd < 0 && p->fixup &
d6b572
+		    (TODO_TIMES | TODO_MODE_BASE | TODO_ACLS | TODO_FFLAGS)) {
d6b572
+			p->fd = open(p->name,
d6b572
+			    O_WRONLY | O_BINARY | O_NOFOLLOW | O_CLOEXEC);
d6b572
+		}
d6b572
 		if (p->fixup & TODO_TIMES) {
d6b572
-			set_times(a, -1, p->mode, p->name,
d6b572
+			set_times(a, p->fd, p->mode, p->name,
d6b572
 			    p->atime, p->atime_nanos,
d6b572
 			    p->birthtime, p->birthtime_nanos,
d6b572
 			    p->mtime, p->mtime_nanos,
d6b572
 			    p->ctime, p->ctime_nanos);
d6b572
 		}
d6b572
-		if (p->fixup & TODO_MODE_BASE)
d6b572
+		if (p->fixup & TODO_MODE_BASE) {
d6b572
+#ifdef HAVE_FCHMOD
d6b572
+			if (p->fd >= 0)
d6b572
+				fchmod(p->fd, p->mode);
d6b572
+			else
d6b572
+#endif
d6b572
 			chmod(p->name, p->mode);
d6b572
+		}
d6b572
 		if (p->fixup & TODO_ACLS)
d6b572
-			archive_write_disk_set_acls(&a->archive, -1, p->name,
d6b572
-			    &p->acl, p->mode);
d6b572
+			archive_write_disk_set_acls(&a->archive, p->fd,
d6b572
+			    p->name, &p->acl, p->mode);
d6b572
 		if (p->fixup & TODO_FFLAGS)
d6b572
-			set_fflags_platform(a, -1, p->name,
d6b572
+			set_fflags_platform(a, p->fd, p->name,
d6b572
 			    p->mode, p->fflags_set, 0);
d6b572
 		if (p->fixup & TODO_MAC_METADATA)
d6b572
 			set_mac_metadata(a, p->name, p->mac_metadata,
d6b572
@@ -2376,6 +2388,8 @@ _archive_write_disk_close(struct archive *_a)
d6b572
 		archive_acl_clear(&p->acl);
d6b572
 		free(p->mac_metadata);
d6b572
 		free(p->name);
d6b572
+		if (p->fd >= 0)
d6b572
+			close(p->fd);	
d6b572
 		free(p);
d6b572
 		p = next;
d6b572
 	}
d6b572
@@ -2510,6 +2524,7 @@ new_fixup(struct archive_write_disk *a, const char *pathname)
d6b572
 	a->fixup_list = fe;
d6b572
 	fe->fixup = 0;
d6b572
 	fe->name = strdup(pathname);
d6b572
+	fe->fd = -1;
d6b572
 	return (fe);
d6b572
 }
d6b572
 
d6b572
-- 
d6b572
2.31.1
d6b572
d6b572
From 6d5204058ed51e11588a438737e9033305cfd248 Mon Sep 17 00:00:00 2001
d6b572
From: Martin Matuska <martin@matuska.org>
d6b572
Date: Thu, 6 Jun 2019 15:12:11 +0200
d6b572
Subject: [PATCH] archive_write_disk_posix changes - private file descriptor in
d6b572
 _archive_write_disk_close() - use la_opendirat() in edit_deep_directories()
d6b572
d6b572
---
d6b572
 libarchive/archive_write_disk_posix.c | 25 ++++++++++++-------------
d6b572
 1 file changed, 12 insertions(+), 13 deletions(-)
d6b572
d6b572
diff --git a/libarchive/archive_write_disk_posix.c b/libarchive/archive_write_disk_posix.c
d6b572
index 89941c64..b1a0bb38 100644
d6b572
--- a/libarchive/archive_write_disk_posix.c
d6b572
+++ b/libarchive/archive_write_disk_posix.c
d6b572
@@ -186,7 +186,6 @@ struct fixup_entry {
d6b572
 	void			*mac_metadata;
d6b572
 	int			 fixup; /* bitmask of what needs fixing */
d6b572
 	char			*name;
d6b572
-	int			 fd;
d6b572
 };
d6b572
 
d6b572
 /*
d6b572
@@ -1947,7 +1946,7 @@ edit_deep_directories(struct archive_write_disk *a)
d6b572
 		return;
d6b572
 
d6b572
 	/* Try to record our starting dir. */
d6b572
-	a->restore_pwd = open(".", O_RDONLY | O_BINARY | O_CLOEXEC);
d6b572
+	a->restore_pwd = open(".", O_RDONLY | O_BINARY | O_CLOEXEC | O_DIRECTORY);
d6b572
 	__archive_ensure_cloexec_flag(a->restore_pwd);
d6b572
 	if (a->restore_pwd < 0)
d6b572
 		return;
d6b572
@@ -2380,7 +2379,7 @@ _archive_write_disk_close(struct archive *_a)
d6b572
 {
d6b572
 	struct archive_write_disk *a = (struct archive_write_disk *)_a;
d6b572
 	struct fixup_entry *next, *p;
d6b572
-	int ret;
d6b572
+	int fd, ret;
d6b572
 
d6b572
 	archive_check_magic(&a->archive, ARCHIVE_WRITE_DISK_MAGIC,
d6b572
 	    ARCHIVE_STATE_HEADER | ARCHIVE_STATE_DATA,
d6b572
@@ -2391,14 +2390,15 @@ _archive_write_disk_close(struct archive *_a)
d6b572
 	p = sort_dir_list(a->fixup_list);
d6b572
 
d6b572
 	while (p != NULL) {
d6b572
+		fd = -1;
d6b572
 		a->pst = NULL; /* Mark stat cache as out-of-date. */
d6b572
-		if (p->fd < 0 && p->fixup &
d6b572
+		if (p->fixup &
d6b572
 		    (TODO_TIMES | TODO_MODE_BASE | TODO_ACLS | TODO_FFLAGS)) {
d6b572
-			p->fd = open(p->name,
d6b572
+			fd = open(p->name,
d6b572
 			    O_WRONLY | O_BINARY | O_NOFOLLOW | O_CLOEXEC);
d6b572
 		}
d6b572
 		if (p->fixup & TODO_TIMES) {
d6b572
-			set_times(a, p->fd, p->mode, p->name,
d6b572
+			set_times(a, fd, p->mode, p->name,
d6b572
 			    p->atime, p->atime_nanos,
d6b572
 			    p->birthtime, p->birthtime_nanos,
d6b572
 			    p->mtime, p->mtime_nanos,
d6b572
@@ -2406,17 +2406,17 @@ _archive_write_disk_close(struct archive *_a)
d6b572
 		}
d6b572
 		if (p->fixup & TODO_MODE_BASE) {
d6b572
 #ifdef HAVE_FCHMOD
d6b572
-			if (p->fd >= 0)
d6b572
-				fchmod(p->fd, p->mode);
d6b572
+			if (fd >= 0)
d6b572
+				fchmod(fd, p->mode);
d6b572
 			else
d6b572
 #endif
d6b572
 			chmod(p->name, p->mode);
d6b572
 		}
d6b572
 		if (p->fixup & TODO_ACLS)
d6b572
-			archive_write_disk_set_acls(&a->archive, p->fd,
d6b572
+			archive_write_disk_set_acls(&a->archive, fd,
d6b572
 			    p->name, &p->acl, p->mode);
d6b572
 		if (p->fixup & TODO_FFLAGS)
d6b572
-			set_fflags_platform(a, p->fd, p->name,
d6b572
+			set_fflags_platform(a, fd, p->name,
d6b572
 			    p->mode, p->fflags_set, 0);
d6b572
 		if (p->fixup & TODO_MAC_METADATA)
d6b572
 			set_mac_metadata(a, p->name, p->mac_metadata,
d6b572
@@ -2425,8 +2425,8 @@ _archive_write_disk_close(struct archive *_a)
d6b572
 		archive_acl_clear(&p->acl);
d6b572
 		free(p->mac_metadata);
d6b572
 		free(p->name);
d6b572
-		if (p->fd >= 0)
d6b572
-			close(p->fd);	
d6b572
+		if (fd >= 0)
d6b572
+			close(fd);
d6b572
 		free(p);
d6b572
 		p = next;
d6b572
 	}
d6b572
@@ -2561,7 +2561,6 @@ new_fixup(struct archive_write_disk *a, const char *pathname)
d6b572
 	a->fixup_list = fe;
d6b572
 	fe->fixup = 0;
d6b572
 	fe->name = strdup(pathname);
d6b572
-	fe->fd = -1;
d6b572
 	return (fe);
d6b572
 }
d6b572
 
d6b572
-- 
d6b572
2.31.1
d6b572
d6b572
From e2ad1a2c3064fa9eba6274b3641c4c1beed25c0b Mon Sep 17 00:00:00 2001
d6b572
From: Martin Matuska <martin@matuska.org>
d6b572
Date: Sun, 22 Aug 2021 03:53:28 +0200
d6b572
Subject: [PATCH] Never follow symlinks when setting file flags on Linux
d6b572
d6b572
When opening a file descriptor to set file flags on linux, ensure
d6b572
no symbolic links are followed. This fixes the case when an archive
d6b572
contains a directory entry followed by a symlink entry with the same
d6b572
path. The fixup code would modify file flags of the symlink target.
d6b572
---
d6b572
 libarchive/archive_write_disk_posix.c | 3 ++-
d6b572
 1 file changed, 2 insertions(+), 1 deletion(-)
d6b572
d6b572
diff --git a/libarchive/archive_write_disk_posix.c b/libarchive/archive_write_disk_posix.c
d6b572
index ba4e65df..8474617e 100644
d6b572
--- a/libarchive/archive_write_disk_posix.c
d6b572
+++ b/libarchive/archive_write_disk_posix.c
d6b572
@@ -3927,7 +3927,8 @@ set_fflags_platform(struct archive_write_disk *a, int fd, const char *name,
d6b572
 
d6b572
 	/* If we weren't given an fd, open it ourselves. */
d6b572
 	if (myfd < 0) {
d6b572
-		myfd = open(name, O_RDONLY | O_NONBLOCK | O_BINARY | O_CLOEXEC);
d6b572
+		myfd = open(name, O_RDONLY | O_NONBLOCK | O_BINARY |
d6b572
+		    O_CLOEXEC | O_NOFOLLOW);
d6b572
 		__archive_ensure_cloexec_flag(myfd);
d6b572
 	}
d6b572
 	if (myfd < 0)
d6b572
-- 
d6b572
2.31.1
d6b572
d6b572
From b41daecb5ccb4c8e3b2c53fd6147109fc12c3043 Mon Sep 17 00:00:00 2001
d6b572
From: Martin Matuska <martin@matuska.org>
d6b572
Date: Fri, 20 Aug 2021 01:50:27 +0200
d6b572
Subject: [PATCH] Do not follow symlinks when processing the fixup list
d6b572
d6b572
Use lchmod() instead of chmod() and tell the remaining functions that the
d6b572
real file to be modified is a symbolic link.
d6b572
d6b572
Fixes #1566
d6b572
---
d6b572
 Makefile.am                             |  1 +
d6b572
 libarchive/archive_write_disk_posix.c   | 24 +++++++-
d6b572
 libarchive/test/CMakeLists.txt          |  1 +
d6b572
 libarchive/test/test_write_disk_fixup.c | 77 +++++++++++++++++++++++++
d6b572
 4 files changed, 102 insertions(+), 1 deletion(-)
d6b572
 create mode 100644 libarchive/test/test_write_disk_fixup.c
d6b572
d6b572
diff --git a/Makefile.am b/Makefile.am
d6b572
index 58edb74e..c93a82e9 100644
d6b572
--- a/Makefile.am
d6b572
+++ b/Makefile.am
d6b572
@@ -560,6 +560,7 @@ libarchive_test_SOURCES= \
d6b572
 	libarchive/test/test_write_disk.c \
d6b572
 	libarchive/test/test_write_disk_appledouble.c \
d6b572
 	libarchive/test/test_write_disk_failures.c \
d6b572
+	libarchive/test/test_write_disk_fixup.c \
d6b572
 	libarchive/test/test_write_disk_hardlink.c \
d6b572
 	libarchive/test/test_write_disk_hfs_compression.c \
d6b572
 	libarchive/test/test_write_disk_lookup.c \
d6b572
diff --git a/libarchive/archive_write_disk_posix.c b/libarchive/archive_write_disk_posix.c
d6b572
index 8474617e..fcd733af 100644
d6b572
--- a/libarchive/archive_write_disk_posix.c
d6b572
+++ b/libarchive/archive_write_disk_posix.c
d6b572
@@ -2461,6 +2461,7 @@ _archive_write_disk_close(struct archive *_a)
d6b572
 {
d6b572
 	struct archive_write_disk *a = (struct archive_write_disk *)_a;
d6b572
 	struct fixup_entry *next, *p;
d6b572
+	struct stat st;
d6b572
 	int fd, ret;
d6b572
 
d6b572
 	archive_check_magic(&a->archive, ARCHIVE_WRITE_DISK_MAGIC,
d6b572
@@ -2478,6 +2479,20 @@ _archive_write_disk_close(struct archive *_a)
d6b572
 		    (TODO_TIMES | TODO_MODE_BASE | TODO_ACLS | TODO_FFLAGS)) {
d6b572
 			fd = open(p->name,
d6b572
 			    O_WRONLY | O_BINARY | O_NOFOLLOW | O_CLOEXEC);
d6b572
+			if (fd == -1) {
d6b572
+				/* If we cannot lstat, skip entry */
d6b572
+				if (lstat(p->name, &st) != 0)
d6b572
+					goto skip_fixup_entry;
d6b572
+				/*
d6b572
+				 * If we deal with a symbolic link, mark
d6b572
+				 * it in the fixup mode to ensure no
d6b572
+				 * modifications are made to its target.
d6b572
+				 */
d6b572
+				if (S_ISLNK(st.st_mode)) {
d6b572
+					p->mode &= ~S_IFMT;
d6b572
+					p->mode |= S_IFLNK;
d6b572
+				}
d6b572
+			}
d6b572
 		}
d6b572
 		if (p->fixup & TODO_TIMES) {
d6b572
 			set_times(a, fd, p->mode, p->name,
d6b572
@@ -2492,7 +2507,12 @@ _archive_write_disk_close(struct archive *_a)
d6b572
 				fchmod(fd, p->mode);
d6b572
 			else
d6b572
 #endif
d6b572
-			chmod(p->name, p->mode);
d6b572
+#ifdef HAVE_LCHMOD
d6b572
+			lchmod(p->name, p->mode);
d6b572
+#else
d6b572
+			if (!S_ISLNK(p->mode))
d6b572
+				chmod(p->name, p->mode);
d6b572
+#endif
d6b572
 		}
d6b572
 		if (p->fixup & TODO_ACLS)
d6b572
 			archive_write_disk_set_acls(&a->archive, fd,
d6b572
@@ -2503,6 +2523,7 @@ _archive_write_disk_close(struct archive *_a)
d6b572
 		if (p->fixup & TODO_MAC_METADATA)
d6b572
 			set_mac_metadata(a, p->name, p->mac_metadata,
d6b572
 					 p->mac_metadata_size);
d6b572
+skip_fixup_entry:
d6b572
 		next = p->next;
d6b572
 		archive_acl_clear(&p->acl);
d6b572
 		free(p->mac_metadata);
d6b572
@@ -2643,6 +2664,7 @@ new_fixup(struct archive_write_disk *a, const char *pathname)
d6b572
 	fe->next = a->fixup_list;
d6b572
 	a->fixup_list = fe;
d6b572
 	fe->fixup = 0;
d6b572
+	fe->mode = 0;
d6b572
 	fe->name = strdup(pathname);
d6b572
 	return (fe);
d6b572
 }
d6b572
diff --git a/libarchive/test/CMakeLists.txt b/libarchive/test/CMakeLists.txt
d6b572
index b26f679c..53cc3e22 100644
d6b572
--- a/libarchive/test/CMakeLists.txt
d6b572
+++ b/libarchive/test/CMakeLists.txt
d6b572
@@ -209,6 +209,7 @@ IF(ENABLE_TEST)
d6b572
     test_write_disk.c
d6b572
     test_write_disk_appledouble.c
d6b572
     test_write_disk_failures.c
d6b572
+    test_write_disk_fixup.c
d6b572
     test_write_disk_hardlink.c
d6b572
     test_write_disk_hfs_compression.c
d6b572
     test_write_disk_lookup.c
d6b572
diff --git a/libarchive/test/test_write_disk_fixup.c b/libarchive/test/test_write_disk_fixup.c
d6b572
new file mode 100644
d6b572
index 00000000..153cc3a9
d6b572
--- /dev/null
d6b572
+++ b/libarchive/test/test_write_disk_fixup.c
d6b572
@@ -0,0 +1,77 @@
d6b572
+/*-
d6b572
+ * Copyright (c) 2021 Martin Matuska
d6b572
+ * All rights reserved.
d6b572
+ *
d6b572
+ * Redistribution and use in source and binary forms, with or without
d6b572
+ * modification, are permitted provided that the following conditions
d6b572
+ * are met:
d6b572
+ * 1. Redistributions of source code must retain the above copyright
d6b572
+ *    notice, this list of conditions and the following disclaimer.
d6b572
+ * 2. Redistributions in binary form must reproduce the above copyright
d6b572
+ *    notice, this list of conditions and the following disclaimer in the
d6b572
+ *    documentation and/or other materials provided with the distribution.
d6b572
+ *
d6b572
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
d6b572
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
d6b572
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
d6b572
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
d6b572
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
d6b572
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
d6b572
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
d6b572
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
d6b572
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
d6b572
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
d6b572
+ */
d6b572
+#include "test.h"
d6b572
+
d6b572
+/*
d6b572
+ * Test fixup entries don't follow symlinks
d6b572
+ */
d6b572
+DEFINE_TEST(test_write_disk_fixup)
d6b572
+{
d6b572
+	struct archive *ad;
d6b572
+	struct archive_entry *ae;
d6b572
+	int r;
d6b572
+
d6b572
+	if (!canSymlink()) {
d6b572
+		skipping("Symlinks not supported");
d6b572
+		return;
d6b572
+	}
d6b572
+
d6b572
+	/* Write entries to disk. */
d6b572
+	assert((ad = archive_write_disk_new()) != NULL);
d6b572
+
d6b572
+	/*
d6b572
+	 * Create a file
d6b572
+	 */
d6b572
+	assertMakeFile("victim", 0600, "a");
d6b572
+
d6b572
+	/*
d6b572
+	 * Create a directory and a symlink with the same name
d6b572
+	 */
d6b572
+
d6b572
+	/* Directory: dir */
d6b572
+        assert((ae = archive_entry_new()) != NULL);
d6b572
+        archive_entry_copy_pathname(ae, "dir");
d6b572
+        archive_entry_set_mode(ae, AE_IFDIR | 0606);
d6b572
+	assertEqualIntA(ad, 0, archive_write_header(ad, ae));
d6b572
+	assertEqualIntA(ad, 0, archive_write_finish_entry(ad));
d6b572
+        archive_entry_free(ae);
d6b572
+
d6b572
+	/* Symbolic Link: dir -> foo */
d6b572
+	assert((ae = archive_entry_new()) != NULL);
d6b572
+	archive_entry_copy_pathname(ae, "dir");
d6b572
+	archive_entry_set_mode(ae, AE_IFLNK | 0777);
d6b572
+	archive_entry_set_size(ae, 0);
d6b572
+	archive_entry_copy_symlink(ae, "victim");
d6b572
+	assertEqualIntA(ad, 0, r = archive_write_header(ad, ae));
d6b572
+	if (r >= ARCHIVE_WARN)
d6b572
+		assertEqualIntA(ad, 0, archive_write_finish_entry(ad));
d6b572
+	archive_entry_free(ae);
d6b572
+
d6b572
+	assertEqualInt(ARCHIVE_OK, archive_write_free(ad));
d6b572
+
d6b572
+	/* Test the entries on disk. */
d6b572
+	assertIsSymlink("dir", "victim");
d6b572
+	assertFileMode("victim", 0600);
d6b572
+}
d6b572
-- 
d6b572
2.31.1
d6b572
d6b572
From 8a1bd5c18e896f0411a991240ce0d772bb02c840 Mon Sep 17 00:00:00 2001
d6b572
From: Martin Matuska <martin@matuska.org>
d6b572
Date: Fri, 27 Aug 2021 10:56:28 +0200
d6b572
Subject: [PATCH] Fix following symlinks when processing the fixup list
d6b572
d6b572
The previous fix in b41daecb5 was incomplete. Fixup entries are
d6b572
given the original path without calling cleanup_pathname().
d6b572
To make sure we don't follow a symlink, we must strip trailing
d6b572
slashes from the path.
d6b572
d6b572
The fixup entries are always directories. Make sure we try to modify
d6b572
only directories by providing O_DIRECTORY to open() (if supported)
d6b572
and if it fails to check directory via lstat().
d6b572
d6b572
Fixes #1566
d6b572
---
d6b572
 libarchive/archive_write_disk_posix.c   | 62 +++++++++++++++++--------
d6b572
 libarchive/test/test_write_disk_fixup.c | 44 ++++++++++++++----
d6b572
 2 files changed, 78 insertions(+), 28 deletions(-)
d6b572
d6b572
diff --git a/libarchive/archive_write_disk_posix.c b/libarchive/archive_write_disk_posix.c
d6b572
index fcd733af..aadc5871 100644
d6b572
--- a/libarchive/archive_write_disk_posix.c
d6b572
+++ b/libarchive/archive_write_disk_posix.c
d6b572
@@ -2462,6 +2462,7 @@ _archive_write_disk_close(struct archive *_a)
d6b572
 	struct archive_write_disk *a = (struct archive_write_disk *)_a;
d6b572
 	struct fixup_entry *next, *p;
d6b572
 	struct stat st;
d6b572
+	char *c;
d6b572
 	int fd, ret;
d6b572
 
d6b572
 	archive_check_magic(&a->archive, ARCHIVE_WRITE_DISK_MAGIC,
d6b572
@@ -2475,24 +2476,49 @@ _archive_write_disk_close(struct archive *_a)
d6b572
 	while (p != NULL) {
d6b572
 		fd = -1;
d6b572
 		a->pst = NULL; /* Mark stat cache as out-of-date. */
d6b572
-		if (p->fixup &
d6b572
-		    (TODO_TIMES | TODO_MODE_BASE | TODO_ACLS | TODO_FFLAGS)) {
d6b572
-			fd = open(p->name,
d6b572
-			    O_WRONLY | O_BINARY | O_NOFOLLOW | O_CLOEXEC);
d6b572
+
d6b572
+		/* We must strip trailing slashes from the path to avoid
d6b572
+		   dereferencing symbolic links to directories */
d6b572
+		c = p->name;
d6b572
+		while (*c != '\0')
d6b572
+			c++;
d6b572
+		while (c != p->name && *(c - 1) == '/') {
d6b572
+			c--;
d6b572
+			*c = '\0';
d6b572
+		}
d6b572
+
d6b572
+		if (p->fixup == 0)
d6b572
+			goto skip_fixup_entry;
d6b572
+		else {
d6b572
+			fd = open(p->name, O_BINARY | O_NOFOLLOW | O_RDONLY
d6b572
+#if defined(O_DIRECTORY)
d6b572
+			    | O_DIRECTORY
d6b572
+#endif
d6b572
+			    | O_CLOEXEC);
d6b572
+			/*
d6b572
+		 `	 * If we don't support O_DIRECTORY,
d6b572
+			 * or open() has failed, we must stat()
d6b572
+			 * to verify that we are opening a directory
d6b572
+			 */
d6b572
+#if defined(O_DIRECTORY)
d6b572
 			if (fd == -1) {
d6b572
-				/* If we cannot lstat, skip entry */
d6b572
-				if (lstat(p->name, &st) != 0)
d6b572
+				if (lstat(p->name, &st) != 0 ||
d6b572
+				    !S_ISDIR(st.st_mode)) {
d6b572
 					goto skip_fixup_entry;
d6b572
-				/*
d6b572
-				 * If we deal with a symbolic link, mark
d6b572
-				 * it in the fixup mode to ensure no
d6b572
-				 * modifications are made to its target.
d6b572
-				 */
d6b572
-				if (S_ISLNK(st.st_mode)) {
d6b572
-					p->mode &= ~S_IFMT;
d6b572
-					p->mode |= S_IFLNK;
d6b572
 				}
d6b572
 			}
d6b572
+#else
d6b572
+#if HAVE_FSTAT
d6b572
+			if (fd > 0 && (
d6b572
+			    fstat(fd, &st) != 0 || !S_ISDIR(st.st_mode))) {
d6b572
+				goto skip_fixup_entry;
d6b572
+			} else
d6b572
+#endif
d6b572
+			if (lstat(p->name, &st) != 0 ||
d6b572
+			    !S_ISDIR(st.st_mode)) {
d6b572
+				goto skip_fixup_entry;
d6b572
+			}
d6b572
+#endif
d6b572
 		}
d6b572
 		if (p->fixup & TODO_TIMES) {
d6b572
 			set_times(a, fd, p->mode, p->name,
d6b572
@@ -2504,14 +2530,13 @@ _archive_write_disk_close(struct archive *_a)
d6b572
 		if (p->fixup & TODO_MODE_BASE) {
d6b572
 #ifdef HAVE_FCHMOD
d6b572
 			if (fd >= 0)
d6b572
-				fchmod(fd, p->mode);
d6b572
+				fchmod(fd, p->mode & 07777);
d6b572
 			else
d6b572
 #endif
d6b572
 #ifdef HAVE_LCHMOD
d6b572
-			lchmod(p->name, p->mode);
d6b572
+			lchmod(p->name, p->mode & 07777);
d6b572
 #else
d6b572
-			if (!S_ISLNK(p->mode))
d6b572
-				chmod(p->name, p->mode);
d6b572
+			chmod(p->name, p->mode & 07777);
d6b572
 #endif
d6b572
 		}
d6b572
 		if (p->fixup & TODO_ACLS)
d6b572
@@ -2664,7 +2689,6 @@ new_fixup(struct archive_write_disk *a, const char *pathname)
d6b572
 	fe->next = a->fixup_list;
d6b572
 	a->fixup_list = fe;
d6b572
 	fe->fixup = 0;
d6b572
-	fe->mode = 0;
d6b572
 	fe->name = strdup(pathname);
d6b572
 	return (fe);
d6b572
 }
d6b572
diff --git a/libarchive/test/test_write_disk_fixup.c b/libarchive/test/test_write_disk_fixup.c
d6b572
index c399c984..b83b7307 100644
d6b572
--- a/libarchive/test/test_write_disk_fixup.c
d6b572
+++ b/libarchive/test/test_write_disk_fixup.c
d6b572
@@ -47,26 +47,50 @@ DEFINE_TEST(test_write_disk_fixup)
d6b572
 	/*
d6b572
 	 * Create a file
d6b572
 	 */
d6b572
-	assertMakeFile("victim", 0600, "a");
d6b572
+	assertMakeFile("file", 0600, "a");
d6b572
+
d6b572
+	/*
d6b572
+	 * Create a directory
d6b572
+	 */
d6b572
+	assertMakeDir("dir", 0700);
d6b572
 
d6b572
 	/*
d6b572
 	 * Create a directory and a symlink with the same name
d6b572
 	 */
d6b572
 
d6b572
-	/* Directory: dir */
d6b572
+	/* Directory: dir1 */
d6b572
+        assert((ae = archive_entry_new()) != NULL);
d6b572
+        archive_entry_copy_pathname(ae, "dir1/");
d6b572
+        archive_entry_set_mode(ae, AE_IFDIR | 0555);
d6b572
+	assertEqualIntA(ad, 0, archive_write_header(ad, ae));
d6b572
+	assertEqualIntA(ad, 0, archive_write_finish_entry(ad));
d6b572
+        archive_entry_free(ae);
d6b572
+
d6b572
+	/* Directory: dir2 */
d6b572
         assert((ae = archive_entry_new()) != NULL);
d6b572
-        archive_entry_copy_pathname(ae, "dir");
d6b572
-        archive_entry_set_mode(ae, AE_IFDIR | 0606);
d6b572
+        archive_entry_copy_pathname(ae, "dir2/");
d6b572
+        archive_entry_set_mode(ae, AE_IFDIR | 0555);
d6b572
 	assertEqualIntA(ad, 0, archive_write_header(ad, ae));
d6b572
 	assertEqualIntA(ad, 0, archive_write_finish_entry(ad));
d6b572
         archive_entry_free(ae);
d6b572
 
d6b572
-	/* Symbolic Link: dir -> foo */
d6b572
+	/* Symbolic Link: dir1 -> dir */
d6b572
+	assert((ae = archive_entry_new()) != NULL);
d6b572
+	archive_entry_copy_pathname(ae, "dir1");
d6b572
+	archive_entry_set_mode(ae, AE_IFLNK | 0777);
d6b572
+	archive_entry_set_size(ae, 0);
d6b572
+	archive_entry_copy_symlink(ae, "dir");
d6b572
+	assertEqualIntA(ad, 0, r = archive_write_header(ad, ae));
d6b572
+	if (r >= ARCHIVE_WARN)
d6b572
+		assertEqualIntA(ad, 0, archive_write_finish_entry(ad));
d6b572
+	archive_entry_free(ae);
d6b572
+
d6b572
+	/* Symbolic Link: dir2 -> file */
d6b572
 	assert((ae = archive_entry_new()) != NULL);
d6b572
-	archive_entry_copy_pathname(ae, "dir");
d6b572
+	archive_entry_copy_pathname(ae, "dir2");
d6b572
 	archive_entry_set_mode(ae, AE_IFLNK | 0777);
d6b572
 	archive_entry_set_size(ae, 0);
d6b572
-	archive_entry_copy_symlink(ae, "victim");
d6b572
+	archive_entry_copy_symlink(ae, "file");
d6b572
 	assertEqualIntA(ad, 0, r = archive_write_header(ad, ae));
d6b572
 	if (r >= ARCHIVE_WARN)
d6b572
 		assertEqualIntA(ad, 0, archive_write_finish_entry(ad));
d6b572
@@ -75,6 +99,8 @@ DEFINE_TEST(test_write_disk_fixup)
d6b572
 	assertEqualInt(ARCHIVE_OK, archive_write_free(ad));
d6b572
 
d6b572
 	/* Test the entries on disk. */
d6b572
-	assertIsSymlink("dir", "victim");
d6b572
-	assertFileMode("victim", 0600);
d6b572
+	assertIsSymlink("dir1", "dir");
d6b572
+	assertIsSymlink("dir2", "file");
d6b572
+	assertFileMode("dir", 0700);
d6b572
+	assertFileMode("file", 0600);
d6b572
 }
d6b572
-- 
d6b572
2.31.1
d6b572
d6b572
From ede459d2ebb879f5eedb6f7abea203be0b334230 Mon Sep 17 00:00:00 2001
d6b572
From: Martin Matuska <martin@matuska.org>
d6b572
Date: Wed, 17 Nov 2021 21:06:00 +0100
d6b572
Subject: [PATCH] archive_write_disk_posix: fix writing fflags broken in
d6b572
 8a1bd5c
d6b572
d6b572
The fixup list was erroneously assumed to be directories only.
d6b572
Only in the case of critical file flags modification (e.g. SF_IMMUTABLE
d6b572
on BSD systems), other file types (e.g. regular files or symbolic links)
d6b572
may be added to the fixup list. We still need to verify that we are writing
d6b572
to the correct file type, so compare the archive entry file type with
d6b572
the file type of the file to be modified.
d6b572
d6b572
Fixes #1617
d6b572
---
d6b572
 libarchive/archive_write_disk_posix.c | 87 +++++++++++++++++++++++----
d6b572
 1 file changed, 75 insertions(+), 12 deletions(-)
d6b572
d6b572
diff --git a/libarchive/archive_write_disk_posix.c b/libarchive/archive_write_disk_posix.c
d6b572
index aadc5871..7e57aac2 100644
d6b572
--- a/libarchive/archive_write_disk_posix.c
d6b572
+++ b/libarchive/archive_write_disk_posix.c
d6b572
@@ -173,6 +173,7 @@ struct fixup_entry {
d6b572
 	struct fixup_entry	*next;
d6b572
 	struct archive_acl	 acl;
d6b572
 	mode_t			 mode;
d6b572
+	__LA_MODE_T		 filetype;
d6b572
 	int64_t			 atime;
d6b572
 	int64_t                  birthtime;
d6b572
 	int64_t			 mtime;
d6b572
@@ -357,6 +358,7 @@ struct archive_write_disk {
d6b572
 
d6b572
 #define HFS_BLOCKS(s)	((s) >> 12)
d6b572
 
d6b572
+static int	la_verify_filetype(mode_t, __LA_MODE_T);
d6b572
 static void	fsobj_error(int *, struct archive_string *, int, const char *,
d6b572
 		    const char *);
d6b572
 static int	check_symlinks_fsobj(char *, int *, struct archive_string *,
d6b572
@@ -464,6 +466,39 @@ la_opendirat(int fd, const char *path) {
d6b572
 static ssize_t	_archive_write_disk_data_block(struct archive *, const void *,
d6b572
 		    size_t, int64_t);
d6b572
 
d6b572
+static int
d6b572
+la_verify_filetype(mode_t mode, __LA_MODE_T filetype) {
d6b572
+	int ret = 0;
d6b572
+
d6b572
+	switch (filetype) {
d6b572
+	case AE_IFREG:
d6b572
+		ret = (S_ISREG(mode));
d6b572
+		break;
d6b572
+	case AE_IFDIR:
d6b572
+		ret = (S_ISDIR(mode));
d6b572
+		break;
d6b572
+	case AE_IFLNK:
d6b572
+		ret = (S_ISLNK(mode));
d6b572
+		break;
d6b572
+	case AE_IFSOCK:
d6b572
+		ret = (S_ISSOCK(mode));
d6b572
+		break;
d6b572
+	case AE_IFCHR:
d6b572
+		ret = (S_ISCHR(mode));
d6b572
+		break;
d6b572
+	case AE_IFBLK:
d6b572
+		ret = (S_ISBLK(mode));
d6b572
+		break;
d6b572
+	case AE_IFIFO:
d6b572
+		ret = (S_ISFIFO(mode));
d6b572
+		break;
d6b572
+	default:
d6b572
+		break;
d6b572
+	}
d6b572
+
d6b572
+	return (ret);
d6b572
+}
d6b572
+
d6b572
 static int
d6b572
 lazy_stat(struct archive_write_disk *a)
d6b572
 {
d6b572
@@ -822,6 +857,7 @@ _archive_write_disk_header(struct archive *_a, struct archive_entry *entry)
d6b572
 		fe = current_fixup(a, archive_entry_pathname(entry));
d6b572
 		if (fe == NULL)
d6b572
 			return (ARCHIVE_FATAL);
d6b572
+		fe->filetype = archive_entry_filetype(entry);
d6b572
 		fe->fixup |= TODO_MODE_BASE;
d6b572
 		fe->mode = a->mode;
d6b572
 	}
d6b572
@@ -832,6 +868,7 @@ _archive_write_disk_header(struct archive *_a, struct archive_entry *entry)
d6b572
 		fe = current_fixup(a, archive_entry_pathname(entry));
d6b572
 		if (fe == NULL)
d6b572
 			return (ARCHIVE_FATAL);
d6b572
+		fe->filetype = archive_entry_filetype(entry);
d6b572
 		fe->mode = a->mode;
d6b572
 		fe->fixup |= TODO_TIMES;
d6b572
 		if (archive_entry_atime_is_set(entry)) {
d6b572
@@ -865,6 +902,7 @@ _archive_write_disk_header(struct archive *_a, struct archive_entry *entry)
d6b572
 		fe = current_fixup(a, archive_entry_pathname(entry));
d6b572
 		if (fe == NULL)
d6b572
 			return (ARCHIVE_FATAL);
d6b572
+		fe->filetype = archive_entry_filetype(entry);
d6b572
 		fe->fixup |= TODO_ACLS;
d6b572
 		archive_acl_copy(&fe->acl, archive_entry_acl(entry));
d6b572
 	}
d6b572
@@ -877,6 +915,7 @@ _archive_write_disk_header(struct archive *_a, struct archive_entry *entry)
d6b572
 			fe = current_fixup(a, archive_entry_pathname(entry));
d6b572
 			if (fe == NULL)
d6b572
 				return (ARCHIVE_FATAL);
d6b572
+			fe->filetype = archive_entry_filetype(entry);
d6b572
 			fe->mac_metadata = malloc(metadata_size);
d6b572
 			if (fe->mac_metadata != NULL) {
d6b572
 				memcpy(fe->mac_metadata, metadata,
d6b572
@@ -891,6 +930,7 @@ _archive_write_disk_header(struct archive *_a, struct archive_entry *entry)
d6b572
 		fe = current_fixup(a, archive_entry_pathname(entry));
d6b572
 		if (fe == NULL)
d6b572
 			return (ARCHIVE_FATAL);
d6b572
+		fe->filetype = archive_entry_filetype(entry);
d6b572
 		fe->fixup |= TODO_FFLAGS;
d6b572
 		/* TODO: Complete this.. defer fflags from below. */
d6b572
 	}
d6b572
@@ -2463,7 +2503,7 @@ _archive_write_disk_close(struct archive *_a)
d6b572
 	struct fixup_entry *next, *p;
d6b572
 	struct stat st;
d6b572
 	char *c;
d6b572
-	int fd, ret;
d6b572
+	int fd, ret, openflags;
d6b572
 
d6b572
 	archive_check_magic(&a->archive, ARCHIVE_WRITE_DISK_MAGIC,
d6b572
 	    ARCHIVE_STATE_HEADER | ARCHIVE_STATE_DATA,
d6b572
@@ -2490,32 +2530,53 @@ _archive_write_disk_close(struct archive *_a)
d6b572
 		if (p->fixup == 0)
d6b572
 			goto skip_fixup_entry;
d6b572
 		else {
d6b572
-			fd = open(p->name, O_BINARY | O_NOFOLLOW | O_RDONLY
d6b572
+			/*
d6b572
+			 * We need to verify if the type of the file
d6b572
+			 * we are going to open matches the file type
d6b572
+			 * of the fixup entry.
d6b572
+			 */
d6b572
+			openflags = O_BINARY | O_NOFOLLOW | O_RDONLY
d6b572
+			    | O_CLOEXEC;
d6b572
 #if defined(O_DIRECTORY)
d6b572
-			    | O_DIRECTORY
d6b572
+			if (p->filetype == AE_IFDIR)
d6b572
+				openflags |= O_DIRECTORY;
d6b572
 #endif
d6b572
-			    | O_CLOEXEC);
d6b572
+			fd = open(p->name, openflags);
d6b572
+
d6b572
+#if defined(O_DIRECTORY)
d6b572
 			/*
d6b572
-		 `	 * If we don't support O_DIRECTORY,
d6b572
-			 * or open() has failed, we must stat()
d6b572
-			 * to verify that we are opening a directory
d6b572
+			 * If we support O_DIRECTORY and open was
d6b572
+			 * successful we can skip the file type check
d6b572
+			 * for directories. For other file types
d6b572
+			 * we need to verify via fstat() or lstat()
d6b572
 			 */
d6b572
-#if defined(O_DIRECTORY)
d6b572
-			if (fd == -1) {
d6b572
+			if (fd == -1 || p->filetype != AE_IFDIR) {
d6b572
+#if HAVE_FSTAT
d6b572
+				if (fd > 0 && (
d6b572
+				    fstat(fd, &st) != 0 ||
d6b572
+				    la_verify_filetype(st.st_mode,
d6b572
+				    p->filetype) == 0)) {
d6b572
+					goto skip_fixup_entry;
d6b572
+				} else
d6b572
+#endif
d6b572
 				if (lstat(p->name, &st) != 0 ||
d6b572
-				    !S_ISDIR(st.st_mode)) {
d6b572
+				    la_verify_filetype(st.st_mode,
d6b572
+				    p->filetype) == 0) {
d6b572
 					goto skip_fixup_entry;
d6b572
 				}
d6b572
 			}
d6b572
 #else
d6b572
 #if HAVE_FSTAT
d6b572
 			if (fd > 0 && (
d6b572
-			    fstat(fd, &st) != 0 || !S_ISDIR(st.st_mode))) {
d6b572
+			    fstat(fd, &st) != 0 ||
d6b572
+			    la_verify_filetype(st.st_mode,
d6b572
+			    p->filetype) == 0)) {
d6b572
 				goto skip_fixup_entry;
d6b572
 			} else
d6b572
 #endif
d6b572
 			if (lstat(p->name, &st) != 0 ||
d6b572
-			    !S_ISDIR(st.st_mode)) {
d6b572
+			    la_verify_filetype(st.st_mode,
d6b572
+			    p->filetype) == 0) {
d6b572
 				goto skip_fixup_entry;
d6b572
 			}
d6b572
 #endif
d6b572
@@ -2689,6 +2750,7 @@ new_fixup(struct archive_write_disk *a, const char *pathname)
d6b572
 	fe->next = a->fixup_list;
d6b572
 	a->fixup_list = fe;
d6b572
 	fe->fixup = 0;
d6b572
+	fe->filetype = 0;
d6b572
 	fe->name = strdup(pathname);
d6b572
 	return (fe);
d6b572
 }
d6b572
@@ -3811,6 +3873,7 @@ set_fflags(struct archive_write_disk *a)
d6b572
 			le = current_fixup(a, a->name);
d6b572
 			if (le == NULL)
d6b572
 				return (ARCHIVE_FATAL);
d6b572
+			le->filetype = archive_entry_filetype(a->entry);
d6b572
 			le->fixup |= TODO_FFLAGS;
d6b572
 			le->fflags_set = set;
d6b572
 			/* Store the mode if it's not already there. */
d6b572
-- 
d6b572
2.31.1
d6b572