Implement filesystem context security hooks for the smack LSM.

Question: Should the ->fs_context_parse_source() hook be implemented to
check the labels on any source devices specified?

Signed-off-by: David Howells <[email protected]>
cc: Casey Schaufler <[email protected]>
cc: [email protected]
---

 security/smack/smack.h     |   11 +
 security/smack/smack_lsm.c |  337 ++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 343 insertions(+), 5 deletions(-)

diff --git a/security/smack/smack.h b/security/smack/smack.h
index f7db791fb566..e6d54e829394 100644
--- a/security/smack/smack.h
+++ b/security/smack/smack.h
@@ -195,11 +195,12 @@ struct smack_known_list_elem {
 
 enum {
        Opt_error = -1,
-       Opt_fsdefault = 1,
-       Opt_fsfloor = 2,
-       Opt_fshat = 3,
-       Opt_fsroot = 4,
-       Opt_fstransmute = 5,
+       Opt_fsdefault = 0,
+       Opt_fsfloor = 1,
+       Opt_fshat = 2,
+       Opt_fsroot = 3,
+       Opt_fstransmute = 4,
+        nr__smack_params
 };
 
 /*
diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c
index 9811476c8441..339ac4d42785 100644
--- a/security/smack/smack_lsm.c
+++ b/security/smack/smack_lsm.c
@@ -42,6 +42,8 @@
 #include <linux/shm.h>
 #include <linux/binfmts.h>
 #include <linux/parser.h>
+#include <linux/fs_context.h>
+#include <linux/fs_parser.h>
 #include "smack.h"
 
 #define TRANS_TRUE     "TRUE"
@@ -521,6 +523,334 @@ static int smack_syslog(int typefrom_file)
        return rc;
 }
 
+/*
+ * Mount context operations
+ */
+
+struct smack_fs_context {
+       union {
+               struct {
+                       char            *fsdefault;
+                       char            *fsfloor;
+                       char            *fshat;
+                       char            *fsroot;
+                       char            *fstransmute;
+               };
+               char                    *ptrs[5];
+
+       };
+       struct superblock_smack         *sbsp;
+       struct inode_smack              *isp;
+       bool                            transmute;
+};
+
+/**
+ * smack_fs_context_free - Free the security data from a filesystem context
+ * @fc: The filesystem context to be cleaned up.
+ */
+static void smack_fs_context_free(struct fs_context *fc)
+{
+       struct smack_fs_context *ctx = fc->security;
+       int i;
+
+       if (ctx) {
+               for (i = 0; i < ARRAY_SIZE(ctx->ptrs); i++)
+                       kfree(ctx->ptrs[i]);
+               kfree(ctx->isp);
+               kfree(ctx->sbsp);
+               kfree(ctx);
+               fc->security = NULL;
+       }
+}
+
+/**
+ * smack_fs_context_alloc - Allocate security data for a filesystem context
+ * @fc: The filesystem context.
+ * @reference: Reference dentry (automount/reconfigure) or NULL
+ *
+ * Returns 0 on success or -ENOMEM on error.
+ */
+static int smack_fs_context_alloc(struct fs_context *fc,
+                                 struct dentry *reference)
+{
+       struct smack_fs_context *ctx;
+       struct superblock_smack *sbsp;
+       struct inode_smack *isp;
+       struct smack_known *skp;
+
+       ctx = kzalloc(sizeof(struct smack_fs_context), GFP_KERNEL);
+       if (!ctx)
+               goto nomem;
+       fc->security = ctx;
+
+       sbsp = kzalloc(sizeof(struct superblock_smack), GFP_KERNEL);
+       if (!sbsp)
+               goto nomem_free;
+       ctx->sbsp = sbsp;
+
+       isp = new_inode_smack(NULL);
+       if (!isp)
+               goto nomem_free;
+       ctx->isp = isp;
+
+       if (reference) {
+               if (reference->d_sb->s_security)
+                       memcpy(sbsp, reference->d_sb->s_security, 
sizeof(*sbsp));
+       } else if (!smack_privileged(CAP_MAC_ADMIN)) {
+               /* Unprivileged mounts get root and default from the caller. */
+               skp = smk_of_current();
+               sbsp->smk_root = skp;
+               sbsp->smk_default = skp;
+       } else {
+               sbsp->smk_root = &smack_known_floor;
+               sbsp->smk_default = &smack_known_floor;
+               sbsp->smk_floor = &smack_known_floor;
+               sbsp->smk_hat = &smack_known_hat;
+               /* SMK_SB_INITIALIZED will be zero from kzalloc. */
+       }
+
+       return 0;
+
+nomem_free:
+       smack_fs_context_free(fc);
+nomem:
+       return -ENOMEM;
+}
+
+/**
+ * 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_fs_context *dst, *src = src_fc->security;
+       int i;
+
+       dst = kzalloc(sizeof(struct smack_fs_context), GFP_KERNEL);
+       if (!dst)
+               goto nomem;
+       fc->security = dst;
+
+       dst->sbsp = kmemdup(src->sbsp, sizeof(struct superblock_smack),
+                           GFP_KERNEL);
+       if (!dst->sbsp)
+               goto nomem_free;
+
+       for (i = 0; i < ARRAY_SIZE(dst->ptrs); i++) {
+               if (src->ptrs[i]) {
+                       dst->ptrs[i] = kstrdup(src->ptrs[i], GFP_KERNEL);
+                       if (!dst->ptrs[i])
+                               goto nomem_free;
+               }
+       }
+
+       return 0;
+
+nomem_free:
+       smack_fs_context_free(fc);
+nomem:
+       return -ENOMEM;
+}
+
+static const struct fs_parameter_spec smack_param_specs[nr__smack_params] = {
+       [Opt_fsdefault]         = { fs_param_is_string },
+       [Opt_fsfloor]           = { fs_param_is_string },
+       [Opt_fshat]             = { fs_param_is_string },
+       [Opt_fsroot]            = { fs_param_is_string },
+       [Opt_fstransmute]       = { fs_param_is_string },
+};
+
+static const struct constant_table smack_param_keys[] = {
+       { SMK_FSDEFAULT,        Opt_fsdefault },
+       { SMK_FSFLOOR,          Opt_fsfloor },
+       { SMK_FSHAT,            Opt_fshat },
+       { SMK_FSROOT,           Opt_fsroot },
+       { SMK_FSTRANS,          Opt_fstransmute },
+};
+
+static const struct fs_parameter_description smack_fs_parameters = {
+       .name           = "smack",
+       .nr_params      = nr__smack_params,
+       .nr_keys        = ARRAY_SIZE(smack_param_keys),
+       .keys           = smack_param_keys,
+       .specs          = smack_param_specs,
+};
+
+/**
+ * smack_fs_context_parse_param - Parse a single mount parameter
+ * @fc: The new filesystem context being constructed.
+ * @param: The parameter.
+ *
+ * Returns 0 on success or -ENOMEM on error.
+ */
+static int smack_fs_context_parse_param(struct fs_context *fc,
+                                       struct fs_parameter *param)
+{
+       struct smack_fs_context *ctx = fc->security;
+       struct fs_parse_result result;
+       int ret;
+
+       /* Unprivileged mounts don't get to specify Smack values. */
+       if (!smack_privileged(CAP_MAC_ADMIN))
+               return -EPERM;
+
+       ret = fs_parse(fc, &smack_fs_parameters, param, &result);
+       if (ret <= 0)
+               return ret; /* Note: 0 indicates no match */
+
+       switch (result.key) {
+       case Opt_fsdefault:
+               if (ctx->fsdefault)
+                       goto error_dup;
+               ctx->fsdefault = param->string;
+               if (!ctx->fsdefault)
+                       goto error;
+               break;
+       case Opt_fsfloor:
+               if (ctx->fsfloor)
+                       goto error_dup;
+               ctx->fsfloor = param->string;
+               if (!ctx->fsfloor)
+                       goto error;
+               break;
+       case Opt_fshat:
+               if (ctx->fshat)
+                       goto error_dup;
+               ctx->fshat = param->string;
+               if (!ctx->fshat)
+                       goto error;
+               break;
+       case Opt_fsroot:
+               if (ctx->fsroot)
+                       goto error_dup;
+               ctx->fsroot = param->string;
+               if (!ctx->fsroot)
+                       goto error;
+               break;
+       case Opt_fstransmute:
+               if (ctx->fstransmute)
+                       goto error_dup;
+               ctx->fstransmute = param->string;
+               if (!ctx->fstransmute)
+                       goto error;
+               break;
+       default:
+               pr_warn("Smack:  unknown mount option\n");
+               goto error_inval;
+       }
+
+       param->string = NULL;
+       return 0;
+
+error_dup:
+       warnf(fc, "Smack: duplicate mount option\n");
+error_inval:
+       ret = -EINVAL;
+error:
+       return ret;
+}
+
+/**
+ * smack_fs_context_validate - Validate the filesystem context security data
+ * @fc: The filesystem context.
+ *
+ * Returns 0 on success or -ENOMEM on error.
+ */
+static int smack_fs_context_validate(struct fs_context *fc)
+{
+       struct smack_fs_context *ctx = fc->security;
+       struct superblock_smack *sbsp = ctx->sbsp;
+       struct inode_smack *isp = ctx->isp;
+       struct smack_known *skp;
+
+       if (ctx->fsdefault) {
+               skp = smk_import_entry(ctx->fsdefault, 0);
+               if (IS_ERR(skp))
+                       return PTR_ERR(skp);
+               sbsp->smk_default = skp;
+       }
+
+       if (ctx->fsfloor) {
+               skp = smk_import_entry(ctx->fsfloor, 0);
+               if (IS_ERR(skp))
+                       return PTR_ERR(skp);
+               sbsp->smk_floor = skp;
+       }
+
+       if (ctx->fshat) {
+               skp = smk_import_entry(ctx->fshat, 0);
+               if (IS_ERR(skp))
+                       return PTR_ERR(skp);
+               sbsp->smk_hat = skp;
+       }
+
+       if (ctx->fsroot || ctx->fstransmute) {
+               skp = smk_import_entry(ctx->fstransmute ?: ctx->fsroot, 0);
+               if (IS_ERR(skp))
+                       return PTR_ERR(skp);
+               sbsp->smk_root = skp;
+               ctx->transmute = !!ctx->fstransmute;
+       }
+
+       isp->smk_inode = sbsp->smk_root;
+       return 0;
+}
+
+/**
+ * smack_sb_get_tree - Assign the context to a newly created superblock
+ * @fc: The new filesystem context.
+ *
+ * Returns 0 on success or -ENOMEM on error.
+ */
+static int smack_sb_get_tree(struct fs_context *fc)
+{
+       struct smack_fs_context *ctx = fc->security;
+       struct superblock_smack *sbsp = ctx->sbsp;
+       struct dentry *root = fc->root;
+       struct inode *inode = d_backing_inode(root);
+       struct super_block *sb = root->d_sb;
+       struct inode_smack *isp;
+       bool transmute = ctx->transmute;
+
+       if (sb->s_security)
+               return 0;
+
+       if (!smack_privileged(CAP_MAC_ADMIN)) {
+               /*
+                * For a handful of fs types with no user-controlled
+                * backing store it's okay to trust security labels
+                * in the filesystem. The rest are untrusted.
+                */
+               if (fc->user_ns != &init_user_ns &&
+                   sb->s_magic != SYSFS_MAGIC && sb->s_magic != TMPFS_MAGIC &&
+                   sb->s_magic != RAMFS_MAGIC) {
+                       transmute = true;
+                       sbsp->smk_flags |= SMK_SB_UNTRUSTED;
+               }
+       }
+
+       sbsp->smk_flags |= SMK_SB_INITIALIZED;
+       sb->s_security = sbsp;
+       ctx->sbsp = NULL;
+
+       /* Initialize the root inode. */
+       isp = inode->i_security;
+       if (isp == NULL) {
+               isp = ctx->isp;
+               ctx->isp = NULL;
+               inode->i_security = isp;
+       } else
+               isp->smk_inode = sbsp->smk_root;
+
+       if (transmute)
+               isp->smk_flags |= SMK_INODE_TRANSMUTE;
+
+       return 0;
+}
 
 /*
  * Superblock Hooks.
@@ -4649,6 +4979,13 @@ 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_alloc, smack_fs_context_alloc),
+       LSM_HOOK_INIT(fs_context_dup, smack_fs_context_dup),
+       LSM_HOOK_INIT(fs_context_free, smack_fs_context_free),
+       LSM_HOOK_INIT(fs_context_parse_param, smack_fs_context_parse_param),
+       LSM_HOOK_INIT(fs_context_validate, smack_fs_context_validate),
+       LSM_HOOK_INIT(sb_get_tree, smack_sb_get_tree),
+
        LSM_HOOK_INIT(sb_alloc_security, smack_sb_alloc_security),
        LSM_HOOK_INIT(sb_free_security, smack_sb_free_security),
        LSM_HOOK_INIT(sb_copy_data, smack_sb_copy_data),

Reply via email to