diff options
Diffstat (limited to 'security/selinux/hooks.c')
| -rw-r--r-- | security/selinux/hooks.c | 147 |
1 files changed, 75 insertions, 72 deletions
diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index 63f131fc42e4..4a7374c12d9c 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c | |||
| @@ -25,7 +25,7 @@ | |||
| 25 | 25 | ||
| 26 | #include <linux/init.h> | 26 | #include <linux/init.h> |
| 27 | #include <linux/kernel.h> | 27 | #include <linux/kernel.h> |
| 28 | #include <linux/ptrace.h> | 28 | #include <linux/tracehook.h> |
| 29 | #include <linux/errno.h> | 29 | #include <linux/errno.h> |
| 30 | #include <linux/sched.h> | 30 | #include <linux/sched.h> |
| 31 | #include <linux/security.h> | 31 | #include <linux/security.h> |
| @@ -957,7 +957,8 @@ out_err: | |||
| 957 | return rc; | 957 | return rc; |
| 958 | } | 958 | } |
| 959 | 959 | ||
| 960 | void selinux_write_opts(struct seq_file *m, struct security_mnt_opts *opts) | 960 | static void selinux_write_opts(struct seq_file *m, |
| 961 | struct security_mnt_opts *opts) | ||
| 961 | { | 962 | { |
| 962 | int i; | 963 | int i; |
| 963 | char *prefix; | 964 | char *prefix; |
| @@ -998,8 +999,12 @@ static int selinux_sb_show_options(struct seq_file *m, struct super_block *sb) | |||
| 998 | int rc; | 999 | int rc; |
| 999 | 1000 | ||
| 1000 | rc = selinux_get_mnt_opts(sb, &opts); | 1001 | rc = selinux_get_mnt_opts(sb, &opts); |
| 1001 | if (rc) | 1002 | if (rc) { |
| 1003 | /* before policy load we may get EINVAL, don't show anything */ | ||
| 1004 | if (rc == -EINVAL) | ||
| 1005 | rc = 0; | ||
| 1002 | return rc; | 1006 | return rc; |
| 1007 | } | ||
| 1003 | 1008 | ||
| 1004 | selinux_write_opts(m, &opts); | 1009 | selinux_write_opts(m, &opts); |
| 1005 | 1010 | ||
| @@ -1286,7 +1291,7 @@ static int inode_doinit_with_dentry(struct inode *inode, struct dentry *opt_dent | |||
| 1286 | /* Default to the fs superblock SID. */ | 1291 | /* Default to the fs superblock SID. */ |
| 1287 | isec->sid = sbsec->sid; | 1292 | isec->sid = sbsec->sid; |
| 1288 | 1293 | ||
| 1289 | if (sbsec->proc) { | 1294 | if (sbsec->proc && !S_ISLNK(inode->i_mode)) { |
| 1290 | struct proc_inode *proci = PROC_I(inode); | 1295 | struct proc_inode *proci = PROC_I(inode); |
| 1291 | if (proci->pde) { | 1296 | if (proci->pde) { |
| 1292 | isec->sclass = inode_mode_to_security_class(inode->i_mode); | 1297 | isec->sclass = inode_mode_to_security_class(inode->i_mode); |
| @@ -1734,24 +1739,34 @@ static inline u32 file_to_av(struct file *file) | |||
| 1734 | 1739 | ||
| 1735 | /* Hook functions begin here. */ | 1740 | /* Hook functions begin here. */ |
| 1736 | 1741 | ||
| 1737 | static int selinux_ptrace(struct task_struct *parent, | 1742 | static int selinux_ptrace_may_access(struct task_struct *child, |
| 1738 | struct task_struct *child, | 1743 | unsigned int mode) |
| 1739 | unsigned int mode) | ||
| 1740 | { | 1744 | { |
| 1741 | int rc; | 1745 | int rc; |
| 1742 | 1746 | ||
| 1743 | rc = secondary_ops->ptrace(parent, child, mode); | 1747 | rc = secondary_ops->ptrace_may_access(child, mode); |
| 1744 | if (rc) | 1748 | if (rc) |
| 1745 | return rc; | 1749 | return rc; |
| 1746 | 1750 | ||
| 1747 | if (mode == PTRACE_MODE_READ) { | 1751 | if (mode == PTRACE_MODE_READ) { |
| 1748 | struct task_security_struct *tsec = parent->security; | 1752 | struct task_security_struct *tsec = current->security; |
| 1749 | struct task_security_struct *csec = child->security; | 1753 | struct task_security_struct *csec = child->security; |
| 1750 | return avc_has_perm(tsec->sid, csec->sid, | 1754 | return avc_has_perm(tsec->sid, csec->sid, |
| 1751 | SECCLASS_FILE, FILE__READ, NULL); | 1755 | SECCLASS_FILE, FILE__READ, NULL); |
| 1752 | } | 1756 | } |
| 1753 | 1757 | ||
| 1754 | return task_has_perm(parent, child, PROCESS__PTRACE); | 1758 | return task_has_perm(current, child, PROCESS__PTRACE); |
| 1759 | } | ||
| 1760 | |||
| 1761 | static int selinux_ptrace_traceme(struct task_struct *parent) | ||
| 1762 | { | ||
| 1763 | int rc; | ||
| 1764 | |||
| 1765 | rc = secondary_ops->ptrace_traceme(parent); | ||
| 1766 | if (rc) | ||
| 1767 | return rc; | ||
| 1768 | |||
| 1769 | return task_has_perm(parent, current, PROCESS__PTRACE); | ||
| 1755 | } | 1770 | } |
| 1756 | 1771 | ||
| 1757 | static int selinux_capget(struct task_struct *target, kernel_cap_t *effective, | 1772 | static int selinux_capget(struct task_struct *target, kernel_cap_t *effective, |
| @@ -1971,22 +1986,6 @@ static int selinux_vm_enough_memory(struct mm_struct *mm, long pages) | |||
| 1971 | return __vm_enough_memory(mm, pages, cap_sys_admin); | 1986 | return __vm_enough_memory(mm, pages, cap_sys_admin); |
| 1972 | } | 1987 | } |
| 1973 | 1988 | ||
| 1974 | /** | ||
| 1975 | * task_tracer_task - return the task that is tracing the given task | ||
| 1976 | * @task: task to consider | ||
| 1977 | * | ||
| 1978 | * Returns NULL if noone is tracing @task, or the &struct task_struct | ||
| 1979 | * pointer to its tracer. | ||
| 1980 | * | ||
| 1981 | * Must be called under rcu_read_lock(). | ||
| 1982 | */ | ||
| 1983 | static struct task_struct *task_tracer_task(struct task_struct *task) | ||
| 1984 | { | ||
| 1985 | if (task->ptrace & PT_PTRACED) | ||
| 1986 | return rcu_dereference(task->parent); | ||
| 1987 | return NULL; | ||
| 1988 | } | ||
| 1989 | |||
| 1990 | /* binprm security operations */ | 1989 | /* binprm security operations */ |
| 1991 | 1990 | ||
| 1992 | static int selinux_bprm_alloc_security(struct linux_binprm *bprm) | 1991 | static int selinux_bprm_alloc_security(struct linux_binprm *bprm) |
| @@ -2238,7 +2237,7 @@ static void selinux_bprm_apply_creds(struct linux_binprm *bprm, int unsafe) | |||
| 2238 | u32 ptsid = 0; | 2237 | u32 ptsid = 0; |
| 2239 | 2238 | ||
| 2240 | rcu_read_lock(); | 2239 | rcu_read_lock(); |
| 2241 | tracer = task_tracer_task(current); | 2240 | tracer = tracehook_tracer_task(current); |
| 2242 | if (likely(tracer != NULL)) { | 2241 | if (likely(tracer != NULL)) { |
| 2243 | sec = tracer->security; | 2242 | sec = tracer->security; |
| 2244 | ptsid = sec->sid; | 2243 | ptsid = sec->sid; |
| @@ -2640,12 +2639,11 @@ static int selinux_inode_follow_link(struct dentry *dentry, struct nameidata *na | |||
| 2640 | return dentry_has_perm(current, NULL, dentry, FILE__READ); | 2639 | return dentry_has_perm(current, NULL, dentry, FILE__READ); |
| 2641 | } | 2640 | } |
| 2642 | 2641 | ||
| 2643 | static int selinux_inode_permission(struct inode *inode, int mask, | 2642 | static int selinux_inode_permission(struct inode *inode, int mask) |
| 2644 | struct nameidata *nd) | ||
| 2645 | { | 2643 | { |
| 2646 | int rc; | 2644 | int rc; |
| 2647 | 2645 | ||
| 2648 | rc = secondary_ops->inode_permission(inode, mask, nd); | 2646 | rc = secondary_ops->inode_permission(inode, mask); |
| 2649 | if (rc) | 2647 | if (rc) |
| 2650 | return rc; | 2648 | return rc; |
| 2651 | 2649 | ||
| @@ -3551,38 +3549,44 @@ out: | |||
| 3551 | #endif /* IPV6 */ | 3549 | #endif /* IPV6 */ |
| 3552 | 3550 | ||
| 3553 | static int selinux_parse_skb(struct sk_buff *skb, struct avc_audit_data *ad, | 3551 | static int selinux_parse_skb(struct sk_buff *skb, struct avc_audit_data *ad, |
| 3554 | char **addrp, int src, u8 *proto) | 3552 | char **_addrp, int src, u8 *proto) |
| 3555 | { | 3553 | { |
| 3556 | int ret = 0; | 3554 | char *addrp; |
| 3555 | int ret; | ||
| 3557 | 3556 | ||
| 3558 | switch (ad->u.net.family) { | 3557 | switch (ad->u.net.family) { |
| 3559 | case PF_INET: | 3558 | case PF_INET: |
| 3560 | ret = selinux_parse_skb_ipv4(skb, ad, proto); | 3559 | ret = selinux_parse_skb_ipv4(skb, ad, proto); |
| 3561 | if (ret || !addrp) | 3560 | if (ret) |
| 3562 | break; | 3561 | goto parse_error; |
| 3563 | *addrp = (char *)(src ? &ad->u.net.v4info.saddr : | 3562 | addrp = (char *)(src ? &ad->u.net.v4info.saddr : |
| 3564 | &ad->u.net.v4info.daddr); | 3563 | &ad->u.net.v4info.daddr); |
| 3565 | break; | 3564 | goto okay; |
| 3566 | 3565 | ||
| 3567 | #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) | 3566 | #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) |
| 3568 | case PF_INET6: | 3567 | case PF_INET6: |
| 3569 | ret = selinux_parse_skb_ipv6(skb, ad, proto); | 3568 | ret = selinux_parse_skb_ipv6(skb, ad, proto); |
| 3570 | if (ret || !addrp) | 3569 | if (ret) |
| 3571 | break; | 3570 | goto parse_error; |
| 3572 | *addrp = (char *)(src ? &ad->u.net.v6info.saddr : | 3571 | addrp = (char *)(src ? &ad->u.net.v6info.saddr : |
| 3573 | &ad->u.net.v6info.daddr); | 3572 | &ad->u.net.v6info.daddr); |
| 3574 | break; | 3573 | goto okay; |
| 3575 | #endif /* IPV6 */ | 3574 | #endif /* IPV6 */ |
| 3576 | default: | 3575 | default: |
| 3577 | break; | 3576 | addrp = NULL; |
| 3577 | goto okay; | ||
| 3578 | } | 3578 | } |
| 3579 | 3579 | ||
| 3580 | if (unlikely(ret)) | 3580 | parse_error: |
| 3581 | printk(KERN_WARNING | 3581 | printk(KERN_WARNING |
| 3582 | "SELinux: failure in selinux_parse_skb()," | 3582 | "SELinux: failure in selinux_parse_skb()," |
| 3583 | " unable to parse packet\n"); | 3583 | " unable to parse packet\n"); |
| 3584 | |||
| 3585 | return ret; | 3584 | return ret; |
| 3585 | |||
| 3586 | okay: | ||
| 3587 | if (_addrp) | ||
| 3588 | *_addrp = addrp; | ||
| 3589 | return 0; | ||
| 3586 | } | 3590 | } |
| 3587 | 3591 | ||
| 3588 | /** | 3592 | /** |
| @@ -5222,8 +5226,12 @@ static int selinux_setprocattr(struct task_struct *p, | |||
| 5222 | 5226 | ||
| 5223 | if (sid == 0) | 5227 | if (sid == 0) |
| 5224 | return -EINVAL; | 5228 | return -EINVAL; |
| 5225 | 5229 | /* | |
| 5226 | /* Only allow single threaded processes to change context */ | 5230 | * SELinux allows to change context in the following case only. |
| 5231 | * - Single threaded processes. | ||
| 5232 | * - Multi threaded processes intend to change its context into | ||
| 5233 | * more restricted domain (defined by TYPEBOUNDS statement). | ||
| 5234 | */ | ||
| 5227 | if (atomic_read(&p->mm->mm_users) != 1) { | 5235 | if (atomic_read(&p->mm->mm_users) != 1) { |
| 5228 | struct task_struct *g, *t; | 5236 | struct task_struct *g, *t; |
| 5229 | struct mm_struct *mm = p->mm; | 5237 | struct mm_struct *mm = p->mm; |
| @@ -5231,11 +5239,16 @@ static int selinux_setprocattr(struct task_struct *p, | |||
| 5231 | do_each_thread(g, t) { | 5239 | do_each_thread(g, t) { |
| 5232 | if (t->mm == mm && t != p) { | 5240 | if (t->mm == mm && t != p) { |
| 5233 | read_unlock(&tasklist_lock); | 5241 | read_unlock(&tasklist_lock); |
| 5234 | return -EPERM; | 5242 | error = security_bounded_transition(tsec->sid, sid); |
| 5243 | if (!error) | ||
| 5244 | goto boundary_ok; | ||
| 5245 | |||
| 5246 | return error; | ||
| 5235 | } | 5247 | } |
| 5236 | } while_each_thread(g, t); | 5248 | } while_each_thread(g, t); |
| 5237 | read_unlock(&tasklist_lock); | 5249 | read_unlock(&tasklist_lock); |
| 5238 | } | 5250 | } |
| 5251 | boundary_ok: | ||
| 5239 | 5252 | ||
| 5240 | /* Check permissions for the transition. */ | 5253 | /* Check permissions for the transition. */ |
| 5241 | error = avc_has_perm(tsec->sid, sid, SECCLASS_PROCESS, | 5254 | error = avc_has_perm(tsec->sid, sid, SECCLASS_PROCESS, |
| @@ -5247,7 +5260,7 @@ static int selinux_setprocattr(struct task_struct *p, | |||
| 5247 | Otherwise, leave SID unchanged and fail. */ | 5260 | Otherwise, leave SID unchanged and fail. */ |
| 5248 | task_lock(p); | 5261 | task_lock(p); |
| 5249 | rcu_read_lock(); | 5262 | rcu_read_lock(); |
| 5250 | tracer = task_tracer_task(p); | 5263 | tracer = tracehook_tracer_task(p); |
| 5251 | if (tracer != NULL) { | 5264 | if (tracer != NULL) { |
| 5252 | struct task_security_struct *ptsec = tracer->security; | 5265 | struct task_security_struct *ptsec = tracer->security; |
| 5253 | u32 ptsid = ptsec->sid; | 5266 | u32 ptsid = ptsec->sid; |
| @@ -5359,7 +5372,8 @@ static int selinux_key_getsecurity(struct key *key, char **_buffer) | |||
| 5359 | static struct security_operations selinux_ops = { | 5372 | static struct security_operations selinux_ops = { |
| 5360 | .name = "selinux", | 5373 | .name = "selinux", |
| 5361 | 5374 | ||
| 5362 | .ptrace = selinux_ptrace, | 5375 | .ptrace_may_access = selinux_ptrace_may_access, |
| 5376 | .ptrace_traceme = selinux_ptrace_traceme, | ||
| 5363 | .capget = selinux_capget, | 5377 | .capget = selinux_capget, |
| 5364 | .capset_check = selinux_capset_check, | 5378 | .capset_check = selinux_capset_check, |
| 5365 | .capset_set = selinux_capset_set, | 5379 | .capset_set = selinux_capset_set, |
| @@ -5670,27 +5684,20 @@ static struct nf_hook_ops selinux_ipv6_ops[] = { | |||
| 5670 | static int __init selinux_nf_ip_init(void) | 5684 | static int __init selinux_nf_ip_init(void) |
| 5671 | { | 5685 | { |
| 5672 | int err = 0; | 5686 | int err = 0; |
| 5673 | u32 iter; | ||
| 5674 | 5687 | ||
| 5675 | if (!selinux_enabled) | 5688 | if (!selinux_enabled) |
| 5676 | goto out; | 5689 | goto out; |
| 5677 | 5690 | ||
| 5678 | printk(KERN_DEBUG "SELinux: Registering netfilter hooks\n"); | 5691 | printk(KERN_DEBUG "SELinux: Registering netfilter hooks\n"); |
| 5679 | 5692 | ||
| 5680 | for (iter = 0; iter < ARRAY_SIZE(selinux_ipv4_ops); iter++) { | 5693 | err = nf_register_hooks(selinux_ipv4_ops, ARRAY_SIZE(selinux_ipv4_ops)); |
| 5681 | err = nf_register_hook(&selinux_ipv4_ops[iter]); | 5694 | if (err) |
| 5682 | if (err) | 5695 | panic("SELinux: nf_register_hooks for IPv4: error %d\n", err); |
| 5683 | panic("SELinux: nf_register_hook for IPv4: error %d\n", | ||
| 5684 | err); | ||
| 5685 | } | ||
| 5686 | 5696 | ||
| 5687 | #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) | 5697 | #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) |
| 5688 | for (iter = 0; iter < ARRAY_SIZE(selinux_ipv6_ops); iter++) { | 5698 | err = nf_register_hooks(selinux_ipv6_ops, ARRAY_SIZE(selinux_ipv6_ops)); |
| 5689 | err = nf_register_hook(&selinux_ipv6_ops[iter]); | 5699 | if (err) |
| 5690 | if (err) | 5700 | panic("SELinux: nf_register_hooks for IPv6: error %d\n", err); |
| 5691 | panic("SELinux: nf_register_hook for IPv6: error %d\n", | ||
| 5692 | err); | ||
| 5693 | } | ||
| 5694 | #endif /* IPV6 */ | 5701 | #endif /* IPV6 */ |
| 5695 | 5702 | ||
| 5696 | out: | 5703 | out: |
| @@ -5702,15 +5709,11 @@ __initcall(selinux_nf_ip_init); | |||
| 5702 | #ifdef CONFIG_SECURITY_SELINUX_DISABLE | 5709 | #ifdef CONFIG_SECURITY_SELINUX_DISABLE |
| 5703 | static void selinux_nf_ip_exit(void) | 5710 | static void selinux_nf_ip_exit(void) |
| 5704 | { | 5711 | { |
| 5705 | u32 iter; | ||
| 5706 | |||
| 5707 | printk(KERN_DEBUG "SELinux: Unregistering netfilter hooks\n"); | 5712 | printk(KERN_DEBUG "SELinux: Unregistering netfilter hooks\n"); |
| 5708 | 5713 | ||
| 5709 | for (iter = 0; iter < ARRAY_SIZE(selinux_ipv4_ops); iter++) | 5714 | nf_unregister_hooks(selinux_ipv4_ops, ARRAY_SIZE(selinux_ipv4_ops)); |
| 5710 | nf_unregister_hook(&selinux_ipv4_ops[iter]); | ||
| 5711 | #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) | 5715 | #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) |
| 5712 | for (iter = 0; iter < ARRAY_SIZE(selinux_ipv6_ops); iter++) | 5716 | nf_unregister_hooks(selinux_ipv6_ops, ARRAY_SIZE(selinux_ipv6_ops)); |
| 5713 | nf_unregister_hook(&selinux_ipv6_ops[iter]); | ||
| 5714 | #endif /* IPV6 */ | 5717 | #endif /* IPV6 */ |
| 5715 | } | 5718 | } |
| 5716 | #endif | 5719 | #endif |
