[RFC PATCH v8 02/17] ipe: add policy parser
Deven Bowers
deven.desai at linux.microsoft.com
Wed Jun 8 19:01:14 UTC 2022
IPE's interpretation of the what the user trusts is accomplished through
its policy. IPE's design is to not provide support for a single trust
provider, but to support multiple providers to enable the end-user to
choose the best one to seek their needs.
This requires the policy to be rather flexible and modular so that
integrity providers, like fs-verity, dm-verity, dm-integrity, or
some other system, can plug into the policy with minimal code changes,
and IPE can
Signed-off-by: Deven Bowers <deven.desai at linux.microsoft.com>
---
v2:
+ Split evaluation loop, access control hooks,
and evaluation loop from policy parser and userspace
interface to pass mailing list character limit
v3:
+ Move policy load and activation audit event to 03/12
+ Fix a potential panic when a policy failed to load.
+ use pr_warn for a failure to parse instead of an
audit record
+ Remove comments from headers
+ Add lockdep assertions to ipe_update_active_policy and
ipe_activate_policy
+ Fix up warnings with checkpatch --strict
+ Use file_ns_capable for CAP_MAC_ADMIN for securityfs
nodes.
+ Use memdup_user instead of kzalloc+simple_write_to_buffer.
+ Remove strict_parse command line parameter, as it is added
by the sysctl command line.
+ Prefix extern variables with ipe_
v4:
+ Remove securityfs to reverse-dependency
+ Add SHA1 reverse dependency.
+ Add versioning scheme for IPE properties, and associated
interface to query the versioning scheme.
+ Cause a parser to always return an error on unknown syntax.
+ Remove strict_parse option
+ Change active_policy interface from sysctl, to securityfs,
and change scheme.
v5:
+ Cause an error if a default action is not defined for each
operaiton.
+ Minor function renames
v6:
+ No changes
v7:
+ Further split parser and userspace interface into two
separate commits, for easier review.
+ Refactor policy parser to make code cleaner via introducing a
more modular design, for easier extension of policy, and
easier review.
v8:
+ remove unnecessary pr_info emission on parser loading
+ add explicit newline to the pr_err emitted when a parser
fails to load.
---
include/asm-generic/vmlinux.lds.h | 16 +
security/ipe/Makefile | 6 +
security/ipe/ipe.c | 61 ++
security/ipe/ipe.h | 5 +
security/ipe/ipe_parser.h | 59 ++
security/ipe/modules.c | 109 +++
security/ipe/modules.h | 17 +
security/ipe/modules/ipe_module.h | 33 +
security/ipe/parsers.c | 143 ++++
security/ipe/parsers/Makefile | 12 +
security/ipe/parsers/default.c | 106 +++
security/ipe/parsers/policy_header.c | 126 ++++
security/ipe/policy.c | 946 +++++++++++++++++++++++++++
security/ipe/policy.h | 97 +++
14 files changed, 1736 insertions(+)
create mode 100644 security/ipe/ipe_parser.h
create mode 100644 security/ipe/modules.c
create mode 100644 security/ipe/modules.h
create mode 100644 security/ipe/modules/ipe_module.h
create mode 100644 security/ipe/parsers.c
create mode 100644 security/ipe/parsers/Makefile
create mode 100644 security/ipe/parsers/default.c
create mode 100644 security/ipe/parsers/policy_header.c
create mode 100644 security/ipe/policy.c
create mode 100644 security/ipe/policy.h
diff --git a/include/asm-generic/vmlinux.lds.h b/include/asm-generic/vmlinux.lds.h
index 7515a465ec03..8549b98d0cf6 100644
--- a/include/asm-generic/vmlinux.lds.h
+++ b/include/asm-generic/vmlinux.lds.h
@@ -284,6 +284,20 @@
#define EARLY_LSM_TABLE()
#endif
+#ifdef CONFIG_SECURITY_IPE
+#define IPE_PARSER_TABLE() . = ALIGN(8); \
+ __start_ipe_parsers = .; \
+ KEEP(*(.ipe_parsers)) \
+ __end_ipe_parsers = .;
+#define IPE_MODULE_TABLE() . = ALIGN(8); \
+ __start_ipe_modules = .; \
+ KEEP(*(.ipe_modules)) \
+ __end_ipe_modules = .;
+#else
+#define IPE_PARSER_TABLE()
+#define IPE_MODULE_TABLE()
+#endif
+
#define ___OF_TABLE(cfg, name) _OF_TABLE_##cfg(name)
#define __OF_TABLE(cfg, name) ___OF_TABLE(cfg, name)
#define OF_TABLE(cfg, name) __OF_TABLE(IS_ENABLED(cfg), name)
@@ -436,6 +450,8 @@
KEEP(*(__tracepoints_ptrs)) /* Tracepoints: pointer array */ \
__stop___tracepoints_ptrs = .; \
*(__tracepoints_strings)/* Tracepoints: strings */ \
+ IPE_PARSER_TABLE() \
+ IPE_MODULE_TABLE() \
} \
\
.rodata1 : AT(ADDR(.rodata1) - LOAD_OFFSET) { \
diff --git a/security/ipe/Makefile b/security/ipe/Makefile
index ba3df729e252..9a97efd8a190 100644
--- a/security/ipe/Makefile
+++ b/security/ipe/Makefile
@@ -5,7 +5,13 @@
# Makefile for building the IPE module as part of the kernel tree.
#
+ccflags-y := -I$(srctree)/security/ipe/modules
+
obj-$(CONFIG_SECURITY_IPE) += \
ctx.o \
hooks.o \
ipe.o \
+ modules.o \
+ parsers/ \
+ parsers.o \
+ policy.o \
diff --git a/security/ipe/ipe.c b/security/ipe/ipe.c
index 8a1e0b1c7240..3f387f4ab38a 100644
--- a/security/ipe/ipe.c
+++ b/security/ipe/ipe.c
@@ -6,6 +6,9 @@
#include "ipe.h"
#include "ctx.h"
#include "hooks.h"
+#include "ipe_parser.h"
+#include "modules/ipe_module.h"
+#include "modules.h"
#include <linux/fs.h>
#include <linux/sched.h>
@@ -40,6 +43,56 @@ struct ipe_context __rcu **ipe_tsk_ctx(struct task_struct *tsk)
return tsk->security + ipe_blobs.lbs_task;
}
+/*
+ * load_parsers: Load all the parsers compiled into IPE. This needs
+ * to be called prior to the boot policy being loaded.
+ *
+ * Return:
+ * 0 - OK
+ * !0 - Error
+ */
+static int load_parsers(void)
+{
+ int rc = 0;
+ struct ipe_parser *parser;
+
+ for (parser = __start_ipe_parsers; parser < __end_ipe_parsers; ++parser) {
+ rc = ipe_register_parser(parser);
+ if (rc) {
+ pr_err("failed to initialize '%s'", parser->first_token);
+ return rc;
+ }
+
+ pr_info("initialized parser module '%s'", parser->first_token);
+ }
+
+ return 0;
+}
+
+/**
+ * load_modules: Load all the modules compiled into IPE. This needs
+ * to be called prior to the boot policy being loaded.
+ *
+ * Return:
+ * 0 - OK
+ * !0 - Error
+ */
+static int load_modules(void)
+{
+ int rc = 0;
+ struct ipe_module *m;
+
+ for (m = __start_ipe_modules; m < __end_ipe_modules; ++m) {
+ rc = ipe_register_module(m);
+ if (rc) {
+ pr_err("failed to initialize '%s'\n", m->name);
+ return rc;
+ }
+ }
+
+ return 0;
+}
+
/**
* ipe_init: Entry point of IPE.
*
@@ -57,6 +110,14 @@ static int __init ipe_init(void)
{
int rc = 0;
+ rc = load_parsers();
+ if (rc)
+ return rc;
+
+ rc = load_modules();
+ if (rc)
+ return rc;
+
rc = ipe_init_ctx();
if (rc)
return rc;
diff --git a/security/ipe/ipe.h b/security/ipe/ipe.h
index 152ae28f3cdd..417b474e0543 100644
--- a/security/ipe/ipe.h
+++ b/security/ipe/ipe.h
@@ -9,9 +9,14 @@
#define pr_fmt(fmt) "IPE " fmt "\n"
#include "ctx.h"
+#include "ipe_parser.h"
+#include "modules/ipe_module.h"
#include <linux/types.h>
#include <linux/sched.h>
#include <linux/lsm_hooks.h>
+extern struct ipe_parser __start_ipe_parsers[], __end_ipe_parsers[];
+extern struct ipe_module __start_ipe_modules[], __end_ipe_modules[];
+
#endif /* IPE_H */
diff --git a/security/ipe/ipe_parser.h b/security/ipe/ipe_parser.h
new file mode 100644
index 000000000000..f7c5c11bde44
--- /dev/null
+++ b/security/ipe/ipe_parser.h
@@ -0,0 +1,59 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) Microsoft Corporation. All rights reserved.
+ */
+#ifndef IPE_PARSER_H
+#define IPE_PARSER_H
+
+#include "policy.h"
+
+#include <linux/list.h>
+#include <linux/types.h>
+
+/*
+ * Struct used to define internal parsers that effect the policy,
+ * but do not belong as policy modules, as they are not used to make
+ * decisions in the event loop, and only effect the internal workings
+ * of IPE.
+ *
+ * These structures are used in pass2, and policy deallocation.
+ */
+struct ipe_parser {
+ u8 version;
+ const char *first_token;
+
+ int (*parse)(const struct ipe_policy_line *line,
+ struct ipe_parsed_policy *pol);
+ int (*free)(struct ipe_parsed_policy *pol);
+ int (*validate)(const struct ipe_parsed_policy *pol);
+};
+
+int ipe_parse_op(const struct ipe_policy_token *tok,
+ enum ipe_operation *op);
+
+int ipe_parse_action(const struct ipe_policy_token *tok,
+ enum ipe_action *action);
+
+/*
+ * Optional struct to make structured parsers easier.
+ */
+struct ipe_token_parser {
+ const char *key;
+ int (*parse_token)(const struct ipe_policy_token *t,
+ struct ipe_parsed_policy *p);
+};
+
+const struct ipe_parser *ipe_lookup_parser(const char *first_token);
+
+int ipe_for_each_parser(int (*view)(const struct ipe_parser *parser,
+ void *ctx),
+ void *ctx);
+
+int ipe_register_parser(struct ipe_parser *p);
+
+#define IPE_PARSER(parser) \
+ static struct ipe_parser __ipe_parser_##parser \
+ __used __section(".ipe_parsers") \
+ __aligned(sizeof(unsigned long))
+
+#endif /* IPE_PARSER_MODULE_H */
diff --git a/security/ipe/modules.c b/security/ipe/modules.c
new file mode 100644
index 000000000000..fb100c14cce5
--- /dev/null
+++ b/security/ipe/modules.c
@@ -0,0 +1,109 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) Microsoft Corporation. All rights reserved.
+ */
+
+#include "ipe.h"
+#include "modules.h"
+
+#include <linux/slab.h>
+#include <linux/types.h>
+#include <linux/rbtree.h>
+
+static struct rb_root module_root = RB_ROOT;
+
+struct module_container {
+ struct rb_node node;
+ const struct ipe_module *mod;
+};
+
+/**
+ * cmp_node: Comparator for a node in the module lookup tree.
+ * @n: First node to compare
+ * @nn: Second node to compare
+ *
+ * Return:
+ * <0 - @n's key is lexigraphically before @nn.
+ * 0 - n's key is identical to @nn
+ * >0 - n's key is legxigraphically after @nn
+ */
+static int cmp_node(struct rb_node *n, const struct rb_node *nn)
+{
+ const struct module_container *c1;
+ const struct module_container *c2;
+
+ c1 = container_of(n, struct module_container, node);
+ c2 = container_of(nn, struct module_container, node);
+
+ return strcmp(c1->mod->name, c2->mod->name);
+}
+
+/**
+ * cmp_key: Comparator to find a module in the tree by key.
+ * @key: Supplies a pointer to a null-terminated string key
+ * @n: Node to compare @key against
+ *
+ * Return:
+ * <0 - Desired node is to the left of @n
+ * 0 - @n is the desired node
+ * >0 - Desired node is to the right of @n
+ */
+static int cmp_key(const void *key, const struct rb_node *n)
+{
+ struct module_container *mod;
+
+ mod = container_of(n, struct module_container, node);
+
+ return strcmp((const char *)key, mod->mod->name);
+}
+
+/**
+ * ipe_lookup_module: Attempt to find a ipe_pmodule structure by @key.
+ * @key: The key to look for in the tree.
+ *
+ * Return:
+ * !NULL - OK
+ * NULL - No property exists under @key
+ */
+const struct ipe_module *ipe_lookup_module(const char *key)
+{
+ struct rb_node *n;
+
+ n = rb_find(key, &module_root, cmp_key);
+ if (!n)
+ return NULL;
+
+ return container_of(n, struct module_container, node)->mod;
+}
+
+/**
+ * ipe_register_module: Register a policy module to be used in IPE's policy.
+ * @m: Module to register.
+ *
+ * This function allows parsers (policy constructs that represent integrations
+ * with other subsystems, to be leveraged in rules) to be leveraged in IPE policy.
+ * This must be called prior to any policies being loaded.
+ *
+ * Return:
+ * 0 - OK
+ * !0 - Error
+ */
+int ipe_register_module(struct ipe_module *m)
+{
+ struct rb_node *n = NULL;
+ struct module_container *c = NULL;
+
+ c = kzalloc(sizeof(*c), GFP_KERNEL);
+ if (!c)
+ return -ENOMEM;
+
+ c->mod = m;
+
+ n = rb_find_add(&c->node, &module_root, cmp_node);
+ if (n) {
+ kfree(c);
+ return -EEXIST;
+ }
+
+ return 0;
+}
diff --git a/security/ipe/modules.h b/security/ipe/modules.h
new file mode 100644
index 000000000000..7b897bdd870b
--- /dev/null
+++ b/security/ipe/modules.h
@@ -0,0 +1,17 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) Microsoft Corporation. All rights reserved.
+ */
+#ifndef IPE_MODULES_H
+#define IPE_MODULES_H
+
+#include "ipe.h"
+#include "ipe_module.h"
+
+#include <linux/types.h>
+#include <linux/rbtree.h>
+
+const struct ipe_module *ipe_lookup_module(const char *key);
+int ipe_register_module(struct ipe_module *m);
+
+#endif /* IPE_MODULES_H */
diff --git a/security/ipe/modules/ipe_module.h b/security/ipe/modules/ipe_module.h
new file mode 100644
index 000000000000..c4e98b548df3
--- /dev/null
+++ b/security/ipe/modules/ipe_module.h
@@ -0,0 +1,33 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) Microsoft Corporation. All rights reserved.
+ */
+#ifndef IPE_MODULE_H
+#define IPE_MODULE_H
+
+#include <linux/types.h>
+
+/**
+ * ipe_module: definition of an extensible module for IPE properties.
+ * These structures are used to implement 'key=value' pairs
+ * in IPE policy, which will be evaluated on every IPE policy
+ * evaluation.
+ *
+ * Integrity mechanisms should be define as a module, and modules
+ * should manage their own dependencies via KConfig. @name is both
+ * the key half of the key=value pair in the policy, and the unique
+ * identifier for the module.
+ */
+struct ipe_module {
+ const char *const name; /* required */
+ u16 version; /* required */
+ int (*parse)(const char *valstr, void **value); /* required */
+ void (*free)(void **value); /* optional */
+};
+
+#define IPE_MODULE(parser) \
+ static struct ipe_module __ipe_module_##parser \
+ __used __section(".ipe_modules") \
+ __aligned(sizeof(unsigned long))
+
+#endif /* IPE_MODULE_H */
diff --git a/security/ipe/parsers.c b/security/ipe/parsers.c
new file mode 100644
index 000000000000..87006270b9cd
--- /dev/null
+++ b/security/ipe/parsers.c
@@ -0,0 +1,143 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) Microsoft Corporation. All rights reserved.
+ */
+
+#include "policy.h"
+#include "ipe_parser.h"
+
+#include <linux/slab.h>
+#include <linux/types.h>
+#include <linux/rbtree.h>
+
+/*
+ * locking unnecessary, this is called by lsm_init,
+ * and is read-only after that point.
+ */
+static struct rb_root ipe_parser_root = RB_ROOT;
+
+struct parser_container {
+ struct rb_node node;
+ const struct ipe_parser *parser;
+};
+
+/**
+ * cmp_key: Comparator for the nodes within the parser tree by key
+ * @key: Supplies a the key to evaluate nodes against
+ * @n: Supplies a pointer to the node to compare.
+ *
+ * Return:
+ * <0 - @key is to the left of @n
+ * 0 - @key identifies @n
+ * >0 - @key is to the right of @n
+ */
+static int cmp_key(const void *key, const struct rb_node *n)
+{
+ const struct parser_container *node;
+
+ node = container_of(n, struct parser_container, node);
+
+ return strcmp((const char *)key, node->parser->first_token);
+}
+
+/**
+ * cmp_node: Comparator for the nodes within the parser tree
+ * @n: Supplies a pointer to the node to compare
+ * @nn: Supplies a pointer to the another node to compare.
+ *
+ * Return:
+ * <0 - @n is lexigraphically before @nn
+ * 0 - @n is identical @nn
+ * >0 - @n is lexigraphically after @nn
+ */
+static int cmp_node(struct rb_node *n, const struct rb_node *nn)
+{
+ const struct parser_container *c1;
+ const struct parser_container *c2;
+
+ c1 = container_of(n, struct parser_container, node);
+ c2 = container_of(nn, struct parser_container, node);
+
+ return strcmp(c1->parser->first_token, c2->parser->first_token);
+}
+
+/**
+ * ipe_lookup_parser: Attempt to find a ipe_property structure by @first_token.
+ * @first_token: The key to look for in the tree.
+ *
+ * Return:
+ * !NULL - OK
+ * NULL - No property exists under @key
+ */
+const struct ipe_parser *ipe_lookup_parser(const char *first_token)
+{
+ struct rb_node *n;
+
+ n = rb_find(first_token, &ipe_parser_root, cmp_key);
+ if (!n)
+ return NULL;
+
+ return container_of(n, struct parser_container, node)->parser;
+}
+
+/**
+ * ipe_for_each_parser: Iterate over all currently-registered parsers
+ * 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_parser(int (*view)(const struct ipe_parser *parser,
+ void *ctx),
+ void *ctx)
+{
+ int rc = 0;
+ struct rb_node *node;
+ struct parser_container *val;
+
+ for (node = rb_first(&ipe_parser_root); node; node = rb_next(node)) {
+ val = container_of(node, struct parser_container, node);
+
+ rc = view(val->parser, ctx);
+ if (rc)
+ return rc;
+ }
+
+ return rc;
+}
+
+/**
+ * ipe_register_parser: Register a parser to be used in IPE's policy.
+ * @p: Parser to register.
+ *
+ * This function allows parsers (policy constructs that effect IPE's
+ * internal functionality) to be leveraged in IPE policy. This must
+ * be called prior to any policies being loaded.
+ *
+ * Return:
+ * 0 - OK
+ * !0 - Error
+ */
+int ipe_register_parser(struct ipe_parser *p)
+{
+ struct rb_node *n = NULL;
+ struct parser_container *c = NULL;
+
+ c = kzalloc(sizeof(*c), GFP_KERNEL);
+ if (!c)
+ return -ENOMEM;
+
+ c->parser = p;
+
+ n = rb_find_add(&c->node, &ipe_parser_root, cmp_node);
+ if (n) {
+ kfree(c);
+ return -EEXIST;
+ }
+
+ return 0;
+}
diff --git a/security/ipe/parsers/Makefile b/security/ipe/parsers/Makefile
new file mode 100644
index 000000000000..1a19a094724f
--- /dev/null
+++ b/security/ipe/parsers/Makefile
@@ -0,0 +1,12 @@
+# 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.
+#
+
+ccflags-y := -I$(srctree)/security/ipe
+
+obj-$(CONFIG_SECURITY_IPE) += \
+ default.o \
+ policy_header.o \
diff --git a/security/ipe/parsers/default.c b/security/ipe/parsers/default.c
new file mode 100644
index 000000000000..30181d2cc4ed
--- /dev/null
+++ b/security/ipe/parsers/default.c
@@ -0,0 +1,106 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) Microsoft Corporation. All rights reserved.
+ */
+
+#include <linux/slab.h>
+#include <linux/list.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+
+#include "ipe_parser.h"
+
+static int set_op_default(enum ipe_operation op, enum ipe_action act,
+ struct ipe_parsed_policy *pol)
+{
+ size_t i, remap_len;
+ const enum ipe_operation *remap;
+
+ if (!ipe_is_op_alias(op, &remap, &remap_len)) {
+ if (pol->rules[op].default_action != ipe_action_max)
+ return -EBADMSG;
+
+ pol->rules[op].default_action = act;
+ return 0;
+ }
+
+ for (i = 0; i < remap_len; ++i) {
+ if (pol->rules[remap[i]].default_action != ipe_action_max)
+ return -EBADMSG;
+
+ pol->rules[remap[i]].default_action = act;
+ }
+
+ return 0;
+}
+
+static int parse_default(const struct ipe_policy_line *line,
+ struct ipe_parsed_policy *pol)
+{
+ int rc = 0;
+ size_t idx = 0;
+ struct ipe_policy_token *tok = NULL;
+ enum ipe_operation op = ipe_operation_max;
+ enum ipe_action act = ipe_action_max;
+
+ list_for_each_entry(tok, &line->tokens, next) {
+ switch (idx) {
+ case 0:
+ if (strcmp("DEFAULT", tok->key) || tok->value)
+ return -EBADMSG;
+ break;
+ case 1:
+ /* schema 1 - operation, followed by action */
+ rc = ipe_parse_op(tok, &op);
+ if (!rc) {
+ ++idx;
+ continue;
+ }
+
+ if (pol->global_default != ipe_action_max)
+ return -EBADMSG;
+
+ /* schema 2 - action */
+ rc = ipe_parse_action(tok, &pol->global_default);
+ if (!rc)
+ return rc;
+
+ return -EBADMSG;
+ case 2:
+ rc = ipe_parse_action(tok, &act);
+ if (rc)
+ return rc;
+
+ return set_op_default(op, act, pol);
+ default:
+ return -EBADMSG;
+ }
+ ++idx;
+ }
+
+ /* met no schema */
+ return -EBADMSG;
+}
+
+static int validate_defaults(const struct ipe_parsed_policy *p)
+{
+ size_t i = 0;
+
+ if (p->global_default != ipe_action_max)
+ return 0;
+
+ for (i = 0; i < ARRAY_SIZE(p->rules); ++i) {
+ if (p->rules[i].default_action == ipe_action_max)
+ return -EBADMSG;
+ }
+
+ return 0;
+}
+
+IPE_PARSER(default_decl) = {
+ .first_token = "DEFAULT",
+ .version = 1,
+ .parse = parse_default,
+ .free = NULL,
+ .validate = validate_defaults,
+};
diff --git a/security/ipe/parsers/policy_header.c b/security/ipe/parsers/policy_header.c
new file mode 100644
index 000000000000..4d3c1a42c915
--- /dev/null
+++ b/security/ipe/parsers/policy_header.c
@@ -0,0 +1,126 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) Microsoft Corporation. All rights reserved.
+ */
+
+#include "ipe.h"
+#include "ipe_parser.h"
+
+#include <linux/slab.h>
+#include <linux/list.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+
+#include "ipe_parser.h"
+
+static int parse_name(const struct ipe_policy_token *t,
+ struct ipe_parsed_policy *p)
+{
+ if (p->name)
+ return -EBADMSG;
+
+ p->name = kstrdup_const(t->value, GFP_KERNEL);
+ if (!p->name)
+ return -ENOMEM;
+
+ return 0;
+}
+
+static int parse_ver(const struct ipe_policy_token *t,
+ struct ipe_parsed_policy *p)
+{
+ int rc = 0;
+ char *dup = NULL;
+ char *dup2 = NULL;
+ char *token = NULL;
+ size_t sep_count = 0;
+ u16 *const cv[] = { &p->version.major, &p->version.minor, &p->version.rev };
+
+ dup = kstrdup(t->value, GFP_KERNEL);
+ if (!dup) {
+ rc = -ENOMEM;
+ goto err;
+ }
+
+ dup2 = dup;
+
+ while ((token = strsep(&dup, ".\n")) != NULL) {
+ /* prevent overflow */
+ if (sep_count >= ARRAY_SIZE(cv)) {
+ rc = -EBADMSG;
+ goto err;
+ }
+
+ rc = kstrtou16(token, 10, cv[sep_count]);
+ if (rc)
+ goto err;
+
+ ++sep_count;
+ }
+
+ /* prevent underflow */
+ if (sep_count != ARRAY_SIZE(cv))
+ rc = -EBADMSG;
+
+err:
+ kfree(dup2);
+ return rc;
+}
+
+static const struct ipe_token_parser parsers[] = {
+ { .key = "policy_name", .parse_token = parse_name },
+ { .key = "policy_version", .parse_token = parse_ver },
+};
+
+static int parse_policy_hdr(const struct ipe_policy_line *line,
+ struct ipe_parsed_policy *pol)
+{
+ int rc = 0;
+ size_t idx = 0;
+ struct ipe_policy_token *tok = NULL;
+ const struct ipe_token_parser *p = NULL;
+
+ list_for_each_entry(tok, &line->tokens, next) {
+ if (!tok->value || idx >= sizeof(parsers)) {
+ rc = -EBADMSG;
+ goto err;
+ }
+
+ p = &parsers[idx];
+
+ if (strcmp(p->key, tok->key)) {
+ rc = -EBADMSG;
+ goto err;
+ }
+
+ rc = p->parse_token(tok, pol);
+ if (rc)
+ goto err;
+
+ ++idx;
+ }
+
+ return 0;
+
+err:
+ return rc;
+}
+
+static int free_policy_hdr(struct ipe_parsed_policy *pol)
+{
+ kfree(pol->name);
+ return 0;
+}
+
+static int validate_policy_hdr(const struct ipe_parsed_policy *p)
+{
+ return !p->name ? -EBADMSG : 0;
+}
+
+IPE_PARSER(policy_header) = {
+ .first_token = "policy_name",
+ .version = 1,
+ .parse = parse_policy_hdr,
+ .free = free_policy_hdr,
+ .validate = validate_policy_hdr,
+};
diff --git a/security/ipe/policy.c b/security/ipe/policy.c
new file mode 100644
index 000000000000..6381fa7c2ec6
--- /dev/null
+++ b/security/ipe/policy.c
@@ -0,0 +1,946 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) Microsoft Corporation. All rights reserved.
+ */
+
+#include "ipe.h"
+#include "policy.h"
+#include "ipe_parser.h"
+#include "modules.h"
+
+#include <linux/slab.h>
+#include <linux/list.h>
+#include <linux/ctype.h>
+#include <linux/types.h>
+#include <linux/parser.h>
+#include <linux/verification.h>
+
+#define START_COMMENT '#'
+#define KEYVAL_DELIMIT '='
+
+static inline bool is_quote(char ch)
+{
+ return ch == '\'' || ch == '\"';
+}
+
+/**
+ * is_key_char: Determine whether @ch is an acceptable character for a
+ * key type
+ * @ch: Supplies the character to evaluate.
+ *
+ * Return:
+ * true - Character is acceptable.
+ * false - Character is not acceptable.
+ */
+static inline bool is_key_char(char ch)
+{
+ return isalnum(ch) || ch == '_';
+}
+
+/**
+ * is_val_char: Determine whether @ch is an acceptable character for a
+ * value type
+ * @ch: Supplies the character to evaluate.
+ *
+ * Return:
+ * true - Character is acceptable.
+ * false - Character is not acceptable.
+ */
+static inline bool is_val_char(char ch)
+{
+ return isgraph(ch) || ch == ' ' || ch == '\t';
+}
+
+/**
+ * free_parser: Callback to invoke, freeing data allocated by parsers.
+ * @parser: parser to free data.
+ * @ctx: ctx object passed to ipe_for_each_parser.
+ *
+ * This function is intended to be used with ipe_for_each_parser only.
+ *
+ * Return:
+ * 0 - Always
+ */
+static int free_parser(const struct ipe_parser *parser, void *ctx)
+{
+ struct ipe_parsed_policy *pol = ctx;
+
+ if (parser->free)
+ parser->free(pol);
+
+ return 0;
+}
+
+/**
+ * free_rule: free an ipe_rule.
+ * @r: Supplies the rule to free.
+ *
+ * This function is safe to call if @r is NULL or ERR_PTR.
+ */
+static void free_rule(struct ipe_rule *r)
+{
+ struct ipe_policy_mod *p, *t;
+
+ if (IS_ERR_OR_NULL(r))
+ return;
+
+ list_for_each_entry_safe(p, t, &r->modules, next) {
+ if (p->mod->free)
+ p->mod->free(&p->mod_value);
+
+ kfree(p);
+ }
+
+ kfree(r);
+}
+
+static void free_parsed_policy(struct ipe_parsed_policy *p)
+{
+ size_t i = 0;
+ struct ipe_rule *pp, *t;
+
+ if (IS_ERR_OR_NULL(p))
+ return;
+
+ for (i = 0; i < ARRAY_SIZE(p->rules); ++i)
+ list_for_each_entry_safe(pp, t, &p->rules[i].rules, next)
+ free_rule(pp);
+
+ (void)ipe_for_each_parser(free_parser, p);
+ kfree(p);
+}
+
+/**
+ * free_parsed_line: free a single parsed line of tokens.
+ * @line: Supplies the line to free.
+ *
+ * This function is safe to call if @line is NULL or ERR_PTR.
+ */
+static void free_parsed_line(struct ipe_policy_line *line)
+{
+ struct ipe_policy_token *p, *t;
+
+ if (IS_ERR_OR_NULL(line))
+ return;
+
+ list_for_each_entry_safe(p, t, &line->tokens, next)
+ kfree(p);
+}
+
+/**
+ * free_parsed_text: free a 2D list representing a tokenized policy.
+ * @parsed: Supplies the policy to free.
+ *
+ * This function is safe to call if @parsed is NULL or ERR_PTR.
+ */
+static void free_parsed_text(struct list_head *parsed)
+{
+ struct ipe_policy_line *p, *t;
+
+ if (IS_ERR_OR_NULL(parsed))
+ return;
+
+ list_for_each_entry_safe(p, t, parsed, next)
+ free_parsed_line(p);
+}
+
+/**
+ * trim_quotes: Edit @str to remove a single instance of a trailing and
+ * leading quotes.
+ * @str: Supplies the string to edit.
+ *
+ * If the string is not quoted, @str will be returned. This function is
+ * safe to call if @str is NULL.
+ *
+ * Return:
+ * !0 - OK
+ * ERR_PTR(-EBADMSG) - Quote mismatch.
+ */
+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 ERR_PTR(-EBADMSG);
+
+ str[len] = '\0';
+ ++str;
+ }
+
+ return str;
+}
+
+/**
+ * parse_token: Parse a string into a proper token representation.
+ * @token: Supplies the token string to parse.
+ *
+ * @token will be edited destructively. Pass a copy if you wish to retain
+ * the state of the original.
+ *
+ * This function will emit an error to pr_err when a parsing error occurs.
+ *
+ * Return:
+ * !0 - OK
+ * ERR_PTR(-EBADMSG) - An invalid character was encountered while parsing.
+ * ERR_PTR(-ENOMEM) - No Memory
+ */
+static struct ipe_policy_token *parse_token(char *token)
+{
+ size_t i, len = 0;
+ char *key = token;
+ char *value = NULL;
+ struct ipe_policy_token *local = NULL;
+
+ len = strlen(token);
+
+ for (i = 0; (i < len) && token[i] != KEYVAL_DELIMIT; ++i)
+ if (!is_key_char(token[i]))
+ return ERR_PTR(-EBADMSG);
+
+ token[i] = '\0';
+ ++i;
+
+ /* there is a value */
+ if (i < len) {
+ value = trim_quotes(&token[i]);
+ if (IS_ERR(value))
+ return ERR_PTR(-EBADMSG);
+
+ len = strlen(value);
+
+ for (i = 0; i < len; ++i)
+ if (!is_val_char(value[i]))
+ return ERR_PTR(-EBADMSG);
+ }
+
+ local = kzalloc(sizeof(*local), GFP_KERNEL);
+ if (!local)
+ return ERR_PTR(-ENOMEM);
+
+ INIT_LIST_HEAD(&local->next);
+ local->key = key;
+ local->value = value;
+
+ return local;
+}
+
+/**
+ * append_token: Parse and append a token into an ipe_policy_line structure.
+ * @p: Supplies the ipe_policy_line structure to append to.
+ * @token: Supplies the token to parse and append to.
+ *
+ * @token will be edited during the parsing destructively. Pass a copy if you
+ * wish to retain the original.
+ *
+ * Return:
+ * 0 - OK
+ * -EBADMSG - Parsing error of @token
+ */
+static int append_token(struct ipe_policy_line *p, char *token)
+{
+ struct ipe_policy_token *t = NULL;
+
+ t = parse_token(token);
+ if (IS_ERR(t))
+ return PTR_ERR(t);
+
+ list_add_tail(&t->next, &p->tokens);
+
+ return 0;
+}
+
+/**
+ * alloc_line: Allocate an ipe_policy_line structure.
+ *
+ * Return:
+ * !0 - OK
+ * -EBADMSG - Parsing error of @token
+ */
+static struct ipe_policy_line *alloc_line(void)
+{
+ struct ipe_policy_line *l = NULL;
+
+ l = kzalloc(sizeof(*l), GFP_KERNEL);
+ if (!l)
+ return ERR_PTR(-ENOMEM);
+
+ INIT_LIST_HEAD(&l->next);
+ INIT_LIST_HEAD(&l->tokens);
+
+ return l;
+}
+
+/**
+ * insert_token: Append a token to @line.
+ * @token: Supplies the token to append to @line.
+ * @line: Supplies a pointer to the ipe_policy_line structure to append to.
+ *
+ * If @line is NULL, it will be allocated on behalf of the caller.
+ *
+ * Return:
+ * 0 - OK
+ * -ENOMEM - No Memory
+ * -EBADMSG - Parsing error of @token
+ */
+static int insert_token(char *token, struct ipe_policy_line **line)
+{
+ int rc = 0;
+ struct ipe_policy_line *local = *line;
+
+ if (!local) {
+ local = alloc_line();
+ if (IS_ERR(local))
+ return PTR_ERR(local);
+
+ *line = local;
+ }
+
+ rc = append_token(local, token);
+ if (rc)
+ return rc;
+
+ return 0;
+}
+
+/**
+ * ipe_tokenize_line: Parse a line of text into a list of token structures.
+ * @line: Supplies the line to parse.
+ *
+ * The final result can be NULL, which represents no tokens were parsed.
+ *
+ * Return:
+ * !0 - OK
+ * NULL - OK, no tokens were parsed.
+ * ERR_PTR(-EBADMSG) - Invalid policy syntax
+ * ERR_PTR(-ENOMEM) - No Memory
+ */
+static struct ipe_policy_line *tokenize_line(char *line)
+{
+ int rc = 0;
+ size_t i = 0;
+ size_t len = 0;
+ char *tok = NULL;
+ char quote = '\0';
+ struct ipe_policy_line *p = NULL;
+
+ /* nullterm guaranteed by strsep */
+ 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] == START_COMMENT) {
+ tok = NULL;
+ break;
+ }
+
+ if (isgraph(line[i]) && !tok)
+ tok = &line[i];
+
+ if (quote == '\0' && isspace(line[i])) {
+ line[i] = '\0';
+
+ if (!tok)
+ continue;
+
+ rc = insert_token(tok, &p);
+ if (rc)
+ goto err;
+
+ tok = NULL;
+ }
+ }
+
+ if (quote != '\0') {
+ rc = -EBADMSG;
+ goto err;
+ }
+
+ if (tok) {
+ rc = insert_token(tok, &p);
+ if (rc)
+ goto err;
+ }
+
+ return p;
+
+err:
+ free_parsed_line(p);
+ return ERR_PTR(rc);
+}
+
+/**
+ * parse_pass1: parse @policy into a 2D list, representing tokens on each line.
+ * @policy: Supplies the policy to parse. Must be nullterminated, and is
+ * edited.
+ *
+ * In pass1 of the parser, the policy is tokenized. Minor structure checks
+ * are done (mismatching quotes, invalid characters).
+ *
+ * Caller must maintain the lifetime of @policy while the return value is
+ * alive.
+ *
+ * Return:
+ * !0 - OK
+ * ERR_PTR(-ENOMEM) - Out of Memory
+ * ERR_PTR(-EBADMSG) - Parsing Error
+ */
+static int parse_pass1(char *policy, struct list_head *tokens)
+{
+ int rc = 0;
+ char *p = NULL;
+
+ while ((p = strsep(&policy, "\n\0")) != NULL) {
+ struct ipe_policy_line *t = NULL;
+
+ t = tokenize_line(p);
+ if (IS_ERR(t)) {
+ rc = PTR_ERR(t);
+ goto err_free_parsed;
+ }
+
+ if (!t)
+ continue;
+
+ list_add_tail(&t->next, tokens);
+ }
+
+ return 0;
+
+err_free_parsed:
+ free_parsed_text(tokens);
+ return rc;
+}
+
+/**
+ * parse_pass2: Take the 2D list of tokens generated from pass1, and transform
+ * it into a partial ipe_policy.
+ * @parsed: Supplies the list of tokens generated from pass1.
+ * @p: Policy to manipulate with parsed tokens.
+ *
+ * This function is where various declarations and references are parsed into
+ * policy. All declarations and references required to parse rules should be
+ * done here as a parser, and then in pass3 these can be utilized.
+ *
+ * Return:
+ * !0 - OK
+ * -EBADMSG - Syntax Parsing Errors
+ * -ENOENT - No handler for a token.
+ * -ENOMEM - Out of memory
+ */
+static int parse_pass2(struct list_head *parsed, struct ipe_parsed_policy *pol)
+{
+ int rc = 0;
+ const struct ipe_parser *p = NULL;
+ struct ipe_policy_line *line = NULL;
+ const struct ipe_policy_token *token = NULL;
+
+ list_for_each_entry(line, parsed, next) {
+ token = list_first_entry(&line->tokens, struct ipe_policy_token, next);
+ p = ipe_lookup_parser(token->key);
+ if (!p)
+ continue;
+
+ rc = p->parse(line, pol);
+ if (rc)
+ return rc;
+
+ line->consumed = true;
+ }
+
+ return rc;
+}
+
+/**
+ * ipe_parse_op: parse a token to an operation value.
+ * @tok: Token to parse
+ * @op: Operation Parsed.
+ *
+ * Return:
+ * 0 - OK
+ * -EINVAL - Invalid key or value.
+ */
+int ipe_parse_op(const struct ipe_policy_token *tok,
+ enum ipe_operation *op)
+{
+ substring_t match[MAX_OPT_ARGS] = { 0 };
+ const match_table_t ops = {
+ { ipe_op_alias_max, NULL },
+ };
+
+ if (strcmp(tok->key, "op") || !tok->value)
+ return -EINVAL;
+
+ *op = match_token((char *)tok->value, ops, match);
+ if ((*op) == (int)ipe_op_alias_max)
+ return -ENOENT;
+
+ return 0;
+}
+
+/**
+ * ipe_parse_action: parse a token to an operation value.
+ * @tok: Token to parse
+ * @action: action parsed.
+ *
+ * Return:
+ * 0 - OK
+ * -EINVAL - Invalid key or value.
+ */
+int ipe_parse_action(const struct ipe_policy_token *tok,
+ enum ipe_action *action)
+{
+ substring_t match[MAX_OPT_ARGS] = { 0 };
+ const match_table_t actions = {
+ { ipe_action_allow, "ALLOW" },
+ { ipe_action_deny, "DENY" },
+ { ipe_action_max, NULL },
+ };
+
+ if (strcmp(tok->key, "action") || !tok->value)
+ return -EINVAL;
+
+ *action = match_token((char *)tok->value, actions, match);
+
+ if (*action == ipe_action_max)
+ return -EINVAL;
+
+ return 0;
+}
+
+/**
+ * parse_mod_to_rule: Parse a module token and append the values to the
+ * provided rule.
+ * @t: Supplies the token to parse.
+ * @r: Supplies the rule to modify with the result.
+ *
+ * Return:
+ * 0 - OK
+ * -ENOENT - No such module to handle @t.
+ * -ENOMEM - No memory.
+ * Others - Module defined errors.
+ */
+static int parse_mod_to_rule(const struct ipe_policy_token *t, struct ipe_rule *r)
+{
+ int rc = 0;
+ struct ipe_policy_mod *p = NULL;
+ const struct ipe_module *m = NULL;
+
+ m = ipe_lookup_module(t->key);
+ if (IS_ERR_OR_NULL(m)) {
+ rc = (m) ? PTR_ERR(m) : -ENOENT;
+ goto err;
+ }
+
+ p = kzalloc(sizeof(*p), GFP_KERNEL);
+ if (!p) {
+ rc = -ENOMEM;
+ goto err;
+ }
+ INIT_LIST_HEAD(&p->next);
+ p->mod = m;
+
+ rc = m->parse(t->value, &p->mod_value);
+ if (rc)
+ goto err2;
+
+ list_add_tail(&p->next, &r->modules);
+ return 0;
+err2:
+ kfree(p);
+err:
+ return rc;
+}
+
+/**
+ * parse_rule: Parse a policy line into an ipe_rule structure.
+ * @line: Supplies the line to parse.
+ *
+ * Return:
+ * Valid ipe_rule - OK
+ * ERR_PTR(-ENOMEM) - Out of Memory
+ * ERR_PTR(-ENOENT) - No such module to handle a token
+ * ERR_PTR(-EINVAL) - An unacceptable value has been encountered.
+ * ERR_PTR(...) - Module defined errors.
+ */
+static struct ipe_rule *parse_rule(const struct ipe_policy_line *line)
+{
+ int rc = 0;
+ struct ipe_rule *r = NULL;
+ const struct list_head *node = NULL;
+
+ r = kzalloc(sizeof(*r), GFP_KERNEL);
+ if (!r) {
+ rc = -ENOMEM;
+ goto err;
+ }
+
+ INIT_LIST_HEAD(&r->next);
+ INIT_LIST_HEAD(&r->modules);
+ r->op = (int)ipe_op_alias_max;
+ r->action = ipe_action_max;
+
+ list_for_each(node, &line->tokens) {
+ const struct ipe_policy_token *token = NULL;
+
+ token = container_of(node, struct ipe_policy_token, next);
+
+ if (list_is_first(node, &line->tokens)) {
+ enum ipe_operation op;
+
+ rc = ipe_parse_op(token, &op);
+ if (rc)
+ goto err;
+
+ r->op = op;
+ continue;
+ }
+
+ if (list_is_last(node, &line->tokens)) {
+ enum ipe_action action;
+
+ rc = ipe_parse_action(token, &action);
+ if (rc)
+ goto err;
+
+ r->action = action;
+ continue;
+ }
+
+ rc = parse_mod_to_rule(token, r);
+ if (rc)
+ goto err;
+ }
+
+ if (r->action == ipe_action_max || r->op == (int)ipe_op_alias_max) {
+ rc = -EBADMSG;
+ goto err;
+ }
+
+ return r;
+err:
+ free_rule(r);
+ return ERR_PTR(rc);
+}
+
+/**
+ * parse_pass3: Take the partially parsed list of tokens from pass 1 and the
+ * parial policy from pass 2, and finalize the policy.
+ * @parsed: Supplies the tokens parsed from pass 1.
+ * @p: Supplies the partial policy from pass 2.
+ *
+ * This function finalizes the IPE policy by parsing all rules in the
+ * policy. This must occur in pass3, as in pass2, references are resolved
+ * that can be used in pass3.
+ *
+ * Return:
+ * 0 - OK
+ * !0 - Standard errno
+ */
+static int parse_pass3(struct list_head *parsed,
+ struct ipe_parsed_policy *p)
+{
+ int rc = 0;
+ size_t i = 0;
+ size_t remap_len = 0;
+ struct ipe_rule *rule = NULL;
+ struct ipe_policy_line *line = NULL;
+ const enum ipe_operation *remap;
+
+ list_for_each_entry(line, parsed, next) {
+ if (line->consumed)
+ continue;
+
+ rule = parse_rule(line);
+ if (IS_ERR(rule)) {
+ rc = PTR_ERR(rule);
+ goto err;
+ }
+
+ if (ipe_is_op_alias(rule->op, &remap, &remap_len)) {
+ for (i = 0; i < remap_len; ++i) {
+ rule->op = remap[i];
+ list_add_tail(&rule->next, &p->rules[rule->op].rules);
+ rule = parse_rule(line);
+ }
+
+ free_rule(rule);
+ } else {
+ list_add_tail(&rule->next, &p->rules[rule->op].rules);
+ }
+
+ line->consumed = true;
+ }
+
+ return 0;
+err:
+ free_rule(rule);
+ return rc;
+}
+
+/**
+ * parser_validate: Callback to invoke, validating parsers as necessary
+ * @parser: parser to call to validate data.
+ * @ctx: ctx object passed to ipe_for_each_parser.
+ *
+ * This function is intended to be used with ipe_for_each_parser only.
+ *
+ * Return:
+ * 0 - OK
+ * !0 - Validation failed.
+ */
+static int parser_validate(const struct ipe_parser *parser, void *ctx)
+{
+ int rc = 0;
+ const struct ipe_parsed_policy *pol = ctx;
+
+ if (parser->validate)
+ rc = parser->validate(pol);
+
+ return rc;
+}
+
+/**
+ * validate_policy: Given a policy structure that was just parsed, validate
+ * that all necessary fields are present, initialized
+ * correctly, and all lines parsed are have been consumed.
+ * @parsed: Supplies the policy lines that were parsed in pass1.
+ * @policy: Supplies the fully parsed policy.
+ *
+ * A parsed policy can be an invalid state for use (a default was undefined,
+ * a header was undefined) by just parsing the policy.
+ *
+ * Return:
+ * 0 - OK
+ * -EBADMSG - Policy is invalid.
+ */
+static int validate_policy(const struct list_head *parsed,
+ const struct ipe_parsed_policy *p)
+{
+ int rc = 0;
+ const struct ipe_policy_line *line = NULL;
+
+ list_for_each_entry(line, parsed, next) {
+ if (!line->consumed)
+ return -EBADMSG;
+ }
+
+ rc = ipe_for_each_parser(parser_validate,
+ (struct ipe_parsed_policy *)p);
+
+ return rc;
+}
+
+/**
+ * new_parsed_policy: Allocate and initialize a parsed policy to its default
+ * values.
+ *
+ * Return:
+ * !IS_ERR - OK
+ */
+static struct ipe_parsed_policy *new_parsed_policy(void)
+{
+ size_t i = 0;
+ struct ipe_parsed_policy *p = NULL;
+ struct ipe_operation_table *t = NULL;
+
+ p = kzalloc(sizeof(*p), GFP_KERNEL);
+ if (!p)
+ return ERR_PTR(-ENOMEM);
+
+ p->global_default = ipe_action_max;
+
+ for (i = 0; i < ARRAY_SIZE(p->rules); ++i) {
+ t = &p->rules[i];
+
+ t->default_action = ipe_action_max;
+ INIT_LIST_HEAD(&t->rules);
+ }
+
+ return p;
+}
+
+/**
+ * parse_policy: Given a string, parse the string into an IPE policy
+ * structure.
+ * @p: partially filled ipe_policy structure to populate with the result.
+ *
+ * @p must have text and textlen set.
+ *
+ * Return:
+ * Valid ipe_policy structure - OK
+ * ERR_PTR(-EBADMSG) - Invalid Policy Syntax (Unrecoverable)
+ * ERR_PTR(-ENOMEM) - Out of Memory
+ */
+static int parse_policy(struct ipe_policy *p)
+{
+ int rc = 0;
+ char *dup = NULL;
+ LIST_HEAD(parsed);
+ struct ipe_parsed_policy *pp = NULL;
+
+ if (!p->textlen)
+ return -EBADMSG;
+
+ dup = kmemdup_nul(p->text, p->textlen, GFP_KERNEL);
+ if (!dup)
+ return -ENOMEM;
+
+ pp = new_parsed_policy();
+ if (IS_ERR(pp)) {
+ rc = PTR_ERR(pp);
+ goto out;
+ }
+
+ rc = parse_pass1(dup, &parsed);
+ if (rc)
+ goto err;
+
+ rc = parse_pass2(&parsed, pp);
+ if (rc)
+ goto err;
+
+ rc = parse_pass3(&parsed, pp);
+ if (rc)
+ goto err;
+
+ rc = validate_policy(&parsed, pp);
+ if (rc)
+ goto err;
+
+ p->parsed = pp;
+
+ goto out;
+err:
+ free_parsed_policy(pp);
+out:
+ free_parsed_text(&parsed);
+ kfree(dup);
+
+ return rc;
+}
+
+/**
+ * ipe_is_op_alias: Determine if @op is an alias for one or more operations
+ * @op: Supplies the operation to check. Should be either ipe_operation or
+ * ipe_op_alias.
+ * @map: Supplies a pointer to populate with the mapping if @op is an alias
+ * @size: Supplies the size of @map if @op is an alias.
+ *
+ * Return:
+ * true - @op is an alias
+ * false - @op is not an alias
+ */
+bool ipe_is_op_alias(int op, const enum ipe_operation **map, size_t *size)
+{
+ switch (op) {
+ default:
+ return false;
+ }
+}
+
+/**
+ * ipe_put_policy: Deallocate a given IPE policy.
+ * @p: Supplies the policy to free.
+ *
+ * Safe to call on IS_ERR/NULL.
+ */
+void ipe_put_policy(struct ipe_policy *p)
+{
+ if (IS_ERR_OR_NULL(p) || !refcount_dec_and_test(&p->refcount))
+ return;
+
+ free_parsed_policy(p->parsed);
+ if (!p->pkcs7)
+ kfree(p->text);
+ kfree(p->pkcs7);
+ kfree(p);
+}
+
+static int set_pkcs7_data(void *ctx, const void *data, size_t len,
+ size_t asn1hdrlen)
+{
+ struct ipe_policy *p = ctx;
+
+ p->text = (const char *)data;
+ p->textlen = len;
+
+ return 0;
+}
+
+/**
+ * ipe_new_policy: allocate and parse an ipe_policy structure.
+ *
+ * @text: Supplies a pointer to the plain-text policy to parse.
+ * @textlen: Supplies the length of @text.
+ * @pkcs7: Supplies a pointer to a pkcs7-signed IPE policy.
+ * @pkcs7len: Supplies the length of @pkcs7.
+ *
+ * @text/@textlen Should be NULL/0 if @pkcs7/@pkcs7len is set.
+ *
+ * The result will still need to be associated with a context via
+ * ipe_add_policy.
+ *
+ * Return:
+ * !IS_ERR - Success
+ */
+struct ipe_policy *ipe_new_policy(const char *text, size_t textlen,
+ const char *pkcs7, size_t pkcs7len)
+{
+ int rc = 0;
+ struct ipe_policy *new = NULL;
+
+ new = kzalloc(sizeof(*new), GFP_KERNEL);
+ if (!new)
+ return ERR_PTR(-ENOMEM);
+
+ refcount_set(&new->refcount, 1);
+
+ if (!text) {
+ new->pkcs7len = pkcs7len;
+ new->pkcs7 = kmemdup(pkcs7, pkcs7len, GFP_KERNEL);
+ if (!new->pkcs7) {
+ rc = -ENOMEM;
+ goto err;
+ }
+
+ rc = verify_pkcs7_signature(NULL, 0, new->pkcs7, pkcs7len, NULL,
+ VERIFYING_UNSPECIFIED_SIGNATURE,
+ set_pkcs7_data, new);
+ if (rc)
+ goto err;
+ } else {
+ new->textlen = textlen;
+ new->text = kstrndup(text, textlen, GFP_KERNEL);
+ if (!new->text) {
+ rc = -ENOMEM;
+ goto err;
+ }
+ }
+
+ rc = parse_policy(new);
+ if (rc)
+ goto err;
+
+ return new;
+err:
+ ipe_put_policy(new);
+ return ERR_PTR(rc);
+}
diff --git a/security/ipe/policy.h b/security/ipe/policy.h
new file mode 100644
index 000000000000..d78788db238c
--- /dev/null
+++ b/security/ipe/policy.h
@@ -0,0 +1,97 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) Microsoft Corporation. All rights reserved.
+ */
+#ifndef IPE_POLICY_H
+#define IPE_POLICY_H
+
+#include <linux/list.h>
+#include <linux/types.h>
+#include <linux/refcount.h>
+
+struct ipe_policy_token {
+ struct list_head next; /* type: policy_token */
+
+ const char *key;
+ const char *value;
+};
+
+struct ipe_policy_line {
+ struct list_head next; /* type: policy_line */
+ struct list_head tokens; /* type: policy_token */
+
+ bool consumed;
+};
+
+struct ipe_module;
+
+enum ipe_operation {
+ ipe_operation_max = 0,
+};
+
+/*
+ * Extension to ipe_operation, representing operations
+ * that are just one or more operations under the hood
+ */
+enum ipe_op_alias {
+ ipe_op_alias_max = ipe_operation_max,
+};
+
+enum ipe_action {
+ ipe_action_allow = 0,
+ ipe_action_deny,
+ ipe_action_max,
+};
+
+struct ipe_policy_mod {
+ const struct ipe_module *mod;
+ void *mod_value;
+
+ struct list_head next;
+};
+
+struct ipe_rule {
+ enum ipe_operation op;
+ enum ipe_action action;
+
+ struct list_head modules;
+
+ struct list_head next;
+};
+
+struct ipe_operation_table {
+ struct list_head rules;
+ enum ipe_action default_action;
+};
+
+struct ipe_parsed_policy {
+ const char *name;
+ struct {
+ u16 major;
+ u16 minor;
+ u16 rev;
+ } version;
+
+ enum ipe_action global_default;
+
+ struct ipe_operation_table rules[ipe_operation_max];
+};
+
+struct ipe_policy {
+ const char *pkcs7;
+ size_t pkcs7len;
+
+ const char *text;
+ size_t textlen;
+
+ struct ipe_parsed_policy *parsed;
+
+ refcount_t refcount;
+};
+
+struct ipe_policy *ipe_new_policy(const char *text, size_t textlen,
+ const char *pkcs7, size_t pkcs7len);
+void ipe_put_policy(struct ipe_policy *pol);
+bool ipe_is_op_alias(int op, const enum ipe_operation **map, size_t *size);
+
+#endif /* IPE_POLICY_H */
--
2.25.1
More information about the Linux-security-module-archive
mailing list