All of lore.kernel.org
 help / color / mirror / Atom feed
From: Kees Cook <kees@outflux.net>
To: Kees Cook <keescook@chromium.org>
Cc: James Morris <jmorris@namei.org>,
	LKML <linux-kernel@vger.kernel.org>,
	James Morris <james.l.morris@oracle.com>,
	Casey Schaufler <casey@schaufler-ca.com>,
	linux-security-module <linux-security-module@vger.kernel.org>,
	Rusty Russell <rusty@rustcorp.com.au>
Subject: Re: [PATCH] LSM: ModPin LSM for module loading restrictions
Date: Thu, 3 Oct 2013 13:55:15 -0700	[thread overview]
Message-ID: <20131003205515.GF5729@outflux.net> (raw)
In-Reply-To: <CAGXu5jJ9t7tet4Bx=VUaVmjp1C18iK-nLpQBgmXqgukoAmmd4A@mail.gmail.com>

On Mon, Sep 23, 2013 at 06:45:35PM -0700, Kees Cook wrote:
> [+rusty]
> 
> On Mon, Sep 23, 2013 at 6:28 PM, James Morris <jmorris@namei.org> wrote:
> > On Tue, 24 Sep 2013, James Morris wrote:
> >
> >> On Fri, 20 Sep 2013, Kees Cook wrote:
> >>
> >> > This LSM enforces that modules must all come from the same filesystem,
> >> > with the expectation that such a filesystem is backed by a read-only
> >> > device such as dm-verity or CDROM. This allows systems that have a
> >> > verified or unchanging filesystem to enforce module loading restrictions
> >> > without needing to sign the modules individually.
> >> >
> >> > Signed-off-by: Kees Cook <keescook@chromium.org>
> >>
> >> Are you using this for ChromeOS?
> 
> Yes. Chrome OS uses a read-only root filesystem that is backed by
> dm-verity. This lets us pin all module loading to that filesystem
> without needing per-module signatures.
> 
> > Also, you should CC Rusty on this.
> 
> Done! :)

Ping... any feedback on this? I'd like to get this landed so I can send
further patches that touch this and IMA.

Thanks,

-Kees

> 
> -Kees
> 
> >
> >
> >>
> >>
> >> > ---
> >> >  security/Kconfig         |    6 ++
> >> >  security/Makefile        |    2 +
> >> >  security/modpin/Kconfig  |    9 +++
> >> >  security/modpin/Makefile |    1 +
> >> >  security/modpin/modpin.c |  197 ++++++++++++++++++++++++++++++++++++++++++++++
> >> >  5 files changed, 215 insertions(+)
> >> >  create mode 100644 security/modpin/Kconfig
> >> >  create mode 100644 security/modpin/Makefile
> >> >  create mode 100644 security/modpin/modpin.c
> >> >
> >> > diff --git a/security/Kconfig b/security/Kconfig
> >> > index e9c6ac7..80172fd 100644
> >> > --- a/security/Kconfig
> >> > +++ b/security/Kconfig
> >> > @@ -121,6 +121,7 @@ source security/selinux/Kconfig
> >> >  source security/smack/Kconfig
> >> >  source security/tomoyo/Kconfig
> >> >  source security/apparmor/Kconfig
> >> > +source security/modpin/Kconfig
> >> >  source security/yama/Kconfig
> >> >
> >> >  source security/integrity/Kconfig
> >> > @@ -131,6 +132,7 @@ choice
> >> >     default DEFAULT_SECURITY_SMACK if SECURITY_SMACK
> >> >     default DEFAULT_SECURITY_TOMOYO if SECURITY_TOMOYO
> >> >     default DEFAULT_SECURITY_APPARMOR if SECURITY_APPARMOR
> >> > +   default DEFAULT_SECURITY_MODPIN if SECURITY_MODPIN
> >> >     default DEFAULT_SECURITY_YAMA if SECURITY_YAMA
> >> >     default DEFAULT_SECURITY_DAC
> >> >
> >> > @@ -150,6 +152,9 @@ choice
> >> >     config DEFAULT_SECURITY_APPARMOR
> >> >             bool "AppArmor" if SECURITY_APPARMOR=y
> >> >
> >> > +   config DEFAULT_SECURITY_MODPIN
> >> > +           bool "ModPin" if SECURITY_MODPIN=y
> >> > +
> >> >     config DEFAULT_SECURITY_YAMA
> >> >             bool "Yama" if SECURITY_YAMA=y
> >> >
> >> > @@ -164,6 +169,7 @@ config DEFAULT_SECURITY
> >> >     default "smack" if DEFAULT_SECURITY_SMACK
> >> >     default "tomoyo" if DEFAULT_SECURITY_TOMOYO
> >> >     default "apparmor" if DEFAULT_SECURITY_APPARMOR
> >> > +   default "modpin" if DEFAULT_SECURITY_MODPIN
> >> >     default "yama" if DEFAULT_SECURITY_YAMA
> >> >     default "" if DEFAULT_SECURITY_DAC
> >> >
> >> > diff --git a/security/Makefile b/security/Makefile
> >> > index c26c81e..73d0a05 100644
> >> > --- a/security/Makefile
> >> > +++ b/security/Makefile
> >> > @@ -7,6 +7,7 @@ subdir-$(CONFIG_SECURITY_SELINUX)   += selinux
> >> >  subdir-$(CONFIG_SECURITY_SMACK)            += smack
> >> >  subdir-$(CONFIG_SECURITY_TOMOYO)        += tomoyo
> >> >  subdir-$(CONFIG_SECURITY_APPARMOR) += apparmor
> >> > +subdir-$(CONFIG_SECURITY_MODPIN)   += modpin
> >> >  subdir-$(CONFIG_SECURITY_YAMA)             += yama
> >> >
> >> >  # always enable default capabilities
> >> > @@ -22,6 +23,7 @@ obj-$(CONFIG_SECURITY_SMACK)              += smack/built-in.o
> >> >  obj-$(CONFIG_AUDIT)                        += lsm_audit.o
> >> >  obj-$(CONFIG_SECURITY_TOMOYO)              += tomoyo/built-in.o
> >> >  obj-$(CONFIG_SECURITY_APPARMOR)            += apparmor/built-in.o
> >> > +obj-$(CONFIG_SECURITY_MODPIN)              += modpin/built-in.o
> >> >  obj-$(CONFIG_SECURITY_YAMA)                += yama/built-in.o
> >> >  obj-$(CONFIG_CGROUP_DEVICE)                += device_cgroup.o
> >> >
> >> > diff --git a/security/modpin/Kconfig b/security/modpin/Kconfig
> >> > new file mode 100644
> >> > index 0000000..5be9dd5
> >> > --- /dev/null
> >> > +++ b/security/modpin/Kconfig
> >> > @@ -0,0 +1,9 @@
> >> > +config SECURITY_MODPIN
> >> > +   bool "Module filesystem origin pinning"
> >> > +   depends on SECURITY
> >> > +   help
> >> > +     Module loading will be pinned to the first filesystem used for
> >> > +     loading. Any modules that come from other filesystems will be
> >> > +     rejected. This is best used on systems without an initrd that
> >> > +     have a root filesystem backed by a read-only device such as
> >> > +     dm-verity or a CDROM.
> >> > diff --git a/security/modpin/Makefile b/security/modpin/Makefile
> >> > new file mode 100644
> >> > index 0000000..9080b29
> >> > --- /dev/null
> >> > +++ b/security/modpin/Makefile
> >> > @@ -0,0 +1 @@
> >> > +obj-$(CONFIG_SECURITY_MODPIN) += modpin.o
> >> > diff --git a/security/modpin/modpin.c b/security/modpin/modpin.c
> >> > new file mode 100644
> >> > index 0000000..107b4d8
> >> > --- /dev/null
> >> > +++ b/security/modpin/modpin.c
> >> > @@ -0,0 +1,197 @@
> >> > +/*
> >> > + * Module Pinning Security Module
> >> > + *
> >> > + * Copyright 2011-2013 Google Inc.
> >> > + *
> >> > + * Authors:
> >> > + *      Kees Cook       <keescook@chromium.org>
> >> > + *
> >> > + * This software is licensed under the terms of the GNU General Public
> >> > + * License version 2, as published by the Free Software Foundation, and
> >> > + * may be copied, distributed, and modified under those terms.
> >> > + *
> >> > + * This program is distributed in the hope that it will be useful,
> >> > + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> >> > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> >> > + * GNU General Public License for more details.
> >> > + */
> >> > +
> >> > +#define pr_fmt(fmt) "ModPin LSM: " fmt
> >> > +
> >> > +#include <linux/module.h>
> >> > +#include <linux/security.h>
> >> > +#include <linux/sched.h>
> >> > +#include <linux/fs.h>
> >> > +#include <linux/fs_struct.h>
> >> > +#include <linux/mount.h>
> >> > +#include <linux/path.h>
> >> > +#include <linux/root_dev.h>
> >> > +
> >> > +static void report_load_module(struct path *path, char *operation)
> >> > +{
> >> > +   char *alloced = NULL;
> >> > +   char *pathname; /* Pointer to either static string or "alloced". */
> >> > +
> >> > +   if (!path)
> >> > +           pathname = "<unknown>";
> >> > +   else {
> >> > +           /* We will allow 11 spaces for ' (deleted)' to be appended */
> >> > +           alloced = pathname = kmalloc(PATH_MAX+11, GFP_KERNEL);
> >> > +           if (!pathname)
> >> > +                   pathname = "<no_memory>";
> >> > +           else {
> >> > +                   pathname = d_path(path, pathname, PATH_MAX+11);
> >> > +                   if (IS_ERR(pathname))
> >> > +                           pathname = "<too_long>";
> >> > +           }
> >> > +   }
> >> > +
> >> > +   pr_notice("init_module %s module=%s pid=%d\n",
> >> > +             operation, pathname, task_pid_nr(current));
> >> > +
> >> > +   kfree(alloced);
> >> > +}
> >> > +
> >> > +static int modpin_enforced = 1;
> >> > +static struct dentry *pinned_root;
> >> > +static DEFINE_SPINLOCK(pinned_root_spinlock);
> >> > +
> >> > +#ifdef CONFIG_SYSCTL
> >> > +static int zero;
> >> > +static int one = 1;
> >> > +
> >> > +static struct ctl_path modpin_sysctl_path[] = {
> >> > +   { .procname = "kernel", },
> >> > +   { .procname = "modpin", },
> >> > +   { }
> >> > +};
> >> > +
> >> > +static struct ctl_table modpin_sysctl_table[] = {
> >> > +   {
> >> > +           .procname       = "enforced",
> >> > +           .data           = &modpin_enforced,
> >> > +           .maxlen         = sizeof(int),
> >> > +           .mode           = 0644,
> >> > +           .proc_handler   = proc_dointvec_minmax,
> >> > +           .extra1         = &zero,
> >> > +           .extra2         = &one,
> >> > +   },
> >> > +   { }
> >> > +};
> >> > +
> >> > +/*
> >> > + * Check if the root device is read-only (e.g. dm-verity is enabled).
> >> > + * This must be called after early kernel init, since only then is the
> >> > + * rootdev available.
> >> > + */
> >> > +static bool rootdev_readonly(void)
> >> > +{
> >> > +   bool rc;
> >> > +   struct block_device *bdev;
> >> > +   const fmode_t mode = FMODE_WRITE;
> >> > +
> >> > +   bdev = blkdev_get_by_dev(ROOT_DEV, mode, NULL);
> >> > +   if (IS_ERR(bdev)) {
> >> > +           /* In this weird case, assume it is read-only. */
> >> > +           pr_info("dev(%u,%u): FMODE_WRITE disallowed?!\n",
> >> > +                   MAJOR(ROOT_DEV), MINOR(ROOT_DEV));
> >> > +           return true;
> >> > +   }
> >> > +
> >> > +   rc = bdev_read_only(bdev);
> >> > +   blkdev_put(bdev, mode);
> >> > +
> >> > +   pr_info("dev(%u,%u): %s\n", MAJOR(ROOT_DEV), MINOR(ROOT_DEV),
> >> > +           rc ? "read-only" : "writable");
> >> > +
> >> > +   return rc;
> >> > +}
> >> > +
> >> > +static void check_pinning_enforcement(void)
> >> > +{
> >> > +   /*
> >> > +    * If module pinning is not being enforced, allow sysctl to change
> >> > +    * modes for testing.
> >> > +    */
> >> > +   if (!rootdev_readonly()) {
> >> > +           if (!register_sysctl_paths(modpin_sysctl_path,
> >> > +                                      modpin_sysctl_table))
> >> > +                   pr_notice("sysctl registration failed!\n");
> >> > +           else
> >> > +                   pr_info("module pinning can be disabled.\n");
> >> > +   } else
> >> > +           pr_info("module pinning engaged.\n");
> >> > +}
> >> > +#else
> >> > +static void check_pinning_enforcement(void) { }
> >> > +#endif
> >> > +
> >> > +
> >> > +static int modpin_load_module(struct file *file)
> >> > +{
> >> > +   struct dentry *module_root;
> >> > +
> >> > +   if (!file) {
> >> > +           if (!modpin_enforced) {
> >> > +                   report_load_module(NULL, "old-api-pinning-ignored");
> >> > +                   return 0;
> >> > +           }
> >> > +
> >> > +           report_load_module(NULL, "old-api-denied");
> >> > +           return -EPERM;
> >> > +   }
> >> > +
> >> > +   module_root = file->f_path.mnt->mnt_root;
> >> > +
> >> > +   /* First loaded module defines the root for all others. */
> >> > +   spin_lock(&pinned_root_spinlock);
> >> > +   if (!pinned_root) {
> >> > +           pinned_root = dget(module_root);
> >> > +           /*
> >> > +            * Unlock now since it's only pinned_root we care about.
> >> > +            * In the worst case, we will (correctly) report pinning
> >> > +            * failures before we have announced that pinning is
> >> > +            * enabled. This would be purely cosmetic.
> >> > +            */
> >> > +           spin_unlock(&pinned_root_spinlock);
> >> > +           check_pinning_enforcement();
> >> > +           report_load_module(&file->f_path, "pinned");
> >> > +           return 0;
> >> > +   }
> >> > +   spin_unlock(&pinned_root_spinlock);
> >> > +
> >> > +   if (module_root != pinned_root) {
> >> > +           if (unlikely(!modpin_enforced)) {
> >> > +                   report_load_module(&file->f_path, "pinning-ignored");
> >> > +                   return 0;
> >> > +           }
> >> > +
> >> > +           report_load_module(&file->f_path, "denied");
> >> > +           return -EPERM;
> >> > +   }
> >> > +
> >> > +   return 0;
> >> > +}
> >> > +
> >> > +static struct security_operations modpin_ops = {
> >> > +   .name   = "modpin",
> >> > +   .kernel_module_from_file = modpin_load_module,
> >> > +};
> >> > +
> >> > +static int __init modpin_init(void)
> >> > +{
> >> > +   int error;
> >> > +
> >> > +   error = register_security(&modpin_ops);
> >> > +
> >> > +   if (error)
> >> > +           panic("Could not register ModPin security module");
> >> > +
> >> > +   pr_info("ready to pin.\n");
> >> > +
> >> > +   return error;
> >> > +}
> >> > +security_initcall(modpin_init);
> >> > +
> >> > +module_param(modpin_enforced, int, S_IRUSR);
> >> > +MODULE_PARM_DESC(modpin_enforced, "Module pinning enforced (default: true)");
> >> > --
> >> > 1.7.9.5
> >> >
> >> >
> >> > --
> >> > Kees Cook
> >> > Chrome OS Security
> >> > --
> >> > To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
> >> > the body of a message to majordomo@vger.kernel.org
> >> > More majordomo info at  http://vger.kernel.org/majordomo-info.html
> >> > Please read the FAQ at  http://www.tux.org/lkml/
> >> >
> >>
> >> --
> >> James Morris
> >> <jmorris@namei.org>
> >> --
> >> To unsubscribe from this list: send the line "unsubscribe linux-security-module" in
> >> the body of a message to majordomo@vger.kernel.org
> >> More majordomo info at  http://vger.kernel.org/majordomo-info.html
> >>
> >
> > --
> > James Morris
> > <jmorris@namei.org>
> 
> 
> 
> -- 
> Kees Cook
> Chrome OS Security
> --
> To unsubscribe from this list: send the line "unsubscribe linux-security-module" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html

-- 
Kees Cook                                            @outflux.net

  reply	other threads:[~2013-10-03 20:55 UTC|newest]

Thread overview: 23+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2013-09-20 20:35 [PATCH] LSM: ModPin LSM for module loading restrictions Kees Cook
2013-09-24  1:24 ` James Morris
2013-09-24  1:28   ` James Morris
2013-09-24  1:45     ` Kees Cook
2013-10-03 20:55       ` Kees Cook [this message]
2013-10-03 21:31         ` Tetsuo Handa
2013-10-03 21:36           ` Kees Cook
2013-10-23  2:55             ` Lucas De Marchi
2013-10-16 15:18       ` Kees Cook
2013-10-16 20:47         ` Tetsuo Handa
2013-10-16 21:42           ` Casey Schaufler
2013-10-16 22:43             ` Kees Cook
2013-10-17  0:37               ` Tetsuo Handa
2013-10-26 13:51                 ` Tetsuo Handa
2013-11-02 19:39                   ` Casey Schaufler
2013-10-18  2:25               ` Casey Schaufler
2013-10-17  8:02 ` James Morris
2013-10-17 11:30   ` Jarkko Sakkinen
2013-10-17 21:00     ` Kees Cook
2013-10-17 17:26   ` Casey Schaufler
2013-10-17 21:09     ` Kees Cook
2013-10-23  0:02     ` James Morris
2013-10-23  1:03       ` Casey Schaufler

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20131003205515.GF5729@outflux.net \
    --to=kees@outflux.net \
    --cc=casey@schaufler-ca.com \
    --cc=james.l.morris@oracle.com \
    --cc=jmorris@namei.org \
    --cc=keescook@chromium.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-security-module@vger.kernel.org \
    --cc=rusty@rustcorp.com.au \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.