Merge tag 'ptrace-cleanups-for-v5.18' of git://git.kernel.org/pub/scm/linux/kernel...
[linux-2.6-microblaze.git] / security / selinux / hooks.c
index 4d2cd6b..e9e9593 100644 (file)
@@ -210,10 +210,9 @@ static int selinux_lsm_notifier_avc_callback(u32 event)
  */
 static void cred_init_security(void)
 {
-       struct cred *cred = (struct cred *) current->real_cred;
        struct task_security_struct *tsec;
 
-       tsec = selinux_cred(cred);
+       tsec = selinux_cred(unrcu_pointer(current->real_cred));
        tsec->osid = tsec->sid = SECINITSID_KERNEL;
 }
 
@@ -340,17 +339,15 @@ static void inode_free_security(struct inode *inode)
 }
 
 struct selinux_mnt_opts {
-       const char *fscontext, *context, *rootcontext, *defcontext;
+       u32 fscontext_sid;
+       u32 context_sid;
+       u32 rootcontext_sid;
+       u32 defcontext_sid;
 };
 
 static void selinux_free_mnt_opts(void *mnt_opts)
 {
-       struct selinux_mnt_opts *opts = mnt_opts;
-       kfree(opts->fscontext);
-       kfree(opts->context);
-       kfree(opts->rootcontext);
-       kfree(opts->defcontext);
-       kfree(opts);
+       kfree(mnt_opts);
 }
 
 enum {
@@ -478,7 +475,7 @@ static int selinux_is_sblabel_mnt(struct super_block *sb)
 
 static int sb_check_xattr_support(struct super_block *sb)
 {
-       struct superblock_security_struct *sbsec = sb->s_security;
+       struct superblock_security_struct *sbsec = selinux_superblock(sb);
        struct dentry *root = sb->s_root;
        struct inode *root_inode = d_backing_inode(root);
        u32 sid;
@@ -597,18 +594,6 @@ static int bad_option(struct superblock_security_struct *sbsec, char flag,
        return 0;
 }
 
-static int parse_sid(struct super_block *sb, const char *s, u32 *sid,
-                    gfp_t gfp)
-{
-       int rc = security_context_str_to_sid(&selinux_state, s,
-                                            sid, gfp);
-       if (rc)
-               pr_warn("SELinux: security_context_str_to_sid"
-                      "(%s) failed for (dev %s, type %s) errno=%d\n",
-                      s, sb->s_id, sb->s_type->name, rc);
-       return rc;
-}
-
 /*
  * Allow filesystems with binary mount data to explicitly set mount point
  * labeling information.
@@ -671,41 +656,29 @@ static int selinux_set_mnt_opts(struct super_block *sb,
         * than once with different security options.
         */
        if (opts) {
-               if (opts->fscontext) {
-                       rc = parse_sid(sb, opts->fscontext, &fscontext_sid,
-                                       GFP_KERNEL);
-                       if (rc)
-                               goto out;
+               if (opts->fscontext_sid) {
+                       fscontext_sid = opts->fscontext_sid;
                        if (bad_option(sbsec, FSCONTEXT_MNT, sbsec->sid,
                                        fscontext_sid))
                                goto out_double_mount;
                        sbsec->flags |= FSCONTEXT_MNT;
                }
-               if (opts->context) {
-                       rc = parse_sid(sb, opts->context, &context_sid,
-                                       GFP_KERNEL);
-                       if (rc)
-                               goto out;
+               if (opts->context_sid) {
+                       context_sid = opts->context_sid;
                        if (bad_option(sbsec, CONTEXT_MNT, sbsec->mntpoint_sid,
                                        context_sid))
                                goto out_double_mount;
                        sbsec->flags |= CONTEXT_MNT;
                }
-               if (opts->rootcontext) {
-                       rc = parse_sid(sb, opts->rootcontext, &rootcontext_sid,
-                                       GFP_KERNEL);
-                       if (rc)
-                               goto out;
+               if (opts->rootcontext_sid) {
+                       rootcontext_sid = opts->rootcontext_sid;
                        if (bad_option(sbsec, ROOTCONTEXT_MNT, root_isec->sid,
                                        rootcontext_sid))
                                goto out_double_mount;
                        sbsec->flags |= ROOTCONTEXT_MNT;
                }
-               if (opts->defcontext) {
-                       rc = parse_sid(sb, opts->defcontext, &defcontext_sid,
-                                       GFP_KERNEL);
-                       if (rc)
-                               goto out;
+               if (opts->defcontext_sid) {
+                       defcontext_sid = opts->defcontext_sid;
                        if (bad_option(sbsec, DEFCONTEXT_MNT, sbsec->def_sid,
                                        defcontext_sid))
                                goto out_double_mount;
@@ -975,6 +948,8 @@ static int selinux_add_opt(int token, const char *s, void **mnt_opts)
 {
        struct selinux_mnt_opts *opts = *mnt_opts;
        bool is_alloc_opts = false;
+       u32 *dst_sid;
+       int rc;
 
        if (token == Opt_seclabel)
                /* eaten and completely ignored */
@@ -982,6 +957,11 @@ static int selinux_add_opt(int token, const char *s, void **mnt_opts)
        if (!s)
                return -ENOMEM;
 
+       if (!selinux_initialized(&selinux_state)) {
+               pr_warn("SELinux: Unable to set superblock options before the security server is initialized\n");
+               return -EINVAL;
+       }
+
        if (!opts) {
                opts = kzalloc(sizeof(*opts), GFP_KERNEL);
                if (!opts)
@@ -992,28 +972,34 @@ static int selinux_add_opt(int token, const char *s, void **mnt_opts)
 
        switch (token) {
        case Opt_context:
-               if (opts->context || opts->defcontext)
+               if (opts->context_sid || opts->defcontext_sid)
                        goto err;
-               opts->context = s;
+               dst_sid = &opts->context_sid;
                break;
        case Opt_fscontext:
-               if (opts->fscontext)
+               if (opts->fscontext_sid)
                        goto err;
-               opts->fscontext = s;
+               dst_sid = &opts->fscontext_sid;
                break;
        case Opt_rootcontext:
-               if (opts->rootcontext)
+               if (opts->rootcontext_sid)
                        goto err;
-               opts->rootcontext = s;
+               dst_sid = &opts->rootcontext_sid;
                break;
        case Opt_defcontext:
-               if (opts->context || opts->defcontext)
+               if (opts->context_sid || opts->defcontext_sid)
                        goto err;
-               opts->defcontext = s;
+               dst_sid = &opts->defcontext_sid;
                break;
+       default:
+               WARN_ON(1);
+               return -EINVAL;
        }
-
-       return 0;
+       rc = security_context_str_to_sid(&selinux_state, s, dst_sid, GFP_KERNEL);
+       if (rc)
+               pr_warn("SELinux: security_context_str_to_sid (%s) failed with errno=%d\n",
+                       s, rc);
+       return rc;
 
 err:
        if (is_alloc_opts) {
@@ -2534,7 +2520,7 @@ static void selinux_bprm_committed_creds(struct linux_binprm *bprm)
        if (rc) {
                clear_itimer();
 
-               spin_lock_irq(&current->sighand->siglock);
+               spin_lock_irq(&unrcu_pointer(current->sighand)->siglock);
                if (!fatal_signal_pending(current)) {
                        flush_sigqueue(&current->pending);
                        flush_sigqueue(&current->signal->shared_pending);
@@ -2542,13 +2528,13 @@ static void selinux_bprm_committed_creds(struct linux_binprm *bprm)
                        sigemptyset(&current->blocked);
                        recalc_sigpending();
                }
-               spin_unlock_irq(&current->sighand->siglock);
+               spin_unlock_irq(&unrcu_pointer(current->sighand)->siglock);
        }
 
        /* Wake up the parent if it is waiting so that it can recheck
         * wait permission to the new task SID. */
        read_lock(&tasklist_lock);
-       __wake_up_parent(current, current->real_parent);
+       __wake_up_parent(current, unrcu_pointer(current->real_parent));
        read_unlock(&tasklist_lock);
 }
 
@@ -2646,9 +2632,7 @@ free_opt:
 static int selinux_sb_mnt_opts_compat(struct super_block *sb, void *mnt_opts)
 {
        struct selinux_mnt_opts *opts = mnt_opts;
-       struct superblock_security_struct *sbsec = sb->s_security;
-       u32 sid;
-       int rc;
+       struct superblock_security_struct *sbsec = selinux_superblock(sb);
 
        /*
         * Superblock not initialized (i.e. no options) - reject if any
@@ -2664,35 +2648,27 @@ static int selinux_sb_mnt_opts_compat(struct super_block *sb, void *mnt_opts)
        if (!opts)
                return (sbsec->flags & SE_MNTMASK) ? 1 : 0;
 
-       if (opts->fscontext) {
-               rc = parse_sid(sb, opts->fscontext, &sid, GFP_NOWAIT);
-               if (rc)
-                       return 1;
-               if (bad_option(sbsec, FSCONTEXT_MNT, sbsec->sid, sid))
+       if (opts->fscontext_sid) {
+               if (bad_option(sbsec, FSCONTEXT_MNT, sbsec->sid,
+                              opts->fscontext_sid))
                        return 1;
        }
-       if (opts->context) {
-               rc = parse_sid(sb, opts->context, &sid, GFP_NOWAIT);
-               if (rc)
-                       return 1;
-               if (bad_option(sbsec, CONTEXT_MNT, sbsec->mntpoint_sid, sid))
+       if (opts->context_sid) {
+               if (bad_option(sbsec, CONTEXT_MNT, sbsec->mntpoint_sid,
+                              opts->context_sid))
                        return 1;
        }
-       if (opts->rootcontext) {
+       if (opts->rootcontext_sid) {
                struct inode_security_struct *root_isec;
 
                root_isec = backing_inode_security(sb->s_root);
-               rc = parse_sid(sb, opts->rootcontext, &sid, GFP_NOWAIT);
-               if (rc)
-                       return 1;
-               if (bad_option(sbsec, ROOTCONTEXT_MNT, root_isec->sid, sid))
+               if (bad_option(sbsec, ROOTCONTEXT_MNT, root_isec->sid,
+                              opts->rootcontext_sid))
                        return 1;
        }
-       if (opts->defcontext) {
-               rc = parse_sid(sb, opts->defcontext, &sid, GFP_NOWAIT);
-               if (rc)
-                       return 1;
-               if (bad_option(sbsec, DEFCONTEXT_MNT, sbsec->def_sid, sid))
+       if (opts->defcontext_sid) {
+               if (bad_option(sbsec, DEFCONTEXT_MNT, sbsec->def_sid,
+                              opts->defcontext_sid))
                        return 1;
        }
        return 0;
@@ -2702,8 +2678,6 @@ static int selinux_sb_remount(struct super_block *sb, void *mnt_opts)
 {
        struct selinux_mnt_opts *opts = mnt_opts;
        struct superblock_security_struct *sbsec = selinux_superblock(sb);
-       u32 sid;
-       int rc;
 
        if (!(sbsec->flags & SE_SBINITIALIZED))
                return 0;
@@ -2711,34 +2685,26 @@ static int selinux_sb_remount(struct super_block *sb, void *mnt_opts)
        if (!opts)
                return 0;
 
-       if (opts->fscontext) {
-               rc = parse_sid(sb, opts->fscontext, &sid, GFP_KERNEL);
-               if (rc)
-                       return rc;
-               if (bad_option(sbsec, FSCONTEXT_MNT, sbsec->sid, sid))
+       if (opts->fscontext_sid) {
+               if (bad_option(sbsec, FSCONTEXT_MNT, sbsec->sid,
+                              opts->fscontext_sid))
                        goto out_bad_option;
        }
-       if (opts->context) {
-               rc = parse_sid(sb, opts->context, &sid, GFP_KERNEL);
-               if (rc)
-                       return rc;
-               if (bad_option(sbsec, CONTEXT_MNT, sbsec->mntpoint_sid, sid))
+       if (opts->context_sid) {
+               if (bad_option(sbsec, CONTEXT_MNT, sbsec->mntpoint_sid,
+                              opts->context_sid))
                        goto out_bad_option;
        }
-       if (opts->rootcontext) {
+       if (opts->rootcontext_sid) {
                struct inode_security_struct *root_isec;
                root_isec = backing_inode_security(sb->s_root);
-               rc = parse_sid(sb, opts->rootcontext, &sid, GFP_KERNEL);
-               if (rc)
-                       return rc;
-               if (bad_option(sbsec, ROOTCONTEXT_MNT, root_isec->sid, sid))
+               if (bad_option(sbsec, ROOTCONTEXT_MNT, root_isec->sid,
+                              opts->rootcontext_sid))
                        goto out_bad_option;
        }
-       if (opts->defcontext) {
-               rc = parse_sid(sb, opts->defcontext, &sid, GFP_KERNEL);
-               if (rc)
-                       return rc;
-               if (bad_option(sbsec, DEFCONTEXT_MNT, sbsec->def_sid, sid))
+       if (opts->defcontext_sid) {
+               if (bad_option(sbsec, DEFCONTEXT_MNT, sbsec->def_sid,
+                              opts->defcontext_sid))
                        goto out_bad_option;
        }
        return 0;
@@ -2805,38 +2771,12 @@ static int selinux_fs_context_dup(struct fs_context *fc,
                                  struct fs_context *src_fc)
 {
        const struct selinux_mnt_opts *src = src_fc->security;
-       struct selinux_mnt_opts *opts;
 
        if (!src)
                return 0;
 
-       fc->security = kzalloc(sizeof(struct selinux_mnt_opts), GFP_KERNEL);
-       if (!fc->security)
-               return -ENOMEM;
-
-       opts = fc->security;
-
-       if (src->fscontext) {
-               opts->fscontext = kstrdup(src->fscontext, GFP_KERNEL);
-               if (!opts->fscontext)
-                       return -ENOMEM;
-       }
-       if (src->context) {
-               opts->context = kstrdup(src->context, GFP_KERNEL);
-               if (!opts->context)
-                       return -ENOMEM;
-       }
-       if (src->rootcontext) {
-               opts->rootcontext = kstrdup(src->rootcontext, GFP_KERNEL);
-               if (!opts->rootcontext)
-                       return -ENOMEM;
-       }
-       if (src->defcontext) {
-               opts->defcontext = kstrdup(src->defcontext, GFP_KERNEL);
-               if (!opts->defcontext)
-                       return -ENOMEM;
-       }
-       return 0;
+       fc->security = kmemdup(src, sizeof(*src), GFP_KERNEL);
+       return fc->security ? 0 : -ENOMEM;
 }
 
 static const struct fs_parameter_spec selinux_fs_parameters[] = {
@@ -2859,10 +2799,9 @@ static int selinux_fs_context_parse_param(struct fs_context *fc,
                return opt;
 
        rc = selinux_add_opt(opt, param->string, &fc->security);
-       if (!rc) {
+       if (!rc)
                param->string = NULL;
-               rc = 1;
-       }
+
        return rc;
 }
 
@@ -3344,8 +3283,6 @@ static void selinux_inode_post_setxattr(struct dentry *dentry, const char *name,
        isec->sid = newsid;
        isec->initialized = LABEL_INITIALIZED;
        spin_unlock(&isec->lock);
-
-       return;
 }
 
 static int selinux_inode_getxattr(struct dentry *dentry, const char *name)
@@ -3744,6 +3681,12 @@ static int selinux_file_ioctl(struct file *file, unsigned int cmd,
                                            CAP_OPT_NONE, true);
                break;
 
+       case FIOCLEX:
+       case FIONCLEX:
+               if (!selinux_policycap_ioctl_skip_cloexec())
+                       error = ioctl_has_perm(cred, file, FILE__IOCTL, (u16) cmd);
+               break;
+
        /* default case assumes that the command will go
         * to the file's ioctl() function.
         */
@@ -5298,37 +5241,38 @@ static void selinux_sock_graft(struct sock *sk, struct socket *parent)
        sksec->sclass = isec->sclass;
 }
 
-/* Called whenever SCTP receives an INIT chunk. This happens when an incoming
- * connect(2), sctp_connectx(3) or sctp_sendmsg(3) (with no association
- * already present).
+/*
+ * Determines peer_secid for the asoc and updates socket's peer label
+ * if it's the first association on the socket.
  */
-static int selinux_sctp_assoc_request(struct sctp_association *asoc,
-                                     struct sk_buff *skb)
+static int selinux_sctp_process_new_assoc(struct sctp_association *asoc,
+                                         struct sk_buff *skb)
 {
-       struct sk_security_struct *sksec = asoc->base.sk->sk_security;
+       struct sock *sk = asoc->base.sk;
+       u16 family = sk->sk_family;
+       struct sk_security_struct *sksec = sk->sk_security;
        struct common_audit_data ad;
        struct lsm_network_audit net = {0,};
-       u8 peerlbl_active;
-       u32 peer_sid = SECINITSID_UNLABELED;
-       u32 conn_sid;
-       int err = 0;
+       int err;
 
-       if (!selinux_policycap_extsockclass())
-               return 0;
+       /* handle mapped IPv4 packets arriving via IPv6 sockets */
+       if (family == PF_INET6 && skb->protocol == htons(ETH_P_IP))
+               family = PF_INET;
 
-       peerlbl_active = selinux_peerlbl_enabled();
+       if (selinux_peerlbl_enabled()) {
+               asoc->peer_secid = SECSID_NULL;
 
-       if (peerlbl_active) {
                /* This will return peer_sid = SECSID_NULL if there are
                 * no peer labels, see security_net_peersid_resolve().
                 */
-               err = selinux_skb_peerlbl_sid(skb, asoc->base.sk->sk_family,
-                                             &peer_sid);
+               err = selinux_skb_peerlbl_sid(skb, family, &asoc->peer_secid);
                if (err)
                        return err;
 
-               if (peer_sid == SECSID_NULL)
-                       peer_sid = SECINITSID_UNLABELED;
+               if (asoc->peer_secid == SECSID_NULL)
+                       asoc->peer_secid = SECINITSID_UNLABELED;
+       } else {
+               asoc->peer_secid = SECINITSID_UNLABELED;
        }
 
        if (sksec->sctp_assoc_state == SCTP_ASSOC_UNSET) {
@@ -5339,8 +5283,8 @@ static int selinux_sctp_assoc_request(struct sctp_association *asoc,
                 * then it is approved by policy and used as the primary
                 * peer SID for getpeercon(3).
                 */
-               sksec->peer_sid = peer_sid;
-       } else if  (sksec->peer_sid != peer_sid) {
+               sksec->peer_sid = asoc->peer_secid;
+       } else if (sksec->peer_sid != asoc->peer_secid) {
                /* Other association peer SIDs are checked to enforce
                 * consistency among the peer SIDs.
                 */
@@ -5348,11 +5292,32 @@ static int selinux_sctp_assoc_request(struct sctp_association *asoc,
                ad.u.net = &net;
                ad.u.net->sk = asoc->base.sk;
                err = avc_has_perm(&selinux_state,
-                                  sksec->peer_sid, peer_sid, sksec->sclass,
-                                  SCTP_SOCKET__ASSOCIATION, &ad);
+                                  sksec->peer_sid, asoc->peer_secid,
+                                  sksec->sclass, SCTP_SOCKET__ASSOCIATION,
+                                  &ad);
                if (err)
                        return err;
        }
+       return 0;
+}
+
+/* Called whenever SCTP receives an INIT or COOKIE ECHO chunk. This
+ * happens on an incoming connect(2), sctp_connectx(3) or
+ * sctp_sendmsg(3) (with no association already present).
+ */
+static int selinux_sctp_assoc_request(struct sctp_association *asoc,
+                                     struct sk_buff *skb)
+{
+       struct sk_security_struct *sksec = asoc->base.sk->sk_security;
+       u32 conn_sid;
+       int err;
+
+       if (!selinux_policycap_extsockclass())
+               return 0;
+
+       err = selinux_sctp_process_new_assoc(asoc, skb);
+       if (err)
+               return err;
 
        /* Compute the MLS component for the connection and store
         * the information in asoc. This will be used by SCTP TCP type
@@ -5360,17 +5325,36 @@ static int selinux_sctp_assoc_request(struct sctp_association *asoc,
         * socket to be generated. selinux_sctp_sk_clone() will then
         * plug this into the new socket.
         */
-       err = selinux_conn_sid(sksec->sid, peer_sid, &conn_sid);
+       err = selinux_conn_sid(sksec->sid, asoc->peer_secid, &conn_sid);
        if (err)
                return err;
 
        asoc->secid = conn_sid;
-       asoc->peer_secid = peer_sid;
 
        /* Set any NetLabel labels including CIPSO/CALIPSO options. */
        return selinux_netlbl_sctp_assoc_request(asoc, skb);
 }
 
+/* Called when SCTP receives a COOKIE ACK chunk as the final
+ * response to an association request (initited by us).
+ */
+static int selinux_sctp_assoc_established(struct sctp_association *asoc,
+                                         struct sk_buff *skb)
+{
+       struct sk_security_struct *sksec = asoc->base.sk->sk_security;
+
+       if (!selinux_policycap_extsockclass())
+               return 0;
+
+       /* Inherit secid from the parent socket - this will be picked up
+        * by selinux_sctp_sk_clone() if the association gets peeled off
+        * into a new socket.
+        */
+       asoc->secid = sksec->sid;
+
+       return selinux_sctp_process_new_assoc(asoc, skb);
+}
+
 /* Check if sctp IPv4/IPv6 addresses are valid for binding or connecting
  * based on their @optname.
  */
@@ -7191,6 +7175,7 @@ static struct security_hook_list selinux_hooks[] __lsm_ro_after_init = {
        LSM_HOOK_INIT(sctp_assoc_request, selinux_sctp_assoc_request),
        LSM_HOOK_INIT(sctp_sk_clone, selinux_sctp_sk_clone),
        LSM_HOOK_INIT(sctp_bind_connect, selinux_sctp_bind_connect),
+       LSM_HOOK_INIT(sctp_assoc_established, selinux_sctp_assoc_established),
        LSM_HOOK_INIT(inet_conn_request, selinux_inet_conn_request),
        LSM_HOOK_INIT(inet_csk_clone, selinux_inet_csk_clone),
        LSM_HOOK_INIT(inet_conn_established, selinux_inet_conn_established),