|
|
@@ -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);
|
|
|
@@ -4272,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,
|
|
|
@@ -4373,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;
|
|
|
@@ -4692,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;
|
|
|
}
|
|
|
@@ -4713,7 +4747,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;
|
|
|
@@ -4739,14 +4774,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;
|
|
|
@@ -4775,7 +4810,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)
|
|
|
@@ -4785,7 +4820,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 */
|
|
|
|
|
|
@@ -4873,11 +4908,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,};
|
|
|
@@ -4958,6 +4995,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);
|
|
|
}
|
|
|
@@ -4989,7 +5027,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))
|
|
|
@@ -5011,7 +5049,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)
|
|
|
@@ -5021,7 +5059,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 */
|
|
|
|
|
|
@@ -6035,7 +6073,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,
|
|
|
@@ -6056,12 +6094,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,
|
|
|
@@ -6075,32 +6109,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));
|
|
|
+ err = nf_register_hooks(selinux_nf_ops, ARRAY_SIZE(selinux_nf_ops));
|
|
|
if (err)
|
|
|
- panic("SELinux: nf_register_hooks for IPv4: error %d\n", err);
|
|
|
+ panic("SELinux: nf_register_hooks: error %d\n", err);
|
|
|
|
|
|
-#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
|
|
|
- err = nf_register_hooks(selinux_ipv6_ops, ARRAY_SIZE(selinux_ipv6_ops));
|
|
|
- if (err)
|
|
|
- panic("SELinux: nf_register_hooks for IPv6: error %d\n", err);
|
|
|
-#endif /* IPV6 */
|
|
|
-
|
|
|
-out:
|
|
|
- return err;
|
|
|
+ return 0;
|
|
|
}
|
|
|
|
|
|
__initcall(selinux_nf_ip_init);
|
|
|
@@ -6110,10 +6136,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
|
|
|
|