]> git.proxmox.com Git - mirror_ubuntu-bionic-kernel.git/blobdiff - security/selinux/hooks.c
lsm,audit,selinux: Introduce a new audit data type LSM_AUDIT_DATA_FILE
[mirror_ubuntu-bionic-kernel.git] / security / selinux / hooks.c
index a86d537eb79b149a7dfe1536a243e180f4b9ec92..2205ea27aa0afb03e1aaa9120158b8b684bef46d 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);
@@ -1738,8 +1761,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);
 }
 
@@ -1761,8 +1784,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,
@@ -1785,13 +1808,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)) {
@@ -1834,8 +1857,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;
 
@@ -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)
@@ -2342,8 +2365,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,
@@ -2809,13 +2832,14 @@ 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;
        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)
@@ -2824,6 +2848,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,
@@ -2840,7 +2885,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);
@@ -3270,6 +3315,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)
@@ -3753,8 +3833,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) {
@@ -3961,7 +4041,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 +4132,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 +4684,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 +5058,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 +5086,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 +5144,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 +5330,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)
@@ -6030,6 +6119,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),
@@ -6056,6 +6146,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 +6377,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 +6390,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 */
 };