[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-Id: <8e8810adf4a10bcc74e0f2c78cf0ba910b7567d1.1328122362.git.dmitry.kasatkin@intel.com>
Date: Wed, 1 Feb 2012 22:25:04 +0200
From: Dmitry Kasatkin <dmitry.kasatkin@...el.com>
To: linux-security-module@...r.kernel.org
Cc: jmorris@...ei.org, linux-kernel@...r.kernel.org,
zohar@...ux.vnet.ibm.com
Subject: [RFC][PATCH v1 2/2] integrity: verify module integrity based on signature
This patch adds support for verifying module integrity using digital signature.
Module signature is stored in the modules's 'security.ima' extended attribute
or in the file <module>.sig.
Modprobe and insmod pass the signature via init_module system call as the first
module parameter 'ima=' in hexadecimal format. The module hash is calculated and
is verified against the signature.
modprobe and insmod need to know if kernel module signature verification
support is enabled in order to pass 'ima=' module parameter.
securityfs entry is exported to check if digsig verification support is enabled.
Initially module checking is disabled and enabled by writing 1 to
securityfs/module_check.
Signed-off-by: Dmitry Kasatkin <dmitry.kasatkin@...el.com>
---
Documentation/ABI/testing/securityfs-module-check | 17 ++
security/integrity/Kconfig | 11 +
security/integrity/Makefile | 1 +
security/integrity/module.c | 251 +++++++++++++++++++++
4 files changed, 280 insertions(+), 0 deletions(-)
create mode 100644 Documentation/ABI/testing/securityfs-module-check
create mode 100644 security/integrity/module.c
diff --git a/Documentation/ABI/testing/securityfs-module-check b/Documentation/ABI/testing/securityfs-module-check
new file mode 100644
index 0000000..e8f7ab5
--- /dev/null
+++ b/Documentation/ABI/testing/securityfs-module-check
@@ -0,0 +1,17 @@
+What: securityfs/module_check
+Date: February 2012
+Contact: Dmitry Kasatkin <dmitry.kasatkin@...el.com>
+Description:
+ Integrity provides a hook module_check() which is called from
+ load_module() in order to verify module integrity.
+ Often, when system starts, initramfs is used to perform
+ different initialization tasks before mounting root file system.
+ initramfs is usually verified together with kernel by the
+ boot loader. Public keys to verify modules are not loaded
+ at that point, so to allow initial module loading from initramfs,
+ verification is disabled. After loading keys, module verification
+ can be enabled by:
+
+ echo 1 > <securityfs>/module_check
+
+ After enabling, verification cannot be disabled anymore.
diff --git a/security/integrity/Kconfig b/security/integrity/Kconfig
index 5bd1cc1..813c5f6 100644
--- a/security/integrity/Kconfig
+++ b/security/integrity/Kconfig
@@ -17,5 +17,16 @@ config INTEGRITY_SIGNATURE
This is useful for evm and module keyrings, when keys are
usually only added from initramfs.
+config INTEGRITY_MODULES
+ bool "Modules integrity checking"
+ depends on SECURITY
+ select INTEGRITY_SIGNATURE
+ default n
+ help
+ This option enables modules integrity checking,
+ using digital signatures
+
+ If unsure, say N.
+
source security/integrity/ima/Kconfig
source security/integrity/evm/Kconfig
diff --git a/security/integrity/Makefile b/security/integrity/Makefile
index d43799c..019907c 100644
--- a/security/integrity/Makefile
+++ b/security/integrity/Makefile
@@ -4,6 +4,7 @@
obj-$(CONFIG_INTEGRITY) += integrity.o
obj-$(CONFIG_INTEGRITY_SIGNATURE) += digsig.o
+obj-$(CONFIG_INTEGRITY_MODULES) += module.o
integrity-y := iint.o
diff --git a/security/integrity/module.c b/security/integrity/module.c
new file mode 100644
index 0000000..ae880eb
--- /dev/null
+++ b/security/integrity/module.c
@@ -0,0 +1,251 @@
+/*
+ * Copyright (C) 2011,2012 Intel Corporation
+ *
+ * Authors:
+ * Dmitry Kasatkin <dmitry.kasatkin@...el.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, version 2 of the
+ * License.
+ *
+ * File: module.c
+ * implements the module hooks: module_check.
+ */
+
+#include <linux/module.h>
+#include <linux/scatterlist.h>
+#include <linux/vmalloc.h>
+#include <linux/crypto.h>
+#include <linux/security.h>
+
+#include "integrity.h"
+
+#define MODULE_DIGEST_SIZE SHA1_DIGEST_SIZE
+
+#define MODULE_CHECK_ENABLE 0x01
+#define MODULE_CHECK_ENABLED 0x02
+#define MODULE_CHECK_ENFORCE 0x04
+
+static int module_check_enable = MODULE_CHECK_ENABLE | MODULE_CHECK_ENFORCE;
+static char *module_hash = "sha1";
+
+static int __init module_check_setup(char *str)
+{
+ if (strncmp(str, "off", 3) == 0)
+ module_check_enable = 0;
+ else if (strncmp(str, "ignore", 3) == 0)
+ module_check_enable &= ~MODULE_CHECK_ENFORCE;
+ return 1;
+}
+__setup("module_check=", module_check_setup);
+
+static int init_desc(struct hash_desc *desc)
+{
+ int rc;
+
+ desc->tfm = crypto_alloc_hash(module_hash, 0, CRYPTO_ALG_ASYNC);
+ if (IS_ERR(desc->tfm)) {
+ rc = PTR_ERR(desc->tfm);
+ pr_info("MODULE_CHECK: failed to load %s transform: %d\n",
+ module_hash, rc);
+ return rc;
+ }
+ desc->flags = 0;
+ rc = crypto_hash_init(desc);
+ if (rc)
+ crypto_free_hash(desc->tfm);
+ return rc;
+}
+
+/* last page - first page + 1 */
+#define PAGECOUNT(buf, buflen) \
+ ((((unsigned long)(buf + buflen - 1) & PAGE_MASK) >> PAGE_SHIFT) - \
+ (((unsigned long) buf & PAGE_MASK) >> PAGE_SHIFT) + 1)
+
+/* offset of buf in it's first page */
+#define PAGEOFFSET(buf) ((unsigned long)buf & ~PAGE_MASK)
+
+/**
+ * vmalloc_to_sg() - Make scatterlist from vmallocated buffer
+ * @virt: vmallocated buffer
+ * @len: buffer length
+ *
+ * Asynchronous SHA1 calculation is using scatterlist data. This
+ * function can be used to create scatterlist from vmallocated
+ * data buffer.
+ *
+ * Return pointer to scatterlist or NULL.
+ */
+static struct scatterlist *vmalloc_to_sg(const void *virt, unsigned long len)
+{
+ int nr_pages = PAGECOUNT(virt, len);
+ struct scatterlist *sglist;
+ struct page *pg;
+ int i;
+ int pglen;
+ int pgoff;
+
+ sglist = vmalloc(nr_pages * sizeof(*sglist));
+ if (!sglist)
+ return NULL;
+ memset(sglist, 0, nr_pages * sizeof(*sglist));
+ sg_init_table(sglist, nr_pages);
+ for (i = 0; i < nr_pages; i++, virt += pglen, len -= pglen) {
+ pg = vmalloc_to_page(virt);
+ if (!pg)
+ goto err;
+ pgoff = PAGEOFFSET(virt);
+ pglen = min((PAGE_SIZE - pgoff), len);
+ sg_set_page(&sglist[i], pg, pglen, pgoff);
+ }
+ return sglist;
+err:
+ vfree(sglist);
+ return NULL;
+}
+
+static int calc_module_hash(const void *module, const int module_len,
+ char *digest)
+{
+ struct hash_desc desc;
+ struct scatterlist sg[1], *sgp = sg;
+ int rc = -ENOMEM;
+
+ rc = init_desc(&desc);
+ if (rc != 0)
+ return rc;
+
+ sgp = vmalloc_to_sg(module, module_len);
+ if (!sgp)
+ goto err;
+
+ rc = crypto_hash_update(&desc, sgp, module_len);
+ if (!rc)
+ rc = crypto_hash_final(&desc, digest);
+
+ vfree(sgp);
+err:
+ crypto_free_hash(desc.tfm);
+ return rc;
+}
+
+/**
+ * module_check - check module integrity
+ * @hdr: module data
+ * @len: length of the module
+ * @args: module arguments passed to init_module
+ * @return: 0 on success, error code on failure
+ *
+ * Hook to verify module integrity.
+ *
+ */
+int module_check(const void *hdr, const unsigned long len, char **args)
+{
+ u8 digest[MODULE_DIGEST_SIZE];
+ int rc = -EINVAL, siglen;
+ char *mod_args, *sig, *tmp;
+
+ if (!(module_check_enable & MODULE_CHECK_ENABLED))
+ return 0;
+
+ if (strncmp(*args, "ima=", 4) != 0) {
+ pr_err("IMA: missing 'ima=' option\n");
+ goto err1;
+ }
+
+ /* we need to remove module signature from arg list */
+ mod_args = *args + 4;
+ sig = strsep(&mod_args, " \t");
+ tmp = kstrdup(mod_args ?: "", GFP_KERNEL);
+ if (!tmp) {
+ rc = -ENOMEM;
+ goto err1;
+ }
+
+ siglen = strlen(sig)/2;
+ if (hex2bin((u8 *)sig, sig, siglen) < 0)
+ goto err2;
+
+ rc = calc_module_hash(hdr, len, digest);
+ if (rc < 0)
+ goto err2;
+
+ rc = integrity_digsig_verify(INTEGRITY_KEYRING_MODULE, sig, siglen,
+ digest, MODULE_DIGEST_SIZE);
+ if (rc) {
+ pr_err("MODULE_CHECK: module verification failed: %d", rc);
+ print_hex_dump(KERN_ERR, "hash: ", DUMP_PREFIX_NONE, 32, 1,
+ digest, MODULE_DIGEST_SIZE, 0);
+ }
+
+err2:
+ kfree(*args);
+ *args = tmp;
+err1:
+ return (module_check_enable & MODULE_CHECK_ENFORCE) ? rc : 0;
+}
+
+#define TMPBUFLEN 12
+
+static ssize_t show_module_check(struct file *filp, char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ char tmpbuf[TMPBUFLEN];
+ ssize_t len;
+
+ len = scnprintf(tmpbuf, TMPBUFLEN, "%d\n", module_check_enable);
+ return simple_read_from_buffer(buf, count, ppos, tmpbuf, len);
+}
+
+static ssize_t store_module_check(struct file *file, const char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ int ret, i;
+
+ if (!(module_check_enable & MODULE_CHECK_ENABLE))
+ return 0;
+
+ if (!capable(CAP_SYS_ADMIN) ||
+ (module_check_enable & MODULE_CHECK_ENABLED))
+ return -EPERM;
+
+ ret = kstrtoint_from_user(buf, count, 10, &i);
+ if (ret < 0)
+ return ret;
+
+ if (i != 1)
+ return -EINVAL;
+
+ module_check_enable |= MODULE_CHECK_ENABLED;
+
+ return count;
+}
+
+static const struct file_operations module_ops = {
+ .read = show_module_check,
+ .write = store_module_check,
+};
+
+static struct dentry *module_fs;
+
+static int __init module_check_init(void)
+{
+ module_fs = securityfs_create_file("module_check", S_IRUSR, NULL, NULL,
+ &module_ops);
+ if (IS_ERR(module_fs))
+ return PTR_ERR(module_fs);
+
+ return 0;
+}
+
+static void __exit module_check_cleanup(void)
+{
+ securityfs_remove(module_fs);
+}
+
+module_init(module_check_init);
+module_exit(module_check_cleanup);
+
+MODULE_DESCRIPTION("Module integrity verification");
+MODULE_LICENSE("GPL");
--
1.7.5.4
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@...r.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
Powered by blists - more mailing lists