From b21507e272627c434e8dd74e8d51fd8245281b59 Mon Sep 17 00:00:00 2001 From: Stephen Smalley Date: Mon, 9 Jan 2017 10:07:31 -0500 Subject: proc,security: move restriction on writing /proc/pid/attr nodes to proc Processes can only alter their own security attributes via /proc/pid/attr nodes. This is presently enforced by each individual security module and is also imposed by the Linux credentials implementation, which only allows a task to alter its own credentials. Move the check enforcing this restriction from the individual security modules to proc_pid_attr_write() before calling the security hook, and drop the unnecessary task argument to the security hook since it can only ever be the current task. Signed-off-by: Stephen Smalley Acked-by: Casey Schaufler Acked-by: John Johansen Signed-off-by: Paul Moore --- include/linux/lsm_hooks.h | 3 +-- include/linux/security.h | 4 ++-- 2 files changed, 3 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/include/linux/lsm_hooks.h b/include/linux/lsm_hooks.h index 558adfa5c8a8..0dde95900196 100644 --- a/include/linux/lsm_hooks.h +++ b/include/linux/lsm_hooks.h @@ -1547,8 +1547,7 @@ union security_list_options { void (*d_instantiate)(struct dentry *dentry, struct inode *inode); int (*getprocattr)(struct task_struct *p, char *name, char **value); - int (*setprocattr)(struct task_struct *p, char *name, void *value, - size_t size); + int (*setprocattr)(const char *name, void *value, size_t size); int (*ismaclabel)(const char *name); int (*secid_to_secctx)(u32 secid, char **secdata, u32 *seclen); int (*secctx_to_secid)(const char *secdata, u32 seclen, u32 *secid); diff --git a/include/linux/security.h b/include/linux/security.h index c2125e9093e8..f4ebac117fa6 100644 --- a/include/linux/security.h +++ b/include/linux/security.h @@ -361,7 +361,7 @@ int security_sem_semop(struct sem_array *sma, struct sembuf *sops, unsigned nsops, int alter); void security_d_instantiate(struct dentry *dentry, struct inode *inode); int security_getprocattr(struct task_struct *p, char *name, char **value); -int security_setprocattr(struct task_struct *p, char *name, void *value, size_t size); +int security_setprocattr(const char *name, void *value, size_t size); int security_netlink_send(struct sock *sk, struct sk_buff *skb); int security_ismaclabel(const char *name); int security_secid_to_secctx(u32 secid, char **secdata, u32 *seclen); @@ -1106,7 +1106,7 @@ static inline int security_getprocattr(struct task_struct *p, char *name, char * return -EINVAL; } -static inline int security_setprocattr(struct task_struct *p, char *name, void *value, size_t size) +static inline int security_setprocattr(char *name, void *value, size_t size) { return -EINVAL; } -- cgit v1.2.3 From b8aa8453918ebfd93d78de56c2afd4b735e02e27 Mon Sep 17 00:00:00 2001 From: Mickaël Salaün Date: Thu, 22 Dec 2016 00:32:25 +0100 Subject: security: Fix inode_getattr documentation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace arguments @mnt and @dentry with @path. Signed-off-by: Mickaël Salaün Acked-by: Serge Hallyn Signed-off-by: James Morris --- include/linux/lsm_hooks.h | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'include') diff --git a/include/linux/lsm_hooks.h b/include/linux/lsm_hooks.h index 558adfa5c8a8..9cf50ad2fe20 100644 --- a/include/linux/lsm_hooks.h +++ b/include/linux/lsm_hooks.h @@ -352,8 +352,7 @@ * Return 0 if permission is granted. * @inode_getattr: * Check permission before obtaining file attributes. - * @mnt is the vfsmount where the dentry was looked up - * @dentry contains the dentry structure for the file. + * @path contains the path structure for the file. * Return 0 if permission is granted. * @inode_setxattr: * Check permission before setting the extended attributes -- cgit v1.2.3 From 3a2f5a59a695a73e0cde9a61e0feae5fa730e936 Mon Sep 17 00:00:00 2001 From: Stephen Smalley Date: Tue, 10 Jan 2017 12:28:32 -0500 Subject: security,selinux,smack: kill security_task_wait hook As reported by yangshukui, a permission denial from security_task_wait() can lead to a soft lockup in zap_pid_ns_processes() since it only expects sys_wait4() to return 0 or -ECHILD. Further, security_task_wait() can in general lead to zombies; in the absence of some way to automatically reparent a child process upon a denial, the hook is not useful. Remove the security hook and its implementations in SELinux and Smack. Smack already removed its check from its hook. Reported-by: yangshukui Signed-off-by: Stephen Smalley Acked-by: Casey Schaufler Acked-by: Oleg Nesterov Signed-off-by: Paul Moore --- include/linux/lsm_hooks.h | 7 ------- include/linux/security.h | 6 ------ kernel/exit.c | 19 ++----------------- security/security.c | 6 ------ security/selinux/hooks.c | 7 ------- security/smack/smack_lsm.c | 20 -------------------- 6 files changed, 2 insertions(+), 63 deletions(-) (limited to 'include') diff --git a/include/linux/lsm_hooks.h b/include/linux/lsm_hooks.h index 0dde95900196..6fe7a5cb0be1 100644 --- a/include/linux/lsm_hooks.h +++ b/include/linux/lsm_hooks.h @@ -666,11 +666,6 @@ * @sig contains the signal value. * @secid contains the sid of the process where the signal originated * Return 0 if permission is granted. - * @task_wait: - * Check permission before allowing a process to reap a child process @p - * and collect its status information. - * @p contains the task_struct for process. - * Return 0 if permission is granted. * @task_prctl: * Check permission before performing a process control operation on the * current process. @@ -1507,7 +1502,6 @@ union security_list_options { int (*task_movememory)(struct task_struct *p); int (*task_kill)(struct task_struct *p, struct siginfo *info, int sig, u32 secid); - int (*task_wait)(struct task_struct *p); int (*task_prctl)(int option, unsigned long arg2, unsigned long arg3, unsigned long arg4, unsigned long arg5); void (*task_to_inode)(struct task_struct *p, struct inode *inode); @@ -1767,7 +1761,6 @@ struct security_hook_heads { struct list_head task_getscheduler; struct list_head task_movememory; struct list_head task_kill; - struct list_head task_wait; struct list_head task_prctl; struct list_head task_to_inode; struct list_head ipc_permission; diff --git a/include/linux/security.h b/include/linux/security.h index f4ebac117fa6..d3868f2ebada 100644 --- a/include/linux/security.h +++ b/include/linux/security.h @@ -332,7 +332,6 @@ int security_task_getscheduler(struct task_struct *p); int security_task_movememory(struct task_struct *p); int security_task_kill(struct task_struct *p, struct siginfo *info, int sig, u32 secid); -int security_task_wait(struct task_struct *p); int security_task_prctl(int option, unsigned long arg2, unsigned long arg3, unsigned long arg4, unsigned long arg5); void security_task_to_inode(struct task_struct *p, struct inode *inode); @@ -980,11 +979,6 @@ static inline int security_task_kill(struct task_struct *p, return 0; } -static inline int security_task_wait(struct task_struct *p) -{ - return 0; -} - static inline int security_task_prctl(int option, unsigned long arg2, unsigned long arg3, unsigned long arg4, diff --git a/kernel/exit.c b/kernel/exit.c index 8f14b866f9f6..60f245190571 100644 --- a/kernel/exit.c +++ b/kernel/exit.c @@ -14,7 +14,6 @@ #include #include #include -#include #include #include #include @@ -1360,7 +1359,7 @@ static int wait_task_continued(struct wait_opts *wo, struct task_struct *p) * Returns nonzero for a final return, when we have unlocked tasklist_lock. * Returns zero if the search for a child should continue; * then ->notask_error is 0 if @p is an eligible child, - * or another error from security_task_wait(), or still -ECHILD. + * or still -ECHILD. */ static int wait_consider_task(struct wait_opts *wo, int ptrace, struct task_struct *p) @@ -1380,20 +1379,6 @@ static int wait_consider_task(struct wait_opts *wo, int ptrace, if (!ret) return ret; - ret = security_task_wait(p); - if (unlikely(ret < 0)) { - /* - * If we have not yet seen any eligible child, - * then let this error code replace -ECHILD. - * A permission error will give the user a clue - * to look for security policy problems, rather - * than for mysterious wait bugs. - */ - if (wo->notask_error) - wo->notask_error = ret; - return 0; - } - if (unlikely(exit_state == EXIT_TRACE)) { /* * ptrace == 0 means we are the natural parent. In this case @@ -1486,7 +1471,7 @@ static int wait_consider_task(struct wait_opts *wo, int ptrace, * Returns nonzero for a final return, when we have unlocked tasklist_lock. * Returns zero if the search for a child should continue; then * ->notask_error is 0 if there were any eligible children, - * or another error from security_task_wait(), or still -ECHILD. + * or still -ECHILD. */ static int do_wait_thread(struct wait_opts *wo, struct task_struct *tsk) { diff --git a/security/security.c b/security/security.c index 32052f5c76b2..8c9fee59e60a 100644 --- a/security/security.c +++ b/security/security.c @@ -1025,11 +1025,6 @@ int security_task_kill(struct task_struct *p, struct siginfo *info, return call_int_hook(task_kill, 0, p, info, sig, secid); } -int security_task_wait(struct task_struct *p) -{ - return call_int_hook(task_wait, 0, p); -} - int security_task_prctl(int option, unsigned long arg2, unsigned long arg3, unsigned long arg4, unsigned long arg5) { @@ -1769,7 +1764,6 @@ struct security_hook_heads security_hook_heads = { .task_movememory = LIST_HEAD_INIT(security_hook_heads.task_movememory), .task_kill = LIST_HEAD_INIT(security_hook_heads.task_kill), - .task_wait = LIST_HEAD_INIT(security_hook_heads.task_wait), .task_prctl = LIST_HEAD_INIT(security_hook_heads.task_prctl), .task_to_inode = LIST_HEAD_INIT(security_hook_heads.task_to_inode), diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index 55ad878f1146..a5398fea0966 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -3963,12 +3963,6 @@ static int selinux_task_kill(struct task_struct *p, struct siginfo *info, return avc_has_perm(secid, task_sid(p), SECCLASS_PROCESS, perm, NULL); } -static int selinux_task_wait(struct task_struct *p) -{ - return avc_has_perm(task_sid(p), current_sid(), SECCLASS_PROCESS, - PROCESS__SIGCHLD, NULL); -} - static void selinux_task_to_inode(struct task_struct *p, struct inode *inode) { @@ -6211,7 +6205,6 @@ static struct security_hook_list selinux_hooks[] = { LSM_HOOK_INIT(task_getscheduler, selinux_task_getscheduler), LSM_HOOK_INIT(task_movememory, selinux_task_movememory), LSM_HOOK_INIT(task_kill, selinux_task_kill), - LSM_HOOK_INIT(task_wait, selinux_task_wait), LSM_HOOK_INIT(task_to_inode, selinux_task_to_inode), LSM_HOOK_INIT(ipc_permission, selinux_ipc_permission), diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c index 8da4a6b9ca4d..2166373ea5a4 100644 --- a/security/smack/smack_lsm.c +++ b/security/smack/smack_lsm.c @@ -2271,25 +2271,6 @@ static int smack_task_kill(struct task_struct *p, struct siginfo *info, return rc; } -/** - * smack_task_wait - Smack access check for waiting - * @p: task to wait for - * - * Returns 0 - */ -static int smack_task_wait(struct task_struct *p) -{ - /* - * Allow the operation to succeed. - * Zombies are bad. - * In userless environments (e.g. phones) programs - * get marked with SMACK64EXEC and even if the parent - * and child shouldn't be talking the parent still - * may expect to know when the child exits. - */ - return 0; -} - /** * smack_task_to_inode - copy task smack into the inode blob * @p: task to copy from @@ -4658,7 +4639,6 @@ static struct security_hook_list smack_hooks[] = { LSM_HOOK_INIT(task_getscheduler, smack_task_getscheduler), LSM_HOOK_INIT(task_movememory, smack_task_movememory), LSM_HOOK_INIT(task_kill, smack_task_kill), - LSM_HOOK_INIT(task_wait, smack_task_wait), LSM_HOOK_INIT(task_to_inode, smack_task_to_inode), LSM_HOOK_INIT(ipc_permission, smack_ipc_permission), -- cgit v1.2.3 From d69dece5f5b6bc7a5e39d2b6136ddc69469331fe Mon Sep 17 00:00:00 2001 From: Casey Schaufler Date: Wed, 18 Jan 2017 17:09:05 -0800 Subject: LSM: Add /sys/kernel/security/lsm I am still tired of having to find indirect ways to determine what security modules are active on a system. I have added /sys/kernel/security/lsm, which contains a comma separated list of the active security modules. No more groping around in /proc/filesystems or other clever hacks. Unchanged from previous versions except for being updated to the latest security next branch. Signed-off-by: Casey Schaufler Acked-by: John Johansen Acked-by: Paul Moore Acked-by: Kees Cook Signed-off-by: James Morris --- Documentation/security/LSM.txt | 7 +++++++ include/linux/lsm_hooks.h | 12 ++++-------- security/apparmor/lsm.c | 3 ++- security/commoncap.c | 3 ++- security/inode.c | 26 ++++++++++++++++++++++++-- security/loadpin/loadpin.c | 2 +- security/security.c | 38 ++++++++++++++++++++++++++++++++++++++ security/selinux/hooks.c | 2 +- security/smack/smack_lsm.c | 2 +- security/tomoyo/tomoyo.c | 2 +- security/yama/yama_lsm.c | 2 +- 11 files changed, 82 insertions(+), 17 deletions(-) (limited to 'include') diff --git a/Documentation/security/LSM.txt b/Documentation/security/LSM.txt index 3db7e671c440..c2683f28ed36 100644 --- a/Documentation/security/LSM.txt +++ b/Documentation/security/LSM.txt @@ -22,6 +22,13 @@ system, building their checks on top of the defined capability hooks. For more details on capabilities, see capabilities(7) in the Linux man-pages project. +A list of the active security modules can be found by reading +/sys/kernel/security/lsm. This is a comma separated list, and +will always include the capability module. The list reflects the +order in which checks are made. The capability module will always +be first, followed by any "minor" modules (e.g. Yama) and then +the one "major" module (e.g. SELinux) if there is one configured. + Based on https://lkml.org/lkml/2007/10/26/215, a new LSM is accepted into the kernel when its intent (a description of what it tries to protect against and in what cases one would expect to diff --git a/include/linux/lsm_hooks.h b/include/linux/lsm_hooks.h index 9cf50ad2fe20..0f3c309065d7 100644 --- a/include/linux/lsm_hooks.h +++ b/include/linux/lsm_hooks.h @@ -1875,6 +1875,7 @@ struct security_hook_list { struct list_head list; struct list_head *head; union security_list_options hook; + char *lsm; }; /* @@ -1887,15 +1888,10 @@ struct security_hook_list { { .head = &security_hook_heads.HEAD, .hook = { .HEAD = HOOK } } extern struct security_hook_heads security_hook_heads; +extern char *lsm_names; -static inline void security_add_hooks(struct security_hook_list *hooks, - int count) -{ - int i; - - for (i = 0; i < count; i++) - list_add_tail_rcu(&hooks[i].list, hooks[i].head); -} +extern void security_add_hooks(struct security_hook_list *hooks, int count, + char *lsm); #ifdef CONFIG_SECURITY_SELINUX_DISABLE /* diff --git a/security/apparmor/lsm.c b/security/apparmor/lsm.c index 5217a0a54047..b63d39ca6278 100644 --- a/security/apparmor/lsm.c +++ b/security/apparmor/lsm.c @@ -999,7 +999,8 @@ static int __init apparmor_init(void) aa_free_root_ns(); goto buffers_out; } - security_add_hooks(apparmor_hooks, ARRAY_SIZE(apparmor_hooks)); + security_add_hooks(apparmor_hooks, ARRAY_SIZE(apparmor_hooks), + "apparmor"); /* Report that AppArmor successfully initialized */ apparmor_initialized = 1; diff --git a/security/commoncap.c b/security/commoncap.c index 8df676fbd393..6d4d586b9356 100644 --- a/security/commoncap.c +++ b/security/commoncap.c @@ -1093,7 +1093,8 @@ struct security_hook_list capability_hooks[] = { void __init capability_add_hooks(void) { - security_add_hooks(capability_hooks, ARRAY_SIZE(capability_hooks)); + security_add_hooks(capability_hooks, ARRAY_SIZE(capability_hooks), + "capability"); } #endif /* CONFIG_SECURITY */ diff --git a/security/inode.c b/security/inode.c index c83db05c15ab..2cb14162ff8d 100644 --- a/security/inode.c +++ b/security/inode.c @@ -20,6 +20,7 @@ #include #include #include +#include #include static struct vfsmount *mount; @@ -204,6 +205,21 @@ void securityfs_remove(struct dentry *dentry) } EXPORT_SYMBOL_GPL(securityfs_remove); +#ifdef CONFIG_SECURITY +static struct dentry *lsm_dentry; +static ssize_t lsm_read(struct file *filp, char __user *buf, size_t count, + loff_t *ppos) +{ + return simple_read_from_buffer(buf, count, ppos, lsm_names, + strlen(lsm_names)); +} + +static const struct file_operations lsm_ops = { + .read = lsm_read, + .llseek = generic_file_llseek, +}; +#endif + static int __init securityfs_init(void) { int retval; @@ -213,9 +229,15 @@ static int __init securityfs_init(void) return retval; retval = register_filesystem(&fs_type); - if (retval) + if (retval) { sysfs_remove_mount_point(kernel_kobj, "security"); - return retval; + return retval; + } +#ifdef CONFIG_SECURITY + lsm_dentry = securityfs_create_file("lsm", 0444, NULL, NULL, + &lsm_ops); +#endif + return 0; } core_initcall(securityfs_init); diff --git a/security/loadpin/loadpin.c b/security/loadpin/loadpin.c index 89a46f10b8a7..1d82eae3a5b8 100644 --- a/security/loadpin/loadpin.c +++ b/security/loadpin/loadpin.c @@ -182,7 +182,7 @@ static struct security_hook_list loadpin_hooks[] = { void __init loadpin_add_hooks(void) { pr_info("ready to pin (currently %sabled)", enabled ? "en" : "dis"); - security_add_hooks(loadpin_hooks, ARRAY_SIZE(loadpin_hooks)); + security_add_hooks(loadpin_hooks, ARRAY_SIZE(loadpin_hooks), "loadpin"); } /* Should not be mutable after boot, so not listed in sysfs (perm == 0). */ diff --git a/security/security.c b/security/security.c index f825304f04a7..f0a802ee29b6 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 +char *lsm_names; /* Boot-time LSM user choice */ static __initdata char chosen_lsm[SECURITY_NAME_MAX + 1] = CONFIG_DEFAULT_SECURITY; @@ -78,6 +79,22 @@ static int __init choose_lsm(char *str) } __setup("security=", choose_lsm); +static int lsm_append(char *new, char **result) +{ + char *cp; + + if (*result == NULL) { + *result = kstrdup(new, GFP_KERNEL); + } else { + cp = kasprintf(GFP_KERNEL, "%s,%s", *result, new); + if (cp == NULL) + return -ENOMEM; + kfree(*result); + *result = cp; + } + return 0; +} + /** * security_module_enable - Load given security module on boot ? * @module: the name of the module @@ -97,6 +114,27 @@ int __init security_module_enable(const char *module) return !strcmp(module, chosen_lsm); } +/** + * security_add_hooks - Add a modules hooks to the hook lists. + * @hooks: the hooks to add + * @count: the number of hooks to add + * @lsm: the name of the security module + * + * Each LSM has to register its hooks with the infrastructure. + */ +void __init security_add_hooks(struct security_hook_list *hooks, int count, + char *lsm) +{ + int i; + + for (i = 0; i < count; i++) { + hooks[i].lsm = lsm; + list_add_tail_rcu(&hooks[i].list, hooks[i].head); + } + if (lsm_append(lsm, &lsm_names) < 0) + panic("%s - Cannot get early memory.\n", __func__); +} + /* * Hook list operation macros. * diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index c7c6619431d5..fdc24ea65fd0 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -6349,7 +6349,7 @@ static __init int selinux_init(void) 0, SLAB_PANIC, NULL); avc_init(); - security_add_hooks(selinux_hooks, ARRAY_SIZE(selinux_hooks)); + security_add_hooks(selinux_hooks, ARRAY_SIZE(selinux_hooks), "selinux"); if (avc_add_callback(selinux_netcache_avc_callback, AVC_CALLBACK_RESET)) panic("SELinux: Unable to register AVC netcache callback\n"); diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c index 94dc9d406ce3..839150e40eb1 100644 --- a/security/smack/smack_lsm.c +++ b/security/smack/smack_lsm.c @@ -4819,7 +4819,7 @@ static __init int smack_init(void) /* * Register with LSM */ - security_add_hooks(smack_hooks, ARRAY_SIZE(smack_hooks)); + security_add_hooks(smack_hooks, ARRAY_SIZE(smack_hooks), "smack"); return 0; } diff --git a/security/tomoyo/tomoyo.c b/security/tomoyo/tomoyo.c index 75c998700190..edc52d620f29 100644 --- a/security/tomoyo/tomoyo.c +++ b/security/tomoyo/tomoyo.c @@ -542,7 +542,7 @@ static int __init tomoyo_init(void) if (!security_module_enable("tomoyo")) return 0; /* register ourselves with the security framework */ - security_add_hooks(tomoyo_hooks, ARRAY_SIZE(tomoyo_hooks)); + security_add_hooks(tomoyo_hooks, ARRAY_SIZE(tomoyo_hooks), "tomoyo"); printk(KERN_INFO "TOMOYO Linux initialized\n"); cred->security = &tomoyo_kernel_domain; tomoyo_mm_init(); diff --git a/security/yama/yama_lsm.c b/security/yama/yama_lsm.c index 968e5e0a3f81..88271a3bf37f 100644 --- a/security/yama/yama_lsm.c +++ b/security/yama/yama_lsm.c @@ -485,6 +485,6 @@ static inline void yama_init_sysctl(void) { } void __init yama_add_hooks(void) { pr_info("Yama: becoming mindful.\n"); - security_add_hooks(yama_hooks, ARRAY_SIZE(yama_hooks)); + security_add_hooks(yama_hooks, ARRAY_SIZE(yama_hooks), "yama"); yama_init_sysctl(); } -- cgit v1.2.3