From patchwork Tue Aug 18 15:20:08 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Krzysztof Struczynski X-Patchwork-Id: 11721631 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 67D0A739 for ; Tue, 18 Aug 2020 15:26:14 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 40CD3206DA for ; Tue, 18 Aug 2020 15:26:14 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726685AbgHRP0M (ORCPT ); Tue, 18 Aug 2020 11:26:12 -0400 Received: from lhrrgout.huawei.com ([185.176.76.210]:2624 "EHLO huawei.com" rhost-flags-OK-OK-OK-FAIL) by vger.kernel.org with ESMTP id S1726904AbgHRP0J (ORCPT ); Tue, 18 Aug 2020 11:26:09 -0400 Received: from lhreml722-chm.china.huawei.com (unknown [172.18.7.108]) by Forcepoint Email with ESMTP id B0BC75F02A3A752483C0; Tue, 18 Aug 2020 16:26:07 +0100 (IST) Received: from kstruczy-linux-box (10.204.65.138) by lhreml722-chm.china.huawei.com (10.201.108.73) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256) id 15.1.1913.5; Tue, 18 Aug 2020 16:26:06 +0100 Received: by kstruczy-linux-box (sSMTP sendmail emulation); Tue, 18 Aug 2020 17:26:08 +0200 From: To: , , , CC: Krzysztof Struczynski , , , , , , , , , , Subject: [RFC PATCH 01/30] ima: Introduce ima namespace Date: Tue, 18 Aug 2020 17:20:08 +0200 Message-ID: <20200818152037.11869-2-krzysztof.struczynski@huawei.com> X-Mailer: git-send-email 2.20.1 In-Reply-To: <20200818152037.11869-1-krzysztof.struczynski@huawei.com> References: <20200818152037.11869-1-krzysztof.struczynski@huawei.com> MIME-Version: 1.0 X-Originating-IP: [10.204.65.138] X-ClientProxiedBy: fraeml707-chm.china.huawei.com (10.206.15.35) To lhreml722-chm.china.huawei.com (10.201.108.73) X-CFilter-Loop: Reflected Sender: linux-integrity-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-integrity@vger.kernel.org From: Krzysztof Struczynski 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 --- 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 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 #include #include +#include #include #include #include @@ -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 #include #include +#include #include #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 #include #include +#include +#include +#include #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 + * + * 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 +#include +#include +#include +#include +#include +#include +#include + +#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, +}; + From patchwork Tue Aug 18 15:20:09 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Krzysztof Struczynski X-Patchwork-Id: 11721663 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id A992F1575 for ; Tue, 18 Aug 2020 15:26:56 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 98CF4206DA for ; Tue, 18 Aug 2020 15:26:56 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1728153AbgHRP0z (ORCPT ); Tue, 18 Aug 2020 11:26:55 -0400 Received: from lhrrgout.huawei.com ([185.176.76.210]:2625 "EHLO huawei.com" rhost-flags-OK-OK-OK-FAIL) by vger.kernel.org with ESMTP id S1727791AbgHRP0L (ORCPT ); Tue, 18 Aug 2020 11:26:11 -0400 Received: from lhreml722-chm.china.huawei.com (unknown [172.18.7.108]) by Forcepoint Email with ESMTP id 225CE751D64D2FEE2140; Tue, 18 Aug 2020 16:26:10 +0100 (IST) Received: from kstruczy-linux-box (10.204.65.138) by lhreml722-chm.china.huawei.com (10.201.108.73) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256) id 15.1.1913.5; Tue, 18 Aug 2020 16:26:08 +0100 Received: by kstruczy-linux-box (sSMTP sendmail emulation); Tue, 18 Aug 2020 17:26:10 +0200 From: To: , , , CC: Krzysztof Struczynski , , , , , , , , , , Subject: [RFC PATCH 02/30] ima: Add a list of the installed ima namespaces Date: Tue, 18 Aug 2020 17:20:09 +0200 Message-ID: <20200818152037.11869-3-krzysztof.struczynski@huawei.com> X-Mailer: git-send-email 2.20.1 In-Reply-To: <20200818152037.11869-1-krzysztof.struczynski@huawei.com> References: <20200818152037.11869-1-krzysztof.struczynski@huawei.com> MIME-Version: 1.0 X-Originating-IP: [10.204.65.138] X-ClientProxiedBy: fraeml707-chm.china.huawei.com (10.206.15.35) To lhreml722-chm.china.huawei.com (10.201.108.73) X-CFilter-Loop: Reflected Sender: linux-integrity-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-integrity@vger.kernel.org From: Krzysztof Struczynski 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 --- 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 #include #include -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 #include #include +#include +#include +#include +#include +#include #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) From patchwork Tue Aug 18 15:20:10 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Krzysztof Struczynski X-Patchwork-Id: 11721635 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 7E5AE1575 for ; Tue, 18 Aug 2020 15:26:19 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 703AC207DA for ; Tue, 18 Aug 2020 15:26:19 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1728061AbgHRP0S (ORCPT ); Tue, 18 Aug 2020 11:26:18 -0400 Received: from lhrrgout.huawei.com ([185.176.76.210]:2626 "EHLO huawei.com" rhost-flags-OK-OK-OK-FAIL) by vger.kernel.org with ESMTP id S1727927AbgHRP0O (ORCPT ); Tue, 18 Aug 2020 11:26:14 -0400 Received: from lhreml722-chm.china.huawei.com (unknown [172.18.7.106]) by Forcepoint Email with ESMTP id 839E617247B5794C5F4C; Tue, 18 Aug 2020 16:26:12 +0100 (IST) Received: from kstruczy-linux-box (10.204.65.138) by lhreml722-chm.china.huawei.com (10.201.108.73) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256) id 15.1.1913.5; Tue, 18 Aug 2020 16:26:10 +0100 Received: by kstruczy-linux-box (sSMTP sendmail emulation); Tue, 18 Aug 2020 17:26:13 +0200 From: To: , , , CC: Krzysztof Struczynski , , , , , , , , , , Subject: [RFC PATCH 03/30] ima: Bind ima namespace to the file descriptor Date: Tue, 18 Aug 2020 17:20:10 +0200 Message-ID: <20200818152037.11869-4-krzysztof.struczynski@huawei.com> X-Mailer: git-send-email 2.20.1 In-Reply-To: <20200818152037.11869-1-krzysztof.struczynski@huawei.com> References: <20200818152037.11869-1-krzysztof.struczynski@huawei.com> MIME-Version: 1.0 X-Originating-IP: [10.204.65.138] X-ClientProxiedBy: fraeml707-chm.china.huawei.com (10.206.15.35) To lhreml722-chm.china.huawei.com (10.201.108.73) X-CFilter-Loop: Reflected Sender: linux-integrity-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-integrity@vger.kernel.org From: Krzysztof Struczynski 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 --- 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, From patchwork Tue Aug 18 15:20:11 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Krzysztof Struczynski X-Patchwork-Id: 11721661 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 34B7E739 for ; Tue, 18 Aug 2020 15:26:56 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 227CE2076E for ; Tue, 18 Aug 2020 15:26:56 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1728150AbgHRP0z (ORCPT ); Tue, 18 Aug 2020 11:26:55 -0400 Received: from lhrrgout.huawei.com ([185.176.76.210]:2627 "EHLO huawei.com" rhost-flags-OK-OK-OK-FAIL) by vger.kernel.org with ESMTP id S1726904AbgHRP0Q (ORCPT ); Tue, 18 Aug 2020 11:26:16 -0400 Received: from lhreml722-chm.china.huawei.com (unknown [172.18.7.107]) by Forcepoint Email with ESMTP id 19CE374562B957EF2E99; Tue, 18 Aug 2020 16:26:15 +0100 (IST) Received: from kstruczy-linux-box (10.204.65.138) by lhreml722-chm.china.huawei.com (10.201.108.73) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256) id 15.1.1913.5; Tue, 18 Aug 2020 16:26:13 +0100 Received: by kstruczy-linux-box (sSMTP sendmail emulation); Tue, 18 Aug 2020 17:26:15 +0200 From: To: , , , CC: Krzysztof Struczynski , , , , , , , , , , Subject: [RFC PATCH 04/30] ima: Add ima policy related data to the ima namespace Date: Tue, 18 Aug 2020 17:20:11 +0200 Message-ID: <20200818152037.11869-5-krzysztof.struczynski@huawei.com> X-Mailer: git-send-email 2.20.1 In-Reply-To: <20200818152037.11869-1-krzysztof.struczynski@huawei.com> References: <20200818152037.11869-1-krzysztof.struczynski@huawei.com> MIME-Version: 1.0 X-Originating-IP: [10.204.65.138] X-ClientProxiedBy: fraeml707-chm.china.huawei.com (10.206.15.35) To lhreml722-chm.china.huawei.com (10.201.108.73) X-CFilter-Loop: Reflected Sender: linux-integrity-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-integrity@vger.kernel.org From: Krzysztof Struczynski 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 --- 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. * From patchwork Tue Aug 18 15:20:12 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Krzysztof Struczynski X-Patchwork-Id: 11721659 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id E240613B1 for ; Tue, 18 Aug 2020 15:26:55 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id CB397206DA for ; Tue, 18 Aug 2020 15:26:55 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1728127AbgHRP0z (ORCPT ); Tue, 18 Aug 2020 11:26:55 -0400 Received: from lhrrgout.huawei.com ([185.176.76.210]:2628 "EHLO huawei.com" rhost-flags-OK-OK-OK-FAIL) by vger.kernel.org with ESMTP id S1728060AbgHRP0T (ORCPT ); Tue, 18 Aug 2020 11:26:19 -0400 Received: from lhreml722-chm.china.huawei.com (unknown [172.18.7.108]) by Forcepoint Email with ESMTP id 7D6AA7FAC173646AF9E4; Tue, 18 Aug 2020 16:26:17 +0100 (IST) Received: from kstruczy-linux-box (10.204.65.138) by lhreml722-chm.china.huawei.com (10.201.108.73) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256) id 15.1.1913.5; Tue, 18 Aug 2020 16:26:15 +0100 Received: by kstruczy-linux-box (sSMTP sendmail emulation); Tue, 18 Aug 2020 17:26:18 +0200 From: To: , , , CC: Krzysztof Struczynski , , , , , , , , , , Subject: [RFC PATCH 05/30] ima: Add methods for parsing ima policy configuration string Date: Tue, 18 Aug 2020 17:20:12 +0200 Message-ID: <20200818152037.11869-6-krzysztof.struczynski@huawei.com> X-Mailer: git-send-email 2.20.1 In-Reply-To: <20200818152037.11869-1-krzysztof.struczynski@huawei.com> References: <20200818152037.11869-1-krzysztof.struczynski@huawei.com> MIME-Version: 1.0 X-Originating-IP: [10.204.65.138] X-ClientProxiedBy: fraeml707-chm.china.huawei.com (10.206.15.35) To lhreml722-chm.china.huawei.com (10.201.108.73) X-CFilter-Loop: Reflected Sender: linux-integrity-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-integrity@vger.kernel.org From: Krzysztof Struczynski 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 --- 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; From patchwork Tue Aug 18 15:20:13 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Krzysztof Struczynski X-Patchwork-Id: 11721643 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id DFEE21744 for ; Tue, 18 Aug 2020 15:26:25 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id CECD42076E for ; Tue, 18 Aug 2020 15:26:25 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1728092AbgHRP0Y (ORCPT ); Tue, 18 Aug 2020 11:26:24 -0400 Received: from lhrrgout.huawei.com ([185.176.76.210]:2629 "EHLO huawei.com" rhost-flags-OK-OK-OK-FAIL) by vger.kernel.org with ESMTP id S1728072AbgHRP0V (ORCPT ); Tue, 18 Aug 2020 11:26:21 -0400 Received: from lhreml722-chm.china.huawei.com (unknown [172.18.7.106]) by Forcepoint Email with ESMTP id D6365A8F61BFE861120E; Tue, 18 Aug 2020 16:26:19 +0100 (IST) Received: from kstruczy-linux-box (10.204.65.138) by lhreml722-chm.china.huawei.com (10.201.108.73) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256) id 15.1.1913.5; Tue, 18 Aug 2020 16:26:18 +0100 Received: by kstruczy-linux-box (sSMTP sendmail emulation); Tue, 18 Aug 2020 17:26:20 +0200 From: To: , , , CC: Krzysztof Struczynski , , , , , , , , , , Subject: [RFC PATCH 06/30] ima: Add ima namespace to the ima subsystem APIs Date: Tue, 18 Aug 2020 17:20:13 +0200 Message-ID: <20200818152037.11869-7-krzysztof.struczynski@huawei.com> X-Mailer: git-send-email 2.20.1 In-Reply-To: <20200818152037.11869-1-krzysztof.struczynski@huawei.com> References: <20200818152037.11869-1-krzysztof.struczynski@huawei.com> MIME-Version: 1.0 X-Originating-IP: [10.204.65.138] X-ClientProxiedBy: fraeml707-chm.china.huawei.com (10.206.15.35) To lhreml722-chm.china.huawei.com (10.201.108.73) X-CFilter-Loop: Reflected Sender: linux-integrity-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-integrity@vger.kernel.org From: Krzysztof Struczynski 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 --- 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); } From patchwork Tue Aug 18 15:20:14 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Krzysztof Struczynski X-Patchwork-Id: 11721657 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 5B4D313B1 for ; Tue, 18 Aug 2020 15:26:54 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 3AED32076E for ; Tue, 18 Aug 2020 15:26:54 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1728087AbgHRP0Y (ORCPT ); Tue, 18 Aug 2020 11:26:24 -0400 Received: from lhrrgout.huawei.com ([185.176.76.210]:2630 "EHLO huawei.com" rhost-flags-OK-OK-OK-FAIL) by vger.kernel.org with ESMTP id S1728080AbgHRP0X (ORCPT ); Tue, 18 Aug 2020 11:26:23 -0400 Received: from lhreml722-chm.china.huawei.com (unknown [172.18.7.106]) by Forcepoint Email with ESMTP id 0B4381ACF16B75453D57; Tue, 18 Aug 2020 16:26:22 +0100 (IST) Received: from kstruczy-linux-box (10.204.65.138) by lhreml722-chm.china.huawei.com (10.201.108.73) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256) id 15.1.1913.5; Tue, 18 Aug 2020 16:26:20 +0100 Received: by kstruczy-linux-box (sSMTP sendmail emulation); Tue, 18 Aug 2020 17:26:23 +0200 From: To: , , , CC: Krzysztof Struczynski , , , , , , , , , , Subject: [RFC PATCH 07/30] ima: Extend the APIs in the integrity subsystem Date: Tue, 18 Aug 2020 17:20:14 +0200 Message-ID: <20200818152037.11869-8-krzysztof.struczynski@huawei.com> X-Mailer: git-send-email 2.20.1 In-Reply-To: <20200818152037.11869-1-krzysztof.struczynski@huawei.com> References: <20200818152037.11869-1-krzysztof.struczynski@huawei.com> MIME-Version: 1.0 X-Originating-IP: [10.204.65.138] X-ClientProxiedBy: fraeml707-chm.china.huawei.com (10.206.15.35) To lhreml722-chm.china.huawei.com (10.201.108.73) X-CFilter-Loop: Reflected Sender: linux-integrity-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-integrity@vger.kernel.org From: Krzysztof Struczynski 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 --- 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 +#include + +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 #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 From patchwork Tue Aug 18 15:20:15 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Krzysztof Struczynski X-Patchwork-Id: 11721647 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id EBBC0739 for ; Tue, 18 Aug 2020 15:26:31 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id DD6F4206DA for ; Tue, 18 Aug 2020 15:26:31 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1728109AbgHRP03 (ORCPT ); Tue, 18 Aug 2020 11:26:29 -0400 Received: from lhrrgout.huawei.com ([185.176.76.210]:2631 "EHLO huawei.com" rhost-flags-OK-OK-OK-FAIL) by vger.kernel.org with ESMTP id S1728101AbgHRP00 (ORCPT ); Tue, 18 Aug 2020 11:26:26 -0400 Received: from lhreml722-chm.china.huawei.com (unknown [172.18.7.108]) by Forcepoint Email with ESMTP id CFC71144517D897E9DA3; Tue, 18 Aug 2020 16:26:24 +0100 (IST) Received: from kstruczy-linux-box (10.204.65.138) by lhreml722-chm.china.huawei.com (10.201.108.73) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256) id 15.1.1913.5; Tue, 18 Aug 2020 16:26:22 +0100 Received: by kstruczy-linux-box (sSMTP sendmail emulation); Tue, 18 Aug 2020 17:26:25 +0200 From: To: , , , CC: Krzysztof Struczynski , , , , , , , , , , Subject: [RFC PATCH 08/30] ima: Add integrity inode related data to the ima namespace Date: Tue, 18 Aug 2020 17:20:15 +0200 Message-ID: <20200818152037.11869-9-krzysztof.struczynski@huawei.com> X-Mailer: git-send-email 2.20.1 In-Reply-To: <20200818152037.11869-1-krzysztof.struczynski@huawei.com> References: <20200818152037.11869-1-krzysztof.struczynski@huawei.com> MIME-Version: 1.0 X-Originating-IP: [10.204.65.138] X-ClientProxiedBy: fraeml707-chm.china.huawei.com (10.206.15.35) To lhreml722-chm.china.huawei.com (10.201.108.73) X-CFilter-Loop: Reflected Sender: linux-integrity-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-integrity@vger.kernel.org From: Krzysztof Struczynski 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 --- 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); } From patchwork Tue Aug 18 15:20:16 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Krzysztof Struczynski X-Patchwork-Id: 11721651 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 9135E739 for ; Tue, 18 Aug 2020 15:26:46 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 54A4C207DA for ; Tue, 18 Aug 2020 15:26:46 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1728083AbgHRP0m (ORCPT ); Tue, 18 Aug 2020 11:26:42 -0400 Received: from lhrrgout.huawei.com ([185.176.76.210]:2632 "EHLO huawei.com" rhost-flags-OK-OK-OK-FAIL) by vger.kernel.org with ESMTP id S1728107AbgHRP03 (ORCPT ); Tue, 18 Aug 2020 11:26:29 -0400 Received: from lhreml722-chm.china.huawei.com (unknown [172.18.7.108]) by Forcepoint Email with ESMTP id 27F259934E2334B329CF; Tue, 18 Aug 2020 16:26:27 +0100 (IST) Received: from kstruczy-linux-box (10.204.65.138) by lhreml722-chm.china.huawei.com (10.201.108.73) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256) id 15.1.1913.5; Tue, 18 Aug 2020 16:26:25 +0100 Received: by kstruczy-linux-box (sSMTP sendmail emulation); Tue, 18 Aug 2020 17:26:28 +0200 From: To: , , , CC: Krzysztof Struczynski , , , , , , , , , , Subject: [RFC PATCH 09/30] ima: Enable per ima namespace policy settings Date: Tue, 18 Aug 2020 17:20:16 +0200 Message-ID: <20200818152037.11869-10-krzysztof.struczynski@huawei.com> X-Mailer: git-send-email 2.20.1 In-Reply-To: <20200818152037.11869-1-krzysztof.struczynski@huawei.com> References: <20200818152037.11869-1-krzysztof.struczynski@huawei.com> MIME-Version: 1.0 X-Originating-IP: [10.204.65.138] X-ClientProxiedBy: fraeml707-chm.china.huawei.com (10.206.15.35) To lhreml722-chm.china.huawei.com (10.201.108.73) X-CFilter-Loop: Reflected Sender: linux-integrity-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-integrity@vger.kernel.org From: Krzysztof Struczynski 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 --- 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