Blame SOURCES/libarchive-3.5.2-symlink-fix.patch

d44754
commit 8a1bd5c18e896f0411a991240ce0d772bb02c840
d44754
Author: Martin Matuska <martin@matuska.org>
d44754
Date:   Fri Aug 27 10:56:28 2021 +0200
d44754
d44754
    Fix following symlinks when processing the fixup list
d44754
    
d44754
    The previous fix in b41daecb5 was incomplete. Fixup entries are
d44754
    given the original path without calling cleanup_pathname().
d44754
    To make sure we don't follow a symlink, we must strip trailing
d44754
    slashes from the path.
d44754
    
d44754
    The fixup entries are always directories. Make sure we try to modify
d44754
    only directories by providing O_DIRECTORY to open() (if supported)
d44754
    and if it fails to check directory via lstat().
d44754
    
d44754
    Fixes #1566
d44754
d44754
diff --git a/libarchive/archive_write_disk_posix.c b/libarchive/archive_write_disk_posix.c
d44754
index fcd733af..aadc5871 100644
d44754
--- a/libarchive/archive_write_disk_posix.c
d44754
+++ b/libarchive/archive_write_disk_posix.c
d44754
@@ -2462,6 +2462,7 @@ _archive_write_disk_close(struct archive *_a)
d44754
 	struct archive_write_disk *a = (struct archive_write_disk *)_a;
d44754
 	struct fixup_entry *next, *p;
d44754
 	struct stat st;
d44754
+	char *c;
d44754
 	int fd, ret;
d44754
 
d44754
 	archive_check_magic(&a->archive, ARCHIVE_WRITE_DISK_MAGIC,
d44754
@@ -2475,24 +2476,49 @@ _archive_write_disk_close(struct archive *_a)
d44754
 	while (p != NULL) {
d44754
 		fd = -1;
d44754
 		a->pst = NULL; /* Mark stat cache as out-of-date. */
d44754
-		if (p->fixup &
d44754
-		    (TODO_TIMES | TODO_MODE_BASE | TODO_ACLS | TODO_FFLAGS)) {
d44754
-			fd = open(p->name,
d44754
-			    O_WRONLY | O_BINARY | O_NOFOLLOW | O_CLOEXEC);
d44754
+
d44754
+		/* We must strip trailing slashes from the path to avoid
d44754
+		   dereferencing symbolic links to directories */
d44754
+		c = p->name;
d44754
+		while (*c != '\0')
d44754
+			c++;
d44754
+		while (c != p->name && *(c - 1) == '/') {
d44754
+			c--;
d44754
+			*c = '\0';
d44754
+		}
d44754
+
d44754
+		if (p->fixup == 0)
d44754
+			goto skip_fixup_entry;
d44754
+		else {
d44754
+			fd = open(p->name, O_BINARY | O_NOFOLLOW | O_RDONLY
d44754
+#if defined(O_DIRECTORY)
d44754
+			    | O_DIRECTORY
d44754
+#endif
d44754
+			    | O_CLOEXEC);
d44754
+			/*
d44754
+		 `	 * If we don't support O_DIRECTORY,
d44754
+			 * or open() has failed, we must stat()
d44754
+			 * to verify that we are opening a directory
d44754
+			 */
d44754
+#if defined(O_DIRECTORY)
d44754
 			if (fd == -1) {
d44754
-				/* If we cannot lstat, skip entry */
d44754
-				if (lstat(p->name, &st) != 0)
d44754
+				if (lstat(p->name, &st) != 0 ||
d44754
+				    !S_ISDIR(st.st_mode)) {
d44754
 					goto skip_fixup_entry;
d44754
-				/*
d44754
-				 * If we deal with a symbolic link, mark
d44754
-				 * it in the fixup mode to ensure no
d44754
-				 * modifications are made to its target.
d44754
-				 */
d44754
-				if (S_ISLNK(st.st_mode)) {
d44754
-					p->mode &= ~S_IFMT;
d44754
-					p->mode |= S_IFLNK;
d44754
 				}
d44754
 			}
d44754
+#else
d44754
+#if HAVE_FSTAT
d44754
+			if (fd > 0 && (
d44754
+			    fstat(fd, &st) != 0 || !S_ISDIR(st.st_mode))) {
d44754
+				goto skip_fixup_entry;
d44754
+			} else
d44754
+#endif
d44754
+			if (lstat(p->name, &st) != 0 ||
d44754
+			    !S_ISDIR(st.st_mode)) {
d44754
+				goto skip_fixup_entry;
d44754
+			}
d44754
+#endif
d44754
 		}
d44754
 		if (p->fixup & TODO_TIMES) {
d44754
 			set_times(a, fd, p->mode, p->name,
d44754
@@ -2504,14 +2530,13 @@ _archive_write_disk_close(struct archive *_a)
d44754
 		if (p->fixup & TODO_MODE_BASE) {
d44754
 #ifdef HAVE_FCHMOD
d44754
 			if (fd >= 0)
d44754
-				fchmod(fd, p->mode);
d44754
+				fchmod(fd, p->mode & 07777);
d44754
 			else
d44754
 #endif
d44754
 #ifdef HAVE_LCHMOD
d44754
-			lchmod(p->name, p->mode);
d44754
+			lchmod(p->name, p->mode & 07777);
d44754
 #else
d44754
-			if (!S_ISLNK(p->mode))
d44754
-				chmod(p->name, p->mode);
d44754
+			chmod(p->name, p->mode & 07777);
d44754
 #endif
d44754
 		}
d44754
 		if (p->fixup & TODO_ACLS)
d44754
@@ -2664,7 +2689,6 @@ new_fixup(struct archive_write_disk *a, const char *pathname)
d44754
 	fe->next = a->fixup_list;
d44754
 	a->fixup_list = fe;
d44754
 	fe->fixup = 0;
d44754
-	fe->mode = 0;
d44754
 	fe->name = strdup(pathname);
d44754
 	return (fe);
d44754
 }
d44754
diff --git a/libarchive/test/test_write_disk_fixup.c b/libarchive/test/test_write_disk_fixup.c
d44754
index c399c984..b83b7307 100644
d44754
--- a/libarchive/test/test_write_disk_fixup.c
d44754
+++ b/libarchive/test/test_write_disk_fixup.c
d44754
@@ -47,26 +47,50 @@ DEFINE_TEST(test_write_disk_fixup)
d44754
 	/*
d44754
 	 * Create a file
d44754
 	 */
d44754
-	assertMakeFile("victim", 0600, "a");
d44754
+	assertMakeFile("file", 0600, "a");
d44754
+
d44754
+	/*
d44754
+	 * Create a directory
d44754
+	 */
d44754
+	assertMakeDir("dir", 0700);
d44754
 
d44754
 	/*
d44754
 	 * Create a directory and a symlink with the same name
d44754
 	 */
d44754
 
d44754
-	/* Directory: dir */
d44754
+	/* Directory: dir1 */
d44754
+        assert((ae = archive_entry_new()) != NULL);
d44754
+        archive_entry_copy_pathname(ae, "dir1/");
d44754
+        archive_entry_set_mode(ae, AE_IFDIR | 0555);
d44754
+	assertEqualIntA(ad, 0, archive_write_header(ad, ae));
d44754
+	assertEqualIntA(ad, 0, archive_write_finish_entry(ad));
d44754
+        archive_entry_free(ae);
d44754
+
d44754
+	/* Directory: dir2 */
d44754
         assert((ae = archive_entry_new()) != NULL);
d44754
-        archive_entry_copy_pathname(ae, "dir");
d44754
-        archive_entry_set_mode(ae, AE_IFDIR | 0606);
d44754
+        archive_entry_copy_pathname(ae, "dir2/");
d44754
+        archive_entry_set_mode(ae, AE_IFDIR | 0555);
d44754
 	assertEqualIntA(ad, 0, archive_write_header(ad, ae));
d44754
 	assertEqualIntA(ad, 0, archive_write_finish_entry(ad));
d44754
         archive_entry_free(ae);
d44754
 
d44754
-	/* Symbolic Link: dir -> foo */
d44754
+	/* Symbolic Link: dir1 -> dir */
d44754
+	assert((ae = archive_entry_new()) != NULL);
d44754
+	archive_entry_copy_pathname(ae, "dir1");
d44754
+	archive_entry_set_mode(ae, AE_IFLNK | 0777);
d44754
+	archive_entry_set_size(ae, 0);
d44754
+	archive_entry_copy_symlink(ae, "dir");
d44754
+	assertEqualIntA(ad, 0, r = archive_write_header(ad, ae));
d44754
+	if (r >= ARCHIVE_WARN)
d44754
+		assertEqualIntA(ad, 0, archive_write_finish_entry(ad));
d44754
+	archive_entry_free(ae);
d44754
+
d44754
+	/* Symbolic Link: dir2 -> file */
d44754
 	assert((ae = archive_entry_new()) != NULL);
d44754
-	archive_entry_copy_pathname(ae, "dir");
d44754
+	archive_entry_copy_pathname(ae, "dir2");
d44754
 	archive_entry_set_mode(ae, AE_IFLNK | 0777);
d44754
 	archive_entry_set_size(ae, 0);
d44754
-	archive_entry_copy_symlink(ae, "victim");
d44754
+	archive_entry_copy_symlink(ae, "file");
d44754
 	assertEqualIntA(ad, 0, r = archive_write_header(ad, ae));
d44754
 	if (r >= ARCHIVE_WARN)
d44754
 		assertEqualIntA(ad, 0, archive_write_finish_entry(ad));
d44754
@@ -75,7 +99,9 @@ DEFINE_TEST(test_write_disk_fixup)
d44754
 	assertEqualInt(ARCHIVE_OK, archive_write_free(ad));
d44754
 
d44754
 	/* Test the entries on disk. */
d44754
-	assertIsSymlink("dir", "victim", 0);
d44754
-	assertFileMode("victim", 0600);
d44754
+	assertIsSymlink("dir1", "dir", 0);
d44754
+	assertIsSymlink("dir2", "file", 0);
d44754
+	assertFileMode("dir", 0700);
d44754
+	assertFileMode("file", 0600);
d44754
 #endif
d44754
 }