Skip to content

Commit 3e2b6fd

Browse files
rhvgoyalMiklos Szeredi
authored andcommitted
fuse: send security context of inode on file
When a new inode is created, send its security context to server along with creation request (FUSE_CREAT, FUSE_MKNOD, FUSE_MKDIR and FUSE_SYMLINK). This gives server an opportunity to create new file and set security context (possibly atomically). In all the configurations it might not be possible to set context atomically. Like nfs and ceph, use security_dentry_init_security() to dermine security context of inode and send it with create, mkdir, mknod, and symlink requests. Following is the information sent to server. fuse_sectx_header, fuse_secctx, xattr_name, security_context - struct fuse_secctx_header This contains total number of security contexts being sent and total size of all the security contexts (including size of fuse_secctx_header). - struct fuse_secctx This contains size of security context which follows this structure. There is one fuse_secctx instance per security context. - xattr name string This string represents name of xattr which should be used while setting security context. - security context This is the actual security context whose size is specified in fuse_secctx struct. Also add the FUSE_SECURITY_CTX flag for the `flags` field of the fuse_init_out struct. When this flag is set the kernel will append the security context for a newly created inode to the request (create, mkdir, mknod, and symlink). The server is responsible for ensuring that the inode appears atomically (preferrably) with the requested security context. For example, If the server is using SELinux and backed by a "real" linux file system that supports extended attributes it can write the security context value to /proc/thread-self/attr/fscreate before making the syscall to create the inode. This patch is based on patch from Chirantan Ekbote <[email protected]> Signed-off-by: Vivek Goyal <[email protected]> Signed-off-by: Miklos Szeredi <[email protected]>
1 parent 53db289 commit 3e2b6fd

File tree

4 files changed

+130
-3
lines changed

4 files changed

+130
-3
lines changed

fs/fuse/dir.c

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,9 @@
1717
#include <linux/xattr.h>
1818
#include <linux/iversion.h>
1919
#include <linux/posix_acl.h>
20+
#include <linux/security.h>
21+
#include <linux/types.h>
22+
#include <linux/kernel.h>
2023

2124
static void fuse_advise_use_readdirplus(struct inode *dir)
2225
{
@@ -456,6 +459,62 @@ static struct dentry *fuse_lookup(struct inode *dir, struct dentry *entry,
456459
return ERR_PTR(err);
457460
}
458461

462+
static int get_security_context(struct dentry *entry, umode_t mode,
463+
void **security_ctx, u32 *security_ctxlen)
464+
{
465+
struct fuse_secctx *fctx;
466+
struct fuse_secctx_header *header;
467+
void *ctx = NULL, *ptr;
468+
u32 ctxlen, total_len = sizeof(*header);
469+
int err, nr_ctx = 0;
470+
const char *name;
471+
size_t namelen;
472+
473+
err = security_dentry_init_security(entry, mode, &entry->d_name,
474+
&name, &ctx, &ctxlen);
475+
if (err) {
476+
if (err != -EOPNOTSUPP)
477+
goto out_err;
478+
/* No LSM is supporting this security hook. Ignore error */
479+
ctxlen = 0;
480+
ctx = NULL;
481+
}
482+
483+
if (ctxlen) {
484+
nr_ctx = 1;
485+
namelen = strlen(name) + 1;
486+
err = -EIO;
487+
if (WARN_ON(namelen > XATTR_NAME_MAX + 1 || ctxlen > S32_MAX))
488+
goto out_err;
489+
total_len += FUSE_REC_ALIGN(sizeof(*fctx) + namelen + ctxlen);
490+
}
491+
492+
err = -ENOMEM;
493+
header = ptr = kzalloc(total_len, GFP_KERNEL);
494+
if (!ptr)
495+
goto out_err;
496+
497+
header->nr_secctx = nr_ctx;
498+
header->size = total_len;
499+
ptr += sizeof(*header);
500+
if (nr_ctx) {
501+
fctx = ptr;
502+
fctx->size = ctxlen;
503+
ptr += sizeof(*fctx);
504+
505+
strcpy(ptr, name);
506+
ptr += namelen;
507+
508+
memcpy(ptr, ctx, ctxlen);
509+
}
510+
*security_ctxlen = total_len;
511+
*security_ctx = header;
512+
err = 0;
513+
out_err:
514+
kfree(ctx);
515+
return err;
516+
}
517+
459518
/*
460519
* Atomic create+open operation
461520
*
@@ -476,6 +535,8 @@ static int fuse_create_open(struct inode *dir, struct dentry *entry,
476535
struct fuse_entry_out outentry;
477536
struct fuse_inode *fi;
478537
struct fuse_file *ff;
538+
void *security_ctx = NULL;
539+
u32 security_ctxlen;
479540

480541
/* Userspace expects S_IFREG in create mode */
481542
BUG_ON((mode & S_IFMT) != S_IFREG);
@@ -517,7 +578,20 @@ static int fuse_create_open(struct inode *dir, struct dentry *entry,
517578
args.out_args[0].value = &outentry;
518579
args.out_args[1].size = sizeof(outopen);
519580
args.out_args[1].value = &outopen;
581+
582+
if (fm->fc->init_security) {
583+
err = get_security_context(entry, mode, &security_ctx,
584+
&security_ctxlen);
585+
if (err)
586+
goto out_put_forget_req;
587+
588+
args.in_numargs = 3;
589+
args.in_args[2].size = security_ctxlen;
590+
args.in_args[2].value = security_ctx;
591+
}
592+
520593
err = fuse_simple_request(fm, &args);
594+
kfree(security_ctx);
521595
if (err)
522596
goto out_free_ff;
523597

@@ -620,6 +694,8 @@ static int create_new_entry(struct fuse_mount *fm, struct fuse_args *args,
620694
struct dentry *d;
621695
int err;
622696
struct fuse_forget_link *forget;
697+
void *security_ctx = NULL;
698+
u32 security_ctxlen;
623699

624700
if (fuse_is_bad(dir))
625701
return -EIO;
@@ -633,7 +709,22 @@ static int create_new_entry(struct fuse_mount *fm, struct fuse_args *args,
633709
args->out_numargs = 1;
634710
args->out_args[0].size = sizeof(outarg);
635711
args->out_args[0].value = &outarg;
712+
713+
if (fm->fc->init_security && args->opcode != FUSE_LINK) {
714+
err = get_security_context(entry, mode, &security_ctx,
715+
&security_ctxlen);
716+
if (err)
717+
goto out_put_forget_req;
718+
719+
BUG_ON(args->in_numargs != 2);
720+
721+
args->in_numargs = 3;
722+
args->in_args[2].size = security_ctxlen;
723+
args->in_args[2].value = security_ctx;
724+
}
725+
636726
err = fuse_simple_request(fm, args);
727+
kfree(security_ctx);
637728
if (err)
638729
goto out_put_forget_req;
639730

fs/fuse/fuse_i.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -765,6 +765,9 @@ struct fuse_conn {
765765
/* Propagate syncfs() to server */
766766
unsigned int sync_fs:1;
767767

768+
/* Initialize security xattrs when creating a new inode */
769+
unsigned int init_security:1;
770+
768771
/** The number of requests waiting for completion */
769772
atomic_t num_waiting;
770773

fs/fuse/inode.c

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1178,6 +1178,8 @@ static void process_init_reply(struct fuse_mount *fm, struct fuse_args *args,
11781178
}
11791179
if (flags & FUSE_SETXATTR_EXT)
11801180
fc->setxattr_ext = 1;
1181+
if (flags & FUSE_SECURITY_CTX)
1182+
fc->init_security = 1;
11811183
} else {
11821184
ra_pages = fc->max_read / PAGE_SIZE;
11831185
fc->no_lock = 1;
@@ -1222,7 +1224,8 @@ void fuse_send_init(struct fuse_mount *fm)
12221224
FUSE_PARALLEL_DIROPS | FUSE_HANDLE_KILLPRIV | FUSE_POSIX_ACL |
12231225
FUSE_ABORT_ERROR | FUSE_MAX_PAGES | FUSE_CACHE_SYMLINKS |
12241226
FUSE_NO_OPENDIR_SUPPORT | FUSE_EXPLICIT_INVAL_DATA |
1225-
FUSE_HANDLE_KILLPRIV_V2 | FUSE_SETXATTR_EXT | FUSE_INIT_EXT;
1227+
FUSE_HANDLE_KILLPRIV_V2 | FUSE_SETXATTR_EXT | FUSE_INIT_EXT |
1228+
FUSE_SECURITY_CTX;
12261229
#ifdef CONFIG_FUSE_DAX
12271230
if (fm->fc->dax)
12281231
flags |= FUSE_MAP_ALIGNMENT;

include/uapi/linux/fuse.h

Lines changed: 32 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,8 @@
191191
* 7.36
192192
* - extend fuse_init_in with reserved fields, add FUSE_INIT_EXT init flag
193193
* - add flags2 to fuse_init_in and fuse_init_out
194+
* - add FUSE_SECURITY_CTX init flag
195+
* - add security context to create, mkdir, symlink, and mknod requests
194196
*/
195197

196198
#ifndef _LINUX_FUSE_H
@@ -347,6 +349,8 @@ struct fuse_file_lock {
347349
* FUSE_SETXATTR_EXT: Server supports extended struct fuse_setxattr_in
348350
* FUSE_INIT_EXT: extended fuse_init_in request
349351
* FUSE_INIT_RESERVED: reserved, do not use
352+
* FUSE_SECURITY_CTX: add security context to create, mkdir, symlink, and
353+
* mknod
350354
*/
351355
#define FUSE_ASYNC_READ (1 << 0)
352356
#define FUSE_POSIX_LOCKS (1 << 1)
@@ -381,6 +385,7 @@ struct fuse_file_lock {
381385
#define FUSE_INIT_EXT (1 << 30)
382386
#define FUSE_INIT_RESERVED (1 << 31)
383387
/* bits 32..63 get shifted down 32 bits into the flags2 field */
388+
#define FUSE_SECURITY_CTX (1ULL << 32)
384389

385390
/**
386391
* CUSE INIT request/reply flags
@@ -877,9 +882,12 @@ struct fuse_dirent {
877882
char name[];
878883
};
879884

880-
#define FUSE_NAME_OFFSET offsetof(struct fuse_dirent, name)
881-
#define FUSE_DIRENT_ALIGN(x) \
885+
/* Align variable length records to 64bit boundary */
886+
#define FUSE_REC_ALIGN(x) \
882887
(((x) + sizeof(uint64_t) - 1) & ~(sizeof(uint64_t) - 1))
888+
889+
#define FUSE_NAME_OFFSET offsetof(struct fuse_dirent, name)
890+
#define FUSE_DIRENT_ALIGN(x) FUSE_REC_ALIGN(x)
883891
#define FUSE_DIRENT_SIZE(d) \
884892
FUSE_DIRENT_ALIGN(FUSE_NAME_OFFSET + (d)->namelen)
885893

@@ -996,4 +1004,26 @@ struct fuse_syncfs_in {
9961004
uint64_t padding;
9971005
};
9981006

1007+
/*
1008+
* For each security context, send fuse_secctx with size of security context
1009+
* fuse_secctx will be followed by security context name and this in turn
1010+
* will be followed by actual context label.
1011+
* fuse_secctx, name, context
1012+
*/
1013+
struct fuse_secctx {
1014+
uint32_t size;
1015+
uint32_t padding;
1016+
};
1017+
1018+
/*
1019+
* Contains the information about how many fuse_secctx structures are being
1020+
* sent and what's the total size of all security contexts (including
1021+
* size of fuse_secctx_header).
1022+
*
1023+
*/
1024+
struct fuse_secctx_header {
1025+
uint32_t size;
1026+
uint32_t nr_secctx;
1027+
};
1028+
9991029
#endif /* _LINUX_FUSE_H */

0 commit comments

Comments
 (0)