All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v3 0/1] fuse: Send file/inode security context during creation
@ 2021-11-10 22:55 ` Vivek Goyal
  0 siblings, 0 replies; 10+ messages in thread
From: Vivek Goyal @ 2021-11-10 22:55 UTC (permalink / raw)
  To: miklos
  Cc: linux-fsdevel, selinux, linux-security-module, virtio-fs,
	chirantan, vgoyal, stephen.smalley.work, dwalsh, casey, omosnace

Hi,

This is V3 of the patch. V2 was posted here.

https://lore.kernel.org/linux-fsdevel/20210924192442.916927-1-vgoyal@redhat.com/

I took care of feedback from Miklos. Changes since v2 are.

- Merged two patches into one. (Miklos)
- Renamed fuse_secctxs to fuse_secctx_header (Miklos)
- Added total size of all contexts (including fuse_secctx_header) field
  in fuse_secctx_header. (Miklos)
- Fixed changelogs (Casey Schaufler)
- Removed one unnecessary if condition check (Miklos)
- Removed a function argument to function create_new_entry() (Miklos)

Thanks
Vivek

Vivek Goyal (1):
  fuse: Send security context of inode on file creation

 fs/fuse/dir.c             | 103 ++++++++++++++++++++++++++++++++++++++
 fs/fuse/fuse_i.h          |   3 ++
 fs/fuse/inode.c           |   4 +-
 include/uapi/linux/fuse.h |  31 +++++++++++-
 4 files changed, 139 insertions(+), 2 deletions(-)

-- 
2.31.1


^ permalink raw reply	[flat|nested] 10+ messages in thread

* [Virtio-fs] [PATCH v3 0/1] fuse: Send file/inode security context during creation
@ 2021-11-10 22:55 ` Vivek Goyal
  0 siblings, 0 replies; 10+ messages in thread
From: Vivek Goyal @ 2021-11-10 22:55 UTC (permalink / raw)
  To: miklos
  Cc: selinux, casey, stephen.smalley.work, omosnace, virtio-fs,
	linux-security-module, linux-fsdevel, vgoyal

Hi,

This is V3 of the patch. V2 was posted here.

https://lore.kernel.org/linux-fsdevel/20210924192442.916927-1-vgoyal@redhat.com/

I took care of feedback from Miklos. Changes since v2 are.

- Merged two patches into one. (Miklos)
- Renamed fuse_secctxs to fuse_secctx_header (Miklos)
- Added total size of all contexts (including fuse_secctx_header) field
  in fuse_secctx_header. (Miklos)
- Fixed changelogs (Casey Schaufler)
- Removed one unnecessary if condition check (Miklos)
- Removed a function argument to function create_new_entry() (Miklos)

Thanks
Vivek

Vivek Goyal (1):
  fuse: Send security context of inode on file creation

 fs/fuse/dir.c             | 103 ++++++++++++++++++++++++++++++++++++++
 fs/fuse/fuse_i.h          |   3 ++
 fs/fuse/inode.c           |   4 +-
 include/uapi/linux/fuse.h |  31 +++++++++++-
 4 files changed, 139 insertions(+), 2 deletions(-)

-- 
2.31.1


^ permalink raw reply	[flat|nested] 10+ messages in thread

* [PATCH v3 1/1] fuse: Send security context of inode on file creation
  2021-11-10 22:55 ` [Virtio-fs] " Vivek Goyal
@ 2021-11-10 22:55   ` Vivek Goyal
  -1 siblings, 0 replies; 10+ messages in thread
From: Vivek Goyal @ 2021-11-10 22:55 UTC (permalink / raw)
  To: miklos
  Cc: linux-fsdevel, selinux, linux-security-module, virtio-fs,
	chirantan, vgoyal, stephen.smalley.work, dwalsh, casey, omosnace

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 <chirantan@chromium.org>.

Signed-off-by: Vivek Goyal <vgoyal@redhat.com>
---
 fs/fuse/dir.c             | 103 ++++++++++++++++++++++++++++++++++++++
 fs/fuse/fuse_i.h          |   3 ++
 fs/fuse/inode.c           |   4 +-
 include/uapi/linux/fuse.h |  31 +++++++++++-
 4 files changed, 139 insertions(+), 2 deletions(-)

diff --git a/fs/fuse/dir.c b/fs/fuse/dir.c
index d9b977c0f38d..69398eb7b325 100644
--- a/fs/fuse/dir.c
+++ b/fs/fuse/dir.c
@@ -17,6 +17,9 @@
 #include <linux/xattr.h>
 #include <linux/iversion.h>
 #include <linux/posix_acl.h>
+#include <linux/security.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
 
 static void fuse_advise_use_readdirplus(struct inode *dir)
 {
@@ -456,6 +459,69 @@ static struct dentry *fuse_lookup(struct inode *dir, struct dentry *entry,
 	return ERR_PTR(err);
 }
 
+static int get_security_context(struct dentry *entry, umode_t mode,
+				void **security_ctx, u32 *security_ctxlen)
+{
+	struct fuse_secctx *fsecctx;
+	struct fuse_secctx_header *fsecctx_header;
+	void *ctx, *full_ctx;
+	u32 ctxlen, full_ctxlen;
+	int err = 0;
+	const char *name;
+
+	err = security_dentry_init_security(entry, mode, &entry->d_name,
+					    &name, &ctx, &ctxlen);
+	if (err) {
+		if (err != -EOPNOTSUPP)
+			goto out_err;
+		/* No LSM is supporting this security hook. Ignore error */
+		err = 0;
+		ctxlen = 0;
+	}
+
+	if (ctxlen > 0) {
+		void *ptr;
+
+		full_ctxlen = sizeof(*fsecctx_header) + sizeof(*fsecctx) +
+			      strlen(name) + ctxlen + 1;
+		full_ctx = kzalloc(full_ctxlen, GFP_KERNEL);
+		if (!full_ctx) {
+			err = -ENOMEM;
+			kfree(ctx);
+			goto out_err;
+		}
+
+		ptr = full_ctx;
+		fsecctx_header = (struct fuse_secctx_header*) ptr;
+		fsecctx_header->nr_secctx = 1;
+		fsecctx_header->size = full_ctxlen;
+		ptr += sizeof(*fsecctx_header);
+
+		fsecctx = (struct fuse_secctx*) ptr;
+		fsecctx->size = ctxlen;
+		ptr += sizeof(*fsecctx);
+
+		strcpy(ptr, name);
+		ptr += strlen(name) + 1;
+		memcpy(ptr, ctx, ctxlen);
+		kfree(ctx);
+	} else {
+		full_ctxlen = sizeof(*fsecctx_header);
+		full_ctx = kzalloc(full_ctxlen, GFP_KERNEL);
+		if (!full_ctx) {
+			err = -ENOMEM;
+			goto out_err;
+		}
+		fsecctx_header = full_ctx;
+		fsecctx_header->size = full_ctxlen;
+	}
+
+	*security_ctxlen = full_ctxlen;
+	*security_ctx = full_ctx;
+out_err:
+	return err;
+}
+
 /*
  * Atomic create+open operation
  *
@@ -476,6 +542,8 @@ static int fuse_create_open(struct inode *dir, struct dentry *entry,
 	struct fuse_entry_out outentry;
 	struct fuse_inode *fi;
 	struct fuse_file *ff;
+	void *security_ctx = NULL;
+	u32 security_ctxlen;
 
 	/* Userspace expects S_IFREG in create mode */
 	BUG_ON((mode & S_IFMT) != S_IFREG);
@@ -517,6 +585,18 @@ static int fuse_create_open(struct inode *dir, struct dentry *entry,
 	args.out_args[0].value = &outentry;
 	args.out_args[1].size = sizeof(outopen);
 	args.out_args[1].value = &outopen;
+
+	if (fm->fc->init_security) {
+		err = get_security_context(entry, mode, &security_ctx,
+					   &security_ctxlen);
+		if (err)
+			goto out_put_forget_req;
+
+		args.in_numargs = 3;
+		args.in_args[2].size = security_ctxlen;
+		args.in_args[2].value = security_ctx;
+	}
+
 	err = fuse_simple_request(fm, &args);
 	if (err)
 		goto out_free_ff;
@@ -554,6 +634,7 @@ static int fuse_create_open(struct inode *dir, struct dentry *entry,
 
 out_free_ff:
 	fuse_file_free(ff);
+	kfree(security_ctx);
 out_put_forget_req:
 	kfree(forget);
 out_err:
@@ -620,6 +701,8 @@ static int create_new_entry(struct fuse_mount *fm, struct fuse_args *args,
 	struct dentry *d;
 	int err;
 	struct fuse_forget_link *forget;
+	void *security_ctx = NULL;
+	u32 security_ctxlen = 0;
 
 	if (fuse_is_bad(dir))
 		return -EIO;
@@ -633,7 +716,27 @@ static int create_new_entry(struct fuse_mount *fm, struct fuse_args *args,
 	args->out_numargs = 1;
 	args->out_args[0].size = sizeof(outarg);
 	args->out_args[0].value = &outarg;
+
+	if (fm->fc->init_security && args->opcode != FUSE_LINK) {
+		unsigned short idx = args->in_numargs;
+
+		if ((size_t)idx >= ARRAY_SIZE(args->in_args)) {
+			err = -ENOMEM;
+			goto out_put_forget_req;
+		}
+
+		err = get_security_context(entry, mode, &security_ctx,
+					   &security_ctxlen);
+		if (err)
+			goto out_put_forget_req;
+
+		args->in_args[idx].size = security_ctxlen;
+		args->in_args[idx].value = security_ctx;
+		args->in_numargs++;
+	}
+
 	err = fuse_simple_request(fm, args);
+	kfree(security_ctx);
 	if (err)
 		goto out_put_forget_req;
 
diff --git a/fs/fuse/fuse_i.h b/fs/fuse/fuse_i.h
index f55f9f94b1a4..0d257c4eeb70 100644
--- a/fs/fuse/fuse_i.h
+++ b/fs/fuse/fuse_i.h
@@ -765,6 +765,9 @@ struct fuse_conn {
 	/* Propagate syncfs() to server */
 	unsigned int sync_fs:1;
 
+	/* Initialize security xattrs when creating a new inode */
+	unsigned int init_security:1;
+
 	/** The number of requests waiting for completion */
 	atomic_t num_waiting;
 
diff --git a/fs/fuse/inode.c b/fs/fuse/inode.c
index 12d49a1914e8..40c5533243c0 100644
--- a/fs/fuse/inode.c
+++ b/fs/fuse/inode.c
@@ -1143,6 +1143,8 @@ static void process_init_reply(struct fuse_mount *fm, struct fuse_args *args,
 			}
 			if (arg->flags & FUSE_SETXATTR_EXT)
 				fc->setxattr_ext = 1;
+			if (arg->flags & FUSE_SECURITY_CTX)
+				fc->init_security = 1;
 		} else {
 			ra_pages = fc->max_read / PAGE_SIZE;
 			fc->no_lock = 1;
@@ -1186,7 +1188,7 @@ void fuse_send_init(struct fuse_mount *fm)
 		FUSE_PARALLEL_DIROPS | FUSE_HANDLE_KILLPRIV | FUSE_POSIX_ACL |
 		FUSE_ABORT_ERROR | FUSE_MAX_PAGES | FUSE_CACHE_SYMLINKS |
 		FUSE_NO_OPENDIR_SUPPORT | FUSE_EXPLICIT_INVAL_DATA |
-		FUSE_HANDLE_KILLPRIV_V2 | FUSE_SETXATTR_EXT;
+		FUSE_HANDLE_KILLPRIV_V2 | FUSE_SETXATTR_EXT | FUSE_SECURITY_CTX;
 #ifdef CONFIG_FUSE_DAX
 	if (fm->fc->dax)
 		ia->in.flags |= FUSE_MAP_ALIGNMENT;
diff --git a/include/uapi/linux/fuse.h b/include/uapi/linux/fuse.h
index 36ed092227fa..710cdf20608d 100644
--- a/include/uapi/linux/fuse.h
+++ b/include/uapi/linux/fuse.h
@@ -184,6 +184,10 @@
  *
  *  7.34
  *  - add FUSE_SYNCFS
+ *
+ *  7.35
+ *  - add FUSE_SECURITY_CTX flag for fuse_init_out
+ *  - add security context to create, mkdir, symlink, and mknod requests
  */
 
 #ifndef _LINUX_FUSE_H
@@ -219,7 +223,7 @@
 #define FUSE_KERNEL_VERSION 7
 
 /** Minor version number of this interface */
-#define FUSE_KERNEL_MINOR_VERSION 34
+#define FUSE_KERNEL_MINOR_VERSION 35
 
 /** The node ID of the root inode */
 #define FUSE_ROOT_ID 1
@@ -336,6 +340,8 @@ struct fuse_file_lock {
  *			write/truncate sgid is killed only if file has group
  *			execute permission. (Same as Linux VFS behavior).
  * FUSE_SETXATTR_EXT:	Server supports extended struct fuse_setxattr_in
+ * FUSE_SECURITY_CTX:	add security context to create, mkdir, symlink, and
+ * 			mknod
  */
 #define FUSE_ASYNC_READ		(1 << 0)
 #define FUSE_POSIX_LOCKS	(1 << 1)
@@ -367,6 +373,7 @@ struct fuse_file_lock {
 #define FUSE_SUBMOUNTS		(1 << 27)
 #define FUSE_HANDLE_KILLPRIV_V2	(1 << 28)
 #define FUSE_SETXATTR_EXT	(1 << 29)
+#define FUSE_SECURITY_CTX	(1 << 30)
 
 /**
  * CUSE INIT request/reply flags
@@ -979,4 +986,26 @@ struct fuse_syncfs_in {
 	uint64_t	padding;
 };
 
+/*
+ * For each security context, send fuse_secctx with size of security context
+ * fuse_secctx will be followed by security context name and this in turn
+ * will be followed by actual context label.
+ * fuse_secctx, name, context
+ * */
+struct fuse_secctx {
+	uint32_t	size;
+	uint32_t	padding;
+};
+
+/*
+ * Contains the information about how many fuse_secctx structures are being
+ * sent and what's the total size of all security contexts (including
+ * size of fuse_secctx_header).
+ *
+ */
+struct fuse_secctx_header {
+	uint32_t	size;
+	uint32_t	nr_secctx;
+};
+
 #endif /* _LINUX_FUSE_H */
-- 
2.31.1


^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [Virtio-fs] [PATCH v3 1/1] fuse: Send security context of inode on file creation
@ 2021-11-10 22:55   ` Vivek Goyal
  0 siblings, 0 replies; 10+ messages in thread
From: Vivek Goyal @ 2021-11-10 22:55 UTC (permalink / raw)
  To: miklos
  Cc: selinux, casey, stephen.smalley.work, omosnace, virtio-fs,
	linux-security-module, linux-fsdevel, vgoyal

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 <chirantan@chromium.org>.

Signed-off-by: Vivek Goyal <vgoyal@redhat.com>
---
 fs/fuse/dir.c             | 103 ++++++++++++++++++++++++++++++++++++++
 fs/fuse/fuse_i.h          |   3 ++
 fs/fuse/inode.c           |   4 +-
 include/uapi/linux/fuse.h |  31 +++++++++++-
 4 files changed, 139 insertions(+), 2 deletions(-)

diff --git a/fs/fuse/dir.c b/fs/fuse/dir.c
index d9b977c0f38d..69398eb7b325 100644
--- a/fs/fuse/dir.c
+++ b/fs/fuse/dir.c
@@ -17,6 +17,9 @@
 #include <linux/xattr.h>
 #include <linux/iversion.h>
 #include <linux/posix_acl.h>
+#include <linux/security.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
 
 static void fuse_advise_use_readdirplus(struct inode *dir)
 {
@@ -456,6 +459,69 @@ static struct dentry *fuse_lookup(struct inode *dir, struct dentry *entry,
 	return ERR_PTR(err);
 }
 
+static int get_security_context(struct dentry *entry, umode_t mode,
+				void **security_ctx, u32 *security_ctxlen)
+{
+	struct fuse_secctx *fsecctx;
+	struct fuse_secctx_header *fsecctx_header;
+	void *ctx, *full_ctx;
+	u32 ctxlen, full_ctxlen;
+	int err = 0;
+	const char *name;
+
+	err = security_dentry_init_security(entry, mode, &entry->d_name,
+					    &name, &ctx, &ctxlen);
+	if (err) {
+		if (err != -EOPNOTSUPP)
+			goto out_err;
+		/* No LSM is supporting this security hook. Ignore error */
+		err = 0;
+		ctxlen = 0;
+	}
+
+	if (ctxlen > 0) {
+		void *ptr;
+
+		full_ctxlen = sizeof(*fsecctx_header) + sizeof(*fsecctx) +
+			      strlen(name) + ctxlen + 1;
+		full_ctx = kzalloc(full_ctxlen, GFP_KERNEL);
+		if (!full_ctx) {
+			err = -ENOMEM;
+			kfree(ctx);
+			goto out_err;
+		}
+
+		ptr = full_ctx;
+		fsecctx_header = (struct fuse_secctx_header*) ptr;
+		fsecctx_header->nr_secctx = 1;
+		fsecctx_header->size = full_ctxlen;
+		ptr += sizeof(*fsecctx_header);
+
+		fsecctx = (struct fuse_secctx*) ptr;
+		fsecctx->size = ctxlen;
+		ptr += sizeof(*fsecctx);
+
+		strcpy(ptr, name);
+		ptr += strlen(name) + 1;
+		memcpy(ptr, ctx, ctxlen);
+		kfree(ctx);
+	} else {
+		full_ctxlen = sizeof(*fsecctx_header);
+		full_ctx = kzalloc(full_ctxlen, GFP_KERNEL);
+		if (!full_ctx) {
+			err = -ENOMEM;
+			goto out_err;
+		}
+		fsecctx_header = full_ctx;
+		fsecctx_header->size = full_ctxlen;
+	}
+
+	*security_ctxlen = full_ctxlen;
+	*security_ctx = full_ctx;
+out_err:
+	return err;
+}
+
 /*
  * Atomic create+open operation
  *
@@ -476,6 +542,8 @@ static int fuse_create_open(struct inode *dir, struct dentry *entry,
 	struct fuse_entry_out outentry;
 	struct fuse_inode *fi;
 	struct fuse_file *ff;
+	void *security_ctx = NULL;
+	u32 security_ctxlen;
 
 	/* Userspace expects S_IFREG in create mode */
 	BUG_ON((mode & S_IFMT) != S_IFREG);
@@ -517,6 +585,18 @@ static int fuse_create_open(struct inode *dir, struct dentry *entry,
 	args.out_args[0].value = &outentry;
 	args.out_args[1].size = sizeof(outopen);
 	args.out_args[1].value = &outopen;
+
+	if (fm->fc->init_security) {
+		err = get_security_context(entry, mode, &security_ctx,
+					   &security_ctxlen);
+		if (err)
+			goto out_put_forget_req;
+
+		args.in_numargs = 3;
+		args.in_args[2].size = security_ctxlen;
+		args.in_args[2].value = security_ctx;
+	}
+
 	err = fuse_simple_request(fm, &args);
 	if (err)
 		goto out_free_ff;
@@ -554,6 +634,7 @@ static int fuse_create_open(struct inode *dir, struct dentry *entry,
 
 out_free_ff:
 	fuse_file_free(ff);
+	kfree(security_ctx);
 out_put_forget_req:
 	kfree(forget);
 out_err:
@@ -620,6 +701,8 @@ static int create_new_entry(struct fuse_mount *fm, struct fuse_args *args,
 	struct dentry *d;
 	int err;
 	struct fuse_forget_link *forget;
+	void *security_ctx = NULL;
+	u32 security_ctxlen = 0;
 
 	if (fuse_is_bad(dir))
 		return -EIO;
@@ -633,7 +716,27 @@ static int create_new_entry(struct fuse_mount *fm, struct fuse_args *args,
 	args->out_numargs = 1;
 	args->out_args[0].size = sizeof(outarg);
 	args->out_args[0].value = &outarg;
+
+	if (fm->fc->init_security && args->opcode != FUSE_LINK) {
+		unsigned short idx = args->in_numargs;
+
+		if ((size_t)idx >= ARRAY_SIZE(args->in_args)) {
+			err = -ENOMEM;
+			goto out_put_forget_req;
+		}
+
+		err = get_security_context(entry, mode, &security_ctx,
+					   &security_ctxlen);
+		if (err)
+			goto out_put_forget_req;
+
+		args->in_args[idx].size = security_ctxlen;
+		args->in_args[idx].value = security_ctx;
+		args->in_numargs++;
+	}
+
 	err = fuse_simple_request(fm, args);
+	kfree(security_ctx);
 	if (err)
 		goto out_put_forget_req;
 
diff --git a/fs/fuse/fuse_i.h b/fs/fuse/fuse_i.h
index f55f9f94b1a4..0d257c4eeb70 100644
--- a/fs/fuse/fuse_i.h
+++ b/fs/fuse/fuse_i.h
@@ -765,6 +765,9 @@ struct fuse_conn {
 	/* Propagate syncfs() to server */
 	unsigned int sync_fs:1;
 
+	/* Initialize security xattrs when creating a new inode */
+	unsigned int init_security:1;
+
 	/** The number of requests waiting for completion */
 	atomic_t num_waiting;
 
diff --git a/fs/fuse/inode.c b/fs/fuse/inode.c
index 12d49a1914e8..40c5533243c0 100644
--- a/fs/fuse/inode.c
+++ b/fs/fuse/inode.c
@@ -1143,6 +1143,8 @@ static void process_init_reply(struct fuse_mount *fm, struct fuse_args *args,
 			}
 			if (arg->flags & FUSE_SETXATTR_EXT)
 				fc->setxattr_ext = 1;
+			if (arg->flags & FUSE_SECURITY_CTX)
+				fc->init_security = 1;
 		} else {
 			ra_pages = fc->max_read / PAGE_SIZE;
 			fc->no_lock = 1;
@@ -1186,7 +1188,7 @@ void fuse_send_init(struct fuse_mount *fm)
 		FUSE_PARALLEL_DIROPS | FUSE_HANDLE_KILLPRIV | FUSE_POSIX_ACL |
 		FUSE_ABORT_ERROR | FUSE_MAX_PAGES | FUSE_CACHE_SYMLINKS |
 		FUSE_NO_OPENDIR_SUPPORT | FUSE_EXPLICIT_INVAL_DATA |
-		FUSE_HANDLE_KILLPRIV_V2 | FUSE_SETXATTR_EXT;
+		FUSE_HANDLE_KILLPRIV_V2 | FUSE_SETXATTR_EXT | FUSE_SECURITY_CTX;
 #ifdef CONFIG_FUSE_DAX
 	if (fm->fc->dax)
 		ia->in.flags |= FUSE_MAP_ALIGNMENT;
diff --git a/include/uapi/linux/fuse.h b/include/uapi/linux/fuse.h
index 36ed092227fa..710cdf20608d 100644
--- a/include/uapi/linux/fuse.h
+++ b/include/uapi/linux/fuse.h
@@ -184,6 +184,10 @@
  *
  *  7.34
  *  - add FUSE_SYNCFS
+ *
+ *  7.35
+ *  - add FUSE_SECURITY_CTX flag for fuse_init_out
+ *  - add security context to create, mkdir, symlink, and mknod requests
  */
 
 #ifndef _LINUX_FUSE_H
@@ -219,7 +223,7 @@
 #define FUSE_KERNEL_VERSION 7
 
 /** Minor version number of this interface */
-#define FUSE_KERNEL_MINOR_VERSION 34
+#define FUSE_KERNEL_MINOR_VERSION 35
 
 /** The node ID of the root inode */
 #define FUSE_ROOT_ID 1
@@ -336,6 +340,8 @@ struct fuse_file_lock {
  *			write/truncate sgid is killed only if file has group
  *			execute permission. (Same as Linux VFS behavior).
  * FUSE_SETXATTR_EXT:	Server supports extended struct fuse_setxattr_in
+ * FUSE_SECURITY_CTX:	add security context to create, mkdir, symlink, and
+ * 			mknod
  */
 #define FUSE_ASYNC_READ		(1 << 0)
 #define FUSE_POSIX_LOCKS	(1 << 1)
@@ -367,6 +373,7 @@ struct fuse_file_lock {
 #define FUSE_SUBMOUNTS		(1 << 27)
 #define FUSE_HANDLE_KILLPRIV_V2	(1 << 28)
 #define FUSE_SETXATTR_EXT	(1 << 29)
+#define FUSE_SECURITY_CTX	(1 << 30)
 
 /**
  * CUSE INIT request/reply flags
@@ -979,4 +986,26 @@ struct fuse_syncfs_in {
 	uint64_t	padding;
 };
 
+/*
+ * For each security context, send fuse_secctx with size of security context
+ * fuse_secctx will be followed by security context name and this in turn
+ * will be followed by actual context label.
+ * fuse_secctx, name, context
+ * */
+struct fuse_secctx {
+	uint32_t	size;
+	uint32_t	padding;
+};
+
+/*
+ * Contains the information about how many fuse_secctx structures are being
+ * sent and what's the total size of all security contexts (including
+ * size of fuse_secctx_header).
+ *
+ */
+struct fuse_secctx_header {
+	uint32_t	size;
+	uint32_t	nr_secctx;
+};
+
 #endif /* _LINUX_FUSE_H */
-- 
2.31.1


^ permalink raw reply related	[flat|nested] 10+ messages in thread

* Re: [PATCH v3 1/1] fuse: Send security context of inode on file creation
  2021-11-10 22:55   ` [Virtio-fs] " Vivek Goyal
  (?)
@ 2021-11-11  9:54   ` kernel test robot
  2021-11-11 14:40     ` Vivek Goyal
  -1 siblings, 1 reply; 10+ messages in thread
From: kernel test robot @ 2021-11-11  9:54 UTC (permalink / raw)
  To: kbuild-all

[-- Attachment #1: Type: text/plain, Size: 5863 bytes --]

Hi Vivek,

I love your patch! Yet something to improve:

[auto build test ERROR on v5.15]
[cannot apply to mszeredi-fuse/for-next next-20211111]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch]

url:    https://github.com/0day-ci/linux/commits/Vivek-Goyal/fuse-Send-file-inode-security-context-during-creation/20211111-065645
base:   DEBUG invalid remote for branch v5.15 8bb7eca972ad531c9b149c0a51ab43a417385813
config: microblaze-buildonly-randconfig-r001-20211111 (attached as .config)
compiler: microblaze-linux-gcc (GCC) 11.2.0
reproduce (this is a W=1 build):
        wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
        chmod +x ~/bin/make.cross
        # https://github.com/0day-ci/linux/commit/3e94c8631307bf28c635eceedd868fe561666da5
        git remote add linux-review https://github.com/0day-ci/linux
        git fetch --no-tags linux-review Vivek-Goyal/fuse-Send-file-inode-security-context-during-creation/20211111-065645
        git checkout 3e94c8631307bf28c635eceedd868fe561666da5
        # save the attached .config to linux build tree
        COMPILER_INSTALL_PATH=$HOME/0day COMPILER=gcc-11.2.0 make.cross ARCH=microblaze 

If you fix the issue, kindly add following tag as appropriate
Reported-by: kernel test robot <lkp@intel.com>

All errors (new ones prefixed by >>):

   fs/fuse/dir.c: In function 'get_security_context':
>> fs/fuse/dir.c:473:45: error: passing argument 4 of 'security_dentry_init_security' from incompatible pointer type [-Werror=incompatible-pointer-types]
     473 |                                             &name, &ctx, &ctxlen);
         |                                             ^~~~~
         |                                             |
         |                                             const char **
   In file included from include/linux/fs_context.h:14,
                    from fs/fuse/dir.c:13:
   include/linux/security.h:742:57: note: expected 'void **' but argument is of type 'const char **'
     742 |                                                  void **ctx,
         |                                                  ~~~~~~~^~~
   fs/fuse/dir.c:473:52: error: passing argument 5 of 'security_dentry_init_security' from incompatible pointer type [-Werror=incompatible-pointer-types]
     473 |                                             &name, &ctx, &ctxlen);
         |                                                    ^~~~
         |                                                    |
         |                                                    void **
   In file included from include/linux/fs_context.h:14,
                    from fs/fuse/dir.c:13:
   include/linux/security.h:743:55: note: expected 'u32 *' {aka 'unsigned int *'} but argument is of type 'void **'
     743 |                                                  u32 *ctxlen)
         |                                                  ~~~~~^~~~~~
>> fs/fuse/dir.c:472:15: error: too many arguments to function 'security_dentry_init_security'
     472 |         err = security_dentry_init_security(entry, mode, &entry->d_name,
         |               ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   In file included from include/linux/fs_context.h:14,
                    from fs/fuse/dir.c:13:
   include/linux/security.h:739:19: note: declared here
     739 | static inline int security_dentry_init_security(struct dentry *dentry,
         |                   ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   cc1: some warnings being treated as errors


vim +/security_dentry_init_security +473 fs/fuse/dir.c

   461	
   462	static int get_security_context(struct dentry *entry, umode_t mode,
   463					void **security_ctx, u32 *security_ctxlen)
   464	{
   465		struct fuse_secctx *fsecctx;
   466		struct fuse_secctx_header *fsecctx_header;
   467		void *ctx, *full_ctx;
   468		u32 ctxlen, full_ctxlen;
   469		int err = 0;
   470		const char *name;
   471	
 > 472		err = security_dentry_init_security(entry, mode, &entry->d_name,
 > 473						    &name, &ctx, &ctxlen);
   474		if (err) {
   475			if (err != -EOPNOTSUPP)
   476				goto out_err;
   477			/* No LSM is supporting this security hook. Ignore error */
   478			err = 0;
   479			ctxlen = 0;
   480		}
   481	
   482		if (ctxlen > 0) {
   483			void *ptr;
   484	
   485			full_ctxlen = sizeof(*fsecctx_header) + sizeof(*fsecctx) +
   486				      strlen(name) + ctxlen + 1;
   487			full_ctx = kzalloc(full_ctxlen, GFP_KERNEL);
   488			if (!full_ctx) {
   489				err = -ENOMEM;
   490				kfree(ctx);
   491				goto out_err;
   492			}
   493	
   494			ptr = full_ctx;
   495			fsecctx_header = (struct fuse_secctx_header*) ptr;
   496			fsecctx_header->nr_secctx = 1;
   497			fsecctx_header->size = full_ctxlen;
   498			ptr += sizeof(*fsecctx_header);
   499	
   500			fsecctx = (struct fuse_secctx*) ptr;
   501			fsecctx->size = ctxlen;
   502			ptr += sizeof(*fsecctx);
   503	
   504			strcpy(ptr, name);
   505			ptr += strlen(name) + 1;
   506			memcpy(ptr, ctx, ctxlen);
   507			kfree(ctx);
   508		} else {
   509			full_ctxlen = sizeof(*fsecctx_header);
   510			full_ctx = kzalloc(full_ctxlen, GFP_KERNEL);
   511			if (!full_ctx) {
   512				err = -ENOMEM;
   513				goto out_err;
   514			}
   515			fsecctx_header = full_ctx;
   516			fsecctx_header->size = full_ctxlen;
   517		}
   518	
   519		*security_ctxlen = full_ctxlen;
   520		*security_ctx = full_ctx;
   521	out_err:
   522		return err;
   523	}
   524	

---
0-DAY CI Kernel Test Service, Intel Corporation
https://lists.01.org/hyperkitty/list/kbuild-all(a)lists.01.org

[-- Attachment #2: config.gz --]
[-- Type: application/gzip, Size: 31688 bytes --]

^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH v3 1/1] fuse: Send security context of inode on file creation
  2021-11-10 22:55   ` [Virtio-fs] " Vivek Goyal
@ 2021-11-11 14:32     ` Vivek Goyal
  -1 siblings, 0 replies; 10+ messages in thread
From: Vivek Goyal @ 2021-11-11 14:32 UTC (permalink / raw)
  To: miklos
  Cc: linux-fsdevel, selinux, linux-security-module, virtio-fs,
	chirantan, stephen.smalley.work, dwalsh, casey, omosnace


I noticed that this patch is conflicting with some fuse patches
merged in Linus's tree. I will post updated version. 

Vivek

On Wed, Nov 10, 2021 at 05:55:28PM -0500, Vivek Goyal wrote:
> 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 <chirantan@chromium.org>.
> 
> Signed-off-by: Vivek Goyal <vgoyal@redhat.com>
> ---
>  fs/fuse/dir.c             | 103 ++++++++++++++++++++++++++++++++++++++
>  fs/fuse/fuse_i.h          |   3 ++
>  fs/fuse/inode.c           |   4 +-
>  include/uapi/linux/fuse.h |  31 +++++++++++-
>  4 files changed, 139 insertions(+), 2 deletions(-)
> 
> diff --git a/fs/fuse/dir.c b/fs/fuse/dir.c
> index d9b977c0f38d..69398eb7b325 100644
> --- a/fs/fuse/dir.c
> +++ b/fs/fuse/dir.c
> @@ -17,6 +17,9 @@
>  #include <linux/xattr.h>
>  #include <linux/iversion.h>
>  #include <linux/posix_acl.h>
> +#include <linux/security.h>
> +#include <linux/types.h>
> +#include <linux/kernel.h>
>  
>  static void fuse_advise_use_readdirplus(struct inode *dir)
>  {
> @@ -456,6 +459,69 @@ static struct dentry *fuse_lookup(struct inode *dir, struct dentry *entry,
>  	return ERR_PTR(err);
>  }
>  
> +static int get_security_context(struct dentry *entry, umode_t mode,
> +				void **security_ctx, u32 *security_ctxlen)
> +{
> +	struct fuse_secctx *fsecctx;
> +	struct fuse_secctx_header *fsecctx_header;
> +	void *ctx, *full_ctx;
> +	u32 ctxlen, full_ctxlen;
> +	int err = 0;
> +	const char *name;
> +
> +	err = security_dentry_init_security(entry, mode, &entry->d_name,
> +					    &name, &ctx, &ctxlen);
> +	if (err) {
> +		if (err != -EOPNOTSUPP)
> +			goto out_err;
> +		/* No LSM is supporting this security hook. Ignore error */
> +		err = 0;
> +		ctxlen = 0;
> +	}
> +
> +	if (ctxlen > 0) {
> +		void *ptr;
> +
> +		full_ctxlen = sizeof(*fsecctx_header) + sizeof(*fsecctx) +
> +			      strlen(name) + ctxlen + 1;
> +		full_ctx = kzalloc(full_ctxlen, GFP_KERNEL);
> +		if (!full_ctx) {
> +			err = -ENOMEM;
> +			kfree(ctx);
> +			goto out_err;
> +		}
> +
> +		ptr = full_ctx;
> +		fsecctx_header = (struct fuse_secctx_header*) ptr;
> +		fsecctx_header->nr_secctx = 1;
> +		fsecctx_header->size = full_ctxlen;
> +		ptr += sizeof(*fsecctx_header);
> +
> +		fsecctx = (struct fuse_secctx*) ptr;
> +		fsecctx->size = ctxlen;
> +		ptr += sizeof(*fsecctx);
> +
> +		strcpy(ptr, name);
> +		ptr += strlen(name) + 1;
> +		memcpy(ptr, ctx, ctxlen);
> +		kfree(ctx);
> +	} else {
> +		full_ctxlen = sizeof(*fsecctx_header);
> +		full_ctx = kzalloc(full_ctxlen, GFP_KERNEL);
> +		if (!full_ctx) {
> +			err = -ENOMEM;
> +			goto out_err;
> +		}
> +		fsecctx_header = full_ctx;
> +		fsecctx_header->size = full_ctxlen;
> +	}
> +
> +	*security_ctxlen = full_ctxlen;
> +	*security_ctx = full_ctx;
> +out_err:
> +	return err;
> +}
> +
>  /*
>   * Atomic create+open operation
>   *
> @@ -476,6 +542,8 @@ static int fuse_create_open(struct inode *dir, struct dentry *entry,
>  	struct fuse_entry_out outentry;
>  	struct fuse_inode *fi;
>  	struct fuse_file *ff;
> +	void *security_ctx = NULL;
> +	u32 security_ctxlen;
>  
>  	/* Userspace expects S_IFREG in create mode */
>  	BUG_ON((mode & S_IFMT) != S_IFREG);
> @@ -517,6 +585,18 @@ static int fuse_create_open(struct inode *dir, struct dentry *entry,
>  	args.out_args[0].value = &outentry;
>  	args.out_args[1].size = sizeof(outopen);
>  	args.out_args[1].value = &outopen;
> +
> +	if (fm->fc->init_security) {
> +		err = get_security_context(entry, mode, &security_ctx,
> +					   &security_ctxlen);
> +		if (err)
> +			goto out_put_forget_req;
> +
> +		args.in_numargs = 3;
> +		args.in_args[2].size = security_ctxlen;
> +		args.in_args[2].value = security_ctx;
> +	}
> +
>  	err = fuse_simple_request(fm, &args);
>  	if (err)
>  		goto out_free_ff;
> @@ -554,6 +634,7 @@ static int fuse_create_open(struct inode *dir, struct dentry *entry,
>  
>  out_free_ff:
>  	fuse_file_free(ff);
> +	kfree(security_ctx);
>  out_put_forget_req:
>  	kfree(forget);
>  out_err:
> @@ -620,6 +701,8 @@ static int create_new_entry(struct fuse_mount *fm, struct fuse_args *args,
>  	struct dentry *d;
>  	int err;
>  	struct fuse_forget_link *forget;
> +	void *security_ctx = NULL;
> +	u32 security_ctxlen = 0;
>  
>  	if (fuse_is_bad(dir))
>  		return -EIO;
> @@ -633,7 +716,27 @@ static int create_new_entry(struct fuse_mount *fm, struct fuse_args *args,
>  	args->out_numargs = 1;
>  	args->out_args[0].size = sizeof(outarg);
>  	args->out_args[0].value = &outarg;
> +
> +	if (fm->fc->init_security && args->opcode != FUSE_LINK) {
> +		unsigned short idx = args->in_numargs;
> +
> +		if ((size_t)idx >= ARRAY_SIZE(args->in_args)) {
> +			err = -ENOMEM;
> +			goto out_put_forget_req;
> +		}
> +
> +		err = get_security_context(entry, mode, &security_ctx,
> +					   &security_ctxlen);
> +		if (err)
> +			goto out_put_forget_req;
> +
> +		args->in_args[idx].size = security_ctxlen;
> +		args->in_args[idx].value = security_ctx;
> +		args->in_numargs++;
> +	}
> +
>  	err = fuse_simple_request(fm, args);
> +	kfree(security_ctx);
>  	if (err)
>  		goto out_put_forget_req;
>  
> diff --git a/fs/fuse/fuse_i.h b/fs/fuse/fuse_i.h
> index f55f9f94b1a4..0d257c4eeb70 100644
> --- a/fs/fuse/fuse_i.h
> +++ b/fs/fuse/fuse_i.h
> @@ -765,6 +765,9 @@ struct fuse_conn {
>  	/* Propagate syncfs() to server */
>  	unsigned int sync_fs:1;
>  
> +	/* Initialize security xattrs when creating a new inode */
> +	unsigned int init_security:1;
> +
>  	/** The number of requests waiting for completion */
>  	atomic_t num_waiting;
>  
> diff --git a/fs/fuse/inode.c b/fs/fuse/inode.c
> index 12d49a1914e8..40c5533243c0 100644
> --- a/fs/fuse/inode.c
> +++ b/fs/fuse/inode.c
> @@ -1143,6 +1143,8 @@ static void process_init_reply(struct fuse_mount *fm, struct fuse_args *args,
>  			}
>  			if (arg->flags & FUSE_SETXATTR_EXT)
>  				fc->setxattr_ext = 1;
> +			if (arg->flags & FUSE_SECURITY_CTX)
> +				fc->init_security = 1;
>  		} else {
>  			ra_pages = fc->max_read / PAGE_SIZE;
>  			fc->no_lock = 1;
> @@ -1186,7 +1188,7 @@ void fuse_send_init(struct fuse_mount *fm)
>  		FUSE_PARALLEL_DIROPS | FUSE_HANDLE_KILLPRIV | FUSE_POSIX_ACL |
>  		FUSE_ABORT_ERROR | FUSE_MAX_PAGES | FUSE_CACHE_SYMLINKS |
>  		FUSE_NO_OPENDIR_SUPPORT | FUSE_EXPLICIT_INVAL_DATA |
> -		FUSE_HANDLE_KILLPRIV_V2 | FUSE_SETXATTR_EXT;
> +		FUSE_HANDLE_KILLPRIV_V2 | FUSE_SETXATTR_EXT | FUSE_SECURITY_CTX;
>  #ifdef CONFIG_FUSE_DAX
>  	if (fm->fc->dax)
>  		ia->in.flags |= FUSE_MAP_ALIGNMENT;
> diff --git a/include/uapi/linux/fuse.h b/include/uapi/linux/fuse.h
> index 36ed092227fa..710cdf20608d 100644
> --- a/include/uapi/linux/fuse.h
> +++ b/include/uapi/linux/fuse.h
> @@ -184,6 +184,10 @@
>   *
>   *  7.34
>   *  - add FUSE_SYNCFS
> + *
> + *  7.35
> + *  - add FUSE_SECURITY_CTX flag for fuse_init_out
> + *  - add security context to create, mkdir, symlink, and mknod requests
>   */
>  
>  #ifndef _LINUX_FUSE_H
> @@ -219,7 +223,7 @@
>  #define FUSE_KERNEL_VERSION 7
>  
>  /** Minor version number of this interface */
> -#define FUSE_KERNEL_MINOR_VERSION 34
> +#define FUSE_KERNEL_MINOR_VERSION 35
>  
>  /** The node ID of the root inode */
>  #define FUSE_ROOT_ID 1
> @@ -336,6 +340,8 @@ struct fuse_file_lock {
>   *			write/truncate sgid is killed only if file has group
>   *			execute permission. (Same as Linux VFS behavior).
>   * FUSE_SETXATTR_EXT:	Server supports extended struct fuse_setxattr_in
> + * FUSE_SECURITY_CTX:	add security context to create, mkdir, symlink, and
> + * 			mknod
>   */
>  #define FUSE_ASYNC_READ		(1 << 0)
>  #define FUSE_POSIX_LOCKS	(1 << 1)
> @@ -367,6 +373,7 @@ struct fuse_file_lock {
>  #define FUSE_SUBMOUNTS		(1 << 27)
>  #define FUSE_HANDLE_KILLPRIV_V2	(1 << 28)
>  #define FUSE_SETXATTR_EXT	(1 << 29)
> +#define FUSE_SECURITY_CTX	(1 << 30)
>  
>  /**
>   * CUSE INIT request/reply flags
> @@ -979,4 +986,26 @@ struct fuse_syncfs_in {
>  	uint64_t	padding;
>  };
>  
> +/*
> + * For each security context, send fuse_secctx with size of security context
> + * fuse_secctx will be followed by security context name and this in turn
> + * will be followed by actual context label.
> + * fuse_secctx, name, context
> + * */
> +struct fuse_secctx {
> +	uint32_t	size;
> +	uint32_t	padding;
> +};
> +
> +/*
> + * Contains the information about how many fuse_secctx structures are being
> + * sent and what's the total size of all security contexts (including
> + * size of fuse_secctx_header).
> + *
> + */
> +struct fuse_secctx_header {
> +	uint32_t	size;
> +	uint32_t	nr_secctx;
> +};
> +
>  #endif /* _LINUX_FUSE_H */
> -- 
> 2.31.1
> 


^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [Virtio-fs] [PATCH v3 1/1] fuse: Send security context of inode on file creation
@ 2021-11-11 14:32     ` Vivek Goyal
  0 siblings, 0 replies; 10+ messages in thread
From: Vivek Goyal @ 2021-11-11 14:32 UTC (permalink / raw)
  To: miklos
  Cc: selinux, casey, stephen.smalley.work, omosnace, virtio-fs,
	linux-security-module, linux-fsdevel


I noticed that this patch is conflicting with some fuse patches
merged in Linus's tree. I will post updated version. 

Vivek

On Wed, Nov 10, 2021 at 05:55:28PM -0500, Vivek Goyal wrote:
> 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 <chirantan@chromium.org>.
> 
> Signed-off-by: Vivek Goyal <vgoyal@redhat.com>
> ---
>  fs/fuse/dir.c             | 103 ++++++++++++++++++++++++++++++++++++++
>  fs/fuse/fuse_i.h          |   3 ++
>  fs/fuse/inode.c           |   4 +-
>  include/uapi/linux/fuse.h |  31 +++++++++++-
>  4 files changed, 139 insertions(+), 2 deletions(-)
> 
> diff --git a/fs/fuse/dir.c b/fs/fuse/dir.c
> index d9b977c0f38d..69398eb7b325 100644
> --- a/fs/fuse/dir.c
> +++ b/fs/fuse/dir.c
> @@ -17,6 +17,9 @@
>  #include <linux/xattr.h>
>  #include <linux/iversion.h>
>  #include <linux/posix_acl.h>
> +#include <linux/security.h>
> +#include <linux/types.h>
> +#include <linux/kernel.h>
>  
>  static void fuse_advise_use_readdirplus(struct inode *dir)
>  {
> @@ -456,6 +459,69 @@ static struct dentry *fuse_lookup(struct inode *dir, struct dentry *entry,
>  	return ERR_PTR(err);
>  }
>  
> +static int get_security_context(struct dentry *entry, umode_t mode,
> +				void **security_ctx, u32 *security_ctxlen)
> +{
> +	struct fuse_secctx *fsecctx;
> +	struct fuse_secctx_header *fsecctx_header;
> +	void *ctx, *full_ctx;
> +	u32 ctxlen, full_ctxlen;
> +	int err = 0;
> +	const char *name;
> +
> +	err = security_dentry_init_security(entry, mode, &entry->d_name,
> +					    &name, &ctx, &ctxlen);
> +	if (err) {
> +		if (err != -EOPNOTSUPP)
> +			goto out_err;
> +		/* No LSM is supporting this security hook. Ignore error */
> +		err = 0;
> +		ctxlen = 0;
> +	}
> +
> +	if (ctxlen > 0) {
> +		void *ptr;
> +
> +		full_ctxlen = sizeof(*fsecctx_header) + sizeof(*fsecctx) +
> +			      strlen(name) + ctxlen + 1;
> +		full_ctx = kzalloc(full_ctxlen, GFP_KERNEL);
> +		if (!full_ctx) {
> +			err = -ENOMEM;
> +			kfree(ctx);
> +			goto out_err;
> +		}
> +
> +		ptr = full_ctx;
> +		fsecctx_header = (struct fuse_secctx_header*) ptr;
> +		fsecctx_header->nr_secctx = 1;
> +		fsecctx_header->size = full_ctxlen;
> +		ptr += sizeof(*fsecctx_header);
> +
> +		fsecctx = (struct fuse_secctx*) ptr;
> +		fsecctx->size = ctxlen;
> +		ptr += sizeof(*fsecctx);
> +
> +		strcpy(ptr, name);
> +		ptr += strlen(name) + 1;
> +		memcpy(ptr, ctx, ctxlen);
> +		kfree(ctx);
> +	} else {
> +		full_ctxlen = sizeof(*fsecctx_header);
> +		full_ctx = kzalloc(full_ctxlen, GFP_KERNEL);
> +		if (!full_ctx) {
> +			err = -ENOMEM;
> +			goto out_err;
> +		}
> +		fsecctx_header = full_ctx;
> +		fsecctx_header->size = full_ctxlen;
> +	}
> +
> +	*security_ctxlen = full_ctxlen;
> +	*security_ctx = full_ctx;
> +out_err:
> +	return err;
> +}
> +
>  /*
>   * Atomic create+open operation
>   *
> @@ -476,6 +542,8 @@ static int fuse_create_open(struct inode *dir, struct dentry *entry,
>  	struct fuse_entry_out outentry;
>  	struct fuse_inode *fi;
>  	struct fuse_file *ff;
> +	void *security_ctx = NULL;
> +	u32 security_ctxlen;
>  
>  	/* Userspace expects S_IFREG in create mode */
>  	BUG_ON((mode & S_IFMT) != S_IFREG);
> @@ -517,6 +585,18 @@ static int fuse_create_open(struct inode *dir, struct dentry *entry,
>  	args.out_args[0].value = &outentry;
>  	args.out_args[1].size = sizeof(outopen);
>  	args.out_args[1].value = &outopen;
> +
> +	if (fm->fc->init_security) {
> +		err = get_security_context(entry, mode, &security_ctx,
> +					   &security_ctxlen);
> +		if (err)
> +			goto out_put_forget_req;
> +
> +		args.in_numargs = 3;
> +		args.in_args[2].size = security_ctxlen;
> +		args.in_args[2].value = security_ctx;
> +	}
> +
>  	err = fuse_simple_request(fm, &args);
>  	if (err)
>  		goto out_free_ff;
> @@ -554,6 +634,7 @@ static int fuse_create_open(struct inode *dir, struct dentry *entry,
>  
>  out_free_ff:
>  	fuse_file_free(ff);
> +	kfree(security_ctx);
>  out_put_forget_req:
>  	kfree(forget);
>  out_err:
> @@ -620,6 +701,8 @@ static int create_new_entry(struct fuse_mount *fm, struct fuse_args *args,
>  	struct dentry *d;
>  	int err;
>  	struct fuse_forget_link *forget;
> +	void *security_ctx = NULL;
> +	u32 security_ctxlen = 0;
>  
>  	if (fuse_is_bad(dir))
>  		return -EIO;
> @@ -633,7 +716,27 @@ static int create_new_entry(struct fuse_mount *fm, struct fuse_args *args,
>  	args->out_numargs = 1;
>  	args->out_args[0].size = sizeof(outarg);
>  	args->out_args[0].value = &outarg;
> +
> +	if (fm->fc->init_security && args->opcode != FUSE_LINK) {
> +		unsigned short idx = args->in_numargs;
> +
> +		if ((size_t)idx >= ARRAY_SIZE(args->in_args)) {
> +			err = -ENOMEM;
> +			goto out_put_forget_req;
> +		}
> +
> +		err = get_security_context(entry, mode, &security_ctx,
> +					   &security_ctxlen);
> +		if (err)
> +			goto out_put_forget_req;
> +
> +		args->in_args[idx].size = security_ctxlen;
> +		args->in_args[idx].value = security_ctx;
> +		args->in_numargs++;
> +	}
> +
>  	err = fuse_simple_request(fm, args);
> +	kfree(security_ctx);
>  	if (err)
>  		goto out_put_forget_req;
>  
> diff --git a/fs/fuse/fuse_i.h b/fs/fuse/fuse_i.h
> index f55f9f94b1a4..0d257c4eeb70 100644
> --- a/fs/fuse/fuse_i.h
> +++ b/fs/fuse/fuse_i.h
> @@ -765,6 +765,9 @@ struct fuse_conn {
>  	/* Propagate syncfs() to server */
>  	unsigned int sync_fs:1;
>  
> +	/* Initialize security xattrs when creating a new inode */
> +	unsigned int init_security:1;
> +
>  	/** The number of requests waiting for completion */
>  	atomic_t num_waiting;
>  
> diff --git a/fs/fuse/inode.c b/fs/fuse/inode.c
> index 12d49a1914e8..40c5533243c0 100644
> --- a/fs/fuse/inode.c
> +++ b/fs/fuse/inode.c
> @@ -1143,6 +1143,8 @@ static void process_init_reply(struct fuse_mount *fm, struct fuse_args *args,
>  			}
>  			if (arg->flags & FUSE_SETXATTR_EXT)
>  				fc->setxattr_ext = 1;
> +			if (arg->flags & FUSE_SECURITY_CTX)
> +				fc->init_security = 1;
>  		} else {
>  			ra_pages = fc->max_read / PAGE_SIZE;
>  			fc->no_lock = 1;
> @@ -1186,7 +1188,7 @@ void fuse_send_init(struct fuse_mount *fm)
>  		FUSE_PARALLEL_DIROPS | FUSE_HANDLE_KILLPRIV | FUSE_POSIX_ACL |
>  		FUSE_ABORT_ERROR | FUSE_MAX_PAGES | FUSE_CACHE_SYMLINKS |
>  		FUSE_NO_OPENDIR_SUPPORT | FUSE_EXPLICIT_INVAL_DATA |
> -		FUSE_HANDLE_KILLPRIV_V2 | FUSE_SETXATTR_EXT;
> +		FUSE_HANDLE_KILLPRIV_V2 | FUSE_SETXATTR_EXT | FUSE_SECURITY_CTX;
>  #ifdef CONFIG_FUSE_DAX
>  	if (fm->fc->dax)
>  		ia->in.flags |= FUSE_MAP_ALIGNMENT;
> diff --git a/include/uapi/linux/fuse.h b/include/uapi/linux/fuse.h
> index 36ed092227fa..710cdf20608d 100644
> --- a/include/uapi/linux/fuse.h
> +++ b/include/uapi/linux/fuse.h
> @@ -184,6 +184,10 @@
>   *
>   *  7.34
>   *  - add FUSE_SYNCFS
> + *
> + *  7.35
> + *  - add FUSE_SECURITY_CTX flag for fuse_init_out
> + *  - add security context to create, mkdir, symlink, and mknod requests
>   */
>  
>  #ifndef _LINUX_FUSE_H
> @@ -219,7 +223,7 @@
>  #define FUSE_KERNEL_VERSION 7
>  
>  /** Minor version number of this interface */
> -#define FUSE_KERNEL_MINOR_VERSION 34
> +#define FUSE_KERNEL_MINOR_VERSION 35
>  
>  /** The node ID of the root inode */
>  #define FUSE_ROOT_ID 1
> @@ -336,6 +340,8 @@ struct fuse_file_lock {
>   *			write/truncate sgid is killed only if file has group
>   *			execute permission. (Same as Linux VFS behavior).
>   * FUSE_SETXATTR_EXT:	Server supports extended struct fuse_setxattr_in
> + * FUSE_SECURITY_CTX:	add security context to create, mkdir, symlink, and
> + * 			mknod
>   */
>  #define FUSE_ASYNC_READ		(1 << 0)
>  #define FUSE_POSIX_LOCKS	(1 << 1)
> @@ -367,6 +373,7 @@ struct fuse_file_lock {
>  #define FUSE_SUBMOUNTS		(1 << 27)
>  #define FUSE_HANDLE_KILLPRIV_V2	(1 << 28)
>  #define FUSE_SETXATTR_EXT	(1 << 29)
> +#define FUSE_SECURITY_CTX	(1 << 30)
>  
>  /**
>   * CUSE INIT request/reply flags
> @@ -979,4 +986,26 @@ struct fuse_syncfs_in {
>  	uint64_t	padding;
>  };
>  
> +/*
> + * For each security context, send fuse_secctx with size of security context
> + * fuse_secctx will be followed by security context name and this in turn
> + * will be followed by actual context label.
> + * fuse_secctx, name, context
> + * */
> +struct fuse_secctx {
> +	uint32_t	size;
> +	uint32_t	padding;
> +};
> +
> +/*
> + * Contains the information about how many fuse_secctx structures are being
> + * sent and what's the total size of all security contexts (including
> + * size of fuse_secctx_header).
> + *
> + */
> +struct fuse_secctx_header {
> +	uint32_t	size;
> +	uint32_t	nr_secctx;
> +};
> +
>  #endif /* _LINUX_FUSE_H */
> -- 
> 2.31.1
> 


^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH v3 1/1] fuse: Send security context of inode on file creation
  2021-11-10 22:55   ` [Virtio-fs] " Vivek Goyal
@ 2021-11-11 14:32     ` Vivek Goyal
  -1 siblings, 0 replies; 10+ messages in thread
From: Vivek Goyal @ 2021-11-11 14:32 UTC (permalink / raw)
  To: miklos
  Cc: linux-fsdevel, selinux, linux-security-module, virtio-fs,
	chirantan, stephen.smalley.work, dwalsh, casey, omosnace

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 <chirantan@chromium.org>.

Signed-off-by: Vivek Goyal <vgoyal@redhat.com>
---
 fs/fuse/dir.c             |  103 ++++++++++++++++++++++++++++++++++++++++++++++
 fs/fuse/fuse_i.h          |    3 +
 fs/fuse/inode.c           |    4 +
 include/uapi/linux/fuse.h |   31 +++++++++++++
 4 files changed, 139 insertions(+), 2 deletions(-)

Index: redhat-linux/include/uapi/linux/fuse.h
===================================================================
--- redhat-linux.orig/include/uapi/linux/fuse.h	2021-11-11 08:53:18.570236125 -0500
+++ redhat-linux/include/uapi/linux/fuse.h	2021-11-11 08:58:38.970236125 -0500
@@ -187,6 +187,10 @@
  *
  *  7.35
  *  - add FOPEN_NOFLUSH
+ *
+ *  7.36
+ *  - add FUSE_SECURITY_CTX flag for fuse_init_out
+ *  - add security context to create, mkdir, symlink, and mknod requests
  */
 
 #ifndef _LINUX_FUSE_H
@@ -222,7 +226,7 @@
 #define FUSE_KERNEL_VERSION 7
 
 /** Minor version number of this interface */
-#define FUSE_KERNEL_MINOR_VERSION 35
+#define FUSE_KERNEL_MINOR_VERSION 36
 
 /** The node ID of the root inode */
 #define FUSE_ROOT_ID 1
@@ -341,6 +345,8 @@ struct fuse_file_lock {
  *			write/truncate sgid is killed only if file has group
  *			execute permission. (Same as Linux VFS behavior).
  * FUSE_SETXATTR_EXT:	Server supports extended struct fuse_setxattr_in
+ * FUSE_SECURITY_CTX:	add security context to create, mkdir, symlink, and
+ * 			mknod
  */
 #define FUSE_ASYNC_READ		(1 << 0)
 #define FUSE_POSIX_LOCKS	(1 << 1)
@@ -372,6 +378,7 @@ struct fuse_file_lock {
 #define FUSE_SUBMOUNTS		(1 << 27)
 #define FUSE_HANDLE_KILLPRIV_V2	(1 << 28)
 #define FUSE_SETXATTR_EXT	(1 << 29)
+#define FUSE_SECURITY_CTX	(1 << 30)
 
 /**
  * CUSE INIT request/reply flags
@@ -984,4 +991,26 @@ struct fuse_syncfs_in {
 	uint64_t	padding;
 };
 
+/*
+ * For each security context, send fuse_secctx with size of security context
+ * fuse_secctx will be followed by security context name and this in turn
+ * will be followed by actual context label.
+ * fuse_secctx, name, context
+ * */
+struct fuse_secctx {
+	uint32_t	size;
+	uint32_t	padding;
+};
+
+/*
+ * Contains the information about how many fuse_secctx structures are being
+ * sent and what's the total size of all security contexts (including
+ * size of fuse_secctx_header).
+ *
+ */
+struct fuse_secctx_header {
+	uint32_t	size;
+	uint32_t	nr_secctx;
+};
+
 #endif /* _LINUX_FUSE_H */
Index: redhat-linux/fs/fuse/dir.c
===================================================================
--- redhat-linux.orig/fs/fuse/dir.c	2021-11-11 08:56:19.812236125 -0500
+++ redhat-linux/fs/fuse/dir.c	2021-11-11 08:57:10.575236125 -0500
@@ -17,6 +17,9 @@
 #include <linux/xattr.h>
 #include <linux/iversion.h>
 #include <linux/posix_acl.h>
+#include <linux/security.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
 
 static void fuse_advise_use_readdirplus(struct inode *dir)
 {
@@ -456,6 +459,69 @@ static struct dentry *fuse_lookup(struct
 	return ERR_PTR(err);
 }
 
+static int get_security_context(struct dentry *entry, umode_t mode,
+				void **security_ctx, u32 *security_ctxlen)
+{
+	struct fuse_secctx *fsecctx;
+	struct fuse_secctx_header *fsecctx_header;
+	void *ctx, *full_ctx;
+	u32 ctxlen, full_ctxlen;
+	int err = 0;
+	const char *name;
+
+	err = security_dentry_init_security(entry, mode, &entry->d_name,
+					    &name, &ctx, &ctxlen);
+	if (err) {
+		if (err != -EOPNOTSUPP)
+			goto out_err;
+		/* No LSM is supporting this security hook. Ignore error */
+		err = 0;
+		ctxlen = 0;
+	}
+
+	if (ctxlen > 0) {
+		void *ptr;
+
+		full_ctxlen = sizeof(*fsecctx_header) + sizeof(*fsecctx) +
+			      strlen(name) + ctxlen + 1;
+		full_ctx = kzalloc(full_ctxlen, GFP_KERNEL);
+		if (!full_ctx) {
+			err = -ENOMEM;
+			kfree(ctx);
+			goto out_err;
+		}
+
+		ptr = full_ctx;
+		fsecctx_header = (struct fuse_secctx_header*) ptr;
+		fsecctx_header->nr_secctx = 1;
+		fsecctx_header->size = full_ctxlen;
+		ptr += sizeof(*fsecctx_header);
+
+		fsecctx = (struct fuse_secctx*) ptr;
+		fsecctx->size = ctxlen;
+		ptr += sizeof(*fsecctx);
+
+		strcpy(ptr, name);
+		ptr += strlen(name) + 1;
+		memcpy(ptr, ctx, ctxlen);
+		kfree(ctx);
+	} else {
+		full_ctxlen = sizeof(*fsecctx_header);
+		full_ctx = kzalloc(full_ctxlen, GFP_KERNEL);
+		if (!full_ctx) {
+			err = -ENOMEM;
+			goto out_err;
+		}
+		fsecctx_header = full_ctx;
+		fsecctx_header->size = full_ctxlen;
+	}
+
+	*security_ctxlen = full_ctxlen;
+	*security_ctx = full_ctx;
+out_err:
+	return err;
+}
+
 /*
  * Atomic create+open operation
  *
@@ -476,6 +542,8 @@ static int fuse_create_open(struct inode
 	struct fuse_entry_out outentry;
 	struct fuse_inode *fi;
 	struct fuse_file *ff;
+	void *security_ctx = NULL;
+	u32 security_ctxlen;
 
 	/* Userspace expects S_IFREG in create mode */
 	BUG_ON((mode & S_IFMT) != S_IFREG);
@@ -517,6 +585,18 @@ static int fuse_create_open(struct inode
 	args.out_args[0].value = &outentry;
 	args.out_args[1].size = sizeof(outopen);
 	args.out_args[1].value = &outopen;
+
+	if (fm->fc->init_security) {
+		err = get_security_context(entry, mode, &security_ctx,
+					   &security_ctxlen);
+		if (err)
+			goto out_put_forget_req;
+
+		args.in_numargs = 3;
+		args.in_args[2].size = security_ctxlen;
+		args.in_args[2].value = security_ctx;
+	}
+
 	err = fuse_simple_request(fm, &args);
 	if (err)
 		goto out_free_ff;
@@ -554,6 +634,7 @@ static int fuse_create_open(struct inode
 
 out_free_ff:
 	fuse_file_free(ff);
+	kfree(security_ctx);
 out_put_forget_req:
 	kfree(forget);
 out_err:
@@ -620,6 +701,8 @@ static int create_new_entry(struct fuse_
 	struct dentry *d;
 	int err;
 	struct fuse_forget_link *forget;
+	void *security_ctx = NULL;
+	u32 security_ctxlen = 0;
 
 	if (fuse_is_bad(dir))
 		return -EIO;
@@ -633,7 +716,27 @@ static int create_new_entry(struct fuse_
 	args->out_numargs = 1;
 	args->out_args[0].size = sizeof(outarg);
 	args->out_args[0].value = &outarg;
+
+	if (fm->fc->init_security && args->opcode != FUSE_LINK) {
+		unsigned short idx = args->in_numargs;
+
+		if ((size_t)idx >= ARRAY_SIZE(args->in_args)) {
+			err = -ENOMEM;
+			goto out_put_forget_req;
+		}
+
+		err = get_security_context(entry, mode, &security_ctx,
+					   &security_ctxlen);
+		if (err)
+			goto out_put_forget_req;
+
+		args->in_args[idx].size = security_ctxlen;
+		args->in_args[idx].value = security_ctx;
+		args->in_numargs++;
+	}
+
 	err = fuse_simple_request(fm, args);
+	kfree(security_ctx);
 	if (err)
 		goto out_put_forget_req;
 
Index: redhat-linux/fs/fuse/fuse_i.h
===================================================================
--- redhat-linux.orig/fs/fuse/fuse_i.h	2021-11-11 08:56:19.814236125 -0500
+++ redhat-linux/fs/fuse/fuse_i.h	2021-11-11 08:57:10.576236125 -0500
@@ -765,6 +765,9 @@ struct fuse_conn {
 	/* Propagate syncfs() to server */
 	unsigned int sync_fs:1;
 
+	/* Initialize security xattrs when creating a new inode */
+	unsigned int init_security:1;
+
 	/** The number of requests waiting for completion */
 	atomic_t num_waiting;
 
Index: redhat-linux/fs/fuse/inode.c
===================================================================
--- redhat-linux.orig/fs/fuse/inode.c	2021-11-11 08:56:19.815236125 -0500
+++ redhat-linux/fs/fuse/inode.c	2021-11-11 08:57:10.576236125 -0500
@@ -1176,6 +1176,8 @@ static void process_init_reply(struct fu
 			}
 			if (arg->flags & FUSE_SETXATTR_EXT)
 				fc->setxattr_ext = 1;
+			if (arg->flags & FUSE_SECURITY_CTX)
+				fc->init_security = 1;
 		} else {
 			ra_pages = fc->max_read / PAGE_SIZE;
 			fc->no_lock = 1;
@@ -1219,7 +1221,7 @@ void fuse_send_init(struct fuse_mount *f
 		FUSE_PARALLEL_DIROPS | FUSE_HANDLE_KILLPRIV | FUSE_POSIX_ACL |
 		FUSE_ABORT_ERROR | FUSE_MAX_PAGES | FUSE_CACHE_SYMLINKS |
 		FUSE_NO_OPENDIR_SUPPORT | FUSE_EXPLICIT_INVAL_DATA |
-		FUSE_HANDLE_KILLPRIV_V2 | FUSE_SETXATTR_EXT;
+		FUSE_HANDLE_KILLPRIV_V2 | FUSE_SETXATTR_EXT | FUSE_SECURITY_CTX;
 #ifdef CONFIG_FUSE_DAX
 	if (fm->fc->dax)
 		ia->in.flags |= FUSE_MAP_ALIGNMENT;


^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [Virtio-fs] [PATCH v3 1/1] fuse: Send security context of inode on file creation
@ 2021-11-11 14:32     ` Vivek Goyal
  0 siblings, 0 replies; 10+ messages in thread
From: Vivek Goyal @ 2021-11-11 14:32 UTC (permalink / raw)
  To: miklos
  Cc: selinux, casey, stephen.smalley.work, omosnace, virtio-fs,
	linux-security-module, linux-fsdevel

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 <chirantan@chromium.org>.

Signed-off-by: Vivek Goyal <vgoyal@redhat.com>
---
 fs/fuse/dir.c             |  103 ++++++++++++++++++++++++++++++++++++++++++++++
 fs/fuse/fuse_i.h          |    3 +
 fs/fuse/inode.c           |    4 +
 include/uapi/linux/fuse.h |   31 +++++++++++++
 4 files changed, 139 insertions(+), 2 deletions(-)

Index: redhat-linux/include/uapi/linux/fuse.h
===================================================================
--- redhat-linux.orig/include/uapi/linux/fuse.h	2021-11-11 08:53:18.570236125 -0500
+++ redhat-linux/include/uapi/linux/fuse.h	2021-11-11 08:58:38.970236125 -0500
@@ -187,6 +187,10 @@
  *
  *  7.35
  *  - add FOPEN_NOFLUSH
+ *
+ *  7.36
+ *  - add FUSE_SECURITY_CTX flag for fuse_init_out
+ *  - add security context to create, mkdir, symlink, and mknod requests
  */
 
 #ifndef _LINUX_FUSE_H
@@ -222,7 +226,7 @@
 #define FUSE_KERNEL_VERSION 7
 
 /** Minor version number of this interface */
-#define FUSE_KERNEL_MINOR_VERSION 35
+#define FUSE_KERNEL_MINOR_VERSION 36
 
 /** The node ID of the root inode */
 #define FUSE_ROOT_ID 1
@@ -341,6 +345,8 @@ struct fuse_file_lock {
  *			write/truncate sgid is killed only if file has group
  *			execute permission. (Same as Linux VFS behavior).
  * FUSE_SETXATTR_EXT:	Server supports extended struct fuse_setxattr_in
+ * FUSE_SECURITY_CTX:	add security context to create, mkdir, symlink, and
+ * 			mknod
  */
 #define FUSE_ASYNC_READ		(1 << 0)
 #define FUSE_POSIX_LOCKS	(1 << 1)
@@ -372,6 +378,7 @@ struct fuse_file_lock {
 #define FUSE_SUBMOUNTS		(1 << 27)
 #define FUSE_HANDLE_KILLPRIV_V2	(1 << 28)
 #define FUSE_SETXATTR_EXT	(1 << 29)
+#define FUSE_SECURITY_CTX	(1 << 30)
 
 /**
  * CUSE INIT request/reply flags
@@ -984,4 +991,26 @@ struct fuse_syncfs_in {
 	uint64_t	padding;
 };
 
+/*
+ * For each security context, send fuse_secctx with size of security context
+ * fuse_secctx will be followed by security context name and this in turn
+ * will be followed by actual context label.
+ * fuse_secctx, name, context
+ * */
+struct fuse_secctx {
+	uint32_t	size;
+	uint32_t	padding;
+};
+
+/*
+ * Contains the information about how many fuse_secctx structures are being
+ * sent and what's the total size of all security contexts (including
+ * size of fuse_secctx_header).
+ *
+ */
+struct fuse_secctx_header {
+	uint32_t	size;
+	uint32_t	nr_secctx;
+};
+
 #endif /* _LINUX_FUSE_H */
Index: redhat-linux/fs/fuse/dir.c
===================================================================
--- redhat-linux.orig/fs/fuse/dir.c	2021-11-11 08:56:19.812236125 -0500
+++ redhat-linux/fs/fuse/dir.c	2021-11-11 08:57:10.575236125 -0500
@@ -17,6 +17,9 @@
 #include <linux/xattr.h>
 #include <linux/iversion.h>
 #include <linux/posix_acl.h>
+#include <linux/security.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
 
 static void fuse_advise_use_readdirplus(struct inode *dir)
 {
@@ -456,6 +459,69 @@ static struct dentry *fuse_lookup(struct
 	return ERR_PTR(err);
 }
 
+static int get_security_context(struct dentry *entry, umode_t mode,
+				void **security_ctx, u32 *security_ctxlen)
+{
+	struct fuse_secctx *fsecctx;
+	struct fuse_secctx_header *fsecctx_header;
+	void *ctx, *full_ctx;
+	u32 ctxlen, full_ctxlen;
+	int err = 0;
+	const char *name;
+
+	err = security_dentry_init_security(entry, mode, &entry->d_name,
+					    &name, &ctx, &ctxlen);
+	if (err) {
+		if (err != -EOPNOTSUPP)
+			goto out_err;
+		/* No LSM is supporting this security hook. Ignore error */
+		err = 0;
+		ctxlen = 0;
+	}
+
+	if (ctxlen > 0) {
+		void *ptr;
+
+		full_ctxlen = sizeof(*fsecctx_header) + sizeof(*fsecctx) +
+			      strlen(name) + ctxlen + 1;
+		full_ctx = kzalloc(full_ctxlen, GFP_KERNEL);
+		if (!full_ctx) {
+			err = -ENOMEM;
+			kfree(ctx);
+			goto out_err;
+		}
+
+		ptr = full_ctx;
+		fsecctx_header = (struct fuse_secctx_header*) ptr;
+		fsecctx_header->nr_secctx = 1;
+		fsecctx_header->size = full_ctxlen;
+		ptr += sizeof(*fsecctx_header);
+
+		fsecctx = (struct fuse_secctx*) ptr;
+		fsecctx->size = ctxlen;
+		ptr += sizeof(*fsecctx);
+
+		strcpy(ptr, name);
+		ptr += strlen(name) + 1;
+		memcpy(ptr, ctx, ctxlen);
+		kfree(ctx);
+	} else {
+		full_ctxlen = sizeof(*fsecctx_header);
+		full_ctx = kzalloc(full_ctxlen, GFP_KERNEL);
+		if (!full_ctx) {
+			err = -ENOMEM;
+			goto out_err;
+		}
+		fsecctx_header = full_ctx;
+		fsecctx_header->size = full_ctxlen;
+	}
+
+	*security_ctxlen = full_ctxlen;
+	*security_ctx = full_ctx;
+out_err:
+	return err;
+}
+
 /*
  * Atomic create+open operation
  *
@@ -476,6 +542,8 @@ static int fuse_create_open(struct inode
 	struct fuse_entry_out outentry;
 	struct fuse_inode *fi;
 	struct fuse_file *ff;
+	void *security_ctx = NULL;
+	u32 security_ctxlen;
 
 	/* Userspace expects S_IFREG in create mode */
 	BUG_ON((mode & S_IFMT) != S_IFREG);
@@ -517,6 +585,18 @@ static int fuse_create_open(struct inode
 	args.out_args[0].value = &outentry;
 	args.out_args[1].size = sizeof(outopen);
 	args.out_args[1].value = &outopen;
+
+	if (fm->fc->init_security) {
+		err = get_security_context(entry, mode, &security_ctx,
+					   &security_ctxlen);
+		if (err)
+			goto out_put_forget_req;
+
+		args.in_numargs = 3;
+		args.in_args[2].size = security_ctxlen;
+		args.in_args[2].value = security_ctx;
+	}
+
 	err = fuse_simple_request(fm, &args);
 	if (err)
 		goto out_free_ff;
@@ -554,6 +634,7 @@ static int fuse_create_open(struct inode
 
 out_free_ff:
 	fuse_file_free(ff);
+	kfree(security_ctx);
 out_put_forget_req:
 	kfree(forget);
 out_err:
@@ -620,6 +701,8 @@ static int create_new_entry(struct fuse_
 	struct dentry *d;
 	int err;
 	struct fuse_forget_link *forget;
+	void *security_ctx = NULL;
+	u32 security_ctxlen = 0;
 
 	if (fuse_is_bad(dir))
 		return -EIO;
@@ -633,7 +716,27 @@ static int create_new_entry(struct fuse_
 	args->out_numargs = 1;
 	args->out_args[0].size = sizeof(outarg);
 	args->out_args[0].value = &outarg;
+
+	if (fm->fc->init_security && args->opcode != FUSE_LINK) {
+		unsigned short idx = args->in_numargs;
+
+		if ((size_t)idx >= ARRAY_SIZE(args->in_args)) {
+			err = -ENOMEM;
+			goto out_put_forget_req;
+		}
+
+		err = get_security_context(entry, mode, &security_ctx,
+					   &security_ctxlen);
+		if (err)
+			goto out_put_forget_req;
+
+		args->in_args[idx].size = security_ctxlen;
+		args->in_args[idx].value = security_ctx;
+		args->in_numargs++;
+	}
+
 	err = fuse_simple_request(fm, args);
+	kfree(security_ctx);
 	if (err)
 		goto out_put_forget_req;
 
Index: redhat-linux/fs/fuse/fuse_i.h
===================================================================
--- redhat-linux.orig/fs/fuse/fuse_i.h	2021-11-11 08:56:19.814236125 -0500
+++ redhat-linux/fs/fuse/fuse_i.h	2021-11-11 08:57:10.576236125 -0500
@@ -765,6 +765,9 @@ struct fuse_conn {
 	/* Propagate syncfs() to server */
 	unsigned int sync_fs:1;
 
+	/* Initialize security xattrs when creating a new inode */
+	unsigned int init_security:1;
+
 	/** The number of requests waiting for completion */
 	atomic_t num_waiting;
 
Index: redhat-linux/fs/fuse/inode.c
===================================================================
--- redhat-linux.orig/fs/fuse/inode.c	2021-11-11 08:56:19.815236125 -0500
+++ redhat-linux/fs/fuse/inode.c	2021-11-11 08:57:10.576236125 -0500
@@ -1176,6 +1176,8 @@ static void process_init_reply(struct fu
 			}
 			if (arg->flags & FUSE_SETXATTR_EXT)
 				fc->setxattr_ext = 1;
+			if (arg->flags & FUSE_SECURITY_CTX)
+				fc->init_security = 1;
 		} else {
 			ra_pages = fc->max_read / PAGE_SIZE;
 			fc->no_lock = 1;
@@ -1219,7 +1221,7 @@ void fuse_send_init(struct fuse_mount *f
 		FUSE_PARALLEL_DIROPS | FUSE_HANDLE_KILLPRIV | FUSE_POSIX_ACL |
 		FUSE_ABORT_ERROR | FUSE_MAX_PAGES | FUSE_CACHE_SYMLINKS |
 		FUSE_NO_OPENDIR_SUPPORT | FUSE_EXPLICIT_INVAL_DATA |
-		FUSE_HANDLE_KILLPRIV_V2 | FUSE_SETXATTR_EXT;
+		FUSE_HANDLE_KILLPRIV_V2 | FUSE_SETXATTR_EXT | FUSE_SECURITY_CTX;
 #ifdef CONFIG_FUSE_DAX
 	if (fm->fc->dax)
 		ia->in.flags |= FUSE_MAP_ALIGNMENT;


^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: [PATCH v3 1/1] fuse: Send security context of inode on file creation
  2021-11-11  9:54   ` kernel test robot
@ 2021-11-11 14:40     ` Vivek Goyal
  0 siblings, 0 replies; 10+ messages in thread
From: Vivek Goyal @ 2021-11-11 14:40 UTC (permalink / raw)
  To: kbuild-all

[-- Attachment #1: Type: text/plain, Size: 6792 bytes --]

On Thu, Nov 11, 2021 at 05:54:20PM +0800, kernel test robot wrote:
> Hi Vivek,
> 
> I love your patch! Yet something to improve:
> 
> [auto build test ERROR on v5.15]
> [cannot apply to mszeredi-fuse/for-next next-20211111]

Hi,

Please apply the patch on latest upstream Linus tree instead. I have
now updated this patch again because it had a conflict with another
fuse patch. So please take the newly posted patch in same thread and
apply on top of following commit in Linus's tree.

commit debe436e77c72fcee804fb867f275e6d31aa999c (HEAD -> master, upstream/master)
Merge: 6070dcc8e5b1 124e7c61deb2
Author: Linus Torvalds <torvalds@linux-foundation.org>
Date:   Wed Nov 10 17:05:37 2021 -0800

    Merge tag 'ext4_for_linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tytso/ext4

Thanks
Vivek

> [If your patch is applied to the wrong git tree, kindly drop us a note.
> And when submitting patch, we suggest to use '--base' as documented in
> https://git-scm.com/docs/git-format-patch]
> 
> url:    https://github.com/0day-ci/linux/commits/Vivek-Goyal/fuse-Send-file-inode-security-context-during-creation/20211111-065645
> base:   DEBUG invalid remote for branch v5.15 8bb7eca972ad531c9b149c0a51ab43a417385813
> config: microblaze-buildonly-randconfig-r001-20211111 (attached as .config)
> compiler: microblaze-linux-gcc (GCC) 11.2.0
> reproduce (this is a W=1 build):
>         wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
>         chmod +x ~/bin/make.cross
>         # https://github.com/0day-ci/linux/commit/3e94c8631307bf28c635eceedd868fe561666da5
>         git remote add linux-review https://github.com/0day-ci/linux
>         git fetch --no-tags linux-review Vivek-Goyal/fuse-Send-file-inode-security-context-during-creation/20211111-065645
>         git checkout 3e94c8631307bf28c635eceedd868fe561666da5
>         # save the attached .config to linux build tree
>         COMPILER_INSTALL_PATH=$HOME/0day COMPILER=gcc-11.2.0 make.cross ARCH=microblaze 
> 
> If you fix the issue, kindly add following tag as appropriate
> Reported-by: kernel test robot <lkp@intel.com>
> 
> All errors (new ones prefixed by >>):
> 
>    fs/fuse/dir.c: In function 'get_security_context':
> >> fs/fuse/dir.c:473:45: error: passing argument 4 of 'security_dentry_init_security' from incompatible pointer type [-Werror=incompatible-pointer-types]
>      473 |                                             &name, &ctx, &ctxlen);
>          |                                             ^~~~~
>          |                                             |
>          |                                             const char **
>    In file included from include/linux/fs_context.h:14,
>                     from fs/fuse/dir.c:13:
>    include/linux/security.h:742:57: note: expected 'void **' but argument is of type 'const char **'
>      742 |                                                  void **ctx,
>          |                                                  ~~~~~~~^~~
>    fs/fuse/dir.c:473:52: error: passing argument 5 of 'security_dentry_init_security' from incompatible pointer type [-Werror=incompatible-pointer-types]
>      473 |                                             &name, &ctx, &ctxlen);
>          |                                                    ^~~~
>          |                                                    |
>          |                                                    void **
>    In file included from include/linux/fs_context.h:14,
>                     from fs/fuse/dir.c:13:
>    include/linux/security.h:743:55: note: expected 'u32 *' {aka 'unsigned int *'} but argument is of type 'void **'
>      743 |                                                  u32 *ctxlen)
>          |                                                  ~~~~~^~~~~~
> >> fs/fuse/dir.c:472:15: error: too many arguments to function 'security_dentry_init_security'
>      472 |         err = security_dentry_init_security(entry, mode, &entry->d_name,
>          |               ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~
>    In file included from include/linux/fs_context.h:14,
>                     from fs/fuse/dir.c:13:
>    include/linux/security.h:739:19: note: declared here
>      739 | static inline int security_dentry_init_security(struct dentry *dentry,
>          |                   ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~
>    cc1: some warnings being treated as errors
> 
> 
> vim +/security_dentry_init_security +473 fs/fuse/dir.c
> 
>    461	
>    462	static int get_security_context(struct dentry *entry, umode_t mode,
>    463					void **security_ctx, u32 *security_ctxlen)
>    464	{
>    465		struct fuse_secctx *fsecctx;
>    466		struct fuse_secctx_header *fsecctx_header;
>    467		void *ctx, *full_ctx;
>    468		u32 ctxlen, full_ctxlen;
>    469		int err = 0;
>    470		const char *name;
>    471	
>  > 472		err = security_dentry_init_security(entry, mode, &entry->d_name,
>  > 473						    &name, &ctx, &ctxlen);
>    474		if (err) {
>    475			if (err != -EOPNOTSUPP)
>    476				goto out_err;
>    477			/* No LSM is supporting this security hook. Ignore error */
>    478			err = 0;
>    479			ctxlen = 0;
>    480		}
>    481	
>    482		if (ctxlen > 0) {
>    483			void *ptr;
>    484	
>    485			full_ctxlen = sizeof(*fsecctx_header) + sizeof(*fsecctx) +
>    486				      strlen(name) + ctxlen + 1;
>    487			full_ctx = kzalloc(full_ctxlen, GFP_KERNEL);
>    488			if (!full_ctx) {
>    489				err = -ENOMEM;
>    490				kfree(ctx);
>    491				goto out_err;
>    492			}
>    493	
>    494			ptr = full_ctx;
>    495			fsecctx_header = (struct fuse_secctx_header*) ptr;
>    496			fsecctx_header->nr_secctx = 1;
>    497			fsecctx_header->size = full_ctxlen;
>    498			ptr += sizeof(*fsecctx_header);
>    499	
>    500			fsecctx = (struct fuse_secctx*) ptr;
>    501			fsecctx->size = ctxlen;
>    502			ptr += sizeof(*fsecctx);
>    503	
>    504			strcpy(ptr, name);
>    505			ptr += strlen(name) + 1;
>    506			memcpy(ptr, ctx, ctxlen);
>    507			kfree(ctx);
>    508		} else {
>    509			full_ctxlen = sizeof(*fsecctx_header);
>    510			full_ctx = kzalloc(full_ctxlen, GFP_KERNEL);
>    511			if (!full_ctx) {
>    512				err = -ENOMEM;
>    513				goto out_err;
>    514			}
>    515			fsecctx_header = full_ctx;
>    516			fsecctx_header->size = full_ctxlen;
>    517		}
>    518	
>    519		*security_ctxlen = full_ctxlen;
>    520		*security_ctx = full_ctx;
>    521	out_err:
>    522		return err;
>    523	}
>    524	
> 
> ---
> 0-DAY CI Kernel Test Service, Intel Corporation
> https://lists.01.org/hyperkitty/list/kbuild-all(a)lists.01.org


^ permalink raw reply	[flat|nested] 10+ messages in thread

end of thread, other threads:[~2021-11-11 14:40 UTC | newest]

Thread overview: 10+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-11-10 22:55 [PATCH v3 0/1] fuse: Send file/inode security context during creation Vivek Goyal
2021-11-10 22:55 ` [Virtio-fs] " Vivek Goyal
2021-11-10 22:55 ` [PATCH v3 1/1] fuse: Send security context of inode on file creation Vivek Goyal
2021-11-10 22:55   ` [Virtio-fs] " Vivek Goyal
2021-11-11  9:54   ` kernel test robot
2021-11-11 14:40     ` Vivek Goyal
2021-11-11 14:32   ` Vivek Goyal
2021-11-11 14:32     ` [Virtio-fs] " Vivek Goyal
2021-11-11 14:32   ` Vivek Goyal
2021-11-11 14:32     ` [Virtio-fs] " Vivek Goyal

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.