Merge tag 'selinux-pr-20190312' of git://git.kernel.org/pub/scm/linux/kernel/git...
[sfrench/cifs-2.6.git] / security / selinux / hooks.c
index 2f82a54f870382440b621fde195ce69859bd286a..1d0b37af2444df6e3358dceb1e1c03522de4a731 100644 (file)
@@ -48,6 +48,8 @@
 #include <linux/fdtable.h>
 #include <linux/namei.h>
 #include <linux/mount.h>
+#include <linux/fs_context.h>
+#include <linux/fs_parser.h>
 #include <linux/netfilter_ipv4.h>
 #include <linux/netfilter_ipv6.h>
 #include <linux/tty.h>
@@ -410,11 +412,11 @@ static inline int inode_doinit(struct inode *inode)
 
 enum {
        Opt_error = -1,
-       Opt_context = 1,
+       Opt_context = 0,
+       Opt_defcontext = 1,
        Opt_fscontext = 2,
-       Opt_defcontext = 3,
-       Opt_rootcontext = 4,
-       Opt_seclabel = 5,
+       Opt_rootcontext = 3,
+       Opt_seclabel = 4,
 };
 
 #define A(s, has_arg) {#s, sizeof(#s) - 1, Opt_##s, has_arg}
@@ -937,8 +939,11 @@ static int selinux_sb_clone_mnt_opts(const struct super_block *oldsb,
        BUG_ON(!(oldsbsec->flags & SE_SBINITIALIZED));
 
        /* if fs is reusing a sb, make sure that the contexts match */
-       if (newsbsec->flags & SE_SBINITIALIZED)
+       if (newsbsec->flags & SE_SBINITIALIZED) {
+               if ((kern_flags & SECURITY_LSM_NATIVE_LABELS) && !set_context)
+                       *set_kern_flags |= SECURITY_LSM_NATIVE_LABELS;
                return selinux_cmp_sb_context(oldsb, newsb);
+       }
 
        mutex_lock(&newsbsec->lock);
 
@@ -1067,6 +1072,7 @@ static int show_sid(struct seq_file *m, u32 sid)
        if (!rc) {
                bool has_comma = context && strchr(context, ',');
 
+               seq_putc(m, '=');
                if (has_comma)
                        seq_putc(m, '\"');
                seq_escape(m, context, "\"\n\\");
@@ -1120,7 +1126,7 @@ static int selinux_sb_show_options(struct seq_file *m, struct super_block *sb)
        }
        if (sbsec->flags & SBLABEL_MNT) {
                seq_putc(m, ',');
-               seq_puts(m, LABELSUPP_STR);
+               seq_puts(m, SECLABEL_STR);
        }
        return 0;
 }
@@ -2739,6 +2745,76 @@ static int selinux_umount(struct vfsmount *mnt, int flags)
                                   FILESYSTEM__UNMOUNT, NULL);
 }
 
+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;
+}
+
+static const struct fs_parameter_spec selinux_param_specs[] = {
+       fsparam_string(CONTEXT_STR,     Opt_context),
+       fsparam_string(DEFCONTEXT_STR,  Opt_defcontext),
+       fsparam_string(FSCONTEXT_STR,   Opt_fscontext),
+       fsparam_string(ROOTCONTEXT_STR, Opt_rootcontext),
+       fsparam_flag  (SECLABEL_STR,    Opt_seclabel),
+       {}
+};
+
+static const struct fs_parameter_description selinux_fs_parameters = {
+       .name           = "SELinux",
+       .specs          = selinux_param_specs,
+};
+
+static int selinux_fs_context_parse_param(struct fs_context *fc,
+                                         struct fs_parameter *param)
+{
+       struct fs_parse_result result;
+       int opt, rc;
+
+       opt = fs_parse(fc, &selinux_fs_parameters, param, &result);
+       if (opt < 0)
+               return opt;
+
+       rc = selinux_add_opt(opt, param->string, &fc->security);
+       if (!rc) {
+               param->string = NULL;
+               rc = 1;
+       }
+       return rc;
+}
+
 /* inode security operations */
 
 static int selinux_inode_alloc_security(struct inode *inode)
@@ -4472,7 +4548,7 @@ err_af:
 }
 
 /* This supports connect(2) and SCTP connect services such as sctp_connectx(3)
- * and sctp_sendmsg(3) as described in Documentation/security/LSM-sctp.rst
+ * and sctp_sendmsg(3) as described in Documentation/security/SCTP.rst
  */
 static int selinux_socket_connect_helper(struct socket *sock,
                                         struct sockaddr *address, int addrlen)
@@ -5061,6 +5137,9 @@ static int selinux_sctp_bind_connect(struct sock *sk, int optname,
                        return -EINVAL;
                }
 
+               if (walk_size + len > addrlen)
+                       return -EINVAL;
+
                err = -EINVAL;
                switch (optname) {
                /* Bind checks */
@@ -6592,6 +6671,9 @@ static struct security_hook_list selinux_hooks[] __lsm_ro_after_init = {
        LSM_HOOK_INIT(bprm_committing_creds, selinux_bprm_committing_creds),
        LSM_HOOK_INIT(bprm_committed_creds, selinux_bprm_committed_creds),
 
+       LSM_HOOK_INIT(fs_context_dup, selinux_fs_context_dup),
+       LSM_HOOK_INIT(fs_context_parse_param, selinux_fs_context_parse_param),
+
        LSM_HOOK_INIT(sb_alloc_security, selinux_sb_alloc_security),
        LSM_HOOK_INIT(sb_free_security, selinux_sb_free_security),
        LSM_HOOK_INIT(sb_eat_lsm_opts, selinux_sb_eat_lsm_opts),
@@ -6837,6 +6919,8 @@ static __init int selinux_init(void)
        else
                pr_debug("SELinux:  Starting in permissive mode\n");
 
+       fs_validate_description(&selinux_fs_parameters);
+
        return 0;
 }