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

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