All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v10 00/27] ima: Namespace IMA with audit support in IMA-ns
@ 2022-02-01 20:37 Stefan Berger
  2022-02-01 20:37 ` [PATCH v10 01/27] ima: Remove ima_policy file before directory Stefan Berger
                   ` (27 more replies)
  0 siblings, 28 replies; 86+ messages in thread
From: Stefan Berger @ 2022-02-01 20:37 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

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 1 > /mnt/ima/active; \
  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.

In the above the writing of '1' to the 'active' file is used to activate
the IMA namespace. Future extensions to IMA namespaces will make use of
the configuration stage after the mounting of securityfs and before the
activation to for example choose the measurement log template.

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+imans.v10.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: https://lore.kernel.org/all/20220104170416.1923685-1-stefanb@linux.vnet.ibm.com/#r
v9: https://lore.kernel.org/linux-integrity/?t=20220131234353

v10:
 - Added A-b's; addressed issues from v9
 - Added 2 patches to support freeing of iint after namespace deletion
 - Added patch to return error code from securityfs functions
 - Added patch to limit number of policy rules in IMA-ns to 1024

v9:
 - Rearranged order of patch that adds IMA-ns pointer to user_ns to be before
   hierarchical processing patch
 - Renamed ns_status variables from status to ns_status to avoid clashes
 - Added bug fixing patches to top
 - Added patch 'Move arch_policy_entry into ima_namespace'
 - Added patch 'Move ima_lsm_policy_notifier into ima_namespace'
 - Addressed comments to v8
 - Added change comments to individual patches
 - Formatted code following checkpatch.pl --strict

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



Christian Brauner (1):
  securityfs: rework dentry creation

Mehmet Kayaalp (2):
  integrity/ima: Define ns_status for storing namespaced iint data
  ima: Namespace audit status flags

Stefan Berger (24):
  ima: Remove ima_policy file before directory
  ima: Do not print policy rule with inactive LSM labels
  ima: Return error code obtained from securityfs functions
  ima: Define ima_namespace struct and start moving variables into it
  ima: Move arch_policy_entry 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 IMA securityfs files into ima_namespace or onto stack
  ima: Move ima_lsm_policy_notifier into ima_namespace
  ima: Define mac_admin_ns_capable() as a wrapper for ns_capable()
  ima: Only accept AUDIT rules for non-init_ima_ns namespaces for now
  userns: Add pointer to ima_namespace to user_namespace
  ima: Implement hierarchical processing of file accesses
  ima: Implement ima_free_policy_rules() for freeing of an ima_namespace
  ima: Add functions for creating and freeing of an ima_namespace
  integrity: Add optional callback function to integrity_inode_free()
  ima: Remove unused iints from the integrity_iint_cache
  securityfs: Extend securityfs with namespacing support
  ima: Setup securityfs for IMA namespace
  ima: Introduce securityfs file to activate an IMA namespace
  ima: Show owning user namespace's uid and gid when displaying policy
  ima: Limit number of policy rules in non-init_ima_ns
  ima: Enable IMA namespaces

 include/linux/capability.h                   |   6 +
 include/linux/ima.h                          |  36 ++
 include/linux/integrity.h                    |   8 +-
 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                    |  26 +-
 security/integrity/ima/Makefile              |   3 +-
 security/integrity/ima/ima.h                 | 241 ++++++++++--
 security/integrity/ima/ima_api.c             |  34 +-
 security/integrity/ima/ima_appraise.c        |  31 +-
 security/integrity/ima/ima_asymmetric_keys.c |   8 +-
 security/integrity/ima/ima_fs.c              | 299 +++++++++++---
 security/integrity/ima/ima_init.c            |  19 +-
 security/integrity/ima/ima_init_ima_ns.c     |  65 ++++
 security/integrity/ima/ima_kexec.c           |  15 +-
 security/integrity/ima/ima_main.c            | 223 ++++++++---
 security/integrity/ima/ima_ns.c              |  61 +++
 security/integrity/ima/ima_ns_status.c       | 385 +++++++++++++++++++
 security/integrity/ima/ima_policy.c          | 266 ++++++++-----
 security/integrity/ima/ima_queue.c           |  63 ++-
 security/integrity/ima/ima_queue_keys.c      |  11 +-
 security/integrity/ima/ima_template.c        |   5 +-
 security/integrity/integrity.h               |  39 +-
 security/security.c                          |   2 +-
 27 files changed, 1606 insertions(+), 344 deletions(-)
 create mode 100644 security/integrity/ima/ima_init_ima_ns.c
 create mode 100644 security/integrity/ima/ima_ns.c
 create mode 100644 security/integrity/ima/ima_ns_status.c

-- 
2.31.1


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

* [PATCH v10 01/27] ima: Remove ima_policy file before directory
  2022-02-01 20:37 [PATCH v10 00/27] ima: Namespace IMA with audit support in IMA-ns Stefan Berger
@ 2022-02-01 20:37 ` Stefan Berger
  2022-02-10 12:02   ` Mimi Zohar
  2022-02-01 20:37 ` [PATCH v10 02/27] ima: Do not print policy rule with inactive LSM labels Stefan Berger
                   ` (26 subsequent siblings)
  27 siblings, 1 reply; 86+ messages in thread
From: Stefan Berger @ 2022-02-01 20:37 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, Christian Brauner

The removal of ima_dir currently fails since ima_policy still exists, so
remove the ima_policy file before removing the directory.

Fixes: 4af4662fa4a9 ("integrity: IMA policy")
Signed-off-by: Stefan Berger <stefanb@linux.ibm.com>
Acked-by: Christian Brauner <brauner@kernel.org>
---
 security/integrity/ima/ima_fs.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/security/integrity/ima/ima_fs.c b/security/integrity/ima/ima_fs.c
index 3d8e9d5db5aa..3ad8f7734208 100644
--- a/security/integrity/ima/ima_fs.c
+++ b/security/integrity/ima/ima_fs.c
@@ -496,12 +496,12 @@ int __init ima_fs_init(void)
 
 	return 0;
 out:
+	securityfs_remove(ima_policy);
 	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;
 }
-- 
2.31.1


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

* [PATCH v10 02/27] ima: Do not print policy rule with inactive LSM labels
  2022-02-01 20:37 [PATCH v10 00/27] ima: Namespace IMA with audit support in IMA-ns Stefan Berger
  2022-02-01 20:37 ` [PATCH v10 01/27] ima: Remove ima_policy file before directory Stefan Berger
@ 2022-02-01 20:37 ` Stefan Berger
  2022-02-02 14:17   ` Christian Brauner
  2022-02-01 20:37 ` [PATCH v10 03/27] ima: Return error code obtained from securityfs functions Stefan Berger
                   ` (25 subsequent siblings)
  27 siblings, 1 reply; 86+ messages in thread
From: Stefan Berger @ 2022-02-01 20:37 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

Before printing a policy rule scan for inactive LSM labels in the policy
rule. Inactive LSM labels are identified by args_p != NULL and
rule == NULL.

Fixes: b16942455193 ("ima: use the lsm policy update notifier")
Signed-off-by: Stefan Berger <stefanb@linux.ibm.com>
---
 security/integrity/ima/ima_policy.c | 8 ++++++++
 1 file changed, 8 insertions(+)

diff --git a/security/integrity/ima/ima_policy.c b/security/integrity/ima/ima_policy.c
index 320ca80aacab..2a1f6418b10a 100644
--- a/security/integrity/ima/ima_policy.c
+++ b/security/integrity/ima/ima_policy.c
@@ -1967,6 +1967,14 @@ int ima_policy_show(struct seq_file *m, void *v)
 
 	rcu_read_lock();
 
+	/* Do not print rules with inactive LSM labels */
+	for (i = 0; i < MAX_LSM_RULES; i++) {
+		if (entry->lsm[i].args_p && !entry->lsm[i].rule) {
+			rcu_read_unlock();
+			return 0;
+		}
+	}
+
 	if (entry->action & MEASURE)
 		seq_puts(m, pt(Opt_measure));
 	if (entry->action & DONT_MEASURE)
-- 
2.31.1


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

* [PATCH v10 03/27] ima: Return error code obtained from securityfs functions
  2022-02-01 20:37 [PATCH v10 00/27] ima: Namespace IMA with audit support in IMA-ns Stefan Berger
  2022-02-01 20:37 ` [PATCH v10 01/27] ima: Remove ima_policy file before directory Stefan Berger
  2022-02-01 20:37 ` [PATCH v10 02/27] ima: Do not print policy rule with inactive LSM labels Stefan Berger
@ 2022-02-01 20:37 ` Stefan Berger
  2022-02-10 12:02   ` Mimi Zohar
  2022-02-01 20:37 ` [PATCH v10 04/27] securityfs: rework dentry creation Stefan Berger
                   ` (24 subsequent siblings)
  27 siblings, 1 reply; 86+ messages in thread
From: Stefan Berger @ 2022-02-01 20:37 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

If an error occurs when creating a securityfs file, return the exact
error code to the caller.

Signed-off-by: Stefan Berger <stefanb@linux.ibm.com>
---
 security/integrity/ima/ima_fs.c | 31 +++++++++++++++++++++++--------
 1 file changed, 23 insertions(+), 8 deletions(-)

diff --git a/security/integrity/ima/ima_fs.c b/security/integrity/ima/ima_fs.c
index 3ad8f7734208..cd1683dad3bf 100644
--- a/security/integrity/ima/ima_fs.c
+++ b/security/integrity/ima/ima_fs.c
@@ -452,47 +452,61 @@ static const struct file_operations ima_measure_policy_ops = {
 
 int __init ima_fs_init(void)
 {
+	int ret;
+
 	ima_dir = securityfs_create_dir("ima", integrity_dir);
 	if (IS_ERR(ima_dir))
-		return -1;
+		return PTR_ERR(ima_dir);
 
 	ima_symlink = securityfs_create_symlink("ima", NULL, "integrity/ima",
 						NULL);
-	if (IS_ERR(ima_symlink))
+	if (IS_ERR(ima_symlink)) {
+		ret = PTR_ERR(ima_symlink);
 		goto out;
+	}
 
 	binary_runtime_measurements =
 	    securityfs_create_file("binary_runtime_measurements",
 				   S_IRUSR | S_IRGRP, ima_dir, NULL,
 				   &ima_measurements_ops);
-	if (IS_ERR(binary_runtime_measurements))
+	if (IS_ERR(binary_runtime_measurements)) {
+		ret = PTR_ERR(binary_runtime_measurements);
 		goto out;
+	}
 
 	ascii_runtime_measurements =
 	    securityfs_create_file("ascii_runtime_measurements",
 				   S_IRUSR | S_IRGRP, ima_dir, NULL,
 				   &ima_ascii_measurements_ops);
-	if (IS_ERR(ascii_runtime_measurements))
+	if (IS_ERR(ascii_runtime_measurements)) {
+		ret = PTR_ERR(ascii_runtime_measurements);
 		goto out;
+	}
 
 	runtime_measurements_count =
 	    securityfs_create_file("runtime_measurements_count",
 				   S_IRUSR | S_IRGRP, ima_dir, NULL,
 				   &ima_measurements_count_ops);
-	if (IS_ERR(runtime_measurements_count))
+	if (IS_ERR(runtime_measurements_count)) {
+		ret = PTR_ERR(runtime_measurements_count);
 		goto out;
+	}
 
 	violations =
 	    securityfs_create_file("violations", S_IRUSR | S_IRGRP,
 				   ima_dir, NULL, &ima_htable_violations_ops);
-	if (IS_ERR(violations))
+	if (IS_ERR(violations)) {
+		ret = PTR_ERR(violations);
 		goto out;
+	}
 
 	ima_policy = securityfs_create_file("policy", POLICY_FILE_FLAGS,
 					    ima_dir, NULL,
 					    &ima_measure_policy_ops);
-	if (IS_ERR(ima_policy))
+	if (IS_ERR(ima_policy)) {
+		ret = PTR_ERR(ima_policy);
 		goto out;
+	}
 
 	return 0;
 out:
@@ -503,5 +517,6 @@ int __init ima_fs_init(void)
 	securityfs_remove(binary_runtime_measurements);
 	securityfs_remove(ima_symlink);
 	securityfs_remove(ima_dir);
-	return -1;
+
+	return ret;
 }
-- 
2.31.1


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

* [PATCH v10 04/27] securityfs: rework dentry creation
  2022-02-01 20:37 [PATCH v10 00/27] ima: Namespace IMA with audit support in IMA-ns Stefan Berger
                   ` (2 preceding siblings ...)
  2022-02-01 20:37 ` [PATCH v10 03/27] ima: Return error code obtained from securityfs functions Stefan Berger
@ 2022-02-01 20:37 ` Stefan Berger
  2022-02-10 12:03   ` Mimi Zohar
  2022-02-01 20:37 ` [PATCH v10 05/27] ima: Define ima_namespace struct and start moving variables into it Stefan Berger
                   ` (23 subsequent siblings)
  27 siblings, 1 reply; 86+ messages in thread
From: Stefan Berger @ 2022-02-01 20:37 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, Christian Brauner

From: Christian Brauner <brauner@kernel.org>

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.31.1


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

* [PATCH v10 05/27] ima: Define ima_namespace struct and start moving variables into it
  2022-02-01 20:37 [PATCH v10 00/27] ima: Namespace IMA with audit support in IMA-ns Stefan Berger
                   ` (3 preceding siblings ...)
  2022-02-01 20:37 ` [PATCH v10 04/27] securityfs: rework dentry creation Stefan Berger
@ 2022-02-01 20:37 ` Stefan Berger
  2022-02-16 14:41   ` Mimi Zohar
  2022-02-01 20:37 ` [PATCH v10 06/27] ima: Move arch_policy_entry into ima_namespace Stefan Berger
                   ` (22 subsequent siblings)
  27 siblings, 1 reply; 86+ messages in thread
From: Stefan Berger @ 2022-02-01 20:37 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, Christian Brauner

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

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 &init_ima_ns when a function
that is related to a policy rule is called.

Signed-off-by: Stefan Berger <stefanb@linux.ibm.com>
Acked-by: Christian Brauner <brauner@kernel.org>

---

v9:
 - squashed patched 2 and 3 of v8
 - ima_post_read_file: only access ima_appraise in case of init_ima_ns
---
 security/integrity/ima/Makefile              |   2 +-
 security/integrity/ima/ima.h                 |  53 ++++---
 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            |  12 +-
 security/integrity/ima/ima_init_ima_ns.c     |  29 ++++
 security/integrity/ima/ima_main.c            |  87 +++++++-----
 security/integrity/ima/ima_policy.c          | 142 ++++++++++---------
 security/integrity/ima/ima_queue_keys.c      |  11 +-
 11 files changed, 248 insertions(+), 144 deletions(-)
 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..5b44fa6f27c4 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;
 
@@ -119,6 +117,17 @@ struct ima_kexec_hdr {
 	u64 count;
 };
 
+struct ima_namespace {
+	/* policy rules */
+	struct list_head ima_default_rules;
+	struct list_head ima_policy_rules;
+	struct list_head ima_temp_rules;
+
+	struct list_head __rcu *ima_rules;	/* current policy */
+	int ima_policy_flag;
+} __randomize_layout;
+extern struct ima_namespace init_ima_ns;
+
 extern const int read_idmap[];
 
 #ifdef CONFIG_HAVE_IMA_KEXEC
@@ -136,6 +145,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);
@@ -243,18 +253,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,
@@ -268,7 +279,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,
@@ -285,17 +297,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);
@@ -311,14 +324,16 @@ int ima_policy_show(struct seq_file *m, void *v);
 #define IMA_APPRAISE_KEXEC	0x40
 
 #ifdef CONFIG_IMA_APPRAISE
-int ima_check_blacklist(struct integrity_iint_cache *iint,
+int ima_check_blacklist(struct ima_namespace *ns,
+			struct integrity_iint_cache *iint,
 			const struct modsig *modsig, int pcr);
 int ima_appraise_measurement(enum ima_hooks func,
 			     struct integrity_iint_cache *iint,
 			     struct file *file, const unsigned char *filename,
 			     struct evm_ima_xattr_data *xattr_value,
 			     int xattr_len, const struct modsig *modsig);
-int ima_must_appraise(struct user_namespace *mnt_userns, struct inode *inode,
+int ima_must_appraise(struct ima_namespace *ns,
+		      struct user_namespace *mnt_userns, struct inode *inode,
 		      int mask, enum ima_hooks func);
 void ima_update_xattr(struct integrity_iint_cache *iint, struct file *file);
 enum integrity_status ima_get_cache_status(struct integrity_iint_cache *iint,
@@ -329,7 +344,8 @@ int ima_read_xattr(struct dentry *dentry,
 		   struct evm_ima_xattr_data **xattr_value);
 
 #else
-static inline int ima_check_blacklist(struct integrity_iint_cache *iint,
+static inline int ima_check_blacklist(struct ima_namespace *ns,
+				      struct integrity_iint_cache *iint,
 				      const struct modsig *modsig, int pcr)
 {
 	return 0;
@@ -346,7 +362,8 @@ static inline int ima_appraise_measurement(enum ima_hooks func,
 	return INTEGRITY_UNKNOWN;
 }
 
-static inline int ima_must_appraise(struct user_namespace *mnt_userns,
+static inline int ima_must_appraise(struct ima_namespace *ns,
+				    struct user_namespace *mnt_userns,
 				    struct inode *inode, int mask,
 				    enum ima_hooks func)
 {
diff --git a/security/integrity/ima/ima_api.c b/security/integrity/ima/ima_api.c
index 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..70d87df26068 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 = &init_ima_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 cd1683dad3bf..f7ad93a56982 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 b26fa67476b4..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
 
@@ -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");
@@ -145,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
new file mode 100644
index 000000000000..c919a456b525
--- /dev/null
+++ b/security/integrity/ima/ima_init_ima_ns.c
@@ -0,0 +1,29 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2016-2022 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)
+{
+	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;
+}
+
+int __init ima_ns_init(void)
+{
+	return ima_init_namespace(&init_ima_ns);
+}
+
+struct ima_namespace init_ima_ns = {
+};
+EXPORT_SYMBOL(init_ima_ns);
diff --git a/security/integrity/ima/ima_main.c b/security/integrity/ima/ima_main.c
index 465865412100..4940f8dda580 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 = &init_ima_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 = &init_ima_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 = &init_ima_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 = &init_ima_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 = &init_ima_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 = &init_ima_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 = &init_ima_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 = &init_ima_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 = &init_ima_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 = &init_ima_ns;
 	enum ima_hooks func;
 	u32 secid;
 
@@ -746,14 +761,15 @@ int ima_post_read_file(struct file *file, void *buf, loff_t size,
 		return 0;
 
 	if (!file || !buf || size == 0) { /* should never happen */
-		if (ima_appraise & IMA_APPRAISE_ENFORCE)
+		if (ns == &init_ima_ns &&
+		    (ima_appraise & IMA_APPRAISE_ENFORCE))
 			return -EACCES;
 		return 0;
 	}
 
 	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 +857,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 +876,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 +905,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 +924,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 +961,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 +995,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 = &init_ima_ns;
 	struct fd f;
 
 	if (!buf || !size)
@@ -986,7 +1005,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 +1036,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 = &init_ima_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);
@@ -1028,6 +1050,7 @@ EXPORT_SYMBOL_GPL(ima_measure_critical_data);
 
 static int __init init_ima(void)
 {
+	struct ima_namespace *ns = &init_ima_ns;
 	int error;
 
 	ima_appraise_parse_cmdline();
@@ -1052,7 +1075,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(ns);
 
 	return error;
 }
diff --git a/security/integrity/ima/ima_policy.c b/security/integrity/ima/ima_policy.c
index 2a1f6418b10a..b0e1c16b7f37 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)
@@ -2174,6 +2185,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;
@@ -2185,7 +2197,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] 86+ messages in thread

* [PATCH v10 06/27] ima: Move arch_policy_entry into ima_namespace
  2022-02-01 20:37 [PATCH v10 00/27] ima: Namespace IMA with audit support in IMA-ns Stefan Berger
                   ` (4 preceding siblings ...)
  2022-02-01 20:37 ` [PATCH v10 05/27] ima: Define ima_namespace struct and start moving variables into it Stefan Berger
@ 2022-02-01 20:37 ` Stefan Berger
  2022-02-16 16:39   ` Mimi Zohar
  2022-02-01 20:37 ` [PATCH v10 07/27] ima: Move ima_htable " Stefan Berger
                   ` (21 subsequent siblings)
  27 siblings, 1 reply; 86+ messages in thread
From: Stefan Berger @ 2022-02-01 20:37 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, Christian Brauner

Move the arch_policy_entry pointer into ima_namespace.

When freeing the memory set the pointer to NULL.

Signed-off-by: Stefan Berger <stefanb@linux.ibm.com>
Acked-by: Christian Brauner <brauner@kernel.org>
Reviewed-by: Mimi Zohar <zohar@linux.ibm.com>
---
 security/integrity/ima/ima.h             |  3 +++
 security/integrity/ima/ima_init_ima_ns.c |  1 +
 security/integrity/ima/ima_policy.c      | 23 +++++++++++------------
 3 files changed, 15 insertions(+), 12 deletions(-)

diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h
index 5b44fa6f27c4..a4669b55c2e0 100644
--- a/security/integrity/ima/ima.h
+++ b/security/integrity/ima/ima.h
@@ -125,6 +125,9 @@ struct ima_namespace {
 
 	struct list_head __rcu *ima_rules;	/* current policy */
 	int ima_policy_flag;
+
+	/* An array of architecture specific rules */
+	struct ima_rule_entry *arch_policy_entry;
 } __randomize_layout;
 extern struct ima_namespace init_ima_ns;
 
diff --git a/security/integrity/ima/ima_init_ima_ns.c b/security/integrity/ima/ima_init_ima_ns.c
index c919a456b525..ae33621c3955 100644
--- a/security/integrity/ima/ima_init_ima_ns.c
+++ b/security/integrity/ima/ima_init_ima_ns.c
@@ -15,6 +15,7 @@ static int ima_init_namespace(struct ima_namespace *ns)
 	INIT_LIST_HEAD(&ns->ima_temp_rules);
 	ns->ima_rules = (struct list_head __rcu *)(&ns->ima_default_rules);
 	ns->ima_policy_flag = 0;
+	ns->arch_policy_entry = NULL;
 
 	return 0;
 }
diff --git a/security/integrity/ima/ima_policy.c b/security/integrity/ima/ima_policy.c
index b0e1c16b7f37..05b2bc06ab0c 100644
--- a/security/integrity/ima/ima_policy.c
+++ b/security/integrity/ima/ima_policy.c
@@ -229,9 +229,6 @@ static struct ima_rule_entry critical_data_rules[] __ro_after_init = {
 	{.action = MEASURE, .func = CRITICAL_DATA, .flags = IMA_FUNC},
 };
 
-/* An array of architecture specific rules */
-static struct ima_rule_entry *arch_policy_entry __ro_after_init;
-
 static int ima_policy __initdata;
 
 static int __init default_measure_policy_setup(char *str)
@@ -860,9 +857,10 @@ static int __init ima_init_arch_policy(struct ima_namespace *ns)
 	for (rules = arch_rules; *rules != NULL; rules++)
 		arch_entries++;
 
-	arch_policy_entry = kcalloc(arch_entries + 1,
-				    sizeof(*arch_policy_entry), GFP_KERNEL);
-	if (!arch_policy_entry)
+	ns->arch_policy_entry = kcalloc(arch_entries + 1,
+					sizeof(*ns->arch_policy_entry),
+					GFP_KERNEL);
+	if (!ns->arch_policy_entry)
 		return 0;
 
 	/* Convert each policy string rules to struct ima_rule_entry format */
@@ -872,13 +870,13 @@ static int __init ima_init_arch_policy(struct ima_namespace *ns)
 
 		result = strscpy(rule, *rules, sizeof(rule));
 
-		INIT_LIST_HEAD(&arch_policy_entry[i].list);
-		result = ima_parse_rule(ns, rule, &arch_policy_entry[i]);
+		INIT_LIST_HEAD(&ns->arch_policy_entry[i].list);
+		result = ima_parse_rule(ns, rule, &ns->arch_policy_entry[i]);
 		if (result) {
 			pr_warn("Skipping unknown architecture policy rule: %s\n",
 				rule);
-			memset(&arch_policy_entry[i], 0,
-			       sizeof(*arch_policy_entry));
+			memset(&ns->arch_policy_entry[i], 0,
+			       sizeof(ns->arch_policy_entry[i]));
 			continue;
 		}
 		i++;
@@ -926,7 +924,7 @@ void __init ima_init_policy(struct ima_namespace *ns)
 	if (!arch_entries)
 		pr_info("No architecture policies found\n");
 	else
-		add_rules(ns, arch_policy_entry, arch_entries,
+		add_rules(ns, ns->arch_policy_entry, arch_entries,
 			  IMA_DEFAULT_POLICY | IMA_CUSTOM_POLICY);
 
 	/*
@@ -1006,7 +1004,8 @@ void ima_update_policy(struct ima_namespace *ns)
 		 * on boot.  After loading a custom policy, free the
 		 * architecture specific rules stored as an array.
 		 */
-		kfree(arch_policy_entry);
+		kfree(ns->arch_policy_entry);
+		ns->arch_policy_entry = NULL;
 	}
 	ima_update_policy_flags(ns);
 
-- 
2.31.1


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

* [PATCH v10 07/27] ima: Move ima_htable into ima_namespace
  2022-02-01 20:37 [PATCH v10 00/27] ima: Namespace IMA with audit support in IMA-ns Stefan Berger
                   ` (5 preceding siblings ...)
  2022-02-01 20:37 ` [PATCH v10 06/27] ima: Move arch_policy_entry into ima_namespace Stefan Berger
@ 2022-02-01 20:37 ` Stefan Berger
  2022-02-16 14:41   ` Mimi Zohar
  2022-02-01 20:37 ` [PATCH v10 08/27] ima: Move measurement list related variables " Stefan Berger
                   ` (20 subsequent siblings)
  27 siblings, 1 reply; 86+ messages in thread
From: Stefan Berger @ 2022-02-01 20:37 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, Christian Brauner

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>
Acked-by: Christian Brauner <brauner@kernel.org>
---
 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       | 36 ++++++++++++------------
 security/integrity/ima/ima_template.c    |  5 ++--
 9 files changed, 76 insertions(+), 52 deletions(-)

diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h
index a4669b55c2e0..340a59174670 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 {
 	/* policy rules */
 	struct list_head ima_default_rules;
@@ -128,6 +134,8 @@ struct ima_namespace {
 
 	/* An array of architecture specific rules */
 	struct ima_rule_entry *arch_policy_entry;
+
+	struct ima_h_table ima_htable;
 } __randomize_layout;
 extern struct ima_namespace init_ima_ns;
 
@@ -149,7 +157,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);
@@ -158,7 +167,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);
@@ -171,8 +181,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);
@@ -186,13 +198,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 */
@@ -277,7 +282,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,
@@ -293,7 +299,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 f7ad93a56982..dca7fe32d65e 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 ae33621c3955..1945fa8cfc4d 100644
--- a/security/integrity/ima/ima_init_ima_ns.c
+++ b/security/integrity/ima/ima_init_ima_ns.c
@@ -17,6 +17,10 @@ static int ima_init_namespace(struct ima_namespace *ns)
 	ns->ima_policy_flag = 0;
 	ns->arch_policy_entry = NULL;
 
+	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 4940f8dda580..2cd5cc90ab79 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)) {
@@ -970,7 +971,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..43961d5cd2ef 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,10 +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) {
@@ -156,7 +154,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 +168,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 +200,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] 86+ messages in thread

* [PATCH v10 08/27] ima: Move measurement list related variables into ima_namespace
  2022-02-01 20:37 [PATCH v10 00/27] ima: Namespace IMA with audit support in IMA-ns Stefan Berger
                   ` (6 preceding siblings ...)
  2022-02-01 20:37 ` [PATCH v10 07/27] ima: Move ima_htable " Stefan Berger
@ 2022-02-01 20:37 ` Stefan Berger
  2022-02-17 14:46   ` Mimi Zohar
  2022-02-01 20:37 ` [PATCH v10 09/27] ima: Move some IMA policy and filesystem " Stefan Berger
                   ` (19 subsequent siblings)
  27 siblings, 1 reply; 86+ messages in thread
From: Stefan Berger @ 2022-02-01 20:37 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

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       | 27 +++++++++++-------------
 5 files changed, 31 insertions(+), 24 deletions(-)

diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h
index 340a59174670..45706836a77b 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 {
@@ -136,6 +135,8 @@ struct ima_namespace {
 	struct ima_rule_entry *arch_policy_entry;
 
 	struct ima_h_table ima_htable;
+	struct list_head ima_measurements;	/* list of all measurements */
+	unsigned long binary_runtime_size;	/* used by init_ima_ns */
 } __randomize_layout;
 extern struct ima_namespace init_ima_ns;
 
@@ -186,7 +187,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 dca7fe32d65e..5ef0e2b2cf64 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 1945fa8cfc4d..a7477072c587 100644
--- a/security/integrity/ima/ima_init_ima_ns.c
+++ b/security/integrity/ima/ima_init_ima_ns.c
@@ -20,6 +20,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..e83b18492f46 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 43961d5cd2ef..0355c2b0932c 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) {
@@ -110,12 +103,14 @@ static int ima_add_digest_entry(struct ima_namespace *ns,
 		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;
 }
@@ -123,14 +118,16 @@ static int ima_add_digest_entry(struct ima_namespace *ns,
 /*
  * Return the amount of memory required for serializing the
  * entire binary_runtime_measurement list, including the ima_kexec_hdr
- * structure.
+ * structure. Carrying the measurement list across kexec is limited
+ * to init_ima_ns.
  */
-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] 86+ messages in thread

* [PATCH v10 09/27] ima: Move some IMA policy and filesystem related variables into ima_namespace
  2022-02-01 20:37 [PATCH v10 00/27] ima: Namespace IMA with audit support in IMA-ns Stefan Berger
                   ` (7 preceding siblings ...)
  2022-02-01 20:37 ` [PATCH v10 08/27] ima: Move measurement list related variables " Stefan Berger
@ 2022-02-01 20:37 ` Stefan Berger
  2022-02-17 14:44   ` Mimi Zohar
  2022-02-01 20:37 ` [PATCH v10 10/27] ima: Move IMA securityfs files into ima_namespace or onto stack Stefan Berger
                   ` (18 subsequent siblings)
  27 siblings, 1 reply; 86+ messages in thread
From: Stefan Berger @ 2022-02-01 20:37 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, Christian Brauner

Move the variables ima_write_mutex, ima_fs_flag, and valid_policy, which
are related to updating the IMA policy, into the ima_namespace. This way
each IMA namespace can set these variables independently in its instance
of securityfs.

Signed-off-by: Stefan Berger <stefanb@linux.ibm.com>
Acked-by: Christian Brauner <brauner@kernel.org>
---
 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 45706836a77b..1092c926daf9 100644
--- a/security/integrity/ima/ima.h
+++ b/security/integrity/ima/ima.h
@@ -137,6 +137,11 @@ struct ima_namespace {
 	struct ima_h_table ima_htable;
 	struct list_head ima_measurements;	/* list of all measurements */
 	unsigned long binary_runtime_size;	/* used by init_ima_ns */
+
+	/* securityfs support related variables */
+	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 5ef0e2b2cf64..4cf786f0bba8 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 a7477072c587..425eed1c6838 100644
--- a/security/integrity/ima/ima_init_ima_ns.c
+++ b/security/integrity/ima/ima_init_ima_ns.c
@@ -26,6 +26,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] 86+ messages in thread

* [PATCH v10 10/27] ima: Move IMA securityfs files into ima_namespace or onto stack
  2022-02-01 20:37 [PATCH v10 00/27] ima: Namespace IMA with audit support in IMA-ns Stefan Berger
                   ` (8 preceding siblings ...)
  2022-02-01 20:37 ` [PATCH v10 09/27] ima: Move some IMA policy and filesystem " Stefan Berger
@ 2022-02-01 20:37 ` Stefan Berger
  2022-02-17 14:44   ` Mimi Zohar
  2022-02-01 20:37 ` [PATCH v10 11/27] ima: Move ima_lsm_policy_notifier into ima_namespace Stefan Berger
                   ` (17 subsequent siblings)
  27 siblings, 1 reply; 86+ messages in thread
From: Stefan Berger @ 2022-02-01 20:37 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, Christian Brauner

Earlier we simplified how dentry creation and deletion is manged in
securityfs. This allows us to move IMA securityfs files from global
variables directly into ima_fs_ns_init() itself. We can now rely on
those dentries to be cleaned up when the securityfs instance is cleaned
when the last reference to it is dropped.

Things are slightly different for the initial IMA namespace. In contrast
to non-initial IMA namespaces it has pinning logic binding the lifetime
of the securityfs superblock to created dentries. We need to keep this
behavior to not regress userspace. Since IMA never removes most of the
securityfs files the initial securityfs instance stays pinned. This also
means even for the initial IMA namespace we don't need to keep
references to these dentries anywhere.

The ima_policy file is the exception since IMA can end up removing it
on systems that don't allow reading or extending the IMA custom policy.

Signed-off-by: Stefan Berger <stefanb@linux.ibm.com>
Acked-by: Christian Brauner <brauner@kernel.org>

---

v9:
 - Revert renaming of ima_policy to policy_dentry
---
 security/integrity/ima/ima.h    |  2 ++
 security/integrity/ima/ima_fs.c | 37 ++++++++++++++++++---------------
 2 files changed, 22 insertions(+), 17 deletions(-)

diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h
index 1092c926daf9..94c6e3a4d666 100644
--- a/security/integrity/ima/ima.h
+++ b/security/integrity/ima/ima.h
@@ -142,6 +142,8 @@ struct ima_namespace {
 	struct mutex ima_write_mutex;
 	unsigned long ima_fs_flags;
 	int valid_policy;
+
+	struct dentry *ima_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 4cf786f0bba8..89d3113ceda1 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->ima_policy);
+	ns->ima_policy = 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,14 @@ 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;
 	int ret;
 
 	ima_dir = securityfs_create_dir("ima", integrity_dir);
@@ -504,17 +502,17 @@ int __init ima_fs_init(void)
 		goto out;
 	}
 
-	ima_policy = securityfs_create_file("policy", POLICY_FILE_FLAGS,
-					    ima_dir, NULL,
-					    &ima_measure_policy_ops);
-	if (IS_ERR(ima_policy)) {
-		ret = PTR_ERR(ima_policy);
+	ns->ima_policy = securityfs_create_file("policy", POLICY_FILE_FLAGS,
+						ima_dir, NULL,
+						&ima_measure_policy_ops);
+	if (IS_ERR(ns->ima_policy)) {
+		ret = PTR_ERR(ns->ima_policy);
 		goto out;
 	}
 
 	return 0;
 out:
-	securityfs_remove(ima_policy);
+	securityfs_remove(ns->ima_policy);
 	securityfs_remove(violations);
 	securityfs_remove(runtime_measurements_count);
 	securityfs_remove(ascii_runtime_measurements);
@@ -524,3 +522,8 @@ int __init ima_fs_init(void)
 
 	return ret;
 }
+
+int __init ima_fs_init(void)
+{
+	return ima_fs_ns_init(&init_ima_ns);
+}
-- 
2.31.1


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

* [PATCH v10 11/27] ima: Move ima_lsm_policy_notifier into ima_namespace
  2022-02-01 20:37 [PATCH v10 00/27] ima: Namespace IMA with audit support in IMA-ns Stefan Berger
                   ` (9 preceding siblings ...)
  2022-02-01 20:37 ` [PATCH v10 10/27] ima: Move IMA securityfs files into ima_namespace or onto stack Stefan Berger
@ 2022-02-01 20:37 ` Stefan Berger
  2022-02-17 20:30   ` Mimi Zohar
  2022-02-01 20:37 ` [PATCH v10 12/27] ima: Define mac_admin_ns_capable() as a wrapper for ns_capable() Stefan Berger
                   ` (16 subsequent siblings)
  27 siblings, 1 reply; 86+ messages in thread
From: Stefan Berger @ 2022-02-01 20:37 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

Move the ima_lsm_policy_notifier into the ima_namespace. Each IMA
namespace can now register its own LSM policy change notifier callback.
The policy change notifier for the init_ima_ns still remains in init_ima()
and therefore handle the registration of the callback for all other
namespaces in init_ima_namespace().

Suppress the kernel warning 'rule for LSM <label> is undefined` for all
other IMA namespaces than the init_ima_ns.

Signed-off-by: Stefan Berger <stefanb@linux.ibm.com>

---
v10:
 - Only call pr_warn('rule for LSM <label> is undefined`) for init_ima_ns
---
 security/integrity/ima/ima.h             |  2 ++
 security/integrity/ima/ima_init_ima_ns.c | 14 ++++++++++++++
 security/integrity/ima/ima_main.c        |  6 +-----
 security/integrity/ima/ima_policy.c      | 16 ++++++++++------
 4 files changed, 27 insertions(+), 11 deletions(-)

diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h
index 94c6e3a4d666..fb6bd054d899 100644
--- a/security/integrity/ima/ima.h
+++ b/security/integrity/ima/ima.h
@@ -144,6 +144,8 @@ struct ima_namespace {
 	int valid_policy;
 
 	struct dentry *ima_policy;
+
+	struct notifier_block ima_lsm_policy_notifier;
 } __randomize_layout;
 extern struct ima_namespace init_ima_ns;
 
diff --git a/security/integrity/ima/ima_init_ima_ns.c b/security/integrity/ima/ima_init_ima_ns.c
index 425eed1c6838..1cba545ae477 100644
--- a/security/integrity/ima/ima_init_ima_ns.c
+++ b/security/integrity/ima/ima_init_ima_ns.c
@@ -10,6 +10,8 @@
 
 static int ima_init_namespace(struct ima_namespace *ns)
 {
+	int rc;
+
 	INIT_LIST_HEAD(&ns->ima_default_rules);
 	INIT_LIST_HEAD(&ns->ima_policy_rules);
 	INIT_LIST_HEAD(&ns->ima_temp_rules);
@@ -30,6 +32,15 @@ static int ima_init_namespace(struct ima_namespace *ns)
 	ns->valid_policy = 1;
 	ns->ima_fs_flags = 0;
 
+	if (ns != &init_ima_ns) {
+		ns->ima_lsm_policy_notifier.notifier_call =
+						ima_lsm_policy_change;
+		rc = register_blocking_lsm_notifier
+						(&ns->ima_lsm_policy_notifier);
+		if (rc)
+			return rc;
+	}
+
 	return 0;
 }
 
@@ -39,5 +50,8 @@ int __init ima_ns_init(void)
 }
 
 struct ima_namespace init_ima_ns = {
+	.ima_lsm_policy_notifier = {
+		.notifier_call = ima_lsm_policy_change,
+	},
 };
 EXPORT_SYMBOL(init_ima_ns);
diff --git a/security/integrity/ima/ima_main.c b/security/integrity/ima/ima_main.c
index 2cd5cc90ab79..ae0e9b14554a 100644
--- a/security/integrity/ima/ima_main.c
+++ b/security/integrity/ima/ima_main.c
@@ -38,10 +38,6 @@ int ima_appraise;
 int __ro_after_init ima_hash_algo = HASH_ALGO_SHA1;
 static int hash_setup_done;
 
-static struct notifier_block ima_lsm_policy_notifier = {
-	.notifier_call = ima_lsm_policy_change,
-};
-
 static int __init hash_setup(char *str)
 {
 	struct ima_template_desc *template_desc = ima_template_desc_current();
@@ -1072,7 +1068,7 @@ static int __init init_ima(void)
 	if (error)
 		return error;
 
-	error = register_blocking_lsm_notifier(&ima_lsm_policy_notifier);
+	error = register_blocking_lsm_notifier(&ns->ima_lsm_policy_notifier);
 	if (error)
 		pr_warn("Couldn't register LSM notifier, error %d\n", error);
 
diff --git a/security/integrity/ima/ima_policy.c b/security/integrity/ima/ima_policy.c
index 05b2bc06ab0c..148ff5a98a8e 100644
--- a/security/integrity/ima/ima_policy.c
+++ b/security/integrity/ima/ima_policy.c
@@ -369,7 +369,8 @@ static void ima_free_rule(struct ima_rule_entry *entry)
 	kfree(entry);
 }
 
-static struct ima_rule_entry *ima_lsm_copy_rule(struct ima_rule_entry *entry)
+static struct ima_rule_entry *ima_lsm_copy_rule(struct ima_namespace *ns,
+						struct ima_rule_entry *entry)
 {
 	struct ima_rule_entry *nentry;
 	int i;
@@ -400,18 +401,19 @@ static struct ima_rule_entry *ima_lsm_copy_rule(struct ima_rule_entry *entry)
 		ima_filter_rule_init(nentry->lsm[i].type, Audit_equal,
 				     nentry->lsm[i].args_p,
 				     &nentry->lsm[i].rule);
-		if (!nentry->lsm[i].rule)
+		if (!nentry->lsm[i].rule && ns == &init_ima_ns)
 			pr_warn("rule for LSM \'%s\' is undefined\n",
 				nentry->lsm[i].args_p);
 	}
 	return nentry;
 }
 
-static int ima_lsm_update_rule(struct ima_rule_entry *entry)
+static int ima_lsm_update_rule(struct ima_namespace *ns,
+			       struct ima_rule_entry *entry)
 {
 	struct ima_rule_entry *nentry;
 
-	nentry = ima_lsm_copy_rule(entry);
+	nentry = ima_lsm_copy_rule(ns, entry);
 	if (!nentry)
 		return -ENOMEM;
 
@@ -454,7 +456,7 @@ static void ima_lsm_update_rules(struct ima_namespace *ns)
 		if (!ima_rule_contains_lsm_cond(entry))
 			continue;
 
-		result = ima_lsm_update_rule(entry);
+		result = ima_lsm_update_rule(ns, entry);
 		if (result) {
 			pr_err("lsm rule update error %d\n", result);
 			return;
@@ -465,12 +467,14 @@ static void ima_lsm_update_rules(struct ima_namespace *ns)
 int ima_lsm_policy_change(struct notifier_block *nb, unsigned long event,
 			  void *lsm_data)
 {
-	struct ima_namespace *ns = &init_ima_ns;
+	struct ima_namespace *ns;
 
 	if (event != LSM_POLICY_CHANGE)
 		return NOTIFY_DONE;
 
+	ns = container_of(nb, struct ima_namespace, ima_lsm_policy_notifier);
 	ima_lsm_update_rules(ns);
+
 	return NOTIFY_OK;
 }
 
-- 
2.31.1


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

* [PATCH v10 12/27] ima: Define mac_admin_ns_capable() as a wrapper for ns_capable()
  2022-02-01 20:37 [PATCH v10 00/27] ima: Namespace IMA with audit support in IMA-ns Stefan Berger
                   ` (10 preceding siblings ...)
  2022-02-01 20:37 ` [PATCH v10 11/27] ima: Move ima_lsm_policy_notifier into ima_namespace Stefan Berger
@ 2022-02-01 20:37 ` Stefan Berger
  2022-02-05  5:58   ` Serge E. Hallyn
  2022-02-01 20:37 ` [PATCH v10 13/27] ima: Only accept AUDIT rules for non-init_ima_ns namespaces for now Stefan Berger
                   ` (15 subsequent siblings)
  27 siblings, 1 reply; 86+ messages in thread
From: Stefan Berger @ 2022-02-01 20:37 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

Define mac_admin_ns_capable() as a wrapper for the combined ns_capable()
checks on CAP_MAC_ADMIN and CAP_SYS_ADMIN in a user namespace. Return
true on the check if either capability or both are available.

Use mac_admin_ns_capable() in place of capable(SYS_ADMIN). This will allow
an IMA namespace to read the policy with only CAP_MAC_ADMIN, which has
less privileges than CAP_SYS_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 | 5 ++++-
 3 files changed, 16 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 fb6bd054d899..0057b1fd6c18 100644
--- a/security/integrity/ima/ima.h
+++ b/security/integrity/ima/ima.h
@@ -487,4 +487,10 @@ 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;
+}
+
 #endif /* __LINUX_IMA_H */
diff --git a/security/integrity/ima/ima_fs.c b/security/integrity/ima/ima_fs.c
index 89d3113ceda1..c41aa61b7393 100644
--- a/security/integrity/ima/ima_fs.c
+++ b/security/integrity/ima/ima_fs.c
@@ -377,6 +377,9 @@ static const struct seq_operations ima_policy_seqops = {
  */
 static int ima_open_policy(struct inode *inode, struct file *filp)
 {
+#ifdef CONFIG_IMA_READ_POLICY
+	struct user_namespace *user_ns = ima_user_ns_from_file(filp);
+#endif
 	struct ima_namespace *ns = &init_ima_ns;
 
 	if (!(filp->f_flags & O_WRONLY)) {
@@ -385,7 +388,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] 86+ messages in thread

* [PATCH v10 13/27] ima: Only accept AUDIT rules for non-init_ima_ns namespaces for now
  2022-02-01 20:37 [PATCH v10 00/27] ima: Namespace IMA with audit support in IMA-ns Stefan Berger
                   ` (11 preceding siblings ...)
  2022-02-01 20:37 ` [PATCH v10 12/27] ima: Define mac_admin_ns_capable() as a wrapper for ns_capable() Stefan Berger
@ 2022-02-01 20:37 ` Stefan Berger
  2022-02-17 21:32   ` Mimi Zohar
  2022-02-01 20:37 ` [PATCH v10 14/27] userns: Add pointer to ima_namespace to user_namespace Stefan Berger
                   ` (14 subsequent siblings)
  27 siblings, 1 reply; 86+ messages in thread
From: Stefan Berger @ 2022-02-01 20:37 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, Christian Brauner

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>
Acked-by: Christian Brauner <brauner@kernel.org>

---
v9:
  - Jump to err_audit when unsupported rules are detected
---
 security/integrity/ima/ima_policy.c | 12 ++++++++++++
 1 file changed, 12 insertions(+)

diff --git a/security/integrity/ima/ima_policy.c b/security/integrity/ima/ima_policy.c
index 148ff5a98a8e..2dcc5a8c585a 100644
--- a/security/integrity/ima/ima_policy.c
+++ b/security/integrity/ima/ima_policy.c
@@ -1796,6 +1796,17 @@ static int ima_parse_rule(struct ima_namespace *ns,
 			result = -EINVAL;
 			break;
 		}
+
+		/* IMA namespace only accepts AUDIT rules */
+		if (ns != &init_ima_ns && result == 0) {
+			switch (entry->action) {
+			case MEASURE:
+			case APPRAISE:
+			case HASH:
+				result = -EINVAL;
+				goto err_audit;
+			}
+		}
 	}
 	if (!result && !ima_validate_rule(entry))
 		result = -EINVAL;
@@ -1808,6 +1819,7 @@ static int ima_parse_rule(struct ima_namespace *ns,
 		check_template_modsig(template_desc);
 	}
 
+err_audit:
 	audit_log_format(ab, "res=%d", !result);
 	audit_log_end(ab);
 	return result;
-- 
2.31.1


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

* [PATCH v10 14/27] userns: Add pointer to ima_namespace to user_namespace
  2022-02-01 20:37 [PATCH v10 00/27] ima: Namespace IMA with audit support in IMA-ns Stefan Berger
                   ` (12 preceding siblings ...)
  2022-02-01 20:37 ` [PATCH v10 13/27] ima: Only accept AUDIT rules for non-init_ima_ns namespaces for now Stefan Berger
@ 2022-02-01 20:37 ` Stefan Berger
  2022-02-18 16:26   ` Mimi Zohar
  2022-02-01 20:37 ` [PATCH v10 15/27] ima: Implement hierarchical processing of file accesses Stefan Berger
                   ` (13 subsequent siblings)
  27 siblings, 1 reply; 86+ messages in thread
From: Stefan Berger @ 2022-02-01 20:37 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

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>

---
v9:
 - Deferred implementation of ima_ns_from_user_ns() to later patch
---
 include/linux/ima.h            | 2 ++
 include/linux/user_namespace.h | 4 ++++
 kernel/user.c                  | 4 ++++
 3 files changed, 10 insertions(+)

diff --git a/include/linux/ima.h b/include/linux/ima.h
index b6ab66a546ae..0cf2a80c8b35 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);
 
-- 
2.31.1


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

* [PATCH v10 15/27] ima: Implement hierarchical processing of file accesses
  2022-02-01 20:37 [PATCH v10 00/27] ima: Namespace IMA with audit support in IMA-ns Stefan Berger
                   ` (13 preceding siblings ...)
  2022-02-01 20:37 ` [PATCH v10 14/27] userns: Add pointer to ima_namespace to user_namespace Stefan Berger
@ 2022-02-01 20:37 ` Stefan Berger
  2022-02-02 13:19     ` kernel test robot
                     ` (2 more replies)
  2022-02-01 20:37 ` [PATCH v10 16/27] ima: Implement ima_free_policy_rules() for freeing of an ima_namespace Stefan Berger
                   ` (12 subsequent siblings)
  27 siblings, 3 replies; 86+ messages in thread
From: Stefan Berger @ 2022-02-01 20:37 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

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.

Pass the user_namespace into process_measurement since we will be walking
the hierarchy of user_namespaces towards the init_user_ns and we can easily
derive the ima_namespace from the user_namespace.

__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 still be NULL due to late initialization of the
IMA namespace.

Signed-off-by: Stefan Berger <stefanb@linux.ibm.com>

---

v10:
  - Fixed compilation issue

v9:
  - Switch callers to pass user_namespace rather than ima_namespace with
    potential NULL pointer
  - Add default case to switch statement and warn if this happens
  - Implement ima_ns_from_user_ns() in this patch now
---
 security/integrity/ima/ima.h      |  8 ++++
 security/integrity/ima/ima_main.c | 76 +++++++++++++++++++++++--------
 2 files changed, 65 insertions(+), 19 deletions(-)

diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h
index 0057b1fd6c18..aea8fb8d2854 100644
--- a/security/integrity/ima/ima.h
+++ b/security/integrity/ima/ima.h
@@ -493,4 +493,12 @@ 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;
+}
+
 #endif /* __LINUX_IMA_H */
diff --git a/security/integrity/ima/ima_main.c b/security/integrity/ima/ima_main.c
index ae0e9b14554a..917504319e7f 100644
--- a/security/integrity/ima/ima_main.c
+++ b/security/integrity/ima/ima_main.c
@@ -196,10 +196,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;
@@ -391,6 +391,41 @@ static int process_measurement(struct ima_namespace *ns,
 	return 0;
 }
 
+static int process_measurement(struct user_namespace *user_ns,
+			       struct file *file, const struct cred *cred,
+			       u32 secid, char *buf, loff_t size, int mask,
+			       enum ima_hooks func)
+{
+	struct ima_namespace *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 0:
+				break;
+			case -EACCES:
+				/* return this error at the end but continue */
+				ret = -EACCES;
+				break;
+			default:
+				/* should not happen */
+				ret = -EACCES;
+				WARN_ON_ONCE(true);
+			}
+		}
+
+		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)
@@ -404,13 +439,14 @@ static int process_measurement(struct ima_namespace *ns,
  */
 int ima_file_mmap(struct file *file, unsigned long prot)
 {
-	struct ima_namespace *ns = &init_ima_ns;
+	struct user_namespace *user_ns = current_user_ns();
 	u32 secid;
 
 	if (file && (prot & PROT_EXEC)) {
 		security_task_getsecid_subj(current, &secid);
-		return process_measurement(ns, file, current_cred(), secid,
-					   NULL, 0, MAY_EXEC, MMAP_CHECK);
+		return process_measurement(user_ns, file, current_cred(),
+					   secid, NULL, 0, MAY_EXEC,
+					   MMAP_CHECK);
 	}
 
 	return 0;
@@ -485,19 +521,19 @@ int ima_file_mprotect(struct vm_area_struct *vma, unsigned long prot)
  */
 int ima_bprm_check(struct linux_binprm *bprm)
 {
-	struct ima_namespace *ns = &init_ima_ns;
+	struct user_namespace *user_ns = current_user_ns();
 	int ret;
 	u32 secid;
 
 	security_task_getsecid_subj(current, &secid);
-	ret = process_measurement(ns, bprm->file, current_cred(), secid, NULL,
-				  0, MAY_EXEC, BPRM_CHECK);
+	ret = process_measurement(user_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(ns, bprm->file, bprm->cred, secid, NULL, 0,
-				   MAY_EXEC, CREDS_CHECK);
+	return process_measurement(user_ns, bprm->file, bprm->cred, secid,
+				   NULL, 0, MAY_EXEC, CREDS_CHECK);
 }
 
 /**
@@ -512,11 +548,12 @@ int ima_bprm_check(struct linux_binprm *bprm)
  */
 int ima_file_check(struct file *file, int mask)
 {
-	struct ima_namespace *ns = &init_ima_ns;
+	struct user_namespace *user_ns = current_user_ns();
 	u32 secid;
 
 	security_task_getsecid_subj(current, &secid);
-	return process_measurement(ns, file, current_cred(), secid, NULL, 0,
+	return process_measurement(user_ns, file, current_cred(), secid,
+				   NULL, 0,
 				   mask & (MAY_READ | MAY_WRITE | MAY_EXEC |
 					   MAY_APPEND), FILE_CHECK);
 }
@@ -698,7 +735,7 @@ void ima_post_path_mknod(struct user_namespace *mnt_userns,
 int ima_read_file(struct file *file, enum kernel_read_file_id read_id,
 		  bool contents)
 {
-	struct ima_namespace *ns = &init_ima_ns;
+	struct user_namespace *user_ns = current_user_ns();
 	enum ima_hooks func;
 	u32 secid;
 
@@ -721,7 +758,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(ns, file, current_cred(), secid, NULL,
+	return process_measurement(user_ns, file, current_cred(), secid, NULL,
 				   0, MAY_READ, func);
 }
 
@@ -749,7 +786,8 @@ const int read_idmap[READING_MAX_ID] = {
 int ima_post_read_file(struct file *file, void *buf, loff_t size,
 		       enum kernel_read_file_id read_id)
 {
-	struct ima_namespace *ns = &init_ima_ns;
+	struct user_namespace *user_ns = current_user_ns();
+	struct ima_namespace *ns = ima_ns_from_user_ns(user_ns);
 	enum ima_hooks func;
 	u32 secid;
 
@@ -766,8 +804,8 @@ 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(ns, file, current_cred(), secid, buf, size,
-				   MAY_READ, func);
+	return process_measurement(user_ns, file, current_cred(), secid,
+				   buf, size, MAY_READ, func);
 }
 
 /**
-- 
2.31.1


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

* [PATCH v10 16/27] ima: Implement ima_free_policy_rules() for freeing of an ima_namespace
  2022-02-01 20:37 [PATCH v10 00/27] ima: Namespace IMA with audit support in IMA-ns Stefan Berger
                   ` (14 preceding siblings ...)
  2022-02-01 20:37 ` [PATCH v10 15/27] ima: Implement hierarchical processing of file accesses Stefan Berger
@ 2022-02-01 20:37 ` Stefan Berger
  2022-02-18 17:09   ` Mimi Zohar
  2022-02-01 20:37 ` [PATCH v10 17/27] ima: Add functions for creating and " Stefan Berger
                   ` (11 subsequent siblings)
  27 siblings, 1 reply; 86+ messages in thread
From: Stefan Berger @ 2022-02-01 20:37 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

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

Signed-off-by: Stefan Berger <stefanb@linux.ibm.com>

---
v10:
  - Not calling ima_delete_rules() anymore
  - Move access check from ima_delete_rules into very last patch

 v9:
  - Only reset temp_ima_appraise when using init_ima_ns.
---
 security/integrity/ima/ima.h        |  1 +
 security/integrity/ima/ima_policy.c | 14 ++++++++++++++
 2 files changed, 15 insertions(+)

diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h
index aea8fb8d2854..8c757223d549 100644
--- a/security/integrity/ima/ima.h
+++ b/security/integrity/ima/ima.h
@@ -329,6 +329,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 2dcc5a8c585a..fe3dce8fb939 100644
--- a/security/integrity/ima/ima_policy.c
+++ b/security/integrity/ima/ima_policy.c
@@ -1889,6 +1889,20 @@ 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;
+
+	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] 86+ messages in thread

* [PATCH v10 17/27] ima: Add functions for creating and freeing of an ima_namespace
  2022-02-01 20:37 [PATCH v10 00/27] ima: Namespace IMA with audit support in IMA-ns Stefan Berger
                   ` (15 preceding siblings ...)
  2022-02-01 20:37 ` [PATCH v10 16/27] ima: Implement ima_free_policy_rules() for freeing of an ima_namespace Stefan Berger
@ 2022-02-01 20:37 ` Stefan Berger
  2022-02-18 19:49   ` Mimi Zohar
  2022-02-01 20:37 ` [PATCH v10 18/27] integrity/ima: Define ns_status for storing namespaced iint data Stefan Berger
                   ` (10 subsequent siblings)
  27 siblings, 1 reply; 86+ messages in thread
From: Stefan Berger @ 2022-02-01 20:37 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, Christian Brauner

Implement create_ima_ns() to create an empty ima_namespace. Defer its
initialization to a later point outside this function. Implement
free_ima_ns() to free it.

Signed-off-by: Stefan Berger <stefanb@linux.ibm.com>
Acked-by: Christian Brauner <brauner@kernel.org>

---
v9:
 - Set user_ns->ims_ns = NULL in free_ima_ns()
 - Refactored create_ima_ns() to defer initialization
 - Removed pr_debug functions
---
 include/linux/ima.h                      | 13 ++++++
 security/integrity/ima/Makefile          |  1 +
 security/integrity/ima/ima.h             | 15 +++++++
 security/integrity/ima/ima_init_ima_ns.c |  2 +-
 security/integrity/ima/ima_ns.c          | 53 ++++++++++++++++++++++++
 5 files changed, 83 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 0cf2a80c8b35..964a08702573 100644
--- a/include/linux/ima.h
+++ b/include/linux/ima.h
@@ -220,4 +220,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 8c757223d549..751e936a6311 100644
--- a/security/integrity/ima/ima.h
+++ b/security/integrity/ima/ima.h
@@ -167,6 +167,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,
@@ -502,4 +503,18 @@ static inline struct ima_namespace
 	return NULL;
 }
 
+#ifdef CONFIG_IMA_NS
+
+struct ima_namespace *create_ima_ns(void);
+
+#else
+
+static inline struct ima_namespace *create_ima_ns(void)
+{
+	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 1cba545ae477..166dab4a3126 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)
 {
 	int rc;
 
diff --git a/security/integrity/ima/ima_ns.c b/security/integrity/ima/ima_ns.c
new file mode 100644
index 000000000000..b3b81a1e313e
--- /dev/null
+++ b/security/integrity/ima/ima_ns.c
@@ -0,0 +1,53 @@
+// 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(void)
+{
+	struct ima_namespace *ns;
+
+	ns = kmem_cache_zalloc(imans_cachep, GFP_KERNEL);
+	if (!ns)
+		return ERR_PTR(-ENOMEM);
+
+	return ns;
+}
+
+/* destroy_ima_ns() must only be called after ima_init_namespace() was called */
+static void destroy_ima_ns(struct ima_namespace *ns)
+{
+	unregister_blocking_lsm_notifier(&ns->ima_lsm_policy_notifier);
+	kfree(ns->arch_policy_entry);
+	ima_free_policy_rules(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);
+
+	kmem_cache_free(imans_cachep, ns);
+
+	user_ns->ima_ns = NULL;
+}
+
+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] 86+ messages in thread

* [PATCH v10 18/27] integrity/ima: Define ns_status for storing namespaced iint data
  2022-02-01 20:37 [PATCH v10 00/27] ima: Namespace IMA with audit support in IMA-ns Stefan Berger
                   ` (16 preceding siblings ...)
  2022-02-01 20:37 ` [PATCH v10 17/27] ima: Add functions for creating and " Stefan Berger
@ 2022-02-01 20:37 ` Stefan Berger
  2022-02-23 16:12   ` Mimi Zohar
  2022-02-01 20:37 ` [PATCH v10 19/27] integrity: Add optional callback function to integrity_inode_free() Stefan Berger
                   ` (9 subsequent siblings)
  27 siblings, 1 reply; 86+ messages in thread
From: Stefan Berger @ 2022-02-01 20:37 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.

To simplify the code in the non-namespaces case embed an ns_status in
the integrity_iint_cache and have it linked into the iint's ns_status list
when calling ima_get_ns_status().

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 concurrently
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>

---
v9:
 - Move ns_status into integrity.h and embedded it into integrity_iint_cache
   for the non-CONFIG_IMA_NS case
---
 include/linux/ima.h                      |   7 +
 security/integrity/iint.c                |   7 +
 security/integrity/ima/Makefile          |   2 +-
 security/integrity/ima/ima.h             |  17 ++
 security/integrity/ima/ima_init_ima_ns.c |   5 +
 security/integrity/ima/ima_ns.c          |   1 +
 security/integrity/ima/ima_ns_status.c   | 344 +++++++++++++++++++++++
 security/integrity/integrity.h           |  35 ++-
 8 files changed, 416 insertions(+), 2 deletions(-)
 create mode 100644 security/integrity/ima/ima_ns_status.c

diff --git a/include/linux/ima.h b/include/linux/ima.h
index 964a08702573..2fe32f1bf84b 100644
--- a/include/linux/ima.h
+++ b/include/linux/ima.h
@@ -225,12 +225,19 @@ static inline bool ima_appraise_signature(enum kernel_read_file_id func)
 
 void free_ima_ns(struct user_namespace *ns);
 
+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)
 {
 }
 
+static inline void ima_free_ns_status_list(struct list_head *head,
+					   rwlock_t *ns_list_lock)
+{
+}
+
 #endif /* CONFIG_IMA_NS */
 
 #endif /* _LINUX_IMA_H */
diff --git a/security/integrity/iint.c b/security/integrity/iint.c
index 8638976f7990..371cbceaf479 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,8 @@ static void iint_free(struct integrity_iint_cache *iint)
 	iint->ima_creds_status = INTEGRITY_UNKNOWN;
 	iint->evm_status = INTEGRITY_UNKNOWN;
 	iint->measured_pcrs = 0;
+	rwlock_init(&iint->ns_list_lock);
+	INIT_LIST_HEAD(&iint->ns_list);
 	kmem_cache_free(iint_cache, iint);
 }
 
@@ -155,6 +158,8 @@ void integrity_inode_free(struct inode *inode)
 	rb_erase(&iint->rb_node, &integrity_iint_tree);
 	write_unlock(&integrity_iint_lock);
 
+	ima_free_ns_status_list(&iint->ns_list, &iint->ns_list_lock);
+
 	iint_free(iint);
 }
 
@@ -170,6 +175,8 @@ static void init_once(void *foo)
 	iint->ima_creds_status = INTEGRITY_UNKNOWN;
 	iint->evm_status = INTEGRITY_UNKNOWN;
 	mutex_init(&iint->mutex);
+	rwlock_init(&iint->ns_list_lock);
+	INIT_LIST_HEAD(&iint->ns_list);
 }
 
 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 751e936a6311..4af8f2c4df40 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;
+
 	/* policy rules */
 	struct list_head ima_default_rules;
 	struct list_head ima_policy_rules;
@@ -507,6 +511,12 @@ static inline struct ima_namespace
 
 struct ima_namespace *create_ima_ns(void);
 
+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 *create_ima_ns(void)
@@ -515,6 +525,13 @@ static inline struct ima_namespace *create_ima_ns(void)
 	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 166dab4a3126..d4ddfd1de60b 100644
--- a/security/integrity/ima/ima_init_ima_ns.c
+++ b/security/integrity/ima/ima_init_ima_ns.c
@@ -12,6 +12,11 @@ int ima_init_namespace(struct ima_namespace *ns)
 {
 	int rc;
 
+	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 b3b81a1e313e..29af6fea2d74 100644
--- a/security/integrity/ima/ima_ns.c
+++ b/security/integrity/ima/ima_ns.c
@@ -29,6 +29,7 @@ static void destroy_ima_ns(struct ima_namespace *ns)
 	unregister_blocking_lsm_notifier(&ns->ima_lsm_policy_notifier);
 	kfree(ns->arch_policy_entry);
 	ima_free_policy_rules(ns);
+	ima_free_ns_status_tree(ns);
 }
 
 void free_ima_ns(struct user_namespace *user_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..9c753caad6ac
--- /dev/null
+++ b/security/integrity/ima/ima_ns_status.c
@@ -0,0 +1,344 @@
+// 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 *ns_status)
+{
+	pr_debug("FREE ns_status: %p\n", ns_status);
+
+	kmem_cache_free(ns->ns_status_cache, ns_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 *ns_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(ns_status, next,
+						     &ns->ns_status_tree,
+						     rb_node) {
+			write_lock(&ns_status->iint->ns_list_lock);
+			if (!list_empty(&ns_status->ns_next)) {
+				list_del_init(&ns_status->ns_next);
+				llist_add(&ns_status->gc_llist, &garbage);
+				ctr++;
+			}
+			write_unlock(&ns_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(ns_status, next, node, gc_llist)
+		ns_status_free(ns, 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 *ns_status;
+
+	lock_group(GRP_NS_STATUS_LIST);
+
+	while (1) {
+		write_lock(ns_list_lock);
+		ns_status = list_first_entry_or_null(head, struct ns_status,
+						     ns_next);
+		if (ns_status)
+			list_del_init(&ns_status->ns_next);
+		write_unlock(ns_list_lock);
+
+		if (!ns_status)
+			break;
+
+		write_lock(&ns_status->ns->ns_tree_lock);
+
+		rb_erase(&ns_status->rb_node, &ns_status->ns->ns_status_tree);
+		RB_CLEAR_NODE(&ns_status->rb_node);
+
+		write_unlock(&ns_status->ns->ns_tree_lock);
+
+		ns_status_free(ns_status->ns, 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 *ns_status;
+	struct rb_node *n = ns->ns_status_tree.rb_node;
+
+	while (n) {
+		ns_status = rb_entry(n, struct ns_status, rb_node);
+
+		if (inode < ns_status->inode)
+			n = n->rb_left;
+		else if (inode > ns_status->inode)
+			n = n->rb_right;
+		else
+			break;
+	}
+	if (!n)
+		return NULL;
+
+	return ns_status;
+}
+
+static void insert_ns_status(struct ima_namespace *ns, struct inode *inode,
+			     struct ns_status *ns_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 = &ns_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 *ns_status)
+{
+	write_lock(&ns_status->iint->ns_list_lock);
+	if (!list_empty(&ns_status->ns_next))
+		list_del_init(&ns_status->ns_next);
+	write_unlock(&ns_status->iint->ns_list_lock);
+
+	rb_erase(&ns_status->rb_node, &ns->ns_status_tree);
+	RB_CLEAR_NODE(&ns_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 *ns_status;
+	bool get_new = true;
+
+	/*
+	 * 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);
+
+	ns_status = ns_status_find(ns, inode);
+	if (ns_status) {
+		if (unlikely(ns_status->iint != iint)) {
+			/* Same inode but stale iint: free it and get new */
+			ns_status_unlink(ns, ns_status);
+			ns_status_free(ns, ns_status);
+		} else if (inode->i_ino == ns_status->i_ino &&
+			   inode->i_generation == ns_status->i_generation) {
+			goto unlock;
+		} else {
+			/* Reuse of ns_status is possible but need reset */
+			ns_status_reset(ns_status);
+			get_new = false;
+		}
+	}
+
+	if (get_new) {
+		ns_status = kmem_cache_alloc(ns->ns_status_cache, GFP_NOFS);
+		if (!ns_status) {
+			ns_status = ERR_PTR(-ENOMEM);
+			goto unlock;
+		}
+
+		pr_debug("NEW  ns_status: %p\n", ns_status);
+
+		ns_status_init(ns_status);
+		insert_ns_status(ns, inode, ns_status);
+	}
+
+	ns_status->iint = iint;
+	ns_status->inode = inode;
+	ns_status->ns = ns;
+	ns_status->i_ino = inode->i_ino;
+	ns_status->i_generation = inode->i_generation;
+
+	/* make visible on list */
+	write_lock(&iint->ns_list_lock);
+	if (list_empty(&ns_status->ns_next))
+		list_add_tail(&ns_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 ns_status;
+}
diff --git a/security/integrity/integrity.h b/security/integrity/integrity.h
index 547425c20e11..b7d5ca108900 100644
--- a/security/integrity/integrity.h
+++ b/security/integrity/integrity.h
@@ -122,13 +122,39 @@ struct signature_v2_hdr {
 	uint8_t sig[];		/* signature payload */
 } __packed;
 
+/* integrity status of an inode in a namespace */
+struct ns_status {
+	struct list_head ns_next;
+	unsigned long flags;		/* flags split with iint */
+#ifdef CONFIG_IMA_NS
+	struct rb_node rb_node;
+	struct integrity_iint_cache *iint;
+	struct inode *inode;
+	struct ima_namespace *ns;
+	ino_t i_ino;
+	u32 i_generation;
+	struct llist_node gc_llist;	/* used while freeing */
+#endif
+};
+
+static inline void ns_status_reset(struct ns_status *ns_status)
+{
+	ns_status->flags = 0;
+}
+
+static inline void ns_status_init(struct ns_status *ns_status)
+{
+	INIT_LIST_HEAD(&ns_status->ns_next);
+	ns_status_reset(ns_status);
+}
+
 /* 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 flags;	/* flags split with ns_status */
 	unsigned long measured_pcrs;
 	unsigned long atomic_flags;
 	enum integrity_status ima_file_status:4;
@@ -138,6 +164,13 @@ struct integrity_iint_cache {
 	enum integrity_status ima_creds_status:4;
 	enum integrity_status evm_status:4;
 	struct ima_digest_data *ima_hash;
+
+	/*
+	 * Lock and list of ns_status for files shared by different
+	 * namespaces
+	 */
+	rwlock_t ns_list_lock;
+	struct list_head ns_list;
 };
 
 /* rbtree tree calls to lookup, insert, delete
-- 
2.31.1


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

* [PATCH v10 19/27] integrity: Add optional callback function to integrity_inode_free()
  2022-02-01 20:37 [PATCH v10 00/27] ima: Namespace IMA with audit support in IMA-ns Stefan Berger
                   ` (17 preceding siblings ...)
  2022-02-01 20:37 ` [PATCH v10 18/27] integrity/ima: Define ns_status for storing namespaced iint data Stefan Berger
@ 2022-02-01 20:37 ` Stefan Berger
  2022-02-01 20:37 ` [PATCH v10 20/27] ima: Namespace audit status flags Stefan Berger
                   ` (8 subsequent siblings)
  27 siblings, 0 replies; 86+ messages in thread
From: Stefan Berger @ 2022-02-01 20:37 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

Add an optional callback function to integrity_inode_free() that,
if provided, must be called and determines whether the iint can be
freed. Adjust all callers of this function to provide a NULL pointer
since none of the existing callers needs this check.

Signed-off-by: Stefan Berger <stefanb@linux.ibm.com>
---
 include/linux/integrity.h |  8 ++++++--
 security/integrity/iint.c | 15 +++++++++++++--
 security/security.c       |  2 +-
 3 files changed, 20 insertions(+), 5 deletions(-)

diff --git a/include/linux/integrity.h b/include/linux/integrity.h
index 2ea0f2f65ab6..9fe826b9146e 100644
--- a/include/linux/integrity.h
+++ b/include/linux/integrity.h
@@ -19,10 +19,13 @@ enum integrity_status {
 	INTEGRITY_UNKNOWN,
 };
 
+struct integrity_iint_cache;
+typedef bool (*iint_removable_cb)(struct integrity_iint_cache *iint);
+
 /* List of EVM protected security xattrs */
 #ifdef CONFIG_INTEGRITY
 extern struct integrity_iint_cache *integrity_inode_get(struct inode *inode);
-extern void integrity_inode_free(struct inode *inode);
+extern void integrity_inode_free(struct inode *inode, iint_removable_cb check);
 extern void __init integrity_load_keys(void);
 
 #else
@@ -32,7 +35,8 @@ static inline struct integrity_iint_cache *
 	return NULL;
 }
 
-static inline void integrity_inode_free(struct inode *inode)
+static inline void integrity_inode_free(struct inode *inode,
+					iint_removable_cb check)
 {
 	return;
 }
diff --git a/security/integrity/iint.c b/security/integrity/iint.c
index 371cbceaf479..4580df0e716e 100644
--- a/security/integrity/iint.c
+++ b/security/integrity/iint.c
@@ -143,21 +143,32 @@ struct integrity_iint_cache *integrity_inode_get(struct inode *inode)
 /**
  * integrity_inode_free - called on security_inode_free
  * @inode: pointer to the inode
+ * @check: optional callback function to check whether the iint can be freed
  *
  * Free the integrity information(iint) associated with an inode.
  */
-void integrity_inode_free(struct inode *inode)
+void integrity_inode_free(struct inode *inode, iint_removable_cb check)
 {
 	struct integrity_iint_cache *iint;
+	bool freeit = true;
 
 	if (!IS_IMA(inode))
 		return;
 
 	write_lock(&integrity_iint_lock);
+
 	iint = __integrity_iint_find(inode);
-	rb_erase(&iint->rb_node, &integrity_iint_tree);
+
+	if (check)
+		freeit = check(iint);
+	if (freeit)
+		rb_erase(&iint->rb_node, &integrity_iint_tree);
+
 	write_unlock(&integrity_iint_lock);
 
+	if (!freeit)
+		return;
+
 	ima_free_ns_status_list(&iint->ns_list, &iint->ns_list_lock);
 
 	iint_free(iint);
diff --git a/security/security.c b/security/security.c
index c88167a414b4..84c9ed18a4b1 100644
--- a/security/security.c
+++ b/security/security.c
@@ -1035,7 +1035,7 @@ static void inode_free_by_rcu(struct rcu_head *head)
 
 void security_inode_free(struct inode *inode)
 {
-	integrity_inode_free(inode);
+	integrity_inode_free(inode, NULL);
 	call_void_hook(inode_free_security, inode);
 	/*
 	 * The inode may still be referenced in a path walk and
-- 
2.31.1


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

* [PATCH v10 20/27] ima: Namespace audit status flags
  2022-02-01 20:37 [PATCH v10 00/27] ima: Namespace IMA with audit support in IMA-ns Stefan Berger
                   ` (18 preceding siblings ...)
  2022-02-01 20:37 ` [PATCH v10 19/27] integrity: Add optional callback function to integrity_inode_free() Stefan Berger
@ 2022-02-01 20:37 ` Stefan Berger
  2022-02-01 20:37 ` [PATCH v10 21/27] ima: Remove unused iints from the integrity_iint_cache Stefan Berger
                   ` (7 subsequent siblings)
  27 siblings, 0 replies; 86+ messages in thread
From: Stefan Berger @ 2022-02-01 20:37 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_AUDIT and IMA_AUDITED flags into the
per-namespace ns_status, enabling IMA audit mechanism to audit the same
file each time it is accessed in a new namespace.

Read and write operations on the iint flags is replaced with function
calls. For reading, iint_flags() returns the bitwise OR of iint->flags
and ns_status->flags. The ns_status flags are masked with
IMA_NS_STATUS_FLAGS (currently only IMA_AUDIT & IMA_AUDITED) while the
iint flags are masked with ~IMA_NS_STATUS_FLAGS. Similarly,
set_iint_flags() writes the one masked portion to the ns_status flags,
while the iint flags receive the remaining flags. The ns_status
parameter added to ima_audit_measurement() is used with the above
functions to query and set the ns_status flags.

Replace all occurrences where the IMA_AUDITED flag is set with the
iint_flags() and set_iint_flags() operations so that the splitting
and merging of the flags works properly. Whenever the IMA_AUDITED flag is
tested, use iint_flags() to receive the merged version of the flags.

Since IMA_AUDITED is also part of the IMA_DONE_MASK, use the new
functions wherever the IMA_DONE_MASK is involved.

Move the IMA_AUDIT flag also into the ns_status flags since this flag
is dependent on policy rules that may be different per namespace.

Signed-off-by: Mehmet Kayaalp <mkayaalp@linux.vnet.ibm.com>
Signed-off-by: Stefan Berger <stefanb@linux.ibm.com>

---
v9:
 - Use ns_status also in non-namespaced case and use same flag splitting
   for namespaced and non-namespaced case, thus use one implementation
   for iint_flags() and set_iint_flags()
 - Merge-in patch 'Enable re-auditing of modified files'
 - Use one implementation of mask_iint_ns_status_flags() for namespaced
   and non-namespaced case
 - Added IMA_AUDIT to IMA_NS_STATUS_FLAGS since it's a per namespace flag
---
 security/integrity/ima/ima.h      | 40 ++++++++++++++++++++--
 security/integrity/ima/ima_api.c  |  8 +++--
 security/integrity/ima/ima_main.c | 56 ++++++++++++++++++++++++++-----
 security/integrity/integrity.h    |  3 ++
 4 files changed, 94 insertions(+), 13 deletions(-)

diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h
index 4af8f2c4df40..883aeb30590f 100644
--- a/security/integrity/ima/ima.h
+++ b/security/integrity/ima/ima.h
@@ -310,7 +310,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 *ns_status);
 int ima_alloc_init_template(struct ima_event_data *event_data,
 			    struct ima_template_entry **entry,
 			    struct ima_template_desc *template_desc);
@@ -493,6 +494,34 @@ static inline int ima_filter_rule_match(u32 secid, u32 field, u32 op,
 #define	POLICY_FILE_FLAGS	S_IWUSR
 #endif /* CONFIG_IMA_READ_POLICY */
 
+#define IMA_NS_STATUS_ACTIONS   IMA_AUDIT
+#define IMA_NS_STATUS_FLAGS     (IMA_AUDIT | IMA_AUDITED)
+
+static inline unsigned long iint_flags(struct integrity_iint_cache *iint,
+				       struct ns_status *ns_status)
+{
+	if (!ns_status)
+		return iint->flags;
+
+	return (iint->flags & ~IMA_NS_STATUS_FLAGS) |
+	       (ns_status->flags & IMA_NS_STATUS_FLAGS);
+}
+
+static inline unsigned long set_iint_flags(struct integrity_iint_cache *iint,
+					   struct ns_status *ns_status,
+					   unsigned long flags)
+{
+	unsigned long ns_status_flags = flags & IMA_NS_STATUS_FLAGS;
+
+	WARN_ON(!ns_status && ns_status_flags);
+
+	iint->flags = flags & ~IMA_NS_STATUS_FLAGS;
+	if (ns_status)
+		ns_status->flags = ns_status_flags;
+
+	return flags;
+}
+
 static inline
 struct user_namespace *ima_user_ns_from_file(const struct file *filp)
 {
@@ -529,7 +558,14 @@ static inline struct ns_status *ima_get_ns_status(struct ima_namespace *ns,
 						  struct inode *inode,
 						  struct integrity_iint_cache *iint)
 {
-	return NULL;
+	struct ns_status *ns_status = &iint->ns_status;
+
+	if (list_empty(&iint->ns_list)) {
+		ns_status_init(ns_status);
+		list_add(&ns_status->ns_next, &iint->ns_list);
+	}
+
+	return ns_status;
 }
 
 #endif /* CONFIG_IMA_NS */
diff --git a/security/integrity/ima/ima_api.c b/security/integrity/ima/ima_api.c
index bee35ebb3a38..4284c216ee7b 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 *ns_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, ns_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, ns_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 917504319e7f..1dee8cb5afa2 100644
--- a/security/integrity/ima/ima_main.c
+++ b/security/integrity/ima/ima_main.c
@@ -149,6 +149,31 @@ static void ima_rdwr_violation_check(struct ima_namespace *ns,
 				  "invalid_pcr", "open_writers");
 }
 
+static void mask_iint_ns_status_flags(struct integrity_iint_cache *iint,
+				      unsigned long mask)
+{
+	struct ns_status *ns_status;
+	unsigned long flags;
+
+	read_lock(&iint->ns_list_lock);
+	if (list_empty(&iint->ns_list)) {
+		/*
+		 * An empty list is possible due to __process_measurement only
+		 * creating ns_status for IMA_NS_STATUS_ACTIONS.
+		 * No ns_status being used implies that for example IMA_AUDIT
+		 * was never used and thus IMA_AUDITED cannot have been set.
+		 */
+		flags = iint_flags(iint, NULL) & mask;
+		set_iint_flags(iint, NULL, flags);
+	} else {
+		list_for_each_entry(ns_status, &iint->ns_list, ns_next) {
+			flags = iint_flags(iint, ns_status) & mask;
+			set_iint_flags(iint, ns_status, flags);
+		}
+	}
+	read_unlock(&iint->ns_list_lock);
+}
+
 static void ima_check_last_writer(struct integrity_iint_cache *iint,
 				  struct inode *inode, struct file *file)
 {
@@ -165,8 +190,11 @@ 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);
 		}
@@ -203,6 +231,7 @@ static int __process_measurement(struct ima_namespace *ns,
 {
 	struct inode *inode = file_inode(file);
 	struct integrity_iint_cache *iint = NULL;
+	struct ns_status *ns_status = NULL;
 	struct ima_template_desc *template_desc = NULL;
 	char *pathbuf = NULL;
 	char filename[NAME_MAX];
@@ -215,6 +244,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;
@@ -243,6 +273,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)) {
+			ns_status = ima_get_ns_status(ns, inode, iint);
+			if (IS_ERR(ns_status)) {
+				rc = PTR_ERR(ns_status);
+				ns_status = NULL;
+			}
+		}
 	}
 
 	if (!rc && violation_check)
@@ -258,11 +296,13 @@ static int __process_measurement(struct ima_namespace *ns,
 
 	mutex_lock(&iint->mutex);
 
+	flags = iint_flags(iint, ns_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
@@ -273,7 +313,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;
 	}
 
@@ -281,9 +321,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, ns_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)))
@@ -358,7 +398,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, ns_status);
 
 	if ((file->f_flags & O_DIRECT) && (iint->flags & IMA_PERMIT_DIRECTIO))
 		rc = 0;
diff --git a/security/integrity/integrity.h b/security/integrity/integrity.h
index b7d5ca108900..dbe9f36d3692 100644
--- a/security/integrity/integrity.h
+++ b/security/integrity/integrity.h
@@ -171,6 +171,9 @@ struct integrity_iint_cache {
 	 */
 	rwlock_t ns_list_lock;
 	struct list_head ns_list;
+#ifndef CONFIG_IMA_NS
+	struct ns_status ns_status;
+#endif
 };
 
 /* rbtree tree calls to lookup, insert, delete
-- 
2.31.1


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

* [PATCH v10 21/27] ima: Remove unused iints from the integrity_iint_cache
  2022-02-01 20:37 [PATCH v10 00/27] ima: Namespace IMA with audit support in IMA-ns Stefan Berger
                   ` (19 preceding siblings ...)
  2022-02-01 20:37 ` [PATCH v10 20/27] ima: Namespace audit status flags Stefan Berger
@ 2022-02-01 20:37 ` Stefan Berger
  2022-02-01 20:37 ` [PATCH v10 22/27] securityfs: Extend securityfs with namespacing support Stefan Berger
                   ` (6 subsequent siblings)
  27 siblings, 0 replies; 86+ messages in thread
From: Stefan Berger @ 2022-02-01 20:37 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

When the rbtree of an IMA namespace is torn down, also remove those iints
that are completely unused since only the torn-down namespace stored data
about the associated inode in it.

An iint is unused when the following two conditions are met:

- Its ns_status list is empty which means that no IMA namespace
  currently has auditing related state stored in it.

- The iint's flags don't contain any of the flags IMA_MEASURE,
  IMA_APPRAISE or IMA_HASH that the host would still store there.
  It doesn't need an ns_status list for these but also only for
  IMA_AUDIT.

Introduce the #define IMA_IINT_FLAGS that represent the mask to test the
iint->flags with in this case. This test provides the reason to keep the
iint if any of these flags are set.

The IMA_IINT_FLAGS mask will loose its flags as more flags are namespaced
and can then be removed in the end and only the check for the empty list
will remain.

Process the list of garbage-collected ns_status outside the locking of
the ns_status tree and related lock-group and free any iint that was
previously found to be unused while walking the list. File accesses, that
may have happened in the meantime, could have re-activated the iint and
therefore pass along the test function to check whether the iint is still
unused.

Signed-off-by: Stefan Berger <stefanb@linux.ibm.com>
---
 security/integrity/iint.c              |  4 +++
 security/integrity/ima/ima.h           |  2 ++
 security/integrity/ima/ima_ns_status.c | 43 +++++++++++++++++++++++++-
 security/integrity/integrity.h         |  1 +
 4 files changed, 49 insertions(+), 1 deletion(-)

diff --git a/security/integrity/iint.c b/security/integrity/iint.c
index 4580df0e716e..b0996bd0ee67 100644
--- a/security/integrity/iint.c
+++ b/security/integrity/iint.c
@@ -158,6 +158,10 @@ void integrity_inode_free(struct inode *inode, iint_removable_cb check)
 	write_lock(&integrity_iint_lock);
 
 	iint = __integrity_iint_find(inode);
+	if (!iint) {
+		write_unlock(&integrity_iint_lock);
+		return;
+	}
 
 	if (check)
 		freeit = check(iint);
diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h
index 883aeb30590f..ec0b81c7dbf5 100644
--- a/security/integrity/ima/ima.h
+++ b/security/integrity/ima/ima.h
@@ -497,6 +497,8 @@ static inline int ima_filter_rule_match(u32 secid, u32 field, u32 op,
 #define IMA_NS_STATUS_ACTIONS   IMA_AUDIT
 #define IMA_NS_STATUS_FLAGS     (IMA_AUDIT | IMA_AUDITED)
 
+#define IMA_IINT_FLAGS		(IMA_MEASURE | IMA_APPRAISE | IMA_HASH)
+
 static inline unsigned long iint_flags(struct integrity_iint_cache *iint,
 				       struct ns_status *ns_status)
 {
diff --git a/security/integrity/ima/ima_ns_status.c b/security/integrity/ima/ima_ns_status.c
index 9c753caad6ac..d1ccae2c2313 100644
--- a/security/integrity/ima/ima_ns_status.c
+++ b/security/integrity/ima/ima_ns_status.c
@@ -131,6 +131,26 @@ static void ns_status_free(struct ima_namespace *ns,
 	kmem_cache_free(ns->ns_status_cache, ns_status);
 }
 
+/* Test whether an iint is unused due to empty ns_status list AND the
+ * not-yet namespaced flags are not set on it.
+ */
+static bool __iint_is_unused(struct integrity_iint_cache *iint)
+{
+	return list_empty(&iint->ns_list) &&
+		(iint_flags(iint, NULL) & IMA_IINT_FLAGS) == 0;
+}
+
+static bool iint_is_unused(struct integrity_iint_cache *iint)
+{
+	bool ret;
+
+	write_lock(&iint->ns_list_lock);
+	ret = __iint_is_unused(iint);
+	write_unlock(&iint->ns_list_lock);
+
+	return ret;
+}
+
 /*
  * 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
@@ -161,6 +181,18 @@ void ima_free_ns_status_tree(struct ima_namespace *ns)
 			if (!list_empty(&ns_status->ns_next)) {
 				list_del_init(&ns_status->ns_next);
 				llist_add(&ns_status->gc_llist, &garbage);
+
+				/*
+				 * While ns_status->iint is guaranteed to be
+				 * there, check whether the iint is still in
+				 * use by anyone at this moment.
+				 */
+				if (__iint_is_unused(ns_status->iint)) {
+					ns_status->inode_to_remove =
+						ns_status->iint->inode;
+				} else {
+					ns_status->inode_to_remove = NULL;
+				}
 				ctr++;
 			}
 			write_unlock(&ns_status->iint->ns_list_lock);
@@ -180,8 +212,17 @@ void ima_free_ns_status_tree(struct ima_namespace *ns)
 	} while (restart);
 
 	node = llist_del_all(&garbage);
-	llist_for_each_entry_safe(ns_status, next, node, gc_llist)
+	llist_for_each_entry_safe(ns_status, next, node, gc_llist) {
+		if (ns_status->inode_to_remove) {
+			/*
+			 * Pass along the test function in case inode is in
+			 * use now.
+			 */
+			integrity_inode_free(ns_status->inode_to_remove,
+					     iint_is_unused);
+		}
 		ns_status_free(ns, ns_status);
+	}
 
 	kmem_cache_destroy(ns->ns_status_cache);
 }
diff --git a/security/integrity/integrity.h b/security/integrity/integrity.h
index dbe9f36d3692..6276e6a615b7 100644
--- a/security/integrity/integrity.h
+++ b/security/integrity/integrity.h
@@ -134,6 +134,7 @@ struct ns_status {
 	ino_t i_ino;
 	u32 i_generation;
 	struct llist_node gc_llist;	/* used while freeing */
+	void *inode_to_remove;		/* used while freeing */
 #endif
 };
 
-- 
2.31.1


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

* [PATCH v10 22/27] securityfs: Extend securityfs with namespacing support
  2022-02-01 20:37 [PATCH v10 00/27] ima: Namespace IMA with audit support in IMA-ns Stefan Berger
                   ` (20 preceding siblings ...)
  2022-02-01 20:37 ` [PATCH v10 21/27] ima: Remove unused iints from the integrity_iint_cache Stefan Berger
@ 2022-02-01 20:37 ` Stefan Berger
  2022-02-23  1:48   ` Mimi Zohar
  2022-02-01 20:37 ` [PATCH v10 23/27] ima: Setup securityfs for IMA namespace Stefan Berger
                   ` (5 subsequent siblings)
  27 siblings, 1 reply; 86+ messages in thread
From: Stefan Berger @ 2022-02-01 20:37 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, Christian Brauner,
	James Bottomley

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

Since we do not need the pinning of the filesystem for the virtualization
case, limit the usage of simple_pin_fs() and simpe_release_fs() to the
case when the init_user_ns is active. This simplifies the cleanup for the
virtualization case where usage of securityfs_remove() to free dentries
is therefore not needed anymore.

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 filesystem 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().

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 is neither the user namespace it belongs to
nor an ancestor of the user namespace that the instance of securityfs
belongs to. Do not prevent access if securityfs was bind-mounted and
therefore the init_user_ns is the owning user namespace.

Suggested-by: Christian Brauner <brauner@kernel.org>
Signed-off-by: Stefan Berger <stefanb@linux.ibm.com>
Signed-off-by: James Bottomley <James.Bottomley@HansenPartnership.com>
---
 security/inode.c | 72 ++++++++++++++++++++++++++++++++++++++++--------
 1 file changed, 61 insertions(+), 11 deletions(-)

diff --git a/security/inode.c b/security/inode.c
index 13e6780c4444..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,6 +198,7 @@ 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);
@@ -167,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;
 }
 
@@ -293,11 +338,14 @@ 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)) {
@@ -310,7 +358,9 @@ void securityfs_remove(struct dentry *dentry)
 		dput(dentry);
 	}
 	inode_unlock(dir);
-	simple_release_fs(&mount, &mount_count);
+	if (ns == &init_user_ns)
+		simple_release_fs(&init_securityfs_mount,
+				  &init_securityfs_mount_count);
 }
 EXPORT_SYMBOL_GPL(securityfs_remove);
 
-- 
2.31.1


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

* [PATCH v10 23/27] ima: Setup securityfs for IMA namespace
  2022-02-01 20:37 [PATCH v10 00/27] ima: Namespace IMA with audit support in IMA-ns Stefan Berger
                   ` (21 preceding siblings ...)
  2022-02-01 20:37 ` [PATCH v10 22/27] securityfs: Extend securityfs with namespacing support Stefan Berger
@ 2022-02-01 20:37 ` Stefan Berger
  2022-02-23 11:45   ` Mimi Zohar
  2022-02-01 20:37 ` [PATCH v10 24/27] ima: Introduce securityfs file to activate an " Stefan Berger
                   ` (4 subsequent siblings)
  27 siblings, 1 reply; 86+ messages in thread
From: Stefan Berger @ 2022-02-01 20:37 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,
	Christian Brauner

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.

Introduce a variable ima_policy_removed in ima_namespace that is used to
remember whether the policy file has previously been removed and thus
should not be created again in case of unmounting and again mounting
securityfs inside an IMA namespace.

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>
Acked-by: Christian Brauner <brauner@kernel.org>

---

v9:
 - rename policy_dentry_removed to ima_policy_removed
---
 include/linux/ima.h             | 13 ++++++++++
 security/inode.c                |  6 ++++-
 security/integrity/ima/ima.h    |  1 +
 security/integrity/ima/ima_fs.c | 46 +++++++++++++++++++++++----------
 4 files changed, 52 insertions(+), 14 deletions(-)

diff --git a/include/linux/ima.h b/include/linux/ima.h
index 2fe32f1bf84b..c584527c0f47 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);
@@ -227,6 +228,12 @@ void free_ima_ns(struct user_namespace *ns);
 
 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)
@@ -238,6 +245,12 @@ static inline void ima_free_ns_status_list(struct list_head *head,
 {
 }
 
+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 ec0b81c7dbf5..1e3f9dda218d 100644
--- a/security/integrity/ima/ima.h
+++ b/security/integrity/ima/ima.h
@@ -148,6 +148,7 @@ struct ima_namespace {
 	int valid_policy;
 
 	struct dentry *ima_policy;
+	bool ima_policy_removed;
 
 	struct notifier_block ima_lsm_policy_notifier;
 } __randomize_layout;
diff --git a/security/integrity/ima/ima_fs.c b/security/integrity/ima/ima_fs.c
index c41aa61b7393..84cd02a9e19b 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"
 
@@ -433,6 +434,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->ima_policy);
 	ns->ima_policy = NULL;
+	ns->ima_policy_removed = true;
 #elif defined(CONFIG_IMA_WRITE_POLICY)
 	clear_bit(IMA_FS_BUSY, &ns->ima_fs_flags);
 #elif defined(CONFIG_IMA_READ_POLICY)
@@ -449,9 +451,11 @@ 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;
@@ -459,11 +463,22 @@ static int __init ima_fs_ns_init(struct ima_namespace *ns)
 	struct dentry *violations = NULL;
 	int ret;
 
-	ima_dir = securityfs_create_dir("ima", integrity_dir);
-	if (IS_ERR(ima_dir))
-		return PTR_ERR(ima_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_symlink = securityfs_create_symlink("ima", NULL, "integrity/ima",
+	ima_dir = securityfs_create_dir("ima", int_dir);
+	if (IS_ERR(ima_dir)) {
+		ret = PTR_ERR(ima_dir);
+		goto out;
+	}
+
+	ima_symlink = securityfs_create_symlink("ima", root, "integrity/ima",
 						NULL);
 	if (IS_ERR(ima_symlink)) {
 		ret = PTR_ERR(ima_symlink);
@@ -505,12 +520,15 @@ static int __init ima_fs_ns_init(struct ima_namespace *ns)
 		goto out;
 	}
 
-	ns->ima_policy = securityfs_create_file("policy", POLICY_FILE_FLAGS,
-						ima_dir, NULL,
-						&ima_measure_policy_ops);
-	if (IS_ERR(ns->ima_policy)) {
-		ret = PTR_ERR(ns->ima_policy);
-		goto out;
+	if (!ns->ima_policy_removed) {
+		ns->ima_policy =
+		    securityfs_create_file("policy", POLICY_FILE_FLAGS,
+					   ima_dir, NULL,
+					   &ima_measure_policy_ops);
+		if (IS_ERR(ns->ima_policy)) {
+			ret = PTR_ERR(ns->ima_policy);
+			goto out;
+		}
 	}
 
 	return 0;
@@ -522,11 +540,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 ret;
 }
 
 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] 86+ messages in thread

* [PATCH v10 24/27] ima: Introduce securityfs file to activate an IMA namespace
  2022-02-01 20:37 [PATCH v10 00/27] ima: Namespace IMA with audit support in IMA-ns Stefan Berger
                   ` (22 preceding siblings ...)
  2022-02-01 20:37 ` [PATCH v10 23/27] ima: Setup securityfs for IMA namespace Stefan Berger
@ 2022-02-01 20:37 ` Stefan Berger
  2022-02-23 13:54   ` Mimi Zohar
  2022-02-01 20:37 ` [PATCH v10 25/27] ima: Show owning user namespace's uid and gid when displaying policy Stefan Berger
                   ` (3 subsequent siblings)
  27 siblings, 1 reply; 86+ messages in thread
From: Stefan Berger @ 2022-02-01 20:37 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

Introduce securityfs file 'active' that allows a user to activate an IMA
namespace by writing a "1" (precisely a '1\0' or '1\n') to it. When
reading from the file, it shows either '0' or '1'.

Also, introduce ns_is_active() to be used in those places where the
ima_namespace pointer may either be NULL or where the active field may not
have been set to '1', yet. An inactive IMA namespace should never be
accessed since it has not been initialized, yet.

Set the init_ima_ns's active field to '1' since it is considered active
right from the beginning.

The rationale for introducing a file to activate an IMA namespace is that
subsequent support for IMA-measurement and IMA-appraisal will add
configuration files for selecting for example the template that an IMA
namespace is supposed to use for logging measurements, which will add
an IMA namespace configuration stage (using securityfs files) before its
activation.

Signed-off-by: Stefan Berger <stefanb@linux.ibm.com>

---
 v10:
 - use memdup_user_nul to copy input from user
 - propagating error returned from ima_fs_add_ns_files()
---
 security/integrity/ima/ima.h             |  7 +++
 security/integrity/ima/ima_fs.c          | 71 ++++++++++++++++++++++++
 security/integrity/ima/ima_init_ima_ns.c |  1 +
 security/integrity/ima/ima_main.c        |  2 +-
 4 files changed, 80 insertions(+), 1 deletion(-)

diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h
index 1e3f9dda218d..05e2de7697da 100644
--- a/security/integrity/ima/ima.h
+++ b/security/integrity/ima/ima.h
@@ -123,6 +123,8 @@ struct ima_h_table {
 };
 
 struct ima_namespace {
+	atomic_t active;		/* whether namespace is active */
+
 	struct rb_root ns_status_tree;
 	rwlock_t ns_tree_lock;
 	struct kmem_cache *ns_status_cache;
@@ -154,6 +156,11 @@ struct ima_namespace {
 } __randomize_layout;
 extern struct ima_namespace init_ima_ns;
 
+static inline bool ns_is_active(struct ima_namespace *ns)
+{
+	return (ns && atomic_read(&ns->active));
+}
+
 extern const int read_idmap[];
 
 #ifdef CONFIG_HAVE_IMA_KEXEC
diff --git a/security/integrity/ima/ima_fs.c b/security/integrity/ima/ima_fs.c
index 84cd02a9e19b..58d80884880f 100644
--- a/security/integrity/ima/ima_fs.c
+++ b/security/integrity/ima/ima_fs.c
@@ -451,6 +451,71 @@ static const struct file_operations ima_measure_policy_ops = {
 	.llseek = generic_file_llseek,
 };
 
+static ssize_t ima_show_active(struct file *filp,
+			       char __user *buf,
+			       size_t count, loff_t *ppos)
+{
+	struct ima_namespace *ns = &init_ima_ns;
+	char tmpbuf[2];
+	ssize_t len;
+
+	len = scnprintf(tmpbuf, sizeof(tmpbuf),
+			"%d\n", atomic_read(&ns->active));
+	return simple_read_from_buffer(buf, count, ppos, tmpbuf, len);
+}
+
+static ssize_t ima_write_active(struct file *filp,
+				const char __user *buf,
+				size_t count, loff_t *ppos)
+{
+	struct ima_namespace *ns = &init_ima_ns;
+	unsigned int active;
+	char *kbuf;
+	int err;
+
+	if (ns_is_active(ns))
+		return -EBUSY;
+
+	/* accepting '1\n' and '1\0' and no partial writes */
+	if (count >= 3 || *ppos != 0)
+		return -EINVAL;
+
+	kbuf = memdup_user_nul(buf, count);
+	if (IS_ERR(kbuf))
+		return PTR_ERR(kbuf);
+
+	err = kstrtouint(kbuf, 10, &active);
+	kfree(kbuf);
+	if (err)
+		return err;
+
+	if (active != 1)
+		return -EINVAL;
+
+	atomic_set(&ns->active, 1);
+
+	return count;
+}
+
+static const struct file_operations ima_active_ops = {
+	.read = ima_show_active,
+	.write = ima_write_active,
+};
+
+static int ima_fs_add_ns_files(struct dentry *ima_dir)
+{
+	struct dentry *active;
+
+	active =
+	    securityfs_create_file("active",
+				   S_IRUSR | S_IWUSR | S_IRGRP, ima_dir, NULL,
+				   &ima_active_ops);
+	if (IS_ERR(active))
+		return PTR_ERR(active);
+
+	return 0;
+}
+
 int ima_fs_ns_init(struct user_namespace *user_ns, struct dentry *root)
 {
 	struct ima_namespace *ns = ima_ns_from_user_ns(user_ns);
@@ -531,6 +596,12 @@ int ima_fs_ns_init(struct user_namespace *user_ns, struct dentry *root)
 		}
 	}
 
+	if (ns != &init_ima_ns) {
+		ret = ima_fs_add_ns_files(ima_dir);
+		if (ret)
+			goto out;
+	}
+
 	return 0;
 out:
 	securityfs_remove(ns->ima_policy);
diff --git a/security/integrity/ima/ima_init_ima_ns.c b/security/integrity/ima/ima_init_ima_ns.c
index d4ddfd1de60b..39ee0c2477a6 100644
--- a/security/integrity/ima/ima_init_ima_ns.c
+++ b/security/integrity/ima/ima_init_ima_ns.c
@@ -58,5 +58,6 @@ struct ima_namespace init_ima_ns = {
 	.ima_lsm_policy_notifier = {
 		.notifier_call = ima_lsm_policy_change,
 	},
+	.active = ATOMIC_INIT(1),
 };
 EXPORT_SYMBOL(init_ima_ns);
diff --git a/security/integrity/ima/ima_main.c b/security/integrity/ima/ima_main.c
index 1dee8cb5afa2..9ca9223bbcf6 100644
--- a/security/integrity/ima/ima_main.c
+++ b/security/integrity/ima/ima_main.c
@@ -441,7 +441,7 @@ static int process_measurement(struct user_namespace *user_ns,
 
 	while (user_ns) {
 		ns = ima_ns_from_user_ns(user_ns);
-		if (ns) {
+		if (ns_is_active(ns)) {
 			int rc;
 
 			rc = __process_measurement(ns, file, cred, secid, buf,
-- 
2.31.1


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

* [PATCH v10 25/27] ima: Show owning user namespace's uid and gid when displaying policy
  2022-02-01 20:37 [PATCH v10 00/27] ima: Namespace IMA with audit support in IMA-ns Stefan Berger
                   ` (23 preceding siblings ...)
  2022-02-01 20:37 ` [PATCH v10 24/27] ima: Introduce securityfs file to activate an " Stefan Berger
@ 2022-02-01 20:37 ` Stefan Berger
  2022-02-23 14:06   ` Mimi Zohar
  2022-02-01 20:37 ` [PATCH v10 26/27] ima: Limit number of policy rules in non-init_ima_ns Stefan Berger
                   ` (2 subsequent siblings)
  27 siblings, 1 reply; 86+ messages in thread
From: Stefan Berger @ 2022-02-01 20:37 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

Show the uid and gid values relative to the user namespace that is
currently active. The effect of this changes is that when one displays
the policy from the user namespace that originally set the policy,
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>

---
v9:
  - use seq_user_ns and from_k{g,u}id_munged()
---
 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 fe3dce8fb939..acb4c36e539f 100644
--- a/security/integrity/ima/ima_policy.c
+++ b/security/integrity/ima/ima_policy.c
@@ -2000,6 +2000,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 = seq_user_ns(m);
 	struct ima_rule_entry *entry = v;
 	int i;
 	char tbuf[64] = {0,};
@@ -2085,7 +2086,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_munged(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)
@@ -2096,7 +2098,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_munged(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)
@@ -2107,7 +2110,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_munged(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)
@@ -2118,7 +2122,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_munged(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)
@@ -2129,7 +2134,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_munged(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)
@@ -2140,7 +2146,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_munged(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] 86+ messages in thread

* [PATCH v10 26/27] ima: Limit number of policy rules in non-init_ima_ns
  2022-02-01 20:37 [PATCH v10 00/27] ima: Namespace IMA with audit support in IMA-ns Stefan Berger
                   ` (24 preceding siblings ...)
  2022-02-01 20:37 ` [PATCH v10 25/27] ima: Show owning user namespace's uid and gid when displaying policy Stefan Berger
@ 2022-02-01 20:37 ` Stefan Berger
  2022-02-23 15:38   ` Mimi Zohar
  2022-02-01 20:37 ` [PATCH v10 27/27] ima: Enable IMA namespaces Stefan Berger
  2022-02-02 14:13 ` [PATCH v10 00/27] ima: Namespace IMA with audit support in IMA-ns Christian Brauner
  27 siblings, 1 reply; 86+ messages in thread
From: Stefan Berger @ 2022-02-01 20:37 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

Limit the number of policy rules one can set in non-init_ima_ns to a
hardcoded 1024 rules. If the user attempts to exceed this limit by
setting too many additional rules, emit an audit message with the cause
'too-many-rules' and simply ignore the newly added rules.

Switch the accounting for the memory allocated for IMA policy rules to
GFP_KERNEL_ACCOUNT so that cgroups kernel memory accounting takes effect.

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

diff --git a/security/integrity/ima/ima_fs.c b/security/integrity/ima/ima_fs.c
index 58d80884880f..cd102bbd4677 100644
--- a/security/integrity/ima/ima_fs.c
+++ b/security/integrity/ima/ima_fs.c
@@ -410,24 +410,32 @@ static int ima_release_policy(struct inode *inode, struct file *file)
 {
 	struct ima_namespace *ns = &init_ima_ns;
 	const char *cause = ns->valid_policy ? "completed" : "failed";
+	int err = 0;
 
 	if ((file->f_flags & O_ACCMODE) == O_RDONLY)
 		return seq_release(inode, file);
 
-	if (ns->valid_policy && ima_check_policy(ns) < 0) {
-		cause = "failed";
-		ns->valid_policy = 0;
+	if (ns->valid_policy) {
+		err = ima_check_policy(ns);
+		if (err < 0) {
+			if (err == -ENOSPC)
+				cause = "too-many-rules";
+			else
+				cause = "failed";
+			ns->valid_policy = 0;
+		}
 	}
 
 	pr_info("policy update %s\n", cause);
-	integrity_audit_msg(AUDIT_INTEGRITY_STATUS, NULL, NULL,
-			    "policy_update", cause, !ns->valid_policy, 0);
+	integrity_audit_message(AUDIT_INTEGRITY_STATUS, NULL, NULL,
+				"policy_update", cause, !ns->valid_policy, 0,
+				-err);
 
 	if (!ns->valid_policy) {
 		ima_delete_rules(ns);
 		ns->valid_policy = 1;
 		clear_bit(IMA_FS_BUSY, &ns->ima_fs_flags);
-		return 0;
+		return err;
 	}
 
 	ima_update_policy(ns);
diff --git a/security/integrity/ima/ima_policy.c b/security/integrity/ima/ima_policy.c
index acb4c36e539f..3f84d04f101d 100644
--- a/security/integrity/ima/ima_policy.c
+++ b/security/integrity/ima/ima_policy.c
@@ -305,7 +305,8 @@ static struct ima_rule_opt_list *ima_alloc_rule_opt_list(const substring_t *src)
 		return ERR_PTR(-EINVAL);
 	}
 
-	opt_list = kzalloc(struct_size(opt_list, items, count), GFP_KERNEL);
+	opt_list = kzalloc(struct_size(opt_list, items, count),
+			   GFP_KERNEL_ACCOUNT);
 	if (!opt_list) {
 		kfree(src_copy);
 		return ERR_PTR(-ENOMEM);
@@ -379,7 +380,7 @@ static struct ima_rule_entry *ima_lsm_copy_rule(struct ima_namespace *ns,
 	 * Immutable elements are copied over as pointers and data; only
 	 * lsm rules can change
 	 */
-	nentry = kmemdup(entry, sizeof(*nentry), GFP_KERNEL);
+	nentry = kmemdup(entry, sizeof(*nentry), GFP_KERNEL_ACCOUNT);
 	if (!nentry)
 		return NULL;
 
@@ -826,7 +827,7 @@ static void add_rules(struct ima_namespace *ns,
 
 		if (policy_rule & IMA_CUSTOM_POLICY) {
 			entry = kmemdup(&entries[i], sizeof(*entry),
-					GFP_KERNEL);
+					GFP_KERNEL_ACCOUNT);
 			if (!entry)
 				continue;
 
@@ -863,7 +864,7 @@ static int __init ima_init_arch_policy(struct ima_namespace *ns)
 
 	ns->arch_policy_entry = kcalloc(arch_entries + 1,
 					sizeof(*ns->arch_policy_entry),
-					GFP_KERNEL);
+					GFP_KERNEL_ACCOUNT);
 	if (!ns->arch_policy_entry)
 		return 0;
 
@@ -975,8 +976,20 @@ void __init ima_init_policy(struct ima_namespace *ns)
 /* Make sure we have a valid policy, at least containing some rules. */
 int ima_check_policy(struct ima_namespace *ns)
 {
+	struct ima_rule_entry *entry;
+	size_t len1 = 0;
+	size_t len2 = 0;
+
 	if (list_empty(&ns->ima_temp_rules))
 		return -EINVAL;
+	if (ns != &init_ima_ns) {
+		list_for_each_entry(entry, &ns->ima_temp_rules, list)
+			len1++;
+		list_for_each_entry(entry, &ns->ima_policy_rules, list)
+			len2++;
+		if (len1 + len2 > 1024)
+			return -ENOSPC;
+	}
 	return 0;
 }
 
@@ -1848,7 +1861,7 @@ ssize_t ima_parse_add_rule(struct ima_namespace *ns, char *rule)
 	if (*p == '#' || *p == '\0')
 		return len;
 
-	entry = kzalloc(sizeof(*entry), GFP_KERNEL);
+	entry = kzalloc(sizeof(*entry), GFP_KERNEL_ACCOUNT);
 	if (!entry) {
 		integrity_audit_msg(AUDIT_INTEGRITY_STATUS, NULL,
 				    NULL, op, "-ENOMEM", -ENOMEM, audit_info);
-- 
2.31.1


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

* [PATCH v10 27/27] ima: Enable IMA namespaces
  2022-02-01 20:37 [PATCH v10 00/27] ima: Namespace IMA with audit support in IMA-ns Stefan Berger
                   ` (25 preceding siblings ...)
  2022-02-01 20:37 ` [PATCH v10 26/27] ima: Limit number of policy rules in non-init_ima_ns Stefan Berger
@ 2022-02-01 20:37 ` Stefan Berger
  2022-02-23 17:58   ` Serge E. Hallyn
  2022-02-02 14:13 ` [PATCH v10 00/27] ima: Namespace IMA with audit support in IMA-ns Christian Brauner
  27 siblings, 1 reply; 86+ messages in thread
From: Stefan Berger @ 2022-02-01 20:37 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

Introduce the IMA_NS in Kconfig for IMA namespace enablement.

Enable the lazy initialization of an IMA namespace when a user mounts
SecurityFS and writes '1' into IMA's 'active' securityfs file. A
user_namespace will now get a pointer to an ima_namespace and therefore
implement get_current_ns() for the namespacing case that returns this
pointer. Use get_current_ns() in those places that require access to the
current IMA namespace. In some places, primarily those related to
IMA-appraisal and changes to file attributes, keep the pointer to
init_ima_ns, since there flags related to file measurements may be
affected, which are not supported in IMA namespaces, yet.

Before using the ima_namespace pointer test it with ns_is_active()
to check whether it is NULL and whether the ima_namespace is active.
If it's not active, it cannot be used, yet. Therefore, return early
from those functions that may now get either get a NULL pointer from
this call or where ns->active is still 0. The init_ima_ns is always
set to be active, thus passing the check.

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.

Return -EACCES to IMA's securityfs files, except for the 'active' file,
until the IMA namespace has been set to active.

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.

Only emit the kernel log message 'policy update completed' for the
init_ima_ns.

When parsing an IMA policy rule use the user namespace of the opener
to translate uid and gid values to kernel values rather than the user
namespace of the writer.

Gate access to ima_appraise variable to init_ima_ns in ima_load_data()
and ima_write_policy().

Gate access to temp_ima_appraise variable to init_ima_ns in
ima_delete_rules().

Signed-off-by: Stefan Berger <stefanb@linux.ibm.com>

---
v10:
 - dropped ima_ns_to_user_ns(); using current_user_ns() instead
 - Pass user_namespace of file opener into ima_parse_rule and propagate
   this parameter back all the way to the initial caller in the chain
 - Gate access to ima_appraise to init_ima_ns in ima_write_policy()

v9:
 - ima_post_key_create_or_update: Only handle key if in init_ima_ns
 - Removed ns == NULL checks where user_namespace is now passed
 - Defer setting of user_ns->ima_ns until end of ima_fs_ns_init();
   required new ima_free_imans() and new user_ns_set_ima_ns()
 - Only emit log message 'policy update completed' for init_ima_ns
 - Introduce get_current_ns() only in this patch
 - Check for ns == &init_ima_ns in ima_load_data()
---
 include/linux/ima.h                          |  1 +
 init/Kconfig                                 | 13 +++
 kernel/user_namespace.c                      |  2 +
 security/integrity/ima/ima.h                 | 55 +++++++++++--
 security/integrity/ima/ima_appraise.c        |  3 +
 security/integrity/ima/ima_asymmetric_keys.c |  6 +-
 security/integrity/ima/ima_fs.c              | 87 ++++++++++++++++----
 security/integrity/ima/ima_init.c            |  2 +-
 security/integrity/ima/ima_init_ima_ns.c     |  2 +
 security/integrity/ima/ima_main.c            | 34 +++++---
 security/integrity/ima/ima_ns.c              | 15 +++-
 security/integrity/ima/ima_policy.c          | 43 ++++++----
 12 files changed, 202 insertions(+), 61 deletions(-)

diff --git a/include/linux/ima.h b/include/linux/ima.h
index c584527c0f47..a8cb2c269f61 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;
 
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 05e2de7697da..73df1d8a2ece 100644
--- a/security/integrity/ima/ima.h
+++ b/security/integrity/ima/ima.h
@@ -337,10 +337,10 @@ int ima_match_policy(struct ima_namespace *ns,
 		     int mask, int flags, int *pcr,
 		     struct ima_template_desc **template_desc,
 		     const char *func_data, unsigned int *allowed_algos);
-void ima_init_policy(struct ima_namespace *ns);
+void ima_init_policy(struct user_namespace *user_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);
+ssize_t ima_parse_add_rule(struct user_namespace *user_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);
@@ -538,32 +538,70 @@ 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 user_ns_set_ima_ns(). */
+	return smp_load_acquire(&user_ns->ima_ns);
 }
 
-#ifdef CONFIG_IMA_NS
+static inline void user_ns_set_ima_ns(struct user_namespace *user_ns,
+				      struct ima_namespace *ns)
+{
+	/* Pairs with smp_load_acquire() in ima_ns_from_user_ns() */
+	smp_store_release(&user_ns->ima_ns, ns);
+}
+
+static inline struct ima_namespace *get_current_ns(void)
+{
+	return ima_ns_from_user_ns(current_user_ns());
+}
 
 struct ima_namespace *create_ima_ns(void);
 
+void ima_free_ima_ns(struct ima_namespace *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);
 
+static inline struct ima_namespace *ima_ns_from_file(const struct file *filp)
+{
+	return ima_user_ns_from_file(filp)->ima_ns;
+}
+
 #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 void user_ns_set_ima_ns(struct user_namespace *user_ns,
+				      struct ima_namespace *ns)
+{
+}
+
+static inline struct ima_namespace *get_current_ns(void)
+{
+	return &init_ima_ns;
+}
+
 static inline struct ima_namespace *create_ima_ns(void)
 {
 	WARN(1, "Cannot create an IMA namespace\n");
 	return ERR_PTR(-EFAULT);
 }
 
+static inline void ima_free_ima_ns(struct ima_namespace *ns) {}
+
 static inline struct ns_status *ima_get_ns_status(struct ima_namespace *ns,
 						  struct inode *inode,
 						  struct integrity_iint_cache *iint)
@@ -578,6 +616,11 @@ static inline struct ns_status *ima_get_ns_status(struct ima_namespace *ns,
 	return ns_status;
 }
 
+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_appraise.c b/security/integrity/ima/ima_appraise.c
index 3461025f671b..2ad3327d7ce6 100644
--- a/security/integrity/ima/ima_appraise.c
+++ b/security/integrity/ima/ima_appraise.c
@@ -74,6 +74,9 @@ int ima_must_appraise(struct ima_namespace *ns,
 {
 	u32 secid;
 
+	if (ns != &init_ima_ns)
+		return 0;
+
 	if (!ima_appraise)
 		return 0;
 
diff --git a/security/integrity/ima/ima_asymmetric_keys.c b/security/integrity/ima/ima_asymmetric_keys.c
index 70d87df26068..0d2cc1e23cde 100644
--- a/security/integrity/ima/ima_asymmetric_keys.c
+++ b/security/integrity/ima/ima_asymmetric_keys.c
@@ -30,9 +30,13 @@ void ima_post_key_create_or_update(struct key *keyring, struct key *key,
 				   const void *payload, size_t payload_len,
 				   unsigned long flags, bool create)
 {
-	struct ima_namespace *ns = &init_ima_ns;
+	struct ima_namespace *ns = get_current_ns();
 	bool queued = false;
 
+	/* only handle key if related to init_ima_ns */
+	if (ns != &init_ima_ns)
+		return;
+
 	/* Only asymmetric keys are handled by this hook. */
 	if (key->type != &key_type_asymmetric)
 		return;
diff --git a/security/integrity/ima/ima_fs.c b/security/integrity/ima/ima_fs.c
index cd102bbd4677..b0e0d37a18cf 100644
--- a/security/integrity/ima/ima_fs.c
+++ b/security/integrity/ima/ima_fs.c
@@ -49,7 +49,10 @@ 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);
+
+	if (!ns_is_active(ns))
+		return -EACCES;
 
 	return ima_show_htable_value(buf, count, ppos,
 				     &ns->ima_htable.violations);
@@ -64,7 +67,10 @@ 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);
+
+	if (!ns_is_active(ns))
+		return -EACCES;
 
 	return ima_show_htable_value(buf, count, ppos, &ns->ima_htable.len);
 }
@@ -77,7 +83,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 +101,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
@@ -198,6 +204,11 @@ static const struct seq_operations ima_measurments_seqops = {
 
 static int ima_measurements_open(struct inode *inode, struct file *file)
 {
+	struct ima_namespace *ns = ima_ns_from_file(file);
+
+	if (!ns_is_active(ns))
+		return -EACCES;
+
 	return seq_open(file, &ima_measurments_seqops);
 }
 
@@ -264,6 +275,11 @@ static const struct seq_operations ima_ascii_measurements_seqops = {
 
 static int ima_ascii_measurements_open(struct inode *inode, struct file *file)
 {
+	struct ima_namespace *ns = ima_ns_from_file(file);
+
+	if (!ns_is_active(ns))
+		return -EACCES;
+
 	return seq_open(file, &ima_ascii_measurements_seqops);
 }
 
@@ -274,7 +290,7 @@ static const struct file_operations ima_ascii_measurements_ops = {
 	.release = seq_release,
 };
 
-static ssize_t ima_read_policy(struct ima_namespace *ns, char *path)
+static ssize_t ima_read_policy(struct user_namespace *user_ns, char *path)
 {
 	void *data = NULL;
 	char *datap;
@@ -299,7 +315,7 @@ static ssize_t ima_read_policy(struct ima_namespace *ns, char *path)
 	datap = data;
 	while (size > 0 && (p = strsep(&datap, "\n"))) {
 		pr_debug("rule: %s\n", p);
-		rc = ima_parse_add_rule(ns, p);
+		rc = ima_parse_add_rule(user_ns, p);
 		if (rc < 0)
 			break;
 		size -= rc;
@@ -317,10 +333,14 @@ 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 user_namespace *user_ns = ima_user_ns_from_file(file);
+	struct ima_namespace *ns = ima_ns_from_user_ns(user_ns);
 	char *data;
 	ssize_t result;
 
+	if (!ns_is_active(ns))
+		return -EACCES;
+
 	if (datalen >= PAGE_SIZE)
 		datalen = PAGE_SIZE - 1;
 
@@ -340,15 +360,16 @@ static ssize_t ima_write_policy(struct file *file, const char __user *buf,
 		goto out_free;
 
 	if (data[0] == '/') {
-		result = ima_read_policy(ns, data);
-	} else if (ima_appraise & IMA_APPRAISE_POLICY) {
+		result = ima_read_policy(user_ns, data);
+	} else if (ns == &init_ima_ns &&
+		   (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,
 				    "policy_update", "signed policy required",
 				    1, 0);
 		result = -EACCES;
 	} else {
-		result = ima_parse_add_rule(ns, data);
+		result = ima_parse_add_rule(user_ns, data);
 	}
 	mutex_unlock(&ns->ima_write_mutex);
 out_free:
@@ -381,7 +402,10 @@ static int ima_open_policy(struct inode *inode, struct file *filp)
 #ifdef CONFIG_IMA_READ_POLICY
 	struct user_namespace *user_ns = ima_user_ns_from_file(filp);
 #endif
-	struct ima_namespace *ns = &init_ima_ns;
+	struct ima_namespace *ns = ima_ns_from_file(filp);
+
+	if (!ns_is_active(ns))
+		return -EACCES;
 
 	if (!(filp->f_flags & O_WRONLY)) {
 #ifndef	CONFIG_IMA_READ_POLICY
@@ -408,7 +432,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";
 	int err = 0;
 
@@ -426,7 +450,8 @@ static int ima_release_policy(struct inode *inode, struct file *file)
 		}
 	}
 
-	pr_info("policy update %s\n", cause);
+	if (ns == &init_ima_ns)
+		pr_info("policy update %s\n", cause);
 	integrity_audit_message(AUDIT_INTEGRITY_STATUS, NULL, NULL,
 				"policy_update", cause, !ns->valid_policy, 0,
 				-err);
@@ -463,7 +488,7 @@ static ssize_t ima_show_active(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);
 	char tmpbuf[2];
 	ssize_t len;
 
@@ -476,7 +501,7 @@ static ssize_t ima_write_active(struct file *filp,
 				const 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);
 	unsigned int active;
 	char *kbuf;
 	int err;
@@ -500,7 +525,9 @@ static ssize_t ima_write_active(struct file *filp,
 	if (active != 1)
 		return -EINVAL;
 
-	atomic_set(&ns->active, 1);
+	err = ima_init_namespace(ns);
+	if (err)
+		return -EINVAL;
 
 	return count;
 }
@@ -536,11 +563,28 @@ int ima_fs_ns_init(struct user_namespace *user_ns, struct dentry *root)
 	struct dentry *violations = NULL;
 	int ret;
 
+	/*
+	 * 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();
+		if (IS_ERR(ns))
+			return PTR_ERR(ns);
+	}
+
 	/* 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);
+		if (IS_ERR(int_dir)) {
+			ret = PTR_ERR(int_dir);
+			goto free_ns;
+		}
 	} else {
 		int_dir = integrity_dir;
 	}
@@ -610,6 +654,9 @@ int ima_fs_ns_init(struct user_namespace *user_ns, struct dentry *root)
 			goto out;
 	}
 
+	if (!ima_ns_from_user_ns(user_ns))
+		user_ns_set_ima_ns(user_ns, ns);
+
 	return 0;
 out:
 	securityfs_remove(ns->ima_policy);
@@ -622,6 +669,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 (!ima_ns_from_user_ns(user_ns))
+		ima_free_ima_ns(ns);
+
 	return ret;
 }
 
diff --git a/security/integrity/ima/ima_init.c b/security/integrity/ima/ima_init.c
index 22ca5d872be0..a14193c8f99b 100644
--- a/security/integrity/ima/ima_init.c
+++ b/security/integrity/ima/ima_init.c
@@ -150,7 +150,7 @@ int __init ima_init(void)
 	if (rc != 0)
 		return rc;
 
-	ima_init_policy(&init_ima_ns);
+	ima_init_policy(&init_user_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 39ee0c2477a6..0ff587b874bd 100644
--- a/security/integrity/ima/ima_init_ima_ns.c
+++ b/security/integrity/ima/ima_init_ima_ns.c
@@ -46,6 +46,8 @@ int ima_init_namespace(struct ima_namespace *ns)
 			return rc;
 	}
 
+	atomic_set(&ns->active, 1);
+
 	return 0;
 }
 
diff --git a/security/integrity/ima/ima_main.c b/security/integrity/ima/ima_main.c
index 9ca9223bbcf6..1ffac9c04df1 100644
--- a/security/integrity/ima/ima_main.c
+++ b/security/integrity/ima/ima_main.c
@@ -506,7 +506,7 @@ int ima_file_mmap(struct file *file, unsigned long prot)
  */
 int ima_file_mprotect(struct vm_area_struct *vma, unsigned long prot)
 {
-	struct ima_namespace *ns = &init_ima_ns;
+	struct ima_namespace *ns = get_current_ns();
 	struct ima_template_desc *template = NULL;
 	struct file *file = vma->vm_file;
 	char filename[NAME_MAX];
@@ -519,7 +519,8 @@ 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_is_active(ns) ||
+	    !(ns->ima_policy_flag & IMA_APPRAISE) || !vma->vm_file ||
 	    !(prot & PROT_EXEC) || (vma->vm_flags & VM_EXEC))
 		return 0;
 
@@ -655,9 +656,9 @@ static int __ima_inode_hash(struct ima_namespace *ns,
  */
 int ima_file_hash(struct file *file, char *buf, size_t buf_size)
 {
-	struct ima_namespace *ns = &init_ima_ns;
+	struct ima_namespace *ns = get_current_ns();
 
-	if (!file)
+	if (!ns_is_active(ns) || !file)
 		return -EINVAL;
 
 	return __ima_inode_hash(ns, file_inode(file), buf, buf_size);
@@ -684,9 +685,9 @@ EXPORT_SYMBOL_GPL(ima_file_hash);
  */
 int ima_inode_hash(struct inode *inode, char *buf, size_t buf_size)
 {
-	struct ima_namespace *ns = &init_ima_ns;
+	struct ima_namespace *ns = get_current_ns();
 
-	if (!inode)
+	if (!ns_is_active(ns) || !inode)
 		return -EINVAL;
 
 	return __ima_inode_hash(ns, inode, buf, buf_size);
@@ -705,11 +706,11 @@ EXPORT_SYMBOL_GPL(ima_inode_hash);
 void ima_post_create_tmpfile(struct user_namespace *mnt_userns,
 			     struct inode *inode)
 {
-	struct ima_namespace *ns = &init_ima_ns;
+	struct ima_namespace *ns = get_current_ns();
 	struct integrity_iint_cache *iint;
 	int must_appraise;
 
-	if (!ns->ima_policy_flag || !S_ISREG(inode->i_mode))
+	if (!ns_is_active(ns) || !ns->ima_policy_flag || !S_ISREG(inode->i_mode))
 		return;
 
 	must_appraise = ima_must_appraise(ns, mnt_userns, inode, MAY_ACCESS,
@@ -738,12 +739,12 @@ void ima_post_create_tmpfile(struct user_namespace *mnt_userns,
 void ima_post_path_mknod(struct user_namespace *mnt_userns,
 			 struct dentry *dentry)
 {
-	struct ima_namespace *ns = &init_ima_ns;
+	struct ima_namespace *ns = get_current_ns();
 	struct integrity_iint_cache *iint;
 	struct inode *inode = dentry->d_inode;
 	int must_appraise;
 
-	if (!ns->ima_policy_flag || !S_ISREG(inode->i_mode))
+	if (!ns_is_active(ns) || !ns->ima_policy_flag || !S_ISREG(inode->i_mode))
 		return;
 
 	must_appraise = ima_must_appraise(ns, mnt_userns, inode, MAY_ACCESS,
@@ -862,8 +863,12 @@ int ima_post_read_file(struct file *file, void *buf, loff_t size,
  */
 int ima_load_data(enum kernel_load_data_id id, bool contents)
 {
+	struct ima_namespace *ns = get_current_ns();
 	bool ima_enforce, sig_enforce;
 
+	if (ns != &init_ima_ns)
+		return 0;
+
 	ima_enforce =
 		(ima_appraise & IMA_APPRAISE_ENFORCE) == IMA_APPRAISE_ENFORCE;
 
@@ -1071,10 +1076,10 @@ int process_buffer_measurement(struct ima_namespace *ns,
  */
 void ima_kexec_cmdline(int kernel_fd, const void *buf, int size)
 {
-	struct ima_namespace *ns = &init_ima_ns;
+	struct ima_namespace *ns = get_current_ns();
 	struct fd f;
 
-	if (!buf || !size)
+	if (!ns_is_active(ns) || !buf || !size)
 		return;
 
 	f = fdget(kernel_fd);
@@ -1112,7 +1117,10 @@ int ima_measure_critical_data(const char *event_label,
 			      const void *buf, size_t buf_len,
 			      bool hash, u8 *digest, size_t digest_len)
 {
-	struct ima_namespace *ns = &init_ima_ns;
+	struct ima_namespace *ns = get_current_ns();
+
+	if (!ns_is_active(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 29af6fea2d74..244dc9d66fb1 100644
--- a/security/integrity/ima/ima_ns.c
+++ b/security/integrity/ima/ima_ns.c
@@ -26,22 +26,29 @@ struct ima_namespace *create_ima_ns(void)
 /* destroy_ima_ns() must only be called after ima_init_namespace() was called */
 static void destroy_ima_ns(struct ima_namespace *ns)
 {
+	atomic_set(&ns->active, 0);
 	unregister_blocking_lsm_notifier(&ns->ima_lsm_policy_notifier);
 	kfree(ns->arch_policy_entry);
 	ima_free_policy_rules(ns);
 	ima_free_ns_status_tree(ns);
 }
 
-void free_ima_ns(struct user_namespace *user_ns)
+void ima_free_ima_ns(struct ima_namespace *ns)
 {
-	struct ima_namespace *ns = user_ns->ima_ns;
-
 	if (!ns || WARN_ON(ns == &init_ima_ns))
 		return;
 
-	destroy_ima_ns(ns);
+	if (ns_is_active(ns))
+		destroy_ima_ns(ns);
 
 	kmem_cache_free(imans_cachep, ns);
+}
+
+void free_ima_ns(struct user_namespace *user_ns)
+{
+	struct ima_namespace *ns = ima_ns_from_user_ns(user_ns);
+
+	ima_free_ima_ns(ns);
 
 	user_ns->ima_ns = NULL;
 }
diff --git a/security/integrity/ima/ima_policy.c b/security/integrity/ima/ima_policy.c
index 3f84d04f101d..c0bbae550994 100644
--- a/security/integrity/ima/ima_policy.c
+++ b/security/integrity/ima/ima_policy.c
@@ -474,7 +474,8 @@ int ima_lsm_policy_change(struct notifier_block *nb, unsigned long event,
 		return NOTIFY_DONE;
 
 	ns = container_of(nb, struct ima_namespace, ima_lsm_policy_notifier);
-	ima_lsm_update_rules(ns);
+	if (ns_is_active(ns))
+		ima_lsm_update_rules(ns);
 
 	return NOTIFY_OK;
 }
@@ -844,11 +845,12 @@ static void add_rules(struct ima_namespace *ns,
 	}
 }
 
-static int ima_parse_rule(struct ima_namespace *ns,
+static int ima_parse_rule(struct user_namespace *user_ns,
 			  char *rule, struct ima_rule_entry *entry);
 
-static int __init ima_init_arch_policy(struct ima_namespace *ns)
+static int __init ima_init_arch_policy(struct user_namespace *user_ns)
 {
+	struct ima_namespace *ns = ima_ns_from_user_ns(user_ns);
 	const char * const *arch_rules;
 	const char * const *rules;
 	int arch_entries = 0;
@@ -876,7 +878,7 @@ static int __init ima_init_arch_policy(struct ima_namespace *ns)
 		result = strscpy(rule, *rules, sizeof(rule));
 
 		INIT_LIST_HEAD(&ns->arch_policy_entry[i].list);
-		result = ima_parse_rule(ns, rule, &ns->arch_policy_entry[i]);
+		result = ima_parse_rule(user_ns, rule, &ns->arch_policy_entry[i]);
 		if (result) {
 			pr_warn("Skipping unknown architecture policy rule: %s\n",
 				rule);
@@ -891,11 +893,12 @@ static int __init ima_init_arch_policy(struct ima_namespace *ns)
 
 /**
  * ima_init_policy - initialize the default measure rules.
- * @ns: IMA namespace to which the policy belongs to
+ * @user_ns: User namespace pointig to IMA namespace to which the policy belongs
  * ima_rules points to either the ima_default_rules or the new ima_policy_rules.
  */
-void __init ima_init_policy(struct ima_namespace *ns)
+void __init ima_init_policy(struct user_namespace *user_ns)
 {
+	struct ima_namespace *ns = ima_ns_from_user_ns(user_ns);
 	int build_appraise_entries, arch_entries;
 
 	/* if !ima_policy, we load NO default rules */
@@ -925,7 +928,7 @@ void __init ima_init_policy(struct ima_namespace *ns)
 	 * and custom policies, prior to other appraise rules.
 	 * (Highest priority)
 	 */
-	arch_entries = ima_init_arch_policy(ns);
+	arch_entries = ima_init_arch_policy(user_ns);
 	if (!arch_entries)
 		pr_info("No architecture policies found\n");
 	else
@@ -1346,9 +1349,10 @@ static unsigned int ima_parse_appraise_algos(char *arg)
 	return res;
 }
 
-static int ima_parse_rule(struct ima_namespace *ns,
+static int ima_parse_rule(struct user_namespace *user_ns,
 			  char *rule, struct ima_rule_entry *entry)
 {
+	struct ima_namespace *ns = ima_ns_from_user_ns(user_ns);
 	struct audit_buffer *ab;
 	char *from;
 	char *p;
@@ -1598,7 +1602,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)
@@ -1633,7 +1637,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))
@@ -1660,7 +1664,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))
@@ -1686,7 +1690,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))
@@ -1840,14 +1844,15 @@ static int ima_parse_rule(struct ima_namespace *ns,
 
 /**
  * ima_parse_add_rule - add a rule to ima_policy_rules
- * @ns: IMA namespace that has the policy
+ * @user_ns: User namespace referencing the 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(struct ima_namespace *ns, char *rule)
+ssize_t ima_parse_add_rule(struct user_namespace *user_ns, char *rule)
 {
+	struct ima_namespace *ns = ima_ns_from_user_ns(user_ns);
 	static const char op[] = "update_policy";
 	char *p;
 	struct ima_rule_entry *entry;
@@ -1870,7 +1875,7 @@ ssize_t ima_parse_add_rule(struct ima_namespace *ns, char *rule)
 
 	INIT_LIST_HEAD(&entry->list);
 
-	result = ima_parse_rule(ns, p, entry);
+	result = ima_parse_rule(user_ns, p, entry);
 	if (result) {
 		ima_free_rule(entry);
 		integrity_audit_msg(AUDIT_INTEGRITY_STATUS, NULL,
@@ -1895,7 +1900,9 @@ void ima_delete_rules(struct ima_namespace *ns)
 {
 	struct ima_rule_entry *entry, *tmp;
 
-	temp_ima_appraise = 0;
+	if (ns == &init_ima_ns)
+		temp_ima_appraise = 0;
+
 	list_for_each_entry_safe(entry, tmp, &ns->ima_temp_rules, list) {
 		list_del(&entry->list);
 		ima_free_rule(entry);
@@ -1936,7 +1943,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;
@@ -1955,7 +1962,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] 86+ messages in thread

* [PATCH] ima: fix semicolon.cocci warnings
  2022-02-01 20:37 ` [PATCH v10 15/27] ima: Implement hierarchical processing of file accesses Stefan Berger
@ 2022-02-02 13:19     ` kernel test robot
  2022-02-02 13:19     ` kernel test robot
  2022-02-18 16:27   ` Mimi Zohar
  2 siblings, 0 replies; 86+ messages in thread
From: kernel test robot @ 2022-02-02 13:19 UTC (permalink / raw)
  To: Stefan Berger, linux-integrity
  Cc: kbuild-all, zohar, serge, christian.brauner, containers,
	dmitry.kasatkin, ebiederm, krzysztof.struczynski, roberto.sassu,
	mpeters

From: kernel test robot <lkp@intel.com>

security/integrity/ima/ima_main.c:424:2-3: Unneeded semicolon


 Remove unneeded semicolon.

Generated by: scripts/coccinelle/misc/semicolon.cocci

CC: Stefan Berger <stefanb@linux.ibm.com>
Reported-by: kernel test robot <lkp@intel.com>
Signed-off-by: kernel test robot <lkp@intel.com>
---

url:    https://github.com/0day-ci/linux/commits/Stefan-Berger/ima-Namespace-IMA-with-audit-support-in-IMA-ns/20220202-044058
base:    df0cc57e057f18e44dac8e6c18aba47ab53202f9
:::::: branch date: 17 hours ago
:::::: commit date: 17 hours ago

 ima_main.c |    2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

--- a/security/integrity/ima/ima_main.c
+++ b/security/integrity/ima/ima_main.c
@@ -421,7 +421,7 @@ static int process_measurement(struct us
 		}
 
 		user_ns = user_ns->parent;
-	};
+	}
 
 	return ret;
 }

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

* [PATCH] ima: fix semicolon.cocci warnings
@ 2022-02-02 13:19     ` kernel test robot
  0 siblings, 0 replies; 86+ messages in thread
From: kernel test robot @ 2022-02-02 13:19 UTC (permalink / raw)
  To: kbuild-all

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

From: kernel test robot <lkp@intel.com>

security/integrity/ima/ima_main.c:424:2-3: Unneeded semicolon


 Remove unneeded semicolon.

Generated by: scripts/coccinelle/misc/semicolon.cocci

CC: Stefan Berger <stefanb@linux.ibm.com>
Reported-by: kernel test robot <lkp@intel.com>
Signed-off-by: kernel test robot <lkp@intel.com>
---

url:    https://github.com/0day-ci/linux/commits/Stefan-Berger/ima-Namespace-IMA-with-audit-support-in-IMA-ns/20220202-044058
base:    df0cc57e057f18e44dac8e6c18aba47ab53202f9
:::::: branch date: 17 hours ago
:::::: commit date: 17 hours ago

 ima_main.c |    2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

--- a/security/integrity/ima/ima_main.c
+++ b/security/integrity/ima/ima_main.c
@@ -421,7 +421,7 @@ static int process_measurement(struct us
 		}
 
 		user_ns = user_ns->parent;
-	};
+	}
 
 	return ret;
 }

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

* Re: [PATCH v10 15/27] ima: Implement hierarchical processing of file accesses
  2022-02-01 20:37 ` [PATCH v10 15/27] ima: Implement hierarchical processing of file accesses Stefan Berger
@ 2022-02-02 13:19     ` kernel test robot
  2022-02-02 13:19     ` kernel test robot
  2022-02-18 16:27   ` Mimi Zohar
  2 siblings, 0 replies; 86+ messages in thread
From: kernel test robot @ 2022-02-02 13:19 UTC (permalink / raw)
  To: Stefan Berger, linux-integrity
  Cc: 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 v5.16]
[cannot apply to zohar-integrity/next-integrity linux/master linus/master jmorris-security/next-testing v5.17-rc2 v5.17-rc1 next-20220202]
[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/20220202-044058
base:    df0cc57e057f18e44dac8e6c18aba47ab53202f9
config: x86_64-randconfig-c003-20220131 (https://download.01.org/0day-ci/archive/20220202/202202022159.CBH5XW23-lkp@intel.com/config)
compiler: gcc-9 (Debian 9.3.0-22) 9.3.0

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


cocci warnings: (new ones prefixed by >>)
>> security/integrity/ima/ima_main.c:424:2-3: Unneeded semicolon

Please review and possibly fold the followup patch.

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

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

* Re: [PATCH v10 15/27] ima: Implement hierarchical processing of file accesses
@ 2022-02-02 13:19     ` kernel test robot
  0 siblings, 0 replies; 86+ messages in thread
From: kernel test robot @ 2022-02-02 13:19 UTC (permalink / raw)
  To: kbuild-all

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

Hi Stefan,

Thank you for the patch! Perhaps something to improve:

[auto build test WARNING on v5.16]
[cannot apply to zohar-integrity/next-integrity linux/master linus/master jmorris-security/next-testing v5.17-rc2 v5.17-rc1 next-20220202]
[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/20220202-044058
base:    df0cc57e057f18e44dac8e6c18aba47ab53202f9
config: x86_64-randconfig-c003-20220131 (https://download.01.org/0day-ci/archive/20220202/202202022159.CBH5XW23-lkp(a)intel.com/config)
compiler: gcc-9 (Debian 9.3.0-22) 9.3.0

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


cocci warnings: (new ones prefixed by >>)
>> security/integrity/ima/ima_main.c:424:2-3: Unneeded semicolon

Please review and possibly fold the followup patch.

---
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] 86+ messages in thread

* Re: [PATCH v10 00/27] ima: Namespace IMA with audit support in IMA-ns
  2022-02-01 20:37 [PATCH v10 00/27] ima: Namespace IMA with audit support in IMA-ns Stefan Berger
                   ` (26 preceding siblings ...)
  2022-02-01 20:37 ` [PATCH v10 27/27] ima: Enable IMA namespaces Stefan Berger
@ 2022-02-02 14:13 ` Christian Brauner
  2022-02-02 14:40   ` Stefan Berger
  27 siblings, 1 reply; 86+ messages in thread
From: Christian Brauner @ 2022-02-02 14:13 UTC (permalink / raw)
  To: Stefan Berger, serge
  Cc: linux-integrity, zohar, 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, Feb 01, 2022 at 03:37:08PM -0500, Stefan Berger wrote:
> 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 1 > /mnt/ima/active; \
>   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.
> 
> In the above the writing of '1' to the 'active' file is used to activate
> the IMA namespace. Future extensions to IMA namespaces will make use of
> the configuration stage after the mounting of securityfs and before the
> activation to for example choose the measurement log template.
> 
> 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+imans.v10.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: https://lore.kernel.org/all/20220104170416.1923685-1-stefanb@linux.vnet.ibm.com/#r
> v9: https://lore.kernel.org/linux-integrity/?t=20220131234353
> 
> v10:
>  - Added A-b's; addressed issues from v9
>  - Added 2 patches to support freeing of iint after namespace deletion
>  - Added patch to return error code from securityfs functions
>  - Added patch to limit number of policy rules in IMA-ns to 1024

I'm going to go take a lighter touch with this round of reviews.
First, because I have February off. :)
Second, because I think that someone who is more familiar with IMA and
its requirements should take another look to provide input and ask more
questions. Last time I spoke to Serge he did want to give this a longer
look and maybe also has additional questions.

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

* Re: [PATCH v10 02/27] ima: Do not print policy rule with inactive LSM labels
  2022-02-01 20:37 ` [PATCH v10 02/27] ima: Do not print policy rule with inactive LSM labels Stefan Berger
@ 2022-02-02 14:17   ` Christian Brauner
  0 siblings, 0 replies; 86+ messages in thread
From: Christian Brauner @ 2022-02-02 14:17 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

On Tue, Feb 01, 2022 at 03:37:10PM -0500, Stefan Berger wrote:
> Before printing a policy rule scan for inactive LSM labels in the policy
> rule. Inactive LSM labels are identified by args_p != NULL and
> rule == NULL.
> 
> Fixes: b16942455193 ("ima: use the lsm policy update notifier")
> Signed-off-by: Stefan Berger <stefanb@linux.ibm.com>
> ---

Ok,
Acked-by: Christian Brauner <brauner@kernel.org>

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

* Re: [PATCH v10 00/27] ima: Namespace IMA with audit support in IMA-ns
  2022-02-02 14:13 ` [PATCH v10 00/27] ima: Namespace IMA with audit support in IMA-ns Christian Brauner
@ 2022-02-02 14:40   ` Stefan Berger
  2022-02-02 16:04     ` Mimi Zohar
  0 siblings, 1 reply; 86+ messages in thread
From: Stefan Berger @ 2022-02-02 14:40 UTC (permalink / raw)
  To: Christian Brauner, serge
  Cc: linux-integrity, zohar, 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 2/2/22 09:13, Christian Brauner wrote:
> On Tue, Feb 01, 2022 at 03:37:08PM -0500, Stefan Berger wrote:
>>
>> v10:
>>   - Added A-b's; addressed issues from v9
>>   - Added 2 patches to support freeing of iint after namespace deletion
>>   - Added patch to return error code from securityfs functions
>>   - Added patch to limit number of policy rules in IMA-ns to 1024
> I'm going to go take a lighter touch with this round of reviews.
> First, because I have February off. :)
> Second, because I think that someone who is more familiar with IMA and
> its requirements should take another look to provide input and ask more
> questions. Last time I spoke to Serge he did want to give this a longer
> look and maybe also has additional questions.

The one problem I am seeing is that we probably cannot support auditing 
in IMA namespaces since every user can now create an IMA namespace. 
Unless auditing was namespaced, the way it is now gives too much control 
to the user to flood the host audit log. So, we may need to head towards 
support for IMA measurements in the IMA namespace right away and not 
support audit rules but also possibly eliminate other actions that are 
being audited by IMA to not occur while an IMA namespace is active, such 
as when policy rules are being set etc. Not supporting auditing in 
IMA-ns affects only few of the patches in this series. We need most of 
them for a basis of IMA measurements but to get to IMA measurements 
along with support for inheritance and configuration of hash algorithm 
and log template etc. to use in the IMA namespace and set it in its 
configuration 'stage' (before activation), we will need at least 25 more 
patches on top of what have here now... so this series will then be 
around 50 patches.

    Stefan


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

* Re: [PATCH v10 00/27] ima: Namespace IMA with audit support in IMA-ns
  2022-02-02 14:40   ` Stefan Berger
@ 2022-02-02 16:04     ` Mimi Zohar
  2022-02-02 18:18       ` Stefan Berger
  0 siblings, 1 reply; 86+ messages in thread
From: Mimi Zohar @ 2022-02-02 16:04 UTC (permalink / raw)
  To: Stefan Berger, Christian Brauner, serge
  Cc: linux-integrity, 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, 2022-02-02 at 09:40 -0500, Stefan Berger wrote:
> On 2/2/22 09:13, Christian Brauner wrote:
> > On Tue, Feb 01, 2022 at 03:37:08PM -0500, Stefan Berger wrote:
> >>
> >> v10:
> >>   - Added A-b's; addressed issues from v9
> >>   - Added 2 patches to support freeing of iint after namespace deletion
> >>   - Added patch to return error code from securityfs functions
> >>   - Added patch to limit number of policy rules in IMA-ns to 1024
> > I'm going to go take a lighter touch with this round of reviews.
> > First, because I have February off. :)
> > Second, because I think that someone who is more familiar with IMA and
> > its requirements should take another look to provide input and ask more
> > questions. Last time I spoke to Serge he did want to give this a longer
> > look and maybe also has additional questions.
> 
> The one problem I am seeing is that we probably cannot support auditing 
> in IMA namespaces since every user can now create an IMA namespace. 
> Unless auditing was namespaced, the way it is now gives too much control 
> to the user to flood the host audit log.

Stefan, we need to differentiate between the different types of audit
records being produced by IMA.  Some of these are informational, like
the policy rules being loaded or "Time of Measure, Time of Use"
(ToMToU) records.  When we discuss IMA-audit we're referring to the
file hashes being added in the audit log.  These are the result of the
IMA "audit" policy rules.

How much of these informational messages should be audited in IMA
namespaces still needs to be discussed.  For now, feel free to limit
the audit messages to just the file hashes.

thanks,

Mimi


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

* Re: [PATCH v10 00/27] ima: Namespace IMA with audit support in IMA-ns
  2022-02-02 16:04     ` Mimi Zohar
@ 2022-02-02 18:18       ` Stefan Berger
  2022-02-02 21:27         ` Stefan Berger
  0 siblings, 1 reply; 86+ messages in thread
From: Stefan Berger @ 2022-02-02 18:18 UTC (permalink / raw)
  To: Mimi Zohar, Christian Brauner, serge
  Cc: linux-integrity, 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 2/2/22 11:04, Mimi Zohar wrote:
> On Wed, 2022-02-02 at 09:40 -0500, Stefan Berger wrote:
>> On 2/2/22 09:13, Christian Brauner wrote:
>>> On Tue, Feb 01, 2022 at 03:37:08PM -0500, Stefan Berger wrote:
>>>> v10:
>>>>    - Added A-b's; addressed issues from v9
>>>>    - Added 2 patches to support freeing of iint after namespace deletion
>>>>    - Added patch to return error code from securityfs functions
>>>>    - Added patch to limit number of policy rules in IMA-ns to 1024
>>> I'm going to go take a lighter touch with this round of reviews.
>>> First, because I have February off. :)
>>> Second, because I think that someone who is more familiar with IMA and
>>> its requirements should take another look to provide input and ask more
>>> questions. Last time I spoke to Serge he did want to give this a longer
>>> look and maybe also has additional questions.
>> The one problem I am seeing is that we probably cannot support auditing
>> in IMA namespaces since every user can now create an IMA namespace.
>> Unless auditing was namespaced, the way it is now gives too much control
>> to the user to flood the host audit log.
> Stefan, we need to differentiate between the different types of audit
> records being produced by IMA.  Some of these are informational, like
> the policy rules being loaded or "Time of Measure, Time of Use"
> (ToMToU) records.  When we discuss IMA-audit we're referring to the
> file hashes being added in the audit log.  These are the result of the
> IMA "audit" policy rules.
>
> How much of these informational messages should be audited in IMA
> namespaces still needs to be discussed.  For now, feel free to limit
> the audit messages to just the file hashes.
I doubt we should let a user produce informational audit messages or 
audit messages related to file hashes... it's unfortunate, but it opens 
a door for abuse.

> thanks,
>
> Mimi
>

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

* Re: [PATCH v10 00/27] ima: Namespace IMA with audit support in IMA-ns
  2022-02-02 18:18       ` Stefan Berger
@ 2022-02-02 21:27         ` Stefan Berger
  0 siblings, 0 replies; 86+ messages in thread
From: Stefan Berger @ 2022-02-02 21:27 UTC (permalink / raw)
  To: Mimi Zohar, Christian Brauner, serge
  Cc: linux-integrity, 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 2/2/22 13:18, Stefan Berger wrote:
>
> On 2/2/22 11:04, Mimi Zohar wrote:
>> Stefan, we need to differentiate between the different types of audit
>> records being produced by IMA.  Some of these are informational, like
>> the policy rules being loaded or "Time of Measure, Time of Use"
>> (ToMToU) records.  When we discuss IMA-audit we're referring to the
>> file hashes being added in the audit log.  These are the result of the
>> IMA "audit" policy rules.
>>
>> How much of these informational messages should be audited in IMA
>> namespaces still needs to be discussed.  For now, feel free to limit
>> the audit messages to just the file hashes.
> I doubt we should let a user produce informational audit messages or 
> audit messages related to file hashes... it's unfortunate, but it 
> opens a door for abuse.

After some offline discussion with Mimi, the solution may be to gate 
setting IMA audit policy rules with CAP_SYS_ADMIN.

    Stefan

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

* Re: [PATCH v10 12/27] ima: Define mac_admin_ns_capable() as a wrapper for ns_capable()
  2022-02-01 20:37 ` [PATCH v10 12/27] ima: Define mac_admin_ns_capable() as a wrapper for ns_capable() Stefan Berger
@ 2022-02-05  5:58   ` Serge E. Hallyn
  2022-02-06 17:20     ` Stefan Berger
  0 siblings, 1 reply; 86+ messages in thread
From: Serge E. Hallyn @ 2022-02-05  5: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, Denis Semakin

On Tue, Feb 01, 2022 at 03:37:20PM -0500, Stefan Berger wrote:
> Define mac_admin_ns_capable() as a wrapper for the combined ns_capable()
> checks on CAP_MAC_ADMIN and CAP_SYS_ADMIN in a user namespace. Return
> true on the check if either capability or both are available.
> 
> Use mac_admin_ns_capable() in place of capable(SYS_ADMIN). This will allow
> an IMA namespace to read the policy with only CAP_MAC_ADMIN, which has
> less privileges than CAP_SYS_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 | 5 ++++-
>  3 files changed, 16 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);

Do you care about audit warnings?  If the task has CAP_SYS_ADMIN but
not CAP_MAC_ADMIN, is it desirable that selinux_capable() will audit the
CAP_MAC_ADMIN failure?

> +}
> +
>  /* 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 fb6bd054d899..0057b1fd6c18 100644
> --- a/security/integrity/ima/ima.h
> +++ b/security/integrity/ima/ima.h
> @@ -487,4 +487,10 @@ 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;
> +}
> +
>  #endif /* __LINUX_IMA_H */
> diff --git a/security/integrity/ima/ima_fs.c b/security/integrity/ima/ima_fs.c
> index 89d3113ceda1..c41aa61b7393 100644
> --- a/security/integrity/ima/ima_fs.c
> +++ b/security/integrity/ima/ima_fs.c
> @@ -377,6 +377,9 @@ static const struct seq_operations ima_policy_seqops = {
>   */
>  static int ima_open_policy(struct inode *inode, struct file *filp)
>  {
> +#ifdef CONFIG_IMA_READ_POLICY
> +	struct user_namespace *user_ns = ima_user_ns_from_file(filp);
> +#endif
>  	struct ima_namespace *ns = &init_ima_ns;
>  
>  	if (!(filp->f_flags & O_WRONLY)) {
> @@ -385,7 +388,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	[flat|nested] 86+ messages in thread

* Re: [PATCH v10 12/27] ima: Define mac_admin_ns_capable() as a wrapper for ns_capable()
  2022-02-05  5:58   ` Serge E. Hallyn
@ 2022-02-06 17:20     ` Stefan Berger
  2022-02-07 18:43       ` Stefan Berger
  0 siblings, 1 reply; 86+ messages in thread
From: Stefan Berger @ 2022-02-06 17:20 UTC (permalink / raw)
  To: Serge E. Hallyn
  Cc: linux-integrity, zohar, christian.brauner, containers,
	dmitry.kasatkin, ebiederm, krzysztof.struczynski, roberto.sassu,
	mpeters, lhinds, lsturman, puiterwi, jejb, jamjoom, linux-kernel,
	paul, rgb, linux-security-module, jmorris, Denis Semakin


On 2/5/22 00:58, Serge E. Hallyn wrote:
> On Tue, Feb 01, 2022 at 03:37:20PM -0500, Stefan Berger wrote:
>> Define mac_admin_ns_capable() as a wrapper for the combined ns_capable()
>> checks on CAP_MAC_ADMIN and CAP_SYS_ADMIN in a user namespace. Return
>> true on the check if either capability or both are available.
>>
>> Use mac_admin_ns_capable() in place of capable(SYS_ADMIN). This will allow
>> an IMA namespace to read the policy with only CAP_MAC_ADMIN, which has
>> less privileges than CAP_SYS_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 | 5 ++++-
>>   3 files changed, 16 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);
> Do you care about audit warnings?  If the task has CAP_SYS_ADMIN but
> not CAP_MAC_ADMIN, is it desirable that selinux_capable() will audit the
> CAP_MAC_ADMIN failure?

Good point.  I will switch both to ns_capable_noaudit() so that the user 
cannot provoke unnecessary audit message.

Thanks.

     Stefan

>
>> +}
>> +
>>   /* 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 fb6bd054d899..0057b1fd6c18 100644
>> --- a/security/integrity/ima/ima.h
>> +++ b/security/integrity/ima/ima.h
>> @@ -487,4 +487,10 @@ 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;
>> +}
>> +
>>   #endif /* __LINUX_IMA_H */
>> diff --git a/security/integrity/ima/ima_fs.c b/security/integrity/ima/ima_fs.c
>> index 89d3113ceda1..c41aa61b7393 100644
>> --- a/security/integrity/ima/ima_fs.c
>> +++ b/security/integrity/ima/ima_fs.c
>> @@ -377,6 +377,9 @@ static const struct seq_operations ima_policy_seqops = {
>>    */
>>   static int ima_open_policy(struct inode *inode, struct file *filp)
>>   {
>> +#ifdef CONFIG_IMA_READ_POLICY
>> +	struct user_namespace *user_ns = ima_user_ns_from_file(filp);
>> +#endif
>>   	struct ima_namespace *ns = &init_ima_ns;
>>   
>>   	if (!(filp->f_flags & O_WRONLY)) {
>> @@ -385,7 +388,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	[flat|nested] 86+ messages in thread

* Re: [PATCH v10 12/27] ima: Define mac_admin_ns_capable() as a wrapper for ns_capable()
  2022-02-06 17:20     ` Stefan Berger
@ 2022-02-07 18:43       ` Stefan Berger
  2022-02-23 17:51         ` Serge E. Hallyn
  0 siblings, 1 reply; 86+ messages in thread
From: Stefan Berger @ 2022-02-07 18:43 UTC (permalink / raw)
  To: Serge E. Hallyn
  Cc: linux-integrity, zohar, christian.brauner, containers,
	dmitry.kasatkin, ebiederm, krzysztof.struczynski, roberto.sassu,
	mpeters, lhinds, lsturman, puiterwi, jejb, jamjoom, linux-kernel,
	paul, rgb, linux-security-module, jmorris, Denis Semakin


On 2/6/22 12:20, Stefan Berger wrote:
>
> On 2/5/22 00:58, Serge E. Hallyn wrote:
>> On Tue, Feb 01, 2022 at 03:37:20PM -0500, Stefan Berger wrote:
>>> Define mac_admin_ns_capable() as a wrapper for the combined 
>>> ns_capable()
>>> checks on CAP_MAC_ADMIN and CAP_SYS_ADMIN in a user namespace. Return
>>> true on the check if either capability or both are available.
>>>
>>> Use mac_admin_ns_capable() in place of capable(SYS_ADMIN). This will 
>>> allow
>>> an IMA namespace to read the policy with only CAP_MAC_ADMIN, which has
>>> less privileges than CAP_SYS_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 | 5 ++++-
>>>   3 files changed, 16 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);
>> Do you care about audit warnings?  If the task has CAP_SYS_ADMIN but
>> not CAP_MAC_ADMIN, is it desirable that selinux_capable() will audit the
>> CAP_MAC_ADMIN failure?
>
> Good point.  I will switch both to ns_capable_noaudit() so that the 
> user cannot provoke unnecessary audit message.

Actually,  I will only change the MAC_ADMIN to not do auditing and not 
change the auditing behavior related to SYS_ADMIN.

    Stefan



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

* Re: [PATCH v10 01/27] ima: Remove ima_policy file before directory
  2022-02-01 20:37 ` [PATCH v10 01/27] ima: Remove ima_policy file before directory Stefan Berger
@ 2022-02-10 12:02   ` Mimi Zohar
  0 siblings, 0 replies; 86+ messages in thread
From: Mimi Zohar @ 2022-02-10 12:02 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, Christian Brauner

Hi Stefan,

On Tue, 2022-02-01 at 15:37 -0500, Stefan Berger wrote:
> The removal of ima_dir currently fails since ima_policy still exists, so
> remove the ima_policy file before removing the directory.
> 
> Fixes: 4af4662fa4a9 ("integrity: IMA policy")
> Signed-off-by: Stefan Berger <stefanb@linux.ibm.com>
> Acked-by: Christian Brauner <brauner@kernel.org>

Thanks, Stefan.  I've started picking off the bug fixes and upstreaming
them.  Both this patch and 2/27 are now in Linus' tree and should be in
-rc4.

-- 
Mimi


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

* Re: [PATCH v10 03/27] ima: Return error code obtained from securityfs functions
  2022-02-01 20:37 ` [PATCH v10 03/27] ima: Return error code obtained from securityfs functions Stefan Berger
@ 2022-02-10 12:02   ` Mimi Zohar
  2022-02-15 18:09     ` Mimi Zohar
  0 siblings, 1 reply; 86+ messages in thread
From: Mimi Zohar @ 2022-02-10 12:02 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

On Tue, 2022-02-01 at 15:37 -0500, Stefan Berger wrote:
> If an error occurs when creating a securityfs file, return the exact
> error code to the caller.
> 
> Signed-off-by: Stefan Berger <stefanb@linux.ibm.com>

Thanks, Stefan.  Nice cleanup.

Reviewed-by: Mimi Zohar <zohar@linux.ibm.com>


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

* Re: [PATCH v10 04/27] securityfs: rework dentry creation
  2022-02-01 20:37 ` [PATCH v10 04/27] securityfs: rework dentry creation Stefan Berger
@ 2022-02-10 12:03   ` Mimi Zohar
  0 siblings, 0 replies; 86+ messages in thread
From: Mimi Zohar @ 2022-02-10 12:03 UTC (permalink / raw)
  To: Stefan Berger, linux-integrity
  Cc: serge, containers, dmitry.kasatkin, ebiederm,
	krzysztof.struczynski, roberto.sassu, mpeters, lhinds, lsturman,
	puiterwi, jejb, jamjoom, linux-kernel, paul, rgb,
	linux-security-module, jmorris, Christian Brauner, John Johansen,
	Matthew Garrett, Micah Morton, Kentaro Takeda

[Cc'ing JJ, Matthew, Micah, Kentaro, Casey - maintainers of securityfs
usages, not already cc'ed]

On Tue, 2022-02-01 at 15:37 -0500, Stefan Berger wrote:
> From: Christian Brauner <brauner@kernel.org>
> 
> 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>

Thanks, Christian, Stefan.

Reviewed-by: Mimi Zohar <zohar@linux.ibm.com>

This change is really independent of the IMA namespacing.  Based on
Greg's request of unification of where platform specific
variables/keys/etc are stored, the consensus so far seems to be
'securityfs/secrets'.  Although this patch isn't a bug fix, let's try
and get this upstreamed.

The current securityfs usages are apparmor, lockdown, safesetid,
tomoyo, core LSM ("security/lsm"), and the TPM.

Only on failure to create securityfs files or directories, are
previously created securityfs files/directories removed.  The one
exception seems to be the TPM, which may be built as a kernel module.

-- 
thanks,

Mimi


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

* Re: [PATCH v10 03/27] ima: Return error code obtained from securityfs functions
  2022-02-10 12:02   ` Mimi Zohar
@ 2022-02-15 18:09     ` Mimi Zohar
  0 siblings, 0 replies; 86+ messages in thread
From: Mimi Zohar @ 2022-02-15 18:09 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

On Thu, 2022-02-10 at 07:02 -0500, Mimi Zohar wrote:
> On Tue, 2022-02-01 at 15:37 -0500, Stefan Berger wrote:
> > If an error occurs when creating a securityfs file, return the exact
> > error code to the caller.
> > 
> > Signed-off-by: Stefan Berger <stefanb@linux.ibm.com>
> 
> Thanks, Stefan.  Nice cleanup.

This is now queued in next-integrity.

-- 
Thanks,

Mimi


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

* Re: [PATCH v10 05/27] ima: Define ima_namespace struct and start moving variables into it
  2022-02-01 20:37 ` [PATCH v10 05/27] ima: Define ima_namespace struct and start moving variables into it Stefan Berger
@ 2022-02-16 14:41   ` Mimi Zohar
  2022-02-16 20:25     ` Stefan Berger
  0 siblings, 1 reply; 86+ messages in thread
From: Mimi Zohar @ 2022-02-16 14:41 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, Christian Brauner

On Tue, 2022-02-01 at 15:37 -0500, Stefan Berger wrote:
> Define the ima_namespace structure and the ima_namespace variable
> init_ima_ns for the host's IMA namespace. Implement basic functions for
> namespacing support.

Implement the basic functions - ima_ns_init()  and ima_init_namespace()
- for namespacing support.

> 
> 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.

Currently, ima_ns_from_file() doesn't exist in this patch.

> 
> To get the current ima_namespace use &init_ima_ns when a function
> that is related to a policy rule is called.

In preparation for IMA namespacing, update the existing functions to
pass the ima_namespace struct.  For now, ...

> 
> 
> Signed-off-by: Stefan Berger <stefanb@linux.ibm.com>
> Acked-by: Christian Brauner <brauner@kernel.org>

After addressing the one inline comment,
	Reviewed-by: Mimi Zohar <zohar@linux.ibm.com>
> 
> ---

>  
> @@ -119,6 +117,17 @@ struct ima_kexec_hdr {
>  	u64 count;
>  };
>  
> +struct ima_namespace {
> +	/* policy rules */
> +	struct list_head ima_default_rules;
> +	struct list_head ima_policy_rules;
> +	struct list_head ima_temp_rules;

These local policy variables weren't previously commented, but with the
move to a structure it would be good to add comments.  For example, the
architecture policy rules persist even after a custom policy is loaded.

ima_default_rules: /* Kconfig, builtin, & arch rules */
ima_policy_rules:  /* arch & custom rules */

> +
> +	struct list_head __rcu *ima_rules;	/* current policy */

/* Pointer to the current policy */.

> +	int ima_policy_flag;
> +} __randomize_layout;
> +extern struct ima_namespace init_ima_ns;
> +
>  extern const int read_idmap[];
>  
>  #ifdef CONFIG_HAVE_IMA_KEXEC
> 
>  {
> +	struct ima_namespace *ns = &init_ima_ns;
>  	char *data;
>  	ssize_t result;
>  


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

* Re: [PATCH v10 07/27] ima: Move ima_htable into ima_namespace
  2022-02-01 20:37 ` [PATCH v10 07/27] ima: Move ima_htable " Stefan Berger
@ 2022-02-16 14:41   ` Mimi Zohar
  0 siblings, 0 replies; 86+ messages in thread
From: Mimi Zohar @ 2022-02-16 14:41 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, Christian Brauner

On Tue, 2022-02-01 at 15:37 -0500, Stefan Berger wrote:
> Move ima_htable into ima_namespace. This way a front-end like
> securityfs can show the number of violations of an IMA namespace.

^can show the number of measurement records and number of violations

> 
> Signed-off-by: Stefan Berger <stefanb@linux.ibm.com>
> Acked-by: Christian Brauner <brauner@kernel.org>

Reviewed-by: Mimi Zohar <zohar@linux.ibm.com>


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

* Re: [PATCH v10 06/27] ima: Move arch_policy_entry into ima_namespace
  2022-02-01 20:37 ` [PATCH v10 06/27] ima: Move arch_policy_entry into ima_namespace Stefan Berger
@ 2022-02-16 16:39   ` Mimi Zohar
  2022-02-16 20:48     ` Stefan Berger
  0 siblings, 1 reply; 86+ messages in thread
From: Mimi Zohar @ 2022-02-16 16:39 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, Christian Brauner

On Tue, 2022-02-01 at 15:37 -0500, Stefan Berger wrote

Let's update the patch description providing a bit more background
info:

The archictecture specific policy rules, currently defined for EFI and
powerpc, require the kexec kernel image and kernel modules to be
validly signed and measured, based on the system's secure boot and/or
trusted boot mode and the IMA_ARCH_POLICY Kconfig option being enabled.

> Move the arch_policy_entry pointer into ima_namespace.

Perhaps include something about namespaces being allowed or not allowed
to kexec a new kernel or load kernel modules.

thanks,

Mimi
> 
> When freeing the memory set the pointer to NULL.
> 
> Signed-off-by: Stefan Berger <stefanb@linux.ibm.com>
> Acked-by: Christian Brauner <brauner@kernel.org>
> Reviewed-by: Mimi Zohar <zohar@linux.ibm.com>


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

* Re: [PATCH v10 05/27] ima: Define ima_namespace struct and start moving variables into it
  2022-02-16 14:41   ` Mimi Zohar
@ 2022-02-16 20:25     ` Stefan Berger
  0 siblings, 0 replies; 86+ messages in thread
From: Stefan Berger @ 2022-02-16 20:25 UTC (permalink / raw)
  To: Mimi Zohar, 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, Christian Brauner


On 2/16/22 09:41, Mimi Zohar wrote:
> On Tue, 2022-02-01 at 15:37 -0500, Stefan Berger wrote:
>> Define the ima_namespace structure and the ima_namespace variable
>> init_ima_ns for the host's IMA namespace. Implement basic functions for
>> namespacing support.
> Implement the basic functions - ima_ns_init()  and ima_init_namespace()
> - for namespacing support.
>
>> 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.
> Currently, ima_ns_from_file() doesn't exist in this patch.
Ah, left-over from previous version. Remove.
>
>> To get the current ima_namespace use &init_ima_ns when a function
>> that is related to a policy rule is called.
> In preparation for IMA namespacing, update the existing functions to
> pass the ima_namespace struct.  For now, ...
>
>>
>> Signed-off-by: Stefan Berger <stefanb@linux.ibm.com>
>> Acked-by: Christian Brauner <brauner@kernel.org>
> After addressing the one inline comment,
Done.
> 	Reviewed-by: Mimi Zohar <zohar@linux.ibm.com>

Thanks.



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

* Re: [PATCH v10 06/27] ima: Move arch_policy_entry into ima_namespace
  2022-02-16 16:39   ` Mimi Zohar
@ 2022-02-16 20:48     ` Stefan Berger
  2022-02-16 20:56       ` Mimi Zohar
  0 siblings, 1 reply; 86+ messages in thread
From: Stefan Berger @ 2022-02-16 20:48 UTC (permalink / raw)
  To: Mimi Zohar, 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, Christian Brauner


On 2/16/22 11:39, Mimi Zohar wrote:
> On Tue, 2022-02-01 at 15:37 -0500, Stefan Berger wrote
>
> Let's update the patch description providing a bit more background
> info:
>
> The archictecture specific policy rules, currently defined for EFI and
> powerpc, require the kexec kernel image and kernel modules to be
> validly signed and measured, based on the system's secure boot and/or
> trusted boot mode and the IMA_ARCH_POLICY Kconfig option being enabled.
>
>> Move the arch_policy_entry pointer into ima_namespace.
> Perhaps include something about namespaces being allowed or not allowed
> to kexec a new kernel or load kernel modules.

Namespaces are not allowed to kexec but special-casing the init_ima_ns 
in the code to handle namespaces differently makes it much harder to 
read the code. I would avoid special-casing init_ima_ns as much as 
possible and therefore I have moved the arch_policy_entry into the 
ima_namespace.

    Stefan


> thanks,
>
> Mimi
>> When freeing the memory set the pointer to NULL.
>>
>> Signed-off-by: Stefan Berger <stefanb@linux.ibm.com>
>> Acked-by: Christian Brauner <brauner@kernel.org>
>> Reviewed-by: Mimi Zohar <zohar@linux.ibm.com>

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

* Re: [PATCH v10 06/27] ima: Move arch_policy_entry into ima_namespace
  2022-02-16 20:48     ` Stefan Berger
@ 2022-02-16 20:56       ` Mimi Zohar
  2022-02-16 21:19         ` Stefan Berger
  0 siblings, 1 reply; 86+ messages in thread
From: Mimi Zohar @ 2022-02-16 20:56 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, Christian Brauner

On Wed, 2022-02-16 at 15:48 -0500, Stefan Berger wrote:
> On 2/16/22 11:39, Mimi Zohar wrote:
> > On Tue, 2022-02-01 at 15:37 -0500, Stefan Berger wrote
> >
> > Let's update the patch description providing a bit more background
> > info:
> >
> > The archictecture specific policy rules, currently defined for EFI and
> > powerpc, require the kexec kernel image and kernel modules to be
> > validly signed and measured, based on the system's secure boot and/or
> > trusted boot mode and the IMA_ARCH_POLICY Kconfig option being enabled.
> >
> >> Move the arch_policy_entry pointer into ima_namespace.
> > Perhaps include something about namespaces being allowed or not allowed
> > to kexec a new kernel or load kernel modules.
> 
> Namespaces are not allowed to kexec but special-casing the init_ima_ns 
> in the code to handle namespaces differently makes it much harder to 
> read the code. I would avoid special-casing init_ima_ns as much as 
> possible and therefore I have moved the arch_policy_entry into the 
> ima_namespace.

Please include this in the patch description, but re-write the last
line in the 3rd person, like:

To avoid special-casing init_ima_ns, as much as possible, move the
arch_policy_entry into the ima_namespace.

thanks,

Mimi



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

* Re: [PATCH v10 06/27] ima: Move arch_policy_entry into ima_namespace
  2022-02-16 20:56       ` Mimi Zohar
@ 2022-02-16 21:19         ` Stefan Berger
  0 siblings, 0 replies; 86+ messages in thread
From: Stefan Berger @ 2022-02-16 21:19 UTC (permalink / raw)
  To: Mimi Zohar, 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, Christian Brauner


On 2/16/22 15:56, Mimi Zohar wrote:
> On Wed, 2022-02-16 at 15:48 -0500, Stefan Berger wrote:
>> On 2/16/22 11:39, Mimi Zohar wrote:
>>> On Tue, 2022-02-01 at 15:37 -0500, Stefan Berger wrote
>>>
>>> Let's update the patch description providing a bit more background
>>> info:
>>>
>>> The archictecture specific policy rules, currently defined for EFI and
>>> powerpc, require the kexec kernel image and kernel modules to be
>>> validly signed and measured, based on the system's secure boot and/or
>>> trusted boot mode and the IMA_ARCH_POLICY Kconfig option being enabled.
>>>
>>>> Move the arch_policy_entry pointer into ima_namespace.
>>> Perhaps include something about namespaces being allowed or not allowed
>>> to kexec a new kernel or load kernel modules.
>> Namespaces are not allowed to kexec but special-casing the init_ima_ns
>> in the code to handle namespaces differently makes it much harder to
>> read the code. I would avoid special-casing init_ima_ns as much as
>> possible and therefore I have moved the arch_policy_entry into the
>> ima_namespace.
> Please include this in the patch description, but re-write the last
> line in the 3rd person, like:
>
> To avoid special-casing init_ima_ns, as much as possible, move the
> arch_policy_entry into the ima_namespace.

I took the paragraph on the background as well as this sentence.


>
> thanks,
>
> Mimi
>
>

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

* Re: [PATCH v10 09/27] ima: Move some IMA policy and filesystem related variables into ima_namespace
  2022-02-01 20:37 ` [PATCH v10 09/27] ima: Move some IMA policy and filesystem " Stefan Berger
@ 2022-02-17 14:44   ` Mimi Zohar
  0 siblings, 0 replies; 86+ messages in thread
From: Mimi Zohar @ 2022-02-17 14:44 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, Christian Brauner

On Tue, 2022-02-01 at 15:37 -0500, Stefan Berger wrote:
> Move the variables ima_write_mutex, ima_fs_flag, and valid_policy, which
> are related to updating the IMA policy, into the ima_namespace. This way
> each IMA namespace can set these variables independently in its instance
> of securityfs.
> 
> Signed-off-by: Stefan Berger <stefanb@linux.ibm.com>
> Acked-by: Christian Brauner <brauner@kernel.org>

Thanks,

Reviewed-by: Mimi Zohar <zohar@linux.ibm.com>


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

* Re: [PATCH v10 10/27] ima: Move IMA securityfs files into ima_namespace or onto stack
  2022-02-01 20:37 ` [PATCH v10 10/27] ima: Move IMA securityfs files into ima_namespace or onto stack Stefan Berger
@ 2022-02-17 14:44   ` Mimi Zohar
  0 siblings, 0 replies; 86+ messages in thread
From: Mimi Zohar @ 2022-02-17 14:44 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, Christian Brauner

On Tue, 2022-02-01 at 15:37 -0500, Stefan Berger wrote:
> Earlier we simplified how dentry creation and deletion is manged in
> securityfs. This allows us to move IMA securityfs files from global
> variables directly into ima_fs_ns_init() itself. We can now rely on
> those dentries to be cleaned up when the securityfs instance is cleaned
> when the last reference to it is dropped.
> 
> Things are slightly different for the initial IMA namespace. In contrast
> to non-initial IMA namespaces it has pinning logic binding the lifetime
> of the securityfs superblock to created dentries. We need to keep this
> behavior to not regress userspace. Since IMA never removes most of the
> securityfs files the initial securityfs instance stays pinned. This also
> means even for the initial IMA namespace we don't need to keep
> references to these dentries anywhere.
> 
> The ima_policy file is the exception since IMA can end up removing it
> on systems that don't allow reading or extending the IMA custom policy.
> 
> Signed-off-by: Stefan Berger <stefanb@linux.ibm.com>
> Acked-by: Christian Brauner <brauner@kernel.org>

Really nicely worded patch description.  Thanks!

Reviewed-by: Mimi Zohar <zohar@linux.ibm.com>


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

* Re: [PATCH v10 08/27] ima: Move measurement list related variables into ima_namespace
  2022-02-01 20:37 ` [PATCH v10 08/27] ima: Move measurement list related variables " Stefan Berger
@ 2022-02-17 14:46   ` Mimi Zohar
  0 siblings, 0 replies; 86+ messages in thread
From: Mimi Zohar @ 2022-02-17 14:46 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

On Tue, 2022-02-01 at 15:37 -0500, Stefan Berger wrote:
> 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.

Also, in order for kexec to allocate memory for the existing
measurement list, the measurement list memory size is stored in the
binary_runtime_size variable.  To avoid special-casing init_ima_ns, as
much as possible, move it into the ima_namespace.

> 
> Signed-off-by: Stefan Berger <stefanb@linux.ibm.com>

Reviewed-by: Mimi Zohar <zohar@linux.ibm.com>


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

* Re: [PATCH v10 11/27] ima: Move ima_lsm_policy_notifier into ima_namespace
  2022-02-01 20:37 ` [PATCH v10 11/27] ima: Move ima_lsm_policy_notifier into ima_namespace Stefan Berger
@ 2022-02-17 20:30   ` Mimi Zohar
  2022-02-17 20:59     ` Stefan Berger
  0 siblings, 1 reply; 86+ messages in thread
From: Mimi Zohar @ 2022-02-17 20:30 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

On Tue, 2022-02-01 at 15:37 -0500, Stefan Berger wrote:

The builtin IMA policy rules are broad and may be constrained by
loading a custom policy, which could be defined in terms of LSM labels.
When an LSM policy is loaded, existing LSM labels might be affected or
even removed.  In either case, IMA policy rules based on LSM
labels, need to reflect these changes.  If an LSM label is removed,
instead of deleting the IMA policy rule based on the LSM label, the IMA
policy rule is made inactive.

> Move the ima_lsm_policy_notifier into the ima_namespace. Each IMA
> namespace can now register its own LSM policy change notifier callback.
> The policy change notifier for the init_ima_ns still remains in init_ima()
> and therefore handle the registration of the callback for all other
> namespaces in init_ima_namespace().
> 
> Suppress the kernel warning 'rule for LSM <label> is undefined` for all
> other IMA namespaces than the init_ima_ns.

Instead of ignoring the warnings totally, perhaps use either the
"ratelimited" or "once" function options for non init_ima_ns.  It would
be nice if these functions could be namespace aware, so that each
affected IMA namespace would contain at least one warning.

> 
> Signed-off-by: Stefan Berger <stefanb@linux.ibm.com>

Reviewed-by: Mimi Zohar <zohar@linux.ibm.com>

> 
> ---
> v10:
>  - Only call pr_warn('rule for LSM <label> is undefined`) for init_ima_ns
> ---
>  security/integrity/ima/ima.h             |  2 ++
>  security/integrity/ima/ima_init_ima_ns.c | 14 ++++++++++++++
>  security/integrity/ima/ima_main.c        |  6 +-----
>  security/integrity/ima/ima_policy.c      | 16 ++++++++++------
>  4 files changed, 27 insertions(+), 11 deletions(-)
> 
> diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h
> index 94c6e3a4d666..fb6bd054d899 100644
> --- a/security/integrity/ima/ima.h
> +++ b/security/integrity/ima/ima.h
> @@ -144,6 +144,8 @@ struct ima_namespace {
>  	int valid_policy;
>  
>  	struct dentry *ima_policy;
> +
> +	struct notifier_block ima_lsm_policy_notifier;
>  } __randomize_layout;
>  extern struct ima_namespace init_ima_ns;
>  
> diff --git a/security/integrity/ima/ima_init_ima_ns.c b/security/integrity/ima/ima_init_ima_ns.c
> index 425eed1c6838..1cba545ae477 100644
> --- a/security/integrity/ima/ima_init_ima_ns.c
> +++ b/security/integrity/ima/ima_init_ima_ns.c
> @@ -10,6 +10,8 @@
>  
>  static int ima_init_namespace(struct ima_namespace *ns)
>  {
> +	int rc;
> +

There's no right or wrong, but the newer IMA code uses 'ret'.

>  	INIT_LIST_HEAD(&ns->ima_default_rules);
>  	INIT_LIST_HEAD(&ns->ima_policy_rules);
>  	INIT_LIST_HEAD(&ns->ima_temp_rules);


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

* Re: [PATCH v10 11/27] ima: Move ima_lsm_policy_notifier into ima_namespace
  2022-02-17 20:30   ` Mimi Zohar
@ 2022-02-17 20:59     ` Stefan Berger
  2022-02-17 21:24       ` Mimi Zohar
  0 siblings, 1 reply; 86+ messages in thread
From: Stefan Berger @ 2022-02-17 20:59 UTC (permalink / raw)
  To: Mimi Zohar, 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 2/17/22 15:30, Mimi Zohar wrote:
> On Tue, 2022-02-01 at 15:37 -0500, Stefan Berger wrote:
>
> The builtin IMA policy rules are broad and may be constrained by
> loading a custom policy, which could be defined in terms of LSM labels.
> When an LSM policy is loaded, existing LSM labels might be affected or
> even removed.  In either case, IMA policy rules based on LSM
> labels, need to reflect these changes.  If an LSM label is removed,
> instead of deleting the IMA policy rule based on the LSM label, the IMA
> policy rule is made inactive.

Ok, so I take this paragraph for the patch description.


>
>> Move the ima_lsm_policy_notifier into the ima_namespace. Each IMA
>> namespace can now register its own LSM policy change notifier callback.
>> The policy change notifier for the init_ima_ns still remains in init_ima()
>> and therefore handle the registration of the callback for all other
>> namespaces in init_ima_namespace().
>>
>> Suppress the kernel warning 'rule for LSM <label> is undefined` for all
>> other IMA namespaces than the init_ima_ns.
> Instead of ignoring the warnings totally, perhaps use either the
> "ratelimited" or "once" function options for non init_ima_ns.  It would
> be nice if these functions could be namespace aware, so that each
> affected IMA namespace would contain at least one warning.

The problem is that any user can now repeatedly create user namespaces 
and with that IMA namespaces and cause the kernel log to fill up with 
these messages and also flood the audit log -- I guess one could 
describe it as an unwanted side-effect. I am afraid that for as long as 
the kernel log is not namespaced it's probably best to just turn them 
off for non-init_ima_ns.


>> Signed-off-by: Stefan Berger <stefanb@linux.ibm.com>
> Reviewed-by: Mimi Zohar <zohar@linux.ibm.com>
Thanks.
>
>> ---
>> v10:
>>   - Only call pr_warn('rule for LSM <label> is undefined`) for init_ima_ns
>> ---
>>   security/integrity/ima/ima.h             |  2 ++
>>   security/integrity/ima/ima_init_ima_ns.c | 14 ++++++++++++++
>>   security/integrity/ima/ima_main.c        |  6 +-----
>>   security/integrity/ima/ima_policy.c      | 16 ++++++++++------
>>   4 files changed, 27 insertions(+), 11 deletions(-)
>>
>> diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h
>> index 94c6e3a4d666..fb6bd054d899 100644
>> --- a/security/integrity/ima/ima.h
>> +++ b/security/integrity/ima/ima.h
>> @@ -144,6 +144,8 @@ struct ima_namespace {
>>   	int valid_policy;
>>   
>>   	struct dentry *ima_policy;
>> +
>> +	struct notifier_block ima_lsm_policy_notifier;
>>   } __randomize_layout;
>>   extern struct ima_namespace init_ima_ns;
>>   
>> diff --git a/security/integrity/ima/ima_init_ima_ns.c b/security/integrity/ima/ima_init_ima_ns.c
>> index 425eed1c6838..1cba545ae477 100644
>> --- a/security/integrity/ima/ima_init_ima_ns.c
>> +++ b/security/integrity/ima/ima_init_ima_ns.c
>> @@ -10,6 +10,8 @@
>>   
>>   static int ima_init_namespace(struct ima_namespace *ns)
>>   {
>> +	int rc;
>> +
> There's no right or wrong, but the newer IMA code uses 'ret'.


I don't mind to change it. I will take your Reviewed-by, though :-)


>
>>   	INIT_LIST_HEAD(&ns->ima_default_rules);
>>   	INIT_LIST_HEAD(&ns->ima_policy_rules);
>>   	INIT_LIST_HEAD(&ns->ima_temp_rules);

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

* Re: [PATCH v10 11/27] ima: Move ima_lsm_policy_notifier into ima_namespace
  2022-02-17 20:59     ` Stefan Berger
@ 2022-02-17 21:24       ` Mimi Zohar
  0 siblings, 0 replies; 86+ messages in thread
From: Mimi Zohar @ 2022-02-17 21:24 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

On Thu, 2022-02-17 at 15:59 -0500, Stefan Berger wrote:
> >
> >> Move the ima_lsm_policy_notifier into the ima_namespace. Each IMA
> >> namespace can now register its own LSM policy change notifier callback.
> >> The policy change notifier for the init_ima_ns still remains in init_ima()
> >> and therefore handle the registration of the callback for all other
> >> namespaces in init_ima_namespace().
> >>
> >> Suppress the kernel warning 'rule for LSM <label> is undefined` for all
> >> other IMA namespaces than the init_ima_ns.
> > Instead of ignoring the warnings totally, perhaps use either the
> > "ratelimited" or "once" function options for non init_ima_ns.  It would
> > be nice if these functions could be namespace aware, so that each
> > affected IMA namespace would contain at least one warning.
> 
> The problem is that any user can now repeatedly create user namespaces 
> and with that IMA namespaces and cause the kernel log to fill up with 
> these messages and also flood the audit log -- I guess one could 
> describe it as an unwanted side-effect. I am afraid that for as long as 
> the kernel log is not namespaced it's probably best to just turn them 
> off for non-init_ima_ns.

There are functions - pr_warn_once() or pr_warn_ratelimited() - that
limit the number of kernel messages.  In addition to the number of
potential kernel messages, there's also the issue of being able to
differentiate between init_ima_ns and other IMA namespaces.  I think
that is more of a concern than rate limiting.

-- 
thanks,

Mimi





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

* Re: [PATCH v10 13/27] ima: Only accept AUDIT rules for non-init_ima_ns namespaces for now
  2022-02-01 20:37 ` [PATCH v10 13/27] ima: Only accept AUDIT rules for non-init_ima_ns namespaces for now Stefan Berger
@ 2022-02-17 21:32   ` Mimi Zohar
  0 siblings, 0 replies; 86+ messages in thread
From: Mimi Zohar @ 2022-02-17 21:32 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, Christian Brauner

On Tue, 2022-02-01 at 15:37 -0500, Stefan Berger wrote:

> Only accept AUDIT rules for non-init_ima_ns namespaces rejecting all rules
> that require support for measuring, appraisal, and hashing.

It's probably obvious, but adding the words "for now" somewhere in the
above line makes it clear this is temporary.

> 
> Signed-off-by: Stefan Berger <stefanb@linux.ibm.com>
> Acked-by: Christian Brauner <brauner@kernel.org>

Reviewed-by: Mimi Zohar <zohar@linux.ibm.com>

-- 
thanks,

Mimi




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

* Re: [PATCH v10 14/27] userns: Add pointer to ima_namespace to user_namespace
  2022-02-01 20:37 ` [PATCH v10 14/27] userns: Add pointer to ima_namespace to user_namespace Stefan Berger
@ 2022-02-18 16:26   ` Mimi Zohar
  0 siblings, 0 replies; 86+ messages in thread
From: Mimi Zohar @ 2022-02-18 16: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

Hi Stefan,

On Tue, 2022-02-01 at 15:37 -0500, Stefan Berger wrote:
> 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>

The patch description explains 'what' is being done, but not 'why'. 
Please add a sentence before what you have providing the motivation.

Otherwise,

Reviewed-by: Mimi Zohar <zohar@linux.ibm.com>

-- 
thanks,

Mimi


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

* Re: [PATCH v10 15/27] ima: Implement hierarchical processing of file accesses
  2022-02-01 20:37 ` [PATCH v10 15/27] ima: Implement hierarchical processing of file accesses Stefan Berger
  2022-02-02 13:19     ` kernel test robot
  2022-02-02 13:19     ` kernel test robot
@ 2022-02-18 16:27   ` Mimi Zohar
  2 siblings, 0 replies; 86+ messages in thread
From: Mimi Zohar @ 2022-02-18 16: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

On Tue, 2022-02-01 at 15:37 -0500, Stefan Berger wrote:
> 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.
> 
> Pass the user_namespace into process_measurement since we will be walking
> the hierarchy of user_namespaces towards the init_user_ns and we can easily
> derive the ima_namespace from the user_namespace.
> 
> __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 still be NULL due to late initialization of the
> IMA namespace.
> 
> Signed-off-by: Stefan Berger <stefanb@linux.ibm.com>
> 
> ---
> 
> v10:
>   - Fixed compilation issue
> 
> v9:
>   - Switch callers to pass user_namespace rather than ima_namespace with
>     potential NULL pointer
>   - Add default case to switch statement and warn if this happens
>   - Implement ima_ns_from_user_ns() in this patch now
> ---
>  security/integrity/ima/ima.h      |  8 ++++
>  security/integrity/ima/ima_main.c | 76 +++++++++++++++++++++++--------
>  2 files changed, 65 insertions(+), 19 deletions(-)
> 
> diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h
> index 0057b1fd6c18..aea8fb8d2854 100644
> --- a/security/integrity/ima/ima.h
> +++ b/security/integrity/ima/ima.h
> @@ -493,4 +493,12 @@ 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;
> +}
> +
>  #endif /* __LINUX_IMA_H */
> diff --git a/security/integrity/ima/ima_main.c b/security/integrity/ima/ima_main.c
> index ae0e9b14554a..917504319e7f 100644
> --- a/security/integrity/ima/ima_main.c
> +++ b/security/integrity/ima/ima_main.c
> @@ -196,10 +196,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;
> @@ -391,6 +391,41 @@ static int process_measurement(struct ima_namespace *ns,
>  	return 0;
>  }
>  
> +static int process_measurement(struct user_namespace *user_ns,
> +			       struct file *file, const struct cred *cred,
> +			       u32 secid, char *buf, loff_t size, int mask,
> +			       enum ima_hooks func)
> +{
> +	struct ima_namespace *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 0:
> +				break;
> +			case -EACCES:
> +				/* return this error at the end but continue */
> +				ret = -EACCES;
> +				break;
> +			default:
> +				/* should not happen */
> +				ret = -EACCES;
> +				WARN_ON_ONCE(true);
> +			}
> +		}
> +
> +		user_ns = user_ns->parent;
> +	};
> +
> +	return ret;
> +}
> +

Very nice and concise!

Reviewed-by: Mimi Zohar <zohar@linux.ibm.com>

-- 
thanks,

Mimi 


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

* Re: [PATCH v10 16/27] ima: Implement ima_free_policy_rules() for freeing of an ima_namespace
  2022-02-01 20:37 ` [PATCH v10 16/27] ima: Implement ima_free_policy_rules() for freeing of an ima_namespace Stefan Berger
@ 2022-02-18 17:09   ` Mimi Zohar
  2022-02-18 19:38     ` Stefan Berger
  0 siblings, 1 reply; 86+ messages in thread
From: Mimi Zohar @ 2022-02-18 17:09 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

On Tue, 2022-02-01 at 15:37 -0500, Stefan Berger wrote:
> Implement ima_free_policy_rules() that is needed when an ima_namespace
> is freed.
> 
> Signed-off-by: Stefan Berger <stefanb@linux.ibm.com>
> 
> ---
> v10:
>   - Not calling ima_delete_rules() anymore
>   - Move access check from ima_delete_rules into very last patch
> 
>  v9:
>   - Only reset temp_ima_appraise when using init_ima_ns.
> ---
>  security/integrity/ima/ima.h        |  1 +
>  security/integrity/ima/ima_policy.c | 14 ++++++++++++++
>  2 files changed, 15 insertions(+)
> 
> diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h
> index aea8fb8d2854..8c757223d549 100644
> --- a/security/integrity/ima/ima.h
> +++ b/security/integrity/ima/ima.h
> @@ -329,6 +329,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 2dcc5a8c585a..fe3dce8fb939 100644
> --- a/security/integrity/ima/ima_policy.c
> +++ b/security/integrity/ima/ima_policy.c
> @@ -1889,6 +1889,20 @@ 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;
> +
> +	list_for_each_entry_safe(entry, tmp, &ns->ima_policy_rules, list) {
> +		list_del(&entry->list);
> +		ima_free_rule(entry);
> +	}
> +}
> +

The first time a policy is loaded, the policy rules pivot
from ima_default_rules to the custom rules.  When this happens, the
architecture specific policy rules are freed.  Here too, if a custom
policy isn't already loaded, the architecture specific rules stored as
an array need to be freed.  Refer to the comment in
ima_update_policy().

>  #define __ima_hook_stringify(func, str)	(#func),
>  
>  const char *const func_tokens[] = {

-- 
thanks,

Mimi



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

* Re: [PATCH v10 16/27] ima: Implement ima_free_policy_rules() for freeing of an ima_namespace
  2022-02-18 17:09   ` Mimi Zohar
@ 2022-02-18 19:38     ` Stefan Berger
  2022-02-18 20:04       ` Mimi Zohar
  0 siblings, 1 reply; 86+ messages in thread
From: Stefan Berger @ 2022-02-18 19:38 UTC (permalink / raw)
  To: Mimi Zohar, 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 2/18/22 12:09, Mimi Zohar wrote:
> On Tue, 2022-02-01 at 15:37 -0500, Stefan Berger wrote:
>> Implement ima_free_policy_rules() that is needed when an ima_namespace
>> is freed.
>>
>> Signed-off-by: Stefan Berger <stefanb@linux.ibm.com>
>>
>> ---
>> v10:
>>    - Not calling ima_delete_rules() anymore
>>    - Move access check from ima_delete_rules into very last patch
>>
>>   v9:
>>    - Only reset temp_ima_appraise when using init_ima_ns.
>> ---
>>   security/integrity/ima/ima.h        |  1 +
>>   security/integrity/ima/ima_policy.c | 14 ++++++++++++++
>>   2 files changed, 15 insertions(+)
>>
>> diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h
>> index aea8fb8d2854..8c757223d549 100644
>> --- a/security/integrity/ima/ima.h
>> +++ b/security/integrity/ima/ima.h
>> @@ -329,6 +329,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 2dcc5a8c585a..fe3dce8fb939 100644
>> --- a/security/integrity/ima/ima_policy.c
>> +++ b/security/integrity/ima/ima_policy.c
>> @@ -1889,6 +1889,20 @@ 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;
>> +
>> +	list_for_each_entry_safe(entry, tmp, &ns->ima_policy_rules, list) {
>> +		list_del(&entry->list);
>> +		ima_free_rule(entry);
>> +	}
>> +}
>> +
> The first time a policy is loaded, the policy rules pivot
> from ima_default_rules to the custom rules.  When this happens, the
> architecture specific policy rules are freed.  Here too, if a custom
> policy isn't already loaded, the architecture specific rules stored as
> an array need to be freed.  Refer to the comment in
> ima_update_policy().

Right. So here's how it's done (before arch_policy_entry was moved into 
ima_namespace).

         /*
          * IMA architecture specific policy rules are specified
          * as strings and converted to an array of ima_entry_rules
          * on boot.  After loading a custom policy, free the
          * architecture specific rules stored as an array.
          */
         kfree(arch_policy_entry);


So, I now added kfree(ns->arch_policy_entry).

Thanks.

    Stefan

>
>>   #define __ima_hook_stringify(func, str)	(#func),
>>   
>>   const char *const func_tokens[] = {

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

* Re: [PATCH v10 17/27] ima: Add functions for creating and freeing of an ima_namespace
  2022-02-01 20:37 ` [PATCH v10 17/27] ima: Add functions for creating and " Stefan Berger
@ 2022-02-18 19:49   ` Mimi Zohar
  0 siblings, 0 replies; 86+ messages in thread
From: Mimi Zohar @ 2022-02-18 19:49 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, Christian Brauner

On Tue, 2022-02-01 at 15:37 -0500, Stefan Berger wrote:
> Implement create_ima_ns() to create an empty ima_namespace. Defer its
> initialization to a later point outside this function. Implement
> free_ima_ns() to free it.
> 
> Signed-off-by: Stefan Berger <stefanb@linux.ibm.com>
> Acked-by: Christian Brauner <brauner@kernel.org>
> 
> ---
> v9:
>  - Set user_ns->ims_ns = NULL in free_ima_ns()
>  - Refactored create_ima_ns() to defer initialization
>  - Removed pr_debug functions
> ---
>  include/linux/ima.h                      | 13 ++++++
>  security/integrity/ima/Makefile          |  1 +
>  security/integrity/ima/ima.h             | 15 +++++++
>  security/integrity/ima/ima_init_ima_ns.c |  2 +-
>  security/integrity/ima/ima_ns.c          | 53 ++++++++++++++++++++++++
>  5 files changed, 83 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 0cf2a80c8b35..964a08702573 100644
> --- a/include/linux/ima.h
> +++ b/include/linux/ima.h
> @@ -220,4 +220,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 8c757223d549..751e936a6311 100644
> --- a/security/integrity/ima/ima.h
> +++ b/security/integrity/ima/ima.h
> @@ -167,6 +167,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,
> @@ -502,4 +503,18 @@ static inline struct ima_namespace
>  	return NULL;
>  }
>  
> +#ifdef CONFIG_IMA_NS
> +
> +struct ima_namespace *create_ima_ns(void);
> +
> +#else
> +
> +static inline struct ima_namespace *create_ima_ns(void)
> +{
> +	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 1cba545ae477..166dab4a3126 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)
>  {
>  	int rc;
>  
> diff --git a/security/integrity/ima/ima_ns.c b/security/integrity/ima/ima_ns.c
> new file mode 100644
> index 000000000000..b3b81a1e313e
> --- /dev/null
> +++ b/security/integrity/ima/ima_ns.c
> @@ -0,0 +1,53 @@
> +// 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(void)
> +{
> +	struct ima_namespace *ns;
> +
> +	ns = kmem_cache_zalloc(imans_cachep, GFP_KERNEL);
> +	if (!ns)
> +		return ERR_PTR(-ENOMEM);
> +
> +	return ns;
> +}
> +
> +/* destroy_ima_ns() must only be called after ima_init_namespace() was called */
> +static void destroy_ima_ns(struct ima_namespace *ns)
> +{
> +	unregister_blocking_lsm_notifier(&ns->ima_lsm_policy_notifier);
> +	kfree(ns->arch_policy_entry);

Oh!  Here's the missing free of the architecture specific rules
(comment on 16/27).  Even if these rules have already been freed after
a custom policy is loaded, I guess it is safe to free them again.

Reviewed-by: Mimi Zohar <zohar@linux.ibm.com>

> +	ima_free_policy_rules(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);
> +
> +	kmem_cache_free(imans_cachep, ns);
> +
> +	user_ns->ima_ns = NULL;
> +}
> +
> +static int __init imans_cache_init(void)
> +{
> +	imans_cachep = KMEM_CACHE(ima_namespace, SLAB_PANIC);
> +	return 0;
> +}
> +subsys_initcall(imans_cache_init)



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

* Re: [PATCH v10 16/27] ima: Implement ima_free_policy_rules() for freeing of an ima_namespace
  2022-02-18 19:38     ` Stefan Berger
@ 2022-02-18 20:04       ` Mimi Zohar
  0 siblings, 0 replies; 86+ messages in thread
From: Mimi Zohar @ 2022-02-18 20:04 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

On Fri, 2022-02-18 at 14:38 -0500, Stefan Berger wrote:
> On 2/18/22 12:09, Mimi Zohar wrote:
> > On Tue, 2022-02-01 at 15:37 -0500, Stefan Berger wrote:
> >> Implement ima_free_policy_rules() that is needed when an ima_namespace
> >> is freed.

ima_free_policy_rules() isn't free all the rules, just the custom
policy rules.  I would update the patch description as:

Implement ima_free_policy_rules() to free the custom policy rules, when
...

Otherwise,

Reviewd-by: Mimi Zohar <zohar@linux.ibm.com>

> >>
> >> Signed-off-by: Stefan Berger <stefanb@linux.ibm.com>
> >>
> >> ---
> >> v10:
> >>    - Not calling ima_delete_rules() anymore
> >>    - Move access check from ima_delete_rules into very last patch
> >>
> >>   v9:
> >>    - Only reset temp_ima_appraise when using init_ima_ns.
> >> ---
> >>   security/integrity/ima/ima.h        |  1 +
> >>   security/integrity/ima/ima_policy.c | 14 ++++++++++++++
> >>   2 files changed, 15 insertions(+)
> >>
> >> diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h
> >> index aea8fb8d2854..8c757223d549 100644
> >> --- a/security/integrity/ima/ima.h
> >> +++ b/security/integrity/ima/ima.h
> >> @@ -329,6 +329,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 2dcc5a8c585a..fe3dce8fb939 100644
> >> --- a/security/integrity/ima/ima_policy.c
> >> +++ b/security/integrity/ima/ima_policy.c
> >> @@ -1889,6 +1889,20 @@ 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;
> >> +
> >> +	list_for_each_entry_safe(entry, tmp, &ns->ima_policy_rules, list) {
> >> +		list_del(&entry->list);
> >> +		ima_free_rule(entry);
> >> +	}
> >> +}
> >> +
> > The first time a policy is loaded, the policy rules pivot
> > from ima_default_rules to the custom rules.  When this happens, the
> > architecture specific policy rules are freed.  Here too, if a custom
> > policy isn't already loaded, the architecture specific rules stored as
> > an array need to be freed.  Refer to the comment in
> > ima_update_policy().
> 
> Right. So here's how it's done (before arch_policy_entry was moved into 
> ima_namespace).
> 
>          /*
>           * IMA architecture specific policy rules are specified
>           * as strings and converted to an array of ima_entry_rules
>           * on boot.  After loading a custom policy, free the
>           * architecture specific rules stored as an array.
>           */
>          kfree(arch_policy_entry);
> 
> 
> So, I now added kfree(ns->arch_policy_entry).

Yes, that is fine.

-- 
thanks,

Mimi


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

* Re: [PATCH v10 22/27] securityfs: Extend securityfs with namespacing support
  2022-02-01 20:37 ` [PATCH v10 22/27] securityfs: Extend securityfs with namespacing support Stefan Berger
@ 2022-02-23  1:48   ` Mimi Zohar
  2022-02-23  8:14     ` Christian Brauner
  0 siblings, 1 reply; 86+ messages in thread
From: Mimi Zohar @ 2022-02-23  1:48 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, Christian Brauner,
	James Bottomley

On Tue, 2022-02-01 at 15:37 -0500, Stefan Berger wrote:
> Enable multiple instances of securityfs by keying each instance with a
> pointer to the user namespace it belongs to.
> 
> Since we do not need the pinning of the filesystem for the virtualization

^namespacing case

> case, limit the usage of simple_pin_fs() and simpe_release_fs() to the

^simple_release_fs

> case when the init_user_ns is active. This simplifies the cleanup for the
> virtualization case where usage of securityfs_remove() to free dentries

^namespacing 

> is therefore not needed anymore.
> 
> 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 filesystem 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().
> 
> 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 is neither the user namespace it belongs to
> nor an ancestor of the user namespace that the instance of securityfs
> belongs to. Do not prevent access if securityfs was bind-mounted and
> therefore the init_user_ns is the owning user namespace.
> 
> Suggested-by: Christian Brauner <brauner@kernel.org>
> Signed-off-by: Stefan Berger <stefanb@linux.ibm.com>
> Signed-off-by: James Bottomley <James.Bottomley@HansenPartnership.com>

Christian, I understand that "[PATCH v10 23/27] ima: Setup securityfs
for IMA namespace" needs to be deferred, but is there a reason for
deferring  "[PATCH v10 22/27] securityfs: Extend securityfs with
namespacing support"?   As the securityfs patches are really
independent of IMA namespacing, I would have thought  "[PATCH v10
04/27] securityfs: rework dentry creation" and this patch should be co-
located at the beginning of the patch set.

-- 
thanks,

Mimi


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

* Re: [PATCH v10 22/27] securityfs: Extend securityfs with namespacing support
  2022-02-23  1:48   ` Mimi Zohar
@ 2022-02-23  8:14     ` Christian Brauner
  2022-02-23 15:44       ` Stefan Berger
  0 siblings, 1 reply; 86+ messages in thread
From: Christian Brauner @ 2022-02-23  8:14 UTC (permalink / raw)
  To: Mimi Zohar
  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, James Bottomley

On Tue, Feb 22, 2022 at 08:48:47PM -0500, Mimi Zohar wrote:
> On Tue, 2022-02-01 at 15:37 -0500, Stefan Berger wrote:
> > Enable multiple instances of securityfs by keying each instance with a
> > pointer to the user namespace it belongs to.
> > 
> > Since we do not need the pinning of the filesystem for the virtualization
> 
> ^namespacing case
> 
> > case, limit the usage of simple_pin_fs() and simpe_release_fs() to the
> 
> ^simple_release_fs
> 
> > case when the init_user_ns is active. This simplifies the cleanup for the
> > virtualization case where usage of securityfs_remove() to free dentries
> 
> ^namespacing 
> 
> > is therefore not needed anymore.
> > 
> > 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 filesystem 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().
> > 
> > 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 is neither the user namespace it belongs to
> > nor an ancestor of the user namespace that the instance of securityfs
> > belongs to. Do not prevent access if securityfs was bind-mounted and
> > therefore the init_user_ns is the owning user namespace.
> > 
> > Suggested-by: Christian Brauner <brauner@kernel.org>
> > Signed-off-by: Stefan Berger <stefanb@linux.ibm.com>
> > Signed-off-by: James Bottomley <James.Bottomley@HansenPartnership.com>
> 
> Christian, I understand that "[PATCH v10 23/27] ima: Setup securityfs
> for IMA namespace" needs to be deferred, but is there a reason for
> deferring  "[PATCH v10 22/27] securityfs: Extend securityfs with
> namespacing support"?   As the securityfs patches are really
> independent of IMA namespacing, I would have thought  "[PATCH v10
> 04/27] securityfs: rework dentry creation" and this patch should be co-
> located at the beginning of the patch set.

It felt more natural to me to defer it until the end but I have no
strong thoughts on this as of right now. Since Stefan has mentioned
moving this earlier already himself and you seem to agree as well, feel
free to do so.

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

* Re: [PATCH v10 23/27] ima: Setup securityfs for IMA namespace
  2022-02-01 20:37 ` [PATCH v10 23/27] ima: Setup securityfs for IMA namespace Stefan Berger
@ 2022-02-23 11:45   ` Mimi Zohar
  0 siblings, 0 replies; 86+ messages in thread
From: Mimi Zohar @ 2022-02-23 11:45 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, James Bottomley,
	Christian Brauner

On Tue, 2022-02-01 at 15:37 -0500, Stefan Berger wrote:
> Setup securityfs with symlinks, directories, and files for IMA
> namespacing support. The same directory structure that IMA uses on the
> host is also created for the namespacing case.
> 
> The securityfs file and directory ownerships cannot be set when the
> IMA namespace is initialized. Therefore, delay the setup of the file
> system to a later point when securityfs is in securityfs_fill_super.
> 
> Introduce a variable ima_policy_removed in ima_namespace that is used to
> remember whether the policy file has previously been removed and thus
> should not be created again in case of unmounting and again mounting
> securityfs inside an IMA namespace.

When the ability of extending the custom IMA policy was added, support
for displaying  the policy was added.  (Refer to the IMA_READ_POLICY
Kconfig.)  This patch set adds support for a user, true root in the
namespace, to be able to write a custom policy.   If the
IMA_READ_POLICY is not enabled, then nobody, including host root, will
be able to view it.

Instead of continuing to support not being able to read the IMA policy,
updating the IMA_READ_POLICY Kconfig for the IMA_NS case to require it
seems preferable.

> 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>
> Acked-by: Christian Brauner <brauner@kernel.org>

Otherwise,

Reviewed-by: Mimi Zohar <zohar@linux.ibm.com>


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

* Re: [PATCH v10 24/27] ima: Introduce securityfs file to activate an IMA namespace
  2022-02-01 20:37 ` [PATCH v10 24/27] ima: Introduce securityfs file to activate an " Stefan Berger
@ 2022-02-23 13:54   ` Mimi Zohar
  2022-02-23 17:08     ` Stefan Berger
  0 siblings, 1 reply; 86+ messages in thread
From: Mimi Zohar @ 2022-02-23 13:54 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

On Tue, 2022-02-01 at 15:37 -0500, Stefan Berger wrote:
> Introduce securityfs file 'active' that allows a user to activate an IMA
> namespace by writing a "1" (precisely a '1\0' or '1\n') to it. When
> reading from the file, it shows either '0' or '1'.

A patch description should start with the motivation for the change
being introduced.  The last paragraph mentions "why" it will be needed
in the future, but there are other reasons for explicitly requiring
activation.   Probably something along the lines of not every user
namespace requires an active IMA namespace.  Please include those
reasons here.

> 
> Also, introduce ns_is_active() to be used in those places where the
> ima_namespace pointer may either be NULL or where the active field may not
> have been set to '1', yet. An inactive IMA namespace should never be
> accessed since it has not been initialized, yet.
> 
> Set the init_ima_ns's active field to '1' since it is considered active
> right from the beginning.
> 
> The rationale for introducing a file to activate an IMA namespace is that
> subsequent support for IMA-measurement and IMA-appraisal will add
> configuration files for selecting for example the template that an IMA
> namespace is supposed to use for logging measurements, which will add
> an IMA namespace configuration stage (using securityfs files) before its
> activation.

This could be included at the beginning, as part of the motivation for
the patch, but it shouldn't be the only reason.

> 
> Signed-off-by: Stefan Berger <stefanb@linux.ibm.com>
> 
> ---
>  v10:
>  - use memdup_user_nul to copy input from user
>  - propagating error returned from ima_fs_add_ns_files()
> ---
>  security/integrity/ima/ima.h             |  7 +++
>  security/integrity/ima/ima_fs.c          | 71 ++++++++++++++++++++++++
>  security/integrity/ima/ima_init_ima_ns.c |  1 +
>  security/integrity/ima/ima_main.c        |  2 +-
>  4 files changed, 80 insertions(+), 1 deletion(-)
> 
> diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h
> index 1e3f9dda218d..05e2de7697da 100644
> --- a/security/integrity/ima/ima.h
> +++ b/security/integrity/ima/ima.h
> @@ -123,6 +123,8 @@ struct ima_h_table {
>  };
>  
>  struct ima_namespace {
> +	atomic_t active;		/* whether namespace is active */
> +
>  	struct rb_root ns_status_tree;
>  	rwlock_t ns_tree_lock;
>  	struct kmem_cache *ns_status_cache;
> @@ -154,6 +156,11 @@ struct ima_namespace {
>  } __randomize_layout;
>  extern struct ima_namespace init_ima_ns;
>  
> +static inline bool ns_is_active(struct ima_namespace *ns)
> +{
> +	return (ns && atomic_read(&ns->active));
> +}
> +
>  extern const int read_idmap[];
>  
>  #ifdef CONFIG_HAVE_IMA_KEXEC
> diff --git a/security/integrity/ima/ima_fs.c b/security/integrity/ima/ima_fs.c
> index 84cd02a9e19b..58d80884880f 100644
> --- a/security/integrity/ima/ima_fs.c
> +++ b/security/integrity/ima/ima_fs.c
> @@ -451,6 +451,71 @@ static const struct file_operations ima_measure_policy_ops = {
>  	.llseek = generic_file_llseek,
>  };
>  
> +static ssize_t ima_show_active(struct file *filp,
> +			       char __user *buf,
> +			       size_t count, loff_t *ppos)
> +{
> +	struct ima_namespace *ns = &init_ima_ns;
> +	char tmpbuf[2];
> +	ssize_t len;
> +
> +	len = scnprintf(tmpbuf, sizeof(tmpbuf),
> +			"%d\n", atomic_read(&ns->active));
> +	return simple_read_from_buffer(buf, count, ppos, tmpbuf, len);
> +}
> +
> +static ssize_t ima_write_active(struct file *filp,
> +				const char __user *buf,
> +				size_t count, loff_t *ppos)
> +{
> +	struct ima_namespace *ns = &init_ima_ns;
> +	unsigned int active;
> +	char *kbuf;
> +	int err;
> +
> +	if (ns_is_active(ns))
> +		return -EBUSY;
> +
> +	/* accepting '1\n' and '1\0' and no partial writes */
> +	if (count >= 3 || *ppos != 0)
> +		return -EINVAL;
> +
> +	kbuf = memdup_user_nul(buf, count);
> +	if (IS_ERR(kbuf))
> +		return PTR_ERR(kbuf);
> +
> +	err = kstrtouint(kbuf, 10, &active);
> +	kfree(kbuf);
> +	if (err)
> +		return err;
> +
> +	if (active != 1)
> +		return -EINVAL;
> +
> +	atomic_set(&ns->active, 1);
> +
> +	return count;
> +}
> +
> +static const struct file_operations ima_active_ops = {
> +	.read = ima_show_active,
> +	.write = ima_write_active,
> +};
> +
> +static int ima_fs_add_ns_files(struct dentry *ima_dir)
> +{
> +	struct dentry *active;
> +
> +	active =
> +	    securityfs_create_file("active",
> +				   S_IRUSR | S_IWUSR | S_IRGRP, ima_dir, NULL,
> +				   &ima_active_ops);
> +	if (IS_ERR(active))
> +		return PTR_ERR(active);
> +
> +	return 0;
> +}
> +
>  int ima_fs_ns_init(struct user_namespace *user_ns, struct dentry *root)
>  {
>  	struct ima_namespace *ns = ima_ns_from_user_ns(user_ns);
> @@ -531,6 +596,12 @@ int ima_fs_ns_init(struct user_namespace *user_ns, struct dentry *root)
>  		}
>  	}
>  
> +	if (ns != &init_ima_ns) {
> +		ret = ima_fs_add_ns_files(ima_dir);
> +		if (ret)
> +			goto out;
> +	}
> +

In all other cases, the securityfs files are directly created in
ima_fs_ns_init().   What is different about "active" that a new
function is defined?

thanks,

Mimi

>  	return 0;
>  out:
>  	securityfs_remove(ns->ima_policy);
> diff --git a/security/integrity/ima/ima_init_ima_ns.c b/security/integrity/ima/ima_init_ima_ns.c
> index d4ddfd1de60b..39ee0c2477a6 100644
> --- a/security/integrity/ima/ima_init_ima_ns.c
> +++ b/security/integrity/ima/ima_init_ima_ns.c
> @@ -58,5 +58,6 @@ struct ima_namespace init_ima_ns = {
>  	.ima_lsm_policy_notifier = {
>  		.notifier_call = ima_lsm_policy_change,
>  	},
> +	.active = ATOMIC_INIT(1),
>  };
>  EXPORT_SYMBOL(init_ima_ns);
> diff --git a/security/integrity/ima/ima_main.c b/security/integrity/ima/ima_main.c
> index 1dee8cb5afa2..9ca9223bbcf6 100644
> --- a/security/integrity/ima/ima_main.c
> +++ b/security/integrity/ima/ima_main.c
> @@ -441,7 +441,7 @@ static int process_measurement(struct user_namespace *user_ns,
>  
>  	while (user_ns) {
>  		ns = ima_ns_from_user_ns(user_ns);
> -		if (ns) {
> +		if (ns_is_active(ns)) {
>  			int rc;
>  
>  			rc = __process_measurement(ns, file, cred, secid, buf,



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

* Re: [PATCH v10 25/27] ima: Show owning user namespace's uid and gid when displaying policy
  2022-02-01 20:37 ` [PATCH v10 25/27] ima: Show owning user namespace's uid and gid when displaying policy Stefan Berger
@ 2022-02-23 14:06   ` Mimi Zohar
  0 siblings, 0 replies; 86+ messages in thread
From: Mimi Zohar @ 2022-02-23 14:06 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

On Tue, 2022-02-01 at 15:37 -0500, Stefan Berger wrote:
> Show the uid and gid values relative to the user namespace that is
> currently active. The effect of this changes is that when one displays
> the policy from the user namespace that originally set the policy,
> 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>

thanks,

Reviewed-by:  Mimi Zohar <zohar@linux.ibm.com>


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

* Re: [PATCH v10 26/27] ima: Limit number of policy rules in non-init_ima_ns
  2022-02-01 20:37 ` [PATCH v10 26/27] ima: Limit number of policy rules in non-init_ima_ns Stefan Berger
@ 2022-02-23 15:38   ` Mimi Zohar
  2022-02-23 16:37     ` Stefan Berger
  0 siblings, 1 reply; 86+ messages in thread
From: Mimi Zohar @ 2022-02-23 15:38 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

On Tue, 2022-02-01 at 15:37 -0500, Stefan Berger wrote:
> Limit the number of policy rules one can set in non-init_ima_ns to a
> hardcoded 1024 rules. If the user attempts to exceed this limit by
> setting too many additional rules, emit an audit message with the cause
> 'too-many-rules' and simply ignore the newly added rules.

This paragraph describes 'what' you're doing, not 'why'.  Please prefix
this paragraph with a short, probably one sentence, reason for the
change.
> 
> Switch the accounting for the memory allocated for IMA policy rules to
> GFP_KERNEL_ACCOUNT so that cgroups kernel memory accounting takes effect.

Does this change affect the existing IMA policy rules for init_ima_ns?

> 
> Signed-off-by: Stefan Berger <stefanb@linux.ibm.com>
> ---
>  security/integrity/ima/ima_fs.c     | 20 ++++++++++++++------
>  security/integrity/ima/ima_policy.c | 23 ++++++++++++++++++-----
>  2 files changed, 32 insertions(+), 11 deletions(-)
> 
> diff --git a/security/integrity/ima/ima_fs.c b/security/integrity/ima/ima_fs.c
> index 58d80884880f..cd102bbd4677 100644
> --- a/security/integrity/ima/ima_fs.c
> +++ b/security/integrity/ima/ima_fs.c
> @@ -410,24 +410,32 @@ static int ima_release_policy(struct inode *inode, struct file *file)
>  {
>  	struct ima_namespace *ns = &init_ima_ns;
>  	const char *cause = ns->valid_policy ? "completed" : "failed";
> +	int err = 0;
>  
>  	if ((file->f_flags & O_ACCMODE) == O_RDONLY)
>  		return seq_release(inode, file);
>  
> -	if (ns->valid_policy && ima_check_policy(ns) < 0) {
> -		cause = "failed";
> -		ns->valid_policy = 0;
> +	if (ns->valid_policy) {
> +		err = ima_check_policy(ns);
> +		if (err < 0) {
> +			if (err == -ENOSPC)

Perhaps "EDQUOT" would be more appropriate.

> +				cause = "too-many-rules";
> +			else
> +				cause = "failed";
> +			ns->valid_policy = 0;
> +		}
>  	}
>  
>  	pr_info("policy update %s\n", cause);
> -	integrity_audit_msg(AUDIT_INTEGRITY_STATUS, NULL, NULL,
> -			    "policy_update", cause, !ns->valid_policy, 0);
> +	integrity_audit_message(AUDIT_INTEGRITY_STATUS, NULL, NULL,
> +				"policy_update", cause, !ns->valid_policy, 0,
> +				-err);

The 'err' value is already properly set.  No need for the minus sign.

>  
>  	if (!ns->valid_policy) {
>  		ima_delete_rules(ns);
>  		ns->valid_policy = 1;
>  		clear_bit(IMA_FS_BUSY, &ns->ima_fs_flags);
> -		return 0;
> +		return err;
>  	}
>  
>  	ima_update_policy(ns);
> diff --git a/security/integrity/ima/ima_policy.c b/security/integrity/ima/ima_policy.c
> index acb4c36e539f..3f84d04f101d 100644
> --- a/security/integrity/ima/ima_policy.c
> +++ b/security/integrity/ima/ima_policy.c
> @@ -305,7 +305,8 @@ static struct ima_rule_opt_list *ima_alloc_rule_opt_list(const substring_t *src)
>  		return ERR_PTR(-EINVAL);
>  	}
>  
> -	opt_list = kzalloc(struct_size(opt_list, items, count), GFP_KERNEL);
> +	opt_list = kzalloc(struct_size(opt_list, items, count),
> +			   GFP_KERNEL_ACCOUNT);
>  	if (!opt_list) {
>  		kfree(src_copy);
>  		return ERR_PTR(-ENOMEM);
> @@ -379,7 +380,7 @@ static struct ima_rule_entry *ima_lsm_copy_rule(struct ima_namespace *ns,
>  	 * Immutable elements are copied over as pointers and data; only
>  	 * lsm rules can change
>  	 */
> -	nentry = kmemdup(entry, sizeof(*nentry), GFP_KERNEL);
> +	nentry = kmemdup(entry, sizeof(*nentry), GFP_KERNEL_ACCOUNT);
>  	if (!nentry)
>  		return NULL;
>  
> @@ -826,7 +827,7 @@ static void add_rules(struct ima_namespace *ns,
>  
>  		if (policy_rule & IMA_CUSTOM_POLICY) {
>  			entry = kmemdup(&entries[i], sizeof(*entry),
> -					GFP_KERNEL);
> +					GFP_KERNEL_ACCOUNT);
>  			if (!entry)
>  				continue;
>  
> @@ -863,7 +864,7 @@ static int __init ima_init_arch_policy(struct ima_namespace *ns)
>  
>  	ns->arch_policy_entry = kcalloc(arch_entries + 1,
>  					sizeof(*ns->arch_policy_entry),
> -					GFP_KERNEL);
> +					GFP_KERNEL_ACCOUNT);
>  	if (!ns->arch_policy_entry)
>  		return 0;
>  
> @@ -975,8 +976,20 @@ void __init ima_init_policy(struct ima_namespace *ns)
>  /* Make sure we have a valid policy, at least containing some rules. */
>  int ima_check_policy(struct ima_namespace *ns)
>  {
> +	struct ima_rule_entry *entry;
> +	size_t len1 = 0;
> +	size_t len2 = 0;
> +
>  	if (list_empty(&ns->ima_temp_rules))
>  		return -EINVAL;
> +	if (ns != &init_ima_ns) {
> +		list_for_each_entry(entry, &ns->ima_temp_rules, list)
> +			len1++;
> +		list_for_each_entry(entry, &ns->ima_policy_rules, list)

Using list_for_each_entry() is fine here, because multiple policy
updates at the same time are blocked.  Please add a comment.

> +			len2++;
> +		if (len1 + len2 > 1024)

One variable should be enough.

> +			return -ENOSPC;
> +	}
>  	return 0;
>  }
>  
> @@ -1848,7 +1861,7 @@ ssize_t ima_parse_add_rule(struct ima_namespace *ns, char *rule)
>  	if (*p == '#' || *p == '\0')
>  		return len;
>  
> -	entry = kzalloc(sizeof(*entry), GFP_KERNEL);
> +	entry = kzalloc(sizeof(*entry), GFP_KERNEL_ACCOUNT);
>  	if (!entry) {
>  		integrity_audit_msg(AUDIT_INTEGRITY_STATUS, NULL,
>  				    NULL, op, "-ENOMEM", -ENOMEM, audit_info);

-- 
thanks,

Mimi


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

* Re: [PATCH v10 22/27] securityfs: Extend securityfs with namespacing support
  2022-02-23  8:14     ` Christian Brauner
@ 2022-02-23 15:44       ` Stefan Berger
  0 siblings, 0 replies; 86+ messages in thread
From: Stefan Berger @ 2022-02-23 15:44 UTC (permalink / raw)
  To: Christian Brauner, Mimi Zohar
  Cc: 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, James Bottomley


On 2/23/22 03:14, Christian Brauner wrote:
> On Tue, Feb 22, 2022 at 08:48:47PM -0500, Mimi Zohar wrote:
>> On Tue, 2022-02-01 at 15:37 -0500, Stefan Berger wrote:
>>> Enable multiple instances of securityfs by keying each instance with a
>>> pointer to the user namespace it belongs to.
>>>
>>> Since we do not need the pinning of the filesystem for the virtualization
>> ^namespacing case
>>
>>> case, limit the usage of simple_pin_fs() and simpe_release_fs() to the
>> ^simple_release_fs
>>
>>> case when the init_user_ns is active. This simplifies the cleanup for the
>>> virtualization case where usage of securityfs_remove() to free dentries
>> ^namespacing
>>
>>> is therefore not needed anymore.
>>>
>>> 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 filesystem 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().
>>>
>>> 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 is neither the user namespace it belongs to
>>> nor an ancestor of the user namespace that the instance of securityfs
>>> belongs to. Do not prevent access if securityfs was bind-mounted and
>>> therefore the init_user_ns is the owning user namespace.
>>>
>>> Suggested-by: Christian Brauner <brauner@kernel.org>
>>> Signed-off-by: Stefan Berger <stefanb@linux.ibm.com>
>>> Signed-off-by: James Bottomley <James.Bottomley@HansenPartnership.com>
>> Christian, I understand that "[PATCH v10 23/27] ima: Setup securityfs
>> for IMA namespace" needs to be deferred, but is there a reason for
>> deferring  "[PATCH v10 22/27] securityfs: Extend securityfs with
>> namespacing support"?   As the securityfs patches are really
>> independent of IMA namespacing, I would have thought  "[PATCH v10
>> 04/27] securityfs: rework dentry creation" and this patch should be co-
>> located at the beginning of the patch set.
> It felt more natural to me to defer it until the end but I have no
> strong thoughts on this as of right now. Since Stefan has mentioned
> moving this earlier already himself and you seem to agree as well, feel
> free to do so.

I'll move it after 'securityfs: rework dentry creation' if that's ok.

    Stefan


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

* Re: [PATCH v10 18/27] integrity/ima: Define ns_status for storing namespaced iint data
  2022-02-01 20:37 ` [PATCH v10 18/27] integrity/ima: Define ns_status for storing namespaced iint data Stefan Berger
@ 2022-02-23 16:12   ` Mimi Zohar
  2022-02-24  2:21     ` Stefan Berger
  0 siblings, 1 reply; 86+ messages in thread
From: Mimi Zohar @ 2022-02-23 16:12 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, Mehmet Kayaalp

On Tue, 2022-02-01 at 15:37 -0500, Stefan Berger wrote:
> 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.
> 
> To simplify the code in the non-namespaces case embed an ns_status in
> the integrity_iint_cache and have it linked into the iint's ns_status list
> when calling ima_get_ns_status().
> 
> 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 concurrently
> but no two threads may run and one delete an inode and the other an
> ima_namespace.

The locking involved here is really complex.  I'm sure you thought
about it a lot, but isn't there a better alternative?

> 
> Signed-off-by: Mehmet Kayaalp <mkayaalp@linux.vnet.ibm.com>
> Signed-off-by: Stefan Berger <stefanb@linux.ibm.com>
> 
> ---
> v9:
>  - Move ns_status into integrity.h and embedded it into integrity_iint_cache
>    for the non-CONFIG_IMA_NS case

<snip>

>  
> +/*
> + * 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 *ns_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(ns_status, next,
> +						     &ns->ns_status_tree,
> +						     rb_node) {
> +			write_lock(&ns_status->iint->ns_list_lock);
> +			if (!list_empty(&ns_status->ns_next)) {
> +				list_del_init(&ns_status->ns_next);
> +				llist_add(&ns_status->gc_llist, &garbage);
> +				ctr++;
> +			}

At this point when the namespace is being deleted, no entries are being
added to the rbtree, so it is safe to remove the nodes here.  There's
no need to first create a list and then remove them.

> +			write_unlock(&ns_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;
> +			}

Giving priority to removing entries in the iint cache is important, but
I wish there was a better alternative.

> +		}
> +
> +		write_unlock(&ns->ns_tree_lock);
> +		unlock_group(GRP_NS_STATUS_TREE);
> +	} while (restart);
> +
> +	node = llist_del_all(&garbage);
> +	llist_for_each_entry_safe(ns_status, next, node, gc_llist)
> +		ns_status_free(ns, ns_status);
> +
> +	kmem_cache_destroy(ns->ns_status_cache);
> +}

-- 
thanks,

Mimi


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

* Re: [PATCH v10 26/27] ima: Limit number of policy rules in non-init_ima_ns
  2022-02-23 15:38   ` Mimi Zohar
@ 2022-02-23 16:37     ` Stefan Berger
  2022-02-23 17:04       ` Mimi Zohar
  0 siblings, 1 reply; 86+ messages in thread
From: Stefan Berger @ 2022-02-23 16:37 UTC (permalink / raw)
  To: Mimi Zohar, 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 2/23/22 10:38, Mimi Zohar wrote:
> On Tue, 2022-02-01 at 15:37 -0500, Stefan Berger wrote:
>> Limit the number of policy rules one can set in non-init_ima_ns to a
>> hardcoded 1024 rules. If the user attempts to exceed this limit by
>> setting too many additional rules, emit an audit message with the cause
>> 'too-many-rules' and simply ignore the newly added rules.
> This paragraph describes 'what' you're doing, not 'why'.  Please prefix
> this paragraph with a short, probably one sentence, reason for the
> change.
>> Switch the accounting for the memory allocated for IMA policy rules to
>> GFP_KERNEL_ACCOUNT so that cgroups kernel memory accounting takes effect.
> Does this change affect the existing IMA policy rules for init_ima_ns?

There's typically no cgroup for the int_ima_ns, so not effect on 
init_ima_ns.


Here's the updated text:

Limit the number of policy rules one can set in non-init_ima_ns to a
hardcoded 1024 rules to restrict the amount of memory used for IMA's
policy. Ignore the added rules if the user attempts to exceed this
limit by setting too many additional rules.

Switch the accounting for the memory allocated for IMA policy rules to
GFP_KERNEL_ACCOUNT so that cgroups kernel memory accounting takes effect.
This switch has no effect on the init_ima_ns.

    Stefan


>> Signed-off-by: Stefan Berger <stefanb@linux.ibm.com>
>> ---
>>   security/integrity/ima/ima_fs.c     | 20 ++++++++++++++------
>>   security/integrity/ima/ima_policy.c | 23 ++++++++++++++++++-----
>>   2 files changed, 32 insertions(+), 11 deletions(-)
>>
>> diff --git a/security/integrity/ima/ima_fs.c b/security/integrity/ima/ima_fs.c
>> index 58d80884880f..cd102bbd4677 100644
>> --- a/security/integrity/ima/ima_fs.c
>> +++ b/security/integrity/ima/ima_fs.c
>> @@ -410,24 +410,32 @@ static int ima_release_policy(struct inode *inode, struct file *file)
>>   {
>>   	struct ima_namespace *ns = &init_ima_ns;
>>   	const char *cause = ns->valid_policy ? "completed" : "failed";
>> +	int err = 0;
>>   
>>   	if ((file->f_flags & O_ACCMODE) == O_RDONLY)
>>   		return seq_release(inode, file);
>>   
>> -	if (ns->valid_policy && ima_check_policy(ns) < 0) {
>> -		cause = "failed";
>> -		ns->valid_policy = 0;
>> +	if (ns->valid_policy) {
>> +		err = ima_check_policy(ns);
>> +		if (err < 0) {
>> +			if (err == -ENOSPC)
> Perhaps "EDQUOT" would be more appropriate.
>
>> +				cause = "too-many-rules";
>> +			else
>> +				cause = "failed";
>> +			ns->valid_policy = 0;
>> +		}
>>   	}
>>   
>>   	pr_info("policy update %s\n", cause);
>> -	integrity_audit_msg(AUDIT_INTEGRITY_STATUS, NULL, NULL,
>> -			    "policy_update", cause, !ns->valid_policy, 0);
>> +	integrity_audit_message(AUDIT_INTEGRITY_STATUS, NULL, NULL,
>> +				"policy_update", cause, !ns->valid_policy, 0,
>> +				-err);
> The 'err' value is already properly set.  No need for the minus sign.
>
>>   
>>   	if (!ns->valid_policy) {
>>   		ima_delete_rules(ns);
>>   		ns->valid_policy = 1;
>>   		clear_bit(IMA_FS_BUSY, &ns->ima_fs_flags);
>> -		return 0;
>> +		return err;
>>   	}
>>   
>>   	ima_update_policy(ns);
>> diff --git a/security/integrity/ima/ima_policy.c b/security/integrity/ima/ima_policy.c
>> index acb4c36e539f..3f84d04f101d 100644
>> --- a/security/integrity/ima/ima_policy.c
>> +++ b/security/integrity/ima/ima_policy.c
>> @@ -305,7 +305,8 @@ static struct ima_rule_opt_list *ima_alloc_rule_opt_list(const substring_t *src)
>>   		return ERR_PTR(-EINVAL);
>>   	}
>>   
>> -	opt_list = kzalloc(struct_size(opt_list, items, count), GFP_KERNEL);
>> +	opt_list = kzalloc(struct_size(opt_list, items, count),
>> +			   GFP_KERNEL_ACCOUNT);
>>   	if (!opt_list) {
>>   		kfree(src_copy);
>>   		return ERR_PTR(-ENOMEM);
>> @@ -379,7 +380,7 @@ static struct ima_rule_entry *ima_lsm_copy_rule(struct ima_namespace *ns,
>>   	 * Immutable elements are copied over as pointers and data; only
>>   	 * lsm rules can change
>>   	 */
>> -	nentry = kmemdup(entry, sizeof(*nentry), GFP_KERNEL);
>> +	nentry = kmemdup(entry, sizeof(*nentry), GFP_KERNEL_ACCOUNT);
>>   	if (!nentry)
>>   		return NULL;
>>   
>> @@ -826,7 +827,7 @@ static void add_rules(struct ima_namespace *ns,
>>   
>>   		if (policy_rule & IMA_CUSTOM_POLICY) {
>>   			entry = kmemdup(&entries[i], sizeof(*entry),
>> -					GFP_KERNEL);
>> +					GFP_KERNEL_ACCOUNT);
>>   			if (!entry)
>>   				continue;
>>   
>> @@ -863,7 +864,7 @@ static int __init ima_init_arch_policy(struct ima_namespace *ns)
>>   
>>   	ns->arch_policy_entry = kcalloc(arch_entries + 1,
>>   					sizeof(*ns->arch_policy_entry),
>> -					GFP_KERNEL);
>> +					GFP_KERNEL_ACCOUNT);
>>   	if (!ns->arch_policy_entry)
>>   		return 0;
>>   
>> @@ -975,8 +976,20 @@ void __init ima_init_policy(struct ima_namespace *ns)
>>   /* Make sure we have a valid policy, at least containing some rules. */
>>   int ima_check_policy(struct ima_namespace *ns)
>>   {
>> +	struct ima_rule_entry *entry;
>> +	size_t len1 = 0;
>> +	size_t len2 = 0;
>> +
>>   	if (list_empty(&ns->ima_temp_rules))
>>   		return -EINVAL;
>> +	if (ns != &init_ima_ns) {
>> +		list_for_each_entry(entry, &ns->ima_temp_rules, list)
>> +			len1++;
>> +		list_for_each_entry(entry, &ns->ima_policy_rules, list)
> Using list_for_each_entry() is fine here, because multiple policy
> updates at the same time are blocked.  Please add a comment.
>
>> +			len2++;
>> +		if (len1 + len2 > 1024)
> One variable should be enough.
>
>> +			return -ENOSPC;
>> +	}
>>   	return 0;
>>   }
>>   
>> @@ -1848,7 +1861,7 @@ ssize_t ima_parse_add_rule(struct ima_namespace *ns, char *rule)
>>   	if (*p == '#' || *p == '\0')
>>   		return len;
>>   
>> -	entry = kzalloc(sizeof(*entry), GFP_KERNEL);
>> +	entry = kzalloc(sizeof(*entry), GFP_KERNEL_ACCOUNT);
>>   	if (!entry) {
>>   		integrity_audit_msg(AUDIT_INTEGRITY_STATUS, NULL,
>>   				    NULL, op, "-ENOMEM", -ENOMEM, audit_info);

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

* Re: [PATCH v10 26/27] ima: Limit number of policy rules in non-init_ima_ns
  2022-02-23 16:37     ` Stefan Berger
@ 2022-02-23 17:04       ` Mimi Zohar
  2022-02-23 20:45         ` Stefan Berger
  0 siblings, 1 reply; 86+ messages in thread
From: Mimi Zohar @ 2022-02-23 17:04 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

On Wed, 2022-02-23 at 11:37 -0500, Stefan Berger wrote:
> On 2/23/22 10:38, Mimi Zohar wrote:
> > On Tue, 2022-02-01 at 15:37 -0500, Stefan Berger wrote:
> >> Limit the number of policy rules one can set in non-init_ima_ns to a
> >> hardcoded 1024 rules. If the user attempts to exceed this limit by
> >> setting too many additional rules, emit an audit message with the cause
> >> 'too-many-rules' and simply ignore the newly added rules.
> > This paragraph describes 'what' you're doing, not 'why'.  Please prefix
> > this paragraph with a short, probably one sentence, reason for the
> > change.
> >> Switch the accounting for the memory allocated for IMA policy rules to
> >> GFP_KERNEL_ACCOUNT so that cgroups kernel memory accounting takes effect.
> > Does this change affect the existing IMA policy rules for init_ima_ns?
> 
> There's typically no cgroup for the int_ima_ns, so not effect on 
> init_ima_ns.
> 
> Here's the updated text:
> 
> Limit the number of policy rules one can set in non-init_ima_ns to a
> hardcoded 1024 rules to restrict the amount of memory used for IMA's
> policy.

The question is "why" there should be a difference between the
init_ima_ns and non-init_ima_ns cases.  Perhaps something like, "Only
host root with CAP_SYS_ADMIN may write init_ima_ns policy rules, but in
the non-init_ima_ns case root in the namespace with CAP_MAC_ADMIN
privileges may write IMA policy rules.  Limit the total number of IMA
policy rules per namespace."

>  Ignore the added rules if the user attempts to exceed this
> limit by setting too many additional rules.
> 
> Switch the accounting for the memory allocated for IMA policy rules to
> GFP_KERNEL_ACCOUNT so that cgroups kernel memory accounting takes effect.
> This switch has no effect on the init_ima_ns.

thanks,

Mimi


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

* Re: [PATCH v10 24/27] ima: Introduce securityfs file to activate an IMA namespace
  2022-02-23 13:54   ` Mimi Zohar
@ 2022-02-23 17:08     ` Stefan Berger
  2022-02-23 17:12       ` Mimi Zohar
  0 siblings, 1 reply; 86+ messages in thread
From: Stefan Berger @ 2022-02-23 17:08 UTC (permalink / raw)
  To: Mimi Zohar, 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 2/23/22 08:54, Mimi Zohar wrote:
> On Tue, 2022-02-01 at 15:37 -0500, Stefan Berger wrote:
>
>>   int ima_fs_ns_init(struct user_namespace *user_ns, struct dentry *root)
>>   {
>>   	struct ima_namespace *ns = ima_ns_from_user_ns(user_ns);
>> @@ -531,6 +596,12 @@ int ima_fs_ns_init(struct user_namespace *user_ns, struct dentry *root)
>>   		}
>>   	}
>>   
>> +	if (ns != &init_ima_ns) {
>> +		ret = ima_fs_add_ns_files(ima_dir);
>> +		if (ret)
>> +			goto out;
>> +	}
>> +
> In all other cases, the securityfs files are directly created in
> ima_fs_ns_init().   What is different about "active" that a new
> function is defined?


It was meant as a function to create namespace-specific files, if more 
were to come along.  I can move the code from ima_fs_add_ns_files() into 
this function if you want.


>
> thanks,
>
> Mimi
>
>

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

* Re: [PATCH v10 24/27] ima: Introduce securityfs file to activate an IMA namespace
  2022-02-23 17:08     ` Stefan Berger
@ 2022-02-23 17:12       ` Mimi Zohar
  2022-02-23 22:30         ` Stefan Berger
  0 siblings, 1 reply; 86+ messages in thread
From: Mimi Zohar @ 2022-02-23 17:12 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

On Wed, 2022-02-23 at 12:08 -0500, Stefan Berger wrote:
> On 2/23/22 08:54, Mimi Zohar wrote:
> > On Tue, 2022-02-01 at 15:37 -0500, Stefan Berger wrote:
> >
> >>   int ima_fs_ns_init(struct user_namespace *user_ns, struct dentry *root)
> >>   {
> >>   	struct ima_namespace *ns = ima_ns_from_user_ns(user_ns);
> >> @@ -531,6 +596,12 @@ int ima_fs_ns_init(struct user_namespace *user_ns, struct dentry *root)
> >>   		}
> >>   	}
> >>   
> >> +	if (ns != &init_ima_ns) {
> >> +		ret = ima_fs_add_ns_files(ima_dir);
> >> +		if (ret)
> >> +			goto out;
> >> +	}
> >> +
> > In all other cases, the securityfs files are directly created in
> > ima_fs_ns_init().   What is different about "active" that a new
> > function is defined?
> 
> 
> It was meant as a function to create namespace-specific files, if more 
> were to come along.  I can move the code from ima_fs_add_ns_files() into 
> this function if you want.

Perhaps defer defining a new function until that happens.

thanks,

Mimi


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

* Re: [PATCH v10 12/27] ima: Define mac_admin_ns_capable() as a wrapper for ns_capable()
  2022-02-07 18:43       ` Stefan Berger
@ 2022-02-23 17:51         ` Serge E. Hallyn
  0 siblings, 0 replies; 86+ messages in thread
From: Serge E. Hallyn @ 2022-02-23 17:51 UTC (permalink / raw)
  To: Stefan Berger
  Cc: Serge E. Hallyn, linux-integrity, zohar, christian.brauner,
	containers, dmitry.kasatkin, ebiederm, krzysztof.struczynski,
	roberto.sassu, mpeters, lhinds, lsturman, puiterwi, jejb,
	jamjoom, linux-kernel, paul, rgb, linux-security-module, jmorris,
	Denis Semakin

On Mon, Feb 07, 2022 at 01:43:31PM -0500, Stefan Berger wrote:
> 
> On 2/6/22 12:20, Stefan Berger wrote:
> > 
> > On 2/5/22 00:58, Serge E. Hallyn wrote:
> > > On Tue, Feb 01, 2022 at 03:37:20PM -0500, Stefan Berger wrote:
> > > > Define mac_admin_ns_capable() as a wrapper for the combined
> > > > ns_capable()
> > > > checks on CAP_MAC_ADMIN and CAP_SYS_ADMIN in a user namespace. Return
> > > > true on the check if either capability or both are available.
> > > > 
> > > > Use mac_admin_ns_capable() in place of capable(SYS_ADMIN). This
> > > > will allow
> > > > an IMA namespace to read the policy with only CAP_MAC_ADMIN, which has
> > > > less privileges than CAP_SYS_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 | 5 ++++-
> > > >   3 files changed, 16 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);
> > > Do you care about audit warnings?  If the task has CAP_SYS_ADMIN but
> > > not CAP_MAC_ADMIN, is it desirable that selinux_capable() will audit the
> > > CAP_MAC_ADMIN failure?
> > 
> > Good point.  I will switch both to ns_capable_noaudit() so that the user
> > cannot provoke unnecessary audit message.
> 
> Actually,  I will only change the MAC_ADMIN to not do auditing and not
> change the auditing behavior related to SYS_ADMIN.

Right, that makes the most sense.

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

* Re: [PATCH v10 27/27] ima: Enable IMA namespaces
  2022-02-01 20:37 ` [PATCH v10 27/27] ima: Enable IMA namespaces Stefan Berger
@ 2022-02-23 17:58   ` Serge E. Hallyn
  2022-02-23 20:53     ` Stefan Berger
  0 siblings, 1 reply; 86+ messages in thread
From: Serge E. Hallyn @ 2022-02-23 17: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

On Tue, Feb 01, 2022 at 03:37:35PM -0500, Stefan Berger wrote:
> Introduce the IMA_NS in Kconfig for IMA namespace enablement.
> 
> Enable the lazy initialization of an IMA namespace when a user mounts
> SecurityFS and writes '1' into IMA's 'active' securityfs file. A
> user_namespace will now get a pointer to an ima_namespace and therefore
> implement get_current_ns() for the namespacing case that returns this
> pointer. Use get_current_ns() in those places that require access to the
> current IMA namespace. In some places, primarily those related to
> IMA-appraisal and changes to file attributes, keep the pointer to
> init_ima_ns, since there flags related to file measurements may be
> affected, which are not supported in IMA namespaces, yet.
> 
> Before using the ima_namespace pointer test it with ns_is_active()
> to check whether it is NULL and whether the ima_namespace is active.
> If it's not active, it cannot be used, yet. Therefore, return early
> from those functions that may now get either get a NULL pointer from
> this call or where ns->active is still 0. The init_ima_ns is always
> set to be active, thus passing the check.
> 
> 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.
> 
> Return -EACCES to IMA's securityfs files, except for the 'active' file,
> until the IMA namespace has been set to active.
> 
> 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.
> 
> Only emit the kernel log message 'policy update completed' for the
> init_ima_ns.
> 
> When parsing an IMA policy rule use the user namespace of the opener
> to translate uid and gid values to kernel values rather than the user
> namespace of the writer.
> 
> Gate access to ima_appraise variable to init_ima_ns in ima_load_data()
> and ima_write_policy().
> 
> Gate access to temp_ima_appraise variable to init_ima_ns in
> ima_delete_rules().
> 
> Signed-off-by: Stefan Berger <stefanb@linux.ibm.com>
> 
> ---
> v10:
>  - dropped ima_ns_to_user_ns(); using current_user_ns() instead
>  - Pass user_namespace of file opener into ima_parse_rule and propagate
>    this parameter back all the way to the initial caller in the chain
>  - Gate access to ima_appraise to init_ima_ns in ima_write_policy()
> 
> v9:
>  - ima_post_key_create_or_update: Only handle key if in init_ima_ns
>  - Removed ns == NULL checks where user_namespace is now passed
>  - Defer setting of user_ns->ima_ns until end of ima_fs_ns_init();
>    required new ima_free_imans() and new user_ns_set_ima_ns()
>  - Only emit log message 'policy update completed' for init_ima_ns
>  - Introduce get_current_ns() only in this patch
>  - Check for ns == &init_ima_ns in ima_load_data()
> ---
>  include/linux/ima.h                          |  1 +
>  init/Kconfig                                 | 13 +++
>  kernel/user_namespace.c                      |  2 +
>  security/integrity/ima/ima.h                 | 55 +++++++++++--
>  security/integrity/ima/ima_appraise.c        |  3 +
>  security/integrity/ima/ima_asymmetric_keys.c |  6 +-
>  security/integrity/ima/ima_fs.c              | 87 ++++++++++++++++----
>  security/integrity/ima/ima_init.c            |  2 +-
>  security/integrity/ima/ima_init_ima_ns.c     |  2 +
>  security/integrity/ima/ima_main.c            | 34 +++++---
>  security/integrity/ima/ima_ns.c              | 15 +++-
>  security/integrity/ima/ima_policy.c          | 43 ++++++----
>  12 files changed, 202 insertions(+), 61 deletions(-)
> 
> diff --git a/include/linux/ima.h b/include/linux/ima.h
> index c584527c0f47..a8cb2c269f61 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;
>  
> 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 05e2de7697da..73df1d8a2ece 100644
> --- a/security/integrity/ima/ima.h
> +++ b/security/integrity/ima/ima.h
> @@ -337,10 +337,10 @@ int ima_match_policy(struct ima_namespace *ns,
>  		     int mask, int flags, int *pcr,
>  		     struct ima_template_desc **template_desc,
>  		     const char *func_data, unsigned int *allowed_algos);
> -void ima_init_policy(struct ima_namespace *ns);
> +void ima_init_policy(struct user_namespace *user_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);
> +ssize_t ima_parse_add_rule(struct user_namespace *user_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);
> @@ -538,32 +538,70 @@ 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 user_ns_set_ima_ns(). */
> +	return smp_load_acquire(&user_ns->ima_ns);
>  }
>  
> -#ifdef CONFIG_IMA_NS
> +static inline void user_ns_set_ima_ns(struct user_namespace *user_ns,
> +				      struct ima_namespace *ns)
> +{
> +	/* Pairs with smp_load_acquire() in ima_ns_from_user_ns() */
> +	smp_store_release(&user_ns->ima_ns, ns);
> +}
> +
> +static inline struct ima_namespace *get_current_ns(void)
> +{
> +	return ima_ns_from_user_ns(current_user_ns());
> +}
>  
>  struct ima_namespace *create_ima_ns(void);
>  
> +void ima_free_ima_ns(struct ima_namespace *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);
>  
> +static inline struct ima_namespace *ima_ns_from_file(const struct file *filp)
> +{

Why is it ok here to dereference userns->ima_ns without
going through ima_ns_from_user_ns() to do the smp_load_acquire()?

> +	return ima_user_ns_from_file(filp)->ima_ns;
> +}
> +
>  #else

-serge

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

* Re: [PATCH v10 26/27] ima: Limit number of policy rules in non-init_ima_ns
  2022-02-23 17:04       ` Mimi Zohar
@ 2022-02-23 20:45         ` Stefan Berger
  2022-02-23 20:59           ` Mimi Zohar
  0 siblings, 1 reply; 86+ messages in thread
From: Stefan Berger @ 2022-02-23 20:45 UTC (permalink / raw)
  To: Mimi Zohar, 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 2/23/22 12:04, Mimi Zohar wrote:
> On Wed, 2022-02-23 at 11:37 -0500, Stefan Berger wrote:
>> On 2/23/22 10:38, Mimi Zohar wrote:
>>> On Tue, 2022-02-01 at 15:37 -0500, Stefan Berger wrote:
>>>> Limit the number of policy rules one can set in non-init_ima_ns to a
>>>> hardcoded 1024 rules. If the user attempts to exceed this limit by
>>>> setting too many additional rules, emit an audit message with the cause
>>>> 'too-many-rules' and simply ignore the newly added rules.
>>> This paragraph describes 'what' you're doing, not 'why'.  Please prefix
>>> this paragraph with a short, probably one sentence, reason for the
>>> change.
>>>> Switch the accounting for the memory allocated for IMA policy rules to
>>>> GFP_KERNEL_ACCOUNT so that cgroups kernel memory accounting takes effect.
>>> Does this change affect the existing IMA policy rules for init_ima_ns?
>> There's typically no cgroup for the int_ima_ns, so not effect on
>> init_ima_ns.
>>
>> Here's the updated text:
>>
>> Limit the number of policy rules one can set in non-init_ima_ns to a
>> hardcoded 1024 rules to restrict the amount of memory used for IMA's
>> policy.
> The question is "why" there should be a difference between the
> init_ima_ns and non-init_ima_ns cases.  Perhaps something like, "Only


Chistian had suggested it to not have the number of policy rules unbounded.


> host root with CAP_SYS_ADMIN may write init_ima_ns policy rules, but in
> the non-init_ima_ns case root in the namespace with CAP_MAC_ADMIN
> privileges may write IMA policy rules.  Limit the total number of IMA
> policy rules per namespace."

What does it have to do with CAP_SYS_ADMIN and CAP_MAC_ADMIN and why 
mention these here? It seem primarily a limit of number of rules to 
avoid huge kernel memory consumption in the case that a cgroup limit for 
memory was not set up.


>
>>   Ignore the added rules if the user attempts to exceed this
>> limit by setting too many additional rules.
>>
>> Switch the accounting for the memory allocated for IMA policy rules to
>> GFP_KERNEL_ACCOUNT so that cgroups kernel memory accounting takes effect.
>> This switch has no effect on the init_ima_ns.
> thanks,
>
> Mimi
>

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

* Re: [PATCH v10 27/27] ima: Enable IMA namespaces
  2022-02-23 17:58   ` Serge E. Hallyn
@ 2022-02-23 20:53     ` Stefan Berger
  0 siblings, 0 replies; 86+ messages in thread
From: Stefan Berger @ 2022-02-23 20:53 UTC (permalink / raw)
  To: Serge E. Hallyn
  Cc: linux-integrity, zohar, 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 2/23/22 12:58, Serge E. Hallyn wrote:
> On Tue, Feb 01, 2022 at 03:37:35PM -0500, Stefan Berger wrote:
>> Introduce the IMA_NS in Kconfig for IMA namespace enablement.
>>
>> Enable the lazy initialization of an IMA namespace when a user mounts
>> SecurityFS and writes '1' into IMA's 'active' securityfs file. A
>> user_namespace will now get a pointer to an ima_namespace and therefore
>> implement get_current_ns() for the namespacing case that returns this
>> pointer. Use get_current_ns() in those places that require access to the
>> current IMA namespace. In some places, primarily those related to
>> IMA-appraisal and changes to file attributes, keep the pointer to
>> init_ima_ns, since there flags related to file measurements may be
>> affected, which are not supported in IMA namespaces, yet.
>>
>> Before using the ima_namespace pointer test it with ns_is_active()
>> to check whether it is NULL and whether the ima_namespace is active.
>> If it's not active, it cannot be used, yet. Therefore, return early
>> from those functions that may now get either get a NULL pointer from
>> this call or where ns->active is still 0. The init_ima_ns is always
>> set to be active, thus passing the check.
>>
>> 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.
>>
>> Return -EACCES to IMA's securityfs files, except for the 'active' file,
>> until the IMA namespace has been set to active.
>>
>> 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.
>>
>> Only emit the kernel log message 'policy update completed' for the
>> init_ima_ns.
>>
>> When parsing an IMA policy rule use the user namespace of the opener
>> to translate uid and gid values to kernel values rather than the user
>> namespace of the writer.
>>
>> Gate access to ima_appraise variable to init_ima_ns in ima_load_data()
>> and ima_write_policy().
>>
>> Gate access to temp_ima_appraise variable to init_ima_ns in
>> ima_delete_rules().
>>
>> Signed-off-by: Stefan Berger <stefanb@linux.ibm.com>
>>
>> ---
>> v10:
>>   - dropped ima_ns_to_user_ns(); using current_user_ns() instead
>>   - Pass user_namespace of file opener into ima_parse_rule and propagate
>>     this parameter back all the way to the initial caller in the chain
>>   - Gate access to ima_appraise to init_ima_ns in ima_write_policy()
>>
>> v9:
>>   - ima_post_key_create_or_update: Only handle key if in init_ima_ns
>>   - Removed ns == NULL checks where user_namespace is now passed
>>   - Defer setting of user_ns->ima_ns until end of ima_fs_ns_init();
>>     required new ima_free_imans() and new user_ns_set_ima_ns()
>>   - Only emit log message 'policy update completed' for init_ima_ns
>>   - Introduce get_current_ns() only in this patch
>>   - Check for ns == &init_ima_ns in ima_load_data()
>> ---
>>   include/linux/ima.h                          |  1 +
>>   init/Kconfig                                 | 13 +++
>>   kernel/user_namespace.c                      |  2 +
>>   security/integrity/ima/ima.h                 | 55 +++++++++++--
>>   security/integrity/ima/ima_appraise.c        |  3 +
>>   security/integrity/ima/ima_asymmetric_keys.c |  6 +-
>>   security/integrity/ima/ima_fs.c              | 87 ++++++++++++++++----
>>   security/integrity/ima/ima_init.c            |  2 +-
>>   security/integrity/ima/ima_init_ima_ns.c     |  2 +
>>   security/integrity/ima/ima_main.c            | 34 +++++---
>>   security/integrity/ima/ima_ns.c              | 15 +++-
>>   security/integrity/ima/ima_policy.c          | 43 ++++++----
>>   12 files changed, 202 insertions(+), 61 deletions(-)
>>
>> diff --git a/include/linux/ima.h b/include/linux/ima.h
>> index c584527c0f47..a8cb2c269f61 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;
>>   
>> 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 05e2de7697da..73df1d8a2ece 100644
>> --- a/security/integrity/ima/ima.h
>> +++ b/security/integrity/ima/ima.h
>> @@ -337,10 +337,10 @@ int ima_match_policy(struct ima_namespace *ns,
>>   		     int mask, int flags, int *pcr,
>>   		     struct ima_template_desc **template_desc,
>>   		     const char *func_data, unsigned int *allowed_algos);
>> -void ima_init_policy(struct ima_namespace *ns);
>> +void ima_init_policy(struct user_namespace *user_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);
>> +ssize_t ima_parse_add_rule(struct user_namespace *user_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);
>> @@ -538,32 +538,70 @@ 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 user_ns_set_ima_ns(). */
>> +	return smp_load_acquire(&user_ns->ima_ns);
>>   }
>>   
>> -#ifdef CONFIG_IMA_NS
>> +static inline void user_ns_set_ima_ns(struct user_namespace *user_ns,
>> +				      struct ima_namespace *ns)
>> +{
>> +	/* Pairs with smp_load_acquire() in ima_ns_from_user_ns() */
>> +	smp_store_release(&user_ns->ima_ns, ns);
>> +}
>> +
>> +static inline struct ima_namespace *get_current_ns(void)
>> +{
>> +	return ima_ns_from_user_ns(current_user_ns());
>> +}
>>   
>>   struct ima_namespace *create_ima_ns(void);
>>   
>> +void ima_free_ima_ns(struct ima_namespace *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);
>>   
>> +static inline struct ima_namespace *ima_ns_from_file(const struct file *filp)
>> +{
> Why is it ok here to dereference userns->ima_ns without
> going through ima_ns_from_user_ns() to do the smp_load_acquire()?

Good catch! Fixed.

     Stefan



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

* Re: [PATCH v10 26/27] ima: Limit number of policy rules in non-init_ima_ns
  2022-02-23 20:45         ` Stefan Berger
@ 2022-02-23 20:59           ` Mimi Zohar
  2022-02-23 21:06             ` Stefan Berger
  0 siblings, 1 reply; 86+ messages in thread
From: Mimi Zohar @ 2022-02-23 20:59 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

On Wed, 2022-02-23 at 15:45 -0500, Stefan Berger wrote:

> avoid huge kernel memory consumption in the case that a cgroup limit for 
> memory was not set up.

Ok, that is the motivation for the this patch.


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

* Re: [PATCH v10 26/27] ima: Limit number of policy rules in non-init_ima_ns
  2022-02-23 20:59           ` Mimi Zohar
@ 2022-02-23 21:06             ` Stefan Berger
  0 siblings, 0 replies; 86+ messages in thread
From: Stefan Berger @ 2022-02-23 21:06 UTC (permalink / raw)
  To: Mimi Zohar, 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 2/23/22 15:59, Mimi Zohar wrote:
> On Wed, 2022-02-23 at 15:45 -0500, Stefan Berger wrote:
>
>> avoid huge kernel memory consumption in the case that a cgroup limit for
>> memory was not set up.
> Ok, that is the motivation for the this patch.
>
Any user can create several user namespaces and with that several IMA 
namespaces and now we want to limit the number of rules inside an IMA 
namespace to limit the amount of kernel memory the policy rules are 
consuming. It isn't necessarily  related to cgroups but a hard limit on 
the number of rules to avoid wasted of memory.

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

* Re: [PATCH v10 24/27] ima: Introduce securityfs file to activate an IMA namespace
  2022-02-23 17:12       ` Mimi Zohar
@ 2022-02-23 22:30         ` Stefan Berger
  0 siblings, 0 replies; 86+ messages in thread
From: Stefan Berger @ 2022-02-23 22:30 UTC (permalink / raw)
  To: Mimi Zohar, 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 2/23/22 12:12, Mimi Zohar wrote:
> On Wed, 2022-02-23 at 12:08 -0500, Stefan Berger wrote:
>> On 2/23/22 08:54, Mimi Zohar wrote:
>>> On Tue, 2022-02-01 at 15:37 -0500, Stefan Berger wrote:
>>>
>>>>    int ima_fs_ns_init(struct user_namespace *user_ns, struct dentry *root)
>>>>    {
>>>>    	struct ima_namespace *ns = ima_ns_from_user_ns(user_ns);
>>>> @@ -531,6 +596,12 @@ int ima_fs_ns_init(struct user_namespace *user_ns, struct dentry *root)
>>>>    		}
>>>>    	}
>>>>    
>>>> +	if (ns != &init_ima_ns) {
>>>> +		ret = ima_fs_add_ns_files(ima_dir);
>>>> +		if (ret)
>>>> +			goto out;
>>>> +	}
>>>> +
>>> In all other cases, the securityfs files are directly created in
>>> ima_fs_ns_init().   What is different about "active" that a new
>>> function is defined?
>>
>> It was meant as a function to create namespace-specific files, if more
>> were to come along.  I can move the code from ima_fs_add_ns_files() into
>> this function if you want.
> Perhaps defer defining a new function until that happens.

I moved the code out of this function into ima_fs_ns_init() now.




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

* Re: [PATCH v10 18/27] integrity/ima: Define ns_status for storing namespaced iint data
  2022-02-23 16:12   ` Mimi Zohar
@ 2022-02-24  2:21     ` Stefan Berger
  2022-02-24  2:49       ` Stefan Berger
  0 siblings, 1 reply; 86+ messages in thread
From: Stefan Berger @ 2022-02-24  2:21 UTC (permalink / raw)
  To: Mimi Zohar, 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, Mehmet Kayaalp


On 2/23/22 11:12, Mimi Zohar wrote:
> On Tue, 2022-02-01 at 15:37 -0500, Stefan Berger wrote:
>> 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.
>>
>> To simplify the code in the non-namespaces case embed an ns_status in
>> the integrity_iint_cache and have it linked into the iint's ns_status list
>> when calling ima_get_ns_status().
>>
>> 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 concurrently
>> but no two threads may run and one delete an inode and the other an
>> ima_namespace.
> The locking involved here is really complex.  I'm sure you thought
> about it a lot, but isn't there a better alternative?

I am afraid this is a difficult question and a short and concise answer 
is not possible...

The complexity of the locking is driven by concurrency and the data 
structures that are involved. The data structures (existing global iint 
rbtree, ns_status structure, and per namespace rbtree for ns_status) and 
how they are organized and connected (via linked lists) are a 
consequence of the fact that we need to be able to handle files shared 
between IMA namespaces (and the host) so that re-auditing, re-measuring 
and re-appraisal of files after file modifications or modifications of 
the security.ima xattr (by any namespaces) can be done efficiently. 
Furthermore, it helps to efficiently remove all the status information 
that an IMA namespace has kept for files it audited/measured/appraised. 
The goal was to make this as scalable as possible by having each 
namespace get out of the way of other namespaces by preventing them from 
locking each other out too much. The single biggest problem are files 
shared between IMA namespaces.

The best argument for the design I can come up with is the 'Big O 
notation' describing the time complexity of operations.


The existing global iint rbtree maintains IMA status information for 
each inode. Lookup and insertion of data into the gloab iint rbtree  is 
O(log(n)), thus optimal.

To accommodate re-auditing/re-measurement/re-appraisal, which is driven 
by resetting status flags, I connected a list of ns_status structures, 
in which each namespace maintains its status information for each inode, 
to the iint maintained in that global rbtree. The resetting of status 
flags is fast because traversing the list after a lookup in the tree is 
O(n). Lookup + resetting the flags therefore is O(log(n) + n). If the 
list didn't exist we would have to search all IMA namespaces for the 
inode to be able to reset the flags, resulting in O(n * log(n)) time 
complexity, which is of course much worse. So, the list of ns_status 
linked to an iint has a good reason: better time complexity to traverse 
the list and reset status flags. Beside  that it also supports fast 
handling of deletion of files where the iint has to be delete from the 
global rbtree and the ns_status list it holds must also be deleted (each 
ns_status also needs to be delete from a per IMA-namespace rbtree then)


There's also a per-IMA namespace rbtree for each inode that serves two 
purposes:

a) Fast lookup of ns_status (O(log(n)) for an IMA namespace; at least to 
insert an ns_status into this tree we need not write-lock the iint tree 
but the initial iint creation required the write-locking of the iint tree

b) Maintaining a collection of inodes that the namespace has 
audited/measured/appraised for efficient deletion upon IMA namespace 
teardown: We can traverse this tree in O(n) time and determine which 
iints have no more namespace users and delete them from the iint tree.


Now the dilemma with this is that an ns_status structure is connected to 
a list hanging off the iint and on top of this it is part of an rbtree. 
And this is where the 'group locking' is coming from. What we need to 
prevent is that an ns_status is deleted from its iint list (when a file 
is deleted) while it is also deleted from the per-IMA namespace rbtree 
(when the namespace is deleted). Both must not be done concurrently. 
What is possible is that a group of threads may tear down namespaces and 
the other group may act on file deletion, but threads from both groups 
must not run concurrently.


Now we can at least look at two alternatives for the per-IMA namespace 
rbtree.

1) One alternative is to use a list instead of an rbtree. We would loose 
the fast lookup via the per IMA namespace tree and get O(n) lookup times 
but quick insertion into the list [O(1)]. We still would have the 
collection of inodes. And we would still have the dilemma that an 
ns_status would be connected to two lists, thus requiring the group 
locking. I don't think using a list instead of an rbtree is a solution.

2) We could try to get rid of the per-IMA namespace rbtree altogether 
and just use the global iint rbtree that exists today with a list of 
ns_status connected to its iints. If we do this we would loose the 
knowledge of which inodes a namespace has an ns_status structure for. 
The only way we would find this is by traversing the global iint tree 
(O(n)) and follow each iint list (O(m)) to see whether we find an 
ns_status holding information about the iint. The time complexity for 
this would be O(n*m) but much less than O(n^2). A downside would also be 
that we would have to keep a lock on the global iint rbtree while 
traversing it, thus locking out those that want to add inodes to the 
tree. On the upside it would allow us to get rid of the group locking. 
Lookup of an ns_status in the global iint tree would be O(n) + O(m) and 
insertion would be O(n) + O(1).


Certainly, the alternative is 2) with its own trade-offs. My guess is 
some sort of yielding could probably also be helpful there then to avoid 
blocking higher priority operations than deleting of a namespace.


>> +/*
>> + * 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 *ns_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(ns_status, next,
>> +						     &ns->ns_status_tree,
>> +						     rb_node) {
>> +			write_lock(&ns_status->iint->ns_list_lock);
>> +			if (!list_empty(&ns_status->ns_next)) {
>> +				list_del_init(&ns_status->ns_next);
>> +				llist_add(&ns_status->gc_llist, &garbage);
>> +				ctr++;
>> +			}
> At this point when the namespace is being deleted, no entries are being
> added to the rbtree, so it is safe to remove the nodes here.  There's
> no need to first create a list and then remove them.


We are traversing the per-namespace rbtree here and later on in this 
series (21/27) we try to determine which iints are now unused in the 
global iint tree and delete the iints there. Due to the locking that's 
necessary to remove the iint from the global rbtree all the ns_status 
are collected in this list here already now. True, in this patch here it 
is not necessary but in 21/27 it will be necessary to have them on this 
list. I had thought about merging the patches but its complex enough as-is.


    Stefan


>
>> +			write_unlock(&ns_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;
>> +			}
> Giving priority to removing entries in the iint cache is important, but
> I wish there was a better alternative.
>
>> +		}
>> +
>> +		write_unlock(&ns->ns_tree_lock);
>> +		unlock_group(GRP_NS_STATUS_TREE);
>> +	} while (restart);
>> +
>> +	node = llist_del_all(&garbage);
>> +	llist_for_each_entry_safe(ns_status, next, node, gc_llist)
>> +		ns_status_free(ns, ns_status);
>> +
>> +	kmem_cache_destroy(ns->ns_status_cache);
>> +}

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

* Re: [PATCH v10 18/27] integrity/ima: Define ns_status for storing namespaced iint data
  2022-02-24  2:21     ` Stefan Berger
@ 2022-02-24  2:49       ` Stefan Berger
  0 siblings, 0 replies; 86+ messages in thread
From: Stefan Berger @ 2022-02-24  2:49 UTC (permalink / raw)
  To: Mimi Zohar, 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, Mehmet Kayaalp


On 2/23/22 21:21, Stefan Berger wrote:
>
> On 2/23/22 11:12, Mimi Zohar wrote:
>> On Tue, 2022-02-01 at 15:37 -0500, Stefan Berger wrote:
>>> 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.
>>>
>>> To simplify the code in the non-namespaces case embed an ns_status in
>>> the integrity_iint_cache and have it linked into the iint's 
>>> ns_status list
>>> when calling ima_get_ns_status().
>>>
>>> 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 
>>> concurrently
>>> but no two threads may run and one delete an inode and the other an
>>> ima_namespace.
>> The locking involved here is really complex.  I'm sure you thought
>> about it a lot, but isn't there a better alternative?
>
> I am afraid this is a difficult question and a short and concise 
> answer is not possible...
>
> The complexity of the locking is driven by concurrency and the data 
> structures that are involved. The data structures (existing global 
> iint rbtree, ns_status structure, and per namespace rbtree for 
> ns_status) and how they are organized and connected (via linked lists) 
> are a consequence of the fact that we need to be able to handle files 
> shared between IMA namespaces (and the host) so that re-auditing, 
> re-measuring and re-appraisal of files after file modifications or 
> modifications of the security.ima xattr (by any namespaces) can be 
> done efficiently. Furthermore, it helps to efficiently remove all the 
> status information that an IMA namespace has kept for files it 
> audited/measured/appraised. The goal was to make this as scalable as 
> possible by having each namespace get out of the way of other 
> namespaces by preventing them from locking each other out too much. 
> The single biggest problem are files shared between IMA namespaces.
>
> The best argument for the design I can come up with is the 'Big O 
> notation' describing the time complexity of operations.
>
>
> The existing global iint rbtree maintains IMA status information for 
> each inode. Lookup and insertion of data into the gloab iint rbtree  
> is O(log(n)), thus optimal.
>
> To accommodate re-auditing/re-measurement/re-appraisal, which is 
> driven by resetting status flags, I connected a list of ns_status 
> structures, in which each namespace maintains its status information 
> for each inode, to the iint maintained in that global rbtree. The 
> resetting of status flags is fast because traversing the list after a 
> lookup in the tree is O(n). Lookup + resetting the flags therefore is 
> O(log(n) + n). If the list didn't exist we would have to search all 
> IMA namespaces for the inode to be able to reset the flags, resulting 
> in O(n * log(n)) time complexity, which is of course much worse. So, 
> the list of ns_status linked to an iint has a good reason: better time 
> complexity to traverse the list and reset status flags. Beside  that 
> it also supports fast handling of deletion of files where the iint has 
> to be delete from the global rbtree and the ns_status list it holds 
> must also be deleted (each ns_status also needs to be delete from a 
> per IMA-namespace rbtree then)
>
>
> There's also a per-IMA namespace rbtree for each inode that serves two 
> purposes:
>
> a) Fast lookup of ns_status (O(log(n)) for an IMA namespace; at least 
> to insert an ns_status into this tree we need not write-lock the iint 
> tree but the initial iint creation required the write-locking of the 
> iint tree
>
> b) Maintaining a collection of inodes that the namespace has 
> audited/measured/appraised for efficient deletion upon IMA namespace 
> teardown: We can traverse this tree in O(n) time and determine which 
> iints have no more namespace users and delete them from the iint tree.
>
>
> Now the dilemma with this is that an ns_status structure is connected 
> to a list hanging off the iint and on top of this it is part of an 
> rbtree. And this is where the 'group locking' is coming from. What we 
> need to prevent is that an ns_status is deleted from its iint list 
> (when a file is deleted) while it is also deleted from the per-IMA 
> namespace rbtree (when the namespace is deleted). Both must not be 
> done concurrently. What is possible is that a group of threads may 
> tear down namespaces and the other group may act on file deletion, but 
> threads from both groups must not run concurrently.
>
>
> Now we can at least look at two alternatives for the per-IMA namespace 
> rbtree.
>
> 1) One alternative is to use a list instead of an rbtree. We would 
> loose the fast lookup via the per IMA namespace tree and get O(n) 
> lookup times but quick insertion into the list [O(1)]. We still would 
> have the collection of inodes. And we would still have the dilemma 
> that an ns_status would be connected to two lists, thus requiring the 
> group locking. I don't think using a list instead of an rbtree is a 
> solution.
>
> 2) We could try to get rid of the per-IMA namespace rbtree altogether 
> and just use the global iint rbtree that exists today with a list of 
> ns_status connected to its iints. If we do this we would loose the 
> knowledge of which inodes a namespace has an ns_status structure for. 
> The only way we would find this is by traversing the global iint tree 
> (O(n)) and follow each iint list (O(m)) to see whether we find an 
> ns_status holding information about the iint. The time complexity for 
> this would be O(n*m) but much less than O(n^2). A downside would also 
> be that we would have to keep a lock on the global iint rbtree while 
> traversing it, thus locking out those that want to add inodes to the 
> tree. On the upside it would allow us to get rid of the group locking. 
> Lookup of an ns_status in the global iint tree would be O(n) + O(m) 
> and insertion would be O(n) + O(1).
>
>
> Certainly, the alternative is 2) with its own trade-offs. My guess is 
> some sort of yielding could probably also be helpful there then to 
> avoid blocking higher priority operations than deleting of a namespace.


I forgot to mention: It makes a difference if one has to walk the global 
iint tree to find the few ns_status for the namespace among possibly 
thousands of entries in that tree than having a per-IMA namespace rbtree 
that has these few ns_status right there. So walking the iint tree is 
more like O(N) versus O(n) walking the per-IMA namespace rbtree.



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

end of thread, other threads:[~2022-02-24  2:49 UTC | newest]

Thread overview: 86+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2022-02-01 20:37 [PATCH v10 00/27] ima: Namespace IMA with audit support in IMA-ns Stefan Berger
2022-02-01 20:37 ` [PATCH v10 01/27] ima: Remove ima_policy file before directory Stefan Berger
2022-02-10 12:02   ` Mimi Zohar
2022-02-01 20:37 ` [PATCH v10 02/27] ima: Do not print policy rule with inactive LSM labels Stefan Berger
2022-02-02 14:17   ` Christian Brauner
2022-02-01 20:37 ` [PATCH v10 03/27] ima: Return error code obtained from securityfs functions Stefan Berger
2022-02-10 12:02   ` Mimi Zohar
2022-02-15 18:09     ` Mimi Zohar
2022-02-01 20:37 ` [PATCH v10 04/27] securityfs: rework dentry creation Stefan Berger
2022-02-10 12:03   ` Mimi Zohar
2022-02-01 20:37 ` [PATCH v10 05/27] ima: Define ima_namespace struct and start moving variables into it Stefan Berger
2022-02-16 14:41   ` Mimi Zohar
2022-02-16 20:25     ` Stefan Berger
2022-02-01 20:37 ` [PATCH v10 06/27] ima: Move arch_policy_entry into ima_namespace Stefan Berger
2022-02-16 16:39   ` Mimi Zohar
2022-02-16 20:48     ` Stefan Berger
2022-02-16 20:56       ` Mimi Zohar
2022-02-16 21:19         ` Stefan Berger
2022-02-01 20:37 ` [PATCH v10 07/27] ima: Move ima_htable " Stefan Berger
2022-02-16 14:41   ` Mimi Zohar
2022-02-01 20:37 ` [PATCH v10 08/27] ima: Move measurement list related variables " Stefan Berger
2022-02-17 14:46   ` Mimi Zohar
2022-02-01 20:37 ` [PATCH v10 09/27] ima: Move some IMA policy and filesystem " Stefan Berger
2022-02-17 14:44   ` Mimi Zohar
2022-02-01 20:37 ` [PATCH v10 10/27] ima: Move IMA securityfs files into ima_namespace or onto stack Stefan Berger
2022-02-17 14:44   ` Mimi Zohar
2022-02-01 20:37 ` [PATCH v10 11/27] ima: Move ima_lsm_policy_notifier into ima_namespace Stefan Berger
2022-02-17 20:30   ` Mimi Zohar
2022-02-17 20:59     ` Stefan Berger
2022-02-17 21:24       ` Mimi Zohar
2022-02-01 20:37 ` [PATCH v10 12/27] ima: Define mac_admin_ns_capable() as a wrapper for ns_capable() Stefan Berger
2022-02-05  5:58   ` Serge E. Hallyn
2022-02-06 17:20     ` Stefan Berger
2022-02-07 18:43       ` Stefan Berger
2022-02-23 17:51         ` Serge E. Hallyn
2022-02-01 20:37 ` [PATCH v10 13/27] ima: Only accept AUDIT rules for non-init_ima_ns namespaces for now Stefan Berger
2022-02-17 21:32   ` Mimi Zohar
2022-02-01 20:37 ` [PATCH v10 14/27] userns: Add pointer to ima_namespace to user_namespace Stefan Berger
2022-02-18 16:26   ` Mimi Zohar
2022-02-01 20:37 ` [PATCH v10 15/27] ima: Implement hierarchical processing of file accesses Stefan Berger
2022-02-02 13:19   ` [PATCH] ima: fix semicolon.cocci warnings kernel test robot
2022-02-02 13:19     ` kernel test robot
2022-02-02 13:19   ` [PATCH v10 15/27] ima: Implement hierarchical processing of file accesses kernel test robot
2022-02-02 13:19     ` kernel test robot
2022-02-18 16:27   ` Mimi Zohar
2022-02-01 20:37 ` [PATCH v10 16/27] ima: Implement ima_free_policy_rules() for freeing of an ima_namespace Stefan Berger
2022-02-18 17:09   ` Mimi Zohar
2022-02-18 19:38     ` Stefan Berger
2022-02-18 20:04       ` Mimi Zohar
2022-02-01 20:37 ` [PATCH v10 17/27] ima: Add functions for creating and " Stefan Berger
2022-02-18 19:49   ` Mimi Zohar
2022-02-01 20:37 ` [PATCH v10 18/27] integrity/ima: Define ns_status for storing namespaced iint data Stefan Berger
2022-02-23 16:12   ` Mimi Zohar
2022-02-24  2:21     ` Stefan Berger
2022-02-24  2:49       ` Stefan Berger
2022-02-01 20:37 ` [PATCH v10 19/27] integrity: Add optional callback function to integrity_inode_free() Stefan Berger
2022-02-01 20:37 ` [PATCH v10 20/27] ima: Namespace audit status flags Stefan Berger
2022-02-01 20:37 ` [PATCH v10 21/27] ima: Remove unused iints from the integrity_iint_cache Stefan Berger
2022-02-01 20:37 ` [PATCH v10 22/27] securityfs: Extend securityfs with namespacing support Stefan Berger
2022-02-23  1:48   ` Mimi Zohar
2022-02-23  8:14     ` Christian Brauner
2022-02-23 15:44       ` Stefan Berger
2022-02-01 20:37 ` [PATCH v10 23/27] ima: Setup securityfs for IMA namespace Stefan Berger
2022-02-23 11:45   ` Mimi Zohar
2022-02-01 20:37 ` [PATCH v10 24/27] ima: Introduce securityfs file to activate an " Stefan Berger
2022-02-23 13:54   ` Mimi Zohar
2022-02-23 17:08     ` Stefan Berger
2022-02-23 17:12       ` Mimi Zohar
2022-02-23 22:30         ` Stefan Berger
2022-02-01 20:37 ` [PATCH v10 25/27] ima: Show owning user namespace's uid and gid when displaying policy Stefan Berger
2022-02-23 14:06   ` Mimi Zohar
2022-02-01 20:37 ` [PATCH v10 26/27] ima: Limit number of policy rules in non-init_ima_ns Stefan Berger
2022-02-23 15:38   ` Mimi Zohar
2022-02-23 16:37     ` Stefan Berger
2022-02-23 17:04       ` Mimi Zohar
2022-02-23 20:45         ` Stefan Berger
2022-02-23 20:59           ` Mimi Zohar
2022-02-23 21:06             ` Stefan Berger
2022-02-01 20:37 ` [PATCH v10 27/27] ima: Enable IMA namespaces Stefan Berger
2022-02-23 17:58   ` Serge E. Hallyn
2022-02-23 20:53     ` Stefan Berger
2022-02-02 14:13 ` [PATCH v10 00/27] ima: Namespace IMA with audit support in IMA-ns Christian Brauner
2022-02-02 14:40   ` Stefan Berger
2022-02-02 16:04     ` Mimi Zohar
2022-02-02 18:18       ` Stefan Berger
2022-02-02 21:27         ` Stefan Berger

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.