selinux.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH v2 0/2] vfs/security/NFS/btrfs: clean up and fix LSM option handling
@ 2021-05-17 13:41 Ondrej Mosnacek
  2021-05-17 13:42 ` [PATCH v2 1/2] vfs,LSM: introduce the FS_HANDLES_LSM_OPTS flag Ondrej Mosnacek
  2021-05-17 13:42 ` [PATCH v2 2/2] selinux: fix SECURITY_LSM_NATIVE_LABELS flag handling on double mount Ondrej Mosnacek
  0 siblings, 2 replies; 5+ messages in thread
From: Ondrej Mosnacek @ 2021-05-17 13:41 UTC (permalink / raw)
  To: linux-security-module, selinux
  Cc: linux-fsdevel, linux-nfs, linux-btrfs, Paul Moore,
	Olga Kornievskaia, Al Viro, David Howells, Stephen Smalley,
	Richard Haines

This series fixes two bugs:
1. A bug with BTRFS where LSM options are ignored when BTRFS is mounted
   via the new fsconfig(2) API. (fixed by patch 1)
2. A bug with NFS + SELinux where an attempt to do the same mount twice
   might incidentally turn off LSM labeling, making any fresh inode
   show up as unlabeled. (fixed by patch 2, with patch 1 as a prereq)

For bug (1.) I previously posted a different patch [1], which is no
longer needed if these patches are applied.

While these patches do add a new fs_type flag (which seems to be frowned
upon), they also reduce the semantics of FS_BINARY_MOUNT_DATA flag to
*only* the mount data being binary, while before it was also (ab)used
to skip mount option processing in SELinux for NFS and BTRFS. The result
is perhaps still not perfect, but it seems to be the only non-invasive
solution for these bugs in the short term. Once BTRFS is finally
converted to the new mount API, a lot of the ugliness can likely be
refactored to something nicer (and these patches do not really make that
any harder to do, IMHO).

I tested the patches by running the NFS part of the SELinux testsuite
[2] (which is now fully passing). I also ran the proposed BTRFS SELinux
test coverage for selinux-testsuite [3], which is now passing.

Changes since v1:
- in BTRFS, move the FS_HANDLES_LSM_OPTS flag to btrfs_root_fs_type, and
  remove FS_BINARY_MOUNTDATA from both fs_types now

v1: https://lore.kernel.org/selinux/20210409111254.271800-1-omosnace@redhat.com/T/

[1] https://lore.kernel.org/selinux/20210401065403.GA1363493@infradead.org/T/
[2] https://github.com/SELinuxProject/selinux-testsuite/
[3] https://lore.kernel.org/selinux/20201103110121.53919-2-richard_c_haines@btinternet.com/
    ^^ the original patch no longer applies - a rebased version is here:
    https://github.com/WOnder93/selinux-testsuite/commit/212e76b5bd0775c7507c1996bd172de3bcbff139.patch

Ondrej Mosnacek (2):
  vfs,LSM: introduce the FS_HANDLES_LSM_OPTS flag
  selinux: fix SECURITY_LSM_NATIVE_LABELS flag handling on double mount

 fs/btrfs/super.c         | 34 +++++-----------------------------
 fs/nfs/fs_context.c      |  6 ++++--
 fs/super.c               | 10 ++++++----
 include/linux/fs.h       |  3 ++-
 security/selinux/hooks.c | 32 +++++++++++++++++---------------
 5 files changed, 34 insertions(+), 51 deletions(-)

-- 
2.31.1


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

* [PATCH v2 1/2] vfs,LSM: introduce the FS_HANDLES_LSM_OPTS flag
  2021-05-17 13:41 [PATCH v2 0/2] vfs/security/NFS/btrfs: clean up and fix LSM option handling Ondrej Mosnacek
@ 2021-05-17 13:42 ` Ondrej Mosnacek
  2021-05-19 17:07   ` Olga Kornievskaia
  2021-05-17 13:42 ` [PATCH v2 2/2] selinux: fix SECURITY_LSM_NATIVE_LABELS flag handling on double mount Ondrej Mosnacek
  1 sibling, 1 reply; 5+ messages in thread
From: Ondrej Mosnacek @ 2021-05-17 13:42 UTC (permalink / raw)
  To: linux-security-module, selinux
  Cc: linux-fsdevel, linux-nfs, linux-btrfs, Paul Moore,
	Olga Kornievskaia, Al Viro, David Howells, Stephen Smalley,
	Richard Haines

Add a new FS_HANDLES_LSM_OPTS filesystem flag to singal to VFS that the
filesystem does LSM option setting for the given mount on its own, so
the security_sb_set_mnt_opts() call in vfs_get_tree() can be skipped.

This allows the following simplifications:
1. Removal of explicit LSM option handling from BTRFS.

   This exists only because of the double-layer mount that BTRFS is
   doing for its subvolume support. Setting FS_HANDLES_LSM_OPTS on the
   inner layer (btrfs_root_fs_type) and unsetting FS_BINARY_MOUNTDATA
   from both layers allows us to leave the LSM option handling entirely
   on VFS as part of the outer vfs_get_tree() call.

2. Removal of FS_BINARY_MOUNTDATA flags from BTRFS's fs_types.

   After applying (1.), we can let VFS eat away LSM opts at the outer
   mount layer and then do selinux_set_mnt_opts() with these opts, so
   setting the flag is no longer needed neither for preserving the LSM
   opts, nor for the SELinux double-set_mnt_opts exception.

3. Removal of the ugly FS_BINARY_MOUNTDATA special case from
   selinux_set_mnt_opts().

   Applying (1.) and also setting FS_HANDLES_LSM_OPTS on NFS fs_types
   (which needs to unavoidably do the LSM options handling on its own
   due to the SECURITY_LSM_NATIVE_LABELS flag usage) gets us to the
   state where there is an exactly one security_sb_set_mnt_opts() or
   security_sb_clone_mnt_opts() call for each superblock, so the rather
   hacky FS_BINARY_MOUNTDATA special case can be finally removed from
   security_sb_set_mnt_opts().

The only other filesystem that sets FS_BINARY_MOUNTDATA is coda, which
is also the only one that has binary mount data && doesn't do its own
LSM options handling. So for coda we leave FS_HANDLES_LSM_OPTS unset and
the behavior remains unchanged - with fsconfig(2) it (probably) won't
even mount and with mount(2) it still won't support LSM options (and the
security_sb_set_mnt_opts() will be always performed with empty LSM
options as before).

AFAICT, this shouldn't negatively affect the other LSMs. In fact, I
think AppArmor will now gain the ability to do its DFA matching on BTRFS
mount options, which was prevented before due to FS_BINARY_MOUNTDATA
being set on both its fs_types.

Signed-off-by: Ondrej Mosnacek <omosnace@redhat.com>
---
 fs/btrfs/super.c         | 34 +++++-----------------------------
 fs/nfs/fs_context.c      |  6 ++++--
 fs/super.c               | 10 ++++++----
 include/linux/fs.h       |  3 ++-
 security/selinux/hooks.c | 15 ---------------
 5 files changed, 17 insertions(+), 51 deletions(-)

diff --git a/fs/btrfs/super.c b/fs/btrfs/super.c
index 4a396c1147f1..80716ead1cde 100644
--- a/fs/btrfs/super.c
+++ b/fs/btrfs/super.c
@@ -1666,19 +1666,12 @@ static struct dentry *btrfs_mount_root(struct file_system_type *fs_type,
 	struct btrfs_device *device = NULL;
 	struct btrfs_fs_devices *fs_devices = NULL;
 	struct btrfs_fs_info *fs_info = NULL;
-	void *new_sec_opts = NULL;
 	fmode_t mode = FMODE_READ;
 	int error = 0;
 
 	if (!(flags & SB_RDONLY))
 		mode |= FMODE_WRITE;
 
-	if (data) {
-		error = security_sb_eat_lsm_opts(data, &new_sec_opts);
-		if (error)
-			return ERR_PTR(error);
-	}
-
 	/*
 	 * Setup a dummy root and fs_info for test/set super.  This is because
 	 * we don't actually fill this stuff out until open_ctree, but we need
@@ -1688,10 +1681,9 @@ static struct dentry *btrfs_mount_root(struct file_system_type *fs_type,
 	 * superblock with our given fs_devices later on at sget() time.
 	 */
 	fs_info = kvzalloc(sizeof(struct btrfs_fs_info), GFP_KERNEL);
-	if (!fs_info) {
-		error = -ENOMEM;
-		goto error_sec_opts;
-	}
+	if (!fs_info)
+		return ERR_PTR(-ENOMEM);
+
 	btrfs_init_fs_info(fs_info);
 
 	fs_info->super_copy = kzalloc(BTRFS_SUPER_INFO_SIZE, GFP_KERNEL);
@@ -1748,9 +1740,6 @@ static struct dentry *btrfs_mount_root(struct file_system_type *fs_type,
 			set_bit(BTRFS_FS_CSUM_IMPL_FAST, &fs_info->flags);
 		error = btrfs_fill_super(s, fs_devices, data);
 	}
-	if (!error)
-		error = security_sb_set_mnt_opts(s, new_sec_opts, 0, NULL);
-	security_free_mnt_opts(&new_sec_opts);
 	if (error) {
 		deactivate_locked_super(s);
 		return ERR_PTR(error);
@@ -1762,8 +1751,6 @@ error_close_devices:
 	btrfs_close_devices(fs_devices);
 error_fs_info:
 	btrfs_free_fs_info(fs_info);
-error_sec_opts:
-	security_free_mnt_opts(&new_sec_opts);
 	return ERR_PTR(error);
 }
 
@@ -1925,17 +1912,6 @@ static int btrfs_remount(struct super_block *sb, int *flags, char *data)
 	sync_filesystem(sb);
 	set_bit(BTRFS_FS_STATE_REMOUNTING, &fs_info->fs_state);
 
-	if (data) {
-		void *new_sec_opts = NULL;
-
-		ret = security_sb_eat_lsm_opts(data, &new_sec_opts);
-		if (!ret)
-			ret = security_sb_remount(sb, new_sec_opts);
-		security_free_mnt_opts(&new_sec_opts);
-		if (ret)
-			goto restore;
-	}
-
 	ret = btrfs_parse_options(fs_info, data, *flags);
 	if (ret)
 		goto restore;
@@ -2385,7 +2361,7 @@ static struct file_system_type btrfs_fs_type = {
 	.name		= "btrfs",
 	.mount		= btrfs_mount,
 	.kill_sb	= btrfs_kill_super,
-	.fs_flags	= FS_REQUIRES_DEV | FS_BINARY_MOUNTDATA,
+	.fs_flags	= FS_REQUIRES_DEV,
 };
 
 static struct file_system_type btrfs_root_fs_type = {
@@ -2393,7 +2369,7 @@ static struct file_system_type btrfs_root_fs_type = {
 	.name		= "btrfs",
 	.mount		= btrfs_mount_root,
 	.kill_sb	= btrfs_kill_super,
-	.fs_flags	= FS_REQUIRES_DEV | FS_BINARY_MOUNTDATA,
+	.fs_flags	= FS_REQUIRES_DEV | FS_HANDLES_LSM_OPTS,
 };
 
 MODULE_ALIAS_FS("btrfs");
diff --git a/fs/nfs/fs_context.c b/fs/nfs/fs_context.c
index d95c9a39bc70..b5db4160e89b 100644
--- a/fs/nfs/fs_context.c
+++ b/fs/nfs/fs_context.c
@@ -1557,7 +1557,8 @@ struct file_system_type nfs_fs_type = {
 	.init_fs_context	= nfs_init_fs_context,
 	.parameters		= nfs_fs_parameters,
 	.kill_sb		= nfs_kill_super,
-	.fs_flags		= FS_RENAME_DOES_D_MOVE|FS_BINARY_MOUNTDATA,
+	.fs_flags		= FS_RENAME_DOES_D_MOVE|FS_BINARY_MOUNTDATA|
+				  FS_HANDLES_LSM_OPTS,
 };
 MODULE_ALIAS_FS("nfs");
 EXPORT_SYMBOL_GPL(nfs_fs_type);
@@ -1569,7 +1570,8 @@ struct file_system_type nfs4_fs_type = {
 	.init_fs_context	= nfs_init_fs_context,
 	.parameters		= nfs_fs_parameters,
 	.kill_sb		= nfs_kill_super,
-	.fs_flags		= FS_RENAME_DOES_D_MOVE|FS_BINARY_MOUNTDATA,
+	.fs_flags		= FS_RENAME_DOES_D_MOVE|FS_BINARY_MOUNTDATA|
+				  FS_HANDLES_LSM_OPTS,
 };
 MODULE_ALIAS_FS("nfs4");
 MODULE_ALIAS("nfs4");
diff --git a/fs/super.c b/fs/super.c
index 11b7e7213fd1..918c77b8c161 100644
--- a/fs/super.c
+++ b/fs/super.c
@@ -1520,10 +1520,12 @@ int vfs_get_tree(struct fs_context *fc)
 	smp_wmb();
 	sb->s_flags |= SB_BORN;
 
-	error = security_sb_set_mnt_opts(sb, fc->security, 0, NULL);
-	if (unlikely(error)) {
-		fc_drop_locked(fc);
-		return error;
+	if (!(fc->fs_type->fs_flags & FS_HANDLES_LSM_OPTS)) {
+		error = security_sb_set_mnt_opts(sb, fc->security, 0, NULL);
+		if (unlikely(error)) {
+			fc_drop_locked(fc);
+			return error;
+		}
 	}
 
 	/*
diff --git a/include/linux/fs.h b/include/linux/fs.h
index c3c88fdb9b2a..36f9cd37bc83 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -2469,7 +2469,8 @@ struct file_system_type {
 #define FS_HAS_SUBTYPE		4
 #define FS_USERNS_MOUNT		8	/* Can be mounted by userns root */
 #define FS_DISALLOW_NOTIFY_PERM	16	/* Disable fanotify permission events */
-#define FS_ALLOW_IDMAP         32      /* FS has been updated to handle vfs idmappings. */
+#define FS_ALLOW_IDMAP		32	/* FS has been updated to handle vfs idmappings. */
+#define FS_HANDLES_LSM_OPTS	64	/* FS handles LSM opts on its own - skip it in VFS */
 #define FS_THP_SUPPORT		8192	/* Remove once all fs converted */
 #define FS_RENAME_DOES_D_MOVE	32768	/* FS will handle d_move() during rename() internally. */
 	int (*init_fs_context)(struct fs_context *);
diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
index eaea837d89d1..041529cbf214 100644
--- a/security/selinux/hooks.c
+++ b/security/selinux/hooks.c
@@ -684,21 +684,6 @@ static int selinux_set_mnt_opts(struct super_block *sb,
 		goto out;
 	}
 
-	/*
-	 * Binary mount data FS will come through this function twice.  Once
-	 * from an explicit call and once from the generic calls from the vfs.
-	 * Since the generic VFS calls will not contain any security mount data
-	 * we need to skip the double mount verification.
-	 *
-	 * This does open a hole in which we will not notice if the first
-	 * mount using this sb set explict options and a second mount using
-	 * this sb does not set any security options.  (The first options
-	 * will be used for both mounts)
-	 */
-	if ((sbsec->flags & SE_SBINITIALIZED) && (sb->s_type->fs_flags & FS_BINARY_MOUNTDATA)
-	    && !opts)
-		goto out;
-
 	root_isec = backing_inode_security_novalidate(root);
 
 	/*
-- 
2.31.1


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

* [PATCH v2 2/2] selinux: fix SECURITY_LSM_NATIVE_LABELS flag handling on double mount
  2021-05-17 13:41 [PATCH v2 0/2] vfs/security/NFS/btrfs: clean up and fix LSM option handling Ondrej Mosnacek
  2021-05-17 13:42 ` [PATCH v2 1/2] vfs,LSM: introduce the FS_HANDLES_LSM_OPTS flag Ondrej Mosnacek
@ 2021-05-17 13:42 ` Ondrej Mosnacek
  1 sibling, 0 replies; 5+ messages in thread
From: Ondrej Mosnacek @ 2021-05-17 13:42 UTC (permalink / raw)
  To: linux-security-module, selinux
  Cc: linux-fsdevel, linux-nfs, linux-btrfs, Paul Moore,
	Olga Kornievskaia, Al Viro, David Howells, Stephen Smalley,
	Richard Haines

When mounting an NFS export that is a mountpoint on the host, doing the
same mount a second time leads to a security_sb_set_mnt_opts() call on
an already intialized superblock, which leaves the
SECURITY_LSM_NATIVE_LABELS flag unset even if it's provided by the FS.
NFS then obediently clears NFS_CAP_SECURITY_LABEL from its server
capability set, leading to any newly created inodes for this superblock
to end up without labels.

To fix this, make sure to return the SECURITY_LSM_NATIVE_LABELS flag
when security_sb_set_mnt_opts() is called on an already initialized
superblock with matching security options.

While there, also do a sanity check to ensure that
SECURITY_LSM_NATIVE_LABELS is set in kflags if and only if
sbsec->behavior == SECURITY_FS_USE_NATIVE.

Minimal reproducer:
    # systemctl start nfs-server
    # exportfs -o rw,no_root_squash,security_label localhost:/
    # mount -t nfs -o "nfsvers=4.2" localhost:/etc /mnt
    # mount -t nfs -o "nfsvers=4.2" localhost:/etc /mnt
    # ls -lZ /mnt
    [all labels are system_u:object_r:unlabeled_t:s0]

Signed-off-by: Ondrej Mosnacek <omosnace@redhat.com>
---
 security/selinux/hooks.c | 17 +++++++++++++++++
 1 file changed, 17 insertions(+)

diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
index 041529cbf214..367e7739cb18 100644
--- a/security/selinux/hooks.c
+++ b/security/selinux/hooks.c
@@ -734,7 +734,24 @@ static int selinux_set_mnt_opts(struct super_block *sb,
 		/* previously mounted with options, but not on this attempt? */
 		if ((sbsec->flags & SE_MNTMASK) && !opts)
 			goto out_double_mount;
+
+		/*
+		 * If we are checking an already initialized mount and the
+		 * options match, make sure to return back the
+		 * SECURITY_LSM_NATIVE_LABELS flag if applicable. If the
+		 * superblock has the NATIVE behavior set and the FS is not
+		 * signaling its support (or vice versa), then it is a
+		 * programmer error, so emit a WARNING and return -EINVAL.
+		 */
 		rc = 0;
+		if (sbsec->behavior == SECURITY_FS_USE_NATIVE) {
+			if (WARN_ON(!(kern_flags & SECURITY_LSM_NATIVE_LABELS)))
+				rc = -EINVAL;
+			else
+				*set_kern_flags |= SECURITY_LSM_NATIVE_LABELS;
+		} else if (WARN_ON(kern_flags & SECURITY_LSM_NATIVE_LABELS)) {
+			rc = -EINVAL;
+		}
 		goto out;
 	}
 
-- 
2.31.1


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

* Re: [PATCH v2 1/2] vfs,LSM: introduce the FS_HANDLES_LSM_OPTS flag
  2021-05-17 13:42 ` [PATCH v2 1/2] vfs,LSM: introduce the FS_HANDLES_LSM_OPTS flag Ondrej Mosnacek
@ 2021-05-19 17:07   ` Olga Kornievskaia
  2021-10-12 19:21     ` Stephen Smalley
  0 siblings, 1 reply; 5+ messages in thread
From: Olga Kornievskaia @ 2021-05-19 17:07 UTC (permalink / raw)
  To: Ondrej Mosnacek
  Cc: Linux Security Module list, SElinux list, linux-fsdevel,
	linux-nfs, linux-btrfs, Paul Moore, Al Viro, David Howells,
	Stephen Smalley, Richard Haines

On Mon, May 17, 2021 at 9:42 AM Ondrej Mosnacek <omosnace@redhat.com> wrote:
>
> Add a new FS_HANDLES_LSM_OPTS filesystem flag to singal to VFS that the
> filesystem does LSM option setting for the given mount on its own, so
> the security_sb_set_mnt_opts() call in vfs_get_tree() can be skipped.
>
> This allows the following simplifications:
> 1. Removal of explicit LSM option handling from BTRFS.
>
>    This exists only because of the double-layer mount that BTRFS is
>    doing for its subvolume support. Setting FS_HANDLES_LSM_OPTS on the
>    inner layer (btrfs_root_fs_type) and unsetting FS_BINARY_MOUNTDATA
>    from both layers allows us to leave the LSM option handling entirely
>    on VFS as part of the outer vfs_get_tree() call.
>
> 2. Removal of FS_BINARY_MOUNTDATA flags from BTRFS's fs_types.
>
>    After applying (1.), we can let VFS eat away LSM opts at the outer
>    mount layer and then do selinux_set_mnt_opts() with these opts, so
>    setting the flag is no longer needed neither for preserving the LSM
>    opts, nor for the SELinux double-set_mnt_opts exception.
>
> 3. Removal of the ugly FS_BINARY_MOUNTDATA special case from
>    selinux_set_mnt_opts().
>
>    Applying (1.) and also setting FS_HANDLES_LSM_OPTS on NFS fs_types
>    (which needs to unavoidably do the LSM options handling on its own
>    due to the SECURITY_LSM_NATIVE_LABELS flag usage) gets us to the
>    state where there is an exactly one security_sb_set_mnt_opts() or
>    security_sb_clone_mnt_opts() call for each superblock, so the rather
>    hacky FS_BINARY_MOUNTDATA special case can be finally removed from
>    security_sb_set_mnt_opts().
>
> The only other filesystem that sets FS_BINARY_MOUNTDATA is coda, which
> is also the only one that has binary mount data && doesn't do its own
> LSM options handling. So for coda we leave FS_HANDLES_LSM_OPTS unset and
> the behavior remains unchanged - with fsconfig(2) it (probably) won't
> even mount and with mount(2) it still won't support LSM options (and the
> security_sb_set_mnt_opts() will be always performed with empty LSM
> options as before).
>
> AFAICT, this shouldn't negatively affect the other LSMs. In fact, I
> think AppArmor will now gain the ability to do its DFA matching on BTRFS
> mount options, which was prevented before due to FS_BINARY_MOUNTDATA
> being set on both its fs_types.

Tested-by: Olga Kornievskaia <kolga@netapp.com> (both patches).

>
> Signed-off-by: Ondrej Mosnacek <omosnace@redhat.com>
> ---
>  fs/btrfs/super.c         | 34 +++++-----------------------------
>  fs/nfs/fs_context.c      |  6 ++++--
>  fs/super.c               | 10 ++++++----
>  include/linux/fs.h       |  3 ++-
>  security/selinux/hooks.c | 15 ---------------
>  5 files changed, 17 insertions(+), 51 deletions(-)
>
> diff --git a/fs/btrfs/super.c b/fs/btrfs/super.c
> index 4a396c1147f1..80716ead1cde 100644
> --- a/fs/btrfs/super.c
> +++ b/fs/btrfs/super.c
> @@ -1666,19 +1666,12 @@ static struct dentry *btrfs_mount_root(struct file_system_type *fs_type,
>         struct btrfs_device *device = NULL;
>         struct btrfs_fs_devices *fs_devices = NULL;
>         struct btrfs_fs_info *fs_info = NULL;
> -       void *new_sec_opts = NULL;
>         fmode_t mode = FMODE_READ;
>         int error = 0;
>
>         if (!(flags & SB_RDONLY))
>                 mode |= FMODE_WRITE;
>
> -       if (data) {
> -               error = security_sb_eat_lsm_opts(data, &new_sec_opts);
> -               if (error)
> -                       return ERR_PTR(error);
> -       }
> -
>         /*
>          * Setup a dummy root and fs_info for test/set super.  This is because
>          * we don't actually fill this stuff out until open_ctree, but we need
> @@ -1688,10 +1681,9 @@ static struct dentry *btrfs_mount_root(struct file_system_type *fs_type,
>          * superblock with our given fs_devices later on at sget() time.
>          */
>         fs_info = kvzalloc(sizeof(struct btrfs_fs_info), GFP_KERNEL);
> -       if (!fs_info) {
> -               error = -ENOMEM;
> -               goto error_sec_opts;
> -       }
> +       if (!fs_info)
> +               return ERR_PTR(-ENOMEM);
> +
>         btrfs_init_fs_info(fs_info);
>
>         fs_info->super_copy = kzalloc(BTRFS_SUPER_INFO_SIZE, GFP_KERNEL);
> @@ -1748,9 +1740,6 @@ static struct dentry *btrfs_mount_root(struct file_system_type *fs_type,
>                         set_bit(BTRFS_FS_CSUM_IMPL_FAST, &fs_info->flags);
>                 error = btrfs_fill_super(s, fs_devices, data);
>         }
> -       if (!error)
> -               error = security_sb_set_mnt_opts(s, new_sec_opts, 0, NULL);
> -       security_free_mnt_opts(&new_sec_opts);
>         if (error) {
>                 deactivate_locked_super(s);
>                 return ERR_PTR(error);
> @@ -1762,8 +1751,6 @@ error_close_devices:
>         btrfs_close_devices(fs_devices);
>  error_fs_info:
>         btrfs_free_fs_info(fs_info);
> -error_sec_opts:
> -       security_free_mnt_opts(&new_sec_opts);
>         return ERR_PTR(error);
>  }
>
> @@ -1925,17 +1912,6 @@ static int btrfs_remount(struct super_block *sb, int *flags, char *data)
>         sync_filesystem(sb);
>         set_bit(BTRFS_FS_STATE_REMOUNTING, &fs_info->fs_state);
>
> -       if (data) {
> -               void *new_sec_opts = NULL;
> -
> -               ret = security_sb_eat_lsm_opts(data, &new_sec_opts);
> -               if (!ret)
> -                       ret = security_sb_remount(sb, new_sec_opts);
> -               security_free_mnt_opts(&new_sec_opts);
> -               if (ret)
> -                       goto restore;
> -       }
> -
>         ret = btrfs_parse_options(fs_info, data, *flags);
>         if (ret)
>                 goto restore;
> @@ -2385,7 +2361,7 @@ static struct file_system_type btrfs_fs_type = {
>         .name           = "btrfs",
>         .mount          = btrfs_mount,
>         .kill_sb        = btrfs_kill_super,
> -       .fs_flags       = FS_REQUIRES_DEV | FS_BINARY_MOUNTDATA,
> +       .fs_flags       = FS_REQUIRES_DEV,
>  };
>
>  static struct file_system_type btrfs_root_fs_type = {
> @@ -2393,7 +2369,7 @@ static struct file_system_type btrfs_root_fs_type = {
>         .name           = "btrfs",
>         .mount          = btrfs_mount_root,
>         .kill_sb        = btrfs_kill_super,
> -       .fs_flags       = FS_REQUIRES_DEV | FS_BINARY_MOUNTDATA,
> +       .fs_flags       = FS_REQUIRES_DEV | FS_HANDLES_LSM_OPTS,
>  };
>
>  MODULE_ALIAS_FS("btrfs");
> diff --git a/fs/nfs/fs_context.c b/fs/nfs/fs_context.c
> index d95c9a39bc70..b5db4160e89b 100644
> --- a/fs/nfs/fs_context.c
> +++ b/fs/nfs/fs_context.c
> @@ -1557,7 +1557,8 @@ struct file_system_type nfs_fs_type = {
>         .init_fs_context        = nfs_init_fs_context,
>         .parameters             = nfs_fs_parameters,
>         .kill_sb                = nfs_kill_super,
> -       .fs_flags               = FS_RENAME_DOES_D_MOVE|FS_BINARY_MOUNTDATA,
> +       .fs_flags               = FS_RENAME_DOES_D_MOVE|FS_BINARY_MOUNTDATA|
> +                                 FS_HANDLES_LSM_OPTS,
>  };
>  MODULE_ALIAS_FS("nfs");
>  EXPORT_SYMBOL_GPL(nfs_fs_type);
> @@ -1569,7 +1570,8 @@ struct file_system_type nfs4_fs_type = {
>         .init_fs_context        = nfs_init_fs_context,
>         .parameters             = nfs_fs_parameters,
>         .kill_sb                = nfs_kill_super,
> -       .fs_flags               = FS_RENAME_DOES_D_MOVE|FS_BINARY_MOUNTDATA,
> +       .fs_flags               = FS_RENAME_DOES_D_MOVE|FS_BINARY_MOUNTDATA|
> +                                 FS_HANDLES_LSM_OPTS,
>  };
>  MODULE_ALIAS_FS("nfs4");
>  MODULE_ALIAS("nfs4");
> diff --git a/fs/super.c b/fs/super.c
> index 11b7e7213fd1..918c77b8c161 100644
> --- a/fs/super.c
> +++ b/fs/super.c
> @@ -1520,10 +1520,12 @@ int vfs_get_tree(struct fs_context *fc)
>         smp_wmb();
>         sb->s_flags |= SB_BORN;
>
> -       error = security_sb_set_mnt_opts(sb, fc->security, 0, NULL);
> -       if (unlikely(error)) {
> -               fc_drop_locked(fc);
> -               return error;
> +       if (!(fc->fs_type->fs_flags & FS_HANDLES_LSM_OPTS)) {
> +               error = security_sb_set_mnt_opts(sb, fc->security, 0, NULL);
> +               if (unlikely(error)) {
> +                       fc_drop_locked(fc);
> +                       return error;
> +               }
>         }
>
>         /*
> diff --git a/include/linux/fs.h b/include/linux/fs.h
> index c3c88fdb9b2a..36f9cd37bc83 100644
> --- a/include/linux/fs.h
> +++ b/include/linux/fs.h
> @@ -2469,7 +2469,8 @@ struct file_system_type {
>  #define FS_HAS_SUBTYPE         4
>  #define FS_USERNS_MOUNT                8       /* Can be mounted by userns root */
>  #define FS_DISALLOW_NOTIFY_PERM        16      /* Disable fanotify permission events */
> -#define FS_ALLOW_IDMAP         32      /* FS has been updated to handle vfs idmappings. */
> +#define FS_ALLOW_IDMAP         32      /* FS has been updated to handle vfs idmappings. */
> +#define FS_HANDLES_LSM_OPTS    64      /* FS handles LSM opts on its own - skip it in VFS */
>  #define FS_THP_SUPPORT         8192    /* Remove once all fs converted */
>  #define FS_RENAME_DOES_D_MOVE  32768   /* FS will handle d_move() during rename() internally. */
>         int (*init_fs_context)(struct fs_context *);
> diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
> index eaea837d89d1..041529cbf214 100644
> --- a/security/selinux/hooks.c
> +++ b/security/selinux/hooks.c
> @@ -684,21 +684,6 @@ static int selinux_set_mnt_opts(struct super_block *sb,
>                 goto out;
>         }
>
> -       /*
> -        * Binary mount data FS will come through this function twice.  Once
> -        * from an explicit call and once from the generic calls from the vfs.
> -        * Since the generic VFS calls will not contain any security mount data
> -        * we need to skip the double mount verification.
> -        *
> -        * This does open a hole in which we will not notice if the first
> -        * mount using this sb set explict options and a second mount using
> -        * this sb does not set any security options.  (The first options
> -        * will be used for both mounts)
> -        */
> -       if ((sbsec->flags & SE_SBINITIALIZED) && (sb->s_type->fs_flags & FS_BINARY_MOUNTDATA)
> -           && !opts)
> -               goto out;
> -
>         root_isec = backing_inode_security_novalidate(root);
>
>         /*
> --
> 2.31.1
>

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

* Re: [PATCH v2 1/2] vfs,LSM: introduce the FS_HANDLES_LSM_OPTS flag
  2021-05-19 17:07   ` Olga Kornievskaia
@ 2021-10-12 19:21     ` Stephen Smalley
  0 siblings, 0 replies; 5+ messages in thread
From: Stephen Smalley @ 2021-10-12 19:21 UTC (permalink / raw)
  To: Olga Kornievskaia
  Cc: Ondrej Mosnacek, Linux Security Module list, SElinux list,
	linux-fsdevel, linux-nfs, linux-btrfs, Paul Moore, Al Viro,
	David Howells, Richard Haines

On Wed, May 19, 2021 at 1:08 PM Olga Kornievskaia <aglo@umich.edu> wrote:
>
> On Mon, May 17, 2021 at 9:42 AM Ondrej Mosnacek <omosnace@redhat.com> wrote:
> >
> > Add a new FS_HANDLES_LSM_OPTS filesystem flag to singal to VFS that the
> > filesystem does LSM option setting for the given mount on its own, so
> > the security_sb_set_mnt_opts() call in vfs_get_tree() can be skipped.
> >
> > This allows the following simplifications:
> > 1. Removal of explicit LSM option handling from BTRFS.
> >
> >    This exists only because of the double-layer mount that BTRFS is
> >    doing for its subvolume support. Setting FS_HANDLES_LSM_OPTS on the
> >    inner layer (btrfs_root_fs_type) and unsetting FS_BINARY_MOUNTDATA
> >    from both layers allows us to leave the LSM option handling entirely
> >    on VFS as part of the outer vfs_get_tree() call.
> >
> > 2. Removal of FS_BINARY_MOUNTDATA flags from BTRFS's fs_types.
> >
> >    After applying (1.), we can let VFS eat away LSM opts at the outer
> >    mount layer and then do selinux_set_mnt_opts() with these opts, so
> >    setting the flag is no longer needed neither for preserving the LSM
> >    opts, nor for the SELinux double-set_mnt_opts exception.
> >
> > 3. Removal of the ugly FS_BINARY_MOUNTDATA special case from
> >    selinux_set_mnt_opts().
> >
> >    Applying (1.) and also setting FS_HANDLES_LSM_OPTS on NFS fs_types
> >    (which needs to unavoidably do the LSM options handling on its own
> >    due to the SECURITY_LSM_NATIVE_LABELS flag usage) gets us to the
> >    state where there is an exactly one security_sb_set_mnt_opts() or
> >    security_sb_clone_mnt_opts() call for each superblock, so the rather
> >    hacky FS_BINARY_MOUNTDATA special case can be finally removed from
> >    security_sb_set_mnt_opts().
> >
> > The only other filesystem that sets FS_BINARY_MOUNTDATA is coda, which
> > is also the only one that has binary mount data && doesn't do its own
> > LSM options handling. So for coda we leave FS_HANDLES_LSM_OPTS unset and
> > the behavior remains unchanged - with fsconfig(2) it (probably) won't
> > even mount and with mount(2) it still won't support LSM options (and the
> > security_sb_set_mnt_opts() will be always performed with empty LSM
> > options as before).
> >
> > AFAICT, this shouldn't negatively affect the other LSMs. In fact, I
> > think AppArmor will now gain the ability to do its DFA matching on BTRFS
> > mount options, which was prevented before due to FS_BINARY_MOUNTDATA
> > being set on both its fs_types.
>
> Tested-by: Olga Kornievskaia <kolga@netapp.com> (both patches).

Is this stalled on getting an Ack from vfs maintainers?

>
> >
> > Signed-off-by: Ondrej Mosnacek <omosnace@redhat.com>
> > ---
> >  fs/btrfs/super.c         | 34 +++++-----------------------------
> >  fs/nfs/fs_context.c      |  6 ++++--
> >  fs/super.c               | 10 ++++++----
> >  include/linux/fs.h       |  3 ++-
> >  security/selinux/hooks.c | 15 ---------------
> >  5 files changed, 17 insertions(+), 51 deletions(-)
> >
> > diff --git a/fs/btrfs/super.c b/fs/btrfs/super.c
> > index 4a396c1147f1..80716ead1cde 100644
> > --- a/fs/btrfs/super.c
> > +++ b/fs/btrfs/super.c
> > @@ -1666,19 +1666,12 @@ static struct dentry *btrfs_mount_root(struct file_system_type *fs_type,
> >         struct btrfs_device *device = NULL;
> >         struct btrfs_fs_devices *fs_devices = NULL;
> >         struct btrfs_fs_info *fs_info = NULL;
> > -       void *new_sec_opts = NULL;
> >         fmode_t mode = FMODE_READ;
> >         int error = 0;
> >
> >         if (!(flags & SB_RDONLY))
> >                 mode |= FMODE_WRITE;
> >
> > -       if (data) {
> > -               error = security_sb_eat_lsm_opts(data, &new_sec_opts);
> > -               if (error)
> > -                       return ERR_PTR(error);
> > -       }
> > -
> >         /*
> >          * Setup a dummy root and fs_info for test/set super.  This is because
> >          * we don't actually fill this stuff out until open_ctree, but we need
> > @@ -1688,10 +1681,9 @@ static struct dentry *btrfs_mount_root(struct file_system_type *fs_type,
> >          * superblock with our given fs_devices later on at sget() time.
> >          */
> >         fs_info = kvzalloc(sizeof(struct btrfs_fs_info), GFP_KERNEL);
> > -       if (!fs_info) {
> > -               error = -ENOMEM;
> > -               goto error_sec_opts;
> > -       }
> > +       if (!fs_info)
> > +               return ERR_PTR(-ENOMEM);
> > +
> >         btrfs_init_fs_info(fs_info);
> >
> >         fs_info->super_copy = kzalloc(BTRFS_SUPER_INFO_SIZE, GFP_KERNEL);
> > @@ -1748,9 +1740,6 @@ static struct dentry *btrfs_mount_root(struct file_system_type *fs_type,
> >                         set_bit(BTRFS_FS_CSUM_IMPL_FAST, &fs_info->flags);
> >                 error = btrfs_fill_super(s, fs_devices, data);
> >         }
> > -       if (!error)
> > -               error = security_sb_set_mnt_opts(s, new_sec_opts, 0, NULL);
> > -       security_free_mnt_opts(&new_sec_opts);
> >         if (error) {
> >                 deactivate_locked_super(s);
> >                 return ERR_PTR(error);
> > @@ -1762,8 +1751,6 @@ error_close_devices:
> >         btrfs_close_devices(fs_devices);
> >  error_fs_info:
> >         btrfs_free_fs_info(fs_info);
> > -error_sec_opts:
> > -       security_free_mnt_opts(&new_sec_opts);
> >         return ERR_PTR(error);
> >  }
> >
> > @@ -1925,17 +1912,6 @@ static int btrfs_remount(struct super_block *sb, int *flags, char *data)
> >         sync_filesystem(sb);
> >         set_bit(BTRFS_FS_STATE_REMOUNTING, &fs_info->fs_state);
> >
> > -       if (data) {
> > -               void *new_sec_opts = NULL;
> > -
> > -               ret = security_sb_eat_lsm_opts(data, &new_sec_opts);
> > -               if (!ret)
> > -                       ret = security_sb_remount(sb, new_sec_opts);
> > -               security_free_mnt_opts(&new_sec_opts);
> > -               if (ret)
> > -                       goto restore;
> > -       }
> > -
> >         ret = btrfs_parse_options(fs_info, data, *flags);
> >         if (ret)
> >                 goto restore;
> > @@ -2385,7 +2361,7 @@ static struct file_system_type btrfs_fs_type = {
> >         .name           = "btrfs",
> >         .mount          = btrfs_mount,
> >         .kill_sb        = btrfs_kill_super,
> > -       .fs_flags       = FS_REQUIRES_DEV | FS_BINARY_MOUNTDATA,
> > +       .fs_flags       = FS_REQUIRES_DEV,
> >  };
> >
> >  static struct file_system_type btrfs_root_fs_type = {
> > @@ -2393,7 +2369,7 @@ static struct file_system_type btrfs_root_fs_type = {
> >         .name           = "btrfs",
> >         .mount          = btrfs_mount_root,
> >         .kill_sb        = btrfs_kill_super,
> > -       .fs_flags       = FS_REQUIRES_DEV | FS_BINARY_MOUNTDATA,
> > +       .fs_flags       = FS_REQUIRES_DEV | FS_HANDLES_LSM_OPTS,
> >  };
> >
> >  MODULE_ALIAS_FS("btrfs");
> > diff --git a/fs/nfs/fs_context.c b/fs/nfs/fs_context.c
> > index d95c9a39bc70..b5db4160e89b 100644
> > --- a/fs/nfs/fs_context.c
> > +++ b/fs/nfs/fs_context.c
> > @@ -1557,7 +1557,8 @@ struct file_system_type nfs_fs_type = {
> >         .init_fs_context        = nfs_init_fs_context,
> >         .parameters             = nfs_fs_parameters,
> >         .kill_sb                = nfs_kill_super,
> > -       .fs_flags               = FS_RENAME_DOES_D_MOVE|FS_BINARY_MOUNTDATA,
> > +       .fs_flags               = FS_RENAME_DOES_D_MOVE|FS_BINARY_MOUNTDATA|
> > +                                 FS_HANDLES_LSM_OPTS,
> >  };
> >  MODULE_ALIAS_FS("nfs");
> >  EXPORT_SYMBOL_GPL(nfs_fs_type);
> > @@ -1569,7 +1570,8 @@ struct file_system_type nfs4_fs_type = {
> >         .init_fs_context        = nfs_init_fs_context,
> >         .parameters             = nfs_fs_parameters,
> >         .kill_sb                = nfs_kill_super,
> > -       .fs_flags               = FS_RENAME_DOES_D_MOVE|FS_BINARY_MOUNTDATA,
> > +       .fs_flags               = FS_RENAME_DOES_D_MOVE|FS_BINARY_MOUNTDATA|
> > +                                 FS_HANDLES_LSM_OPTS,
> >  };
> >  MODULE_ALIAS_FS("nfs4");
> >  MODULE_ALIAS("nfs4");
> > diff --git a/fs/super.c b/fs/super.c
> > index 11b7e7213fd1..918c77b8c161 100644
> > --- a/fs/super.c
> > +++ b/fs/super.c
> > @@ -1520,10 +1520,12 @@ int vfs_get_tree(struct fs_context *fc)
> >         smp_wmb();
> >         sb->s_flags |= SB_BORN;
> >
> > -       error = security_sb_set_mnt_opts(sb, fc->security, 0, NULL);
> > -       if (unlikely(error)) {
> > -               fc_drop_locked(fc);
> > -               return error;
> > +       if (!(fc->fs_type->fs_flags & FS_HANDLES_LSM_OPTS)) {
> > +               error = security_sb_set_mnt_opts(sb, fc->security, 0, NULL);
> > +               if (unlikely(error)) {
> > +                       fc_drop_locked(fc);
> > +                       return error;
> > +               }
> >         }
> >
> >         /*
> > diff --git a/include/linux/fs.h b/include/linux/fs.h
> > index c3c88fdb9b2a..36f9cd37bc83 100644
> > --- a/include/linux/fs.h
> > +++ b/include/linux/fs.h
> > @@ -2469,7 +2469,8 @@ struct file_system_type {
> >  #define FS_HAS_SUBTYPE         4
> >  #define FS_USERNS_MOUNT                8       /* Can be mounted by userns root */
> >  #define FS_DISALLOW_NOTIFY_PERM        16      /* Disable fanotify permission events */
> > -#define FS_ALLOW_IDMAP         32      /* FS has been updated to handle vfs idmappings. */
> > +#define FS_ALLOW_IDMAP         32      /* FS has been updated to handle vfs idmappings. */
> > +#define FS_HANDLES_LSM_OPTS    64      /* FS handles LSM opts on its own - skip it in VFS */
> >  #define FS_THP_SUPPORT         8192    /* Remove once all fs converted */
> >  #define FS_RENAME_DOES_D_MOVE  32768   /* FS will handle d_move() during rename() internally. */
> >         int (*init_fs_context)(struct fs_context *);
> > diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
> > index eaea837d89d1..041529cbf214 100644
> > --- a/security/selinux/hooks.c
> > +++ b/security/selinux/hooks.c
> > @@ -684,21 +684,6 @@ static int selinux_set_mnt_opts(struct super_block *sb,
> >                 goto out;
> >         }
> >
> > -       /*
> > -        * Binary mount data FS will come through this function twice.  Once
> > -        * from an explicit call and once from the generic calls from the vfs.
> > -        * Since the generic VFS calls will not contain any security mount data
> > -        * we need to skip the double mount verification.
> > -        *
> > -        * This does open a hole in which we will not notice if the first
> > -        * mount using this sb set explict options and a second mount using
> > -        * this sb does not set any security options.  (The first options
> > -        * will be used for both mounts)
> > -        */
> > -       if ((sbsec->flags & SE_SBINITIALIZED) && (sb->s_type->fs_flags & FS_BINARY_MOUNTDATA)
> > -           && !opts)
> > -               goto out;
> > -
> >         root_isec = backing_inode_security_novalidate(root);
> >
> >         /*
> > --
> > 2.31.1
> >

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

end of thread, other threads:[~2021-10-12 19:22 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-05-17 13:41 [PATCH v2 0/2] vfs/security/NFS/btrfs: clean up and fix LSM option handling Ondrej Mosnacek
2021-05-17 13:42 ` [PATCH v2 1/2] vfs,LSM: introduce the FS_HANDLES_LSM_OPTS flag Ondrej Mosnacek
2021-05-19 17:07   ` Olga Kornievskaia
2021-10-12 19:21     ` Stephen Smalley
2021-05-17 13:42 ` [PATCH v2 2/2] selinux: fix SECURITY_LSM_NATIVE_LABELS flag handling on double mount Ondrej Mosnacek

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).