From 7b0d0b40cd78cadb525df760ee4cac151533c2b5 Mon Sep 17 00:00:00 2001 From: Stephen Smalley Date: Mon, 4 Aug 2014 13:36:49 -0400 Subject: selinux: Permit bounded transitions under NO_NEW_PRIVS or NOSUID. If the callee SID is bounded by the caller SID, then allowing the transition to occur poses no risk of privilege escalation and we can therefore safely allow the transition to occur. Add this exemption for both the case where a transition was explicitly requested by the application and the case where an automatic transition is defined in policy. Signed-off-by: Stephen Smalley Reviewed-by: Andy Lutomirski Signed-off-by: Paul Moore --- security/selinux/hooks.c | 59 ++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 47 insertions(+), 12 deletions(-) (limited to 'security/selinux/hooks.c') diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index b0e940497e23..6c90d491fab4 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -2097,6 +2097,41 @@ static int selinux_vm_enough_memory(struct mm_struct *mm, long pages) /* binprm security operations */ +static int check_nnp_nosuid(const struct linux_binprm *bprm, + const struct task_security_struct *old_tsec, + const struct task_security_struct *new_tsec) +{ + int nnp = (bprm->unsafe & LSM_UNSAFE_NO_NEW_PRIVS); + int nosuid = (bprm->file->f_path.mnt->mnt_flags & MNT_NOSUID); + int rc; + + if (!nnp && !nosuid) + return 0; /* neither NNP nor nosuid */ + + if (new_tsec->sid == old_tsec->sid) + return 0; /* No change in credentials */ + + /* + * The only transitions we permit under NNP or nosuid + * are transitions to bounded SIDs, i.e. SIDs that are + * guaranteed to only be allowed a subset of the permissions + * of the current SID. + */ + rc = security_bounded_transition(old_tsec->sid, new_tsec->sid); + if (rc) { + /* + * On failure, preserve the errno values for NNP vs nosuid. + * NNP: Operation not permitted for caller. + * nosuid: Permission denied to file. + */ + if (nnp) + return -EPERM; + else + return -EACCES; + } + return 0; +} + static int selinux_bprm_set_creds(struct linux_binprm *bprm) { const struct task_security_struct *old_tsec; @@ -2133,14 +2168,10 @@ static int selinux_bprm_set_creds(struct linux_binprm *bprm) /* Reset exec SID on execve. */ new_tsec->exec_sid = 0; - /* - * Minimize confusion: if no_new_privs or nosuid and a - * transition is explicitly requested, then fail the exec. - */ - if (bprm->unsafe & LSM_UNSAFE_NO_NEW_PRIVS) - return -EPERM; - if (bprm->file->f_path.mnt->mnt_flags & MNT_NOSUID) - return -EACCES; + /* Fail on NNP or nosuid if not an allowed transition. */ + rc = check_nnp_nosuid(bprm, old_tsec, new_tsec); + if (rc) + return rc; } else { /* Check for a default transition on this program. */ rc = security_transition_sid(old_tsec->sid, isec->sid, @@ -2148,15 +2179,19 @@ static int selinux_bprm_set_creds(struct linux_binprm *bprm) &new_tsec->sid); if (rc) return rc; + + /* + * Fallback to old SID on NNP or nosuid if not an allowed + * transition. + */ + rc = check_nnp_nosuid(bprm, old_tsec, new_tsec); + if (rc) + new_tsec->sid = old_tsec->sid; } ad.type = LSM_AUDIT_DATA_PATH; ad.u.path = bprm->file->f_path; - if ((bprm->file->f_path.mnt->mnt_flags & MNT_NOSUID) || - (bprm->unsafe & LSM_UNSAFE_NO_NEW_PRIVS)) - new_tsec->sid = old_tsec->sid; - if (new_tsec->sid == old_tsec->sid) { rc = avc_has_perm(old_tsec->sid, isec->sid, SECCLASS_FILE, FILE__EXECUTE_NO_TRANS, &ad); -- cgit v1.2.3 From a7a91a1928fe69cc98814cb746d5171ae14d757e Mon Sep 17 00:00:00 2001 From: Paul Moore Date: Wed, 3 Sep 2014 10:51:59 -0400 Subject: selinux: fix a problem with IPv6 traffic denials in selinux_ip_postroute() A previous commit c0828e50485932b7e019df377a6b0a8d1ebd3080 ("selinux: process labeled IPsec TCP SYN-ACK packets properly in selinux_ip_postroute()") mistakenly left out a 'break' from a switch statement which caused problems with IPv6 traffic. Thanks to Florian Westphal for reporting and debugging the issue. Reported-by: Florian Westphal Signed-off-by: Paul Moore --- security/selinux/hooks.c | 1 + 1 file changed, 1 insertion(+) (limited to 'security/selinux/hooks.c') diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index 6c90d491fab4..e1e082796a49 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -4993,6 +4993,7 @@ static unsigned int selinux_ip_postroute(struct sk_buff *skb, int ifindex, case PF_INET6: if (IP6CB(skb)->flags & IP6SKB_XFRM_TRANSFORMED) return NF_ACCEPT; + break; default: return NF_DROP_ERR(-ECONNREFUSED); } -- cgit v1.2.3 From 25db6bea1ff5a78ef493eefdcbb9c1d27134e560 Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Wed, 3 Sep 2014 17:42:13 +0200 Subject: selinux: register nf hooks with single nf_register_hooks call Push ipv4 and ipv6 nf hooks into single array and register/unregister them via single call. Signed-off-by: Jiri Pirko Signed-off-by: Paul Moore --- security/selinux/hooks.c | 35 ++++++++++------------------------- 1 file changed, 10 insertions(+), 25 deletions(-) (limited to 'security/selinux/hooks.c') diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index e1e082796a49..50978d3183ea 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -6071,7 +6071,7 @@ security_initcall(selinux_init); #if defined(CONFIG_NETFILTER) -static struct nf_hook_ops selinux_ipv4_ops[] = { +static struct nf_hook_ops selinux_nf_ops[] = { { .hook = selinux_ipv4_postroute, .owner = THIS_MODULE, @@ -6092,12 +6092,8 @@ static struct nf_hook_ops selinux_ipv4_ops[] = { .pf = NFPROTO_IPV4, .hooknum = NF_INET_LOCAL_OUT, .priority = NF_IP_PRI_SELINUX_FIRST, - } -}; - + }, #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) - -static struct nf_hook_ops selinux_ipv6_ops[] = { { .hook = selinux_ipv6_postroute, .owner = THIS_MODULE, @@ -6111,32 +6107,24 @@ static struct nf_hook_ops selinux_ipv6_ops[] = { .pf = NFPROTO_IPV6, .hooknum = NF_INET_FORWARD, .priority = NF_IP6_PRI_SELINUX_FIRST, - } -}; - + }, #endif /* IPV6 */ +}; static int __init selinux_nf_ip_init(void) { - int err = 0; + int err; if (!selinux_enabled) - goto out; + return 0; printk(KERN_DEBUG "SELinux: Registering netfilter hooks\n"); - err = nf_register_hooks(selinux_ipv4_ops, ARRAY_SIZE(selinux_ipv4_ops)); - if (err) - panic("SELinux: nf_register_hooks for IPv4: error %d\n", err); - -#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) - err = nf_register_hooks(selinux_ipv6_ops, ARRAY_SIZE(selinux_ipv6_ops)); + err = nf_register_hooks(selinux_nf_ops, ARRAY_SIZE(selinux_nf_ops)); if (err) - panic("SELinux: nf_register_hooks for IPv6: error %d\n", err); -#endif /* IPV6 */ + panic("SELinux: nf_register_hooks: error %d\n", err); -out: - return err; + return 0; } __initcall(selinux_nf_ip_init); @@ -6146,10 +6134,7 @@ static void selinux_nf_ip_exit(void) { printk(KERN_DEBUG "SELinux: Unregistering netfilter hooks\n"); - nf_unregister_hooks(selinux_ipv4_ops, ARRAY_SIZE(selinux_ipv4_ops)); -#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) - nf_unregister_hooks(selinux_ipv6_ops, ARRAY_SIZE(selinux_ipv6_ops)); -#endif /* IPV6 */ + nf_unregister_hooks(selinux_nf_ops, ARRAY_SIZE(selinux_nf_ops)); } #endif -- cgit v1.2.3 From e0b93eddfe17dcb7d644eb5d6ad02a86fc41a977 Mon Sep 17 00:00:00 2001 From: Jeff Layton Date: Fri, 22 Aug 2014 11:27:32 -0400 Subject: security: make security_file_set_fowner, f_setown and __f_setown void return security_file_set_fowner always returns 0, so make it f_setown and __f_setown void return functions and fix up the error handling in the callers. Cc: linux-security-module@vger.kernel.org Signed-off-by: Jeff Layton Reviewed-by: Christoph Hellwig --- drivers/net/tun.c | 4 +--- drivers/tty/tty_io.c | 3 ++- fs/fcntl.c | 21 +++++++-------------- fs/locks.c | 2 +- fs/notify/dnotify/dnotify.c | 8 +------- include/linux/fs.h | 4 ++-- include/linux/security.h | 8 ++++---- net/socket.c | 3 ++- security/capability.c | 4 ++-- security/security.c | 4 ++-- security/selinux/hooks.c | 4 +--- security/smack/smack_lsm.c | 3 +-- 12 files changed, 26 insertions(+), 42 deletions(-) (limited to 'security/selinux/hooks.c') diff --git a/drivers/net/tun.c b/drivers/net/tun.c index acaaf6784179..186ce541c657 100644 --- a/drivers/net/tun.c +++ b/drivers/net/tun.c @@ -2152,9 +2152,7 @@ static int tun_chr_fasync(int fd, struct file *file, int on) goto out; if (on) { - ret = __f_setown(file, task_pid(current), PIDTYPE_PID, 0); - if (ret) - goto out; + __f_setown(file, task_pid(current), PIDTYPE_PID, 0); tfile->flags |= TUN_FASYNC; } else tfile->flags &= ~TUN_FASYNC; diff --git a/drivers/tty/tty_io.c b/drivers/tty/tty_io.c index 8fbad3410c75..aea3b66f7bf2 100644 --- a/drivers/tty/tty_io.c +++ b/drivers/tty/tty_io.c @@ -2163,8 +2163,9 @@ static int __tty_fasync(int fd, struct file *filp, int on) } get_pid(pid); spin_unlock_irqrestore(&tty->ctrl_lock, flags); - retval = __f_setown(filp, pid, type, 0); + __f_setown(filp, pid, type, 0); put_pid(pid); + retval = 0; } out: return retval; diff --git a/fs/fcntl.c b/fs/fcntl.c index 22d1c3df61ac..99d440a4a6ba 100644 --- a/fs/fcntl.c +++ b/fs/fcntl.c @@ -98,26 +98,19 @@ static void f_modown(struct file *filp, struct pid *pid, enum pid_type type, write_unlock_irq(&filp->f_owner.lock); } -int __f_setown(struct file *filp, struct pid *pid, enum pid_type type, +void __f_setown(struct file *filp, struct pid *pid, enum pid_type type, int force) { - int err; - - err = security_file_set_fowner(filp); - if (err) - return err; - + security_file_set_fowner(filp); f_modown(filp, pid, type, force); - return 0; } EXPORT_SYMBOL(__f_setown); -int f_setown(struct file *filp, unsigned long arg, int force) +void f_setown(struct file *filp, unsigned long arg, int force) { enum pid_type type; struct pid *pid; int who = arg; - int result; type = PIDTYPE_PID; if (who < 0) { type = PIDTYPE_PGID; @@ -125,9 +118,8 @@ int f_setown(struct file *filp, unsigned long arg, int force) } rcu_read_lock(); pid = find_vpid(who); - result = __f_setown(filp, pid, type, force); + __f_setown(filp, pid, type, force); rcu_read_unlock(); - return result; } EXPORT_SYMBOL(f_setown); @@ -181,7 +173,7 @@ static int f_setown_ex(struct file *filp, unsigned long arg) if (owner.pid && !pid) ret = -ESRCH; else - ret = __f_setown(filp, pid, type, 1); + __f_setown(filp, pid, type, 1); rcu_read_unlock(); return ret; @@ -302,7 +294,8 @@ static long do_fcntl(int fd, unsigned int cmd, unsigned long arg, force_successful_syscall_return(); break; case F_SETOWN: - err = f_setown(filp, arg, 1); + f_setown(filp, arg, 1); + err = 0; break; case F_GETOWN_EX: err = f_getown_ex(filp, arg); diff --git a/fs/locks.c b/fs/locks.c index 5200ffd2ba9b..f5f648e003dd 100644 --- a/fs/locks.c +++ b/fs/locks.c @@ -1776,7 +1776,7 @@ static int do_fcntl_add_lease(unsigned int fd, struct file *filp, long arg) if (!fasync_insert_entry(fd, filp, &ret->fl_fasync, new)) new = NULL; - error = __f_setown(filp, task_pid(current), PIDTYPE_PID, 0); + __f_setown(filp, task_pid(current), PIDTYPE_PID, 0); out_unlock: spin_unlock(&inode->i_lock); if (fl) diff --git a/fs/notify/dnotify/dnotify.c b/fs/notify/dnotify/dnotify.c index abc8cbcfe90e..caaaf9dfe353 100644 --- a/fs/notify/dnotify/dnotify.c +++ b/fs/notify/dnotify/dnotify.c @@ -346,13 +346,7 @@ int fcntl_dirnotify(int fd, struct file *filp, unsigned long arg) goto out; } - error = __f_setown(filp, task_pid(current), PIDTYPE_PID, 0); - if (error) { - /* if we added, we must shoot */ - if (dn_mark == new_dn_mark) - destroy = 1; - goto out; - } + __f_setown(filp, task_pid(current), PIDTYPE_PID, 0); error = attach_dn(dn, dn_mark, id, fd, filp, mask); /* !error means that we attached the dn to the dn_mark, so don't free it */ diff --git a/include/linux/fs.h b/include/linux/fs.h index 435e3d9ec5cf..96528f73dda4 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -1139,8 +1139,8 @@ extern void fasync_free(struct fasync_struct *); /* can be called from interrupts */ extern void kill_fasync(struct fasync_struct **, int, int); -extern int __f_setown(struct file *filp, struct pid *, enum pid_type, int force); -extern int f_setown(struct file *filp, unsigned long arg, int force); +extern void __f_setown(struct file *filp, struct pid *, enum pid_type, int force); +extern void f_setown(struct file *filp, unsigned long arg, int force); extern void f_delown(struct file *filp); extern pid_t f_getown(struct file *filp); extern int send_sigurg(struct fown_struct *fown); diff --git a/include/linux/security.h b/include/linux/security.h index 623f90e5f38d..b10e7af95d3b 100644 --- a/include/linux/security.h +++ b/include/linux/security.h @@ -1559,7 +1559,7 @@ struct security_operations { int (*file_lock) (struct file *file, unsigned int cmd); int (*file_fcntl) (struct file *file, unsigned int cmd, unsigned long arg); - int (*file_set_fowner) (struct file *file); + void (*file_set_fowner) (struct file *file); int (*file_send_sigiotask) (struct task_struct *tsk, struct fown_struct *fown, int sig); int (*file_receive) (struct file *file); @@ -1834,7 +1834,7 @@ int security_file_mprotect(struct vm_area_struct *vma, unsigned long reqprot, unsigned long prot); int security_file_lock(struct file *file, unsigned int cmd); int security_file_fcntl(struct file *file, unsigned int cmd, unsigned long arg); -int security_file_set_fowner(struct file *file); +void security_file_set_fowner(struct file *file); int security_file_send_sigiotask(struct task_struct *tsk, struct fown_struct *fown, int sig); int security_file_receive(struct file *file); @@ -2312,9 +2312,9 @@ static inline int security_file_fcntl(struct file *file, unsigned int cmd, return 0; } -static inline int security_file_set_fowner(struct file *file) +static inline void security_file_set_fowner(struct file *file) { - return 0; + return; } static inline int security_file_send_sigiotask(struct task_struct *tsk, diff --git a/net/socket.c b/net/socket.c index 95ee7d8682e7..769c9671847e 100644 --- a/net/socket.c +++ b/net/socket.c @@ -1069,7 +1069,8 @@ static long sock_ioctl(struct file *file, unsigned cmd, unsigned long arg) err = -EFAULT; if (get_user(pid, (int __user *)argp)) break; - err = f_setown(sock->file, pid, 1); + f_setown(sock->file, pid, 1); + err = 0; break; case FIOGETOWN: case SIOCGPGRP: diff --git a/security/capability.c b/security/capability.c index a74fde6a7468..d68c57a62bcf 100644 --- a/security/capability.c +++ b/security/capability.c @@ -343,9 +343,9 @@ static int cap_file_fcntl(struct file *file, unsigned int cmd, return 0; } -static int cap_file_set_fowner(struct file *file) +static void cap_file_set_fowner(struct file *file) { - return 0; + return; } static int cap_file_send_sigiotask(struct task_struct *tsk, diff --git a/security/security.c b/security/security.c index e41b1a8d7644..18b35c63fc0c 100644 --- a/security/security.c +++ b/security/security.c @@ -775,9 +775,9 @@ int security_file_fcntl(struct file *file, unsigned int cmd, unsigned long arg) return security_ops->file_fcntl(file, cmd, arg); } -int security_file_set_fowner(struct file *file) +void security_file_set_fowner(struct file *file) { - return security_ops->file_set_fowner(file); + security_ops->file_set_fowner(file); } int security_file_send_sigiotask(struct task_struct *tsk, diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index b0e940497e23..ada0d0bf3463 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -3346,14 +3346,12 @@ static int selinux_file_fcntl(struct file *file, unsigned int cmd, return err; } -static int selinux_file_set_fowner(struct file *file) +static void selinux_file_set_fowner(struct file *file) { struct file_security_struct *fsec; fsec = file->f_security; fsec->fown_sid = current_sid(); - - return 0; } static int selinux_file_send_sigiotask(struct task_struct *tsk, diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c index e6ab307ce86e..69e5635d89e5 100644 --- a/security/smack/smack_lsm.c +++ b/security/smack/smack_lsm.c @@ -1390,12 +1390,11 @@ static int smack_mmap_file(struct file *file, * Returns 0 * Further research may be required on this one. */ -static int smack_file_set_fowner(struct file *file) +static void smack_file_set_fowner(struct file *file) { struct smack_known *skp = smk_of_current(); file->f_security = skp->smk_known; - return 0; } /** -- cgit v1.2.3 From cbe0d6e8794f1da6cac1ea3864d2cfaf0bf87c8e Mon Sep 17 00:00:00 2001 From: Paul Moore Date: Wed, 10 Sep 2014 17:09:57 -0400 Subject: selinux: make the netif cache namespace aware While SELinux largely ignores namespaces, for good reason, there are some places where it needs to at least be aware of namespaces in order to function correctly. Network namespaces are one example. Basic awareness of network namespaces are necessary in order to match a network interface's index number to an actual network device. This patch corrects a problem with network interfaces added to a non-init namespace, and can be reproduced with the following commands: [NOTE: the NetLabel configuration is here only to active the dynamic networking controls ] # netlabelctl unlbl add default address:0.0.0.0/0 \ label:system_u:object_r:unlabeled_t:s0 # netlabelctl unlbl add default address:::/0 \ label:system_u:object_r:unlabeled_t:s0 # netlabelctl cipsov4 add pass doi:100 tags:1 # netlabelctl map add domain:lspp_test_netlabel_t \ protocol:cipsov4,100 # ip link add type veth # ip netns add myns # ip link set veth1 netns myns # ip a add dev veth0 10.250.13.100/24 # ip netns exec myns ip a add dev veth1 10.250.13.101/24 # ip l set veth0 up # ip netns exec myns ip l set veth1 up # ping -c 1 10.250.13.101 # ip netns exec myns ping -c 1 10.250.13.100 Reported-by: Jiri Jaburek Signed-off-by: Paul Moore --- security/selinux/hooks.c | 33 ++++++++++++++++-------------- security/selinux/include/netif.h | 4 +++- security/selinux/include/objsec.h | 2 ++ security/selinux/netif.c | 43 +++++++++++++++++++++------------------ 4 files changed, 46 insertions(+), 36 deletions(-) (limited to 'security/selinux/hooks.c') diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index 50978d3183ea..5eb512b7ac31 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -4307,15 +4307,15 @@ static int selinux_socket_unix_may_send(struct socket *sock, &ad); } -static int selinux_inet_sys_rcv_skb(int ifindex, char *addrp, u16 family, - u32 peer_sid, +static int selinux_inet_sys_rcv_skb(struct net *ns, int ifindex, + char *addrp, u16 family, u32 peer_sid, struct common_audit_data *ad) { int err; u32 if_sid; u32 node_sid; - err = sel_netif_sid(ifindex, &if_sid); + err = sel_netif_sid(ns, ifindex, &if_sid); if (err) return err; err = avc_has_perm(peer_sid, if_sid, @@ -4408,8 +4408,8 @@ static int selinux_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb) err = selinux_skb_peerlbl_sid(skb, family, &peer_sid); if (err) return err; - err = selinux_inet_sys_rcv_skb(skb->skb_iif, addrp, family, - peer_sid, &ad); + err = selinux_inet_sys_rcv_skb(sock_net(sk), skb->skb_iif, + addrp, family, peer_sid, &ad); if (err) { selinux_netlbl_err(skb, err, 0); return err; @@ -4748,7 +4748,8 @@ out: #ifdef CONFIG_NETFILTER -static unsigned int selinux_ip_forward(struct sk_buff *skb, int ifindex, +static unsigned int selinux_ip_forward(struct sk_buff *skb, + const struct net_device *indev, u16 family) { int err; @@ -4774,14 +4775,14 @@ static unsigned int selinux_ip_forward(struct sk_buff *skb, int ifindex, ad.type = LSM_AUDIT_DATA_NET; ad.u.net = &net; - ad.u.net->netif = ifindex; + ad.u.net->netif = indev->ifindex; ad.u.net->family = family; if (selinux_parse_skb(skb, &ad, &addrp, 1, NULL) != 0) return NF_DROP; if (peerlbl_active) { - err = selinux_inet_sys_rcv_skb(ifindex, addrp, family, - peer_sid, &ad); + err = selinux_inet_sys_rcv_skb(dev_net(indev), indev->ifindex, + addrp, family, peer_sid, &ad); if (err) { selinux_netlbl_err(skb, err, 1); return NF_DROP; @@ -4810,7 +4811,7 @@ static unsigned int selinux_ipv4_forward(const struct nf_hook_ops *ops, const struct net_device *out, int (*okfn)(struct sk_buff *)) { - return selinux_ip_forward(skb, in->ifindex, PF_INET); + return selinux_ip_forward(skb, in, PF_INET); } #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) @@ -4820,7 +4821,7 @@ static unsigned int selinux_ipv6_forward(const struct nf_hook_ops *ops, const struct net_device *out, int (*okfn)(struct sk_buff *)) { - return selinux_ip_forward(skb, in->ifindex, PF_INET6); + return selinux_ip_forward(skb, in, PF_INET6); } #endif /* IPV6 */ @@ -4908,11 +4909,13 @@ static unsigned int selinux_ip_postroute_compat(struct sk_buff *skb, return NF_ACCEPT; } -static unsigned int selinux_ip_postroute(struct sk_buff *skb, int ifindex, +static unsigned int selinux_ip_postroute(struct sk_buff *skb, + const struct net_device *outdev, u16 family) { u32 secmark_perm; u32 peer_sid; + int ifindex = outdev->ifindex; struct sock *sk; struct common_audit_data ad; struct lsm_network_audit net = {0,}; @@ -5025,7 +5028,7 @@ static unsigned int selinux_ip_postroute(struct sk_buff *skb, int ifindex, u32 if_sid; u32 node_sid; - if (sel_netif_sid(ifindex, &if_sid)) + if (sel_netif_sid(dev_net(outdev), ifindex, &if_sid)) return NF_DROP; if (avc_has_perm(peer_sid, if_sid, SECCLASS_NETIF, NETIF__EGRESS, &ad)) @@ -5047,7 +5050,7 @@ static unsigned int selinux_ipv4_postroute(const struct nf_hook_ops *ops, const struct net_device *out, int (*okfn)(struct sk_buff *)) { - return selinux_ip_postroute(skb, out->ifindex, PF_INET); + return selinux_ip_postroute(skb, out, PF_INET); } #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) @@ -5057,7 +5060,7 @@ static unsigned int selinux_ipv6_postroute(const struct nf_hook_ops *ops, const struct net_device *out, int (*okfn)(struct sk_buff *)) { - return selinux_ip_postroute(skb, out->ifindex, PF_INET6); + return selinux_ip_postroute(skb, out, PF_INET6); } #endif /* IPV6 */ diff --git a/security/selinux/include/netif.h b/security/selinux/include/netif.h index 57c6eae81eac..c72145444090 100644 --- a/security/selinux/include/netif.h +++ b/security/selinux/include/netif.h @@ -17,9 +17,11 @@ #ifndef _SELINUX_NETIF_H_ #define _SELINUX_NETIF_H_ +#include + void sel_netif_flush(void); -int sel_netif_sid(int ifindex, u32 *sid); +int sel_netif_sid(struct net *ns, int ifindex, u32 *sid); #endif /* _SELINUX_NETIF_H_ */ diff --git a/security/selinux/include/objsec.h b/security/selinux/include/objsec.h index 078e553f52f2..81fa718d5cb3 100644 --- a/security/selinux/include/objsec.h +++ b/security/selinux/include/objsec.h @@ -24,6 +24,7 @@ #include #include #include +#include #include "flask.h" #include "avc.h" @@ -78,6 +79,7 @@ struct ipc_security_struct { }; struct netif_security_struct { + struct net *ns; /* network namespace */ int ifindex; /* device index */ u32 sid; /* SID for this interface */ }; diff --git a/security/selinux/netif.c b/security/selinux/netif.c index 3c3de4ca0ebc..485524c477a4 100644 --- a/security/selinux/netif.c +++ b/security/selinux/netif.c @@ -45,6 +45,7 @@ static struct list_head sel_netif_hash[SEL_NETIF_HASH_SIZE]; /** * sel_netif_hashfn - Hashing function for the interface table + * @ns: the network namespace * @ifindex: the network interface * * Description: @@ -52,13 +53,14 @@ static struct list_head sel_netif_hash[SEL_NETIF_HASH_SIZE]; * bucket number for the given interface. * */ -static inline u32 sel_netif_hashfn(int ifindex) +static inline u32 sel_netif_hashfn(const struct net *ns, int ifindex) { - return (ifindex & (SEL_NETIF_HASH_SIZE - 1)); + return (((uintptr_t)ns + ifindex) & (SEL_NETIF_HASH_SIZE - 1)); } /** * sel_netif_find - Search for an interface record + * @ns: the network namespace * @ifindex: the network interface * * Description: @@ -66,15 +68,15 @@ static inline u32 sel_netif_hashfn(int ifindex) * If an entry can not be found in the table return NULL. * */ -static inline struct sel_netif *sel_netif_find(int ifindex) +static inline struct sel_netif *sel_netif_find(const struct net *ns, + int ifindex) { - int idx = sel_netif_hashfn(ifindex); + int idx = sel_netif_hashfn(ns, ifindex); struct sel_netif *netif; list_for_each_entry_rcu(netif, &sel_netif_hash[idx], list) - /* all of the devices should normally fit in the hash, so we - * optimize for that case */ - if (likely(netif->nsec.ifindex == ifindex)) + if (net_eq(netif->nsec.ns, ns) && + netif->nsec.ifindex == ifindex) return netif; return NULL; @@ -96,7 +98,7 @@ static int sel_netif_insert(struct sel_netif *netif) if (sel_netif_total >= SEL_NETIF_HASH_MAX) return -ENOSPC; - idx = sel_netif_hashfn(netif->nsec.ifindex); + idx = sel_netif_hashfn(netif->nsec.ns, netif->nsec.ifindex); list_add_rcu(&netif->list, &sel_netif_hash[idx]); sel_netif_total++; @@ -120,6 +122,7 @@ static void sel_netif_destroy(struct sel_netif *netif) /** * sel_netif_sid_slow - Lookup the SID of a network interface using the policy + * @ns: the network namespace * @ifindex: the network interface * @sid: interface SID * @@ -130,7 +133,7 @@ static void sel_netif_destroy(struct sel_netif *netif) * failure. * */ -static int sel_netif_sid_slow(int ifindex, u32 *sid) +static int sel_netif_sid_slow(struct net *ns, int ifindex, u32 *sid) { int ret; struct sel_netif *netif; @@ -140,7 +143,7 @@ static int sel_netif_sid_slow(int ifindex, u32 *sid) /* NOTE: we always use init's network namespace since we don't * currently support containers */ - dev = dev_get_by_index(&init_net, ifindex); + dev = dev_get_by_index(ns, ifindex); if (unlikely(dev == NULL)) { printk(KERN_WARNING "SELinux: failure in sel_netif_sid_slow()," @@ -149,7 +152,7 @@ static int sel_netif_sid_slow(int ifindex, u32 *sid) } spin_lock_bh(&sel_netif_lock); - netif = sel_netif_find(ifindex); + netif = sel_netif_find(ns, ifindex); if (netif != NULL) { *sid = netif->nsec.sid; ret = 0; @@ -163,6 +166,7 @@ static int sel_netif_sid_slow(int ifindex, u32 *sid) ret = security_netif_sid(dev->name, &new->nsec.sid); if (ret != 0) goto out; + new->nsec.ns = ns; new->nsec.ifindex = ifindex; ret = sel_netif_insert(new); if (ret != 0) @@ -184,6 +188,7 @@ out: /** * sel_netif_sid - Lookup the SID of a network interface + * @ns: the network namespace * @ifindex: the network interface * @sid: interface SID * @@ -195,12 +200,12 @@ out: * on failure. * */ -int sel_netif_sid(int ifindex, u32 *sid) +int sel_netif_sid(struct net *ns, int ifindex, u32 *sid) { struct sel_netif *netif; rcu_read_lock(); - netif = sel_netif_find(ifindex); + netif = sel_netif_find(ns, ifindex); if (likely(netif != NULL)) { *sid = netif->nsec.sid; rcu_read_unlock(); @@ -208,11 +213,12 @@ int sel_netif_sid(int ifindex, u32 *sid) } rcu_read_unlock(); - return sel_netif_sid_slow(ifindex, sid); + return sel_netif_sid_slow(ns, ifindex, sid); } /** * sel_netif_kill - Remove an entry from the network interface table + * @ns: the network namespace * @ifindex: the network interface * * Description: @@ -220,13 +226,13 @@ int sel_netif_sid(int ifindex, u32 *sid) * table if it exists. * */ -static void sel_netif_kill(int ifindex) +static void sel_netif_kill(const struct net *ns, int ifindex) { struct sel_netif *netif; rcu_read_lock(); spin_lock_bh(&sel_netif_lock); - netif = sel_netif_find(ifindex); + netif = sel_netif_find(ns, ifindex); if (netif) sel_netif_destroy(netif); spin_unlock_bh(&sel_netif_lock); @@ -257,11 +263,8 @@ static int sel_netif_netdev_notifier_handler(struct notifier_block *this, { struct net_device *dev = netdev_notifier_info_to_dev(ptr); - if (dev_net(dev) != &init_net) - return NOTIFY_DONE; - if (event == NETDEV_DOWN) - sel_netif_kill(dev->ifindex); + sel_netif_kill(dev_net(dev), dev->ifindex); return NOTIFY_DONE; } -- cgit v1.2.3 From e173fb2646a832b424c80904c306b816760ce477 Mon Sep 17 00:00:00 2001 From: Richard Guy Briggs Date: Thu, 18 Sep 2014 20:50:17 -0400 Subject: selinux: cleanup error reporting in selinux_nlmsg_perm() Convert audit_log() call to WARN_ONCE(). Rename "type=" to nlmsg_type=" to avoid confusion with the audit record type. Added "protocol=" to help track down which protocol (NETLINK_AUDIT?) was used within the netlink protocol family. Signed-off-by: Richard Guy Briggs [Rewrote the patch subject line] Signed-off-by: Paul Moore --- security/selinux/hooks.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) (limited to 'security/selinux/hooks.c') diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index 5eb512b7ac31..29e64d4ca099 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -4727,10 +4727,9 @@ static int selinux_nlmsg_perm(struct sock *sk, struct sk_buff *skb) err = selinux_nlmsg_lookup(sksec->sclass, nlh->nlmsg_type, &perm); if (err) { if (err == -EINVAL) { - audit_log(current->audit_context, GFP_KERNEL, AUDIT_SELINUX_ERR, - "SELinux: unrecognized netlink message" - " type=%hu for sclass=%hu\n", - nlh->nlmsg_type, sksec->sclass); + WARN_ONCE(1, "selinux_nlmsg_perm: unrecognized netlink message:" + " protocol=%hu nlmsg_type=%hu sclass=%hu\n", + sk->sk_protocol, nlh->nlmsg_type, sksec->sclass); if (!selinux_enforcing || security_get_allow_unknown()) err = 0; } -- cgit v1.2.3 From 923190d32de4428afbea5e5773be86bea60a9925 Mon Sep 17 00:00:00 2001 From: Stephen Smalley Date: Mon, 6 Oct 2014 16:32:52 -0400 Subject: selinux: fix inode security list corruption sb_finish_set_opts() can race with inode_free_security() when initializing inode security structures for inodes created prior to initial policy load or by the filesystem during ->mount(). This appears to have always been a possible race, but commit 3dc91d4 ("SELinux: Fix possible NULL pointer dereference in selinux_inode_permission()") made it more evident by immediately reusing the unioned list/rcu element of the inode security structure for call_rcu() upon an inode_free_security(). But the underlying issue was already present before that commit as a possible use-after-free of isec. Shivnandan Kumar reported the list corruption and proposed a patch to split the list and rcu elements out of the union as separate fields of the inode_security_struct so that setting the rcu element would not affect the list element. However, this would merely hide the issue and not truly fix the code. This patch instead moves up the deletion of the list entry prior to dropping the sbsec->isec_lock initially. Then, if the inode is dropped subsequently, there will be no further references to the isec. Reported-by: Shivnandan Kumar Signed-off-by: Stephen Smalley Cc: stable@vger.kernel.org Signed-off-by: Paul Moore --- security/selinux/hooks.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'security/selinux/hooks.c') diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index 29e64d4ca099..2478976fc894 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -481,6 +481,7 @@ next_inode: list_entry(sbsec->isec_head.next, struct inode_security_struct, list); struct inode *inode = isec->inode; + list_del_init(&isec->list); spin_unlock(&sbsec->isec_lock); inode = igrab(inode); if (inode) { @@ -489,7 +490,6 @@ next_inode: iput(inode); } spin_lock(&sbsec->isec_lock); - list_del_init(&isec->list); goto next_inode; } spin_unlock(&sbsec->isec_lock); -- cgit v1.2.3 From d950f84c1c6658faec2ecbf5b09f7e7191953394 Mon Sep 17 00:00:00 2001 From: Richard Guy Briggs Date: Wed, 12 Nov 2014 14:01:34 -0500 Subject: selinux: convert WARN_ONCE() to printk() in selinux_nlmsg_perm() Convert WARN_ONCE() to printk() in selinux_nlmsg_perm(). After conversion from audit_log() in commit e173fb26, WARN_ONCE() was deemed too alarmist, so switch it to printk(). Signed-off-by: Richard Guy Briggs [PM: Changed to printk(WARNING) so we catch all of the different invalid netlink messages. In Richard's defense, he brought this point up earlier, but I didn't understand his point at the time.] Signed-off-by: Paul Moore --- security/selinux/hooks.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'security/selinux/hooks.c') diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index 2478976fc894..654f0710620a 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -4727,9 +4727,10 @@ static int selinux_nlmsg_perm(struct sock *sk, struct sk_buff *skb) err = selinux_nlmsg_lookup(sksec->sclass, nlh->nlmsg_type, &perm); if (err) { if (err == -EINVAL) { - WARN_ONCE(1, "selinux_nlmsg_perm: unrecognized netlink message:" - " protocol=%hu nlmsg_type=%hu sclass=%hu\n", - sk->sk_protocol, nlh->nlmsg_type, sksec->sclass); + printk(KERN_WARNING + "SELinux: unrecognized netlink message:" + " protocol=%hu nlmsg_type=%hu sclass=%hu\n", + sk->sk_protocol, nlh->nlmsg_type, sksec->sclass); if (!selinux_enforcing || security_get_allow_unknown()) err = 0; } -- cgit v1.2.3