Merge branch 'smack-for-4.5' of https://github.com/cschaufler/smack-next into next
[cascardo/linux.git] / security / selinux / hooks.c
index 9e591e5..40e071a 100644 (file)
@@ -242,6 +242,77 @@ static int inode_alloc_security(struct inode *inode)
        return 0;
 }
 
+static int inode_doinit_with_dentry(struct inode *inode, struct dentry *opt_dentry);
+
+/*
+ * Try reloading inode security labels that have been marked as invalid.  The
+ * @may_sleep parameter indicates when sleeping and thus reloading labels is
+ * allowed; when set to false, returns ERR_PTR(-ECHILD) when the label is
+ * invalid.  The @opt_dentry parameter should be set to a dentry of the inode;
+ * when no dentry is available, set it to NULL instead.
+ */
+static int __inode_security_revalidate(struct inode *inode,
+                                      struct dentry *opt_dentry,
+                                      bool may_sleep)
+{
+       struct inode_security_struct *isec = inode->i_security;
+
+       might_sleep_if(may_sleep);
+
+       if (isec->initialized == LABEL_INVALID) {
+               if (!may_sleep)
+                       return -ECHILD;
+
+               /*
+                * Try reloading the inode security label.  This will fail if
+                * @opt_dentry is NULL and no dentry for this inode can be
+                * found; in that case, continue using the old label.
+                */
+               inode_doinit_with_dentry(inode, opt_dentry);
+       }
+       return 0;
+}
+
+static void inode_security_revalidate(struct inode *inode)
+{
+       __inode_security_revalidate(inode, NULL, true);
+}
+
+static struct inode_security_struct *inode_security_novalidate(struct inode *inode)
+{
+       return inode->i_security;
+}
+
+static struct inode_security_struct *inode_security_rcu(struct inode *inode, bool rcu)
+{
+       int error;
+
+       error = __inode_security_revalidate(inode, NULL, !rcu);
+       if (error)
+               return ERR_PTR(error);
+       return inode->i_security;
+}
+
+/*
+ * Get the security label of an inode.
+ */
+static struct inode_security_struct *inode_security(struct inode *inode)
+{
+       __inode_security_revalidate(inode, NULL, true);
+       return inode->i_security;
+}
+
+/*
+ * Get the security label of a dentry's backing inode.
+ */
+static struct inode_security_struct *backing_inode_security(struct dentry *dentry)
+{
+       struct inode *inode = d_backing_inode(dentry);
+
+       __inode_security_revalidate(inode, dentry, true);
+       return inode->i_security;
+}
+
 static void inode_free_rcu(struct rcu_head *head)
 {
        struct inode_security_struct *isec;
@@ -345,8 +416,6 @@ static const char *labeling_behaviors[7] = {
        "uses native labeling",
 };
 
-static int inode_doinit_with_dentry(struct inode *inode, struct dentry *opt_dentry);
-
 static inline int inode_doinit(struct inode *inode)
 {
        return inode_doinit_with_dentry(inode, NULL);
@@ -565,8 +634,8 @@ static int selinux_get_mnt_opts(const struct super_block *sb,
                opts->mnt_opts_flags[i++] = DEFCONTEXT_MNT;
        }
        if (sbsec->flags & ROOTCONTEXT_MNT) {
-               struct inode *root = d_backing_inode(sbsec->sb->s_root);
-               struct inode_security_struct *isec = root->i_security;
+               struct dentry *root = sbsec->sb->s_root;
+               struct inode_security_struct *isec = backing_inode_security(root);
 
                rc = security_sid_to_context(isec->sid, &context, &len);
                if (rc)
@@ -621,8 +690,8 @@ static int selinux_set_mnt_opts(struct super_block *sb,
        int rc = 0, i;
        struct superblock_security_struct *sbsec = sb->s_security;
        const char *name = sb->s_type->name;
-       struct inode *inode = d_backing_inode(sbsec->sb->s_root);
-       struct inode_security_struct *root_isec = inode->i_security;
+       struct dentry *root = sbsec->sb->s_root;
+       struct inode_security_struct *root_isec = backing_inode_security(root);
        u32 fscontext_sid = 0, context_sid = 0, rootcontext_sid = 0;
        u32 defcontext_sid = 0;
        char **mount_options = opts->mnt_opts;
@@ -802,7 +871,7 @@ static int selinux_set_mnt_opts(struct super_block *sb,
                        goto out;
 
                root_isec->sid = rootcontext_sid;
-               root_isec->initialized = 1;
+               root_isec->initialized = LABEL_INITIALIZED;
        }
 
        if (defcontext_sid) {
@@ -852,8 +921,8 @@ static int selinux_cmp_sb_context(const struct super_block *oldsb,
        if ((oldflags & DEFCONTEXT_MNT) && old->def_sid != new->def_sid)
                goto mismatch;
        if (oldflags & ROOTCONTEXT_MNT) {
-               struct inode_security_struct *oldroot = d_backing_inode(oldsb->s_root)->i_security;
-               struct inode_security_struct *newroot = d_backing_inode(newsb->s_root)->i_security;
+               struct inode_security_struct *oldroot = backing_inode_security(oldsb->s_root);
+               struct inode_security_struct *newroot = backing_inode_security(newsb->s_root);
                if (oldroot->sid != newroot->sid)
                        goto mismatch;
        }
@@ -903,17 +972,14 @@ static int selinux_sb_clone_mnt_opts(const struct super_block *oldsb,
                if (!set_fscontext)
                        newsbsec->sid = sid;
                if (!set_rootcontext) {
-                       struct inode *newinode = d_backing_inode(newsb->s_root);
-                       struct inode_security_struct *newisec = newinode->i_security;
+                       struct inode_security_struct *newisec = backing_inode_security(newsb->s_root);
                        newisec->sid = sid;
                }
                newsbsec->mntpoint_sid = sid;
        }
        if (set_rootcontext) {
-               const struct inode *oldinode = d_backing_inode(oldsb->s_root);
-               const struct inode_security_struct *oldisec = oldinode->i_security;
-               struct inode *newinode = d_backing_inode(newsb->s_root);
-               struct inode_security_struct *newisec = newinode->i_security;
+               const struct inode_security_struct *oldisec = backing_inode_security(oldsb->s_root);
+               struct inode_security_struct *newisec = backing_inode_security(newsb->s_root);
 
                newisec->sid = oldisec->sid;
        }
@@ -1293,11 +1359,11 @@ static int inode_doinit_with_dentry(struct inode *inode, struct dentry *opt_dent
        unsigned len = 0;
        int rc = 0;
 
-       if (isec->initialized)
+       if (isec->initialized == LABEL_INITIALIZED)
                goto out;
 
        mutex_lock(&isec->lock);
-       if (isec->initialized)
+       if (isec->initialized == LABEL_INITIALIZED)
                goto out_unlock;
 
        sbsec = inode->i_sb->s_security;
@@ -1469,7 +1535,7 @@ static int inode_doinit_with_dentry(struct inode *inode, struct dentry *opt_dent
                break;
        }
 
-       isec->initialized = 1;
+       isec->initialized = LABEL_INITIALIZED;
 
 out_unlock:
        mutex_unlock(&isec->lock);
@@ -1640,6 +1706,7 @@ static inline int dentry_has_perm(const struct cred *cred,
 
        ad.type = LSM_AUDIT_DATA_DENTRY;
        ad.u.dentry = dentry;
+       __inode_security_revalidate(inode, dentry, true);
        return inode_has_perm(cred, inode, av, &ad);
 }
 
@@ -1655,6 +1722,7 @@ static inline int path_has_perm(const struct cred *cred,
 
        ad.type = LSM_AUDIT_DATA_PATH;
        ad.u.path = *path;
+       __inode_security_revalidate(inode, path->dentry, true);
        return inode_has_perm(cred, inode, av, &ad);
 }
 
@@ -1712,13 +1780,13 @@ out:
 /*
  * Determine the label for an inode that might be unioned.
  */
-static int selinux_determine_inode_label(const struct inode *dir,
+static int selinux_determine_inode_label(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 inode_security_struct *dsec = dir->i_security;
+       const struct inode_security_struct *dsec = inode_security(dir);
        const struct task_security_struct *tsec = current_security();
 
        if ((sbsec->flags & SE_SBINITIALIZED) &&
@@ -1747,7 +1815,7 @@ static int may_create(struct inode *dir,
        struct common_audit_data ad;
        int rc;
 
-       dsec = dir->i_security;
+       dsec = inode_security(dir);
        sbsec = dir->i_sb->s_security;
 
        sid = tsec->sid;
@@ -1800,8 +1868,8 @@ static int may_link(struct inode *dir,
        u32 av;
        int rc;
 
-       dsec = dir->i_security;
-       isec = d_backing_inode(dentry)->i_security;
+       dsec = inode_security(dir);
+       isec = backing_inode_security(dentry);
 
        ad.type = LSM_AUDIT_DATA_DENTRY;
        ad.u.dentry = dentry;
@@ -1844,10 +1912,10 @@ static inline int may_rename(struct inode *old_dir,
        int old_is_dir, new_is_dir;
        int rc;
 
-       old_dsec = old_dir->i_security;
-       old_isec = d_backing_inode(old_dentry)->i_security;
+       old_dsec = inode_security(old_dir);
+       old_isec = backing_inode_security(old_dentry);
        old_is_dir = d_is_dir(old_dentry);
-       new_dsec = new_dir->i_security;
+       new_dsec = inode_security(new_dir);
 
        ad.type = LSM_AUDIT_DATA_DENTRY;
 
@@ -1875,7 +1943,7 @@ static inline int may_rename(struct inode *old_dir,
        if (rc)
                return rc;
        if (d_is_positive(new_dentry)) {
-               new_isec = d_backing_inode(new_dentry)->i_security;
+               new_isec = backing_inode_security(new_dentry);
                new_is_dir = d_is_dir(new_dentry);
                rc = avc_has_perm(sid, new_isec->sid,
                                  new_isec->sclass,
@@ -2011,8 +2079,8 @@ static int selinux_binder_transfer_file(struct task_struct *from,
 {
        u32 sid = task_sid(to);
        struct file_security_struct *fsec = file->f_security;
-       struct inode *inode = d_backing_inode(file->f_path.dentry);
-       struct inode_security_struct *isec = inode->i_security;
+       struct dentry *dentry = file->f_path.dentry;
+       struct inode_security_struct *isec = backing_inode_security(dentry);
        struct common_audit_data ad;
        int rc;
 
@@ -2028,7 +2096,7 @@ static int selinux_binder_transfer_file(struct task_struct *from,
                        return rc;
        }
 
-       if (unlikely(IS_PRIVATE(inode)))
+       if (unlikely(IS_PRIVATE(d_backing_inode(dentry))))
                return 0;
 
        return avc_has_perm(sid, isec->sid, isec->sclass, file_to_av(file),
@@ -2217,7 +2285,7 @@ static int selinux_bprm_set_creds(struct linux_binprm *bprm)
 
        old_tsec = current_security();
        new_tsec = bprm->cred->security;
-       isec = inode->i_security;
+       isec = inode_security(inode);
 
        /* Default to the current task SID. */
        new_tsec->sid = old_tsec->sid;
@@ -2639,7 +2707,7 @@ static int selinux_sb_remount(struct super_block *sb, void *data)
                        break;
                case ROOTCONTEXT_MNT: {
                        struct inode_security_struct *root_isec;
-                       root_isec = d_backing_inode(sb->s_root)->i_security;
+                       root_isec = backing_inode_security(sb->s_root);
 
                        if (bad_option(sbsec, ROOTCONTEXT_MNT, root_isec->sid, sid))
                                goto out_bad_option;
@@ -2753,13 +2821,11 @@ static int selinux_inode_init_security(struct inode *inode, struct inode *dir,
                                       void **value, size_t *len)
 {
        const struct task_security_struct *tsec = current_security();
-       struct inode_security_struct *dsec;
        struct superblock_security_struct *sbsec;
        u32 sid, newsid, clen;
        int rc;
        char *context;
 
-       dsec = dir->i_security;
        sbsec = dir->i_sb->s_security;
 
        sid = tsec->sid;
@@ -2777,7 +2843,7 @@ static int selinux_inode_init_security(struct inode *inode, struct inode *dir,
                struct inode_security_struct *isec = inode->i_security;
                isec->sclass = inode_mode_to_security_class(inode->i_mode);
                isec->sid = newsid;
-               isec->initialized = 1;
+               isec->initialized = LABEL_INITIALIZED;
        }
 
        if (!ss_initialized || !(sbsec->flags & SBLABEL_MNT))
@@ -2858,7 +2924,9 @@ static int selinux_inode_follow_link(struct dentry *dentry, struct inode *inode,
        ad.type = LSM_AUDIT_DATA_DENTRY;
        ad.u.dentry = dentry;
        sid = cred_sid(cred);
-       isec = inode->i_security;
+       isec = inode_security_rcu(inode, rcu);
+       if (IS_ERR(isec))
+               return PTR_ERR(isec);
 
        return avc_has_perm_flags(sid, isec->sid, isec->sclass, FILE__READ, &ad,
                                  rcu ? MAY_NOT_BLOCK : 0);
@@ -2910,7 +2978,9 @@ static int selinux_inode_permission(struct inode *inode, int mask)
        perms = file_mask_to_av(inode->i_mode, mask);
 
        sid = cred_sid(cred);
-       isec = inode->i_security;
+       isec = inode_security_rcu(inode, flags & MAY_NOT_BLOCK);
+       if (IS_ERR(isec))
+               return PTR_ERR(isec);
 
        rc = avc_has_perm_noaudit(sid, isec->sid, isec->sclass, perms, 0, &avd);
        audited = avc_audit_required(perms, &avd, rc,
@@ -2980,7 +3050,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 = inode->i_security;
+       struct inode_security_struct *isec = backing_inode_security(dentry);
        struct superblock_security_struct *sbsec;
        struct common_audit_data ad;
        u32 newsid, sid = current_sid();
@@ -3057,7 +3127,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 = inode->i_security;
+       struct inode_security_struct *isec = backing_inode_security(dentry);
        u32 newsid;
        int rc;
 
@@ -3076,7 +3146,7 @@ static void selinux_inode_post_setxattr(struct dentry *dentry, const char *name,
 
        isec->sclass = inode_mode_to_security_class(inode->i_mode);
        isec->sid = newsid;
-       isec->initialized = 1;
+       isec->initialized = LABEL_INITIALIZED;
 
        return;
 }
@@ -3110,12 +3180,12 @@ static int selinux_inode_removexattr(struct dentry *dentry, const char *name)
  *
  * Permission check is handled by selinux_inode_getxattr hook.
  */
-static int selinux_inode_getsecurity(const struct inode *inode, const char *name, void **buffer, bool alloc)
+static int selinux_inode_getsecurity(struct inode *inode, const char *name, void **buffer, bool alloc)
 {
        u32 size;
        int error;
        char *context = NULL;
-       struct inode_security_struct *isec = inode->i_security;
+       struct inode_security_struct *isec = inode_security(inode);
 
        if (strcmp(name, XATTR_SELINUX_SUFFIX))
                return -EOPNOTSUPP;
@@ -3154,7 +3224,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->i_security;
+       struct inode_security_struct *isec = inode_security(inode);
        u32 newsid;
        int rc;
 
@@ -3170,7 +3240,7 @@ static int selinux_inode_setsecurity(struct inode *inode, const char *name,
 
        isec->sclass = inode_mode_to_security_class(inode->i_mode);
        isec->sid = newsid;
-       isec->initialized = 1;
+       isec->initialized = LABEL_INITIALIZED;
        return 0;
 }
 
@@ -3182,9 +3252,9 @@ static int selinux_inode_listsecurity(struct inode *inode, char *buffer, size_t
        return len;
 }
 
-static void selinux_inode_getsecid(const struct inode *inode, u32 *secid)
+static void selinux_inode_getsecid(struct inode *inode, u32 *secid)
 {
-       struct inode_security_struct *isec = inode->i_security;
+       struct inode_security_struct *isec = inode_security(inode);
        *secid = isec->sid;
 }
 
@@ -3207,7 +3277,7 @@ static int selinux_file_permission(struct file *file, int mask)
 {
        struct inode *inode = file_inode(file);
        struct file_security_struct *fsec = file->f_security;
-       struct inode_security_struct *isec = inode->i_security;
+       struct inode_security_struct *isec = inode_security(inode);
        u32 sid = current_sid();
 
        if (!mask)
@@ -3219,6 +3289,7 @@ static int selinux_file_permission(struct file *file, int mask)
                /* No change since file_open check. */
                return 0;
 
+       inode_security_revalidate(inode);
        return selinux_revalidate_file_permission(file, mask);
 }
 
@@ -3242,7 +3313,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->i_security;
+       struct inode_security_struct *isec = inode_security(inode);
        struct lsm_ioctlop_audit ioctl;
        u32 ssid = cred_sid(cred);
        int rc;
@@ -3506,7 +3577,7 @@ static int selinux_file_open(struct file *file, const struct cred *cred)
        struct inode_security_struct *isec;
 
        fsec = file->f_security;
-       isec = file_inode(file)->i_security;
+       isec = inode_security(file_inode(file));
        /*
         * Save inode label and policy sequence number
         * at open-time so that selinux_file_permission
@@ -3524,6 +3595,7 @@ static int selinux_file_open(struct file *file, const struct cred *cred)
         * new inode label or new policy.
         * This check is not redundant - do not remove.
         */
+       inode_security_revalidate(file_inode(file));
        return file_path_has_perm(cred, file, open_file_to_av(file));
 }
 
@@ -3624,7 +3696,7 @@ static int selinux_kernel_act_as(struct cred *new, u32 secid)
  */
 static int selinux_kernel_create_files_as(struct cred *new, struct inode *inode)
 {
-       struct inode_security_struct *isec = inode->i_security;
+       struct inode_security_struct *isec = inode_security(inode);
        struct task_security_struct *tsec = new->security;
        u32 sid = current_sid();
        int ret;
@@ -3748,7 +3820,7 @@ static void selinux_task_to_inode(struct task_struct *p,
        u32 sid = task_sid(p);
 
        isec->sid = sid;
-       isec->initialized = 1;
+       isec->initialized = LABEL_INITIALIZED;
 }
 
 /* Returns error only if unable to parse addresses */
@@ -4065,7 +4137,7 @@ static int selinux_socket_post_create(struct socket *sock, int family,
                                      int type, int protocol, int kern)
 {
        const struct task_security_struct *tsec = current_security();
-       struct inode_security_struct *isec = SOCK_INODE(sock)->i_security;
+       struct inode_security_struct *isec = inode_security_novalidate(SOCK_INODE(sock));
        struct sk_security_struct *sksec;
        int err = 0;
 
@@ -4079,7 +4151,7 @@ static int selinux_socket_post_create(struct socket *sock, int family,
                        return err;
        }
 
-       isec->initialized = 1;
+       isec->initialized = LABEL_INITIALIZED;
 
        if (sock->sk) {
                sksec = sock->sk->sk_security;
@@ -4265,12 +4337,12 @@ static int selinux_socket_accept(struct socket *sock, struct socket *newsock)
        if (err)
                return err;
 
-       newisec = SOCK_INODE(newsock)->i_security;
+       newisec = inode_security_novalidate(SOCK_INODE(newsock));
 
-       isec = SOCK_INODE(sock)->i_security;
+       isec = inode_security_novalidate(SOCK_INODE(sock));
        newisec->sclass = isec->sclass;
        newisec->sid = isec->sid;
-       newisec->initialized = 1;
+       newisec->initialized = LABEL_INITIALIZED;
 
        return 0;
 }
@@ -4605,7 +4677,8 @@ static void selinux_sk_getsecid(struct sock *sk, u32 *secid)
 
 static void selinux_sock_graft(struct sock *sk, struct socket *parent)
 {
-       struct inode_security_struct *isec = SOCK_INODE(parent)->i_security;
+       struct inode_security_struct *isec =
+               inode_security_novalidate(SOCK_INODE(parent));
        struct sk_security_struct *sksec = sk->sk_security;
 
        if (sk->sk_family == PF_INET || sk->sk_family == PF_INET6 ||
@@ -4785,11 +4858,12 @@ 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) {
-                       printk(KERN_WARNING
-                              "SELinux: unrecognized netlink message:"
-                              " protocol=%hu nlmsg_type=%hu sclass=%s\n",
+                       pr_warn_ratelimited("SELinux: unrecognized netlink"
+                              " message: protocol=%hu nlmsg_type=%hu sclass=%s"
+                              " pig=%d comm=%s\n",
                               sk->sk_protocol, nlh->nlmsg_type,
-                              secclass_map[sksec->sclass - 1].name);
+                              secclass_map[sksec->sclass - 1].name,
+                              task_pid_nr(current), current->comm);
                        if (!selinux_enforcing || security_get_allow_unknown())
                                err = 0;
                }
@@ -4933,7 +5007,7 @@ static unsigned int selinux_ip_postroute_compat(struct sk_buff *skb,
                                                int ifindex,
                                                u16 family)
 {
-       struct sock *sk = skb->sk;
+       struct sock *sk = skb_to_full_sk(skb);
        struct sk_security_struct *sksec;
        struct common_audit_data ad;
        struct lsm_network_audit net = {0,};
@@ -4988,7 +5062,7 @@ static unsigned int selinux_ip_postroute(struct sk_buff *skb,
        if (!secmark_active && !peerlbl_active)
                return NF_ACCEPT;
 
-       sk = skb->sk;
+       sk = skb_to_full_sk(skb);
 
 #ifdef CONFIG_XFRM
        /* If skb->dst->xfrm is non-NULL then the packet is undergoing an IPsec
@@ -5033,8 +5107,6 @@ static unsigned int selinux_ip_postroute(struct sk_buff *skb,
                u32 skb_sid;
                struct sk_security_struct *sksec;
 
-               if (sk->sk_state == TCP_NEW_SYN_RECV)
-                       sk = inet_reqsk(sk)->rsk_listener;
                sksec = sk->sk_security;
                if (selinux_skb_peerlbl_sid(skb, family, &skb_sid))
                        return NF_DROP;
@@ -5764,6 +5836,15 @@ static void selinux_release_secctx(char *secdata, u32 seclen)
        kfree(secdata);
 }
 
+static void selinux_inode_invalidate_secctx(struct inode *inode)
+{
+       struct inode_security_struct *isec = inode->i_security;
+
+       mutex_lock(&isec->lock);
+       isec->initialized = LABEL_INVALID;
+       mutex_unlock(&isec->lock);
+}
+
 /*
  *     called with inode->i_mutex locked
  */
@@ -5995,6 +6076,7 @@ static struct security_hook_list selinux_hooks[] = {
        LSM_HOOK_INIT(secid_to_secctx, selinux_secid_to_secctx),
        LSM_HOOK_INIT(secctx_to_secid, selinux_secctx_to_secid),
        LSM_HOOK_INIT(release_secctx, selinux_release_secctx),
+       LSM_HOOK_INIT(inode_invalidate_secctx, selinux_inode_invalidate_secctx),
        LSM_HOOK_INIT(inode_notifysecctx, selinux_inode_notifysecctx),
        LSM_HOOK_INIT(inode_setsecctx, selinux_inode_setsecctx),
        LSM_HOOK_INIT(inode_getsecctx, selinux_inode_getsecctx),