xref: /linux-6.15/security/ipe/policy_parser.c (revision 31f8c868)
154a88cd2SDeven Bowers // SPDX-License-Identifier: GPL-2.0
254a88cd2SDeven Bowers /*
354a88cd2SDeven Bowers  * Copyright (C) 2020-2024 Microsoft Corporation. All rights reserved.
454a88cd2SDeven Bowers  */
554a88cd2SDeven Bowers 
654a88cd2SDeven Bowers #include <linux/err.h>
754a88cd2SDeven Bowers #include <linux/slab.h>
854a88cd2SDeven Bowers #include <linux/parser.h>
954a88cd2SDeven Bowers #include <linux/types.h>
1054a88cd2SDeven Bowers #include <linux/ctype.h>
1154a88cd2SDeven Bowers 
1254a88cd2SDeven Bowers #include "policy.h"
1354a88cd2SDeven Bowers #include "policy_parser.h"
14e155858dSDeven Bowers #include "digest.h"
1554a88cd2SDeven Bowers 
1654a88cd2SDeven Bowers #define START_COMMENT	'#'
1754a88cd2SDeven Bowers #define IPE_POLICY_DELIM " \t"
1854a88cd2SDeven Bowers #define IPE_LINE_DELIM "\n\r"
1954a88cd2SDeven Bowers 
2054a88cd2SDeven Bowers /**
2154a88cd2SDeven Bowers  * new_parsed_policy() - Allocate and initialize a parsed policy.
2254a88cd2SDeven Bowers  *
2354a88cd2SDeven Bowers  * Return:
2454a88cd2SDeven Bowers  * * a pointer to the ipe_parsed_policy structure	- Success
2554a88cd2SDeven Bowers  * * %-ENOMEM						- Out of memory (OOM)
2654a88cd2SDeven Bowers  */
new_parsed_policy(void)2754a88cd2SDeven Bowers static struct ipe_parsed_policy *new_parsed_policy(void)
2854a88cd2SDeven Bowers {
2954a88cd2SDeven Bowers 	struct ipe_parsed_policy *p = NULL;
3054a88cd2SDeven Bowers 	struct ipe_op_table *t = NULL;
3154a88cd2SDeven Bowers 	size_t i = 0;
3254a88cd2SDeven Bowers 
3354a88cd2SDeven Bowers 	p = kzalloc(sizeof(*p), GFP_KERNEL);
3454a88cd2SDeven Bowers 	if (!p)
3554a88cd2SDeven Bowers 		return ERR_PTR(-ENOMEM);
3654a88cd2SDeven Bowers 
3754a88cd2SDeven Bowers 	p->global_default_action = IPE_ACTION_INVALID;
3854a88cd2SDeven Bowers 
3954a88cd2SDeven Bowers 	for (i = 0; i < ARRAY_SIZE(p->rules); ++i) {
4054a88cd2SDeven Bowers 		t = &p->rules[i];
4154a88cd2SDeven Bowers 
4254a88cd2SDeven Bowers 		t->default_action = IPE_ACTION_INVALID;
4354a88cd2SDeven Bowers 		INIT_LIST_HEAD(&t->rules);
4454a88cd2SDeven Bowers 	}
4554a88cd2SDeven Bowers 
4654a88cd2SDeven Bowers 	return p;
4754a88cd2SDeven Bowers }
4854a88cd2SDeven Bowers 
4954a88cd2SDeven Bowers /**
5054a88cd2SDeven Bowers  * remove_comment() - Truncate all chars following START_COMMENT in a string.
5154a88cd2SDeven Bowers  *
5254a88cd2SDeven Bowers  * @line: Supplies a policy line string for preprocessing.
5354a88cd2SDeven Bowers  */
remove_comment(char * line)5454a88cd2SDeven Bowers static void remove_comment(char *line)
5554a88cd2SDeven Bowers {
5654a88cd2SDeven Bowers 	line = strchr(line, START_COMMENT);
5754a88cd2SDeven Bowers 
5854a88cd2SDeven Bowers 	if (line)
5954a88cd2SDeven Bowers 		*line = '\0';
6054a88cd2SDeven Bowers }
6154a88cd2SDeven Bowers 
6254a88cd2SDeven Bowers /**
6354a88cd2SDeven Bowers  * remove_trailing_spaces() - Truncate all trailing spaces in a string.
6454a88cd2SDeven Bowers  *
6554a88cd2SDeven Bowers  * @line: Supplies a policy line string for preprocessing.
6654a88cd2SDeven Bowers  *
6754a88cd2SDeven Bowers  * Return: The length of truncated string.
6854a88cd2SDeven Bowers  */
remove_trailing_spaces(char * line)6954a88cd2SDeven Bowers static size_t remove_trailing_spaces(char *line)
7054a88cd2SDeven Bowers {
7154a88cd2SDeven Bowers 	size_t i = 0;
7254a88cd2SDeven Bowers 
7354a88cd2SDeven Bowers 	i = strlen(line);
7454a88cd2SDeven Bowers 	while (i > 0 && isspace(line[i - 1]))
7554a88cd2SDeven Bowers 		i--;
7654a88cd2SDeven Bowers 
7754a88cd2SDeven Bowers 	line[i] = '\0';
7854a88cd2SDeven Bowers 
7954a88cd2SDeven Bowers 	return i;
8054a88cd2SDeven Bowers }
8154a88cd2SDeven Bowers 
8254a88cd2SDeven Bowers /**
8354a88cd2SDeven Bowers  * parse_version() - Parse policy version.
8454a88cd2SDeven Bowers  * @ver: Supplies a version string to be parsed.
8554a88cd2SDeven Bowers  * @p: Supplies the partial parsed policy.
8654a88cd2SDeven Bowers  *
8754a88cd2SDeven Bowers  * Return:
8854a88cd2SDeven Bowers  * * %0		- Success
8954a88cd2SDeven Bowers  * * %-EBADMSG	- Version string is invalid
9054a88cd2SDeven Bowers  * * %-ERANGE	- Version number overflow
9154a88cd2SDeven Bowers  * * %-EINVAL	- Parsing error
9254a88cd2SDeven Bowers  */
parse_version(char * ver,struct ipe_parsed_policy * p)9354a88cd2SDeven Bowers static int parse_version(char *ver, struct ipe_parsed_policy *p)
9454a88cd2SDeven Bowers {
9554a88cd2SDeven Bowers 	u16 *const cv[] = { &p->version.major, &p->version.minor, &p->version.rev };
9654a88cd2SDeven Bowers 	size_t sep_count = 0;
9754a88cd2SDeven Bowers 	char *token;
9854a88cd2SDeven Bowers 	int rc = 0;
9954a88cd2SDeven Bowers 
10054a88cd2SDeven Bowers 	while ((token = strsep(&ver, ".")) != NULL) {
10154a88cd2SDeven Bowers 		/* prevent overflow */
10254a88cd2SDeven Bowers 		if (sep_count >= ARRAY_SIZE(cv))
10354a88cd2SDeven Bowers 			return -EBADMSG;
10454a88cd2SDeven Bowers 
10554a88cd2SDeven Bowers 		rc = kstrtou16(token, 10, cv[sep_count]);
10654a88cd2SDeven Bowers 		if (rc)
10754a88cd2SDeven Bowers 			return rc;
10854a88cd2SDeven Bowers 
10954a88cd2SDeven Bowers 		++sep_count;
11054a88cd2SDeven Bowers 	}
11154a88cd2SDeven Bowers 
11254a88cd2SDeven Bowers 	/* prevent underflow */
11354a88cd2SDeven Bowers 	if (sep_count != ARRAY_SIZE(cv))
11454a88cd2SDeven Bowers 		return -EBADMSG;
11554a88cd2SDeven Bowers 
11654a88cd2SDeven Bowers 	return 0;
11754a88cd2SDeven Bowers }
11854a88cd2SDeven Bowers 
11954a88cd2SDeven Bowers enum header_opt {
12054a88cd2SDeven Bowers 	IPE_HEADER_POLICY_NAME = 0,
12154a88cd2SDeven Bowers 	IPE_HEADER_POLICY_VERSION,
12254a88cd2SDeven Bowers 	__IPE_HEADER_MAX
12354a88cd2SDeven Bowers };
12454a88cd2SDeven Bowers 
12554a88cd2SDeven Bowers static const match_table_t header_tokens = {
12654a88cd2SDeven Bowers 	{IPE_HEADER_POLICY_NAME,	"policy_name=%s"},
12754a88cd2SDeven Bowers 	{IPE_HEADER_POLICY_VERSION,	"policy_version=%s"},
12854a88cd2SDeven Bowers 	{__IPE_HEADER_MAX,		NULL}
12954a88cd2SDeven Bowers };
13054a88cd2SDeven Bowers 
13154a88cd2SDeven Bowers /**
13254a88cd2SDeven Bowers  * parse_header() - Parse policy header information.
13354a88cd2SDeven Bowers  * @line: Supplies header line to be parsed.
13454a88cd2SDeven Bowers  * @p: Supplies the partial parsed policy.
13554a88cd2SDeven Bowers  *
13654a88cd2SDeven Bowers  * Return:
13754a88cd2SDeven Bowers  * * %0		- Success
13854a88cd2SDeven Bowers  * * %-EBADMSG	- Header string is invalid
13954a88cd2SDeven Bowers  * * %-ENOMEM	- Out of memory (OOM)
14054a88cd2SDeven Bowers  * * %-ERANGE	- Version number overflow
14154a88cd2SDeven Bowers  * * %-EINVAL	- Version parsing error
14254a88cd2SDeven Bowers  */
parse_header(char * line,struct ipe_parsed_policy * p)14354a88cd2SDeven Bowers static int parse_header(char *line, struct ipe_parsed_policy *p)
14454a88cd2SDeven Bowers {
14554a88cd2SDeven Bowers 	substring_t args[MAX_OPT_ARGS];
14654a88cd2SDeven Bowers 	char *t, *ver = NULL;
14754a88cd2SDeven Bowers 	size_t idx = 0;
14854a88cd2SDeven Bowers 	int rc = 0;
14954a88cd2SDeven Bowers 
15054a88cd2SDeven Bowers 	while ((t = strsep(&line, IPE_POLICY_DELIM)) != NULL) {
15154a88cd2SDeven Bowers 		int token;
15254a88cd2SDeven Bowers 
15354a88cd2SDeven Bowers 		if (*t == '\0')
15454a88cd2SDeven Bowers 			continue;
15554a88cd2SDeven Bowers 		if (idx >= __IPE_HEADER_MAX) {
15654a88cd2SDeven Bowers 			rc = -EBADMSG;
15754a88cd2SDeven Bowers 			goto out;
15854a88cd2SDeven Bowers 		}
15954a88cd2SDeven Bowers 
16054a88cd2SDeven Bowers 		token = match_token(t, header_tokens, args);
16154a88cd2SDeven Bowers 		if (token != idx) {
16254a88cd2SDeven Bowers 			rc = -EBADMSG;
16354a88cd2SDeven Bowers 			goto out;
16454a88cd2SDeven Bowers 		}
16554a88cd2SDeven Bowers 
16654a88cd2SDeven Bowers 		switch (token) {
16754a88cd2SDeven Bowers 		case IPE_HEADER_POLICY_NAME:
16854a88cd2SDeven Bowers 			p->name = match_strdup(&args[0]);
16954a88cd2SDeven Bowers 			if (!p->name)
17054a88cd2SDeven Bowers 				rc = -ENOMEM;
17154a88cd2SDeven Bowers 			break;
17254a88cd2SDeven Bowers 		case IPE_HEADER_POLICY_VERSION:
17354a88cd2SDeven Bowers 			ver = match_strdup(&args[0]);
17454a88cd2SDeven Bowers 			if (!ver) {
17554a88cd2SDeven Bowers 				rc = -ENOMEM;
17654a88cd2SDeven Bowers 				break;
17754a88cd2SDeven Bowers 			}
17854a88cd2SDeven Bowers 			rc = parse_version(ver, p);
17954a88cd2SDeven Bowers 			break;
18054a88cd2SDeven Bowers 		default:
18154a88cd2SDeven Bowers 			rc = -EBADMSG;
18254a88cd2SDeven Bowers 		}
18354a88cd2SDeven Bowers 		if (rc)
18454a88cd2SDeven Bowers 			goto out;
18554a88cd2SDeven Bowers 		++idx;
18654a88cd2SDeven Bowers 	}
18754a88cd2SDeven Bowers 
18854a88cd2SDeven Bowers 	if (idx != __IPE_HEADER_MAX)
18954a88cd2SDeven Bowers 		rc = -EBADMSG;
19054a88cd2SDeven Bowers 
19154a88cd2SDeven Bowers out:
19254a88cd2SDeven Bowers 	kfree(ver);
19354a88cd2SDeven Bowers 	return rc;
19454a88cd2SDeven Bowers }
19554a88cd2SDeven Bowers 
19654a88cd2SDeven Bowers /**
19754a88cd2SDeven Bowers  * token_default() - Determine if the given token is "DEFAULT".
19854a88cd2SDeven Bowers  * @token: Supplies the token string to be compared.
19954a88cd2SDeven Bowers  *
20054a88cd2SDeven Bowers  * Return:
20154a88cd2SDeven Bowers  * * %false	- The token is not "DEFAULT"
20254a88cd2SDeven Bowers  * * %true	- The token is "DEFAULT"
20354a88cd2SDeven Bowers  */
token_default(char * token)20454a88cd2SDeven Bowers static bool token_default(char *token)
20554a88cd2SDeven Bowers {
20654a88cd2SDeven Bowers 	return !strcmp(token, "DEFAULT");
20754a88cd2SDeven Bowers }
20854a88cd2SDeven Bowers 
20954a88cd2SDeven Bowers /**
21054a88cd2SDeven Bowers  * free_rule() - Free the supplied ipe_rule struct.
21154a88cd2SDeven Bowers  * @r: Supplies the ipe_rule struct to be freed.
21254a88cd2SDeven Bowers  *
21354a88cd2SDeven Bowers  * Free a ipe_rule struct @r. Note @r must be removed from any lists before
21454a88cd2SDeven Bowers  * calling this function.
21554a88cd2SDeven Bowers  */
free_rule(struct ipe_rule * r)21654a88cd2SDeven Bowers static void free_rule(struct ipe_rule *r)
21754a88cd2SDeven Bowers {
21854a88cd2SDeven Bowers 	struct ipe_prop *p, *t;
21954a88cd2SDeven Bowers 
22054a88cd2SDeven Bowers 	if (IS_ERR_OR_NULL(r))
22154a88cd2SDeven Bowers 		return;
22254a88cd2SDeven Bowers 
22354a88cd2SDeven Bowers 	list_for_each_entry_safe(p, t, &r->props, next) {
22454a88cd2SDeven Bowers 		list_del(&p->next);
225e155858dSDeven Bowers 		ipe_digest_free(p->value);
22654a88cd2SDeven Bowers 		kfree(p);
22754a88cd2SDeven Bowers 	}
22854a88cd2SDeven Bowers 
22954a88cd2SDeven Bowers 	kfree(r);
23054a88cd2SDeven Bowers }
23154a88cd2SDeven Bowers 
23254a88cd2SDeven Bowers static const match_table_t operation_tokens = {
23354a88cd2SDeven Bowers 	{IPE_OP_EXEC,			"op=EXECUTE"},
23454a88cd2SDeven Bowers 	{IPE_OP_FIRMWARE,		"op=FIRMWARE"},
23554a88cd2SDeven Bowers 	{IPE_OP_KERNEL_MODULE,		"op=KMODULE"},
23654a88cd2SDeven Bowers 	{IPE_OP_KEXEC_IMAGE,		"op=KEXEC_IMAGE"},
23754a88cd2SDeven Bowers 	{IPE_OP_KEXEC_INITRAMFS,	"op=KEXEC_INITRAMFS"},
23854a88cd2SDeven Bowers 	{IPE_OP_POLICY,			"op=POLICY"},
23954a88cd2SDeven Bowers 	{IPE_OP_X509,			"op=X509_CERT"},
24054a88cd2SDeven Bowers 	{IPE_OP_INVALID,		NULL}
24154a88cd2SDeven Bowers };
24254a88cd2SDeven Bowers 
24354a88cd2SDeven Bowers /**
24454a88cd2SDeven Bowers  * parse_operation() - Parse the operation type given a token string.
24554a88cd2SDeven Bowers  * @t: Supplies the token string to be parsed.
24654a88cd2SDeven Bowers  *
24754a88cd2SDeven Bowers  * Return: The parsed operation type.
24854a88cd2SDeven Bowers  */
parse_operation(char * t)24954a88cd2SDeven Bowers static enum ipe_op_type parse_operation(char *t)
25054a88cd2SDeven Bowers {
25154a88cd2SDeven Bowers 	substring_t args[MAX_OPT_ARGS];
25254a88cd2SDeven Bowers 
25354a88cd2SDeven Bowers 	return match_token(t, operation_tokens, args);
25454a88cd2SDeven Bowers }
25554a88cd2SDeven Bowers 
25654a88cd2SDeven Bowers static const match_table_t action_tokens = {
25754a88cd2SDeven Bowers 	{IPE_ACTION_ALLOW,	"action=ALLOW"},
25854a88cd2SDeven Bowers 	{IPE_ACTION_DENY,	"action=DENY"},
25954a88cd2SDeven Bowers 	{IPE_ACTION_INVALID,	NULL}
26054a88cd2SDeven Bowers };
26154a88cd2SDeven Bowers 
26254a88cd2SDeven Bowers /**
26354a88cd2SDeven Bowers  * parse_action() - Parse the action type given a token string.
26454a88cd2SDeven Bowers  * @t: Supplies the token string to be parsed.
26554a88cd2SDeven Bowers  *
26654a88cd2SDeven Bowers  * Return: The parsed action type.
26754a88cd2SDeven Bowers  */
parse_action(char * t)26854a88cd2SDeven Bowers static enum ipe_action_type parse_action(char *t)
26954a88cd2SDeven Bowers {
27054a88cd2SDeven Bowers 	substring_t args[MAX_OPT_ARGS];
27154a88cd2SDeven Bowers 
27254a88cd2SDeven Bowers 	return match_token(t, action_tokens, args);
27354a88cd2SDeven Bowers }
27454a88cd2SDeven Bowers 
275a8a74df1SFan Wu static const match_table_t property_tokens = {
276a8a74df1SFan Wu 	{IPE_PROP_BOOT_VERIFIED_FALSE,	"boot_verified=FALSE"},
277a8a74df1SFan Wu 	{IPE_PROP_BOOT_VERIFIED_TRUE,	"boot_verified=TRUE"},
278e155858dSDeven Bowers 	{IPE_PROP_DMV_ROOTHASH,		"dmverity_roothash=%s"},
279e155858dSDeven Bowers 	{IPE_PROP_DMV_SIG_FALSE,	"dmverity_signature=FALSE"},
280e155858dSDeven Bowers 	{IPE_PROP_DMV_SIG_TRUE,		"dmverity_signature=TRUE"},
281*31f8c868SFan Wu 	{IPE_PROP_FSV_DIGEST,		"fsverity_digest=%s"},
282*31f8c868SFan Wu 	{IPE_PROP_FSV_SIG_FALSE,	"fsverity_signature=FALSE"},
283*31f8c868SFan Wu 	{IPE_PROP_FSV_SIG_TRUE,		"fsverity_signature=TRUE"},
284a8a74df1SFan Wu 	{IPE_PROP_INVALID,		NULL}
285a8a74df1SFan Wu };
286a8a74df1SFan Wu 
28754a88cd2SDeven Bowers /**
28854a88cd2SDeven Bowers  * parse_property() - Parse a rule property given a token string.
28954a88cd2SDeven Bowers  * @t: Supplies the token string to be parsed.
29054a88cd2SDeven Bowers  * @r: Supplies the ipe_rule the parsed property will be associated with.
29154a88cd2SDeven Bowers  *
292a8a74df1SFan Wu  * This function parses and associates a property with an IPE rule based
293a8a74df1SFan Wu  * on a token string.
29454a88cd2SDeven Bowers  *
29554a88cd2SDeven Bowers  * Return:
29654a88cd2SDeven Bowers  * * %0		- Success
29754a88cd2SDeven Bowers  * * %-ENOMEM	- Out of memory (OOM)
29854a88cd2SDeven Bowers  * * %-EBADMSG	- The supplied token cannot be parsed
29954a88cd2SDeven Bowers  */
parse_property(char * t,struct ipe_rule * r)30054a88cd2SDeven Bowers static int parse_property(char *t, struct ipe_rule *r)
30154a88cd2SDeven Bowers {
302a8a74df1SFan Wu 	substring_t args[MAX_OPT_ARGS];
303a8a74df1SFan Wu 	struct ipe_prop *p = NULL;
304a8a74df1SFan Wu 	int rc = 0;
305a8a74df1SFan Wu 	int token;
306e155858dSDeven Bowers 	char *dup = NULL;
307a8a74df1SFan Wu 
308a8a74df1SFan Wu 	p = kzalloc(sizeof(*p), GFP_KERNEL);
309a8a74df1SFan Wu 	if (!p)
310a8a74df1SFan Wu 		return -ENOMEM;
311a8a74df1SFan Wu 
312a8a74df1SFan Wu 	token = match_token(t, property_tokens, args);
313a8a74df1SFan Wu 
314a8a74df1SFan Wu 	switch (token) {
315e155858dSDeven Bowers 	case IPE_PROP_DMV_ROOTHASH:
316*31f8c868SFan Wu 	case IPE_PROP_FSV_DIGEST:
317e155858dSDeven Bowers 		dup = match_strdup(&args[0]);
318e155858dSDeven Bowers 		if (!dup) {
319e155858dSDeven Bowers 			rc = -ENOMEM;
320e155858dSDeven Bowers 			goto err;
321e155858dSDeven Bowers 		}
322e155858dSDeven Bowers 		p->value = ipe_digest_parse(dup);
323e155858dSDeven Bowers 		if (IS_ERR(p->value)) {
324e155858dSDeven Bowers 			rc = PTR_ERR(p->value);
325e155858dSDeven Bowers 			goto err;
326e155858dSDeven Bowers 		}
327e155858dSDeven Bowers 		fallthrough;
328a8a74df1SFan Wu 	case IPE_PROP_BOOT_VERIFIED_FALSE:
329a8a74df1SFan Wu 	case IPE_PROP_BOOT_VERIFIED_TRUE:
330e155858dSDeven Bowers 	case IPE_PROP_DMV_SIG_FALSE:
331e155858dSDeven Bowers 	case IPE_PROP_DMV_SIG_TRUE:
332*31f8c868SFan Wu 	case IPE_PROP_FSV_SIG_FALSE:
333*31f8c868SFan Wu 	case IPE_PROP_FSV_SIG_TRUE:
334a8a74df1SFan Wu 		p->type = token;
335a8a74df1SFan Wu 		break;
336a8a74df1SFan Wu 	default:
337a8a74df1SFan Wu 		rc = -EBADMSG;
338a8a74df1SFan Wu 		break;
339a8a74df1SFan Wu 	}
340a8a74df1SFan Wu 	if (rc)
341a8a74df1SFan Wu 		goto err;
342a8a74df1SFan Wu 	list_add_tail(&p->next, &r->props);
343a8a74df1SFan Wu 
344e155858dSDeven Bowers out:
345e155858dSDeven Bowers 	kfree(dup);
346a8a74df1SFan Wu 	return rc;
347a8a74df1SFan Wu err:
348a8a74df1SFan Wu 	kfree(p);
349e155858dSDeven Bowers 	goto out;
35054a88cd2SDeven Bowers }
35154a88cd2SDeven Bowers 
35254a88cd2SDeven Bowers /**
35354a88cd2SDeven Bowers  * parse_rule() - parse a policy rule line.
35454a88cd2SDeven Bowers  * @line: Supplies rule line to be parsed.
35554a88cd2SDeven Bowers  * @p: Supplies the partial parsed policy.
35654a88cd2SDeven Bowers  *
35754a88cd2SDeven Bowers  * Return:
35854a88cd2SDeven Bowers  * * 0		- Success
35954a88cd2SDeven Bowers  * * %-ENOMEM	- Out of memory (OOM)
36054a88cd2SDeven Bowers  * * %-EBADMSG	- Policy syntax error
36154a88cd2SDeven Bowers  */
parse_rule(char * line,struct ipe_parsed_policy * p)36254a88cd2SDeven Bowers static int parse_rule(char *line, struct ipe_parsed_policy *p)
36354a88cd2SDeven Bowers {
36454a88cd2SDeven Bowers 	enum ipe_action_type action = IPE_ACTION_INVALID;
36554a88cd2SDeven Bowers 	enum ipe_op_type op = IPE_OP_INVALID;
36654a88cd2SDeven Bowers 	bool is_default_rule = false;
36754a88cd2SDeven Bowers 	struct ipe_rule *r = NULL;
36854a88cd2SDeven Bowers 	bool first_token = true;
36954a88cd2SDeven Bowers 	bool op_parsed = false;
37054a88cd2SDeven Bowers 	int rc = 0;
37154a88cd2SDeven Bowers 	char *t;
37254a88cd2SDeven Bowers 
37354a88cd2SDeven Bowers 	if (IS_ERR_OR_NULL(line))
37454a88cd2SDeven Bowers 		return -EBADMSG;
37554a88cd2SDeven Bowers 
37654a88cd2SDeven Bowers 	r = kzalloc(sizeof(*r), GFP_KERNEL);
37754a88cd2SDeven Bowers 	if (!r)
37854a88cd2SDeven Bowers 		return -ENOMEM;
37954a88cd2SDeven Bowers 
38054a88cd2SDeven Bowers 	INIT_LIST_HEAD(&r->next);
38154a88cd2SDeven Bowers 	INIT_LIST_HEAD(&r->props);
38254a88cd2SDeven Bowers 
38354a88cd2SDeven Bowers 	while (t = strsep(&line, IPE_POLICY_DELIM), line) {
38454a88cd2SDeven Bowers 		if (*t == '\0')
38554a88cd2SDeven Bowers 			continue;
38654a88cd2SDeven Bowers 		if (first_token && token_default(t)) {
38754a88cd2SDeven Bowers 			is_default_rule = true;
38854a88cd2SDeven Bowers 		} else {
38954a88cd2SDeven Bowers 			if (!op_parsed) {
39054a88cd2SDeven Bowers 				op = parse_operation(t);
39154a88cd2SDeven Bowers 				if (op == IPE_OP_INVALID)
39254a88cd2SDeven Bowers 					rc = -EBADMSG;
39354a88cd2SDeven Bowers 				else
39454a88cd2SDeven Bowers 					op_parsed = true;
39554a88cd2SDeven Bowers 			} else {
39654a88cd2SDeven Bowers 				rc = parse_property(t, r);
39754a88cd2SDeven Bowers 			}
39854a88cd2SDeven Bowers 		}
39954a88cd2SDeven Bowers 
40054a88cd2SDeven Bowers 		if (rc)
40154a88cd2SDeven Bowers 			goto err;
40254a88cd2SDeven Bowers 		first_token = false;
40354a88cd2SDeven Bowers 	}
40454a88cd2SDeven Bowers 
40554a88cd2SDeven Bowers 	action = parse_action(t);
40654a88cd2SDeven Bowers 	if (action == IPE_ACTION_INVALID) {
40754a88cd2SDeven Bowers 		rc = -EBADMSG;
40854a88cd2SDeven Bowers 		goto err;
40954a88cd2SDeven Bowers 	}
41054a88cd2SDeven Bowers 
41154a88cd2SDeven Bowers 	if (is_default_rule) {
41254a88cd2SDeven Bowers 		if (!list_empty(&r->props)) {
41354a88cd2SDeven Bowers 			rc = -EBADMSG;
41454a88cd2SDeven Bowers 		} else if (op == IPE_OP_INVALID) {
41554a88cd2SDeven Bowers 			if (p->global_default_action != IPE_ACTION_INVALID)
41654a88cd2SDeven Bowers 				rc = -EBADMSG;
41754a88cd2SDeven Bowers 			else
41854a88cd2SDeven Bowers 				p->global_default_action = action;
41954a88cd2SDeven Bowers 		} else {
42054a88cd2SDeven Bowers 			if (p->rules[op].default_action != IPE_ACTION_INVALID)
42154a88cd2SDeven Bowers 				rc = -EBADMSG;
42254a88cd2SDeven Bowers 			else
42354a88cd2SDeven Bowers 				p->rules[op].default_action = action;
42454a88cd2SDeven Bowers 		}
42554a88cd2SDeven Bowers 	} else if (op != IPE_OP_INVALID && action != IPE_ACTION_INVALID) {
42654a88cd2SDeven Bowers 		r->op = op;
42754a88cd2SDeven Bowers 		r->action = action;
42854a88cd2SDeven Bowers 	} else {
42954a88cd2SDeven Bowers 		rc = -EBADMSG;
43054a88cd2SDeven Bowers 	}
43154a88cd2SDeven Bowers 
43254a88cd2SDeven Bowers 	if (rc)
43354a88cd2SDeven Bowers 		goto err;
43454a88cd2SDeven Bowers 	if (!is_default_rule)
43554a88cd2SDeven Bowers 		list_add_tail(&r->next, &p->rules[op].rules);
43654a88cd2SDeven Bowers 	else
43754a88cd2SDeven Bowers 		free_rule(r);
43854a88cd2SDeven Bowers 
43954a88cd2SDeven Bowers 	return rc;
44054a88cd2SDeven Bowers err:
44154a88cd2SDeven Bowers 	free_rule(r);
44254a88cd2SDeven Bowers 	return rc;
44354a88cd2SDeven Bowers }
44454a88cd2SDeven Bowers 
44554a88cd2SDeven Bowers /**
44654a88cd2SDeven Bowers  * ipe_free_parsed_policy() - free a parsed policy structure.
44754a88cd2SDeven Bowers  * @p: Supplies the parsed policy.
44854a88cd2SDeven Bowers  */
ipe_free_parsed_policy(struct ipe_parsed_policy * p)44954a88cd2SDeven Bowers void ipe_free_parsed_policy(struct ipe_parsed_policy *p)
45054a88cd2SDeven Bowers {
45154a88cd2SDeven Bowers 	struct ipe_rule *pp, *t;
45254a88cd2SDeven Bowers 	size_t i = 0;
45354a88cd2SDeven Bowers 
45454a88cd2SDeven Bowers 	if (IS_ERR_OR_NULL(p))
45554a88cd2SDeven Bowers 		return;
45654a88cd2SDeven Bowers 
45754a88cd2SDeven Bowers 	for (i = 0; i < ARRAY_SIZE(p->rules); ++i)
45854a88cd2SDeven Bowers 		list_for_each_entry_safe(pp, t, &p->rules[i].rules, next) {
45954a88cd2SDeven Bowers 			list_del(&pp->next);
46054a88cd2SDeven Bowers 			free_rule(pp);
46154a88cd2SDeven Bowers 		}
46254a88cd2SDeven Bowers 
46354a88cd2SDeven Bowers 	kfree(p->name);
46454a88cd2SDeven Bowers 	kfree(p);
46554a88cd2SDeven Bowers }
46654a88cd2SDeven Bowers 
46754a88cd2SDeven Bowers /**
46854a88cd2SDeven Bowers  * validate_policy() - validate a parsed policy.
46954a88cd2SDeven Bowers  * @p: Supplies the fully parsed policy.
47054a88cd2SDeven Bowers  *
47154a88cd2SDeven Bowers  * Given a policy structure that was just parsed, validate that all
47254a88cd2SDeven Bowers  * operations have their default rules or a global default rule is set.
47354a88cd2SDeven Bowers  *
47454a88cd2SDeven Bowers  * Return:
47554a88cd2SDeven Bowers  * * %0		- Success
47654a88cd2SDeven Bowers  * * %-EBADMSG	- Policy is invalid
47754a88cd2SDeven Bowers  */
validate_policy(const struct ipe_parsed_policy * p)47854a88cd2SDeven Bowers static int validate_policy(const struct ipe_parsed_policy *p)
47954a88cd2SDeven Bowers {
48054a88cd2SDeven Bowers 	size_t i = 0;
48154a88cd2SDeven Bowers 
48254a88cd2SDeven Bowers 	if (p->global_default_action != IPE_ACTION_INVALID)
48354a88cd2SDeven Bowers 		return 0;
48454a88cd2SDeven Bowers 
48554a88cd2SDeven Bowers 	for (i = 0; i < ARRAY_SIZE(p->rules); ++i) {
48654a88cd2SDeven Bowers 		if (p->rules[i].default_action == IPE_ACTION_INVALID)
48754a88cd2SDeven Bowers 			return -EBADMSG;
48854a88cd2SDeven Bowers 	}
48954a88cd2SDeven Bowers 
49054a88cd2SDeven Bowers 	return 0;
49154a88cd2SDeven Bowers }
49254a88cd2SDeven Bowers 
49354a88cd2SDeven Bowers /**
49454a88cd2SDeven Bowers  * ipe_parse_policy() - Given a string, parse the string into an IPE policy.
49554a88cd2SDeven Bowers  * @p: partially filled ipe_policy structure to populate with the result.
49654a88cd2SDeven Bowers  *     it must have text and textlen set.
49754a88cd2SDeven Bowers  *
49854a88cd2SDeven Bowers  * Return:
49954a88cd2SDeven Bowers  * * %0		- Success
50054a88cd2SDeven Bowers  * * %-EBADMSG	- Policy is invalid
50154a88cd2SDeven Bowers  * * %-ENOMEM	- Out of Memory
50254a88cd2SDeven Bowers  * * %-ERANGE	- Policy version number overflow
50354a88cd2SDeven Bowers  * * %-EINVAL	- Policy version parsing error
50454a88cd2SDeven Bowers  */
ipe_parse_policy(struct ipe_policy * p)50554a88cd2SDeven Bowers int ipe_parse_policy(struct ipe_policy *p)
50654a88cd2SDeven Bowers {
50754a88cd2SDeven Bowers 	struct ipe_parsed_policy *pp = NULL;
50854a88cd2SDeven Bowers 	char *policy = NULL, *dup = NULL;
50954a88cd2SDeven Bowers 	bool header_parsed = false;
51054a88cd2SDeven Bowers 	char *line = NULL;
51154a88cd2SDeven Bowers 	size_t len;
51254a88cd2SDeven Bowers 	int rc = 0;
51354a88cd2SDeven Bowers 
51454a88cd2SDeven Bowers 	if (!p->textlen)
51554a88cd2SDeven Bowers 		return -EBADMSG;
51654a88cd2SDeven Bowers 
51754a88cd2SDeven Bowers 	policy = kmemdup_nul(p->text, p->textlen, GFP_KERNEL);
51854a88cd2SDeven Bowers 	if (!policy)
51954a88cd2SDeven Bowers 		return -ENOMEM;
52054a88cd2SDeven Bowers 	dup = policy;
52154a88cd2SDeven Bowers 
52254a88cd2SDeven Bowers 	pp = new_parsed_policy();
52354a88cd2SDeven Bowers 	if (IS_ERR(pp)) {
52454a88cd2SDeven Bowers 		rc = PTR_ERR(pp);
52554a88cd2SDeven Bowers 		goto out;
52654a88cd2SDeven Bowers 	}
52754a88cd2SDeven Bowers 
52854a88cd2SDeven Bowers 	while ((line = strsep(&policy, IPE_LINE_DELIM)) != NULL) {
52954a88cd2SDeven Bowers 		remove_comment(line);
53054a88cd2SDeven Bowers 		len = remove_trailing_spaces(line);
53154a88cd2SDeven Bowers 		if (!len)
53254a88cd2SDeven Bowers 			continue;
53354a88cd2SDeven Bowers 
53454a88cd2SDeven Bowers 		if (!header_parsed) {
53554a88cd2SDeven Bowers 			rc = parse_header(line, pp);
53654a88cd2SDeven Bowers 			if (rc)
53754a88cd2SDeven Bowers 				goto err;
53854a88cd2SDeven Bowers 			header_parsed = true;
53954a88cd2SDeven Bowers 		} else {
54054a88cd2SDeven Bowers 			rc = parse_rule(line, pp);
54154a88cd2SDeven Bowers 			if (rc)
54254a88cd2SDeven Bowers 				goto err;
54354a88cd2SDeven Bowers 		}
54454a88cd2SDeven Bowers 	}
54554a88cd2SDeven Bowers 
54654a88cd2SDeven Bowers 	if (!header_parsed || validate_policy(pp)) {
54754a88cd2SDeven Bowers 		rc = -EBADMSG;
54854a88cd2SDeven Bowers 		goto err;
54954a88cd2SDeven Bowers 	}
55054a88cd2SDeven Bowers 
55154a88cd2SDeven Bowers 	p->parsed = pp;
55254a88cd2SDeven Bowers 
55354a88cd2SDeven Bowers out:
55454a88cd2SDeven Bowers 	kfree(dup);
55554a88cd2SDeven Bowers 	return rc;
55654a88cd2SDeven Bowers err:
55754a88cd2SDeven Bowers 	ipe_free_parsed_policy(pp);
55854a88cd2SDeven Bowers 	goto out;
55954a88cd2SDeven Bowers }
560