diff options
author | Miklos Szeredi <mszeredi@redhat.com> | 2022-11-10 17:46:33 +0300 |
---|---|---|
committer | Miklos Szeredi <mszeredi@redhat.com> | 2023-01-26 19:10:38 +0300 |
commit | 8ed7cb3f279fe67a93f407ee2ec3ea661a483a65 (patch) | |
tree | cdce22c462eab384ddfa139affcedfd28b7e93c8 /fs/fuse/dir.c | |
parent | 15d937d7ca8c55d2b0ce9116e20c780fdd0b67cc (diff) | |
download | linux-8ed7cb3f279fe67a93f407ee2ec3ea661a483a65.tar.xz |
fuse: optional supplementary group in create requests
Permission to create an object (create, mkdir, symlink, mknod) needs to
take supplementary groups into account.
Add a supplementary group request extension. This can contain an arbitrary
number of group IDs and can be added to any request. This extension is not
added to any request by default.
Add FUSE_CREATE_SUPP_GROUP init flag to enable supplementary group info in
creation requests. This adds just a single supplementary group that
matches the parent group in the case described above. In other cases the
extension is not added.
Signed-off-by: Miklos Szeredi <mszeredi@redhat.com>
Diffstat (limited to 'fs/fuse/dir.c')
-rw-r--r-- | fs/fuse/dir.c | 64 |
1 files changed, 61 insertions, 3 deletions
diff --git a/fs/fuse/dir.c b/fs/fuse/dir.c index fa6381f199b3..0c1d7e387f0d 100644 --- a/fs/fuse/dir.c +++ b/fs/fuse/dir.c @@ -521,7 +521,63 @@ out_err: return err; } -static int get_create_ext(struct fuse_args *args, struct dentry *dentry, +static void *extend_arg(struct fuse_in_arg *buf, u32 bytes) +{ + void *p; + u32 newlen = buf->size + bytes; + + p = krealloc(buf->value, newlen, GFP_KERNEL); + if (!p) { + kfree(buf->value); + buf->size = 0; + buf->value = NULL; + return NULL; + } + + memset(p + buf->size, 0, bytes); + buf->value = p; + buf->size = newlen; + + return p + newlen - bytes; +} + +static u32 fuse_ext_size(size_t size) +{ + return FUSE_REC_ALIGN(sizeof(struct fuse_ext_header) + size); +} + +/* + * This adds just a single supplementary group that matches the parent's group. + */ +static int get_create_supp_group(struct inode *dir, struct fuse_in_arg *ext) +{ + struct fuse_conn *fc = get_fuse_conn(dir); + struct fuse_ext_header *xh; + struct fuse_supp_groups *sg; + kgid_t kgid = dir->i_gid; + gid_t parent_gid = from_kgid(fc->user_ns, kgid); + u32 sg_len = fuse_ext_size(sizeof(*sg) + sizeof(sg->groups[0])); + + if (parent_gid == (gid_t) -1 || gid_eq(kgid, current_fsgid()) || + !in_group_p(kgid)) + return 0; + + xh = extend_arg(ext, sg_len); + if (!xh) + return -ENOMEM; + + xh->size = sg_len; + xh->type = FUSE_EXT_GROUPS; + + sg = (struct fuse_supp_groups *) &xh[1]; + sg->nr_groups = 1; + sg->groups[0] = parent_gid; + + return 0; +} + +static int get_create_ext(struct fuse_args *args, + struct inode *dir, struct dentry *dentry, umode_t mode) { struct fuse_conn *fc = get_fuse_conn_super(dentry->d_sb); @@ -530,6 +586,8 @@ static int get_create_ext(struct fuse_args *args, struct dentry *dentry, if (fc->init_security) err = get_security_context(dentry, mode, &ext); + if (!err && fc->create_supp_group) + err = get_create_supp_group(dir, &ext); if (!err && ext.size) { WARN_ON(args->in_numargs >= ARRAY_SIZE(args->in_args)); @@ -612,7 +670,7 @@ static int fuse_create_open(struct inode *dir, struct dentry *entry, args.out_args[1].size = sizeof(outopen); args.out_args[1].value = &outopen; - err = get_create_ext(&args, entry, mode); + err = get_create_ext(&args, dir, entry, mode); if (err) goto out_put_forget_req; @@ -739,7 +797,7 @@ static int create_new_entry(struct fuse_mount *fm, struct fuse_args *args, args->out_args[0].value = &outarg; if (args->opcode != FUSE_LINK) { - err = get_create_ext(args, entry, mode); + err = get_create_ext(args, dir, entry, mode); if (err) goto out_put_forget_req; } |