All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v8 00/19] ima: Namespace IMA with audit support in IMA-ns
@ 2022-01-04 17:03 Stefan Berger
  2022-01-04 17:03 ` [PATCH v8 01/19] securityfs: Extend securityfs with namespacing support Stefan Berger
                   ` (18 more replies)
  0 siblings, 19 replies; 50+ messages in thread
From: Stefan Berger @ 2022-01-04 17:03 UTC (permalink / raw)
  To: linux-integrity
  Cc: zohar, serge, christian.brauner, containers, dmitry.kasatkin,
	ebiederm, krzysztof.struczynski, roberto.sassu, mpeters, lhinds,
	lsturman, puiterwi, jejb, jamjoom, linux-kernel, paul, rgb,
	linux-security-module, jmorris, Stefan Berger

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

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 is created when a user namespace is
created, although this is done late when SecurityFS is mounted inside
a user namespace. The advantage of piggy backing on the user namespace
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
cp /sbin/busybox rootfs/bin/busybox2
echo >> rootfs/bin/busybox2
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; \
  busybox2 cat /mnt/ima/policy"

[busybox2 is used to demonstrate 2 audit messages; see below]

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 'busybox2 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 fetch https://github.com/stefanberger/linux-ima-namespaces v5.16-rc8+imans.v8.posted

Regards,
   Stefan

Links to previous postings:
v1: https://lore.kernel.org/linux-integrity/20211130160654.1418231-1-stefanb@linux.ibm.com/T/#t
v2: https://lore.kernel.org/linux-integrity/20211203023118.1447229-1-stefanb@linux.ibm.com/T/#t
v3: https://lore.kernel.org/linux-integrity/6240b686-89cf-2e31-1c1b-ebdcf1e972c1@linux.ibm.com/T/#t
v4: https://lore.kernel.org/linux-integrity/20211207202127.1508689-1-stefanb@linux.ibm.com/T/#t
v5: https://lore.kernel.org/linux-integrity/20211208221818.1519628-1-stefanb@linux.ibm.com/T/#t
v6: https://lore.kernel.org/linux-integrity/20211210194736.1538863-1-stefanb@linux.ibm.com/T/#t
v7: https://lore.kernel.org/linux-integrity/20211217100659.2iah5prshavjk6v6@wittgenstein/T/#t

v8:
 - Rearranged patches to support lazy creation of IMA namespaces
 - Fixed issue related to re-auditing of a modified file. This required the
   introduction of ns_status structure connected to list starting on an iint
 - Fixed issue related to display of uid and gid in IMA policy to show uid
   and gid values relative to the user namespace
 - Handling of error code during hierarchical processing

v7:
 - Dropped 2 patches related to key queues; using &init_ima_ns for all calls
   from functions related to key queues where calls need ima_namespace
 - Moved ima_namespace to security/integrity/ima/ima.h
 - Extended API descriptions with ns parameter where needed
 - Using init_ima_ns in functions related to appraisal and xattrs
 - SecurityFS: Using ima_ns_from_file() to get ns pointer 
 - Reformatted to 80 columns per line

v6:
 - Removed kref and pointer to user_ns in ima_namespace (patch 1)
 - Moved only the policy file dentry into ima_namespace; other dentries are on
   stack now and can be discarded
 - Merged James's patch simplifying securityfs_remove and dropping dget()
 - Added patch with Christian's suggestion to tie opened SecurityFS file to
   the user/IMA namespace it belongs to
 - Passing missing ima_namespace parameter in functions in ima_kexec.c (ppc64)
 - Reverted v5's change to patch 4 related to protection of ima_namespace

v5:
 - Followed Christian's suggestions on patch 1. Also, reverted increased reference
   counter on init_user_ns since ima_ns doesn't take reference to its user_ns.
 - No addtional reference is taken on securityfs dentries for user_ns != init_user_ns.
   Updated documentation and removed cleanup of dentries on superblock kill.
   (patches 12 & 16)
 - Moved else branch to earlier patch (patch 11)
 - Protect ima_namespace by taking reference on user namespace for delayed work queue.
   (patch 4)

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):
  integrity/ima: Define ns_status for storing namespaced iint data
  ima: Namespace audit status flags

Stefan Berger (17):
  securityfs: Extend securityfs with namespacing support
  ima: Define ima_namespace structure and implement basic functions
  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: Move some IMA policy and filesystem related variables into
    ima_namespace
  ima: Move dentry into ima_namespace and others onto stack
  ima: Use mac_admin_ns_capable() to check corresponding capability
  ima: Only accept AUDIT rules for non-init_ima_ns namespaces for now
  ima: Implement hierarchical processing of file accesses
  ima: Implement ima_free_policy_rules() for freeing of an ima_namespace
  userns: Add pointer to ima_namespace to user_namespace
  ima: Add functions for creation and freeing of an ima_namespace
  ima: Enable re-auditing of modified files
  ima: Setup securityfs for IMA namespace
  ima: Show owning user namespace's uid and gid when displaying policy
  ima: Enable IMA namespaces

 include/linux/capability.h                   |   6 +
 include/linux/ima.h                          |  44 +++
 include/linux/user_namespace.h               |   4 +
 init/Kconfig                                 |  13 +
 kernel/user.c                                |   4 +
 kernel/user_namespace.c                      |   2 +
 security/inode.c                             |  81 ++++-
 security/integrity/iint.c                    |  13 +
 security/integrity/ima/Makefile              |   3 +-
 security/integrity/ima/ima.h                 | 215 ++++++++++--
 security/integrity/ima/ima_api.c             |  34 +-
 security/integrity/ima/ima_appraise.c        |  28 +-
 security/integrity/ima/ima_asymmetric_keys.c |   4 +-
 security/integrity/ima/ima_fs.c              | 147 +++++---
 security/integrity/ima/ima_init.c            |  19 +-
 security/integrity/ima/ima_init_ima_ns.c     |  47 +++
 security/integrity/ima/ima_kexec.c           |  15 +-
 security/integrity/ima/ima_main.c            | 212 +++++++++---
 security/integrity/ima/ima_ns.c              |  83 +++++
 security/integrity/ima/ima_ns_status.c       | 335 +++++++++++++++++++
 security/integrity/ima/ima_policy.c          | 196 ++++++-----
 security/integrity/ima/ima_queue.c           |  61 ++--
 security/integrity/ima/ima_queue_keys.c      |  11 +-
 security/integrity/ima/ima_template.c        |   5 +-
 security/integrity/integrity.h               |   4 +
 25 files changed, 1288 insertions(+), 298 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


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

* [PATCH v8 01/19] securityfs: Extend securityfs with namespacing support
  2022-01-04 17:03 [PATCH v8 00/19] ima: Namespace IMA with audit support in IMA-ns Stefan Berger
@ 2022-01-04 17:03 ` Stefan Berger
  2022-01-05  3:58   ` Al Viro
  2022-01-04 17:03 ` [PATCH v8 02/19] ima: Define ima_namespace structure and implement basic functions Stefan Berger
                   ` (17 subsequent siblings)
  18 siblings, 1 reply; 50+ messages in thread
From: Stefan Berger @ 2022-01-04 17:03 UTC (permalink / raw)
  To: linux-integrity
  Cc: zohar, serge, christian.brauner, containers, dmitry.kasatkin,
	ebiederm, krzysztof.struczynski, roberto.sassu, mpeters, lhinds,
	lsturman, puiterwi, jejb, jamjoom, linux-kernel, paul, rgb,
	linux-security-module, jmorris, Stefan Berger, James Bottomley

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

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

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

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

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

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

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

diff --git a/security/inode.c b/security/inode.c
index 6c326939750d..e525ba960063 100644
--- a/security/inode.c
+++ b/security/inode.c
@@ -21,9 +21,37 @@
 #include <linux/security.h>
 #include <linux/lsm_hooks.h>
 #include <linux/magic.h>
+#include <linux/user_namespace.h>
 
-static struct vfsmount *mount;
-static int mount_count;
+static struct vfsmount *init_securityfs_mount;
+static int init_securityfs_mount_count;
+
+static int securityfs_permission(struct user_namespace *mnt_userns,
+				 struct inode *inode, int mask)
+{
+	int err;
+
+	err = generic_permission(&init_user_ns, inode, mask);
+	if (!err) {
+		/* Unless bind-mounted, deny access if current_user_ns() is not
+		 * ancestor.
+		 */
+		if (inode->i_sb->s_user_ns != &init_user_ns &&
+		    !in_userns(current_user_ns(), inode->i_sb->s_user_ns))
+			err = -EACCES;
+	}
+
+	return err;
+}
+
+static const struct inode_operations securityfs_dir_inode_operations = {
+	.permission	= securityfs_permission,
+	.lookup		= simple_lookup,
+};
+
+static const struct inode_operations securityfs_file_inode_operations = {
+	.permission	= securityfs_permission,
+};
 
 static void securityfs_free_inode(struct inode *inode)
 {
@@ -40,20 +68,25 @@ static const struct super_operations securityfs_super_operations = {
 static int securityfs_fill_super(struct super_block *sb, struct fs_context *fc)
 {
 	static const struct tree_descr files[] = {{""}};
+	struct user_namespace *ns = fc->user_ns;
 	int error;
 
+	if (WARN_ON(ns != current_user_ns()))
+		return -EINVAL;
+
 	error = simple_fill_super(sb, SECURITYFS_MAGIC, files);
 	if (error)
 		return error;
 
 	sb->s_op = &securityfs_super_operations;
+	sb->s_root->d_inode->i_op = &securityfs_dir_inode_operations;
 
 	return 0;
 }
 
 static int securityfs_get_tree(struct fs_context *fc)
 {
-	return get_tree_single(fc, securityfs_fill_super);
+	return get_tree_keyed(fc, securityfs_fill_super, fc->user_ns);
 }
 
 static const struct fs_context_operations securityfs_context_ops = {
@@ -71,6 +104,7 @@ static struct file_system_type fs_type = {
 	.name =		"securityfs",
 	.init_fs_context = securityfs_init_fs_context,
 	.kill_sb =	kill_litter_super,
+	.fs_flags =	FS_USERNS_MOUNT,
 };
 
 /**
@@ -109,6 +143,7 @@ static struct dentry *securityfs_create_dentry(const char *name, umode_t mode,
 					const struct file_operations *fops,
 					const struct inode_operations *iops)
 {
+	struct user_namespace *ns = current_user_ns();
 	struct dentry *dentry;
 	struct inode *dir, *inode;
 	int error;
@@ -118,12 +153,19 @@ static struct dentry *securityfs_create_dentry(const char *name, umode_t mode,
 
 	pr_debug("securityfs: creating file '%s'\n",name);
 
-	error = simple_pin_fs(&fs_type, &mount, &mount_count);
-	if (error)
-		return ERR_PTR(error);
+	if (ns == &init_user_ns) {
+		error = simple_pin_fs(&fs_type, &init_securityfs_mount,
+				      &init_securityfs_mount_count);
+		if (error)
+			return ERR_PTR(error);
+	}
 
-	if (!parent)
-		parent = mount->mnt_root;
+	if (!parent) {
+		if (ns == &init_user_ns)
+			parent = init_securityfs_mount->mnt_root;
+		else
+			return ERR_PTR(-EINVAL);
+	}
 
 	dir = d_inode(parent);
 
@@ -148,7 +190,7 @@ static struct dentry *securityfs_create_dentry(const char *name, umode_t mode,
 	inode->i_atime = inode->i_mtime = inode->i_ctime = current_time(inode);
 	inode->i_private = data;
 	if (S_ISDIR(mode)) {
-		inode->i_op = &simple_dir_inode_operations;
+		inode->i_op = &securityfs_dir_inode_operations;
 		inode->i_fop = &simple_dir_operations;
 		inc_nlink(inode);
 		inc_nlink(dir);
@@ -156,10 +198,10 @@ static struct dentry *securityfs_create_dentry(const char *name, umode_t mode,
 		inode->i_op = iops ? iops : &simple_symlink_inode_operations;
 		inode->i_link = data;
 	} else {
+		inode->i_op = &securityfs_file_inode_operations;
 		inode->i_fop = fops;
 	}
 	d_instantiate(dentry, inode);
-	dget(dentry);
 	inode_unlock(dir);
 	return dentry;
 
@@ -168,7 +210,9 @@ static struct dentry *securityfs_create_dentry(const char *name, umode_t mode,
 	dentry = ERR_PTR(error);
 out:
 	inode_unlock(dir);
-	simple_release_fs(&mount, &mount_count);
+	if (ns == &init_user_ns)
+		simple_release_fs(&init_securityfs_mount,
+				  &init_securityfs_mount_count);
 	return dentry;
 }
 
@@ -294,22 +338,29 @@ EXPORT_SYMBOL_GPL(securityfs_create_symlink);
  */
 void securityfs_remove(struct dentry *dentry)
 {
+	struct user_namespace *ns;
 	struct inode *dir;
 
 	if (!dentry || IS_ERR(dentry))
 		return;
 
+	ns = dentry->d_sb->s_user_ns;
+
 	dir = d_inode(dentry->d_parent);
 	inode_lock(dir);
 	if (simple_positive(dentry)) {
+		dget(dentry);
 		if (d_is_dir(dentry))
 			simple_rmdir(dir, dentry);
 		else
 			simple_unlink(dir, dentry);
+		d_delete(dentry);
 		dput(dentry);
 	}
 	inode_unlock(dir);
-	simple_release_fs(&mount, &mount_count);
+	if (ns == &init_user_ns)
+		simple_release_fs(&init_securityfs_mount,
+				  &init_securityfs_mount_count);
 }
 EXPORT_SYMBOL_GPL(securityfs_remove);
 
-- 
2.31.1


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

* [PATCH v8 02/19] ima: Define ima_namespace structure and implement basic functions
  2022-01-04 17:03 [PATCH v8 00/19] ima: Namespace IMA with audit support in IMA-ns Stefan Berger
  2022-01-04 17:03 ` [PATCH v8 01/19] securityfs: Extend securityfs with namespacing support Stefan Berger
@ 2022-01-04 17:03 ` Stefan Berger
  2022-01-04 17:04 ` [PATCH v8 03/19] ima: Move policy related variables into ima_namespace Stefan Berger
                   ` (16 subsequent siblings)
  18 siblings, 0 replies; 50+ messages in thread
From: Stefan Berger @ 2022-01-04 17:03 UTC (permalink / raw)
  To: linux-integrity
  Cc: zohar, serge, christian.brauner, containers, dmitry.kasatkin,
	ebiederm, krzysztof.struczynski, roberto.sassu, mpeters, lhinds,
	lsturman, puiterwi, jejb, jamjoom, linux-kernel, paul, rgb,
	linux-security-module, jmorris, Stefan Berger

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

Define the initial ima_namespace structure and the ima_namespace
variable init_ima_ns for the host's IMA namespace. Implement basic
functions for namespacing support.

For now let get_current_ns() always returns a pointer to init_ima_ns.

Signed-off-by: Stefan Berger <stefanb@linux.ibm.com>
---
 security/integrity/ima/Makefile          |  2 +-
 security/integrity/ima/ima.h             | 10 ++++++++++
 security/integrity/ima/ima_init.c        |  4 ++++
 security/integrity/ima/ima_init_ima_ns.c | 23 +++++++++++++++++++++++
 4 files changed, 38 insertions(+), 1 deletion(-)
 create mode 100644 security/integrity/ima/ima_init_ima_ns.c

diff --git a/security/integrity/ima/Makefile b/security/integrity/ima/Makefile
index 2499f2485c04..f8a5e5f3975d 100644
--- a/security/integrity/ima/Makefile
+++ b/security/integrity/ima/Makefile
@@ -7,7 +7,7 @@
 obj-$(CONFIG_IMA) += ima.o
 
 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_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_HAVE_IMA_KEXEC) += ima_kexec.o
diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h
index be965a8715e4..c4af3275f015 100644
--- a/security/integrity/ima/ima.h
+++ b/security/integrity/ima/ima.h
@@ -119,6 +119,10 @@ struct ima_kexec_hdr {
 	u64 count;
 };
 
+struct ima_namespace {
+} __randomize_layout;
+extern struct ima_namespace init_ima_ns;
+
 extern const int read_idmap[];
 
 #ifdef CONFIG_HAVE_IMA_KEXEC
@@ -136,6 +140,7 @@ extern bool ima_canonical_fmt;
 /* Internal IMA function definitions */
 int ima_init(void);
 int ima_fs_init(void);
+int ima_ns_init(void);
 int ima_add_template_entry(struct ima_template_entry *entry, int violation,
 			   const char *op, struct inode *inode,
 			   const unsigned char *filename);
@@ -450,4 +455,9 @@ static inline int ima_filter_rule_match(u32 secid, u32 field, u32 op,
 #define	POLICY_FILE_FLAGS	S_IWUSR
 #endif /* CONFIG_IMA_READ_POLICY */
 
+static inline struct ima_namespace *get_current_ns(void)
+{
+	return &init_ima_ns;
+}
+
 #endif /* __LINUX_IMA_H */
diff --git a/security/integrity/ima/ima_init.c b/security/integrity/ima/ima_init.c
index b26fa67476b4..f6ae4557a0da 100644
--- a/security/integrity/ima/ima_init.c
+++ b/security/integrity/ima/ima_init.c
@@ -120,6 +120,10 @@ int __init ima_init(void)
 {
 	int rc;
 
+	rc = ima_ns_init();
+	if (rc)
+		return rc;
+
 	ima_tpm_chip = tpm_default_chip();
 	if (!ima_tpm_chip)
 		pr_info("No TPM chip found, activating TPM-bypass!\n");
diff --git a/security/integrity/ima/ima_init_ima_ns.c b/security/integrity/ima/ima_init_ima_ns.c
new file mode 100644
index 000000000000..daa609892849
--- /dev/null
+++ b/security/integrity/ima/ima_init_ima_ns.c
@@ -0,0 +1,23 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2016-2021 IBM Corporation
+ * Author:
+ *   Yuqiong Sun <suny@us.ibm.com>
+ *   Stefan Berger <stefanb@linux.vnet.ibm.com>
+ */
+
+#include "ima.h"
+
+static int ima_init_namespace(struct ima_namespace *ns)
+{
+	return 0;
+}
+
+int __init ima_ns_init(void)
+{
+	return ima_init_namespace(&init_ima_ns);
+}
+
+struct ima_namespace init_ima_ns = {
+};
+EXPORT_SYMBOL(init_ima_ns);
-- 
2.31.1


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

* [PATCH v8 03/19] ima: Move policy related variables into ima_namespace
  2022-01-04 17:03 [PATCH v8 00/19] ima: Namespace IMA with audit support in IMA-ns Stefan Berger
  2022-01-04 17:03 ` [PATCH v8 01/19] securityfs: Extend securityfs with namespacing support Stefan Berger
  2022-01-04 17:03 ` [PATCH v8 02/19] ima: Define ima_namespace structure and implement basic functions Stefan Berger
@ 2022-01-04 17:04 ` Stefan Berger
  2022-01-13 20:26   ` Mimi Zohar
  2022-01-04 17:04 ` [PATCH v8 04/19] ima: Move ima_htable " Stefan Berger
                   ` (15 subsequent siblings)
  18 siblings, 1 reply; 50+ messages in thread
From: Stefan Berger @ 2022-01-04 17:04 UTC (permalink / raw)
  To: linux-integrity
  Cc: zohar, serge, christian.brauner, containers, dmitry.kasatkin,
	ebiederm, krzysztof.struczynski, roberto.sassu, mpeters, lhinds,
	lsturman, puiterwi, jejb, jamjoom, linux-kernel, paul, rgb,
	linux-security-module, jmorris, Stefan Berger

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

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_ns_from_file() to get the IMA namespace via the user
namespace of the SecurityFS superblock that a file belongs to.

To get the current ima_namespace use get_current_ns() when a function
that is related to a policy rule is called. In other cases where functions
are called due file attribute modifications, use init_ima_ns, since these
functions are related to IMA appraisal and changes to file attributes are
only relevant to the init_ima_ns until IMA namespaces also support IMA
appraisal. In ima_file_free() use init_ima_ns since in this case flags
related to file measurements may be affected, which is not supported in
IMA namespaces, yet.

Signed-off-by: Stefan Berger <stefanb@linux.ibm.com>
---
 security/integrity/ima/ima.h                 |  49 ++++---
 security/integrity/ima/ima_api.c             |   8 +-
 security/integrity/ima/ima_appraise.c        |  28 ++--
 security/integrity/ima/ima_asymmetric_keys.c |   4 +-
 security/integrity/ima/ima_fs.c              |  16 ++-
 security/integrity/ima/ima_init.c            |   8 +-
 security/integrity/ima/ima_init_ima_ns.c     |   6 +
 security/integrity/ima/ima_main.c            |  83 +++++++----
 security/integrity/ima/ima_policy.c          | 142 ++++++++++---------
 security/integrity/ima/ima_queue_keys.c      |  11 +-
 10 files changed, 213 insertions(+), 142 deletions(-)

diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h
index c4af3275f015..0b3dc9425076 100644
--- a/security/integrity/ima/ima.h
+++ b/security/integrity/ima/ima.h
@@ -20,6 +20,7 @@
 #include <linux/hash.h>
 #include <linux/tpm.h>
 #include <linux/audit.h>
+#include <linux/user_namespace.h>
 #include <crypto/hash_info.h>
 
 #include "../integrity.h"
@@ -43,9 +44,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;
 
@@ -120,6 +118,14 @@ struct ima_kexec_hdr {
 };
 
 struct ima_namespace {
+	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;
 } __randomize_layout;
 extern struct ima_namespace init_ima_ns;
 
@@ -248,18 +254,19 @@ void ima_init_key_queue(void);
 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);
 #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 */
-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,
@@ -273,7 +280,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,
@@ -290,17 +298,18 @@ 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_update_policy(void);
-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_init_policy(struct ima_namespace *ns);
+void ima_update_policy(struct ima_namespace *ns);
+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_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);
@@ -316,14 +325,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,
@@ -334,7 +345,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;
@@ -351,7 +363,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 a64fb0130b01..2df0d8549c13 100644
--- a/security/integrity/ima/ima_api.c
+++ b/security/integrity/ima/ima_api.c
@@ -162,6 +162,7 @@ void ima_add_violation(struct file *file, const unsigned char *filename,
 
 /**
  * ima_get_action - appraise & measure decision based on policy.
+ * @ns: IMA namespace that has the policy
  * @mnt_userns:	user namespace of the mount the inode was found from
  * @inode: pointer to the inode associated with the object being validated
  * @cred: pointer to credentials structure to validate
@@ -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..3461025f671b 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,8 @@ 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 +530,16 @@ void ima_inode_post_setattr(struct user_namespace *mnt_userns,
 			    struct dentry *dentry)
 {
 	struct inode *inode = d_backing_inode(dentry);
+	struct ima_namespace *ns = &init_ima_ns;
 	struct 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 +564,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 +647,7 @@ int ima_inode_setxattr(struct dentry *dentry, const char *xattr_name,
 		       const void *xattr_value, size_t xattr_value_len)
 {
 	const struct evm_ima_xattr_data *xvalue = xattr_value;
+	struct ima_namespace *ns = &init_ima_ns;
 	int digsig = 0;
 	int result;
 
@@ -658,18 +665,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 = &init_ima_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 f6aa0b47a772..bc95f9ae4214 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. */
@@ -60,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 3d8e9d5db5aa..b7f379cfcb74 100644
--- a/security/integrity/ima/ima_fs.c
+++ b/security/integrity/ima/ima_fs.c
@@ -271,7 +271,7 @@ static const struct file_operations ima_ascii_measurements_ops = {
 	.release = seq_release,
 };
 
-static ssize_t ima_read_policy(char *path)
+static ssize_t ima_read_policy(struct ima_namespace *ns, char *path)
 {
 	void *data = NULL;
 	char *datap;
@@ -296,7 +296,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;
@@ -314,6 +314,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 = &init_ima_ns;
 	char *data;
 	ssize_t result;
 
@@ -336,7 +337,7 @@ static ssize_t ima_write_policy(struct file *file, const char __user *buf,
 		goto out_free;
 
 	if (data[0] == '/') {
-		result = ima_read_policy(data);
+		result = ima_read_policy(ns, data);
 	} else if (ima_appraise & IMA_APPRAISE_POLICY) {
 		pr_err("signed policy file (specified as an absolute pathname) required\n");
 		integrity_audit_msg(AUDIT_INTEGRITY_STATUS, NULL, NULL,
@@ -344,7 +345,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:
@@ -410,11 +411,12 @@ 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 = &init_ima_ns;
 
 	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;
 	}
@@ -424,13 +426,13 @@ 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;
 	}
 
-	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..d6b829c360d7 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 daa609892849..f04cc5e46668 100644
--- a/security/integrity/ima/ima_init_ima_ns.c
+++ b/security/integrity/ima/ima_init_ima_ns.c
@@ -10,6 +10,12 @@
 
 static int ima_init_namespace(struct ima_namespace *ns)
 {
+	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 465865412100..19f564f8e255 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 = &init_ima_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)
 {
@@ -217,18 +219,18 @@ static int process_measurement(struct file *file, const struct cred *cred,
 	enum hash_algo hash_algo;
 	unsigned int allowed_algos = 0;
 
-	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;
 
@@ -346,7 +348,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,
@@ -405,12 +407,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;
@@ -430,6 +433,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];
@@ -442,13 +446,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);
 
@@ -484,17 +488,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);
 }
 
@@ -510,21 +515,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);
@@ -574,10 +581,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);
 
@@ -601,10 +610,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);
 
@@ -620,13 +631,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;
@@ -652,14 +664,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;
@@ -688,6 +701,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;
 
@@ -710,7 +724,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);
 }
 
@@ -738,6 +752,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;
 
@@ -753,7 +768,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);
 }
 
@@ -841,6 +856,7 @@ int ima_post_load_data(char *buf, loff_t size,
 
 /**
  * process_buffer_measurement - Measure the buffer or the buffer data hash
+ * @ns: IMA namespace that has the policy
  * @mnt_userns:	user namespace of the mount the inode was found from
  * @inode: inode associated with the object being measured (NULL for KEY_CHECK)
  * @buf: pointer to the buffer that needs to be added to the log.
@@ -859,7 +875,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,
@@ -887,7 +904,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();
@@ -906,7 +923,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)
@@ -943,7 +960,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);
@@ -977,6 +994,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)
@@ -986,7 +1004,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);
@@ -1016,10 +1035,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);
@@ -1052,7 +1073,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_policy.c b/security/integrity/ima/ima_policy.c
index 320ca80aacab..66d005be3577 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 = &init_ima_ns;
+
 	if (event != LSM_POLICY_CHANGE)
 		return NOTIFY_DONE;
 
-	ima_lsm_update_rules();
+	ima_lsm_update_rules(ns);
 	return NOTIFY_OK;
 }
 
@@ -669,6 +665,7 @@ static int get_subaction(struct ima_rule_entry *rule, enum ima_hooks func)
 
 /**
  * ima_match_policy - decision based on LSM and other conditions
+ * @ns: IMA namespace that has the policy
  * @mnt_userns:	user namespace of the mount the inode was found from
  * @inode: pointer to an inode for which the policy decision is being made
  * @cred: pointer to a credentials structure for which the policy decision is
@@ -688,7 +685,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 +700,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))
@@ -746,8 +744,8 @@ int ima_match_policy(struct user_namespace *mnt_userns, struct inode *inode,
 }
 
 /**
- * ima_update_policy_flags() - Update global IMA variables
- *
+ * ima_update_policy_flags() - Update namespaced IMA variables
+ * @ns: IMA namespace that has the policy
  * Update ima_policy_flag and ima_setxattr_allowed_hash_algorithms
  * based on the currently loaded policy.
  *
@@ -760,14 +758,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 +795,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 +811,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 +821,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 +829,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 +842,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 +873,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);
@@ -888,26 +888,27 @@ static int __init ima_init_arch_policy(void)
 
 /**
  * ima_init_policy - initialize the default measure rules.
- *
+ * @ns: IMA namespace to which the policy belongs to
  * 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 +922,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 +934,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,39 +946,41 @@ 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;
 }
 
 /**
  * ima_update_policy - update default_rules with new measure rules
- *
+ * @ns: IMA namespace that has the policy
  * Called on file .release to update the default rules with a complete new
  * policy.  What we do here is to splice ima_policy_rules and ima_temp_rules so
  * they make a queue.  The policy may be updated multiple times and this is the
@@ -986,16 +989,17 @@ 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;
+	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,10 +1008,10 @@ void ima_update_policy(void)
 		 */
 		kfree(arch_policy_entry);
 	}
-	ima_update_policy_flags();
+	ima_update_policy_flags(ns);
 
 	/* Custom IMA policy has been loaded */
-	ima_process_queued_keys();
+	ima_process_queued_keys(ns);
 }
 
 /* Keep the enumeration in sync with the policy_tokens! */
@@ -1077,7 +1081,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 +1102,8 @@ 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 +1330,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 +1681,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;
@@ -1805,12 +1812,13 @@ static int ima_parse_rule(char *rule, struct ima_rule_entry *entry)
 
 /**
  * ima_parse_add_rule - add a rule to ima_policy_rules
+ * @ns: IMA namespace that has the policy
  * @rule - ima measurement policy rule
  *
  * 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 +1842,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,23 +1851,24 @@ 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;
 }
 
 /**
- * ima_delete_rules() called to cleanup invalid in-flight policy.
+ * ima_delete_rules - called to cleanup invalid in-flight policy.
+ * @ns: IMA namespace that has the policy
  * We don't need locking as we operate on the temp list, which is
  * 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);
 	}
@@ -1885,12 +1894,13 @@ static const char *const mask_tokens[] = {
 
 void *ima_policy_start(struct seq_file *m, loff_t *pos)
 {
+	struct ima_namespace *ns = &init_ima_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();
@@ -1903,6 +1913,7 @@ void *ima_policy_start(struct seq_file *m, loff_t *pos)
 
 void *ima_policy_next(struct seq_file *m, void *v, loff_t *pos)
 {
+	struct ima_namespace *ns = &init_ima_ns;
 	struct ima_rule_entry *entry = v;
 
 	rcu_read_lock();
@@ -1910,8 +1921,8 @@ void *ima_policy_next(struct seq_file *m, void *v, loff_t *pos)
 	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 +2177,7 @@ int ima_policy_show(struct seq_file *m, void *v)
  */
 bool ima_appraise_signature(enum kernel_read_file_id id)
 {
+	struct ima_namespace *ns = &init_ima_ns;
 	struct ima_rule_entry *entry;
 	bool found = false;
 	enum ima_hooks func;
@@ -2177,7 +2189,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 93056c03bf5a..e366a21dd8be 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"
 
@@ -42,7 +43,7 @@ static bool timer_expired;
 static void ima_keys_handler(struct work_struct *work)
 {
 	timer_expired = true;
-	ima_process_queued_keys();
+	ima_process_queued_keys(&init_ima_ns);
 }
 
 /*
@@ -130,11 +131,15 @@ 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;
 
+	/* only applies to init_ima_ns */
+	if (ns != &init_ima_ns)
+		return;
+
 	if (ima_process_keys)
 		return;
 
@@ -159,7 +164,7 @@ void ima_process_queued_keys(void)
 
 	list_for_each_entry_safe(entry, tmp, &ima_keys, list) {
 		if (!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


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

* [PATCH v8 04/19] ima: Move ima_htable into ima_namespace
  2022-01-04 17:03 [PATCH v8 00/19] ima: Namespace IMA with audit support in IMA-ns Stefan Berger
                   ` (2 preceding siblings ...)
  2022-01-04 17:04 ` [PATCH v8 03/19] ima: Move policy related variables into ima_namespace Stefan Berger
@ 2022-01-04 17:04 ` Stefan Berger
  2022-01-04 17:04 ` [PATCH v8 05/19] ima: Move measurement list related variables " Stefan Berger
                   ` (14 subsequent siblings)
  18 siblings, 0 replies; 50+ messages in thread
From: Stefan Berger @ 2022-01-04 17:04 UTC (permalink / raw)
  To: linux-integrity
  Cc: zohar, serge, christian.brauner, containers, dmitry.kasatkin,
	ebiederm, krzysztof.struczynski, roberto.sassu, mpeters, lhinds,
	lsturman, puiterwi, jejb, jamjoom, linux-kernel, paul, rgb,
	linux-security-module, jmorris, Stefan Berger

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

Move ima_htable into ima_namespace. This way a front-end like
SecurityFS can show the number of violations of an IMA namespace.

Signed-off-by: Stefan Berger <stefanb@linux.ibm.com>
---
 security/integrity/ima/ima.h             | 33 ++++++++++++---------
 security/integrity/ima/ima_api.c         | 18 +++++++-----
 security/integrity/ima/ima_fs.c          |  8 +++--
 security/integrity/ima/ima_init.c        |  7 +++--
 security/integrity/ima/ima_init_ima_ns.c |  4 +++
 security/integrity/ima/ima_kexec.c       |  3 +-
 security/integrity/ima/ima_main.c        | 14 +++++----
 security/integrity/ima/ima_queue.c       | 37 ++++++++++++------------
 security/integrity/ima/ima_template.c    |  5 ++--
 9 files changed, 76 insertions(+), 53 deletions(-)

diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h
index 0b3dc9425076..68d8a8e6fd1d 100644
--- a/security/integrity/ima/ima.h
+++ b/security/integrity/ima/ima.h
@@ -117,6 +117,12 @@ struct ima_kexec_hdr {
 	u64 count;
 };
 
+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 list_head ima_default_rules;
 	/* ns's policy rules */
@@ -126,6 +132,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;
 } __randomize_layout;
 extern struct ima_namespace init_ima_ns;
 
@@ -147,7 +155,8 @@ extern bool ima_canonical_fmt;
 int ima_init(void);
 int ima_fs_init(void);
 int ima_ns_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);
@@ -156,7 +165,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);
@@ -169,8 +179,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);
@@ -184,13 +196,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 */
@@ -275,7 +280,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,
@@ -291,7 +297,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 2df0d8549c13..bee35ebb3a38 100644
--- a/security/integrity/ima/ima_api.c
+++ b/security/integrity/ima/ima_api.c
@@ -99,7 +99,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)
 {
@@ -119,7 +120,8 @@ 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;
 }
 
@@ -130,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)
 {
@@ -144,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 +302,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 +338,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 b7f379cfcb74..e5468031acdb 100644
--- a/security/integrity/ima/ima_fs.c
+++ b/security/integrity/ima/ima_fs.c
@@ -52,7 +52,10 @@ 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 = &init_ima_ns;
+
+	return ima_show_htable_value(buf, count, ppos,
+				     &ns->ima_htable.violations);
 }
 
 static const struct file_operations ima_htable_violations_ops = {
@@ -64,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 = &init_ima_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 d6b829c360d7..22ca5d872be0 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,8 @@ 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 */
+	/* boot aggregate must be first entry */
+	rc = ima_add_boot_aggregate(&init_ima_ns);
 	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 f04cc5e46668..5a755fa7d091 100644
--- a/security/integrity/ima/ima_init_ima_ns.c
+++ b/security/integrity/ima/ima_init_ima_ns.c
@@ -16,6 +16,10 @@ static 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_kexec.c b/security/integrity/ima/ima_kexec.c
index f799cc278a9a..f3ef8a0df992 100644
--- a/security/integrity/ima/ima_kexec.c
+++ b/security/integrity/ima/ima_kexec.c
@@ -146,7 +146,8 @@ void ima_load_kexec_buffer(void)
 	rc = ima_get_kexec_buffer(&kexec_buffer, &kexec_buffer_size);
 	switch (rc) {
 	case 0:
-		rc = ima_restore_measurement_list(kexec_buffer_size,
+		rc = ima_restore_measurement_list(&init_ima_ns,
+						  kexec_buffer_size,
 						  kexec_buffer);
 		if (rc != 0)
 			pr_err("Failed to restore the measurement list: %d\n",
diff --git a/security/integrity/ima/ima_main.c b/security/integrity/ima/ima_main.c
index 19f564f8e255..621685d4eb95 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");
 }
 
@@ -249,7 +250,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);
@@ -344,7 +345,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)) {
@@ -969,7 +970,8 @@ 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..ba1cffd083b8 100644
--- a/security/integrity/ima/ima_queue.c
+++ b/security/integrity/ima/ima_queue.c
@@ -31,13 +31,6 @@ 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.
@@ -45,8 +38,10 @@ struct ima_h_table ima_htable = {
 static DEFINE_MUTEX(ima_extend_list_mutex);
 
 /* 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,
-						       int pcr)
+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;
 	unsigned int key;
@@ -54,7 +49,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 +85,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 +102,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 +153,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 +167,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 +199,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..0f8aa10b56fc 100644
--- a/security/integrity/ima/ima_template.c
+++ b/security/integrity/ima/ima_template.c
@@ -400,7 +400,8 @@ 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 +517,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


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

* [PATCH v8 05/19] ima: Move measurement list related variables into ima_namespace
  2022-01-04 17:03 [PATCH v8 00/19] ima: Namespace IMA with audit support in IMA-ns Stefan Berger
                   ` (3 preceding siblings ...)
  2022-01-04 17:04 ` [PATCH v8 04/19] ima: Move ima_htable " Stefan Berger
@ 2022-01-04 17:04 ` Stefan Berger
  2022-01-13 20:27   ` Mimi Zohar
  2022-01-04 17:04 ` [PATCH v8 06/19] ima: Move some IMA policy and filesystem " Stefan Berger
                   ` (13 subsequent siblings)
  18 siblings, 1 reply; 50+ messages in thread
From: Stefan Berger @ 2022-01-04 17:04 UTC (permalink / raw)
  To: linux-integrity
  Cc: zohar, serge, christian.brauner, containers, dmitry.kasatkin,
	ebiederm, krzysztof.struczynski, roberto.sassu, mpeters, lhinds,
	lsturman, puiterwi, jejb, jamjoom, linux-kernel, paul, rgb,
	linux-security-module, jmorris, Stefan Berger

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

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.

Signed-off-by: Stefan Berger <stefanb@linux.ibm.com>
---
 security/integrity/ima/ima.h             |  5 +++--
 security/integrity/ima/ima_fs.c          |  6 ++++--
 security/integrity/ima/ima_init_ima_ns.c |  5 +++++
 security/integrity/ima/ima_kexec.c       | 12 +++++++-----
 security/integrity/ima/ima_queue.c       | 24 ++++++++++--------------
 5 files changed, 29 insertions(+), 23 deletions(-)

diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h
index 68d8a8e6fd1d..ee16ce5050c8 100644
--- a/security/integrity/ima/ima.h
+++ b/security/integrity/ima/ima.h
@@ -106,7 +106,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 {
@@ -134,6 +133,8 @@ struct ima_namespace {
 	int ima_policy_flag;
 
 	struct ima_h_table ima_htable;
+	struct list_head ima_measurements;
+	unsigned long binary_runtime_size;
 } __randomize_layout;
 extern struct ima_namespace init_ima_ns;
 
@@ -184,7 +185,7 @@ int ima_restore_measurement_entry(struct ima_namespace *ns,
 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);
+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 e5468031acdb..5462cb49e455 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 = &init_ima_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 = &init_ima_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 5a755fa7d091..d9c505fd8a14 100644
--- a/security/integrity/ima/ima_init_ima_ns.c
+++ b/security/integrity/ima/ima_init_ima_ns.c
@@ -19,6 +19,11 @@ static 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_kexec.c b/security/integrity/ima/ima_kexec.c
index f3ef8a0df992..c07149228013 100644
--- a/security/integrity/ima/ima_kexec.c
+++ b/security/integrity/ima/ima_kexec.c
@@ -15,7 +15,8 @@
 #include "ima.h"
 
 #ifdef CONFIG_IMA_KEXEC
-static int ima_dump_measurement_list(unsigned long *buffer_size, void **buffer,
+static int ima_dump_measurement_list(struct ima_namespace *ns,
+				    unsigned long *buffer_size, void **buffer,
 				     unsigned long segment_size)
 {
 	struct ima_queue_entry *qe;
@@ -36,7 +37,7 @@ static int ima_dump_measurement_list(unsigned long *buffer_size, void **buffer,
 
 	memset(&khdr, 0, sizeof(khdr));
 	khdr.version = 1;
-	list_for_each_entry_rcu(qe, &ima_measurements, later) {
+	list_for_each_entry_rcu(qe, &ns->ima_measurements, later) {
 		if (file.count < file.size) {
 			khdr.count++;
 			ima_measurements_show(&file, qe);
@@ -84,6 +85,7 @@ void ima_add_kexec_buffer(struct kimage *image)
 	struct kexec_buf kbuf = { .image = image, .buf_align = PAGE_SIZE,
 				  .buf_min = 0, .buf_max = ULONG_MAX,
 				  .top_down = true };
+	struct ima_namespace *ns = &init_ima_ns;
 	unsigned long binary_runtime_size;
 
 	/* use more understandable variable names than defined in kbuf */
@@ -96,11 +98,11 @@ void ima_add_kexec_buffer(struct kimage *image)
 	 * Reserve an extra half page of memory for additional measurements
 	 * added during the kexec load.
 	 */
-	binary_runtime_size = ima_get_binary_runtime_size();
+	binary_runtime_size = ima_get_binary_runtime_size(ns);
 	if (binary_runtime_size >= ULONG_MAX - PAGE_SIZE)
 		kexec_segment_size = ULONG_MAX;
 	else
-		kexec_segment_size = ALIGN(ima_get_binary_runtime_size() +
+		kexec_segment_size = ALIGN(ima_get_binary_runtime_size(ns) +
 					   PAGE_SIZE / 2, PAGE_SIZE);
 	if ((kexec_segment_size == ULONG_MAX) ||
 	    ((kexec_segment_size >> PAGE_SHIFT) > totalram_pages() / 2)) {
@@ -108,7 +110,7 @@ void ima_add_kexec_buffer(struct kimage *image)
 		return;
 	}
 
-	ima_dump_measurement_list(&kexec_buffer_size, &kexec_buffer,
+	ima_dump_measurement_list(ns, &kexec_buffer_size, &kexec_buffer,
 				  kexec_segment_size);
 	if (!kexec_buffer) {
 		pr_err("Not enough memory for the kexec measurement buffer.\n");
diff --git a/security/integrity/ima/ima_queue.c b/security/integrity/ima/ima_queue.c
index ba1cffd083b8..9d2cb9ab5d8c 100644
--- a/security/integrity/ima/ima_queue.c
+++ b/security/integrity/ima/ima_queue.c
@@ -24,13 +24,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.
@@ -100,7 +93,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) {
@@ -109,12 +102,14 @@ 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;
 }
@@ -124,12 +119,13 @@ 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)
-- 
2.31.1


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

* [PATCH v8 06/19] ima: Move some IMA policy and filesystem related variables into ima_namespace
  2022-01-04 17:03 [PATCH v8 00/19] ima: Namespace IMA with audit support in IMA-ns Stefan Berger
                   ` (4 preceding siblings ...)
  2022-01-04 17:04 ` [PATCH v8 05/19] ima: Move measurement list related variables " Stefan Berger
@ 2022-01-04 17:04 ` Stefan Berger
  2022-01-04 17:04 ` [PATCH v8 07/19] ima: Move dentry into ima_namespace and others onto stack Stefan Berger
                   ` (12 subsequent siblings)
  18 siblings, 0 replies; 50+ messages in thread
From: Stefan Berger @ 2022-01-04 17:04 UTC (permalink / raw)
  To: linux-integrity
  Cc: zohar, serge, christian.brauner, containers, dmitry.kasatkin,
	ebiederm, krzysztof.struczynski, roberto.sassu, mpeters, lhinds,
	lsturman, puiterwi, jejb, jamjoom, linux-kernel, paul, rgb,
	linux-security-module, jmorris, Stefan Berger

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

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 <stefanb@linux.ibm.com>
---
 security/integrity/ima/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/security/integrity/ima/ima.h b/security/integrity/ima/ima.h
index ee16ce5050c8..82b3f6a98320 100644
--- a/security/integrity/ima/ima.h
+++ b/security/integrity/ima/ima.h
@@ -135,6 +135,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;
 } __randomize_layout;
 extern struct ima_namespace init_ima_ns;
 
diff --git a/security/integrity/ima/ima_fs.c b/security/integrity/ima/ima_fs.c
index 5462cb49e455..973bf17754b2 100644
--- a/security/integrity/ima/ima_fs.c
+++ b/security/integrity/ima/ima_fs.c
@@ -24,8 +24,6 @@
 
 #include "ima.h"
 
-static DEFINE_MUTEX(ima_write_mutex);
-
 bool ima_canonical_fmt;
 static int __init default_canonical_fmt_setup(char *str)
 {
@@ -36,8 +34,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)
 {
@@ -338,7 +334,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;
 
@@ -353,12 +349,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;
 }
@@ -375,8 +371,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,
@@ -391,6 +385,8 @@ static const struct seq_operations ima_policy_seqops = {
  */
 static int ima_open_policy(struct inode *inode, struct file *filp)
 {
+	struct ima_namespace *ns = &init_ima_ns;
+
 	if (!(filp->f_flags & O_WRONLY)) {
 #ifndef	CONFIG_IMA_READ_POLICY
 		return -EACCES;
@@ -402,7 +398,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;
 }
@@ -416,25 +412,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 = &init_ima_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;
 	}
 
@@ -443,7 +439,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 d9c505fd8a14..68671f976756 100644
--- a/security/integrity/ima/ima_init_ima_ns.c
+++ b/security/integrity/ima/ima_init_ima_ns.c
@@ -25,6 +25,10 @@ static 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


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

* [PATCH v8 07/19] ima: Move dentry into ima_namespace and others onto stack
  2022-01-04 17:03 [PATCH v8 00/19] ima: Namespace IMA with audit support in IMA-ns Stefan Berger
                   ` (5 preceding siblings ...)
  2022-01-04 17:04 ` [PATCH v8 06/19] ima: Move some IMA policy and filesystem " Stefan Berger
@ 2022-01-04 17:04 ` Stefan Berger
  2022-01-13 20:28   ` Mimi Zohar
  2022-01-04 17:04 ` [PATCH v8 08/19] ima: Use mac_admin_ns_capable() to check corresponding capability Stefan Berger
                   ` (11 subsequent siblings)
  18 siblings, 1 reply; 50+ messages in thread
From: Stefan Berger @ 2022-01-04 17:04 UTC (permalink / raw)
  To: linux-integrity
  Cc: zohar, serge, christian.brauner, containers, dmitry.kasatkin,
	ebiederm, krzysztof.struczynski, roberto.sassu, mpeters, lhinds,
	lsturman, puiterwi, jejb, jamjoom, linux-kernel, paul, rgb,
	linux-security-module, jmorris, Stefan Berger

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

Move the policy file dentry into the ima_namespace for reuse by
virtualized SecurityFS and for being able to remove it from
the filesystem. Move the other dentries onto the stack.

Signed-off-by: Stefan Berger <stefanb@linux.ibm.com>
---
 security/integrity/ima/ima.h    |  2 ++
 security/integrity/ima/ima_fs.c | 32 ++++++++++++++++++--------------
 2 files changed, 20 insertions(+), 14 deletions(-)

diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h
index 82b3f6a98320..224b09617c52 100644
--- a/security/integrity/ima/ima.h
+++ b/security/integrity/ima/ima.h
@@ -140,6 +140,8 @@ struct ima_namespace {
 	struct mutex ima_write_mutex;
 	unsigned long ima_fs_flags;
 	int valid_policy;
+
+	struct dentry *policy_dentry;
 } __randomize_layout;
 extern struct ima_namespace init_ima_ns;
 
diff --git a/security/integrity/ima/ima_fs.c b/security/integrity/ima/ima_fs.c
index 973bf17754b2..5b6404991b37 100644
--- a/security/integrity/ima/ima_fs.c
+++ b/security/integrity/ima/ima_fs.c
@@ -359,14 +359,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,
 };
@@ -436,8 +428,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->policy_dentry);
+	ns->policy_dentry = NULL;
 #elif defined(CONFIG_IMA_WRITE_POLICY)
 	clear_bit(IMA_FS_BUSY, &ns->ima_fs_flags);
 #elif defined(CONFIG_IMA_READ_POLICY)
@@ -454,8 +446,15 @@ static const struct file_operations ima_measure_policy_ops = {
 	.llseek = generic_file_llseek,
 };
 
-int __init ima_fs_init(void)
+static int __init ima_fs_ns_init(struct ima_namespace *ns)
 {
+	struct dentry *ima_dir;
+	struct dentry *ima_symlink = NULL;
+	struct dentry *binary_runtime_measurements = NULL;
+	struct dentry *ascii_runtime_measurements = NULL;
+	struct dentry *runtime_measurements_count = NULL;
+	struct dentry *violations = NULL;
+
 	ima_dir = securityfs_create_dir("ima", integrity_dir);
 	if (IS_ERR(ima_dir))
 		return -1;
@@ -492,20 +491,25 @@ int __init ima_fs_init(void)
 	if (IS_ERR(violations))
 		goto out;
 
-	ima_policy = securityfs_create_file("policy", POLICY_FILE_FLAGS,
+	ns->policy_dentry = securityfs_create_file("policy", POLICY_FILE_FLAGS,
 					    ima_dir, NULL,
 					    &ima_measure_policy_ops);
-	if (IS_ERR(ima_policy))
+	if (IS_ERR(ns->policy_dentry))
 		goto out;
 
 	return 0;
 out:
+	securityfs_remove(ns->policy_dentry);
 	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);
 	return -1;
 }
+
+int __init ima_fs_init(void)
+{
+	return ima_fs_ns_init(&init_ima_ns);
+}
-- 
2.31.1


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

* [PATCH v8 08/19] ima: Use mac_admin_ns_capable() to check corresponding capability
  2022-01-04 17:03 [PATCH v8 00/19] ima: Namespace IMA with audit support in IMA-ns Stefan Berger
                   ` (6 preceding siblings ...)
  2022-01-04 17:04 ` [PATCH v8 07/19] ima: Move dentry into ima_namespace and others onto stack Stefan Berger
@ 2022-01-04 17:04 ` Stefan Berger
  2022-01-05 20:55     ` kernel test robot
  2022-01-13 20:28   ` Mimi Zohar
  2022-01-04 17:04 ` [PATCH v8 09/19] ima: Only accept AUDIT rules for non-init_ima_ns namespaces for now Stefan Berger
                   ` (10 subsequent siblings)
  18 siblings, 2 replies; 50+ messages in thread
From: Stefan Berger @ 2022-01-04 17:04 UTC (permalink / raw)
  To: linux-integrity
  Cc: zohar, serge, christian.brauner, containers, dmitry.kasatkin,
	ebiederm, krzysztof.struczynski, roberto.sassu, mpeters, lhinds,
	lsturman, puiterwi, jejb, jamjoom, linux-kernel, paul, rgb,
	linux-security-module, jmorris, Stefan Berger, Denis Semakin

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

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 <denis.semakin@huawei.com>
Signed-off-by: Stefan Berger <stefanb@linux.ibm.com>
---
 include/linux/capability.h      | 6 ++++++
 security/integrity/ima/ima.h    | 6 ++++++
 security/integrity/ima/ima_fs.c | 3 ++-
 3 files changed, 14 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.h b/security/integrity/ima/ima.h
index 224b09617c52..0c86a955cedf 100644
--- a/security/integrity/ima/ima.h
+++ b/security/integrity/ima/ima.h
@@ -483,6 +483,12 @@ static inline int ima_filter_rule_match(u32 secid, u32 field, u32 op,
 #define	POLICY_FILE_FLAGS	S_IWUSR
 #endif /* CONFIG_IMA_READ_POLICY */
 
+static inline
+struct user_namespace *ima_user_ns_from_file(const struct file *filp)
+{
+	return file_inode(filp)->i_sb->s_user_ns;
+}
+
 static inline struct ima_namespace *get_current_ns(void)
 {
 	return &init_ima_ns;
diff --git a/security/integrity/ima/ima_fs.c b/security/integrity/ima/ima_fs.c
index 5b6404991b37..71302bb5427f 100644
--- a/security/integrity/ima/ima_fs.c
+++ b/security/integrity/ima/ima_fs.c
@@ -377,6 +377,7 @@ static const struct seq_operations ima_policy_seqops = {
  */
 static int ima_open_policy(struct inode *inode, struct file *filp)
 {
+	struct user_namespace *user_ns = ima_user_ns_from_file(filp);
 	struct ima_namespace *ns = &init_ima_ns;
 
 	if (!(filp->f_flags & O_WRONLY)) {
@@ -385,7 +386,7 @@ static int ima_open_policy(struct inode *inode, struct file *filp)
 #else
 		if ((filp->f_flags & O_ACCMODE) != O_RDONLY)
 			return -EACCES;
-		if (!capable(CAP_SYS_ADMIN))
+		if (!mac_admin_ns_capable(user_ns))
 			return -EPERM;
 		return seq_open(filp, &ima_policy_seqops);
 #endif
-- 
2.31.1


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

* [PATCH v8 09/19] ima: Only accept AUDIT rules for non-init_ima_ns namespaces for now
  2022-01-04 17:03 [PATCH v8 00/19] ima: Namespace IMA with audit support in IMA-ns Stefan Berger
                   ` (7 preceding siblings ...)
  2022-01-04 17:04 ` [PATCH v8 08/19] ima: Use mac_admin_ns_capable() to check corresponding capability Stefan Berger
@ 2022-01-04 17:04 ` Stefan Berger
  2022-01-04 17:04 ` [PATCH v8 10/19] ima: Implement hierarchical processing of file accesses Stefan Berger
                   ` (9 subsequent siblings)
  18 siblings, 0 replies; 50+ messages in thread
From: Stefan Berger @ 2022-01-04 17:04 UTC (permalink / raw)
  To: linux-integrity
  Cc: zohar, serge, christian.brauner, containers, dmitry.kasatkin,
	ebiederm, krzysztof.struczynski, roberto.sassu, mpeters, lhinds,
	lsturman, puiterwi, jejb, jamjoom, linux-kernel, paul, rgb,
	linux-security-module, jmorris, Stefan Berger

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

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 <stefanb@linux.ibm.com>
---
 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 66d005be3577..c265c3b5a9f2 100644
--- a/security/integrity/ima/ima_policy.c
+++ b/security/integrity/ima/ima_policy.c
@@ -1793,6 +1793,16 @@ static int ima_parse_rule(struct ima_namespace *ns,
 			result = -EINVAL;
 			break;
 		}
+
+		/* IMA namespace only accepts AUDIT rules */
+		if (ns != &init_ima_ns) {
+			switch (entry->action) {
+			case MEASURE:
+			case APPRAISE:
+			case HASH:
+				result = -EINVAL;
+			}
+		}
 	}
 	if (!result && !ima_validate_rule(entry))
 		result = -EINVAL;
-- 
2.31.1


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

* [PATCH v8 10/19] ima: Implement hierarchical processing of file accesses
  2022-01-04 17:03 [PATCH v8 00/19] ima: Namespace IMA with audit support in IMA-ns Stefan Berger
                   ` (8 preceding siblings ...)
  2022-01-04 17:04 ` [PATCH v8 09/19] ima: Only accept AUDIT rules for non-init_ima_ns namespaces for now Stefan Berger
@ 2022-01-04 17:04 ` Stefan Berger
  2022-01-14 11:21   ` Christian Brauner
  2022-01-04 17:04 ` [PATCH v8 11/19] ima: Implement ima_free_policy_rules() for freeing of an ima_namespace Stefan Berger
                   ` (8 subsequent siblings)
  18 siblings, 1 reply; 50+ messages in thread
From: Stefan Berger @ 2022-01-04 17:04 UTC (permalink / raw)
  To: linux-integrity
  Cc: zohar, serge, christian.brauner, containers, dmitry.kasatkin,
	ebiederm, krzysztof.struczynski, roberto.sassu, mpeters, lhinds,
	lsturman, puiterwi, jejb, jamjoom, linux-kernel, paul, rgb,
	linux-security-module, jmorris, Stefan Berger

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

Implement hierarchical processing of file accesses in IMA namespaces by
walking the list of user namespaces towards the root. This way file
accesses can be audited in an IMA namespace and also be evaluated against
the IMA policies of parent IMA namespaces.

__process_measurement() returns either 0 or -EACCES. For hierarchical
processing remember the -EACCES returned by this function but continue
to the parent user namespace. At the end either return 0 or -EACCES
if an error occurred in one of the IMA namespaces.

Currently the ima_ns pointer of the user_namespace is always NULL except
at the init_user_ns, so test ima_ns for NULL pointer and skip the call to
__process_measurement() if it is NULL. Once IMA namespacing is fully
enabled, the pointer may also be NULL due to late initialization of the
IMA namespace.

Signed-off-by: Stefan Berger <stefanb@linux.ibm.com>
---
 include/linux/ima.h               |  6 +++++
 security/integrity/ima/ima_main.c | 37 +++++++++++++++++++++++++++----
 2 files changed, 39 insertions(+), 4 deletions(-)

diff --git a/include/linux/ima.h b/include/linux/ima.h
index b6ab66a546ae..fcee2a51bb87 100644
--- a/include/linux/ima.h
+++ b/include/linux/ima.h
@@ -65,6 +65,12 @@ static inline const char * const *arch_get_ima_policy(void)
 }
 #endif
 
+static inline struct user_namespace
+*ima_ns_to_user_ns(struct ima_namespace *ns)
+{
+	return current_user_ns();
+}
+
 #else
 static inline enum hash_algo ima_get_current_hash_algo(void)
 {
diff --git a/security/integrity/ima/ima_main.c b/security/integrity/ima/ima_main.c
index 621685d4eb95..51b0ef1cebbe 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;
@@ -395,6 +395,35 @@ static int process_measurement(struct ima_namespace *ns,
 	return 0;
 }
 
+static int process_measurement(struct ima_namespace *ns,
+			       struct file *file, const struct cred *cred,
+			       u32 secid, char *buf, loff_t size, int mask,
+			       enum ima_hooks func)
+{
+	struct user_namespace *user_ns = ima_ns_to_user_ns(ns);
+	int ret = 0;
+
+	while (user_ns) {
+		ns = ima_ns_from_user_ns(user_ns);
+		if (ns) {
+			int rc;
+
+			rc = __process_measurement(ns, file, cred, secid, buf,
+						   size, mask, func);
+			switch (rc) {
+			case -EACCES:
+				/* return this error at the end but continue */
+				ret = -EACCES;
+				break;
+			}
+		}
+
+		user_ns = user_ns->parent;
+	};
+
+	return ret;
+}
+
 /**
  * ima_file_mmap - based on policy, collect/store measurement.
  * @file: pointer to the file to be measured (May be NULL)
-- 
2.31.1


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

* [PATCH v8 11/19] ima: Implement ima_free_policy_rules() for freeing of an ima_namespace
  2022-01-04 17:03 [PATCH v8 00/19] ima: Namespace IMA with audit support in IMA-ns Stefan Berger
                   ` (9 preceding siblings ...)
  2022-01-04 17:04 ` [PATCH v8 10/19] ima: Implement hierarchical processing of file accesses Stefan Berger
@ 2022-01-04 17:04 ` Stefan Berger
  2022-01-04 17:04 ` [PATCH v8 12/19] userns: Add pointer to ima_namespace to user_namespace Stefan Berger
                   ` (7 subsequent siblings)
  18 siblings, 0 replies; 50+ messages in thread
From: Stefan Berger @ 2022-01-04 17:04 UTC (permalink / raw)
  To: linux-integrity
  Cc: zohar, serge, christian.brauner, containers, dmitry.kasatkin,
	ebiederm, krzysztof.struczynski, roberto.sassu, mpeters, lhinds,
	lsturman, puiterwi, jejb, jamjoom, linux-kernel, paul, rgb,
	linux-security-module, jmorris, Stefan Berger

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

Implement ima_free_policy_rules() that is needed when an ima_namespace
is freed.

Signed-off-by: Stefan Berger <stefanb@linux.ibm.com>
---
 security/integrity/ima/ima.h        |  1 +
 security/integrity/ima/ima_policy.c | 16 ++++++++++++++++
 2 files changed, 17 insertions(+)

diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h
index 0c86a955cedf..1c1e859721ee 100644
--- a/security/integrity/ima/ima.h
+++ b/security/integrity/ima/ima.h
@@ -325,6 +325,7 @@ 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);
diff --git a/security/integrity/ima/ima_policy.c b/security/integrity/ima/ima_policy.c
index c265c3b5a9f2..15c68dc5da9e 100644
--- a/security/integrity/ima/ima_policy.c
+++ b/security/integrity/ima/ima_policy.c
@@ -1884,6 +1884,22 @@ void ima_delete_rules(struct ima_namespace *ns)
 	}
 }
 
+/**
+ * ima_free_policy_rules - free all policy rules
+ * @ns: IMA namespace that has the policy
+ */
+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);
+	}
+}
+
 #define __ima_hook_stringify(func, str)	(#func),
 
 const char *const func_tokens[] = {
-- 
2.31.1


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

* [PATCH v8 12/19] userns: Add pointer to ima_namespace to user_namespace
  2022-01-04 17:03 [PATCH v8 00/19] ima: Namespace IMA with audit support in IMA-ns Stefan Berger
                   ` (10 preceding siblings ...)
  2022-01-04 17:04 ` [PATCH v8 11/19] ima: Implement ima_free_policy_rules() for freeing of an ima_namespace Stefan Berger
@ 2022-01-04 17:04 ` Stefan Berger
  2022-01-04 17:04 ` [PATCH v8 13/19] ima: Add functions for creation and freeing of an ima_namespace Stefan Berger
                   ` (6 subsequent siblings)
  18 siblings, 0 replies; 50+ messages in thread
From: Stefan Berger @ 2022-01-04 17:04 UTC (permalink / raw)
  To: linux-integrity
  Cc: zohar, serge, christian.brauner, containers, dmitry.kasatkin,
	ebiederm, krzysztof.struczynski, roberto.sassu, mpeters, lhinds,
	lsturman, puiterwi, jejb, jamjoom, linux-kernel, paul, rgb,
	linux-security-module, jmorris, Stefan Berger

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

Add a pointer to ima_namespace to the user_namespace and initialize
the init_user_ns with a pointer to init_ima_ns.

Signed-off-by: Stefan Berger <stefanb@linux.ibm.com>
---
 include/linux/ima.h            | 2 ++
 include/linux/user_namespace.h | 4 ++++
 kernel/user.c                  | 4 ++++
 security/integrity/ima/ima.h   | 8 ++++++++
 4 files changed, 18 insertions(+)

diff --git a/include/linux/ima.h b/include/linux/ima.h
index fcee2a51bb87..e1d65162d1fb 100644
--- a/include/linux/ima.h
+++ b/include/linux/ima.h
@@ -14,6 +14,8 @@
 #include <crypto/hash_info.h>
 struct linux_binprm;
 
+extern struct ima_namespace init_ima_ns;
+
 #ifdef CONFIG_IMA
 extern enum hash_algo ima_get_current_hash_algo(void);
 extern int ima_bprm_check(struct linux_binprm *bprm);
diff --git a/include/linux/user_namespace.h b/include/linux/user_namespace.h
index 33a4240e6a6f..019e8cf7b633 100644
--- a/include/linux/user_namespace.h
+++ b/include/linux/user_namespace.h
@@ -36,6 +36,7 @@ struct uid_gid_map { /* 64 bytes -- 1 cache line */
 #define USERNS_INIT_FLAGS USERNS_SETGROUPS_ALLOWED
 
 struct ucounts;
+struct ima_namespace;
 
 enum ucount_type {
 	UCOUNT_USER_NAMESPACES,
@@ -99,6 +100,9 @@ struct user_namespace {
 #endif
 	struct ucounts		*ucounts;
 	long ucount_max[UCOUNT_COUNTS];
+#ifdef CONFIG_IMA_NS
+	struct ima_namespace	*ima_ns;
+#endif
 } __randomize_layout;
 
 struct ucounts {
diff --git a/kernel/user.c b/kernel/user.c
index e2cf8c22b539..e5d1f4b9b8ba 100644
--- a/kernel/user.c
+++ b/kernel/user.c
@@ -19,6 +19,7 @@
 #include <linux/export.h>
 #include <linux/user_namespace.h>
 #include <linux/proc_ns.h>
+#include <linux/ima.h>
 
 /*
  * userns count is 1 for root user, 1 for init_uts_ns,
@@ -67,6 +68,9 @@ struct user_namespace init_user_ns = {
 	.keyring_name_list = LIST_HEAD_INIT(init_user_ns.keyring_name_list),
 	.keyring_sem = __RWSEM_INITIALIZER(init_user_ns.keyring_sem),
 #endif
+#ifdef CONFIG_IMA_NS
+	.ima_ns = &init_ima_ns,
+#endif
 };
 EXPORT_SYMBOL_GPL(init_user_ns);
 
diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h
index 1c1e859721ee..f63c6f22b853 100644
--- a/security/integrity/ima/ima.h
+++ b/security/integrity/ima/ima.h
@@ -490,6 +490,14 @@ struct user_namespace *ima_user_ns_from_file(const struct file *filp)
 	return file_inode(filp)->i_sb->s_user_ns;
 }
 
+static inline struct ima_namespace
+*ima_ns_from_user_ns(struct user_namespace *user_ns)
+{
+	if (user_ns == &init_user_ns)
+		return &init_ima_ns;
+	return NULL;
+}
+
 static inline struct ima_namespace *get_current_ns(void)
 {
 	return &init_ima_ns;
-- 
2.31.1


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

* [PATCH v8 13/19] ima: Add functions for creation and freeing of an ima_namespace
  2022-01-04 17:03 [PATCH v8 00/19] ima: Namespace IMA with audit support in IMA-ns Stefan Berger
                   ` (11 preceding siblings ...)
  2022-01-04 17:04 ` [PATCH v8 12/19] userns: Add pointer to ima_namespace to user_namespace Stefan Berger
@ 2022-01-04 17:04 ` Stefan Berger
  2022-01-14 11:43   ` Christian Brauner
  2022-01-04 17:04 ` [PATCH v8 14/19] integrity/ima: Define ns_status for storing namespaced iint data Stefan Berger
                   ` (5 subsequent siblings)
  18 siblings, 1 reply; 50+ messages in thread
From: Stefan Berger @ 2022-01-04 17:04 UTC (permalink / raw)
  To: linux-integrity
  Cc: zohar, serge, christian.brauner, containers, dmitry.kasatkin,
	ebiederm, krzysztof.struczynski, roberto.sassu, mpeters, lhinds,
	lsturman, puiterwi, jejb, jamjoom, linux-kernel, paul, rgb,
	linux-security-module, jmorris, Stefan Berger

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

Implement create_ima_ns() to create and initialize an ima_namespace
and implement free_ima_ns() to free it.

Signed-off-by: Stefan Berger <stefanb@linux.ibm.com>
---
 include/linux/ima.h                      | 13 +++++
 security/integrity/ima/Makefile          |  1 +
 security/integrity/ima/ima.h             | 16 +++++++
 security/integrity/ima/ima_init_ima_ns.c |  2 +-
 security/integrity/ima/ima_ns.c          | 61 ++++++++++++++++++++++++
 5 files changed, 92 insertions(+), 1 deletion(-)
 create mode 100644 security/integrity/ima/ima_ns.c

diff --git a/include/linux/ima.h b/include/linux/ima.h
index e1d65162d1fb..06c88cb17b21 100644
--- a/include/linux/ima.h
+++ b/include/linux/ima.h
@@ -226,4 +226,17 @@ static inline bool ima_appraise_signature(enum kernel_read_file_id func)
 	return false;
 }
 #endif /* CONFIG_IMA_APPRAISE && CONFIG_INTEGRITY_TRUSTED_KEYRING */
+
+#ifdef CONFIG_IMA_NS
+
+void free_ima_ns(struct user_namespace *ns);
+
+#else
+
+static inline void free_ima_ns(struct user_namespace *user_ns)
+{
+}
+
+#endif /* CONFIG_IMA_NS */
+
 #endif /* _LINUX_IMA_H */
diff --git a/security/integrity/ima/Makefile b/security/integrity/ima/Makefile
index f8a5e5f3975d..b86a35fbed60 100644
--- a/security/integrity/ima/Makefile
+++ b/security/integrity/ima/Makefile
@@ -14,6 +14,7 @@ ima-$(CONFIG_HAVE_IMA_KEXEC) += ima_kexec.o
 ima-$(CONFIG_IMA_BLACKLIST_KEYRING) += ima_mok.o
 ima-$(CONFIG_IMA_MEASURE_ASYMMETRIC_KEYS) += ima_asymmetric_keys.o
 ima-$(CONFIG_IMA_QUEUE_EARLY_BOOT_KEYS) += ima_queue_keys.o
+ima-$(CONFIG_IMA_NS) += ima_ns.o
 
 ifeq ($(CONFIG_EFI),y)
 ima-$(CONFIG_IMA_SECURE_AND_OR_TRUSTED_BOOT) += ima_efi.o
diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h
index f63c6f22b853..4255301e5b96 100644
--- a/security/integrity/ima/ima.h
+++ b/security/integrity/ima/ima.h
@@ -163,6 +163,7 @@ extern bool ima_canonical_fmt;
 int ima_init(void);
 int ima_fs_init(void);
 int ima_ns_init(void);
+int ima_init_namespace(struct ima_namespace *ns);
 int ima_add_template_entry(struct ima_namespace *ns,
 			   struct ima_template_entry *entry, int violation,
 			   const char *op, struct inode *inode,
@@ -503,4 +504,19 @@ static inline struct ima_namespace *get_current_ns(void)
 	return &init_ima_ns;
 }
 
+#ifdef CONFIG_IMA_NS
+
+struct ima_namespace *create_ima_ns(struct user_namespace *user_ns);
+
+#else
+
+static inline struct ima_namespace *
+create_ima_ns(struct user_namespace *user_ns)
+{
+	WARN(1, "Cannot create an IMA namespace\n");
+	return ERR_PTR(-EFAULT);
+}
+
+#endif /* CONFIG_IMA_NS */
+
 #endif /* __LINUX_IMA_H */
diff --git a/security/integrity/ima/ima_init_ima_ns.c b/security/integrity/ima/ima_init_ima_ns.c
index 68671f976756..6eac998781c3 100644
--- a/security/integrity/ima/ima_init_ima_ns.c
+++ b/security/integrity/ima/ima_init_ima_ns.c
@@ -8,7 +8,7 @@
 
 #include "ima.h"
 
-static int ima_init_namespace(struct ima_namespace *ns)
+int ima_init_namespace(struct ima_namespace *ns)
 {
 	INIT_LIST_HEAD(&ns->ima_default_rules);
 	INIT_LIST_HEAD(&ns->ima_policy_rules);
diff --git a/security/integrity/ima/ima_ns.c b/security/integrity/ima/ima_ns.c
new file mode 100644
index 000000000000..675466d292e8
--- /dev/null
+++ b/security/integrity/ima/ima_ns.c
@@ -0,0 +1,61 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2016-2021 IBM Corporation
+ * Author:
+ *  Yuqiong Sun <suny@us.ibm.com>
+ *  Stefan Berger <stefanb@linux.vnet.ibm.com>
+ */
+
+#include <linux/ima.h>
+
+#include "ima.h"
+
+static struct kmem_cache *imans_cachep;
+
+struct ima_namespace *create_ima_ns(struct user_namespace *user_ns)
+{
+	struct ima_namespace *ns;
+	int err;
+
+	ns = kmem_cache_zalloc(imans_cachep, GFP_KERNEL);
+	if (!ns)
+		return ERR_PTR(-ENOMEM);
+	pr_debug("NEW     ima_ns: %p\n", ns);
+
+	err = ima_init_namespace(ns);
+	if (err)
+		goto fail_free;
+
+	user_ns->ima_ns = ns;
+
+	return ns;
+
+fail_free:
+	kmem_cache_free(imans_cachep, ns);
+
+	return ERR_PTR(err);
+}
+
+static void destroy_ima_ns(struct ima_namespace *ns)
+{
+	pr_debug("DESTROY ima_ns: %p\n", ns);
+	ima_free_policy_rules(ns);
+	kmem_cache_free(imans_cachep, ns);
+}
+
+void free_ima_ns(struct user_namespace *user_ns)
+{
+	struct ima_namespace *ns = user_ns->ima_ns;
+
+	if (!ns || WARN_ON(ns == &init_ima_ns))
+		return;
+
+	destroy_ima_ns(ns);
+}
+
+static int __init imans_cache_init(void)
+{
+	imans_cachep = KMEM_CACHE(ima_namespace, SLAB_PANIC);
+	return 0;
+}
+subsys_initcall(imans_cache_init)
-- 
2.31.1


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

* [PATCH v8 14/19] integrity/ima: Define ns_status for storing namespaced iint data
  2022-01-04 17:03 [PATCH v8 00/19] ima: Namespace IMA with audit support in IMA-ns Stefan Berger
                   ` (12 preceding siblings ...)
  2022-01-04 17:04 ` [PATCH v8 13/19] ima: Add functions for creation and freeing of an ima_namespace Stefan Berger
@ 2022-01-04 17:04 ` Stefan Berger
  2022-01-04 17:04 ` [PATCH v8 15/19] ima: Namespace audit status flags Stefan Berger
                   ` (4 subsequent siblings)
  18 siblings, 0 replies; 50+ messages in thread
From: Stefan Berger @ 2022-01-04 17:04 UTC (permalink / raw)
  To: linux-integrity
  Cc: zohar, serge, christian.brauner, containers, dmitry.kasatkin,
	ebiederm, krzysztof.struczynski, roberto.sassu, mpeters, lhinds,
	lsturman, puiterwi, jejb, jamjoom, linux-kernel, paul, rgb,
	linux-security-module, jmorris, Mehmet Kayaalp, Stefan Berger

From: Mehmet Kayaalp <mkayaalp@linux.vnet.ibm.com>

Add 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 and ns_status are looked up using the
inode pointer value. The lookup, allocate, and insertion code is also
similar.

In subsequent patches we will have to find all ns_status entries an iint
is being used in and reset flags there. To do this, connect a list of
ns_status to the integrity_iint_cache and provide a reader-writer
lock in the integrity_iint_cache to lock access to the list.

When getting an ns_status first try to find it in the RB tree. Here we can
run into the situation that an ns_status found in the RB tree has a
different iint associated with it for the same inode. In this case we need
to delete the ns_status structure and get a new one.

There are two cases for freeing:
- when the iint is freed (inode deletion): Walk the list of ns_status
  entries and disconnect each ns_status from the list; take the
  writer lock to protect access to the list; also, take the item off the
  per-namespace rbtree

- when the ima_namepace is freed: While walking the rbtree, remove the
  ns_status from the list while also holding the iint's writer lock;
  to be able to grab the lock we have to have a pointer to the iint on
  the ns_status structure.

To avoid an ns_status to be freed by the two cases concurrently, prevent
these two cases to run concurrently. Therefore, groups of threads
deleting either inodes or ima_namespaces are allowed to run concurrenty
but no two threads may run and one delete an inode and the other an
ima_namespace.

Signed-off-by: Mehmet Kayaalp <mkayaalp@linux.vnet.ibm.com>
Signed-off-by: Stefan Berger <stefanb@linux.ibm.com>
---
 include/linux/ima.h                      |   3 +
 security/integrity/iint.c                |  13 +
 security/integrity/ima/Makefile          |   2 +-
 security/integrity/ima/ima.h             |  29 ++
 security/integrity/ima/ima_init_ima_ns.c |   5 +
 security/integrity/ima/ima_ns.c          |   1 +
 security/integrity/ima/ima_ns_status.c   | 335 +++++++++++++++++++++++
 security/integrity/integrity.h           |   4 +
 8 files changed, 391 insertions(+), 1 deletion(-)
 create mode 100644 security/integrity/ima/ima_ns_status.c

diff --git a/include/linux/ima.h b/include/linux/ima.h
index 06c88cb17b21..e236eef5b486 100644
--- a/include/linux/ima.h
+++ b/include/linux/ima.h
@@ -231,6 +231,9 @@ static inline bool ima_appraise_signature(enum kernel_read_file_id func)
 
 void free_ima_ns(struct user_namespace *ns);
 
+extern void ima_free_ns_status_list(struct list_head *head,
+				    rwlock_t *ns_list_lock);
+
 #else
 
 static inline void free_ima_ns(struct user_namespace *user_ns)
diff --git a/security/integrity/iint.c b/security/integrity/iint.c
index 8638976f7990..9d8db7d4358c 100644
--- a/security/integrity/iint.c
+++ b/security/integrity/iint.c
@@ -19,6 +19,7 @@
 #include <linux/uaccess.h>
 #include <linux/security.h>
 #include <linux/lsm_hooks.h>
+#include <linux/ima.h>
 #include "integrity.h"
 
 static struct rb_root integrity_iint_tree = RB_ROOT;
@@ -82,6 +83,10 @@ static void iint_free(struct integrity_iint_cache *iint)
 	iint->ima_creds_status = INTEGRITY_UNKNOWN;
 	iint->evm_status = INTEGRITY_UNKNOWN;
 	iint->measured_pcrs = 0;
+#ifdef CONFIG_IMA_NS
+	rwlock_init(&iint->ns_list_lock);
+	INIT_LIST_HEAD(&iint->ns_list);
+#endif
 	kmem_cache_free(iint_cache, iint);
 }
 
@@ -155,6 +160,10 @@ void integrity_inode_free(struct inode *inode)
 	rb_erase(&iint->rb_node, &integrity_iint_tree);
 	write_unlock(&integrity_iint_lock);
 
+#ifdef CONFIG_IMA_NS
+	ima_free_ns_status_list(&iint->ns_list, &iint->ns_list_lock);
+#endif
+
 	iint_free(iint);
 }
 
@@ -170,6 +179,10 @@ static void init_once(void *foo)
 	iint->ima_creds_status = INTEGRITY_UNKNOWN;
 	iint->evm_status = INTEGRITY_UNKNOWN;
 	mutex_init(&iint->mutex);
+#ifdef CONFIG_IMA_NS
+	rwlock_init(&iint->ns_list_lock);
+	INIT_LIST_HEAD(&iint->ns_list);
+#endif
 }
 
 static int __init integrity_iintcache_init(void)
diff --git a/security/integrity/ima/Makefile b/security/integrity/ima/Makefile
index b86a35fbed60..edfb0c30a063 100644
--- a/security/integrity/ima/Makefile
+++ b/security/integrity/ima/Makefile
@@ -14,7 +14,7 @@ ima-$(CONFIG_HAVE_IMA_KEXEC) += ima_kexec.o
 ima-$(CONFIG_IMA_BLACKLIST_KEYRING) += ima_mok.o
 ima-$(CONFIG_IMA_MEASURE_ASYMMETRIC_KEYS) += ima_asymmetric_keys.o
 ima-$(CONFIG_IMA_QUEUE_EARLY_BOOT_KEYS) += ima_queue_keys.o
-ima-$(CONFIG_IMA_NS) += ima_ns.o
+ima-$(CONFIG_IMA_NS) += ima_ns.o ima_ns_status.o
 
 ifeq ($(CONFIG_EFI),y)
 ima-$(CONFIG_IMA_SECURE_AND_OR_TRUSTED_BOOT) += ima_efi.o
diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h
index 4255301e5b96..e4804be6b524 100644
--- a/security/integrity/ima/ima.h
+++ b/security/integrity/ima/ima.h
@@ -123,6 +123,10 @@ struct ima_h_table {
 };
 
 struct ima_namespace {
+	struct rb_root ns_status_tree;
+	rwlock_t ns_tree_lock;
+	struct kmem_cache *ns_status_cache;
+
 	struct list_head ima_default_rules;
 	/* ns's policy rules */
 	struct list_head ima_policy_rules;
@@ -159,6 +163,18 @@ static inline void ima_load_kexec_buffer(void) {}
  */
 extern bool ima_canonical_fmt;
 
+struct ns_status {
+	struct list_head ns_next;
+	struct rb_node rb_node;
+	struct integrity_iint_cache *iint;
+	struct inode *inode;
+	struct ima_namespace *ns;
+	ino_t i_ino;
+	u32 i_generation;
+	unsigned long flags;
+	struct llist_node gc_llist; /* used while freeing */
+};
+
 /* Internal IMA function definitions */
 int ima_init(void);
 int ima_fs_init(void);
@@ -508,6 +524,12 @@ static inline struct ima_namespace *get_current_ns(void)
 
 struct ima_namespace *create_ima_ns(struct user_namespace *user_ns);
 
+struct ns_status *ima_get_ns_status(struct ima_namespace *ns,
+				    struct inode *inode,
+				    struct integrity_iint_cache *iint);
+
+void ima_free_ns_status_tree(struct ima_namespace *ns);
+
 #else
 
 static inline struct ima_namespace *
@@ -517,6 +539,13 @@ create_ima_ns(struct user_namespace *user_ns)
 	return ERR_PTR(-EFAULT);
 }
 
+static inline struct ns_status *ima_get_ns_status(struct ima_namespace *ns,
+						  struct inode *inode,
+						  struct integrity_iint_cache *iint)
+{
+	return NULL;
+}
+
 #endif /* CONFIG_IMA_NS */
 
 #endif /* __LINUX_IMA_H */
diff --git a/security/integrity/ima/ima_init_ima_ns.c b/security/integrity/ima/ima_init_ima_ns.c
index 6eac998781c3..f8ffa07d6edc 100644
--- a/security/integrity/ima/ima_init_ima_ns.c
+++ b/security/integrity/ima/ima_init_ima_ns.c
@@ -10,6 +10,11 @@
 
 int ima_init_namespace(struct ima_namespace *ns)
 {
+	ns->ns_status_tree = RB_ROOT;
+	rwlock_init(&ns->ns_tree_lock);
+	/* Use KMEM_CACHE for simplicity */
+	ns->ns_status_cache = KMEM_CACHE(ns_status, SLAB_PANIC);
+
 	INIT_LIST_HEAD(&ns->ima_default_rules);
 	INIT_LIST_HEAD(&ns->ima_policy_rules);
 	INIT_LIST_HEAD(&ns->ima_temp_rules);
diff --git a/security/integrity/ima/ima_ns.c b/security/integrity/ima/ima_ns.c
index 675466d292e8..5a79fb6c10c0 100644
--- a/security/integrity/ima/ima_ns.c
+++ b/security/integrity/ima/ima_ns.c
@@ -40,6 +40,7 @@ static void destroy_ima_ns(struct ima_namespace *ns)
 {
 	pr_debug("DESTROY ima_ns: %p\n", ns);
 	ima_free_policy_rules(ns);
+	ima_free_ns_status_tree(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..78c1ace49ccd
--- /dev/null
+++ b/security/integrity/ima/ima_ns_status.c
@@ -0,0 +1,335 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2016-2021 IBM Corporation
+ * Author:
+ *  Yuqiong Sun <suny@us.ibm.com>
+ *  Stefan Berger <stefanb@linux.vnet.ibm.com>
+ */
+
+#include <linux/user_namespace.h>
+#include <linux/ima.h>
+
+#include "ima.h"
+
+/*
+ * An ns_status must be on a per-namespace rbtree and on a per-iint list.
+ *
+ * Locking order for ns_status:
+ * 1) ns->ns_tree_lock  : Lock the rbtree
+ * 2) iint->ns_list_lock: Lock the list
+ *
+ * An ns_status can be freed either by walking the rbtree (namespace deletion)
+ * or by walking the linked list of ns_status (inode/iint deletion). There are
+ * two functions that implement each one of the cases. To avoid concurrent
+ * freeing of the same ns_status, the two freeing paths cannot be run
+ * concurrently but each path can be run by multiple threads since no two
+ * threads will free the same inode/iint and no two threads will free the same
+ * namespace. Grouping threads like this ensures that:
+ * - while walking the rbtree: all ns_status will be on their list and the iint
+ *                             will still exist
+ * - while walking the list:   all ns_status will be on their rbtree
+ */
+enum lk_group {
+	GRP_NS_STATUS_LIST = 0,
+	GRP_NS_STATUS_TREE
+};
+static atomic_t lg_ctr[2] = {
+	ATOMIC_INIT(0),
+	ATOMIC_INIT(0)
+};
+static DEFINE_SPINLOCK(lg_ctr_lock);
+static struct wait_queue_head lg_wq[2] = {
+	__WAIT_QUEUE_HEAD_INITIALIZER(lg_wq[0]),
+	__WAIT_QUEUE_HEAD_INITIALIZER(lg_wq[1])
+};
+static atomic_t ns_list_waiters = ATOMIC_INIT(0);
+
+/* Any number of concurrent threads may free ns_status's in either one of the
+ * groups but the groups must not run concurrently. The GRP_NS_STATUS_TREE
+ * group yields to waiters in the GRP_NS_STATUS_LIST group since namespace
+ * deletion has more time.
+ */
+static void lock_group(enum lk_group group)
+{
+	unsigned long flags;
+	bool done = false;
+	int announced = 0;
+
+	while (1) {
+		spin_lock_irqsave(&lg_ctr_lock, flags);
+
+		switch (group) {
+		case GRP_NS_STATUS_LIST:
+			if (atomic_read(&lg_ctr[GRP_NS_STATUS_TREE]) == 0) {
+				if (announced)
+					atomic_dec(&ns_list_waiters);
+				done = true;
+				atomic_inc(&lg_ctr[GRP_NS_STATUS_LIST]);
+			} else {
+				/* rbtree being deleted; announce waiting */
+				if (!announced) {
+					atomic_inc(&ns_list_waiters);
+					announced = 1;
+				}
+			}
+			break;
+		case GRP_NS_STATUS_TREE:
+			if (atomic_read(&lg_ctr[GRP_NS_STATUS_LIST]) == 0 &&
+			    atomic_read(&ns_list_waiters) == 0) {
+				done = true;
+				atomic_inc(&lg_ctr[GRP_NS_STATUS_TREE]);
+			}
+			break;
+		}
+
+		spin_unlock_irqrestore(&lg_ctr_lock, flags);
+
+		if (done)
+			break;
+
+		/* wait until opposite group is done */
+		switch (group) {
+		case GRP_NS_STATUS_LIST:
+			wait_event_interruptible(
+				lg_wq[GRP_NS_STATUS_LIST],
+				atomic_read(&lg_ctr[GRP_NS_STATUS_TREE]) == 0);
+			break;
+		case GRP_NS_STATUS_TREE:
+			wait_event_interruptible(
+				lg_wq[GRP_NS_STATUS_TREE],
+				atomic_read(&lg_ctr[GRP_NS_STATUS_LIST]) == 0 &&
+				atomic_read(&ns_list_waiters) == 0);
+			break;
+		}
+	}
+}
+
+static void unlock_group(enum lk_group group)
+{
+	switch (group) {
+	case GRP_NS_STATUS_LIST:
+		if (atomic_dec_and_test(&lg_ctr[GRP_NS_STATUS_LIST]))
+			wake_up_interruptible_all(&lg_wq[GRP_NS_STATUS_TREE]);
+		break;
+	case GRP_NS_STATUS_TREE:
+		if (atomic_dec_and_test(&lg_ctr[GRP_NS_STATUS_TREE]))
+			wake_up_interruptible_all(&lg_wq[GRP_NS_STATUS_LIST]);
+		break;
+	}
+}
+
+static void ns_status_free(struct ima_namespace *ns, struct ns_status *status)
+{
+	pr_debug("FREE ns_status: %p\n", status);
+
+	kmem_cache_free(ns->ns_status_cache, status);
+}
+
+
+/*
+ * ima_free_ns_status_tree - free all items on the ns_status_tree and take each
+ *                           one off the list; yield to ns_list free'ers
+ *
+ * This function is called when an ima_namespace is freed. All entries in the
+ * rbtree will be taken off their list and collected in a garbage collection
+ * list and freed at the end. This allows to walk the rbtree again.
+ */
+void ima_free_ns_status_tree(struct ima_namespace *ns)
+{
+	struct ns_status *status, *next;
+	struct llist_node *node;
+	LLIST_HEAD(garbage);
+	unsigned int ctr;
+	bool restart;
+
+	do {
+		ctr = 0;
+		restart = false;
+
+		lock_group(GRP_NS_STATUS_TREE);
+		write_lock(&ns->ns_tree_lock);
+
+		rbtree_postorder_for_each_entry_safe(status, next,
+						&ns->ns_status_tree, rb_node) {
+			write_lock(&status->iint->ns_list_lock);
+			if (!list_empty(&status->ns_next)) {
+				list_del_init(&status->ns_next);
+				llist_add(&status->gc_llist, &garbage);
+				ctr++;
+			}
+			write_unlock(&status->iint->ns_list_lock);
+
+			/* After some progress yield to any waiting ns_list
+			 * free'ers.
+			 */
+			if (atomic_read(&ns_list_waiters) > 0 && ctr >= 5) {
+				restart = true;
+				break;
+			}
+		}
+
+		write_unlock(&ns->ns_tree_lock);
+		unlock_group(GRP_NS_STATUS_TREE);
+	} while (restart);
+
+	node = llist_del_all(&garbage);
+	llist_for_each_entry_safe(status, next, node, gc_llist)
+		ns_status_free(ns, status);
+
+	kmem_cache_destroy(ns->ns_status_cache);
+}
+
+/*
+ * ima_free_ns_status_list: free the list of ns_status items and take
+ *                          each one off its namespace rbtree
+ */
+void ima_free_ns_status_list(struct list_head *head, rwlock_t *ns_list_lock)
+{
+	struct ns_status *status;
+
+	lock_group(GRP_NS_STATUS_LIST);
+
+	while (1) {
+		write_lock(ns_list_lock);
+		status = list_first_entry_or_null(head,
+						  struct ns_status, ns_next);
+		if (status)
+			list_del_init(&status->ns_next);
+		write_unlock(ns_list_lock);
+
+		if (!status)
+			break;
+
+		write_lock(&status->ns->ns_tree_lock);
+
+		rb_erase(&status->rb_node, &status->ns->ns_status_tree);
+		RB_CLEAR_NODE(&status->rb_node);
+
+		write_unlock(&status->ns->ns_tree_lock);
+
+		ns_status_free(status->ns, status);
+	}
+
+	unlock_group(GRP_NS_STATUS_LIST);
+}
+
+/*
+ * ns_status_find - return the ns_status associated with an inode;
+ *                  caller must hold lock for tree
+ */
+static struct ns_status *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;
+}
+
+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);
+}
+
+static void ns_status_unlink(struct ima_namespace *ns,
+				 struct ns_status *status)
+{
+	write_lock(&status->iint->ns_list_lock);
+	if (!list_empty(&status->ns_next))
+		list_del_init(&status->ns_next);
+	write_unlock(&status->iint->ns_list_lock);
+
+	rb_erase(&status->rb_node, &ns->ns_status_tree);
+	RB_CLEAR_NODE(&status->rb_node);
+}
+
+struct ns_status *ima_get_ns_status(struct ima_namespace *ns,
+				    struct inode *inode,
+				    struct integrity_iint_cache *iint)
+{
+	struct ns_status *status;
+
+	/* Prevent finding the status via the list (inode/iint deletion) since
+	 * we may free it.
+	 */
+	lock_group(GRP_NS_STATUS_TREE);
+
+	write_lock(&ns->ns_tree_lock);
+
+	status = ns_status_find(ns, inode);
+	if (status) {
+		/* Check for ns_status with same inode but a stale iint.
+		 */
+		if (unlikely(status->iint != iint)) {
+			ns_status_unlink(ns, status);
+			ns_status_free(ns, status);
+			goto get_new;
+		} else if (inode->i_ino == status->i_ino &&
+			   inode->i_generation == status->i_generation) {
+			goto unlock;
+		}
+
+		/* Same inode number is reused, overwrite the ns_status */
+	} else {
+get_new:
+		status = kmem_cache_alloc(ns->ns_status_cache, GFP_NOFS);
+		if (!status) {
+			status = ERR_PTR(-ENOMEM);
+			goto unlock;
+		}
+
+		pr_debug("NEW  ns_status: %p\n", status);
+
+		INIT_LIST_HEAD(&status->ns_next);
+		insert_ns_status(ns, inode, status);
+	}
+
+	status->iint = iint;
+	status->inode = inode;
+	status->ns = ns;
+	status->i_ino = inode->i_ino;
+	status->i_generation = inode->i_generation;
+	status->flags = 0UL;
+
+	/* make visible on list */
+	write_lock(&iint->ns_list_lock);
+	if (list_empty(&status->ns_next))
+		list_add_tail(&status->ns_next, &iint->ns_list);
+	write_unlock(&iint->ns_list_lock);
+
+unlock:
+	write_unlock(&ns->ns_tree_lock);
+
+	unlock_group(GRP_NS_STATUS_TREE);
+
+	return status;
+}
diff --git a/security/integrity/integrity.h b/security/integrity/integrity.h
index 547425c20e11..201a9d46d6e1 100644
--- a/security/integrity/integrity.h
+++ b/security/integrity/integrity.h
@@ -138,6 +138,10 @@ struct integrity_iint_cache {
 	enum integrity_status ima_creds_status:4;
 	enum integrity_status evm_status:4;
 	struct ima_digest_data *ima_hash;
+#ifdef CONFIG_IMA_NS
+	rwlock_t ns_list_lock;
+	struct list_head ns_list;
+#endif
 };
 
 /* rbtree tree calls to lookup, insert, delete
-- 
2.31.1


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

* [PATCH v8 15/19] ima: Namespace audit status flags
  2022-01-04 17:03 [PATCH v8 00/19] ima: Namespace IMA with audit support in IMA-ns Stefan Berger
                   ` (13 preceding siblings ...)
  2022-01-04 17:04 ` [PATCH v8 14/19] integrity/ima: Define ns_status for storing namespaced iint data Stefan Berger
@ 2022-01-04 17:04 ` Stefan Berger
  2022-01-04 17:04 ` [PATCH v8 16/19] ima: Enable re-auditing of modified files Stefan Berger
                   ` (3 subsequent siblings)
  18 siblings, 0 replies; 50+ messages in thread
From: Stefan Berger @ 2022-01-04 17:04 UTC (permalink / raw)
  To: linux-integrity
  Cc: zohar, serge, christian.brauner, containers, dmitry.kasatkin,
	ebiederm, krzysztof.struczynski, roberto.sassu, mpeters, lhinds,
	lsturman, puiterwi, jejb, jamjoom, linux-kernel, paul, rgb,
	linux-security-module, jmorris, Mehmet Kayaalp, Stefan Berger

From: Mehmet Kayaalp <mkayaalp@linux.vnet.ibm.com>

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
none of the IMA_NS_STATUS_ACTIONS (currently only IMA_AUDIT) are 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 <mkayaalp@linux.vnet.ibm.com>
Signed-off-by: Stefan Berger <stefanb@linux.ibm.com>
---
 security/integrity/ima/ima.h      | 27 ++++++++++++++++++++++++++-
 security/integrity/ima/ima_api.c  |  8 +++++---
 security/integrity/ima/ima_main.c | 26 +++++++++++++++++++-------
 security/integrity/ima/ima_ns.c   | 20 ++++++++++++++++++++
 4 files changed, 70 insertions(+), 11 deletions(-)

diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h
index e4804be6b524..1d7f140138be 100644
--- a/security/integrity/ima/ima.h
+++ b/security/integrity/ima/ima.h
@@ -318,7 +318,8 @@ int process_buffer_measurement(struct ima_namespace *ns,
 			       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);
@@ -530,6 +531,14 @@ struct ns_status *ima_get_ns_status(struct ima_namespace *ns,
 
 void ima_free_ns_status_tree(struct ima_namespace *ns);
 
+#define IMA_NS_STATUS_ACTIONS   IMA_AUDIT
+#define IMA_NS_STATUS_FLAGS     IMA_AUDITED
+
+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 ima_namespace *
@@ -546,6 +555,22 @@ static inline struct ns_status *ima_get_ns_status(struct ima_namespace *ns,
 	return NULL;
 }
 
+#define IMA_NS_STATUS_ACTIONS	0
+
+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 */
 
 #endif /* __LINUX_IMA_H */
diff --git a/security/integrity/ima/ima_api.c b/security/integrity/ima/ima_api.c
index bee35ebb3a38..25163d64c057 100644
--- a/security/integrity/ima/ima_api.c
+++ b/security/integrity/ima/ima_api.c
@@ -348,14 +348,16 @@ void ima_store_measurement(struct ima_namespace *ns,
 }
 
 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);
@@ -378,7 +380,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 51b0ef1cebbe..99dc984b49c9 100644
--- a/security/integrity/ima/ima_main.c
+++ b/security/integrity/ima/ima_main.c
@@ -207,6 +207,7 @@ static int __process_measurement(struct ima_namespace *ns,
 {
 	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];
@@ -219,6 +220,7 @@ static int __process_measurement(struct ima_namespace *ns,
 	bool violation_check;
 	enum hash_algo hash_algo;
 	unsigned int allowed_algos = 0;
+	unsigned long flags;
 
 	if (!ns->ima_policy_flag || !S_ISREG(inode->i_mode))
 		return 0;
@@ -247,6 +249,14 @@ static int __process_measurement(struct ima_namespace *ns,
 		iint = integrity_inode_get(inode);
 		if (!iint)
 			rc = -ENOMEM;
+
+		if (!rc && (action & IMA_NS_STATUS_ACTIONS)) {
+			status = ima_get_ns_status(ns, inode, iint);
+			if (IS_ERR(status)) {
+				rc = PTR_ERR(status);
+				status = NULL;
+			}
+		}
 	}
 
 	if (!rc && violation_check)
@@ -262,11 +272,13 @@ static int __process_measurement(struct ima_namespace *ns,
 
 	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
@@ -277,7 +289,7 @@ static int __process_measurement(struct ima_namespace *ns,
 	    ((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;
 	}
 
@@ -285,9 +297,9 @@ static int __process_measurement(struct ima_namespace *ns,
 	 * (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)))
@@ -362,7 +374,7 @@ static int __process_measurement(struct ima_namespace *ns,
 						  &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 5a79fb6c10c0..205dd06ac41e 100644
--- a/security/integrity/ima/ima_ns.c
+++ b/security/integrity/ima/ima_ns.c
@@ -54,6 +54,26 @@ void free_ima_ns(struct user_namespace *user_ns)
 	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


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

* [PATCH v8 16/19] ima: Enable re-auditing of modified files
  2022-01-04 17:03 [PATCH v8 00/19] ima: Namespace IMA with audit support in IMA-ns Stefan Berger
                   ` (14 preceding siblings ...)
  2022-01-04 17:04 ` [PATCH v8 15/19] ima: Namespace audit status flags Stefan Berger
@ 2022-01-04 17:04 ` Stefan Berger
  2022-01-05 15:21   ` Stefan Berger
  2022-01-04 17:04 ` [PATCH v8 17/19] ima: Setup securityfs for IMA namespace Stefan Berger
                   ` (2 subsequent siblings)
  18 siblings, 1 reply; 50+ messages in thread
From: Stefan Berger @ 2022-01-04 17:04 UTC (permalink / raw)
  To: linux-integrity
  Cc: zohar, serge, christian.brauner, containers, dmitry.kasatkin,
	ebiederm, krzysztof.struczynski, roberto.sassu, mpeters, lhinds,
	lsturman, puiterwi, jejb, jamjoom, linux-kernel, paul, rgb,
	linux-security-module, jmorris, Stefan Berger

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

Walk the list of ns_status associated with an iint if the file has
changed and reset the IMA_AUDITED flag, which is part of the
IMA_DONE_MASK. This causes a new audit message to be emitted when the
file is again accessed on either the host or in an IMA namespace.

Signed-off-by: Stefan Berger <stefanb@linux.ibm.com>
---
 security/integrity/ima/ima_main.c | 33 ++++++++++++++++++++++++++++++-
 1 file changed, 32 insertions(+), 1 deletion(-)

diff --git a/security/integrity/ima/ima_main.c b/security/integrity/ima/ima_main.c
index 99dc984b49c9..bc3ab08f39c6 100644
--- a/security/integrity/ima/ima_main.c
+++ b/security/integrity/ima/ima_main.c
@@ -153,6 +153,35 @@ static void ima_rdwr_violation_check(struct ima_namespace *ns,
 				  "invalid_pcr", "open_writers");
 }
 
+#ifdef CONFIG_IMA_NS
+
+static void mask_iint_ns_status_flags(struct integrity_iint_cache *iint,
+				      unsigned long mask)
+{
+	struct ns_status *status;
+	unsigned long flags;
+
+	read_lock(&iint->ns_list_lock);
+	list_for_each_entry(status, &iint->ns_list, ns_next) {
+		flags = iint_flags(iint, status) & mask;
+		set_iint_flags(iint, status, flags);
+	}
+	read_unlock(&iint->ns_list_lock);
+}
+
+#else
+
+static void mask_iint_ns_status_flags(struct integrity_iint_cache *iint,
+				      unsigned long mask)
+{
+	unsigned long flags;
+
+	flags = iint_flags(iint, NULL) & mask;
+	set_iint_flags(iint, NULL, flags);
+}
+
+#endif
+
 static void ima_check_last_writer(struct integrity_iint_cache *iint,
 				  struct inode *inode, struct file *file)
 {
@@ -169,8 +198,10 @@ static void ima_check_last_writer(struct integrity_iint_cache *iint,
 		if (!IS_I_VERSION(inode) ||
 		    !inode_eq_iversion(inode, iint->version) ||
 		    (iint->flags & IMA_NEW_FILE)) {
-			iint->flags &= ~(IMA_DONE_MASK | IMA_NEW_FILE);
+			mask_iint_ns_status_flags(iint,
+					~(IMA_DONE_MASK | IMA_NEW_FILE));
 			iint->measured_pcrs = 0;
+
 			if (update)
 				ima_update_xattr(iint, file);
 		}
-- 
2.31.1


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

* [PATCH v8 17/19] ima: Setup securityfs for IMA namespace
  2022-01-04 17:03 [PATCH v8 00/19] ima: Namespace IMA with audit support in IMA-ns Stefan Berger
                   ` (15 preceding siblings ...)
  2022-01-04 17:04 ` [PATCH v8 16/19] ima: Enable re-auditing of modified files Stefan Berger
@ 2022-01-04 17:04 ` Stefan Berger
  2022-01-04 17:04 ` [PATCH v8 18/19] ima: Show owning user namespace's uid and gid when displaying policy Stefan Berger
  2022-01-04 17:04 ` [PATCH v8 19/19] ima: Enable IMA namespaces Stefan Berger
  18 siblings, 0 replies; 50+ messages in thread
From: Stefan Berger @ 2022-01-04 17:04 UTC (permalink / raw)
  To: linux-integrity
  Cc: zohar, serge, christian.brauner, containers, dmitry.kasatkin,
	ebiederm, krzysztof.struczynski, roberto.sassu, mpeters, lhinds,
	lsturman, puiterwi, jejb, jamjoom, linux-kernel, paul, rgb,
	linux-security-module, jmorris, Stefan Berger, James Bottomley

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

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 available
when IMA namespacing is enabled, otherwise it will be empty:

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

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

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

diff --git a/include/linux/ima.h b/include/linux/ima.h
index e236eef5b486..5354e83d1694 100644
--- a/include/linux/ima.h
+++ b/include/linux/ima.h
@@ -41,6 +41,7 @@ extern int ima_measure_critical_data(const char *event_label,
 				     const char *event_name,
 				     const void *buf, size_t buf_len,
 				     bool hash, u8 *digest, size_t digest_len);
+extern int ima_fs_ns_init(struct user_namespace *user_ns, struct dentry *root);
 
 #ifdef CONFIG_IMA_APPRAISE_BOOTPARAM
 extern void ima_appraise_parse_cmdline(void);
@@ -234,12 +235,24 @@ void free_ima_ns(struct user_namespace *ns);
 extern void ima_free_ns_status_list(struct list_head *head,
 				    rwlock_t *ns_list_lock);
 
+static inline int ima_securityfs_init(struct user_namespace *user_ns,
+				      struct dentry *root)
+{
+	return ima_fs_ns_init(user_ns, root);
+}
+
 #else
 
 static inline void free_ima_ns(struct user_namespace *user_ns)
 {
 }
 
+static inline int ima_securityfs_init(struct user_namespace *ns,
+				      struct dentry *root)
+{
+	return 0;
+}
+
 #endif /* CONFIG_IMA_NS */
 
 #endif /* _LINUX_IMA_H */
diff --git a/security/inode.c b/security/inode.c
index e525ba960063..cdb08520151c 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>
@@ -81,7 +82,10 @@ static int securityfs_fill_super(struct super_block *sb, struct fs_context *fc)
 	sb->s_op = &securityfs_super_operations;
 	sb->s_root->d_inode->i_op = &securityfs_dir_inode_operations;
 
-	return 0;
+	if (ns != &init_user_ns)
+		error = ima_securityfs_init(ns, sb->s_root);
+
+	return error;
 }
 
 static int securityfs_get_tree(struct fs_context *fc)
diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h
index 1d7f140138be..344c8c4bd030 100644
--- a/security/integrity/ima/ima.h
+++ b/security/integrity/ima/ima.h
@@ -146,6 +146,7 @@ struct ima_namespace {
 	int valid_policy;
 
 	struct dentry *policy_dentry;
+	bool policy_dentry_removed;
 } __randomize_layout;
 extern struct ima_namespace init_ima_ns;
 
diff --git a/security/integrity/ima/ima_fs.c b/security/integrity/ima/ima_fs.c
index 71302bb5427f..468508f6a7e8 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"
 
@@ -431,6 +432,7 @@ static int ima_release_policy(struct inode *inode, struct file *file)
 #if !defined(CONFIG_IMA_WRITE_POLICY) && !defined(CONFIG_IMA_READ_POLICY)
 	securityfs_remove(ns->policy_dentry);
 	ns->policy_dentry = NULL;
+	ns->policy_dentry_removed = true;
 #elif defined(CONFIG_IMA_WRITE_POLICY)
 	clear_bit(IMA_FS_BUSY, &ns->ima_fs_flags);
 #elif defined(CONFIG_IMA_READ_POLICY)
@@ -447,20 +449,30 @@ static const struct file_operations ima_measure_policy_ops = {
 	.llseek = generic_file_llseek,
 };
 
-static int __init ima_fs_ns_init(struct ima_namespace *ns)
+int ima_fs_ns_init(struct user_namespace *user_ns, struct dentry *root)
 {
-	struct dentry *ima_dir;
+	struct ima_namespace *ns = ima_ns_from_user_ns(user_ns);
+	struct dentry *int_dir;
+	struct dentry *ima_dir = NULL;
 	struct dentry *ima_symlink = NULL;
 	struct dentry *binary_runtime_measurements = NULL;
 	struct dentry *ascii_runtime_measurements = NULL;
 	struct dentry *runtime_measurements_count = NULL;
 	struct dentry *violations = NULL;
 
-	ima_dir = securityfs_create_dir("ima", integrity_dir);
+	/* FIXME: update when evm and integrity are namespaced */
+	if (user_ns != &init_user_ns) {
+		int_dir = securityfs_create_dir("integrity", root);
+		if (IS_ERR(int_dir))
+			return PTR_ERR(int_dir);
+	} else
+		int_dir = integrity_dir;
+
+	ima_dir = securityfs_create_dir("ima", int_dir);
 	if (IS_ERR(ima_dir))
-		return -1;
+		goto out;
 
-	ima_symlink = securityfs_create_symlink("ima", NULL, "integrity/ima",
+	ima_symlink = securityfs_create_symlink("ima", root, "integrity/ima",
 						NULL);
 	if (IS_ERR(ima_symlink))
 		goto out;
@@ -492,11 +504,15 @@ static int __init ima_fs_ns_init(struct ima_namespace *ns)
 	if (IS_ERR(violations))
 		goto out;
 
-	ns->policy_dentry = securityfs_create_file("policy", POLICY_FILE_FLAGS,
-					    ima_dir, NULL,
-					    &ima_measure_policy_ops);
-	if (IS_ERR(ns->policy_dentry))
-		goto out;
+
+	if (!ns->policy_dentry_removed) {
+		ns->policy_dentry =
+		    securityfs_create_file("policy", POLICY_FILE_FLAGS,
+					   ima_dir, NULL,
+					   &ima_measure_policy_ops);
+		if (IS_ERR(ns->policy_dentry))
+			goto out;
+	}
 
 	return 0;
 out:
@@ -507,10 +523,13 @@ static int __init ima_fs_ns_init(struct ima_namespace *ns)
 	securityfs_remove(binary_runtime_measurements);
 	securityfs_remove(ima_symlink);
 	securityfs_remove(ima_dir);
+	if (user_ns != &init_user_ns)
+		securityfs_remove(int_dir);
+
 	return -1;
 }
 
 int __init ima_fs_init(void)
 {
-	return ima_fs_ns_init(&init_ima_ns);
+	return ima_fs_ns_init(&init_user_ns, NULL);
 }
-- 
2.31.1


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

* [PATCH v8 18/19] ima: Show owning user namespace's uid and gid when displaying policy
  2022-01-04 17:03 [PATCH v8 00/19] ima: Namespace IMA with audit support in IMA-ns Stefan Berger
                   ` (16 preceding siblings ...)
  2022-01-04 17:04 ` [PATCH v8 17/19] ima: Setup securityfs for IMA namespace Stefan Berger
@ 2022-01-04 17:04 ` Stefan Berger
  2022-01-14 13:45   ` Christian Brauner
  2022-01-04 17:04 ` [PATCH v8 19/19] ima: Enable IMA namespaces Stefan Berger
  18 siblings, 1 reply; 50+ messages in thread
From: Stefan Berger @ 2022-01-04 17:04 UTC (permalink / raw)
  To: linux-integrity
  Cc: zohar, serge, christian.brauner, containers, dmitry.kasatkin,
	ebiederm, krzysztof.struczynski, roberto.sassu, mpeters, lhinds,
	lsturman, puiterwi, jejb, jamjoom, linux-kernel, paul, rgb,
	linux-security-module, jmorris, Stefan Berger

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

Show the uid and gid values of the owning user namespace when displaying
the IMA policy rather than the kernel uid and gid values. Now the same uid
and gid values are shown in the policy as those that were used when the
policy was set.

Signed-off-by: Stefan Berger <stefanb@linux.ibm.com>
---
 security/integrity/ima/ima_policy.c | 19 +++++++++++++------
 1 file changed, 13 insertions(+), 6 deletions(-)

diff --git a/security/integrity/ima/ima_policy.c b/security/integrity/ima/ima_policy.c
index 15c68dc5da9e..b7dbc687b6ff 100644
--- a/security/integrity/ima/ima_policy.c
+++ b/security/integrity/ima/ima_policy.c
@@ -1997,6 +1997,7 @@ static void ima_policy_show_appraise_algos(struct seq_file *m,
 
 int ima_policy_show(struct seq_file *m, void *v)
 {
+	struct user_namespace *user_ns = ima_user_ns_from_file(m->file);
 	struct ima_rule_entry *entry = v;
 	int i;
 	char tbuf[64] = {0,};
@@ -2074,7 +2075,8 @@ int ima_policy_show(struct seq_file *m, void *v)
 	}
 
 	if (entry->flags & IMA_UID) {
-		snprintf(tbuf, sizeof(tbuf), "%d", __kuid_val(entry->uid));
+		snprintf(tbuf, sizeof(tbuf),
+			 "%d", from_kuid(user_ns, entry->uid));
 		if (entry->uid_op == &uid_gt)
 			seq_printf(m, pt(Opt_uid_gt), tbuf);
 		else if (entry->uid_op == &uid_lt)
@@ -2085,7 +2087,8 @@ int ima_policy_show(struct seq_file *m, void *v)
 	}
 
 	if (entry->flags & IMA_EUID) {
-		snprintf(tbuf, sizeof(tbuf), "%d", __kuid_val(entry->uid));
+		snprintf(tbuf, sizeof(tbuf),
+			 "%d", from_kuid(user_ns, entry->uid));
 		if (entry->uid_op == &uid_gt)
 			seq_printf(m, pt(Opt_euid_gt), tbuf);
 		else if (entry->uid_op == &uid_lt)
@@ -2096,7 +2099,8 @@ int ima_policy_show(struct seq_file *m, void *v)
 	}
 
 	if (entry->flags & IMA_GID) {
-		snprintf(tbuf, sizeof(tbuf), "%d", __kgid_val(entry->gid));
+		snprintf(tbuf, sizeof(tbuf),
+			 "%d", from_kgid(user_ns, entry->gid));
 		if (entry->gid_op == &gid_gt)
 			seq_printf(m, pt(Opt_gid_gt), tbuf);
 		else if (entry->gid_op == &gid_lt)
@@ -2107,7 +2111,8 @@ int ima_policy_show(struct seq_file *m, void *v)
 	}
 
 	if (entry->flags & IMA_EGID) {
-		snprintf(tbuf, sizeof(tbuf), "%d", __kgid_val(entry->gid));
+		snprintf(tbuf, sizeof(tbuf),
+			 "%d", from_kgid(user_ns, entry->gid));
 		if (entry->gid_op == &gid_gt)
 			seq_printf(m, pt(Opt_egid_gt), tbuf);
 		else if (entry->gid_op == &gid_lt)
@@ -2118,7 +2123,8 @@ int ima_policy_show(struct seq_file *m, void *v)
 	}
 
 	if (entry->flags & IMA_FOWNER) {
-		snprintf(tbuf, sizeof(tbuf), "%d", __kuid_val(entry->fowner));
+		snprintf(tbuf, sizeof(tbuf),
+			 "%d", from_kuid(user_ns, entry->fowner));
 		if (entry->fowner_op == &uid_gt)
 			seq_printf(m, pt(Opt_fowner_gt), tbuf);
 		else if (entry->fowner_op == &uid_lt)
@@ -2129,7 +2135,8 @@ int ima_policy_show(struct seq_file *m, void *v)
 	}
 
 	if (entry->flags & IMA_FGROUP) {
-		snprintf(tbuf, sizeof(tbuf), "%d", __kgid_val(entry->fgroup));
+		snprintf(tbuf, sizeof(tbuf),
+			 "%d", from_kgid(user_ns, entry->fgroup));
 		if (entry->fgroup_op == &gid_gt)
 			seq_printf(m, pt(Opt_fgroup_gt), tbuf);
 		else if (entry->fgroup_op == &gid_lt)
-- 
2.31.1


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

* [PATCH v8 19/19] ima: Enable IMA namespaces
  2022-01-04 17:03 [PATCH v8 00/19] ima: Namespace IMA with audit support in IMA-ns Stefan Berger
                   ` (17 preceding siblings ...)
  2022-01-04 17:04 ` [PATCH v8 18/19] ima: Show owning user namespace's uid and gid when displaying policy Stefan Berger
@ 2022-01-04 17:04 ` Stefan Berger
  2022-01-14 12:05   ` Christian Brauner
  2022-01-14 14:45   ` Christian Brauner
  18 siblings, 2 replies; 50+ messages in thread
From: Stefan Berger @ 2022-01-04 17:04 UTC (permalink / raw)
  To: linux-integrity
  Cc: zohar, serge, christian.brauner, containers, dmitry.kasatkin,
	ebiederm, krzysztof.struczynski, roberto.sassu, mpeters, lhinds,
	lsturman, puiterwi, jejb, jamjoom, linux-kernel, paul, rgb,
	linux-security-module, jmorris, Stefan Berger

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

Introduce the IMA_NS in Kconfig for IMA namespace enablement.

Enable the lazy initialization of an IMA namespace when a user mounts
SecurityFS. Now a user_namespace will get a pointer to an ima_namespace
and therefore add an implementation of get_current_ns() that returns this
pointer.

get_current_ns() may now return a NULL pointer for as long as the IMA
namespace hasn't been created, yet. Therefore, return early from those
functions that may now get a NULL pointer from this call. The NULL
pointer can typically be treated similar to not having an IMA policy set
and simply return early from a function.

Implement ima_ns_from_file() for SecurityFS-related files where we can
now get the IMA namespace via the user namespace pointer associated
with the superblock of the SecurityFS filesystem instance. Since
the functions using ima_ns_from_file() will only be called after an
ima_namesapce has been allocated they will never get a NULL pointer
for the ima_namespace.

Switch access to userns->ima_ns to use acquire/release semantics to ensure
that a newly created ima_namespace structure is fully visible upon access.

Replace usage of current_user_ns() with ima_ns_from_user_ns() that
implements a method to derive the user_namespace from the given
ima_namespace. It leads to the same result.

Signed-off-by: Stefan Berger <stefanb@linux.ibm.com>
---
 include/linux/ima.h                 |  9 ++++++-
 init/Kconfig                        | 13 ++++++++++
 kernel/user_namespace.c             |  2 ++
 security/integrity/ima/ima.h        | 35 ++++++++++++++++++++++-----
 security/integrity/ima/ima_fs.c     | 37 ++++++++++++++++++++++-------
 security/integrity/ima/ima_main.c   | 29 ++++++++++++++++------
 security/integrity/ima/ima_ns.c     |  3 ++-
 security/integrity/ima/ima_policy.c | 13 +++++-----
 8 files changed, 112 insertions(+), 29 deletions(-)

diff --git a/include/linux/ima.h b/include/linux/ima.h
index 5354e83d1694..7b9713b290ae 100644
--- a/include/linux/ima.h
+++ b/include/linux/ima.h
@@ -11,6 +11,7 @@
 #include <linux/fs.h>
 #include <linux/security.h>
 #include <linux/kexec.h>
+#include <linux/user_namespace.h>
 #include <crypto/hash_info.h>
 struct linux_binprm;
 
@@ -71,7 +72,13 @@ static inline const char * const *arch_get_ima_policy(void)
 static inline struct user_namespace
 *ima_ns_to_user_ns(struct ima_namespace *ns)
 {
-	return current_user_ns();
+	struct user_namespace *user_ns;
+
+	user_ns = current_user_ns();
+#ifdef CONFIG_IMA_NS
+	WARN_ON(user_ns->ima_ns != ns);
+#endif
+	return user_ns;
 }
 
 #else
diff --git a/init/Kconfig b/init/Kconfig
index 4b7bac10c72d..e27155e0ddba 100644
--- a/init/Kconfig
+++ b/init/Kconfig
@@ -1247,6 +1247,19 @@ config NET_NS
 	  Allow user space to create what appear to be multiple instances
 	  of the network stack.
 
+config IMA_NS
+	bool "IMA namespace"
+	depends on USER_NS
+	depends on IMA
+	default n
+	help
+	  Allow the creation of an IMA namespace 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
 
 config CHECKPOINT_RESTORE
diff --git a/kernel/user_namespace.c b/kernel/user_namespace.c
index 6b2e3ca7ee99..653f8fa83b69 100644
--- a/kernel/user_namespace.c
+++ b/kernel/user_namespace.c
@@ -20,6 +20,7 @@
 #include <linux/fs_struct.h>
 #include <linux/bsearch.h>
 #include <linux/sort.h>
+#include <linux/ima.h>
 
 static struct kmem_cache *user_ns_cachep __read_mostly;
 static DEFINE_MUTEX(userns_state_mutex);
@@ -196,6 +197,7 @@ static void free_user_ns(struct work_struct *work)
 			kfree(ns->projid_map.forward);
 			kfree(ns->projid_map.reverse);
 		}
+		free_ima_ns(ns);
 		retire_userns_sysctls(ns);
 		key_free_user_ns(ns);
 		ns_free_inum(&ns->ns);
diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h
index 344c8c4bd030..d993655ec796 100644
--- a/security/integrity/ima/ima.h
+++ b/security/integrity/ima/ima.h
@@ -509,21 +509,20 @@ struct user_namespace *ima_user_ns_from_file(const struct file *filp)
 	return file_inode(filp)->i_sb->s_user_ns;
 }
 
+#ifdef CONFIG_IMA_NS
+
 static inline struct ima_namespace
 *ima_ns_from_user_ns(struct user_namespace *user_ns)
 {
-	if (user_ns == &init_user_ns)
-		return &init_ima_ns;
-	return NULL;
+	/* Pairs with smp_store_releases() in create_ima_ns(). */
+	return smp_load_acquire(&user_ns->ima_ns);
 }
 
 static inline struct ima_namespace *get_current_ns(void)
 {
-	return &init_ima_ns;
+	return ima_ns_from_user_ns(current_user_ns());
 }
 
-#ifdef CONFIG_IMA_NS
-
 struct ima_namespace *create_ima_ns(struct user_namespace *user_ns);
 
 struct ns_status *ima_get_ns_status(struct ima_namespace *ns,
@@ -532,6 +531,11 @@ struct ns_status *ima_get_ns_status(struct ima_namespace *ns,
 
 void ima_free_ns_status_tree(struct ima_namespace *ns);
 
+static inline struct ima_namespace *ima_ns_from_file(const struct file *filp)
+{
+	return ima_user_ns_from_file(filp)->ima_ns;
+}
+
 #define IMA_NS_STATUS_ACTIONS   IMA_AUDIT
 #define IMA_NS_STATUS_FLAGS     IMA_AUDITED
 
@@ -542,6 +546,20 @@ unsigned long set_iint_flags(struct integrity_iint_cache *iint,
 
 #else
 
+static inline struct ima_namespace
+*ima_ns_from_user_ns(struct user_namespace *user_ns)
+{
+	if (user_ns == &init_user_ns)
+		return &init_ima_ns;
+	return NULL;
+}
+
+
+static inline struct ima_namespace *get_current_ns(void)
+{
+	return &init_ima_ns;
+}
+
 static inline struct ima_namespace *
 create_ima_ns(struct user_namespace *user_ns)
 {
@@ -572,6 +590,11 @@ static inline unsigned long set_iint_flags(struct integrity_iint_cache *iint,
 	return flags;
 }
 
+static inline struct ima_namespace *ima_ns_from_file(const struct file *filp)
+{
+	return &init_ima_ns;
+}
+
 #endif /* CONFIG_IMA_NS */
 
 #endif /* __LINUX_IMA_H */
diff --git a/security/integrity/ima/ima_fs.c b/security/integrity/ima/ima_fs.c
index 468508f6a7e8..ee3af81d1c3e 100644
--- a/security/integrity/ima/ima_fs.c
+++ b/security/integrity/ima/ima_fs.c
@@ -49,7 +49,7 @@ static ssize_t ima_show_htable_violations(struct file *filp,
 					  char __user *buf,
 					  size_t count, loff_t *ppos)
 {
-	struct ima_namespace *ns = &init_ima_ns;
+	struct ima_namespace *ns = ima_ns_from_file(filp);
 
 	return ima_show_htable_value(buf, count, ppos,
 				     &ns->ima_htable.violations);
@@ -64,7 +64,7 @@ static ssize_t ima_show_measurements_count(struct file *filp,
 					   char __user *buf,
 					   size_t count, loff_t *ppos)
 {
-	struct ima_namespace *ns = &init_ima_ns;
+	struct ima_namespace *ns = ima_ns_from_file(filp);
 
 	return ima_show_htable_value(buf, count, ppos, &ns->ima_htable.len);
 }
@@ -77,7 +77,7 @@ static const struct file_operations ima_measurements_count_ops = {
 /* returns pointer to hlist_node */
 static void *ima_measurements_start(struct seq_file *m, loff_t *pos)
 {
-	struct ima_namespace *ns = &init_ima_ns;
+	struct ima_namespace *ns = ima_ns_from_file(m->file);
 	loff_t l = *pos;
 	struct ima_queue_entry *qe;
 
@@ -95,7 +95,7 @@ static void *ima_measurements_start(struct seq_file *m, loff_t *pos)
 
 static void *ima_measurements_next(struct seq_file *m, void *v, loff_t *pos)
 {
-	struct ima_namespace *ns = &init_ima_ns;
+	struct ima_namespace *ns = ima_ns_from_file(m->file);
 	struct ima_queue_entry *qe = v;
 
 	/* lock protects when reading beyond last element
@@ -317,7 +317,7 @@ static ssize_t ima_read_policy(struct ima_namespace *ns, char *path)
 static ssize_t ima_write_policy(struct file *file, const char __user *buf,
 				size_t datalen, loff_t *ppos)
 {
-	struct ima_namespace *ns = &init_ima_ns;
+	struct ima_namespace *ns = ima_ns_from_file(file);
 	char *data;
 	ssize_t result;
 
@@ -379,7 +379,7 @@ static const struct seq_operations ima_policy_seqops = {
 static int ima_open_policy(struct inode *inode, struct file *filp)
 {
 	struct user_namespace *user_ns = ima_user_ns_from_file(filp);
-	struct ima_namespace *ns = &init_ima_ns;
+	struct ima_namespace *ns = ima_ns_from_file(filp);
 
 	if (!(filp->f_flags & O_WRONLY)) {
 #ifndef	CONFIG_IMA_READ_POLICY
@@ -406,7 +406,7 @@ static int ima_open_policy(struct inode *inode, struct file *filp)
  */
 static int ima_release_policy(struct inode *inode, struct file *file)
 {
-	struct ima_namespace *ns = &init_ima_ns;
+	struct ima_namespace *ns = ima_ns_from_file(file);
 	const char *cause = ns->valid_policy ? "completed" : "failed";
 
 	if ((file->f_flags & O_ACCMODE) == O_RDONLY)
@@ -459,12 +459,29 @@ int ima_fs_ns_init(struct user_namespace *user_ns, struct dentry *root)
 	struct dentry *ascii_runtime_measurements = NULL;
 	struct dentry *runtime_measurements_count = NULL;
 	struct dentry *violations = NULL;
+	bool created_ns = false;
+
+	/*
+	 * While multiple superblocks can exist they are keyed by userns in
+	 * s_fs_info for securityfs. The first time a userns mounts a
+	 * securityfs instance we lazily allocate the ima_namespace for the
+	 * userns since that's the only way a userns can meaningfully use ima.
+	 * The vfs ensures we're the only one to call fill_super() and hence
+	 * ima_fs_ns_init(), so we don't need any memory barriers here, i.e.
+	 * user_ns->ima_ns can't change while we're in here.
+	 */
+	if (!ns) {
+		ns = create_ima_ns(user_ns);
+		if (IS_ERR(ns))
+			return PTR_ERR(ns);
+		created_ns = true;
+	}
 
 	/* FIXME: update when evm and integrity are namespaced */
 	if (user_ns != &init_user_ns) {
 		int_dir = securityfs_create_dir("integrity", root);
 		if (IS_ERR(int_dir))
-			return PTR_ERR(int_dir);
+			goto free_ns;
 	} else
 		int_dir = integrity_dir;
 
@@ -526,6 +543,10 @@ int ima_fs_ns_init(struct user_namespace *user_ns, struct dentry *root)
 	if (user_ns != &init_user_ns)
 		securityfs_remove(int_dir);
 
+free_ns:
+	if (created_ns)
+		free_ima_ns(user_ns);
+
 	return -1;
 }
 
diff --git a/security/integrity/ima/ima_main.c b/security/integrity/ima/ima_main.c
index bc3ab08f39c6..fc878577cdd7 100644
--- a/security/integrity/ima/ima_main.c
+++ b/security/integrity/ima/ima_main.c
@@ -483,7 +483,7 @@ int ima_file_mmap(struct file *file, unsigned long prot)
 	struct ima_namespace *ns = get_current_ns();
 	u32 secid;
 
-	if (file && (prot & PROT_EXEC)) {
+	if (ns && file && (prot & PROT_EXEC)) {
 		security_task_getsecid_subj(current, &secid);
 		return process_measurement(ns, file, current_cred(), secid,
 					   NULL, 0, MAY_EXEC, MMAP_CHECK);
@@ -519,7 +519,7 @@ int ima_file_mprotect(struct vm_area_struct *vma, unsigned long prot)
 	int pcr;
 
 	/* Is mprotect making an mmap'ed file executable? */
-	if (!(ns->ima_policy_flag & IMA_APPRAISE) || !vma->vm_file ||
+	if (!ns || !(ns->ima_policy_flag & IMA_APPRAISE) || !vma->vm_file ||
 	    !(prot & PROT_EXEC) || (vma->vm_flags & VM_EXEC))
 		return 0;
 
@@ -565,6 +565,9 @@ int ima_bprm_check(struct linux_binprm *bprm)
 	int ret;
 	u32 secid;
 
+	if (!ns)
+		return 0;
+
 	security_task_getsecid_subj(current, &secid);
 	ret = process_measurement(ns, bprm->file, current_cred(), secid, NULL,
 				  0, MAY_EXEC, BPRM_CHECK);
@@ -591,6 +594,9 @@ int ima_file_check(struct file *file, int mask)
 	struct ima_namespace *ns = get_current_ns();
 	u32 secid;
 
+	if (!ns)
+		return 0;
+
 	security_task_getsecid_subj(current, &secid);
 	return process_measurement(ns, file, current_cred(), secid, NULL, 0,
 				   mask & (MAY_READ | MAY_WRITE | MAY_EXEC |
@@ -656,7 +662,7 @@ int ima_file_hash(struct file *file, char *buf, size_t buf_size)
 {
 	struct ima_namespace *ns = get_current_ns();
 
-	if (!file)
+	if (!ns || !file)
 		return -EINVAL;
 
 	return __ima_inode_hash(ns, file_inode(file), buf, buf_size);
@@ -685,7 +691,7 @@ int ima_inode_hash(struct inode *inode, char *buf, size_t buf_size)
 {
 	struct ima_namespace *ns = get_current_ns();
 
-	if (!inode)
+	if (!ns || !inode)
 		return -EINVAL;
 
 	return __ima_inode_hash(ns, inode, buf, buf_size);
@@ -708,7 +714,7 @@ void ima_post_create_tmpfile(struct user_namespace *mnt_userns,
 	struct integrity_iint_cache *iint;
 	int must_appraise;
 
-	if (!ns->ima_policy_flag || !S_ISREG(inode->i_mode))
+	if (!ns || !ns->ima_policy_flag || !S_ISREG(inode->i_mode))
 		return;
 
 	must_appraise = ima_must_appraise(ns, mnt_userns, inode, MAY_ACCESS,
@@ -742,7 +748,7 @@ void ima_post_path_mknod(struct user_namespace *mnt_userns,
 	struct inode *inode = dentry->d_inode;
 	int must_appraise;
 
-	if (!ns->ima_policy_flag || !S_ISREG(inode->i_mode))
+	if (!ns || !ns->ima_policy_flag || !S_ISREG(inode->i_mode))
 		return;
 
 	must_appraise = ima_must_appraise(ns, mnt_userns, inode, MAY_ACCESS,
@@ -778,6 +784,9 @@ int ima_read_file(struct file *file, enum kernel_read_file_id read_id,
 	enum ima_hooks func;
 	u32 secid;
 
+	if (!ns)
+		return 0;
+
 	/*
 	 * Do devices using pre-allocated memory run the risk of the
 	 * firmware being accessible to the device prior to the completion
@@ -829,6 +838,9 @@ int ima_post_read_file(struct file *file, void *buf, loff_t size,
 	enum ima_hooks func;
 	u32 secid;
 
+	if (!ns)
+		return 0;
+
 	/* permit signed certs */
 	if (!file && read_id == READING_X509_CERTIFICATE)
 		return 0;
@@ -1071,7 +1083,7 @@ 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)
+	if (!ns || !buf || !size)
 		return;
 
 	f = fdget(kernel_fd);
@@ -1111,6 +1123,9 @@ int ima_measure_critical_data(const char *event_label,
 {
 	struct ima_namespace *ns = get_current_ns();
 
+	if (!ns)
+		return -EINVAL;
+
 	if (!event_name || !event_label || !buf || !buf_len)
 		return -ENOPARAM;
 
diff --git a/security/integrity/ima/ima_ns.c b/security/integrity/ima/ima_ns.c
index 205dd06ac41e..9a5105d0ea89 100644
--- a/security/integrity/ima/ima_ns.c
+++ b/security/integrity/ima/ima_ns.c
@@ -26,7 +26,8 @@ struct ima_namespace *create_ima_ns(struct user_namespace *user_ns)
 	if (err)
 		goto fail_free;
 
-	user_ns->ima_ns = ns;
+	/* Pairs with smp_load_acquire() in ima_ns_from_user_ns(). */
+	smp_store_release(&user_ns->ima_ns, ns);
 
 	return ns;
 
diff --git a/security/integrity/ima/ima_policy.c b/security/integrity/ima/ima_policy.c
index b7dbc687b6ff..5a9b511ebbae 100644
--- a/security/integrity/ima/ima_policy.c
+++ b/security/integrity/ima/ima_policy.c
@@ -1333,6 +1333,7 @@ static unsigned int ima_parse_appraise_algos(char *arg)
 static int ima_parse_rule(struct ima_namespace *ns,
 			  char *rule, struct ima_rule_entry *entry)
 {
+	struct user_namespace *user_ns = ima_ns_to_user_ns(ns);
 	struct audit_buffer *ab;
 	char *from;
 	char *p;
@@ -1582,7 +1583,7 @@ static int ima_parse_rule(struct ima_namespace *ns,
 
 			result = kstrtoul(args[0].from, 10, &lnum);
 			if (!result) {
-				entry->uid = make_kuid(current_user_ns(),
+				entry->uid = make_kuid(user_ns,
 						       (uid_t) lnum);
 				if (!uid_valid(entry->uid) ||
 				    (uid_t)lnum != lnum)
@@ -1617,7 +1618,7 @@ static int ima_parse_rule(struct ima_namespace *ns,
 
 			result = kstrtoul(args[0].from, 10, &lnum);
 			if (!result) {
-				entry->gid = make_kgid(current_user_ns(),
+				entry->gid = make_kgid(user_ns,
 						       (gid_t)lnum);
 				if (!gid_valid(entry->gid) ||
 				    (((gid_t)lnum) != lnum))
@@ -1644,7 +1645,7 @@ static int ima_parse_rule(struct ima_namespace *ns,
 
 			result = kstrtoul(args[0].from, 10, &lnum);
 			if (!result) {
-				entry->fowner = make_kuid(current_user_ns(),
+				entry->fowner = make_kuid(user_ns,
 							  (uid_t)lnum);
 				if (!uid_valid(entry->fowner) ||
 				    (((uid_t)lnum) != lnum))
@@ -1670,7 +1671,7 @@ static int ima_parse_rule(struct ima_namespace *ns,
 
 			result = kstrtoul(args[0].from, 10, &lnum);
 			if (!result) {
-				entry->fgroup = make_kgid(current_user_ns(),
+				entry->fgroup = make_kgid(user_ns,
 							  (gid_t)lnum);
 				if (!gid_valid(entry->fgroup) ||
 				    (((gid_t)lnum) != lnum))
@@ -1920,7 +1921,7 @@ static const char *const mask_tokens[] = {
 
 void *ima_policy_start(struct seq_file *m, loff_t *pos)
 {
-	struct ima_namespace *ns = &init_ima_ns;
+	struct ima_namespace *ns = ima_ns_from_file(m->file);
 	loff_t l = *pos;
 	struct ima_rule_entry *entry;
 	struct list_head *ima_rules_tmp;
@@ -1939,7 +1940,7 @@ void *ima_policy_start(struct seq_file *m, loff_t *pos)
 
 void *ima_policy_next(struct seq_file *m, void *v, loff_t *pos)
 {
-	struct ima_namespace *ns = &init_ima_ns;
+	struct ima_namespace *ns = ima_ns_from_file(m->file);
 	struct ima_rule_entry *entry = v;
 
 	rcu_read_lock();
-- 
2.31.1


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

* Re: [PATCH v8 01/19] securityfs: Extend securityfs with namespacing support
  2022-01-04 17:03 ` [PATCH v8 01/19] securityfs: Extend securityfs with namespacing support Stefan Berger
@ 2022-01-05  3:58   ` Al Viro
  2022-01-05 10:18     ` Christian Brauner
  0 siblings, 1 reply; 50+ messages in thread
From: Al Viro @ 2022-01-05  3:58 UTC (permalink / raw)
  To: Stefan Berger
  Cc: linux-integrity, zohar, serge, christian.brauner, containers,
	dmitry.kasatkin, ebiederm, krzysztof.struczynski, roberto.sassu,
	mpeters, lhinds, lsturman, puiterwi, jejb, jamjoom, linux-kernel,
	paul, rgb, linux-security-module, jmorris, Stefan Berger,
	James Bottomley

On Tue, Jan 04, 2022 at 12:03:58PM -0500, Stefan Berger wrote:
> From: Stefan Berger <stefanb@linux.ibm.com>
> 
> To prepare for virtualization of SecurityFS, use simple_pin_fs and
> simpe_release_fs only when init_user_ns is active.
> 
> Extend 'securityfs' for support of IMA namespacing so that each
> IMA (user) namespace can have its own front-end for showing the currently
> active policy, the measurement list, number of violations and so on.
> 
> Enable multiple instances of securityfs by keying each instance with a
> pointer to the user namespace it belongs to.
> 
> Drop the additional dentry reference to enable simple cleanup of dentries
> upon umount. Now the dentries do not need to be explicitly freed anymore
> but we can just rely on d_genocide() and the dcache shrinker to do all
> the required work.

Looks brittle...  What are the new rules for securityfs_remove()?  Is it
still paired with securityfs_create_...()?  When is removal done?  On
securityfs instance shutdown?  What about the underlying data structures, BTW?
When can they be freed?

That kind of commit message is asking for trouble down the road; please,
document the rules properly.

Incidentally, what happens if you open a file, pass it to somebody in a
different userns and try to shut the opener's userns down?

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

* Re: [PATCH v8 01/19] securityfs: Extend securityfs with namespacing support
  2022-01-05  3:58   ` Al Viro
@ 2022-01-05 10:18     ` Christian Brauner
  2022-01-11 12:16       ` Mimi Zohar
  0 siblings, 1 reply; 50+ messages in thread
From: Christian Brauner @ 2022-01-05 10:18 UTC (permalink / raw)
  To: Al Viro
  Cc: Stefan Berger, linux-integrity, zohar, serge, containers,
	dmitry.kasatkin, ebiederm, krzysztof.struczynski, roberto.sassu,
	mpeters, lhinds, lsturman, puiterwi, jejb, jamjoom, linux-kernel,
	paul, rgb, linux-security-module, jmorris, Stefan Berger,
	James Bottomley

On Wed, Jan 05, 2022 at 03:58:11AM +0000, Al Viro wrote:
> On Tue, Jan 04, 2022 at 12:03:58PM -0500, Stefan Berger wrote:
> > From: Stefan Berger <stefanb@linux.ibm.com>
> > 
> > To prepare for virtualization of SecurityFS, use simple_pin_fs and
> > simpe_release_fs only when init_user_ns is active.
> > 
> > Extend 'securityfs' for support of IMA namespacing so that each
> > IMA (user) namespace can have its own front-end for showing the currently
> > active policy, the measurement list, number of violations and so on.
> > 
> > Enable multiple instances of securityfs by keying each instance with a
> > pointer to the user namespace it belongs to.
> > 
> > Drop the additional dentry reference to enable simple cleanup of dentries
> > upon umount. Now the dentries do not need to be explicitly freed anymore
> > but we can just rely on d_genocide() and the dcache shrinker to do all
> > the required work.
> 
> Looks brittle...  What are the new rules for securityfs_remove()?  Is it
> still paired with securityfs_create_...()?  When is removal done?  On
> securityfs instance shutdown?  What about the underlying data structures, BTW?
> When can they be freed?
> 
> That kind of commit message is asking for trouble down the road; please,
> document the rules properly.

Yeah, it's not explaining it in detail. I've asked for that as well. My
explanations below are what I expressed it should look like in prior
reviews. I haven't reviewed this version yet so this as I would expect
it to go.

For the initial securityfs, i.e. the one mounted in the host userns
mount nothing changes.
The rules for securityfs_remove() are as before and it is still paired
with securityfs_create(). Specifically, a file created via
securityfs_create_dentry() in the initial securityfs mount still needs
to be removed by a call to securityfs_remove().
Creating a new dentry in the initial securityfs mount still pins the
filesytem like it always did. Consequently, the initial securityfs
mount is not destroyed on umount/shutdown as long as at least one user
of it still has dentries that it hasn't removed with a call to
securityfs_remove().

This specific part of the commit message you responded to is not
giving enough details, I think:

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

The "additional dentry reference" mentioned only relates to an afaict
unnecessary dget() in securityfs_create_dentry() which I pointed out
as part of earlier reviews. But the phrasing implies that there's a
behavioral change for the initial securityfs instance based on the
removal of this additional dget() when there really isn't.

After securityfs_create_dentry() has created a new dentry via
lookup_one_len() and eventually called d_instantiate() it currently
takes an additional reference on the newly created dentry via dget().
This additional reference is then paired with an additional dput() in
securityfs_remove(). I have not yet seen a reason why this is
necessary maybe you can help there.

For example, contrast this with debugfs which has the same underlying
logic as securityfs, i.e. any created dentry pins the whole filesystem
via simple_pin_fs() until the dentry is released and simple_unpin_fs()
is called. It uses a similar pairing as securityfs: where securityfs
has the securityfs_create_dentry() and securityfs_remove() pairing,
debugfs has the __debugfs_create_file() and debugfs_remove() pairing.
But debugfs doesn't take an additional reference on the just created
dentry in __debugfs_create_file() which would need to be put in
debugfs_remove().

So if we contrast the creation routines of securityfs and debugfs directly
condensed to just the dentry references:

securityfs       |   debugfs
---------------- | ------------------
                 |
lookup_one_len() |   lookup_one_len()
d_instantiate()  |   d_instantiate() 
dget()           |

And I have not understood why securityfs would need that additional
dget(). Not just intrinsically but also when contrasted with debugfs. So
that additional dget() is removed as part of this patch.

But the explanation in the commit message isn't ideal as it implies
the removal of the additional dget() would have any impact on the
pinning logic for securityfs when it does not.

But the pinning logic doesn't make sense outside of the initial
namespace which can never go away and there are security modules that
have files or settings for the whole system that never go away and will
always keep the filesystem around.

But for unprivileged/userns containers that mount their own securityfs
instance we want the securityfs instance cleaned up when it is
unmounted. There is no need to duplicate the pinning logic or make the
global securityfs instance display different information based on the
userns. Both options would be really messy and hacky.

Instead we can simply give each userns it's own securityfs instance
similar to how each ipc ns has its own mqueue instance and all entries
in there are cleaned up on umount or when the whole container is
shutdown. After the container is shutdown all of the security module
settings for the container go away with it anyway. So for that we don't
want any filesystem pinning done in securityfs_create_dentry(). And we
also really don't want the additional dget() that is currently taken in
securityfs_create_dentry() as it would pointlessly require us to dput()
during superblock shutdown afaict. None of this however should cause any
behavioral changes for the initial securityfs instance.

> 
> Incidentally, what happens if you open a file, pass it to somebody in a
> different userns and try to shut the opener's userns down?

I'm not exactly sure what you mean by "shutting down" and whether that's
a generic question or specific to this patch. I assume that you just
mean what happens when the last task for a userns exits and the userns
isn't pinned by e.g. a bind-mount of it to somewhere.

If you're just asking about the generic case then the opener's creds are
pinned by ->f_cred including the caller's userns at open-time. So it
will be released once that file has been closed. 

If you're asking about what happens to the userns the
securityfs/tmpfs/mqueue/devpts etc. instance was mounted in it will be
pinned by the superblock which in turn is pinned by the open file.

Does that answer your question or did you have something else in mind?

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

* Re: [PATCH v8 16/19] ima: Enable re-auditing of modified files
  2022-01-04 17:04 ` [PATCH v8 16/19] ima: Enable re-auditing of modified files Stefan Berger
@ 2022-01-05 15:21   ` Stefan Berger
  0 siblings, 0 replies; 50+ messages in thread
From: Stefan Berger @ 2022-01-05 15:21 UTC (permalink / raw)
  To: Stefan Berger, linux-integrity
  Cc: zohar, serge, christian.brauner, containers, dmitry.kasatkin,
	ebiederm, krzysztof.struczynski, roberto.sassu, mpeters, lhinds,
	lsturman, puiterwi, jejb, jamjoom, linux-kernel, paul, rgb,
	linux-security-module, jmorris


On 1/4/22 12:04, Stefan Berger wrote:
> From: Stefan Berger <stefanb@linux.ibm.com>
>
> Walk the list of ns_status associated with an iint if the file has
> changed and reset the IMA_AUDITED flag, which is part of the
> IMA_DONE_MASK. This causes a new audit message to be emitted when the
> file is again accessed on either the host or in an IMA namespace.
>
> Signed-off-by: Stefan Berger <stefanb@linux.ibm.com>
> ---
>   security/integrity/ima/ima_main.c | 33 ++++++++++++++++++++++++++++++-
>   1 file changed, 32 insertions(+), 1 deletion(-)
>
> diff --git a/security/integrity/ima/ima_main.c b/security/integrity/ima/ima_main.c
> index 99dc984b49c9..bc3ab08f39c6 100644
> --- a/security/integrity/ima/ima_main.c
> +++ b/security/integrity/ima/ima_main.c
> @@ -153,6 +153,35 @@ static void ima_rdwr_violation_check(struct ima_namespace *ns,
>   				  "invalid_pcr", "open_writers");
>   }
>   
> +#ifdef CONFIG_IMA_NS
> +
> +static void mask_iint_ns_status_flags(struct integrity_iint_cache *iint,
> +				      unsigned long mask)
> +{
> +	struct ns_status *status;
> +	unsigned long flags;
> +
> +	read_lock(&iint->ns_list_lock);
> +	list_for_each_entry(status, &iint->ns_list, ns_next) {
> +		flags = iint_flags(iint, status) & mask;
> +		set_iint_flags(iint, status, flags);
> +	}
> +	read_unlock(&iint->ns_list_lock);
> +}
> +
> +#else
> +
> +static void mask_iint_ns_status_flags(struct integrity_iint_cache *iint,
> +				      unsigned long mask)
> +{
> +	unsigned long flags;
> +
> +	flags = iint_flags(iint, NULL) & mask;
> +	set_iint_flags(iint, NULL, flags);
> +}
> +
> +#endif
> +

The above two cases are due to this here:

diff --git a/security/integrity/integrity.h b/security/integrity/integrity.h
index 547425c20e11..201a9d46d6e1 100644
--- a/security/integrity/integrity.h
+++ b/security/integrity/integrity.h
@@ -138,6 +138,10 @@ struct integrity_iint_cache {
  	enum integrity_status ima_creds_status:4;
  	enum integrity_status evm_status:4;
  	struct ima_digest_data *ima_hash;
+#ifdef CONFIG_IMA_NS
+	rwlock_t ns_list_lock;
+	struct list_head ns_list;
+#endif
  };

Ideally namespaced and non-namespaced code cases would share the code and to be able to share it the above #ifdef CONFIG_IMA_NS  in integrity.hshouldn't be there but we would have the lock and list in IMA namespacing and non-namespacing case. The above two code cases shown above are just the beginning and as more variables are moved from the iint into the ns_status these types of 'two cases of code' would show up in more places. So I think we should prevent this already now.

To be able to share the code we need an ns_status on a list in the non-namespacing case as well. In the non-namespacing case it would always be a single ns_status on the list. What is worth a decision is how to get the ns_status on the list. One idea would be to conditionally embed it into the integrity_iint_cache like this:

/* integrity data associated with an inode */
struct integrity_iint_cache {
         struct rb_node rb_node; /* rooted in integrity_iint_tree */
         struct mutex mutex;     /* protects: version, flags, digest */
         struct inode *inode;    /* back pointer to inode in question */
         u64 version;            /* track inode changes */
         unsigned long flags;
         unsigned long atomic_flags;
         enum integrity_status ima_file_status:4;
         enum integrity_status ima_mmap_status:4;
         enum integrity_status ima_bprm_status:4;
         enum integrity_status ima_read_status:4;
         enum integrity_status ima_creds_status:4;
         enum integrity_status evm_status:4;
         struct ima_digest_data *ima_hash;
         rwlock_t ns_list_lock;
         struct list_head ns_list;
#ifndef CONFIG_IMA_NS
	struct ns_status status;
#endif
};

This would prevent a 2nd cache just for allocation of ns_status in the 
non-namespacing case and getting the  embedded ns_status onto the list 
would also be like this:

     INIT_LIST_HEAD(&iint->ns_list);

#ifndef CONFIG_IMA_NS

     INIT_LIST_HEAD(&iint->status.ns_next);

     list_add_tail(&iint->status.ns_next, &iint->ns_list);

#endif

The other option is to allocated the ns_status via a minimal 
implementation of ima_ns_status.c for the non-namespaced case using 
kmalloc's from a cache for ns_status structures.


Also, the new 'rwlock_t ns_list_lock' in the iint would really only be 
necessary for locking in the namespacing case. However, to be able to 
share the code we would need to keep this lock around for the 
non-namespacing case as well so that we can call read_lock() in both 
cases. An option would be to introduce a macro for the locking that is a 
no-op in the non-namespacing case and does the actual locking in the 
namespacing case. I am not sure what would be better. I would prefer to 
explicitly see the read_lock()...



>   static void ima_check_last_writer(struct integrity_iint_cache *iint,
>   				  struct inode *inode, struct file *file)
>   {
> @@ -169,8 +198,10 @@ static void ima_check_last_writer(struct integrity_iint_cache *iint,
>   		if (!IS_I_VERSION(inode) ||
>   		    !inode_eq_iversion(inode, iint->version) ||
>   		    (iint->flags & IMA_NEW_FILE)) {
> -			iint->flags &= ~(IMA_DONE_MASK | IMA_NEW_FILE);
> +			mask_iint_ns_status_flags(iint,
> +					~(IMA_DONE_MASK | IMA_NEW_FILE));
>   			iint->measured_pcrs = 0;
> +
>   			if (update)
>   				ima_update_xattr(iint, file);
>   		}

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

* Re: [PATCH v8 08/19] ima: Use mac_admin_ns_capable() to check corresponding capability
  2022-01-04 17:04 ` [PATCH v8 08/19] ima: Use mac_admin_ns_capable() to check corresponding capability Stefan Berger
@ 2022-01-05 20:55     ` kernel test robot
  2022-01-13 20:28   ` Mimi Zohar
  1 sibling, 0 replies; 50+ messages in thread
From: kernel test robot @ 2022-01-05 20:55 UTC (permalink / raw)
  To: Stefan Berger, linux-integrity
  Cc: llvm, kbuild-all, zohar, serge, christian.brauner, containers,
	dmitry.kasatkin, ebiederm, krzysztof.struczynski, roberto.sassu,
	mpeters

Hi Stefan,

Thank you for the patch! Perhaps something to improve:

[auto build test WARNING on linux/master]
[also build test WARNING on linus/master v5.16-rc8]
[cannot apply to zohar-integrity/next-integrity jmorris-security/next-testing next-20220105]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch]

url:    https://github.com/0day-ci/linux/commits/Stefan-Berger/ima-Namespace-IMA-with-audit-support-in-IMA-ns/20220105-010946
base:   https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git 136057256686de39cc3a07c2e39ef6bc43003ff6
config: mips-randconfig-r002-20220105 (https://download.01.org/0day-ci/archive/20220106/202201060430.LHZbFhad-lkp@intel.com/config)
compiler: clang version 14.0.0 (https://github.com/llvm/llvm-project d5b6e30ed3acad794dd0aec400e617daffc6cc3d)
reproduce (this is a W=1 build):
        wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
        chmod +x ~/bin/make.cross
        # install mips cross compiling tool for clang build
        # apt-get install binutils-mips-linux-gnu
        # https://github.com/0day-ci/linux/commit/fa09a3da70380ef32e9a644c08a04cc8f4630baf
        git remote add linux-review https://github.com/0day-ci/linux
        git fetch --no-tags linux-review Stefan-Berger/ima-Namespace-IMA-with-audit-support-in-IMA-ns/20220105-010946
        git checkout fa09a3da70380ef32e9a644c08a04cc8f4630baf
        # save the config file to linux build tree
        mkdir build_dir
        COMPILER_INSTALL_PATH=$HOME/0day COMPILER=clang make.cross W=1 O=build_dir ARCH=mips SHELL=/bin/bash drivers/nvmem/ security/integrity/ima/

If you fix the issue, kindly add following tag as appropriate
Reported-by: kernel test robot <lkp@intel.com>

All warnings (new ones prefixed by >>):

>> security/integrity/ima/ima_fs.c:380:25: warning: unused variable 'user_ns'
   struct user_namespace = ima_user_ns_from_file(filp);
   ^
   fatal error: error in backend: Nested variants found in inline asm string: ' .set push
   .set mips64r2
   .if ( 0x00 ) != -1)) 0x00 ) != -1)) : ($( static struct ftrace_branch_data __attribute__((__aligned__(4))) __attribute__((__section__("_ftrace_branch"))) __if_trace = $( .func = __func__, .file = "arch/mips/include/asm/bitops.h", .line = 192, $); 0x00 ) != -1)) : $))) ) && ( 0 ); .set push; .set mips64r2; .rept 1; sync 0x00; .endr; .set pop; .else; ; .endif
   1: ll $0, $2
   or $1, $0, $3
   sc $1, $2
   beqz $1, 1b
   .set pop
   '
   PLEASE submit a bug report to https://bugs.llvm.org/ and include the crash backtrace, preprocessed source, and associated run script.
   Stack dump:
   0. Program arguments: clang -Wp,-MMD,security/integrity/ima/.ima_fs.o.d -nostdinc -Iarch/mips/include -I./arch/mips/include/generated -Iinclude -I./include -Iarch/mips/include/uapi -I./arch/mips/include/generated/uapi -Iinclude/uapi -I./include/generated/uapi -include include/linux/compiler-version.h -include include/linux/kconfig.h -include include/linux/compiler_types.h -D__KERNEL__ -DVMLINUX_LOAD_ADDRESS=0xffffffff84000000 -DLINKER_LOAD_ADDRESS=0x84000000 -DDATAOFFSET=0 -Qunused-arguments -fmacro-prefix-map== -DKBUILD_EXTRA_WARN1 -Wall -Wundef -Werror=strict-prototypes -Wno-trigraphs -fno-strict-aliasing -fno-common -fshort-wchar -fno-PIE -Werror=implicit-function-declaration -Werror=implicit-int -Werror=return-type -Wno-format-security -std=gnu89 --target=mipsel-linux -fintegrated-as -Werror=unknown-warning-option -Werror=ignored-optimization-argument -mno-check-zero-division -mabi=32 -G 0 -mno-abicalls -fno-pic -pipe -msoft-float -DGAS_HAS_SET_HARDFLOAT -Wa,-msoft-float -ffreestanding -EL -fno-stack-check -march=mips32 -Wa,--trap -DTOOLCHAIN_SUPPORTS_VIRT -Iarch/mips/include/asm/mach-au1x00 -Iarch/mips/include/asm/mach-generic -fno-asynchronous-unwind-tables -fno-delete-null-pointer-checks -Wno-frame-address -Wno-address-of-packed-member -O2 -Wframe-larger-than=1024 -fno-stack-protector -Wimplicit-fallthrough -Wno-gnu -mno-global-merge -Wno-unused-but-set-variable -Wno-unused-const-variable -ftrivial-auto-var-init=pattern -fno-stack-clash-protection -pg -falign-functions=64 -Wdeclaration-after-statement -Wvla -Wno-pointer-sign -Wno-array-bounds -fno-strict-overflow -fno-stack-check -Werror=date-time -Werror=incompatible-pointer-types -Wextra -Wunused -Wno-unused-parameter -Wmissing-declarations -Wmissing-format-attribute -Wmissing-prototypes -Wold-style-definition -Wmissing-include-dirs -Wunused-but-set-variable -Wunused-const-variable -Wno-missing-field-initializers -Wno-sign-compare -Wno-type-limits -fsanitize=array-bounds -fsanitize=unreachable -fsanitize=object-size -fsanitize=enum -fsanitize-coverage=trace-pc -I security/integrity/ima -I ./security/integrity/ima -ffunction-sections -fdata-sections -DKBUILD_MODFILE="security/integrity/ima/ima" -DKBUILD_BASENAME="ima_fs" -DKBUILD_MODNAME="ima" -D__KBUILD_MODNAME=kmod_ima -c -o security/integrity/ima/ima_fs.o security/integrity/ima/ima_fs.c
   1. <eof> parser at end of file
   2. Code generation
   3. Running pass 'Function Pass Manager' on module 'security/integrity/ima/ima_fs.c'.
   4. Running pass 'Mips Assembly Printer' on function '@ima_open_policy'
   #0 0x0000557a749c4b3f Signals.cpp:0:0
   #1 0x0000557a749c2a8c llvm::sys::CleanupOnSignal(unsigned long) (/opt/cross/clang-d5b6e30ed3/bin/clang-14+0x3401a8c)
   #2 0x0000557a74906667 llvm::CrashRecoveryContext::HandleExit(int) (/opt/cross/clang-d5b6e30ed3/bin/clang-14+0x3345667)
   #3 0x0000557a749bb13e llvm::sys::Process::Exit(int, bool) (/opt/cross/clang-d5b6e30ed3/bin/clang-14+0x33fa13e)
   #4 0x0000557a7264133b (/opt/cross/clang-d5b6e30ed3/bin/clang-14+0x108033b)
   #5 0x0000557a7490d10c llvm::report_fatal_error(llvm::Twine const&, bool) (/opt/cross/clang-d5b6e30ed3/bin/clang-14+0x334c10c)
   #6 0x0000557a755ef9b8 llvm::AsmPrinter::emitInlineAsm(llvm::MachineInstr const (/opt/cross/clang-d5b6e30ed3/bin/clang-14+0x402e9b8)
   #7 0x0000557a755eb759 llvm::AsmPrinter::emitFunctionBody() (/opt/cross/clang-d5b6e30ed3/bin/clang-14+0x402a759)
   #8 0x0000557a7309c82e llvm::MipsAsmPrinter::runOnMachineFunction(llvm::MachineFunction&) (/opt/cross/clang-d5b6e30ed3/bin/clang-14+0x1adb82e)
   #9 0x0000557a73d332fd llvm::MachineFunctionPass::runOnFunction(llvm::Function&) (.part.53) MachineFunctionPass.cpp:0:0
   #10 0x0000557a7416b867 llvm::FPPassManager::runOnFunction(llvm::Function&) (/opt/cross/clang-d5b6e30ed3/bin/clang-14+0x2baa867)
   #11 0x0000557a7416b9e1 llvm::FPPassManager::runOnModule(llvm::Module&) (/opt/cross/clang-d5b6e30ed3/bin/clang-14+0x2baa9e1)
   #12 0x0000557a7416ccbf llvm::legacy::PassManagerImpl::run(llvm::Module&) (/opt/cross/clang-d5b6e30ed3/bin/clang-14+0x2babcbf)
   #13 0x0000557a74cd64fa clang::EmitBackendOutput(clang::DiagnosticsEngine&, clang::HeaderSearchOptions const&, clang::CodeGenOptions const&, clang::TargetOptions const&, clang::LangOptions const&, llvm::StringRef, clang::BackendAction, std::unique_ptr<llvm::raw_pwrite_stream, std::default_delete<llvm::raw_pwrite_stream> >) (/opt/cross/clang-d5b6e30ed3/bin/clang-14+0x37154fa)
   #14 0x0000557a75903ea3 clang::BackendConsumer::HandleTranslationUnit(clang::ASTContext&) (/opt/cross/clang-d5b6e30ed3/bin/clang-14+0x4342ea3)
   #15 0x0000557a76407fd9 clang::ParseAST(clang::Sema&, bool, bool) (/opt/cross/clang-d5b6e30ed3/bin/clang-14+0x4e46fd9)
   #16 0x0000557a75902cff clang::CodeGenAction::ExecuteAction() (/opt/cross/clang-d5b6e30ed3/bin/clang-14+0x4341cff)
   #17 0x0000557a75302001 clang::FrontendAction::Execute() (/opt/cross/clang-d5b6e30ed3/bin/clang-14+0x3d41001)
   #18 0x0000557a75299bda clang::CompilerInstance::ExecuteAction(clang::FrontendAction&) (/opt/cross/clang-d5b6e30ed3/bin/clang-14+0x3cd8bda)
   #19 0x0000557a753cb07b (/opt/cross/clang-d5b6e30ed3/bin/clang-14+0x3e0a07b)
   #20 0x0000557a72642084 cc1_main(llvm::ArrayRef<char char (/opt/cross/clang-d5b6e30ed3/bin/clang-14+0x1081084)
   #21 0x0000557a7263f5cb ExecuteCC1Tool(llvm::SmallVectorImpl<char driver.cpp:0:0
   #22 0x0000557a75136b15 void llvm::function_ref<void ()>::callback_fn<clang::driver::CC1Command::Execute(llvm::ArrayRef<llvm::Optional<llvm::StringRef> >, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> const::'lambda'()>(long) Job.cpp:0:0
   #23 0x0000557a74906523 llvm::CrashRecoveryContext::RunSafely(llvm::function_ref<void ()>) (/opt/cross/clang-d5b6e30ed3/bin/clang-14+0x3345523)
   #24 0x0000557a7513740e clang::driver::CC1Command::Execute(llvm::ArrayRef<llvm::Optional<llvm::StringRef> >, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> const (.part.216) Job.cpp:0:0
   #25 0x0000557a7510dee7 clang::driver::Compilation::ExecuteCommand(clang::driver::Command const&, clang::driver::Command const (/opt/cross/clang-d5b6e30ed3/bin/clang-14+0x3b4cee7)
   #26 0x0000557a7510e8c7 clang::driver::Compilation::ExecuteJobs(clang::driver::JobList const&, llvm::SmallVectorImpl<std::pair<int, clang::driver::Command >&) const (/opt/cross/clang-d5b6e30ed3/bin/clang-14+0x3b4d8c7)
   #27 0x0000557a75118139 clang::driver::Driver::ExecuteCompilation(clang::driver::Compilation&, llvm::SmallVectorImpl<std::pair<int, clang::driver::Command >&) (/opt/cross/clang-d5b6e30ed3/bin/clang-14+0x3b57139)
   #28 0x0000557a7256a19f main (/opt/cross/clang-d5b6e30ed3/bin/clang-14+0xfa919f)
   #29 0x00007fc0e7a42d0a __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x26d0a)
   #30 0x0000557a7263f0ea _start (/opt/cross/clang-d5b6e30ed3/bin/clang-14+0x107e0ea)
   clang-14: error: clang frontend command failed with exit code 70 (use -v to see invocation)
   clang version 14.0.0 (git://gitmirror/llvm_project d5b6e30ed3acad794dd0aec400e617daffc6cc3d)
   Target: mipsel-unknown-linux
   Thread model: posix
   InstalledDir: /opt/cross/clang-d5b6e30ed3/bin
   clang-14: note: diagnostic msg:
   Makefile arch drivers include kernel nr_bisected scripts security source usr


vim +/user_ns +380 security/integrity/ima/ima_fs.c

   374	
   375	/*
   376	 * ima_open_policy: sequentialize access to the policy file
   377	 */
   378	static int ima_open_policy(struct inode *inode, struct file *filp)
   379	{
 > 380		struct user_namespace *user_ns = ima_user_ns_from_file(filp);
   381		struct ima_namespace *ns = &init_ima_ns;
   382	
   383		if (!(filp->f_flags & O_WRONLY)) {
   384	#ifndef	CONFIG_IMA_READ_POLICY
   385			return -EACCES;
   386	#else
   387			if ((filp->f_flags & O_ACCMODE) != O_RDONLY)
   388				return -EACCES;
   389			if (!mac_admin_ns_capable(user_ns))
   390				return -EPERM;
   391			return seq_open(filp, &ima_policy_seqops);
   392	#endif
   393		}
   394		if (test_and_set_bit(IMA_FS_BUSY, &ns->ima_fs_flags))
   395			return -EBUSY;
   396		return 0;
   397	}
   398	

---
0-DAY CI Kernel Test Service, Intel Corporation
https://lists.01.org/hyperkitty/list/kbuild-all@lists.01.org

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

* Re: [PATCH v8 08/19] ima: Use mac_admin_ns_capable() to check corresponding capability
@ 2022-01-05 20:55     ` kernel test robot
  0 siblings, 0 replies; 50+ messages in thread
From: kernel test robot @ 2022-01-05 20:55 UTC (permalink / raw)
  To: kbuild-all

[-- Attachment #1: Type: text/plain, Size: 11107 bytes --]

Hi Stefan,

Thank you for the patch! Perhaps something to improve:

[auto build test WARNING on linux/master]
[also build test WARNING on linus/master v5.16-rc8]
[cannot apply to zohar-integrity/next-integrity jmorris-security/next-testing next-20220105]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch]

url:    https://github.com/0day-ci/linux/commits/Stefan-Berger/ima-Namespace-IMA-with-audit-support-in-IMA-ns/20220105-010946
base:   https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git 136057256686de39cc3a07c2e39ef6bc43003ff6
config: mips-randconfig-r002-20220105 (https://download.01.org/0day-ci/archive/20220106/202201060430.LHZbFhad-lkp(a)intel.com/config)
compiler: clang version 14.0.0 (https://github.com/llvm/llvm-project d5b6e30ed3acad794dd0aec400e617daffc6cc3d)
reproduce (this is a W=1 build):
        wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
        chmod +x ~/bin/make.cross
        # install mips cross compiling tool for clang build
        # apt-get install binutils-mips-linux-gnu
        # https://github.com/0day-ci/linux/commit/fa09a3da70380ef32e9a644c08a04cc8f4630baf
        git remote add linux-review https://github.com/0day-ci/linux
        git fetch --no-tags linux-review Stefan-Berger/ima-Namespace-IMA-with-audit-support-in-IMA-ns/20220105-010946
        git checkout fa09a3da70380ef32e9a644c08a04cc8f4630baf
        # save the config file to linux build tree
        mkdir build_dir
        COMPILER_INSTALL_PATH=$HOME/0day COMPILER=clang make.cross W=1 O=build_dir ARCH=mips SHELL=/bin/bash drivers/nvmem/ security/integrity/ima/

If you fix the issue, kindly add following tag as appropriate
Reported-by: kernel test robot <lkp@intel.com>

All warnings (new ones prefixed by >>):

>> security/integrity/ima/ima_fs.c:380:25: warning: unused variable 'user_ns'
   struct user_namespace = ima_user_ns_from_file(filp);
   ^
   fatal error: error in backend: Nested variants found in inline asm string: ' .set push
   .set mips64r2
   .if ( 0x00 ) != -1)) 0x00 ) != -1)) : ($( static struct ftrace_branch_data __attribute__((__aligned__(4))) __attribute__((__section__("_ftrace_branch"))) __if_trace = $( .func = __func__, .file = "arch/mips/include/asm/bitops.h", .line = 192, $); 0x00 ) != -1)) : $))) ) && ( 0 ); .set push; .set mips64r2; .rept 1; sync 0x00; .endr; .set pop; .else; ; .endif
   1: ll $0, $2
   or $1, $0, $3
   sc $1, $2
   beqz $1, 1b
   .set pop
   '
   PLEASE submit a bug report to https://bugs.llvm.org/ and include the crash backtrace, preprocessed source, and associated run script.
   Stack dump:
   0. Program arguments: clang -Wp,-MMD,security/integrity/ima/.ima_fs.o.d -nostdinc -Iarch/mips/include -I./arch/mips/include/generated -Iinclude -I./include -Iarch/mips/include/uapi -I./arch/mips/include/generated/uapi -Iinclude/uapi -I./include/generated/uapi -include include/linux/compiler-version.h -include include/linux/kconfig.h -include include/linux/compiler_types.h -D__KERNEL__ -DVMLINUX_LOAD_ADDRESS=0xffffffff84000000 -DLINKER_LOAD_ADDRESS=0x84000000 -DDATAOFFSET=0 -Qunused-arguments -fmacro-prefix-map== -DKBUILD_EXTRA_WARN1 -Wall -Wundef -Werror=strict-prototypes -Wno-trigraphs -fno-strict-aliasing -fno-common -fshort-wchar -fno-PIE -Werror=implicit-function-declaration -Werror=implicit-int -Werror=return-type -Wno-format-security -std=gnu89 --target=mipsel-linux -fintegrated-as -Werror=unknown-warning-option -Werror=ignored-optimization-argument -mno-check-zero-division -mabi=32 -G 0 -mno-abicalls -fno-pic -pipe -msoft-float -DGAS_HAS_SET_HARDFLOAT -Wa,-msoft-float -ffreestanding -EL -fno-stack-check -march=mips32 -Wa,--trap -DTOOLCHAIN_SUPPORTS_VIRT -Iarch/mips/include/asm/mach-au1x00 -Iarch/mips/include/asm/mach-generic -fno-asynchronous-unwind-tables -fno-delete-null-pointer-checks -Wno-frame-address -Wno-address-of-packed-member -O2 -Wframe-larger-than=1024 -fno-stack-protector -Wimplicit-fallthrough -Wno-gnu -mno-global-merge -Wno-unused-but-set-variable -Wno-unused-const-variable -ftrivial-auto-var-init=pattern -fno-stack-clash-protection -pg -falign-functions=64 -Wdeclaration-after-statement -Wvla -Wno-pointer-sign -Wno-array-bounds -fno-strict-overflow -fno-stack-check -Werror=date-time -Werror=incompatible-pointer-types -Wextra -Wunused -Wno-unused-parameter -Wmissing-declarations -Wmissing-format-attribute -Wmissing-prototypes -Wold-style-definition -Wmissing-include-dirs -Wunused-but-set-variable -Wunused-const-variable -Wno-missing-field-initializers -Wno-sign-compare -Wno-type-limits -fsanitize=array-bounds -fsanitize=unreachable -fsanitize=object-size -fsanitize=enum -fsanitize-coverage=trace-pc -I security/integrity/ima -I ./security/integrity/ima -ffunction-sections -fdata-sections -DKBUILD_MODFILE="security/integrity/ima/ima" -DKBUILD_BASENAME="ima_fs" -DKBUILD_MODNAME="ima" -D__KBUILD_MODNAME=kmod_ima -c -o security/integrity/ima/ima_fs.o security/integrity/ima/ima_fs.c
   1. <eof> parser at end of file
   2. Code generation
   3. Running pass 'Function Pass Manager' on module 'security/integrity/ima/ima_fs.c'.
   4. Running pass 'Mips Assembly Printer' on function '@ima_open_policy'
   #0 0x0000557a749c4b3f Signals.cpp:0:0
   #1 0x0000557a749c2a8c llvm::sys::CleanupOnSignal(unsigned long) (/opt/cross/clang-d5b6e30ed3/bin/clang-14+0x3401a8c)
   #2 0x0000557a74906667 llvm::CrashRecoveryContext::HandleExit(int) (/opt/cross/clang-d5b6e30ed3/bin/clang-14+0x3345667)
   #3 0x0000557a749bb13e llvm::sys::Process::Exit(int, bool) (/opt/cross/clang-d5b6e30ed3/bin/clang-14+0x33fa13e)
   #4 0x0000557a7264133b (/opt/cross/clang-d5b6e30ed3/bin/clang-14+0x108033b)
   #5 0x0000557a7490d10c llvm::report_fatal_error(llvm::Twine const&, bool) (/opt/cross/clang-d5b6e30ed3/bin/clang-14+0x334c10c)
   #6 0x0000557a755ef9b8 llvm::AsmPrinter::emitInlineAsm(llvm::MachineInstr const (/opt/cross/clang-d5b6e30ed3/bin/clang-14+0x402e9b8)
   #7 0x0000557a755eb759 llvm::AsmPrinter::emitFunctionBody() (/opt/cross/clang-d5b6e30ed3/bin/clang-14+0x402a759)
   #8 0x0000557a7309c82e llvm::MipsAsmPrinter::runOnMachineFunction(llvm::MachineFunction&) (/opt/cross/clang-d5b6e30ed3/bin/clang-14+0x1adb82e)
   #9 0x0000557a73d332fd llvm::MachineFunctionPass::runOnFunction(llvm::Function&) (.part.53) MachineFunctionPass.cpp:0:0
   #10 0x0000557a7416b867 llvm::FPPassManager::runOnFunction(llvm::Function&) (/opt/cross/clang-d5b6e30ed3/bin/clang-14+0x2baa867)
   #11 0x0000557a7416b9e1 llvm::FPPassManager::runOnModule(llvm::Module&) (/opt/cross/clang-d5b6e30ed3/bin/clang-14+0x2baa9e1)
   #12 0x0000557a7416ccbf llvm::legacy::PassManagerImpl::run(llvm::Module&) (/opt/cross/clang-d5b6e30ed3/bin/clang-14+0x2babcbf)
   #13 0x0000557a74cd64fa clang::EmitBackendOutput(clang::DiagnosticsEngine&, clang::HeaderSearchOptions const&, clang::CodeGenOptions const&, clang::TargetOptions const&, clang::LangOptions const&, llvm::StringRef, clang::BackendAction, std::unique_ptr<llvm::raw_pwrite_stream, std::default_delete<llvm::raw_pwrite_stream> >) (/opt/cross/clang-d5b6e30ed3/bin/clang-14+0x37154fa)
   #14 0x0000557a75903ea3 clang::BackendConsumer::HandleTranslationUnit(clang::ASTContext&) (/opt/cross/clang-d5b6e30ed3/bin/clang-14+0x4342ea3)
   #15 0x0000557a76407fd9 clang::ParseAST(clang::Sema&, bool, bool) (/opt/cross/clang-d5b6e30ed3/bin/clang-14+0x4e46fd9)
   #16 0x0000557a75902cff clang::CodeGenAction::ExecuteAction() (/opt/cross/clang-d5b6e30ed3/bin/clang-14+0x4341cff)
   #17 0x0000557a75302001 clang::FrontendAction::Execute() (/opt/cross/clang-d5b6e30ed3/bin/clang-14+0x3d41001)
   #18 0x0000557a75299bda clang::CompilerInstance::ExecuteAction(clang::FrontendAction&) (/opt/cross/clang-d5b6e30ed3/bin/clang-14+0x3cd8bda)
   #19 0x0000557a753cb07b (/opt/cross/clang-d5b6e30ed3/bin/clang-14+0x3e0a07b)
   #20 0x0000557a72642084 cc1_main(llvm::ArrayRef<char char (/opt/cross/clang-d5b6e30ed3/bin/clang-14+0x1081084)
   #21 0x0000557a7263f5cb ExecuteCC1Tool(llvm::SmallVectorImpl<char driver.cpp:0:0
   #22 0x0000557a75136b15 void llvm::function_ref<void ()>::callback_fn<clang::driver::CC1Command::Execute(llvm::ArrayRef<llvm::Optional<llvm::StringRef> >, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> const::'lambda'()>(long) Job.cpp:0:0
   #23 0x0000557a74906523 llvm::CrashRecoveryContext::RunSafely(llvm::function_ref<void ()>) (/opt/cross/clang-d5b6e30ed3/bin/clang-14+0x3345523)
   #24 0x0000557a7513740e clang::driver::CC1Command::Execute(llvm::ArrayRef<llvm::Optional<llvm::StringRef> >, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> const (.part.216) Job.cpp:0:0
   #25 0x0000557a7510dee7 clang::driver::Compilation::ExecuteCommand(clang::driver::Command const&, clang::driver::Command const (/opt/cross/clang-d5b6e30ed3/bin/clang-14+0x3b4cee7)
   #26 0x0000557a7510e8c7 clang::driver::Compilation::ExecuteJobs(clang::driver::JobList const&, llvm::SmallVectorImpl<std::pair<int, clang::driver::Command >&) const (/opt/cross/clang-d5b6e30ed3/bin/clang-14+0x3b4d8c7)
   #27 0x0000557a75118139 clang::driver::Driver::ExecuteCompilation(clang::driver::Compilation&, llvm::SmallVectorImpl<std::pair<int, clang::driver::Command >&) (/opt/cross/clang-d5b6e30ed3/bin/clang-14+0x3b57139)
   #28 0x0000557a7256a19f main (/opt/cross/clang-d5b6e30ed3/bin/clang-14+0xfa919f)
   #29 0x00007fc0e7a42d0a __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x26d0a)
   #30 0x0000557a7263f0ea _start (/opt/cross/clang-d5b6e30ed3/bin/clang-14+0x107e0ea)
   clang-14: error: clang frontend command failed with exit code 70 (use -v to see invocation)
   clang version 14.0.0 (git://gitmirror/llvm_project d5b6e30ed3acad794dd0aec400e617daffc6cc3d)
   Target: mipsel-unknown-linux
   Thread model: posix
   InstalledDir: /opt/cross/clang-d5b6e30ed3/bin
   clang-14: note: diagnostic msg:
   Makefile arch drivers include kernel nr_bisected scripts security source usr


vim +/user_ns +380 security/integrity/ima/ima_fs.c

   374	
   375	/*
   376	 * ima_open_policy: sequentialize access to the policy file
   377	 */
   378	static int ima_open_policy(struct inode *inode, struct file *filp)
   379	{
 > 380		struct user_namespace *user_ns = ima_user_ns_from_file(filp);
   381		struct ima_namespace *ns = &init_ima_ns;
   382	
   383		if (!(filp->f_flags & O_WRONLY)) {
   384	#ifndef	CONFIG_IMA_READ_POLICY
   385			return -EACCES;
   386	#else
   387			if ((filp->f_flags & O_ACCMODE) != O_RDONLY)
   388				return -EACCES;
   389			if (!mac_admin_ns_capable(user_ns))
   390				return -EPERM;
   391			return seq_open(filp, &ima_policy_seqops);
   392	#endif
   393		}
   394		if (test_and_set_bit(IMA_FS_BUSY, &ns->ima_fs_flags))
   395			return -EBUSY;
   396		return 0;
   397	}
   398	

---
0-DAY CI Kernel Test Service, Intel Corporation
https://lists.01.org/hyperkitty/list/kbuild-all(a)lists.01.org

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

* Re: [PATCH v8 01/19] securityfs: Extend securityfs with namespacing support
  2022-01-05 10:18     ` Christian Brauner
@ 2022-01-11 12:16       ` Mimi Zohar
  2022-01-11 14:12         ` Christian Brauner
  0 siblings, 1 reply; 50+ messages in thread
From: Mimi Zohar @ 2022-01-11 12:16 UTC (permalink / raw)
  To: Christian Brauner, Al Viro
  Cc: Stefan Berger, linux-integrity, serge, containers,
	dmitry.kasatkin, ebiederm, krzysztof.struczynski, roberto.sassu,
	mpeters, lhinds, lsturman, puiterwi, jejb, jamjoom, linux-kernel,
	paul, rgb, linux-security-module, jmorris, Stefan Berger,
	James Bottomley

On Wed, 2022-01-05 at 11:18 +0100, Christian Brauner wrote:
> On Wed, Jan 05, 2022 at 03:58:11AM +0000, Al Viro wrote:
> > On Tue, Jan 04, 2022 at 12:03:58PM -0500, Stefan Berger wrote:
> > > From: Stefan Berger <stefanb@linux.ibm.com> 

> > > Drop the additional dentry reference to enable simple cleanup of dentries
> > > upon umount. Now the dentries do not need to be explicitly freed anymore
> > > but we can just rely on d_genocide() and the dcache shrinker to do all
> > > the required work.
> 
> The "additional dentry reference" mentioned only relates to an afaict
> unnecessary dget() in securityfs_create_dentry() which I pointed out
> as part of earlier reviews. But the phrasing implies that there's a
> behavioral change for the initial securityfs instance based on the
> removal of this additional dget() when there really isn't.
> 
> After securityfs_create_dentry() has created a new dentry via
> lookup_one_len() and eventually called d_instantiate() it currently
> takes an additional reference on the newly created dentry via dget().
> This additional reference is then paired with an additional dput() in
> securityfs_remove(). I have not yet seen a reason why this is
> necessary maybe you can help there.
> 
> For example, contrast this with debugfs which has the same underlying
> logic as securityfs, i.e. any created dentry pins the whole filesystem
> via simple_pin_fs() until the dentry is released and simple_unpin_fs()
> is called. It uses a similar pairing as securityfs: where securityfs
> has the securityfs_create_dentry() and securityfs_remove() pairing,
> debugfs has the __debugfs_create_file() and debugfs_remove() pairing.
> But debugfs doesn't take an additional reference on the just created
> dentry in __debugfs_create_file() which would need to be put in
> debugfs_remove().
> 
> So if we contrast the creation routines of securityfs and debugfs directly
> condensed to just the dentry references:
> 
> securityfs       |   debugfs
> ---------------- | ------------------
>                  |
> lookup_one_len() |   lookup_one_len()
> d_instantiate()  |   d_instantiate() 
> dget()           |
> 
> And I have not understood why securityfs would need that additional
> dget(). Not just intrinsically but also when contrasted with debugfs. So
> that additional dget() is removed as part of this patch.

Assuming it isn't needed, could removing it be a separate patch and
upstreamed independently of either the securityfs or IMA namespacing
changes?

thanks,

Mimi

> 
> But the explanation in the commit message isn't ideal as it implies
> the removal of the additional dget() would have any impact on the
> pinning logic for securityfs when it does not.
> 
> But the pinning logic doesn't make sense outside of the initial
> namespace which can never go away and there are security modules that
> have files or settings for the whole system that never go away and will
> always keep the filesystem around.
> 
> But for unprivileged/userns containers that mount their own securityfs
> instance we want the securityfs instance cleaned up when it is
> unmounted. There is no need to duplicate the pinning logic or make the
> global securityfs instance display different information based on the
> userns. Both options would be really messy and hacky.
> 
> Instead we can simply give each userns it's own securityfs instance
> similar to how each ipc ns has its own mqueue instance and all entries
> in there are cleaned up on umount or when the whole container is
> shutdown. After the container is shutdown all of the security module
> settings for the container go away with it anyway. So for that we don't
> want any filesystem pinning done in securityfs_create_dentry(). And we
> also really don't want the additional dget() that is currently taken in
> securityfs_create_dentry() as it would pointlessly require us to dput()
> during superblock shutdown afaict. None of this however should cause any
> behavioral changes for the initial securityfs instance.


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

* Re: [PATCH v8 01/19] securityfs: Extend securityfs with namespacing support
  2022-01-11 12:16       ` Mimi Zohar
@ 2022-01-11 14:12         ` Christian Brauner
  0 siblings, 0 replies; 50+ messages in thread
From: Christian Brauner @ 2022-01-11 14:12 UTC (permalink / raw)
  To: Mimi Zohar
  Cc: Christian Brauner, Al Viro, Stefan Berger, linux-integrity,
	serge, containers, dmitry.kasatkin, ebiederm,
	krzysztof.struczynski, roberto.sassu, mpeters, lhinds, lsturman,
	puiterwi, jejb, jamjoom, linux-kernel, paul, rgb,
	linux-security-module, jmorris, Stefan Berger, James Bottomley

On Tue, Jan 11, 2022 at 07:16:26AM -0500, Mimi Zohar wrote:
> On Wed, 2022-01-05 at 11:18 +0100, Christian Brauner wrote:
> > On Wed, Jan 05, 2022 at 03:58:11AM +0000, Al Viro wrote:
> > > On Tue, Jan 04, 2022 at 12:03:58PM -0500, Stefan Berger wrote:
> > > > From: Stefan Berger <stefanb@linux.ibm.com> 
> 
> > > > Drop the additional dentry reference to enable simple cleanup of dentries
> > > > upon umount. Now the dentries do not need to be explicitly freed anymore
> > > > but we can just rely on d_genocide() and the dcache shrinker to do all
> > > > the required work.
> > 
> > The "additional dentry reference" mentioned only relates to an afaict
> > unnecessary dget() in securityfs_create_dentry() which I pointed out
> > as part of earlier reviews. But the phrasing implies that there's a
> > behavioral change for the initial securityfs instance based on the
> > removal of this additional dget() when there really isn't.
> > 
> > After securityfs_create_dentry() has created a new dentry via
> > lookup_one_len() and eventually called d_instantiate() it currently
> > takes an additional reference on the newly created dentry via dget().
> > This additional reference is then paired with an additional dput() in
> > securityfs_remove(). I have not yet seen a reason why this is
> > necessary maybe you can help there.
> > 
> > For example, contrast this with debugfs which has the same underlying
> > logic as securityfs, i.e. any created dentry pins the whole filesystem
> > via simple_pin_fs() until the dentry is released and simple_unpin_fs()
> > is called. It uses a similar pairing as securityfs: where securityfs
> > has the securityfs_create_dentry() and securityfs_remove() pairing,
> > debugfs has the __debugfs_create_file() and debugfs_remove() pairing.
> > But debugfs doesn't take an additional reference on the just created
> > dentry in __debugfs_create_file() which would need to be put in
> > debugfs_remove().
> > 
> > So if we contrast the creation routines of securityfs and debugfs directly
> > condensed to just the dentry references:
> > 
> > securityfs       |   debugfs
> > ---------------- | ------------------
> >                  |
> > lookup_one_len() |   lookup_one_len()
> > d_instantiate()  |   d_instantiate() 
> > dget()           |
> > 
> > And I have not understood why securityfs would need that additional
> > dget(). Not just intrinsically but also when contrasted with debugfs. So
> > that additional dget() is removed as part of this patch.
> 
> Assuming it isn't needed, could removing it be a separate patch and
> upstreamed independently of either the securityfs or IMA namespacing
> changes?

Yeah, if the security tree wants to take it. So sm like:

From 478e96d1da24960e50897e6752f410b3d0833570 Mon Sep 17 00:00:00 2001
From: Christian Brauner <brauner@kernel.org>
Date: Tue, 11 Jan 2022 14:04:11 +0100
Subject: [PATCH] securityfs: rework dentry creation

When securityfs creates a new file or directory via
securityfs_create_dentry() it will take an additional reference on the
newly created dentry after it has attached the new inode to the new
dentry and added it to the hashqueues.
If we contrast this with debugfs which has the same underlying logic as
securityfs. It uses a similar pairing as securityfs. Where securityfs
has the securityfs_create_dentry() and securityfs_remove() pairing,
debugfs has the __debugfs_create_file() and debugfs_remove() pairing.

In contrast to securityfs, debugfs doesn't take an additional reference
on the newly created dentry in __debugfs_create_file() which would need
to be put in debugfs_remove().

The additional dget() isn't a problem per se. In the current
implementation of securityfs each created dentry pins the filesystem via
until it is removed. Since it is virtually guaranteed that there is at
least one user of securityfs that has created dentries the initial
securityfs mount cannot go away until all dentries have been removed.

Since most of the users of the initial securityfs mount don't go away
until the system is shutdown the initial securityfs won't go away when
unmounted. Instead a mount will usually surface the same superblock as
before. The additional dget() doesn't matter in this scenario since it
is required that all dentries have been cleaned up by the respective
users before the superblock can be destroyed, i.e. superblock shutdown
is tied to the lifetime of the associated dentries.

However, in order to support ima namespaces we need to extend securityfs
to support being mounted outside of the initial user namespace. For
namespaced users the pinning logic doesn't make sense. Whereas in the
initial namespace the securityfs instance and the associated data
structures of its users can't go away for reason explained earlier users
of non-initial securityfs instances do go away when the last users of
the namespace are gone.

So for those users we neither want to duplicate the pinning logic nor
make the global securityfs instance display different information based
on the namespace. Both options would be really messy and hacky.

Instead we will simply give each namespace its own securityfs instance
similar to how each ipc namespace has its own mqueue instance and all
entries in there are cleaned up on umount or when the last user of the
associated namespace is gone.

This means that the superblock's lifetime isn't tied to the dentries.
Instead the last umount, without any fds kept open, will trigger a clean
shutdown. But now the additional dget() gets in the way. Instead of
being able to rely on the generic superblock shutdown logic we would
need to drop the additional dentry reference during superblock shutdown
for all associated users. That would force the use of a generic
coordination mechanism for current and future users of securityfs which
is unnecessary. Simply remove the additional dget() in
securityfs_dentry_create().

In securityfs_remove() we will call dget() to take an additional
reference on the dentry about to be removed. After simple_unlink() or
simple_rmdir() have dropped the dentry refcount we can call d_delete()
which will either turn the dentry into negative dentry if our earlier
dget() is the only reference to the dentry, i.e. it has no other users,
or remove it from the hashqueues in case there are additional users.

All of these changes should not have any effect on the userspace
semantics of the initial securityfs mount.

Signed-off-by: Christian Brauner <brauner@kernel.org>
---
 security/inode.c | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/security/inode.c b/security/inode.c
index 6c326939750d..13e6780c4444 100644
--- a/security/inode.c
+++ b/security/inode.c
@@ -159,7 +159,6 @@ static struct dentry *securityfs_create_dentry(const char *name, umode_t mode,
 		inode->i_fop = fops;
 	}
 	d_instantiate(dentry, inode);
-	dget(dentry);
 	inode_unlock(dir);
 	return dentry;
 
@@ -302,10 +301,12 @@ void securityfs_remove(struct dentry *dentry)
 	dir = d_inode(dentry->d_parent);
 	inode_lock(dir);
 	if (simple_positive(dentry)) {
+		dget(dentry);
 		if (d_is_dir(dentry))
 			simple_rmdir(dir, dentry);
 		else
 			simple_unlink(dir, dentry);
+		d_delete(dentry);
 		dput(dentry);
 	}
 	inode_unlock(dir);
-- 
2.32.0


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

* Re: [PATCH v8 03/19] ima: Move policy related variables into ima_namespace
  2022-01-04 17:04 ` [PATCH v8 03/19] ima: Move policy related variables into ima_namespace Stefan Berger
@ 2022-01-13 20:26   ` Mimi Zohar
  2022-01-14 10:48     ` Christian Brauner
  2022-01-19 13:32     ` Stefan Berger
  0 siblings, 2 replies; 50+ messages in thread
From: Mimi Zohar @ 2022-01-13 20:26 UTC (permalink / raw)
  To: Stefan Berger, linux-integrity
  Cc: serge, christian.brauner, containers, dmitry.kasatkin, ebiederm,
	krzysztof.struczynski, roberto.sassu, mpeters, lhinds, lsturman,
	puiterwi, jejb, jamjoom, linux-kernel, paul, rgb,
	linux-security-module, jmorris, Stefan Berger

Hi Stefan,

On Tue, 2022-01-04 at 12:04 -0500, Stefan Berger wrote:
> From: Stefan Berger <stefanb@linux.ibm.com>
> 
> 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_ns_from_file() to get the IMA namespace via the user
> namespace of the SecurityFS superblock that a file belongs to.
> 
> To get the current ima_namespace use get_current_ns() when a function
> that is related to a policy rule is called. In other cases where functions
> are called due file attribute modifications, use init_ima_ns, since these
> functions are related to IMA appraisal and changes to file attributes are
> only relevant to the init_ima_ns until IMA namespaces also support IMA
> appraisal. In ima_file_free() use init_ima_ns since in this case flags
> related to file measurements may be affected, which is not supported in
> IMA namespaces, yet.
> 
> Signed-off-by: Stefan Berger <stefanb@linux.ibm.com>

Please split this patch into "ima: pass through ima namespace", or some
other name,  and "ima: Move policy related variables into
ima_namespace".  The other option is to combine the "pass through ima
namespace" with the 2nd patch, like Christian's example.

> ---
>  security/integrity/ima/ima.h                 |  49 ++++---
>  security/integrity/ima/ima_api.c             |   8 +-
>  security/integrity/ima/ima_appraise.c        |  28 ++--
>  security/integrity/ima/ima_asymmetric_keys.c |   4 +-
>  security/integrity/ima/ima_fs.c              |  16 ++-
>  security/integrity/ima/ima_init.c            |   8 +-
>  security/integrity/ima/ima_init_ima_ns.c     |   6 +
>  security/integrity/ima/ima_main.c            |  83 +++++++----
>  security/integrity/ima/ima_policy.c          | 142 ++++++++++---------
>  security/integrity/ima/ima_queue_keys.c      |  11 +-
>  10 files changed, 213 insertions(+), 142 deletions(-)
> 
> diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h
> index c4af3275f015..0b3dc9425076 100644
> --- a/security/integrity/ima/ima.h
> +++ b/security/integrity/ima/ima.h
> @@ -20,6 +20,7 @@
>  #include <linux/hash.h>
>  #include <linux/tpm.h>
>  #include <linux/audit.h>
> +#include <linux/user_namespace.h>
>  #include <crypto/hash_info.h>
>  
>  #include "../integrity.h"
> @@ -43,9 +44,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;
>  
> @@ -120,6 +118,14 @@ struct ima_kexec_hdr {
>  };
>  
>  struct ima_namespace {
> +	struct list_head ima_default_rules;
> +	/* ns's policy rules */

Thank you for adding comments.  Why is the ima_default_rules not
considered "ns's policy rules"?   Will this come later or is it limited
to init_ima_ns?

> +	struct list_head ima_policy_rules;
> +	struct list_head ima_temp_rules;
> +	/* Pointer to ns's current policy */
> +	struct list_head __rcu *ima_rules;

Since "Pointer to ns's current policy" only refers to ima_rules, append
it to the variable definition.

> +	/* current content of the policy */
> +	int ima_policy_flag;

Similarly here append the comment to the variable definition.

>  } __randomize_layout;
>  extern struct ima_namespace init_ima_ns;

thanks,

Mimi



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

* Re: [PATCH v8 05/19] ima: Move measurement list related variables into ima_namespace
  2022-01-04 17:04 ` [PATCH v8 05/19] ima: Move measurement list related variables " Stefan Berger
@ 2022-01-13 20:27   ` Mimi Zohar
  2022-01-19 12:23     ` Stefan Berger
  0 siblings, 1 reply; 50+ messages in thread
From: Mimi Zohar @ 2022-01-13 20:27 UTC (permalink / raw)
  To: Stefan Berger, linux-integrity
  Cc: serge, christian.brauner, containers, dmitry.kasatkin, ebiederm,
	krzysztof.struczynski, roberto.sassu, mpeters, lhinds, lsturman,
	puiterwi, jejb, jamjoom, linux-kernel, paul, rgb,
	linux-security-module, jmorris, Stefan Berger

Hi Stefan,

On Tue, 2022-01-04 at 12:04 -0500, Stefan Berger wrote:
> From: Stefan Berger <stefanb@linux.ibm.com>
> 
> 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.
> 
> Signed-off-by: Stefan Berger <stefanb@linux.ibm.com>
> ---
>  security/integrity/ima/ima.h             |  5 +++--
>  security/integrity/ima/ima_fs.c          |  6 ++++--
>  security/integrity/ima/ima_init_ima_ns.c |  5 +++++
>  security/integrity/ima/ima_kexec.c       | 12 +++++++-----
>  security/integrity/ima/ima_queue.c       | 24 ++++++++++--------------
>  5 files changed, 29 insertions(+), 23 deletions(-)
> 
> diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h
> index 68d8a8e6fd1d..ee16ce5050c8 100644
> --- a/security/integrity/ima/ima.h
> +++ b/security/integrity/ima/ima.h
> @@ -106,7 +106,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 {
> @@ -134,6 +133,8 @@ struct ima_namespace {
>  	int ima_policy_flag;
>  
>  	struct ima_h_table ima_htable;
> +	struct list_head ima_measurements;
> +	unsigned long binary_runtime_size;

Please add a comment indicating binary_runtime_size is only applicable
to ns_ima_init.


>  } __randomize_layout;
>  extern struct ima_namespace init_ima_ns;


> @@ -124,12 +119,13 @@ 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);
>  }
>  

Please update the function description indicating that either carrying
the measurement list across kexec is limited to ns_ima_init or not
supported by namespaces.

thanks,

Mimi


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

* Re: [PATCH v8 07/19] ima: Move dentry into ima_namespace and others onto stack
  2022-01-04 17:04 ` [PATCH v8 07/19] ima: Move dentry into ima_namespace and others onto stack Stefan Berger
@ 2022-01-13 20:28   ` Mimi Zohar
  2022-01-18 20:12     ` Stefan Berger
  0 siblings, 1 reply; 50+ messages in thread
From: Mimi Zohar @ 2022-01-13 20:28 UTC (permalink / raw)
  To: Stefan Berger, linux-integrity
  Cc: serge, christian.brauner, containers, dmitry.kasatkin, ebiederm,
	krzysztof.struczynski, roberto.sassu, mpeters, lhinds, lsturman,
	puiterwi, jejb, jamjoom, linux-kernel, paul, rgb,
	linux-security-module, jmorris, Stefan Berger

Hi Stefan,

Nobody refers to the IMA securityfs files as dentries.  The Subject
line is suppose to provide a hint about the patch.  How about changing
the "Subject" line to "ima: Move IMA securityfs files into
ima_namespaces or onto stack".

On Tue, 2022-01-04 at 12:04 -0500, Stefan Berger wrote:
> From: Stefan Berger <stefanb@linux.ibm.com>
> 
> Move the policy file dentry into the ima_namespace for reuse by
> virtualized SecurityFS and for being able to remove it from
> the filesystem. Move the other dentries onto the stack.

Missing is an explanation why the other IMA securityfs files can be on
the stack.  Maybe start out by saying that the ns_ima_init securityfs
files are never deleted.  Then transition into the IMA namespaced
securityfs files and how they will be deleted.

> 
> Signed-off-by: Stefan Berger <stefanb@linux.ibm.com>
> ---
>  security/integrity/ima/ima.h    |  2 ++
>  security/integrity/ima/ima_fs.c | 32 ++++++++++++++++++--------------
>  2 files changed, 20 insertions(+), 14 deletions(-)
> 
> diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h
> index 82b3f6a98320..224b09617c52 100644
> --- a/security/integrity/ima/ima.h
> +++ b/security/integrity/ima/ima.h
> @@ -140,6 +140,8 @@ struct ima_namespace {
>  	struct mutex ima_write_mutex;
>  	unsigned long ima_fs_flags;
>  	int valid_policy;
> +
> +	struct dentry *policy_dentry;

None of the other securityfs files are renamed.  Why is "ima_policy" 
being renamed to "policy_dentry"?  If there is a need, it should be
documented in the patch description.

thanks,

Mimi

>  } __randomize_layout;
>  extern struct ima_namespace init_ima_ns;
> 


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

* Re: [PATCH v8 08/19] ima: Use mac_admin_ns_capable() to check corresponding capability
  2022-01-04 17:04 ` [PATCH v8 08/19] ima: Use mac_admin_ns_capable() to check corresponding capability Stefan Berger
  2022-01-05 20:55     ` kernel test robot
@ 2022-01-13 20:28   ` Mimi Zohar
  1 sibling, 0 replies; 50+ messages in thread
From: Mimi Zohar @ 2022-01-13 20:28 UTC (permalink / raw)
  To: Stefan Berger, linux-integrity
  Cc: serge, christian.brauner, containers, dmitry.kasatkin, ebiederm,
	krzysztof.struczynski, roberto.sassu, mpeters, lhinds, lsturman,
	puiterwi, jejb, jamjoom, linux-kernel, paul, rgb,
	linux-security-module, jmorris, Stefan Berger, Denis Semakin

Hi Stefan, Denis,

mac_admin_ns_capable() is being introduced in this patch.  Either
rename the "Subject" line as "ima: replace capable() call with
ns_capable()" or "ima: define mac_admin_ns_capable() as a wrapper for
ns_capable()".

On Tue, 2022-01-04 at 12:04 -0500, Stefan Berger wrote:
> From: Stefan Berger <stefanb@linux.ibm.com>
> 
> Use mac_admin_ns_capable() to check corresponding capability to allow
> read/write IMA policy without CAP_SYS_ADMIN but with CAP_MAC_ADMIN.

Updatethe patch description accordingly.

> 
> Signed-off-by: Denis Semakin <denis.semakin@huawei.com>
> Signed-off-by: Stefan Berger <stefanb@linux.ibm.com>

thanks,

Mimi


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

* Re: [PATCH v8 03/19] ima: Move policy related variables into ima_namespace
  2022-01-13 20:26   ` Mimi Zohar
@ 2022-01-14 10:48     ` Christian Brauner
  2022-01-19 13:32     ` Stefan Berger
  1 sibling, 0 replies; 50+ messages in thread
From: Christian Brauner @ 2022-01-14 10:48 UTC (permalink / raw)
  To: Mimi Zohar
  Cc: Stefan Berger, linux-integrity, serge, christian.brauner,
	containers, dmitry.kasatkin, ebiederm, krzysztof.struczynski,
	roberto.sassu, mpeters, lhinds, lsturman, puiterwi, jejb,
	jamjoom, linux-kernel, paul, rgb, linux-security-module, jmorris,
	Stefan Berger

On Thu, Jan 13, 2022 at 03:26:51PM -0500, Mimi Zohar wrote:
> Hi Stefan,
> 
> On Tue, 2022-01-04 at 12:04 -0500, Stefan Berger wrote:
> > From: Stefan Berger <stefanb@linux.ibm.com>
> > 
> > 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_ns_from_file() to get the IMA namespace via the user
> > namespace of the SecurityFS superblock that a file belongs to.
> > 
> > To get the current ima_namespace use get_current_ns() when a function
> > that is related to a policy rule is called. In other cases where functions
> > are called due file attribute modifications, use init_ima_ns, since these
> > functions are related to IMA appraisal and changes to file attributes are
> > only relevant to the init_ima_ns until IMA namespaces also support IMA
> > appraisal. In ima_file_free() use init_ima_ns since in this case flags
> > related to file measurements may be affected, which is not supported in
> > IMA namespaces, yet.
> > 
> > Signed-off-by: Stefan Berger <stefanb@linux.ibm.com>
> 
> Please split this patch into "ima: pass through ima namespace", or some
> other name,  and "ima: Move policy related variables into
> ima_namespace".  The other option is to combine the "pass through ima
> namespace" with the 2nd patch, like Christian's example.

I was just about to comment something similar on 02/19.

I would also not introduce get_current_ns(). It's simply more honest and
if &init_ima_ns is used everywhere until multiple namespaces can
actually exists.

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

* Re: [PATCH v8 10/19] ima: Implement hierarchical processing of file accesses
  2022-01-04 17:04 ` [PATCH v8 10/19] ima: Implement hierarchical processing of file accesses Stefan Berger
@ 2022-01-14 11:21   ` Christian Brauner
  2022-01-18 18:25     ` Stefan Berger
  0 siblings, 1 reply; 50+ messages in thread
From: Christian Brauner @ 2022-01-14 11:21 UTC (permalink / raw)
  To: Stefan Berger
  Cc: linux-integrity, zohar, serge, christian.brauner, containers,
	dmitry.kasatkin, ebiederm, krzysztof.struczynski, roberto.sassu,
	mpeters, lhinds, lsturman, puiterwi, jejb, jamjoom, linux-kernel,
	paul, rgb, linux-security-module, jmorris, Stefan Berger

On Tue, Jan 04, 2022 at 12:04:07PM -0500, Stefan Berger wrote:
> From: Stefan Berger <stefanb@linux.ibm.com>
> 
> Implement hierarchical processing of file accesses in IMA namespaces by
> walking the list of user namespaces towards the root. This way file
> accesses can be audited in an IMA namespace and also be evaluated against
> the IMA policies of parent IMA namespaces.
> 
> __process_measurement() returns either 0 or -EACCES. For hierarchical
> processing remember the -EACCES returned by this function but continue
> to the parent user namespace. At the end either return 0 or -EACCES
> if an error occurred in one of the IMA namespaces.
> 
> Currently the ima_ns pointer of the user_namespace is always NULL except
> at the init_user_ns, so test ima_ns for NULL pointer and skip the call to
> __process_measurement() if it is NULL. Once IMA namespacing is fully
> enabled, the pointer may also be NULL due to late initialization of the
> IMA namespace.
> 
> Signed-off-by: Stefan Berger <stefanb@linux.ibm.com>
> ---
>  include/linux/ima.h               |  6 +++++
>  security/integrity/ima/ima_main.c | 37 +++++++++++++++++++++++++++----
>  2 files changed, 39 insertions(+), 4 deletions(-)
> 
> diff --git a/include/linux/ima.h b/include/linux/ima.h
> index b6ab66a546ae..fcee2a51bb87 100644
> --- a/include/linux/ima.h
> +++ b/include/linux/ima.h
> @@ -65,6 +65,12 @@ static inline const char * const *arch_get_ima_policy(void)
>  }
>  #endif
>  
> +static inline struct user_namespace
> +*ima_ns_to_user_ns(struct ima_namespace *ns)
> +{
> +	return current_user_ns();
> +}
> +
>  #else
>  static inline enum hash_algo ima_get_current_hash_algo(void)
>  {
> diff --git a/security/integrity/ima/ima_main.c b/security/integrity/ima/ima_main.c
> index 621685d4eb95..51b0ef1cebbe 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;
> @@ -395,6 +395,35 @@ static int process_measurement(struct ima_namespace *ns,
>  	return 0;
>  }
>  
> +static int process_measurement(struct ima_namespace *ns,
> +			       struct file *file, const struct cred *cred,
> +			       u32 secid, char *buf, loff_t size, int mask,
> +			       enum ima_hooks func)
> +{
> +	struct user_namespace *user_ns = ima_ns_to_user_ns(ns);
> +	int ret = 0;
> +
> +	while (user_ns) {
> +		ns = ima_ns_from_user_ns(user_ns);
> +		if (ns) {
> +			int rc;
> +
> +			rc = __process_measurement(ns, file, cred, secid, buf,
> +						   size, mask, func);
> +			switch (rc) {
> +			case -EACCES:
> +				/* return this error at the end but continue */
> +				ret = -EACCES;
> +				break;

This seems risky. Every error not -EACCES will be counted as a success.
It doesn't look like __process_measurement() will return anything else
but I would still place a WARN_ON() or WARN_ON_ONCE() in there to make
that assumption explicit.

Right now it looks like your only error condition is -EACCES and non-ima
cracks like me need to read through __process_measurement() to figure
out that that's ok. With a WARN_ON* in there I'd not have needed to bother.

switch (rc) {
case -EACCES:
	/* return this error at the end but continue */
	ret = -EACCES;
	break
default:
	WARN_ON_ONCE(true);
}

or sm similar.

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

* Re: [PATCH v8 13/19] ima: Add functions for creation and freeing of an ima_namespace
  2022-01-04 17:04 ` [PATCH v8 13/19] ima: Add functions for creation and freeing of an ima_namespace Stefan Berger
@ 2022-01-14 11:43   ` Christian Brauner
  0 siblings, 0 replies; 50+ messages in thread
From: Christian Brauner @ 2022-01-14 11:43 UTC (permalink / raw)
  To: Stefan Berger
  Cc: linux-integrity, zohar, serge, christian.brauner, containers,
	dmitry.kasatkin, ebiederm, krzysztof.struczynski, roberto.sassu,
	mpeters, lhinds, lsturman, puiterwi, jejb, jamjoom, linux-kernel,
	paul, rgb, linux-security-module, jmorris, Stefan Berger

On Tue, Jan 04, 2022 at 12:04:10PM -0500, Stefan Berger wrote:
> From: Stefan Berger <stefanb@linux.ibm.com>
> 
> Implement create_ima_ns() to create and initialize an ima_namespace
> and implement free_ima_ns() to free it.
> 
> Signed-off-by: Stefan Berger <stefanb@linux.ibm.com>
> ---
>  include/linux/ima.h                      | 13 +++++
>  security/integrity/ima/Makefile          |  1 +
>  security/integrity/ima/ima.h             | 16 +++++++
>  security/integrity/ima/ima_init_ima_ns.c |  2 +-
>  security/integrity/ima/ima_ns.c          | 61 ++++++++++++++++++++++++
>  5 files changed, 92 insertions(+), 1 deletion(-)
>  create mode 100644 security/integrity/ima/ima_ns.c
> 
> diff --git a/include/linux/ima.h b/include/linux/ima.h
> index e1d65162d1fb..06c88cb17b21 100644
> --- a/include/linux/ima.h
> +++ b/include/linux/ima.h
> @@ -226,4 +226,17 @@ static inline bool ima_appraise_signature(enum kernel_read_file_id func)
>  	return false;
>  }
>  #endif /* CONFIG_IMA_APPRAISE && CONFIG_INTEGRITY_TRUSTED_KEYRING */
> +
> +#ifdef CONFIG_IMA_NS
> +
> +void free_ima_ns(struct user_namespace *ns);
> +
> +#else
> +
> +static inline void free_ima_ns(struct user_namespace *user_ns)
> +{
> +}
> +
> +#endif /* CONFIG_IMA_NS */
> +
>  #endif /* _LINUX_IMA_H */
> diff --git a/security/integrity/ima/Makefile b/security/integrity/ima/Makefile
> index f8a5e5f3975d..b86a35fbed60 100644
> --- a/security/integrity/ima/Makefile
> +++ b/security/integrity/ima/Makefile
> @@ -14,6 +14,7 @@ ima-$(CONFIG_HAVE_IMA_KEXEC) += ima_kexec.o
>  ima-$(CONFIG_IMA_BLACKLIST_KEYRING) += ima_mok.o
>  ima-$(CONFIG_IMA_MEASURE_ASYMMETRIC_KEYS) += ima_asymmetric_keys.o
>  ima-$(CONFIG_IMA_QUEUE_EARLY_BOOT_KEYS) += ima_queue_keys.o
> +ima-$(CONFIG_IMA_NS) += ima_ns.o
>  
>  ifeq ($(CONFIG_EFI),y)
>  ima-$(CONFIG_IMA_SECURE_AND_OR_TRUSTED_BOOT) += ima_efi.o
> diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h
> index f63c6f22b853..4255301e5b96 100644
> --- a/security/integrity/ima/ima.h
> +++ b/security/integrity/ima/ima.h
> @@ -163,6 +163,7 @@ extern bool ima_canonical_fmt;
>  int ima_init(void);
>  int ima_fs_init(void);
>  int ima_ns_init(void);
> +int ima_init_namespace(struct ima_namespace *ns);
>  int ima_add_template_entry(struct ima_namespace *ns,
>  			   struct ima_template_entry *entry, int violation,
>  			   const char *op, struct inode *inode,
> @@ -503,4 +504,19 @@ static inline struct ima_namespace *get_current_ns(void)
>  	return &init_ima_ns;
>  }
>  
> +#ifdef CONFIG_IMA_NS
> +
> +struct ima_namespace *create_ima_ns(struct user_namespace *user_ns);
> +
> +#else
> +
> +static inline struct ima_namespace *
> +create_ima_ns(struct user_namespace *user_ns)

Why are you introducing these functions in 13/19 when you're first using
it in the last patch of this series?

> +{
> +	WARN(1, "Cannot create an IMA namespace\n");
> +	return ERR_PTR(-EFAULT);
> +}

I think I pointed this out earlier or made illustrated it in my draft
patch earlier. This function should not return a ptr to an ima
namespace. That's just not necessary: the ima namespace _is stashed in
struct user namespace_:

static int create_ima_ns(struct uesr_namespace *user_ns)
{
	[...]

	/* Pairs with [...] */
	smp_store_release(&user_ns->ima_ns, ns);
	return 0;
}

and in the other case:

static inline create_ima_ns(struct user_namespace *user_ns)
{
	return -EINVAL;
}

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

* Re: [PATCH v8 19/19] ima: Enable IMA namespaces
  2022-01-04 17:04 ` [PATCH v8 19/19] ima: Enable IMA namespaces Stefan Berger
@ 2022-01-14 12:05   ` Christian Brauner
  2022-01-18 17:53     ` Stefan Berger
  2022-01-14 14:45   ` Christian Brauner
  1 sibling, 1 reply; 50+ messages in thread
From: Christian Brauner @ 2022-01-14 12:05 UTC (permalink / raw)
  To: Stefan Berger
  Cc: linux-integrity, zohar, serge, christian.brauner, containers,
	dmitry.kasatkin, ebiederm, krzysztof.struczynski, roberto.sassu,
	mpeters, lhinds, lsturman, puiterwi, jejb, jamjoom, linux-kernel,
	paul, rgb, linux-security-module, jmorris, Stefan Berger

On Tue, Jan 04, 2022 at 12:04:16PM -0500, Stefan Berger wrote:
> From: Stefan Berger <stefanb@linux.ibm.com>
> 
> Introduce the IMA_NS in Kconfig for IMA namespace enablement.
> 
> Enable the lazy initialization of an IMA namespace when a user mounts
> SecurityFS. Now a user_namespace will get a pointer to an ima_namespace
> and therefore add an implementation of get_current_ns() that returns this
> pointer.
> 
> get_current_ns() may now return a NULL pointer for as long as the IMA
> namespace hasn't been created, yet. Therefore, return early from those
> functions that may now get a NULL pointer from this call. The NULL
> pointer can typically be treated similar to not having an IMA policy set
> and simply return early from a function.
> 
> Implement ima_ns_from_file() for SecurityFS-related files where we can
> now get the IMA namespace via the user namespace pointer associated
> with the superblock of the SecurityFS filesystem instance. Since
> the functions using ima_ns_from_file() will only be called after an
> ima_namesapce has been allocated they will never get a NULL pointer
> for the ima_namespace.
> 
> Switch access to userns->ima_ns to use acquire/release semantics to ensure
> that a newly created ima_namespace structure is fully visible upon access.
> 
> Replace usage of current_user_ns() with ima_ns_from_user_ns() that
> implements a method to derive the user_namespace from the given
> ima_namespace. It leads to the same result.
> 
> Signed-off-by: Stefan Berger <stefanb@linux.ibm.com>
> ---
>  include/linux/ima.h                 |  9 ++++++-
>  init/Kconfig                        | 13 ++++++++++
>  kernel/user_namespace.c             |  2 ++
>  security/integrity/ima/ima.h        | 35 ++++++++++++++++++++++-----
>  security/integrity/ima/ima_fs.c     | 37 ++++++++++++++++++++++-------
>  security/integrity/ima/ima_main.c   | 29 ++++++++++++++++------
>  security/integrity/ima/ima_ns.c     |  3 ++-
>  security/integrity/ima/ima_policy.c | 13 +++++-----
>  8 files changed, 112 insertions(+), 29 deletions(-)
> 
> diff --git a/include/linux/ima.h b/include/linux/ima.h
> index 5354e83d1694..7b9713b290ae 100644
> --- a/include/linux/ima.h
> +++ b/include/linux/ima.h
> @@ -11,6 +11,7 @@
>  #include <linux/fs.h>
>  #include <linux/security.h>
>  #include <linux/kexec.h>
> +#include <linux/user_namespace.h>
>  #include <crypto/hash_info.h>
>  struct linux_binprm;
>  
> @@ -71,7 +72,13 @@ static inline const char * const *arch_get_ima_policy(void)
>  static inline struct user_namespace
>  *ima_ns_to_user_ns(struct ima_namespace *ns)
>  {
> -	return current_user_ns();
> +	struct user_namespace *user_ns;
> +
> +	user_ns = current_user_ns();
> +#ifdef CONFIG_IMA_NS
> +	WARN_ON(user_ns->ima_ns != ns);
> +#endif
> +	return user_ns;
>  }
>  
>  #else
> diff --git a/init/Kconfig b/init/Kconfig
> index 4b7bac10c72d..e27155e0ddba 100644
> --- a/init/Kconfig
> +++ b/init/Kconfig
> @@ -1247,6 +1247,19 @@ config NET_NS
>  	  Allow user space to create what appear to be multiple instances
>  	  of the network stack.
>  
> +config IMA_NS
> +	bool "IMA namespace"
> +	depends on USER_NS
> +	depends on IMA
> +	default n
> +	help
> +	  Allow the creation of an IMA namespace 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
>  
>  config CHECKPOINT_RESTORE
> diff --git a/kernel/user_namespace.c b/kernel/user_namespace.c
> index 6b2e3ca7ee99..653f8fa83b69 100644
> --- a/kernel/user_namespace.c
> +++ b/kernel/user_namespace.c
> @@ -20,6 +20,7 @@
>  #include <linux/fs_struct.h>
>  #include <linux/bsearch.h>
>  #include <linux/sort.h>
> +#include <linux/ima.h>
>  
>  static struct kmem_cache *user_ns_cachep __read_mostly;
>  static DEFINE_MUTEX(userns_state_mutex);
> @@ -196,6 +197,7 @@ static void free_user_ns(struct work_struct *work)
>  			kfree(ns->projid_map.forward);
>  			kfree(ns->projid_map.reverse);
>  		}
> +		free_ima_ns(ns);
>  		retire_userns_sysctls(ns);
>  		key_free_user_ns(ns);
>  		ns_free_inum(&ns->ns);
> diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h
> index 344c8c4bd030..d993655ec796 100644
> --- a/security/integrity/ima/ima.h
> +++ b/security/integrity/ima/ima.h
> @@ -509,21 +509,20 @@ struct user_namespace *ima_user_ns_from_file(const struct file *filp)
>  	return file_inode(filp)->i_sb->s_user_ns;
>  }
>  
> +#ifdef CONFIG_IMA_NS
> +
>  static inline struct ima_namespace
>  *ima_ns_from_user_ns(struct user_namespace *user_ns)
>  {
> -	if (user_ns == &init_user_ns)
> -		return &init_ima_ns;
> -	return NULL;
> +	/* Pairs with smp_store_releases() in create_ima_ns(). */
> +	return smp_load_acquire(&user_ns->ima_ns);
>  }
>  
>  static inline struct ima_namespace *get_current_ns(void)
>  {
> -	return &init_ima_ns;
> +	return ima_ns_from_user_ns(current_user_ns());
>  }
>  
> -#ifdef CONFIG_IMA_NS
> -
>  struct ima_namespace *create_ima_ns(struct user_namespace *user_ns);
>  
>  struct ns_status *ima_get_ns_status(struct ima_namespace *ns,
> @@ -532,6 +531,11 @@ struct ns_status *ima_get_ns_status(struct ima_namespace *ns,
>  
>  void ima_free_ns_status_tree(struct ima_namespace *ns);
>  
> +static inline struct ima_namespace *ima_ns_from_file(const struct file *filp)
> +{
> +	return ima_user_ns_from_file(filp)->ima_ns;
> +}
> +
>  #define IMA_NS_STATUS_ACTIONS   IMA_AUDIT
>  #define IMA_NS_STATUS_FLAGS     IMA_AUDITED
>  
> @@ -542,6 +546,20 @@ unsigned long set_iint_flags(struct integrity_iint_cache *iint,
>  
>  #else
>  
> +static inline struct ima_namespace
> +*ima_ns_from_user_ns(struct user_namespace *user_ns)
> +{
> +	if (user_ns == &init_user_ns)
> +		return &init_ima_ns;
> +	return NULL;
> +}
> +
> +
> +static inline struct ima_namespace *get_current_ns(void)
> +{
> +	return &init_ima_ns;
> +}
> +
>  static inline struct ima_namespace *
>  create_ima_ns(struct user_namespace *user_ns)
>  {
> @@ -572,6 +590,11 @@ static inline unsigned long set_iint_flags(struct integrity_iint_cache *iint,
>  	return flags;
>  }
>  
> +static inline struct ima_namespace *ima_ns_from_file(const struct file *filp)
> +{
> +	return &init_ima_ns;
> +}
> +
>  #endif /* CONFIG_IMA_NS */
>  
>  #endif /* __LINUX_IMA_H */
> diff --git a/security/integrity/ima/ima_fs.c b/security/integrity/ima/ima_fs.c
> index 468508f6a7e8..ee3af81d1c3e 100644
> --- a/security/integrity/ima/ima_fs.c
> +++ b/security/integrity/ima/ima_fs.c
> @@ -49,7 +49,7 @@ static ssize_t ima_show_htable_violations(struct file *filp,
>  					  char __user *buf,
>  					  size_t count, loff_t *ppos)
>  {
> -	struct ima_namespace *ns = &init_ima_ns;
> +	struct ima_namespace *ns = ima_ns_from_file(filp);
>  
>  	return ima_show_htable_value(buf, count, ppos,
>  				     &ns->ima_htable.violations);
> @@ -64,7 +64,7 @@ static ssize_t ima_show_measurements_count(struct file *filp,
>  					   char __user *buf,
>  					   size_t count, loff_t *ppos)
>  {
> -	struct ima_namespace *ns = &init_ima_ns;
> +	struct ima_namespace *ns = ima_ns_from_file(filp);
>  
>  	return ima_show_htable_value(buf, count, ppos, &ns->ima_htable.len);
>  }
> @@ -77,7 +77,7 @@ static const struct file_operations ima_measurements_count_ops = {
>  /* returns pointer to hlist_node */
>  static void *ima_measurements_start(struct seq_file *m, loff_t *pos)
>  {
> -	struct ima_namespace *ns = &init_ima_ns;
> +	struct ima_namespace *ns = ima_ns_from_file(m->file);
>  	loff_t l = *pos;
>  	struct ima_queue_entry *qe;
>  
> @@ -95,7 +95,7 @@ static void *ima_measurements_start(struct seq_file *m, loff_t *pos)
>  
>  static void *ima_measurements_next(struct seq_file *m, void *v, loff_t *pos)
>  {
> -	struct ima_namespace *ns = &init_ima_ns;
> +	struct ima_namespace *ns = ima_ns_from_file(m->file);
>  	struct ima_queue_entry *qe = v;
>  
>  	/* lock protects when reading beyond last element
> @@ -317,7 +317,7 @@ static ssize_t ima_read_policy(struct ima_namespace *ns, char *path)
>  static ssize_t ima_write_policy(struct file *file, const char __user *buf,
>  				size_t datalen, loff_t *ppos)
>  {
> -	struct ima_namespace *ns = &init_ima_ns;
> +	struct ima_namespace *ns = ima_ns_from_file(file);
>  	char *data;
>  	ssize_t result;
>  
> @@ -379,7 +379,7 @@ static const struct seq_operations ima_policy_seqops = {
>  static int ima_open_policy(struct inode *inode, struct file *filp)
>  {
>  	struct user_namespace *user_ns = ima_user_ns_from_file(filp);
> -	struct ima_namespace *ns = &init_ima_ns;
> +	struct ima_namespace *ns = ima_ns_from_file(filp);
>  
>  	if (!(filp->f_flags & O_WRONLY)) {
>  #ifndef	CONFIG_IMA_READ_POLICY
> @@ -406,7 +406,7 @@ static int ima_open_policy(struct inode *inode, struct file *filp)
>   */
>  static int ima_release_policy(struct inode *inode, struct file *file)
>  {
> -	struct ima_namespace *ns = &init_ima_ns;
> +	struct ima_namespace *ns = ima_ns_from_file(file);
>  	const char *cause = ns->valid_policy ? "completed" : "failed";
>  
>  	if ((file->f_flags & O_ACCMODE) == O_RDONLY)
> @@ -459,12 +459,29 @@ int ima_fs_ns_init(struct user_namespace *user_ns, struct dentry *root)
>  	struct dentry *ascii_runtime_measurements = NULL;
>  	struct dentry *runtime_measurements_count = NULL;
>  	struct dentry *violations = NULL;
> +	bool created_ns = false;
> +
> +	/*
> +	 * While multiple superblocks can exist they are keyed by userns in
> +	 * s_fs_info for securityfs. The first time a userns mounts a
> +	 * securityfs instance we lazily allocate the ima_namespace for the
> +	 * userns since that's the only way a userns can meaningfully use ima.
> +	 * The vfs ensures we're the only one to call fill_super() and hence
> +	 * ima_fs_ns_init(), so we don't need any memory barriers here, i.e.
> +	 * user_ns->ima_ns can't change while we're in here.
> +	 */
> +	if (!ns) {
> +		ns = create_ima_ns(user_ns);
> +		if (IS_ERR(ns))
> +			return PTR_ERR(ns);
> +		created_ns = true;
> +	}

Since create_ima_ns() initializes user_ns->ima_ns via
smp_store_release() the patch currently implies that concurrent access
to user_ns->ima_ns are safe once create_ima_ns() returns.

Specifically, it entails that no caller will access entries in the ima
namespace that will only be filled in past this point. Afaict, this only
relates to the ns->policy_dentry which can't be accessed until
securityfs is finished.

Nonetheless, I would recommend that you change create_ima_ns() to not
initialize user_ns->ima_ns and instead defer this until everything in
the namespace is setup. So maybe move the smp_store_release() to the end
of ima_fs_ns_init(). If ns->policy_dentry wouldn't be stashed in ima_ns
it wouldn't matter but since it is I would not publish ima_ns before
this is set. Sm like (uncompiled, untested):

diff --git a/security/integrity/ima/ima_fs.c b/security/integrity/ima/ima_fs.c
index ee3af81d1c3e..64ca47671d31 100644
--- a/security/integrity/ima/ima_fs.c
+++ b/security/integrity/ima/ima_fs.c
@@ -531,6 +531,8 @@ int ima_fs_ns_init(struct user_namespace *user_ns, struct dentry *root)
                        goto out;
        }

+       if (!user_ns->ima_ns)
+               smp_store_release(&user_ns->ima_ns, ns);
        return 0;
 out:
        securityfs_remove(ns->policy_dentry);

As a side-effect this will let you get rid of the bool created_ns and
thereby simplify the codeflow.

(Note, that obviously means that the changes I mentioned earlier in
https://lore.kernel.org/containers/20220114114321.7prnt72ukvch4wxa@wittgenstein
can't be made.)

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

* Re: [PATCH v8 18/19] ima: Show owning user namespace's uid and gid when displaying policy
  2022-01-04 17:04 ` [PATCH v8 18/19] ima: Show owning user namespace's uid and gid when displaying policy Stefan Berger
@ 2022-01-14 13:45   ` Christian Brauner
  2022-01-18 16:31     ` Stefan Berger
  0 siblings, 1 reply; 50+ messages in thread
From: Christian Brauner @ 2022-01-14 13:45 UTC (permalink / raw)
  To: Stefan Berger
  Cc: linux-integrity, zohar, serge, christian.brauner, containers,
	dmitry.kasatkin, ebiederm, krzysztof.struczynski, roberto.sassu,
	mpeters, lhinds, lsturman, puiterwi, jejb, jamjoom, linux-kernel,
	paul, rgb, linux-security-module, jmorris, Stefan Berger

On Tue, Jan 04, 2022 at 12:04:15PM -0500, Stefan Berger wrote:
> From: Stefan Berger <stefanb@linux.ibm.com>
> 
> Show the uid and gid values of the owning user namespace when displaying
> the IMA policy rather than the kernel uid and gid values. Now the same uid
> and gid values are shown in the policy as those that were used when the
> policy was set.
> 
> Signed-off-by: Stefan Berger <stefanb@linux.ibm.com>
> ---
>  security/integrity/ima/ima_policy.c | 19 +++++++++++++------
>  1 file changed, 13 insertions(+), 6 deletions(-)
> 
> diff --git a/security/integrity/ima/ima_policy.c b/security/integrity/ima/ima_policy.c
> index 15c68dc5da9e..b7dbc687b6ff 100644
> --- a/security/integrity/ima/ima_policy.c
> +++ b/security/integrity/ima/ima_policy.c
> @@ -1997,6 +1997,7 @@ static void ima_policy_show_appraise_algos(struct seq_file *m,
>  
>  int ima_policy_show(struct seq_file *m, void *v)
>  {
> +	struct user_namespace *user_ns = ima_user_ns_from_file(m->file);

Hm, so when looking at the policy entries via seq_file's .show method
and displaying the {g,u}id values of the rules we don't want the values
resolved according to the user namespace the securityfs instances was
mounted in. That would be misleading for callers that are in an
ancestor userns (which we allow in .permission).

So we want to make sure that we see the values as the opener of the file
would see them. This is similar to e.g. looking at a task's ids through
/proc/<pid>/status. So this should be seq_user_ns(m) instead of
ima_user_ns_from_file().

>  	struct ima_rule_entry *entry = v;
>  	int i;
>  	char tbuf[64] = {0,};
> @@ -2074,7 +2075,8 @@ int ima_policy_show(struct seq_file *m, void *v)
>  	}
>  
>  	if (entry->flags & IMA_UID) {
> -		snprintf(tbuf, sizeof(tbuf), "%d", __kuid_val(entry->uid));
> +		snprintf(tbuf, sizeof(tbuf),
> +			 "%d", from_kuid(user_ns, entry->uid));

This should be from_k{g,u}id_munged().

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

* Re: [PATCH v8 19/19] ima: Enable IMA namespaces
  2022-01-04 17:04 ` [PATCH v8 19/19] ima: Enable IMA namespaces Stefan Berger
  2022-01-14 12:05   ` Christian Brauner
@ 2022-01-14 14:45   ` Christian Brauner
  2022-01-18 18:09     ` Stefan Berger
  1 sibling, 1 reply; 50+ messages in thread
From: Christian Brauner @ 2022-01-14 14:45 UTC (permalink / raw)
  To: Stefan Berger
  Cc: linux-integrity, zohar, serge, christian.brauner, containers,
	dmitry.kasatkin, ebiederm, krzysztof.struczynski, roberto.sassu,
	mpeters, lhinds, lsturman, puiterwi, jejb, jamjoom, linux-kernel,
	paul, rgb, linux-security-module, jmorris, Stefan Berger

On Tue, Jan 04, 2022 at 12:04:16PM -0500, Stefan Berger wrote:
> From: Stefan Berger <stefanb@linux.ibm.com>
> 
> Introduce the IMA_NS in Kconfig for IMA namespace enablement.
> 
> Enable the lazy initialization of an IMA namespace when a user mounts
> SecurityFS. Now a user_namespace will get a pointer to an ima_namespace
> and therefore add an implementation of get_current_ns() that returns this
> pointer.
> 
> get_current_ns() may now return a NULL pointer for as long as the IMA
> namespace hasn't been created, yet. Therefore, return early from those
> functions that may now get a NULL pointer from this call. The NULL
> pointer can typically be treated similar to not having an IMA policy set
> and simply return early from a function.
> 
> Implement ima_ns_from_file() for SecurityFS-related files where we can
> now get the IMA namespace via the user namespace pointer associated
> with the superblock of the SecurityFS filesystem instance. Since
> the functions using ima_ns_from_file() will only be called after an
> ima_namesapce has been allocated they will never get a NULL pointer
> for the ima_namespace.
> 
> Switch access to userns->ima_ns to use acquire/release semantics to ensure
> that a newly created ima_namespace structure is fully visible upon access.
> 
> Replace usage of current_user_ns() with ima_ns_from_user_ns() that
> implements a method to derive the user_namespace from the given
> ima_namespace. It leads to the same result.
> 
> Signed-off-by: Stefan Berger <stefanb@linux.ibm.com>
> ---
>  include/linux/ima.h                 |  9 ++++++-
>  init/Kconfig                        | 13 ++++++++++
>  kernel/user_namespace.c             |  2 ++
>  security/integrity/ima/ima.h        | 35 ++++++++++++++++++++++-----
>  security/integrity/ima/ima_fs.c     | 37 ++++++++++++++++++++++-------
>  security/integrity/ima/ima_main.c   | 29 ++++++++++++++++------
>  security/integrity/ima/ima_ns.c     |  3 ++-
>  security/integrity/ima/ima_policy.c | 13 +++++-----
>  8 files changed, 112 insertions(+), 29 deletions(-)
> 
> diff --git a/include/linux/ima.h b/include/linux/ima.h
> index 5354e83d1694..7b9713b290ae 100644
> --- a/include/linux/ima.h
> +++ b/include/linux/ima.h
> @@ -11,6 +11,7 @@
>  #include <linux/fs.h>
>  #include <linux/security.h>
>  #include <linux/kexec.h>
> +#include <linux/user_namespace.h>
>  #include <crypto/hash_info.h>
>  struct linux_binprm;
>  
> @@ -71,7 +72,13 @@ static inline const char * const *arch_get_ima_policy(void)
>  static inline struct user_namespace
>  *ima_ns_to_user_ns(struct ima_namespace *ns)
>  {
> -	return current_user_ns();
> +	struct user_namespace *user_ns;
> +
> +	user_ns = current_user_ns();
> +#ifdef CONFIG_IMA_NS
> +	WARN_ON(user_ns->ima_ns != ns);
> +#endif
> +	return user_ns;
>  }
>  
>  #else
> diff --git a/init/Kconfig b/init/Kconfig
> index 4b7bac10c72d..e27155e0ddba 100644
> --- a/init/Kconfig
> +++ b/init/Kconfig
> @@ -1247,6 +1247,19 @@ config NET_NS
>  	  Allow user space to create what appear to be multiple instances
>  	  of the network stack.
>  
> +config IMA_NS
> +	bool "IMA namespace"
> +	depends on USER_NS
> +	depends on IMA
> +	default n
> +	help
> +	  Allow the creation of an IMA namespace 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
>  
>  config CHECKPOINT_RESTORE
> diff --git a/kernel/user_namespace.c b/kernel/user_namespace.c
> index 6b2e3ca7ee99..653f8fa83b69 100644
> --- a/kernel/user_namespace.c
> +++ b/kernel/user_namespace.c
> @@ -20,6 +20,7 @@
>  #include <linux/fs_struct.h>
>  #include <linux/bsearch.h>
>  #include <linux/sort.h>
> +#include <linux/ima.h>
>  
>  static struct kmem_cache *user_ns_cachep __read_mostly;
>  static DEFINE_MUTEX(userns_state_mutex);
> @@ -196,6 +197,7 @@ static void free_user_ns(struct work_struct *work)
>  			kfree(ns->projid_map.forward);
>  			kfree(ns->projid_map.reverse);
>  		}
> +		free_ima_ns(ns);
>  		retire_userns_sysctls(ns);
>  		key_free_user_ns(ns);
>  		ns_free_inum(&ns->ns);
> diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h
> index 344c8c4bd030..d993655ec796 100644
> --- a/security/integrity/ima/ima.h
> +++ b/security/integrity/ima/ima.h
> @@ -509,21 +509,20 @@ struct user_namespace *ima_user_ns_from_file(const struct file *filp)
>  	return file_inode(filp)->i_sb->s_user_ns;
>  }
>  
> +#ifdef CONFIG_IMA_NS
> +
>  static inline struct ima_namespace
>  *ima_ns_from_user_ns(struct user_namespace *user_ns)
>  {
> -	if (user_ns == &init_user_ns)
> -		return &init_ima_ns;
> -	return NULL;
> +	/* Pairs with smp_store_releases() in create_ima_ns(). */
> +	return smp_load_acquire(&user_ns->ima_ns);
>  }
>  
>  static inline struct ima_namespace *get_current_ns(void)
>  {
> -	return &init_ima_ns;
> +	return ima_ns_from_user_ns(current_user_ns());
>  }
>  
> -#ifdef CONFIG_IMA_NS
> -
>  struct ima_namespace *create_ima_ns(struct user_namespace *user_ns);
>  
>  struct ns_status *ima_get_ns_status(struct ima_namespace *ns,
> @@ -532,6 +531,11 @@ struct ns_status *ima_get_ns_status(struct ima_namespace *ns,
>  
>  void ima_free_ns_status_tree(struct ima_namespace *ns);
>  
> +static inline struct ima_namespace *ima_ns_from_file(const struct file *filp)
> +{
> +	return ima_user_ns_from_file(filp)->ima_ns;
> +}
> +
>  #define IMA_NS_STATUS_ACTIONS   IMA_AUDIT
>  #define IMA_NS_STATUS_FLAGS     IMA_AUDITED
>  
> @@ -542,6 +546,20 @@ unsigned long set_iint_flags(struct integrity_iint_cache *iint,
>  
>  #else
>  
> +static inline struct ima_namespace
> +*ima_ns_from_user_ns(struct user_namespace *user_ns)
> +{
> +	if (user_ns == &init_user_ns)
> +		return &init_ima_ns;
> +	return NULL;
> +}
> +
> +
> +static inline struct ima_namespace *get_current_ns(void)
> +{
> +	return &init_ima_ns;
> +}
> +
>  static inline struct ima_namespace *
>  create_ima_ns(struct user_namespace *user_ns)
>  {
> @@ -572,6 +590,11 @@ static inline unsigned long set_iint_flags(struct integrity_iint_cache *iint,
>  	return flags;
>  }
>  
> +static inline struct ima_namespace *ima_ns_from_file(const struct file *filp)
> +{
> +	return &init_ima_ns;
> +}
> +
>  #endif /* CONFIG_IMA_NS */
>  
>  #endif /* __LINUX_IMA_H */
> diff --git a/security/integrity/ima/ima_fs.c b/security/integrity/ima/ima_fs.c
> index 468508f6a7e8..ee3af81d1c3e 100644
> --- a/security/integrity/ima/ima_fs.c
> +++ b/security/integrity/ima/ima_fs.c
> @@ -49,7 +49,7 @@ static ssize_t ima_show_htable_violations(struct file *filp,
>  					  char __user *buf,
>  					  size_t count, loff_t *ppos)
>  {
> -	struct ima_namespace *ns = &init_ima_ns;
> +	struct ima_namespace *ns = ima_ns_from_file(filp);
>  
>  	return ima_show_htable_value(buf, count, ppos,
>  				     &ns->ima_htable.violations);
> @@ -64,7 +64,7 @@ static ssize_t ima_show_measurements_count(struct file *filp,
>  					   char __user *buf,
>  					   size_t count, loff_t *ppos)
>  {
> -	struct ima_namespace *ns = &init_ima_ns;
> +	struct ima_namespace *ns = ima_ns_from_file(filp);
>  
>  	return ima_show_htable_value(buf, count, ppos, &ns->ima_htable.len);
>  }
> @@ -77,7 +77,7 @@ static const struct file_operations ima_measurements_count_ops = {
>  /* returns pointer to hlist_node */
>  static void *ima_measurements_start(struct seq_file *m, loff_t *pos)
>  {
> -	struct ima_namespace *ns = &init_ima_ns;
> +	struct ima_namespace *ns = ima_ns_from_file(m->file);
>  	loff_t l = *pos;
>  	struct ima_queue_entry *qe;
>  
> @@ -95,7 +95,7 @@ static void *ima_measurements_start(struct seq_file *m, loff_t *pos)
>  
>  static void *ima_measurements_next(struct seq_file *m, void *v, loff_t *pos)
>  {
> -	struct ima_namespace *ns = &init_ima_ns;
> +	struct ima_namespace *ns = ima_ns_from_file(m->file);
>  	struct ima_queue_entry *qe = v;
>  
>  	/* lock protects when reading beyond last element
> @@ -317,7 +317,7 @@ static ssize_t ima_read_policy(struct ima_namespace *ns, char *path)
>  static ssize_t ima_write_policy(struct file *file, const char __user *buf,
>  				size_t datalen, loff_t *ppos)
>  {
> -	struct ima_namespace *ns = &init_ima_ns;
> +	struct ima_namespace *ns = ima_ns_from_file(file);
>  	char *data;
>  	ssize_t result;
>  
> @@ -379,7 +379,7 @@ static const struct seq_operations ima_policy_seqops = {
>  static int ima_open_policy(struct inode *inode, struct file *filp)
>  {
>  	struct user_namespace *user_ns = ima_user_ns_from_file(filp);
> -	struct ima_namespace *ns = &init_ima_ns;
> +	struct ima_namespace *ns = ima_ns_from_file(filp);
>  
>  	if (!(filp->f_flags & O_WRONLY)) {
>  #ifndef	CONFIG_IMA_READ_POLICY
> @@ -406,7 +406,7 @@ static int ima_open_policy(struct inode *inode, struct file *filp)
>   */
>  static int ima_release_policy(struct inode *inode, struct file *file)
>  {
> -	struct ima_namespace *ns = &init_ima_ns;
> +	struct ima_namespace *ns = ima_ns_from_file(file);
>  	const char *cause = ns->valid_policy ? "completed" : "failed";
>  
>  	if ((file->f_flags & O_ACCMODE) == O_RDONLY)
> @@ -459,12 +459,29 @@ int ima_fs_ns_init(struct user_namespace *user_ns, struct dentry *root)
>  	struct dentry *ascii_runtime_measurements = NULL;
>  	struct dentry *runtime_measurements_count = NULL;
>  	struct dentry *violations = NULL;
> +	bool created_ns = false;
> +
> +	/*
> +	 * While multiple superblocks can exist they are keyed by userns in
> +	 * s_fs_info for securityfs. The first time a userns mounts a
> +	 * securityfs instance we lazily allocate the ima_namespace for the
> +	 * userns since that's the only way a userns can meaningfully use ima.
> +	 * The vfs ensures we're the only one to call fill_super() and hence
> +	 * ima_fs_ns_init(), so we don't need any memory barriers here, i.e.
> +	 * user_ns->ima_ns can't change while we're in here.
> +	 */
> +	if (!ns) {
> +		ns = create_ima_ns(user_ns);
> +		if (IS_ERR(ns))
> +			return PTR_ERR(ns);
> +		created_ns = true;
> +	}
>  
>  	/* FIXME: update when evm and integrity are namespaced */
>  	if (user_ns != &init_user_ns) {
>  		int_dir = securityfs_create_dir("integrity", root);
>  		if (IS_ERR(int_dir))
> -			return PTR_ERR(int_dir);
> +			goto free_ns;
>  	} else
>  		int_dir = integrity_dir;
>  
> @@ -526,6 +543,10 @@ int ima_fs_ns_init(struct user_namespace *user_ns, struct dentry *root)
>  	if (user_ns != &init_user_ns)
>  		securityfs_remove(int_dir);
>  
> +free_ns:
> +	if (created_ns)
> +		free_ima_ns(user_ns);
> +
>  	return -1;
>  }
>  
> diff --git a/security/integrity/ima/ima_main.c b/security/integrity/ima/ima_main.c
> index bc3ab08f39c6..fc878577cdd7 100644
> --- a/security/integrity/ima/ima_main.c
> +++ b/security/integrity/ima/ima_main.c
> @@ -483,7 +483,7 @@ int ima_file_mmap(struct file *file, unsigned long prot)
>  	struct ima_namespace *ns = get_current_ns();
>  	u32 secid;
>  
> -	if (file && (prot & PROT_EXEC)) {
> +	if (ns && file && (prot & PROT_EXEC)) {
>  		security_task_getsecid_subj(current, &secid);
>  		return process_measurement(ns, file, current_cred(), secid,
>  					   NULL, 0, MAY_EXEC, MMAP_CHECK);
> @@ -519,7 +519,7 @@ int ima_file_mprotect(struct vm_area_struct *vma, unsigned long prot)
>  	int pcr;
>  
>  	/* Is mprotect making an mmap'ed file executable? */
> -	if (!(ns->ima_policy_flag & IMA_APPRAISE) || !vma->vm_file ||
> +	if (!ns || !(ns->ima_policy_flag & IMA_APPRAISE) || !vma->vm_file ||
>  	    !(prot & PROT_EXEC) || (vma->vm_flags & VM_EXEC))
>  		return 0;
>  
> @@ -565,6 +565,9 @@ int ima_bprm_check(struct linux_binprm *bprm)
>  	int ret;
>  	u32 secid;
>  
> +	if (!ns)
> +		return 0;
> +
>  	security_task_getsecid_subj(current, &secid);
>  	ret = process_measurement(ns, bprm->file, current_cred(), secid, NULL,
>  				  0, MAY_EXEC, BPRM_CHECK);
> @@ -591,6 +594,9 @@ int ima_file_check(struct file *file, int mask)
>  	struct ima_namespace *ns = get_current_ns();
>  	u32 secid;
>  
> +	if (!ns)
> +		return 0;
> +
>  	security_task_getsecid_subj(current, &secid);
>  	return process_measurement(ns, file, current_cred(), secid, NULL, 0,
>  				   mask & (MAY_READ | MAY_WRITE | MAY_EXEC |
> @@ -656,7 +662,7 @@ int ima_file_hash(struct file *file, char *buf, size_t buf_size)
>  {
>  	struct ima_namespace *ns = get_current_ns();
>  
> -	if (!file)
> +	if (!ns || !file)
>  		return -EINVAL;
>  
>  	return __ima_inode_hash(ns, file_inode(file), buf, buf_size);
> @@ -685,7 +691,7 @@ int ima_inode_hash(struct inode *inode, char *buf, size_t buf_size)
>  {
>  	struct ima_namespace *ns = get_current_ns();
>  
> -	if (!inode)
> +	if (!ns || !inode)
>  		return -EINVAL;
>  
>  	return __ima_inode_hash(ns, inode, buf, buf_size);
> @@ -708,7 +714,7 @@ void ima_post_create_tmpfile(struct user_namespace *mnt_userns,
>  	struct integrity_iint_cache *iint;
>  	int must_appraise;
>  
> -	if (!ns->ima_policy_flag || !S_ISREG(inode->i_mode))
> +	if (!ns || !ns->ima_policy_flag || !S_ISREG(inode->i_mode))
>  		return;
>  
>  	must_appraise = ima_must_appraise(ns, mnt_userns, inode, MAY_ACCESS,
> @@ -742,7 +748,7 @@ void ima_post_path_mknod(struct user_namespace *mnt_userns,
>  	struct inode *inode = dentry->d_inode;
>  	int must_appraise;
>  
> -	if (!ns->ima_policy_flag || !S_ISREG(inode->i_mode))
> +	if (!ns || !ns->ima_policy_flag || !S_ISREG(inode->i_mode))
>  		return;
>  
>  	must_appraise = ima_must_appraise(ns, mnt_userns, inode, MAY_ACCESS,
> @@ -778,6 +784,9 @@ int ima_read_file(struct file *file, enum kernel_read_file_id read_id,
>  	enum ima_hooks func;
>  	u32 secid;
>  
> +	if (!ns)
> +		return 0;
> +
>  	/*
>  	 * Do devices using pre-allocated memory run the risk of the
>  	 * firmware being accessible to the device prior to the completion
> @@ -829,6 +838,9 @@ int ima_post_read_file(struct file *file, void *buf, loff_t size,
>  	enum ima_hooks func;
>  	u32 secid;
>  
> +	if (!ns)
> +		return 0;
> +
>  	/* permit signed certs */
>  	if (!file && read_id == READING_X509_CERTIFICATE)
>  		return 0;
> @@ -1071,7 +1083,7 @@ 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)
> +	if (!ns || !buf || !size)
>  		return;
>  
>  	f = fdget(kernel_fd);
> @@ -1111,6 +1123,9 @@ int ima_measure_critical_data(const char *event_label,
>  {
>  	struct ima_namespace *ns = get_current_ns();
>  
> +	if (!ns)
> +		return -EINVAL;
> +
>  	if (!event_name || !event_label || !buf || !buf_len)
>  		return -ENOPARAM;
>  
> diff --git a/security/integrity/ima/ima_ns.c b/security/integrity/ima/ima_ns.c
> index 205dd06ac41e..9a5105d0ea89 100644
> --- a/security/integrity/ima/ima_ns.c
> +++ b/security/integrity/ima/ima_ns.c
> @@ -26,7 +26,8 @@ struct ima_namespace *create_ima_ns(struct user_namespace *user_ns)
>  	if (err)
>  		goto fail_free;
>  
> -	user_ns->ima_ns = ns;
> +	/* Pairs with smp_load_acquire() in ima_ns_from_user_ns(). */
> +	smp_store_release(&user_ns->ima_ns, ns);
>  
>  	return ns;
>  
> diff --git a/security/integrity/ima/ima_policy.c b/security/integrity/ima/ima_policy.c
> index b7dbc687b6ff..5a9b511ebbae 100644
> --- a/security/integrity/ima/ima_policy.c
> +++ b/security/integrity/ima/ima_policy.c
> @@ -1333,6 +1333,7 @@ static unsigned int ima_parse_appraise_algos(char *arg)
>  static int ima_parse_rule(struct ima_namespace *ns,
>  			  char *rule, struct ima_rule_entry *entry)
>  {
> +	struct user_namespace *user_ns = ima_ns_to_user_ns(ns);

So I think ima_policy_write() and therefore ima_parse_rule() can
legitimately be reached at least from an ancestor userns but also from a
completely unrelated userns via securityfs. Sorry, I didn't see this
earlier. Think of the following two scenarios:

* userns1: unshare -U --map-root --mount
-----------------------------------------
   mount -t securityfs securityfs /userns1_securityfs
   fd_in_userns1 = open("/userns1_securityfs/ima_file, O_RDWR);

   /* I _think_ that sending of fds here should work but I haven't
    * bothered to recheck the scm code as I need to do some driving in a
    * little bit so I'm running out of time...
    */
   send_fd_scm_rights(fd_in_userns1, task_in_userns2);

* userns2: unshare -U --map-root --mount
-----------------------------------------
   fd_from_userns1 = receive_fd_scm_rights();
   write_policy(fd_from_userns1, "my fancy policy");

It also means that if you inherit an fd from a more privileged imans
instance you can write to it:

* initial_userns:
------------------
   mount -t securityfs securityfs /initial_securityfs

   fd_in_initial_securityfs = open("/initial_securityfs/ima_file, O_RDWR);

   pid = fork():
   if (pid == 0) {
	unshare(CLONE_NEWUSER);
	/* write idmapping for yourself */

	write_policy(fd_in_initial_securityfs, "my fancy policy");
   }

would allow an unprivileged caller to alter the host's ima policy (as
you can see the example requires cooperation).

In both cases the write can legitimately reach ima_policy_write() and
trigger ima_parse_rule() from another user namespace.

There are multiple ways to go here, I think.

It's important to figure out whether - coming back to an earlier review
of mine - you're ok with everyone with access to an opened policy fd
being able to write an ima policy for the namespace in questions as long
as _the opener of the policy file_ was privileged enough.

If that's the case then you can just remove the WARN_ON()/add a
non-WARN_ON() helper in there.

From my ima-naive perspective this seems fine and preferable as this
means clean permission checking once at open time.

A good question to answer in order to solve this is whether or not a
given operation is allowed is dependent on what is written, i.e. on the
content of the rule, I guess. I don't think there is.

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

* Re: [PATCH v8 18/19] ima: Show owning user namespace's uid and gid when displaying policy
  2022-01-14 13:45   ` Christian Brauner
@ 2022-01-18 16:31     ` Stefan Berger
  2022-01-19  9:23       ` Christian Brauner
  0 siblings, 1 reply; 50+ messages in thread
From: Stefan Berger @ 2022-01-18 16:31 UTC (permalink / raw)
  To: Christian Brauner, Stefan Berger
  Cc: linux-integrity, zohar, serge, christian.brauner, containers,
	dmitry.kasatkin, ebiederm, krzysztof.struczynski, roberto.sassu,
	mpeters, lhinds, lsturman, puiterwi, jejb, jamjoom, linux-kernel,
	paul, rgb, linux-security-module, jmorris


On 1/14/22 08:45, Christian Brauner wrote:
> On Tue, Jan 04, 2022 at 12:04:15PM -0500, Stefan Berger wrote:
>> From: Stefan Berger <stefanb@linux.ibm.com>
>>
>> Show the uid and gid values of the owning user namespace when displaying
>> the IMA policy rather than the kernel uid and gid values. Now the same uid
>> and gid values are shown in the policy as those that were used when the
>> policy was set.
>>
>> Signed-off-by: Stefan Berger <stefanb@linux.ibm.com>
>> ---
>>   security/integrity/ima/ima_policy.c | 19 +++++++++++++------
>>   1 file changed, 13 insertions(+), 6 deletions(-)
>>
>> diff --git a/security/integrity/ima/ima_policy.c b/security/integrity/ima/ima_policy.c
>> index 15c68dc5da9e..b7dbc687b6ff 100644
>> --- a/security/integrity/ima/ima_policy.c
>> +++ b/security/integrity/ima/ima_policy.c
>> @@ -1997,6 +1997,7 @@ static void ima_policy_show_appraise_algos(struct seq_file *m,
>>   
>>   int ima_policy_show(struct seq_file *m, void *v)
>>   {
>> +	struct user_namespace *user_ns = ima_user_ns_from_file(m->file);
> Hm, so when looking at the policy entries via seq_file's .show method
> and displaying the {g,u}id values of the rules we don't want the values
> resolved according to the user namespace the securityfs instances was
> mounted in. That would be misleading for callers that are in an
> ancestor userns (which we allow in .permission).
>
> So we want to make sure that we see the values as the opener of the file
> would see them. This is similar to e.g. looking at a task's ids through
> /proc/<pid>/status. So this should be seq_user_ns(m) instead of
> ima_user_ns_from_file().
>>   	struct ima_rule_entry *entry = v;
>>   	int i;
>>   	char tbuf[64] = {0,};
>> @@ -2074,7 +2075,8 @@ int ima_policy_show(struct seq_file *m, void *v)
>>   	}
>>   
>>   	if (entry->flags & IMA_UID) {
>> -		snprintf(tbuf, sizeof(tbuf), "%d", __kuid_val(entry->uid));
>> +		snprintf(tbuf, sizeof(tbuf),
>> +			 "%d", from_kuid(user_ns, entry->uid));
> This should be from_k{g,u}id_munged().

Thanks, fixed.

When I run a runc container as uid=1000 I see uid = 0 when inside the 
container and when entering its mount namespace from root account via 
nsenter it shows 'uid = 1000' while before it was showing 'uid = 0'.


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

* Re: [PATCH v8 19/19] ima: Enable IMA namespaces
  2022-01-14 12:05   ` Christian Brauner
@ 2022-01-18 17:53     ` Stefan Berger
  0 siblings, 0 replies; 50+ messages in thread
From: Stefan Berger @ 2022-01-18 17:53 UTC (permalink / raw)
  To: Christian Brauner, Stefan Berger
  Cc: linux-integrity, zohar, serge, christian.brauner, containers,
	dmitry.kasatkin, ebiederm, krzysztof.struczynski, roberto.sassu,
	mpeters, lhinds, lsturman, puiterwi, jejb, jamjoom, linux-kernel,
	paul, rgb, linux-security-module, jmorris


On 1/14/22 07:05, Christian Brauner wrote:
> On Tue, Jan 04, 2022 at 12:04:16PM -0500, Stefan Berger wrote:
>> From: Stefan Berger <stefanb@linux.ibm.com>
>>
>> Introduce the IMA_NS in Kconfig for IMA namespace enablement.
>>
>> Enable the lazy initialization of an IMA namespace when a user mounts
>> SecurityFS. Now a user_namespace will get a pointer to an ima_namespace
>> and therefore add an implementation of get_current_ns() that returns this
>> pointer.
>>
>> get_current_ns() may now return a NULL pointer for as long as the IMA
>> namespace hasn't been created, yet. Therefore, return early from those
>> functions that may now get a NULL pointer from this call. The NULL
>> pointer can typically be treated similar to not having an IMA policy set
>> and simply return early from a function.
>>
>> Implement ima_ns_from_file() for SecurityFS-related files where we can
>> now get the IMA namespace via the user namespace pointer associated
>> with the superblock of the SecurityFS filesystem instance. Since
>> the functions using ima_ns_from_file() will only be called after an
>> ima_namesapce has been allocated they will never get a NULL pointer
>> for the ima_namespace.
>>
>> Switch access to userns->ima_ns to use acquire/release semantics to ensure
>> that a newly created ima_namespace structure is fully visible upon access.
>>
>> Replace usage of current_user_ns() with ima_ns_from_user_ns() that
>> implements a method to derive the user_namespace from the given
>> ima_namespace. It leads to the same result.
>>
>> Signed-off-by: Stefan Berger <stefanb@linux.ibm.com>
>> ---
>>   include/linux/ima.h                 |  9 ++++++-
>>   init/Kconfig                        | 13 ++++++++++
>>   kernel/user_namespace.c             |  2 ++
>>   security/integrity/ima/ima.h        | 35 ++++++++++++++++++++++-----
>>   security/integrity/ima/ima_fs.c     | 37 ++++++++++++++++++++++-------
>>   security/integrity/ima/ima_main.c   | 29 ++++++++++++++++------
>>   security/integrity/ima/ima_ns.c     |  3 ++-
>>   security/integrity/ima/ima_policy.c | 13 +++++-----
>>   8 files changed, 112 insertions(+), 29 deletions(-)
>>
>> diff --git a/include/linux/ima.h b/include/linux/ima.h
>> index 5354e83d1694..7b9713b290ae 100644
>> --- a/include/linux/ima.h
>> +++ b/include/linux/ima.h
>> @@ -11,6 +11,7 @@
>>   #include <linux/fs.h>
>>   #include <linux/security.h>
>>   #include <linux/kexec.h>
>> +#include <linux/user_namespace.h>
>>   #include <crypto/hash_info.h>
>>   struct linux_binprm;
>>   
>> @@ -71,7 +72,13 @@ static inline const char * const *arch_get_ima_policy(void)
>>   static inline struct user_namespace
>>   *ima_ns_to_user_ns(struct ima_namespace *ns)
>>   {
>> -	return current_user_ns();
>> +	struct user_namespace *user_ns;
>> +
>> +	user_ns = current_user_ns();
>> +#ifdef CONFIG_IMA_NS
>> +	WARN_ON(user_ns->ima_ns != ns);
>> +#endif
>> +	return user_ns;
>>   }
>>   
>>   #else
>> diff --git a/init/Kconfig b/init/Kconfig
>> index 4b7bac10c72d..e27155e0ddba 100644
>> --- a/init/Kconfig
>> +++ b/init/Kconfig
>> @@ -1247,6 +1247,19 @@ config NET_NS
>>   	  Allow user space to create what appear to be multiple instances
>>   	  of the network stack.
>>   
>> +config IMA_NS
>> +	bool "IMA namespace"
>> +	depends on USER_NS
>> +	depends on IMA
>> +	default n
>> +	help
>> +	  Allow the creation of an IMA namespace 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
>>   
>>   config CHECKPOINT_RESTORE
>> diff --git a/kernel/user_namespace.c b/kernel/user_namespace.c
>> index 6b2e3ca7ee99..653f8fa83b69 100644
>> --- a/kernel/user_namespace.c
>> +++ b/kernel/user_namespace.c
>> @@ -20,6 +20,7 @@
>>   #include <linux/fs_struct.h>
>>   #include <linux/bsearch.h>
>>   #include <linux/sort.h>
>> +#include <linux/ima.h>
>>   
>>   static struct kmem_cache *user_ns_cachep __read_mostly;
>>   static DEFINE_MUTEX(userns_state_mutex);
>> @@ -196,6 +197,7 @@ static void free_user_ns(struct work_struct *work)
>>   			kfree(ns->projid_map.forward);
>>   			kfree(ns->projid_map.reverse);
>>   		}
>> +		free_ima_ns(ns);
>>   		retire_userns_sysctls(ns);
>>   		key_free_user_ns(ns);
>>   		ns_free_inum(&ns->ns);
>> diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h
>> index 344c8c4bd030..d993655ec796 100644
>> --- a/security/integrity/ima/ima.h
>> +++ b/security/integrity/ima/ima.h
>> @@ -509,21 +509,20 @@ struct user_namespace *ima_user_ns_from_file(const struct file *filp)
>>   	return file_inode(filp)->i_sb->s_user_ns;
>>   }
>>   
>> +#ifdef CONFIG_IMA_NS
>> +
>>   static inline struct ima_namespace
>>   *ima_ns_from_user_ns(struct user_namespace *user_ns)
>>   {
>> -	if (user_ns == &init_user_ns)
>> -		return &init_ima_ns;
>> -	return NULL;
>> +	/* Pairs with smp_store_releases() in create_ima_ns(). */
>> +	return smp_load_acquire(&user_ns->ima_ns);
>>   }
>>   
>>   static inline struct ima_namespace *get_current_ns(void)
>>   {
>> -	return &init_ima_ns;
>> +	return ima_ns_from_user_ns(current_user_ns());
>>   }
>>   
>> -#ifdef CONFIG_IMA_NS
>> -
>>   struct ima_namespace *create_ima_ns(struct user_namespace *user_ns);
>>   
>>   struct ns_status *ima_get_ns_status(struct ima_namespace *ns,
>> @@ -532,6 +531,11 @@ struct ns_status *ima_get_ns_status(struct ima_namespace *ns,
>>   
>>   void ima_free_ns_status_tree(struct ima_namespace *ns);
>>   
>> +static inline struct ima_namespace *ima_ns_from_file(const struct file *filp)
>> +{
>> +	return ima_user_ns_from_file(filp)->ima_ns;
>> +}
>> +
>>   #define IMA_NS_STATUS_ACTIONS   IMA_AUDIT
>>   #define IMA_NS_STATUS_FLAGS     IMA_AUDITED
>>   
>> @@ -542,6 +546,20 @@ unsigned long set_iint_flags(struct integrity_iint_cache *iint,
>>   
>>   #else
>>   
>> +static inline struct ima_namespace
>> +*ima_ns_from_user_ns(struct user_namespace *user_ns)
>> +{
>> +	if (user_ns == &init_user_ns)
>> +		return &init_ima_ns;
>> +	return NULL;
>> +}
>> +
>> +
>> +static inline struct ima_namespace *get_current_ns(void)
>> +{
>> +	return &init_ima_ns;
>> +}
>> +
>>   static inline struct ima_namespace *
>>   create_ima_ns(struct user_namespace *user_ns)
>>   {
>> @@ -572,6 +590,11 @@ static inline unsigned long set_iint_flags(struct integrity_iint_cache *iint,
>>   	return flags;
>>   }
>>   
>> +static inline struct ima_namespace *ima_ns_from_file(const struct file *filp)
>> +{
>> +	return &init_ima_ns;
>> +}
>> +
>>   #endif /* CONFIG_IMA_NS */
>>   
>>   #endif /* __LINUX_IMA_H */
>> diff --git a/security/integrity/ima/ima_fs.c b/security/integrity/ima/ima_fs.c
>> index 468508f6a7e8..ee3af81d1c3e 100644
>> --- a/security/integrity/ima/ima_fs.c
>> +++ b/security/integrity/ima/ima_fs.c
>> @@ -49,7 +49,7 @@ static ssize_t ima_show_htable_violations(struct file *filp,
>>   					  char __user *buf,
>>   					  size_t count, loff_t *ppos)
>>   {
>> -	struct ima_namespace *ns = &init_ima_ns;
>> +	struct ima_namespace *ns = ima_ns_from_file(filp);
>>   
>>   	return ima_show_htable_value(buf, count, ppos,
>>   				     &ns->ima_htable.violations);
>> @@ -64,7 +64,7 @@ static ssize_t ima_show_measurements_count(struct file *filp,
>>   					   char __user *buf,
>>   					   size_t count, loff_t *ppos)
>>   {
>> -	struct ima_namespace *ns = &init_ima_ns;
>> +	struct ima_namespace *ns = ima_ns_from_file(filp);
>>   
>>   	return ima_show_htable_value(buf, count, ppos, &ns->ima_htable.len);
>>   }
>> @@ -77,7 +77,7 @@ static const struct file_operations ima_measurements_count_ops = {
>>   /* returns pointer to hlist_node */
>>   static void *ima_measurements_start(struct seq_file *m, loff_t *pos)
>>   {
>> -	struct ima_namespace *ns = &init_ima_ns;
>> +	struct ima_namespace *ns = ima_ns_from_file(m->file);
>>   	loff_t l = *pos;
>>   	struct ima_queue_entry *qe;
>>   
>> @@ -95,7 +95,7 @@ static void *ima_measurements_start(struct seq_file *m, loff_t *pos)
>>   
>>   static void *ima_measurements_next(struct seq_file *m, void *v, loff_t *pos)
>>   {
>> -	struct ima_namespace *ns = &init_ima_ns;
>> +	struct ima_namespace *ns = ima_ns_from_file(m->file);
>>   	struct ima_queue_entry *qe = v;
>>   
>>   	/* lock protects when reading beyond last element
>> @@ -317,7 +317,7 @@ static ssize_t ima_read_policy(struct ima_namespace *ns, char *path)
>>   static ssize_t ima_write_policy(struct file *file, const char __user *buf,
>>   				size_t datalen, loff_t *ppos)
>>   {
>> -	struct ima_namespace *ns = &init_ima_ns;
>> +	struct ima_namespace *ns = ima_ns_from_file(file);
>>   	char *data;
>>   	ssize_t result;
>>   
>> @@ -379,7 +379,7 @@ static const struct seq_operations ima_policy_seqops = {
>>   static int ima_open_policy(struct inode *inode, struct file *filp)
>>   {
>>   	struct user_namespace *user_ns = ima_user_ns_from_file(filp);
>> -	struct ima_namespace *ns = &init_ima_ns;
>> +	struct ima_namespace *ns = ima_ns_from_file(filp);
>>   
>>   	if (!(filp->f_flags & O_WRONLY)) {
>>   #ifndef	CONFIG_IMA_READ_POLICY
>> @@ -406,7 +406,7 @@ static int ima_open_policy(struct inode *inode, struct file *filp)
>>    */
>>   static int ima_release_policy(struct inode *inode, struct file *file)
>>   {
>> -	struct ima_namespace *ns = &init_ima_ns;
>> +	struct ima_namespace *ns = ima_ns_from_file(file);
>>   	const char *cause = ns->valid_policy ? "completed" : "failed";
>>   
>>   	if ((file->f_flags & O_ACCMODE) == O_RDONLY)
>> @@ -459,12 +459,29 @@ int ima_fs_ns_init(struct user_namespace *user_ns, struct dentry *root)
>>   	struct dentry *ascii_runtime_measurements = NULL;
>>   	struct dentry *runtime_measurements_count = NULL;
>>   	struct dentry *violations = NULL;
>> +	bool created_ns = false;
>> +
>> +	/*
>> +	 * While multiple superblocks can exist they are keyed by userns in
>> +	 * s_fs_info for securityfs. The first time a userns mounts a
>> +	 * securityfs instance we lazily allocate the ima_namespace for the
>> +	 * userns since that's the only way a userns can meaningfully use ima.
>> +	 * The vfs ensures we're the only one to call fill_super() and hence
>> +	 * ima_fs_ns_init(), so we don't need any memory barriers here, i.e.
>> +	 * user_ns->ima_ns can't change while we're in here.
>> +	 */
>> +	if (!ns) {
>> +		ns = create_ima_ns(user_ns);
>> +		if (IS_ERR(ns))
>> +			return PTR_ERR(ns);
>> +		created_ns = true;
>> +	}
> Since create_ima_ns() initializes user_ns->ima_ns via
> smp_store_release() the patch currently implies that concurrent access
> to user_ns->ima_ns are safe once create_ima_ns() returns.
>
> Specifically, it entails that no caller will access entries in the ima
> namespace that will only be filled in past this point. Afaict, this only
> relates to the ns->policy_dentry which can't be accessed until
> securityfs is finished.
>
> Nonetheless, I would recommend that you change create_ima_ns() to not
> initialize user_ns->ima_ns and instead defer this until everything in
> the namespace is setup. So maybe move the smp_store_release() to the end
> of ima_fs_ns_init(). If ns->policy_dentry wouldn't be stashed in ima_ns
> it wouldn't matter but since it is I would not publish ima_ns before
> this is set. Sm like (uncompiled, untested):
>
> diff --git a/security/integrity/ima/ima_fs.c b/security/integrity/ima/ima_fs.c
> index ee3af81d1c3e..64ca47671d31 100644
> --- a/security/integrity/ima/ima_fs.c
> +++ b/security/integrity/ima/ima_fs.c
> @@ -531,6 +531,8 @@ int ima_fs_ns_init(struct user_namespace *user_ns, struct dentry *root)
>                          goto out;
>          }
>
> +       if (!user_ns->ima_ns)
> +               smp_store_release(&user_ns->ima_ns, ns);
>          return 0;
>   out:
>          securityfs_remove(ns->policy_dentry);
>
> As a side-effect this will let you get rid of the bool created_ns and
> thereby simplify the codeflow.

Fixed. Thanks.


>
> (Note, that obviously means that the changes I mentioned earlier in
> https://lore.kernel.org/containers/20220114114321.7prnt72ukvch4wxa@wittgenstein
> can't be made.)

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

* Re: [PATCH v8 19/19] ima: Enable IMA namespaces
  2022-01-14 14:45   ` Christian Brauner
@ 2022-01-18 18:09     ` Stefan Berger
  2022-01-19  9:46       ` Christian Brauner
  0 siblings, 1 reply; 50+ messages in thread
From: Stefan Berger @ 2022-01-18 18:09 UTC (permalink / raw)
  To: Christian Brauner, Stefan Berger
  Cc: linux-integrity, zohar, serge, christian.brauner, containers,
	dmitry.kasatkin, ebiederm, krzysztof.struczynski, roberto.sassu,
	mpeters, lhinds, lsturman, puiterwi, jejb, jamjoom, linux-kernel,
	paul, rgb, linux-security-module, jmorris


On 1/14/22 09:45, Christian Brauner wrote:
> On Tue, Jan 04, 2022 at 12:04:16PM -0500, Stefan Berger wrote:
>> From: Stefan Berger <stefanb@linux.ibm.com>
>>
>> Introduce the IMA_NS in Kconfig for IMA namespace enablement.
>>
>> Enable the lazy initialization of an IMA namespace when a user mounts
>> SecurityFS. Now a user_namespace will get a pointer to an ima_namespace
>> and therefore add an implementation of get_current_ns() that returns this
>> pointer.
>>
>> get_current_ns() may now return a NULL pointer for as long as the IMA
>> namespace hasn't been created, yet. Therefore, return early from those
>> functions that may now get a NULL pointer from this call. The NULL
>> pointer can typically be treated similar to not having an IMA policy set
>> and simply return early from a function.
>>
>> Implement ima_ns_from_file() for SecurityFS-related files where we can
>> now get the IMA namespace via the user namespace pointer associated
>> with the superblock of the SecurityFS filesystem instance. Since
>> the functions using ima_ns_from_file() will only be called after an
>> ima_namesapce has been allocated they will never get a NULL pointer
>> for the ima_namespace.
>>
>> Switch access to userns->ima_ns to use acquire/release semantics to ensure
>> that a newly created ima_namespace structure is fully visible upon access.
>>
>> Replace usage of current_user_ns() with ima_ns_from_user_ns() that
>> implements a method to derive the user_namespace from the given
>> ima_namespace. It leads to the same result.
>>
>> Signed-off-by: Stefan Berger <stefanb@linux.ibm.com>
>> ---
[...]
>> diff --git a/security/integrity/ima/ima_policy.c b/security/integrity/ima/ima_policy.c
>> index b7dbc687b6ff..5a9b511ebbae 100644
>> --- a/security/integrity/ima/ima_policy.c
>> +++ b/security/integrity/ima/ima_policy.c
>> @@ -1333,6 +1333,7 @@ static unsigned int ima_parse_appraise_algos(char *arg)
>>   static int ima_parse_rule(struct ima_namespace *ns,
>>   			  char *rule, struct ima_rule_entry *entry)
>>   {
>> +	struct user_namespace *user_ns = ima_ns_to_user_ns(ns);
> So I think ima_policy_write() and therefore ima_parse_rule() can
> legitimately be reached at least from an ancestor userns but also from a
> completely unrelated userns via securityfs. Sorry, I didn't see this
> earlier. Think of the following two scenarios:
>
> * userns1: unshare -U --map-root --mount
> -----------------------------------------
>     mount -t securityfs securityfs /userns1_securityfs
>     fd_in_userns1 = open("/userns1_securityfs/ima_file, O_RDWR);
>
>     /* I _think_ that sending of fds here should work but I haven't
>      * bothered to recheck the scm code as I need to do some driving in a
>      * little bit so I'm running out of time...
>      */
>     send_fd_scm_rights(fd_in_userns1, task_in_userns2);
>
> * userns2: unshare -U --map-root --mount
> -----------------------------------------
>     fd_from_userns1 = receive_fd_scm_rights();
>     write_policy(fd_from_userns1, "my fancy policy");

Passing an fd around like this presumably indicates that you intend to 
let the recipient read/write to it.


> It also means that if you inherit an fd from a more privileged imans
> instance you can write to it:

Now in this example we have to assume that root is making a mistake 
passing the file descriptor around?

# ls -l /sys/kernel/security/ima/
total 0
-r--r-----. 1 root root 0 Jan 18 12:48 ascii_runtime_measurements
-r--r-----. 1 root root 0 Jan 18 12:48 binary_runtime_measurements
-rw-------. 1 root root 0 Jan 18 12:48 policy
-r--r-----. 1 root root 0 Jan 18 12:48 runtime_measurements_count
-r--r-----. 1 root root 0 Jan 18 12:48 violations

>
> * initial_userns:


So that's the host, right? And this is a 2nd independent example from 
the first.

 > ------------------

>     mount -t securityfs securityfs /initial_securityfs
>
>     fd_in_initial_securityfs = open("/initial_securityfs/ima_file, O_RDWR);
>
>     pid = fork():
>     if (pid == 0) {
> 	unshare(CLONE_NEWUSER);
> 	/* write idmapping for yourself */
>
> 	write_policy(fd_in_initial_securityfs, "my fancy policy");
>     }
>
> would allow an unprivileged caller to alter the host's ima policy (as
> you can see the example requires cooperation).

Sorry, not currently following. Root is the only one being able to open 
that IMA files on the host, right? Is this a mistake here where root 
passed the fd onto the child and that child is not trusted to mess with 
the fd including passing it on further?


>
> In both cases the write can legitimately reach ima_policy_write() and
> trigger ima_parse_rule() from another user namespace.
>
> There are multiple ways to go here, I think.
>
> It's important to figure out whether - coming back to an earlier review
> of mine - you're ok with everyone with access to an opened policy fd
> being able to write an ima policy for the namespace in questions as long
> as _the opener of the policy file_ was privileged enough.
>
> If that's the case then you can just remove the WARN_ON()/add a
> non-WARN_ON() helper in there.
>
>  From my ima-naive perspective this seems fine and preferable as this
> means clean permission checking once at open time.
>
> A good question to answer in order to solve this is whether or not a
> given operation is allowed is dependent on what is written, i.e. on the
> content of the rule, I guess. I don't think there is.

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

* Re: [PATCH v8 10/19] ima: Implement hierarchical processing of file accesses
  2022-01-14 11:21   ` Christian Brauner
@ 2022-01-18 18:25     ` Stefan Berger
  0 siblings, 0 replies; 50+ messages in thread
From: Stefan Berger @ 2022-01-18 18:25 UTC (permalink / raw)
  To: Christian Brauner, Stefan Berger
  Cc: linux-integrity, zohar, serge, christian.brauner, containers,
	dmitry.kasatkin, ebiederm, krzysztof.struczynski, roberto.sassu,
	mpeters, lhinds, lsturman, puiterwi, jejb, jamjoom, linux-kernel,
	paul, rgb, linux-security-module, jmorris


On 1/14/22 06:21, Christian Brauner wrote:
> On Tue, Jan 04, 2022 at 12:04:07PM -0500, Stefan Berger wrote:
>> From: Stefan Berger <stefanb@linux.ibm.com>
>>
>> Implement hierarchical processing of file accesses in IMA namespaces by
>> walking the list of user namespaces towards the root. This way file
>> accesses can be audited in an IMA namespace and also be evaluated against
>> the IMA policies of parent IMA namespaces.
>>
>> __process_measurement() returns either 0 or -EACCES. For hierarchical
>> processing remember the -EACCES returned by this function but continue
>> to the parent user namespace. At the end either return 0 or -EACCES
>> if an error occurred in one of the IMA namespaces.
>>
>> Currently the ima_ns pointer of the user_namespace is always NULL except
>> at the init_user_ns, so test ima_ns for NULL pointer and skip the call to
>> __process_measurement() if it is NULL. Once IMA namespacing is fully
>> enabled, the pointer may also be NULL due to late initialization of the
>> IMA namespace.
>>
>> Signed-off-by: Stefan Berger <stefanb@linux.ibm.com>
>> ---
>>   include/linux/ima.h               |  6 +++++
>>   security/integrity/ima/ima_main.c | 37 +++++++++++++++++++++++++++----
>>   2 files changed, 39 insertions(+), 4 deletions(-)
>>
>> diff --git a/include/linux/ima.h b/include/linux/ima.h
>> index b6ab66a546ae..fcee2a51bb87 100644
>> --- a/include/linux/ima.h
>> +++ b/include/linux/ima.h
>> @@ -65,6 +65,12 @@ static inline const char * const *arch_get_ima_policy(void)
>>   }
>>   #endif
>>   
>> +static inline struct user_namespace
>> +*ima_ns_to_user_ns(struct ima_namespace *ns)
>> +{
>> +	return current_user_ns();
>> +}
>> +
>>   #else
>>   static inline enum hash_algo ima_get_current_hash_algo(void)
>>   {
>> diff --git a/security/integrity/ima/ima_main.c b/security/integrity/ima/ima_main.c
>> index 621685d4eb95..51b0ef1cebbe 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;
>> @@ -395,6 +395,35 @@ static int process_measurement(struct ima_namespace *ns,
>>   	return 0;
>>   }
>>   
>> +static int process_measurement(struct ima_namespace *ns,
>> +			       struct file *file, const struct cred *cred,
>> +			       u32 secid, char *buf, loff_t size, int mask,
>> +			       enum ima_hooks func)
>> +{
>> +	struct user_namespace *user_ns = ima_ns_to_user_ns(ns);
>> +	int ret = 0;
>> +
>> +	while (user_ns) {
>> +		ns = ima_ns_from_user_ns(user_ns);
>> +		if (ns) {
>> +			int rc;
>> +
>> +			rc = __process_measurement(ns, file, cred, secid, buf,
>> +						   size, mask, func);
>> +			switch (rc) {
>> +			case -EACCES:
>> +				/* return this error at the end but continue */
>> +				ret = -EACCES;
>> +				break;
> This seems risky. Every error not -EACCES will be counted as a success.
> It doesn't look like __process_measurement() will return anything else
> but I would still place a WARN_ON() or WARN_ON_ONCE() in there to make
> that assumption explicit.
>
> Right now it looks like your only error condition is -EACCES and non-ima
> cracks like me need to read through __process_measurement() to figure
> out that that's ok. With a WARN_ON* in there I'd not have needed to bother.
>
> switch (rc) {
> case -EACCES:
> 	/* return this error at the end but continue */
> 	ret = -EACCES;
> 	break
> default:
> 	WARN_ON_ONCE(true);
              ret = -EINVAL;
> }
>
> or sm similar.


Agreed. To be on the safe side I would add a ret = -EINVAL to it for the 
unhandled case as shown above.


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

* Re: [PATCH v8 07/19] ima: Move dentry into ima_namespace and others onto stack
  2022-01-13 20:28   ` Mimi Zohar
@ 2022-01-18 20:12     ` Stefan Berger
  2022-01-18 20:42       ` Mimi Zohar
  0 siblings, 1 reply; 50+ messages in thread
From: Stefan Berger @ 2022-01-18 20:12 UTC (permalink / raw)
  To: Mimi Zohar, Stefan Berger, linux-integrity
  Cc: serge, christian.brauner, containers, dmitry.kasatkin, ebiederm,
	krzysztof.struczynski, roberto.sassu, mpeters, lhinds, lsturman,
	puiterwi, jejb, jamjoom, linux-kernel, paul, rgb,
	linux-security-module, jmorris


On 1/13/22 15:28, Mimi Zohar wrote:
> Hi Stefan,
>
> Nobody refers to the IMA securityfs files as dentries.  The Subject
> line is suppose to provide a hint about the patch.  How about changing
> the "Subject" line to "ima: Move IMA securityfs files into
> ima_namespaces or onto stack".
>
> On Tue, 2022-01-04 at 12:04 -0500, Stefan Berger wrote:
>> From: Stefan Berger <stefanb@linux.ibm.com>
>>
>> Move the policy file dentry into the ima_namespace for reuse by
>> virtualized SecurityFS and for being able to remove it from
>> the filesystem. Move the other dentries onto the stack.
> Missing is an explanation why the other IMA securityfs files can be on
> the stack.  Maybe start out by saying that the ns_ima_init securityfs
> files are never deleted.  Then transition into the IMA namespaced
> securityfs files and how they will be deleted.

How about this:

ima: Move IMA securityfs files into ima_namespace or onto stack

Move the IMA policy file's dentry into the ima_namespace for reuse by
virtualized securityfs and for being able to remove the file from the
filesystem using securityfs_remove().

Move the other files' dentries onto the stack since they are not needed
outside the function where they are created in. Also, their cleanup is
automatically handled by the filesystem upon umount of a virtualized
secruityfs instance, so they don't need to be explicitly freed anymore.

When moving the dentry 'ima_policy' into ima_namespace rename it to
'policy_dentry' to clarify its datatype and avoid a name clash with
'int ima_policy' from ima_policy.c.

    Stefan



>> Signed-off-by: Stefan Berger <stefanb@linux.ibm.com>
>> ---
>>   security/integrity/ima/ima.h    |  2 ++
>>   security/integrity/ima/ima_fs.c | 32 ++++++++++++++++++--------------
>>   2 files changed, 20 insertions(+), 14 deletions(-)
>>
>> diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h
>> index 82b3f6a98320..224b09617c52 100644
>> --- a/security/integrity/ima/ima.h
>> +++ b/security/integrity/ima/ima.h
>> @@ -140,6 +140,8 @@ struct ima_namespace {
>>   	struct mutex ima_write_mutex;
>>   	unsigned long ima_fs_flags;
>>   	int valid_policy;
>> +
>> +	struct dentry *policy_dentry;
> None of the other securityfs files are renamed.  Why is "ima_policy"
> being renamed to "policy_dentry"?  If there is a need, it should be
> documented in the patch description.
>
> thanks,
>
> Mimi
>
>>   } __randomize_layout;
>>   extern struct ima_namespace init_ima_ns;
>>

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

* Re: [PATCH v8 07/19] ima: Move dentry into ima_namespace and others onto stack
  2022-01-18 20:12     ` Stefan Berger
@ 2022-01-18 20:42       ` Mimi Zohar
  2022-01-18 20:54         ` Stefan Berger
  0 siblings, 1 reply; 50+ messages in thread
From: Mimi Zohar @ 2022-01-18 20:42 UTC (permalink / raw)
  To: Stefan Berger, Stefan Berger, linux-integrity
  Cc: serge, christian.brauner, containers, dmitry.kasatkin, ebiederm,
	krzysztof.struczynski, roberto.sassu, mpeters, lhinds, lsturman,
	puiterwi, jejb, jamjoom, linux-kernel, paul, rgb,
	linux-security-module, jmorris

On Tue, 2022-01-18 at 15:12 -0500, Stefan Berger wrote:
> On 1/13/22 15:28, Mimi Zohar wrote:
> > Hi Stefan,
> >
> > Nobody refers to the IMA securityfs files as dentries.  The Subject
> > line is suppose to provide a hint about the patch.  How about changing
> > the "Subject" line to "ima: Move IMA securityfs files into
> > ima_namespaces or onto stack".
> >
> > On Tue, 2022-01-04 at 12:04 -0500, Stefan Berger wrote:
> >> From: Stefan Berger <stefanb@linux.ibm.com>
> >>
> >> Move the policy file dentry into the ima_namespace for reuse by
> >> virtualized SecurityFS and for being able to remove it from
> >> the filesystem. Move the other dentries onto the stack.
> > Missing is an explanation why the other IMA securityfs files can be on
> > the stack.  Maybe start out by saying that the ns_ima_init securityfs
> > files are never deleted.  Then transition into the IMA namespaced
> > securityfs files and how they will be deleted.
> 
> How about this:
> 
> ima: Move IMA securityfs files into ima_namespace or onto stack
> 
> Move the IMA policy file's dentry into the ima_namespace for reuse by
> virtualized securityfs and for being able to remove the file from the
> filesystem using securityfs_remove().

How about "Move the IMA securityfs policy file ..."

> Move the other files' dentries onto the stack since they are not needed

How about "Move the other IMA securityfs files ..."

> outside the function where they are created in. Also, their cleanup is
> automatically handled by the filesystem upon umount of a virtualized
> secruityfs instance, so they don't need to be explicitly freed anymore.
> 
> When moving the dentry 'ima_policy' into ima_namespace rename it to
> 'policy_dentry' to clarify its datatype and avoid a name clash with
> 'int ima_policy' from ima_policy.c.

To prevent namespace pollution, static variables need to be prefixed
(e.g. "ima_").  This is not a concern with variables inside the
ima_namespace structure.  Why not just rename the variable "policy".

Refer to the section on "Naming" in Documentation/process/coding-
style.rst.

thanks,

Mimi

> 
> 
> 
> >> Signed-off-by: Stefan Berger <stefanb@linux.ibm.com>
> >> ---
> >>   security/integrity/ima/ima.h    |  2 ++
> >>   security/integrity/ima/ima_fs.c | 32 ++++++++++++++++++--------------
> >>   2 files changed, 20 insertions(+), 14 deletions(-)
> >>
> >> diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h
> >> index 82b3f6a98320..224b09617c52 100644
> >> --- a/security/integrity/ima/ima.h
> >> +++ b/security/integrity/ima/ima.h
> >> @@ -140,6 +140,8 @@ struct ima_namespace {
> >>   	struct mutex ima_write_mutex;
> >>   	unsigned long ima_fs_flags;
> >>   	int valid_policy;
> >> +
> >> +	struct dentry *policy_dentry;
> > None of the other securityfs files are renamed.  Why is "ima_policy"
> > being renamed to "policy_dentry"?  If there is a need, it should be
> > documented in the patch description.
> >
> > thanks,
> >
> > Mimi
> >
> >>   } __randomize_layout;
> >>   extern struct ima_namespace init_ima_ns;
> >>



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

* Re: [PATCH v8 07/19] ima: Move dentry into ima_namespace and others onto stack
  2022-01-18 20:42       ` Mimi Zohar
@ 2022-01-18 20:54         ` Stefan Berger
  0 siblings, 0 replies; 50+ messages in thread
From: Stefan Berger @ 2022-01-18 20:54 UTC (permalink / raw)
  To: Mimi Zohar, Stefan Berger, linux-integrity
  Cc: serge, christian.brauner, containers, dmitry.kasatkin, ebiederm,
	krzysztof.struczynski, roberto.sassu, mpeters, lhinds, lsturman,
	puiterwi, jejb, jamjoom, linux-kernel, paul, rgb,
	linux-security-module, jmorris


On 1/18/22 15:42, Mimi Zohar wrote:
> On Tue, 2022-01-18 at 15:12 -0500, Stefan Berger wrote:
>> On 1/13/22 15:28, Mimi Zohar wrote:
>>> Hi Stefan,
>>>
>>> Nobody refers to the IMA securityfs files as dentries.  The Subject
>>> line is suppose to provide a hint about the patch.  How about changing
>>> the "Subject" line to "ima: Move IMA securityfs files into
>>> ima_namespaces or onto stack".
>>>
>>> On Tue, 2022-01-04 at 12:04 -0500, Stefan Berger wrote:
>>>> From: Stefan Berger <stefanb@linux.ibm.com>
>>>>
>>>> Move the policy file dentry into the ima_namespace for reuse by
>>>> virtualized SecurityFS and for being able to remove it from
>>>> the filesystem. Move the other dentries onto the stack.
>>> Missing is an explanation why the other IMA securityfs files can be on
>>> the stack.  Maybe start out by saying that the ns_ima_init securityfs
>>> files are never deleted.  Then transition into the IMA namespaced
>>> securityfs files and how they will be deleted.
>> How about this:
>>
>> ima: Move IMA securityfs files into ima_namespace or onto stack
>>
>> Move the IMA policy file's dentry into the ima_namespace for reuse by
>> virtualized securityfs and for being able to remove the file from the
>> filesystem using securityfs_remove().
> How about "Move the IMA securityfs policy file ..."
>
>> Move the other files' dentries onto the stack since they are not needed
> How about "Move the other IMA securityfs files ..."
>
>> outside the function where they are created in. Also, their cleanup is
>> automatically handled by the filesystem upon umount of a virtualized
>> secruityfs instance, so they don't need to be explicitly freed anymore.
>>
>> When moving the dentry 'ima_policy' into ima_namespace rename it to
>> 'policy_dentry' to clarify its datatype and avoid a name clash with
>> 'int ima_policy' from ima_policy.c.
> To prevent namespace pollution, static variables need to be prefixed
> (e.g. "ima_").  This is not a concern with variables inside the
> ima_namespace structure.  Why not just rename the variable "policy".

'policy' is so generic. It can be the internal representation of the policy.


>
> Refer to the section on "Naming" in Documentation/process/coding-
> style.rst.

Hm, it cannot also be the point to work around the naming just to avoid 
it and come up with ambiguous names...

'policy_dentry' explains much better what it is but following this style 
guide that is then "Hungarian" notation, which is 'asinine.'

What about 'policy_file'?

    Stefan

>
> thanks,
>
> Mimi
>
>>
>>
>>>> Signed-off-by: Stefan Berger <stefanb@linux.ibm.com>
>>>> ---
>>>>    security/integrity/ima/ima.h    |  2 ++
>>>>    security/integrity/ima/ima_fs.c | 32 ++++++++++++++++++--------------
>>>>    2 files changed, 20 insertions(+), 14 deletions(-)
>>>>
>>>> diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h
>>>> index 82b3f6a98320..224b09617c52 100644
>>>> --- a/security/integrity/ima/ima.h
>>>> +++ b/security/integrity/ima/ima.h
>>>> @@ -140,6 +140,8 @@ struct ima_namespace {
>>>>    	struct mutex ima_write_mutex;
>>>>    	unsigned long ima_fs_flags;
>>>>    	int valid_policy;
>>>> +
>>>> +	struct dentry *policy_dentry;
>>> None of the other securityfs files are renamed.  Why is "ima_policy"
>>> being renamed to "policy_dentry"?  If there is a need, it should be
>>> documented in the patch description.
>>>
>>> thanks,
>>>
>>> Mimi
>>>
>>>>    } __randomize_layout;
>>>>    extern struct ima_namespace init_ima_ns;
>>>>
>

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

* Re: [PATCH v8 18/19] ima: Show owning user namespace's uid and gid when displaying policy
  2022-01-18 16:31     ` Stefan Berger
@ 2022-01-19  9:23       ` Christian Brauner
  0 siblings, 0 replies; 50+ messages in thread
From: Christian Brauner @ 2022-01-19  9:23 UTC (permalink / raw)
  To: Stefan Berger
  Cc: Stefan Berger, linux-integrity, zohar, serge, christian.brauner,
	containers, dmitry.kasatkin, ebiederm, krzysztof.struczynski,
	roberto.sassu, mpeters, lhinds, lsturman, puiterwi, jejb,
	jamjoom, linux-kernel, paul, rgb, linux-security-module, jmorris

On Tue, Jan 18, 2022 at 11:31:29AM -0500, Stefan Berger wrote:
> 
> On 1/14/22 08:45, Christian Brauner wrote:
> > On Tue, Jan 04, 2022 at 12:04:15PM -0500, Stefan Berger wrote:
> > > From: Stefan Berger <stefanb@linux.ibm.com>
> > > 
> > > Show the uid and gid values of the owning user namespace when displaying
> > > the IMA policy rather than the kernel uid and gid values. Now the same uid
> > > and gid values are shown in the policy as those that were used when the
> > > policy was set.
> > > 
> > > Signed-off-by: Stefan Berger <stefanb@linux.ibm.com>
> > > ---
> > >   security/integrity/ima/ima_policy.c | 19 +++++++++++++------
> > >   1 file changed, 13 insertions(+), 6 deletions(-)
> > > 
> > > diff --git a/security/integrity/ima/ima_policy.c b/security/integrity/ima/ima_policy.c
> > > index 15c68dc5da9e..b7dbc687b6ff 100644
> > > --- a/security/integrity/ima/ima_policy.c
> > > +++ b/security/integrity/ima/ima_policy.c
> > > @@ -1997,6 +1997,7 @@ static void ima_policy_show_appraise_algos(struct seq_file *m,
> > >   int ima_policy_show(struct seq_file *m, void *v)
> > >   {
> > > +	struct user_namespace *user_ns = ima_user_ns_from_file(m->file);
> > Hm, so when looking at the policy entries via seq_file's .show method
> > and displaying the {g,u}id values of the rules we don't want the values
> > resolved according to the user namespace the securityfs instances was
> > mounted in. That would be misleading for callers that are in an
> > ancestor userns (which we allow in .permission).
> > 
> > So we want to make sure that we see the values as the opener of the file
> > would see them. This is similar to e.g. looking at a task's ids through
> > /proc/<pid>/status. So this should be seq_user_ns(m) instead of
> > ima_user_ns_from_file().
> > >   	struct ima_rule_entry *entry = v;
> > >   	int i;
> > >   	char tbuf[64] = {0,};
> > > @@ -2074,7 +2075,8 @@ int ima_policy_show(struct seq_file *m, void *v)
> > >   	}
> > >   	if (entry->flags & IMA_UID) {
> > > -		snprintf(tbuf, sizeof(tbuf), "%d", __kuid_val(entry->uid));
> > > +		snprintf(tbuf, sizeof(tbuf),
> > > +			 "%d", from_kuid(user_ns, entry->uid));
> > This should be from_k{g,u}id_munged().
> 
> Thanks, fixed.
> 
> When I run a runc container as uid=1000 I see uid = 0 when inside the
> container and when entering its mount namespace from root account via
> nsenter it shows 'uid = 1000' while before it was showing 'uid = 0'.

Yes, when you're only entering the mountns you should see uid 1000 as
that's what that {g,u}id is mapped to in your namespace and you've
opened __and read__ that file from the same namespace.

(Now, if you were to open that fd and send it back to a process running
in the container and that process does the read it would still see 1000.
But that's ok, because we care about the opener's creds.)

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

* Re: [PATCH v8 19/19] ima: Enable IMA namespaces
  2022-01-18 18:09     ` Stefan Berger
@ 2022-01-19  9:46       ` Christian Brauner
  2022-01-19 12:45         ` Stefan Berger
  0 siblings, 1 reply; 50+ messages in thread
From: Christian Brauner @ 2022-01-19  9:46 UTC (permalink / raw)
  To: Stefan Berger
  Cc: Stefan Berger, linux-integrity, zohar, serge, christian.brauner,
	containers, dmitry.kasatkin, ebiederm, krzysztof.struczynski,
	roberto.sassu, mpeters, lhinds, lsturman, puiterwi, jejb,
	jamjoom, linux-kernel, paul, rgb, linux-security-module, jmorris

On Tue, Jan 18, 2022 at 01:09:12PM -0500, Stefan Berger wrote:
> 
> On 1/14/22 09:45, Christian Brauner wrote:
> > On Tue, Jan 04, 2022 at 12:04:16PM -0500, Stefan Berger wrote:
> > > From: Stefan Berger <stefanb@linux.ibm.com>
> > > 
> > > Introduce the IMA_NS in Kconfig for IMA namespace enablement.
> > > 
> > > Enable the lazy initialization of an IMA namespace when a user mounts
> > > SecurityFS. Now a user_namespace will get a pointer to an ima_namespace
> > > and therefore add an implementation of get_current_ns() that returns this
> > > pointer.
> > > 
> > > get_current_ns() may now return a NULL pointer for as long as the IMA
> > > namespace hasn't been created, yet. Therefore, return early from those
> > > functions that may now get a NULL pointer from this call. The NULL
> > > pointer can typically be treated similar to not having an IMA policy set
> > > and simply return early from a function.
> > > 
> > > Implement ima_ns_from_file() for SecurityFS-related files where we can
> > > now get the IMA namespace via the user namespace pointer associated
> > > with the superblock of the SecurityFS filesystem instance. Since
> > > the functions using ima_ns_from_file() will only be called after an
> > > ima_namesapce has been allocated they will never get a NULL pointer
> > > for the ima_namespace.
> > > 
> > > Switch access to userns->ima_ns to use acquire/release semantics to ensure
> > > that a newly created ima_namespace structure is fully visible upon access.
> > > 
> > > Replace usage of current_user_ns() with ima_ns_from_user_ns() that
> > > implements a method to derive the user_namespace from the given
> > > ima_namespace. It leads to the same result.
> > > 
> > > Signed-off-by: Stefan Berger <stefanb@linux.ibm.com>
> > > ---
> [...]
> > > diff --git a/security/integrity/ima/ima_policy.c b/security/integrity/ima/ima_policy.c
> > > index b7dbc687b6ff..5a9b511ebbae 100644
> > > --- a/security/integrity/ima/ima_policy.c
> > > +++ b/security/integrity/ima/ima_policy.c
> > > @@ -1333,6 +1333,7 @@ static unsigned int ima_parse_appraise_algos(char *arg)
> > >   static int ima_parse_rule(struct ima_namespace *ns,
> > >   			  char *rule, struct ima_rule_entry *entry)
> > >   {
> > > +	struct user_namespace *user_ns = ima_ns_to_user_ns(ns);
> > So I think ima_policy_write() and therefore ima_parse_rule() can
> > legitimately be reached at least from an ancestor userns but also from a
> > completely unrelated userns via securityfs. Sorry, I didn't see this
> > earlier. Think of the following two scenarios:
> > 
> > * userns1: unshare -U --map-root --mount
> > -----------------------------------------
> >     mount -t securityfs securityfs /userns1_securityfs
> >     fd_in_userns1 = open("/userns1_securityfs/ima_file, O_RDWR);
> > 
> >     /* I _think_ that sending of fds here should work but I haven't
> >      * bothered to recheck the scm code as I need to do some driving in a
> >      * little bit so I'm running out of time...
> >      */
> >     send_fd_scm_rights(fd_in_userns1, task_in_userns2);
> > 
> > * userns2: unshare -U --map-root --mount
> > -----------------------------------------
> >     fd_from_userns1 = receive_fd_scm_rights();
> >     write_policy(fd_from_userns1, "my fancy policy");
> 
> Passing an fd around like this presumably indicates that you intend to let
> the recipient read/write to it.

Yes.

> 
> 
> > It also means that if you inherit an fd from a more privileged imans
> > instance you can write to it:
> 
> Now in this example we have to assume that root is making a mistake passing
> the file descriptor around?
> 
> # ls -l /sys/kernel/security/ima/
> total 0
> -r--r-----. 1 root root 0 Jan 18 12:48 ascii_runtime_measurements
> -r--r-----. 1 root root 0 Jan 18 12:48 binary_runtime_measurements
> -rw-------. 1 root root 0 Jan 18 12:48 policy
> -r--r-----. 1 root root 0 Jan 18 12:48 runtime_measurements_count
> -r--r-----. 1 root root 0 Jan 18 12:48 violations
> 
> > 
> > * initial_userns:
> 
> 
> So that's the host, right? And this is a 2nd independent example from the
> first.

Yes, these are just two examples to give a more complete idea of the
semantics by specifying two cases and how ima would behave.

> 
> > ------------------
> 
> >     mount -t securityfs securityfs /initial_securityfs
> > 
> >     fd_in_initial_securityfs = open("/initial_securityfs/ima_file, O_RDWR);
> > 
> >     pid = fork():
> >     if (pid == 0) {
> > 	unshare(CLONE_NEWUSER);
> > 	/* write idmapping for yourself */
> > 
> > 	write_policy(fd_in_initial_securityfs, "my fancy policy");
> >     }
> > 
> > would allow an unprivileged caller to alter the host's ima policy (as
> > you can see the example requires cooperation).
> 
> Sorry, not currently following. Root is the only one being able to open that
> IMA files on the host, right? Is this a mistake here where root passed the

Yes.

> fd onto the child and that child is not trusted to mess with the fd
> including passing it on further?

This is just an example what the current semantics mean in practice.
The above code snippet is neither good nor bad by itself as that depends
on context:

1) Let's say for whatever reason you would like to let unprivileged
   containers add policy rules (sorry in case I'm using the wrong ima
   vernacular) for themselves to the initial ima namespace during
   startup. That can be a rather valid and important use-case. Then this
   code snipped above where root opens a policy fd in the host
   securityfs instance and then let's the container access it across
   fork() + post namespace creation is "good" as it will allow the
   container to write the rules during setup while e.g. letting the
   container manager process (the code prior to fork) continue doing
   other stuff.

2) If you only want to ever allow container manager on the host write
   rules for the container in the initial ima ns but never the container
   setup process itself then the above code is "bad". The policy fd
   should've been closed before the fork() and definitely be opened
   o-cloexec.

The examples really were just trying to make obvious what the semantics
are that you're buying.

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

* Re: [PATCH v8 05/19] ima: Move measurement list related variables into ima_namespace
  2022-01-13 20:27   ` Mimi Zohar
@ 2022-01-19 12:23     ` Stefan Berger
  0 siblings, 0 replies; 50+ messages in thread
From: Stefan Berger @ 2022-01-19 12:23 UTC (permalink / raw)
  To: Mimi Zohar, Stefan Berger, linux-integrity
  Cc: serge, christian.brauner, containers, dmitry.kasatkin, ebiederm,
	krzysztof.struczynski, roberto.sassu, mpeters, lhinds, lsturman,
	puiterwi, jejb, jamjoom, linux-kernel, paul, rgb,
	linux-security-module, jmorris


On 1/13/22 15:27, Mimi Zohar wrote:
> Hi Stefan,
>
> On Tue, 2022-01-04 at 12:04 -0500, Stefan Berger wrote:
>> From: Stefan Berger <stefanb@linux.ibm.com>
>>
>> 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.
>>
>> Signed-off-by: Stefan Berger <stefanb@linux.ibm.com>
>> ---
>>   security/integrity/ima/ima.h             |  5 +++--
>>   security/integrity/ima/ima_fs.c          |  6 ++++--
>>   security/integrity/ima/ima_init_ima_ns.c |  5 +++++
>>   security/integrity/ima/ima_kexec.c       | 12 +++++++-----
>>   security/integrity/ima/ima_queue.c       | 24 ++++++++++--------------
>>   5 files changed, 29 insertions(+), 23 deletions(-)
>>
>> diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h
>> index 68d8a8e6fd1d..ee16ce5050c8 100644
>> --- a/security/integrity/ima/ima.h
>> +++ b/security/integrity/ima/ima.h
>> @@ -106,7 +106,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 {
>> @@ -134,6 +133,8 @@ struct ima_namespace {
>>   	int ima_policy_flag;
>>   
>>   	struct ima_h_table ima_htable;
>> +	struct list_head ima_measurements;
>> +	unsigned long binary_runtime_size;
> Please add a comment indicating binary_runtime_size is only applicable
> to ns_ima_init.

It looks like this now:

         struct list_head ima_measurements;
         /* binary_runtime_size is used only by init_ima_ns */
         unsigned long binary_runtime_size;


>
>
>>   } __randomize_layout;
>>   extern struct ima_namespace init_ima_ns;
>
>> @@ -124,12 +119,13 @@ 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);
>>   }
>>   
> Please update the function description indicating that either carrying
> the measurement list across kexec is limited to ns_ima_init or not
> supported by namespaces.

This is the updated function description:


/*
  * Return the amount of memory required for serializing the
  * entire binary_runtime_measurement list, including the ima_kexec_hdr
  * structure. Carrying the measurement list across kexec is limited
  * to init_ima_ns.
  */
>
> thanks,
>
> Mimi
>

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

* Re: [PATCH v8 19/19] ima: Enable IMA namespaces
  2022-01-19  9:46       ` Christian Brauner
@ 2022-01-19 12:45         ` Stefan Berger
  2022-01-19 13:03           ` Christian Brauner
  0 siblings, 1 reply; 50+ messages in thread
From: Stefan Berger @ 2022-01-19 12:45 UTC (permalink / raw)
  To: Christian Brauner
  Cc: Stefan Berger, linux-integrity, zohar, serge, christian.brauner,
	containers, dmitry.kasatkin, ebiederm, krzysztof.struczynski,
	roberto.sassu, mpeters, lhinds, lsturman, puiterwi, jejb,
	jamjoom, linux-kernel, paul, rgb, linux-security-module, jmorris


On 1/19/22 04:46, Christian Brauner wrote:
> On Tue, Jan 18, 2022 at 01:09:12PM -0500, Stefan Berger wrote:
>> On 1/14/22 09:45, Christian Brauner wrote:
>>> On Tue, Jan 04, 2022 at 12:04:16PM -0500, Stefan Berger wrote:
>>>> From: Stefan Berger <stefanb@linux.ibm.com>
>>>>
>>>> Introduce the IMA_NS in Kconfig for IMA namespace enablement.
>>>>
>>>> Enable the lazy initialization of an IMA namespace when a user mounts
>>>> SecurityFS. Now a user_namespace will get a pointer to an ima_namespace
>>>> and therefore add an implementation of get_current_ns() that returns this
>>>> pointer.
>>>>
>>>> get_current_ns() may now return a NULL pointer for as long as the IMA
>>>> namespace hasn't been created, yet. Therefore, return early from those
>>>> functions that may now get a NULL pointer from this call. The NULL
>>>> pointer can typically be treated similar to not having an IMA policy set
>>>> and simply return early from a function.
>>>>
>>>> Implement ima_ns_from_file() for SecurityFS-related files where we can
>>>> now get the IMA namespace via the user namespace pointer associated
>>>> with the superblock of the SecurityFS filesystem instance. Since
>>>> the functions using ima_ns_from_file() will only be called after an
>>>> ima_namesapce has been allocated they will never get a NULL pointer
>>>> for the ima_namespace.
>>>>
>>>> Switch access to userns->ima_ns to use acquire/release semantics to ensure
>>>> that a newly created ima_namespace structure is fully visible upon access.
>>>>
>>>> Replace usage of current_user_ns() with ima_ns_from_user_ns() that
>>>> implements a method to derive the user_namespace from the given
>>>> ima_namespace. It leads to the same result.
>>>>
>>>> Signed-off-by: Stefan Berger <stefanb@linux.ibm.com>
>>>> ---
>> [...]
>>>> diff --git a/security/integrity/ima/ima_policy.c b/security/integrity/ima/ima_policy.c
>>>> index b7dbc687b6ff..5a9b511ebbae 100644
>>>> --- a/security/integrity/ima/ima_policy.c
>>>> +++ b/security/integrity/ima/ima_policy.c
>>>> @@ -1333,6 +1333,7 @@ static unsigned int ima_parse_appraise_algos(char *arg)
>>>>    static int ima_parse_rule(struct ima_namespace *ns,
>>>>    			  char *rule, struct ima_rule_entry *entry)
>>>>    {
>>>> +	struct user_namespace *user_ns = ima_ns_to_user_ns(ns);
>>> So I think ima_policy_write() and therefore ima_parse_rule() can
>>> legitimately be reached at least from an ancestor userns but also from a
>>> completely unrelated userns via securityfs. Sorry, I didn't see this
>>> earlier. Think of the following two scenarios:
>>>
>>> * userns1: unshare -U --map-root --mount
>>> -----------------------------------------
>>>      mount -t securityfs securityfs /userns1_securityfs
>>>      fd_in_userns1 = open("/userns1_securityfs/ima_file, O_RDWR);
>>>
>>>      /* I _think_ that sending of fds here should work but I haven't
>>>       * bothered to recheck the scm code as I need to do some driving in a
>>>       * little bit so I'm running out of time...
>>>       */
>>>      send_fd_scm_rights(fd_in_userns1, task_in_userns2);
>>>
>>> * userns2: unshare -U --map-root --mount
>>> -----------------------------------------
>>>      fd_from_userns1 = receive_fd_scm_rights();
>>>      write_policy(fd_from_userns1, "my fancy policy");
>> Passing an fd around like this presumably indicates that you intend to let
>> the recipient read/write to it.
> Yes.
>
>>
>>> It also means that if you inherit an fd from a more privileged imans
>>> instance you can write to it:
>> Now in this example we have to assume that root is making a mistake passing
>> the file descriptor around?
>>
>> # ls -l /sys/kernel/security/ima/
>> total 0
>> -r--r-----. 1 root root 0 Jan 18 12:48 ascii_runtime_measurements
>> -r--r-----. 1 root root 0 Jan 18 12:48 binary_runtime_measurements
>> -rw-------. 1 root root 0 Jan 18 12:48 policy
>> -r--r-----. 1 root root 0 Jan 18 12:48 runtime_measurements_count
>> -r--r-----. 1 root root 0 Jan 18 12:48 violations
>>
>>> * initial_userns:
>>
>> So that's the host, right? And this is a 2nd independent example from the
>> first.
> Yes, these are just two examples to give a more complete idea of the
> semantics by specifying two cases and how ima would behave.
>
>>> ------------------
>>>      mount -t securityfs securityfs /initial_securityfs
>>>
>>>      fd_in_initial_securityfs = open("/initial_securityfs/ima_file, O_RDWR);
>>>
>>>      pid = fork():
>>>      if (pid == 0) {
>>> 	unshare(CLONE_NEWUSER);
>>> 	/* write idmapping for yourself */
>>>
>>> 	write_policy(fd_in_initial_securityfs, "my fancy policy");
>>>      }
>>>
>>> would allow an unprivileged caller to alter the host's ima policy (as
>>> you can see the example requires cooperation).
>> Sorry, not currently following. Root is the only one being able to open that
>> IMA files on the host, right? Is this a mistake here where root passed the
> Yes.
>
>> fd onto the child and that child is not trusted to mess with the fd
>> including passing it on further?
> This is just an example what the current semantics mean in practice.
> The above code snippet is neither good nor bad by itself as that depends
> on context:
>
> 1) Let's say for whatever reason you would like to let unprivileged
>     containers add policy rules (sorry in case I'm using the wrong ima
>     vernacular) for themselves to the initial ima namespace during
>     startup. That can be a rather valid and important use-case. Then this
>     code snipped above where root opens a policy fd in the host
>     securityfs instance and then let's the container access it across
>     fork() + post namespace creation is "good" as it will allow the
>     container to write the rules during setup while e.g. letting the
>     container manager process (the code prior to fork) continue doing
>     other stuff.

I would agree to supporting passing the fd to other containers to have 
them add rules to the policy, if that's what is intended.


>
> 2) If you only want to ever allow container manager on the host write
>     rules for the container in the initial ima ns but never the container
>     setup process itself then the above code is "bad". The policy fd
>     should've been closed before the fork() and definitely be opened
>     o-cloexec.

I would treat the IMA files' file descriptors like those of fd = 
open("/top/secret/file", O_RDWR) assuming the programmer knows the 
implications of passing the fd around, including knowing that open fds 
are inherited by child processes... I don't see that there's anything 
wrong with that.


>
> The examples really were just trying to make obvious what the semantics
> are that you're buying.

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

* Re: [PATCH v8 19/19] ima: Enable IMA namespaces
  2022-01-19 12:45         ` Stefan Berger
@ 2022-01-19 13:03           ` Christian Brauner
  0 siblings, 0 replies; 50+ messages in thread
From: Christian Brauner @ 2022-01-19 13:03 UTC (permalink / raw)
  To: Stefan Berger
  Cc: Stefan Berger, linux-integrity, zohar, serge, christian.brauner,
	containers, dmitry.kasatkin, ebiederm, krzysztof.struczynski,
	roberto.sassu, mpeters, lhinds, lsturman, puiterwi, jejb,
	jamjoom, linux-kernel, paul, rgb, linux-security-module, jmorris

On Wed, Jan 19, 2022 at 07:45:22AM -0500, Stefan Berger wrote:
> 
> On 1/19/22 04:46, Christian Brauner wrote:
> > On Tue, Jan 18, 2022 at 01:09:12PM -0500, Stefan Berger wrote:
> > > On 1/14/22 09:45, Christian Brauner wrote:
> > > > On Tue, Jan 04, 2022 at 12:04:16PM -0500, Stefan Berger wrote:
> > > > > From: Stefan Berger <stefanb@linux.ibm.com>
> > > > > 
> > > > > Introduce the IMA_NS in Kconfig for IMA namespace enablement.
> > > > > 
> > > > > Enable the lazy initialization of an IMA namespace when a user mounts
> > > > > SecurityFS. Now a user_namespace will get a pointer to an ima_namespace
> > > > > and therefore add an implementation of get_current_ns() that returns this
> > > > > pointer.
> > > > > 
> > > > > get_current_ns() may now return a NULL pointer for as long as the IMA
> > > > > namespace hasn't been created, yet. Therefore, return early from those
> > > > > functions that may now get a NULL pointer from this call. The NULL
> > > > > pointer can typically be treated similar to not having an IMA policy set
> > > > > and simply return early from a function.
> > > > > 
> > > > > Implement ima_ns_from_file() for SecurityFS-related files where we can
> > > > > now get the IMA namespace via the user namespace pointer associated
> > > > > with the superblock of the SecurityFS filesystem instance. Since
> > > > > the functions using ima_ns_from_file() will only be called after an
> > > > > ima_namesapce has been allocated they will never get a NULL pointer
> > > > > for the ima_namespace.
> > > > > 
> > > > > Switch access to userns->ima_ns to use acquire/release semantics to ensure
> > > > > that a newly created ima_namespace structure is fully visible upon access.
> > > > > 
> > > > > Replace usage of current_user_ns() with ima_ns_from_user_ns() that
> > > > > implements a method to derive the user_namespace from the given
> > > > > ima_namespace. It leads to the same result.
> > > > > 
> > > > > Signed-off-by: Stefan Berger <stefanb@linux.ibm.com>
> > > > > ---
> > > [...]
> > > > > diff --git a/security/integrity/ima/ima_policy.c b/security/integrity/ima/ima_policy.c
> > > > > index b7dbc687b6ff..5a9b511ebbae 100644
> > > > > --- a/security/integrity/ima/ima_policy.c
> > > > > +++ b/security/integrity/ima/ima_policy.c
> > > > > @@ -1333,6 +1333,7 @@ static unsigned int ima_parse_appraise_algos(char *arg)
> > > > >    static int ima_parse_rule(struct ima_namespace *ns,
> > > > >    			  char *rule, struct ima_rule_entry *entry)
> > > > >    {
> > > > > +	struct user_namespace *user_ns = ima_ns_to_user_ns(ns);
> > > > So I think ima_policy_write() and therefore ima_parse_rule() can
> > > > legitimately be reached at least from an ancestor userns but also from a
> > > > completely unrelated userns via securityfs. Sorry, I didn't see this
> > > > earlier. Think of the following two scenarios:
> > > > 
> > > > * userns1: unshare -U --map-root --mount
> > > > -----------------------------------------
> > > >      mount -t securityfs securityfs /userns1_securityfs
> > > >      fd_in_userns1 = open("/userns1_securityfs/ima_file, O_RDWR);
> > > > 
> > > >      /* I _think_ that sending of fds here should work but I haven't
> > > >       * bothered to recheck the scm code as I need to do some driving in a
> > > >       * little bit so I'm running out of time...
> > > >       */
> > > >      send_fd_scm_rights(fd_in_userns1, task_in_userns2);
> > > > 
> > > > * userns2: unshare -U --map-root --mount
> > > > -----------------------------------------
> > > >      fd_from_userns1 = receive_fd_scm_rights();
> > > >      write_policy(fd_from_userns1, "my fancy policy");
> > > Passing an fd around like this presumably indicates that you intend to let
> > > the recipient read/write to it.
> > Yes.
> > 
> > > 
> > > > It also means that if you inherit an fd from a more privileged imans
> > > > instance you can write to it:
> > > Now in this example we have to assume that root is making a mistake passing
> > > the file descriptor around?
> > > 
> > > # ls -l /sys/kernel/security/ima/
> > > total 0
> > > -r--r-----. 1 root root 0 Jan 18 12:48 ascii_runtime_measurements
> > > -r--r-----. 1 root root 0 Jan 18 12:48 binary_runtime_measurements
> > > -rw-------. 1 root root 0 Jan 18 12:48 policy
> > > -r--r-----. 1 root root 0 Jan 18 12:48 runtime_measurements_count
> > > -r--r-----. 1 root root 0 Jan 18 12:48 violations
> > > 
> > > > * initial_userns:
> > > 
> > > So that's the host, right? And this is a 2nd independent example from the
> > > first.
> > Yes, these are just two examples to give a more complete idea of the
> > semantics by specifying two cases and how ima would behave.
> > 
> > > > ------------------
> > > >      mount -t securityfs securityfs /initial_securityfs
> > > > 
> > > >      fd_in_initial_securityfs = open("/initial_securityfs/ima_file, O_RDWR);
> > > > 
> > > >      pid = fork():
> > > >      if (pid == 0) {
> > > > 	unshare(CLONE_NEWUSER);
> > > > 	/* write idmapping for yourself */
> > > > 
> > > > 	write_policy(fd_in_initial_securityfs, "my fancy policy");
> > > >      }
> > > > 
> > > > would allow an unprivileged caller to alter the host's ima policy (as
> > > > you can see the example requires cooperation).
> > > Sorry, not currently following. Root is the only one being able to open that
> > > IMA files on the host, right? Is this a mistake here where root passed the
> > Yes.
> > 
> > > fd onto the child and that child is not trusted to mess with the fd
> > > including passing it on further?
> > This is just an example what the current semantics mean in practice.
> > The above code snippet is neither good nor bad by itself as that depends
> > on context:
> > 
> > 1) Let's say for whatever reason you would like to let unprivileged
> >     containers add policy rules (sorry in case I'm using the wrong ima
> >     vernacular) for themselves to the initial ima namespace during
> >     startup. That can be a rather valid and important use-case. Then this
> >     code snipped above where root opens a policy fd in the host
> >     securityfs instance and then let's the container access it across
> >     fork() + post namespace creation is "good" as it will allow the
> >     container to write the rules during setup while e.g. letting the
> >     container manager process (the code prior to fork) continue doing
> >     other stuff.
> 
> I would agree to supporting passing the fd to other containers to have them
> add rules to the policy, if that's what is intended.
> 
> 
> > 
> > 2) If you only want to ever allow container manager on the host write
> >     rules for the container in the initial ima ns but never the container
> >     setup process itself then the above code is "bad". The policy fd
> >     should've been closed before the fork() and definitely be opened
> >     o-cloexec.
> 
> I would treat the IMA files' file descriptors like those of fd =
> open("/top/secret/file", O_RDWR) assuming the programmer knows the
> implications of passing the fd around, including knowing that open fds are
> inherited by child processes... I don't see that there's anything wrong with
> that.

Agreed. And we do already have these semantics for a range of fds
including splitting open and write between callers and namespaces.

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

* Re: [PATCH v8 03/19] ima: Move policy related variables into ima_namespace
  2022-01-13 20:26   ` Mimi Zohar
  2022-01-14 10:48     ` Christian Brauner
@ 2022-01-19 13:32     ` Stefan Berger
  1 sibling, 0 replies; 50+ messages in thread
From: Stefan Berger @ 2022-01-19 13:32 UTC (permalink / raw)
  To: Mimi Zohar, Stefan Berger, linux-integrity
  Cc: serge, christian.brauner, containers, dmitry.kasatkin, ebiederm,
	krzysztof.struczynski, roberto.sassu, mpeters, lhinds, lsturman,
	puiterwi, jejb, jamjoom, linux-kernel, paul, rgb,
	linux-security-module, jmorris


On 1/13/22 15:26, Mimi Zohar wrote:
> Hi Stefan,
>
> On Tue, 2022-01-04 at 12:04 -0500, Stefan Berger wrote:
>> From: Stefan Berger <stefanb@linux.ibm.com>
>>
>> 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_ns_from_file() to get the IMA namespace via the user
>> namespace of the SecurityFS superblock that a file belongs to.
>>
>> To get the current ima_namespace use get_current_ns() when a function
>> that is related to a policy rule is called. In other cases where functions
>> are called due file attribute modifications, use init_ima_ns, since these
>> functions are related to IMA appraisal and changes to file attributes are
>> only relevant to the init_ima_ns until IMA namespaces also support IMA
>> appraisal. In ima_file_free() use init_ima_ns since in this case flags
>> related to file measurements may be affected, which is not supported in
>> IMA namespaces, yet.
>>
>> Signed-off-by: Stefan Berger <stefanb@linux.ibm.com>
> Please split this patch into "ima: pass through ima namespace", or some
> other name,  and "ima: Move policy related variables into

What is supposed to happen in the 'ima: pass through ima namespace' 
patch? What part of this patch do you expect to see there and what do 
you want to see deferred to a 2nd patch?


> ima_namespace".  The other option is to combine the "pass through ima
> namespace" with the 2nd patch, like Christian's example.

You mean I should merge this patch with the 2nd?

I am a bit confused as to what you are proposing. The first option would 
create 2 patches out of this one, the 2nd option would merge this one 
with the 2nd patch.

The equivalent of Christian's 2nd patch would be to merge this patch 
into the 2nd. So then let's do that?


>
>> ---
>>   security/integrity/ima/ima.h                 |  49 ++++---
>>   security/integrity/ima/ima_api.c             |   8 +-
>>   security/integrity/ima/ima_appraise.c        |  28 ++--
>>   security/integrity/ima/ima_asymmetric_keys.c |   4 +-
>>   security/integrity/ima/ima_fs.c              |  16 ++-
>>   security/integrity/ima/ima_init.c            |   8 +-
>>   security/integrity/ima/ima_init_ima_ns.c     |   6 +
>>   security/integrity/ima/ima_main.c            |  83 +++++++----
>>   security/integrity/ima/ima_policy.c          | 142 ++++++++++---------
>>   security/integrity/ima/ima_queue_keys.c      |  11 +-
>>   10 files changed, 213 insertions(+), 142 deletions(-)
>>
>> diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h
>> index c4af3275f015..0b3dc9425076 100644
>> --- a/security/integrity/ima/ima.h
>> +++ b/security/integrity/ima/ima.h
>> @@ -20,6 +20,7 @@
>>   #include <linux/hash.h>
>>   #include <linux/tpm.h>
>>   #include <linux/audit.h>
>> +#include <linux/user_namespace.h>
>>   #include <crypto/hash_info.h>
>>   
>>   #include "../integrity.h"
>> @@ -43,9 +44,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;
>>   
>> @@ -120,6 +118,14 @@ struct ima_kexec_hdr {
>>   };
>>   
>>   struct ima_namespace {
>> +	struct list_head ima_default_rules;
>> +	/* ns's policy rules */
> Thank you for adding comments.  Why is the ima_default_rules not
> considered "ns's policy rules"?   Will this come later or is it limited
> to init_ima_ns?
Let me move the comment up.
>
>> +	struct list_head ima_policy_rules;
>> +	struct list_head ima_temp_rules;
>> +	/* Pointer to ns's current policy */
>> +	struct list_head __rcu *ima_rules;
> Since "Pointer to ns's current policy" only refers to ima_rules, append
> it to the variable definition.


Ok, I will move it onto the same line then.


>
>> +	/* current content of the policy */
>> +	int ima_policy_flag;
> Similarly here append the comment to the variable definition.

Will do. Thanks.


>
>>   } __randomize_layout;
>>   extern struct ima_namespace init_ima_ns;
> thanks,
>
> Mimi
>
>

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

end of thread, other threads:[~2022-01-19 13:32 UTC | newest]

Thread overview: 50+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2022-01-04 17:03 [PATCH v8 00/19] ima: Namespace IMA with audit support in IMA-ns Stefan Berger
2022-01-04 17:03 ` [PATCH v8 01/19] securityfs: Extend securityfs with namespacing support Stefan Berger
2022-01-05  3:58   ` Al Viro
2022-01-05 10:18     ` Christian Brauner
2022-01-11 12:16       ` Mimi Zohar
2022-01-11 14:12         ` Christian Brauner
2022-01-04 17:03 ` [PATCH v8 02/19] ima: Define ima_namespace structure and implement basic functions Stefan Berger
2022-01-04 17:04 ` [PATCH v8 03/19] ima: Move policy related variables into ima_namespace Stefan Berger
2022-01-13 20:26   ` Mimi Zohar
2022-01-14 10:48     ` Christian Brauner
2022-01-19 13:32     ` Stefan Berger
2022-01-04 17:04 ` [PATCH v8 04/19] ima: Move ima_htable " Stefan Berger
2022-01-04 17:04 ` [PATCH v8 05/19] ima: Move measurement list related variables " Stefan Berger
2022-01-13 20:27   ` Mimi Zohar
2022-01-19 12:23     ` Stefan Berger
2022-01-04 17:04 ` [PATCH v8 06/19] ima: Move some IMA policy and filesystem " Stefan Berger
2022-01-04 17:04 ` [PATCH v8 07/19] ima: Move dentry into ima_namespace and others onto stack Stefan Berger
2022-01-13 20:28   ` Mimi Zohar
2022-01-18 20:12     ` Stefan Berger
2022-01-18 20:42       ` Mimi Zohar
2022-01-18 20:54         ` Stefan Berger
2022-01-04 17:04 ` [PATCH v8 08/19] ima: Use mac_admin_ns_capable() to check corresponding capability Stefan Berger
2022-01-05 20:55   ` kernel test robot
2022-01-05 20:55     ` kernel test robot
2022-01-13 20:28   ` Mimi Zohar
2022-01-04 17:04 ` [PATCH v8 09/19] ima: Only accept AUDIT rules for non-init_ima_ns namespaces for now Stefan Berger
2022-01-04 17:04 ` [PATCH v8 10/19] ima: Implement hierarchical processing of file accesses Stefan Berger
2022-01-14 11:21   ` Christian Brauner
2022-01-18 18:25     ` Stefan Berger
2022-01-04 17:04 ` [PATCH v8 11/19] ima: Implement ima_free_policy_rules() for freeing of an ima_namespace Stefan Berger
2022-01-04 17:04 ` [PATCH v8 12/19] userns: Add pointer to ima_namespace to user_namespace Stefan Berger
2022-01-04 17:04 ` [PATCH v8 13/19] ima: Add functions for creation and freeing of an ima_namespace Stefan Berger
2022-01-14 11:43   ` Christian Brauner
2022-01-04 17:04 ` [PATCH v8 14/19] integrity/ima: Define ns_status for storing namespaced iint data Stefan Berger
2022-01-04 17:04 ` [PATCH v8 15/19] ima: Namespace audit status flags Stefan Berger
2022-01-04 17:04 ` [PATCH v8 16/19] ima: Enable re-auditing of modified files Stefan Berger
2022-01-05 15:21   ` Stefan Berger
2022-01-04 17:04 ` [PATCH v8 17/19] ima: Setup securityfs for IMA namespace Stefan Berger
2022-01-04 17:04 ` [PATCH v8 18/19] ima: Show owning user namespace's uid and gid when displaying policy Stefan Berger
2022-01-14 13:45   ` Christian Brauner
2022-01-18 16:31     ` Stefan Berger
2022-01-19  9:23       ` Christian Brauner
2022-01-04 17:04 ` [PATCH v8 19/19] ima: Enable IMA namespaces Stefan Berger
2022-01-14 12:05   ` Christian Brauner
2022-01-18 17:53     ` Stefan Berger
2022-01-14 14:45   ` Christian Brauner
2022-01-18 18:09     ` Stefan Berger
2022-01-19  9:46       ` Christian Brauner
2022-01-19 12:45         ` Stefan Berger
2022-01-19 13:03           ` Christian Brauner

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.