File Index
Files and directories
Sign in
FILE

policy_parser.c

File preview with syntax highlighting.

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