[PATCH 8/8] !!!! UNCOMPILED & UNTESTED !!!! ima: support ima namespaces

From: Plummy McPlumbface
Date: Sat Dec 18 2021 - 06:31:20 EST


Signed-off-by: Plummy McPlumbface <plummy@mcblumbface.events>
---
include/linux/capability.h | 6 ++
include/linux/ima.h | 38 +++++++++++
include/linux/user_namespace.h | 4 ++
init/Kconfig | 10 +++
kernel/user.c | 8 +++
kernel/user_namespace.c | 2 +
security/integrity/ima/Makefile | 1 +
security/integrity/ima/ima.h | 17 +++++
security/integrity/ima/ima_appraise.c | 6 +-
security/integrity/ima/ima_asymmetric_keys.c | 2 +-
security/integrity/ima/ima_fs.c | 48 +++++++++++---
security/integrity/ima/ima_init.c | 4 ++
security/integrity/ima/ima_init_ima_ns.c | 6 ++
security/integrity/ima/ima_kexec.c | 4 +-
security/integrity/ima/ima_main.c | 68 ++++++++++++++-----
security/integrity/ima/ima_ns.c | 70 ++++++++++++++++++++
security/integrity/ima/ima_policy.c | 18 +++--
17 files changed, 273 insertions(+), 39 deletions(-)
create mode 100644 security/integrity/ima/ima_ns.c

diff --git a/include/linux/capability.h b/include/linux/capability.h
index 65efb74c3585..991579178f32 100644
--- a/include/linux/capability.h
+++ b/include/linux/capability.h
@@ -270,6 +270,12 @@ static inline bool checkpoint_restore_ns_capable(struct user_namespace *ns)
ns_capable(ns, CAP_SYS_ADMIN);
}

+static inline bool mac_admin_ns_capable(struct user_namespace *ns)
+{
+ return ns_capable(ns, CAP_MAC_ADMIN) ||
+ ns_capable(ns, CAP_SYS_ADMIN);
+}
+
/* audit system wants to get cap info from files as well */
int get_vfs_caps_from_disk(struct user_namespace *mnt_userns,
const struct dentry *dentry,
diff --git a/include/linux/ima.h b/include/linux/ima.h
index 08d507084b72..51f996f3c4c7 100644
--- a/include/linux/ima.h
+++ b/include/linux/ima.h
@@ -11,6 +11,7 @@
#include <linux/fs.h>
#include <linux/security.h>
#include <linux/kexec.h>
+#include <linux/user_namespace.h>
#include <crypto/hash_info.h>
struct linux_binprm;

@@ -210,6 +211,43 @@ static inline int ima_inode_removexattr(struct dentry *dentry,
}
#endif /* CONFIG_IMA_APPRAISE */

+extern struct ima_namespace init_ima_ns;
+
+#ifdef CONFIG_IMA_NS
+
+void free_ima_ns(struct user_namespace *ns);
+int create_ima_ns(struct user_namespace *user_ns);
+
+static inline struct ima_namespace *get_ima_ns(struct user_namespace *user_ns)
+{
+ return smp_load_acquire(&user_ns->ima_ns);
+}
+
+static inline struct ima_namespace *get_current_ns(void)
+{
+ return get_ima_ns(current_user_ns());
+}
+
+#else
+
+static inline void free_ima_ns(struct user_namespace *user_ns)
+{
+}
+
+static inline int create_ima_ns(struct user_namespace *user_ns)
+{
+#ifdef CONFIG_IMA
+ user_ns->ima_ns = &init_ima_ns;
+#endif
+ return 0;
+}
+
+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)
extern bool ima_appraise_signature(enum kernel_read_file_id func);
#else
diff --git a/include/linux/user_namespace.h b/include/linux/user_namespace.h
index 33a4240e6a6f..5249db04d62b 100644
--- a/include/linux/user_namespace.h
+++ b/include/linux/user_namespace.h
@@ -36,6 +36,7 @@ struct uid_gid_map { /* 64 bytes -- 1 cache line */
#define USERNS_INIT_FLAGS USERNS_SETGROUPS_ALLOWED

struct ucounts;
+struct ima_namespace;

enum ucount_type {
UCOUNT_USER_NAMESPACES,
@@ -99,6 +100,9 @@ struct user_namespace {
#endif
struct ucounts *ucounts;
long ucount_max[UCOUNT_COUNTS];
+#ifdef CONFIG_IMA
+ struct ima_namespace *ima_ns;
+#endif
} __randomize_layout;

struct ucounts {
diff --git a/init/Kconfig b/init/Kconfig
index 11f8a845f259..27890607e8cb 100644
--- a/init/Kconfig
+++ b/init/Kconfig
@@ -1242,6 +1242,16 @@ config NET_NS
Allow user space to create what appear to be multiple instances
of the network stack.

+config IMA_NS
+ bool "IMA namespace"
+ depends on USER_NS
+ depends on IMA
+ default y
+ help
+ Allow the creation of IMA namespaces for each user namespace.
+ Namespaced IMA enables having IMA features work separately
+ in each IMA namespace.
+
endif # NAMESPACES

config CHECKPOINT_RESTORE
diff --git a/kernel/user.c b/kernel/user.c
index e2cf8c22b539..5aac06e29298 100644
--- a/kernel/user.c
+++ b/kernel/user.c
@@ -19,6 +19,11 @@
#include <linux/export.h>
#include <linux/user_namespace.h>
#include <linux/proc_ns.h>
+#include <linux/ima.h>
+
+#ifdef CONFIG_IMA
+extern struct ima_namespace init_ima_ns;
+#endif

/*
* userns count is 1 for root user, 1 for init_uts_ns,
@@ -67,6 +72,9 @@ struct user_namespace init_user_ns = {
.keyring_name_list = LIST_HEAD_INIT(init_user_ns.keyring_name_list),
.keyring_sem = __RWSEM_INITIALIZER(init_user_ns.keyring_sem),
#endif
+#ifdef CONFIG_IMA
+ .ima_ns = &init_ima_ns,
+#endif
};
EXPORT_SYMBOL_GPL(init_user_ns);

diff --git a/kernel/user_namespace.c b/kernel/user_namespace.c
index 6b2e3ca7ee99..653f8fa83b69 100644
--- a/kernel/user_namespace.c
+++ b/kernel/user_namespace.c
@@ -20,6 +20,7 @@
#include <linux/fs_struct.h>
#include <linux/bsearch.h>
#include <linux/sort.h>
+#include <linux/ima.h>

static struct kmem_cache *user_ns_cachep __read_mostly;
static DEFINE_MUTEX(userns_state_mutex);
@@ -196,6 +197,7 @@ static void free_user_ns(struct work_struct *work)
kfree(ns->projid_map.forward);
kfree(ns->projid_map.reverse);
}
+ free_ima_ns(ns);
retire_userns_sysctls(ns);
key_free_user_ns(ns);
ns_free_inum(&ns->ns);
diff --git a/security/integrity/ima/Makefile b/security/integrity/ima/Makefile
index f8a5e5f3975d..b86a35fbed60 100644
--- a/security/integrity/ima/Makefile
+++ b/security/integrity/ima/Makefile
@@ -14,6 +14,7 @@ ima-$(CONFIG_HAVE_IMA_KEXEC) += ima_kexec.o
ima-$(CONFIG_IMA_BLACKLIST_KEYRING) += ima_mok.o
ima-$(CONFIG_IMA_MEASURE_ASYMMETRIC_KEYS) += ima_asymmetric_keys.o
ima-$(CONFIG_IMA_QUEUE_EARLY_BOOT_KEYS) += ima_queue_keys.o
+ima-$(CONFIG_IMA_NS) += ima_ns.o

ifeq ($(CONFIG_EFI),y)
ima-$(CONFIG_IMA_SECURE_AND_OR_TRUSTED_BOOT) += ima_efi.o
diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h
index 7ae197185db6..9ea10f4f65fb 100644
--- a/security/integrity/ima/ima.h
+++ b/security/integrity/ima/ima.h
@@ -20,6 +20,7 @@
#include <linux/hash.h>
#include <linux/tpm.h>
#include <linux/audit.h>
+#include <linux/user_namespace.h>
#include <crypto/hash_info.h>

#include "../integrity.h"
@@ -161,6 +162,7 @@ extern bool ima_canonical_fmt;
/* Internal IMA function definitions */
int ima_init(void);
int ima_fs_init(void);
+int ima_ns_init(void);
int ima_add_template_entry(struct ima_namespace *ns,
struct ima_template_entry *entry, int violation,
const char *op, struct inode *inode,
@@ -449,6 +451,10 @@ static inline void ima_free_modsig(struct modsig *modsig)
}
#endif /* CONFIG_IMA_APPRAISE_MODSIG */

+int ima_ns_init(void);
+struct ima_namespace;
+int ima_init_namespace(struct ima_namespace *ns);
+
/* LSM based policy rules require audit */
#ifdef CONFIG_IMA_LSM_RULES

@@ -486,4 +492,15 @@ static inline struct ima_namespace *get_current_ns(void)
return &init_ima_ns;
}

+static inline
+struct user_namespace *ima_user_ns_from_file(const struct file *filp)
+{
+ return file_inode(filp)->i_sb->s_user_ns;
+}
+
+static inline struct ima_namespace *ima_ns_from_file(const struct file *filp)
+{
+ return ima_user_ns_from_file(filp)->ima_ns;
+}
+
#endif /* __LINUX_IMA_H */
diff --git a/security/integrity/ima/ima_appraise.c b/security/integrity/ima/ima_appraise.c
index 3461025f671b..b6cc9244855a 100644
--- a/security/integrity/ima/ima_appraise.c
+++ b/security/integrity/ima/ima_appraise.c
@@ -530,7 +530,7 @@ void ima_inode_post_setattr(struct user_namespace *mnt_userns,
struct dentry *dentry)
{
struct inode *inode = d_backing_inode(dentry);
- struct ima_namespace *ns = &init_ima_ns;
+ struct ima_namespace *ns = get_current_ns();
struct integrity_iint_cache *iint;
int action;

@@ -647,7 +647,7 @@ int ima_inode_setxattr(struct dentry *dentry, const char *xattr_name,
const void *xattr_value, size_t xattr_value_len)
{
const struct evm_ima_xattr_data *xvalue = xattr_value;
- struct ima_namespace *ns = &init_ima_ns;
+ struct ima_namespace *ns = get_current_ns();
int digsig = 0;
int result;

@@ -672,7 +672,7 @@ int ima_inode_setxattr(struct dentry *dentry, const char *xattr_name,

int ima_inode_removexattr(struct dentry *dentry, const char *xattr_name)
{
- struct ima_namespace *ns = &init_ima_ns;
+ struct ima_namespace *ns = get_current_ns();
int result;

result = ima_protect_xattr(dentry, xattr_name, NULL, 0);
diff --git a/security/integrity/ima/ima_asymmetric_keys.c b/security/integrity/ima/ima_asymmetric_keys.c
index 70d87df26068..bc95f9ae4214 100644
--- a/security/integrity/ima/ima_asymmetric_keys.c
+++ b/security/integrity/ima/ima_asymmetric_keys.c
@@ -30,7 +30,7 @@ void ima_post_key_create_or_update(struct key *keyring, struct key *key,
const void *payload, size_t payload_len,
unsigned long flags, bool create)
{
- struct ima_namespace *ns = &init_ima_ns;
+ struct ima_namespace *ns = get_current_ns();
bool queued = false;

/* Only asymmetric keys are handled by this hook. */
diff --git a/security/integrity/ima/ima_fs.c b/security/integrity/ima/ima_fs.c
index 7cf66cd26b81..6c21096a29d2 100644
--- a/security/integrity/ima/ima_fs.c
+++ b/security/integrity/ima/ima_fs.c
@@ -48,7 +48,7 @@ static ssize_t ima_show_htable_violations(struct file *filp,
char __user *buf,
size_t count, loff_t *ppos)
{
- struct ima_namespace *ns = &init_ima_ns;
+ struct ima_namespace *ns = ima_ns_from_file(filp);

return ima_show_htable_value(buf, count, ppos,
&ns->ima_htable.violations);
@@ -63,7 +63,7 @@ static ssize_t ima_show_measurements_count(struct file *filp,
char __user *buf,
size_t count, loff_t *ppos)
{
- struct ima_namespace *ns = &init_ima_ns;
+ struct ima_namespace *ns = ima_ns_from_file(filp);

return ima_show_htable_value(buf, count, ppos, &ns->ima_htable.len);
}
@@ -76,7 +76,7 @@ static const struct file_operations ima_measurements_count_ops = {
/* returns pointer to hlist_node */
static void *ima_measurements_start(struct seq_file *m, loff_t *pos)
{
- struct ima_namespace *ns = &init_ima_ns;
+ struct ima_namespace *ns = ima_ns_from_file(m->file);
loff_t l = *pos;
struct ima_queue_entry *qe;

@@ -94,7 +94,7 @@ static void *ima_measurements_start(struct seq_file *m, loff_t *pos)

static void *ima_measurements_next(struct seq_file *m, void *v, loff_t *pos)
{
- struct ima_namespace *ns = &init_ima_ns;
+ struct ima_namespace *ns = ima_ns_from_file(m->file);
struct ima_queue_entry *qe = v;

/* lock protects when reading beyond last element
@@ -316,7 +316,7 @@ static ssize_t ima_read_policy(struct ima_namespace *ns, char *path)
static ssize_t ima_write_policy(struct file *file, const char __user *buf,
size_t datalen, loff_t *ppos)
{
- struct ima_namespace *ns = &init_ima_ns;
+ struct ima_namespace *ns = ima_ns_from_file(file);
char *data;
ssize_t result;

@@ -377,7 +377,8 @@ static const struct seq_operations ima_policy_seqops = {
*/
static int ima_open_policy(struct inode *inode, struct file *filp)
{
- struct ima_namespace *ns = &init_ima_ns;
+ struct user_namespace *user_ns = ima_user_ns_from_file(filp);
+ struct ima_namespace *ns = user_ns->ima_ns; /* no need to use acquire semantics it's guaranteed to be initialized */

if (!(filp->f_flags & O_WRONLY)) {
#ifndef CONFIG_IMA_READ_POLICY
@@ -385,7 +386,7 @@ static int ima_open_policy(struct inode *inode, struct file *filp)
#else
if ((filp->f_flags & O_ACCMODE) != O_RDONLY)
return -EACCES;
- if (!capable(CAP_SYS_ADMIN))
+ if (!mac_admin_ns_capable(user_ns))
return -EPERM;
return seq_open(filp, &ima_policy_seqops);
#endif
@@ -404,7 +405,7 @@ static int ima_open_policy(struct inode *inode, struct file *filp)
*/
static int ima_release_policy(struct inode *inode, struct file *file)
{
- struct ima_namespace *ns = &init_ima_ns;
+ struct ima_namespace *ns = ima_ns_from_file(file);
const char *cause = ns->valid_policy ? "completed" : "failed";

if ((file->f_flags & O_ACCMODE) == O_RDONLY)
@@ -448,15 +449,40 @@ static const struct file_operations ima_measure_policy_ops = {

static int __init ima_fs_ns_init(struct user_namespace *user_ns)
{
- struct ima_namespace *ns = user_ns->ima_ns;
- struct dentry *ima_dir;
+ int ret;
+ struct ima_namespace *ns = user_ns->ima_ns; /* no need to use acquire semantics it's guaranteed to be initialized */
+ struct dentry *int_dir;
+ struct dentry *ima_dir = NULL;
struct dentry *ima_symlink = NULL;
struct dentry *binary_runtime_measurements = NULL;
struct dentry *ascii_runtime_measurements = NULL;
struct dentry *runtime_measurements_count = NULL;
struct dentry *violations = NULL;

- ima_dir = securityfs_create_dir("ima", integrity_dir);
+ /*
+ * While multiple superblocks can exist they are keyed by userns in
+ * s_fs_info for securityfs. The first time a userns mounts a
+ * securityfs instance we lazily allocate the ima_namespace for the
+ * userns since that's the only way a userns can meaningfully use ima.
+ * The vfs ensure we're the only one to call fill_super() and hence
+ * ima_fs_ns_init() so we don't need any memory barriers here, i.e.
+ * user_ns->ima_ns can't change while we're in here.
+ */
+ if (!ns) {
+ ret = create_ima_ns(user_ns);
+ if (ret)
+ return ret;
+ }
+
+ /* FIXME: update when evm and integrity are namespaced */
+ if (user_ns != &init_user_ns) {
+ int_dir = securityfs_create_dir("integrity", root);
+ if (IS_ERR(int_dir))
+ return -1;
+ } else
+ int_dir = integrity_dir;
+
+ ima_dir = securityfs_create_dir("ima", int_dir);
if (IS_ERR(ima_dir))
return -1;

diff --git a/security/integrity/ima/ima_init.c b/security/integrity/ima/ima_init.c
index 548b73e1921a..22ca5d872be0 100644
--- a/security/integrity/ima/ima_init.c
+++ b/security/integrity/ima/ima_init.c
@@ -120,6 +120,10 @@ int __init ima_init(void)
{
int rc;

+ rc = ima_ns_init();
+ if (rc)
+ return rc;
+
ima_tpm_chip = tpm_default_chip();
if (!ima_tpm_chip)
pr_info("No TPM chip found, activating TPM-bypass!\n");
diff --git a/security/integrity/ima/ima_init_ima_ns.c b/security/integrity/ima/ima_init_ima_ns.c
index dec2bc9217da..d282a1f9ee36 100644
--- a/security/integrity/ima/ima_init_ima_ns.c
+++ b/security/integrity/ima/ima_init_ima_ns.c
@@ -7,6 +7,7 @@
*/

#include <linux/export.h>
+#include <linux/user_namespace.h>
#include <linux/proc_ns.h>
#include <linux/ima.h>
#include <linux/slab.h>
@@ -15,6 +16,11 @@

int ima_init_namespace(struct ima_namespace *ns)
{
+ ns->ns_status_tree = RB_ROOT;
+ rwlock_init(&ns->ns_status_lock);
+ /* Use KMEM_CACHE for simplicity ? */
+ ns->ns_status_cache = KMEM_CACHE(ns_status, SLAB_PANIC);
+
INIT_LIST_HEAD(&ns->ima_default_rules);
INIT_LIST_HEAD(&ns->ima_policy_rules);
INIT_LIST_HEAD(&ns->ima_temp_rules);
diff --git a/security/integrity/ima/ima_kexec.c b/security/integrity/ima/ima_kexec.c
index c07149228013..4f0d74d1a91a 100644
--- a/security/integrity/ima/ima_kexec.c
+++ b/security/integrity/ima/ima_kexec.c
@@ -85,7 +85,7 @@ void ima_add_kexec_buffer(struct kimage *image)
struct kexec_buf kbuf = { .image = image, .buf_align = PAGE_SIZE,
.buf_min = 0, .buf_max = ULONG_MAX,
.top_down = true };
- struct ima_namespace *ns = &init_ima_ns;
+ struct ima_namespace *ns = get_current_ns();
unsigned long binary_runtime_size;

/* use more understandable variable names than defined in kbuf */
@@ -148,7 +148,7 @@ void ima_load_kexec_buffer(void)
rc = ima_get_kexec_buffer(&kexec_buffer, &kexec_buffer_size);
switch (rc) {
case 0:
- rc = ima_restore_measurement_list(&init_ima_ns,
+ rc = ima_restore_measurement_list(get_current_ns(),
kexec_buffer_size,
kexec_buffer);
if (rc != 0)
diff --git a/security/integrity/ima/ima_main.c b/security/integrity/ima/ima_main.c
index b34ab2a9eef9..3c6db254064b 100644
--- a/security/integrity/ima/ima_main.c
+++ b/security/integrity/ima/ima_main.c
@@ -186,7 +186,7 @@ static void ima_check_last_writer(struct integrity_iint_cache *iint,
*/
void ima_file_free(struct file *file)
{
- struct ima_namespace *ns = &init_ima_ns;
+ struct ima_namespace *ns = ima_ns_from_file(file);
struct inode *inode = file_inode(file);
struct integrity_iint_cache *iint;

@@ -200,10 +200,10 @@ void ima_file_free(struct file *file)
ima_check_last_writer(iint, inode, file);
}

-static int process_measurement(struct ima_namespace *ns,
- struct file *file, const struct cred *cred,
- u32 secid, char *buf, loff_t size, int mask,
- enum ima_hooks func)
+static int __process_measurement(struct ima_namespace *ns,
+ struct file *file, const struct cred *cred,
+ u32 secid, char *buf, loff_t size, int mask,
+ enum ima_hooks func)
{
struct inode *inode = file_inode(file);
struct integrity_iint_cache *iint = NULL;
@@ -395,6 +395,38 @@ static int process_measurement(struct ima_namespace *ns,
return 0;
}

+static int process_measurement(struct ima_namespace *ns,
+ struct file *file, const struct cred *cred,
+ u32 secid, char *buf, loff_t size, int mask,
+ enum ima_hooks func)
+{
+ struct user_namespace *user_ns = current_user_ns();
+ int ret = 0;
+
+ /*
+ * We currently assume that this is always called from current's
+ * context, i.e. the passed ns must be the same as
+ * current_user_ns()->im_ns. Notice when that changes.
+ */
+ if (WARN_ON(get_ima_ns(user_ns) != ns))
+ return -EINVAL;
+
+ while (user_ns) {
+ /* the container has not loaded a separate policy (yet) */
+ ns = get_ima_ns(user_ns);
+ if (ns) {
+ ret = __process_measurement(ns, file, cred, secid, buf,
+ size, mask, func);
+ if (ret)
+ break;
+ }
+
+ user_ns = user_ns->parent;
+ };
+
+ return ret;
+}
+
/**
* ima_file_mmap - based on policy, collect/store measurement.
* @file: pointer to the file to be measured (May be NULL)
@@ -408,7 +440,7 @@ static int process_measurement(struct ima_namespace *ns,
*/
int ima_file_mmap(struct file *file, unsigned long prot)
{
- struct ima_namespace *ns = &init_ima_ns;
+ struct ima_namespace *ns = get_current_ns();
u32 secid;

if (file && (prot & PROT_EXEC)) {
@@ -434,7 +466,7 @@ int ima_file_mmap(struct file *file, unsigned long prot)
*/
int ima_file_mprotect(struct vm_area_struct *vma, unsigned long prot)
{
- struct ima_namespace *ns = &init_ima_ns;
+ struct ima_namespace *ns = get_current_ns();
struct ima_template_desc *template = NULL;
struct file *file = vma->vm_file;
char filename[NAME_MAX];
@@ -489,7 +521,7 @@ int ima_file_mprotect(struct vm_area_struct *vma, unsigned long prot)
*/
int ima_bprm_check(struct linux_binprm *bprm)
{
- struct ima_namespace *ns = &init_ima_ns;
+ struct ima_namespace *ns = get_current_ns();
int ret;
u32 secid;

@@ -516,7 +548,7 @@ int ima_bprm_check(struct linux_binprm *bprm)
*/
int ima_file_check(struct file *file, int mask)
{
- struct ima_namespace *ns = &init_ima_ns;
+ struct ima_namespace *ns = get_current_ns();
u32 secid;

security_task_getsecid_subj(current, &secid);
@@ -582,7 +614,7 @@ static int __ima_inode_hash(struct ima_namespace *ns,
*/
int ima_file_hash(struct file *file, char *buf, size_t buf_size)
{
- struct ima_namespace *ns = &init_ima_ns;
+ struct ima_namespace *ns = get_current_ns();

if (!file)
return -EINVAL;
@@ -611,7 +643,7 @@ EXPORT_SYMBOL_GPL(ima_file_hash);
*/
int ima_inode_hash(struct inode *inode, char *buf, size_t buf_size)
{
- struct ima_namespace *ns = &init_ima_ns;
+ struct ima_namespace *ns = get_current_ns();

if (!inode)
return -EINVAL;
@@ -632,7 +664,7 @@ EXPORT_SYMBOL_GPL(ima_inode_hash);
void ima_post_create_tmpfile(struct user_namespace *mnt_userns,
struct inode *inode)
{
- struct ima_namespace *ns = &init_ima_ns;
+ struct ima_namespace *ns = get_current_ns();
struct integrity_iint_cache *iint;
int must_appraise;

@@ -665,7 +697,7 @@ void ima_post_create_tmpfile(struct user_namespace *mnt_userns,
void ima_post_path_mknod(struct user_namespace *mnt_userns,
struct dentry *dentry)
{
- struct ima_namespace *ns = &init_ima_ns;
+ struct ima_namespace *ns = get_current_ns();
struct integrity_iint_cache *iint;
struct inode *inode = dentry->d_inode;
int must_appraise;
@@ -702,7 +734,7 @@ void ima_post_path_mknod(struct user_namespace *mnt_userns,
int ima_read_file(struct file *file, enum kernel_read_file_id read_id,
bool contents)
{
- struct ima_namespace *ns = &init_ima_ns;
+ struct ima_namespace *ns = get_current_ns();
enum ima_hooks func;
u32 secid;

@@ -753,7 +785,7 @@ const int read_idmap[READING_MAX_ID] = {
int ima_post_read_file(struct file *file, void *buf, loff_t size,
enum kernel_read_file_id read_id)
{
- struct ima_namespace *ns = &init_ima_ns;
+ struct ima_namespace *ns = get_current_ns();
enum ima_hooks func;
u32 secid;

@@ -996,7 +1028,7 @@ int process_buffer_measurement(struct ima_namespace *ns,
*/
void ima_kexec_cmdline(int kernel_fd, const void *buf, int size)
{
- struct ima_namespace *ns = &init_ima_ns;
+ struct ima_namespace *ns = get_current_ns();
struct fd f;

if (!buf || !size)
@@ -1037,7 +1069,7 @@ int ima_measure_critical_data(const char *event_label,
const void *buf, size_t buf_len,
bool hash, u8 *digest, size_t digest_len)
{
- struct ima_namespace *ns = &init_ima_ns;
+ struct ima_namespace *ns = get_current_ns();

if (!event_name || !event_label || !buf || !buf_len)
return -ENOPARAM;
@@ -1075,7 +1107,7 @@ static int __init init_ima(void)
pr_warn("Couldn't register LSM notifier, error %d\n", error);

if (!error)
- ima_update_policy_flags(&init_ima_ns);
+ ima_update_policy_flags(get_current_ns());

return error;
}
diff --git a/security/integrity/ima/ima_ns.c b/security/integrity/ima/ima_ns.c
new file mode 100644
index 000000000000..eb1e1e4cfc78
--- /dev/null
+++ b/security/integrity/ima/ima_ns.c
@@ -0,0 +1,70 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2016-2021 IBM Corporation
+ * Author:
+ * Yuqiong Sun <suny@xxxxxxxxxx>
+ * Stefan Berger <stefanb@xxxxxxxxxxxxxxxxxx>
+ */
+
+#include <linux/kref.h>
+#include <linux/slab.h>
+#include <linux/ima.h>
+#include <linux/mount.h>
+#include <linux/proc_ns.h>
+#include <linux/lsm_hooks.h>
+
+#include "ima.h"
+
+static struct kmem_cache *imans_cachep;
+
+int create_ima_ns(struct user_namespace *user_ns)
+{
+ struct ima_namespace *ns;
+ int err;
+
+ ns = kmem_cache_zalloc(imans_cachep, GFP_KERNEL);
+ if (!ns)
+ return -ENOMEM;
+ pr_debug("NEW ima_ns: 0x%p\n", ns);
+
+ err = ima_init_namespace(ns);
+ if (err)
+ goto fail_free;
+
+ /* Pairs with smp_load_acquire() in ima_fs_ns_init(). */
+ smp_store_release(&user_ns->ima_ns, ns);
+
+ return 0;
+
+fail_free:
+ kmem_cache_free(imans_cachep, ns);
+
+ return err;
+}
+
+static void destroy_ima_ns(struct ima_namespace *ns)
+{
+ pr_debug("DESTROY ima_ns: 0x%p\n", ns);
+ kmem_cache_free(imans_cachep, ns);
+}
+
+void free_ima_ns(struct user_namespace *user_ns)
+{
+ /* No need to use acquire semantics as the userns can't be reached
+ * anymore from userspace so either ima_ns has been initialized or it
+ * never has.
+ */
+ struct ima_namespace *ns = user_ns->ima_ns;
+
+ if (WARN_ON(ns == &init_ima_ns))
+ return;
+
+ destroy_ima_ns(ns);
+}
+
+static int __init imans_cache_init(void)
+{
+ imans_cachep = KMEM_CACHE(ima_namespace, SLAB_PANIC);
+ return 0;
+}
+subsys_initcall(imans_cache_init)
diff --git a/security/integrity/ima/ima_policy.c b/security/integrity/ima/ima_policy.c
index 66d005be3577..74d8ba594356 100644
--- a/security/integrity/ima/ima_policy.c
+++ b/security/integrity/ima/ima_policy.c
@@ -468,7 +468,7 @@ static void ima_lsm_update_rules(struct ima_namespace *ns)
int ima_lsm_policy_change(struct notifier_block *nb, unsigned long event,
void *lsm_data)
{
- struct ima_namespace *ns = &init_ima_ns;
+ struct ima_namespace *ns = get_current_ns();

if (event != LSM_POLICY_CHANGE)
return NOTIFY_DONE;
@@ -1793,6 +1793,16 @@ static int ima_parse_rule(struct ima_namespace *ns,
result = -EINVAL;
break;
}
+
+ /* IMA namespace only accepts AUDIT rules */
+ if (ns != &init_ima_ns) {
+ switch (entry->action) {
+ case MEASURE:
+ case APPRAISE:
+ case HASH:
+ result = -EINVAL;
+ }
+ }
}
if (!result && !ima_validate_rule(entry))
result = -EINVAL;
@@ -1894,7 +1904,7 @@ static const char *const mask_tokens[] = {

void *ima_policy_start(struct seq_file *m, loff_t *pos)
{
- struct ima_namespace *ns = &init_ima_ns;
+ struct ima_namespace *ns = ima_ns_from_file(m->file);
loff_t l = *pos;
struct ima_rule_entry *entry;
struct list_head *ima_rules_tmp;
@@ -1913,7 +1923,7 @@ void *ima_policy_start(struct seq_file *m, loff_t *pos)

void *ima_policy_next(struct seq_file *m, void *v, loff_t *pos)
{
- struct ima_namespace *ns = &init_ima_ns;
+ struct ima_namespace *ns = ima_ns_from_file(m->file);
struct ima_rule_entry *entry = v;

rcu_read_lock();
@@ -2177,7 +2187,7 @@ int ima_policy_show(struct seq_file *m, void *v)
*/
bool ima_appraise_signature(enum kernel_read_file_id id)
{
- struct ima_namespace *ns = &init_ima_ns;
+ struct ima_namespace *ns = get_current_ns();
struct ima_rule_entry *entry;
bool found = false;
enum ima_hooks func;
--
2.30.2