lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<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

Powered by Openwall GNU/*/Linux Powered by OpenVZ