(this same issue has been discussed on misc@ recently.
with the subject 'issue with cp(1) and S_ISUID S_ISGID bits.')
In the form of 'cp src dst' where both src and dst are regular existing
files with the same uid and gid of the calling process, in
the case of the src having S_ISUID or S_ISGID bits active the
mode of the target file will be overwritten with the combination
of the source mode and the calling umask, this also applies
if the '-p' option is used and maybe not desirable as the man page
says that an existing destination will preserve the it's mode.
Quote from the cp(1) manpage:
For each destination file that already exists, its contents are
overwritten if
permissions allow, but its mode, user ID, and group ID are unchanged.
[...]
If the source file has its set-user-ID bit on, that bit is removed
unless both
the source file and the destination file are owned by the same user. If the
source file has its set-group-ID bit on, that bit is removed unless both the
source file and the destination file are in the same group and the user is a
member of that group. If both the set-user-ID and set-group-ID bits are
set,
all of the above conditions must be fulfilled or both bits are removed.
[...]
-p
Preserve in the copy as many of the modification time, access
time, file flags, file mode, user ID, and group ID as allowed by
permissions.
How to reproduce as non-root user in a directory with same gid:
umask 0000
mkdir tmp
cd tmp
touch src
touch dst
chmod 0600 dst
chmod 4666 src
cp src dst
ls -l
----- dst have mode 4666 instead of 4600 as expected ----
In the case of using umask 0022 it will apply even if the '-p' option
is used.
I posted a quick and dirty diff (this time ignoring umask in case of
existing file)
to illustrate the issue, Otto asked for the diff but I thought it should
also be posted
here.
If I'm right the 'mode_t myumask' global variable can also be deleted in
the source.
I hope any of this helps.
-Jesus
----------------------------------------------------------------
--- utils.c.orig Tue Jun 14 03:18:56 2011
+++ utils.c Tue Jun 14 03:18:51 2011
@@ -170,20 +170,26 @@
return (1);
}
- if (pflag && setfile(fs, to_fd))
- rval = 1;
+ if (pflag){ /* In case of pflag we don't try to retain S_ISUID or
S_ISGID bits and ignore the umask values */
+ if( setfile(fs, to_fd))
+ rval = 1;
+#define RETAINBITS \
+ (S_ISUID | S_ISGID | S_ISVTX | S_IRWXU | S_IRWXG | S_IRWXO)
+ }
/*
* If the source was setuid or setgid, lose the bits unless the
* copy is owned by the same user and group.
*/
-#define RETAINBITS \
- (S_ISUID | S_ISGID | S_ISVTX | S_IRWXU | S_IRWXG | S_IRWXO)
else if (fs->st_mode & (S_ISUID | S_ISGID) && fs->st_uid == myuid) {
if (fstat(to_fd, &to_stat)) {
warn("%s", to.p_path);
rval = 1;
} else if (fs->st_gid == to_stat.st_gid &&
- fchmod(to_fd, fs->st_mode & RETAINBITS & ~myumask)) {
+ fchmod(to_fd, (((S_ISUID | S_ISGID) & fs->st_mode ) |
to_stat.st_mode) & RETAINBITS )) {
+ /*
+ * We preserve the target file mode and add only the
+ * S_ISUID and S_ISGID bits from the source file mode
+ */
warn("%s", to.p_path);
rval = 1;
}