gzip: fix permissions issue on Solaris-like systems
authorPaul Eggert <eggert@cs.ucla.edu>
Thu, 24 Oct 2013 07:19:56 +0000 (00:19 -0700)
committerPaul Eggert <eggert@cs.ucla.edu>
Thu, 24 Oct 2013 07:20:51 +0000 (00:20 -0700)
I.e., on systems that let users give files away.
* gzip.c (do_chown): New function.
(copy_stat): Use it, to change the group, then the permissions,
then the owner.  Idea suggested by Vladimir Marek in
<http://bugs.gnu.org/15672#11>

gzip.c

diff --git a/gzip.c b/gzip.c
index 93cc7384ff3959664807e7dad470fe01f4c4f99c..f40cd219abf97749d9109a41eee03ef74c83b679 100644 (file)
--- a/gzip.c
+++ b/gzip.c
@@ -1696,6 +1696,21 @@ local int check_ofname()
     return OK;
 }
 
+/* Change the owner and group of a file.  FD is a file descriptor for
+   the file and NAME its name.  Change it to user UID and to group GID.
+   If UID or GID is -1, though, do not change the corresponding user
+   or group.  */
+static void
+do_chown (int fd, char const *name, uid_t uid, gid_t gid)
+{
+#ifndef NO_CHOWN
+# if HAVE_FCHOWN
+  ignore_value (fchown (fd, uid, gid));
+# else
+  ignore_value (chown (name, uid, gid));
+# endif
+#endif
+}
 
 /* ========================================================================
  * Copy modes, times, ownership from input file to output file.
@@ -1734,16 +1749,14 @@ local void copy_stat(ifstat)
       }
 #endif
 
-#ifndef NO_CHOWN
-    /* Copy ownership */
-# if HAVE_FCHOWN
-    ignore_value (fchown (ofd, ifstat->st_uid, ifstat->st_gid));
-# elif HAVE_CHOWN
-    ignore_value (chown (ofname, ifstat->st_uid, ifstat->st_gid));
-# endif
-#endif
+    /* Change the group first, then the permissions, then the owner.
+       That way, the permissions will be correct on systems that allow
+       users to give away files, without introducing a security hole.
+       Security depends on permissions not containing the setuid or
+       setgid bits.  */
+
+    do_chown (ofd, ofname, -1, ifstat->st_gid);
 
-    /* Copy the protection modes */
 #if HAVE_FCHMOD
     r = fchmod (ofd, mode);
 #else
@@ -1757,6 +1770,8 @@ local void copy_stat(ifstat)
             perror(ofname);
         }
     }
+
+    do_chown (ofd, ofname, ifstat->st_uid, -1);
 }
 
 #if ! NO_DIR