提交 a8352473 编写于 作者: K Krzysztof Struczynski 提交者: Zheng Zengkai

ima: Introduce ima namespace

hulk inclusion
category: feature
bugzilla: https://gitee.com/openeuler/kernel/issues/I49KW1
CVE: NA

--------------------------------

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: NKrzysztof Struczynski <krzysztof.struczynski@huawei.com>
Reviewed-by: NZhang Tianxing <zhangtianxing3@huawei.com>
Signed-off-by: NZheng Zengkai <zhengzengkai@huawei.com>
上级 061052a9
......@@ -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,
......
......@@ -13,6 +13,9 @@
#include <linux/kexec.h>
struct linux_binprm;
struct nsproxy;
struct task_struct;
#ifdef CONFIG_IMA
extern int ima_bprm_check(struct linux_binprm *bprm);
extern int ima_file_check(struct file *file, int mask);
......@@ -197,4 +200,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 */
......@@ -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;
......
......@@ -16,7 +16,7 @@ struct inode;
struct proc_ns_operations {
const char *name;
const char *real_ns_name;
int type;
uint64_t 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
......
......@@ -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,
......
......@@ -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
......
......@@ -1206,6 +1206,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
......
......@@ -1922,11 +1922,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);
}
......@@ -2701,7 +2714,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;
/*
......@@ -2848,7 +2862,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
......
......@@ -19,6 +19,7 @@
#include <net/net_namespace.h>
#include <linux/ipc_namespace.h>
#include <linux/time_namespace.h>
#include <linux/ima.h>
#include <linux/fs_struct.h>
#include <linux/proc_fs.h>
#include <linux/proc_ns.h>
......@@ -47,6 +48,10 @@ struct nsproxy init_nsproxy = {
.time_ns = &init_time_ns,
.time_ns_for_children = &init_time_ns,
#endif
#ifdef CONFIG_IMA_NS
.ima_ns = &init_ima_ns,
.ima_ns_for_children = &init_ima_ns,
#endif
};
static inline struct nsproxy *create_nsproxy(void)
......@@ -121,8 +126,19 @@ static struct nsproxy *create_new_namespaces(unsigned long flags,
}
new_nsp->time_ns = get_time_ns(tsk->nsproxy->time_ns);
new_nsp->ima_ns_for_children = copy_ima_ns(flags, user_ns,
tsk->nsproxy->ima_ns_for_children);
if (IS_ERR(new_nsp->ima_ns_for_children)) {
err = PTR_ERR(new_nsp->ima_ns_for_children);
goto out_ima;
}
new_nsp->ima_ns = get_ima_ns(tsk->nsproxy->ima_ns);
return new_nsp;
out_ima:
put_time_ns(new_nsp->time_ns);
put_time_ns(new_nsp->time_ns_for_children);
out_time:
put_net(new_nsp->net_ns);
out_net:
......@@ -157,8 +173,10 @@ int copy_namespaces(unsigned long flags, struct task_struct *tsk)
if (likely(!(flags & (CLONE_NEWNS | CLONE_NEWUTS | CLONE_NEWIPC |
CLONE_NEWPID | CLONE_NEWNET |
CLONE_NEWCGROUP | CLONE_NEWTIME)))) {
if (likely(old_ns->time_ns_for_children == old_ns->time_ns)) {
CLONE_NEWCGROUP | CLONE_NEWTIME |
CLONE_NEWIMA)))) {
if (likely((old_ns->time_ns_for_children == old_ns->time_ns) &&
(old_ns->ima_ns_for_children == old_ns->ima_ns))) {
get_nsproxy(old_ns);
return 0;
}
......@@ -186,6 +204,12 @@ int copy_namespaces(unsigned long flags, struct task_struct *tsk)
return ret;
}
ret = imans_on_fork(new_ns, tsk);
if (ret) {
free_nsproxy(new_ns);
return ret;
}
tsk->nsproxy = new_ns;
return 0;
}
......@@ -204,6 +228,10 @@ void free_nsproxy(struct nsproxy *ns)
put_time_ns(ns->time_ns);
if (ns->time_ns_for_children)
put_time_ns(ns->time_ns_for_children);
if (ns->ima_ns)
put_ima_ns(ns->ima_ns);
if (ns->ima_ns_for_children)
put_ima_ns(ns->ima_ns_for_children);
put_cgroup_ns(ns->cgroup_ns);
put_net(ns->net_ns);
kmem_cache_free(nsproxy_cachep, ns);
......@@ -221,7 +249,7 @@ int unshare_nsproxy_namespaces(unsigned long unshare_flags,
if (!(unshare_flags & (CLONE_NEWNS | CLONE_NEWUTS | CLONE_NEWIPC |
CLONE_NEWNET | CLONE_NEWPID | CLONE_NEWCGROUP |
CLONE_NEWTIME)))
CLONE_NEWTIME | CLONE_NEWIMA)))
return 0;
user_ns = new_cred ? new_cred->user_ns : current_user_ns();
......@@ -476,6 +504,14 @@ static int validate_nsset(struct nsset *nsset, struct pid *pid)
}
#endif
#ifdef CONFIG_IMA_NS
if (flags & CLONE_NEWIMA) {
ret = validate_ns(nsset, &nsp->ima_ns->ns);
if (ret)
goto out;
}
#endif
out:
if (pid_ns)
put_pid_ns(pid_ns);
......
......@@ -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"),
......
......@@ -15,3 +15,4 @@ 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_DIGEST_LIST) += ima_digest_list.o
ima-$(CONFIG_IMA_NS) += ima_ns.o
......@@ -20,6 +20,7 @@
#include <linux/hash.h>
#include <linux/tpm.h>
#include <linux/audit.h>
#include <linux/ima.h>
#include <crypto/hash_info.h>
#include "../integrity.h"
......@@ -362,6 +363,18 @@ static inline enum integrity_status ima_get_cache_status(struct integrity_iint_c
#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);
......
......@@ -275,7 +275,7 @@ static const struct file_operations ima_ascii_measurements_ops = {
.release = seq_release,
};
static ssize_t ima_read_file(char *path, struct dentry *dentry)
static ssize_t ima_read_sfs_file(char *path, struct dentry *dentry)
{
void *data = NULL;
char *datap;
......@@ -398,7 +398,7 @@ static ssize_t ima_write_data(struct file *file, const char __user *buf,
goto out_free;
if (data[0] == '/') {
result = ima_read_file(data, dentry);
result = ima_read_sfs_file(data, dentry);
} else if (dentry == ima_policy) {
if (ima_appraise & IMA_APPRAISE_POLICY) {
pr_err("signed policy file (specified "
......
......@@ -15,6 +15,9 @@
#include <linux/scatterlist.h>
#include <linux/slab.h>
#include <linux/err.h>
#include <linux/kref.h>
#include <linux/proc_ns.h>
#include <linux/user_namespace.h>
#include "ima.h"
......@@ -22,6 +25,16 @@
const char boot_aggregate_name[] = "boot_aggregate";
struct tpm_chip *ima_tpm_chip;
struct ima_namespace init_ima_ns = {
.kref = KREF_INIT(2),
.user_ns = &init_user_ns,
.ns.inum = PROC_IMA_INIT_INO,
#ifdef CONFIG_IMA_NS
.ns.ops = &imans_operations,
#endif
};
EXPORT_SYMBOL(init_ima_ns);
/* Add the boot aggregate to the IMA measurement list and extend
* the PCR register.
*
......
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2019-2020 Huawei Technologies Duesseldorf GmbH
*
* Author: Krzysztof Struczynski <krzysztof.struczynski@huawei.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation, version 2 of the
* License.
*
* File: ima_ns.c
* Functions to manage the IMA namespace.
*/
#include <linux/export.h>
#include <linux/ima.h>
#include <linux/kref.h>
#include <linux/proc_ns.h>
#include <linux/slab.h>
#include <linux/user_namespace.h>
#include <linux/nsproxy.h>
#include <linux/sched.h>
#include "ima.h"
static struct ucounts *inc_ima_namespaces(struct user_namespace *ns)
{
return inc_ucount(ns, current_euid(), UCOUNT_IMA_NAMESPACES);
}
static void dec_ima_namespaces(struct ucounts *ucounts)
{
return dec_ucount(ucounts, UCOUNT_IMA_NAMESPACES);
}
static struct ima_namespace *ima_ns_alloc(void)
{
struct ima_namespace *ima_ns;
ima_ns = kzalloc(sizeof(*ima_ns), GFP_KERNEL);
if (!ima_ns)
return NULL;
return ima_ns;
}
/**
* Clone a new ns copying an original ima namespace, setting refcount to 1
*
* @user_ns: User namespace that current task runs in
* @old_ns: Old ima namespace to clone
* Return: ERR_PTR(-ENOMEM) on error (failure to kmalloc), new ns otherwise
*/
static struct ima_namespace *clone_ima_ns(struct user_namespace *user_ns,
struct ima_namespace *old_ns)
{
struct ima_namespace *ns;
struct ucounts *ucounts;
int err;
err = -ENOSPC;
ucounts = inc_ima_namespaces(user_ns);
if (!ucounts)
goto fail;
err = -ENOMEM;
ns = ima_ns_alloc();
if (!ns)
goto fail_dec;
kref_init(&ns->kref);
err = ns_alloc_inum(&ns->ns);
if (err)
goto fail_free;
ns->ns.ops = &imans_operations;
ns->user_ns = get_user_ns(user_ns);
ns->ucounts = ucounts;
return ns;
fail_free:
kfree(ns);
fail_dec:
dec_ima_namespaces(ucounts);
fail:
return ERR_PTR(err);
}
/**
* Copy task's ima namespace, or clone it if flags specifies CLONE_NEWNS.
*
* @flags: Cloning flags
* @user_ns: User namespace that current task runs in
* @old_ns: Old ima namespace to clone
*
* Return: IMA namespace or ERR_PTR.
*/
struct ima_namespace *copy_ima_ns(unsigned long flags,
struct user_namespace *user_ns,
struct ima_namespace *old_ns)
{
if (!(flags & CLONE_NEWIMA))
return get_ima_ns(old_ns);
return clone_ima_ns(user_ns, old_ns);
}
static void destroy_ima_ns(struct ima_namespace *ns)
{
dec_ima_namespaces(ns->ucounts);
put_user_ns(ns->user_ns);
ns_free_inum(&ns->ns);
kfree(ns);
}
void free_ima_ns(struct kref *kref)
{
struct ima_namespace *ns;
ns = container_of(kref, struct ima_namespace, kref);
destroy_ima_ns(ns);
}
static inline struct ima_namespace *to_ima_ns(struct ns_common *ns)
{
return container_of(ns, struct ima_namespace, ns);
}
static struct ns_common *imans_get(struct task_struct *task)
{
struct ima_namespace *ns = NULL;
struct nsproxy *nsproxy;
task_lock(task);
nsproxy = task->nsproxy;
if (nsproxy) {
ns = nsproxy->ima_ns;
get_ima_ns(ns);
}
task_unlock(task);
return ns ? &ns->ns : NULL;
}
static struct ns_common *imans_for_children_get(struct task_struct *task)
{
struct ima_namespace *ns = NULL;
struct nsproxy *nsproxy;
task_lock(task);
nsproxy = task->nsproxy;
if (nsproxy) {
ns = nsproxy->ima_ns_for_children;
get_ima_ns(ns);
}
task_unlock(task);
return ns ? &ns->ns : NULL;
}
static void imans_put(struct ns_common *ns)
{
put_ima_ns(to_ima_ns(ns));
}
static int imans_install(struct nsset *nsset, struct ns_common *new)
{
struct nsproxy *nsproxy = nsset->nsproxy;
struct ima_namespace *ns = to_ima_ns(new);
if (!current_is_single_threaded())
return -EUSERS;
if (!ns_capable(ns->user_ns, CAP_SYS_ADMIN) ||
!ns_capable(nsset->cred->user_ns, CAP_SYS_ADMIN))
return -EPERM;
get_ima_ns(ns);
put_ima_ns(nsproxy->ima_ns);
nsproxy->ima_ns = ns;
get_ima_ns(ns);
put_ima_ns(nsproxy->ima_ns_for_children);
nsproxy->ima_ns_for_children = ns;
return 0;
}
int imans_on_fork(struct nsproxy *nsproxy, struct task_struct *tsk)
{
struct ns_common *nsc = &nsproxy->ima_ns_for_children->ns;
struct ima_namespace *ns = to_ima_ns(nsc);
/* create_new_namespaces() already incremented the ref counter */
if (nsproxy->ima_ns == nsproxy->ima_ns_for_children)
return 0;
get_ima_ns(ns);
put_ima_ns(nsproxy->ima_ns);
nsproxy->ima_ns = ns;
return 0;
}
static struct user_namespace *imans_owner(struct ns_common *ns)
{
return to_ima_ns(ns)->user_ns;
}
const struct proc_ns_operations imans_operations = {
.name = "ima",
.type = CLONE_NEWIMA,
.get = imans_get,
.put = imans_put,
.install = imans_install,
.owner = imans_owner,
};
const struct proc_ns_operations imans_for_children_operations = {
.name = "ima_for_children",
.type = CLONE_NEWIMA,
.get = imans_for_children_get,
.put = imans_put,
.install = imans_install,
.owner = imans_owner,
};
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册