diff options
author | Chris Zankel <chris@zankel.net> | 2014-10-21 00:26:01 -0400 |
---|---|---|
committer | Chris Zankel <chris@zankel.net> | 2014-10-21 00:26:01 -0400 |
commit | a13926db3e5ea5bbac297f59e9f35061f52892d3 (patch) | |
tree | b794f25550b7dbbe9cc8eda81633df9023b4821c /security/selinux/hooks.c | |
parent | f2589bff1ce8b94cebc044e5dfeac4d4e8701cbc (diff) | |
parent | f114040e3ea6e07372334ade75d1ee0775c355e1 (diff) |
Merge tag 'v3.18-rc1' into for_next
Linux 3.18-rc1
Diffstat (limited to 'security/selinux/hooks.c')
-rw-r--r-- | security/selinux/hooks.c | 141 |
1 files changed, 81 insertions, 60 deletions
diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index b0e940497e23..e66314138b38 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c | |||
@@ -481,6 +481,7 @@ next_inode: | |||
481 | list_entry(sbsec->isec_head.next, | 481 | list_entry(sbsec->isec_head.next, |
482 | struct inode_security_struct, list); | 482 | struct inode_security_struct, list); |
483 | struct inode *inode = isec->inode; | 483 | struct inode *inode = isec->inode; |
484 | list_del_init(&isec->list); | ||
484 | spin_unlock(&sbsec->isec_lock); | 485 | spin_unlock(&sbsec->isec_lock); |
485 | inode = igrab(inode); | 486 | inode = igrab(inode); |
486 | if (inode) { | 487 | if (inode) { |
@@ -489,7 +490,6 @@ next_inode: | |||
489 | iput(inode); | 490 | iput(inode); |
490 | } | 491 | } |
491 | spin_lock(&sbsec->isec_lock); | 492 | spin_lock(&sbsec->isec_lock); |
492 | list_del_init(&isec->list); | ||
493 | goto next_inode; | 493 | goto next_inode; |
494 | } | 494 | } |
495 | spin_unlock(&sbsec->isec_lock); | 495 | spin_unlock(&sbsec->isec_lock); |
@@ -2097,6 +2097,41 @@ static int selinux_vm_enough_memory(struct mm_struct *mm, long pages) | |||
2097 | 2097 | ||
2098 | /* binprm security operations */ | 2098 | /* binprm security operations */ |
2099 | 2099 | ||
2100 | static int check_nnp_nosuid(const struct linux_binprm *bprm, | ||
2101 | const struct task_security_struct *old_tsec, | ||
2102 | const struct task_security_struct *new_tsec) | ||
2103 | { | ||
2104 | int nnp = (bprm->unsafe & LSM_UNSAFE_NO_NEW_PRIVS); | ||
2105 | int nosuid = (bprm->file->f_path.mnt->mnt_flags & MNT_NOSUID); | ||
2106 | int rc; | ||
2107 | |||
2108 | if (!nnp && !nosuid) | ||
2109 | return 0; /* neither NNP nor nosuid */ | ||
2110 | |||
2111 | if (new_tsec->sid == old_tsec->sid) | ||
2112 | return 0; /* No change in credentials */ | ||
2113 | |||
2114 | /* | ||
2115 | * The only transitions we permit under NNP or nosuid | ||
2116 | * are transitions to bounded SIDs, i.e. SIDs that are | ||
2117 | * guaranteed to only be allowed a subset of the permissions | ||
2118 | * of the current SID. | ||
2119 | */ | ||
2120 | rc = security_bounded_transition(old_tsec->sid, new_tsec->sid); | ||
2121 | if (rc) { | ||
2122 | /* | ||
2123 | * On failure, preserve the errno values for NNP vs nosuid. | ||
2124 | * NNP: Operation not permitted for caller. | ||
2125 | * nosuid: Permission denied to file. | ||
2126 | */ | ||
2127 | if (nnp) | ||
2128 | return -EPERM; | ||
2129 | else | ||
2130 | return -EACCES; | ||
2131 | } | ||
2132 | return 0; | ||
2133 | } | ||
2134 | |||
2100 | static int selinux_bprm_set_creds(struct linux_binprm *bprm) | 2135 | static int selinux_bprm_set_creds(struct linux_binprm *bprm) |
2101 | { | 2136 | { |
2102 | const struct task_security_struct *old_tsec; | 2137 | const struct task_security_struct *old_tsec; |
@@ -2133,14 +2168,10 @@ static int selinux_bprm_set_creds(struct linux_binprm *bprm) | |||
2133 | /* Reset exec SID on execve. */ | 2168 | /* Reset exec SID on execve. */ |
2134 | new_tsec->exec_sid = 0; | 2169 | new_tsec->exec_sid = 0; |
2135 | 2170 | ||
2136 | /* | 2171 | /* Fail on NNP or nosuid if not an allowed transition. */ |
2137 | * Minimize confusion: if no_new_privs or nosuid and a | 2172 | rc = check_nnp_nosuid(bprm, old_tsec, new_tsec); |
2138 | * transition is explicitly requested, then fail the exec. | 2173 | if (rc) |
2139 | */ | 2174 | return rc; |
2140 | if (bprm->unsafe & LSM_UNSAFE_NO_NEW_PRIVS) | ||
2141 | return -EPERM; | ||
2142 | if (bprm->file->f_path.mnt->mnt_flags & MNT_NOSUID) | ||
2143 | return -EACCES; | ||
2144 | } else { | 2175 | } else { |
2145 | /* Check for a default transition on this program. */ | 2176 | /* Check for a default transition on this program. */ |
2146 | rc = security_transition_sid(old_tsec->sid, isec->sid, | 2177 | rc = security_transition_sid(old_tsec->sid, isec->sid, |
@@ -2148,15 +2179,19 @@ static int selinux_bprm_set_creds(struct linux_binprm *bprm) | |||
2148 | &new_tsec->sid); | 2179 | &new_tsec->sid); |
2149 | if (rc) | 2180 | if (rc) |
2150 | return rc; | 2181 | return rc; |
2182 | |||
2183 | /* | ||
2184 | * Fallback to old SID on NNP or nosuid if not an allowed | ||
2185 | * transition. | ||
2186 | */ | ||
2187 | rc = check_nnp_nosuid(bprm, old_tsec, new_tsec); | ||
2188 | if (rc) | ||
2189 | new_tsec->sid = old_tsec->sid; | ||
2151 | } | 2190 | } |
2152 | 2191 | ||
2153 | ad.type = LSM_AUDIT_DATA_PATH; | 2192 | ad.type = LSM_AUDIT_DATA_PATH; |
2154 | ad.u.path = bprm->file->f_path; | 2193 | ad.u.path = bprm->file->f_path; |
2155 | 2194 | ||
2156 | if ((bprm->file->f_path.mnt->mnt_flags & MNT_NOSUID) || | ||
2157 | (bprm->unsafe & LSM_UNSAFE_NO_NEW_PRIVS)) | ||
2158 | new_tsec->sid = old_tsec->sid; | ||
2159 | |||
2160 | if (new_tsec->sid == old_tsec->sid) { | 2195 | if (new_tsec->sid == old_tsec->sid) { |
2161 | rc = avc_has_perm(old_tsec->sid, isec->sid, | 2196 | rc = avc_has_perm(old_tsec->sid, isec->sid, |
2162 | SECCLASS_FILE, FILE__EXECUTE_NO_TRANS, &ad); | 2197 | SECCLASS_FILE, FILE__EXECUTE_NO_TRANS, &ad); |
@@ -3346,14 +3381,12 @@ static int selinux_file_fcntl(struct file *file, unsigned int cmd, | |||
3346 | return err; | 3381 | return err; |
3347 | } | 3382 | } |
3348 | 3383 | ||
3349 | static int selinux_file_set_fowner(struct file *file) | 3384 | static void selinux_file_set_fowner(struct file *file) |
3350 | { | 3385 | { |
3351 | struct file_security_struct *fsec; | 3386 | struct file_security_struct *fsec; |
3352 | 3387 | ||
3353 | fsec = file->f_security; | 3388 | fsec = file->f_security; |
3354 | fsec->fown_sid = current_sid(); | 3389 | fsec->fown_sid = current_sid(); |
3355 | |||
3356 | return 0; | ||
3357 | } | 3390 | } |
3358 | 3391 | ||
3359 | static int selinux_file_send_sigiotask(struct task_struct *tsk, | 3392 | static int selinux_file_send_sigiotask(struct task_struct *tsk, |
@@ -4272,15 +4305,15 @@ static int selinux_socket_unix_may_send(struct socket *sock, | |||
4272 | &ad); | 4305 | &ad); |
4273 | } | 4306 | } |
4274 | 4307 | ||
4275 | static int selinux_inet_sys_rcv_skb(int ifindex, char *addrp, u16 family, | 4308 | static int selinux_inet_sys_rcv_skb(struct net *ns, int ifindex, |
4276 | u32 peer_sid, | 4309 | char *addrp, u16 family, u32 peer_sid, |
4277 | struct common_audit_data *ad) | 4310 | struct common_audit_data *ad) |
4278 | { | 4311 | { |
4279 | int err; | 4312 | int err; |
4280 | u32 if_sid; | 4313 | u32 if_sid; |
4281 | u32 node_sid; | 4314 | u32 node_sid; |
4282 | 4315 | ||
4283 | err = sel_netif_sid(ifindex, &if_sid); | 4316 | err = sel_netif_sid(ns, ifindex, &if_sid); |
4284 | if (err) | 4317 | if (err) |
4285 | return err; | 4318 | return err; |
4286 | err = avc_has_perm(peer_sid, if_sid, | 4319 | err = avc_has_perm(peer_sid, if_sid, |
@@ -4373,8 +4406,8 @@ static int selinux_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb) | |||
4373 | err = selinux_skb_peerlbl_sid(skb, family, &peer_sid); | 4406 | err = selinux_skb_peerlbl_sid(skb, family, &peer_sid); |
4374 | if (err) | 4407 | if (err) |
4375 | return err; | 4408 | return err; |
4376 | err = selinux_inet_sys_rcv_skb(skb->skb_iif, addrp, family, | 4409 | err = selinux_inet_sys_rcv_skb(sock_net(sk), skb->skb_iif, |
4377 | peer_sid, &ad); | 4410 | addrp, family, peer_sid, &ad); |
4378 | if (err) { | 4411 | if (err) { |
4379 | selinux_netlbl_err(skb, err, 0); | 4412 | selinux_netlbl_err(skb, err, 0); |
4380 | return err; | 4413 | return err; |
@@ -4692,10 +4725,9 @@ static int selinux_nlmsg_perm(struct sock *sk, struct sk_buff *skb) | |||
4692 | err = selinux_nlmsg_lookup(sksec->sclass, nlh->nlmsg_type, &perm); | 4725 | err = selinux_nlmsg_lookup(sksec->sclass, nlh->nlmsg_type, &perm); |
4693 | if (err) { | 4726 | if (err) { |
4694 | if (err == -EINVAL) { | 4727 | if (err == -EINVAL) { |
4695 | audit_log(current->audit_context, GFP_KERNEL, AUDIT_SELINUX_ERR, | 4728 | WARN_ONCE(1, "selinux_nlmsg_perm: unrecognized netlink message:" |
4696 | "SELinux: unrecognized netlink message" | 4729 | " protocol=%hu nlmsg_type=%hu sclass=%hu\n", |
4697 | " type=%hu for sclass=%hu\n", | 4730 | sk->sk_protocol, nlh->nlmsg_type, sksec->sclass); |
4698 | nlh->nlmsg_type, sksec->sclass); | ||
4699 | if (!selinux_enforcing || security_get_allow_unknown()) | 4731 | if (!selinux_enforcing || security_get_allow_unknown()) |
4700 | err = 0; | 4732 | err = 0; |
4701 | } | 4733 | } |
@@ -4713,7 +4745,8 @@ out: | |||
4713 | 4745 | ||
4714 | #ifdef CONFIG_NETFILTER | 4746 | #ifdef CONFIG_NETFILTER |
4715 | 4747 | ||
4716 | static unsigned int selinux_ip_forward(struct sk_buff *skb, int ifindex, | 4748 | static unsigned int selinux_ip_forward(struct sk_buff *skb, |
4749 | const struct net_device *indev, | ||
4717 | u16 family) | 4750 | u16 family) |
4718 | { | 4751 | { |
4719 | int err; | 4752 | int err; |
@@ -4739,14 +4772,14 @@ static unsigned int selinux_ip_forward(struct sk_buff *skb, int ifindex, | |||
4739 | 4772 | ||
4740 | ad.type = LSM_AUDIT_DATA_NET; | 4773 | ad.type = LSM_AUDIT_DATA_NET; |
4741 | ad.u.net = &net; | 4774 | ad.u.net = &net; |
4742 | ad.u.net->netif = ifindex; | 4775 | ad.u.net->netif = indev->ifindex; |
4743 | ad.u.net->family = family; | 4776 | ad.u.net->family = family; |
4744 | if (selinux_parse_skb(skb, &ad, &addrp, 1, NULL) != 0) | 4777 | if (selinux_parse_skb(skb, &ad, &addrp, 1, NULL) != 0) |
4745 | return NF_DROP; | 4778 | return NF_DROP; |
4746 | 4779 | ||
4747 | if (peerlbl_active) { | 4780 | if (peerlbl_active) { |
4748 | err = selinux_inet_sys_rcv_skb(ifindex, addrp, family, | 4781 | err = selinux_inet_sys_rcv_skb(dev_net(indev), indev->ifindex, |
4749 | peer_sid, &ad); | 4782 | addrp, family, peer_sid, &ad); |
4750 | if (err) { | 4783 | if (err) { |
4751 | selinux_netlbl_err(skb, err, 1); | 4784 | selinux_netlbl_err(skb, err, 1); |
4752 | return NF_DROP; | 4785 | return NF_DROP; |
@@ -4775,7 +4808,7 @@ static unsigned int selinux_ipv4_forward(const struct nf_hook_ops *ops, | |||
4775 | const struct net_device *out, | 4808 | const struct net_device *out, |
4776 | int (*okfn)(struct sk_buff *)) | 4809 | int (*okfn)(struct sk_buff *)) |
4777 | { | 4810 | { |
4778 | return selinux_ip_forward(skb, in->ifindex, PF_INET); | 4811 | return selinux_ip_forward(skb, in, PF_INET); |
4779 | } | 4812 | } |
4780 | 4813 | ||
4781 | #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) | 4814 | #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) |
@@ -4785,7 +4818,7 @@ static unsigned int selinux_ipv6_forward(const struct nf_hook_ops *ops, | |||
4785 | const struct net_device *out, | 4818 | const struct net_device *out, |
4786 | int (*okfn)(struct sk_buff *)) | 4819 | int (*okfn)(struct sk_buff *)) |
4787 | { | 4820 | { |
4788 | return selinux_ip_forward(skb, in->ifindex, PF_INET6); | 4821 | return selinux_ip_forward(skb, in, PF_INET6); |
4789 | } | 4822 | } |
4790 | #endif /* IPV6 */ | 4823 | #endif /* IPV6 */ |
4791 | 4824 | ||
@@ -4873,11 +4906,13 @@ static unsigned int selinux_ip_postroute_compat(struct sk_buff *skb, | |||
4873 | return NF_ACCEPT; | 4906 | return NF_ACCEPT; |
4874 | } | 4907 | } |
4875 | 4908 | ||
4876 | static unsigned int selinux_ip_postroute(struct sk_buff *skb, int ifindex, | 4909 | static unsigned int selinux_ip_postroute(struct sk_buff *skb, |
4910 | const struct net_device *outdev, | ||
4877 | u16 family) | 4911 | u16 family) |
4878 | { | 4912 | { |
4879 | u32 secmark_perm; | 4913 | u32 secmark_perm; |
4880 | u32 peer_sid; | 4914 | u32 peer_sid; |
4915 | int ifindex = outdev->ifindex; | ||
4881 | struct sock *sk; | 4916 | struct sock *sk; |
4882 | struct common_audit_data ad; | 4917 | struct common_audit_data ad; |
4883 | struct lsm_network_audit net = {0,}; | 4918 | struct lsm_network_audit net = {0,}; |
@@ -4958,6 +4993,7 @@ static unsigned int selinux_ip_postroute(struct sk_buff *skb, int ifindex, | |||
4958 | case PF_INET6: | 4993 | case PF_INET6: |
4959 | if (IP6CB(skb)->flags & IP6SKB_XFRM_TRANSFORMED) | 4994 | if (IP6CB(skb)->flags & IP6SKB_XFRM_TRANSFORMED) |
4960 | return NF_ACCEPT; | 4995 | return NF_ACCEPT; |
4996 | break; | ||
4961 | default: | 4997 | default: |
4962 | return NF_DROP_ERR(-ECONNREFUSED); | 4998 | return NF_DROP_ERR(-ECONNREFUSED); |
4963 | } | 4999 | } |
@@ -4989,7 +5025,7 @@ static unsigned int selinux_ip_postroute(struct sk_buff *skb, int ifindex, | |||
4989 | u32 if_sid; | 5025 | u32 if_sid; |
4990 | u32 node_sid; | 5026 | u32 node_sid; |
4991 | 5027 | ||
4992 | if (sel_netif_sid(ifindex, &if_sid)) | 5028 | if (sel_netif_sid(dev_net(outdev), ifindex, &if_sid)) |
4993 | return NF_DROP; | 5029 | return NF_DROP; |
4994 | if (avc_has_perm(peer_sid, if_sid, | 5030 | if (avc_has_perm(peer_sid, if_sid, |
4995 | SECCLASS_NETIF, NETIF__EGRESS, &ad)) | 5031 | SECCLASS_NETIF, NETIF__EGRESS, &ad)) |
@@ -5011,7 +5047,7 @@ static unsigned int selinux_ipv4_postroute(const struct nf_hook_ops *ops, | |||
5011 | const struct net_device *out, | 5047 | const struct net_device *out, |
5012 | int (*okfn)(struct sk_buff *)) | 5048 | int (*okfn)(struct sk_buff *)) |
5013 | { | 5049 | { |
5014 | return selinux_ip_postroute(skb, out->ifindex, PF_INET); | 5050 | return selinux_ip_postroute(skb, out, PF_INET); |
5015 | } | 5051 | } |
5016 | 5052 | ||
5017 | #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) | 5053 | #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) |
@@ -5021,7 +5057,7 @@ static unsigned int selinux_ipv6_postroute(const struct nf_hook_ops *ops, | |||
5021 | const struct net_device *out, | 5057 | const struct net_device *out, |
5022 | int (*okfn)(struct sk_buff *)) | 5058 | int (*okfn)(struct sk_buff *)) |
5023 | { | 5059 | { |
5024 | return selinux_ip_postroute(skb, out->ifindex, PF_INET6); | 5060 | return selinux_ip_postroute(skb, out, PF_INET6); |
5025 | } | 5061 | } |
5026 | #endif /* IPV6 */ | 5062 | #endif /* IPV6 */ |
5027 | 5063 | ||
@@ -6035,7 +6071,7 @@ security_initcall(selinux_init); | |||
6035 | 6071 | ||
6036 | #if defined(CONFIG_NETFILTER) | 6072 | #if defined(CONFIG_NETFILTER) |
6037 | 6073 | ||
6038 | static struct nf_hook_ops selinux_ipv4_ops[] = { | 6074 | static struct nf_hook_ops selinux_nf_ops[] = { |
6039 | { | 6075 | { |
6040 | .hook = selinux_ipv4_postroute, | 6076 | .hook = selinux_ipv4_postroute, |
6041 | .owner = THIS_MODULE, | 6077 | .owner = THIS_MODULE, |
@@ -6056,12 +6092,8 @@ static struct nf_hook_ops selinux_ipv4_ops[] = { | |||
6056 | .pf = NFPROTO_IPV4, | 6092 | .pf = NFPROTO_IPV4, |
6057 | .hooknum = NF_INET_LOCAL_OUT, | 6093 | .hooknum = NF_INET_LOCAL_OUT, |
6058 | .priority = NF_IP_PRI_SELINUX_FIRST, | 6094 | .priority = NF_IP_PRI_SELINUX_FIRST, |
6059 | } | 6095 | }, |
6060 | }; | ||
6061 | |||
6062 | #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) | 6096 | #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) |
6063 | |||
6064 | static struct nf_hook_ops selinux_ipv6_ops[] = { | ||
6065 | { | 6097 | { |
6066 | .hook = selinux_ipv6_postroute, | 6098 | .hook = selinux_ipv6_postroute, |
6067 | .owner = THIS_MODULE, | 6099 | .owner = THIS_MODULE, |
@@ -6075,32 +6107,24 @@ static struct nf_hook_ops selinux_ipv6_ops[] = { | |||
6075 | .pf = NFPROTO_IPV6, | 6107 | .pf = NFPROTO_IPV6, |
6076 | .hooknum = NF_INET_FORWARD, | 6108 | .hooknum = NF_INET_FORWARD, |
6077 | .priority = NF_IP6_PRI_SELINUX_FIRST, | 6109 | .priority = NF_IP6_PRI_SELINUX_FIRST, |
6078 | } | 6110 | }, |
6079 | }; | ||
6080 | |||
6081 | #endif /* IPV6 */ | 6111 | #endif /* IPV6 */ |
6112 | }; | ||
6082 | 6113 | ||
6083 | static int __init selinux_nf_ip_init(void) | 6114 | static int __init selinux_nf_ip_init(void) |
6084 | { | 6115 | { |
6085 | int err = 0; | 6116 | int err; |
6086 | 6117 | ||
6087 | if (!selinux_enabled) | 6118 | if (!selinux_enabled) |
6088 | goto out; | 6119 | return 0; |
6089 | 6120 | ||
6090 | printk(KERN_DEBUG "SELinux: Registering netfilter hooks\n"); | 6121 | printk(KERN_DEBUG "SELinux: Registering netfilter hooks\n"); |
6091 | 6122 | ||
6092 | err = nf_register_hooks(selinux_ipv4_ops, ARRAY_SIZE(selinux_ipv4_ops)); | 6123 | err = nf_register_hooks(selinux_nf_ops, ARRAY_SIZE(selinux_nf_ops)); |
6093 | if (err) | ||
6094 | panic("SELinux: nf_register_hooks for IPv4: error %d\n", err); | ||
6095 | |||
6096 | #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) | ||
6097 | err = nf_register_hooks(selinux_ipv6_ops, ARRAY_SIZE(selinux_ipv6_ops)); | ||
6098 | if (err) | 6124 | if (err) |
6099 | panic("SELinux: nf_register_hooks for IPv6: error %d\n", err); | 6125 | panic("SELinux: nf_register_hooks: error %d\n", err); |
6100 | #endif /* IPV6 */ | ||
6101 | 6126 | ||
6102 | out: | 6127 | return 0; |
6103 | return err; | ||
6104 | } | 6128 | } |
6105 | 6129 | ||
6106 | __initcall(selinux_nf_ip_init); | 6130 | __initcall(selinux_nf_ip_init); |
@@ -6110,10 +6134,7 @@ static void selinux_nf_ip_exit(void) | |||
6110 | { | 6134 | { |
6111 | printk(KERN_DEBUG "SELinux: Unregistering netfilter hooks\n"); | 6135 | printk(KERN_DEBUG "SELinux: Unregistering netfilter hooks\n"); |
6112 | 6136 | ||
6113 | nf_unregister_hooks(selinux_ipv4_ops, ARRAY_SIZE(selinux_ipv4_ops)); | 6137 | nf_unregister_hooks(selinux_nf_ops, ARRAY_SIZE(selinux_nf_ops)); |
6114 | #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) | ||
6115 | nf_unregister_hooks(selinux_ipv6_ops, ARRAY_SIZE(selinux_ipv6_ops)); | ||
6116 | #endif /* IPV6 */ | ||
6117 | } | 6138 | } |
6118 | #endif | 6139 | #endif |
6119 | 6140 | ||