Merge branch 'work.xattr' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs
[cascardo/linux.git] / security / selinux / hooks.c
index 13185a6..0850579 100644 (file)
@@ -507,14 +507,14 @@ static int sb_finish_set_opts(struct super_block *sb)
                   the root directory.  -ENODATA is ok, as this may be
                   the first boot of the SELinux kernel before we have
                   assigned xattr values to the filesystem. */
-               if (!root_inode->i_op->getxattr) {
+               if (!(root_inode->i_opflags & IOP_XATTR)) {
                        printk(KERN_WARNING "SELinux: (dev %s, type %s) has no "
                               "xattr support\n", sb->s_id, sb->s_type->name);
                        rc = -EOPNOTSUPP;
                        goto out;
                }
-               rc = root_inode->i_op->getxattr(root, root_inode,
-                                               XATTR_NAME_SELINUX, NULL, 0);
+
+               rc = __vfs_getxattr(root, root_inode, XATTR_NAME_SELINUX, NULL, 0);
                if (rc < 0 && rc != -ENODATA) {
                        if (rc == -EOPNOTSUPP)
                                printk(KERN_WARNING "SELinux: (dev %s, type "
@@ -1410,11 +1410,10 @@ static int inode_doinit_with_dentry(struct inode *inode, struct dentry *opt_dent
        case SECURITY_FS_USE_NATIVE:
                break;
        case SECURITY_FS_USE_XATTR:
-               if (!inode->i_op->getxattr) {
+               if (!(inode->i_opflags & IOP_XATTR)) {
                        isec->sid = sbsec->def_sid;
                        break;
                }
-
                /* Need a dentry, since the xattr API requires one.
                   Life would be simpler if we could just pass the inode. */
                if (opt_dentry) {
@@ -1445,14 +1444,12 @@ static int inode_doinit_with_dentry(struct inode *inode, struct dentry *opt_dent
                        goto out_unlock;
                }
                context[len] = '\0';
-               rc = inode->i_op->getxattr(dentry, inode, XATTR_NAME_SELINUX,
-                                          context, len);
+               rc = __vfs_getxattr(dentry, inode, XATTR_NAME_SELINUX, context, len);
                if (rc == -ERANGE) {
                        kfree(context);
 
                        /* Need a larger buffer.  Query for the right size. */
-                       rc = inode->i_op->getxattr(dentry, inode, XATTR_NAME_SELINUX,
-                                                  NULL, 0);
+                       rc = __vfs_getxattr(dentry, inode, XATTR_NAME_SELINUX, NULL, 0);
                        if (rc < 0) {
                                dput(dentry);
                                goto out_unlock;
@@ -1465,9 +1462,7 @@ static int inode_doinit_with_dentry(struct inode *inode, struct dentry *opt_dent
                                goto out_unlock;
                        }
                        context[len] = '\0';
-                       rc = inode->i_op->getxattr(dentry, inode,
-                                                  XATTR_NAME_SELINUX,
-                                                  context, len);
+                       rc = __vfs_getxattr(dentry, inode, XATTR_NAME_SELINUX, context, len);
                }
                dput(dentry);
                if (rc < 0) {
@@ -1761,8 +1756,8 @@ static inline int file_path_has_perm(const struct cred *cred,
 {
        struct common_audit_data ad;
 
-       ad.type = LSM_AUDIT_DATA_PATH;
-       ad.u.path = file->f_path;
+       ad.type = LSM_AUDIT_DATA_FILE;
+       ad.u.file = file;
        return inode_has_perm(cred, file_inode(file), av, &ad);
 }
 
@@ -1784,8 +1779,8 @@ static int file_has_perm(const struct cred *cred,
        u32 sid = cred_sid(cred);
        int rc;
 
-       ad.type = LSM_AUDIT_DATA_PATH;
-       ad.u.path = file->f_path;
+       ad.type = LSM_AUDIT_DATA_FILE;
+       ad.u.file = file;
 
        if (sid != fsec->sid) {
                rc = avc_has_perm(sid, fsec->sid,
@@ -1808,13 +1803,13 @@ out:
 /*
  * Determine the label for an inode that might be unioned.
  */
-static int selinux_determine_inode_label(struct inode *dir,
-                                        const struct qstr *name,
-                                        u16 tclass,
-                                        u32 *_new_isid)
+static int
+selinux_determine_inode_label(const struct task_security_struct *tsec,
+                                struct inode *dir,
+                                const struct qstr *name, u16 tclass,
+                                u32 *_new_isid)
 {
        const struct superblock_security_struct *sbsec = dir->i_sb->s_security;
-       const struct task_security_struct *tsec = current_security();
 
        if ((sbsec->flags & SE_SBINITIALIZED) &&
            (sbsec->behavior == SECURITY_FS_USE_MNTPOINT)) {
@@ -1857,8 +1852,8 @@ static int may_create(struct inode *dir,
        if (rc)
                return rc;
 
-       rc = selinux_determine_inode_label(dir, &dentry->d_name, tclass,
-                                          &newsid);
+       rc = selinux_determine_inode_label(current_security(), dir,
+                                          &dentry->d_name, tclass, &newsid);
        if (rc)
                return rc;
 
@@ -2365,8 +2360,8 @@ static int selinux_bprm_set_creds(struct linux_binprm *bprm)
                        new_tsec->sid = old_tsec->sid;
        }
 
-       ad.type = LSM_AUDIT_DATA_PATH;
-       ad.u.path = bprm->file->f_path;
+       ad.type = LSM_AUDIT_DATA_FILE;
+       ad.u.file = bprm->file;
 
        if (new_tsec->sid == old_tsec->sid) {
                rc = avc_has_perm(old_tsec->sid, isec->sid,
@@ -2838,7 +2833,8 @@ static int selinux_dentry_init_security(struct dentry *dentry, int mode,
        u32 newsid;
        int rc;
 
-       rc = selinux_determine_inode_label(d_inode(dentry->d_parent), name,
+       rc = selinux_determine_inode_label(current_security(),
+                                          d_inode(dentry->d_parent), name,
                                           inode_mode_to_security_class(mode),
                                           &newsid);
        if (rc)
@@ -2847,6 +2843,27 @@ static int selinux_dentry_init_security(struct dentry *dentry, int mode,
        return security_sid_to_context(newsid, (char **)ctx, ctxlen);
 }
 
+static int selinux_dentry_create_files_as(struct dentry *dentry, int mode,
+                                         struct qstr *name,
+                                         const struct cred *old,
+                                         struct cred *new)
+{
+       u32 newsid;
+       int rc;
+       struct task_security_struct *tsec;
+
+       rc = selinux_determine_inode_label(old->security,
+                                          d_inode(dentry->d_parent), name,
+                                          inode_mode_to_security_class(mode),
+                                          &newsid);
+       if (rc)
+               return rc;
+
+       tsec = new->security;
+       tsec->create_sid = newsid;
+       return 0;
+}
+
 static int selinux_inode_init_security(struct inode *inode, struct inode *dir,
                                       const struct qstr *qstr,
                                       const char **name,
@@ -2863,7 +2880,7 @@ static int selinux_inode_init_security(struct inode *inode, struct inode *dir,
        sid = tsec->sid;
        newsid = tsec->create_sid;
 
-       rc = selinux_determine_inode_label(
+       rc = selinux_determine_inode_label(current_security(),
                dir, qstr,
                inode_mode_to_security_class(inode->i_mode),
                &newsid);
@@ -3293,6 +3310,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)
@@ -3776,8 +3828,8 @@ static int selinux_kernel_module_from_file(struct file *file)
 
        /* finit_module */
 
-       ad.type = LSM_AUDIT_DATA_PATH;
-       ad.u.path = file->f_path;
+       ad.type = LSM_AUDIT_DATA_FILE;
+       ad.u.file = file;
 
        fsec = file->f_security;
        if (sid != fsec->sid) {
@@ -3984,7 +4036,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,
@@ -4075,7 +4127,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)
@@ -5029,7 +5081,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)
@@ -5087,7 +5139,7 @@ static unsigned int selinux_ipv4_output(void *priv,
        return selinux_ip_output(skb, PF_INET);
 }
 
-#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+#if IS_ENABLED(CONFIG_IPV6)
 static unsigned int selinux_ipv6_output(void *priv,
                                        struct sk_buff *skb,
                                        const struct nf_hook_state *state)
@@ -5273,7 +5325,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)
@@ -6062,6 +6114,7 @@ static struct security_hook_list selinux_hooks[] = {
        LSM_HOOK_INIT(sb_parse_opts_str, selinux_parse_opts_str),
 
        LSM_HOOK_INIT(dentry_init_security, selinux_dentry_init_security),
+       LSM_HOOK_INIT(dentry_create_files_as, selinux_dentry_create_files_as),
 
        LSM_HOOK_INIT(inode_alloc_security, selinux_inode_alloc_security),
        LSM_HOOK_INIT(inode_free_security, selinux_inode_free_security),
@@ -6088,6 +6141,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),
@@ -6317,7 +6372,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,