Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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>

+84 -4
+61 -3
fs/fuse/dir.c
··· 521 521 return err; 522 522 } 523 523 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, 525 581 umode_t mode) 526 582 { 527 583 struct fuse_conn *fc = get_fuse_conn_super(dentry->d_sb); ··· 586 530 587 531 if (fc->init_security) 588 532 err = get_security_context(dentry, mode, &ext); 533 + if (!err && fc->create_supp_group) 534 + err = get_create_supp_group(dir, &ext); 589 535 590 536 if (!err && ext.size) { 591 537 WARN_ON(args->in_numargs >= ARRAY_SIZE(args->in_args)); ··· 670 612 args.out_args[1].size = sizeof(outopen); 671 613 args.out_args[1].value = &outopen; 672 614 673 - err = get_create_ext(&args, entry, mode); 615 + err = get_create_ext(&args, dir, entry, mode); 674 616 if (err) 675 617 goto out_put_forget_req; 676 618 ··· 797 739 args->out_args[0].value = &outarg; 798 740 799 741 if (args->opcode != FUSE_LINK) { 800 - err = get_create_ext(args, entry, mode); 742 + err = get_create_ext(args, dir, entry, mode); 801 743 if (err) 802 744 goto out_put_forget_req; 803 745 }
+3
fs/fuse/fuse_i.h
··· 783 783 /* Initialize security xattrs when creating a new inode */ 784 784 unsigned int init_security:1; 785 785 786 + /* Add supplementary group info when creating a new inode */ 787 + unsigned int create_supp_group:1; 788 + 786 789 /* Does the filesystem support per inode DAX? */ 787 790 unsigned int inode_dax:1; 788 791
+3 -1
fs/fuse/inode.c
··· 1201 1201 fc->setxattr_ext = 1; 1202 1202 if (flags & FUSE_SECURITY_CTX) 1203 1203 fc->init_security = 1; 1204 + if (flags & FUSE_CREATE_SUPP_GROUP) 1205 + fc->create_supp_group = 1; 1204 1206 } else { 1205 1207 ra_pages = fc->max_read / PAGE_SIZE; 1206 1208 fc->no_lock = 1; ··· 1248 1246 FUSE_ABORT_ERROR | FUSE_MAX_PAGES | FUSE_CACHE_SYMLINKS | 1249 1247 FUSE_NO_OPENDIR_SUPPORT | FUSE_EXPLICIT_INVAL_DATA | 1250 1248 FUSE_HANDLE_KILLPRIV_V2 | FUSE_SETXATTR_EXT | FUSE_INIT_EXT | 1251 - FUSE_SECURITY_CTX; 1249 + FUSE_SECURITY_CTX | FUSE_CREATE_SUPP_GROUP; 1252 1250 #ifdef CONFIG_FUSE_DAX 1253 1251 if (fm->fc->dax) 1254 1252 flags |= FUSE_MAP_ALIGNMENT;
+17
include/uapi/linux/fuse.h
··· 204 204 * - add total_extlen to fuse_in_header 205 205 * - add FUSE_MAX_NR_SECCTX 206 206 * - add extension header 207 + * - add FUSE_EXT_GROUPS 208 + * - add FUSE_CREATE_SUPP_GROUP 207 209 */ 208 210 209 211 #ifndef _LINUX_FUSE_H ··· 367 365 * FUSE_SECURITY_CTX: add security context to create, mkdir, symlink, and 368 366 * mknod 369 367 * FUSE_HAS_INODE_DAX: use per inode DAX 368 + * FUSE_CREATE_SUPP_GROUP: add supplementary group info to create, mkdir, 369 + * symlink and mknod (single group that matches parent) 370 370 */ 371 371 #define FUSE_ASYNC_READ (1 << 0) 372 372 #define FUSE_POSIX_LOCKS (1 << 1) ··· 405 401 /* bits 32..63 get shifted down 32 bits into the flags2 field */ 406 402 #define FUSE_SECURITY_CTX (1ULL << 32) 407 403 #define FUSE_HAS_INODE_DAX (1ULL << 33) 404 + #define FUSE_CREATE_SUPP_GROUP (1ULL << 34) 408 405 409 406 /** 410 407 * CUSE INIT request/reply flags ··· 514 509 /** 515 510 * extension type 516 511 * FUSE_MAX_NR_SECCTX: maximum value of &fuse_secctx_header.nr_secctx 512 + * FUSE_EXT_GROUPS: &fuse_supp_groups extension 517 513 */ 518 514 enum fuse_ext_type { 519 515 /* Types 0..31 are reserved for fuse_secctx_header */ 520 516 FUSE_MAX_NR_SECCTX = 31, 517 + FUSE_EXT_GROUPS = 32, 521 518 }; 522 519 523 520 enum fuse_opcode { ··· 1078 1071 struct fuse_ext_header { 1079 1072 uint32_t size; 1080 1073 uint32_t type; 1074 + }; 1075 + 1076 + /** 1077 + * struct fuse_supp_groups - Supplementary group extension 1078 + * @nr_groups: number of supplementary groups 1079 + * @groups: flexible array of group IDs 1080 + */ 1081 + struct fuse_supp_groups { 1082 + uint32_t nr_groups; 1083 + uint32_t groups[]; 1081 1084 }; 1082 1085 1083 1086 #endif /* _LINUX_FUSE_H */