selinux: Implementation for inode_copy_up_xattr() hook
[linux-2.6-microblaze.git] / security / selinux / hooks.c
index a86d537..a2d5108 100644 (file)
@@ -830,6 +830,28 @@ static int selinux_set_mnt_opts(struct super_block *sb,
                        goto out;
                }
        }
+
+       /*
+        * If this is a user namespace mount, no contexts are allowed
+        * on the command line and security labels must be ignored.
+        */
+       if (sb->s_user_ns != &init_user_ns) {
+               if (context_sid || fscontext_sid || rootcontext_sid ||
+                   defcontext_sid) {
+                       rc = -EACCES;
+                       goto out;
+               }
+               if (sbsec->behavior == SECURITY_FS_USE_XATTR) {
+                       sbsec->behavior = SECURITY_FS_USE_MNTPOINT;
+                       rc = security_transition_sid(current_sid(), current_sid(),
+                                                    SECCLASS_FILE, NULL,
+                                                    &sbsec->mntpoint_sid);
+                       if (rc)
+                               goto out;
+               }
+               goto out_set_opts;
+       }
+
        /* sets the context of the superblock for the fs being mounted. */
        if (fscontext_sid) {
                rc = may_context_mount_sb_relabel(fscontext_sid, sbsec, cred);
@@ -898,6 +920,7 @@ static int selinux_set_mnt_opts(struct super_block *sb,
                sbsec->def_sid = defcontext_sid;
        }
 
+out_set_opts:
        rc = sb_finish_set_opts(sb);
 out:
        mutex_unlock(&sbsec->lock);
@@ -2259,7 +2282,7 @@ static int check_nnp_nosuid(const struct linux_binprm *bprm,
                            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 nosuid = !mnt_may_suid(bprm->file->f_path.mnt);
        int rc;
 
        if (!nnp && !nosuid)
@@ -2809,7 +2832,7 @@ static void selinux_inode_free_security(struct inode *inode)
 }
 
 static int selinux_dentry_init_security(struct dentry *dentry, int mode,
-                                       struct qstr *name, void **ctx,
+                                       const struct qstr *name, void **ctx,
                                        u32 *ctxlen)
 {
        u32 newsid;
@@ -3270,6 +3293,41 @@ static void selinux_inode_getsecid(struct inode *inode, u32 *secid)
        *secid = isec->sid;
 }
 
+static int selinux_inode_copy_up(struct dentry *src, struct cred **new)
+{
+       u32 sid;
+       struct task_security_struct *tsec;
+       struct cred *new_creds = *new;
+
+       if (new_creds == NULL) {
+               new_creds = prepare_creds();
+               if (!new_creds)
+                       return -ENOMEM;
+       }
+
+       tsec = new_creds->security;
+       /* Get label from overlay inode and set it in create_sid */
+       selinux_inode_getsecid(d_inode(src), &sid);
+       tsec->create_sid = sid;
+       *new = new_creds;
+       return 0;
+}
+
+static int selinux_inode_copy_up_xattr(const char *name)
+{
+       /* The copy_up hook above sets the initial context on an inode, but we
+        * don't then want to overwrite it by blindly copying all the lower
+        * xattrs up.  Instead, we have to filter out SELinux-related xattrs.
+        */
+       if (strcmp(name, XATTR_NAME_SELINUX) == 0)
+               return 1; /* Discard */
+       /*
+        * Any other attribute apart from SELINUX is not claimed, supported
+        * by selinux.
+        */
+       return -EOPNOTSUPP;
+}
+
 /* file security operations */
 
 static int selinux_revalidate_file_permission(struct file *file, int mask)
@@ -3961,7 +4019,7 @@ out:
        return ret;
 }
 
-#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+#if IS_ENABLED(CONFIG_IPV6)
 
 /* Returns error only if unable to parse addresses */
 static int selinux_parse_skb_ipv6(struct sk_buff *skb,
@@ -4052,7 +4110,7 @@ static int selinux_parse_skb(struct sk_buff *skb, struct common_audit_data *ad,
                                       &ad->u.net->v4info.daddr);
                goto okay;
 
-#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+#if IS_ENABLED(CONFIG_IPV6)
        case PF_INET6:
                ret = selinux_parse_skb_ipv6(skb, ad, proto);
                if (ret)
@@ -4604,13 +4662,13 @@ static int selinux_socket_sock_rcv_skb(struct sock *sk, struct sk_buff *skb)
                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);
+                       selinux_netlbl_err(skb, family, err, 0);
                        return err;
                }
                err = avc_has_perm(sk_sid, peer_sid, SECCLASS_PEER,
                                   PEER__RECV, &ad);
                if (err) {
-                       selinux_netlbl_err(skb, err, 0);
+                       selinux_netlbl_err(skb, family, err, 0);
                        return err;
                }
        }
@@ -4978,7 +5036,7 @@ static unsigned int selinux_ip_forward(struct sk_buff *skb,
                err = selinux_inet_sys_rcv_skb(dev_net(indev), indev->ifindex,
                                               addrp, family, peer_sid, &ad);
                if (err) {
-                       selinux_netlbl_err(skb, err, 1);
+                       selinux_netlbl_err(skb, family, err, 1);
                        return NF_DROP;
                }
        }
@@ -5006,7 +5064,7 @@ static unsigned int selinux_ipv4_forward(void *priv,
        return selinux_ip_forward(skb, state->in, PF_INET);
 }
 
-#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+#if IS_ENABLED(CONFIG_IPV6)
 static unsigned int selinux_ipv6_forward(void *priv,
                                         struct sk_buff *skb,
                                         const struct nf_hook_state *state)
@@ -5064,6 +5122,15 @@ static unsigned int selinux_ipv4_output(void *priv,
        return selinux_ip_output(skb, PF_INET);
 }
 
+#if IS_ENABLED(CONFIG_IPV6)
+static unsigned int selinux_ipv6_output(void *priv,
+                                       struct sk_buff *skb,
+                                       const struct nf_hook_state *state)
+{
+       return selinux_ip_output(skb, PF_INET6);
+}
+#endif /* IPV6 */
+
 static unsigned int selinux_ip_postroute_compat(struct sk_buff *skb,
                                                int ifindex,
                                                u16 family)
@@ -5241,7 +5308,7 @@ static unsigned int selinux_ipv4_postroute(void *priv,
        return selinux_ip_postroute(skb, state->out, PF_INET);
 }
 
-#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+#if IS_ENABLED(CONFIG_IPV6)
 static unsigned int selinux_ipv6_postroute(void *priv,
                                           struct sk_buff *skb,
                                           const struct nf_hook_state *state)
@@ -6056,6 +6123,8 @@ static struct security_hook_list selinux_hooks[] = {
        LSM_HOOK_INIT(inode_setsecurity, selinux_inode_setsecurity),
        LSM_HOOK_INIT(inode_listsecurity, selinux_inode_listsecurity),
        LSM_HOOK_INIT(inode_getsecid, selinux_inode_getsecid),
+       LSM_HOOK_INIT(inode_copy_up, selinux_inode_copy_up),
+       LSM_HOOK_INIT(inode_copy_up_xattr, selinux_inode_copy_up_xattr),
 
        LSM_HOOK_INIT(file_permission, selinux_file_permission),
        LSM_HOOK_INIT(file_alloc_security, selinux_file_alloc_security),
@@ -6285,7 +6354,7 @@ static struct nf_hook_ops selinux_nf_ops[] = {
                .hooknum =      NF_INET_LOCAL_OUT,
                .priority =     NF_IP_PRI_SELINUX_FIRST,
        },
-#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+#if IS_ENABLED(CONFIG_IPV6)
        {
                .hook =         selinux_ipv6_postroute,
                .pf =           NFPROTO_IPV6,
@@ -6298,6 +6367,12 @@ static struct nf_hook_ops selinux_nf_ops[] = {
                .hooknum =      NF_INET_FORWARD,
                .priority =     NF_IP6_PRI_SELINUX_FIRST,
        },
+       {
+               .hook =         selinux_ipv6_output,
+               .pf =           NFPROTO_IPV6,
+               .hooknum =      NF_INET_LOCAL_OUT,
+               .priority =     NF_IP6_PRI_SELINUX_FIRST,
+       },
 #endif /* IPV6 */
 };