| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559 |
- // 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
- * * %-ENOMEM - Out of Memory
- * * %-ERANGE - Policy version number overflow
- * * %-EINVAL - Policy version parsing error
- */
- int ipe_parse_policy(struct ipe_policy *p)
- {
- struct ipe_parsed_policy *pp = NULL;
- char *policy = NULL, *dup = NULL;
- bool header_parsed = false;
- char *line = NULL;
- size_t len;
- int rc = 0;
- if (!p->textlen)
- return -EBADMSG;
- policy = kmemdup_nul(p->text, p->textlen, GFP_KERNEL);
- if (!policy)
- return -ENOMEM;
- dup = policy;
- pp = new_parsed_policy();
- if (IS_ERR(pp)) {
- rc = PTR_ERR(pp);
- goto out;
- }
- while ((line = strsep(&policy, IPE_LINE_DELIM)) != NULL) {
- remove_comment(line);
- len = remove_trailing_spaces(line);
- if (!len)
- continue;
- if (!header_parsed) {
- rc = parse_header(line, pp);
- if (rc)
- goto err;
- header_parsed = true;
- } else {
- rc = parse_rule(line, pp);
- if (rc)
- goto err;
- }
- }
- if (!header_parsed || validate_policy(pp)) {
- rc = -EBADMSG;
- goto err;
- }
- p->parsed = pp;
- out:
- kfree(dup);
- return rc;
- err:
- ipe_free_parsed_policy(pp);
- goto out;
- }
|