From 791ec491c372f49cea3ea7a7143454a9023ac9d4 Mon Sep 17 00:00:00 2001 From: Stephen Smalley Date: Fri, 17 Feb 2017 07:57:00 -0500 Subject: prlimit,security,selinux: add a security hook for prlimit When SELinux was first added to the kernel, a process could only get and set its own resource limits via getrlimit(2) and setrlimit(2), so no MAC checks were required for those operations, and thus no security hooks were defined for them. Later, SELinux introduced a hook for setlimit(2) with a check if the hard limit was being changed in order to be able to rely on the hard limit value as a safe reset point upon context transitions. Later on, when prlimit(2) was added to the kernel with the ability to get or set resource limits (hard or soft) of another process, LSM/SELinux was not updated other than to pass the target process to the setrlimit hook. This resulted in incomplete control over both getting and setting the resource limits of another process. Add a new security_task_prlimit() hook to the check_prlimit_permission() function to provide complete mediation. The hook is only called when acting on another task, and only if the existing DAC/capability checks would allow access. Pass flags down to the hook to indicate whether the prlimit(2) call will read, write, or both read and write the resource limits of the target process. The existing security_task_setrlimit() hook is left alone; it continues to serve a purpose in supporting the ability to make decisions based on the old and/or new resource limit values when setting limits. This is consistent with the DAC/capability logic, where check_prlimit_permission() performs generic DAC/capability checks for acting on another task, while do_prlimit() performs a capability check based on a comparison of the old and new resource limits. Fix the inline documentation for the hook to match the code. Implement the new hook for SELinux. For setting resource limits, we reuse the existing setrlimit permission. Note that this does overload the setrlimit permission to mean the ability to set the resource limit (soft or hard) of another process or the ability to change one's own hard limit. For getting resource limits, a new getrlimit permission is defined. This was not originally defined since getrlimit(2) could only be used to obtain a process' own limits. Signed-off-by: Stephen Smalley Signed-off-by: James Morris --- security/security.c | 8 ++++++++ security/selinux/hooks.c | 14 ++++++++++++++ security/selinux/include/classmap.h | 2 +- 3 files changed, 23 insertions(+), 1 deletion(-) (limited to 'security') diff --git a/security/security.c b/security/security.c index d0e07f269b2d..905dad2811d3 100644 --- a/security/security.c +++ b/security/security.c @@ -1036,6 +1036,12 @@ int security_task_getioprio(struct task_struct *p) return call_int_hook(task_getioprio, 0, p); } +int security_task_prlimit(const struct cred *cred, const struct cred *tcred, + unsigned int flags) +{ + return call_int_hook(task_prlimit, 0, cred, tcred, flags); +} + int security_task_setrlimit(struct task_struct *p, unsigned int resource, struct rlimit *new_rlim) { @@ -1793,6 +1799,8 @@ struct security_hook_heads security_hook_heads = { LIST_HEAD_INIT(security_hook_heads.task_setioprio), .task_getioprio = LIST_HEAD_INIT(security_hook_heads.task_getioprio), + .task_prlimit = + LIST_HEAD_INIT(security_hook_heads.task_prlimit), .task_setrlimit = LIST_HEAD_INIT(security_hook_heads.task_setrlimit), .task_setscheduler = diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index 0c2ac318aa7f..870d24ecc2de 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -3920,6 +3920,19 @@ static int selinux_task_getioprio(struct task_struct *p) PROCESS__GETSCHED, NULL); } +int selinux_task_prlimit(const struct cred *cred, const struct cred *tcred, + unsigned int flags) +{ + u32 av = 0; + + if (flags & LSM_PRLIMIT_WRITE) + av |= PROCESS__SETRLIMIT; + if (flags & LSM_PRLIMIT_READ) + av |= PROCESS__GETRLIMIT; + return avc_has_perm(cred_sid(cred), cred_sid(tcred), + SECCLASS_PROCESS, av, NULL); +} + static int selinux_task_setrlimit(struct task_struct *p, unsigned int resource, struct rlimit *new_rlim) { @@ -6206,6 +6219,7 @@ static struct security_hook_list selinux_hooks[] = { LSM_HOOK_INIT(task_setnice, selinux_task_setnice), LSM_HOOK_INIT(task_setioprio, selinux_task_setioprio), LSM_HOOK_INIT(task_getioprio, selinux_task_getioprio), + LSM_HOOK_INIT(task_prlimit, selinux_task_prlimit), LSM_HOOK_INIT(task_setrlimit, selinux_task_setrlimit), LSM_HOOK_INIT(task_setscheduler, selinux_task_setscheduler), LSM_HOOK_INIT(task_getscheduler, selinux_task_getscheduler), diff --git a/security/selinux/include/classmap.h b/security/selinux/include/classmap.h index d429c4a1c551..1e0cc9b5de20 100644 --- a/security/selinux/include/classmap.h +++ b/security/selinux/include/classmap.h @@ -47,7 +47,7 @@ struct security_class_mapping secclass_map[] = { "getattr", "setexec", "setfscreate", "noatsecure", "siginh", "setrlimit", "rlimitinh", "dyntransition", "setcurrent", "execmem", "execstack", "execheap", "setkeycreate", - "setsockcreate", NULL } }, + "setsockcreate", "getrlimit", NULL } }, { "system", { "ipc_info", "syslog_read", "syslog_mod", "syslog_console", "module_request", "module_load", NULL } }, -- cgit v1.2.3 From 84e6885e9e6a818d1ca1eabb9b720b357ab07a8b Mon Sep 17 00:00:00 2001 From: Stephen Smalley Date: Tue, 28 Feb 2017 09:35:08 -0500 Subject: selinux: fix kernel BUG on prlimit(..., NULL, NULL) commit 79bcf325e6b32b3c ("prlimit,security,selinux: add a security hook for prlimit") introduced a security hook for prlimit() and implemented it for SELinux. However, if prlimit() is called with NULL arguments for both the new limit and the old limit, then the hook is called with 0 for the read/write flags, since the prlimit() will neither read nor write the process' limits. This would in turn lead to calling avc_has_perm() with 0 for the requested permissions, which triggers a BUG_ON() in avc_has_perm_noaudit() since the kernel should never be invoking avc_has_perm() with no permissions. Fix this in the SELinux hook by returning immediately if the flags are 0. Arguably prlimit64() itself ought to return immediately if both old_rlim and new_rlim are NULL since it is effectively a no-op in that case. Reported by the lkp-robot based on trinity testing. Signed-off-by: Stephen Smalley Acked-by: Paul Moore Signed-off-by: James Morris --- security/selinux/hooks.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'security') diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index 870d24ecc2de..3ba5ce1f4e05 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -3925,6 +3925,8 @@ int selinux_task_prlimit(const struct cred *cred, const struct cred *tcred, { u32 av = 0; + if (!flags) + return 0; if (flags & LSM_PRLIMIT_WRITE) av |= PROCESS__SETRLIMIT; if (flags & LSM_PRLIMIT_READ) -- cgit v1.2.3 From dd0859dccbe291cf8179a96390f5c0e45cb9af1d Mon Sep 17 00:00:00 2001 From: James Morris Date: Wed, 15 Feb 2017 00:17:24 +1100 Subject: security: introduce CONFIG_SECURITY_WRITABLE_HOOKS Subsequent patches will add RO hardening to LSM hooks, however, SELinux still needs to be able to perform runtime disablement after init to handle architectures where init-time disablement via boot parameters is not feasible. Introduce a new kernel configuration parameter CONFIG_SECURITY_WRITABLE_HOOKS, and a helper macro __lsm_ro_after_init, to handle this case. Signed-off-by: James Morris Acked-by: Stephen Smalley Acked-by: Casey Schaufler Acked-by: Kees Cook --- include/linux/lsm_hooks.h | 7 +++++++ security/Kconfig | 5 +++++ security/selinux/Kconfig | 6 ++++++ 3 files changed, 18 insertions(+) (limited to 'security') diff --git a/include/linux/lsm_hooks.h b/include/linux/lsm_hooks.h index ba3049f05aea..1aa63335de9e 100644 --- a/include/linux/lsm_hooks.h +++ b/include/linux/lsm_hooks.h @@ -1920,6 +1920,13 @@ static inline void security_delete_hooks(struct security_hook_list *hooks, } #endif /* CONFIG_SECURITY_SELINUX_DISABLE */ +/* Currently required to handle SELinux runtime hook disable. */ +#ifdef CONFIG_SECURITY_WRITABLE_HOOKS +#define __lsm_ro_after_init +#else +#define __lsm_ro_after_init __ro_after_init +#endif /* CONFIG_SECURITY_WRITABLE_HOOKS */ + extern int __init security_module_enable(const char *module); extern void __init capability_add_hooks(void); #ifdef CONFIG_SECURITY_YAMA diff --git a/security/Kconfig b/security/Kconfig index d900f47eaa68..3ff1bf91080e 100644 --- a/security/Kconfig +++ b/security/Kconfig @@ -31,6 +31,11 @@ config SECURITY If you are unsure how to answer this question, answer N. +config SECURITY_WRITABLE_HOOKS + depends on SECURITY + bool + default n + config SECURITYFS bool "Enable the securityfs filesystem" help diff --git a/security/selinux/Kconfig b/security/selinux/Kconfig index ea7e3efbe0f7..8af7a690eb40 100644 --- a/security/selinux/Kconfig +++ b/security/selinux/Kconfig @@ -40,6 +40,7 @@ config SECURITY_SELINUX_BOOTPARAM_VALUE config SECURITY_SELINUX_DISABLE bool "NSA SELinux runtime disable" depends on SECURITY_SELINUX + select SECURITY_WRITABLE_HOOKS default n help This option enables writing to a selinuxfs node 'disable', which @@ -50,6 +51,11 @@ config SECURITY_SELINUX_DISABLE portability across platforms where boot parameters are difficult to employ. + NOTE: selecting this option will disable the '__ro_after_init' + kernel hardening feature for security hooks. Please consider + using the selinux=0 boot parameter instead of enabling this + option. + If you are unsure how to answer this question, answer N. config SECURITY_SELINUX_DEVELOP -- cgit v1.2.3 From ca97d939db114c8d1619e10a3b82af8615372dae Mon Sep 17 00:00:00 2001 From: James Morris Date: Wed, 15 Feb 2017 00:18:51 +1100 Subject: security: mark LSM hooks as __ro_after_init Mark all of the registration hooks as __ro_after_init (via the __lsm_ro_after_init macro). Signed-off-by: James Morris Acked-by: Stephen Smalley Acked-by: Kees Cook --- security/apparmor/lsm.c | 2 +- security/commoncap.c | 2 +- security/loadpin/loadpin.c | 2 +- security/security.c | 2 +- security/selinux/hooks.c | 2 +- security/smack/smack_lsm.c | 2 +- security/tomoyo/tomoyo.c | 2 +- security/yama/yama_lsm.c | 2 +- 8 files changed, 8 insertions(+), 8 deletions(-) (limited to 'security') diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c index 709eacd23909..e287b691a30e 100644 --- a/security/apparmor/lsm.c +++ b/security/apparmor/lsm.c @@ -587,7 +587,7 @@ static int apparmor_task_setrlimit(struct task_struct *task, return error; } -static struct security_hook_list apparmor_hooks[] = { +static struct security_hook_list apparmor_hooks[] __lsm_ro_after_init = { LSM_HOOK_INIT(ptrace_access_check, apparmor_ptrace_access_check), LSM_HOOK_INIT(ptrace_traceme, apparmor_ptrace_traceme), LSM_HOOK_INIT(capget, apparmor_capget), diff --git a/security/commoncap.c b/security/commoncap.c index 78b37838a2d3..7abebd782d5e 100644 --- a/security/commoncap.c +++ b/security/commoncap.c @@ -1071,7 +1071,7 @@ int cap_mmap_file(struct file *file, unsigned long reqprot, #ifdef CONFIG_SECURITY -struct security_hook_list capability_hooks[] = { +struct security_hook_list capability_hooks[] __lsm_ro_after_init = { LSM_HOOK_INIT(capable, cap_capable), LSM_HOOK_INIT(settime, cap_settime), LSM_HOOK_INIT(ptrace_access_check, cap_ptrace_access_check), diff --git a/security/loadpin/loadpin.c b/security/loadpin/loadpin.c index 1d82eae3a5b8..dbe6efde77a0 100644 --- a/security/loadpin/loadpin.c +++ b/security/loadpin/loadpin.c @@ -174,7 +174,7 @@ static int loadpin_read_file(struct file *file, enum kernel_read_file_id id) return 0; } -static struct security_hook_list loadpin_hooks[] = { +static struct security_hook_list loadpin_hooks[] __lsm_ro_after_init = { LSM_HOOK_INIT(sb_free_security, loadpin_sb_free_security), LSM_HOOK_INIT(kernel_read_file, loadpin_read_file), }; diff --git a/security/security.c b/security/security.c index 905dad2811d3..d6d18a3721aa 100644 --- a/security/security.c +++ b/security/security.c @@ -1628,7 +1628,7 @@ int security_audit_rule_match(u32 secid, u32 field, u32 op, void *lsmrule, } #endif /* CONFIG_AUDIT */ -struct security_hook_heads security_hook_heads = { +struct security_hook_heads security_hook_heads __lsm_ro_after_init = { .binder_set_context_mgr = LIST_HEAD_INIT(security_hook_heads.binder_set_context_mgr), .binder_transaction = diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index 3ba5ce1f4e05..d37a72316e9d 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -6123,7 +6123,7 @@ static int selinux_key_getsecurity(struct key *key, char **_buffer) #endif -static struct security_hook_list selinux_hooks[] = { +static struct security_hook_list selinux_hooks[] __lsm_ro_after_init = { LSM_HOOK_INIT(binder_set_context_mgr, selinux_binder_set_context_mgr), LSM_HOOK_INIT(binder_transaction, selinux_binder_transaction), LSM_HOOK_INIT(binder_transfer_binder, selinux_binder_transfer_binder), diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c index fc8fb31fc24f..927e60e622d1 100644 --- a/security/smack/smack_lsm.c +++ b/security/smack/smack_lsm.c @@ -4633,7 +4633,7 @@ static int smack_inode_getsecctx(struct inode *inode, void **ctx, u32 *ctxlen) return 0; } -static struct security_hook_list smack_hooks[] = { +static struct security_hook_list smack_hooks[] __lsm_ro_after_init = { LSM_HOOK_INIT(ptrace_access_check, smack_ptrace_access_check), LSM_HOOK_INIT(ptrace_traceme, smack_ptrace_traceme), LSM_HOOK_INIT(syslog, smack_syslog), diff --git a/security/tomoyo/tomoyo.c b/security/tomoyo/tomoyo.c index edc52d620f29..b5fb930349a9 100644 --- a/security/tomoyo/tomoyo.c +++ b/security/tomoyo/tomoyo.c @@ -496,7 +496,7 @@ static int tomoyo_socket_sendmsg(struct socket *sock, struct msghdr *msg, * tomoyo_security_ops is a "struct security_operations" which is used for * registering TOMOYO. */ -static struct security_hook_list tomoyo_hooks[] = { +static struct security_hook_list tomoyo_hooks[] __lsm_ro_after_init = { LSM_HOOK_INIT(cred_alloc_blank, tomoyo_cred_alloc_blank), LSM_HOOK_INIT(cred_prepare, tomoyo_cred_prepare), LSM_HOOK_INIT(cred_transfer, tomoyo_cred_transfer), diff --git a/security/yama/yama_lsm.c b/security/yama/yama_lsm.c index 88271a3bf37f..8298e094f4f7 100644 --- a/security/yama/yama_lsm.c +++ b/security/yama/yama_lsm.c @@ -428,7 +428,7 @@ int yama_ptrace_traceme(struct task_struct *parent) return rc; } -static struct security_hook_list yama_hooks[] = { +static struct security_hook_list yama_hooks[] __lsm_ro_after_init = { LSM_HOOK_INIT(ptrace_access_check, yama_ptrace_access_check), LSM_HOOK_INIT(ptrace_traceme, yama_ptrace_traceme), LSM_HOOK_INIT(task_prctl, yama_task_prctl), -- cgit v1.2.3 From 579fc0dc09111c1f8b94742a0ed5a0bb8fdddbb9 Mon Sep 17 00:00:00 2001 From: James Morris Date: Mon, 6 Mar 2017 11:58:08 -0500 Subject: selinux: constify nlmsg permission tables Constify nlmsg permission tables, which are initialized once and then do not change. Signed-off-by: James Morris Signed-off-by: Paul Moore --- security/selinux/nlmsgtab.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'security') diff --git a/security/selinux/nlmsgtab.c b/security/selinux/nlmsgtab.c index 2ca9cde939d4..57e2596bdd8a 100644 --- a/security/selinux/nlmsgtab.c +++ b/security/selinux/nlmsgtab.c @@ -28,7 +28,7 @@ struct nlmsg_perm { u32 perm; }; -static struct nlmsg_perm nlmsg_route_perms[] = +static const struct nlmsg_perm nlmsg_route_perms[] = { { RTM_NEWLINK, NETLINK_ROUTE_SOCKET__NLMSG_WRITE }, { RTM_DELLINK, NETLINK_ROUTE_SOCKET__NLMSG_WRITE }, @@ -80,7 +80,7 @@ static struct nlmsg_perm nlmsg_route_perms[] = { RTM_GETSTATS, NETLINK_ROUTE_SOCKET__NLMSG_READ }, }; -static struct nlmsg_perm nlmsg_tcpdiag_perms[] = +static const struct nlmsg_perm nlmsg_tcpdiag_perms[] = { { TCPDIAG_GETSOCK, NETLINK_TCPDIAG_SOCKET__NLMSG_READ }, { DCCPDIAG_GETSOCK, NETLINK_TCPDIAG_SOCKET__NLMSG_READ }, @@ -88,7 +88,7 @@ static struct nlmsg_perm nlmsg_tcpdiag_perms[] = { SOCK_DESTROY, NETLINK_TCPDIAG_SOCKET__NLMSG_WRITE }, }; -static struct nlmsg_perm nlmsg_xfrm_perms[] = +static const struct nlmsg_perm nlmsg_xfrm_perms[] = { { XFRM_MSG_NEWSA, NETLINK_XFRM_SOCKET__NLMSG_WRITE }, { XFRM_MSG_DELSA, NETLINK_XFRM_SOCKET__NLMSG_WRITE }, @@ -115,7 +115,7 @@ static struct nlmsg_perm nlmsg_xfrm_perms[] = { XFRM_MSG_MAPPING, NETLINK_XFRM_SOCKET__NLMSG_READ }, }; -static struct nlmsg_perm nlmsg_audit_perms[] = +static const struct nlmsg_perm nlmsg_audit_perms[] = { { AUDIT_GET, NETLINK_AUDIT_SOCKET__NLMSG_READ }, { AUDIT_SET, NETLINK_AUDIT_SOCKET__NLMSG_WRITE }, @@ -136,7 +136,7 @@ static struct nlmsg_perm nlmsg_audit_perms[] = }; -static int nlmsg_perm(u16 nlmsg_type, u32 *perm, struct nlmsg_perm *tab, size_t tabsize) +static int nlmsg_perm(u16 nlmsg_type, u32 *perm, const struct nlmsg_perm *tab, size_t tabsize) { int i, err = -EINVAL; -- cgit v1.2.3 From bad4417b692ede5cf31105b329cea1544875b526 Mon Sep 17 00:00:00 2001 From: James Morris Date: Mon, 13 Feb 2017 16:34:35 +1100 Subject: integrity: mark default IMA rules as __ro_after_init The default IMA rules are loaded during init and then do not change, so mark them as __ro_after_init. Signed-off-by: James Morris Signed-off-by: Mimi Zohar --- security/integrity/ima/ima_policy.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'security') diff --git a/security/integrity/ima/ima_policy.c b/security/integrity/ima/ima_policy.c index aed47b777a57..e8498a3f4887 100644 --- a/security/integrity/ima/ima_policy.c +++ b/security/integrity/ima/ima_policy.c @@ -83,7 +83,7 @@ struct ima_rule_entry { * normal users can easily run the machine out of memory simply building * and running executables. */ -static struct ima_rule_entry dont_measure_rules[] = { +static struct ima_rule_entry dont_measure_rules[] __ro_after_init = { {.action = DONT_MEASURE, .fsmagic = PROC_SUPER_MAGIC, .flags = IMA_FSMAGIC}, {.action = DONT_MEASURE, .fsmagic = SYSFS_MAGIC, .flags = IMA_FSMAGIC}, {.action = DONT_MEASURE, .fsmagic = DEBUGFS_MAGIC, .flags = IMA_FSMAGIC}, @@ -97,7 +97,7 @@ static struct ima_rule_entry dont_measure_rules[] = { {.action = DONT_MEASURE, .fsmagic = NSFS_MAGIC, .flags = IMA_FSMAGIC} }; -static struct ima_rule_entry original_measurement_rules[] = { +static struct ima_rule_entry original_measurement_rules[] __ro_after_init = { {.action = MEASURE, .func = MMAP_CHECK, .mask = MAY_EXEC, .flags = IMA_FUNC | IMA_MASK}, {.action = MEASURE, .func = BPRM_CHECK, .mask = MAY_EXEC, @@ -108,7 +108,7 @@ static struct ima_rule_entry original_measurement_rules[] = { {.action = MEASURE, .func = FIRMWARE_CHECK, .flags = IMA_FUNC}, }; -static struct ima_rule_entry default_measurement_rules[] = { +static struct ima_rule_entry default_measurement_rules[] __ro_after_init = { {.action = MEASURE, .func = MMAP_CHECK, .mask = MAY_EXEC, .flags = IMA_FUNC | IMA_MASK}, {.action = MEASURE, .func = BPRM_CHECK, .mask = MAY_EXEC, @@ -122,7 +122,7 @@ static struct ima_rule_entry default_measurement_rules[] = { {.action = MEASURE, .func = POLICY_CHECK, .flags = IMA_FUNC}, }; -static struct ima_rule_entry default_appraise_rules[] = { +static struct ima_rule_entry default_appraise_rules[] __ro_after_init = { {.action = DONT_APPRAISE, .fsmagic = PROC_SUPER_MAGIC, .flags = IMA_FSMAGIC}, {.action = DONT_APPRAISE, .fsmagic = SYSFS_MAGIC, .flags = IMA_FSMAGIC}, {.action = DONT_APPRAISE, .fsmagic = DEBUGFS_MAGIC, .flags = IMA_FSMAGIC}, -- cgit v1.2.3 From 1ac202e978e18f045006d75bd549612620c6ec3a Mon Sep 17 00:00:00 2001 From: Daniel Glöckner Date: Fri, 24 Feb 2017 15:05:14 +0100 Subject: ima: accept previously set IMA_NEW_FILE MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Modifying the attributes of a file makes ima_inode_post_setattr reset the IMA cache flags. So if the file, which has just been created, is opened a second time before the first file descriptor is closed, verification fails since the security.ima xattr has not been written yet. We therefore have to look at the IMA_NEW_FILE even if the file already existed. With this patch there should no longer be an error when cat tries to open testfile: $ rm -f testfile $ ( echo test >&3 ; touch testfile ; cat testfile ) 3>testfile A file being new is no reason to accept that it is missing a digital signature demanded by the policy. Signed-off-by: Daniel Glöckner Cc: stable@vger.kernel.org Signed-off-by: Mimi Zohar --- security/integrity/ima/ima_appraise.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'security') diff --git a/security/integrity/ima/ima_appraise.c b/security/integrity/ima/ima_appraise.c index 1fd9539a969d..5d0785cfe063 100644 --- a/security/integrity/ima/ima_appraise.c +++ b/security/integrity/ima/ima_appraise.c @@ -207,10 +207,11 @@ int ima_appraise_measurement(enum ima_hooks func, cause = "missing-hash"; status = INTEGRITY_NOLABEL; - if (opened & FILE_CREATED) { + if (opened & FILE_CREATED) iint->flags |= IMA_NEW_FILE; + if ((iint->flags & IMA_NEW_FILE) && + !(iint->flags & IMA_DIGSIG_REQUIRED)) status = INTEGRITY_PASS; - } goto out; } -- cgit v1.2.3 From e2f586bd83177d22072b275edd4b8b872daba924 Mon Sep 17 00:00:00 2001 From: Alexander Potapenko Date: Mon, 6 Mar 2017 19:46:14 +0100 Subject: selinux: check for address length in selinux_socket_bind() KMSAN (KernelMemorySanitizer, a new error detection tool) reports use of uninitialized memory in selinux_socket_bind(): ================================================================== BUG: KMSAN: use of unitialized memory inter: 0 CPU: 3 PID: 1074 Comm: packet2 Tainted: G B 4.8.0-rc6+ #1916 Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS Bochs 01/01/2011 0000000000000000 ffff8800882ffb08 ffffffff825759c8 ffff8800882ffa48 ffffffff818bf551 ffffffff85bab870 0000000000000092 ffffffff85bab550 0000000000000000 0000000000000092 00000000bb0009bb 0000000000000002 Call Trace: [< inline >] __dump_stack lib/dump_stack.c:15 [] dump_stack+0x238/0x290 lib/dump_stack.c:51 [] kmsan_report+0x276/0x2e0 mm/kmsan/kmsan.c:1008 [] __msan_warning+0x5b/0xb0 mm/kmsan/kmsan_instr.c:424 [] selinux_socket_bind+0xf41/0x1080 security/selinux/hooks.c:4288 [] security_socket_bind+0x1ec/0x240 security/security.c:1240 [] SYSC_bind+0x358/0x5f0 net/socket.c:1366 [] SyS_bind+0x82/0xa0 net/socket.c:1356 [] do_syscall_64+0x58/0x70 arch/x86/entry/common.c:292 [] entry_SYSCALL64_slow_path+0x25/0x25 arch/x86/entry/entry_64.o:? chained origin: 00000000ba6009bb [] save_stack_trace+0x27/0x50 arch/x86/kernel/stacktrace.c:67 [< inline >] kmsan_save_stack_with_flags mm/kmsan/kmsan.c:322 [< inline >] kmsan_save_stack mm/kmsan/kmsan.c:337 [] kmsan_internal_chain_origin+0x118/0x1e0 mm/kmsan/kmsan.c:530 [] __msan_set_alloca_origin4+0xc3/0x130 mm/kmsan/kmsan_instr.c:380 [] SYSC_bind+0x129/0x5f0 net/socket.c:1356 [] SyS_bind+0x82/0xa0 net/socket.c:1356 [] do_syscall_64+0x58/0x70 arch/x86/entry/common.c:292 [] return_from_SYSCALL_64+0x0/0x6a arch/x86/entry/entry_64.o:? origin description: ----address@SYSC_bind (origin=00000000b8c00900) ================================================================== (the line numbers are relative to 4.8-rc6, but the bug persists upstream) , when I run the following program as root: ======================================================= #include #include #include int main(int argc, char *argv[]) { struct sockaddr addr; int size = 0; if (argc > 1) { size = atoi(argv[1]); } memset(&addr, 0, sizeof(addr)); int fd = socket(PF_INET6, SOCK_DGRAM, IPPROTO_IP); bind(fd, &addr, size); return 0; } ======================================================= (for different values of |size| other error reports are printed). This happens because bind() unconditionally copies |size| bytes of |addr| to the kernel, leaving the rest uninitialized. Then security_socket_bind() reads the IP address bytes, including the uninitialized ones, to determine the port, or e.g. pass them further to sel_netnode_find(), which uses them to calculate a hash. Signed-off-by: Alexander Potapenko Acked-by: Eric Dumazet [PM: fixed some whitespace damage] Signed-off-by: Paul Moore --- security/selinux/hooks.c | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'security') diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index d37a72316e9d..e67a526d1f30 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -4367,10 +4367,18 @@ static int selinux_socket_bind(struct socket *sock, struct sockaddr *address, in u32 sid, node_perm; if (family == PF_INET) { + if (addrlen < sizeof(struct sockaddr_in)) { + err = -EINVAL; + goto out; + } addr4 = (struct sockaddr_in *)address; snum = ntohs(addr4->sin_port); addrp = (char *)&addr4->sin_addr.s_addr; } else { + if (addrlen < SIN6_LEN_RFC2133) { + err = -EINVAL; + goto out; + } addr6 = (struct sockaddr_in6 *)address; snum = ntohs(addr6->sin6_port); addrp = (char *)&addr6->sin6_addr.s6_addr; -- cgit v1.2.3 From 3dd0c8d06511c7c61c62305fcf431ca28884d263 Mon Sep 17 00:00:00 2001 From: Mikhail Kurinnoi Date: Fri, 27 Jan 2017 19:23:01 +0300 Subject: ima: provide ">" and "<" operators for fowner/uid/euid rules. For now we have only "=" operator for fowner/uid/euid rules. This patch provide two more operators - ">" and "<" in order to make fowner/uid/euid rules more flexible. Examples of usage. Appraise all files owned by special and system users (SYS_UID_MAX 999): appraise fowner<1000 Don't appraise files owned by normal users (UID_MIN 1000): dont_appraise fowner>999 Appraise all files owned by users with UID 1000-1010: dont_appraise fowner>1010 appraise fowner>999 Changelog v3: - Removed code duplication in ima_parse_rule(). - Fix ima_policy_show() - (Mimi) Changelog v2: - Fixed default policy rules. Signed-off-by: Mikhail Kurinnoi Signed-off-by: Mimi Zohar security/integrity/ima/ima_policy.c | 115 +++++++++++++++++++++++++++--------- 1 file changed, 87 insertions(+), 28 deletions(-) --- security/integrity/ima/ima_policy.c | 115 +++++++++++++++++++++++++++--------- 1 file changed, 87 insertions(+), 28 deletions(-) (limited to 'security') diff --git a/security/integrity/ima/ima_policy.c b/security/integrity/ima/ima_policy.c index e8498a3f4887..3ab1067db624 100644 --- a/security/integrity/ima/ima_policy.c +++ b/security/integrity/ima/ima_policy.c @@ -64,6 +64,8 @@ struct ima_rule_entry { u8 fsuuid[16]; kuid_t uid; kuid_t fowner; + bool (*uid_op)(kuid_t, kuid_t); /* Handlers for operators */ + bool (*fowner_op)(kuid_t, kuid_t); /* uid_eq(), uid_gt(), uid_lt() */ int pcr; struct { void *rule; /* LSM file metadata specific */ @@ -103,7 +105,8 @@ static struct ima_rule_entry original_measurement_rules[] __ro_after_init = { {.action = MEASURE, .func = BPRM_CHECK, .mask = MAY_EXEC, .flags = IMA_FUNC | IMA_MASK}, {.action = MEASURE, .func = FILE_CHECK, .mask = MAY_READ, - .uid = GLOBAL_ROOT_UID, .flags = IMA_FUNC | IMA_MASK | IMA_UID}, + .uid = GLOBAL_ROOT_UID, .uid_op = &uid_eq, + .flags = IMA_FUNC | IMA_MASK | IMA_UID}, {.action = MEASURE, .func = MODULE_CHECK, .flags = IMA_FUNC}, {.action = MEASURE, .func = FIRMWARE_CHECK, .flags = IMA_FUNC}, }; @@ -114,9 +117,11 @@ static struct ima_rule_entry default_measurement_rules[] __ro_after_init = { {.action = MEASURE, .func = BPRM_CHECK, .mask = MAY_EXEC, .flags = IMA_FUNC | IMA_MASK}, {.action = MEASURE, .func = FILE_CHECK, .mask = MAY_READ, - .uid = GLOBAL_ROOT_UID, .flags = IMA_FUNC | IMA_INMASK | IMA_EUID}, + .uid = GLOBAL_ROOT_UID, .uid_op = &uid_eq, + .flags = IMA_FUNC | IMA_INMASK | IMA_EUID}, {.action = MEASURE, .func = FILE_CHECK, .mask = MAY_READ, - .uid = GLOBAL_ROOT_UID, .flags = IMA_FUNC | IMA_INMASK | IMA_UID}, + .uid = GLOBAL_ROOT_UID, .uid_op = &uid_eq, + .flags = IMA_FUNC | IMA_INMASK | IMA_UID}, {.action = MEASURE, .func = MODULE_CHECK, .flags = IMA_FUNC}, {.action = MEASURE, .func = FIRMWARE_CHECK, .flags = IMA_FUNC}, {.action = MEASURE, .func = POLICY_CHECK, .flags = IMA_FUNC}, @@ -139,10 +144,11 @@ static struct ima_rule_entry default_appraise_rules[] __ro_after_init = { .flags = IMA_FUNC | IMA_DIGSIG_REQUIRED}, #endif #ifndef CONFIG_IMA_APPRAISE_SIGNED_INIT - {.action = APPRAISE, .fowner = GLOBAL_ROOT_UID, .flags = IMA_FOWNER}, + {.action = APPRAISE, .fowner = GLOBAL_ROOT_UID, .fowner_op = &uid_eq, + .flags = IMA_FOWNER}, #else /* force signature */ - {.action = APPRAISE, .fowner = GLOBAL_ROOT_UID, + {.action = APPRAISE, .fowner = GLOBAL_ROOT_UID, .fowner_op = &uid_eq, .flags = IMA_FOWNER | IMA_DIGSIG_REQUIRED}, #endif }; @@ -240,19 +246,20 @@ static bool ima_match_rules(struct ima_rule_entry *rule, struct inode *inode, if ((rule->flags & IMA_FSUUID) && memcmp(rule->fsuuid, inode->i_sb->s_uuid, sizeof(rule->fsuuid))) return false; - if ((rule->flags & IMA_UID) && !uid_eq(rule->uid, cred->uid)) + if ((rule->flags & IMA_UID) && !rule->uid_op(cred->uid, rule->uid)) return false; if (rule->flags & IMA_EUID) { if (has_capability_noaudit(current, CAP_SETUID)) { - if (!uid_eq(rule->uid, cred->euid) - && !uid_eq(rule->uid, cred->suid) - && !uid_eq(rule->uid, cred->uid)) + if (!rule->uid_op(cred->euid, rule->uid) + && !rule->uid_op(cred->suid, rule->uid) + && !rule->uid_op(cred->uid, rule->uid)) return false; - } else if (!uid_eq(rule->uid, cred->euid)) + } else if (!rule->uid_op(cred->euid, rule->uid)) return false; } - if ((rule->flags & IMA_FOWNER) && !uid_eq(rule->fowner, inode->i_uid)) + if ((rule->flags & IMA_FOWNER) && + !rule->fowner_op(inode->i_uid, rule->fowner)) return false; for (i = 0; i < MAX_LSM_RULES; i++) { int rc = 0; @@ -486,7 +493,9 @@ enum { Opt_obj_user, Opt_obj_role, Opt_obj_type, Opt_subj_user, Opt_subj_role, Opt_subj_type, Opt_func, Opt_mask, Opt_fsmagic, - Opt_fsuuid, Opt_uid, Opt_euid, Opt_fowner, + Opt_fsuuid, Opt_uid_eq, Opt_euid_eq, Opt_fowner_eq, + Opt_uid_gt, Opt_euid_gt, Opt_fowner_gt, + Opt_uid_lt, Opt_euid_lt, Opt_fowner_lt, Opt_appraise_type, Opt_permit_directio, Opt_pcr }; @@ -507,9 +516,15 @@ static match_table_t policy_tokens = { {Opt_mask, "mask=%s"}, {Opt_fsmagic, "fsmagic=%s"}, {Opt_fsuuid, "fsuuid=%s"}, - {Opt_uid, "uid=%s"}, - {Opt_euid, "euid=%s"}, - {Opt_fowner, "fowner=%s"}, + {Opt_uid_eq, "uid=%s"}, + {Opt_euid_eq, "euid=%s"}, + {Opt_fowner_eq, "fowner=%s"}, + {Opt_uid_gt, "uid>%s"}, + {Opt_euid_gt, "euid>%s"}, + {Opt_fowner_gt, "fowner>%s"}, + {Opt_uid_lt, "uid<%s"}, + {Opt_euid_lt, "euid<%s"}, + {Opt_fowner_lt, "fowner<%s"}, {Opt_appraise_type, "appraise_type=%s"}, {Opt_permit_directio, "permit_directio"}, {Opt_pcr, "pcr=%s"}, @@ -541,24 +556,37 @@ static int ima_lsm_rule_init(struct ima_rule_entry *entry, return result; } -static void ima_log_string(struct audit_buffer *ab, char *key, char *value) +static void ima_log_string_op(struct audit_buffer *ab, char *key, char *value, + bool (*rule_operator)(kuid_t, kuid_t)) { - audit_log_format(ab, "%s=", key); + if (rule_operator == &uid_gt) + audit_log_format(ab, "%s>", key); + else if (rule_operator == &uid_lt) + audit_log_format(ab, "%s<", key); + else + audit_log_format(ab, "%s=", key); audit_log_untrustedstring(ab, value); audit_log_format(ab, " "); } +static void ima_log_string(struct audit_buffer *ab, char *key, char *value) +{ + ima_log_string_op(ab, key, value, NULL); +} static int ima_parse_rule(char *rule, struct ima_rule_entry *entry) { struct audit_buffer *ab; char *from; char *p; + bool uid_token; int result = 0; ab = audit_log_start(NULL, GFP_KERNEL, AUDIT_INTEGRITY_RULE); entry->uid = INVALID_UID; entry->fowner = INVALID_UID; + entry->uid_op = &uid_eq; + entry->fowner_op = &uid_eq; entry->action = UNKNOWN; while ((p = strsep(&rule, " \t")) != NULL) { substring_t args[MAX_OPT_ARGS]; @@ -694,11 +722,21 @@ static int ima_parse_rule(char *rule, struct ima_rule_entry *entry) if (!result) entry->flags |= IMA_FSUUID; break; - case Opt_uid: - ima_log_string(ab, "uid", args[0].from); - case Opt_euid: - if (token == Opt_euid) - ima_log_string(ab, "euid", args[0].from); + case Opt_uid_gt: + case Opt_euid_gt: + entry->uid_op = &uid_gt; + case Opt_uid_lt: + case Opt_euid_lt: + if ((token == Opt_uid_lt) || (token == Opt_euid_lt)) + entry->uid_op = &uid_lt; + case Opt_uid_eq: + case Opt_euid_eq: + uid_token = (token == Opt_uid_eq) || + (token == Opt_uid_gt) || + (token == Opt_uid_lt); + + ima_log_string_op(ab, uid_token ? "uid" : "euid", + args[0].from, entry->uid_op); if (uid_valid(entry->uid)) { result = -EINVAL; @@ -713,12 +751,18 @@ static int ima_parse_rule(char *rule, struct ima_rule_entry *entry) (uid_t)lnum != lnum) result = -EINVAL; else - entry->flags |= (token == Opt_uid) + entry->flags |= uid_token ? IMA_UID : IMA_EUID; } break; - case Opt_fowner: - ima_log_string(ab, "fowner", args[0].from); + case Opt_fowner_gt: + entry->fowner_op = &uid_gt; + case Opt_fowner_lt: + if (token == Opt_fowner_lt) + entry->fowner_op = &uid_lt; + case Opt_fowner_eq: + ima_log_string_op(ab, "fowner", args[0].from, + entry->fowner_op); if (uid_valid(entry->fowner)) { result = -EINVAL; @@ -1049,19 +1093,34 @@ int ima_policy_show(struct seq_file *m, void *v) if (entry->flags & IMA_UID) { snprintf(tbuf, sizeof(tbuf), "%d", __kuid_val(entry->uid)); - seq_printf(m, pt(Opt_uid), tbuf); + if (entry->uid_op == &uid_gt) + seq_printf(m, pt(Opt_uid_gt), tbuf); + else if (entry->uid_op == &uid_lt) + seq_printf(m, pt(Opt_uid_lt), tbuf); + else + seq_printf(m, pt(Opt_uid_eq), tbuf); seq_puts(m, " "); } if (entry->flags & IMA_EUID) { snprintf(tbuf, sizeof(tbuf), "%d", __kuid_val(entry->uid)); - seq_printf(m, pt(Opt_euid), tbuf); + if (entry->uid_op == &uid_gt) + seq_printf(m, pt(Opt_euid_gt), tbuf); + else if (entry->uid_op == &uid_lt) + seq_printf(m, pt(Opt_euid_lt), tbuf); + else + seq_printf(m, pt(Opt_euid_eq), tbuf); seq_puts(m, " "); } if (entry->flags & IMA_FOWNER) { snprintf(tbuf, sizeof(tbuf), "%d", __kuid_val(entry->fowner)); - seq_printf(m, pt(Opt_fowner), tbuf); + if (entry->fowner_op == &uid_gt) + seq_printf(m, pt(Opt_fowner_gt), tbuf); + else if (entry->fowner_op == &uid_lt) + seq_printf(m, pt(Opt_fowner_lt), tbuf); + else + seq_printf(m, pt(Opt_fowner_eq), tbuf); seq_puts(m, " "); } -- cgit v1.2.3 From f6076f704aa29679fdba114b0f60e71e0884840a Mon Sep 17 00:00:00 2001 From: Markus Elfring Date: Sat, 14 Jan 2017 10:48:28 +0100 Subject: selinux: Use kmalloc_array() in cond_init_bool_indexes() * A multiplication for the size determination of a memory allocation indicated that an array data structure should be processed. Thus use the corresponding function "kmalloc_array". This issue was detected by using the Coccinelle software. * Replace the specification of a data type by a pointer dereference to make the corresponding size determination a bit safer according to the Linux coding style convention. Signed-off-by: Markus Elfring Signed-off-by: Paul Moore --- security/selinux/ss/conditional.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'security') diff --git a/security/selinux/ss/conditional.c b/security/selinux/ss/conditional.c index 34afeadd9e73..fcfab2635c11 100644 --- a/security/selinux/ss/conditional.c +++ b/security/selinux/ss/conditional.c @@ -176,8 +176,9 @@ void cond_policydb_destroy(struct policydb *p) int cond_init_bool_indexes(struct policydb *p) { kfree(p->bool_val_to_struct); - p->bool_val_to_struct = - kmalloc(p->p_bools.nprim * sizeof(struct cond_bool_datum *), GFP_KERNEL); + p->bool_val_to_struct = kmalloc_array(p->p_bools.nprim, + sizeof(*p->bool_val_to_struct), + GFP_KERNEL); if (!p->bool_val_to_struct) return -ENOMEM; return 0; -- cgit v1.2.3 From e34cfef901badc0e17c2b296a535e7412ef038f3 Mon Sep 17 00:00:00 2001 From: Markus Elfring Date: Sat, 14 Jan 2017 11:00:23 +0100 Subject: selinux: Delete an unnecessary return statement in cond_compute_av() The script "checkpatch.pl" pointed information out like the following. WARNING: void function return statements are not generally useful Thus remove such a statement in the affected function. Signed-off-by: Markus Elfring Signed-off-by: Paul Moore --- security/selinux/ss/conditional.c | 1 - 1 file changed, 1 deletion(-) (limited to 'security') diff --git a/security/selinux/ss/conditional.c b/security/selinux/ss/conditional.c index fcfab2635c11..4a3bf29f7565 100644 --- a/security/selinux/ss/conditional.c +++ b/security/selinux/ss/conditional.c @@ -664,5 +664,4 @@ void cond_compute_av(struct avtab *ctab, struct avtab_key *key, (node->key.specified & AVTAB_XPERMS)) services_compute_xperms_drivers(xperms, node); } - return; } -- cgit v1.2.3 From fb13a312daa11005b0230695a1d6a0b4e2b27069 Mon Sep 17 00:00:00 2001 From: Markus Elfring Date: Sat, 14 Jan 2017 11:22:12 +0100 Subject: selinux: Improve size determinations in four functions Replace the specification of data structures by pointer dereferences as the parameter for the operator "sizeof" to make the corresponding size determination a bit safer. Signed-off-by: Markus Elfring Signed-off-by: Paul Moore --- security/selinux/ss/conditional.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'security') diff --git a/security/selinux/ss/conditional.c b/security/selinux/ss/conditional.c index 4a3bf29f7565..771c96afe1d5 100644 --- a/security/selinux/ss/conditional.c +++ b/security/selinux/ss/conditional.c @@ -227,7 +227,7 @@ int cond_read_bool(struct policydb *p, struct hashtab *h, void *fp) u32 len; int rc; - booldatum = kzalloc(sizeof(struct cond_bool_datum), GFP_KERNEL); + booldatum = kzalloc(sizeof(*booldatum), GFP_KERNEL); if (!booldatum) return -ENOMEM; @@ -332,7 +332,7 @@ static int cond_insertf(struct avtab *a, struct avtab_key *k, struct avtab_datum goto err; } - list = kzalloc(sizeof(struct cond_av_list), GFP_KERNEL); + list = kzalloc(sizeof(*list), GFP_KERNEL); if (!list) { rc = -ENOMEM; goto err; @@ -421,7 +421,7 @@ static int cond_read_node(struct policydb *p, struct cond_node *node, void *fp) goto err; rc = -ENOMEM; - expr = kzalloc(sizeof(struct cond_expr), GFP_KERNEL); + expr = kzalloc(sizeof(*expr), GFP_KERNEL); if (!expr) goto err; @@ -472,7 +472,7 @@ int cond_read_list(struct policydb *p, void *fp) for (i = 0; i < len; i++) { rc = -ENOMEM; - node = kzalloc(sizeof(struct cond_node), GFP_KERNEL); + node = kzalloc(sizeof(*node), GFP_KERNEL); if (!node) goto err; -- cgit v1.2.3 From 2f00e680fe25d8d2758879b772d54cb46a78b59d Mon Sep 17 00:00:00 2001 From: Markus Elfring Date: Sat, 14 Jan 2017 12:06:13 +0100 Subject: selinux: Use kmalloc_array() in hashtab_create() A multiplication for the size determination of a memory allocation indicated that an array data structure should be processed. Thus use the corresponding function "kmalloc_array". This issue was detected by using the Coccinelle software. Signed-off-by: Markus Elfring Signed-off-by: Paul Moore --- security/selinux/ss/hashtab.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'security') diff --git a/security/selinux/ss/hashtab.c b/security/selinux/ss/hashtab.c index 2cc496149842..dc99fff64ecb 100644 --- a/security/selinux/ss/hashtab.c +++ b/security/selinux/ss/hashtab.c @@ -24,7 +24,7 @@ struct hashtab *hashtab_create(u32 (*hash_value)(struct hashtab *h, const void * p->nel = 0; p->hash_value = hash_value; p->keycmp = keycmp; - p->htable = kmalloc(sizeof(*(p->htable)) * size, GFP_KERNEL); + p->htable = kmalloc_array(size, sizeof(*p->htable), GFP_KERNEL); if (p->htable == NULL) { kfree(p); return NULL; -- cgit v1.2.3 From cb8d21e3640f18444c597bddaec156637eacecf8 Mon Sep 17 00:00:00 2001 From: Markus Elfring Date: Sat, 14 Jan 2017 12:36:59 +0100 Subject: selinux: Adjust four checks for null pointers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The script "checkpatch.pl" pointed information out like the following. Comparison to NULL could be written !… Thus fix affected source code places. Signed-off-by: Markus Elfring Signed-off-by: Paul Moore --- security/selinux/ss/hashtab.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'security') diff --git a/security/selinux/ss/hashtab.c b/security/selinux/ss/hashtab.c index dc99fff64ecb..3858706a29fb 100644 --- a/security/selinux/ss/hashtab.c +++ b/security/selinux/ss/hashtab.c @@ -17,7 +17,7 @@ struct hashtab *hashtab_create(u32 (*hash_value)(struct hashtab *h, const void * u32 i; p = kzalloc(sizeof(*p), GFP_KERNEL); - if (p == NULL) + if (!p) return p; p->size = size; @@ -25,7 +25,7 @@ struct hashtab *hashtab_create(u32 (*hash_value)(struct hashtab *h, const void * p->hash_value = hash_value; p->keycmp = keycmp; p->htable = kmalloc_array(size, sizeof(*p->htable), GFP_KERNEL); - if (p->htable == NULL) { + if (!p->htable) { kfree(p); return NULL; } @@ -58,7 +58,7 @@ int hashtab_insert(struct hashtab *h, void *key, void *datum) return -EEXIST; newnode = kzalloc(sizeof(*newnode), GFP_KERNEL); - if (newnode == NULL) + if (!newnode) return -ENOMEM; newnode->key = key; newnode->datum = datum; @@ -87,7 +87,7 @@ void *hashtab_search(struct hashtab *h, const void *key) while (cur && h->keycmp(h, key, cur->key) > 0) cur = cur->next; - if (cur == NULL || (h->keycmp(h, key, cur->key) != 0)) + if (!cur || (h->keycmp(h, key, cur->key) != 0)) return NULL; return cur->datum; -- cgit v1.2.3 From ad10a10567e243425d7be35a3d950709371fa048 Mon Sep 17 00:00:00 2001 From: Markus Elfring Date: Sat, 14 Jan 2017 13:08:59 +0100 Subject: selinux: Use kcalloc() in policydb_index() Multiplications for the size determination of memory allocations indicated that array data structures should be processed. Thus use the corresponding function "kcalloc". This issue was detected by using the Coccinelle software. Signed-off-by: Markus Elfring Signed-off-by: Paul Moore --- security/selinux/ss/policydb.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) (limited to 'security') diff --git a/security/selinux/ss/policydb.c b/security/selinux/ss/policydb.c index 9c92f29a38ea..66b9a357fa1b 100644 --- a/security/selinux/ss/policydb.c +++ b/security/selinux/ss/policydb.c @@ -540,23 +540,23 @@ static int policydb_index(struct policydb *p) #endif rc = -ENOMEM; - p->class_val_to_struct = - kzalloc(p->p_classes.nprim * sizeof(*(p->class_val_to_struct)), - GFP_KERNEL); + p->class_val_to_struct = kcalloc(p->p_classes.nprim, + sizeof(*p->class_val_to_struct), + GFP_KERNEL); if (!p->class_val_to_struct) goto out; rc = -ENOMEM; - p->role_val_to_struct = - kzalloc(p->p_roles.nprim * sizeof(*(p->role_val_to_struct)), - GFP_KERNEL); + p->role_val_to_struct = kcalloc(p->p_roles.nprim, + sizeof(*p->role_val_to_struct), + GFP_KERNEL); if (!p->role_val_to_struct) goto out; rc = -ENOMEM; - p->user_val_to_struct = - kzalloc(p->p_users.nprim * sizeof(*(p->user_val_to_struct)), - GFP_KERNEL); + p->user_val_to_struct = kcalloc(p->p_users.nprim, + sizeof(*p->user_val_to_struct), + GFP_KERNEL); if (!p->user_val_to_struct) goto out; -- cgit v1.2.3 From b4e4686f65a3092f63ed01c887d9f56714d29f4a Mon Sep 17 00:00:00 2001 From: Markus Elfring Date: Sat, 14 Jan 2017 14:00:02 +0100 Subject: selinux: Delete an unnecessary return statement in policydb_destroy() The script "checkpatch.pl" pointed information out like the following. WARNING: void function return statements are not generally useful Thus remove such a statement in the affected function. Signed-off-by: Markus Elfring Signed-off-by: Paul Moore --- security/selinux/ss/policydb.c | 2 -- 1 file changed, 2 deletions(-) (limited to 'security') diff --git a/security/selinux/ss/policydb.c b/security/selinux/ss/policydb.c index 66b9a357fa1b..bccc9acf6bc5 100644 --- a/security/selinux/ss/policydb.c +++ b/security/selinux/ss/policydb.c @@ -880,8 +880,6 @@ void policydb_destroy(struct policydb *p) ebitmap_destroy(&p->filename_trans_ttypes); ebitmap_destroy(&p->policycaps); ebitmap_destroy(&p->permissive_map); - - return; } /* -- cgit v1.2.3 From 3a0aa56518432a1a598ac3e48a93f2e99c66a393 Mon Sep 17 00:00:00 2001 From: Markus Elfring Date: Sat, 14 Jan 2017 16:34:25 +0100 Subject: selinux: Return directly after a failed next_entry() in genfs_read() Return directly after a call of the function "next_entry" failed at the beginning. Signed-off-by: Markus Elfring Signed-off-by: Paul Moore --- security/selinux/ss/policydb.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'security') diff --git a/security/selinux/ss/policydb.c b/security/selinux/ss/policydb.c index bccc9acf6bc5..375e304070e1 100644 --- a/security/selinux/ss/policydb.c +++ b/security/selinux/ss/policydb.c @@ -2006,7 +2006,7 @@ static int genfs_read(struct policydb *p, void *fp) rc = next_entry(buf, fp, sizeof(u32)); if (rc) - goto out; + return rc; nel = le32_to_cpu(buf[0]); for (i = 0; i < nel; i++) { -- cgit v1.2.3 From 315e01ada8047cba0520ecca050ad8f5237abb41 Mon Sep 17 00:00:00 2001 From: Markus Elfring Date: Sat, 14 Jan 2017 17:43:47 +0100 Subject: selinux: One function call less in genfs_read() after null pointer detection Call the function "kfree" at the end only after it was determined that the local variable "newgenfs" contained a non-null pointer. Signed-off-by: Markus Elfring Signed-off-by: Paul Moore --- security/selinux/ss/policydb.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'security') diff --git a/security/selinux/ss/policydb.c b/security/selinux/ss/policydb.c index 375e304070e1..4390558464c5 100644 --- a/security/selinux/ss/policydb.c +++ b/security/selinux/ss/policydb.c @@ -2098,9 +2098,10 @@ static int genfs_read(struct policydb *p, void *fp) } rc = 0; out: - if (newgenfs) + if (newgenfs) { kfree(newgenfs->fstype); - kfree(newgenfs); + kfree(newgenfs); + } ocontext_destroy(newc, OCON_FSUSE); return rc; -- cgit v1.2.3 From 02fcef27cc908e22ddb68d30ad1b7fd9ac3a1c24 Mon Sep 17 00:00:00 2001 From: Markus Elfring Date: Sat, 14 Jan 2017 19:02:42 +0100 Subject: selinux: Delete an unnecessary variable assignment in filename_trans_read() The local variable "ft" was set to a null pointer despite of an immediate reassignment. Thus remove this statement from the beginning of a loop. Signed-off-by: Markus Elfring Signed-off-by: Paul Moore --- security/selinux/ss/policydb.c | 1 - 1 file changed, 1 deletion(-) (limited to 'security') diff --git a/security/selinux/ss/policydb.c b/security/selinux/ss/policydb.c index 4390558464c5..7131251be628 100644 --- a/security/selinux/ss/policydb.c +++ b/security/selinux/ss/policydb.c @@ -1929,7 +1929,6 @@ static int filename_trans_read(struct policydb *p, void *fp) nel = le32_to_cpu(buf[0]); for (i = 0; i < nel; i++) { - ft = NULL; otype = NULL; name = NULL; -- cgit v1.2.3 From 57152a5be08e240654993984a0f6e54254882626 Mon Sep 17 00:00:00 2001 From: Markus Elfring Date: Sat, 14 Jan 2017 19:35:59 +0100 Subject: selinux: Return directly after a failed next_entry() in range_read() Return directly after a call of the function "next_entry" failed at the beginning. Signed-off-by: Markus Elfring Signed-off-by: Paul Moore --- security/selinux/ss/policydb.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'security') diff --git a/security/selinux/ss/policydb.c b/security/selinux/ss/policydb.c index 7131251be628..6c093018ae8d 100644 --- a/security/selinux/ss/policydb.c +++ b/security/selinux/ss/policydb.c @@ -1852,7 +1852,7 @@ static int range_read(struct policydb *p, void *fp) rc = next_entry(buf, fp, sizeof(u32)); if (rc) - goto out; + return rc; nel = le32_to_cpu(buf[0]); for (i = 0; i < nel; i++) { -- cgit v1.2.3 From 9c312e79d6afb673114ebe7aabd5594e79827eea Mon Sep 17 00:00:00 2001 From: Markus Elfring Date: Sat, 14 Jan 2017 20:40:12 +0100 Subject: selinux: Delete an unnecessary variable initialisation in range_read() The local variable "rt" will be set to an appropriate pointer a bit later. Thus omit the explicit initialisation at the beginning which became unnecessary with a previous update step. Signed-off-by: Markus Elfring Signed-off-by: Paul Moore --- security/selinux/ss/policydb.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'security') diff --git a/security/selinux/ss/policydb.c b/security/selinux/ss/policydb.c index 6c093018ae8d..a8389396e9a9 100644 --- a/security/selinux/ss/policydb.c +++ b/security/selinux/ss/policydb.c @@ -1841,7 +1841,7 @@ u32 string_to_av_perm(struct policydb *p, u16 tclass, const char *name) static int range_read(struct policydb *p, void *fp) { - struct range_trans *rt = NULL; + struct range_trans *rt; struct mls_range *r = NULL; int i, rc; __le32 buf[2]; -- cgit v1.2.3 From 3dfc9b02864b19f4dab376f14479ee4ad1de6c9e Mon Sep 17 00:00:00 2001 From: Tetsuo Handa Date: Wed, 22 Mar 2017 19:46:19 +0900 Subject: LSM: Initialize security_hook_heads upon registration. "struct security_hook_heads" is an array of "struct list_head" where elements can be initialized just before registration. There is no need to waste 350+ lines for initialization. Let's initialize "struct security_hook_heads" just before registration. Signed-off-by: Tetsuo Handa Acked-by: Kees Cook Cc: John Johansen Cc: Kees Cook Cc: Paul Moore Cc: Stephen Smalley Cc: Casey Schaufler Cc: James Morris Signed-off-by: James Morris --- security/security.c | 361 +--------------------------------------------------- 1 file changed, 7 insertions(+), 354 deletions(-) (limited to 'security') diff --git a/security/security.c b/security/security.c index d6d18a3721aa..2f15488dc6bc 100644 --- a/security/security.c +++ b/security/security.c @@ -32,6 +32,7 @@ /* Maximum number of letters for an LSM name string */ #define SECURITY_NAME_MAX 10 +struct security_hook_heads security_hook_heads __lsm_ro_after_init; char *lsm_names; /* Boot-time LSM user choice */ static __initdata char chosen_lsm[SECURITY_NAME_MAX + 1] = @@ -54,6 +55,12 @@ static void __init do_security_initcalls(void) */ int __init security_init(void) { + int i; + struct list_head *list = (struct list_head *) &security_hook_heads; + + for (i = 0; i < sizeof(security_hook_heads) / sizeof(struct list_head); + i++) + INIT_LIST_HEAD(&list[i]); pr_info("Security Framework initialized\n"); /* @@ -1627,357 +1634,3 @@ int security_audit_rule_match(u32 secid, u32 field, u32 op, void *lsmrule, actx); } #endif /* CONFIG_AUDIT */ - -struct security_hook_heads security_hook_heads __lsm_ro_after_init = { - .binder_set_context_mgr = - LIST_HEAD_INIT(security_hook_heads.binder_set_context_mgr), - .binder_transaction = - LIST_HEAD_INIT(security_hook_heads.binder_transaction), - .binder_transfer_binder = - LIST_HEAD_INIT(security_hook_heads.binder_transfer_binder), - .binder_transfer_file = - LIST_HEAD_INIT(security_hook_heads.binder_transfer_file), - - .ptrace_access_check = - LIST_HEAD_INIT(security_hook_heads.ptrace_access_check), - .ptrace_traceme = - LIST_HEAD_INIT(security_hook_heads.ptrace_traceme), - .capget = LIST_HEAD_INIT(security_hook_heads.capget), - .capset = LIST_HEAD_INIT(security_hook_heads.capset), - .capable = LIST_HEAD_INIT(security_hook_heads.capable), - .quotactl = LIST_HEAD_INIT(security_hook_heads.quotactl), - .quota_on = LIST_HEAD_INIT(security_hook_heads.quota_on), - .syslog = LIST_HEAD_INIT(security_hook_heads.syslog), - .settime = LIST_HEAD_INIT(security_hook_heads.settime), - .vm_enough_memory = - LIST_HEAD_INIT(security_hook_heads.vm_enough_memory), - .bprm_set_creds = - LIST_HEAD_INIT(security_hook_heads.bprm_set_creds), - .bprm_check_security = - LIST_HEAD_INIT(security_hook_heads.bprm_check_security), - .bprm_secureexec = - LIST_HEAD_INIT(security_hook_heads.bprm_secureexec), - .bprm_committing_creds = - LIST_HEAD_INIT(security_hook_heads.bprm_committing_creds), - .bprm_committed_creds = - LIST_HEAD_INIT(security_hook_heads.bprm_committed_creds), - .sb_alloc_security = - LIST_HEAD_INIT(security_hook_heads.sb_alloc_security), - .sb_free_security = - LIST_HEAD_INIT(security_hook_heads.sb_free_security), - .sb_copy_data = LIST_HEAD_INIT(security_hook_heads.sb_copy_data), - .sb_remount = LIST_HEAD_INIT(security_hook_heads.sb_remount), - .sb_kern_mount = - LIST_HEAD_INIT(security_hook_heads.sb_kern_mount), - .sb_show_options = - LIST_HEAD_INIT(security_hook_heads.sb_show_options), - .sb_statfs = LIST_HEAD_INIT(security_hook_heads.sb_statfs), - .sb_mount = LIST_HEAD_INIT(security_hook_heads.sb_mount), - .sb_umount = LIST_HEAD_INIT(security_hook_heads.sb_umount), - .sb_pivotroot = LIST_HEAD_INIT(security_hook_heads.sb_pivotroot), - .sb_set_mnt_opts = - LIST_HEAD_INIT(security_hook_heads.sb_set_mnt_opts), - .sb_clone_mnt_opts = - LIST_HEAD_INIT(security_hook_heads.sb_clone_mnt_opts), - .sb_parse_opts_str = - LIST_HEAD_INIT(security_hook_heads.sb_parse_opts_str), - .dentry_init_security = - LIST_HEAD_INIT(security_hook_heads.dentry_init_security), - .dentry_create_files_as = - LIST_HEAD_INIT(security_hook_heads.dentry_create_files_as), -#ifdef CONFIG_SECURITY_PATH - .path_unlink = LIST_HEAD_INIT(security_hook_heads.path_unlink), - .path_mkdir = LIST_HEAD_INIT(security_hook_heads.path_mkdir), - .path_rmdir = LIST_HEAD_INIT(security_hook_heads.path_rmdir), - .path_mknod = LIST_HEAD_INIT(security_hook_heads.path_mknod), - .path_truncate = - LIST_HEAD_INIT(security_hook_heads.path_truncate), - .path_symlink = LIST_HEAD_INIT(security_hook_heads.path_symlink), - .path_link = LIST_HEAD_INIT(security_hook_heads.path_link), - .path_rename = LIST_HEAD_INIT(security_hook_heads.path_rename), - .path_chmod = LIST_HEAD_INIT(security_hook_heads.path_chmod), - .path_chown = LIST_HEAD_INIT(security_hook_heads.path_chown), - .path_chroot = LIST_HEAD_INIT(security_hook_heads.path_chroot), -#endif - .inode_alloc_security = - LIST_HEAD_INIT(security_hook_heads.inode_alloc_security), - .inode_free_security = - LIST_HEAD_INIT(security_hook_heads.inode_free_security), - .inode_init_security = - LIST_HEAD_INIT(security_hook_heads.inode_init_security), - .inode_create = LIST_HEAD_INIT(security_hook_heads.inode_create), - .inode_link = LIST_HEAD_INIT(security_hook_heads.inode_link), - .inode_unlink = LIST_HEAD_INIT(security_hook_heads.inode_unlink), - .inode_symlink = - LIST_HEAD_INIT(security_hook_heads.inode_symlink), - .inode_mkdir = LIST_HEAD_INIT(security_hook_heads.inode_mkdir), - .inode_rmdir = LIST_HEAD_INIT(security_hook_heads.inode_rmdir), - .inode_mknod = LIST_HEAD_INIT(security_hook_heads.inode_mknod), - .inode_rename = LIST_HEAD_INIT(security_hook_heads.inode_rename), - .inode_readlink = - LIST_HEAD_INIT(security_hook_heads.inode_readlink), - .inode_follow_link = - LIST_HEAD_INIT(security_hook_heads.inode_follow_link), - .inode_permission = - LIST_HEAD_INIT(security_hook_heads.inode_permission), - .inode_setattr = - LIST_HEAD_INIT(security_hook_heads.inode_setattr), - .inode_getattr = - LIST_HEAD_INIT(security_hook_heads.inode_getattr), - .inode_setxattr = - LIST_HEAD_INIT(security_hook_heads.inode_setxattr), - .inode_post_setxattr = - LIST_HEAD_INIT(security_hook_heads.inode_post_setxattr), - .inode_getxattr = - LIST_HEAD_INIT(security_hook_heads.inode_getxattr), - .inode_listxattr = - LIST_HEAD_INIT(security_hook_heads.inode_listxattr), - .inode_removexattr = - LIST_HEAD_INIT(security_hook_heads.inode_removexattr), - .inode_need_killpriv = - LIST_HEAD_INIT(security_hook_heads.inode_need_killpriv), - .inode_killpriv = - LIST_HEAD_INIT(security_hook_heads.inode_killpriv), - .inode_getsecurity = - LIST_HEAD_INIT(security_hook_heads.inode_getsecurity), - .inode_setsecurity = - LIST_HEAD_INIT(security_hook_heads.inode_setsecurity), - .inode_listsecurity = - LIST_HEAD_INIT(security_hook_heads.inode_listsecurity), - .inode_getsecid = - LIST_HEAD_INIT(security_hook_heads.inode_getsecid), - .inode_copy_up = - LIST_HEAD_INIT(security_hook_heads.inode_copy_up), - .inode_copy_up_xattr = - LIST_HEAD_INIT(security_hook_heads.inode_copy_up_xattr), - .file_permission = - LIST_HEAD_INIT(security_hook_heads.file_permission), - .file_alloc_security = - LIST_HEAD_INIT(security_hook_heads.file_alloc_security), - .file_free_security = - LIST_HEAD_INIT(security_hook_heads.file_free_security), - .file_ioctl = LIST_HEAD_INIT(security_hook_heads.file_ioctl), - .mmap_addr = LIST_HEAD_INIT(security_hook_heads.mmap_addr), - .mmap_file = LIST_HEAD_INIT(security_hook_heads.mmap_file), - .file_mprotect = - LIST_HEAD_INIT(security_hook_heads.file_mprotect), - .file_lock = LIST_HEAD_INIT(security_hook_heads.file_lock), - .file_fcntl = LIST_HEAD_INIT(security_hook_heads.file_fcntl), - .file_set_fowner = - LIST_HEAD_INIT(security_hook_heads.file_set_fowner), - .file_send_sigiotask = - LIST_HEAD_INIT(security_hook_heads.file_send_sigiotask), - .file_receive = LIST_HEAD_INIT(security_hook_heads.file_receive), - .file_open = LIST_HEAD_INIT(security_hook_heads.file_open), - .task_create = LIST_HEAD_INIT(security_hook_heads.task_create), - .task_free = LIST_HEAD_INIT(security_hook_heads.task_free), - .cred_alloc_blank = - LIST_HEAD_INIT(security_hook_heads.cred_alloc_blank), - .cred_free = LIST_HEAD_INIT(security_hook_heads.cred_free), - .cred_prepare = LIST_HEAD_INIT(security_hook_heads.cred_prepare), - .cred_transfer = - LIST_HEAD_INIT(security_hook_heads.cred_transfer), - .kernel_act_as = - LIST_HEAD_INIT(security_hook_heads.kernel_act_as), - .kernel_create_files_as = - LIST_HEAD_INIT(security_hook_heads.kernel_create_files_as), - .kernel_module_request = - LIST_HEAD_INIT(security_hook_heads.kernel_module_request), - .kernel_read_file = - LIST_HEAD_INIT(security_hook_heads.kernel_read_file), - .kernel_post_read_file = - LIST_HEAD_INIT(security_hook_heads.kernel_post_read_file), - .task_fix_setuid = - LIST_HEAD_INIT(security_hook_heads.task_fix_setuid), - .task_setpgid = LIST_HEAD_INIT(security_hook_heads.task_setpgid), - .task_getpgid = LIST_HEAD_INIT(security_hook_heads.task_getpgid), - .task_getsid = LIST_HEAD_INIT(security_hook_heads.task_getsid), - .task_getsecid = - LIST_HEAD_INIT(security_hook_heads.task_getsecid), - .task_setnice = LIST_HEAD_INIT(security_hook_heads.task_setnice), - .task_setioprio = - LIST_HEAD_INIT(security_hook_heads.task_setioprio), - .task_getioprio = - LIST_HEAD_INIT(security_hook_heads.task_getioprio), - .task_prlimit = - LIST_HEAD_INIT(security_hook_heads.task_prlimit), - .task_setrlimit = - LIST_HEAD_INIT(security_hook_heads.task_setrlimit), - .task_setscheduler = - LIST_HEAD_INIT(security_hook_heads.task_setscheduler), - .task_getscheduler = - LIST_HEAD_INIT(security_hook_heads.task_getscheduler), - .task_movememory = - LIST_HEAD_INIT(security_hook_heads.task_movememory), - .task_kill = LIST_HEAD_INIT(security_hook_heads.task_kill), - .task_prctl = LIST_HEAD_INIT(security_hook_heads.task_prctl), - .task_to_inode = - LIST_HEAD_INIT(security_hook_heads.task_to_inode), - .ipc_permission = - LIST_HEAD_INIT(security_hook_heads.ipc_permission), - .ipc_getsecid = LIST_HEAD_INIT(security_hook_heads.ipc_getsecid), - .msg_msg_alloc_security = - LIST_HEAD_INIT(security_hook_heads.msg_msg_alloc_security), - .msg_msg_free_security = - LIST_HEAD_INIT(security_hook_heads.msg_msg_free_security), - .msg_queue_alloc_security = - LIST_HEAD_INIT(security_hook_heads.msg_queue_alloc_security), - .msg_queue_free_security = - LIST_HEAD_INIT(security_hook_heads.msg_queue_free_security), - .msg_queue_associate = - LIST_HEAD_INIT(security_hook_heads.msg_queue_associate), - .msg_queue_msgctl = - LIST_HEAD_INIT(security_hook_heads.msg_queue_msgctl), - .msg_queue_msgsnd = - LIST_HEAD_INIT(security_hook_heads.msg_queue_msgsnd), - .msg_queue_msgrcv = - LIST_HEAD_INIT(security_hook_heads.msg_queue_msgrcv), - .shm_alloc_security = - LIST_HEAD_INIT(security_hook_heads.shm_alloc_security), - .shm_free_security = - LIST_HEAD_INIT(security_hook_heads.shm_free_security), - .shm_associate = - LIST_HEAD_INIT(security_hook_heads.shm_associate), - .shm_shmctl = LIST_HEAD_INIT(security_hook_heads.shm_shmctl), - .shm_shmat = LIST_HEAD_INIT(security_hook_heads.shm_shmat), - .sem_alloc_security = - LIST_HEAD_INIT(security_hook_heads.sem_alloc_security), - .sem_free_security = - LIST_HEAD_INIT(security_hook_heads.sem_free_security), - .sem_associate = - LIST_HEAD_INIT(security_hook_heads.sem_associate), - .sem_semctl = LIST_HEAD_INIT(security_hook_heads.sem_semctl), - .sem_semop = LIST_HEAD_INIT(security_hook_heads.sem_semop), - .netlink_send = LIST_HEAD_INIT(security_hook_heads.netlink_send), - .d_instantiate = - LIST_HEAD_INIT(security_hook_heads.d_instantiate), - .getprocattr = LIST_HEAD_INIT(security_hook_heads.getprocattr), - .setprocattr = LIST_HEAD_INIT(security_hook_heads.setprocattr), - .ismaclabel = LIST_HEAD_INIT(security_hook_heads.ismaclabel), - .secid_to_secctx = - LIST_HEAD_INIT(security_hook_heads.secid_to_secctx), - .secctx_to_secid = - LIST_HEAD_INIT(security_hook_heads.secctx_to_secid), - .release_secctx = - LIST_HEAD_INIT(security_hook_heads.release_secctx), - .inode_invalidate_secctx = - LIST_HEAD_INIT(security_hook_heads.inode_invalidate_secctx), - .inode_notifysecctx = - LIST_HEAD_INIT(security_hook_heads.inode_notifysecctx), - .inode_setsecctx = - LIST_HEAD_INIT(security_hook_heads.inode_setsecctx), - .inode_getsecctx = - LIST_HEAD_INIT(security_hook_heads.inode_getsecctx), -#ifdef CONFIG_SECURITY_NETWORK - .unix_stream_connect = - LIST_HEAD_INIT(security_hook_heads.unix_stream_connect), - .unix_may_send = - LIST_HEAD_INIT(security_hook_heads.unix_may_send), - .socket_create = - LIST_HEAD_INIT(security_hook_heads.socket_create), - .socket_post_create = - LIST_HEAD_INIT(security_hook_heads.socket_post_create), - .socket_bind = LIST_HEAD_INIT(security_hook_heads.socket_bind), - .socket_connect = - LIST_HEAD_INIT(security_hook_heads.socket_connect), - .socket_listen = - LIST_HEAD_INIT(security_hook_heads.socket_listen), - .socket_accept = - LIST_HEAD_INIT(security_hook_heads.socket_accept), - .socket_sendmsg = - LIST_HEAD_INIT(security_hook_heads.socket_sendmsg), - .socket_recvmsg = - LIST_HEAD_INIT(security_hook_heads.socket_recvmsg), - .socket_getsockname = - LIST_HEAD_INIT(security_hook_heads.socket_getsockname), - .socket_getpeername = - LIST_HEAD_INIT(security_hook_heads.socket_getpeername), - .socket_getsockopt = - LIST_HEAD_INIT(security_hook_heads.socket_getsockopt), - .socket_setsockopt = - LIST_HEAD_INIT(security_hook_heads.socket_setsockopt), - .socket_shutdown = - LIST_HEAD_INIT(security_hook_heads.socket_shutdown), - .socket_sock_rcv_skb = - LIST_HEAD_INIT(security_hook_heads.socket_sock_rcv_skb), - .socket_getpeersec_stream = - LIST_HEAD_INIT(security_hook_heads.socket_getpeersec_stream), - .socket_getpeersec_dgram = - LIST_HEAD_INIT(security_hook_heads.socket_getpeersec_dgram), - .sk_alloc_security = - LIST_HEAD_INIT(security_hook_heads.sk_alloc_security), - .sk_free_security = - LIST_HEAD_INIT(security_hook_heads.sk_free_security), - .sk_clone_security = - LIST_HEAD_INIT(security_hook_heads.sk_clone_security), - .sk_getsecid = LIST_HEAD_INIT(security_hook_heads.sk_getsecid), - .sock_graft = LIST_HEAD_INIT(security_hook_heads.sock_graft), - .inet_conn_request = - LIST_HEAD_INIT(security_hook_heads.inet_conn_request), - .inet_csk_clone = - LIST_HEAD_INIT(security_hook_heads.inet_csk_clone), - .inet_conn_established = - LIST_HEAD_INIT(security_hook_heads.inet_conn_established), - .secmark_relabel_packet = - LIST_HEAD_INIT(security_hook_heads.secmark_relabel_packet), - .secmark_refcount_inc = - LIST_HEAD_INIT(security_hook_heads.secmark_refcount_inc), - .secmark_refcount_dec = - LIST_HEAD_INIT(security_hook_heads.secmark_refcount_dec), - .req_classify_flow = - LIST_HEAD_INIT(security_hook_heads.req_classify_flow), - .tun_dev_alloc_security = - LIST_HEAD_INIT(security_hook_heads.tun_dev_alloc_security), - .tun_dev_free_security = - LIST_HEAD_INIT(security_hook_heads.tun_dev_free_security), - .tun_dev_create = - LIST_HEAD_INIT(security_hook_heads.tun_dev_create), - .tun_dev_attach_queue = - LIST_HEAD_INIT(security_hook_heads.tun_dev_attach_queue), - .tun_dev_attach = - LIST_HEAD_INIT(security_hook_heads.tun_dev_attach), - .tun_dev_open = LIST_HEAD_INIT(security_hook_heads.tun_dev_open), -#endif /* CONFIG_SECURITY_NETWORK */ -#ifdef CONFIG_SECURITY_NETWORK_XFRM - .xfrm_policy_alloc_security = - LIST_HEAD_INIT(security_hook_heads.xfrm_policy_alloc_security), - .xfrm_policy_clone_security = - LIST_HEAD_INIT(security_hook_heads.xfrm_policy_clone_security), - .xfrm_policy_free_security = - LIST_HEAD_INIT(security_hook_heads.xfrm_policy_free_security), - .xfrm_policy_delete_security = - LIST_HEAD_INIT(security_hook_heads.xfrm_policy_delete_security), - .xfrm_state_alloc = - LIST_HEAD_INIT(security_hook_heads.xfrm_state_alloc), - .xfrm_state_alloc_acquire = - LIST_HEAD_INIT(security_hook_heads.xfrm_state_alloc_acquire), - .xfrm_state_free_security = - LIST_HEAD_INIT(security_hook_heads.xfrm_state_free_security), - .xfrm_state_delete_security = - LIST_HEAD_INIT(security_hook_heads.xfrm_state_delete_security), - .xfrm_policy_lookup = - LIST_HEAD_INIT(security_hook_heads.xfrm_policy_lookup), - .xfrm_state_pol_flow_match = - LIST_HEAD_INIT(security_hook_heads.xfrm_state_pol_flow_match), - .xfrm_decode_session = - LIST_HEAD_INIT(security_hook_heads.xfrm_decode_session), -#endif /* CONFIG_SECURITY_NETWORK_XFRM */ -#ifdef CONFIG_KEYS - .key_alloc = LIST_HEAD_INIT(security_hook_heads.key_alloc), - .key_free = LIST_HEAD_INIT(security_hook_heads.key_free), - .key_permission = - LIST_HEAD_INIT(security_hook_heads.key_permission), - .key_getsecurity = - LIST_HEAD_INIT(security_hook_heads.key_getsecurity), -#endif /* CONFIG_KEYS */ -#ifdef CONFIG_AUDIT - .audit_rule_init = - LIST_HEAD_INIT(security_hook_heads.audit_rule_init), - .audit_rule_known = - LIST_HEAD_INIT(security_hook_heads.audit_rule_known), - .audit_rule_match = - LIST_HEAD_INIT(security_hook_heads.audit_rule_match), - .audit_rule_free = - LIST_HEAD_INIT(security_hook_heads.audit_rule_free), -#endif /* CONFIG_AUDIT */ -}; -- cgit v1.2.3 From e4e55b47ed9ae2c05ff062601ff6dacbe9dc4775 Mon Sep 17 00:00:00 2001 From: Tetsuo Handa Date: Fri, 24 Mar 2017 20:46:33 +0900 Subject: LSM: Revive security_task_alloc() hook and per "struct task_struct" security blob. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We switched from "struct task_struct"->security to "struct cred"->security in Linux 2.6.29. But not all LSM modules were happy with that change. TOMOYO LSM module is an example which want to use per "struct task_struct" security blob, for TOMOYO's security context is defined based on "struct task_struct" rather than "struct cred". AppArmor LSM module is another example which want to use it, for AppArmor is currently abusing the cred a little bit to store the change_hat and setexeccon info. Although security_task_free() hook was revived in Linux 3.4 because Yama LSM module wanted to release per "struct task_struct" security blob, security_task_alloc() hook and "struct task_struct"->security field were not revived. Nowadays, we are getting proposals of lightweight LSM modules which want to use per "struct task_struct" security blob. We are already allowing multiple concurrent LSM modules (up to one fully armored module which uses "struct cred"->security field or exclusive hooks like security_xfrm_state_pol_flow_match(), plus unlimited number of lightweight modules which do not use "struct cred"->security nor exclusive hooks) as long as they are built into the kernel. But this patch does not implement variable length "struct task_struct"->security field which will become needed when multiple LSM modules want to use "struct task_struct"-> security field. Although it won't be difficult to implement variable length "struct task_struct"->security field, let's think about it after we merged this patch. Signed-off-by: Tetsuo Handa Acked-by: John Johansen Acked-by: Serge Hallyn Acked-by: Casey Schaufler Tested-by: Djalal Harouni Acked-by: José Bollo Cc: Paul Moore Cc: Stephen Smalley Cc: Eric Paris Cc: Kees Cook Cc: James Morris Cc: José Bollo Signed-off-by: James Morris --- include/linux/init_task.h | 7 +++++++ include/linux/lsm_hooks.h | 9 ++++++++- include/linux/sched.h | 4 ++++ include/linux/security.h | 7 +++++++ kernel/fork.c | 7 ++++++- security/security.c | 5 +++++ 6 files changed, 37 insertions(+), 2 deletions(-) (limited to 'security') diff --git a/include/linux/init_task.h b/include/linux/init_task.h index 91d9049f0039..926f2f553cc5 100644 --- a/include/linux/init_task.h +++ b/include/linux/init_task.h @@ -210,6 +210,12 @@ extern struct cred init_cred; # define INIT_TASK_TI(tsk) #endif +#ifdef CONFIG_SECURITY +#define INIT_TASK_SECURITY .security = NULL, +#else +#define INIT_TASK_SECURITY +#endif + /* * INIT_TASK is used to set up the first task table, touch at * your own risk!. Base=0, limit=0x1fffff (=2MB) @@ -288,6 +294,7 @@ extern struct cred init_cred; INIT_VTIME(tsk) \ INIT_NUMA_BALANCING(tsk) \ INIT_KASAN(tsk) \ + INIT_TASK_SECURITY \ } diff --git a/include/linux/lsm_hooks.h b/include/linux/lsm_hooks.h index 1aa63335de9e..080f34e66017 100644 --- a/include/linux/lsm_hooks.h +++ b/include/linux/lsm_hooks.h @@ -533,8 +533,13 @@ * manual page for definitions of the @clone_flags. * @clone_flags contains the flags indicating what should be shared. * Return 0 if permission is granted. + * @task_alloc: + * @task task being allocated. + * @clone_flags contains the flags indicating what should be shared. + * Handle allocation of task-related resources. + * Returns a zero on success, negative values on failure. * @task_free: - * @task task being freed + * @task task about to be freed. * Handle release of task-related resources. (Note that this can be called * from interrupt context.) * @cred_alloc_blank: @@ -1482,6 +1487,7 @@ union security_list_options { int (*file_open)(struct file *file, const struct cred *cred); int (*task_create)(unsigned long clone_flags); + int (*task_alloc)(struct task_struct *task, unsigned long clone_flags); void (*task_free)(struct task_struct *task); int (*cred_alloc_blank)(struct cred *cred, gfp_t gfp); void (*cred_free)(struct cred *cred); @@ -1748,6 +1754,7 @@ struct security_hook_heads { struct list_head file_receive; struct list_head file_open; struct list_head task_create; + struct list_head task_alloc; struct list_head task_free; struct list_head cred_alloc_blank; struct list_head cred_free; diff --git a/include/linux/sched.h b/include/linux/sched.h index d67eee84fd43..71b8df306bb0 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -1037,6 +1037,10 @@ struct task_struct { #ifdef CONFIG_THREAD_INFO_IN_TASK /* A live task holds one reference: */ atomic_t stack_refcount; +#endif +#ifdef CONFIG_SECURITY + /* Used by LSM modules for access restriction: */ + void *security; #endif /* CPU-specific state of this task: */ struct thread_struct thread; diff --git a/include/linux/security.h b/include/linux/security.h index 97df7bac5b48..af675b576645 100644 --- a/include/linux/security.h +++ b/include/linux/security.h @@ -308,6 +308,7 @@ int security_file_send_sigiotask(struct task_struct *tsk, int security_file_receive(struct file *file); int security_file_open(struct file *file, const struct cred *cred); int security_task_create(unsigned long clone_flags); +int security_task_alloc(struct task_struct *task, unsigned long clone_flags); void security_task_free(struct task_struct *task); int security_cred_alloc_blank(struct cred *cred, gfp_t gfp); void security_cred_free(struct cred *cred); @@ -861,6 +862,12 @@ static inline int security_task_create(unsigned long clone_flags) return 0; } +static inline int security_task_alloc(struct task_struct *task, + unsigned long clone_flags) +{ + return 0; +} + static inline void security_task_free(struct task_struct *task) { } diff --git a/kernel/fork.c b/kernel/fork.c index 6c463c80e93d..3d32513d6c73 100644 --- a/kernel/fork.c +++ b/kernel/fork.c @@ -1679,9 +1679,12 @@ static __latent_entropy struct task_struct *copy_process( goto bad_fork_cleanup_perf; /* copy all the process information */ shm_init_task(p); - retval = copy_semundo(clone_flags, p); + retval = security_task_alloc(p, clone_flags); if (retval) goto bad_fork_cleanup_audit; + retval = copy_semundo(clone_flags, p); + if (retval) + goto bad_fork_cleanup_security; retval = copy_files(clone_flags, p); if (retval) goto bad_fork_cleanup_semundo; @@ -1903,6 +1906,8 @@ bad_fork_cleanup_files: exit_files(p); /* blocking */ bad_fork_cleanup_semundo: exit_sem(p); +bad_fork_cleanup_security: + security_task_free(p); bad_fork_cleanup_audit: audit_free(p); bad_fork_cleanup_perf: diff --git a/security/security.c b/security/security.c index 2f15488dc6bc..549bddcc2116 100644 --- a/security/security.c +++ b/security/security.c @@ -937,6 +937,11 @@ int security_task_create(unsigned long clone_flags) return call_int_hook(task_create, 0, clone_flags); } +int security_task_alloc(struct task_struct *task, unsigned long clone_flags) +{ + return call_int_hook(task_alloc, 0, task, clone_flags); +} + void security_task_free(struct task_struct *task) { call_void_hook(task_free, task); -- cgit v1.2.3 From 7f6d0ad8b771a4936f448180de3bbfad92be34dc Mon Sep 17 00:00:00 2001 From: Markus Elfring Date: Sat, 14 Jan 2017 21:20:43 +0100 Subject: selinux: Return directly after a failed kzalloc() in cat_read() Return directly after a call of the function "kzalloc" failed at the beginning. Signed-off-by: Markus Elfring Signed-off-by: Paul Moore --- security/selinux/ss/policydb.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'security') diff --git a/security/selinux/ss/policydb.c b/security/selinux/ss/policydb.c index a8389396e9a9..36285d12c2e9 100644 --- a/security/selinux/ss/policydb.c +++ b/security/selinux/ss/policydb.c @@ -1637,10 +1637,9 @@ static int cat_read(struct policydb *p, struct hashtab *h, void *fp) __le32 buf[3]; u32 len; - rc = -ENOMEM; catdatum = kzalloc(sizeof(*catdatum), GFP_ATOMIC); if (!catdatum) - goto bad; + return -ENOMEM; rc = next_entry(buf, fp, sizeof buf); if (rc) -- cgit v1.2.3 From 3c354d7d7bd5e788c3d94969e4a85648d29d9d12 Mon Sep 17 00:00:00 2001 From: Markus Elfring Date: Sat, 14 Jan 2017 21:42:02 +0100 Subject: selinux: Return directly after a failed kzalloc() in sens_read() Return directly after a call of the function "kzalloc" failed at the beginning. Signed-off-by: Markus Elfring Signed-off-by: Paul Moore --- security/selinux/ss/policydb.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'security') diff --git a/security/selinux/ss/policydb.c b/security/selinux/ss/policydb.c index 36285d12c2e9..aa1ce7ce3524 100644 --- a/security/selinux/ss/policydb.c +++ b/security/selinux/ss/policydb.c @@ -1595,10 +1595,9 @@ static int sens_read(struct policydb *p, struct hashtab *h, void *fp) __le32 buf[2]; u32 len; - rc = -ENOMEM; levdatum = kzalloc(sizeof(*levdatum), GFP_ATOMIC); if (!levdatum) - goto bad; + return -ENOMEM; rc = next_entry(buf, fp, sizeof buf); if (rc) -- cgit v1.2.3 From b5921191005d050d55cf4f3b10f60110f7ed2c24 Mon Sep 17 00:00:00 2001 From: Markus Elfring Date: Sat, 14 Jan 2017 21:52:55 +0100 Subject: selinux: Improve another size determination in sens_read() Replace the specification of a data type by a pointer dereference as the parameter for the operator "sizeof" to make the corresponding size determination a bit safer according to the Linux coding style convention. Signed-off-by: Markus Elfring Signed-off-by: Paul Moore --- security/selinux/ss/policydb.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'security') diff --git a/security/selinux/ss/policydb.c b/security/selinux/ss/policydb.c index aa1ce7ce3524..4759c22d1ae6 100644 --- a/security/selinux/ss/policydb.c +++ b/security/selinux/ss/policydb.c @@ -1611,7 +1611,7 @@ static int sens_read(struct policydb *p, struct hashtab *h, void *fp) goto bad; rc = -ENOMEM; - levdatum->level = kmalloc(sizeof(struct mls_level), GFP_ATOMIC); + levdatum->level = kmalloc(sizeof(*levdatum->level), GFP_ATOMIC); if (!levdatum->level) goto bad; -- cgit v1.2.3 From 4bd9f07b89f1a300f96b4863c3a8a4ec36991930 Mon Sep 17 00:00:00 2001 From: Markus Elfring Date: Sat, 14 Jan 2017 22:08:22 +0100 Subject: selinux: Return directly after a failed kzalloc() in user_read() Return directly after a call of the function "kzalloc" failed at the beginning. Signed-off-by: Markus Elfring Signed-off-by: Paul Moore --- security/selinux/ss/policydb.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'security') diff --git a/security/selinux/ss/policydb.c b/security/selinux/ss/policydb.c index 4759c22d1ae6..9a90953974b8 100644 --- a/security/selinux/ss/policydb.c +++ b/security/selinux/ss/policydb.c @@ -1544,10 +1544,9 @@ static int user_read(struct policydb *p, struct hashtab *h, void *fp) __le32 buf[3]; u32 len; - rc = -ENOMEM; usrdatum = kzalloc(sizeof(*usrdatum), GFP_KERNEL); if (!usrdatum) - goto bad; + return -ENOMEM; if (p->policyvers >= POLICYDB_VERSION_BOUNDARY) to_read = 3; -- cgit v1.2.3 From 549fe69ee5c7a7f55c34555032961a2265e6e713 Mon Sep 17 00:00:00 2001 From: Markus Elfring Date: Sat, 14 Jan 2017 22:15:54 +0100 Subject: selinux: Return directly after a failed kzalloc() in type_read() Return directly after a call of the function "kzalloc" failed at the beginning. Signed-off-by: Markus Elfring Signed-off-by: Paul Moore --- security/selinux/ss/policydb.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'security') diff --git a/security/selinux/ss/policydb.c b/security/selinux/ss/policydb.c index 9a90953974b8..fd58de5a83ad 100644 --- a/security/selinux/ss/policydb.c +++ b/security/selinux/ss/policydb.c @@ -1469,10 +1469,9 @@ static int type_read(struct policydb *p, struct hashtab *h, void *fp) __le32 buf[4]; u32 len; - rc = -ENOMEM; typdatum = kzalloc(sizeof(*typdatum), GFP_KERNEL); if (!typdatum) - goto bad; + return -ENOMEM; if (p->policyvers >= POLICYDB_VERSION_BOUNDARY) to_read = 4; -- cgit v1.2.3 From ea6e2f7d12921f336def7398805ee3b1619e2f4b Mon Sep 17 00:00:00 2001 From: Markus Elfring Date: Sat, 14 Jan 2017 22:20:25 +0100 Subject: selinux: Return directly after a failed kzalloc() in role_read() Return directly after a call of the function "kzalloc" failed at the beginning. Signed-off-by: Markus Elfring Signed-off-by: Paul Moore --- security/selinux/ss/policydb.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'security') diff --git a/security/selinux/ss/policydb.c b/security/selinux/ss/policydb.c index fd58de5a83ad..30f29c669e32 100644 --- a/security/selinux/ss/policydb.c +++ b/security/selinux/ss/policydb.c @@ -1412,10 +1412,9 @@ static int role_read(struct policydb *p, struct hashtab *h, void *fp) __le32 buf[3]; u32 len; - rc = -ENOMEM; role = kzalloc(sizeof(*role), GFP_KERNEL); if (!role) - goto bad; + return -ENOMEM; if (p->policyvers >= POLICYDB_VERSION_BOUNDARY) to_read = 3; -- cgit v1.2.3 From df4a14dfb484f95d81126e481e66b6e22eec49e8 Mon Sep 17 00:00:00 2001 From: Markus Elfring Date: Sat, 14 Jan 2017 22:30:51 +0100 Subject: selinux: Return directly after a failed kzalloc() in class_read() Return directly after a call of the function "kzalloc" failed at the beginning. Signed-off-by: Markus Elfring Signed-off-by: Paul Moore --- security/selinux/ss/policydb.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'security') diff --git a/security/selinux/ss/policydb.c b/security/selinux/ss/policydb.c index 30f29c669e32..edf173ed05f8 100644 --- a/security/selinux/ss/policydb.c +++ b/security/selinux/ss/policydb.c @@ -1318,10 +1318,9 @@ static int class_read(struct policydb *p, struct hashtab *h, void *fp) u32 len, len2, ncons, nel; int i, rc; - rc = -ENOMEM; cladatum = kzalloc(sizeof(*cladatum), GFP_KERNEL); if (!cladatum) - goto bad; + return -ENOMEM; rc = next_entry(buf, fp, sizeof(u32)*6); if (rc) -- cgit v1.2.3 From 442ca4d656645505346017c37ac137cde680bf38 Mon Sep 17 00:00:00 2001 From: Markus Elfring Date: Sun, 15 Jan 2017 11:15:19 +0100 Subject: selinux: Return directly after a failed kzalloc() in common_read() Return directly after a call of the function "kzalloc" failed at the beginning. Signed-off-by: Markus Elfring Signed-off-by: Paul Moore --- security/selinux/ss/policydb.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'security') diff --git a/security/selinux/ss/policydb.c b/security/selinux/ss/policydb.c index edf173ed05f8..99ee0ee2d92a 100644 --- a/security/selinux/ss/policydb.c +++ b/security/selinux/ss/policydb.c @@ -1152,10 +1152,9 @@ static int common_read(struct policydb *p, struct hashtab *h, void *fp) u32 len, nel; int i, rc; - rc = -ENOMEM; comdatum = kzalloc(sizeof(*comdatum), GFP_KERNEL); if (!comdatum) - goto bad; + return -ENOMEM; rc = next_entry(buf, fp, sizeof buf); if (rc) -- cgit v1.2.3 From 7befb7514e5d53026e9fe4a6548f118a65a20a4f Mon Sep 17 00:00:00 2001 From: Markus Elfring Date: Sun, 15 Jan 2017 11:20:13 +0100 Subject: selinux: Return directly after a failed kzalloc() in perm_read() Return directly after a call of the function "kzalloc" failed at the beginning. Signed-off-by: Markus Elfring Signed-off-by: Paul Moore --- security/selinux/ss/policydb.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'security') diff --git a/security/selinux/ss/policydb.c b/security/selinux/ss/policydb.c index 99ee0ee2d92a..5ca2d26ecf7f 100644 --- a/security/selinux/ss/policydb.c +++ b/security/selinux/ss/policydb.c @@ -1118,10 +1118,9 @@ static int perm_read(struct policydb *p, struct hashtab *h, void *fp) __le32 buf[2]; u32 len; - rc = -ENOMEM; perdatum = kzalloc(sizeof(*perdatum), GFP_KERNEL); if (!perdatum) - goto bad; + return -ENOMEM; rc = next_entry(buf, fp, sizeof buf); if (rc) -- cgit v1.2.3 From ebd2b47ba52760e9653456ba19032f79d734a343 Mon Sep 17 00:00:00 2001 From: Markus Elfring Date: Sun, 15 Jan 2017 12:10:09 +0100 Subject: selinux: Return directly after a failed kzalloc() in roles_init() Return directly after a call of the function "kzalloc" failed at the beginning. Signed-off-by: Markus Elfring Signed-off-by: Paul Moore --- security/selinux/ss/policydb.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'security') diff --git a/security/selinux/ss/policydb.c b/security/selinux/ss/policydb.c index 5ca2d26ecf7f..658247f98dc1 100644 --- a/security/selinux/ss/policydb.c +++ b/security/selinux/ss/policydb.c @@ -178,10 +178,9 @@ static int roles_init(struct policydb *p) int rc; struct role_datum *role; - rc = -ENOMEM; role = kzalloc(sizeof(*role), GFP_KERNEL); if (!role) - goto out; + return -ENOMEM; rc = -EINVAL; role->value = ++p->p_roles.nprim; -- cgit v1.2.3 From b380f783772eeb66a0da85549a85aadbe5f8e76d Mon Sep 17 00:00:00 2001 From: Markus Elfring Date: Sun, 15 Jan 2017 13:13:19 +0100 Subject: selinux: Use kmalloc_array() in sidtab_init() A multiplication for the size determination of a memory allocation indicated that an array data structure should be processed. Thus use the corresponding function "kmalloc_array". This issue was detected by using the Coccinelle software. Signed-off-by: Markus Elfring Signed-off-by: Paul Moore --- security/selinux/ss/sidtab.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'security') diff --git a/security/selinux/ss/sidtab.c b/security/selinux/ss/sidtab.c index 5840a35155fc..c9533b21942b 100644 --- a/security/selinux/ss/sidtab.c +++ b/security/selinux/ss/sidtab.c @@ -18,7 +18,7 @@ int sidtab_init(struct sidtab *s) { int i; - s->htable = kmalloc(sizeof(*(s->htable)) * SIDTAB_SIZE, GFP_ATOMIC); + s->htable = kmalloc_array(SIDTAB_SIZE, sizeof(*s->htable), GFP_ATOMIC); if (!s->htable) return -ENOMEM; for (i = 0; i < SIDTAB_SIZE; i++) -- cgit v1.2.3 From 8ee4586ca5fe6c2a00f6a39f828f54c8f6cda472 Mon Sep 17 00:00:00 2001 From: Markus Elfring Date: Sun, 15 Jan 2017 13:30:20 +0100 Subject: selinux: Adjust two checks for null pointers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The script "checkpatch.pl" pointed information out like the following. Comparison to NULL could be written !… Thus fix affected source code places. Signed-off-by: Markus Elfring Signed-off-by: Paul Moore --- security/selinux/ss/sidtab.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'security') diff --git a/security/selinux/ss/sidtab.c b/security/selinux/ss/sidtab.c index c9533b21942b..f6915f257486 100644 --- a/security/selinux/ss/sidtab.c +++ b/security/selinux/ss/sidtab.c @@ -54,7 +54,7 @@ int sidtab_insert(struct sidtab *s, u32 sid, struct context *context) } newnode = kmalloc(sizeof(*newnode), GFP_ATOMIC); - if (newnode == NULL) { + if (!newnode) { rc = -ENOMEM; goto out; } @@ -98,7 +98,7 @@ static struct context *sidtab_search_core(struct sidtab *s, u32 sid, int force) if (force && cur && sid == cur->sid && cur->context.len) return &cur->context; - if (cur == NULL || sid != cur->sid || cur->context.len) { + if (!cur || sid != cur->sid || cur->context.len) { /* Remap invalid SIDs to the unlabeled SID. */ sid = SECINITSID_UNLABELED; hvalue = SIDTAB_HASH(sid); -- cgit v1.2.3 From 710a0647ba955abd25460c36a09d80fdbe878273 Mon Sep 17 00:00:00 2001 From: Markus Elfring Date: Sun, 15 Jan 2017 14:04:53 +0100 Subject: selinuxfs: Use seq_puts() in sel_avc_stats_seq_show() A string which did not contain data format specifications should be put into a sequence. Thus use the corresponding function "seq_puts". This issue was detected by using the Coccinelle software. Signed-off-by: Markus Elfring Signed-off-by: Paul Moore --- security/selinux/selinuxfs.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'security') diff --git a/security/selinux/selinuxfs.c b/security/selinux/selinuxfs.c index cb3fd98fb05a..ce7171884223 100644 --- a/security/selinux/selinuxfs.c +++ b/security/selinux/selinuxfs.c @@ -1456,10 +1456,10 @@ static int sel_avc_stats_seq_show(struct seq_file *seq, void *v) { struct avc_cache_stats *st = v; - if (v == SEQ_START_TOKEN) - seq_printf(seq, "lookups hits misses allocations reclaims " - "frees\n"); - else { + if (v == SEQ_START_TOKEN) { + seq_puts(seq, + "lookups hits misses allocations reclaims frees\n"); + } else { unsigned int lookups = st->lookups; unsigned int misses = st->misses; unsigned int hits = lookups - misses; -- cgit v1.2.3 From 342e91578eb6909529bc7095964cd44b9c057c4e Mon Sep 17 00:00:00 2001 From: Matthias Kaehlcke Date: Thu, 16 Mar 2017 15:26:52 -0700 Subject: selinux: Remove unnecessary check of array base in selinux_set_mapping() 'perms' will never be NULL since it isn't a plain pointer but an array of u32 values. This fixes the following warning when building with clang: security/selinux/ss/services.c:158:16: error: address of array 'p_in->perms' will always evaluate to 'true' [-Werror,-Wpointer-bool-conversion] while (p_in->perms && p_in->perms[k]) { Signed-off-by: Matthias Kaehlcke Signed-off-by: Paul Moore --- security/selinux/ss/services.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'security') diff --git a/security/selinux/ss/services.c b/security/selinux/ss/services.c index b4aa491a0a23..60d9b0252321 100644 --- a/security/selinux/ss/services.c +++ b/security/selinux/ss/services.c @@ -157,7 +157,7 @@ static int selinux_set_mapping(struct policydb *pol, } k = 0; - while (p_in->perms && p_in->perms[k]) { + while (p_in->perms[k]) { /* An empty permission string skips ahead */ if (!*p_in->perms[k]) { k++; -- cgit v1.2.3 From 8291798dcf059cdc5e55a59b2c4ad70ae14508c2 Mon Sep 17 00:00:00 2001 From: Kees Cook Date: Wed, 29 Mar 2017 16:52:58 -0700 Subject: TOMOYO: Use designated initializers Prepare to mark sensitive kernel structures for randomization by making sure they're using designated initializers. These were identified during allyesconfig builds of x86, arm, and arm64, with most initializer fixes extracted from grsecurity. Signed-off-by: Kees Cook Acked-by: Tetsuo Handa Signed-off-by: James Morris --- security/tomoyo/file.c | 12 ++++++------ security/tomoyo/tomoyo.c | 20 ++++++++++---------- 2 files changed, 16 insertions(+), 16 deletions(-) (limited to 'security') diff --git a/security/tomoyo/file.c b/security/tomoyo/file.c index 7041a580019e..223f21ffa632 100644 --- a/security/tomoyo/file.c +++ b/security/tomoyo/file.c @@ -692,7 +692,7 @@ int tomoyo_path_number_perm(const u8 type, const struct path *path, { struct tomoyo_request_info r; struct tomoyo_obj_info obj = { - .path1 = *path, + .path1 = { .mnt = path->mnt, .dentry = path->dentry }, }; int error = -ENOMEM; struct tomoyo_path_info buf; @@ -740,7 +740,7 @@ int tomoyo_check_open_permission(struct tomoyo_domain_info *domain, struct tomoyo_path_info buf; struct tomoyo_request_info r; struct tomoyo_obj_info obj = { - .path1 = *path, + .path1 = { .mnt = path->mnt, .dentry = path->dentry }, }; int idx; @@ -786,7 +786,7 @@ int tomoyo_path_perm(const u8 operation, const struct path *path, const char *ta { struct tomoyo_request_info r; struct tomoyo_obj_info obj = { - .path1 = *path, + .path1 = { .mnt = path->mnt, .dentry = path->dentry }, }; int error; struct tomoyo_path_info buf; @@ -843,7 +843,7 @@ int tomoyo_mkdev_perm(const u8 operation, const struct path *path, { struct tomoyo_request_info r; struct tomoyo_obj_info obj = { - .path1 = *path, + .path1 = { .mnt = path->mnt, .dentry = path->dentry }, }; int error = -ENOMEM; struct tomoyo_path_info buf; @@ -890,8 +890,8 @@ int tomoyo_path2_perm(const u8 operation, const struct path *path1, struct tomoyo_path_info buf2; struct tomoyo_request_info r; struct tomoyo_obj_info obj = { - .path1 = *path1, - .path2 = *path2, + .path1 = { .mnt = path1->mnt, .dentry = path1->dentry }, + .path2 = { .mnt = path2->mnt, .dentry = path2->dentry } }; int idx; diff --git a/security/tomoyo/tomoyo.c b/security/tomoyo/tomoyo.c index b5fb930349a9..130b4fa4f65f 100644 --- a/security/tomoyo/tomoyo.c +++ b/security/tomoyo/tomoyo.c @@ -165,7 +165,7 @@ static int tomoyo_path_truncate(const struct path *path) */ static int tomoyo_path_unlink(const struct path *parent, struct dentry *dentry) { - struct path path = { parent->mnt, dentry }; + struct path path = { .mnt = parent->mnt, .dentry = dentry }; return tomoyo_path_perm(TOMOYO_TYPE_UNLINK, &path, NULL); } @@ -181,7 +181,7 @@ static int tomoyo_path_unlink(const struct path *parent, struct dentry *dentry) static int tomoyo_path_mkdir(const struct path *parent, struct dentry *dentry, umode_t mode) { - struct path path = { parent->mnt, dentry }; + struct path path = { .mnt = parent->mnt, .dentry = dentry }; return tomoyo_path_number_perm(TOMOYO_TYPE_MKDIR, &path, mode & S_IALLUGO); } @@ -196,7 +196,7 @@ static int tomoyo_path_mkdir(const struct path *parent, struct dentry *dentry, */ static int tomoyo_path_rmdir(const struct path *parent, struct dentry *dentry) { - struct path path = { parent->mnt, dentry }; + struct path path = { .mnt = parent->mnt, .dentry = dentry }; return tomoyo_path_perm(TOMOYO_TYPE_RMDIR, &path, NULL); } @@ -212,7 +212,7 @@ static int tomoyo_path_rmdir(const struct path *parent, struct dentry *dentry) static int tomoyo_path_symlink(const struct path *parent, struct dentry *dentry, const char *old_name) { - struct path path = { parent->mnt, dentry }; + struct path path = { .mnt = parent->mnt, .dentry = dentry }; return tomoyo_path_perm(TOMOYO_TYPE_SYMLINK, &path, old_name); } @@ -229,7 +229,7 @@ static int tomoyo_path_symlink(const struct path *parent, struct dentry *dentry, static int tomoyo_path_mknod(const struct path *parent, struct dentry *dentry, umode_t mode, unsigned int dev) { - struct path path = { parent->mnt, dentry }; + struct path path = { .mnt = parent->mnt, .dentry = dentry }; int type = TOMOYO_TYPE_CREATE; const unsigned int perm = mode & S_IALLUGO; @@ -268,8 +268,8 @@ static int tomoyo_path_mknod(const struct path *parent, struct dentry *dentry, static int tomoyo_path_link(struct dentry *old_dentry, const struct path *new_dir, struct dentry *new_dentry) { - struct path path1 = { new_dir->mnt, old_dentry }; - struct path path2 = { new_dir->mnt, new_dentry }; + struct path path1 = { .mnt = new_dir->mnt, .dentry = old_dentry }; + struct path path2 = { .mnt = new_dir->mnt, .dentry = new_dentry }; return tomoyo_path2_perm(TOMOYO_TYPE_LINK, &path1, &path2); } @@ -288,8 +288,8 @@ static int tomoyo_path_rename(const struct path *old_parent, const struct path *new_parent, struct dentry *new_dentry) { - struct path path1 = { old_parent->mnt, old_dentry }; - struct path path2 = { new_parent->mnt, new_dentry }; + struct path path1 = { .mnt = old_parent->mnt, .dentry = old_dentry }; + struct path path2 = { .mnt = new_parent->mnt, .dentry = new_dentry }; return tomoyo_path2_perm(TOMOYO_TYPE_RENAME, &path1, &path2); } @@ -417,7 +417,7 @@ static int tomoyo_sb_mount(const char *dev_name, const struct path *path, */ static int tomoyo_sb_umount(struct vfsmount *mnt, int flags) { - struct path path = { mnt, mnt->mnt_root }; + struct path path = { .mnt = mnt, .dentry = mnt->mnt_root }; return tomoyo_path_perm(TOMOYO_TYPE_UMOUNT, &path, NULL); } -- cgit v1.2.3 From cae303df3f379f04ce7efadb2e30de460918b302 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Fri, 31 Mar 2017 18:21:18 +0300 Subject: selinux: Fix an uninitialized variable bug We removed this initialization as a cleanup but it is probably required. The concern is that "nel" can be zero. I'm not an expert on SELinux code but I think it looks possible to write an SELinux policy which triggers this bug. GCC doesn't catch this, but my static checker does. Fixes: 9c312e79d6af ("selinux: Delete an unnecessary variable initialisation in range_read()") Signed-off-by: Dan Carpenter Acked-by: Stephen Smalley Signed-off-by: Paul Moore --- security/selinux/ss/policydb.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'security') diff --git a/security/selinux/ss/policydb.c b/security/selinux/ss/policydb.c index 658247f98dc1..0080122760ad 100644 --- a/security/selinux/ss/policydb.c +++ b/security/selinux/ss/policydb.c @@ -1832,7 +1832,7 @@ u32 string_to_av_perm(struct policydb *p, u16 tclass, const char *name) static int range_read(struct policydb *p, void *fp) { - struct range_trans *rt; + struct range_trans *rt = NULL; struct mls_range *r = NULL; int i, rc; __le32 buf[2]; -- cgit v1.2.3 From fff292914d3a2f1efd05ca71c2ba72a3c663201e Mon Sep 17 00:00:00 2001 From: Elena Reshetova Date: Fri, 31 Mar 2017 15:20:48 +0300 Subject: security, keys: convert key.usage from atomic_t to refcount_t refcount_t type and corresponding API should be used instead of atomic_t when the variable is used as a reference counter. This allows to avoid accidental refcounter overflows that might lead to use-after-free situations. Signed-off-by: Elena Reshetova Signed-off-by: Hans Liljestrand Signed-off-by: Kees Cook Signed-off-by: David Windsor Acked-by: David Howells Signed-off-by: James Morris --- include/linux/key.h | 5 +++-- security/keys/gc.c | 2 +- security/keys/key.c | 6 +++--- security/keys/keyring.c | 8 ++++---- security/keys/proc.c | 2 +- security/keys/request_key_auth.c | 2 +- 6 files changed, 13 insertions(+), 12 deletions(-) (limited to 'security') diff --git a/include/linux/key.h b/include/linux/key.h index e45212f2777e..9d9fac583dd3 100644 --- a/include/linux/key.h +++ b/include/linux/key.h @@ -23,6 +23,7 @@ #include #include #include +#include #ifdef __KERNEL__ #include @@ -135,7 +136,7 @@ static inline bool is_key_possessed(const key_ref_t key_ref) * - Kerberos TGTs and tickets */ struct key { - atomic_t usage; /* number of references */ + refcount_t usage; /* number of references */ key_serial_t serial; /* key serial number */ union { struct list_head graveyard_link; @@ -242,7 +243,7 @@ extern void key_put(struct key *key); static inline struct key *__key_get(struct key *key) { - atomic_inc(&key->usage); + refcount_inc(&key->usage); return key; } diff --git a/security/keys/gc.c b/security/keys/gc.c index addf060399e0..44789256c88c 100644 --- a/security/keys/gc.c +++ b/security/keys/gc.c @@ -220,7 +220,7 @@ continue_scanning: key = rb_entry(cursor, struct key, serial_node); cursor = rb_next(cursor); - if (atomic_read(&key->usage) == 0) + if (refcount_read(&key->usage) == 0) goto found_unreferenced_key; if (unlikely(gc_state & KEY_GC_REAPING_DEAD_1)) { diff --git a/security/keys/key.c b/security/keys/key.c index 346fbf201c22..ff9244392d35 100644 --- a/security/keys/key.c +++ b/security/keys/key.c @@ -285,7 +285,7 @@ struct key *key_alloc(struct key_type *type, const char *desc, if (!key->index_key.description) goto no_memory_3; - atomic_set(&key->usage, 1); + refcount_set(&key->usage, 1); init_rwsem(&key->sem); lockdep_set_class(&key->sem, &type->lock_class); key->index_key.type = type; @@ -621,7 +621,7 @@ void key_put(struct key *key) if (key) { key_check(key); - if (atomic_dec_and_test(&key->usage)) + if (refcount_dec_and_test(&key->usage)) schedule_work(&key_gc_work); } } @@ -656,7 +656,7 @@ not_found: found: /* pretend it doesn't exist if it is awaiting deletion */ - if (atomic_read(&key->usage) == 0) + if (refcount_read(&key->usage) == 0) goto not_found; /* this races with key_put(), but that doesn't matter since key_put() diff --git a/security/keys/keyring.c b/security/keys/keyring.c index c91e4e0cea08..3d95f7d02ba1 100644 --- a/security/keys/keyring.c +++ b/security/keys/keyring.c @@ -1033,7 +1033,7 @@ struct key *find_keyring_by_name(const char *name, bool skip_perm_check) /* we've got a match but we might end up racing with * key_cleanup() if the keyring is currently 'dead' * (ie. it has a zero usage count) */ - if (!atomic_inc_not_zero(&keyring->usage)) + if (!refcount_inc_not_zero(&keyring->usage)) continue; keyring->last_used_at = current_kernel_time().tv_sec; goto out; @@ -1250,14 +1250,14 @@ int key_link(struct key *keyring, struct key *key) struct assoc_array_edit *edit; int ret; - kenter("{%d,%d}", keyring->serial, atomic_read(&keyring->usage)); + kenter("{%d,%d}", keyring->serial, refcount_read(&keyring->usage)); key_check(keyring); key_check(key); ret = __key_link_begin(keyring, &key->index_key, &edit); if (ret == 0) { - kdebug("begun {%d,%d}", keyring->serial, atomic_read(&keyring->usage)); + kdebug("begun {%d,%d}", keyring->serial, refcount_read(&keyring->usage)); ret = __key_link_check_restriction(keyring, key); if (ret == 0) ret = __key_link_check_live_key(keyring, key); @@ -1266,7 +1266,7 @@ int key_link(struct key *keyring, struct key *key) __key_link_end(keyring, &key->index_key, edit); } - kleave(" = %d {%d,%d}", ret, keyring->serial, atomic_read(&keyring->usage)); + kleave(" = %d {%d,%d}", ret, keyring->serial, refcount_read(&keyring->usage)); return ret; } EXPORT_SYMBOL(key_link); diff --git a/security/keys/proc.c b/security/keys/proc.c index b9f531c9e4fa..69199f18bfb3 100644 --- a/security/keys/proc.c +++ b/security/keys/proc.c @@ -252,7 +252,7 @@ static int proc_keys_show(struct seq_file *m, void *v) showflag(key, 'U', KEY_FLAG_USER_CONSTRUCT), showflag(key, 'N', KEY_FLAG_NEGATIVE), showflag(key, 'i', KEY_FLAG_INVALIDATED), - atomic_read(&key->usage), + refcount_read(&key->usage), xbuf, key->perm, from_kuid_munged(seq_user_ns(m), key->uid), diff --git a/security/keys/request_key_auth.c b/security/keys/request_key_auth.c index 6bbe2f535f08..0f062156dfb2 100644 --- a/security/keys/request_key_auth.c +++ b/security/keys/request_key_auth.c @@ -213,7 +213,7 @@ struct key *request_key_auth_new(struct key *target, const void *callout_info, if (ret < 0) goto error_inst; - kleave(" = {%d,%d}", authkey->serial, atomic_read(&authkey->usage)); + kleave(" = {%d,%d}", authkey->serial, refcount_read(&authkey->usage)); return authkey; auth_key_revoked: -- cgit v1.2.3 From ddb99e118e37f324a4be65a411bb60ae62795cf9 Mon Sep 17 00:00:00 2001 From: Elena Reshetova Date: Fri, 31 Mar 2017 15:20:49 +0300 Subject: security, keys: convert key_user.usage from atomic_t to refcount_t refcount_t type and corresponding API should be used instead of atomic_t when the variable is used as a reference counter. This allows to avoid accidental refcounter overflows that might lead to use-after-free situations. Signed-off-by: Elena Reshetova Signed-off-by: Hans Liljestrand Signed-off-by: Kees Cook Signed-off-by: David Windsor Acked-by: David Howells Signed-off-by: James Morris --- security/keys/internal.h | 3 ++- security/keys/key.c | 6 +++--- security/keys/proc.c | 2 +- security/keys/process_keys.c | 2 +- 4 files changed, 7 insertions(+), 6 deletions(-) (limited to 'security') diff --git a/security/keys/internal.h b/security/keys/internal.h index a2f4c0abb8d8..6bee06ae026d 100644 --- a/security/keys/internal.h +++ b/security/keys/internal.h @@ -17,6 +17,7 @@ #include #include #include +#include struct iovec; @@ -53,7 +54,7 @@ struct key_user { struct rb_node node; struct mutex cons_lock; /* construction initiation lock */ spinlock_t lock; - atomic_t usage; /* for accessing qnkeys & qnbytes */ + refcount_t usage; /* for accessing qnkeys & qnbytes */ atomic_t nkeys; /* number of keys */ atomic_t nikeys; /* number of instantiated keys */ kuid_t uid; diff --git a/security/keys/key.c b/security/keys/key.c index ff9244392d35..b4958b36fa27 100644 --- a/security/keys/key.c +++ b/security/keys/key.c @@ -93,7 +93,7 @@ try_again: /* if we get here, then the user record still hadn't appeared on the * second pass - so we use the candidate record */ - atomic_set(&candidate->usage, 1); + refcount_set(&candidate->usage, 1); atomic_set(&candidate->nkeys, 0); atomic_set(&candidate->nikeys, 0); candidate->uid = uid; @@ -110,7 +110,7 @@ try_again: /* okay - we found a user record for this UID */ found: - atomic_inc(&user->usage); + refcount_inc(&user->usage); spin_unlock(&key_user_lock); kfree(candidate); out: @@ -122,7 +122,7 @@ out: */ void key_user_put(struct key_user *user) { - if (atomic_dec_and_lock(&user->usage, &key_user_lock)) { + if (refcount_dec_and_lock(&user->usage, &key_user_lock)) { rb_erase(&user->node, &key_user_tree); spin_unlock(&key_user_lock); diff --git a/security/keys/proc.c b/security/keys/proc.c index 69199f18bfb3..bf08d02b6646 100644 --- a/security/keys/proc.c +++ b/security/keys/proc.c @@ -340,7 +340,7 @@ static int proc_key_users_show(struct seq_file *m, void *v) seq_printf(m, "%5u: %5d %d/%d %d/%d %d/%d\n", from_kuid_munged(seq_user_ns(m), user->uid), - atomic_read(&user->usage), + refcount_read(&user->usage), atomic_read(&user->nkeys), atomic_read(&user->nikeys), user->qnkeys, diff --git a/security/keys/process_keys.c b/security/keys/process_keys.c index b6fdd22205b1..44451af828c0 100644 --- a/security/keys/process_keys.c +++ b/security/keys/process_keys.c @@ -30,7 +30,7 @@ static DEFINE_MUTEX(key_user_keyring_mutex); /* The root user's tracking struct */ struct key_user root_key_user = { - .usage = ATOMIC_INIT(3), + .usage = REFCOUNT_INIT(3), .cons_lock = __MUTEX_INITIALIZER(root_key_user.cons_lock), .lock = __SPIN_LOCK_UNLOCKED(root_key_user.lock), .nkeys = ATOMIC_INIT(2), -- cgit v1.2.3 From 469ff8f7d46d75b36de68a0411a2ce80109ad00b Mon Sep 17 00:00:00 2001 From: Mat Martineau Date: Mon, 25 Apr 2016 11:30:39 -0700 Subject: KEYS: Use a typedef for restrict_link function pointers This pointer type needs to be returned from a lookup function, and without a typedef the syntax gets cumbersome. Signed-off-by: Mat Martineau --- Documentation/security/keys.txt | 5 +---- include/linux/key.h | 16 +++++++--------- security/keys/key.c | 8 ++------ security/keys/keyring.c | 4 +--- 4 files changed, 11 insertions(+), 22 deletions(-) (limited to 'security') diff --git a/Documentation/security/keys.txt b/Documentation/security/keys.txt index 0e03baf271bd..4502237b12a7 100644 --- a/Documentation/security/keys.txt +++ b/Documentation/security/keys.txt @@ -1032,10 +1032,7 @@ payload contents" for more information. struct key *keyring_alloc(const char *description, uid_t uid, gid_t gid, const struct cred *cred, key_perm_t perm, - int (*restrict_link)(struct key *, - const struct key_type *, - unsigned long, - const union key_payload *), + key_restrict_link_func_t restrict_link, unsigned long flags, struct key *dest); diff --git a/include/linux/key.h b/include/linux/key.h index 9d9fac583dd3..3bb327043869 100644 --- a/include/linux/key.h +++ b/include/linux/key.h @@ -127,6 +127,10 @@ static inline bool is_key_possessed(const key_ref_t key_ref) return (unsigned long) key_ref & 1UL; } +typedef int (*key_restrict_link_func_t)(struct key *keyring, + const struct key_type *type, + const union key_payload *payload); + /*****************************************************************************/ /* * authentication token / access credential / keyring @@ -215,9 +219,7 @@ struct key { * overrides this, allowing the kernel to add extra keys without * restriction. */ - int (*restrict_link)(struct key *keyring, - const struct key_type *type, - const union key_payload *payload); + key_restrict_link_func_t restrict_link; }; extern struct key *key_alloc(struct key_type *type, @@ -226,9 +228,7 @@ extern struct key *key_alloc(struct key_type *type, const struct cred *cred, key_perm_t perm, unsigned long flags, - int (*restrict_link)(struct key *, - const struct key_type *, - const union key_payload *)); + key_restrict_link_func_t restrict_link); #define KEY_ALLOC_IN_QUOTA 0x0000 /* add to quota, reject if would overrun */ @@ -304,9 +304,7 @@ extern struct key *keyring_alloc(const char *description, kuid_t uid, kgid_t gid const struct cred *cred, key_perm_t perm, unsigned long flags, - int (*restrict_link)(struct key *, - const struct key_type *, - const union key_payload *), + key_restrict_link_func_t restrict_link, struct key *dest); extern int restrict_link_reject(struct key *keyring, diff --git a/security/keys/key.c b/security/keys/key.c index b4958b36fa27..08dfa13f6a85 100644 --- a/security/keys/key.c +++ b/security/keys/key.c @@ -225,9 +225,7 @@ serial_exists: struct key *key_alloc(struct key_type *type, const char *desc, kuid_t uid, kgid_t gid, const struct cred *cred, key_perm_t perm, unsigned long flags, - int (*restrict_link)(struct key *, - const struct key_type *, - const union key_payload *)) + key_restrict_link_func_t restrict_link) { struct key_user *user = NULL; struct key *key; @@ -806,9 +804,7 @@ key_ref_t key_create_or_update(key_ref_t keyring_ref, struct key *keyring, *key = NULL; key_ref_t key_ref; int ret; - int (*restrict_link)(struct key *, - const struct key_type *, - const union key_payload *) = NULL; + key_restrict_link_func_t restrict_link = NULL; /* look up the key type to see if it's one of the registered kernel * types */ diff --git a/security/keys/keyring.c b/security/keys/keyring.c index 3d95f7d02ba1..1b29ac759bf7 100644 --- a/security/keys/keyring.c +++ b/security/keys/keyring.c @@ -492,9 +492,7 @@ static long keyring_read(const struct key *keyring, struct key *keyring_alloc(const char *description, kuid_t uid, kgid_t gid, const struct cred *cred, key_perm_t perm, unsigned long flags, - int (*restrict_link)(struct key *, - const struct key_type *, - const union key_payload *), + key_restrict_link_func_t restrict_link, struct key *dest) { struct key *keyring; -- cgit v1.2.3 From aaf66c883813f0078e3dafe7d20d1461321ac14f Mon Sep 17 00:00:00 2001 From: Mat Martineau Date: Tue, 30 Aug 2016 11:33:13 -0700 Subject: KEYS: Split role of the keyring pointer for keyring restrict functions The first argument to the restrict_link_func_t functions was a keyring pointer. These functions are called by the key subsystem with this argument set to the destination keyring, but restrict_link_by_signature expects a pointer to the relevant trusted keyring. Restrict functions may need something other than a single struct key pointer to allow or reject key linkage, so the data used to make that decision (such as the trust keyring) is moved to a new, fourth argument. The first argument is now always the destination keyring. Signed-off-by: Mat Martineau --- Documentation/security/keys.txt | 8 ++++---- certs/system_keyring.c | 18 +++++++++++------- crypto/asymmetric_keys/restrict.c | 8 +++++--- include/crypto/public_key.h | 5 +++-- include/keys/system_keyring.h | 6 ++++-- include/linux/key.h | 8 +++++--- security/keys/key.c | 5 +++-- security/keys/keyring.c | 6 ++++-- 8 files changed, 39 insertions(+), 25 deletions(-) (limited to 'security') diff --git a/Documentation/security/keys.txt b/Documentation/security/keys.txt index 4502237b12a7..bb575ab80207 100644 --- a/Documentation/security/keys.txt +++ b/Documentation/security/keys.txt @@ -1054,10 +1054,10 @@ payload contents" for more information. can be verified by a key the kernel already has. When called, the restriction function will be passed the keyring being - added to, the key flags value and the type and payload of the key being - added. Note that when a new key is being created, this is called between - payload preparsing and actual key creation. The function should return 0 - to allow the link or an error to reject it. + added to, the key type, the payload of the key being added, and data to be + used in the restriction check. Note that when a new key is being created, + this is called between payload preparsing and actual key creation. The + function should return 0 to allow the link or an error to reject it. A convenience function, restrict_link_reject, exists to always return -EPERM to in this case. diff --git a/certs/system_keyring.c b/certs/system_keyring.c index 50979d6dcecd..e39cce68dcfa 100644 --- a/certs/system_keyring.c +++ b/certs/system_keyring.c @@ -32,11 +32,13 @@ extern __initconst const unsigned long system_certificate_list_size; * Restrict the addition of keys into a keyring based on the key-to-be-added * being vouched for by a key in the built in system keyring. */ -int restrict_link_by_builtin_trusted(struct key *keyring, +int restrict_link_by_builtin_trusted(struct key *dest_keyring, const struct key_type *type, - const union key_payload *payload) + const union key_payload *payload, + struct key *restriction_key) { - return restrict_link_by_signature(builtin_trusted_keys, type, payload); + return restrict_link_by_signature(dest_keyring, type, payload, + builtin_trusted_keys); } #ifdef CONFIG_SECONDARY_TRUSTED_KEYRING @@ -49,20 +51,22 @@ int restrict_link_by_builtin_trusted(struct key *keyring, * keyrings. */ int restrict_link_by_builtin_and_secondary_trusted( - struct key *keyring, + struct key *dest_keyring, const struct key_type *type, - const union key_payload *payload) + const union key_payload *payload, + struct key *restrict_key) { /* If we have a secondary trusted keyring, then that contains a link * through to the builtin keyring and the search will follow that link. */ if (type == &key_type_keyring && - keyring == secondary_trusted_keys && + dest_keyring == secondary_trusted_keys && payload == &builtin_trusted_keys->payload) /* Allow the builtin keyring to be added to the secondary */ return 0; - return restrict_link_by_signature(secondary_trusted_keys, type, payload); + return restrict_link_by_signature(dest_keyring, type, payload, + secondary_trusted_keys); } #endif diff --git a/crypto/asymmetric_keys/restrict.c b/crypto/asymmetric_keys/restrict.c index 19d1afb9890f..a3afbf783255 100644 --- a/crypto/asymmetric_keys/restrict.c +++ b/crypto/asymmetric_keys/restrict.c @@ -56,9 +56,10 @@ __setup("ca_keys=", ca_keys_setup); /** * restrict_link_by_signature - Restrict additions to a ring of public keys - * @trust_keyring: A ring of keys that can be used to vouch for the new cert. + * @dest_keyring: Keyring being linked to. * @type: The type of key being added. * @payload: The payload of the new key. + * @trust_keyring: A ring of keys that can be used to vouch for the new cert. * * Check the new certificate against the ones in the trust keyring. If one of * those is the signing key and validates the new certificate, then mark the @@ -69,9 +70,10 @@ __setup("ca_keys=", ca_keys_setup); * signature check fails or the key is blacklisted and some other error if * there is a matching certificate but the signature check cannot be performed. */ -int restrict_link_by_signature(struct key *trust_keyring, +int restrict_link_by_signature(struct key *dest_keyring, const struct key_type *type, - const union key_payload *payload) + const union key_payload *payload, + struct key *trust_keyring) { const struct public_key_signature *sig; struct key *key; diff --git a/include/crypto/public_key.h b/include/crypto/public_key.h index 882ca0e1e7a5..ec0262fa08f8 100644 --- a/include/crypto/public_key.h +++ b/include/crypto/public_key.h @@ -50,9 +50,10 @@ struct key; struct key_type; union key_payload; -extern int restrict_link_by_signature(struct key *trust_keyring, +extern int restrict_link_by_signature(struct key *dest_keyring, const struct key_type *type, - const union key_payload *payload); + const union key_payload *payload, + struct key *trust_keyring); extern int verify_signature(const struct key *key, const struct public_key_signature *sig); diff --git a/include/keys/system_keyring.h b/include/keys/system_keyring.h index 0d8762622ab9..359c2f936004 100644 --- a/include/keys/system_keyring.h +++ b/include/keys/system_keyring.h @@ -18,7 +18,8 @@ extern int restrict_link_by_builtin_trusted(struct key *keyring, const struct key_type *type, - const union key_payload *payload); + const union key_payload *payload, + struct key *restriction_key); #else #define restrict_link_by_builtin_trusted restrict_link_reject @@ -28,7 +29,8 @@ extern int restrict_link_by_builtin_trusted(struct key *keyring, extern int restrict_link_by_builtin_and_secondary_trusted( struct key *keyring, const struct key_type *type, - const union key_payload *payload); + const union key_payload *payload, + struct key *restriction_key); #else #define restrict_link_by_builtin_and_secondary_trusted restrict_link_by_builtin_trusted #endif diff --git a/include/linux/key.h b/include/linux/key.h index 3bb327043869..c59d1008c4fc 100644 --- a/include/linux/key.h +++ b/include/linux/key.h @@ -127,9 +127,10 @@ static inline bool is_key_possessed(const key_ref_t key_ref) return (unsigned long) key_ref & 1UL; } -typedef int (*key_restrict_link_func_t)(struct key *keyring, +typedef int (*key_restrict_link_func_t)(struct key *dest_keyring, const struct key_type *type, - const union key_payload *payload); + const union key_payload *payload, + struct key *restriction_key); /*****************************************************************************/ /* @@ -309,7 +310,8 @@ extern struct key *keyring_alloc(const char *description, kuid_t uid, kgid_t gid extern int restrict_link_reject(struct key *keyring, const struct key_type *type, - const union key_payload *payload); + const union key_payload *payload, + struct key *restriction_key); extern int keyring_clear(struct key *keyring); diff --git a/security/keys/key.c b/security/keys/key.c index 08dfa13f6a85..27fc1bb40034 100644 --- a/security/keys/key.c +++ b/security/keys/key.c @@ -499,7 +499,7 @@ int key_instantiate_and_link(struct key *key, if (keyring) { if (keyring->restrict_link) { ret = keyring->restrict_link(keyring, key->type, - &prep.payload); + &prep.payload, NULL); if (ret < 0) goto error; } @@ -851,7 +851,8 @@ key_ref_t key_create_or_update(key_ref_t keyring_ref, index_key.desc_len = strlen(index_key.description); if (restrict_link) { - ret = restrict_link(keyring, index_key.type, &prep.payload); + ret = restrict_link(keyring, index_key.type, &prep.payload, + NULL); if (ret < 0) { key_ref = ERR_PTR(ret); goto error_free_prep; diff --git a/security/keys/keyring.c b/security/keys/keyring.c index 1b29ac759bf7..2ccc66ec35d7 100644 --- a/security/keys/keyring.c +++ b/security/keys/keyring.c @@ -517,6 +517,7 @@ EXPORT_SYMBOL(keyring_alloc); * @keyring: The keyring being added to. * @type: The type of key being added. * @payload: The payload of the key intended to be added. + * @data: Additional data for evaluating restriction. * * Reject the addition of any links to a keyring. It can be overridden by * passing KEY_ALLOC_BYPASS_RESTRICTION to key_instantiate_and_link() when @@ -527,7 +528,8 @@ EXPORT_SYMBOL(keyring_alloc); */ int restrict_link_reject(struct key *keyring, const struct key_type *type, - const union key_payload *payload) + const union key_payload *payload, + struct key *restriction_key) { return -EPERM; } @@ -1220,7 +1222,7 @@ static int __key_link_check_restriction(struct key *keyring, struct key *key) { if (!keyring->restrict_link) return 0; - return keyring->restrict_link(keyring, key->type, &key->payload); + return keyring->restrict_link(keyring, key->type, &key->payload, NULL); } /** -- cgit v1.2.3 From 2b6aa412ff23a02ac777ad307249c60a839cfd25 Mon Sep 17 00:00:00 2001 From: Mat Martineau Date: Wed, 31 Aug 2016 16:05:43 -0700 Subject: KEYS: Use structure to capture key restriction function and data Replace struct key's restrict_link function pointer with a pointer to the new struct key_restriction. The structure contains pointers to the restriction function as well as relevant data for evaluating the restriction. The garbage collector checks restrict_link->keytype when key types are unregistered. Restrictions involving a removed key type are converted to use restrict_link_reject so that restrictions cannot be removed by unregistering key types. Signed-off-by: Mat Martineau --- Documentation/security/keys.txt | 21 +++++++------ certs/system_keyring.c | 21 ++++++++++++- include/linux/key.h | 8 ++--- security/integrity/digsig.c | 9 +++++- security/integrity/ima/ima_mok.c | 11 ++++++- security/keys/gc.c | 11 +++++++ security/keys/internal.h | 2 ++ security/keys/key.c | 23 ++++++++------ security/keys/keyring.c | 68 +++++++++++++++++++++++++++++++++++++--- 9 files changed, 144 insertions(+), 30 deletions(-) (limited to 'security') diff --git a/Documentation/security/keys.txt b/Documentation/security/keys.txt index bb575ab80207..e35de987fc48 100644 --- a/Documentation/security/keys.txt +++ b/Documentation/security/keys.txt @@ -1032,7 +1032,7 @@ payload contents" for more information. struct key *keyring_alloc(const char *description, uid_t uid, gid_t gid, const struct cred *cred, key_perm_t perm, - key_restrict_link_func_t restrict_link, + struct key_restriction *restrict_link, unsigned long flags, struct key *dest); @@ -1044,14 +1044,17 @@ payload contents" for more information. KEY_ALLOC_NOT_IN_QUOTA in flags if the keyring shouldn't be accounted towards the user's quota). Error ENOMEM can also be returned. - If restrict_link not NULL, it should point to a function that will be - called each time an attempt is made to link a key into the new keyring. - This function is called to check whether a key may be added into the keying - or not. Callers of key_create_or_update() within the kernel can pass - KEY_ALLOC_BYPASS_RESTRICTION to suppress the check. An example of using - this is to manage rings of cryptographic keys that are set up when the - kernel boots where userspace is also permitted to add keys - provided they - can be verified by a key the kernel already has. + If restrict_link is not NULL, it should point to a structure that contains + the function that will be called each time an attempt is made to link a + key into the new keyring. The structure may also contain a key pointer + and an associated key type. The function is called to check whether a key + may be added into the keyring or not. The key type is used by the garbage + collector to clean up function or data pointers in this structure if the + given key type is unregistered. Callers of key_create_or_update() within + the kernel can pass KEY_ALLOC_BYPASS_RESTRICTION to suppress the check. + An example of using this is to manage rings of cryptographic keys that are + set up when the kernel boots where userspace is also permitted to add keys + - provided they can be verified by a key the kernel already has. When called, the restriction function will be passed the keyring being added to, the key type, the payload of the key being added, and data to be diff --git a/certs/system_keyring.c b/certs/system_keyring.c index e39cce68dcfa..6251d1b27f0c 100644 --- a/certs/system_keyring.c +++ b/certs/system_keyring.c @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -68,6 +69,24 @@ int restrict_link_by_builtin_and_secondary_trusted( return restrict_link_by_signature(dest_keyring, type, payload, secondary_trusted_keys); } + +/** + * Allocate a struct key_restriction for the "builtin and secondary trust" + * keyring. Only for use in system_trusted_keyring_init(). + */ +static __init struct key_restriction *get_builtin_and_secondary_restriction(void) +{ + struct key_restriction *restriction; + + restriction = kzalloc(sizeof(struct key_restriction), GFP_KERNEL); + + if (!restriction) + panic("Can't allocate secondary trusted keyring restriction\n"); + + restriction->check = restrict_link_by_builtin_and_secondary_trusted; + + return restriction; +} #endif /* @@ -95,7 +114,7 @@ static __init int system_trusted_keyring_init(void) KEY_USR_VIEW | KEY_USR_READ | KEY_USR_SEARCH | KEY_USR_WRITE), KEY_ALLOC_NOT_IN_QUOTA, - restrict_link_by_builtin_and_secondary_trusted, + get_builtin_and_secondary_restriction(), NULL); if (IS_ERR(secondary_trusted_keys)) panic("Can't allocate secondary trusted keyring\n"); diff --git a/include/linux/key.h b/include/linux/key.h index a06649f3223d..d2916363689c 100644 --- a/include/linux/key.h +++ b/include/linux/key.h @@ -217,7 +217,7 @@ struct key { }; /* This is set on a keyring to restrict the addition of a link to a key - * to it. If this method isn't provided then it is assumed that the + * to it. If this structure isn't provided then it is assumed that the * keyring is open to any addition. It is ignored for non-keyring * keys. * @@ -226,7 +226,7 @@ struct key { * overrides this, allowing the kernel to add extra keys without * restriction. */ - key_restrict_link_func_t restrict_link; + struct key_restriction *restrict_link; }; extern struct key *key_alloc(struct key_type *type, @@ -235,7 +235,7 @@ extern struct key *key_alloc(struct key_type *type, const struct cred *cred, key_perm_t perm, unsigned long flags, - key_restrict_link_func_t restrict_link); + struct key_restriction *restrict_link); #define KEY_ALLOC_IN_QUOTA 0x0000 /* add to quota, reject if would overrun */ @@ -311,7 +311,7 @@ extern struct key *keyring_alloc(const char *description, kuid_t uid, kgid_t gid const struct cred *cred, key_perm_t perm, unsigned long flags, - key_restrict_link_func_t restrict_link, + struct key_restriction *restrict_link, struct key *dest); extern int restrict_link_reject(struct key *keyring, diff --git a/security/integrity/digsig.c b/security/integrity/digsig.c index 106e855e2d9d..06554c448dce 100644 --- a/security/integrity/digsig.c +++ b/security/integrity/digsig.c @@ -81,18 +81,25 @@ int integrity_digsig_verify(const unsigned int id, const char *sig, int siglen, int __init integrity_init_keyring(const unsigned int id) { const struct cred *cred = current_cred(); + struct key_restriction *restriction; int err = 0; if (!init_keyring) return 0; + restriction = kzalloc(sizeof(struct key_restriction), GFP_KERNEL); + if (!restriction) + return -ENOMEM; + + restriction->check = restrict_link_to_ima; + keyring[id] = keyring_alloc(keyring_name[id], KUIDT_INIT(0), KGIDT_INIT(0), cred, ((KEY_POS_ALL & ~KEY_POS_SETATTR) | KEY_USR_VIEW | KEY_USR_READ | KEY_USR_WRITE | KEY_USR_SEARCH), KEY_ALLOC_NOT_IN_QUOTA, - restrict_link_to_ima, NULL); + restriction, NULL); if (IS_ERR(keyring[id])) { err = PTR_ERR(keyring[id]); pr_info("Can't allocate %s keyring (%d)\n", diff --git a/security/integrity/ima/ima_mok.c b/security/integrity/ima/ima_mok.c index 74a279957464..073ddc9bce5b 100644 --- a/security/integrity/ima/ima_mok.c +++ b/security/integrity/ima/ima_mok.c @@ -17,6 +17,7 @@ #include #include #include +#include #include @@ -27,15 +28,23 @@ struct key *ima_blacklist_keyring; */ __init int ima_mok_init(void) { + struct key_restriction *restriction; + pr_notice("Allocating IMA blacklist keyring.\n"); + restriction = kzalloc(sizeof(struct key_restriction), GFP_KERNEL); + if (!restriction) + panic("Can't allocate IMA blacklist restriction."); + + restriction->check = restrict_link_by_builtin_trusted; + ima_blacklist_keyring = keyring_alloc(".ima_blacklist", KUIDT_INIT(0), KGIDT_INIT(0), current_cred(), (KEY_POS_ALL & ~KEY_POS_SETATTR) | KEY_USR_VIEW | KEY_USR_READ | KEY_USR_WRITE | KEY_USR_SEARCH, KEY_ALLOC_NOT_IN_QUOTA, - restrict_link_by_builtin_trusted, NULL); + restriction, NULL); if (IS_ERR(ima_blacklist_keyring)) panic("Can't allocate IMA blacklist keyring."); diff --git a/security/keys/gc.c b/security/keys/gc.c index 44789256c88c..15b9ddf510e4 100644 --- a/security/keys/gc.c +++ b/security/keys/gc.c @@ -229,6 +229,9 @@ continue_scanning: set_bit(KEY_FLAG_DEAD, &key->flags); key->perm = 0; goto skip_dead_key; + } else if (key->type == &key_type_keyring && + key->restrict_link) { + goto found_restricted_keyring; } } @@ -334,6 +337,14 @@ found_unreferenced_key: gc_state |= KEY_GC_REAP_AGAIN; goto maybe_resched; + /* We found a restricted keyring and need to update the restriction if + * it is associated with the dead key type. + */ +found_restricted_keyring: + spin_unlock(&key_serial_lock); + keyring_restriction_gc(key, key_gc_dead_keytype); + goto maybe_resched; + /* We found a keyring and we need to check the payload for links to * dead or expired keys. We don't flag another reap immediately as we * have to wait for the old payload to be destroyed by RCU before we diff --git a/security/keys/internal.h b/security/keys/internal.h index 6bee06ae026d..24762ae9a198 100644 --- a/security/keys/internal.h +++ b/security/keys/internal.h @@ -168,6 +168,8 @@ extern void key_change_session_keyring(struct callback_head *twork); extern struct work_struct key_gc_work; extern unsigned key_gc_delay; extern void keyring_gc(struct key *keyring, time_t limit); +extern void keyring_restriction_gc(struct key *keyring, + struct key_type *dead_type); extern void key_schedule_gc(time_t gc_at); extern void key_schedule_gc_links(void); extern void key_gc_keytype(struct key_type *ktype); diff --git a/security/keys/key.c b/security/keys/key.c index 27fc1bb40034..2ea5967121de 100644 --- a/security/keys/key.c +++ b/security/keys/key.c @@ -201,12 +201,15 @@ serial_exists: * @cred: The credentials specifying UID namespace. * @perm: The permissions mask of the new key. * @flags: Flags specifying quota properties. - * @restrict_link: Optional link restriction method for new keyrings. + * @restrict_link: Optional link restriction for new keyrings. * * Allocate a key of the specified type with the attributes given. The key is * returned in an uninstantiated state and the caller needs to instantiate the * key before returning. * + * The restrict_link structure (if not NULL) will be freed when the + * keyring is destroyed, so it must be dynamically allocated. + * * The user's key count quota is updated to reflect the creation of the key and * the user's key data quota has the default for the key type reserved. The * instantiation function should amend this as necessary. If insufficient @@ -225,7 +228,7 @@ serial_exists: struct key *key_alloc(struct key_type *type, const char *desc, kuid_t uid, kgid_t gid, const struct cred *cred, key_perm_t perm, unsigned long flags, - key_restrict_link_func_t restrict_link) + struct key_restriction *restrict_link) { struct key_user *user = NULL; struct key *key; @@ -497,9 +500,11 @@ int key_instantiate_and_link(struct key *key, } if (keyring) { - if (keyring->restrict_link) { - ret = keyring->restrict_link(keyring, key->type, - &prep.payload, NULL); + if (keyring->restrict_link && keyring->restrict_link->check) { + struct key_restriction *keyres = keyring->restrict_link; + + ret = keyres->check(keyring, key->type, &prep.payload, + keyres->key); if (ret < 0) goto error; } @@ -804,7 +809,7 @@ key_ref_t key_create_or_update(key_ref_t keyring_ref, struct key *keyring, *key = NULL; key_ref_t key_ref; int ret; - key_restrict_link_func_t restrict_link = NULL; + struct key_restriction *restrict_link = NULL; /* look up the key type to see if it's one of the registered kernel * types */ @@ -850,9 +855,9 @@ key_ref_t key_create_or_update(key_ref_t keyring_ref, } index_key.desc_len = strlen(index_key.description); - if (restrict_link) { - ret = restrict_link(keyring, index_key.type, &prep.payload, - NULL); + if (restrict_link && restrict_link->check) { + ret = restrict_link->check(keyring, index_key.type, + &prep.payload, restrict_link->key); if (ret < 0) { key_ref = ERR_PTR(ret); goto error_free_prep; diff --git a/security/keys/keyring.c b/security/keys/keyring.c index 2ccc66ec35d7..838334fec6ce 100644 --- a/security/keys/keyring.c +++ b/security/keys/keyring.c @@ -394,6 +394,13 @@ static void keyring_destroy(struct key *keyring) write_unlock(&keyring_name_lock); } + if (keyring->restrict_link) { + struct key_restriction *keyres = keyring->restrict_link; + + key_put(keyres->key); + kfree(keyres); + } + assoc_array_destroy(&keyring->keys, &keyring_assoc_array_ops); } @@ -492,7 +499,7 @@ static long keyring_read(const struct key *keyring, struct key *keyring_alloc(const char *description, kuid_t uid, kgid_t gid, const struct cred *cred, key_perm_t perm, unsigned long flags, - key_restrict_link_func_t restrict_link, + struct key_restriction *restrict_link, struct key *dest) { struct key *keyring; @@ -523,8 +530,8 @@ EXPORT_SYMBOL(keyring_alloc); * passing KEY_ALLOC_BYPASS_RESTRICTION to key_instantiate_and_link() when * adding a key to a keyring. * - * This is meant to be passed as the restrict_link parameter to - * keyring_alloc(). + * This is meant to be stored in a key_restriction structure which is passed + * in the restrict_link parameter to keyring_alloc(). */ int restrict_link_reject(struct key *keyring, const struct key_type *type, @@ -1220,9 +1227,10 @@ void __key_link_end(struct key *keyring, */ static int __key_link_check_restriction(struct key *keyring, struct key *key) { - if (!keyring->restrict_link) + if (!keyring->restrict_link || !keyring->restrict_link->check) return 0; - return keyring->restrict_link(keyring, key->type, &key->payload, NULL); + return keyring->restrict_link->check(keyring, key->type, &key->payload, + keyring->restrict_link->key); } /** @@ -1426,3 +1434,53 @@ do_gc: up_write(&keyring->sem); kleave(" [gc]"); } + +/* + * Garbage collect restriction pointers from a keyring. + * + * Keyring restrictions are associated with a key type, and must be cleaned + * up if the key type is unregistered. The restriction is altered to always + * reject additional keys so a keyring cannot be opened up by unregistering + * a key type. + * + * Not called with any keyring locks held. The keyring's key struct will not + * be deallocated under us as only our caller may deallocate it. + * + * The caller is required to hold key_types_sem and dead_type->sem. This is + * fulfilled by key_gc_keytype() holding the locks on behalf of + * key_garbage_collector(), which it invokes on a workqueue. + */ +void keyring_restriction_gc(struct key *keyring, struct key_type *dead_type) +{ + struct key_restriction *keyres; + + kenter("%x{%s}", keyring->serial, keyring->description ?: ""); + + /* + * keyring->restrict_link is only assigned at key allocation time + * or with the key type locked, so the only values that could be + * concurrently assigned to keyring->restrict_link are for key + * types other than dead_type. Given this, it's ok to check + * the key type before acquiring keyring->sem. + */ + if (!dead_type || !keyring->restrict_link || + keyring->restrict_link->keytype != dead_type) { + kleave(" [no restriction gc]"); + return; + } + + /* Lock the keyring to ensure that a link is not in progress */ + down_write(&keyring->sem); + + keyres = keyring->restrict_link; + + keyres->check = restrict_link_reject; + + key_put(keyres->key); + keyres->key = NULL; + keyres->keytype = NULL; + + up_write(&keyring->sem); + + kleave(" [restriction gc]"); +} -- cgit v1.2.3 From 4a420896f12d2d043602f134ae18ad6be5b9d9dd Mon Sep 17 00:00:00 2001 From: Mat Martineau Date: Tue, 4 Oct 2016 16:27:32 -0700 Subject: KEYS: Consistent ordering for __key_link_begin and restrict check The keyring restrict callback was sometimes called before __key_link_begin and sometimes after, which meant that the keyring semaphores were not always held during the restrict callback. If the semaphores are consistently acquired before checking link restrictions, keyring contents cannot be changed after the restrict check is complete but before the evaluated key is linked to the keyring. Signed-off-by: Mat Martineau --- security/keys/key.c | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) (limited to 'security') diff --git a/security/keys/key.c b/security/keys/key.c index 2ea5967121de..455c04d80bbb 100644 --- a/security/keys/key.c +++ b/security/keys/key.c @@ -500,21 +500,23 @@ int key_instantiate_and_link(struct key *key, } if (keyring) { + ret = __key_link_begin(keyring, &key->index_key, &edit); + if (ret < 0) + goto error; + if (keyring->restrict_link && keyring->restrict_link->check) { struct key_restriction *keyres = keyring->restrict_link; ret = keyres->check(keyring, key->type, &prep.payload, keyres->key); if (ret < 0) - goto error; + goto error_link_end; } - ret = __key_link_begin(keyring, &key->index_key, &edit); - if (ret < 0) - goto error; } ret = __key_instantiate_and_link(key, &prep, keyring, authkey, &edit); +error_link_end: if (keyring) __key_link_end(keyring, &key->index_key, edit); @@ -855,21 +857,21 @@ key_ref_t key_create_or_update(key_ref_t keyring_ref, } index_key.desc_len = strlen(index_key.description); + ret = __key_link_begin(keyring, &index_key, &edit); + if (ret < 0) { + key_ref = ERR_PTR(ret); + goto error_free_prep; + } + if (restrict_link && restrict_link->check) { ret = restrict_link->check(keyring, index_key.type, &prep.payload, restrict_link->key); if (ret < 0) { key_ref = ERR_PTR(ret); - goto error_free_prep; + goto error_link_end; } } - ret = __key_link_begin(keyring, &index_key, &edit); - if (ret < 0) { - key_ref = ERR_PTR(ret); - goto error_free_prep; - } - /* if we're going to allocate a new key, we're going to have * to modify the keyring */ ret = key_permission(keyring_ref, KEY_NEED_WRITE); -- cgit v1.2.3 From 6563c91fd645556c7801748f15bc727c77fcd311 Mon Sep 17 00:00:00 2001 From: Mat Martineau Date: Wed, 1 Mar 2017 16:44:09 -0800 Subject: KEYS: Add KEYCTL_RESTRICT_KEYRING Keyrings recently gained restrict_link capabilities that allow individual keys to be validated prior to linking. This functionality was only available using internal kernel APIs. With the KEYCTL_RESTRICT_KEYRING command existing keyrings can be configured to check the content of keys before they are linked, and then allow or disallow linkage of that key to the keyring. To restrict a keyring, call: keyctl(KEYCTL_RESTRICT_KEYRING, key_serial_t keyring, const char *type, const char *restriction) where 'type' is the name of a registered key type and 'restriction' is a string describing how key linkage is to be restricted. The restriction option syntax is specific to each key type. Signed-off-by: Mat Martineau --- Documentation/security/keys.txt | 25 ++++++++++ include/linux/key.h | 6 ++- include/uapi/linux/keyctl.h | 1 + security/keys/compat.c | 4 ++ security/keys/internal.h | 3 ++ security/keys/keyctl.c | 58 ++++++++++++++++++++++ security/keys/keyring.c | 105 ++++++++++++++++++++++++++++++++++++++++ 7 files changed, 201 insertions(+), 1 deletion(-) (limited to 'security') diff --git a/Documentation/security/keys.txt b/Documentation/security/keys.txt index 5fe04a7cc03d..5f554aab8751 100644 --- a/Documentation/security/keys.txt +++ b/Documentation/security/keys.txt @@ -857,6 +857,31 @@ The keyctl syscall functions are: supported, error ENOKEY if the key could not be found, or error EACCES if the key is not readable by the caller. + (*) Restrict keyring linkage + + long keyctl(KEYCTL_RESTRICT_KEYRING, key_serial_t keyring, + const char *type, const char *restriction); + + An existing keyring can restrict linkage of additional keys by evaluating + the contents of the key according to a restriction scheme. + + "keyring" is the key ID for an existing keyring to apply a restriction + to. It may be empty or may already have keys linked. Existing linked keys + will remain in the keyring even if the new restriction would reject them. + + "type" is a registered key type. + + "restriction" is a string describing how key linkage is to be restricted. + The format varies depending on the key type, and the string is passed to + the lookup_restriction() function for the requested type. It may specify + a method and relevant data for the restriction such as signature + verification or constraints on key payload. If the requested key type is + later unregistered, no keys may be added to the keyring after the key type + is removed. + + To apply a keyring restriction the process must have Set Attribute + permission and the keyring must not be previously restricted. + =============== KERNEL SERVICES =============== diff --git a/include/linux/key.h b/include/linux/key.h index d2916363689c..0c9b93b0d1f7 100644 --- a/include/linux/key.h +++ b/include/linux/key.h @@ -219,7 +219,8 @@ struct key { /* This is set on a keyring to restrict the addition of a link to a key * to it. If this structure isn't provided then it is assumed that the * keyring is open to any addition. It is ignored for non-keyring - * keys. + * keys. Only set this value using keyring_restrict(), keyring_alloc(), + * or key_alloc(). * * This is intended for use with rings of trusted keys whereby addition * to the keyring needs to be controlled. KEY_ALLOC_BYPASS_RESTRICTION @@ -328,6 +329,9 @@ extern key_ref_t keyring_search(key_ref_t keyring, extern int keyring_add_key(struct key *keyring, struct key *key); +extern int keyring_restrict(key_ref_t keyring, const char *type, + const char *restriction); + extern struct key *key_lookup(key_serial_t id); static inline key_serial_t key_serial(const struct key *key) diff --git a/include/uapi/linux/keyctl.h b/include/uapi/linux/keyctl.h index 86eddd6241f3..ff79c44e49a3 100644 --- a/include/uapi/linux/keyctl.h +++ b/include/uapi/linux/keyctl.h @@ -60,6 +60,7 @@ #define KEYCTL_INVALIDATE 21 /* invalidate a key */ #define KEYCTL_GET_PERSISTENT 22 /* get a user's persistent keyring */ #define KEYCTL_DH_COMPUTE 23 /* Compute Diffie-Hellman values */ +#define KEYCTL_RESTRICT_KEYRING 29 /* Restrict keys allowed to link to a keyring */ /* keyctl structures */ struct keyctl_dh_params { diff --git a/security/keys/compat.c b/security/keys/compat.c index 36c80bf5b89c..bb98f2b8dd7d 100644 --- a/security/keys/compat.c +++ b/security/keys/compat.c @@ -136,6 +136,10 @@ COMPAT_SYSCALL_DEFINE5(keyctl, u32, option, return keyctl_dh_compute(compat_ptr(arg2), compat_ptr(arg3), arg4, compat_ptr(arg5)); + case KEYCTL_RESTRICT_KEYRING: + return keyctl_restrict_keyring(arg2, compat_ptr(arg3), + compat_ptr(arg4)); + default: return -EOPNOTSUPP; } diff --git a/security/keys/internal.h b/security/keys/internal.h index 24762ae9a198..6ce016314897 100644 --- a/security/keys/internal.h +++ b/security/keys/internal.h @@ -252,6 +252,9 @@ struct iov_iter; extern long keyctl_instantiate_key_common(key_serial_t, struct iov_iter *, key_serial_t); +extern long keyctl_restrict_keyring(key_serial_t id, + const char __user *_type, + const char __user *_restriction); #ifdef CONFIG_PERSISTENT_KEYRINGS extern long keyctl_get_persistent(uid_t, key_serial_t); extern unsigned persistent_keyring_expiry; diff --git a/security/keys/keyctl.c b/security/keys/keyctl.c index 52c34532c785..6ee2826a2d06 100644 --- a/security/keys/keyctl.c +++ b/security/keys/keyctl.c @@ -1582,6 +1582,59 @@ error_keyring: return ret; } +/* + * Apply a restriction to a given keyring. + * + * The caller must have Setattr permission to change keyring restrictions. + * + * The requested type name may be a NULL pointer to reject all attempts + * to link to the keyring. If _type is non-NULL, _restriction can be + * NULL or a pointer to a string describing the restriction. If _type is + * NULL, _restriction must also be NULL. + * + * Returns 0 if successful. + */ +long keyctl_restrict_keyring(key_serial_t id, const char __user *_type, + const char __user *_restriction) +{ + key_ref_t key_ref; + bool link_reject = !_type; + char type[32]; + char *restriction = NULL; + long ret; + + key_ref = lookup_user_key(id, 0, KEY_NEED_SETATTR); + if (IS_ERR(key_ref)) + return PTR_ERR(key_ref); + + if (_type) { + ret = key_get_type_from_user(type, _type, sizeof(type)); + if (ret < 0) + goto error; + } + + if (_restriction) { + if (!_type) { + ret = -EINVAL; + goto error; + } + + restriction = strndup_user(_restriction, PAGE_SIZE); + if (IS_ERR(restriction)) { + ret = PTR_ERR(restriction); + goto error; + } + } + + ret = keyring_restrict(key_ref, link_reject ? NULL : type, restriction); + kfree(restriction); + +error: + key_ref_put(key_ref); + + return ret; +} + /* * The key control system call */ @@ -1693,6 +1746,11 @@ SYSCALL_DEFINE5(keyctl, int, option, unsigned long, arg2, unsigned long, arg3, (char __user *) arg3, (size_t) arg4, (void __user *) arg5); + case KEYCTL_RESTRICT_KEYRING: + return keyctl_restrict_keyring((key_serial_t) arg2, + (const char __user *) arg3, + (const char __user *) arg4); + default: return -EOPNOTSUPP; } diff --git a/security/keys/keyring.c b/security/keys/keyring.c index 838334fec6ce..4d1678e4586f 100644 --- a/security/keys/keyring.c +++ b/security/keys/keyring.c @@ -947,6 +947,111 @@ key_ref_t keyring_search(key_ref_t keyring, } EXPORT_SYMBOL(keyring_search); +static struct key_restriction *keyring_restriction_alloc( + key_restrict_link_func_t check) +{ + struct key_restriction *keyres = + kzalloc(sizeof(struct key_restriction), GFP_KERNEL); + + if (!keyres) + return ERR_PTR(-ENOMEM); + + keyres->check = check; + + return keyres; +} + +/* + * Semaphore to serialise restriction setup to prevent reference count + * cycles through restriction key pointers. + */ +static DECLARE_RWSEM(keyring_serialise_restrict_sem); + +/* + * Check for restriction cycles that would prevent keyring garbage collection. + * keyring_serialise_restrict_sem must be held. + */ +static bool keyring_detect_restriction_cycle(const struct key *dest_keyring, + struct key_restriction *keyres) +{ + while (keyres && keyres->key && + keyres->key->type == &key_type_keyring) { + if (keyres->key == dest_keyring) + return true; + + keyres = keyres->key->restrict_link; + } + + return false; +} + +/** + * keyring_restrict - Look up and apply a restriction to a keyring + * + * @keyring: The keyring to be restricted + * @restriction: The restriction options to apply to the keyring + */ +int keyring_restrict(key_ref_t keyring_ref, const char *type, + const char *restriction) +{ + struct key *keyring; + struct key_type *restrict_type = NULL; + struct key_restriction *restrict_link; + int ret = 0; + + keyring = key_ref_to_ptr(keyring_ref); + key_check(keyring); + + if (keyring->type != &key_type_keyring) + return -ENOTDIR; + + if (!type) { + restrict_link = keyring_restriction_alloc(restrict_link_reject); + } else { + restrict_type = key_type_lookup(type); + + if (IS_ERR(restrict_type)) + return PTR_ERR(restrict_type); + + if (!restrict_type->lookup_restriction) { + ret = -ENOENT; + goto error; + } + + restrict_link = restrict_type->lookup_restriction(restriction); + } + + if (IS_ERR(restrict_link)) { + ret = PTR_ERR(restrict_link); + goto error; + } + + down_write(&keyring->sem); + down_write(&keyring_serialise_restrict_sem); + + if (keyring->restrict_link) + ret = -EEXIST; + else if (keyring_detect_restriction_cycle(keyring, restrict_link)) + ret = -EDEADLK; + else + keyring->restrict_link = restrict_link; + + up_write(&keyring_serialise_restrict_sem); + up_write(&keyring->sem); + + if (ret < 0) { + key_put(restrict_link->key); + kfree(restrict_link); + } + +error: + if (restrict_type) + key_type_put(restrict_type); + + return ret; +} +EXPORT_SYMBOL(keyring_restrict); + /* * Search the given keyring for a key that might be updated. * -- cgit v1.2.3 From f1c316a3ab9d24df6022682422fe897492f2c0c8 Mon Sep 17 00:00:00 2001 From: Stephan Mueller Date: Fri, 19 Aug 2016 20:39:09 +0200 Subject: KEYS: add SP800-56A KDF support for DH SP800-56A defines the use of DH with key derivation function based on a counter. The input to the KDF is defined as (DH shared secret || other information). The value for the "other information" is to be provided by the caller. The KDF is implemented using the hash support from the kernel crypto API. The implementation uses the symmetric hash support as the input to the hash operation is usually very small. The caller is allowed to specify the hash name that he wants to use to derive the key material allowing the use of all supported hashes provided with the kernel crypto API. As the KDF implements the proper truncation of the DH shared secret to the requested size, this patch fills the caller buffer up to its size. The patch is tested with a new test added to the keyutils user space code which uses a CAVS test vector testing the compliance with SP800-56A. Signed-off-by: Stephan Mueller Signed-off-by: David Howells --- Documentation/security/keys.txt | 34 +++++-- include/linux/compat.h | 7 ++ include/uapi/linux/keyctl.h | 7 ++ security/keys/Kconfig | 1 + security/keys/Makefile | 3 +- security/keys/compat.c | 5 +- security/keys/compat_dh.c | 38 +++++++ security/keys/dh.c | 220 +++++++++++++++++++++++++++++++++++++--- security/keys/internal.h | 24 ++++- security/keys/keyctl.c | 2 +- 10 files changed, 315 insertions(+), 26 deletions(-) create mode 100644 security/keys/compat_dh.c (limited to 'security') diff --git a/Documentation/security/keys.txt b/Documentation/security/keys.txt index 5f554aab8751..cd5019934d7f 100644 --- a/Documentation/security/keys.txt +++ b/Documentation/security/keys.txt @@ -827,7 +827,7 @@ The keyctl syscall functions are: long keyctl(KEYCTL_DH_COMPUTE, struct keyctl_dh_params *params, char *buffer, size_t buflen, - void *reserved); + struct keyctl_kdf_params *kdf); The params struct contains serial numbers for three keys: @@ -844,18 +844,36 @@ The keyctl syscall functions are: public key. If the base is the remote public key, the result is the shared secret. - The reserved argument must be set to NULL. + If the parameter kdf is NULL, the following applies: - The buffer length must be at least the length of the prime, or zero. + - The buffer length must be at least the length of the prime, or zero. - If the buffer length is nonzero, the length of the result is - returned when it is successfully calculated and copied in to the - buffer. When the buffer length is zero, the minimum required - buffer length is returned. + - If the buffer length is nonzero, the length of the result is + returned when it is successfully calculated and copied in to the + buffer. When the buffer length is zero, the minimum required + buffer length is returned. + + The kdf parameter allows the caller to apply a key derivation function + (KDF) on the Diffie-Hellman computation where only the result + of the KDF is returned to the caller. The KDF is characterized with + struct keyctl_kdf_params as follows: + + - char *hashname specifies the NUL terminated string identifying + the hash used from the kernel crypto API and applied for the KDF + operation. The KDF implemenation complies with SP800-56A as well + as with SP800-108 (the counter KDF). + + - char *otherinfo specifies the OtherInfo data as documented in + SP800-56A section 5.8.1.2. The length of the buffer is given with + otherinfolen. The format of OtherInfo is defined by the caller. + The otherinfo pointer may be NULL if no OtherInfo shall be used. This function will return error EOPNOTSUPP if the key type is not supported, error ENOKEY if the key could not be found, or error - EACCES if the key is not readable by the caller. + EACCES if the key is not readable by the caller. In addition, the + function will return EMSGSIZE when the parameter kdf is non-NULL + and either the buffer length or the OtherInfo length exceeds the + allowed length. (*) Restrict keyring linkage diff --git a/include/linux/compat.h b/include/linux/compat.h index aef47be2a5c1..993c87182e02 100644 --- a/include/linux/compat.h +++ b/include/linux/compat.h @@ -295,6 +295,13 @@ struct compat_old_sigaction { }; #endif +struct compat_keyctl_kdf_params { + compat_uptr_t hashname; + compat_uptr_t otherinfo; + __u32 otherinfolen; + __u32 __spare[8]; +}; + struct compat_statfs; struct compat_statfs64; struct compat_old_linux_dirent; diff --git a/include/uapi/linux/keyctl.h b/include/uapi/linux/keyctl.h index ff79c44e49a3..201c6644b237 100644 --- a/include/uapi/linux/keyctl.h +++ b/include/uapi/linux/keyctl.h @@ -69,4 +69,11 @@ struct keyctl_dh_params { __s32 base; }; +struct keyctl_kdf_params { + char *hashname; + char *otherinfo; + __u32 otherinfolen; + __u32 __spare[8]; +}; + #endif /* _LINUX_KEYCTL_H */ diff --git a/security/keys/Kconfig b/security/keys/Kconfig index d942c7c2bc0a..4ac1b83a23f8 100644 --- a/security/keys/Kconfig +++ b/security/keys/Kconfig @@ -90,6 +90,7 @@ config KEY_DH_OPERATIONS bool "Diffie-Hellman operations on retained keys" depends on KEYS select MPILIB + select CRYPTO_HASH help This option provides support for calculating Diffie-Hellman public keys and shared secrets using values stored as keys diff --git a/security/keys/Makefile b/security/keys/Makefile index 1fd4a16e6daf..57dff0c15809 100644 --- a/security/keys/Makefile +++ b/security/keys/Makefile @@ -15,7 +15,8 @@ obj-y := \ request_key.o \ request_key_auth.o \ user_defined.o -obj-$(CONFIG_KEYS_COMPAT) += compat.o +compat-obj-$(CONFIG_KEY_DH_OPERATIONS) += compat_dh.o +obj-$(CONFIG_KEYS_COMPAT) += compat.o $(compat-obj-y) obj-$(CONFIG_PROC_FS) += proc.o obj-$(CONFIG_SYSCTL) += sysctl.o obj-$(CONFIG_PERSISTENT_KEYRINGS) += persistent.o diff --git a/security/keys/compat.c b/security/keys/compat.c index bb98f2b8dd7d..e87c89c0177c 100644 --- a/security/keys/compat.c +++ b/security/keys/compat.c @@ -133,8 +133,9 @@ COMPAT_SYSCALL_DEFINE5(keyctl, u32, option, return keyctl_get_persistent(arg2, arg3); case KEYCTL_DH_COMPUTE: - return keyctl_dh_compute(compat_ptr(arg2), compat_ptr(arg3), - arg4, compat_ptr(arg5)); + return compat_keyctl_dh_compute(compat_ptr(arg2), + compat_ptr(arg3), + arg4, compat_ptr(arg5)); case KEYCTL_RESTRICT_KEYRING: return keyctl_restrict_keyring(arg2, compat_ptr(arg3), diff --git a/security/keys/compat_dh.c b/security/keys/compat_dh.c new file mode 100644 index 000000000000..a6a659b6bcb6 --- /dev/null +++ b/security/keys/compat_dh.c @@ -0,0 +1,38 @@ +/* 32-bit compatibility syscall for 64-bit systems for DH operations + * + * Copyright (C) 2016 Stephan Mueller + * + * 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; either version + * 2 of the License, or (at your option) any later version. + */ + +#include + +#include "internal.h" + +/* + * Perform the DH computation or DH based key derivation. + * + * If successful, 0 will be returned. + */ +long compat_keyctl_dh_compute(struct keyctl_dh_params __user *params, + char __user *buffer, size_t buflen, + struct compat_keyctl_kdf_params __user *kdf) +{ + struct keyctl_kdf_params kdfcopy; + struct compat_keyctl_kdf_params compat_kdfcopy; + + if (!kdf) + return __keyctl_dh_compute(params, buffer, buflen, NULL); + + if (copy_from_user(&compat_kdfcopy, kdf, sizeof(compat_kdfcopy)) != 0) + return -EFAULT; + + kdfcopy.hashname = compat_ptr(compat_kdfcopy.hashname); + kdfcopy.otherinfo = compat_ptr(compat_kdfcopy.otherinfo); + kdfcopy.otherinfolen = compat_kdfcopy.otherinfolen; + + return __keyctl_dh_compute(params, buffer, buflen, &kdfcopy); +} diff --git a/security/keys/dh.c b/security/keys/dh.c index 893af4c45038..e603bd912e4c 100644 --- a/security/keys/dh.c +++ b/security/keys/dh.c @@ -11,6 +11,8 @@ #include #include #include +#include +#include #include #include "internal.h" @@ -77,9 +79,146 @@ error: return ret; } -long keyctl_dh_compute(struct keyctl_dh_params __user *params, - char __user *buffer, size_t buflen, - void __user *reserved) +struct kdf_sdesc { + struct shash_desc shash; + char ctx[]; +}; + +static int kdf_alloc(struct kdf_sdesc **sdesc_ret, char *hashname) +{ + struct crypto_shash *tfm; + struct kdf_sdesc *sdesc; + int size; + + /* allocate synchronous hash */ + tfm = crypto_alloc_shash(hashname, 0, 0); + if (IS_ERR(tfm)) { + pr_info("could not allocate digest TFM handle %s\n", hashname); + return PTR_ERR(tfm); + } + + size = sizeof(struct shash_desc) + crypto_shash_descsize(tfm); + sdesc = kmalloc(size, GFP_KERNEL); + if (!sdesc) + return -ENOMEM; + sdesc->shash.tfm = tfm; + sdesc->shash.flags = 0x0; + + *sdesc_ret = sdesc; + + return 0; +} + +static void kdf_dealloc(struct kdf_sdesc *sdesc) +{ + if (!sdesc) + return; + + if (sdesc->shash.tfm) + crypto_free_shash(sdesc->shash.tfm); + + kzfree(sdesc); +} + +/* convert 32 bit integer into its string representation */ +static inline void crypto_kw_cpu_to_be32(u32 val, u8 *buf) +{ + __be32 *a = (__be32 *)buf; + + *a = cpu_to_be32(val); +} + +/* + * Implementation of the KDF in counter mode according to SP800-108 section 5.1 + * as well as SP800-56A section 5.8.1 (Single-step KDF). + * + * SP800-56A: + * The src pointer is defined as Z || other info where Z is the shared secret + * from DH and other info is an arbitrary string (see SP800-56A section + * 5.8.1.2). + */ +static int kdf_ctr(struct kdf_sdesc *sdesc, const u8 *src, unsigned int slen, + u8 *dst, unsigned int dlen) +{ + struct shash_desc *desc = &sdesc->shash; + unsigned int h = crypto_shash_digestsize(desc->tfm); + int err = 0; + u8 *dst_orig = dst; + u32 i = 1; + u8 iteration[sizeof(u32)]; + + while (dlen) { + err = crypto_shash_init(desc); + if (err) + goto err; + + crypto_kw_cpu_to_be32(i, iteration); + err = crypto_shash_update(desc, iteration, sizeof(u32)); + if (err) + goto err; + + if (src && slen) { + err = crypto_shash_update(desc, src, slen); + if (err) + goto err; + } + + if (dlen < h) { + u8 tmpbuffer[h]; + + err = crypto_shash_final(desc, tmpbuffer); + if (err) + goto err; + memcpy(dst, tmpbuffer, dlen); + memzero_explicit(tmpbuffer, h); + return 0; + } else { + err = crypto_shash_final(desc, dst); + if (err) + goto err; + + dlen -= h; + dst += h; + i++; + } + } + + return 0; + +err: + memzero_explicit(dst_orig, dlen); + return err; +} + +static int keyctl_dh_compute_kdf(struct kdf_sdesc *sdesc, + char __user *buffer, size_t buflen, + uint8_t *kbuf, size_t kbuflen) +{ + uint8_t *outbuf = NULL; + int ret; + + outbuf = kmalloc(buflen, GFP_KERNEL); + if (!outbuf) { + ret = -ENOMEM; + goto err; + } + + ret = kdf_ctr(sdesc, kbuf, kbuflen, outbuf, buflen); + if (ret) + goto err; + + ret = buflen; + if (copy_to_user(buffer, outbuf, buflen) != 0) + ret = -EFAULT; + +err: + kzfree(outbuf); + return ret; +} + +long __keyctl_dh_compute(struct keyctl_dh_params __user *params, + char __user *buffer, size_t buflen, + struct keyctl_kdf_params *kdfcopy) { long ret; MPI base, private, prime, result; @@ -88,6 +227,7 @@ long keyctl_dh_compute(struct keyctl_dh_params __user *params, uint8_t *kbuf; ssize_t keylen; size_t resultlen; + struct kdf_sdesc *sdesc = NULL; if (!params || (!buffer && buflen)) { ret = -EINVAL; @@ -98,12 +238,34 @@ long keyctl_dh_compute(struct keyctl_dh_params __user *params, goto out; } - if (reserved) { - ret = -EINVAL; - goto out; + if (kdfcopy) { + char *hashname; + + if (buflen > KEYCTL_KDF_MAX_OUTPUT_LEN || + kdfcopy->otherinfolen > KEYCTL_KDF_MAX_OI_LEN) { + ret = -EMSGSIZE; + goto out; + } + + /* get KDF name string */ + hashname = strndup_user(kdfcopy->hashname, CRYPTO_MAX_ALG_NAME); + if (IS_ERR(hashname)) { + ret = PTR_ERR(hashname); + goto out; + } + + /* allocate KDF from the kernel crypto API */ + ret = kdf_alloc(&sdesc, hashname); + kfree(hashname); + if (ret) + goto out; } - keylen = mpi_from_key(pcopy.prime, buflen, &prime); + /* + * If the caller requests postprocessing with a KDF, allow an + * arbitrary output buffer size since the KDF ensures proper truncation. + */ + keylen = mpi_from_key(pcopy.prime, kdfcopy ? SIZE_MAX : buflen, &prime); if (keylen < 0 || !prime) { /* buflen == 0 may be used to query the required buffer size, * which is the prime key length. @@ -133,12 +295,25 @@ long keyctl_dh_compute(struct keyctl_dh_params __user *params, goto error3; } - kbuf = kmalloc(resultlen, GFP_KERNEL); + /* allocate space for DH shared secret and SP800-56A otherinfo */ + kbuf = kmalloc(kdfcopy ? (resultlen + kdfcopy->otherinfolen) : resultlen, + GFP_KERNEL); if (!kbuf) { ret = -ENOMEM; goto error4; } + /* + * Concatenate SP800-56A otherinfo past DH shared secret -- the + * input to the KDF is (DH shared secret || otherinfo) + */ + if (kdfcopy && kdfcopy->otherinfo && + copy_from_user(kbuf + resultlen, kdfcopy->otherinfo, + kdfcopy->otherinfolen) != 0) { + ret = -EFAULT; + goto error5; + } + ret = do_dh(result, base, private, prime); if (ret) goto error5; @@ -147,12 +322,17 @@ long keyctl_dh_compute(struct keyctl_dh_params __user *params, if (ret != 0) goto error5; - ret = nbytes; - if (copy_to_user(buffer, kbuf, nbytes) != 0) - ret = -EFAULT; + if (kdfcopy) { + ret = keyctl_dh_compute_kdf(sdesc, buffer, buflen, kbuf, + resultlen + kdfcopy->otherinfolen); + } else { + ret = nbytes; + if (copy_to_user(buffer, kbuf, nbytes) != 0) + ret = -EFAULT; + } error5: - kfree(kbuf); + kzfree(kbuf); error4: mpi_free(result); error3: @@ -162,5 +342,21 @@ error2: error1: mpi_free(prime); out: + kdf_dealloc(sdesc); return ret; } + +long keyctl_dh_compute(struct keyctl_dh_params __user *params, + char __user *buffer, size_t buflen, + struct keyctl_kdf_params __user *kdf) +{ + struct keyctl_kdf_params kdfcopy; + + if (!kdf) + return __keyctl_dh_compute(params, buffer, buflen, NULL); + + if (copy_from_user(&kdfcopy, kdf, sizeof(kdfcopy)) != 0) + return -EFAULT; + + return __keyctl_dh_compute(params, buffer, buflen, &kdfcopy); +} diff --git a/security/keys/internal.h b/security/keys/internal.h index 6ce016314897..c0f8682eba69 100644 --- a/security/keys/internal.h +++ b/security/keys/internal.h @@ -18,6 +18,7 @@ #include #include #include +#include struct iovec; @@ -267,15 +268,34 @@ static inline long keyctl_get_persistent(uid_t uid, key_serial_t destring) #ifdef CONFIG_KEY_DH_OPERATIONS extern long keyctl_dh_compute(struct keyctl_dh_params __user *, char __user *, - size_t, void __user *); + size_t, struct keyctl_kdf_params __user *); +extern long __keyctl_dh_compute(struct keyctl_dh_params __user *, char __user *, + size_t, struct keyctl_kdf_params *); +#ifdef CONFIG_KEYS_COMPAT +extern long compat_keyctl_dh_compute(struct keyctl_dh_params __user *params, + char __user *buffer, size_t buflen, + struct compat_keyctl_kdf_params __user *kdf); +#endif +#define KEYCTL_KDF_MAX_OUTPUT_LEN 1024 /* max length of KDF output */ +#define KEYCTL_KDF_MAX_OI_LEN 64 /* max length of otherinfo */ #else static inline long keyctl_dh_compute(struct keyctl_dh_params __user *params, char __user *buffer, size_t buflen, - void __user *reserved) + struct keyctl_kdf_params __user *kdf) +{ + return -EOPNOTSUPP; +} + +#ifdef CONFIG_KEYS_COMPAT +static inline long compat_keyctl_dh_compute( + struct keyctl_dh_params __user *params, + char __user *buffer, size_t buflen, + struct keyctl_kdf_params __user *kdf) { return -EOPNOTSUPP; } #endif +#endif /* * Debugging key validation diff --git a/security/keys/keyctl.c b/security/keys/keyctl.c index 6ee2826a2d06..10fcea154c0f 100644 --- a/security/keys/keyctl.c +++ b/security/keys/keyctl.c @@ -1744,7 +1744,7 @@ SYSCALL_DEFINE5(keyctl, int, option, unsigned long, arg2, unsigned long, arg3, case KEYCTL_DH_COMPUTE: return keyctl_dh_compute((struct keyctl_dh_params __user *) arg2, (char __user *) arg3, (size_t) arg4, - (void __user *) arg5); + (struct keyctl_kdf_params __user *) arg5); case KEYCTL_RESTRICT_KEYRING: return keyctl_restrict_keyring((key_serial_t) arg2, -- cgit v1.2.3 From c3c8dc9f13e2e13013822ee54a529a6fe284f1e1 Mon Sep 17 00:00:00 2001 From: Tetsuo Handa Date: Fri, 24 Mar 2017 20:42:05 +0900 Subject: smack: fix double free in smack_parse_opts_str() smack_parse_opts_str() calls kfree(opts->mnt_opts) when kcalloc() for opts->mnt_opts_flags failed. But it should not have called it because security_free_mnt_opts() will call kfree(opts->mnt_opts). Signed-off-by: Tetsuo Handa Signed-off-by: Casey Schaufler fixes: 3bf2789cad9e6573 ("smack: allow mount opts setting over filesystems with binary mount data") Cc: Vivek Trivedi Cc: Amit Sahrawat Cc: Casey Schaufler --- security/smack/smack_lsm.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'security') diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c index 927e60e622d1..658f5d8c7e76 100644 --- a/security/smack/smack_lsm.c +++ b/security/smack/smack_lsm.c @@ -695,10 +695,8 @@ static int smack_parse_opts_str(char *options, opts->mnt_opts_flags = kcalloc(NUM_SMK_MNT_OPTS, sizeof(int), GFP_KERNEL); - if (!opts->mnt_opts_flags) { - kfree(opts->mnt_opts); + if (!opts->mnt_opts_flags) goto out_err; - } if (fsdefault) { opts->mnt_opts[num_mnt_opts] = fsdefault; -- cgit v1.2.3 From af96f0d6394a0af59c4dd71d6bcd3b1ddfba5196 Mon Sep 17 00:00:00 2001 From: Tetsuo Handa Date: Mon, 14 Nov 2016 20:12:56 +0900 Subject: Smack: Use GFP_KERNEL for smk_netlbl_mls(). Since all callers of smk_netlbl_mls() are GFP_KERNEL context (smk_set_cipso() calls memdup_user_nul(), init_smk_fs() calls __kernfs_new_node(), smk_import_entry() calls kzalloc(GFP_KERNEL)), it is safe to use GFP_KERNEL from netlbl_catmap_setbit(). Signed-off-by: Tetsuo Handa Signed-off-by: Casey Schaufler --- security/smack/smack_access.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'security') diff --git a/security/smack/smack_access.c b/security/smack/smack_access.c index 356e3764cad9..a4b2e6b94abd 100644 --- a/security/smack/smack_access.c +++ b/security/smack/smack_access.c @@ -504,7 +504,7 @@ int smk_netlbl_mls(int level, char *catset, struct netlbl_lsm_secattr *sap, if ((m & *cp) == 0) continue; rc = netlbl_catmap_setbit(&sap->attr.mls.cat, - cat, GFP_ATOMIC); + cat, GFP_KERNEL); if (rc < 0) { netlbl_catmap_free(sap->attr.mls.cat); return rc; -- cgit v1.2.3 From b9c42ac76ea13ab6d07681ff3079b3a242333764 Mon Sep 17 00:00:00 2001 From: kbuild test robot Date: Thu, 6 Apr 2017 06:55:19 -0700 Subject: apparmor: fix boolreturn.cocci warnings security/apparmor/lib.c:132:9-10: WARNING: return of 0/1 in function 'aa_policy_init' with return type bool Return statements in functions returning bool should use true/false instead of 1/0. Generated by: scripts/coccinelle/misc/boolreturn.cocci Signed-off-by: Fengguang Wu Signed-off-by: John Johansen Signed-off-by: James Morris --- security/apparmor/lib.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'security') diff --git a/security/apparmor/lib.c b/security/apparmor/lib.c index 66475bda6f72..32cafc12593e 100644 --- a/security/apparmor/lib.c +++ b/security/apparmor/lib.c @@ -180,13 +180,13 @@ bool aa_policy_init(struct aa_policy *policy, const char *prefix, } else policy->hname = kstrdup(name, gfp); if (!policy->hname) - return 0; + return false; /* base.name is a substring of fqname */ policy->name = basename(policy->hname); INIT_LIST_HEAD(&policy->list); INIT_LIST_HEAD(&policy->profiles); - return 1; + return true; } /** -- cgit v1.2.3 From eea7a05f1937b585cf53c6d53a4dd88fcb398eb8 Mon Sep 17 00:00:00 2001 From: Valentin Rothberg Date: Thu, 6 Apr 2017 06:55:20 -0700 Subject: security/apparmor/lsm.c: set debug messages Add the _APPARMOR substring to reference the intended Kconfig option. Signed-off-by: Valentin Rothberg Signed-off-by: John Johansen Signed-off-by: James Morris --- security/apparmor/lsm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'security') diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c index e287b691a30e..4aa944794c7b 100644 --- a/security/apparmor/lsm.c +++ b/security/apparmor/lsm.c @@ -681,7 +681,7 @@ module_param_named(hash_policy, aa_g_hash_policy, aabool, S_IRUSR | S_IWUSR); #endif /* Debug mode */ -bool aa_g_debug = IS_ENABLED(CONFIG_SECURITY_DEBUG_MESSAGES); +bool aa_g_debug = IS_ENABLED(CONFIG_SECURITY_APPARMOR_DEBUG_MESSAGES); module_param_named(debug, aa_g_debug, aabool, S_IRUSR | S_IWUSR); /* Audit mode */ -- cgit v1.2.3 From 9814448da7a84dd50b69e4ada2d7d1c042493daf Mon Sep 17 00:00:00 2001 From: Nicolas Iooss Date: Thu, 6 Apr 2017 06:55:21 -0700 Subject: apparmor: use SHASH_DESC_ON_STACK When building the kernel with clang, the compiler fails to build security/apparmor/crypto.c with the following error: security/apparmor/crypto.c:36:8: error: fields must have a constant size: 'variable length array in structure' extension will never be supported char ctx[crypto_shash_descsize(apparmor_tfm)]; ^ Since commit a0a77af14117 ("crypto: LLVMLinux: Add macro to remove use of VLAIS in crypto code"), include/crypto/hash.h defines SHASH_DESC_ON_STACK to work around this issue. Use it in aa_calc_hash() and aa_calc_profile_hash(). Signed-off-by: Nicolas Iooss Signed-off-by: John Johansen Signed-off-by: James Morris --- security/apparmor/crypto.c | 32 +++++++++++++------------------- 1 file changed, 13 insertions(+), 19 deletions(-) (limited to 'security') diff --git a/security/apparmor/crypto.c b/security/apparmor/crypto.c index de8dc78b6144..136f2a047836 100644 --- a/security/apparmor/crypto.c +++ b/security/apparmor/crypto.c @@ -31,10 +31,7 @@ unsigned int aa_hash_size(void) char *aa_calc_hash(void *data, size_t len) { - struct { - struct shash_desc shash; - char ctx[crypto_shash_descsize(apparmor_tfm)]; - } desc; + SHASH_DESC_ON_STACK(desc, apparmor_tfm); char *hash = NULL; int error = -ENOMEM; @@ -45,16 +42,16 @@ char *aa_calc_hash(void *data, size_t len) if (!hash) goto fail; - desc.shash.tfm = apparmor_tfm; - desc.shash.flags = 0; + desc->tfm = apparmor_tfm; + desc->flags = 0; - error = crypto_shash_init(&desc.shash); + error = crypto_shash_init(desc); if (error) goto fail; - error = crypto_shash_update(&desc.shash, (u8 *) data, len); + error = crypto_shash_update(desc, (u8 *) data, len); if (error) goto fail; - error = crypto_shash_final(&desc.shash, hash); + error = crypto_shash_final(desc, hash); if (error) goto fail; @@ -69,10 +66,7 @@ fail: int aa_calc_profile_hash(struct aa_profile *profile, u32 version, void *start, size_t len) { - struct { - struct shash_desc shash; - char ctx[crypto_shash_descsize(apparmor_tfm)]; - } desc; + SHASH_DESC_ON_STACK(desc, apparmor_tfm); int error = -ENOMEM; __le32 le32_version = cpu_to_le32(version); @@ -86,19 +80,19 @@ int aa_calc_profile_hash(struct aa_profile *profile, u32 version, void *start, if (!profile->hash) goto fail; - desc.shash.tfm = apparmor_tfm; - desc.shash.flags = 0; + desc->tfm = apparmor_tfm; + desc->flags = 0; - error = crypto_shash_init(&desc.shash); + error = crypto_shash_init(desc); if (error) goto fail; - error = crypto_shash_update(&desc.shash, (u8 *) &le32_version, 4); + error = crypto_shash_update(desc, (u8 *) &le32_version, 4); if (error) goto fail; - error = crypto_shash_update(&desc.shash, (u8 *) start, len); + error = crypto_shash_update(desc, (u8 *) start, len); if (error) goto fail; - error = crypto_shash_final(&desc.shash, profile->hash); + error = crypto_shash_final(desc, profile->hash); if (error) goto fail; -- cgit v1.2.3 From b9b144bcafbdd53f68e227968009327b76db08a4 Mon Sep 17 00:00:00 2001 From: John Johansen Date: Thu, 6 Apr 2017 06:55:22 -0700 Subject: apparmor: fix invalid reference to index variable of iterator line 836 Once the loop on lines 836-853 is complete and exits normally, ent is a pointer to the dummy list head value. The derefernces accessible from eg the goto fail on line 860 or the various goto fail_lock's afterwards thus seem incorrect. Reported-by: Julia Lawall Signed-off-by: John Johansen Signed-off-by: James Morris --- security/apparmor/policy.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'security') diff --git a/security/apparmor/policy.c b/security/apparmor/policy.c index def1fbd6bdfd..cf9d670dca94 100644 --- a/security/apparmor/policy.c +++ b/security/apparmor/policy.c @@ -876,9 +876,11 @@ ssize_t aa_replace_profiles(struct aa_ns *view, struct aa_profile *profile, if (ns_name) { ns = aa_prepare_ns(view, ns_name); if (IS_ERR(ns)) { + op = OP_PROF_LOAD; info = "failed to prepare namespace"; error = PTR_ERR(ns); ns = NULL; + ent = NULL; goto fail; } } else @@ -1013,7 +1015,7 @@ fail_lock: /* audit cause of failure */ op = (!ent->old) ? OP_PROF_LOAD : OP_PROF_REPL; fail: - audit_policy(profile, op, ns_name, ent->new->base.hname, + audit_policy(profile, op, ns_name, ent ? ent->new->base.hname : NULL, info, error); /* audit status that rest of profiles in the atomic set failed too */ info = "valid profile in failed atomic policy load"; @@ -1023,7 +1025,7 @@ fail: /* skip entry that caused failure */ continue; } - op = (!ent->old) ? OP_PROF_LOAD : OP_PROF_REPL; + op = (!tmp->old) ? OP_PROF_LOAD : OP_PROF_REPL; audit_policy(profile, op, ns_name, tmp->new->base.hname, info, error); } -- cgit v1.2.3 From 545de8fe0f1b3b97d6a29a78ccdc3403a8296710 Mon Sep 17 00:00:00 2001 From: John Johansen Date: Thu, 6 Apr 2017 06:55:23 -0700 Subject: apparmor: fix parameters so that the permission test is bypassed at boot Boot parameters are written before apparmor is ready to answer whether the user is policy_view_capable(). Setting the parameters at boot results in an oops and failure to boot. Setting the parameters at boot is obviously allowed so skip the permission check when apparmor is not initialized. While we are at it move the more complicated check to last. Signed-off-by: John Johansen Signed-off-by: James Morris --- security/apparmor/include/lib.h | 2 +- security/apparmor/lsm.c | 47 +++++++++++++++++++---------------------- 2 files changed, 23 insertions(+), 26 deletions(-) (limited to 'security') diff --git a/security/apparmor/include/lib.h b/security/apparmor/include/lib.h index 65ff492a9807..0291ff3902f9 100644 --- a/security/apparmor/include/lib.h +++ b/security/apparmor/include/lib.h @@ -57,7 +57,7 @@ pr_err_ratelimited("AppArmor: " fmt, ##args) /* Flag indicating whether initialization completed */ -extern int apparmor_initialized __initdata; +extern int apparmor_initialized; /* fn's in lib */ char *aa_split_fqname(char *args, char **ns_name); diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c index 4aa944794c7b..35444c8e9064 100644 --- a/security/apparmor/lsm.c +++ b/security/apparmor/lsm.c @@ -39,7 +39,7 @@ #include "include/procattr.h" /* Flag indicating whether initialization completed */ -int apparmor_initialized __initdata; +int apparmor_initialized; DEFINE_PER_CPU(struct aa_buffers, aa_buffers); @@ -738,78 +738,77 @@ __setup("apparmor=", apparmor_enabled_setup); /* set global flag turning off the ability to load policy */ static int param_set_aalockpolicy(const char *val, const struct kernel_param *kp) { - if (!policy_admin_capable(NULL)) + if (!apparmor_enabled) + return -EINVAL; + if (apparmor_initialized && !policy_admin_capable(NULL)) return -EPERM; return param_set_bool(val, kp); } static int param_get_aalockpolicy(char *buffer, const struct kernel_param *kp) { - if (!policy_view_capable(NULL)) - return -EPERM; if (!apparmor_enabled) return -EINVAL; + if (apparmor_initialized && !policy_view_capable(NULL)) + return -EPERM; return param_get_bool(buffer, kp); } static int param_set_aabool(const char *val, const struct kernel_param *kp) { - if (!policy_admin_capable(NULL)) - return -EPERM; if (!apparmor_enabled) return -EINVAL; + if (apparmor_initialized && !policy_admin_capable(NULL)) + return -EPERM; return param_set_bool(val, kp); } static int param_get_aabool(char *buffer, const struct kernel_param *kp) { - if (!policy_view_capable(NULL)) - return -EPERM; if (!apparmor_enabled) return -EINVAL; + if (apparmor_initialized && !policy_view_capable(NULL)) + return -EPERM; return param_get_bool(buffer, kp); } static int param_set_aauint(const char *val, const struct kernel_param *kp) { - if (!policy_admin_capable(NULL)) - return -EPERM; if (!apparmor_enabled) return -EINVAL; + if (apparmor_initialized && !policy_admin_capable(NULL)) + return -EPERM; return param_set_uint(val, kp); } static int param_get_aauint(char *buffer, const struct kernel_param *kp) { - if (!policy_view_capable(NULL)) - return -EPERM; if (!apparmor_enabled) return -EINVAL; + if (apparmor_initialized && !policy_view_capable(NULL)) + return -EPERM; return param_get_uint(buffer, kp); } static int param_get_audit(char *buffer, struct kernel_param *kp) { - if (!policy_view_capable(NULL)) - return -EPERM; - if (!apparmor_enabled) return -EINVAL; - + if (apparmor_initialized && !policy_view_capable(NULL)) + return -EPERM; return sprintf(buffer, "%s", audit_mode_names[aa_g_audit]); } static int param_set_audit(const char *val, struct kernel_param *kp) { int i; - if (!policy_admin_capable(NULL)) - return -EPERM; if (!apparmor_enabled) return -EINVAL; - if (!val) return -EINVAL; + if (apparmor_initialized && !policy_admin_capable(NULL)) + return -EPERM; for (i = 0; i < AUDIT_MAX_INDEX; i++) { if (strcmp(val, audit_mode_names[i]) == 0) { @@ -823,11 +822,10 @@ static int param_set_audit(const char *val, struct kernel_param *kp) static int param_get_mode(char *buffer, struct kernel_param *kp) { - if (!policy_view_capable(NULL)) - return -EPERM; - if (!apparmor_enabled) return -EINVAL; + if (apparmor_initialized && !policy_view_capable(NULL)) + return -EPERM; return sprintf(buffer, "%s", aa_profile_mode_names[aa_g_profile_mode]); } @@ -835,14 +833,13 @@ static int param_get_mode(char *buffer, struct kernel_param *kp) static int param_set_mode(const char *val, struct kernel_param *kp) { int i; - if (!policy_admin_capable(NULL)) - return -EPERM; if (!apparmor_enabled) return -EINVAL; - if (!val) return -EINVAL; + if (apparmor_initialized && !policy_admin_capable(NULL)) + return -EPERM; for (i = 0; i < APPARMOR_MODE_NAMES_MAX_INDEX; i++) { if (strcmp(val, aa_profile_mode_names[i]) == 0) { -- cgit v1.2.3 From 622f6e3265707ebf02ba776ac6e68003bcc31213 Mon Sep 17 00:00:00 2001 From: John Johansen Date: Thu, 6 Apr 2017 06:55:24 -0700 Subject: apparmor: Make path_max parameter readonly The path_max parameter determines the max size of buffers allocated but it should not be setable at run time. If can be used to cause an oops root@ubuntu:~# echo 16777216 > /sys/module/apparmor/parameters/path_max root@ubuntu:~# cat /sys/module/apparmor/parameters/path_max Killed [ 122.141911] BUG: unable to handle kernel paging request at ffff880080945fff [ 122.143497] IP: [] d_absolute_path+0x44/0xa0 [ 122.144742] PGD 220c067 PUD 0 [ 122.145453] Oops: 0002 [#1] SMP [ 122.146204] Modules linked in: vmw_vsock_vmci_transport vsock ppdev vmw_balloon snd_ens1371 btusb snd_ac97_codec gameport snd_rawmidi btrtl snd_seq_device ac97_bus btbcm btintel snd_pcm input_leds bluetooth snd_timer snd joydev soundcore serio_raw coretemp shpchp nfit parport_pc i2c_piix4 8250_fintek vmw_vmci parport mac_hid ib_iser rdma_cm iw_cm ib_cm ib_sa ib_mad ib_core ib_addr iscsi_tcp libiscsi_tcp libiscsi scsi_transport_iscsi autofs4 btrfs raid10 raid456 async_raid6_recov async_memcpy async_pq async_xor async_tx xor raid6_pq libcrc32c raid1 raid0 multipath linear hid_generic usbhid hid crct10dif_pclmul crc32_pclmul ghash_clmulni_intel aesni_intel aes_x86_64 lrw gf128mul glue_helper ablk_helper cryptd vmwgfx psmouse mptspi ttm mptscsih drm_kms_helper mptbase syscopyarea scsi_transport_spi sysfillrect [ 122.163365] ahci sysimgblt e1000 fb_sys_fops libahci drm pata_acpi fjes [ 122.164747] CPU: 3 PID: 1501 Comm: bash Not tainted 4.4.0-59-generic #80-Ubuntu [ 122.166250] Hardware name: VMware, Inc. VMware Virtual Platform/440BX Desktop Reference Platform, BIOS 6.00 07/02/2015 [ 122.168611] task: ffff88003496aa00 ti: ffff880076474000 task.ti: ffff880076474000 [ 122.170018] RIP: 0010:[] [] d_absolute_path+0x44/0xa0 [ 122.171525] RSP: 0018:ffff880076477b90 EFLAGS: 00010206 [ 122.172462] RAX: ffff880080945fff RBX: 0000000000000000 RCX: 0000000001000000 [ 122.173709] RDX: 0000000000ffffff RSI: ffff880080946000 RDI: ffff8800348a1010 [ 122.174978] RBP: ffff880076477bb8 R08: ffff880076477c80 R09: 0000000000000000 [ 122.176227] R10: 00007ffffffff000 R11: ffff88007f946000 R12: ffff88007f946000 [ 122.177496] R13: ffff880076477c80 R14: ffff8800348a1010 R15: ffff8800348a2400 [ 122.178745] FS: 00007fd459eb4700(0000) GS:ffff88007b6c0000(0000) knlGS:0000000000000000 [ 122.180176] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 [ 122.181186] CR2: ffff880080945fff CR3: 0000000073422000 CR4: 00000000001406e0 [ 122.182469] Stack: [ 122.182843] 00ffffff00000001 ffff880080946000 0000000000000000 0000000000000000 [ 122.184409] 00000000570f789c ffff880076477c30 ffffffff81385671 ffff88007a2e7a58 [ 122.185810] 0000000000000000 ffff880076477c88 01000000008a1000 0000000000000000 [ 122.187231] Call Trace: [ 122.187680] [] aa_path_name+0x81/0x370 [ 122.188637] [] profile_transition+0xbd/0xb80 [ 122.190181] [] ? zone_statistics+0x7c/0xa0 [ 122.191674] [] apparmor_bprm_set_creds+0x9b0/0xac0 [ 122.193288] [] ? ext4_xattr_get+0x81/0x220 [ 122.194793] [] ? ext4_xattr_security_get+0x1c/0x30 [ 122.196392] [] ? get_vfs_caps_from_disk+0x69/0x110 [ 122.198004] [] ? mnt_may_suid+0x3f/0x50 [ 122.199737] [] ? cap_bprm_set_creds+0xa3/0x600 [ 122.201377] [] security_bprm_set_creds+0x33/0x50 [ 122.203024] [] prepare_binprm+0x85/0x190 [ 122.204515] [] do_execveat_common.isra.33+0x485/0x710 [ 122.206200] [] SyS_execve+0x3a/0x50 [ 122.207615] [] stub_execve+0x5/0x5 [ 122.208978] [] ? entry_SYSCALL_64_fastpath+0x16/0x71 [ 122.210615] Code: f8 31 c0 48 63 c2 83 ea 01 48 c7 45 e8 00 00 00 00 48 01 c6 85 d2 48 c7 45 f0 00 00 00 00 48 89 75 e0 89 55 dc 78 0c 48 8d 46 ff 46 ff 00 48 89 45 e0 48 8d 55 e0 48 8d 4d dc 48 8d 75 e8 e8 [ 122.217320] RIP [] d_absolute_path+0x44/0xa0 [ 122.218860] RSP [ 122.219919] CR2: ffff880080945fff [ 122.220936] ---[ end trace 506cdbd85eb6c55e ]--- Reported-by: Tetsuo Handa Signed-off-by: John Johansen Signed-off-by: James Morris --- security/apparmor/lsm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'security') diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c index 35444c8e9064..8f3c0f7aca5a 100644 --- a/security/apparmor/lsm.c +++ b/security/apparmor/lsm.c @@ -710,7 +710,7 @@ module_param_named(logsyscall, aa_g_logsyscall, aabool, S_IRUSR | S_IWUSR); /* Maximum pathname length before accesses will start getting rejected */ unsigned int aa_g_path_max = 2 * PATH_MAX; -module_param_named(path_max, aa_g_path_max, aauint, S_IRUSR | S_IWUSR); +module_param_named(path_max, aa_g_path_max, aauint, S_IRUSR); /* Determines how paranoid loading of policy is and how much verification * on the loaded policy is done. -- cgit v1.2.3 From 4cd4ca7cc848bedc70b5d0acac9d1ae33d73513a Mon Sep 17 00:00:00 2001 From: Stephan Müller Date: Tue, 11 Apr 2017 13:07:07 +0200 Subject: keys: select CONFIG_CRYPTO when selecting DH / KDF Select CONFIG_CRYPTO in addition to CONFIG_HASH to ensure that also CONFIG_HASH2 is selected. Both are needed for the shash cipher support required for the KDF operation. Signed-off-by: Stephan Mueller Signed-off-by: David Howells --- security/keys/Kconfig | 1 + 1 file changed, 1 insertion(+) (limited to 'security') diff --git a/security/keys/Kconfig b/security/keys/Kconfig index 4ac1b83a23f8..6fd95f76bfae 100644 --- a/security/keys/Kconfig +++ b/security/keys/Kconfig @@ -90,6 +90,7 @@ config KEY_DH_OPERATIONS bool "Diffie-Hellman operations on retained keys" depends on KEYS select MPILIB + select CRYPTO select CRYPTO_HASH help This option provides support for calculating Diffie-Hellman -- cgit v1.2.3