From: Krzysztof Struczynski krzysztof.struczynski@huawei.com
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: Krzysztof Struczynski krzysztof.struczynski@huawei.com Reviewed-by: Zhang Tianxing zhangtianxing3@huawei.com Signed-off-by: Zheng Zengkai zhengzengkai@huawei.com --- 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 | 42 +++++- kernel/ucount.c | 1 + security/integrity/ima/Makefile | 1 + security/integrity/ima/ima.h | 13 ++ security/integrity/ima/ima_fs.c | 4 +- security/integrity/ima/ima_init.c | 13 ++ security/integrity/ima/ima_ns.c | 232 ++++++++++++++++++++++++++++++ 15 files changed, 402 insertions(+), 11 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 f7a088b2579e..67af79961e4e 100644 --- a/include/linux/ima.h +++ b/include/linux/ima.h @@ -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 */ 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..c8c596d67629 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; + 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 diff --git a/include/linux/user_namespace.h b/include/linux/user_namespace.h index 7616c7bf4b24..3eb64a50f248 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 fb3eb910f224..b095baa2b83c 100644 --- a/init/Kconfig +++ b/init/Kconfig @@ -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 diff --git a/kernel/fork.c b/kernel/fork.c index a1db14fc6656..705c3a8dcecd 100644 --- a/kernel/fork.c +++ b/kernel/fork.c @@ -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 diff --git a/kernel/nsproxy.c b/kernel/nsproxy.c index 12dd41b39a7f..e2cddc22dc53 100644 --- a/kernel/nsproxy.c +++ b/kernel/nsproxy.c @@ -19,6 +19,7 @@ #include <net/net_namespace.h> #include <linux/ipc_namespace.h> #include <linux/time_namespace.h> +#include <linux/ima.h> #include <linux/fs_struct.h> #include <linux/proc_fs.h> #include <linux/proc_ns.h> @@ -47,6 +48,10 @@ struct nsproxy init_nsproxy = { .time_ns = &init_time_ns, .time_ns_for_children = &init_time_ns, #endif +#ifdef CONFIG_IMA_NS + .ima_ns = &init_ima_ns, + .ima_ns_for_children = &init_ima_ns, +#endif };
static inline struct nsproxy *create_nsproxy(void) @@ -121,8 +126,19 @@ static struct nsproxy *create_new_namespaces(unsigned long flags, } new_nsp->time_ns = get_time_ns(tsk->nsproxy->time_ns);
+ new_nsp->ima_ns_for_children = copy_ima_ns(flags, user_ns, + tsk->nsproxy->ima_ns_for_children); + if (IS_ERR(new_nsp->ima_ns_for_children)) { + err = PTR_ERR(new_nsp->ima_ns_for_children); + goto out_ima; + } + new_nsp->ima_ns = get_ima_ns(tsk->nsproxy->ima_ns); + return new_nsp;
+out_ima: + put_time_ns(new_nsp->time_ns); + put_time_ns(new_nsp->time_ns_for_children); out_time: put_net(new_nsp->net_ns); out_net: @@ -157,8 +173,10 @@ int copy_namespaces(unsigned long flags, struct task_struct *tsk)
if (likely(!(flags & (CLONE_NEWNS | CLONE_NEWUTS | CLONE_NEWIPC | CLONE_NEWPID | CLONE_NEWNET | - CLONE_NEWCGROUP | CLONE_NEWTIME)))) { - if (likely(old_ns->time_ns_for_children == old_ns->time_ns)) { + CLONE_NEWCGROUP | CLONE_NEWTIME | + CLONE_NEWIMA)))) { + if (likely((old_ns->time_ns_for_children == old_ns->time_ns) && + (old_ns->ima_ns_for_children == old_ns->ima_ns))) { get_nsproxy(old_ns); return 0; } @@ -186,6 +204,12 @@ int copy_namespaces(unsigned long flags, struct task_struct *tsk) return ret; }
+ ret = imans_on_fork(new_ns, tsk); + if (ret) { + free_nsproxy(new_ns); + return ret; + } + tsk->nsproxy = new_ns; return 0; } @@ -204,6 +228,10 @@ void free_nsproxy(struct nsproxy *ns) put_time_ns(ns->time_ns); if (ns->time_ns_for_children) put_time_ns(ns->time_ns_for_children); + if (ns->ima_ns) + put_ima_ns(ns->ima_ns); + if (ns->ima_ns_for_children) + put_ima_ns(ns->ima_ns_for_children); put_cgroup_ns(ns->cgroup_ns); put_net(ns->net_ns); kmem_cache_free(nsproxy_cachep, ns); @@ -221,7 +249,7 @@ int unshare_nsproxy_namespaces(unsigned long unshare_flags,
if (!(unshare_flags & (CLONE_NEWNS | CLONE_NEWUTS | CLONE_NEWIPC | CLONE_NEWNET | CLONE_NEWPID | CLONE_NEWCGROUP | - CLONE_NEWTIME))) + CLONE_NEWTIME | CLONE_NEWIMA))) return 0;
user_ns = new_cred ? new_cred->user_ns : current_user_ns(); @@ -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); 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 9dda78739c85..7c7272b8df65 100644 --- a/security/integrity/ima/Makefile +++ b/security/integrity/ima/Makefile @@ -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 diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h index bd554510d67f..dc536da8058e 100644 --- a/security/integrity/ima/ima.h +++ b/security/integrity/ima/ima.h @@ -20,6 +20,7 @@ #include <linux/hash.h> #include <linux/tpm.h> #include <linux/audit.h> +#include <linux/ima.h> #include <crypto/hash_info.h>
#include "../integrity.h" @@ -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); diff --git a/security/integrity/ima/ima_fs.c b/security/integrity/ima/ima_fs.c index 96eeee9e12c1..e3d5f4154586 100644 --- a/security/integrity/ima/ima_fs.c +++ b/security/integrity/ima/ima_fs.c @@ -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 " diff --git a/security/integrity/ima/ima_init.c b/security/integrity/ima/ima_init.c index 913d6b879b0b..b1c341e239a8 100644 --- a/security/integrity/ima/ima_init.c +++ b/security/integrity/ima/ima_init.c @@ -15,6 +15,9 @@ #include <linux/scatterlist.h> #include <linux/slab.h> #include <linux/err.h> +#include <linux/kref.h> +#include <linux/proc_ns.h> +#include <linux/user_namespace.h>
#include "ima.h"
@@ -22,6 +25,16 @@ const char boot_aggregate_name[] = "boot_aggregate"; struct tpm_chip *ima_tpm_chip;
+struct ima_namespace init_ima_ns = { + .kref = KREF_INIT(2), + .user_ns = &init_user_ns, + .ns.inum = PROC_IMA_INIT_INO, +#ifdef CONFIG_IMA_NS + .ns.ops = &imans_operations, +#endif +}; +EXPORT_SYMBOL(init_ima_ns); + /* Add the boot aggregate to the IMA measurement list and extend * the PCR register. * diff --git a/security/integrity/ima/ima_ns.c b/security/integrity/ima/ima_ns.c new file mode 100644 index 000000000000..8f5f301406a2 --- /dev/null +++ b/security/integrity/ima/ima_ns.c @@ -0,0 +1,232 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2019-2020 Huawei Technologies Duesseldorf GmbH + * + * Author: Krzysztof Struczynski 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, +}; +
From: Krzysztof Struczynski krzysztof.struczynski@huawei.com
hulk inclusion category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/I49KW1 CVE: NA
--------------------------------
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 krzysztof.struczynski@huawei.com Reviewed-by: Zhang Tianxing zhangtianxing3@huawei.com Signed-off-by: Zheng Zengkai zhengzengkai@huawei.com --- 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 67af79961e4e..77e7f512f61b 100644 --- a/include/linux/ima.h +++ b/include/linux/ima.h @@ -11,10 +11,12 @@ #include <linux/fs.h> #include <linux/security.h> #include <linux/kexec.h> -struct linux_binprm;
+struct linux_binprm; struct nsproxy; struct task_struct; +struct list_head; +struct llist_node;
#ifdef CONFIG_IMA extern int ima_bprm_check(struct linux_binprm *bprm); @@ -206,6 +208,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 dc536da8058e..da7e732b7e80 100644 --- a/security/integrity/ima/ima.h +++ b/security/integrity/ima/ima.h @@ -363,12 +363,23 @@ static inline enum integrity_status ima_get_cache_status(struct integrity_iint_c
#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 b1c341e239a8..2238fb21eaf0 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);
@@ -158,6 +159,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 f3242727634a..fbec552886c1 100644 --- a/security/integrity/ima/ima_main.c +++ b/security/integrity/ima/ima_main.c @@ -47,6 +47,9 @@ bool ima_plus_standard_pcr;
static int hash_setup_done;
+DECLARE_RWSEM(ima_ns_list_lock); +LIST_HEAD(ima_ns_list); + static struct notifier_block ima_lsm_policy_notifier = { .notifier_call = ima_lsm_policy_change, }; diff --git a/security/integrity/ima/ima_ns.c b/security/integrity/ima/ima_ns.c index 8f5f301406a2..3a98cd536d05 100644 --- a/security/integrity/ima/ima_ns.c +++ b/security/integrity/ima/ima_ns.c @@ -21,9 +21,20 @@ #include <linux/user_namespace.h> #include <linux/nsproxy.h> #include <linux/sched.h> +#include <linux/list.h> +#include <linux/llist.h> +#include <linux/rwsem.h> +#include <linux/workqueue.h> +#include <linux/mutex.h>
#include "ima.h"
+static LLIST_HEAD(cleanup_list); +static struct workqueue_struct *imans_wq; + +/* Protects tasks entering the same, not yet active namespace */ +static DEFINE_MUTEX(frozen_lock); + static struct ucounts *inc_ima_namespaces(struct user_namespace *ns) { return inc_ucount(ns, current_euid(), UCOUNT_IMA_NAMESPACES); @@ -78,6 +89,7 @@ static struct ima_namespace *clone_ima_ns(struct user_namespace *user_ns, ns->ns.ops = &imans_operations; ns->user_ns = get_user_ns(user_ns); ns->ucounts = ucounts; + ns->frozen = false;
return ns;
@@ -109,6 +121,19 @@ struct ima_namespace *copy_ima_ns(unsigned long flags, return clone_ima_ns(user_ns, old_ns); }
+int __init ima_init_namespace(void) +{ + /* Create workqueue for cleanup */ + imans_wq = create_singlethread_workqueue("imans"); + if (unlikely(!imans_wq)) + return -ENOMEM; + + /* No other reader or writer at this stage */ + list_add_tail(&init_ima_ns.list, &ima_ns_list); + + return 0; +} + static void destroy_ima_ns(struct ima_namespace *ns) { dec_ima_namespaces(ns->ucounts); @@ -117,13 +142,46 @@ static void destroy_ima_ns(struct ima_namespace *ns) kfree(ns); }
+static void cleanup_ima(struct work_struct *work) +{ + struct ima_namespace *ima_ns, *tmp; + struct llist_node *ima_kill_list; + + /* Atomically snapshot the list of namespaces to cleanup */ + ima_kill_list = llist_del_all(&cleanup_list); + + /* Remove ima namespace from the namespace list */ + down_write(&ima_ns_list_lock); + llist_for_each_entry(ima_ns, ima_kill_list, cleanup_list) + list_del(&ima_ns->list); + up_write(&ima_ns_list_lock); + + /* After removing ima namespace from the ima_ns_list, memory can be + * freed. At this stage nothing should keep a reference to the given + * namespace. + */ + llist_for_each_entry_safe(ima_ns, tmp, ima_kill_list, cleanup_list) + destroy_ima_ns(ima_ns); +} + +static DECLARE_WORK(ima_cleanup_work, cleanup_ima); + void free_ima_ns(struct kref *kref) { - struct ima_namespace *ns; + struct ima_namespace *ima_ns;
- ns = container_of(kref, struct ima_namespace, kref); + ima_ns = container_of(kref, struct ima_namespace, kref); + /* Namespace can be destroyed instantly if no process ever was born + * into it - it was never added to the ima_ns_list. + */ + if (!ima_ns->frozen) { + destroy_ima_ns(ima_ns); + return; + }
- destroy_ima_ns(ns); + atomic_set(&ima_ns->inactive, 1); + if (llist_add(&ima_ns->cleanup_list, &cleanup_list)) + queue_work(imans_wq, &ima_cleanup_work); }
static inline struct ima_namespace *to_ima_ns(struct ns_common *ns) @@ -168,8 +226,32 @@ static void imans_put(struct ns_common *ns) put_ima_ns(to_ima_ns(ns)); }
+static int imans_activate(struct ima_namespace *ima_ns) +{ + if (ima_ns == &init_ima_ns) + return 0; + + if (ima_ns->frozen) + return 0; + + mutex_lock(&frozen_lock); + if (ima_ns->frozen) + goto out; + + ima_ns->frozen = true; + + down_write(&ima_ns_list_lock); + list_add_tail(&ima_ns->list, &ima_ns_list); + up_write(&ima_ns_list_lock); +out: + mutex_unlock(&frozen_lock); + + return 0; +} + static int imans_install(struct nsset *nsset, struct ns_common *new) { + int res; struct nsproxy *nsproxy = nsset->nsproxy; struct ima_namespace *ns = to_ima_ns(new);
@@ -180,6 +262,10 @@ static int imans_install(struct nsset *nsset, struct ns_common *new) !ns_capable(nsset->cred->user_ns, CAP_SYS_ADMIN)) return -EPERM;
+ res = imans_activate(ns); + if (res) + return res; + get_ima_ns(ns); put_ima_ns(nsproxy->ima_ns); nsproxy->ima_ns = ns; @@ -188,11 +274,12 @@ static int imans_install(struct nsset *nsset, struct ns_common *new) put_ima_ns(nsproxy->ima_ns_for_children); nsproxy->ima_ns_for_children = ns;
- return 0; + return res; }
int imans_on_fork(struct nsproxy *nsproxy, struct task_struct *tsk) { + int res; struct ns_common *nsc = &nsproxy->ima_ns_for_children->ns; struct ima_namespace *ns = to_ima_ns(nsc);
@@ -200,11 +287,15 @@ int imans_on_fork(struct nsproxy *nsproxy, struct task_struct *tsk) if (nsproxy->ima_ns == nsproxy->ima_ns_for_children) return 0;
+ res = imans_activate(ns); + if (res) + return res; + get_ima_ns(ns); put_ima_ns(nsproxy->ima_ns); nsproxy->ima_ns = ns;
- return 0; + return res; }
static struct user_namespace *imans_owner(struct ns_common *ns)
From: Krzysztof Struczynski krzysztof.struczynski@huawei.com
hulk inclusion category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/I49KW1 CVE: NA
--------------------------------
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 krzysztof.struczynski@huawei.com Reviewed-by: Zhang Tianxing zhangtianxing3@huawei.com Signed-off-by: Zheng Zengkai zhengzengkai@huawei.com --- fs/file_table.c | 6 ++++- include/linux/fs.h | 3 +++ include/linux/ima.h | 6 +++++ security/integrity/ima/ima_main.c | 37 +++++++++++++++++++++++++++++-- 4 files changed, 49 insertions(+), 3 deletions(-)
diff --git a/fs/file_table.c b/fs/file_table.c index 709ada3151da..40020a81c216 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 700a262611e7..163a02c780a9 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -950,6 +950,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 77e7f512f61b..88734870af59 100644 --- a/include/linux/ima.h +++ b/include/linux/ima.h @@ -22,6 +22,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); @@ -70,6 +71,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 fbec552886c1..c124ad4f2c82 100644 --- a/security/integrity/ima/ima_main.c +++ b/security/integrity/ima/ima_main.c @@ -238,6 +238,30 @@ 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) +{ + /* It is possible that ima_file_alloc() is called after + * exit_task_namespaces(), when IMA does the last writer check from + * __fput(). In that case it's not necessary to store the namespace + * information */ + if (!current->nsproxy) + return 0; + + 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 @@ -248,15 +272,24 @@ 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 (!ima_policy_flag || !S_ISREG(inode->i_mode)) + if (!ima_ns) return;
+ if (unlikely(!(file->f_mode & FMODE_OPENED))) + goto out; + + if (!ima_policy_flag || !S_ISREG(inode->i_mode)) + 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: Krzysztof Struczynski krzysztof.struczynski@huawei.com
hulk inclusion category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/I49KW1 CVE: NA
--------------------------------
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 krzysztof.struczynski@huawei.com Reviewed-by: Zhang Tianxing zhangtianxing3@huawei.com Signed-off-by: Zheng Zengkai zhengzengkai@huawei.com --- 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 88734870af59..5cb5659c0a06 100644 --- a/include/linux/ima.h +++ b/include/linux/ima.h @@ -17,6 +17,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); @@ -218,6 +219,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 da7e732b7e80..6a59c8b9bc4c 100644 --- a/security/integrity/ima/ima.h +++ b/security/integrity/ima/ima.h @@ -63,6 +63,14 @@ extern size_t ima_digest_db_max_size __ro_after_init; extern size_t ima_digest_db_size; #endif
+/* 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; @@ -291,6 +299,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 *); @@ -363,6 +373,20 @@ static inline enum integrity_status ima_get_cache_status(struct integrity_iint_c
#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 2238fb21eaf0..9f0e9dc3b77f 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 8e288da66503..bdfe5ec0a79a 100644 --- a/security/integrity/ima/ima_policy.c +++ b/security/integrity/ima/ima_policy.c @@ -235,6 +235,17 @@ static struct list_head *ima_rules = &ima_default_rules;
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) @@ -839,6 +850,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: Krzysztof Struczynski krzysztof.struczynski@huawei.com
hulk inclusion category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/I49KW1 CVE: NA
--------------------------------
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 krzysztof.struczynski@huawei.com Reviewed-by: Zhang Tianxing zhangtianxing3@huawei.com Signed-off-by: Zheng Zengkai zhengzengkai@huawei.com --- security/integrity/ima/ima.h | 10 ++++++++ security/integrity/ima/ima_appraise.c | 10 ++++++++ security/integrity/ima/ima_policy.c | 37 +++++++++++++++++++++++++++ 3 files changed, 57 insertions(+)
diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h index 6a59c8b9bc4c..e62bd2dfa595 100644 --- a/security/integrity/ima/ima.h +++ b/security/integrity/ima/ima.h @@ -311,6 +311,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 3fb378017f9d..ad7715822e06 100644 --- a/security/integrity/ima/ima_appraise.c +++ b/security/integrity/ima/ima_appraise.c @@ -18,6 +18,16 @@ #include "ima_digest_list.h"
static bool ima_appraise_req_evm __ro_after_init; + +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 bdfe5ec0a79a..fe5d0f311f1c 100644 --- a/security/integrity/ima/ima_policy.c +++ b/security/integrity/ima/ima_policy.c @@ -246,6 +246,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) @@ -261,6 +270,25 @@ static bool ima_use_appraise_exec_tcb __initdata; static bool ima_use_appraise_exec_immutable __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; @@ -290,6 +318,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: Krzysztof Struczynski krzysztof.struczynski@huawei.com
hulk inclusion category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/I49KW1 CVE: NA
--------------------------------
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 krzysztof.struczynski@huawei.com Reviewed-by: Zhang Tianxing zhangtianxing3@huawei.com Signed-off-by: Zheng Zengkai zhengzengkai@huawei.com --- include/linux/ima.h | 4 +- security/integrity/ima/ima.h | 28 ++++++++----- security/integrity/ima/ima_api.c | 9 ++-- 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 | 43 +++++++++++--------- security/integrity/ima/ima_policy.c | 42 +++++++++++-------- security/integrity/ima/ima_queue_keys.c | 3 +- 9 files changed, 90 insertions(+), 60 deletions(-)
diff --git a/include/linux/ima.h b/include/linux/ima.h index 5cb5659c0a06..7f847cf0297c 100644 --- a/include/linux/ima.h +++ b/include/linux/ima.h @@ -152,7 +152,7 @@ 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); @@ -164,7 +164,7 @@ extern int ima_inode_removexattr(struct dentry *dentry, const char *xattr_name); extern void ima_inode_post_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 e62bd2dfa595..8e181863565b 100644 --- a/security/integrity/ima/ima.h +++ b/security/integrity/ima/ima.h @@ -268,7 +268,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, + 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, @@ -278,10 +279,12 @@ void ima_store_measurement(struct integrity_iint_cache *iint, struct file *file, struct evm_ima_xattr_data *xattr_value, int xattr_len, const struct modsig *modsig, int pcr, struct ima_template_desc *template_desc, - struct ima_digest *digest); + struct ima_digest *digest, + struct ima_namespace *ima_ns); 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, @@ -297,15 +300,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, + 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); @@ -333,20 +337,23 @@ 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, struct ima_digest *found_digest); -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); #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; } @@ -364,7 +371,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 d9f4599dee40..b4347eac9c85 100644 --- a/security/integrity/ima/ima_api.c +++ b/security/integrity/ima/ima_api.c @@ -191,6 +191,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= @@ -206,14 +207,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, + 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); }
/* @@ -318,7 +320,8 @@ void ima_store_measurement(struct integrity_iint_cache *iint, struct evm_ima_xattr_data *xattr_value, int xattr_len, const struct modsig *modsig, int pcr, struct ima_template_desc *template_desc, - struct ima_digest *digest) + struct ima_digest *digest, + struct ima_namespace *ima_ns) { static const char op[] = "add_template_measure"; static const char audit_cause[] = "ENOMEM"; diff --git a/security/integrity/ima/ima_appraise.c b/security/integrity/ima/ima_appraise.c index ad7715822e06..9213c012cbe4 100644 --- a/security/integrity/ima/ima_appraise.c +++ b/security/integrity/ima/ima_appraise.c @@ -81,10 +81,11 @@ __setup("ima_appraise_digest_list=", appraise_digest_list_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; } @@ -94,7 +95,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;
@@ -103,7 +105,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, @@ -331,7 +334,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; @@ -348,7 +352,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; @@ -575,7 +579,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); iint = integrity_iint_find(inode); if (iint) { set_bit(IMA_CHANGE_ATTR, &iint->atomic_flags); 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 e3d5f4154586..8c45e404351e 100644 --- a/security/integrity/ima/ima_fs.c +++ b/security/integrity/ima/ima_fs.c @@ -527,7 +527,7 @@ static int ima_release_data_upload(struct inode *inode, struct file *file) return 0; }
- 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 c124ad4f2c82..422b253006fb 100644 --- a/security/integrity/ima/ima_main.c +++ b/security/integrity/ima/ima_main.c @@ -124,7 +124,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; @@ -294,7 +295,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; @@ -319,7 +321,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) @@ -341,7 +343,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);
@@ -442,10 +444,11 @@ static int process_measurement(struct file *file, const struct cred *cred, xattr_value, xattr_len, modsig, pcr, template_desc, ima_digest_allow(found_digest, - IMA_MEASURE)); + IMA_MEASURE), + ima_ns);
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, @@ -501,7 +504,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; @@ -540,7 +543,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))) @@ -579,13 +582,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); }
/** @@ -606,7 +609,7 @@ int ima_file_check(struct file *file, int mask) security_task_getsecid(current, &secid); rc = process_measurement(file, current_cred(), secid, NULL, 0, mask & (MAY_READ | MAY_WRITE | MAY_EXEC | - MAY_APPEND), FILE_CHECK); + MAY_APPEND), FILE_CHECK, NULL); if (ima_current_is_parser() && !rc) ima_check_measured_appraised(file); return rc; @@ -685,7 +688,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;
@@ -712,7 +715,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;
@@ -763,7 +766,7 @@ int ima_read_file(struct file *file, enum kernel_read_file_id read_id, func = read_idmap[read_id] ?: FILE_CHECK; security_task_getsecid(current, &secid); return process_measurement(file, current_cred(), secid, NULL, - 0, MAY_READ, func); + 0, MAY_READ, func, NULL); }
const int read_idmap[READING_MAX_ID] = { @@ -807,7 +810,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); }
/** @@ -905,7 +908,8 @@ int ima_post_load_data(char *buf, loff_t size, */ 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"; @@ -937,7 +941,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; } @@ -1009,7 +1013,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); }
@@ -1038,7 +1043,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 fe5d0f311f1c..ebb4721032d4 100644 --- a/security/integrity/ima/ima_policy.c +++ b/security/integrity/ima/ima_policy.c @@ -692,6 +692,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. @@ -703,7 +704,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, + struct ima_namespace *ima_ns) { struct ima_rule_entry *entry; int action = 0, actmask = flags | (flags << 1); @@ -756,8 +758,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;
@@ -786,7 +789,8 @@ static int ima_appraise_flag(enum ima_hooks func) return 0; }
-static void __init add_rules(struct ima_rule_entry *entries, int count, +static void __init add_rules(struct ima_policy_data *policy_data, + struct ima_rule_entry *entries, int count, enum policy_rule_list policy_rule) { int i = 0; @@ -914,19 +918,20 @@ 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 EXEC_TCB: fallthrough; case DEFAULT_TCB: - add_rules(default_measurement_rules, + add_rules(NULL, default_measurement_rules, ARRAY_SIZE(default_measurement_rules), IMA_DEFAULT_POLICY); default: @@ -934,7 +939,7 @@ void __init ima_init_policy(void) }
if (ima_policy) - add_rules(&ima_parser_measure_rule, 1, IMA_DEFAULT_POLICY); + add_rules(NULL, &ima_parser_measure_rule, 1, IMA_DEFAULT_POLICY);
/* * Based on runtime secure boot flags, insert arch specific measurement @@ -946,7 +951,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);
/* @@ -954,7 +959,8 @@ void __init ima_init_policy(void) * signatures, prior to other appraise rules. */ if (ima_use_secure_boot || ima_use_appraise_exec_tcb) - add_rules(secure_boot_rules, ARRAY_SIZE(secure_boot_rules), + add_rules(NULL, + secure_boot_rules, ARRAY_SIZE(secure_boot_rules), IMA_DEFAULT_POLICY);
/* @@ -966,32 +972,34 @@ 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 || ima_use_appraise_exec_tcb) - add_rules(default_appraise_rules, + add_rules(NULL, default_appraise_rules, ARRAY_SIZE(default_appraise_rules), IMA_DEFAULT_POLICY);
if (ima_use_appraise_exec_tcb) - add_rules(appraise_exec_rules, + add_rules(NULL, appraise_exec_rules, ARRAY_SIZE(appraise_exec_rules), IMA_DEFAULT_POLICY);
if (ima_use_secure_boot || ima_use_appraise_tcb || ima_use_appraise_exec_tcb) - add_rules(&ima_parser_appraise_rule, 1, IMA_DEFAULT_POLICY); + add_rules(NULL, &ima_parser_appraise_rule, 1, 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; @@ -1027,7 +1035,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: Krzysztof Struczynski krzysztof.struczynski@huawei.com
hulk inclusion category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/I49KW1 CVE: NA
--------------------------------
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 krzysztof.struczynski@huawei.com Reviewed-by: Zhang Tianxing zhangtianxing3@huawei.com Signed-off-by: Zheng Zengkai zhengzengkai@huawei.com --- 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 2ea0f2f65ab6..4a38310e6e7e 100644 --- a/include/linux/integrity.h +++ b/include/linux/integrity.h @@ -8,6 +8,10 @@ #define _LINUX_INTEGRITY_H
#include <linux/fs.h> +#include <linux/rwlock_types.h> + +struct rb_root; +struct integrity_iint_tree;
enum integrity_status { INTEGRITY_PASS = 0, @@ -22,8 +26,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 * @@ -32,14 +43,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 8953ac6412c3..48716d058ed1 100644 --- a/security/integrity/iint.c +++ b/security/integrity/iint.c @@ -21,19 +21,29 @@ #include <linux/lsm_hooks.h> #include "integrity.h"
-static struct rb_root integrity_iint_tree = RB_ROOT; -static DEFINE_RWLOCK(integrity_iint_lock); +struct integrity_iint_tree init_iint_tree = { + .lock = __RW_LOCK_UNLOCKED(init_iint_tree.lock), + .root = RB_ROOT +}; + static struct kmem_cache *iint_cache __read_mostly;
struct dentry *integrity_dir;
/* - * __integrity_iint_find - return the iint associated with an inode + * __integrity_iint_rb_find - return the iint associated with an inode + * @iint_rb_root: pointer to the root of the iint tree + * @inode: pointer to the inode + * @return: pointer to the iint if found, NULL otherwise */ -static struct integrity_iint_cache *__integrity_iint_find(struct inode *inode) +static struct integrity_iint_cache * + __integrity_iint_rb_find(const struct rb_root + *iint_rb_root, + const struct inode + *inode) { struct integrity_iint_cache *iint; - struct rb_node *n = integrity_iint_tree.rb_node; + struct rb_node *n = iint_rb_root->rb_node;
while (n) { iint = rb_entry(n, struct integrity_iint_cache, rb_node); @@ -52,22 +62,37 @@ static struct integrity_iint_cache *__integrity_iint_find(struct inode *inode) }
/* - * integrity_iint_find - return the iint associated with an inode + * integrity_iint_rb_find - return the iint associated with an inode + * @iint_tree: pointer to the iint tree root node and the associated lock + * @inode: pointer to the inode + * @return: pointer to the iint if found, NULL otherwise */ -struct integrity_iint_cache *integrity_iint_find(struct inode *inode) +struct integrity_iint_cache *integrity_iint_rb_find(struct integrity_iint_tree + *iint_tree, + const struct inode *inode) { struct integrity_iint_cache *iint;
if (!IS_IMA(inode)) return NULL;
- read_lock(&integrity_iint_lock); - iint = __integrity_iint_find(inode); - read_unlock(&integrity_iint_lock); + read_lock(&iint_tree->lock); + iint = __integrity_iint_rb_find(&iint_tree->root, inode); + read_unlock(&iint_tree->lock);
return iint; }
+/* + * integrity_iint_find - return the iint associated with an inode + * @inode: pointer to the inode + * @return: pointer to the iint if found, NULL otherwise + */ +struct integrity_iint_cache *integrity_iint_find(struct inode *inode) +{ + return integrity_iint_rb_find(&init_iint_tree, inode); +} + static void iint_free(struct integrity_iint_cache *iint) { kfree(iint->ima_hash); @@ -86,13 +111,36 @@ 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; @@ -106,7 +154,7 @@ struct integrity_iint_cache *integrity_inode_get(struct inode *inode) if (!iint_cache) panic("%s: lsm=integrity required.\n", __func__);
- iint = integrity_iint_find(inode); + iint = integrity_iint_rb_find(iint_tree, inode); if (iint) return iint;
@@ -114,9 +162,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, @@ -131,33 +179,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 77e6819e8db8..05f6eabef9fe 100644 --- a/security/integrity/integrity.h +++ b/security/integrity/integrity.h @@ -184,11 +184,20 @@ static inline void ima_load_digest_lists(void) } #endif
+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);
@@ -199,6 +208,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: Krzysztof Struczynski krzysztof.struczynski@huawei.com
hulk inclusion category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/I49KW1 CVE: NA
--------------------------------
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 krzysztof.struczynski@huawei.com Reviewed-by: Zhang Tianxing zhangtianxing3@huawei.com Signed-off-by: Zheng Zengkai zhengzengkai@huawei.com --- 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 7f847cf0297c..f5683756f2b5 100644 --- a/include/linux/ima.h +++ b/include/linux/ima.h @@ -220,6 +220,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 9f0e9dc3b77f..3b8839b97a98 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: Krzysztof Struczynski krzysztof.struczynski@huawei.com
hulk inclusion category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/I49KW1 CVE: NA
--------------------------------
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 krzysztof.struczynski@huawei.com Reviewed-by: Zhang Tianxing zhangtianxing3@huawei.com Signed-off-by: Zheng Zengkai zhengzengkai@huawei.com --- include/linux/ima.h | 11 + kernel/kexec_file.c | 7 + security/integrity/ima/ima.h | 12 +- security/integrity/ima/ima_api.c | 2 +- security/integrity/ima/ima_appraise.c | 95 +++-- security/integrity/ima/ima_asymmetric_keys.c | 13 +- security/integrity/ima/ima_digest_list.c | 4 +- security/integrity/ima/ima_fs.c | 6 +- security/integrity/ima/ima_init.c | 7 +- security/integrity/ima/ima_main.c | 240 +++++++++---- security/integrity/ima/ima_ns.c | 3 + security/integrity/ima/ima_policy.c | 345 +++++++++++-------- security/integrity/ima/ima_queue_keys.c | 10 +- security/security.c | 2 +- 14 files changed, 507 insertions(+), 250 deletions(-)
diff --git a/include/linux/ima.h b/include/linux/ima.h index f5683756f2b5..d7b2864d7d7c 100644 --- a/include/linux/ima.h +++ b/include/linux/ima.h @@ -37,6 +37,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); @@ -128,6 +130,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 33400ff051a8..d407f7983020 100644 --- a/kernel/kexec_file.c +++ b/kernel/kexec_file.c @@ -362,6 +362,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 8e181863565b..1a1c1eebc01c 100644 --- a/security/integrity/ima/ima.h +++ b/security/integrity/ima/ima.h @@ -44,15 +44,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 int ima_digest_list_pcr; extern bool ima_plus_standard_pcr; @@ -69,6 +65,8 @@ struct ima_policy_setup_data { int ima_appraise; bool ima_use_secure_boot; bool ima_use_appraise_tcb; + bool ima_use_appraise_exec_tcb; + bool ima_use_appraise_exec_immutable; };
/* IMA event related data */ @@ -402,6 +400,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;
@@ -415,6 +416,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 b4347eac9c85..743b9337d9e5 100644 --- a/security/integrity/ima/ima_api.c +++ b/security/integrity/ima/ima_api.c @@ -212,7 +212,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 9213c012cbe4..9d041e3deef2 100644 --- a/security/integrity/ima/ima_appraise.c +++ b/security/integrity/ima/ima_appraise.c @@ -21,18 +21,10 @@ static bool ima_appraise_req_evm __ro_after_init;
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 bool sb_state = arch_ima_get_secureboot(); - int appraisal_state = ima_appraise; + int appraisal_state = setup_data->ima_appraise;
if (strncmp(str, "off", 3) == 0) appraisal_state = 0; @@ -52,7 +44,7 @@ static int __init default_appraise_setup(char *str) pr_info("Secure boot enabled: ignoring ima_appraise=%s option", str); } else { - ima_appraise = appraisal_state; + setup_data->ima_appraise = appraisal_state; } #endif if (strcmp(str, "enforce-evm") == 0 || @@ -61,6 +53,11 @@ static int __init default_appraise_setup(char *str) 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);
static bool ima_appraise_no_metadata __ro_after_init; @@ -87,7 +84,10 @@ __setup("ima_appraise_digest_list=", appraise_digest_list_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; }
/* @@ -95,18 +95,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, @@ -352,7 +352,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; @@ -381,6 +381,7 @@ int ima_appraise_measurement(enum ima_hooks func, int rc = xattr_len, rc_evm; char _buf[sizeof(struct evm_ima_xattr_data) + 1 + SHA512_DIGEST_SIZE]; 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) @@ -509,7 +510,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)) @@ -574,18 +576,30 @@ 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); - iint = integrity_iint_find(inode); - if (iint) { - set_bit(IMA_CHANGE_ATTR, &iint->atomic_flags); - if (!action) - clear_bit(IMA_UPDATE_XATTR, &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); + 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); }
/* @@ -607,19 +621,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_rb_find(ima_ns->iint_tree, 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_digest_list.c b/security/integrity/ima/ima_digest_list.c index 4759f0435b29..74b08c3a3c81 100644 --- a/security/integrity/ima/ima_digest_list.c +++ b/security/integrity/ima/ima_digest_list.c @@ -167,7 +167,7 @@ int ima_parse_compact_list(loff_t size, void *buf, int op) size_t digest_len; int ret = 0, i;
- if (!(ima_digest_list_actions & ima_policy_flag)) + if (!(ima_digest_list_actions & init_policy_data.ima_policy_flag)) return -EACCES;
while (bufp < bufendp) { @@ -383,7 +383,7 @@ void __init ima_load_digest_lists(void) .ctx.actor = load_digest_list, };
- if (!(ima_digest_list_actions & ima_policy_flag)) + if (!(ima_digest_list_actions & init_policy_data.ima_policy_flag)) return;
ret = kern_path(CONFIG_IMA_DIGEST_LISTS_DIR, 0, &path); diff --git a/security/integrity/ima/ima_fs.c b/security/integrity/ima/ima_fs.c index 8c45e404351e..2ae4adfbaa82 100644 --- a/security/integrity/ima/ima_fs.c +++ b/security/integrity/ima/ima_fs.c @@ -365,6 +365,7 @@ static ssize_t ima_write_data(struct file *file, const char __user *buf, ssize_t result; struct dentry *dentry = file_dentry(file); int i; + struct ima_namespace *ima_ns = get_current_ns();
/* No partial writes. */ result = -EINVAL; @@ -400,7 +401,7 @@ static ssize_t ima_write_data(struct file *file, const char __user *buf, if (data[0] == '/') { result = ima_read_sfs_file(data, dentry); } else if (dentry == ima_policy) { - if (ima_appraise & IMA_APPRAISE_POLICY) { + 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, @@ -515,6 +516,7 @@ static int ima_release_data_upload(struct inode *inode, struct file *file) struct dentry *dentry = file_dentry(file); const char *cause = valid_policy ? "completed" : "failed"; enum ima_fs_flags flag = ima_get_dentry_flag(dentry); + struct ima_namespace *ima_ns = get_current_ns();
if ((file->f_flags & O_ACCMODE) == O_RDONLY) return seq_release(inode, file); @@ -527,7 +529,7 @@ static int ima_release_data_upload(struct inode *inode, struct file *file) return 0; }
- 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 3b8839b97a98..dea0251142fd 100644 --- a/security/integrity/ima/ima_init.c +++ b/security/integrity/ima/ima_init.c @@ -118,15 +118,16 @@ 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);
/* load also EVM key to avoid appraisal */ evm_load_x509();
- 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 422b253006fb..e0460462193d 100644 --- a/security/integrity/ima/ima_main.c +++ b/security/integrity/ima/ima_main.c @@ -30,12 +30,6 @@ #include "ima.h" #include "ima_digest_list.h"
-#ifdef CONFIG_IMA_APPRAISE -int ima_appraise = IMA_APPRAISE_ENFORCE; -#else -int ima_appraise; -#endif - int ima_hash_algo = HASH_ALGO_SHA1;
/* Actions (measure/appraisal) for which digest lists can be used */ @@ -134,7 +128,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)) @@ -214,11 +209,38 @@ static int ima_read_xattr(struct dentry *dentry, return ret; }
+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; @@ -232,6 +254,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); } @@ -281,10 +306,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;
@@ -293,10 +318,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; @@ -312,8 +337,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 @@ -323,7 +349,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;
@@ -336,7 +363,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; } @@ -351,6 +378,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);
@@ -478,7 +507,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); @@ -486,6 +516,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) @@ -504,7 +560,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; @@ -524,6 +580,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]; @@ -536,14 +593,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))) @@ -582,13 +640,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); }
/** @@ -609,7 +667,7 @@ int ima_file_check(struct file *file, int mask) security_task_getsecid(current, &secid); rc = process_measurement(file, current_cred(), secid, NULL, 0, mask & (MAY_READ | MAY_WRITE | MAY_EXEC | - MAY_APPEND), FILE_CHECK, NULL); + MAY_APPEND), FILE_CHECK); if (ima_current_is_parser() && !rc) ima_check_measured_appraised(file); return rc; @@ -618,7 +676,7 @@ 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 @@ -636,6 +694,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; @@ -643,11 +702,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;
@@ -685,21 +744,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); }
/** @@ -711,21 +779,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); }
/** @@ -766,7 +843,7 @@ int ima_read_file(struct file *file, enum kernel_read_file_id read_id, func = read_idmap[read_id] ?: FILE_CHECK; security_task_getsecid(current, &secid); return process_measurement(file, current_cred(), secid, NULL, - 0, MAY_READ, func, NULL); + 0, MAY_READ, func); }
const int read_idmap[READING_MAX_ID] = { @@ -796,13 +873,14 @@ 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();
/* permit signed certs */ if (!file && read_id == READING_X509_CERTIFICATE) 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; } @@ -810,7 +888,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); }
/** @@ -828,9 +906,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 contents) { 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: @@ -840,13 +925,16 @@ int ima_load_data(enum kernel_load_data_id id, bool contents) 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) && !contents) { + if (ima_enforce && + (ima_ns->policy_data->ima_appraise & + IMA_APPRAISE_FIRMWARE) && !contents) { pr_err("Prevent firmware sysfs fallback loading.\n"); return -EACCES; /* INTEGRITY_UNKNOWN */ } @@ -854,8 +942,10 @@ int ima_load_data(enum kernel_load_data_id id, bool contents) 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 */ } @@ -882,9 +972,14 @@ int ima_post_load_data(char *buf, loff_t size, enum kernel_load_data_id load_id, char *description) { + struct ima_namespace *ima_ns = get_current_ns(); + if (load_id == LOADING_FIRMWARE) { - if ((ima_appraise & IMA_APPRAISE_FIRMWARE) && - (ima_appraise & IMA_APPRAISE_ENFORCE)) { + if (WARN_ON(ima_ns != &init_ima_ns)) + return -EACCES; + + 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 */ } @@ -903,6 +998,7 @@ int ima_post_load_data(char *buf, loff_t size, * @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. */ @@ -928,7 +1024,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;
/* @@ -941,7 +1037,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; } @@ -1004,6 +1100,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; @@ -1014,10 +1115,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 ebb4721032d4..0ab91cb31121 100644 --- a/security/integrity/ima/ima_policy.c +++ b/security/integrity/ima/ima_policy.c @@ -49,8 +49,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 @@ -225,16 +223,11 @@ static struct ima_rule_entry ima_parser_appraise_rule __ro_after_init = { .flags = IMA_PARSER | 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; - -static int ima_policy __initdata; - struct ima_policy_setup_data init_policy_setup_data = { #ifdef CONFIG_IMA_APPRAISE .ima_appraise = IMA_APPRAISE_ENFORCE, @@ -244,31 +237,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_appraise_exec_tcb __initdata; -static bool ima_use_appraise_exec_immutable __initdata; -static bool ima_use_secure_boot __initdata; static bool ima_fail_unverifiable_sigs __ro_after_init;
/** @@ -276,61 +263,55 @@ 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; - else if ((strcmp(p, "exec_tcb") == 0) && !ima_policy) - ima_policy = EXEC_TCB; + if ((strcmp(p, "tcb") == 0) && !setup_data->ima_policy) + setup_data->ima_policy = DEFAULT_TCB; + else if ((strcmp(p, "exec_tcb") == 0) && !setup_data->ima_policy) + setup_data->ima_policy = EXEC_TCB; else if (strcmp(p, "appraise_tcb") == 0) - ima_use_appraise_tcb = true; + setup_data->ima_use_appraise_tcb = true; else if (strcmp(p, "appraise_exec_tcb") == 0) - ima_use_appraise_exec_tcb = true; + setup_data->ima_use_appraise_exec_tcb = true; else if (strcmp(p, "appraise_exec_immutable") == 0) - ima_use_appraise_exec_immutable = true; + setup_data->ima_use_appraise_exec_immutable = 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; else pr_err("policy "%s" not found", p); }
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);
@@ -505,9 +486,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;
@@ -709,12 +692,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; @@ -729,7 +713,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; }
@@ -764,14 +751,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) @@ -789,16 +777,17 @@ static int ima_appraise_flag(enum ima_hooks func) return 0; }
-static void __init add_rules(struct ima_policy_data *policy_data, - struct ima_rule_entry *entries, int count, - enum policy_rule_list policy_rule) +static void add_rules(struct ima_namespace *ima_ns, + struct ima_rule_entry *entries, int count, + enum policy_rule_list policy_rule, + const struct ima_policy_setup_data *setup_data) { int i = 0;
for (i = 0; i < count; i++) { struct ima_rule_entry *entry;
- if (ima_policy == EXEC_TCB) { + if (setup_data->ima_policy == EXEC_TCB) { if (entries == dont_measure_rules) if ((entries[i].flags & IMA_FSMAGIC) && entries[i].fsmagic == TMPFS_MAGIC) @@ -810,7 +799,7 @@ static void __init add_rules(struct ima_policy_data *policy_data, continue; }
- if (ima_use_appraise_exec_tcb) { + if (setup_data->ima_use_appraise_exec_tcb) { if (entries == default_appraise_rules) { if (entries[i].action != DONT_APPRAISE) continue; @@ -820,14 +809,24 @@ static void __init add_rules(struct ima_policy_data *policy_data, } }
- if (ima_use_appraise_exec_immutable) + if (setup_data->ima_use_appraise_exec_immutable) if (entries == appraise_exec_rules && (entries[i].flags & IMA_FUNC) && entries[i].func == BPRM_CHECK) entries[i].flags |= IMA_META_IMMUTABLE_REQUIRED;
- 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), @@ -835,11 +834,12 @@ static void __init 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 |= @@ -850,7 +850,7 @@ static void __init add_rules(struct ima_policy_data *policy_data,
static int ima_parse_rule(char *rule, struct ima_rule_entry *entry);
-static int __init ima_init_arch_policy(void) +static int ima_init_arch_policy(void) { const char * const *arch_rules; const char * const *rules; @@ -899,69 +899,71 @@ 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), - IMA_DEFAULT_POLICY); + if (setup_data->ima_policy) + add_rules(ima_ns, dont_measure_rules, + ARRAY_SIZE(dont_measure_rules), + IMA_DEFAULT_POLICY, + setup_data);
- 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); + IMA_DEFAULT_POLICY, + setup_data); break; case EXEC_TCB: fallthrough; case DEFAULT_TCB: - add_rules(NULL, default_measurement_rules, + add_rules(ima_ns, default_measurement_rules, ARRAY_SIZE(default_measurement_rules), - IMA_DEFAULT_POLICY); + IMA_DEFAULT_POLICY, + setup_data); default: break; }
- if (ima_policy) - add_rules(NULL, &ima_parser_measure_rule, 1, IMA_DEFAULT_POLICY); + if (setup_data->ima_policy) + add_rules(ima_ns, &ima_parser_measure_rule, 1, IMA_DEFAULT_POLICY, + setup_data);
- /* - * 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, - IMA_DEFAULT_POLICY | IMA_CUSTOM_POLICY); + if (arch_entries_size) + add_rules(ima_ns, + arch_policy_entry, + arch_entries_size, + IMA_DEFAULT_POLICY | IMA_CUSTOM_POLICY, + setup_data);
/* * Insert the builtin "secure_boot" policy rules requiring file * signatures, prior to other appraise rules. */ - if (ima_use_secure_boot || ima_use_appraise_exec_tcb) - add_rules(NULL, - secure_boot_rules, ARRAY_SIZE(secure_boot_rules), - IMA_DEFAULT_POLICY); + if (setup_data->ima_use_secure_boot || + setup_data->ima_use_appraise_exec_tcb) + add_rules(ima_ns, secure_boot_rules, + ARRAY_SIZE(secure_boot_rules), + IMA_DEFAULT_POLICY, + setup_data);
/* * Insert the build time appraise rules requiring file signatures @@ -970,38 +972,53 @@ 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, - IMA_CUSTOM_POLICY); + 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, + setup_data); else - add_rules(NULL, - build_appraise_rules, build_appraise_entries, - IMA_DEFAULT_POLICY | IMA_CUSTOM_POLICY); + add_rules(ima_ns, build_appraise_rules, + build_appraise_entries, + IMA_DEFAULT_POLICY | IMA_CUSTOM_POLICY, + setup_data); }
- if (ima_use_appraise_tcb || ima_use_appraise_exec_tcb) - add_rules(NULL, default_appraise_rules, + if (setup_data->ima_use_appraise_tcb || + setup_data->ima_use_appraise_exec_tcb) + add_rules(ima_ns, default_appraise_rules, ARRAY_SIZE(default_appraise_rules), - IMA_DEFAULT_POLICY); + IMA_DEFAULT_POLICY, setup_data);
- if (ima_use_appraise_exec_tcb) - add_rules(NULL, appraise_exec_rules, + if (setup_data->ima_use_appraise_exec_tcb) + add_rules(ima_ns, appraise_exec_rules, ARRAY_SIZE(appraise_exec_rules), - IMA_DEFAULT_POLICY); + IMA_DEFAULT_POLICY, setup_data);
- if (ima_use_secure_boot || ima_use_appraise_tcb || - ima_use_appraise_exec_tcb) - add_rules(NULL, &ima_parser_appraise_rule, 1, IMA_DEFAULT_POLICY); + if (setup_data->ima_use_secure_boot || + setup_data->ima_use_appraise_tcb || + setup_data->ima_use_appraise_exec_tcb) + add_rules(ima_ns, &ima_parser_appraise_rule, 1, + IMA_DEFAULT_POLICY, setup_data);
- 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; } @@ -1019,14 +1036,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 @@ -1034,8 +1055,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(); @@ -1099,6 +1121,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; @@ -1115,7 +1138,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; @@ -1279,6 +1303,7 @@ static int ima_parse_rule(char *rule, struct ima_rule_entry *entry) bool uid_token; struct ima_template_desc *template_desc; int result = 0; + struct ima_namespace *ima_ns = get_current_ns();
ab = integrity_audit_log_start(audit_context(), GFP_KERNEL, AUDIT_INTEGRITY_POLICY_RULE); @@ -1640,7 +1665,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 : @@ -1667,6 +1693,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; @@ -1693,7 +1721,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; } @@ -1705,15 +1733,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),
@@ -1737,9 +1801,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; @@ -1751,6 +1816,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(); @@ -1758,7 +1824,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) @@ -1956,6 +2022,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; @@ -1966,7 +2033,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 0c95a714168c..93db7a266bcd 100644 --- a/security/security.c +++ b/security/security.c @@ -989,7 +989,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
From: Krzysztof Struczynski krzysztof.struczynski@huawei.com
hulk inclusion category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/I49KW1 CVE: NA
--------------------------------
Add ima namespace id to the ima_event_data and ima_template_entry. This is done so that the template entries can be tracked per ima namespace. The following patches will add new templates that will include the namespace id, but the namespace id has to be stored separately so that the namespace functionality is enabled for every template.
After kexec, all entries from the old measurement list will be associated with the new root ima namespace. This will prevent users in the new ima namespaces from accessing the old entries if the ima namespace id is reused.
Signed-off-by: Krzysztof Struczynski krzysztof.struczynski@huawei.com Reviewed-by: Zhang Tianxing zhangtianxing3@huawei.com Signed-off-by: Zheng Zengkai zhengzengkai@huawei.com --- security/integrity/ima/ima.h | 10 +++++++++- security/integrity/ima/ima_api.c | 8 +++++++- security/integrity/ima/ima_init.c | 2 ++ security/integrity/ima/ima_main.c | 5 +++-- security/integrity/ima/ima_template.c | 2 ++ 5 files changed, 23 insertions(+), 4 deletions(-)
diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h index 1a1c1eebc01c..324ccfbbc1dc 100644 --- a/security/integrity/ima/ima.h +++ b/security/integrity/ima/ima.h @@ -80,6 +80,7 @@ struct ima_event_data { const char *violation; const void *buf; int buf_len; + unsigned int ns_id; };
/* IMA template field data definition */ @@ -108,6 +109,7 @@ struct ima_template_desc {
struct ima_template_entry { int pcr; + unsigned int ns_id; struct tpm_digest *digests; struct ima_template_desc *template_desc; /* template descriptor */ u32 template_data_len; @@ -158,7 +160,8 @@ int ima_calc_field_array_hash(struct ima_field_data *field_data, int ima_calc_boot_aggregate(struct ima_digest_data *hash); void ima_add_violation(struct file *file, const unsigned char *filename, struct integrity_iint_cache *iint, - const char *op, const char *cause); + const char *op, const char *cause, + struct ima_namespace *ima_ns); int ima_init_crypto(void); void ima_putc(struct seq_file *m, void *data, int datalen); void ima_print_digest(struct seq_file *m, u8 *digest, u32 size); @@ -409,6 +412,11 @@ 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;
+static inline unsigned int get_ns_id(const struct ima_namespace *ima_ns) +{ + return ima_ns->ns.inum; +} + #ifdef CONFIG_IMA_NS int __init ima_init_namespace(void);
diff --git a/security/integrity/ima/ima_api.c b/security/integrity/ima/ima_api.c index 743b9337d9e5..2678faaa7a15 100644 --- a/security/integrity/ima/ima_api.c +++ b/security/integrity/ima/ima_api.c @@ -76,6 +76,8 @@ int ima_alloc_init_template(struct ima_event_data *event_data, (*entry)->template_data_len += sizeof(len); (*entry)->template_data_len += len; } + + (*entry)->ns_id = event_data->ns_id; return 0; out: ima_free_template_entry(*entry); @@ -152,7 +154,8 @@ int ima_store_template(struct ima_template_entry *entry, */ void ima_add_violation(struct file *file, const unsigned char *filename, struct integrity_iint_cache *iint, - const char *op, const char *cause) + const char *op, const char *cause, + struct ima_namespace *ima_ns) { struct ima_template_entry *entry; struct inode *inode = file_inode(file); @@ -163,6 +166,8 @@ void ima_add_violation(struct file *file, const unsigned char *filename, int violation = 1; int result;
+ event_data.ns_id = get_ns_id(ima_ns); + /* can overflow, only indicator */ atomic_long_inc(&ima_htable.violations);
@@ -336,6 +341,7 @@ void ima_store_measurement(struct integrity_iint_cache *iint, .modsig = modsig }; int violation = 0;
+ event_data.ns_id = get_ns_id(ima_ns); /* * We still need to store the measurement in the case of MODSIG because * we only have its contents to put in the list at the time of diff --git a/security/integrity/ima/ima_init.c b/security/integrity/ima/ima_init.c index dea0251142fd..52b675d47177 100644 --- a/security/integrity/ima/ima_init.c +++ b/security/integrity/ima/ima_init.c @@ -68,6 +68,8 @@ static int __init ima_add_boot_aggregate(void) char digest[TPM_MAX_DIGEST_SIZE]; } hash;
+ event_data.ns_id = get_ns_id(&init_ima_ns); + memset(iint, 0, sizeof(*iint)); memset(&hash, 0, sizeof(hash)); iint->ima_hash = &hash.hdr; diff --git a/security/integrity/ima/ima_main.c b/security/integrity/ima/ima_main.c index e0460462193d..18291787dbb5 100644 --- a/security/integrity/ima/ima_main.c +++ b/security/integrity/ima/ima_main.c @@ -149,10 +149,10 @@ static void ima_rdwr_violation_check(struct file *file,
if (send_tomtou) ima_add_violation(file, *pathname, iint, - "invalid_pcr", "ToMToU"); + "invalid_pcr", "ToMToU", ima_ns); if (send_writers) ima_add_violation(file, *pathname, iint, - "invalid_pcr", "open_writers"); + "invalid_pcr", "open_writers", ima_ns); }
static enum hash_algo ima_get_hash_algo(struct evm_ima_xattr_data *xattr_value, @@ -1068,6 +1068,7 @@ void process_buffer_measurement(struct inode *inode, const void *buf, int size, goto out; }
+ event_data.ns_id = get_ns_id(ima_ns); ret = ima_alloc_init_template(&event_data, &entry, template); if (ret < 0) { audit_cause = "alloc_entry"; diff --git a/security/integrity/ima/ima_template.c b/security/integrity/ima/ima_template.c index 0d4c698d2707..db4d3893d32d 100644 --- a/security/integrity/ima/ima_template.c +++ b/security/integrity/ima/ima_template.c @@ -373,6 +373,7 @@ int ima_restore_measurement_list(loff_t size, void *buf) struct ima_template_desc *template_desc; DECLARE_BITMAP(hdr_mask, HDR__LAST); unsigned long count = 0; + unsigned int init_ns_id = get_ns_id(&init_ima_ns); int ret = 0;
if (!buf || size < sizeof(*khdr)) @@ -472,6 +473,7 @@ int ima_restore_measurement_list(loff_t size, void *buf)
entry->pcr = !ima_canonical_fmt ? *(u32 *)(hdr[HDR_PCR].data) : le32_to_cpu(*(u32 *)(hdr[HDR_PCR].data)); + entry->ns_id = init_ns_id; ret = ima_restore_measurement_entry(entry); if (ret < 0) break;
From: Krzysztof Struczynski krzysztof.struczynski@huawei.com
hulk inclusion category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/I49KW1 CVE: NA
--------------------------------
Maintain per ima namespace measurement list. It will be used to provide information about the namespace measurements in securityfs and to clean up hash table entries when the namespace is destroyed.
The global measurement list remains and is not modified. It is necessary to keep it so that the PCR value can be recreated.
Signed-off-by: Krzysztof Struczynski krzysztof.struczynski@huawei.com Reviewed-by: Zhang Tianxing zhangtianxing3@huawei.com Signed-off-by: Zheng Zengkai zhengzengkai@huawei.com --- include/linux/ima.h | 1 + security/integrity/ima/ima.h | 7 +++++-- security/integrity/ima/ima_api.c | 12 +++++++----- security/integrity/ima/ima_init.c | 4 +++- security/integrity/ima/ima_main.c | 3 ++- security/integrity/ima/ima_ns.c | 1 + security/integrity/ima/ima_queue.c | 11 +++++++---- 7 files changed, 26 insertions(+), 13 deletions(-)
diff --git a/include/linux/ima.h b/include/linux/ima.h index d7b2864d7d7c..0ab1dbf34c47 100644 --- a/include/linux/ima.h +++ b/include/linux/ima.h @@ -232,6 +232,7 @@ struct ima_namespace { bool frozen; struct ima_policy_data *policy_data; struct integrity_iint_tree *iint_tree; + struct list_head ns_measurements; } __randomize_layout;
extern struct ima_namespace init_ima_ns; diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h index 324ccfbbc1dc..6e7a5cf253cc 100644 --- a/security/integrity/ima/ima.h +++ b/security/integrity/ima/ima.h @@ -119,6 +119,7 @@ struct ima_template_entry { struct ima_queue_entry { struct hlist_node hnext; /* place in hash collision list */ struct list_head later; /* place in ima_measurements list */ + struct list_head ns_later; /* place in ima namespace list */ struct ima_template_entry *entry; }; extern struct list_head ima_measurements; /* list of all measurements */ @@ -151,7 +152,8 @@ int ima_init(void); int ima_fs_init(void); int ima_add_template_entry(struct ima_template_entry *entry, int violation, const char *op, struct inode *inode, - const unsigned char *filename); + const unsigned char *filename, + struct ima_namespace *ima_ns); int ima_calc_file_hash(struct file *file, struct ima_digest_data *hash); int ima_calc_buffer_hash(const void *buf, loff_t len, struct ima_digest_data *hash); @@ -293,7 +295,8 @@ int ima_alloc_init_template(struct ima_event_data *event_data, struct ima_template_desc *template_desc); int ima_store_template(struct ima_template_entry *entry, int violation, struct inode *inode, const unsigned char *filename, - int pcr, struct ima_digest *digest); + int pcr, struct ima_digest *digest, + struct ima_namespace *ima_ns); void ima_free_template_entry(struct ima_template_entry *entry); const char *ima_d_path(const struct path *path, char **pathbuf, char *filename);
diff --git a/security/integrity/ima/ima_api.c b/security/integrity/ima/ima_api.c index 2678faaa7a15..6724ee072cd9 100644 --- a/security/integrity/ima/ima_api.c +++ b/security/integrity/ima/ima_api.c @@ -104,7 +104,8 @@ int ima_alloc_init_template(struct ima_event_data *event_data, int ima_store_template(struct ima_template_entry *entry, int violation, struct inode *inode, const unsigned char *filename, int pcr, - struct ima_digest *digest) + struct ima_digest *digest, + struct ima_namespace *ima_ns) { static const char op[] = "add_template_measure"; static const char audit_cause[] = "hashing_error"; @@ -134,10 +135,11 @@ int ima_store_template(struct ima_template_entry *entry, }
entry->pcr = pcr; - result = ima_add_template_entry(entry, violation, op, inode, filename); + result = ima_add_template_entry(entry, violation, op, inode, filename, + ima_ns); if (!result && duplicated_entry) { result = ima_add_template_entry(duplicated_entry, violation, op, - inode, filename); + inode, filename, ima_ns); if (result < 0) kfree(duplicated_entry); } @@ -177,7 +179,7 @@ void ima_add_violation(struct file *file, const unsigned char *filename, goto err_out; } result = ima_store_template(entry, violation, inode, filename, - CONFIG_IMA_MEASURE_PCR_IDX, NULL); + CONFIG_IMA_MEASURE_PCR_IDX, NULL, ima_ns); if (result < 0) ima_free_template_entry(entry); err_out: @@ -364,7 +366,7 @@ void ima_store_measurement(struct integrity_iint_cache *iint, }
result = ima_store_template(entry, violation, inode, filename, pcr, - digest); + digest, ima_ns); out: if ((!result || result == -EEXIST) && !(file->f_flags & O_DIRECT)) { iint->flags |= IMA_MEASURED; diff --git a/security/integrity/ima/ima_init.c b/security/integrity/ima/ima_init.c index 52b675d47177..00c8dfea6ba8 100644 --- a/security/integrity/ima/ima_init.c +++ b/security/integrity/ima/ima_init.c @@ -35,6 +35,7 @@ struct ima_namespace init_ima_ns = { .frozen = true, .policy_data = &init_policy_data, .iint_tree = &init_iint_tree, + .ns_measurements = LIST_HEAD_INIT(init_ima_ns.ns_measurements), }; EXPORT_SYMBOL(init_ima_ns);
@@ -104,7 +105,8 @@ static int __init ima_add_boot_aggregate(void)
result = ima_store_template(entry, violation, NULL, boot_aggregate_name, - CONFIG_IMA_MEASURE_PCR_IDX, NULL); + CONFIG_IMA_MEASURE_PCR_IDX, NULL, + &init_ima_ns); if (result < 0) { ima_free_template_entry(entry); audit_cause = "store_entry"; diff --git a/security/integrity/ima/ima_main.c b/security/integrity/ima/ima_main.c index 18291787dbb5..673091d612de 100644 --- a/security/integrity/ima/ima_main.c +++ b/security/integrity/ima/ima_main.c @@ -1075,7 +1075,8 @@ void process_buffer_measurement(struct inode *inode, const void *buf, int size, goto out; }
- ret = ima_store_template(entry, violation, NULL, buf, pcr, NULL); + ret = ima_store_template(entry, violation, NULL, buf, pcr, NULL, + ima_ns); if (ret < 0) { audit_cause = "store_entry"; ima_free_template_entry(entry); diff --git a/security/integrity/ima/ima_ns.c b/security/integrity/ima/ima_ns.c index 04aa50473971..a6197c708f3a 100644 --- a/security/integrity/ima/ima_ns.c +++ b/security/integrity/ima/ima_ns.c @@ -130,6 +130,7 @@ static struct ima_namespace *clone_ima_ns(struct user_namespace *user_ns, rwlock_init(&ns->iint_tree->lock); ns->iint_tree->root = RB_ROOT;
+ INIT_LIST_HEAD(&ns->ns_measurements); 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); diff --git a/security/integrity/ima/ima_queue.c b/security/integrity/ima/ima_queue.c index c096ef8945c7..673fd95c8d33 100644 --- a/security/integrity/ima/ima_queue.c +++ b/security/integrity/ima/ima_queue.c @@ -91,7 +91,8 @@ static int get_binary_runtime_size(struct ima_template_entry *entry) * (Called with ima_extend_list_mutex held.) */ static int ima_add_digest_entry(struct ima_template_entry *entry, - bool update_htable) + bool update_htable, + struct ima_namespace *ima_ns) { struct ima_queue_entry *qe; unsigned int key; @@ -105,6 +106,7 @@ static int ima_add_digest_entry(struct ima_template_entry *entry,
INIT_LIST_HEAD(&qe->later); list_add_tail_rcu(&qe->later, &ima_measurements); + list_add_tail_rcu(&qe->ns_later, &ima_ns->ns_measurements);
atomic_long_inc(&ima_htable.len); if (update_htable) { @@ -158,7 +160,8 @@ static int ima_pcr_extend(struct tpm_digest *digests_arg, int pcr) */ int ima_add_template_entry(struct ima_template_entry *entry, int violation, const char *op, struct inode *inode, - const unsigned char *filename) + const unsigned char *filename, + struct ima_namespace *ima_ns) { u8 *digest = entry->digests[ima_hash_algo_idx].digest; struct tpm_digest *digests_arg = entry->digests; @@ -176,7 +179,7 @@ int ima_add_template_entry(struct ima_template_entry *entry, int violation, } }
- result = ima_add_digest_entry(entry, 1); + result = ima_add_digest_entry(entry, 1, ima_ns); if (result < 0) { audit_cause = "ENOMEM"; audit_info = 0; @@ -205,7 +208,7 @@ int ima_restore_measurement_entry(struct ima_template_entry *entry) int result = 0;
mutex_lock(&ima_extend_list_mutex); - result = ima_add_digest_entry(entry, 0); + result = ima_add_digest_entry(entry, 0, &init_ima_ns); mutex_unlock(&ima_extend_list_mutex); return result; }
From: Krzysztof Struczynski krzysztof.struczynski@huawei.com
hulk inclusion category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/I49KW1 CVE: NA
--------------------------------
Compare the namespace id during the digest entry lookup. Remove digests from hash table when the namespace is destroyed, but keep the global measurement list entry.
Signed-off-by: Krzysztof Struczynski krzysztof.struczynski@huawei.com Reviewed-by: Zhang Tianxing zhangtianxing3@huawei.com Signed-off-by: Zheng Zengkai zhengzengkai@huawei.com --- security/integrity/ima/ima.h | 1 + security/integrity/ima/ima_ns.c | 19 +++++++++++++++++++ security/integrity/ima/ima_queue.c | 12 +++++++++--- 3 files changed, 29 insertions(+), 3 deletions(-)
diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h index 6e7a5cf253cc..f757aedea66f 100644 --- a/security/integrity/ima/ima.h +++ b/security/integrity/ima/ima.h @@ -187,6 +187,7 @@ int ima_lsm_policy_change(struct notifier_block *nb, unsigned long event, * used to protect h_table and sha_table */ extern spinlock_t ima_queue_lock; +extern spinlock_t ima_htable_lock;
struct ima_h_table { atomic_long_t len; /* number of stored measurements in the list */ diff --git a/security/integrity/ima/ima_ns.c b/security/integrity/ima/ima_ns.c index a6197c708f3a..885f12043cb3 100644 --- a/security/integrity/ima/ima_ns.c +++ b/security/integrity/ima/ima_ns.c @@ -26,6 +26,7 @@ #include <linux/rwsem.h> #include <linux/workqueue.h> #include <linux/mutex.h> +#include <linux/spinlock.h>
#include "ima.h"
@@ -180,10 +181,28 @@ int __init ima_init_namespace(void) return 0; }
+static void imans_remove_hash_entries(struct ima_namespace *ima_ns) +{ + struct list_head *ele; + struct ima_queue_entry *qe; + + /* The namespace is inactive, no lock is needed */ + list_for_each(ele, &ima_ns->ns_measurements) { + qe = list_entry(ele, struct ima_queue_entry, ns_later); + /* Don't free the queue entry, it should stay on the global + * measurement list, remove only the hash table entry */ + spin_lock(&ima_htable_lock); + hlist_del_rcu(&qe->hnext); + spin_unlock(&ima_htable_lock); + atomic_long_dec(&ima_htable.len); + } +} + static void destroy_ima_ns(struct ima_namespace *ns) { bool is_init_ns = (ns == &init_ima_ns);
+ imans_remove_hash_entries(ns); dec_ima_namespaces(ns->ucounts); put_user_ns(ns->user_ns); ns_free_inum(&ns->ns); diff --git a/security/integrity/ima/ima_queue.c b/security/integrity/ima/ima_queue.c index 673fd95c8d33..3aeff7b5e036 100644 --- a/security/integrity/ima/ima_queue.c +++ b/security/integrity/ima/ima_queue.c @@ -17,6 +17,7 @@
#include <linux/rculist.h> #include <linux/slab.h> +#include <linux/spinlock.h> #include "ima.h"
#define AUDIT_CAUSE_LEN_MAX 32 @@ -31,6 +32,8 @@ static unsigned long binary_runtime_size; static unsigned long binary_runtime_size = ULONG_MAX; #endif
+DEFINE_SPINLOCK(ima_htable_lock); + /* key: inode (before secure-hashing a file) */ struct ima_h_table ima_htable = { .len = ATOMIC_LONG_INIT(0), @@ -46,7 +49,7 @@ static DEFINE_MUTEX(ima_extend_list_mutex);
/* lookup up the digest value in the hash table, and return the entry */ static struct ima_queue_entry *ima_lookup_digest_entry(u8 *digest_value, - int pcr) + int pcr, int ns_id) { struct ima_queue_entry *qe, *ret = NULL; unsigned int key; @@ -57,7 +60,8 @@ static struct ima_queue_entry *ima_lookup_digest_entry(u8 *digest_value, hlist_for_each_entry_rcu(qe, &ima_htable.queue[key], hnext) { rc = memcmp(qe->entry->digests[ima_hash_algo_idx].digest, digest_value, hash_digest_size[ima_hash_algo]); - if ((rc == 0) && (qe->entry->pcr == pcr)) { + if ((rc == 0) && (qe->entry->pcr == pcr) && + (qe->entry->ns_id == ns_id)) { ret = qe; break; } @@ -111,7 +115,9 @@ static int ima_add_digest_entry(struct ima_template_entry *entry, atomic_long_inc(&ima_htable.len); if (update_htable) { key = ima_hash_key(entry->digests[ima_hash_algo_idx].digest); + spin_lock(&ima_htable_lock); hlist_add_head_rcu(&qe->hnext, &ima_htable.queue[key]); + spin_unlock(&ima_htable_lock); }
if (binary_runtime_size != ULONG_MAX) { @@ -172,7 +178,7 @@ int ima_add_template_entry(struct ima_template_entry *entry, int violation,
mutex_lock(&ima_extend_list_mutex); if (!violation) { - if (ima_lookup_digest_entry(digest, entry->pcr)) { + if (ima_lookup_digest_entry(digest, entry->pcr, entry->ns_id)) { audit_cause = "hash_exists"; result = -EEXIST; goto out;
From: Krzysztof Struczynski krzysztof.struczynski@huawei.com
hulk inclusion category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/I49KW1 CVE: NA
--------------------------------
Add a new ima-ns template: "d-ng|n-ng|ns"
Signed-off-by: Krzysztof Struczynski krzysztof.struczynski@huawei.com Reviewed-by: Zhang Tianxing zhangtianxing3@huawei.com Signed-off-by: Zheng Zengkai zhengzengkai@huawei.com --- security/integrity/ima/ima_template.c | 5 ++++- security/integrity/ima/ima_template_lib.c | 13 +++++++++++++ security/integrity/ima/ima_template_lib.h | 2 ++ 3 files changed, 19 insertions(+), 1 deletion(-)
diff --git a/security/integrity/ima/ima_template.c b/security/integrity/ima/ima_template.c index db4d3893d32d..3d6f6e9d530c 100644 --- a/security/integrity/ima/ima_template.c +++ b/security/integrity/ima/ima_template.c @@ -22,6 +22,7 @@ static struct ima_template_desc builtin_templates[] = { {.name = "ima-sig", .fmt = "d-ng|n-ng|sig"}, {.name = "ima-buf", .fmt = "d-ng|n-ng|buf"}, {.name = "ima-modsig", .fmt = "d-ng|n-ng|sig|d-modsig|modsig"}, + {.name = "ima-ns", .fmt = "d-ng|n-ng|ns"}, {.name = "", .fmt = ""}, /* placeholder for a custom format */ };
@@ -47,6 +48,8 @@ static const struct ima_template_field supported_fields[] = { .field_show = ima_show_template_sig}, {.field_id = "evmsig", .field_init = ima_eventevmsig_init, .field_show = ima_show_template_sig}, + {.field_id = "ns", .field_init = ima_eventns_init, + .field_show = ima_show_template_buf}, };
/* @@ -54,7 +57,7 @@ static const struct ima_template_field supported_fields[] = { * need to be accounted for since they shouldn't be defined in the same template * description as 'd-ng' and 'n-ng' respectively. */ -#define MAX_TEMPLATE_NAME_LEN sizeof("d-ng|n-ng|sig|buf|d-modisg|modsig") +#define MAX_TEMPLATE_NAME_LEN sizeof("d-ng|n-ng|sig|buf|d-modisg|modsig|ns")
static struct ima_template_desc *ima_template;
diff --git a/security/integrity/ima/ima_template_lib.c b/security/integrity/ima/ima_template_lib.c index 90040fac150b..ca6153680cf0 100644 --- a/security/integrity/ima/ima_template_lib.c +++ b/security/integrity/ima/ima_template_lib.c @@ -514,3 +514,16 @@ int ima_eventevmsig_init(struct ima_event_data *event_data, kfree(xattr_data); return rc; } + +/* + * ima_eventns_init - include the ima namespace id as part of the + * template data + */ +int ima_eventns_init(struct ima_event_data *event_data, + struct ima_field_data *field_data) +{ + return ima_write_template_field_data(&(event_data->ns_id), + sizeof(event_data->ns_id), + DATA_FMT_HEX, + field_data); +} diff --git a/security/integrity/ima/ima_template_lib.h b/security/integrity/ima/ima_template_lib.h index f4b2a2056d1d..b34054858f05 100644 --- a/security/integrity/ima/ima_template_lib.h +++ b/security/integrity/ima/ima_template_lib.h @@ -48,4 +48,6 @@ int ima_eventmodsig_init(struct ima_event_data *event_data, struct ima_field_data *field_data); int ima_eventevmsig_init(struct ima_event_data *event_data, struct ima_field_data *field_data); +int ima_eventns_init(struct ima_event_data *event_data, + struct ima_field_data *field_data); #endif /* __LINUX_IMA_TEMPLATE_LIB_H */
From: Krzysztof Struczynski krzysztof.struczynski@huawei.com
hulk inclusion category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/I49KW1 CVE: NA
--------------------------------
Modify ima securityfs interface, so that only measurement list entries that belong to the given ima namespace are visible/counted. The initial ima namespace is an exception, its processes have access to all measurement list entries.
Signed-off-by: Krzysztof Struczynski krzysztof.struczynski@huawei.com Reviewed-by: Zhang Tianxing zhangtianxing3@huawei.com Signed-off-by: Zheng Zengkai zhengzengkai@huawei.com --- include/linux/ima.h | 1 + security/integrity/ima/ima.h | 4 ++- security/integrity/ima/ima_fs.c | 52 ++++++++++++++++++++++-------- security/integrity/ima/ima_init.c | 1 + security/integrity/ima/ima_ns.c | 1 + security/integrity/ima/ima_queue.c | 3 ++ 6 files changed, 48 insertions(+), 14 deletions(-)
diff --git a/include/linux/ima.h b/include/linux/ima.h index 0ab1dbf34c47..12738e37f714 100644 --- a/include/linux/ima.h +++ b/include/linux/ima.h @@ -233,6 +233,7 @@ struct ima_namespace { struct ima_policy_data *policy_data; struct integrity_iint_tree *iint_tree; struct list_head ns_measurements; + atomic_long_t ml_len; /* number of stored measurements in the list */ } __randomize_layout;
extern struct ima_namespace init_ima_ns; diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h index f757aedea66f..0e864ef80874 100644 --- a/security/integrity/ima/ima.h +++ b/security/integrity/ima/ima.h @@ -190,12 +190,14 @@ extern spinlock_t ima_queue_lock; extern spinlock_t ima_htable_lock;
struct ima_h_table { - atomic_long_t len; /* number of stored measurements in the list */ + atomic_long_t len; /* number of stored measurements in the list */ atomic_long_t violations; struct hlist_head queue[IMA_MEASURE_HTABLE_SIZE]; }; extern struct ima_h_table ima_htable;
+extern atomic_long_t ima_ml_len; + static inline unsigned int ima_hash_key(u8 *digest) { /* there is no point in taking a hash of part of a digest */ diff --git a/security/integrity/ima/ima_fs.c b/security/integrity/ima/ima_fs.c index 2ae4adfbaa82..f8a19751c80d 100644 --- a/security/integrity/ima/ima_fs.c +++ b/security/integrity/ima/ima_fs.c @@ -58,11 +58,12 @@ static ssize_t ima_show_htable_value(struct file *filp, char __user *buf, atomic_long_t *val = NULL; char tmpbuf[32]; /* greater than largest 'long' string value */ ssize_t len; + struct ima_namespace *ima_ns = get_current_ns();
if (filp->f_path.dentry == violations) val = &ima_htable.violations; else if (filp->f_path.dentry == runtime_measurements_count) - val = &ima_htable.len; + val = (ima_ns == &init_ima_ns) ? &ima_ml_len : &ima_ns->ml_len; #ifdef CONFIG_IMA_DIGEST_LIST else if (filp->f_path.dentry == digests_count) val = &ima_digests_htable.len; @@ -82,32 +83,57 @@ static void *ima_measurements_start(struct seq_file *m, loff_t *pos) { loff_t l = *pos; struct ima_queue_entry *qe; + struct ima_namespace *ima_ns = get_current_ns();
- /* we need a lock since pos could point beyond last element */ - rcu_read_lock(); - list_for_each_entry_rcu(qe, &ima_measurements, later) { - if (!l--) { - rcu_read_unlock(); - return qe; + if (ima_ns == &init_ima_ns) { + /* we need a lock since pos could point beyond last element */ + rcu_read_lock(); + list_for_each_entry_rcu(qe, &ima_measurements, later) { + if (!l--) { + rcu_read_unlock(); + return qe; + } + } + rcu_read_unlock(); + } else { + rcu_read_lock(); + list_for_each_entry_rcu(qe, &ima_ns->ns_measurements, ns_later) { + if (!l--) { + rcu_read_unlock(); + return qe; + } } + rcu_read_unlock(); } - rcu_read_unlock(); + return NULL; }
static void *ima_measurements_next(struct seq_file *m, void *v, loff_t *pos) { struct ima_queue_entry *qe = v; + struct ima_namespace *ima_ns = get_current_ns();
/* lock protects when reading beyond last element * against concurrent list-extension */ - rcu_read_lock(); - qe = list_entry_rcu(qe->later.next, struct ima_queue_entry, later); - rcu_read_unlock(); - (*pos)++; + if (ima_ns == &init_ima_ns) { + rcu_read_lock(); + qe = list_entry_rcu(qe->later.next, struct ima_queue_entry, + later); + rcu_read_unlock(); + (*pos)++; + + return (&qe->later == &ima_measurements) ? NULL : qe; + } else { + rcu_read_lock(); + qe = list_entry_rcu(qe->ns_later.next, struct ima_queue_entry, + ns_later); + rcu_read_unlock(); + (*pos)++;
- return (&qe->later == &ima_measurements) ? NULL : qe; + return (&qe->ns_later == &ima_ns->ns_measurements) ? NULL : qe; + } }
static void ima_measurements_stop(struct seq_file *m, void *v) diff --git a/security/integrity/ima/ima_init.c b/security/integrity/ima/ima_init.c index 00c8dfea6ba8..99b9643e6763 100644 --- a/security/integrity/ima/ima_init.c +++ b/security/integrity/ima/ima_init.c @@ -36,6 +36,7 @@ struct ima_namespace init_ima_ns = { .policy_data = &init_policy_data, .iint_tree = &init_iint_tree, .ns_measurements = LIST_HEAD_INIT(init_ima_ns.ns_measurements), + .ml_len = ATOMIC_LONG_INIT(0), }; EXPORT_SYMBOL(init_ima_ns);
diff --git a/security/integrity/ima/ima_ns.c b/security/integrity/ima/ima_ns.c index 885f12043cb3..2a4b7a23f9a7 100644 --- a/security/integrity/ima/ima_ns.c +++ b/security/integrity/ima/ima_ns.c @@ -127,6 +127,7 @@ static struct ima_namespace *clone_ima_ns(struct user_namespace *user_ns, ns->user_ns = get_user_ns(user_ns); ns->ucounts = ucounts; ns->frozen = false; + atomic_long_set(&ns->ml_len, 0);
rwlock_init(&ns->iint_tree->lock); ns->iint_tree->root = RB_ROOT; diff --git a/security/integrity/ima/ima_queue.c b/security/integrity/ima/ima_queue.c index 3aeff7b5e036..89b9c3734890 100644 --- a/security/integrity/ima/ima_queue.c +++ b/security/integrity/ima/ima_queue.c @@ -25,6 +25,7 @@ /* pre-allocated array of tpm_digest structures to extend a PCR */ static struct tpm_digest *digests;
+atomic_long_t ima_ml_len = ATOMIC_LONG_INIT(0); /* number of stored measurements in the list */ LIST_HEAD(ima_measurements); /* list of all measurements */ #ifdef CONFIG_IMA_KEXEC static unsigned long binary_runtime_size; @@ -113,6 +114,8 @@ static int ima_add_digest_entry(struct ima_template_entry *entry, list_add_tail_rcu(&qe->ns_later, &ima_ns->ns_measurements);
atomic_long_inc(&ima_htable.len); + atomic_long_inc(&ima_ml_len); + atomic_long_inc(&ima_ns->ml_len); if (update_htable) { key = ima_hash_key(entry->digests[ima_hash_algo_idx].digest); spin_lock(&ima_htable_lock);
From: Krzysztof Struczynski krzysztof.struczynski@huawei.com
hulk inclusion category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/I49KW1 CVE: NA
--------------------------------
To detect ToMToU violations reader counter of the given inode is checked. This is not enough, because the reader may exist in a different ima namespace. Per inode reader counter tracks readers in all ima namespaces, whereas the per namespace counter is necessary to avoid false positives.
Add a new reader counter to the integrity inode cache entry.
Signed-off-by: Krzysztof Struczynski krzysztof.struczynski@huawei.com Reviewed-by: Zhang Tianxing zhangtianxing3@huawei.com Signed-off-by: Zheng Zengkai zhengzengkai@huawei.com --- security/integrity/ima/ima_main.c | 75 ++++++++++++++++++++++--------- security/integrity/integrity.h | 18 ++++++++ 2 files changed, 72 insertions(+), 21 deletions(-)
diff --git a/security/integrity/ima/ima_main.c b/security/integrity/ima/ima_main.c index 673091d612de..663552073997 100644 --- a/security/integrity/ima/ima_main.c +++ b/security/integrity/ima/ima_main.c @@ -30,6 +30,11 @@ #include "ima.h" #include "ima_digest_list.h"
+struct ima_file_data { + struct ima_namespace *ima_ns; + bool is_readcount; +}; + int ima_hash_algo = HASH_ALGO_SHA1;
/* Actions (measure/appraisal) for which digest lists can be used */ @@ -131,8 +136,8 @@ static void ima_rdwr_violation_check(struct file *file, 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)) + if (iint && atomic_read(&iint->readcount) && + test_bit(IMA_MUST_MEASURE, &iint->atomic_flags)) send_tomtou = true; } } else { @@ -240,7 +245,7 @@ static void ima_check_last_writer(struct integrity_iint_cache *iint, { fmode_t mode = file->f_mode; bool update; - struct ima_namespace *ima_ns = (struct ima_namespace *)file->f_ima; + struct ima_file_data *f_data = (struct ima_file_data *)file->f_ima;
if (!(mode & FMODE_WRITE)) return; @@ -255,7 +260,7 @@ static void ima_check_last_writer(struct integrity_iint_cache *iint, iint->flags &= ~(IMA_DONE_MASK | IMA_NEW_FILE); iint->measured_pcrs = 0;
- ima_check_active_ns(ima_ns, inode); + ima_check_active_ns(f_data->ima_ns, inode);
if (update) ima_update_xattr(iint, file); @@ -276,6 +281,8 @@ static void ima_check_last_writer(struct integrity_iint_cache *iint, */ int ima_file_alloc(struct file *file) { + struct ima_file_data *f_data; + /* It is possible that ima_file_alloc() is called after * exit_task_namespaces(), when IMA does the last writer check from * __fput(). In that case it's not necessary to store the namespace @@ -283,8 +290,16 @@ int ima_file_alloc(struct file *file) if (!current->nsproxy) return 0;
- file->f_ima = get_current_ns(); - get_ima_ns((struct ima_namespace *)file->f_ima); + f_data = kmalloc(sizeof(struct ima_file_data), GFP_KERNEL); + if (!f_data) + return -ENOMEM; + + f_data->ima_ns = get_current_ns(); + f_data->is_readcount = false; + get_ima_ns(f_data->ima_ns); + + file->f_ima = f_data; + return 0; }
@@ -298,30 +313,36 @@ 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; + struct ima_file_data *f_data = (struct ima_file_data *)file->f_ima;
- if (!ima_ns) + if (!f_data) return;
if (unlikely(!(file->f_mode & FMODE_OPENED))) goto out;
- if (!ima_ns->policy_data->ima_policy_flag || !S_ISREG(inode->i_mode)) + if (!f_data->ima_ns->policy_data->ima_policy_flag || + !S_ISREG(inode->i_mode)) goto out;
- iint = integrity_iint_rb_find(ima_ns->iint_tree, inode); + iint = integrity_iint_rb_find(f_data->ima_ns->iint_tree, inode); if (!iint) goto out;
ima_check_last_writer(iint, inode, file); + + if (f_data->is_readcount) + iint_readcount_dec(iint); out: - put_ima_ns(ima_ns); + put_ima_ns(f_data->ima_ns); + kfree(f_data); }
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 ima_namespace *ima_ns, + bool readcount_open) { struct inode *inode = file_inode(file); struct integrity_iint_cache *iint = NULL; @@ -342,6 +363,12 @@ static int process_ns_measurement(struct file *file, const struct cred *cred, if (!ima_ns->policy_data->ima_policy_flag) return 0;
+ if (ima_ns != current_ima_ns) { + iint = integrity_iint_rb_find(ima_ns->iint_tree, inode); + if (!iint) + return 0; + } + /* Return an IMA_MEASURE, IMA_APPRAISE, IMA_AUDIT action * bitmask based on the appraise/audit/measurement policy. * Included is the appraise submask. @@ -362,12 +389,18 @@ static int process_ns_measurement(struct file *file, const struct cred *cred,
inode_lock(inode);
- if (action) { + if (action && !iint) { iint = integrity_inode_rb_get(ima_ns->iint_tree, inode); if (!iint) rc = -ENOMEM; }
+ if ((ima_ns == current_ima_ns) && iint && readcount_open && + ((file->f_mode & (FMODE_READ | FMODE_WRITE)) == FMODE_READ)) { + iint_readcount_inc(iint); + ((struct ima_file_data *)file->f_ima)->is_readcount = true; + } + if (!rc && violation_check) ima_rdwr_violation_check(file, iint, action & IMA_MEASURE, &pathbuf, &pathname, filename, ima_ns); @@ -518,7 +551,7 @@ static int process_ns_measurement(struct file *file, const struct cred *cred,
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, bool readcount_open) { int ret; struct ima_namespace *ima_ns; @@ -533,7 +566,7 @@ static int process_measurement(struct file *file, const struct cred *cred, continue;
ret = process_ns_measurement(file, cred, secid, buf, size, mask, - func, ima_ns); + func, ima_ns, readcount_open); if (ret != 0) break; } @@ -560,7 +593,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, true); }
return 0; @@ -640,13 +673,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, false); 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, false); }
/** @@ -667,7 +700,7 @@ int ima_file_check(struct file *file, int mask) security_task_getsecid(current, &secid); rc = process_measurement(file, current_cred(), secid, NULL, 0, mask & (MAY_READ | MAY_WRITE | MAY_EXEC | - MAY_APPEND), FILE_CHECK); + MAY_APPEND), FILE_CHECK, true); if (ima_current_is_parser() && !rc) ima_check_measured_appraised(file); return rc; @@ -843,7 +876,7 @@ int ima_read_file(struct file *file, enum kernel_read_file_id read_id, func = read_idmap[read_id] ?: FILE_CHECK; security_task_getsecid(current, &secid); return process_measurement(file, current_cred(), secid, NULL, - 0, MAY_READ, func); + 0, MAY_READ, func, false); }
const int read_idmap[READING_MAX_ID] = { @@ -888,7 +921,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, false); }
/** diff --git a/security/integrity/integrity.h b/security/integrity/integrity.h index 05f6eabef9fe..78078d55fd33 100644 --- a/security/integrity/integrity.h +++ b/security/integrity/integrity.h @@ -18,6 +18,7 @@ #include <linux/key.h> #include <linux/audit.h> #include <linux/hash_info.h> +#include <linux/atomic.h>
/* iint action cache flags */ #define IMA_MEASURE 0x00000001 @@ -142,6 +143,9 @@ struct integrity_iint_cache { enum integrity_status ima_creds_status:4; enum integrity_status evm_status:4; struct ima_digest_data *ima_hash; + atomic_t readcount; /* Reader counter, incremented only in FILE_CHECK or + * MMAP_CHECK hooks, used for ima violation check. + */ };
enum compact_types { COMPACT_KEY, COMPACT_PARSER, COMPACT_FILE, @@ -338,3 +342,17 @@ static inline void __init add_to_platform_keyring(const char *source, { } #endif + +#ifdef CONFIG_IMA +static inline void iint_readcount_inc(struct integrity_iint_cache *iint) +{ + atomic_inc(&iint->readcount); +} +static inline void iint_readcount_dec(struct integrity_iint_cache *iint) +{ + if (WARN_ON(!atomic_read(&iint->readcount))) + return; + + atomic_dec(&iint->readcount); +} +#endif
From: Krzysztof Struczynski krzysztof.struczynski@huawei.com
hulk inclusion category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/I49KW1 CVE: NA
--------------------------------
Add "others" permissions to the namespaced ima securityfs entries. It is necessary so that the root in the user namespace that is the parent of the given ima namespace has access to the ima related data.
Loosened DAC restrictrions are compensated by an extra check for SYS_ADMIN capabilities in the ima code. The access is given only to the namespaced data, e.g. root user in the new ima namespace will see measurement list entries collected for that namespace and not for the other existing namespaces. The only exception is made for the admin in the initial user namespace, who has access to all the data.
Signed-off-by: Krzysztof Struczynski krzysztof.struczynski@huawei.com Reviewed-by: Zhang Tianxing zhangtianxing3@huawei.com Signed-off-by: Zheng Zengkai zhengzengkai@huawei.com --- security/integrity/ima/ima.h | 4 ++-- security/integrity/ima/ima_fs.c | 32 +++++++++++++++++++++++++++----- 2 files changed, 29 insertions(+), 7 deletions(-)
diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h index 0e864ef80874..8efc41c84249 100644 --- a/security/integrity/ima/ima.h +++ b/security/integrity/ima/ima.h @@ -512,9 +512,9 @@ static inline int ima_filter_rule_match(u32 secid, u32 field, u32 op, #endif /* CONFIG_IMA_LSM_RULES */
#ifdef CONFIG_IMA_READ_POLICY -#define POLICY_FILE_FLAGS (S_IWUSR | S_IRUSR) +#define POLICY_FILE_FLAGS (S_IWUSR | S_IRUSR | S_IROTH | S_IWOTH) #else -#define POLICY_FILE_FLAGS S_IWUSR +#define POLICY_FILE_FLAGS (S_IWUSR | S_IWOTH) #endif /* CONFIG_IMA_READ_POLICY */
#endif /* __LINUX_IMA_H */ diff --git a/security/integrity/ima/ima_fs.c b/security/integrity/ima/ima_fs.c index f8a19751c80d..26a2170699b0 100644 --- a/security/integrity/ima/ima_fs.c +++ b/security/integrity/ima/ima_fs.c @@ -73,7 +73,18 @@ static ssize_t ima_show_htable_value(struct file *filp, char __user *buf, return simple_read_from_buffer(buf, count, ppos, tmpbuf, len); }
+static int ima_open_htable_value(struct inode *inode, struct file *file) +{ + struct ima_namespace *ima_ns = get_current_ns(); + + if (!ns_capable(ima_ns->user_ns, CAP_SYS_ADMIN)) + return -EPERM; + + return 0; +} + static const struct file_operations ima_htable_value_ops = { + .open = ima_open_htable_value, .read = ima_show_htable_value, .llseek = generic_file_llseek, }; @@ -225,6 +236,11 @@ static const struct seq_operations ima_measurments_seqops = {
static int ima_measurements_open(struct inode *inode, struct file *file) { + struct ima_namespace *ima_ns = get_current_ns(); + + if (!ns_capable(ima_ns->user_ns, CAP_SYS_ADMIN)) + return -EPERM; + return seq_open(file, &ima_measurments_seqops); }
@@ -291,6 +307,11 @@ static const struct seq_operations ima_ascii_measurements_seqops = {
static int ima_ascii_measurements_open(struct inode *inode, struct file *file) { + struct ima_namespace *ima_ns = get_current_ns(); + + if (!ns_capable(ima_ns->user_ns, CAP_SYS_ADMIN)) + return -EPERM; + return seq_open(file, &ima_ascii_measurements_seqops); }
@@ -503,6 +524,7 @@ static int ima_open_data_upload(struct inode *inode, struct file *filp) const struct seq_operations *seq_ops = NULL; enum ima_fs_flags flag = ima_get_dentry_flag(dentry); bool read_allowed = false; + struct ima_namespace *ima_ns = get_current_ns();
if (dentry == ima_policy) { #ifdef CONFIG_IMA_READ_POLICY @@ -516,10 +538,10 @@ static int ima_open_data_upload(struct inode *inode, struct file *filp) return -EACCES; if ((filp->f_flags & O_ACCMODE) != O_RDONLY) return -EACCES; - if (!capable(CAP_SYS_ADMIN)) - return -EPERM; return seq_open(filp, seq_ops); } + if (!ns_capable(ima_ns->user_ns, CAP_SYS_ADMIN)) + return -EPERM; if (test_and_set_bit(flag, &ima_fs_flags)) return -EBUSY;
@@ -604,21 +626,21 @@ int __init ima_fs_init(void)
binary_runtime_measurements = securityfs_create_file("binary_runtime_measurements", - S_IRUSR | S_IRGRP, ima_dir, NULL, + S_IRUSR | S_IRGRP | S_IROTH, ima_dir, NULL, &ima_measurements_ops); if (IS_ERR(binary_runtime_measurements)) goto out;
ascii_runtime_measurements = securityfs_create_file("ascii_runtime_measurements", - S_IRUSR | S_IRGRP, ima_dir, NULL, + S_IRUSR | S_IRGRP | S_IROTH, ima_dir, NULL, &ima_ascii_measurements_ops); if (IS_ERR(ascii_runtime_measurements)) goto out;
runtime_measurements_count = securityfs_create_file("runtime_measurements_count", - S_IRUSR | S_IRGRP, ima_dir, NULL, + S_IRUSR | S_IRGRP | S_IROTH, ima_dir, NULL, &ima_htable_value_ops); if (IS_ERR(runtime_measurements_count)) goto out;
From: Krzysztof Struczynski krzysztof.struczynski@huawei.com
hulk inclusion category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/I49KW1 CVE: NA
--------------------------------
The violations are now tracked per namespace.
Signed-off-by: Krzysztof Struczynski krzysztof.struczynski@huawei.com Reviewed-by: Zhang Tianxing zhangtianxing3@huawei.com Signed-off-by: Zheng Zengkai zhengzengkai@huawei.com --- include/linux/ima.h | 1 + security/integrity/ima/ima.h | 1 - security/integrity/ima/ima_api.c | 2 +- security/integrity/ima/ima_fs.c | 4 ++-- security/integrity/ima/ima_init.c | 1 + security/integrity/ima/ima_ns.c | 1 + security/integrity/ima/ima_queue.c | 1 - 7 files changed, 6 insertions(+), 5 deletions(-)
diff --git a/include/linux/ima.h b/include/linux/ima.h index 12738e37f714..cfdd1280daff 100644 --- a/include/linux/ima.h +++ b/include/linux/ima.h @@ -234,6 +234,7 @@ struct ima_namespace { struct integrity_iint_tree *iint_tree; struct list_head ns_measurements; atomic_long_t ml_len; /* number of stored measurements in the list */ + atomic_long_t violations; } __randomize_layout;
extern struct ima_namespace init_ima_ns; diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h index 8efc41c84249..96ac4e5fbe3d 100644 --- a/security/integrity/ima/ima.h +++ b/security/integrity/ima/ima.h @@ -191,7 +191,6 @@ extern spinlock_t ima_htable_lock;
struct ima_h_table { atomic_long_t len; /* number of stored measurements in the list */ - atomic_long_t violations; struct hlist_head queue[IMA_MEASURE_HTABLE_SIZE]; }; extern struct ima_h_table ima_htable; diff --git a/security/integrity/ima/ima_api.c b/security/integrity/ima/ima_api.c index 6724ee072cd9..5921922d6930 100644 --- a/security/integrity/ima/ima_api.c +++ b/security/integrity/ima/ima_api.c @@ -171,7 +171,7 @@ void ima_add_violation(struct file *file, const unsigned char *filename, event_data.ns_id = get_ns_id(ima_ns);
/* can overflow, only indicator */ - atomic_long_inc(&ima_htable.violations); + atomic_long_inc(&ima_ns->violations);
result = ima_alloc_init_template(&event_data, &entry, NULL); if (result < 0) { diff --git a/security/integrity/ima/ima_fs.c b/security/integrity/ima/ima_fs.c index 26a2170699b0..97bda1826a94 100644 --- a/security/integrity/ima/ima_fs.c +++ b/security/integrity/ima/ima_fs.c @@ -61,7 +61,7 @@ static ssize_t ima_show_htable_value(struct file *filp, char __user *buf, struct ima_namespace *ima_ns = get_current_ns();
if (filp->f_path.dentry == violations) - val = &ima_htable.violations; + val = &ima_ns->violations; else if (filp->f_path.dentry == runtime_measurements_count) val = (ima_ns == &init_ima_ns) ? &ima_ml_len : &ima_ns->ml_len; #ifdef CONFIG_IMA_DIGEST_LIST @@ -646,7 +646,7 @@ int __init ima_fs_init(void) goto out;
violations = - securityfs_create_file("violations", S_IRUSR | S_IRGRP, + securityfs_create_file("violations", S_IRUSR | S_IRGRP | S_IROTH, ima_dir, NULL, &ima_htable_value_ops); if (IS_ERR(violations)) goto out; diff --git a/security/integrity/ima/ima_init.c b/security/integrity/ima/ima_init.c index 99b9643e6763..923373a12f5c 100644 --- a/security/integrity/ima/ima_init.c +++ b/security/integrity/ima/ima_init.c @@ -37,6 +37,7 @@ struct ima_namespace init_ima_ns = { .iint_tree = &init_iint_tree, .ns_measurements = LIST_HEAD_INIT(init_ima_ns.ns_measurements), .ml_len = ATOMIC_LONG_INIT(0), + .violations = ATOMIC_LONG_INIT(0), }; EXPORT_SYMBOL(init_ima_ns);
diff --git a/security/integrity/ima/ima_ns.c b/security/integrity/ima/ima_ns.c index 2a4b7a23f9a7..26c9bcd5ff74 100644 --- a/security/integrity/ima/ima_ns.c +++ b/security/integrity/ima/ima_ns.c @@ -128,6 +128,7 @@ static struct ima_namespace *clone_ima_ns(struct user_namespace *user_ns, ns->ucounts = ucounts; ns->frozen = false; atomic_long_set(&ns->ml_len, 0); + atomic_long_set(&ns->violations, 0);
rwlock_init(&ns->iint_tree->lock); ns->iint_tree->root = RB_ROOT; diff --git a/security/integrity/ima/ima_queue.c b/security/integrity/ima/ima_queue.c index 89b9c3734890..e9a88910d726 100644 --- a/security/integrity/ima/ima_queue.c +++ b/security/integrity/ima/ima_queue.c @@ -38,7 +38,6 @@ DEFINE_SPINLOCK(ima_htable_lock); /* key: inode (before secure-hashing a file) */ struct ima_h_table ima_htable = { .len = ATOMIC_LONG_INIT(0), - .violations = ATOMIC_LONG_INIT(0), .queue[0 ... IMA_MEASURE_HTABLE_SIZE - 1] = HLIST_HEAD_INIT };
From: Krzysztof Struczynski krzysztof.struczynski@huawei.com
hulk inclusion category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/I49KW1 CVE: NA
--------------------------------
It's possible that the user first unshares the ima namespace and then creates a new user namespace using clone3(). In that case the owning user namespace is the newly created one, because it is associated with the first process in the new ima namespace.
Signed-off-by: Krzysztof Struczynski krzysztof.struczynski@huawei.com Reviewed-by: Zhang Tianxing zhangtianxing3@huawei.com Signed-off-by: Zheng Zengkai zhengzengkai@huawei.com --- include/linux/ima.h | 6 +++-- kernel/nsproxy.c | 2 +- security/integrity/ima/ima_ns.c | 42 +++++++++++++++++++++++++++------ 3 files changed, 40 insertions(+), 10 deletions(-)
diff --git a/include/linux/ima.h b/include/linux/ima.h index cfdd1280daff..91c637c943ed 100644 --- a/include/linux/ima.h +++ b/include/linux/ima.h @@ -246,7 +246,8 @@ struct ima_namespace *copy_ima_ns(unsigned long flags,
void free_ima_ns(struct kref *kref);
-int imans_on_fork(struct nsproxy *nsproxy, struct task_struct *tsk); +int imans_on_fork(struct nsproxy *nsproxy, struct task_struct *tsk, + struct user_namespace *user_ns);
static inline struct ima_namespace *get_ima_ns(struct ima_namespace *ns) { @@ -269,7 +270,8 @@ static inline struct ima_namespace *copy_ima_ns(unsigned long flags, }
static inline int imans_on_fork(struct nsproxy *nsproxy, - struct task_struct *tsk) + struct task_struct *tsk, + struct user_namespace *user_ns) { return 0; } diff --git a/kernel/nsproxy.c b/kernel/nsproxy.c index e2cddc22dc53..ff138b24b25d 100644 --- a/kernel/nsproxy.c +++ b/kernel/nsproxy.c @@ -204,7 +204,7 @@ int copy_namespaces(unsigned long flags, struct task_struct *tsk) return ret; }
- ret = imans_on_fork(new_ns, tsk); + ret = imans_on_fork(new_ns, tsk, user_ns); if (ret) { free_nsproxy(new_ns); return ret; diff --git a/security/integrity/ima/ima_ns.c b/security/integrity/ima/ima_ns.c index 26c9bcd5ff74..ac000920d486 100644 --- a/security/integrity/ima/ima_ns.c +++ b/security/integrity/ima/ima_ns.c @@ -93,6 +93,24 @@ static void ima_set_ns_policy(struct ima_namespace *ima_ns, ima_init_ns_policy(ima_ns, &setup_data); }
+static int ima_swap_user_ns(struct ima_namespace *ima_ns, + struct user_namespace *user_ns) +{ + struct ucounts *ucounts; + + dec_ima_namespaces(ima_ns->ucounts); + put_user_ns(ima_ns->user_ns); + + ucounts = inc_ima_namespaces(user_ns); + if (!ucounts) + return -ENOSPC; + + ima_ns->user_ns = get_user_ns(user_ns); + ima_ns->ucounts = ucounts; + + return 0; +} + /** * Clone a new ns copying an original ima namespace, setting refcount to 1 * @@ -352,23 +370,33 @@ static int imans_install(struct nsset *nsset, struct ns_common *new) return res; }
-int imans_on_fork(struct nsproxy *nsproxy, struct task_struct *tsk) +int imans_on_fork(struct nsproxy *nsproxy, struct task_struct *tsk, + struct user_namespace *user_ns) { int res; - struct ns_common *nsc = &nsproxy->ima_ns_for_children->ns; - struct ima_namespace *ns = to_ima_ns(nsc); + struct ima_namespace *ima_ns = nsproxy->ima_ns_for_children;
/* create_new_namespaces() already incremented the ref counter */ - if (nsproxy->ima_ns == nsproxy->ima_ns_for_children) + if (nsproxy->ima_ns == ima_ns) return 0;
- res = imans_activate(ns); + /* It's possible that the user first unshares the IMA namespace and + * then creates a new user namespace on clone3(). In that case swap + * user namespace for the "current" one. + */ + if (ima_ns->user_ns != user_ns) { + res = ima_swap_user_ns(ima_ns, user_ns); + if (res) + return res; + } + + res = imans_activate(ima_ns); if (res) return res;
- get_ima_ns(ns); + get_ima_ns(ima_ns); put_ima_ns(nsproxy->ima_ns); - nsproxy->ima_ns = ns; + nsproxy->ima_ns = ima_ns;
return res; }
From: Krzysztof Struczynski krzysztof.struczynski@huawei.com
hulk inclusion category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/I49KW1 CVE: NA
--------------------------------
Add ima securityfs entries to configure per ima namespace: - path to the x509 certificate - ima kernel boot parameters
The x509 certificate will be parsed and loaded when the first process is born into the new ima namespace, paths are not validated when written.
Kernel boot parameters are pre-parsed and applied when the first process is born into the new namespace.
Signed-off-by: Krzysztof Struczynski krzysztof.struczynski@huawei.com Reviewed-by: Zhang Tianxing zhangtianxing3@huawei.com Signed-off-by: Zheng Zengkai zhengzengkai@huawei.com --- include/linux/ima.h | 2 + security/integrity/ima/ima.h | 19 ++-- security/integrity/ima/ima_appraise.c | 9 +- security/integrity/ima/ima_fs.c | 121 ++++++++++++++++++++-- security/integrity/ima/ima_init.c | 2 + security/integrity/ima/ima_ns.c | 140 ++++++++++++++++++++++---- security/integrity/ima/ima_policy.c | 40 +++++--- 7 files changed, 278 insertions(+), 55 deletions(-)
diff --git a/include/linux/ima.h b/include/linux/ima.h index 91c637c943ed..1270337c1d99 100644 --- a/include/linux/ima.h +++ b/include/linux/ima.h @@ -235,6 +235,8 @@ struct ima_namespace { struct list_head ns_measurements; atomic_long_t ml_len; /* number of stored measurements in the list */ atomic_long_t violations; + char *x509_path_for_children; + struct ima_policy_setup_data *policy_setup_for_children; } __randomize_layout;
extern struct ima_namespace init_ima_ns; diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h index 96ac4e5fbe3d..98a10a2d89b4 100644 --- a/security/integrity/ima/ima.h +++ b/security/integrity/ima/ima.h @@ -67,6 +67,7 @@ struct ima_policy_setup_data { bool ima_use_appraise_tcb; bool ima_use_appraise_exec_tcb; bool ima_use_appraise_exec_immutable; + bool fail_unverifiable_sigs; };
/* IMA event related data */ @@ -321,15 +322,10 @@ 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); +int ima_policy_setup(char *str, struct ima_namespace *ima_ns); +int ima_default_measure_policy_setup(char *str, struct ima_namespace *ima_ns); +int ima_default_appraise_policy_setup(char *str, struct ima_namespace *ima_ns); +int ima_default_appraise_setup(char *str, struct ima_namespace *ima_ns);
/* Appraise integrity measurements */ #define IMA_APPRAISE_ENFORCE 0x01 @@ -432,6 +428,11 @@ static inline struct ima_namespace *get_current_ns(void)
void ima_delete_ns_rules(struct ima_policy_data *policy_data, bool is_root_ns); + +ssize_t ima_ns_write_x509_for_children(struct ima_namespace *ima_ns, + char *x509_path); +ssize_t ima_ns_write_kcmd_for_children(struct ima_namespace *ima_ns, + char *kcmd); #else static inline int __init ima_init_namespace(void) { diff --git a/security/integrity/ima/ima_appraise.c b/security/integrity/ima/ima_appraise.c index 9d041e3deef2..c30262468e4c 100644 --- a/security/integrity/ima/ima_appraise.c +++ b/security/integrity/ima/ima_appraise.c @@ -19,9 +19,12 @@
static bool ima_appraise_req_evm __ro_after_init;
-int ima_default_appraise_setup(const char *str, - struct ima_policy_setup_data *setup_data) +int ima_default_appraise_setup(char *str, + struct ima_namespace *ima_ns) { + struct ima_policy_setup_data *setup_data = (ima_ns == &init_ima_ns) ? + &init_policy_setup_data : ima_ns->policy_setup_for_children; + #ifdef CONFIG_IMA_APPRAISE_BOOTPARAM bool sb_state = arch_ima_get_secureboot(); int appraisal_state = setup_data->ima_appraise; @@ -55,7 +58,7 @@ int ima_default_appraise_setup(const char *str,
static int __init default_appraise_setup(char *str) { - return ima_default_appraise_setup(str, &init_policy_setup_data); + return ima_default_appraise_setup(str, &init_ima_ns); }
__setup("ima_appraise=", default_appraise_setup); diff --git a/security/integrity/ima/ima_fs.c b/security/integrity/ima/ima_fs.c index 97bda1826a94..8436729642fe 100644 --- a/security/integrity/ima/ima_fs.c +++ b/security/integrity/ima/ima_fs.c @@ -23,6 +23,8 @@ #include <linux/vmalloc.h> #include <linux/file.h> #include <linux/ctype.h> +#include <linux/string.h> +#include <linux/kernel.h>
#include "ima.h" #include "ima_digest_list.h" @@ -39,6 +41,10 @@ static struct dentry *ima_policy; static struct dentry *digests_count; static struct dentry *digest_list_data; static struct dentry *digest_list_data_del; +#ifdef CONFIG_IMA_NS +static struct dentry *x509_for_children; +static struct dentry *kcmd_for_children; +#endif /* CONFIG_IMA_NS */
bool ima_canonical_fmt; static int __init default_canonical_fmt_setup(char *str) @@ -52,6 +58,16 @@ __setup("ima_canonical_fmt", default_canonical_fmt_setup);
static int valid_policy = 1;
+static int ima_open_simple(struct inode *inode, struct file *file) +{ + struct ima_namespace *ima_ns = get_current_ns(); + + if (!ns_capable(ima_ns->user_ns, CAP_SYS_ADMIN)) + return -EPERM; + + return 0; +} + static ssize_t ima_show_htable_value(struct file *filp, char __user *buf, size_t count, loff_t *ppos) { @@ -73,18 +89,8 @@ static ssize_t ima_show_htable_value(struct file *filp, char __user *buf, return simple_read_from_buffer(buf, count, ppos, tmpbuf, len); }
-static int ima_open_htable_value(struct inode *inode, struct file *file) -{ - struct ima_namespace *ima_ns = get_current_ns(); - - if (!ns_capable(ima_ns->user_ns, CAP_SYS_ADMIN)) - return -EPERM; - - return 0; -} - static const struct file_operations ima_htable_value_ops = { - .open = ima_open_htable_value, + .open = ima_open_simple, .read = ima_show_htable_value, .llseek = generic_file_llseek, }; @@ -613,6 +619,79 @@ static const struct file_operations ima_data_upload_ops = { .llseek = generic_file_llseek, };
+#ifdef CONFIG_IMA_NS +static int ima_open_for_children(struct inode *inode, struct file *file) +{ + struct ima_namespace *ima_ns = get_current_ns(); + + /* Allow to set children configuration only after unshare() */ + if (ima_ns == current->nsproxy->ima_ns_for_children) + return -EPERM; + + return ima_open_simple(inode, file); +} + +static ssize_t ima_write_x509_for_children(struct file *file, + const char __user *buf, + size_t count, loff_t *ppos) +{ + ssize_t res; + char *x509_path; + struct ima_namespace *ima_ns = current->nsproxy->ima_ns_for_children; + + /* Only allow < page size writes at the beginning of the file */ + if ((*ppos != 0) || (count >= PAGE_SIZE)) + return -EINVAL; + + x509_path = memdup_user_nul(buf, count); + if (IS_ERR(x509_path)) + return PTR_ERR(x509_path); + + res = ima_ns_write_x509_for_children(ima_ns, x509_path); + if (res) { + kfree(x509_path); + count = res; + } + + return count; +} + +static const struct file_operations ima_x509_for_children_ops = { + .open = ima_open_for_children, + .write = ima_write_x509_for_children, +}; + +static ssize_t ima_write_kcmd_for_children(struct file *file, + const char __user *buf, + size_t count, loff_t *ppos) +{ + ssize_t res; + char *kcmd; + struct ima_namespace *ima_ns = current->nsproxy->ima_ns_for_children; + + /* Only allow < page size writes at the beginning of the file */ + if ((*ppos != 0) || (count >= PAGE_SIZE)) + return -EINVAL; + + kcmd = memdup_user_nul(buf, count); + if (IS_ERR(kcmd)) + return PTR_ERR(kcmd); + + res = ima_ns_write_kcmd_for_children(ima_ns, kcmd); + if (res) + count = res; + + kfree(kcmd); + + return count; +} + +static const struct file_operations ima_kcmd_for_children_ops = { + .open = ima_open_for_children, + .write = ima_write_kcmd_for_children, +}; +#endif /* CONFIG_IMA_NS */ + int __init ima_fs_init(void) { ima_dir = securityfs_create_dir("ima", integrity_dir); @@ -676,6 +755,22 @@ int __init ima_fs_init(void) if (IS_ERR(digest_list_data_del)) goto out; #endif +#ifdef CONFIG_IMA_NS + x509_for_children = securityfs_create_file("x509_for_children", + 0202, + ima_dir, NULL, + &ima_x509_for_children_ops); + if (IS_ERR(x509_for_children)) + goto out; + + kcmd_for_children = securityfs_create_file("kcmd_for_children", + 0202, + ima_dir, NULL, + &ima_kcmd_for_children_ops); + if (IS_ERR(kcmd_for_children)) + goto out; +#endif /* CONFIG_IMA_NS */ + return 0; out: securityfs_remove(digest_list_data_del); @@ -688,5 +783,9 @@ int __init ima_fs_init(void) securityfs_remove(ima_symlink); securityfs_remove(ima_dir); securityfs_remove(ima_policy); +#ifdef CONFIG_IMA_NS + securityfs_remove(x509_for_children); + securityfs_remove(kcmd_for_children); +#endif /* CONFIG_IMA_NS */ return -1; } diff --git a/security/integrity/ima/ima_init.c b/security/integrity/ima/ima_init.c index 923373a12f5c..7a0a640fdf9c 100644 --- a/security/integrity/ima/ima_init.c +++ b/security/integrity/ima/ima_init.c @@ -31,6 +31,8 @@ struct ima_namespace init_ima_ns = { .ns.inum = PROC_IMA_INIT_INO, #ifdef CONFIG_IMA_NS .ns.ops = &imans_operations, + .x509_path_for_children = NULL, + .policy_setup_for_children = NULL, #endif .frozen = true, .policy_data = &init_policy_data, diff --git a/security/integrity/ima/ima_ns.c b/security/integrity/ima/ima_ns.c index ac000920d486..57dc9fedb8fd 100644 --- a/security/integrity/ima/ima_ns.c +++ b/security/integrity/ima/ima_ns.c @@ -27,6 +27,8 @@ #include <linux/workqueue.h> #include <linux/mutex.h> #include <linux/spinlock.h> +#include <linux/string.h> +#include <linux/kernel.h>
#include "ima.h"
@@ -74,23 +76,17 @@ static struct ima_namespace *ima_ns_alloc(void) return NULL; }
-static void ima_set_ns_policy(struct ima_namespace *ima_ns, - char *policy_setup_str) +static void ima_set_ns_policy(struct ima_namespace *ima_ns) { - struct ima_policy_setup_data setup_data; + struct ima_policy_setup_data setup_data = {0};
+ if (!ima_ns->policy_setup_for_children) { #ifdef CONFIG_IMA_APPRAISE - setup_data.ima_appraise = IMA_APPRAISE_ENFORCE; + 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); + ima_init_ns_policy(ima_ns, &setup_data); + } else + ima_init_ns_policy(ima_ns, ima_ns->policy_setup_for_children); }
static int ima_swap_user_ns(struct ima_namespace *ima_ns, @@ -151,6 +147,9 @@ static struct ima_namespace *clone_ima_ns(struct user_namespace *user_ns, rwlock_init(&ns->iint_tree->lock); ns->iint_tree->root = RB_ROOT;
+ ns->x509_path_for_children = NULL; + ns->policy_setup_for_children = NULL; + INIT_LIST_HEAD(&ns->ns_measurements); INIT_LIST_HEAD(&ns->policy_data->ima_default_rules); INIT_LIST_HEAD(&ns->policy_data->ima_policy_rules); @@ -218,6 +217,14 @@ static void imans_remove_hash_entries(struct ima_namespace *ima_ns) } }
+static void destroy_child_config(struct ima_namespace *ima_ns) +{ + kfree(ima_ns->x509_path_for_children); + ima_ns->x509_path_for_children = NULL; + kfree(ima_ns->policy_setup_for_children); + ima_ns->policy_setup_for_children = NULL; +} + static void destroy_ima_ns(struct ima_namespace *ns) { bool is_init_ns = (ns == &init_ima_ns); @@ -230,6 +237,7 @@ static void destroy_ima_ns(struct ima_namespace *ns) kfree(ns->iint_tree); ima_delete_ns_rules(ns->policy_data, is_init_ns); kfree(ns->policy_data); + destroy_child_config(ns); kfree(ns); }
@@ -319,27 +327,31 @@ static void imans_put(struct ns_common *ns)
static int imans_activate(struct ima_namespace *ima_ns) { + int res = 0; + if (ima_ns == &init_ima_ns) - return 0; + return res;
if (ima_ns->frozen) - return 0; + return res;
mutex_lock(&frozen_lock); if (ima_ns->frozen) goto out;
- ima_set_ns_policy(ima_ns, NULL); + ima_set_ns_policy(ima_ns);
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); + + destroy_child_config(ima_ns); out: mutex_unlock(&frozen_lock);
- return 0; + return res; }
static int imans_install(struct nsset *nsset, struct ns_common *new) @@ -424,3 +436,97 @@ const struct proc_ns_operations imans_for_children_operations = { .owner = imans_owner, };
+struct ima_kernel_param { + const char *name; + int (*set)(char *val, struct ima_namespace *ima_ns); +}; + +/* TODO: add ima_template, ima_template_fmt, ima_hash, ... */ +static const struct ima_kernel_param ima_kernel_params[] = { + {"ima_appraise", ima_default_appraise_setup}, + {"ima_policy", ima_policy_setup}, +}; +static const size_t ima_kernel_params_size = ARRAY_SIZE(ima_kernel_params); + +ssize_t ima_ns_write_x509_for_children(struct ima_namespace *ima_ns, + char *x509_path) +{ + ssize_t retval = 0; + + mutex_lock(&frozen_lock); + if (ima_ns->frozen) { + retval = -EACCES; + goto out; + } + + kfree(ima_ns->x509_path_for_children); + ima_ns->x509_path_for_children = x509_path; +out: + mutex_unlock(&frozen_lock); + + return retval; +} + +ssize_t ima_ns_write_kcmd_for_children(struct ima_namespace *ima_ns, + char *kcmd) +{ + u32 i; + char *param, *val; + ssize_t ret = 0; + + mutex_lock(&frozen_lock); + if (ima_ns->frozen) { + ret = -EACCES; + goto err_unlock; + } + + if (!ima_ns->policy_setup_for_children) { + ima_ns->policy_setup_for_children = + kmalloc(sizeof(struct ima_policy_setup_data), + GFP_KERNEL); + if (!ima_ns->policy_setup_for_children) { + ret = -ENOMEM; + goto err_unlock; + } + } + + memset(ima_ns->policy_setup_for_children, + 0, sizeof(struct ima_policy_setup_data)); + +#ifdef CONFIG_IMA_APPRAISE + ima_ns->policy_setup_for_children->ima_appraise = IMA_APPRAISE_ENFORCE; +#endif + + kcmd = skip_spaces(kcmd); + while (*kcmd) { + kcmd = next_arg(kcmd, ¶m, &val); + if (!val) { + ret = -EINVAL; + goto err_free; + } + + for (i = 0; i < ima_kernel_params_size; i++) { + if (strcmp(param, ima_kernel_params[i].name) == 0) + break; + } + + if (i == ima_kernel_params_size) { + ret = -EINVAL; + goto err_free; + } + + ima_kernel_params[i].set(val, ima_ns); + } + mutex_unlock(&frozen_lock); + + return ret; + +err_free: + kfree(ima_ns->policy_setup_for_children); + ima_ns->policy_setup_for_children = NULL; +err_unlock: + mutex_unlock(&frozen_lock); + + return ret; +} + diff --git a/security/integrity/ima/ima_policy.c b/security/integrity/ima/ima_policy.c index 0ab91cb31121..828793553a0e 100644 --- a/security/integrity/ima/ima_policy.c +++ b/security/integrity/ima/ima_policy.c @@ -240,9 +240,13 @@ struct ima_policy_data init_policy_data = { .ima_rules = &init_policy_data.ima_default_rules, };
-int ima_default_measure_policy_setup(const char *str, - struct ima_policy_setup_data *setup_data) +int ima_default_measure_policy_setup(char *str, struct ima_namespace *ima_ns) { + struct ima_policy_setup_data *setup_data; + + setup_data = (ima_ns == &init_ima_ns) ? + &init_policy_setup_data : ima_ns->policy_setup_for_children; + if (setup_data->ima_policy) return 1;
@@ -252,7 +256,7 @@ int ima_default_measure_policy_setup(const char *str,
static int __init default_measure_policy_setup(char *str) { - return ima_default_measure_policy_setup(str, &init_policy_setup_data); + return ima_default_measure_policy_setup(str, &init_ima_ns); } __setup("ima_tcb", default_measure_policy_setup);
@@ -261,15 +265,15 @@ 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 + * @ima_ns: pointer to the ima namespace which policy is being set */ -int ima_policy_setup(char *str, - struct ima_policy_setup_data *setup_data, - bool *fail_unverifiable_sigs) +int ima_policy_setup(char *str, struct ima_namespace *ima_ns) { char *p; + struct ima_policy_setup_data *setup_data; + + setup_data = (ima_ns == &init_ima_ns) ? + &init_policy_setup_data : ima_ns->policy_setup_for_children;
while ((p = strsep(&str, " |\n")) != NULL) { if (*p == ' ') @@ -287,7 +291,7 @@ int ima_policy_setup(char *str, else if (strcmp(p, "secure_boot") == 0) setup_data->ima_use_secure_boot = true; else if (strcmp(p, "fail_securely") == 0) - *fail_unverifiable_sigs = true; + setup_data->fail_unverifiable_sigs = true; else pr_err("policy "%s" not found", p); } @@ -297,21 +301,27 @@ int ima_policy_setup(char *str,
static int __init policy_setup(char *str) { - return ima_policy_setup(str, &init_policy_setup_data, - &ima_fail_unverifiable_sigs); + ima_policy_setup(str, &init_ima_ns); + ima_fail_unverifiable_sigs = + init_policy_setup_data.fail_unverifiable_sigs; + return 1; } __setup("ima_policy=", policy_setup);
-int ima_default_appraise_policy_setup(const char *str, - struct ima_policy_setup_data *setup_data) +int ima_default_appraise_policy_setup(char *str, struct ima_namespace *ima_ns) { + struct ima_policy_setup_data *setup_data; + + setup_data = (ima_ns == &init_ima_ns) ? + &init_policy_setup_data : ima_ns->policy_setup_for_children; + setup_data->ima_use_appraise_tcb = true; return 1; }
static int __init default_appraise_policy_setup(char *str) { - return ima_default_appraise_policy_setup(str, &init_policy_setup_data); + return ima_default_appraise_policy_setup(str, &init_ima_ns); } __setup("ima_appraise_tcb", default_appraise_policy_setup);
From: Krzysztof Struczynski krzysztof.struczynski@huawei.com
hulk inclusion category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/I49KW1 CVE: NA
--------------------------------
Parse per ima namespace policy file. The path is passed as for the root ima namespace through the ima securityfs 'policy' entry.
Signed-off-by: Krzysztof Struczynski krzysztof.struczynski@huawei.com Reviewed-by: Zhang Tianxing zhangtianxing3@huawei.com Signed-off-by: Zheng Zengkai zhengzengkai@huawei.com --- security/integrity/ima/ima.h | 6 +++--- security/integrity/ima/ima_fs.c | 13 +++++++------ security/integrity/ima/ima_policy.c | 25 +++++++++++-------------- 3 files changed, 21 insertions(+), 23 deletions(-)
diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h index 98a10a2d89b4..aff8a3c748bf 100644 --- a/security/integrity/ima/ima.h +++ b/security/integrity/ima/ima.h @@ -312,10 +312,10 @@ int ima_match_policy(struct inode *inode, const struct cred *cred, u32 secid, 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(struct ima_namespace *ima_ns); void ima_update_policy_flag(struct ima_namespace *ima_ns); -ssize_t ima_parse_add_rule(char *); -void ima_delete_rules(void); +ssize_t ima_parse_add_rule(char *rule, struct ima_namespace *ima_ns); +void ima_delete_rules(struct ima_namespace *ima_ns); 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); diff --git a/security/integrity/ima/ima_fs.c b/security/integrity/ima/ima_fs.c index 8436729642fe..6738cfc5a3dd 100644 --- a/security/integrity/ima/ima_fs.c +++ b/security/integrity/ima/ima_fs.c @@ -328,7 +328,8 @@ static const struct file_operations ima_ascii_measurements_ops = { .release = seq_release, };
-static ssize_t ima_read_sfs_file(char *path, struct dentry *dentry) +static ssize_t ima_read_sfs_file(char *path, struct dentry *dentry, + struct ima_namespace *ima_ns) { void *data = NULL; char *datap; @@ -370,7 +371,7 @@ static ssize_t ima_read_sfs_file(char *path, struct dentry *dentry) break;
pr_debug("rule: %s\n", p); - rc = ima_parse_add_rule(p); + rc = ima_parse_add_rule(p, ima_ns); } else if (dentry == digest_list_data || dentry == digest_list_data_del) { /* Only check size when adding digest lists */ @@ -452,7 +453,7 @@ static ssize_t ima_write_data(struct file *file, const char __user *buf, goto out_free;
if (data[0] == '/') { - result = ima_read_sfs_file(data, dentry); + result = ima_read_sfs_file(data, dentry, ima_ns); } else if (dentry == ima_policy) { if (ima_ns->policy_data->ima_appraise & IMA_APPRAISE_POLICY) { pr_err("signed policy file (specified " @@ -462,7 +463,7 @@ static ssize_t ima_write_data(struct file *file, const char __user *buf, "signed policy required", 1, 0); result = -EACCES; } else { - result = ima_parse_add_rule(data); + result = ima_parse_add_rule(data, ima_ns); } } else if (dentry == digest_list_data) { if (!ima_current_is_parser()) { @@ -593,13 +594,13 @@ static int ima_release_data_upload(struct inode *inode, struct file *file) "policy_update", cause, !valid_policy, 0);
if (!valid_policy) { - ima_delete_rules(); + ima_delete_rules(ima_ns); valid_policy = 1; clear_bit(flag, &ima_fs_flags); return 0; }
- ima_update_policy(); + ima_update_policy(ima_ns); #if !defined(CONFIG_IMA_WRITE_POLICY) && !defined(CONFIG_IMA_READ_POLICY) securityfs_remove(ima_policy); ima_policy = NULL; diff --git a/security/integrity/ima/ima_policy.c b/security/integrity/ima/ima_policy.c index 828793553a0e..a2f1253b46dc 100644 --- a/security/integrity/ima/ima_policy.c +++ b/security/integrity/ima/ima_policy.c @@ -858,7 +858,8 @@ static void add_rules(struct ima_namespace *ima_ns, } }
-static int ima_parse_rule(char *rule, struct ima_rule_entry *entry); +static int ima_parse_rule(char *rule, struct ima_rule_entry *entry, + struct ima_namespace *ima_ns);
static int ima_init_arch_policy(void) { @@ -888,7 +889,8 @@ static int ima_init_arch_policy(void) result = strlcpy(rule, *rules, sizeof(rule));
INIT_LIST_HEAD(&arch_policy_entry[i].list); - result = ima_parse_rule(rule, &arch_policy_entry[i]); + result = ima_parse_rule(rule, &arch_policy_entry[i], + &init_ima_ns); if (result) { pr_warn("Skipping unknown architecture policy rule: %s\n", rule); @@ -1044,10 +1046,8 @@ int ima_check_policy(const struct ima_namespace *ima_ns) * Policy rules are never deleted so ima_policy_flag gets zeroed only once when * we switch from the default policy to user defined. */ -void ima_update_policy(void) +void ima_update_policy(struct ima_namespace *ima_ns) { - /* 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_ns->policy_data->ima_temp_rules, @@ -1305,7 +1305,8 @@ static bool ima_validate_rule(struct ima_rule_entry *entry) return true; }
-static int ima_parse_rule(char *rule, struct ima_rule_entry *entry) +static int ima_parse_rule(char *rule, struct ima_rule_entry *entry, + struct ima_namespace *ima_ns) { struct audit_buffer *ab; char *from; @@ -1313,7 +1314,6 @@ static int ima_parse_rule(char *rule, struct ima_rule_entry *entry) bool uid_token; struct ima_template_desc *template_desc; int result = 0; - struct ima_namespace *ima_ns = get_current_ns();
ab = integrity_audit_log_start(audit_context(), GFP_KERNEL, AUDIT_INTEGRITY_POLICY_RULE); @@ -1692,19 +1692,18 @@ static int ima_parse_rule(char *rule, struct ima_rule_entry *entry) /** * ima_parse_add_rule - add a rule to ima_policy_rules * @rule - ima measurement policy rule + * @ima_ns - pointer to the ima namespace the rule will be added to * * Avoid locking by allowing just one writer at a time in ima_write_policy() * Returns the length of the rule parsed, an error code on failure */ -ssize_t ima_parse_add_rule(char *rule) +ssize_t ima_parse_add_rule(char *rule, struct ima_namespace *ima_ns) { static const char op[] = "update_policy"; char *p; 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; @@ -1722,7 +1721,7 @@ ssize_t ima_parse_add_rule(char *rule)
INIT_LIST_HEAD(&entry->list);
- result = ima_parse_rule(p, entry); + result = ima_parse_rule(p, entry, ima_ns); if (result) { ima_free_rule(entry); integrity_audit_msg(AUDIT_INTEGRITY_STATUS, NULL, @@ -1742,10 +1741,8 @@ ssize_t ima_parse_add_rule(char *rule) * different from the active one. There is also only one user of * ima_delete_rules() at a time. */ -void ima_delete_rules(void) +void ima_delete_rules(struct ima_namespace *ima_ns) { - /* 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;
From: Krzysztof Struczynski krzysztof.struczynski@huawei.com
hulk inclusion category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/I49KW1 CVE: NA
--------------------------------
Add function that checks if the UID map is defined. It will be used by ima to check if ID remapping in subject-based rules is necessary.
Signed-off-by: Krzysztof Struczynski krzysztof.struczynski@huawei.com Reviewed-by: Zhang Tianxing zhangtianxing3@huawei.com Signed-off-by: Zheng Zengkai zhengzengkai@huawei.com --- include/linux/user_namespace.h | 6 ++++++ kernel/user_namespace.c | 11 +++++++++++ 2 files changed, 17 insertions(+)
diff --git a/include/linux/user_namespace.h b/include/linux/user_namespace.h index 3eb64a50f248..6cb6a455e7aa 100644 --- a/include/linux/user_namespace.h +++ b/include/linux/user_namespace.h @@ -141,6 +141,7 @@ extern bool in_userns(const struct user_namespace *ancestor, const struct user_namespace *child); extern bool current_in_userns(const struct user_namespace *target_ns); struct ns_common *ns_get_owner(struct ns_common *ns); +extern bool userns_set_uidmap(const struct user_namespace *ns); #else
static inline struct user_namespace *get_user_ns(struct user_namespace *ns) @@ -185,6 +186,11 @@ static inline struct ns_common *ns_get_owner(struct ns_common *ns) { return ERR_PTR(-EPERM); } + +static inline bool userns_set_uidmap(const struct user_namespace *ns) +{ + return true; +} #endif
#endif /* _LINUX_USER_H */ diff --git a/kernel/user_namespace.c b/kernel/user_namespace.c index ce396ea4de60..cf9b7019e8ef 100644 --- a/kernel/user_namespace.c +++ b/kernel/user_namespace.c @@ -1291,6 +1291,17 @@ bool current_in_userns(const struct user_namespace *target_ns) } EXPORT_SYMBOL(current_in_userns);
+bool userns_set_uidmap(const struct user_namespace *ns) +{ + bool mapping_defined; + + mutex_lock(&userns_state_mutex); + mapping_defined = ns->uid_map.nr_extents != 0; + mutex_unlock(&userns_state_mutex); + + return mapping_defined; +} + static inline struct user_namespace *to_user_ns(struct ns_common *ns) { return container_of(ns, struct user_namespace, ns);
From: Krzysztof Struczynski krzysztof.struczynski@huawei.com
hulk inclusion category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/I49KW1 CVE: NA
--------------------------------
If subject based rule is added to the policy before the user namespace uid mapping is defined, ID has to be recalculated.
It can happen if the new user namespace is created alongside the new ima namespace. The default policy rules are loaded when the first process is born into the new ima namespace. In that case, user has no chance to define the mapping. It can also happen for the custom policy rules loaded from within the new ima namespace, before the mapping is created.
Signed-off-by: Krzysztof Struczynski krzysztof.struczynski@huawei.com Reviewed-by: Zhang Tianxing zhangtianxing3@huawei.com Signed-off-by: Zheng Zengkai zhengzengkai@huawei.com --- security/integrity/ima/ima_policy.c | 99 +++++++++++++++++++++++++---- 1 file changed, 87 insertions(+), 12 deletions(-)
diff --git a/security/integrity/ima/ima_policy.c b/security/integrity/ima/ima_policy.c index a2f1253b46dc..42bafb520e4e 100644 --- a/security/integrity/ima/ima_policy.c +++ b/security/integrity/ima/ima_policy.c @@ -19,6 +19,7 @@ #include <linux/genhd.h> #include <linux/seq_file.h> #include <linux/ima.h> +#include <linux/user_namespace.h>
#include "ima.h" #include "ima_digest_list.h" @@ -86,6 +87,10 @@ struct ima_rule_entry { char *fsname; struct ima_rule_opt_list *keyrings; /* Measure keys added to these keyrings */ struct ima_template_desc *template; + bool remap_uid; /* IDs of all subject oriented rules, added before the + * user namespace mapping is defined, + * have to be remapped. + */ };
/* @@ -573,6 +578,8 @@ static bool ima_match_rules(struct ima_rule_entry *rule, struct inode *inode, const char *keyring) { int i; + kuid_t remapped_kuid; + struct ima_namespace *current_ima_ns = get_current_ns();
if (func == KEY_CHECK) { return (rule->flags & IMA_FUNC) && (rule->func == func) && @@ -596,24 +603,49 @@ static bool ima_match_rules(struct ima_rule_entry *rule, struct inode *inode, if ((rule->flags & IMA_FSUUID) && !uuid_equal(&rule->fsuuid, &inode->i_sb->s_uuid)) return false; - if ((rule->flags & IMA_UID) && !rule->uid_op(cred->uid, rule->uid)) - return false; + if (rule->flags & IMA_UID) { + if (rule->remap_uid) { + remapped_kuid = make_kuid(current_ima_ns->user_ns, + __kuid_val(rule->uid)); + if (!uid_valid(remapped_kuid)) + return false; + } else + remapped_kuid = rule->uid; + if (!rule->uid_op(cred->uid, remapped_kuid)) + return false; + } if (rule->flags & IMA_EUID) { + if (rule->remap_uid) { + remapped_kuid = make_kuid(current_ima_ns->user_ns, + __kuid_val(rule->uid)); + if (!uid_valid(remapped_kuid)) + return false; + } else + remapped_kuid = rule->uid; if (has_capability_noaudit(current, CAP_SETUID)) { - if (!rule->uid_op(cred->euid, rule->uid) - && !rule->uid_op(cred->suid, rule->uid) - && !rule->uid_op(cred->uid, rule->uid)) + if (!rule->uid_op(cred->euid, remapped_kuid) + && !rule->uid_op(cred->suid, remapped_kuid) + && !rule->uid_op(cred->uid, remapped_kuid)) return false; - } else if (!rule->uid_op(cred->euid, rule->uid)) + } else if (!rule->uid_op(cred->euid, remapped_kuid)) return false; }
- if ((rule->flags & IMA_FOWNER) && - !rule->fowner_op(inode->i_uid, rule->fowner)) - return false; + if (rule->flags & IMA_FOWNER) { + if (rule->remap_uid) { + remapped_kuid = make_kuid(current_ima_ns->user_ns, + __kuid_val(rule->fowner)); + if (!uid_valid(remapped_kuid)) + return false; + } else + remapped_kuid = rule->fowner; + if (!rule->fowner_op(inode->i_uid, remapped_kuid)) + return false; + } if ((rule->flags & IMA_PARSER) && !ima_current_is_parser()) return false; + for (i = 0; i < MAX_LSM_RULES; i++) { int rc = 0; u32 osid; @@ -796,6 +828,9 @@ static void add_rules(struct ima_namespace *ima_ns,
for (i = 0; i < count; i++) { struct ima_rule_entry *entry; + bool set_uidmap; + + set_uidmap = userns_set_uidmap(ima_ns->user_ns);
if (setup_data->ima_policy == EXEC_TCB) { if (entries == dont_measure_rules) @@ -832,6 +867,17 @@ static void add_rules(struct ima_namespace *ima_ns, GFP_KERNEL); if (!entry) continue; + + if (!set_uidmap) + entry->remap_uid = true; + else { + entry->uid = + make_kuid(ima_ns->user_ns, + __kuid_val(entry->uid)); + entry->fowner = + make_kuid(ima_ns->user_ns, + __kuid_val(entry->fowner)); + } }
list_add_tail(&entry->list, @@ -844,6 +890,19 @@ static void add_rules(struct ima_namespace *ima_ns, if (!entry) continue;
+ if (ima_ns != &init_ima_ns) { + if (!set_uidmap) + entry->remap_uid = true; + else { + entry->uid = + make_kuid(ima_ns->user_ns, + __kuid_val(entry->uid)); + entry->fowner = + make_kuid(ima_ns->user_ns, + __kuid_val(entry->fowner)); + } + } + list_add_tail(&entry->list, &ima_ns->policy_data->ima_policy_rules); } @@ -1318,6 +1377,10 @@ static int ima_parse_rule(char *rule, struct ima_rule_entry *entry, ab = integrity_audit_log_start(audit_context(), GFP_KERNEL, AUDIT_INTEGRITY_POLICY_RULE);
+ if ((ima_ns != &init_ima_ns) && + (!userns_set_uidmap(ima_ns->user_ns))) + entry->remap_uid = true; + entry->uid = INVALID_UID; entry->fowner = INVALID_UID; entry->uid_op = &uid_eq; @@ -1534,8 +1597,13 @@ static int ima_parse_rule(char *rule, struct ima_rule_entry *entry,
result = kstrtoul(args[0].from, 10, &lnum); if (!result) { - entry->uid = make_kuid(current_user_ns(), - (uid_t) lnum); + if (!entry->remap_uid) + entry->uid = + make_kuid(current_user_ns(), + (uid_t) lnum); + else + entry->uid = KUIDT_INIT((uid_t) lnum); + if (!uid_valid(entry->uid) || (uid_t)lnum != lnum) result = -EINVAL; @@ -1562,7 +1630,14 @@ static int ima_parse_rule(char *rule, struct ima_rule_entry *entry,
result = kstrtoul(args[0].from, 10, &lnum); if (!result) { - entry->fowner = make_kuid(current_user_ns(), (uid_t)lnum); + if (!entry->remap_uid) + entry->fowner = + make_kuid(current_user_ns(), + (uid_t) lnum); + else + entry->fowner = + KUIDT_INIT((uid_t) lnum); + if (!uid_valid(entry->fowner) || (((uid_t)lnum) != lnum)) result = -EINVAL; else
From: Krzysztof Struczynski krzysztof.struczynski@huawei.com
hulk inclusion category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/I49KW1 CVE: NA
--------------------------------
Add keyring_search_tag() version of keyring_search(), that allows to specify the key domain tag.
Signed-off-by: Krzysztof Struczynski krzysztof.struczynski@huawei.com Reviewed-by: Zhang Tianxing zhangtianxing3@huawei.com Signed-off-by: Zheng Zengkai zhengzengkai@huawei.com --- include/linux/key.h | 17 +++++++++++++---- security/keys/keyring.c | 15 +++++++++------ 2 files changed, 22 insertions(+), 10 deletions(-)
diff --git a/include/linux/key.h b/include/linux/key.h index eed3ce139a32..53684db44615 100644 --- a/include/linux/key.h +++ b/include/linux/key.h @@ -417,10 +417,11 @@ extern int restrict_link_reject(struct key *keyring,
extern int keyring_clear(struct key *keyring);
-extern key_ref_t keyring_search(key_ref_t keyring, - struct key_type *type, - const char *description, - bool recurse); +extern key_ref_t keyring_search_tag(key_ref_t keyring, + struct key_type *type, + const char *description, + struct key_tag *domain_tag, + bool recurse);
extern int keyring_add_key(struct key *keyring, struct key *key); @@ -430,6 +431,14 @@ extern int keyring_restrict(key_ref_t keyring, const char *type,
extern struct key *key_lookup(key_serial_t id);
+static inline key_ref_t keyring_search(key_ref_t keyring, + struct key_type *type, + const char *description, + bool recurse) +{ + return keyring_search_tag(keyring, type, description, NULL, recurse); +} + static inline key_serial_t key_serial(const struct key *key) { return key ? key->serial : 0; diff --git a/security/keys/keyring.c b/security/keys/keyring.c index 14abfe765b7e..12583241ff63 100644 --- a/security/keys/keyring.c +++ b/security/keys/keyring.c @@ -925,22 +925,25 @@ key_ref_t keyring_search_rcu(key_ref_t keyring_ref, }
/** - * keyring_search - Search the supplied keyring tree for a matching key + * keyring_search_tag - Search the supplied keyring tree for a matching key * @keyring: The root of the keyring tree to be searched. * @type: The type of keyring we want to find. * @description: The name of the keyring we want to find. + * @domain_tag: The domain_tag of the key we want to find. * @recurse: True to search the children of @keyring also * * As keyring_search_rcu() above, but using the current task's credentials and * type's default matching function and preferred search method. */ -key_ref_t keyring_search(key_ref_t keyring, - struct key_type *type, - const char *description, - bool recurse) +key_ref_t keyring_search_tag(key_ref_t keyring, + struct key_type *type, + const char *description, + struct key_tag *domain_tag, + bool recurse) { struct keyring_search_context ctx = { .index_key.type = type, + .index_key.domain_tag = domain_tag, .index_key.description = description, .index_key.desc_len = strlen(description), .cred = current_cred(), @@ -968,7 +971,7 @@ key_ref_t keyring_search(key_ref_t keyring, type->match_free(&ctx.match_data); return key; } -EXPORT_SYMBOL(keyring_search); +EXPORT_SYMBOL(keyring_search_tag);
static struct key_restriction *keyring_restriction_alloc( key_restrict_link_func_t check)
From: Krzysztof Struczynski krzysztof.struczynski@huawei.com
hulk inclusion category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/I49KW1 CVE: NA
--------------------------------
Add domain tag to the key_match_data. If set, check domain tag in the default match function and asymmetric keys match functions.
This will allow to use the key domain tag in the search criteria for the iterative search, not only for the direct lookup that is based on the index key.
Signed-off-by: Krzysztof Struczynski krzysztof.struczynski@huawei.com Reviewed-by: Zhang Tianxing zhangtianxing3@huawei.com Signed-off-by: Zheng Zengkai zhengzengkai@huawei.com --- crypto/asymmetric_keys/asymmetric_type.c | 20 ++++++++++++++++---- include/linux/key-type.h | 1 + security/keys/keyring.c | 10 +++++++++- 3 files changed, 26 insertions(+), 5 deletions(-)
diff --git a/crypto/asymmetric_keys/asymmetric_type.c b/crypto/asymmetric_keys/asymmetric_type.c index 33e77d846caa..177429bc5c7b 100644 --- a/crypto/asymmetric_keys/asymmetric_type.c +++ b/crypto/asymmetric_keys/asymmetric_type.c @@ -249,9 +249,15 @@ static bool asymmetric_key_cmp(const struct key *key, { const struct asymmetric_key_ids *kids = asymmetric_key_ids(key); const struct asymmetric_key_id *match_id = match_data->preparsed; + bool match;
- return asymmetric_match_key_ids(kids, match_id, - asymmetric_key_id_same); + match = asymmetric_match_key_ids(kids, match_id, + asymmetric_key_id_same); + + if (match_data->domain_tag) + match &= key->index_key.domain_tag == match_data->domain_tag; + + return match; }
/* @@ -262,9 +268,15 @@ static bool asymmetric_key_cmp_partial(const struct key *key, { const struct asymmetric_key_ids *kids = asymmetric_key_ids(key); const struct asymmetric_key_id *match_id = match_data->preparsed; + bool match; + + match = asymmetric_match_key_ids(kids, match_id, + asymmetric_key_id_partial); + + if (match_data->domain_tag) + match &= key->index_key.domain_tag == match_data->domain_tag;
- return asymmetric_match_key_ids(kids, match_id, - asymmetric_key_id_partial); + return match; }
/* diff --git a/include/linux/key-type.h b/include/linux/key-type.h index 2ab2d6d6aeab..c8ea26ab242c 100644 --- a/include/linux/key-type.h +++ b/include/linux/key-type.h @@ -55,6 +55,7 @@ struct key_match_data { unsigned lookup_type; /* Type of lookup for this search. */ #define KEYRING_SEARCH_LOOKUP_DIRECT 0x0000 /* Direct lookup by description. */ #define KEYRING_SEARCH_LOOKUP_ITERATE 0x0001 /* Iterative search. */ + struct key_tag *domain_tag; /* Key domain tag */ };
/* diff --git a/security/keys/keyring.c b/security/keys/keyring.c index 12583241ff63..7e45e534035f 100644 --- a/security/keys/keyring.c +++ b/security/keys/keyring.c @@ -565,7 +565,13 @@ int restrict_link_reject(struct key *keyring, bool key_default_cmp(const struct key *key, const struct key_match_data *match_data) { - return strcmp(key->description, match_data->raw_data) == 0; + bool match; + + match = strcmp(key->description, match_data->raw_data) == 0; + if (match_data->domain_tag) + match &= key->index_key.domain_tag == match_data->domain_tag; + + return match; }
/* @@ -957,6 +963,8 @@ key_ref_t keyring_search_tag(key_ref_t keyring,
if (recurse) ctx.flags |= KEYRING_SEARCH_RECURSE; + if (domain_tag) + ctx.match_data.domain_tag = domain_tag; if (type->match_preparse) { ret = type->match_preparse(&ctx.match_data); if (ret < 0)
From: Krzysztof Struczynski krzysztof.struczynski@huawei.com
hulk inclusion category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/I49KW1 CVE: NA
--------------------------------
Add KEY_ALLOC_DOMAIN_* flags so that the key domain tag can be specified on the key creation. This is done to separate the key domain setting from the key type.
If applied to the keyring, it will set the requested domain tag for every key added to that keyring.
IMA uses the existing key_type_asymmetric for appraisal, but also has to specify the key domain to bind appraisal key with the ima namespace.
Signed-off-by: Krzysztof Struczynski krzysztof.struczynski@huawei.com Reviewed-by: Zhang Tianxing zhangtianxing3@huawei.com Signed-off-by: Zheng Zengkai zhengzengkai@huawei.com --- include/linux/key.h | 10 ++++++++++ security/keys/key.c | 16 ++++++++++++++++ 2 files changed, 26 insertions(+)
diff --git a/include/linux/key.h b/include/linux/key.h index 53684db44615..61250dfd9ccc 100644 --- a/include/linux/key.h +++ b/include/linux/key.h @@ -272,6 +272,12 @@ struct key { * restriction. */ struct key_restriction *restrict_link; + + /* This is set on a keyring to indicate that every key added to this + * keyring should be tagged with a given key domain tag. It is ignored + * for the non-keyring keys and can be overridden by the key-type flags. + */ + unsigned long key_alloc_domain; };
extern struct key *key_alloc(struct key_type *type, @@ -291,6 +297,10 @@ extern struct key *key_alloc(struct key_type *type, #define KEY_ALLOC_UID_KEYRING 0x0010 /* allocating a user or user session keyring */ #define KEY_ALLOC_SET_KEEP 0x0020 /* Set the KEEP flag on the key/keyring */
+/* Only one domain can be set */ +#define KEY_ALLOC_DOMAIN_IMA 0x0100 /* add IMA domain tag, based on the "current" */ +#define KEY_ALLOC_DOMAIN_MASK 0xFF00 + extern void key_revoke(struct key *key); extern void key_invalidate(struct key *key); extern void key_put(struct key *key); diff --git a/security/keys/key.c b/security/keys/key.c index 151ff39b6803..d052b9a0b1fd 100644 --- a/security/keys/key.c +++ b/security/keys/key.c @@ -278,6 +278,19 @@ struct key *key_alloc(struct key_type *type, const char *desc, if (!key) goto no_memory_2;
+ if (flags & KEY_ALLOC_DOMAIN_MASK) { + /* set alloc domain for all keys added to this keyring */ + if (type == &key_type_keyring) + key->key_alloc_domain = (flags & KEY_ALLOC_DOMAIN_MASK); + + /* set domain tag if it's not predefined for the key type */ + if ((!type->flags) && (flags & KEY_ALLOC_DOMAIN_IMA)) + /* Set it to something meaningful after adding a key + * domain to the ima namespace. + */ + key->index_key.domain_tag = NULL; + } + key->index_key.desc_len = desclen; key->index_key.description = kmemdup(desc, desclen + 1, GFP_KERNEL); if (!key->index_key.description) @@ -927,6 +940,9 @@ key_ref_t key_create_or_update(key_ref_t keyring_ref, perm |= KEY_POS_WRITE; }
+ if (keyring->key_alloc_domain) + flags |= keyring->key_alloc_domain; + /* allocate a new key */ key = key_alloc(index_key.type, index_key.description, cred->fsuid, cred->fsgid, cred, perm, flags, NULL);
From: Krzysztof Struczynski krzysztof.struczynski@huawei.com
hulk inclusion category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/I49KW1 CVE: NA
--------------------------------
Add key domain to the ima namespace. This will allow to bind the appraisal keys with the namespace and store all appraisal keys in the ima system keyring.
Signed-off-by: Krzysztof Struczynski krzysztof.struczynski@huawei.com Reviewed-by: Zhang Tianxing zhangtianxing3@huawei.com Signed-off-by: Zheng Zengkai zhengzengkai@huawei.com --- include/linux/ima.h | 3 +++ security/integrity/ima/ima_init.c | 8 ++++++++ security/integrity/ima/ima_ns.c | 14 ++++++++++++++ security/keys/key.c | 10 +++++++--- 4 files changed, 32 insertions(+), 3 deletions(-)
diff --git a/include/linux/ima.h b/include/linux/ima.h index 1270337c1d99..eb022229c694 100644 --- a/include/linux/ima.h +++ b/include/linux/ima.h @@ -237,6 +237,9 @@ struct ima_namespace { atomic_long_t violations; char *x509_path_for_children; struct ima_policy_setup_data *policy_setup_for_children; +#ifdef CONFIG_KEYS + struct key_tag *key_domain; +#endif } __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 7a0a640fdf9c..73fb0f0e82c5 100644 --- a/security/integrity/ima/ima_init.c +++ b/security/integrity/ima/ima_init.c @@ -18,6 +18,7 @@ #include <linux/kref.h> #include <linux/proc_ns.h> #include <linux/user_namespace.h> +#include <linux/key.h>
#include "ima.h"
@@ -25,6 +26,10 @@ const char boot_aggregate_name[] = "boot_aggregate"; struct tpm_chip *ima_tpm_chip;
+#ifdef CONFIG_KEYS +static struct key_tag init_ima_key_domain = { .usage = REFCOUNT_INIT(1) }; +#endif + struct ima_namespace init_ima_ns = { .kref = KREF_INIT(2), .user_ns = &init_user_ns, @@ -40,6 +45,9 @@ struct ima_namespace init_ima_ns = { .ns_measurements = LIST_HEAD_INIT(init_ima_ns.ns_measurements), .ml_len = ATOMIC_LONG_INIT(0), .violations = ATOMIC_LONG_INIT(0), +#ifdef CONFIG_KEYS + .key_domain = &init_ima_key_domain, +#endif }; EXPORT_SYMBOL(init_ima_ns);
diff --git a/security/integrity/ima/ima_ns.c b/security/integrity/ima/ima_ns.c index 57dc9fedb8fd..639e5eadee31 100644 --- a/security/integrity/ima/ima_ns.c +++ b/security/integrity/ima/ima_ns.c @@ -29,6 +29,7 @@ #include <linux/spinlock.h> #include <linux/string.h> #include <linux/kernel.h> +#include <linux/key.h>
#include "ima.h"
@@ -66,8 +67,16 @@ static struct ima_namespace *ima_ns_alloc(void) if (!ima_ns->iint_tree) goto policy_free;
+#ifdef CONFIG_KEYS + ima_ns->key_domain = kzalloc(sizeof(struct key_tag), GFP_KERNEL); + if (!ima_ns->key_domain) + goto iint_free; +#endif + return ima_ns;
+iint_free: + kfree(ima_ns->iint_tree); policy_free: kfree(ima_ns->policy_data); ns_free: @@ -147,6 +156,9 @@ static struct ima_namespace *clone_ima_ns(struct user_namespace *user_ns, rwlock_init(&ns->iint_tree->lock); ns->iint_tree->root = RB_ROOT;
+#ifdef CONFIG_KEYS + refcount_set(&ns->key_domain->usage, 1); +#endif ns->x509_path_for_children = NULL; ns->policy_setup_for_children = NULL;
@@ -160,6 +172,7 @@ static struct ima_namespace *clone_ima_ns(struct user_namespace *user_ns, fail_free: kfree(ns->iint_tree); kfree(ns->policy_data); + kfree(ns->key_domain); kfree(ns); fail_dec: dec_ima_namespaces(ucounts); @@ -231,6 +244,7 @@ static void destroy_ima_ns(struct ima_namespace *ns)
imans_remove_hash_entries(ns); dec_ima_namespaces(ns->ucounts); + key_remove_domain(ns->key_domain); put_user_ns(ns->user_ns); ns_free_inum(&ns->ns); integrity_iint_tree_free(ns->iint_tree); diff --git a/security/keys/key.c b/security/keys/key.c index d052b9a0b1fd..43de2dfe95cf 100644 --- a/security/keys/key.c +++ b/security/keys/key.c @@ -285,10 +285,14 @@ struct key *key_alloc(struct key_type *type, const char *desc,
/* set domain tag if it's not predefined for the key type */ if ((!type->flags) && (flags & KEY_ALLOC_DOMAIN_IMA)) - /* Set it to something meaningful after adding a key - * domain to the ima namespace. + /* Use ima_ns_for_children, not ima_ns. ima_ns_for + * children is equal to ima_ns, unless ima namespace was + * unshared and the new namespace is being configured. + * In that case, new keys should be associated with the + * new ima namespace. */ - key->index_key.domain_tag = NULL; + key->index_key.domain_tag = + current->nsproxy->ima_ns_for_children->key_domain; }
key->index_key.desc_len = desclen;
From: Krzysztof Struczynski krzysztof.struczynski@huawei.com
hulk inclusion category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/I49KW1 CVE: NA
--------------------------------
Add key domain tag to the search criteria in digsig module. If the domain tag is not set for the keys from the given keyring, it is set to NULL and the behaviour is unchanged.
The key domain tag is added to the ima appraisal keys loaded to the system ima keyring.
Signed-off-by: Krzysztof Struczynski krzysztof.struczynski@huawei.com Reviewed-by: Zhang Tianxing zhangtianxing3@huawei.com Signed-off-by: Zheng Zengkai zhengzengkai@huawei.com --- include/linux/digsig.h | 11 ++++--- lib/digsig.c | 11 +++---- security/integrity/digsig.c | 40 ++++++++++++++++++++++---- security/integrity/digsig_asymmetric.c | 20 ++++++++----- security/integrity/integrity.h | 11 ++++--- 5 files changed, 67 insertions(+), 26 deletions(-)
diff --git a/include/linux/digsig.h b/include/linux/digsig.h index 2ace69e41088..9e4121253899 100644 --- a/include/linux/digsig.h +++ b/include/linux/digsig.h @@ -44,13 +44,16 @@ struct signature_hdr {
#if defined(CONFIG_SIGNATURE) || defined(CONFIG_SIGNATURE_MODULE)
-int digsig_verify(struct key *keyring, const char *sig, int siglen, - const char *digest, int digestlen); +int digsig_verify(struct key *keyring, struct key_tag *domain_tag, + const char *sig, int siglen, const char *digest, + int digestlen);
#else
-static inline int digsig_verify(struct key *keyring, const char *sig, - int siglen, const char *digest, int digestlen) +static inline int digsig_verify(struct key *keyring, + struct key_tag *domain_tag, + const char *sig, int siglen, const char *digest, + int digestlen) { return -EOPNOTSUPP; } diff --git a/lib/digsig.c b/lib/digsig.c index 04b5e55ed95f..4cf5327327c8 100644 --- a/lib/digsig.c +++ b/lib/digsig.c @@ -196,8 +196,8 @@ static int digsig_verify_rsa(struct key *key, * Normally hash of the content is used as a data for this function. * */ -int digsig_verify(struct key *keyring, const char *sig, int siglen, - const char *data, int datalen) +int digsig_verify(struct key *keyring, struct key_tag *domain_tag, + const char *sig, int siglen, const char *data, int datalen) { int err = -ENOMEM; struct signature_hdr *sh = (struct signature_hdr *)sig; @@ -217,14 +217,15 @@ int digsig_verify(struct key *keyring, const char *sig, int siglen, if (keyring) { /* search in specific keyring */ key_ref_t kref; - kref = keyring_search(make_key_ref(keyring, 1UL), - &key_type_user, name, true); + kref = keyring_search_tag(make_key_ref(keyring, 1UL), + &key_type_user, name, + domain_tag, true); if (IS_ERR(kref)) key = ERR_CAST(kref); else key = key_ref_to_ptr(kref); } else { - key = request_key(&key_type_user, name, NULL); + key = request_key_tag(&key_type_user, name, domain_tag, NULL); } if (IS_ERR(key)) { pr_err("key not found, id: %s\n", name); diff --git a/security/integrity/digsig.c b/security/integrity/digsig.c index 0f518dcfde05..4d2579a2c5ea 100644 --- a/security/integrity/digsig.c +++ b/security/integrity/digsig.c @@ -16,6 +16,7 @@ #include <linux/vmalloc.h> #include <crypto/public_key.h> #include <keys/system_keyring.h> +#include <linux/ima.h>
#include "integrity.h"
@@ -32,6 +33,16 @@ static const char * const keyring_name[INTEGRITY_KEYRING_MAX] = { ".platform", };
+static unsigned long keyring_alloc_flags[INTEGRITY_KEYRING_MAX] = { + KEY_ALLOC_NOT_IN_QUOTA, +#ifdef CONFIG_IMA_NS + KEY_ALLOC_NOT_IN_QUOTA | KEY_ALLOC_DOMAIN_IMA, +#else + KEY_ALLOC_NOT_IN_QUOTA, +#endif + KEY_ALLOC_NOT_IN_QUOTA, +}; + #ifdef CONFIG_IMA_KEYRINGS_PERMIT_SIGNED_BY_BUILTIN_OR_SECONDARY #define restrict_link_to_ima restrict_link_by_builtin_and_secondary_trusted #else @@ -57,10 +68,22 @@ static struct key *integrity_keyring_from_id(const unsigned int id) return keyring[id]; }
+static struct key_tag *domain_tag_from_id(const unsigned int id) +{ + if (id >= INTEGRITY_KEYRING_MAX) + return ERR_PTR(-EINVAL); + + if (id == INTEGRITY_KEYRING_IMA) + return current->nsproxy->ima_ns->key_domain; + + return NULL; +} + int integrity_digsig_verify(const unsigned int id, const char *sig, int siglen, const char *digest, int digestlen) { struct key *keyring; + struct key_tag *domain_tag;
if (siglen < 2) return -EINVAL; @@ -69,14 +92,18 @@ int integrity_digsig_verify(const unsigned int id, const char *sig, int siglen, if (IS_ERR(keyring)) return PTR_ERR(keyring);
+ domain_tag = domain_tag_from_id(id); + if (IS_ERR(domain_tag)) + return PTR_ERR(domain_tag); + switch (sig[1]) { case 1: /* v1 API expect signature without xattr type */ - return digsig_verify(keyring, sig + 1, siglen - 1, digest, - digestlen); + return digsig_verify(keyring, domain_tag, + sig + 1, siglen - 1, digest, digestlen); case 2: - return asymmetric_verify(keyring, sig, siglen, digest, - digestlen); + return asymmetric_verify(keyring, domain_tag, sig, siglen, + digest, digestlen); }
return -EOPNOTSUPP; @@ -102,7 +129,8 @@ static int __init __integrity_init_keyring(const unsigned int id,
keyring[id] = keyring_alloc(keyring_name[id], KUIDT_INIT(0), KGIDT_INIT(0), cred, perm, - KEY_ALLOC_NOT_IN_QUOTA, restriction, NULL); + keyring_alloc_flags[id], + restriction, NULL); if (IS_ERR(keyring[id])) { err = PTR_ERR(keyring[id]); pr_info("Can't allocate %s keyring (%d)\n", @@ -154,7 +182,7 @@ int __init integrity_add_key(const unsigned int id, const void *data,
key = key_create_or_update(make_key_ref(keyring[id], 1), "asymmetric", NULL, data, size, perm, - KEY_ALLOC_NOT_IN_QUOTA); + keyring_alloc_flags[id]); if (IS_ERR(key)) { rc = PTR_ERR(key); pr_err("Problem loading X.509 certificate %d\n", rc); diff --git a/security/integrity/digsig_asymmetric.c b/security/integrity/digsig_asymmetric.c index 92dc64755e53..8f4cffd73602 100644 --- a/security/integrity/digsig_asymmetric.c +++ b/security/integrity/digsig_asymmetric.c @@ -20,7 +20,9 @@ /* * Request an asymmetric key. */ -static struct key *request_asymmetric_key(struct key *keyring, uint32_t keyid) +static struct key *request_asymmetric_key(struct key *keyring, + struct key_tag *domain_tag, + uint32_t keyid) { struct key *key; char name[12]; @@ -45,14 +47,16 @@ static struct key *request_asymmetric_key(struct key *keyring, uint32_t keyid) /* search in specific keyring */ key_ref_t kref;
- kref = keyring_search(make_key_ref(keyring, 1), - &key_type_asymmetric, name, true); + kref = keyring_search_tag(make_key_ref(keyring, 1), + &key_type_asymmetric, name, + domain_tag, true); if (IS_ERR(kref)) key = ERR_CAST(kref); else key = key_ref_to_ptr(kref); } else { - key = request_key(&key_type_asymmetric, name, NULL); + key = request_key_tag(&key_type_asymmetric, + name, domain_tag, NULL); }
if (IS_ERR(key)) { @@ -89,8 +93,9 @@ static struct key *request_asymmetric_key(struct key *keyring, uint32_t keyid) return key; }
-int asymmetric_verify(struct key *keyring, const char *sig, - int siglen, const char *data, int datalen) +int asymmetric_verify(struct key *keyring, struct key_tag *domain_tag, + const char *sig, int siglen, + const char *data, int datalen) { struct public_key_signature pks; struct signature_v2_hdr *hdr = (struct signature_v2_hdr *)sig; @@ -108,7 +113,8 @@ int asymmetric_verify(struct key *keyring, const char *sig, if (hdr->hash_algo >= HASH_ALGO__LAST) return -ENOPKG;
- key = request_asymmetric_key(keyring, be32_to_cpu(hdr->keyid)); + key = request_asymmetric_key(keyring, domain_tag, + be32_to_cpu(hdr->keyid)); if (IS_ERR(key)) return PTR_ERR(key);
diff --git a/security/integrity/integrity.h b/security/integrity/integrity.h index 78078d55fd33..423e61627716 100644 --- a/security/integrity/integrity.h +++ b/security/integrity/integrity.h @@ -256,11 +256,14 @@ static inline int __init integrity_load_cert(const unsigned int id, #endif /* CONFIG_INTEGRITY_SIGNATURE */
#ifdef CONFIG_INTEGRITY_ASYMMETRIC_KEYS -int asymmetric_verify(struct key *keyring, const char *sig, - int siglen, const char *data, int datalen); +int asymmetric_verify(struct key *keyring, struct key_tag *domain_tag, + const char *sig, int siglen, + const char *data, int datalen); #else -static inline int asymmetric_verify(struct key *keyring, const char *sig, - int siglen, const char *data, int datalen) +static inline int asymmetric_verify(struct key *keyring, + struct key_tag *domain_tag, + const char *sig, int siglen, + const char *data, int datalen) { return -EOPNOTSUPP; }
From: Krzysztof Struczynski krzysztof.struczynski@huawei.com
hulk inclusion category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/I49KW1 CVE: NA
--------------------------------
If configured, load the x509 certificate when the first process is born into the new ima namespace. User can set the path to the certificate by writing to the x509_for_children entry in the ima securityfs. The certificate may be appraised in the parent ima namespace, in that case it may need to be signed with the parent ns' key. Appraisal of the key in the newly created namespace is disabled as for the original ima.
Signed-off-by: Krzysztof Struczynski krzysztof.struczynski@huawei.com Reviewed-by: Zhang Tianxing zhangtianxing3@huawei.com Signed-off-by: Zheng Zengkai zhengzengkai@huawei.com --- include/linux/ima.h | 1 + security/integrity/digsig.c | 6 +-- security/integrity/ima/ima_main.c | 12 ++++-- security/integrity/ima/ima_ns.c | 65 ++++++++++++++++++++++++------- security/integrity/integrity.h | 2 +- 5 files changed, 65 insertions(+), 21 deletions(-)
diff --git a/include/linux/ima.h b/include/linux/ima.h index eb022229c694..f011c6e6aa86 100644 --- a/include/linux/ima.h +++ b/include/linux/ima.h @@ -240,6 +240,7 @@ struct ima_namespace { #ifdef CONFIG_KEYS struct key_tag *key_domain; #endif + struct task_struct *activating_tsk; /* used only for the ns activation */ } __randomize_layout;
extern struct ima_namespace init_ima_ns; diff --git a/security/integrity/digsig.c b/security/integrity/digsig.c index 4d2579a2c5ea..2c923dc0dbd3 100644 --- a/security/integrity/digsig.c +++ b/security/integrity/digsig.c @@ -171,8 +171,8 @@ int __init integrity_init_keyring(const unsigned int id) return __integrity_init_keyring(id, perm, restriction); }
-int __init integrity_add_key(const unsigned int id, const void *data, - off_t size, key_perm_t perm) +int integrity_add_key(const unsigned int id, const void *data, + off_t size, key_perm_t perm) { key_ref_t key; int rc = 0; @@ -196,7 +196,7 @@ int __init integrity_add_key(const unsigned int id, const void *data,
}
-int __init integrity_load_x509(const unsigned int id, const char *path) +int integrity_load_x509(const unsigned int id, const char *path) { void *data = NULL; size_t size; diff --git a/security/integrity/ima/ima_main.c b/security/integrity/ima/ima_main.c index 663552073997..84826e4c844d 100644 --- a/security/integrity/ima/ima_main.c +++ b/security/integrity/ima/ima_main.c @@ -364,9 +364,12 @@ static int process_ns_measurement(struct file *file, const struct cred *cred, return 0;
if (ima_ns != current_ima_ns) { - iint = integrity_iint_rb_find(ima_ns->iint_tree, inode); - if (!iint) - return 0; + if (!(ima_ns->activating_tsk && (ima_ns->activating_tsk == + current))) { + iint = integrity_iint_rb_find(ima_ns->iint_tree, inode); + if (!iint) + return 0; + } }
/* Return an IMA_MEASURE, IMA_APPRAISE, IMA_AUDIT action @@ -411,7 +414,8 @@ static int process_ns_measurement(struct file *file, const struct cred *cred, goto out; if (!action) goto out; - if (ima_ns != current_ima_ns) + if ((ima_ns != current_ima_ns) && + !(ima_ns->activating_tsk && (ima_ns->activating_tsk == current))) goto out;
mutex_lock(&iint->mutex); diff --git a/security/integrity/ima/ima_ns.c b/security/integrity/ima/ima_ns.c index 639e5eadee31..b04f35f35f85 100644 --- a/security/integrity/ima/ima_ns.c +++ b/security/integrity/ima/ima_ns.c @@ -49,6 +49,30 @@ static void dec_ima_namespaces(struct ucounts *ucounts) return dec_ucount(ucounts, UCOUNT_IMA_NAMESPACES); }
+#ifdef CONFIG_IMA_LOAD_X509 +static int ima_ns_load_x509(struct ima_namespace *ima_ns) +{ + int res = 0; + int unset_flags = + ima_ns->policy_data->ima_policy_flag & IMA_APPRAISE; + + if (!ima_ns->x509_path_for_children) + return res; + + ima_ns->policy_data->ima_policy_flag &= ~unset_flags; + res = integrity_load_x509(INTEGRITY_KEYRING_IMA, + ima_ns->x509_path_for_children); + ima_ns->policy_data->ima_policy_flag |= unset_flags; + + return res; +} +#else +static inline int ima_ns_load_x509(struct ima_namespace *ima_ns) +{ + return 0; +} +#endif + static struct ima_namespace *ima_ns_alloc(void) { struct ima_namespace *ima_ns; @@ -361,6 +385,22 @@ static int imans_activate(struct ima_namespace *ima_ns) list_add_tail(&ima_ns->list, &ima_ns_list); up_write(&ima_ns_list_lock);
+ /* The x509 certificate has to be measured in the new namespace as + * well as in the parent namespace, therefore it has to be loaded + * after adding the namespace to the list of active namespaces. If + * defined in the policy, the parent IMA ns can also appraise the + * certificate, appraisal is disabled only in the new namespace. If + * loading the certificate fails, print a warning but don't return an + * error - there is no way to handle it well at this point, in + * the worst case, user will end up with a failed appraisal */ + ima_ns->activating_tsk = current; + res = ima_ns_load_x509(ima_ns); + ima_ns->activating_tsk = NULL; + if (res < 0) { + pr_err("IMA ns x509 cert. loading failed, appraisal will fail\n"); + res = 0; + } + destroy_child_config(ima_ns); out: mutex_unlock(&frozen_lock); @@ -370,9 +410,10 @@ static int imans_activate(struct ima_namespace *ima_ns)
static int imans_install(struct nsset *nsset, struct ns_common *new) { - int res; + int res = 0; struct nsproxy *nsproxy = nsset->nsproxy; struct ima_namespace *ns = to_ima_ns(new); + struct ima_namespace *old_ns = nsproxy->ima_ns;
if (!current_is_single_threaded()) return -EUSERS; @@ -381,19 +422,20 @@ 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); + put_ima_ns(old_ns); nsproxy->ima_ns = ns;
get_ima_ns(ns); put_ima_ns(nsproxy->ima_ns_for_children); nsproxy->ima_ns_for_children = ns;
- return res; + if (!ns->frozen && (ns->user_ns != nsset->cred->user_ns)) { + res = ima_swap_user_ns(ns, nsset->cred->user_ns); + if (res) + return res; + } + return imans_activate(ns); }
int imans_on_fork(struct nsproxy *nsproxy, struct task_struct *tsk, @@ -401,6 +443,7 @@ int imans_on_fork(struct nsproxy *nsproxy, struct task_struct *tsk, { int res; struct ima_namespace *ima_ns = nsproxy->ima_ns_for_children; + struct ima_namespace *old_ima_ns = nsproxy->ima_ns;
/* create_new_namespaces() already incremented the ref counter */ if (nsproxy->ima_ns == ima_ns) @@ -416,15 +459,11 @@ int imans_on_fork(struct nsproxy *nsproxy, struct task_struct *tsk, return res; }
- res = imans_activate(ima_ns); - if (res) - return res; - get_ima_ns(ima_ns); - put_ima_ns(nsproxy->ima_ns); + put_ima_ns(old_ima_ns); nsproxy->ima_ns = ima_ns;
- return res; + return imans_activate(ima_ns); }
static struct user_namespace *imans_owner(struct ns_common *ns) diff --git a/security/integrity/integrity.h b/security/integrity/integrity.h index 423e61627716..e00adc23f3e9 100644 --- a/security/integrity/integrity.h +++ b/security/integrity/integrity.h @@ -223,7 +223,7 @@ int integrity_digsig_verify(const unsigned int id, const char *sig, int siglen, int integrity_modsig_verify(unsigned int id, const struct modsig *modsig);
int __init integrity_init_keyring(const unsigned int id); -int __init integrity_load_x509(const unsigned int id, const char *path); +int integrity_load_x509(const unsigned int id, const char *path); int __init integrity_load_cert(const unsigned int id, const char *source, const void *data, size_t len, key_perm_t perm); #else
From: Krzysztof Struczynski krzysztof.struczynski@huawei.com
hulk inclusion category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/I49KW1 CVE: NA
--------------------------------
Add dummy boot aggregate entry to the ima measurement list, for every new ima namespace, when the first process is born into that namespace.
There is at most one TPM chip in the system and one measurement list associated to one of its PCRs. IMA namespace IDs can be re-used after namespace is destroyed. The per namespace boot aggregate entry marks the moment of the ima namespace creation. It is useful when host's root parses the global measurement list to find entries for destroyed containers. If the ima namespace ID is reused, the user will know, that the given entry belongs to a different container.
Signed-off-by: Krzysztof Struczynski krzysztof.struczynski@huawei.com Reviewed-by: Zhang Tianxing zhangtianxing3@huawei.com Signed-off-by: Zheng Zengkai zhengzengkai@huawei.com --- security/integrity/ima/ima_ns.c | 62 +++++++++++++++++++++++++++++++++ 1 file changed, 62 insertions(+)
diff --git a/security/integrity/ima/ima_ns.c b/security/integrity/ima/ima_ns.c index b04f35f35f85..29a1cbdac40d 100644 --- a/security/integrity/ima/ima_ns.c +++ b/security/integrity/ima/ima_ns.c @@ -49,6 +49,66 @@ static void dec_ima_namespaces(struct ucounts *ucounts) return dec_ucount(ucounts, UCOUNT_IMA_NAMESPACES); }
+static int ima_ns_add_boot_aggregate(struct ima_namespace *ima_ns) +{ + static const char op[] = "ns_add_boot_aggregate"; + static const char ns_aggregate_name_prefix[] = "ns_aggregate_"; + const char *audit_cause = "ENOMEM"; + struct ima_template_entry *entry; + struct integrity_iint_cache tmp_iint, *iint = &tmp_iint; + struct ima_event_data event_data = { .iint = iint }; + int result = -ENOMEM; + int violation = 0; + struct { + struct ima_digest_data hdr; + char digest[TPM_DIGEST_SIZE]; + } hash; + unsigned int ns_id = get_ns_id(ima_ns); + char *ns_aggregate_name; + + ns_aggregate_name = kmalloc(sizeof(ns_aggregate_name_prefix) + + sizeof(unsigned int), + GFP_KERNEL); + if (!ns_aggregate_name) + goto err_out; + + sprintf(ns_aggregate_name, "%s%u", ns_aggregate_name_prefix, ns_id); + + event_data.filename = ns_aggregate_name; + event_data.ns_id = ns_id; + + memset(iint, 0, sizeof(*iint)); + memset(&hash, 0, sizeof(hash)); + iint->ima_hash = &hash.hdr; + iint->ima_hash->algo = HASH_ALGO_SHA1; + iint->ima_hash->length = SHA1_DIGEST_SIZE; + + result = ima_alloc_init_template(&event_data, &entry, NULL); + if (result < 0) { + audit_cause = "alloc_entry"; + goto err_out; + } + + result = ima_store_template(entry, violation, NULL, + ns_aggregate_name, + CONFIG_IMA_MEASURE_PCR_IDX, + NULL, + ima_ns); + if (result < 0) { + ima_free_template_entry(entry); + audit_cause = "store_entry"; + } + +err_out: + if (result < 0) + integrity_audit_msg(AUDIT_INTEGRITY_PCR, NULL, + ns_aggregate_name, op, audit_cause, + result, 0); + kfree(ns_aggregate_name); + + return result; +} + #ifdef CONFIG_IMA_LOAD_X509 static int ima_ns_load_x509(struct ima_namespace *ima_ns) { @@ -385,6 +445,8 @@ static int imans_activate(struct ima_namespace *ima_ns) list_add_tail(&ima_ns->list, &ima_ns_list); up_write(&ima_ns_list_lock);
+ ima_ns_add_boot_aggregate(ima_ns); + /* The x509 certificate has to be measured in the new namespace as * well as in the parent namespace, therefore it has to be loaded * after adding the namespace to the list of active namespaces. If
From: Krzysztof Struczynski krzysztof.struczynski@huawei.com
hulk inclusion category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/I49KW1 CVE: NA
--------------------------------
Set ML template based on the ima_template string. It can be defined by the user through kcmd_for_children ima securityfs entry. Acceptable values are the same as for the ima_template kernel boot parameter.
Signed-off-by: Krzysztof Struczynski krzysztof.struczynski@huawei.com Reviewed-by: Zhang Tianxing zhangtianxing3@huawei.com Signed-off-by: Zheng Zengkai zhengzengkai@huawei.com --- include/linux/ima.h | 1 + security/integrity/ima/ima.h | 2 ++ security/integrity/ima/ima_init.c | 1 + security/integrity/ima/ima_ns.c | 30 ++++++++++++++++++++- security/integrity/ima/ima_policy.c | 2 +- security/integrity/ima/ima_template.c | 38 ++++++++++++++++++--------- 6 files changed, 59 insertions(+), 15 deletions(-)
diff --git a/include/linux/ima.h b/include/linux/ima.h index f011c6e6aa86..5a2dbd8fab6d 100644 --- a/include/linux/ima.h +++ b/include/linux/ima.h @@ -240,6 +240,7 @@ struct ima_namespace { #ifdef CONFIG_KEYS struct key_tag *key_domain; #endif + struct ima_template_desc *ima_template; struct task_struct *activating_tsk; /* used only for the ns activation */ } __randomize_layout;
diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h index aff8a3c748bf..acdc8ee6e53d 100644 --- a/security/integrity/ima/ima.h +++ b/security/integrity/ima/ima.h @@ -172,8 +172,10 @@ int template_desc_init_fields(const char *template_fmt, const struct ima_template_field ***fields, int *num_fields); struct ima_template_desc *ima_template_desc_current(void); +struct ima_template_desc *ima_template_desc_ns(struct ima_namespace *ima_ns); struct ima_template_desc *lookup_template_desc(const char *name); bool ima_template_has_modsig(const struct ima_template_desc *ima_template); +int ima_template_setup(char *str, struct ima_namespace *ima_ns); int ima_restore_measurement_entry(struct ima_template_entry *entry); int ima_restore_measurement_list(loff_t bufsize, void *buf); int ima_measurements_show(struct seq_file *m, void *v); diff --git a/security/integrity/ima/ima_init.c b/security/integrity/ima/ima_init.c index 73fb0f0e82c5..f4bd3ca98b54 100644 --- a/security/integrity/ima/ima_init.c +++ b/security/integrity/ima/ima_init.c @@ -48,6 +48,7 @@ struct ima_namespace init_ima_ns = { #ifdef CONFIG_KEYS .key_domain = &init_ima_key_domain, #endif + .ima_template = NULL, }; EXPORT_SYMBOL(init_ima_ns);
diff --git a/security/integrity/ima/ima_ns.c b/security/integrity/ima/ima_ns.c index 29a1cbdac40d..97185b8ed52c 100644 --- a/security/integrity/ima/ima_ns.c +++ b/security/integrity/ima/ima_ns.c @@ -49,6 +49,28 @@ static void dec_ima_namespaces(struct ucounts *ucounts) return dec_ucount(ucounts, UCOUNT_IMA_NAMESPACES); }
+static int ima_set_ns_template(struct ima_namespace *ima_ns) +{ + int result; + + if (!ima_ns->ima_template) + ima_ns->ima_template = + lookup_template_desc(CONFIG_IMA_DEFAULT_TEMPLATE); + + result = template_desc_init_fields(ima_ns->ima_template->fmt, + &(ima_ns->ima_template->fields), + &(ima_ns->ima_template->num_fields)); + if (result < 0) { + pr_err("template %s init failed, result: %d\n", + (strlen(ima_ns->ima_template->name) ? + ima_ns->ima_template->name : + ima_ns->ima_template->fmt), result); + ima_ns->ima_template = NULL; + } + + return result; +} + static int ima_ns_add_boot_aggregate(struct ima_namespace *ima_ns) { static const char op[] = "ns_add_boot_aggregate"; @@ -245,6 +267,7 @@ static struct ima_namespace *clone_ima_ns(struct user_namespace *user_ns, #endif ns->x509_path_for_children = NULL; ns->policy_setup_for_children = NULL; + ns->ima_template = NULL;
INIT_LIST_HEAD(&ns->ns_measurements); INIT_LIST_HEAD(&ns->policy_data->ima_default_rules); @@ -437,6 +460,10 @@ static int imans_activate(struct ima_namespace *ima_ns) if (ima_ns->frozen) goto out;
+ res = ima_set_ns_template(ima_ns); + if (res < 0) + goto out; + ima_set_ns_policy(ima_ns);
ima_ns->frozen = true; @@ -556,10 +583,11 @@ struct ima_kernel_param { int (*set)(char *val, struct ima_namespace *ima_ns); };
-/* TODO: add ima_template, ima_template_fmt, ima_hash, ... */ +/* TODO: add ima_template_fmt, ima_hash, ... */ static const struct ima_kernel_param ima_kernel_params[] = { {"ima_appraise", ima_default_appraise_setup}, {"ima_policy", ima_policy_setup}, + {"ima_template", ima_template_setup}, }; static const size_t ima_kernel_params_size = ARRAY_SIZE(ima_kernel_params);
diff --git a/security/integrity/ima/ima_policy.c b/security/integrity/ima/ima_policy.c index 42bafb520e4e..35ec2c8f2069 100644 --- a/security/integrity/ima/ima_policy.c +++ b/security/integrity/ima/ima_policy.c @@ -737,7 +737,7 @@ int ima_match_policy(struct inode *inode, const struct cred *cred, u32 secid, bool fail_unverifiable_sigs;
if (template_desc) - *template_desc = ima_template_desc_current(); + *template_desc = ima_template_desc_ns(ima_ns);
rcu_read_lock(); list_for_each_entry_rcu(entry, ima_ns->policy_data->ima_rules, list) { diff --git a/security/integrity/ima/ima_template.c b/security/integrity/ima/ima_template.c index 3d6f6e9d530c..e72497f37bb5 100644 --- a/security/integrity/ima/ima_template.c +++ b/security/integrity/ima/ima_template.c @@ -59,8 +59,6 @@ static const struct ima_template_field supported_fields[] = { */ #define MAX_TEMPLATE_NAME_LEN sizeof("d-ng|n-ng|sig|buf|d-modisg|modsig|ns")
-static struct ima_template_desc *ima_template; - /** * ima_template_has_modsig - Check whether template has modsig-related fields. * @ima_template: IMA template to check. @@ -80,12 +78,12 @@ bool ima_template_has_modsig(const struct ima_template_desc *ima_template) return false; }
-static int __init ima_template_setup(char *str) +int ima_template_setup(char *str, struct ima_namespace *ima_ns) { struct ima_template_desc *template_desc; int template_len = strlen(str);
- if (ima_template) + if (ima_ns->ima_template) return 1;
ima_init_template_list(); @@ -111,16 +109,21 @@ static int __init ima_template_setup(char *str) return 1; }
- ima_template = template_desc; + ima_ns->ima_template = template_desc; return 1; } -__setup("ima_template=", ima_template_setup); + +static int __init template_setup(char *str) +{ + return ima_template_setup(str, &init_ima_ns); +} +__setup("ima_template=", template_setup);
static int __init ima_template_fmt_setup(char *str) { int num_templates = ARRAY_SIZE(builtin_templates);
- if (ima_template) + if (init_ima_ns.ima_template) return 1;
if (template_desc_init_fields(str, NULL, NULL) < 0) { @@ -130,7 +133,7 @@ static int __init ima_template_fmt_setup(char *str) }
builtin_templates[num_templates - 1].fmt = str; - ima_template = builtin_templates + num_templates - 1; + init_ima_ns.ima_template = builtin_templates + num_templates - 1;
return 1; } @@ -247,14 +250,23 @@ void ima_init_template_list(void) spin_unlock(&template_list); }
-struct ima_template_desc *ima_template_desc_current(void) +struct ima_template_desc *ima_template_desc_ns(struct ima_namespace *ima_ns) { - if (!ima_template) { + if (!ima_ns) + return NULL; + + if (!ima_ns->ima_template) { ima_init_template_list(); - ima_template = - lookup_template_desc(CONFIG_IMA_DEFAULT_TEMPLATE); + ima_ns->ima_template = + lookup_template_desc(CONFIG_IMA_DEFAULT_TEMPLATE); } - return ima_template; + return ima_ns->ima_template; +} + +struct ima_template_desc *ima_template_desc_current(void) +{ + struct ima_namespace *ima_ns = get_current_ns(); + return ima_template_desc_ns(ima_ns); }
int __init ima_init_template(void)
From: Krzysztof Struczynski krzysztof.struczynski@huawei.com
hulk inclusion category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/I49KW1 CVE: NA
--------------------------------
The build_appraise_rules may be a zero length array depends on the kernel configuration. This is (vaguely) forbidden in a standard and leads to the compiler optimisation where the address of the build_appraise_rules is the same as the default_appraise_rules. That leads to the unexpected flow in add_rules() where condition: if (entries != build_appraise_rules) is false for entries equal to default_appraise_rules.
Signed-off-by: Krzysztof Struczynski krzysztof.struczynski@huawei.com Reviewed-by: Zhang Tianxing zhangtianxing3@huawei.com Signed-off-by: Zheng Zengkai zhengzengkai@huawei.com --- security/integrity/ima/ima_policy.c | 7 +++++++ 1 file changed, 7 insertions(+)
diff --git a/security/integrity/ima/ima_policy.c b/security/integrity/ima/ima_policy.c index 35ec2c8f2069..8b7ca11e82f7 100644 --- a/security/integrity/ima/ima_policy.c +++ b/security/integrity/ima/ima_policy.c @@ -208,6 +208,13 @@ static struct ima_rule_entry build_appraise_rules[] __ro_after_init = { {.action = APPRAISE, .func = POLICY_CHECK, .flags = IMA_FUNC | IMA_DIGSIG_REQUIRED}, #endif +#if !defined(CONFIG_IMA_APPRAISE_REQUIRE_MODULE_SIGS) && \ + !defined(CONFIG_IMA_APPRAISE_REQUIRE_FIRMWARE_SIGS) && \ + !defined(CONFIG_IMA_APPRAISE_REQUIRE_KEXEC_SIGS) && \ + !defined(CONFIG_IMA_APPRAISE_REQUIRE_POLICY_SIGS) + /* Add a member to avoid a zero length array */ + {.action = UNKNOWN, .func = NONE, .flags = 0}, +#endif };
static struct ima_rule_entry secure_boot_rules[] __ro_after_init = {
From: Krzysztof Struczynski krzysztof.struczynski@huawei.com
hulk inclusion category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/I49KW1 CVE: NA
--------------------------------
Introduce ima-ns-sig template: "d-ng|n-ng|ns|sig"
Signed-off-by: Krzysztof Struczynski krzysztof.struczynski@huawei.com Reviewed-by: Zhang Tianxing zhangtianxing3@huawei.com Signed-off-by: Zheng Zengkai zhengzengkai@huawei.com --- security/integrity/ima/ima_template.c | 1 + 1 file changed, 1 insertion(+)
diff --git a/security/integrity/ima/ima_template.c b/security/integrity/ima/ima_template.c index e72497f37bb5..5cd4ccd0d240 100644 --- a/security/integrity/ima/ima_template.c +++ b/security/integrity/ima/ima_template.c @@ -23,6 +23,7 @@ static struct ima_template_desc builtin_templates[] = { {.name = "ima-buf", .fmt = "d-ng|n-ng|buf"}, {.name = "ima-modsig", .fmt = "d-ng|n-ng|sig|d-modsig|modsig"}, {.name = "ima-ns", .fmt = "d-ng|n-ng|ns"}, + {.name = "ima-ns-sig", .fmt = "d-ng|n-ng|ns|sig"}, {.name = "", .fmt = ""}, /* placeholder for a custom format */ };
From: Krzysztof Struczynski krzysztof.struczynski@huawei.com
hulk inclusion category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/I49KW1 CVE: NA
--------------------------------
Disable digest list checks in non-root ima namespaces. Digest lists can be re-enabled when they are namespaced.
Signed-off-by: Krzysztof Struczynski krzysztof.struczynski@huawei.com Reviewed-by: Zhang Tianxing zhangtianxing3@huawei.com Signed-off-by: Zheng Zengkai zhengzengkai@huawei.com --- security/integrity/ima/ima_digest_list.c | 12 ++++++++++++ security/integrity/ima/ima_fs.c | 11 ++++++++++- 2 files changed, 22 insertions(+), 1 deletion(-)
diff --git a/security/integrity/ima/ima_digest_list.c b/security/integrity/ima/ima_digest_list.c index 74b08c3a3c81..3ffaffe8d064 100644 --- a/security/integrity/ima/ima_digest_list.c +++ b/security/integrity/ima/ima_digest_list.c @@ -89,6 +89,9 @@ struct ima_digest *ima_lookup_digest(u8 *digest, enum hash_algo algo, int digest_len = hash_digest_size[algo]; unsigned int key = ima_hash_key(digest);
+ if (&init_ima_ns != get_current_ns()) + return NULL; + rcu_read_lock(); hlist_for_each_entry_rcu(d, &ima_digests_htable.queue[key], hnext) if (d->algo == algo && d->type == type && @@ -167,6 +170,9 @@ int ima_parse_compact_list(loff_t size, void *buf, int op) size_t digest_len; int ret = 0, i;
+ if (&init_ima_ns != get_current_ns()) + return -EACCES; + if (!(ima_digest_list_actions & init_policy_data.ima_policy_flag)) return -EACCES;
@@ -239,6 +245,9 @@ void ima_check_measured_appraised(struct file *file) { struct integrity_iint_cache *iint;
+ if (&init_ima_ns != get_current_ns()) + return; + if (!ima_digest_list_actions) return;
@@ -275,6 +284,9 @@ void ima_check_measured_appraised(struct file *file)
struct ima_digest *ima_digest_allow(struct ima_digest *digest, int action) { + if (&init_ima_ns != get_current_ns()) + return NULL; + if (!(ima_digest_list_actions & action)) return NULL;
diff --git a/security/integrity/ima/ima_fs.c b/security/integrity/ima/ima_fs.c index 6738cfc5a3dd..dd0b0f431a23 100644 --- a/security/integrity/ima/ima_fs.c +++ b/security/integrity/ima/ima_fs.c @@ -60,11 +60,17 @@ static int valid_policy = 1;
static int ima_open_simple(struct inode *inode, struct file *file) { + struct dentry *dentry = file_dentry(file); struct ima_namespace *ima_ns = get_current_ns();
if (!ns_capable(ima_ns->user_ns, CAP_SYS_ADMIN)) return -EPERM;
+ if (dentry == digests_count) { + if (&init_ima_ns != get_current_ns()) + return -EACCES; + } + return 0; }
@@ -552,9 +558,12 @@ static int ima_open_data_upload(struct inode *inode, struct file *filp) if (test_and_set_bit(flag, &ima_fs_flags)) return -EBUSY;
- if (dentry == digest_list_data || dentry == digest_list_data_del) + if (dentry == digest_list_data || dentry == digest_list_data_del) { + if (&init_ima_ns != get_current_ns()) + return -EACCES; if (ima_check_current_is_parser()) ima_set_parser(); + }
return 0; }
From: Colin Ian King colin.king@canonical.com
mainline inclusion from mainline-master commit 0bc277cb8234f8e6fcf8842e318e0873dd223565 category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/I46N6O CVE: NA
Reference: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?i...
----------------------------------------------------------------------
Don't populate the array spec_opcode on the stack but instead it static const. Makes the object code smaller by 158 bytes:
Before: text data bss dec hex filename 12271 3976 128 16375 3ff7 .../hisilicon/hns3/hns3pf/hclge_cmd.o
After: text data bss dec hex filename 12017 4072 128 16217 3f59 .../hisilicon/hns3/hns3pf/hclge_cmd.o
(gcc version 10.3.0)
Signed-off-by: Colin Ian King colin.king@canonical.com Signed-off-by: David S. Miller davem@davemloft.net Reviewed-by: Yongxin Li liyongxin1@huawei.com Signed-off-by: Junxin Chen chenjunxin1@huawei.com Signed-off-by: Zheng Zengkai zhengzengkai@huawei.com --- .../hisilicon/hns3/hns3pf/hclge_cmd.c | 24 ++++++++++--------- 1 file changed, 13 insertions(+), 11 deletions(-)
diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_cmd.c b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_cmd.c index 887297e37cf3..13042f1cac6f 100644 --- a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_cmd.c +++ b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_cmd.c @@ -169,17 +169,19 @@ static bool hclge_is_special_opcode(u16 opcode) /* these commands have several descriptors, * and use the first one to save opcode and return value */ - u16 spec_opcode[] = {HCLGE_OPC_STATS_64_BIT, - HCLGE_OPC_STATS_32_BIT, - HCLGE_OPC_STATS_MAC, - HCLGE_OPC_STATS_MAC_ALL, - HCLGE_OPC_QUERY_32_BIT_REG, - HCLGE_OPC_QUERY_64_BIT_REG, - HCLGE_QUERY_CLEAR_MPF_RAS_INT, - HCLGE_QUERY_CLEAR_PF_RAS_INT, - HCLGE_QUERY_CLEAR_ALL_MPF_MSIX_INT, - HCLGE_QUERY_CLEAR_ALL_PF_MSIX_INT, - HCLGE_QUERY_ALL_ERR_INFO}; + static const u16 spec_opcode[] = { + HCLGE_OPC_STATS_64_BIT, + HCLGE_OPC_STATS_32_BIT, + HCLGE_OPC_STATS_MAC, + HCLGE_OPC_STATS_MAC_ALL, + HCLGE_OPC_QUERY_32_BIT_REG, + HCLGE_OPC_QUERY_64_BIT_REG, + HCLGE_QUERY_CLEAR_MPF_RAS_INT, + HCLGE_QUERY_CLEAR_PF_RAS_INT, + HCLGE_QUERY_CLEAR_ALL_MPF_MSIX_INT, + HCLGE_QUERY_CLEAR_ALL_PF_MSIX_INT, + HCLGE_QUERY_ALL_ERR_INFO + }; int i;
for (i = 0; i < ARRAY_SIZE(spec_opcode); i++) {
From: Yufeng Mo moyufeng@huawei.com
mainline inclusion from mainline-v5.14 commit 1a6d281946c330cee2855f6d0cd796616e54601f category: bugfix bugzilla: https://gitee.com/openeuler/kernel/issues/I46N6O CVE: NA
Reference: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?i...
----------------------------------------------------------------------
If a PF is bonded to a virtual machine and the virtual machine exits unexpectedly, some hardware resource cannot be cleared. In this case, loading driver may cause exceptions. Therefore, the hardware resource needs to be cleared when the driver is loaded.
Fixes: 46a3df9f9718 ("net: hns3: Add HNS3 Acceleration Engine & Compatibility Layer Support") Signed-off-by: Yufeng Mo moyufeng@huawei.com Signed-off-by: Salil Mehta salil.mehta@huawei.com Signed-off-by: Guangbin Huang huangguangbin2@huawei.com Signed-off-by: Jakub Kicinski kuba@kernel.org Reviewed-by: Yongxin Li liyongxin1@huawei.com Signed-off-by: Junxin Chen chenjunxin1@huawei.com Signed-off-by: Zheng Zengkai zhengzengkai@huawei.com --- .../hisilicon/hns3/hns3pf/hclge_cmd.h | 3 +++ .../hisilicon/hns3/hns3pf/hclge_main.c | 26 +++++++++++++++++++ 2 files changed, 29 insertions(+)
diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_cmd.h b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_cmd.h index 8e5be127909b..7114014a6ad6 100644 --- a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_cmd.h +++ b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_cmd.h @@ -270,6 +270,9 @@ enum hclge_opcode_type { /* Led command */ HCLGE_OPC_LED_STATUS_CFG = 0xB000,
+ /* clear hardware resource command */ + HCLGE_OPC_CLEAR_HW_RESOURCE = 0x700B, + /* NCL config command */ HCLGE_OPC_QUERY_NCL_CONFIG = 0x7011,
diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c index 8779a63d51b3..70b39d1fb851 100644 --- a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c +++ b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c @@ -11450,6 +11450,28 @@ static void hclge_clear_resetting_state(struct hclge_dev *hdev) } }
+static int hclge_clear_hw_resource(struct hclge_dev *hdev) +{ + struct hclge_desc desc; + int ret; + + hclge_cmd_setup_basic_desc(&desc, HCLGE_OPC_CLEAR_HW_RESOURCE, false); + + ret = hclge_cmd_send(&hdev->hw, &desc, 1); + /* This new command is only supported by new firmware, it will + * fail with older firmware. Error value -EOPNOSUPP can only be + * returned by older firmware running this command, to keep code + * backward compatible we will override this value and return + * success. + */ + if (ret && ret != -EOPNOTSUPP) { + dev_err(&hdev->pdev->dev, + "failed to clear hw resource, ret = %d\n", ret); + return ret; + } + return 0; +} + static void hclge_init_rxd_adv_layout(struct hclge_dev *hdev) { if (hnae3_ae_dev_rxd_adv_layout_supported(hdev->ae_dev)) @@ -11503,6 +11525,10 @@ static int hclge_init_ae_dev(struct hnae3_ae_dev *ae_dev) if (ret) goto err_cmd_uninit;
+ ret = hclge_clear_hw_resource(hdev); + if (ret) + goto err_cmd_uninit; + ret = hclge_get_cap(hdev); if (ret) goto err_cmd_uninit;
From: Yufeng Mo moyufeng@huawei.com
mainline inclusion from mainline-v5.14 commit a96d9330b02a3d051ae689bc2c5e7d3a2ba25594 category: bugfix bugzilla: https://gitee.com/openeuler/kernel/issues/I46N6O CVE: NA
Reference: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?i...
----------------------------------------------------------------------
After the cmdq registers are cleared, the firmware may take time to clear out possible left over commands in the cmdq. Driver must release cmdq memory only after firmware has completed processing of left over commands.
Fixes: 232d0d55fca6 ("net: hns3: uninitialize command queue while unloading PF driver") Signed-off-by: Yufeng Mo moyufeng@huawei.com Signed-off-by: Guangbin Huang huangguangbin2@huawei.com Signed-off-by: Jakub Kicinski kuba@kernel.org Reviewed-by: Yongxin Li liyongxin1@huawei.com Signed-off-by: Junxin Chen chenjunxin1@huawei.com Signed-off-by: Zheng Zengkai zhengzengkai@huawei.com --- drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_cmd.c | 6 +++++- drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_cmd.h | 1 + drivers/net/ethernet/hisilicon/hns3/hns3vf/hclgevf_cmd.c | 7 ++++++- drivers/net/ethernet/hisilicon/hns3/hns3vf/hclgevf_cmd.h | 1 + 4 files changed, 13 insertions(+), 2 deletions(-)
diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_cmd.c b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_cmd.c index 13042f1cac6f..444c46241afc 100644 --- a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_cmd.c +++ b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_cmd.c @@ -575,9 +575,13 @@ static void hclge_cmd_uninit_regs(struct hclge_hw *hw)
void hclge_cmd_uninit(struct hclge_dev *hdev) { + set_bit(HCLGE_STATE_CMD_DISABLE, &hdev->state); + /* wait to ensure that the firmware completes the possible left + * over commands. + */ + msleep(HCLGE_CMDQ_CLEAR_WAIT_TIME); spin_lock_bh(&hdev->hw.cmq.csq.lock); spin_lock(&hdev->hw.cmq.crq.lock); - set_bit(HCLGE_STATE_CMD_DISABLE, &hdev->state); hclge_cmd_uninit_regs(&hdev->hw); spin_unlock(&hdev->hw.cmq.crq.lock); spin_unlock_bh(&hdev->hw.cmq.csq.lock); diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_cmd.h b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_cmd.h index 7114014a6ad6..53872c7b2940 100644 --- a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_cmd.h +++ b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_cmd.h @@ -9,6 +9,7 @@ #include "hnae3.h"
#define HCLGE_CMDQ_TX_TIMEOUT 30000 +#define HCLGE_CMDQ_CLEAR_WAIT_TIME 200 #define HCLGE_DESC_DATA_LEN 6
struct hclge_dev; diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3vf/hclgevf_cmd.c b/drivers/net/ethernet/hisilicon/hns3/hns3vf/hclgevf_cmd.c index bd19a2d89f6c..d9ddb0a243d4 100644 --- a/drivers/net/ethernet/hisilicon/hns3/hns3vf/hclgevf_cmd.c +++ b/drivers/net/ethernet/hisilicon/hns3/hns3vf/hclgevf_cmd.c @@ -507,12 +507,17 @@ static void hclgevf_cmd_uninit_regs(struct hclgevf_hw *hw)
void hclgevf_cmd_uninit(struct hclgevf_dev *hdev) { + set_bit(HCLGEVF_STATE_CMD_DISABLE, &hdev->state); + /* wait to ensure that the firmware completes the possible left + * over commands. + */ + msleep(HCLGEVF_CMDQ_CLEAR_WAIT_TIME); spin_lock_bh(&hdev->hw.cmq.csq.lock); spin_lock(&hdev->hw.cmq.crq.lock); - set_bit(HCLGEVF_STATE_CMD_DISABLE, &hdev->state); hclgevf_cmd_uninit_regs(&hdev->hw); spin_unlock(&hdev->hw.cmq.crq.lock); spin_unlock_bh(&hdev->hw.cmq.csq.lock); + hclgevf_free_cmd_desc(&hdev->hw.cmq.csq); hclgevf_free_cmd_desc(&hdev->hw.cmq.crq); } diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3vf/hclgevf_cmd.h b/drivers/net/ethernet/hisilicon/hns3/hns3vf/hclgevf_cmd.h index 202feb70dba5..5b82177f98b4 100644 --- a/drivers/net/ethernet/hisilicon/hns3/hns3vf/hclgevf_cmd.h +++ b/drivers/net/ethernet/hisilicon/hns3/hns3vf/hclgevf_cmd.h @@ -8,6 +8,7 @@ #include "hnae3.h"
#define HCLGEVF_CMDQ_TX_TIMEOUT 30000 +#define HCLGEVF_CMDQ_CLEAR_WAIT_TIME 200 #define HCLGEVF_CMDQ_RX_INVLD_B 0 #define HCLGEVF_CMDQ_RX_OUTVLD_B 1
From: Yonglong Liu liuyonglong@huawei.com
mainline inclusion from mainline-v5.14 commit b15c072a9f4a404c09ad589477f4389034742a8b category: bugfix bugzilla: https://gitee.com/openeuler/kernel/issues/I46N6O CVE: NA
Reference: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?i...
----------------------------------------------------------------------
In bond 4, when the link goes down and up repeatedly, the bond may get an unknown speed, and then this port can not work.
The driver notify netif_carrier_on() before update the link state, when the bond receive carrier on, will query the speed of the port, if the query operation happens before updating the link state, will get an unknown speed. So need to notify netif_carrier_on() after update the link state.
Fixes: 46a3df9f9718 ("net: hns3: Add HNS3 Acceleration Engine & Compatibility Layer Support") Fixes: e2cb1dec9779 ("net: hns3: Add HNS3 VF HCL(Hardware Compatibility Layer) Support") Signed-off-by: Yonglong Liu liuyonglong@huawei.com Signed-off-by: Guangbin Huang huangguangbin2@huawei.com Signed-off-by: Jakub Kicinski kuba@kernel.org Reviewed-by: Yongxin Li liyongxin1@huawei.com Signed-off-by: Junxin Chen chenjunxin1@huawei.com Signed-off-by: Zheng Zengkai zhengzengkai@huawei.com --- drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c | 2 +- drivers/net/ethernet/hisilicon/hns3/hns3vf/hclgevf_main.c | 2 +- drivers/net/ethernet/hisilicon/hns3/hns3vf/hclgevf_mbx.c | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c index 70b39d1fb851..7ac7aa745881 100644 --- a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c +++ b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c @@ -2953,12 +2953,12 @@ static void hclge_update_link_status(struct hclge_dev *hdev) }
if (state != hdev->hw.mac.link) { + hdev->hw.mac.link = state; client->ops->link_status_change(handle, state); hclge_config_mac_tnl_int(hdev, state); if (rclient && rclient->ops->link_status_change) rclient->ops->link_status_change(rhandle, state);
- hdev->hw.mac.link = state; hclge_push_link_status(hdev); }
diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3vf/hclgevf_main.c b/drivers/net/ethernet/hisilicon/hns3/hns3vf/hclgevf_main.c index 3a19f08bfff3..0b0f9f1989f7 100644 --- a/drivers/net/ethernet/hisilicon/hns3/hns3vf/hclgevf_main.c +++ b/drivers/net/ethernet/hisilicon/hns3/hns3vf/hclgevf_main.c @@ -507,10 +507,10 @@ void hclgevf_update_link_status(struct hclgevf_dev *hdev, int link_state) link_state = test_bit(HCLGEVF_STATE_DOWN, &hdev->state) ? 0 : link_state; if (link_state != hdev->hw.mac.link) { + hdev->hw.mac.link = link_state; client->ops->link_status_change(handle, !!link_state); if (rclient && rclient->ops->link_status_change) rclient->ops->link_status_change(rhandle, !!link_state); - hdev->hw.mac.link = link_state; }
clear_bit(HCLGEVF_STATE_LINK_UPDATING, &hdev->state); diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3vf/hclgevf_mbx.c b/drivers/net/ethernet/hisilicon/hns3/hns3vf/hclgevf_mbx.c index 772b2f8acd2e..b339b9bc0625 100644 --- a/drivers/net/ethernet/hisilicon/hns3/hns3vf/hclgevf_mbx.c +++ b/drivers/net/ethernet/hisilicon/hns3/hns3vf/hclgevf_mbx.c @@ -323,8 +323,8 @@ void hclgevf_mbx_async_handler(struct hclgevf_dev *hdev) flag = (u8)msg_q[5];
/* update upper layer with new link link status */ - hclgevf_update_link_status(hdev, link_status); hclgevf_update_speed_duplex(hdev, speed, duplex); + hclgevf_update_link_status(hdev, link_status);
if (flag & HCLGE_MBX_PUSH_LINK_STATUS_EN) set_bit(HCLGEVF_STATE_PF_PUSH_LINK_STATUS,
From: Guojia Liao liaoguojia@huawei.com
mainline inclusion from mainline-v5.14 commit 94391fae82f71c98ecc7716a32611fcca73c74eb category: bugfix bugzilla: https://gitee.com/openeuler/kernel/issues/I46N6O CVE: NA
Reference: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?i...
----------------------------------------------------------------------
VLAN list should not be added duplicate VLAN node, otherwise it would cause "add failed" when restore VLAN from VLAN list, so this patch adds VLAN ID check before adding node into VLAN list.
Fixes: c6075b193462 ("net: hns3: Record VF vlan tables") Signed-off-by: Guojia Liao liaoguojia@huawei.com Signed-off-by: Guangbin Huang huangguangbin2@huawei.com Signed-off-by: Jakub Kicinski kuba@kernel.org Reviewed-by: Yongxin Li liyongxin1@huawei.com Signed-off-by: Junxin Chen chenjunxin1@huawei.com Signed-off-by: Zheng Zengkai zhengzengkai@huawei.com --- drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-)
diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c index 7ac7aa745881..1d5eee48b23b 100644 --- a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c +++ b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c @@ -10080,7 +10080,11 @@ static int hclge_init_vlan_config(struct hclge_dev *hdev) static void hclge_add_vport_vlan_table(struct hclge_vport *vport, u16 vlan_id, bool writen_to_tbl) { - struct hclge_vport_vlan_cfg *vlan; + struct hclge_vport_vlan_cfg *vlan, *tmp; + + list_for_each_entry_safe(vlan, tmp, &vport->vlan_list, node) + if (vlan->vlan_id == vlan_id) + return;
vlan = kzalloc(sizeof(*vlan), GFP_KERNEL); if (!vlan)
From: Yufeng Mo moyufeng@huawei.com
mainline inclusion from mainline-v5.14 commit 55649d56541bebecfd45ecd14f02944190538a66 category: bugfix bugzilla: https://gitee.com/openeuler/kernel/issues/I46N6O CVE: NA
Reference: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?i...
----------------------------------------------------------------------
Currently, the cmd index is obtained in debugfs by comparing file names. However, this method may cause errors when processing more complex file names. So, change this method by saving cmd in private data and comparing it when getting cmd index in debugfs for optimization.
Fixes: 5e69ea7ee2a6 ("net: hns3: refactor the debugfs process") Signed-off-by: Yufeng Mo moyufeng@huawei.com Signed-off-by: Guangbin Huang huangguangbin2@huawei.com Signed-off-by: Jakub Kicinski kuba@kernel.org Reviewed-by: Yongxin Li liyongxin1@huawei.com Signed-off-by: Junxin Chen chenjunxin1@huawei.com Signed-off-by: Zheng Zengkai zhengzengkai@huawei.com --- drivers/net/ethernet/hisilicon/hns3/hns3_debugfs.c | 14 +++++++------- drivers/net/ethernet/hisilicon/hns3/hns3_debugfs.h | 1 + 2 files changed, 8 insertions(+), 7 deletions(-)
diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3_debugfs.c b/drivers/net/ethernet/hisilicon/hns3/hns3_debugfs.c index 532523069d74..80461ab0ce9e 100644 --- a/drivers/net/ethernet/hisilicon/hns3/hns3_debugfs.c +++ b/drivers/net/ethernet/hisilicon/hns3/hns3_debugfs.c @@ -938,20 +938,19 @@ static int hns3_dbg_dev_info(struct hnae3_handle *h, char *buf, int len) return 0; }
-static int hns3_dbg_get_cmd_index(struct hnae3_handle *handle, - const unsigned char *name, u32 *index) +static int hns3_dbg_get_cmd_index(struct hns3_dbg_data *dbg_data, u32 *index) { u32 i;
for (i = 0; i < ARRAY_SIZE(hns3_dbg_cmd); i++) { - if (!strncmp(name, hns3_dbg_cmd[i].name, - strlen(hns3_dbg_cmd[i].name))) { + if (hns3_dbg_cmd[i].cmd == dbg_data->cmd) { *index = i; return 0; } }
- dev_err(&handle->pdev->dev, "unknown command(%s)\n", name); + dev_err(&dbg_data->handle->pdev->dev, "unknown command(%d)\n", + dbg_data->cmd); return -EINVAL; }
@@ -1019,8 +1018,7 @@ static ssize_t hns3_dbg_read(struct file *filp, char __user *buffer, u32 index; int ret;
- ret = hns3_dbg_get_cmd_index(handle, filp->f_path.dentry->d_iname, - &index); + ret = hns3_dbg_get_cmd_index(dbg_data, &index); if (ret) return ret;
@@ -1090,6 +1088,7 @@ static int hns3_dbg_bd_file_init(struct hnae3_handle *handle, u32 cmd) char name[HNS3_DBG_FILE_NAME_LEN];
data[i].handle = handle; + data[i].cmd = hns3_dbg_cmd[cmd].cmd; data[i].qid = i; sprintf(name, "%s%u", hns3_dbg_cmd[cmd].name, i); debugfs_create_file(name, 0400, entry_dir, &data[i], @@ -1110,6 +1109,7 @@ hns3_dbg_common_file_init(struct hnae3_handle *handle, u32 cmd) return -ENOMEM;
data->handle = handle; + data->cmd = hns3_dbg_cmd[cmd].cmd; entry_dir = hns3_dbg_dentry[hns3_dbg_cmd[cmd].dentry].dentry; debugfs_create_file(hns3_dbg_cmd[cmd].name, 0400, entry_dir, data, &hns3_dbg_fops); diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3_debugfs.h b/drivers/net/ethernet/hisilicon/hns3/hns3_debugfs.h index f3766ff38bb7..bd8801065e02 100644 --- a/drivers/net/ethernet/hisilicon/hns3/hns3_debugfs.h +++ b/drivers/net/ethernet/hisilicon/hns3/hns3_debugfs.h @@ -22,6 +22,7 @@ struct hns3_dbg_item {
struct hns3_dbg_data { struct hnae3_handle *handle; + enum hnae3_dbg_cmd cmd; u16 qid; };
From: Yufeng Mo moyufeng@huawei.com
mainline inclusion from mainline-v5.14 commit 3462207d2d684658d97499ca77c00c9ac7c87ea8 category: bugfix bugzilla: https://gitee.com/openeuler/kernel/issues/I46N6O CVE: NA
Reference: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?i...
----------------------------------------------------------------------
The GRO configuration is enabled by default after reset. This is incorrect and should be restored to the user-configured value. So this restoration is added during reset initialization.
Signed-off-by: Yufeng Mo moyufeng@huawei.com Signed-off-by: Guangbin Huang huangguangbin2@huawei.com Signed-off-by: Jakub Kicinski kuba@kernel.org Reviewed-by: Yongxin Li liyongxin1@huawei.com Signed-off-by: Junxin Chen chenjunxin1@huawei.com Signed-off-by: Zheng Zengkai zhengzengkai@huawei.com --- .../hisilicon/hns3/hns3pf/hclge_main.c | 18 +++++++++++++----- .../hisilicon/hns3/hns3pf/hclge_main.h | 1 + .../hisilicon/hns3/hns3vf/hclgevf_main.c | 19 ++++++++++++++----- .../hisilicon/hns3/hns3vf/hclgevf_main.h | 2 ++ 4 files changed, 30 insertions(+), 10 deletions(-)
diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c index 1d5eee48b23b..6e744eca0d50 100644 --- a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c +++ b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c @@ -1551,6 +1551,7 @@ static int hclge_configure(struct hclge_dev *hdev) hdev->tm_info.hw_pfc_map = 0; hdev->wanted_umv_size = cfg.umv_space; hdev->tx_spare_buf_size = cfg.tx_spare_buf_size; + hdev->gro_en = true; if (cfg.vlan_fliter_cap == HCLGE_VLAN_FLTR_CAN_MDF) set_bit(HNAE3_DEV_SUPPORT_VLAN_FLTR_MDF_B, ae_dev->caps);
@@ -1619,7 +1620,7 @@ static int hclge_config_tso(struct hclge_dev *hdev, u16 tso_mss_min, return hclge_cmd_send(&hdev->hw, &desc, 1); }
-static int hclge_config_gro(struct hclge_dev *hdev, bool en) +static int hclge_config_gro(struct hclge_dev *hdev) { struct hclge_cfg_gro_status_cmd *req; struct hclge_desc desc; @@ -1631,7 +1632,7 @@ static int hclge_config_gro(struct hclge_dev *hdev, bool en) hclge_cmd_setup_basic_desc(&desc, HCLGE_OPC_GRO_GENERIC_CONFIG, false); req = (struct hclge_cfg_gro_status_cmd *)desc.data;
- req->gro_en = en ? 1 : 0; + req->gro_en = hdev->gro_en ? 1 : 0;
ret = hclge_cmd_send(&hdev->hw, &desc, 1); if (ret) @@ -11597,7 +11598,7 @@ static int hclge_init_ae_dev(struct hnae3_ae_dev *ae_dev) goto err_mdiobus_unreg; }
- ret = hclge_config_gro(hdev, true); + ret = hclge_config_gro(hdev); if (ret) goto err_mdiobus_unreg;
@@ -11980,7 +11981,7 @@ static int hclge_reset_ae_dev(struct hnae3_ae_dev *ae_dev) return ret; }
- ret = hclge_config_gro(hdev, true); + ret = hclge_config_gro(hdev); if (ret) return ret;
@@ -12715,8 +12716,15 @@ static int hclge_gro_en(struct hnae3_handle *handle, bool enable) { struct hclge_vport *vport = hclge_get_vport(handle); struct hclge_dev *hdev = vport->back; + bool gro_en_old = hdev->gro_en; + int ret; + + hdev->gro_en = enable; + ret = hclge_config_gro(hdev); + if (ret) + hdev->gro_en = gro_en_old;
- return hclge_config_gro(hdev, enable); + return ret; }
static void hclge_sync_promisc_mode(struct hclge_dev *hdev) diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.h b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.h index ada5c68f2851..b6c1153945e5 100644 --- a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.h +++ b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.h @@ -929,6 +929,7 @@ struct hclge_dev { unsigned long fd_bmap[BITS_TO_LONGS(MAX_FD_FILTER_NUM)]; enum HCLGE_FD_ACTIVE_RULE_TYPE fd_active_type; u8 fd_en; + bool gro_en;
u16 wanted_umv_size; /* max available unicast mac vlan space */ diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3vf/hclgevf_main.c b/drivers/net/ethernet/hisilicon/hns3/hns3vf/hclgevf_main.c index 0b0f9f1989f7..f5b3532695dd 100644 --- a/drivers/net/ethernet/hisilicon/hns3/hns3vf/hclgevf_main.c +++ b/drivers/net/ethernet/hisilicon/hns3/hns3vf/hclgevf_main.c @@ -2488,6 +2488,8 @@ static int hclgevf_configure(struct hclgevf_dev *hdev) { int ret;
+ hdev->gro_en = true; + ret = hclgevf_get_basic_info(hdev); if (ret) return ret; @@ -2550,7 +2552,7 @@ static int hclgevf_init_roce_base_info(struct hclgevf_dev *hdev) return 0; }
-static int hclgevf_config_gro(struct hclgevf_dev *hdev, bool en) +static int hclgevf_config_gro(struct hclgevf_dev *hdev) { struct hclgevf_cfg_gro_status_cmd *req; struct hclgevf_desc desc; @@ -2563,7 +2565,7 @@ static int hclgevf_config_gro(struct hclgevf_dev *hdev, bool en) false); req = (struct hclgevf_cfg_gro_status_cmd *)desc.data;
- req->gro_en = en ? 1 : 0; + req->gro_en = hdev->gro_en ? 1 : 0;
ret = hclgevf_cmd_send(&hdev->hw, &desc, 1); if (ret) @@ -3309,7 +3311,7 @@ static int hclgevf_reset_hdev(struct hclgevf_dev *hdev) return ret; }
- ret = hclgevf_config_gro(hdev, true); + ret = hclgevf_config_gro(hdev); if (ret) return ret;
@@ -3394,7 +3396,7 @@ static int hclgevf_init_hdev(struct hclgevf_dev *hdev) if (ret) goto err_config;
- ret = hclgevf_config_gro(hdev, true); + ret = hclgevf_config_gro(hdev); if (ret) goto err_config;
@@ -3646,8 +3648,15 @@ void hclgevf_update_speed_duplex(struct hclgevf_dev *hdev, u32 speed, static int hclgevf_gro_en(struct hnae3_handle *handle, bool enable) { struct hclgevf_dev *hdev = hclgevf_ae_get_hdev(handle); + bool gro_en_old = hdev->gro_en; + int ret;
- return hclgevf_config_gro(hdev, enable); + hdev->gro_en = enable; + ret = hclgevf_config_gro(hdev); + if (ret) + hdev->gro_en = gro_en_old; + + return ret; }
static void hclgevf_get_media_type(struct hnae3_handle *handle, u8 *media_type, diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3vf/hclgevf_main.h b/drivers/net/ethernet/hisilicon/hns3/hns3vf/hclgevf_main.h index 6f222a3a0bf2..73e8bb5efc30 100644 --- a/drivers/net/ethernet/hisilicon/hns3/hns3vf/hclgevf_main.h +++ b/drivers/net/ethernet/hisilicon/hns3/hns3vf/hclgevf_main.h @@ -311,6 +311,8 @@ struct hclgevf_dev { u16 *vector_status; int *vector_irq;
+ bool gro_en; + unsigned long vlan_del_fail_bmap[BITS_TO_LONGS(VLAN_N_VID)];
struct hclgevf_mac_table_cfg mac_table;
From: Guangbin Huang huangguangbin2@huawei.com
mainline inclusion from mainline-v5.14 commit 8c1671e0d13d4a0ba4fb3a0da932bf3736d7ff73 category: bugfix bugzilla: https://gitee.com/openeuler/kernel/issues/I46N6O CVE: NA
Reference: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?i...
----------------------------------------------------------------------
Currently, when query PFC configuration by dcbtool, driver will return PFC enable status based on TC. As all priorities are mapped to TC0 by default, if TC0 is enabled, then all priorities mapped to TC0 will be shown as enabled status when query PFC setting, even though some priorities have never been set.
for example: $ dcb pfc show dev eth0 pfc-cap 4 macsec-bypass off delay 0 prio-pfc 0:off 1:off 2:off 3:off 4:off 5:off 6:off 7:off $ dcb pfc set dev eth0 prio-pfc 0:on 1:on 2:on 3:on $ dcb pfc show dev eth0 pfc-cap 4 macsec-bypass off delay 0 prio-pfc 0:on 1:on 2:on 3:on 4:on 5:on 6:on 7:on
To fix this problem, just returns user's PFC config parameter saved in driver.
Fixes: cacde272dd00 ("net: hns3: Add hclge_dcb module for the support of DCB feature") Signed-off-by: Guangbin Huang huangguangbin2@huawei.com Signed-off-by: Jakub Kicinski kuba@kernel.org Reviewed-by: Yongxin Li liyongxin1@huawei.com Signed-off-by: Junxin Chen chenjunxin1@huawei.com Signed-off-by: Zheng Zengkai zhengzengkai@huawei.com --- .../net/ethernet/hisilicon/hns3/hns3pf/hclge_dcb.c | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-)
diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_dcb.c b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_dcb.c index 5bf5db91d16c..39f56f245d84 100644 --- a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_dcb.c +++ b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_dcb.c @@ -255,21 +255,12 @@ static int hclge_ieee_getpfc(struct hnae3_handle *h, struct ieee_pfc *pfc) u64 requests[HNAE3_MAX_TC], indications[HNAE3_MAX_TC]; struct hclge_vport *vport = hclge_get_vport(h); struct hclge_dev *hdev = vport->back; - u8 i, j, pfc_map, *prio_tc; int ret; + u8 i;
memset(pfc, 0, sizeof(*pfc)); pfc->pfc_cap = hdev->pfc_max; - prio_tc = hdev->tm_info.prio_tc; - pfc_map = hdev->tm_info.hw_pfc_map; - - /* Pfc setting is based on TC */ - for (i = 0; i < hdev->tm_info.num_tc; i++) { - for (j = 0; j < HNAE3_MAX_USER_PRIO; j++) { - if ((prio_tc[j] == i) && (pfc_map & BIT(i))) - pfc->pfc_en |= BIT(j); - } - } + pfc->pfc_en = hdev->tm_info.pfc_en;
ret = hclge_pfc_tx_stats_get(hdev, requests); if (ret)
From: Antonio Cardace acardace@redhat.com
mainline inclusion from mainline-v5.11-rc1 commit 4ae21993f07422ec1cb83e9530f87fa61bff02bd category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/I46N7D CVE: NA
Reference: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?i...
----------------------------------------------------------------------
This bitmask represents all existing coalesce parameters.
Signed-off-by: Antonio Cardace acardace@redhat.com Reviewed-by: Michal Kubecek mkubecek@suse.cz Signed-off-by: Jakub Kicinski kuba@kernel.org Reviewed-by: Yongxin Li liyongxin1@huawei.com Signed-off-by: Junxin Chen chenjunxin1@huawei.com Signed-off-by: Zheng Zengkai zhengzengkai@huawei.com --- include/linux/ethtool.h | 1 + 1 file changed, 1 insertion(+)
diff --git a/include/linux/ethtool.h b/include/linux/ethtool.h index 6408b446051f..e3da25b51ae4 100644 --- a/include/linux/ethtool.h +++ b/include/linux/ethtool.h @@ -215,6 +215,7 @@ bool ethtool_convert_link_mode_to_legacy_u32(u32 *legacy_u32, #define ETHTOOL_COALESCE_TX_USECS_HIGH BIT(19) #define ETHTOOL_COALESCE_TX_MAX_FRAMES_HIGH BIT(20) #define ETHTOOL_COALESCE_RATE_SAMPLE_INTERVAL BIT(21) +#define ETHTOOL_COALESCE_ALL_PARAMS GENMASK(21, 0)
#define ETHTOOL_COALESCE_USECS \ (ETHTOOL_COALESCE_RX_USECS | ETHTOOL_COALESCE_TX_USECS)
From: Yufeng Mo moyufeng@huawei.com
mainline inclusion from mainline-master commit 029ee6b14356b94120bedb852dcdaefc0a282cf1 category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/I46N7D CVE: NA
Reference: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?i...
----------------------------------------------------------------------
Currently, there are many drivers who support CQE mode configuration, some configure it as a fixed when initialized, some provide an interface to change it by ethtool private flags. In order to make it more generic, add two new 'ETHTOOL_A_COALESCE_USE_CQE_TX' and 'ETHTOOL_A_COALESCE_USE_CQE_RX' coalesce attributes, then these parameters can be accessed by ethtool netlink coalesce uAPI.
Also add an new structure kernel_ethtool_coalesce, then the new parameter can be added into this struct.
Signed-off-by: Yufeng Mo moyufeng@huawei.com Signed-off-by: Huazhong Tan tanhuazhong@huawei.com Signed-off-by: Jakub Kicinski kuba@kernel.org Reviewed-by: Yongxin Li liyongxin1@huawei.com Signed-off-by: Junxin Chen chenjunxin1@huawei.com Signed-off-by: Zheng Zengkai zhengzengkai@huawei.com --- Documentation/networking/ethtool-netlink.rst | 15 +++++++++++++++ include/linux/ethtool.h | 11 ++++++++++- include/uapi/linux/ethtool_netlink.h | 2 ++ net/ethtool/coalesce.c | 19 +++++++++++++++++-- net/ethtool/netlink.h | 2 +- 5 files changed, 45 insertions(+), 4 deletions(-)
diff --git a/Documentation/networking/ethtool-netlink.rst b/Documentation/networking/ethtool-netlink.rst index f7b88ee9227d..dd8c30c51ce0 100644 --- a/Documentation/networking/ethtool-netlink.rst +++ b/Documentation/networking/ethtool-netlink.rst @@ -936,12 +936,25 @@ Kernel response contents: ``ETHTOOL_A_COALESCE_TX_USECS_HIGH`` u32 delay (us), high Tx ``ETHTOOL_A_COALESCE_TX_MAX_FRAMES_HIGH`` u32 max packets, high Tx ``ETHTOOL_A_COALESCE_RATE_SAMPLE_INTERVAL`` u32 rate sampling interval + ``ETHTOOL_A_COALESCE_USE_CQE_TX`` bool timer reset mode, Tx + ``ETHTOOL_A_COALESCE_USE_CQE_RX`` bool timer reset mode, Rx =========================================== ====== =======================
Attributes are only included in reply if their value is not zero or the corresponding bit in ``ethtool_ops::supported_coalesce_params`` is set (i.e. they are declared as supported by driver).
+Timer reset mode (``ETHTOOL_A_COALESCE_USE_CQE_TX`` and +``ETHTOOL_A_COALESCE_USE_CQE_RX``) controls the interaction between packet +arrival and the various time based delay parameters. By default timers are +expected to limit the max delay between any packet arrival/departure and a +corresponding interrupt. In this mode timer should be started by packet +arrival (sometimes delivery of previous interrupt) and reset when interrupt +is delivered. +Setting the appropriate attribute to 1 will enable ``CQE`` mode, where +each packet event resets the timer. In this mode timer is used to force +the interrupt if queue goes idle, while busy queues depend on the packet +limit to trigger interrupts.
COALESCE_SET ============ @@ -974,6 +987,8 @@ Request contents: ``ETHTOOL_A_COALESCE_TX_USECS_HIGH`` u32 delay (us), high Tx ``ETHTOOL_A_COALESCE_TX_MAX_FRAMES_HIGH`` u32 max packets, high Tx ``ETHTOOL_A_COALESCE_RATE_SAMPLE_INTERVAL`` u32 rate sampling interval + ``ETHTOOL_A_COALESCE_USE_CQE_TX`` bool timer reset mode, Tx + ``ETHTOOL_A_COALESCE_USE_CQE_RX`` bool timer reset mode, Rx =========================================== ====== =======================
Request is rejected if it attributes declared as unsupported by driver (i.e. diff --git a/include/linux/ethtool.h b/include/linux/ethtool.h index e3da25b51ae4..023e05b22890 100644 --- a/include/linux/ethtool.h +++ b/include/linux/ethtool.h @@ -176,6 +176,11 @@ extern int __ethtool_get_link_ksettings(struct net_device *dev, struct ethtool_link_ksettings *link_ksettings);
+struct kernel_ethtool_coalesce { + u8 use_cqe_mode_tx; + u8 use_cqe_mode_rx; +}; + /** * ethtool_intersect_link_masks - Given two link masks, AND them together * @dst: first mask and where result is stored @@ -215,7 +220,9 @@ bool ethtool_convert_link_mode_to_legacy_u32(u32 *legacy_u32, #define ETHTOOL_COALESCE_TX_USECS_HIGH BIT(19) #define ETHTOOL_COALESCE_TX_MAX_FRAMES_HIGH BIT(20) #define ETHTOOL_COALESCE_RATE_SAMPLE_INTERVAL BIT(21) -#define ETHTOOL_COALESCE_ALL_PARAMS GENMASK(21, 0) +#define ETHTOOL_COALESCE_USE_CQE_RX BIT(22) +#define ETHTOOL_COALESCE_USE_CQE_TX BIT(23) +#define ETHTOOL_COALESCE_ALL_PARAMS GENMASK(23, 0)
#define ETHTOOL_COALESCE_USECS \ (ETHTOOL_COALESCE_RX_USECS | ETHTOOL_COALESCE_TX_USECS) @@ -241,6 +248,8 @@ bool ethtool_convert_link_mode_to_legacy_u32(u32 *legacy_u32, ETHTOOL_COALESCE_RX_USECS_LOW | ETHTOOL_COALESCE_RX_USECS_HIGH | \ ETHTOOL_COALESCE_PKT_RATE_LOW | ETHTOOL_COALESCE_PKT_RATE_HIGH | \ ETHTOOL_COALESCE_RATE_SAMPLE_INTERVAL) +#define ETHTOOL_COALESCE_USE_CQE \ + (ETHTOOL_COALESCE_USE_CQE_RX | ETHTOOL_COALESCE_USE_CQE_TX)
#define ETHTOOL_STAT_NOT_SET (~0ULL)
diff --git a/include/uapi/linux/ethtool_netlink.h b/include/uapi/linux/ethtool_netlink.h index e2bf36e6964b..4f552430c69e 100644 --- a/include/uapi/linux/ethtool_netlink.h +++ b/include/uapi/linux/ethtool_netlink.h @@ -366,6 +366,8 @@ enum { ETHTOOL_A_COALESCE_TX_USECS_HIGH, /* u32 */ ETHTOOL_A_COALESCE_TX_MAX_FRAMES_HIGH, /* u32 */ ETHTOOL_A_COALESCE_RATE_SAMPLE_INTERVAL, /* u32 */ + ETHTOOL_A_COALESCE_USE_CQE_MODE_TX, /* u8 */ + ETHTOOL_A_COALESCE_USE_CQE_MODE_RX, /* u8 */
/* add new constants above here */ __ETHTOOL_A_COALESCE_CNT, diff --git a/net/ethtool/coalesce.c b/net/ethtool/coalesce.c index 1d6bc132aa4d..e6bc53634e68 100644 --- a/net/ethtool/coalesce.c +++ b/net/ethtool/coalesce.c @@ -10,6 +10,7 @@ struct coalesce_req_info { struct coalesce_reply_data { struct ethnl_reply_data base; struct ethtool_coalesce coalesce; + struct kernel_ethtool_coalesce kernel_coalesce; u32 supported_params; };
@@ -100,7 +101,9 @@ static int coalesce_reply_size(const struct ethnl_req_info *req_base, nla_total_size(sizeof(u32)) + /* _RX_MAX_FRAMES_HIGH */ nla_total_size(sizeof(u32)) + /* _TX_USECS_HIGH */ nla_total_size(sizeof(u32)) + /* _TX_MAX_FRAMES_HIGH */ - nla_total_size(sizeof(u32)); /* _RATE_SAMPLE_INTERVAL */ + nla_total_size(sizeof(u32)) + /* _RATE_SAMPLE_INTERVAL */ + nla_total_size(sizeof(u8)) + /* _USE_CQE_MODE_TX */ + nla_total_size(sizeof(u8)); /* _USE_CQE_MODE_RX */ }
static bool coalesce_put_u32(struct sk_buff *skb, u16 attr_type, u32 val, @@ -124,6 +127,7 @@ static int coalesce_fill_reply(struct sk_buff *skb, const struct ethnl_reply_data *reply_base) { const struct coalesce_reply_data *data = COALESCE_REPDATA(reply_base); + const struct kernel_ethtool_coalesce *kcoal = &data->kernel_coalesce; const struct ethtool_coalesce *coal = &data->coalesce; u32 supported = data->supported_params;
@@ -170,7 +174,11 @@ static int coalesce_fill_reply(struct sk_buff *skb, coalesce_put_u32(skb, ETHTOOL_A_COALESCE_TX_MAX_FRAMES_HIGH, coal->tx_max_coalesced_frames_high, supported) || coalesce_put_u32(skb, ETHTOOL_A_COALESCE_RATE_SAMPLE_INTERVAL, - coal->rate_sample_interval, supported)) + coal->rate_sample_interval, supported) || + coalesce_put_bool(skb, ETHTOOL_A_COALESCE_USE_CQE_MODE_TX, + kcoal->use_cqe_mode_tx, supported) || + coalesce_put_bool(skb, ETHTOOL_A_COALESCE_USE_CQE_MODE_RX, + kcoal->use_cqe_mode_rx, supported)) return -EMSGSIZE;
return 0; @@ -215,10 +223,13 @@ const struct nla_policy ethnl_coalesce_set_policy[] = { [ETHTOOL_A_COALESCE_TX_USECS_HIGH] = { .type = NLA_U32 }, [ETHTOOL_A_COALESCE_TX_MAX_FRAMES_HIGH] = { .type = NLA_U32 }, [ETHTOOL_A_COALESCE_RATE_SAMPLE_INTERVAL] = { .type = NLA_U32 }, + [ETHTOOL_A_COALESCE_USE_CQE_MODE_TX] = NLA_POLICY_MAX(NLA_U8, 1), + [ETHTOOL_A_COALESCE_USE_CQE_MODE_RX] = NLA_POLICY_MAX(NLA_U8, 1), };
int ethnl_set_coalesce(struct sk_buff *skb, struct genl_info *info) { + struct kernel_ethtool_coalesce kernel_coalesce = {}; struct ethtool_coalesce coalesce = {}; struct ethnl_req_info req_info = {}; struct nlattr **tb = info->attrs; @@ -303,6 +314,10 @@ int ethnl_set_coalesce(struct sk_buff *skb, struct genl_info *info) tb[ETHTOOL_A_COALESCE_TX_MAX_FRAMES_HIGH], &mod); ethnl_update_u32(&coalesce.rate_sample_interval, tb[ETHTOOL_A_COALESCE_RATE_SAMPLE_INTERVAL], &mod); + ethnl_update_u8(&kernel_coalesce.use_cqe_mode_tx, + tb[ETHTOOL_A_COALESCE_USE_CQE_MODE_TX], &mod); + ethnl_update_u8(&kernel_coalesce.use_cqe_mode_rx, + tb[ETHTOOL_A_COALESCE_USE_CQE_MODE_RX], &mod); ret = 0; if (!mod) goto out_ops; diff --git a/net/ethtool/netlink.h b/net/ethtool/netlink.h index d8efec516d86..5e35ec53c036 100644 --- a/net/ethtool/netlink.h +++ b/net/ethtool/netlink.h @@ -366,7 +366,7 @@ extern const struct nla_policy ethnl_rings_set_policy[ETHTOOL_A_RINGS_TX + 1]; extern const struct nla_policy ethnl_channels_get_policy[ETHTOOL_A_CHANNELS_HEADER + 1]; extern const struct nla_policy ethnl_channels_set_policy[ETHTOOL_A_CHANNELS_COMBINED_COUNT + 1]; extern const struct nla_policy ethnl_coalesce_get_policy[ETHTOOL_A_COALESCE_HEADER + 1]; -extern const struct nla_policy ethnl_coalesce_set_policy[ETHTOOL_A_COALESCE_RATE_SAMPLE_INTERVAL + 1]; +extern const struct nla_policy ethnl_coalesce_set_policy[ETHTOOL_A_COALESCE_MAX + 1]; extern const struct nla_policy ethnl_pause_get_policy[ETHTOOL_A_PAUSE_HEADER + 1]; extern const struct nla_policy ethnl_pause_set_policy[ETHTOOL_A_PAUSE_TX + 1]; extern const struct nla_policy ethnl_eee_get_policy[ETHTOOL_A_EEE_HEADER + 1];
From: Yufeng Mo moyufeng@huawei.com
mainline inclusion from mainline-master commit f3ccfda1931977b80267ba54070a1aeafa18f6ca category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/I46N7D CVE: NA
Reference: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?i...
----------------------------------------------------------------------
In order to support more coalesce parameters through netlink, add two new parameter kernel_coal and extack for .set_coalesce and .get_coalesce, then some extra info can return to user with the netlink API.
Signed-off-by: Yufeng Mo moyufeng@huawei.com Signed-off-by: Huazhong Tan tanhuazhong@huawei.com Signed-off-by: Jakub Kicinski kuba@kernel.org Reviewed-by: Yongxin Li liyongxin1@huawei.com Signed-off-by: Junxin Chen chenjunxin1@huawei.com (fix conflicts: adapt diffs for qede/qlge/stmmac/netdevsim/txgbe) Signed-off-by: Zheng Zengkai zhengzengkai@huawei.com --- drivers/infiniband/ulp/ipoib/ipoib_ethtool.c | 8 ++++++-- drivers/net/ethernet/amazon/ena/ena_ethtool.c | 8 ++++++-- drivers/net/ethernet/amd/xgbe/xgbe-ethtool.c | 8 ++++++-- .../ethernet/aquantia/atlantic/aq_ethtool.c | 8 ++++++-- drivers/net/ethernet/broadcom/bcmsysport.c | 8 ++++++-- drivers/net/ethernet/broadcom/bnx2.c | 12 ++++++++---- .../ethernet/broadcom/bnx2x/bnx2x_ethtool.c | 8 ++++++-- .../net/ethernet/broadcom/bnxt/bnxt_ethtool.c | 8 ++++++-- drivers/net/ethernet/broadcom/genet/bcmgenet.c | 8 ++++++-- drivers/net/ethernet/broadcom/tg3.c | 10 ++++++++-- .../net/ethernet/brocade/bna/bnad_ethtool.c | 12 ++++++++---- .../net/ethernet/cavium/liquidio/lio_ethtool.c | 8 ++++++-- .../ethernet/cavium/thunder/nicvf_ethtool.c | 4 +++- drivers/net/ethernet/chelsio/cxgb/cxgb2.c | 8 ++++++-- .../net/ethernet/chelsio/cxgb3/cxgb3_main.c | 8 ++++++-- .../net/ethernet/chelsio/cxgb4/cxgb4_ethtool.c | 8 ++++++-- .../ethernet/chelsio/cxgb4vf/cxgb4vf_main.c | 8 ++++++-- drivers/net/ethernet/cisco/enic/enic_ethtool.c | 8 ++++++-- drivers/net/ethernet/cortina/gemini.c | 8 ++++++-- drivers/net/ethernet/emulex/benet/be_ethtool.c | 8 ++++++-- .../net/ethernet/freescale/dpaa/dpaa_ethtool.c | 8 ++++++-- .../ethernet/freescale/enetc/enetc_ethtool.c | 8 ++++++-- drivers/net/ethernet/freescale/fec_main.c | 14 +++++++++----- .../net/ethernet/freescale/gianfar_ethtool.c | 8 ++++++-- drivers/net/ethernet/hisilicon/hip04_eth.c | 8 ++++++-- .../net/ethernet/hisilicon/hns/hns_ethtool.c | 12 ++++++++++-- .../net/ethernet/hisilicon/hns3/hns3_ethtool.c | 8 ++++++-- .../net/ethernet/huawei/hinic/hinic_ethtool.c | 8 ++++++-- .../net/ethernet/intel/e1000/e1000_ethtool.c | 8 ++++++-- drivers/net/ethernet/intel/e1000e/ethtool.c | 8 ++++++-- .../net/ethernet/intel/fm10k/fm10k_ethtool.c | 8 ++++++-- drivers/net/ethernet/intel/i40e/i40e_ethtool.c | 12 ++++++++++-- drivers/net/ethernet/intel/iavf/iavf_ethtool.c | 12 ++++++++++-- drivers/net/ethernet/intel/ice/ice_ethtool.c | 12 ++++++++---- drivers/net/ethernet/intel/igb/igb_ethtool.c | 8 ++++++-- drivers/net/ethernet/intel/igbvf/ethtool.c | 8 ++++++-- drivers/net/ethernet/intel/igc/igc_ethtool.c | 8 ++++++-- .../net/ethernet/intel/ixgbe/ixgbe_ethtool.c | 8 ++++++-- drivers/net/ethernet/intel/ixgbevf/ethtool.c | 8 ++++++-- drivers/net/ethernet/jme.c | 12 ++++++++---- drivers/net/ethernet/marvell/mv643xx_eth.c | 12 ++++++++---- drivers/net/ethernet/marvell/mvneta.c | 14 ++++++++++---- .../net/ethernet/marvell/mvpp2/mvpp2_main.c | 14 ++++++++++---- .../marvell/octeontx2/nic/otx2_ethtool.c | 8 ++++++-- drivers/net/ethernet/marvell/skge.c | 8 ++++++-- drivers/net/ethernet/marvell/sky2.c | 8 ++++++-- .../net/ethernet/mellanox/mlx4/en_ethtool.c | 8 ++++++-- .../ethernet/mellanox/mlx5/core/en_ethtool.c | 8 ++++++-- .../net/ethernet/mellanox/mlx5/core/en_rep.c | 8 ++++++-- .../mellanox/mlx5/core/ipoib/ethtool.c | 8 ++++++-- .../net/ethernet/myricom/myri10ge/myri10ge.c | 12 ++++++++---- .../ethernet/netronome/nfp/nfp_net_ethtool.c | 8 ++++++-- .../ethernet/netswift/txgbe/txgbe_ethtool.c | 8 ++++++-- drivers/net/ethernet/ni/nixge.c | 14 ++++++++++---- .../ethernet/pensando/ionic/ionic_ethtool.c | 8 ++++++-- .../qlogic/netxen/netxen_nic_ethtool.c | 8 ++++++-- .../net/ethernet/qlogic/qede/qede_ethtool.c | 8 ++++++-- .../ethernet/qlogic/qlcnic/qlcnic_ethtool.c | 8 ++++++-- drivers/net/ethernet/realtek/r8169_main.c | 10 ++++++++-- .../net/ethernet/samsung/sxgbe/sxgbe_ethtool.c | 8 ++++++-- drivers/net/ethernet/sfc/ethtool.c | 8 ++++++-- drivers/net/ethernet/sfc/falcon/ethtool.c | 8 ++++++-- drivers/net/ethernet/socionext/netsec.c | 10 +++++++--- .../ethernet/stmicro/stmmac/stmmac_ethtool.c | 8 ++++++-- .../net/ethernet/synopsys/dwc-xlgmac-ethtool.c | 14 ++++++++++---- drivers/net/ethernet/tehuti/tehuti.c | 12 ++++++++---- drivers/net/ethernet/ti/cpsw.c | 2 +- drivers/net/ethernet/ti/cpsw_ethtool.c | 8 ++++++-- drivers/net/ethernet/ti/cpsw_new.c | 2 +- drivers/net/ethernet/ti/cpsw_priv.h | 8 ++++++-- drivers/net/ethernet/ti/davinci_emac.c | 14 +++++++++++--- drivers/net/ethernet/via/via-velocity.c | 8 ++++++-- drivers/net/ethernet/xilinx/ll_temac_main.c | 14 ++++++++++---- .../net/ethernet/xilinx/xilinx_axienet_main.c | 18 ++++++++++++++---- drivers/net/tun.c | 8 ++++++-- drivers/net/usb/r8152.c | 8 ++++++-- drivers/net/virtio_net.c | 8 ++++++-- drivers/net/vmxnet3/vmxnet3_ethtool.c | 12 ++++++++---- drivers/net/wireless/ath/wil6210/ethtool.c | 14 ++++++++++---- drivers/s390/net/qeth_ethtool.c | 4 +++- drivers/staging/qlge/qlge_ethtool.c | 10 ++++++++-- include/linux/ethtool.h | 11 +++++++++-- net/ethtool/coalesce.c | 10 +++++++--- net/ethtool/ioctl.c | 15 ++++++++++++--- 84 files changed, 573 insertions(+), 201 deletions(-)
diff --git a/drivers/infiniband/ulp/ipoib/ipoib_ethtool.c b/drivers/infiniband/ulp/ipoib/ipoib_ethtool.c index 67a21fdf5367..480885a6bb44 100644 --- a/drivers/infiniband/ulp/ipoib/ipoib_ethtool.c +++ b/drivers/infiniband/ulp/ipoib/ipoib_ethtool.c @@ -72,7 +72,9 @@ static void ipoib_get_drvinfo(struct net_device *netdev, }
static int ipoib_get_coalesce(struct net_device *dev, - struct ethtool_coalesce *coal) + struct ethtool_coalesce *coal, + struct kernel_ethtool_coalesce *kernel_coal, + struct netlink_ext_ack *extack) { struct ipoib_dev_priv *priv = ipoib_priv(dev);
@@ -83,7 +85,9 @@ static int ipoib_get_coalesce(struct net_device *dev, }
static int ipoib_set_coalesce(struct net_device *dev, - struct ethtool_coalesce *coal) + struct ethtool_coalesce *coal, + struct kernel_ethtool_coalesce *kernel_coal, + struct netlink_ext_ack *extack) { struct ipoib_dev_priv *priv = ipoib_priv(dev); int ret; diff --git a/drivers/net/ethernet/amazon/ena/ena_ethtool.c b/drivers/net/ethernet/amazon/ena/ena_ethtool.c index 3b2cd28f962d..7bae8261bb90 100644 --- a/drivers/net/ethernet/amazon/ena/ena_ethtool.c +++ b/drivers/net/ethernet/amazon/ena/ena_ethtool.c @@ -354,7 +354,9 @@ static int ena_get_link_ksettings(struct net_device *netdev, }
static int ena_get_coalesce(struct net_device *net_dev, - struct ethtool_coalesce *coalesce) + struct ethtool_coalesce *coalesce, + struct kernel_ethtool_coalesce *kernel_coal, + struct netlink_ext_ack *extack) { struct ena_adapter *adapter = netdev_priv(net_dev); struct ena_com_dev *ena_dev = adapter->ena_dev; @@ -399,7 +401,9 @@ static void ena_update_rx_rings_nonadaptive_intr_moderation(struct ena_adapter * }
static int ena_set_coalesce(struct net_device *net_dev, - struct ethtool_coalesce *coalesce) + struct ethtool_coalesce *coalesce, + struct kernel_ethtool_coalesce *kernel_coal, + struct netlink_ext_ack *extack) { struct ena_adapter *adapter = netdev_priv(net_dev); struct ena_com_dev *ena_dev = adapter->ena_dev; diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-ethtool.c b/drivers/net/ethernet/amd/xgbe/xgbe-ethtool.c index 61f39a0e04f9..bafc51c34e0b 100644 --- a/drivers/net/ethernet/amd/xgbe/xgbe-ethtool.c +++ b/drivers/net/ethernet/amd/xgbe/xgbe-ethtool.c @@ -428,7 +428,9 @@ static void xgbe_set_msglevel(struct net_device *netdev, u32 msglevel) }
static int xgbe_get_coalesce(struct net_device *netdev, - struct ethtool_coalesce *ec) + struct ethtool_coalesce *ec, + struct kernel_ethtool_coalesce *kernel_coal, + struct netlink_ext_ack *extack) { struct xgbe_prv_data *pdata = netdev_priv(netdev);
@@ -443,7 +445,9 @@ static int xgbe_get_coalesce(struct net_device *netdev, }
static int xgbe_set_coalesce(struct net_device *netdev, - struct ethtool_coalesce *ec) + struct ethtool_coalesce *ec, + struct kernel_ethtool_coalesce *kernel_coal, + struct netlink_ext_ack *extack) { struct xgbe_prv_data *pdata = netdev_priv(netdev); struct xgbe_hw_if *hw_if = &pdata->hw_if; diff --git a/drivers/net/ethernet/aquantia/atlantic/aq_ethtool.c b/drivers/net/ethernet/aquantia/atlantic/aq_ethtool.c index de2a9348bc3f..a9ef0544e30f 100644 --- a/drivers/net/ethernet/aquantia/atlantic/aq_ethtool.c +++ b/drivers/net/ethernet/aquantia/atlantic/aq_ethtool.c @@ -547,7 +547,9 @@ static int aq_ethtool_set_rxnfc(struct net_device *ndev, }
static int aq_ethtool_get_coalesce(struct net_device *ndev, - struct ethtool_coalesce *coal) + struct ethtool_coalesce *coal, + struct kernel_ethtool_coalesce *kernel_coal, + struct netlink_ext_ack *extack) { struct aq_nic_s *aq_nic = netdev_priv(ndev); struct aq_nic_cfg_s *cfg; @@ -571,7 +573,9 @@ static int aq_ethtool_get_coalesce(struct net_device *ndev, }
static int aq_ethtool_set_coalesce(struct net_device *ndev, - struct ethtool_coalesce *coal) + struct ethtool_coalesce *coal, + struct kernel_ethtool_coalesce *kernel_coal, + struct netlink_ext_ack *extack) { struct aq_nic_s *aq_nic = netdev_priv(ndev); struct aq_nic_cfg_s *cfg; diff --git a/drivers/net/ethernet/broadcom/bcmsysport.c b/drivers/net/ethernet/broadcom/bcmsysport.c index 0404aafd5ce5..c4886764b7c9 100644 --- a/drivers/net/ethernet/broadcom/bcmsysport.c +++ b/drivers/net/ethernet/broadcom/bcmsysport.c @@ -606,7 +606,9 @@ static void bcm_sysport_set_tx_coalesce(struct bcm_sysport_tx_ring *ring, }
static int bcm_sysport_get_coalesce(struct net_device *dev, - struct ethtool_coalesce *ec) + struct ethtool_coalesce *ec, + struct kernel_ethtool_coalesce *kernel_coal, + struct netlink_ext_ack *extack) { struct bcm_sysport_priv *priv = netdev_priv(dev); u32 reg; @@ -626,7 +628,9 @@ static int bcm_sysport_get_coalesce(struct net_device *dev, }
static int bcm_sysport_set_coalesce(struct net_device *dev, - struct ethtool_coalesce *ec) + struct ethtool_coalesce *ec, + struct kernel_ethtool_coalesce *kernel_coal, + struct netlink_ext_ack *extack) { struct bcm_sysport_priv *priv = netdev_priv(dev); struct dim_cq_moder moder; diff --git a/drivers/net/ethernet/broadcom/bnx2.c b/drivers/net/ethernet/broadcom/bnx2.c index 633b10389653..c749ee375f40 100644 --- a/drivers/net/ethernet/broadcom/bnx2.c +++ b/drivers/net/ethernet/broadcom/bnx2.c @@ -7241,8 +7241,10 @@ bnx2_set_eeprom(struct net_device *dev, struct ethtool_eeprom *eeprom, return rc; }
-static int -bnx2_get_coalesce(struct net_device *dev, struct ethtool_coalesce *coal) +static int bnx2_get_coalesce(struct net_device *dev, + struct ethtool_coalesce *coal, + struct kernel_ethtool_coalesce *kernel_coal, + struct netlink_ext_ack *extack) { struct bnx2 *bp = netdev_priv(dev);
@@ -7263,8 +7265,10 @@ bnx2_get_coalesce(struct net_device *dev, struct ethtool_coalesce *coal) return 0; }
-static int -bnx2_set_coalesce(struct net_device *dev, struct ethtool_coalesce *coal) +static int bnx2_set_coalesce(struct net_device *dev, + struct ethtool_coalesce *coal, + struct kernel_ethtool_coalesce *kernel_coal, + struct netlink_ext_ack *extack) { struct bnx2 *bp = netdev_priv(dev);
diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_ethtool.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_ethtool.c index 32245bbe88a8..472a3a478038 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_ethtool.c +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_ethtool.c @@ -1878,7 +1878,9 @@ static int bnx2x_set_eeprom(struct net_device *dev, }
static int bnx2x_get_coalesce(struct net_device *dev, - struct ethtool_coalesce *coal) + struct ethtool_coalesce *coal, + struct kernel_ethtool_coalesce *kernel_coal, + struct netlink_ext_ack *extack) { struct bnx2x *bp = netdev_priv(dev);
@@ -1891,7 +1893,9 @@ static int bnx2x_get_coalesce(struct net_device *dev, }
static int bnx2x_set_coalesce(struct net_device *dev, - struct ethtool_coalesce *coal) + struct ethtool_coalesce *coal, + struct kernel_ethtool_coalesce *kernel_coal, + struct netlink_ext_ack *extack) { struct bnx2x *bp = netdev_priv(dev);
diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c b/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c index 1471c9a36238..e5c363ee735d 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt_ethtool.c @@ -45,7 +45,9 @@ static void bnxt_set_msglevel(struct net_device *dev, u32 value) }
static int bnxt_get_coalesce(struct net_device *dev, - struct ethtool_coalesce *coal) + struct ethtool_coalesce *coal, + struct kernel_ethtool_coalesce *kernel_coal, + struct netlink_ext_ack *extack) { struct bnxt *bp = netdev_priv(dev); struct bnxt_coal *hw_coal; @@ -75,7 +77,9 @@ static int bnxt_get_coalesce(struct net_device *dev, }
static int bnxt_set_coalesce(struct net_device *dev, - struct ethtool_coalesce *coal) + struct ethtool_coalesce *coal, + struct kernel_ethtool_coalesce *kernel_coal, + struct netlink_ext_ack *extack) { struct bnxt *bp = netdev_priv(dev); bool update_stats = false; diff --git a/drivers/net/ethernet/broadcom/genet/bcmgenet.c b/drivers/net/ethernet/broadcom/genet/bcmgenet.c index fcca023f22e5..9dbbfd00a438 100644 --- a/drivers/net/ethernet/broadcom/genet/bcmgenet.c +++ b/drivers/net/ethernet/broadcom/genet/bcmgenet.c @@ -828,7 +828,9 @@ static void bcmgenet_set_msglevel(struct net_device *dev, u32 level) }
static int bcmgenet_get_coalesce(struct net_device *dev, - struct ethtool_coalesce *ec) + struct ethtool_coalesce *ec, + struct kernel_ethtool_coalesce *kernel_coal, + struct netlink_ext_ack *extack) { struct bcmgenet_priv *priv = netdev_priv(dev); struct bcmgenet_rx_ring *ring; @@ -890,7 +892,9 @@ static void bcmgenet_set_ring_rx_coalesce(struct bcmgenet_rx_ring *ring, }
static int bcmgenet_set_coalesce(struct net_device *dev, - struct ethtool_coalesce *ec) + struct ethtool_coalesce *ec, + struct kernel_ethtool_coalesce *kernel_coal, + struct netlink_ext_ack *extack) { struct bcmgenet_priv *priv = netdev_priv(dev); unsigned int i; diff --git a/drivers/net/ethernet/broadcom/tg3.c b/drivers/net/ethernet/broadcom/tg3.c index 5143cdd0eeca..85c6f627d665 100644 --- a/drivers/net/ethernet/broadcom/tg3.c +++ b/drivers/net/ethernet/broadcom/tg3.c @@ -14054,7 +14054,10 @@ static int tg3_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) return -EOPNOTSUPP; }
-static int tg3_get_coalesce(struct net_device *dev, struct ethtool_coalesce *ec) +static int tg3_get_coalesce(struct net_device *dev, + struct ethtool_coalesce *ec, + struct kernel_ethtool_coalesce *kernel_coal, + struct netlink_ext_ack *extack) { struct tg3 *tp = netdev_priv(dev);
@@ -14062,7 +14065,10 @@ static int tg3_get_coalesce(struct net_device *dev, struct ethtool_coalesce *ec) return 0; }
-static int tg3_set_coalesce(struct net_device *dev, struct ethtool_coalesce *ec) +static int tg3_set_coalesce(struct net_device *dev, + struct ethtool_coalesce *ec, + struct kernel_ethtool_coalesce *kernel_coal, + struct netlink_ext_ack *extack) { struct tg3 *tp = netdev_priv(dev); u32 max_rxcoal_tick_int = 0, max_txcoal_tick_int = 0; diff --git a/drivers/net/ethernet/brocade/bna/bnad_ethtool.c b/drivers/net/ethernet/brocade/bna/bnad_ethtool.c index 588c4804d10a..916bde53f13d 100644 --- a/drivers/net/ethernet/brocade/bna/bnad_ethtool.c +++ b/drivers/net/ethernet/brocade/bna/bnad_ethtool.c @@ -307,8 +307,10 @@ bnad_get_wol(struct net_device *netdev, struct ethtool_wolinfo *wolinfo) wolinfo->wolopts = 0; }
-static int -bnad_get_coalesce(struct net_device *netdev, struct ethtool_coalesce *coalesce) +static int bnad_get_coalesce(struct net_device *netdev, + struct ethtool_coalesce *coalesce, + struct kernel_ethtool_coalesce *kernel_coal, + struct netlink_ext_ack *extack) { struct bnad *bnad = netdev_priv(netdev); unsigned long flags; @@ -328,8 +330,10 @@ bnad_get_coalesce(struct net_device *netdev, struct ethtool_coalesce *coalesce) return 0; }
-static int -bnad_set_coalesce(struct net_device *netdev, struct ethtool_coalesce *coalesce) +static int bnad_set_coalesce(struct net_device *netdev, + struct ethtool_coalesce *coalesce, + struct kernel_ethtool_coalesce *kernel_coal, + struct netlink_ext_ack *extack) { struct bnad *bnad = netdev_priv(netdev); unsigned long flags; diff --git a/drivers/net/ethernet/cavium/liquidio/lio_ethtool.c b/drivers/net/ethernet/cavium/liquidio/lio_ethtool.c index 16eebfc52109..999dc567c8ee 100644 --- a/drivers/net/ethernet/cavium/liquidio/lio_ethtool.c +++ b/drivers/net/ethernet/cavium/liquidio/lio_ethtool.c @@ -2107,7 +2107,9 @@ static int octnet_set_intrmod_cfg(struct lio *lio, }
static int lio_get_intr_coalesce(struct net_device *netdev, - struct ethtool_coalesce *intr_coal) + struct ethtool_coalesce *intr_coal, + struct kernel_ethtool_coalesce *kernel_coal, + struct netlink_ext_ack *extack) { struct lio *lio = GET_LIO(netdev); struct octeon_device *oct = lio->oct_dev; @@ -2411,7 +2413,9 @@ oct_cfg_tx_intrcnt(struct lio *lio, }
static int lio_set_intr_coalesce(struct net_device *netdev, - struct ethtool_coalesce *intr_coal) + struct ethtool_coalesce *intr_coal, + struct kernel_ethtool_coalesce *kernel_coal, + struct netlink_ext_ack *extack) { struct lio *lio = GET_LIO(netdev); int ret; diff --git a/drivers/net/ethernet/cavium/thunder/nicvf_ethtool.c b/drivers/net/ethernet/cavium/thunder/nicvf_ethtool.c index c7bdac79299a..f794273df95a 100644 --- a/drivers/net/ethernet/cavium/thunder/nicvf_ethtool.c +++ b/drivers/net/ethernet/cavium/thunder/nicvf_ethtool.c @@ -455,7 +455,9 @@ static void nicvf_get_regs(struct net_device *dev, }
static int nicvf_get_coalesce(struct net_device *netdev, - struct ethtool_coalesce *cmd) + struct ethtool_coalesce *cmd, + struct kernel_ethtool_coalesce *kernel_coal, + struct netlink_ext_ack *extack) { struct nicvf *nic = netdev_priv(netdev);
diff --git a/drivers/net/ethernet/chelsio/cxgb/cxgb2.c b/drivers/net/ethernet/chelsio/cxgb/cxgb2.c index 0e4a0f413960..e63cd7136c31 100644 --- a/drivers/net/ethernet/chelsio/cxgb/cxgb2.c +++ b/drivers/net/ethernet/chelsio/cxgb/cxgb2.c @@ -747,7 +747,9 @@ static int set_sge_param(struct net_device *dev, struct ethtool_ringparam *e) return 0; }
-static int set_coalesce(struct net_device *dev, struct ethtool_coalesce *c) +static int set_coalesce(struct net_device *dev, struct ethtool_coalesce *c, + struct kernel_ethtool_coalesce *kernel_coal, + struct netlink_ext_ack *extack) { struct adapter *adapter = dev->ml_priv;
@@ -758,7 +760,9 @@ static int set_coalesce(struct net_device *dev, struct ethtool_coalesce *c) return 0; }
-static int get_coalesce(struct net_device *dev, struct ethtool_coalesce *c) +static int get_coalesce(struct net_device *dev, struct ethtool_coalesce *c, + struct kernel_ethtool_coalesce *kernel_coal, + struct netlink_ext_ack *extack) { struct adapter *adapter = dev->ml_priv;
diff --git a/drivers/net/ethernet/chelsio/cxgb3/cxgb3_main.c b/drivers/net/ethernet/chelsio/cxgb3/cxgb3_main.c index 84ad7261e243..c22ddbd20664 100644 --- a/drivers/net/ethernet/chelsio/cxgb3/cxgb3_main.c +++ b/drivers/net/ethernet/chelsio/cxgb3/cxgb3_main.c @@ -1996,7 +1996,9 @@ static int set_sge_param(struct net_device *dev, struct ethtool_ringparam *e) return 0; }
-static int set_coalesce(struct net_device *dev, struct ethtool_coalesce *c) +static int set_coalesce(struct net_device *dev, struct ethtool_coalesce *c, + struct kernel_ethtool_coalesce *kernel_coal, + struct netlink_ext_ack *extack) { struct port_info *pi = netdev_priv(dev); struct adapter *adapter = pi->adapter; @@ -2017,7 +2019,9 @@ static int set_coalesce(struct net_device *dev, struct ethtool_coalesce *c) return 0; }
-static int get_coalesce(struct net_device *dev, struct ethtool_coalesce *c) +static int get_coalesce(struct net_device *dev, struct ethtool_coalesce *c, + struct kernel_ethtool_coalesce *kernel_coal, + struct netlink_ext_ack *extack) { struct port_info *pi = netdev_priv(dev); struct adapter *adapter = pi->adapter; diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_ethtool.c b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_ethtool.c index 83ed10ac8660..5903bdb78916 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_ethtool.c +++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_ethtool.c @@ -1147,7 +1147,9 @@ static int set_dbqtimer_tickval(struct net_device *dev, }
static int set_coalesce(struct net_device *dev, - struct ethtool_coalesce *coalesce) + struct ethtool_coalesce *coalesce, + struct kernel_ethtool_coalesce *kernel_coal, + struct netlink_ext_ack *extack) { int ret;
@@ -1163,7 +1165,9 @@ static int set_coalesce(struct net_device *dev, coalesce->tx_coalesce_usecs); }
-static int get_coalesce(struct net_device *dev, struct ethtool_coalesce *c) +static int get_coalesce(struct net_device *dev, struct ethtool_coalesce *c, + struct kernel_ethtool_coalesce *kernel_coal, + struct netlink_ext_ack *extack) { const struct port_info *pi = netdev_priv(dev); const struct adapter *adap = pi->adapter; diff --git a/drivers/net/ethernet/chelsio/cxgb4vf/cxgb4vf_main.c b/drivers/net/ethernet/chelsio/cxgb4vf/cxgb4vf_main.c index 2820a0bb971b..fabe4eb88842 100644 --- a/drivers/net/ethernet/chelsio/cxgb4vf/cxgb4vf_main.c +++ b/drivers/net/ethernet/chelsio/cxgb4vf/cxgb4vf_main.c @@ -1647,7 +1647,9 @@ static int cxgb4vf_set_ringparam(struct net_device *dev, * interrupt holdoff timer to be read on all of the device's Queue Sets. */ static int cxgb4vf_get_coalesce(struct net_device *dev, - struct ethtool_coalesce *coalesce) + struct ethtool_coalesce *coalesce, + struct kernel_ethtool_coalesce *kernel_coal, + struct netlink_ext_ack *extack) { const struct port_info *pi = netdev_priv(dev); const struct adapter *adapter = pi->adapter; @@ -1667,7 +1669,9 @@ static int cxgb4vf_get_coalesce(struct net_device *dev, * the interrupt holdoff timer on any of the device's Queue Sets. */ static int cxgb4vf_set_coalesce(struct net_device *dev, - struct ethtool_coalesce *coalesce) + struct ethtool_coalesce *coalesce, + struct kernel_ethtool_coalesce *kernel_coal, + struct netlink_ext_ack *extack) { const struct port_info *pi = netdev_priv(dev); struct adapter *adapter = pi->adapter; diff --git a/drivers/net/ethernet/cisco/enic/enic_ethtool.c b/drivers/net/ethernet/cisco/enic/enic_ethtool.c index 1a9803f2073e..12ffc14fbecd 100644 --- a/drivers/net/ethernet/cisco/enic/enic_ethtool.c +++ b/drivers/net/ethernet/cisco/enic/enic_ethtool.c @@ -298,7 +298,9 @@ static void enic_set_msglevel(struct net_device *netdev, u32 value) }
static int enic_get_coalesce(struct net_device *netdev, - struct ethtool_coalesce *ecmd) + struct ethtool_coalesce *ecmd, + struct kernel_ethtool_coalesce *kernel_coal, + struct netlink_ext_ack *extack) { struct enic *enic = netdev_priv(netdev); struct enic_rx_coal *rxcoal = &enic->rx_coalesce_setting; @@ -343,7 +345,9 @@ static int enic_coalesce_valid(struct enic *enic, }
static int enic_set_coalesce(struct net_device *netdev, - struct ethtool_coalesce *ecmd) + struct ethtool_coalesce *ecmd, + struct kernel_ethtool_coalesce *kernel_coal, + struct netlink_ext_ack *extack) { struct enic *enic = netdev_priv(netdev); u32 tx_coalesce_usecs; diff --git a/drivers/net/ethernet/cortina/gemini.c b/drivers/net/ethernet/cortina/gemini.c index 8df6f081f244..75ea23d42055 100644 --- a/drivers/net/ethernet/cortina/gemini.c +++ b/drivers/net/ethernet/cortina/gemini.c @@ -2144,7 +2144,9 @@ static int gmac_set_ringparam(struct net_device *netdev, }
static int gmac_get_coalesce(struct net_device *netdev, - struct ethtool_coalesce *ecmd) + struct ethtool_coalesce *ecmd, + struct kernel_ethtool_coalesce *kernel_coal, + struct netlink_ext_ack *extack) { struct gemini_ethernet_port *port = netdev_priv(netdev);
@@ -2156,7 +2158,9 @@ static int gmac_get_coalesce(struct net_device *netdev, }
static int gmac_set_coalesce(struct net_device *netdev, - struct ethtool_coalesce *ecmd) + struct ethtool_coalesce *ecmd, + struct kernel_ethtool_coalesce *kernel_coal, + struct netlink_ext_ack *extack) { struct gemini_ethernet_port *port = netdev_priv(netdev);
diff --git a/drivers/net/ethernet/emulex/benet/be_ethtool.c b/drivers/net/ethernet/emulex/benet/be_ethtool.c index 99cc1c46fb30..f9955308b93d 100644 --- a/drivers/net/ethernet/emulex/benet/be_ethtool.c +++ b/drivers/net/ethernet/emulex/benet/be_ethtool.c @@ -315,7 +315,9 @@ static int be_read_dump_data(struct be_adapter *adapter, u32 dump_len, }
static int be_get_coalesce(struct net_device *netdev, - struct ethtool_coalesce *et) + struct ethtool_coalesce *et, + struct kernel_ethtool_coalesce *kernel_coal, + struct netlink_ext_ack *extack) { struct be_adapter *adapter = netdev_priv(netdev); struct be_aic_obj *aic = &adapter->aic_obj[0]; @@ -338,7 +340,9 @@ static int be_get_coalesce(struct net_device *netdev, * eqd cmd is issued in the worker thread. */ static int be_set_coalesce(struct net_device *netdev, - struct ethtool_coalesce *et) + struct ethtool_coalesce *et, + struct kernel_ethtool_coalesce *kernel_coal, + struct netlink_ext_ack *extack) { struct be_adapter *adapter = netdev_priv(netdev); struct be_aic_obj *aic = &adapter->aic_obj[0]; diff --git a/drivers/net/ethernet/freescale/dpaa/dpaa_ethtool.c b/drivers/net/ethernet/freescale/dpaa/dpaa_ethtool.c index 1268996b7030..763d2c7b5fb1 100644 --- a/drivers/net/ethernet/freescale/dpaa/dpaa_ethtool.c +++ b/drivers/net/ethernet/freescale/dpaa/dpaa_ethtool.c @@ -513,7 +513,9 @@ static int dpaa_get_ts_info(struct net_device *net_dev, }
static int dpaa_get_coalesce(struct net_device *dev, - struct ethtool_coalesce *c) + struct ethtool_coalesce *c, + struct kernel_ethtool_coalesce *kernel_coal, + struct netlink_ext_ack *extack) { struct qman_portal *portal; u32 period; @@ -530,7 +532,9 @@ static int dpaa_get_coalesce(struct net_device *dev, }
static int dpaa_set_coalesce(struct net_device *dev, - struct ethtool_coalesce *c) + struct ethtool_coalesce *c, + struct kernel_ethtool_coalesce *kernel_coal, + struct netlink_ext_ack *extack) { const cpumask_t *cpus = qman_affine_cpus(); bool needs_revert[NR_CPUS] = {false}; diff --git a/drivers/net/ethernet/freescale/enetc/enetc_ethtool.c b/drivers/net/ethernet/freescale/enetc/enetc_ethtool.c index 89e558135432..8d0ad3c5bb12 100644 --- a/drivers/net/ethernet/freescale/enetc/enetc_ethtool.c +++ b/drivers/net/ethernet/freescale/enetc/enetc_ethtool.c @@ -568,7 +568,9 @@ static void enetc_get_ringparam(struct net_device *ndev, }
static int enetc_get_coalesce(struct net_device *ndev, - struct ethtool_coalesce *ic) + struct ethtool_coalesce *ic, + struct kernel_ethtool_coalesce *kernel_coal, + struct netlink_ext_ack *extack) { struct enetc_ndev_priv *priv = netdev_priv(ndev); struct enetc_int_vector *v = priv->int_vector[0]; @@ -585,7 +587,9 @@ static int enetc_get_coalesce(struct net_device *ndev, }
static int enetc_set_coalesce(struct net_device *ndev, - struct ethtool_coalesce *ic) + struct ethtool_coalesce *ic, + struct kernel_ethtool_coalesce *kernel_coal, + struct netlink_ext_ack *extack) { struct enetc_ndev_priv *priv = netdev_priv(ndev); u32 rx_ictt, tx_ictt; diff --git a/drivers/net/ethernet/freescale/fec_main.c b/drivers/net/ethernet/freescale/fec_main.c index 960def41cc55..0aedee47be4e 100644 --- a/drivers/net/ethernet/freescale/fec_main.c +++ b/drivers/net/ethernet/freescale/fec_main.c @@ -2562,8 +2562,10 @@ static void fec_enet_itr_coal_set(struct net_device *ndev) } }
-static int -fec_enet_get_coalesce(struct net_device *ndev, struct ethtool_coalesce *ec) +static int fec_enet_get_coalesce(struct net_device *ndev, + struct ethtool_coalesce *ec, + struct kernel_ethtool_coalesce *kernel_coal, + struct netlink_ext_ack *extack) { struct fec_enet_private *fep = netdev_priv(ndev);
@@ -2579,8 +2581,10 @@ fec_enet_get_coalesce(struct net_device *ndev, struct ethtool_coalesce *ec) return 0; }
-static int -fec_enet_set_coalesce(struct net_device *ndev, struct ethtool_coalesce *ec) +static int fec_enet_set_coalesce(struct net_device *ndev, + struct ethtool_coalesce *ec, + struct kernel_ethtool_coalesce *kernel_coal, + struct netlink_ext_ack *extack) { struct fec_enet_private *fep = netdev_priv(ndev); struct device *dev = &fep->pdev->dev; @@ -2632,7 +2636,7 @@ static void fec_enet_itr_coal_init(struct net_device *ndev) ec.tx_coalesce_usecs = FEC_ITR_ICTT_DEFAULT; ec.tx_max_coalesced_frames = FEC_ITR_ICFT_DEFAULT;
- fec_enet_set_coalesce(ndev, &ec); + fec_enet_set_coalesce(ndev, &ec, NULL, NULL); }
static int fec_enet_get_tunable(struct net_device *netdev, diff --git a/drivers/net/ethernet/freescale/gianfar_ethtool.c b/drivers/net/ethernet/freescale/gianfar_ethtool.c index cc7d4f93da54..7b32ed29bf4c 100644 --- a/drivers/net/ethernet/freescale/gianfar_ethtool.c +++ b/drivers/net/ethernet/freescale/gianfar_ethtool.c @@ -243,7 +243,9 @@ static unsigned int gfar_ticks2usecs(struct gfar_private *priv, /* Get the coalescing parameters, and put them in the cvals * structure. */ static int gfar_gcoalesce(struct net_device *dev, - struct ethtool_coalesce *cvals) + struct ethtool_coalesce *cvals, + struct kernel_ethtool_coalesce *kernel_coal, + struct netlink_ext_ack *extack) { struct gfar_private *priv = netdev_priv(dev); struct gfar_priv_rx_q *rx_queue = NULL; @@ -280,7 +282,9 @@ static int gfar_gcoalesce(struct net_device *dev, * in order for coalescing to be active */ static int gfar_scoalesce(struct net_device *dev, - struct ethtool_coalesce *cvals) + struct ethtool_coalesce *cvals, + struct kernel_ethtool_coalesce *kernel_coal, + struct netlink_ext_ack *extack) { struct gfar_private *priv = netdev_priv(dev); int i, err = 0; diff --git a/drivers/net/ethernet/hisilicon/hip04_eth.c b/drivers/net/ethernet/hisilicon/hip04_eth.c index 12f6c2442a7a..09579c894983 100644 --- a/drivers/net/ethernet/hisilicon/hip04_eth.c +++ b/drivers/net/ethernet/hisilicon/hip04_eth.c @@ -796,7 +796,9 @@ static void hip04_tx_timeout_task(struct work_struct *work) }
static int hip04_get_coalesce(struct net_device *netdev, - struct ethtool_coalesce *ec) + struct ethtool_coalesce *ec, + struct kernel_ethtool_coalesce *kernel_coal, + struct netlink_ext_ack *extack) { struct hip04_priv *priv = netdev_priv(netdev);
@@ -807,7 +809,9 @@ static int hip04_get_coalesce(struct net_device *netdev, }
static int hip04_set_coalesce(struct net_device *netdev, - struct ethtool_coalesce *ec) + struct ethtool_coalesce *ec, + struct kernel_ethtool_coalesce *kernel_coal, + struct netlink_ext_ack *extack) { struct hip04_priv *priv = netdev_priv(netdev);
diff --git a/drivers/net/ethernet/hisilicon/hns/hns_ethtool.c b/drivers/net/ethernet/hisilicon/hns/hns_ethtool.c index a6e3f07caf99..bf0f0ca82cd0 100644 --- a/drivers/net/ethernet/hisilicon/hns/hns_ethtool.c +++ b/drivers/net/ethernet/hisilicon/hns/hns_ethtool.c @@ -731,11 +731,15 @@ static int hns_set_pauseparam(struct net_device *net_dev, * hns_get_coalesce - get coalesce info. * @net_dev: net device * @ec: coalesce info. + * @kernel_coal: ethtool CQE mode setting structure + * @extack: extack for reporting error messages * * Return 0 on success, negative on failure. */ static int hns_get_coalesce(struct net_device *net_dev, - struct ethtool_coalesce *ec) + struct ethtool_coalesce *ec, + struct kernel_ethtool_coalesce *kernel_coal, + struct netlink_ext_ack *extack) { struct hns_nic_priv *priv = netdev_priv(net_dev); struct hnae_ae_ops *ops; @@ -775,11 +779,15 @@ static int hns_get_coalesce(struct net_device *net_dev, * hns_set_coalesce - set coalesce info. * @net_dev: net device * @ec: coalesce info. + * @kernel_coal: ethtool CQE mode setting structure + * @extack: extack for reporting error messages * * Return 0 on success, negative on failure. */ static int hns_set_coalesce(struct net_device *net_dev, - struct ethtool_coalesce *ec) + struct ethtool_coalesce *ec, + struct kernel_ethtool_coalesce *kernel_coal, + struct netlink_ext_ack *extack) { struct hns_nic_priv *priv = netdev_priv(net_dev); struct hnae_ae_ops *ops; diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3_ethtool.c b/drivers/net/ethernet/hisilicon/hns3/hns3_ethtool.c index 835105015763..049be076fd18 100644 --- a/drivers/net/ethernet/hisilicon/hns3/hns3_ethtool.c +++ b/drivers/net/ethernet/hisilicon/hns3/hns3_ethtool.c @@ -1179,7 +1179,9 @@ static void hns3_get_channels(struct net_device *netdev, }
static int hns3_get_coalesce(struct net_device *netdev, - struct ethtool_coalesce *cmd) + struct ethtool_coalesce *cmd, + struct kernel_ethtool_coalesce *kernel_coal, + struct netlink_ext_ack *extack) { struct hns3_nic_priv *priv = netdev_priv(netdev); struct hns3_enet_coalesce *tx_coal = &priv->tx_coal; @@ -1361,7 +1363,9 @@ static void hns3_set_coalesce_per_queue(struct net_device *netdev, }
static int hns3_set_coalesce(struct net_device *netdev, - struct ethtool_coalesce *cmd) + struct ethtool_coalesce *cmd, + struct kernel_ethtool_coalesce *kernel_coal, + struct netlink_ext_ack *extack) { struct hnae3_handle *h = hns3_get_handle(netdev); struct hns3_nic_priv *priv = netdev_priv(netdev); diff --git a/drivers/net/ethernet/huawei/hinic/hinic_ethtool.c b/drivers/net/ethernet/huawei/hinic/hinic_ethtool.c index 2f870a866b38..28c7a34d204a 100644 --- a/drivers/net/ethernet/huawei/hinic/hinic_ethtool.c +++ b/drivers/net/ethernet/huawei/hinic/hinic_ethtool.c @@ -1470,13 +1470,17 @@ static int __hinic_set_coalesce(struct net_device *netdev, }
static int hinic_get_coalesce(struct net_device *netdev, - struct ethtool_coalesce *coal) + struct ethtool_coalesce *coal, + struct kernel_ethtool_coalesce *kernel_coal, + struct netlink_ext_ack *extack) { return __hinic_get_coalesce(netdev, coal, COALESCE_ALL_QUEUE); }
static int hinic_set_coalesce(struct net_device *netdev, - struct ethtool_coalesce *coal) + struct ethtool_coalesce *coal, + struct kernel_ethtool_coalesce *kernel_coal, + struct netlink_ext_ack *extack) { return __hinic_set_coalesce(netdev, coal, COALESCE_ALL_QUEUE); } diff --git a/drivers/net/ethernet/intel/e1000/e1000_ethtool.c b/drivers/net/ethernet/intel/e1000/e1000_ethtool.c index f976e9daa3d8..3ac044ad4f55 100644 --- a/drivers/net/ethernet/intel/e1000/e1000_ethtool.c +++ b/drivers/net/ethernet/intel/e1000/e1000_ethtool.c @@ -1739,7 +1739,9 @@ static int e1000_set_phys_id(struct net_device *netdev, }
static int e1000_get_coalesce(struct net_device *netdev, - struct ethtool_coalesce *ec) + struct ethtool_coalesce *ec, + struct kernel_ethtool_coalesce *kernel_coal, + struct netlink_ext_ack *extack) { struct e1000_adapter *adapter = netdev_priv(netdev);
@@ -1755,7 +1757,9 @@ static int e1000_get_coalesce(struct net_device *netdev, }
static int e1000_set_coalesce(struct net_device *netdev, - struct ethtool_coalesce *ec) + struct ethtool_coalesce *ec, + struct kernel_ethtool_coalesce *kernel_coal, + struct netlink_ext_ack *extack) { struct e1000_adapter *adapter = netdev_priv(netdev); struct e1000_hw *hw = &adapter->hw; diff --git a/drivers/net/ethernet/intel/e1000e/ethtool.c b/drivers/net/ethernet/intel/e1000e/ethtool.c index 06442e6bef73..9b2071d3ebdc 100644 --- a/drivers/net/ethernet/intel/e1000e/ethtool.c +++ b/drivers/net/ethernet/intel/e1000e/ethtool.c @@ -1991,7 +1991,9 @@ static int e1000_set_phys_id(struct net_device *netdev, }
static int e1000_get_coalesce(struct net_device *netdev, - struct ethtool_coalesce *ec) + struct ethtool_coalesce *ec, + struct kernel_ethtool_coalesce *kernel_coal, + struct netlink_ext_ack *extack) { struct e1000_adapter *adapter = netdev_priv(netdev);
@@ -2004,7 +2006,9 @@ static int e1000_get_coalesce(struct net_device *netdev, }
static int e1000_set_coalesce(struct net_device *netdev, - struct ethtool_coalesce *ec) + struct ethtool_coalesce *ec, + struct kernel_ethtool_coalesce *kernel_coal, + struct netlink_ext_ack *extack) { struct e1000_adapter *adapter = netdev_priv(netdev);
diff --git a/drivers/net/ethernet/intel/fm10k/fm10k_ethtool.c b/drivers/net/ethernet/intel/fm10k/fm10k_ethtool.c index 908fefaa6b85..57b78566c364 100644 --- a/drivers/net/ethernet/intel/fm10k/fm10k_ethtool.c +++ b/drivers/net/ethernet/intel/fm10k/fm10k_ethtool.c @@ -631,7 +631,9 @@ static int fm10k_set_ringparam(struct net_device *netdev, }
static int fm10k_get_coalesce(struct net_device *dev, - struct ethtool_coalesce *ec) + struct ethtool_coalesce *ec, + struct kernel_ethtool_coalesce *kernel_coal, + struct netlink_ext_ack *extack) { struct fm10k_intfc *interface = netdev_priv(dev);
@@ -645,7 +647,9 @@ static int fm10k_get_coalesce(struct net_device *dev, }
static int fm10k_set_coalesce(struct net_device *dev, - struct ethtool_coalesce *ec) + struct ethtool_coalesce *ec, + struct kernel_ethtool_coalesce *kernel_coal, + struct netlink_ext_ack *extack) { struct fm10k_intfc *interface = netdev_priv(dev); u16 tx_itr, rx_itr; diff --git a/drivers/net/ethernet/intel/i40e/i40e_ethtool.c b/drivers/net/ethernet/intel/i40e/i40e_ethtool.c index 5d48bc0c3f6c..ffd8f6f8f868 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_ethtool.c +++ b/drivers/net/ethernet/intel/i40e/i40e_ethtool.c @@ -2819,13 +2819,17 @@ static int __i40e_get_coalesce(struct net_device *netdev, * i40e_get_coalesce - get a netdev's coalesce settings * @netdev: the netdev to check * @ec: ethtool coalesce data structure + * @kernel_coal: ethtool CQE mode setting structure + * @extack: extack for reporting error messages * * Gets the coalesce settings for a particular netdev. Note that if user has * modified per-queue settings, this only guarantees to represent queue 0. See * __i40e_get_coalesce for more details. **/ static int i40e_get_coalesce(struct net_device *netdev, - struct ethtool_coalesce *ec) + struct ethtool_coalesce *ec, + struct kernel_ethtool_coalesce *kernel_coal, + struct netlink_ext_ack *extack) { return __i40e_get_coalesce(netdev, ec, -1); } @@ -2993,11 +2997,15 @@ static int __i40e_set_coalesce(struct net_device *netdev, * i40e_set_coalesce - set coalesce settings for every queue on the netdev * @netdev: the netdev to change * @ec: ethtool coalesce settings + * @kernel_coal: ethtool CQE mode setting structure + * @extack: extack for reporting error messages * * This will set each queue to the same coalesce settings. **/ static int i40e_set_coalesce(struct net_device *netdev, - struct ethtool_coalesce *ec) + struct ethtool_coalesce *ec, + struct kernel_ethtool_coalesce *kernel_coal, + struct netlink_ext_ack *extack) { return __i40e_set_coalesce(netdev, ec, -1); } diff --git a/drivers/net/ethernet/intel/iavf/iavf_ethtool.c b/drivers/net/ethernet/intel/iavf/iavf_ethtool.c index c93567f4d0f7..63458ee7021d 100644 --- a/drivers/net/ethernet/intel/iavf/iavf_ethtool.c +++ b/drivers/net/ethernet/intel/iavf/iavf_ethtool.c @@ -685,6 +685,8 @@ static int __iavf_get_coalesce(struct net_device *netdev, * iavf_get_coalesce - Get interrupt coalescing settings * @netdev: network interface device structure * @ec: ethtool coalesce structure + * @kernel_coal: ethtool CQE mode setting structure + * @extack: extack for reporting error messages * * Returns current coalescing settings. This is referred to elsewhere in the * driver as Interrupt Throttle Rate, as this is how the hardware describes @@ -692,7 +694,9 @@ static int __iavf_get_coalesce(struct net_device *netdev, * only represents the settings of queue 0. **/ static int iavf_get_coalesce(struct net_device *netdev, - struct ethtool_coalesce *ec) + struct ethtool_coalesce *ec, + struct kernel_ethtool_coalesce *kernel_coal, + struct netlink_ext_ack *extack) { return __iavf_get_coalesce(netdev, ec, -1); } @@ -804,11 +808,15 @@ static int __iavf_set_coalesce(struct net_device *netdev, * iavf_set_coalesce - Set interrupt coalescing settings * @netdev: network interface device structure * @ec: ethtool coalesce structure + * @kernel_coal: ethtool CQE mode setting structure + * @extack: extack for reporting error messages * * Change current coalescing settings for every queue. **/ static int iavf_set_coalesce(struct net_device *netdev, - struct ethtool_coalesce *ec) + struct ethtool_coalesce *ec, + struct kernel_ethtool_coalesce *kernel_coal, + struct netlink_ext_ack *extack) { return __iavf_set_coalesce(netdev, ec, -1); } diff --git a/drivers/net/ethernet/intel/ice/ice_ethtool.c b/drivers/net/ethernet/intel/ice/ice_ethtool.c index a7975afecf70..a80a24e16f50 100644 --- a/drivers/net/ethernet/intel/ice/ice_ethtool.c +++ b/drivers/net/ethernet/intel/ice/ice_ethtool.c @@ -3577,8 +3577,10 @@ __ice_get_coalesce(struct net_device *netdev, struct ethtool_coalesce *ec, return 0; }
-static int -ice_get_coalesce(struct net_device *netdev, struct ethtool_coalesce *ec) +static int ice_get_coalesce(struct net_device *netdev, + struct ethtool_coalesce *ec, + struct kernel_ethtool_coalesce *kernel_coal, + struct netlink_ext_ack *extack) { return __ice_get_coalesce(netdev, ec, -1); } @@ -3788,8 +3790,10 @@ __ice_set_coalesce(struct net_device *netdev, struct ethtool_coalesce *ec, return 0; }
-static int -ice_set_coalesce(struct net_device *netdev, struct ethtool_coalesce *ec) +static int ice_set_coalesce(struct net_device *netdev, + struct ethtool_coalesce *ec, + struct kernel_ethtool_coalesce *kernel_coal, + struct netlink_ext_ack *extack) { return __ice_set_coalesce(netdev, ec, -1); } diff --git a/drivers/net/ethernet/intel/igb/igb_ethtool.c b/drivers/net/ethernet/intel/igb/igb_ethtool.c index 28baf203459a..c75bbbd39eff 100644 --- a/drivers/net/ethernet/intel/igb/igb_ethtool.c +++ b/drivers/net/ethernet/intel/igb/igb_ethtool.c @@ -2182,7 +2182,9 @@ static int igb_set_phys_id(struct net_device *netdev, }
static int igb_set_coalesce(struct net_device *netdev, - struct ethtool_coalesce *ec) + struct ethtool_coalesce *ec, + struct kernel_ethtool_coalesce *kernel_coal, + struct netlink_ext_ack *extack) { struct igb_adapter *adapter = netdev_priv(netdev); int i; @@ -2238,7 +2240,9 @@ static int igb_set_coalesce(struct net_device *netdev, }
static int igb_get_coalesce(struct net_device *netdev, - struct ethtool_coalesce *ec) + struct ethtool_coalesce *ec, + struct kernel_ethtool_coalesce *kernel_coal, + struct netlink_ext_ack *extack) { struct igb_adapter *adapter = netdev_priv(netdev);
diff --git a/drivers/net/ethernet/intel/igbvf/ethtool.c b/drivers/net/ethernet/intel/igbvf/ethtool.c index f4835eb62fee..06e5bd646a0e 100644 --- a/drivers/net/ethernet/intel/igbvf/ethtool.c +++ b/drivers/net/ethernet/intel/igbvf/ethtool.c @@ -314,7 +314,9 @@ static int igbvf_set_wol(struct net_device *netdev, }
static int igbvf_get_coalesce(struct net_device *netdev, - struct ethtool_coalesce *ec) + struct ethtool_coalesce *ec, + struct kernel_ethtool_coalesce *kernel_coal, + struct netlink_ext_ack *extack) { struct igbvf_adapter *adapter = netdev_priv(netdev);
@@ -327,7 +329,9 @@ static int igbvf_get_coalesce(struct net_device *netdev, }
static int igbvf_set_coalesce(struct net_device *netdev, - struct ethtool_coalesce *ec) + struct ethtool_coalesce *ec, + struct kernel_ethtool_coalesce *kernel_coal, + struct netlink_ext_ack *extack) { struct igbvf_adapter *adapter = netdev_priv(netdev); struct e1000_hw *hw = &adapter->hw; diff --git a/drivers/net/ethernet/intel/igc/igc_ethtool.c b/drivers/net/ethernet/intel/igc/igc_ethtool.c index da259cd59add..97ce3397f47f 100644 --- a/drivers/net/ethernet/intel/igc/igc_ethtool.c +++ b/drivers/net/ethernet/intel/igc/igc_ethtool.c @@ -857,7 +857,9 @@ static void igc_ethtool_get_stats(struct net_device *netdev, }
static int igc_ethtool_get_coalesce(struct net_device *netdev, - struct ethtool_coalesce *ec) + struct ethtool_coalesce *ec, + struct kernel_ethtool_coalesce *kernel_coal, + struct netlink_ext_ack *extack) { struct igc_adapter *adapter = netdev_priv(netdev);
@@ -877,7 +879,9 @@ static int igc_ethtool_get_coalesce(struct net_device *netdev, }
static int igc_ethtool_set_coalesce(struct net_device *netdev, - struct ethtool_coalesce *ec) + struct ethtool_coalesce *ec, + struct kernel_ethtool_coalesce *kernel_coal, + struct netlink_ext_ack *extack) { struct igc_adapter *adapter = netdev_priv(netdev); int i; diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_ethtool.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_ethtool.c index a280aa34ca1d..f38d03912bca 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_ethtool.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_ethtool.c @@ -2370,7 +2370,9 @@ static int ixgbe_set_phys_id(struct net_device *netdev, }
static int ixgbe_get_coalesce(struct net_device *netdev, - struct ethtool_coalesce *ec) + struct ethtool_coalesce *ec, + struct kernel_ethtool_coalesce *kernel_coal, + struct netlink_ext_ack *extack) { struct ixgbe_adapter *adapter = netdev_priv(netdev);
@@ -2424,7 +2426,9 @@ static bool ixgbe_update_rsc(struct ixgbe_adapter *adapter) }
static int ixgbe_set_coalesce(struct net_device *netdev, - struct ethtool_coalesce *ec) + struct ethtool_coalesce *ec, + struct kernel_ethtool_coalesce *kernel_coal, + struct netlink_ext_ack *extack) { struct ixgbe_adapter *adapter = netdev_priv(netdev); struct ixgbe_q_vector *q_vector; diff --git a/drivers/net/ethernet/intel/ixgbevf/ethtool.c b/drivers/net/ethernet/intel/ixgbevf/ethtool.c index e49fb1cd9a99..8380f905e708 100644 --- a/drivers/net/ethernet/intel/ixgbevf/ethtool.c +++ b/drivers/net/ethernet/intel/ixgbevf/ethtool.c @@ -787,7 +787,9 @@ static int ixgbevf_nway_reset(struct net_device *netdev) }
static int ixgbevf_get_coalesce(struct net_device *netdev, - struct ethtool_coalesce *ec) + struct ethtool_coalesce *ec, + struct kernel_ethtool_coalesce *kernel_coal, + struct netlink_ext_ack *extack) { struct ixgbevf_adapter *adapter = netdev_priv(netdev);
@@ -811,7 +813,9 @@ static int ixgbevf_get_coalesce(struct net_device *netdev, }
static int ixgbevf_set_coalesce(struct net_device *netdev, - struct ethtool_coalesce *ec) + struct ethtool_coalesce *ec, + struct kernel_ethtool_coalesce *kernel_coal, + struct netlink_ext_ack *extack) { struct ixgbevf_adapter *adapter = netdev_priv(netdev); struct ixgbevf_q_vector *q_vector; diff --git a/drivers/net/ethernet/jme.c b/drivers/net/ethernet/jme.c index e9efe074edc1..4185ca3dd575 100644 --- a/drivers/net/ethernet/jme.c +++ b/drivers/net/ethernet/jme.c @@ -2401,8 +2401,10 @@ jme_get_regs(struct net_device *netdev, struct ethtool_regs *regs, void *p) mdio_memcpy(jme, p32, JME_PHY_REG_NR); }
-static int -jme_get_coalesce(struct net_device *netdev, struct ethtool_coalesce *ecmd) +static int jme_get_coalesce(struct net_device *netdev, + struct ethtool_coalesce *ecmd, + struct kernel_ethtool_coalesce *kernel_coal, + struct netlink_ext_ack *extack) { struct jme_adapter *jme = netdev_priv(netdev);
@@ -2438,8 +2440,10 @@ jme_get_coalesce(struct net_device *netdev, struct ethtool_coalesce *ecmd) return 0; }
-static int -jme_set_coalesce(struct net_device *netdev, struct ethtool_coalesce *ecmd) +static int jme_set_coalesce(struct net_device *netdev, + struct ethtool_coalesce *ecmd, + struct kernel_ethtool_coalesce *kernel_coal, + struct netlink_ext_ack *extack) { struct jme_adapter *jme = netdev_priv(netdev); struct dynpcc_info *dpi = &(jme->dpi); diff --git a/drivers/net/ethernet/marvell/mv643xx_eth.c b/drivers/net/ethernet/marvell/mv643xx_eth.c index 90e6111ce534..1f65a6e5685c 100644 --- a/drivers/net/ethernet/marvell/mv643xx_eth.c +++ b/drivers/net/ethernet/marvell/mv643xx_eth.c @@ -1609,8 +1609,10 @@ static void mv643xx_eth_get_drvinfo(struct net_device *dev, strlcpy(drvinfo->bus_info, "platform", sizeof(drvinfo->bus_info)); }
-static int -mv643xx_eth_get_coalesce(struct net_device *dev, struct ethtool_coalesce *ec) +static int mv643xx_eth_get_coalesce(struct net_device *dev, + struct ethtool_coalesce *ec, + struct kernel_ethtool_coalesce *kernel_coal, + struct netlink_ext_ack *extack) { struct mv643xx_eth_private *mp = netdev_priv(dev);
@@ -1620,8 +1622,10 @@ mv643xx_eth_get_coalesce(struct net_device *dev, struct ethtool_coalesce *ec) return 0; }
-static int -mv643xx_eth_set_coalesce(struct net_device *dev, struct ethtool_coalesce *ec) +static int mv643xx_eth_set_coalesce(struct net_device *dev, + struct ethtool_coalesce *ec, + struct kernel_ethtool_coalesce *kernel_coal, + struct netlink_ext_ack *extack) { struct mv643xx_eth_private *mp = netdev_priv(dev);
diff --git a/drivers/net/ethernet/marvell/mvneta.c b/drivers/net/ethernet/marvell/mvneta.c index c6b735b30515..d8152d227fbd 100644 --- a/drivers/net/ethernet/marvell/mvneta.c +++ b/drivers/net/ethernet/marvell/mvneta.c @@ -4480,8 +4480,11 @@ static int mvneta_ethtool_nway_reset(struct net_device *dev) }
/* Set interrupt coalescing for ethtools */ -static int mvneta_ethtool_set_coalesce(struct net_device *dev, - struct ethtool_coalesce *c) +static int +mvneta_ethtool_set_coalesce(struct net_device *dev, + struct ethtool_coalesce *c, + struct kernel_ethtool_coalesce *kernel_coal, + struct netlink_ext_ack *extack) { struct mvneta_port *pp = netdev_priv(dev); int queue; @@ -4504,8 +4507,11 @@ static int mvneta_ethtool_set_coalesce(struct net_device *dev, }
/* get coalescing for ethtools */ -static int mvneta_ethtool_get_coalesce(struct net_device *dev, - struct ethtool_coalesce *c) +static int +mvneta_ethtool_get_coalesce(struct net_device *dev, + struct ethtool_coalesce *c, + struct kernel_ethtool_coalesce *kernel_coal, + struct netlink_ext_ack *extack) { struct mvneta_port *pp = netdev_priv(dev);
diff --git a/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c b/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c index 6aa13c9f9fc9..de4a3b131b93 100644 --- a/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c +++ b/drivers/net/ethernet/marvell/mvpp2/mvpp2_main.c @@ -5004,8 +5004,11 @@ static int mvpp2_ethtool_nway_reset(struct net_device *dev) }
/* Set interrupt coalescing for ethtools */ -static int mvpp2_ethtool_set_coalesce(struct net_device *dev, - struct ethtool_coalesce *c) +static int +mvpp2_ethtool_set_coalesce(struct net_device *dev, + struct ethtool_coalesce *c, + struct kernel_ethtool_coalesce *kernel_coal, + struct netlink_ext_ack *extack) { struct mvpp2_port *port = netdev_priv(dev); int queue; @@ -5037,8 +5040,11 @@ static int mvpp2_ethtool_set_coalesce(struct net_device *dev, }
/* get coalescing for ethtools */ -static int mvpp2_ethtool_get_coalesce(struct net_device *dev, - struct ethtool_coalesce *c) +static int +mvpp2_ethtool_get_coalesce(struct net_device *dev, + struct ethtool_coalesce *c, + struct kernel_ethtool_coalesce *kernel_coal, + struct netlink_ext_ack *extack) { struct mvpp2_port *port = netdev_priv(dev);
diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_ethtool.c b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_ethtool.c index 662fb80dbb9d..257c2b3d9c9a 100644 --- a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_ethtool.c +++ b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_ethtool.c @@ -348,7 +348,9 @@ static int otx2_set_ringparam(struct net_device *netdev, }
static int otx2_get_coalesce(struct net_device *netdev, - struct ethtool_coalesce *cmd) + struct ethtool_coalesce *cmd, + struct kernel_ethtool_coalesce *kernel_coal, + struct netlink_ext_ack *extack) { struct otx2_nic *pfvf = netdev_priv(netdev); struct otx2_hw *hw = &pfvf->hw; @@ -362,7 +364,9 @@ static int otx2_get_coalesce(struct net_device *netdev, }
static int otx2_set_coalesce(struct net_device *netdev, - struct ethtool_coalesce *ec) + struct ethtool_coalesce *ec, + struct kernel_ethtool_coalesce *kernel_coal, + struct netlink_ext_ack *extack) { struct otx2_nic *pfvf = netdev_priv(netdev); struct otx2_hw *hw = &pfvf->hw; diff --git a/drivers/net/ethernet/marvell/skge.c b/drivers/net/ethernet/marvell/skge.c index 8a9c0f490bfb..715b2483e7fc 100644 --- a/drivers/net/ethernet/marvell/skge.c +++ b/drivers/net/ethernet/marvell/skge.c @@ -615,7 +615,9 @@ static inline u32 skge_usecs2clk(const struct skge_hw *hw, u32 usec) }
static int skge_get_coalesce(struct net_device *dev, - struct ethtool_coalesce *ecmd) + struct ethtool_coalesce *ecmd, + struct kernel_ethtool_coalesce *kernel_coal, + struct netlink_ext_ack *extack) { struct skge_port *skge = netdev_priv(dev); struct skge_hw *hw = skge->hw; @@ -639,7 +641,9 @@ static int skge_get_coalesce(struct net_device *dev,
/* Note: interrupt timer is per board, but can turn on/off per port */ static int skge_set_coalesce(struct net_device *dev, - struct ethtool_coalesce *ecmd) + struct ethtool_coalesce *ecmd, + struct kernel_ethtool_coalesce *kernel_coal, + struct netlink_ext_ack *extack) { struct skge_port *skge = netdev_priv(dev); struct skge_hw *hw = skge->hw; diff --git a/drivers/net/ethernet/marvell/sky2.c b/drivers/net/ethernet/marvell/sky2.c index 25981a7a43b5..e049fd653669 100644 --- a/drivers/net/ethernet/marvell/sky2.c +++ b/drivers/net/ethernet/marvell/sky2.c @@ -4050,7 +4050,9 @@ static int sky2_set_pauseparam(struct net_device *dev, }
static int sky2_get_coalesce(struct net_device *dev, - struct ethtool_coalesce *ecmd) + struct ethtool_coalesce *ecmd, + struct kernel_ethtool_coalesce *kernel_coal, + struct netlink_ext_ack *extack) { struct sky2_port *sky2 = netdev_priv(dev); struct sky2_hw *hw = sky2->hw; @@ -4085,7 +4087,9 @@ static int sky2_get_coalesce(struct net_device *dev,
/* Note: this affect both ports */ static int sky2_set_coalesce(struct net_device *dev, - struct ethtool_coalesce *ecmd) + struct ethtool_coalesce *ecmd, + struct kernel_ethtool_coalesce *kernel_coal, + struct netlink_ext_ack *extack) { struct sky2_port *sky2 = netdev_priv(dev); struct sky2_hw *hw = sky2->hw; diff --git a/drivers/net/ethernet/mellanox/mlx4/en_ethtool.c b/drivers/net/ethernet/mellanox/mlx4/en_ethtool.c index 3616b77caa0a..ef518b1040f7 100644 --- a/drivers/net/ethernet/mellanox/mlx4/en_ethtool.c +++ b/drivers/net/ethernet/mellanox/mlx4/en_ethtool.c @@ -998,7 +998,9 @@ mlx4_en_set_link_ksettings(struct net_device *dev, }
static int mlx4_en_get_coalesce(struct net_device *dev, - struct ethtool_coalesce *coal) + struct ethtool_coalesce *coal, + struct kernel_ethtool_coalesce *kernel_coal, + struct netlink_ext_ack *extack) { struct mlx4_en_priv *priv = netdev_priv(dev);
@@ -1020,7 +1022,9 @@ static int mlx4_en_get_coalesce(struct net_device *dev, }
static int mlx4_en_set_coalesce(struct net_device *dev, - struct ethtool_coalesce *coal) + struct ethtool_coalesce *coal, + struct kernel_ethtool_coalesce *kernel_coal, + struct netlink_ext_ack *extack) { struct mlx4_en_priv *priv = netdev_priv(dev);
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_ethtool.c b/drivers/net/ethernet/mellanox/mlx5/core/en_ethtool.c index bc7c1962f9e6..c798e634885f 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_ethtool.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_ethtool.c @@ -511,7 +511,9 @@ int mlx5e_ethtool_get_coalesce(struct mlx5e_priv *priv, }
static int mlx5e_get_coalesce(struct net_device *netdev, - struct ethtool_coalesce *coal) + struct ethtool_coalesce *coal, + struct kernel_ethtool_coalesce *kernel_coal, + struct netlink_ext_ack *extack) { struct mlx5e_priv *priv = netdev_priv(netdev);
@@ -632,7 +634,9 @@ int mlx5e_ethtool_set_coalesce(struct mlx5e_priv *priv, }
static int mlx5e_set_coalesce(struct net_device *netdev, - struct ethtool_coalesce *coal) + struct ethtool_coalesce *coal, + struct kernel_ethtool_coalesce *kernel_coal, + struct netlink_ext_ack *extack) { struct mlx5e_priv *priv = netdev_priv(netdev);
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_rep.c b/drivers/net/ethernet/mellanox/mlx5/core/en_rep.c index 304435e56117..5bbf8e686708 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_rep.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_rep.c @@ -258,7 +258,9 @@ static int mlx5e_rep_set_channels(struct net_device *dev, }
static int mlx5e_rep_get_coalesce(struct net_device *netdev, - struct ethtool_coalesce *coal) + struct ethtool_coalesce *coal, + struct kernel_ethtool_coalesce *kernel_coal, + struct netlink_ext_ack *extack) { struct mlx5e_priv *priv = netdev_priv(netdev);
@@ -266,7 +268,9 @@ static int mlx5e_rep_get_coalesce(struct net_device *netdev, }
static int mlx5e_rep_set_coalesce(struct net_device *netdev, - struct ethtool_coalesce *coal) + struct ethtool_coalesce *coal, + struct kernel_ethtool_coalesce *kernel_coal, + struct netlink_ext_ack *extack) { struct mlx5e_priv *priv = netdev_priv(netdev);
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/ipoib/ethtool.c b/drivers/net/ethernet/mellanox/mlx5/core/ipoib/ethtool.c index cac8f085b16d..04c9a138029b 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/ipoib/ethtool.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/ipoib/ethtool.c @@ -99,7 +99,9 @@ static void mlx5i_get_channels(struct net_device *dev, }
static int mlx5i_set_coalesce(struct net_device *netdev, - struct ethtool_coalesce *coal) + struct ethtool_coalesce *coal, + struct kernel_ethtool_coalesce *kernel_coal, + struct netlink_ext_ack *extack) { struct mlx5e_priv *priv = mlx5i_epriv(netdev);
@@ -107,7 +109,9 @@ static int mlx5i_set_coalesce(struct net_device *netdev, }
static int mlx5i_get_coalesce(struct net_device *netdev, - struct ethtool_coalesce *coal) + struct ethtool_coalesce *coal, + struct kernel_ethtool_coalesce *kernel_coal, + struct netlink_ext_ack *extack) { struct mlx5e_priv *priv = mlx5i_epriv(netdev);
diff --git a/drivers/net/ethernet/myricom/myri10ge/myri10ge.c b/drivers/net/ethernet/myricom/myri10ge/myri10ge.c index fc99ad8e4a38..6b69512e742e 100644 --- a/drivers/net/ethernet/myricom/myri10ge/myri10ge.c +++ b/drivers/net/ethernet/myricom/myri10ge/myri10ge.c @@ -1651,8 +1651,10 @@ myri10ge_get_drvinfo(struct net_device *netdev, struct ethtool_drvinfo *info) strlcpy(info->bus_info, pci_name(mgp->pdev), sizeof(info->bus_info)); }
-static int -myri10ge_get_coalesce(struct net_device *netdev, struct ethtool_coalesce *coal) +static int myri10ge_get_coalesce(struct net_device *netdev, + struct ethtool_coalesce *coal, + struct kernel_ethtool_coalesce *kernel_coal, + struct netlink_ext_ack *extack) { struct myri10ge_priv *mgp = netdev_priv(netdev);
@@ -1660,8 +1662,10 @@ myri10ge_get_coalesce(struct net_device *netdev, struct ethtool_coalesce *coal) return 0; }
-static int -myri10ge_set_coalesce(struct net_device *netdev, struct ethtool_coalesce *coal) +static int myri10ge_set_coalesce(struct net_device *netdev, + struct ethtool_coalesce *coal, + struct kernel_ethtool_coalesce *kernel_coal, + struct netlink_ext_ack *extack) { struct myri10ge_priv *mgp = netdev_priv(netdev);
diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net_ethtool.c b/drivers/net/ethernet/netronome/nfp/nfp_net_ethtool.c index 9c9ae33d84ce..928e58578c81 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_net_ethtool.c +++ b/drivers/net/ethernet/netronome/nfp/nfp_net_ethtool.c @@ -1087,7 +1087,9 @@ static void nfp_net_get_regs(struct net_device *netdev, }
static int nfp_net_get_coalesce(struct net_device *netdev, - struct ethtool_coalesce *ec) + struct ethtool_coalesce *ec, + struct kernel_ethtool_coalesce *kernel_coal, + struct netlink_ext_ack *extack) { struct nfp_net *nn = netdev_priv(netdev);
@@ -1336,7 +1338,9 @@ nfp_port_get_module_eeprom(struct net_device *netdev, }
static int nfp_net_set_coalesce(struct net_device *netdev, - struct ethtool_coalesce *ec) + struct ethtool_coalesce *ec, + struct kernel_ethtool_coalesce *kernel_coal, + struct netlink_ext_ack *extack) { struct nfp_net *nn = netdev_priv(netdev); unsigned int factor; diff --git a/drivers/net/ethernet/netswift/txgbe/txgbe_ethtool.c b/drivers/net/ethernet/netswift/txgbe/txgbe_ethtool.c index a6039747548c..ca7265259a36 100644 --- a/drivers/net/ethernet/netswift/txgbe/txgbe_ethtool.c +++ b/drivers/net/ethernet/netswift/txgbe/txgbe_ethtool.c @@ -2215,7 +2215,9 @@ static int txgbe_set_phys_id(struct net_device *netdev, }
static int txgbe_get_coalesce(struct net_device *netdev, - struct ethtool_coalesce *ec) + struct ethtool_coalesce *ec, + struct kernel_ethtool_coalesce *kernel_coal, + struct netlink_ext_ack *extack) { struct txgbe_adapter *adapter = netdev_priv(netdev);
@@ -2271,7 +2273,9 @@ static bool txgbe_update_rsc(struct txgbe_adapter *adapter) }
static int txgbe_set_coalesce(struct net_device *netdev, - struct ethtool_coalesce *ec) + struct ethtool_coalesce *ec, + struct kernel_ethtool_coalesce *kernel_coal, + struct netlink_ext_ack *extack) { struct txgbe_adapter *adapter = netdev_priv(netdev); struct txgbe_hw *hw = &adapter->hw; diff --git a/drivers/net/ethernet/ni/nixge.c b/drivers/net/ethernet/ni/nixge.c index a6861df9904f..17205264c2c1 100644 --- a/drivers/net/ethernet/ni/nixge.c +++ b/drivers/net/ethernet/ni/nixge.c @@ -993,8 +993,11 @@ static void nixge_ethtools_get_drvinfo(struct net_device *ndev, strlcpy(ed->bus_info, "platform", sizeof(ed->bus_info)); }
-static int nixge_ethtools_get_coalesce(struct net_device *ndev, - struct ethtool_coalesce *ecoalesce) +static int +nixge_ethtools_get_coalesce(struct net_device *ndev, + struct ethtool_coalesce *ecoalesce, + struct kernel_ethtool_coalesce *kernel_coal, + struct netlink_ext_ack *extack) { struct nixge_priv *priv = netdev_priv(ndev); u32 regval = 0; @@ -1008,8 +1011,11 @@ static int nixge_ethtools_get_coalesce(struct net_device *ndev, return 0; }
-static int nixge_ethtools_set_coalesce(struct net_device *ndev, - struct ethtool_coalesce *ecoalesce) +static int +nixge_ethtools_set_coalesce(struct net_device *ndev, + struct ethtool_coalesce *ecoalesce, + struct kernel_ethtool_coalesce *kernel_coal, + struct netlink_ext_ack *extack) { struct nixge_priv *priv = netdev_priv(ndev);
diff --git a/drivers/net/ethernet/pensando/ionic/ionic_ethtool.c b/drivers/net/ethernet/pensando/ionic/ionic_ethtool.c index 35c72d4a78b3..f968cd26c56e 100644 --- a/drivers/net/ethernet/pensando/ionic/ionic_ethtool.c +++ b/drivers/net/ethernet/pensando/ionic/ionic_ethtool.c @@ -404,7 +404,9 @@ static int ionic_set_fecparam(struct net_device *netdev, }
static int ionic_get_coalesce(struct net_device *netdev, - struct ethtool_coalesce *coalesce) + struct ethtool_coalesce *coalesce, + struct kernel_ethtool_coalesce *kernel_coal, + struct netlink_ext_ack *extack) { struct ionic_lif *lif = netdev_priv(netdev);
@@ -422,7 +424,9 @@ static int ionic_get_coalesce(struct net_device *netdev, }
static int ionic_set_coalesce(struct net_device *netdev, - struct ethtool_coalesce *coalesce) + struct ethtool_coalesce *coalesce, + struct kernel_ethtool_coalesce *kernel_coal, + struct netlink_ext_ack *extack) { struct ionic_lif *lif = netdev_priv(netdev); struct ionic_identity *ident; diff --git a/drivers/net/ethernet/qlogic/netxen/netxen_nic_ethtool.c b/drivers/net/ethernet/qlogic/netxen/netxen_nic_ethtool.c index dd22cb056d03..a075643f5826 100644 --- a/drivers/net/ethernet/qlogic/netxen/netxen_nic_ethtool.c +++ b/drivers/net/ethernet/qlogic/netxen/netxen_nic_ethtool.c @@ -731,7 +731,9 @@ netxen_nic_set_wol(struct net_device *dev, struct ethtool_wolinfo *wol) * firmware coalescing to default. */ static int netxen_set_intr_coalesce(struct net_device *netdev, - struct ethtool_coalesce *ethcoal) + struct ethtool_coalesce *ethcoal, + struct kernel_ethtool_coalesce *kernel_coal, + struct netlink_ext_ack *extack) { struct netxen_adapter *adapter = netdev_priv(netdev);
@@ -775,7 +777,9 @@ static int netxen_set_intr_coalesce(struct net_device *netdev, }
static int netxen_get_intr_coalesce(struct net_device *netdev, - struct ethtool_coalesce *ethcoal) + struct ethtool_coalesce *ethcoal, + struct kernel_ethtool_coalesce *kernel_coal, + struct netlink_ext_ack *extack) { struct netxen_adapter *adapter = netdev_priv(netdev);
diff --git a/drivers/net/ethernet/qlogic/qede/qede_ethtool.c b/drivers/net/ethernet/qlogic/qede/qede_ethtool.c index bedbb85a179a..4360c6602356 100644 --- a/drivers/net/ethernet/qlogic/qede/qede_ethtool.c +++ b/drivers/net/ethernet/qlogic/qede/qede_ethtool.c @@ -760,7 +760,9 @@ static int qede_flash_device(struct net_device *dev, }
static int qede_get_coalesce(struct net_device *dev, - struct ethtool_coalesce *coal) + struct ethtool_coalesce *coal, + struct kernel_ethtool_coalesce *kernel_coal, + struct netlink_ext_ack *extack) { void *rx_handle = NULL, *tx_handle = NULL; struct qede_dev *edev = netdev_priv(dev); @@ -820,7 +822,9 @@ static int qede_get_coalesce(struct net_device *dev, }
static int qede_set_coalesce(struct net_device *dev, - struct ethtool_coalesce *coal) + struct ethtool_coalesce *coal, + struct kernel_ethtool_coalesce *kernel_coal, + struct netlink_ext_ack *extack) { struct qede_dev *edev = netdev_priv(dev); struct qede_fastpath *fp; diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_ethtool.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_ethtool.c index d8f0863b3934..7ac4b5f33378 100644 --- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_ethtool.c +++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_ethtool.c @@ -1527,7 +1527,9 @@ qlcnic_set_wol(struct net_device *dev, struct ethtool_wolinfo *wol) * firmware coalescing to default. */ static int qlcnic_set_intr_coalesce(struct net_device *netdev, - struct ethtool_coalesce *ethcoal) + struct ethtool_coalesce *ethcoal, + struct kernel_ethtool_coalesce *kernel_coal, + struct netlink_ext_ack *extack) { struct qlcnic_adapter *adapter = netdev_priv(netdev); int err; @@ -1551,7 +1553,9 @@ static int qlcnic_set_intr_coalesce(struct net_device *netdev, }
static int qlcnic_get_intr_coalesce(struct net_device *netdev, - struct ethtool_coalesce *ethcoal) + struct ethtool_coalesce *ethcoal, + struct kernel_ethtool_coalesce *kernel_coal, + struct netlink_ext_ack *extack) { struct qlcnic_adapter *adapter = netdev_priv(netdev);
diff --git a/drivers/net/ethernet/realtek/r8169_main.c b/drivers/net/ethernet/realtek/r8169_main.c index 3bb36f4a984e..e6e7e54f20a9 100644 --- a/drivers/net/ethernet/realtek/r8169_main.c +++ b/drivers/net/ethernet/realtek/r8169_main.c @@ -1753,7 +1753,10 @@ rtl_coalesce_info(struct rtl8169_private *tp) return ERR_PTR(-ELNRNG); }
-static int rtl_get_coalesce(struct net_device *dev, struct ethtool_coalesce *ec) +static int rtl_get_coalesce(struct net_device *dev, + struct ethtool_coalesce *ec, + struct kernel_ethtool_coalesce *kernel_coal, + struct netlink_ext_ack *extack) { struct rtl8169_private *tp = netdev_priv(dev); const struct rtl_coalesce_info *ci; @@ -1811,7 +1814,10 @@ static int rtl_coalesce_choose_scale(struct rtl8169_private *tp, u32 usec, return -ERANGE; }
-static int rtl_set_coalesce(struct net_device *dev, struct ethtool_coalesce *ec) +static int rtl_set_coalesce(struct net_device *dev, + struct ethtool_coalesce *ec, + struct kernel_ethtool_coalesce *kernel_coal, + struct netlink_ext_ack *extack) { struct rtl8169_private *tp = netdev_priv(dev); u32 tx_fr = ec->tx_max_coalesced_frames; diff --git a/drivers/net/ethernet/samsung/sxgbe/sxgbe_ethtool.c b/drivers/net/ethernet/samsung/sxgbe/sxgbe_ethtool.c index 7f8b10c49660..98edb01024f0 100644 --- a/drivers/net/ethernet/samsung/sxgbe/sxgbe_ethtool.c +++ b/drivers/net/ethernet/samsung/sxgbe/sxgbe_ethtool.c @@ -274,7 +274,9 @@ static u32 sxgbe_usec2riwt(u32 usec, struct sxgbe_priv_data *priv) }
static int sxgbe_get_coalesce(struct net_device *dev, - struct ethtool_coalesce *ec) + struct ethtool_coalesce *ec, + struct kernel_ethtool_coalesce *kernel_coal, + struct netlink_ext_ack *extack) { struct sxgbe_priv_data *priv = netdev_priv(dev);
@@ -285,7 +287,9 @@ static int sxgbe_get_coalesce(struct net_device *dev, }
static int sxgbe_set_coalesce(struct net_device *dev, - struct ethtool_coalesce *ec) + struct ethtool_coalesce *ec, + struct kernel_ethtool_coalesce *kernel_coal, + struct netlink_ext_ack *extack) { struct sxgbe_priv_data *priv = netdev_priv(dev); unsigned int rx_riwt; diff --git a/drivers/net/ethernet/sfc/ethtool.c b/drivers/net/ethernet/sfc/ethtool.c index 12a91c559aa2..286056a063aa 100644 --- a/drivers/net/ethernet/sfc/ethtool.c +++ b/drivers/net/ethernet/sfc/ethtool.c @@ -97,7 +97,9 @@ static void efx_ethtool_get_regs(struct net_device *net_dev, */
static int efx_ethtool_get_coalesce(struct net_device *net_dev, - struct ethtool_coalesce *coalesce) + struct ethtool_coalesce *coalesce, + struct kernel_ethtool_coalesce *kernel_coal, + struct netlink_ext_ack *extack) { struct efx_nic *efx = netdev_priv(net_dev); unsigned int tx_usecs, rx_usecs; @@ -115,7 +117,9 @@ static int efx_ethtool_get_coalesce(struct net_device *net_dev, }
static int efx_ethtool_set_coalesce(struct net_device *net_dev, - struct ethtool_coalesce *coalesce) + struct ethtool_coalesce *coalesce, + struct kernel_ethtool_coalesce *kernel_coal, + struct netlink_ext_ack *extack) { struct efx_nic *efx = netdev_priv(net_dev); struct efx_channel *channel; diff --git a/drivers/net/ethernet/sfc/falcon/ethtool.c b/drivers/net/ethernet/sfc/falcon/ethtool.c index a6bae6a234ba..137e8a7aeaa1 100644 --- a/drivers/net/ethernet/sfc/falcon/ethtool.c +++ b/drivers/net/ethernet/sfc/falcon/ethtool.c @@ -577,7 +577,9 @@ static int ef4_ethtool_nway_reset(struct net_device *net_dev) */
static int ef4_ethtool_get_coalesce(struct net_device *net_dev, - struct ethtool_coalesce *coalesce) + struct ethtool_coalesce *coalesce, + struct kernel_ethtool_coalesce *kernel_coal, + struct netlink_ext_ack *extack) { struct ef4_nic *efx = netdev_priv(net_dev); unsigned int tx_usecs, rx_usecs; @@ -595,7 +597,9 @@ static int ef4_ethtool_get_coalesce(struct net_device *net_dev, }
static int ef4_ethtool_set_coalesce(struct net_device *net_dev, - struct ethtool_coalesce *coalesce) + struct ethtool_coalesce *coalesce, + struct kernel_ethtool_coalesce *kernel_coal, + struct netlink_ext_ack *extack) { struct ef4_nic *efx = netdev_priv(net_dev); struct ef4_channel *channel; diff --git a/drivers/net/ethernet/socionext/netsec.c b/drivers/net/ethernet/socionext/netsec.c index ef3634d1b9f7..73bcc92a1656 100644 --- a/drivers/net/ethernet/socionext/netsec.c +++ b/drivers/net/ethernet/socionext/netsec.c @@ -532,7 +532,9 @@ static void netsec_et_get_drvinfo(struct net_device *net_device, }
static int netsec_et_get_coalesce(struct net_device *net_device, - struct ethtool_coalesce *et_coalesce) + struct ethtool_coalesce *et_coalesce, + struct kernel_ethtool_coalesce *kernel_coal, + struct netlink_ext_ack *extack) { struct netsec_priv *priv = netdev_priv(net_device);
@@ -542,7 +544,9 @@ static int netsec_et_get_coalesce(struct net_device *net_device, }
static int netsec_et_set_coalesce(struct net_device *net_device, - struct ethtool_coalesce *et_coalesce) + struct ethtool_coalesce *et_coalesce, + struct kernel_ethtool_coalesce *kernel_coal, + struct netlink_ext_ack *extack) { struct netsec_priv *priv = netdev_priv(net_device);
@@ -1540,7 +1544,7 @@ static int netsec_start_gmac(struct netsec_priv *priv) netsec_write(priv, NETSEC_REG_NRM_RX_INTEN_CLR, ~0); netsec_write(priv, NETSEC_REG_NRM_TX_INTEN_CLR, ~0);
- netsec_et_set_coalesce(priv->ndev, &priv->et_coalesce); + netsec_et_set_coalesce(priv->ndev, &priv->et_coalesce, NULL, NULL);
if (netsec_mac_write(priv, GMAC_REG_OMR, value)) return -ETIMEDOUT; diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c index 9e54f953634b..7b5f29617795 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c @@ -753,7 +753,9 @@ static u32 stmmac_riwt2usec(u32 riwt, struct stmmac_priv *priv) }
static int stmmac_get_coalesce(struct net_device *dev, - struct ethtool_coalesce *ec) + struct ethtool_coalesce *ec, + struct kernel_ethtool_coalesce *kernel_coal, + struct netlink_ext_ack *extack) { struct stmmac_priv *priv = netdev_priv(dev);
@@ -769,7 +771,9 @@ static int stmmac_get_coalesce(struct net_device *dev, }
static int stmmac_set_coalesce(struct net_device *dev, - struct ethtool_coalesce *ec) + struct ethtool_coalesce *ec, + struct kernel_ethtool_coalesce *kernel_coal, + struct netlink_ext_ack *extack) { struct stmmac_priv *priv = netdev_priv(dev); u32 rx_cnt = priv->plat->rx_queues_to_use; diff --git a/drivers/net/ethernet/synopsys/dwc-xlgmac-ethtool.c b/drivers/net/ethernet/synopsys/dwc-xlgmac-ethtool.c index bc198eadfcab..49f8c6be9459 100644 --- a/drivers/net/ethernet/synopsys/dwc-xlgmac-ethtool.c +++ b/drivers/net/ethernet/synopsys/dwc-xlgmac-ethtool.c @@ -146,8 +146,11 @@ static void xlgmac_ethtool_get_channels(struct net_device *netdev, channel->tx_count = pdata->tx_q_count; }
-static int xlgmac_ethtool_get_coalesce(struct net_device *netdev, - struct ethtool_coalesce *ec) +static int +xlgmac_ethtool_get_coalesce(struct net_device *netdev, + struct ethtool_coalesce *ec, + struct kernel_ethtool_coalesce *kernel_coal, + struct netlink_ext_ack *extack) { struct xlgmac_pdata *pdata = netdev_priv(netdev);
@@ -158,8 +161,11 @@ static int xlgmac_ethtool_get_coalesce(struct net_device *netdev, return 0; }
-static int xlgmac_ethtool_set_coalesce(struct net_device *netdev, - struct ethtool_coalesce *ec) +static int +xlgmac_ethtool_set_coalesce(struct net_device *netdev, + struct ethtool_coalesce *ec, + struct kernel_ethtool_coalesce *kernel_coal, + struct netlink_ext_ack *extack) { struct xlgmac_pdata *pdata = netdev_priv(netdev); struct xlgmac_hw_ops *hw_ops = &pdata->hw_ops; diff --git a/drivers/net/ethernet/tehuti/tehuti.c b/drivers/net/ethernet/tehuti/tehuti.c index d054c6e83b1c..7aebb2228dc8 100644 --- a/drivers/net/ethernet/tehuti/tehuti.c +++ b/drivers/net/ethernet/tehuti/tehuti.c @@ -2159,8 +2159,10 @@ bdx_get_drvinfo(struct net_device *netdev, struct ethtool_drvinfo *drvinfo) * @netdev * @ecoal */ -static int -bdx_get_coalesce(struct net_device *netdev, struct ethtool_coalesce *ecoal) +static int bdx_get_coalesce(struct net_device *netdev, + struct ethtool_coalesce *ecoal, + struct kernel_ethtool_coalesce *kernel_coal, + struct netlink_ext_ack *extack) { u32 rdintcm; u32 tdintcm; @@ -2188,8 +2190,10 @@ bdx_get_coalesce(struct net_device *netdev, struct ethtool_coalesce *ecoal) * @netdev * @ecoal */ -static int -bdx_set_coalesce(struct net_device *netdev, struct ethtool_coalesce *ecoal) +static int bdx_set_coalesce(struct net_device *netdev, + struct ethtool_coalesce *ecoal, + struct kernel_ethtool_coalesce *kernel_coal, + struct netlink_ext_ack *extack) { u32 rdintcm; u32 tdintcm; diff --git a/drivers/net/ethernet/ti/cpsw.c b/drivers/net/ethernet/ti/cpsw.c index b0f00b4edd94..ca1168bdcf72 100644 --- a/drivers/net/ethernet/ti/cpsw.c +++ b/drivers/net/ethernet/ti/cpsw.c @@ -853,7 +853,7 @@ static int cpsw_ndo_open(struct net_device *ndev) struct ethtool_coalesce coal;
coal.rx_coalesce_usecs = cpsw->coal_intvl; - cpsw_set_coalesce(ndev, &coal); + cpsw_set_coalesce(ndev, &coal, NULL, NULL); }
cpdma_ctlr_start(cpsw->dma); diff --git a/drivers/net/ethernet/ti/cpsw_ethtool.c b/drivers/net/ethernet/ti/cpsw_ethtool.c index 4619c3a950b0..158c8d3793f4 100644 --- a/drivers/net/ethernet/ti/cpsw_ethtool.c +++ b/drivers/net/ethernet/ti/cpsw_ethtool.c @@ -152,7 +152,9 @@ void cpsw_set_msglevel(struct net_device *ndev, u32 value) priv->msg_enable = value; }
-int cpsw_get_coalesce(struct net_device *ndev, struct ethtool_coalesce *coal) +int cpsw_get_coalesce(struct net_device *ndev, struct ethtool_coalesce *coal, + struct kernel_ethtool_coalesce *kernel_coal, + struct netlink_ext_ack *extack) { struct cpsw_common *cpsw = ndev_to_cpsw(ndev);
@@ -160,7 +162,9 @@ int cpsw_get_coalesce(struct net_device *ndev, struct ethtool_coalesce *coal) return 0; }
-int cpsw_set_coalesce(struct net_device *ndev, struct ethtool_coalesce *coal) +int cpsw_set_coalesce(struct net_device *ndev, struct ethtool_coalesce *coal, + struct kernel_ethtool_coalesce *kernel_coal, + struct netlink_ext_ack *extack) { struct cpsw_priv *priv = netdev_priv(ndev); u32 int_ctrl; diff --git a/drivers/net/ethernet/ti/cpsw_new.c b/drivers/net/ethernet/ti/cpsw_new.c index 2f5e0ad23ad7..c11c572288a1 100644 --- a/drivers/net/ethernet/ti/cpsw_new.c +++ b/drivers/net/ethernet/ti/cpsw_new.c @@ -901,7 +901,7 @@ static int cpsw_ndo_open(struct net_device *ndev) struct ethtool_coalesce coal;
coal.rx_coalesce_usecs = cpsw->coal_intvl; - cpsw_set_coalesce(ndev, &coal); + cpsw_set_coalesce(ndev, &coal, NULL, NULL); }
cpdma_ctlr_start(cpsw->dma); diff --git a/drivers/net/ethernet/ti/cpsw_priv.h b/drivers/net/ethernet/ti/cpsw_priv.h index 7b7f3596b20d..5d8a3b5f4333 100644 --- a/drivers/net/ethernet/ti/cpsw_priv.h +++ b/drivers/net/ethernet/ti/cpsw_priv.h @@ -462,8 +462,12 @@ void cpsw_mqprio_resume(struct cpsw_slave *slave, struct cpsw_priv *priv); /* ethtool */ u32 cpsw_get_msglevel(struct net_device *ndev); void cpsw_set_msglevel(struct net_device *ndev, u32 value); -int cpsw_get_coalesce(struct net_device *ndev, struct ethtool_coalesce *coal); -int cpsw_set_coalesce(struct net_device *ndev, struct ethtool_coalesce *coal); +int cpsw_get_coalesce(struct net_device *ndev, struct ethtool_coalesce *coal, + struct kernel_ethtool_coalesce *kernel_coal, + struct netlink_ext_ack *extack); +int cpsw_set_coalesce(struct net_device *ndev, struct ethtool_coalesce *coal, + struct kernel_ethtool_coalesce *kernel_coal, + struct netlink_ext_ack *extack); int cpsw_get_sset_count(struct net_device *ndev, int sset); void cpsw_get_strings(struct net_device *ndev, u32 stringset, u8 *data); void cpsw_get_ethtool_stats(struct net_device *ndev, diff --git a/drivers/net/ethernet/ti/davinci_emac.c b/drivers/net/ethernet/ti/davinci_emac.c index 03055c96f076..1d7ee07a0885 100644 --- a/drivers/net/ethernet/ti/davinci_emac.c +++ b/drivers/net/ethernet/ti/davinci_emac.c @@ -383,12 +383,16 @@ static void emac_get_drvinfo(struct net_device *ndev, * emac_get_coalesce - Get interrupt coalesce settings for this device * @ndev : The DaVinci EMAC network adapter * @coal : ethtool coalesce settings structure + * @kernel_coal: ethtool CQE mode setting structure + * @extack: extack for reporting error messages * * Fetch the current interrupt coalesce settings * */ static int emac_get_coalesce(struct net_device *ndev, - struct ethtool_coalesce *coal) + struct ethtool_coalesce *coal, + struct kernel_ethtool_coalesce *kernel_coal, + struct netlink_ext_ack *extack) { struct emac_priv *priv = netdev_priv(ndev);
@@ -401,12 +405,16 @@ static int emac_get_coalesce(struct net_device *ndev, * emac_set_coalesce - Set interrupt coalesce settings for this device * @ndev : The DaVinci EMAC network adapter * @coal : ethtool coalesce settings structure + * @kernel_coal: ethtool CQE mode setting structure + * @extack: extack for reporting error messages * * Set interrupt coalesce parameters * */ static int emac_set_coalesce(struct net_device *ndev, - struct ethtool_coalesce *coal) + struct ethtool_coalesce *coal, + struct kernel_ethtool_coalesce *kernel_coal, + struct netlink_ext_ack *extack) { struct emac_priv *priv = netdev_priv(ndev); u32 int_ctrl, num_interrupts = 0; @@ -1462,7 +1470,7 @@ static int emac_dev_open(struct net_device *ndev) struct ethtool_coalesce coal;
coal.rx_coalesce_usecs = (priv->coal_intvl << 4); - emac_set_coalesce(ndev, &coal); + emac_set_coalesce(ndev, &coal, NULL, NULL); }
cpdma_ctlr_start(priv->dma); diff --git a/drivers/net/ethernet/via/via-velocity.c b/drivers/net/ethernet/via/via-velocity.c index b65767f9e499..32b9b3dae9ca 100644 --- a/drivers/net/ethernet/via/via-velocity.c +++ b/drivers/net/ethernet/via/via-velocity.c @@ -3520,7 +3520,9 @@ static void set_pending_timer_val(int *val, u32 us)
static int velocity_get_coalesce(struct net_device *dev, - struct ethtool_coalesce *ecmd) + struct ethtool_coalesce *ecmd, + struct kernel_ethtool_coalesce *kernel_coal, + struct netlink_ext_ack *extack) { struct velocity_info *vptr = netdev_priv(dev);
@@ -3534,7 +3536,9 @@ static int velocity_get_coalesce(struct net_device *dev, }
static int velocity_set_coalesce(struct net_device *dev, - struct ethtool_coalesce *ecmd) + struct ethtool_coalesce *ecmd, + struct kernel_ethtool_coalesce *kernel_coal, + struct netlink_ext_ack *extack) { struct velocity_info *vptr = netdev_priv(dev); int max_us = 0x3f * 64; diff --git a/drivers/net/ethernet/xilinx/ll_temac_main.c b/drivers/net/ethernet/xilinx/ll_temac_main.c index 01bb36e7cff0..9c394fb407d6 100644 --- a/drivers/net/ethernet/xilinx/ll_temac_main.c +++ b/drivers/net/ethernet/xilinx/ll_temac_main.c @@ -1295,8 +1295,11 @@ static int ll_temac_ethtools_set_ringparam(struct net_device *ndev, return 0; }
-static int ll_temac_ethtools_get_coalesce(struct net_device *ndev, - struct ethtool_coalesce *ec) +static int +ll_temac_ethtools_get_coalesce(struct net_device *ndev, + struct ethtool_coalesce *ec, + struct kernel_ethtool_coalesce *kernel_coal, + struct netlink_ext_ack *extack) { struct temac_local *lp = netdev_priv(ndev);
@@ -1307,8 +1310,11 @@ static int ll_temac_ethtools_get_coalesce(struct net_device *ndev, return 0; }
-static int ll_temac_ethtools_set_coalesce(struct net_device *ndev, - struct ethtool_coalesce *ec) +static int +ll_temac_ethtools_set_coalesce(struct net_device *ndev, + struct ethtool_coalesce *ec, + struct kernel_ethtool_coalesce *kernel_coal, + struct netlink_ext_ack *extack) { struct temac_local *lp = netdev_priv(ndev);
diff --git a/drivers/net/ethernet/xilinx/xilinx_axienet_main.c b/drivers/net/ethernet/xilinx/xilinx_axienet_main.c index 69c79cc24e6e..11c1c5a3240c 100644 --- a/drivers/net/ethernet/xilinx/xilinx_axienet_main.c +++ b/drivers/net/ethernet/xilinx/xilinx_axienet_main.c @@ -1409,6 +1409,8 @@ axienet_ethtools_set_pauseparam(struct net_device *ndev, * axienet_ethtools_get_coalesce - Get DMA interrupt coalescing count. * @ndev: Pointer to net_device structure * @ecoalesce: Pointer to ethtool_coalesce structure + * @kernel_coal: ethtool CQE mode setting structure + * @extack: extack for reporting error messages * * This implements ethtool command for getting the DMA interrupt coalescing * count on Tx and Rx paths. Issue "ethtool -c ethX" under linux prompt to @@ -1416,8 +1418,11 @@ axienet_ethtools_set_pauseparam(struct net_device *ndev, * * Return: 0 always */ -static int axienet_ethtools_get_coalesce(struct net_device *ndev, - struct ethtool_coalesce *ecoalesce) +static int +axienet_ethtools_get_coalesce(struct net_device *ndev, + struct ethtool_coalesce *ecoalesce, + struct kernel_ethtool_coalesce *kernel_coal, + struct netlink_ext_ack *extack) { u32 regval = 0; struct axienet_local *lp = netdev_priv(ndev); @@ -1434,6 +1439,8 @@ static int axienet_ethtools_get_coalesce(struct net_device *ndev, * axienet_ethtools_set_coalesce - Set DMA interrupt coalescing count. * @ndev: Pointer to net_device structure * @ecoalesce: Pointer to ethtool_coalesce structure + * @kernel_coal: ethtool CQE mode setting structure + * @extack: extack for reporting error messages * * This implements ethtool command for setting the DMA interrupt coalescing * count on Tx and Rx paths. Issue "ethtool -C ethX rx-frames 5" under linux @@ -1441,8 +1448,11 @@ static int axienet_ethtools_get_coalesce(struct net_device *ndev, * * Return: 0, on success, Non-zero error value on failure. */ -static int axienet_ethtools_set_coalesce(struct net_device *ndev, - struct ethtool_coalesce *ecoalesce) +static int +axienet_ethtools_set_coalesce(struct net_device *ndev, + struct ethtool_coalesce *ecoalesce, + struct kernel_ethtool_coalesce *kernel_coal, + struct netlink_ext_ack *extack) { struct axienet_local *lp = netdev_priv(ndev);
diff --git a/drivers/net/tun.c b/drivers/net/tun.c index c671d8e25774..44470067f668 100644 --- a/drivers/net/tun.c +++ b/drivers/net/tun.c @@ -3558,7 +3558,9 @@ static void tun_set_msglevel(struct net_device *dev, u32 value) }
static int tun_get_coalesce(struct net_device *dev, - struct ethtool_coalesce *ec) + struct ethtool_coalesce *ec, + struct kernel_ethtool_coalesce *kernel_coal, + struct netlink_ext_ack *extack) { struct tun_struct *tun = netdev_priv(dev);
@@ -3568,7 +3570,9 @@ static int tun_get_coalesce(struct net_device *dev, }
static int tun_set_coalesce(struct net_device *dev, - struct ethtool_coalesce *ec) + struct ethtool_coalesce *ec, + struct kernel_ethtool_coalesce *kernel_coal, + struct netlink_ext_ack *extack) { struct tun_struct *tun = netdev_priv(dev);
diff --git a/drivers/net/usb/r8152.c b/drivers/net/usb/r8152.c index f5010f8ac1ec..c92bb345e987 100644 --- a/drivers/net/usb/r8152.c +++ b/drivers/net/usb/r8152.c @@ -6185,7 +6185,9 @@ static int rtl8152_nway_reset(struct net_device *dev) }
static int rtl8152_get_coalesce(struct net_device *netdev, - struct ethtool_coalesce *coalesce) + struct ethtool_coalesce *coalesce, + struct kernel_ethtool_coalesce *kernel_coal, + struct netlink_ext_ack *extack) { struct r8152 *tp = netdev_priv(netdev);
@@ -6204,7 +6206,9 @@ static int rtl8152_get_coalesce(struct net_device *netdev, }
static int rtl8152_set_coalesce(struct net_device *netdev, - struct ethtool_coalesce *coalesce) + struct ethtool_coalesce *coalesce, + struct kernel_ethtool_coalesce *kernel_coal, + struct netlink_ext_ack *extack) { struct r8152 *tp = netdev_priv(netdev); int ret; diff --git a/drivers/net/virtio_net.c b/drivers/net/virtio_net.c index 286f836a53bf..111811463834 100644 --- a/drivers/net/virtio_net.c +++ b/drivers/net/virtio_net.c @@ -2222,7 +2222,9 @@ static int virtnet_get_link_ksettings(struct net_device *dev, }
static int virtnet_set_coalesce(struct net_device *dev, - struct ethtool_coalesce *ec) + struct ethtool_coalesce *ec, + struct kernel_ethtool_coalesce *kernel_coal, + struct netlink_ext_ack *extack) { struct virtnet_info *vi = netdev_priv(dev); int i, napi_weight; @@ -2243,7 +2245,9 @@ static int virtnet_set_coalesce(struct net_device *dev, }
static int virtnet_get_coalesce(struct net_device *dev, - struct ethtool_coalesce *ec) + struct ethtool_coalesce *ec, + struct kernel_ethtool_coalesce *kernel_coal, + struct netlink_ext_ack *extack) { struct ethtool_coalesce ec_default = { .cmd = ETHTOOL_GCOALESCE, diff --git a/drivers/net/vmxnet3/vmxnet3_ethtool.c b/drivers/net/vmxnet3/vmxnet3_ethtool.c index 7ec8652f2c26..2824abab9919 100644 --- a/drivers/net/vmxnet3/vmxnet3_ethtool.c +++ b/drivers/net/vmxnet3/vmxnet3_ethtool.c @@ -1030,8 +1030,10 @@ vmxnet3_set_rss(struct net_device *netdev, const u32 *p, const u8 *key, } #endif
-static int -vmxnet3_get_coalesce(struct net_device *netdev, struct ethtool_coalesce *ec) +static int vmxnet3_get_coalesce(struct net_device *netdev, + struct ethtool_coalesce *ec, + struct kernel_ethtool_coalesce *kernel_coal, + struct netlink_ext_ack *extack) { struct vmxnet3_adapter *adapter = netdev_priv(netdev);
@@ -1065,8 +1067,10 @@ vmxnet3_get_coalesce(struct net_device *netdev, struct ethtool_coalesce *ec) return 0; }
-static int -vmxnet3_set_coalesce(struct net_device *netdev, struct ethtool_coalesce *ec) +static int vmxnet3_set_coalesce(struct net_device *netdev, + struct ethtool_coalesce *ec, + struct kernel_ethtool_coalesce *kernel_coal, + struct netlink_ext_ack *extack) { struct vmxnet3_adapter *adapter = netdev_priv(netdev); struct Vmxnet3_DriverShared *shared = adapter->shared; diff --git a/drivers/net/wireless/ath/wil6210/ethtool.c b/drivers/net/wireless/ath/wil6210/ethtool.c index e481674485c2..29a9f17c2df0 100644 --- a/drivers/net/wireless/ath/wil6210/ethtool.c +++ b/drivers/net/wireless/ath/wil6210/ethtool.c @@ -11,8 +11,11 @@
#include "wil6210.h"
-static int wil_ethtoolops_get_coalesce(struct net_device *ndev, - struct ethtool_coalesce *cp) +static int +wil_ethtoolops_get_coalesce(struct net_device *ndev, + struct ethtool_coalesce *cp, + struct kernel_ethtool_coalesce *kernel_coal, + struct netlink_ext_ack *extack) { struct wil6210_priv *wil = ndev_to_wil(ndev); u32 tx_itr_en, tx_itr_val = 0; @@ -45,8 +48,11 @@ static int wil_ethtoolops_get_coalesce(struct net_device *ndev, return ret; }
-static int wil_ethtoolops_set_coalesce(struct net_device *ndev, - struct ethtool_coalesce *cp) +static int +wil_ethtoolops_set_coalesce(struct net_device *ndev, + struct ethtool_coalesce *cp, + struct kernel_ethtool_coalesce *kernel_coal, + struct netlink_ext_ack *extack) { struct wil6210_priv *wil = ndev_to_wil(ndev); struct wireless_dev *wdev = ndev->ieee80211_ptr; diff --git a/drivers/s390/net/qeth_ethtool.c b/drivers/s390/net/qeth_ethtool.c index b5caa723326e..72f3fa404e13 100644 --- a/drivers/s390/net/qeth_ethtool.c +++ b/drivers/s390/net/qeth_ethtool.c @@ -124,7 +124,9 @@ static void __qeth_set_coalesce(struct net_device *dev, }
static int qeth_set_coalesce(struct net_device *dev, - struct ethtool_coalesce *coal) + struct ethtool_coalesce *coal, + struct kernel_ethtool_coalesce *kernel_coal, + struct netlink_ext_ack *extack) { struct qeth_card *card = dev->ml_priv; struct qeth_qdio_out_q *queue; diff --git a/drivers/staging/qlge/qlge_ethtool.c b/drivers/staging/qlge/qlge_ethtool.c index d44b2dae9213..cc9d619b4b78 100644 --- a/drivers/staging/qlge/qlge_ethtool.c +++ b/drivers/staging/qlge/qlge_ethtool.c @@ -623,7 +623,10 @@ static void ql_get_regs(struct net_device *ndev, regs->len = sizeof(struct ql_reg_dump); }
-static int ql_get_coalesce(struct net_device *dev, struct ethtool_coalesce *c) +static int ql_get_coalesce(struct net_device *dev, + struct ethtool_coalesce *c, + struct kernel_ethtool_coalesce *kernel_coal, + struct netlink_ext_ack *extack) { struct ql_adapter *qdev = netdev_priv(dev);
@@ -646,7 +649,10 @@ static int ql_get_coalesce(struct net_device *dev, struct ethtool_coalesce *c) return 0; }
-static int ql_set_coalesce(struct net_device *ndev, struct ethtool_coalesce *c) +static int ql_set_coalesce(struct net_device *ndev, + struct ethtool_coalesce *c, + struct kernel_ethtool_coalesce *kernel_coal, + struct netlink_ext_ack *extack) { struct ql_adapter *qdev = netdev_priv(ndev);
diff --git a/include/linux/ethtool.h b/include/linux/ethtool.h index 023e05b22890..24e36c771108 100644 --- a/include/linux/ethtool.h +++ b/include/linux/ethtool.h @@ -15,6 +15,7 @@
#include <linux/bitmap.h> #include <linux/compat.h> +#include <linux/netlink.h> #include <uapi/linux/ethtool.h>
#ifdef CONFIG_COMPAT @@ -446,8 +447,14 @@ struct ethtool_ops { struct ethtool_eeprom *, u8 *); int (*set_eeprom)(struct net_device *, struct ethtool_eeprom *, u8 *); - int (*get_coalesce)(struct net_device *, struct ethtool_coalesce *); - int (*set_coalesce)(struct net_device *, struct ethtool_coalesce *); + int (*get_coalesce)(struct net_device *, + struct ethtool_coalesce *, + struct kernel_ethtool_coalesce *, + struct netlink_ext_ack *); + int (*set_coalesce)(struct net_device *, + struct ethtool_coalesce *, + struct kernel_ethtool_coalesce *, + struct netlink_ext_ack *); void (*get_ringparam)(struct net_device *, struct ethtool_ringparam *); int (*set_ringparam)(struct net_device *, diff --git a/net/ethtool/coalesce.c b/net/ethtool/coalesce.c index e6bc53634e68..46776ea42a92 100644 --- a/net/ethtool/coalesce.c +++ b/net/ethtool/coalesce.c @@ -62,6 +62,7 @@ static int coalesce_prepare_data(const struct ethnl_req_info *req_base, struct genl_info *info) { struct coalesce_reply_data *data = COALESCE_REPDATA(reply_base); + struct netlink_ext_ack *extack = info ? info->extack : NULL; struct net_device *dev = reply_base->dev; int ret;
@@ -71,7 +72,8 @@ static int coalesce_prepare_data(const struct ethnl_req_info *req_base, ret = ethnl_ops_begin(dev); if (ret < 0) return ret; - ret = dev->ethtool_ops->get_coalesce(dev, &data->coalesce); + ret = dev->ethtool_ops->get_coalesce(dev, &data->coalesce, + &data->kernel_coalesce, extack); ethnl_ops_complete(dev);
return ret; @@ -266,7 +268,8 @@ int ethnl_set_coalesce(struct sk_buff *skb, struct genl_info *info) ret = ethnl_ops_begin(dev); if (ret < 0) goto out_rtnl; - ret = ops->get_coalesce(dev, &coalesce); + ret = ops->get_coalesce(dev, &coalesce, &kernel_coalesce, + info->extack); if (ret < 0) goto out_ops;
@@ -322,7 +325,8 @@ int ethnl_set_coalesce(struct sk_buff *skb, struct genl_info *info) if (!mod) goto out_ops;
- ret = dev->ethtool_ops->set_coalesce(dev, &coalesce); + ret = dev->ethtool_ops->set_coalesce(dev, &coalesce, &kernel_coalesce, + info->extack); if (ret < 0) goto out_ops; ethtool_notify(dev, ETHTOOL_MSG_COALESCE_NTF, NULL); diff --git a/net/ethtool/ioctl.c b/net/ethtool/ioctl.c index 2917af3f5ac1..488d64ccc122 100644 --- a/net/ethtool/ioctl.c +++ b/net/ethtool/ioctl.c @@ -1514,12 +1514,14 @@ static noinline_for_stack int ethtool_get_coalesce(struct net_device *dev, void __user *useraddr) { struct ethtool_coalesce coalesce = { .cmd = ETHTOOL_GCOALESCE }; + struct kernel_ethtool_coalesce kernel_coalesce = {}; int ret;
if (!dev->ethtool_ops->get_coalesce) return -EOPNOTSUPP;
- ret = dev->ethtool_ops->get_coalesce(dev, &coalesce); + ret = dev->ethtool_ops->get_coalesce(dev, &coalesce, &kernel_coalesce, + NULL); if (ret) return ret;
@@ -1586,19 +1588,26 @@ ethtool_set_coalesce_supported(struct net_device *dev, static noinline_for_stack int ethtool_set_coalesce(struct net_device *dev, void __user *useraddr) { + struct kernel_ethtool_coalesce kernel_coalesce = {}; struct ethtool_coalesce coalesce; int ret;
- if (!dev->ethtool_ops->set_coalesce) + if (!dev->ethtool_ops->set_coalesce && !dev->ethtool_ops->get_coalesce) return -EOPNOTSUPP;
+ ret = dev->ethtool_ops->get_coalesce(dev, &coalesce, &kernel_coalesce, + NULL); + if (ret) + return ret; + if (copy_from_user(&coalesce, useraddr, sizeof(coalesce))) return -EFAULT;
if (!ethtool_set_coalesce_supported(dev, &coalesce)) return -EOPNOTSUPP;
- ret = dev->ethtool_ops->set_coalesce(dev, &coalesce); + ret = dev->ethtool_ops->set_coalesce(dev, &coalesce, &kernel_coalesce, + NULL); if (!ret) ethtool_notify(dev, ETHTOOL_MSG_COALESCE_NTF, NULL); return ret;
From: Yufeng Mo moyufeng@huawei.com
mainline inclusion from mainline-master commit 9f0c6f4b7475dd97e1f0eed81dd6ff173cf8c7fc category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/I46N7D CVE: NA
Reference: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?i...
----------------------------------------------------------------------
For device whose version is above V3(include V3), the GL can select EQE or CQE mode, so adds support for it.
In CQE mode, the coalesced timer will restart when the first new completion occurs, while in EQE mode, the timer will not restart.
Signed-off-by: Yufeng Mo moyufeng@huawei.com Signed-off-by: Huazhong Tan tanhuazhong@huawei.com Signed-off-by: Jakub Kicinski kuba@kernel.org Reviewed-by: Yongxin Li liyongxin1@huawei.com Signed-off-by: Junxin Chen chenjunxin1@huawei.com Signed-off-by: Zheng Zengkai zhengzengkai@huawei.com --- drivers/net/ethernet/hisilicon/hns3/hnae3.h | 1 + .../net/ethernet/hisilicon/hns3/hns3_enet.c | 49 ++++++++++++++++++- .../net/ethernet/hisilicon/hns3/hns3_enet.h | 8 +++ .../hisilicon/hns3/hns3pf/hclge_main.c | 1 + .../hisilicon/hns3/hns3vf/hclgevf_main.c | 1 + 5 files changed, 58 insertions(+), 2 deletions(-)
diff --git a/drivers/net/ethernet/hisilicon/hns3/hnae3.h b/drivers/net/ethernet/hisilicon/hns3/hnae3.h index ef0f897d53fa..7dc44a674614 100644 --- a/drivers/net/ethernet/hisilicon/hns3/hnae3.h +++ b/drivers/net/ethernet/hisilicon/hns3/hnae3.h @@ -773,6 +773,7 @@ struct hnae3_knic_private_info {
u16 int_rl_setting; enum pkt_hash_types rss_type; + void __iomem *io_base; };
struct hnae3_roce_private_info { diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3_enet.c b/drivers/net/ethernet/hisilicon/hns3/hns3_enet.c index cdb5f14fb6bc..bd42593dbebe 100644 --- a/drivers/net/ethernet/hisilicon/hns3/hns3_enet.c +++ b/drivers/net/ethernet/hisilicon/hns3/hns3_enet.c @@ -4434,9 +4434,7 @@ static void hns3_tx_dim_work(struct work_struct *work) static void hns3_nic_init_dim(struct hns3_enet_tqp_vector *tqp_vector) { INIT_WORK(&tqp_vector->rx_group.dim.work, hns3_rx_dim_work); - tqp_vector->rx_group.dim.mode = DIM_CQ_PERIOD_MODE_START_FROM_EQE; INIT_WORK(&tqp_vector->tx_group.dim.work, hns3_tx_dim_work); - tqp_vector->tx_group.dim.mode = DIM_CQ_PERIOD_MODE_START_FROM_EQE; }
static int hns3_nic_init_vector_data(struct hns3_nic_priv *priv) @@ -4954,6 +4952,48 @@ static void hns3_info_show(struct hns3_nic_priv *priv) dev_info(priv->dev, "Max mtu size: %u\n", priv->netdev->max_mtu); }
+static void hns3_set_cq_period_mode(struct hns3_nic_priv *priv, + enum dim_cq_period_mode mode, bool is_tx) +{ + struct hnae3_ae_dev *ae_dev = pci_get_drvdata(priv->ae_handle->pdev); + struct hnae3_handle *handle = priv->ae_handle; + int i; + + if (is_tx) { + priv->tx_cqe_mode = mode; + + for (i = 0; i < priv->vector_num; i++) + priv->tqp_vector[i].tx_group.dim.mode = mode; + } else { + priv->rx_cqe_mode = mode; + + for (i = 0; i < priv->vector_num; i++) + priv->tqp_vector[i].rx_group.dim.mode = mode; + } + + /* only device version above V3(include V3), GL can switch CQ/EQ + * period mode. + */ + if (ae_dev->dev_version >= HNAE3_DEVICE_VERSION_V3) { + u32 new_mode; + u64 reg; + + new_mode = (mode == DIM_CQ_PERIOD_MODE_START_FROM_CQE) ? + HNS3_CQ_MODE_CQE : HNS3_CQ_MODE_EQE; + reg = is_tx ? HNS3_GL1_CQ_MODE_REG : HNS3_GL0_CQ_MODE_REG; + + writel(new_mode, handle->kinfo.io_base + reg); + } +} + +static void hns3_cq_period_mode_init(struct hns3_nic_priv *priv, + enum dim_cq_period_mode tx_mode, + enum dim_cq_period_mode rx_mode) +{ + hns3_set_cq_period_mode(priv, tx_mode, true); + hns3_set_cq_period_mode(priv, rx_mode, false); +} + static int hns3_client_init(struct hnae3_handle *handle) { struct pci_dev *pdev = handle->pdev; @@ -5021,6 +5061,9 @@ static int hns3_client_init(struct hnae3_handle *handle) goto out_init_ring; }
+ hns3_cq_period_mode_init(priv, DIM_CQ_PERIOD_MODE_START_FROM_EQE, + DIM_CQ_PERIOD_MODE_START_FROM_EQE); + ret = hns3_init_phy(netdev); if (ret) goto out_init_phy; @@ -5353,6 +5396,8 @@ static int hns3_reset_notify_init_enet(struct hnae3_handle *handle) if (ret) goto err_uninit_vector;
+ hns3_cq_period_mode_init(priv, priv->tx_cqe_mode, priv->rx_cqe_mode); + /* the device can work without cpu rmap, only aRFS needs it */ ret = hns3_set_rx_cpu_rmap(netdev); if (ret) diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3_enet.h b/drivers/net/ethernet/hisilicon/hns3/hns3_enet.h index f40d44a7994e..ed3587dcfbbc 100644 --- a/drivers/net/ethernet/hisilicon/hns3/hns3_enet.h +++ b/drivers/net/ethernet/hisilicon/hns3/hns3_enet.h @@ -201,6 +201,12 @@ enum hns3_nic_state {
#define HNS3_RING_EN_B 0
+#define HNS3_GL0_CQ_MODE_REG 0x20d00 +#define HNS3_GL1_CQ_MODE_REG 0x20d04 +#define HNS3_GL2_CQ_MODE_REG 0x20d08 +#define HNS3_CQ_MODE_EQE 1U +#define HNS3_CQ_MODE_CQE 0U + enum hns3_pkt_l2t_type { HNS3_L2_TYPE_UNICAST, HNS3_L2_TYPE_MULTICAST, @@ -569,6 +575,8 @@ struct hns3_nic_priv {
unsigned long state;
+ enum dim_cq_period_mode tx_cqe_mode; + enum dim_cq_period_mode rx_cqe_mode; struct hns3_enet_coalesce tx_coal; struct hns3_enet_coalesce rx_coal; u32 tx_copybreak; diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c index 6e744eca0d50..1b6bb0d71fcb 100644 --- a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c +++ b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hclge_main.c @@ -1815,6 +1815,7 @@ static int hclge_vport_setup(struct hclge_vport *vport, u16 num_tqps) nic->pdev = hdev->pdev; nic->ae_algo = &ae_algo; nic->numa_node_mask = hdev->numa_node_mask; + nic->kinfo.io_base = hdev->hw.io_base;
ret = hclge_knic_setup(vport, num_tqps, hdev->num_tx_desc, hdev->num_rx_desc); diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3vf/hclgevf_main.c b/drivers/net/ethernet/hisilicon/hns3/hns3vf/hclgevf_main.c index f5b3532695dd..60588b194fe7 100644 --- a/drivers/net/ethernet/hisilicon/hns3/hns3vf/hclgevf_main.c +++ b/drivers/net/ethernet/hisilicon/hns3/hns3vf/hclgevf_main.c @@ -539,6 +539,7 @@ static int hclgevf_set_handle_info(struct hclgevf_dev *hdev) nic->pdev = hdev->pdev; nic->numa_node_mask = hdev->numa_node_mask; nic->flags |= HNAE3_SUPPORT_VF; + nic->kinfo.io_base = hdev->hw.io_base;
ret = hclgevf_knic_setup(hdev); if (ret)
From: Yufeng Mo moyufeng@huawei.com
mainline inclusion from mainline-master commit cce1689eb58d2fe3219da2ecd27cef8e644c4cc6 category: feature bugzilla: https://gitee.com/openeuler/kernel/issues/I46N7D CVE: NA
Reference: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?i...
----------------------------------------------------------------------
Add support in ethtool for switching EQE/CQE mode.
Signed-off-by: Yufeng Mo moyufeng@huawei.com Signed-off-by: Huazhong Tan tanhuazhong@huawei.com Signed-off-by: Jakub Kicinski kuba@kernel.org Reviewed-by: Yongxin Li liyongxin1@huawei.com Signed-off-by: Junxin Chen chenjunxin1@huawei.com Signed-off-by: Zheng Zengkai zhengzengkai@huawei.com --- .../net/ethernet/hisilicon/hns3/hns3_enet.c | 6 +++--- .../net/ethernet/hisilicon/hns3/hns3_enet.h | 3 +++ .../net/ethernet/hisilicon/hns3/hns3_ethtool.c | 18 +++++++++++++++++- 3 files changed, 23 insertions(+), 4 deletions(-)
diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3_enet.c b/drivers/net/ethernet/hisilicon/hns3/hns3_enet.c index bd42593dbebe..e8b81bf1b5c2 100644 --- a/drivers/net/ethernet/hisilicon/hns3/hns3_enet.c +++ b/drivers/net/ethernet/hisilicon/hns3/hns3_enet.c @@ -4986,9 +4986,9 @@ static void hns3_set_cq_period_mode(struct hns3_nic_priv *priv, } }
-static void hns3_cq_period_mode_init(struct hns3_nic_priv *priv, - enum dim_cq_period_mode tx_mode, - enum dim_cq_period_mode rx_mode) +void hns3_cq_period_mode_init(struct hns3_nic_priv *priv, + enum dim_cq_period_mode tx_mode, + enum dim_cq_period_mode rx_mode) { hns3_set_cq_period_mode(priv, tx_mode, true); hns3_set_cq_period_mode(priv, rx_mode, false); diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3_enet.h b/drivers/net/ethernet/hisilicon/hns3/hns3_enet.h index ed3587dcfbbc..8bc06401c7ac 100644 --- a/drivers/net/ethernet/hisilicon/hns3/hns3_enet.h +++ b/drivers/net/ethernet/hisilicon/hns3/hns3_enet.h @@ -715,4 +715,7 @@ void hns3_dbg_register_debugfs(const char *debugfs_dir_name); void hns3_dbg_unregister_debugfs(void); void hns3_shinfo_pack(struct skb_shared_info *shinfo, __u32 *size); u16 hns3_get_max_available_channels(struct hnae3_handle *h); +void hns3_cq_period_mode_init(struct hns3_nic_priv *priv, + enum dim_cq_period_mode tx_mode, + enum dim_cq_period_mode rx_mode); #endif diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3_ethtool.c b/drivers/net/ethernet/hisilicon/hns3/hns3_ethtool.c index 049be076fd18..b8d9851aefc5 100644 --- a/drivers/net/ethernet/hisilicon/hns3/hns3_ethtool.c +++ b/drivers/net/ethernet/hisilicon/hns3/hns3_ethtool.c @@ -1203,6 +1203,11 @@ static int hns3_get_coalesce(struct net_device *netdev, cmd->tx_max_coalesced_frames = tx_coal->int_ql; cmd->rx_max_coalesced_frames = rx_coal->int_ql;
+ kernel_coal->use_cqe_mode_tx = (priv->tx_cqe_mode == + DIM_CQ_PERIOD_MODE_START_FROM_CQE); + kernel_coal->use_cqe_mode_rx = (priv->rx_cqe_mode == + DIM_CQ_PERIOD_MODE_START_FROM_CQE); + return 0; }
@@ -1372,6 +1377,8 @@ static int hns3_set_coalesce(struct net_device *netdev, struct hns3_enet_coalesce *tx_coal = &priv->tx_coal; struct hns3_enet_coalesce *rx_coal = &priv->rx_coal; u16 queue_num = h->kinfo.num_tqps; + enum dim_cq_period_mode tx_mode; + enum dim_cq_period_mode rx_mode; int ret; int i;
@@ -1397,6 +1404,14 @@ static int hns3_set_coalesce(struct net_device *netdev, for (i = 0; i < queue_num; i++) hns3_set_coalesce_per_queue(netdev, cmd, i);
+ tx_mode = kernel_coal->use_cqe_mode_tx ? + DIM_CQ_PERIOD_MODE_START_FROM_CQE : + DIM_CQ_PERIOD_MODE_START_FROM_EQE; + rx_mode = kernel_coal->use_cqe_mode_rx ? + DIM_CQ_PERIOD_MODE_START_FROM_CQE : + DIM_CQ_PERIOD_MODE_START_FROM_EQE; + hns3_cq_period_mode_init(priv, tx_mode, rx_mode); + return 0; }
@@ -1702,7 +1717,8 @@ static int hns3_set_tunable(struct net_device *netdev, ETHTOOL_COALESCE_USE_ADAPTIVE | \ ETHTOOL_COALESCE_RX_USECS_HIGH | \ ETHTOOL_COALESCE_TX_USECS_HIGH | \ - ETHTOOL_COALESCE_MAX_FRAMES) + ETHTOOL_COALESCE_MAX_FRAMES | \ + ETHTOOL_COALESCE_USE_CQE)
static int hns3_get_ts_info(struct net_device *netdev, struct ethtool_ts_info *info)