diff options
author | Miklos Szeredi <mszeredi@redhat.com> | 2019-05-27 09:08:12 +0200 |
---|---|---|
committer | Miklos Szeredi <mszeredi@redhat.com> | 2019-05-27 11:42:36 +0200 |
commit | 4a2abf99f9c2877039006b8dfb3ec4e0d1278fe8 (patch) | |
tree | 01872fd7aac66a14a0aaaccb7f74289e1603eccf | |
parent | 35d6fcbb7c3e296a52136347346a698a35af3fda (diff) | |
download | linux-4a2abf99f9c2877039006b8dfb3ec4e0d1278fe8.tar.gz linux-4a2abf99f9c2877039006b8dfb3ec4e0d1278fe8.tar.bz2 linux-4a2abf99f9c2877039006b8dfb3ec4e0d1278fe8.zip |
fuse: add FUSE_WRITE_KILL_PRIV
In the FOPEN_DIRECT_IO case the write path doesn't call file_remove_privs()
and that means setuid bit is not cleared if unpriviliged user writes to a
file with setuid bit set.
pjdfstest chmod test 12.t tests this and fails.
Fix this by adding a flag to the FUSE_WRITE message that requests clearing
privileges on the given file. This needs
This better than just calling fuse_remove_privs(), because the attributes
may not be up to date, so in that case a write may miss clearing the
privileges.
Test case:
$ passthrough_ll /mnt/pasthrough-mnt -o default_permissions,allow_other,cache=never
$ mkdir /mnt/pasthrough-mnt/testdir
$ cd /mnt/pasthrough-mnt/testdir
$ prove -rv pjdfstests/tests/chmod/12.t
Reported-by: Vivek Goyal <vgoyal@redhat.com>
Signed-off-by: Miklos Szeredi <mszeredi@redhat.com>
Tested-by: Vivek Goyal <vgoyal@redhat.com>
-rw-r--r-- | fs/fuse/file.c | 11 | ||||
-rw-r--r-- | include/uapi/linux/fuse.h | 7 |
2 files changed, 15 insertions, 3 deletions
diff --git a/fs/fuse/file.c b/fs/fuse/file.c index 143c54f93f2d..c1064866c402 100644 --- a/fs/fuse/file.c +++ b/fs/fuse/file.c @@ -1377,10 +1377,17 @@ ssize_t fuse_direct_io(struct fuse_io_priv *io, struct iov_iter *iter, if (err && !nbytes) break; - if (write) + if (write) { + if (!capable(CAP_FSETID)) { + struct fuse_write_in *inarg; + + inarg = &req->misc.write.in; + inarg->write_flags |= FUSE_WRITE_KILL_PRIV; + } nres = fuse_send_write(req, io, pos, nbytes, owner); - else + } else { nres = fuse_send_read(req, io, pos, nbytes, owner); + } if (!io->async) fuse_release_user_pages(req, io->should_dirty); diff --git a/include/uapi/linux/fuse.h b/include/uapi/linux/fuse.h index 19fb55e3c73e..2971d29a42e4 100644 --- a/include/uapi/linux/fuse.h +++ b/include/uapi/linux/fuse.h @@ -130,6 +130,9 @@ * 7.30 * - add FUSE_EXPLICIT_INVAL_DATA * - add FUSE_IOCTL_COMPAT_X32 + * + * 7.31 + * - add FUSE_WRITE_KILL_PRIV flag */ #ifndef _LINUX_FUSE_H @@ -165,7 +168,7 @@ #define FUSE_KERNEL_VERSION 7 /** Minor version number of this interface */ -#define FUSE_KERNEL_MINOR_VERSION 30 +#define FUSE_KERNEL_MINOR_VERSION 31 /** The node ID of the root inode */ #define FUSE_ROOT_ID 1 @@ -327,9 +330,11 @@ struct fuse_file_lock { * * FUSE_WRITE_CACHE: delayed write from page cache, file handle is guessed * FUSE_WRITE_LOCKOWNER: lock_owner field is valid + * FUSE_WRITE_KILL_PRIV: kill suid and sgid bits */ #define FUSE_WRITE_CACHE (1 << 0) #define FUSE_WRITE_LOCKOWNER (1 << 1) +#define FUSE_WRITE_KILL_PRIV (1 << 2) /** * Read flags |