Merge branch 'stable-4.8' of git://git.infradead.org/users/pcmoore/selinux into next
[cascardo/linux.git] / security / selinux / hooks.c
index 912deee..da93434 100644 (file)
@@ -259,7 +259,7 @@ static int __inode_security_revalidate(struct inode *inode,
 
        might_sleep_if(may_sleep);
 
-       if (isec->initialized == LABEL_INVALID) {
+       if (ss_initialized && isec->initialized != LABEL_INITIALIZED) {
                if (!may_sleep)
                        return -ECHILD;
 
@@ -297,6 +297,13 @@ static struct inode_security_struct *inode_security(struct inode *inode)
        return inode->i_security;
 }
 
+static struct inode_security_struct *backing_inode_security_novalidate(struct dentry *dentry)
+{
+       struct inode *inode = d_backing_inode(dentry);
+
+       return inode->i_security;
+}
+
 /*
  * Get the security label of a dentry's backing inode.
  */
@@ -506,7 +513,8 @@ static int sb_finish_set_opts(struct super_block *sb)
                        rc = -EOPNOTSUPP;
                        goto out;
                }
-               rc = root_inode->i_op->getxattr(root, XATTR_NAME_SELINUX, NULL, 0);
+               rc = root_inode->i_op->getxattr(root, root_inode,
+                                               XATTR_NAME_SELINUX, NULL, 0);
                if (rc < 0 && rc != -ENODATA) {
                        if (rc == -EOPNOTSUPP)
                                printk(KERN_WARNING "SELinux: (dev %s, type "
@@ -686,7 +694,7 @@ static int selinux_set_mnt_opts(struct super_block *sb,
        struct superblock_security_struct *sbsec = sb->s_security;
        const char *name = sb->s_type->name;
        struct dentry *root = sbsec->sb->s_root;
-       struct inode_security_struct *root_isec = backing_inode_security(root);
+       struct inode_security_struct *root_isec;
        u32 fscontext_sid = 0, context_sid = 0, rootcontext_sid = 0;
        u32 defcontext_sid = 0;
        char **mount_options = opts->mnt_opts;
@@ -729,6 +737,8 @@ static int selinux_set_mnt_opts(struct super_block *sb,
            && (num_opts == 0))
                goto out;
 
+       root_isec = backing_inode_security_novalidate(root);
+
        /*
         * parse the mount options, check if they are valid sids.
         * also check if someone is trying to mount the same sb more
@@ -1316,7 +1326,7 @@ static int selinux_genfs_get_sid(struct dentry *dentry,
                                 u32 *sid)
 {
        int rc;
-       struct super_block *sb = dentry->d_inode->i_sb;
+       struct super_block *sb = dentry->d_sb;
        char *buffer, *path;
 
        buffer = (char *)__get_free_page(GFP_KERNEL);
@@ -1412,13 +1422,13 @@ 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, XATTR_NAME_SELINUX,
+               rc = inode->i_op->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, XATTR_NAME_SELINUX,
+                       rc = inode->i_op->getxattr(dentry, inode, XATTR_NAME_SELINUX,
                                                   NULL, 0);
                        if (rc < 0) {
                                dput(dentry);
@@ -1432,7 +1442,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,
+                       rc = inode->i_op->getxattr(dentry, inode,
                                                   XATTR_NAME_SELINUX,
                                                   context, len);
                }
@@ -1622,7 +1632,7 @@ static int current_has_perm(const struct task_struct *tsk,
 
 /* Check whether a task is allowed to use a capability. */
 static int cred_has_capability(const struct cred *cred,
-                              int cap, int audit)
+                              int cap, int audit, bool initns)
 {
        struct common_audit_data ad;
        struct av_decision avd;
@@ -1636,10 +1646,10 @@ static int cred_has_capability(const struct cred *cred,
 
        switch (CAP_TO_INDEX(cap)) {
        case 0:
-               sclass = SECCLASS_CAPABILITY;
+               sclass = initns ? SECCLASS_CAPABILITY : SECCLASS_CAP_USERNS;
                break;
        case 1:
-               sclass = SECCLASS_CAPABILITY2;
+               sclass = initns ? SECCLASS_CAPABILITY2 : SECCLASS_CAP2_USERNS;
                break;
        default:
                printk(KERN_ERR
@@ -1781,7 +1791,6 @@ static int selinux_determine_inode_label(struct inode *dir,
                                         u32 *_new_isid)
 {
        const struct superblock_security_struct *sbsec = dir->i_sb->s_security;
-       const struct inode_security_struct *dsec = inode_security(dir);
        const struct task_security_struct *tsec = current_security();
 
        if ((sbsec->flags & SE_SBINITIALIZED) &&
@@ -1791,6 +1800,7 @@ static int selinux_determine_inode_label(struct inode *dir,
                   tsec->create_sid) {
                *_new_isid = tsec->create_sid;
        } else {
+               const struct inode_security_struct *dsec = inode_security(dir);
                return security_transition_sid(tsec->sid, dsec->sid, tclass,
                                               name, _new_isid);
        }
@@ -2075,7 +2085,7 @@ static int selinux_binder_transfer_file(struct task_struct *from,
        u32 sid = task_sid(to);
        struct file_security_struct *fsec = file->f_security;
        struct dentry *dentry = file->f_path.dentry;
-       struct inode_security_struct *isec = backing_inode_security(dentry);
+       struct inode_security_struct *isec;
        struct common_audit_data ad;
        int rc;
 
@@ -2094,6 +2104,7 @@ static int selinux_binder_transfer_file(struct task_struct *from,
        if (unlikely(IS_PRIVATE(d_backing_inode(dentry))))
                return 0;
 
+       isec = backing_inode_security(dentry);
        return avc_has_perm(sid, isec->sid, isec->sclass, file_to_av(file),
                            &ad);
 }
@@ -2142,7 +2153,7 @@ static int selinux_capset(struct cred *new, const struct cred *old,
 static int selinux_capable(const struct cred *cred, struct user_namespace *ns,
                           int cap, int audit)
 {
-       return cred_has_capability(cred, cap, audit);
+       return cred_has_capability(cred, cap, audit, ns == &init_user_ns);
 }
 
 static int selinux_quotactl(int cmds, int type, int id, struct super_block *sb)
@@ -2220,7 +2231,7 @@ static int selinux_vm_enough_memory(struct mm_struct *mm, long pages)
        int rc, cap_sys_admin = 0;
 
        rc = cred_has_capability(current_cred(), CAP_SYS_ADMIN,
-                                       SECURITY_CAP_NOAUDIT);
+                                SECURITY_CAP_NOAUDIT, true);
        if (rc == 0)
                cap_sys_admin = 1;
 
@@ -2229,6 +2240,20 @@ static int selinux_vm_enough_memory(struct mm_struct *mm, long pages)
 
 /* binprm security operations */
 
+static u32 ptrace_parent_sid(struct task_struct *task)
+{
+       u32 sid = 0;
+       struct task_struct *tracer;
+
+       rcu_read_lock();
+       tracer = ptrace_parent(task);
+       if (tracer)
+               sid = task_sid(tracer);
+       rcu_read_unlock();
+
+       return sid;
+}
+
 static int check_nnp_nosuid(const struct linux_binprm *bprm,
                            const struct task_security_struct *old_tsec,
                            const struct task_security_struct *new_tsec)
@@ -2350,18 +2375,7 @@ static int selinux_bprm_set_creds(struct linux_binprm *bprm)
                 * changes its SID has the appropriate permit */
                if (bprm->unsafe &
                    (LSM_UNSAFE_PTRACE | LSM_UNSAFE_PTRACE_CAP)) {
-                       struct task_struct *tracer;
-                       struct task_security_struct *sec;
-                       u32 ptsid = 0;
-
-                       rcu_read_lock();
-                       tracer = ptrace_parent(current);
-                       if (likely(tracer != NULL)) {
-                               sec = __task_cred(tracer)->security;
-                               ptsid = sec->sid;
-                       }
-                       rcu_read_unlock();
-
+                       u32 ptsid = ptrace_parent_sid(current);
                        if (ptsid != 0) {
                                rc = avc_has_perm(ptsid, new_tsec->sid,
                                                  SECCLASS_PROCESS,
@@ -2760,7 +2774,7 @@ static int selinux_sb_statfs(struct dentry *dentry)
 }
 
 static int selinux_mount(const char *dev_name,
-                        struct path *path,
+                        const struct path *path,
                         const char *type,
                         unsigned long flags,
                         void *data)
@@ -3045,7 +3059,7 @@ static int selinux_inode_setxattr(struct dentry *dentry, const char *name,
                                  const void *value, size_t size, int flags)
 {
        struct inode *inode = d_backing_inode(dentry);
-       struct inode_security_struct *isec = backing_inode_security(dentry);
+       struct inode_security_struct *isec;
        struct superblock_security_struct *sbsec;
        struct common_audit_data ad;
        u32 newsid, sid = current_sid();
@@ -3064,6 +3078,7 @@ static int selinux_inode_setxattr(struct dentry *dentry, const char *name,
        ad.type = LSM_AUDIT_DATA_DENTRY;
        ad.u.dentry = dentry;
 
+       isec = backing_inode_security(dentry);
        rc = avc_has_perm(sid, isec->sid, isec->sclass,
                          FILE__RELABELFROM, &ad);
        if (rc)
@@ -3122,7 +3137,7 @@ static void selinux_inode_post_setxattr(struct dentry *dentry, const char *name,
                                        int flags)
 {
        struct inode *inode = d_backing_inode(dentry);
-       struct inode_security_struct *isec = backing_inode_security(dentry);
+       struct inode_security_struct *isec;
        u32 newsid;
        int rc;
 
@@ -3139,6 +3154,7 @@ static void selinux_inode_post_setxattr(struct dentry *dentry, const char *name,
                return;
        }
 
+       isec = backing_inode_security(dentry);
        isec->sclass = inode_mode_to_security_class(inode->i_mode);
        isec->sid = newsid;
        isec->initialized = LABEL_INITIALIZED;
@@ -3180,7 +3196,7 @@ static int selinux_inode_getsecurity(struct inode *inode, const char *name, void
        u32 size;
        int error;
        char *context = NULL;
-       struct inode_security_struct *isec = inode_security(inode);
+       struct inode_security_struct *isec;
 
        if (strcmp(name, XATTR_SELINUX_SUFFIX))
                return -EOPNOTSUPP;
@@ -3198,7 +3214,8 @@ static int selinux_inode_getsecurity(struct inode *inode, const char *name, void
                            SECURITY_CAP_NOAUDIT);
        if (!error)
                error = cred_has_capability(current_cred(), CAP_MAC_ADMIN,
-                                           SECURITY_CAP_NOAUDIT);
+                                           SECURITY_CAP_NOAUDIT, true);
+       isec = inode_security(inode);
        if (!error)
                error = security_sid_to_context_force(isec->sid, &context,
                                                      &size);
@@ -3219,7 +3236,7 @@ out_nofree:
 static int selinux_inode_setsecurity(struct inode *inode, const char *name,
                                     const void *value, size_t size, int flags)
 {
-       struct inode_security_struct *isec = inode_security(inode);
+       struct inode_security_struct *isec = inode_security_novalidate(inode);
        u32 newsid;
        int rc;
 
@@ -3308,7 +3325,7 @@ static int ioctl_has_perm(const struct cred *cred, struct file *file,
        struct common_audit_data ad;
        struct file_security_struct *fsec = file->f_security;
        struct inode *inode = file_inode(file);
-       struct inode_security_struct *isec = inode_security(inode);
+       struct inode_security_struct *isec;
        struct lsm_ioctlop_audit ioctl;
        u32 ssid = cred_sid(cred);
        int rc;
@@ -3332,6 +3349,7 @@ static int ioctl_has_perm(const struct cred *cred, struct file *file,
        if (unlikely(IS_PRIVATE(inode)))
                return 0;
 
+       isec = inode_security(inode);
        rc = avc_has_extended_perms(ssid, isec->sid, isec->sclass,
                        requested, driver, xperm, &ad);
 out:
@@ -3373,7 +3391,7 @@ static int selinux_file_ioctl(struct file *file, unsigned int cmd,
        case KDSKBENT:
        case KDSKBSENT:
                error = cred_has_capability(cred, CAP_SYS_TTY_CONFIG,
-                                           SECURITY_CAP_AUDIT);
+                                           SECURITY_CAP_AUDIT, true);
                break;
 
        /* default case assumes that the command will go
@@ -3462,8 +3480,9 @@ static int selinux_file_mprotect(struct vm_area_struct *vma,
                    vma->vm_end <= vma->vm_mm->brk) {
                        rc = cred_has_perm(cred, cred, PROCESS__EXECHEAP);
                } else if (!vma->vm_file &&
-                          vma->vm_start <= vma->vm_mm->start_stack &&
-                          vma->vm_end >= vma->vm_mm->start_stack) {
+                          ((vma->vm_start <= vma->vm_mm->start_stack &&
+                            vma->vm_end >= vma->vm_mm->start_stack) ||
+                           vma_is_stack_for_task(vma, current))) {
                        rc = current_has_perm(current, PROCESS__EXECSTACK);
                } else if (vma->vm_file && vma->anon_vma) {
                        /*
@@ -3719,6 +3738,52 @@ static int selinux_kernel_module_request(char *kmod_name)
                            SYSTEM__MODULE_REQUEST, &ad);
 }
 
+static int selinux_kernel_module_from_file(struct file *file)
+{
+       struct common_audit_data ad;
+       struct inode_security_struct *isec;
+       struct file_security_struct *fsec;
+       u32 sid = current_sid();
+       int rc;
+
+       /* init_module */
+       if (file == NULL)
+               return avc_has_perm(sid, sid, SECCLASS_SYSTEM,
+                                       SYSTEM__MODULE_LOAD, NULL);
+
+       /* finit_module */
+
+       ad.type = LSM_AUDIT_DATA_PATH;
+       ad.u.path = file->f_path;
+
+       fsec = file->f_security;
+       if (sid != fsec->sid) {
+               rc = avc_has_perm(sid, fsec->sid, SECCLASS_FD, FD__USE, &ad);
+               if (rc)
+                       return rc;
+       }
+
+       isec = inode_security(file_inode(file));
+       return avc_has_perm(sid, isec->sid, SECCLASS_SYSTEM,
+                               SYSTEM__MODULE_LOAD, &ad);
+}
+
+static int selinux_kernel_read_file(struct file *file,
+                                   enum kernel_read_file_id id)
+{
+       int rc = 0;
+
+       switch (id) {
+       case READING_MODULE:
+               rc = selinux_kernel_module_from_file(file);
+               break;
+       default:
+               break;
+       }
+
+       return rc;
+}
+
 static int selinux_task_setpgid(struct task_struct *p, pid_t pgid)
 {
        return current_has_perm(p, PROCESS__SETPGID);
@@ -4539,13 +4604,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;
                }
        }
@@ -4598,6 +4663,7 @@ static int selinux_socket_getpeersec_dgram(struct socket *sock, struct sk_buff *
 {
        u32 peer_secid = SECSID_NULL;
        u16 family;
+       struct inode_security_struct *isec;
 
        if (skb && skb->protocol == htons(ETH_P_IP))
                family = PF_INET;
@@ -4608,9 +4674,10 @@ static int selinux_socket_getpeersec_dgram(struct socket *sock, struct sk_buff *
        else
                goto out;
 
-       if (sock && family == PF_UNIX)
-               selinux_inode_getsecid(SOCK_INODE(sock), &peer_secid);
-       else if (skb)
+       if (sock && family == PF_UNIX) {
+               isec = inode_security_novalidate(SOCK_INODE(sock));
+               peer_secid = isec->sid;
+       } else if (skb)
                selinux_skb_peerlbl_sid(skb, family, &peer_secid);
 
 out:
@@ -4911,7 +4978,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;
                }
        }
@@ -4997,6 +5064,15 @@ static unsigned int selinux_ipv4_output(void *priv,
        return selinux_ip_output(skb, PF_INET);
 }
 
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+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)
@@ -5675,7 +5751,6 @@ static int selinux_setprocattr(struct task_struct *p,
                               char *name, void *value, size_t size)
 {
        struct task_security_struct *tsec;
-       struct task_struct *tracer;
        struct cred *new;
        u32 sid = 0, ptsid;
        int error;
@@ -5782,14 +5857,8 @@ static int selinux_setprocattr(struct task_struct *p,
 
                /* Check for ptracing, and update the task SID if ok.
                   Otherwise, leave SID unchanged and fail. */
-               ptsid = 0;
-               rcu_read_lock();
-               tracer = ptrace_parent(p);
-               if (tracer)
-                       ptsid = task_sid(tracer);
-               rcu_read_unlock();
-
-               if (tracer) {
+               ptsid = ptrace_parent_sid(p);
+               if (ptsid != 0) {
                        error = avc_has_perm(ptsid, sid, SECCLASS_PROCESS,
                                             PROCESS__PTRACE, NULL);
                        if (error)
@@ -6020,6 +6089,7 @@ static struct security_hook_list selinux_hooks[] = {
        LSM_HOOK_INIT(kernel_act_as, selinux_kernel_act_as),
        LSM_HOOK_INIT(kernel_create_files_as, selinux_kernel_create_files_as),
        LSM_HOOK_INIT(kernel_module_request, selinux_kernel_module_request),
+       LSM_HOOK_INIT(kernel_read_file, selinux_kernel_read_file),
        LSM_HOOK_INIT(task_setpgid, selinux_task_setpgid),
        LSM_HOOK_INIT(task_getpgid, selinux_task_getpgid),
        LSM_HOOK_INIT(task_getsid, selinux_task_getsid),
@@ -6237,6 +6307,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 */
 };