userns: Support fuse interacting with multiple user namespaces
Subsystems (2)
PATCH 1/1 userns: Support fuse interacting with multiple user namespaces
Use kuid_t and kgid_t in struct fuse_conn and struct fuse_mount_data.
The connection between between a fuse filesystem and a fuse daemon is established when a fuse filesystem is mounted and provided with a file descriptor the fuse daemon created by opening /dev/fuse.
For now restrict the communication of uids and gids between the fuse filesystem and the fuse daemon to the initial user namespace. Enforce this by verifying the file descriptor passed to the mount of fuse was opened in the initial user namespace. Ensuring the mount happens in the initial user namespace is not necessary as mounts from non-initial user namespaces are not yet allowed.
In fuse_req_init_context convert the currrent fsuid and fsgid into the initial user namespace for the request that will be sent to the fuse daemon.
In fuse_fill_attr convert the uid and gid passed from the fuse daemon from the initial user namespace into kuids and kgids.
In iattr_to_fattr called from fuse_setattr convert kuids and kgids into the uids and gids in the initial user namespace before passing them to the fuse filesystem.
In fuse_change_attributes_common called from fuse_dentry_revalidate, fuse_permission, fuse_geattr, and fuse_setattr, and fuse_iget convert the uid and gid from the fuse daemon into a kuid and a kgid to store on the fuse inode.
By default fuse mounts are restricted to task whose uid, suid, and euid matches the fuse user_id and whose gid, sgid, and egid matches the fuse group id. Convert the user_id and group_id mount options into kuids and kgids at mount time, and use uid_eq and gid_eq to compare the in fuse_allow_task.
--- fs/fuse/dev.c | 4 ++-- fs/fuse/dir.c | 20 ++++++++++---------- fs/fuse/fuse_i.h | 4 ++-- fs/fuse/inode.c | 23 ++++++++++++++--------- init/Kconfig | 1 - 5 files changed, 28 insertions(+), 24 deletions(-)
fs/fuse/dev.c +2 -2
@@ -92,8 +92,8 @@ static void __fuse_put_request(struct fuse_req *req)static void fuse_req_init_context (struct fuse_req *req) { - req->in.h.uid =current_fsuid (); - req->in.h.gid =current_fsgid (); + req->in.h.uid =from_kuid_munged (&init_user_ns,current_fsuid ()); + req->in.h.gid =from_kgid_munged (&init_user_ns,current_fsgid ()); req->in.h.pid = current->pid; }
fs/fuse/dir.c +10 -10
@@ -818,8 +818,8 @@ static void fuse_fillattr(struct inode *inode, struct fuse_attr *attr, stat->ino = attr->ino; stat->mode = (inode->i_mode &S_IFMT ) | (attr->mode &07777 ); stat->nlink = attr->nlink; - stat->uid = attr->uid; - stat->gid = attr->gid; + stat->uid =make_kuid (&init_user_ns, attr->uid); + stat->gid =make_kgid (&init_user_ns, attr->gid); stat->rdev = inode->i_rdev; stat->atime.tv_sec = attr->atime; stat->atime.tv_nsec = attr->atimensec;
@@ -1007,12 +1007,12 @@ int fuse_allow_task(struct fuse_conn *fc, struct task_struct *task)rcu_read_lock (); ret =0 ; cred =__task_cred (task); -if (cred->euid == fc->user_id && - cred->suid == fc->user_id && - cred->uid == fc->user_id && - cred->egid == fc->group_id && - cred->sgid == fc->group_id && - cred->gid == fc->group_id) +if (uid_eq (cred->euid, fc->user_id) && +uid_eq (cred->suid, fc->user_id) && +uid_eq (cred->uid, fc->user_id) && +gid_eq (cred->egid, fc->group_id) && +gid_eq (cred->sgid, fc->group_id) && +gid_eq (cred->gid, fc->group_id)) ret =1 ;rcu_read_unlock ();
@@ -1306,9 +1306,9 @@ static void iattr_to_fattr(struct iattr *iattr, struct fuse_setattr_in *arg)if (ivalid &ATTR_MODE ) arg->valid |=FATTR_MODE , arg->mode = iattr->ia_mode;if (ivalid &ATTR_UID ) - arg->valid |=FATTR_UID , arg->uid = iattr->ia_uid; + arg->valid |=FATTR_UID , arg->uid =from_kuid (&init_user_ns, iattr->ia_uid);if (ivalid &ATTR_GID ) - arg->valid |=FATTR_GID , arg->gid = iattr->ia_gid; + arg->valid |=FATTR_GID , arg->gid =from_kgid (&init_user_ns, iattr->ia_gid);if (ivalid &ATTR_SIZE ) arg->valid |=FATTR_SIZE , arg->size = iattr->ia_size;if (ivalid &ATTR_ATIME ) {
fs/fuse/fuse_i.h +2 -2
@@ -333,10 +333,10 @@ struct fuse_conn {atomic_t count;/** The user id for this mount */ -uid_t user_id; +kuid_t user_id;/** The group id for this mount */ -gid_t group_id; +kgid_t group_id;/** The fuse mount flags for this mount */ unsigned flags;
fs/fuse/inode.c +14 -9
@@ -60,8 +60,8 @@ MODULE_PARM_DESC(max_user_congthresh,struct fuse_mount_data {int fd;unsigned rootmode; -unsigned user_id; -unsigned group_id; +kuid_t user_id; +kgid_t group_id;unsigned fd_present:1 ;unsigned rootmode_present:1 ;unsigned user_id_present:1 ;
@@ -164,8 +164,8 @@ void fuse_change_attributes_common(struct inode *inode, struct fuse_attr *attr, inode->i_ino =fuse_squash_ino (attr->ino); inode->i_mode = (inode->i_mode &S_IFMT ) | (attr->mode &07777 );set_nlink (inode, attr->nlink); - inode->i_uid = attr->uid; - inode->i_gid = attr->gid; + inode->i_uid =make_kuid (&init_user_ns, attr->uid); + inode->i_gid =make_kgid (&init_user_ns, attr->gid); inode->i_blocks = attr->blocks; inode->i_atime.tv_sec = attr->atime; inode->i_atime.tv_nsec = attr->atimensec;
@@ -492,14 +492,18 @@ static int parse_fuse_opt(char *opt, struct fuse_mount_data *d, int is_bdev)case OPT_USER_ID :if (match_int (&args[0 ], &value))return 0 ; - d->user_id = value; + d->user_id =make_kuid (current_user_ns (), value); +if (!uid_valid (d->user_id)) +return 0 ; d->user_id_present =1 ;break ;case OPT_GROUP_ID :if (match_int (&args[0 ], &value))return 0 ; - d->group_id = value; + d->group_id =make_kgid (current_user_ns (), value); +if (!gid_valid (d->group_id)) +return 0 ; d->group_id_present =1 ;break ;
@@ -540,8 +544,8 @@ static int fuse_show_options(struct seq_file *m, struct dentry *root)struct super_block *sb = root->d_sb;struct fuse_conn *fc =get_fuse_conn_super (sb); -seq_printf (m,",user_id=%u" , fc->user_id); -seq_printf (m,",group_id=%u" , fc->group_id); +seq_printf (m,",user_id=%u" ,from_kuid_munged (&init_user_ns, fc->user_id)); +seq_printf (m,",group_id=%u" ,from_kgid_munged (&init_user_ns, fc->group_id));if (fc->flags &FUSE_DEFAULT_PERMISSIONS )seq_puts (m,",default_permissions" );if (fc->flags &FUSE_ALLOW_OTHER )
@@ -989,7 +993,8 @@ static int fuse_fill_super(struct super_block *sb, void *data, int silent)if (!file) goto err; -if (file->f_op != &fuse_dev_operations) +if ((file->f_op != &fuse_dev_operations) || + (file->f_cred->user_ns != &init_user_ns)) goto err_fput; fc =kmalloc (sizeof (*fc),GFP_KERNEL );
init/Kconfig +0 -1
@@ -1007,7 +1007,6 @@ config UIDGID_CONVERTED depends on CEPH_FS = n depends on CIFS = n depends on CODA_FS = n - depends on FUSE_FS = n depends on GFS2_FS = n depends on NCP_FS = n depends on NFSD = n
Signature · 1 line
1.7.5.4
Miklos Szeredi
maintainerQuoted 8 lines · [email protected] (Eric W. Biederman) writes:
Use
kuid_tandkgid_tin structfuse_connand structfuse_mount_data.The connection between between a fuse filesystem and a fuse daemon is established when a fuse filesystem is mounted and provided with a file descriptor the fuse daemon created by opening
/dev/fuse.For now restrict the communication of uids and gids between the fuse filesystem and the fuse daemon to the initial user namespace.
Why?
I think far more logical would be to limit a single instance of the filesystem and the daemon to an arbitrary but *single* namespace. I.e. one fuse_conn <-> one user namespace.
Is there a reason to treat the initial namespace specially?
Thanks,
Miklos
Quoted 11 lines · Miklos Szeredi
Quoted 8 lines · ebiederm-aS9lmoZGLiVWk0Htik3J/[email protected] (Eric W. Biederman) writes:
Use
kuid_tandkgid_tin structfuse_connand structfuse_mount_data.The connection between between a fuse filesystem and a fuse daemon is established when a fuse filesystem is mounted and provided with a file descriptor the fuse daemon created by opening
/dev/fuse.For now restrict the communication of uids and gids between the fuse filesystem and the fuse daemon to the initial user namespace.
Quoted 7 lines
Why?
I think far more logical would be to limit a single instance of the filesystem and the daemon to an arbitrary but *single* namespace. I.e. one
fuse_conn<-> one user namespace.Is there a reason to treat the initial namespace specially?
Stepwise making this work.
The initial user namespace is special in that is the only user namespace you can currently mount a filesystem in.
My goal with the posted patch is to support users on the other side of the vfs that are in multiple user namespaces. Which basically means accepting kuid_t and kgid_t values and converting them to uid_t and gid_t values where needed.
As for creating a version of fuse that can be mounted in different user namespaces and can communicate with the fuse daemon in the user namespace the filesystem was mounted in, I have a preliminary patch for that. My goal with that patch is to support unprivileged mounts of fuse without the help of a suid root mount program. But that patch is more involved and not quite ready yet.
I want to get to the point that I can enable fuse and user namespaces at the same time (what this patch allows) and then put in the effort to fully take advantage of user namespaces.
Eric