]> www.infradead.org Git - users/willy/linux.git/commitdiff
smack: Implement filesystem context security hooks
authorDavid Howells <dhowells@redhat.com>
Tue, 11 Sep 2018 21:20:55 +0000 (22:20 +0100)
committerDavid Howells <dhowells@redhat.com>
Tue, 23 Oct 2018 16:38:57 +0000 (17:38 +0100)
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 <dhowells@redhat.com>
cc: Casey Schaufler <casey@schaufler-ca.com>
cc: linux-security-module@vger.kernel.org

security/smack/smack.h
security/smack/smack_lsm.c

index f7db791fb5660ad14479af3d4b48e104d8bc37ed..891a307a2029b14f840a63bcf685bc08fa76822c 100644 (file)
@@ -195,21 +195,22 @@ 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
 };
 
 /*
  * Mount options
  */
-#define SMK_FSDEFAULT  "smackfsdef="
-#define SMK_FSFLOOR    "smackfsfloor="
-#define SMK_FSHAT      "smackfshat="
-#define SMK_FSROOT     "smackfsroot="
-#define SMK_FSTRANS    "smackfstransmute="
+#define SMK_FSDEFAULT  "smackfsdef"
+#define SMK_FSFLOOR    "smackfsfloor"
+#define SMK_FSHAT      "smackfshat"
+#define SMK_FSROOT     "smackfsroot"
+#define SMK_FSTRANS    "smackfstransmute"
 
 #define SMACK_DELETE_OPTION    "-DELETE"
 #define SMACK_CIPSO_OPTION     "-CIPSO"
index 03a2f0213d57f2a658da661434c2100042653434..da7121d24bce9a3ac3f1a85cb3a6b6b25370bd31 100644 (file)
@@ -43,6 +43,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"
@@ -60,11 +62,11 @@ static struct kmem_cache *smack_inode_cache;
 int smack_enabled;
 
 static const match_table_t smk_mount_tokens = {
-       {Opt_fsdefault, SMK_FSDEFAULT "%s"},
-       {Opt_fsfloor, SMK_FSFLOOR "%s"},
-       {Opt_fshat, SMK_FSHAT "%s"},
-       {Opt_fsroot, SMK_FSROOT "%s"},
-       {Opt_fstransmute, SMK_FSTRANS "%s"},
+       {Opt_fsdefault, SMK_FSDEFAULT "=%s"},
+       {Opt_fsfloor, SMK_FSFLOOR "=%s"},
+       {Opt_fshat, SMK_FSHAT "=%s"},
+       {Opt_fsroot, SMK_FSROOT "=%s"},
+       {Opt_fstransmute, SMK_FSTRANS "=%s"},
        {Opt_error, NULL},
 };
 
@@ -522,6 +524,319 @@ 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 char *const smack_param_keys[nr__smack_params] = {
+       [Opt_fsdefault]         = SMK_FSDEFAULT,
+       [Opt_fsfloor]           = SMK_FSFLOOR,
+       [Opt_fshat]             = SMK_FSHAT,
+       [Opt_fsroot]            = SMK_FSROOT,
+       [Opt_fstransmute]       = SMK_FSTRANS,
+};
+
+static const struct fs_parameter_description smack_fs_parameters = {
+       .name           = "smack",
+       .nr_params      = nr__smack_params,
+       .keys           = smack_param_keys,
+       .specs          = smack_param_specs,
+       .no_source      = true,
+};
+
+/**
+ * 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 opt;
+
+       /* Unprivileged mounts don't get to specify Smack values. */
+       if (!smack_privileged(CAP_MAC_ADMIN))
+               return -EPERM;
+
+       opt = fs_parse(fc, &smack_fs_parameters, param, &result);
+       if (opt < 0)
+               return opt;
+
+       switch (opt) {
+       case Opt_fsdefault:
+               if (ctx->fsdefault)
+                       goto error_dup;
+               ctx->fsdefault = param->string;
+               break;
+       case Opt_fsfloor:
+               if (ctx->fsfloor)
+                       goto error_dup;
+               ctx->fsfloor = param->string;
+               break;
+       case Opt_fshat:
+               if (ctx->fshat)
+                       goto error_dup;
+               ctx->fshat = param->string;
+               break;
+       case Opt_fsroot:
+               if (ctx->fsroot)
+                       goto error_dup;
+               ctx->fsroot = param->string;
+               break;
+       case Opt_fstransmute:
+               if (ctx->fstransmute)
+                       goto error_dup;
+               ctx->fstransmute = param->string;
+               break;
+       default:
+               return invalf(fc, "Smack:  unknown mount option\n");
+       }
+
+       param->string = NULL;
+       return 0;
+
+error_dup:
+       return invalf(fc, "Smack: duplicate mount option\n");
+}
+
+/**
+ * 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.
@@ -4660,6 +4975,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),