559 lines · 11.5 KB · C
RawDownload
// SPDX-License-Identifier: GPL-2.0/* * Copyright (C) 2020-2024 Microsoft Corporation. All rights reserved. */ #include <linux/err.h>#include <linux/slab.h>#include <linux/parser.h>#include <linux/types.h>#include <linux/ctype.h> #include "policy.h"#include "policy_parser.h"#include "digest.h" #define START_COMMENT	'#'#define IPE_POLICY_DELIM " \t"#define IPE_LINE_DELIM "\n\r" /** * new_parsed_policy() - Allocate and initialize a parsed policy. * * Return: * * a pointer to the ipe_parsed_policy structure	- Success * * %-ENOMEM						- Out of memory (OOM) */static struct ipe_parsed_policy *new_parsed_policy(void){	struct ipe_parsed_policy *p = NULL;	struct ipe_op_table *t = NULL;	size_t i = 0; 	p = kzalloc(sizeof(*p), GFP_KERNEL);	if (!p)		return ERR_PTR(-ENOMEM); 	p->global_default_action = IPE_ACTION_INVALID; 	for (i = 0; i < ARRAY_SIZE(p->rules); ++i) {		t = &p->rules[i]; 		t->default_action = IPE_ACTION_INVALID;		INIT_LIST_HEAD(&t->rules);	} 	return p;} /** * remove_comment() - Truncate all chars following START_COMMENT in a string. * * @line: Supplies a policy line string for preprocessing. */static void remove_comment(char *line){	line = strchr(line, START_COMMENT); 	if (line)		*line = '\0';} /** * remove_trailing_spaces() - Truncate all trailing spaces in a string. * * @line: Supplies a policy line string for preprocessing. * * Return: The length of truncated string. */static size_t remove_trailing_spaces(char *line){	size_t i = 0; 	i = strlen(line);	while (i > 0 && isspace(line[i - 1]))		i--; 	line[i] = '\0'; 	return i;} /** * parse_version() - Parse policy version. * @ver: Supplies a version string to be parsed. * @p: Supplies the partial parsed policy. * * Return: * * %0		- Success * * %-EBADMSG	- Version string is invalid * * %-ERANGE	- Version number overflow * * %-EINVAL	- Parsing error */static int parse_version(char *ver, struct ipe_parsed_policy *p){	u16 *const cv[] = { &p->version.major, &p->version.minor, &p->version.rev };	size_t sep_count = 0;	char *token;	int rc = 0; 	while ((token = strsep(&ver, ".")) != NULL) {		/* prevent overflow */		if (sep_count >= ARRAY_SIZE(cv))			return -EBADMSG; 		rc = kstrtou16(token, 10, cv[sep_count]);		if (rc)			return rc; 		++sep_count;	} 	/* prevent underflow */	if (sep_count != ARRAY_SIZE(cv))		return -EBADMSG; 	return 0;} enum header_opt {	IPE_HEADER_POLICY_NAME = 0,	IPE_HEADER_POLICY_VERSION,	__IPE_HEADER_MAX}; static const match_table_t header_tokens = {	{IPE_HEADER_POLICY_NAME,	"policy_name=%s"},	{IPE_HEADER_POLICY_VERSION,	"policy_version=%s"},	{__IPE_HEADER_MAX,		NULL}}; /** * parse_header() - Parse policy header information. * @line: Supplies header line to be parsed. * @p: Supplies the partial parsed policy. * * Return: * * %0		- Success * * %-EBADMSG	- Header string is invalid * * %-ENOMEM	- Out of memory (OOM) * * %-ERANGE	- Version number overflow * * %-EINVAL	- Version parsing error */static int parse_header(char *line, struct ipe_parsed_policy *p){	substring_t args[MAX_OPT_ARGS];	char *t, *ver = NULL;	size_t idx = 0;	int rc = 0; 	while ((t = strsep(&line, IPE_POLICY_DELIM)) != NULL) {		int token; 		if (*t == '\0')			continue;		if (idx >= __IPE_HEADER_MAX) {			rc = -EBADMSG;			goto out;		} 		token = match_token(t, header_tokens, args);		if (token != idx) {			rc = -EBADMSG;			goto out;		} 		switch (token) {		case IPE_HEADER_POLICY_NAME:			p->name = match_strdup(&args[0]);			if (!p->name)				rc = -ENOMEM;			break;		case IPE_HEADER_POLICY_VERSION:			ver = match_strdup(&args[0]);			if (!ver) {				rc = -ENOMEM;				break;			}			rc = parse_version(ver, p);			break;		default:			rc = -EBADMSG;		}		if (rc)			goto out;		++idx;	} 	if (idx != __IPE_HEADER_MAX)		rc = -EBADMSG; out:	kfree(ver);	return rc;} /** * token_default() - Determine if the given token is "DEFAULT". * @token: Supplies the token string to be compared. * * Return: * * %false	- The token is not "DEFAULT" * * %true	- The token is "DEFAULT" */static bool token_default(char *token){	return !strcmp(token, "DEFAULT");} /** * free_rule() - Free the supplied ipe_rule struct. * @r: Supplies the ipe_rule struct to be freed. * * Free a ipe_rule struct @r. Note @r must be removed from any lists before * calling this function. */static void free_rule(struct ipe_rule *r){	struct ipe_prop *p, *t; 	if (IS_ERR_OR_NULL(r))		return; 	list_for_each_entry_safe(p, t, &r->props, next) {		list_del(&p->next);		ipe_digest_free(p->value);		kfree(p);	} 	kfree(r);} static const match_table_t operation_tokens = {	{IPE_OP_EXEC,			"op=EXECUTE"},	{IPE_OP_FIRMWARE,		"op=FIRMWARE"},	{IPE_OP_KERNEL_MODULE,		"op=KMODULE"},	{IPE_OP_KEXEC_IMAGE,		"op=KEXEC_IMAGE"},	{IPE_OP_KEXEC_INITRAMFS,	"op=KEXEC_INITRAMFS"},	{IPE_OP_POLICY,			"op=POLICY"},	{IPE_OP_X509,			"op=X509_CERT"},	{IPE_OP_INVALID,		NULL}}; /** * parse_operation() - Parse the operation type given a token string. * @t: Supplies the token string to be parsed. * * Return: The parsed operation type. */static enum ipe_op_type parse_operation(char *t){	substring_t args[MAX_OPT_ARGS]; 	return match_token(t, operation_tokens, args);} static const match_table_t action_tokens = {	{IPE_ACTION_ALLOW,	"action=ALLOW"},	{IPE_ACTION_DENY,	"action=DENY"},	{IPE_ACTION_INVALID,	NULL}}; /** * parse_action() - Parse the action type given a token string. * @t: Supplies the token string to be parsed. * * Return: The parsed action type. */static enum ipe_action_type parse_action(char *t){	substring_t args[MAX_OPT_ARGS]; 	return match_token(t, action_tokens, args);} static const match_table_t property_tokens = {	{IPE_PROP_BOOT_VERIFIED_FALSE,	"boot_verified=FALSE"},	{IPE_PROP_BOOT_VERIFIED_TRUE,	"boot_verified=TRUE"},	{IPE_PROP_DMV_ROOTHASH,		"dmverity_roothash=%s"},	{IPE_PROP_DMV_SIG_FALSE,	"dmverity_signature=FALSE"},	{IPE_PROP_DMV_SIG_TRUE,		"dmverity_signature=TRUE"},	{IPE_PROP_FSV_DIGEST,		"fsverity_digest=%s"},	{IPE_PROP_FSV_SIG_FALSE,	"fsverity_signature=FALSE"},	{IPE_PROP_FSV_SIG_TRUE,		"fsverity_signature=TRUE"},	{IPE_PROP_INVALID,		NULL}}; /** * parse_property() - Parse a rule property given a token string. * @t: Supplies the token string to be parsed. * @r: Supplies the ipe_rule the parsed property will be associated with. * * This function parses and associates a property with an IPE rule based * on a token string. * * Return: * * %0		- Success * * %-ENOMEM	- Out of memory (OOM) * * %-EBADMSG	- The supplied token cannot be parsed */static int parse_property(char *t, struct ipe_rule *r){	substring_t args[MAX_OPT_ARGS];	struct ipe_prop *p = NULL;	int rc = 0;	int token;	char *dup = NULL; 	p = kzalloc(sizeof(*p), GFP_KERNEL);	if (!p)		return -ENOMEM; 	token = match_token(t, property_tokens, args); 	switch (token) {	case IPE_PROP_DMV_ROOTHASH:	case IPE_PROP_FSV_DIGEST:		dup = match_strdup(&args[0]);		if (!dup) {			rc = -ENOMEM;			goto err;		}		p->value = ipe_digest_parse(dup);		if (IS_ERR(p->value)) {			rc = PTR_ERR(p->value);			goto err;		}		fallthrough;	case IPE_PROP_BOOT_VERIFIED_FALSE:	case IPE_PROP_BOOT_VERIFIED_TRUE:	case IPE_PROP_DMV_SIG_FALSE:	case IPE_PROP_DMV_SIG_TRUE:	case IPE_PROP_FSV_SIG_FALSE:	case IPE_PROP_FSV_SIG_TRUE:		p->type = token;		break;	default:		rc = -EBADMSG;		break;	}	if (rc)		goto err;	list_add_tail(&p->next, &r->props); out:	kfree(dup);	return rc;err:	kfree(p);	goto out;} /** * parse_rule() - parse a policy rule line. * @line: Supplies rule line to be parsed. * @p: Supplies the partial parsed policy. * * Return: * * 0		- Success * * %-ENOMEM	- Out of memory (OOM) * * %-EBADMSG	- Policy syntax error */static int parse_rule(char *line, struct ipe_parsed_policy *p){	enum ipe_action_type action = IPE_ACTION_INVALID;	enum ipe_op_type op = IPE_OP_INVALID;	bool is_default_rule = false;	struct ipe_rule *r = NULL;	bool first_token = true;	bool op_parsed = false;	int rc = 0;	char *t; 	if (IS_ERR_OR_NULL(line))		return -EBADMSG; 	r = kzalloc(sizeof(*r), GFP_KERNEL);	if (!r)		return -ENOMEM; 	INIT_LIST_HEAD(&r->next);	INIT_LIST_HEAD(&r->props); 	while (t = strsep(&line, IPE_POLICY_DELIM), line) {		if (*t == '\0')			continue;		if (first_token && token_default(t)) {			is_default_rule = true;		} else {			if (!op_parsed) {				op = parse_operation(t);				if (op == IPE_OP_INVALID)					rc = -EBADMSG;				else					op_parsed = true;			} else {				rc = parse_property(t, r);			}		} 		if (rc)			goto err;		first_token = false;	} 	action = parse_action(t);	if (action == IPE_ACTION_INVALID) {		rc = -EBADMSG;		goto err;	} 	if (is_default_rule) {		if (!list_empty(&r->props)) {			rc = -EBADMSG;		} else if (op == IPE_OP_INVALID) {			if (p->global_default_action != IPE_ACTION_INVALID)				rc = -EBADMSG;			else				p->global_default_action = action;		} else {			if (p->rules[op].default_action != IPE_ACTION_INVALID)				rc = -EBADMSG;			else				p->rules[op].default_action = action;		}	} else if (op != IPE_OP_INVALID && action != IPE_ACTION_INVALID) {		r->op = op;		r->action = action;	} else {		rc = -EBADMSG;	} 	if (rc)		goto err;	if (!is_default_rule)		list_add_tail(&r->next, &p->rules[op].rules);	else		free_rule(r); 	return rc;err:	free_rule(r);	return rc;} /** * ipe_free_parsed_policy() - free a parsed policy structure. * @p: Supplies the parsed policy. */void ipe_free_parsed_policy(struct ipe_parsed_policy *p){	struct ipe_rule *pp, *t;	size_t i = 0; 	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) {			list_del(&pp->next);			free_rule(pp);		} 	kfree(p->name);	kfree(p);} /** * validate_policy() - validate a parsed policy. * @p: Supplies the fully parsed policy. * * Given a policy structure that was just parsed, validate that all * operations have their default rules or a global default rule is set. * * Return: * * %0		- Success * * %-EBADMSG	- Policy is invalid */static int validate_policy(const struct ipe_parsed_policy *p){	size_t i = 0; 	if (p->global_default_action != IPE_ACTION_INVALID)		return 0; 	for (i = 0; i < ARRAY_SIZE(p->rules); ++i) {		if (p->rules[i].default_action == IPE_ACTION_INVALID)			return -EBADMSG;	} 	return 0;} /** * ipe_parse_policy() - Given a string, parse the string into an IPE policy. * @p: partially filled ipe_policy structure to populate with the result. *     it must have text and textlen set. * * Return: * * %0		- Success * * %-EBADMSG	- Policy is invalid

Lines 1–500 of 559.