Something went wrong. Try again.
Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560// 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_obj(*p); 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_obj(*p); 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_obj(*r); 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;}