linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH v5 00/16] ima: Namespace IMA with audit support in IMA-ns
@ 2021-12-08 22:18 Stefan Berger
  2021-12-08 22:18 ` [PATCH v5 01/16] ima: Add IMA namespace support Stefan Berger
                   ` (15 more replies)
  0 siblings, 16 replies; 63+ messages in thread
From: Stefan Berger @ 2021-12-08 22:18 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 gets created when a user namespace is
created. The advantage of this is that the user namespace can provide
the keys infrastructure that IMA appraisal support will need later on.

We chose the goal of supporting auditing within an IMA namespace since it
requires the least changes to IMA. Following this series, auditing within
an IMA namespace can be activated by a user running the following lines
that rely on a statically linked busybox to be installed on the host for
execution within the minimal container environment:

mkdir -p rootfs/{bin,mnt,proc}
cp /sbin/busybox rootfs/bin
PATH=/bin unshare --user --map-root-user --mount-proc --pid --fork \
  --root rootfs busybox sh -c \
 "busybox mount -t securityfs /mnt /mnt; \
  busybox echo 'audit func=BPRM_CHECK mask=MAY_EXEC' > /mnt/ima/policy; \
  busybox cat /mnt/ima/policy"

Following the audit log on the host the last line cat'ing the IMA policy
inside the namespace would have been audited. Unfortunately the auditing
line is not distinguishable from one stemming from actions on the host.
The hope here is that Richard Brigg's container id support for auditing
would help resolve the problem.

The following lines added to a suitable IMA policy on the host would
cause the execution of the commands inside the container (by uid 1000)
to be measured and audited as well on the host, thus leading to two
auditing messages for the 'busybox cat' above and log entries in IMA's
system log.

echo -e "measure func=BPRM_CHECK mask=MAY_EXEC uid=1000\n" \
        "audit func=BPRM_CHECK mask=MAY_EXEC uid=1000\n" \
    > /sys/kernel/security/ima/policy

The goal of supporting measurement and auditing by the host, of actions
occurring within IMA namespaces, is that users, particularly root,
should not be able to evade the host's IMA policy just by spawning
new IMA namespaces, running programs there, and discarding the namespaces
again. This is achieved through 'hierarchical processing' of file
accesses that are evaluated against the policy of the namespace where
the action occurred and against all namespaces' and their policies leading
back to the root IMA namespace (init_ima_ns).

The patch series adds support for a virtualized SecurityFS with a few
new API calls that are used by IMA namespacing. Only the data relevant
to the IMA namespace are shown. The files and directories of other
security subsystems (TPM, evm, Tomoyo, safesetid) are not showing
up when secruityfs is mounted inside a user namespace.

Much of the code leading up to the virtualization of SecurityFS deals
with moving IMA's variables from various files into the IMA namespace
structure called 'ima_namespace'. When it comes to determining the
current IMA namespace I took the approach to get the current IMA
namespace (get_current_ns()) on the top level and pass the pointer all
the way down to those functions that now need access to the ima_namespace
to get to their variables. This later on comes in handy once hierarchical
processing is implemented in this series where we walk the list of
namespaces backwards and again need to pass the pointer into functions.

This patch also introduces usage of CAP_MAC_ADMIN to allow access to the
IMA policy via reduced capabilities. We would again later on use this
capability to allow users to set file extended attributes for IMA appraisal
support.

My tree with these patches is here:

git clone https://github.com/stefanberger/linux-ima-namespaces v5.15+imans.v5.posted 

Regards,
   Stefan

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

v4:
 - For consistency moved 'ns = get_current_ns()' to top of functions
 - Merge in James's latest SecurityFS patch

v3:
 - Further modifications to virtualized SecurityFS following James's posted patch
 - Dropping of early teardown for user_namespaces since not needed anymore

v2:
 - Folllwed Christian's suggestion to virtualize securitytfs; no more securityfs_ns
 - Followed James's advice for late 'population' of securityfs for IMA namespaces
 - Squashed 2 patches dealing with capabilities
 - Added missing 'depends on USER_NS' to Kconfig
 - Added missing 'static' to several functions


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

Stefan Berger (14):
  ima: Add IMA namespace support
  ima: Move delayed work queue and variables into ima_namespace
  ima: Move IMA's keys queue related variables into ima_namespace
  ima: Move policy related variables into ima_namespace
  ima: Move ima_htable into ima_namespace
  ima: Move measurement list related variables into ima_namespace
  ima: Only accept AUDIT rules for IMA non-init_ima_ns namespaces for
    now
  ima: Implement hierarchical processing of file accesses
  securityfs: Only use simple_pin_fs/simple_release_fs for init_user_ns
  securityfs: Extend securityfs with namespacing support
  ima: Move some IMA policy and filesystem related variables into
    ima_namespace
  ima: Use mac_admin_ns_capable() to check corresponding capability
  ima: Move dentries into ima_namespace
  ima: Setup securityfs for IMA namespace

 include/linux/capability.h                   |   6 +
 include/linux/ima.h                          | 138 +++++++++++++++++
 include/linux/user_namespace.h               |   4 +
 init/Kconfig                                 |  13 ++
 kernel/user.c                                |   7 +
 kernel/user_namespace.c                      |   8 +
 security/inode.c                             |  55 +++++--
 security/integrity/ima/Makefile              |   4 +-
 security/integrity/ima/ima.h                 | 145 +++++++++++------
 security/integrity/ima/ima_api.c             |  33 ++--
 security/integrity/ima/ima_appraise.c        |  26 ++--
 security/integrity/ima/ima_asymmetric_keys.c |   8 +-
 security/integrity/ima/ima_fs.c              | 154 +++++++++++--------
 security/integrity/ima/ima_init.c            |  20 +--
 security/integrity/ima/ima_init_ima_ns.c     |  73 +++++++++
 security/integrity/ima/ima_main.c            | 144 +++++++++++------
 security/integrity/ima/ima_ns.c              | 102 ++++++++++++
 security/integrity/ima/ima_ns_status.c       | 132 ++++++++++++++++
 security/integrity/ima/ima_policy.c          | 146 ++++++++++--------
 security/integrity/ima/ima_queue.c           |  75 +++++----
 security/integrity/ima/ima_queue_keys.c      |  81 +++++-----
 security/integrity/ima/ima_template.c        |   4 +-
 22 files changed, 1032 insertions(+), 346 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] 63+ messages in thread

* [PATCH v5 01/16] ima: Add IMA namespace support
  2021-12-08 22:18 [PATCH v5 00/16] ima: Namespace IMA with audit support in IMA-ns Stefan Berger
@ 2021-12-08 22:18 ` Stefan Berger
  2021-12-08 22:18 ` [PATCH v5 02/16] ima: Define ns_status for storing namespaced iint data Stefan Berger
                   ` (14 subsequent siblings)
  15 siblings, 0 replies; 63+ messages in thread
From: Stefan Berger @ 2021-12-08 22:18 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

Implement an IMA namespace data structure that gets created alongside a
user namespace with CLONE_NEWUSER. This lays down the foundation for
namespacing the different aspects of IMA (eg. IMA-audit, IMA-measurement,
IMA-appraisal).

Signed-off-by: Stefan Berger <stefanb@linux.ibm.com>
Suggested-by: James Bottomley <James.Bottomley@HansenPartnership.com>
---
 include/linux/ima.h                      | 62 ++++++++++++++++++++
 include/linux/user_namespace.h           |  4 ++
 init/Kconfig                             | 10 ++++
 kernel/user.c                            |  7 +++
 kernel/user_namespace.c                  |  8 +++
 security/integrity/ima/Makefile          |  3 +-
 security/integrity/ima/ima.h             |  4 ++
 security/integrity/ima/ima_init.c        |  4 ++
 security/integrity/ima/ima_init_ima_ns.c | 32 +++++++++++
 security/integrity/ima/ima_ns.c          | 73 ++++++++++++++++++++++++
 10 files changed, 206 insertions(+), 1 deletion(-)
 create mode 100644 security/integrity/ima/ima_init_ima_ns.c
 create mode 100644 security/integrity/ima/ima_ns.c

diff --git a/include/linux/ima.h b/include/linux/ima.h
index b6ab66a546ae..d81df821c85b 100644
--- a/include/linux/ima.h
+++ b/include/linux/ima.h
@@ -11,6 +11,7 @@
 #include <linux/fs.h>
 #include <linux/security.h>
 #include <linux/kexec.h>
+#include <linux/user_namespace.h>
 #include <crypto/hash_info.h>
 struct linux_binprm;
 
@@ -210,6 +211,67 @@ static inline int ima_inode_removexattr(struct dentry *dentry,
 }
 #endif /* CONFIG_IMA_APPRAISE */
 
+struct ima_namespace {
+	struct kref kref;
+	struct user_namespace *user_ns;
+};
+
+extern struct ima_namespace init_ima_ns;
+
+#ifdef CONFIG_IMA_NS
+
+void free_ima_ns(struct kref *kref);
+
+static inline struct ima_namespace *get_ima_ns(struct ima_namespace *ns)
+{
+	if (ns)
+		kref_get(&ns->kref);
+
+	return ns;
+}
+
+static inline void put_ima_ns(struct user_namespace *user_ns)
+{
+	struct ima_namespace *ns = user_ns->ima_ns;
+
+	if (ns) {
+		pr_debug("DEREF   ima_ns: 0x%p  ctr: %d\n", ns, kref_read(&ns->kref));
+		kref_put(&ns->kref, free_ima_ns);
+	}
+}
+
+int create_ima_ns(struct user_namespace *user_ns);
+
+static inline struct ima_namespace *get_current_ns(void)
+{
+	return current_user_ns()->ima_ns;
+}
+
+#else
+
+static inline struct ima_namespace *get_ima_ns(struct ima_namespace *ns)
+{
+	return ns;
+}
+
+static inline void put_ima_ns(struct user_namespace *user_ns)
+{
+}
+
+static inline int create_ima_ns(struct user_namespace *user_ns)
+{
+#if CONFIG_IMA
+	user_ns->ima_ns = get_ima_ns(&init_ima_ns);
+#endif
+	return 0;
+}
+
+static inline struct ima_namespace *get_current_ns(void)
+{
+	return &init_ima_ns;
+}
+#endif /* CONFIG_IMA_NS */
+
 #if defined(CONFIG_IMA_APPRAISE) && defined(CONFIG_INTEGRITY_TRUSTED_KEYRING)
 extern bool ima_appraise_signature(enum kernel_read_file_id func);
 #else
diff --git a/include/linux/user_namespace.h b/include/linux/user_namespace.h
index 33a4240e6a6f..5249db04d62b 100644
--- a/include/linux/user_namespace.h
+++ b/include/linux/user_namespace.h
@@ -36,6 +36,7 @@ struct uid_gid_map { /* 64 bytes -- 1 cache line */
 #define USERNS_INIT_FLAGS USERNS_SETGROUPS_ALLOWED
 
 struct ucounts;
+struct ima_namespace;
 
 enum ucount_type {
 	UCOUNT_USER_NAMESPACES,
@@ -99,6 +100,9 @@ struct user_namespace {
 #endif
 	struct ucounts		*ucounts;
 	long ucount_max[UCOUNT_COUNTS];
+#ifdef CONFIG_IMA
+	struct ima_namespace	*ima_ns;
+#endif
 } __randomize_layout;
 
 struct ucounts {
diff --git a/init/Kconfig b/init/Kconfig
index 11f8a845f259..27890607e8cb 100644
--- a/init/Kconfig
+++ b/init/Kconfig
@@ -1242,6 +1242,16 @@ config NET_NS
 	  Allow user space to create what appear to be multiple instances
 	  of the network stack.
 
+config IMA_NS
+	bool "IMA namespace"
+	depends on USER_NS
+	depends on IMA
+	default y
+	help
+	  Allow the creation of IMA namespaces for each user namespace.
+	  Namespaced IMA enables having IMA features work separately
+	  in each IMA namespace.
+
 endif # NAMESPACES
 
 config CHECKPOINT_RESTORE
diff --git a/kernel/user.c b/kernel/user.c
index e2cf8c22b539..287751d89b44 100644
--- a/kernel/user.c
+++ b/kernel/user.c
@@ -20,6 +20,10 @@
 #include <linux/user_namespace.h>
 #include <linux/proc_ns.h>
 
+#ifdef CONFIG_IMA
+extern struct ima_namespace init_ima_ns;
+#endif
+
 /*
  * userns count is 1 for root user, 1 for init_uts_ns,
  * and 1 for... ?
@@ -67,6 +71,9 @@ struct user_namespace init_user_ns = {
 	.keyring_name_list = LIST_HEAD_INIT(init_user_ns.keyring_name_list),
 	.keyring_sem = __RWSEM_INITIALIZER(init_user_ns.keyring_sem),
 #endif
+#ifdef CONFIG_IMA
+	.ima_ns = &init_ima_ns,
+#endif
 };
 EXPORT_SYMBOL_GPL(init_user_ns);
 
diff --git a/kernel/user_namespace.c b/kernel/user_namespace.c
index 6b2e3ca7ee99..0985e1c86e59 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);
@@ -141,8 +142,14 @@ int create_user_ns(struct cred *new)
 	if (!setup_userns_sysctls(ns))
 		goto fail_keyring;
 
+	ret = create_ima_ns(ns);
+	if (ret)
+		goto fail_sysctls;
+
 	set_cred_user_ns(new, ns);
 	return 0;
+fail_sysctls:
+	retire_userns_sysctls(ns);
 fail_keyring:
 #ifdef CONFIG_PERSISTENT_KEYRINGS
 	key_put(ns->persistent_keyring_register);
@@ -196,6 +203,7 @@ static void free_user_ns(struct work_struct *work)
 			kfree(ns->projid_map.forward);
 			kfree(ns->projid_map.reverse);
 		}
+		put_ima_ns(ns);
 		retire_userns_sysctls(ns);
 		key_free_user_ns(ns);
 		ns_free_inum(&ns->ns);
diff --git a/security/integrity/ima/Makefile b/security/integrity/ima/Makefile
index 2499f2485c04..b86a35fbed60 100644
--- a/security/integrity/ima/Makefile
+++ b/security/integrity/ima/Makefile
@@ -7,13 +7,14 @@
 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
 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 be965a8715e4..2f8adf383054 100644
--- a/security/integrity/ima/ima.h
+++ b/security/integrity/ima/ima.h
@@ -418,6 +418,10 @@ static inline void ima_free_modsig(struct modsig *modsig)
 }
 #endif /* CONFIG_IMA_APPRAISE_MODSIG */
 
+int ima_ns_init(void);
+struct ima_namespace;
+int ima_init_namespace(struct ima_namespace *ns);
+
 /* LSM based policy rules require audit */
 #ifdef CONFIG_IMA_LSM_RULES
 
diff --git a/security/integrity/ima/ima_init.c b/security/integrity/ima/ima_init.c
index b26fa67476b4..f6ae4557a0da 100644
--- a/security/integrity/ima/ima_init.c
+++ b/security/integrity/ima/ima_init.c
@@ -120,6 +120,10 @@ int __init ima_init(void)
 {
 	int rc;
 
+	rc = ima_ns_init();
+	if (rc)
+		return rc;
+
 	ima_tpm_chip = tpm_default_chip();
 	if (!ima_tpm_chip)
 		pr_info("No TPM chip found, activating TPM-bypass!\n");
diff --git a/security/integrity/ima/ima_init_ima_ns.c b/security/integrity/ima/ima_init_ima_ns.c
new file mode 100644
index 000000000000..c225b89818ac
--- /dev/null
+++ b/security/integrity/ima/ima_init_ima_ns.c
@@ -0,0 +1,32 @@
+// 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>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, version 2 of the License.
+ */
+
+#include <linux/export.h>
+#include <linux/user_namespace.h>
+#include <linux/ima.h>
+#include <linux/proc_ns.h>
+
+int ima_init_namespace(struct ima_namespace *ns)
+{
+	return 0;
+}
+
+int __init ima_ns_init(void)
+{
+	return ima_init_namespace(&init_ima_ns);
+}
+
+struct ima_namespace init_ima_ns = {
+	.kref = KREF_INIT(1),
+	.user_ns = &init_user_ns,
+};
+EXPORT_SYMBOL(init_ima_ns);
diff --git a/security/integrity/ima/ima_ns.c b/security/integrity/ima/ima_ns.c
new file mode 100644
index 000000000000..91557cae5b80
--- /dev/null
+++ b/security/integrity/ima/ima_ns.c
@@ -0,0 +1,73 @@
+// 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>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, version 2 of the License.
+ */
+
+#include <linux/kref.h>
+#include <linux/slab.h>
+#include <linux/ima.h>
+#include <linux/mount.h>
+#include <linux/proc_ns.h>
+#include <linux/lsm_hooks.h>
+
+#include "ima.h"
+
+static struct kmem_cache *imans_cachep;
+
+int create_ima_ns(struct user_namespace *user_ns)
+{
+	struct ima_namespace *ns;
+	int err;
+
+	ns = kmem_cache_zalloc(imans_cachep, GFP_KERNEL);
+	if (!ns)
+		return -ENOMEM;
+	pr_debug("NEW     ima_ns: 0x%p\n", ns);
+
+	kref_init(&ns->kref);
+	ns->user_ns = user_ns;
+
+	err = ima_init_namespace(ns);
+	if (err)
+		goto fail_free;
+
+	user_ns->ima_ns = ns;
+
+	return 0;
+
+fail_free:
+	kmem_cache_free(imans_cachep, ns);
+
+	return err;
+}
+
+static void destroy_ima_ns(struct ima_namespace *ns)
+{
+	pr_debug("DESTROY ima_ns: 0x%p\n", ns);
+	kmem_cache_free(imans_cachep, ns);
+}
+
+void free_ima_ns(struct kref *kref)
+{
+	struct ima_namespace *ns;
+
+	ns = container_of(kref, struct ima_namespace, kref);
+	if (WARN_ON(ns == &init_ima_ns))
+		return;
+
+	destroy_ima_ns(ns);
+}
+
+static int __init imans_cache_init(void)
+{
+	imans_cachep = KMEM_CACHE(ima_namespace, SLAB_PANIC);
+	return 0;
+}
+subsys_initcall(imans_cache_init)
-- 
2.31.1


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

* [PATCH v5 02/16] ima: Define ns_status for storing namespaced iint data
  2021-12-08 22:18 [PATCH v5 00/16] ima: Namespace IMA with audit support in IMA-ns Stefan Berger
  2021-12-08 22:18 ` [PATCH v5 01/16] ima: Add IMA namespace support Stefan Berger
@ 2021-12-08 22:18 ` Stefan Berger
  2021-12-08 22:18 ` [PATCH v5 03/16] ima: Namespace audit status flags Stefan Berger
                   ` (13 subsequent siblings)
  15 siblings, 0 replies; 63+ messages in thread
From: Stefan Berger @ 2021-12-08 22:18 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>

This patch adds an rbtree to the IMA namespace structure that stores a
namespaced version of iint->flags in ns_status struct. Similar to the
integrity_iint_cache, both the iint ns_struct are looked up using the
inode pointer value. The lookup, allocate, and insertion code is also
similar, except ns_struct is not free'd when the inode is free'd.
Instead, the lookup verifies the i_ino and i_generation fields are also a
match.

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

Changelog:
v2:
 * fixed tree traversal in __ima_ns_status_find()
---
 include/linux/ima.h                      |   3 +
 security/integrity/ima/Makefile          |   1 +
 security/integrity/ima/ima.h             |  24 +++++
 security/integrity/ima/ima_init_ima_ns.c |   9 ++
 security/integrity/ima/ima_ns.c          |   1 +
 security/integrity/ima/ima_ns_status.c   | 132 +++++++++++++++++++++++
 6 files changed, 170 insertions(+)
 create mode 100644 security/integrity/ima/ima_ns_status.c

diff --git a/include/linux/ima.h b/include/linux/ima.h
index d81df821c85b..9f6de36240b0 100644
--- a/include/linux/ima.h
+++ b/include/linux/ima.h
@@ -214,6 +214,9 @@ static inline int ima_inode_removexattr(struct dentry *dentry,
 struct ima_namespace {
 	struct kref kref;
 	struct user_namespace *user_ns;
+	struct rb_root ns_status_tree;
+	rwlock_t ns_status_lock;
+	struct kmem_cache *ns_status_cache;
 };
 
 extern struct ima_namespace init_ima_ns;
diff --git a/security/integrity/ima/Makefile b/security/integrity/ima/Makefile
index b86a35fbed60..78c84214e109 100644
--- a/security/integrity/ima/Makefile
+++ b/security/integrity/ima/Makefile
@@ -10,6 +10,7 @@ ima-y := ima_fs.o ima_queue.o ima_init.o ima_main.o ima_crypto.o ima_api.o \
 	 ima_policy.o ima_template.o ima_template_lib.o ima_init_ima_ns.o
 ima-$(CONFIG_IMA_APPRAISE) += ima_appraise.o
 ima-$(CONFIG_IMA_APPRAISE_MODSIG) += ima_modsig.o
+ima-$(CONFIG_IMA_NS) += ima_ns.o ima_ns_status.o
 ima-$(CONFIG_HAVE_IMA_KEXEC) += ima_kexec.o
 ima-$(CONFIG_IMA_BLACKLIST_KEYRING) += ima_mok.o
 ima-$(CONFIG_IMA_MEASURE_ASYMMETRIC_KEYS) += ima_asymmetric_keys.o
diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h
index 2f8adf383054..28896d256e36 100644
--- a/security/integrity/ima/ima.h
+++ b/security/integrity/ima/ima.h
@@ -133,6 +133,14 @@ static inline void ima_load_kexec_buffer(void) {}
  */
 extern bool ima_canonical_fmt;
 
+struct ns_status {
+	struct rb_node rb_node;
+	struct inode *inode;
+	ino_t i_ino;
+	u32 i_generation;
+	unsigned long flags;
+};
+
 /* Internal IMA function definitions */
 int ima_init(void);
 int ima_fs_init(void);
@@ -422,6 +430,22 @@ int ima_ns_init(void);
 struct ima_namespace;
 int ima_init_namespace(struct ima_namespace *ns);
 
+#ifdef CONFIG_IMA_NS
+struct ns_status *ima_get_ns_status(struct ima_namespace *ns,
+				    struct inode *inode);
+
+void free_ns_status_cache(struct ima_namespace *ns);
+
+#else
+
+static inline struct ns_status *ima_get_ns_status(struct ima_namespace *ns,
+						  struct inode *inode)
+{
+	return NULL;
+}
+
+#endif /* CONFIG_IMA_NS */
+
 /* LSM based policy rules require audit */
 #ifdef CONFIG_IMA_LSM_RULES
 
diff --git a/security/integrity/ima/ima_init_ima_ns.c b/security/integrity/ima/ima_init_ima_ns.c
index c225b89818ac..64777377664b 100644
--- a/security/integrity/ima/ima_init_ima_ns.c
+++ b/security/integrity/ima/ima_init_ima_ns.c
@@ -14,9 +14,18 @@
 #include <linux/user_namespace.h>
 #include <linux/ima.h>
 #include <linux/proc_ns.h>
+#include <linux/slab.h>
+
+#include "ima.h"
 
 int ima_init_namespace(struct ima_namespace *ns)
 {
+	ns->ns_status_tree = RB_ROOT;
+	rwlock_init(&ns->ns_status_lock);
+	ns->ns_status_cache = KMEM_CACHE(ns_status, SLAB_PANIC);
+	if (!ns->ns_status_cache)
+		return -ENOMEM;
+
 	return 0;
 }
 
diff --git a/security/integrity/ima/ima_ns.c b/security/integrity/ima/ima_ns.c
index 91557cae5b80..e21e9e0d2316 100644
--- a/security/integrity/ima/ima_ns.c
+++ b/security/integrity/ima/ima_ns.c
@@ -51,6 +51,7 @@ int create_ima_ns(struct user_namespace *user_ns)
 static void destroy_ima_ns(struct ima_namespace *ns)
 {
 	pr_debug("DESTROY ima_ns: 0x%p\n", ns);
+	free_ns_status_cache(ns);
 	kmem_cache_free(imans_cachep, ns);
 }
 
diff --git a/security/integrity/ima/ima_ns_status.c b/security/integrity/ima/ima_ns_status.c
new file mode 100644
index 000000000000..807dfaecdb5e
--- /dev/null
+++ b/security/integrity/ima/ima_ns_status.c
@@ -0,0 +1,132 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2016-2021 IBM Corporation
+ * Author:
+ *  Yuqiong Sun <suny@us.ibm.com>
+ *  Stefan Berger <stefanb@linux.vnet.ibm.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, version 2 of the License.
+ */
+
+#include <linux/user_namespace.h>
+#include <linux/proc_ns.h>
+#include <linux/ima.h>
+
+#include "ima.h"
+
+void free_ns_status_cache(struct ima_namespace *ns)
+{
+	struct ns_status *status, *next;
+
+	write_lock(&ns->ns_status_lock);
+	rbtree_postorder_for_each_entry_safe(status, next,
+					     &ns->ns_status_tree, rb_node)
+		kmem_cache_free(ns->ns_status_cache, status);
+	ns->ns_status_tree = RB_ROOT;
+	write_unlock(&ns->ns_status_lock);
+	kmem_cache_destroy(ns->ns_status_cache);
+}
+
+/*
+ * __ima_ns_status_find - return the ns_status associated with an inode
+ */
+static struct ns_status *__ima_ns_status_find(struct ima_namespace *ns,
+					      struct inode *inode)
+{
+	struct ns_status *status;
+	struct rb_node *n = ns->ns_status_tree.rb_node;
+
+	while (n) {
+		status = rb_entry(n, struct ns_status, rb_node);
+
+		if (inode < status->inode)
+			n = n->rb_left;
+		else if (inode > status->inode)
+			n = n->rb_right;
+		else
+			break;
+	}
+	if (!n)
+		return NULL;
+
+	return status;
+}
+
+/*
+ * ima_ns_status_find - return the ns_status associated with an inode
+ */
+static struct ns_status *ima_ns_status_find(struct ima_namespace *ns,
+					    struct inode *inode)
+{
+	struct ns_status *status;
+
+	read_lock(&ns->ns_status_lock);
+	status = __ima_ns_status_find(ns, inode);
+	read_unlock(&ns->ns_status_lock);
+
+	return status;
+}
+
+static void insert_ns_status(struct ima_namespace *ns, struct inode *inode,
+			     struct ns_status *status)
+{
+	struct rb_node **p;
+	struct rb_node *node, *parent = NULL;
+	struct ns_status *test_status;
+
+	p = &ns->ns_status_tree.rb_node;
+	while (*p) {
+		parent = *p;
+		test_status = rb_entry(parent, struct ns_status, rb_node);
+		if (inode < test_status->inode)
+			p = &(*p)->rb_left;
+		else
+			p = &(*p)->rb_right;
+	}
+	node = &status->rb_node;
+	rb_link_node(node, parent, p);
+	rb_insert_color(node, &ns->ns_status_tree);
+}
+
+struct ns_status *ima_get_ns_status(struct ima_namespace *ns,
+				    struct inode *inode)
+{
+	struct ns_status *status;
+	int skip_insert = 0;
+
+	status = ima_ns_status_find(ns, inode);
+	if (status) {
+		/*
+		 * Unlike integrity_iint_cache we are not free'ing the
+		 * ns_status data when the inode is free'd. So, in addition to
+		 * checking the inode pointer, we need to make sure the
+		 * (i_generation, i_ino) pair matches as well.
+		 */
+		if (inode->i_ino == status->i_ino &&
+		    inode->i_generation == status->i_generation)
+			return status;
+
+		/* Same inode number is reused, overwrite the ns_status */
+		skip_insert = 1;
+	} else {
+		status = kmem_cache_alloc(ns->ns_status_cache, GFP_NOFS);
+		if (!status)
+			return ERR_PTR(-ENOMEM);
+	}
+
+	write_lock(&ns->ns_status_lock);
+
+	if (!skip_insert)
+		insert_ns_status(ns, inode, status);
+
+	status->inode = inode;
+	status->i_ino = inode->i_ino;
+	status->i_generation = inode->i_generation;
+	status->flags = 0UL;
+
+	write_unlock(&ns->ns_status_lock);
+
+	return status;
+}
-- 
2.31.1


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

* [PATCH v5 03/16] ima: Namespace audit status flags
  2021-12-08 22:18 [PATCH v5 00/16] ima: Namespace IMA with audit support in IMA-ns Stefan Berger
  2021-12-08 22:18 ` [PATCH v5 01/16] ima: Add IMA namespace support Stefan Berger
  2021-12-08 22:18 ` [PATCH v5 02/16] ima: Define ns_status for storing namespaced iint data Stefan Berger
@ 2021-12-08 22:18 ` Stefan Berger
  2021-12-08 22:18 ` [PATCH v5 04/16] ima: Move delayed work queue and variables into ima_namespace Stefan Berger
                   ` (12 subsequent siblings)
  15 siblings, 0 replies; 63+ messages in thread
From: Stefan Berger @ 2021-12-08 22:18 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

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

The iint cache stores whether the file is measured, appraised, audited
etc. This patch moves the IMA_AUDITED flag into the per-namespace
ns_status, enabling IMA audit mechanism to audit the same file each time
it is accessed in a new namespace.

The ns_status is not looked up if the CONFIG_IMA_NS is disabled or if
any of the IMA_NS_STATUS_ACTIONS (currently only IMA_AUDIT) is not
enabled.

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

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

Changelog:
v2:
 * fixed flag calculation in iint_flags()
---
 init/Kconfig                      |  3 +++
 security/integrity/ima/ima.h      | 23 ++++++++++++++++++++++-
 security/integrity/ima/ima_api.c  |  8 +++++---
 security/integrity/ima/ima_main.c | 25 ++++++++++++++++++-------
 security/integrity/ima/ima_ns.c   | 20 ++++++++++++++++++++
 5 files changed, 68 insertions(+), 11 deletions(-)

diff --git a/init/Kconfig b/init/Kconfig
index 27890607e8cb..1e1c49f1d129 100644
--- a/init/Kconfig
+++ b/init/Kconfig
@@ -1251,6 +1251,9 @@ config IMA_NS
 	  Allow the creation of IMA namespaces for each user namespace.
 	  Namespaced IMA enables having IMA features work separately
 	  in each IMA namespace.
+	  Currently, only the audit status flags are stored in the namespace,
+	  which allows the same file to be audited each time it is accessed
+	  in a new namespace.
 
 endif # NAMESPACES
 
diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h
index 28896d256e36..dd06e16c4e1c 100644
--- a/security/integrity/ima/ima.h
+++ b/security/integrity/ima/ima.h
@@ -282,7 +282,8 @@ int process_buffer_measurement(struct user_namespace *mnt_userns,
 			       int pcr, const char *func_data,
 			       bool buf_hash, u8 *digest, size_t digest_len);
 void ima_audit_measurement(struct integrity_iint_cache *iint,
-			   const unsigned char *filename);
+			   const unsigned char *filename,
+			   struct ns_status *status);
 int ima_alloc_init_template(struct ima_event_data *event_data,
 			    struct ima_template_entry **entry,
 			    struct ima_template_desc *template_desc);
@@ -426,6 +427,9 @@ static inline void ima_free_modsig(struct modsig *modsig)
 }
 #endif /* CONFIG_IMA_APPRAISE_MODSIG */
 
+#define IMA_NS_STATUS_ACTIONS   IMA_AUDIT
+#define IMA_NS_STATUS_FLAGS     IMA_AUDITED
+
 int ima_ns_init(void);
 struct ima_namespace;
 int ima_init_namespace(struct ima_namespace *ns);
@@ -436,6 +440,10 @@ struct ns_status *ima_get_ns_status(struct ima_namespace *ns,
 
 void free_ns_status_cache(struct ima_namespace *ns);
 
+unsigned long iint_flags(struct integrity_iint_cache *iint,
+			 struct ns_status *status);
+unsigned long set_iint_flags(struct integrity_iint_cache *iint,
+			     struct ns_status *status, unsigned long flags);
 #else
 
 static inline struct ns_status *ima_get_ns_status(struct ima_namespace *ns,
@@ -444,6 +452,19 @@ static inline struct ns_status *ima_get_ns_status(struct ima_namespace *ns,
 	return NULL;
 }
 
+static inline unsigned long iint_flags(struct integrity_iint_cache *iint,
+				       struct ns_status *status)
+{
+	return iint->flags;
+}
+
+static inline unsigned long set_iint_flags(struct integrity_iint_cache *iint,
+					   struct ns_status *status,
+					   unsigned long flags)
+{
+	iint->flags = flags;
+	return flags;
+}
 #endif /* CONFIG_IMA_NS */
 
 /* LSM based policy rules require audit */
diff --git a/security/integrity/ima/ima_api.c b/security/integrity/ima/ima_api.c
index a64fb0130b01..8f7bab17b784 100644
--- a/security/integrity/ima/ima_api.c
+++ b/security/integrity/ima/ima_api.c
@@ -342,14 +342,16 @@ void ima_store_measurement(struct integrity_iint_cache *iint,
 }
 
 void ima_audit_measurement(struct integrity_iint_cache *iint,
-			   const unsigned char *filename)
+			   const unsigned char *filename,
+			   struct ns_status *status)
 {
 	struct audit_buffer *ab;
 	char *hash;
 	const char *algo_name = hash_algo_name[iint->ima_hash->algo];
 	int i;
+	unsigned long flags = iint_flags(iint, status);
 
-	if (iint->flags & IMA_AUDITED)
+	if (flags & IMA_AUDITED)
 		return;
 
 	hash = kzalloc((iint->ima_hash->length * 2) + 1, GFP_KERNEL);
@@ -372,7 +374,7 @@ void ima_audit_measurement(struct integrity_iint_cache *iint,
 	audit_log_task_info(ab);
 	audit_log_end(ab);
 
-	iint->flags |= IMA_AUDITED;
+	set_iint_flags(iint, status, flags | IMA_AUDITED);
 out:
 	kfree(hash);
 	return;
diff --git a/security/integrity/ima/ima_main.c b/security/integrity/ima/ima_main.c
index 465865412100..4386010a480e 100644
--- a/security/integrity/ima/ima_main.c
+++ b/security/integrity/ima/ima_main.c
@@ -202,8 +202,10 @@ static int process_measurement(struct file *file, const struct cred *cred,
 			       u32 secid, char *buf, loff_t size, int mask,
 			       enum ima_hooks func)
 {
+	struct ima_namespace *ns = get_current_ns();
 	struct inode *inode = file_inode(file);
 	struct integrity_iint_cache *iint = NULL;
+	struct ns_status *status = NULL;
 	struct ima_template_desc *template_desc = NULL;
 	char *pathbuf = NULL;
 	char filename[NAME_MAX];
@@ -216,6 +218,7 @@ static int process_measurement(struct file *file, const struct cred *cred,
 	bool violation_check;
 	enum hash_algo hash_algo;
 	unsigned int allowed_algos = 0;
+	unsigned long flags;
 
 	if (!ima_policy_flag || !S_ISREG(inode->i_mode))
 		return 0;
@@ -244,6 +247,12 @@ static int process_measurement(struct file *file, const struct cred *cred,
 		iint = integrity_inode_get(inode);
 		if (!iint)
 			rc = -ENOMEM;
+
+		if (!rc && (action & IMA_NS_STATUS_ACTIONS)) {
+			status = ima_get_ns_status(ns, inode);
+			if (IS_ERR(status))
+				rc = PTR_ERR(status);
+		}
 	}
 
 	if (!rc && violation_check)
@@ -259,11 +268,13 @@ static int process_measurement(struct file *file, const struct cred *cred,
 
 	mutex_lock(&iint->mutex);
 
+	flags = iint_flags(iint, status);
+
 	if (test_and_clear_bit(IMA_CHANGE_ATTR, &iint->atomic_flags))
 		/* reset appraisal flags if ima_inode_post_setattr was called */
-		iint->flags &= ~(IMA_APPRAISE | IMA_APPRAISED |
-				 IMA_APPRAISE_SUBMASK | IMA_APPRAISED_SUBMASK |
-				 IMA_ACTION_FLAGS);
+		flags &= ~(IMA_APPRAISE | IMA_APPRAISED |
+			   IMA_APPRAISE_SUBMASK | IMA_APPRAISED_SUBMASK |
+			   IMA_ACTION_FLAGS);
 
 	/*
 	 * Re-evaulate the file if either the xattr has changed or the
@@ -274,7 +285,7 @@ static int process_measurement(struct file *file, const struct cred *cred,
 	    ((inode->i_sb->s_iflags & SB_I_IMA_UNVERIFIABLE_SIGNATURE) &&
 	     !(inode->i_sb->s_iflags & SB_I_UNTRUSTED_MOUNTER) &&
 	     !(action & IMA_FAIL_UNVERIFIABLE_SIGS))) {
-		iint->flags &= ~IMA_DONE_MASK;
+		flags &= ~IMA_DONE_MASK;
 		iint->measured_pcrs = 0;
 	}
 
@@ -282,9 +293,9 @@ static int process_measurement(struct file *file, const struct cred *cred,
 	 * (IMA_MEASURE, IMA_MEASURED, IMA_XXXX_APPRAISE, IMA_XXXX_APPRAISED,
 	 *  IMA_AUDIT, IMA_AUDITED)
 	 */
-	iint->flags |= action;
+	flags = set_iint_flags(iint, status, flags | action);
 	action &= IMA_DO_MASK;
-	action &= ~((iint->flags & (IMA_DONE_MASK ^ IMA_MEASURED)) >> 1);
+	action &= ~((flags & (IMA_DONE_MASK ^ IMA_MEASURED)) >> 1);
 
 	/* If target pcr is already measured, unset IMA_MEASURE action */
 	if ((action & IMA_MEASURE) && (iint->measured_pcrs & (0x1 << pcr)))
@@ -359,7 +370,7 @@ static int process_measurement(struct file *file, const struct cred *cred,
 						  &pathname, filename);
 	}
 	if (action & IMA_AUDIT)
-		ima_audit_measurement(iint, pathname);
+		ima_audit_measurement(iint, pathname, status);
 
 	if ((file->f_flags & O_DIRECT) && (iint->flags & IMA_PERMIT_DIRECTIO))
 		rc = 0;
diff --git a/security/integrity/ima/ima_ns.c b/security/integrity/ima/ima_ns.c
index e21e9e0d2316..000dbde2f264 100644
--- a/security/integrity/ima/ima_ns.c
+++ b/security/integrity/ima/ima_ns.c
@@ -66,6 +66,26 @@ void free_ima_ns(struct kref *kref)
 	destroy_ima_ns(ns);
 }
 
+unsigned long iint_flags(struct integrity_iint_cache *iint,
+			 struct ns_status *status)
+{
+	if (!status)
+		return iint->flags;
+
+	return (iint->flags & ~IMA_NS_STATUS_FLAGS) |
+	       (status->flags & IMA_NS_STATUS_FLAGS);
+}
+
+unsigned long set_iint_flags(struct integrity_iint_cache *iint,
+			     struct ns_status *status, unsigned long flags)
+{
+	iint->flags = flags;
+	if (status)
+		status->flags = flags & IMA_NS_STATUS_FLAGS;
+
+	return flags;
+}
+
 static int __init imans_cache_init(void)
 {
 	imans_cachep = KMEM_CACHE(ima_namespace, SLAB_PANIC);
-- 
2.31.1


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

* [PATCH v5 04/16] ima: Move delayed work queue and variables into ima_namespace
  2021-12-08 22:18 [PATCH v5 00/16] ima: Namespace IMA with audit support in IMA-ns Stefan Berger
                   ` (2 preceding siblings ...)
  2021-12-08 22:18 ` [PATCH v5 03/16] ima: Namespace audit status flags Stefan Berger
@ 2021-12-08 22:18 ` Stefan Berger
  2021-12-09 13:11   ` Christian Brauner
  2021-12-08 22:18 ` [PATCH v5 05/16] ima: Move IMA's keys queue related " Stefan Berger
                   ` (11 subsequent siblings)
  15 siblings, 1 reply; 63+ messages in thread
From: Stefan Berger @ 2021-12-08 22:18 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 delayed work queue and associated variables to the
ima_namespace and initialize them.

Since keys queued up for measurement currently are only relevant in the
init_ima_ns, call ima_init_key_queue() only when the init_ima_ns is
initialized.

Protect the ima_namespace when scheduling the delayed work by taking an
additional reference to its user namespace. Put the reference when either
the delayed work has completed or when it was cancelled but hadn't run.

Signed-off-by: Stefan Berger <stefanb@linux.ibm.com>
---
 include/linux/ima.h                      | 11 +++++++
 security/integrity/ima/ima.h             | 12 ++++---
 security/integrity/ima/ima_fs.c          |  4 ++-
 security/integrity/ima/ima_init.c        |  2 --
 security/integrity/ima/ima_init_ima_ns.c |  8 +++++
 security/integrity/ima/ima_policy.c      |  4 +--
 security/integrity/ima/ima_queue_keys.c  | 42 +++++++++++++-----------
 7 files changed, 53 insertions(+), 30 deletions(-)

diff --git a/include/linux/ima.h b/include/linux/ima.h
index 9f6de36240b0..529defe4d272 100644
--- a/include/linux/ima.h
+++ b/include/linux/ima.h
@@ -217,6 +217,17 @@ struct ima_namespace {
 	struct rb_root ns_status_tree;
 	rwlock_t ns_status_lock;
 	struct kmem_cache *ns_status_cache;
+
+#ifdef CONFIG_IMA_QUEUE_EARLY_BOOT_KEYS
+	/*
+	 * If custom IMA policy is not loaded then keys queued up
+	 * for measurement should be freed. This worker is used
+	 * for handling this scenario.
+	 */
+	struct delayed_work ima_keys_delayed_work;
+	long ima_key_queue_timeout;
+	bool timer_expired;
+#endif
 };
 
 extern struct ima_namespace init_ima_ns;
diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h
index dd06e16c4e1c..9edab9050dc7 100644
--- a/security/integrity/ima/ima.h
+++ b/security/integrity/ima/ima.h
@@ -77,6 +77,8 @@ struct ima_field_data {
 	u32 len;
 };
 
+struct ima_namespace;
+
 /* IMA template field definition */
 struct ima_template_field {
 	const char field_id[IMA_TEMPLATE_FIELD_ID_MAX_LEN];
@@ -247,18 +249,18 @@ struct ima_key_entry {
 	size_t payload_len;
 	char *keyring_name;
 };
-void ima_init_key_queue(void);
+void ima_init_key_queue(struct ima_namespace *ns);
 bool ima_should_queue_key(void);
 bool ima_queue_key(struct key *keyring, const void *payload,
 		   size_t payload_len);
-void ima_process_queued_keys(void);
+void ima_process_queued_keys(struct ima_namespace *ns);
+void ima_keys_handler(struct work_struct *work);
 #else
-static inline void ima_init_key_queue(void) {}
 static inline bool ima_should_queue_key(void) { return false; }
 static inline bool ima_queue_key(struct key *keyring,
 				 const void *payload,
 				 size_t payload_len) { return false; }
-static inline void ima_process_queued_keys(void) {}
+static inline void ima_process_queued_keys(struct ima_namespace *ns) {}
 #endif /* CONFIG_IMA_QUEUE_EARLY_BOOT_KEYS */
 
 /* LIM API function definitions */
@@ -300,7 +302,7 @@ int ima_match_policy(struct user_namespace *mnt_userns, struct inode *inode,
 		     struct ima_template_desc **template_desc,
 		     const char *func_data, unsigned int *allowed_algos);
 void ima_init_policy(void);
-void ima_update_policy(void);
+void ima_update_policy(struct ima_namespace *ns);
 void ima_update_policy_flags(void);
 ssize_t ima_parse_add_rule(char *);
 void ima_delete_rules(void);
diff --git a/security/integrity/ima/ima_fs.c b/security/integrity/ima/ima_fs.c
index 3d8e9d5db5aa..5cff3d6c3dc7 100644
--- a/security/integrity/ima/ima_fs.c
+++ b/security/integrity/ima/ima_fs.c
@@ -21,6 +21,7 @@
 #include <linux/rcupdate.h>
 #include <linux/parser.h>
 #include <linux/vmalloc.h>
+#include <linux/ima.h>
 
 #include "ima.h"
 
@@ -410,6 +411,7 @@ static int ima_open_policy(struct inode *inode, struct file *filp)
 static int ima_release_policy(struct inode *inode, struct file *file)
 {
 	const char *cause = valid_policy ? "completed" : "failed";
+	struct ima_namespace *ns = get_current_ns();
 
 	if ((file->f_flags & O_ACCMODE) == O_RDONLY)
 		return seq_release(inode, file);
@@ -430,7 +432,7 @@ static int ima_release_policy(struct inode *inode, struct file *file)
 		return 0;
 	}
 
-	ima_update_policy();
+	ima_update_policy(ns);
 #if !defined(CONFIG_IMA_WRITE_POLICY) && !defined(CONFIG_IMA_READ_POLICY)
 	securityfs_remove(ima_policy);
 	ima_policy = NULL;
diff --git a/security/integrity/ima/ima_init.c b/security/integrity/ima/ima_init.c
index f6ae4557a0da..24848373a061 100644
--- a/security/integrity/ima/ima_init.c
+++ b/security/integrity/ima/ima_init.c
@@ -155,8 +155,6 @@ int __init ima_init(void)
 	if (rc != 0)
 		return rc;
 
-	ima_init_key_queue();
-
 	ima_measure_critical_data("kernel_info", "kernel_version",
 				  UTS_RELEASE, strlen(UTS_RELEASE), false,
 				  NULL, 0);
diff --git a/security/integrity/ima/ima_init_ima_ns.c b/security/integrity/ima/ima_init_ima_ns.c
index 64777377664b..75ef17d52b5b 100644
--- a/security/integrity/ima/ima_init_ima_ns.c
+++ b/security/integrity/ima/ima_init_ima_ns.c
@@ -26,6 +26,14 @@ int ima_init_namespace(struct ima_namespace *ns)
 	if (!ns->ns_status_cache)
 		return -ENOMEM;
 
+#ifdef CONFIG_IMA_QUEUE_EARLY_BOOT_KEYS
+	INIT_DELAYED_WORK(&ns->ima_keys_delayed_work, ima_keys_handler);
+	ns->ima_key_queue_timeout = 300000;
+	ns->timer_expired = false;
+	if (ns == &init_ima_ns)
+		ima_init_key_queue(ns);
+#endif
+
 	return 0;
 }
 
diff --git a/security/integrity/ima/ima_policy.c b/security/integrity/ima/ima_policy.c
index 320ca80aacab..e5aef287d14d 100644
--- a/security/integrity/ima/ima_policy.c
+++ b/security/integrity/ima/ima_policy.c
@@ -986,7 +986,7 @@ int ima_check_policy(void)
  * Policy rules are never deleted so ima_policy_flag gets zeroed only once when
  * we switch from the default policy to user defined.
  */
-void ima_update_policy(void)
+void ima_update_policy(struct ima_namespace *ns)
 {
 	struct list_head *policy = &ima_policy_rules;
 
@@ -1007,7 +1007,7 @@ void ima_update_policy(void)
 	ima_update_policy_flags();
 
 	/* Custom IMA policy has been loaded */
-	ima_process_queued_keys();
+	ima_process_queued_keys(ns);
 }
 
 /* Keep the enumeration in sync with the policy_tokens! */
diff --git a/security/integrity/ima/ima_queue_keys.c b/security/integrity/ima/ima_queue_keys.c
index 93056c03bf5a..ed5923dfe71b 100644
--- a/security/integrity/ima/ima_queue_keys.c
+++ b/security/integrity/ima/ima_queue_keys.c
@@ -10,6 +10,7 @@
 
 #include <linux/user_namespace.h>
 #include <linux/workqueue.h>
+#include <linux/ima.h>
 #include <keys/asymmetric-type.h>
 #include "ima.h"
 
@@ -25,34 +26,32 @@ static bool ima_process_keys;
 static DEFINE_MUTEX(ima_keys_lock);
 static LIST_HEAD(ima_keys);
 
-/*
- * If custom IMA policy is not loaded then keys queued up
- * for measurement should be freed. This worker is used
- * for handling this scenario.
- */
-static long ima_key_queue_timeout = 300000; /* 5 Minutes */
-static void ima_keys_handler(struct work_struct *work);
-static DECLARE_DELAYED_WORK(ima_keys_delayed_work, ima_keys_handler);
-static bool timer_expired;
-
 /*
  * This worker function frees keys that may still be
  * queued up in case custom IMA policy was not loaded.
  */
-static void ima_keys_handler(struct work_struct *work)
+void ima_keys_handler(struct work_struct *work)
 {
-	timer_expired = true;
-	ima_process_queued_keys();
+	struct ima_namespace *ns;
+
+	ns = container_of(work, struct ima_namespace, ima_keys_delayed_work.work);
+	ns->timer_expired = true;
+	ima_process_queued_keys(ns);
+
+	put_user_ns(ns->user_ns);
 }
 
 /*
  * This function sets up a worker to free queued keys in case
  * custom IMA policy was never loaded.
  */
-void ima_init_key_queue(void)
+void ima_init_key_queue(struct ima_namespace *ns)
 {
-	schedule_delayed_work(&ima_keys_delayed_work,
-			      msecs_to_jiffies(ima_key_queue_timeout));
+	/* keep IMA namespace until delayed work is done */
+	get_user_ns(ns->user_ns);
+
+	schedule_delayed_work(&ns->ima_keys_delayed_work,
+			      msecs_to_jiffies(ns->ima_key_queue_timeout));
 }
 
 static void ima_free_key_entry(struct ima_key_entry *entry)
@@ -130,7 +129,7 @@ bool ima_queue_key(struct key *keyring, const void *payload,
  * This function sets ima_process_keys to true and processes queued keys.
  * From here on keys will be processed right away (not queued).
  */
-void ima_process_queued_keys(void)
+void ima_process_queued_keys(struct ima_namespace *ns)
 {
 	struct ima_key_entry *entry, *tmp;
 	bool process = false;
@@ -154,11 +153,14 @@ void ima_process_queued_keys(void)
 	if (!process)
 		return;
 
-	if (!timer_expired)
-		cancel_delayed_work_sync(&ima_keys_delayed_work);
+	if (!ns->timer_expired) {
+		if (cancel_delayed_work_sync(&ns->ima_keys_delayed_work))
+			/* undo reference from ima_init_key_queue */
+			put_user_ns(ns->user_ns);
+	}
 
 	list_for_each_entry_safe(entry, tmp, &ima_keys, list) {
-		if (!timer_expired)
+		if (!ns->timer_expired)
 			process_buffer_measurement(&init_user_ns, NULL,
 						   entry->payload,
 						   entry->payload_len,
-- 
2.31.1


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

* [PATCH v5 05/16] ima: Move IMA's keys queue related variables into ima_namespace
  2021-12-08 22:18 [PATCH v5 00/16] ima: Namespace IMA with audit support in IMA-ns Stefan Berger
                   ` (3 preceding siblings ...)
  2021-12-08 22:18 ` [PATCH v5 04/16] ima: Move delayed work queue and variables into ima_namespace Stefan Berger
@ 2021-12-08 22:18 ` Stefan Berger
  2021-12-08 22:18 ` [PATCH v5 06/16] ima: Move policy " Stefan Berger
                   ` (10 subsequent siblings)
  15 siblings, 0 replies; 63+ messages in thread
From: Stefan Berger @ 2021-12-08 22:18 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 variables from keys queue into ima_namespace.

Some variables have to be initialized before ima_init() runs, so statically
initialize them for the init_ima_ns.

Since only init_ima_ns uses the queued keys there's no need to free the
list of queued keys when tearing down IMA namespaces.

Signed-off-by: Stefan Berger <stefanb@linux.ibm.com>
---
 include/linux/ima.h                          | 11 ++++++
 security/integrity/ima/ima.h                 |  9 ++---
 security/integrity/ima/ima_asymmetric_keys.c |  5 +--
 security/integrity/ima/ima_init_ima_ns.c     |  5 +++
 security/integrity/ima/ima_ns.c              |  6 ++++
 security/integrity/ima/ima_queue_keys.c      | 37 +++++++-------------
 6 files changed, 43 insertions(+), 30 deletions(-)

diff --git a/include/linux/ima.h b/include/linux/ima.h
index 529defe4d272..0ce58c17cb25 100644
--- a/include/linux/ima.h
+++ b/include/linux/ima.h
@@ -219,6 +219,17 @@ struct ima_namespace {
 	struct kmem_cache *ns_status_cache;
 
 #ifdef CONFIG_IMA_QUEUE_EARLY_BOOT_KEYS
+	/*
+	 * Flag to indicate whether a key can be processed
+	 * right away or should be queued for processing later.
+	 */
+	bool ima_process_keys;
+
+	/*
+	 * To synchronize access to the list of keys that need to be measured
+	 */
+	struct mutex ima_keys_lock;
+	struct list_head ima_keys;
 	/*
 	 * If custom IMA policy is not loaded then keys queued up
 	 * for measurement should be freed. This worker is used
diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h
index 9edab9050dc7..97eb03376855 100644
--- a/security/integrity/ima/ima.h
+++ b/security/integrity/ima/ima.h
@@ -250,14 +250,15 @@ struct ima_key_entry {
 	char *keyring_name;
 };
 void ima_init_key_queue(struct ima_namespace *ns);
-bool ima_should_queue_key(void);
-bool ima_queue_key(struct key *keyring, const void *payload,
+bool ima_should_queue_key(struct ima_namespace *ns);
+bool ima_queue_key(struct ima_namespace *ns, struct key *keyring, const void *payload,
 		   size_t payload_len);
 void ima_process_queued_keys(struct ima_namespace *ns);
 void ima_keys_handler(struct work_struct *work);
 #else
-static inline bool ima_should_queue_key(void) { return false; }
-static inline bool ima_queue_key(struct key *keyring,
+static inline bool ima_should_queue_key(struct ima_namespace *ns) { return false; }
+static inline bool ima_queue_key(struct ima_namespace *ns,
+				 struct key *keyring,
 				 const void *payload,
 				 size_t payload_len) { return false; }
 static inline void ima_process_queued_keys(struct ima_namespace *ns) {}
diff --git a/security/integrity/ima/ima_asymmetric_keys.c b/security/integrity/ima/ima_asymmetric_keys.c
index f6aa0b47a772..b20e82eda8f4 100644
--- a/security/integrity/ima/ima_asymmetric_keys.c
+++ b/security/integrity/ima/ima_asymmetric_keys.c
@@ -30,6 +30,7 @@ void ima_post_key_create_or_update(struct key *keyring, struct key *key,
 				   const void *payload, size_t payload_len,
 				   unsigned long flags, bool create)
 {
+	struct ima_namespace *ns = get_current_ns();
 	bool queued = false;
 
 	/* Only asymmetric keys are handled by this hook. */
@@ -39,8 +40,8 @@ void ima_post_key_create_or_update(struct key *keyring, struct key *key,
 	if (!payload || (payload_len == 0))
 		return;
 
-	if (ima_should_queue_key())
-		queued = ima_queue_key(keyring, payload, payload_len);
+	if (ima_should_queue_key(ns))
+		queued = ima_queue_key(ns, keyring, payload, payload_len);
 
 	if (queued)
 		return;
diff --git a/security/integrity/ima/ima_init_ima_ns.c b/security/integrity/ima/ima_init_ima_ns.c
index 75ef17d52b5b..144b30ceefb0 100644
--- a/security/integrity/ima/ima_init_ima_ns.c
+++ b/security/integrity/ima/ima_init_ima_ns.c
@@ -45,5 +45,10 @@ int __init ima_ns_init(void)
 struct ima_namespace init_ima_ns = {
 	.kref = KREF_INIT(1),
 	.user_ns = &init_user_ns,
+#ifdef CONFIG_IMA_QUEUE_EARLY_BOOT_KEYS
+	.ima_process_keys = false,
+	.ima_keys_lock = __MUTEX_INITIALIZER(init_ima_ns.ima_keys_lock),
+	.ima_keys = LIST_HEAD_INIT(init_ima_ns.ima_keys),
+#endif
 };
 EXPORT_SYMBOL(init_ima_ns);
diff --git a/security/integrity/ima/ima_ns.c b/security/integrity/ima/ima_ns.c
index 000dbde2f264..1ee724bab714 100644
--- a/security/integrity/ima/ima_ns.c
+++ b/security/integrity/ima/ima_ns.c
@@ -38,6 +38,12 @@ int create_ima_ns(struct user_namespace *user_ns)
 	if (err)
 		goto fail_free;
 
+#ifdef CONFIG_IMA_QUEUE_EARLY_BOOT_KEYS
+	ns->ima_process_keys = false;
+	mutex_init(&ns->ima_keys_lock);
+	INIT_LIST_HEAD(&ns->ima_keys);
+#endif
+
 	user_ns->ima_ns = ns;
 
 	return 0;
diff --git a/security/integrity/ima/ima_queue_keys.c b/security/integrity/ima/ima_queue_keys.c
index ed5923dfe71b..4c69178b1ffe 100644
--- a/security/integrity/ima/ima_queue_keys.c
+++ b/security/integrity/ima/ima_queue_keys.c
@@ -14,17 +14,6 @@
 #include <keys/asymmetric-type.h>
 #include "ima.h"
 
-/*
- * Flag to indicate whether a key can be processed
- * right away or should be queued for processing later.
- */
-static bool ima_process_keys;
-
-/*
- * To synchronize access to the list of keys that need to be measured
- */
-static DEFINE_MUTEX(ima_keys_lock);
-static LIST_HEAD(ima_keys);
 
 /*
  * This worker function frees keys that may still be
@@ -100,7 +89,7 @@ static struct ima_key_entry *ima_alloc_key_entry(struct key *keyring,
 	return entry;
 }
 
-bool ima_queue_key(struct key *keyring, const void *payload,
+bool ima_queue_key(struct ima_namespace *ns, struct key *keyring, const void *payload,
 		   size_t payload_len)
 {
 	bool queued = false;
@@ -110,12 +99,12 @@ bool ima_queue_key(struct key *keyring, const void *payload,
 	if (!entry)
 		return false;
 
-	mutex_lock(&ima_keys_lock);
-	if (!ima_process_keys) {
-		list_add_tail(&entry->list, &ima_keys);
+	mutex_lock(&ns->ima_keys_lock);
+	if (!ns->ima_process_keys) {
+		list_add_tail(&entry->list, &ns->ima_keys);
 		queued = true;
 	}
-	mutex_unlock(&ima_keys_lock);
+	mutex_unlock(&ns->ima_keys_lock);
 
 	if (!queued)
 		ima_free_key_entry(entry);
@@ -134,7 +123,7 @@ void ima_process_queued_keys(struct ima_namespace *ns)
 	struct ima_key_entry *entry, *tmp;
 	bool process = false;
 
-	if (ima_process_keys)
+	if (ns->ima_process_keys)
 		return;
 
 	/*
@@ -143,12 +132,12 @@ void ima_process_queued_keys(struct ima_namespace *ns)
 	 * First one setting the ima_process_keys flag to true will
 	 * process the queued keys.
 	 */
-	mutex_lock(&ima_keys_lock);
-	if (!ima_process_keys) {
-		ima_process_keys = true;
+	mutex_lock(&ns->ima_keys_lock);
+	if (!ns->ima_process_keys) {
+		ns->ima_process_keys = true;
 		process = true;
 	}
-	mutex_unlock(&ima_keys_lock);
+	mutex_unlock(&ns->ima_keys_lock);
 
 	if (!process)
 		return;
@@ -159,7 +148,7 @@ void ima_process_queued_keys(struct ima_namespace *ns)
 			put_user_ns(ns->user_ns);
 	}
 
-	list_for_each_entry_safe(entry, tmp, &ima_keys, list) {
+	list_for_each_entry_safe(entry, tmp, &ns->ima_keys, list) {
 		if (!ns->timer_expired)
 			process_buffer_measurement(&init_user_ns, NULL,
 						   entry->payload,
@@ -173,7 +162,7 @@ void ima_process_queued_keys(struct ima_namespace *ns)
 	}
 }
 
-inline bool ima_should_queue_key(void)
+inline bool ima_should_queue_key(struct ima_namespace *ns)
 {
-	return !ima_process_keys;
+	return !ns->ima_process_keys;
 }
-- 
2.31.1


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

* [PATCH v5 06/16] ima: Move policy related variables into ima_namespace
  2021-12-08 22:18 [PATCH v5 00/16] ima: Namespace IMA with audit support in IMA-ns Stefan Berger
                   ` (4 preceding siblings ...)
  2021-12-08 22:18 ` [PATCH v5 05/16] ima: Move IMA's keys queue related " Stefan Berger
@ 2021-12-08 22:18 ` Stefan Berger
  2021-12-08 22:18 ` [PATCH v5 07/16] ima: Move ima_htable " Stefan Berger
                   ` (9 subsequent siblings)
  15 siblings, 0 replies; 63+ messages in thread
From: Stefan Berger @ 2021-12-08 22:18 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 variables related to the IMA policy into the ima_namespace. This way
the IMA policy of an IMA namespace can be set and displayed using a
front-end like SecurityFS.

Implement ima_free_policy_rules() that frees the policy rules on
ima_namespace deletion.

Signed-off-by: Stefan Berger <stefanb@linux.ibm.com>
---
 include/linux/ima.h                          |  10 ++
 security/integrity/ima/ima.h                 |  35 ++---
 security/integrity/ima/ima_api.c             |   8 +-
 security/integrity/ima/ima_appraise.c        |  26 ++--
 security/integrity/ima/ima_asymmetric_keys.c |   3 +-
 security/integrity/ima/ima_fs.c              |  10 +-
 security/integrity/ima/ima_init.c            |   8 +-
 security/integrity/ima/ima_init_ima_ns.c     |   6 +
 security/integrity/ima/ima_main.c            |  83 +++++++-----
 security/integrity/ima/ima_ns.c              |   1 +
 security/integrity/ima/ima_policy.c          | 132 +++++++++++--------
 security/integrity/ima/ima_queue_keys.c      |   2 +-
 12 files changed, 196 insertions(+), 128 deletions(-)

diff --git a/include/linux/ima.h b/include/linux/ima.h
index 0ce58c17cb25..9971ee09b7ee 100644
--- a/include/linux/ima.h
+++ b/include/linux/ima.h
@@ -239,9 +239,19 @@ struct ima_namespace {
 	long ima_key_queue_timeout;
 	bool timer_expired;
 #endif
+
+	struct list_head ima_default_rules;
+	/* ns's policy rules */
+	struct list_head ima_policy_rules;
+	struct list_head ima_temp_rules;
+	/* Pointer to ns's current policy */
+	struct list_head __rcu *ima_rules;
+	/* current content of the policy */
+	int ima_policy_flag;
 };
 
 extern struct ima_namespace init_ima_ns;
+extern struct list_head ima_default_rules;
 
 #ifdef CONFIG_IMA_NS
 
diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h
index 97eb03376855..e295141f2478 100644
--- a/security/integrity/ima/ima.h
+++ b/security/integrity/ima/ima.h
@@ -43,9 +43,6 @@ enum tpm_pcrs { TPM_PCR0 = 0, TPM_PCR8 = 8, TPM_PCR10 = 10 };
 
 #define NR_BANKS(chip) ((chip != NULL) ? chip->nr_allocated_banks : 0)
 
-/* current content of the policy */
-extern int ima_policy_flag;
-
 /* bitset of digests algorithms allowed in the setxattr hook */
 extern atomic_t ima_setxattr_allowed_hash_algorithms;
 
@@ -265,7 +262,8 @@ static inline void ima_process_queued_keys(struct ima_namespace *ns) {}
 #endif /* CONFIG_IMA_QUEUE_EARLY_BOOT_KEYS */
 
 /* LIM API function definitions */
-int ima_get_action(struct user_namespace *mnt_userns, struct inode *inode,
+int ima_get_action(struct ima_namespace *ns,
+		   struct user_namespace *mnt_userns, struct inode *inode,
 		   const struct cred *cred, u32 secid, int mask,
 		   enum ima_hooks func, int *pcr,
 		   struct ima_template_desc **template_desc,
@@ -279,7 +277,8 @@ void ima_store_measurement(struct integrity_iint_cache *iint, struct file *file,
 			   struct evm_ima_xattr_data *xattr_value,
 			   int xattr_len, const struct modsig *modsig, int pcr,
 			   struct ima_template_desc *template_desc);
-int process_buffer_measurement(struct user_namespace *mnt_userns,
+int process_buffer_measurement(struct ima_namespace *ns,
+			       struct user_namespace *mnt_userns,
 			       struct inode *inode, const void *buf, int size,
 			       const char *eventname, enum ima_hooks func,
 			       int pcr, const char *func_data,
@@ -297,17 +296,19 @@ void ima_free_template_entry(struct ima_template_entry *entry);
 const char *ima_d_path(const struct path *path, char **pathbuf, char *filename);
 
 /* IMA policy related functions */
-int ima_match_policy(struct user_namespace *mnt_userns, struct inode *inode,
+int ima_match_policy(struct ima_namespace *ns,
+		     struct user_namespace *mnt_userns, struct inode *inode,
 		     const struct cred *cred, u32 secid, enum ima_hooks func,
 		     int mask, int flags, int *pcr,
 		     struct ima_template_desc **template_desc,
 		     const char *func_data, unsigned int *allowed_algos);
-void ima_init_policy(void);
+void ima_init_policy(struct ima_namespace *ns);
 void ima_update_policy(struct ima_namespace *ns);
-void ima_update_policy_flags(void);
-ssize_t ima_parse_add_rule(char *);
-void ima_delete_rules(void);
-int ima_check_policy(void);
+void ima_update_policy_flags(struct ima_namespace *ns);
+ssize_t ima_parse_add_rule(struct ima_namespace *ns, char *rule);
+void ima_delete_rules(struct ima_namespace *ns);
+int ima_check_policy(struct ima_namespace *ns);
+void ima_free_policy_rules(struct ima_namespace *ns);
 void *ima_policy_start(struct seq_file *m, loff_t *pos);
 void *ima_policy_next(struct seq_file *m, void *v, loff_t *pos);
 void ima_policy_stop(struct seq_file *m, void *v);
@@ -323,14 +324,16 @@ int ima_policy_show(struct seq_file *m, void *v);
 #define IMA_APPRAISE_KEXEC	0x40
 
 #ifdef CONFIG_IMA_APPRAISE
-int ima_check_blacklist(struct integrity_iint_cache *iint,
+int ima_check_blacklist(struct ima_namespace *ns,
+			struct integrity_iint_cache *iint,
 			const struct modsig *modsig, int pcr);
 int ima_appraise_measurement(enum ima_hooks func,
 			     struct integrity_iint_cache *iint,
 			     struct file *file, const unsigned char *filename,
 			     struct evm_ima_xattr_data *xattr_value,
 			     int xattr_len, const struct modsig *modsig);
-int ima_must_appraise(struct user_namespace *mnt_userns, struct inode *inode,
+int ima_must_appraise(struct ima_namespace *ns,
+		      struct user_namespace *mnt_userns, struct inode *inode,
 		      int mask, enum ima_hooks func);
 void ima_update_xattr(struct integrity_iint_cache *iint, struct file *file);
 enum integrity_status ima_get_cache_status(struct integrity_iint_cache *iint,
@@ -341,7 +344,8 @@ int ima_read_xattr(struct dentry *dentry,
 		   struct evm_ima_xattr_data **xattr_value);
 
 #else
-static inline int ima_check_blacklist(struct integrity_iint_cache *iint,
+static inline int ima_check_blacklist(struct ima_namespace *ns,
+				      struct integrity_iint_cache *iint,
 				      const struct modsig *modsig, int pcr)
 {
 	return 0;
@@ -358,7 +362,8 @@ static inline int ima_appraise_measurement(enum ima_hooks func,
 	return INTEGRITY_UNKNOWN;
 }
 
-static inline int ima_must_appraise(struct user_namespace *mnt_userns,
+static inline int ima_must_appraise(struct ima_namespace *ns,
+				    struct user_namespace *mnt_userns,
 				    struct inode *inode, int mask,
 				    enum ima_hooks func)
 {
diff --git a/security/integrity/ima/ima_api.c b/security/integrity/ima/ima_api.c
index 8f7bab17b784..808aec56dbb6 100644
--- a/security/integrity/ima/ima_api.c
+++ b/security/integrity/ima/ima_api.c
@@ -14,6 +14,7 @@
 #include <linux/xattr.h>
 #include <linux/evm.h>
 #include <linux/iversion.h>
+#include <linux/ima.h>
 
 #include "ima.h"
 
@@ -185,7 +186,8 @@ void ima_add_violation(struct file *file, const unsigned char *filename,
  * Returns IMA_MEASURE, IMA_APPRAISE mask.
  *
  */
-int ima_get_action(struct user_namespace *mnt_userns, struct inode *inode,
+int ima_get_action(struct ima_namespace *ns,
+		   struct user_namespace *mnt_userns, struct inode *inode,
 		   const struct cred *cred, u32 secid, int mask,
 		   enum ima_hooks func, int *pcr,
 		   struct ima_template_desc **template_desc,
@@ -193,9 +195,9 @@ int ima_get_action(struct user_namespace *mnt_userns, struct inode *inode,
 {
 	int flags = IMA_MEASURE | IMA_AUDIT | IMA_APPRAISE | IMA_HASH;
 
-	flags &= ima_policy_flag;
+	flags &= ns->ima_policy_flag;
 
-	return ima_match_policy(mnt_userns, inode, cred, secid, func, mask,
+	return ima_match_policy(ns, mnt_userns, inode, cred, secid, func, mask,
 				flags, pcr, template_desc, func_data,
 				allowed_algos);
 }
diff --git a/security/integrity/ima/ima_appraise.c b/security/integrity/ima/ima_appraise.c
index dbba51583e7c..b0c1992d8c4b 100644
--- a/security/integrity/ima/ima_appraise.c
+++ b/security/integrity/ima/ima_appraise.c
@@ -68,7 +68,8 @@ bool is_ima_appraise_enabled(void)
  *
  * Return 1 to appraise or hash
  */
-int ima_must_appraise(struct user_namespace *mnt_userns, struct inode *inode,
+int ima_must_appraise(struct ima_namespace *ns,
+		      struct user_namespace *mnt_userns, struct inode *inode,
 		      int mask, enum ima_hooks func)
 {
 	u32 secid;
@@ -77,7 +78,7 @@ int ima_must_appraise(struct user_namespace *mnt_userns, struct inode *inode,
 		return 0;
 
 	security_task_getsecid_subj(current, &secid);
-	return ima_match_policy(mnt_userns, inode, current_cred(), secid,
+	return ima_match_policy(ns, mnt_userns, inode, current_cred(), secid,
 				func, mask, IMA_APPRAISE | IMA_HASH, NULL,
 				NULL, NULL, NULL);
 }
@@ -341,7 +342,8 @@ static int modsig_verify(enum ima_hooks func, const struct modsig *modsig,
  *
  * Returns -EPERM if the hash is blacklisted.
  */
-int ima_check_blacklist(struct integrity_iint_cache *iint,
+int ima_check_blacklist(struct ima_namespace *ns,
+			struct integrity_iint_cache *iint,
 			const struct modsig *modsig, int pcr)
 {
 	enum hash_algo hash_algo;
@@ -357,7 +359,7 @@ int ima_check_blacklist(struct integrity_iint_cache *iint,
 
 		rc = is_binary_blacklisted(digest, digestsize);
 		if ((rc == -EPERM) && (iint->flags & IMA_MEASURE))
-			process_buffer_measurement(&init_user_ns, NULL, digest, digestsize,
+			process_buffer_measurement(ns, &init_user_ns, NULL, digest, digestsize,
 						   "blacklisted-hash", NONE,
 						   pcr, NULL, false, NULL, 0);
 	}
@@ -527,14 +529,15 @@ void ima_inode_post_setattr(struct user_namespace *mnt_userns,
 			    struct dentry *dentry)
 {
 	struct inode *inode = d_backing_inode(dentry);
+	struct ima_namespace *ns = get_current_ns();
 	struct integrity_iint_cache *iint;
 	int action;
 
-	if (!(ima_policy_flag & IMA_APPRAISE) || !S_ISREG(inode->i_mode)
+	if (!(ns->ima_policy_flag & IMA_APPRAISE) || !S_ISREG(inode->i_mode)
 	    || !(inode->i_opflags & IOP_XATTR))
 		return;
 
-	action = ima_must_appraise(mnt_userns, inode, MAY_ACCESS, POST_SETATTR);
+	action = ima_must_appraise(ns, mnt_userns, inode, MAY_ACCESS, POST_SETATTR);
 	iint = integrity_iint_find(inode);
 	if (iint) {
 		set_bit(IMA_CHANGE_ATTR, &iint->atomic_flags);
@@ -559,11 +562,12 @@ static int ima_protect_xattr(struct dentry *dentry, const char *xattr_name,
 	return 0;
 }
 
-static void ima_reset_appraise_flags(struct inode *inode, int digsig)
+static void ima_reset_appraise_flags(struct ima_namespace *ns,
+				     struct inode *inode, int digsig)
 {
 	struct integrity_iint_cache *iint;
 
-	if (!(ima_policy_flag & IMA_APPRAISE) || !S_ISREG(inode->i_mode))
+	if (!(ns->ima_policy_flag & IMA_APPRAISE) || !S_ISREG(inode->i_mode))
 		return;
 
 	iint = integrity_iint_find(inode);
@@ -641,6 +645,7 @@ int ima_inode_setxattr(struct dentry *dentry, const char *xattr_name,
 		       const void *xattr_value, size_t xattr_value_len)
 {
 	const struct evm_ima_xattr_data *xvalue = xattr_value;
+	struct ima_namespace *ns = get_current_ns();
 	int digsig = 0;
 	int result;
 
@@ -658,18 +663,19 @@ int ima_inode_setxattr(struct dentry *dentry, const char *xattr_name,
 		if (result)
 			return result;
 
-		ima_reset_appraise_flags(d_backing_inode(dentry), digsig);
+		ima_reset_appraise_flags(ns, d_backing_inode(dentry), digsig);
 	}
 	return result;
 }
 
 int ima_inode_removexattr(struct dentry *dentry, const char *xattr_name)
 {
+	struct ima_namespace *ns = get_current_ns();
 	int result;
 
 	result = ima_protect_xattr(dentry, xattr_name, NULL, 0);
 	if (result == 1 || evm_revalidate_status(xattr_name)) {
-		ima_reset_appraise_flags(d_backing_inode(dentry), 0);
+		ima_reset_appraise_flags(ns, d_backing_inode(dentry), 0);
 		if (result == 1)
 			result = 0;
 	}
diff --git a/security/integrity/ima/ima_asymmetric_keys.c b/security/integrity/ima/ima_asymmetric_keys.c
index b20e82eda8f4..959e9f2f6c26 100644
--- a/security/integrity/ima/ima_asymmetric_keys.c
+++ b/security/integrity/ima/ima_asymmetric_keys.c
@@ -61,7 +61,8 @@ void ima_post_key_create_or_update(struct key *keyring, struct key *key,
 	 * if the IMA policy is configured to measure a key linked
 	 * to the given keyring.
 	 */
-	process_buffer_measurement(&init_user_ns, NULL, payload, payload_len,
+	process_buffer_measurement(ns, &init_user_ns, NULL,
+				   payload, payload_len,
 				   keyring->description, KEY_CHECK, 0,
 				   keyring->description, false, NULL, 0);
 }
diff --git a/security/integrity/ima/ima_fs.c b/security/integrity/ima/ima_fs.c
index 5cff3d6c3dc7..25f85c58ebef 100644
--- a/security/integrity/ima/ima_fs.c
+++ b/security/integrity/ima/ima_fs.c
@@ -274,6 +274,7 @@ static const struct file_operations ima_ascii_measurements_ops = {
 
 static ssize_t ima_read_policy(char *path)
 {
+	struct ima_namespace *ns = get_current_ns();
 	void *data = NULL;
 	char *datap;
 	size_t size;
@@ -297,7 +298,7 @@ static ssize_t ima_read_policy(char *path)
 	datap = data;
 	while (size > 0 && (p = strsep(&datap, "\n"))) {
 		pr_debug("rule: %s\n", p);
-		rc = ima_parse_add_rule(p);
+		rc = ima_parse_add_rule(ns, p);
 		if (rc < 0)
 			break;
 		size -= rc;
@@ -315,6 +316,7 @@ static ssize_t ima_read_policy(char *path)
 static ssize_t ima_write_policy(struct file *file, const char __user *buf,
 				size_t datalen, loff_t *ppos)
 {
+	struct ima_namespace *ns = get_current_ns();
 	char *data;
 	ssize_t result;
 
@@ -345,7 +347,7 @@ static ssize_t ima_write_policy(struct file *file, const char __user *buf,
 				    1, 0);
 		result = -EACCES;
 	} else {
-		result = ima_parse_add_rule(data);
+		result = ima_parse_add_rule(ns, data);
 	}
 	mutex_unlock(&ima_write_mutex);
 out_free:
@@ -416,7 +418,7 @@ static int ima_release_policy(struct inode *inode, struct file *file)
 	if ((file->f_flags & O_ACCMODE) == O_RDONLY)
 		return seq_release(inode, file);
 
-	if (valid_policy && ima_check_policy() < 0) {
+	if (valid_policy && ima_check_policy(ns) < 0) {
 		cause = "failed";
 		valid_policy = 0;
 	}
@@ -426,7 +428,7 @@ static int ima_release_policy(struct inode *inode, struct file *file)
 			    "policy_update", cause, !valid_policy, 0);
 
 	if (!valid_policy) {
-		ima_delete_rules();
+		ima_delete_rules(ns);
 		valid_policy = 1;
 		clear_bit(IMA_FS_BUSY, &ima_fs_flags);
 		return 0;
diff --git a/security/integrity/ima/ima_init.c b/security/integrity/ima/ima_init.c
index 24848373a061..d78f8faf6dcd 100644
--- a/security/integrity/ima/ima_init.c
+++ b/security/integrity/ima/ima_init.c
@@ -104,15 +104,15 @@ static int __init ima_add_boot_aggregate(void)
 #ifdef CONFIG_IMA_LOAD_X509
 void __init ima_load_x509(void)
 {
-	int unset_flags = ima_policy_flag & IMA_APPRAISE;
+	int unset_flags = init_ima_ns.ima_policy_flag & IMA_APPRAISE;
 
-	ima_policy_flag &= ~unset_flags;
+	init_ima_ns.ima_policy_flag &= ~unset_flags;
 	integrity_load_x509(INTEGRITY_KEYRING_IMA, CONFIG_IMA_X509_PATH);
 
 	/* load also EVM key to avoid appraisal */
 	evm_load_x509();
 
-	ima_policy_flag |= unset_flags;
+	init_ima_ns.ima_policy_flag |= unset_flags;
 }
 #endif
 
@@ -149,7 +149,7 @@ int __init ima_init(void)
 	if (rc != 0)
 		return rc;
 
-	ima_init_policy();
+	ima_init_policy(&init_ima_ns);
 
 	rc = ima_fs_init();
 	if (rc != 0)
diff --git a/security/integrity/ima/ima_init_ima_ns.c b/security/integrity/ima/ima_init_ima_ns.c
index 144b30ceefb0..f32cd9f22eff 100644
--- a/security/integrity/ima/ima_init_ima_ns.c
+++ b/security/integrity/ima/ima_init_ima_ns.c
@@ -34,6 +34,12 @@ int ima_init_namespace(struct ima_namespace *ns)
 		ima_init_key_queue(ns);
 #endif
 
+	INIT_LIST_HEAD(&ns->ima_default_rules);
+	INIT_LIST_HEAD(&ns->ima_policy_rules);
+	INIT_LIST_HEAD(&ns->ima_temp_rules);
+	ns->ima_rules = (struct list_head __rcu *)(&ns->ima_default_rules);
+	ns->ima_policy_flag = 0;
+
 	return 0;
 }
 
diff --git a/security/integrity/ima/ima_main.c b/security/integrity/ima/ima_main.c
index 4386010a480e..897304046ba4 100644
--- a/security/integrity/ima/ima_main.c
+++ b/security/integrity/ima/ima_main.c
@@ -185,10 +185,11 @@ static void ima_check_last_writer(struct integrity_iint_cache *iint,
  */
 void ima_file_free(struct file *file)
 {
+	struct ima_namespace *ns = get_current_ns();
 	struct inode *inode = file_inode(file);
 	struct integrity_iint_cache *iint;
 
-	if (!ima_policy_flag || !S_ISREG(inode->i_mode))
+	if (!ns->ima_policy_flag || !S_ISREG(inode->i_mode))
 		return;
 
 	iint = integrity_iint_find(inode);
@@ -198,11 +199,11 @@ 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)
 {
-	struct ima_namespace *ns = get_current_ns();
 	struct inode *inode = file_inode(file);
 	struct integrity_iint_cache *iint = NULL;
 	struct ns_status *status = NULL;
@@ -220,18 +221,18 @@ static int process_measurement(struct file *file, const struct cred *cred,
 	unsigned int allowed_algos = 0;
 	unsigned long flags;
 
-	if (!ima_policy_flag || !S_ISREG(inode->i_mode))
+	if (!ns->ima_policy_flag || !S_ISREG(inode->i_mode))
 		return 0;
 
 	/* Return an IMA_MEASURE, IMA_APPRAISE, IMA_AUDIT action
 	 * bitmask based on the appraise/audit/measurement policy.
 	 * Included is the appraise submask.
 	 */
-	action = ima_get_action(file_mnt_user_ns(file), inode, cred, secid,
+	action = ima_get_action(ns, file_mnt_user_ns(file), inode, cred, secid,
 				mask, func, &pcr, &template_desc, NULL,
 				&allowed_algos);
 	violation_check = ((func == FILE_CHECK || func == MMAP_CHECK) &&
-			   (ima_policy_flag & IMA_MEASURE));
+			   (ns->ima_policy_flag & IMA_MEASURE));
 	if (!action && !violation_check)
 		return 0;
 
@@ -357,7 +358,7 @@ static int process_measurement(struct file *file, const struct cred *cred,
 				      xattr_value, xattr_len, modsig, pcr,
 				      template_desc);
 	if (rc == 0 && (action & IMA_APPRAISE_SUBMASK)) {
-		rc = ima_check_blacklist(iint, modsig, pcr);
+		rc = ima_check_blacklist(ns, iint, modsig, pcr);
 		if (rc != -EPERM) {
 			inode_lock(inode);
 			rc = ima_appraise_measurement(func, iint, file,
@@ -416,12 +417,13 @@ static int process_measurement(struct file *file, const struct cred *cred,
  */
 int ima_file_mmap(struct file *file, unsigned long prot)
 {
+	struct ima_namespace *ns = get_current_ns();
 	u32 secid;
 
 	if (file && (prot & PROT_EXEC)) {
 		security_task_getsecid_subj(current, &secid);
-		return process_measurement(file, current_cred(), secid, NULL,
-					   0, MAY_EXEC, MMAP_CHECK);
+		return process_measurement(ns, file, current_cred(), secid,
+					   NULL, 0, MAY_EXEC, MMAP_CHECK);
 	}
 
 	return 0;
@@ -441,6 +443,7 @@ int ima_file_mmap(struct file *file, unsigned long prot)
  */
 int ima_file_mprotect(struct vm_area_struct *vma, unsigned long prot)
 {
+	struct ima_namespace *ns = get_current_ns();
 	struct ima_template_desc *template = NULL;
 	struct file *file = vma->vm_file;
 	char filename[NAME_MAX];
@@ -453,13 +456,13 @@ int ima_file_mprotect(struct vm_area_struct *vma, unsigned long prot)
 	int pcr;
 
 	/* Is mprotect making an mmap'ed file executable? */
-	if (!(ima_policy_flag & IMA_APPRAISE) || !vma->vm_file ||
+	if (!(ns->ima_policy_flag & IMA_APPRAISE) || !vma->vm_file ||
 	    !(prot & PROT_EXEC) || (vma->vm_flags & VM_EXEC))
 		return 0;
 
 	security_task_getsecid_subj(current, &secid);
 	inode = file_inode(vma->vm_file);
-	action = ima_get_action(file_mnt_user_ns(vma->vm_file), inode,
+	action = ima_get_action(ns, file_mnt_user_ns(vma->vm_file), inode,
 				current_cred(), secid, MAY_EXEC, MMAP_CHECK,
 				&pcr, &template, NULL, NULL);
 
@@ -495,17 +498,18 @@ int ima_file_mprotect(struct vm_area_struct *vma, unsigned long prot)
  */
 int ima_bprm_check(struct linux_binprm *bprm)
 {
+	struct ima_namespace *ns = get_current_ns();
 	int ret;
 	u32 secid;
 
 	security_task_getsecid_subj(current, &secid);
-	ret = process_measurement(bprm->file, current_cred(), secid, NULL, 0,
-				  MAY_EXEC, BPRM_CHECK);
+	ret = process_measurement(ns, bprm->file, current_cred(), secid, NULL,
+				  0, MAY_EXEC, BPRM_CHECK);
 	if (ret)
 		return ret;
 
 	security_cred_getsecid(bprm->cred, &secid);
-	return process_measurement(bprm->file, bprm->cred, secid, NULL, 0,
+	return process_measurement(ns, bprm->file, bprm->cred, secid, NULL, 0,
 				   MAY_EXEC, CREDS_CHECK);
 }
 
@@ -521,21 +525,23 @@ int ima_bprm_check(struct linux_binprm *bprm)
  */
 int ima_file_check(struct file *file, int mask)
 {
+	struct ima_namespace *ns = get_current_ns();
 	u32 secid;
 
 	security_task_getsecid_subj(current, &secid);
-	return process_measurement(file, current_cred(), secid, NULL, 0,
+	return process_measurement(ns, file, current_cred(), secid, NULL, 0,
 				   mask & (MAY_READ | MAY_WRITE | MAY_EXEC |
 					   MAY_APPEND), FILE_CHECK);
 }
 EXPORT_SYMBOL_GPL(ima_file_check);
 
-static int __ima_inode_hash(struct inode *inode, char *buf, size_t buf_size)
+static int __ima_inode_hash(struct ima_namespace *ns,
+			    struct inode *inode, char *buf, size_t buf_size)
 {
 	struct integrity_iint_cache *iint;
 	int hash_algo;
 
-	if (!ima_policy_flag)
+	if (!ns->ima_policy_flag)
 		return -EOPNOTSUPP;
 
 	iint = integrity_iint_find(inode);
@@ -585,10 +591,12 @@ static int __ima_inode_hash(struct inode *inode, char *buf, size_t buf_size)
  */
 int ima_file_hash(struct file *file, char *buf, size_t buf_size)
 {
+	struct ima_namespace *ns = get_current_ns();
+
 	if (!file)
 		return -EINVAL;
 
-	return __ima_inode_hash(file_inode(file), buf, buf_size);
+	return __ima_inode_hash(ns, file_inode(file), buf, buf_size);
 }
 EXPORT_SYMBOL_GPL(ima_file_hash);
 
@@ -612,10 +620,12 @@ EXPORT_SYMBOL_GPL(ima_file_hash);
  */
 int ima_inode_hash(struct inode *inode, char *buf, size_t buf_size)
 {
+	struct ima_namespace *ns = get_current_ns();
+
 	if (!inode)
 		return -EINVAL;
 
-	return __ima_inode_hash(inode, buf, buf_size);
+	return __ima_inode_hash(ns, inode, buf, buf_size);
 }
 EXPORT_SYMBOL_GPL(ima_inode_hash);
 
@@ -631,13 +641,14 @@ EXPORT_SYMBOL_GPL(ima_inode_hash);
 void ima_post_create_tmpfile(struct user_namespace *mnt_userns,
 			     struct inode *inode)
 {
+	struct ima_namespace *ns = get_current_ns();
 	struct integrity_iint_cache *iint;
 	int must_appraise;
 
-	if (!ima_policy_flag || !S_ISREG(inode->i_mode))
+	if (!ns->ima_policy_flag || !S_ISREG(inode->i_mode))
 		return;
 
-	must_appraise = ima_must_appraise(mnt_userns, inode, MAY_ACCESS,
+	must_appraise = ima_must_appraise(ns, mnt_userns, inode, MAY_ACCESS,
 					  FILE_CHECK);
 	if (!must_appraise)
 		return;
@@ -663,14 +674,15 @@ void ima_post_create_tmpfile(struct user_namespace *mnt_userns,
 void ima_post_path_mknod(struct user_namespace *mnt_userns,
 			 struct dentry *dentry)
 {
+	struct ima_namespace *ns = get_current_ns();
 	struct integrity_iint_cache *iint;
 	struct inode *inode = dentry->d_inode;
 	int must_appraise;
 
-	if (!ima_policy_flag || !S_ISREG(inode->i_mode))
+	if (!ns->ima_policy_flag || !S_ISREG(inode->i_mode))
 		return;
 
-	must_appraise = ima_must_appraise(mnt_userns, inode, MAY_ACCESS,
+	must_appraise = ima_must_appraise(ns, mnt_userns, inode, MAY_ACCESS,
 					  FILE_CHECK);
 	if (!must_appraise)
 		return;
@@ -699,6 +711,7 @@ void ima_post_path_mknod(struct user_namespace *mnt_userns,
 int ima_read_file(struct file *file, enum kernel_read_file_id read_id,
 		  bool contents)
 {
+	struct ima_namespace *ns = get_current_ns();
 	enum ima_hooks func;
 	u32 secid;
 
@@ -721,7 +734,7 @@ int ima_read_file(struct file *file, enum kernel_read_file_id read_id,
 	/* Read entire file for all partial reads. */
 	func = read_idmap[read_id] ?: FILE_CHECK;
 	security_task_getsecid_subj(current, &secid);
-	return process_measurement(file, current_cred(), secid, NULL,
+	return process_measurement(ns, file, current_cred(), secid, NULL,
 				   0, MAY_READ, func);
 }
 
@@ -749,6 +762,7 @@ const int read_idmap[READING_MAX_ID] = {
 int ima_post_read_file(struct file *file, void *buf, loff_t size,
 		       enum kernel_read_file_id read_id)
 {
+	struct ima_namespace *ns = get_current_ns();
 	enum ima_hooks func;
 	u32 secid;
 
@@ -764,7 +778,7 @@ int ima_post_read_file(struct file *file, void *buf, loff_t size,
 
 	func = read_idmap[read_id] ?: FILE_CHECK;
 	security_task_getsecid_subj(current, &secid);
-	return process_measurement(file, current_cred(), secid, buf, size,
+	return process_measurement(ns, file, current_cred(), secid, buf, size,
 				   MAY_READ, func);
 }
 
@@ -870,7 +884,8 @@ int ima_post_load_data(char *buf, loff_t size,
  * has been written to the passed location but not added to a measurement entry,
  * a negative value otherwise.
  */
-int process_buffer_measurement(struct user_namespace *mnt_userns,
+int process_buffer_measurement(struct ima_namespace *ns,
+			       struct user_namespace *mnt_userns,
 			       struct inode *inode, const void *buf, int size,
 			       const char *eventname, enum ima_hooks func,
 			       int pcr, const char *func_data,
@@ -898,7 +913,7 @@ int process_buffer_measurement(struct user_namespace *mnt_userns,
 	if (digest && digest_len < digest_hash_len)
 		return -EINVAL;
 
-	if (!ima_policy_flag && !digest)
+	if (!ns->ima_policy_flag && !digest)
 		return -ENOENT;
 
 	template = ima_template_desc_buf();
@@ -917,7 +932,7 @@ int process_buffer_measurement(struct user_namespace *mnt_userns,
 	 */
 	if (func) {
 		security_task_getsecid_subj(current, &secid);
-		action = ima_get_action(mnt_userns, inode, current_cred(),
+		action = ima_get_action(ns, mnt_userns, inode, current_cred(),
 					secid, 0, func, &pcr, &template,
 					func_data, NULL);
 		if (!(action & IMA_MEASURE) && !digest)
@@ -954,7 +969,7 @@ int process_buffer_measurement(struct user_namespace *mnt_userns,
 	if (digest)
 		memcpy(digest, iint.ima_hash->digest, digest_hash_len);
 
-	if (!ima_policy_flag || (func && !(action & IMA_MEASURE)))
+	if (!ns->ima_policy_flag || (func && !(action & IMA_MEASURE)))
 		return 1;
 
 	ret = ima_alloc_init_template(&event_data, &entry, template);
@@ -988,6 +1003,7 @@ int process_buffer_measurement(struct user_namespace *mnt_userns,
  */
 void ima_kexec_cmdline(int kernel_fd, const void *buf, int size)
 {
+	struct ima_namespace *ns = get_current_ns();
 	struct fd f;
 
 	if (!buf || !size)
@@ -997,7 +1013,8 @@ void ima_kexec_cmdline(int kernel_fd, const void *buf, int size)
 	if (!f.file)
 		return;
 
-	process_buffer_measurement(file_mnt_user_ns(f.file), file_inode(f.file),
+	process_buffer_measurement(ns,
+				   file_mnt_user_ns(f.file), file_inode(f.file),
 				   buf, size, "kexec-cmdline", KEXEC_CMDLINE, 0,
 				   NULL, false, NULL, 0);
 	fdput(f);
@@ -1027,10 +1044,12 @@ int ima_measure_critical_data(const char *event_label,
 			      const void *buf, size_t buf_len,
 			      bool hash, u8 *digest, size_t digest_len)
 {
+	struct ima_namespace *ns = get_current_ns();
+
 	if (!event_name || !event_label || !buf || !buf_len)
 		return -ENOPARAM;
 
-	return process_buffer_measurement(&init_user_ns, NULL, buf, buf_len,
+	return process_buffer_measurement(ns, &init_user_ns, NULL, buf, buf_len,
 					  event_name, CRITICAL_DATA, 0,
 					  event_label, hash, digest,
 					  digest_len);
@@ -1063,7 +1082,7 @@ static int __init init_ima(void)
 		pr_warn("Couldn't register LSM notifier, error %d\n", error);
 
 	if (!error)
-		ima_update_policy_flags();
+		ima_update_policy_flags(&init_ima_ns);
 
 	return error;
 }
diff --git a/security/integrity/ima/ima_ns.c b/security/integrity/ima/ima_ns.c
index 1ee724bab714..9c628ab2978b 100644
--- a/security/integrity/ima/ima_ns.c
+++ b/security/integrity/ima/ima_ns.c
@@ -57,6 +57,7 @@ int create_ima_ns(struct user_namespace *user_ns)
 static void destroy_ima_ns(struct ima_namespace *ns)
 {
 	pr_debug("DESTROY ima_ns: 0x%p\n", ns);
+	ima_free_policy_rules(ns);
 	free_ns_status_cache(ns);
 	kmem_cache_free(imans_cachep, ns);
 }
diff --git a/security/integrity/ima/ima_policy.c b/security/integrity/ima/ima_policy.c
index e5aef287d14d..4d6d3a39f65e 100644
--- a/security/integrity/ima/ima_policy.c
+++ b/security/integrity/ima/ima_policy.c
@@ -52,7 +52,6 @@
 #define INVALID_PCR(a) (((a) < 0) || \
 	(a) >= (sizeof_field(struct integrity_iint_cache, measured_pcrs) * 8))
 
-int ima_policy_flag;
 static int temp_ima_appraise;
 static int build_ima_appraise __ro_after_init;
 
@@ -233,11 +232,6 @@ static struct ima_rule_entry critical_data_rules[] __ro_after_init = {
 /* An array of architecture specific rules */
 static struct ima_rule_entry *arch_policy_entry __ro_after_init;
 
-static LIST_HEAD(ima_default_rules);
-static LIST_HEAD(ima_policy_rules);
-static LIST_HEAD(ima_temp_rules);
-static struct list_head __rcu *ima_rules = (struct list_head __rcu *)(&ima_default_rules);
-
 static int ima_policy __initdata;
 
 static int __init default_measure_policy_setup(char *str)
@@ -454,12 +448,12 @@ static bool ima_rule_contains_lsm_cond(struct ima_rule_entry *entry)
  * to the old, stale LSM policy.  Update the IMA LSM based rules to reflect
  * the reloaded LSM policy.
  */
-static void ima_lsm_update_rules(void)
+static void ima_lsm_update_rules(struct ima_namespace *ns)
 {
 	struct ima_rule_entry *entry, *e;
 	int result;
 
-	list_for_each_entry_safe(entry, e, &ima_policy_rules, list) {
+	list_for_each_entry_safe(entry, e, &ns->ima_policy_rules, list) {
 		if (!ima_rule_contains_lsm_cond(entry))
 			continue;
 
@@ -474,10 +468,12 @@ static void ima_lsm_update_rules(void)
 int ima_lsm_policy_change(struct notifier_block *nb, unsigned long event,
 			  void *lsm_data)
 {
+	struct ima_namespace *ns = get_current_ns();
+
 	if (event != LSM_POLICY_CHANGE)
 		return NOTIFY_DONE;
 
-	ima_lsm_update_rules();
+	ima_lsm_update_rules(ns);
 	return NOTIFY_OK;
 }
 
@@ -688,7 +684,8 @@ static int get_subaction(struct ima_rule_entry *rule, enum ima_hooks func)
  * list when walking it.  Reads are many orders of magnitude more numerous
  * than writes so ima_match_policy() is classical RCU candidate.
  */
-int ima_match_policy(struct user_namespace *mnt_userns, struct inode *inode,
+int ima_match_policy(struct ima_namespace *ns,
+		     struct user_namespace *mnt_userns, struct inode *inode,
 		     const struct cred *cred, u32 secid, enum ima_hooks func,
 		     int mask, int flags, int *pcr,
 		     struct ima_template_desc **template_desc,
@@ -702,7 +699,7 @@ int ima_match_policy(struct user_namespace *mnt_userns, struct inode *inode,
 		*template_desc = ima_template_desc_current();
 
 	rcu_read_lock();
-	ima_rules_tmp = rcu_dereference(ima_rules);
+	ima_rules_tmp = rcu_dereference(ns->ima_rules);
 	list_for_each_entry_rcu(entry, ima_rules_tmp, list) {
 
 		if (!(entry->action & actmask))
@@ -760,14 +757,14 @@ int ima_match_policy(struct user_namespace *mnt_userns, struct inode *inode,
  *
  * Context: called after a policy update and at system initialization.
  */
-void ima_update_policy_flags(void)
+void ima_update_policy_flags(struct ima_namespace *ns)
 {
 	struct ima_rule_entry *entry;
 	int new_policy_flag = 0;
 	struct list_head *ima_rules_tmp;
 
 	rcu_read_lock();
-	ima_rules_tmp = rcu_dereference(ima_rules);
+	ima_rules_tmp = rcu_dereference(ns->ima_rules);
 	list_for_each_entry_rcu(entry, ima_rules_tmp, list) {
 		/*
 		 * SETXATTR_CHECK rules do not implement a full policy check
@@ -797,7 +794,7 @@ void ima_update_policy_flags(void)
 	if (!ima_appraise)
 		new_policy_flag &= ~IMA_APPRAISE;
 
-	ima_policy_flag = new_policy_flag;
+	ns->ima_policy_flag = new_policy_flag;
 }
 
 static int ima_appraise_flag(enum ima_hooks func)
@@ -813,7 +810,8 @@ static int ima_appraise_flag(enum ima_hooks func)
 	return 0;
 }
 
-static void add_rules(struct ima_rule_entry *entries, int count,
+static void add_rules(struct ima_namespace *ns,
+		      struct ima_rule_entry *entries, int count,
 		      enum policy_rule_list policy_rule)
 {
 	int i = 0;
@@ -822,7 +820,7 @@ static void add_rules(struct ima_rule_entry *entries, int count,
 		struct ima_rule_entry *entry;
 
 		if (policy_rule & IMA_DEFAULT_POLICY)
-			list_add_tail(&entries[i].list, &ima_default_rules);
+			list_add_tail(&entries[i].list, &ns->ima_default_rules);
 
 		if (policy_rule & IMA_CUSTOM_POLICY) {
 			entry = kmemdup(&entries[i], sizeof(*entry),
@@ -830,7 +828,7 @@ static void add_rules(struct ima_rule_entry *entries, int count,
 			if (!entry)
 				continue;
 
-			list_add_tail(&entry->list, &ima_policy_rules);
+			list_add_tail(&entry->list, &ns->ima_policy_rules);
 		}
 		if (entries[i].action == APPRAISE) {
 			if (entries != build_appraise_rules)
@@ -843,9 +841,10 @@ static void add_rules(struct ima_rule_entry *entries, int count,
 	}
 }
 
-static int ima_parse_rule(char *rule, struct ima_rule_entry *entry);
+static int ima_parse_rule(struct ima_namespace *ns,
+			  char *rule, struct ima_rule_entry *entry);
 
-static int __init ima_init_arch_policy(void)
+static int __init ima_init_arch_policy(struct ima_namespace *ns)
 {
 	const char * const *arch_rules;
 	const char * const *rules;
@@ -873,7 +872,7 @@ static int __init ima_init_arch_policy(void)
 		result = strscpy(rule, *rules, sizeof(rule));
 
 		INIT_LIST_HEAD(&arch_policy_entry[i].list);
-		result = ima_parse_rule(rule, &arch_policy_entry[i]);
+		result = ima_parse_rule(ns, rule, &arch_policy_entry[i]);
 		if (result) {
 			pr_warn("Skipping unknown architecture policy rule: %s\n",
 				rule);
@@ -891,23 +890,23 @@ static int __init ima_init_arch_policy(void)
  *
  * ima_rules points to either the ima_default_rules or the new ima_policy_rules.
  */
-void __init ima_init_policy(void)
+void __init ima_init_policy(struct ima_namespace *ns)
 {
 	int build_appraise_entries, arch_entries;
 
 	/* if !ima_policy, we load NO default rules */
 	if (ima_policy)
-		add_rules(dont_measure_rules, ARRAY_SIZE(dont_measure_rules),
+		add_rules(ns, dont_measure_rules, ARRAY_SIZE(dont_measure_rules),
 			  IMA_DEFAULT_POLICY);
 
 	switch (ima_policy) {
 	case ORIGINAL_TCB:
-		add_rules(original_measurement_rules,
+		add_rules(ns, original_measurement_rules,
 			  ARRAY_SIZE(original_measurement_rules),
 			  IMA_DEFAULT_POLICY);
 		break;
 	case DEFAULT_TCB:
-		add_rules(default_measurement_rules,
+		add_rules(ns, default_measurement_rules,
 			  ARRAY_SIZE(default_measurement_rules),
 			  IMA_DEFAULT_POLICY);
 		break;
@@ -921,11 +920,11 @@ void __init ima_init_policy(void)
 	 * and custom policies, prior to other appraise rules.
 	 * (Highest priority)
 	 */
-	arch_entries = ima_init_arch_policy();
+	arch_entries = ima_init_arch_policy(ns);
 	if (!arch_entries)
 		pr_info("No architecture policies found\n");
 	else
-		add_rules(arch_policy_entry, arch_entries,
+		add_rules(ns, arch_policy_entry, arch_entries,
 			  IMA_DEFAULT_POLICY | IMA_CUSTOM_POLICY);
 
 	/*
@@ -933,7 +932,7 @@ void __init ima_init_policy(void)
 	 * signatures, prior to other appraise rules.
 	 */
 	if (ima_use_secure_boot)
-		add_rules(secure_boot_rules, ARRAY_SIZE(secure_boot_rules),
+		add_rules(ns, secure_boot_rules, ARRAY_SIZE(secure_boot_rules),
 			  IMA_DEFAULT_POLICY);
 
 	/*
@@ -945,32 +944,32 @@ void __init ima_init_policy(void)
 	build_appraise_entries = ARRAY_SIZE(build_appraise_rules);
 	if (build_appraise_entries) {
 		if (ima_use_secure_boot)
-			add_rules(build_appraise_rules, build_appraise_entries,
+			add_rules(ns, build_appraise_rules, build_appraise_entries,
 				  IMA_CUSTOM_POLICY);
 		else
-			add_rules(build_appraise_rules, build_appraise_entries,
+			add_rules(ns, build_appraise_rules, build_appraise_entries,
 				  IMA_DEFAULT_POLICY | IMA_CUSTOM_POLICY);
 	}
 
 	if (ima_use_appraise_tcb)
-		add_rules(default_appraise_rules,
+		add_rules(ns, default_appraise_rules,
 			  ARRAY_SIZE(default_appraise_rules),
 			  IMA_DEFAULT_POLICY);
 
 	if (ima_use_critical_data)
-		add_rules(critical_data_rules,
+		add_rules(ns, critical_data_rules,
 			  ARRAY_SIZE(critical_data_rules),
 			  IMA_DEFAULT_POLICY);
 
 	atomic_set(&ima_setxattr_allowed_hash_algorithms, 0);
 
-	ima_update_policy_flags();
+	ima_update_policy_flags(ns);
 }
 
 /* Make sure we have a valid policy, at least containing some rules. */
-int ima_check_policy(void)
+int ima_check_policy(struct ima_namespace *ns)
 {
-	if (list_empty(&ima_temp_rules))
+	if (list_empty(&ns->ima_temp_rules))
 		return -EINVAL;
 	return 0;
 }
@@ -988,14 +987,14 @@ int ima_check_policy(void)
  */
 void ima_update_policy(struct ima_namespace *ns)
 {
-	struct list_head *policy = &ima_policy_rules;
+	struct list_head *policy = &ns->ima_policy_rules;
 
-	list_splice_tail_init_rcu(&ima_temp_rules, policy, synchronize_rcu);
+	list_splice_tail_init_rcu(&ns->ima_temp_rules, policy, synchronize_rcu);
 
-	if (ima_rules != (struct list_head __rcu *)policy) {
-		ima_policy_flag = 0;
+	if (ns->ima_rules != (struct list_head __rcu *)policy) {
+		ns->ima_policy_flag = 0;
 
-		rcu_assign_pointer(ima_rules, policy);
+		rcu_assign_pointer(ns->ima_rules, policy);
 		/*
 		 * IMA architecture specific policy rules are specified
 		 * as strings and converted to an array of ima_entry_rules
@@ -1004,7 +1003,7 @@ void ima_update_policy(struct ima_namespace *ns)
 		 */
 		kfree(arch_policy_entry);
 	}
-	ima_update_policy_flags();
+	ima_update_policy_flags(ns);
 
 	/* Custom IMA policy has been loaded */
 	ima_process_queued_keys(ns);
@@ -1077,7 +1076,8 @@ static const match_table_t policy_tokens = {
 	{Opt_err, NULL}
 };
 
-static int ima_lsm_rule_init(struct ima_rule_entry *entry,
+static int ima_lsm_rule_init(struct ima_namespace *ns,
+			     struct ima_rule_entry *entry,
 			     substring_t *args, int lsm_rule, int audit_type)
 {
 	int result;
@@ -1097,7 +1097,7 @@ static int ima_lsm_rule_init(struct ima_rule_entry *entry,
 		pr_warn("rule for LSM \'%s\' is undefined\n",
 			entry->lsm[lsm_rule].args_p);
 
-		if (ima_rules == (struct list_head __rcu *)(&ima_default_rules)) {
+		if (ns->ima_rules == (struct list_head __rcu *)(&ns->ima_default_rules)) {
 			kfree(entry->lsm[lsm_rule].args_p);
 			entry->lsm[lsm_rule].args_p = NULL;
 			result = -EINVAL;
@@ -1324,7 +1324,8 @@ static unsigned int ima_parse_appraise_algos(char *arg)
 	return res;
 }
 
-static int ima_parse_rule(char *rule, struct ima_rule_entry *entry)
+static int ima_parse_rule(struct ima_namespace *ns,
+			  char *rule, struct ima_rule_entry *entry)
 {
 	struct audit_buffer *ab;
 	char *from;
@@ -1674,37 +1675,37 @@ static int ima_parse_rule(char *rule, struct ima_rule_entry *entry)
 			break;
 		case Opt_obj_user:
 			ima_log_string(ab, "obj_user", args[0].from);
-			result = ima_lsm_rule_init(entry, args,
+			result = ima_lsm_rule_init(ns, entry, args,
 						   LSM_OBJ_USER,
 						   AUDIT_OBJ_USER);
 			break;
 		case Opt_obj_role:
 			ima_log_string(ab, "obj_role", args[0].from);
-			result = ima_lsm_rule_init(entry, args,
+			result = ima_lsm_rule_init(ns, entry, args,
 						   LSM_OBJ_ROLE,
 						   AUDIT_OBJ_ROLE);
 			break;
 		case Opt_obj_type:
 			ima_log_string(ab, "obj_type", args[0].from);
-			result = ima_lsm_rule_init(entry, args,
+			result = ima_lsm_rule_init(ns, entry, args,
 						   LSM_OBJ_TYPE,
 						   AUDIT_OBJ_TYPE);
 			break;
 		case Opt_subj_user:
 			ima_log_string(ab, "subj_user", args[0].from);
-			result = ima_lsm_rule_init(entry, args,
+			result = ima_lsm_rule_init(ns, entry, args,
 						   LSM_SUBJ_USER,
 						   AUDIT_SUBJ_USER);
 			break;
 		case Opt_subj_role:
 			ima_log_string(ab, "subj_role", args[0].from);
-			result = ima_lsm_rule_init(entry, args,
+			result = ima_lsm_rule_init(ns, entry, args,
 						   LSM_SUBJ_ROLE,
 						   AUDIT_SUBJ_ROLE);
 			break;
 		case Opt_subj_type:
 			ima_log_string(ab, "subj_type", args[0].from);
-			result = ima_lsm_rule_init(entry, args,
+			result = ima_lsm_rule_init(ns, entry, args,
 						   LSM_SUBJ_TYPE,
 						   AUDIT_SUBJ_TYPE);
 			break;
@@ -1810,7 +1811,7 @@ static int ima_parse_rule(char *rule, struct ima_rule_entry *entry)
  * Avoid locking by allowing just one writer at a time in ima_write_policy()
  * Returns the length of the rule parsed, an error code on failure
  */
-ssize_t ima_parse_add_rule(char *rule)
+ssize_t ima_parse_add_rule(struct ima_namespace *ns, char *rule)
 {
 	static const char op[] = "update_policy";
 	char *p;
@@ -1834,7 +1835,7 @@ ssize_t ima_parse_add_rule(char *rule)
 
 	INIT_LIST_HEAD(&entry->list);
 
-	result = ima_parse_rule(p, entry);
+	result = ima_parse_rule(ns, p, entry);
 	if (result) {
 		ima_free_rule(entry);
 		integrity_audit_msg(AUDIT_INTEGRITY_STATUS, NULL,
@@ -1843,7 +1844,7 @@ ssize_t ima_parse_add_rule(char *rule)
 		return result;
 	}
 
-	list_add_tail(&entry->list, &ima_temp_rules);
+	list_add_tail(&entry->list, &ns->ima_temp_rules);
 
 	return len;
 }
@@ -1854,12 +1855,24 @@ ssize_t ima_parse_add_rule(char *rule)
  * different from the active one.  There is also only one user of
  * ima_delete_rules() at a time.
  */
-void ima_delete_rules(void)
+void ima_delete_rules(struct ima_namespace *ns)
 {
 	struct ima_rule_entry *entry, *tmp;
 
 	temp_ima_appraise = 0;
-	list_for_each_entry_safe(entry, tmp, &ima_temp_rules, list) {
+	list_for_each_entry_safe(entry, tmp, &ns->ima_temp_rules, list) {
+		list_del(&entry->list);
+		ima_free_rule(entry);
+	}
+}
+
+void ima_free_policy_rules(struct ima_namespace *ns)
+{
+	struct ima_rule_entry *entry, *tmp;
+
+	ima_delete_rules(ns);
+
+	list_for_each_entry_safe(entry, tmp, &ns->ima_policy_rules, list) {
 		list_del(&entry->list);
 		ima_free_rule(entry);
 	}
@@ -1885,12 +1898,13 @@ static const char *const mask_tokens[] = {
 
 void *ima_policy_start(struct seq_file *m, loff_t *pos)
 {
+	struct ima_namespace *ns = get_current_ns();
 	loff_t l = *pos;
 	struct ima_rule_entry *entry;
 	struct list_head *ima_rules_tmp;
 
 	rcu_read_lock();
-	ima_rules_tmp = rcu_dereference(ima_rules);
+	ima_rules_tmp = rcu_dereference(ns->ima_rules);
 	list_for_each_entry_rcu(entry, ima_rules_tmp, list) {
 		if (!l--) {
 			rcu_read_unlock();
@@ -1904,14 +1918,15 @@ void *ima_policy_start(struct seq_file *m, loff_t *pos)
 void *ima_policy_next(struct seq_file *m, void *v, loff_t *pos)
 {
 	struct ima_rule_entry *entry = v;
+	struct ima_namespace *ns = get_current_ns();
 
 	rcu_read_lock();
 	entry = list_entry_rcu(entry->list.next, struct ima_rule_entry, list);
 	rcu_read_unlock();
 	(*pos)++;
 
-	return (&entry->list == &ima_default_rules ||
-		&entry->list == &ima_policy_rules) ? NULL : entry;
+	return (&entry->list == &ns->ima_default_rules ||
+		&entry->list == &ns->ima_policy_rules) ? NULL : entry;
 }
 
 void ima_policy_stop(struct seq_file *m, void *v)
@@ -2166,6 +2181,7 @@ int ima_policy_show(struct seq_file *m, void *v)
  */
 bool ima_appraise_signature(enum kernel_read_file_id id)
 {
+	struct ima_namespace *ns = get_current_ns();
 	struct ima_rule_entry *entry;
 	bool found = false;
 	enum ima_hooks func;
@@ -2177,7 +2193,7 @@ bool ima_appraise_signature(enum kernel_read_file_id id)
 	func = read_idmap[id] ?: FILE_CHECK;
 
 	rcu_read_lock();
-	ima_rules_tmp = rcu_dereference(ima_rules);
+	ima_rules_tmp = rcu_dereference(ns->ima_rules);
 	list_for_each_entry_rcu(entry, ima_rules_tmp, list) {
 		if (entry->action != APPRAISE)
 			continue;
diff --git a/security/integrity/ima/ima_queue_keys.c b/security/integrity/ima/ima_queue_keys.c
index 4c69178b1ffe..a6eb802e5ae4 100644
--- a/security/integrity/ima/ima_queue_keys.c
+++ b/security/integrity/ima/ima_queue_keys.c
@@ -150,7 +150,7 @@ void ima_process_queued_keys(struct ima_namespace *ns)
 
 	list_for_each_entry_safe(entry, tmp, &ns->ima_keys, list) {
 		if (!ns->timer_expired)
-			process_buffer_measurement(&init_user_ns, NULL,
+			process_buffer_measurement(ns, &init_user_ns, NULL,
 						   entry->payload,
 						   entry->payload_len,
 						   entry->keyring_name,
-- 
2.31.1


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

* [PATCH v5 07/16] ima: Move ima_htable into ima_namespace
  2021-12-08 22:18 [PATCH v5 00/16] ima: Namespace IMA with audit support in IMA-ns Stefan Berger
                   ` (5 preceding siblings ...)
  2021-12-08 22:18 ` [PATCH v5 06/16] ima: Move policy " Stefan Berger
@ 2021-12-08 22:18 ` Stefan Berger
  2021-12-08 22:18 ` [PATCH v5 08/16] ima: Move measurement list related variables " Stefan Berger
                   ` (8 subsequent siblings)
  15 siblings, 0 replies; 63+ messages in thread
From: Stefan Berger @ 2021-12-08 22:18 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 ima_htable into ima_namespace. This way a front-end like
SecurityFS can show the number of violations of an IMA namespace.

Move ima_hash_key() into ima_queue.c since it's only used there.

Signed-off-by: Stefan Berger <stefanb@linux.ibm.com>
---
 include/linux/ima.h                      | 11 +++++++
 security/integrity/ima/ima.h             | 34 +++++++------------
 security/integrity/ima/ima_api.c         | 17 ++++++----
 security/integrity/ima/ima_fs.c          |  7 ++--
 security/integrity/ima/ima_init.c        |  6 ++--
 security/integrity/ima/ima_init_ima_ns.c |  4 +++
 security/integrity/ima/ima_main.c        | 13 ++++----
 security/integrity/ima/ima_queue.c       | 42 ++++++++++++++----------
 security/integrity/ima/ima_template.c    |  4 +--
 9 files changed, 78 insertions(+), 60 deletions(-)

diff --git a/include/linux/ima.h b/include/linux/ima.h
index 9971ee09b7ee..080edecc0ea1 100644
--- a/include/linux/ima.h
+++ b/include/linux/ima.h
@@ -211,6 +211,15 @@ static inline int ima_inode_removexattr(struct dentry *dentry,
 }
 #endif /* CONFIG_IMA_APPRAISE */
 
+#define IMA_HASH_BITS 10
+#define IMA_MEASURE_HTABLE_SIZE (1 << IMA_HASH_BITS)
+
+struct ima_h_table {
+	atomic_long_t len;	/* number of stored measurements in the list */
+	atomic_long_t violations;
+	struct hlist_head queue[IMA_MEASURE_HTABLE_SIZE];
+};
+
 struct ima_namespace {
 	struct kref kref;
 	struct user_namespace *user_ns;
@@ -248,6 +257,8 @@ struct ima_namespace {
 	struct list_head __rcu *ima_rules;
 	/* current content of the policy */
 	int ima_policy_flag;
+
+	struct ima_h_table ima_htable;
 };
 
 extern struct ima_namespace init_ima_ns;
diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h
index e295141f2478..a7e6c8fb152a 100644
--- a/security/integrity/ima/ima.h
+++ b/security/integrity/ima/ima.h
@@ -32,9 +32,6 @@ enum tpm_pcrs { TPM_PCR0 = 0, TPM_PCR8 = 8, TPM_PCR10 = 10 };
 #define IMA_DIGEST_SIZE		SHA1_DIGEST_SIZE
 #define IMA_EVENT_NAME_LEN_MAX	255
 
-#define IMA_HASH_BITS 10
-#define IMA_MEASURE_HTABLE_SIZE (1 << IMA_HASH_BITS)
-
 #define IMA_TEMPLATE_FIELD_ID_MAX_LEN	16
 #define IMA_TEMPLATE_NUM_FIELDS_MAX	15
 
@@ -143,7 +140,8 @@ struct ns_status {
 /* Internal IMA function definitions */
 int ima_init(void);
 int ima_fs_init(void);
-int ima_add_template_entry(struct ima_template_entry *entry, int violation,
+int ima_add_template_entry(struct ima_namespace *ns,
+			   struct ima_template_entry *entry, int violation,
 			   const char *op, struct inode *inode,
 			   const unsigned char *filename);
 int ima_calc_file_hash(struct file *file, struct ima_digest_data *hash);
@@ -152,7 +150,8 @@ int ima_calc_buffer_hash(const void *buf, loff_t len,
 int ima_calc_field_array_hash(struct ima_field_data *field_data,
 			      struct ima_template_entry *entry);
 int ima_calc_boot_aggregate(struct ima_digest_data *hash);
-void ima_add_violation(struct file *file, const unsigned char *filename,
+void ima_add_violation(struct ima_namespace *ns,
+		       struct file *file, const unsigned char *filename,
 		       struct integrity_iint_cache *iint,
 		       const char *op, const char *cause);
 int ima_init_crypto(void);
@@ -165,8 +164,10 @@ struct ima_template_desc *ima_template_desc_current(void);
 struct ima_template_desc *ima_template_desc_buf(void);
 struct ima_template_desc *lookup_template_desc(const char *name);
 bool ima_template_has_modsig(const struct ima_template_desc *ima_template);
-int ima_restore_measurement_entry(struct ima_template_entry *entry);
-int ima_restore_measurement_list(loff_t bufsize, void *buf);
+int ima_restore_measurement_entry(struct ima_namespace *ns,
+				  struct ima_template_entry *entry);
+int ima_restore_measurement_list(struct ima_namespace *ns,
+				 loff_t bufsize, void *buf);
 int ima_measurements_show(struct seq_file *m, void *v);
 unsigned long ima_get_binary_runtime_size(void);
 int ima_init_template(void);
@@ -180,19 +181,6 @@ int ima_lsm_policy_change(struct notifier_block *nb, unsigned long event,
  */
 extern spinlock_t ima_queue_lock;
 
-struct ima_h_table {
-	atomic_long_t len;	/* number of stored measurements in the list */
-	atomic_long_t violations;
-	struct hlist_head queue[IMA_MEASURE_HTABLE_SIZE];
-};
-extern struct ima_h_table ima_htable;
-
-static inline unsigned int ima_hash_key(u8 *digest)
-{
-	/* there is no point in taking a hash of part of a digest */
-	return (digest[0] | digest[1] << 8) % IMA_MEASURE_HTABLE_SIZE;
-}
-
 #define __ima_hooks(hook)				\
 	hook(NONE, none)				\
 	hook(FILE_CHECK, file)				\
@@ -272,7 +260,8 @@ int ima_must_measure(struct inode *inode, int mask, enum ima_hooks func);
 int ima_collect_measurement(struct integrity_iint_cache *iint,
 			    struct file *file, void *buf, loff_t size,
 			    enum hash_algo algo, struct modsig *modsig);
-void ima_store_measurement(struct integrity_iint_cache *iint, struct file *file,
+void ima_store_measurement(struct ima_namespace *ns,
+			   struct integrity_iint_cache *iint, struct file *file,
 			   const unsigned char *filename,
 			   struct evm_ima_xattr_data *xattr_value,
 			   int xattr_len, const struct modsig *modsig, int pcr,
@@ -289,7 +278,8 @@ void ima_audit_measurement(struct integrity_iint_cache *iint,
 int ima_alloc_init_template(struct ima_event_data *event_data,
 			    struct ima_template_entry **entry,
 			    struct ima_template_desc *template_desc);
-int ima_store_template(struct ima_template_entry *entry, int violation,
+int ima_store_template(struct ima_namespace *ns,
+		       struct ima_template_entry *entry, int violation,
 		       struct inode *inode,
 		       const unsigned char *filename, int pcr);
 void ima_free_template_entry(struct ima_template_entry *entry);
diff --git a/security/integrity/ima/ima_api.c b/security/integrity/ima/ima_api.c
index 808aec56dbb6..71c5517fe8bc 100644
--- a/security/integrity/ima/ima_api.c
+++ b/security/integrity/ima/ima_api.c
@@ -100,7 +100,8 @@ int ima_alloc_init_template(struct ima_event_data *event_data,
  *
  * Returns 0 on success, error code otherwise
  */
-int ima_store_template(struct ima_template_entry *entry,
+int ima_store_template(struct ima_namespace *ns,
+		       struct ima_template_entry *entry,
 		       int violation, struct inode *inode,
 		       const unsigned char *filename, int pcr)
 {
@@ -120,7 +121,7 @@ int ima_store_template(struct ima_template_entry *entry,
 		}
 	}
 	entry->pcr = pcr;
-	result = ima_add_template_entry(entry, violation, op, inode, filename);
+	result = ima_add_template_entry(ns, entry, violation, op, inode, filename);
 	return result;
 }
 
@@ -131,7 +132,8 @@ int ima_store_template(struct ima_template_entry *entry,
  * By extending the PCR with 0xFF's instead of with zeroes, the PCR
  * value is invalidated.
  */
-void ima_add_violation(struct file *file, const unsigned char *filename,
+void ima_add_violation(struct ima_namespace *ns,
+		       struct file *file, const unsigned char *filename,
 		       struct integrity_iint_cache *iint,
 		       const char *op, const char *cause)
 {
@@ -145,14 +147,14 @@ void ima_add_violation(struct file *file, const unsigned char *filename,
 	int result;
 
 	/* can overflow, only indicator */
-	atomic_long_inc(&ima_htable.violations);
+	atomic_long_inc(&ns->ima_htable.violations);
 
 	result = ima_alloc_init_template(&event_data, &entry, NULL);
 	if (result < 0) {
 		result = -ENOMEM;
 		goto err_out;
 	}
-	result = ima_store_template(entry, violation, inode,
+	result = ima_store_template(ns, entry, violation, inode,
 				    filename, CONFIG_IMA_MEASURE_PCR_IDX);
 	if (result < 0)
 		ima_free_template_entry(entry);
@@ -299,7 +301,8 @@ int ima_collect_measurement(struct integrity_iint_cache *iint,
  *
  * Must be called with iint->mutex held.
  */
-void ima_store_measurement(struct integrity_iint_cache *iint,
+void ima_store_measurement(struct ima_namespace *ns,
+			   struct integrity_iint_cache *iint,
 			   struct file *file, const unsigned char *filename,
 			   struct evm_ima_xattr_data *xattr_value,
 			   int xattr_len, const struct modsig *modsig, int pcr,
@@ -334,7 +337,7 @@ void ima_store_measurement(struct integrity_iint_cache *iint,
 		return;
 	}
 
-	result = ima_store_template(entry, violation, inode, filename, pcr);
+	result = ima_store_template(ns, entry, violation, inode, filename, pcr);
 	if ((!result || result == -EEXIST) && !(file->f_flags & O_DIRECT)) {
 		iint->flags |= IMA_MEASURED;
 		iint->measured_pcrs |= (0x1 << pcr);
diff --git a/security/integrity/ima/ima_fs.c b/security/integrity/ima/ima_fs.c
index 25f85c58ebef..e37c973a94c6 100644
--- a/security/integrity/ima/ima_fs.c
+++ b/security/integrity/ima/ima_fs.c
@@ -53,7 +53,9 @@ static ssize_t ima_show_htable_violations(struct file *filp,
 					  char __user *buf,
 					  size_t count, loff_t *ppos)
 {
-	return ima_show_htable_value(buf, count, ppos, &ima_htable.violations);
+	struct ima_namespace *ns = get_current_ns();
+
+	return ima_show_htable_value(buf, count, ppos, &ns->ima_htable.violations);
 }
 
 static const struct file_operations ima_htable_violations_ops = {
@@ -65,8 +67,9 @@ static ssize_t ima_show_measurements_count(struct file *filp,
 					   char __user *buf,
 					   size_t count, loff_t *ppos)
 {
-	return ima_show_htable_value(buf, count, ppos, &ima_htable.len);
+	struct ima_namespace *ns = get_current_ns();
 
+	return ima_show_htable_value(buf, count, ppos, &ns->ima_htable.len);
 }
 
 static const struct file_operations ima_measurements_count_ops = {
diff --git a/security/integrity/ima/ima_init.c b/security/integrity/ima/ima_init.c
index d78f8faf6dcd..8e0ae3a4f04a 100644
--- a/security/integrity/ima/ima_init.c
+++ b/security/integrity/ima/ima_init.c
@@ -39,7 +39,7 @@ struct tpm_chip *ima_tpm_chip;
  * a different value.) Violations add a zero entry to the measurement
  * list and extend the aggregate PCR value with ff...ff's.
  */
-static int __init ima_add_boot_aggregate(void)
+static int __init ima_add_boot_aggregate(struct ima_namespace *ns)
 {
 	static const char op[] = "add_boot_aggregate";
 	const char *audit_cause = "ENOMEM";
@@ -86,7 +86,7 @@ static int __init ima_add_boot_aggregate(void)
 		goto err_out;
 	}
 
-	result = ima_store_template(entry, violation, NULL,
+	result = ima_store_template(ns, entry, violation, NULL,
 				    boot_aggregate_name,
 				    CONFIG_IMA_MEASURE_PCR_IDX);
 	if (result < 0) {
@@ -145,7 +145,7 @@ int __init ima_init(void)
 	rc = ima_init_digests();
 	if (rc != 0)
 		return rc;
-	rc = ima_add_boot_aggregate();	/* boot aggregate must be first entry */
+	rc = ima_add_boot_aggregate(&init_ima_ns);	/* boot aggregate must be first entry */
 	if (rc != 0)
 		return rc;
 
diff --git a/security/integrity/ima/ima_init_ima_ns.c b/security/integrity/ima/ima_init_ima_ns.c
index f32cd9f22eff..32ff7e271024 100644
--- a/security/integrity/ima/ima_init_ima_ns.c
+++ b/security/integrity/ima/ima_init_ima_ns.c
@@ -40,6 +40,10 @@ int ima_init_namespace(struct ima_namespace *ns)
 	ns->ima_rules = (struct list_head __rcu *)(&ns->ima_default_rules);
 	ns->ima_policy_flag = 0;
 
+	atomic_long_set(&ns->ima_htable.len, 0);
+	atomic_long_set(&ns->ima_htable.violations, 0);
+	memset(&ns->ima_htable.queue, 0, sizeof(ns->ima_htable.queue));
+
 	return 0;
 }
 
diff --git a/security/integrity/ima/ima_main.c b/security/integrity/ima/ima_main.c
index 897304046ba4..2121a831f38a 100644
--- a/security/integrity/ima/ima_main.c
+++ b/security/integrity/ima/ima_main.c
@@ -112,7 +112,8 @@ static int mmap_violation_check(enum ima_hooks func, struct file *file,
  *	  could result in a file measurement error.
  *
  */
-static void ima_rdwr_violation_check(struct file *file,
+static void ima_rdwr_violation_check(struct ima_namespace *ns,
+				     struct file *file,
 				     struct integrity_iint_cache *iint,
 				     int must_measure,
 				     char **pathbuf,
@@ -145,10 +146,10 @@ static void ima_rdwr_violation_check(struct file *file,
 	*pathname = ima_d_path(&file->f_path, pathbuf, filename);
 
 	if (send_tomtou)
-		ima_add_violation(file, *pathname, iint,
+		ima_add_violation(ns, file, *pathname, iint,
 				  "invalid_pcr", "ToMToU");
 	if (send_writers)
-		ima_add_violation(file, *pathname, iint,
+		ima_add_violation(ns, file, *pathname, iint,
 				  "invalid_pcr", "open_writers");
 }
 
@@ -257,7 +258,7 @@ static int process_measurement(struct ima_namespace *ns,
 	}
 
 	if (!rc && violation_check)
-		ima_rdwr_violation_check(file, iint, action & IMA_MEASURE,
+		ima_rdwr_violation_check(ns, file, iint, action & IMA_MEASURE,
 					 &pathbuf, &pathname, filename);
 
 	inode_unlock(inode);
@@ -354,7 +355,7 @@ static int process_measurement(struct ima_namespace *ns,
 		pathname = ima_d_path(&file->f_path, &pathbuf, filename);
 
 	if (action & IMA_MEASURE)
-		ima_store_measurement(iint, file, pathname,
+		ima_store_measurement(ns, iint, file, pathname,
 				      xattr_value, xattr_len, modsig, pcr,
 				      template_desc);
 	if (rc == 0 && (action & IMA_APPRAISE_SUBMASK)) {
@@ -978,7 +979,7 @@ int process_buffer_measurement(struct ima_namespace *ns,
 		goto out;
 	}
 
-	ret = ima_store_template(entry, violation, NULL, event_data.buf, pcr);
+	ret = ima_store_template(ns, entry, violation, NULL, event_data.buf, pcr);
 	if (ret < 0) {
 		audit_cause = "store_entry";
 		ima_free_template_entry(entry);
diff --git a/security/integrity/ima/ima_queue.c b/security/integrity/ima/ima_queue.c
index 532da87ce519..373154039b91 100644
--- a/security/integrity/ima/ima_queue.c
+++ b/security/integrity/ima/ima_queue.c
@@ -17,6 +17,7 @@
 
 #include <linux/rculist.h>
 #include <linux/slab.h>
+#include <linux/ima.h>
 #include "ima.h"
 
 #define AUDIT_CAUSE_LEN_MAX 32
@@ -31,21 +32,22 @@ static unsigned long binary_runtime_size;
 static unsigned long binary_runtime_size = ULONG_MAX;
 #endif
 
-/* key: inode (before secure-hashing a file) */
-struct ima_h_table ima_htable = {
-	.len = ATOMIC_LONG_INIT(0),
-	.violations = ATOMIC_LONG_INIT(0),
-	.queue[0 ... IMA_MEASURE_HTABLE_SIZE - 1] = HLIST_HEAD_INIT
-};
-
 /* mutex protects atomicity of extending measurement list
  * and extending the TPM PCR aggregate. Since tpm_extend can take
  * long (and the tpm driver uses a mutex), we can't use the spinlock.
  */
 static DEFINE_MUTEX(ima_extend_list_mutex);
 
+
+static inline unsigned int ima_hash_key(u8 *digest)
+{
+	/* there is no point in taking a hash of part of a digest */
+	return (digest[0] | digest[1] << 8) % IMA_MEASURE_HTABLE_SIZE;
+}
+
 /* lookup up the digest value in the hash table, and return the entry */
-static struct ima_queue_entry *ima_lookup_digest_entry(u8 *digest_value,
+static struct ima_queue_entry *ima_lookup_digest_entry(struct ima_namespace *ns,
+						       u8 *digest_value,
 						       int pcr)
 {
 	struct ima_queue_entry *qe, *ret = NULL;
@@ -54,7 +56,7 @@ static struct ima_queue_entry *ima_lookup_digest_entry(u8 *digest_value,
 
 	key = ima_hash_key(digest_value);
 	rcu_read_lock();
-	hlist_for_each_entry_rcu(qe, &ima_htable.queue[key], hnext) {
+	hlist_for_each_entry_rcu(qe, &ns->ima_htable.queue[key], hnext) {
 		rc = memcmp(qe->entry->digests[ima_hash_algo_idx].digest,
 			    digest_value, hash_digest_size[ima_hash_algo]);
 		if ((rc == 0) && (qe->entry->pcr == pcr)) {
@@ -90,7 +92,8 @@ static int get_binary_runtime_size(struct ima_template_entry *entry)
  *
  * (Called with ima_extend_list_mutex held.)
  */
-static int ima_add_digest_entry(struct ima_template_entry *entry,
+static int ima_add_digest_entry(struct ima_namespace *ns,
+				struct ima_template_entry *entry,
 				bool update_htable)
 {
 	struct ima_queue_entry *qe;
@@ -106,11 +109,12 @@ static int ima_add_digest_entry(struct ima_template_entry *entry,
 	INIT_LIST_HEAD(&qe->later);
 	list_add_tail_rcu(&qe->later, &ima_measurements);
 
-	atomic_long_inc(&ima_htable.len);
+	atomic_long_inc(&ns->ima_htable.len);
 	if (update_htable) {
 		key = ima_hash_key(entry->digests[ima_hash_algo_idx].digest);
-		hlist_add_head_rcu(&qe->hnext, &ima_htable.queue[key]);
-	}
+		hlist_add_head_rcu(&qe->hnext, &ns->ima_htable.queue[key]);
+	} else
+		INIT_HLIST_NODE(&qe->hnext);
 
 	if (binary_runtime_size != ULONG_MAX) {
 		int size;
@@ -156,7 +160,8 @@ static int ima_pcr_extend(struct tpm_digest *digests_arg, int pcr)
  * kexec, maintain the total memory size required for serializing the
  * binary_runtime_measurements.
  */
-int ima_add_template_entry(struct ima_template_entry *entry, int violation,
+int ima_add_template_entry(struct ima_namespace *ns,
+			   struct ima_template_entry *entry, int violation,
 			   const char *op, struct inode *inode,
 			   const unsigned char *filename)
 {
@@ -169,14 +174,14 @@ int ima_add_template_entry(struct ima_template_entry *entry, int violation,
 
 	mutex_lock(&ima_extend_list_mutex);
 	if (!violation && !IS_ENABLED(CONFIG_IMA_DISABLE_HTABLE)) {
-		if (ima_lookup_digest_entry(digest, entry->pcr)) {
+		if (ima_lookup_digest_entry(ns, digest, entry->pcr)) {
 			audit_cause = "hash_exists";
 			result = -EEXIST;
 			goto out;
 		}
 	}
 
-	result = ima_add_digest_entry(entry,
+	result = ima_add_digest_entry(ns, entry,
 				      !IS_ENABLED(CONFIG_IMA_DISABLE_HTABLE));
 	if (result < 0) {
 		audit_cause = "ENOMEM";
@@ -201,12 +206,13 @@ int ima_add_template_entry(struct ima_template_entry *entry, int violation,
 	return result;
 }
 
-int ima_restore_measurement_entry(struct ima_template_entry *entry)
+int ima_restore_measurement_entry(struct ima_namespace *ns,
+				  struct ima_template_entry *entry)
 {
 	int result = 0;
 
 	mutex_lock(&ima_extend_list_mutex);
-	result = ima_add_digest_entry(entry, 0);
+	result = ima_add_digest_entry(ns, entry, 0);
 	mutex_unlock(&ima_extend_list_mutex);
 	return result;
 }
diff --git a/security/integrity/ima/ima_template.c b/security/integrity/ima/ima_template.c
index 694560396be0..2ae87eb23a59 100644
--- a/security/integrity/ima/ima_template.c
+++ b/security/integrity/ima/ima_template.c
@@ -400,7 +400,7 @@ static int ima_restore_template_data(struct ima_template_desc *template_desc,
 }
 
 /* Restore the serialized binary measurement list without extending PCRs. */
-int ima_restore_measurement_list(loff_t size, void *buf)
+int ima_restore_measurement_list(struct ima_namespace *ns, loff_t size, void *buf)
 {
 	char template_name[MAX_TEMPLATE_NAME_LEN];
 	unsigned char zero[TPM_DIGEST_SIZE] = { 0 };
@@ -516,7 +516,7 @@ int ima_restore_measurement_list(loff_t size, void *buf)
 
 		entry->pcr = !ima_canonical_fmt ? *(u32 *)(hdr[HDR_PCR].data) :
 			     le32_to_cpu(*(__le32 *)(hdr[HDR_PCR].data));
-		ret = ima_restore_measurement_entry(entry);
+		ret = ima_restore_measurement_entry(ns, entry);
 		if (ret < 0)
 			break;
 
-- 
2.31.1


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

* [PATCH v5 08/16] ima: Move measurement list related variables into ima_namespace
  2021-12-08 22:18 [PATCH v5 00/16] ima: Namespace IMA with audit support in IMA-ns Stefan Berger
                   ` (6 preceding siblings ...)
  2021-12-08 22:18 ` [PATCH v5 07/16] ima: Move ima_htable " Stefan Berger
@ 2021-12-08 22:18 ` Stefan Berger
  2021-12-08 22:18 ` [PATCH v5 09/16] ima: Only accept AUDIT rules for IMA non-init_ima_ns namespaces for now Stefan Berger
                   ` (7 subsequent siblings)
  15 siblings, 0 replies; 63+ messages in thread
From: Stefan Berger @ 2021-12-08 22:18 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.

Implement ima_free_measurements() to free a list of measurements
and call it when an IMA namespace is deleted.

Signed-off-by: Stefan Berger <stefanb@linux.ibm.com>
---
 include/linux/ima.h                      |  2 ++
 security/integrity/ima/ima.h             |  4 +--
 security/integrity/ima/ima_fs.c          |  6 +++--
 security/integrity/ima/ima_init_ima_ns.c |  5 ++++
 security/integrity/ima/ima_ns.c          |  1 +
 security/integrity/ima/ima_queue.c       | 33 ++++++++++++++----------
 6 files changed, 33 insertions(+), 18 deletions(-)

diff --git a/include/linux/ima.h b/include/linux/ima.h
index 080edecc0ea1..2ce801bfc449 100644
--- a/include/linux/ima.h
+++ b/include/linux/ima.h
@@ -259,6 +259,8 @@ struct ima_namespace {
 	int ima_policy_flag;
 
 	struct ima_h_table ima_htable;
+	struct list_head ima_measurements;
+	unsigned long binary_runtime_size;
 };
 
 extern struct ima_namespace init_ima_ns;
diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h
index a7e6c8fb152a..bb9763cd5fb1 100644
--- a/security/integrity/ima/ima.h
+++ b/security/integrity/ima/ima.h
@@ -104,7 +104,6 @@ struct ima_queue_entry {
 	struct list_head later;		/* place in ima_measurements list */
 	struct ima_template_entry *entry;
 };
-extern struct list_head ima_measurements;	/* list of all measurements */
 
 /* Some details preceding the binary serialized measurement list */
 struct ima_kexec_hdr {
@@ -168,8 +167,9 @@ int ima_restore_measurement_entry(struct ima_namespace *ns,
 				  struct ima_template_entry *entry);
 int ima_restore_measurement_list(struct ima_namespace *ns,
 				 loff_t bufsize, void *buf);
+void ima_free_measurements(struct ima_namespace *ns);
 int ima_measurements_show(struct seq_file *m, void *v);
-unsigned long ima_get_binary_runtime_size(void);
+unsigned long ima_get_binary_runtime_size(struct ima_namespace *ns);
 int ima_init_template(void);
 void ima_init_template_list(void);
 int __init ima_init_digests(void);
diff --git a/security/integrity/ima/ima_fs.c b/security/integrity/ima/ima_fs.c
index e37c973a94c6..38b1c26479b3 100644
--- a/security/integrity/ima/ima_fs.c
+++ b/security/integrity/ima/ima_fs.c
@@ -80,12 +80,13 @@ static const struct file_operations ima_measurements_count_ops = {
 /* returns pointer to hlist_node */
 static void *ima_measurements_start(struct seq_file *m, loff_t *pos)
 {
+	struct ima_namespace *ns = get_current_ns();
 	loff_t l = *pos;
 	struct ima_queue_entry *qe;
 
 	/* we need a lock since pos could point beyond last element */
 	rcu_read_lock();
-	list_for_each_entry_rcu(qe, &ima_measurements, later) {
+	list_for_each_entry_rcu(qe, &ns->ima_measurements, later) {
 		if (!l--) {
 			rcu_read_unlock();
 			return qe;
@@ -97,6 +98,7 @@ static void *ima_measurements_start(struct seq_file *m, loff_t *pos)
 
 static void *ima_measurements_next(struct seq_file *m, void *v, loff_t *pos)
 {
+	struct ima_namespace *ns = get_current_ns();
 	struct ima_queue_entry *qe = v;
 
 	/* lock protects when reading beyond last element
@@ -107,7 +109,7 @@ static void *ima_measurements_next(struct seq_file *m, void *v, loff_t *pos)
 	rcu_read_unlock();
 	(*pos)++;
 
-	return (&qe->later == &ima_measurements) ? NULL : qe;
+	return (&qe->later == &ns->ima_measurements) ? NULL : qe;
 }
 
 static void ima_measurements_stop(struct seq_file *m, void *v)
diff --git a/security/integrity/ima/ima_init_ima_ns.c b/security/integrity/ima/ima_init_ima_ns.c
index 32ff7e271024..b097aad7b156 100644
--- a/security/integrity/ima/ima_init_ima_ns.c
+++ b/security/integrity/ima/ima_init_ima_ns.c
@@ -43,6 +43,11 @@ int ima_init_namespace(struct ima_namespace *ns)
 	atomic_long_set(&ns->ima_htable.len, 0);
 	atomic_long_set(&ns->ima_htable.violations, 0);
 	memset(&ns->ima_htable.queue, 0, sizeof(ns->ima_htable.queue));
+	INIT_LIST_HEAD(&ns->ima_measurements);
+	if (IS_ENABLED(CONFIG_IMA_KEXEC) && ns == &init_ima_ns)
+		ns->binary_runtime_size = 0;
+	else
+		ns->binary_runtime_size = ULONG_MAX;
 
 	return 0;
 }
diff --git a/security/integrity/ima/ima_ns.c b/security/integrity/ima/ima_ns.c
index 9c628ab2978b..6a0632806cdb 100644
--- a/security/integrity/ima/ima_ns.c
+++ b/security/integrity/ima/ima_ns.c
@@ -59,6 +59,7 @@ static void destroy_ima_ns(struct ima_namespace *ns)
 	pr_debug("DESTROY ima_ns: 0x%p\n", ns);
 	ima_free_policy_rules(ns);
 	free_ns_status_cache(ns);
+	ima_free_measurements(ns);
 	kmem_cache_free(imans_cachep, ns);
 }
 
diff --git a/security/integrity/ima/ima_queue.c b/security/integrity/ima/ima_queue.c
index 373154039b91..f15f776918ec 100644
--- a/security/integrity/ima/ima_queue.c
+++ b/security/integrity/ima/ima_queue.c
@@ -25,13 +25,6 @@
 /* pre-allocated array of tpm_digest structures to extend a PCR */
 static struct tpm_digest *digests;
 
-LIST_HEAD(ima_measurements);	/* list of all measurements */
-#ifdef CONFIG_IMA_KEXEC
-static unsigned long binary_runtime_size;
-#else
-static unsigned long binary_runtime_size = ULONG_MAX;
-#endif
-
 /* mutex protects atomicity of extending measurement list
  * and extending the TPM PCR aggregate. Since tpm_extend can take
  * long (and the tpm driver uses a mutex), we can't use the spinlock.
@@ -107,7 +100,7 @@ static int ima_add_digest_entry(struct ima_namespace *ns,
 	qe->entry = entry;
 
 	INIT_LIST_HEAD(&qe->later);
-	list_add_tail_rcu(&qe->later, &ima_measurements);
+	list_add_tail_rcu(&qe->later, &ns->ima_measurements);
 
 	atomic_long_inc(&ns->ima_htable.len);
 	if (update_htable) {
@@ -116,12 +109,12 @@ static int ima_add_digest_entry(struct ima_namespace *ns,
 	} else
 		INIT_HLIST_NODE(&qe->hnext);
 
-	if (binary_runtime_size != ULONG_MAX) {
+	if (ns->binary_runtime_size != ULONG_MAX) {
 		int size;
 
 		size = get_binary_runtime_size(entry);
-		binary_runtime_size = (binary_runtime_size < ULONG_MAX - size) ?
-		     binary_runtime_size + size : ULONG_MAX;
+		ns->binary_runtime_size = (ns->binary_runtime_size < ULONG_MAX - size) ?
+		     ns->binary_runtime_size + size : ULONG_MAX;
 	}
 	return 0;
 }
@@ -131,12 +124,12 @@ static int ima_add_digest_entry(struct ima_namespace *ns,
  * entire binary_runtime_measurement list, including the ima_kexec_hdr
  * structure.
  */
-unsigned long ima_get_binary_runtime_size(void)
+unsigned long ima_get_binary_runtime_size(struct ima_namespace *ns)
 {
-	if (binary_runtime_size >= (ULONG_MAX - sizeof(struct ima_kexec_hdr)))
+	if (ns->binary_runtime_size >= (ULONG_MAX - sizeof(struct ima_kexec_hdr)))
 		return ULONG_MAX;
 	else
-		return binary_runtime_size + sizeof(struct ima_kexec_hdr);
+		return ns->binary_runtime_size + sizeof(struct ima_kexec_hdr);
 }
 
 static int ima_pcr_extend(struct tpm_digest *digests_arg, int pcr)
@@ -217,6 +210,18 @@ int ima_restore_measurement_entry(struct ima_namespace *ns,
 	return result;
 }
 
+void ima_free_measurements(struct ima_namespace *ns)
+{
+	struct ima_queue_entry *qe, *tmp;
+
+	list_for_each_entry_safe(qe, tmp, &ns->ima_measurements, later) {
+		list_del(&qe->later);
+		if (!hlist_unhashed(&qe->hnext))
+			hlist_del(&qe->hnext);
+		kfree(qe);
+	}
+}
+
 int __init ima_init_digests(void)
 {
 	u16 digest_size;
-- 
2.31.1


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

* [PATCH v5 09/16] ima: Only accept AUDIT rules for IMA non-init_ima_ns namespaces for now
  2021-12-08 22:18 [PATCH v5 00/16] ima: Namespace IMA with audit support in IMA-ns Stefan Berger
                   ` (7 preceding siblings ...)
  2021-12-08 22:18 ` [PATCH v5 08/16] ima: Move measurement list related variables " Stefan Berger
@ 2021-12-08 22:18 ` Stefan Berger
  2021-12-08 22:18 ` [PATCH v5 10/16] ima: Implement hierarchical processing of file accesses Stefan Berger
                   ` (6 subsequent siblings)
  15 siblings, 0 replies; 63+ messages in thread
From: Stefan Berger @ 2021-12-08 22:18 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

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

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

diff --git a/security/integrity/ima/ima_policy.c b/security/integrity/ima/ima_policy.c
index 4d6d3a39f65e..747dca6131d6 100644
--- a/security/integrity/ima/ima_policy.c
+++ b/security/integrity/ima/ima_policy.c
@@ -1787,6 +1787,16 @@ static int ima_parse_rule(struct ima_namespace *ns,
 			result = -EINVAL;
 			break;
 		}
+
+		/* IMA namespace only accepts AUDIT rules */
+		if (ns != &init_ima_ns) {
+			switch (entry->action) {
+			case MEASURE:
+			case APPRAISE:
+			case HASH:
+				result = -EINVAL;
+			}
+		}
 	}
 	if (!result && !ima_validate_rule(entry))
 		result = -EINVAL;
-- 
2.31.1


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

* [PATCH v5 10/16] ima: Implement hierarchical processing of file accesses
  2021-12-08 22:18 [PATCH v5 00/16] ima: Namespace IMA with audit support in IMA-ns Stefan Berger
                   ` (8 preceding siblings ...)
  2021-12-08 22:18 ` [PATCH v5 09/16] ima: Only accept AUDIT rules for IMA non-init_ima_ns namespaces for now Stefan Berger
@ 2021-12-08 22:18 ` Stefan Berger
  2021-12-08 22:18 ` [PATCH v5 11/16] securityfs: Only use simple_pin_fs/simple_release_fs for init_user_ns Stefan Berger
                   ` (5 subsequent siblings)
  15 siblings, 0 replies; 63+ messages in thread
From: Stefan Berger @ 2021-12-08 22:18 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 IMA namespaces towards the init_ima_ns. This way
file accesses can be audited in an IMA namespace and also be evaluated
against the IMA policies of parent IMA namespaces.

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

diff --git a/security/integrity/ima/ima_main.c b/security/integrity/ima/ima_main.c
index 2121a831f38a..70fa26b7bd3f 100644
--- a/security/integrity/ima/ima_main.c
+++ b/security/integrity/ima/ima_main.c
@@ -200,10 +200,10 @@ void ima_file_free(struct file *file)
 	ima_check_last_writer(iint, inode, file);
 }
 
-static int process_measurement(struct ima_namespace *ns,
-			       struct file *file, const struct cred *cred,
-			       u32 secid, char *buf, loff_t size, int mask,
-			       enum ima_hooks func)
+static int __process_measurement(struct ima_namespace *ns,
+				 struct file *file, const struct cred *cred,
+				 u32 secid, char *buf, loff_t size, int mask,
+				 enum ima_hooks func)
 {
 	struct inode *inode = file_inode(file);
 	struct integrity_iint_cache *iint = NULL;
@@ -405,6 +405,27 @@ static int process_measurement(struct ima_namespace *ns,
 	return 0;
 }
 
+static int process_measurement(struct ima_namespace *ns,
+			       struct file *file, const struct cred *cred,
+			       u32 secid, char *buf, loff_t size, int mask,
+			       enum ima_hooks func)
+{
+	struct user_namespace *user_ns = ns->user_ns;
+	int ret = 0;
+
+	while (user_ns) {
+		ns = user_ns->ima_ns;
+
+		ret = __process_measurement(ns, file, cred, secid, buf, size, mask, func);
+		if (ret)
+			break;
+
+		user_ns = user_ns->parent;
+	};
+
+	return ret;
+}
+
 /**
  * ima_file_mmap - based on policy, collect/store measurement.
  * @file: pointer to the file to be measured (May be NULL)
-- 
2.31.1


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

* [PATCH v5 11/16] securityfs: Only use simple_pin_fs/simple_release_fs for init_user_ns
  2021-12-08 22:18 [PATCH v5 00/16] ima: Namespace IMA with audit support in IMA-ns Stefan Berger
                   ` (9 preceding siblings ...)
  2021-12-08 22:18 ` [PATCH v5 10/16] ima: Implement hierarchical processing of file accesses Stefan Berger
@ 2021-12-08 22:18 ` Stefan Berger
  2021-12-08 22:18 ` [PATCH v5 12/16] securityfs: Extend securityfs with namespacing support Stefan Berger
                   ` (4 subsequent siblings)
  15 siblings, 0 replies; 63+ messages in thread
From: Stefan Berger @ 2021-12-08 22:18 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

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

Signed-off-by: Stefan Berger <stefanb@linux.ibm.com>
Signed-off-by: James Bottomley <James.Bottomley@HansenPartnership.com>
Acked-by: Christian Brauner <christian.brauner@ubuntu.com>
---
 security/inode.c | 32 +++++++++++++++++++++++---------
 1 file changed, 23 insertions(+), 9 deletions(-)

diff --git a/security/inode.c b/security/inode.c
index 6c326939750d..9ba2fa875c4c 100644
--- a/security/inode.c
+++ b/security/inode.c
@@ -21,9 +21,10 @@
 #include <linux/security.h>
 #include <linux/lsm_hooks.h>
 #include <linux/magic.h>
+#include <linux/user_namespace.h>
 
-static struct vfsmount *mount;
-static int mount_count;
+static struct vfsmount *init_securityfs_mount;
+static int init_securityfs_mount_count;
 
 static void securityfs_free_inode(struct inode *inode)
 {
@@ -109,6 +110,7 @@ static struct dentry *securityfs_create_dentry(const char *name, umode_t mode,
 					const struct file_operations *fops,
 					const struct inode_operations *iops)
 {
+	struct user_namespace *ns = current_user_ns();
 	struct dentry *dentry;
 	struct inode *dir, *inode;
 	int error;
@@ -118,12 +120,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);
 
@@ -168,7 +177,9 @@ static struct dentry *securityfs_create_dentry(const char *name, umode_t mode,
 	dentry = ERR_PTR(error);
 out:
 	inode_unlock(dir);
-	simple_release_fs(&mount, &mount_count);
+	if (ns == &init_user_ns)
+		simple_release_fs(&init_securityfs_mount,
+				  &init_securityfs_mount_count);
 	return dentry;
 }
 
@@ -294,6 +305,7 @@ EXPORT_SYMBOL_GPL(securityfs_create_symlink);
  */
 void securityfs_remove(struct dentry *dentry)
 {
+	struct user_namespace *ns = dentry->d_sb->s_user_ns;
 	struct inode *dir;
 
 	if (!dentry || IS_ERR(dentry))
@@ -309,7 +321,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] 63+ messages in thread

* [PATCH v5 12/16] securityfs: Extend securityfs with namespacing support
  2021-12-08 22:18 [PATCH v5 00/16] ima: Namespace IMA with audit support in IMA-ns Stefan Berger
                   ` (10 preceding siblings ...)
  2021-12-08 22:18 ` [PATCH v5 11/16] securityfs: Only use simple_pin_fs/simple_release_fs for init_user_ns Stefan Berger
@ 2021-12-08 22:18 ` Stefan Berger
  2021-12-08 22:18 ` [PATCH v5 13/16] ima: Move some IMA policy and filesystem related variables into ima_namespace Stefan Berger
                   ` (3 subsequent siblings)
  15 siblings, 0 replies; 63+ messages in thread
From: Stefan Berger @ 2021-12-08 22:18 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

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

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

diff --git a/security/inode.c b/security/inode.c
index 9ba2fa875c4c..c1aef4613a17 100644
--- a/security/inode.c
+++ b/security/inode.c
@@ -54,7 +54,7 @@ static int securityfs_fill_super(struct super_block *sb, struct fs_context *fc)
 
 static int securityfs_get_tree(struct fs_context *fc)
 {
-	return get_tree_single(fc, securityfs_fill_super);
+	return get_tree_keyed(fc, securityfs_fill_super, fc->user_ns);
 }
 
 static const struct fs_context_operations securityfs_context_ops = {
@@ -72,6 +72,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,
 };
 
 /**
-- 
2.31.1


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

* [PATCH v5 13/16] ima: Move some IMA policy and filesystem related variables into ima_namespace
  2021-12-08 22:18 [PATCH v5 00/16] ima: Namespace IMA with audit support in IMA-ns Stefan Berger
                   ` (11 preceding siblings ...)
  2021-12-08 22:18 ` [PATCH v5 12/16] securityfs: Extend securityfs with namespacing support Stefan Berger
@ 2021-12-08 22:18 ` Stefan Berger
  2021-12-09 19:11   ` Christian Brauner
  2021-12-08 22:18 ` [PATCH v5 14/16] ima: Use mac_admin_ns_capable() to check corresponding capability Stefan Berger
                   ` (2 subsequent siblings)
  15 siblings, 1 reply; 63+ messages in thread
From: Stefan Berger @ 2021-12-08 22:18 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_write_mutex, ima_fs_flag, and valid_policy variables into
ima_namespace. This way each IMA namespace can set those variables
independently.

Signed-off-by: Stefan Berger <stefanb@linux.ibm.com>
---
 include/linux/ima.h                      |  5 ++++
 security/integrity/ima/ima_fs.c          | 32 +++++++++++-------------
 security/integrity/ima/ima_init_ima_ns.c |  4 +++
 3 files changed, 23 insertions(+), 18 deletions(-)

diff --git a/include/linux/ima.h b/include/linux/ima.h
index 2ce801bfc449..3aaf6e806db4 100644
--- a/include/linux/ima.h
+++ b/include/linux/ima.h
@@ -261,6 +261,11 @@ struct ima_namespace {
 	struct ima_h_table ima_htable;
 	struct list_head ima_measurements;
 	unsigned long binary_runtime_size;
+
+	/* IMA's filesystem */
+	struct mutex ima_write_mutex;
+	unsigned long ima_fs_flags;
+	int valid_policy;
 };
 
 extern struct ima_namespace init_ima_ns;
diff --git a/security/integrity/ima/ima_fs.c b/security/integrity/ima/ima_fs.c
index 38b1c26479b3..0e582ceecc7f 100644
--- a/security/integrity/ima/ima_fs.c
+++ b/security/integrity/ima/ima_fs.c
@@ -25,8 +25,6 @@
 
 #include "ima.h"
 
-static DEFINE_MUTEX(ima_write_mutex);
-
 bool ima_canonical_fmt;
 static int __init default_canonical_fmt_setup(char *str)
 {
@@ -37,8 +35,6 @@ static int __init default_canonical_fmt_setup(char *str)
 }
 __setup("ima_canonical_fmt", default_canonical_fmt_setup);
 
-static int valid_policy = 1;
-
 static ssize_t ima_show_htable_value(char __user *buf, size_t count,
 				     loff_t *ppos, atomic_long_t *val)
 {
@@ -339,7 +335,7 @@ static ssize_t ima_write_policy(struct file *file, const char __user *buf,
 		goto out;
 	}
 
-	result = mutex_lock_interruptible(&ima_write_mutex);
+	result = mutex_lock_interruptible(&ns->ima_write_mutex);
 	if (result < 0)
 		goto out_free;
 
@@ -354,12 +350,12 @@ static ssize_t ima_write_policy(struct file *file, const char __user *buf,
 	} else {
 		result = ima_parse_add_rule(ns, data);
 	}
-	mutex_unlock(&ima_write_mutex);
+	mutex_unlock(&ns->ima_write_mutex);
 out_free:
 	kfree(data);
 out:
 	if (result < 0)
-		valid_policy = 0;
+		ns->valid_policy = 0;
 
 	return result;
 }
@@ -376,8 +372,6 @@ enum ima_fs_flags {
 	IMA_FS_BUSY,
 };
 
-static unsigned long ima_fs_flags;
-
 #ifdef	CONFIG_IMA_READ_POLICY
 static const struct seq_operations ima_policy_seqops = {
 		.start = ima_policy_start,
@@ -392,6 +386,8 @@ static const struct seq_operations ima_policy_seqops = {
  */
 static int ima_open_policy(struct inode *inode, struct file *filp)
 {
+	struct ima_namespace *ns = get_current_ns();
+
 	if (!(filp->f_flags & O_WRONLY)) {
 #ifndef	CONFIG_IMA_READ_POLICY
 		return -EACCES;
@@ -403,7 +399,7 @@ static int ima_open_policy(struct inode *inode, struct file *filp)
 		return seq_open(filp, &ima_policy_seqops);
 #endif
 	}
-	if (test_and_set_bit(IMA_FS_BUSY, &ima_fs_flags))
+	if (test_and_set_bit(IMA_FS_BUSY, &ns->ima_fs_flags))
 		return -EBUSY;
 	return 0;
 }
@@ -417,25 +413,25 @@ static int ima_open_policy(struct inode *inode, struct file *filp)
  */
 static int ima_release_policy(struct inode *inode, struct file *file)
 {
-	const char *cause = valid_policy ? "completed" : "failed";
 	struct ima_namespace *ns = get_current_ns();
+	const char *cause = ns->valid_policy ? "completed" : "failed";
 
 	if ((file->f_flags & O_ACCMODE) == O_RDONLY)
 		return seq_release(inode, file);
 
-	if (valid_policy && ima_check_policy(ns) < 0) {
+	if (ns->valid_policy && ima_check_policy(ns) < 0) {
 		cause = "failed";
-		valid_policy = 0;
+		ns->valid_policy = 0;
 	}
 
 	pr_info("policy update %s\n", cause);
 	integrity_audit_msg(AUDIT_INTEGRITY_STATUS, NULL, NULL,
-			    "policy_update", cause, !valid_policy, 0);
+			    "policy_update", cause, !ns->valid_policy, 0);
 
-	if (!valid_policy) {
+	if (!ns->valid_policy) {
 		ima_delete_rules(ns);
-		valid_policy = 1;
-		clear_bit(IMA_FS_BUSY, &ima_fs_flags);
+		ns->valid_policy = 1;
+		clear_bit(IMA_FS_BUSY, &ns->ima_fs_flags);
 		return 0;
 	}
 
@@ -444,7 +440,7 @@ static int ima_release_policy(struct inode *inode, struct file *file)
 	securityfs_remove(ima_policy);
 	ima_policy = NULL;
 #elif defined(CONFIG_IMA_WRITE_POLICY)
-	clear_bit(IMA_FS_BUSY, &ima_fs_flags);
+	clear_bit(IMA_FS_BUSY, &ns->ima_fs_flags);
 #elif defined(CONFIG_IMA_READ_POLICY)
 	inode->i_mode &= ~S_IWUSR;
 #endif
diff --git a/security/integrity/ima/ima_init_ima_ns.c b/security/integrity/ima/ima_init_ima_ns.c
index b097aad7b156..162c94e06d13 100644
--- a/security/integrity/ima/ima_init_ima_ns.c
+++ b/security/integrity/ima/ima_init_ima_ns.c
@@ -49,6 +49,10 @@ int ima_init_namespace(struct ima_namespace *ns)
 	else
 		ns->binary_runtime_size = ULONG_MAX;
 
+	mutex_init(&ns->ima_write_mutex);
+	ns->valid_policy = 1;
+	ns->ima_fs_flags = 0;
+
 	return 0;
 }
 
-- 
2.31.1


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

* [PATCH v5 14/16] ima: Use mac_admin_ns_capable() to check corresponding capability
  2021-12-08 22:18 [PATCH v5 00/16] ima: Namespace IMA with audit support in IMA-ns Stefan Berger
                   ` (12 preceding siblings ...)
  2021-12-08 22:18 ` [PATCH v5 13/16] ima: Move some IMA policy and filesystem related variables into ima_namespace Stefan Berger
@ 2021-12-08 22:18 ` Stefan Berger
  2021-12-09  7:22   ` Denis Semakin
  2021-12-09  8:09   ` Denis Semakin
  2021-12-08 22:18 ` [PATCH v5 15/16] ima: Move dentries into ima_namespace Stefan Berger
  2021-12-08 22:18 ` [PATCH v5 16/16] ima: Setup securityfs for IMA namespace Stefan Berger
  15 siblings, 2 replies; 63+ messages in thread
From: Stefan Berger @ 2021-12-08 22:18 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

Use mac_admin_ns_capable() to check corresponding capability to allow
read/write IMA policy without CAP_SYS_ADMIN but with CAP_MAC_ADMIN.

Signed-off-by: Denis Semakin <denis.semakin@huawei.com>
Signed-off-by: Stefan Berger <stefanb@linux.ibm.com>
---
 include/linux/capability.h      | 6 ++++++
 security/integrity/ima/ima_fs.c | 2 +-
 2 files changed, 7 insertions(+), 1 deletion(-)

diff --git a/include/linux/capability.h b/include/linux/capability.h
index 65efb74c3585..991579178f32 100644
--- a/include/linux/capability.h
+++ b/include/linux/capability.h
@@ -270,6 +270,12 @@ static inline bool checkpoint_restore_ns_capable(struct user_namespace *ns)
 		ns_capable(ns, CAP_SYS_ADMIN);
 }
 
+static inline bool mac_admin_ns_capable(struct user_namespace *ns)
+{
+	return ns_capable(ns, CAP_MAC_ADMIN) ||
+		ns_capable(ns, CAP_SYS_ADMIN);
+}
+
 /* audit system wants to get cap info from files as well */
 int get_vfs_caps_from_disk(struct user_namespace *mnt_userns,
 			   const struct dentry *dentry,
diff --git a/security/integrity/ima/ima_fs.c b/security/integrity/ima/ima_fs.c
index 0e582ceecc7f..a749a3e79304 100644
--- a/security/integrity/ima/ima_fs.c
+++ b/security/integrity/ima/ima_fs.c
@@ -394,7 +394,7 @@ static int ima_open_policy(struct inode *inode, struct file *filp)
 #else
 		if ((filp->f_flags & O_ACCMODE) != O_RDONLY)
 			return -EACCES;
-		if (!capable(CAP_SYS_ADMIN))
+		if (!mac_admin_ns_capable(ns->user_ns))
 			return -EPERM;
 		return seq_open(filp, &ima_policy_seqops);
 #endif
-- 
2.31.1


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

* [PATCH v5 15/16] ima: Move dentries into ima_namespace
  2021-12-08 22:18 [PATCH v5 00/16] ima: Namespace IMA with audit support in IMA-ns Stefan Berger
                   ` (13 preceding siblings ...)
  2021-12-08 22:18 ` [PATCH v5 14/16] ima: Use mac_admin_ns_capable() to check corresponding capability Stefan Berger
@ 2021-12-08 22:18 ` Stefan Berger
  2021-12-09 14:34   ` Christian Brauner
  2021-12-08 22:18 ` [PATCH v5 16/16] ima: Setup securityfs for IMA namespace Stefan Berger
  15 siblings, 1 reply; 63+ messages in thread
From: Stefan Berger @ 2021-12-08 22:18 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 dentries into the ima_namespace for reuse by virtualized
SecurityFS. Implement function freeing the dentries in order of
files and symlinks before directories.

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

diff --git a/include/linux/ima.h b/include/linux/ima.h
index 3aaf6e806db4..4dd64e318b15 100644
--- a/include/linux/ima.h
+++ b/include/linux/ima.h
@@ -220,6 +220,17 @@ struct ima_h_table {
 	struct hlist_head queue[IMA_MEASURE_HTABLE_SIZE];
 };
 
+enum {
+	IMAFS_DENTRY_DIR = 0,
+	IMAFS_DENTRY_SYMLINK,
+	IMAFS_DENTRY_BINARY_RUNTIME_MEASUREMENTS,
+	IMAFS_DENTRY_ASCII_RUNTIME_MEASUREMENTS,
+	IMAFS_DENTRY_RUNTIME_MEASUREMENTS_COUNT,
+	IMAFS_DENTRY_VIOLATIONS,
+	IMAFS_DENTRY_IMA_POLICY,
+	IMAFS_DENTRY_LAST
+};
+
 struct ima_namespace {
 	struct kref kref;
 	struct user_namespace *user_ns;
@@ -266,6 +277,8 @@ struct ima_namespace {
 	struct mutex ima_write_mutex;
 	unsigned long ima_fs_flags;
 	int valid_policy;
+
+	struct dentry *dentry[IMAFS_DENTRY_LAST];
 };
 
 extern struct ima_namespace init_ima_ns;
diff --git a/security/integrity/ima/ima_fs.c b/security/integrity/ima/ima_fs.c
index a749a3e79304..3810d11fb463 100644
--- a/security/integrity/ima/ima_fs.c
+++ b/security/integrity/ima/ima_fs.c
@@ -360,14 +360,6 @@ static ssize_t ima_write_policy(struct file *file, const char __user *buf,
 	return result;
 }
 
-static struct dentry *ima_dir;
-static struct dentry *ima_symlink;
-static struct dentry *binary_runtime_measurements;
-static struct dentry *ascii_runtime_measurements;
-static struct dentry *runtime_measurements_count;
-static struct dentry *violations;
-static struct dentry *ima_policy;
-
 enum ima_fs_flags {
 	IMA_FS_BUSY,
 };
@@ -437,8 +429,8 @@ static int ima_release_policy(struct inode *inode, struct file *file)
 
 	ima_update_policy(ns);
 #if !defined(CONFIG_IMA_WRITE_POLICY) && !defined(CONFIG_IMA_READ_POLICY)
-	securityfs_remove(ima_policy);
-	ima_policy = NULL;
+	securityfs_remove(ns->dentry[IMAFS_DENTRY_IMA_POLICY]);
+	ns->dentry[IMAFS_DENTRY_IMA_POLICY] = NULL;
 #elif defined(CONFIG_IMA_WRITE_POLICY)
 	clear_bit(IMA_FS_BUSY, &ns->ima_fs_flags);
 #elif defined(CONFIG_IMA_READ_POLICY)
@@ -455,58 +447,72 @@ static const struct file_operations ima_measure_policy_ops = {
 	.llseek = generic_file_llseek,
 };
 
-int __init ima_fs_init(void)
+static void ima_fs_ns_free_dentries(struct ima_namespace *ns)
 {
-	ima_dir = securityfs_create_dir("ima", integrity_dir);
-	if (IS_ERR(ima_dir))
+	int i;
+
+	for (i = IMAFS_DENTRY_LAST - 1; i >= 0; i--)
+		securityfs_remove(ns->dentry[i]);
+
+	memset(ns->dentry, 0, sizeof(ns->dentry));
+}
+
+static int __init ima_securityfs_init(struct user_namespace *user_ns)
+{
+	struct ima_namespace *ns = user_ns->ima_ns;
+	struct dentry *ima_dir;
+
+	ns->dentry[IMAFS_DENTRY_DIR] = securityfs_create_dir("ima", integrity_dir);
+	if (IS_ERR(ns->dentry[IMAFS_DENTRY_DIR]))
 		return -1;
+	ima_dir = ns->dentry[IMAFS_DENTRY_DIR];
 
-	ima_symlink = securityfs_create_symlink("ima", NULL, "integrity/ima",
-						NULL);
-	if (IS_ERR(ima_symlink))
+	ns->dentry[IMAFS_DENTRY_SYMLINK] =
+	    securityfs_create_symlink("ima", NULL, "integrity/ima", NULL);
+	if (IS_ERR(ns->dentry[IMAFS_DENTRY_SYMLINK]))
 		goto out;
 
-	binary_runtime_measurements =
+	ns->dentry[IMAFS_DENTRY_BINARY_RUNTIME_MEASUREMENTS] =
 	    securityfs_create_file("binary_runtime_measurements",
 				   S_IRUSR | S_IRGRP, ima_dir, NULL,
 				   &ima_measurements_ops);
-	if (IS_ERR(binary_runtime_measurements))
+	if (IS_ERR(ns->dentry[IMAFS_DENTRY_BINARY_RUNTIME_MEASUREMENTS]))
 		goto out;
 
-	ascii_runtime_measurements =
+	ns->dentry[IMAFS_DENTRY_ASCII_RUNTIME_MEASUREMENTS] =
 	    securityfs_create_file("ascii_runtime_measurements",
 				   S_IRUSR | S_IRGRP, ima_dir, NULL,
 				   &ima_ascii_measurements_ops);
-	if (IS_ERR(ascii_runtime_measurements))
+	if (IS_ERR(ns->dentry[IMAFS_DENTRY_ASCII_RUNTIME_MEASUREMENTS]))
 		goto out;
 
-	runtime_measurements_count =
+	ns->dentry[IMAFS_DENTRY_RUNTIME_MEASUREMENTS_COUNT] =
 	    securityfs_create_file("runtime_measurements_count",
 				   S_IRUSR | S_IRGRP, ima_dir, NULL,
 				   &ima_measurements_count_ops);
-	if (IS_ERR(runtime_measurements_count))
+	if (IS_ERR(ns->dentry[IMAFS_DENTRY_RUNTIME_MEASUREMENTS_COUNT]))
 		goto out;
 
-	violations =
+	ns->dentry[IMAFS_DENTRY_VIOLATIONS] =
 	    securityfs_create_file("violations", S_IRUSR | S_IRGRP,
 				   ima_dir, NULL, &ima_htable_violations_ops);
-	if (IS_ERR(violations))
+	if (IS_ERR(ns->dentry[IMAFS_DENTRY_VIOLATIONS]))
 		goto out;
 
-	ima_policy = securityfs_create_file("policy", POLICY_FILE_FLAGS,
+	ns->dentry[IMAFS_DENTRY_IMA_POLICY] =
+	    securityfs_create_file("policy", POLICY_FILE_FLAGS,
 					    ima_dir, NULL,
 					    &ima_measure_policy_ops);
-	if (IS_ERR(ima_policy))
+	if (IS_ERR(ns->dentry[IMAFS_DENTRY_IMA_POLICY]))
 		goto out;
 
 	return 0;
 out:
-	securityfs_remove(violations);
-	securityfs_remove(runtime_measurements_count);
-	securityfs_remove(ascii_runtime_measurements);
-	securityfs_remove(binary_runtime_measurements);
-	securityfs_remove(ima_symlink);
-	securityfs_remove(ima_dir);
-	securityfs_remove(ima_policy);
+	ima_fs_ns_free_dentries(ns);
 	return -1;
 }
+
+int __init ima_fs_init(void)
+{
+	return ima_securityfs_init(&init_user_ns);
+}
-- 
2.31.1


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

* [PATCH v5 16/16] ima: Setup securityfs for IMA namespace
  2021-12-08 22:18 [PATCH v5 00/16] ima: Namespace IMA with audit support in IMA-ns Stefan Berger
                   ` (14 preceding siblings ...)
  2021-12-08 22:18 ` [PATCH v5 15/16] ima: Move dentries into ima_namespace Stefan Berger
@ 2021-12-08 22:18 ` Stefan Berger
  15 siblings, 0 replies; 63+ messages in thread
From: Stefan Berger @ 2021-12-08 22:18 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

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

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

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

This filesystem can now be mounted as follows:

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

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

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

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

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

diff --git a/include/linux/ima.h b/include/linux/ima.h
index 4dd64e318b15..32bf98092143 100644
--- a/include/linux/ima.h
+++ b/include/linux/ima.h
@@ -66,6 +66,9 @@ static inline const char * const *arch_get_ima_policy(void)
 }
 #endif
 
+extern int ima_securityfs_init(struct user_namespace *user_ns,
+			       struct dentry *root);
+
 #else
 static inline enum hash_algo ima_get_current_hash_algo(void)
 {
@@ -154,6 +157,11 @@ static inline int ima_measure_critical_data(const char *event_label,
 	return -ENOENT;
 }
 
+static inline int ima_securityfs_init(struct user_namespace *ns, struct dentry *root)
+{
+	return 0;
+}
+
 #endif /* CONFIG_IMA */
 
 #ifndef CONFIG_IMA_KEXEC
@@ -221,7 +229,8 @@ struct ima_h_table {
 };
 
 enum {
-	IMAFS_DENTRY_DIR = 0,
+	IMAFS_DENTRY_INTEGRITY_DIR = 0,
+	IMAFS_DENTRY_DIR,
 	IMAFS_DENTRY_SYMLINK,
 	IMAFS_DENTRY_BINARY_RUNTIME_MEASUREMENTS,
 	IMAFS_DENTRY_ASCII_RUNTIME_MEASUREMENTS,
@@ -336,6 +345,7 @@ static inline struct ima_namespace *get_current_ns(void)
 {
 	return &init_ima_ns;
 }
+
 #endif /* CONFIG_IMA_NS */
 
 #if defined(CONFIG_IMA_APPRAISE) && defined(CONFIG_INTEGRITY_TRUSTED_KEYRING)
diff --git a/security/inode.c b/security/inode.c
index c1aef4613a17..eaccba7017d9 100644
--- a/security/inode.c
+++ b/security/inode.c
@@ -16,6 +16,7 @@
 #include <linux/fs_context.h>
 #include <linux/mount.h>
 #include <linux/pagemap.h>
+#include <linux/ima.h>
 #include <linux/init.h>
 #include <linux/namei.h>
 #include <linux/security.h>
@@ -41,6 +42,7 @@ static const struct super_operations securityfs_super_operations = {
 static int securityfs_fill_super(struct super_block *sb, struct fs_context *fc)
 {
 	static const struct tree_descr files[] = {{""}};
+	struct user_namespace *ns = fc->user_ns;
 	int error;
 
 	error = simple_fill_super(sb, SECURITYFS_MAGIC, files);
@@ -49,7 +51,10 @@ static int securityfs_fill_super(struct super_block *sb, struct fs_context *fc)
 
 	sb->s_op = &securityfs_super_operations;
 
-	return 0;
+	if (ns != &init_user_ns)
+		error = ima_securityfs_init(ns, sb->s_root);
+
+	return error;
 }
 
 static int securityfs_get_tree(struct fs_context *fc)
@@ -97,11 +102,15 @@ static struct file_system_type fs_type = {
  * securityfs_create_dir() function is recommended to be used
  * instead).
  *
- * This function returns a pointer to a dentry if it succeeds.  This
+ * This function returns a pointer to a dentry if it succeeds. If the
+ * dentry was created while the init_user_ns was active, then this
  * pointer must be passed to the securityfs_remove() function when the
  * file is to be removed (no automatic cleanup happens if your module
- * is unloaded, you are responsible here).  If an error occurs, the
- * function will return the error value (via ERR_PTR).
+ * is unloaded, you are responsible here). If any other user namespace
+ * was active then the dentry may be removed using securityfs_remove()
+ * when a module is removed but no cleanup must be done once the
+ * superblock was delete since then it will be deleted automatically.
+ * If an error occurs, the function will return the error value (via ERR_PTR).
  *
  * If securityfs is not enabled in the kernel, the value %-ENODEV is
  * returned.
@@ -169,7 +178,8 @@ static struct dentry *securityfs_create_dentry(const char *name, umode_t mode,
 		inode->i_fop = fops;
 	}
 	d_instantiate(dentry, inode);
-	dget(dentry);
+	if (ns == &init_user_ns)
+		dget(dentry);
 	inode_unlock(dir);
 	return dentry;
 
diff --git a/security/integrity/ima/ima_fs.c b/security/integrity/ima/ima_fs.c
index 3810d11fb463..778983fd9a73 100644
--- a/security/integrity/ima/ima_fs.c
+++ b/security/integrity/ima/ima_fs.c
@@ -447,8 +447,12 @@ static const struct file_operations ima_measure_policy_ops = {
 	.llseek = generic_file_llseek,
 };
 
-static void ima_fs_ns_free_dentries(struct ima_namespace *ns)
+/* Remove the dentries. In case user_ns == &init_user_ns this function
+ * does not need to be called since there's one less reference to dentries.
+ */
+static void ima_fs_ns_free_dentries(struct user_namespace *user_ns)
 {
+	struct ima_namespace *ns = user_ns->ima_ns;
 	int i;
 
 	for (i = IMAFS_DENTRY_LAST - 1; i >= 0; i--)
@@ -457,18 +461,35 @@ static void ima_fs_ns_free_dentries(struct ima_namespace *ns)
 	memset(ns->dentry, 0, sizeof(ns->dentry));
 }
 
-static int __init ima_securityfs_init(struct user_namespace *user_ns)
+int ima_securityfs_init(struct user_namespace *user_ns, struct dentry *root)
 {
 	struct ima_namespace *ns = user_ns->ima_ns;
 	struct dentry *ima_dir;
 
-	ns->dentry[IMAFS_DENTRY_DIR] = securityfs_create_dir("ima", integrity_dir);
+	/* already initialized? */
+	if (ns->dentry[IMAFS_DENTRY_INTEGRITY_DIR])
+		return 0;
+
+	/* FIXME: update when evm and integrity are namespaced */
+	if (user_ns != &init_user_ns) {
+		ns->dentry[IMAFS_DENTRY_INTEGRITY_DIR] =
+			securityfs_create_dir("integrity", root);
+		if (IS_ERR(ns->dentry[IMAFS_DENTRY_INTEGRITY_DIR])) {
+			ns->dentry[IMAFS_DENTRY_INTEGRITY_DIR] = NULL;
+			return -1;
+		}
+	} else
+		ns->dentry[IMAFS_DENTRY_INTEGRITY_DIR] = integrity_dir;
+
+	ns->dentry[IMAFS_DENTRY_DIR] =
+	    securityfs_create_dir("ima",
+				  ns->dentry[IMAFS_DENTRY_INTEGRITY_DIR]);
 	if (IS_ERR(ns->dentry[IMAFS_DENTRY_DIR]))
-		return -1;
+		goto out;
 	ima_dir = ns->dentry[IMAFS_DENTRY_DIR];
 
 	ns->dentry[IMAFS_DENTRY_SYMLINK] =
-	    securityfs_create_symlink("ima", NULL, "integrity/ima", NULL);
+	    securityfs_create_symlink("ima", root, "integrity/ima", NULL);
 	if (IS_ERR(ns->dentry[IMAFS_DENTRY_SYMLINK]))
 		goto out;
 
@@ -508,11 +529,11 @@ static int __init ima_securityfs_init(struct user_namespace *user_ns)
 
 	return 0;
 out:
-	ima_fs_ns_free_dentries(ns);
+	ima_fs_ns_free_dentries(user_ns);
 	return -1;
 }
 
 int __init ima_fs_init(void)
 {
-	return ima_securityfs_init(&init_user_ns);
+	return ima_securityfs_init(&init_user_ns, NULL);
 }
-- 
2.31.1


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

* RE: [PATCH v5 14/16] ima: Use mac_admin_ns_capable() to check corresponding capability
  2021-12-08 22:18 ` [PATCH v5 14/16] ima: Use mac_admin_ns_capable() to check corresponding capability Stefan Berger
@ 2021-12-09  7:22   ` Denis Semakin
  2021-12-09 13:23     ` James Bottomley
  2021-12-09  8:09   ` Denis Semakin
  1 sibling, 1 reply; 63+ messages in thread
From: Denis Semakin @ 2021-12-09  7:22 UTC (permalink / raw)
  To: Stefan Berger, linux-integrity
  Cc: zohar, serge, christian.brauner, containers, dmitry.kasatkin,
	ebiederm, Krzysztof Struczynski, Roberto Sassu, mpeters, lhinds,
	lsturman, puiterwi, jejb, jamjoom, linux-kernel, paul, rgb,
	linux-security-module, jmorris

Hi. 
My question won't be about capabilities. I'm wondering how IMA-ns which is associated with USER-ns and is created during USER-ns creation
would be used by some namespaces orchestration systems, e.g. Kubernetes?.. It seems that it can be run without any user namespaces... 
Their community just discuss this opportunity to support User namespaces. (see https://github.com/kubernetes/enhancements/pull/2101)
Looks like currently IMA-ns will not be applicable for Kubernetes.

Br,
Denis

-----Original Message-----
From: Stefan Berger [mailto:stefanb@linux.ibm.com] 
Sent: Thursday, December 9, 2021 1:18 AM
To: linux-integrity@vger.kernel.org
Cc: zohar@linux.ibm.com; serge@hallyn.com; christian.brauner@ubuntu.com; containers@lists.linux.dev; dmitry.kasatkin@gmail.com; ebiederm@xmission.com; Krzysztof Struczynski <krzysztof.struczynski@huawei.com>; Roberto Sassu <roberto.sassu@huawei.com>; mpeters@redhat.com; lhinds@redhat.com; lsturman@redhat.com; puiterwi@redhat.com; jejb@linux.ibm.com; jamjoom@us.ibm.com; linux-kernel@vger.kernel.org; paul@paul-moore.com; rgb@redhat.com; linux-security-module@vger.kernel.org; jmorris@namei.org; Stefan Berger <stefanb@linux.ibm.com>; Denis Semakin <denis.semakin@huawei.com>
Subject: [PATCH v5 14/16] ima: Use mac_admin_ns_capable() to check corresponding capability

Use mac_admin_ns_capable() to check corresponding capability to allow read/write IMA policy without CAP_SYS_ADMIN but with CAP_MAC_ADMIN.

Signed-off-by: Denis Semakin <denis.semakin@huawei.com>
Signed-off-by: Stefan Berger <stefanb@linux.ibm.com>
---
 include/linux/capability.h      | 6 ++++++
 security/integrity/ima/ima_fs.c | 2 +-
 2 files changed, 7 insertions(+), 1 deletion(-)

diff --git a/include/linux/capability.h b/include/linux/capability.h index 65efb74c3585..991579178f32 100644
--- a/include/linux/capability.h
+++ b/include/linux/capability.h
@@ -270,6 +270,12 @@ static inline bool checkpoint_restore_ns_capable(struct user_namespace *ns)
 		ns_capable(ns, CAP_SYS_ADMIN);
 }
 
+static inline bool mac_admin_ns_capable(struct user_namespace *ns) {
+	return ns_capable(ns, CAP_MAC_ADMIN) ||
+		ns_capable(ns, CAP_SYS_ADMIN);
+}
+
 /* audit system wants to get cap info from files as well */  int get_vfs_caps_from_disk(struct user_namespace *mnt_userns,
 			   const struct dentry *dentry,
diff --git a/security/integrity/ima/ima_fs.c b/security/integrity/ima/ima_fs.c index 0e582ceecc7f..a749a3e79304 100644
--- a/security/integrity/ima/ima_fs.c
+++ b/security/integrity/ima/ima_fs.c
@@ -394,7 +394,7 @@ static int ima_open_policy(struct inode *inode, struct file *filp)  #else
 		if ((filp->f_flags & O_ACCMODE) != O_RDONLY)
 			return -EACCES;
-		if (!capable(CAP_SYS_ADMIN))
+		if (!mac_admin_ns_capable(ns->user_ns))
 			return -EPERM;
 		return seq_open(filp, &ima_policy_seqops);  #endif
--
2.31.1


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

* RE: [PATCH v5 14/16] ima: Use mac_admin_ns_capable() to check corresponding capability
  2021-12-08 22:18 ` [PATCH v5 14/16] ima: Use mac_admin_ns_capable() to check corresponding capability Stefan Berger
  2021-12-09  7:22   ` Denis Semakin
@ 2021-12-09  8:09   ` Denis Semakin
  2021-12-11 15:02     ` Serge E. Hallyn
  1 sibling, 1 reply; 63+ messages in thread
From: Denis Semakin @ 2021-12-09  8:09 UTC (permalink / raw)
  To: Stefan Berger, linux-integrity
  Cc: zohar, serge, christian.brauner, containers, dmitry.kasatkin,
	ebiederm, Krzysztof Struczynski, Roberto Sassu, mpeters, lhinds,
	lsturman, puiterwi, jejb, jamjoom, linux-kernel, paul, rgb,
	linux-security-module, jmorris

Following that thoughts...
Will it be so incorrectly to unbound IMA-ns from USER-ns?
I realize that it could lead a lot of problems but it is still unclear will current IMA-ns will be useful for Kuber...
How userland supposed to use current IMA-ns implementation?

Br,
Denis

-----Original Message-----
From: Denis Semakin 
Sent: Thursday, December 9, 2021 10:22 AM
To: 'Stefan Berger' <stefanb@linux.ibm.com>; linux-integrity@vger.kernel.org
Cc: zohar@linux.ibm.com; serge@hallyn.com; christian.brauner@ubuntu.com; containers@lists.linux.dev; dmitry.kasatkin@gmail.com; ebiederm@xmission.com; Krzysztof Struczynski <krzysztof.struczynski@huawei.com>; Roberto Sassu <roberto.sassu@huawei.com>; mpeters@redhat.com; lhinds@redhat.com; lsturman@redhat.com; puiterwi@redhat.com; jejb@linux.ibm.com; jamjoom@us.ibm.com; linux-kernel@vger.kernel.org; paul@paul-moore.com; rgb@redhat.com; linux-security-module@vger.kernel.org; jmorris@namei.org
Subject: RE: [PATCH v5 14/16] ima: Use mac_admin_ns_capable() to check corresponding capability

Hi. 
My question won't be about capabilities. I'm wondering how IMA-ns which is associated with USER-ns and is created during USER-ns creation would be used by some namespaces orchestration systems, e.g. Kubernetes?.. It seems that it can be run without any user namespaces... 
Their community just discuss this opportunity to support User namespaces. (see https://github.com/kubernetes/enhancements/pull/2101)
Looks like currently IMA-ns will not be applicable for Kubernetes.

Br,
Denis

-----Original Message-----
From: Stefan Berger [mailto:stefanb@linux.ibm.com]
Sent: Thursday, December 9, 2021 1:18 AM
To: linux-integrity@vger.kernel.org
Cc: zohar@linux.ibm.com; serge@hallyn.com; christian.brauner@ubuntu.com; containers@lists.linux.dev; dmitry.kasatkin@gmail.com; ebiederm@xmission.com; Krzysztof Struczynski <krzysztof.struczynski@huawei.com>; Roberto Sassu <roberto.sassu@huawei.com>; mpeters@redhat.com; lhinds@redhat.com; lsturman@redhat.com; puiterwi@redhat.com; jejb@linux.ibm.com; jamjoom@us.ibm.com; linux-kernel@vger.kernel.org; paul@paul-moore.com; rgb@redhat.com; linux-security-module@vger.kernel.org; jmorris@namei.org; Stefan Berger <stefanb@linux.ibm.com>; Denis Semakin <denis.semakin@huawei.com>
Subject: [PATCH v5 14/16] ima: Use mac_admin_ns_capable() to check corresponding capability

Use mac_admin_ns_capable() to check corresponding capability to allow read/write IMA policy without CAP_SYS_ADMIN but with CAP_MAC_ADMIN.

Signed-off-by: Denis Semakin <denis.semakin@huawei.com>
Signed-off-by: Stefan Berger <stefanb@linux.ibm.com>
---
 include/linux/capability.h      | 6 ++++++
 security/integrity/ima/ima_fs.c | 2 +-
 2 files changed, 7 insertions(+), 1 deletion(-)

diff --git a/include/linux/capability.h b/include/linux/capability.h index 65efb74c3585..991579178f32 100644
--- a/include/linux/capability.h
+++ b/include/linux/capability.h
@@ -270,6 +270,12 @@ static inline bool checkpoint_restore_ns_capable(struct user_namespace *ns)
 		ns_capable(ns, CAP_SYS_ADMIN);
 }
 
+static inline bool mac_admin_ns_capable(struct user_namespace *ns) {
+	return ns_capable(ns, CAP_MAC_ADMIN) ||
+		ns_capable(ns, CAP_SYS_ADMIN);
+}
+
 /* audit system wants to get cap info from files as well */  int get_vfs_caps_from_disk(struct user_namespace *mnt_userns,
 			   const struct dentry *dentry,
diff --git a/security/integrity/ima/ima_fs.c b/security/integrity/ima/ima_fs.c index 0e582ceecc7f..a749a3e79304 100644
--- a/security/integrity/ima/ima_fs.c
+++ b/security/integrity/ima/ima_fs.c
@@ -394,7 +394,7 @@ static int ima_open_policy(struct inode *inode, struct file *filp)  #else
 		if ((filp->f_flags & O_ACCMODE) != O_RDONLY)
 			return -EACCES;
-		if (!capable(CAP_SYS_ADMIN))
+		if (!mac_admin_ns_capable(ns->user_ns))
 			return -EPERM;
 		return seq_open(filp, &ima_policy_seqops);  #endif
--
2.31.1


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

* Re: [PATCH v5 04/16] ima: Move delayed work queue and variables into ima_namespace
  2021-12-08 22:18 ` [PATCH v5 04/16] ima: Move delayed work queue and variables into ima_namespace Stefan Berger
@ 2021-12-09 13:11   ` Christian Brauner
  2021-12-09 15:09     ` Stefan Berger
  0 siblings, 1 reply; 63+ messages in thread
From: Christian Brauner @ 2021-12-09 13:11 UTC (permalink / raw)
  To: Stefan Berger
  Cc: linux-integrity, zohar, serge, containers, dmitry.kasatkin,
	ebiederm, krzysztof.struczynski, roberto.sassu, mpeters, lhinds,
	lsturman, puiterwi, jejb, jamjoom, linux-kernel, paul, rgb,
	linux-security-module, jmorris

On Wed, Dec 08, 2021 at 05:18:06PM -0500, Stefan Berger wrote:
> Move the delayed work queue and associated variables to the
> ima_namespace and initialize them.
> 
> Since keys queued up for measurement currently are only relevant in the
> init_ima_ns, call ima_init_key_queue() only when the init_ima_ns is
> initialized.
> 
> Protect the ima_namespace when scheduling the delayed work by taking an
> additional reference to its user namespace. Put the reference when either
> the delayed work has completed or when it was cancelled but hadn't run.
> 
> Signed-off-by: Stefan Berger <stefanb@linux.ibm.com>
> ---
>  include/linux/ima.h                      | 11 +++++++
>  security/integrity/ima/ima.h             | 12 ++++---
>  security/integrity/ima/ima_fs.c          |  4 ++-
>  security/integrity/ima/ima_init.c        |  2 --
>  security/integrity/ima/ima_init_ima_ns.c |  8 +++++
>  security/integrity/ima/ima_policy.c      |  4 +--
>  security/integrity/ima/ima_queue_keys.c  | 42 +++++++++++++-----------
>  7 files changed, 53 insertions(+), 30 deletions(-)
> 
> diff --git a/include/linux/ima.h b/include/linux/ima.h
> index 9f6de36240b0..529defe4d272 100644
> --- a/include/linux/ima.h
> +++ b/include/linux/ima.h
> @@ -217,6 +217,17 @@ struct ima_namespace {
>  	struct rb_root ns_status_tree;
>  	rwlock_t ns_status_lock;
>  	struct kmem_cache *ns_status_cache;
> +
> +#ifdef CONFIG_IMA_QUEUE_EARLY_BOOT_KEYS
> +	/*
> +	 * If custom IMA policy is not loaded then keys queued up
> +	 * for measurement should be freed. This worker is used
> +	 * for handling this scenario.
> +	 */
> +	struct delayed_work ima_keys_delayed_work;
> +	long ima_key_queue_timeout;
> +	bool timer_expired;
> +#endif
>  };
>  
>  extern struct ima_namespace init_ima_ns;
> diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h
> index dd06e16c4e1c..9edab9050dc7 100644
> --- a/security/integrity/ima/ima.h
> +++ b/security/integrity/ima/ima.h
> @@ -77,6 +77,8 @@ struct ima_field_data {
>  	u32 len;
>  };
>  
> +struct ima_namespace;
> +
>  /* IMA template field definition */
>  struct ima_template_field {
>  	const char field_id[IMA_TEMPLATE_FIELD_ID_MAX_LEN];
> @@ -247,18 +249,18 @@ struct ima_key_entry {
>  	size_t payload_len;
>  	char *keyring_name;
>  };
> -void ima_init_key_queue(void);
> +void ima_init_key_queue(struct ima_namespace *ns);
>  bool ima_should_queue_key(void);
>  bool ima_queue_key(struct key *keyring, const void *payload,
>  		   size_t payload_len);
> -void ima_process_queued_keys(void);
> +void ima_process_queued_keys(struct ima_namespace *ns);
> +void ima_keys_handler(struct work_struct *work);
>  #else
> -static inline void ima_init_key_queue(void) {}
>  static inline bool ima_should_queue_key(void) { return false; }
>  static inline bool ima_queue_key(struct key *keyring,
>  				 const void *payload,
>  				 size_t payload_len) { return false; }
> -static inline void ima_process_queued_keys(void) {}
> +static inline void ima_process_queued_keys(struct ima_namespace *ns) {}
>  #endif /* CONFIG_IMA_QUEUE_EARLY_BOOT_KEYS */
>  
>  /* LIM API function definitions */
> @@ -300,7 +302,7 @@ int ima_match_policy(struct user_namespace *mnt_userns, struct inode *inode,
>  		     struct ima_template_desc **template_desc,
>  		     const char *func_data, unsigned int *allowed_algos);
>  void ima_init_policy(void);
> -void ima_update_policy(void);
> +void ima_update_policy(struct ima_namespace *ns);
>  void ima_update_policy_flags(void);
>  ssize_t ima_parse_add_rule(char *);
>  void ima_delete_rules(void);
> diff --git a/security/integrity/ima/ima_fs.c b/security/integrity/ima/ima_fs.c
> index 3d8e9d5db5aa..5cff3d6c3dc7 100644
> --- a/security/integrity/ima/ima_fs.c
> +++ b/security/integrity/ima/ima_fs.c
> @@ -21,6 +21,7 @@
>  #include <linux/rcupdate.h>
>  #include <linux/parser.h>
>  #include <linux/vmalloc.h>
> +#include <linux/ima.h>
>  
>  #include "ima.h"
>  
> @@ -410,6 +411,7 @@ static int ima_open_policy(struct inode *inode, struct file *filp)
>  static int ima_release_policy(struct inode *inode, struct file *file)
>  {
>  	const char *cause = valid_policy ? "completed" : "failed";
> +	struct ima_namespace *ns = get_current_ns();
>  
>  	if ((file->f_flags & O_ACCMODE) == O_RDONLY)
>  		return seq_release(inode, file);
> @@ -430,7 +432,7 @@ static int ima_release_policy(struct inode *inode, struct file *file)
>  		return 0;
>  	}
>  
> -	ima_update_policy();
> +	ima_update_policy(ns);
>  #if !defined(CONFIG_IMA_WRITE_POLICY) && !defined(CONFIG_IMA_READ_POLICY)
>  	securityfs_remove(ima_policy);
>  	ima_policy = NULL;
> diff --git a/security/integrity/ima/ima_init.c b/security/integrity/ima/ima_init.c
> index f6ae4557a0da..24848373a061 100644
> --- a/security/integrity/ima/ima_init.c
> +++ b/security/integrity/ima/ima_init.c
> @@ -155,8 +155,6 @@ int __init ima_init(void)
>  	if (rc != 0)
>  		return rc;
>  
> -	ima_init_key_queue();
> -
>  	ima_measure_critical_data("kernel_info", "kernel_version",
>  				  UTS_RELEASE, strlen(UTS_RELEASE), false,
>  				  NULL, 0);
> diff --git a/security/integrity/ima/ima_init_ima_ns.c b/security/integrity/ima/ima_init_ima_ns.c
> index 64777377664b..75ef17d52b5b 100644
> --- a/security/integrity/ima/ima_init_ima_ns.c
> +++ b/security/integrity/ima/ima_init_ima_ns.c
> @@ -26,6 +26,14 @@ int ima_init_namespace(struct ima_namespace *ns)
>  	if (!ns->ns_status_cache)
>  		return -ENOMEM;
>  
> +#ifdef CONFIG_IMA_QUEUE_EARLY_BOOT_KEYS
> +	INIT_DELAYED_WORK(&ns->ima_keys_delayed_work, ima_keys_handler);
> +	ns->ima_key_queue_timeout = 300000;
> +	ns->timer_expired = false;
> +	if (ns == &init_ima_ns)
> +		ima_init_key_queue(ns);

The refcounting seems wrong?
ima_init_key_queue() only takes a reference for init_ima_ns and
consequently on init_user_ns (which is a bit pointless since it can't go
away and so can't init_ima_ns).

In contrast non-init_ima_ns will not take a reference on their user_ns.
But ima_keys_handler() always puts the refcount for user_ns for both
non-init_ima_ns and init_ima_ns alike.

Maybe I'm misreading this.

In your earlier mail in [1] you said:

> > The only problem that I see where we are accessing the IMA namespace outside a
> > process context is in 4/16 'ima: Move delayed work queue and variables into
> > ima_namespace' where a delayed work queue is used. I fixed this now by getting

So we seem to know that ima always accesses ima_ns from
current_user_ns() and only in the workqueue case will it delay key
processing for a specific ima namespace without walking a userns
hierarchy.

If that's the case we should remove the user_ns member from ima_ns and
enforce that ima_ns is always accessed from current_user_ns().

Since the workqueue case luckily doesn't need access to user_ns anywhere
we can add a workqueue specific refcount that only keeps it alive for
the workqueue case. We just need to enforce that when the refcount is
bumped for the workqeue it must be done from process context so we're
guaranteed that when we bump the reference the user_ns and consequently
the ima_ns is still alive.

This should solve your lifetime issues (once you fixed the problem I
pointed out above).

(Btw, the kref member was unused before my patch. It didn't really do
any lifetime management for ima_ns afaict.)

[1]: https://lore.kernel.org/lkml/60fa585b-984e-fa13-e76f-56083a726259@linux.ibm.com

Here's a sketch neither compile nor runtime tested and without the
refcount issues I pointed out above fixed:

From 130e8d3faaad42820040587eff8695027fcf062a Mon Sep 17 00:00:00 2001
From: Christian Brauner <christian.brauner@ubuntu.com>
Date: Thu, 9 Dec 2021 13:15:49 +0100
Subject: [PATCH] !!!! HERE BE DRAGONS - UNFIXED REFCOUNT ISSUES FROM PREVIOUS
 PATCH AND ALL UNTESTED !!!!

ima: get rid of user_ns member in struct ima_namespace
---
 include/linux/ima.h                      | 40 +++++++++---------------
 security/integrity/ima/ima_fs.c          |  2 +-
 security/integrity/ima/ima_init_ima_ns.c |  3 +-
 security/integrity/ima/ima_main.c        |  2 +-
 security/integrity/ima/ima_ns.c          |  9 ++----
 security/integrity/ima/ima_queue_keys.c  | 22 +++++++++----
 6 files changed, 36 insertions(+), 42 deletions(-)

diff --git a/include/linux/ima.h b/include/linux/ima.h
index 32bf98092143..73cdfbf3f9d4 100644
--- a/include/linux/ima.h
+++ b/include/linux/ima.h
@@ -13,6 +13,7 @@
 #include <linux/kexec.h>
 #include <linux/user_namespace.h>
 #include <crypto/hash_info.h>
+#include <linux/refcount.h>
 struct linux_binprm;
 
 #ifdef CONFIG_IMA
@@ -241,8 +242,6 @@ enum {
 };
 
 struct ima_namespace {
-	struct kref kref;
-	struct user_namespace *user_ns;
 	struct rb_root ns_status_tree;
 	rwlock_t ns_status_lock;
 	struct kmem_cache *ns_status_cache;
@@ -264,6 +263,7 @@ struct ima_namespace {
 	 * for measurement should be freed. This worker is used
 	 * for handling this scenario.
 	 */
+	refcount_t ima_keys_delayed_ref;
 	struct delayed_work ima_keys_delayed_work;
 	long ima_key_queue_timeout;
 	bool timer_expired;
@@ -295,24 +295,12 @@ extern struct list_head ima_default_rules;
 
 #ifdef CONFIG_IMA_NS
 
-void free_ima_ns(struct kref *kref);
-
-static inline struct ima_namespace *get_ima_ns(struct ima_namespace *ns)
-{
-	if (ns)
-		kref_get(&ns->kref);
-
-	return ns;
-}
+void free_ima_ns(struct ima_namespace *ns);
+void __put_delayed_ima_ns(struct ima_namespace *ns);
 
 static inline void put_ima_ns(struct user_namespace *user_ns)
 {
-	struct ima_namespace *ns = user_ns->ima_ns;
-
-	if (ns) {
-		pr_debug("DEREF   ima_ns: 0x%p  ctr: %d\n", ns, kref_read(&ns->kref));
-		kref_put(&ns->kref, free_ima_ns);
-	}
+	__put_delayed_ima_ns(user_ns->ima_ns);
 }
 
 int create_ima_ns(struct user_namespace *user_ns);
@@ -322,21 +310,20 @@ static inline struct ima_namespace *get_current_ns(void)
 	return current_user_ns()->ima_ns;
 }
 
-#else
-
-static inline struct ima_namespace *get_ima_ns(struct ima_namespace *ns)
+static inline struct user_namespace *ima_user_ns(const struct ima_namespace *ima_ns)
 {
-	return ns;
+	struct user_namespace *user_ns;
+	user_ns = current_user_ns();
+	WARN_ON(user_ns->ima_ns != ima_ns);
+	return user_ns;
 }
 
-static inline void put_ima_ns(struct user_namespace *user_ns)
-{
-}
+#else
 
 static inline int create_ima_ns(struct user_namespace *user_ns)
 {
 #if CONFIG_IMA
-	user_ns->ima_ns = get_ima_ns(&init_ima_ns);
+	user_ns->ima_ns = &init_ima_ns;
 #endif
 	return 0;
 }
@@ -346,6 +333,9 @@ static inline struct ima_namespace *get_current_ns(void)
 	return &init_ima_ns;
 }
 
+static inline void put_ima_ns(struct user_namespace *user_ns)
+{
+}
 #endif /* CONFIG_IMA_NS */
 
 #if defined(CONFIG_IMA_APPRAISE) && defined(CONFIG_INTEGRITY_TRUSTED_KEYRING)
diff --git a/security/integrity/ima/ima_fs.c b/security/integrity/ima/ima_fs.c
index 778983fd9a73..583462b29cb5 100644
--- a/security/integrity/ima/ima_fs.c
+++ b/security/integrity/ima/ima_fs.c
@@ -386,7 +386,7 @@ static int ima_open_policy(struct inode *inode, struct file *filp)
 #else
 		if ((filp->f_flags & O_ACCMODE) != O_RDONLY)
 			return -EACCES;
-		if (!mac_admin_ns_capable(ns->user_ns))
+		if (!mac_admin_ns_capable(ima_user_ns(ns)))
 			return -EPERM;
 		return seq_open(filp, &ima_policy_seqops);
 #endif
diff --git a/security/integrity/ima/ima_init_ima_ns.c b/security/integrity/ima/ima_init_ima_ns.c
index 162c94e06d13..6ae6df037f03 100644
--- a/security/integrity/ima/ima_init_ima_ns.c
+++ b/security/integrity/ima/ima_init_ima_ns.c
@@ -62,12 +62,11 @@ int __init ima_ns_init(void)
 }
 
 struct ima_namespace init_ima_ns = {
-	.kref = KREF_INIT(1),
-	.user_ns = &init_user_ns,
 #ifdef CONFIG_IMA_QUEUE_EARLY_BOOT_KEYS
 	.ima_process_keys = false,
 	.ima_keys_lock = __MUTEX_INITIALIZER(init_ima_ns.ima_keys_lock),
 	.ima_keys = LIST_HEAD_INIT(init_ima_ns.ima_keys),
 #endif
+	.ima_keys_delayed_ref = REFCOUNT_INIT(1),
 };
 EXPORT_SYMBOL(init_ima_ns);
diff --git a/security/integrity/ima/ima_main.c b/security/integrity/ima/ima_main.c
index 70fa26b7bd3f..6ebc57cd91d3 100644
--- a/security/integrity/ima/ima_main.c
+++ b/security/integrity/ima/ima_main.c
@@ -410,7 +410,7 @@ static int process_measurement(struct ima_namespace *ns,
 			       u32 secid, char *buf, loff_t size, int mask,
 			       enum ima_hooks func)
 {
-	struct user_namespace *user_ns = ns->user_ns;
+	struct user_namespace *user_ns = ima_user_ns(ns);
 	int ret = 0;
 
 	while (user_ns) {
diff --git a/security/integrity/ima/ima_ns.c b/security/integrity/ima/ima_ns.c
index 6a0632806cdb..f96286ad0da8 100644
--- a/security/integrity/ima/ima_ns.c
+++ b/security/integrity/ima/ima_ns.c
@@ -31,9 +31,6 @@ int create_ima_ns(struct user_namespace *user_ns)
 		return -ENOMEM;
 	pr_debug("NEW     ima_ns: 0x%p\n", ns);
 
-	kref_init(&ns->kref);
-	ns->user_ns = user_ns;
-
 	err = ima_init_namespace(ns);
 	if (err)
 		goto fail_free;
@@ -44,6 +41,7 @@ int create_ima_ns(struct user_namespace *user_ns)
 	INIT_LIST_HEAD(&ns->ima_keys);
 #endif
 
+	refcount_set(&ns->ima_keys_delayed_ref, 1);
 	user_ns->ima_ns = ns;
 
 	return 0;
@@ -63,11 +61,8 @@ static void destroy_ima_ns(struct ima_namespace *ns)
 	kmem_cache_free(imans_cachep, ns);
 }
 
-void free_ima_ns(struct kref *kref)
+void free_ima_ns(struct ima_namespace *ns)
 {
-	struct ima_namespace *ns;
-
-	ns = container_of(kref, struct ima_namespace, kref);
 	if (WARN_ON(ns == &init_ima_ns))
 		return;
 
diff --git a/security/integrity/ima/ima_queue_keys.c b/security/integrity/ima/ima_queue_keys.c
index a6eb802e5ae4..d7c43e592e2c 100644
--- a/security/integrity/ima/ima_queue_keys.c
+++ b/security/integrity/ima/ima_queue_keys.c
@@ -14,6 +14,19 @@
 #include <keys/asymmetric-type.h>
 #include "ima.h"
 
+static inline void __get_delayed_ima_ns(struct ima_namespace *ima_ns)
+{
+	refcount_inc(&ima_ns->ima_keys_delayed_ref);
+}
+
+void __put_delayed_ima_ns(struct ima_namespace *ima_ns)
+{
+	if (ima_ns && refcount_dec_and_test(&ima_ns->ima_keys_delayed_ref)) {
+		pr_debug("DEREF   ima_ns: 0x%p  ctr: %d\n", ima_ns,
+			 refcount_read(&ima_ns->ima_keys_delayed_ref));
+		free_ima_ns(ima_ns);
+	}
+}
 
 /*
  * This worker function frees keys that may still be
@@ -26,8 +39,7 @@ void ima_keys_handler(struct work_struct *work)
 	ns = container_of(work, struct ima_namespace, ima_keys_delayed_work.work);
 	ns->timer_expired = true;
 	ima_process_queued_keys(ns);
-
-	put_user_ns(ns->user_ns);
+	__put_delayed_ima_ns(ns);
 }
 
 /*
@@ -36,9 +48,7 @@ void ima_keys_handler(struct work_struct *work)
  */
 void ima_init_key_queue(struct ima_namespace *ns)
 {
-	/* keep IMA namespace until delayed work is done */
-	get_user_ns(ns->user_ns);
-
+	__get_delayed_ima_ns(ns);
 	schedule_delayed_work(&ns->ima_keys_delayed_work,
 			      msecs_to_jiffies(ns->ima_key_queue_timeout));
 }
@@ -145,7 +155,7 @@ void ima_process_queued_keys(struct ima_namespace *ns)
 	if (!ns->timer_expired) {
 		if (cancel_delayed_work_sync(&ns->ima_keys_delayed_work))
 			/* undo reference from ima_init_key_queue */
-			put_user_ns(ns->user_ns);
+			__put_delayed_ima_ns(ns);
 	}
 
 	list_for_each_entry_safe(entry, tmp, &ns->ima_keys, list) {
-- 
2.30.2


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

* Re: [PATCH v5 14/16] ima: Use mac_admin_ns_capable() to check corresponding capability
  2021-12-09  7:22   ` Denis Semakin
@ 2021-12-09 13:23     ` James Bottomley
  0 siblings, 0 replies; 63+ messages in thread
From: James Bottomley @ 2021-12-09 13:23 UTC (permalink / raw)
  To: Denis Semakin, Stefan Berger, linux-integrity
  Cc: zohar, serge, christian.brauner, containers, dmitry.kasatkin,
	ebiederm, Krzysztof Struczynski, Roberto Sassu, mpeters, lhinds,
	lsturman, puiterwi, jamjoom, linux-kernel, paul, rgb,
	linux-security-module, jmorris

On Thu, 2021-12-09 at 07:22 +0000, Denis Semakin wrote:
> Hi. 
> My question won't be about capabilities. I'm wondering how IMA-ns
> which is associated with USER-ns and is created during USER-ns
> creation would be used by some namespaces orchestration systems, e.g.
> Kubernetes?

Orchestration systems that don't adopt the user namespace can't really
run containers requiring admin correctly without giving them root minus
some capabilities, which is rather unsafe, so the expectation is that
they'll all figure it out eventually for security reasons.

> .. It seems that it can be run without any user namespaces... 
> Their community just discuss this opportunity to support User
> namespaces. (see https://github.com/kubernetes/enhancements/pull/2101
> ) Looks like currently IMA-ns will not be applicable for Kubernetes.

Well, lets just say it adds one more reason to get kubernetes to
finally run rootless privileged containers correctly ...

James



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

* Re: [PATCH v5 15/16] ima: Move dentries into ima_namespace
  2021-12-08 22:18 ` [PATCH v5 15/16] ima: Move dentries into ima_namespace Stefan Berger
@ 2021-12-09 14:34   ` Christian Brauner
  2021-12-09 14:37     ` Christian Brauner
  0 siblings, 1 reply; 63+ messages in thread
From: Christian Brauner @ 2021-12-09 14:34 UTC (permalink / raw)
  To: Stefan Berger
  Cc: linux-integrity, zohar, serge, containers, dmitry.kasatkin,
	ebiederm, krzysztof.struczynski, roberto.sassu, mpeters, lhinds,
	lsturman, puiterwi, jejb, jamjoom, linux-kernel, paul, rgb,
	linux-security-module, jmorris

On Wed, Dec 08, 2021 at 05:18:17PM -0500, Stefan Berger wrote:
> Move the dentries into the ima_namespace for reuse by virtualized
> SecurityFS. Implement function freeing the dentries in order of
> files and symlinks before directories.
> 
> Signed-off-by: Stefan Berger <stefanb@linux.ibm.com>
> ---

This doesn't work as implemented, I think.

What I would have preferred and what I tried to explain in the earlier
review was:
Keep the dentry stashing global since it is only needed for init_ima_ns.
Then struct ima_namespace becomes way smaller and simpler.
If you do that then it makes sense to remove the additional dget() in
securityfs_create_dentry() for non-init_ima_ns.
Then you can rely on auto-cleanup in .kill_sb() or on
ima_securityfs_init() failure and you only need to call
ima_fs_ns_free_dentries() if ns != init_ima_ns.

IIuc, it seems you're currently doing one dput() too many since you're
calling securityfs_remove() in the error path for non-init_ima_ns which
relies on the previous increased dget() which we removed.

>  include/linux/ima.h             | 13 ++++++
>  security/integrity/ima/ima_fs.c | 72 ++++++++++++++++++---------------
>  2 files changed, 52 insertions(+), 33 deletions(-)
> 
> diff --git a/include/linux/ima.h b/include/linux/ima.h
> index 3aaf6e806db4..4dd64e318b15 100644
> --- a/include/linux/ima.h
> +++ b/include/linux/ima.h
> @@ -220,6 +220,17 @@ struct ima_h_table {
>  	struct hlist_head queue[IMA_MEASURE_HTABLE_SIZE];
>  };
>  
> +enum {
> +	IMAFS_DENTRY_DIR = 0,
> +	IMAFS_DENTRY_SYMLINK,
> +	IMAFS_DENTRY_BINARY_RUNTIME_MEASUREMENTS,
> +	IMAFS_DENTRY_ASCII_RUNTIME_MEASUREMENTS,
> +	IMAFS_DENTRY_RUNTIME_MEASUREMENTS_COUNT,
> +	IMAFS_DENTRY_VIOLATIONS,
> +	IMAFS_DENTRY_IMA_POLICY,
> +	IMAFS_DENTRY_LAST
> +};
> +
>  struct ima_namespace {
>  	struct kref kref;
>  	struct user_namespace *user_ns;
> @@ -266,6 +277,8 @@ struct ima_namespace {
>  	struct mutex ima_write_mutex;
>  	unsigned long ima_fs_flags;
>  	int valid_policy;
> +
> +	struct dentry *dentry[IMAFS_DENTRY_LAST];
>  };
>  
>  extern struct ima_namespace init_ima_ns;
> diff --git a/security/integrity/ima/ima_fs.c b/security/integrity/ima/ima_fs.c
> index a749a3e79304..3810d11fb463 100644
> --- a/security/integrity/ima/ima_fs.c
> +++ b/security/integrity/ima/ima_fs.c
> @@ -360,14 +360,6 @@ static ssize_t ima_write_policy(struct file *file, const char __user *buf,
>  	return result;
>  }
>  
> -static struct dentry *ima_dir;
> -static struct dentry *ima_symlink;
> -static struct dentry *binary_runtime_measurements;
> -static struct dentry *ascii_runtime_measurements;
> -static struct dentry *runtime_measurements_count;
> -static struct dentry *violations;
> -static struct dentry *ima_policy;
> -
>  enum ima_fs_flags {
>  	IMA_FS_BUSY,
>  };
> @@ -437,8 +429,8 @@ static int ima_release_policy(struct inode *inode, struct file *file)
>  
>  	ima_update_policy(ns);
>  #if !defined(CONFIG_IMA_WRITE_POLICY) && !defined(CONFIG_IMA_READ_POLICY)
> -	securityfs_remove(ima_policy);
> -	ima_policy = NULL;
> +	securityfs_remove(ns->dentry[IMAFS_DENTRY_IMA_POLICY]);
> +	ns->dentry[IMAFS_DENTRY_IMA_POLICY] = NULL;
>  #elif defined(CONFIG_IMA_WRITE_POLICY)
>  	clear_bit(IMA_FS_BUSY, &ns->ima_fs_flags);
>  #elif defined(CONFIG_IMA_READ_POLICY)
> @@ -455,58 +447,72 @@ static const struct file_operations ima_measure_policy_ops = {
>  	.llseek = generic_file_llseek,
>  };
>  
> -int __init ima_fs_init(void)
> +static void ima_fs_ns_free_dentries(struct ima_namespace *ns)
>  {
> -	ima_dir = securityfs_create_dir("ima", integrity_dir);
> -	if (IS_ERR(ima_dir))
> +	int i;
> +
> +	for (i = IMAFS_DENTRY_LAST - 1; i >= 0; i--)
> +		securityfs_remove(ns->dentry[i]);
> +
> +	memset(ns->dentry, 0, sizeof(ns->dentry));
> +}
> +
> +static int __init ima_securityfs_init(struct user_namespace *user_ns)
> +{
> +	struct ima_namespace *ns = user_ns->ima_ns;
> +	struct dentry *ima_dir;
> +
> +	ns->dentry[IMAFS_DENTRY_DIR] = securityfs_create_dir("ima", integrity_dir);
> +	if (IS_ERR(ns->dentry[IMAFS_DENTRY_DIR]))
>  		return -1;
> +	ima_dir = ns->dentry[IMAFS_DENTRY_DIR];
>  
> -	ima_symlink = securityfs_create_symlink("ima", NULL, "integrity/ima",
> -						NULL);
> -	if (IS_ERR(ima_symlink))
> +	ns->dentry[IMAFS_DENTRY_SYMLINK] =
> +	    securityfs_create_symlink("ima", NULL, "integrity/ima", NULL);
> +	if (IS_ERR(ns->dentry[IMAFS_DENTRY_SYMLINK]))
>  		goto out;
>  
> -	binary_runtime_measurements =
> +	ns->dentry[IMAFS_DENTRY_BINARY_RUNTIME_MEASUREMENTS] =
>  	    securityfs_create_file("binary_runtime_measurements",
>  				   S_IRUSR | S_IRGRP, ima_dir, NULL,
>  				   &ima_measurements_ops);
> -	if (IS_ERR(binary_runtime_measurements))
> +	if (IS_ERR(ns->dentry[IMAFS_DENTRY_BINARY_RUNTIME_MEASUREMENTS]))
>  		goto out;
>  
> -	ascii_runtime_measurements =
> +	ns->dentry[IMAFS_DENTRY_ASCII_RUNTIME_MEASUREMENTS] =
>  	    securityfs_create_file("ascii_runtime_measurements",
>  				   S_IRUSR | S_IRGRP, ima_dir, NULL,
>  				   &ima_ascii_measurements_ops);
> -	if (IS_ERR(ascii_runtime_measurements))
> +	if (IS_ERR(ns->dentry[IMAFS_DENTRY_ASCII_RUNTIME_MEASUREMENTS]))
>  		goto out;
>  
> -	runtime_measurements_count =
> +	ns->dentry[IMAFS_DENTRY_RUNTIME_MEASUREMENTS_COUNT] =
>  	    securityfs_create_file("runtime_measurements_count",
>  				   S_IRUSR | S_IRGRP, ima_dir, NULL,
>  				   &ima_measurements_count_ops);
> -	if (IS_ERR(runtime_measurements_count))
> +	if (IS_ERR(ns->dentry[IMAFS_DENTRY_RUNTIME_MEASUREMENTS_COUNT]))
>  		goto out;
>  
> -	violations =
> +	ns->dentry[IMAFS_DENTRY_VIOLATIONS] =
>  	    securityfs_create_file("violations", S_IRUSR | S_IRGRP,
>  				   ima_dir, NULL, &ima_htable_violations_ops);
> -	if (IS_ERR(violations))
> +	if (IS_ERR(ns->dentry[IMAFS_DENTRY_VIOLATIONS]))
>  		goto out;
>  
> -	ima_policy = securityfs_create_file("policy", POLICY_FILE_FLAGS,
> +	ns->dentry[IMAFS_DENTRY_IMA_POLICY] =
> +	    securityfs_create_file("policy", POLICY_FILE_FLAGS,
>  					    ima_dir, NULL,
>  					    &ima_measure_policy_ops);
> -	if (IS_ERR(ima_policy))
> +	if (IS_ERR(ns->dentry[IMAFS_DENTRY_IMA_POLICY]))
>  		goto out;
>  
>  	return 0;
>  out:
> -	securityfs_remove(violations);
> -	securityfs_remove(runtime_measurements_count);
> -	securityfs_remove(ascii_runtime_measurements);
> -	securityfs_remove(binary_runtime_measurements);
> -	securityfs_remove(ima_symlink);
> -	securityfs_remove(ima_dir);
> -	securityfs_remove(ima_policy);
> +	ima_fs_ns_free_dentries(ns);
>  	return -1;
>  }
> +
> +int __init ima_fs_init(void)
> +{
> +	return ima_securityfs_init(&init_user_ns);
> +}
> -- 
> 2.31.1
> 
> 

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

* Re: [PATCH v5 15/16] ima: Move dentries into ima_namespace
  2021-12-09 14:34   ` Christian Brauner
@ 2021-12-09 14:37     ` Christian Brauner
  2021-12-09 14:41       ` Christian Brauner
  2021-12-09 15:30       ` James Bottomley
  0 siblings, 2 replies; 63+ messages in thread
From: Christian Brauner @ 2021-12-09 14:37 UTC (permalink / raw)
  To: Stefan Berger
  Cc: linux-integrity, zohar, serge, containers, dmitry.kasatkin,
	ebiederm, krzysztof.struczynski, roberto.sassu, mpeters, lhinds,
	lsturman, puiterwi, jejb, jamjoom, linux-kernel, paul, rgb,
	linux-security-module, jmorris

On Thu, Dec 09, 2021 at 03:34:28PM +0100, Christian Brauner wrote:
> On Wed, Dec 08, 2021 at 05:18:17PM -0500, Stefan Berger wrote:
> > Move the dentries into the ima_namespace for reuse by virtualized
> > SecurityFS. Implement function freeing the dentries in order of
> > files and symlinks before directories.
> > 
> > Signed-off-by: Stefan Berger <stefanb@linux.ibm.com>
> > ---
> 
> This doesn't work as implemented, I think.
> 
> What I would have preferred and what I tried to explain in the earlier
> review was:
> Keep the dentry stashing global since it is only needed for init_ima_ns.
> Then struct ima_namespace becomes way smaller and simpler.
> If you do that then it makes sense to remove the additional dget() in
> securityfs_create_dentry() for non-init_ima_ns.
> Then you can rely on auto-cleanup in .kill_sb() or on
> ima_securityfs_init() failure and you only need to call
> ima_fs_ns_free_dentries() if ns != init_ima_ns.
> 
> IIuc, it seems you're currently doing one dput() too many since you're
> calling securityfs_remove() in the error path for non-init_ima_ns which
> relies on the previous increased dget() which we removed.

If you really want to move the dentry stashing into struct ima_namespace
even though it's really unnecessary then you may as well not care about
the auto-cleanup and keep that additional ima_fs_ns_free_dentries(ns)
call in .kill_sb(). But I really think not dragging dentry stashing into
struct ima_namespace is the correct way to go about this.

> 
> >  include/linux/ima.h             | 13 ++++++
> >  security/integrity/ima/ima_fs.c | 72 ++++++++++++++++++---------------
> >  2 files changed, 52 insertions(+), 33 deletions(-)
> > 
> > diff --git a/include/linux/ima.h b/include/linux/ima.h
> > index 3aaf6e806db4..4dd64e318b15 100644
> > --- a/include/linux/ima.h
> > +++ b/include/linux/ima.h
> > @@ -220,6 +220,17 @@ struct ima_h_table {
> >  	struct hlist_head queue[IMA_MEASURE_HTABLE_SIZE];
> >  };
> >  
> > +enum {
> > +	IMAFS_DENTRY_DIR = 0,
> > +	IMAFS_DENTRY_SYMLINK,
> > +	IMAFS_DENTRY_BINARY_RUNTIME_MEASUREMENTS,
> > +	IMAFS_DENTRY_ASCII_RUNTIME_MEASUREMENTS,
> > +	IMAFS_DENTRY_RUNTIME_MEASUREMENTS_COUNT,
> > +	IMAFS_DENTRY_VIOLATIONS,
> > +	IMAFS_DENTRY_IMA_POLICY,
> > +	IMAFS_DENTRY_LAST
> > +};
> > +
> >  struct ima_namespace {
> >  	struct kref kref;
> >  	struct user_namespace *user_ns;
> > @@ -266,6 +277,8 @@ struct ima_namespace {
> >  	struct mutex ima_write_mutex;
> >  	unsigned long ima_fs_flags;
> >  	int valid_policy;
> > +
> > +	struct dentry *dentry[IMAFS_DENTRY_LAST];
> >  };
> >  
> >  extern struct ima_namespace init_ima_ns;
> > diff --git a/security/integrity/ima/ima_fs.c b/security/integrity/ima/ima_fs.c
> > index a749a3e79304..3810d11fb463 100644
> > --- a/security/integrity/ima/ima_fs.c
> > +++ b/security/integrity/ima/ima_fs.c
> > @@ -360,14 +360,6 @@ static ssize_t ima_write_policy(struct file *file, const char __user *buf,
> >  	return result;
> >  }
> >  
> > -static struct dentry *ima_dir;
> > -static struct dentry *ima_symlink;
> > -static struct dentry *binary_runtime_measurements;
> > -static struct dentry *ascii_runtime_measurements;
> > -static struct dentry *runtime_measurements_count;
> > -static struct dentry *violations;
> > -static struct dentry *ima_policy;
> > -
> >  enum ima_fs_flags {
> >  	IMA_FS_BUSY,
> >  };
> > @@ -437,8 +429,8 @@ static int ima_release_policy(struct inode *inode, struct file *file)
> >  
> >  	ima_update_policy(ns);
> >  #if !defined(CONFIG_IMA_WRITE_POLICY) && !defined(CONFIG_IMA_READ_POLICY)
> > -	securityfs_remove(ima_policy);
> > -	ima_policy = NULL;
> > +	securityfs_remove(ns->dentry[IMAFS_DENTRY_IMA_POLICY]);
> > +	ns->dentry[IMAFS_DENTRY_IMA_POLICY] = NULL;
> >  #elif defined(CONFIG_IMA_WRITE_POLICY)
> >  	clear_bit(IMA_FS_BUSY, &ns->ima_fs_flags);
> >  #elif defined(CONFIG_IMA_READ_POLICY)
> > @@ -455,58 +447,72 @@ static const struct file_operations ima_measure_policy_ops = {
> >  	.llseek = generic_file_llseek,
> >  };
> >  
> > -int __init ima_fs_init(void)
> > +static void ima_fs_ns_free_dentries(struct ima_namespace *ns)
> >  {
> > -	ima_dir = securityfs_create_dir("ima", integrity_dir);
> > -	if (IS_ERR(ima_dir))
> > +	int i;
> > +
> > +	for (i = IMAFS_DENTRY_LAST - 1; i >= 0; i--)
> > +		securityfs_remove(ns->dentry[i]);
> > +
> > +	memset(ns->dentry, 0, sizeof(ns->dentry));
> > +}
> > +
> > +static int __init ima_securityfs_init(struct user_namespace *user_ns)
> > +{
> > +	struct ima_namespace *ns = user_ns->ima_ns;
> > +	struct dentry *ima_dir;
> > +
> > +	ns->dentry[IMAFS_DENTRY_DIR] = securityfs_create_dir("ima", integrity_dir);
> > +	if (IS_ERR(ns->dentry[IMAFS_DENTRY_DIR]))
> >  		return -1;
> > +	ima_dir = ns->dentry[IMAFS_DENTRY_DIR];
> >  
> > -	ima_symlink = securityfs_create_symlink("ima", NULL, "integrity/ima",
> > -						NULL);
> > -	if (IS_ERR(ima_symlink))
> > +	ns->dentry[IMAFS_DENTRY_SYMLINK] =
> > +	    securityfs_create_symlink("ima", NULL, "integrity/ima", NULL);
> > +	if (IS_ERR(ns->dentry[IMAFS_DENTRY_SYMLINK]))
> >  		goto out;
> >  
> > -	binary_runtime_measurements =
> > +	ns->dentry[IMAFS_DENTRY_BINARY_RUNTIME_MEASUREMENTS] =
> >  	    securityfs_create_file("binary_runtime_measurements",
> >  				   S_IRUSR | S_IRGRP, ima_dir, NULL,
> >  				   &ima_measurements_ops);
> > -	if (IS_ERR(binary_runtime_measurements))
> > +	if (IS_ERR(ns->dentry[IMAFS_DENTRY_BINARY_RUNTIME_MEASUREMENTS]))
> >  		goto out;
> >  
> > -	ascii_runtime_measurements =
> > +	ns->dentry[IMAFS_DENTRY_ASCII_RUNTIME_MEASUREMENTS] =
> >  	    securityfs_create_file("ascii_runtime_measurements",
> >  				   S_IRUSR | S_IRGRP, ima_dir, NULL,
> >  				   &ima_ascii_measurements_ops);
> > -	if (IS_ERR(ascii_runtime_measurements))
> > +	if (IS_ERR(ns->dentry[IMAFS_DENTRY_ASCII_RUNTIME_MEASUREMENTS]))
> >  		goto out;
> >  
> > -	runtime_measurements_count =
> > +	ns->dentry[IMAFS_DENTRY_RUNTIME_MEASUREMENTS_COUNT] =
> >  	    securityfs_create_file("runtime_measurements_count",
> >  				   S_IRUSR | S_IRGRP, ima_dir, NULL,
> >  				   &ima_measurements_count_ops);
> > -	if (IS_ERR(runtime_measurements_count))
> > +	if (IS_ERR(ns->dentry[IMAFS_DENTRY_RUNTIME_MEASUREMENTS_COUNT]))
> >  		goto out;
> >  
> > -	violations =
> > +	ns->dentry[IMAFS_DENTRY_VIOLATIONS] =
> >  	    securityfs_create_file("violations", S_IRUSR | S_IRGRP,
> >  				   ima_dir, NULL, &ima_htable_violations_ops);
> > -	if (IS_ERR(violations))
> > +	if (IS_ERR(ns->dentry[IMAFS_DENTRY_VIOLATIONS]))
> >  		goto out;
> >  
> > -	ima_policy = securityfs_create_file("policy", POLICY_FILE_FLAGS,
> > +	ns->dentry[IMAFS_DENTRY_IMA_POLICY] =
> > +	    securityfs_create_file("policy", POLICY_FILE_FLAGS,
> >  					    ima_dir, NULL,
> >  					    &ima_measure_policy_ops);
> > -	if (IS_ERR(ima_policy))
> > +	if (IS_ERR(ns->dentry[IMAFS_DENTRY_IMA_POLICY]))
> >  		goto out;
> >  
> >  	return 0;
> >  out:
> > -	securityfs_remove(violations);
> > -	securityfs_remove(runtime_measurements_count);
> > -	securityfs_remove(ascii_runtime_measurements);
> > -	securityfs_remove(binary_runtime_measurements);
> > -	securityfs_remove(ima_symlink);
> > -	securityfs_remove(ima_dir);
> > -	securityfs_remove(ima_policy);
> > +	ima_fs_ns_free_dentries(ns);
> >  	return -1;
> >  }
> > +
> > +int __init ima_fs_init(void)
> > +{
> > +	return ima_securityfs_init(&init_user_ns);
> > +}
> > -- 
> > 2.31.1
> > 
> > 
> 

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

* Re: [PATCH v5 15/16] ima: Move dentries into ima_namespace
  2021-12-09 14:37     ` Christian Brauner
@ 2021-12-09 14:41       ` Christian Brauner
  2021-12-09 15:00         ` Stefan Berger
  2021-12-09 15:30       ` James Bottomley
  1 sibling, 1 reply; 63+ messages in thread
From: Christian Brauner @ 2021-12-09 14:41 UTC (permalink / raw)
  To: Stefan Berger
  Cc: linux-integrity, zohar, serge, containers, dmitry.kasatkin,
	ebiederm, krzysztof.struczynski, roberto.sassu, mpeters, lhinds,
	lsturman, puiterwi, jejb, jamjoom, linux-kernel, paul, rgb,
	linux-security-module, jmorris

On Thu, Dec 09, 2021 at 03:37:49PM +0100, Christian Brauner wrote:
> On Thu, Dec 09, 2021 at 03:34:28PM +0100, Christian Brauner wrote:
> > On Wed, Dec 08, 2021 at 05:18:17PM -0500, Stefan Berger wrote:
> > > Move the dentries into the ima_namespace for reuse by virtualized
> > > SecurityFS. Implement function freeing the dentries in order of
> > > files and symlinks before directories.
> > > 
> > > Signed-off-by: Stefan Berger <stefanb@linux.ibm.com>
> > > ---
> > 
> > This doesn't work as implemented, I think.
> > 
> > What I would have preferred and what I tried to explain in the earlier
> > review was:
> > Keep the dentry stashing global since it is only needed for init_ima_ns.
> > Then struct ima_namespace becomes way smaller and simpler.
> > If you do that then it makes sense to remove the additional dget() in
> > securityfs_create_dentry() for non-init_ima_ns.
> > Then you can rely on auto-cleanup in .kill_sb() or on
> > ima_securityfs_init() failure and you only need to call
> > ima_fs_ns_free_dentries() if ns != init_ima_ns.

s/ns != init_ima_ns/ns == init_ima_ns/

> > 
> > IIuc, it seems you're currently doing one dput() too many since you're
> > calling securityfs_remove() in the error path for non-init_ima_ns which
> > relies on the previous increased dget() which we removed.
> 
> If you really want to move the dentry stashing into struct ima_namespace
> even though it's really unnecessary then you may as well not care about
> the auto-cleanup and keep that additional ima_fs_ns_free_dentries(ns)
> call in .kill_sb(). But I really think not dragging dentry stashing into
> struct ima_namespace is the correct way to go about this.
> 
> > 
> > >  include/linux/ima.h             | 13 ++++++
> > >  security/integrity/ima/ima_fs.c | 72 ++++++++++++++++++---------------
> > >  2 files changed, 52 insertions(+), 33 deletions(-)
> > > 
> > > diff --git a/include/linux/ima.h b/include/linux/ima.h
> > > index 3aaf6e806db4..4dd64e318b15 100644
> > > --- a/include/linux/ima.h
> > > +++ b/include/linux/ima.h
> > > @@ -220,6 +220,17 @@ struct ima_h_table {
> > >  	struct hlist_head queue[IMA_MEASURE_HTABLE_SIZE];
> > >  };
> > >  
> > > +enum {
> > > +	IMAFS_DENTRY_DIR = 0,
> > > +	IMAFS_DENTRY_SYMLINK,
> > > +	IMAFS_DENTRY_BINARY_RUNTIME_MEASUREMENTS,
> > > +	IMAFS_DENTRY_ASCII_RUNTIME_MEASUREMENTS,
> > > +	IMAFS_DENTRY_RUNTIME_MEASUREMENTS_COUNT,
> > > +	IMAFS_DENTRY_VIOLATIONS,
> > > +	IMAFS_DENTRY_IMA_POLICY,
> > > +	IMAFS_DENTRY_LAST
> > > +};
> > > +
> > >  struct ima_namespace {
> > >  	struct kref kref;
> > >  	struct user_namespace *user_ns;
> > > @@ -266,6 +277,8 @@ struct ima_namespace {
> > >  	struct mutex ima_write_mutex;
> > >  	unsigned long ima_fs_flags;
> > >  	int valid_policy;
> > > +
> > > +	struct dentry *dentry[IMAFS_DENTRY_LAST];
> > >  };
> > >  
> > >  extern struct ima_namespace init_ima_ns;
> > > diff --git a/security/integrity/ima/ima_fs.c b/security/integrity/ima/ima_fs.c
> > > index a749a3e79304..3810d11fb463 100644
> > > --- a/security/integrity/ima/ima_fs.c
> > > +++ b/security/integrity/ima/ima_fs.c
> > > @@ -360,14 +360,6 @@ static ssize_t ima_write_policy(struct file *file, const char __user *buf,
> > >  	return result;
> > >  }
> > >  
> > > -static struct dentry *ima_dir;
> > > -static struct dentry *ima_symlink;
> > > -static struct dentry *binary_runtime_measurements;
> > > -static struct dentry *ascii_runtime_measurements;
> > > -static struct dentry *runtime_measurements_count;
> > > -static struct dentry *violations;
> > > -static struct dentry *ima_policy;
> > > -
> > >  enum ima_fs_flags {
> > >  	IMA_FS_BUSY,
> > >  };
> > > @@ -437,8 +429,8 @@ static int ima_release_policy(struct inode *inode, struct file *file)
> > >  
> > >  	ima_update_policy(ns);
> > >  #if !defined(CONFIG_IMA_WRITE_POLICY) && !defined(CONFIG_IMA_READ_POLICY)
> > > -	securityfs_remove(ima_policy);
> > > -	ima_policy = NULL;
> > > +	securityfs_remove(ns->dentry[IMAFS_DENTRY_IMA_POLICY]);
> > > +	ns->dentry[IMAFS_DENTRY_IMA_POLICY] = NULL;
> > >  #elif defined(CONFIG_IMA_WRITE_POLICY)
> > >  	clear_bit(IMA_FS_BUSY, &ns->ima_fs_flags);
> > >  #elif defined(CONFIG_IMA_READ_POLICY)
> > > @@ -455,58 +447,72 @@ static const struct file_operations ima_measure_policy_ops = {
> > >  	.llseek = generic_file_llseek,
> > >  };
> > >  
> > > -int __init ima_fs_init(void)
> > > +static void ima_fs_ns_free_dentries(struct ima_namespace *ns)
> > >  {
> > > -	ima_dir = securityfs_create_dir("ima", integrity_dir);
> > > -	if (IS_ERR(ima_dir))
> > > +	int i;
> > > +
> > > +	for (i = IMAFS_DENTRY_LAST - 1; i >= 0; i--)
> > > +		securityfs_remove(ns->dentry[i]);
> > > +
> > > +	memset(ns->dentry, 0, sizeof(ns->dentry));
> > > +}
> > > +
> > > +static int __init ima_securityfs_init(struct user_namespace *user_ns)
> > > +{
> > > +	struct ima_namespace *ns = user_ns->ima_ns;
> > > +	struct dentry *ima_dir;
> > > +
> > > +	ns->dentry[IMAFS_DENTRY_DIR] = securityfs_create_dir("ima", integrity_dir);
> > > +	if (IS_ERR(ns->dentry[IMAFS_DENTRY_DIR]))
> > >  		return -1;
> > > +	ima_dir = ns->dentry[IMAFS_DENTRY_DIR];
> > >  
> > > -	ima_symlink = securityfs_create_symlink("ima", NULL, "integrity/ima",
> > > -						NULL);
> > > -	if (IS_ERR(ima_symlink))
> > > +	ns->dentry[IMAFS_DENTRY_SYMLINK] =
> > > +	    securityfs_create_symlink("ima", NULL, "integrity/ima", NULL);
> > > +	if (IS_ERR(ns->dentry[IMAFS_DENTRY_SYMLINK]))
> > >  		goto out;
> > >  
> > > -	binary_runtime_measurements =
> > > +	ns->dentry[IMAFS_DENTRY_BINARY_RUNTIME_MEASUREMENTS] =
> > >  	    securityfs_create_file("binary_runtime_measurements",
> > >  				   S_IRUSR | S_IRGRP, ima_dir, NULL,
> > >  				   &ima_measurements_ops);
> > > -	if (IS_ERR(binary_runtime_measurements))
> > > +	if (IS_ERR(ns->dentry[IMAFS_DENTRY_BINARY_RUNTIME_MEASUREMENTS]))
> > >  		goto out;
> > >  
> > > -	ascii_runtime_measurements =
> > > +	ns->dentry[IMAFS_DENTRY_ASCII_RUNTIME_MEASUREMENTS] =
> > >  	    securityfs_create_file("ascii_runtime_measurements",
> > >  				   S_IRUSR | S_IRGRP, ima_dir, NULL,
> > >  				   &ima_ascii_measurements_ops);
> > > -	if (IS_ERR(ascii_runtime_measurements))
> > > +	if (IS_ERR(ns->dentry[IMAFS_DENTRY_ASCII_RUNTIME_MEASUREMENTS]))
> > >  		goto out;
> > >  
> > > -	runtime_measurements_count =
> > > +	ns->dentry[IMAFS_DENTRY_RUNTIME_MEASUREMENTS_COUNT] =
> > >  	    securityfs_create_file("runtime_measurements_count",
> > >  				   S_IRUSR | S_IRGRP, ima_dir, NULL,
> > >  				   &ima_measurements_count_ops);
> > > -	if (IS_ERR(runtime_measurements_count))
> > > +	if (IS_ERR(ns->dentry[IMAFS_DENTRY_RUNTIME_MEASUREMENTS_COUNT]))
> > >  		goto out;
> > >  
> > > -	violations =
> > > +	ns->dentry[IMAFS_DENTRY_VIOLATIONS] =
> > >  	    securityfs_create_file("violations", S_IRUSR | S_IRGRP,
> > >  				   ima_dir, NULL, &ima_htable_violations_ops);
> > > -	if (IS_ERR(violations))
> > > +	if (IS_ERR(ns->dentry[IMAFS_DENTRY_VIOLATIONS]))
> > >  		goto out;
> > >  
> > > -	ima_policy = securityfs_create_file("policy", POLICY_FILE_FLAGS,
> > > +	ns->dentry[IMAFS_DENTRY_IMA_POLICY] =
> > > +	    securityfs_create_file("policy", POLICY_FILE_FLAGS,
> > >  					    ima_dir, NULL,
> > >  					    &ima_measure_policy_ops);
> > > -	if (IS_ERR(ima_policy))
> > > +	if (IS_ERR(ns->dentry[IMAFS_DENTRY_IMA_POLICY]))
> > >  		goto out;
> > >  
> > >  	return 0;
> > >  out:
> > > -	securityfs_remove(violations);
> > > -	securityfs_remove(runtime_measurements_count);
> > > -	securityfs_remove(ascii_runtime_measurements);
> > > -	securityfs_remove(binary_runtime_measurements);
> > > -	securityfs_remove(ima_symlink);
> > > -	securityfs_remove(ima_dir);
> > > -	securityfs_remove(ima_policy);
> > > +	ima_fs_ns_free_dentries(ns);
> > >  	return -1;
> > >  }
> > > +
> > > +int __init ima_fs_init(void)
> > > +{
> > > +	return ima_securityfs_init(&init_user_ns);
> > > +}
> > > -- 
> > > 2.31.1
> > > 
> > > 
> > 
> 

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

* Re: [PATCH v5 15/16] ima: Move dentries into ima_namespace
  2021-12-09 14:41       ` Christian Brauner
@ 2021-12-09 15:00         ` Stefan Berger
  2021-12-09 15:47           ` Christian Brauner
  0 siblings, 1 reply; 63+ messages in thread
From: Stefan Berger @ 2021-12-09 15:00 UTC (permalink / raw)
  To: Christian Brauner
  Cc: linux-integrity, zohar, serge, containers, dmitry.kasatkin,
	ebiederm, krzysztof.struczynski, roberto.sassu, mpeters, lhinds,
	lsturman, puiterwi, jejb, jamjoom, linux-kernel, paul, rgb,
	linux-security-module, jmorris


On 12/9/21 09:41, Christian Brauner wrote:
> On Thu, Dec 09, 2021 at 03:37:49PM +0100, Christian Brauner wrote:
>> On Thu, Dec 09, 2021 at 03:34:28PM +0100, Christian Brauner wrote:
>>> On Wed, Dec 08, 2021 at 05:18:17PM -0500, Stefan Berger wrote:
>>>> Move the dentries into the ima_namespace for reuse by virtualized
>>>> SecurityFS. Implement function freeing the dentries in order of
>>>> files and symlinks before directories.
>>>>
>>>> Signed-off-by: Stefan Berger <stefanb@linux.ibm.com>
>>>> ---
>>> This doesn't work as implemented, I think.
>>>
>>> What I would have preferred and what I tried to explain in the earlier
>>> review was:
>>> Keep the dentry stashing global since it is only needed for init_ima_ns.
>>> Then struct ima_namespace becomes way smaller and simpler.
>>> If you do that then it makes sense to remove the additional dget() in
>>> securityfs_create_dentry() for non-init_ima_ns.
>>> Then you can rely on auto-cleanup in .kill_sb() or on
>>> ima_securityfs_init() failure and you only need to call
>>> ima_fs_ns_free_dentries() if ns != init_ima_ns.
> s/ns != init_ima_ns/ns == init_ima_ns/
>
>>> IIuc, it seems you're currently doing one dput() too many since you're
>>> calling securityfs_remove() in the error path for non-init_ima_ns which
>>> relies on the previous increased dget() which we removed.

I thought that securityfs_remove() will now simply influence when a 
dentry is removed and freed. If we call it in the error cleanup path in 
non-init_user_ns case it would go away right there and leave nothing to 
do for .kill_sb() while an additional dget() would require the cleanup 
as well but do another cleanup then in .kill_sb() since that brings the 
reference count to 0 via the dput()s that it does. Am I wrong on this?


>> If you really want to move the dentry stashing into struct ima_namespace
>> even though it's really unnecessary then you may as well not care about
>> the auto-cleanup and keep that additional ima_fs_ns_free_dentries(ns)
>> call in .kill_sb(). But I really think not dragging dentry stashing into
>> struct ima_namespace is the correct way to go about this.


I moved the dentries into the ima_namespace so that each namespace holds 
a pointer to the dentries it owns and isolates them. We certainly 
wouldn't want to have IMA namespaces write over the current static 
variables and create a mess with what these are pointing to ( 
https://elixir.bootlin.com/linux/latest/source/security/integrity/ima/ima_fs.c#L359 
) and possible race conditions when doing parallel initialization (if 
that's possible at all). This also reduces the code size and we don't 
need two different implementations for init_user_ns and 
non-init_user_ns. So I don't quite understand whey we wouldn't want to 
have the dentries isolated via ima_namespace?


>>
>>>>   include/linux/ima.h             | 13 ++++++
>>>>   security/integrity/ima/ima_fs.c | 72 ++++++++++++++++++---------------
>>>>   2 files changed, 52 insertions(+), 33 deletions(-)
>>>>
>>>> diff --git a/include/linux/ima.h b/include/linux/ima.h
>>>> index 3aaf6e806db4..4dd64e318b15 100644
>>>> --- a/include/linux/ima.h
>>>> +++ b/include/linux/ima.h
>>>> @@ -220,6 +220,17 @@ struct ima_h_table {
>>>>   	struct hlist_head queue[IMA_MEASURE_HTABLE_SIZE];
>>>>   };
>>>>   
>>>> +enum {
>>>> +	IMAFS_DENTRY_DIR = 0,
>>>> +	IMAFS_DENTRY_SYMLINK,
>>>> +	IMAFS_DENTRY_BINARY_RUNTIME_MEASUREMENTS,
>>>> +	IMAFS_DENTRY_ASCII_RUNTIME_MEASUREMENTS,
>>>> +	IMAFS_DENTRY_RUNTIME_MEASUREMENTS_COUNT,
>>>> +	IMAFS_DENTRY_VIOLATIONS,
>>>> +	IMAFS_DENTRY_IMA_POLICY,
>>>> +	IMAFS_DENTRY_LAST
>>>> +};
>>>> +
>>>>   struct ima_namespace {
>>>>   	struct kref kref;
>>>>   	struct user_namespace *user_ns;
>>>> @@ -266,6 +277,8 @@ struct ima_namespace {
>>>>   	struct mutex ima_write_mutex;
>>>>   	unsigned long ima_fs_flags;
>>>>   	int valid_policy;
>>>> +
>>>> +	struct dentry *dentry[IMAFS_DENTRY_LAST];
>>>>   };
>>>>   
>>>>   extern struct ima_namespace init_ima_ns;
>>>> diff --git a/security/integrity/ima/ima_fs.c b/security/integrity/ima/ima_fs.c
>>>> index a749a3e79304..3810d11fb463 100644
>>>> --- a/security/integrity/ima/ima_fs.c
>>>> +++ b/security/integrity/ima/ima_fs.c
>>>> @@ -360,14 +360,6 @@ static ssize_t ima_write_policy(struct file *file, const char __user *buf,
>>>>   	return result;
>>>>   }
>>>>   
>>>> -static struct dentry *ima_dir;
>>>> -static struct dentry *ima_symlink;
>>>> -static struct dentry *binary_runtime_measurements;
>>>> -static struct dentry *ascii_runtime_measurements;
>>>> -static struct dentry *runtime_measurements_count;
>>>> -static struct dentry *violations;
>>>> -static struct dentry *ima_policy;
>>>> -
>>>>   enum ima_fs_flags {
>>>>   	IMA_FS_BUSY,
>>>>   };
>>>> @@ -437,8 +429,8 @@ static int ima_release_policy(struct inode *inode, struct file *file)
>>>>   
>>>>   	ima_update_policy(ns);
>>>>   #if !defined(CONFIG_IMA_WRITE_POLICY) && !defined(CONFIG_IMA_READ_POLICY)
>>>> -	securityfs_remove(ima_policy);
>>>> -	ima_policy = NULL;
>>>> +	securityfs_remove(ns->dentry[IMAFS_DENTRY_IMA_POLICY]);
>>>> +	ns->dentry[IMAFS_DENTRY_IMA_POLICY] = NULL;
>>>>   #elif defined(CONFIG_IMA_WRITE_POLICY)
>>>>   	clear_bit(IMA_FS_BUSY, &ns->ima_fs_flags);
>>>>   #elif defined(CONFIG_IMA_READ_POLICY)
>>>> @@ -455,58 +447,72 @@ static const struct file_operations ima_measure_policy_ops = {
>>>>   	.llseek = generic_file_llseek,
>>>>   };
>>>>   
>>>> -int __init ima_fs_init(void)
>>>> +static void ima_fs_ns_free_dentries(struct ima_namespace *ns)
>>>>   {
>>>> -	ima_dir = securityfs_create_dir("ima", integrity_dir);
>>>> -	if (IS_ERR(ima_dir))
>>>> +	int i;
>>>> +
>>>> +	for (i = IMAFS_DENTRY_LAST - 1; i >= 0; i--)
>>>> +		securityfs_remove(ns->dentry[i]);
>>>> +
>>>> +	memset(ns->dentry, 0, sizeof(ns->dentry));
>>>> +}
>>>> +
>>>> +static int __init ima_securityfs_init(struct user_namespace *user_ns)
>>>> +{
>>>> +	struct ima_namespace *ns = user_ns->ima_ns;
>>>> +	struct dentry *ima_dir;
>>>> +
>>>> +	ns->dentry[IMAFS_DENTRY_DIR] = securityfs_create_dir("ima", integrity_dir);
>>>> +	if (IS_ERR(ns->dentry[IMAFS_DENTRY_DIR]))
>>>>   		return -1;
>>>> +	ima_dir = ns->dentry[IMAFS_DENTRY_DIR];
>>>>   
>>>> -	ima_symlink = securityfs_create_symlink("ima", NULL, "integrity/ima",
>>>> -						NULL);
>>>> -	if (IS_ERR(ima_symlink))
>>>> +	ns->dentry[IMAFS_DENTRY_SYMLINK] =
>>>> +	    securityfs_create_symlink("ima", NULL, "integrity/ima", NULL);
>>>> +	if (IS_ERR(ns->dentry[IMAFS_DENTRY_SYMLINK]))
>>>>   		goto out;
>>>>   
>>>> -	binary_runtime_measurements =
>>>> +	ns->dentry[IMAFS_DENTRY_BINARY_RUNTIME_MEASUREMENTS] =
>>>>   	    securityfs_create_file("binary_runtime_measurements",
>>>>   				   S_IRUSR | S_IRGRP, ima_dir, NULL,
>>>>   				   &ima_measurements_ops);
>>>> -	if (IS_ERR(binary_runtime_measurements))
>>>> +	if (IS_ERR(ns->dentry[IMAFS_DENTRY_BINARY_RUNTIME_MEASUREMENTS]))
>>>>   		goto out;
>>>>   
>>>> -	ascii_runtime_measurements =
>>>> +	ns->dentry[IMAFS_DENTRY_ASCII_RUNTIME_MEASUREMENTS] =
>>>>   	    securityfs_create_file("ascii_runtime_measurements",
>>>>   				   S_IRUSR | S_IRGRP, ima_dir, NULL,
>>>>   				   &ima_ascii_measurements_ops);
>>>> -	if (IS_ERR(ascii_runtime_measurements))
>>>> +	if (IS_ERR(ns->dentry[IMAFS_DENTRY_ASCII_RUNTIME_MEASUREMENTS]))
>>>>   		goto out;
>>>>   
>>>> -	runtime_measurements_count =
>>>> +	ns->dentry[IMAFS_DENTRY_RUNTIME_MEASUREMENTS_COUNT] =
>>>>   	    securityfs_create_file("runtime_measurements_count",
>>>>   				   S_IRUSR | S_IRGRP, ima_dir, NULL,
>>>>   				   &ima_measurements_count_ops);
>>>> -	if (IS_ERR(runtime_measurements_count))
>>>> +	if (IS_ERR(ns->dentry[IMAFS_DENTRY_RUNTIME_MEASUREMENTS_COUNT]))
>>>>   		goto out;
>>>>   
>>>> -	violations =
>>>> +	ns->dentry[IMAFS_DENTRY_VIOLATIONS] =
>>>>   	    securityfs_create_file("violations", S_IRUSR | S_IRGRP,
>>>>   				   ima_dir, NULL, &ima_htable_violations_ops);
>>>> -	if (IS_ERR(violations))
>>>> +	if (IS_ERR(ns->dentry[IMAFS_DENTRY_VIOLATIONS]))
>>>>   		goto out;
>>>>   
>>>> -	ima_policy = securityfs_create_file("policy", POLICY_FILE_FLAGS,
>>>> +	ns->dentry[IMAFS_DENTRY_IMA_POLICY] =
>>>> +	    securityfs_create_file("policy", POLICY_FILE_FLAGS,
>>>>   					    ima_dir, NULL,
>>>>   					    &ima_measure_policy_ops);
>>>> -	if (IS_ERR(ima_policy))
>>>> +	if (IS_ERR(ns->dentry[IMAFS_DENTRY_IMA_POLICY]))
>>>>   		goto out;
>>>>   
>>>>   	return 0;
>>>>   out:
>>>> -	securityfs_remove(violations);
>>>> -	securityfs_remove(runtime_measurements_count);
>>>> -	securityfs_remove(ascii_runtime_measurements);
>>>> -	securityfs_remove(binary_runtime_measurements);
>>>> -	securityfs_remove(ima_symlink);
>>>> -	securityfs_remove(ima_dir);
>>>> -	securityfs_remove(ima_policy);
>>>> +	ima_fs_ns_free_dentries(ns);
>>>>   	return -1;
>>>>   }
>>>> +
>>>> +int __init ima_fs_init(void)
>>>> +{
>>>> +	return ima_securityfs_init(&init_user_ns);
>>>> +}
>>>> -- 
>>>> 2.31.1
>>>>
>>>>

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

* Re: [PATCH v5 04/16] ima: Move delayed work queue and variables into ima_namespace
  2021-12-09 13:11   ` Christian Brauner
@ 2021-12-09 15:09     ` Stefan Berger
  0 siblings, 0 replies; 63+ messages in thread
From: Stefan Berger @ 2021-12-09 15:09 UTC (permalink / raw)
  To: Christian Brauner
  Cc: linux-integrity, zohar, serge, containers, dmitry.kasatkin,
	ebiederm, krzysztof.struczynski, roberto.sassu, mpeters, lhinds,
	lsturman, puiterwi, jejb, jamjoom, linux-kernel, paul, rgb,
	linux-security-module, jmorris


On 12/9/21 08:11, Christian Brauner wrote:
> On Wed, Dec 08, 2021 at 05:18:06PM -0500, Stefan Berger wrote:
>> Move the delayed work queue and associated variables to the
>> ima_namespace and initialize them.
>>
>> Since keys queued up for measurement currently are only relevant in the
>> init_ima_ns, call ima_init_key_queue() only when the init_ima_ns is
>> initialized.
>>
>> Protect the ima_namespace when scheduling the delayed work by taking an
>> additional reference to its user namespace. Put the reference when either
>> the delayed work has completed or when it was cancelled but hadn't run.
>>
>> Signed-off-by: Stefan Berger <stefanb@linux.ibm.com>
>> ---
>>   include/linux/ima.h                      | 11 +++++++
>>   security/integrity/ima/ima.h             | 12 ++++---
>>   security/integrity/ima/ima_fs.c          |  4 ++-
>>   security/integrity/ima/ima_init.c        |  2 --
>>   security/integrity/ima/ima_init_ima_ns.c |  8 +++++
>>   security/integrity/ima/ima_policy.c      |  4 +--
>>   security/integrity/ima/ima_queue_keys.c  | 42 +++++++++++++-----------
>>   7 files changed, 53 insertions(+), 30 deletions(-)
>>
>> diff --git a/include/linux/ima.h b/include/linux/ima.h
>> index 9f6de36240b0..529defe4d272 100644
>> --- a/include/linux/ima.h
>> +++ b/include/linux/ima.h
>> @@ -217,6 +217,17 @@ struct ima_namespace {
>>   	struct rb_root ns_status_tree;
>>   	rwlock_t ns_status_lock;
>>   	struct kmem_cache *ns_status_cache;
>> +
>> +#ifdef CONFIG_IMA_QUEUE_EARLY_BOOT_KEYS
>> +	/*
>> +	 * If custom IMA policy is not loaded then keys queued up
>> +	 * for measurement should be freed. This worker is used
>> +	 * for handling this scenario.
>> +	 */
>> +	struct delayed_work ima_keys_delayed_work;
>> +	long ima_key_queue_timeout;
>> +	bool timer_expired;
>> +#endif
>>   };
>>   
>>   extern struct ima_namespace init_ima_ns;
>> diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h
>> index dd06e16c4e1c..9edab9050dc7 100644
>> --- a/security/integrity/ima/ima.h
>> +++ b/security/integrity/ima/ima.h
>> @@ -77,6 +77,8 @@ struct ima_field_data {
>>   	u32 len;
>>   };
>>   
>> +struct ima_namespace;
>> +
>>   /* IMA template field definition */
>>   struct ima_template_field {
>>   	const char field_id[IMA_TEMPLATE_FIELD_ID_MAX_LEN];
>> @@ -247,18 +249,18 @@ struct ima_key_entry {
>>   	size_t payload_len;
>>   	char *keyring_name;
>>   };
>> -void ima_init_key_queue(void);
>> +void ima_init_key_queue(struct ima_namespace *ns);
>>   bool ima_should_queue_key(void);
>>   bool ima_queue_key(struct key *keyring, const void *payload,
>>   		   size_t payload_len);
>> -void ima_process_queued_keys(void);
>> +void ima_process_queued_keys(struct ima_namespace *ns);
>> +void ima_keys_handler(struct work_struct *work);
>>   #else
>> -static inline void ima_init_key_queue(void) {}
>>   static inline bool ima_should_queue_key(void) { return false; }
>>   static inline bool ima_queue_key(struct key *keyring,
>>   				 const void *payload,
>>   				 size_t payload_len) { return false; }
>> -static inline void ima_process_queued_keys(void) {}
>> +static inline void ima_process_queued_keys(struct ima_namespace *ns) {}
>>   #endif /* CONFIG_IMA_QUEUE_EARLY_BOOT_KEYS */
>>   
>>   /* LIM API function definitions */
>> @@ -300,7 +302,7 @@ int ima_match_policy(struct user_namespace *mnt_userns, struct inode *inode,
>>   		     struct ima_template_desc **template_desc,
>>   		     const char *func_data, unsigned int *allowed_algos);
>>   void ima_init_policy(void);
>> -void ima_update_policy(void);
>> +void ima_update_policy(struct ima_namespace *ns);
>>   void ima_update_policy_flags(void);
>>   ssize_t ima_parse_add_rule(char *);
>>   void ima_delete_rules(void);
>> diff --git a/security/integrity/ima/ima_fs.c b/security/integrity/ima/ima_fs.c
>> index 3d8e9d5db5aa..5cff3d6c3dc7 100644
>> --- a/security/integrity/ima/ima_fs.c
>> +++ b/security/integrity/ima/ima_fs.c
>> @@ -21,6 +21,7 @@
>>   #include <linux/rcupdate.h>
>>   #include <linux/parser.h>
>>   #include <linux/vmalloc.h>
>> +#include <linux/ima.h>
>>   
>>   #include "ima.h"
>>   
>> @@ -410,6 +411,7 @@ static int ima_open_policy(struct inode *inode, struct file *filp)
>>   static int ima_release_policy(struct inode *inode, struct file *file)
>>   {
>>   	const char *cause = valid_policy ? "completed" : "failed";
>> +	struct ima_namespace *ns = get_current_ns();
>>   
>>   	if ((file->f_flags & O_ACCMODE) == O_RDONLY)
>>   		return seq_release(inode, file);
>> @@ -430,7 +432,7 @@ static int ima_release_policy(struct inode *inode, struct file *file)
>>   		return 0;
>>   	}
>>   
>> -	ima_update_policy();
>> +	ima_update_policy(ns);
>>   #if !defined(CONFIG_IMA_WRITE_POLICY) && !defined(CONFIG_IMA_READ_POLICY)
>>   	securityfs_remove(ima_policy);
>>   	ima_policy = NULL;
>> diff --git a/security/integrity/ima/ima_init.c b/security/integrity/ima/ima_init.c
>> index f6ae4557a0da..24848373a061 100644
>> --- a/security/integrity/ima/ima_init.c
>> +++ b/security/integrity/ima/ima_init.c
>> @@ -155,8 +155,6 @@ int __init ima_init(void)
>>   	if (rc != 0)
>>   		return rc;
>>   
>> -	ima_init_key_queue();
>> -
>>   	ima_measure_critical_data("kernel_info", "kernel_version",
>>   				  UTS_RELEASE, strlen(UTS_RELEASE), false,
>>   				  NULL, 0);
>> diff --git a/security/integrity/ima/ima_init_ima_ns.c b/security/integrity/ima/ima_init_ima_ns.c
>> index 64777377664b..75ef17d52b5b 100644
>> --- a/security/integrity/ima/ima_init_ima_ns.c
>> +++ b/security/integrity/ima/ima_init_ima_ns.c
>> @@ -26,6 +26,14 @@ int ima_init_namespace(struct ima_namespace *ns)
>>   	if (!ns->ns_status_cache)
>>   		return -ENOMEM;
>>   
>> +#ifdef CONFIG_IMA_QUEUE_EARLY_BOOT_KEYS
>> +	INIT_DELAYED_WORK(&ns->ima_keys_delayed_work, ima_keys_handler);
>> +	ns->ima_key_queue_timeout = 300000;
>> +	ns->timer_expired = false;
>> +	if (ns == &init_ima_ns)
>> +		ima_init_key_queue(ns);
> The refcounting seems wrong?
> ima_init_key_queue() only takes a reference for init_ima_ns and
> consequently on init_user_ns (which is a bit pointless since it can't go
> away and so can't init_ima_ns).
>
> In contrast non-init_ima_ns will not take a reference on their user_ns.
> But ima_keys_handler() always puts the refcount for user_ns for both
> non-init_ima_ns and init_ima_ns alike.
>
> Maybe I'm misreading this.

My changes aren't correct and I have to roll this back. The correct 
solution would be to cancel the delayed work when deleting the 
ima_namespace [cancel_delayed_work_sync()]. I hadn't implemented that 
since currently there's not delayed work in non-init_ima_ns running. Any 
release of a last reference, as implemented by the modifications to this 
patch, could also delete the delayed_work structure and this would 
likely crash the system shortly after (although the delayed freeing 
behavior of the user namespace may save us from this).

I apologize for the confusion.


>
> In your earlier mail in [1] you said:
>
>>> The only problem that I see where we are accessing the IMA namespace outside a
>>> process context is in 4/16 'ima: Move delayed work queue and variables into
>>> ima_namespace' where a delayed work queue is used. I fixed this now by getting
> So we seem to know that ima always accesses ima_ns from
> current_user_ns() and only in the workqueue case will it delay key
> processing for a specific ima namespace without walking a userns
> hierarchy.
>
> If that's the case we should remove the user_ns member from ima_ns and
> enforce that ima_ns is always accessed from current_user_ns().

I will do that, yes.


> Since the workqueue case luckily doesn't need access to user_ns anywhere
> we can add a workqueue specific refcount that only keeps it alive for
> the workqueue case. We just need to enforce that when the refcount is
> bumped for the workqeue it must be done from process context so we're
> guaranteed that when we bump the reference the user_ns and consequently
> the ima_ns is still alive.

That won't work due to the work_queue getting freed. I will revert the 
modifications to this patch.


>
> This should solve your lifetime issues (once you fixed the problem I
> pointed out above).
>
> (Btw, the kref member was unused before my patch. It didn't really do
> any lifetime management for ima_ns afaict.)

True. I will remove it.


>
> [1]: https://lore.kernel.org/lkml/60fa585b-984e-fa13-e76f-56083a726259@linux.ibm.com
>
> Here's a sketch neither compile nor runtime tested and without the
> refcount issues I pointed out above fixed:
>
>  From 130e8d3faaad42820040587eff8695027fcf062a Mon Sep 17 00:00:00 2001
> From: Christian Brauner <christian.brauner@ubuntu.com>
> Date: Thu, 9 Dec 2021 13:15:49 +0100
> Subject: [PATCH] !!!! HERE BE DRAGONS - UNFIXED REFCOUNT ISSUES FROM PREVIOUS
>   PATCH AND ALL UNTESTED !!!!
>
> ima: get rid of user_ns member in struct ima_namespace
> ---
>   include/linux/ima.h                      | 40 +++++++++---------------
>   security/integrity/ima/ima_fs.c          |  2 +-
>   security/integrity/ima/ima_init_ima_ns.c |  3 +-
>   security/integrity/ima/ima_main.c        |  2 +-
>   security/integrity/ima/ima_ns.c          |  9 ++----
>   security/integrity/ima/ima_queue_keys.c  | 22 +++++++++----
>   6 files changed, 36 insertions(+), 42 deletions(-)
>
> diff --git a/include/linux/ima.h b/include/linux/ima.h
> index 32bf98092143..73cdfbf3f9d4 100644
> --- a/include/linux/ima.h
> +++ b/include/linux/ima.h
> @@ -13,6 +13,7 @@
>   #include <linux/kexec.h>
>   #include <linux/user_namespace.h>
>   #include <crypto/hash_info.h>
> +#include <linux/refcount.h>
>   struct linux_binprm;
>   
>   #ifdef CONFIG_IMA
> @@ -241,8 +242,6 @@ enum {
>   };
>   
>   struct ima_namespace {
> -	struct kref kref;
> -	struct user_namespace *user_ns;
>   	struct rb_root ns_status_tree;
>   	rwlock_t ns_status_lock;
>   	struct kmem_cache *ns_status_cache;
> @@ -264,6 +263,7 @@ struct ima_namespace {
>   	 * for measurement should be freed. This worker is used
>   	 * for handling this scenario.
>   	 */
> +	refcount_t ima_keys_delayed_ref;
>   	struct delayed_work ima_keys_delayed_work;
>   	long ima_key_queue_timeout;
>   	bool timer_expired;
> @@ -295,24 +295,12 @@ extern struct list_head ima_default_rules;
>   
>   #ifdef CONFIG_IMA_NS
>   
> -void free_ima_ns(struct kref *kref);
> -
> -static inline struct ima_namespace *get_ima_ns(struct ima_namespace *ns)
> -{
> -	if (ns)
> -		kref_get(&ns->kref);
> -
> -	return ns;
> -}
> +void free_ima_ns(struct ima_namespace *ns);
> +void __put_delayed_ima_ns(struct ima_namespace *ns);
>   
>   static inline void put_ima_ns(struct user_namespace *user_ns)
>   {
> -	struct ima_namespace *ns = user_ns->ima_ns;
> -
> -	if (ns) {
> -		pr_debug("DEREF   ima_ns: 0x%p  ctr: %d\n", ns, kref_read(&ns->kref));
> -		kref_put(&ns->kref, free_ima_ns);
> -	}
> +	__put_delayed_ima_ns(user_ns->ima_ns);
>   }
>   
>   int create_ima_ns(struct user_namespace *user_ns);
> @@ -322,21 +310,20 @@ static inline struct ima_namespace *get_current_ns(void)
>   	return current_user_ns()->ima_ns;
>   }
>   
> -#else
> -
> -static inline struct ima_namespace *get_ima_ns(struct ima_namespace *ns)
> +static inline struct user_namespace *ima_user_ns(const struct ima_namespace *ima_ns)
>   {
> -	return ns;
> +	struct user_namespace *user_ns;
> +	user_ns = current_user_ns();
> +	WARN_ON(user_ns->ima_ns != ima_ns);
> +	return user_ns;
>   }
>   
> -static inline void put_ima_ns(struct user_namespace *user_ns)
> -{
> -}
> +#else
>   
>   static inline int create_ima_ns(struct user_namespace *user_ns)
>   {
>   #if CONFIG_IMA
> -	user_ns->ima_ns = get_ima_ns(&init_ima_ns);
> +	user_ns->ima_ns = &init_ima_ns;
>   #endif
>   	return 0;
>   }
> @@ -346,6 +333,9 @@ static inline struct ima_namespace *get_current_ns(void)
>   	return &init_ima_ns;
>   }
>   
> +static inline void put_ima_ns(struct user_namespace *user_ns)
> +{
> +}
>   #endif /* CONFIG_IMA_NS */
>   
>   #if defined(CONFIG_IMA_APPRAISE) && defined(CONFIG_INTEGRITY_TRUSTED_KEYRING)
> diff --git a/security/integrity/ima/ima_fs.c b/security/integrity/ima/ima_fs.c
> index 778983fd9a73..583462b29cb5 100644
> --- a/security/integrity/ima/ima_fs.c
> +++ b/security/integrity/ima/ima_fs.c
> @@ -386,7 +386,7 @@ static int ima_open_policy(struct inode *inode, struct file *filp)
>   #else
>   		if ((filp->f_flags & O_ACCMODE) != O_RDONLY)
>   			return -EACCES;
> -		if (!mac_admin_ns_capable(ns->user_ns))
> +		if (!mac_admin_ns_capable(ima_user_ns(ns)))
>   			return -EPERM;
>   		return seq_open(filp, &ima_policy_seqops);
>   #endif
> diff --git a/security/integrity/ima/ima_init_ima_ns.c b/security/integrity/ima/ima_init_ima_ns.c
> index 162c94e06d13..6ae6df037f03 100644
> --- a/security/integrity/ima/ima_init_ima_ns.c
> +++ b/security/integrity/ima/ima_init_ima_ns.c
> @@ -62,12 +62,11 @@ int __init ima_ns_init(void)
>   }
>   
>   struct ima_namespace init_ima_ns = {
> -	.kref = KREF_INIT(1),
> -	.user_ns = &init_user_ns,
>   #ifdef CONFIG_IMA_QUEUE_EARLY_BOOT_KEYS
>   	.ima_process_keys = false,
>   	.ima_keys_lock = __MUTEX_INITIALIZER(init_ima_ns.ima_keys_lock),
>   	.ima_keys = LIST_HEAD_INIT(init_ima_ns.ima_keys),
>   #endif
> +	.ima_keys_delayed_ref = REFCOUNT_INIT(1),
>   };
>   EXPORT_SYMBOL(init_ima_ns);
> diff --git a/security/integrity/ima/ima_main.c b/security/integrity/ima/ima_main.c
> index 70fa26b7bd3f..6ebc57cd91d3 100644
> --- a/security/integrity/ima/ima_main.c
> +++ b/security/integrity/ima/ima_main.c
> @@ -410,7 +410,7 @@ static int process_measurement(struct ima_namespace *ns,
>   			       u32 secid, char *buf, loff_t size, int mask,
>   			       enum ima_hooks func)
>   {
> -	struct user_namespace *user_ns = ns->user_ns;
> +	struct user_namespace *user_ns = ima_user_ns(ns);
>   	int ret = 0;
>   
>   	while (user_ns) {
> diff --git a/security/integrity/ima/ima_ns.c b/security/integrity/ima/ima_ns.c
> index 6a0632806cdb..f96286ad0da8 100644
> --- a/security/integrity/ima/ima_ns.c
> +++ b/security/integrity/ima/ima_ns.c
> @@ -31,9 +31,6 @@ int create_ima_ns(struct user_namespace *user_ns)
>   		return -ENOMEM;
>   	pr_debug("NEW     ima_ns: 0x%p\n", ns);
>   
> -	kref_init(&ns->kref);
> -	ns->user_ns = user_ns;
> -
>   	err = ima_init_namespace(ns);
>   	if (err)
>   		goto fail_free;
> @@ -44,6 +41,7 @@ int create_ima_ns(struct user_namespace *user_ns)
>   	INIT_LIST_HEAD(&ns->ima_keys);
>   #endif
>   
> +	refcount_set(&ns->ima_keys_delayed_ref, 1);
>   	user_ns->ima_ns = ns;
>   
>   	return 0;
> @@ -63,11 +61,8 @@ static void destroy_ima_ns(struct ima_namespace *ns)
>   	kmem_cache_free(imans_cachep, ns);
>   }
>   
> -void free_ima_ns(struct kref *kref)
> +void free_ima_ns(struct ima_namespace *ns)
>   {
> -	struct ima_namespace *ns;
> -
> -	ns = container_of(kref, struct ima_namespace, kref);
>   	if (WARN_ON(ns == &init_ima_ns))
>   		return;
>   
> diff --git a/security/integrity/ima/ima_queue_keys.c b/security/integrity/ima/ima_queue_keys.c
> index a6eb802e5ae4..d7c43e592e2c 100644
> --- a/security/integrity/ima/ima_queue_keys.c
> +++ b/security/integrity/ima/ima_queue_keys.c
> @@ -14,6 +14,19 @@
>   #include <keys/asymmetric-type.h>
>   #include "ima.h"
>   
> +static inline void __get_delayed_ima_ns(struct ima_namespace *ima_ns)
> +{
> +	refcount_inc(&ima_ns->ima_keys_delayed_ref);
> +}
> +
> +void __put_delayed_ima_ns(struct ima_namespace *ima_ns)
> +{
> +	if (ima_ns && refcount_dec_and_test(&ima_ns->ima_keys_delayed_ref)) {
> +		pr_debug("DEREF   ima_ns: 0x%p  ctr: %d\n", ima_ns,
> +			 refcount_read(&ima_ns->ima_keys_delayed_ref));
> +		free_ima_ns(ima_ns);
> +	}
> +}
>   
>   /*
>    * This worker function frees keys that may still be
> @@ -26,8 +39,7 @@ void ima_keys_handler(struct work_struct *work)
>   	ns = container_of(work, struct ima_namespace, ima_keys_delayed_work.work);
>   	ns->timer_expired = true;
>   	ima_process_queued_keys(ns);
> -
> -	put_user_ns(ns->user_ns);
> +	__put_delayed_ima_ns(ns);
>   }
>   
>   /*
> @@ -36,9 +48,7 @@ void ima_keys_handler(struct work_struct *work)
>    */
>   void ima_init_key_queue(struct ima_namespace *ns)
>   {
> -	/* keep IMA namespace until delayed work is done */
> -	get_user_ns(ns->user_ns);
> -
> +	__get_delayed_ima_ns(ns);
>   	schedule_delayed_work(&ns->ima_keys_delayed_work,
>   			      msecs_to_jiffies(ns->ima_key_queue_timeout));
>   }
> @@ -145,7 +155,7 @@ void ima_process_queued_keys(struct ima_namespace *ns)
>   	if (!ns->timer_expired) {
>   		if (cancel_delayed_work_sync(&ns->ima_keys_delayed_work))
>   			/* undo reference from ima_init_key_queue */
> -			put_user_ns(ns->user_ns);
> +			__put_delayed_ima_ns(ns);
>   	}
>   
>   	list_for_each_entry_safe(entry, tmp, &ns->ima_keys, list) {

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

* Re: [PATCH v5 15/16] ima: Move dentries into ima_namespace
  2021-12-09 14:37     ` Christian Brauner
  2021-12-09 14:41       ` Christian Brauner
@ 2021-12-09 15:30       ` James Bottomley
  2021-12-09 19:38         ` James Bottomley
  1 sibling, 1 reply; 63+ messages in thread
From: James Bottomley @ 2021-12-09 15:30 UTC (permalink / raw)
  To: Christian Brauner, Stefan Berger
  Cc: linux-integrity, zohar, serge, containers, dmitry.kasatkin,
	ebiederm, krzysztof.struczynski, roberto.sassu, mpeters, lhinds,
	lsturman, puiterwi, jamjoom, linux-kernel, paul, rgb,
	linux-security-module, jmorris

On Thu, 2021-12-09 at 15:37 +0100, Christian Brauner wrote:
> On Thu, Dec 09, 2021 at 03:34:28PM +0100, Christian Brauner wrote:
> > On Wed, Dec 08, 2021 at 05:18:17PM -0500, Stefan Berger wrote:
> > > Move the dentries into the ima_namespace for reuse by virtualized
> > > SecurityFS. Implement function freeing the dentries in order of
> > > files and symlinks before directories.
> > > 
> > > Signed-off-by: Stefan Berger <stefanb@linux.ibm.com>
> > > ---
> > 
> > This doesn't work as implemented, I think.
> > 
> > What I would have preferred and what I tried to explain in the
> > earlier review was:
> > Keep the dentry stashing global since it is only needed for
> > init_ima_ns.
> > Then struct ima_namespace becomes way smaller and simpler.
> > If you do that then it makes sense to remove the additional dget()
> > in securityfs_create_dentry() for non-init_ima_ns.
> > Then you can rely on auto-cleanup in .kill_sb() or on
> > ima_securityfs_init() failure and you only need to call
> > ima_fs_ns_free_dentries() if ns != init_ima_ns.
> > 
> > IIuc, it seems you're currently doing one dput() too many since
> > you're calling securityfs_remove() in the error path for non-
> > init_ima_ns which relies on the previous increased dget() which we
> > removed.
> 
> If you really want to move the dentry stashing into struct
> ima_namespace even though it's really unnecessary then you may as
> well not care about the auto-cleanup and keep that additional
> ima_fs_ns_free_dentries(ns) call in .kill_sb(). But I really think
> not dragging dentry stashing into struct ima_namespace is the correct
> way to go about this.

We, unfortunately, do have one case we can't avoid stashing for the
policy file.  It's this code in ima_release_policy:

> #if !defined(CONFIG_IMA_WRITE_POLICY) &&
> !defined(CONFIG_IMA_READ_POLICY)
> 	securityfs_remove(ns->dentry[IMAFS_DENTRY_IMA_POLICY]);
> 	ns->dentry[IMAFS_DENTRY_IMA_POLICY] = NULL;
> 

What it does is that in certain config options, the policy file entry
gets removed from the securityfs ima directory after you write to it.

James



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

* Re: [PATCH v5 15/16] ima: Move dentries into ima_namespace
  2021-12-09 15:00         ` Stefan Berger
@ 2021-12-09 15:47           ` Christian Brauner
  0 siblings, 0 replies; 63+ messages in thread
From: Christian Brauner @ 2021-12-09 15:47 UTC (permalink / raw)
  To: Stefan Berger
  Cc: linux-integrity, zohar, serge, containers, dmitry.kasatkin,
	ebiederm, krzysztof.struczynski, roberto.sassu, mpeters, lhinds,
	lsturman, puiterwi, jejb, jamjoom, linux-kernel, paul, rgb,
	linux-security-module, jmorris

On Thu, Dec 09, 2021 at 10:00:59AM -0500, Stefan Berger wrote:
> 
> On 12/9/21 09:41, Christian Brauner wrote:
> > On Thu, Dec 09, 2021 at 03:37:49PM +0100, Christian Brauner wrote:
> > > On Thu, Dec 09, 2021 at 03:34:28PM +0100, Christian Brauner wrote:
> > > > On Wed, Dec 08, 2021 at 05:18:17PM -0500, Stefan Berger wrote:
> > > > > Move the dentries into the ima_namespace for reuse by virtualized
> > > > > SecurityFS. Implement function freeing the dentries in order of
> > > > > files and symlinks before directories.
> > > > > 
> > > > > Signed-off-by: Stefan Berger <stefanb@linux.ibm.com>
> > > > > ---
> > > > This doesn't work as implemented, I think.
> > > > 
> > > > What I would have preferred and what I tried to explain in the earlier
> > > > review was:
> > > > Keep the dentry stashing global since it is only needed for init_ima_ns.
> > > > Then struct ima_namespace becomes way smaller and simpler.
> > > > If you do that then it makes sense to remove the additional dget() in
> > > > securityfs_create_dentry() for non-init_ima_ns.
> > > > Then you can rely on auto-cleanup in .kill_sb() or on
> > > > ima_securityfs_init() failure and you only need to call
> > > > ima_fs_ns_free_dentries() if ns != init_ima_ns.
> > s/ns != init_ima_ns/ns == init_ima_ns/
> > 
> > > > IIuc, it seems you're currently doing one dput() too many since you're
> > > > calling securityfs_remove() in the error path for non-init_ima_ns which
> > > > relies on the previous increased dget() which we removed.
> 
> I thought that securityfs_remove() will now simply influence when a dentry
> is removed and freed. If we call it in the error cleanup path in
> non-init_user_ns case it would go away right there and leave nothing to do
> for .kill_sb() while an additional dget() would require the cleanup as well
> but do another cleanup then in .kill_sb() since that brings the reference
> count to 0 via the dput()s that it does. Am I wrong on this?

With your change you get one dget() from lookup_one_len() in
securityfs_create_dentry() for non-init_ima_ns. That's added to the
dcache via d_instantiate().
If you call securityfs_dentry_remove() in the error path or anywhere
else it does:

	dir = d_inode(dentry->d_parent);
	inode_lock(dir);
	if (simple_positive(dentry)) {
		if (d_is_dir(dentry))
			simple_rmdir(dir, dentry);
		else
			simple_unlink(dir, dentry);
		dput(dentry);
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

That dput() right there is for the additional dget() in
securityfs_create_dentry() but we didn't take that. So the dput() is one
too many now since simple_rmdir() and simple_unlink() will have consumed
one already. (You should be able to easily see this if you compile with
sanitizers on and let your init function fail somewhere in the middle.)

(What usually should happen is sm like this:

void binderfs_remove_file(struct dentry *dentry)
{
	struct inode *parent_inode;

	parent_inode = d_inode(dentry->d_parent);
	inode_lock(parent_inode);
	if (simple_positive(dentry)) {
		dget(dentry);
		simple_unlink(parent_inode, dentry);
		d_delete(dentry);
		dput(dentry);
	}
	inode_unlock(parent_inode);
})

> 
> 
> > > If you really want to move the dentry stashing into struct ima_namespace
> > > even though it's really unnecessary then you may as well not care about
> > > the auto-cleanup and keep that additional ima_fs_ns_free_dentries(ns)
> > > call in .kill_sb(). But I really think not dragging dentry stashing into
> > > struct ima_namespace is the correct way to go about this.
> 
> 
> I moved the dentries into the ima_namespace so that each namespace holds a
> pointer to the dentries it owns and isolates them. We certainly wouldn't
> want to have IMA namespaces write over the current static variables and
> create a mess with what these are pointing to ( https://elixir.bootlin.com/linux/latest/source/security/integrity/ima/ima_fs.c#L359
> ) and possible race conditions when doing parallel initialization (if that's
> possible at all). This also reduces the code size and we don't need two
> different implementations for init_user_ns and non-init_user_ns. So I don't
> quite understand whey we wouldn't want to have the dentries isolated via
> ima_namespace?

My point was this:
Afaict, nowhere in ima are the stashed dentries needed apart from
ima_policy_release() which is the .release method of the
file_operations where the policy dentry is removed.

The dentries only exist because for pre-namespaced ima if you created a
dentry going through securityfs_create_dentry() you're pinning the
super_block of init-securityfs via simple_pin_fs(). That obliges you to
call securityfs_remove() later on in order to call simple_unpin_fs() for
all dentries.

But for namespaced-ima with namespaced-securityfs there is no more call
to simple_{pin,unpin}_fs(). Consequently you don't need to stash the
dentries anywhere to have them available for removal later on. They will
be automatically cleaned up during .kill_sb().

The one exception I was unaware of reading the code before is in
ima_policy_release(). So apologies, I didn't see that. There you remove:

static int ima_release_policy(struct inode *inode, struct file *file)
{
	struct ima_namespace *ns = get_current_ns();
	const char *cause = ns->valid_policy ? "completed" : "failed";

	if ((file->f_flags & O_ACCMODE) == O_RDONLY)
		return seq_release(inode, file);

	if (ns->valid_policy && ima_check_policy(ns) < 0) {
		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);

	if (!ns->valid_policy) {
		ima_delete_rules(ns);
		ns->valid_policy = 1;
		clear_bit(IMA_FS_BUSY, &ns->ima_fs_flags);
		return 0;
	}

	ima_update_policy(ns);
#if !defined(CONFIG_IMA_WRITE_POLICY) && !defined(CONFIG_IMA_READ_POLICY)
	securityfs_remove(ns->dentry[IMAFS_DENTRY_IMA_POLICY]);
	ns->dentry[IMAFS_DENTRY_IMA_POLICY] = NULL;

^^^^^^^^^^^^^^^^^^^^^^^^^

But even so, why then stash all those dentries if the only dentry that
you ever remove while ima is active - and ima isn't a module so can't be
unloaded - is the IMAFS_DENTRY_IMA_POLICY. Simply stash the single
dentry in struct ima_namespace and forget about all the other ones and
avoid wasting memory. But maybe I'm misunderstanding something.

I'm going to get my booster shot and hopefully I'll be able to work
tomorrow and later today but I wouldn't bet on it.

Christian

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

* Re: [PATCH v5 13/16] ima: Move some IMA policy and filesystem related variables into ima_namespace
  2021-12-08 22:18 ` [PATCH v5 13/16] ima: Move some IMA policy and filesystem related variables into ima_namespace Stefan Berger
@ 2021-12-09 19:11   ` Christian Brauner
  2021-12-09 20:42     ` Stefan Berger
  2021-12-10  0:57     ` Stefan Berger
  0 siblings, 2 replies; 63+ messages in thread
From: Christian Brauner @ 2021-12-09 19:11 UTC (permalink / raw)
  To: Stefan Berger
  Cc: linux-integrity, zohar, serge, containers, dmitry.kasatkin,
	ebiederm, krzysztof.struczynski, roberto.sassu, mpeters, lhinds,
	lsturman, puiterwi, jejb, jamjoom, linux-kernel, paul, rgb,
	linux-security-module, jmorris

On Wed, Dec 08, 2021 at 05:18:15PM -0500, Stefan Berger wrote:
> Move the ima_write_mutex, ima_fs_flag, and valid_policy variables into
> ima_namespace. This way each IMA namespace can set those variables
> independently.
> 
> Signed-off-by: Stefan Berger <stefanb@linux.ibm.com>
> ---
>  include/linux/ima.h                      |  5 ++++
>  security/integrity/ima/ima_fs.c          | 32 +++++++++++-------------
>  security/integrity/ima/ima_init_ima_ns.c |  4 +++
>  3 files changed, 23 insertions(+), 18 deletions(-)
> 
> diff --git a/include/linux/ima.h b/include/linux/ima.h
> index 2ce801bfc449..3aaf6e806db4 100644
> --- a/include/linux/ima.h
> +++ b/include/linux/ima.h
> @@ -261,6 +261,11 @@ struct ima_namespace {
>  	struct ima_h_table ima_htable;
>  	struct list_head ima_measurements;
>  	unsigned long binary_runtime_size;
> +
> +	/* IMA's filesystem */
> +	struct mutex ima_write_mutex;
> +	unsigned long ima_fs_flags;
> +	int valid_policy;
>  };
>  
>  extern struct ima_namespace init_ima_ns;
> diff --git a/security/integrity/ima/ima_fs.c b/security/integrity/ima/ima_fs.c
> index 38b1c26479b3..0e582ceecc7f 100644
> --- a/security/integrity/ima/ima_fs.c
> +++ b/security/integrity/ima/ima_fs.c
> @@ -25,8 +25,6 @@
>  
>  #include "ima.h"
>  
> -static DEFINE_MUTEX(ima_write_mutex);
> -
>  bool ima_canonical_fmt;
>  static int __init default_canonical_fmt_setup(char *str)
>  {
> @@ -37,8 +35,6 @@ static int __init default_canonical_fmt_setup(char *str)
>  }
>  __setup("ima_canonical_fmt", default_canonical_fmt_setup);
>  
> -static int valid_policy = 1;
> -
>  static ssize_t ima_show_htable_value(char __user *buf, size_t count,
>  				     loff_t *ppos, atomic_long_t *val)
>  {
> @@ -339,7 +335,7 @@ static ssize_t ima_write_policy(struct file *file, const char __user *buf,
>  		goto out;
>  	}
>  
> -	result = mutex_lock_interruptible(&ima_write_mutex);
> +	result = mutex_lock_interruptible(&ns->ima_write_mutex);
>  	if (result < 0)
>  		goto out_free;
>  
> @@ -354,12 +350,12 @@ static ssize_t ima_write_policy(struct file *file, const char __user *buf,
>  	} else {
>  		result = ima_parse_add_rule(ns, data);
>  	}
> -	mutex_unlock(&ima_write_mutex);
> +	mutex_unlock(&ns->ima_write_mutex);
>  out_free:
>  	kfree(data);
>  out:
>  	if (result < 0)
> -		valid_policy = 0;
> +		ns->valid_policy = 0;
>  
>  	return result;
>  }
> @@ -376,8 +372,6 @@ enum ima_fs_flags {
>  	IMA_FS_BUSY,
>  };
>  
> -static unsigned long ima_fs_flags;
> -
>  #ifdef	CONFIG_IMA_READ_POLICY
>  static const struct seq_operations ima_policy_seqops = {
>  		.start = ima_policy_start,
> @@ -392,6 +386,8 @@ static const struct seq_operations ima_policy_seqops = {
>   */
>  static int ima_open_policy(struct inode *inode, struct file *filp)
>  {
> +	struct ima_namespace *ns = get_current_ns();
> +

I'm a bit confused here. In all those callbacks:
	.open = ima_open_policy,
	.write = ima_write_policy,
	.release = ima_release_policy,
you're calling get_current_ns() at the top of it. What guarantees that
the same ima_namespace is returned here? What if the fd is sent to
someone who is in a different user namespace and the write to that
file?

Maybe I'm just confused but wouldn't you want something like this?

From 1f03dc427c583d5e9ebc9ebe9de77c3c535bbebe Mon Sep 17 00:00:00 2001
From: Christian Brauner <christian.brauner@ubuntu.com>
Date: Thu, 9 Dec 2021 20:07:02 +0100
Subject: [PATCH] !!!! HERE BE DRAGONS - UNTESTED !!!!

---
 security/integrity/ima/ima_fs.c | 43 +++++++++++++++++++++++++++++----
 1 file changed, 38 insertions(+), 5 deletions(-)

diff --git a/security/integrity/ima/ima_fs.c b/security/integrity/ima/ima_fs.c
index 583462b29cb5..d5b302b925b8 100644
--- a/security/integrity/ima/ima_fs.c
+++ b/security/integrity/ima/ima_fs.c
@@ -317,10 +317,14 @@ static ssize_t ima_read_policy(char *path)
 static ssize_t ima_write_policy(struct file *file, const char __user *buf,
 				size_t datalen, loff_t *ppos)
 {
-	struct ima_namespace *ns = get_current_ns();
+	struct ima_namespace *ns;
+	struct user_namespace *user_ns;
 	char *data;
 	ssize_t result;
 
+	user_ns = ima_filp_private(filp);
+	ns = user_ns->ima_ns
+
 	if (datalen >= PAGE_SIZE)
 		datalen = PAGE_SIZE - 1;
 
@@ -373,26 +377,51 @@ static const struct seq_operations ima_policy_seqops = {
 };
 #endif
 
+static struct user_namespace *ima_filp_private(struct file *filp)
+{
+	if (!(filp->f_flags & O_WRONLY)) {
+#ifdef CONFIG_IMA_READ_POLICY
+		struct seq_file *seq;
+
+		seq = filp->private_data;
+		return seq->private;
+#endif
+	}
+	return filp->private_data;
+}
+
 /*
  * ima_open_policy: sequentialize access to the policy file
  */
 static int ima_open_policy(struct inode *inode, struct file *filp)
 {
-	struct ima_namespace *ns = get_current_ns();
+	struct user_namespace *user_ns = current_user_ns();
+	struct ima_namespace *ns = user_ns->ima_ns;
 
 	if (!(filp->f_flags & O_WRONLY)) {
 #ifndef	CONFIG_IMA_READ_POLICY
 		return -EACCES;
 #else
+		int err;
+		struct seq_file *seq;
+
 		if ((filp->f_flags & O_ACCMODE) != O_RDONLY)
 			return -EACCES;
-		if (!mac_admin_ns_capable(ima_user_ns(ns)))
+		if (!mac_admin_ns_capable(user_ns))
 			return -EPERM;
-		return seq_open(filp, &ima_policy_seqops);
+		err = seq_open(filp, &ima_policy_seqops);
+		if (err)
+			return err;
+
+		seq = filp->private_data;
+		seq->private = user_ns;
+		return 0;
 #endif
 	}
 	if (test_and_set_bit(IMA_FS_BUSY, &ns->ima_fs_flags))
 		return -EBUSY;
+
+	filp->private_data = user_ns;
 	return 0;
 }
 
@@ -405,9 +434,13 @@ 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 = get_current_ns();
+	struct ima_namespace *ns;
+	struct user_namespace *user_ns;
 	const char *cause = ns->valid_policy ? "completed" : "failed";
 
+	user_ns = ima_filp_private(filp);
+	ns = user_ns->ima_ns
+
 	if ((file->f_flags & O_ACCMODE) == O_RDONLY)
 		return seq_release(inode, file);
 
-- 
2.30.2


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

* Re: [PATCH v5 15/16] ima: Move dentries into ima_namespace
  2021-12-09 15:30       ` James Bottomley
@ 2021-12-09 19:38         ` James Bottomley
  2021-12-09 20:13           ` Stefan Berger
  2021-12-10 11:49           ` Christian Brauner
  0 siblings, 2 replies; 63+ messages in thread
From: James Bottomley @ 2021-12-09 19:38 UTC (permalink / raw)
  To: Christian Brauner, Stefan Berger
  Cc: linux-integrity, zohar, serge, containers, dmitry.kasatkin,
	ebiederm, krzysztof.struczynski, roberto.sassu, mpeters, lhinds,
	lsturman, puiterwi, jamjoom, linux-kernel, paul, rgb,
	linux-security-module, jmorris

On Thu, 2021-12-09 at 10:30 -0500, James Bottomley wrote:
> On Thu, 2021-12-09 at 15:37 +0100, Christian Brauner wrote:
> > On Thu, Dec 09, 2021 at 03:34:28PM +0100, Christian Brauner wrote:
> > > On Wed, Dec 08, 2021 at 05:18:17PM -0500, Stefan Berger wrote:
> > > > Move the dentries into the ima_namespace for reuse by
> > > > virtualized
> > > > SecurityFS. Implement function freeing the dentries in order of
> > > > files and symlinks before directories.
> > > > 
> > > > Signed-off-by: Stefan Berger <stefanb@linux.ibm.com>
> > > > ---
> > > 
> > > This doesn't work as implemented, I think.
> > > 
> > > What I would have preferred and what I tried to explain in the
> > > earlier review was:
> > > Keep the dentry stashing global since it is only needed for
> > > init_ima_ns.
> > > Then struct ima_namespace becomes way smaller and simpler.
> > > If you do that then it makes sense to remove the additional
> > > dget() in securityfs_create_dentry() for non-init_ima_ns.
> > > Then you can rely on auto-cleanup in .kill_sb() or on
> > > ima_securityfs_init() failure and you only need to call
> > > ima_fs_ns_free_dentries() if ns != init_ima_ns.
> > > 
> > > IIuc, it seems you're currently doing one dput() too many since
> > > you're calling securityfs_remove() in the error path for non-
> > > init_ima_ns which relies on the previous increased dget() which
> > > we removed.
> > 
> > If you really want to move the dentry stashing into struct
> > ima_namespace even though it's really unnecessary then you may as
> > well not care about the auto-cleanup and keep that additional
> > ima_fs_ns_free_dentries(ns) call in .kill_sb(). But I really think
> > not dragging dentry stashing into struct ima_namespace is the
> > correct way to go about this.
> 
> We, unfortunately, do have one case we can't avoid stashing for the
> policy file.  It's this code in ima_release_policy:
> 
> > #if !defined(CONFIG_IMA_WRITE_POLICY) &&
> > !defined(CONFIG_IMA_READ_POLICY)
> > 	securityfs_remove(ns->dentry[IMAFS_DENTRY_IMA_POLICY]);
> > 	ns->dentry[IMAFS_DENTRY_IMA_POLICY] = NULL;
> > 
> 
> What it does is that in certain config options, the policy file entry
> gets removed from the securityfs ima directory after you write to it.

This is what I have incremental to v5 that corrects all of this.  It
actually keeps every dentry reference (including init_user_ns ones) at
1 so they can be reaped on unmount.  For the remove case it does
d_delete and then puts the only reference.  This means
securityfs_remove() works for the namespaced policy file as well.

I also got rid of the spurious initialized check in ima_securityfs_init
because it prevents you doing a mount;umount;mount on securityfs within
a namespace.

There's still the problem that if you write the policy, making the file
disappear then unmount and remount securityfs it will come back.  My
guess for fixing this is that we only stash the policy file reference,
create it if NULL but then set the pointer to PTR_ERR(-EINVAL) or
something and refuse to create it for that value.

James

---

From 7de285a81ff06b6e0eb2c6db24810aeef9f6dd17 Mon Sep 17 00:00:00 2001
From: James Bottomley <James.Bottomley@HansenPartnership.com>
Date: Thu, 9 Dec 2021 19:33:49 +0000
Subject: [PATCH] fix dentry ref counting

---
 security/inode.c                | 12 ++----------
 security/integrity/ima/ima_fs.c |  4 ----
 2 files changed, 2 insertions(+), 14 deletions(-)

diff --git a/security/inode.c b/security/inode.c
index eaccba7017d9..b53152f7a625 100644
--- a/security/inode.c
+++ b/security/inode.c
@@ -178,8 +178,6 @@ static struct dentry *securityfs_create_dentry(const char *name, umode_t mode,
 		inode->i_fop = fops;
 	}
 	d_instantiate(dentry, inode);
-	if (ns == &init_user_ns)
-		dget(dentry);
 	inode_unlock(dir);
 	return dentry;
 
@@ -317,21 +315,15 @@ EXPORT_SYMBOL_GPL(securityfs_create_symlink);
 void securityfs_remove(struct dentry *dentry)
 {
 	struct user_namespace *ns = dentry->d_sb->s_user_ns;
-	struct inode *dir;
 
 	if (!dentry || IS_ERR(dentry))
 		return;
 
-	dir = d_inode(dentry->d_parent);
-	inode_lock(dir);
 	if (simple_positive(dentry)) {
-		if (d_is_dir(dentry))
-			simple_rmdir(dir, dentry);
-		else
-			simple_unlink(dir, dentry);
+		d_delete(dentry);
 		dput(dentry);
 	}
-	inode_unlock(dir);
+
 	if (ns == &init_user_ns)
 		simple_release_fs(&init_securityfs_mount,
 				  &init_securityfs_mount_count);
diff --git a/security/integrity/ima/ima_fs.c b/security/integrity/ima/ima_fs.c
index 778983fd9a73..077a6ff46858 100644
--- a/security/integrity/ima/ima_fs.c
+++ b/security/integrity/ima/ima_fs.c
@@ -466,10 +466,6 @@ int ima_securityfs_init(struct user_namespace *user_ns, struct dentry *root)
 	struct ima_namespace *ns = user_ns->ima_ns;
 	struct dentry *ima_dir;
 
-	/* already initialized? */
-	if (ns->dentry[IMAFS_DENTRY_INTEGRITY_DIR])
-		return 0;
-
 	/* FIXME: update when evm and integrity are namespaced */
 	if (user_ns != &init_user_ns) {
 		ns->dentry[IMAFS_DENTRY_INTEGRITY_DIR] =
-- 
2.33.0



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

* Re: [PATCH v5 15/16] ima: Move dentries into ima_namespace
  2021-12-09 19:38         ` James Bottomley
@ 2021-12-09 20:13           ` Stefan Berger
  2021-12-10 11:49           ` Christian Brauner
  1 sibling, 0 replies; 63+ messages in thread
From: Stefan Berger @ 2021-12-09 20:13 UTC (permalink / raw)
  To: jejb, Christian Brauner
  Cc: linux-integrity, zohar, serge, containers, dmitry.kasatkin,
	ebiederm, krzysztof.struczynski, roberto.sassu, mpeters, lhinds,
	lsturman, puiterwi, jamjoom, linux-kernel, paul, rgb,
	linux-security-module, jmorris


On 12/9/21 14:38, James Bottomley wrote:
> On Thu, 2021-12-09 at 10:30 -0500, James Bottomley wrote:
>> On Thu, 2021-12-09 at 15:37 +0100, Christian Brauner wrote:
>>> On Thu, Dec 09, 2021 at 03:34:28PM +0100, Christian Brauner wrote:
>>>> On Wed, Dec 08, 2021 at 05:18:17PM -0500, Stefan Berger wrote:
>>>>> Move the dentries into the ima_namespace for reuse by
>>>>> virtualized
>>>>> SecurityFS. Implement function freeing the dentries in order of
>>>>> files and symlinks before directories.
>>>>>
>>>>> Signed-off-by: Stefan Berger <stefanb@linux.ibm.com>
>>>>> ---
>>>> This doesn't work as implemented, I think.
>>>>
>>>> What I would have preferred and what I tried to explain in the
>>>> earlier review was:
>>>> Keep the dentry stashing global since it is only needed for
>>>> init_ima_ns.
>>>> Then struct ima_namespace becomes way smaller and simpler.
>>>> If you do that then it makes sense to remove the additional
>>>> dget() in securityfs_create_dentry() for non-init_ima_ns.
>>>> Then you can rely on auto-cleanup in .kill_sb() or on
>>>> ima_securityfs_init() failure and you only need to call
>>>> ima_fs_ns_free_dentries() if ns != init_ima_ns.
>>>>
>>>> IIuc, it seems you're currently doing one dput() too many since
>>>> you're calling securityfs_remove() in the error path for non-
>>>> init_ima_ns which relies on the previous increased dget() which
>>>> we removed.
>>> If you really want to move the dentry stashing into struct
>>> ima_namespace even though it's really unnecessary then you may as
>>> well not care about the auto-cleanup and keep that additional
>>> ima_fs_ns_free_dentries(ns) call in .kill_sb(). But I really think
>>> not dragging dentry stashing into struct ima_namespace is the
>>> correct way to go about this.
>> We, unfortunately, do have one case we can't avoid stashing for the
>> policy file.  It's this code in ima_release_policy:
>>
>>> #if !defined(CONFIG_IMA_WRITE_POLICY) &&
>>> !defined(CONFIG_IMA_READ_POLICY)
>>> 	securityfs_remove(ns->dentry[IMAFS_DENTRY_IMA_POLICY]);
>>> 	ns->dentry[IMAFS_DENTRY_IMA_POLICY] = NULL;
>>>
>> What it does is that in certain config options, the policy file entry
>> gets removed from the securityfs ima directory after you write to it.
> This is what I have incremental to v5 that corrects all of this.  It
> actually keeps every dentry reference (including init_user_ns ones) at
> 1 so they can be reaped on unmount.  For the remove case it does
> d_delete and then puts the only reference.  This means
> securityfs_remove() works for the namespaced policy file as well.
I fixed it now as well but do another dget() on securityfs_removed() for 
ns != init_user_ns. Ok, I will add what you have below.
>
> I also got rid of the spurious initialized check in ima_securityfs_init
> because it prevents you doing a mount;umount;mount on securityfs within
> a namespace.
>
> There's still the problem that if you write the policy, making the file
> disappear then unmount and remount securityfs it will come back.  My
> guess for fixing this is that we only stash the policy file reference,
> create it if NULL but then set the pointer to PTR_ERR(-EINVAL) or
> something and refuse to create it for that value.

What about boolean to remember this? I just added this.


>
> James
>
> ---
>
>  From 7de285a81ff06b6e0eb2c6db24810aeef9f6dd17 Mon Sep 17 00:00:00 2001
> From: James Bottomley <James.Bottomley@HansenPartnership.com>
> Date: Thu, 9 Dec 2021 19:33:49 +0000
> Subject: [PATCH] fix dentry ref counting
>
> ---
>   security/inode.c                | 12 ++----------
>   security/integrity/ima/ima_fs.c |  4 ----
>   2 files changed, 2 insertions(+), 14 deletions(-)
>
> diff --git a/security/inode.c b/security/inode.c
> index eaccba7017d9..b53152f7a625 100644
> --- a/security/inode.c
> +++ b/security/inode.c
> @@ -178,8 +178,6 @@ static struct dentry *securityfs_create_dentry(const char *name, umode_t mode,
>   		inode->i_fop = fops;
>   	}
>   	d_instantiate(dentry, inode);
> -	if (ns == &init_user_ns)
> -		dget(dentry);
>   	inode_unlock(dir);
>   	return dentry;
>   
> @@ -317,21 +315,15 @@ EXPORT_SYMBOL_GPL(securityfs_create_symlink);
>   void securityfs_remove(struct dentry *dentry)
>   {
>   	struct user_namespace *ns = dentry->d_sb->s_user_ns;
> -	struct inode *dir;
>   
>   	if (!dentry || IS_ERR(dentry))
>   		return;
>   
> -	dir = d_inode(dentry->d_parent);
> -	inode_lock(dir);
>   	if (simple_positive(dentry)) {
> -		if (d_is_dir(dentry))
> -			simple_rmdir(dir, dentry);
> -		else
> -			simple_unlink(dir, dentry);
> +		d_delete(dentry);
>   		dput(dentry);
>   	}
> -	inode_unlock(dir);
> +
>   	if (ns == &init_user_ns)
>   		simple_release_fs(&init_securityfs_mount,
>   				  &init_securityfs_mount_count);
> diff --git a/security/integrity/ima/ima_fs.c b/security/integrity/ima/ima_fs.c
> index 778983fd9a73..077a6ff46858 100644
> --- a/security/integrity/ima/ima_fs.c
> +++ b/security/integrity/ima/ima_fs.c
> @@ -466,10 +466,6 @@ int ima_securityfs_init(struct user_namespace *user_ns, struct dentry *root)
>   	struct ima_namespace *ns = user_ns->ima_ns;
>   	struct dentry *ima_dir;
>   
> -	/* already initialized? */
> -	if (ns->dentry[IMAFS_DENTRY_INTEGRITY_DIR])
> -		return 0;
> -
>   	/* FIXME: update when evm and integrity are namespaced */
>   	if (user_ns != &init_user_ns) {
>   		ns->dentry[IMAFS_DENTRY_INTEGRITY_DIR] =

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

* Re: [PATCH v5 13/16] ima: Move some IMA policy and filesystem related variables into ima_namespace
  2021-12-09 19:11   ` Christian Brauner
@ 2021-12-09 20:42     ` Stefan Berger
  2021-12-10  0:57     ` Stefan Berger
  1 sibling, 0 replies; 63+ messages in thread
From: Stefan Berger @ 2021-12-09 20:42 UTC (permalink / raw)
  To: Christian Brauner
  Cc: linux-integrity, zohar, serge, containers, dmitry.kasatkin,
	ebiederm, krzysztof.struczynski, roberto.sassu, mpeters, lhinds,
	lsturman, puiterwi, jejb, jamjoom, linux-kernel, paul, rgb,
	linux-security-module, jmorris


On 12/9/21 14:11, Christian Brauner wrote:
> On Wed, Dec 08, 2021 at 05:18:15PM -0500, Stefan Berger wrote:
>> Move the ima_write_mutex, ima_fs_flag, and valid_policy variables into
>> ima_namespace. This way each IMA namespace can set those variables
>> independently.
>>
>> Signed-off-by: Stefan Berger <stefanb@linux.ibm.com>
>> ---
>>   include/linux/ima.h                      |  5 ++++
>>   security/integrity/ima/ima_fs.c          | 32 +++++++++++-------------
>>   security/integrity/ima/ima_init_ima_ns.c |  4 +++
>>   3 files changed, 23 insertions(+), 18 deletions(-)
>>
>> diff --git a/include/linux/ima.h b/include/linux/ima.h
>> index 2ce801bfc449..3aaf6e806db4 100644
>> --- a/include/linux/ima.h
>> +++ b/include/linux/ima.h
>> @@ -261,6 +261,11 @@ struct ima_namespace {
>>   	struct ima_h_table ima_htable;
>>   	struct list_head ima_measurements;
>>   	unsigned long binary_runtime_size;
>> +
>> +	/* IMA's filesystem */
>> +	struct mutex ima_write_mutex;
>> +	unsigned long ima_fs_flags;
>> +	int valid_policy;
>>   };
>>   
>>   extern struct ima_namespace init_ima_ns;
>> diff --git a/security/integrity/ima/ima_fs.c b/security/integrity/ima/ima_fs.c
>> index 38b1c26479b3..0e582ceecc7f 100644
>> --- a/security/integrity/ima/ima_fs.c
>> +++ b/security/integrity/ima/ima_fs.c
>> @@ -25,8 +25,6 @@
>>   
>>   #include "ima.h"
>>   
>> -static DEFINE_MUTEX(ima_write_mutex);
>> -
>>   bool ima_canonical_fmt;
>>   static int __init default_canonical_fmt_setup(char *str)
>>   {
>> @@ -37,8 +35,6 @@ static int __init default_canonical_fmt_setup(char *str)
>>   }
>>   __setup("ima_canonical_fmt", default_canonical_fmt_setup);
>>   
>> -static int valid_policy = 1;
>> -
>>   static ssize_t ima_show_htable_value(char __user *buf, size_t count,
>>   				     loff_t *ppos, atomic_long_t *val)
>>   {
>> @@ -339,7 +335,7 @@ static ssize_t ima_write_policy(struct file *file, const char __user *buf,
>>   		goto out;
>>   	}
>>   
>> -	result = mutex_lock_interruptible(&ima_write_mutex);
>> +	result = mutex_lock_interruptible(&ns->ima_write_mutex);
>>   	if (result < 0)
>>   		goto out_free;
>>   
>> @@ -354,12 +350,12 @@ static ssize_t ima_write_policy(struct file *file, const char __user *buf,
>>   	} else {
>>   		result = ima_parse_add_rule(ns, data);
>>   	}
>> -	mutex_unlock(&ima_write_mutex);
>> +	mutex_unlock(&ns->ima_write_mutex);
>>   out_free:
>>   	kfree(data);
>>   out:
>>   	if (result < 0)
>> -		valid_policy = 0;
>> +		ns->valid_policy = 0;
>>   
>>   	return result;
>>   }
>> @@ -376,8 +372,6 @@ enum ima_fs_flags {
>>   	IMA_FS_BUSY,
>>   };
>>   
>> -static unsigned long ima_fs_flags;
>> -
>>   #ifdef	CONFIG_IMA_READ_POLICY
>>   static const struct seq_operations ima_policy_seqops = {
>>   		.start = ima_policy_start,
>> @@ -392,6 +386,8 @@ static const struct seq_operations ima_policy_seqops = {
>>    */
>>   static int ima_open_policy(struct inode *inode, struct file *filp)
>>   {
>> +	struct ima_namespace *ns = get_current_ns();
>> +
> I'm a bit confused here. In all those callbacks:
> 	.open = ima_open_policy,
> 	.write = ima_write_policy,
> 	.release = ima_release_policy,
> you're calling get_current_ns() at the top of it. What guarantees that
> the same ima_namespace is returned here? What if the fd is sent to
> someone who is in a different user namespace and the write to that
> file?
>
> Maybe I'm just confused but wouldn't you want something like this?

I hadn't thought about inheritance or passing fds. But yes. I will adopt 
your patch and extend all the files to tie them to the user namespace 
they are opened in...

Thanks.


>
>  From 1f03dc427c583d5e9ebc9ebe9de77c3c535bbebe Mon Sep 17 00:00:00 2001
> From: Christian Brauner <christian.brauner@ubuntu.com>
> Date: Thu, 9 Dec 2021 20:07:02 +0100
> Subject: [PATCH] !!!! HERE BE DRAGONS - UNTESTED !!!!
>
> ---
>   security/integrity/ima/ima_fs.c | 43 +++++++++++++++++++++++++++++----
>   1 file changed, 38 insertions(+), 5 deletions(-)
>
> diff --git a/security/integrity/ima/ima_fs.c b/security/integrity/ima/ima_fs.c
> index 583462b29cb5..d5b302b925b8 100644
> --- a/security/integrity/ima/ima_fs.c
> +++ b/security/integrity/ima/ima_fs.c
> @@ -317,10 +317,14 @@ static ssize_t ima_read_policy(char *path)
>   static ssize_t ima_write_policy(struct file *file, const char __user *buf,
>   				size_t datalen, loff_t *ppos)
>   {
> -	struct ima_namespace *ns = get_current_ns();
> +	struct ima_namespace *ns;
> +	struct user_namespace *user_ns;
>   	char *data;
>   	ssize_t result;
>   
> +	user_ns = ima_filp_private(filp);
> +	ns = user_ns->ima_ns
> +
>   	if (datalen >= PAGE_SIZE)
>   		datalen = PAGE_SIZE - 1;
>   
> @@ -373,26 +377,51 @@ static const struct seq_operations ima_policy_seqops = {
>   };
>   #endif
>   
> +static struct user_namespace *ima_filp_private(struct file *filp)
> +{
> +	if (!(filp->f_flags & O_WRONLY)) {
> +#ifdef CONFIG_IMA_READ_POLICY
> +		struct seq_file *seq;
> +
> +		seq = filp->private_data;
> +		return seq->private;
> +#endif
> +	}
> +	return filp->private_data;
> +}
> +
>   /*
>    * ima_open_policy: sequentialize access to the policy file
>    */
>   static int ima_open_policy(struct inode *inode, struct file *filp)
>   {
> -	struct ima_namespace *ns = get_current_ns();
> +	struct user_namespace *user_ns = current_user_ns();
> +	struct ima_namespace *ns = user_ns->ima_ns;
>   
>   	if (!(filp->f_flags & O_WRONLY)) {
>   #ifndef	CONFIG_IMA_READ_POLICY
>   		return -EACCES;
>   #else
> +		int err;
> +		struct seq_file *seq;
> +
>   		if ((filp->f_flags & O_ACCMODE) != O_RDONLY)
>   			return -EACCES;
> -		if (!mac_admin_ns_capable(ima_user_ns(ns)))
> +		if (!mac_admin_ns_capable(user_ns))
>   			return -EPERM;
> -		return seq_open(filp, &ima_policy_seqops);
> +		err = seq_open(filp, &ima_policy_seqops);
> +		if (err)
> +			return err;
> +
> +		seq = filp->private_data;
> +		seq->private = user_ns;
> +		return 0;
>   #endif
>   	}
>   	if (test_and_set_bit(IMA_FS_BUSY, &ns->ima_fs_flags))
>   		return -EBUSY;
> +
> +	filp->private_data = user_ns;
>   	return 0;
>   }
>   
> @@ -405,9 +434,13 @@ 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 = get_current_ns();
> +	struct ima_namespace *ns;
> +	struct user_namespace *user_ns;
>   	const char *cause = ns->valid_policy ? "completed" : "failed";
>   
> +	user_ns = ima_filp_private(filp);
> +	ns = user_ns->ima_ns
> +
>   	if ((file->f_flags & O_ACCMODE) == O_RDONLY)
>   		return seq_release(inode, file);
>   

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

* Re: [PATCH v5 13/16] ima: Move some IMA policy and filesystem related variables into ima_namespace
  2021-12-09 19:11   ` Christian Brauner
  2021-12-09 20:42     ` Stefan Berger
@ 2021-12-10  0:57     ` Stefan Berger
  2021-12-10 11:32       ` Christian Brauner
  1 sibling, 1 reply; 63+ messages in thread
From: Stefan Berger @ 2021-12-10  0:57 UTC (permalink / raw)
  To: Christian Brauner
  Cc: linux-integrity, zohar, serge, containers, dmitry.kasatkin,
	ebiederm, krzysztof.struczynski, roberto.sassu, mpeters, lhinds,
	lsturman, puiterwi, jejb, jamjoom, linux-kernel, paul, rgb,
	linux-security-module, jmorris


On 12/9/21 14:11, Christian Brauner wrote:
>
>  From 1f03dc427c583d5e9ebc9ebe9de77c3c535bbebe Mon Sep 17 00:00:00 2001
> From: Christian Brauner <christian.brauner@ubuntu.com>
> Date: Thu, 9 Dec 2021 20:07:02 +0100
> Subject: [PATCH] !!!! HERE BE DRAGONS - UNTESTED !!!!
>
> ---
>   security/integrity/ima/ima_fs.c | 43 +++++++++++++++++++++++++++++----
>   1 file changed, 38 insertions(+), 5 deletions(-)
>
> diff --git a/security/integrity/ima/ima_fs.c b/security/integrity/ima/ima_fs.c
> index 583462b29cb5..d5b302b925b8 100644
> --- a/security/integrity/ima/ima_fs.c
> +++ b/security/integrity/ima/ima_fs.c
> @@ -317,10 +317,14 @@ static ssize_t ima_read_policy(char *path)
>   static ssize_t ima_write_policy(struct file *file, const char __user *buf,
>   				size_t datalen, loff_t *ppos)
>   {
> -	struct ima_namespace *ns = get_current_ns();
> +	struct ima_namespace *ns;
> +	struct user_namespace *user_ns;
>   	char *data;
>   	ssize_t result;
>   
> +	user_ns = ima_filp_private(filp);
> +	ns = user_ns->ima_ns
> +
>   	if (datalen >= PAGE_SIZE)
>   		datalen = PAGE_SIZE - 1;
>   
> @@ -373,26 +377,51 @@ static const struct seq_operations ima_policy_seqops = {
>   };
>   #endif
>   
> +static struct user_namespace *ima_filp_private(struct file *filp)
> +{
> +	if (!(filp->f_flags & O_WRONLY)) {
> +#ifdef CONFIG_IMA_READ_POLICY
> +		struct seq_file *seq;
> +
> +		seq = filp->private_data;
> +		return seq->private;
> +#endif
> +	}
> +	return filp->private_data;
> +}
> +
>   /*
>    * ima_open_policy: sequentialize access to the policy file
>    */
>   static int ima_open_policy(struct inode *inode, struct file *filp)
>   {
> -	struct ima_namespace *ns = get_current_ns();
> +	struct user_namespace *user_ns = current_user_ns();


Do we have to take a reference on the user namespace assuming one can 
open the file, pass the fd down the hierarchy, and then the user 
namespace with the opened file goes away? Or is there anything else that 
keeps the user namespace alive?



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

* Re: [PATCH v5 13/16] ima: Move some IMA policy and filesystem related variables into ima_namespace
  2021-12-10  0:57     ` Stefan Berger
@ 2021-12-10 11:32       ` Christian Brauner
  2021-12-10 13:57         ` Stefan Berger
  2021-12-10 20:08         ` Stefan Berger
  0 siblings, 2 replies; 63+ messages in thread
From: Christian Brauner @ 2021-12-10 11:32 UTC (permalink / raw)
  To: Stefan Berger
  Cc: linux-integrity, zohar, serge, containers, dmitry.kasatkin,
	ebiederm, krzysztof.struczynski, roberto.sassu, mpeters, lhinds,
	lsturman, puiterwi, jejb, jamjoom, linux-kernel, paul, rgb,
	linux-security-module, jmorris

On Thu, Dec 09, 2021 at 07:57:02PM -0500, Stefan Berger wrote:
> 
> On 12/9/21 14:11, Christian Brauner wrote:
> > 
> >  From 1f03dc427c583d5e9ebc9ebe9de77c3c535bbebe Mon Sep 17 00:00:00 2001
> > From: Christian Brauner <christian.brauner@ubuntu.com>
> > Date: Thu, 9 Dec 2021 20:07:02 +0100
> > Subject: [PATCH] !!!! HERE BE DRAGONS - UNTESTED !!!!
> > 
> > ---
> >   security/integrity/ima/ima_fs.c | 43 +++++++++++++++++++++++++++++----
> >   1 file changed, 38 insertions(+), 5 deletions(-)
> > 
> > diff --git a/security/integrity/ima/ima_fs.c b/security/integrity/ima/ima_fs.c
> > index 583462b29cb5..d5b302b925b8 100644
> > --- a/security/integrity/ima/ima_fs.c
> > +++ b/security/integrity/ima/ima_fs.c
> > @@ -317,10 +317,14 @@ static ssize_t ima_read_policy(char *path)
> >   static ssize_t ima_write_policy(struct file *file, const char __user *buf,
> >   				size_t datalen, loff_t *ppos)
> >   {
> > -	struct ima_namespace *ns = get_current_ns();
> > +	struct ima_namespace *ns;
> > +	struct user_namespace *user_ns;
> >   	char *data;
> >   	ssize_t result;
> > +	user_ns = ima_filp_private(filp);
> > +	ns = user_ns->ima_ns
> > +
> >   	if (datalen >= PAGE_SIZE)
> >   		datalen = PAGE_SIZE - 1;
> > @@ -373,26 +377,51 @@ static const struct seq_operations ima_policy_seqops = {
> >   };
> >   #endif
> > +static struct user_namespace *ima_filp_private(struct file *filp)
> > +{
> > +	if (!(filp->f_flags & O_WRONLY)) {
> > +#ifdef CONFIG_IMA_READ_POLICY
> > +		struct seq_file *seq;
> > +
> > +		seq = filp->private_data;
> > +		return seq->private;
> > +#endif
> > +	}
> > +	return filp->private_data;
> > +}
> > +
> >   /*
> >    * ima_open_policy: sequentialize access to the policy file
> >    */
> >   static int ima_open_policy(struct inode *inode, struct file *filp)
> >   {
> > -	struct ima_namespace *ns = get_current_ns();
> > +	struct user_namespace *user_ns = current_user_ns();
> 
> 
> Do we have to take a reference on the user namespace assuming one can open
> the file, pass the fd down the hierarchy, and then the user namespace with
> the opened file goes away? Or is there anything else that keeps the user
> namespace alive?

No, we don't. When ima_policy_open() is called we do current_user_ns()
but that will be guaranteed to be identical to filp->f_cred->user_ns.
And f_cred is a reference that has been taken when the vfs allocated a
struct file for this .open call so won't go away until the last fput.

My proposal is also too complicated, I think.
(The booster is giving me the same side-effects as my second shot so
this looks like two good days of fever and headache. So I'll use that as
an excuse. :))

Your patch series as it stands has a bit of a security issue with those
get_current_ns() calls across differnet file/seq_file operations.

You have to make an architectural decision, I think. I see two sensible
options:
1. The relevant ima_ns that .open/.read/.write operate on is always taken
   to be the ima_ns of the filesystem's userns, i.e.
   sb->s_user_ns->ima_ns.
   This - but I'm not an ima person - makes the most sense to me and the
   semantics are straightforward. If I write to a file to alter some
   policy then I expect the ima namespace of the user namespace to be
   affected that the securityfs instance was mounted in.
2. The relevant ima_ns that .open/.read/.write operate on is always
   taken to be the one of the opener. I don't really like that as that
   gets weird if for some complicated reason the caller is not located
   in the userns the filesystem was mounted in (weird mount propagation
   scenario or sm). It also feels strange to operate on an ima_ns that's
   different from s_user_ns->ima_ns in a securityfs instance.

So I think I would propose you do sm:

From 6c8018f14f22e7bc2255dcebab96f9b8c39a8459 Mon Sep 17 00:00:00 2001
From: Christian Brauner <christian.brauner@ubuntu.com>
Date: Fri, 10 Dec 2021 10:31:27 +0100
Subject: [PATCH 1/2] !!!! HERE BE DRAGONS - COMPLETELY UNTESTED !!!!

The relevant ima_ns that .open/.read/.write operate on is always taken to be
the ima_ns of the filesystem's userns, i.e.  sb->s_user_ns->ima_ns. This - but
I'm not an ima person - makes the most sense to me and the semantics are
straightforward. If I write to a file to alter some policy then I expect the
ima namespace of the user namespace to be affected that the securityfs instance
was mounted in.
---
 security/integrity/ima/ima_fs.c     | 30 ++++++++++++++++++-----------
 security/integrity/ima/ima_policy.c |  8 ++++++--
 2 files changed, 25 insertions(+), 13 deletions(-)

diff --git a/security/integrity/ima/ima_fs.c b/security/integrity/ima/ima_fs.c
index 778983fd9a73..95b7b9ec2a76 100644
--- a/security/integrity/ima/ima_fs.c
+++ b/security/integrity/ima/ima_fs.c
@@ -49,7 +49,8 @@ static ssize_t ima_show_htable_violations(struct file *filp,
 					  char __user *buf,
 					  size_t count, loff_t *ppos)
 {
-	struct ima_namespace *ns = get_current_ns();
+	struct user_namespace *user_ns = filp->f_path.mnt->mnt_sb->s_user_ns;
+	struct ima_namespace *ns = user_ns->ima_ns;
 
 	return ima_show_htable_value(buf, count, ppos, &ns->ima_htable.violations);
 }
@@ -63,7 +64,8 @@ static ssize_t ima_show_measurements_count(struct file *filp,
 					   char __user *buf,
 					   size_t count, loff_t *ppos)
 {
-	struct ima_namespace *ns = get_current_ns();
+	struct user_namespace *user_ns = filp->f_path.mnt->mnt_sb->s_user_ns;
+	struct ima_namespace *ns = user_ns->ima_ns;
 
 	return ima_show_htable_value(buf, count, ppos, &ns->ima_htable.len);
 }
@@ -76,7 +78,9 @@ static const struct file_operations ima_measurements_count_ops = {
 /* returns pointer to hlist_node */
 static void *ima_measurements_start(struct seq_file *m, loff_t *pos)
 {
-	struct ima_namespace *ns = get_current_ns();
+	const struct file *filp = m->file;
+	struct user_namespace *user_ns = filp->f_path.mnt->mnt_sb->s_user_ns;
+	struct ima_namespace *ns = user_ns->ima_ns;
 	loff_t l = *pos;
 	struct ima_queue_entry *qe;
 
@@ -94,7 +98,9 @@ static void *ima_measurements_start(struct seq_file *m, loff_t *pos)
 
 static void *ima_measurements_next(struct seq_file *m, void *v, loff_t *pos)
 {
-	struct ima_namespace *ns = get_current_ns();
+	const struct file *filp = m->file;
+	struct user_namespace *user_ns = filp->f_path.mnt->mnt_sb->s_user_ns;
+	struct ima_namespace *ns = user_ns->ima_ns;
 	struct ima_queue_entry *qe = v;
 
 	/* lock protects when reading beyond last element
@@ -273,9 +279,8 @@ 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)
 {
-	struct ima_namespace *ns = get_current_ns();
 	void *data = NULL;
 	char *datap;
 	size_t size;
@@ -317,7 +322,8 @@ static ssize_t ima_read_policy(char *path)
 static ssize_t ima_write_policy(struct file *file, const char __user *buf,
 				size_t datalen, loff_t *ppos)
 {
-	struct ima_namespace *ns = get_current_ns();
+	struct user_namespace *user_ns = file->f_path.mnt->mnt_sb->s_user_ns;
+	struct ima_namespace *ns = user_ns->ima_ns;
 	char *data;
 	ssize_t result;
 
@@ -340,7 +346,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,
@@ -378,7 +384,8 @@ static const struct seq_operations ima_policy_seqops = {
  */
 static int ima_open_policy(struct inode *inode, struct file *filp)
 {
-	struct ima_namespace *ns = get_current_ns();
+	struct user_namespace *user_ns = inode->i_sb->s_user_ns;
+	struct ima_namespace *ns = user_ns->ima_ns;
 
 	if (!(filp->f_flags & O_WRONLY)) {
 #ifndef	CONFIG_IMA_READ_POLICY
@@ -386,7 +393,7 @@ static int ima_open_policy(struct inode *inode, struct file *filp)
 #else
 		if ((filp->f_flags & O_ACCMODE) != O_RDONLY)
 			return -EACCES;
-		if (!mac_admin_ns_capable(ns->user_ns))
+		if (!mac_admin_ns_capable(user_ns))
 			return -EPERM;
 		return seq_open(filp, &ima_policy_seqops);
 #endif
@@ -405,7 +412,8 @@ 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 = get_current_ns();
+	struct user_namespace *user_ns = inode->i_sb->s_user_ns;
+	struct ima_namespace *ns = user_ns->ima_ns;
 	const char *cause = ns->valid_policy ? "completed" : "failed";
 
 	if ((file->f_flags & O_ACCMODE) == O_RDONLY)
diff --git a/security/integrity/ima/ima_policy.c b/security/integrity/ima/ima_policy.c
index 747dca6131d6..41e5f17ec44d 100644
--- a/security/integrity/ima/ima_policy.c
+++ b/security/integrity/ima/ima_policy.c
@@ -1908,7 +1908,9 @@ static const char *const mask_tokens[] = {
 
 void *ima_policy_start(struct seq_file *m, loff_t *pos)
 {
-	struct ima_namespace *ns = get_current_ns();
+	const struct file *filp = m->file;
+	struct user_namespace *user_ns = filp->f_path.mnt->mnt_sb->user_ns;
+	struct ima_namespace *ns = user_ns->ima_ns;
 	loff_t l = *pos;
 	struct ima_rule_entry *entry;
 	struct list_head *ima_rules_tmp;
@@ -1928,7 +1930,9 @@ void *ima_policy_start(struct seq_file *m, loff_t *pos)
 void *ima_policy_next(struct seq_file *m, void *v, loff_t *pos)
 {
 	struct ima_rule_entry *entry = v;
-	struct ima_namespace *ns = get_current_ns();
+	const struct file *filp = m->file;
+	struct user_namespace *user_ns = filp->f_path.mnt->mnt_sb->user_ns;
+	struct ima_namespace *ns = user_ns->ima_ns;
 
 	rcu_read_lock();
 	entry = list_entry_rcu(entry->list.next, struct ima_rule_entry, list);
-- 
2.30.2

From ecf25d6b2b5895005d4103169bdb55d970e7a865 Mon Sep 17 00:00:00 2001
From: Christian Brauner <christian.brauner@ubuntu.com>
Date: Fri, 10 Dec 2021 11:56:25 +0100
Subject: [PATCH 2/2] !!!! HERE BE DRAGONS - COMPLETELY UNTESTED !!!!

securityfs: don't allow mounting from outside the filesystem's userns

If we ever need to allow that we should revisit the semantics.
---
 security/inode.c | 5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)

diff --git a/security/inode.c b/security/inode.c
index eaccba7017d9..71f9634228f3 100644
--- a/security/inode.c
+++ b/security/inode.c
@@ -43,7 +43,10 @@ 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;
+	int error = -EINVAL;
+
+	if (WARN_ON(ns != current_user_ns()))
+		return error;
 
 	error = simple_fill_super(sb, SECURITYFS_MAGIC, files);
 	if (error)
-- 
2.30.2


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

* Re: [PATCH v5 15/16] ima: Move dentries into ima_namespace
  2021-12-09 19:38         ` James Bottomley
  2021-12-09 20:13           ` Stefan Berger
@ 2021-12-10 11:49           ` Christian Brauner
  2021-12-10 12:09             ` Mimi Zohar
  2021-12-12 14:13             ` James Bottomley
  1 sibling, 2 replies; 63+ messages in thread
From: Christian Brauner @ 2021-12-10 11:49 UTC (permalink / raw)
  To: James Bottomley
  Cc: Stefan Berger, linux-integrity, zohar, serge, containers,
	dmitry.kasatkin, ebiederm, krzysztof.struczynski, roberto.sassu,
	mpeters, lhinds, lsturman, puiterwi, jamjoom, linux-kernel, paul,
	rgb, linux-security-module, jmorris

On Thu, Dec 09, 2021 at 02:38:13PM -0500, James Bottomley wrote:
> On Thu, 2021-12-09 at 10:30 -0500, James Bottomley wrote:
> > On Thu, 2021-12-09 at 15:37 +0100, Christian Brauner wrote:
> > > On Thu, Dec 09, 2021 at 03:34:28PM +0100, Christian Brauner wrote:
> > > > On Wed, Dec 08, 2021 at 05:18:17PM -0500, Stefan Berger wrote:
> > > > > Move the dentries into the ima_namespace for reuse by
> > > > > virtualized
> > > > > SecurityFS. Implement function freeing the dentries in order of
> > > > > files and symlinks before directories.
> > > > > 
> > > > > Signed-off-by: Stefan Berger <stefanb@linux.ibm.com>
> > > > > ---
> > > > 
> > > > This doesn't work as implemented, I think.
> > > > 
> > > > What I would have preferred and what I tried to explain in the
> > > > earlier review was:
> > > > Keep the dentry stashing global since it is only needed for
> > > > init_ima_ns.
> > > > Then struct ima_namespace becomes way smaller and simpler.
> > > > If you do that then it makes sense to remove the additional
> > > > dget() in securityfs_create_dentry() for non-init_ima_ns.
> > > > Then you can rely on auto-cleanup in .kill_sb() or on
> > > > ima_securityfs_init() failure and you only need to call
> > > > ima_fs_ns_free_dentries() if ns != init_ima_ns.
> > > > 
> > > > IIuc, it seems you're currently doing one dput() too many since
> > > > you're calling securityfs_remove() in the error path for non-
> > > > init_ima_ns which relies on the previous increased dget() which
> > > > we removed.
> > > 
> > > If you really want to move the dentry stashing into struct
> > > ima_namespace even though it's really unnecessary then you may as
> > > well not care about the auto-cleanup and keep that additional
> > > ima_fs_ns_free_dentries(ns) call in .kill_sb(). But I really think
> > > not dragging dentry stashing into struct ima_namespace is the
> > > correct way to go about this.
> > 
> > We, unfortunately, do have one case we can't avoid stashing for the
> > policy file.  It's this code in ima_release_policy:
> > 
> > > #if !defined(CONFIG_IMA_WRITE_POLICY) &&
> > > !defined(CONFIG_IMA_READ_POLICY)
> > > 	securityfs_remove(ns->dentry[IMAFS_DENTRY_IMA_POLICY]);
> > > 	ns->dentry[IMAFS_DENTRY_IMA_POLICY] = NULL;
> > > 
> > 
> > What it does is that in certain config options, the policy file entry
> > gets removed from the securityfs ima directory after you write to it.
> 
> This is what I have incremental to v5 that corrects all of this.  It
> actually keeps every dentry reference (including init_user_ns ones) at
> 1 so they can be reaped on unmount.  For the remove case it does
> d_delete and then puts the only reference.  This means
> securityfs_remove() works for the namespaced policy file as well.
> 
> I also got rid of the spurious initialized check in ima_securityfs_init
> because it prevents you doing a mount;umount;mount on securityfs within
> a namespace.
> 
> There's still the problem that if you write the policy, making the file
> disappear then unmount and remount securityfs it will come back.  My
> guess for fixing this is that we only stash the policy file reference,
> create it if NULL but then set the pointer to PTR_ERR(-EINVAL) or
> something and refuse to create it for that value.

Some sort of indicator that gets stashed in struct ima_ns that the file
does not get recreated on consecutive mounts. That shouldn't be hard to
fix.

> 
> James
> 
> ---
> 
> From 7de285a81ff06b6e0eb2c6db24810aeef9f6dd17 Mon Sep 17 00:00:00 2001
> From: James Bottomley <James.Bottomley@HansenPartnership.com>
> Date: Thu, 9 Dec 2021 19:33:49 +0000
> Subject: [PATCH] fix dentry ref counting
> 
> ---
>  security/inode.c                | 12 ++----------
>  security/integrity/ima/ima_fs.c |  4 ----
>  2 files changed, 2 insertions(+), 14 deletions(-)
> 
> diff --git a/security/inode.c b/security/inode.c
> index eaccba7017d9..b53152f7a625 100644
> --- a/security/inode.c
> +++ b/security/inode.c
> @@ -178,8 +178,6 @@ static struct dentry *securityfs_create_dentry(const char *name, umode_t mode,
>  		inode->i_fop = fops;
>  	}
>  	d_instantiate(dentry, inode);
> -	if (ns == &init_user_ns)
> -		dget(dentry);
>  	inode_unlock(dir);
>  	return dentry;
>  
> @@ -317,21 +315,15 @@ EXPORT_SYMBOL_GPL(securityfs_create_symlink);
>  void securityfs_remove(struct dentry *dentry)
>  {
>  	struct user_namespace *ns = dentry->d_sb->s_user_ns;
> -	struct inode *dir;
>  
>  	if (!dentry || IS_ERR(dentry))
>  		return;
>  
> -	dir = d_inode(dentry->d_parent);
> -	inode_lock(dir);
>  	if (simple_positive(dentry)) {
> -		if (d_is_dir(dentry))
> -			simple_rmdir(dir, dentry);
> -		else
> -			simple_unlink(dir, dentry);
> +		d_delete(dentry);

Not, that doesn't work. You can't just call d_delete() and dput() and
even if I wouldn't advise it. And you also can't do this without taking
the inode lock on the directory.
simple_rmdir()/simple_unlink() take care to update various inode fields
in the parent dir and handle link counts. This really wants to be sm
like

	struct inode *parent_inode;

	parent_inode = d_inode(dentry->d_parent);
	inode_lock(parent_inode);
	if (simple_positive(dentry)) {
		dget(dentry);
		if (d_is_dir(dentry)
			simple_unlink(parent_inode, dentry);
		else
			simple_unlink(parent_inode, dentry);
		d_delete(dentry);
		dput(dentry);
	}
	inode_unlock(parent_inode);

>  		dput(dentry);
>  	}
> -	inode_unlock(dir);
> +
>  	if (ns == &init_user_ns)
>  		simple_release_fs(&init_securityfs_mount,
>  				  &init_securityfs_mount_count);
> diff --git a/security/integrity/ima/ima_fs.c b/security/integrity/ima/ima_fs.c
> index 778983fd9a73..077a6ff46858 100644
> --- a/security/integrity/ima/ima_fs.c
> +++ b/security/integrity/ima/ima_fs.c
> @@ -466,10 +466,6 @@ int ima_securityfs_init(struct user_namespace *user_ns, struct dentry *root)
>  	struct ima_namespace *ns = user_ns->ima_ns;
>  	struct dentry *ima_dir;
>  
> -	/* already initialized? */
> -	if (ns->dentry[IMAFS_DENTRY_INTEGRITY_DIR])
> -		return 0;
> -
>  	/* FIXME: update when evm and integrity are namespaced */
>  	if (user_ns != &init_user_ns) {
>  		ns->dentry[IMAFS_DENTRY_INTEGRITY_DIR] =
> -- 
> 2.33.0
> 
> 
> 

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

* Re: [PATCH v5 15/16] ima: Move dentries into ima_namespace
  2021-12-10 11:49           ` Christian Brauner
@ 2021-12-10 12:09             ` Mimi Zohar
  2021-12-10 12:40               ` Stefan Berger
  2021-12-10 12:40               ` James Bottomley
  2021-12-12 14:13             ` James Bottomley
  1 sibling, 2 replies; 63+ messages in thread
From: Mimi Zohar @ 2021-12-10 12:09 UTC (permalink / raw)
  To: Christian Brauner, James Bottomley
  Cc: Stefan Berger, linux-integrity, serge, containers,
	dmitry.kasatkin, ebiederm, krzysztof.struczynski, roberto.sassu,
	mpeters, lhinds, lsturman, puiterwi, jamjoom, linux-kernel, paul,
	rgb, linux-security-module, jmorris

On Fri, 2021-12-10 at 12:49 +0100, Christian Brauner wrote:
> > There's still the problem that if you write the policy, making the file
> > disappear then unmount and remount securityfs it will come back.  My
> > guess for fixing this is that we only stash the policy file reference,
> > create it if NULL but then set the pointer to PTR_ERR(-EINVAL) or
> > something and refuse to create it for that value.
> 
> Some sort of indicator that gets stashed in struct ima_ns that the file
> does not get recreated on consecutive mounts. That shouldn't be hard to
> fix.

The policy file disappearing is for backwards compatibility, prior to
being able to extend the custom policy.  For embedded usecases,
allowing the policy to be written exactly once might makes sense.  Do
we really want/need to continue to support removing the policy in
namespaces?

thanks,

Mimi


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

* Re: [PATCH v5 15/16] ima: Move dentries into ima_namespace
  2021-12-10 12:09             ` Mimi Zohar
@ 2021-12-10 12:40               ` Stefan Berger
  2021-12-10 13:02                 ` Mimi Zohar
  2021-12-10 12:40               ` James Bottomley
  1 sibling, 1 reply; 63+ messages in thread
From: Stefan Berger @ 2021-12-10 12:40 UTC (permalink / raw)
  To: Mimi Zohar, Christian Brauner, James Bottomley
  Cc: linux-integrity, serge, containers, dmitry.kasatkin, ebiederm,
	krzysztof.struczynski, roberto.sassu, mpeters, lhinds, lsturman,
	puiterwi, jamjoom, linux-kernel, paul, rgb,
	linux-security-module, jmorris


On 12/10/21 07:09, Mimi Zohar wrote:
> On Fri, 2021-12-10 at 12:49 +0100, Christian Brauner wrote:
>>> There's still the problem that if you write the policy, making the file
>>> disappear then unmount and remount securityfs it will come back.  My
>>> guess for fixing this is that we only stash the policy file reference,
>>> create it if NULL but then set the pointer to PTR_ERR(-EINVAL) or
>>> something and refuse to create it for that value.
>> Some sort of indicator that gets stashed in struct ima_ns that the file
>> does not get recreated on consecutive mounts. That shouldn't be hard to
>> fix.
> The policy file disappearing is for backwards compatibility, prior to
> being able to extend the custom policy.  For embedded usecases,
> allowing the policy to be written exactly once might makes sense.  Do
> we really want/need to continue to support removing the policy in
> namespaces?

I don't have an answer but should the behavior for the same #define in 
this case be different for host and namespaces? Or should we just 
'select IMA_WRITE_POLICY and IMA_READ_POLICY' when IMA_NS is selected?


>
> thanks,
>
> Mimi
>

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

* Re: [PATCH v5 15/16] ima: Move dentries into ima_namespace
  2021-12-10 12:09             ` Mimi Zohar
  2021-12-10 12:40               ` Stefan Berger
@ 2021-12-10 12:40               ` James Bottomley
  2021-12-10 12:54                 ` Mimi Zohar
  1 sibling, 1 reply; 63+ messages in thread
From: James Bottomley @ 2021-12-10 12:40 UTC (permalink / raw)
  To: Mimi Zohar, Christian Brauner
  Cc: Stefan Berger, linux-integrity, serge, containers,
	dmitry.kasatkin, ebiederm, krzysztof.struczynski, roberto.sassu,
	mpeters, lhinds, lsturman, puiterwi, jamjoom, linux-kernel, paul,
	rgb, linux-security-module, jmorris

On Fri, 2021-12-10 at 07:09 -0500, Mimi Zohar wrote:
> On Fri, 2021-12-10 at 12:49 +0100, Christian Brauner wrote:
> > > There's still the problem that if you write the policy, making
> > > the file disappear then unmount and remount securityfs it will
> > > come back.  My guess for fixing this is that we only stash the
> > > policy file reference, create it if NULL but then set the pointer
> > > to PTR_ERR(-EINVAL) or something and refuse to create it for that
> > > value.
> > 
> > Some sort of indicator that gets stashed in struct ima_ns that the
> > file does not get recreated on consecutive mounts. That shouldn't
> > be hard to fix.

Yes, Stefan said he was doing that.

> The policy file disappearing is for backwards compatibility, prior to
> being able to extend the custom policy.  For embedded usecases,
> allowing the policy to be written exactly once might makes sense.  Do
> we really want/need to continue to support removing the policy in
> namespaces?

The embedded world tends also to be a big consumer of namespaces, so if
this semantic is for them, likely it should remain in the namespaced
IMA.

But how necessary is the semantic?  If we got rid of it from the whole
of IMA, what would break? If we can't think of anything it could likely
be removed from both namespaced and non-namespaced IMA.

James



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

* Re: [PATCH v5 15/16] ima: Move dentries into ima_namespace
  2021-12-10 12:40               ` James Bottomley
@ 2021-12-10 12:54                 ` Mimi Zohar
  0 siblings, 0 replies; 63+ messages in thread
From: Mimi Zohar @ 2021-12-10 12:54 UTC (permalink / raw)
  To: jejb, Christian Brauner
  Cc: Stefan Berger, linux-integrity, serge, containers,
	dmitry.kasatkin, ebiederm, krzysztof.struczynski, roberto.sassu,
	mpeters, lhinds, lsturman, puiterwi, jamjoom, linux-kernel, paul,
	rgb, linux-security-module, jmorris

On Fri, 2021-12-10 at 07:40 -0500, James Bottomley wrote:
> On Fri, 2021-12-10 at 07:09 -0500, Mimi Zohar wrote:
> > On Fri, 2021-12-10 at 12:49 +0100, Christian Brauner wrote:
> > > > There's still the problem that if you write the policy, making
> > > > the file disappear then unmount and remount securityfs it will
> > > > come back.  My guess for fixing this is that we only stash the
> > > > policy file reference, create it if NULL but then set the pointer
> > > > to PTR_ERR(-EINVAL) or something and refuse to create it for that
> > > > value.
> > > 
> > > Some sort of indicator that gets stashed in struct ima_ns that the
> > > file does not get recreated on consecutive mounts. That shouldn't
> > > be hard to fix.
> 
> Yes, Stefan said he was doing that.
> 
> > The policy file disappearing is for backwards compatibility, prior to
> > being able to extend the custom policy.  For embedded usecases,
> > allowing the policy to be written exactly once might makes sense.  Do
> > we really want/need to continue to support removing the policy in
> > namespaces?
> 
> The embedded world tends also to be a big consumer of namespaces, so if
> this semantic is for them, likely it should remain in the namespaced
> IMA.

Think of a simple device that loads a custom IMA policy, which never
changes once loaded.
> 
> But how necessary is the semantic?  If we got rid of it from the whole
> of IMA, what would break? If we can't think of anything it could likely
> be removed from both namespaced and non-namespaced IMA.

The question isn't an issue of "breaking", but of leaking info.  If
this isn't a real concern, then the ability of removing the securityfs
isn't needed.

thanks,

Mimi


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

* Re: [PATCH v5 15/16] ima: Move dentries into ima_namespace
  2021-12-10 12:40               ` Stefan Berger
@ 2021-12-10 13:02                 ` Mimi Zohar
  2021-12-10 14:17                   ` Stefan Berger
  0 siblings, 1 reply; 63+ messages in thread
From: Mimi Zohar @ 2021-12-10 13:02 UTC (permalink / raw)
  To: Stefan Berger, Christian Brauner, James Bottomley
  Cc: linux-integrity, serge, containers, dmitry.kasatkin, ebiederm,
	krzysztof.struczynski, roberto.sassu, mpeters, lhinds, lsturman,
	puiterwi, jamjoom, linux-kernel, paul, rgb,
	linux-security-module, jmorris

On Fri, 2021-12-10 at 07:40 -0500, Stefan Berger wrote:
> On 12/10/21 07:09, Mimi Zohar wrote:
> > On Fri, 2021-12-10 at 12:49 +0100, Christian Brauner wrote:
> >>> There's still the problem that if you write the policy, making the file
> >>> disappear then unmount and remount securityfs it will come back.  My
> >>> guess for fixing this is that we only stash the policy file reference,
> >>> create it if NULL but then set the pointer to PTR_ERR(-EINVAL) or
> >>> something and refuse to create it for that value.
> >> Some sort of indicator that gets stashed in struct ima_ns that the file
> >> does not get recreated on consecutive mounts. That shouldn't be hard to
> >> fix.
> > The policy file disappearing is for backwards compatibility, prior to
> > being able to extend the custom policy.  For embedded usecases,
> > allowing the policy to be written exactly once might makes sense.  Do
> > we really want/need to continue to support removing the policy in
> > namespaces?
> 
> I don't have an answer but should the behavior for the same #define in 
> this case be different for host and namespaces? Or should we just 
> 'select IMA_WRITE_POLICY and IMA_READ_POLICY' when IMA_NS is selected?

The latter option sounds good.  Being able to analyze the namespace
policy is really important.

thanks,

Mimi


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

* Re: [PATCH v5 13/16] ima: Move some IMA policy and filesystem related variables into ima_namespace
  2021-12-10 11:32       ` Christian Brauner
@ 2021-12-10 13:57         ` Stefan Berger
  2021-12-10 14:21           ` James Bottomley
  2021-12-11  9:50           ` Christian Brauner
  2021-12-10 20:08         ` Stefan Berger
  1 sibling, 2 replies; 63+ messages in thread
From: Stefan Berger @ 2021-12-10 13:57 UTC (permalink / raw)
  To: Christian Brauner
  Cc: linux-integrity, zohar, serge, containers, dmitry.kasatkin,
	ebiederm, krzysztof.struczynski, roberto.sassu, mpeters, lhinds,
	lsturman, puiterwi, jejb, jamjoom, linux-kernel, paul, rgb,
	linux-security-module, jmorris


On 12/10/21 06:32, Christian Brauner wrote:
> On Thu, Dec 09, 2021 at 07:57:02PM -0500, Stefan Berger wrote:
>> On 12/9/21 14:11, Christian Brauner wrote:
>>>   From 1f03dc427c583d5e9ebc9ebe9de77c3c535bbebe Mon Sep 17 00:00:00 2001
>>> From: Christian Brauner <christian.brauner@ubuntu.com>
>>> Date: Thu, 9 Dec 2021 20:07:02 +0100
>>> Subject: [PATCH] !!!! HERE BE DRAGONS - UNTESTED !!!!
>>>
>>> ---
>>>    security/integrity/ima/ima_fs.c | 43 +++++++++++++++++++++++++++++----
>>>    1 file changed, 38 insertions(+), 5 deletions(-)
>>>
>>> diff --git a/security/integrity/ima/ima_fs.c b/security/integrity/ima/ima_fs.c
>>> index 583462b29cb5..d5b302b925b8 100644
>>> --- a/security/integrity/ima/ima_fs.c
>>> +++ b/security/integrity/ima/ima_fs.c
>>> @@ -317,10 +317,14 @@ static ssize_t ima_read_policy(char *path)
>>>    static ssize_t ima_write_policy(struct file *file, const char __user *buf,
>>>    				size_t datalen, loff_t *ppos)
>>>    {
>>> -	struct ima_namespace *ns = get_current_ns();
>>> +	struct ima_namespace *ns;
>>> +	struct user_namespace *user_ns;
>>>    	char *data;
>>>    	ssize_t result;
>>> +	user_ns = ima_filp_private(filp);
>>> +	ns = user_ns->ima_ns
>>> +
>>>    	if (datalen >= PAGE_SIZE)
>>>    		datalen = PAGE_SIZE - 1;
>>> @@ -373,26 +377,51 @@ static const struct seq_operations ima_policy_seqops = {
>>>    };
>>>    #endif
>>> +static struct user_namespace *ima_filp_private(struct file *filp)
>>> +{
>>> +	if (!(filp->f_flags & O_WRONLY)) {
>>> +#ifdef CONFIG_IMA_READ_POLICY
>>> +		struct seq_file *seq;
>>> +
>>> +		seq = filp->private_data;
>>> +		return seq->private;
>>> +#endif
>>> +	}
>>> +	return filp->private_data;
>>> +}
>>> +
>>>    /*
>>>     * ima_open_policy: sequentialize access to the policy file
>>>     */
>>>    static int ima_open_policy(struct inode *inode, struct file *filp)
>>>    {
>>> -	struct ima_namespace *ns = get_current_ns();
>>> +	struct user_namespace *user_ns = current_user_ns();
>>
>> Do we have to take a reference on the user namespace assuming one can open
>> the file, pass the fd down the hierarchy, and then the user namespace with
>> the opened file goes away? Or is there anything else that keeps the user
>> namespace alive?
> No, we don't. When ima_policy_open() is called we do current_user_ns()
> but that will be guaranteed to be identical to filp->f_cred->user_ns.
> And f_cred is a reference that has been taken when the vfs allocated a
> struct file for this .open call so won't go away until the last fput.
>
> My proposal is also too complicated, I think.
> (The booster is giving me the same side-effects as my second shot so
> this looks like two good days of fever and headache. So I'll use that as
> an excuse. :))
>
> Your patch series as it stands has a bit of a security issue with those
> get_current_ns() calls across differnet file/seq_file operations.
>
> You have to make an architectural decision, I think. I see two sensible
> options:
> 1. The relevant ima_ns that .open/.read/.write operate on is always taken
>     to be the ima_ns of the filesystem's userns, i.e.
>     sb->s_user_ns->ima_ns.
>     This - but I'm not an ima person - makes the most sense to me and the
>     semantics are straightforward. If I write to a file to alter some
>     policy then I expect the ima namespace of the user namespace to be
>     affected that the securityfs instance was mounted in.
> 2. The relevant ima_ns that .open/.read/.write operate on is always
>     taken to be the one of the opener. I don't really like that as that
>     gets weird if for some complicated reason the caller is not located
>     in the userns the filesystem was mounted in (weird mount propagation
>     scenario or sm). It also feels strange to operate on an ima_ns that's
>     different from s_user_ns->ima_ns in a securityfs instance.

We have this situation because one can setns() to another mount 
namespaces but the data shown by SecurityFS lives in a user namespace, 
right? And now we need to decide whether to affect the data in the user 
namespace  that did the open (option 2) or to which the SecurityFS 
belongs to (option 1). If we were to open a regular file it would be 
option 1, so we should probably not break that existing semantic and 
also choose option 1 unless there one wasn't allowed to choose the user 
namespace the SecurityFS files belonged to then it should be option 2 
but then we have file descriptor passing where 'being allowed' can 
change depending on who is reading/writing a file... Is there anything 
that would prevent us from setns()'ing to that target user namespace so 
that we would now see that of a user namespace that we are not allowed 
to see?

Following man page of setns:

"   User namespaces
               A process reassociating itself with a user namespace must
               have the CAP_SYS_ADMIN capability in the target user
               namespace.  (This necessarily implies that it is only
               possible to join a descendant user namespace.)  Upon
               successfully joining a user namespace, a process is
               granted all capabilities in that namespace, regardless of
               its user and group IDs."


So if we choose option 1 maybe we have to test for this capability upon 
every read/write from/to a file?


    Stefan


>
> So I think I would propose you do sm:
>
>  From 6c8018f14f22e7bc2255dcebab96f9b8c39a8459 Mon Sep 17 00:00:00 2001
> From: Christian Brauner <christian.brauner@ubuntu.com>
> Date: Fri, 10 Dec 2021 10:31:27 +0100
> Subject: [PATCH 1/2] !!!! HERE BE DRAGONS - COMPLETELY UNTESTED !!!!
>
> The relevant ima_ns that .open/.read/.write operate on is always taken to be
> the ima_ns of the filesystem's userns, i.e.  sb->s_user_ns->ima_ns. This - but
> I'm not an ima person - makes the most sense to me and the semantics are
> straightforward. If I write to a file to alter some policy then I expect the
> ima namespace of the user namespace to be affected that the securityfs instance
> was mounted in.
> ---
>   security/integrity/ima/ima_fs.c     | 30 ++++++++++++++++++-----------
>   security/integrity/ima/ima_policy.c |  8 ++++++--
>   2 files changed, 25 insertions(+), 13 deletions(-)
>
> diff --git a/security/integrity/ima/ima_fs.c b/security/integrity/ima/ima_fs.c
> index 778983fd9a73..95b7b9ec2a76 100644
> --- a/security/integrity/ima/ima_fs.c
> +++ b/security/integrity/ima/ima_fs.c
> @@ -49,7 +49,8 @@ static ssize_t ima_show_htable_violations(struct file *filp,
>   					  char __user *buf,
>   					  size_t count, loff_t *ppos)
>   {
> -	struct ima_namespace *ns = get_current_ns();
> +	struct user_namespace *user_ns = filp->f_path.mnt->mnt_sb->s_user_ns;
> +	struct ima_namespace *ns = user_ns->ima_ns;
>   
>   	return ima_show_htable_value(buf, count, ppos, &ns->ima_htable.violations);
>   }
> @@ -63,7 +64,8 @@ static ssize_t ima_show_measurements_count(struct file *filp,
>   					   char __user *buf,
>   					   size_t count, loff_t *ppos)
>   {
> -	struct ima_namespace *ns = get_current_ns();
> +	struct user_namespace *user_ns = filp->f_path.mnt->mnt_sb->s_user_ns;
> +	struct ima_namespace *ns = user_ns->ima_ns;
>   
>   	return ima_show_htable_value(buf, count, ppos, &ns->ima_htable.len);
>   }
> @@ -76,7 +78,9 @@ static const struct file_operations ima_measurements_count_ops = {
>   /* returns pointer to hlist_node */
>   static void *ima_measurements_start(struct seq_file *m, loff_t *pos)
>   {
> -	struct ima_namespace *ns = get_current_ns();
> +	const struct file *filp = m->file;
> +	struct user_namespace *user_ns = filp->f_path.mnt->mnt_sb->s_user_ns;
> +	struct ima_namespace *ns = user_ns->ima_ns;
>   	loff_t l = *pos;
>   	struct ima_queue_entry *qe;
>   
> @@ -94,7 +98,9 @@ static void *ima_measurements_start(struct seq_file *m, loff_t *pos)
>   
>   static void *ima_measurements_next(struct seq_file *m, void *v, loff_t *pos)
>   {
> -	struct ima_namespace *ns = get_current_ns();
> +	const struct file *filp = m->file;
> +	struct user_namespace *user_ns = filp->f_path.mnt->mnt_sb->s_user_ns;
> +	struct ima_namespace *ns = user_ns->ima_ns;
>   	struct ima_queue_entry *qe = v;
>   
>   	/* lock protects when reading beyond last element
> @@ -273,9 +279,8 @@ 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)
>   {
> -	struct ima_namespace *ns = get_current_ns();
>   	void *data = NULL;
>   	char *datap;
>   	size_t size;
> @@ -317,7 +322,8 @@ static ssize_t ima_read_policy(char *path)
>   static ssize_t ima_write_policy(struct file *file, const char __user *buf,
>   				size_t datalen, loff_t *ppos)
>   {
> -	struct ima_namespace *ns = get_current_ns();
> +	struct user_namespace *user_ns = file->f_path.mnt->mnt_sb->s_user_ns;
> +	struct ima_namespace *ns = user_ns->ima_ns;
>   	char *data;
>   	ssize_t result;
>   
> @@ -340,7 +346,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,
> @@ -378,7 +384,8 @@ static const struct seq_operations ima_policy_seqops = {
>    */
>   static int ima_open_policy(struct inode *inode, struct file *filp)
>   {
> -	struct ima_namespace *ns = get_current_ns();
> +	struct user_namespace *user_ns = inode->i_sb->s_user_ns;
> +	struct ima_namespace *ns = user_ns->ima_ns;
>   
>   	if (!(filp->f_flags & O_WRONLY)) {
>   #ifndef	CONFIG_IMA_READ_POLICY
> @@ -386,7 +393,7 @@ static int ima_open_policy(struct inode *inode, struct file *filp)
>   #else
>   		if ((filp->f_flags & O_ACCMODE) != O_RDONLY)
>   			return -EACCES;
> -		if (!mac_admin_ns_capable(ns->user_ns))
> +		if (!mac_admin_ns_capable(user_ns))
>   			return -EPERM;
>   		return seq_open(filp, &ima_policy_seqops);
>   #endif
> @@ -405,7 +412,8 @@ 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 = get_current_ns();
> +	struct user_namespace *user_ns = inode->i_sb->s_user_ns;
> +	struct ima_namespace *ns = user_ns->ima_ns;
>   	const char *cause = ns->valid_policy ? "completed" : "failed";
>   
>   	if ((file->f_flags & O_ACCMODE) == O_RDONLY)
> diff --git a/security/integrity/ima/ima_policy.c b/security/integrity/ima/ima_policy.c
> index 747dca6131d6..41e5f17ec44d 100644
> --- a/security/integrity/ima/ima_policy.c
> +++ b/security/integrity/ima/ima_policy.c
> @@ -1908,7 +1908,9 @@ static const char *const mask_tokens[] = {
>   
>   void *ima_policy_start(struct seq_file *m, loff_t *pos)
>   {
> -	struct ima_namespace *ns = get_current_ns();
> +	const struct file *filp = m->file;
> +	struct user_namespace *user_ns = filp->f_path.mnt->mnt_sb->user_ns;
> +	struct ima_namespace *ns = user_ns->ima_ns;
>   	loff_t l = *pos;
>   	struct ima_rule_entry *entry;
>   	struct list_head *ima_rules_tmp;
> @@ -1928,7 +1930,9 @@ void *ima_policy_start(struct seq_file *m, loff_t *pos)
>   void *ima_policy_next(struct seq_file *m, void *v, loff_t *pos)
>   {
>   	struct ima_rule_entry *entry = v;
> -	struct ima_namespace *ns = get_current_ns();
> +	const struct file *filp = m->file;
> +	struct user_namespace *user_ns = filp->f_path.mnt->mnt_sb->user_ns;
> +	struct ima_namespace *ns = user_ns->ima_ns;
>   
>   	rcu_read_lock();
>   	entry = list_entry_rcu(entry->list.next, struct ima_rule_entry, list);

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

* Re: [PATCH v5 15/16] ima: Move dentries into ima_namespace
  2021-12-10 13:02                 ` Mimi Zohar
@ 2021-12-10 14:17                   ` Stefan Berger
  2021-12-10 14:26                     ` James Bottomley
  0 siblings, 1 reply; 63+ messages in thread
From: Stefan Berger @ 2021-12-10 14:17 UTC (permalink / raw)
  To: Mimi Zohar, Christian Brauner, James Bottomley
  Cc: linux-integrity, serge, containers, dmitry.kasatkin, ebiederm,
	krzysztof.struczynski, roberto.sassu, mpeters, lhinds, lsturman,
	puiterwi, jamjoom, linux-kernel, paul, rgb,
	linux-security-module, jmorris


On 12/10/21 08:02, Mimi Zohar wrote:
> On Fri, 2021-12-10 at 07:40 -0500, Stefan Berger wrote:
>> On 12/10/21 07:09, Mimi Zohar wrote:
>>> On Fri, 2021-12-10 at 12:49 +0100, Christian Brauner wrote:
>>>>> There's still the problem that if you write the policy, making the file
>>>>> disappear then unmount and remount securityfs it will come back.  My
>>>>> guess for fixing this is that we only stash the policy file reference,
>>>>> create it if NULL but then set the pointer to PTR_ERR(-EINVAL) or
>>>>> something and refuse to create it for that value.
>>>> Some sort of indicator that gets stashed in struct ima_ns that the file
>>>> does not get recreated on consecutive mounts. That shouldn't be hard to
>>>> fix.
>>> The policy file disappearing is for backwards compatibility, prior to
>>> being able to extend the custom policy.  For embedded usecases,
>>> allowing the policy to be written exactly once might makes sense.  Do
>>> we really want/need to continue to support removing the policy in
>>> namespaces?
>> I don't have an answer but should the behavior for the same #define in
>> this case be different for host and namespaces? Or should we just
>> 'select IMA_WRITE_POLICY and IMA_READ_POLICY' when IMA_NS is selected?
> The latter option sounds good.  Being able to analyze the namespace
> policy is really important.

Ok, I will adjust the Kconfig for this then. This then warrants the 
question whether to move the dentry into the ima_namespace. The current 
code looks like this.

#if !defined(CONFIG_IMA_WRITE_POLICY) && !defined(CONFIG_IMA_READ_POLICY)
         securityfs_remove(ns->policy_dentry);
         ns->policy_dentry = NULL;
         ns->policy_dentry_removed = true;
#elif defined(CONFIG_IMA_WRITE_POLICY)

With IMA_NS selecting IMA_WRITE_POLICY and IMA_READ_POLICY the above 
wouldn't be necessary anymore but I find it 'cleaner' to still have the 
dentry isolated rather than it being a global static as it was before...


>
> thanks,
>
> Mimi
>

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

* Re: [PATCH v5 13/16] ima: Move some IMA policy and filesystem related variables into ima_namespace
  2021-12-10 13:57         ` Stefan Berger
@ 2021-12-10 14:21           ` James Bottomley
  2021-12-11  9:50           ` Christian Brauner
  1 sibling, 0 replies; 63+ messages in thread
From: James Bottomley @ 2021-12-10 14:21 UTC (permalink / raw)
  To: Stefan Berger, Christian Brauner
  Cc: linux-integrity, zohar, serge, containers, dmitry.kasatkin,
	ebiederm, krzysztof.struczynski, roberto.sassu, mpeters, lhinds,
	lsturman, puiterwi, jamjoom, linux-kernel, paul, rgb,
	linux-security-module, jmorris

On Fri, 2021-12-10 at 08:57 -0500, Stefan Berger wrote:
> On 12/10/21 06:32, Christian Brauner wrote:
> > On Thu, Dec 09, 2021 at 07:57:02PM -0500, Stefan Berger wrote:
> > > On 12/9/21 14:11, Christian Brauner wrote:
> > > >   From 1f03dc427c583d5e9ebc9ebe9de77c3c535bbebe Mon Sep 17
> > > > 00:00:00 2001
> > > > From: Christian Brauner <christian.brauner@ubuntu.com>
> > > > Date: Thu, 9 Dec 2021 20:07:02 +0100
> > > > Subject: [PATCH] !!!! HERE BE DRAGONS - UNTESTED !!!!
> > > > 
> > > > ---
> > > >    security/integrity/ima/ima_fs.c | 43
> > > > +++++++++++++++++++++++++++++----
> > > >    1 file changed, 38 insertions(+), 5 deletions(-)
> > > > 
> > > > diff --git a/security/integrity/ima/ima_fs.c
> > > > b/security/integrity/ima/ima_fs.c
> > > > index 583462b29cb5..d5b302b925b8 100644
> > > > --- a/security/integrity/ima/ima_fs.c
> > > > +++ b/security/integrity/ima/ima_fs.c
> > > > @@ -317,10 +317,14 @@ static ssize_t ima_read_policy(char
> > > > *path)
> > > >    static ssize_t ima_write_policy(struct file *file, const
> > > > char __user *buf,
> > > >    				size_t datalen, loff_t *ppos)
> > > >    {
> > > > -	struct ima_namespace *ns = get_current_ns();
> > > > +	struct ima_namespace *ns;
> > > > +	struct user_namespace *user_ns;
> > > >    	char *data;
> > > >    	ssize_t result;
> > > > +	user_ns = ima_filp_private(filp);
> > > > +	ns = user_ns->ima_ns
> > > > +
> > > >    	if (datalen >= PAGE_SIZE)
> > > >    		datalen = PAGE_SIZE - 1;
> > > > @@ -373,26 +377,51 @@ static const struct seq_operations
> > > > ima_policy_seqops = {
> > > >    };
> > > >    #endif
> > > > +static struct user_namespace *ima_filp_private(struct file
> > > > *filp)
> > > > +{
> > > > +	if (!(filp->f_flags & O_WRONLY)) {
> > > > +#ifdef CONFIG_IMA_READ_POLICY
> > > > +		struct seq_file *seq;
> > > > +
> > > > +		seq = filp->private_data;
> > > > +		return seq->private;
> > > > +#endif
> > > > +	}
> > > > +	return filp->private_data;
> > > > +}
> > > > +
> > > >    /*
> > > >     * ima_open_policy: sequentialize access to the policy file
> > > >     */
> > > >    static int ima_open_policy(struct inode *inode, struct file
> > > > *filp)
> > > >    {
> > > > -	struct ima_namespace *ns = get_current_ns();
> > > > +	struct user_namespace *user_ns = current_user_ns();
> > > 
> > > Do we have to take a reference on the user namespace assuming one
> > > can open
> > > the file, pass the fd down the hierarchy, and then the user
> > > namespace with
> > > the opened file goes away? Or is there anything else that keeps
> > > the user
> > > namespace alive?
> > No, we don't. When ima_policy_open() is called we do
> > current_user_ns() but that will be guaranteed to be identical to
> > filp->f_cred->user_ns. And f_cred is a reference that has been
> > taken when the vfs allocated a struct file for this .open call so
> > won't go away until the last fput.
> > 
> > My proposal is also too complicated, I think.
> > (The booster is giving me the same side-effects as my second shot
> > so this looks like two good days of fever and headache. So I'll use
> > that as an excuse. :))
> > 
> > Your patch series as it stands has a bit of a security issue with
> > those get_current_ns() calls across differnet file/seq_file
> > operations. 
> > You have to make an architectural decision, I think. I see two
> > sensible options:
> > 1. The relevant ima_ns that .open/.read/.write operate on is always
> > taken to be the ima_ns of the filesystem's userns, i.e. sb-
> > >s_user_ns->ima_ns.
> >     This - but I'm not an ima person - makes the most sense to me
> > and the semantics are straightforward. If I write to a file to
> > alter some policy then I expect the ima namespace of the user
> > namespace to be affected that the securityfs instance was mounted
> > in.
> > 2. The relevant ima_ns that .open/.read/.write operate on is always
> > taken to be the one of the opener. I don't really like that as that
> > gets weird if for some complicated reason the caller is not located
> > in the userns the filesystem was mounted in (weird mount
> > propagation scenario or sm). It also feels strange to operate on an
> > ima_ns that's different from s_user_ns->ima_ns in a securityfs
> > instance.
> 
> We have this situation because one can setns() to another mount 
> namespaces but the data shown by SecurityFS lives in a user
> namespace,  right?

Well, not necessarily.  There is another case where only the userns is
unshared and securityfs is never mounted inside the container.  If the
process has the capability to open the securityfs files (kubernetes
privileged container, say), what should it see? The analogue with the
pid namespace says it should see the contents of the what the parent
had mounted because if it wanted to see its own it would have done a
mount of securityfs inside the userns.  This argues for sb->s_user_ns-
>ima_ns.

for the setns mount namespace case, the vfsmnt tree is duplicated, so
if the securityfs sb->s_user_ns is your user namespace in the prior
mount namespace, it will end up being so in the new one.  sb->s_user_ns 
only changes on actual mount.

>  And now we need to decide whether to affect the data in the
> user namespace  that did the open (option 2) or to which the
> SecurityFS  belongs to (option 1). If we were to open a regular file
> it would be option 1, so we should probably not break that existing
> semantic and also choose option 1 unless there one wasn't allowed to
> choose the user namespace the SecurityFS files belonged to then it
> should be option 2 

Once the userns is unshared, IMA accounting is done inside the
namespace.  However, in order to see the results, the container must
mount securityfs in the userns.  I can't think of a good reason why a
privileged container should want to be accounted separately but see the
results of its parents, but similarly I can't see why a pid namespace
should want to see /proc of its parent either ... yet that's the
semantic we have today.

> but then we have file descriptor passing where 'being allowed' can 
> change depending on who is reading/writing a file... Is there
> anything that would prevent us from setns()'ing to that target user
> namespace so that we would now see that of a user namespace that we
> are not allowed to see?

If you're able to setns to a user namespace, you logically have all its
privileges, so that problem shouldn't arise.

Option 2 is basically sliding back towards securityfs magically
changing properties depending on which userns is asking.  If we're
going to support that, I don't see what was wrong with the owner/guid
magically changing as well like I first propsed.  If we're going to
insist on a new mount of securityfs, I think it has to function cleanly
like the pid namespace, so option 1 is required.

James



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

* Re: [PATCH v5 15/16] ima: Move dentries into ima_namespace
  2021-12-10 14:17                   ` Stefan Berger
@ 2021-12-10 14:26                     ` James Bottomley
  2021-12-10 15:26                       ` Mimi Zohar
  0 siblings, 1 reply; 63+ messages in thread
From: James Bottomley @ 2021-12-10 14:26 UTC (permalink / raw)
  To: Stefan Berger, Mimi Zohar, Christian Brauner
  Cc: linux-integrity, serge, containers, dmitry.kasatkin, ebiederm,
	krzysztof.struczynski, roberto.sassu, mpeters, lhinds, lsturman,
	puiterwi, jamjoom, linux-kernel, paul, rgb,
	linux-security-module, jmorris

On Fri, 2021-12-10 at 09:17 -0500, Stefan Berger wrote:
> On 12/10/21 08:02, Mimi Zohar wrote:
> > On Fri, 2021-12-10 at 07:40 -0500, Stefan Berger wrote:
> > > On 12/10/21 07:09, Mimi Zohar wrote:
> > > > On Fri, 2021-12-10 at 12:49 +0100, Christian Brauner wrote:
> > > > > > There's still the problem that if you write the policy,
> > > > > > making the file disappear then unmount and remount
> > > > > > securityfs it will come back.  My guess for fixing this is
> > > > > > that we only stash the policy file reference,
> > > > > > create it if NULL but then set the pointer to PTR_ERR(-
> > > > > > EINVAL) or something and refuse to create it for that
> > > > > > value.
> > > > > Some sort of indicator that gets stashed in struct ima_ns
> > > > > that the file does not get recreated on consecutive mounts.
> > > > > That shouldn't be hard to fix.
> > > > The policy file disappearing is for backwards compatibility,
> > > > prior to being able to extend the custom policy.  For embedded
> > > > usecases, allowing the policy to be written exactly once might
> > > > makes sense.  Do we really want/need to continue to support
> > > > removing the policy in namespaces?
> > > I don't have an answer but should the behavior for the same
> > > #define in this case be different for host and namespaces? Or
> > > should we just 'select IMA_WRITE_POLICY and IMA_READ_POLICY' when
> > > IMA_NS is selected?
> > The latter option sounds good.  Being able to analyze the namespace
> > policy is really important.
> 
> Ok, I will adjust the Kconfig for this then. This then warrants the 
> question whether to move the dentry into the ima_namespace. The
> current code looks like this.
> 
> #if !defined(CONFIG_IMA_WRITE_POLICY) &&
> !defined(CONFIG_IMA_READ_POLICY)
>          securityfs_remove(ns->policy_dentry);
>          ns->policy_dentry = NULL;
>          ns->policy_dentry_removed = true;
> #elif defined(CONFIG_IMA_WRITE_POLICY)
> 
> With IMA_NS selecting IMA_WRITE_POLICY and IMA_READ_POLICY the above 
> wouldn't be necessary anymore but I find it 'cleaner' to still have
> the dentry isolated rather than it being a global static as it was
> before...

This is really, really why you don't want the semantics inside the
namespace to differ from those outside, because it creates confusion
for the people reading the code, especially with magically forced
config options like this.  I'd strongly suggest you either keep the
semantic in the namespace or eliminate it entirely.

If you really, really have to make the namespace behave differently,
then use global variables and put a big comment on that code saying it
can never be reached once CONFIG_IMA_NS is enabled.

James



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

* Re: [PATCH v5 15/16] ima: Move dentries into ima_namespace
  2021-12-10 14:26                     ` James Bottomley
@ 2021-12-10 15:26                       ` Mimi Zohar
  2021-12-10 15:32                         ` Stefan Berger
  0 siblings, 1 reply; 63+ messages in thread
From: Mimi Zohar @ 2021-12-10 15:26 UTC (permalink / raw)
  To: jejb, Stefan Berger, Christian Brauner
  Cc: linux-integrity, serge, containers, dmitry.kasatkin, ebiederm,
	krzysztof.struczynski, roberto.sassu, mpeters, lhinds, lsturman,
	puiterwi, jamjoom, linux-kernel, paul, rgb,
	linux-security-module, jmorris

On Fri, 2021-12-10 at 09:26 -0500, James Bottomley wrote:
> On Fri, 2021-12-10 at 09:17 -0500, Stefan Berger wrote:
> > On 12/10/21 08:02, Mimi Zohar wrote:
> > > On Fri, 2021-12-10 at 07:40 -0500, Stefan Berger wrote:
> > > > On 12/10/21 07:09, Mimi Zohar wrote:
> > > > > On Fri, 2021-12-10 at 12:49 +0100, Christian Brauner wrote:
> > > > > > > There's still the problem that if you write the policy,
> > > > > > > making the file disappear then unmount and remount
> > > > > > > securityfs it will come back.  My guess for fixing this is
> > > > > > > that we only stash the policy file reference,
> > > > > > > create it if NULL but then set the pointer to PTR_ERR(-
> > > > > > > EINVAL) or something and refuse to create it for that
> > > > > > > value.
> > > > > > Some sort of indicator that gets stashed in struct ima_ns
> > > > > > that the file does not get recreated on consecutive mounts.
> > > > > > That shouldn't be hard to fix.
> > > > > The policy file disappearing is for backwards compatibility,
> > > > > prior to being able to extend the custom policy.  For embedded
> > > > > usecases, allowing the policy to be written exactly once might
> > > > > makes sense.  Do we really want/need to continue to support
> > > > > removing the policy in namespaces?
> > > > I don't have an answer but should the behavior for the same
> > > > #define in this case be different for host and namespaces? Or
> > > > should we just 'select IMA_WRITE_POLICY and IMA_READ_POLICY' when
> > > > IMA_NS is selected?
> > > The latter option sounds good.  Being able to analyze the namespace
> > > policy is really important.
> > 
> > Ok, I will adjust the Kconfig for this then. This then warrants the 
> > question whether to move the dentry into the ima_namespace. The
> > current code looks like this.
> > 
> > #if !defined(CONFIG_IMA_WRITE_POLICY) &&
> > !defined(CONFIG_IMA_READ_POLICY)
> >          securityfs_remove(ns->policy_dentry);
> >          ns->policy_dentry = NULL;
> >          ns->policy_dentry_removed = true;
> > #elif defined(CONFIG_IMA_WRITE_POLICY)
> > 
> > With IMA_NS selecting IMA_WRITE_POLICY and IMA_READ_POLICY the above 
> > wouldn't be necessary anymore but I find it 'cleaner' to still have
> > the dentry isolated rather than it being a global static as it was
> > before...
> 
> This is really, really why you don't want the semantics inside the
> namespace to differ from those outside, because it creates confusion
> for the people reading the code, especially with magically forced
> config options like this.  I'd strongly suggest you either keep the
> semantic in the namespace or eliminate it entirely.
> 
> If you really, really have to make the namespace behave differently,
> then use global variables and put a big comment on that code saying it
> can never be reached once CONFIG_IMA_NS is enabled.

The problem seems to be with removing the securityfs policy file. 
Instead of removing it, just make it inacessible for the "if
!defined(CONFIG_IMA_WRITE_POLICY) && !defined(CONFIG_IMA_READ_POLICY)"
case.

thanks,

Mimi


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

* Re: [PATCH v5 15/16] ima: Move dentries into ima_namespace
  2021-12-10 15:26                       ` Mimi Zohar
@ 2021-12-10 15:32                         ` Stefan Berger
  2021-12-10 15:48                           ` Mimi Zohar
  0 siblings, 1 reply; 63+ messages in thread
From: Stefan Berger @ 2021-12-10 15:32 UTC (permalink / raw)
  To: Mimi Zohar, jejb, Christian Brauner
  Cc: linux-integrity, serge, containers, dmitry.kasatkin, ebiederm,
	krzysztof.struczynski, roberto.sassu, mpeters, lhinds, lsturman,
	puiterwi, jamjoom, linux-kernel, paul, rgb,
	linux-security-module, jmorris


On 12/10/21 10:26, Mimi Zohar wrote:
> On Fri, 2021-12-10 at 09:26 -0500, James Bottomley wrote:
>> On Fri, 2021-12-10 at 09:17 -0500, Stefan Berger wrote:
>>> On 12/10/21 08:02, Mimi Zohar wrote:
>>>> On Fri, 2021-12-10 at 07:40 -0500, Stefan Berger wrote:
>>>>> On 12/10/21 07:09, Mimi Zohar wrote:
>>>>>> On Fri, 2021-12-10 at 12:49 +0100, Christian Brauner wrote:
>>>>>>>> There's still the problem that if you write the policy,
>>>>>>>> making the file disappear then unmount and remount
>>>>>>>> securityfs it will come back.  My guess for fixing this is
>>>>>>>> that we only stash the policy file reference,
>>>>>>>> create it if NULL but then set the pointer to PTR_ERR(-
>>>>>>>> EINVAL) or something and refuse to create it for that
>>>>>>>> value.
>>>>>>> Some sort of indicator that gets stashed in struct ima_ns
>>>>>>> that the file does not get recreated on consecutive mounts.
>>>>>>> That shouldn't be hard to fix.
>>>>>> The policy file disappearing is for backwards compatibility,
>>>>>> prior to being able to extend the custom policy.  For embedded
>>>>>> usecases, allowing the policy to be written exactly once might
>>>>>> makes sense.  Do we really want/need to continue to support
>>>>>> removing the policy in namespaces?
>>>>> I don't have an answer but should the behavior for the same
>>>>> #define in this case be different for host and namespaces? Or
>>>>> should we just 'select IMA_WRITE_POLICY and IMA_READ_POLICY' when
>>>>> IMA_NS is selected?
>>>> The latter option sounds good.  Being able to analyze the namespace
>>>> policy is really important.
>>> Ok, I will adjust the Kconfig for this then. This then warrants the
>>> question whether to move the dentry into the ima_namespace. The
>>> current code looks like this.
>>>
>>> #if !defined(CONFIG_IMA_WRITE_POLICY) &&
>>> !defined(CONFIG_IMA_READ_POLICY)
>>>           securityfs_remove(ns->policy_dentry);
>>>           ns->policy_dentry = NULL;
>>>           ns->policy_dentry_removed = true;
>>> #elif defined(CONFIG_IMA_WRITE_POLICY)
>>>
>>> With IMA_NS selecting IMA_WRITE_POLICY and IMA_READ_POLICY the above
>>> wouldn't be necessary anymore but I find it 'cleaner' to still have
>>> the dentry isolated rather than it being a global static as it was
>>> before...
>> This is really, really why you don't want the semantics inside the
>> namespace to differ from those outside, because it creates confusion
>> for the people reading the code, especially with magically forced
>> config options like this.  I'd strongly suggest you either keep the
>> semantic in the namespace or eliminate it entirely.
>>
>> If you really, really have to make the namespace behave differently,
>> then use global variables and put a big comment on that code saying it
>> can never be reached once CONFIG_IMA_NS is enabled.
> The problem seems to be with removing the securityfs policy file.
> Instead of removing it, just make it inacessible for the "if
> !defined(CONFIG_IMA_WRITE_POLICY) && !defined(CONFIG_IMA_READ_POLICY)"
> case.

So we would then leave it up to the one building the kernel to select 
the proper compile time options (suggested ones being IMA_WRITE_POLICY 
and IMA_READ_POLICY being enabled?) and behavior of host and IMA 
namespace is then the same per those options? Removing the file didn't 
seem the problem to me but more like whether the host should ever behave 
differently from the namespace.

    Stefan

>
> thanks,
>
> Mimi
>

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

* Re: [PATCH v5 15/16] ima: Move dentries into ima_namespace
  2021-12-10 15:32                         ` Stefan Berger
@ 2021-12-10 15:48                           ` Mimi Zohar
  2021-12-10 16:40                             ` Stefan Berger
  0 siblings, 1 reply; 63+ messages in thread
From: Mimi Zohar @ 2021-12-10 15:48 UTC (permalink / raw)
  To: Stefan Berger, jejb, Christian Brauner
  Cc: linux-integrity, serge, containers, dmitry.kasatkin, ebiederm,
	krzysztof.struczynski, roberto.sassu, mpeters, lhinds, lsturman,
	puiterwi, jamjoom, linux-kernel, paul, rgb,
	linux-security-module, jmorris

On Fri, 2021-12-10 at 10:32 -0500, Stefan Berger wrote:
> On 12/10/21 10:26, Mimi Zohar wrote:
> > On Fri, 2021-12-10 at 09:26 -0500, James Bottomley wrote:
> >> On Fri, 2021-12-10 at 09:17 -0500, Stefan Berger wrote:
> >>> On 12/10/21 08:02, Mimi Zohar wrote:
> >>>> On Fri, 2021-12-10 at 07:40 -0500, Stefan Berger wrote:
> >>>>> On 12/10/21 07:09, Mimi Zohar wrote:
> >>>>>> On Fri, 2021-12-10 at 12:49 +0100, Christian Brauner wrote:
> >>>>>>>> There's still the problem that if you write the policy,
> >>>>>>>> making the file disappear then unmount and remount
> >>>>>>>> securityfs it will come back.  My guess for fixing this is
> >>>>>>>> that we only stash the policy file reference,
> >>>>>>>> create it if NULL but then set the pointer to PTR_ERR(-
> >>>>>>>> EINVAL) or something and refuse to create it for that
> >>>>>>>> value.
> >>>>>>> Some sort of indicator that gets stashed in struct ima_ns
> >>>>>>> that the file does not get recreated on consecutive mounts.
> >>>>>>> That shouldn't be hard to fix.
> >>>>>> The policy file disappearing is for backwards compatibility,
> >>>>>> prior to being able to extend the custom policy.  For embedded
> >>>>>> usecases, allowing the policy to be written exactly once might
> >>>>>> makes sense.  Do we really want/need to continue to support
> >>>>>> removing the policy in namespaces?
> >>>>> I don't have an answer but should the behavior for the same
> >>>>> #define in this case be different for host and namespaces? Or
> >>>>> should we just 'select IMA_WRITE_POLICY and IMA_READ_POLICY' when
> >>>>> IMA_NS is selected?
> >>>> The latter option sounds good.  Being able to analyze the namespace
> >>>> policy is really important.
> >>> Ok, I will adjust the Kconfig for this then. This then warrants the
> >>> question whether to move the dentry into the ima_namespace. The
> >>> current code looks like this.
> >>>
> >>> #if !defined(CONFIG_IMA_WRITE_POLICY) &&
> >>> !defined(CONFIG_IMA_READ_POLICY)
> >>>           securityfs_remove(ns->policy_dentry);
> >>>           ns->policy_dentry = NULL;
> >>>           ns->policy_dentry_removed = true;
> >>> #elif defined(CONFIG_IMA_WRITE_POLICY)
> >>>
> >>> With IMA_NS selecting IMA_WRITE_POLICY and IMA_READ_POLICY the above
> >>> wouldn't be necessary anymore but I find it 'cleaner' to still have
> >>> the dentry isolated rather than it being a global static as it was
> >>> before...
> >> This is really, really why you don't want the semantics inside the
> >> namespace to differ from those outside, because it creates confusion
> >> for the people reading the code, especially with magically forced
> >> config options like this.  I'd strongly suggest you either keep the
> >> semantic in the namespace or eliminate it entirely.
> >>
> >> If you really, really have to make the namespace behave differently,
> >> then use global variables and put a big comment on that code saying it
> >> can never be reached once CONFIG_IMA_NS is enabled.
> > The problem seems to be with removing the securityfs policy file.
> > Instead of removing it, just make it inacessible for the "if
> > !defined(CONFIG_IMA_WRITE_POLICY) && !defined(CONFIG_IMA_READ_POLICY)"
> > case.
> 
> So we would then leave it up to the one building the kernel to select 
> the proper compile time options (suggested ones being IMA_WRITE_POLICY 
> and IMA_READ_POLICY being enabled?) and behavior of host and IMA 
> namespace is then the same per those options? Removing the file didn't 
> seem the problem to me but more like whether the host should ever behave 
> differently from the namespace.

You proposed "select IMA_WRITE_POLICY and IMA_READ_POLICY'" when IMA_NS
is selected.  At least IMA_READ_POLICY should be enabled for
namespaces.

In addition, if removing the securityfs file after a custom policy is
loaded complicates namespacing, then don't remove it.

thanks,

Mimi


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

* Re: [PATCH v5 15/16] ima: Move dentries into ima_namespace
  2021-12-10 15:48                           ` Mimi Zohar
@ 2021-12-10 16:40                             ` Stefan Berger
  0 siblings, 0 replies; 63+ messages in thread
From: Stefan Berger @ 2021-12-10 16:40 UTC (permalink / raw)
  To: Mimi Zohar, jejb, Christian Brauner
  Cc: linux-integrity, serge, containers, dmitry.kasatkin, ebiederm,
	krzysztof.struczynski, roberto.sassu, mpeters, lhinds, lsturman,
	puiterwi, jamjoom, linux-kernel, paul, rgb,
	linux-security-module, jmorris


On 12/10/21 10:48, Mimi Zohar wrote:
> On Fri, 2021-12-10 at 10:32 -0500, Stefan Berger wrote:
>> On 12/10/21 10:26, Mimi Zohar wrote:
>>> On Fri, 2021-12-10 at 09:26 -0500, James Bottomley wrote:
>>>> On Fri, 2021-12-10 at 09:17 -0500, Stefan Berger wrote:
>>>>> On 12/10/21 08:02, Mimi Zohar wrote:
>>>>>> On Fri, 2021-12-10 at 07:40 -0500, Stefan Berger wrote:
>>>>>>> On 12/10/21 07:09, Mimi Zohar wrote:
>>>>>>>> On Fri, 2021-12-10 at 12:49 +0100, Christian Brauner wrote:
>>>>>>>>>> There's still the problem that if you write the policy,
>>>>>>>>>> making the file disappear then unmount and remount
>>>>>>>>>> securityfs it will come back.  My guess for fixing this is
>>>>>>>>>> that we only stash the policy file reference,
>>>>>>>>>> create it if NULL but then set the pointer to PTR_ERR(-
>>>>>>>>>> EINVAL) or something and refuse to create it for that
>>>>>>>>>> value.
>>>>>>>>> Some sort of indicator that gets stashed in struct ima_ns
>>>>>>>>> that the file does not get recreated on consecutive mounts.
>>>>>>>>> That shouldn't be hard to fix.
>>>>>>>> The policy file disappearing is for backwards compatibility,
>>>>>>>> prior to being able to extend the custom policy.  For embedded
>>>>>>>> usecases, allowing the policy to be written exactly once might
>>>>>>>> makes sense.  Do we really want/need to continue to support
>>>>>>>> removing the policy in namespaces?
>>>>>>> I don't have an answer but should the behavior for the same
>>>>>>> #define in this case be different for host and namespaces? Or
>>>>>>> should we just 'select IMA_WRITE_POLICY and IMA_READ_POLICY' when
>>>>>>> IMA_NS is selected?
>>>>>> The latter option sounds good.  Being able to analyze the namespace
>>>>>> policy is really important.
>>>>> Ok, I will adjust the Kconfig for this then. This then warrants the
>>>>> question whether to move the dentry into the ima_namespace. The
>>>>> current code looks like this.
>>>>>
>>>>> #if !defined(CONFIG_IMA_WRITE_POLICY) &&
>>>>> !defined(CONFIG_IMA_READ_POLICY)
>>>>>            securityfs_remove(ns->policy_dentry);
>>>>>            ns->policy_dentry = NULL;
>>>>>            ns->policy_dentry_removed = true;
>>>>> #elif defined(CONFIG_IMA_WRITE_POLICY)
>>>>>
>>>>> With IMA_NS selecting IMA_WRITE_POLICY and IMA_READ_POLICY the above
>>>>> wouldn't be necessary anymore but I find it 'cleaner' to still have
>>>>> the dentry isolated rather than it being a global static as it was
>>>>> before...
>>>> This is really, really why you don't want the semantics inside the
>>>> namespace to differ from those outside, because it creates confusion
>>>> for the people reading the code, especially with magically forced
>>>> config options like this.  I'd strongly suggest you either keep the
>>>> semantic in the namespace or eliminate it entirely.
>>>>
>>>> If you really, really have to make the namespace behave differently,
>>>> then use global variables and put a big comment on that code saying it
>>>> can never be reached once CONFIG_IMA_NS is enabled.
>>> The problem seems to be with removing the securityfs policy file.
>>> Instead of removing it, just make it inacessible for the "if
>>> !defined(CONFIG_IMA_WRITE_POLICY) && !defined(CONFIG_IMA_READ_POLICY)"
>>> case.
>> So we would then leave it up to the one building the kernel to select
>> the proper compile time options (suggested ones being IMA_WRITE_POLICY
>> and IMA_READ_POLICY being enabled?) and behavior of host and IMA
>> namespace is then the same per those options? Removing the file didn't
>> seem the problem to me but more like whether the host should ever behave
>> differently from the namespace.
> You proposed "select IMA_WRITE_POLICY and IMA_READ_POLICY'" when IMA_NS
> is selected.  At least IMA_READ_POLICY should be enabled for
> namespaces.
>
> In addition, if removing the securityfs file after a custom policy is
> loaded complicates namespacing, then don't remove it.

If we just leave the selection of the compile time options to the user 
(suggested ones being IMA_WRITE_POLICY

and IMA_READ_POLICY being enabled) we don't need to make any changes.

Choices are for IMA_NS:
1) Leave compile-time options to the user and suggest IMA_WRITE_POLICY and IMA_READ_POLICY
2) Auto-select IMA_WRITE_POLICY and IMA_READ_POLICY for IMA_NS and still remove file for non-IMA_NS case and have the dentry in the ima_namespace
3) Auto-select IMA_WRITE_POLICY and IMA_READ_POLICY for IMA_NS and still remove file for non-IMA_NS case and use global dentry
4) Changing mode bits on the file/inode to avoid having the dentry

for v6 I just leave things as they are and we can then see what we need.



>
> thanks,
>
> Mimi
>

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

* Re: [PATCH v5 13/16] ima: Move some IMA policy and filesystem related variables into ima_namespace
  2021-12-10 11:32       ` Christian Brauner
  2021-12-10 13:57         ` Stefan Berger
@ 2021-12-10 20:08         ` Stefan Berger
  2021-12-11  8:46           ` Christian Brauner
  1 sibling, 1 reply; 63+ messages in thread
From: Stefan Berger @ 2021-12-10 20:08 UTC (permalink / raw)
  To: Christian Brauner
  Cc: linux-integrity, zohar, serge, containers, dmitry.kasatkin,
	ebiederm, krzysztof.struczynski, roberto.sassu, mpeters, lhinds,
	lsturman, puiterwi, jejb, jamjoom, linux-kernel, paul, rgb,
	linux-security-module, jmorris


On 12/10/21 06:32, Christian Brauner wrote:
>  From ecf25d6b2b5895005d4103169bdb55d970e7a865 Mon Sep 17 00:00:00 2001
> From: Christian Brauner<christian.brauner@ubuntu.com>
> Date: Fri, 10 Dec 2021 11:56:25 +0100
> Subject: [PATCH 2/2] !!!! HERE BE DRAGONS - COMPLETELY UNTESTED !!!!
>
> securityfs: don't allow mounting from outside the filesystem's userns
>
> If we ever need to allow that we should revisit the semantics.
> ---
>   security/inode.c | 5 ++++-
>   1 file changed, 4 insertions(+), 1 deletion(-)
>
> diff --git a/security/inode.c b/security/inode.c
> index eaccba7017d9..71f9634228f3 100644
> --- a/security/inode.c
> +++ b/security/inode.c
> @@ -43,7 +43,10 @@ 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;
> +	int error = -EINVAL;
> +
> +	if (WARN_ON(ns != current_user_ns()))
> +		return error;
>   
>   	error = simple_fill_super(sb, SECURITYFS_MAGIC, files);
>   	if (error)


Oops, I hadn't seen this patch. How can one 'mount from outside the 
filesystem's userns'?



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

* Re: [PATCH v5 13/16] ima: Move some IMA policy and filesystem related variables into ima_namespace
  2021-12-10 20:08         ` Stefan Berger
@ 2021-12-11  8:46           ` Christian Brauner
  0 siblings, 0 replies; 63+ messages in thread
From: Christian Brauner @ 2021-12-11  8:46 UTC (permalink / raw)
  To: Stefan Berger
  Cc: linux-integrity, zohar, serge, containers, dmitry.kasatkin,
	ebiederm, krzysztof.struczynski, roberto.sassu, mpeters, lhinds,
	lsturman, puiterwi, jejb, jamjoom, linux-kernel, paul, rgb,
	linux-security-module, jmorris

On Fri, Dec 10, 2021 at 03:08:27PM -0500, Stefan Berger wrote:
> 
> On 12/10/21 06:32, Christian Brauner wrote:
> >  From ecf25d6b2b5895005d4103169bdb55d970e7a865 Mon Sep 17 00:00:00 2001
> > From: Christian Brauner<christian.brauner@ubuntu.com>
> > Date: Fri, 10 Dec 2021 11:56:25 +0100
> > Subject: [PATCH 2/2] !!!! HERE BE DRAGONS - COMPLETELY UNTESTED !!!!
> > 
> > securityfs: don't allow mounting from outside the filesystem's userns
> > 
> > If we ever need to allow that we should revisit the semantics.
> > ---
> >   security/inode.c | 5 ++++-
> >   1 file changed, 4 insertions(+), 1 deletion(-)
> > 
> > diff --git a/security/inode.c b/security/inode.c
> > index eaccba7017d9..71f9634228f3 100644
> > --- a/security/inode.c
> > +++ b/security/inode.c
> > @@ -43,7 +43,10 @@ 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;
> > +	int error = -EINVAL;
> > +
> > +	if (WARN_ON(ns != current_user_ns()))
> > +		return error;
> >   	error = simple_fill_super(sb, SECURITYFS_MAGIC, files);
> >   	if (error)
> 
> 
> Oops, I hadn't seen this patch. How can one 'mount from outside the
> filesystem's userns'?

The new mount api is generic enough that this could be possible and it
might gain that ability explicitly at some point in the future for the
sake of delegated mounting. So that's just a good hardening measure.

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

* Re: [PATCH v5 13/16] ima: Move some IMA policy and filesystem related variables into ima_namespace
  2021-12-10 13:57         ` Stefan Berger
  2021-12-10 14:21           ` James Bottomley
@ 2021-12-11  9:50           ` Christian Brauner
  2021-12-11 10:45             ` Christian Brauner
  2021-12-13 15:33             ` Stefan Berger
  1 sibling, 2 replies; 63+ messages in thread
From: Christian Brauner @ 2021-12-11  9:50 UTC (permalink / raw)
  To: Stefan Berger
  Cc: linux-integrity, zohar, serge, containers, dmitry.kasatkin,
	ebiederm, krzysztof.struczynski, roberto.sassu, mpeters, lhinds,
	lsturman, puiterwi, jejb, jamjoom, linux-kernel, paul, rgb,
	linux-security-module, jmorris

On Fri, Dec 10, 2021 at 08:57:11AM -0500, Stefan Berger wrote:
> 
> On 12/10/21 06:32, Christian Brauner wrote:
> > On Thu, Dec 09, 2021 at 07:57:02PM -0500, Stefan Berger wrote:
> > > On 12/9/21 14:11, Christian Brauner wrote:
> > > >   From 1f03dc427c583d5e9ebc9ebe9de77c3c535bbebe Mon Sep 17 00:00:00 2001
> > > > From: Christian Brauner <christian.brauner@ubuntu.com>
> > > > Date: Thu, 9 Dec 2021 20:07:02 +0100
> > > > Subject: [PATCH] !!!! HERE BE DRAGONS - UNTESTED !!!!
> > > > 
> > > > ---
> > > >    security/integrity/ima/ima_fs.c | 43 +++++++++++++++++++++++++++++----
> > > >    1 file changed, 38 insertions(+), 5 deletions(-)
> > > > 
> > > > diff --git a/security/integrity/ima/ima_fs.c b/security/integrity/ima/ima_fs.c
> > > > index 583462b29cb5..d5b302b925b8 100644
> > > > --- a/security/integrity/ima/ima_fs.c
> > > > +++ b/security/integrity/ima/ima_fs.c
> > > > @@ -317,10 +317,14 @@ static ssize_t ima_read_policy(char *path)
> > > >    static ssize_t ima_write_policy(struct file *file, const char __user *buf,
> > > >    				size_t datalen, loff_t *ppos)
> > > >    {
> > > > -	struct ima_namespace *ns = get_current_ns();
> > > > +	struct ima_namespace *ns;
> > > > +	struct user_namespace *user_ns;
> > > >    	char *data;
> > > >    	ssize_t result;
> > > > +	user_ns = ima_filp_private(filp);
> > > > +	ns = user_ns->ima_ns
> > > > +
> > > >    	if (datalen >= PAGE_SIZE)
> > > >    		datalen = PAGE_SIZE - 1;
> > > > @@ -373,26 +377,51 @@ static const struct seq_operations ima_policy_seqops = {
> > > >    };
> > > >    #endif
> > > > +static struct user_namespace *ima_filp_private(struct file *filp)
> > > > +{
> > > > +	if (!(filp->f_flags & O_WRONLY)) {
> > > > +#ifdef CONFIG_IMA_READ_POLICY
> > > > +		struct seq_file *seq;
> > > > +
> > > > +		seq = filp->private_data;
> > > > +		return seq->private;
> > > > +#endif
> > > > +	}
> > > > +	return filp->private_data;
> > > > +}
> > > > +
> > > >    /*
> > > >     * ima_open_policy: sequentialize access to the policy file
> > > >     */
> > > >    static int ima_open_policy(struct inode *inode, struct file *filp)
> > > >    {
> > > > -	struct ima_namespace *ns = get_current_ns();
> > > > +	struct user_namespace *user_ns = current_user_ns();
> > > 
> > > Do we have to take a reference on the user namespace assuming one can open
> > > the file, pass the fd down the hierarchy, and then the user namespace with
> > > the opened file goes away? Or is there anything else that keeps the user
> > > namespace alive?
> > No, we don't. When ima_policy_open() is called we do current_user_ns()
> > but that will be guaranteed to be identical to filp->f_cred->user_ns.
> > And f_cred is a reference that has been taken when the vfs allocated a
> > struct file for this .open call so won't go away until the last fput.
> > 
> > My proposal is also too complicated, I think.
> > (The booster is giving me the same side-effects as my second shot so
> > this looks like two good days of fever and headache. So I'll use that as
> > an excuse. :))
> > 
> > Your patch series as it stands has a bit of a security issue with those
> > get_current_ns() calls across differnet file/seq_file operations.
> > 
> > You have to make an architectural decision, I think. I see two sensible
> > options:
> > 1. The relevant ima_ns that .open/.read/.write operate on is always taken
> >     to be the ima_ns of the filesystem's userns, i.e.
> >     sb->s_user_ns->ima_ns.
> >     This - but I'm not an ima person - makes the most sense to me and the
> >     semantics are straightforward. If I write to a file to alter some
> >     policy then I expect the ima namespace of the user namespace to be
> >     affected that the securityfs instance was mounted in.
> > 2. The relevant ima_ns that .open/.read/.write operate on is always
> >     taken to be the one of the opener. I don't really like that as that
> >     gets weird if for some complicated reason the caller is not located
> >     in the userns the filesystem was mounted in (weird mount propagation
> >     scenario or sm). It also feels strange to operate on an ima_ns that's
> >     different from s_user_ns->ima_ns in a securityfs instance.
> 
> We have this situation because one can setns() to another mount namespaces
> but the data shown by SecurityFS lives in a user namespace, right? And now
> we need to decide whether to affect the data in the user namespace  that did
> the open (option 2) or to which the SecurityFS belongs to (option 1). If we
> were to open a regular file it would be option 1, so we should probably not
> break that existing semantic and also choose option 1 unless there one
> wasn't allowed to choose the user namespace the SecurityFS files belonged to
> then it should be option 2 but then we have file descriptor passing where
> 'being allowed' can change depending on who is reading/writing a file... Is

A general remark that's always worth repeating: under no circumstances
should the object that you're operating on change from .open to
.read/.write. Consider the following very rough sketch:

	pid = fork():
	if (pid == 0) {
		// create USERNS1
		unshare(CLONE_NEWUSER);
	
		// write an idmapping for getuid() to userns 0
	
		mount("", "/sys/kernel/security", "securityfs", [...]);
	
		int fd_ima = open("/sys/kernel/security/some_ima_file", O_WRONLY);
	
		// Send fd_ima to parent via SCM_RIGHTS
	}
	// Receive fd_ima from child.
	
	write(fd_ima, "SET_OPTION_ON_INIT_USER_NS", ...); 

That example above is an instant security issue if the ima_ns changed
depending on the caller since it means you could open a file in an
unprivileged userns and then funnel it to a random process in
init_user_ns that you control via SCM_RIGHTS and have it do the write()
and instead of USERNS1->ima_ns you'd suddenly have changed
init_user_ns->ima_ns.
And this is what your previous patch with these multiple
get_current_ns() calls allowed.

So the write in the example above must operate on USERNS1->ima_ns.

Now, both option 1 and 2 will ensure that the ima_ns that is operated on
is always the same across .open/.read/.write:
1. securityfs->sb->s_user_ns->ima_ns this is trivially guaranteed
   because the filesystem's user namespace doesn't change no matter
   where you access that filesystem from.
2. securityfs_file->f_cred->user_ns->ima_ns this is guaranteed because
   f_cred stashes the openers credentials.

What I fundamentally dislike about option 2 (f_cred) is - and I tried to
say that in my prior mail - that what ima_ns is operated on depends on
the userns of the process that called .open, i.e. the contents in
securityfs change depending on the opener's userns.

And I think that is fundamentally sloppy semantics. The contents of a
filesystem should generally not depend on the caller if it can be
avoided. We have a few instances where this is the case (e.g. some
procfs sysctls) but especially for securityfs this does not make sense.

> there anything that would prevent us from setns()'ing to that target user
> namespace so that we would now see that of a user namespace that we are not
> allowed to see?

If you're really worried about someone being able to access a securityfs
instance whose userns doesn't match the userns the securityfs instance
was mounted in there are multiple ways to fix it. The one that I tend to
prefer is:

From e0ff6a8dcc573763568e685dd70d1547efd68df9 Mon Sep 17 00:00:00 2001
From: Christian Brauner <christian.brauner@ubuntu.com>
Date: Fri, 10 Dec 2021 11:47:37 +0100
Subject: !!!! HERE BE DRAGONS - COMPLETELY UNTESTED !!!!

securityfs: only allow access to securityfs from within same namespace

Limit opening of securityfs files to callers located in the same namespace.

---
 security/inode.c | 33 +++++++++++++++++++++++++++++++--
 1 file changed, 31 insertions(+), 2 deletions(-)

diff --git a/security/inode.c b/security/inode.c
index eaccba7017d9..9eaf757c08cb 100644
--- a/security/inode.c
+++ b/security/inode.c
@@ -80,6 +80,35 @@ static struct file_system_type fs_type = {
 	.fs_flags =	FS_USERNS_MOUNT,
 };
 
+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) {
+		if (inode->i_sb->s_user_ns != current_user_ns())
+			err = -EACCES;
+	}
+
+	return err;
+}
+
+const struct inode_operations securityfs_dir_inode_operations = {
+	.permission	= securityfs_permission,
+	.lookup		= simple_lookup,
+};
+
+const struct file_operations securityfs_dir_operations = {
+	.permission	= securityfs_permission,
+	.open		= dcache_dir_open,
+	.release	= dcache_dir_close,
+	.llseek		= dcache_dir_lseek,
+	.read		= generic_read_dir,
+	.iterate_shared	= dcache_readdir,
+	.fsync		= noop_fsync,
+};
+
 /**
  * securityfs_create_dentry - create a dentry in the securityfs filesystem
  *
@@ -167,8 +196,8 @@ 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_fop = &simple_dir_operations;
+		inode->i_op = &securityfs_dir_inode_operations;
+		inode->i_fop = &securityfs_dir_operations;
 		inc_nlink(inode);
 		inc_nlink(dir);
 	} else if (S_ISLNK(mode)) {
-- 
2.30.2

> 
> Following man page of setns:
> 
> "   User namespaces
>               A process reassociating itself with a user namespace must
>               have the CAP_SYS_ADMIN capability in the target user
>               namespace.  (This necessarily implies that it is only
>               possible to join a descendant user namespace.)  Upon
>               successfully joining a user namespace, a process is
>               granted all capabilities in that namespace, regardless of
>               its user and group IDs."
> 
> 
> So if we choose option 1 maybe we have to test for this capability upon
> every read/write from/to a file?
> 

In general, never do permission checking at read/write time unless the
read/write fundamentally depends on what is read or written. Clean
semantics will do permission checking once at open time. If you really
really need to do permission checking at .read/.write time you need to
use f_cred possibly calling override_creds/revert_creds() while doing so
but simply don't do it.

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

* Re: [PATCH v5 13/16] ima: Move some IMA policy and filesystem related variables into ima_namespace
  2021-12-11  9:50           ` Christian Brauner
@ 2021-12-11 10:45             ` Christian Brauner
  2021-12-13 15:33             ` Stefan Berger
  1 sibling, 0 replies; 63+ messages in thread
From: Christian Brauner @ 2021-12-11 10:45 UTC (permalink / raw)
  To: Stefan Berger
  Cc: linux-integrity, zohar, serge, containers, dmitry.kasatkin,
	ebiederm, krzysztof.struczynski, roberto.sassu, mpeters, lhinds,
	lsturman, puiterwi, jejb, jamjoom, linux-kernel, paul, rgb,
	linux-security-module, jmorris

On Sat, Dec 11, 2021 at 10:50:26AM +0100, Christian Brauner wrote:
> On Fri, Dec 10, 2021 at 08:57:11AM -0500, Stefan Berger wrote:
> > 
> > On 12/10/21 06:32, Christian Brauner wrote:
> > > On Thu, Dec 09, 2021 at 07:57:02PM -0500, Stefan Berger wrote:
> > > > On 12/9/21 14:11, Christian Brauner wrote:
> > > > >   From 1f03dc427c583d5e9ebc9ebe9de77c3c535bbebe Mon Sep 17 00:00:00 2001
> > > > > From: Christian Brauner <christian.brauner@ubuntu.com>
> > > > > Date: Thu, 9 Dec 2021 20:07:02 +0100
> > > > > Subject: [PATCH] !!!! HERE BE DRAGONS - UNTESTED !!!!
> > > > > 
> > > > > ---
> > > > >    security/integrity/ima/ima_fs.c | 43 +++++++++++++++++++++++++++++----
> > > > >    1 file changed, 38 insertions(+), 5 deletions(-)
> > > > > 
> > > > > diff --git a/security/integrity/ima/ima_fs.c b/security/integrity/ima/ima_fs.c
> > > > > index 583462b29cb5..d5b302b925b8 100644
> > > > > --- a/security/integrity/ima/ima_fs.c
> > > > > +++ b/security/integrity/ima/ima_fs.c
> > > > > @@ -317,10 +317,14 @@ static ssize_t ima_read_policy(char *path)
> > > > >    static ssize_t ima_write_policy(struct file *file, const char __user *buf,
> > > > >    				size_t datalen, loff_t *ppos)
> > > > >    {
> > > > > -	struct ima_namespace *ns = get_current_ns();
> > > > > +	struct ima_namespace *ns;
> > > > > +	struct user_namespace *user_ns;
> > > > >    	char *data;
> > > > >    	ssize_t result;
> > > > > +	user_ns = ima_filp_private(filp);
> > > > > +	ns = user_ns->ima_ns
> > > > > +
> > > > >    	if (datalen >= PAGE_SIZE)
> > > > >    		datalen = PAGE_SIZE - 1;
> > > > > @@ -373,26 +377,51 @@ static const struct seq_operations ima_policy_seqops = {
> > > > >    };
> > > > >    #endif
> > > > > +static struct user_namespace *ima_filp_private(struct file *filp)
> > > > > +{
> > > > > +	if (!(filp->f_flags & O_WRONLY)) {
> > > > > +#ifdef CONFIG_IMA_READ_POLICY
> > > > > +		struct seq_file *seq;
> > > > > +
> > > > > +		seq = filp->private_data;
> > > > > +		return seq->private;
> > > > > +#endif
> > > > > +	}
> > > > > +	return filp->private_data;
> > > > > +}
> > > > > +
> > > > >    /*
> > > > >     * ima_open_policy: sequentialize access to the policy file
> > > > >     */
> > > > >    static int ima_open_policy(struct inode *inode, struct file *filp)
> > > > >    {
> > > > > -	struct ima_namespace *ns = get_current_ns();
> > > > > +	struct user_namespace *user_ns = current_user_ns();
> > > > 
> > > > Do we have to take a reference on the user namespace assuming one can open
> > > > the file, pass the fd down the hierarchy, and then the user namespace with
> > > > the opened file goes away? Or is there anything else that keeps the user
> > > > namespace alive?
> > > No, we don't. When ima_policy_open() is called we do current_user_ns()
> > > but that will be guaranteed to be identical to filp->f_cred->user_ns.
> > > And f_cred is a reference that has been taken when the vfs allocated a
> > > struct file for this .open call so won't go away until the last fput.
> > > 
> > > My proposal is also too complicated, I think.
> > > (The booster is giving me the same side-effects as my second shot so
> > > this looks like two good days of fever and headache. So I'll use that as
> > > an excuse. :))
> > > 
> > > Your patch series as it stands has a bit of a security issue with those
> > > get_current_ns() calls across differnet file/seq_file operations.
> > > 
> > > You have to make an architectural decision, I think. I see two sensible
> > > options:
> > > 1. The relevant ima_ns that .open/.read/.write operate on is always taken
> > >     to be the ima_ns of the filesystem's userns, i.e.
> > >     sb->s_user_ns->ima_ns.
> > >     This - but I'm not an ima person - makes the most sense to me and the
> > >     semantics are straightforward. If I write to a file to alter some
> > >     policy then I expect the ima namespace of the user namespace to be
> > >     affected that the securityfs instance was mounted in.
> > > 2. The relevant ima_ns that .open/.read/.write operate on is always
> > >     taken to be the one of the opener. I don't really like that as that
> > >     gets weird if for some complicated reason the caller is not located
> > >     in the userns the filesystem was mounted in (weird mount propagation
> > >     scenario or sm). It also feels strange to operate on an ima_ns that's
> > >     different from s_user_ns->ima_ns in a securityfs instance.
> > 
> > We have this situation because one can setns() to another mount namespaces
> > but the data shown by SecurityFS lives in a user namespace, right? And now
> > we need to decide whether to affect the data in the user namespace  that did
> > the open (option 2) or to which the SecurityFS belongs to (option 1). If we
> > were to open a regular file it would be option 1, so we should probably not
> > break that existing semantic and also choose option 1 unless there one
> > wasn't allowed to choose the user namespace the SecurityFS files belonged to
> > then it should be option 2 but then we have file descriptor passing where
> > 'being allowed' can change depending on who is reading/writing a file... Is
> 
> A general remark that's always worth repeating: under no circumstances
> should the object that you're operating on change from .open to
> .read/.write. Consider the following very rough sketch:
> 
> 	pid = fork():
> 	if (pid == 0) {
> 		// create USERNS1
> 		unshare(CLONE_NEWUSER);
> 	
> 		// write an idmapping for getuid() to userns 0
> 	
> 		mount("", "/sys/kernel/security", "securityfs", [...]);
> 	
> 		int fd_ima = open("/sys/kernel/security/some_ima_file", O_WRONLY);
> 	
> 		// Send fd_ima to parent via SCM_RIGHTS
> 	}
> 	// Receive fd_ima from child.
> 	
> 	write(fd_ima, "SET_OPTION_ON_INIT_USER_NS", ...); 
> 
> That example above is an instant security issue if the ima_ns changed
> depending on the caller since it means you could open a file in an
> unprivileged userns and then funnel it to a random process in
> init_user_ns that you control via SCM_RIGHTS and have it do the write()
> and instead of USERNS1->ima_ns you'd suddenly have changed
> init_user_ns->ima_ns.
> And this is what your previous patch with these multiple
> get_current_ns() calls allowed.
> 
> So the write in the example above must operate on USERNS1->ima_ns.
> 
> Now, both option 1 and 2 will ensure that the ima_ns that is operated on
> is always the same across .open/.read/.write:
> 1. securityfs->sb->s_user_ns->ima_ns this is trivially guaranteed
>    because the filesystem's user namespace doesn't change no matter
>    where you access that filesystem from.
> 2. securityfs_file->f_cred->user_ns->ima_ns this is guaranteed because
>    f_cred stashes the openers credentials.
> 
> What I fundamentally dislike about option 2 (f_cred) is - and I tried to
> say that in my prior mail - that what ima_ns is operated on depends on
> the userns of the process that called .open, i.e. the contents in
> securityfs change depending on the opener's userns.
> 
> And I think that is fundamentally sloppy semantics. The contents of a
> filesystem should generally not depend on the caller if it can be
> avoided. We have a few instances where this is the case (e.g. some
> procfs sysctls) but especially for securityfs this does not make sense.
> 
> > there anything that would prevent us from setns()'ing to that target user
> > namespace so that we would now see that of a user namespace that we are not
> > allowed to see?
> 
> If you're really worried about someone being able to access a securityfs
> instance whose userns doesn't match the userns the securityfs instance
> was mounted in there are multiple ways to fix it. The one that I tend to
> prefer is:
> 
> From e0ff6a8dcc573763568e685dd70d1547efd68df9 Mon Sep 17 00:00:00 2001
> From: Christian Brauner <christian.brauner@ubuntu.com>
> Date: Fri, 10 Dec 2021 11:47:37 +0100
> Subject: !!!! HERE BE DRAGONS - COMPLETELY UNTESTED !!!!
> 
> securityfs: only allow access to securityfs from within same namespace
> 
> Limit opening of securityfs files to callers located in the same namespace.
> 
> ---
>  security/inode.c | 33 +++++++++++++++++++++++++++++++--
>  1 file changed, 31 insertions(+), 2 deletions(-)
> 
> diff --git a/security/inode.c b/security/inode.c
> index eaccba7017d9..9eaf757c08cb 100644
> --- a/security/inode.c
> +++ b/security/inode.c
> @@ -80,6 +80,35 @@ static struct file_system_type fs_type = {
>  	.fs_flags =	FS_USERNS_MOUNT,
>  };
>  
> +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) {
> +		if (inode->i_sb->s_user_ns != current_user_ns())
> +			err = -EACCES;
> +	}

Thinking about this a better alternative might be (on top of my
previous suggestion):

diff --git a/security/inode.c b/security/inode.c
index 0000b2bd4c0c..a540372e4c8c 100644
--- a/security/inode.c
+++ b/security/inode.c
@@ -86,15 +86,15 @@ static struct file_system_type fs_type = {
 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) {
-               if (inode->i_sb->s_user_ns != current_user_ns())
-                       err = -EACCES;
-       }
-
-       return err;
+       /*
+        * Restrict access to securityfs to callers whose user
+        * namespace is the same or an ancestor of the user namespace
+        * this securityfs instance was mounted in.
+        */
+       if (!in_userns(current_user_ns(), inode->i_sb->s_user_ns))
+               return -EACCES;
+
+       return generic_permission(&init_user_ns, inode, mask);
 }

 const struct inode_operations securityfs_dir_inode_operations = {

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

* Re: [PATCH v5 14/16] ima: Use mac_admin_ns_capable() to check corresponding capability
  2021-12-09  8:09   ` Denis Semakin
@ 2021-12-11 15:02     ` Serge E. Hallyn
  2021-12-11 15:38       ` Stefan Berger
  0 siblings, 1 reply; 63+ messages in thread
From: Serge E. Hallyn @ 2021-12-11 15:02 UTC (permalink / raw)
  To: Denis Semakin
  Cc: Stefan Berger, linux-integrity, zohar, serge, christian.brauner,
	containers, dmitry.kasatkin, ebiederm, Krzysztof Struczynski,
	Roberto Sassu, mpeters, lhinds, lsturman, puiterwi, jejb,
	jamjoom, linux-kernel, paul, rgb, linux-security-module, jmorris

IMO yes it is unsafe, however I concede that I am not sufficiently familiar
with the policy language.  At least Stefan and Mimi (IIUC) want the host
policy language to be able to specify cases where an IMA ns can be
configured.  What's not clear to me is what sorts of triggers the host
IMA policy could specify that would safely identify a IMA ns generation
trigger.

Stefan, would you mind showing what such a policy statement would look like?
Does it amount to "/usr/bin/runc may create an IMA ns which escapes current
policy" ?  Or is it by UID, or any file which has a certain xattr on it?

-serge

On Thu, Dec 09, 2021 at 08:09:20AM +0000, Denis Semakin wrote:
> Following that thoughts...
> Will it be so incorrectly to unbound IMA-ns from USER-ns?
> I realize that it could lead a lot of problems but it is still unclear will current IMA-ns will be useful for Kuber...
> How userland supposed to use current IMA-ns implementation?
> 
> Br,
> Denis
> 
> -----Original Message-----
> From: Denis Semakin 
> Sent: Thursday, December 9, 2021 10:22 AM
> To: 'Stefan Berger' <stefanb@linux.ibm.com>; linux-integrity@vger.kernel.org
> Cc: zohar@linux.ibm.com; serge@hallyn.com; christian.brauner@ubuntu.com; containers@lists.linux.dev; dmitry.kasatkin@gmail.com; ebiederm@xmission.com; Krzysztof Struczynski <krzysztof.struczynski@huawei.com>; Roberto Sassu <roberto.sassu@huawei.com>; mpeters@redhat.com; lhinds@redhat.com; lsturman@redhat.com; puiterwi@redhat.com; jejb@linux.ibm.com; jamjoom@us.ibm.com; linux-kernel@vger.kernel.org; paul@paul-moore.com; rgb@redhat.com; linux-security-module@vger.kernel.org; jmorris@namei.org
> Subject: RE: [PATCH v5 14/16] ima: Use mac_admin_ns_capable() to check corresponding capability
> 
> Hi. 
> My question won't be about capabilities. I'm wondering how IMA-ns which is associated with USER-ns and is created during USER-ns creation would be used by some namespaces orchestration systems, e.g. Kubernetes?.. It seems that it can be run without any user namespaces... 
> Their community just discuss this opportunity to support User namespaces. (see https://github.com/kubernetes/enhancements/pull/2101)
> Looks like currently IMA-ns will not be applicable for Kubernetes.
> 
> Br,
> Denis
> 
> -----Original Message-----
> From: Stefan Berger [mailto:stefanb@linux.ibm.com]
> Sent: Thursday, December 9, 2021 1:18 AM
> To: linux-integrity@vger.kernel.org
> Cc: zohar@linux.ibm.com; serge@hallyn.com; christian.brauner@ubuntu.com; containers@lists.linux.dev; dmitry.kasatkin@gmail.com; ebiederm@xmission.com; Krzysztof Struczynski <krzysztof.struczynski@huawei.com>; Roberto Sassu <roberto.sassu@huawei.com>; mpeters@redhat.com; lhinds@redhat.com; lsturman@redhat.com; puiterwi@redhat.com; jejb@linux.ibm.com; jamjoom@us.ibm.com; linux-kernel@vger.kernel.org; paul@paul-moore.com; rgb@redhat.com; linux-security-module@vger.kernel.org; jmorris@namei.org; Stefan Berger <stefanb@linux.ibm.com>; Denis Semakin <denis.semakin@huawei.com>
> Subject: [PATCH v5 14/16] ima: Use mac_admin_ns_capable() to check corresponding capability
> 
> Use mac_admin_ns_capable() to check corresponding capability to allow read/write IMA policy without CAP_SYS_ADMIN but with CAP_MAC_ADMIN.
> 
> Signed-off-by: Denis Semakin <denis.semakin@huawei.com>
> Signed-off-by: Stefan Berger <stefanb@linux.ibm.com>
> ---
>  include/linux/capability.h      | 6 ++++++
>  security/integrity/ima/ima_fs.c | 2 +-
>  2 files changed, 7 insertions(+), 1 deletion(-)
> 
> diff --git a/include/linux/capability.h b/include/linux/capability.h index 65efb74c3585..991579178f32 100644
> --- a/include/linux/capability.h
> +++ b/include/linux/capability.h
> @@ -270,6 +270,12 @@ static inline bool checkpoint_restore_ns_capable(struct user_namespace *ns)
>  		ns_capable(ns, CAP_SYS_ADMIN);
>  }
>  
> +static inline bool mac_admin_ns_capable(struct user_namespace *ns) {
> +	return ns_capable(ns, CAP_MAC_ADMIN) ||
> +		ns_capable(ns, CAP_SYS_ADMIN);
> +}
> +
>  /* audit system wants to get cap info from files as well */  int get_vfs_caps_from_disk(struct user_namespace *mnt_userns,
>  			   const struct dentry *dentry,
> diff --git a/security/integrity/ima/ima_fs.c b/security/integrity/ima/ima_fs.c index 0e582ceecc7f..a749a3e79304 100644
> --- a/security/integrity/ima/ima_fs.c
> +++ b/security/integrity/ima/ima_fs.c
> @@ -394,7 +394,7 @@ static int ima_open_policy(struct inode *inode, struct file *filp)  #else
>  		if ((filp->f_flags & O_ACCMODE) != O_RDONLY)
>  			return -EACCES;
> -		if (!capable(CAP_SYS_ADMIN))
> +		if (!mac_admin_ns_capable(ns->user_ns))
>  			return -EPERM;
>  		return seq_open(filp, &ima_policy_seqops);  #endif
> --
> 2.31.1

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

* Re: [PATCH v5 14/16] ima: Use mac_admin_ns_capable() to check corresponding capability
  2021-12-11 15:02     ` Serge E. Hallyn
@ 2021-12-11 15:38       ` Stefan Berger
  2021-12-11 16:00         ` James Bottomley
  0 siblings, 1 reply; 63+ messages in thread
From: Stefan Berger @ 2021-12-11 15:38 UTC (permalink / raw)
  To: Serge E. Hallyn, Denis Semakin
  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 12/11/21 10:02, Serge E. Hallyn wrote:
> IMO yes it is unsafe, however I concede that I am not sufficiently familiar
> with the policy language.  At least Stefan and Mimi (IIUC) want the host
> policy language to be able to specify cases where an IMA ns can be
> configured.  What's not clear to me is what sorts of triggers the host
> IMA policy could specify that would safely identify a IMA ns generation
> trigger.
>
> Stefan, would you mind showing what such a policy statement would look like?
> Does it amount to "/usr/bin/runc may create an IMA ns which escapes current
> policy" ?  Or is it by UID, or any file which has a certain xattr on it?

If this policy here is active on the host then file executions 
(BPRM_CHECK) of uid=0 should be measured and audited on the host in any 
IMA namespace that uid=0 may create. We achieve this with hierarchical 
processing (v6: 10/17).

measure func=BPRM_CHECK mask=MAY_EXEC uid=0

audit func=BPRM_CHECK mask=MAY_EXEC uid=0

    Stefan


>
> -serge
>
> On Thu, Dec 09, 2021 at 08:09:20AM +0000, Denis Semakin wrote:
>> Following that thoughts...
>> Will it be so incorrectly to unbound IMA-ns from USER-ns?
>> I realize that it could lead a lot of problems but it is still unclear will current IMA-ns will be useful for Kuber...
>> How userland supposed to use current IMA-ns implementation?
>>
>> Br,
>> Denis
>>
>> -----Original Message-----
>> From: Denis Semakin
>> Sent: Thursday, December 9, 2021 10:22 AM
>> To: 'Stefan Berger' <stefanb@linux.ibm.com>; linux-integrity@vger.kernel.org
>> Cc: zohar@linux.ibm.com; serge@hallyn.com; christian.brauner@ubuntu.com; containers@lists.linux.dev; dmitry.kasatkin@gmail.com; ebiederm@xmission.com; Krzysztof Struczynski <krzysztof.struczynski@huawei.com>; Roberto Sassu <roberto.sassu@huawei.com>; mpeters@redhat.com; lhinds@redhat.com; lsturman@redhat.com; puiterwi@redhat.com; jejb@linux.ibm.com; jamjoom@us.ibm.com; linux-kernel@vger.kernel.org; paul@paul-moore.com; rgb@redhat.com; linux-security-module@vger.kernel.org; jmorris@namei.org
>> Subject: RE: [PATCH v5 14/16] ima: Use mac_admin_ns_capable() to check corresponding capability
>>
>> Hi.
>> My question won't be about capabilities. I'm wondering how IMA-ns which is associated with USER-ns and is created during USER-ns creation would be used by some namespaces orchestration systems, e.g. Kubernetes?.. It seems that it can be run without any user namespaces...
>> Their community just discuss this opportunity to support User namespaces. (see https://github.com/kubernetes/enhancements/pull/2101)
>> Looks like currently IMA-ns will not be applicable for Kubernetes.
>>
>> Br,
>> Denis
>>
>> -----Original Message-----
>> From: Stefan Berger [mailto:stefanb@linux.ibm.com]
>> Sent: Thursday, December 9, 2021 1:18 AM
>> To: linux-integrity@vger.kernel.org
>> Cc: zohar@linux.ibm.com; serge@hallyn.com; christian.brauner@ubuntu.com; containers@lists.linux.dev; dmitry.kasatkin@gmail.com; ebiederm@xmission.com; Krzysztof Struczynski <krzysztof.struczynski@huawei.com>; Roberto Sassu <roberto.sassu@huawei.com>; mpeters@redhat.com; lhinds@redhat.com; lsturman@redhat.com; puiterwi@redhat.com; jejb@linux.ibm.com; jamjoom@us.ibm.com; linux-kernel@vger.kernel.org; paul@paul-moore.com; rgb@redhat.com; linux-security-module@vger.kernel.org; jmorris@namei.org; Stefan Berger <stefanb@linux.ibm.com>; Denis Semakin <denis.semakin@huawei.com>
>> Subject: [PATCH v5 14/16] ima: Use mac_admin_ns_capable() to check corresponding capability
>>
>> Use mac_admin_ns_capable() to check corresponding capability to allow read/write IMA policy without CAP_SYS_ADMIN but with CAP_MAC_ADMIN.
>>
>> Signed-off-by: Denis Semakin <denis.semakin@huawei.com>
>> Signed-off-by: Stefan Berger <stefanb@linux.ibm.com>
>> ---
>>   include/linux/capability.h      | 6 ++++++
>>   security/integrity/ima/ima_fs.c | 2 +-
>>   2 files changed, 7 insertions(+), 1 deletion(-)
>>
>> diff --git a/include/linux/capability.h b/include/linux/capability.h index 65efb74c3585..991579178f32 100644
>> --- a/include/linux/capability.h
>> +++ b/include/linux/capability.h
>> @@ -270,6 +270,12 @@ static inline bool checkpoint_restore_ns_capable(struct user_namespace *ns)
>>   		ns_capable(ns, CAP_SYS_ADMIN);
>>   }
>>   
>> +static inline bool mac_admin_ns_capable(struct user_namespace *ns) {
>> +	return ns_capable(ns, CAP_MAC_ADMIN) ||
>> +		ns_capable(ns, CAP_SYS_ADMIN);
>> +}
>> +
>>   /* audit system wants to get cap info from files as well */  int get_vfs_caps_from_disk(struct user_namespace *mnt_userns,
>>   			   const struct dentry *dentry,
>> diff --git a/security/integrity/ima/ima_fs.c b/security/integrity/ima/ima_fs.c index 0e582ceecc7f..a749a3e79304 100644
>> --- a/security/integrity/ima/ima_fs.c
>> +++ b/security/integrity/ima/ima_fs.c
>> @@ -394,7 +394,7 @@ static int ima_open_policy(struct inode *inode, struct file *filp)  #else
>>   		if ((filp->f_flags & O_ACCMODE) != O_RDONLY)
>>   			return -EACCES;
>> -		if (!capable(CAP_SYS_ADMIN))
>> +		if (!mac_admin_ns_capable(ns->user_ns))
>>   			return -EPERM;
>>   		return seq_open(filp, &ima_policy_seqops);  #endif
>> --
>> 2.31.1

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

* Re: [PATCH v5 14/16] ima: Use mac_admin_ns_capable() to check corresponding capability
  2021-12-11 15:38       ` Stefan Berger
@ 2021-12-11 16:00         ` James Bottomley
  0 siblings, 0 replies; 63+ messages in thread
From: James Bottomley @ 2021-12-11 16:00 UTC (permalink / raw)
  To: Stefan Berger, Serge E. Hallyn, Denis Semakin
  Cc: linux-integrity, zohar, christian.brauner, containers,
	dmitry.kasatkin, ebiederm, Krzysztof Struczynski, Roberto Sassu,
	mpeters, lhinds, lsturman, puiterwi, jamjoom, linux-kernel, paul,
	rgb, linux-security-module, jmorris

On Sat, 2021-12-11 at 10:38 -0500, Stefan Berger wrote:
> On 12/11/21 10:02, Serge E. Hallyn wrote:
> > IMO yes it is unsafe, however I concede that I am not sufficiently
> > familiar with the policy language.  At least Stefan and Mimi (IIUC)
> > want the host policy language to be able to specify cases where an
> > IMA ns can be configured.  What's not clear to me is what sorts of
> > triggers the host IMA policy could specify that would safely
> > identify a IMA ns generation
> > trigger.
> > 
> > Stefan, would you mind showing what such a policy statement would
> > look like? Does it amount to "/usr/bin/runc may create an IMA ns
> > which escapes current policy" ?  Or is it by UID, or any file which
> > has a certain xattr on it?
> 
> If this policy here is active on the host then file executions 
> (BPRM_CHECK) of uid=0 should be measured and audited on the host in
> any IMA namespace that uid=0 may create. We achieve this with
> hierarchical processing (v6: 10/17).
> 
> measure func=BPRM_CHECK mask=MAY_EXEC uid=0
> 
> audit func=BPRM_CHECK mask=MAY_EXEC uid=0

Or perhaps to put another way that might be more useful to unprivileged
containers: if you strip the uid=0 from both of those statements, you
get a rule that logs and audits any execution.  Once you enter the IMA
namespace, in that namespace you see nothing, but outside the parent is
still logging and auditing all executions, including those inside the
container, according to its measure/audit all executions rule.  The
container can't turn that off by writes to its policy file.

So the container can never escape any policy rule imposed by the parent

James



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

* Re: [PATCH v5 15/16] ima: Move dentries into ima_namespace
  2021-12-10 11:49           ` Christian Brauner
  2021-12-10 12:09             ` Mimi Zohar
@ 2021-12-12 14:13             ` James Bottomley
  2021-12-13 11:25               ` Christian Brauner
  1 sibling, 1 reply; 63+ messages in thread
From: James Bottomley @ 2021-12-12 14:13 UTC (permalink / raw)
  To: Christian Brauner
  Cc: Stefan Berger, linux-integrity, zohar, serge, containers,
	dmitry.kasatkin, ebiederm, krzysztof.struczynski, roberto.sassu,
	mpeters, lhinds, lsturman, puiterwi, jamjoom, linux-kernel, paul,
	rgb, linux-security-module, jmorris

On Fri, 2021-12-10 at 12:49 +0100, Christian Brauner wrote:
> On Thu, Dec 09, 2021 at 02:38:13PM -0500, James Bottomley wrote:
[...]
> > @@ -317,21 +315,15 @@ EXPORT_SYMBOL_GPL(securityfs_create_symlink);
> >  void securityfs_remove(struct dentry *dentry)
> >  {
> >  	struct user_namespace *ns = dentry->d_sb->s_user_ns;
> > -	struct inode *dir;
> >  
> >  	if (!dentry || IS_ERR(dentry))
> >  		return;
> >  
> > -	dir = d_inode(dentry->d_parent);
> > -	inode_lock(dir);
> >  	if (simple_positive(dentry)) {
> > -		if (d_is_dir(dentry))
> > -			simple_rmdir(dir, dentry);
> > -		else
> > -			simple_unlink(dir, dentry);
> > +		d_delete(dentry);
> 
> Not, that doesn't work. You can't just call d_delete() and dput() and
> even if I wouldn't advise it. And you also can't do this without
> taking the inode lock on the directory.

Agreed on that

> simple_rmdir()/simple_unlink() take care to update various inode
> fields in the parent dir and handle link counts. This really wants to
> be sm like
> 
> 	struct inode *parent_inode;
> 
> 	parent_inode = d_inode(dentry->d_parent);
> 	inode_lock(parent_inode);
> 	if (simple_positive(dentry)) {
> 		dget(dentry);
> 		if (d_is_dir(dentry)
> 			simple_unlink(parent_inode, dentry);
> 		else
> 			simple_unlink(parent_inode, dentry);
> 		d_delete(dentry);
> 		dput(dentry);
> 	}
> 	inode_unlock(parent_inode);

It just slightly annoys me how the simple_ functions change fields in
an inode that is about to be evicted ... it seems redundant; plus we
shouldn't care if the object we're deleting is a directory or file.  I
also don't think we need the additional dget because the only consumer
is policy file removal and the opener of that file will have done a
dget.  The inode lock now prevents us racing with another remove in the
case of two simultaneous writes.

How about

        struct inode *parent_inode;

        parent_inode = d_inode(dentry->d_parent);
        inode_lock(parent_inode);
        if (simple_positive(dentry)) {
		drop_nlink(parent_inode);
                d_delete(dentry);
                dput(dentry);
        }
        inode_unlock(parent_inode);

James



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

* Re: [PATCH v5 15/16] ima: Move dentries into ima_namespace
  2021-12-12 14:13             ` James Bottomley
@ 2021-12-13 11:25               ` Christian Brauner
  0 siblings, 0 replies; 63+ messages in thread
From: Christian Brauner @ 2021-12-13 11:25 UTC (permalink / raw)
  To: James Bottomley
  Cc: Stefan Berger, linux-integrity, zohar, serge, containers,
	dmitry.kasatkin, ebiederm, krzysztof.struczynski, roberto.sassu,
	mpeters, lhinds, lsturman, puiterwi, jamjoom, linux-kernel, paul,
	rgb, linux-security-module, jmorris

On Sun, Dec 12, 2021 at 09:13:12AM -0500, James Bottomley wrote:
> On Fri, 2021-12-10 at 12:49 +0100, Christian Brauner wrote:
> > On Thu, Dec 09, 2021 at 02:38:13PM -0500, James Bottomley wrote:
> [...]
> > > @@ -317,21 +315,15 @@ EXPORT_SYMBOL_GPL(securityfs_create_symlink);
> > >  void securityfs_remove(struct dentry *dentry)
> > >  {
> > >  	struct user_namespace *ns = dentry->d_sb->s_user_ns;
> > > -	struct inode *dir;
> > >  
> > >  	if (!dentry || IS_ERR(dentry))
> > >  		return;
> > >  
> > > -	dir = d_inode(dentry->d_parent);
> > > -	inode_lock(dir);
> > >  	if (simple_positive(dentry)) {
> > > -		if (d_is_dir(dentry))
> > > -			simple_rmdir(dir, dentry);
> > > -		else
> > > -			simple_unlink(dir, dentry);
> > > +		d_delete(dentry);
> > 
> > Not, that doesn't work. You can't just call d_delete() and dput() and
> > even if I wouldn't advise it. And you also can't do this without
> > taking the inode lock on the directory.
> 
> Agreed on that
> 
> > simple_rmdir()/simple_unlink() take care to update various inode
> > fields in the parent dir and handle link counts. This really wants to
> > be sm like
> > 
> > 	struct inode *parent_inode;
> > 
> > 	parent_inode = d_inode(dentry->d_parent);
> > 	inode_lock(parent_inode);
> > 	if (simple_positive(dentry)) {
> > 		dget(dentry);
> > 		if (d_is_dir(dentry)
> > 			simple_unlink(parent_inode, dentry);
> > 		else
> > 			simple_unlink(parent_inode, dentry);
> > 		d_delete(dentry);
> > 		dput(dentry);
> > 	}
> > 	inode_unlock(parent_inode);
> 
> It just slightly annoys me how the simple_ functions change fields in
> an inode that is about to be evicted ... it seems redundant; plus we
> shouldn't care if the object we're deleting is a directory or file.  I
> also don't think we need the additional dget because the only consumer
> is policy file removal and the opener of that file will have done a
> dget.  The inode lock now prevents us racing with another remove in the
> case of two simultaneous writes.
> 
> How about
> 
>         struct inode *parent_inode;
> 
>         parent_inode = d_inode(dentry->d_parent);
>         inode_lock(parent_inode);
>         if (simple_positive(dentry)) {
> 		drop_nlink(parent_inode);
>                 d_delete(dentry);
>                 dput(dentry);
>         }
>         inode_unlock(parent_inode);

It doesn't just change fields in an inode that is about to be evicted.
It changes fields in the parent inode.
If you're deleting any file or directory your function currently fails
to update mtime and ctime for the parent directory.

What you're doing below also isn't all that future proof or safe for
callers other than ima.

Consider a future caller that might want to call securityfs_remove() with

.open   = first_file()

.relase = first_release(
	securityfs_remove(second_file)
)

.open    = second_file()

If your securityfs_remove() is called from the first file's release
function while the second_file is still open and thus holds a reference
and won't go way during first_release()'s securityfs_remove() call you
have just failed to update relevant inode fields of a file that can
still be queried via stat* functions and can be used to create other
files below it.

In addition, if someone accidently calls your securityfs_remove() on a
directory that is not-empty you're effectively deleting the directory
without deleting the files in that directory first whereas
simple_rmdir() would tell you to go away.

If a user later needs an .unlink/.rmdir method for securityfs or allows
calls of securityfs_remove() on the same dentry from concurrent
locations you need the dget() in securityfs_remove() even if the
inode_lock() is exclusive otherwise you can end up doing a dput() too
many, iirc.

I would recommened to not turn this into a nih exercise for simple vfs
functionality. Your version isn't even significantly simpler. The
securityfs_remove() function doesn't need to be reinvented.

void securityfs_remove(struct dentry *dentry)
{
	struct inode *dir;

	if (WARN_ON(!dentry || IS_ERR(dentry)))
		return;

	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);
}

I'm not claiming or trying to make this the most minimal version of
securityfs_remove() that we could possibly get but I'm making it one
where we don't have to worry that there's a subtle corner case that'll
bite us in the future just because we tried to hand-massage a function
that isn't in any hotpath.

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

* Re: [PATCH v5 13/16] ima: Move some IMA policy and filesystem related variables into ima_namespace
  2021-12-11  9:50           ` Christian Brauner
  2021-12-11 10:45             ` Christian Brauner
@ 2021-12-13 15:33             ` Stefan Berger
  2021-12-13 15:50               ` Christian Brauner
  1 sibling, 1 reply; 63+ messages in thread
From: Stefan Berger @ 2021-12-13 15:33 UTC (permalink / raw)
  To: Christian Brauner
  Cc: linux-integrity, zohar, serge, containers, dmitry.kasatkin,
	ebiederm, krzysztof.struczynski, roberto.sassu, mpeters, lhinds,
	lsturman, puiterwi, jejb, jamjoom, linux-kernel, paul, rgb,
	linux-security-module, jmorris


On 12/11/21 04:50, Christian Brauner wrote:
> On Fri, Dec 10, 2021 at 08:57:11AM -0500, Stefan Berger wrote:
>>
>>
>> there anything that would prevent us from setns()'ing to that target user
>> namespace so that we would now see that of a user namespace that we are not
>> allowed to see?
> If you're really worried about someone being able to access a securityfs
> instance whose userns doesn't match the userns the securityfs instance
> was mounted in there are multiple ways to fix it. The one that I tend to
> prefer is:
>
>  From e0ff6a8dcc573763568e685dd70d1547efd68df9 Mon Sep 17 00:00:00 2001
> From: Christian Brauner <christian.brauner@ubuntu.com>
> Date: Fri, 10 Dec 2021 11:47:37 +0100
> Subject: !!!! HERE BE DRAGONS - COMPLETELY UNTESTED !!!!
>
> securityfs: only allow access to securityfs from within same namespace
>
> Limit opening of securityfs files to callers located in the same namespace.
>
> ---
>   security/inode.c | 33 +++++++++++++++++++++++++++++++--
>   1 file changed, 31 insertions(+), 2 deletions(-)
>
> diff --git a/security/inode.c b/security/inode.c
> index eaccba7017d9..9eaf757c08cb 100644
> --- a/security/inode.c
> +++ b/security/inode.c
> @@ -80,6 +80,35 @@ static struct file_system_type fs_type = {
>   	.fs_flags =	FS_USERNS_MOUNT,
>   };
>   
> +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) {
> +		if (inode->i_sb->s_user_ns != current_user_ns())
> +			err = -EACCES;
> +	}
> +
> +	return err;
> +}
> +
> +const struct inode_operations securityfs_dir_inode_operations = {
> +	.permission	= securityfs_permission,
> +	.lookup		= simple_lookup,
> +};
> +
> +const struct file_operations securityfs_dir_operations = {
> +	.permission	= securityfs_permission,


This interface function on file operations doesn't exist.

I'll use the inode_operations and also hook it to the root dentry of the 
super_block. Then there's no need to have this check on symlinks and 
files...



> +	.open		= dcache_dir_open,
> +	.release	= dcache_dir_close,
> +	.llseek		= dcache_dir_lseek,
> +	.read		= generic_read_dir,
> +	.iterate_shared	= dcache_readdir,
> +	.fsync		= noop_fsync,
> +};
> +
>   /**
>    * securityfs_create_dentry - create a dentry in the securityfs filesystem
>    *
> @@ -167,8 +196,8 @@ 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_fop = &simple_dir_operations;
> +		inode->i_op = &securityfs_dir_inode_operations;
> +		inode->i_fop = &securityfs_dir_operations;
>   		inc_nlink(inode);
>   		inc_nlink(dir);
>   	} else if (S_ISLNK(mode)) {

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

* Re: [PATCH v5 13/16] ima: Move some IMA policy and filesystem related variables into ima_namespace
  2021-12-13 15:33             ` Stefan Berger
@ 2021-12-13 15:50               ` Christian Brauner
  2021-12-13 16:03                 ` Christian Brauner
                                   ` (2 more replies)
  0 siblings, 3 replies; 63+ messages in thread
From: Christian Brauner @ 2021-12-13 15:50 UTC (permalink / raw)
  To: Stefan Berger
  Cc: linux-integrity, zohar, serge, containers, dmitry.kasatkin,
	ebiederm, krzysztof.struczynski, roberto.sassu, mpeters, lhinds,
	lsturman, puiterwi, jejb, jamjoom, linux-kernel, paul, rgb,
	linux-security-module, jmorris

On Mon, Dec 13, 2021 at 10:33:40AM -0500, Stefan Berger wrote:
> 
> On 12/11/21 04:50, Christian Brauner wrote:
> > On Fri, Dec 10, 2021 at 08:57:11AM -0500, Stefan Berger wrote:
> > > 
> > > 
> > > there anything that would prevent us from setns()'ing to that target user
> > > namespace so that we would now see that of a user namespace that we are not
> > > allowed to see?
> > If you're really worried about someone being able to access a securityfs
> > instance whose userns doesn't match the userns the securityfs instance
> > was mounted in there are multiple ways to fix it. The one that I tend to
> > prefer is:
> > 
> >  From e0ff6a8dcc573763568e685dd70d1547efd68df9 Mon Sep 17 00:00:00 2001
> > From: Christian Brauner <christian.brauner@ubuntu.com>
> > Date: Fri, 10 Dec 2021 11:47:37 +0100
> > Subject: !!!! HERE BE DRAGONS - COMPLETELY UNTESTED !!!!
> > 
> > securityfs: only allow access to securityfs from within same namespace
> > 
> > Limit opening of securityfs files to callers located in the same namespace.
> > 
> > ---
> >   security/inode.c | 33 +++++++++++++++++++++++++++++++--
> >   1 file changed, 31 insertions(+), 2 deletions(-)
> > 
> > diff --git a/security/inode.c b/security/inode.c
> > index eaccba7017d9..9eaf757c08cb 100644
> > --- a/security/inode.c
> > +++ b/security/inode.c
> > @@ -80,6 +80,35 @@ static struct file_system_type fs_type = {
> >   	.fs_flags =	FS_USERNS_MOUNT,
> >   };
> > +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) {
> > +		if (inode->i_sb->s_user_ns != current_user_ns())
> > +			err = -EACCES;
> > +	}
> > +
> > +	return err;
> > +}
> > +
> > +const struct inode_operations securityfs_dir_inode_operations = {
> > +	.permission	= securityfs_permission,
> > +	.lookup		= simple_lookup,
> > +};
> > +
> > +const struct file_operations securityfs_dir_operations = {
> > +	.permission	= securityfs_permission,
> 
> 
> This interface function on file operations doesn't exist.

It's almost as if the subject line of this patch warned about its draft
character. That was supposed for regular files.

> 
> I'll use the inode_operations and also hook it to the root dentry of the
> super_block. Then there's no need to have this check on symlinks and
> files...

Don't special case the inode_operations for the root inode!
If a privileged process opens an fd refering to a struct file for the
root inode and leaks it to an unprivileged process by accident the
unprivileged process can open any file or directory beneath via openat()
and friends.

Symlinks don't need a .permission handler anyway because they just
contain the name of another file and that is checked for .permission
unless you have a separate .getlink handler where you want to restrict
link contents further.

But regular files need to have a .permission method see openat().

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

* Re: [PATCH v5 13/16] ima: Move some IMA policy and filesystem related variables into ima_namespace
  2021-12-13 15:50               ` Christian Brauner
@ 2021-12-13 16:03                 ` Christian Brauner
  2021-12-13 16:25                 ` Stefan Berger
  2021-12-13 16:40                 ` Christian Brauner
  2 siblings, 0 replies; 63+ messages in thread
From: Christian Brauner @ 2021-12-13 16:03 UTC (permalink / raw)
  To: Stefan Berger
  Cc: linux-integrity, zohar, serge, containers, dmitry.kasatkin,
	ebiederm, krzysztof.struczynski, roberto.sassu, mpeters, lhinds,
	lsturman, puiterwi, jejb, jamjoom, linux-kernel, paul, rgb,
	linux-security-module, jmorris

On Mon, Dec 13, 2021 at 04:50:20PM +0100, Christian Brauner wrote:
> On Mon, Dec 13, 2021 at 10:33:40AM -0500, Stefan Berger wrote:
> > 
> > On 12/11/21 04:50, Christian Brauner wrote:
> > > On Fri, Dec 10, 2021 at 08:57:11AM -0500, Stefan Berger wrote:
> > > > 
> > > > 
> > > > there anything that would prevent us from setns()'ing to that target user
> > > > namespace so that we would now see that of a user namespace that we are not
> > > > allowed to see?
> > > If you're really worried about someone being able to access a securityfs
> > > instance whose userns doesn't match the userns the securityfs instance
> > > was mounted in there are multiple ways to fix it. The one that I tend to
> > > prefer is:
> > > 
> > >  From e0ff6a8dcc573763568e685dd70d1547efd68df9 Mon Sep 17 00:00:00 2001
> > > From: Christian Brauner <christian.brauner@ubuntu.com>
> > > Date: Fri, 10 Dec 2021 11:47:37 +0100
> > > Subject: !!!! HERE BE DRAGONS - COMPLETELY UNTESTED !!!!
> > > 
> > > securityfs: only allow access to securityfs from within same namespace
> > > 
> > > Limit opening of securityfs files to callers located in the same namespace.
> > > 
> > > ---
> > >   security/inode.c | 33 +++++++++++++++++++++++++++++++--
> > >   1 file changed, 31 insertions(+), 2 deletions(-)
> > > 
> > > diff --git a/security/inode.c b/security/inode.c
> > > index eaccba7017d9..9eaf757c08cb 100644
> > > --- a/security/inode.c
> > > +++ b/security/inode.c
> > > @@ -80,6 +80,35 @@ static struct file_system_type fs_type = {
> > >   	.fs_flags =	FS_USERNS_MOUNT,
> > >   };
> > > +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) {
> > > +		if (inode->i_sb->s_user_ns != current_user_ns())
> > > +			err = -EACCES;
> > > +	}
> > > +
> > > +	return err;
> > > +}
> > > +
> > > +const struct inode_operations securityfs_dir_inode_operations = {
> > > +	.permission	= securityfs_permission,
> > > +	.lookup		= simple_lookup,
> > > +};
> > > +
> > > +const struct file_operations securityfs_dir_operations = {
> > > +	.permission	= securityfs_permission,
> > 
> > 
> > This interface function on file operations doesn't exist.
> 
> It's almost as if the subject line of this patch warned about its draft
> character. That was supposed for regular files.
> 
> > 
> > I'll use the inode_operations and also hook it to the root dentry of the
> > super_block. Then there's no need to have this check on symlinks and
> > files...
> 
> Don't special case the inode_operations for the root inode!
> If a privileged process opens an fd refering to a struct file for the

s/a privileged process/a process that is located in an ancestor userns
of the securityfs instance

> root inode and leaks it to an unprivileged process by accident the

s/unprivileged process/process located in a descendant userns

> unprivileged process can open any file or directory beneath via openat()
> and friends.

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

* Re: [PATCH v5 13/16] ima: Move some IMA policy and filesystem related variables into ima_namespace
  2021-12-13 15:50               ` Christian Brauner
  2021-12-13 16:03                 ` Christian Brauner
@ 2021-12-13 16:25                 ` Stefan Berger
  2021-12-13 16:37                   ` Christian Brauner
  2021-12-13 16:40                 ` Christian Brauner
  2 siblings, 1 reply; 63+ messages in thread
From: Stefan Berger @ 2021-12-13 16:25 UTC (permalink / raw)
  To: Christian Brauner
  Cc: linux-integrity, zohar, serge, containers, dmitry.kasatkin,
	ebiederm, krzysztof.struczynski, roberto.sassu, mpeters, lhinds,
	lsturman, puiterwi, jejb, jamjoom, linux-kernel, paul, rgb,
	linux-security-module, jmorris


On 12/13/21 10:50, Christian Brauner wrote:
> On Mon, Dec 13, 2021 at 10:33:40AM -0500, Stefan Berger wrote:
>> On 12/11/21 04:50, Christian Brauner wrote:
>>> On Fri, Dec 10, 2021 at 08:57:11AM -0500, Stefan Berger wrote:
>>>>
>>>> there anything that would prevent us from setns()'ing to that target user
>>>> namespace so that we would now see that of a user namespace that we are not
>>>> allowed to see?
>>> If you're really worried about someone being able to access a securityfs
>>> instance whose userns doesn't match the userns the securityfs instance
>>> was mounted in there are multiple ways to fix it. The one that I tend to
>>> prefer is:
>>>
>>>   From e0ff6a8dcc573763568e685dd70d1547efd68df9 Mon Sep 17 00:00:00 2001
>>> From: Christian Brauner <christian.brauner@ubuntu.com>
>>> Date: Fri, 10 Dec 2021 11:47:37 +0100
>>> Subject: !!!! HERE BE DRAGONS - COMPLETELY UNTESTED !!!!
>>>
>>> securityfs: only allow access to securityfs from within same namespace
>>>
>>> Limit opening of securityfs files to callers located in the same namespace.
>>>
>>> ---
>>>    security/inode.c | 33 +++++++++++++++++++++++++++++++--
>>>    1 file changed, 31 insertions(+), 2 deletions(-)
>>>
>>> diff --git a/security/inode.c b/security/inode.c
>>> index eaccba7017d9..9eaf757c08cb 100644
>>> --- a/security/inode.c
>>> +++ b/security/inode.c
>>> @@ -80,6 +80,35 @@ static struct file_system_type fs_type = {
>>>    	.fs_flags =	FS_USERNS_MOUNT,
>>>    };
>>> +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) {
>>> +		if (inode->i_sb->s_user_ns != current_user_ns())
>>> +			err = -EACCES;
>>> +	}
>>> +
>>> +	return err;
>>> +}
>>> +
>>> +const struct inode_operations securityfs_dir_inode_operations = {
>>> +	.permission	= securityfs_permission,
>>> +	.lookup		= simple_lookup,
>>> +};
>>> +
>>> +const struct file_operations securityfs_dir_operations = {
>>> +	.permission	= securityfs_permission,
>>
>> This interface function on file operations doesn't exist.
> It's almost as if the subject line of this patch warned about its draft
> character. That was supposed for regular files.
>
>> I'll use the inode_operations and also hook it to the root dentry of the
>> super_block. Then there's no need to have this check on symlinks and
>> files...
> Don't special case the inode_operations for the root inode!

I modified the inode_operations *also* for the root node, since that one 
is initialized with &simple_dir_inode_operationsby simple_fill_super, so 
I didn't want to miss it...


> If a privileged process opens an fd refering to a struct file for the
> root inode and leaks it to an unprivileged process by accident the
> unprivileged process can open any file or directory beneath via openat()
> and friends.
>
> Symlinks don't need a .permission handler anyway because they just
> contain the name of another file and that is checked for .permission
> unless you have a separate .getlink handler where you want to restrict
> link contents further.
>
> But regular files need to have a .permission method see openat().

So here's what I have now for the hardening.


diff --git a/security/inode.c b/security/inode.c
index fee01ff4d831..a0d9f086e3d5 100644
--- a/security/inode.c
+++ b/security/inode.c
@@ -26,6 +26,29 @@
  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) {
+               if (inode->i_sb->s_user_ns != current_user_ns())
+                       err = -EACCES;
+       }
+
+       return err;
+}
+
+const struct inode_operations securityfs_dir_inode_operations = {
+       .permission     = securityfs_permission,
+       .lookup         = simple_lookup,
+};
+
+const struct inode_operations securityfs_file_inode_operations = {
+       .permission     = securityfs_permission,
+};
+
  static void securityfs_free_inode(struct inode *inode)
  {
         if (S_ISLNK(inode->i_mode))
@@ -41,20 +64,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;
  }
[...]
@@ -157,7 +186,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);
@@ -165,10 +194,10 @@ static struct dentry 
*securityfs_create_dentry(const char *name, umode_t mode,
                 inode->i_op = iops ? iops : 
&simple_symlink_inode_operations;
                 inode->i_link = data;
         } else {
+               inode->i_op = &securityfs_file_inode_operations;
                 inode->i_fop = fops;
         }
         d_instantiate(dentry, inode);


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

* Re: [PATCH v5 13/16] ima: Move some IMA policy and filesystem related variables into ima_namespace
  2021-12-13 16:25                 ` Stefan Berger
@ 2021-12-13 16:37                   ` Christian Brauner
  0 siblings, 0 replies; 63+ messages in thread
From: Christian Brauner @ 2021-12-13 16:37 UTC (permalink / raw)
  To: Stefan Berger
  Cc: linux-integrity, zohar, serge, containers, dmitry.kasatkin,
	ebiederm, krzysztof.struczynski, roberto.sassu, mpeters, lhinds,
	lsturman, puiterwi, jejb, jamjoom, linux-kernel, paul, rgb,
	linux-security-module, jmorris

On Mon, Dec 13, 2021 at 11:25:28AM -0500, Stefan Berger wrote:
> 
> On 12/13/21 10:50, Christian Brauner wrote:
> > On Mon, Dec 13, 2021 at 10:33:40AM -0500, Stefan Berger wrote:
> > > On 12/11/21 04:50, Christian Brauner wrote:
> > > > On Fri, Dec 10, 2021 at 08:57:11AM -0500, Stefan Berger wrote:
> > > > > 
> > > > > there anything that would prevent us from setns()'ing to that target user
> > > > > namespace so that we would now see that of a user namespace that we are not
> > > > > allowed to see?
> > > > If you're really worried about someone being able to access a securityfs
> > > > instance whose userns doesn't match the userns the securityfs instance
> > > > was mounted in there are multiple ways to fix it. The one that I tend to
> > > > prefer is:
> > > > 
> > > >   From e0ff6a8dcc573763568e685dd70d1547efd68df9 Mon Sep 17 00:00:00 2001
> > > > From: Christian Brauner <christian.brauner@ubuntu.com>
> > > > Date: Fri, 10 Dec 2021 11:47:37 +0100
> > > > Subject: !!!! HERE BE DRAGONS - COMPLETELY UNTESTED !!!!
> > > > 
> > > > securityfs: only allow access to securityfs from within same namespace
> > > > 
> > > > Limit opening of securityfs files to callers located in the same namespace.
> > > > 
> > > > ---
> > > >    security/inode.c | 33 +++++++++++++++++++++++++++++++--
> > > >    1 file changed, 31 insertions(+), 2 deletions(-)
> > > > 
> > > > diff --git a/security/inode.c b/security/inode.c
> > > > index eaccba7017d9..9eaf757c08cb 100644
> > > > --- a/security/inode.c
> > > > +++ b/security/inode.c
> > > > @@ -80,6 +80,35 @@ static struct file_system_type fs_type = {
> > > >    	.fs_flags =	FS_USERNS_MOUNT,
> > > >    };
> > > > +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) {
> > > > +		if (inode->i_sb->s_user_ns != current_user_ns())
> > > > +			err = -EACCES;
> > > > +	}
> > > > +
> > > > +	return err;
> > > > +}
> > > > +
> > > > +const struct inode_operations securityfs_dir_inode_operations = {
> > > > +	.permission	= securityfs_permission,
> > > > +	.lookup		= simple_lookup,
> > > > +};
> > > > +
> > > > +const struct file_operations securityfs_dir_operations = {
> > > > +	.permission	= securityfs_permission,
> > > 
> > > This interface function on file operations doesn't exist.
> > It's almost as if the subject line of this patch warned about its draft
> > character. That was supposed for regular files.
> > 
> > > I'll use the inode_operations and also hook it to the root dentry of the
> > > super_block. Then there's no need to have this check on symlinks and
> > > files...
> > Don't special case the inode_operations for the root inode!
> 
> I modified the inode_operations *also* for the root node, since that one is
> initialized with &simple_dir_inode_operationsby simple_fill_super, so I
> didn't want to miss it...
> 
> 
> > If a privileged process opens an fd refering to a struct file for the
> > root inode and leaks it to an unprivileged process by accident the
> > unprivileged process can open any file or directory beneath via openat()
> > and friends.
> > 
> > Symlinks don't need a .permission handler anyway because they just
> > contain the name of another file and that is checked for .permission
> > unless you have a separate .getlink handler where you want to restrict
> > link contents further.
> > 
> > But regular files need to have a .permission method see openat().
> 
> So here's what I have now for the hardening.
> 
> 
> diff --git a/security/inode.c b/security/inode.c
> index fee01ff4d831..a0d9f086e3d5 100644
> --- a/security/inode.c
> +++ b/security/inode.c
> @@ -26,6 +26,29 @@
>  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) {
> +               if (inode->i_sb->s_user_ns != current_user_ns())
> +                       err = -EACCES;

Please consider
https://lore.kernel.org/lkml/20211211104527.7cp4mbznjpcijfqx@wittgenstein
otherwise looks ok.

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

* Re: [PATCH v5 13/16] ima: Move some IMA policy and filesystem related variables into ima_namespace
  2021-12-13 15:50               ` Christian Brauner
  2021-12-13 16:03                 ` Christian Brauner
  2021-12-13 16:25                 ` Stefan Berger
@ 2021-12-13 16:40                 ` Christian Brauner
  2 siblings, 0 replies; 63+ messages in thread
From: Christian Brauner @ 2021-12-13 16:40 UTC (permalink / raw)
  To: Stefan Berger
  Cc: linux-integrity, zohar, serge, containers, dmitry.kasatkin,
	ebiederm, krzysztof.struczynski, roberto.sassu, mpeters, lhinds,
	lsturman, puiterwi, jejb, jamjoom, linux-kernel, paul, rgb,
	linux-security-module, jmorris

On Mon, Dec 13, 2021 at 04:50:20PM +0100, Christian Brauner wrote:
> On Mon, Dec 13, 2021 at 10:33:40AM -0500, Stefan Berger wrote:
> > 
> > On 12/11/21 04:50, Christian Brauner wrote:
> > > On Fri, Dec 10, 2021 at 08:57:11AM -0500, Stefan Berger wrote:
> > > > 
> > > > 
> > > > there anything that would prevent us from setns()'ing to that target user
> > > > namespace so that we would now see that of a user namespace that we are not
> > > > allowed to see?
> > > If you're really worried about someone being able to access a securityfs
> > > instance whose userns doesn't match the userns the securityfs instance
> > > was mounted in there are multiple ways to fix it. The one that I tend to
> > > prefer is:
> > > 
> > >  From e0ff6a8dcc573763568e685dd70d1547efd68df9 Mon Sep 17 00:00:00 2001
> > > From: Christian Brauner <christian.brauner@ubuntu.com>
> > > Date: Fri, 10 Dec 2021 11:47:37 +0100
> > > Subject: !!!! HERE BE DRAGONS - COMPLETELY UNTESTED !!!!
> > > 
> > > securityfs: only allow access to securityfs from within same namespace
> > > 
> > > Limit opening of securityfs files to callers located in the same namespace.
> > > 
> > > ---
> > >   security/inode.c | 33 +++++++++++++++++++++++++++++++--
> > >   1 file changed, 31 insertions(+), 2 deletions(-)
> > > 
> > > diff --git a/security/inode.c b/security/inode.c
> > > index eaccba7017d9..9eaf757c08cb 100644
> > > --- a/security/inode.c
> > > +++ b/security/inode.c
> > > @@ -80,6 +80,35 @@ static struct file_system_type fs_type = {
> > >   	.fs_flags =	FS_USERNS_MOUNT,
> > >   };
> > > +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) {
> > > +		if (inode->i_sb->s_user_ns != current_user_ns())
> > > +			err = -EACCES;
> > > +	}
> > > +
> > > +	return err;
> > > +}
> > > +
> > > +const struct inode_operations securityfs_dir_inode_operations = {
> > > +	.permission	= securityfs_permission,
> > > +	.lookup		= simple_lookup,
> > > +};
> > > +
> > > +const struct file_operations securityfs_dir_operations = {
> > > +	.permission	= securityfs_permission,
> > 
> > 
> > This interface function on file operations doesn't exist.
> 
> It's almost as if the subject line of this patch warned about its draft
> character. That was supposed for regular files.

Mah, I deleted the ;) after it on accident. I wasn't mocking. :)

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

end of thread, other threads:[~2021-12-13 16:40 UTC | newest]

Thread overview: 63+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-12-08 22:18 [PATCH v5 00/16] ima: Namespace IMA with audit support in IMA-ns Stefan Berger
2021-12-08 22:18 ` [PATCH v5 01/16] ima: Add IMA namespace support Stefan Berger
2021-12-08 22:18 ` [PATCH v5 02/16] ima: Define ns_status for storing namespaced iint data Stefan Berger
2021-12-08 22:18 ` [PATCH v5 03/16] ima: Namespace audit status flags Stefan Berger
2021-12-08 22:18 ` [PATCH v5 04/16] ima: Move delayed work queue and variables into ima_namespace Stefan Berger
2021-12-09 13:11   ` Christian Brauner
2021-12-09 15:09     ` Stefan Berger
2021-12-08 22:18 ` [PATCH v5 05/16] ima: Move IMA's keys queue related " Stefan Berger
2021-12-08 22:18 ` [PATCH v5 06/16] ima: Move policy " Stefan Berger
2021-12-08 22:18 ` [PATCH v5 07/16] ima: Move ima_htable " Stefan Berger
2021-12-08 22:18 ` [PATCH v5 08/16] ima: Move measurement list related variables " Stefan Berger
2021-12-08 22:18 ` [PATCH v5 09/16] ima: Only accept AUDIT rules for IMA non-init_ima_ns namespaces for now Stefan Berger
2021-12-08 22:18 ` [PATCH v5 10/16] ima: Implement hierarchical processing of file accesses Stefan Berger
2021-12-08 22:18 ` [PATCH v5 11/16] securityfs: Only use simple_pin_fs/simple_release_fs for init_user_ns Stefan Berger
2021-12-08 22:18 ` [PATCH v5 12/16] securityfs: Extend securityfs with namespacing support Stefan Berger
2021-12-08 22:18 ` [PATCH v5 13/16] ima: Move some IMA policy and filesystem related variables into ima_namespace Stefan Berger
2021-12-09 19:11   ` Christian Brauner
2021-12-09 20:42     ` Stefan Berger
2021-12-10  0:57     ` Stefan Berger
2021-12-10 11:32       ` Christian Brauner
2021-12-10 13:57         ` Stefan Berger
2021-12-10 14:21           ` James Bottomley
2021-12-11  9:50           ` Christian Brauner
2021-12-11 10:45             ` Christian Brauner
2021-12-13 15:33             ` Stefan Berger
2021-12-13 15:50               ` Christian Brauner
2021-12-13 16:03                 ` Christian Brauner
2021-12-13 16:25                 ` Stefan Berger
2021-12-13 16:37                   ` Christian Brauner
2021-12-13 16:40                 ` Christian Brauner
2021-12-10 20:08         ` Stefan Berger
2021-12-11  8:46           ` Christian Brauner
2021-12-08 22:18 ` [PATCH v5 14/16] ima: Use mac_admin_ns_capable() to check corresponding capability Stefan Berger
2021-12-09  7:22   ` Denis Semakin
2021-12-09 13:23     ` James Bottomley
2021-12-09  8:09   ` Denis Semakin
2021-12-11 15:02     ` Serge E. Hallyn
2021-12-11 15:38       ` Stefan Berger
2021-12-11 16:00         ` James Bottomley
2021-12-08 22:18 ` [PATCH v5 15/16] ima: Move dentries into ima_namespace Stefan Berger
2021-12-09 14:34   ` Christian Brauner
2021-12-09 14:37     ` Christian Brauner
2021-12-09 14:41       ` Christian Brauner
2021-12-09 15:00         ` Stefan Berger
2021-12-09 15:47           ` Christian Brauner
2021-12-09 15:30       ` James Bottomley
2021-12-09 19:38         ` James Bottomley
2021-12-09 20:13           ` Stefan Berger
2021-12-10 11:49           ` Christian Brauner
2021-12-10 12:09             ` Mimi Zohar
2021-12-10 12:40               ` Stefan Berger
2021-12-10 13:02                 ` Mimi Zohar
2021-12-10 14:17                   ` Stefan Berger
2021-12-10 14:26                     ` James Bottomley
2021-12-10 15:26                       ` Mimi Zohar
2021-12-10 15:32                         ` Stefan Berger
2021-12-10 15:48                           ` Mimi Zohar
2021-12-10 16:40                             ` Stefan Berger
2021-12-10 12:40               ` James Bottomley
2021-12-10 12:54                 ` Mimi Zohar
2021-12-12 14:13             ` James Bottomley
2021-12-13 11:25               ` Christian Brauner
2021-12-08 22:18 ` [PATCH v5 16/16] ima: Setup securityfs for IMA namespace Stefan Berger

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).