xref: /linux/security/ipe/policy_parser.c (revision a8a74df1)
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * Copyright (C) 2020-2024 Microsoft Corporation. All rights reserved.
4  */
5 
6 #include <linux/err.h>
7 #include <linux/slab.h>
8 #include <linux/parser.h>
9 #include <linux/types.h>
10 #include <linux/ctype.h>
11 
12 #include "policy.h"
13 #include "policy_parser.h"
14 
15 #define START_COMMENT	'#'
16 #define IPE_POLICY_DELIM " \t"
17 #define IPE_LINE_DELIM "\n\r"
18 
19 /**
20  * new_parsed_policy() - Allocate and initialize a parsed policy.
21  *
22  * Return:
23  * * a pointer to the ipe_parsed_policy structure	- Success
24  * * %-ENOMEM						- Out of memory (OOM)
25  */
26 static struct ipe_parsed_policy *new_parsed_policy(void)
27 {
28 	struct ipe_parsed_policy *p = NULL;
29 	struct ipe_op_table *t = NULL;
30 	size_t i = 0;
31 
32 	p = kzalloc(sizeof(*p), GFP_KERNEL);
33 	if (!p)
34 		return ERR_PTR(-ENOMEM);
35 
36 	p->global_default_action = IPE_ACTION_INVALID;
37 
38 	for (i = 0; i < ARRAY_SIZE(p->rules); ++i) {
39 		t = &p->rules[i];
40 
41 		t->default_action = IPE_ACTION_INVALID;
42 		INIT_LIST_HEAD(&t->rules);
43 	}
44 
45 	return p;
46 }
47 
48 /**
49  * remove_comment() - Truncate all chars following START_COMMENT in a string.
50  *
51  * @line: Supplies a policy line string for preprocessing.
52  */
53 static void remove_comment(char *line)
54 {
55 	line = strchr(line, START_COMMENT);
56 
57 	if (line)
58 		*line = '\0';
59 }
60 
61 /**
62  * remove_trailing_spaces() - Truncate all trailing spaces in a string.
63  *
64  * @line: Supplies a policy line string for preprocessing.
65  *
66  * Return: The length of truncated string.
67  */
68 static size_t remove_trailing_spaces(char *line)
69 {
70 	size_t i = 0;
71 
72 	i = strlen(line);
73 	while (i > 0 && isspace(line[i - 1]))
74 		i--;
75 
76 	line[i] = '\0';
77 
78 	return i;
79 }
80 
81 /**
82  * parse_version() - Parse policy version.
83  * @ver: Supplies a version string to be parsed.
84  * @p: Supplies the partial parsed policy.
85  *
86  * Return:
87  * * %0		- Success
88  * * %-EBADMSG	- Version string is invalid
89  * * %-ERANGE	- Version number overflow
90  * * %-EINVAL	- Parsing error
91  */
92 static int parse_version(char *ver, struct ipe_parsed_policy *p)
93 {
94 	u16 *const cv[] = { &p->version.major, &p->version.minor, &p->version.rev };
95 	size_t sep_count = 0;
96 	char *token;
97 	int rc = 0;
98 
99 	while ((token = strsep(&ver, ".")) != NULL) {
100 		/* prevent overflow */
101 		if (sep_count >= ARRAY_SIZE(cv))
102 			return -EBADMSG;
103 
104 		rc = kstrtou16(token, 10, cv[sep_count]);
105 		if (rc)
106 			return rc;
107 
108 		++sep_count;
109 	}
110 
111 	/* prevent underflow */
112 	if (sep_count != ARRAY_SIZE(cv))
113 		return -EBADMSG;
114 
115 	return 0;
116 }
117 
118 enum header_opt {
119 	IPE_HEADER_POLICY_NAME = 0,
120 	IPE_HEADER_POLICY_VERSION,
121 	__IPE_HEADER_MAX
122 };
123 
124 static const match_table_t header_tokens = {
125 	{IPE_HEADER_POLICY_NAME,	"policy_name=%s"},
126 	{IPE_HEADER_POLICY_VERSION,	"policy_version=%s"},
127 	{__IPE_HEADER_MAX,		NULL}
128 };
129 
130 /**
131  * parse_header() - Parse policy header information.
132  * @line: Supplies header line to be parsed.
133  * @p: Supplies the partial parsed policy.
134  *
135  * Return:
136  * * %0		- Success
137  * * %-EBADMSG	- Header string is invalid
138  * * %-ENOMEM	- Out of memory (OOM)
139  * * %-ERANGE	- Version number overflow
140  * * %-EINVAL	- Version parsing error
141  */
142 static int parse_header(char *line, struct ipe_parsed_policy *p)
143 {
144 	substring_t args[MAX_OPT_ARGS];
145 	char *t, *ver = NULL;
146 	size_t idx = 0;
147 	int rc = 0;
148 
149 	while ((t = strsep(&line, IPE_POLICY_DELIM)) != NULL) {
150 		int token;
151 
152 		if (*t == '\0')
153 			continue;
154 		if (idx >= __IPE_HEADER_MAX) {
155 			rc = -EBADMSG;
156 			goto out;
157 		}
158 
159 		token = match_token(t, header_tokens, args);
160 		if (token != idx) {
161 			rc = -EBADMSG;
162 			goto out;
163 		}
164 
165 		switch (token) {
166 		case IPE_HEADER_POLICY_NAME:
167 			p->name = match_strdup(&args[0]);
168 			if (!p->name)
169 				rc = -ENOMEM;
170 			break;
171 		case IPE_HEADER_POLICY_VERSION:
172 			ver = match_strdup(&args[0]);
173 			if (!ver) {
174 				rc = -ENOMEM;
175 				break;
176 			}
177 			rc = parse_version(ver, p);
178 			break;
179 		default:
180 			rc = -EBADMSG;
181 		}
182 		if (rc)
183 			goto out;
184 		++idx;
185 	}
186 
187 	if (idx != __IPE_HEADER_MAX)
188 		rc = -EBADMSG;
189 
190 out:
191 	kfree(ver);
192 	return rc;
193 }
194 
195 /**
196  * token_default() - Determine if the given token is "DEFAULT".
197  * @token: Supplies the token string to be compared.
198  *
199  * Return:
200  * * %false	- The token is not "DEFAULT"
201  * * %true	- The token is "DEFAULT"
202  */
203 static bool token_default(char *token)
204 {
205 	return !strcmp(token, "DEFAULT");
206 }
207 
208 /**
209  * free_rule() - Free the supplied ipe_rule struct.
210  * @r: Supplies the ipe_rule struct to be freed.
211  *
212  * Free a ipe_rule struct @r. Note @r must be removed from any lists before
213  * calling this function.
214  */
215 static void free_rule(struct ipe_rule *r)
216 {
217 	struct ipe_prop *p, *t;
218 
219 	if (IS_ERR_OR_NULL(r))
220 		return;
221 
222 	list_for_each_entry_safe(p, t, &r->props, next) {
223 		list_del(&p->next);
224 		kfree(p);
225 	}
226 
227 	kfree(r);
228 }
229 
230 static const match_table_t operation_tokens = {
231 	{IPE_OP_EXEC,			"op=EXECUTE"},
232 	{IPE_OP_FIRMWARE,		"op=FIRMWARE"},
233 	{IPE_OP_KERNEL_MODULE,		"op=KMODULE"},
234 	{IPE_OP_KEXEC_IMAGE,		"op=KEXEC_IMAGE"},
235 	{IPE_OP_KEXEC_INITRAMFS,	"op=KEXEC_INITRAMFS"},
236 	{IPE_OP_POLICY,			"op=POLICY"},
237 	{IPE_OP_X509,			"op=X509_CERT"},
238 	{IPE_OP_INVALID,		NULL}
239 };
240 
241 /**
242  * parse_operation() - Parse the operation type given a token string.
243  * @t: Supplies the token string to be parsed.
244  *
245  * Return: The parsed operation type.
246  */
247 static enum ipe_op_type parse_operation(char *t)
248 {
249 	substring_t args[MAX_OPT_ARGS];
250 
251 	return match_token(t, operation_tokens, args);
252 }
253 
254 static const match_table_t action_tokens = {
255 	{IPE_ACTION_ALLOW,	"action=ALLOW"},
256 	{IPE_ACTION_DENY,	"action=DENY"},
257 	{IPE_ACTION_INVALID,	NULL}
258 };
259 
260 /**
261  * parse_action() - Parse the action type given a token string.
262  * @t: Supplies the token string to be parsed.
263  *
264  * Return: The parsed action type.
265  */
266 static enum ipe_action_type parse_action(char *t)
267 {
268 	substring_t args[MAX_OPT_ARGS];
269 
270 	return match_token(t, action_tokens, args);
271 }
272 
273 static const match_table_t property_tokens = {
274 	{IPE_PROP_BOOT_VERIFIED_FALSE,	"boot_verified=FALSE"},
275 	{IPE_PROP_BOOT_VERIFIED_TRUE,	"boot_verified=TRUE"},
276 	{IPE_PROP_INVALID,		NULL}
277 };
278 
279 /**
280  * parse_property() - Parse a rule property given a token string.
281  * @t: Supplies the token string to be parsed.
282  * @r: Supplies the ipe_rule the parsed property will be associated with.
283  *
284  * This function parses and associates a property with an IPE rule based
285  * on a token string.
286  *
287  * Return:
288  * * %0		- Success
289  * * %-ENOMEM	- Out of memory (OOM)
290  * * %-EBADMSG	- The supplied token cannot be parsed
291  */
292 static int parse_property(char *t, struct ipe_rule *r)
293 {
294 	substring_t args[MAX_OPT_ARGS];
295 	struct ipe_prop *p = NULL;
296 	int rc = 0;
297 	int token;
298 
299 	p = kzalloc(sizeof(*p), GFP_KERNEL);
300 	if (!p)
301 		return -ENOMEM;
302 
303 	token = match_token(t, property_tokens, args);
304 
305 	switch (token) {
306 	case IPE_PROP_BOOT_VERIFIED_FALSE:
307 	case IPE_PROP_BOOT_VERIFIED_TRUE:
308 		p->type = token;
309 		break;
310 	default:
311 		rc = -EBADMSG;
312 		break;
313 	}
314 	if (rc)
315 		goto err;
316 	list_add_tail(&p->next, &r->props);
317 
318 	return rc;
319 err:
320 	kfree(p);
321 	return rc;
322 }
323 
324 /**
325  * parse_rule() - parse a policy rule line.
326  * @line: Supplies rule line to be parsed.
327  * @p: Supplies the partial parsed policy.
328  *
329  * Return:
330  * * 0		- Success
331  * * %-ENOMEM	- Out of memory (OOM)
332  * * %-EBADMSG	- Policy syntax error
333  */
334 static int parse_rule(char *line, struct ipe_parsed_policy *p)
335 {
336 	enum ipe_action_type action = IPE_ACTION_INVALID;
337 	enum ipe_op_type op = IPE_OP_INVALID;
338 	bool is_default_rule = false;
339 	struct ipe_rule *r = NULL;
340 	bool first_token = true;
341 	bool op_parsed = false;
342 	int rc = 0;
343 	char *t;
344 
345 	if (IS_ERR_OR_NULL(line))
346 		return -EBADMSG;
347 
348 	r = kzalloc(sizeof(*r), GFP_KERNEL);
349 	if (!r)
350 		return -ENOMEM;
351 
352 	INIT_LIST_HEAD(&r->next);
353 	INIT_LIST_HEAD(&r->props);
354 
355 	while (t = strsep(&line, IPE_POLICY_DELIM), line) {
356 		if (*t == '\0')
357 			continue;
358 		if (first_token && token_default(t)) {
359 			is_default_rule = true;
360 		} else {
361 			if (!op_parsed) {
362 				op = parse_operation(t);
363 				if (op == IPE_OP_INVALID)
364 					rc = -EBADMSG;
365 				else
366 					op_parsed = true;
367 			} else {
368 				rc = parse_property(t, r);
369 			}
370 		}
371 
372 		if (rc)
373 			goto err;
374 		first_token = false;
375 	}
376 
377 	action = parse_action(t);
378 	if (action == IPE_ACTION_INVALID) {
379 		rc = -EBADMSG;
380 		goto err;
381 	}
382 
383 	if (is_default_rule) {
384 		if (!list_empty(&r->props)) {
385 			rc = -EBADMSG;
386 		} else if (op == IPE_OP_INVALID) {
387 			if (p->global_default_action != IPE_ACTION_INVALID)
388 				rc = -EBADMSG;
389 			else
390 				p->global_default_action = action;
391 		} else {
392 			if (p->rules[op].default_action != IPE_ACTION_INVALID)
393 				rc = -EBADMSG;
394 			else
395 				p->rules[op].default_action = action;
396 		}
397 	} else if (op != IPE_OP_INVALID && action != IPE_ACTION_INVALID) {
398 		r->op = op;
399 		r->action = action;
400 	} else {
401 		rc = -EBADMSG;
402 	}
403 
404 	if (rc)
405 		goto err;
406 	if (!is_default_rule)
407 		list_add_tail(&r->next, &p->rules[op].rules);
408 	else
409 		free_rule(r);
410 
411 	return rc;
412 err:
413 	free_rule(r);
414 	return rc;
415 }
416 
417 /**
418  * ipe_free_parsed_policy() - free a parsed policy structure.
419  * @p: Supplies the parsed policy.
420  */
421 void ipe_free_parsed_policy(struct ipe_parsed_policy *p)
422 {
423 	struct ipe_rule *pp, *t;
424 	size_t i = 0;
425 
426 	if (IS_ERR_OR_NULL(p))
427 		return;
428 
429 	for (i = 0; i < ARRAY_SIZE(p->rules); ++i)
430 		list_for_each_entry_safe(pp, t, &p->rules[i].rules, next) {
431 			list_del(&pp->next);
432 			free_rule(pp);
433 		}
434 
435 	kfree(p->name);
436 	kfree(p);
437 }
438 
439 /**
440  * validate_policy() - validate a parsed policy.
441  * @p: Supplies the fully parsed policy.
442  *
443  * Given a policy structure that was just parsed, validate that all
444  * operations have their default rules or a global default rule is set.
445  *
446  * Return:
447  * * %0		- Success
448  * * %-EBADMSG	- Policy is invalid
449  */
450 static int validate_policy(const struct ipe_parsed_policy *p)
451 {
452 	size_t i = 0;
453 
454 	if (p->global_default_action != IPE_ACTION_INVALID)
455 		return 0;
456 
457 	for (i = 0; i < ARRAY_SIZE(p->rules); ++i) {
458 		if (p->rules[i].default_action == IPE_ACTION_INVALID)
459 			return -EBADMSG;
460 	}
461 
462 	return 0;
463 }
464 
465 /**
466  * ipe_parse_policy() - Given a string, parse the string into an IPE policy.
467  * @p: partially filled ipe_policy structure to populate with the result.
468  *     it must have text and textlen set.
469  *
470  * Return:
471  * * %0		- Success
472  * * %-EBADMSG	- Policy is invalid
473  * * %-ENOMEM	- Out of Memory
474  * * %-ERANGE	- Policy version number overflow
475  * * %-EINVAL	- Policy version parsing error
476  */
477 int ipe_parse_policy(struct ipe_policy *p)
478 {
479 	struct ipe_parsed_policy *pp = NULL;
480 	char *policy = NULL, *dup = NULL;
481 	bool header_parsed = false;
482 	char *line = NULL;
483 	size_t len;
484 	int rc = 0;
485 
486 	if (!p->textlen)
487 		return -EBADMSG;
488 
489 	policy = kmemdup_nul(p->text, p->textlen, GFP_KERNEL);
490 	if (!policy)
491 		return -ENOMEM;
492 	dup = policy;
493 
494 	pp = new_parsed_policy();
495 	if (IS_ERR(pp)) {
496 		rc = PTR_ERR(pp);
497 		goto out;
498 	}
499 
500 	while ((line = strsep(&policy, IPE_LINE_DELIM)) != NULL) {
501 		remove_comment(line);
502 		len = remove_trailing_spaces(line);
503 		if (!len)
504 			continue;
505 
506 		if (!header_parsed) {
507 			rc = parse_header(line, pp);
508 			if (rc)
509 				goto err;
510 			header_parsed = true;
511 		} else {
512 			rc = parse_rule(line, pp);
513 			if (rc)
514 				goto err;
515 		}
516 	}
517 
518 	if (!header_parsed || validate_policy(pp)) {
519 		rc = -EBADMSG;
520 		goto err;
521 	}
522 
523 	p->parsed = pp;
524 
525 out:
526 	kfree(dup);
527 	return rc;
528 err:
529 	ipe_free_parsed_policy(pp);
530 	goto out;
531 }
532