Return-Path: Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1753400Ab3IXB1O (ORCPT ); Mon, 23 Sep 2013 21:27:14 -0400 Received: from tundra.namei.org ([65.99.196.166]:34323 "EHLO tundra.namei.org" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1752744Ab3IXB1M (ORCPT ); Mon, 23 Sep 2013 21:27:12 -0400 Date: Tue, 24 Sep 2013 11:28:57 +1000 (EST) From: James Morris To: Kees Cook cc: linux-kernel@vger.kernel.org, James Morris , Casey Schaufler , linux-security-module@vger.kernel.org Subject: Re: [PATCH] LSM: ModPin LSM for module loading restrictions In-Reply-To: Message-ID: References: <20130920203556.GA8726@www.outflux.net> User-Agent: Alpine 2.02 (LRH 1266 2009-07-14) MIME-Version: 1.0 Content-Type: TEXT/PLAIN; charset=US-ASCII Sender: linux-kernel-owner@vger.kernel.org List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Content-Length: 11002 Lines: 344 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 > > Are you using this for ChromeOS? Also, you should CC Rusty on this. > > > > --- > > 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 > > + * > > + * 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 > > +#include > > +#include > > +#include > > +#include > > +#include > > +#include > > +#include > > + > > +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 = ""; > > + else { > > + /* We will allow 11 spaces for ' (deleted)' to be appended */ > > + alloced = pathname = kmalloc(PATH_MAX+11, GFP_KERNEL); > > + if (!pathname) > > + pathname = ""; > > + else { > > + pathname = d_path(path, pathname, PATH_MAX+11); > > + if (IS_ERR(pathname)) > > + pathname = ""; > > + } > > + } > > + > > + 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 > > -- > 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 -- 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/