- * [RFC PATCH v6 01/11] scripts: add ipe tooling to generate boot policy
  2020-07-30  0:31 [RFC PATCH v6 00/11] Integrity Policy Enforcement LSM (IPE) Deven Bowers
@ 2020-07-30  0:31 ` Deven Bowers
  2020-07-30  0:31 ` [RFC PATCH v6 02/11] security: add ipe lsm evaluation loop and audit system Deven Bowers
                   ` (9 subsequent siblings)
  10 siblings, 0 replies; 12+ messages in thread
From: Deven Bowers @ 2020-07-30  0:31 UTC (permalink / raw)
  To: agk, axboe, snitzer, jmorris, serge, zohar, viro, paul, eparis,
	jannh, dm-devel, linux-integrity, linux-security-module,
	linux-fsdevel, linux-block, linux-audit
  Cc: tyhicks, linux-kernel, corbet, sashal, jaskarankhurana, mdsakib,
	nramas, pasha.tatashin
Add a tool for the generation of an IPE policy to be compiled into the
kernel. This policy will be enforced until userland deploys and activates
a new policy.
Signed-off-by: Deven Bowers <deven.desai@linux.microsoft.com>
---
 MAINTAINERS                   |   6 ++
 scripts/Makefile              |   1 +
 scripts/ipe/Makefile          |   2 +
 scripts/ipe/polgen/.gitignore |   1 +
 scripts/ipe/polgen/Makefile   |   7 ++
 scripts/ipe/polgen/polgen.c   | 136 ++++++++++++++++++++++++++++++++++
 6 files changed, 153 insertions(+)
 create mode 100644 scripts/ipe/Makefile
 create mode 100644 scripts/ipe/polgen/.gitignore
 create mode 100644 scripts/ipe/polgen/Makefile
 create mode 100644 scripts/ipe/polgen/polgen.c
diff --git a/MAINTAINERS b/MAINTAINERS
index f0569cf304ca..d29c2e4612e4 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -8580,6 +8580,12 @@ S:	Supported
 T:	git git://git.kernel.org/pub/scm/linux/kernel/git/zohar/linux-integrity.git
 F:	security/integrity/ima/
 
+INTEGRITY POLICY ENFORCEMENT (IPE)
+M:	Deven Bowers <deven.desai@linux.microsoft.com>
+L:	linux-integrity@vger.kernel.org
+S:	Supported
+F:	scripts/ipe/
+
 INTEL 810/815 FRAMEBUFFER DRIVER
 M:	Antonino Daplas <adaplas@gmail.com>
 L:	linux-fbdev@vger.kernel.org
diff --git a/scripts/Makefile b/scripts/Makefile
index 95ecf970c74c..b3c1882fd6dd 100644
--- a/scripts/Makefile
+++ b/scripts/Makefile
@@ -34,6 +34,7 @@ hostprogs += unifdef
 subdir-$(CONFIG_GCC_PLUGINS) += gcc-plugins
 subdir-$(CONFIG_MODVERSIONS) += genksyms
 subdir-$(CONFIG_SECURITY_SELINUX) += selinux
+subdir-$(CONFIG_SECURITY_IPE) += ipe
 
 # Let clean descend into subdirs
 subdir-	+= basic dtc gdb kconfig mod
diff --git a/scripts/ipe/Makefile b/scripts/ipe/Makefile
new file mode 100644
index 000000000000..e87553fbb8d6
--- /dev/null
+++ b/scripts/ipe/Makefile
@@ -0,0 +1,2 @@
+# SPDX-License-Identifier: GPL-2.0-only
+subdir-y := polgen
diff --git a/scripts/ipe/polgen/.gitignore b/scripts/ipe/polgen/.gitignore
new file mode 100644
index 000000000000..80f32f25d200
--- /dev/null
+++ b/scripts/ipe/polgen/.gitignore
@@ -0,0 +1 @@
+polgen
diff --git a/scripts/ipe/polgen/Makefile b/scripts/ipe/polgen/Makefile
new file mode 100644
index 000000000000..a519b594e13c
--- /dev/null
+++ b/scripts/ipe/polgen/Makefile
@@ -0,0 +1,7 @@
+# SPDX-License-Identifier: GPL-2.0
+hostprogs-y	:= polgen
+HOST_EXTRACFLAGS += \
+	-I$(srctree)/include \
+	-I$(srctree)/include/uapi \
+
+always		:= $(hostprogs-y)
diff --git a/scripts/ipe/polgen/polgen.c b/scripts/ipe/polgen/polgen.c
new file mode 100644
index 000000000000..a80fffe1b27c
--- /dev/null
+++ b/scripts/ipe/polgen/polgen.c
@@ -0,0 +1,136 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) Microsoft Corporation. All rights reserved.
+ */
+
+#include <stdlib.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <errno.h>
+
+static void usage(const char *const name)
+{
+	printf("Usage: %s OutputFile (PolicyFile)\n", name);
+	exit(EINVAL);
+}
+
+static int policy_to_buffer(const char *pathname, char **buffer, size_t *size)
+{
+	int rc = 0;
+	FILE *fd;
+	char *lbuf;
+	size_t fsize;
+	size_t read;
+
+	fd = fopen(pathname, "r");
+	if (!fd) {
+		rc = errno;
+		goto out;
+	}
+
+	fseek(fd, 0, SEEK_END);
+	fsize = ftell(fd);
+	rewind(fd);
+
+	lbuf = malloc(fsize);
+	if (!lbuf) {
+		rc = ENOMEM;
+		goto out_close;
+	}
+
+	read = fread((void *)lbuf, sizeof(*lbuf), fsize, fd);
+	if (read != fsize) {
+		rc = -1;
+		goto out_free;
+	}
+
+	*buffer = lbuf;
+	*size = fsize;
+	fclose(fd);
+
+	return rc;
+
+out_free:
+	free(lbuf);
+out_close:
+	fclose(fd);
+out:
+	return rc;
+}
+
+static int write_boot_policy(const char *pathname, const char *buf, size_t size)
+{
+	FILE *fd;
+	size_t i;
+
+	fd = fopen(pathname, "w");
+	if (!fd)
+		goto err;
+
+	fprintf(fd, "/* This file is automatically generated.");
+	fprintf(fd, " Do not edit. */\n");
+	fprintf(fd, "#include <stddef.h>\n");
+	fprintf(fd, "const char *const ipe_boot_policy =\n");
+
+	if (!buf || size == 0) {
+		fprintf(fd, "\tNULL;\n");
+		fclose(fd);
+		return 0;
+	}
+
+	for (i = 0; i < size; ++i) {
+		if (i == 0)
+			fprintf(fd, "\t\"");
+
+		switch (buf[i]) {
+		case '"':
+			fprintf(fd, "\\\"");
+			break;
+		case '\'':
+			fprintf(fd, "'");
+			break;
+		case '\n':
+			fprintf(fd, "\\n\"\n\t\"");
+			break;
+		case '\\':
+			fprintf(fd, "\\\\");
+			break;
+		default:
+			fprintf(fd, "%c", buf[i]);
+		}
+	}
+	fprintf(fd, "\";\n");
+	fclose(fd);
+
+	return 0;
+
+err:
+	if (fd)
+		fclose(fd);
+	return errno;
+}
+
+int main(int argc, const char *argv[])
+{
+	int rc = 0;
+	size_t len = 0;
+	char *policy = NULL;
+
+	if (argc < 2)
+		usage(argv[0]);
+
+	if (argc > 2) {
+		rc = policy_to_buffer(argv[2], &policy, &len);
+		if (rc != 0)
+			goto cleanup;
+	}
+
+	rc = write_boot_policy(argv[1], policy, len);
+cleanup:
+	if (policy)
+		free(policy);
+	if (rc != 0)
+		perror("An error occurred during policy conversion: ");
+	return rc;
+}
-- 
2.27.0
^ permalink raw reply related	[flat|nested] 12+ messages in thread
- * [RFC PATCH v6 02/11] security: add ipe lsm evaluation loop and audit system
  2020-07-30  0:31 [RFC PATCH v6 00/11] Integrity Policy Enforcement LSM (IPE) Deven Bowers
  2020-07-30  0:31 ` [RFC PATCH v6 01/11] scripts: add ipe tooling to generate boot policy Deven Bowers
@ 2020-07-30  0:31 ` Deven Bowers
  2020-07-30  0:31 ` [RFC PATCH v6 03/11] security: add ipe lsm policy parser and policy loading Deven Bowers
                   ` (8 subsequent siblings)
  10 siblings, 0 replies; 12+ messages in thread
From: Deven Bowers @ 2020-07-30  0:31 UTC (permalink / raw)
  To: agk, axboe, snitzer, jmorris, serge, zohar, viro, paul, eparis,
	jannh, dm-devel, linux-integrity, linux-security-module,
	linux-fsdevel, linux-block, linux-audit
  Cc: tyhicks, linux-kernel, corbet, sashal, jaskarankhurana, mdsakib,
	nramas, pasha.tatashin
Add the core logic of the IPE LSM, the evaluation loop (engine),
a portion of the audit system, and the skeleton of the policy
structure.
Signed-off-by: Deven Bowers <deven.desai@linux.microsoft.com>
---
 MAINTAINERS                      |   1 +
 include/uapi/linux/audit.h       |   4 +
 security/Kconfig                 |  12 +-
 security/Makefile                |   2 +
 security/ipe/.gitignore          |   2 +
 security/ipe/Kconfig             |  44 ++++++
 security/ipe/Makefile            |  25 ++++
 security/ipe/ipe-audit.c         | 231 +++++++++++++++++++++++++++++++
 security/ipe/ipe-audit.h         |  18 +++
 security/ipe/ipe-engine.c        | 205 +++++++++++++++++++++++++++
 security/ipe/ipe-engine.h        |  37 +++++
 security/ipe/ipe-hooks.c         | 149 ++++++++++++++++++++
 security/ipe/ipe-hooks.h         |  61 ++++++++
 security/ipe/ipe-policy.h        |  62 +++++++++
 security/ipe/ipe-prop-internal.h |  37 +++++
 security/ipe/ipe-property.c      | 142 +++++++++++++++++++
 security/ipe/ipe-property.h      |  99 +++++++++++++
 security/ipe/ipe.c               |  67 +++++++++
 security/ipe/ipe.h               |  20 +++
 19 files changed, 1212 insertions(+), 6 deletions(-)
 create mode 100644 security/ipe/.gitignore
 create mode 100644 security/ipe/Kconfig
 create mode 100644 security/ipe/Makefile
 create mode 100644 security/ipe/ipe-audit.c
 create mode 100644 security/ipe/ipe-audit.h
 create mode 100644 security/ipe/ipe-engine.c
 create mode 100644 security/ipe/ipe-engine.h
 create mode 100644 security/ipe/ipe-hooks.c
 create mode 100644 security/ipe/ipe-hooks.h
 create mode 100644 security/ipe/ipe-policy.h
 create mode 100644 security/ipe/ipe-prop-internal.h
 create mode 100644 security/ipe/ipe-property.c
 create mode 100644 security/ipe/ipe-property.h
 create mode 100644 security/ipe/ipe.c
 create mode 100644 security/ipe/ipe.h
diff --git a/MAINTAINERS b/MAINTAINERS
index d29c2e4612e4..e817e4bfeae3 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -8585,6 +8585,7 @@ M:	Deven Bowers <deven.desai@linux.microsoft.com>
 L:	linux-integrity@vger.kernel.org
 S:	Supported
 F:	scripts/ipe/
+F:	security/ipe/
 
 INTEL 810/815 FRAMEBUFFER DRIVER
 M:	Antonino Daplas <adaplas@gmail.com>
diff --git a/include/uapi/linux/audit.h b/include/uapi/linux/audit.h
index 9b6a973f4cc3..5a634cca1d42 100644
--- a/include/uapi/linux/audit.h
+++ b/include/uapi/linux/audit.h
@@ -154,6 +154,10 @@
 #define AUDIT_INTEGRITY_RULE	    1805 /* policy rule */
 #define AUDIT_INTEGRITY_EVM_XATTR   1806 /* New EVM-covered xattr */
 #define AUDIT_INTEGRITY_POLICY_RULE 1807 /* IMA policy rules */
+#define AUDIT_INTEGRITY_POLICY_LOAD	1808 /* IPE Policy Load */
+#define AUDIT_INTEGRITY_POLICY_ACTIVATE	1809 /* IPE Policy Activation */
+#define AUDIT_INTEGRITY_EVENT		1810 /* IPE Evaluation Event */
+#define AUDIT_INTEGRITY_MODE		1811 /* IPE Mode Switch */
 
 #define AUDIT_KERNEL		2000	/* Asynchronous audit record. NOT A REQUEST. */
 
diff --git a/security/Kconfig b/security/Kconfig
index cd3cc7da3a55..94924556b637 100644
--- a/security/Kconfig
+++ b/security/Kconfig
@@ -238,6 +238,7 @@ source "security/loadpin/Kconfig"
 source "security/yama/Kconfig"
 source "security/safesetid/Kconfig"
 source "security/lockdown/Kconfig"
+source "security/ipe/Kconfig"
 
 source "security/integrity/Kconfig"
 
@@ -277,11 +278,11 @@ endchoice
 
 config LSM
 	string "Ordered list of enabled LSMs"
-	default "lockdown,yama,loadpin,safesetid,integrity,smack,selinux,tomoyo,apparmor,bpf" if DEFAULT_SECURITY_SMACK
-	default "lockdown,yama,loadpin,safesetid,integrity,apparmor,selinux,smack,tomoyo,bpf" if DEFAULT_SECURITY_APPARMOR
-	default "lockdown,yama,loadpin,safesetid,integrity,tomoyo,bpf" if DEFAULT_SECURITY_TOMOYO
-	default "lockdown,yama,loadpin,safesetid,integrity,bpf" if DEFAULT_SECURITY_DAC
-	default "lockdown,yama,loadpin,safesetid,integrity,selinux,smack,tomoyo,apparmor,bpf"
+	default "lockdown,yama,loadpin,ipe,safesetid,integrity,smack,selinux,tomoyo,apparmor,bpf" if DEFAULT_SECURITY_SMACK
+	default "lockdown,yama,loadpin,ipe,safesetid,integrity,apparmor,selinux,smack,tomoyo,bpf" if DEFAULT_SECURITY_APPARMOR
+	default "lockdown,yama,loadpin,ipe,safesetid,integrity,tomoyo,bpf" if DEFAULT_SECURITY_TOMOYO
+	default "lockdown,yama,loadpin,ipe,safesetid,integrity,bpf" if DEFAULT_SECURITY_DAC
+	default "lockdown,yama,loadpin,ipe,safesetid,integrity,selinux,smack,tomoyo,apparmor,bpf"
 	help
 	  A comma-separated list of LSMs, in initialization order.
 	  Any LSMs left off this list will be ignored. This can be
@@ -292,4 +293,3 @@ config LSM
 source "security/Kconfig.hardening"
 
 endmenu
-
diff --git a/security/Makefile b/security/Makefile
index 3baf435de541..48bd063d66e1 100644
--- a/security/Makefile
+++ b/security/Makefile
@@ -13,6 +13,7 @@ subdir-$(CONFIG_SECURITY_LOADPIN)	+= loadpin
 subdir-$(CONFIG_SECURITY_SAFESETID)    += safesetid
 subdir-$(CONFIG_SECURITY_LOCKDOWN_LSM)	+= lockdown
 subdir-$(CONFIG_BPF_LSM)		+= bpf
+subdir-$(CONFIG_SECURITY_IPE)		+= ipe
 
 # always enable default capabilities
 obj-y					+= commoncap.o
@@ -32,6 +33,7 @@ obj-$(CONFIG_SECURITY_SAFESETID)       += safesetid/
 obj-$(CONFIG_SECURITY_LOCKDOWN_LSM)	+= lockdown/
 obj-$(CONFIG_CGROUPS)			+= device_cgroup.o
 obj-$(CONFIG_BPF_LSM)			+= bpf/
+obj-$(CONFIG_SECURITY_IPE)		+= ipe/
 
 # Object integrity file lists
 subdir-$(CONFIG_INTEGRITY)		+= integrity
diff --git a/security/ipe/.gitignore b/security/ipe/.gitignore
new file mode 100644
index 000000000000..bbf824e665d7
--- /dev/null
+++ b/security/ipe/.gitignore
@@ -0,0 +1,2 @@
+# Generated Boot Policy
+ipe-bp.c
diff --git a/security/ipe/Kconfig b/security/ipe/Kconfig
new file mode 100644
index 000000000000..7615109a19ca
--- /dev/null
+++ b/security/ipe/Kconfig
@@ -0,0 +1,44 @@
+# SPDX-License-Identifier: GPL-2.0-only
+#
+# Integrity Policy Enforcement (IPE) configuration
+#
+
+menuconfig SECURITY_IPE
+	bool "Integrity Policy Enforcement (IPE)"
+	depends on SECURITY && AUDIT
+	select SYSTEM_DATA_VERIFICATION
+	help
+	  This option enables the Integrity Policy Enforcement subsystem
+	  allowing systems to enforce integrity having no dependencies
+	  on filesystem metadata, making its decisions based off of kernel-
+	  resident features and data structures. A key feature of IPE is a
+	  customizable policy to allow admins to reconfigure integrity
+	  requirements on the fly.
+
+	  If unsure, answer N.
+
+if SECURITY_IPE
+
+config SECURITY_IPE_BOOT_POLICY
+	string "Integrity policy to apply on system startup"
+	help
+	  This option specifies a filepath to a IPE policy that is compiled
+	  into the kernel. This policy will be enforced until a policy update
+	  is deployed via the $securityfs/ipe/policies/$policy_name/active
+	  interface.
+
+	  If unsure, leave blank.
+
+config SECURITY_IPE_PERMISSIVE_SWITCH
+	bool "Enable the ability to switch IPE to permissive mode"
+	default y
+	help
+	  This option enables two ways of switching IPE to permissive mode,
+	  a securityfs node, `$securityfs/ipe/enforce`, or a kernel command line
+	  parameter, `ipe.enforce`. If either of these is set to 0, files
+	  will be subject to IPE's policy, audit messages will be logged, but
+	  the policy will not be enforced.
+
+	  If unsure, answer Y.
+
+endif
diff --git a/security/ipe/Makefile b/security/ipe/Makefile
new file mode 100644
index 000000000000..40565b73fac2
--- /dev/null
+++ b/security/ipe/Makefile
@@ -0,0 +1,25 @@
+# SPDX-License-Identifier: GPL-2.0
+#
+# Copyright (C) Microsoft Corporation. All rights reserved.
+#
+# Makefile for building the IPE module as part of the kernel tree.
+#
+
+quiet_cmd_polgen  = IPE_POL $(patsubst "%",%,$(2))
+      cmd_polgen  = scripts/ipe/polgen/polgen security/ipe/ipe-bp.c $(2)
+
+$(eval $(call config_filename,SECURITY_IPE_BOOT_POLICY))
+
+targets += ipe-bp.c
+$(obj)/ipe-bp.c: scripts/ipe/polgen/polgen $(SECURITY_IPE_BOOT_POLICY_FILENAME) FORCE
+	$(call if_changed,polgen,$(SECURITY_IPE_BOOT_POLICY_FILENAME))
+
+obj-$(CONFIG_SECURITY_IPE) += \
+	ipe.o \
+	ipe-audit.o \
+	ipe-bp.o \
+	ipe-engine.o \
+	ipe-property.o \
+	ipe-hooks.o \
+
+clean-files := ipe-bp.c
diff --git a/security/ipe/ipe-audit.c b/security/ipe/ipe-audit.c
new file mode 100644
index 000000000000..2c754851bd40
--- /dev/null
+++ b/security/ipe/ipe-audit.c
@@ -0,0 +1,231 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) Microsoft Corporation. All rights reserved.
+ */
+
+#include "ipe.h"
+#include "ipe-audit.h"
+#include "ipe-engine.h"
+#include "ipe-prop-internal.h"
+
+#include <linux/types.h>
+#include <linux/audit.h>
+#include <linux/rcupdate.h>
+#include <linux/lsm_audit.h>
+#include <linux/rbtree.h>
+#include <crypto/hash.h>
+#include <crypto/sha1_base.h>
+
+#define ACTION_STR(a) ((a) == ipe_action_allow ? "ALLOW" : "DENY")
+
+#define IPE_UNKNOWN		"UNKNOWN"
+
+/* Keep in sync with ipe_op in ipe-hooks.h */
+const char *audit_op_names[] = {
+	IPE_OP_EXECUTE,
+	IPE_OP_FIRMWARE,
+	IPE_OP_KEXEC_IMAGE,
+	IPE_OP_KEXEC_INITRAMFS,
+	IPE_OP_X509_CERTIFICATE,
+	IPE_OP_POLICY,
+	IPE_OP_KMODULE,
+	IPE_OP_KERNEL_READ,
+	IPE_UNKNOWN,
+};
+
+/* Keep in sync with ipe_hook in ipe-hooks.h */
+const char *audit_hook_names[] = {
+	IPE_HOOK_EXEC,
+	IPE_HOOK_MMAP,
+	IPE_HOOK_MPROTECT,
+	IPE_HOOK_KERNEL_READ,
+	IPE_HOOK_KERNEL_LOAD,
+	IPE_UNKNOWN,
+};
+
+/**
+ * ipe_audit_mode: Emit an audit event indicating what mode IPE is currently
+ *		   in.
+ *
+ * This event is of form "IPE mode=(enforce|audit)"
+ */
+void ipe_audit_mode(bool enforcing)
+{
+	struct audit_buffer *ab;
+	const char *mode_str = (enforcing) ? IPE_MODE_ENFORCE :
+					     IPE_MODE_PERMISSIVE;
+
+	ab = audit_log_start(audit_context(), GFP_KERNEL,
+			     AUDIT_INTEGRITY_MODE);
+	if (!ab)
+		return;
+
+	audit_log_format(ab, "IPE mode=%s", mode_str);
+
+	audit_log_end(ab);
+}
+
+/**
+ * audit_engine_ctx: Add the string representation of ipe_engine_ctx to the
+ *		     end of an audit buffer.
+ * @ab: the audit buffer to append the string representation of @ctx
+ * @ctx: the ipe_engine_ctx structure to transform into a string
+ *	 representation
+ *
+ * This string representation is of form:
+ * "ctx_pid=%d ctx_op=%s ctx_hook=%s ctx_comm=%s ctx_audit_pathname=%s ctx_ino=%ld ctx_dev=%s"
+ *
+ * Certain fields may be omitted or replaced with ERR(%d).
+ *
+ */
+static void audit_engine_ctx(struct audit_buffer *ab,
+			     const struct ipe_engine_ctx *ctx)
+{
+	audit_log_format(ab, "ctx_pid=%d ctx_op=%s ctx_hook=%s ctx_comm=",
+			 task_tgid_nr(current),
+			 audit_op_names[ctx->op],
+			 audit_hook_names[ctx->hook]);
+
+	audit_log_untrustedstring(ab, current->comm);
+
+	if (ctx->file) {
+		if (IS_ERR(ctx->audit_pathname)) {
+			audit_log_format(ab, " ctx_audit_pathname=ERR(%ld) ",
+					 PTR_ERR(ctx->audit_pathname));
+		} else {
+			audit_log_format(ab, " ctx_audit_pathname=\"%s\" ",
+					 ctx->audit_pathname);
+		}
+
+		audit_log_format(ab, "ctx_ino=%ld ctx_dev=%s",
+				 ctx->file->f_inode->i_ino,
+				 ctx->file->f_inode->i_sb->s_id);
+	}
+}
+
+struct prop_audit_ctx {
+	struct audit_buffer *ab;
+	const struct ipe_engine_ctx *ctx;
+};
+
+/**
+ * audit_property: callback to print a property, used with ipe_for_each_prop.
+ * @prop: property to print an audit record for.
+ * @ctx: context passed to ipe_for_each_prop. In this case, it is of type
+ *	prop_audit_ctx, containing the audit buffer and engine ctx.
+ *
+ * Return:
+ * 0 - Always
+ */
+static int audit_property(const struct ipe_property *prop, void *ctx)
+{
+	const struct prop_audit_ctx *aud_ctx = (struct prop_audit_ctx *)ctx;
+
+	audit_log_format(aud_ctx->ab, "prop_%s=", prop->property_name);
+	prop->ctx_audit(aud_ctx->ab, aud_ctx->ctx);
+	audit_log_format(aud_ctx->ab, " ");
+
+	return 0;
+}
+
+/**
+ * audit_eval_properties: Append the string representation of evaluated
+ *			  properties to an audit buffer.
+ * @ab: the audit buffer to append the string representation of the evaluated
+ *	properties.
+ * @ctx: the ipe_engine_ctx structure to pass to property audit function.
+ *
+ * This string representation is of form:
+ * "prop_key1=value1 prop_key2=value2 ... "
+ *
+ * Certain values may be replaced with ERR(%d). Prop may also be empty,
+ * and thus omitted entirely.
+ *
+ */
+static inline void audit_eval_properties(struct audit_buffer *ab,
+					 const struct ipe_engine_ctx *ctx)
+{
+	const struct prop_audit_ctx aud_ctx = {
+		.ab = ab,
+		.ctx = ctx
+	};
+
+	(void)ipe_for_each_prop(audit_property, (void *)&aud_ctx);
+}
+
+/**
+ * audit_rule: Add the string representation of a non-default IPE rule to the
+ *	       end of an audit buffer.
+ * @ab: the audit buffer to append the string representation of a rule.
+ * @rule: the ipe_rule structure to transform into a string representation.
+ *
+ * This string representation is of form:
+ * 'rule="op=%s key1=value1 key2=value2 ... action=%s"'
+ *
+ * Certain values may be replaced with ERR(%d).
+ *
+ */
+static void audit_rule(struct audit_buffer *ab,
+		       const struct ipe_rule *rule)
+{
+	struct ipe_prop_container *ptr;
+
+	audit_log_format(ab, "rule=\"op=%s ", audit_op_names[rule->op]);
+
+	list_for_each_entry(ptr, &rule->props, next) {
+		audit_log_format(ab, "%s=", ptr->prop->property_name);
+
+		ptr->prop->rule_audit(ab, ptr->value);
+
+		audit_log_format(ab, " ");
+	}
+
+	audit_log_format(ab, "action=%s\"", ACTION_STR(rule->action));
+}
+
+/**
+ * ipe_audit_match: Emit an audit event indicating that the IPE engine has
+ *		    determined a match to a rule in IPE policy.
+ * @ctx: the engine context structure to audit
+ * @rule: The rule that was matched. If NULL, then assumed to be a default
+ *	  either operation specific, indicated by table, or global.
+ * @table: the operation-specific rule table. If NULL, then it assumed
+ *	   that the global default is matched.
+ * @match_type: The type of match that the engine used during evaluation
+ * @action: The action that the engine decided to take
+ * @rule: The rule that was matched. Must be set if @match_type is
+ *	  ipe_match_rule and NULL otherwise.
+ */
+void ipe_audit_match(const struct ipe_engine_ctx *ctx,
+		     enum ipe_match match_type, enum ipe_action action,
+		     const struct ipe_rule *rule)
+{
+	struct audit_buffer *ab;
+
+	if (!ipe_success_audit && action == ipe_action_allow)
+		return;
+
+	ab = audit_log_start(audit_context(), GFP_ATOMIC | __GFP_NOWARN,
+			     AUDIT_INTEGRITY_EVENT);
+	if (!ab)
+		return;
+
+	audit_log_format(ab, "IPE ");
+
+	audit_engine_ctx(ab, ctx);
+
+	audit_log_format(ab, " ");
+
+	audit_eval_properties(ab, ctx);
+
+	if (match_type == ipe_match_rule)
+		audit_rule(ab, rule);
+	else if (match_type == ipe_match_table)
+		audit_log_format(ab, "rule=\"DEFAULT op=%s action=%s\"",
+				 audit_op_names[ctx->op], ACTION_STR(action));
+	else if (match_type == ipe_match_global)
+		audit_log_format(ab, "rule=\"DEFAULT action=%s\"",
+				 ACTION_STR(action));
+
+	audit_log_end(ab);
+}
diff --git a/security/ipe/ipe-audit.h b/security/ipe/ipe-audit.h
new file mode 100644
index 000000000000..e00f415bed2c
--- /dev/null
+++ b/security/ipe/ipe-audit.h
@@ -0,0 +1,18 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) Microsoft Corporation. All rights reserved.
+ */
+
+#include "ipe-engine.h"
+#include "ipe-policy.h"
+
+#ifndef IPE_AUDIT_H
+#define IPE_AUDIT_H
+
+void ipe_audit_mode(bool enforcing);
+
+void ipe_audit_match(const struct ipe_engine_ctx *ctx,
+		     enum ipe_match match_type, enum ipe_action action,
+		     const struct ipe_rule *rule);
+
+#endif /* IPE_AUDIT_H */
diff --git a/security/ipe/ipe-engine.c b/security/ipe/ipe-engine.c
new file mode 100644
index 000000000000..ac526d4ea5e6
--- /dev/null
+++ b/security/ipe/ipe-engine.c
@@ -0,0 +1,205 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) Microsoft Corporation. All rights reserved.
+ */
+
+#include "ipe.h"
+#include "ipe-property.h"
+#include "ipe-prop-internal.h"
+#include "ipe-policy.h"
+#include "ipe-engine.h"
+#include "ipe-audit.h"
+
+#include <linux/types.h>
+#include <linux/fs.h>
+#include <linux/list.h>
+#include <linux/rbtree.h>
+#include <linux/rcupdate.h>
+#include <linux/security.h>
+
+const struct ipe_policy *ipe_active_policy;
+
+/**
+ * get_audit_pathname: Return the absolute path of the file struct passed in
+ * @file: file to derive an absolute path from.
+ *
+ * This function walks past chroots and mount points.
+ *
+ * Return:
+ * !NULL - OK
+ * ERR_PTR(-ENOENT) - No File
+ * ERR_PTR(-ENOMEM) - No Memory
+ * ERR_PTR(-ENAMETOOLONG) - Path Exceeds PATH_MAX
+ */
+static char *get_audit_pathname(const struct file *file)
+{
+	int rc = 0;
+	char *pos = NULL;
+	char *pathbuf = NULL;
+	char *temp_path = NULL;
+
+	/* No File to get Path From */
+	if (!file)
+		return ERR_PTR(-ENOENT);
+
+	pathbuf = __getname();
+	if (!pathbuf)
+		return ERR_PTR(-ENOMEM);
+
+	pos = d_absolute_path(&file->f_path, pathbuf, PATH_MAX);
+	if (IS_ERR(pos)) {
+		rc = PTR_ERR(pos);
+		goto err;
+	}
+
+	temp_path = __getname();
+	if (!temp_path) {
+		rc = -ENOMEM;
+		goto err;
+	}
+
+	strlcpy(temp_path, pos, PATH_MAX);
+
+	__putname(pathbuf);
+
+	return temp_path;
+
+err:
+	__putname(pathbuf);
+	return ERR_PTR(rc);
+}
+
+/**
+ * free_ctx: free a previously allocated ipe_engine_ctx struct
+ * @ctx: structure to deallocate.
+ *
+ */
+static void free_ctx(struct ipe_engine_ctx *ctx)
+{
+	if (IS_ERR_OR_NULL(ctx))
+		return;
+
+	if (!IS_ERR_OR_NULL(ctx->audit_pathname))
+		__putname(ctx->audit_pathname);
+
+	kfree(ctx);
+}
+
+/**
+ * build_ctx: allocate a new ipe_engine_ctx structure
+ * @file: File that is being evaluated against IPE policy.
+ * @op: Operation that the file is being evaluated against.
+ * @hook: Specific hook that the file is being evaluated through.
+ *
+ * Return:
+ * !NULL - OK
+ * ERR_PTR(-ENOMEM) - no memory
+ */
+static struct ipe_engine_ctx *build_ctx(const struct file *file,
+					enum ipe_op op, enum ipe_hook hook)
+{
+	struct ipe_engine_ctx *local;
+
+	local = kzalloc(sizeof(*local), GFP_KERNEL);
+	if (!local)
+		return ERR_PTR(-ENOMEM);
+
+	/* if there's an error here, it's O.K. */
+	local->audit_pathname = get_audit_pathname(file);
+	local->file = file;
+	local->op = op;
+	local->hook = hook;
+
+	return local;
+}
+
+/**
+ * evaluate: Process an @ctx against IPE's current active policy.
+ * @ctx: the engine ctx to perform an evaluation on.
+ *
+ * Return:
+ * -EACCES - A match occurred against a "action=DENY" rule
+ * -ENOMEM - Out of memory
+ */
+static int evaluate(struct ipe_engine_ctx *ctx)
+{
+	int rc = 0;
+	bool match = false;
+	enum ipe_action action;
+	enum ipe_match match_type;
+	const struct ipe_rule *rule;
+	const struct ipe_policy *pol;
+	const struct ipe_rule_table *rules;
+	const struct ipe_prop_container *prop;
+
+	if (!rcu_access_pointer(ipe_active_policy))
+		return rc;
+
+	rcu_read_lock();
+
+	pol = rcu_dereference(ipe_active_policy);
+
+	rules = &pol->ops[ctx->op];
+
+	list_for_each_entry(rule, &rules->rules, next) {
+		match = true;
+
+		list_for_each_entry(prop, &rule->props, next)
+			match = match && prop->prop->eval(ctx, prop->value);
+
+		if (match)
+			break;
+	}
+
+	if (match) {
+		match_type = ipe_match_rule;
+		action = rule->action;
+	} else if (rules->def != ipe_action_unset) {
+		match_type = ipe_match_table;
+		action = rules->def;
+		rule = NULL;
+	} else {
+		match_type = ipe_match_global;
+		action = pol->def;
+		rule = NULL;
+	}
+
+	ipe_audit_match(ctx, match_type, action, rule);
+
+	if (action == ipe_action_deny)
+		rc = -EACCES;
+
+	if (ipe_enforce == 0)
+		rc = 0;
+
+	rcu_read_unlock();
+	return rc;
+}
+
+/**
+ * ipe_process_event: Perform an evaluation of @file, @op, and @hook against
+ *		      IPE's current active policy.
+ * @file: File that is being evaluated against IPE policy.
+ * @op: Operation that the file is being evaluated against.
+ * @hook: Specific hook that the file is being evaluated through.
+ *
+ * Return:
+ * -ENOMEM: (No Memory)
+ * -EACCES: (A match occurred against a "action=DENY" rule)
+ */
+int ipe_process_event(const struct file *file, enum ipe_op op,
+		      enum ipe_hook hook)
+{
+	int rc = 0;
+	struct ipe_engine_ctx *ctx;
+
+	ctx = build_ctx(file, op, hook);
+	if (IS_ERR(ctx))
+		goto cleanup;
+
+	rc = evaluate(ctx);
+
+cleanup:
+	free_ctx(ctx);
+	return rc;
+}
diff --git a/security/ipe/ipe-engine.h b/security/ipe/ipe-engine.h
new file mode 100644
index 000000000000..d9a95674e70d
--- /dev/null
+++ b/security/ipe/ipe-engine.h
@@ -0,0 +1,37 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) Microsoft Corporation. All rights reserved.
+ */
+
+#include "ipe-hooks.h"
+
+#include <linux/types.h>
+#include <linux/rbtree.h>
+#include <linux/fs.h>
+
+#ifndef IPE_ENGINE_H
+#define IPE_ENGINE_H
+
+struct ipe_engine_ctx {
+	enum ipe_op op;
+	enum ipe_hook hook;
+	const struct file *file;
+	const char *audit_pathname;
+};
+
+struct ipe_prop_cache {
+	struct rb_node node;
+	void *storage;
+	const struct ipe_property *prop;
+};
+
+enum ipe_match {
+	ipe_match_rule = 0,
+	ipe_match_table,
+	ipe_match_global
+};
+
+int ipe_process_event(const struct file *file, enum ipe_op op,
+		      enum ipe_hook hook);
+
+#endif /* IPE_ENGINE_H */
diff --git a/security/ipe/ipe-hooks.c b/security/ipe/ipe-hooks.c
new file mode 100644
index 000000000000..071c4af23a3d
--- /dev/null
+++ b/security/ipe/ipe-hooks.c
@@ -0,0 +1,149 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) Microsoft Corporation. All rights reserved.
+ */
+
+#include "ipe.h"
+#include "ipe-hooks.h"
+#include "ipe-engine.h"
+
+#include <linux/types.h>
+#include <linux/fs.h>
+#include <linux/binfmts.h>
+#include <linux/mount.h>
+#include <linux/mman.h>
+#include <linux/mm.h>
+#include <linux/security.h>
+
+/**
+ * ipe_on_exec: LSM hook called on the exec family of system calls.
+ * @bprm: A structure to hold arguments that are used when loading binaries,
+ *	  used to extract the file being executed.
+ *
+ * Return:
+ * 0 - OK
+ * !0 - see ipe_process_event
+ */
+int ipe_on_exec(struct linux_binprm *bprm)
+{
+	return ipe_process_event(bprm->file, ipe_op_execute, ipe_hook_exec);
+}
+
+/**
+ * ipe_on_mmap: LSM hook called on the mmap system call.
+ * @file: File being mapped into memory.
+ * @reqprot: Unused.
+ * @prot: A protection mapping of the memory region, calculated based on
+ *	  @reqprot, and the system configuration.
+ * @flags: Unused.
+ *
+ * Return:
+ * 0 - OK
+ * !0 - see ipe_process_event
+ */
+int ipe_on_mmap(struct file *file, unsigned long reqprot, unsigned long prot,
+		unsigned long flags)
+{
+	if (prot & PROT_EXEC)
+		return ipe_process_event(file, ipe_op_execute, ipe_hook_mmap);
+
+	return 0;
+}
+
+/**
+ * ipe_on_mprotect: LSM hook called on the mprotect system call
+ * @vma: A structure representing the existing memory region.
+ * @reqprot: Unused.
+ * @prot: A protection mapping of the memory region, calculated based on
+ *	  @reqprot, and the system configuration.
+ *
+ * Return:
+ * 0 - OK
+ * !0 - see ipe_process_event
+ */
+int ipe_on_mprotect(struct vm_area_struct *vma, unsigned long reqprot,
+		    unsigned long prot)
+{
+	if ((prot & PROT_EXEC) && !(vma->vm_flags & VM_EXEC))
+		return ipe_process_event(vma->vm_file, ipe_op_execute,
+					 ipe_hook_mprotect);
+
+	return 0;
+}
+
+/**
+ * ipe_on_kernel_read: LSM hook called on kernel_read_file.
+ * @file: File being read by the hook kernel_read_file.
+ * @id: Enumeration indicating the type of file being read.
+ *
+ * For more information, see the LSM hook, kernel_read_file.
+ *
+ * Return:
+ * 0 - OK
+ * !0 - see ipe_process_event
+ */
+int ipe_on_kernel_read(struct file *file, enum kernel_read_file_id id)
+{
+	switch (id) {
+	case READING_FIRMWARE:
+	case READING_FIRMWARE_PREALLOC_BUFFER:
+		return ipe_process_event(file, ipe_op_firmware,
+					 ipe_hook_kernel_read);
+	case READING_MODULE:
+		return ipe_process_event(file, ipe_op_kmodule,
+					 ipe_hook_kernel_read);
+	case READING_KEXEC_INITRAMFS:
+		return ipe_process_event(file, ipe_op_kexec_initramfs,
+					 ipe_hook_kernel_read);
+	case READING_KEXEC_IMAGE:
+		return ipe_process_event(file, ipe_op_kexec_image,
+					 ipe_hook_kernel_read);
+	case READING_POLICY:
+		return ipe_process_event(file, ipe_op_policy,
+					 ipe_hook_kernel_read);
+	case READING_X509_CERTIFICATE:
+		return ipe_process_event(file, ipe_op_x509,
+					 ipe_hook_kernel_read);
+	default:
+		return ipe_process_event(file, ipe_op_kernel_read,
+					 ipe_hook_kernel_read);
+	}
+}
+
+/**
+ * ipe_on_kernel_load_data: LSM hook called on kernel_load_data.
+ * @id: Enumeration indicating what type of data is being loaded.
+ *
+ * For more information, see the LSM hook, kernel_load_data.
+ *
+ * Return:
+ * 0 - OK
+ * !0 - see ipe_process_event
+ */
+int ipe_on_kernel_load_data(enum kernel_load_data_id id)
+{
+	switch (id) {
+	case LOADING_FIRMWARE:
+	case LOADING_FIRMWARE_PREALLOC_BUFFER:
+		return ipe_process_event(NULL, ipe_op_firmware,
+					 ipe_hook_kernel_load);
+	case LOADING_MODULE:
+		return ipe_process_event(NULL, ipe_op_kmodule,
+					 ipe_hook_kernel_load);
+	case LOADING_KEXEC_INITRAMFS:
+		return ipe_process_event(NULL, ipe_op_kexec_initramfs,
+					 ipe_hook_kernel_load);
+	case LOADING_KEXEC_IMAGE:
+		return ipe_process_event(NULL, ipe_op_kexec_image,
+					 ipe_hook_kernel_load);
+	case LOADING_POLICY:
+		return ipe_process_event(NULL, ipe_op_policy,
+					 ipe_hook_kernel_load);
+	case LOADING_X509_CERTIFICATE:
+		return ipe_process_event(NULL, ipe_op_x509,
+					 ipe_hook_kernel_load);
+	default:
+		return ipe_process_event(NULL, ipe_op_kernel_read,
+					 ipe_hook_kernel_load);
+	}
+}
diff --git a/security/ipe/ipe-hooks.h b/security/ipe/ipe-hooks.h
new file mode 100644
index 000000000000..806659b7cdbe
--- /dev/null
+++ b/security/ipe/ipe-hooks.h
@@ -0,0 +1,61 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) Microsoft Corporation. All rights reserved.
+ */
+
+#include <linux/types.h>
+#include <linux/fs.h>
+#include <linux/mount.h>
+#include <linux/binfmts.h>
+#include <linux/mman.h>
+#include <linux/mm.h>
+#include <linux/security.h>
+
+#ifndef IPE_HOOK_H
+#define IPE_HOOK_H
+
+#define IPE_HOOK_EXEC		"EXEC"
+#define IPE_HOOK_MMAP		"MMAP"
+#define IPE_HOOK_MPROTECT	"MPROTECT"
+#define IPE_HOOK_KERNEL_READ	"KERNEL_READ"
+#define IPE_HOOK_KERNEL_LOAD	"KERNEL_LOAD"
+
+enum ipe_hook {
+	ipe_hook_exec = 0,
+	ipe_hook_mmap,
+	ipe_hook_mprotect,
+	ipe_hook_kernel_read,
+	ipe_hook_kernel_load,
+	ipe_hook_max
+};
+
+/*
+ * The sequence between ipe_op_firmware and ipe_op_kmodule
+ * must remain the same for ipe_op_kernel read to function
+ * appropriately.
+ */
+enum ipe_op {
+	ipe_op_execute = 0,
+	ipe_op_firmware,
+	ipe_op_kexec_image,
+	ipe_op_kexec_initramfs,
+	ipe_op_x509,
+	ipe_op_policy,
+	ipe_op_kmodule,
+	ipe_op_kernel_read,
+	ipe_op_max
+};
+
+int ipe_on_exec(struct linux_binprm *bprm);
+
+int ipe_on_mmap(struct file *file, unsigned long reqprot, unsigned long prot,
+		unsigned long flags);
+
+int ipe_on_mprotect(struct vm_area_struct *vma, unsigned long reqprot,
+		    unsigned long prot);
+
+int ipe_on_kernel_read(struct file *file, enum kernel_read_file_id id);
+
+int ipe_on_kernel_load_data(enum kernel_load_data_id id);
+
+#endif /* IPE_HOOK_H */
diff --git a/security/ipe/ipe-policy.h b/security/ipe/ipe-policy.h
new file mode 100644
index 000000000000..c0c9f2962c92
--- /dev/null
+++ b/security/ipe/ipe-policy.h
@@ -0,0 +1,62 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) Microsoft Corporation. All rights reserved.
+ */
+
+#include "ipe-hooks.h"
+#include "ipe-property.h"
+
+#include <linux/types.h>
+#include <linux/list.h>
+#include <linux/mutex.h>
+#include <linux/rcupdate.h>
+
+#ifndef IPE_POLICY_H
+#define IPE_POLICY_H
+
+#define IPE_HEADER_POLICY_NAME		"policy_name"
+#define IPE_HEADER_POLICY_VERSION	"policy_version"
+
+extern const char *const ipe_boot_policy;
+extern const struct ipe_policy *ipe_active_policy;
+
+enum ipe_action {
+	ipe_action_unset = 0,
+	ipe_action_allow,
+	ipe_action_deny
+};
+
+struct ipe_prop_container {
+	struct list_head next;
+	void *value;
+	const struct ipe_property *prop;
+};
+
+struct ipe_rule {
+	struct list_head props;
+	struct list_head next;
+	enum ipe_action action;
+	enum ipe_op op;
+};
+
+struct ipe_rule_table {
+	struct list_head rules;
+	enum ipe_action def;
+};
+
+struct ipe_pol_ver {
+	u16 major;
+	u16 minor;
+	u16 rev;
+};
+
+struct ipe_policy {
+	char *policy_name;
+	struct ipe_pol_ver policy_version;
+	enum ipe_action def;
+
+	/* KERNEL_READ stores no data itself */
+	struct ipe_rule_table ops[ipe_op_max - 1];
+};
+
+#endif /* IPE_POLICY_H */
diff --git a/security/ipe/ipe-prop-internal.h b/security/ipe/ipe-prop-internal.h
new file mode 100644
index 000000000000..95a2081e77ee
--- /dev/null
+++ b/security/ipe/ipe-prop-internal.h
@@ -0,0 +1,37 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) Microsoft Corporation. All rights reserved.
+ */
+
+#include "ipe-property.h"
+
+#include <linux/types.h>
+
+#ifndef IPE_PROPERTY_INTERNAL_H
+#define IPE_PROPERTY_INTERNAL_H
+
+#define IPE_PROPERTY_OPERATION	"op"
+#define IPE_PROPERTY_DEFAULT	"DEFAULT"
+#define IPE_PROPERTY_ACTION	"action"
+
+#define IPE_OP_EXECUTE		"EXECUTE"
+#define IPE_OP_FIRMWARE		"FIRMWARE"
+#define IPE_OP_KEXEC_IMAGE	"KEXEC_IMAGE"
+#define IPE_OP_KEXEC_INITRAMFS	"KEXEC_INITRAMFS"
+#define IPE_OP_X509_CERTIFICATE	"X509_CERT"
+#define IPE_OP_POLICY		"POLICY"
+#define IPE_OP_KMODULE		"KMODULE"
+#define IPE_OP_KERNEL_READ	"KERNEL_READ"
+
+struct ipe_prop_reg {
+	struct rb_node node;
+	const struct ipe_property *prop;
+};
+
+int ipe_for_each_prop(int (*view)(const struct ipe_property *prop,
+				  void *ctx),
+		      void *ctx);
+
+const struct ipe_property *ipe_lookup_prop(const char *key);
+
+#endif /* IPE_PROPERTY_INTERNAL_H */
diff --git a/security/ipe/ipe-property.c b/security/ipe/ipe-property.c
new file mode 100644
index 000000000000..d4b0283f86bd
--- /dev/null
+++ b/security/ipe/ipe-property.c
@@ -0,0 +1,142 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) Microsoft Corporation. All rights reserved.
+ */
+
+#include "ipe.h"
+#include "ipe-prop-internal.h"
+#include "ipe-property.h"
+
+#include <linux/types.h>
+#include <linux/rbtree.h>
+#include <linux/slab.h>
+
+/* global root containing all registered properties */
+struct rb_root ipe_registry_root = RB_ROOT;
+
+/**
+ * reg_lookup: Attempt to find a `prop_reg` structure with property_name @key.
+ * @key: The property_name to look for in the tree.
+ *
+ * Return:
+ * ipe_prop_reg structure - OK
+ * NULL - No such property exists
+ */
+static struct ipe_prop_reg *reg_lookup(const char *key)
+{
+	struct rb_node *n = ipe_registry_root.rb_node;
+
+	while (n) {
+		int r;
+		struct ipe_prop_reg *reg =
+			container_of(n, struct ipe_prop_reg, node);
+
+		r = strcmp(reg->prop->property_name, key);
+		if (r == 0)
+			return reg;
+		else if (r > 0)
+			n = n->rb_right;
+		else
+			n = n->rb_left;
+	}
+
+	return NULL;
+}
+
+/**
+ * ipe_lookup_prop: Attempt to find a ipe_property structure by name @key.
+ * @key: The property_name to look for in the tree.
+ *
+ * Return:
+ * ipe_property structure - OK
+ * NULL - No property exists under @key
+ */
+const struct ipe_property *ipe_lookup_prop(const char *key)
+{
+	struct ipe_prop_reg *reg = reg_lookup(key);
+
+	if (!reg)
+		return NULL;
+
+	return reg->prop;
+}
+
+/**
+ * ipe_register_property: Insert a property into the registration system.
+ * @prop: Read-only property structure containing the property_name, as well
+ *	  as the necessary function pointers for a property.
+ *
+ * The caller needs to maintain the lifetime of @prop throughout the life of
+ * the system, after calling ipe_register_property.
+ *
+ * All necessary properties need to be loaded via this method before
+ * loading a policy, otherwise the properties will be ignored as unknown.
+ *
+ * Return:
+ * 0 - OK
+ * -EEXIST - A key exists with the name @prop->property_name
+ * -ENOMEM - Out of Memory
+ */
+int ipe_register_property(const struct ipe_property *prop)
+{
+	struct rb_node *parent = NULL;
+	struct ipe_prop_reg *new_data = NULL;
+	struct rb_node **new = &ipe_registry_root.rb_node;
+
+	while (*new) {
+		int r;
+		struct ipe_prop_reg *reg =
+			container_of(*new, struct ipe_prop_reg, node);
+
+		parent = *new;
+
+		r = strcmp(reg->prop->property_name, prop->property_name);
+		if (r == 0)
+			return -EEXIST;
+		else if (r > 0)
+			new = &((*new)->rb_right);
+		else
+			new = &((*new)->rb_left);
+	}
+
+	new_data = kzalloc(sizeof(*new_data), GFP_KERNEL);
+	if (!new_data)
+		return -ENOMEM;
+
+	new_data->prop = prop;
+
+	rb_link_node(&new_data->node, parent, new);
+	rb_insert_color(&new_data->node, &ipe_registry_root);
+
+	return 0;
+}
+
+/**
+ * ipe_for_each_prop: Iterate over all currently-registered properties
+ *	calling @fn on the values, and providing @view @ctx.
+ * @view: The function to call for each property. This is given the property
+ *	structure as the first argument, and @ctx as the second.
+ * @ctx: caller-specified context that is passed to the function. Can be NULL.
+ *
+ * Return:
+ * 0 - OK
+ * !0 - Proper errno as returned by @view.
+ */
+int ipe_for_each_prop(int (*view)(const struct ipe_property *prop,
+				  void *ctx),
+		      void *ctx)
+{
+	struct rb_node *node;
+	struct ipe_prop_reg *val;
+	int rc = 0;
+
+	for (node = rb_first(&ipe_registry_root); node; node = rb_next(node)) {
+		val = container_of(node, struct ipe_prop_reg, node);
+
+		rc = view(val->prop, ctx);
+		if (rc)
+			return rc;
+	}
+
+	return rc;
+}
diff --git a/security/ipe/ipe-property.h b/security/ipe/ipe-property.h
new file mode 100644
index 000000000000..cf570d52d0d2
--- /dev/null
+++ b/security/ipe/ipe-property.h
@@ -0,0 +1,99 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) Microsoft Corporation. All rights reserved.
+ */
+
+#include "ipe-engine.h"
+
+#include <linux/types.h>
+#include <linux/lsm_audit.h>
+
+#ifndef IPE_PROPERTY_H
+#define IPE_PROPERTY_H
+
+/**
+ * ipe_property_evaluator: Determines whether a file subject matches the
+ *			   property.
+ * @value: Value to compare against for a match
+ *
+ * NOTE: This is done in an rcu read critical section - sleeping
+ *	 allocations are prohibited.
+ *
+ * Return:
+ * true - The property matches evaluation
+ * false - The property does not match evaluation
+ */
+typedef bool (*ipe_property_evaluator)(const struct ipe_engine_ctx *ctx,
+				       const void *value);
+
+/**
+ * ipe_property_audit: Transform a rule value into a string representation.
+ * @ab: Audit buffer to add the string representation of @value to.
+ * @value: Value to transform into a string representation.
+ *
+ * NOTE: This is done in an rcu read critical section - sleeping
+ *	 allocations are prohibited.
+ */
+typedef void (*ipe_property_audit)(struct audit_buffer *ab, const void *value);
+
+/**
+ * ipe_ctx_audit: Called by the auditing to provide the values
+ *	that were evaluated about the subject, @ctx->file, to determine how
+ *	a value was evaluated.
+ *
+ * NOTE: This is done in an rcu read critical section - sleeping
+ *	 allocations are prohibited.
+ *
+ * @ab: Audit buffer to add the string representation of @value to.
+ * @value: Value to transform into a string representation.
+ *
+ */
+typedef void (*ipe_ctx_audit)(struct audit_buffer *ab,
+			     const struct ipe_engine_ctx *ctx);
+
+/**
+ * ipe_parse_value: Transform a string representation of a rule into an
+ *		    internal ipe data-structure, opaque to the engine.
+ * @val_str: String-value parsed by the policy parser.
+ * @value: Valid-pointer indicating address to store parsed value.
+ *
+ * Return:
+ * 0 - OK
+ * !0 - ERR, use Standard Return Codes
+ */
+typedef int(*ipe_parse_value)(const char *val_str, void **value);
+
+/**
+ * ipe_dup_val: Called by the policy parser to make duplicate properties for
+ *		pseudo-properties like "KERNEL_READ".
+ * @src:  Value to copy.
+ * @dest: Pointer to the destination where the value should be copied.
+ *
+ * Return:
+ * 0 - OK
+ * !0 - ERR, use Standard Return Codes
+ */
+typedef int (*ipe_dup_val)(const void *src, void **dest);
+
+/**
+ * ipe_free_value: Free a policy value, created by ipe_parse_value.
+ * @value: Valid-pointer to the value to be interpreted and
+ *	   freed by the property.
+ *
+ * Optional, can be NULL - in which case, this will not be called.
+ */
+typedef void (*ipe_free_value)(void **value);
+
+struct ipe_property {
+	const char			*const property_name;
+	ipe_property_evaluator		eval;
+	ipe_property_audit		rule_audit;
+	ipe_ctx_audit			ctx_audit;
+	ipe_parse_value			parse;
+	ipe_dup_val			dup;
+	ipe_free_value			free_val;
+};
+
+int ipe_register_property(const struct ipe_property *prop);
+
+#endif /* IPE_PROPERTY_H */
diff --git a/security/ipe/ipe.c b/security/ipe/ipe.c
new file mode 100644
index 000000000000..6e3b9a10813c
--- /dev/null
+++ b/security/ipe/ipe.c
@@ -0,0 +1,67 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) Microsoft Corporation. All rights reserved.
+ */
+
+#include "ipe.h"
+#include "ipe-policy.h"
+#include "ipe-hooks.h"
+
+#include <linux/module.h>
+#include <linux/lsm_hooks.h>
+#include <linux/sysctl.h>
+#include <linux/rcupdate.h>
+#include <linux/fs.h>
+#include <linux/kernel.h>
+#include <linux/security.h>
+
+static struct security_hook_list ipe_hooks[] __lsm_ro_after_init = {
+	LSM_HOOK_INIT(bprm_check_security, ipe_on_exec),
+	LSM_HOOK_INIT(mmap_file, ipe_on_mmap),
+	LSM_HOOK_INIT(kernel_read_file, ipe_on_kernel_read),
+	LSM_HOOK_INIT(kernel_load_data, ipe_on_kernel_load_data),
+	LSM_HOOK_INIT(file_mprotect, ipe_on_mprotect),
+};
+
+/**
+ * ipe_init: Entry point of IPE.
+ *
+ * This is called at LSM init, which happens occurs early during kernel
+ * start up. During this phase, IPE loads the
+ * properties compiled into the kernel, and register's IPE's hooks.
+ * The boot policy is loaded later, during securityfs init, at which point
+ * IPE will start enforcing its policy.
+ *
+ * Return:
+ * 0 - OK
+ * -ENOMEM - sysctl registration failed.
+ */
+static int __init ipe_init(void)
+{
+	pr_info("mode=%s", (ipe_enforce == 1) ? IPE_MODE_ENFORCE :
+						IPE_MODE_PERMISSIVE);
+
+	security_add_hooks(ipe_hooks, ARRAY_SIZE(ipe_hooks), "IPE");
+
+	return 0;
+}
+
+DEFINE_LSM(ipe) = {
+	.name = "ipe",
+	.init = ipe_init,
+};
+
+bool ipe_enforce = true;
+bool ipe_success_audit;
+
+#ifdef CONFIG_SECURITY_IPE_PERMISSIVE_SWITCH
+
+/* Module Parameter for Default Behavior on Boot */
+module_param_named(enforce, ipe_enforce, bool, 0644);
+MODULE_PARM_DESC(enforce, "IPE Permissive Switch");
+
+#endif /* CONFIG_SECURITY_IPE_PERMISSIVE_SWITCH */
+
+/* Module Parameter for Success Audit on Boot */
+module_param_named(success_audit, ipe_success_audit, bool, 0644);
+MODULE_PARM_DESC(success_audit, "IPE Audit on Success");
diff --git a/security/ipe/ipe.h b/security/ipe/ipe.h
new file mode 100644
index 000000000000..af72bb574f73
--- /dev/null
+++ b/security/ipe/ipe.h
@@ -0,0 +1,20 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) Microsoft Corporation. All rights reserved.
+ */
+
+#ifndef IPE_H
+#define IPE_H
+
+#define pr_fmt(fmt) "IPE " fmt "\n"
+
+#include <linux/types.h>
+#include <linux/fs.h>
+
+#define IPE_MODE_ENFORCE	"enforce"
+#define IPE_MODE_PERMISSIVE	"permissive"
+
+extern bool ipe_enforce;
+extern bool ipe_success_audit;
+
+#endif /* IPE_H */
-- 
2.27.0
^ permalink raw reply related	[flat|nested] 12+ messages in thread
- * [RFC PATCH v6 03/11] security: add ipe lsm policy parser and policy loading
  2020-07-30  0:31 [RFC PATCH v6 00/11] Integrity Policy Enforcement LSM (IPE) Deven Bowers
  2020-07-30  0:31 ` [RFC PATCH v6 01/11] scripts: add ipe tooling to generate boot policy Deven Bowers
  2020-07-30  0:31 ` [RFC PATCH v6 02/11] security: add ipe lsm evaluation loop and audit system Deven Bowers
@ 2020-07-30  0:31 ` Deven Bowers
  2020-07-30  0:31 ` [RFC PATCH v6 04/11] ipe: add property for trust of boot volume Deven Bowers
                   ` (7 subsequent siblings)
  10 siblings, 0 replies; 12+ messages in thread
From: Deven Bowers @ 2020-07-30  0:31 UTC (permalink / raw)
  To: agk, axboe, snitzer, jmorris, serge, zohar, viro, paul, eparis,
	jannh, dm-devel, linux-integrity, linux-security-module,
	linux-fsdevel, linux-block, linux-audit
  Cc: tyhicks, linux-kernel, corbet, sashal, jaskarankhurana, mdsakib,
	nramas, pasha.tatashin
Adds the policy parser and the policy loading to IPE, along with the
related securityfs entries and audit events.
Signed-off-by: Deven Bowers <deven.desai@linux.microsoft.com>
---
 security/ipe/Kconfig             |    2 +
 security/ipe/Makefile            |    3 +
 security/ipe/ipe-audit.c         |   74 +-
 security/ipe/ipe-audit.h         |    6 +
 security/ipe/ipe-parse.c         |  889 ++++++++++++++++++++
 security/ipe/ipe-parse.h         |   17 +
 security/ipe/ipe-policy.c        |  149 ++++
 security/ipe/ipe-policy.h        |   13 +-
 security/ipe/ipe-prop-internal.h |   18 +-
 security/ipe/ipe-property.c      |    9 +-
 security/ipe/ipe-property.h      |    1 +
 security/ipe/ipe-secfs.c         | 1309 ++++++++++++++++++++++++++++++
 security/ipe/ipe-secfs.h         |   14 +
 13 files changed, 2493 insertions(+), 11 deletions(-)
 create mode 100644 security/ipe/ipe-parse.c
 create mode 100644 security/ipe/ipe-parse.h
 create mode 100644 security/ipe/ipe-policy.c
 create mode 100644 security/ipe/ipe-secfs.c
 create mode 100644 security/ipe/ipe-secfs.h
diff --git a/security/ipe/Kconfig b/security/ipe/Kconfig
index 7615109a19ca..665524fc3ca4 100644
--- a/security/ipe/Kconfig
+++ b/security/ipe/Kconfig
@@ -7,6 +7,8 @@ menuconfig SECURITY_IPE
 	bool "Integrity Policy Enforcement (IPE)"
 	depends on SECURITY && AUDIT
 	select SYSTEM_DATA_VERIFICATION
+	select SECURITYFS
+	select CRYPTO_SHA1
 	help
 	  This option enables the Integrity Policy Enforcement subsystem
 	  allowing systems to enforce integrity having no dependencies
diff --git a/security/ipe/Makefile b/security/ipe/Makefile
index 40565b73fac2..7d6da33dd0c4 100644
--- a/security/ipe/Makefile
+++ b/security/ipe/Makefile
@@ -19,7 +19,10 @@ obj-$(CONFIG_SECURITY_IPE) += \
 	ipe-audit.o \
 	ipe-bp.o \
 	ipe-engine.o \
+	ipe-parse.o \
+	ipe-policy.o \
 	ipe-property.o \
 	ipe-hooks.o \
+	ipe-secfs.o \
 
 clean-files := ipe-bp.c
diff --git a/security/ipe/ipe-audit.c b/security/ipe/ipe-audit.c
index 2c754851bd40..0e731025f2f5 100644
--- a/security/ipe/ipe-audit.c
+++ b/security/ipe/ipe-audit.c
@@ -17,7 +17,8 @@
 #include <crypto/sha1_base.h>
 
 #define ACTION_STR(a) ((a) == ipe_action_allow ? "ALLOW" : "DENY")
-
+#define POLICY_LOAD_FSTR	"IPE policy_name=\"%s\" policy_version=%hu.%hu.%hu sha1="
+#define POLICY_ACTIVATE_STR	"IPE policy_name=\"%s\" policy_version=%hu.%hu.%hu"
 #define IPE_UNKNOWN		"UNKNOWN"
 
 /* Keep in sync with ipe_op in ipe-hooks.h */
@@ -229,3 +230,74 @@ void ipe_audit_match(const struct ipe_engine_ctx *ctx,
 
 	audit_log_end(ab);
 }
+
+/**
+ * ipe_audit_policy_load: Emit an audit event that an IPE policy has been
+ *			  loaded, with the name of the policy, the policy
+ *			  version triple, and a flat hash of the content.
+ * @pol: The parsed policy to derive the policy_name and policy_version
+ *	 triple.
+ * @raw: The raw content that was passed to the ipe.policy sysctl to derive
+ *	 the sha1 hash.
+ * @raw_size: the length of @raw.
+ * @tfm: shash structure allocated by the caller, used to fingerprint the
+ *	 policy being deployed
+ */
+void ipe_audit_policy_load(const struct ipe_policy *pol, const uint8_t *raw,
+			   size_t raw_size, struct crypto_shash *tfm)
+{
+	int rc = 0;
+	struct audit_buffer *ab;
+	u8 digest[SHA1_DIGEST_SIZE];
+	SHASH_DESC_ON_STACK(desc, tfm);
+
+	ab = audit_log_start(audit_context(), GFP_KERNEL,
+			     AUDIT_INTEGRITY_POLICY_LOAD);
+	if (!ab)
+		return;
+
+	audit_log_format(ab, POLICY_LOAD_FSTR, pol->policy_name,
+			 pol->policy_version.major, pol->policy_version.minor,
+			 pol->policy_version.rev);
+
+	desc->tfm = tfm;
+
+	if (crypto_shash_init(desc) != 0)
+		goto err;
+
+	if (crypto_shash_update(desc, raw, raw_size) != 0)
+		goto err;
+
+	if (crypto_shash_final(desc, digest) != 0)
+		goto err;
+
+	audit_log_n_hex(ab, digest, crypto_shash_digestsize(tfm));
+
+err:
+	if (rc != 0)
+		audit_log_format(ab, "ERR(%d)", rc);
+
+	audit_log_end(ab);
+}
+
+/**
+ * ipe_audit_policy_activation: Emit an audit event that a specific policy
+ *				was activated as the active policy.
+ * @pol: policy that is being activated
+ */
+void ipe_audit_policy_activation(const struct ipe_policy *pol)
+{
+	struct audit_buffer *ab;
+
+	ab = audit_log_start(audit_context(), GFP_KERNEL,
+			     AUDIT_INTEGRITY_POLICY_ACTIVATE);
+
+	if (!ab)
+		return;
+
+	audit_log_format(ab, POLICY_ACTIVATE_STR, pol->policy_name,
+			 pol->policy_version.major, pol->policy_version.minor,
+			 pol->policy_version.rev);
+
+	audit_log_end(ab);
+}
diff --git a/security/ipe/ipe-audit.h b/security/ipe/ipe-audit.h
index e00f415bed2c..350818d5c47f 100644
--- a/security/ipe/ipe-audit.h
+++ b/security/ipe/ipe-audit.h
@@ -3,6 +3,7 @@
  * Copyright (C) Microsoft Corporation. All rights reserved.
  */
 
+#include "ipe-prop-internal.h"
 #include "ipe-engine.h"
 #include "ipe-policy.h"
 
@@ -15,4 +16,9 @@ void ipe_audit_match(const struct ipe_engine_ctx *ctx,
 		     enum ipe_match match_type, enum ipe_action action,
 		     const struct ipe_rule *rule);
 
+void ipe_audit_policy_load(const struct ipe_policy *pol, const uint8_t *raw,
+			   size_t raw_size, struct crypto_shash *tfm);
+
+void ipe_audit_policy_activation(const struct ipe_policy *pol);
+
 #endif /* IPE_AUDIT_H */
diff --git a/security/ipe/ipe-parse.c b/security/ipe/ipe-parse.c
new file mode 100644
index 000000000000..edc3f52617c5
--- /dev/null
+++ b/security/ipe/ipe-parse.c
@@ -0,0 +1,889 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) Microsoft Corporation. All rights reserved.
+ */
+
+#include "ipe.h"
+#include "ipe-prop-internal.h"
+#include "ipe-hooks.h"
+#include "ipe-parse.h"
+#include "ipe-property.h"
+#include "ipe-audit.h"
+
+#include <linux/types.h>
+#include <linux/list.h>
+#include <linux/list_sort.h>
+#include <linux/slab.h>
+#include <linux/ctype.h>
+#include <linux/parser.h>
+#include <linux/errno.h>
+#include <linux/err.h>
+
+#define ALLOW_ACTION	"ALLOW"
+#define DENY_ACTION	"DENY"
+#define COMMENT_CHAR	'#'
+#define VER_FSTR	"%hu.%hu.%hu"
+
+/* Internal Type Definitions */
+enum property_priority {
+	other = 0,
+	action = 1,
+	op = 2,
+	default_action = 3,
+	policy_ver = 4,
+	policy_name = 5,
+};
+
+struct token {
+	struct list_head	next_tok;
+	const char		*key;
+	enum property_priority	key_priority;
+	const char		*val;
+};
+
+/* Utility Functions */
+static inline bool is_quote(char c)
+{
+	return c == '"' || c == '\'';
+}
+
+static inline bool valid_token(char *s)
+{
+	return !s || !strpbrk(s, "\"\'");
+}
+
+static inline bool is_default(const struct token *t)
+{
+	return !t->val &&  t->key_priority == default_action;
+}
+
+static inline bool is_operation(const struct token *t)
+{
+	return t->val && t->key_priority == op;
+}
+
+static inline bool is_action(const struct token *t)
+{
+	return t->val && t->key_priority == action;
+}
+
+static inline bool is_name(const struct token *t)
+{
+	return t->val && t->key_priority == policy_name;
+}
+
+static inline bool is_ver(const struct token *t)
+{
+	return t->val && t->key_priority == policy_ver;
+}
+
+static int cmp_pri(void *priv, struct list_head *a, struct list_head *b)
+{
+	struct token *t_a = container_of(a, struct token, next_tok);
+	struct token *t_b = container_of(b, struct token, next_tok);
+
+	return t_b->key_priority - t_a->key_priority;
+}
+
+static char *trim_quotes(char *str)
+{
+	char s;
+	size_t len;
+
+	if (!str)
+		return str;
+
+	s = *str;
+
+	if (is_quote(s)) {
+		len = strlen(str) - 1;
+
+		if (str[len] != s)
+			return NULL;
+
+		str[len] = '\0';
+		++str;
+	}
+
+	return str;
+}
+
+/**
+ * ipe_set_action: Set an action with error checking.
+ * @src: Valid pointer to the source location to set wih the result
+ * @set: Value to apply to @src, if valid
+ *
+ * Return:
+ * 0 - OK
+ * -EBADMSG - Attempting to set something that is already set
+ */
+static int ipe_set_action(enum ipe_action *src, enum ipe_action set)
+{
+	if (*src != ipe_action_unset)
+		return -EBADMSG;
+
+	*src = set;
+
+	return 0;
+}
+
+/**
+ * ipe_insert_token: Allocate and append the key=value pair indicated by @val,
+ *		     to the list represented by @head.
+ * @val: Token to parse, of form "key=val".
+ * @head: Head of the list to insert the token structure into.
+ *
+ * If "=val" is omitted, this function will succeed, and the value set will be
+ * NULL.
+ *
+ * Return:
+ * 0 - OK
+ * -EBADMSG - Invalid policy syntax
+ * -ENOMEM - No Memory
+ */
+static int ipe_insert_token(char *val, struct list_head *head)
+{
+	char *key;
+	substring_t match[MAX_OPT_ARGS];
+	struct token *tok;
+	const match_table_t prop_priorities = {
+		{ policy_name,		IPE_HEADER_POLICY_NAME },
+		{ policy_ver,		IPE_HEADER_POLICY_VERSION},
+		{ op,			IPE_PROPERTY_OPERATION },
+		{ default_action,	IPE_PROPERTY_DEFAULT },
+		{ action,		IPE_PROPERTY_ACTION },
+		{ other, NULL },
+	};
+
+	key = strsep(&val, "=");
+	if (!key)
+		return -EBADMSG;
+
+	tok = kzalloc(sizeof(*tok), GFP_KERNEL);
+	if (!tok)
+		return -ENOMEM;
+
+	tok->key = key;
+	tok->val = trim_quotes(val);
+
+	/* remap empty string */
+	if (tok->val && !strlen(tok->val))
+		tok->val = NULL;
+
+	tok->key_priority = match_token(key, prop_priorities, match);
+	INIT_LIST_HEAD(&tok->next_tok);
+
+	list_add_tail(&tok->next_tok, head);
+
+	return 0;
+}
+
+/**
+ * ipe_tokenize_line: Parse a line of text into a list of token structures.
+ * @line: Line to parse.
+ * @list: Head of the list to insert the token structure into.
+ *
+ * The final result will be sorted in the priority order definted by
+ * enum property_priorities to enforce policy structure.
+ *
+ * Return:
+ * 0 - OK
+ * -EBADMSG - Invalid policy syntax
+ * -ENOMEM - No Memory
+ * -ENOENT - No tokens were parsed
+ */
+static int ipe_tokenize_line(char *line, struct list_head *list)
+{
+	int rc = 0;
+	size_t i = 0;
+	size_t len = 0;
+	char *tok = NULL;
+	char quote = '\0';
+
+	len = strlen(line);
+
+	for (i = 0; i < len; ++i) {
+		if (quote == '\0' && is_quote(line[i])) {
+			quote = line[i];
+			continue;
+		}
+
+		if (quote != '\0' && line[i] == quote) {
+			quote = '\0';
+			continue;
+		}
+
+		if (quote == '\0' && line[i] == COMMENT_CHAR) {
+			tok = NULL;
+			break;
+		}
+
+		if (isgraph(line[i]) && !tok)
+			tok = &line[i];
+
+		if (quote == '\0' && isspace(line[i])) {
+			line[i] = '\0';
+
+			if (!tok)
+				continue;
+
+			rc = ipe_insert_token(tok, list);
+			if (rc != 0)
+				return rc;
+
+			tok = NULL;
+		}
+	}
+
+	if (quote != '\0')
+		return -EBADMSG;
+
+	if (tok)
+		ipe_insert_token(tok, list);
+
+	if (list_empty(list))
+		return -ENOENT;
+
+	list_sort(NULL, list, cmp_pri);
+
+	return 0;
+}
+
+static inline int ipe_parse_version(const char *val, struct ipe_pol_ver *ver)
+{
+	if (sscanf(val, VER_FSTR, &ver->major, &ver->minor, &ver->rev) != 3)
+		return -EBADMSG;
+
+	return 0;
+}
+
+/**
+ * ipe_parse_action: Given a token, parse the value as if it were an 'action'
+ *		     token.
+ * @action: Token to parse to determine the action.
+ *
+ * Action tokens are of the form: action=(ALLOW|DENY) for more information
+ * about IPE policy, please see the documentation.
+ *
+ * Return:
+ * ipe_action_allow - OK
+ * ipe_action_deny - OK
+ * ipe_action_unset - ERR
+ */
+static enum ipe_action ipe_parse_action(struct token *action)
+{
+	if (!action->val)
+		return ipe_action_unset;
+	else if (!strcmp(action->val, ALLOW_ACTION))
+		return ipe_action_allow;
+	else if (!strcmp(action->val, DENY_ACTION))
+		return ipe_action_deny;
+
+	return ipe_action_unset;
+}
+
+/**
+ * ipe_parse_op: Given a token, parse the value as if it were an 'op' token.
+ * @op: Token to parse to determine the operation.
+ *
+ * "op" tokens are of the form: op=(EXECUTE|FIRMWARE|KEXEC_IMAGE|...)
+ * for more information about IPE policy, please see the documentation.
+ *
+ * Return:
+ * ipe_op_max - ERR
+ * otherwise - OK
+ */
+static enum ipe_op ipe_parse_op(struct token *op)
+{
+	substring_t match[MAX_OPT_ARGS];
+	const match_table_t ops = {
+		{ ipe_op_execute,		IPE_OP_EXECUTE },
+		{ ipe_op_firmware,		IPE_OP_FIRMWARE },
+		{ ipe_op_kexec_image,		IPE_OP_KEXEC_IMAGE },
+		{ ipe_op_kexec_initramfs,	IPE_OP_KEXEC_INITRAMFS },
+		{ ipe_op_x509,			IPE_OP_X509_CERTIFICATE },
+		{ ipe_op_policy,		IPE_OP_POLICY },
+		{ ipe_op_kmodule,		IPE_OP_KMODULE },
+		{ ipe_op_kernel_read,		IPE_OP_KERNEL_READ },
+		{ ipe_op_max,			NULL },
+	};
+
+	return match_token((char *)op->val, ops, match);
+}
+
+/**
+ * ipe_set_default: Set the default of the policy, at various scope levels
+ *		    depending on the value of op.
+ * @op: Operation that was parsed.
+ * @pol: Policy to modify with the newly-parsed default action.
+ * @a: Action token (see parse_action) to parse to determine
+ *     the default.
+ *
+ * Return:
+ * 0 - OK
+ * -EBADMSG - Invalid policy format
+ */
+static int ipe_set_default(enum ipe_op op, struct ipe_policy *pol,
+			   struct token *a)
+{
+	int rc = 0;
+	size_t i = 0;
+	enum ipe_action act = ipe_parse_action(a);
+
+	if (act == ipe_action_unset)
+		return -EBADMSG;
+
+	if (op == ipe_op_max)
+		return ipe_set_action(&pol->def, act);
+
+	if (op == ipe_op_kernel_read) {
+		for (i = ipe_op_firmware; i <= ipe_op_kmodule; ++i) {
+			rc = ipe_set_action(&pol->ops[i].def, act);
+			if (rc != 0)
+				return rc;
+		}
+		return 0;
+	}
+
+	return ipe_set_action(&pol->ops[op].def, act);
+}
+
+/**
+ * ipe_parse_default: Parse a default statement of an IPE policy modify @pol
+ *		      with the proper changes
+ * @tokens: List of tokens parsed from the line
+ * @pol: Policy to modify with the newly-parsed default action
+ *
+ *
+ * Return:
+ * 0 - OK
+ * -EBADMSG - Invalid policy format
+ * -ENOENT - Unknown policy structure
+ */
+static int ipe_parse_default(struct list_head *tokens,
+			     struct ipe_policy *pol)
+{
+	struct token *f = NULL;
+	struct token *s = NULL;
+	struct token *t = NULL;
+	enum ipe_op i = ipe_op_max;
+
+	f = list_first_entry(tokens, struct token, next_tok);
+	s = list_next_entry(f, next_tok);
+	if (is_action(s))
+		return ipe_set_default(ipe_op_max, pol, s);
+
+	i = ipe_parse_op(s);
+	if (i == ipe_op_max)
+		return -ENOENT;
+
+	t = list_next_entry(s, next_tok);
+	if (is_action(t)) {
+		t = list_next_entry(s, next_tok);
+		return ipe_set_default(i, pol, t);
+	}
+
+	return -ENOENT;
+}
+
+/**
+ * ipe_free_token_list - Free a list of tokens, and then reinitialize @list
+ *			 dropping all tokens.
+ * @list: List to be freed.
+ */
+static void ipe_free_token_list(struct list_head *list)
+{
+	struct token *ptr, *next;
+
+	list_for_each_entry_safe(ptr, next, list, next_tok)
+		kfree(ptr);
+
+	INIT_LIST_HEAD(list);
+}
+
+/**
+ * ipe_free_prop - Deallocator for an ipe_prop_container structure.
+ * @cont: Object to free.
+ */
+static void ipe_free_prop(struct ipe_prop_container *cont)
+{
+	if (IS_ERR_OR_NULL(cont))
+		return;
+
+	if (cont->prop->free_val)
+		cont->prop->free_val(&cont->value);
+	kfree(cont);
+}
+
+/**
+ * ipe_alloc_prop: Allocator for a ipe_prop_container structure.
+ * @tok: Token structure representing the "key=value" pair of the property.
+ *
+ * Return:
+ * Pointer to ipe_rule - OK
+ * ERR_PTR(-ENOMEM) - Allocation failed
+ */
+static struct ipe_prop_container *ipe_alloc_prop(const struct token *tok)
+{
+	int rc = 0;
+	const struct ipe_property *prop = NULL;
+	struct ipe_prop_container *cont = NULL;
+
+	prop = ipe_lookup_prop(tok->key);
+	if (!prop) {
+		rc = -ENOENT;
+		goto err;
+	}
+
+	cont = kzalloc(sizeof(*cont), GFP_KERNEL);
+	if (!cont) {
+		rc = -ENOMEM;
+		goto err;
+	}
+
+	INIT_LIST_HEAD(&cont->next);
+
+	rc = prop->parse(tok->val, &cont->value);
+	if (rc != 0)
+		goto err;
+
+	cont->prop = prop;
+
+	return cont;
+err:
+	ipe_free_prop(cont);
+	return ERR_PTR(rc);
+}
+
+/**
+ * ipe_free_rule: Deallocator for an ipe_rule structure.
+ * @rule: Object to free.
+ */
+static void ipe_free_rule(struct ipe_rule *rule)
+{
+	struct ipe_prop_container *ptr;
+	struct list_head *l_ptr, *l_next;
+
+	if (IS_ERR_OR_NULL(rule))
+		return;
+
+	list_for_each_safe(l_ptr, l_next, &rule->props) {
+		ptr = container_of(l_ptr, struct ipe_prop_container, next);
+		list_del(l_ptr);
+		ipe_free_prop(ptr);
+	}
+
+	kfree(rule);
+}
+
+/**
+ * ipe_alloc_rule: Allocate a ipe_rule structure, for operation @op, parsed
+ *		   from the first token in list @head.
+ * @op: Operation parsed from the first token in @head.
+ * @t: The first token in @head that was parsed.
+ * @head: List of remaining tokens to parse.
+ *
+ * Return:
+ * Valid ipe_rule pointer - OK
+ * ERR_PTR(-EBADMSG) - Invalid syntax
+ * ERR_PTR(-ENOMEM) - Out of memory
+ */
+static struct ipe_rule *ipe_alloc_rule(enum ipe_op op, struct token *t,
+				       struct list_head *head)
+{
+	int rc = 0;
+	struct token *ptr;
+	enum ipe_action act;
+	struct ipe_rule *rule = NULL;
+	struct ipe_prop_container *prop = NULL;
+
+	ptr = list_next_entry(t, next_tok);
+	if (!is_action(ptr)) {
+		rc = -EBADMSG;
+		goto err;
+	}
+
+	act = ipe_parse_action(ptr);
+	if (act == ipe_action_unset) {
+		rc = -EBADMSG;
+		goto err;
+	}
+
+	rule = kzalloc(sizeof(*rule), GFP_KERNEL);
+	if (!rule) {
+		rc = -ENOMEM;
+		goto err;
+	}
+
+	INIT_LIST_HEAD(&rule->props);
+	INIT_LIST_HEAD(&rule->next);
+	rule->action = act;
+	rule->op = op;
+
+	list_for_each_entry_continue(ptr, head, next_tok) {
+		prop = ipe_alloc_prop(ptr);
+
+		if (IS_ERR(prop)) {
+			rc = PTR_ERR(prop);
+			goto err;
+		}
+
+		list_add_tail(&prop->next, &rule->props);
+	}
+
+	return rule;
+err:
+	ipe_free_prop(prop);
+	ipe_free_rule(rule);
+	return ERR_PTR(rc);
+}
+
+/**
+ * ipe_dup_prop: Duplicate an ipe_prop_container structure
+ * @p: Container to duplicate.
+ *
+ * This function is used to duplicate individual properties within a rule.
+ * It should only be called in operations that actually map to one or more
+ * operations.
+ *
+ * Return:
+ * Valid ipe_prop_container - OK
+ * ERR_PTR(-ENOMEM) - Out of memory
+ * Other Errors - see various property duplicator functions
+ */
+static
+struct ipe_prop_container *ipe_dup_prop(const struct ipe_prop_container *p)
+{
+	int rc = 0;
+	struct ipe_prop_container *dup;
+
+	dup = kzalloc(sizeof(*dup), GFP_KERNEL);
+	if (!dup) {
+		rc = -ENOMEM;
+		goto err;
+	}
+
+	dup->prop = p->prop;
+	INIT_LIST_HEAD(&dup->next);
+
+	rc = p->prop->dup(p->value, &dup->value);
+	if (rc != 0)
+		goto err;
+
+	return dup;
+err:
+	ipe_free_prop(dup);
+	return ERR_PTR(rc);
+}
+
+/**
+ * ipe_dup_rule: Duplicate a policy rule, used for pseudo hooks like
+ *		 KERNEL_READ to map a policy rule across all hooks.
+ * @r: Rule to duplicate.
+ *
+ * Return:
+ * valid ipe_rule - OK
+ * ERR_PTR(-ENOMEM) - Out of memory
+ * Other Errors - See ipe_dup_prop
+ */
+static struct ipe_rule *ipe_dup_rule(const struct ipe_rule *r)
+{
+	int rc = 0;
+	struct ipe_rule *dup;
+	struct ipe_prop_container *ptr;
+
+	dup = kzalloc(sizeof(*dup), GFP_KERNEL);
+	if (!dup) {
+		rc = -ENOMEM;
+		goto err;
+	}
+
+	dup->op = r->op;
+	dup->action = r->action;
+	INIT_LIST_HEAD(&dup->props);
+	INIT_LIST_HEAD(&dup->next);
+
+	list_for_each_entry(ptr, &r->props, next) {
+		struct ipe_prop_container *prop2;
+
+		prop2 = ipe_dup_prop(ptr);
+		if (IS_ERR(prop2)) {
+			rc = PTR_ERR(prop2);
+			goto err;
+		}
+
+		list_add_tail(&prop2->next, &dup->props);
+	}
+
+	return dup;
+err:
+	ipe_free_rule(dup);
+	return ERR_PTR(rc);
+}
+
+/**
+ * ipe_free_policy: Deallocate an ipe_policy structure.
+ * @pol: Policy to free.
+ */
+void ipe_free_policy(struct ipe_policy *pol)
+{
+	size_t i;
+	struct ipe_rule *ptr;
+	struct ipe_rule_table *op;
+	struct list_head *l_ptr, *l_next;
+
+	if (IS_ERR_OR_NULL(pol))
+		return;
+
+	for (i = 0; i < ARRAY_SIZE(pol->ops); ++i) {
+		op = &pol->ops[i];
+
+		list_for_each_safe(l_ptr, l_next, &op->rules) {
+			ptr = list_entry(l_ptr, struct ipe_rule, next);
+			list_del(l_ptr);
+			ipe_free_rule(ptr);
+		}
+	}
+
+	kfree(pol->policy_name);
+	kfree(pol);
+}
+
+/**
+ * ipe_alloc_policy: Give a list of tokens representing the first line of the
+ *		     token, attempt to parse it as an IPE policy header, and
+ *		     allocate a policy structure based on those values.
+ * @tokens: List of tokens parsed from the first line of the policy
+ *
+ * Return:
+ * Valid ipe_policy pointer - OK
+ * ERR_PTR(-ENOMEM) - Out of memory
+ * ERR_PTR(-EBADMSG) - Invalid policy syntax
+ */
+static struct ipe_policy *ipe_alloc_policy(struct list_head *tokens)
+{
+	size_t i;
+	int rc = 0;
+	struct token *name = NULL;
+	struct token *ver = NULL;
+	struct ipe_policy *lp = NULL;
+
+	name = list_first_entry(tokens, struct token, next_tok);
+	if (!is_name(name)) {
+		rc = -EBADMSG;
+		goto err;
+	}
+
+	if (list_is_singular(tokens)) {
+		rc = -EBADMSG;
+		goto err;
+	}
+
+	ver = list_next_entry(name, next_tok);
+	if (!is_ver(ver)) {
+		rc = -EBADMSG;
+		goto err;
+	}
+
+	lp = kzalloc(sizeof(*lp), GFP_KERNEL);
+	if (!lp) {
+		rc = -ENOMEM;
+		goto err;
+	}
+
+	for (i = 0; i < ARRAY_SIZE(lp->ops); ++i) {
+		lp->ops[i].def = ipe_action_unset;
+		INIT_LIST_HEAD(&lp->ops[i].rules);
+	}
+
+	lp->policy_name = kstrdup(name->val, GFP_KERNEL);
+	if (!lp->policy_name) {
+		rc = -ENOMEM;
+		goto err;
+	}
+
+	rc = ipe_parse_version(ver->val, &lp->policy_version);
+	if (rc != 0)
+		goto err;
+
+	lp->def = ipe_action_unset;
+
+	return lp;
+err:
+	ipe_free_policy(lp);
+	return ERR_PTR(rc);
+}
+
+/**
+ * ipe_add_rule_for_range: Given a ipe_rule @r, duplicate @r and add the rule
+ *			   to @pol for the operation range @start to @end.
+ * @start: The starting point of the range to add the rule to.
+ * @end: The ending point of the range to add the rule to.
+ * @r: The rule to copy.
+ * @pol: Policy structure to modify with the result.
+ *
+ * This is @start to @end, inclusive. @r is still valid after this function,
+ * and should be freed if appropriate.
+ *
+ * Return:
+ * 0 - OK
+ * Other Errors - See ipe_dup_prop
+ */
+static int ipe_add_rule_for_range(enum ipe_op start, enum ipe_op end,
+				  struct ipe_rule *r, struct ipe_policy *pol)
+{
+	enum ipe_op i;
+	struct ipe_rule *cpy = NULL;
+
+	for (i = start; i <= end; ++i) {
+		cpy = ipe_dup_rule(r);
+		if (IS_ERR(cpy))
+			return PTR_ERR(cpy);
+
+		list_add_tail(&cpy->next, &pol->ops[i].rules);
+	}
+
+	return 0;
+}
+
+/**
+ * ipe_parse_line: Given a list of tokens, attempt to parse it into a rule
+ *		   structure, and add it to the passed-in ipe_policy structure.
+ * @tokens: List of tokens that were parsed.
+ * @pol: Policy structure to modify with the result.
+ *
+ * Return:
+ * 0 - OK
+ * -ENOENT - Unrecognized property
+ * -ENOMEM - Out of memory
+ * Other Errors - See ipe_dup_prop
+ */
+static int ipe_parse_line(struct list_head *tokens,
+			  struct ipe_policy *pol)
+{
+	int rc = 0;
+	struct token *f;
+	enum ipe_op i = ipe_op_max;
+	struct ipe_rule *rule = NULL;
+
+	f = list_first_entry(tokens, struct token, next_tok);
+
+	switch (f->key_priority) {
+	case default_action:
+		rc = ipe_parse_default(tokens, pol);
+		break;
+	case op:
+		i = ipe_parse_op(f);
+		if (i == ipe_op_max)
+			return -ENOENT;
+
+		if (list_is_singular(tokens))
+			return -EBADMSG;
+
+		rule = ipe_alloc_rule(i, f, tokens);
+		if (IS_ERR(rule)) {
+			rc = PTR_ERR(rule);
+			goto cleanup;
+		}
+
+		if (i == ipe_op_kernel_read) {
+			rc = ipe_add_rule_for_range(ipe_op_firmware,
+						    ipe_op_kmodule, rule, pol);
+			if (rc != 0)
+				goto cleanup;
+		} else {
+			list_add_tail(&rule->next, &pol->ops[i].rules);
+			rule = NULL;
+		}
+		break;
+	default:
+		return -ENOENT;
+	}
+
+cleanup:
+	ipe_free_rule(rule);
+	return rc;
+}
+
+/**
+ * ipe_check_policy_defaults: Ensure all defaults in policy are set
+ *	for every operation known to IPE.
+ *
+ * @p: Policy to check the defaults.
+ *
+ * Return:
+ * 0 - OK
+ * -EBADMSG - A default was left unset.
+ */
+static int ipe_check_policy_defaults(const struct ipe_policy *p)
+{
+	size_t i;
+
+	if (p->def == ipe_action_unset) {
+		for (i = 0; i < ARRAY_SIZE(p->ops); ++i) {
+			if (p->ops[i].def == ipe_action_unset)
+				return -EBADMSG;
+		}
+	}
+
+	return 0;
+}
+
+/**
+ * ipe_parse_policy: Given a string, parse the string into an IPE policy
+ *		     structure.
+ * @policy: NULL terminated string to parse.
+ *
+ * This function will modify @policy, callers should pass a copy if this
+ * value is needed later.
+ *
+ * Return:
+ * Valid ipe_policy structure - OK
+ * ERR_PTR(-EBADMSG) - Invalid Policy Syntax (Unrecoverable)
+ * ERR_PTR(-ENOMEM) - Out of Memory
+ */
+struct ipe_policy *ipe_parse_policy(char *policy)
+{
+	int rc = 0;
+	size_t i = 1;
+	char *p = NULL;
+	LIST_HEAD(t_list);
+	struct ipe_policy *local_p = NULL;
+
+	while ((p = strsep(&policy, "\n\0")) != NULL) {
+		rc = ipe_tokenize_line(p, &t_list);
+		if (rc == -ENOENT) {
+			++i;
+			continue;
+		}
+		if (rc != 0)
+			goto err;
+
+		if (!local_p) {
+			local_p = ipe_alloc_policy(&t_list);
+			if (IS_ERR(local_p)) {
+				rc = PTR_ERR(local_p);
+				goto err;
+			}
+		} else {
+			rc = ipe_parse_line(&t_list, local_p);
+			if (rc) {
+				pr_warn("failed to parse line %zu", i);
+				goto err;
+			}
+		}
+
+		ipe_free_token_list(&t_list);
+		++i;
+	}
+
+	rc = ipe_check_policy_defaults(local_p);
+	if (rc != 0)
+		goto err;
+
+	return local_p;
+err:
+	ipe_free_token_list(&t_list);
+	ipe_free_policy(local_p);
+	return ERR_PTR(rc);
+}
diff --git a/security/ipe/ipe-parse.h b/security/ipe/ipe-parse.h
new file mode 100644
index 000000000000..b1a9bbd97534
--- /dev/null
+++ b/security/ipe/ipe-parse.h
@@ -0,0 +1,17 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) Microsoft Corporation. All rights reserved.
+ */
+
+#include "ipe-policy.h"
+
+#include <linux/types.h>
+
+#ifndef IPE_PARSE_H
+#define IPE_PARSE_H
+
+struct ipe_policy *ipe_parse_policy(char *policy);
+
+void ipe_free_policy(struct ipe_policy *pol);
+
+#endif /* IPE_AUDIT_H */
diff --git a/security/ipe/ipe-policy.c b/security/ipe/ipe-policy.c
new file mode 100644
index 000000000000..74535fb03666
--- /dev/null
+++ b/security/ipe/ipe-policy.c
@@ -0,0 +1,149 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) Microsoft Corporation. All rights reserved.
+ */
+
+#include "ipe.h"
+#include "ipe-secfs.h"
+#include "ipe-policy.h"
+#include "ipe-parse.h"
+#include "ipe-audit.h"
+
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/err.h>
+#include <linux/slab.h>
+#include <linux/list.h>
+#include <linux/mutex.h>
+#include <linux/lockdep.h>
+#include <linux/fs.h>
+#include <linux/security.h>
+#include <linux/rcupdate.h>
+
+#define VER_TO_UINT64(_major, _minor, _rev) \
+	((((((u64)(_major)) << 16) | ((u64)(_minor))) << 16) | ((u64)(_rev)))
+
+/**
+ * ipe_is_version_allowed: Determine if @new has a greater or equal
+ *			   policy version than @old.
+ * @old: The policy to compare against.
+ * @new: The policy staged to replace @old.
+ *
+ * Return:
+ * true - @new has a policy version >= than @old
+ * false - @new does not have a policy version >= than @old
+ */
+static bool ipe_is_version_allowed(const struct ipe_pol_ver *old,
+				   const struct ipe_pol_ver *new)
+{
+	u64 old_ver = VER_TO_UINT64(old->major, old->minor, old->rev);
+	u64 new_ver = VER_TO_UINT64(new->major, new->minor, new->rev);
+
+	return new_ver >= old_ver;
+}
+
+/**
+ * ipe_is_valid_policy: determine if @old is allowed to replace @new.
+ * @old: policy that the @new is supposed to replace. Can be NULL.
+ * @new: the policy that is supposed to replace @new.
+ *
+ * Return:
+ * true - @new can replace @old
+ * false - @new cannot replace @old
+ */
+bool ipe_is_valid_policy(const struct ipe_policy *old,
+			 const struct ipe_policy *new)
+{
+	if (old)
+		return ipe_is_version_allowed(&old->policy_version,
+					      &new->policy_version);
+	return true;
+}
+
+/**
+ * ipe_is_active_policy: Determine if @policy is the currently active policy.
+ * @policy: Policy to check if it's the active policy.
+ *
+ * Return:
+ * true - @policy is the active policy
+ * false - @policy is not the active policy
+ */
+bool ipe_is_active_policy(const struct ipe_policy *policy)
+{
+	return rcu_access_pointer(ipe_active_policy) == policy;
+}
+
+/**
+ * ipe_update_active_policy: Determine if @old is the active policy, and update
+ *			     the active policy if necessary.
+ * @old: The previous policy that the update is trying to replace.
+ * @new: The new policy attempting to replace @old.
+ *
+ * If @old is not the active policy, nothing will be done.
+ *
+ * Return:
+ * 0 - OK
+ * -EBADMSG - Invalid Policy
+ */
+int ipe_update_active_policy(const struct ipe_policy *old,
+			     const struct ipe_policy *new)
+{
+	const struct ipe_policy *curr = NULL;
+
+	lockdep_assert_held(&ipe_policy_lock);
+
+	/* no active policy, safe to ignore */
+	if (!rcu_access_pointer(ipe_active_policy))
+		return 0;
+
+	curr = rcu_dereference_protected(ipe_active_policy,
+					 lockdep_is_held(&ipe_policy_lock));
+
+	if (curr == old) {
+		if (!ipe_is_valid_policy(curr, new))
+			return -EINVAL;
+
+		ipe_audit_policy_activation(new);
+
+		(void)rcu_replace_pointer(ipe_active_policy, new,
+					  lockdep_is_held(&ipe_policy_lock));
+	}
+
+	return 0;
+}
+
+/**
+ * ipe_activate_policy: Set a specific policy as the active policy.
+ * @pol: The policy to set as the active policy.
+ *
+ * This is only called by the securityfs entry,
+ *	"$securityfs/ipe/policies/$policy_name/active".
+ *
+ * Return:
+ * 0 - OK
+ * -EINVAL - Policy that is being activated is lower in version than
+ *	     currently running policy.
+ */
+int ipe_activate_policy(const struct ipe_policy *pol)
+{
+	const struct ipe_policy *curr = NULL;
+
+	lockdep_assert_held(&ipe_policy_lock);
+
+	curr = rcu_dereference_protected(ipe_active_policy,
+					 lockdep_is_held(&ipe_policy_lock));
+
+	/*
+	 * User-set policies must be >= to current running policy.
+	 */
+	if (!ipe_is_valid_policy(curr, pol))
+		return -EINVAL;
+
+	ipe_audit_policy_activation(pol);
+
+	/* cleanup of this pointer is handled by the secfs removal */
+	(void)rcu_replace_pointer(ipe_active_policy, pol,
+				  lockdep_is_held(&ipe_policy_lock));
+
+	return 0;
+}
diff --git a/security/ipe/ipe-policy.h b/security/ipe/ipe-policy.h
index c0c9f2962c92..b3ef3d3d2e7f 100644
--- a/security/ipe/ipe-policy.h
+++ b/security/ipe/ipe-policy.h
@@ -14,9 +14,6 @@
 #ifndef IPE_POLICY_H
 #define IPE_POLICY_H
 
-#define IPE_HEADER_POLICY_NAME		"policy_name"
-#define IPE_HEADER_POLICY_VERSION	"policy_version"
-
 extern const char *const ipe_boot_policy;
 extern const struct ipe_policy *ipe_active_policy;
 
@@ -59,4 +56,14 @@ struct ipe_policy {
 	struct ipe_rule_table ops[ipe_op_max - 1];
 };
 
+bool ipe_is_valid_policy(const struct ipe_policy *old,
+			 const struct ipe_policy *new);
+
+bool ipe_is_active_policy(const struct ipe_policy *policy);
+
+int ipe_update_active_policy(const struct ipe_policy *old,
+			     const struct ipe_policy *new);
+
+int ipe_activate_policy(const struct ipe_policy *policy);
+
 #endif /* IPE_POLICY_H */
diff --git a/security/ipe/ipe-prop-internal.h b/security/ipe/ipe-prop-internal.h
index 95a2081e77ee..7c14b204be13 100644
--- a/security/ipe/ipe-prop-internal.h
+++ b/security/ipe/ipe-prop-internal.h
@@ -10,9 +10,19 @@
 #ifndef IPE_PROPERTY_INTERNAL_H
 #define IPE_PROPERTY_INTERNAL_H
 
-#define IPE_PROPERTY_OPERATION	"op"
-#define IPE_PROPERTY_DEFAULT	"DEFAULT"
-#define IPE_PROPERTY_ACTION	"action"
+/* built-in tokens */
+#define IPE_HEADER_POLICY_NAME		"policy_name"
+#define IPE_HEADER_POLICY_VERSION	"policy_version"
+#define IPE_PROPERTY_OPERATION		"op"
+#define IPE_PROPERTY_DEFAULT		"DEFAULT"
+#define IPE_PROPERTY_ACTION		"action"
+
+/* Version strings for built-in tokens */
+#define IPE_PROPERTY_OPERATION_VER	IPE_PROPERTY_OPERATION		"=1"
+#define IPE_PROPERTY_ACTION_VER		IPE_PROPERTY_ACTION		"=1"
+#define IPE_PROPERTY_DEFAULT_VER	IPE_PROPERTY_DEFAULT		"=1"
+#define IPE_HEADER_POLICY_NAME_VER	IPE_HEADER_POLICY_NAME		"=1"
+#define IPE_HEADER_POLICY_VERSION_VER	IPE_HEADER_POLICY_VERSION	"=1"
 
 #define IPE_OP_EXECUTE		"EXECUTE"
 #define IPE_OP_FIRMWARE		"FIRMWARE"
@@ -23,6 +33,8 @@
 #define IPE_OP_KMODULE		"KMODULE"
 #define IPE_OP_KERNEL_READ	"KERNEL_READ"
 
+#define IPE_UNKNOWN		"UNKNOWN"
+
 struct ipe_prop_reg {
 	struct rb_node node;
 	const struct ipe_property *prop;
diff --git a/security/ipe/ipe-property.c b/security/ipe/ipe-property.c
index d4b0283f86bd..262da9f622d6 100644
--- a/security/ipe/ipe-property.c
+++ b/security/ipe/ipe-property.c
@@ -12,7 +12,7 @@
 #include <linux/slab.h>
 
 /* global root containing all registered properties */
-struct rb_root ipe_registry_root = RB_ROOT;
+static struct rb_root ipe_registry_root = RB_ROOT;
 
 /**
  * reg_lookup: Attempt to find a `prop_reg` structure with property_name @key.
@@ -70,7 +70,8 @@ const struct ipe_property *ipe_lookup_prop(const char *key)
  * the system, after calling ipe_register_property.
  *
  * All necessary properties need to be loaded via this method before
- * loading a policy, otherwise the properties will be ignored as unknown.
+ * loading a policy, otherwise the marked as unknown, and cause parsing to
+ * fail.
  *
  * Return:
  * 0 - OK
@@ -113,9 +114,9 @@ int ipe_register_property(const struct ipe_property *prop)
 
 /**
  * ipe_for_each_prop: Iterate over all currently-registered properties
- *	calling @fn on the values, and providing @view @ctx.
+ *		      calling @fn on the values, and providing @view @ctx.
  * @view: The function to call for each property. This is given the property
- *	structure as the first argument, and @ctx as the second.
+ *	  structure as the first argument, and @ctx as the second.
  * @ctx: caller-specified context that is passed to the function. Can be NULL.
  *
  * Return:
diff --git a/security/ipe/ipe-property.h b/security/ipe/ipe-property.h
index cf570d52d0d2..8bb2e2c1619c 100644
--- a/security/ipe/ipe-property.h
+++ b/security/ipe/ipe-property.h
@@ -86,6 +86,7 @@ typedef void (*ipe_free_value)(void **value);
 
 struct ipe_property {
 	const char			*const property_name;
+	u16				version;
 	ipe_property_evaluator		eval;
 	ipe_property_audit		rule_audit;
 	ipe_ctx_audit			ctx_audit;
diff --git a/security/ipe/ipe-secfs.c b/security/ipe/ipe-secfs.c
new file mode 100644
index 000000000000..006619598d57
--- /dev/null
+++ b/security/ipe/ipe-secfs.c
@@ -0,0 +1,1309 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) Microsoft Corporation. All rights reserved.
+ */
+
+#include "ipe.h"
+#include "ipe-parse.h"
+#include "ipe-secfs.h"
+#include "ipe-policy.h"
+#include "ipe-audit.h"
+
+#include <linux/types.h>
+#include <linux/security.h>
+#include <linux/fs.h>
+#include <linux/rcupdate.h>
+#include <linux/mutex.h>
+#include <linux/init.h>
+#include <linux/dcache.h>
+#include <linux/namei.h>
+#include <linux/verification.h>
+#include <linux/capability.h>
+
+#define IPE_ROOT		"ipe"
+#define IPE_POLICIES		"policies"
+#define NEW_POLICY		"new_policy"
+#define IPE_PROPERTY_CFG	"property_config"
+#define IPE_SUCCESS_AUDIT	"success_audit"
+#define IPE_ENFORCE		"enforce"
+
+#define IPE_FULL_CONTENT	"raw"
+#define IPE_INNER_CONTENT	"content"
+#define IPE_ACTIVE_POLICY	"active"
+#define IPE_DELETE_POLICY	"delete"
+
+struct ipe_policy_node {
+	u8		*data;
+	size_t		data_len;
+	const u8	*content;
+	size_t		content_size;
+
+	struct ipe_policy *parsed;
+};
+
+/* root directory */
+static struct dentry *securityfs_root __ro_after_init;
+
+/* subdirectory containing policies */
+static struct dentry *policies_root __ro_after_init;
+
+/* boot policy */
+static struct dentry *boot_policy_node __ro_after_init;
+
+/* top-level IPE commands */
+static struct dentry *new_policy_node __ro_after_init;
+static struct dentry *property_cfg_node __ro_after_init;
+static struct dentry *enforce_node __ro_after_init;
+static struct dentry *success_audit_node __ro_after_init;
+
+/* lock for synchronizing writers across ipe policy */
+DEFINE_MUTEX(ipe_policy_lock);
+
+/**
+ * get_int_user - retrieve a single integer from a string located in userspace.
+ * @data: usespace address to parse for an integer
+ * @len: length of @data
+ * @offset: offset into @data. Unused.
+ * @value: pointer to a value to propagate with the result
+ *
+ * Return:
+ * 0 - OK
+ * -ENOMEM - allocation failed
+ * -EINVAL - more than 1 integer was present
+ * Other - see strnpy_from_user
+ */
+static int get_int_user(const char __user *data, size_t len, loff_t *offset,
+			int *value)
+{
+	int rc = 0;
+	char *buffer = NULL;
+
+	buffer = kzalloc(len + 1, GFP_KERNEL);
+	if (!buffer)
+		return -ENOMEM;
+
+	rc = strncpy_from_user(buffer, data, len + 1);
+	if (rc < 0)
+		goto out;
+
+	rc = kstrtoint(buffer, 10, value);
+out:
+	kfree(buffer);
+	return rc;
+}
+
+/**
+ * ipe_get_audit_mode - retrieve the current value of the success_audit flag
+ *			as a string representation.
+ * @f: The file structure representing the securityfs entry. Unused.
+ * @data: userspace buffer to place the result
+ * @len: length of @data
+ * @offset: offset into @data
+ *
+ * This is the handler for the 'read' syscall on the securityfs node,
+ * ipe/success_audit
+ *
+ * Return:
+ * > 0 - OK
+ * < 0 - Error, see simple_read_from_buffer
+ */
+static ssize_t ipe_get_audit_mode(struct file *f, char __user *data, size_t len,
+				  loff_t *offset)
+{
+	char tmp[3] = { 0 };
+
+	snprintf(tmp, ARRAY_SIZE(tmp), "%c\n", (ipe_success_audit) ? '1' : '0');
+
+	return simple_read_from_buffer(data, len, offset, tmp,
+				       ARRAY_SIZE(tmp));
+}
+
+/**
+ * ipe_set_audit_mode - change the value of the ipe_success_audit flag.
+ * @f: The file structure representing the securityfs entry
+ * @data: userspace buffer containing value to be set. Should be "1" or "0".
+ * @len: length of @data
+ * @offset: offset into @data
+ *
+ * Return:
+ * > 0 - OK
+ * -EPERM - if MAC system available, missing CAP_MAC_ADMIN.
+ * -EINVAL - value written was not "1" or "0".
+ */
+static ssize_t ipe_set_audit_mode(struct file *f, const char __user *data, size_t len,
+				  loff_t *offset)
+{
+	int v = 0;
+	int rc = 0;
+
+	if (!file_ns_capable(f, &init_user_ns, CAP_MAC_ADMIN))
+		return -EPERM;
+
+	rc = get_int_user(data, len, offset, &v);
+	if (rc)
+		return rc;
+
+	if (v != 0 && v != 1)
+		return -EINVAL;
+
+	ipe_success_audit = v == 1;
+
+	return len;
+}
+
+static const struct file_operations audit_ops = {
+	.read = ipe_get_audit_mode,
+	.write = ipe_set_audit_mode
+};
+
+#ifdef CONFIG_SECURITY_IPE_PERMISSIVE_SWITCH
+
+/**
+ * ipe_get_enforce - retrieve the current value of the ipe_enforce flag
+ *		     as a string representation.
+ * @f: The file structure representing the securityfs entry. Unused.
+ * @data: userspace buffer to place the result
+ * @len: length of @data
+ * @offset: offset into @data
+ *
+ * This is the handler for the 'read' syscall on the securityfs node,
+ * ipe/enforce
+ *
+ * Return:
+ * > 0 - OK
+ * < 0 - Error, see simple_read_from_buffer
+ */
+static ssize_t ipe_get_enforce(struct file *f, char __user *data, size_t len,
+			       loff_t *offset)
+{
+	char tmp[3] = { 0 };
+
+	snprintf(tmp, ARRAY_SIZE(tmp), "%c\n", (ipe_enforce) ? '1' : '0');
+
+	return simple_read_from_buffer(data, len, offset, tmp,
+				       ARRAY_SIZE(tmp));
+}
+
+/**
+ * ipe_set_enforce - change the value of the ipe_enforce flag.
+ * @f: The file structure representing the securityfs entry
+ * @data: userspace buffer containing value to be set. Should be "1" or "0".
+ * @len: length of @data
+ * @offset: offset into @data
+ *
+ * Return:
+ * > 0 - OK
+ * -EPERM - if MAC system available, missing CAP_MAC_ADMIN.
+ * -EINVAL - value written was not "1" or "0".
+ */
+static ssize_t ipe_set_enforce(struct file *f, const char __user *data, size_t len,
+			       loff_t *offset)
+{
+	int v = 0;
+	int rc = 0;
+	bool ret = 0;
+
+	if (!file_ns_capable(f, &init_user_ns, CAP_MAC_ADMIN))
+		return -EPERM;
+
+	rc = get_int_user(data, len, offset, &v);
+	if (rc)
+		return rc;
+
+	if (v != 0 && v != 1)
+		return -EINVAL;
+
+	ret = v == 1;
+
+	if (ret != ipe_enforce)
+		ipe_audit_mode(ret);
+
+	ipe_enforce = ret;
+
+	return len;
+}
+
+static const struct file_operations enforce_ops = {
+	.read = ipe_get_enforce,
+	.write = ipe_set_enforce
+};
+
+/**
+ * ipe_init_enforce_node - Wrapper around securityfs_create_file for the
+ *			   ipe/enforce securityfs node.
+ * @root: securityfs node that is the parent of the new node to be created
+ *
+ * This allows this function to be no-op'd when the permissive switch is
+ * disabled.
+ *
+ * Return:
+ * See securityfs_create_file.
+ */
+static inline struct dentry *ipe_init_enforce_node(struct dentry *root)
+{
+	return securityfs_create_file(IPE_ENFORCE, 0644, root, NULL,
+				      &enforce_ops);
+}
+
+#else
+
+/**
+ * ipe_init_enforce_node - Wrapper around securityfs_create_file for the
+ *			   ipe/enforce securityfs node.
+ * @root: Unused
+ *
+ * This allows this function to be no-op'd when the permissive switch is
+ * disabled.
+ *
+ * Return:
+ * NULL.
+ */
+static inline struct dentry *ipe_init_enforce_node(struct dentry *root)
+{
+	return NULL;
+}
+
+#endif /* CONFIG_SECURITY_IPE_PERMISSIVE_SWITCH */
+
+/**
+ * retrieve_backed_dentry: Retrieve a dentry with a backing inode, identified
+ *			   by @name, under @parent.
+ * @name: Name of the dentry under @parent.
+ * @parent: The parent dentry to search under for @name.
+ * @size: Length of @name.
+ *
+ * This takes a reference to the returned dentry. Caller needs to call dput
+ * to drop the reference.
+ *
+ * Return:
+ * valid dentry - OK
+ * ERR_PTR - Error, see lookup_one_len_unlocked
+ * NULL - No backing inode was found
+ */
+static struct dentry *retrieve_backed_dentry(const char *name,
+					     struct dentry *parent,
+					     size_t size)
+{
+	struct dentry *tmp = NULL;
+
+	tmp = lookup_one_len_unlocked(name, parent, size);
+	if (IS_ERR(tmp))
+		return tmp;
+
+	if (!d_really_is_positive(tmp))
+		return NULL;
+
+	return tmp;
+}
+
+/**
+ * alloc_size_cb: Callback for determining the allocation size of the grammar
+ *		  buffer
+ * @prop: ipe_property structure to determine allocation size
+ * @ctx: void* representing a size_t* to add the allocation size to.
+ *
+ * Return:
+ * 0 - Always
+ */
+static int alloc_size_cb(const struct ipe_property *prop, void *ctx)
+{
+	size_t *ref = ctx;
+	char tmp[6] = { 0 };
+
+	snprintf(tmp, ARRAY_SIZE(tmp), "%d", prop->version);
+
+	/* property_name=u16\n */
+	*ref += strlen(prop->property_name) + strlen(tmp) + 2;
+
+	return 0;
+}
+
+/**
+ * build_cfg_str: Callback to populate the previously-allocated string
+ *		  buffer for ipe's grammar version with the content.
+ * @prop: ipe_property structure to determine allocation size
+ * @ctx: void* representing a char* to append the population to.
+ *
+ * Return:
+ * 0 - Always
+ */
+static int build_cfg_str(const struct ipe_property *prop, void *ctx)
+{
+	char *ref = (char *)ctx;
+	char tmp[6] = { 0 };
+
+	snprintf(tmp, ARRAY_SIZE(tmp), "%d", prop->version);
+	strcat(ref, prop->property_name);
+	strcat(ref, "=");
+	strcat(ref, tmp);
+	strcat(ref, "\n");
+
+	return 0;
+}
+
+/**
+ * create_new_prop_cfg: create a new property configuration string for consumers
+ *			of IPE policy.
+ *
+ * This function will iterate over all currently registered properties, and
+ * return a string of form:
+ *
+ *	property1=version1\n
+ *	property2=version2\n
+ *	...
+ *	propertyN=versionN
+ *
+ * Where propertyX is the property_name and versionX is the version associated.
+ *
+ * Return:
+ * !ERR_PTR - Success
+ * ERR_PTR(-ENOMEM) - Allocation Failed
+ */
+static char *create_new_prop_cfg(void)
+{
+	size_t i;
+	ssize_t rc = 0;
+	size_t alloc = 0;
+	char *ret = NULL;
+	const char *const built_ins[] = {
+		IPE_PROPERTY_OPERATION_VER,
+		IPE_PROPERTY_ACTION_VER,
+		IPE_PROPERTY_DEFAULT_VER,
+		IPE_HEADER_POLICY_NAME_VER,
+		IPE_HEADER_POLICY_VERSION_VER
+	};
+
+	for (i = 0; i < ARRAY_SIZE(built_ins); ++i)
+		alloc += strlen(built_ins[i]) + 1; /* \n */
+
+	(void)ipe_for_each_prop(alloc_size_cb, (void *)&alloc);
+	++alloc; /* null for strcat */
+
+	ret = kzalloc(alloc, GFP_KERNEL);
+	if (!ret)
+		return ERR_PTR(-ENOMEM);
+
+	for (i = 0; i < ARRAY_SIZE(built_ins); ++i) {
+		strcat(ret, built_ins[i]);
+		strcat(ret,  "\n");
+	}
+
+	rc = ipe_for_each_prop(build_cfg_str, (void *)ret);
+	if (rc)
+		goto err;
+
+	return ret;
+err:
+	kfree(ret);
+	return ERR_PTR(rc);
+}
+
+/**
+ * ipe_get_prop_cfg: Get (or allocate if one does not exist) the property
+ *		     configuration string for IPE.
+ *
+ * @f: File representing the securityfs entry.
+ * @data: User mode buffer to place the configuration string.
+ * @len: Length of @data.
+ * @offset: Offset into @data.
+ *
+ * As this string can only change on a new kernel build, this string
+ * is cached in the i_private field of @f's inode for subsequent calls.
+ *
+ * Return:
+ * < 0 - Error
+ * > 0 - Success, bytes written to @data
+ */
+static ssize_t ipe_get_prop_cfg(struct file *f, char __user *data, size_t size,
+				loff_t *offset)
+{
+	ssize_t rc = 0;
+	const char *cfg = NULL;
+	struct inode *grammar = d_inode(property_cfg_node);
+
+	inode_lock(grammar);
+
+	/*
+	 * This can only change with a new kernel build,
+	 * so cache the result in i->private
+	 */
+	if (IS_ERR_OR_NULL(grammar->i_private)) {
+		grammar->i_private = create_new_prop_cfg();
+		if (IS_ERR(grammar->i_private)) {
+			rc = PTR_ERR(grammar->i_private);
+			goto out;
+		}
+	}
+	cfg = (const char *)grammar->i_private;
+
+	rc = simple_read_from_buffer(data, size, offset, cfg, strlen(cfg));
+
+out:
+	inode_unlock(grammar);
+	return rc;
+}
+
+static const struct file_operations prop_cfg_ops = {
+	.read = ipe_get_prop_cfg
+};
+
+/**
+ * ipe_free_policy_node: Free an ipe_policy_node structure allocated by
+ *			 ipe_alloc_policy_node.
+ * @n: ipe_policy_node to free
+ */
+static void ipe_free_policy_node(struct ipe_policy_node *n)
+{
+	if (IS_ERR_OR_NULL(n))
+		return;
+
+	ipe_free_policy(n->parsed);
+	kfree(n->data);
+
+	kfree(n);
+}
+
+/**
+ * alloc_callback: Callback given to verify_pkcs7_signature function to set
+ *		   the inner content reference and parse the policy.
+ * @ctx: "ipe_policy_node" to set inner content, size and parsed policy of.
+ * @data: Start of PKCS#7 inner content.
+ * @len: Length of @data.
+ * @asn1hdrlen: Unused.
+ *
+ * Return:
+ * 0 - OK
+ * ERR_PTR(-EBADMSG) - Invalid policy syntax
+ * ERR_PTR(-ENOMEM) - Out of memory
+ */
+static int alloc_callback(void *ctx, const void *data, size_t len,
+			  size_t asn1hdrlen)
+{
+	char *cpy = NULL;
+	struct ipe_policy *pol = NULL;
+	struct ipe_policy_node *n = (struct ipe_policy_node *)ctx;
+
+	n->content = (const u8 *)data;
+	n->content_size = len;
+
+	if (len == 0)
+		return -EBADMSG;
+
+	cpy = kzalloc(len + 1, GFP_KERNEL);
+	if (!cpy)
+		return -ENOMEM;
+
+	(void)memcpy(cpy, data, len);
+
+	pol = ipe_parse_policy(cpy);
+	if (IS_ERR(pol)) {
+		kfree(cpy);
+		return PTR_ERR(pol);
+	}
+
+	n->parsed = pol;
+	kfree(cpy);
+	return 0;
+}
+
+/**
+ * ipe_delete_policy_tree - delete the policy subtree under
+ *			    $securityfs/ipe/policies.
+ * @policy_root: the policy root directory, i.e.
+ *		 $securityfs/ipe/policies/$policy_name
+ *
+ * Return:
+ * 0 - OK
+ * -EPERM - Tree being deleted is the active policy
+ * -ENOENT - A subnode is missing under the tree.
+ * Other - see lookup_one_len_unlocked.
+ */
+static int ipe_delete_policy_tree(struct dentry *policy_root)
+{
+	int rc = 0;
+	struct dentry *raw = NULL;
+	struct dentry *active = NULL;
+	struct dentry *content = NULL;
+	struct dentry *delete = NULL;
+	const struct ipe_policy_node *target = NULL;
+
+	/* ensure the active policy cannot be changed */
+	lockdep_assert_held(&ipe_policy_lock);
+
+	/* fail if it's the active policy */
+	target = (const struct ipe_policy_node *)d_inode(policy_root)->i_private;
+	if (ipe_is_active_policy(target->parsed)) {
+		rc = -EPERM;
+		goto out;
+	}
+
+	raw = retrieve_backed_dentry(IPE_FULL_CONTENT, policy_root,
+				     strlen(IPE_FULL_CONTENT));
+	if (IS_ERR_OR_NULL(raw)) {
+		rc = IS_ERR(raw) ? PTR_ERR(raw) : -ENOENT;
+		goto out;
+	}
+
+	content = retrieve_backed_dentry(IPE_INNER_CONTENT, policy_root,
+					 strlen(IPE_INNER_CONTENT));
+	if (IS_ERR_OR_NULL(content)) {
+		rc = IS_ERR(content) ? PTR_ERR(content) : -ENOENT;
+		goto out_free_raw;
+	}
+
+	active = retrieve_backed_dentry(IPE_ACTIVE_POLICY, policy_root,
+					strlen(IPE_ACTIVE_POLICY));
+	if (IS_ERR_OR_NULL(active)) {
+		rc = IS_ERR(active) ? PTR_ERR(active) : -ENOENT;
+		goto out_free_content;
+	}
+
+	delete = retrieve_backed_dentry(IPE_DELETE_POLICY, policy_root,
+					strlen(IPE_DELETE_POLICY));
+	if (IS_ERR_OR_NULL(active)) {
+		rc = IS_ERR(active) ? PTR_ERR(active) : -ENOENT;
+		goto out_free_active;
+	}
+
+	inode_lock(d_inode(policy_root));
+	ipe_free_policy_node(d_inode(policy_root)->i_private);
+	d_inode(policy_root)->i_private = NULL;
+	inode_unlock(d_inode(policy_root));
+
+	/* drop references from acquired in this function */
+	dput(raw);
+	dput(content);
+	dput(policy_root);
+	dput(active);
+	dput(delete);
+
+	/* drop securityfs' references */
+	securityfs_remove(raw);
+	securityfs_remove(content);
+	securityfs_remove(policy_root);
+	securityfs_remove(active);
+	securityfs_remove(delete);
+
+	return rc;
+
+out_free_active:
+	dput(active);
+out_free_content:
+	dput(content);
+out_free_raw:
+	dput(raw);
+out:
+	return rc;
+}
+
+/**
+ * ipe_delete_policy: Delete a policy, which is stored in this file's parent
+ *		      dentry's inode.
+ * @f: File representing the securityfs entry.
+ * @data: Buffer containing the value 1.
+ * @len: sizeof(u8).
+ * @offset: Offset into @data.
+ *
+ * Return:
+ * > 0 - OK
+ * -ENOMEM - Out of memory
+ * -EINVAL - Incorrect parameter
+ * -EPERM - Policy is active
+ * -ENOENT - A policy subnode does not exist
+ * -EPERM - if a MAC subsystem is enabled, missing CAP_MAC_ADMIN
+ * Other - See retrieve_backed_dentry
+ */
+static ssize_t ipe_delete_policy(struct file *f, const char __user *data,
+				 size_t len, loff_t *offset)
+{
+	int v = 0;
+	ssize_t rc = 0;
+	struct inode *policy_i = NULL;
+	struct dentry *policy_root = NULL;
+
+	if (!file_ns_capable(f, &init_user_ns, CAP_MAC_ADMIN))
+		return -EPERM;
+
+	rc = get_int_user(data, len, offset, &v);
+	if (rc)
+		return rc;
+
+	if (v != 1)
+		return -EINVAL;
+
+	policy_root = f->f_path.dentry->d_parent;
+	policy_i = d_inode(policy_root);
+
+	if (!policy_i->i_private)
+		return -ENOENT;
+
+	/* guarantee active policy cannot change */
+	mutex_lock(&ipe_policy_lock);
+
+	rc = ipe_delete_policy_tree(policy_root);
+	if (rc)
+		goto out_unlock;
+
+	mutex_unlock(&ipe_policy_lock);
+	synchronize_rcu();
+
+	return len;
+
+out_unlock:
+	mutex_unlock(&ipe_policy_lock);
+	return rc;
+}
+
+static const struct file_operations policy_delete_ops = {
+	.write = ipe_delete_policy
+};
+
+/**
+ * ipe_alloc_policy_node: Allocate a new ipe_policy_node structure.
+ * @data: Raw enveloped PKCS#7 data that represents the policy.
+ * @len: Length of @data.
+ *
+ * Return:
+ * valid ipe_policy_node - OK
+ * ERR_PTR(-EBADMSG) - Invalid policy syntax
+ * ERR_PTR(-ENOMEM) - Out of memory
+ */
+static struct ipe_policy_node *ipe_alloc_policy_node(const u8 *data,
+						     size_t len)
+{
+	int rc = 0;
+	struct ipe_policy_node *node = NULL;
+
+	node = kzalloc(sizeof(*node), GFP_KERNEL);
+	if (!node) {
+		rc = -ENOMEM;
+		goto out;
+	}
+
+	node->data_len = len;
+	node->data = kmemdup(data, len, GFP_KERNEL);
+	if (!node->data) {
+		rc = -ENOMEM;
+		goto out2;
+	}
+
+	rc = verify_pkcs7_signature(node->content, node->content_size,
+				    node->data, node->data_len, NULL,
+				    VERIFYING_UNSPECIFIED_SIGNATURE,
+				    alloc_callback, node);
+	if (rc != 0)
+		goto out2;
+
+	return node;
+out2:
+	ipe_free_policy_node(node);
+out:
+	return ERR_PTR(rc);
+}
+
+/**
+ * ipe_read_policy: Read the raw content (full enveloped PKCS7) data of
+ *			the policy stored within the file's parent inode.
+ * @f: File representing the securityfs entry.
+ * @data: User mode buffer to place the raw pkcs7.
+ * @len: Length of @data.
+ * @offset: Offset into @data.
+ *
+ * Return:
+ * > 0 - OK
+ * -ENOMEM - Out of memory
+ */
+static ssize_t ipe_read_policy(struct file *f, char __user *data,
+			       size_t size, loff_t *offset)
+{
+	ssize_t rc = 0;
+	size_t avail = 0;
+	struct inode *root = NULL;
+	const struct ipe_policy_node *node = NULL;
+
+	root = d_inode(f->f_path.dentry->d_parent);
+
+	inode_lock_shared(root);
+	node = (const struct ipe_policy_node *)root->i_private;
+
+	avail = node->data_len;
+	rc = simple_read_from_buffer(data, size, offset, node->data, avail);
+
+	inode_unlock_shared(root);
+	return rc;
+}
+
+/**
+ * ipe_update_policy: Update a policy in place with a new PKCS7 policy.
+ * @f: File representing the securityfs entry.
+ * @data: Buffer user mode to place the raw pkcs7.
+ * @len: Length of @data.
+ * @offset: Offset into @data.
+ *
+ * Return:
+ * 0 - OK
+ * -EBADMSG - Invalid policy format
+ * -ENOMEM - Out of memory
+ * -EPERM - if a MAC subsystem is enabled, missing CAP_MAC_ADMIN
+ * -EINVAL - Incorrect policy name for this node, or version is < current
+ */
+static ssize_t ipe_update_policy(struct file *f, const char __user *data,
+				 size_t len, loff_t *offset)
+{
+	ssize_t rc = 0;
+	u8 *cpy = NULL;
+	struct inode *root = NULL;
+	struct crypto_shash *tfm = NULL;
+	struct ipe_policy_node *new = NULL;
+	struct ipe_policy_node *old = NULL;
+
+	if (!file_ns_capable(f, &init_user_ns, CAP_MAC_ADMIN))
+		return -EPERM;
+
+	cpy = memdup_user(data, len);
+	if (IS_ERR(cpy))
+		return PTR_ERR(cpy);
+
+	new = ipe_alloc_policy_node(cpy, len);
+	if (IS_ERR(new)) {
+		rc = PTR_ERR(new);
+		goto out_free_cpy;
+	}
+
+	tfm = crypto_alloc_shash("sha1", 0, 0);
+	if (IS_ERR(tfm))
+		goto out_free_cpy;
+
+	root = d_inode(f->f_path.dentry->d_parent);
+	inode_lock(root);
+	mutex_lock(&ipe_policy_lock);
+
+	old = (struct ipe_policy_node *)root->i_private;
+
+	if (strcmp(old->parsed->policy_name, new->parsed->policy_name)) {
+		rc = -EINVAL;
+		goto out_unlock_inode;
+	}
+
+	if (!ipe_is_valid_policy(old->parsed, new->parsed)) {
+		rc = -EINVAL;
+		goto out_unlock_inode;
+	}
+
+	rc = ipe_update_active_policy(old->parsed, new->parsed);
+	if (rc != 0)
+		goto out_unlock_inode;
+
+	ipe_audit_policy_load(new->parsed, new->data, new->data_len, tfm);
+	swap(root->i_private, new);
+
+	mutex_unlock(&ipe_policy_lock);
+	synchronize_rcu();
+
+	inode_unlock(root);
+	kfree(cpy);
+	ipe_free_policy_node(new);
+	crypto_free_shash(tfm);
+
+	return len;
+
+out_unlock_inode:
+	mutex_unlock(&ipe_policy_lock);
+	inode_unlock(root);
+	ipe_free_policy_node(new);
+	crypto_free_shash(tfm);
+out_free_cpy:
+	kfree(cpy);
+	return rc;
+}
+
+static const struct file_operations policy_raw_ops = {
+	.read = ipe_read_policy,
+	.write = ipe_update_policy
+};
+
+/**
+ * ipe_read_content: Read the inner content of the enveloped PKCS7 data,
+ *			 representing the IPE policy.
+ * @f: File representing the securityfs entry.
+ * @data: User mode buffer to place the inner content of the pkcs7 data.
+ * @len: Length of @data.
+ * @offset: Offset into @data.
+ *
+ * Return:
+ * > 0 - OK
+ * -ENOMEM - Out of memory
+ */
+static ssize_t ipe_read_content(struct file *f, char __user *data,
+				size_t size, loff_t *offset)
+{
+	ssize_t rc = 0;
+	size_t avail = 0;
+	struct inode *root = NULL;
+	const struct ipe_policy_node *node = NULL;
+
+	root = d_inode(f->f_path.dentry->d_parent);
+
+	inode_lock_shared(root);
+	node = (const struct ipe_policy_node *)root->i_private;
+
+	avail = node->content_size;
+	rc = simple_read_from_buffer(data, size, offset, node->content, avail);
+
+	inode_unlock_shared(root);
+	return rc;
+}
+
+static const struct file_operations policy_content_ops = {
+	.read = ipe_read_content
+};
+
+/**
+ * ipe_get_active - return a string representation of whether a policy
+ *		    is active.
+ * @f: File struct representing the securityfs node. Unused.
+ * @data: buffer to place the result.
+ * @len: length of @data.
+ * @offset: offset into @data.
+ *
+ * This is the 'read' syscall handler for
+ * $securityfs/ipe/policies/$policy_name/active
+ *
+ * Return:
+ * > 0 - OK
+ * < 0 - see simple_read_from_buffer.
+ */
+static ssize_t ipe_get_active(struct file *f, char __user *data, size_t len,
+			      loff_t *offset)
+{
+	ssize_t rc = 0;
+	char tmp[3] = { 0 };
+	struct inode *root = NULL;
+	const struct ipe_policy_node *node = NULL;
+
+	root = d_inode(f->f_path.dentry->d_parent);
+	inode_lock_shared(root);
+
+	node = (const struct ipe_policy_node *)root->i_private;
+
+	snprintf(tmp, ARRAY_SIZE(tmp), "%c\n",
+		 ipe_is_active_policy(node->parsed) ? '1' : '0');
+
+	rc = simple_read_from_buffer(data, len, offset, tmp,
+				     ARRAY_SIZE(tmp));
+
+	inode_unlock_shared(root);
+
+	return rc;
+}
+
+/**
+ * ipe_set_active - mark a policy as active, causing IPE to start enforcing
+ *		    this policy.
+ * @f: File struct representing the securityfs node.
+ * @data: buffer containing data written to the securityfs node..
+ * @len: length of @data.
+ * @offset: offset into @data.
+ *
+ * This is the 'write' syscall handler for
+ * $securityfs/ipe/policies/$policy_name/active
+ *
+ * Return:
+ * > 0 - OK
+ * -EINVAL - Value written is not "1".
+ * -EPERM - if MAC system is enabled, missing CAP_MAC_ADMIN.
+ * Other - see ipe_activate_policy, get_int_user
+ */
+static ssize_t ipe_set_active(struct file *f, const char __user *data, size_t len,
+			      loff_t *offset)
+{
+	int v = 0;
+	ssize_t rc = 0;
+	struct inode *root = NULL;
+	const struct ipe_policy_node *node = NULL;
+
+	if (!file_ns_capable(f, &init_user_ns, CAP_MAC_ADMIN))
+		return -EPERM;
+
+	rc = get_int_user(data, len, offset, &v);
+	if (rc)
+		return rc;
+
+	if (v != 1)
+		return -EINVAL;
+
+	root = d_inode(f->f_path.dentry->d_parent);
+	mutex_lock(&ipe_policy_lock);
+	inode_lock_shared(root);
+
+	node = (const struct ipe_policy_node *)root->i_private;
+	rc = ipe_activate_policy(node->parsed);
+
+	inode_unlock_shared(root);
+	mutex_unlock(&ipe_policy_lock);
+	synchronize_rcu();
+
+	return len;
+}
+
+static const struct file_operations policy_active_ops = {
+	.read = ipe_get_active,
+	.write = ipe_set_active
+};
+
+/**
+ * ipe_alloc_policy_tree - allocate the proper subnodes for a policy under
+ *			   securityfs.
+ * @parent: The parent directory that these securityfs files should be created
+ *	    under.
+ *
+ * Return:
+ * 0 - OK
+ * !0 - See securityfs_create_file
+ */
+static int ipe_alloc_policy_tree(struct dentry *parent)
+{
+	int rc = 0;
+	struct dentry *raw = NULL;
+	struct dentry *delete = NULL;
+	struct dentry *active = NULL;
+	struct dentry *content = NULL;
+
+	raw = securityfs_create_file(IPE_FULL_CONTENT, 0644, parent, NULL,
+				     &policy_raw_ops);
+	if (IS_ERR(raw))
+		return PTR_ERR(raw);
+
+	content = securityfs_create_file(IPE_INNER_CONTENT, 0444, parent,
+					 NULL, &policy_content_ops);
+	if (IS_ERR(raw)) {
+		rc = PTR_ERR(raw);
+		goto free_raw;
+	}
+
+	active = securityfs_create_file(IPE_ACTIVE_POLICY, 0644, parent, NULL,
+					&policy_active_ops);
+	if (IS_ERR(active)) {
+		rc = PTR_ERR(active);
+		goto free_content;
+	}
+
+	delete = securityfs_create_file(IPE_DELETE_POLICY, 0644, parent, NULL,
+					&policy_delete_ops);
+	if (IS_ERR(delete)) {
+		rc = PTR_ERR(delete);
+		goto free_active;
+	}
+
+	return rc;
+
+free_active:
+	securityfs_remove(active);
+free_content:
+	securityfs_remove(content);
+free_raw:
+	securityfs_remove(raw);
+
+	return rc;
+}
+
+/**
+ * ipe_build_policy_node: Build a new securityfs node for IPE policies.
+ * @data: Raw enveloped PKCS#7 data that represents the policy.
+ * @len: Length of @data.
+ *
+ * Return:
+ * 0 - OK
+ * -EEXIST - Policy already exists
+ * -EBADMSG - Invalid policy syntax
+ * -ENOMEM - Out of memory
+ */
+static int ipe_build_policy_node(const u8 *data, size_t len)
+{
+	int rc = 0;
+	struct dentry *root = NULL;
+	struct inode *root_i = NULL;
+	struct crypto_shash *tfm = NULL;
+	struct ipe_policy_node *node = NULL;
+
+	tfm = crypto_alloc_shash("sha1", 0, 0);
+	if (IS_ERR(tfm)) {
+		rc = PTR_ERR(tfm);
+		goto out;
+	}
+
+	node = ipe_alloc_policy_node(data, len);
+	if (IS_ERR(node)) {
+		rc = PTR_ERR(node);
+		goto free_hash;
+	}
+
+	root = securityfs_create_dir(node->parsed->policy_name,
+				     policies_root);
+	if (IS_ERR(root)) {
+		rc = PTR_ERR(root);
+		goto free_private;
+	}
+
+	root_i = d_inode(root);
+
+	inode_lock(root_i);
+	root_i->i_private = node;
+	ipe_audit_policy_load(node->parsed, node->data, node->data_len, tfm);
+	inode_unlock(root_i);
+
+	rc = ipe_alloc_policy_tree(root);
+	if (rc)
+		goto free_secfs;
+
+	crypto_free_shash(tfm);
+	return rc;
+
+free_secfs:
+	securityfs_remove(root);
+free_private:
+	ipe_free_policy_node(node);
+free_hash:
+	crypto_free_shash(tfm);
+out:
+	return rc;
+}
+
+/**
+ * ipe_new_policy: Entry point of the securityfs node, "ipe/new_policy".
+ * @f: File representing the securityfs entry.
+ * @data: Raw enveloped PKCS#7 data that represents the policy.
+ * @len: Length of @data.
+ * @offset: Offset for @data.
+ *
+ * Return:
+ * > 0 - OK
+ * -EEXIST - Policy already exists
+ * -EBADMSG - Invalid policy syntax
+ * -ENOMEM - Out of memory
+ * -EPERM - if a MAC subsystem is enabled, missing CAP_MAC_ADMIN
+ */
+static ssize_t ipe_new_policy(struct file *f, const char __user *data,
+			      size_t len, loff_t *offset)
+{
+	ssize_t rc = 0;
+	u8 *cpy = NULL;
+
+	if (!file_ns_capable(f, &init_user_ns, CAP_MAC_ADMIN))
+		return -EPERM;
+
+	cpy = memdup_user(data, len);
+	if (IS_ERR(cpy))
+		return PTR_ERR(cpy);
+
+	rc = ipe_build_policy_node(cpy, len);
+
+	kfree(cpy);
+	return rc < 0 ? rc : len;
+}
+
+static const struct file_operations new_policy_ops = {
+	.write = ipe_new_policy
+};
+
+/**
+ * ipe_build_secfs_root: Build the root of securityfs for IPE.
+ *
+ * Return:
+ * 0 - OK
+ * !0 - See securityfs_create_dir and securityfs_create_file
+ */
+static int __init ipe_build_secfs_root(void)
+{
+	int rc = 0;
+	struct dentry *new = NULL;
+	struct dentry *cfg = NULL;
+	struct dentry *root = NULL;
+	struct dentry *audit = NULL;
+	struct dentry *enforce = NULL;
+	struct dentry *policies = NULL;
+
+	root = securityfs_create_dir(IPE_ROOT, NULL);
+	if (IS_ERR(root)) {
+		rc = PTR_ERR(root);
+		goto out;
+	}
+
+	new = securityfs_create_file(NEW_POLICY, 0644, root, NULL,
+				     &new_policy_ops);
+	if (IS_ERR(new)) {
+		rc = PTR_ERR(new);
+		goto out_free_root;
+	}
+
+	policies = securityfs_create_dir(IPE_POLICIES, root);
+	if (IS_ERR(policies)) {
+		rc = PTR_ERR(policies);
+		goto out_free_new;
+	}
+
+	cfg = securityfs_create_file(IPE_PROPERTY_CFG, 0444, root, NULL,
+				     &prop_cfg_ops);
+	if (IS_ERR(cfg)) {
+		rc = PTR_ERR(cfg);
+		goto out_free_policies;
+	}
+
+	audit = securityfs_create_file(IPE_SUCCESS_AUDIT, 0644, root, NULL,
+				       &audit_ops);
+	if (IS_ERR(cfg)) {
+		rc = PTR_ERR(audit);
+		goto out_free_cfg;
+	}
+
+	enforce = ipe_init_enforce_node(root);
+	if (IS_ERR(enforce)) {
+		rc = PTR_ERR(audit);
+		goto out_free_audit;
+	}
+
+	securityfs_root = root;
+	new_policy_node = new;
+	policies_root = policies;
+	property_cfg_node = cfg;
+	success_audit_node = audit;
+	enforce_node = enforce;
+
+	return rc;
+
+out_free_audit:
+	securityfs_remove(audit);
+out_free_cfg:
+	securityfs_remove(cfg);
+out_free_policies:
+	securityfs_remove(policies);
+out_free_new:
+	securityfs_remove(new);
+out_free_root:
+	securityfs_remove(root);
+out:
+	return rc;
+}
+
+/**
+ * ipe_build_boot_node: Build a policy node for IPE's boot policy.
+ *
+ * This differs from the normal policy nodes, as the IPE boot policy is
+ * read only, and only has the 'content' and 'active' nodes (as it is
+ * unsigned).
+ *
+ * Return:
+ * 0 - OK
+ * !0 - See securityfs_create_dir and securityfs_create_file
+ */
+static int __init ipe_build_boot_node(void)
+{
+	int rc = 0;
+	char *cpy = NULL;
+	struct inode *root_i = NULL;
+	struct dentry *root = NULL;
+	struct dentry *active = NULL;
+	struct dentry *content = NULL;
+	struct ipe_policy *parsed = NULL;
+	struct ipe_policy_node *node = NULL;
+
+	if (!ipe_boot_policy)
+		return 0;
+
+	node = kzalloc(sizeof(*node), GFP_KERNEL);
+	if (!node) {
+		rc = -ENOMEM;
+		goto out;
+	}
+
+	cpy = kstrdup(ipe_boot_policy, GFP_KERNEL);
+	if (!cpy) {
+		rc = -ENOMEM;
+		goto out;
+	}
+
+	parsed = ipe_parse_policy(cpy);
+	if (IS_ERR(parsed)) {
+		rc = PTR_ERR(parsed);
+		goto out_free_policy;
+	}
+
+	node->content = ipe_boot_policy;
+	node->content_size = strlen(ipe_boot_policy);
+	node->parsed = parsed;
+
+	root = securityfs_create_dir(node->parsed->policy_name,
+				     policies_root);
+	if (IS_ERR(root)) {
+		rc = PTR_ERR(root);
+		goto out_free_policy;
+	}
+
+	content = securityfs_create_file(IPE_INNER_CONTENT, 0444, root, NULL,
+					 &policy_content_ops);
+	if (IS_ERR(content)) {
+		rc = PTR_ERR(content);
+		goto out_free_root;
+	}
+
+	active = securityfs_create_file(IPE_ACTIVE_POLICY, 0644, root, NULL,
+					&policy_active_ops);
+	if (IS_ERR(active)) {
+		rc = PTR_ERR(active);
+		goto out_free_content;
+	}
+
+	root_i = d_inode(root);
+
+	inode_lock(root_i);
+	root_i->i_private = node;
+	inode_unlock(root_i);
+
+	boot_policy_node = root;
+	mutex_lock(&ipe_policy_lock);
+	rc = ipe_activate_policy(node->parsed);
+	mutex_unlock(&ipe_policy_lock);
+	synchronize_rcu();
+
+	return rc;
+
+out_free_content:
+	securityfs_remove(active);
+out_free_root:
+	securityfs_remove(root);
+out_free_policy:
+	ipe_free_policy(parsed);
+out:
+	kfree(cpy);
+	kfree(node);
+	return rc;
+}
+
+/**
+ * ipe_securityfs_init: Initialize IPE's securityfs entries.
+ *
+ * This is called after the lsm initialization.
+ *
+ * Return:
+ * 0 - OK
+ * !0 - Error
+ */
+static int __init ipe_securityfs_init(void)
+{
+	int rc = 0;
+
+	rc = ipe_build_secfs_root();
+	if (rc != 0)
+		goto err;
+
+	rc = ipe_build_boot_node();
+	if (rc != 0)
+		panic("IPE failed to initialize the boot policy: %d", rc);
+
+	return rc;
+err:
+	pr_err("failed to initialize secfs: %d", -rc);
+	return rc;
+}
+
+core_initcall(ipe_securityfs_init);
diff --git a/security/ipe/ipe-secfs.h b/security/ipe/ipe-secfs.h
new file mode 100644
index 000000000000..7732a8dcf11f
--- /dev/null
+++ b/security/ipe/ipe-secfs.h
@@ -0,0 +1,14 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) Microsoft Corporation. All rights reserved.
+ */
+#include <linux/types.h>
+
+#include "ipe-policy.h"
+
+#ifndef IPE_SECFS_H
+#define IPE_SECFS_H
+
+extern struct mutex ipe_policy_lock;
+
+#endif /* IPE_SECFS_H */
-- 
2.27.0
^ permalink raw reply related	[flat|nested] 12+ messages in thread
- * [RFC PATCH v6 04/11] ipe: add property for trust of boot volume
  2020-07-30  0:31 [RFC PATCH v6 00/11] Integrity Policy Enforcement LSM (IPE) Deven Bowers
                   ` (2 preceding siblings ...)
  2020-07-30  0:31 ` [RFC PATCH v6 03/11] security: add ipe lsm policy parser and policy loading Deven Bowers
@ 2020-07-30  0:31 ` Deven Bowers
  2020-07-30  0:31 ` [RFC PATCH v6 05/11] fs: add security blob and hooks for block_device Deven Bowers
                   ` (6 subsequent siblings)
  10 siblings, 0 replies; 12+ messages in thread
From: Deven Bowers @ 2020-07-30  0:31 UTC (permalink / raw)
  To: agk, axboe, snitzer, jmorris, serge, zohar, viro, paul, eparis,
	jannh, dm-devel, linux-integrity, linux-security-module,
	linux-fsdevel, linux-block, linux-audit
  Cc: tyhicks, linux-kernel, corbet, sashal, jaskarankhurana, mdsakib,
	nramas, pasha.tatashin
Add a property for IPE policy to express trust of the first superblock
where a file would be evaluated to determine trust.
Signed-off-by: Deven Bowers <deven.desai@linux.microsoft.com>
---
 security/ipe/Kconfig                    |  2 +
 security/ipe/Makefile                   |  4 ++
 security/ipe/ipe-engine.c               |  4 ++
 security/ipe/ipe-hooks.c                | 19 +++++
 security/ipe/ipe-hooks.h                |  2 +
 security/ipe/ipe-pin.c                  | 93 +++++++++++++++++++++++++
 security/ipe/ipe-pin.h                  | 36 ++++++++++
 security/ipe/ipe.c                      | 28 +++++++-
 security/ipe/properties/Kconfig         | 15 ++++
 security/ipe/properties/Makefile        | 11 +++
 security/ipe/properties/boot-verified.c | 82 ++++++++++++++++++++++
 security/ipe/properties/prop-entry.h    | 20 ++++++
 security/ipe/utility.h                  | 22 ++++++
 13 files changed, 337 insertions(+), 1 deletion(-)
 create mode 100644 security/ipe/ipe-pin.c
 create mode 100644 security/ipe/ipe-pin.h
 create mode 100644 security/ipe/properties/Kconfig
 create mode 100644 security/ipe/properties/Makefile
 create mode 100644 security/ipe/properties/boot-verified.c
 create mode 100644 security/ipe/properties/prop-entry.h
 create mode 100644 security/ipe/utility.h
diff --git a/security/ipe/Kconfig b/security/ipe/Kconfig
index 665524fc3ca4..469ef78c2f4f 100644
--- a/security/ipe/Kconfig
+++ b/security/ipe/Kconfig
@@ -43,4 +43,6 @@ config SECURITY_IPE_PERMISSIVE_SWITCH
 
 	  If unsure, answer Y.
 
+source "security/ipe/properties/Kconfig"
+
 endif
diff --git a/security/ipe/Makefile b/security/ipe/Makefile
index 7d6da33dd0c4..7e98982c5035 100644
--- a/security/ipe/Makefile
+++ b/security/ipe/Makefile
@@ -26,3 +26,7 @@ obj-$(CONFIG_SECURITY_IPE) += \
 	ipe-secfs.o \
 
 clean-files := ipe-bp.c
+
+obj-$(CONFIG_IPE_BOOT_PROP) += ipe-pin.o
+
+obj-$(CONFIG_SECURITY_IPE) += properties/
diff --git a/security/ipe/ipe-engine.c b/security/ipe/ipe-engine.c
index ac526d4ea5e6..0291ced99d64 100644
--- a/security/ipe/ipe-engine.c
+++ b/security/ipe/ipe-engine.c
@@ -9,6 +9,8 @@
 #include "ipe-policy.h"
 #include "ipe-engine.h"
 #include "ipe-audit.h"
+#include "ipe-pin.h"
+#include "utility.h"
 
 #include <linux/types.h>
 #include <linux/fs.h>
@@ -197,6 +199,8 @@ int ipe_process_event(const struct file *file, enum ipe_op op,
 	if (IS_ERR(ctx))
 		goto cleanup;
 
+	ipe_pin_superblock(ctx->file);
+
 	rc = evaluate(ctx);
 
 cleanup:
diff --git a/security/ipe/ipe-hooks.c b/security/ipe/ipe-hooks.c
index 071c4af23a3d..45efe022be04 100644
--- a/security/ipe/ipe-hooks.c
+++ b/security/ipe/ipe-hooks.c
@@ -6,6 +6,7 @@
 #include "ipe.h"
 #include "ipe-hooks.h"
 #include "ipe-engine.h"
+#include "ipe-pin.h"
 
 #include <linux/types.h>
 #include <linux/fs.h>
@@ -147,3 +148,21 @@ int ipe_on_kernel_load_data(enum kernel_load_data_id id)
 					 ipe_hook_kernel_load);
 	}
 }
+
+/**
+ * ipe_sb_free_security: LSM hook called on sb_free_security.
+ * @mnt_sb: Super block that is being freed.
+ *
+ * IPE does not currently utilize the super block security hook,
+ * it utilizes this hook to invalidate the saved super block for
+ * the boot_verified property.
+ *
+ * For more information, see the LSM hook, sb_free_security.
+ *
+ * Return:
+ * 0 - OK
+ */
+void ipe_sb_free_security(struct super_block *mnt_sb)
+{
+	ipe_invalidate_pinned_sb(mnt_sb);
+}
diff --git a/security/ipe/ipe-hooks.h b/security/ipe/ipe-hooks.h
index 806659b7cdbe..5e46726f2562 100644
--- a/security/ipe/ipe-hooks.h
+++ b/security/ipe/ipe-hooks.h
@@ -58,4 +58,6 @@ int ipe_on_kernel_read(struct file *file, enum kernel_read_file_id id);
 
 int ipe_on_kernel_load_data(enum kernel_load_data_id id);
 
+void ipe_sb_free_security(struct super_block *mnt_sb);
+
 #endif /* IPE_HOOK_H */
diff --git a/security/ipe/ipe-pin.c b/security/ipe/ipe-pin.c
new file mode 100644
index 000000000000..a963be8e5321
--- /dev/null
+++ b/security/ipe/ipe-pin.c
@@ -0,0 +1,93 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * This file has been heavily adapted from the source code of the
+ * loadpin LSM. The source code for loadpin is co-located in the linux
+ * tree under security/loadpin/loadpin.c.
+ *
+ * Please see loadpin.c for up-to-date information about
+ * loadpin.
+ */
+
+#include "ipe.h"
+
+#include <linux/types.h>
+#include <linux/spinlock_types.h>
+#include <linux/fs.h>
+#include <linux/mount.h>
+#include <linux/magic.h>
+#include <linux/mm.h>
+#include <linux/mman.h>
+
+static DEFINE_SPINLOCK(pinned_sb_spinlock);
+
+static struct super_block *pinned_sb;
+
+/**
+ * ipe_is_from_pinned_sb: Determine if @file originates from the initial
+ *			  super block that a file was executed from.
+ * @file: File to check if it originates from the super block.
+ *
+ * Return:
+ * true - File originates from the initial super block
+ * false - File does not originate from the initial super block
+ */
+bool ipe_is_from_pinned_sb(const struct file *file)
+{
+	bool rv = false;
+
+	spin_lock(&pinned_sb_spinlock);
+
+	/*
+	 * Check if pinned_sb is set:
+	 *  NULL == not set -> exit
+	 *  ERR == was once set (and has been unmounted) -> exit
+	 * AND check that the pinned sb is the same as the file's.
+	 */
+	if (!IS_ERR_OR_NULL(pinned_sb) &&
+	    file->f_path.mnt->mnt_sb == pinned_sb) {
+		rv = true;
+		goto cleanup;
+	}
+
+cleanup:
+	spin_unlock(&pinned_sb_spinlock);
+	return rv;
+}
+
+/**
+ * ipe_pin_superblock: Attempt to save a file's super block address to later
+ *		       determine if a file originates from a super block.
+ * @file: File to source the super block from.
+ */
+void ipe_pin_superblock(const struct file *file)
+{
+	spin_lock(&pinned_sb_spinlock);
+
+	/* if set, return */
+	if (pinned_sb || !file)
+		goto cleanup;
+
+	pinned_sb = file->f_path.mnt->mnt_sb;
+cleanup:
+	spin_unlock(&pinned_sb_spinlock);
+}
+
+/**
+ * ipe_invalidate_pinned_sb: Invalidate the saved super block.
+ * @mnt_sb: Super block to compare against the saved super block.
+ *
+ * This avoids authorizing a file when the super block does not exist anymore.
+ */
+void ipe_invalidate_pinned_sb(const struct super_block *mnt_sb)
+{
+	spin_lock(&pinned_sb_spinlock);
+
+	/*
+	 * On pinned sb unload - invalidate the pinned address
+	 * by setting the pinned_sb to ERR_PTR(-EIO)
+	 */
+	if (!IS_ERR_OR_NULL(pinned_sb) && mnt_sb == pinned_sb)
+		pinned_sb = ERR_PTR(-EIO);
+
+	spin_unlock(&pinned_sb_spinlock);
+}
diff --git a/security/ipe/ipe-pin.h b/security/ipe/ipe-pin.h
new file mode 100644
index 000000000000..b707e6253c33
--- /dev/null
+++ b/security/ipe/ipe-pin.h
@@ -0,0 +1,36 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) Microsoft Corporation. All rights reserved.
+ */
+#ifndef IPE_PIN_H
+#define IPE_PIN_H
+
+#include <linux/types.h>
+#include <linux/fs.h>
+
+#ifdef CONFIG_IPE_BOOT_PROP
+
+bool ipe_is_from_pinned_sb(const struct file *file);
+
+void ipe_pin_superblock(const struct file *file);
+
+void ipe_invalidate_pinned_sb(const struct super_block *mnt_sb);
+
+#else /* CONFIG_IPE_BOOT_PROP */
+
+static inline bool ipe_is_from_pinned_sb(const struct file *file)
+{
+	return false;
+}
+
+static inline void ipe_pin_superblock(const struct file *file)
+{
+}
+
+static inline void ipe_invalidate_pinned_sb(const struct super_block *mnt_sb)
+{
+}
+
+#endif /* !CONFIG_IPE_BOOT_PROP */
+
+#endif /* IPE_PIN_H */
diff --git a/security/ipe/ipe.c b/security/ipe/ipe.c
index 6e3b9a10813c..706ff38083c6 100644
--- a/security/ipe/ipe.c
+++ b/security/ipe/ipe.c
@@ -6,6 +6,7 @@
 #include "ipe.h"
 #include "ipe-policy.h"
 #include "ipe-hooks.h"
+#include "properties/prop-entry.h"
 
 #include <linux/module.h>
 #include <linux/lsm_hooks.h>
@@ -21,8 +22,27 @@ static struct security_hook_list ipe_hooks[] __lsm_ro_after_init = {
 	LSM_HOOK_INIT(kernel_read_file, ipe_on_kernel_read),
 	LSM_HOOK_INIT(kernel_load_data, ipe_on_kernel_load_data),
 	LSM_HOOK_INIT(file_mprotect, ipe_on_mprotect),
+	LSM_HOOK_INIT(sb_free_security, ipe_sb_free_security),
 };
 
+/**
+ * ipe_load_properties: Call the property entry points for all the IPE modules
+ *			that were selected at kernel build-time.
+ *
+ * Return:
+ * 0 - OK
+ */
+static int __init ipe_load_properties(void)
+{
+	int rc = 0;
+
+	rc = ipe_init_bootv();
+	if (rc != 0)
+		return rc;
+
+	return rc;
+}
+
 /**
  * ipe_init: Entry point of IPE.
  *
@@ -38,12 +58,18 @@ static struct security_hook_list ipe_hooks[] __lsm_ro_after_init = {
  */
 static int __init ipe_init(void)
 {
+	int rc;
+
+	rc = ipe_load_properties();
+	if (rc != 0)
+		panic("IPE: properties failed to load");
+
 	pr_info("mode=%s", (ipe_enforce == 1) ? IPE_MODE_ENFORCE :
 						IPE_MODE_PERMISSIVE);
 
 	security_add_hooks(ipe_hooks, ARRAY_SIZE(ipe_hooks), "IPE");
 
-	return 0;
+	return rc;
 }
 
 DEFINE_LSM(ipe) = {
diff --git a/security/ipe/properties/Kconfig b/security/ipe/properties/Kconfig
new file mode 100644
index 000000000000..75c6c6ff6cd8
--- /dev/null
+++ b/security/ipe/properties/Kconfig
@@ -0,0 +1,15 @@
+# SPDX-License-Identifier: GPL-2.0-only
+#
+# Integrity Policy Enforcement (IPE) configuration
+#
+
+config IPE_BOOT_PROP
+	bool "Enable trust for boot volume"
+	help
+	  This option enables the property "boot_verified" in IPE policy.
+	  This property 'pins' the initial superblock when something is
+	  evaluated as an execution. This property will evaluate to true
+	  when the file being evaluated originates from the initial
+	  superblock.
+
+	  if unsure, answer N.
diff --git a/security/ipe/properties/Makefile b/security/ipe/properties/Makefile
new file mode 100644
index 000000000000..e3e7fe17cf58
--- /dev/null
+++ b/security/ipe/properties/Makefile
@@ -0,0 +1,11 @@
+# SPDX-License-Identifier: GPL-2.0
+#
+# Copyright (C) Microsoft Corporation. All rights reserved.
+#
+# Makefile for building the properties that IPE uses
+# as part of the kernel tree.
+#
+
+obj-$(CONFIG_SECURITY_IPE) += properties.o
+
+properties-$(CONFIG_IPE_BOOT_PROP) += boot-verified.o
diff --git a/security/ipe/properties/boot-verified.c b/security/ipe/properties/boot-verified.c
new file mode 100644
index 000000000000..eb9e6ebe34fa
--- /dev/null
+++ b/security/ipe/properties/boot-verified.c
@@ -0,0 +1,82 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) Microsoft Corporation. All rights reserved.
+ */
+
+#include "../ipe.h"
+#include "../ipe-pin.h"
+#include "../ipe-property.h"
+#include "../utility.h"
+
+#include <linux/types.h>
+#include <linux/slab.h>
+#include <linux/fs.h>
+#include <linux/mount.h>
+#include <linux/audit.h>
+
+#define PROPERTY_NAME "boot_verified"
+
+static void audit(struct audit_buffer *ab, bool value)
+{
+	audit_log_format(ab, "%s", (value) ? "TRUE" : "FALSE");
+}
+
+static inline void audit_rule_value(struct audit_buffer *ab,
+				    const void *value)
+{
+	audit(ab, (bool)value);
+}
+
+static inline void audit_ctx(struct audit_buffer *ab,
+			     const struct ipe_engine_ctx *ctx)
+{
+	bool b = has_sb(ctx->file) && ipe_is_from_pinned_sb(ctx->file);
+
+	audit(ab, b);
+}
+
+static bool evaluate(const struct ipe_engine_ctx *ctx,
+		     const void *value)
+{
+	bool expect = (bool)value;
+
+	if (!ctx->file || !has_sb(ctx->file))
+		return false;
+
+	return ipe_is_from_pinned_sb(ctx->file) == expect;
+}
+
+static int parse(const char *val_str, void **value)
+{
+	if (strcmp("TRUE", val_str) == 0)
+		*value = (void *)true;
+	else if (strcmp("FALSE", val_str) == 0)
+		*value = (void *)false;
+	else
+		return -EBADMSG;
+
+	return 0;
+}
+
+static inline int duplicate(const void *src, void **dest)
+{
+	*dest = (void *)(bool)src;
+
+	return 0;
+}
+
+static const struct ipe_property boot_verified = {
+	.property_name = PROPERTY_NAME,
+	.version = 1,
+	.eval = evaluate,
+	.rule_audit = audit_rule_value,
+	.ctx_audit = audit_ctx,
+	.parse = parse,
+	.dup = duplicate,
+	.free_val = NULL,
+};
+
+int ipe_init_bootv(void)
+{
+	return ipe_register_property(&boot_verified);
+}
diff --git a/security/ipe/properties/prop-entry.h b/security/ipe/properties/prop-entry.h
new file mode 100644
index 000000000000..f598dd9608b9
--- /dev/null
+++ b/security/ipe/properties/prop-entry.h
@@ -0,0 +1,20 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) Microsoft Corporation. All rights reserved.
+ */
+
+#include <linux/types.h>
+
+#ifndef IPE_PROP_ENTRY_H
+#define IPE_PROP_ENTRY_H
+
+#ifndef CONFIG_IPE_BOOT_PROP
+static inline int __init ipe_init_bootv(void)
+{
+	return 0;
+}
+#else
+int __init ipe_init_bootv(void);
+#endif /* CONFIG_IPE_BOOT_PROP */
+
+#endif /* IPE_PROP_ENTRY_H */
diff --git a/security/ipe/utility.h b/security/ipe/utility.h
new file mode 100644
index 000000000000..a13089bb0d8f
--- /dev/null
+++ b/security/ipe/utility.h
@@ -0,0 +1,22 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) Microsoft Corporation. All rights reserved.
+ */
+
+#ifndef IPE_UTILITY_H
+#define IPE_UTILITY_H
+
+#include <linux/types.h>
+#include <linux/fs.h>
+
+static inline bool has_mount(const struct file *file)
+{
+	return file && file->f_path.mnt;
+}
+
+static inline bool has_sb(const struct file *file)
+{
+	return has_mount(file) && file->f_path.mnt->mnt_sb;
+}
+
+#endif /* IPE_UTILITY_H */
-- 
2.27.0
^ permalink raw reply related	[flat|nested] 12+ messages in thread
- * [RFC PATCH v6 05/11] fs: add security blob and hooks for block_device
  2020-07-30  0:31 [RFC PATCH v6 00/11] Integrity Policy Enforcement LSM (IPE) Deven Bowers
                   ` (3 preceding siblings ...)
  2020-07-30  0:31 ` [RFC PATCH v6 04/11] ipe: add property for trust of boot volume Deven Bowers
@ 2020-07-30  0:31 ` Deven Bowers
  2020-07-30  0:31 ` [RFC PATCH v6 06/11] dm-verity: add bdev_setsecurity hook for dm-verity signature Deven Bowers
                   ` (5 subsequent siblings)
  10 siblings, 0 replies; 12+ messages in thread
From: Deven Bowers @ 2020-07-30  0:31 UTC (permalink / raw)
  To: agk, axboe, snitzer, jmorris, serge, zohar, viro, paul, eparis,
	jannh, dm-devel, linux-integrity, linux-security-module,
	linux-fsdevel, linux-block, linux-audit
  Cc: tyhicks, linux-kernel, corbet, sashal, jaskarankhurana, mdsakib,
	nramas, pasha.tatashin
Add a security blob and associated allocation, deallocation and set hooks
for a block_device structure.
Signed-off-by: Deven Bowers <deven.desai@linux.microsoft.com>
---
 fs/block_dev.c                |  8 ++++
 include/linux/fs.h            |  1 +
 include/linux/lsm_hook_defs.h |  5 +++
 include/linux/lsm_hooks.h     | 12 ++++++
 include/linux/security.h      | 22 +++++++++++
 security/security.c           | 70 +++++++++++++++++++++++++++++++++++
 6 files changed, 118 insertions(+)
diff --git a/fs/block_dev.c b/fs/block_dev.c
index 0ae656e022fd..8602dd62c3e2 100644
--- a/fs/block_dev.c
+++ b/fs/block_dev.c
@@ -34,6 +34,7 @@
 #include <linux/falloc.h>
 #include <linux/uaccess.h>
 #include <linux/suspend.h>
+#include <linux/security.h>
 #include "internal.h"
 
 struct bdev_inode {
@@ -768,11 +769,18 @@ static struct inode *bdev_alloc_inode(struct super_block *sb)
 	struct bdev_inode *ei = kmem_cache_alloc(bdev_cachep, GFP_KERNEL);
 	if (!ei)
 		return NULL;
+
+	if (unlikely(security_bdev_alloc(&ei->bdev))) {
+		kmem_cache_free(bdev_cachep, ei);
+		return NULL;
+	}
+
 	return &ei->vfs_inode;
 }
 
 static void bdev_free_inode(struct inode *inode)
 {
+	security_bdev_free(&BDEV_I(inode)->bdev);
 	kmem_cache_free(bdev_cachep, BDEV_I(inode));
 }
 
diff --git a/include/linux/fs.h b/include/linux/fs.h
index f5abba86107d..42d7e3ce7712 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -509,6 +509,7 @@ struct block_device {
 	int			bd_fsfreeze_count;
 	/* Mutex for freeze */
 	struct mutex		bd_fsfreeze_mutex;
+	void			*security;
 } __randomize_layout;
 
 /* XArray tags, for tagging dirty and writeback pages in the pagecache. */
diff --git a/include/linux/lsm_hook_defs.h b/include/linux/lsm_hook_defs.h
index af998f93d256..f3c0da0db4e8 100644
--- a/include/linux/lsm_hook_defs.h
+++ b/include/linux/lsm_hook_defs.h
@@ -391,3 +391,8 @@ LSM_HOOK(void, LSM_RET_VOID, perf_event_free, struct perf_event *event)
 LSM_HOOK(int, 0, perf_event_read, struct perf_event *event)
 LSM_HOOK(int, 0, perf_event_write, struct perf_event *event)
 #endif /* CONFIG_PERF_EVENTS */
+
+LSM_HOOK(int, 0, bdev_alloc_security, struct block_device *bdev)
+LSM_HOOK(void, LSM_RET_VOID, bdev_free_security, struct block_device *bdev)
+LSM_HOOK(int, 0, bdev_setsecurity, struct block_device *bdev, const char *name,
+	 const void *value, size_t size)
diff --git a/include/linux/lsm_hooks.h b/include/linux/lsm_hooks.h
index 95b7c1d32062..8670c19a8cef 100644
--- a/include/linux/lsm_hooks.h
+++ b/include/linux/lsm_hooks.h
@@ -1507,6 +1507,17 @@
  *
  *     @what: kernel feature being accessed
  *
+ * @bdev_alloc_security:
+ *	Initialize the security field inside a block_device structure.
+ *
+ * @bdev_free_security:
+ *	Cleanup the security information stored inside a block_device structure.
+ *
+ * @bdev_setsecurity:
+ *	Set a security property associated with @name for @bdev with
+ *	value @value. @size indicates the size of @value in bytes.
+ *	If a @name is not implemented, return -ENOSYS.
+ *
  * Security hooks for perf events
  *
  * @perf_event_open:
@@ -1553,6 +1564,7 @@ struct lsm_blob_sizes {
 	int	lbs_ipc;
 	int	lbs_msg_msg;
 	int	lbs_task;
+	int	lbs_bdev;
 };
 
 /*
diff --git a/include/linux/security.h b/include/linux/security.h
index 0a0a03b36a3b..8f83fdc6c65d 100644
--- a/include/linux/security.h
+++ b/include/linux/security.h
@@ -451,6 +451,11 @@ int security_inode_notifysecctx(struct inode *inode, void *ctx, u32 ctxlen);
 int security_inode_setsecctx(struct dentry *dentry, void *ctx, u32 ctxlen);
 int security_inode_getsecctx(struct inode *inode, void **ctx, u32 *ctxlen);
 int security_locked_down(enum lockdown_reason what);
+int security_bdev_alloc(struct block_device *bdev);
+void security_bdev_free(struct block_device *bdev);
+int security_bdev_setsecurity(struct block_device *bdev,
+			      const char *name, const void *value,
+			      size_t size);
 #else /* CONFIG_SECURITY */
 
 static inline int call_blocking_lsm_notifier(enum lsm_event event, void *data)
@@ -1291,6 +1296,23 @@ static inline int security_locked_down(enum lockdown_reason what)
 {
 	return 0;
 }
+
+static inline int security_bdev_alloc(struct block_device *bdev)
+{
+	return 0;
+}
+
+static inline void security_bdev_free(struct block_device *bdev)
+{
+}
+
+static inline int security_bdev_setsecurity(struct block_device *bdev,
+					    const char *name,
+					    const void *value, size_t size)
+{
+	return 0;
+}
+
 #endif	/* CONFIG_SECURITY */
 
 #if defined(CONFIG_SECURITY) && defined(CONFIG_WATCH_QUEUE)
diff --git a/security/security.c b/security/security.c
index 70a7ad357bc6..de523b0d8f56 100644
--- a/security/security.c
+++ b/security/security.c
@@ -28,6 +28,7 @@
 #include <linux/string.h>
 #include <linux/msg.h>
 #include <net/flow.h>
+#include <linux/fs.h>
 
 #define MAX_LSM_EVM_XATTR	2
 
@@ -202,6 +203,7 @@ static void __init lsm_set_blob_sizes(struct lsm_blob_sizes *needed)
 	lsm_set_blob_size(&needed->lbs_ipc, &blob_sizes.lbs_ipc);
 	lsm_set_blob_size(&needed->lbs_msg_msg, &blob_sizes.lbs_msg_msg);
 	lsm_set_blob_size(&needed->lbs_task, &blob_sizes.lbs_task);
+	lsm_set_blob_size(&needed->lbs_bdev, &blob_sizes.lbs_bdev);
 }
 
 /* Prepare LSM for initialization. */
@@ -337,6 +339,7 @@ static void __init ordered_lsm_init(void)
 	init_debug("ipc blob size      = %d\n", blob_sizes.lbs_ipc);
 	init_debug("msg_msg blob size  = %d\n", blob_sizes.lbs_msg_msg);
 	init_debug("task blob size     = %d\n", blob_sizes.lbs_task);
+	init_debug("bdev blob size     = %d\n", blob_sizes.lbs_bdev);
 
 	/*
 	 * Create any kmem_caches needed for blobs
@@ -654,6 +657,28 @@ static int lsm_msg_msg_alloc(struct msg_msg *mp)
 	return 0;
 }
 
+/**
+ * lsm_bdev_alloc - allocate a composite block_device blob
+ * @bdev: the block_device that needs a blob
+ *
+ * Allocate the block_device blob for all the modules
+ *
+ * Returns 0, or -ENOMEM if memory can't be allocated.
+ */
+static int lsm_bdev_alloc(struct block_device *bdev)
+{
+	if (blob_sizes.lbs_bdev == 0) {
+		bdev->security = NULL;
+		return 0;
+	}
+
+	bdev->security = kzalloc(blob_sizes.lbs_bdev, GFP_KERNEL);
+	if (!bdev->security)
+		return -ENOMEM;
+
+	return 0;
+}
+
 /**
  * lsm_early_task - during initialization allocate a composite task blob
  * @task: the task that needs a blob
@@ -2516,6 +2541,51 @@ int security_locked_down(enum lockdown_reason what)
 }
 EXPORT_SYMBOL(security_locked_down);
 
+int security_bdev_alloc(struct block_device *bdev)
+{
+	int rc = 0;
+
+	rc = lsm_bdev_alloc(bdev);
+	if (unlikely(rc))
+		return rc;
+
+	rc = call_int_hook(bdev_alloc_security, 0, bdev);
+	if (unlikely(rc))
+		security_bdev_free(bdev);
+
+	return 0;
+}
+EXPORT_SYMBOL(security_bdev_alloc);
+
+void security_bdev_free(struct block_device *bdev)
+{
+	if (!bdev->security)
+		return;
+
+	call_void_hook(bdev_free_security, bdev);
+
+	kfree(bdev->security);
+	bdev->security = NULL;
+}
+EXPORT_SYMBOL(security_bdev_free);
+
+int security_bdev_setsecurity(struct block_device *bdev,
+			      const char *name, const void *value,
+			      size_t size)
+{
+	int rc = 0;
+	struct security_hook_list *p;
+
+	hlist_for_each_entry(p, &security_hook_heads.bdev_setsecurity, list) {
+		rc = p->hook.bdev_setsecurity(bdev, name, value, size);
+		if (rc && rc != -ENOSYS)
+			return rc;
+	}
+
+	return 0;
+}
+EXPORT_SYMBOL(security_bdev_setsecurity);
+
 #ifdef CONFIG_PERF_EVENTS
 int security_perf_event_open(struct perf_event_attr *attr, int type)
 {
-- 
2.27.0
^ permalink raw reply related	[flat|nested] 12+ messages in thread
- * [RFC PATCH v6 06/11] dm-verity: add bdev_setsecurity hook for dm-verity signature
  2020-07-30  0:31 [RFC PATCH v6 00/11] Integrity Policy Enforcement LSM (IPE) Deven Bowers
                   ` (4 preceding siblings ...)
  2020-07-30  0:31 ` [RFC PATCH v6 05/11] fs: add security blob and hooks for block_device Deven Bowers
@ 2020-07-30  0:31 ` Deven Bowers
  2020-07-30  0:31 ` [RFC PATCH v6 07/11] ipe: add property for signed dmverity volumes Deven Bowers
                   ` (4 subsequent siblings)
  10 siblings, 0 replies; 12+ messages in thread
From: Deven Bowers @ 2020-07-30  0:31 UTC (permalink / raw)
  To: agk, axboe, snitzer, jmorris, serge, zohar, viro, paul, eparis,
	jannh, dm-devel, linux-integrity, linux-security-module,
	linux-fsdevel, linux-block, linux-audit
  Cc: tyhicks, linux-kernel, corbet, sashal, jaskarankhurana, mdsakib,
	nramas, pasha.tatashin
Add a security hook call to set a security property of a block_device
in dm-verity with the results of a verified, signed root-hash.
Signed-off-by: Deven Bowers <deven.desai@linux.microsoft.com>
---
 drivers/md/dm-verity-target.c     |  2 +-
 drivers/md/dm-verity-verify-sig.c | 14 +++++++++++---
 drivers/md/dm-verity-verify-sig.h | 10 ++++++----
 include/linux/device-mapper.h     |  2 ++
 4 files changed, 20 insertions(+), 8 deletions(-)
diff --git a/drivers/md/dm-verity-target.c b/drivers/md/dm-verity-target.c
index eec9f252e935..9970488e67ed 100644
--- a/drivers/md/dm-verity-target.c
+++ b/drivers/md/dm-verity-target.c
@@ -1134,7 +1134,7 @@ static int verity_ctr(struct dm_target *ti, unsigned argc, char **argv)
 	}
 
 	/* Root hash signature is  a optional parameter*/
-	r = verity_verify_root_hash(root_hash_digest_to_validate,
+	r = verity_verify_root_hash(v, root_hash_digest_to_validate,
 				    strlen(root_hash_digest_to_validate),
 				    verify_args.sig,
 				    verify_args.sig_size);
diff --git a/drivers/md/dm-verity-verify-sig.c b/drivers/md/dm-verity-verify-sig.c
index 614e43db93aa..2cd9f8c85449 100644
--- a/drivers/md/dm-verity-verify-sig.c
+++ b/drivers/md/dm-verity-verify-sig.c
@@ -8,7 +8,10 @@
 #include <linux/device-mapper.h>
 #include <linux/verification.h>
 #include <keys/user-type.h>
+#include <linux/security.h>
+#include <linux/list.h>
 #include <linux/module.h>
+#include "dm-core.h"
 #include "dm-verity.h"
 #include "dm-verity-verify-sig.h"
 
@@ -103,8 +106,9 @@ int verity_verify_sig_parse_opt_args(struct dm_arg_set *as,
  * @sig_len: Size of the signature.
  *
  */
-int verity_verify_root_hash(const void *root_hash, size_t root_hash_len,
-			    const void *sig_data, size_t sig_len)
+int verity_verify_root_hash(struct dm_verity *v, const void *root_hash,
+			    size_t root_hash_len, const void *sig_data,
+			    size_t sig_len)
 {
 	int ret;
 
@@ -121,8 +125,12 @@ int verity_verify_root_hash(const void *root_hash, size_t root_hash_len,
 	ret = verify_pkcs7_signature(root_hash, root_hash_len, sig_data,
 				sig_len, NULL, VERIFYING_UNSPECIFIED_SIGNATURE,
 				NULL, NULL);
+	if (ret)
+		return ret;
 
-	return ret;
+	return security_bdev_setsecurity(dm_table_get_md(v->ti->table)->bdev,
+					 DM_VERITY_SIGNATURE_SEC_NAME,
+					 sig_data, sig_len);
 }
 
 void verity_verify_sig_opts_cleanup(struct dm_verity_sig_opts *sig_opts)
diff --git a/drivers/md/dm-verity-verify-sig.h b/drivers/md/dm-verity-verify-sig.h
index 19b1547aa741..6b7de1d48e5a 100644
--- a/drivers/md/dm-verity-verify-sig.h
+++ b/drivers/md/dm-verity-verify-sig.h
@@ -20,8 +20,9 @@ struct dm_verity_sig_opts {
 
 #define DM_VERITY_ROOT_HASH_VERIFICATION_OPTS 2
 
-int verity_verify_root_hash(const void *data, size_t data_len,
-			    const void *sig_data, size_t sig_len);
+int verity_verify_root_hash(struct dm_verity *v, const void *data,
+			    size_t data_len, const void *sig_data,
+			    size_t sig_len);
 bool verity_verify_is_sig_opt_arg(const char *arg_name);
 
 int verity_verify_sig_parse_opt_args(struct dm_arg_set *as, struct dm_verity *v,
@@ -34,8 +35,9 @@ void verity_verify_sig_opts_cleanup(struct dm_verity_sig_opts *sig_opts);
 
 #define DM_VERITY_ROOT_HASH_VERIFICATION_OPTS 0
 
-int verity_verify_root_hash(const void *data, size_t data_len,
-			    const void *sig_data, size_t sig_len)
+int verity_verify_root_hash(struct dm_verity *v, const void *data,
+			    size_t data_len, const void *sig_data,
+			    size_t sig_len)
 {
 	return 0;
 }
diff --git a/include/linux/device-mapper.h b/include/linux/device-mapper.h
index 73dec4b5d5be..ab6b8eb0a150 100644
--- a/include/linux/device-mapper.h
+++ b/include/linux/device-mapper.h
@@ -625,4 +625,6 @@ static inline unsigned long to_bytes(sector_t n)
 	return (n << SECTOR_SHIFT);
 }
 
+#define DM_VERITY_SIGNATURE_SEC_NAME DM_NAME	".verity-sig"
+
 #endif	/* _LINUX_DEVICE_MAPPER_H */
-- 
2.27.0
^ permalink raw reply related	[flat|nested] 12+ messages in thread
- * [RFC PATCH v6 07/11] ipe: add property for signed dmverity volumes
  2020-07-30  0:31 [RFC PATCH v6 00/11] Integrity Policy Enforcement LSM (IPE) Deven Bowers
                   ` (5 preceding siblings ...)
  2020-07-30  0:31 ` [RFC PATCH v6 06/11] dm-verity: add bdev_setsecurity hook for dm-verity signature Deven Bowers
@ 2020-07-30  0:31 ` Deven Bowers
  2020-07-30  0:31 ` [RFC PATCH v6 08/11] dm-verity: add bdev_setsecurity hook for root-hash Deven Bowers
                   ` (3 subsequent siblings)
  10 siblings, 0 replies; 12+ messages in thread
From: Deven Bowers @ 2020-07-30  0:31 UTC (permalink / raw)
  To: agk, axboe, snitzer, jmorris, serge, zohar, viro, paul, eparis,
	jannh, dm-devel, linux-integrity, linux-security-module,
	linux-fsdevel, linux-block, linux-audit
  Cc: tyhicks, linux-kernel, corbet, sashal, jaskarankhurana, mdsakib,
	nramas, pasha.tatashin
Allow IPE to leverage the stacked security blob infrastructure,
and enlighten IPE to the block_device security blob.
This allows IPE to have a property to express rules around a device-mapper
verity volume whose root-hash has been signed, and the signature has been
verified against the system keyring. This is property is also added in
this patch.
Signed-off-by: Deven Bowers <deven.desai@linux.microsoft.com>
---
 security/ipe/Kconfig                         |  2 +-
 security/ipe/Makefile                        |  1 +
 security/ipe/ipe-blobs.c                     | 84 ++++++++++++++++++++
 security/ipe/ipe-blobs.h                     | 18 +++++
 security/ipe/ipe-engine.c                    |  4 +
 security/ipe/ipe-engine.h                    |  9 +++
 security/ipe/ipe-hooks.c                     |  1 +
 security/ipe/ipe-hooks.h                     |  7 ++
 security/ipe/ipe.c                           | 18 +++++
 security/ipe/ipe.h                           |  2 +
 security/ipe/properties/Kconfig              | 10 +++
 security/ipe/properties/Makefile             |  1 +
 security/ipe/properties/dmverity-signature.c | 82 +++++++++++++++++++
 security/ipe/properties/prop-entry.h         |  9 +++
 security/ipe/utility.h                       | 10 +++
 15 files changed, 257 insertions(+), 1 deletion(-)
 create mode 100644 security/ipe/ipe-blobs.c
 create mode 100644 security/ipe/ipe-blobs.h
 create mode 100644 security/ipe/properties/dmverity-signature.c
diff --git a/security/ipe/Kconfig b/security/ipe/Kconfig
index 469ef78c2f4f..11b50ef9abca 100644
--- a/security/ipe/Kconfig
+++ b/security/ipe/Kconfig
@@ -5,7 +5,7 @@
 
 menuconfig SECURITY_IPE
 	bool "Integrity Policy Enforcement (IPE)"
-	depends on SECURITY && AUDIT
+	depends on SECURITY && AUDIT && BLOCK
 	select SYSTEM_DATA_VERIFICATION
 	select SECURITYFS
 	select CRYPTO_SHA1
diff --git a/security/ipe/Makefile b/security/ipe/Makefile
index 7e98982c5035..98a2245b6956 100644
--- a/security/ipe/Makefile
+++ b/security/ipe/Makefile
@@ -24,6 +24,7 @@ obj-$(CONFIG_SECURITY_IPE) += \
 	ipe-property.o \
 	ipe-hooks.o \
 	ipe-secfs.o \
+	ipe-blobs.o \
 
 clean-files := ipe-bp.c
 
diff --git a/security/ipe/ipe-blobs.c b/security/ipe/ipe-blobs.c
new file mode 100644
index 000000000000..041d7d47b723
--- /dev/null
+++ b/security/ipe/ipe-blobs.c
@@ -0,0 +1,84 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) Microsoft Corporation. All rights reserved.
+ */
+
+#include "ipe.h"
+#include "ipe-engine.h"
+#include "ipe-blobs.h"
+
+#include <linux/types.h>
+#include <linux/fs.h>
+#include <linux/device-mapper.h>
+
+/**
+ * ipe_bdev_alloc_security: Performs the initialization of IPE's security blob.
+ * @bdev: The block device to source the security blob from.
+ *
+ * The allocation is performed earlier by the LSM infrastructure,
+ * (on behalf of all LSMs) in lsm_alloc_bdev. At the moment, IPE uses
+ * this time to zero out the region of memory reserved for IPE.
+ *
+ * Return:
+ * 0 - OK
+ */
+int ipe_bdev_alloc_security(struct block_device *bdev)
+{
+	struct ipe_bdev_blob *bdev_sec = ipe_bdev(bdev);
+
+	memset(bdev_sec, 0x0, sizeof(*bdev_sec));
+
+	return 0;
+}
+
+/**
+ * ipe_bdev_free_security: Frees all fields of IPE's block dev security blob.
+ * @bdev: The block device to source the security blob from.
+ *
+ * The deallocation of the blob itself is performed later by the LSM
+ * infrastructure, (on behalf of all LSMs) in lsm_free_bdev.
+ *
+ * Pointers allocated by the bdev_setsecurity hook and alloc_security
+ * hook need to be deallocated here.
+ */
+void ipe_bdev_free_security(struct block_device *bdev)
+{
+	struct ipe_bdev_blob *bdev_sec = ipe_bdev(bdev);
+
+	kfree(bdev_sec->dmverity_rh_sig);
+
+	memset(bdev_sec, 0x0, sizeof(*bdev_sec));
+}
+
+/**
+ * ipe_bdev_setsecurity: Sets the a certain field of a block device security
+ *			 blob, based on @key.
+ * @bdev: The block device to source the security blob from.
+ * @key: The key representing the information to be stored.
+ * @value: The value to be stored.
+ * @len: The length of @value.
+ *
+ * As block-devices are a generic implementation across specific stacks,
+ * this allows information to be stored from various stacks.
+ *
+ * Return:
+ * 0 - OK
+ * !0 - Error
+ */
+int ipe_bdev_setsecurity(struct block_device *bdev, const char *key,
+			 const void *value, size_t len)
+{
+	struct ipe_bdev_blob *bdev_sec = ipe_bdev(bdev);
+
+	if (!strcmp(key, DM_VERITY_SIGNATURE_SEC_NAME)) {
+		bdev_sec->dmverity_rh_sig = kmemdup(value, len, GFP_KERNEL);
+		if (!bdev_sec->dmverity_rh_sig)
+			return -ENOMEM;
+
+		bdev_sec->dmv_rh_sig_len = len;
+
+		return 0;
+	}
+
+	return -ENOSYS;
+}
diff --git a/security/ipe/ipe-blobs.h b/security/ipe/ipe-blobs.h
new file mode 100644
index 000000000000..7561f4cef558
--- /dev/null
+++ b/security/ipe/ipe-blobs.h
@@ -0,0 +1,18 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) Microsoft Corporation. All rights reserved.
+ */
+#include <linux/types.h>
+#include <linux/fs.h>
+
+#include "ipe.h"
+
+#ifndef IPE_BLOB_H
+#define IPE_BLOB_H
+
+static inline struct ipe_bdev_blob *ipe_bdev(struct block_device *bdev)
+{
+	return bdev->security + ipe_blobs.lbs_bdev;
+}
+
+#endif /* IPE_BLOB_H */
diff --git a/security/ipe/ipe-engine.c b/security/ipe/ipe-engine.c
index 0291ced99d64..043faf64ceef 100644
--- a/security/ipe/ipe-engine.c
+++ b/security/ipe/ipe-engine.c
@@ -10,6 +10,7 @@
 #include "ipe-engine.h"
 #include "ipe-audit.h"
 #include "ipe-pin.h"
+#include "ipe-blobs.h"
 #include "utility.h"
 
 #include <linux/types.h>
@@ -112,6 +113,9 @@ static struct ipe_engine_ctx *build_ctx(const struct file *file,
 	local->op = op;
 	local->hook = hook;
 
+	if (has_bdev(file))
+		local->sec_bdev = ipe_bdev(bdev(file));
+
 	return local;
 }
 
diff --git a/security/ipe/ipe-engine.h b/security/ipe/ipe-engine.h
index d9a95674e70d..038c39a8973e 100644
--- a/security/ipe/ipe-engine.h
+++ b/security/ipe/ipe-engine.h
@@ -3,20 +3,29 @@
  * Copyright (C) Microsoft Corporation. All rights reserved.
  */
 
+#include "ipe.h"
 #include "ipe-hooks.h"
 
 #include <linux/types.h>
 #include <linux/rbtree.h>
 #include <linux/fs.h>
 
+#include <crypto/pkcs7.h>
+
 #ifndef IPE_ENGINE_H
 #define IPE_ENGINE_H
 
+struct ipe_bdev_blob {
+	u8	*dmverity_rh_sig;
+	size_t	dmv_rh_sig_len;
+};
+
 struct ipe_engine_ctx {
 	enum ipe_op op;
 	enum ipe_hook hook;
 	const struct file *file;
 	const char *audit_pathname;
+	const struct ipe_bdev_blob *sec_bdev;
 };
 
 struct ipe_prop_cache {
diff --git a/security/ipe/ipe-hooks.c b/security/ipe/ipe-hooks.c
index 45efe022be04..18ab2dcd74d1 100644
--- a/security/ipe/ipe-hooks.c
+++ b/security/ipe/ipe-hooks.c
@@ -15,6 +15,7 @@
 #include <linux/mman.h>
 #include <linux/mm.h>
 #include <linux/security.h>
+#include <linux/device-mapper.h>
 
 /**
  * ipe_on_exec: LSM hook called on the exec family of system calls.
diff --git a/security/ipe/ipe-hooks.h b/security/ipe/ipe-hooks.h
index 5e46726f2562..b2417831cfc1 100644
--- a/security/ipe/ipe-hooks.h
+++ b/security/ipe/ipe-hooks.h
@@ -60,4 +60,11 @@ int ipe_on_kernel_load_data(enum kernel_load_data_id id);
 
 void ipe_sb_free_security(struct super_block *mnt_sb);
 
+int ipe_bdev_alloc_security(struct block_device *bdev);
+
+void ipe_bdev_free_security(struct block_device *bdev);
+
+int ipe_bdev_setsecurity(struct block_device *bdev, const char *key,
+			 const void *value, size_t len);
+
 #endif /* IPE_HOOK_H */
diff --git a/security/ipe/ipe.c b/security/ipe/ipe.c
index 706ff38083c6..8a612eb62879 100644
--- a/security/ipe/ipe.c
+++ b/security/ipe/ipe.c
@@ -23,6 +23,9 @@ static struct security_hook_list ipe_hooks[] __lsm_ro_after_init = {
 	LSM_HOOK_INIT(kernel_load_data, ipe_on_kernel_load_data),
 	LSM_HOOK_INIT(file_mprotect, ipe_on_mprotect),
 	LSM_HOOK_INIT(sb_free_security, ipe_sb_free_security),
+	LSM_HOOK_INIT(bdev_alloc_security, ipe_bdev_alloc_security),
+	LSM_HOOK_INIT(bdev_free_security, ipe_bdev_free_security),
+	LSM_HOOK_INIT(bdev_setsecurity, ipe_bdev_setsecurity),
 };
 
 /**
@@ -40,6 +43,10 @@ static int __init ipe_load_properties(void)
 	if (rc != 0)
 		return rc;
 
+	rc = ipe_init_dm_verity_signature();
+	if (rc != 0)
+		return rc;
+
 	return rc;
 }
 
@@ -72,9 +79,20 @@ static int __init ipe_init(void)
 	return rc;
 }
 
+struct lsm_blob_sizes ipe_blobs = {
+	.lbs_cred = 0,
+	.lbs_file = 0,
+	.lbs_inode = 0,
+	.lbs_ipc = 0,
+	.lbs_msg_msg = 0,
+	.lbs_task = 0,
+	.lbs_bdev = sizeof(struct ipe_bdev_blob),
+};
+
 DEFINE_LSM(ipe) = {
 	.name = "ipe",
 	.init = ipe_init,
+	.blobs = &ipe_blobs,
 };
 
 bool ipe_enforce = true;
diff --git a/security/ipe/ipe.h b/security/ipe/ipe.h
index af72bb574f73..a59cae2deec6 100644
--- a/security/ipe/ipe.h
+++ b/security/ipe/ipe.h
@@ -10,11 +10,13 @@
 
 #include <linux/types.h>
 #include <linux/fs.h>
+#include <linux/lsm_hooks.h>
 
 #define IPE_MODE_ENFORCE	"enforce"
 #define IPE_MODE_PERMISSIVE	"permissive"
 
 extern bool ipe_enforce;
 extern bool ipe_success_audit;
+extern struct lsm_blob_sizes ipe_blobs;
 
 #endif /* IPE_H */
diff --git a/security/ipe/properties/Kconfig b/security/ipe/properties/Kconfig
index 75c6c6ff6cd8..4046f7e5eaef 100644
--- a/security/ipe/properties/Kconfig
+++ b/security/ipe/properties/Kconfig
@@ -13,3 +13,13 @@ config IPE_BOOT_PROP
 	  superblock.
 
 	  if unsure, answer N.
+
+config IPE_DM_VERITY_SIGNATURE
+	bool "Enable property for signature verified dm-verity volumes"
+	depends on DM_VERITY_VERIFY_ROOTHASH_SIG
+	help
+	  This option enables IPE's integration with Device-Mapper Verity's
+	  signed root-hashes. This enables the usage of the property,
+	  "dmverity_signature" in IPE's policy.
+
+	  if unsure, answer Y.
diff --git a/security/ipe/properties/Makefile b/security/ipe/properties/Makefile
index e3e7fe17cf58..6b67cbe36e31 100644
--- a/security/ipe/properties/Makefile
+++ b/security/ipe/properties/Makefile
@@ -9,3 +9,4 @@
 obj-$(CONFIG_SECURITY_IPE) += properties.o
 
 properties-$(CONFIG_IPE_BOOT_PROP) += boot-verified.o
+properties-$(CONFIG_IPE_DM_VERITY_SIGNATURE) += dmverity-signature.o
diff --git a/security/ipe/properties/dmverity-signature.c b/security/ipe/properties/dmverity-signature.c
new file mode 100644
index 000000000000..819222f226b7
--- /dev/null
+++ b/security/ipe/properties/dmverity-signature.c
@@ -0,0 +1,82 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) Microsoft Corporation. All rights reserved.
+ */
+
+#include "../ipe.h"
+#include "../ipe-pin.h"
+#include "../ipe-property.h"
+#include "../utility.h"
+
+#include <linux/types.h>
+#include <linux/slab.h>
+#include <linux/fs.h>
+#include <linux/audit.h>
+#include <linux/mount.h>
+
+#define PROPERTY_NAME "dmverity_signature"
+
+static void audit(struct audit_buffer *ab, bool value)
+{
+	audit_log_format(ab, "%s", (value) ? "TRUE" : "FALSE");
+}
+
+static inline void audit_rule_value(struct audit_buffer *ab,
+				    const void *value)
+{
+	audit(ab, (bool)value);
+}
+
+static inline void audit_ctx(struct audit_buffer *ab,
+			     const struct ipe_engine_ctx *ctx)
+{
+	bool b = has_bdev(ctx->file) && ctx->sec_bdev->dmverity_rh_sig;
+
+	audit(ab, b);
+}
+
+static bool evaluate(const struct ipe_engine_ctx *ctx,
+		     const void *value)
+{
+	bool expect = (bool)value;
+
+	if (!has_bdev(ctx->file))
+		return false;
+
+	return ((bool)ctx->sec_bdev->dmverity_rh_sig) == expect;
+}
+
+static int parse(const char *val_str, void **value)
+{
+	if (strcmp("TRUE", val_str) == 0)
+		*value = (void *)true;
+	else if (strcmp("FALSE", val_str) == 0)
+		*value = (void *)false;
+	else
+		return -EBADMSG;
+
+	return 0;
+}
+
+static inline int duplicate(const void *src, void **dest)
+{
+	*dest = (void *)(bool)src;
+
+	return 0;
+}
+
+static const struct ipe_property dmv_signature = {
+	.property_name = PROPERTY_NAME,
+	.version = 1,
+	.eval = evaluate,
+	.parse = parse,
+	.rule_audit = audit_rule_value,
+	.ctx_audit = audit_ctx,
+	.dup = duplicate,
+	.free_val = NULL,
+};
+
+int ipe_init_dm_verity_signature(void)
+{
+	return ipe_register_property(&dmv_signature);
+}
diff --git a/security/ipe/properties/prop-entry.h b/security/ipe/properties/prop-entry.h
index f598dd9608b9..85366366ff0d 100644
--- a/security/ipe/properties/prop-entry.h
+++ b/security/ipe/properties/prop-entry.h
@@ -17,4 +17,13 @@ static inline int __init ipe_init_bootv(void)
 int __init ipe_init_bootv(void);
 #endif /* CONFIG_IPE_BOOT_PROP */
 
+#ifndef CONFIG_IPE_DM_VERITY_SIGNATURE
+static inline int __init ipe_init_dm_verity_signature(void)
+{
+	return 0;
+}
+#else
+int __init ipe_init_dm_verity_signature(void);
+#endif /* CONFIG_IPE_DM_VERITY_SIGNATURE */
+
 #endif /* IPE_PROP_ENTRY_H */
diff --git a/security/ipe/utility.h b/security/ipe/utility.h
index a13089bb0d8f..7580f17274a3 100644
--- a/security/ipe/utility.h
+++ b/security/ipe/utility.h
@@ -19,4 +19,14 @@ static inline bool has_sb(const struct file *file)
 	return has_mount(file) && file->f_path.mnt->mnt_sb;
 }
 
+static inline bool has_bdev(const struct file *file)
+{
+	return has_sb(file) && file->f_path.mnt->mnt_sb->s_bdev;
+}
+
+static inline struct block_device *bdev(const struct file *file)
+{
+	return file->f_path.mnt->mnt_sb->s_bdev;
+}
+
 #endif /* IPE_UTILITY_H */
-- 
2.27.0
^ permalink raw reply related	[flat|nested] 12+ messages in thread
- * [RFC PATCH v6 08/11] dm-verity: add bdev_setsecurity hook for root-hash
  2020-07-30  0:31 [RFC PATCH v6 00/11] Integrity Policy Enforcement LSM (IPE) Deven Bowers
                   ` (6 preceding siblings ...)
  2020-07-30  0:31 ` [RFC PATCH v6 07/11] ipe: add property for signed dmverity volumes Deven Bowers
@ 2020-07-30  0:31 ` Deven Bowers
  2020-07-30  0:31 ` [RFC PATCH v6 09/11] ipe: add property for dmverity roothash Deven Bowers
                   ` (2 subsequent siblings)
  10 siblings, 0 replies; 12+ messages in thread
From: Deven Bowers @ 2020-07-30  0:31 UTC (permalink / raw)
  To: agk, axboe, snitzer, jmorris, serge, zohar, viro, paul, eparis,
	jannh, dm-devel, linux-integrity, linux-security-module,
	linux-fsdevel, linux-block, linux-audit
  Cc: tyhicks, linux-kernel, corbet, sashal, jaskarankhurana, mdsakib,
	nramas, pasha.tatashin
Add a security hook call to set a security property of a block_device
in dm-verity with the root-hash that was passed to device-mapper.
Signed-off-by: Deven Bowers <deven.desai@linux.microsoft.com>
---
 drivers/md/dm-verity-target.c | 8 ++++++++
 include/linux/device-mapper.h | 1 +
 2 files changed, 9 insertions(+)
diff --git a/drivers/md/dm-verity-target.c b/drivers/md/dm-verity-target.c
index 9970488e67ed..44914668398d 100644
--- a/drivers/md/dm-verity-target.c
+++ b/drivers/md/dm-verity-target.c
@@ -16,8 +16,10 @@
 #include "dm-verity.h"
 #include "dm-verity-fec.h"
 #include "dm-verity-verify-sig.h"
+#include "dm-core.h"
 #include <linux/module.h>
 #include <linux/reboot.h>
+#include <linux/security.h>
 
 #define DM_MSG_PREFIX			"verity"
 
@@ -1207,6 +1209,12 @@ static int verity_ctr(struct dm_target *ti, unsigned argc, char **argv)
 	ti->per_io_data_size = roundup(ti->per_io_data_size,
 				       __alignof__(struct dm_verity_io));
 
+	r = security_bdev_setsecurity(dm_table_get_md(v->ti->table)->bdev,
+				      DM_VERITY_ROOTHASH_SEC_NAME,
+				      v->root_digest, v->digest_size);
+	if (r)
+		goto bad;
+
 	verity_verify_sig_opts_cleanup(&verify_args);
 
 	return 0;
diff --git a/include/linux/device-mapper.h b/include/linux/device-mapper.h
index ab6b8eb0a150..e8ef887e4bae 100644
--- a/include/linux/device-mapper.h
+++ b/include/linux/device-mapper.h
@@ -626,5 +626,6 @@ static inline unsigned long to_bytes(sector_t n)
 }
 
 #define DM_VERITY_SIGNATURE_SEC_NAME DM_NAME	".verity-sig"
+#define DM_VERITY_ROOTHASH_SEC_NAME DM_NAME	".verity-rh"
 
 #endif	/* _LINUX_DEVICE_MAPPER_H */
-- 
2.27.0
^ permalink raw reply related	[flat|nested] 12+ messages in thread
- * [RFC PATCH v6 09/11] ipe: add property for dmverity roothash
  2020-07-30  0:31 [RFC PATCH v6 00/11] Integrity Policy Enforcement LSM (IPE) Deven Bowers
                   ` (7 preceding siblings ...)
  2020-07-30  0:31 ` [RFC PATCH v6 08/11] dm-verity: add bdev_setsecurity hook for root-hash Deven Bowers
@ 2020-07-30  0:31 ` Deven Bowers
  2020-07-30  0:31 ` [RFC PATCH v6 10/11] documentation: add ipe documentation Deven Bowers
  2020-07-30  0:31 ` [RFC PATCH v6 11/11] cleanup: uapi/linux/audit.h Deven Bowers
  10 siblings, 0 replies; 12+ messages in thread
From: Deven Bowers @ 2020-07-30  0:31 UTC (permalink / raw)
  To: agk, axboe, snitzer, jmorris, serge, zohar, viro, paul, eparis,
	jannh, dm-devel, linux-integrity, linux-security-module,
	linux-fsdevel, linux-block, linux-audit
  Cc: tyhicks, linux-kernel, corbet, sashal, jaskarankhurana, mdsakib,
	nramas, pasha.tatashin
Add a property to allow IPE policy to express rules around a specific
root-hash of a dm-verity volume.
This can be used for revocation, (when combined with the previous dm-verity
property) or the authorization of a single dm-verity volume.
Signed-off-by: Deven Bowers <deven.desai@linux.microsoft.com>
---
 security/ipe/ipe-blobs.c                    |  11 ++
 security/ipe/ipe-engine.h                   |   3 +
 security/ipe/ipe.c                          |   4 +
 security/ipe/properties/Kconfig             |  13 +-
 security/ipe/properties/Makefile            |   1 +
 security/ipe/properties/dmverity-roothash.c | 153 ++++++++++++++++++++
 security/ipe/properties/prop-entry.h        |   9 ++
 7 files changed, 193 insertions(+), 1 deletion(-)
 create mode 100644 security/ipe/properties/dmverity-roothash.c
diff --git a/security/ipe/ipe-blobs.c b/security/ipe/ipe-blobs.c
index 041d7d47b723..6a09d5c6dea8 100644
--- a/security/ipe/ipe-blobs.c
+++ b/security/ipe/ipe-blobs.c
@@ -46,6 +46,7 @@ void ipe_bdev_free_security(struct block_device *bdev)
 	struct ipe_bdev_blob *bdev_sec = ipe_bdev(bdev);
 
 	kfree(bdev_sec->dmverity_rh_sig);
+	kfree(bdev_sec->dmverity_rh);
 
 	memset(bdev_sec, 0x0, sizeof(*bdev_sec));
 }
@@ -80,5 +81,15 @@ int ipe_bdev_setsecurity(struct block_device *bdev, const char *key,
 		return 0;
 	}
 
+	if (!strcmp(key, DM_VERITY_ROOTHASH_SEC_NAME)) {
+		bdev_sec->dmverity_rh = kmemdup(value, len, GFP_KERNEL);
+		if (!bdev_sec->dmverity_rh)
+			return -ENOMEM;
+
+		bdev_sec->rh_size = len;
+
+		return 0;
+	}
+
 	return -ENOSYS;
 }
diff --git a/security/ipe/ipe-engine.h b/security/ipe/ipe-engine.h
index 038c39a8973e..696baaa423ff 100644
--- a/security/ipe/ipe-engine.h
+++ b/security/ipe/ipe-engine.h
@@ -18,6 +18,9 @@
 struct ipe_bdev_blob {
 	u8	*dmverity_rh_sig;
 	size_t	dmv_rh_sig_len;
+
+	u8 *dmverity_rh;
+	size_t rh_size;
 };
 
 struct ipe_engine_ctx {
diff --git a/security/ipe/ipe.c b/security/ipe/ipe.c
index 8a612eb62879..8f4dfb8c547f 100644
--- a/security/ipe/ipe.c
+++ b/security/ipe/ipe.c
@@ -47,6 +47,10 @@ static int __init ipe_load_properties(void)
 	if (rc != 0)
 		return rc;
 
+	rc = ipe_init_dm_verity_rh();
+	if (rc != 0)
+		return rc;
+
 	return rc;
 }
 
diff --git a/security/ipe/properties/Kconfig b/security/ipe/properties/Kconfig
index 4046f7e5eaef..4f09092522d9 100644
--- a/security/ipe/properties/Kconfig
+++ b/security/ipe/properties/Kconfig
@@ -14,8 +14,19 @@ config IPE_BOOT_PROP
 
 	  if unsure, answer N.
 
+config IPE_DM_VERITY_ROOTHASH
+	bool "Enable property for authorizing dm-verity volumes via root-hash"
+	depends on DM_VERITY
+	help
+	  This option enables IPE's integration with Device-Mapper Verity.
+	  This enables the usage of the property "dmverity_roothash" in IPE's
+	  policy. This property allows authorization or revocation via a
+	  a hex-string representing the roothash of a dmverity volume.
+
+	  if unsure, answer Y.
+
 config IPE_DM_VERITY_SIGNATURE
-	bool "Enable property for signature verified dm-verity volumes"
+	bool "Enable property for verified dm-verity volumes"
 	depends on DM_VERITY_VERIFY_ROOTHASH_SIG
 	help
 	  This option enables IPE's integration with Device-Mapper Verity's
diff --git a/security/ipe/properties/Makefile b/security/ipe/properties/Makefile
index 6b67cbe36e31..d9a3807797f4 100644
--- a/security/ipe/properties/Makefile
+++ b/security/ipe/properties/Makefile
@@ -10,3 +10,4 @@ obj-$(CONFIG_SECURITY_IPE) += properties.o
 
 properties-$(CONFIG_IPE_BOOT_PROP) += boot-verified.o
 properties-$(CONFIG_IPE_DM_VERITY_SIGNATURE) += dmverity-signature.o
+properties-$(CONFIG_IPE_DM_VERITY_ROOTHASH) += dmverity-roothash.o
diff --git a/security/ipe/properties/dmverity-roothash.c b/security/ipe/properties/dmverity-roothash.c
new file mode 100644
index 000000000000..09112e1af753
--- /dev/null
+++ b/security/ipe/properties/dmverity-roothash.c
@@ -0,0 +1,153 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) Microsoft Corporation. All rights reserved.
+ */
+
+#include "../ipe.h"
+#include "../ipe-pin.h"
+#include "../ipe-property.h"
+#include "../utility.h"
+
+#include <linux/types.h>
+#include <linux/slab.h>
+#include <linux/fs.h>
+#include <linux/mount.h>
+#include <linux/audit.h>
+#include <linux/kernel.h>
+
+#define PROPERTY_NAME "dmverity_roothash"
+
+struct counted_array {
+	u8 *arr;
+	size_t len;
+};
+
+static void audit(struct audit_buffer *ab, const void *value)
+{
+	const struct counted_array *a = (const struct counted_array *)value;
+
+	if (!a || a->len == 0)
+		audit_log_format(ab, "NULL");
+	else
+		audit_log_n_hex(ab, a->arr, a->len);
+}
+
+static inline void audit_rule_value(struct audit_buffer *ab,
+				    const void *value)
+{
+	audit(ab, value);
+}
+
+static inline void audit_ctx(struct audit_buffer *ab,
+			     const struct ipe_engine_ctx *ctx)
+{
+	struct counted_array a;
+
+	if (!has_bdev(ctx->file))
+		return audit(ab, NULL);
+
+	a.arr = ctx->sec_bdev->dmverity_rh;
+	a.len = ctx->sec_bdev->rh_size;
+
+	return audit(ab, &a);
+}
+
+static bool evaluate(const struct ipe_engine_ctx *ctx,
+		     const void *value)
+{
+	const struct counted_array *a = (const struct counted_array *)value;
+
+	if (!has_bdev(ctx->file))
+		return false;
+
+	if (a->len != ctx->sec_bdev->rh_size)
+		return false;
+
+	return memcmp(a->arr, ctx->sec_bdev->dmverity_rh, a->len) == 0;
+}
+
+static int parse(const char *val_str, void **value)
+{
+	struct counted_array *arr = NULL;
+	int rv = 0;
+
+	arr = kzalloc(sizeof(*arr), GFP_KERNEL);
+	if (!arr) {
+		rv = -ENOMEM;
+		goto err;
+	}
+
+	arr->len = strlen(val_str) / 2;
+
+	arr->arr = kzalloc(arr->len, GFP_KERNEL);
+	if (!arr->arr) {
+		rv = -ENOMEM;
+		goto err;
+	}
+
+	rv = hex2bin(arr->arr, val_str, arr->len);
+	if (rv != 0)
+		goto err;
+
+	*value = arr;
+	return rv;
+err:
+	if (arr)
+		kfree(arr->arr);
+	kfree(arr);
+	return rv;
+}
+
+static int duplicate(const void *src, void **dest)
+{
+	struct counted_array *arr = NULL;
+	const struct counted_array *src_arr = src;
+	int rv = 0;
+
+	arr = kmemdup(src_arr, sizeof(*arr), GFP_KERNEL);
+	if (!arr) {
+		rv = -ENOMEM;
+		goto err;
+	}
+
+	arr->arr = kmemdup(src_arr->arr, src_arr->len, GFP_KERNEL);
+	if (!arr->arr) {
+		rv = -ENOMEM;
+		goto err;
+	}
+
+	*dest = arr;
+	return rv;
+err:
+	if (arr)
+		kfree(arr->arr);
+	kfree(arr);
+
+	return rv;
+}
+
+static void free_val(void **value)
+{
+	struct counted_array *a = (struct counted_array *)*value;
+
+	if (a)
+		kfree(a->arr);
+	kfree(a);
+	*value = NULL;
+}
+
+static const struct ipe_property dmv_roothash = {
+	.property_name = PROPERTY_NAME,
+	.version = 1,
+	.eval = evaluate,
+	.parse = parse,
+	.rule_audit = audit_rule_value,
+	.ctx_audit = audit_ctx,
+	.dup = duplicate,
+	.free_val = free_val,
+};
+
+int ipe_init_dm_verity_rh(void)
+{
+	return ipe_register_property(&dmv_roothash);
+}
diff --git a/security/ipe/properties/prop-entry.h b/security/ipe/properties/prop-entry.h
index 85366366ff0d..86a360570f3b 100644
--- a/security/ipe/properties/prop-entry.h
+++ b/security/ipe/properties/prop-entry.h
@@ -26,4 +26,13 @@ static inline int __init ipe_init_dm_verity_signature(void)
 int __init ipe_init_dm_verity_signature(void);
 #endif /* CONFIG_IPE_DM_VERITY_SIGNATURE */
 
+#ifndef CONFIG_IPE_DM_VERITY_ROOTHASH
+static inline int __init ipe_init_dm_verity_rh(void)
+{
+	return 0;
+}
+#else
+int __init ipe_init_dm_verity_rh(void);
+#endif /* CONFIG_IPE_DM_VERITY_ROOTHASH */
+
 #endif /* IPE_PROP_ENTRY_H */
-- 
2.27.0
^ permalink raw reply related	[flat|nested] 12+ messages in thread
- * [RFC PATCH v6 10/11] documentation: add ipe documentation
  2020-07-30  0:31 [RFC PATCH v6 00/11] Integrity Policy Enforcement LSM (IPE) Deven Bowers
                   ` (8 preceding siblings ...)
  2020-07-30  0:31 ` [RFC PATCH v6 09/11] ipe: add property for dmverity roothash Deven Bowers
@ 2020-07-30  0:31 ` Deven Bowers
  2020-07-30  0:31 ` [RFC PATCH v6 11/11] cleanup: uapi/linux/audit.h Deven Bowers
  10 siblings, 0 replies; 12+ messages in thread
From: Deven Bowers @ 2020-07-30  0:31 UTC (permalink / raw)
  To: agk, axboe, snitzer, jmorris, serge, zohar, viro, paul, eparis,
	jannh, dm-devel, linux-integrity, linux-security-module,
	linux-fsdevel, linux-block, linux-audit
  Cc: tyhicks, linux-kernel, corbet, sashal, jaskarankhurana, mdsakib,
	nramas, pasha.tatashin
Add IPE's documentation to the kernel tree.
Signed-off-by: Deven Bowers <deven.desai@linux.microsoft.com>
Acked-by: Jonathan Corbet <corbet@lwn.net>
---
 Documentation/admin-guide/LSM/index.rst       |   1 +
 Documentation/admin-guide/LSM/ipe.rst         | 508 ++++++++++++++++++
 .../admin-guide/kernel-parameters.txt         |  12 +
 MAINTAINERS                                   |   1 +
 4 files changed, 522 insertions(+)
 create mode 100644 Documentation/admin-guide/LSM/ipe.rst
diff --git a/Documentation/admin-guide/LSM/index.rst b/Documentation/admin-guide/LSM/index.rst
index a6ba95fbaa9f..ce63be6d64ad 100644
--- a/Documentation/admin-guide/LSM/index.rst
+++ b/Documentation/admin-guide/LSM/index.rst
@@ -47,3 +47,4 @@ subdirectories.
    tomoyo
    Yama
    SafeSetID
+   ipe
diff --git a/Documentation/admin-guide/LSM/ipe.rst b/Documentation/admin-guide/LSM/ipe.rst
new file mode 100644
index 000000000000..2e6610c4a134
--- /dev/null
+++ b/Documentation/admin-guide/LSM/ipe.rst
@@ -0,0 +1,508 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+Integrity Policy Enforcement (IPE)
+==================================
+
+Overview
+--------
+
+IPE is a Linux Security Module which allows for a configurable policy
+to enforce integrity requirements on the whole system. It attempts to
+solve the issue of Code Integrity: that any code being executed (or
+files being read), are identical to the version that was built by a
+trusted source.
+
+There are multiple implementations within the Linux kernel that solve
+some measure of integrity verification. For instance, device-mapper
+verity, which ensures integrity for a block device, and fs-verity which
+is a system that ensures integrity for a filesystem. What these
+implementations lack is a measure of run-time verification that binaries
+are sourced from these locations. IPE aims to address this gap.
+
+IPE is separated between two major components: A configurable policy,
+provided by the LSM ("IPE Core"), and deterministic attributes provided
+by the kernel to evaluate files against, ("IPE Properties").
+
+Use Cases
+---------
+
+IPE is designed for use is an embedded device with a specific purpose
+(e.g. network firewall device in a data center), where all software and
+configuration is built and provisioned by the owner.
+
+Ideally, a system which leverages IPE is not intended for general
+purpose computing and does not utilize any software or configuration
+built by a third party. An ideal system to leverage IPE has both mutable
+and immutable components, however, all binary executable code is
+immutable.
+
+For the highest level of security, platform firmware should verify the
+the kernel and optionally the root filesystem (e.g. via U-Boot verified
+boot). This allows the entire system to be integrity verified.
+
+Known Gaps
+----------
+
+IPE cannot verify the integrity of anonymous executable memory, such as
+the trampolines created by gcc closures and libffi, or JIT'd code.
+Unfortunately, as this is dynamically generated code, there is no way
+for IPE to detect that this code has not been tampered with in
+transition from where it was built, to where it is running. As a result,
+IPE is incapable of tackling this problem for dynamically generated
+code.
+
+IPE cannot verify the integrity of interpreted languages' programs when
+these scripts invoked via ``<interpreter> <file>``. This is because the
+way interpreters execute these files, the scripts themselves are not
+evaluated as executable code through one of IPE's hooks. Interpreters
+can be enlightened to the usage of IPE by trying to mmap a file into
+executable memory (+X), after opening the file and responding to the
+error code appropriately. This also applies to included files, or high
+value files, such as configuration files of critical system components.
+This specific gap is planned on being addressed within IPE.
+
+Threat Model
+------------
+
+The threat type addressed by IPE is tampering of executable user-land
+code beyond the initially booted kernel, and the initial verification of
+kernel modules that are loaded in userland through ``modprobe`` or
+``insmod``.
+
+Tampering violates the property of integrity. IPE's role in mitigating
+this threat is to verify the integrity (and authenticity) of all
+executable code and to deny their use if integrity verification fails.
+IPE generates audit logs which may be utilized to detect integrity
+verification failures.
+
+Tampering threat scenarios include modification or replacement of
+executable code by a range of actors including:
+
+-  Insiders with physical access to the hardware
+-  Insiders with local network access to the system
+-  Insiders with access to the deployment system
+-  Compromised internal systems under external control
+-  Malicious end users of the system
+-  Compromised end users of the system
+-  Remote (external) compromise of the system
+
+IPE does not mitigate threats arising from malicious authorized
+developers, or compromised developer tools used by authorized
+developers. Additionally, IPE draws hard security boundary between user
+mode and kernel mode. As a result, IPE does not provide any protections
+against a kernel level exploit, and a kernel-level exploit can disable
+or tamper with IPE's protections.
+
+The root of trust for all of IPE's verifications is the
+``SYSTEM_TRUSTED_KEYRING``.
+
+IPE Core
+--------
+
+IPE Policy
+~~~~~~~~~~
+
+IPE policy is designed to be both forward compatible and backwards
+compatible. There is one required line, at the top of the policy,
+indicating the policy name, and the policy version, for instance::
+
+   policy_name="Ex Policy" policy_version=0.0.0
+
+The policy name is a unique key identifying this policy in a human
+readable name. This is used to create nodes under securityfs as well as
+uniquely identify policies to deploy new policies vs update existing
+policies.
+
+The policy version indicates the current version of the policy (NOT the
+policy syntax version). This is used to prevent roll-back of policy to
+potentially insecure previous versions of the policy.
+
+The next portion of IPE policy, are rules. Rules are formed by key=value
+pairs, known as properties. IPE rules require two properties: "action",
+which determines what IPE does when it encounters a match against the
+rule, and "op", which determines when that rule should be evaluated.
+Thus, a minimal rule is::
+
+   op=EXECUTE action=ALLOW
+
+This example will allow any execution. Additional properties are used to
+restrict attributes about the files being evaluated. These properties
+are intended to be deterministic attributes that are resident in the
+kernel.
+
+Order does not matter for the rule's properties - they can be listed in
+any order, however it is encouraged to have the "op" property be first,
+and the "action" property be last for readability. Rules are evaluated
+top-to-bottom. As a result, any revocation rules, or denies should be
+placed early in the file to ensure that these rules are evaluated before
+as rule with "action=ALLOW" is hit.
+
+IPE policy is designed to be forward compatible and backwards
+compatible, thus any failure to parse a rule will result in the line
+being ignored, and a warning being emitted. If backwards compatibility
+is not required, the kernel command line parameter and sysctl,
+``ipe.strict_parse`` can be enabled, which will cause these warnings to
+be fatal.
+
+IPE policy supports comments. The character '#' will function as a
+comment, ignoring all characters to the right of '#' until the newline.
+
+The default behavior of IPE evaluations can also be expressed in policy,
+through the ``DEFAULT`` statement. This can be done at a global level,
+or a per-operation level::
+
+   # Global
+   DEFAULT action=ALLOW
+
+   # Operation Specific
+   DEFAULT op=EXECUTE action=ALLOW
+
+A default must be set for all known operations in IPE. If you want to
+preserve older policies being compatible with newer kernels that can introduce
+new operations, please set a global default of 'ALLOW', and override the
+defaults on a per-operation basis.
+
+With configurable policy-based LSMs, there's several issues with
+enforcing the configurable policies at startup, around reading and
+parsing the policy:
+
+1. The kernel *should* not read files from userland, so directly reading
+   the policy file is prohibited.
+2. The kernel command line has a character limit, and one kernel module
+   should not reserve the entire character limit for its own
+   configuration.
+3. There are various boot loaders in the kernel ecosystem, so handing
+   off a memory block would be costly to maintain.
+
+As a result, IPE has addressed this problem through a concept of a "boot
+policy". A boot policy is a minimal policy, compiled into the kernel.
+This policy is intended to get the system to a state where userland is
+setup and ready to receive commands, at which point a more complex
+policy ("user policies") can be deployed via securityfs. The boot policy
+can be specified via the Kconfig, ``SECURITY_IPE_BOOT_POLICY``, which
+accepts a path to a plain-text version of the IPE policy to apply. This
+policy will be compiled into the kernel. If not specified, IPE will be
+disabled until a policy is deployed through securityfs, and activated
+through sysfs.
+
+Deploying Policies
+^^^^^^^^^^^^^^^^^^
+
+User policies as explained above, are policies that are deployed from
+userland, through securityfs. These policies are signed to enforce some
+level of authorization of the policies (prohibiting an attacker from
+gaining root, and deploying an "allow all" policy), through the PKCS#7
+enveloped data format. These policies must be signed by a certificate
+that chains to the ``SYSTEM_TRUSTED_KEYRING``. Through openssl, the
+signing can be done via::
+
+   openssl smime -sign -in "$MY_POLICY" -signer "$MY_CERTIFICATE" \
+     -inkey "$MY_PRIVATE_KEY" -binary -outform der -noattr -nodetach \
+     -out "$MY_POLICY.p7s"
+
+Deploying the policies is done through securityfs, through the
+``new_policy`` node. To deploy a policy, simply cat the file into the
+securityfs node::
+
+   cat "$MY_POLICY.p7s" > /sys/kernel/security/ipe/new_policy
+
+Upon success, this will create one subdirectory under
+``/sys/kernel/security/ipe/policies/``. The subdirectory will be the
+``policy_name`` field of the policy deployed, so for the example above,
+the directory will be ``/sys/kernel/security/ipe/policies/Ex\ Policy``.
+Within this directory, there will be four files: ``raw``, ``content``,
+``active``, and ``delete``.
+
+The ``raw`` file is rw, reading will provide the raw PKCS#7 data that
+was provided to the kernel, representing the policy. Writing, will
+deploy an in-place policy update - if this policy is the currently
+running policy, the new updated policy will replace it immediately upon
+success.
+
+The ``content`` file is read only. Reading will provide the PKCS#7 inner
+content of the policy, which will be the plain text policy.
+
+The ``active`` file is used to set a policy as the currently active policy.
+This file is rw, and accepts a value of ``"1"`` to set the policy as active.
+Since only a single policy can be active at one time, all other policies
+will be marked inactive.
+
+Similarly, the ``cat`` command above will result in an error upon
+syntactically invalid or untrusted policies. It will also error if a
+policy already exists with the same ``policy_name``. The write to the
+``raw`` node will error upon syntactically invalid, untrusted policies,
+or if the payload fails the version check. The write will also fail if
+the ``policy_name`` in the payload does not match the existing policy.
+
+Deploying these policies will *not* cause IPE to start enforcing this
+policy. Once deployment is successful, a policy can be marked as active,
+via ``/sys/kernel/security/ipe/$policy_name/active``. IPE will enforce
+whatever policy is marked as active. For our example, we can activate
+the ``Ex Policy`` via::
+
+   echo -n 1 > "/sys/kernel/security/ipe/Ex Policy/active"
+
+At which point, ``Ex Policy`` will now be the enforced policy on the
+system.
+
+.. NOTE::
+
+   The -n parameter is important, as it strips an additional newline.
+
+IPE also provides a way to delete policies. This can be done via the
+``delete`` securityfs node, ``/sys/kernel/security/ipe/$policy_name/delete``.
+Writing ``1`` to that file will delete that node::
+
+   echo -n 1 > "/sys/kernel/security/ipe/$policy_name/delete"
+
+There are two requirements to delete policies:
+
+1. The policy being deleted must not be the active policy.
+2. The policy being deleted must not be the boot policy.
+
+.. NOTE::
+
+   If a MAC system is enabled, all writes to ipe's securityfs nodes require
+   ``CAP_MAC_ADMIN``.
+
+Modes
+~~~~~
+
+IPE supports two modes of operation: permissive (similar to SELinux's
+permissive mode) and enforce. Permissive mode performs the same checks
+as enforce mode, and logs policy violations, but will not enforce the
+policy. This allows users to test policies before enforcing them.
+
+The default mode is enforce, and can be changed via the kernel command
+line parameter ``ipe.enforce=(0|1)``, or the securityfs node
+``/sys/kernel/security/ipe/enforce``. The ability to switch modes can
+be compiled out of the LSM via setting the Kconfig
+``CONFIG_SECURITY_IPE_PERMISSIVE_SWITCH`` to N.
+
+.. NOTE::
+
+   If a MAC system is enabled, all writes to ipe's securityfs nodes require
+   ``CAP_MAC_ADMIN``.
+
+Audit Events
+~~~~~~~~~~~~
+
+Success Auditing
+^^^^^^^^^^^^^^^^
+
+IPE supports success auditing. When enabled, all events that pass IPE
+policy and are not blocked will emit an audit event. This is disabled by
+default, and can be enabled via the kernel command line
+``ipe.success_audit=(0|1)`` or the securityfs node,
+``/sys/kernel/security/ipe/success_audit``.
+
+This is very noisy, as IPE will check every user-mode binary on the
+system, but is useful for debugging policies.
+
+.. NOTE::
+
+   If a MAC system is enabled, all writes to ipe's securityfs nodes require
+   ``CAP_MAC_ADMIN``.
+
+IPE Properties
+--------------
+
+As explained above, IPE properties are ``key=value`` pairs expressed in
+IPE policy. Two properties are built-into the policy parser: 'op' and
+'action'. The other properties are determinstic attributes to express
+across files. Currently those properties are: 'boot_verified',
+'dmverity_signature', 'dmverity_roothash'. A description of all
+properties supported by IPE are listed below:
+
+op
+~~
+
+Indicates the operation for a rule to apply to. Must be in every rule.
+IPE supports the following operations:
+
+Version 1
+^^^^^^^^^
+
+``EXECUTE``
+
+   Pertains to any file attempting to be executed, or loaded as an
+   executable.
+
+``FIRMWARE``:
+
+   Pertains to firmware being loaded via the firmware_class interface.
+   This covers both the preallocated buffer and the firmware file
+   itself.
+
+``KMODULE``:
+
+   Pertains to loading kernel modules via ``modprobe`` or ``insmod``.
+
+``KEXEC_IMAGE``:
+
+   Pertains to kernel images loading via ``kexec``.
+
+``KEXEC_INITRAMFS``
+
+   Pertains to initrd images loading via ``kexec --initrd``.
+
+``POLICY``:
+
+   Controls loading IMA policies through the
+   ``/sys/kernel/security/ima/policy`` securityfs entry.
+
+``X509_CERT``:
+
+   Controls loading IMA certificates through the Kconfigs,
+   ``CONFIG_IMA_X509_PATH`` and ``CONFIG_EVM_X509_PATH``.
+
+``KERNEL_READ``:
+
+   Short hand for all of the following: ``FIRMWARE``, ``KMODULE``,
+   ``KEXEC_IMAGE``, ``KEXEC_INITRAMFS``, ``POLICY``, and ``X509_CERT``.
+
+action
+~~~~~~
+
+Version 1
+^^^^^^^^^
+
+Determines what IPE should do when a rule matches. Must be in every
+rule. Can be one of:
+
+``ALLOW``:
+
+   If the rule matches, explicitly allow the call to proceed without
+   executing any more rules.
+
+``DENY``:
+
+   If the rule matches, explicitly prohibit the call from proceeding
+   without executing any more rules.
+
+boot_verified
+~~~~~~~~~~~~~
+
+Version 1
+^^^^^^^^^
+
+This property can be utilized for authorization of the first super-block
+that executes a file. This is almost always init. Typically this is used
+for systems with an initramfs or other initial disk, where this is unmounted
+before the system becomes available, and is not covered by any other property.
+This property is controlled by the Kconfig, ``CONFIG_IPE_BOOT_PROP``. The
+format of this property is::
+
+       boot_verified=(TRUE|FALSE)
+
+
+.. WARNING::
+
+  This property will trust any disk where the first execution occurs
+  evaluation occurs. If you do not have a startup disk that is
+  unpacked and unmounted (like initramfs), then it will automatically
+  trust the root filesystem and potentially overauthorize the entire
+  disk.
+
+dmverity_roothash
+~~~~~~~~~~~~~~~~~
+
+Version 1
+^^^^^^^^^
+
+This property can be utilized for authorization or revocation of
+specific dm-verity volumes, identified via root hash. It has a
+dependency on the DM_VERITY module. This property is controlled by the
+property: ``CONFIG_IPE_DM_VERITY_ROOTHASH``. The format of this property
+is::
+
+   dmverity_roothash=HashHexDigest
+
+dmverity_signature
+~~~~~~~~~~~~~~~~~~
+
+Version 1
+^^^^^^^^^
+
+This property can be utilized for authorization of all dm-verity volumes
+that have a signed roothash that chains to the system trusted keyring.
+It has a dependency on the ``DM_VERITY_VERIFY_ROOTHASH_SIG`` Kconfig.
+This property is controlled by the Kconfig:
+``CONFIG_IPE_DM_VERITY_SIGNATURE``. The format of this property is::
+
+   dmverity_signature=(TRUE|FALSE)
+
+Policy Examples
+---------------
+
+Allow all
+~~~~~~~~~
+
+::
+
+   policy_name="Allow All" policy_version=0.0.0
+   DEFAULT action=ALLOW
+
+Allow only initial superblock
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+::
+
+   policy_name="Allow All Initial SB" policy_version=0.0.0
+   DEFAULT action=DENY
+
+   op=EXECUTE boot_verified=TRUE action=ALLOW
+
+Allow any signed dm-verity volume and the initial superblock
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+::
+
+   policy_name="AllowSignedAndInitial" policy_version=0.0.0
+   DEFAULT action=DENY
+
+   op=EXECUTE boot_verified=TRUE action=ALLOW
+   op=EXECUTE dmverity_signature=TRUE action=ALLOW
+
+Prohibit execution from a specific dm-verity volume
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+::
+
+   policy_name="AllowSignedAndInitial" policy_version=0.0.0
+   DEFAULT action=DENY
+
+   op=EXECUTE dmverity_roothash=401fcec5944823ae12f62726e8184407a5fa9599783f030dec146938 action=DENY
+   op=EXECUTE boot_verified=TRUE action=ALLOW
+   op=EXECUTE dmverity_signature=TRUE action=ALLOW
+
+Allow only a specific dm-verity volume
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+::
+
+   policy_name="AllowSignedAndInitial" policy_version=0.0.0
+   DEFAULT action=DENY
+
+   op=EXECUTE dmverity_roothash=401fcec5944823ae12f62726e8184407a5fa9599783f030dec146938 action=ALLOW
+
+External Information
+--------------------
+
+Please see the github repository at: https://github.com/microsoft/ipe
+
+FAQ
+---
+
+Q: What's the difference between other LSMs which provide integrity
+verification (i.e. IMA)?
+
+A: IPE differs from other LSMs which provide integrity checking, as it
+has no dependency on the filesystem metadata itself. The attributes that
+IPE checks are deterministic properties that exist solely in the kernel.
+Additionally, IPE provides no additional mechanisms of verifying these
+files (e.g. IMA Signatures) - all of the attributes of verifying files
+are existing features within the kernel.
+
+Additionally, IPE is completely restricted to integrity. It offers no
+measurement or attestation features, which IMA addresses.
diff --git a/Documentation/admin-guide/kernel-parameters.txt b/Documentation/admin-guide/kernel-parameters.txt
index fb95fad81c79..5309813f25f7 100644
--- a/Documentation/admin-guide/kernel-parameters.txt
+++ b/Documentation/admin-guide/kernel-parameters.txt
@@ -1953,6 +1953,18 @@
 	ipcmni_extend	[KNL] Extend the maximum number of unique System V
 			IPC identifiers from 32,768 to 16,777,216.
 
+	ipe.enforce=	[IPE]
+			Format: <bool>
+			Determine whether IPE starts in permissive (0) or
+			enforce (1) mode. The default is enforce.
+
+	ipe.success_audit=
+			[IPE]
+			Format: <bool>
+			Start IPE with success auditing enabled, emitting
+			an audit event when a binary is allowed. The default
+			is 0.
+
 	irqaffinity=	[SMP] Set the default irq affinity mask
 			The argument is a cpu list, as described above.
 
diff --git a/MAINTAINERS b/MAINTAINERS
index e817e4bfeae3..b448fd435712 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -8584,6 +8584,7 @@ INTEGRITY POLICY ENFORCEMENT (IPE)
 M:	Deven Bowers <deven.desai@linux.microsoft.com>
 L:	linux-integrity@vger.kernel.org
 S:	Supported
+F:	Documentation/admin-guide/LSM/ipe.rst
 F:	scripts/ipe/
 F:	security/ipe/
 
-- 
2.27.0
^ permalink raw reply related	[flat|nested] 12+ messages in thread
- * [RFC PATCH v6 11/11] cleanup: uapi/linux/audit.h
  2020-07-30  0:31 [RFC PATCH v6 00/11] Integrity Policy Enforcement LSM (IPE) Deven Bowers
                   ` (9 preceding siblings ...)
  2020-07-30  0:31 ` [RFC PATCH v6 10/11] documentation: add ipe documentation Deven Bowers
@ 2020-07-30  0:31 ` Deven Bowers
  10 siblings, 0 replies; 12+ messages in thread
From: Deven Bowers @ 2020-07-30  0:31 UTC (permalink / raw)
  To: agk, axboe, snitzer, jmorris, serge, zohar, viro, paul, eparis,
	jannh, dm-devel, linux-integrity, linux-security-module,
	linux-fsdevel, linux-block, linux-audit
  Cc: tyhicks, linux-kernel, corbet, sashal, jaskarankhurana, mdsakib,
	nramas, pasha.tatashin
Remove trailing whitespaces and align the integrity #defines in
linux/uapi/audit.h
Signed-off-by: Deven Bowers <deven.desai@linux.microsoft.com>
---
 include/uapi/linux/audit.h | 32 ++++++++++++++++----------------
 1 file changed, 16 insertions(+), 16 deletions(-)
diff --git a/include/uapi/linux/audit.h b/include/uapi/linux/audit.h
index 5a634cca1d42..609b4a5e8a80 100644
--- a/include/uapi/linux/audit.h
+++ b/include/uapi/linux/audit.h
@@ -48,7 +48,7 @@
  * 2500 - 2999 future user space (maybe integrity labels and related events)
  *
  * Messages from 1000-1199 are bi-directional. 1200-1299 & 2100 - 2999 are
- * exclusively user space. 1300-2099 is kernel --> user space 
+ * exclusively user space. 1300-2099 is kernel --> user space
  * communication.
  */
 #define AUDIT_GET		1000	/* Get status */
@@ -78,7 +78,7 @@
 #define AUDIT_LAST_USER_MSG	1199
 #define AUDIT_FIRST_USER_MSG2	2100	/* More user space messages */
 #define AUDIT_LAST_USER_MSG2	2999
- 
+
 #define AUDIT_DAEMON_START      1200    /* Daemon startup record */
 #define AUDIT_DAEMON_END        1201    /* Daemon normal stop record */
 #define AUDIT_DAEMON_ABORT      1202    /* Daemon error stop record */
@@ -140,20 +140,20 @@
 #define AUDIT_MAC_CALIPSO_ADD	1418	/* NetLabel: add CALIPSO DOI entry */
 #define AUDIT_MAC_CALIPSO_DEL	1419	/* NetLabel: del CALIPSO DOI entry */
 
-#define AUDIT_FIRST_KERN_ANOM_MSG   1700
-#define AUDIT_LAST_KERN_ANOM_MSG    1799
-#define AUDIT_ANOM_PROMISCUOUS      1700 /* Device changed promiscuous mode */
-#define AUDIT_ANOM_ABEND            1701 /* Process ended abnormally */
-#define AUDIT_ANOM_LINK		    1702 /* Suspicious use of file links */
-#define AUDIT_ANOM_CREAT	    1703 /* Suspicious file creation */
-#define AUDIT_INTEGRITY_DATA	    1800 /* Data integrity verification */
-#define AUDIT_INTEGRITY_METADATA    1801 /* Metadata integrity verification */
-#define AUDIT_INTEGRITY_STATUS	    1802 /* Integrity enable status */
-#define AUDIT_INTEGRITY_HASH	    1803 /* Integrity HASH type */
-#define AUDIT_INTEGRITY_PCR	    1804 /* PCR invalidation msgs */
-#define AUDIT_INTEGRITY_RULE	    1805 /* policy rule */
-#define AUDIT_INTEGRITY_EVM_XATTR   1806 /* New EVM-covered xattr */
-#define AUDIT_INTEGRITY_POLICY_RULE 1807 /* IMA policy rules */
+#define AUDIT_FIRST_KERN_ANOM_MSG	1700
+#define AUDIT_LAST_KERN_ANOM_MSG	1799
+#define AUDIT_ANOM_PROMISCUOUS		1700 /* Device changed promiscuous mode */
+#define AUDIT_ANOM_ABEND		1701 /* Process ended abnormally */
+#define AUDIT_ANOM_LINK			1702 /* Suspicious use of file links */
+#define AUDIT_ANOM_CREAT		1703 /* Suspicious file creation */
+#define AUDIT_INTEGRITY_DATA		1800 /* Data integrity verification */
+#define AUDIT_INTEGRITY_METADATA	1801 /* Metadata integrity verification */
+#define AUDIT_INTEGRITY_STATUS		1802 /* Integrity enable status */
+#define AUDIT_INTEGRITY_HASH		1803 /* Integrity HASH type */
+#define AUDIT_INTEGRITY_PCR		1804 /* PCR invalidation msgs */
+#define AUDIT_INTEGRITY_RULE		1805 /* policy rule */
+#define AUDIT_INTEGRITY_EVM_XATTR	1806 /* New EVM-covered xattr */
+#define AUDIT_INTEGRITY_POLICY_RULE	1807 /* IMA policy rules */
 #define AUDIT_INTEGRITY_POLICY_LOAD	1808 /* IPE Policy Load */
 #define AUDIT_INTEGRITY_POLICY_ACTIVATE	1809 /* IPE Policy Activation */
 #define AUDIT_INTEGRITY_EVENT		1810 /* IPE Evaluation Event */
-- 
2.27.0
^ permalink raw reply related	[flat|nested] 12+ messages in thread