introduce cloning of fs_context
authorAl Viro <viro@zeniv.linux.org.uk>
Sun, 23 Dec 2018 21:02:47 +0000 (16:02 -0500)
committerAl Viro <viro@zeniv.linux.org.uk>
Thu, 28 Feb 2019 08:29:27 +0000 (03:29 -0500)
new primitive: vfs_dup_fs_context().  Comes with fs_context
method (->dup()) for copying the filesystem-specific parts
of fs_context, along with LSM one (->fs_context_dup()) for
doing the same to LSM parts.

[needs better commit message, and change of Author:, anyway]

Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
fs/fs_context.c
include/linux/fs_context.h
include/linux/lsm_hooks.h
include/linux/security.h
security/security.c
security/selinux/hooks.c
security/smack/smack_lsm.c

index aa7e0ff..57f6183 100644 (file)
@@ -337,6 +337,47 @@ void fc_drop_locked(struct fs_context *fc)
 
 static void legacy_fs_context_free(struct fs_context *fc);
 
+/**
+ * vfs_dup_fc_config: Duplicate a filesystem context.
+ * @src_fc: The context to copy.
+ */
+struct fs_context *vfs_dup_fs_context(struct fs_context *src_fc)
+{
+       struct fs_context *fc;
+       int ret;
+
+       if (!src_fc->ops->dup)
+               return ERR_PTR(-EOPNOTSUPP);
+
+       fc = kmemdup(src_fc, sizeof(struct fs_context), GFP_KERNEL);
+       if (!fc)
+               return ERR_PTR(-ENOMEM);
+
+       fc->fs_private  = NULL;
+       fc->s_fs_info   = NULL;
+       fc->source      = NULL;
+       fc->security    = NULL;
+       get_filesystem(fc->fs_type);
+       get_net(fc->net_ns);
+       get_user_ns(fc->user_ns);
+       get_cred(fc->cred);
+
+       /* Can't call put until we've called ->dup */
+       ret = fc->ops->dup(fc, src_fc);
+       if (ret < 0)
+               goto err_fc;
+
+       ret = security_fs_context_dup(fc, src_fc);
+       if (ret < 0)
+               goto err_fc;
+       return fc;
+
+err_fc:
+       put_fs_context(fc);
+       return ERR_PTR(ret);
+}
+EXPORT_SYMBOL(vfs_dup_fs_context);
+
 /**
  * put_fs_context - Dispose of a superblock configuration context.
  * @fc: The context to dispose of.
@@ -380,6 +421,31 @@ static void legacy_fs_context_free(struct fs_context *fc)
        }
 }
 
+/*
+ * Duplicate a legacy config.
+ */
+static int legacy_fs_context_dup(struct fs_context *fc, struct fs_context *src_fc)
+{
+       struct legacy_fs_context *ctx;
+       struct legacy_fs_context *src_ctx = src_fc->fs_private;
+
+       ctx = kmemdup(src_ctx, sizeof(*src_ctx), GFP_KERNEL);
+       if (!ctx)
+               return -ENOMEM;
+
+       if (ctx->param_type == LEGACY_FS_INDIVIDUAL_PARAMS) {
+               ctx->legacy_data = kmemdup(src_ctx->legacy_data,
+                                          src_ctx->data_size, GFP_KERNEL);
+               if (!ctx->legacy_data) {
+                       kfree(ctx);
+                       return -ENOMEM;
+               }
+       }
+
+       fc->fs_private = ctx;
+       return 0;
+}
+
 /*
  * Add a parameter to a legacy config.  We build up a comma-separated list of
  * options.
@@ -514,6 +580,7 @@ static int legacy_reconfigure(struct fs_context *fc)
 
 const struct fs_context_operations legacy_fs_context_ops = {
        .free                   = legacy_fs_context_free,
+       .dup                    = legacy_fs_context_dup,
        .parse_param            = legacy_parse_param,
        .parse_monolithic       = legacy_parse_monolithic,
        .get_tree               = legacy_get_tree,
index b1a95db..0db0b64 100644 (file)
@@ -94,6 +94,7 @@ struct fs_context {
 
 struct fs_context_operations {
        void (*free)(struct fs_context *fc);
+       int (*dup)(struct fs_context *fc, struct fs_context *src_fc);
        int (*parse_param)(struct fs_context *fc, struct fs_parameter *param);
        int (*parse_monolithic)(struct fs_context *fc, void *data);
        int (*get_tree)(struct fs_context *fc);
@@ -111,6 +112,7 @@ extern struct fs_context *fs_context_for_reconfigure(struct dentry *dentry,
 extern struct fs_context *fs_context_for_submount(struct file_system_type *fs_type,
                                                struct dentry *reference);
 
+extern struct fs_context *vfs_dup_fs_context(struct fs_context *fc);
 extern int vfs_parse_fs_param(struct fs_context *fc, struct fs_parameter *param);
 extern int vfs_parse_fs_string(struct fs_context *fc, const char *key,
                               const char *value, size_t v_size);
index 47ba4db..356e78f 100644 (file)
  * Security hooks for mount using fs_context.
  *     [See also Documentation/filesystems/mounting.txt]
  *
+ * @fs_context_dup:
+ *     Allocate and attach a security structure to sc->security.  This pointer
+ *     is initialised to NULL by the caller.
+ *     @fc indicates the new filesystem context.
+ *     @src_fc indicates the original filesystem context.
  * @fs_context_parse_param:
  *     Userspace provided a parameter to configure a superblock.  The LSM may
  *     reject it with an error and may use it for itself, in which case it
@@ -1470,6 +1475,7 @@ union security_list_options {
        void (*bprm_committing_creds)(struct linux_binprm *bprm);
        void (*bprm_committed_creds)(struct linux_binprm *bprm);
 
+       int (*fs_context_dup)(struct fs_context *fc, struct fs_context *src_sc);
        int (*fs_context_parse_param)(struct fs_context *fc, struct fs_parameter *param);
 
        int (*sb_alloc_security)(struct super_block *sb);
@@ -1813,6 +1819,7 @@ struct security_hook_heads {
        struct hlist_head bprm_check_security;
        struct hlist_head bprm_committing_creds;
        struct hlist_head bprm_committed_creds;
+       struct hlist_head fs_context_dup;
        struct hlist_head fs_context_parse_param;
        struct hlist_head sb_alloc_security;
        struct hlist_head sb_free_security;
index 2da9336..f28a1eb 100644 (file)
@@ -223,6 +223,7 @@ int security_bprm_set_creds(struct linux_binprm *bprm);
 int security_bprm_check(struct linux_binprm *bprm);
 void security_bprm_committing_creds(struct linux_binprm *bprm);
 void security_bprm_committed_creds(struct linux_binprm *bprm);
+int security_fs_context_dup(struct fs_context *fc, struct fs_context *src_fc);
 int security_fs_context_parse_param(struct fs_context *fc, struct fs_parameter *param);
 int security_sb_alloc(struct super_block *sb);
 void security_sb_free(struct super_block *sb);
@@ -521,6 +522,11 @@ static inline void security_bprm_committed_creds(struct linux_binprm *bprm)
 {
 }
 
+static inline int security_fs_context_dup(struct fs_context *fc,
+                                         struct fs_context *src_fc)
+{
+       return 0;
+}
 static inline int security_fs_context_parse_param(struct fs_context *fc,
                                                  struct fs_parameter *param)
 {
index e551948..5759339 100644 (file)
@@ -374,6 +374,11 @@ void security_bprm_committed_creds(struct linux_binprm *bprm)
        call_void_hook(bprm_committed_creds, bprm);
 }
 
+int security_fs_context_dup(struct fs_context *fc, struct fs_context *src_fc)
+{
+       return call_int_hook(fs_context_dup, 0, fc, src_fc);
+}
+
 int security_fs_context_parse_param(struct fs_context *fc, struct fs_parameter *param)
 {
        return call_int_hook(fs_context_parse_param, -ENOPARAM, fc, param);
index f99381e..4ba83de 100644 (file)
@@ -2764,6 +2764,44 @@ 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),
@@ -6745,6 +6783,7 @@ 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),
index 5f93c4f..03176f6 100644 (file)
@@ -647,6 +647,54 @@ out_opt_err:
        return -EINVAL;
 }
 
+/**
+ * smack_fs_context_dup - Duplicate the security data on fs_context duplication
+ * @fc: The new filesystem context.
+ * @src_fc: The source filesystem context being duplicated.
+ *
+ * Returns 0 on success or -ENOMEM on error.
+ */
+static int smack_fs_context_dup(struct fs_context *fc,
+                               struct fs_context *src_fc)
+{
+       struct smack_mnt_opts *dst, *src = src_fc->security;
+
+       if (!src)
+               return 0;
+
+       fc->security = kzalloc(sizeof(struct smack_mnt_opts), GFP_KERNEL);
+       if (!fc->security)
+               return -ENOMEM;
+       dst = fc->security;
+
+       if (src->fsdefault) {
+               dst->fsdefault = kstrdup(src->fsdefault, GFP_KERNEL);
+               if (!dst->fsdefault)
+                       return -ENOMEM;
+       }
+       if (src->fsfloor) {
+               dst->fsfloor = kstrdup(src->fsfloor, GFP_KERNEL);
+               if (!dst->fsfloor)
+                       return -ENOMEM;
+       }
+       if (src->fshat) {
+               dst->fshat = kstrdup(src->fshat, GFP_KERNEL);
+               if (!dst->fshat)
+                       return -ENOMEM;
+       }
+       if (src->fsroot) {
+               dst->fsroot = kstrdup(src->fsroot, GFP_KERNEL);
+               if (!dst->fsroot)
+                       return -ENOMEM;
+       }
+       if (src->fstransmute) {
+               dst->fstransmute = kstrdup(src->fstransmute, GFP_KERNEL);
+               if (!dst->fstransmute)
+                       return -ENOMEM;
+       }
+       return 0;
+}
+
 static const struct fs_parameter_spec smack_param_specs[] = {
        fsparam_string("fsdefault",     Opt_fsdefault),
        fsparam_string("fsfloor",       Opt_fsfloor),
@@ -4626,6 +4674,7 @@ static struct security_hook_list smack_hooks[] __lsm_ro_after_init = {
        LSM_HOOK_INIT(ptrace_traceme, smack_ptrace_traceme),
        LSM_HOOK_INIT(syslog, smack_syslog),
 
+       LSM_HOOK_INIT(fs_context_dup, smack_fs_context_dup),
        LSM_HOOK_INIT(fs_context_parse_param, smack_fs_context_parse_param),
 
        LSM_HOOK_INIT(sb_alloc_security, smack_sb_alloc_security),