[PATCH v8 01/19] securityfs: Extend securityfs with namespacing support

Stefan Berger
2022-01-04T17:04:50Z

From: Stefan Berger <stefanb@linux.ibm.com>

To prepare for virtualization of SecurityFS, use simple_pin_fs and
simpe_release_fs only when init_user_ns is active.

Extend 'securityfs' for support of IMA namespacing so that each
IMA (user) namespace can have its own front-end for showing the currently
active policy, the measurement list, number of violations and so on.

Enable multiple instances of securityfs by keying each instance with a
pointer to the user namespace it belongs to.

Drop the additional dentry reference to enable simple cleanup of dentries
upon umount. Now the dentries do not need to be explicitly freed anymore
but we can just rely on d_genocide() and the dcache shrinker to do all
the required work.

Prevent mounting of an instance of securityfs in another user namespace
than it belongs to. Also, prevent accesses to files and directories by
a user namespace that it not either the user namespace it belongs to
or an ancestor of the user namespace that the instance of securityfs
belongs to. Do not prevent access if securityfs was bind-mounted and the
init_user_ns is the owning user namespace.

Signed-off-by: Stefan Berger <stefanb@linux.ibm.com>
Signed-off-by: James Bottomley <James.Bottomley@HansenPartnership.com>
---
security/inode.c | 75 ++++++++++++++++++++++++++++++++++++++++--------
1 file changed, 63 insertions(+), 12 deletions(-)

diff --git a/security/inode.c b/security/inode.c
index 6c326939750d..e525ba960063 100644
--- a/security/inode.c
+++ b/security/inode.c
@@ -21,9 +21,37 @@
#include <linux/security.h>
#include <linux/lsm_hooks.h>
#include <linux/magic.h>
+#include <linux/user_namespace.h>

-static struct vfsmount *mount;
-static int mount_count;
+static struct vfsmount *init_securityfs_mount;
+static int init_securityfs_mount_count;
+
+static int securityfs_permission(struct user_namespace *mnt_userns,
+ struct inode *inode, int mask)
+{
+ int err;
+
+ err = generic_permission(&init_user_ns, inode, mask);
+ if (!err) {
+ /* Unless bind-mounted, deny access if current_user_ns() is not
+ * ancestor.
+ */
+ if (inode->i_sb->s_user_ns != &init_user_ns &&
+ !in_userns(current_user_ns(), inode->i_sb->s_user_ns))
+ err = -EACCES;
+ }
+
+ return err;
+}
+
+static const struct inode_operations securityfs_dir_inode_operations = {
+ .permission = securityfs_permission,
+ .lookup = simple_lookup,
+};
+
+static const struct inode_operations securityfs_file_inode_operations = {
+ .permission = securityfs_permission,
+};

static void securityfs_free_inode(struct inode *inode)
{
@@ -40,20 +68,25 @@ static const struct super_operations securityfs_super_operations = {
static int securityfs_fill_super(struct super_block *sb, struct fs_context *fc)
{
static const struct tree_descr files[] = {{""}};
+ struct user_namespace *ns = fc->user_ns;
int error;

+ if (WARN_ON(ns != current_user_ns()))
+ return -EINVAL;
+
error = simple_fill_super(sb, SECURITYFS_MAGIC, files);
if (error)
return error;

sb->s_op = &securityfs_super_operations;
+ sb->s_root->d_inode->i_op = &securityfs_dir_inode_operations;

return 0;
}

static int securityfs_get_tree(struct fs_context *fc)
{
- return get_tree_single(fc, securityfs_fill_super);
+ return get_tree_keyed(fc, securityfs_fill_super, fc->user_ns);
}

static const struct fs_context_operations securityfs_context_ops = {
@@ -71,6 +104,7 @@ static struct file_system_type fs_type = {
.name = "securityfs",
.init_fs_context = securityfs_init_fs_context,
.kill_sb = kill_litter_super,
+ .fs_flags = FS_USERNS_MOUNT,
};

/**
@@ -109,6 +143,7 @@ static struct dentry *securityfs_create_dentry(const char *name, umode_t mode,
const struct file_operations *fops,
const struct inode_operations *iops)
{
+ struct user_namespace *ns = current_user_ns();
struct dentry *dentry;
struct inode *dir, *inode;
int error;
@@ -118,12 +153,19 @@ static struct dentry *securityfs_create_dentry(const char *name, umode_t mode,

pr_debug("securityfs: creating file '%s'\n",name);

- error = simple_pin_fs(&fs_type, &mount, &mount_count);
- if (error)
- return ERR_PTR(error);
+ if (ns == &init_user_ns) {
+ error = simple_pin_fs(&fs_type, &init_securityfs_mount,
+ &init_securityfs_mount_count);
+ if (error)
+ return ERR_PTR(error);
+ }

- if (!parent)
- parent = mount->mnt_root;
+ if (!parent) {
+ if (ns == &init_user_ns)
+ parent = init_securityfs_mount->mnt_root;
+ else
+ return ERR_PTR(-EINVAL);
+ }

dir = d_inode(parent);

@@ -148,7 +190,7 @@ static struct dentry *securityfs_create_dentry(const char *name, umode_t mode,
inode->i_atime = inode->i_mtime = inode->i_ctime = current_time(inode);
inode->i_private = data;
if (S_ISDIR(mode)) {
- inode->i_op = &simple_dir_inode_operations;
+ inode->i_op = &securityfs_dir_inode_operations;
inode->i_fop = &simple_dir_operations;
inc_nlink(inode);
inc_nlink(dir);
@@ -156,10 +198,10 @@ static struct dentry *securityfs_create_dentry(const char *name, umode_t mode,
inode->i_op = iops ? iops : &simple_symlink_inode_operations;
inode->i_link = data;
} else {
+ inode->i_op = &securityfs_file_inode_operations;
inode->i_fop = fops;
}
d_instantiate(dentry, inode);
- dget(dentry);
inode_unlock(dir);
return dentry;

@@ -168,7 +210,9 @@ static struct dentry *securityfs_create_dentry(const char *name, umode_t mode,
dentry = ERR_PTR(error);
out:
inode_unlock(dir);
- simple_release_fs(&mount, &mount_count);
+ if (ns == &init_user_ns)
+ simple_release_fs(&init_securityfs_mount,
+ &init_securityfs_mount_count);
return dentry;
}

@@ -294,22 +338,29 @@ EXPORT_SYMBOL_GPL(securityfs_create_symlink);
*/
void securityfs_remove(struct dentry *dentry)
{
+ struct user_namespace *ns;
struct inode *dir;

if (!dentry || IS_ERR(dentry))
return;

+ ns = dentry->d_sb->s_user_ns;
+
dir = d_inode(dentry->d_parent);
inode_lock(dir);
if (simple_positive(dentry)) {
+ dget(dentry);
if (d_is_dir(dentry))
simple_rmdir(dir, dentry);
else
simple_unlink(dir, dentry);
+ d_delete(dentry);
dput(dentry);
}
inode_unlock(dir);
- simple_release_fs(&mount, &mount_count);
+ if (ns == &init_user_ns)
+ simple_release_fs(&init_securityfs_mount,
+ &init_securityfs_mount_count);
}
EXPORT_SYMBOL_GPL(securityfs_remove);

--
2.31.1