From: Krzysztof Struczynski <[email protected]>
IMA has not been designed to work with containers. It handles every
process in the same way, and it cannot distinguish if a process belongs to
a container or not.
Containers use namespaces to make it appear to the processes in the
containers that they have their own isolated instance of the global
resource. For IMA as well, it is desirable to let processes in the
containers have IMA functionality independent from other containers:
separate policy rules, measurement list, additional appraisal keys to
verify the container image, separate audit logs.
As previous work done in this area, this patch series introduces the IMA
namespace, which is a separate instance of IMA to handle a subset of
processes that belong to a container.
The IMA namespace is created using clone3() or unshare() system calls. It
is important to configure the namespace before any process appears in it,
so that the new policy rules apply to the very first process in the
namespace. To achieve that, the intermediate namespace ima_ns_for_children
is used. It stores the configuration and becomes active on the next fork
or when the first process enters it using the setns() system call. The
similar process is used for the time namespace.
The IMA namespace can be configured using the new securityfs directory
entries that allow the user to set the policy rules, x509 certificate for
appraisal and pass IMA configuration parameters normally included in the
kernel command line parameters. It is intended to extend the clone_args to
allow configuration from clone3() syscall.
To inform other containers about an action made by a given container, a
linked list of IMA namespaces has been implemented. The state/policy of
those containers is evaluated to see if read-write violations (ToMToU,
open-writer) should be recorded in the respective measurement list. Any
change to the files shared across containers is recorded in the namespaced
inode's integrity cache when the file is freed.
To isolate the measurement list and the appraisal keys, the following
decisions were made:
The measurement list remains global, with the assumption that there is
only one TPM in the system. Each IMA namespace has a unique ID, that
allows to track measurements per IMA namespace. Processes in one
namespace, have access only to the measurements from that namespace. The
exception is made for the initial IMA namespace, whose processes have
access to all entries.
The appraisal keys of all IMA namespaces are stored in the IMA system
keyring. Each key is linked to the respective IMA namespace using the key
domain tag. The process that belongs to one IMA namespace, cannot
add/replace/modify a key that belongs to another IMA namespace.
To give access to the IMA securityfs directory entries to the container's
owner, read and write (when needed) permissions are given to the "other"
users not in the file's group 'o'. The access to the files is controlled
by IMA, and given only to the user that has SYS_ADMIN capabilities in the
user namespace owning the IMA namespace. The processes from one IMA
namespace have access to the data from that namespace only. This mechanism
can be changed in the future. The one alternative is to create per IMA
namespace entries, similar to the AppArmour file system.
This work is inspired by Stefan Berger's, Mehmet Kayaalp's, Yuqiong Sun's
and Mimi Zohar's series of patches:
https://lore.kernel.org/patchwork/cover/899419/
Patches are logically divided into 5 groups. That order is not yet
reflected in the commit order. This will be fixed as soon as possible.
1. Base mechanism for the IMA namespace; patches: 1-3, 6-8, 15
Add a new IMA namespace. Add a new CLONE_NEWIMA flag. Create and
configure IMA analogously to the time namespace, using the intermediate
ima_ns_for_children. Add the IMA namespace to the IMA subsystem API.
Create a list of active IMA namespaces.
Add a reader counter to the integrity inode data to detect violations
correctly.
2. Policy; patches: 4, 5, 9, 18, 20-22
Replace global policy data with the per IMA namespace policies.
Record read-write violations (ToMToU, open-writer) across namespaces.
Record modifications to the files shared across containers when the
files are freed.
Set the owning user namespace of the IMA namespace, to the user
namespace of the first process born into the new IMA namespace.
Remap IDs in the policy rules, if the rules were loaded before the user
namespace mapping was defined.
3. IMA-measurement; patches: 10-14, 17, 29
Link measurement list entries to the respective IMA namespaces using
the IMA namespace ID. Include the namespace ID in the digest entry
lookup.
Add a new measurement list template that includes IMA namespace ID.
Add a dummy boot aggregate entry for non-root IMA namespaces.
Show the measurement list data only for the IMA namespace the process
belongs to, unless it is the root IMA namespace.
4. IMA-appraisal; patches: 23-28
Modify keyring search mechanism to include the key domain tag in the
search criteria for both, direct lookup and the iterative search. Allow
to set the key domain tag separately from the key type using the
KEY_ALLOC* flags.
Add the key domain to the IMA namespace, so that the key is linked to
the namespace.
Add the key domain tag to the integrity module's API. Use the new API
to load the IMA namespace's key to the system IMA keyring.
5. Configuration; patches: 16, 19, 30
Add the new entries in the IMA securityfs directory. Parse and validate
configuration data. Apply the new configuration when the first process
is born in the new IMA namespace.
Extend read/write permissions to the IMA securityfs entries to the
other users not in the file's group 'o'. Allow access only to the users
that have the SYS_ADMIN caps in the user namespace owning the given IMA
namespace.
Krzysztof Struczynski (30):
ima: Introduce ima namespace
ima: Add a list of the installed ima namespaces
ima: Bind ima namespace to the file descriptor
ima: Add ima policy related data to the ima namespace
ima: Add methods for parsing ima policy configuration string
ima: Add ima namespace to the ima subsystem APIs
ima: Extend the APIs in the integrity subsystem
ima: Add integrity inode related data to the ima namespace
ima: Enable per ima namespace policy settings
ima: Add ima namespace ID to the ima ML related structures
ima: Keep track of the measurment list per ima namespace
ima: Check ima namespace ID during digest entry lookup
ima: Add a new ima template that includes namespace ID
ima: Add per namespace view of the measurement list
ima: Add a reader counter to the integrity inode data
ima: Extend permissions to the ima securityfs entries
ima: Add the violation counter to the namespace
ima: Change the owning user namespace of the ima namespace if
necessary
ima: Configure the new ima namespace from securityfs
ima: Parse per ima namespace policy file
user namespace: Add function that checks if the UID map is defined
ima: Remap IDs of subject based rules if necessary
keys: Add domain tag to the keyring search criteria
keys: Include key domain tag in the iterative search
keys: Allow to set key domain tag separately from the key type
ima: Add key domain to the ima namespace
integrity: Add key domain tag to the search criteria
ima: Load per ima namespace x509 certificate
ima: Add dummy boot aggregate to per ima namespace measurement list
ima: Set ML template per ima namespace
crypto/asymmetric_keys/asymmetric_type.c | 20 +-
fs/file_table.c | 6 +-
fs/proc/namespaces.c | 4 +
include/linux/digsig.h | 11 +-
include/linux/fs.h | 3 +
include/linux/ima.h | 99 ++-
include/linux/integrity.h | 31 +
include/linux/key-type.h | 1 +
include/linux/key.h | 27 +-
include/linux/nsproxy.h | 3 +
include/linux/proc_ns.h | 5 +-
include/linux/user_namespace.h | 7 +
include/uapi/linux/sched.h | 1 +
init/Kconfig | 12 +
kernel/fork.c | 24 +-
kernel/kexec_file.c | 7 +
kernel/nsproxy.c | 34 +-
kernel/ucount.c | 1 +
kernel/user_namespace.c | 11 +
lib/digsig.c | 11 +-
security/integrity/digsig.c | 46 +-
security/integrity/digsig_asymmetric.c | 20 +-
security/integrity/iint.c | 126 +++-
security/integrity/ima/Makefile | 1 +
security/integrity/ima/ima.h | 134 +++-
security/integrity/ima/ima_api.c | 31 +-
security/integrity/ima/ima_appraise.c | 105 ++-
security/integrity/ima/ima_asymmetric_keys.c | 12 +-
security/integrity/ima/ima_fs.c | 272 ++++++-
security/integrity/ima/ima_init.c | 47 +-
security/integrity/ima/ima_kexec.c | 4 +-
security/integrity/ima/ima_main.c | 323 +++++++--
security/integrity/ima/ima_ns.c | 718 +++++++++++++++++++
security/integrity/ima/ima_policy.c | 397 +++++++---
security/integrity/ima/ima_queue.c | 69 +-
security/integrity/ima/ima_queue_keys.c | 11 +-
security/integrity/ima/ima_template.c | 44 +-
security/integrity/ima/ima_template_lib.c | 13 +
security/integrity/ima/ima_template_lib.h | 2 +
security/integrity/integrity.h | 42 +-
security/keys/key.c | 20 +
security/keys/keyring.c | 25 +-
security/security.c | 2 +-
43 files changed, 2405 insertions(+), 377 deletions(-)
create mode 100644 security/integrity/ima/ima_ns.c
base-commit: fc80c51fd4b23ec007e88d4c688f2cac1b8648e7
prerequisite-patch-id: 409c4abf4ee18e7d43eda995ff0db7879d3d0f5c
--
2.20.1
From: Krzysztof Struczynski <[email protected]>
IMA namespace wraps global ima resources in an abstraction, to enable ima
to work with the containers. Currently, ima namespace contains no useful
data but a dummy interface. IMA resources related to different aspects of
IMA, namely IMA-audit, IMA-measurement, IMA-appraisal will be added in the
following patches.
The way how ima namespace is created is analogous to the time namespace:
unshare(CLONE_NEWIMA) system call creates a new ima namespace but doesn't
assign it to the current process. All children of the process will be born
in the new ima namespace, or a process can use setns() system call to join
the new ima namespace. Call to clone3(CLONE_NEWIMA) system call creates a
new namespace, which the new process joins instantly.
This scheme, allows to configure the new ima namespace before any process
appears in it. If user initially unshares the new ima namespace, ima can
be configured using ima entries in the securityfs. If user calls clone3()
system call directly, the new ima namespace can be configured using clone
arguments. To allow this, new securityfs entries have to be added, and
structures clone_args and kernel_clone_args have to be extended.
Early configuration is crucial. The new ima polices must apply to the
first process in the new namespace, and the appraisal key has to be loaded
beforehand.
Add a new CONFIG_IMA_NS option to the kernel configuration, that enables
one to create a new IMA namespace. IMA namespace functionality is disabled
by default.
Signed-off-by: Krzysztof Struczynski <[email protected]>
---
fs/proc/namespaces.c | 4 +
include/linux/ima.h | 57 ++++++++
include/linux/nsproxy.h | 3 +
include/linux/proc_ns.h | 5 +-
include/linux/user_namespace.h | 1 +
include/uapi/linux/sched.h | 1 +
init/Kconfig | 12 ++
kernel/fork.c | 24 +++-
kernel/nsproxy.c | 34 ++++-
kernel/ucount.c | 1 +
security/integrity/ima/Makefile | 1 +
security/integrity/ima/ima.h | 13 ++
security/integrity/ima/ima_init.c | 13 ++
security/integrity/ima/ima_ns.c | 232 ++++++++++++++++++++++++++++++
14 files changed, 392 insertions(+), 9 deletions(-)
create mode 100644 security/integrity/ima/ima_ns.c
diff --git a/fs/proc/namespaces.c b/fs/proc/namespaces.c
index 8e159fc78c0a..117812a59e5d 100644
--- a/fs/proc/namespaces.c
+++ b/fs/proc/namespaces.c
@@ -37,6 +37,10 @@ static const struct proc_ns_operations *ns_entries[] = {
&timens_operations,
&timens_for_children_operations,
#endif
+#ifdef CONFIG_IMA_NS
+ &imans_operations,
+ &imans_for_children_operations,
+#endif
};
static const char *proc_ns_get_link(struct dentry *dentry,
diff --git a/include/linux/ima.h b/include/linux/ima.h
index d15100de6cdd..4a9c29d4d056 100644
--- a/include/linux/ima.h
+++ b/include/linux/ima.h
@@ -12,6 +12,9 @@
#include <linux/kexec.h>
struct linux_binprm;
+struct nsproxy;
+struct task_struct;
+
#ifdef CONFIG_IMA
extern int ima_bprm_check(struct linux_binprm *bprm);
extern int ima_file_check(struct file *file, int mask);
@@ -167,4 +170,58 @@ static inline bool ima_appraise_signature(enum kernel_read_file_id func)
return false;
}
#endif /* CONFIG_IMA_APPRAISE && CONFIG_INTEGRITY_TRUSTED_KEYRING */
+
+struct ima_namespace {
+ struct kref kref;
+ struct ns_common ns;
+ struct ucounts *ucounts;
+ struct user_namespace *user_ns;
+} __randomize_layout;
+
+extern struct ima_namespace init_ima_ns;
+
+#ifdef CONFIG_IMA_NS
+struct ima_namespace *copy_ima_ns(unsigned long flags,
+ struct user_namespace *user_ns,
+ struct ima_namespace *old_ns);
+
+void free_ima_ns(struct kref *kref);
+
+int imans_on_fork(struct nsproxy *nsproxy, struct task_struct *tsk);
+
+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 ima_namespace *ns)
+{
+ if (ns)
+ kref_put(&ns->kref, free_ima_ns);
+}
+
+#else
+static inline struct ima_namespace *copy_ima_ns(unsigned long flags,
+ struct user_namespace *user_ns,
+ struct ima_namespace *old_ns)
+{
+ return old_ns;
+}
+
+static inline int imans_on_fork(struct nsproxy *nsproxy,
+ struct task_struct *tsk)
+{
+ return 0;
+}
+
+static inline struct ima_namespace *get_ima_ns(struct ima_namespace *ns)
+{
+ return ns;
+}
+
+static inline void put_ima_ns(struct ima_namespace *ns)
+{
+}
+#endif /* CONFIG_IMA_NS */
#endif /* _LINUX_IMA_H */
diff --git a/include/linux/nsproxy.h b/include/linux/nsproxy.h
index cdb171efc7cb..56216a94009d 100644
--- a/include/linux/nsproxy.h
+++ b/include/linux/nsproxy.h
@@ -10,6 +10,7 @@ struct uts_namespace;
struct ipc_namespace;
struct pid_namespace;
struct cgroup_namespace;
+struct ima_namespace;
struct fs_struct;
/*
@@ -38,6 +39,8 @@ struct nsproxy {
struct time_namespace *time_ns;
struct time_namespace *time_ns_for_children;
struct cgroup_namespace *cgroup_ns;
+ struct ima_namespace *ima_ns;
+ struct ima_namespace *ima_ns_for_children;
};
extern struct nsproxy init_nsproxy;
diff --git a/include/linux/proc_ns.h b/include/linux/proc_ns.h
index 75807ecef880..93735b7bbb65 100644
--- a/include/linux/proc_ns.h
+++ b/include/linux/proc_ns.h
@@ -16,7 +16,7 @@ struct inode;
struct proc_ns_operations {
const char *name;
const char *real_ns_name;
- int type;
+ unsigned long type;
struct ns_common *(*get)(struct task_struct *task);
void (*put)(struct ns_common *ns);
int (*install)(struct nsset *nsset, struct ns_common *ns);
@@ -34,6 +34,8 @@ extern const struct proc_ns_operations mntns_operations;
extern const struct proc_ns_operations cgroupns_operations;
extern const struct proc_ns_operations timens_operations;
extern const struct proc_ns_operations timens_for_children_operations;
+extern const struct proc_ns_operations imans_operations;
+extern const struct proc_ns_operations imans_for_children_operations;
/*
* We always define these enumerators
@@ -46,6 +48,7 @@ enum {
PROC_PID_INIT_INO = 0xEFFFFFFCU,
PROC_CGROUP_INIT_INO = 0xEFFFFFFBU,
PROC_TIME_INIT_INO = 0xEFFFFFFAU,
+ PROC_IMA_INIT_INO = 0xEFFFFFF9U,
};
#ifdef CONFIG_PROC_FS
diff --git a/include/linux/user_namespace.h b/include/linux/user_namespace.h
index 6ef1c7109fc4..d9759c54fead 100644
--- a/include/linux/user_namespace.h
+++ b/include/linux/user_namespace.h
@@ -46,6 +46,7 @@ enum ucount_type {
UCOUNT_MNT_NAMESPACES,
UCOUNT_CGROUP_NAMESPACES,
UCOUNT_TIME_NAMESPACES,
+ UCOUNT_IMA_NAMESPACES,
#ifdef CONFIG_INOTIFY_USER
UCOUNT_INOTIFY_INSTANCES,
UCOUNT_INOTIFY_WATCHES,
diff --git a/include/uapi/linux/sched.h b/include/uapi/linux/sched.h
index 3bac0a8ceab2..b30e27efee92 100644
--- a/include/uapi/linux/sched.h
+++ b/include/uapi/linux/sched.h
@@ -36,6 +36,7 @@
/* Flags for the clone3() syscall. */
#define CLONE_CLEAR_SIGHAND 0x100000000ULL /* Clear any signal handler and reset to SIG_DFL. */
#define CLONE_INTO_CGROUP 0x200000000ULL /* Clone into a specific cgroup given the right permissions. */
+#define CLONE_NEWIMA 0x400000000ULL /* New IMA namespace. */
/*
* cloning flags intersect with CSIGNAL so can be used with unshare and clone3
diff --git a/init/Kconfig b/init/Kconfig
index d6a0b31b13dc..f188b33588a2 100644
--- a/init/Kconfig
+++ b/init/Kconfig
@@ -1184,6 +1184,18 @@ 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 IMA
+ default n
+ help
+ This allows container engines to use ima namespaces to provide
+ different IMA policy rules for different containers. Also, it allows
+ to create what appear to be multiple instances of the IMA measurement
+ list and other IMA related resources.
+
+ If unsure, say N.
+
endif # NAMESPACES
config CHECKPOINT_RESTORE
diff --git a/kernel/fork.c b/kernel/fork.c
index 35e9894d394c..b977fd92fe3f 100644
--- a/kernel/fork.c
+++ b/kernel/fork.c
@@ -1876,11 +1876,24 @@ static __latent_entropy struct task_struct *copy_process(
}
/*
- * If the new process will be in a different time namespace
- * do not allow it to share VM or a thread group with the forking task.
+ * If the new process will be in a different time namespace or a
+ * different ima namespace, do not allow it to share VM or a thread
+ * group with the forking task.
*/
if (clone_flags & (CLONE_THREAD | CLONE_VM)) {
- if (nsp->time_ns != nsp->time_ns_for_children)
+ if ((nsp->time_ns != nsp->time_ns_for_children) ||
+ ((clone_flags & CLONE_NEWIMA) ||
+ (nsp->ima_ns != nsp->ima_ns_for_children)))
+ return ERR_PTR(-EINVAL);
+ }
+
+ /*
+ * If the new process will be in a different ima namespace
+ * do not allow it to share the same file descriptor table.
+ */
+ if (clone_flags & CLONE_FILES) {
+ if ((clone_flags & CLONE_NEWIMA) ||
+ (nsp->ima_ns != nsp->ima_ns_for_children))
return ERR_PTR(-EINVAL);
}
@@ -2649,7 +2662,8 @@ static bool clone3_args_valid(struct kernel_clone_args *kargs)
{
/* Verify that no unknown flags are passed along. */
if (kargs->flags &
- ~(CLONE_LEGACY_FLAGS | CLONE_CLEAR_SIGHAND | CLONE_INTO_CGROUP))
+ ~(CLONE_LEGACY_FLAGS |
+ CLONE_CLEAR_SIGHAND | CLONE_INTO_CGROUP | CLONE_NEWIMA))
return false;
/*
@@ -2796,7 +2810,7 @@ static int check_unshare_flags(unsigned long unshare_flags)
CLONE_VM|CLONE_FILES|CLONE_SYSVSEM|
CLONE_NEWUTS|CLONE_NEWIPC|CLONE_NEWNET|
CLONE_NEWUSER|CLONE_NEWPID|CLONE_NEWCGROUP|
- CLONE_NEWTIME))
+ CLONE_NEWTIME|CLONE_NEWIMA))
return -EINVAL;
/*
* Not implemented, but pretend it works if there is nothing
diff --git a/kernel/nsproxy.c b/kernel/nsproxy.c
index 12dd41b39a7f..791efffd7a03 100644
--- a/kernel/nsproxy.c
+++ b/kernel/nsproxy.c
@@ -19,6 +19,7 @@
#include <net/net_namespace.h>
#include <linux/ipc_namespace.h>
#include <linux/time_namespace.h>
+#include <linux/ima.h>
#include <linux/fs_struct.h>
#include <linux/proc_fs.h>
#include <linux/proc_ns.h>
@@ -47,6 +48,10 @@ struct nsproxy init_nsproxy = {
.time_ns = &init_time_ns,
.time_ns_for_children = &init_time_ns,
#endif
+#ifdef CONFIG_IMA_NS
+ .ima_ns = &init_ima_ns,
+ .ima_ns_for_children = &init_ima_ns,
+#endif
};
static inline struct nsproxy *create_nsproxy(void)
@@ -121,8 +126,19 @@ static struct nsproxy *create_new_namespaces(unsigned long flags,
}
new_nsp->time_ns = get_time_ns(tsk->nsproxy->time_ns);
+ new_nsp->ima_ns_for_children = copy_ima_ns(flags, user_ns,
+ tsk->nsproxy->ima_ns_for_children);
+ if (IS_ERR(new_nsp->ima_ns_for_children)) {
+ err = PTR_ERR(new_nsp->ima_ns_for_children);
+ goto out_ima;
+ }
+ new_nsp->ima_ns = get_ima_ns(tsk->nsproxy->ima_ns);
+
return new_nsp;
+out_ima:
+ put_time_ns(new_nsp->time_ns);
+ put_time_ns(new_nsp->time_ns_for_children);
out_time:
put_net(new_nsp->net_ns);
out_net:
@@ -157,8 +173,10 @@ int copy_namespaces(unsigned long flags, struct task_struct *tsk)
if (likely(!(flags & (CLONE_NEWNS | CLONE_NEWUTS | CLONE_NEWIPC |
CLONE_NEWPID | CLONE_NEWNET |
- CLONE_NEWCGROUP | CLONE_NEWTIME)))) {
- if (likely(old_ns->time_ns_for_children == old_ns->time_ns)) {
+ CLONE_NEWCGROUP | CLONE_NEWTIME |
+ CLONE_NEWIMA)))) {
+ if (likely((old_ns->time_ns_for_children == old_ns->time_ns) &&
+ (old_ns->ima_ns_for_children == old_ns->ima_ns))) {
get_nsproxy(old_ns);
return 0;
}
@@ -186,6 +204,12 @@ int copy_namespaces(unsigned long flags, struct task_struct *tsk)
return ret;
}
+ ret = imans_on_fork(new_ns, tsk);
+ if (ret) {
+ free_nsproxy(new_ns);
+ return ret;
+ }
+
tsk->nsproxy = new_ns;
return 0;
}
@@ -204,6 +228,10 @@ void free_nsproxy(struct nsproxy *ns)
put_time_ns(ns->time_ns);
if (ns->time_ns_for_children)
put_time_ns(ns->time_ns_for_children);
+ if (ns->ima_ns)
+ put_ima_ns(ns->ima_ns);
+ if (ns->ima_ns_for_children)
+ put_ima_ns(ns->ima_ns_for_children);
put_cgroup_ns(ns->cgroup_ns);
put_net(ns->net_ns);
kmem_cache_free(nsproxy_cachep, ns);
@@ -221,7 +249,7 @@ int unshare_nsproxy_namespaces(unsigned long unshare_flags,
if (!(unshare_flags & (CLONE_NEWNS | CLONE_NEWUTS | CLONE_NEWIPC |
CLONE_NEWNET | CLONE_NEWPID | CLONE_NEWCGROUP |
- CLONE_NEWTIME)))
+ CLONE_NEWTIME | CLONE_NEWIMA)))
return 0;
user_ns = new_cred ? new_cred->user_ns : current_user_ns();
diff --git a/kernel/ucount.c b/kernel/ucount.c
index 11b1596e2542..3f4768d62b8f 100644
--- a/kernel/ucount.c
+++ b/kernel/ucount.c
@@ -70,6 +70,7 @@ static struct ctl_table user_table[] = {
UCOUNT_ENTRY("max_mnt_namespaces"),
UCOUNT_ENTRY("max_cgroup_namespaces"),
UCOUNT_ENTRY("max_time_namespaces"),
+ UCOUNT_ENTRY("max_ima_namespaces"),
#ifdef CONFIG_INOTIFY_USER
UCOUNT_ENTRY("max_inotify_instances"),
UCOUNT_ENTRY("max_inotify_watches"),
diff --git a/security/integrity/ima/Makefile b/security/integrity/ima/Makefile
index 67dabca670e2..d804d93f1a99 100644
--- a/security/integrity/ima/Makefile
+++ b/security/integrity/ima/Makefile
@@ -14,3 +14,4 @@ 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
diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h
index 38043074ce5e..603da5b2db08 100644
--- a/security/integrity/ima/ima.h
+++ b/security/integrity/ima/ima.h
@@ -20,6 +20,7 @@
#include <linux/hash.h>
#include <linux/tpm.h>
#include <linux/audit.h>
+#include <linux/ima.h>
#include <crypto/hash_info.h>
#include "../integrity.h"
@@ -371,6 +372,18 @@ static inline int ima_read_xattr(struct dentry *dentry,
#endif /* CONFIG_IMA_APPRAISE */
+#ifdef CONFIG_IMA_NS
+static inline struct ima_namespace *get_current_ns(void)
+{
+ return current->nsproxy->ima_ns;
+}
+#else
+static inline struct ima_namespace *get_current_ns(void)
+{
+ return &init_ima_ns;
+}
+#endif /* CONFIG_IMA_NS */
+
#ifdef CONFIG_IMA_APPRAISE_MODSIG
int ima_read_modsig(enum ima_hooks func, const void *buf, loff_t buf_len,
struct modsig **modsig);
diff --git a/security/integrity/ima/ima_init.c b/security/integrity/ima/ima_init.c
index 4902fe7bd570..013bbec16849 100644
--- a/security/integrity/ima/ima_init.c
+++ b/security/integrity/ima/ima_init.c
@@ -15,6 +15,9 @@
#include <linux/scatterlist.h>
#include <linux/slab.h>
#include <linux/err.h>
+#include <linux/kref.h>
+#include <linux/proc_ns.h>
+#include <linux/user_namespace.h>
#include "ima.h"
@@ -22,6 +25,16 @@
const char boot_aggregate_name[] = "boot_aggregate";
struct tpm_chip *ima_tpm_chip;
+struct ima_namespace init_ima_ns = {
+ .kref = KREF_INIT(2),
+ .user_ns = &init_user_ns,
+ .ns.inum = PROC_IMA_INIT_INO,
+#ifdef CONFIG_IMA_NS
+ .ns.ops = &imans_operations,
+#endif
+};
+EXPORT_SYMBOL(init_ima_ns);
+
/* Add the boot aggregate to the IMA measurement list and extend
* the PCR register.
*
diff --git a/security/integrity/ima/ima_ns.c b/security/integrity/ima/ima_ns.c
new file mode 100644
index 000000000000..8f5f301406a2
--- /dev/null
+++ b/security/integrity/ima/ima_ns.c
@@ -0,0 +1,232 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2019-2020 Huawei Technologies Duesseldorf GmbH
+ *
+ * Author: Krzysztof Struczynski <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, version 2 of the
+ * License.
+ *
+ * File: ima_ns.c
+ * Functions to manage the IMA namespace.
+ */
+
+#include <linux/export.h>
+#include <linux/ima.h>
+#include <linux/kref.h>
+#include <linux/proc_ns.h>
+#include <linux/slab.h>
+#include <linux/user_namespace.h>
+#include <linux/nsproxy.h>
+#include <linux/sched.h>
+
+#include "ima.h"
+
+static struct ucounts *inc_ima_namespaces(struct user_namespace *ns)
+{
+ return inc_ucount(ns, current_euid(), UCOUNT_IMA_NAMESPACES);
+}
+
+static void dec_ima_namespaces(struct ucounts *ucounts)
+{
+ return dec_ucount(ucounts, UCOUNT_IMA_NAMESPACES);
+}
+
+static struct ima_namespace *ima_ns_alloc(void)
+{
+ struct ima_namespace *ima_ns;
+
+ ima_ns = kzalloc(sizeof(*ima_ns), GFP_KERNEL);
+ if (!ima_ns)
+ return NULL;
+
+ return ima_ns;
+}
+
+/**
+ * Clone a new ns copying an original ima namespace, setting refcount to 1
+ *
+ * @user_ns: User namespace that current task runs in
+ * @old_ns: Old ima namespace to clone
+ * Return: ERR_PTR(-ENOMEM) on error (failure to kmalloc), new ns otherwise
+ */
+static struct ima_namespace *clone_ima_ns(struct user_namespace *user_ns,
+ struct ima_namespace *old_ns)
+{
+ struct ima_namespace *ns;
+ struct ucounts *ucounts;
+ int err;
+
+ err = -ENOSPC;
+ ucounts = inc_ima_namespaces(user_ns);
+ if (!ucounts)
+ goto fail;
+
+ err = -ENOMEM;
+ ns = ima_ns_alloc();
+ if (!ns)
+ goto fail_dec;
+
+ kref_init(&ns->kref);
+
+ err = ns_alloc_inum(&ns->ns);
+ if (err)
+ goto fail_free;
+
+ ns->ns.ops = &imans_operations;
+ ns->user_ns = get_user_ns(user_ns);
+ ns->ucounts = ucounts;
+
+ return ns;
+
+fail_free:
+ kfree(ns);
+fail_dec:
+ dec_ima_namespaces(ucounts);
+fail:
+ return ERR_PTR(err);
+}
+
+/**
+ * Copy task's ima namespace, or clone it if flags specifies CLONE_NEWNS.
+ *
+ * @flags: Cloning flags
+ * @user_ns: User namespace that current task runs in
+ * @old_ns: Old ima namespace to clone
+ *
+ * Return: IMA namespace or ERR_PTR.
+ */
+
+struct ima_namespace *copy_ima_ns(unsigned long flags,
+ struct user_namespace *user_ns,
+ struct ima_namespace *old_ns)
+{
+ if (!(flags & CLONE_NEWIMA))
+ return get_ima_ns(old_ns);
+
+ return clone_ima_ns(user_ns, old_ns);
+}
+
+static void destroy_ima_ns(struct ima_namespace *ns)
+{
+ dec_ima_namespaces(ns->ucounts);
+ put_user_ns(ns->user_ns);
+ ns_free_inum(&ns->ns);
+ kfree(ns);
+}
+
+void free_ima_ns(struct kref *kref)
+{
+ struct ima_namespace *ns;
+
+ ns = container_of(kref, struct ima_namespace, kref);
+
+ destroy_ima_ns(ns);
+}
+
+static inline struct ima_namespace *to_ima_ns(struct ns_common *ns)
+{
+ return container_of(ns, struct ima_namespace, ns);
+}
+
+static struct ns_common *imans_get(struct task_struct *task)
+{
+ struct ima_namespace *ns = NULL;
+ struct nsproxy *nsproxy;
+
+ task_lock(task);
+ nsproxy = task->nsproxy;
+ if (nsproxy) {
+ ns = nsproxy->ima_ns;
+ get_ima_ns(ns);
+ }
+ task_unlock(task);
+
+ return ns ? &ns->ns : NULL;
+}
+
+static struct ns_common *imans_for_children_get(struct task_struct *task)
+{
+ struct ima_namespace *ns = NULL;
+ struct nsproxy *nsproxy;
+
+ task_lock(task);
+ nsproxy = task->nsproxy;
+ if (nsproxy) {
+ ns = nsproxy->ima_ns_for_children;
+ get_ima_ns(ns);
+ }
+ task_unlock(task);
+
+ return ns ? &ns->ns : NULL;
+}
+
+static void imans_put(struct ns_common *ns)
+{
+ put_ima_ns(to_ima_ns(ns));
+}
+
+static int imans_install(struct nsset *nsset, struct ns_common *new)
+{
+ struct nsproxy *nsproxy = nsset->nsproxy;
+ struct ima_namespace *ns = to_ima_ns(new);
+
+ if (!current_is_single_threaded())
+ return -EUSERS;
+
+ if (!ns_capable(ns->user_ns, CAP_SYS_ADMIN) ||
+ !ns_capable(nsset->cred->user_ns, CAP_SYS_ADMIN))
+ return -EPERM;
+
+ get_ima_ns(ns);
+ put_ima_ns(nsproxy->ima_ns);
+ nsproxy->ima_ns = ns;
+
+ get_ima_ns(ns);
+ put_ima_ns(nsproxy->ima_ns_for_children);
+ nsproxy->ima_ns_for_children = ns;
+
+ return 0;
+}
+
+int imans_on_fork(struct nsproxy *nsproxy, struct task_struct *tsk)
+{
+ struct ns_common *nsc = &nsproxy->ima_ns_for_children->ns;
+ struct ima_namespace *ns = to_ima_ns(nsc);
+
+ /* create_new_namespaces() already incremented the ref counter */
+ if (nsproxy->ima_ns == nsproxy->ima_ns_for_children)
+ return 0;
+
+ get_ima_ns(ns);
+ put_ima_ns(nsproxy->ima_ns);
+ nsproxy->ima_ns = ns;
+
+ return 0;
+}
+
+static struct user_namespace *imans_owner(struct ns_common *ns)
+{
+ return to_ima_ns(ns)->user_ns;
+}
+
+const struct proc_ns_operations imans_operations = {
+ .name = "ima",
+ .type = CLONE_NEWIMA,
+ .get = imans_get,
+ .put = imans_put,
+ .install = imans_install,
+ .owner = imans_owner,
+};
+
+const struct proc_ns_operations imans_for_children_operations = {
+ .name = "ima_for_children",
+ .type = CLONE_NEWIMA,
+ .get = imans_for_children_get,
+ .put = imans_put,
+ .install = imans_install,
+ .owner = imans_owner,
+};
+
--
2.20.1
From: Krzysztof Struczynski <[email protected]>
Add ima namespace pointer to the input parameters of the relevant
functions. This is a preparation for the policy namespacing, more
functions may be modified later, when other aspects of the ima are
namespaced.
Signed-off-by: Krzysztof Struczynski <[email protected]>
---
include/linux/ima.h | 4 +--
security/integrity/ima/ima.h | 25 ++++++++-----
security/integrity/ima/ima_api.c | 6 ++--
security/integrity/ima/ima_appraise.c | 16 +++++----
security/integrity/ima/ima_asymmetric_keys.c | 3 +-
security/integrity/ima/ima_fs.c | 2 +-
security/integrity/ima/ima_main.c | 38 +++++++++++---------
security/integrity/ima/ima_policy.c | 36 +++++++++++--------
security/integrity/ima/ima_queue_keys.c | 3 +-
9 files changed, 80 insertions(+), 53 deletions(-)
diff --git a/include/linux/ima.h b/include/linux/ima.h
index d61c9c21ffb9..3fd3746a0dee 100644
--- a/include/linux/ima.h
+++ b/include/linux/ima.h
@@ -140,13 +140,13 @@ static inline void ima_post_key_create_or_update(struct key *keyring,
#endif /* CONFIG_IMA_MEASURE_ASYMMETRIC_KEYS */
#ifdef CONFIG_IMA_APPRAISE
-extern bool is_ima_appraise_enabled(void);
+extern bool is_ima_appraise_enabled(const struct ima_namespace *ima_ns);
extern void ima_inode_post_setattr(struct dentry *dentry);
extern int ima_inode_setxattr(struct dentry *dentry, const char *xattr_name,
const void *xattr_value, size_t xattr_value_len);
extern int ima_inode_removexattr(struct dentry *dentry, const char *xattr_name);
#else
-static inline bool is_ima_appraise_enabled(void)
+static inline bool is_ima_appraise_enabled(const struct ima_namespace *ima_ns)
{
return 0;
}
diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h
index 4872f193f7a3..7d522fdab0d8 100644
--- a/security/integrity/ima/ima.h
+++ b/security/integrity/ima/ima.h
@@ -264,7 +264,8 @@ static inline void ima_process_queued_keys(void) {}
int ima_get_action(struct inode *inode, const struct cred *cred, u32 secid,
int mask, enum ima_hooks func, int *pcr,
struct ima_template_desc **template_desc,
- const char *keyring);
+ const char *keyring,
+ const struct ima_namespace *ima_ns);
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,
@@ -276,7 +277,8 @@ void ima_store_measurement(struct integrity_iint_cache *iint, struct file *file,
struct ima_template_desc *template_desc);
void process_buffer_measurement(struct inode *inode, const void *buf, int size,
const char *eventname, enum ima_hooks func,
- int pcr, const char *keyring);
+ int pcr, const char *keyring,
+ struct ima_namespace *ima_ns);
void ima_audit_measurement(struct integrity_iint_cache *iint,
const unsigned char *filename);
int ima_alloc_init_template(struct ima_event_data *event_data,
@@ -292,15 +294,16 @@ const char *ima_d_path(const struct path *path, char **pathbuf, char *filename);
int ima_match_policy(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 *keyring);
+ const char *keyring,
+ const struct ima_namespace *ima_ns);
void ima_init_policy(void);
void ima_init_ns_policy(struct ima_namespace *ima_ns,
const struct ima_policy_setup_data *policy_setup_data);
void ima_update_policy(void);
-void ima_update_policy_flag(void);
+void ima_update_policy_flag(struct ima_namespace *ima_ns);
ssize_t ima_parse_add_rule(char *);
void ima_delete_rules(void);
-int ima_check_policy(void);
+int ima_check_policy(const struct ima_namespace *ima_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);
@@ -327,13 +330,15 @@ int ima_default_appraise_setup(const char *str,
#ifdef CONFIG_IMA_APPRAISE
int ima_check_blacklist(struct integrity_iint_cache *iint,
- const struct modsig *modsig, int pcr);
+ const struct modsig *modsig, int pcr,
+ struct ima_namespace *ima_ns);
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 inode *inode, int mask, enum ima_hooks func);
+int ima_must_appraise(struct inode *inode, int mask, enum ima_hooks func,
+ struct ima_namespace *ima_ns);
void ima_update_xattr(struct integrity_iint_cache *iint, struct file *file);
enum integrity_status ima_get_cache_status(struct integrity_iint_cache *iint,
enum ima_hooks func);
@@ -344,7 +349,8 @@ int ima_read_xattr(struct dentry *dentry,
#else
static inline int ima_check_blacklist(struct integrity_iint_cache *iint,
- const struct modsig *modsig, int pcr)
+ const struct modsig *modsig, int pcr,
+ struct ima_namespace *ima_ns)
{
return 0;
}
@@ -361,7 +367,8 @@ static inline int ima_appraise_measurement(enum ima_hooks func,
}
static inline int ima_must_appraise(struct inode *inode, int mask,
- enum ima_hooks func)
+ enum ima_hooks func,
+ struct ima_namespace *ima_ns)
{
return 0;
}
diff --git a/security/integrity/ima/ima_api.c b/security/integrity/ima/ima_api.c
index 4f39fb93f278..8b41183200e8 100644
--- a/security/integrity/ima/ima_api.c
+++ b/security/integrity/ima/ima_api.c
@@ -171,6 +171,7 @@ void ima_add_violation(struct file *file, const unsigned char *filename,
* @pcr: pointer filled in if matched measure policy sets pcr=
* @template_desc: pointer filled in if matched measure policy sets template=
* @keyring: keyring name used to determine the action
+ * @ima_ns: ima namespace whose policy data will be used
*
* The policy is defined in terms of keypairs:
* subj=, obj=, type=, func=, mask=, fsmagic=
@@ -186,14 +187,15 @@ void ima_add_violation(struct file *file, const unsigned char *filename,
int ima_get_action(struct inode *inode, const struct cred *cred, u32 secid,
int mask, enum ima_hooks func, int *pcr,
struct ima_template_desc **template_desc,
- const char *keyring)
+ const char *keyring,
+ const struct ima_namespace *ima_ns)
{
int flags = IMA_MEASURE | IMA_AUDIT | IMA_APPRAISE | IMA_HASH;
flags &= ima_policy_flag;
return ima_match_policy(inode, cred, secid, func, mask, flags, pcr,
- template_desc, keyring);
+ template_desc, keyring, ima_ns);
}
/*
diff --git a/security/integrity/ima/ima_appraise.c b/security/integrity/ima/ima_appraise.c
index 0632d3881611..9388ff88ca4d 100644
--- a/security/integrity/ima/ima_appraise.c
+++ b/security/integrity/ima/ima_appraise.c
@@ -48,10 +48,11 @@ __setup("ima_appraise=", default_appraise_setup);
/*
* is_ima_appraise_enabled - return appraise status
+ * @ima_ns: pointer to the ima namespace being checked
*
* Only return enabled, if not in ima_appraise="fix" or "log" modes.
*/
-bool is_ima_appraise_enabled(void)
+bool is_ima_appraise_enabled(const struct ima_namespace *ima_ns)
{
return ima_appraise & IMA_APPRAISE_ENFORCE;
}
@@ -61,7 +62,8 @@ bool is_ima_appraise_enabled(void)
*
* Return 1 to appraise or hash
*/
-int ima_must_appraise(struct inode *inode, int mask, enum ima_hooks func)
+int ima_must_appraise(struct inode *inode, int mask, enum ima_hooks func,
+ struct ima_namespace *ima_ns)
{
u32 secid;
@@ -70,7 +72,8 @@ int ima_must_appraise(struct inode *inode, int mask, enum ima_hooks func)
security_task_getsecid(current, &secid);
return ima_match_policy(inode, current_cred(), secid, func, mask,
- IMA_APPRAISE | IMA_HASH, NULL, NULL, NULL);
+ IMA_APPRAISE | IMA_HASH, NULL, NULL, NULL,
+ NULL);
}
static int ima_fix_xattr(struct dentry *dentry,
@@ -328,7 +331,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,
- const struct modsig *modsig, int pcr)
+ const struct modsig *modsig, int pcr,
+ struct ima_namespace *ima_ns)
{
enum hash_algo hash_algo;
const u8 *digest = NULL;
@@ -345,7 +349,7 @@ int ima_check_blacklist(struct integrity_iint_cache *iint,
if ((rc == -EPERM) && (iint->flags & IMA_MEASURE))
process_buffer_measurement(NULL, digest, digestsize,
"blacklisted-hash", NONE,
- pcr, NULL);
+ pcr, NULL, NULL);
}
return rc;
@@ -511,7 +515,7 @@ void ima_inode_post_setattr(struct dentry *dentry)
|| !(inode->i_opflags & IOP_XATTR))
return;
- action = ima_must_appraise(inode, MAY_ACCESS, POST_SETATTR);
+ action = ima_must_appraise(inode, MAY_ACCESS, POST_SETATTR, NULL);
if (!action)
__vfs_removexattr(dentry, XATTR_NAME_IMA);
iint = integrity_iint_find(inode);
diff --git a/security/integrity/ima/ima_asymmetric_keys.c b/security/integrity/ima/ima_asymmetric_keys.c
index 1c68c500c26f..58aa56b0422d 100644
--- a/security/integrity/ima/ima_asymmetric_keys.c
+++ b/security/integrity/ima/ima_asymmetric_keys.c
@@ -60,5 +60,6 @@ void ima_post_key_create_or_update(struct key *keyring, struct key *key,
*/
process_buffer_measurement(NULL, payload, payload_len,
keyring->description, KEY_CHECK, 0,
- keyring->description);
+ keyring->description,
+ NULL);
}
diff --git a/security/integrity/ima/ima_fs.c b/security/integrity/ima/ima_fs.c
index e3fcad871861..97aadee7e68e 100644
--- a/security/integrity/ima/ima_fs.c
+++ b/security/integrity/ima/ima_fs.c
@@ -410,7 +410,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(NULL) < 0) {
cause = "failed";
valid_policy = 0;
}
diff --git a/security/integrity/ima/ima_main.c b/security/integrity/ima/ima_main.c
index c7e29277b953..196fa2bd490d 100644
--- a/security/integrity/ima/ima_main.c
+++ b/security/integrity/ima/ima_main.c
@@ -109,7 +109,8 @@ static void ima_rdwr_violation_check(struct file *file,
int must_measure,
char **pathbuf,
const char **pathname,
- char *filename)
+ char *filename,
+ struct ima_namespace *ima_ns)
{
struct inode *inode = file_inode(file);
fmode_t mode = file->f_mode;
@@ -215,7 +216,8 @@ void ima_file_free(struct file *file)
static int process_measurement(struct file *file, const struct cred *cred,
u32 secid, char *buf, loff_t size, int mask,
- enum ima_hooks func)
+ enum ima_hooks func,
+ struct ima_namespace *ima_ns)
{
struct inode *inode = file_inode(file);
struct integrity_iint_cache *iint = NULL;
@@ -239,7 +241,7 @@ static int process_measurement(struct file *file, const struct cred *cred,
* Included is the appraise submask.
*/
action = ima_get_action(inode, cred, secid, mask, func, &pcr,
- &template_desc, NULL);
+ &template_desc, NULL, ima_ns);
violation_check = ((func == FILE_CHECK || func == MMAP_CHECK) &&
(ima_policy_flag & IMA_MEASURE));
if (!action && !violation_check)
@@ -261,7 +263,7 @@ static int process_measurement(struct file *file, const struct cred *cred,
if (!rc && violation_check)
ima_rdwr_violation_check(file, iint, action & IMA_MEASURE,
- &pathbuf, &pathname, filename);
+ &pathbuf, &pathname, filename, ima_ns);
inode_unlock(inode);
@@ -359,7 +361,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(iint, modsig, pcr, ima_ns);
if (rc != -EPERM) {
inode_lock(inode);
rc = ima_appraise_measurement(func, iint, file,
@@ -413,7 +415,7 @@ int ima_file_mmap(struct file *file, unsigned long prot)
if (file && (prot & PROT_EXEC)) {
security_task_getsecid(current, &secid);
return process_measurement(file, current_cred(), secid, NULL,
- 0, MAY_EXEC, MMAP_CHECK);
+ 0, MAY_EXEC, MMAP_CHECK, NULL);
}
return 0;
@@ -452,7 +454,7 @@ int ima_file_mprotect(struct vm_area_struct *vma, unsigned long prot)
security_task_getsecid(current, &secid);
inode = file_inode(vma->vm_file);
action = ima_get_action(inode, current_cred(), secid, MAY_EXEC,
- MMAP_CHECK, &pcr, &template, 0);
+ MMAP_CHECK, &pcr, &template, 0, NULL);
/* Is the mmap'ed file in policy? */
if (!(action & (IMA_MEASURE | IMA_APPRAISE_SUBMASK)))
@@ -491,13 +493,13 @@ int ima_bprm_check(struct linux_binprm *bprm)
security_task_getsecid(current, &secid);
ret = process_measurement(bprm->file, current_cred(), secid, NULL, 0,
- MAY_EXEC, BPRM_CHECK);
+ MAY_EXEC, BPRM_CHECK, NULL);
if (ret)
return ret;
security_cred_getsecid(bprm->cred, &secid);
return process_measurement(bprm->file, bprm->cred, secid, NULL, 0,
- MAY_EXEC, CREDS_CHECK);
+ MAY_EXEC, CREDS_CHECK, NULL);
}
/**
@@ -517,7 +519,7 @@ int ima_file_check(struct file *file, int mask)
security_task_getsecid(current, &secid);
return process_measurement(file, current_cred(), secid, NULL, 0,
mask & (MAY_READ | MAY_WRITE | MAY_EXEC |
- MAY_APPEND), FILE_CHECK);
+ MAY_APPEND), FILE_CHECK, NULL);
}
EXPORT_SYMBOL_GPL(ima_file_check);
@@ -583,7 +585,7 @@ void ima_post_create_tmpfile(struct inode *inode)
struct integrity_iint_cache *iint;
int must_appraise;
- must_appraise = ima_must_appraise(inode, MAY_ACCESS, FILE_CHECK);
+ must_appraise = ima_must_appraise(inode, MAY_ACCESS, FILE_CHECK, NULL);
if (!must_appraise)
return;
@@ -610,7 +612,7 @@ void ima_post_path_mknod(struct dentry *dentry)
struct inode *inode = dentry->d_inode;
int must_appraise;
- must_appraise = ima_must_appraise(inode, MAY_ACCESS, FILE_CHECK);
+ must_appraise = ima_must_appraise(inode, MAY_ACCESS, FILE_CHECK, NULL);
if (!must_appraise)
return;
@@ -697,7 +699,7 @@ int ima_post_read_file(struct file *file, void *buf, loff_t size,
func = read_idmap[read_id] ?: FILE_CHECK;
security_task_getsecid(current, &secid);
return process_measurement(file, current_cred(), secid, buf, size,
- MAY_READ, func);
+ MAY_READ, func, NULL);
}
/**
@@ -764,7 +766,8 @@ int ima_load_data(enum kernel_load_data_id id)
*/
void process_buffer_measurement(struct inode *inode, const void *buf, int size,
const char *eventname, enum ima_hooks func,
- int pcr, const char *keyring)
+ int pcr, const char *keyring,
+ struct ima_namespace *ima_ns)
{
int ret = 0;
const char *audit_cause = "ENOMEM";
@@ -796,7 +799,7 @@ void process_buffer_measurement(struct inode *inode, const void *buf, int size,
if (func) {
security_task_getsecid(current, &secid);
action = ima_get_action(inode, current_cred(), secid, 0, func,
- &pcr, &template, keyring);
+ &pcr, &template, keyring, NULL);
if (!(action & IMA_MEASURE))
return;
}
@@ -868,7 +871,8 @@ void ima_kexec_cmdline(int kernel_fd, const void *buf, int size)
return;
process_buffer_measurement(file_inode(f.file), buf, size,
- "kexec-cmdline", KEXEC_CMDLINE, 0, NULL);
+ "kexec-cmdline", KEXEC_CMDLINE, 0, NULL,
+ NULL);
fdput(f);
}
@@ -897,7 +901,7 @@ static int __init init_ima(void)
pr_warn("Couldn't register LSM notifier, error %d\n", error);
if (!error)
- ima_update_policy_flag();
+ ima_update_policy_flag(&init_ima_ns);
return error;
}
diff --git a/security/integrity/ima/ima_policy.c b/security/integrity/ima/ima_policy.c
index 403854b18ef2..12f9dcf73c83 100644
--- a/security/integrity/ima/ima_policy.c
+++ b/security/integrity/ima/ima_policy.c
@@ -598,6 +598,7 @@ static int get_subaction(struct ima_rule_entry *rule, enum ima_hooks func)
* @template_desc: the template that should be used for this rule
* @keyring: the keyring name, if given, to be used to check in the policy.
* keyring can be NULL if func is anything other than KEY_CHECK.
+ * @ima_ns: IMA namespace whose policies are being checked
*
* Measure decision based on func/mask/fsmagic and LSM(subj/obj/type)
* conditions.
@@ -609,7 +610,8 @@ static int get_subaction(struct ima_rule_entry *rule, enum ima_hooks func)
int ima_match_policy(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 *keyring)
+ const char *keyring,
+ const struct ima_namespace *ima_ns)
{
struct ima_rule_entry *entry;
int action = 0, actmask = flags | (flags << 1);
@@ -662,8 +664,9 @@ int ima_match_policy(struct inode *inode, const struct cred *cred, u32 secid,
* loaded policy. Based on this flag, the decision to short circuit
* out of a function or not call the function in the first place
* can be made earlier.
+ * @ima_ns: pointer to the ima namespace whose policy flag is updated
*/
-void ima_update_policy_flag(void)
+void ima_update_policy_flag(struct ima_namespace *ima_ns)
{
struct ima_rule_entry *entry;
@@ -690,7 +693,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_policy_data *policy_data,
+ struct ima_rule_entry *entries, int count,
enum policy_rule_list policy_rule)
{
int i = 0;
@@ -790,17 +794,18 @@ void __init ima_init_policy(void)
/* if !ima_policy, we load NO default rules */
if (ima_policy)
- add_rules(dont_measure_rules, ARRAY_SIZE(dont_measure_rules),
+ add_rules(NULL,
+ dont_measure_rules, ARRAY_SIZE(dont_measure_rules),
IMA_DEFAULT_POLICY);
switch (ima_policy) {
case ORIGINAL_TCB:
- add_rules(original_measurement_rules,
+ add_rules(NULL, original_measurement_rules,
ARRAY_SIZE(original_measurement_rules),
IMA_DEFAULT_POLICY);
break;
case DEFAULT_TCB:
- add_rules(default_measurement_rules,
+ add_rules(NULL, default_measurement_rules,
ARRAY_SIZE(default_measurement_rules),
IMA_DEFAULT_POLICY);
default:
@@ -817,7 +822,7 @@ void __init ima_init_policy(void)
if (!arch_entries)
pr_info("No architecture policies found\n");
else
- add_rules(arch_policy_entry, arch_entries,
+ add_rules(NULL, arch_policy_entry, arch_entries,
IMA_DEFAULT_POLICY | IMA_CUSTOM_POLICY);
/*
@@ -825,7 +830,8 @@ 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(NULL,
+ secure_boot_rules, ARRAY_SIZE(secure_boot_rules),
IMA_DEFAULT_POLICY);
/*
@@ -837,23 +843,25 @@ 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(NULL,
+ build_appraise_rules, build_appraise_entries,
IMA_CUSTOM_POLICY);
else
- add_rules(build_appraise_rules, build_appraise_entries,
+ add_rules(NULL,
+ build_appraise_rules, build_appraise_entries,
IMA_DEFAULT_POLICY | IMA_CUSTOM_POLICY);
}
if (ima_use_appraise_tcb)
- add_rules(default_appraise_rules,
+ add_rules(NULL, default_appraise_rules,
ARRAY_SIZE(default_appraise_rules),
IMA_DEFAULT_POLICY);
- ima_update_policy_flag();
+ ima_update_policy_flag(NULL);
}
/* Make sure we have a valid policy, at least containing some rules. */
-int ima_check_policy(void)
+int ima_check_policy(const struct ima_namespace *ima_ns)
{
if (list_empty(&ima_temp_rules))
return -EINVAL;
@@ -889,7 +897,7 @@ void ima_update_policy(void)
*/
kfree(arch_policy_entry);
}
- ima_update_policy_flag();
+ ima_update_policy_flag(NULL);
/* Custom IMA policy has been loaded */
ima_process_queued_keys();
diff --git a/security/integrity/ima/ima_queue_keys.c b/security/integrity/ima/ima_queue_keys.c
index 69a8626a35c0..34ca54ba52b7 100644
--- a/security/integrity/ima/ima_queue_keys.c
+++ b/security/integrity/ima/ima_queue_keys.c
@@ -162,7 +162,8 @@ void ima_process_queued_keys(void)
entry->payload_len,
entry->keyring_name,
KEY_CHECK, 0,
- entry->keyring_name);
+ entry->keyring_name,
+ NULL);
list_del(&entry->list);
ima_free_key_entry(entry);
}
--
2.20.1
From: Krzysztof Struczynski <[email protected]>
IMA namespace reference will be required in ima_file_free() to check
the policy and find inode integrity data for the correct ima namespace.
ima_file_free() is called on __fput(), and __fput() may be called after
releasing namespaces in exit_task_namespaces() in do_exit() and
therefore nsproxy reference cannot be used - it is already set to NULL.
This is a preparation for namespacing policy and inode integrity data.
Signed-off-by: Krzysztof Struczynski <[email protected]>
---
fs/file_table.c | 6 +++++-
include/linux/fs.h | 3 +++
include/linux/ima.h | 6 ++++++
security/integrity/ima/ima_main.c | 27 +++++++++++++++++++++++++--
4 files changed, 39 insertions(+), 3 deletions(-)
diff --git a/fs/file_table.c b/fs/file_table.c
index 656647f9575a..878213d8af92 100644
--- a/fs/file_table.c
+++ b/fs/file_table.c
@@ -109,6 +109,8 @@ static struct file *__alloc_file(int flags, const struct cred *cred)
return ERR_PTR(error);
}
+ ima_file_alloc(f);
+
atomic_long_set(&f->f_count, 1);
rwlock_init(&f->f_owner.lock);
spin_lock_init(&f->f_lock);
@@ -259,8 +261,10 @@ static void __fput(struct file *file)
struct inode *inode = file->f_inode;
fmode_t mode = file->f_mode;
- if (unlikely(!(file->f_mode & FMODE_OPENED)))
+ if (unlikely(!(file->f_mode & FMODE_OPENED))) {
+ ima_file_free(file);
goto out;
+ }
might_sleep();
diff --git a/include/linux/fs.h b/include/linux/fs.h
index 407881ebeab1..8d6264755935 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -947,6 +947,9 @@ struct file {
struct address_space *f_mapping;
errseq_t f_wb_err;
errseq_t f_sb_err; /* for syncfs */
+#ifdef CONFIG_IMA
+ void *f_ima;
+#endif
} __randomize_layout
__attribute__((aligned(4))); /* lest something weird decides that 2 is OK */
diff --git a/include/linux/ima.h b/include/linux/ima.h
index 5b6235b97603..3954cef57c00 100644
--- a/include/linux/ima.h
+++ b/include/linux/ima.h
@@ -21,6 +21,7 @@ struct llist_node;
extern int ima_bprm_check(struct linux_binprm *bprm);
extern int ima_file_check(struct file *file, int mask);
extern void ima_post_create_tmpfile(struct inode *inode);
+extern int ima_file_alloc(struct file *file);
extern void ima_file_free(struct file *file);
extern int ima_file_mmap(struct file *file, unsigned long prot);
extern int ima_file_mprotect(struct vm_area_struct *vma, unsigned long prot);
@@ -66,6 +67,11 @@ static inline void ima_post_create_tmpfile(struct inode *inode)
{
}
+static inline int ima_file_alloc(struct file *file)
+{
+ return 0;
+}
+
static inline void ima_file_free(struct file *file)
{
return;
diff --git a/security/integrity/ima/ima_main.c b/security/integrity/ima/ima_main.c
index d800e73c8b62..c7e29277b953 100644
--- a/security/integrity/ima/ima_main.c
+++ b/security/integrity/ima/ima_main.c
@@ -169,6 +169,23 @@ static void ima_check_last_writer(struct integrity_iint_cache *iint,
mutex_unlock(&iint->mutex);
}
+/**
+ * ima_file_alloc - called on __alloc_file()
+ * @file: pointer to file structure being created
+ *
+ * Bind IMA namespace to the file descriptor. This is necessary, because
+ * __fput can be called after exit_task_namespaces() in do_exit().
+ * In that case nsproxy is already NULL and ima ns has to be found
+ * differently in ima_file_free(). If process joins different ima ns, files
+ * opened in the old ns will point to that (old) ns.
+ */
+int ima_file_alloc(struct file *file)
+{
+ file->f_ima = get_current_ns();
+ get_ima_ns((struct ima_namespace *)file->f_ima);
+ return 0;
+}
+
/**
* ima_file_free - called on __fput()
* @file: pointer to file structure being freed
@@ -179,15 +196,21 @@ void ima_file_free(struct file *file)
{
struct inode *inode = file_inode(file);
struct integrity_iint_cache *iint;
+ struct ima_namespace *ima_ns = (struct ima_namespace *)file->f_ima;
+
+ if (unlikely(!(file->f_mode & FMODE_OPENED)))
+ goto out;
if (!ima_policy_flag || !S_ISREG(inode->i_mode))
- return;
+ goto out;
iint = integrity_iint_find(inode);
if (!iint)
- return;
+ goto out;
ima_check_last_writer(iint, inode, file);
+out:
+ put_ima_ns(ima_ns);
}
static int process_measurement(struct file *file, const struct cred *cred,
--
2.20.1
From: Krzysztof Struczynski <[email protected]>
Add an iint tree to the ima namespace. Each namespace should track
operations on its objects separately. Per namespace iint tree is not
yet used, it will be done in the following patches.
Signed-off-by: Krzysztof Struczynski <[email protected]>
---
include/linux/ima.h | 1 +
security/integrity/ima/ima_init.c | 1 +
security/integrity/ima/ima_ns.c | 17 +++++++++++++++--
3 files changed, 17 insertions(+), 2 deletions(-)
diff --git a/include/linux/ima.h b/include/linux/ima.h
index 3fd3746a0dee..9069aafd905f 100644
--- a/include/linux/ima.h
+++ b/include/linux/ima.h
@@ -190,6 +190,7 @@ struct ima_namespace {
atomic_t inactive; /* set only when ns is added to the cleanup list */
bool frozen;
struct ima_policy_data *policy_data;
+ struct integrity_iint_tree *iint_tree;
} __randomize_layout;
extern struct ima_namespace init_ima_ns;
diff --git a/security/integrity/ima/ima_init.c b/security/integrity/ima/ima_init.c
index ea5ff42eb7fe..be1afc42fdf5 100644
--- a/security/integrity/ima/ima_init.c
+++ b/security/integrity/ima/ima_init.c
@@ -34,6 +34,7 @@ struct ima_namespace init_ima_ns = {
#endif
.frozen = true,
.policy_data = &init_policy_data,
+ .iint_tree = &init_iint_tree,
};
EXPORT_SYMBOL(init_ima_ns);
diff --git a/security/integrity/ima/ima_ns.c b/security/integrity/ima/ima_ns.c
index 1aeb9cfeb3a2..226a53279f71 100644
--- a/security/integrity/ima/ima_ns.c
+++ b/security/integrity/ima/ima_ns.c
@@ -56,11 +56,18 @@ static struct ima_namespace *ima_ns_alloc(void)
ima_ns->policy_data = kzalloc(sizeof(struct ima_policy_data),
GFP_KERNEL);
if (!ima_ns->policy_data)
- goto out_free;
+ goto ns_free;
+
+ ima_ns->iint_tree = kzalloc(sizeof(struct integrity_iint_tree),
+ GFP_KERNEL);
+ if (!ima_ns->iint_tree)
+ goto policy_free;
return ima_ns;
-out_free:
+policy_free:
+ kfree(ima_ns->policy_data);
+ns_free:
kfree(ima_ns);
out:
return NULL;
@@ -120,6 +127,9 @@ static struct ima_namespace *clone_ima_ns(struct user_namespace *user_ns,
ns->ucounts = ucounts;
ns->frozen = false;
+ rwlock_init(&ns->iint_tree->lock);
+ ns->iint_tree->root = RB_ROOT;
+
INIT_LIST_HEAD(&ns->policy_data->ima_default_rules);
INIT_LIST_HEAD(&ns->policy_data->ima_policy_rules);
INIT_LIST_HEAD(&ns->policy_data->ima_temp_rules);
@@ -127,6 +137,7 @@ static struct ima_namespace *clone_ima_ns(struct user_namespace *user_ns,
return ns;
fail_free:
+ kfree(ns->iint_tree);
kfree(ns->policy_data);
kfree(ns);
fail_dec:
@@ -173,6 +184,8 @@ static void destroy_ima_ns(struct ima_namespace *ns)
dec_ima_namespaces(ns->ucounts);
put_user_ns(ns->user_ns);
ns_free_inum(&ns->ns);
+ integrity_iint_tree_free(ns->iint_tree);
+ kfree(ns->iint_tree);
kfree(ns->policy_data);
kfree(ns);
}
--
2.20.1
From: Krzysztof Struczynski <[email protected]>
Inode integrity cache will be maintained per ima namespace. Add new
functions that allow to specify the iint tree to use.
Signed-off-by: Krzysztof Struczynski <[email protected]>
---
include/linux/integrity.h | 31 ++++++++
security/integrity/iint.c | 126 ++++++++++++++++++++++++++-------
security/integrity/integrity.h | 11 +++
3 files changed, 144 insertions(+), 24 deletions(-)
diff --git a/include/linux/integrity.h b/include/linux/integrity.h
index 2271939c5c31..5019fedaa17a 100644
--- a/include/linux/integrity.h
+++ b/include/linux/integrity.h
@@ -8,6 +8,10 @@
#define _LINUX_INTEGRITY_H
#include <linux/fs.h>
+#include <linux/rwlock_types.h>
+
+struct rb_root;
+struct integrity_iint_tree;
enum integrity_status {
INTEGRITY_PASS = 0,
@@ -21,8 +25,15 @@ enum integrity_status {
/* List of EVM protected security xattrs */
#ifdef CONFIG_INTEGRITY
extern struct integrity_iint_cache *integrity_inode_get(struct inode *inode);
+extern struct integrity_iint_cache *integrity_inode_rb_get(struct
+ integrity_iint_tree
+ *iint_tree,
+ struct inode *inode);
extern void integrity_inode_free(struct inode *inode);
+extern void integrity_inode_rb_free(struct integrity_iint_tree *iint_tree,
+ struct inode *inode);
extern void __init integrity_load_keys(void);
+extern void integrity_iint_tree_free(struct integrity_iint_tree *iint_tree);
#else
static inline struct integrity_iint_cache *
@@ -31,14 +42,34 @@ static inline struct integrity_iint_cache *
return NULL;
}
+static inline struct integrity_iint_cache *
+ integrity_inode_rb_get(struct
+ integrity_iint_tree
+ *iint_tree,
+ struct inode *inode)
+{
+ return NULL;
+}
+
static inline void integrity_inode_free(struct inode *inode)
{
return;
}
+static inline void integrity_inode_rb_free(struct integrity_iint_tree
+ *iint_tree,
+ struct inode *inode)
+{
+}
+
static inline void integrity_load_keys(void)
{
}
+
+static inline void integrity_iint_tree_free(struct integrity_iint_tree
+ *iint_tree)
+{
+}
#endif /* CONFIG_INTEGRITY */
#ifdef CONFIG_INTEGRITY_ASYMMETRIC_KEYS
diff --git a/security/integrity/iint.c b/security/integrity/iint.c
index 1d20003243c3..34a36f298f92 100644
--- a/security/integrity/iint.c
+++ b/security/integrity/iint.c
@@ -21,19 +21,29 @@
#include <linux/lsm_hooks.h>
#include "integrity.h"
-static struct rb_root integrity_iint_tree = RB_ROOT;
-static DEFINE_RWLOCK(integrity_iint_lock);
+struct integrity_iint_tree init_iint_tree = {
+ .lock = __RW_LOCK_UNLOCKED(init_iint_tree.lock),
+ .root = RB_ROOT
+};
+
static struct kmem_cache *iint_cache __read_mostly;
struct dentry *integrity_dir;
/*
- * __integrity_iint_find - return the iint associated with an inode
+ * __integrity_iint_rb_find - return the iint associated with an inode
+ * @iint_rb_root: pointer to the root of the iint tree
+ * @inode: pointer to the inode
+ * @return: pointer to the iint if found, NULL otherwise
*/
-static struct integrity_iint_cache *__integrity_iint_find(struct inode *inode)
+static struct integrity_iint_cache *
+ __integrity_iint_rb_find(const struct rb_root
+ *iint_rb_root,
+ const struct inode
+ *inode)
{
struct integrity_iint_cache *iint;
- struct rb_node *n = integrity_iint_tree.rb_node;
+ struct rb_node *n = iint_rb_root->rb_node;
while (n) {
iint = rb_entry(n, struct integrity_iint_cache, rb_node);
@@ -52,22 +62,37 @@ static struct integrity_iint_cache *__integrity_iint_find(struct inode *inode)
}
/*
- * integrity_iint_find - return the iint associated with an inode
+ * integrity_iint_rb_find - return the iint associated with an inode
+ * @iint_tree: pointer to the iint tree root node and the associated lock
+ * @inode: pointer to the inode
+ * @return: pointer to the iint if found, NULL otherwise
*/
-struct integrity_iint_cache *integrity_iint_find(struct inode *inode)
+struct integrity_iint_cache *integrity_iint_rb_find(struct integrity_iint_tree
+ *iint_tree,
+ const struct inode *inode)
{
struct integrity_iint_cache *iint;
if (!IS_IMA(inode))
return NULL;
- read_lock(&integrity_iint_lock);
- iint = __integrity_iint_find(inode);
- read_unlock(&integrity_iint_lock);
+ read_lock(&iint_tree->lock);
+ iint = __integrity_iint_rb_find(&iint_tree->root, inode);
+ read_unlock(&iint_tree->lock);
return iint;
}
+/*
+ * integrity_iint_find - return the iint associated with an inode
+ * @inode: pointer to the inode
+ * @return: pointer to the iint if found, NULL otherwise
+ */
+struct integrity_iint_cache *integrity_iint_find(struct inode *inode)
+{
+ return integrity_iint_rb_find(&init_iint_tree, inode);
+}
+
static void iint_free(struct integrity_iint_cache *iint)
{
kfree(iint->ima_hash);
@@ -86,19 +111,42 @@ static void iint_free(struct integrity_iint_cache *iint)
}
/**
- * integrity_inode_get - find or allocate an iint associated with an inode
+ * integrity_iint_tree_free - traverse the tree and free all nodes
+ * @iint_tree: pointer to the iint tree root node and the associated lock
+ *
+ * The tree cannot be in use. This function should be called only from the
+ * destructor when no locks are required.
+ */
+void integrity_iint_tree_free(struct integrity_iint_tree *iint_tree)
+{
+ struct rb_root *root = &iint_tree->root;
+ struct integrity_iint_cache *iint, *tmp;
+
+ rbtree_postorder_for_each_entry_safe(iint, tmp, root, rb_node) {
+ iint_free(iint);
+ }
+
+ iint_tree->root = RB_ROOT;
+}
+
+/**
+ * integrity_inode_rb_get - find or allocate an iint associated with an inode
+ * @iint_tree: pointer to the iint tree root node and the associated lock
* @inode: pointer to the inode
- * @return: allocated iint
+ * @return: pointer to the existing iint if found, pointer to the allocated iint
+ * if it didn't exist, NULL in case of error
*
* Caller must lock i_mutex
*/
-struct integrity_iint_cache *integrity_inode_get(struct inode *inode)
+struct integrity_iint_cache *integrity_inode_rb_get(struct integrity_iint_tree
+ *iint_tree,
+ struct inode *inode)
{
struct rb_node **p;
struct rb_node *node, *parent = NULL;
struct integrity_iint_cache *iint, *test_iint;
- iint = integrity_iint_find(inode);
+ iint = integrity_iint_rb_find(iint_tree, inode);
if (iint)
return iint;
@@ -106,9 +154,9 @@ struct integrity_iint_cache *integrity_inode_get(struct inode *inode)
if (!iint)
return NULL;
- write_lock(&integrity_iint_lock);
+ write_lock(&iint_tree->lock);
- p = &integrity_iint_tree.rb_node;
+ p = &iint_tree->root.rb_node;
while (*p) {
parent = *p;
test_iint = rb_entry(parent, struct integrity_iint_cache,
@@ -123,33 +171,63 @@ struct integrity_iint_cache *integrity_inode_get(struct inode *inode)
node = &iint->rb_node;
inode->i_flags |= S_IMA;
rb_link_node(node, parent, p);
- rb_insert_color(node, &integrity_iint_tree);
+ rb_insert_color(node, &iint_tree->root);
- write_unlock(&integrity_iint_lock);
+ write_unlock(&iint_tree->lock);
return iint;
}
/**
- * integrity_inode_free - called on security_inode_free
+ * integrity_inode_get - find or allocate an iint associated with an inode
+ * @inode: pointer to the inode
+ * @return: pointer to the existing iint if found, pointer to the allocated iint
+ * if it didn't exist, NULL in case of error
+ *
+ * Caller must lock i_mutex
+ */
+struct integrity_iint_cache *integrity_inode_get(struct inode *inode)
+{
+ return integrity_inode_rb_get(&init_iint_tree, inode);
+}
+
+/**
+ * integrity_inode_rb_free - called on security_inode_free
+ * @iint_tree: pointer to the iint tree root node and the associated lock
* @inode: pointer to the inode
*
* Free the integrity information(iint) associated with an inode.
*/
-void integrity_inode_free(struct inode *inode)
+void integrity_inode_rb_free(struct integrity_iint_tree *iint_tree,
+ struct inode *inode)
{
struct integrity_iint_cache *iint;
if (!IS_IMA(inode))
return;
- write_lock(&integrity_iint_lock);
- iint = __integrity_iint_find(inode);
- rb_erase(&iint->rb_node, &integrity_iint_tree);
- write_unlock(&integrity_iint_lock);
+ write_lock(&iint_tree->lock);
+ iint = __integrity_iint_rb_find(&iint_tree->root, inode);
+ if (!iint) {
+ write_unlock(&iint_tree->lock);
+ return;
+ }
+ rb_erase(&iint->rb_node, &iint_tree->root);
+ write_unlock(&iint_tree->lock);
iint_free(iint);
}
+/**
+ * integrity_inode_free - called on security_inode_free
+ * @inode: pointer to the inode
+ *
+ * Free the integrity information(iint) associated with an inode.
+ */
+void integrity_inode_free(struct inode *inode)
+{
+ integrity_inode_rb_free(&init_iint_tree, inode);
+}
+
static void init_once(void *foo)
{
struct integrity_iint_cache *iint = foo;
diff --git a/security/integrity/integrity.h b/security/integrity/integrity.h
index 413c803c5208..721d1850e4f9 100644
--- a/security/integrity/integrity.h
+++ b/security/integrity/integrity.h
@@ -140,11 +140,20 @@ struct integrity_iint_cache {
struct ima_digest_data *ima_hash;
};
+struct integrity_iint_tree {
+ rwlock_t lock;
+ struct rb_root root;
+};
+
/* rbtree tree calls to lookup, insert, delete
* integrity data associated with an inode.
*/
struct integrity_iint_cache *integrity_iint_find(struct inode *inode);
+struct integrity_iint_cache *integrity_iint_rb_find(struct integrity_iint_tree
+ *iint_tree,
+ const struct inode *inode);
+
int integrity_kernel_read(struct file *file, loff_t offset,
void *addr, unsigned long count);
@@ -155,6 +164,8 @@ int integrity_kernel_read(struct file *file, loff_t offset,
extern struct dentry *integrity_dir;
+extern struct integrity_iint_tree init_iint_tree;
+
struct modsig;
#ifdef CONFIG_INTEGRITY_SIGNATURE
--
2.20.1
From: Krzysztof Struczynski <[email protected]>
Set ima policy per namespace and remove the global settings. Operations
on the objects may now have impact in more than one ima namespace and
therefore iterate all active ima namespaces when necessary.
Read-write violations can now happen across namespaces and should be
checked in all namespaces for each relevant ima hook.
Inform all concerned ima namespaces about the actions on the objects
when the object is freed. E.g. if an object had been appraised in the
ima_ns_1 and then modified in the ima_ns_2, appraised flag in the
ima_ns_1 is cleared and the object will be re-appraised in the ima_ns_1
namespace.
Signed-off-by: Krzysztof Struczynski <[email protected]>
---
include/linux/ima.h | 11 +
kernel/kexec_file.c | 7 +
security/integrity/ima/ima.h | 10 +-
security/integrity/ima/ima_api.c | 2 +-
security/integrity/ima/ima_appraise.c | 99 ++++---
security/integrity/ima/ima_asymmetric_keys.c | 13 +-
security/integrity/ima/ima_fs.c | 6 +-
security/integrity/ima/ima_init.c | 7 +-
security/integrity/ima/ima_main.c | 235 ++++++++++++----
security/integrity/ima/ima_ns.c | 3 +
security/integrity/ima/ima_policy.c | 278 +++++++++++--------
security/integrity/ima/ima_queue_keys.c | 10 +-
security/security.c | 2 +-
13 files changed, 462 insertions(+), 221 deletions(-)
diff --git a/include/linux/ima.h b/include/linux/ima.h
index 9069aafd905f..1d0439d86ade 100644
--- a/include/linux/ima.h
+++ b/include/linux/ima.h
@@ -33,6 +33,8 @@ extern int ima_post_read_file(struct file *file, void *buf, loff_t size,
extern void ima_post_path_mknod(struct dentry *dentry);
extern int ima_file_hash(struct file *file, char *buf, size_t buf_size);
extern void ima_kexec_cmdline(int kernel_fd, const void *buf, int size);
+extern void ima_inode_free(struct inode *inode);
+extern bool ima_is_root_namespace(void);
#ifdef CONFIG_IMA_KEXEC
extern void ima_add_kexec_buffer(struct kimage *image);
@@ -116,6 +118,15 @@ static inline int ima_file_hash(struct file *file, char *buf, size_t buf_size)
}
static inline void ima_kexec_cmdline(int kernel_fd, const void *buf, int size) {}
+
+static inline void ima_inode_free(struct inode *inode)
+{
+}
+
+static inline bool ima_is_root_namespace(void)
+{
+ return true;
+}
#endif /* CONFIG_IMA */
#ifndef CONFIG_IMA_KEXEC
diff --git a/kernel/kexec_file.c b/kernel/kexec_file.c
index 78c0837bfd7b..e17542057dfb 100644
--- a/kernel/kexec_file.c
+++ b/kernel/kexec_file.c
@@ -356,6 +356,13 @@ SYSCALL_DEFINE5(kexec_file_load, int, kernel_fd, int, initrd_fd,
if (!capable(CAP_SYS_BOOT) || kexec_load_disabled)
return -EPERM;
+ /* Allow only from the initial IMA namespace, so that the user can't
+ * spawn a new IMA namespace with the empty policy and circumvent the
+ * appraisal protection.
+ */
+ if (!ima_is_root_namespace())
+ return -EPERM;
+
/* Make sure we have a legal set of flags */
if (flags != (flags & KEXEC_FILE_FLAGS))
return -EINVAL;
diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h
index 7d522fdab0d8..33b4a8295c41 100644
--- a/security/integrity/ima/ima.h
+++ b/security/integrity/ima/ima.h
@@ -48,15 +48,11 @@ 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;
-
/* set during initialization */
extern int ima_hash_algo;
extern int ima_sha1_idx __ro_after_init;
extern int ima_hash_algo_idx __ro_after_init;
extern int ima_extra_slots __ro_after_init;
-extern int ima_appraise;
extern struct tpm_chip *ima_tpm_chip;
extern const char boot_aggregate_name[];
@@ -410,6 +406,9 @@ struct ima_policy_data {
int temp_ima_appraise;
};
+extern struct list_head ima_ns_list;
+extern struct rw_semaphore ima_ns_list_lock;
+
extern struct ima_policy_data init_policy_data;
extern struct ima_policy_setup_data init_policy_setup_data;
@@ -423,6 +422,9 @@ static inline struct ima_namespace *get_current_ns(void)
{
return current->nsproxy->ima_ns;
}
+
+void ima_delete_ns_rules(struct ima_policy_data *policy_data,
+ bool is_root_ns);
#else
static inline int __init ima_init_namespace(void)
{
diff --git a/security/integrity/ima/ima_api.c b/security/integrity/ima/ima_api.c
index 8b41183200e8..8d7b0d4635fc 100644
--- a/security/integrity/ima/ima_api.c
+++ b/security/integrity/ima/ima_api.c
@@ -192,7 +192,7 @@ int ima_get_action(struct inode *inode, const struct cred *cred, u32 secid,
{
int flags = IMA_MEASURE | IMA_AUDIT | IMA_APPRAISE | IMA_HASH;
- flags &= ima_policy_flag;
+ flags &= ima_ns->policy_data->ima_policy_flag;
return ima_match_policy(inode, cred, secid, func, mask, flags, pcr,
template_desc, keyring, ima_ns);
diff --git a/security/integrity/ima/ima_appraise.c b/security/integrity/ima/ima_appraise.c
index 9388ff88ca4d..a5e775182fb0 100644
--- a/security/integrity/ima/ima_appraise.c
+++ b/security/integrity/ima/ima_appraise.c
@@ -18,14 +18,6 @@
int ima_default_appraise_setup(const char *str,
struct ima_policy_setup_data *setup_data)
-{
- /* Currently unused. It will be implemented after namespacing ima
- * policy, when global variables are removed.
- */
- return 1;
-}
-
-static int __init default_appraise_setup(char *str)
{
#ifdef CONFIG_IMA_APPRAISE_BOOTPARAM
if (arch_ima_get_secureboot()) {
@@ -35,15 +27,20 @@ static int __init default_appraise_setup(char *str)
}
if (strncmp(str, "off", 3) == 0)
- ima_appraise = 0;
+ setup_data->ima_appraise = 0;
else if (strncmp(str, "log", 3) == 0)
- ima_appraise = IMA_APPRAISE_LOG;
+ setup_data->ima_appraise = IMA_APPRAISE_LOG;
else if (strncmp(str, "fix", 3) == 0)
- ima_appraise = IMA_APPRAISE_FIX;
+ setup_data->ima_appraise = IMA_APPRAISE_FIX;
#endif
return 1;
}
+static int __init default_appraise_setup(char *str)
+{
+ return ima_default_appraise_setup(str, &init_policy_setup_data);
+}
+
__setup("ima_appraise=", default_appraise_setup);
/*
@@ -54,7 +51,10 @@ __setup("ima_appraise=", default_appraise_setup);
*/
bool is_ima_appraise_enabled(const struct ima_namespace *ima_ns)
{
- return ima_appraise & IMA_APPRAISE_ENFORCE;
+ if (!ima_ns)
+ return false;
+
+ return ima_ns->policy_data->ima_appraise & IMA_APPRAISE_ENFORCE;
}
/*
@@ -62,18 +62,18 @@ bool is_ima_appraise_enabled(const struct ima_namespace *ima_ns)
*
* Return 1 to appraise or hash
*/
-int ima_must_appraise(struct inode *inode, int mask, enum ima_hooks func,
- struct ima_namespace *ima_ns)
+int ima_must_appraise(struct inode *inode, int mask,
+ enum ima_hooks func, struct ima_namespace *ima_ns)
{
u32 secid;
- if (!ima_appraise)
+ if (!ima_ns->policy_data->ima_appraise)
return 0;
security_task_getsecid(current, &secid);
return ima_match_policy(inode, current_cred(), secid, func, mask,
IMA_APPRAISE | IMA_HASH, NULL, NULL, NULL,
- NULL);
+ ima_ns);
}
static int ima_fix_xattr(struct dentry *dentry,
@@ -349,7 +349,7 @@ int ima_check_blacklist(struct integrity_iint_cache *iint,
if ((rc == -EPERM) && (iint->flags & IMA_MEASURE))
process_buffer_measurement(NULL, digest, digestsize,
"blacklisted-hash", NONE,
- pcr, NULL, NULL);
+ pcr, NULL, ima_ns);
}
return rc;
@@ -376,6 +376,7 @@ int ima_appraise_measurement(enum ima_hooks func,
enum integrity_status status = INTEGRITY_UNKNOWN;
int rc = xattr_len;
bool try_modsig = iint->flags & IMA_MODSIG_ALLOWED && modsig;
+ struct ima_namespace *ima_ns = get_current_ns();
/* If not appraising a modsig, we need an xattr. */
if (!(inode->i_opflags & IOP_XATTR) && !try_modsig)
@@ -448,7 +449,8 @@ int ima_appraise_measurement(enum ima_hooks func,
op, cause, rc, 0);
} else if (status != INTEGRITY_PASS) {
/* Fix mode, but don't replace file signatures. */
- if ((ima_appraise & IMA_APPRAISE_FIX) && !try_modsig &&
+ if ((ima_ns->policy_data->ima_appraise & IMA_APPRAISE_FIX) &&
+ !try_modsig &&
(!xattr_value ||
xattr_value->type != EVM_IMA_XATTR_DIGSIG)) {
if (!ima_fix_xattr(dentry, iint))
@@ -510,20 +512,32 @@ void ima_inode_post_setattr(struct dentry *dentry)
struct inode *inode = d_backing_inode(dentry);
struct integrity_iint_cache *iint;
int action;
+ struct ima_namespace *ima_ns;
- if (!(ima_policy_flag & IMA_APPRAISE) || !S_ISREG(inode->i_mode)
- || !(inode->i_opflags & IOP_XATTR))
+ if (!S_ISREG(inode->i_mode) ||
+ !(inode->i_opflags & IOP_XATTR))
return;
- action = ima_must_appraise(inode, MAY_ACCESS, POST_SETATTR, NULL);
- if (!action)
- __vfs_removexattr(dentry, XATTR_NAME_IMA);
- iint = integrity_iint_find(inode);
- if (iint) {
- set_bit(IMA_CHANGE_ATTR, &iint->atomic_flags);
+ down_read(&ima_ns_list_lock);
+ list_for_each_entry(ima_ns, &ima_ns_list, list) {
+ if (atomic_read(&ima_ns->inactive))
+ continue;
+ if (!(ima_ns->policy_data->ima_policy_flag & IMA_APPRAISE))
+ continue;
+
+ action = ima_must_appraise(inode, MAY_ACCESS, POST_SETATTR,
+ ima_ns);
if (!action)
- clear_bit(IMA_UPDATE_XATTR, &iint->atomic_flags);
+ __vfs_removexattr(dentry, XATTR_NAME_IMA);
+ iint = integrity_iint_rb_find(ima_ns->iint_tree, inode);
+ if (iint) {
+ set_bit(IMA_CHANGE_ATTR, &iint->atomic_flags);
+ if (!action)
+ clear_bit(IMA_UPDATE_XATTR,
+ &iint->atomic_flags);
+ }
}
+ up_read(&ima_ns_list_lock);
}
/*
@@ -545,19 +559,30 @@ static int ima_protect_xattr(struct dentry *dentry, const char *xattr_name,
static void ima_reset_appraise_flags(struct inode *inode, int digsig)
{
struct integrity_iint_cache *iint;
+ struct ima_namespace *ima_ns;
- if (!(ima_policy_flag & IMA_APPRAISE) || !S_ISREG(inode->i_mode))
+ if (!S_ISREG(inode->i_mode))
return;
- iint = integrity_iint_find(inode);
- if (!iint)
- return;
- iint->measured_pcrs = 0;
- set_bit(IMA_CHANGE_XATTR, &iint->atomic_flags);
- if (digsig)
- set_bit(IMA_DIGSIG, &iint->atomic_flags);
- else
- clear_bit(IMA_DIGSIG, &iint->atomic_flags);
+ down_read(&ima_ns_list_lock);
+ list_for_each_entry(ima_ns, &ima_ns_list, list) {
+ if (atomic_read(&ima_ns->inactive))
+ continue;
+ if (!(ima_ns->policy_data->ima_policy_flag & IMA_APPRAISE))
+ continue;
+
+ iint = integrity_iint_find(inode);
+ if (!iint)
+ continue;
+
+ iint->measured_pcrs = 0;
+ set_bit(IMA_CHANGE_XATTR, &iint->atomic_flags);
+ if (digsig)
+ set_bit(IMA_DIGSIG, &iint->atomic_flags);
+ else
+ clear_bit(IMA_DIGSIG, &iint->atomic_flags);
+ }
+ up_read(&ima_ns_list_lock);
}
int ima_inode_setxattr(struct dentry *dentry, const char *xattr_name,
diff --git a/security/integrity/ima/ima_asymmetric_keys.c b/security/integrity/ima/ima_asymmetric_keys.c
index 58aa56b0422d..b3330a0a1481 100644
--- a/security/integrity/ima/ima_asymmetric_keys.c
+++ b/security/integrity/ima/ima_asymmetric_keys.c
@@ -29,6 +29,16 @@ void ima_post_key_create_or_update(struct key *keyring, struct key *key,
unsigned long flags, bool create)
{
bool queued = false;
+ /* Measure the keys according to the current ima namespace's policy
+ * rules. If the new ima namespace with empty policy is created to hide
+ * the log, parent can join it to inspect the log until the child
+ * namespace exists. After its destruction, log can be accessed only
+ * by the processes from the initial ima namespace that see all
+ * measurement list entries. If this is a problem, maybe the solution
+ * is to track in which namespaces the key was measured and re-measure
+ * it when necessary.
+ */
+ struct ima_namespace *ima_ns = get_current_ns();
/* Only asymmetric keys are handled by this hook. */
if (key->type != &key_type_asymmetric)
@@ -60,6 +70,5 @@ void ima_post_key_create_or_update(struct key *keyring, struct key *key,
*/
process_buffer_measurement(NULL, payload, payload_len,
keyring->description, KEY_CHECK, 0,
- keyring->description,
- NULL);
+ keyring->description, ima_ns);
}
diff --git a/security/integrity/ima/ima_fs.c b/security/integrity/ima/ima_fs.c
index 97aadee7e68e..3839b9eaecab 100644
--- a/security/integrity/ima/ima_fs.c
+++ b/security/integrity/ima/ima_fs.c
@@ -312,6 +312,7 @@ static ssize_t ima_write_policy(struct file *file, const char __user *buf,
{
char *data;
ssize_t result;
+ struct ima_namespace *ima_ns = get_current_ns();
if (datalen >= PAGE_SIZE)
datalen = PAGE_SIZE - 1;
@@ -333,7 +334,7 @@ static ssize_t ima_write_policy(struct file *file, const char __user *buf,
if (data[0] == '/') {
result = ima_read_policy(data);
- } else if (ima_appraise & IMA_APPRAISE_POLICY) {
+ } else if (ima_ns->policy_data->ima_appraise & IMA_APPRAISE_POLICY) {
pr_err("signed policy file (specified as an absolute pathname) required\n");
integrity_audit_msg(AUDIT_INTEGRITY_STATUS, NULL, NULL,
"policy_update", "signed policy required",
@@ -406,11 +407,12 @@ static int ima_open_policy(struct inode *inode, struct file *filp)
static int ima_release_policy(struct inode *inode, struct file *file)
{
const char *cause = valid_policy ? "completed" : "failed";
+ struct ima_namespace *ima_ns = get_current_ns();
if ((file->f_flags & O_ACCMODE) == O_RDONLY)
return seq_release(inode, file);
- if (valid_policy && ima_check_policy(NULL) < 0) {
+ if (valid_policy && ima_check_policy(ima_ns) < 0) {
cause = "failed";
valid_policy = 0;
}
diff --git a/security/integrity/ima/ima_init.c b/security/integrity/ima/ima_init.c
index be1afc42fdf5..d042b08cc4d7 100644
--- a/security/integrity/ima/ima_init.c
+++ b/security/integrity/ima/ima_init.c
@@ -118,11 +118,12 @@ 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.policy_data->ima_policy_flag & IMA_APPRAISE;
- ima_policy_flag &= ~unset_flags;
+ init_ima_ns.policy_data->ima_policy_flag &= ~unset_flags;
integrity_load_x509(INTEGRITY_KEYRING_IMA, CONFIG_IMA_X509_PATH);
- ima_policy_flag |= unset_flags;
+ init_ima_ns.policy_data->ima_policy_flag |= unset_flags;
}
#endif
diff --git a/security/integrity/ima/ima_main.c b/security/integrity/ima/ima_main.c
index 196fa2bd490d..80b1737a3369 100644
--- a/security/integrity/ima/ima_main.c
+++ b/security/integrity/ima/ima_main.c
@@ -28,12 +28,6 @@
#include "ima.h"
-#ifdef CONFIG_IMA_APPRAISE
-int ima_appraise = IMA_APPRAISE_ENFORCE;
-#else
-int ima_appraise;
-#endif
-
int ima_hash_algo = HASH_ALGO_SHA1;
static int hash_setup_done;
@@ -119,7 +113,8 @@ static void ima_rdwr_violation_check(struct file *file,
if (mode & FMODE_WRITE) {
if (atomic_read(&inode->i_readcount) && IS_IMA(inode)) {
if (!iint)
- iint = integrity_iint_find(inode);
+ iint = integrity_iint_rb_find(ima_ns->iint_tree,
+ inode);
/* IMA_MEASURE is set from reader side */
if (iint && test_bit(IMA_MUST_MEASURE,
&iint->atomic_flags))
@@ -145,11 +140,38 @@ static void ima_rdwr_violation_check(struct file *file,
"invalid_pcr", "open_writers");
}
+static void ima_check_active_ns(struct ima_namespace *current_ima_ns,
+ struct inode *inode)
+{
+ struct ima_namespace *ima_ns;
+ struct integrity_iint_cache *iint;
+
+ down_read(&ima_ns_list_lock);
+ list_for_each_entry(ima_ns, &ima_ns_list, list) {
+ if (atomic_read(&ima_ns->inactive))
+ continue;
+ if ((ima_ns == current_ima_ns) ||
+ !ima_ns->policy_data->ima_policy_flag)
+ continue;
+
+ iint = integrity_iint_rb_find(ima_ns->iint_tree, inode);
+ if (!iint)
+ continue;
+
+ mutex_lock(&iint->mutex);
+ iint->flags &= ~IMA_DONE_MASK;
+ iint->measured_pcrs = 0;
+ mutex_unlock(&iint->mutex);
+ }
+ up_read(&ima_ns_list_lock);
+}
+
static void ima_check_last_writer(struct integrity_iint_cache *iint,
struct inode *inode, struct file *file)
{
fmode_t mode = file->f_mode;
bool update;
+ struct ima_namespace *ima_ns = (struct ima_namespace *)file->f_ima;
if (!(mode & FMODE_WRITE))
return;
@@ -163,6 +185,9 @@ static void ima_check_last_writer(struct integrity_iint_cache *iint,
(iint->flags & IMA_NEW_FILE)) {
iint->flags &= ~(IMA_DONE_MASK | IMA_NEW_FILE);
iint->measured_pcrs = 0;
+
+ ima_check_active_ns(ima_ns, inode);
+
if (update)
ima_update_xattr(iint, file);
}
@@ -202,10 +227,10 @@ void ima_file_free(struct file *file)
if (unlikely(!(file->f_mode & FMODE_OPENED)))
goto out;
- if (!ima_policy_flag || !S_ISREG(inode->i_mode))
+ if (!ima_ns->policy_data->ima_policy_flag || !S_ISREG(inode->i_mode))
goto out;
- iint = integrity_iint_find(inode);
+ iint = integrity_iint_rb_find(ima_ns->iint_tree, inode);
if (!iint)
goto out;
@@ -214,10 +239,10 @@ void ima_file_free(struct file *file)
put_ima_ns(ima_ns);
}
-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 *ima_ns)
+static int process_ns_measurement(struct file *file, const struct cred *cred,
+ u32 secid, char *buf, loff_t size, int mask,
+ enum ima_hooks func,
+ struct ima_namespace *ima_ns)
{
struct inode *inode = file_inode(file);
struct integrity_iint_cache *iint = NULL;
@@ -232,8 +257,9 @@ static int process_measurement(struct file *file, const struct cred *cred,
int xattr_len = 0;
bool violation_check;
enum hash_algo hash_algo;
+ struct ima_namespace *current_ima_ns = get_current_ns();
- if (!ima_policy_flag || !S_ISREG(inode->i_mode))
+ if (!ima_ns->policy_data->ima_policy_flag)
return 0;
/* Return an IMA_MEASURE, IMA_APPRAISE, IMA_AUDIT action
@@ -243,7 +269,8 @@ static int process_measurement(struct file *file, const struct cred *cred,
action = ima_get_action(inode, cred, secid, mask, func, &pcr,
&template_desc, NULL, ima_ns);
violation_check = ((func == FILE_CHECK || func == MMAP_CHECK) &&
- (ima_policy_flag & IMA_MEASURE));
+ (ima_ns->policy_data->ima_policy_flag &
+ IMA_MEASURE));
if (!action && !violation_check)
return 0;
@@ -256,7 +283,7 @@ static int process_measurement(struct file *file, const struct cred *cred,
inode_lock(inode);
if (action) {
- iint = integrity_inode_get(inode);
+ iint = integrity_inode_rb_get(ima_ns->iint_tree, inode);
if (!iint)
rc = -ENOMEM;
}
@@ -271,6 +298,8 @@ static int process_measurement(struct file *file, const struct cred *cred,
goto out;
if (!action)
goto out;
+ if (ima_ns != current_ima_ns)
+ goto out;
mutex_lock(&iint->mutex);
@@ -389,7 +418,8 @@ static int process_measurement(struct file *file, const struct cred *cred,
if (pathbuf)
__putname(pathbuf);
if (must_appraise) {
- if (rc && (ima_appraise & IMA_APPRAISE_ENFORCE))
+ if (rc &&
+ (ima_ns->policy_data->ima_appraise & IMA_APPRAISE_ENFORCE))
return -EACCES;
if (file->f_mode & FMODE_WRITE)
set_bit(IMA_UPDATE_XATTR, &iint->atomic_flags);
@@ -397,6 +427,32 @@ static int process_measurement(struct file *file, const struct cred *cred,
return 0;
}
+static int process_measurement(struct file *file, const struct cred *cred,
+ u32 secid, char *buf, loff_t size, int mask,
+ enum ima_hooks func)
+{
+ int ret;
+ struct ima_namespace *ima_ns;
+ struct inode *inode = file_inode(file);
+
+ if (!S_ISREG(inode->i_mode))
+ return 0;
+
+ down_read(&ima_ns_list_lock);
+ list_for_each_entry(ima_ns, &ima_ns_list, list) {
+ if (atomic_read(&ima_ns->inactive))
+ continue;
+
+ ret = process_ns_measurement(file, cred, secid, buf, size, mask,
+ func, ima_ns);
+ if (ret != 0)
+ break;
+ }
+ up_read(&ima_ns_list_lock);
+
+ return ret;
+}
+
/**
* ima_file_mmap - based on policy, collect/store measurement.
* @file: pointer to the file to be measured (May be NULL)
@@ -415,7 +471,7 @@ int ima_file_mmap(struct file *file, unsigned long prot)
if (file && (prot & PROT_EXEC)) {
security_task_getsecid(current, &secid);
return process_measurement(file, current_cred(), secid, NULL,
- 0, MAY_EXEC, MMAP_CHECK, NULL);
+ 0, MAY_EXEC, MMAP_CHECK);
}
return 0;
@@ -435,6 +491,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 *ima_ns = get_current_ns();
struct ima_template_desc *template;
struct file *file = vma->vm_file;
char filename[NAME_MAX];
@@ -447,14 +504,15 @@ 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 ||
- !(prot & PROT_EXEC) || (vma->vm_flags & VM_EXEC))
+ if (!(ima_ns->policy_data->ima_policy_flag & IMA_APPRAISE) ||
+ !vma->vm_file || !(prot & PROT_EXEC) ||
+ (vma->vm_flags & VM_EXEC))
return 0;
security_task_getsecid(current, &secid);
inode = file_inode(vma->vm_file);
action = ima_get_action(inode, current_cred(), secid, MAY_EXEC,
- MMAP_CHECK, &pcr, &template, 0, NULL);
+ MMAP_CHECK, &pcr, &template, 0, ima_ns);
/* Is the mmap'ed file in policy? */
if (!(action & (IMA_MEASURE | IMA_APPRAISE_SUBMASK)))
@@ -493,13 +551,13 @@ int ima_bprm_check(struct linux_binprm *bprm)
security_task_getsecid(current, &secid);
ret = process_measurement(bprm->file, current_cred(), secid, NULL, 0,
- MAY_EXEC, BPRM_CHECK, NULL);
+ MAY_EXEC, BPRM_CHECK);
if (ret)
return ret;
security_cred_getsecid(bprm->cred, &secid);
return process_measurement(bprm->file, bprm->cred, secid, NULL, 0,
- MAY_EXEC, CREDS_CHECK, NULL);
+ MAY_EXEC, CREDS_CHECK);
}
/**
@@ -519,13 +577,13 @@ int ima_file_check(struct file *file, int mask)
security_task_getsecid(current, &secid);
return process_measurement(file, current_cred(), secid, NULL, 0,
mask & (MAY_READ | MAY_WRITE | MAY_EXEC |
- MAY_APPEND), FILE_CHECK, NULL);
+ MAY_APPEND), FILE_CHECK);
}
EXPORT_SYMBOL_GPL(ima_file_check);
/**
* ima_file_hash - return the stored measurement if a file has been hashed and
- * is in the iint cache.
+ * is in the iint cache of the current IMA namespace.
* @file: pointer to the file
* @buf: buffer in which to store the hash
* @buf_size: length of the buffer
@@ -543,6 +601,7 @@ EXPORT_SYMBOL_GPL(ima_file_check);
*/
int ima_file_hash(struct file *file, char *buf, size_t buf_size)
{
+ struct ima_namespace *ima_ns = get_current_ns();
struct inode *inode;
struct integrity_iint_cache *iint;
int hash_algo;
@@ -550,11 +609,11 @@ int ima_file_hash(struct file *file, char *buf, size_t buf_size)
if (!file)
return -EINVAL;
- if (!ima_policy_flag)
+ if (!ima_ns->policy_data->ima_policy_flag)
return -EOPNOTSUPP;
inode = file_inode(file);
- iint = integrity_iint_find(inode);
+ iint = integrity_iint_rb_find(ima_ns->iint_tree, inode);
if (!iint)
return -EOPNOTSUPP;
@@ -582,21 +641,30 @@ EXPORT_SYMBOL_GPL(ima_file_hash);
*/
void ima_post_create_tmpfile(struct inode *inode)
{
+ struct ima_namespace *ima_ns;
struct integrity_iint_cache *iint;
int must_appraise;
- must_appraise = ima_must_appraise(inode, MAY_ACCESS, FILE_CHECK, NULL);
- if (!must_appraise)
- return;
+ down_read(&ima_ns_list_lock);
+ list_for_each_entry(ima_ns, &ima_ns_list, list) {
+ if (atomic_read(&ima_ns->inactive))
+ continue;
- /* Nothing to do if we can't allocate memory */
- iint = integrity_inode_get(inode);
- if (!iint)
- return;
+ must_appraise = ima_must_appraise(inode, MAY_ACCESS,
+ FILE_CHECK, ima_ns);
+ if (!must_appraise)
+ continue;
+
+ /* Nothing to do if we can't allocate memory */
+ iint = integrity_inode_rb_get(ima_ns->iint_tree, inode);
+ if (!iint)
+ continue;
- /* needed for writing the security xattrs */
- set_bit(IMA_UPDATE_XATTR, &iint->atomic_flags);
- iint->ima_file_status = INTEGRITY_PASS;
+ /* needed for writing the security xattrs */
+ set_bit(IMA_UPDATE_XATTR, &iint->atomic_flags);
+ iint->ima_file_status = INTEGRITY_PASS;
+ }
+ up_read(&ima_ns_list_lock);
}
/**
@@ -608,21 +676,30 @@ void ima_post_create_tmpfile(struct inode *inode)
*/
void ima_post_path_mknod(struct dentry *dentry)
{
+ struct ima_namespace *ima_ns;
struct integrity_iint_cache *iint;
struct inode *inode = dentry->d_inode;
int must_appraise;
- must_appraise = ima_must_appraise(inode, MAY_ACCESS, FILE_CHECK, NULL);
- if (!must_appraise)
- return;
+ down_read(&ima_ns_list_lock);
+ list_for_each_entry(ima_ns, &ima_ns_list, list) {
+ if (atomic_read(&ima_ns->inactive))
+ continue;
- /* Nothing to do if we can't allocate memory */
- iint = integrity_inode_get(inode);
- if (!iint)
- return;
+ must_appraise = ima_must_appraise(inode, MAY_ACCESS,
+ FILE_CHECK, ima_ns);
+ if (!must_appraise)
+ continue;
- /* needed for re-opening empty files */
- iint->flags |= IMA_NEW_FILE;
+ /* Nothing to do if we can't allocate memory */
+ iint = integrity_inode_rb_get(ima_ns->iint_tree, inode);
+ if (!iint)
+ continue;
+
+ /* needed for re-opening empty files */
+ iint->flags |= IMA_NEW_FILE;
+ }
+ up_read(&ima_ns_list_lock);
}
/**
@@ -676,10 +753,13 @@ int ima_post_read_file(struct file *file, void *buf, loff_t size,
{
enum ima_hooks func;
u32 secid;
+ struct ima_namespace *ima_ns = get_current_ns();
if (!file && read_id == READING_FIRMWARE) {
- if ((ima_appraise & IMA_APPRAISE_FIRMWARE) &&
- (ima_appraise & IMA_APPRAISE_ENFORCE)) {
+ if ((ima_ns->policy_data->ima_appraise &
+ IMA_APPRAISE_FIRMWARE) &&
+ (ima_ns->policy_data->ima_appraise &
+ IMA_APPRAISE_ENFORCE)) {
pr_err("Prevent firmware loading_store.\n");
return -EACCES; /* INTEGRITY_UNKNOWN */
}
@@ -691,7 +771,7 @@ int ima_post_read_file(struct file *file, void *buf, loff_t size,
return 0;
if (!file || !buf || size == 0) { /* should never happen */
- if (ima_appraise & IMA_APPRAISE_ENFORCE)
+ if (ima_ns->policy_data->ima_appraise & IMA_APPRAISE_ENFORCE)
return -EACCES;
return 0;
}
@@ -699,7 +779,7 @@ int ima_post_read_file(struct file *file, void *buf, loff_t size,
func = read_idmap[read_id] ?: FILE_CHECK;
security_task_getsecid(current, &secid);
return process_measurement(file, current_cred(), secid, buf, size,
- MAY_READ, func, NULL);
+ MAY_READ, func);
}
/**
@@ -715,9 +795,16 @@ int ima_post_read_file(struct file *file, void *buf, loff_t size,
int ima_load_data(enum kernel_load_data_id id)
{
bool ima_enforce, sig_enforce;
+ struct ima_namespace *ima_ns = get_current_ns();
+
+ if (ima_ns != &init_ima_ns) {
+ pr_err("Prevent data loading in IMA namespaces other than the root\n");
+ return -EACCES;
+ }
ima_enforce =
- (ima_appraise & IMA_APPRAISE_ENFORCE) == IMA_APPRAISE_ENFORCE;
+ (ima_ns->policy_data->ima_appraise & IMA_APPRAISE_ENFORCE) ==
+ IMA_APPRAISE_ENFORCE;
switch (id) {
case LOADING_KEXEC_IMAGE:
@@ -727,13 +814,16 @@ int ima_load_data(enum kernel_load_data_id id)
return -EACCES;
}
- if (ima_enforce && (ima_appraise & IMA_APPRAISE_KEXEC)) {
+ if (ima_enforce &&
+ (ima_ns->policy_data->ima_appraise & IMA_APPRAISE_KEXEC)) {
pr_err("impossible to appraise a kernel image without a file descriptor; try using kexec_file_load syscall.\n");
return -EACCES; /* INTEGRITY_UNKNOWN */
}
break;
case LOADING_FIRMWARE:
- if (ima_enforce && (ima_appraise & IMA_APPRAISE_FIRMWARE)) {
+ if (ima_enforce &&
+ (ima_ns->policy_data->ima_appraise &
+ IMA_APPRAISE_FIRMWARE)) {
pr_err("Prevent firmware sysfs fallback loading.\n");
return -EACCES; /* INTEGRITY_UNKNOWN */
}
@@ -741,8 +831,10 @@ int ima_load_data(enum kernel_load_data_id id)
case LOADING_MODULE:
sig_enforce = is_module_sig_enforced();
- if (ima_enforce && (!sig_enforce
- && (ima_appraise & IMA_APPRAISE_MODULES))) {
+ if (ima_enforce &&
+ (!sig_enforce &&
+ (ima_ns->policy_data->ima_appraise &
+ IMA_APPRAISE_MODULES))) {
pr_err("impossible to appraise a module without a file descriptor. sig_enforce kernel parameter might help\n");
return -EACCES; /* INTEGRITY_UNKNOWN */
}
@@ -761,6 +853,7 @@ int ima_load_data(enum kernel_load_data_id id)
* @func: IMA hook
* @pcr: pcr to extend the measurement
* @keyring: keyring name to determine the action to be performed
+ * @ima_ns: pointer to the IMA namespace in consideration
*
* Based on policy, the buffer is measured into the ima log.
*/
@@ -786,7 +879,7 @@ void process_buffer_measurement(struct inode *inode, const void *buf, int size,
int action = 0;
u32 secid;
- if (!ima_policy_flag)
+ if (!ima_ns->policy_data->ima_policy_flag)
return;
/*
@@ -799,7 +892,7 @@ void process_buffer_measurement(struct inode *inode, const void *buf, int size,
if (func) {
security_task_getsecid(current, &secid);
action = ima_get_action(inode, current_cred(), secid, 0, func,
- &pcr, &template, keyring, NULL);
+ &pcr, &template, keyring, ima_ns);
if (!(action & IMA_MEASURE))
return;
}
@@ -862,6 +955,11 @@ void process_buffer_measurement(struct inode *inode, const void *buf, int size,
void ima_kexec_cmdline(int kernel_fd, const void *buf, int size)
{
struct fd f;
+ struct ima_namespace *ima_ns = get_current_ns();
+
+ /* Currently allowed only from the root IMA namespace */
+ if (WARN_ON(ima_ns != &init_ima_ns))
+ return;
if (!buf || !size)
return;
@@ -872,10 +970,31 @@ void ima_kexec_cmdline(int kernel_fd, const void *buf, int size)
process_buffer_measurement(file_inode(f.file), buf, size,
"kexec-cmdline", KEXEC_CMDLINE, 0, NULL,
- NULL);
+ ima_ns);
fdput(f);
}
+void ima_inode_free(struct inode *inode)
+{
+ struct ima_namespace *ima_ns;
+
+ if (!IS_IMA(inode))
+ return;
+
+ down_read(&ima_ns_list_lock);
+ list_for_each_entry(ima_ns, &ima_ns_list, list) {
+ if (atomic_read(&ima_ns->inactive))
+ continue;
+ integrity_inode_rb_free(ima_ns->iint_tree, inode);
+ }
+ up_read(&ima_ns_list_lock);
+}
+
+bool ima_is_root_namespace(void)
+{
+ return get_current_ns() == &init_ima_ns;
+}
+
static int __init init_ima(void)
{
int error;
diff --git a/security/integrity/ima/ima_ns.c b/security/integrity/ima/ima_ns.c
index 226a53279f71..04aa50473971 100644
--- a/security/integrity/ima/ima_ns.c
+++ b/security/integrity/ima/ima_ns.c
@@ -181,11 +181,14 @@ int __init ima_init_namespace(void)
static void destroy_ima_ns(struct ima_namespace *ns)
{
+ bool is_init_ns = (ns == &init_ima_ns);
+
dec_ima_namespaces(ns->ucounts);
put_user_ns(ns->user_ns);
ns_free_inum(&ns->ns);
integrity_iint_tree_free(ns->iint_tree);
kfree(ns->iint_tree);
+ ima_delete_ns_rules(ns->policy_data, is_init_ns);
kfree(ns->policy_data);
kfree(ns);
}
diff --git a/security/integrity/ima/ima_policy.c b/security/integrity/ima/ima_policy.c
index 12f9dcf73c83..1f60ce9b2ffa 100644
--- a/security/integrity/ima/ima_policy.c
+++ b/security/integrity/ima/ima_policy.c
@@ -46,8 +46,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;
#define MAX_LSM_RULES 6
@@ -198,20 +196,15 @@ static struct ima_rule_entry secure_boot_rules[] __ro_after_init = {
.flags = IMA_FUNC | IMA_DIGSIG_REQUIRED},
};
+/* Number of architecture specific rules found */
+static int arch_entries_size __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 *ima_rules = &ima_default_rules;
-
/* Pre-allocated buffer used for matching keyrings. */
static char *ima_keyrings;
static size_t ima_keyrings_len;
-static int ima_policy __initdata;
-
struct ima_policy_setup_data init_policy_setup_data = {
#ifdef CONFIG_IMA_APPRAISE
.ima_appraise = IMA_APPRAISE_ENFORCE,
@@ -221,29 +214,25 @@ struct ima_policy_data init_policy_data = {
.ima_default_rules = LIST_HEAD_INIT(init_policy_data.ima_default_rules),
.ima_policy_rules = LIST_HEAD_INIT(init_policy_data.ima_policy_rules),
.ima_temp_rules = LIST_HEAD_INIT(init_policy_data.ima_temp_rules),
+ .ima_rules = &init_policy_data.ima_default_rules,
};
int ima_default_measure_policy_setup(const char *str,
struct ima_policy_setup_data *setup_data)
{
- /* Currently unused. It will be implemented after namespacing ima
- * policy, when global variables are removed.
- */
+ if (setup_data->ima_policy)
+ return 1;
+
+ setup_data->ima_policy = ORIGINAL_TCB;
return 1;
}
static int __init default_measure_policy_setup(char *str)
{
- if (ima_policy)
- return 1;
-
- ima_policy = ORIGINAL_TCB;
- return 1;
+ return ima_default_measure_policy_setup(str, &init_policy_setup_data);
}
__setup("ima_tcb", default_measure_policy_setup);
-static bool ima_use_appraise_tcb __initdata;
-static bool ima_use_secure_boot __initdata;
static bool ima_fail_unverifiable_sigs __ro_after_init;
/**
@@ -251,53 +240,47 @@ static bool ima_fail_unverifiable_sigs __ro_after_init;
* @str: string to be parsed
* @setup_data: pointer to a structure where parsed data is stored
* @fail_unverifiable_sigs: boolean flag treated separately to preserve
- * __ro_after_init
+ * __ro_after_init
*/
int ima_policy_setup(char *str,
struct ima_policy_setup_data *setup_data,
bool *fail_unverifiable_sigs)
-{
-
- /* Currently unused. It will be implemented after namespacing ima
- * policy, when global variables are removed.
- */
- return 1;
-}
-
-static int __init policy_setup(char *str)
{
char *p;
while ((p = strsep(&str, " |\n")) != NULL) {
if (*p == ' ')
continue;
- if ((strcmp(p, "tcb") == 0) && !ima_policy)
- ima_policy = DEFAULT_TCB;
+ if ((strcmp(p, "tcb") == 0) && !setup_data->ima_policy)
+ setup_data->ima_policy = DEFAULT_TCB;
else if (strcmp(p, "appraise_tcb") == 0)
- ima_use_appraise_tcb = true;
+ setup_data->ima_use_appraise_tcb = true;
else if (strcmp(p, "secure_boot") == 0)
- ima_use_secure_boot = true;
+ setup_data->ima_use_secure_boot = true;
else if (strcmp(p, "fail_securely") == 0)
- ima_fail_unverifiable_sigs = true;
+ *fail_unverifiable_sigs = true;
}
return 1;
}
+
+static int __init policy_setup(char *str)
+{
+ return ima_policy_setup(str, &init_policy_setup_data,
+ &ima_fail_unverifiable_sigs);
+}
__setup("ima_policy=", policy_setup);
int ima_default_appraise_policy_setup(const char *str,
struct ima_policy_setup_data *setup_data)
{
- /* Currently unused. It will be implemented after namespacing ima
- * policy, when global variables are removed.
- */
+ setup_data->ima_use_appraise_tcb = true;
return 1;
}
static int __init default_appraise_policy_setup(char *str)
{
- ima_use_appraise_tcb = true;
- return 1;
+ return ima_default_appraise_policy_setup(str, &init_policy_setup_data);
}
__setup("ima_appraise_tcb", default_appraise_policy_setup);
@@ -407,9 +390,11 @@ static bool ima_rule_contains_lsm_cond(struct ima_rule_entry *entry)
static void ima_lsm_update_rules(void)
{
struct ima_rule_entry *entry, *e;
+ struct ima_namespace *ima_ns = get_current_ns();
int result;
- list_for_each_entry_safe(entry, e, &ima_policy_rules, list) {
+ list_for_each_entry_safe(entry, e,
+ &ima_ns->policy_data->ima_policy_rules, list) {
if (!ima_rule_contains_lsm_cond(entry))
continue;
@@ -615,12 +600,13 @@ int ima_match_policy(struct inode *inode, const struct cred *cred, u32 secid,
{
struct ima_rule_entry *entry;
int action = 0, actmask = flags | (flags << 1);
+ bool fail_unverifiable_sigs;
if (template_desc)
*template_desc = ima_template_desc_current();
rcu_read_lock();
- list_for_each_entry_rcu(entry, ima_rules, list) {
+ list_for_each_entry_rcu(entry, ima_ns->policy_data->ima_rules, list) {
if (!(entry->action & actmask))
continue;
@@ -635,7 +621,10 @@ int ima_match_policy(struct inode *inode, const struct cred *cred, u32 secid,
if (entry->action & IMA_APPRAISE) {
action |= get_subaction(entry, func);
action &= ~IMA_HASH;
- if (ima_fail_unverifiable_sigs)
+ fail_unverifiable_sigs = (ima_ns == &init_ima_ns) ?
+ ima_fail_unverifiable_sigs :
+ ima_ns->policy_data->ima_fail_unverifiable_sigs;
+ if (fail_unverifiable_sigs)
action |= IMA_FAIL_UNVERIFIABLE_SIGS;
}
@@ -670,14 +659,15 @@ void ima_update_policy_flag(struct ima_namespace *ima_ns)
{
struct ima_rule_entry *entry;
- list_for_each_entry(entry, ima_rules, list) {
+ list_for_each_entry(entry, ima_ns->policy_data->ima_rules, list) {
if (entry->action & IMA_DO_MASK)
- ima_policy_flag |= entry->action;
+ ima_ns->policy_data->ima_policy_flag |= entry->action;
}
- ima_appraise |= (build_ima_appraise | temp_ima_appraise);
- if (!ima_appraise)
- ima_policy_flag &= ~IMA_APPRAISE;
+ ima_ns->policy_data->ima_appraise |=
+ (build_ima_appraise | ima_ns->policy_data->temp_ima_appraise);
+ if (!ima_ns->policy_data->ima_appraise)
+ ima_ns->policy_data->ima_policy_flag &= ~IMA_APPRAISE;
}
static int ima_appraise_flag(enum ima_hooks func)
@@ -693,7 +683,7 @@ static int ima_appraise_flag(enum ima_hooks func)
return 0;
}
-static void add_rules(struct ima_policy_data *policy_data,
+static void add_rules(struct ima_namespace *ima_ns,
struct ima_rule_entry *entries, int count,
enum policy_rule_list policy_rule)
{
@@ -702,8 +692,18 @@ static void add_rules(struct ima_policy_data *policy_data,
for (i = 0; i < count; i++) {
struct ima_rule_entry *entry;
- if (policy_rule & IMA_DEFAULT_POLICY)
- list_add_tail(&entries[i].list, &ima_default_rules);
+ if (policy_rule & IMA_DEFAULT_POLICY) {
+ entry = &entries[i];
+ if (ima_ns != &init_ima_ns) {
+ entry = kmemdup(&entries[i], sizeof(*entry),
+ GFP_KERNEL);
+ if (!entry)
+ continue;
+ }
+
+ list_add_tail(&entry->list,
+ &ima_ns->policy_data->ima_default_rules);
+ }
if (policy_rule & IMA_CUSTOM_POLICY) {
entry = kmemdup(&entries[i], sizeof(*entry),
@@ -711,11 +711,12 @@ static void add_rules(struct ima_policy_data *policy_data,
if (!entry)
continue;
- list_add_tail(&entry->list, &ima_policy_rules);
+ list_add_tail(&entry->list,
+ &ima_ns->policy_data->ima_policy_rules);
}
if (entries[i].action == APPRAISE) {
if (entries != build_appraise_rules)
- temp_ima_appraise |=
+ ima_ns->policy_data->temp_ima_appraise |=
ima_appraise_flag(entries[i].func);
else
build_ima_appraise |=
@@ -775,63 +776,58 @@ static int __init ima_init_arch_policy(void)
void ima_init_ns_policy(struct ima_namespace *ima_ns,
const struct ima_policy_setup_data *setup_data)
{
- /* Set policy rules to the empty set of default rules. The rest will be
- * implemented after namespacing policy.
- */
- ima_ns->policy_data->ima_rules =
- &ima_ns->policy_data->ima_default_rules;
-}
+ int build_appraise_entries;
-/**
- * ima_init_policy - initialize the default measure rules.
- *
- * ima_rules points to either the ima_default_rules or the
- * the new ima_policy_rules.
- */
-void __init ima_init_policy(void)
-{
- int build_appraise_entries, arch_entries;
+ ima_ns->policy_data->ima_appraise = setup_data->ima_appraise;
+
+ if (ima_ns == &init_ima_ns) {
+ /*
+ * Based on runtime secure boot flags, insert arch specific
+ * measurement and appraise rules requiring file signatures for
+ * both the initial and custom policies, prior to other
+ * appraise rules. (Highest priority)
+ */
+ arch_entries_size = ima_init_arch_policy();
+ if (!arch_entries_size)
+ pr_info("No architecture policies found\n");
+
+ ima_ns->policy_data->ima_fail_unverifiable_sigs =
+ ima_fail_unverifiable_sigs;
+ }
/* if !ima_policy, we load NO default rules */
- if (ima_policy)
- add_rules(NULL,
- dont_measure_rules, ARRAY_SIZE(dont_measure_rules),
+ if (setup_data->ima_policy)
+ add_rules(ima_ns, dont_measure_rules,
+ ARRAY_SIZE(dont_measure_rules),
IMA_DEFAULT_POLICY);
- switch (ima_policy) {
+ switch (setup_data->ima_policy) {
case ORIGINAL_TCB:
- add_rules(NULL, original_measurement_rules,
+ add_rules(ima_ns, original_measurement_rules,
ARRAY_SIZE(original_measurement_rules),
IMA_DEFAULT_POLICY);
break;
case DEFAULT_TCB:
- add_rules(NULL, default_measurement_rules,
+ add_rules(ima_ns, default_measurement_rules,
ARRAY_SIZE(default_measurement_rules),
IMA_DEFAULT_POLICY);
default:
break;
}
- /*
- * Based on runtime secure boot flags, insert arch specific measurement
- * and appraise rules requiring file signatures for both the initial
- * and custom policies, prior to other appraise rules.
- * (Highest priority)
- */
- arch_entries = ima_init_arch_policy();
- if (!arch_entries)
- pr_info("No architecture policies found\n");
- else
- add_rules(NULL, arch_policy_entry, arch_entries,
+ if (arch_entries_size)
+ add_rules(ima_ns,
+ arch_policy_entry,
+ arch_entries_size,
IMA_DEFAULT_POLICY | IMA_CUSTOM_POLICY);
/*
* Insert the builtin "secure_boot" policy rules requiring file
* signatures, prior to other appraise rules.
*/
- if (ima_use_secure_boot)
- add_rules(NULL,
- secure_boot_rules, ARRAY_SIZE(secure_boot_rules),
+ if (setup_data->ima_use_secure_boot)
+ add_rules(ima_ns, secure_boot_rules,
+ ARRAY_SIZE(secure_boot_rules),
IMA_DEFAULT_POLICY);
/*
@@ -841,29 +837,39 @@ void __init ima_init_policy(void)
* rules, include either one or the other set of rules, but not both.
*/
build_appraise_entries = ARRAY_SIZE(build_appraise_rules);
- if (build_appraise_entries) {
- if (ima_use_secure_boot)
- add_rules(NULL,
- build_appraise_rules, build_appraise_entries,
+ if (build_appraise_entries && (ima_ns == &init_ima_ns)) {
+ if (setup_data->ima_use_secure_boot)
+ add_rules(ima_ns, build_appraise_rules,
+ build_appraise_entries,
IMA_CUSTOM_POLICY);
else
- add_rules(NULL,
- build_appraise_rules, build_appraise_entries,
+ add_rules(ima_ns, build_appraise_rules,
+ build_appraise_entries,
IMA_DEFAULT_POLICY | IMA_CUSTOM_POLICY);
}
- if (ima_use_appraise_tcb)
- add_rules(NULL, default_appraise_rules,
+ if (setup_data->ima_use_appraise_tcb)
+ add_rules(ima_ns, default_appraise_rules,
ARRAY_SIZE(default_appraise_rules),
IMA_DEFAULT_POLICY);
- ima_update_policy_flag(NULL);
+ ima_ns->policy_data->ima_rules =
+ &ima_ns->policy_data->ima_default_rules;
+ ima_update_policy_flag(ima_ns);
+}
+
+/**
+ * ima_init_policy - initialize the default measure rules for the initial ima ns
+ */
+void __init ima_init_policy(void)
+{
+ ima_init_ns_policy(&init_ima_ns, &init_policy_setup_data);
}
/* Make sure we have a valid policy, at least containing some rules. */
int ima_check_policy(const struct ima_namespace *ima_ns)
{
- if (list_empty(&ima_temp_rules))
+ if (list_empty(&ima_ns->policy_data->ima_temp_rules))
return -EINVAL;
return 0;
}
@@ -881,14 +887,18 @@ int ima_check_policy(const struct ima_namespace *ima_ns)
*/
void ima_update_policy(void)
{
- struct list_head *policy = &ima_policy_rules;
+ /* Update only the current ima namespace */
+ struct ima_namespace *ima_ns = get_current_ns();
+ struct list_head *policy = &ima_ns->policy_data->ima_policy_rules;
- list_splice_tail_init_rcu(&ima_temp_rules, policy, synchronize_rcu);
+ list_splice_tail_init_rcu(&ima_ns->policy_data->ima_temp_rules,
+ policy, synchronize_rcu);
- if (ima_rules != policy) {
- ima_policy_flag = 0;
- ima_rules = policy;
+ if (ima_ns->policy_data->ima_rules != policy) {
+ ima_ns->policy_data->ima_policy_flag = 0;
+ ima_ns->policy_data->ima_rules = policy;
+#ifndef CONFIG_IMA_NS
/*
* IMA architecture specific policy rules are specified
* as strings and converted to an array of ima_entry_rules
@@ -896,8 +906,9 @@ void ima_update_policy(void)
* architecture specific rules stored as an array.
*/
kfree(arch_policy_entry);
+#endif
}
- ima_update_policy_flag(NULL);
+ ima_update_policy_flag(ima_ns);
/* Custom IMA policy has been loaded */
ima_process_queued_keys();
@@ -960,6 +971,7 @@ static int ima_lsm_rule_init(struct ima_rule_entry *entry,
substring_t *args, int lsm_rule, int audit_type)
{
int result;
+ struct ima_namespace *ima_ns = get_current_ns();
if (entry->lsm[lsm_rule].rule)
return -EINVAL;
@@ -976,7 +988,8 @@ static int ima_lsm_rule_init(struct ima_rule_entry *entry,
pr_warn("rule for LSM \'%s\' is undefined\n",
entry->lsm[lsm_rule].args_p);
- if (ima_rules == &ima_default_rules) {
+ if (ima_ns->policy_data->ima_rules ==
+ &ima_ns->policy_data->ima_default_rules) {
kfree(entry->lsm[lsm_rule].args_p);
entry->lsm[lsm_rule].args_p = NULL;
result = -EINVAL;
@@ -1137,6 +1150,7 @@ static int ima_parse_rule(char *rule, struct ima_rule_entry *entry)
struct ima_template_desc *template_desc;
int result = 0;
size_t keyrings_len;
+ struct ima_namespace *ima_ns = get_current_ns();
ab = integrity_audit_log_start(audit_context(), GFP_KERNEL,
AUDIT_INTEGRITY_POLICY_RULE);
@@ -1506,7 +1520,8 @@ static int ima_parse_rule(char *rule, struct ima_rule_entry *entry)
if (!result && !ima_validate_rule(entry))
result = -EINVAL;
else if (entry->action == APPRAISE)
- temp_ima_appraise |= ima_appraise_flag(entry->func);
+ ima_ns->policy_data->temp_ima_appraise |=
+ ima_appraise_flag(entry->func);
if (!result && entry->flags & IMA_MODSIG_ALLOWED) {
template_desc = entry->template ? entry->template :
@@ -1533,6 +1548,8 @@ ssize_t ima_parse_add_rule(char *rule)
struct ima_rule_entry *entry;
ssize_t result, len;
int audit_info = 0;
+ /* Add rules only to the current ima namespace */
+ struct ima_namespace *ima_ns = get_current_ns();
p = strsep(&rule, "\n");
len = strlen(p) + 1;
@@ -1559,7 +1576,7 @@ ssize_t ima_parse_add_rule(char *rule)
return result;
}
- list_add_tail(&entry->list, &ima_temp_rules);
+ list_add_tail(&entry->list, &ima_ns->policy_data->ima_temp_rules);
return len;
}
@@ -1571,15 +1588,51 @@ ssize_t ima_parse_add_rule(char *rule)
* ima_delete_rules() at a time.
*/
void ima_delete_rules(void)
+{
+ /* Delete rules only from the current ima namespace */
+ struct ima_namespace *ima_ns = get_current_ns();
+ struct ima_rule_entry *entry, *tmp;
+
+ ima_ns->policy_data->temp_ima_appraise = 0;
+ list_for_each_entry_safe(entry, tmp,
+ &ima_ns->policy_data->ima_temp_rules, list) {
+ list_del(&entry->list);
+ ima_free_rule(entry);
+ }
+}
+
+#ifdef CONFIG_IMA_NS
+/**
+ * ima_delete_ns_rules - delete policy rules and free the memory
+ * @policy_data: a pointer to the policy data of the given namespace
+ * @is_root_ns: indicates if the namespace being cleaned up is the root
+ * namespace
+ *
+ * This function should be called only for the inactive namespace, when it is
+ * being destroyed.
+ */
+void ima_delete_ns_rules(struct ima_policy_data *policy_data,
+ bool is_root_ns)
{
struct ima_rule_entry *entry, *tmp;
- temp_ima_appraise = 0;
- list_for_each_entry_safe(entry, tmp, &ima_temp_rules, list) {
+ /* no locks necessary, namespace is inactive */
+ list_for_each_entry_safe(entry, tmp,
+ &policy_data->ima_policy_rules, list) {
list_del(&entry->list);
ima_free_rule(entry);
}
+
+ if (!is_root_ns) {
+ list_for_each_entry_safe(entry, tmp,
+ &policy_data->ima_default_rules,
+ list) {
+ list_del(&entry->list);
+ ima_free_rule(entry);
+ }
+ }
}
+#endif
#define __ima_hook_stringify(func, str) (#func),
@@ -1603,9 +1656,10 @@ void *ima_policy_start(struct seq_file *m, loff_t *pos)
{
loff_t l = *pos;
struct ima_rule_entry *entry;
+ struct ima_namespace *ima_ns = get_current_ns();
rcu_read_lock();
- list_for_each_entry_rcu(entry, ima_rules, list) {
+ list_for_each_entry_rcu(entry, ima_ns->policy_data->ima_rules, list) {
if (!l--) {
rcu_read_unlock();
return entry;
@@ -1617,6 +1671,7 @@ void *ima_policy_start(struct seq_file *m, loff_t *pos)
void *ima_policy_next(struct seq_file *m, void *v, loff_t *pos)
{
+ struct ima_namespace *ima_ns = get_current_ns();
struct ima_rule_entry *entry = v;
rcu_read_lock();
@@ -1624,7 +1679,7 @@ void *ima_policy_next(struct seq_file *m, void *v, loff_t *pos)
rcu_read_unlock();
(*pos)++;
- return (&entry->list == ima_rules) ? NULL : entry;
+ return (&entry->list == ima_ns->policy_data->ima_rules) ? NULL : entry;
}
void ima_policy_stop(struct seq_file *m, void *v)
@@ -1809,6 +1864,7 @@ int ima_policy_show(struct seq_file *m, void *v)
*/
bool ima_appraise_signature(enum kernel_read_file_id id)
{
+ struct ima_namespace *ima_ns = get_current_ns();
struct ima_rule_entry *entry;
bool found = false;
enum ima_hooks func;
@@ -1819,7 +1875,7 @@ bool ima_appraise_signature(enum kernel_read_file_id id)
func = read_idmap[id] ?: FILE_CHECK;
rcu_read_lock();
- list_for_each_entry_rcu(entry, ima_rules, list) {
+ list_for_each_entry_rcu(entry, ima_ns->policy_data->ima_rules, 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 34ca54ba52b7..542cbe894a99 100644
--- a/security/integrity/ima/ima_queue_keys.c
+++ b/security/integrity/ima/ima_queue_keys.c
@@ -110,8 +110,11 @@ bool ima_queue_key(struct key *keyring, const void *payload,
if (!entry)
return false;
+ /* Queued keys will be processed according to the root IMA namespace
+ * policy, therefore allow queueing only for the root namespace.
+ */
mutex_lock(&ima_keys_lock);
- if (!ima_process_keys) {
+ if (!ima_process_keys && (get_current_ns() == &init_ima_ns)) {
list_add_tail(&entry->list, &ima_keys);
queued = true;
}
@@ -158,12 +161,15 @@ void ima_process_queued_keys(void)
list_for_each_entry_safe(entry, tmp, &ima_keys, list) {
if (!timer_expired)
+ /* Queued keys are always measured according to the
+ * initial namespace policy.
+ */
process_buffer_measurement(NULL, entry->payload,
entry->payload_len,
entry->keyring_name,
KEY_CHECK, 0,
entry->keyring_name,
- NULL);
+ &init_ima_ns);
list_del(&entry->list);
ima_free_key_entry(entry);
}
diff --git a/security/security.c b/security/security.c
index 70a7ad357bc6..9eb78909cc03 100644
--- a/security/security.c
+++ b/security/security.c
@@ -988,7 +988,7 @@ static void inode_free_by_rcu(struct rcu_head *head)
void security_inode_free(struct inode *inode)
{
- integrity_inode_free(inode);
+ ima_inode_free(inode);
call_void_hook(inode_free_security, inode);
/*
* The inode may still be referenced in a path walk and
--
2.20.1
From: Krzysztof Struczynski <[email protected]>
IMA subsystem is configured at boot time using kernel command-line
parameters, e.g.: ima_policy=tcb|appraise_tcb|secure_boot. The same
configuration options should be available for the new ima namespace.
Add new functions to parse configuration string and store parsed data
in the new policy data structures. Don't implement it yet, just add the
dummy interface.
Signed-off-by: Krzysztof Struczynski <[email protected]>
---
security/integrity/ima/ima.h | 10 ++++++++
security/integrity/ima/ima_appraise.c | 9 +++++++
security/integrity/ima/ima_policy.c | 37 +++++++++++++++++++++++++++
3 files changed, 56 insertions(+)
diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h
index 0d88222e3500..4872f193f7a3 100644
--- a/security/integrity/ima/ima.h
+++ b/security/integrity/ima/ima.h
@@ -306,6 +306,16 @@ void *ima_policy_next(struct seq_file *m, void *v, loff_t *pos);
void ima_policy_stop(struct seq_file *m, void *v);
int ima_policy_show(struct seq_file *m, void *v);
+int ima_policy_setup(char *str,
+ struct ima_policy_setup_data *policy_setup_data,
+ bool *fail_unverifiable_sigs);
+int ima_default_measure_policy_setup(const char *str,
+ struct ima_policy_setup_data *setup_data);
+int ima_default_appraise_policy_setup(const char *str,
+ struct ima_policy_setup_data *setup_data);
+int ima_default_appraise_setup(const char *str,
+ struct ima_policy_setup_data *setup_data);
+
/* Appraise integrity measurements */
#define IMA_APPRAISE_ENFORCE 0x01
#define IMA_APPRAISE_FIX 0x02
diff --git a/security/integrity/ima/ima_appraise.c b/security/integrity/ima/ima_appraise.c
index 372d16382960..0632d3881611 100644
--- a/security/integrity/ima/ima_appraise.c
+++ b/security/integrity/ima/ima_appraise.c
@@ -16,6 +16,15 @@
#include "ima.h"
+int ima_default_appraise_setup(const char *str,
+ struct ima_policy_setup_data *setup_data)
+{
+ /* Currently unused. It will be implemented after namespacing ima
+ * policy, when global variables are removed.
+ */
+ return 1;
+}
+
static int __init default_appraise_setup(char *str)
{
#ifdef CONFIG_IMA_APPRAISE_BOOTPARAM
diff --git a/security/integrity/ima/ima_policy.c b/security/integrity/ima/ima_policy.c
index 6b56741ec1c9..403854b18ef2 100644
--- a/security/integrity/ima/ima_policy.c
+++ b/security/integrity/ima/ima_policy.c
@@ -223,6 +223,15 @@ struct ima_policy_data init_policy_data = {
.ima_temp_rules = LIST_HEAD_INIT(init_policy_data.ima_temp_rules),
};
+int ima_default_measure_policy_setup(const char *str,
+ struct ima_policy_setup_data *setup_data)
+{
+ /* Currently unused. It will be implemented after namespacing ima
+ * policy, when global variables are removed.
+ */
+ return 1;
+}
+
static int __init default_measure_policy_setup(char *str)
{
if (ima_policy)
@@ -236,6 +245,25 @@ __setup("ima_tcb", default_measure_policy_setup);
static bool ima_use_appraise_tcb __initdata;
static bool ima_use_secure_boot __initdata;
static bool ima_fail_unverifiable_sigs __ro_after_init;
+
+/**
+ * ima_policy_setup - parse policy configuration string "ima_policy="
+ * @str: string to be parsed
+ * @setup_data: pointer to a structure where parsed data is stored
+ * @fail_unverifiable_sigs: boolean flag treated separately to preserve
+ * __ro_after_init
+ */
+int ima_policy_setup(char *str,
+ struct ima_policy_setup_data *setup_data,
+ bool *fail_unverifiable_sigs)
+{
+
+ /* Currently unused. It will be implemented after namespacing ima
+ * policy, when global variables are removed.
+ */
+ return 1;
+}
+
static int __init policy_setup(char *str)
{
char *p;
@@ -257,6 +285,15 @@ static int __init policy_setup(char *str)
}
__setup("ima_policy=", policy_setup);
+int ima_default_appraise_policy_setup(const char *str,
+ struct ima_policy_setup_data *setup_data)
+{
+ /* Currently unused. It will be implemented after namespacing ima
+ * policy, when global variables are removed.
+ */
+ return 1;
+}
+
static int __init default_appraise_policy_setup(char *str)
{
ima_use_appraise_tcb = true;
--
2.20.1
From: Krzysztof Struczynski <[email protected]>
Collate global variables describing the ima policy in one structure and
add it to the ima namespace. Collate setup data (parsed kernel boot
parameters) in a separate structure.
Per namespace policy is not yet properly set and it is not used. This
will be done in the following patches.
Signed-off-by: Krzysztof Struczynski <[email protected]>
---
include/linux/ima.h | 2 ++
security/integrity/ima/ima.h | 24 +++++++++++++++++
security/integrity/ima/ima_init.c | 3 ++-
security/integrity/ima/ima_ns.c | 41 +++++++++++++++++++++++++++--
security/integrity/ima/ima_policy.c | 26 ++++++++++++++++++
5 files changed, 93 insertions(+), 3 deletions(-)
diff --git a/include/linux/ima.h b/include/linux/ima.h
index 3954cef57c00..d61c9c21ffb9 100644
--- a/include/linux/ima.h
+++ b/include/linux/ima.h
@@ -16,6 +16,7 @@ struct nsproxy;
struct task_struct;
struct list_head;
struct llist_node;
+struct ima_policy_data;
#ifdef CONFIG_IMA
extern int ima_bprm_check(struct linux_binprm *bprm);
@@ -188,6 +189,7 @@ struct ima_namespace {
struct llist_node cleanup_list; /* namespaces on a death row */
atomic_t inactive; /* set only when ns is added to the cleanup list */
bool frozen;
+ struct ima_policy_data *policy_data;
} __randomize_layout;
extern struct ima_namespace init_ima_ns;
diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h
index 092e87190c6d..0d88222e3500 100644
--- a/security/integrity/ima/ima.h
+++ b/security/integrity/ima/ima.h
@@ -60,6 +60,14 @@ extern int ima_appraise;
extern struct tpm_chip *ima_tpm_chip;
extern const char boot_aggregate_name[];
+/* IMA policy setup data */
+struct ima_policy_setup_data {
+ int ima_policy;
+ int ima_appraise;
+ bool ima_use_secure_boot;
+ bool ima_use_appraise_tcb;
+};
+
/* IMA event related data */
struct ima_event_data {
struct integrity_iint_cache *iint;
@@ -286,6 +294,8 @@ int ima_match_policy(struct inode *inode, const struct cred *cred, u32 secid,
struct ima_template_desc **template_desc,
const char *keyring);
void ima_init_policy(void);
+void ima_init_ns_policy(struct ima_namespace *ima_ns,
+ const struct ima_policy_setup_data *policy_setup_data);
void ima_update_policy(void);
void ima_update_policy_flag(void);
ssize_t ima_parse_add_rule(char *);
@@ -372,6 +382,20 @@ static inline int ima_read_xattr(struct dentry *dentry,
#endif /* CONFIG_IMA_APPRAISE */
+struct ima_policy_data {
+ struct list_head ima_default_rules;
+ struct list_head ima_policy_rules;
+ struct list_head ima_temp_rules;
+ struct list_head *ima_rules;
+ bool ima_fail_unverifiable_sigs;
+ int ima_policy_flag; /* current content of the policy */
+ int ima_appraise;
+ int temp_ima_appraise;
+};
+
+extern struct ima_policy_data init_policy_data;
+extern struct ima_policy_setup_data init_policy_setup_data;
+
extern struct list_head ima_ns_list;
extern struct rw_semaphore ima_ns_list_lock;
diff --git a/security/integrity/ima/ima_init.c b/security/integrity/ima/ima_init.c
index 0ba04a1a68cc..ea5ff42eb7fe 100644
--- a/security/integrity/ima/ima_init.c
+++ b/security/integrity/ima/ima_init.c
@@ -32,7 +32,8 @@ struct ima_namespace init_ima_ns = {
#ifdef CONFIG_IMA_NS
.ns.ops = &imans_operations,
#endif
- .frozen = true
+ .frozen = true,
+ .policy_data = &init_policy_data,
};
EXPORT_SYMBOL(init_ima_ns);
diff --git a/security/integrity/ima/ima_ns.c b/security/integrity/ima/ima_ns.c
index 3a98cd536d05..1aeb9cfeb3a2 100644
--- a/security/integrity/ima/ima_ns.c
+++ b/security/integrity/ima/ima_ns.c
@@ -51,9 +51,38 @@ static struct ima_namespace *ima_ns_alloc(void)
ima_ns = kzalloc(sizeof(*ima_ns), GFP_KERNEL);
if (!ima_ns)
- return NULL;
+ goto out;
+
+ ima_ns->policy_data = kzalloc(sizeof(struct ima_policy_data),
+ GFP_KERNEL);
+ if (!ima_ns->policy_data)
+ goto out_free;
return ima_ns;
+
+out_free:
+ kfree(ima_ns);
+out:
+ return NULL;
+}
+
+static void ima_set_ns_policy(struct ima_namespace *ima_ns,
+ char *policy_setup_str)
+{
+ struct ima_policy_setup_data setup_data;
+
+#ifdef CONFIG_IMA_APPRAISE
+ setup_data.ima_appraise = IMA_APPRAISE_ENFORCE;
+#endif
+ /* Configuring IMA namespace will be implemented in the following
+ * patches. When it is done, parse configuration string and store result
+ * in setup_data. Temporarily use init_policy_setup_data.
+ */
+ setup_data = init_policy_setup_data;
+ ima_ns->policy_data->ima_fail_unverifiable_sigs =
+ init_ima_ns.policy_data->ima_fail_unverifiable_sigs;
+
+ ima_init_ns_policy(ima_ns, &setup_data);
}
/**
@@ -64,7 +93,7 @@ static struct ima_namespace *ima_ns_alloc(void)
* Return: ERR_PTR(-ENOMEM) on error (failure to kmalloc), new ns otherwise
*/
static struct ima_namespace *clone_ima_ns(struct user_namespace *user_ns,
- struct ima_namespace *old_ns)
+ struct ima_namespace *old_ns)
{
struct ima_namespace *ns;
struct ucounts *ucounts;
@@ -91,9 +120,14 @@ static struct ima_namespace *clone_ima_ns(struct user_namespace *user_ns,
ns->ucounts = ucounts;
ns->frozen = false;
+ INIT_LIST_HEAD(&ns->policy_data->ima_default_rules);
+ INIT_LIST_HEAD(&ns->policy_data->ima_policy_rules);
+ INIT_LIST_HEAD(&ns->policy_data->ima_temp_rules);
+
return ns;
fail_free:
+ kfree(ns->policy_data);
kfree(ns);
fail_dec:
dec_ima_namespaces(ucounts);
@@ -139,6 +173,7 @@ static void destroy_ima_ns(struct ima_namespace *ns)
dec_ima_namespaces(ns->ucounts);
put_user_ns(ns->user_ns);
ns_free_inum(&ns->ns);
+ kfree(ns->policy_data);
kfree(ns);
}
@@ -238,6 +273,8 @@ static int imans_activate(struct ima_namespace *ima_ns)
if (ima_ns->frozen)
goto out;
+ ima_set_ns_policy(ima_ns, NULL);
+
ima_ns->frozen = true;
down_write(&ima_ns_list_lock);
diff --git a/security/integrity/ima/ima_policy.c b/security/integrity/ima/ima_policy.c
index 07f033634b27..6b56741ec1c9 100644
--- a/security/integrity/ima/ima_policy.c
+++ b/security/integrity/ima/ima_policy.c
@@ -212,6 +212,17 @@ static size_t ima_keyrings_len;
static int ima_policy __initdata;
+struct ima_policy_setup_data init_policy_setup_data = {
+#ifdef CONFIG_IMA_APPRAISE
+ .ima_appraise = IMA_APPRAISE_ENFORCE,
+#endif
+};
+struct ima_policy_data init_policy_data = {
+ .ima_default_rules = LIST_HEAD_INIT(init_policy_data.ima_default_rules),
+ .ima_policy_rules = LIST_HEAD_INIT(init_policy_data.ima_policy_rules),
+ .ima_temp_rules = LIST_HEAD_INIT(init_policy_data.ima_temp_rules),
+};
+
static int __init default_measure_policy_setup(char *str)
{
if (ima_policy)
@@ -715,6 +726,21 @@ static int __init ima_init_arch_policy(void)
return i;
}
+/**
+ * ima_init_ns_policy - initialize the default measure rules.
+ * @ima_ns: pointer to the namespace whose rules are being initialized
+ * @setup_data: pointer to the policy setup data
+ */
+void ima_init_ns_policy(struct ima_namespace *ima_ns,
+ const struct ima_policy_setup_data *setup_data)
+{
+ /* Set policy rules to the empty set of default rules. The rest will be
+ * implemented after namespacing policy.
+ */
+ ima_ns->policy_data->ima_rules =
+ &ima_ns->policy_data->ima_default_rules;
+}
+
/**
* ima_init_policy - initialize the default measure rules.
*
--
2.20.1
From: Krzysztof Struczynski <[email protected]>
Add a list of the installed ima namespaces. IMA namespace is considered
installed, if there is at least one process born in that namespace.
This list will be used to check the read-write violations and to detect
any object related changes relevant across namespaces.
Signed-off-by: Krzysztof Struczynski <[email protected]>
---
include/linux/ima.h | 8 ++-
security/integrity/ima/ima.h | 11 ++++
security/integrity/ima/ima_init.c | 5 ++
security/integrity/ima/ima_main.c | 3 +
security/integrity/ima/ima_ns.c | 101 ++++++++++++++++++++++++++++--
5 files changed, 122 insertions(+), 6 deletions(-)
diff --git a/include/linux/ima.h b/include/linux/ima.h
index 4a9c29d4d056..5b6235b97603 100644
--- a/include/linux/ima.h
+++ b/include/linux/ima.h
@@ -10,10 +10,12 @@
#include <linux/fs.h>
#include <linux/security.h>
#include <linux/kexec.h>
-struct linux_binprm;
+struct linux_binprm;
struct nsproxy;
struct task_struct;
+struct list_head;
+struct llist_node;
#ifdef CONFIG_IMA
extern int ima_bprm_check(struct linux_binprm *bprm);
@@ -176,6 +178,10 @@ struct ima_namespace {
struct ns_common ns;
struct ucounts *ucounts;
struct user_namespace *user_ns;
+ struct list_head list;
+ struct llist_node cleanup_list; /* namespaces on a death row */
+ atomic_t inactive; /* set only when ns is added to the cleanup list */
+ bool frozen;
} __randomize_layout;
extern struct ima_namespace init_ima_ns;
diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h
index 603da5b2db08..092e87190c6d 100644
--- a/security/integrity/ima/ima.h
+++ b/security/integrity/ima/ima.h
@@ -372,12 +372,23 @@ static inline int ima_read_xattr(struct dentry *dentry,
#endif /* CONFIG_IMA_APPRAISE */
+extern struct list_head ima_ns_list;
+extern struct rw_semaphore ima_ns_list_lock;
+
#ifdef CONFIG_IMA_NS
+int __init ima_init_namespace(void);
+
static inline struct ima_namespace *get_current_ns(void)
{
return current->nsproxy->ima_ns;
}
#else
+static inline int __init ima_init_namespace(void)
+{
+ list_add_tail(&init_ima_ns.list, &ima_ns_list);
+ return 0;
+}
+
static inline struct ima_namespace *get_current_ns(void)
{
return &init_ima_ns;
diff --git a/security/integrity/ima/ima_init.c b/security/integrity/ima/ima_init.c
index 013bbec16849..0ba04a1a68cc 100644
--- a/security/integrity/ima/ima_init.c
+++ b/security/integrity/ima/ima_init.c
@@ -32,6 +32,7 @@ struct ima_namespace init_ima_ns = {
#ifdef CONFIG_IMA_NS
.ns.ops = &imans_operations,
#endif
+ .frozen = true
};
EXPORT_SYMBOL(init_ima_ns);
@@ -154,6 +155,10 @@ int __init ima_init(void)
ima_init_policy();
+ rc = ima_init_namespace();
+ if (rc != 0)
+ return rc;
+
rc = ima_fs_init();
if (rc != 0)
return rc;
diff --git a/security/integrity/ima/ima_main.c b/security/integrity/ima/ima_main.c
index 8a91711ca79b..d800e73c8b62 100644
--- a/security/integrity/ima/ima_main.c
+++ b/security/integrity/ima/ima_main.c
@@ -37,6 +37,9 @@ int ima_appraise;
int ima_hash_algo = HASH_ALGO_SHA1;
static int hash_setup_done;
+DECLARE_RWSEM(ima_ns_list_lock);
+LIST_HEAD(ima_ns_list);
+
static struct notifier_block ima_lsm_policy_notifier = {
.notifier_call = ima_lsm_policy_change,
};
diff --git a/security/integrity/ima/ima_ns.c b/security/integrity/ima/ima_ns.c
index 8f5f301406a2..3a98cd536d05 100644
--- a/security/integrity/ima/ima_ns.c
+++ b/security/integrity/ima/ima_ns.c
@@ -21,9 +21,20 @@
#include <linux/user_namespace.h>
#include <linux/nsproxy.h>
#include <linux/sched.h>
+#include <linux/list.h>
+#include <linux/llist.h>
+#include <linux/rwsem.h>
+#include <linux/workqueue.h>
+#include <linux/mutex.h>
#include "ima.h"
+static LLIST_HEAD(cleanup_list);
+static struct workqueue_struct *imans_wq;
+
+/* Protects tasks entering the same, not yet active namespace */
+static DEFINE_MUTEX(frozen_lock);
+
static struct ucounts *inc_ima_namespaces(struct user_namespace *ns)
{
return inc_ucount(ns, current_euid(), UCOUNT_IMA_NAMESPACES);
@@ -78,6 +89,7 @@ static struct ima_namespace *clone_ima_ns(struct user_namespace *user_ns,
ns->ns.ops = &imans_operations;
ns->user_ns = get_user_ns(user_ns);
ns->ucounts = ucounts;
+ ns->frozen = false;
return ns;
@@ -109,6 +121,19 @@ struct ima_namespace *copy_ima_ns(unsigned long flags,
return clone_ima_ns(user_ns, old_ns);
}
+int __init ima_init_namespace(void)
+{
+ /* Create workqueue for cleanup */
+ imans_wq = create_singlethread_workqueue("imans");
+ if (unlikely(!imans_wq))
+ return -ENOMEM;
+
+ /* No other reader or writer at this stage */
+ list_add_tail(&init_ima_ns.list, &ima_ns_list);
+
+ return 0;
+}
+
static void destroy_ima_ns(struct ima_namespace *ns)
{
dec_ima_namespaces(ns->ucounts);
@@ -117,13 +142,46 @@ static void destroy_ima_ns(struct ima_namespace *ns)
kfree(ns);
}
+static void cleanup_ima(struct work_struct *work)
+{
+ struct ima_namespace *ima_ns, *tmp;
+ struct llist_node *ima_kill_list;
+
+ /* Atomically snapshot the list of namespaces to cleanup */
+ ima_kill_list = llist_del_all(&cleanup_list);
+
+ /* Remove ima namespace from the namespace list */
+ down_write(&ima_ns_list_lock);
+ llist_for_each_entry(ima_ns, ima_kill_list, cleanup_list)
+ list_del(&ima_ns->list);
+ up_write(&ima_ns_list_lock);
+
+ /* After removing ima namespace from the ima_ns_list, memory can be
+ * freed. At this stage nothing should keep a reference to the given
+ * namespace.
+ */
+ llist_for_each_entry_safe(ima_ns, tmp, ima_kill_list, cleanup_list)
+ destroy_ima_ns(ima_ns);
+}
+
+static DECLARE_WORK(ima_cleanup_work, cleanup_ima);
+
void free_ima_ns(struct kref *kref)
{
- struct ima_namespace *ns;
+ struct ima_namespace *ima_ns;
- ns = container_of(kref, struct ima_namespace, kref);
+ ima_ns = container_of(kref, struct ima_namespace, kref);
+ /* Namespace can be destroyed instantly if no process ever was born
+ * into it - it was never added to the ima_ns_list.
+ */
+ if (!ima_ns->frozen) {
+ destroy_ima_ns(ima_ns);
+ return;
+ }
- destroy_ima_ns(ns);
+ atomic_set(&ima_ns->inactive, 1);
+ if (llist_add(&ima_ns->cleanup_list, &cleanup_list))
+ queue_work(imans_wq, &ima_cleanup_work);
}
static inline struct ima_namespace *to_ima_ns(struct ns_common *ns)
@@ -168,8 +226,32 @@ static void imans_put(struct ns_common *ns)
put_ima_ns(to_ima_ns(ns));
}
+static int imans_activate(struct ima_namespace *ima_ns)
+{
+ if (ima_ns == &init_ima_ns)
+ return 0;
+
+ if (ima_ns->frozen)
+ return 0;
+
+ mutex_lock(&frozen_lock);
+ if (ima_ns->frozen)
+ goto out;
+
+ ima_ns->frozen = true;
+
+ down_write(&ima_ns_list_lock);
+ list_add_tail(&ima_ns->list, &ima_ns_list);
+ up_write(&ima_ns_list_lock);
+out:
+ mutex_unlock(&frozen_lock);
+
+ return 0;
+}
+
static int imans_install(struct nsset *nsset, struct ns_common *new)
{
+ int res;
struct nsproxy *nsproxy = nsset->nsproxy;
struct ima_namespace *ns = to_ima_ns(new);
@@ -180,6 +262,10 @@ static int imans_install(struct nsset *nsset, struct ns_common *new)
!ns_capable(nsset->cred->user_ns, CAP_SYS_ADMIN))
return -EPERM;
+ res = imans_activate(ns);
+ if (res)
+ return res;
+
get_ima_ns(ns);
put_ima_ns(nsproxy->ima_ns);
nsproxy->ima_ns = ns;
@@ -188,11 +274,12 @@ static int imans_install(struct nsset *nsset, struct ns_common *new)
put_ima_ns(nsproxy->ima_ns_for_children);
nsproxy->ima_ns_for_children = ns;
- return 0;
+ return res;
}
int imans_on_fork(struct nsproxy *nsproxy, struct task_struct *tsk)
{
+ int res;
struct ns_common *nsc = &nsproxy->ima_ns_for_children->ns;
struct ima_namespace *ns = to_ima_ns(nsc);
@@ -200,11 +287,15 @@ int imans_on_fork(struct nsproxy *nsproxy, struct task_struct *tsk)
if (nsproxy->ima_ns == nsproxy->ima_ns_for_children)
return 0;
+ res = imans_activate(ns);
+ if (res)
+ return res;
+
get_ima_ns(ns);
put_ima_ns(nsproxy->ima_ns);
nsproxy->ima_ns = ns;
- return 0;
+ return res;
}
static struct user_namespace *imans_owner(struct ns_common *ns)
--
2.20.1
On Tue, Aug 18, 2020 at 05:20:07PM +0200, [email protected] wrote:
> From: Krzysztof Struczynski <[email protected]>
>
> IMA has not been designed to work with containers. It handles every
> process in the same way, and it cannot distinguish if a process belongs to
> a container or not.
>
> Containers use namespaces to make it appear to the processes in the
> containers that they have their own isolated instance of the global
> resource. For IMA as well, it is desirable to let processes in the
> containers have IMA functionality independent from other containers:
> separate policy rules, measurement list, additional appraisal keys to
> verify the container image, separate audit logs.
>
> As previous work done in this area, this patch series introduces the IMA
> namespace, which is a separate instance of IMA to handle a subset of
> processes that belong to a container.
>
> The IMA namespace is created using clone3() or unshare() system calls. It
> is important to configure the namespace before any process appears in it,
> so that the new policy rules apply to the very first process in the
> namespace. To achieve that, the intermediate namespace ima_ns_for_children
> is used. It stores the configuration and becomes active on the next fork
> or when the first process enters it using the setns() system call. The
> similar process is used for the time namespace.
>
> The IMA namespace can be configured using the new securityfs directory
> entries that allow the user to set the policy rules, x509 certificate for
> appraisal and pass IMA configuration parameters normally included in the
> kernel command line parameters. It is intended to extend the clone_args to
> allow configuration from clone3() syscall.
Not to be the downer right away but just as an fyi, if this patchset
makes it, clone3() will not allow to be extended with any real
second-level pointers. That will see a hard NAK from me and several
other maintainers.
Christian
On Tue, 2020-08-18 at 17:20 +0200, [email protected]
wrote:
> The measurement list remains global, with the assumption that there
> is only one TPM in the system. Each IMA namespace has a unique ID,
> that allows to track measurements per IMA namespace. Processes in one
> namespace, have access only to the measurements from that namespace.
> The exception is made for the initial IMA namespace, whose processes
> have access to all entries.
So I think this can work in the use case where the system owner is
responsible for doing the logging and attestation and the tenants just
trust the owner without requiring an attestation. However, in a multi-
tenant system you need a way for the attestation to be per-container
(because the combined list of who executed what would be a security
leak between tenants). Since we can't virtualise the PCRs without
introducing a vtpm this is going to require a vtpm infrastructure like
that used for virtual machines and then we can do IMA logging per
container.
I don't think the above has to be in your first patch set, we just have
to have an idea of how it could be done to show that nothing in this
patch set precludes a follow on from doing this.
James
On Tue, Aug 18, 2020 at 05:20:07PM +0200, [email protected] wrote:
> From: Krzysztof Struczynski <[email protected]>
>
> IMA has not been designed to work with containers. It handles every
> process in the same way, and it cannot distinguish if a process belongs to
> a container or not.
>
> Containers use namespaces to make it appear to the processes in the
> containers that they have their own isolated instance of the global
> resource. For IMA as well, it is desirable to let processes in the
IMA is brought up on a regular basis with "we want to have this" for
years and then non-one seems to really care enough.
I'm highly skeptical of the value of ~2500 lines of code even if it
includes a bunch of namespace boilerplate. It's yet another namespace,
and yet another security framework.
Why does IMA need to be a separate namespace? Keyrings are tied to user
namespaces why can't IMA be? I believe Eric has even pointed that out
before.
Eric, thoughts?
Christian
> From: James Bottomley [mailto:[email protected]]
> On Tue, 2020-08-18 at 17:20 +0200, [email protected]
> wrote:
> > The measurement list remains global, with the assumption that there
> > is only one TPM in the system. Each IMA namespace has a unique ID,
> > that allows to track measurements per IMA namespace. Processes in one
> > namespace, have access only to the measurements from that namespace.
> > The exception is made for the initial IMA namespace, whose processes
> > have access to all entries.
>
> So I think this can work in the use case where the system owner is
> responsible for doing the logging and attestation and the tenants just
> trust the owner without requiring an attestation. However, in a multi-
> tenant system you need a way for the attestation to be per-container
> (because the combined list of who executed what would be a security
> leak between tenants). Since we can't virtualise the PCRs without
> introducing a vtpm this is going to require a vtpm infrastructure like
> that used for virtual machines and then we can do IMA logging per
> container.
I agree and wonder if we should decouple the attestation trust model,
which depends on the specific use case (e.g. multi/single tenant,
public/private cloud), from the IMA logic of linking the measurements to
the container. Indeed, attestation from within the container might require
anchoring to a vTPM/vPCR and the current measurement tagging mechanism can
support several ways of anchoring them to a (virtual) root of trust.
> I don't think the above has to be in your first patch set, we just have
> to have an idea of how it could be done to show that nothing in this
> patch set precludes a follow on from doing this.
Given that virtualizing trust anchors seems like a separate problem in
which industry consensus is not easy to reach for all use cases, an
anchoring mechanism should probably be a separate IMA feature.
>
> James
> From: Christian Brauner [mailto:[email protected]]
> On Tue, Aug 18, 2020 at 05:20:07PM +0200, [email protected]
> wrote:
> > From: Krzysztof Struczynski <[email protected]>
> >
> > IMA has not been designed to work with containers. It handles every
> > process in the same way, and it cannot distinguish if a process belongs to
> > a container or not.
> >
> > Containers use namespaces to make it appear to the processes in the
> > containers that they have their own isolated instance of the global
> > resource. For IMA as well, it is desirable to let processes in the
> > containers have IMA functionality independent from other containers:
> > separate policy rules, measurement list, additional appraisal keys to
> > verify the container image, separate audit logs.
> >
> > As previous work done in this area, this patch series introduces the IMA
> > namespace, which is a separate instance of IMA to handle a subset of
> > processes that belong to a container.
> >
> > The IMA namespace is created using clone3() or unshare() system calls. It
> > is important to configure the namespace before any process appears in it,
> > so that the new policy rules apply to the very first process in the
> > namespace. To achieve that, the intermediate namespace
> ima_ns_for_children
> > is used. It stores the configuration and becomes active on the next fork
> > or when the first process enters it using the setns() system call. The
> > similar process is used for the time namespace.
> >
> > The IMA namespace can be configured using the new securityfs directory
> > entries that allow the user to set the policy rules, x509 certificate for
> > appraisal and pass IMA configuration parameters normally included in the
> > kernel command line parameters. It is intended to extend the clone_args to
> > allow configuration from clone3() syscall.
>
> Not to be the downer right away but just as an fyi, if this patchset
> makes it, clone3() will not allow to be extended with any real
> second-level pointers. That will see a hard NAK from me and several
> other maintainers.
Ok, that's a good point. It can be done without the second-level pointers
but if that's not desirable then IMA namespace creation via a direct
clone3() call can be removed. It will make the process less flexible but
it will still work with unshare() and clone3() or unshare() and setns()
calls.
>
> Christian
> From: Christian Brauner [mailto:[email protected]]
> On Tue, Aug 18, 2020 at 05:20:07PM +0200, [email protected]
> wrote:
> > From: Krzysztof Struczynski <[email protected]>
> >
> > IMA has not been designed to work with containers. It handles every
> > process in the same way, and it cannot distinguish if a process belongs to
> > a container or not.
> >
> > Containers use namespaces to make it appear to the processes in the
> > containers that they have their own isolated instance of the global
> > resource. For IMA as well, it is desirable to let processes in the
>
> IMA is brought up on a regular basis with "we want to have this" for
> years and then non-one seems to really care enough.
>
> I'm highly skeptical of the value of ~2500 lines of code even if it
> includes a bunch of namespace boilerplate. It's yet another namespace,
> and yet another security framework.
> Why does IMA need to be a separate namespace? Keyrings are tied to user
> namespaces why can't IMA be? I believe Eric has even pointed that out
> before.
The user namespace has its well defined purpose to isolate
security-related identifiers and attributes, particularly UIDs and GIDs.
I think that IMA goals are different.
A user may want to isolate e.g. UIDs but not to create a separate IML or
define the new IMA policies. On the other hand, especially in the
single-tenant environment, the user may want to have a per container IML,
but no UID/GID mapping is required. IMA policy defines subject-based
rules (uid, euid, subj_*, ...), but also object-based rules.
IMA has to be pre-configured, e.g. all actions of the process have to be
appraised/measured/audited according to the pre-defined policy, appraisal
key has to be available before the process is created, etc. If IMA is tied
to the user namespace, when is a good moment to do it?
What's the argument against adding a new namespace?
>
> Eric, thoughts?
>
> Christian
On Fri, 2020-08-21 at 15:13 +0000, Krzysztof Struczynski wrote:
> > From: James Bottomley [mailto:[email protected]]
> > On Tue, 2020-08-18 at 17:20 +0200, [email protected]
> > wrote:
> > > The measurement list remains global, with the assumption that there
> > > is only one TPM in the system. Each IMA namespace has a unique ID,
> > > that allows to track measurements per IMA namespace. Processes in one
> > > namespace, have access only to the measurements from that namespace.
> > > The exception is made for the initial IMA namespace, whose processes
> > > have access to all entries.
> >
> > So I think this can work in the use case where the system owner is
> > responsible for doing the logging and attestation and the tenants just
> > trust the owner without requiring an attestation. However, in a multi-
> > tenant system you need a way for the attestation to be per-container
> > (because the combined list of who executed what would be a security
> > leak between tenants). Since we can't virtualise the PCRs without
> > introducing a vtpm this is going to require a vtpm infrastructure like
> > that used for virtual machines and then we can do IMA logging per
> > container.
>
> I agree and wonder if we should decouple the attestation trust model,
> which depends on the specific use case (e.g. multi/single tenant,
> public/private cloud), from the IMA logic of linking the measurements to
> the container. Indeed, attestation from within the container might require
> anchoring to a vTPM/vPCR and the current measurement tagging mechanism can
> support several ways of anchoring them to a (virtual) root of trust.
>
> > I don't think the above has to be in your first patch set, we just have
> > to have an idea of how it could be done to show that nothing in this
> > patch set precludes a follow on from doing this.
>
> Given that virtualizing trust anchors seems like a separate problem in
> which industry consensus is not easy to reach for all use cases, an
> anchoring mechanism should probably be a separate IMA feature.
Other trust anchors for "trusted keys" has been discussed, but I wasn't
aware of any discussion about other trust anchors for the IMA
measurement list. The IMA measurement list is very much tied to a TPM.
Including container measurements in the host measurement list, will
unnecessarily cause the host measurement list to grow. The decision of
what should and shouldn't be included in the host measurement list
shouldn't be defined by the container.
Mimi
On Tue, 2020-08-18 at 18:49 +0200, Christian Brauner wrote:
> On Tue, Aug 18, 2020 at 05:20:07PM +0200, [email protected] wrote:
> > From: Krzysztof Struczynski <[email protected]>
> >
> > IMA has not been designed to work with containers. It handles every
> > process in the same way, and it cannot distinguish if a process belongs to
> > a container or not.
> >
> > Containers use namespaces to make it appear to the processes in the
> > containers that they have their own isolated instance of the global
> > resource. For IMA as well, it is desirable to let processes in the
>
> IMA is brought up on a regular basis with "we want to have this" for
> years and then non-one seems to really care enough.
There is a lot of interest in IMA namespacing, but the question always
comes back to how to enable it. Refer to
https://kernsec.org/wiki/index.php/IMA_Namespacing_design_considerations
for Stefan's analysis.
I understand "containers" is not a kernel construct, but from my very
limited perspective, IMA namespacing only makes sense in the context of
a "container". The container owner may want to know which files have
been accessed/executed (measurements, remote attestation) and/or
constrain which files may be accessed/executed based on signatures
(appraisal).
>
> I'm highly skeptical of the value of ~2500 lines of code even if it
> includes a bunch of namespace boilerplate. It's yet another namespace,
> and yet another security framework.
> Why does IMA need to be a separate namespace? Keyrings are tied to user
> namespaces why can't IMA be?
In the context of a container, the measurement list and IMA/EVM
keyrings need to be setup before the first file is measured, signature
verified, or file hash included in the audit log.
> I believe Eric has even pointed that out
> before.
>
> Eric, thoughts?
Any help with the above scenario would very be much appreciated.
Mimi
On Wed, Sep 02, 2020 at 02:53:17PM -0400, Mimi Zohar wrote:
Good morning, I hope the week is ending well for everyone.
> On Fri, 2020-08-21 at 15:13 +0000, Krzysztof Struczynski wrote:
> > > From: James Bottomley [mailto:[email protected]]
> > > On Tue, 2020-08-18 at 17:20 +0200, [email protected]
> > > wrote:
> > > > The measurement list remains global, with the assumption that there
> > > > is only one TPM in the system. Each IMA namespace has a unique ID,
> > > > that allows to track measurements per IMA namespace. Processes in one
> > > > namespace, have access only to the measurements from that namespace.
> > > > The exception is made for the initial IMA namespace, whose processes
> > > > have access to all entries.
> > >
> > > So I think this can work in the use case where the system owner is
> > > responsible for doing the logging and attestation and the tenants just
> > > trust the owner without requiring an attestation. However, in a multi-
> > > tenant system you need a way for the attestation to be per-container
> > > (because the combined list of who executed what would be a security
> > > leak between tenants). Since we can't virtualise the PCRs without
> > > introducing a vtpm this is going to require a vtpm infrastructure like
> > > that used for virtual machines and then we can do IMA logging per
> > > container.
> >
> > I agree and wonder if we should decouple the attestation trust model,
> > which depends on the specific use case (e.g. multi/single tenant,
> > public/private cloud), from the IMA logic of linking the measurements to
> > the container. Indeed, attestation from within the container might require
> > anchoring to a vTPM/vPCR and the current measurement tagging mechanism can
> > support several ways of anchoring them to a (virtual) root of trust.
> >
> > > I don't think the above has to be in your first patch set, we just have
> > > to have an idea of how it could be done to show that nothing in this
> > > patch set precludes a follow on from doing this.
> >
> > Given that virtualizing trust anchors seems like a separate problem in
> > which industry consensus is not easy to reach for all use cases, an
> > anchoring mechanism should probably be a separate IMA feature.
> Other trust anchors for "trusted keys" has been discussed, but I wasn't
> aware of any discussion about other trust anchors for the IMA
> measurement list. The IMA measurement list is very much tied to a TPM.
>
> Including container measurements in the host measurement list, will
> unnecessarily cause the host measurement list to grow. The decision of
> what should and shouldn't be included in the host measurement list
> shouldn't be defined by the container.
We have been shipping, and more importantly maintaining in the wild,
systems with a namespaced IMA implementation for 4+ years now. We
presented the foundations for all of this at the 2015 Linux Security
Summit in Seattle.
For the purposes of further conversation, I should clarify and
indicate that we have been shipping and maintaining what a namespaced
IMA implementation turns into when all of the engineering challenges
have been addressed with respect to workability issues, particularly
in regards to keeping the resultant system from being too fragile to
be effectively deployed and maintained.
If practical experience is worth anything, I don't believe that
namespacing the current IMA implementation is the optimum path
forward. With respect to developing operationally relevant trusted
platforms, the objective needs to be modeling the behavior of
namespaces spawned from a known root behavior.
The current IMA implementation provides a great deal of relevant
infrastructure, but as these conversations have suggested, namespacing
the current implementation is problematic given how entangled it has
become with existing kernel infrastructure. What is needed is
something far simpler that delegates, on the basis of a namespace,
security policy to something other then the kernel, consistent with
what we have learned about policy over the last 29+ years of Linux
development.
With respect to roots of trust, I don't think TPM's/fTPM's, virtual or
otherwise, are going to be the relevant technology moving forward,
although they will be part of the picture.
Mimi has another post down thread that I will provide some more direct
reflections on all of this for whatever value they may have.
> Mimi
Have a good day.
Dr. Greg
As always,
Dr. Greg Wettstein, Ph.D, Worker Autonomously self-defensive
Enjellic Systems Development, LLC IOT platforms and edge devices.
4206 N. 19th Ave.
Fargo, ND 58102
PH: 701-281-1686 EMAIL: [email protected]
------------------------------------------------------------------------------
"I had far rather walk, as I do, in daily terror of eternity, than feel
that this was only a children's game in which all of the contestants
would get equally worthless prizes in the end."
-- T. S. Elliot
On Wed, Sep 02, 2020 at 03:54:58PM -0400, Mimi Zohar wrote:
Good morning, I hope the weekend is going well for everyone.
A follow on to my previous e-mail regarding what 'namespaced IMA'
should look like.
> On Tue, 2020-08-18 at 18:49 +0200, Christian Brauner wrote:
> > On Tue, Aug 18, 2020 at 05:20:07PM +0200, [email protected] wrote:
> > > From: Krzysztof Struczynski <[email protected]>
> > > IMA has not been designed to work with containers. It handles
> > > every process in the same way, and it cannot distinguish if a
> > > process belongs to a container or not.
> > >
> > > Containers use namespaces to make it appear to the processes in
> > > the containers that they have their own isolated instance of the
> > > global resource. For IMA as well, it is desirable to let
> > > processes in the
> > IMA is brought up on a regular basis with "we want to have this" for
> > years and then non-one seems to really care enough.
I don't think it is a matter of lack of interest or not caring. The
challenge becomes whether or not a business case exists for expending
the resources and navigating the challenges needed to advance
infrastructure for inclusion in the kernel.
> There is a lot of interest in IMA namespacing, but the question
> always comes back to how to enable it. Refer to
> https://kernsec.org/wiki/index.php/IMA_Namespacing_design_considerations
> for Stefan's analysis.
As I noted in my previous e-mail, I believe the path forward is not to
figure out how to address the rather complex and invasive issue of how
to namespace IMA, but instead, needs to be what a flexible
'next-generation' architecture for platform behavioral assessment
looks like.
In a larger context, I believe the future for security is going to
involve at a minimum the kernel, and more likely, something tightly
coupled to the kernel, making active decisions on whether or not a
behavior that the kernel is contemplating mediating is consistent with
platform or container security policy.
To frame this a bit, the well understood role of the kernel with
respect to security is to mediate 'Turing Events', or Actor/Subject
(A/S) interactions. Both DAC and MAC are based on this concept with
the interface between an Actor and Subject being a security 'gate'.
Given this model, the most simplistic and direct path forward is to
provide a namespace capable method of exporting a description of the
identity parameters that characterize the entities involved in an A/S
interaction. The kernel, or as I previously suggested as more likely
moving forward, a closely linked entity can then make a decision as to
whether or not the behavior should be allowed.
FWIW, as an example of the minimal impact of this method, here is the
diffstat of such an implementation:
---------------------------------------------------------------------------
arch/x86/entry/syscalls/syscall_32.tbl | 2 +
arch/x86/entry/syscalls/syscall_64.tbl | 2 +
fs/proc/array.c | 7 +
fs/proc/namespaces.c | 4 +
include/linux/ima.h | 27 +
include/linux/nsproxy.h | 2 +
include/linux/proc_ns.h | 2 +
include/linux/sched.h | 3 +
include/linux/syscalls.h | 6 +
include/uapi/asm-generic/unistd.h | 7 +-
include/uapi/linux/sched.h | 1 +
kernel/fork.c | 5 +-
kernel/nsproxy.c | 18 +-
kernel/sys_ni.c | 4 +
security/Kconfig | 1 +
security/Makefile | 2 +
Security/ai/Kconfig | 12 +
security/ai/Makefile | 3 +
security/ai/ai.c | 137 ++
security/integrity/iint.c | 5 +
security/integrity/ima/Makefile | 2 +-
security/integrity/ima/ima.h | 17 +
security/integrity/ima/ima_api.c | 37 +-
security/integrity/ima/ima_fs.c | 10 +
security/integrity/ima/ima_identity.c | 2204 +++++++++++++++++++++++++++++
security/integrity/ima/ima_init.c | 6 +-
security/integrity/ima/ima_main.c | 7 +
security/integrity/ima/ima_policy.c | 74 +-
security/integrity/ima/ima_queue.c | 10 +-
security/integrity/ima/ima_template.c | 6 +
security/integrity/ima/ima_template_lib.c | 71 +
security/integrity/ima/ima_template_lib.h | 10 +
security/integrity/integrity.h | 4 +-
security/security.c | 1 +
34 files changed, 2684 insertions(+), 25 deletions(-)
---------------------------------------------------------------------------
As can be seen, this was built out in the context of the IMA
sub-system with the majority of the changes being encapsulated in one
file. This includes all of the infrastructure needed for a Trusted
Execution Environment (TEE) to enforce kernel security policy
decisions.
In addition to a container specific representation of the current
behavioral state, each namespace exports via a sysfs pseudo-file, the
following behavioral definition for each A/S interaction.
exchange pid{1} event{cboot:/home/greg/runc} actor{uid=0, euid=0, suid=0, gid=0, egid=0, sgid=0, fsuid=0, fsgid=0, cap=0x3fffffffff} subject{uid=50, gid=50, mode=0100755, name_length=15, name=f0da604ff3f0a3e16163bc9d2f99bb9bcd70397d211b746d0104299972cc5505, s_id=sda1, s_uuid=1bfef8aaa45f4bcaa846640ae4547ddc, digest=791a7cf8dec2afe302836b974b3c0f7b0a5983f76d857aa97658ce09d54f60f8}
Which provides the framework for implementing any number of policy
decisions, of which integrity is only one element.
We had initially used SGX to implement a TEE based enforcement engine,
but given the direction of hardware support, we have largely shelved
our SGX development efforts in favor of using a micro-controller based
approach.
Given what appears to be the direction for mobile devices, a
collection of specialized harware linked by an OS, the notion of a
separate entity making security policy decisions seems relevant.
> I understand "containers" is not a kernel construct, but from my very
> limited perspective, IMA namespacing only makes sense in the context of
> a "container". The container owner may want to know which files have
> been accessed/executed (measurements, remote attestation) and/or
> constrain which files may be accessed/executed based on signatures
> (appraisal).
Trying to implement supportable and field maintainable 'trusted
computing' is a fools errand without the notion of containerization of
platform behavior. This is true whether the target is the cloud or
endpoint/IOT class devices.
It seems well understood, that while containers are not a first class
kernel entity, the kernel takes responsibility for implementing
compartmentalization of resources. It would seem that security event
characterizations are consistent with that model.
In addition, none of this works without developer support. Framing
behavior assessment in the form of containers means that behavioral
trajectory definitions for the containers can be a byproduct of
standard DEVOP's pipelines.
> > I'm highly skeptical of the value of ~2500 lines of code even if
> > it includes a bunch of namespace boilerplate. It's yet another
> > namespace, and yet another security framework. Why does IMA need
> > to be a separate namespace? Keyrings are tied to user namespaces
> > why can't IMA be?
> In the context of a container, the measurement list and IMA/EVM
> keyrings need to be setup before the first file is measured,
> signature verified, or file hash included in the audit log.
As I've noted previously, namespacing IMA is problematic, what is
needed is something far simpler and more flexible that provides a
framework for implementing policy outside of the kernel, or if in the
kernel, in a highly customizable fashion.
I think that it would be found that user namespaces bring too much
baggage to the table. The most effective path forward in this venue
would seem to be to bring forward the most simplistic, flexiable and
uncomplicated mechanism possible.
In this model, classic IMA would serve as a trust root on whose
shoulders a security orchestration framework stands.
> > I believe Eric has even pointed that out before.
> >
> > Eric, thoughts?
> Any help with the above scenario would very be much appreciated.
Hopefully the conversation will benefit from actual field experience
with doing this sort of thing in a supportable fashion.
The concept of 'trusted computing' has been around since the days when
Dave Grawrock designed TXT, which is heavily linked to the heritage of
IMA. The fact that effective solutions in widespread practice have
not emerged, in the face of demonstrated need, suggests the need to
develop new solution strategies.
Just to be clear, we are not campaigning or advocating what we have
done but are simply providing background for discussion. We haven't
campaigned this approach given how complex the kernel development has
become, particurlarly with respect to security infrastructure.
Candidly, given the politics of security technology being viewed as
'constraining' user rights, I think that a lot of forthcoming security
technology may end up being out of tree moving forward.
> Mimi
Best wishes for a productive week to everyone.
Dr. Greg
As always,
Dr. Greg Wettstein, Ph.D, Worker Autonomously self-defensive
Enjellic Systems Development, LLC IOT platforms and edge devices.
4206 N. 19th Ave.
Fargo, ND 58102
PH: 701-281-1686 EMAIL: [email protected]
------------------------------------------------------------------------------
"A large number of the world's technical challenges have been solved.
The far greater challenge lies in conveying an understanding of this
to the world."
-- Dr. Greg Wettstein
Resurrection
On Mon, 2020-09-07 at 12:50 +0100, Luke Hinds wrote:
> > Candidly, given the politics of security technology being viewed as
> > 'constraining' user rights, I think that a lot of forthcoming security
> > technology may end up being out of tree moving forward.
> >
>
> I think it's prudent to look forward and plan diligently, but I would
> not want perfect to be the enemy of good.
Agreed. This isn't an abstract problem, but one that has already come
up and, hopefully, has been addressed appropriately.
>
> I approach this more from a user's perspective. We are using IMA in
> https://keylime.dev to measure a host and would like to measure
> within a container too. It's the most common request we hear from our
> users.
>
> Perhaps we all collaborate on a proposal extending Stefans work here:
> https://kernsec.org/wiki/index.php/IMA_Namespacing_design_considerati
> ons
>
> I have seen around 3-4 patches now get submitted, so work has been
> done before, and as above, users are present too. We could then have
> some consensus on how this should look and later patches might have
> more success at landing.
>
> Would anyone be interested in this and have recommendations on how we
> could approach this?
When Roberto Sassu and Krzysztof Struczynski contacted me about the
status of Stefan Berger's patch set, based on Yuqiong Sun's work, I was
under the impression that they would be rebasing it on the latest
kernel and going forward from there. Obviously things changed. I
pointed out to them resolving the "IMA namespacing" issue would be the
first thing that needs to be addressed. So here we are.
Definitely, let's have this discussion.
Mimi
On Mon, Sep 07, 2020 at 12:50:07PM +0100, Luke Hinds wrote:
Good morning, I hope the week is going well for everyone.
> On Sun, Sep 6, 2020 at 6:15 PM Dr. Greg <[email protected]> wrote:
> > Just to be clear, we are not campaigning or advocating what we have
> > done but are simply providing background for discussion. We haven't
> > campaigned this approach given how complex the kernel development has
> > become, particurlarly with respect to security infrastructure.
> >
> > Candidly, given the politics of security technology being viewed as
> > 'constraining' user rights, I think that a lot of forthcoming security
> > technology may end up being out of tree moving forward.
> I think it's prudent to look forward and plan diligently, but I would not
> want perfect to be the enemy of good.
>
> I approach this more from a user's perspective. We are using IMA in
> https://keylime.dev to measure a host and would like to measure
> within a container too. It's the most common request we hear from
> our users.
>
> Perhaps we all collaborate on a proposal extending Stefans work here:
> https://kernsec.org/wiki/index.php/IMA_Namespacing_design_considerations
>
> I have seen around 3-4 patches now get submitted, so work has been
> done before, and as above, users are present too. We could then have
> some consensus on how this should look and later patches might have
> more success at landing.
>
> Would anyone be interested in this and have recommendations on how
> we could approach this?
Obviously everyone is interested in sharpening their own knives so the
first challenge will be defining where this theme of measurement and
attestation needs to go.
Our focus in all of this is from a platform behavior modeling
perspective. Our objective is to design platforms/containers that are
capable of self-disciplining themselves in the event that they exhibit
behavior inconsistent with the wishes of their designer. Container
measurement trivially falls out of this model.
With respect to measurement namespaces, the first problem to be
addressed is what takes custody and responsibility for the measurement
events. In classic IMA this is, of course, a TPM. In our model we
use a Trusted Execution Environment (TEE) as this entity.
The TEE makes a decision as to whether or not the kernel should label
a context of execution as being a 'bad actor' if it indicates a desire
to exhibit a behavior inconsistent with a previously defined model.
As I noted previously we have an SGX based solution that provides this
infrastructure but have designed and are moving to a micro-controller
based alternative, given the fact that SGX is now moving to a 'cloud
only' solution.
One of the pain points in all of this appears to be whether or not a
measurement stream from a container should feed into the root
measurement of the platform or be fed into a measurement/monitoring
domain that can be attested against the root measurement of the
platform. Based on our experiences the latter model is the only one
that is feasible or makes sense from an attestation perspective.
So it would seem that a generic approach to directing the target of
the measurement events would be the first objective. If there is
interest we can make a copy of our patch available as it supports both
models.
> - Luke
Have a good day.
Dr. Greg
As always,
Dr. Greg Wettstein, Ph.D, Worker Autonomously self-defensive
Enjellic Systems Development, LLC IOT platforms and edge devices.
4206 N. 19th Ave.
Fargo, ND 58102
PH: 701-281-1686 EMAIL: [email protected]
------------------------------------------------------------------------------
"I created a hack to make the division come out right ... I was
relieved because I thought I was coding wrong.
Did you? It took a guy (Thomas Nicely) with a Ph.D. doing heavy
research in computational number theory to find it, yet you found it
while working on a game in QuickBasic?"
-- Slashdot
> From: Mimi Zohar [mailto:[email protected]]
> Sent: Tuesday, September 8, 2020 4:03 PM
> > > Candidly, given the politics of security technology being viewed as
> > > 'constraining' user rights, I think that a lot of forthcoming security
> > > technology may end up being out of tree moving forward.
> > >
> >
> > I think it's prudent to look forward and plan diligently, but I would
> > not want perfect to be the enemy of good.
>
> Agreed. This isn't an abstract problem, but one that has already come
> up and, hopefully, has been addressed appropriately.
>
> >
> > I approach this more from a user's perspective. We are using IMA in
> > https://keylime.dev to measure a host and would like to measure
> > within a container too. It's the most common request we hear from our
> > users.
> >
> > Perhaps we all collaborate on a proposal extending Stefans work here:
> > https://kernsec.org/wiki/index.php/IMA_Namespacing_design_considerati
> > ons
> >
> > I have seen around 3-4 patches now get submitted, so work has been
> > done before, and as above, users are present too. We could then have
> > some consensus on how this should look and later patches might have
> > more success at landing.
> >
> > Would anyone be interested in this and have recommendations on how we
> > could approach this?
>
> When Roberto Sassu and Krzysztof Struczynski contacted me about the
> status of Stefan Berger's patch set, based on Yuqiong Sun's work, I was
> under the impression that they would be rebasing it on the latest
> kernel and going forward from there. Obviously things changed. I
> pointed out to them resolving the "IMA namespacing" issue would be the
> first thing that needs to be addressed. So here we are.
>
The concept of the IMA namespace as a separate namespace, not attached to
the user or mount namespace, remains the same as in the last set of
Stefan's patches. The creation and configuration mechanism changed,
partially because of the changes in the upstream kernel like extension of
the CLONE_ flags, that facilitated other solutions. The main inspiration
in this regard was the new time namespace.
The implemented mechanism is intended to make the configuration of the IMA
namespace as similar as possible to the upstream IMA implementation and
allows to configure the new IMA namespace before any process enters that
namespace, so the very first actions can be measured/appraised/audited.
In this implementation many design points formulated by Stefan still stand.
The significant difference in the presented design is the threat model.
The actions of the host's root and any other user within the container are
measured/appraised/audited according to the container's policy. However,
every action that affects objects in the root namespace, is detected when
processes from the root namespace access them.
Best regards,
Krzysztof
> Definitely, let's have this discussion.
>
> Mimi
> From: Mimi Zohar [mailto:[email protected]]
> Sent: Wednesday, September 2, 2020 8:53 PM
> > > So I think this can work in the use case where the system owner is
> > > responsible for doing the logging and attestation and the tenants just
> > > trust the owner without requiring an attestation. However, in a multi-
> > > tenant system you need a way for the attestation to be per-container
> > > (because the combined list of who executed what would be a security
> > > leak between tenants). Since we can't virtualise the PCRs without
> > > introducing a vtpm this is going to require a vtpm infrastructure like
> > > that used for virtual machines and then we can do IMA logging per
> > > container.
> >
> > I agree and wonder if we should decouple the attestation trust model,
> > which depends on the specific use case (e.g. multi/single tenant,
> > public/private cloud), from the IMA logic of linking the measurements to
> > the container. Indeed, attestation from within the container might require
> > anchoring to a vTPM/vPCR and the current measurement tagging mechanism
> can
> > support several ways of anchoring them to a (virtual) root of trust.
> >
> > > I don't think the above has to be in your first patch set, we just have
> > > to have an idea of how it could be done to show that nothing in this
> > > patch set precludes a follow on from doing this.
> >
> > Given that virtualizing trust anchors seems like a separate problem in
> > which industry consensus is not easy to reach for all use cases, an
> > anchoring mechanism should probably be a separate IMA feature.
>
> Other trust anchors for "trusted keys" has been discussed, but I wasn't
> aware of any discussion about other trust anchors for the IMA
> measurement list. The IMA measurement list is very much tied to a TPM.
>
Agreed. I wouldn't consider anything else than the TPM in the IMA
measurement list context. The anchoring mechanism mentioned above
pertained to the possible extensions of the TPM anchor in the
containerized environment, like the vTPM.
> Including container measurements in the host measurement list, will
> unnecessarily cause the host measurement list to grow. The decision of
> what should and shouldn't be included in the host measurement list
> shouldn't be defined by the container.
>
The container has no impact on the measurement list entries other than the
ones related to the processes running within the container. This in turn,
the same as for the original IMA, is defined by the (container's) policy,
loaded on the container's creation.
Best regards,
Krzysztof
> Mimi
>
>
> From: Krzysztof Struczynski
> Sent: Monday, September 14, 2020 2:08 PM
> > When Roberto Sassu and Krzysztof Struczynski contacted me about the
> > status of Stefan Berger's patch set, based on Yuqiong Sun's work, I was
> > under the impression that they would be rebasing it on the latest
> > kernel and going forward from there. Obviously things changed. I
> > pointed out to them resolving the "IMA namespacing" issue would be the
> > first thing that needs to be addressed. So here we are.
> >
>
> The concept of the IMA namespace as a separate namespace, not attached to
> the user or mount namespace, remains the same as in the last set of
> Stefan's patches. The creation and configuration mechanism changed,
> partially because of the changes in the upstream kernel like extension of
> the CLONE_ flags, that facilitated other solutions. The main inspiration
> in this regard was the new time namespace.
>
> The implemented mechanism is intended to make the configuration of the
> IMA
> namespace as similar as possible to the upstream IMA implementation and
> allows to configure the new IMA namespace before any process enters that
> namespace, so the very first actions can be measured/appraised/audited.
>
> In this implementation many design points formulated by Stefan still stand.
> The significant difference in the presented design is the threat model.
> The actions of the host's root and any other user within the container are
> measured/appraised/audited according to the container's policy. However,
> every action that affects objects in the root namespace, is detected when
> processes from the root namespace access them.
Do you think that the above proposal addresses the aforementioned "IMA
namespacing" issue? If it doesn't, would you please clarify what are the
main, outstanding problems?
> > Definitely, let's have this discussion.
Shall we continue the discussion here, or in addition use a format similar
to the Stefan's "drawing board":
https://kernsec.org/wiki/index.php/IMA_Namespacing_design_considerations
Best regards,
Krzysztof Struczynski
On Mon, Oct 19, 2020 at 09:30:27AM +0000, Krzysztof Struczynski wrote:
Good morning, I hope this note finds the weekend going well for
everyone.
> > The concept of the IMA namespace as a separate namespace, not
> > attached to the user or mount namespace, remains the same as in
> > the last set of Stefan's patches. The creation and configuration
> > mechanism changed, partially because of the changes in the
> > upstream kernel like extension of the CLONE_ flags, that
> > facilitated other solutions. The main inspiration in this regard
> > was the new time namespace.
> >
> > The implemented mechanism is intended to make the configuration of
> > the IMA namespace as similar as possible to the upstream IMA
> > implementation and allows to configure the new IMA namespace
> > before any process enters that namespace, so the very first
> > actions can be measured/appraised/audited.
> >
> > In this implementation many design points formulated by Stefan
> > still stand. The significant difference in the presented design
> > is the threat model. The actions of the host's root and any other
> > user within the container are measured/appraised/audited according
> > to the container's policy. However, every action that affects
> > objects in the root namespace, is detected when processes from the
> > root namespace access them.
> Do you think that the above proposal addresses the aforementioned
> "IMA namespacing" issue? If it doesn't, would you please clarify
> what are the main, outstanding problems?
From our perspective, a significant objective of the namespacing
discussion needs to be considerations regarding how additional growth
and sophistication of IMA as a sub-system will occur.
IMA, if taken to its limit, resolves into the notion of modeling the
behavior of a hardware platform, its operating system and the
supported application stack. We believe the most important issue with
respect to namespacing is to provide an opportunity to allow more
sophisticated and capable models to be developed and implemented,
without having the process unduely constrained by the realities of
kernel development.
Coupled with this issue is the fact that it doesn't seem reasonable to
believe that TPM's are going to be the venue or root of trust for this
modeling, for a variety of reasons, perhaps most principally the fact
that they are a 'blackbox' that have not enjoyed universal trust.
This is perhaps particularly relevant now that they are being
implemented in firmware rather then actual hardware.
The sophistication and security capabilities of micro-controllers that
are being developed for the IOT security space make a case for
developing open-source/architecture security co-processors that
implement the modeling and root of trust for independent modeling/IMA
namespaces.
TPM's also implicitly constrain the modeling to be a function based on
linear extension summing. It seems pretty clear that stochastic as
well as deterministic models will be needed, particularly for more
dynamic systems.
So the optimum approach would seem to be for the namespace to have the
option of implementing its own policy or to provide a mechanism for
forwarding all of the Turing event characteristics to an entity that
can implement the desired integrity or model policy.
All of this would also seem to be consistent with the direction of the
mobile market, which appears to be moving toward a model of an
operating system that binds together multiple co-processors or
functional units. IMA, in this model, is a separate functional entity
resonsible for interpreting and disciplining kernel behavior.
> > > Definitely, let's have this discussion.
> Shall we continue the discussion here, or in addition use a format similar
> to the Stefan's "drawing board":
> https://kernsec.org/wiki/index.php/IMA_Namespacing_design_considerations
It has always been said that code speaks louder then words. So we are
working through the process of cleaning up our implementation of the
approach described above so that people can at least take a look at
the architecture we are proposing.
We don't have any belief that it would be suitable to survive the
gauntlet of the kernel development process but we will offer it as a
proof of concept of where we think all of this will, can and needs to
end up going.
We were able to bolt all of this on top of standard IMA in a
reasonably straight forward fashion. Our initial implementation of an
external modeling engine was based on SGX, which was remarkably useful
for this purpose, albeit not well understood.
Given the trajectory that technology has been placed on, we now have
an implementation based on an ARM micro-controller as the external
modeling/policy engine. While not quite as flexible as the SGX based
approach it has a number of its own advantages, most principally
ubiquity of implementation. Both approaches required no modification
of the namespaced IMA implementation which we believe speaks to the
flexibility of the approach.
> Best regards,
> Krzysztof Struczynski
Hopefully the above reflections are helpful in steering progress of
discussions, if not the price was certainly right.
Best wishes for a productive week to everyone.
Dr. Greg
As always,
Dr. Greg Wettstein, Ph.D, Worker Autonomously self-defensive
Enjellic Systems Development, LLC IOT platforms and edge devices.
4206 N. 19th Ave.
Fargo, ND 58102
PH: 701-281-1686 EMAIL: [email protected]
------------------------------------------------------------------------------
"Attendants at a service station in Eunice, Louisiana, handed more than
$100 to a naked man who claimed to have a gun in his pocket."
-- Unknown