The goal of this series of patches is to start with the namespacing of
IMA and support auditing within an IMA namespace (IMA-ns) as the first
step.
In this series the IMA namespace is piggy backing on the user namespace
and therefore an IMA namespace gets created when a user namespace is
created. The advantage of this is that the user namespace can provide
the keys infrastructure that IMA appraisal support will need later on.
We chose the goal of supporting auditing within an IMA namespace since it
requires the least changes to IMA. Following this series, auditing within
an IMA namespace can be activated by a user running the following lines
that rely on a statically linked busybox to be installed on the host for
execution within the minimal container environment:
mkdir -p rootfs/{bin,mnt,proc}
cp /sbin/busybox rootfs/bin
PATH=/bin unshare --user --map-root-user --mount-proc --pid --fork \
--root rootfs busybox sh -c \
"busybox mount -t securityfs /mnt /mnt; \
busybox echo 'audit func=BPRM_CHECK mask=MAY_EXEC' > /mnt/ima/policy; \
busybox cat /mnt/ima/policy"
Following the audit log on the host the last line cat'ing the IMA policy
inside the namespace would have been audited. Unfortunately the auditing
line is not distinguishable from one stemming from actions on the host.
The hope here is that Richard Brigg's container id support for auditing
would help resolve the problem.
The following lines added to a suitable IMA policy on the host would
cause the execution of the commands inside the container (by uid 1000)
to be measured and audited as well on the host, thus leading to two
auditing messages for the 'busybox cat' above and log entries in IMA's
system log.
echo -e "measure func=BPRM_CHECK mask=MAY_EXEC uid=1000\n" \
"audit func=BPRM_CHECK mask=MAY_EXEC uid=1000\n" \
> /sys/kernel/security/ima/policy
The goal of supporting measurement and auditing by the host, of actions
occurring within IMA namespaces, is that users, particularly root,
should not be able to evade the host's IMA policy just by spawning
new IMA namespaces, running programs there, and discarding the namespaces
again. This is achieved through 'hierarchical processing' of file
accesses that are evaluated against the policy of the namespace where
the action occurred and against all namespaces' and their policies leading
back to the root IMA namespace (init_ima_ns).
The patch series adds support for a virtualized SecurityFS with a few
new API calls that are used by IMA namespacing. Only the data relevant
to the IMA namespace are shown. The files and directories of other
security subsystems (TPM, evm, Tomoyo, safesetid) are not showing
up when secruityfs is mounted inside a user namespace.
Much of the code leading up to the virtualization of SecurityFS deals
with moving IMA's variables from various files into the IMA namespace
structure called 'ima_namespace'. When it comes to determining the
current IMA namespace I took the approach to get the current IMA
namespace (get_current_ns()) on the top level and pass the pointer all
the way down to those functions that now need access to the ima_namespace
to get to their variables. This later on comes in handy once hierarchical
processing is implemented in this series where we walk the list of
namespaces backwards and again need to pass the pointer into functions.
This patch also introduces usage of CAP_MAC_ADMIN to allow access to the
IMA policy via reduced capabilities. We would again later on use this
capability to allow users to set file extended attributes for IMA appraisal
support.
My tree with these patches is here:
git clone https://github.com/stefanberger/linux-ima-namespaces v5.15+imans.v4.posted
Regards,
Stefan
v4:
- For consistency moved 'ns = get_current_ns()' to top of functions
- Merge in James's latest SecurityFS patch
v3:
- Further modifications to virtualized SecurityFS following James's posted patch
- Dropping of early teardown for user_namespaces since not needed anymore
v2:
- Folllwed Christian's suggestion to virtualize securitytfs; no more securityfs_ns
- Followed James's advice for late 'population' of securityfs for IMA namespaces
- Squashed 2 patches dealing with capabilities
- Added missing 'depends on USER_NS' to Kconfig
- Added missing 'static' to several functions
Mehmet Kayaalp (2):
ima: Define ns_status for storing namespaced iint data
ima: Namespace audit status flags
Stefan Berger (14):
ima: Add IMA namespace support
ima: Move delayed work queue and variables into ima_namespace
ima: Move IMA's keys queue related variables into ima_namespace
ima: Move policy related variables into ima_namespace
ima: Move ima_htable into ima_namespace
ima: Move measurement list related variables into ima_namespace
ima: Only accept AUDIT rules for IMA non-init_ima_ns namespaces for
now
ima: Implement hierarchical processing of file accesses
securityfs: Only use simple_pin_fs/simple_release_fs for init_user_ns
securityfs: Extend securityfs with namespacing support
ima: Move some IMA policy and filesystem related variables into
ima_namespace
ima: Use mac_admin_ns_capable() to check corresponding capability
ima: Move dentries into ima_namespace
ima: Setup securityfs for IMA namespace
include/linux/capability.h | 6 +
include/linux/ima.h | 140 +++++++++++++++++
include/linux/user_namespace.h | 4 +
init/Kconfig | 13 ++
kernel/user.c | 9 +-
kernel/user_namespace.c | 16 ++
security/inode.c | 54 +++++--
security/integrity/ima/Makefile | 4 +-
security/integrity/ima/ima.h | 145 ++++++++++++------
security/integrity/ima/ima_api.c | 33 ++--
security/integrity/ima/ima_appraise.c | 26 ++--
security/integrity/ima/ima_asymmetric_keys.c | 8 +-
security/integrity/ima/ima_fs.c | 152 +++++++++++--------
security/integrity/ima/ima_init.c | 20 +--
security/integrity/ima/ima_init_ima_ns.c | 73 +++++++++
security/integrity/ima/ima_main.c | 144 ++++++++++++------
security/integrity/ima/ima_ns.c | 111 ++++++++++++++
security/integrity/ima/ima_ns_status.c | 132 ++++++++++++++++
security/integrity/ima/ima_policy.c | 146 ++++++++++--------
security/integrity/ima/ima_queue.c | 75 +++++----
security/integrity/ima/ima_queue_keys.c | 73 ++++-----
security/integrity/ima/ima_template.c | 4 +-
22 files changed, 1044 insertions(+), 344 deletions(-)
create mode 100644 security/integrity/ima/ima_init_ima_ns.c
create mode 100644 security/integrity/ima/ima_ns.c
create mode 100644 security/integrity/ima/ima_ns_status.c
--
2.31.1
From: Mehmet Kayaalp <[email protected]>
This patch adds an rbtree to the IMA namespace structure that stores a
namespaced version of iint->flags in ns_status struct. Similar to the
integrity_iint_cache, both the iint ns_struct are looked up using the
inode pointer value. The lookup, allocate, and insertion code is also
similar, except ns_struct is not free'd when the inode is free'd.
Instead, the lookup verifies the i_ino and i_generation fields are also a
match.
Signed-off-by: Mehmet Kayaalp <[email protected]>
Signed-off-by: Stefan Berger <[email protected]>
Changelog:
v2:
* fixed tree traversal in __ima_ns_status_find()
---
include/linux/ima.h | 3 +
security/integrity/ima/Makefile | 1 +
security/integrity/ima/ima.h | 24 +++++
security/integrity/ima/ima_init_ima_ns.c | 9 ++
security/integrity/ima/ima_ns.c | 1 +
security/integrity/ima/ima_ns_status.c | 132 +++++++++++++++++++++++
6 files changed, 170 insertions(+)
create mode 100644 security/integrity/ima/ima_ns_status.c
diff --git a/include/linux/ima.h b/include/linux/ima.h
index 86d126b9ff2f..cc0e8c509fa2 100644
--- a/include/linux/ima.h
+++ b/include/linux/ima.h
@@ -214,6 +214,9 @@ static inline int ima_inode_removexattr(struct dentry *dentry,
struct ima_namespace {
struct kref kref;
struct user_namespace *user_ns;
+ struct rb_root ns_status_tree;
+ rwlock_t ns_status_lock;
+ struct kmem_cache *ns_status_cache;
};
extern struct ima_namespace init_ima_ns;
diff --git a/security/integrity/ima/Makefile b/security/integrity/ima/Makefile
index b86a35fbed60..78c84214e109 100644
--- a/security/integrity/ima/Makefile
+++ b/security/integrity/ima/Makefile
@@ -10,6 +10,7 @@ ima-y := ima_fs.o ima_queue.o ima_init.o ima_main.o ima_crypto.o ima_api.o \
ima_policy.o ima_template.o ima_template_lib.o ima_init_ima_ns.o
ima-$(CONFIG_IMA_APPRAISE) += ima_appraise.o
ima-$(CONFIG_IMA_APPRAISE_MODSIG) += ima_modsig.o
+ima-$(CONFIG_IMA_NS) += ima_ns.o ima_ns_status.o
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
diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h
index 2f8adf383054..28896d256e36 100644
--- a/security/integrity/ima/ima.h
+++ b/security/integrity/ima/ima.h
@@ -133,6 +133,14 @@ static inline void ima_load_kexec_buffer(void) {}
*/
extern bool ima_canonical_fmt;
+struct ns_status {
+ struct rb_node rb_node;
+ struct inode *inode;
+ ino_t i_ino;
+ u32 i_generation;
+ unsigned long flags;
+};
+
/* Internal IMA function definitions */
int ima_init(void);
int ima_fs_init(void);
@@ -422,6 +430,22 @@ int ima_ns_init(void);
struct ima_namespace;
int ima_init_namespace(struct ima_namespace *ns);
+#ifdef CONFIG_IMA_NS
+struct ns_status *ima_get_ns_status(struct ima_namespace *ns,
+ struct inode *inode);
+
+void free_ns_status_cache(struct ima_namespace *ns);
+
+#else
+
+static inline struct ns_status *ima_get_ns_status(struct ima_namespace *ns,
+ struct inode *inode)
+{
+ return NULL;
+}
+
+#endif /* CONFIG_IMA_NS */
+
/* LSM based policy rules require audit */
#ifdef CONFIG_IMA_LSM_RULES
diff --git a/security/integrity/ima/ima_init_ima_ns.c b/security/integrity/ima/ima_init_ima_ns.c
index 12723d77fe17..1a44963e8ba9 100644
--- a/security/integrity/ima/ima_init_ima_ns.c
+++ b/security/integrity/ima/ima_init_ima_ns.c
@@ -14,9 +14,18 @@
#include <linux/user_namespace.h>
#include <linux/ima.h>
#include <linux/proc_ns.h>
+#include <linux/slab.h>
+
+#include "ima.h"
int ima_init_namespace(struct ima_namespace *ns)
{
+ ns->ns_status_tree = RB_ROOT;
+ rwlock_init(&ns->ns_status_lock);
+ ns->ns_status_cache = KMEM_CACHE(ns_status, SLAB_PANIC);
+ if (!ns->ns_status_cache)
+ return -ENOMEM;
+
return 0;
}
diff --git a/security/integrity/ima/ima_ns.c b/security/integrity/ima/ima_ns.c
index 9a782c08c34e..566e59524958 100644
--- a/security/integrity/ima/ima_ns.c
+++ b/security/integrity/ima/ima_ns.c
@@ -61,6 +61,7 @@ struct ima_namespace *copy_ima_ns(struct ima_namespace *old_ns,
static void destroy_ima_ns(struct ima_namespace *ns)
{
pr_debug("DESTROY ima_ns: 0x%p\n", ns);
+ free_ns_status_cache(ns);
kmem_cache_free(imans_cachep, ns);
}
diff --git a/security/integrity/ima/ima_ns_status.c b/security/integrity/ima/ima_ns_status.c
new file mode 100644
index 000000000000..807dfaecdb5e
--- /dev/null
+++ b/security/integrity/ima/ima_ns_status.c
@@ -0,0 +1,132 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2016-2021 IBM Corporation
+ * Author:
+ * Yuqiong Sun <[email protected]>
+ * Stefan Berger <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, version 2 of the License.
+ */
+
+#include <linux/user_namespace.h>
+#include <linux/proc_ns.h>
+#include <linux/ima.h>
+
+#include "ima.h"
+
+void free_ns_status_cache(struct ima_namespace *ns)
+{
+ struct ns_status *status, *next;
+
+ write_lock(&ns->ns_status_lock);
+ rbtree_postorder_for_each_entry_safe(status, next,
+ &ns->ns_status_tree, rb_node)
+ kmem_cache_free(ns->ns_status_cache, status);
+ ns->ns_status_tree = RB_ROOT;
+ write_unlock(&ns->ns_status_lock);
+ kmem_cache_destroy(ns->ns_status_cache);
+}
+
+/*
+ * __ima_ns_status_find - return the ns_status associated with an inode
+ */
+static struct ns_status *__ima_ns_status_find(struct ima_namespace *ns,
+ struct inode *inode)
+{
+ struct ns_status *status;
+ struct rb_node *n = ns->ns_status_tree.rb_node;
+
+ while (n) {
+ status = rb_entry(n, struct ns_status, rb_node);
+
+ if (inode < status->inode)
+ n = n->rb_left;
+ else if (inode > status->inode)
+ n = n->rb_right;
+ else
+ break;
+ }
+ if (!n)
+ return NULL;
+
+ return status;
+}
+
+/*
+ * ima_ns_status_find - return the ns_status associated with an inode
+ */
+static struct ns_status *ima_ns_status_find(struct ima_namespace *ns,
+ struct inode *inode)
+{
+ struct ns_status *status;
+
+ read_lock(&ns->ns_status_lock);
+ status = __ima_ns_status_find(ns, inode);
+ read_unlock(&ns->ns_status_lock);
+
+ return status;
+}
+
+static void insert_ns_status(struct ima_namespace *ns, struct inode *inode,
+ struct ns_status *status)
+{
+ struct rb_node **p;
+ struct rb_node *node, *parent = NULL;
+ struct ns_status *test_status;
+
+ p = &ns->ns_status_tree.rb_node;
+ while (*p) {
+ parent = *p;
+ test_status = rb_entry(parent, struct ns_status, rb_node);
+ if (inode < test_status->inode)
+ p = &(*p)->rb_left;
+ else
+ p = &(*p)->rb_right;
+ }
+ node = &status->rb_node;
+ rb_link_node(node, parent, p);
+ rb_insert_color(node, &ns->ns_status_tree);
+}
+
+struct ns_status *ima_get_ns_status(struct ima_namespace *ns,
+ struct inode *inode)
+{
+ struct ns_status *status;
+ int skip_insert = 0;
+
+ status = ima_ns_status_find(ns, inode);
+ if (status) {
+ /*
+ * Unlike integrity_iint_cache we are not free'ing the
+ * ns_status data when the inode is free'd. So, in addition to
+ * checking the inode pointer, we need to make sure the
+ * (i_generation, i_ino) pair matches as well.
+ */
+ if (inode->i_ino == status->i_ino &&
+ inode->i_generation == status->i_generation)
+ return status;
+
+ /* Same inode number is reused, overwrite the ns_status */
+ skip_insert = 1;
+ } else {
+ status = kmem_cache_alloc(ns->ns_status_cache, GFP_NOFS);
+ if (!status)
+ return ERR_PTR(-ENOMEM);
+ }
+
+ write_lock(&ns->ns_status_lock);
+
+ if (!skip_insert)
+ insert_ns_status(ns, inode, status);
+
+ status->inode = inode;
+ status->i_ino = inode->i_ino;
+ status->i_generation = inode->i_generation;
+ status->flags = 0UL;
+
+ write_unlock(&ns->ns_status_lock);
+
+ return status;
+}
--
2.31.1
From: Mehmet Kayaalp <[email protected]>
The iint cache stores whether the file is measured, appraised, audited
etc. This patch moves the IMA_AUDITED flag into the per-namespace
ns_status, enabling IMA audit mechanism to audit the same file each time
it is accessed in a new namespace.
The ns_status is not looked up if the CONFIG_IMA_NS is disabled or if
any of the IMA_NS_STATUS_ACTIONS (currently only IMA_AUDIT) is not
enabled.
Read and write operations on the iint flags is replaced with function
calls. For reading, iint_flags() returns the bitwise AND of iint->flags
and ns_status->flags. The ns_status flags are masked with
IMA_NS_STATUS_FLAGS (currently only IMA_AUDITED). Similarly
set_iint_flags() only writes the masked portion to the ns_status flags,
while the iint flags is set as before. The ns_status parameter added to
ima_audit_measurement() is used with the above functions to query and
set the ns_status flags.
Signed-off-by: Mehmet Kayaalp <[email protected]>
Changelog:
v2:
* fixed flag calculation in iint_flags()
---
init/Kconfig | 3 +++
security/integrity/ima/ima.h | 23 ++++++++++++++++++++++-
security/integrity/ima/ima_api.c | 8 +++++---
security/integrity/ima/ima_main.c | 24 +++++++++++++++++-------
security/integrity/ima/ima_ns.c | 20 ++++++++++++++++++++
5 files changed, 67 insertions(+), 11 deletions(-)
diff --git a/init/Kconfig b/init/Kconfig
index 27890607e8cb..1e1c49f1d129 100644
--- a/init/Kconfig
+++ b/init/Kconfig
@@ -1251,6 +1251,9 @@ config IMA_NS
Allow the creation of IMA namespaces for each user namespace.
Namespaced IMA enables having IMA features work separately
in each IMA namespace.
+ Currently, only the audit status flags are stored in the namespace,
+ which allows the same file to be audited each time it is accessed
+ in a new namespace.
endif # NAMESPACES
diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h
index 28896d256e36..dd06e16c4e1c 100644
--- a/security/integrity/ima/ima.h
+++ b/security/integrity/ima/ima.h
@@ -282,7 +282,8 @@ int process_buffer_measurement(struct user_namespace *mnt_userns,
int pcr, const char *func_data,
bool buf_hash, u8 *digest, size_t digest_len);
void ima_audit_measurement(struct integrity_iint_cache *iint,
- const unsigned char *filename);
+ const unsigned char *filename,
+ struct ns_status *status);
int ima_alloc_init_template(struct ima_event_data *event_data,
struct ima_template_entry **entry,
struct ima_template_desc *template_desc);
@@ -426,6 +427,9 @@ static inline void ima_free_modsig(struct modsig *modsig)
}
#endif /* CONFIG_IMA_APPRAISE_MODSIG */
+#define IMA_NS_STATUS_ACTIONS IMA_AUDIT
+#define IMA_NS_STATUS_FLAGS IMA_AUDITED
+
int ima_ns_init(void);
struct ima_namespace;
int ima_init_namespace(struct ima_namespace *ns);
@@ -436,6 +440,10 @@ struct ns_status *ima_get_ns_status(struct ima_namespace *ns,
void free_ns_status_cache(struct ima_namespace *ns);
+unsigned long iint_flags(struct integrity_iint_cache *iint,
+ struct ns_status *status);
+unsigned long set_iint_flags(struct integrity_iint_cache *iint,
+ struct ns_status *status, unsigned long flags);
#else
static inline struct ns_status *ima_get_ns_status(struct ima_namespace *ns,
@@ -444,6 +452,19 @@ static inline struct ns_status *ima_get_ns_status(struct ima_namespace *ns,
return NULL;
}
+static inline unsigned long iint_flags(struct integrity_iint_cache *iint,
+ struct ns_status *status)
+{
+ return iint->flags;
+}
+
+static inline unsigned long set_iint_flags(struct integrity_iint_cache *iint,
+ struct ns_status *status,
+ unsigned long flags)
+{
+ iint->flags = flags;
+ return flags;
+}
#endif /* CONFIG_IMA_NS */
/* LSM based policy rules require audit */
diff --git a/security/integrity/ima/ima_api.c b/security/integrity/ima/ima_api.c
index a64fb0130b01..8f7bab17b784 100644
--- a/security/integrity/ima/ima_api.c
+++ b/security/integrity/ima/ima_api.c
@@ -342,14 +342,16 @@ void ima_store_measurement(struct integrity_iint_cache *iint,
}
void ima_audit_measurement(struct integrity_iint_cache *iint,
- const unsigned char *filename)
+ const unsigned char *filename,
+ struct ns_status *status)
{
struct audit_buffer *ab;
char *hash;
const char *algo_name = hash_algo_name[iint->ima_hash->algo];
int i;
+ unsigned long flags = iint_flags(iint, status);
- if (iint->flags & IMA_AUDITED)
+ if (flags & IMA_AUDITED)
return;
hash = kzalloc((iint->ima_hash->length * 2) + 1, GFP_KERNEL);
@@ -372,7 +374,7 @@ void ima_audit_measurement(struct integrity_iint_cache *iint,
audit_log_task_info(ab);
audit_log_end(ab);
- iint->flags |= IMA_AUDITED;
+ set_iint_flags(iint, status, flags | IMA_AUDITED);
out:
kfree(hash);
return;
diff --git a/security/integrity/ima/ima_main.c b/security/integrity/ima/ima_main.c
index 465865412100..4df60dbb56f7 100644
--- a/security/integrity/ima/ima_main.c
+++ b/security/integrity/ima/ima_main.c
@@ -204,6 +204,7 @@ static int process_measurement(struct file *file, const struct cred *cred,
{
struct inode *inode = file_inode(file);
struct integrity_iint_cache *iint = NULL;
+ struct ns_status *status = NULL;
struct ima_template_desc *template_desc = NULL;
char *pathbuf = NULL;
char filename[NAME_MAX];
@@ -216,6 +217,7 @@ static int process_measurement(struct file *file, const struct cred *cred,
bool violation_check;
enum hash_algo hash_algo;
unsigned int allowed_algos = 0;
+ unsigned long flags;
if (!ima_policy_flag || !S_ISREG(inode->i_mode))
return 0;
@@ -244,6 +246,12 @@ static int process_measurement(struct file *file, const struct cred *cred,
iint = integrity_inode_get(inode);
if (!iint)
rc = -ENOMEM;
+
+ if (!rc && (action & IMA_NS_STATUS_ACTIONS)) {
+ status = ima_get_ns_status(get_current_ns(), inode);
+ if (IS_ERR(status))
+ rc = PTR_ERR(status);
+ }
}
if (!rc && violation_check)
@@ -259,11 +267,13 @@ static int process_measurement(struct file *file, const struct cred *cred,
mutex_lock(&iint->mutex);
+ flags = iint_flags(iint, status);
+
if (test_and_clear_bit(IMA_CHANGE_ATTR, &iint->atomic_flags))
/* reset appraisal flags if ima_inode_post_setattr was called */
- iint->flags &= ~(IMA_APPRAISE | IMA_APPRAISED |
- IMA_APPRAISE_SUBMASK | IMA_APPRAISED_SUBMASK |
- IMA_ACTION_FLAGS);
+ flags &= ~(IMA_APPRAISE | IMA_APPRAISED |
+ IMA_APPRAISE_SUBMASK | IMA_APPRAISED_SUBMASK |
+ IMA_ACTION_FLAGS);
/*
* Re-evaulate the file if either the xattr has changed or the
@@ -274,7 +284,7 @@ static int process_measurement(struct file *file, const struct cred *cred,
((inode->i_sb->s_iflags & SB_I_IMA_UNVERIFIABLE_SIGNATURE) &&
!(inode->i_sb->s_iflags & SB_I_UNTRUSTED_MOUNTER) &&
!(action & IMA_FAIL_UNVERIFIABLE_SIGS))) {
- iint->flags &= ~IMA_DONE_MASK;
+ flags &= ~IMA_DONE_MASK;
iint->measured_pcrs = 0;
}
@@ -282,9 +292,9 @@ static int process_measurement(struct file *file, const struct cred *cred,
* (IMA_MEASURE, IMA_MEASURED, IMA_XXXX_APPRAISE, IMA_XXXX_APPRAISED,
* IMA_AUDIT, IMA_AUDITED)
*/
- iint->flags |= action;
+ flags = set_iint_flags(iint, status, flags | action);
action &= IMA_DO_MASK;
- action &= ~((iint->flags & (IMA_DONE_MASK ^ IMA_MEASURED)) >> 1);
+ action &= ~((flags & (IMA_DONE_MASK ^ IMA_MEASURED)) >> 1);
/* If target pcr is already measured, unset IMA_MEASURE action */
if ((action & IMA_MEASURE) && (iint->measured_pcrs & (0x1 << pcr)))
@@ -359,7 +369,7 @@ static int process_measurement(struct file *file, const struct cred *cred,
&pathname, filename);
}
if (action & IMA_AUDIT)
- ima_audit_measurement(iint, pathname);
+ ima_audit_measurement(iint, pathname, status);
if ((file->f_flags & O_DIRECT) && (iint->flags & IMA_PERMIT_DIRECTIO))
rc = 0;
diff --git a/security/integrity/ima/ima_ns.c b/security/integrity/ima/ima_ns.c
index 566e59524958..1fe1d910996b 100644
--- a/security/integrity/ima/ima_ns.c
+++ b/security/integrity/ima/ima_ns.c
@@ -75,6 +75,26 @@ void free_ima_ns(struct kref *kref)
destroy_ima_ns(ns);
}
+unsigned long iint_flags(struct integrity_iint_cache *iint,
+ struct ns_status *status)
+{
+ if (!status)
+ return iint->flags;
+
+ return (iint->flags & ~IMA_NS_STATUS_FLAGS) |
+ (status->flags & IMA_NS_STATUS_FLAGS);
+}
+
+unsigned long set_iint_flags(struct integrity_iint_cache *iint,
+ struct ns_status *status, unsigned long flags)
+{
+ iint->flags = flags;
+ if (status)
+ status->flags = flags & IMA_NS_STATUS_FLAGS;
+
+ return flags;
+}
+
static int __init imans_cache_init(void)
{
imans_cachep = KMEM_CACHE(ima_namespace, SLAB_PANIC);
--
2.31.1
Move the delayed work queue and associated variables to the
ima_namespace and initialize them.
Since keys queued up for measurement currently are only relevant in the
init_ima_ns, call ima_init_key_queue() only when the init_ima_ns is
initialized.
Signed-off-by: Stefan Berger <[email protected]>
---
include/linux/ima.h | 11 ++++++++
security/integrity/ima/ima.h | 12 +++++----
security/integrity/ima/ima_fs.c | 4 ++-
security/integrity/ima/ima_init.c | 2 --
security/integrity/ima/ima_init_ima_ns.c | 8 ++++++
security/integrity/ima/ima_policy.c | 4 +--
security/integrity/ima/ima_queue_keys.c | 34 ++++++++++--------------
7 files changed, 45 insertions(+), 30 deletions(-)
diff --git a/include/linux/ima.h b/include/linux/ima.h
index cc0e8c509fa2..4b5dada581e4 100644
--- a/include/linux/ima.h
+++ b/include/linux/ima.h
@@ -217,6 +217,17 @@ struct ima_namespace {
struct rb_root ns_status_tree;
rwlock_t ns_status_lock;
struct kmem_cache *ns_status_cache;
+
+#ifdef CONFIG_IMA_QUEUE_EARLY_BOOT_KEYS
+ /*
+ * If custom IMA policy is not loaded then keys queued up
+ * for measurement should be freed. This worker is used
+ * for handling this scenario.
+ */
+ struct delayed_work ima_keys_delayed_work;
+ long ima_key_queue_timeout;
+ bool timer_expired;
+#endif
};
extern struct ima_namespace init_ima_ns;
diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h
index dd06e16c4e1c..9edab9050dc7 100644
--- a/security/integrity/ima/ima.h
+++ b/security/integrity/ima/ima.h
@@ -77,6 +77,8 @@ struct ima_field_data {
u32 len;
};
+struct ima_namespace;
+
/* IMA template field definition */
struct ima_template_field {
const char field_id[IMA_TEMPLATE_FIELD_ID_MAX_LEN];
@@ -247,18 +249,18 @@ struct ima_key_entry {
size_t payload_len;
char *keyring_name;
};
-void ima_init_key_queue(void);
+void ima_init_key_queue(struct ima_namespace *ns);
bool ima_should_queue_key(void);
bool ima_queue_key(struct key *keyring, const void *payload,
size_t payload_len);
-void ima_process_queued_keys(void);
+void ima_process_queued_keys(struct ima_namespace *ns);
+void ima_keys_handler(struct work_struct *work);
#else
-static inline void ima_init_key_queue(void) {}
static inline bool ima_should_queue_key(void) { return false; }
static inline bool ima_queue_key(struct key *keyring,
const void *payload,
size_t payload_len) { return false; }
-static inline void ima_process_queued_keys(void) {}
+static inline void ima_process_queued_keys(struct ima_namespace *ns) {}
#endif /* CONFIG_IMA_QUEUE_EARLY_BOOT_KEYS */
/* LIM API function definitions */
@@ -300,7 +302,7 @@ int ima_match_policy(struct user_namespace *mnt_userns, struct inode *inode,
struct ima_template_desc **template_desc,
const char *func_data, unsigned int *allowed_algos);
void ima_init_policy(void);
-void ima_update_policy(void);
+void ima_update_policy(struct ima_namespace *ns);
void ima_update_policy_flags(void);
ssize_t ima_parse_add_rule(char *);
void ima_delete_rules(void);
diff --git a/security/integrity/ima/ima_fs.c b/security/integrity/ima/ima_fs.c
index 3d8e9d5db5aa..5cff3d6c3dc7 100644
--- a/security/integrity/ima/ima_fs.c
+++ b/security/integrity/ima/ima_fs.c
@@ -21,6 +21,7 @@
#include <linux/rcupdate.h>
#include <linux/parser.h>
#include <linux/vmalloc.h>
+#include <linux/ima.h>
#include "ima.h"
@@ -410,6 +411,7 @@ static int ima_open_policy(struct inode *inode, struct file *filp)
static int ima_release_policy(struct inode *inode, struct file *file)
{
const char *cause = valid_policy ? "completed" : "failed";
+ struct ima_namespace *ns = get_current_ns();
if ((file->f_flags & O_ACCMODE) == O_RDONLY)
return seq_release(inode, file);
@@ -430,7 +432,7 @@ static int ima_release_policy(struct inode *inode, struct file *file)
return 0;
}
- ima_update_policy();
+ ima_update_policy(ns);
#if !defined(CONFIG_IMA_WRITE_POLICY) && !defined(CONFIG_IMA_READ_POLICY)
securityfs_remove(ima_policy);
ima_policy = NULL;
diff --git a/security/integrity/ima/ima_init.c b/security/integrity/ima/ima_init.c
index f6ae4557a0da..24848373a061 100644
--- a/security/integrity/ima/ima_init.c
+++ b/security/integrity/ima/ima_init.c
@@ -155,8 +155,6 @@ int __init ima_init(void)
if (rc != 0)
return rc;
- ima_init_key_queue();
-
ima_measure_critical_data("kernel_info", "kernel_version",
UTS_RELEASE, strlen(UTS_RELEASE), false,
NULL, 0);
diff --git a/security/integrity/ima/ima_init_ima_ns.c b/security/integrity/ima/ima_init_ima_ns.c
index 1a44963e8ba9..3bc2d3611739 100644
--- a/security/integrity/ima/ima_init_ima_ns.c
+++ b/security/integrity/ima/ima_init_ima_ns.c
@@ -26,6 +26,14 @@ int ima_init_namespace(struct ima_namespace *ns)
if (!ns->ns_status_cache)
return -ENOMEM;
+#ifdef CONFIG_IMA_QUEUE_EARLY_BOOT_KEYS
+ INIT_DELAYED_WORK(&ns->ima_keys_delayed_work, ima_keys_handler);
+ ns->ima_key_queue_timeout = 300000;
+ ns->timer_expired = false;
+ if (ns == &init_ima_ns)
+ ima_init_key_queue(ns);
+#endif
+
return 0;
}
diff --git a/security/integrity/ima/ima_policy.c b/security/integrity/ima/ima_policy.c
index 320ca80aacab..e5aef287d14d 100644
--- a/security/integrity/ima/ima_policy.c
+++ b/security/integrity/ima/ima_policy.c
@@ -986,7 +986,7 @@ int ima_check_policy(void)
* Policy rules are never deleted so ima_policy_flag gets zeroed only once when
* we switch from the default policy to user defined.
*/
-void ima_update_policy(void)
+void ima_update_policy(struct ima_namespace *ns)
{
struct list_head *policy = &ima_policy_rules;
@@ -1007,7 +1007,7 @@ void ima_update_policy(void)
ima_update_policy_flags();
/* Custom IMA policy has been loaded */
- ima_process_queued_keys();
+ ima_process_queued_keys(ns);
}
/* Keep the enumeration in sync with the policy_tokens! */
diff --git a/security/integrity/ima/ima_queue_keys.c b/security/integrity/ima/ima_queue_keys.c
index 93056c03bf5a..fcaa1645dba3 100644
--- a/security/integrity/ima/ima_queue_keys.c
+++ b/security/integrity/ima/ima_queue_keys.c
@@ -10,6 +10,7 @@
#include <linux/user_namespace.h>
#include <linux/workqueue.h>
+#include <linux/ima.h>
#include <keys/asymmetric-type.h>
#include "ima.h"
@@ -25,34 +26,27 @@ static bool ima_process_keys;
static DEFINE_MUTEX(ima_keys_lock);
static LIST_HEAD(ima_keys);
-/*
- * If custom IMA policy is not loaded then keys queued up
- * for measurement should be freed. This worker is used
- * for handling this scenario.
- */
-static long ima_key_queue_timeout = 300000; /* 5 Minutes */
-static void ima_keys_handler(struct work_struct *work);
-static DECLARE_DELAYED_WORK(ima_keys_delayed_work, ima_keys_handler);
-static bool timer_expired;
-
/*
* This worker function frees keys that may still be
* queued up in case custom IMA policy was not loaded.
*/
-static void ima_keys_handler(struct work_struct *work)
+void ima_keys_handler(struct work_struct *work)
{
- timer_expired = true;
- ima_process_queued_keys();
+ struct ima_namespace *ns;
+
+ ns = container_of(work, struct ima_namespace, ima_keys_delayed_work.work);
+ ns->timer_expired = true;
+ ima_process_queued_keys(ns);
}
/*
* This function sets up a worker to free queued keys in case
* custom IMA policy was never loaded.
*/
-void ima_init_key_queue(void)
+void ima_init_key_queue(struct ima_namespace *ns)
{
- schedule_delayed_work(&ima_keys_delayed_work,
- msecs_to_jiffies(ima_key_queue_timeout));
+ schedule_delayed_work(&ns->ima_keys_delayed_work,
+ msecs_to_jiffies(ns->ima_key_queue_timeout));
}
static void ima_free_key_entry(struct ima_key_entry *entry)
@@ -130,7 +124,7 @@ bool ima_queue_key(struct key *keyring, const void *payload,
* This function sets ima_process_keys to true and processes queued keys.
* From here on keys will be processed right away (not queued).
*/
-void ima_process_queued_keys(void)
+void ima_process_queued_keys(struct ima_namespace *ns)
{
struct ima_key_entry *entry, *tmp;
bool process = false;
@@ -154,11 +148,11 @@ void ima_process_queued_keys(void)
if (!process)
return;
- if (!timer_expired)
- cancel_delayed_work_sync(&ima_keys_delayed_work);
+ if (!ns->timer_expired)
+ cancel_delayed_work_sync(&ns->ima_keys_delayed_work);
list_for_each_entry_safe(entry, tmp, &ima_keys, list) {
- if (!timer_expired)
+ if (!ns->timer_expired)
process_buffer_measurement(&init_user_ns, NULL,
entry->payload,
entry->payload_len,
--
2.31.1
Move variables from keys queue into ima_namespace.
Some variables have to be initialized before ima_init() runs, so statically
initialize them for the init_ima_ns.
Since only init_ima_ns uses the queued keys there's no need to free the
list of queued keys when tearing down IMA namespaces.
Signed-off-by: Stefan Berger <[email protected]>
---
include/linux/ima.h | 11 ++++++
security/integrity/ima/ima.h | 9 ++---
security/integrity/ima/ima_asymmetric_keys.c | 5 +--
security/integrity/ima/ima_init_ima_ns.c | 5 +++
security/integrity/ima/ima_ns.c | 6 ++++
security/integrity/ima/ima_queue_keys.c | 37 +++++++-------------
6 files changed, 43 insertions(+), 30 deletions(-)
diff --git a/include/linux/ima.h b/include/linux/ima.h
index 4b5dada581e4..977df9155cde 100644
--- a/include/linux/ima.h
+++ b/include/linux/ima.h
@@ -219,6 +219,17 @@ struct ima_namespace {
struct kmem_cache *ns_status_cache;
#ifdef CONFIG_IMA_QUEUE_EARLY_BOOT_KEYS
+ /*
+ * Flag to indicate whether a key can be processed
+ * right away or should be queued for processing later.
+ */
+ bool ima_process_keys;
+
+ /*
+ * To synchronize access to the list of keys that need to be measured
+ */
+ struct mutex ima_keys_lock;
+ struct list_head ima_keys;
/*
* If custom IMA policy is not loaded then keys queued up
* for measurement should be freed. This worker is used
diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h
index 9edab9050dc7..97eb03376855 100644
--- a/security/integrity/ima/ima.h
+++ b/security/integrity/ima/ima.h
@@ -250,14 +250,15 @@ struct ima_key_entry {
char *keyring_name;
};
void ima_init_key_queue(struct ima_namespace *ns);
-bool ima_should_queue_key(void);
-bool ima_queue_key(struct key *keyring, const void *payload,
+bool ima_should_queue_key(struct ima_namespace *ns);
+bool ima_queue_key(struct ima_namespace *ns, struct key *keyring, const void *payload,
size_t payload_len);
void ima_process_queued_keys(struct ima_namespace *ns);
void ima_keys_handler(struct work_struct *work);
#else
-static inline bool ima_should_queue_key(void) { return false; }
-static inline bool ima_queue_key(struct key *keyring,
+static inline bool ima_should_queue_key(struct ima_namespace *ns) { return false; }
+static inline bool ima_queue_key(struct ima_namespace *ns,
+ struct key *keyring,
const void *payload,
size_t payload_len) { return false; }
static inline void ima_process_queued_keys(struct ima_namespace *ns) {}
diff --git a/security/integrity/ima/ima_asymmetric_keys.c b/security/integrity/ima/ima_asymmetric_keys.c
index f6aa0b47a772..b20e82eda8f4 100644
--- a/security/integrity/ima/ima_asymmetric_keys.c
+++ b/security/integrity/ima/ima_asymmetric_keys.c
@@ -30,6 +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 = get_current_ns();
bool queued = false;
/* Only asymmetric keys are handled by this hook. */
@@ -39,8 +40,8 @@ void ima_post_key_create_or_update(struct key *keyring, struct key *key,
if (!payload || (payload_len == 0))
return;
- if (ima_should_queue_key())
- queued = ima_queue_key(keyring, payload, payload_len);
+ if (ima_should_queue_key(ns))
+ queued = ima_queue_key(ns, keyring, payload, payload_len);
if (queued)
return;
diff --git a/security/integrity/ima/ima_init_ima_ns.c b/security/integrity/ima/ima_init_ima_ns.c
index 3bc2d3611739..7b66fe598789 100644
--- a/security/integrity/ima/ima_init_ima_ns.c
+++ b/security/integrity/ima/ima_init_ima_ns.c
@@ -45,5 +45,10 @@ int __init ima_ns_init(void)
struct ima_namespace init_ima_ns = {
.kref = KREF_INIT(1),
.user_ns = &init_user_ns,
+#ifdef CONFIG_IMA_QUEUE_EARLY_BOOT_KEYS
+ .ima_process_keys = false,
+ .ima_keys_lock = __MUTEX_INITIALIZER(init_ima_ns.ima_keys_lock),
+ .ima_keys = LIST_HEAD_INIT(init_ima_ns.ima_keys),
+#endif
};
EXPORT_SYMBOL(init_ima_ns);
diff --git a/security/integrity/ima/ima_ns.c b/security/integrity/ima/ima_ns.c
index 1fe1d910996b..11e0343f1f55 100644
--- a/security/integrity/ima/ima_ns.c
+++ b/security/integrity/ima/ima_ns.c
@@ -38,6 +38,12 @@ static struct ima_namespace *create_ima_ns(struct user_namespace *user_ns)
if (err)
goto fail_free;
+#ifdef CONFIG_IMA_QUEUE_EARLY_BOOT_KEYS
+ ns->ima_process_keys = false;
+ mutex_init(&ns->ima_keys_lock);
+ INIT_LIST_HEAD(&ns->ima_keys);
+#endif
+
return ns;
fail_free:
diff --git a/security/integrity/ima/ima_queue_keys.c b/security/integrity/ima/ima_queue_keys.c
index fcaa1645dba3..9e5e9a178253 100644
--- a/security/integrity/ima/ima_queue_keys.c
+++ b/security/integrity/ima/ima_queue_keys.c
@@ -14,17 +14,6 @@
#include <keys/asymmetric-type.h>
#include "ima.h"
-/*
- * Flag to indicate whether a key can be processed
- * right away or should be queued for processing later.
- */
-static bool ima_process_keys;
-
-/*
- * To synchronize access to the list of keys that need to be measured
- */
-static DEFINE_MUTEX(ima_keys_lock);
-static LIST_HEAD(ima_keys);
/*
* This worker function frees keys that may still be
@@ -95,7 +84,7 @@ static struct ima_key_entry *ima_alloc_key_entry(struct key *keyring,
return entry;
}
-bool ima_queue_key(struct key *keyring, const void *payload,
+bool ima_queue_key(struct ima_namespace *ns, struct key *keyring, const void *payload,
size_t payload_len)
{
bool queued = false;
@@ -105,12 +94,12 @@ bool ima_queue_key(struct key *keyring, const void *payload,
if (!entry)
return false;
- mutex_lock(&ima_keys_lock);
- if (!ima_process_keys) {
- list_add_tail(&entry->list, &ima_keys);
+ mutex_lock(&ns->ima_keys_lock);
+ if (!ns->ima_process_keys) {
+ list_add_tail(&entry->list, &ns->ima_keys);
queued = true;
}
- mutex_unlock(&ima_keys_lock);
+ mutex_unlock(&ns->ima_keys_lock);
if (!queued)
ima_free_key_entry(entry);
@@ -129,7 +118,7 @@ void ima_process_queued_keys(struct ima_namespace *ns)
struct ima_key_entry *entry, *tmp;
bool process = false;
- if (ima_process_keys)
+ if (ns->ima_process_keys)
return;
/*
@@ -138,12 +127,12 @@ void ima_process_queued_keys(struct ima_namespace *ns)
* First one setting the ima_process_keys flag to true will
* process the queued keys.
*/
- mutex_lock(&ima_keys_lock);
- if (!ima_process_keys) {
- ima_process_keys = true;
+ mutex_lock(&ns->ima_keys_lock);
+ if (!ns->ima_process_keys) {
+ ns->ima_process_keys = true;
process = true;
}
- mutex_unlock(&ima_keys_lock);
+ mutex_unlock(&ns->ima_keys_lock);
if (!process)
return;
@@ -151,7 +140,7 @@ void ima_process_queued_keys(struct ima_namespace *ns)
if (!ns->timer_expired)
cancel_delayed_work_sync(&ns->ima_keys_delayed_work);
- list_for_each_entry_safe(entry, tmp, &ima_keys, list) {
+ list_for_each_entry_safe(entry, tmp, &ns->ima_keys, list) {
if (!ns->timer_expired)
process_buffer_measurement(&init_user_ns, NULL,
entry->payload,
@@ -165,7 +154,7 @@ void ima_process_queued_keys(struct ima_namespace *ns)
}
}
-inline bool ima_should_queue_key(void)
+inline bool ima_should_queue_key(struct ima_namespace *ns)
{
- return !ima_process_keys;
+ return !ns->ima_process_keys;
}
--
2.31.1
Move variables related to the IMA policy into the ima_namespace. This way
the IMA policy of an IMA namespace can be set and displayed using a
front-end like SecurityFS.
Implement ima_free_policy_rules() that frees the policy rules on
ima_namespace deletion.
Signed-off-by: Stefan Berger <[email protected]>
---
include/linux/ima.h | 10 ++
security/integrity/ima/ima.h | 35 ++---
security/integrity/ima/ima_api.c | 8 +-
security/integrity/ima/ima_appraise.c | 26 ++--
security/integrity/ima/ima_asymmetric_keys.c | 3 +-
security/integrity/ima/ima_fs.c | 10 +-
security/integrity/ima/ima_init.c | 8 +-
security/integrity/ima/ima_init_ima_ns.c | 6 +
security/integrity/ima/ima_main.c | 84 +++++++-----
security/integrity/ima/ima_ns.c | 1 +
security/integrity/ima/ima_policy.c | 132 +++++++++++--------
security/integrity/ima/ima_queue_keys.c | 2 +-
12 files changed, 197 insertions(+), 128 deletions(-)
diff --git a/include/linux/ima.h b/include/linux/ima.h
index 977df9155cde..e13e63a539d8 100644
--- a/include/linux/ima.h
+++ b/include/linux/ima.h
@@ -239,9 +239,19 @@ struct ima_namespace {
long ima_key_queue_timeout;
bool timer_expired;
#endif
+
+ struct list_head ima_default_rules;
+ /* ns's policy rules */
+ struct list_head ima_policy_rules;
+ struct list_head ima_temp_rules;
+ /* Pointer to ns's current policy */
+ struct list_head __rcu *ima_rules;
+ /* current content of the policy */
+ int ima_policy_flag;
};
extern struct ima_namespace init_ima_ns;
+extern struct list_head ima_default_rules;
#ifdef CONFIG_IMA_NS
diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h
index 97eb03376855..e295141f2478 100644
--- a/security/integrity/ima/ima.h
+++ b/security/integrity/ima/ima.h
@@ -43,9 +43,6 @@ enum tpm_pcrs { TPM_PCR0 = 0, TPM_PCR8 = 8, TPM_PCR10 = 10 };
#define NR_BANKS(chip) ((chip != NULL) ? chip->nr_allocated_banks : 0)
-/* current content of the policy */
-extern int ima_policy_flag;
-
/* bitset of digests algorithms allowed in the setxattr hook */
extern atomic_t ima_setxattr_allowed_hash_algorithms;
@@ -265,7 +262,8 @@ static inline void ima_process_queued_keys(struct ima_namespace *ns) {}
#endif /* CONFIG_IMA_QUEUE_EARLY_BOOT_KEYS */
/* LIM API function definitions */
-int ima_get_action(struct user_namespace *mnt_userns, struct inode *inode,
+int ima_get_action(struct ima_namespace *ns,
+ struct user_namespace *mnt_userns, struct inode *inode,
const struct cred *cred, u32 secid, int mask,
enum ima_hooks func, int *pcr,
struct ima_template_desc **template_desc,
@@ -279,7 +277,8 @@ void ima_store_measurement(struct integrity_iint_cache *iint, struct file *file,
struct evm_ima_xattr_data *xattr_value,
int xattr_len, const struct modsig *modsig, int pcr,
struct ima_template_desc *template_desc);
-int process_buffer_measurement(struct user_namespace *mnt_userns,
+int process_buffer_measurement(struct ima_namespace *ns,
+ struct user_namespace *mnt_userns,
struct inode *inode, const void *buf, int size,
const char *eventname, enum ima_hooks func,
int pcr, const char *func_data,
@@ -297,17 +296,19 @@ void ima_free_template_entry(struct ima_template_entry *entry);
const char *ima_d_path(const struct path *path, char **pathbuf, char *filename);
/* IMA policy related functions */
-int ima_match_policy(struct user_namespace *mnt_userns, struct inode *inode,
+int ima_match_policy(struct ima_namespace *ns,
+ struct user_namespace *mnt_userns, struct inode *inode,
const struct cred *cred, u32 secid, enum ima_hooks func,
int mask, int flags, int *pcr,
struct ima_template_desc **template_desc,
const char *func_data, unsigned int *allowed_algos);
-void ima_init_policy(void);
+void ima_init_policy(struct ima_namespace *ns);
void ima_update_policy(struct ima_namespace *ns);
-void ima_update_policy_flags(void);
-ssize_t ima_parse_add_rule(char *);
-void ima_delete_rules(void);
-int ima_check_policy(void);
+void ima_update_policy_flags(struct ima_namespace *ns);
+ssize_t ima_parse_add_rule(struct ima_namespace *ns, char *rule);
+void ima_delete_rules(struct ima_namespace *ns);
+int ima_check_policy(struct ima_namespace *ns);
+void ima_free_policy_rules(struct ima_namespace *ns);
void *ima_policy_start(struct seq_file *m, loff_t *pos);
void *ima_policy_next(struct seq_file *m, void *v, loff_t *pos);
void ima_policy_stop(struct seq_file *m, void *v);
@@ -323,14 +324,16 @@ int ima_policy_show(struct seq_file *m, void *v);
#define IMA_APPRAISE_KEXEC 0x40
#ifdef CONFIG_IMA_APPRAISE
-int ima_check_blacklist(struct integrity_iint_cache *iint,
+int ima_check_blacklist(struct ima_namespace *ns,
+ struct integrity_iint_cache *iint,
const struct modsig *modsig, int pcr);
int ima_appraise_measurement(enum ima_hooks func,
struct integrity_iint_cache *iint,
struct file *file, const unsigned char *filename,
struct evm_ima_xattr_data *xattr_value,
int xattr_len, const struct modsig *modsig);
-int ima_must_appraise(struct user_namespace *mnt_userns, struct inode *inode,
+int ima_must_appraise(struct ima_namespace *ns,
+ struct user_namespace *mnt_userns, struct inode *inode,
int mask, enum ima_hooks func);
void ima_update_xattr(struct integrity_iint_cache *iint, struct file *file);
enum integrity_status ima_get_cache_status(struct integrity_iint_cache *iint,
@@ -341,7 +344,8 @@ int ima_read_xattr(struct dentry *dentry,
struct evm_ima_xattr_data **xattr_value);
#else
-static inline int ima_check_blacklist(struct integrity_iint_cache *iint,
+static inline int ima_check_blacklist(struct ima_namespace *ns,
+ struct integrity_iint_cache *iint,
const struct modsig *modsig, int pcr)
{
return 0;
@@ -358,7 +362,8 @@ static inline int ima_appraise_measurement(enum ima_hooks func,
return INTEGRITY_UNKNOWN;
}
-static inline int ima_must_appraise(struct user_namespace *mnt_userns,
+static inline int ima_must_appraise(struct ima_namespace *ns,
+ struct user_namespace *mnt_userns,
struct inode *inode, int mask,
enum ima_hooks func)
{
diff --git a/security/integrity/ima/ima_api.c b/security/integrity/ima/ima_api.c
index 8f7bab17b784..808aec56dbb6 100644
--- a/security/integrity/ima/ima_api.c
+++ b/security/integrity/ima/ima_api.c
@@ -14,6 +14,7 @@
#include <linux/xattr.h>
#include <linux/evm.h>
#include <linux/iversion.h>
+#include <linux/ima.h>
#include "ima.h"
@@ -185,7 +186,8 @@ void ima_add_violation(struct file *file, const unsigned char *filename,
* Returns IMA_MEASURE, IMA_APPRAISE mask.
*
*/
-int ima_get_action(struct user_namespace *mnt_userns, struct inode *inode,
+int ima_get_action(struct ima_namespace *ns,
+ struct user_namespace *mnt_userns, struct inode *inode,
const struct cred *cred, u32 secid, int mask,
enum ima_hooks func, int *pcr,
struct ima_template_desc **template_desc,
@@ -193,9 +195,9 @@ int ima_get_action(struct user_namespace *mnt_userns, struct inode *inode,
{
int flags = IMA_MEASURE | IMA_AUDIT | IMA_APPRAISE | IMA_HASH;
- flags &= ima_policy_flag;
+ flags &= ns->ima_policy_flag;
- return ima_match_policy(mnt_userns, inode, cred, secid, func, mask,
+ return ima_match_policy(ns, mnt_userns, inode, cred, secid, func, mask,
flags, pcr, template_desc, func_data,
allowed_algos);
}
diff --git a/security/integrity/ima/ima_appraise.c b/security/integrity/ima/ima_appraise.c
index dbba51583e7c..b0c1992d8c4b 100644
--- a/security/integrity/ima/ima_appraise.c
+++ b/security/integrity/ima/ima_appraise.c
@@ -68,7 +68,8 @@ bool is_ima_appraise_enabled(void)
*
* Return 1 to appraise or hash
*/
-int ima_must_appraise(struct user_namespace *mnt_userns, struct inode *inode,
+int ima_must_appraise(struct ima_namespace *ns,
+ struct user_namespace *mnt_userns, struct inode *inode,
int mask, enum ima_hooks func)
{
u32 secid;
@@ -77,7 +78,7 @@ int ima_must_appraise(struct user_namespace *mnt_userns, struct inode *inode,
return 0;
security_task_getsecid_subj(current, &secid);
- return ima_match_policy(mnt_userns, inode, current_cred(), secid,
+ return ima_match_policy(ns, mnt_userns, inode, current_cred(), secid,
func, mask, IMA_APPRAISE | IMA_HASH, NULL,
NULL, NULL, NULL);
}
@@ -341,7 +342,8 @@ static int modsig_verify(enum ima_hooks func, const struct modsig *modsig,
*
* Returns -EPERM if the hash is blacklisted.
*/
-int ima_check_blacklist(struct integrity_iint_cache *iint,
+int ima_check_blacklist(struct ima_namespace *ns,
+ struct integrity_iint_cache *iint,
const struct modsig *modsig, int pcr)
{
enum hash_algo hash_algo;
@@ -357,7 +359,7 @@ int ima_check_blacklist(struct integrity_iint_cache *iint,
rc = is_binary_blacklisted(digest, digestsize);
if ((rc == -EPERM) && (iint->flags & IMA_MEASURE))
- process_buffer_measurement(&init_user_ns, NULL, digest, digestsize,
+ process_buffer_measurement(ns, &init_user_ns, NULL, digest, digestsize,
"blacklisted-hash", NONE,
pcr, NULL, false, NULL, 0);
}
@@ -527,14 +529,15 @@ void ima_inode_post_setattr(struct user_namespace *mnt_userns,
struct dentry *dentry)
{
struct inode *inode = d_backing_inode(dentry);
+ struct ima_namespace *ns = get_current_ns();
struct integrity_iint_cache *iint;
int action;
- if (!(ima_policy_flag & IMA_APPRAISE) || !S_ISREG(inode->i_mode)
+ if (!(ns->ima_policy_flag & IMA_APPRAISE) || !S_ISREG(inode->i_mode)
|| !(inode->i_opflags & IOP_XATTR))
return;
- action = ima_must_appraise(mnt_userns, inode, MAY_ACCESS, POST_SETATTR);
+ action = ima_must_appraise(ns, mnt_userns, inode, MAY_ACCESS, POST_SETATTR);
iint = integrity_iint_find(inode);
if (iint) {
set_bit(IMA_CHANGE_ATTR, &iint->atomic_flags);
@@ -559,11 +562,12 @@ static int ima_protect_xattr(struct dentry *dentry, const char *xattr_name,
return 0;
}
-static void ima_reset_appraise_flags(struct inode *inode, int digsig)
+static void ima_reset_appraise_flags(struct ima_namespace *ns,
+ struct inode *inode, int digsig)
{
struct integrity_iint_cache *iint;
- if (!(ima_policy_flag & IMA_APPRAISE) || !S_ISREG(inode->i_mode))
+ if (!(ns->ima_policy_flag & IMA_APPRAISE) || !S_ISREG(inode->i_mode))
return;
iint = integrity_iint_find(inode);
@@ -641,6 +645,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 = get_current_ns();
int digsig = 0;
int result;
@@ -658,18 +663,19 @@ int ima_inode_setxattr(struct dentry *dentry, const char *xattr_name,
if (result)
return result;
- ima_reset_appraise_flags(d_backing_inode(dentry), digsig);
+ ima_reset_appraise_flags(ns, d_backing_inode(dentry), digsig);
}
return result;
}
int ima_inode_removexattr(struct dentry *dentry, const char *xattr_name)
{
+ struct ima_namespace *ns = get_current_ns();
int result;
result = ima_protect_xattr(dentry, xattr_name, NULL, 0);
if (result == 1 || evm_revalidate_status(xattr_name)) {
- ima_reset_appraise_flags(d_backing_inode(dentry), 0);
+ ima_reset_appraise_flags(ns, d_backing_inode(dentry), 0);
if (result == 1)
result = 0;
}
diff --git a/security/integrity/ima/ima_asymmetric_keys.c b/security/integrity/ima/ima_asymmetric_keys.c
index b20e82eda8f4..959e9f2f6c26 100644
--- a/security/integrity/ima/ima_asymmetric_keys.c
+++ b/security/integrity/ima/ima_asymmetric_keys.c
@@ -61,7 +61,8 @@ void ima_post_key_create_or_update(struct key *keyring, struct key *key,
* if the IMA policy is configured to measure a key linked
* to the given keyring.
*/
- process_buffer_measurement(&init_user_ns, NULL, payload, payload_len,
+ process_buffer_measurement(ns, &init_user_ns, NULL,
+ payload, payload_len,
keyring->description, KEY_CHECK, 0,
keyring->description, false, NULL, 0);
}
diff --git a/security/integrity/ima/ima_fs.c b/security/integrity/ima/ima_fs.c
index 5cff3d6c3dc7..25f85c58ebef 100644
--- a/security/integrity/ima/ima_fs.c
+++ b/security/integrity/ima/ima_fs.c
@@ -274,6 +274,7 @@ static const struct file_operations ima_ascii_measurements_ops = {
static ssize_t ima_read_policy(char *path)
{
+ struct ima_namespace *ns = get_current_ns();
void *data = NULL;
char *datap;
size_t size;
@@ -297,7 +298,7 @@ static ssize_t ima_read_policy(char *path)
datap = data;
while (size > 0 && (p = strsep(&datap, "\n"))) {
pr_debug("rule: %s\n", p);
- rc = ima_parse_add_rule(p);
+ rc = ima_parse_add_rule(ns, p);
if (rc < 0)
break;
size -= rc;
@@ -315,6 +316,7 @@ static ssize_t ima_read_policy(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 = get_current_ns();
char *data;
ssize_t result;
@@ -345,7 +347,7 @@ static ssize_t ima_write_policy(struct file *file, const char __user *buf,
1, 0);
result = -EACCES;
} else {
- result = ima_parse_add_rule(data);
+ result = ima_parse_add_rule(ns, data);
}
mutex_unlock(&ima_write_mutex);
out_free:
@@ -416,7 +418,7 @@ static int ima_release_policy(struct inode *inode, struct file *file)
if ((file->f_flags & O_ACCMODE) == O_RDONLY)
return seq_release(inode, file);
- if (valid_policy && ima_check_policy() < 0) {
+ if (valid_policy && ima_check_policy(ns) < 0) {
cause = "failed";
valid_policy = 0;
}
@@ -426,7 +428,7 @@ static int ima_release_policy(struct inode *inode, struct file *file)
"policy_update", cause, !valid_policy, 0);
if (!valid_policy) {
- ima_delete_rules();
+ ima_delete_rules(ns);
valid_policy = 1;
clear_bit(IMA_FS_BUSY, &ima_fs_flags);
return 0;
diff --git a/security/integrity/ima/ima_init.c b/security/integrity/ima/ima_init.c
index 24848373a061..d78f8faf6dcd 100644
--- a/security/integrity/ima/ima_init.c
+++ b/security/integrity/ima/ima_init.c
@@ -104,15 +104,15 @@ static int __init ima_add_boot_aggregate(void)
#ifdef CONFIG_IMA_LOAD_X509
void __init ima_load_x509(void)
{
- int unset_flags = ima_policy_flag & IMA_APPRAISE;
+ int unset_flags = init_ima_ns.ima_policy_flag & IMA_APPRAISE;
- ima_policy_flag &= ~unset_flags;
+ init_ima_ns.ima_policy_flag &= ~unset_flags;
integrity_load_x509(INTEGRITY_KEYRING_IMA, CONFIG_IMA_X509_PATH);
/* load also EVM key to avoid appraisal */
evm_load_x509();
- ima_policy_flag |= unset_flags;
+ init_ima_ns.ima_policy_flag |= unset_flags;
}
#endif
@@ -149,7 +149,7 @@ int __init ima_init(void)
if (rc != 0)
return rc;
- ima_init_policy();
+ ima_init_policy(&init_ima_ns);
rc = ima_fs_init();
if (rc != 0)
diff --git a/security/integrity/ima/ima_init_ima_ns.c b/security/integrity/ima/ima_init_ima_ns.c
index 7b66fe598789..2d644791a795 100644
--- a/security/integrity/ima/ima_init_ima_ns.c
+++ b/security/integrity/ima/ima_init_ima_ns.c
@@ -34,6 +34,12 @@ int ima_init_namespace(struct ima_namespace *ns)
ima_init_key_queue(ns);
#endif
+ INIT_LIST_HEAD(&ns->ima_default_rules);
+ INIT_LIST_HEAD(&ns->ima_policy_rules);
+ INIT_LIST_HEAD(&ns->ima_temp_rules);
+ ns->ima_rules = (struct list_head __rcu *)(&ns->ima_default_rules);
+ ns->ima_policy_flag = 0;
+
return 0;
}
diff --git a/security/integrity/ima/ima_main.c b/security/integrity/ima/ima_main.c
index 4df60dbb56f7..897304046ba4 100644
--- a/security/integrity/ima/ima_main.c
+++ b/security/integrity/ima/ima_main.c
@@ -185,10 +185,11 @@ static void ima_check_last_writer(struct integrity_iint_cache *iint,
*/
void ima_file_free(struct file *file)
{
+ struct ima_namespace *ns = get_current_ns();
struct inode *inode = file_inode(file);
struct integrity_iint_cache *iint;
- if (!ima_policy_flag || !S_ISREG(inode->i_mode))
+ if (!ns->ima_policy_flag || !S_ISREG(inode->i_mode))
return;
iint = integrity_iint_find(inode);
@@ -198,7 +199,8 @@ void ima_file_free(struct file *file)
ima_check_last_writer(iint, inode, file);
}
-static int process_measurement(struct file *file, const struct cred *cred,
+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)
{
@@ -219,18 +221,18 @@ static int process_measurement(struct file *file, const struct cred *cred,
unsigned int allowed_algos = 0;
unsigned long flags;
- if (!ima_policy_flag || !S_ISREG(inode->i_mode))
+ if (!ns->ima_policy_flag || !S_ISREG(inode->i_mode))
return 0;
/* Return an IMA_MEASURE, IMA_APPRAISE, IMA_AUDIT action
* bitmask based on the appraise/audit/measurement policy.
* Included is the appraise submask.
*/
- action = ima_get_action(file_mnt_user_ns(file), inode, cred, secid,
+ action = ima_get_action(ns, file_mnt_user_ns(file), inode, cred, secid,
mask, func, &pcr, &template_desc, NULL,
&allowed_algos);
violation_check = ((func == FILE_CHECK || func == MMAP_CHECK) &&
- (ima_policy_flag & IMA_MEASURE));
+ (ns->ima_policy_flag & IMA_MEASURE));
if (!action && !violation_check)
return 0;
@@ -248,7 +250,7 @@ static int process_measurement(struct file *file, const struct cred *cred,
rc = -ENOMEM;
if (!rc && (action & IMA_NS_STATUS_ACTIONS)) {
- status = ima_get_ns_status(get_current_ns(), inode);
+ status = ima_get_ns_status(ns, inode);
if (IS_ERR(status))
rc = PTR_ERR(status);
}
@@ -356,7 +358,7 @@ static int process_measurement(struct file *file, const struct cred *cred,
xattr_value, xattr_len, modsig, pcr,
template_desc);
if (rc == 0 && (action & IMA_APPRAISE_SUBMASK)) {
- rc = ima_check_blacklist(iint, modsig, pcr);
+ rc = ima_check_blacklist(ns, iint, modsig, pcr);
if (rc != -EPERM) {
inode_lock(inode);
rc = ima_appraise_measurement(func, iint, file,
@@ -415,12 +417,13 @@ static int process_measurement(struct file *file, const struct cred *cred,
*/
int ima_file_mmap(struct file *file, unsigned long prot)
{
+ struct ima_namespace *ns = get_current_ns();
u32 secid;
if (file && (prot & PROT_EXEC)) {
security_task_getsecid_subj(current, &secid);
- return process_measurement(file, current_cred(), secid, NULL,
- 0, MAY_EXEC, MMAP_CHECK);
+ return process_measurement(ns, file, current_cred(), secid,
+ NULL, 0, MAY_EXEC, MMAP_CHECK);
}
return 0;
@@ -440,6 +443,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 = get_current_ns();
struct ima_template_desc *template = NULL;
struct file *file = vma->vm_file;
char filename[NAME_MAX];
@@ -452,13 +456,13 @@ int ima_file_mprotect(struct vm_area_struct *vma, unsigned long prot)
int pcr;
/* Is mprotect making an mmap'ed file executable? */
- if (!(ima_policy_flag & IMA_APPRAISE) || !vma->vm_file ||
+ if (!(ns->ima_policy_flag & IMA_APPRAISE) || !vma->vm_file ||
!(prot & PROT_EXEC) || (vma->vm_flags & VM_EXEC))
return 0;
security_task_getsecid_subj(current, &secid);
inode = file_inode(vma->vm_file);
- action = ima_get_action(file_mnt_user_ns(vma->vm_file), inode,
+ action = ima_get_action(ns, file_mnt_user_ns(vma->vm_file), inode,
current_cred(), secid, MAY_EXEC, MMAP_CHECK,
&pcr, &template, NULL, NULL);
@@ -494,17 +498,18 @@ int ima_file_mprotect(struct vm_area_struct *vma, unsigned long prot)
*/
int ima_bprm_check(struct linux_binprm *bprm)
{
+ struct ima_namespace *ns = get_current_ns();
int ret;
u32 secid;
security_task_getsecid_subj(current, &secid);
- ret = process_measurement(bprm->file, current_cred(), secid, NULL, 0,
- MAY_EXEC, BPRM_CHECK);
+ ret = process_measurement(ns, bprm->file, current_cred(), secid, NULL,
+ 0, MAY_EXEC, BPRM_CHECK);
if (ret)
return ret;
security_cred_getsecid(bprm->cred, &secid);
- return process_measurement(bprm->file, bprm->cred, secid, NULL, 0,
+ return process_measurement(ns, bprm->file, bprm->cred, secid, NULL, 0,
MAY_EXEC, CREDS_CHECK);
}
@@ -520,21 +525,23 @@ int ima_bprm_check(struct linux_binprm *bprm)
*/
int ima_file_check(struct file *file, int mask)
{
+ struct ima_namespace *ns = get_current_ns();
u32 secid;
security_task_getsecid_subj(current, &secid);
- return process_measurement(file, current_cred(), secid, NULL, 0,
+ return process_measurement(ns, file, current_cred(), secid, NULL, 0,
mask & (MAY_READ | MAY_WRITE | MAY_EXEC |
MAY_APPEND), FILE_CHECK);
}
EXPORT_SYMBOL_GPL(ima_file_check);
-static int __ima_inode_hash(struct inode *inode, char *buf, size_t buf_size)
+static int __ima_inode_hash(struct ima_namespace *ns,
+ struct inode *inode, char *buf, size_t buf_size)
{
struct integrity_iint_cache *iint;
int hash_algo;
- if (!ima_policy_flag)
+ if (!ns->ima_policy_flag)
return -EOPNOTSUPP;
iint = integrity_iint_find(inode);
@@ -584,10 +591,12 @@ static int __ima_inode_hash(struct inode *inode, char *buf, size_t buf_size)
*/
int ima_file_hash(struct file *file, char *buf, size_t buf_size)
{
+ struct ima_namespace *ns = get_current_ns();
+
if (!file)
return -EINVAL;
- return __ima_inode_hash(file_inode(file), buf, buf_size);
+ return __ima_inode_hash(ns, file_inode(file), buf, buf_size);
}
EXPORT_SYMBOL_GPL(ima_file_hash);
@@ -611,10 +620,12 @@ EXPORT_SYMBOL_GPL(ima_file_hash);
*/
int ima_inode_hash(struct inode *inode, char *buf, size_t buf_size)
{
+ struct ima_namespace *ns = get_current_ns();
+
if (!inode)
return -EINVAL;
- return __ima_inode_hash(inode, buf, buf_size);
+ return __ima_inode_hash(ns, inode, buf, buf_size);
}
EXPORT_SYMBOL_GPL(ima_inode_hash);
@@ -630,13 +641,14 @@ EXPORT_SYMBOL_GPL(ima_inode_hash);
void ima_post_create_tmpfile(struct user_namespace *mnt_userns,
struct inode *inode)
{
+ struct ima_namespace *ns = get_current_ns();
struct integrity_iint_cache *iint;
int must_appraise;
- if (!ima_policy_flag || !S_ISREG(inode->i_mode))
+ if (!ns->ima_policy_flag || !S_ISREG(inode->i_mode))
return;
- must_appraise = ima_must_appraise(mnt_userns, inode, MAY_ACCESS,
+ must_appraise = ima_must_appraise(ns, mnt_userns, inode, MAY_ACCESS,
FILE_CHECK);
if (!must_appraise)
return;
@@ -662,14 +674,15 @@ 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 = get_current_ns();
struct integrity_iint_cache *iint;
struct inode *inode = dentry->d_inode;
int must_appraise;
- if (!ima_policy_flag || !S_ISREG(inode->i_mode))
+ if (!ns->ima_policy_flag || !S_ISREG(inode->i_mode))
return;
- must_appraise = ima_must_appraise(mnt_userns, inode, MAY_ACCESS,
+ must_appraise = ima_must_appraise(ns, mnt_userns, inode, MAY_ACCESS,
FILE_CHECK);
if (!must_appraise)
return;
@@ -698,6 +711,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 = get_current_ns();
enum ima_hooks func;
u32 secid;
@@ -720,7 +734,7 @@ int ima_read_file(struct file *file, enum kernel_read_file_id read_id,
/* Read entire file for all partial reads. */
func = read_idmap[read_id] ?: FILE_CHECK;
security_task_getsecid_subj(current, &secid);
- return process_measurement(file, current_cred(), secid, NULL,
+ return process_measurement(ns, file, current_cred(), secid, NULL,
0, MAY_READ, func);
}
@@ -748,6 +762,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 = get_current_ns();
enum ima_hooks func;
u32 secid;
@@ -763,7 +778,7 @@ int ima_post_read_file(struct file *file, void *buf, loff_t size,
func = read_idmap[read_id] ?: FILE_CHECK;
security_task_getsecid_subj(current, &secid);
- return process_measurement(file, current_cred(), secid, buf, size,
+ return process_measurement(ns, file, current_cred(), secid, buf, size,
MAY_READ, func);
}
@@ -869,7 +884,8 @@ int ima_post_load_data(char *buf, loff_t size,
* has been written to the passed location but not added to a measurement entry,
* a negative value otherwise.
*/
-int process_buffer_measurement(struct user_namespace *mnt_userns,
+int process_buffer_measurement(struct ima_namespace *ns,
+ struct user_namespace *mnt_userns,
struct inode *inode, const void *buf, int size,
const char *eventname, enum ima_hooks func,
int pcr, const char *func_data,
@@ -897,7 +913,7 @@ int process_buffer_measurement(struct user_namespace *mnt_userns,
if (digest && digest_len < digest_hash_len)
return -EINVAL;
- if (!ima_policy_flag && !digest)
+ if (!ns->ima_policy_flag && !digest)
return -ENOENT;
template = ima_template_desc_buf();
@@ -916,7 +932,7 @@ int process_buffer_measurement(struct user_namespace *mnt_userns,
*/
if (func) {
security_task_getsecid_subj(current, &secid);
- action = ima_get_action(mnt_userns, inode, current_cred(),
+ action = ima_get_action(ns, mnt_userns, inode, current_cred(),
secid, 0, func, &pcr, &template,
func_data, NULL);
if (!(action & IMA_MEASURE) && !digest)
@@ -953,7 +969,7 @@ int process_buffer_measurement(struct user_namespace *mnt_userns,
if (digest)
memcpy(digest, iint.ima_hash->digest, digest_hash_len);
- if (!ima_policy_flag || (func && !(action & IMA_MEASURE)))
+ if (!ns->ima_policy_flag || (func && !(action & IMA_MEASURE)))
return 1;
ret = ima_alloc_init_template(&event_data, &entry, template);
@@ -987,6 +1003,7 @@ int process_buffer_measurement(struct user_namespace *mnt_userns,
*/
void ima_kexec_cmdline(int kernel_fd, const void *buf, int size)
{
+ struct ima_namespace *ns = get_current_ns();
struct fd f;
if (!buf || !size)
@@ -996,7 +1013,8 @@ void ima_kexec_cmdline(int kernel_fd, const void *buf, int size)
if (!f.file)
return;
- process_buffer_measurement(file_mnt_user_ns(f.file), file_inode(f.file),
+ process_buffer_measurement(ns,
+ file_mnt_user_ns(f.file), file_inode(f.file),
buf, size, "kexec-cmdline", KEXEC_CMDLINE, 0,
NULL, false, NULL, 0);
fdput(f);
@@ -1026,10 +1044,12 @@ 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 = get_current_ns();
+
if (!event_name || !event_label || !buf || !buf_len)
return -ENOPARAM;
- return process_buffer_measurement(&init_user_ns, NULL, buf, buf_len,
+ return process_buffer_measurement(ns, &init_user_ns, NULL, buf, buf_len,
event_name, CRITICAL_DATA, 0,
event_label, hash, digest,
digest_len);
@@ -1062,7 +1082,7 @@ static int __init init_ima(void)
pr_warn("Couldn't register LSM notifier, error %d\n", error);
if (!error)
- ima_update_policy_flags();
+ ima_update_policy_flags(&init_ima_ns);
return error;
}
diff --git a/security/integrity/ima/ima_ns.c b/security/integrity/ima/ima_ns.c
index 11e0343f1f55..efbf7087a8ee 100644
--- a/security/integrity/ima/ima_ns.c
+++ b/security/integrity/ima/ima_ns.c
@@ -67,6 +67,7 @@ struct ima_namespace *copy_ima_ns(struct ima_namespace *old_ns,
static void destroy_ima_ns(struct ima_namespace *ns)
{
pr_debug("DESTROY ima_ns: 0x%p\n", ns);
+ ima_free_policy_rules(ns);
free_ns_status_cache(ns);
kmem_cache_free(imans_cachep, ns);
}
diff --git a/security/integrity/ima/ima_policy.c b/security/integrity/ima/ima_policy.c
index e5aef287d14d..4d6d3a39f65e 100644
--- a/security/integrity/ima/ima_policy.c
+++ b/security/integrity/ima/ima_policy.c
@@ -52,7 +52,6 @@
#define INVALID_PCR(a) (((a) < 0) || \
(a) >= (sizeof_field(struct integrity_iint_cache, measured_pcrs) * 8))
-int ima_policy_flag;
static int temp_ima_appraise;
static int build_ima_appraise __ro_after_init;
@@ -233,11 +232,6 @@ static struct ima_rule_entry critical_data_rules[] __ro_after_init = {
/* An array of architecture specific rules */
static struct ima_rule_entry *arch_policy_entry __ro_after_init;
-static LIST_HEAD(ima_default_rules);
-static LIST_HEAD(ima_policy_rules);
-static LIST_HEAD(ima_temp_rules);
-static struct list_head __rcu *ima_rules = (struct list_head __rcu *)(&ima_default_rules);
-
static int ima_policy __initdata;
static int __init default_measure_policy_setup(char *str)
@@ -454,12 +448,12 @@ static bool ima_rule_contains_lsm_cond(struct ima_rule_entry *entry)
* to the old, stale LSM policy. Update the IMA LSM based rules to reflect
* the reloaded LSM policy.
*/
-static void ima_lsm_update_rules(void)
+static void ima_lsm_update_rules(struct ima_namespace *ns)
{
struct ima_rule_entry *entry, *e;
int result;
- list_for_each_entry_safe(entry, e, &ima_policy_rules, list) {
+ list_for_each_entry_safe(entry, e, &ns->ima_policy_rules, list) {
if (!ima_rule_contains_lsm_cond(entry))
continue;
@@ -474,10 +468,12 @@ static void ima_lsm_update_rules(void)
int ima_lsm_policy_change(struct notifier_block *nb, unsigned long event,
void *lsm_data)
{
+ struct ima_namespace *ns = get_current_ns();
+
if (event != LSM_POLICY_CHANGE)
return NOTIFY_DONE;
- ima_lsm_update_rules();
+ ima_lsm_update_rules(ns);
return NOTIFY_OK;
}
@@ -688,7 +684,8 @@ static int get_subaction(struct ima_rule_entry *rule, enum ima_hooks func)
* list when walking it. Reads are many orders of magnitude more numerous
* than writes so ima_match_policy() is classical RCU candidate.
*/
-int ima_match_policy(struct user_namespace *mnt_userns, struct inode *inode,
+int ima_match_policy(struct ima_namespace *ns,
+ struct user_namespace *mnt_userns, struct inode *inode,
const struct cred *cred, u32 secid, enum ima_hooks func,
int mask, int flags, int *pcr,
struct ima_template_desc **template_desc,
@@ -702,7 +699,7 @@ int ima_match_policy(struct user_namespace *mnt_userns, struct inode *inode,
*template_desc = ima_template_desc_current();
rcu_read_lock();
- ima_rules_tmp = rcu_dereference(ima_rules);
+ ima_rules_tmp = rcu_dereference(ns->ima_rules);
list_for_each_entry_rcu(entry, ima_rules_tmp, list) {
if (!(entry->action & actmask))
@@ -760,14 +757,14 @@ int ima_match_policy(struct user_namespace *mnt_userns, struct inode *inode,
*
* Context: called after a policy update and at system initialization.
*/
-void ima_update_policy_flags(void)
+void ima_update_policy_flags(struct ima_namespace *ns)
{
struct ima_rule_entry *entry;
int new_policy_flag = 0;
struct list_head *ima_rules_tmp;
rcu_read_lock();
- ima_rules_tmp = rcu_dereference(ima_rules);
+ ima_rules_tmp = rcu_dereference(ns->ima_rules);
list_for_each_entry_rcu(entry, ima_rules_tmp, list) {
/*
* SETXATTR_CHECK rules do not implement a full policy check
@@ -797,7 +794,7 @@ void ima_update_policy_flags(void)
if (!ima_appraise)
new_policy_flag &= ~IMA_APPRAISE;
- ima_policy_flag = new_policy_flag;
+ ns->ima_policy_flag = new_policy_flag;
}
static int ima_appraise_flag(enum ima_hooks func)
@@ -813,7 +810,8 @@ static int ima_appraise_flag(enum ima_hooks func)
return 0;
}
-static void add_rules(struct ima_rule_entry *entries, int count,
+static void add_rules(struct ima_namespace *ns,
+ struct ima_rule_entry *entries, int count,
enum policy_rule_list policy_rule)
{
int i = 0;
@@ -822,7 +820,7 @@ static void add_rules(struct ima_rule_entry *entries, int count,
struct ima_rule_entry *entry;
if (policy_rule & IMA_DEFAULT_POLICY)
- list_add_tail(&entries[i].list, &ima_default_rules);
+ list_add_tail(&entries[i].list, &ns->ima_default_rules);
if (policy_rule & IMA_CUSTOM_POLICY) {
entry = kmemdup(&entries[i], sizeof(*entry),
@@ -830,7 +828,7 @@ static void add_rules(struct ima_rule_entry *entries, int count,
if (!entry)
continue;
- list_add_tail(&entry->list, &ima_policy_rules);
+ list_add_tail(&entry->list, &ns->ima_policy_rules);
}
if (entries[i].action == APPRAISE) {
if (entries != build_appraise_rules)
@@ -843,9 +841,10 @@ static void add_rules(struct ima_rule_entry *entries, int count,
}
}
-static int ima_parse_rule(char *rule, struct ima_rule_entry *entry);
+static int ima_parse_rule(struct ima_namespace *ns,
+ char *rule, struct ima_rule_entry *entry);
-static int __init ima_init_arch_policy(void)
+static int __init ima_init_arch_policy(struct ima_namespace *ns)
{
const char * const *arch_rules;
const char * const *rules;
@@ -873,7 +872,7 @@ static int __init ima_init_arch_policy(void)
result = strscpy(rule, *rules, sizeof(rule));
INIT_LIST_HEAD(&arch_policy_entry[i].list);
- result = ima_parse_rule(rule, &arch_policy_entry[i]);
+ result = ima_parse_rule(ns, rule, &arch_policy_entry[i]);
if (result) {
pr_warn("Skipping unknown architecture policy rule: %s\n",
rule);
@@ -891,23 +890,23 @@ static int __init ima_init_arch_policy(void)
*
* ima_rules points to either the ima_default_rules or the new ima_policy_rules.
*/
-void __init ima_init_policy(void)
+void __init ima_init_policy(struct ima_namespace *ns)
{
int build_appraise_entries, arch_entries;
/* if !ima_policy, we load NO default rules */
if (ima_policy)
- add_rules(dont_measure_rules, ARRAY_SIZE(dont_measure_rules),
+ add_rules(ns, dont_measure_rules, ARRAY_SIZE(dont_measure_rules),
IMA_DEFAULT_POLICY);
switch (ima_policy) {
case ORIGINAL_TCB:
- add_rules(original_measurement_rules,
+ add_rules(ns, original_measurement_rules,
ARRAY_SIZE(original_measurement_rules),
IMA_DEFAULT_POLICY);
break;
case DEFAULT_TCB:
- add_rules(default_measurement_rules,
+ add_rules(ns, default_measurement_rules,
ARRAY_SIZE(default_measurement_rules),
IMA_DEFAULT_POLICY);
break;
@@ -921,11 +920,11 @@ void __init ima_init_policy(void)
* and custom policies, prior to other appraise rules.
* (Highest priority)
*/
- arch_entries = ima_init_arch_policy();
+ arch_entries = ima_init_arch_policy(ns);
if (!arch_entries)
pr_info("No architecture policies found\n");
else
- add_rules(arch_policy_entry, arch_entries,
+ add_rules(ns, arch_policy_entry, arch_entries,
IMA_DEFAULT_POLICY | IMA_CUSTOM_POLICY);
/*
@@ -933,7 +932,7 @@ void __init ima_init_policy(void)
* signatures, prior to other appraise rules.
*/
if (ima_use_secure_boot)
- add_rules(secure_boot_rules, ARRAY_SIZE(secure_boot_rules),
+ add_rules(ns, secure_boot_rules, ARRAY_SIZE(secure_boot_rules),
IMA_DEFAULT_POLICY);
/*
@@ -945,32 +944,32 @@ void __init ima_init_policy(void)
build_appraise_entries = ARRAY_SIZE(build_appraise_rules);
if (build_appraise_entries) {
if (ima_use_secure_boot)
- add_rules(build_appraise_rules, build_appraise_entries,
+ add_rules(ns, build_appraise_rules, build_appraise_entries,
IMA_CUSTOM_POLICY);
else
- add_rules(build_appraise_rules, build_appraise_entries,
+ add_rules(ns, build_appraise_rules, build_appraise_entries,
IMA_DEFAULT_POLICY | IMA_CUSTOM_POLICY);
}
if (ima_use_appraise_tcb)
- add_rules(default_appraise_rules,
+ add_rules(ns, default_appraise_rules,
ARRAY_SIZE(default_appraise_rules),
IMA_DEFAULT_POLICY);
if (ima_use_critical_data)
- add_rules(critical_data_rules,
+ add_rules(ns, critical_data_rules,
ARRAY_SIZE(critical_data_rules),
IMA_DEFAULT_POLICY);
atomic_set(&ima_setxattr_allowed_hash_algorithms, 0);
- ima_update_policy_flags();
+ ima_update_policy_flags(ns);
}
/* Make sure we have a valid policy, at least containing some rules. */
-int ima_check_policy(void)
+int ima_check_policy(struct ima_namespace *ns)
{
- if (list_empty(&ima_temp_rules))
+ if (list_empty(&ns->ima_temp_rules))
return -EINVAL;
return 0;
}
@@ -988,14 +987,14 @@ int ima_check_policy(void)
*/
void ima_update_policy(struct ima_namespace *ns)
{
- struct list_head *policy = &ima_policy_rules;
+ struct list_head *policy = &ns->ima_policy_rules;
- list_splice_tail_init_rcu(&ima_temp_rules, policy, synchronize_rcu);
+ list_splice_tail_init_rcu(&ns->ima_temp_rules, policy, synchronize_rcu);
- if (ima_rules != (struct list_head __rcu *)policy) {
- ima_policy_flag = 0;
+ if (ns->ima_rules != (struct list_head __rcu *)policy) {
+ ns->ima_policy_flag = 0;
- rcu_assign_pointer(ima_rules, policy);
+ rcu_assign_pointer(ns->ima_rules, policy);
/*
* IMA architecture specific policy rules are specified
* as strings and converted to an array of ima_entry_rules
@@ -1004,7 +1003,7 @@ void ima_update_policy(struct ima_namespace *ns)
*/
kfree(arch_policy_entry);
}
- ima_update_policy_flags();
+ ima_update_policy_flags(ns);
/* Custom IMA policy has been loaded */
ima_process_queued_keys(ns);
@@ -1077,7 +1076,8 @@ static const match_table_t policy_tokens = {
{Opt_err, NULL}
};
-static int ima_lsm_rule_init(struct ima_rule_entry *entry,
+static int ima_lsm_rule_init(struct ima_namespace *ns,
+ struct ima_rule_entry *entry,
substring_t *args, int lsm_rule, int audit_type)
{
int result;
@@ -1097,7 +1097,7 @@ static int ima_lsm_rule_init(struct ima_rule_entry *entry,
pr_warn("rule for LSM \'%s\' is undefined\n",
entry->lsm[lsm_rule].args_p);
- if (ima_rules == (struct list_head __rcu *)(&ima_default_rules)) {
+ if (ns->ima_rules == (struct list_head __rcu *)(&ns->ima_default_rules)) {
kfree(entry->lsm[lsm_rule].args_p);
entry->lsm[lsm_rule].args_p = NULL;
result = -EINVAL;
@@ -1324,7 +1324,8 @@ static unsigned int ima_parse_appraise_algos(char *arg)
return res;
}
-static int ima_parse_rule(char *rule, struct ima_rule_entry *entry)
+static int ima_parse_rule(struct ima_namespace *ns,
+ char *rule, struct ima_rule_entry *entry)
{
struct audit_buffer *ab;
char *from;
@@ -1674,37 +1675,37 @@ static int ima_parse_rule(char *rule, struct ima_rule_entry *entry)
break;
case Opt_obj_user:
ima_log_string(ab, "obj_user", args[0].from);
- result = ima_lsm_rule_init(entry, args,
+ result = ima_lsm_rule_init(ns, entry, args,
LSM_OBJ_USER,
AUDIT_OBJ_USER);
break;
case Opt_obj_role:
ima_log_string(ab, "obj_role", args[0].from);
- result = ima_lsm_rule_init(entry, args,
+ result = ima_lsm_rule_init(ns, entry, args,
LSM_OBJ_ROLE,
AUDIT_OBJ_ROLE);
break;
case Opt_obj_type:
ima_log_string(ab, "obj_type", args[0].from);
- result = ima_lsm_rule_init(entry, args,
+ result = ima_lsm_rule_init(ns, entry, args,
LSM_OBJ_TYPE,
AUDIT_OBJ_TYPE);
break;
case Opt_subj_user:
ima_log_string(ab, "subj_user", args[0].from);
- result = ima_lsm_rule_init(entry, args,
+ result = ima_lsm_rule_init(ns, entry, args,
LSM_SUBJ_USER,
AUDIT_SUBJ_USER);
break;
case Opt_subj_role:
ima_log_string(ab, "subj_role", args[0].from);
- result = ima_lsm_rule_init(entry, args,
+ result = ima_lsm_rule_init(ns, entry, args,
LSM_SUBJ_ROLE,
AUDIT_SUBJ_ROLE);
break;
case Opt_subj_type:
ima_log_string(ab, "subj_type", args[0].from);
- result = ima_lsm_rule_init(entry, args,
+ result = ima_lsm_rule_init(ns, entry, args,
LSM_SUBJ_TYPE,
AUDIT_SUBJ_TYPE);
break;
@@ -1810,7 +1811,7 @@ static int ima_parse_rule(char *rule, struct ima_rule_entry *entry)
* Avoid locking by allowing just one writer at a time in ima_write_policy()
* Returns the length of the rule parsed, an error code on failure
*/
-ssize_t ima_parse_add_rule(char *rule)
+ssize_t ima_parse_add_rule(struct ima_namespace *ns, char *rule)
{
static const char op[] = "update_policy";
char *p;
@@ -1834,7 +1835,7 @@ ssize_t ima_parse_add_rule(char *rule)
INIT_LIST_HEAD(&entry->list);
- result = ima_parse_rule(p, entry);
+ result = ima_parse_rule(ns, p, entry);
if (result) {
ima_free_rule(entry);
integrity_audit_msg(AUDIT_INTEGRITY_STATUS, NULL,
@@ -1843,7 +1844,7 @@ ssize_t ima_parse_add_rule(char *rule)
return result;
}
- list_add_tail(&entry->list, &ima_temp_rules);
+ list_add_tail(&entry->list, &ns->ima_temp_rules);
return len;
}
@@ -1854,12 +1855,24 @@ ssize_t ima_parse_add_rule(char *rule)
* different from the active one. There is also only one user of
* ima_delete_rules() at a time.
*/
-void ima_delete_rules(void)
+void ima_delete_rules(struct ima_namespace *ns)
{
struct ima_rule_entry *entry, *tmp;
temp_ima_appraise = 0;
- list_for_each_entry_safe(entry, tmp, &ima_temp_rules, list) {
+ list_for_each_entry_safe(entry, tmp, &ns->ima_temp_rules, list) {
+ list_del(&entry->list);
+ ima_free_rule(entry);
+ }
+}
+
+void ima_free_policy_rules(struct ima_namespace *ns)
+{
+ struct ima_rule_entry *entry, *tmp;
+
+ ima_delete_rules(ns);
+
+ list_for_each_entry_safe(entry, tmp, &ns->ima_policy_rules, list) {
list_del(&entry->list);
ima_free_rule(entry);
}
@@ -1885,12 +1898,13 @@ static const char *const mask_tokens[] = {
void *ima_policy_start(struct seq_file *m, loff_t *pos)
{
+ struct ima_namespace *ns = get_current_ns();
loff_t l = *pos;
struct ima_rule_entry *entry;
struct list_head *ima_rules_tmp;
rcu_read_lock();
- ima_rules_tmp = rcu_dereference(ima_rules);
+ ima_rules_tmp = rcu_dereference(ns->ima_rules);
list_for_each_entry_rcu(entry, ima_rules_tmp, list) {
if (!l--) {
rcu_read_unlock();
@@ -1904,14 +1918,15 @@ 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_rule_entry *entry = v;
+ struct ima_namespace *ns = get_current_ns();
rcu_read_lock();
entry = list_entry_rcu(entry->list.next, struct ima_rule_entry, list);
rcu_read_unlock();
(*pos)++;
- return (&entry->list == &ima_default_rules ||
- &entry->list == &ima_policy_rules) ? NULL : entry;
+ return (&entry->list == &ns->ima_default_rules ||
+ &entry->list == &ns->ima_policy_rules) ? NULL : entry;
}
void ima_policy_stop(struct seq_file *m, void *v)
@@ -2166,6 +2181,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 = get_current_ns();
struct ima_rule_entry *entry;
bool found = false;
enum ima_hooks func;
@@ -2177,7 +2193,7 @@ bool ima_appraise_signature(enum kernel_read_file_id id)
func = read_idmap[id] ?: FILE_CHECK;
rcu_read_lock();
- ima_rules_tmp = rcu_dereference(ima_rules);
+ ima_rules_tmp = rcu_dereference(ns->ima_rules);
list_for_each_entry_rcu(entry, ima_rules_tmp, list) {
if (entry->action != APPRAISE)
continue;
diff --git a/security/integrity/ima/ima_queue_keys.c b/security/integrity/ima/ima_queue_keys.c
index 9e5e9a178253..14f334272160 100644
--- a/security/integrity/ima/ima_queue_keys.c
+++ b/security/integrity/ima/ima_queue_keys.c
@@ -142,7 +142,7 @@ void ima_process_queued_keys(struct ima_namespace *ns)
list_for_each_entry_safe(entry, tmp, &ns->ima_keys, list) {
if (!ns->timer_expired)
- process_buffer_measurement(&init_user_ns, NULL,
+ process_buffer_measurement(ns, &init_user_ns, NULL,
entry->payload,
entry->payload_len,
entry->keyring_name,
--
2.31.1
Move ima_htable into ima_namespace. This way a front-end like
SecurityFS can show the number of violations of an IMA namespace.
Move ima_hash_key() into ima_queue.c since it's only used there.
Signed-off-by: Stefan Berger <[email protected]>
---
include/linux/ima.h | 11 +++++++
security/integrity/ima/ima.h | 34 +++++++------------
security/integrity/ima/ima_api.c | 17 ++++++----
security/integrity/ima/ima_fs.c | 7 ++--
security/integrity/ima/ima_init.c | 6 ++--
security/integrity/ima/ima_init_ima_ns.c | 4 +++
security/integrity/ima/ima_main.c | 13 ++++----
security/integrity/ima/ima_queue.c | 42 ++++++++++++++----------
security/integrity/ima/ima_template.c | 4 +--
9 files changed, 78 insertions(+), 60 deletions(-)
diff --git a/include/linux/ima.h b/include/linux/ima.h
index e13e63a539d8..929bf87b1bbf 100644
--- a/include/linux/ima.h
+++ b/include/linux/ima.h
@@ -211,6 +211,15 @@ static inline int ima_inode_removexattr(struct dentry *dentry,
}
#endif /* CONFIG_IMA_APPRAISE */
+#define IMA_HASH_BITS 10
+#define IMA_MEASURE_HTABLE_SIZE (1 << IMA_HASH_BITS)
+
+struct ima_h_table {
+ atomic_long_t len; /* number of stored measurements in the list */
+ atomic_long_t violations;
+ struct hlist_head queue[IMA_MEASURE_HTABLE_SIZE];
+};
+
struct ima_namespace {
struct kref kref;
struct user_namespace *user_ns;
@@ -248,6 +257,8 @@ struct ima_namespace {
struct list_head __rcu *ima_rules;
/* current content of the policy */
int ima_policy_flag;
+
+ struct ima_h_table ima_htable;
};
extern struct ima_namespace init_ima_ns;
diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h
index e295141f2478..a7e6c8fb152a 100644
--- a/security/integrity/ima/ima.h
+++ b/security/integrity/ima/ima.h
@@ -32,9 +32,6 @@ enum tpm_pcrs { TPM_PCR0 = 0, TPM_PCR8 = 8, TPM_PCR10 = 10 };
#define IMA_DIGEST_SIZE SHA1_DIGEST_SIZE
#define IMA_EVENT_NAME_LEN_MAX 255
-#define IMA_HASH_BITS 10
-#define IMA_MEASURE_HTABLE_SIZE (1 << IMA_HASH_BITS)
-
#define IMA_TEMPLATE_FIELD_ID_MAX_LEN 16
#define IMA_TEMPLATE_NUM_FIELDS_MAX 15
@@ -143,7 +140,8 @@ struct ns_status {
/* Internal IMA function definitions */
int ima_init(void);
int ima_fs_init(void);
-int ima_add_template_entry(struct ima_template_entry *entry, int violation,
+int ima_add_template_entry(struct ima_namespace *ns,
+ struct ima_template_entry *entry, int violation,
const char *op, struct inode *inode,
const unsigned char *filename);
int ima_calc_file_hash(struct file *file, struct ima_digest_data *hash);
@@ -152,7 +150,8 @@ int ima_calc_buffer_hash(const void *buf, loff_t len,
int ima_calc_field_array_hash(struct ima_field_data *field_data,
struct ima_template_entry *entry);
int ima_calc_boot_aggregate(struct ima_digest_data *hash);
-void ima_add_violation(struct file *file, const unsigned char *filename,
+void ima_add_violation(struct ima_namespace *ns,
+ struct file *file, const unsigned char *filename,
struct integrity_iint_cache *iint,
const char *op, const char *cause);
int ima_init_crypto(void);
@@ -165,8 +164,10 @@ struct ima_template_desc *ima_template_desc_current(void);
struct ima_template_desc *ima_template_desc_buf(void);
struct ima_template_desc *lookup_template_desc(const char *name);
bool ima_template_has_modsig(const struct ima_template_desc *ima_template);
-int ima_restore_measurement_entry(struct ima_template_entry *entry);
-int ima_restore_measurement_list(loff_t bufsize, void *buf);
+int ima_restore_measurement_entry(struct ima_namespace *ns,
+ struct ima_template_entry *entry);
+int ima_restore_measurement_list(struct ima_namespace *ns,
+ loff_t bufsize, void *buf);
int ima_measurements_show(struct seq_file *m, void *v);
unsigned long ima_get_binary_runtime_size(void);
int ima_init_template(void);
@@ -180,19 +181,6 @@ int ima_lsm_policy_change(struct notifier_block *nb, unsigned long event,
*/
extern spinlock_t ima_queue_lock;
-struct ima_h_table {
- atomic_long_t len; /* number of stored measurements in the list */
- atomic_long_t violations;
- struct hlist_head queue[IMA_MEASURE_HTABLE_SIZE];
-};
-extern struct ima_h_table ima_htable;
-
-static inline unsigned int ima_hash_key(u8 *digest)
-{
- /* there is no point in taking a hash of part of a digest */
- return (digest[0] | digest[1] << 8) % IMA_MEASURE_HTABLE_SIZE;
-}
-
#define __ima_hooks(hook) \
hook(NONE, none) \
hook(FILE_CHECK, file) \
@@ -272,7 +260,8 @@ int ima_must_measure(struct inode *inode, int mask, enum ima_hooks func);
int ima_collect_measurement(struct integrity_iint_cache *iint,
struct file *file, void *buf, loff_t size,
enum hash_algo algo, struct modsig *modsig);
-void ima_store_measurement(struct integrity_iint_cache *iint, struct file *file,
+void ima_store_measurement(struct ima_namespace *ns,
+ struct integrity_iint_cache *iint, struct file *file,
const unsigned char *filename,
struct evm_ima_xattr_data *xattr_value,
int xattr_len, const struct modsig *modsig, int pcr,
@@ -289,7 +278,8 @@ void ima_audit_measurement(struct integrity_iint_cache *iint,
int ima_alloc_init_template(struct ima_event_data *event_data,
struct ima_template_entry **entry,
struct ima_template_desc *template_desc);
-int ima_store_template(struct ima_template_entry *entry, int violation,
+int ima_store_template(struct ima_namespace *ns,
+ struct ima_template_entry *entry, int violation,
struct inode *inode,
const unsigned char *filename, int pcr);
void ima_free_template_entry(struct ima_template_entry *entry);
diff --git a/security/integrity/ima/ima_api.c b/security/integrity/ima/ima_api.c
index 808aec56dbb6..71c5517fe8bc 100644
--- a/security/integrity/ima/ima_api.c
+++ b/security/integrity/ima/ima_api.c
@@ -100,7 +100,8 @@ int ima_alloc_init_template(struct ima_event_data *event_data,
*
* Returns 0 on success, error code otherwise
*/
-int ima_store_template(struct ima_template_entry *entry,
+int ima_store_template(struct ima_namespace *ns,
+ struct ima_template_entry *entry,
int violation, struct inode *inode,
const unsigned char *filename, int pcr)
{
@@ -120,7 +121,7 @@ int ima_store_template(struct ima_template_entry *entry,
}
}
entry->pcr = pcr;
- result = ima_add_template_entry(entry, violation, op, inode, filename);
+ result = ima_add_template_entry(ns, entry, violation, op, inode, filename);
return result;
}
@@ -131,7 +132,8 @@ int ima_store_template(struct ima_template_entry *entry,
* By extending the PCR with 0xFF's instead of with zeroes, the PCR
* value is invalidated.
*/
-void ima_add_violation(struct file *file, const unsigned char *filename,
+void ima_add_violation(struct ima_namespace *ns,
+ struct file *file, const unsigned char *filename,
struct integrity_iint_cache *iint,
const char *op, const char *cause)
{
@@ -145,14 +147,14 @@ void ima_add_violation(struct file *file, const unsigned char *filename,
int result;
/* can overflow, only indicator */
- atomic_long_inc(&ima_htable.violations);
+ atomic_long_inc(&ns->ima_htable.violations);
result = ima_alloc_init_template(&event_data, &entry, NULL);
if (result < 0) {
result = -ENOMEM;
goto err_out;
}
- result = ima_store_template(entry, violation, inode,
+ result = ima_store_template(ns, entry, violation, inode,
filename, CONFIG_IMA_MEASURE_PCR_IDX);
if (result < 0)
ima_free_template_entry(entry);
@@ -299,7 +301,8 @@ int ima_collect_measurement(struct integrity_iint_cache *iint,
*
* Must be called with iint->mutex held.
*/
-void ima_store_measurement(struct integrity_iint_cache *iint,
+void ima_store_measurement(struct ima_namespace *ns,
+ struct integrity_iint_cache *iint,
struct file *file, const unsigned char *filename,
struct evm_ima_xattr_data *xattr_value,
int xattr_len, const struct modsig *modsig, int pcr,
@@ -334,7 +337,7 @@ void ima_store_measurement(struct integrity_iint_cache *iint,
return;
}
- result = ima_store_template(entry, violation, inode, filename, pcr);
+ result = ima_store_template(ns, entry, violation, inode, filename, pcr);
if ((!result || result == -EEXIST) && !(file->f_flags & O_DIRECT)) {
iint->flags |= IMA_MEASURED;
iint->measured_pcrs |= (0x1 << pcr);
diff --git a/security/integrity/ima/ima_fs.c b/security/integrity/ima/ima_fs.c
index 25f85c58ebef..e37c973a94c6 100644
--- a/security/integrity/ima/ima_fs.c
+++ b/security/integrity/ima/ima_fs.c
@@ -53,7 +53,9 @@ static ssize_t ima_show_htable_violations(struct file *filp,
char __user *buf,
size_t count, loff_t *ppos)
{
- return ima_show_htable_value(buf, count, ppos, &ima_htable.violations);
+ struct ima_namespace *ns = get_current_ns();
+
+ return ima_show_htable_value(buf, count, ppos, &ns->ima_htable.violations);
}
static const struct file_operations ima_htable_violations_ops = {
@@ -65,8 +67,9 @@ static ssize_t ima_show_measurements_count(struct file *filp,
char __user *buf,
size_t count, loff_t *ppos)
{
- return ima_show_htable_value(buf, count, ppos, &ima_htable.len);
+ struct ima_namespace *ns = get_current_ns();
+ return ima_show_htable_value(buf, count, ppos, &ns->ima_htable.len);
}
static const struct file_operations ima_measurements_count_ops = {
diff --git a/security/integrity/ima/ima_init.c b/security/integrity/ima/ima_init.c
index d78f8faf6dcd..8e0ae3a4f04a 100644
--- a/security/integrity/ima/ima_init.c
+++ b/security/integrity/ima/ima_init.c
@@ -39,7 +39,7 @@ struct tpm_chip *ima_tpm_chip;
* a different value.) Violations add a zero entry to the measurement
* list and extend the aggregate PCR value with ff...ff's.
*/
-static int __init ima_add_boot_aggregate(void)
+static int __init ima_add_boot_aggregate(struct ima_namespace *ns)
{
static const char op[] = "add_boot_aggregate";
const char *audit_cause = "ENOMEM";
@@ -86,7 +86,7 @@ static int __init ima_add_boot_aggregate(void)
goto err_out;
}
- result = ima_store_template(entry, violation, NULL,
+ result = ima_store_template(ns, entry, violation, NULL,
boot_aggregate_name,
CONFIG_IMA_MEASURE_PCR_IDX);
if (result < 0) {
@@ -145,7 +145,7 @@ int __init ima_init(void)
rc = ima_init_digests();
if (rc != 0)
return rc;
- rc = ima_add_boot_aggregate(); /* boot aggregate must be first entry */
+ rc = ima_add_boot_aggregate(&init_ima_ns); /* boot aggregate must be first entry */
if (rc != 0)
return rc;
diff --git a/security/integrity/ima/ima_init_ima_ns.c b/security/integrity/ima/ima_init_ima_ns.c
index 2d644791a795..e13adc3287ed 100644
--- a/security/integrity/ima/ima_init_ima_ns.c
+++ b/security/integrity/ima/ima_init_ima_ns.c
@@ -40,6 +40,10 @@ int ima_init_namespace(struct ima_namespace *ns)
ns->ima_rules = (struct list_head __rcu *)(&ns->ima_default_rules);
ns->ima_policy_flag = 0;
+ atomic_long_set(&ns->ima_htable.len, 0);
+ atomic_long_set(&ns->ima_htable.violations, 0);
+ memset(&ns->ima_htable.queue, 0, sizeof(ns->ima_htable.queue));
+
return 0;
}
diff --git a/security/integrity/ima/ima_main.c b/security/integrity/ima/ima_main.c
index 897304046ba4..2121a831f38a 100644
--- a/security/integrity/ima/ima_main.c
+++ b/security/integrity/ima/ima_main.c
@@ -112,7 +112,8 @@ static int mmap_violation_check(enum ima_hooks func, struct file *file,
* could result in a file measurement error.
*
*/
-static void ima_rdwr_violation_check(struct file *file,
+static void ima_rdwr_violation_check(struct ima_namespace *ns,
+ struct file *file,
struct integrity_iint_cache *iint,
int must_measure,
char **pathbuf,
@@ -145,10 +146,10 @@ static void ima_rdwr_violation_check(struct file *file,
*pathname = ima_d_path(&file->f_path, pathbuf, filename);
if (send_tomtou)
- ima_add_violation(file, *pathname, iint,
+ ima_add_violation(ns, file, *pathname, iint,
"invalid_pcr", "ToMToU");
if (send_writers)
- ima_add_violation(file, *pathname, iint,
+ ima_add_violation(ns, file, *pathname, iint,
"invalid_pcr", "open_writers");
}
@@ -257,7 +258,7 @@ static int process_measurement(struct ima_namespace *ns,
}
if (!rc && violation_check)
- ima_rdwr_violation_check(file, iint, action & IMA_MEASURE,
+ ima_rdwr_violation_check(ns, file, iint, action & IMA_MEASURE,
&pathbuf, &pathname, filename);
inode_unlock(inode);
@@ -354,7 +355,7 @@ static int process_measurement(struct ima_namespace *ns,
pathname = ima_d_path(&file->f_path, &pathbuf, filename);
if (action & IMA_MEASURE)
- ima_store_measurement(iint, file, pathname,
+ ima_store_measurement(ns, iint, file, pathname,
xattr_value, xattr_len, modsig, pcr,
template_desc);
if (rc == 0 && (action & IMA_APPRAISE_SUBMASK)) {
@@ -978,7 +979,7 @@ int process_buffer_measurement(struct ima_namespace *ns,
goto out;
}
- ret = ima_store_template(entry, violation, NULL, event_data.buf, pcr);
+ ret = ima_store_template(ns, entry, violation, NULL, event_data.buf, pcr);
if (ret < 0) {
audit_cause = "store_entry";
ima_free_template_entry(entry);
diff --git a/security/integrity/ima/ima_queue.c b/security/integrity/ima/ima_queue.c
index 532da87ce519..373154039b91 100644
--- a/security/integrity/ima/ima_queue.c
+++ b/security/integrity/ima/ima_queue.c
@@ -17,6 +17,7 @@
#include <linux/rculist.h>
#include <linux/slab.h>
+#include <linux/ima.h>
#include "ima.h"
#define AUDIT_CAUSE_LEN_MAX 32
@@ -31,21 +32,22 @@ static unsigned long binary_runtime_size;
static unsigned long binary_runtime_size = ULONG_MAX;
#endif
-/* key: inode (before secure-hashing a file) */
-struct ima_h_table ima_htable = {
- .len = ATOMIC_LONG_INIT(0),
- .violations = ATOMIC_LONG_INIT(0),
- .queue[0 ... IMA_MEASURE_HTABLE_SIZE - 1] = HLIST_HEAD_INIT
-};
-
/* mutex protects atomicity of extending measurement list
* and extending the TPM PCR aggregate. Since tpm_extend can take
* long (and the tpm driver uses a mutex), we can't use the spinlock.
*/
static DEFINE_MUTEX(ima_extend_list_mutex);
+
+static inline unsigned int ima_hash_key(u8 *digest)
+{
+ /* there is no point in taking a hash of part of a digest */
+ return (digest[0] | digest[1] << 8) % IMA_MEASURE_HTABLE_SIZE;
+}
+
/* lookup up the digest value in the hash table, and return the entry */
-static struct ima_queue_entry *ima_lookup_digest_entry(u8 *digest_value,
+static struct ima_queue_entry *ima_lookup_digest_entry(struct ima_namespace *ns,
+ u8 *digest_value,
int pcr)
{
struct ima_queue_entry *qe, *ret = NULL;
@@ -54,7 +56,7 @@ static struct ima_queue_entry *ima_lookup_digest_entry(u8 *digest_value,
key = ima_hash_key(digest_value);
rcu_read_lock();
- hlist_for_each_entry_rcu(qe, &ima_htable.queue[key], hnext) {
+ hlist_for_each_entry_rcu(qe, &ns->ima_htable.queue[key], hnext) {
rc = memcmp(qe->entry->digests[ima_hash_algo_idx].digest,
digest_value, hash_digest_size[ima_hash_algo]);
if ((rc == 0) && (qe->entry->pcr == pcr)) {
@@ -90,7 +92,8 @@ static int get_binary_runtime_size(struct ima_template_entry *entry)
*
* (Called with ima_extend_list_mutex held.)
*/
-static int ima_add_digest_entry(struct ima_template_entry *entry,
+static int ima_add_digest_entry(struct ima_namespace *ns,
+ struct ima_template_entry *entry,
bool update_htable)
{
struct ima_queue_entry *qe;
@@ -106,11 +109,12 @@ static int ima_add_digest_entry(struct ima_template_entry *entry,
INIT_LIST_HEAD(&qe->later);
list_add_tail_rcu(&qe->later, &ima_measurements);
- atomic_long_inc(&ima_htable.len);
+ atomic_long_inc(&ns->ima_htable.len);
if (update_htable) {
key = ima_hash_key(entry->digests[ima_hash_algo_idx].digest);
- hlist_add_head_rcu(&qe->hnext, &ima_htable.queue[key]);
- }
+ hlist_add_head_rcu(&qe->hnext, &ns->ima_htable.queue[key]);
+ } else
+ INIT_HLIST_NODE(&qe->hnext);
if (binary_runtime_size != ULONG_MAX) {
int size;
@@ -156,7 +160,8 @@ static int ima_pcr_extend(struct tpm_digest *digests_arg, int pcr)
* kexec, maintain the total memory size required for serializing the
* binary_runtime_measurements.
*/
-int ima_add_template_entry(struct ima_template_entry *entry, int violation,
+int ima_add_template_entry(struct ima_namespace *ns,
+ struct ima_template_entry *entry, int violation,
const char *op, struct inode *inode,
const unsigned char *filename)
{
@@ -169,14 +174,14 @@ int ima_add_template_entry(struct ima_template_entry *entry, int violation,
mutex_lock(&ima_extend_list_mutex);
if (!violation && !IS_ENABLED(CONFIG_IMA_DISABLE_HTABLE)) {
- if (ima_lookup_digest_entry(digest, entry->pcr)) {
+ if (ima_lookup_digest_entry(ns, digest, entry->pcr)) {
audit_cause = "hash_exists";
result = -EEXIST;
goto out;
}
}
- result = ima_add_digest_entry(entry,
+ result = ima_add_digest_entry(ns, entry,
!IS_ENABLED(CONFIG_IMA_DISABLE_HTABLE));
if (result < 0) {
audit_cause = "ENOMEM";
@@ -201,12 +206,13 @@ int ima_add_template_entry(struct ima_template_entry *entry, int violation,
return result;
}
-int ima_restore_measurement_entry(struct ima_template_entry *entry)
+int ima_restore_measurement_entry(struct ima_namespace *ns,
+ struct ima_template_entry *entry)
{
int result = 0;
mutex_lock(&ima_extend_list_mutex);
- result = ima_add_digest_entry(entry, 0);
+ result = ima_add_digest_entry(ns, entry, 0);
mutex_unlock(&ima_extend_list_mutex);
return result;
}
diff --git a/security/integrity/ima/ima_template.c b/security/integrity/ima/ima_template.c
index 694560396be0..2ae87eb23a59 100644
--- a/security/integrity/ima/ima_template.c
+++ b/security/integrity/ima/ima_template.c
@@ -400,7 +400,7 @@ static int ima_restore_template_data(struct ima_template_desc *template_desc,
}
/* Restore the serialized binary measurement list without extending PCRs. */
-int ima_restore_measurement_list(loff_t size, void *buf)
+int ima_restore_measurement_list(struct ima_namespace *ns, loff_t size, void *buf)
{
char template_name[MAX_TEMPLATE_NAME_LEN];
unsigned char zero[TPM_DIGEST_SIZE] = { 0 };
@@ -516,7 +516,7 @@ int ima_restore_measurement_list(loff_t size, void *buf)
entry->pcr = !ima_canonical_fmt ? *(u32 *)(hdr[HDR_PCR].data) :
le32_to_cpu(*(__le32 *)(hdr[HDR_PCR].data));
- ret = ima_restore_measurement_entry(entry);
+ ret = ima_restore_measurement_entry(ns, entry);
if (ret < 0)
break;
--
2.31.1
Move measurement list related variables into the ima_namespace. This way a
front-end like SecurityFS can show the measurement list inside an IMA
namespace.
Implement ima_free_measurements() to free a list of measurements
and call it when an IMA namespace is deleted.
Signed-off-by: Stefan Berger <[email protected]>
---
include/linux/ima.h | 2 ++
security/integrity/ima/ima.h | 4 +--
security/integrity/ima/ima_fs.c | 6 +++--
security/integrity/ima/ima_init_ima_ns.c | 5 ++++
security/integrity/ima/ima_ns.c | 1 +
security/integrity/ima/ima_queue.c | 33 ++++++++++++++----------
6 files changed, 33 insertions(+), 18 deletions(-)
diff --git a/include/linux/ima.h b/include/linux/ima.h
index 929bf87b1bbf..53f944469de7 100644
--- a/include/linux/ima.h
+++ b/include/linux/ima.h
@@ -259,6 +259,8 @@ struct ima_namespace {
int ima_policy_flag;
struct ima_h_table ima_htable;
+ struct list_head ima_measurements;
+ unsigned long binary_runtime_size;
};
extern struct ima_namespace init_ima_ns;
diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h
index a7e6c8fb152a..bb9763cd5fb1 100644
--- a/security/integrity/ima/ima.h
+++ b/security/integrity/ima/ima.h
@@ -104,7 +104,6 @@ struct ima_queue_entry {
struct list_head later; /* place in ima_measurements list */
struct ima_template_entry *entry;
};
-extern struct list_head ima_measurements; /* list of all measurements */
/* Some details preceding the binary serialized measurement list */
struct ima_kexec_hdr {
@@ -168,8 +167,9 @@ int ima_restore_measurement_entry(struct ima_namespace *ns,
struct ima_template_entry *entry);
int ima_restore_measurement_list(struct ima_namespace *ns,
loff_t bufsize, void *buf);
+void ima_free_measurements(struct ima_namespace *ns);
int ima_measurements_show(struct seq_file *m, void *v);
-unsigned long ima_get_binary_runtime_size(void);
+unsigned long ima_get_binary_runtime_size(struct ima_namespace *ns);
int ima_init_template(void);
void ima_init_template_list(void);
int __init ima_init_digests(void);
diff --git a/security/integrity/ima/ima_fs.c b/security/integrity/ima/ima_fs.c
index e37c973a94c6..38b1c26479b3 100644
--- a/security/integrity/ima/ima_fs.c
+++ b/security/integrity/ima/ima_fs.c
@@ -80,12 +80,13 @@ 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 = get_current_ns();
loff_t l = *pos;
struct ima_queue_entry *qe;
/* we need a lock since pos could point beyond last element */
rcu_read_lock();
- list_for_each_entry_rcu(qe, &ima_measurements, later) {
+ list_for_each_entry_rcu(qe, &ns->ima_measurements, later) {
if (!l--) {
rcu_read_unlock();
return qe;
@@ -97,6 +98,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 = get_current_ns();
struct ima_queue_entry *qe = v;
/* lock protects when reading beyond last element
@@ -107,7 +109,7 @@ static void *ima_measurements_next(struct seq_file *m, void *v, loff_t *pos)
rcu_read_unlock();
(*pos)++;
- return (&qe->later == &ima_measurements) ? NULL : qe;
+ return (&qe->later == &ns->ima_measurements) ? NULL : qe;
}
static void ima_measurements_stop(struct seq_file *m, void *v)
diff --git a/security/integrity/ima/ima_init_ima_ns.c b/security/integrity/ima/ima_init_ima_ns.c
index e13adc3287ed..57e46a10c001 100644
--- a/security/integrity/ima/ima_init_ima_ns.c
+++ b/security/integrity/ima/ima_init_ima_ns.c
@@ -43,6 +43,11 @@ int ima_init_namespace(struct ima_namespace *ns)
atomic_long_set(&ns->ima_htable.len, 0);
atomic_long_set(&ns->ima_htable.violations, 0);
memset(&ns->ima_htable.queue, 0, sizeof(ns->ima_htable.queue));
+ INIT_LIST_HEAD(&ns->ima_measurements);
+ if (IS_ENABLED(CONFIG_IMA_KEXEC) && ns == &init_ima_ns)
+ ns->binary_runtime_size = 0;
+ else
+ ns->binary_runtime_size = ULONG_MAX;
return 0;
}
diff --git a/security/integrity/ima/ima_ns.c b/security/integrity/ima/ima_ns.c
index efbf7087a8ee..debe863364fd 100644
--- a/security/integrity/ima/ima_ns.c
+++ b/security/integrity/ima/ima_ns.c
@@ -69,6 +69,7 @@ static void destroy_ima_ns(struct ima_namespace *ns)
pr_debug("DESTROY ima_ns: 0x%p\n", ns);
ima_free_policy_rules(ns);
free_ns_status_cache(ns);
+ ima_free_measurements(ns);
kmem_cache_free(imans_cachep, ns);
}
diff --git a/security/integrity/ima/ima_queue.c b/security/integrity/ima/ima_queue.c
index 373154039b91..f15f776918ec 100644
--- a/security/integrity/ima/ima_queue.c
+++ b/security/integrity/ima/ima_queue.c
@@ -25,13 +25,6 @@
/* pre-allocated array of tpm_digest structures to extend a PCR */
static struct tpm_digest *digests;
-LIST_HEAD(ima_measurements); /* list of all measurements */
-#ifdef CONFIG_IMA_KEXEC
-static unsigned long binary_runtime_size;
-#else
-static unsigned long binary_runtime_size = ULONG_MAX;
-#endif
-
/* mutex protects atomicity of extending measurement list
* and extending the TPM PCR aggregate. Since tpm_extend can take
* long (and the tpm driver uses a mutex), we can't use the spinlock.
@@ -107,7 +100,7 @@ static int ima_add_digest_entry(struct ima_namespace *ns,
qe->entry = entry;
INIT_LIST_HEAD(&qe->later);
- list_add_tail_rcu(&qe->later, &ima_measurements);
+ list_add_tail_rcu(&qe->later, &ns->ima_measurements);
atomic_long_inc(&ns->ima_htable.len);
if (update_htable) {
@@ -116,12 +109,12 @@ static int ima_add_digest_entry(struct ima_namespace *ns,
} else
INIT_HLIST_NODE(&qe->hnext);
- if (binary_runtime_size != ULONG_MAX) {
+ if (ns->binary_runtime_size != ULONG_MAX) {
int size;
size = get_binary_runtime_size(entry);
- binary_runtime_size = (binary_runtime_size < ULONG_MAX - size) ?
- binary_runtime_size + size : ULONG_MAX;
+ ns->binary_runtime_size = (ns->binary_runtime_size < ULONG_MAX - size) ?
+ ns->binary_runtime_size + size : ULONG_MAX;
}
return 0;
}
@@ -131,12 +124,12 @@ static int ima_add_digest_entry(struct ima_namespace *ns,
* entire binary_runtime_measurement list, including the ima_kexec_hdr
* structure.
*/
-unsigned long ima_get_binary_runtime_size(void)
+unsigned long ima_get_binary_runtime_size(struct ima_namespace *ns)
{
- if (binary_runtime_size >= (ULONG_MAX - sizeof(struct ima_kexec_hdr)))
+ if (ns->binary_runtime_size >= (ULONG_MAX - sizeof(struct ima_kexec_hdr)))
return ULONG_MAX;
else
- return binary_runtime_size + sizeof(struct ima_kexec_hdr);
+ return ns->binary_runtime_size + sizeof(struct ima_kexec_hdr);
}
static int ima_pcr_extend(struct tpm_digest *digests_arg, int pcr)
@@ -217,6 +210,18 @@ int ima_restore_measurement_entry(struct ima_namespace *ns,
return result;
}
+void ima_free_measurements(struct ima_namespace *ns)
+{
+ struct ima_queue_entry *qe, *tmp;
+
+ list_for_each_entry_safe(qe, tmp, &ns->ima_measurements, later) {
+ list_del(&qe->later);
+ if (!hlist_unhashed(&qe->hnext))
+ hlist_del(&qe->hnext);
+ kfree(qe);
+ }
+}
+
int __init ima_init_digests(void)
{
u16 digest_size;
--
2.31.1
Only accept AUDIT rules for non-init_ima_ns namespaces rejecting all rules
that require support for measuring, appraisal, and hashing.
Signed-off-by: Stefan Berger <[email protected]>
---
security/integrity/ima/ima_policy.c | 10 ++++++++++
1 file changed, 10 insertions(+)
diff --git a/security/integrity/ima/ima_policy.c b/security/integrity/ima/ima_policy.c
index 4d6d3a39f65e..747dca6131d6 100644
--- a/security/integrity/ima/ima_policy.c
+++ b/security/integrity/ima/ima_policy.c
@@ -1787,6 +1787,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;
--
2.31.1
Implement hierarchical processing of file accesses in IMA namespaces by
walking the list of IMA namespaces towards the init_ima_ns. This way
file accesses can be audited in an IMA namespace and also be evaluated
against the IMA policies of parent IMA namespaces.
Signed-off-by: Stefan Berger <[email protected]>
---
security/integrity/ima/ima_main.c | 29 +++++++++++++++++++++++++----
1 file changed, 25 insertions(+), 4 deletions(-)
diff --git a/security/integrity/ima/ima_main.c b/security/integrity/ima/ima_main.c
index 2121a831f38a..e9fa46eedd27 100644
--- a/security/integrity/ima/ima_main.c
+++ b/security/integrity/ima/ima_main.c
@@ -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;
@@ -405,6 +405,27 @@ 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)
+{
+ int ret = 0;
+ struct user_namespace *user_ns;
+
+ do {
+ ret = _process_measurement(ns, file, cred, secid, buf, size, mask, func);
+ if (ret)
+ break;
+ user_ns = ns->user_ns->parent;
+ if (!user_ns)
+ break;
+ ns = user_ns->ima_ns;
+ } while (1);
+
+ return ret;
+}
+
/**
* ima_file_mmap - based on policy, collect/store measurement.
* @file: pointer to the file to be measured (May be NULL)
--
2.31.1
To prepare for virtualization of SecurityFS, use simple_pin_fs and
simpe_release_fs only when init_user_ns is active.
Signed-off-by: Stefan Berger <[email protected]>
Signed-off-by: James Bottomley <[email protected]>
---
security/inode.c | 30 +++++++++++++++++++++---------
1 file changed, 21 insertions(+), 9 deletions(-)
diff --git a/security/inode.c b/security/inode.c
index 6c326939750d..1a720b2c566d 100644
--- a/security/inode.c
+++ b/security/inode.c
@@ -21,9 +21,10 @@
#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 *securityfs_mount;
+static int securityfs_mount_count;
static void securityfs_free_inode(struct inode *inode)
{
@@ -109,6 +110,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 +120,17 @@ 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, &securityfs_mount,
+ &securityfs_mount_count);
+ if (error)
+ return ERR_PTR(error);
+ }
- if (!parent)
- parent = mount->mnt_root;
+ if (!parent) {
+ if (ns == &init_user_ns)
+ parent = securityfs_mount->mnt_root;
+ }
dir = d_inode(parent);
@@ -168,7 +175,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(&securityfs_mount,
+ &securityfs_mount_count);
return dentry;
}
@@ -294,6 +303,7 @@ EXPORT_SYMBOL_GPL(securityfs_create_symlink);
*/
void securityfs_remove(struct dentry *dentry)
{
+ struct user_namespace *ns = dentry->d_sb->s_user_ns;
struct inode *dir;
if (!dentry || IS_ERR(dentry))
@@ -309,7 +319,9 @@ void securityfs_remove(struct dentry *dentry)
dput(dentry);
}
inode_unlock(dir);
- simple_release_fs(&mount, &mount_count);
+ if (ns == &init_user_ns)
+ simple_release_fs(&securityfs_mount,
+ &securityfs_mount_count);
}
EXPORT_SYMBOL_GPL(securityfs_remove);
--
2.31.1
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.
Signed-off-by: Stefan Berger <[email protected]>
Signed-off-by: James Bottomley <[email protected]>
---
security/inode.c | 12 ++++++++++--
1 file changed, 10 insertions(+), 2 deletions(-)
diff --git a/security/inode.c b/security/inode.c
index 1a720b2c566d..121ac1874dde 100644
--- a/security/inode.c
+++ b/security/inode.c
@@ -54,7 +54,7 @@ static int securityfs_fill_super(struct super_block *sb, struct fs_context *fc)
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 = {
@@ -67,11 +67,17 @@ static int securityfs_init_fs_context(struct fs_context *fc)
return 0;
}
+static void securityfs_kill_super(struct super_block *sb)
+{
+ kill_litter_super(sb);
+}
+
static struct file_system_type fs_type = {
.owner = THIS_MODULE,
.name = "securityfs",
.init_fs_context = securityfs_init_fs_context,
- .kill_sb = kill_litter_super,
+ .kill_sb = securityfs_kill_super,
+ .fs_flags = FS_USERNS_MOUNT,
};
/**
@@ -130,6 +136,8 @@ static struct dentry *securityfs_create_dentry(const char *name, umode_t mode,
if (!parent) {
if (ns == &init_user_ns)
parent = securityfs_mount->mnt_root;
+ else
+ return ERR_PTR(-EINVAL);
}
dir = d_inode(parent);
--
2.31.1
Move the ima_write_mutex, ima_fs_flag, and valid_policy variables into
ima_namespace. This way each IMA namespace can set those variables
independently.
Signed-off-by: Stefan Berger <[email protected]>
---
include/linux/ima.h | 5 ++++
security/integrity/ima/ima_fs.c | 32 +++++++++++-------------
security/integrity/ima/ima_init_ima_ns.c | 4 +++
3 files changed, 23 insertions(+), 18 deletions(-)
diff --git a/include/linux/ima.h b/include/linux/ima.h
index 53f944469de7..889e9c70cbfb 100644
--- a/include/linux/ima.h
+++ b/include/linux/ima.h
@@ -261,6 +261,11 @@ struct ima_namespace {
struct ima_h_table ima_htable;
struct list_head ima_measurements;
unsigned long binary_runtime_size;
+
+ /* IMA's filesystem */
+ struct mutex ima_write_mutex;
+ unsigned long ima_fs_flags;
+ int valid_policy;
};
extern struct ima_namespace init_ima_ns;
diff --git a/security/integrity/ima/ima_fs.c b/security/integrity/ima/ima_fs.c
index 38b1c26479b3..0e582ceecc7f 100644
--- a/security/integrity/ima/ima_fs.c
+++ b/security/integrity/ima/ima_fs.c
@@ -25,8 +25,6 @@
#include "ima.h"
-static DEFINE_MUTEX(ima_write_mutex);
-
bool ima_canonical_fmt;
static int __init default_canonical_fmt_setup(char *str)
{
@@ -37,8 +35,6 @@ static int __init default_canonical_fmt_setup(char *str)
}
__setup("ima_canonical_fmt", default_canonical_fmt_setup);
-static int valid_policy = 1;
-
static ssize_t ima_show_htable_value(char __user *buf, size_t count,
loff_t *ppos, atomic_long_t *val)
{
@@ -339,7 +335,7 @@ static ssize_t ima_write_policy(struct file *file, const char __user *buf,
goto out;
}
- result = mutex_lock_interruptible(&ima_write_mutex);
+ result = mutex_lock_interruptible(&ns->ima_write_mutex);
if (result < 0)
goto out_free;
@@ -354,12 +350,12 @@ static ssize_t ima_write_policy(struct file *file, const char __user *buf,
} else {
result = ima_parse_add_rule(ns, data);
}
- mutex_unlock(&ima_write_mutex);
+ mutex_unlock(&ns->ima_write_mutex);
out_free:
kfree(data);
out:
if (result < 0)
- valid_policy = 0;
+ ns->valid_policy = 0;
return result;
}
@@ -376,8 +372,6 @@ enum ima_fs_flags {
IMA_FS_BUSY,
};
-static unsigned long ima_fs_flags;
-
#ifdef CONFIG_IMA_READ_POLICY
static const struct seq_operations ima_policy_seqops = {
.start = ima_policy_start,
@@ -392,6 +386,8 @@ static const struct seq_operations ima_policy_seqops = {
*/
static int ima_open_policy(struct inode *inode, struct file *filp)
{
+ struct ima_namespace *ns = get_current_ns();
+
if (!(filp->f_flags & O_WRONLY)) {
#ifndef CONFIG_IMA_READ_POLICY
return -EACCES;
@@ -403,7 +399,7 @@ static int ima_open_policy(struct inode *inode, struct file *filp)
return seq_open(filp, &ima_policy_seqops);
#endif
}
- if (test_and_set_bit(IMA_FS_BUSY, &ima_fs_flags))
+ if (test_and_set_bit(IMA_FS_BUSY, &ns->ima_fs_flags))
return -EBUSY;
return 0;
}
@@ -417,25 +413,25 @@ static int ima_open_policy(struct inode *inode, struct file *filp)
*/
static int ima_release_policy(struct inode *inode, struct file *file)
{
- const char *cause = valid_policy ? "completed" : "failed";
struct ima_namespace *ns = get_current_ns();
+ const char *cause = ns->valid_policy ? "completed" : "failed";
if ((file->f_flags & O_ACCMODE) == O_RDONLY)
return seq_release(inode, file);
- if (valid_policy && ima_check_policy(ns) < 0) {
+ if (ns->valid_policy && ima_check_policy(ns) < 0) {
cause = "failed";
- valid_policy = 0;
+ ns->valid_policy = 0;
}
pr_info("policy update %s\n", cause);
integrity_audit_msg(AUDIT_INTEGRITY_STATUS, NULL, NULL,
- "policy_update", cause, !valid_policy, 0);
+ "policy_update", cause, !ns->valid_policy, 0);
- if (!valid_policy) {
+ if (!ns->valid_policy) {
ima_delete_rules(ns);
- valid_policy = 1;
- clear_bit(IMA_FS_BUSY, &ima_fs_flags);
+ ns->valid_policy = 1;
+ clear_bit(IMA_FS_BUSY, &ns->ima_fs_flags);
return 0;
}
@@ -444,7 +440,7 @@ static int ima_release_policy(struct inode *inode, struct file *file)
securityfs_remove(ima_policy);
ima_policy = NULL;
#elif defined(CONFIG_IMA_WRITE_POLICY)
- clear_bit(IMA_FS_BUSY, &ima_fs_flags);
+ clear_bit(IMA_FS_BUSY, &ns->ima_fs_flags);
#elif defined(CONFIG_IMA_READ_POLICY)
inode->i_mode &= ~S_IWUSR;
#endif
diff --git a/security/integrity/ima/ima_init_ima_ns.c b/security/integrity/ima/ima_init_ima_ns.c
index 57e46a10c001..22ff74e85a5f 100644
--- a/security/integrity/ima/ima_init_ima_ns.c
+++ b/security/integrity/ima/ima_init_ima_ns.c
@@ -49,6 +49,10 @@ int ima_init_namespace(struct ima_namespace *ns)
else
ns->binary_runtime_size = ULONG_MAX;
+ mutex_init(&ns->ima_write_mutex);
+ ns->valid_policy = 1;
+ ns->ima_fs_flags = 0;
+
return 0;
}
--
2.31.1
Use mac_admin_ns_capable() to check corresponding capability to allow
read/write IMA policy without CAP_SYS_ADMIN but with CAP_MAC_ADMIN.
Signed-off-by: Denis Semakin <[email protected]>
Signed-off-by: Stefan Berger <[email protected]>
---
include/linux/capability.h | 6 ++++++
security/integrity/ima/ima_fs.c | 2 +-
2 files changed, 7 insertions(+), 1 deletion(-)
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/security/integrity/ima/ima_fs.c b/security/integrity/ima/ima_fs.c
index 0e582ceecc7f..a749a3e79304 100644
--- a/security/integrity/ima/ima_fs.c
+++ b/security/integrity/ima/ima_fs.c
@@ -394,7 +394,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(ns->user_ns))
return -EPERM;
return seq_open(filp, &ima_policy_seqops);
#endif
--
2.31.1
Move the dentries into the ima_namespace for reuse by virtualized
SecurityFS. Implement function freeing the dentries in order of
files and symlinks before directories.
Signed-off-by: Stefan Berger <[email protected]>
---
include/linux/ima.h | 13 ++++++
security/integrity/ima/ima_fs.c | 72 ++++++++++++++++++---------------
2 files changed, 52 insertions(+), 33 deletions(-)
diff --git a/include/linux/ima.h b/include/linux/ima.h
index 889e9c70cbfb..bfb978a7f8d5 100644
--- a/include/linux/ima.h
+++ b/include/linux/ima.h
@@ -220,6 +220,17 @@ struct ima_h_table {
struct hlist_head queue[IMA_MEASURE_HTABLE_SIZE];
};
+enum {
+ IMAFS_DENTRY_DIR = 0,
+ IMAFS_DENTRY_SYMLINK,
+ IMAFS_DENTRY_BINARY_RUNTIME_MEASUREMENTS,
+ IMAFS_DENTRY_ASCII_RUNTIME_MEASUREMENTS,
+ IMAFS_DENTRY_RUNTIME_MEASUREMENTS_COUNT,
+ IMAFS_DENTRY_VIOLATIONS,
+ IMAFS_DENTRY_IMA_POLICY,
+ IMAFS_DENTRY_LAST
+};
+
struct ima_namespace {
struct kref kref;
struct user_namespace *user_ns;
@@ -266,6 +277,8 @@ struct ima_namespace {
struct mutex ima_write_mutex;
unsigned long ima_fs_flags;
int valid_policy;
+
+ struct dentry *dentry[IMAFS_DENTRY_LAST];
};
extern struct ima_namespace init_ima_ns;
diff --git a/security/integrity/ima/ima_fs.c b/security/integrity/ima/ima_fs.c
index a749a3e79304..e2cee1457aaa 100644
--- a/security/integrity/ima/ima_fs.c
+++ b/security/integrity/ima/ima_fs.c
@@ -360,14 +360,6 @@ static ssize_t ima_write_policy(struct file *file, const char __user *buf,
return result;
}
-static struct dentry *ima_dir;
-static struct dentry *ima_symlink;
-static struct dentry *binary_runtime_measurements;
-static struct dentry *ascii_runtime_measurements;
-static struct dentry *runtime_measurements_count;
-static struct dentry *violations;
-static struct dentry *ima_policy;
-
enum ima_fs_flags {
IMA_FS_BUSY,
};
@@ -437,8 +429,8 @@ static int ima_release_policy(struct inode *inode, struct file *file)
ima_update_policy(ns);
#if !defined(CONFIG_IMA_WRITE_POLICY) && !defined(CONFIG_IMA_READ_POLICY)
- securityfs_remove(ima_policy);
- ima_policy = NULL;
+ securityfs_remove(ns->dentry[IMAFS_DENTRY_IMA_POLICY]);
+ ns->dentry[IMAFS_DENTRY_IMA_POLICY] = NULL;
#elif defined(CONFIG_IMA_WRITE_POLICY)
clear_bit(IMA_FS_BUSY, &ns->ima_fs_flags);
#elif defined(CONFIG_IMA_READ_POLICY)
@@ -455,58 +447,72 @@ static const struct file_operations ima_measure_policy_ops = {
.llseek = generic_file_llseek,
};
-int __init ima_fs_init(void)
+static void ima_fs_ns_free_dentries(struct ima_namespace *ns)
{
- ima_dir = securityfs_create_dir("ima", integrity_dir);
- if (IS_ERR(ima_dir))
+ int i;
+
+ for (i = IMAFS_DENTRY_LAST - 1; i >= 0; i--)
+ securityfs_remove(ns->dentry[i]);
+
+ memset(ns->dentry, 0, sizeof(ns->dentry));
+}
+
+static int __init ima_fs_ns_init(struct user_namespace *user_ns)
+{
+ struct ima_namespace *ns = user_ns->ima_ns;
+ struct dentry *ima_dir;
+
+ ns->dentry[IMAFS_DENTRY_DIR] = securityfs_create_dir("ima", integrity_dir);
+ if (IS_ERR(ns->dentry[IMAFS_DENTRY_DIR]))
return -1;
+ ima_dir = ns->dentry[IMAFS_DENTRY_DIR];
- ima_symlink = securityfs_create_symlink("ima", NULL, "integrity/ima",
- NULL);
- if (IS_ERR(ima_symlink))
+ ns->dentry[IMAFS_DENTRY_SYMLINK] =
+ securityfs_create_symlink("ima", NULL, "integrity/ima", NULL);
+ if (IS_ERR(ns->dentry[IMAFS_DENTRY_SYMLINK]))
goto out;
- binary_runtime_measurements =
+ ns->dentry[IMAFS_DENTRY_BINARY_RUNTIME_MEASUREMENTS] =
securityfs_create_file("binary_runtime_measurements",
S_IRUSR | S_IRGRP, ima_dir, NULL,
&ima_measurements_ops);
- if (IS_ERR(binary_runtime_measurements))
+ if (IS_ERR(ns->dentry[IMAFS_DENTRY_BINARY_RUNTIME_MEASUREMENTS]))
goto out;
- ascii_runtime_measurements =
+ ns->dentry[IMAFS_DENTRY_ASCII_RUNTIME_MEASUREMENTS] =
securityfs_create_file("ascii_runtime_measurements",
S_IRUSR | S_IRGRP, ima_dir, NULL,
&ima_ascii_measurements_ops);
- if (IS_ERR(ascii_runtime_measurements))
+ if (IS_ERR(ns->dentry[IMAFS_DENTRY_ASCII_RUNTIME_MEASUREMENTS]))
goto out;
- runtime_measurements_count =
+ ns->dentry[IMAFS_DENTRY_RUNTIME_MEASUREMENTS_COUNT] =
securityfs_create_file("runtime_measurements_count",
S_IRUSR | S_IRGRP, ima_dir, NULL,
&ima_measurements_count_ops);
- if (IS_ERR(runtime_measurements_count))
+ if (IS_ERR(ns->dentry[IMAFS_DENTRY_RUNTIME_MEASUREMENTS_COUNT]))
goto out;
- violations =
+ ns->dentry[IMAFS_DENTRY_VIOLATIONS] =
securityfs_create_file("violations", S_IRUSR | S_IRGRP,
ima_dir, NULL, &ima_htable_violations_ops);
- if (IS_ERR(violations))
+ if (IS_ERR(ns->dentry[IMAFS_DENTRY_VIOLATIONS]))
goto out;
- ima_policy = securityfs_create_file("policy", POLICY_FILE_FLAGS,
+ ns->dentry[IMAFS_DENTRY_IMA_POLICY] =
+ securityfs_create_file("policy", POLICY_FILE_FLAGS,
ima_dir, NULL,
&ima_measure_policy_ops);
- if (IS_ERR(ima_policy))
+ if (IS_ERR(ns->dentry[IMAFS_DENTRY_IMA_POLICY]))
goto out;
return 0;
out:
- securityfs_remove(violations);
- securityfs_remove(runtime_measurements_count);
- securityfs_remove(ascii_runtime_measurements);
- securityfs_remove(binary_runtime_measurements);
- securityfs_remove(ima_symlink);
- securityfs_remove(ima_dir);
- securityfs_remove(ima_policy);
+ ima_fs_ns_free_dentries(ns);
return -1;
}
+
+int __init ima_fs_init(void)
+{
+ return ima_fs_ns_init(&init_user_ns);
+}
--
2.31.1
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.
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 <[email protected]>
Signed-off-by: James Bottomley <[email protected]>
---
include/linux/ima.h | 17 ++++++++++++++++-
security/inode.c | 12 +++++++++++-
security/integrity/ima/ima_fs.c | 33 ++++++++++++++++++++++++++-------
3 files changed, 53 insertions(+), 9 deletions(-)
diff --git a/include/linux/ima.h b/include/linux/ima.h
index bfb978a7f8d5..a8017272d78d 100644
--- a/include/linux/ima.h
+++ b/include/linux/ima.h
@@ -66,6 +66,10 @@ static inline const char * const *arch_get_ima_policy(void)
}
#endif
+extern int ima_fs_ns_init(struct user_namespace *user_ns,
+ struct dentry *root);
+extern void ima_fs_ns_free_dentries(struct user_namespace *user_ns);
+
#else
static inline enum hash_algo ima_get_current_hash_algo(void)
{
@@ -154,6 +158,15 @@ static inline int ima_measure_critical_data(const char *event_label,
return -ENOENT;
}
+static inline int ima_fs_ns_init(struct user_namespace *ns, struct dentry *root)
+{
+ return 0;
+}
+
+static inline void ima_fs_ns_free_dentries(struct user_namespace *user_ns)
+{
+}
+
#endif /* CONFIG_IMA */
#ifndef CONFIG_IMA_KEXEC
@@ -221,7 +234,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,
@@ -333,6 +347,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 121ac1874dde..10ee20917f42 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_fs_ns_init(ns, sb->s_root);
+
+ return error;
}
static int securityfs_get_tree(struct fs_context *fc)
@@ -69,6 +74,11 @@ static int securityfs_init_fs_context(struct fs_context *fc)
static void securityfs_kill_super(struct super_block *sb)
{
+ struct user_namespace *ns = sb->s_fs_info;
+
+ if (ns != &init_user_ns)
+ ima_fs_ns_free_dentries(ns);
+
kill_litter_super(sb);
}
diff --git a/security/integrity/ima/ima_fs.c b/security/integrity/ima/ima_fs.c
index e2cee1457aaa..ea366c8e68c6 100644
--- a/security/integrity/ima/ima_fs.c
+++ b/security/integrity/ima/ima_fs.c
@@ -447,8 +447,9 @@ static const struct file_operations ima_measure_policy_ops = {
.llseek = generic_file_llseek,
};
-static void ima_fs_ns_free_dentries(struct ima_namespace *ns)
+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 +458,36 @@ static void ima_fs_ns_free_dentries(struct ima_namespace *ns)
memset(ns->dentry, 0, sizeof(ns->dentry));
}
-static int __init ima_fs_ns_init(struct user_namespace *user_ns)
+int ima_fs_ns_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 +527,11 @@ static int __init ima_fs_ns_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_fs_ns_init(&init_user_ns);
+ return ima_fs_ns_init(&init_user_ns, NULL);
}
--
2.31.1
On Tue, Dec 07, 2021 at 03:21:22PM -0500, Stefan Berger wrote:
> To prepare for virtualization of SecurityFS, use simple_pin_fs and
> simpe_release_fs only when init_user_ns is active.
>
> Signed-off-by: Stefan Berger <[email protected]>
> Signed-off-by: James Bottomley <[email protected]>
> ---
> security/inode.c | 30 +++++++++++++++++++++---------
> 1 file changed, 21 insertions(+), 9 deletions(-)
>
> diff --git a/security/inode.c b/security/inode.c
> index 6c326939750d..1a720b2c566d 100644
> --- a/security/inode.c
> +++ b/security/inode.c
> @@ -21,9 +21,10 @@
> #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 *securityfs_mount;
> +static int securityfs_mount_count;
>
> static void securityfs_free_inode(struct inode *inode)
> {
> @@ -109,6 +110,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 +120,17 @@ 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, &securityfs_mount,
> + &securityfs_mount_count);
> + if (error)
> + return ERR_PTR(error);
> + }
>
> - if (!parent)
> - parent = mount->mnt_root;
> + if (!parent) {
> + if (ns == &init_user_ns)
> + parent = securityfs_mount->mnt_root;
Wouldn't you want an
else
return ERR_PTR(-EINVAL);
in here already?
> + }
>
> dir = d_inode(parent);
>
> @@ -168,7 +175,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(&securityfs_mount,
> + &securityfs_mount_count);
> return dentry;
> }
>
> @@ -294,6 +303,7 @@ EXPORT_SYMBOL_GPL(securityfs_create_symlink);
> */
> void securityfs_remove(struct dentry *dentry)
> {
> + struct user_namespace *ns = dentry->d_sb->s_user_ns;
> struct inode *dir;
>
> if (!dentry || IS_ERR(dentry))
> @@ -309,7 +319,9 @@ void securityfs_remove(struct dentry *dentry)
> dput(dentry);
> }
> inode_unlock(dir);
> - simple_release_fs(&mount, &mount_count);
> + if (ns == &init_user_ns)
> + simple_release_fs(&securityfs_mount,
> + &securityfs_mount_count);
> }
> EXPORT_SYMBOL_GPL(securityfs_remove);
>
> --
> 2.31.1
>
>
On Tue, Dec 07, 2021 at 03:21:21PM -0500, Stefan Berger wrote:
> Implement hierarchical processing of file accesses in IMA namespaces by
> walking the list of IMA namespaces towards the init_ima_ns. This way
> file accesses can be audited in an IMA namespace and also be evaluated
> against the IMA policies of parent IMA namespaces.
>
> Signed-off-by: Stefan Berger <[email protected]>
> ---
> security/integrity/ima/ima_main.c | 29 +++++++++++++++++++++++++----
> 1 file changed, 25 insertions(+), 4 deletions(-)
>
> diff --git a/security/integrity/ima/ima_main.c b/security/integrity/ima/ima_main.c
> index 2121a831f38a..e9fa46eedd27 100644
> --- a/security/integrity/ima/ima_main.c
> +++ b/security/integrity/ima/ima_main.c
> @@ -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,
Hm, it's much more common to use double underscores then single
underscores to
__process_measurement()
reads a lot more natural to people perusing kernel code quite often.
> + 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;
> @@ -405,6 +405,27 @@ 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)
> +{
> + int ret = 0;
> + struct user_namespace *user_ns;
> +
> + do {
> + ret = _process_measurement(ns, file, cred, secid, buf, size, mask, func);
> + if (ret)
> + break;
> + user_ns = ns->user_ns->parent;
> + if (!user_ns)
> + break;
> + ns = user_ns->ima_ns;
> + } while (1);
I'd rather write this as:
struct user_namespace *user_ns = ns->user_ns;
while (user_ns) {
ns = user_ns->ima_ns;
ret = __process_measurement(ns, file, cred, secid, buf, size, mask, func);
if (ret)
break;
user_ns = user_ns->parent;
}
because the hierarchy is only an implicit property inherited by ima
namespaces from the implementation of user namespaces. In other words,
we're only indirectly walking a hierarchy of ima namespaces because
we're walking a hierarchy of user namespaces. So the ima ns actually
just gives us the entrypoint into the userns hierarchy which the double
deref writing it with a while() makes obvious.
But that's just how I'd conceptualize it. This you should do however you
prefer.
On Wed, Dec 08, 2021 at 01:09:54PM +0100, Christian Brauner wrote:
> On Tue, Dec 07, 2021 at 03:21:21PM -0500, Stefan Berger wrote:
> > Implement hierarchical processing of file accesses in IMA namespaces by
> > walking the list of IMA namespaces towards the init_ima_ns. This way
> > file accesses can be audited in an IMA namespace and also be evaluated
> > against the IMA policies of parent IMA namespaces.
> >
> > Signed-off-by: Stefan Berger <[email protected]>
> > ---
> > security/integrity/ima/ima_main.c | 29 +++++++++++++++++++++++++----
> > 1 file changed, 25 insertions(+), 4 deletions(-)
> >
> > diff --git a/security/integrity/ima/ima_main.c b/security/integrity/ima/ima_main.c
> > index 2121a831f38a..e9fa46eedd27 100644
> > --- a/security/integrity/ima/ima_main.c
> > +++ b/security/integrity/ima/ima_main.c
> > @@ -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,
>
> Hm, it's much more common to use double underscores then single
> underscores to
>
> __process_measurement()
>
> reads a lot more natural to people perusing kernel code quite often.
>
> > + 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;
> > @@ -405,6 +405,27 @@ 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)
> > +{
> > + int ret = 0;
> > + struct user_namespace *user_ns;
> > +
> > + do {
> > + ret = _process_measurement(ns, file, cred, secid, buf, size, mask, func);
> > + if (ret)
> > + break;
> > + user_ns = ns->user_ns->parent;
> > + if (!user_ns)
> > + break;
> > + ns = user_ns->ima_ns;
> > + } while (1);
>
> I'd rather write this as:
>
> struct user_namespace *user_ns = ns->user_ns;
>
> while (user_ns) {
> ns = user_ns->ima_ns;
>
> ret = __process_measurement(ns, file, cred, secid, buf, size, mask, func);
> if (ret)
> break;
> user_ns = user_ns->parent;
>
> }
>
> because the hierarchy is only an implicit property inherited by ima
> namespaces from the implementation of user namespaces. In other words,
> we're only indirectly walking a hierarchy of ima namespaces because
> we're walking a hierarchy of user namespaces. So the ima ns actually
> just gives us the entrypoint into the userns hierarchy which the double
> deref writing it with a while() makes obvious.
Which brings me to another point.
Technically nothing seems to prevent an ima_ns to survive the
destruction of its associated userns in ima_ns->user_ns?
One thread does get_ima_ns() and mucks around with it while another one
does put_user_ns().
Assume it's the last reference to the userns which is now -
asynchronously - cleaned up from ->work. So at some point you're ending
with a dangling pointer in ima_ns->user_ns eventually causing a UAF.
If I'm thinking correct than you need to fix this. I can think of two
ways right now where one of them I'm not sure how well that would work:
1. ima_ns takes a reference count to userns at creation. Here you need
to make very sure that you're not ending up with reference counting
cycles where the two structs keep each other alive.
2. rcu trickery. That's the one I'm not sure how well that would work
where you'd need rcu_read_lock()/rcu_read_unlock() with a
get_user_ns() in the middle whenever you're trying to get a ref to
the userns from an ima_ns and handle the case where the userns is
gone.
Or maybe I'me missing something in the patch series that makes this all
a non-issue.
On Tue, Dec 07, 2021 at 03:21:25PM -0500, Stefan Berger wrote:
> Use mac_admin_ns_capable() to check corresponding capability to allow
> read/write IMA policy without CAP_SYS_ADMIN but with CAP_MAC_ADMIN.
>
> Signed-off-by: Denis Semakin <[email protected]>
> Signed-off-by: Stefan Berger <[email protected]>
> ---
> include/linux/capability.h | 6 ++++++
> security/integrity/ima/ima_fs.c | 2 +-
> 2 files changed, 7 insertions(+), 1 deletion(-)
>
> 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/security/integrity/ima/ima_fs.c b/security/integrity/ima/ima_fs.c
> index 0e582ceecc7f..a749a3e79304 100644
> --- a/security/integrity/ima/ima_fs.c
> +++ b/security/integrity/ima/ima_fs.c
> @@ -394,7 +394,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(ns->user_ns))
> return -EPERM;
Hm, couldn't this rather just be:
if (ns_capable(ns, CAP_MAC_ADMIN) || capable(CAP_SYS_ADMIN))
so we don't carry CAP_SYS_ADMIN as an alternative way for ima into user
namespaces as well? This way containers don't need to drop CAP_SYS_ADMIN
just to prevent mac policy from being altered. But that's more on the
LSM side of questions.
On Tue, Dec 07, 2021 at 03:21:22PM -0500, Stefan Berger wrote:
> To prepare for virtualization of SecurityFS, use simple_pin_fs and
> simpe_release_fs only when init_user_ns is active.
>
> Signed-off-by: Stefan Berger <[email protected]>
> Signed-off-by: James Bottomley <[email protected]>
> ---
> security/inode.c | 30 +++++++++++++++++++++---------
> 1 file changed, 21 insertions(+), 9 deletions(-)
>
> diff --git a/security/inode.c b/security/inode.c
> index 6c326939750d..1a720b2c566d 100644
> --- a/security/inode.c
> +++ b/security/inode.c
> @@ -21,9 +21,10 @@
> #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 *securityfs_mount;
> +static int securityfs_mount_count;
Maybe better:
static struct vfsmount *init_securityfs_mount;
static int init_securityfs_mount_count;
gets a bit long but gets the meaning across better plus it's a global so
not really an issue imho if it's long.
Otherwise, looks good.
Acked-by: Christian Brauner <[email protected]>
On Tue, Dec 07, 2021 at 03:21:27PM -0500, Stefan Berger wrote:
> 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.
>
> 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 <[email protected]>
> Signed-off-by: James Bottomley <[email protected]>
> ---
> include/linux/ima.h | 17 ++++++++++++++++-
> security/inode.c | 12 +++++++++++-
> security/integrity/ima/ima_fs.c | 33 ++++++++++++++++++++++++++-------
> 3 files changed, 53 insertions(+), 9 deletions(-)
>
> diff --git a/include/linux/ima.h b/include/linux/ima.h
> index bfb978a7f8d5..a8017272d78d 100644
> --- a/include/linux/ima.h
> +++ b/include/linux/ima.h
> @@ -66,6 +66,10 @@ static inline const char * const *arch_get_ima_policy(void)
> }
> #endif
>
> +extern int ima_fs_ns_init(struct user_namespace *user_ns,
> + struct dentry *root);
> +extern void ima_fs_ns_free_dentries(struct user_namespace *user_ns);
> +
> #else
> static inline enum hash_algo ima_get_current_hash_algo(void)
> {
> @@ -154,6 +158,15 @@ static inline int ima_measure_critical_data(const char *event_label,
> return -ENOENT;
> }
>
> +static inline int ima_fs_ns_init(struct user_namespace *ns, struct dentry *root)
> +{
> + return 0;
> +}
> +
> +static inline void ima_fs_ns_free_dentries(struct user_namespace *user_ns)
> +{
> +}
> +
> #endif /* CONFIG_IMA */
>
> #ifndef CONFIG_IMA_KEXEC
> @@ -221,7 +234,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,
> @@ -333,6 +347,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 121ac1874dde..10ee20917f42 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_fs_ns_init(ns, sb->s_root);
> +
> + return error;
> }
>
> static int securityfs_get_tree(struct fs_context *fc)
> @@ -69,6 +74,11 @@ static int securityfs_init_fs_context(struct fs_context *fc)
>
> static void securityfs_kill_super(struct super_block *sb)
> {
> + struct user_namespace *ns = sb->s_fs_info;
> +
> + if (ns != &init_user_ns)
> + ima_fs_ns_free_dentries(ns);
Say securityfs is unmounted. Then all the inodes and dentries become
invalid. It's not allowed to hold on to any dentries or inodes after the
super_block is shut down. So I just want to be sure that nothing in ima
can access these dentries after securityfs is unmounted.
To put it another way: why are they stored in struct ima_namespace in
the first place? If you don't pin a filesystem when creating files or
directories like you do for securityfs in init_ima_ns then you don't
need to hold on to them as they will be automatically be wiped during
umount.
On Wed, Dec 08, 2021 at 01:58:14PM +0100, Christian Brauner wrote:
> On Tue, Dec 07, 2021 at 03:21:27PM -0500, Stefan Berger wrote:
> > 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.
> >
> > 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 <[email protected]>
> > Signed-off-by: James Bottomley <[email protected]>
> > ---
> > include/linux/ima.h | 17 ++++++++++++++++-
> > security/inode.c | 12 +++++++++++-
> > security/integrity/ima/ima_fs.c | 33 ++++++++++++++++++++++++++-------
> > 3 files changed, 53 insertions(+), 9 deletions(-)
> >
> > diff --git a/include/linux/ima.h b/include/linux/ima.h
> > index bfb978a7f8d5..a8017272d78d 100644
> > --- a/include/linux/ima.h
> > +++ b/include/linux/ima.h
> > @@ -66,6 +66,10 @@ static inline const char * const *arch_get_ima_policy(void)
> > }
> > #endif
> >
> > +extern int ima_fs_ns_init(struct user_namespace *user_ns,
> > + struct dentry *root);
> > +extern void ima_fs_ns_free_dentries(struct user_namespace *user_ns);
> > +
> > #else
> > static inline enum hash_algo ima_get_current_hash_algo(void)
> > {
> > @@ -154,6 +158,15 @@ static inline int ima_measure_critical_data(const char *event_label,
> > return -ENOENT;
> > }
> >
> > +static inline int ima_fs_ns_init(struct user_namespace *ns, struct dentry *root)
> > +{
> > + return 0;
> > +}
> > +
> > +static inline void ima_fs_ns_free_dentries(struct user_namespace *user_ns)
> > +{
> > +}
> > +
> > #endif /* CONFIG_IMA */
> >
> > #ifndef CONFIG_IMA_KEXEC
> > @@ -221,7 +234,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,
> > @@ -333,6 +347,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 121ac1874dde..10ee20917f42 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_fs_ns_init(ns, sb->s_root);
> > +
> > + return error;
> > }
> >
> > static int securityfs_get_tree(struct fs_context *fc)
> > @@ -69,6 +74,11 @@ static int securityfs_init_fs_context(struct fs_context *fc)
> >
> > static void securityfs_kill_super(struct super_block *sb)
> > {
> > + struct user_namespace *ns = sb->s_fs_info;
> > +
> > + if (ns != &init_user_ns)
> > + ima_fs_ns_free_dentries(ns);
>
> Say securityfs is unmounted. Then all the inodes and dentries become
> invalid. It's not allowed to hold on to any dentries or inodes after the
> super_block is shut down. So I just want to be sure that nothing in ima
> can access these dentries after securityfs is unmounted.
>
> To put it another way: why are they stored in struct ima_namespace in
> the first place? If you don't pin a filesystem when creating files or
> directories like you do for securityfs in init_ima_ns then you don't
> need to hold on to them as they will be automatically be wiped during
> umount.
The way I see it you need to do the following:
If securityfs is mounted in a userns and fill_super is called you need
to call
int ima_fs_ns_init(struct user_namespace *user_ns,
(which you really should call ima_securitfs_init()...)
and when you create those entries for non-init-securityfs you just need
sm like:
struct dentry *dentry;
/* XXXX useless comment XXXX:
* The lookup_one_len() function will always return with an
* increased refcount on the dentry that you need to release.
*/
dentry = lookup_one_len(name, parent, strlen(name));
if (IS_ERR(dentry))
return dentry;
/* Return error if the file/dir already exists. */
if (d_really_is_positive(dentry)) {
/*
* XXXX useless comment XXXX:
* Put the reference from lookup_one_len()
*/
dput(dentry);
return ERR_PTR(-EEXIST);
}
inode = new_inode(dir->i_sb);
if (!inode) {
error = -ENOMEM;
goto out1;
}
// DO A LOT OF OTHER STUFF
d_instantiate(dentry, new_inode);
// DON'T CALL dget() again
The point is to not increase the refcount again like
securityfs_create_dentry() does after d_instantiate which requires you
to call securityfs_remove(). That's unnecessary for the
non-init_user_ns-securityfs case and then you don't need all that
cleanup stuff in kill_super() and can just rely on d_genocide() and the
dcache shrinker to do all the required work.
Don't hold on to objects that can go away beneath you in any structs.
Stashing them in ima_namespace will just make people think that these
things can be accessed without any lifetime concerns which is imho an
invitation to disaster in the long run.
On 12/8/21 06:58, Christian Brauner wrote:
> On Tue, Dec 07, 2021 at 03:21:22PM -0500, Stefan Berger wrote:
>> To prepare for virtualization of SecurityFS, use simple_pin_fs and
>> simpe_release_fs only when init_user_ns is active.
>>
>> Signed-off-by: Stefan Berger <[email protected]>
>> Signed-off-by: James Bottomley <[email protected]>
>> ---
>> security/inode.c | 30 +++++++++++++++++++++---------
>> 1 file changed, 21 insertions(+), 9 deletions(-)
>>
>> diff --git a/security/inode.c b/security/inode.c
>> index 6c326939750d..1a720b2c566d 100644
>> --- a/security/inode.c
>> +++ b/security/inode.c
>> @@ -21,9 +21,10 @@
>> #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 *securityfs_mount;
>> +static int securityfs_mount_count;
>>
>> static void securityfs_free_inode(struct inode *inode)
>> {
>> @@ -109,6 +110,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 +120,17 @@ 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, &securityfs_mount,
>> + &securityfs_mount_count);
>> + if (error)
>> + return ERR_PTR(error);
>> + }
>>
>> - if (!parent)
>> - parent = mount->mnt_root;
>> + if (!parent) {
>> + if (ns == &init_user_ns)
>> + parent = securityfs_mount->mnt_root;
> Wouldn't you want an
>
> else
> return ERR_PTR(-EINVAL);
>
> in here already?
Fixed.
On Wed, 2021-12-08 at 13:58 +0100, Christian Brauner wrote:
> On Tue, Dec 07, 2021 at 03:21:27PM -0500, Stefan Berger wrote:
[...]
> > @@ -69,6 +74,11 @@ static int securityfs_init_fs_context(struct
> > fs_context *fc)
> >
> > static void securityfs_kill_super(struct super_block *sb)
> > {
> > + struct user_namespace *ns = sb->s_fs_info;
> > +
> > + if (ns != &init_user_ns)
> > + ima_fs_ns_free_dentries(ns);
>
> Say securityfs is unmounted. Then all the inodes and dentries become
> invalid. It's not allowed to hold on to any dentries or inodes after
> the super_block is shut down. So I just want to be sure that nothing
> in ima can access these dentries after securityfs is unmounted.
>
> To put it another way: why are they stored in struct ima_namespace in
> the first place? If you don't pin a filesystem when creating files or
> directories like you do for securityfs in init_ima_ns then you don't
> need to hold on to them as they will be automatically be wiped during
> umount.
For IMA this is true because IMA can't be a module. However, a modular
consumer, like the TPM, must be able to remove its entries from a
mounted securityfs because the code that serves the operations is going
away. In order to do this removal, it needs the dentries somewhere.
The current convention seems to be everything has a directory in the
top level, so we could call d_genocide() on this directory and not have
to worry about storing the dentries underneath, but I think we can't
avoid storing the dentry for the top level directory.
James
On Wed, Dec 08, 2021 at 09:11:09AM -0500, James Bottomley wrote:
> On Wed, 2021-12-08 at 13:58 +0100, Christian Brauner wrote:
> > On Tue, Dec 07, 2021 at 03:21:27PM -0500, Stefan Berger wrote:
> [...]
> > > @@ -69,6 +74,11 @@ static int securityfs_init_fs_context(struct
> > > fs_context *fc)
> > >
> > > static void securityfs_kill_super(struct super_block *sb)
> > > {
> > > + struct user_namespace *ns = sb->s_fs_info;
> > > +
> > > + if (ns != &init_user_ns)
> > > + ima_fs_ns_free_dentries(ns);
> >
> > Say securityfs is unmounted. Then all the inodes and dentries become
> > invalid. It's not allowed to hold on to any dentries or inodes after
> > the super_block is shut down. So I just want to be sure that nothing
> > in ima can access these dentries after securityfs is unmounted.
> >
> > To put it another way: why are they stored in struct ima_namespace in
> > the first place? If you don't pin a filesystem when creating files or
> > directories like you do for securityfs in init_ima_ns then you don't
> > need to hold on to them as they will be automatically be wiped during
> > umount.
>
> For IMA this is true because IMA can't be a module. However, a modular
This thread is about ima and its stashing of dentries in struct
ima_namespace. That things might be different for other consumers is
uninteresting for this specific case, I think.
> consumer, like the TPM, must be able to remove its entries from a
> mounted securityfs because the code that serves the operations is going
> away. In order to do this removal, it needs the dentries somewhere.
That still doesn't require you to take an additional reference on the
dentry per se.
Aside from this brings in a whole different and way bigger issue as that
requires way more fundamental work since this is about a (pseudo or
proper) device. It's not even clear that this should have entries
outside of init_user_ns-securityfs.
> The current convention seems to be everything has a directory in the
> top level, so we could call d_genocide() on this directory and not have
> to worry about storing the dentries underneath, but I think we can't
> avoid storing the dentry for the top level directory.
I have not heard an argument why ima needs to stash these dentries as it
doesn't remove them once created until umount.
On Wed, 2021-12-08 at 15:46 +0100, Christian Brauner wrote:
> On Wed, Dec 08, 2021 at 09:11:09AM -0500, James Bottomley wrote:
> > On Wed, 2021-12-08 at 13:58 +0100, Christian Brauner wrote:
> > > On Tue, Dec 07, 2021 at 03:21:27PM -0500, Stefan Berger wrote:
> > [...]
> > > > @@ -69,6 +74,11 @@ static int securityfs_init_fs_context(struct
> > > > fs_context *fc)
> > > >
> > > > static void securityfs_kill_super(struct super_block *sb)
> > > > {
> > > > + struct user_namespace *ns = sb->s_fs_info;
> > > > +
> > > > + if (ns != &init_user_ns)
> > > > + ima_fs_ns_free_dentries(ns);
> > >
> > > Say securityfs is unmounted. Then all the inodes and dentries
> > > become invalid. It's not allowed to hold on to any dentries or
> > > inodes after the super_block is shut down. So I just want to be
> > > sure that nothing in ima can access these dentries after
> > > securityfs is unmounted.
> > >
> > > To put it another way: why are they stored in struct
> > > ima_namespace in the first place? If you don't pin a filesystem
> > > when creating files or directories like you do for securityfs in
> > > init_ima_ns then you don't need to hold on to them as they will
> > > be automatically be wiped during umount.
> >
> > For IMA this is true because IMA can't be a module. However, a
> > modular
>
> This thread is about ima and its stashing of dentries in struct
> ima_namespace. That things might be different for other consumers is
> uninteresting for this specific case, I think.
Well, yes, but the patch series also includes namespacing securityfs.
We have to get that right for all consumers, including the modular
ones. So I think the way it works is we don't need a remove callback
in kill_sb() if we don't raise the dentry refcount in create. However,
we still need to return the dentry to allow for stashing and we still
need to be able to cope with remove being called for the namespaced
entries ... for teardown on error in the IMA case and module
removal+teardown on error in other cases.
> > consumer, like the TPM, must be able to remove its entries from a
> > mounted securityfs because the code that serves the operations is
> > going away. In order to do this removal, it needs the dentries
> > somewhere.
>
> That still doesn't require you to take an additional reference on the
> dentry per se.
No, I don't believe it does ... however it does require a stash
somewhere.
> Aside from this brings in a whole different and way bigger issue as
> that requires way more fundamental work since this is about a (pseudo
> or proper) device. It's not even clear that this should have entries
> outside of init_user_ns-securityfs.
adding vTPMs is a use case that people want to support, so I don't want
to get to that bit and find it all doesn't work.
> > The current convention seems to be everything has a directory in
> > the top level, so we could call d_genocide() on this directory and
> > not have to worry about storing the dentries underneath, but I
> > think we can't avoid storing the dentry for the top level
> > directory.
>
> I have not heard an argument why ima needs to stash these dentries as
> it doesn't remove them once created until umount.
I'm not saying IMA does, I'm just saying we still need the dentry
returned by the API in case a consumer does need to stash.
James
On Wed, Dec 08, 2021 at 10:04:55AM -0500, James Bottomley wrote:
> On Wed, 2021-12-08 at 15:46 +0100, Christian Brauner wrote:
> > On Wed, Dec 08, 2021 at 09:11:09AM -0500, James Bottomley wrote:
> > > On Wed, 2021-12-08 at 13:58 +0100, Christian Brauner wrote:
> > > > On Tue, Dec 07, 2021 at 03:21:27PM -0500, Stefan Berger wrote:
> > > [...]
> > > > > @@ -69,6 +74,11 @@ static int securityfs_init_fs_context(struct
> > > > > fs_context *fc)
> > > > >
> > > > > static void securityfs_kill_super(struct super_block *sb)
> > > > > {
> > > > > + struct user_namespace *ns = sb->s_fs_info;
> > > > > +
> > > > > + if (ns != &init_user_ns)
> > > > > + ima_fs_ns_free_dentries(ns);
> > > >
> > > > Say securityfs is unmounted. Then all the inodes and dentries
> > > > become invalid. It's not allowed to hold on to any dentries or
> > > > inodes after the super_block is shut down. So I just want to be
> > > > sure that nothing in ima can access these dentries after
> > > > securityfs is unmounted.
> > > >
> > > > To put it another way: why are they stored in struct
> > > > ima_namespace in the first place? If you don't pin a filesystem
> > > > when creating files or directories like you do for securityfs in
> > > > init_ima_ns then you don't need to hold on to them as they will
> > > > be automatically be wiped during umount.
> > >
> > > For IMA this is true because IMA can't be a module. However, a
> > > modular
> >
> > This thread is about ima and its stashing of dentries in struct
> > ima_namespace. That things might be different for other consumers is
> > uninteresting for this specific case, I think.
>
> Well, yes, but the patch series also includes namespacing securityfs.
> We have to get that right for all consumers, including the modular
> ones. So I think the way it works is we don't need a remove callback
> in kill_sb() if we don't raise the dentry refcount in create. However,
> we still need to return the dentry to allow for stashing and we still
> need to be able to cope with remove being called for the namespaced
> entries ... for teardown on error in the IMA case and module
> removal+teardown on error in other cases.
This is a two-way street. Securityfs namespacing places requirements on
the callers as well. I won't bend generic vfs infrastucture to our will
because some users want to remove dentries at random points. It is on
the users to make sure that they don't cause UAFs when securityfs is
umounted. And that isn't that hard to do. You just need to guard removal
in .kill_sb() with a lock against a concurrent securityfs_remove() call
that some piece of code might want to issue and make sure that any
stashed stuff is properly invalidated.
The point is that we don't need all this right now since we only have
ima as user. I did not say that it cannot be done I said we don't need
to do it for ima. So I feel discussing this point further is deterring
the patches more than it helps them.
On 12/8/21 07:58, Christian Brauner wrote:
> On Tue, Dec 07, 2021 at 03:21:27PM -0500, Stefan Berger wrote:
>>
>> #if defined(CONFIG_IMA_APPRAISE) && defined(CONFIG_INTEGRITY_TRUSTED_KEYRING)
>> diff --git a/security/inode.c b/security/inode.c
>> index 121ac1874dde..10ee20917f42 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_fs_ns_init(ns, sb->s_root);
>> +
>> + return error;
>> }
>>
>> static int securityfs_get_tree(struct fs_context *fc)
>> @@ -69,6 +74,11 @@ static int securityfs_init_fs_context(struct fs_context *fc)
>>
>> static void securityfs_kill_super(struct super_block *sb)
>> {
>> + struct user_namespace *ns = sb->s_fs_info;
>> +
>> + if (ns != &init_user_ns)
>> + ima_fs_ns_free_dentries(ns);
> Say securityfs is unmounted. Then all the inodes and dentries become
> invalid. It's not allowed to hold on to any dentries or inodes after the
> super_block is shut down. So I just want to be sure that nothing in ima
> can access these dentries after securityfs is unmounted.
> To put it another way: why are they stored in struct ima_namespace in
> the first place? If you don't pin a filesystem when creating files or
> directories like you do for securityfs in init_ima_ns then you don't
> need to hold on to them as they will be automatically be wiped during
> umount.
The reason was so that securityfs for init_ima_ns and IMA namespaces
could share the code assigning to dentries to keep around and can clean
up if an error occurs while creating a dentry.
What about this: We keep the dentries in the ima_namespace, modify the
code creating the dentries in securityfs_create_dentry() to only take
the additional reference in case of init_user_ns (I suppose this is what
you suggest) and then keep 'static void ima_fs_ns_free_dentries()' only
for removing the dentries for the error case and never call it from
securityfs_kill_super()? Would that be acceptable?
On Wed, Dec 08, 2021 at 10:39:48AM -0500, Stefan Berger wrote:
>
> On 12/8/21 07:58, Christian Brauner wrote:
> > On Tue, Dec 07, 2021 at 03:21:27PM -0500, Stefan Berger wrote:
> > > #if defined(CONFIG_IMA_APPRAISE) && defined(CONFIG_INTEGRITY_TRUSTED_KEYRING)
> > > diff --git a/security/inode.c b/security/inode.c
> > > index 121ac1874dde..10ee20917f42 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_fs_ns_init(ns, sb->s_root);
> > > +
> > > + return error;
> > > }
> > > static int securityfs_get_tree(struct fs_context *fc)
> > > @@ -69,6 +74,11 @@ static int securityfs_init_fs_context(struct fs_context *fc)
> > > static void securityfs_kill_super(struct super_block *sb)
> > > {
> > > + struct user_namespace *ns = sb->s_fs_info;
> > > +
> > > + if (ns != &init_user_ns)
> > > + ima_fs_ns_free_dentries(ns);
> > Say securityfs is unmounted. Then all the inodes and dentries become
> > invalid. It's not allowed to hold on to any dentries or inodes after the
> > super_block is shut down. So I just want to be sure that nothing in ima
> > can access these dentries after securityfs is unmounted.
>
> > To put it another way: why are they stored in struct ima_namespace in
> > the first place? If you don't pin a filesystem when creating files or
> > directories like you do for securityfs in init_ima_ns then you don't
> > need to hold on to them as they will be automatically be wiped during
> > umount.
>
>
> The reason was so that securityfs for init_ima_ns and IMA namespaces could
> share the code assigning to dentries to keep around and can clean up if an
> error occurs while creating a dentry.
>
> What about this: We keep the dentries in the ima_namespace, modify the code
> creating the dentries in securityfs_create_dentry() to only take the
> additional reference in case of init_user_ns (I suppose this is what you
> suggest) and then keep 'static void ima_fs_ns_free_dentries()' only for
> removing the dentries for the error case and never call it from
> securityfs_kill_super()? Would that be acceptable?
If you create a range of dentries in fill_super post sb->s_root is
properly allocated and you fail in the middle you can simply return from
fill_super without bothering to clean them up as the vfs will
automatically clean those up when the dcache shrinker runs (If you've
increased the refcount as these functions do currently you need to
decrease it of course.).
On 12/8/21 07:23, Christian Brauner wrote:
> On Wed, Dec 08, 2021 at 01:09:54PM +0100, Christian Brauner wrote:
>> On Tue, Dec 07, 2021 at 03:21:21PM -0500, Stefan Berger wrote:
>>> Implement hierarchical processing of file accesses in IMA namespaces by
>>> walking the list of IMA namespaces towards the init_ima_ns. This way
>>> file accesses can be audited in an IMA namespace and also be evaluated
>>> against the IMA policies of parent IMA namespaces.
>>>
>>> Signed-off-by: Stefan Berger <[email protected]>
>>> ---
>>> security/integrity/ima/ima_main.c | 29 +++++++++++++++++++++++++----
>>> 1 file changed, 25 insertions(+), 4 deletions(-)
>>>
>>> diff --git a/security/integrity/ima/ima_main.c b/security/integrity/ima/ima_main.c
>>> index 2121a831f38a..e9fa46eedd27 100644
>>> --- a/security/integrity/ima/ima_main.c
>>> +++ b/security/integrity/ima/ima_main.c
>>> @@ -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,
>> Hm, it's much more common to use double underscores then single
>> underscores to
>>
>> __process_measurement()
>>
>> reads a lot more natural to people perusing kernel code quite often.
>>
>>> + 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;
>>> @@ -405,6 +405,27 @@ 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)
>>> +{
>>> + int ret = 0;
>>> + struct user_namespace *user_ns;
>>> +
>>> + do {
>>> + ret = _process_measurement(ns, file, cred, secid, buf, size, mask, func);
>>> + if (ret)
>>> + break;
>>> + user_ns = ns->user_ns->parent;
>>> + if (!user_ns)
>>> + break;
>>> + ns = user_ns->ima_ns;
>>> + } while (1);
>> I'd rather write this as:
>>
>> struct user_namespace *user_ns = ns->user_ns;
>>
>> while (user_ns) {
>> ns = user_ns->ima_ns;
>>
>> ret = __process_measurement(ns, file, cred, secid, buf, size, mask, func);
>> if (ret)
>> break;
>> user_ns = user_ns->parent;
>>
>> }
>>
>> because the hierarchy is only an implicit property inherited by ima
>> namespaces from the implementation of user namespaces. In other words,
>> we're only indirectly walking a hierarchy of ima namespaces because
>> we're walking a hierarchy of user namespaces. So the ima ns actually
>> just gives us the entrypoint into the userns hierarchy which the double
>> deref writing it with a while() makes obvious.
> Which brings me to another point.
>
> Technically nothing seems to prevent an ima_ns to survive the
> destruction of its associated userns in ima_ns->user_ns?
>
> One thread does get_ima_ns() and mucks around with it while another one
> does put_user_ns().
>
> Assume it's the last reference to the userns which is now -
> asynchronously - cleaned up from ->work. So at some point you're ending
> with a dangling pointer in ima_ns->user_ns eventually causing a UAF.
>
> If I'm thinking correct than you need to fix this. I can think of two
> ways right now where one of them I'm not sure how well that would work:
> 1. ima_ns takes a reference count to userns at creation. Here you need
> to make very sure that you're not ending up with reference counting
> cycles where the two structs keep each other alive.
Right. I am not sure what the trigger would be for ima_ns to release
that one reference.
> 2. rcu trickery. That's the one I'm not sure how well that would work
> where you'd need rcu_read_lock()/rcu_read_unlock() with a
> get_user_ns() in the middle whenever you're trying to get a ref to
> the userns from an ima_ns and handle the case where the userns is
> gone.
>
> Or maybe I'me missing something in the patch series that makes this all
> a non-issue.
I suppose one can always call current_user_ns() to get a pointer to the
current user namespace that the process is accessing the file in that
IMA now reacts to. With the hierarchical processing we are walking
backwards towards init_user_ns. The problem should only exist if
something else frees the current user namespace (or its parents) so that
the hierarchy collapses. Assuming we are always in a process context
then 'current' should protect us, no ?
On 12/8/21 11:50, Stefan Berger wrote:
>
> On 12/8/21 07:23, Christian Brauner wrote:
>> On Wed, Dec 08, 2021 at 01:09:54PM +0100, Christian Brauner wrote:
>>> On Tue, Dec 07, 2021 at 03:21:21PM -0500, Stefan Berger wrote:
>>>> Implement hierarchical processing of file accesses in IMA
>>>> namespaces by
>>>> walking the list of IMA namespaces towards the init_ima_ns. This way
>>>> file accesses can be audited in an IMA namespace and also be evaluated
>>>> against the IMA policies of parent IMA namespaces.
>>>>
>>>> Signed-off-by: Stefan Berger <[email protected]>
>>>> ---
>>>> security/integrity/ima/ima_main.c | 29 +++++++++++++++++++++++++----
>>>> 1 file changed, 25 insertions(+), 4 deletions(-)
>>>>
>>>> diff --git a/security/integrity/ima/ima_main.c
>>>> b/security/integrity/ima/ima_main.c
>>>> index 2121a831f38a..e9fa46eedd27 100644
>>>> --- a/security/integrity/ima/ima_main.c
>>>> +++ b/security/integrity/ima/ima_main.c
>>>> @@ -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,
>>> Hm, it's much more common to use double underscores then single
>>> underscores to
>>>
>>> __process_measurement()
>>>
>>> reads a lot more natural to people perusing kernel code quite often.
>>>
>>>> + 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;
>>>> @@ -405,6 +405,27 @@ 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)
>>>> +{
>>>> + int ret = 0;
>>>> + struct user_namespace *user_ns;
>>>> +
>>>> + do {
>>>> + ret = _process_measurement(ns, file, cred, secid, buf,
>>>> size, mask, func);
>>>> + if (ret)
>>>> + break;
>>>> + user_ns = ns->user_ns->parent;
>>>> + if (!user_ns)
>>>> + break;
>>>> + ns = user_ns->ima_ns;
>>>> + } while (1);
>>> I'd rather write this as:
>>>
>>> struct user_namespace *user_ns = ns->user_ns;
>>>
>>> while (user_ns) {
>>> ns = user_ns->ima_ns;
>>>
>>> ret = __process_measurement(ns, file, cred, secid, buf,
>>> size, mask, func);
>>> if (ret)
>>> break;
>>> user_ns = user_ns->parent;
>>>
>>> }
>>>
>>> because the hierarchy is only an implicit property inherited by ima
>>> namespaces from the implementation of user namespaces. In other words,
>>> we're only indirectly walking a hierarchy of ima namespaces because
>>> we're walking a hierarchy of user namespaces. So the ima ns actually
>>> just gives us the entrypoint into the userns hierarchy which the double
>>> deref writing it with a while() makes obvious.
>> Which brings me to another point.
>>
>> Technically nothing seems to prevent an ima_ns to survive the
>> destruction of its associated userns in ima_ns->user_ns?
>>
>> One thread does get_ima_ns() and mucks around with it while another one
>> does put_user_ns().
>>
>> Assume it's the last reference to the userns which is now -
>> asynchronously - cleaned up from ->work. So at some point you're ending
>> with a dangling pointer in ima_ns->user_ns eventually causing a UAF.
>>
>> If I'm thinking correct than you need to fix this. I can think of two
>> ways right now where one of them I'm not sure how well that would work:
>> 1. ima_ns takes a reference count to userns at creation. Here you need
>> to make very sure that you're not ending up with reference counting
>> cycles where the two structs keep each other alive.
>
> Right. I am not sure what the trigger would be for ima_ns to release
> that one reference.
>
>
>> 2. rcu trickery. That's the one I'm not sure how well that would work
>> where you'd need rcu_read_lock()/rcu_read_unlock() with a
>> get_user_ns() in the middle whenever you're trying to get a ref to
>> the userns from an ima_ns and handle the case where the userns is
>> gone.
>>
>> Or maybe I'me missing something in the patch series that makes this all
>> a non-issue.
>
> I suppose one can always call current_user_ns() to get a pointer to
> the current user namespace that the process is accessing the file in
> that IMA now reacts to. With the hierarchical processing we are
> walking backwards towards init_user_ns. The problem should only exist
> if something else frees the current user namespace (or its parents) so
> that the hierarchy collapses. Assuming we are always in a process
> context then 'current' should protect us, no ?
>
All existing callers to process_measurements call it at least once with
current_cred().
The only problem that I see where we are accessing the IMA namespace
outside a process context is in 4/16 'ima: Move delayed work queue and
variables into ima_namespace' where a delayed work queue is used. I
fixed this now by getting an additional reference to the user namesapce
before scheduling the delayed work and release it when it ran or when it
is canceled (cancel_delayed_work_sync()) but it didn't run.
On Tue, 2021-12-07 at 15:21 -0500, Stefan Berger wrote:
> To prepare for virtualization of SecurityFS, use simple_pin_fs and
> simpe_release_fs only when init_user_ns is active.
>
> Signed-off-by: Stefan Berger <[email protected]>
> Signed-off-by: James Bottomley <[email protected]>
What do you mean by virtualization, and how does this prepare
securityfs for it? The commit message should be way more verbose.
/Jarkko
On Sat, 2021-12-11 at 16:16 +0200, Jarkko Sakkinen wrote:
> On Tue, 2021-12-07 at 15:21 -0500, Stefan Berger wrote:
> > To prepare for virtualization of SecurityFS, use simple_pin_fs and
> > simpe_release_fs only when init_user_ns is active.
> >
> > Signed-off-by: Stefan Berger <[email protected]>
> > Signed-off-by: James Bottomley <
> > [email protected]>
>
> What do you mean by virtualization, and how does this prepare
> securityfs for it? The commit message should be way more verbose.
Heh, well cart before horse: we're still trying to work out how to do
it correctly, so we can't really document it until we've figured that
bit out.
Once that's all sorted, the output is likely something in
Documentation/ explaining how to namespace a pseudo filesystem (since
we have quite a few of them in the kernel) rather than a commit message
which will get hard to find the next time someone wants to do this.
James
On Wed, 2021-12-08 at 13:22 -0500, Stefan Berger wrote:
> On 12/8/21 11:50, Stefan Berger wrote:
> >
> > On 12/8/21 07:23, Christian Brauner wrote:
> >> On Wed, Dec 08, 2021 at 01:09:54PM +0100, Christian Brauner wrote:
> >>> On Tue, Dec 07, 2021 at 03:21:21PM -0500, Stefan Berger wrote:
> >>>> Implement hierarchical processing of file accesses in IMA
> >>>> namespaces by
> >>>> walking the list of IMA namespaces towards the init_ima_ns. This way
> >>>> file accesses can be audited in an IMA namespace and also be evaluated
> >>>> against the IMA policies of parent IMA namespaces.
> >>>>
> >>>> Signed-off-by: Stefan Berger <[email protected]>
> >>>> ---
> >>>> security/integrity/ima/ima_main.c | 29 +++++++++++++++++++++++++----
> >>>> 1 file changed, 25 insertions(+), 4 deletions(-)
> >>>>
> >>>> diff --git a/security/integrity/ima/ima_main.c
> >>>> b/security/integrity/ima/ima_main.c
> >>>> index 2121a831f38a..e9fa46eedd27 100644
> >>>> --- a/security/integrity/ima/ima_main.c
> >>>> +++ b/security/integrity/ima/ima_main.c
> >>>> @@ -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,
> >>> Hm, it's much more common to use double underscores then single
> >>> underscores to
> >>>
> >>> __process_measurement()
> >>>
> >>> reads a lot more natural to people perusing kernel code quite often.
> >>>
> >>>> + 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;
> >>>> @@ -405,6 +405,27 @@ 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)
> >>>> +{
> >>>> + int ret = 0;
> >>>> + struct user_namespace *user_ns;
> >>>> +
> >>>> + do {
> >>>> + ret = _process_measurement(ns, file, cred, secid, buf,
> >>>> size, mask, func);
> >>>> + if (ret)
> >>>> + break;
> >>>> + user_ns = ns->user_ns->parent;
> >>>> + if (!user_ns)
> >>>> + break;
> >>>> + ns = user_ns->ima_ns;
> >>>> + } while (1);
> >>> I'd rather write this as:
> >>>
> >>> struct user_namespace *user_ns = ns->user_ns;
> >>>
> >>> while (user_ns) {
> >>> ns = user_ns->ima_ns;
> >>>
> >>> ret = __process_measurement(ns, file, cred, secid, buf,
> >>> size, mask, func);
> >>> if (ret)
> >>> break;
> >>> user_ns = user_ns->parent;
> >>>
> >>> }
> >>>
> >>> because the hierarchy is only an implicit property inherited by ima
> >>> namespaces from the implementation of user namespaces. In other words,
> >>> we're only indirectly walking a hierarchy of ima namespaces because
> >>> we're walking a hierarchy of user namespaces. So the ima ns actually
> >>> just gives us the entrypoint into the userns hierarchy which the double
> >>> deref writing it with a while() makes obvious.
> >> Which brings me to another point.
> >>
> >> Technically nothing seems to prevent an ima_ns to survive the
> >> destruction of its associated userns in ima_ns->user_ns?
> >>
> >> One thread does get_ima_ns() and mucks around with it while another one
> >> does put_user_ns().
> >>
> >> Assume it's the last reference to the userns which is now -
> >> asynchronously - cleaned up from ->work. So at some point you're ending
> >> with a dangling pointer in ima_ns->user_ns eventually causing a UAF.
> >>
> >> If I'm thinking correct than you need to fix this. I can think of two
> >> ways right now where one of them I'm not sure how well that would work:
> >> 1. ima_ns takes a reference count to userns at creation. Here you need
> >> to make very sure that you're not ending up with reference counting
> >> cycles where the two structs keep each other alive.
> >
> > Right. I am not sure what the trigger would be for ima_ns to release
> > that one reference.
> >
> >
> >> 2. rcu trickery. That's the one I'm not sure how well that would work
> >> where you'd need rcu_read_lock()/rcu_read_unlock() with a
> >> get_user_ns() in the middle whenever you're trying to get a ref to
> >> the userns from an ima_ns and handle the case where the userns is
> >> gone.
> >>
> >> Or maybe I'me missing something in the patch series that makes this all
> >> a non-issue.
> >
> > I suppose one can always call current_user_ns() to get a pointer to
> > the current user namespace that the process is accessing the file in
> > that IMA now reacts to. With the hierarchical processing we are
> > walking backwards towards init_user_ns. The problem should only exist
> > if something else frees the current user namespace (or its parents) so
> > that the hierarchy collapses. Assuming we are always in a process
> > context then 'current' should protect us, no ?
> >
> All existing callers to process_measurements call it at least once with
> current_cred().
>
> The only problem that I see where we are accessing the IMA namespace
> outside a process context is in 4/16 'ima: Move delayed work queue and
> variables into ima_namespace' where a delayed work queue is used. I
> fixed this now by getting an additional reference to the user namesapce
> before scheduling the delayed work and release it when it ran or when it
> is canceled (cancel_delayed_work_sync()) but it didn't run.
>
From the "ima: Move delayed work queue and variables into
ima_namespace" patch description:
Since keys queued up for measurement currently are only relevant in
the init_ima_ns, call ima_init_key_queue() only when the init_ima_ns
is initialized.
When IMA_QUEUE_EARLY_BOOT_KEYS is not enabled, ima_should_queue_key()
simply returns false. Why do the keys workqueue need to be namespaced?
Is this preparatory for some future IMA namespacing?
thanks,
Mimi
On 12/15/21 18:04, Mimi Zohar wrote:
> On Wed, 2021-12-08 at 13:22 -0500, Stefan Berger wrote:
>> On 12/8/21 11:50, Stefan Berger wrote:
>>> On 12/8/21 07:23, Christian Brauner wrote:
>>>> On Wed, Dec 08, 2021 at 01:09:54PM +0100, Christian Brauner wrote:
>>>>> On Tue, Dec 07, 2021 at 03:21:21PM -0500, Stefan Berger wrote:
>>>>>> Implement hierarchical processing of file accesses in IMA
>>>>>> namespaces by
>>>>>> walking the list of IMA namespaces towards the init_ima_ns. This way
>>>>>> file accesses can be audited in an IMA namespace and also be evaluated
>>>>>> against the IMA policies of parent IMA namespaces.
>>>>>>
>>>>>> Signed-off-by: Stefan Berger <[email protected]>
>>>>>> ---
>>>>>> security/integrity/ima/ima_main.c | 29 +++++++++++++++++++++++++----
>>>>>> 1 file changed, 25 insertions(+), 4 deletions(-)
>>>>>>
>>>>>> diff --git a/security/integrity/ima/ima_main.c
>>>>>> b/security/integrity/ima/ima_main.c
>>>>>> index 2121a831f38a..e9fa46eedd27 100644
>>>>>> --- a/security/integrity/ima/ima_main.c
>>>>>> +++ b/security/integrity/ima/ima_main.c
>>>>>> @@ -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,
>>>>> Hm, it's much more common to use double underscores then single
>>>>> underscores to
>>>>>
>>>>> __process_measurement()
>>>>>
>>>>> reads a lot more natural to people perusing kernel code quite often.
>>>>>
>>>>>> + 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;
>>>>>> @@ -405,6 +405,27 @@ 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)
>>>>>> +{
>>>>>> + int ret = 0;
>>>>>> + struct user_namespace *user_ns;
>>>>>> +
>>>>>> + do {
>>>>>> + ret = _process_measurement(ns, file, cred, secid, buf,
>>>>>> size, mask, func);
>>>>>> + if (ret)
>>>>>> + break;
>>>>>> + user_ns = ns->user_ns->parent;
>>>>>> + if (!user_ns)
>>>>>> + break;
>>>>>> + ns = user_ns->ima_ns;
>>>>>> + } while (1);
>>>>> I'd rather write this as:
>>>>>
>>>>> struct user_namespace *user_ns = ns->user_ns;
>>>>>
>>>>> while (user_ns) {
>>>>> ns = user_ns->ima_ns;
>>>>>
>>>>> ret = __process_measurement(ns, file, cred, secid, buf,
>>>>> size, mask, func);
>>>>> if (ret)
>>>>> break;
>>>>> user_ns = user_ns->parent;
>>>>>
>>>>> }
>>>>>
>>>>> because the hierarchy is only an implicit property inherited by ima
>>>>> namespaces from the implementation of user namespaces. In other words,
>>>>> we're only indirectly walking a hierarchy of ima namespaces because
>>>>> we're walking a hierarchy of user namespaces. So the ima ns actually
>>>>> just gives us the entrypoint into the userns hierarchy which the double
>>>>> deref writing it with a while() makes obvious.
>>>> Which brings me to another point.
>>>>
>>>> Technically nothing seems to prevent an ima_ns to survive the
>>>> destruction of its associated userns in ima_ns->user_ns?
>>>>
>>>> One thread does get_ima_ns() and mucks around with it while another one
>>>> does put_user_ns().
>>>>
>>>> Assume it's the last reference to the userns which is now -
>>>> asynchronously - cleaned up from ->work. So at some point you're ending
>>>> with a dangling pointer in ima_ns->user_ns eventually causing a UAF.
>>>>
>>>> If I'm thinking correct than you need to fix this. I can think of two
>>>> ways right now where one of them I'm not sure how well that would work:
>>>> 1. ima_ns takes a reference count to userns at creation. Here you need
>>>> to make very sure that you're not ending up with reference counting
>>>> cycles where the two structs keep each other alive.
>>> Right. I am not sure what the trigger would be for ima_ns to release
>>> that one reference.
>>>
>>>
>>>> 2. rcu trickery. That's the one I'm not sure how well that would work
>>>> where you'd need rcu_read_lock()/rcu_read_unlock() with a
>>>> get_user_ns() in the middle whenever you're trying to get a ref to
>>>> the userns from an ima_ns and handle the case where the userns is
>>>> gone.
>>>>
>>>> Or maybe I'me missing something in the patch series that makes this all
>>>> a non-issue.
>>> I suppose one can always call current_user_ns() to get a pointer to
>>> the current user namespace that the process is accessing the file in
>>> that IMA now reacts to. With the hierarchical processing we are
>>> walking backwards towards init_user_ns. The problem should only exist
>>> if something else frees the current user namespace (or its parents) so
>>> that the hierarchy collapses. Assuming we are always in a process
>>> context then 'current' should protect us, no ?
>>>
>> All existing callers to process_measurements call it at least once with
>> current_cred().
>>
>> The only problem that I see where we are accessing the IMA namespace
>> outside a process context is in 4/16 'ima: Move delayed work queue and
>> variables into ima_namespace' where a delayed work queue is used. I
>> fixed this now by getting an additional reference to the user namesapce
>> before scheduling the delayed work and release it when it ran or when it
>> is canceled (cancel_delayed_work_sync()) but it didn't run.
>>
> From the "ima: Move delayed work queue and variables into
> ima_namespace" patch description:
> Since keys queued up for measurement currently are only relevant in
> the init_ima_ns, call ima_init_key_queue() only when the init_ima_ns
> is initialized.
>
> When IMA_QUEUE_EARLY_BOOT_KEYS is not enabled, ima_should_queue_key()
> simply returns false. Why do the keys workqueue need to be namespaced?
> Is this preparatory for some future IMA namespacing?
06 ima: Move policy related variables into ima_namespace
05 ima: Move IMA's keys queue related variables into ima_namespace
04 ima: Move delayed work queue and variables into ima_namespace
06 requires the ima_namespace parameter to be passed into
process_buffer_measurement(). The problem was ima_process_queued_keys()
that needs to pass the namespace but it's probably sufficient to use
&init_ima_ns there as the ima_namespace parameter, which would allow to
drop 05 and 04.
Stefan
>
> thanks,
>
> Mimi
>