Skip to content

Commit 8ed7cb3

Browse files
author
Miklos Szeredi
committed
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 <[email protected]>
1 parent 15d937d commit 8ed7cb3

File tree

4 files changed

+84
-4
lines changed

4 files changed

+84
-4
lines changed

fs/fuse/dir.c

Lines changed: 61 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -521,7 +521,63 @@ static int get_security_context(struct dentry *entry, umode_t mode,
521521
return err;
522522
}
523523

524-
static int get_create_ext(struct fuse_args *args, struct dentry *dentry,
524+
static void *extend_arg(struct fuse_in_arg *buf, u32 bytes)
525+
{
526+
void *p;
527+
u32 newlen = buf->size + bytes;
528+
529+
p = krealloc(buf->value, newlen, GFP_KERNEL);
530+
if (!p) {
531+
kfree(buf->value);
532+
buf->size = 0;
533+
buf->value = NULL;
534+
return NULL;
535+
}
536+
537+
memset(p + buf->size, 0, bytes);
538+
buf->value = p;
539+
buf->size = newlen;
540+
541+
return p + newlen - bytes;
542+
}
543+
544+
static u32 fuse_ext_size(size_t size)
545+
{
546+
return FUSE_REC_ALIGN(sizeof(struct fuse_ext_header) + size);
547+
}
548+
549+
/*
550+
* This adds just a single supplementary group that matches the parent's group.
551+
*/
552+
static int get_create_supp_group(struct inode *dir, struct fuse_in_arg *ext)
553+
{
554+
struct fuse_conn *fc = get_fuse_conn(dir);
555+
struct fuse_ext_header *xh;
556+
struct fuse_supp_groups *sg;
557+
kgid_t kgid = dir->i_gid;
558+
gid_t parent_gid = from_kgid(fc->user_ns, kgid);
559+
u32 sg_len = fuse_ext_size(sizeof(*sg) + sizeof(sg->groups[0]));
560+
561+
if (parent_gid == (gid_t) -1 || gid_eq(kgid, current_fsgid()) ||
562+
!in_group_p(kgid))
563+
return 0;
564+
565+
xh = extend_arg(ext, sg_len);
566+
if (!xh)
567+
return -ENOMEM;
568+
569+
xh->size = sg_len;
570+
xh->type = FUSE_EXT_GROUPS;
571+
572+
sg = (struct fuse_supp_groups *) &xh[1];
573+
sg->nr_groups = 1;
574+
sg->groups[0] = parent_gid;
575+
576+
return 0;
577+
}
578+
579+
static int get_create_ext(struct fuse_args *args,
580+
struct inode *dir, struct dentry *dentry,
525581
umode_t mode)
526582
{
527583
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,
530586

531587
if (fc->init_security)
532588
err = get_security_context(dentry, mode, &ext);
589+
if (!err && fc->create_supp_group)
590+
err = get_create_supp_group(dir, &ext);
533591

534592
if (!err && ext.size) {
535593
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,
612670
args.out_args[1].size = sizeof(outopen);
613671
args.out_args[1].value = &outopen;
614672

615-
err = get_create_ext(&args, entry, mode);
673+
err = get_create_ext(&args, dir, entry, mode);
616674
if (err)
617675
goto out_put_forget_req;
618676

@@ -739,7 +797,7 @@ static int create_new_entry(struct fuse_mount *fm, struct fuse_args *args,
739797
args->out_args[0].value = &outarg;
740798

741799
if (args->opcode != FUSE_LINK) {
742-
err = get_create_ext(args, entry, mode);
800+
err = get_create_ext(args, dir, entry, mode);
743801
if (err)
744802
goto out_put_forget_req;
745803
}

fs/fuse/fuse_i.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -783,6 +783,9 @@ struct fuse_conn {
783783
/* Initialize security xattrs when creating a new inode */
784784
unsigned int init_security:1;
785785

786+
/* Add supplementary group info when creating a new inode */
787+
unsigned int create_supp_group:1;
788+
786789
/* Does the filesystem support per inode DAX? */
787790
unsigned int inode_dax:1;
788791

fs/fuse/inode.c

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1201,6 +1201,8 @@ static void process_init_reply(struct fuse_mount *fm, struct fuse_args *args,
12011201
fc->setxattr_ext = 1;
12021202
if (flags & FUSE_SECURITY_CTX)
12031203
fc->init_security = 1;
1204+
if (flags & FUSE_CREATE_SUPP_GROUP)
1205+
fc->create_supp_group = 1;
12041206
} else {
12051207
ra_pages = fc->max_read / PAGE_SIZE;
12061208
fc->no_lock = 1;
@@ -1246,7 +1248,7 @@ void fuse_send_init(struct fuse_mount *fm)
12461248
FUSE_ABORT_ERROR | FUSE_MAX_PAGES | FUSE_CACHE_SYMLINKS |
12471249
FUSE_NO_OPENDIR_SUPPORT | FUSE_EXPLICIT_INVAL_DATA |
12481250
FUSE_HANDLE_KILLPRIV_V2 | FUSE_SETXATTR_EXT | FUSE_INIT_EXT |
1249-
FUSE_SECURITY_CTX;
1251+
FUSE_SECURITY_CTX | FUSE_CREATE_SUPP_GROUP;
12501252
#ifdef CONFIG_FUSE_DAX
12511253
if (fm->fc->dax)
12521254
flags |= FUSE_MAP_ALIGNMENT;

include/uapi/linux/fuse.h

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -204,6 +204,8 @@
204204
* - add total_extlen to fuse_in_header
205205
* - add FUSE_MAX_NR_SECCTX
206206
* - add extension header
207+
* - add FUSE_EXT_GROUPS
208+
* - add FUSE_CREATE_SUPP_GROUP
207209
*/
208210

209211
#ifndef _LINUX_FUSE_H
@@ -365,6 +367,8 @@ struct fuse_file_lock {
365367
* FUSE_SECURITY_CTX: add security context to create, mkdir, symlink, and
366368
* mknod
367369
* FUSE_HAS_INODE_DAX: use per inode DAX
370+
* FUSE_CREATE_SUPP_GROUP: add supplementary group info to create, mkdir,
371+
* symlink and mknod (single group that matches parent)
368372
*/
369373
#define FUSE_ASYNC_READ (1 << 0)
370374
#define FUSE_POSIX_LOCKS (1 << 1)
@@ -401,6 +405,7 @@ struct fuse_file_lock {
401405
/* bits 32..63 get shifted down 32 bits into the flags2 field */
402406
#define FUSE_SECURITY_CTX (1ULL << 32)
403407
#define FUSE_HAS_INODE_DAX (1ULL << 33)
408+
#define FUSE_CREATE_SUPP_GROUP (1ULL << 34)
404409

405410
/**
406411
* CUSE INIT request/reply flags
@@ -509,10 +514,12 @@ struct fuse_file_lock {
509514
/**
510515
* extension type
511516
* FUSE_MAX_NR_SECCTX: maximum value of &fuse_secctx_header.nr_secctx
517+
* FUSE_EXT_GROUPS: &fuse_supp_groups extension
512518
*/
513519
enum fuse_ext_type {
514520
/* Types 0..31 are reserved for fuse_secctx_header */
515521
FUSE_MAX_NR_SECCTX = 31,
522+
FUSE_EXT_GROUPS = 32,
516523
};
517524

518525
enum fuse_opcode {
@@ -1073,4 +1080,14 @@ struct fuse_ext_header {
10731080
uint32_t type;
10741081
};
10751082

1083+
/**
1084+
* struct fuse_supp_groups - Supplementary group extension
1085+
* @nr_groups: number of supplementary groups
1086+
* @groups: flexible array of group IDs
1087+
*/
1088+
struct fuse_supp_groups {
1089+
uint32_t nr_groups;
1090+
uint32_t groups[];
1091+
};
1092+
10761093
#endif /* _LINUX_FUSE_H */

0 commit comments

Comments
 (0)