| From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 |
| From: Alessio Balsini <balsini@google.com> |
| Date: Mon, 25 Jan 2021 17:04:38 +0000 |
| Subject: FROMLIST: fuse: Use daemon creds in passthrough mode |
| |
| When using FUSE passthrough, read/write operations are directly |
| forwarded to the lower file system file through VFS, but there is no |
| guarantee that the process that is triggering the request has the right |
| permissions to access the lower file system. This would cause the |
| read/write access to fail. |
| |
| In passthrough file systems, where the FUSE daemon is responsible for |
| the enforcement of the lower file system access policies, often happens |
| that the process dealing with the FUSE file system doesn't have access |
| to the lower file system. |
| Being the FUSE daemon in charge of implementing the FUSE file |
| operations, that in the case of read/write operations usually simply |
| results in the copy of memory buffers from/to the lower file system |
| respectively, these operations are executed with the FUSE daemon |
| privileges. |
| |
| This patch adds a reference to the FUSE daemon credentials, referenced |
| at FUSE_DEV_IOC_PASSTHROUGH_OPEN ioctl() time so that they can be used |
| to temporarily raise the user credentials when accessing lower file |
| system files in passthrough. |
| The process accessing the FUSE file with passthrough enabled temporarily |
| receives the privileges of the FUSE daemon while performing read/write |
| operations. Similar behavior is implemented in overlayfs. |
| These privileges will be reverted as soon as the IO operation completes. |
| This feature does not provide any higher security privileges to those |
| processes accessing the FUSE file system with passthrough enabled. This |
| is because it is still the FUSE daemon responsible for enabling or not |
| the passthrough feature at file open time, and should enable the feature |
| only after appropriate access policy checks. |
| |
| [CPNOTE: 21/05/21] Lee: Still fresh - hopefully this will land upstream soon |
| |
| Bug: 168023149 |
| Link: https://lore.kernel.org/lkml/20210125153057.3623715-8-balsini@android.com/ |
| Signed-off-by: Alessio Balsini <balsini@android.com> |
| Change-Id: Idb4f03a2ce7c536691e5eaf8fadadfcf002e1677 |
| Signed-off-by: Alessio Balsini <balsini@google.com> |
| Signed-off-by: Lee Jones <joneslee@google.com> |
| --- |
| fs/fuse/fuse_i.h | 5 ++++- |
| fs/fuse/passthrough.c | 18 ++++++++++++++++-- |
| 2 files changed, 20 insertions(+), 3 deletions(-) |
| |
| diff --git a/fs/fuse/fuse_i.h b/fs/fuse/fuse_i.h |
| --- a/fs/fuse/fuse_i.h |
| +++ b/fs/fuse/fuse_i.h |
| @@ -175,10 +175,13 @@ struct fuse_release_args; |
| |
| /** |
| * Reference to lower filesystem file for read/write operations handled in |
| - * passthrough mode |
| + * passthrough mode. |
| + * This struct also tracks the credentials to be used for handling read/write |
| + * operations. |
| */ |
| struct fuse_passthrough { |
| struct file *filp; |
| + struct cred *cred; |
| }; |
| |
| /** FUSE specific file data */ |
| diff --git a/fs/fuse/passthrough.c b/fs/fuse/passthrough.c |
| --- a/fs/fuse/passthrough.c |
| +++ b/fs/fuse/passthrough.c |
| @@ -53,6 +53,7 @@ ssize_t fuse_passthrough_read_iter(struct kiocb *iocb_fuse, |
| struct iov_iter *iter) |
| { |
| ssize_t ret; |
| + const struct cred *old_cred; |
| struct file *fuse_filp = iocb_fuse->ki_filp; |
| struct fuse_file *ff = fuse_filp->private_data; |
| struct file *passthrough_filp = ff->passthrough.filp; |
| @@ -60,6 +61,7 @@ ssize_t fuse_passthrough_read_iter(struct kiocb *iocb_fuse, |
| if (!iov_iter_count(iter)) |
| return 0; |
| |
| + old_cred = override_creds(ff->passthrough.cred); |
| if (is_sync_kiocb(iocb_fuse)) { |
| ret = vfs_iter_read(passthrough_filp, iter, &iocb_fuse->ki_pos, |
| iocb_to_rw_flags(iocb_fuse->ki_flags, |
| @@ -68,8 +70,10 @@ ssize_t fuse_passthrough_read_iter(struct kiocb *iocb_fuse, |
| struct fuse_aio_req *aio_req; |
| |
| aio_req = kmalloc(sizeof(struct fuse_aio_req), GFP_KERNEL); |
| - if (!aio_req) |
| - return -ENOMEM; |
| + if (!aio_req) { |
| + ret = -ENOMEM; |
| + goto out; |
| + } |
| |
| aio_req->iocb_fuse = iocb_fuse; |
| kiocb_clone(&aio_req->iocb, iocb_fuse, passthrough_filp); |
| @@ -78,6 +82,8 @@ ssize_t fuse_passthrough_read_iter(struct kiocb *iocb_fuse, |
| if (ret != -EIOCBQUEUED) |
| fuse_aio_cleanup_handler(aio_req); |
| } |
| +out: |
| + revert_creds(old_cred); |
| |
| return ret; |
| } |
| @@ -86,6 +92,7 @@ ssize_t fuse_passthrough_write_iter(struct kiocb *iocb_fuse, |
| struct iov_iter *iter) |
| { |
| ssize_t ret; |
| + const struct cred *old_cred; |
| struct file *fuse_filp = iocb_fuse->ki_filp; |
| struct fuse_file *ff = fuse_filp->private_data; |
| struct inode *fuse_inode = file_inode(fuse_filp); |
| @@ -97,6 +104,7 @@ ssize_t fuse_passthrough_write_iter(struct kiocb *iocb_fuse, |
| |
| inode_lock(fuse_inode); |
| |
| + old_cred = override_creds(ff->passthrough.cred); |
| if (is_sync_kiocb(iocb_fuse)) { |
| file_start_write(passthrough_filp); |
| ret = vfs_iter_write(passthrough_filp, iter, &iocb_fuse->ki_pos, |
| @@ -125,6 +133,7 @@ ssize_t fuse_passthrough_write_iter(struct kiocb *iocb_fuse, |
| fuse_aio_cleanup_handler(aio_req); |
| } |
| out: |
| + revert_creds(old_cred); |
| inode_unlock(fuse_inode); |
| |
| return ret; |
| @@ -175,6 +184,7 @@ int fuse_passthrough_open(struct fuse_dev *fud, |
| } |
| |
| passthrough->filp = passthrough_filp; |
| + passthrough->cred = prepare_creds(); |
| |
| idr_preload(GFP_KERNEL); |
| spin_lock(&fc->passthrough_req_lock); |
| @@ -226,4 +236,8 @@ void fuse_passthrough_release(struct fuse_passthrough *passthrough) |
| fput(passthrough->filp); |
| passthrough->filp = NULL; |
| } |
| + if (passthrough->cred) { |
| + put_cred(passthrough->cred); |
| + passthrough->cred = NULL; |
| + } |
| } |