[PATCH v5 16/16] ima: Setup securityfs for IMA namespace

Stefan Berger
2021-12-08T22:18:36Z

Setup securityfs with symlinks, directories, and files for IMA
namespacing support. The same directory structure that IMA uses on the
host is also created for the namespacing case.

The securityfs file and directory ownerships cannot be set when the
IMA namespace is initialized. Therefore, delay the setup of the file
system to a later point when securityfs is in securityfs_fill_super.

Only take an additional reference on the dentry in the init_user_ns
case. This avoids having to explicitly free the dentries when the
superblock is killed. Adjust the documentation of securityfs_create_dentry
to reflect this.

This filesystem can now be mounted as follows:

mount -t securityfs /sys/kernel/security/ /sys/kernel/security/

The following directories, symlinks, and files are then available.

$ ls -l sys/kernel/security/
total 0
lr--r--r--. 1 root root 0 Dec 2 00:18 ima -> integrity/ima
drwxr-xr-x. 3 root root 0 Dec 2 00:18 integrity

$ ls -l sys/kernel/security/ima/
total 0
-r--r-----. 1 root root 0 Dec 2 00:18 ascii_runtime_measurements
-r--r-----. 1 root root 0 Dec 2 00:18 binary_runtime_measurements
-rw-------. 1 root root 0 Dec 2 00:18 policy
-r--r-----. 1 root root 0 Dec 2 00:18 runtime_measurements_count
-r--r-----. 1 root root 0 Dec 2 00:18 violations

Signed-off-by: Stefan Berger <stefanb@linux.ibm.com>
Signed-off-by: James Bottomley <James.Bottomley@HansenPartnership.com>
---
include/linux/ima.h | 12 ++++++++++-
security/inode.c | 20 ++++++++++++++-----
security/integrity/ima/ima_fs.c | 35 ++++++++++++++++++++++++++-------
3 files changed, 54 insertions(+), 13 deletions(-)

diff --git a/include/linux/ima.h b/include/linux/ima.h
index 4dd64e318b15..32bf98092143 100644
--- a/include/linux/ima.h
+++ b/include/linux/ima.h
@@ -66,6 +66,9 @@ static inline const char * const *arch_get_ima_policy(void)
}
#endif

+extern int ima_securityfs_init(struct user_namespace *user_ns,
+ struct dentry *root);
+
#else
static inline enum hash_algo ima_get_current_hash_algo(void)
{
@@ -154,6 +157,11 @@ static inline int ima_measure_critical_data(const char *event_label,
return -ENOENT;
}

+static inline int ima_securityfs_init(struct user_namespace *ns, struct dentry *root)
+{
+ return 0;
+}
+
#endif /* CONFIG_IMA */

#ifndef CONFIG_IMA_KEXEC
@@ -221,7 +229,8 @@ struct ima_h_table {
};

enum {
- IMAFS_DENTRY_DIR = 0,
+ IMAFS_DENTRY_INTEGRITY_DIR = 0,
+ IMAFS_DENTRY_DIR,
IMAFS_DENTRY_SYMLINK,
IMAFS_DENTRY_BINARY_RUNTIME_MEASUREMENTS,
IMAFS_DENTRY_ASCII_RUNTIME_MEASUREMENTS,
@@ -336,6 +345,7 @@ static inline struct ima_namespace *get_current_ns(void)
{
return &init_ima_ns;
}
+
#endif /* CONFIG_IMA_NS */

#if defined(CONFIG_IMA_APPRAISE) && defined(CONFIG_INTEGRITY_TRUSTED_KEYRING)
diff --git a/security/inode.c b/security/inode.c
index c1aef4613a17..eaccba7017d9 100644
--- a/security/inode.c
+++ b/security/inode.c
@@ -16,6 +16,7 @@
#include <linux/fs_context.h>
#include <linux/mount.h>
#include <linux/pagemap.h>
+#include <linux/ima.h>
#include <linux/init.h>
#include <linux/namei.h>
#include <linux/security.h>
@@ -41,6 +42,7 @@ 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;

error = simple_fill_super(sb, SECURITYFS_MAGIC, files);
@@ -49,7 +51,10 @@ static int securityfs_fill_super(struct super_block *sb, struct fs_context *fc)

sb->s_op = &securityfs_super_operations;

- return 0;
+ if (ns != &init_user_ns)
+ error = ima_securityfs_init(ns, sb->s_root);
+
+ return error;
}

static int securityfs_get_tree(struct fs_context *fc)
@@ -97,11 +102,15 @@ static struct file_system_type fs_type = {
* securityfs_create_dir() function is recommended to be used
* instead).
*
- * This function returns a pointer to a dentry if it succeeds. This
+ * This function returns a pointer to a dentry if it succeeds. If the
+ * dentry was created while the init_user_ns was active, then this
* pointer must be passed to the securityfs_remove() function when the
* file is to be removed (no automatic cleanup happens if your module
- * is unloaded, you are responsible here). If an error occurs, the
- * function will return the error value (via ERR_PTR).
+ * is unloaded, you are responsible here). If any other user namespace
+ * was active then the dentry may be removed using securityfs_remove()
+ * when a module is removed but no cleanup must be done once the
+ * superblock was delete since then it will be deleted automatically.
+ * If an error occurs, the function will return the error value (via ERR_PTR).
*
* If securityfs is not enabled in the kernel, the value %-ENODEV is
* returned.
@@ -169,7 +178,8 @@ static struct dentry *securityfs_create_dentry(const char *name, umode_t mode,
inode->i_fop = fops;
}
d_instantiate(dentry, inode);
- dget(dentry);
+ if (ns == &init_user_ns)
+ dget(dentry);
inode_unlock(dir);
return dentry;

diff --git a/security/integrity/ima/ima_fs.c b/security/integrity/ima/ima_fs.c
index 3810d11fb463..778983fd9a73 100644
--- a/security/integrity/ima/ima_fs.c
+++ b/security/integrity/ima/ima_fs.c
@@ -447,8 +447,12 @@ static const struct file_operations ima_measure_policy_ops = {
.llseek = generic_file_llseek,
};

-static void ima_fs_ns_free_dentries(struct ima_namespace *ns)
+/* Remove the dentries. In case user_ns == &init_user_ns this function
+ * does not need to be called since there's one less reference to dentries.
+ */
+static void ima_fs_ns_free_dentries(struct user_namespace *user_ns)
{
+ struct ima_namespace *ns = user_ns->ima_ns;
int i;

for (i = IMAFS_DENTRY_LAST - 1; i >= 0; i--)
@@ -457,18 +461,35 @@ static void ima_fs_ns_free_dentries(struct ima_namespace *ns)
memset(ns->dentry, 0, sizeof(ns->dentry));
}

-static int __init ima_securityfs_init(struct user_namespace *user_ns)
+int ima_securityfs_init(struct user_namespace *user_ns, struct dentry *root)
{
struct ima_namespace *ns = user_ns->ima_ns;
struct dentry *ima_dir;

- ns->dentry[IMAFS_DENTRY_DIR] = securityfs_create_dir("ima", integrity_dir);
+ /* already initialized? */
+ if (ns->dentry[IMAFS_DENTRY_INTEGRITY_DIR])
+ return 0;
+
+ /* FIXME: update when evm and integrity are namespaced */
+ if (user_ns != &init_user_ns) {
+ ns->dentry[IMAFS_DENTRY_INTEGRITY_DIR] =
+ securityfs_create_dir("integrity", root);
+ if (IS_ERR(ns->dentry[IMAFS_DENTRY_INTEGRITY_DIR])) {
+ ns->dentry[IMAFS_DENTRY_INTEGRITY_DIR] = NULL;
+ return -1;
+ }
+ } else
+ ns->dentry[IMAFS_DENTRY_INTEGRITY_DIR] = integrity_dir;
+
+ ns->dentry[IMAFS_DENTRY_DIR] =
+ securityfs_create_dir("ima",
+ ns->dentry[IMAFS_DENTRY_INTEGRITY_DIR]);
if (IS_ERR(ns->dentry[IMAFS_DENTRY_DIR]))
- return -1;
+ goto out;
ima_dir = ns->dentry[IMAFS_DENTRY_DIR];

ns->dentry[IMAFS_DENTRY_SYMLINK] =
- securityfs_create_symlink("ima", NULL, "integrity/ima", NULL);
+ securityfs_create_symlink("ima", root, "integrity/ima", NULL);
if (IS_ERR(ns->dentry[IMAFS_DENTRY_SYMLINK]))
goto out;

@@ -508,11 +529,11 @@ static int __init ima_securityfs_init(struct user_namespace *user_ns)

return 0;
out:
- ima_fs_ns_free_dentries(ns);
+ ima_fs_ns_free_dentries(user_ns);
return -1;
}

int __init ima_fs_init(void)
{
- return ima_securityfs_init(&init_user_ns);
+ return ima_securityfs_init(&init_user_ns, NULL);
}
--
2.31.1