Received: by 2002:a05:7412:251c:b0:e2:908c:2ebd with SMTP id w28csp1637679rda; Mon, 23 Oct 2023 20:53:07 -0700 (PDT) X-Google-Smtp-Source: AGHT+IHTRsQmBhTYnOSw7VDxWF6fjR1KtzQIph3EBER+tL6p8T3YAOFQiF8fHxAhJcjYZZHMc1Nt X-Received: by 2002:a05:6a21:66c5:b0:17b:4c98:840d with SMTP id ze5-20020a056a2166c500b0017b4c98840dmr1616474pzb.51.1698119587119; Mon, 23 Oct 2023 20:53:07 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1698119587; cv=none; d=google.com; s=arc-20160816; b=Euk0XSCL5e2Q1GpnmmuSu1OU8F+O9bAc8v7BfSa9f/MWViHeQd90yDqw+QGjI4AMSC pVmzTx08lv/G/AY0x1TSPd1d3KR+oBol9l2ZgrcvXl4+tv7xrkEX948JSKQEecMs2h7d KkcWysL6tooDQN8e3PnnO8zu29mNIQnPBVao9+bMWdjLE7tIsVdqM0N9ebjwweIotkMj OUsMfqjpgBiOpBHYlPq0eBbUNF6CvWweynPSl9GjQhvX4UP2+d+duTMTqhi1d3U3DibB ri3doWRFoI4Pw065OWKRjGRI6t/mGY/WrNcl94PGYpZc5j66dOLRA/TsVuwEc8+icURN FcbA== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:in-reply-to:references:subject:cc:to:from :message-id:date:dkim-signature; bh=PYQStjjr2Q8HKTHbB20ou4WiXIqkha1Xi9gNGY1VyVs=; fh=fHHNkGg+NzKoJxHGMh0JSMLpg18Ht1OPApOBL8ui3GA=; b=wFAgth1t0AWQHBaAqVyfA9NVlYankgAo2gJ7vjw+9egXx7KW+3pNlE/RpTDBmLZn8a StTNCwBXHmuIoXPcWi4eUnBowyDFKhrlDvHLl/9VVmk2iflVaBkTJ4dtHtBvW/OiUvwp e9K05UYuSCUvtvcim46j6xyET2aPnue/rfA39rGsCIIVBGcEuxnXWDyyl3+emC07uO9x EVvE0YjlkSJnznfcoIq5km2y8G/qopS0rh0wsym9CFjuGkSJqXG3jwmUHgN6wFA30VVj eHKyx4YjcFiykMzLxgpF5itjXTEZpD7KNn+9IJxhDfidLbnWMQN/eYCfUS1MgHszKcvt Xqzg== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@paul-moore.com header.s=google header.b=WAHBHXtj; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::3:2 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=paul-moore.com Return-Path: Received: from agentk.vger.email (agentk.vger.email. [2620:137:e000::3:2]) by mx.google.com with ESMTPS id p3-20020a170902a40300b001c5c344a425si7490903plq.418.2023.10.23.20.53.06 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 23 Oct 2023 20:53:07 -0700 (PDT) Received-SPF: pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::3:2 as permitted sender) client-ip=2620:137:e000::3:2; Authentication-Results: mx.google.com; dkim=pass header.i=@paul-moore.com header.s=google header.b=WAHBHXtj; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::3:2 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=paul-moore.com Received: from out1.vger.email (depot.vger.email [IPv6:2620:137:e000::3:0]) by agentk.vger.email (Postfix) with ESMTP id 790C4809D321; Mon, 23 Oct 2023 20:53:00 -0700 (PDT) X-Virus-Status: Clean X-Virus-Scanned: clamav-milter 0.103.10 at agentk.vger.email Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S232234AbjJXDwh (ORCPT + 99 others); Mon, 23 Oct 2023 23:52:37 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:51618 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S231982AbjJXDwa (ORCPT ); Mon, 23 Oct 2023 23:52:30 -0400 Received: from mail-qk1-x736.google.com (mail-qk1-x736.google.com [IPv6:2607:f8b0:4864:20::736]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 13D8C10C for ; Mon, 23 Oct 2023 20:52:24 -0700 (PDT) Received: by mail-qk1-x736.google.com with SMTP id af79cd13be357-7781bc3783fso282559585a.1 for ; Mon, 23 Oct 2023 20:52:23 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=paul-moore.com; s=google; t=1698119543; x=1698724343; darn=vger.kernel.org; h=in-reply-to:references:subject:cc:to:from:message-id:date:from:to :cc:subject:date:message-id:reply-to; bh=PYQStjjr2Q8HKTHbB20ou4WiXIqkha1Xi9gNGY1VyVs=; b=WAHBHXtjHEsNLoriS8vOtzrxgPI6/NMjDrdW5rIviWtRjDjfsUKxfuTOekd8pKSCyk D1j8V4OyAzgJh5iLtBrkUgqGbWOPoj3VTOZC9JcpciWz4cfj+rKZxhmRF+aEX4hl+T// nYVBex5k/D3yPgZa64W8kV4L66lKiEm323MlPTmkYLspb6+hojACf8PyxQeG/Yi1/2Fd +OW/2kBtN4yHRtVJALJYvZoAKP6yTODb8TTIJABeqk/QHJmguWtEAWhvnIO3uRtSFoP6 Z31sTINDDOu0o0B9lHst/GL//IKm5o/Qqk6Ny3uJV1kKyC3Zjd7UiZcqkB+qzb9Ja7sv JeXg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1698119543; x=1698724343; h=in-reply-to:references:subject:cc:to:from:message-id:date :x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=PYQStjjr2Q8HKTHbB20ou4WiXIqkha1Xi9gNGY1VyVs=; b=P6CDxwjZbethXxao5SzZTfvpgwc8KXgyaV/yZx/KYjjICv0OWiE/p1MOsVuVX/vOPo ShNoiIhOBSddPAHXKbs5nyg1UkFZw3lGyau+gSYFP1j5qG5UovsnK3MPdRxJO9h+FZTZ QXxLPO+KAviDj61SU4xQu0rnR26UkZaj4a8mXFuSpBnRp7QF1XtexBsSknkvrvWTDHpo ulyQHaSLDWNZ7gq7c51YdLcUbwE7y/fNYW2OxQukmbpjWo2iy2CDxQTp8z5APeU7Zi+v Q9grahvWSx4VjA82mloE71Ldh2m3s4D9e29G0c6LGl57yeKv1xAozvfXytJrOX2mkHLn 34zw== X-Gm-Message-State: AOJu0Ywwawq+Bn9838ZeJNslhMQt7bUujSCKxJDVfks6oU125MppPvZA vsSDvjiKZfFKFmu2OyFU6Xv+ X-Received: by 2002:a05:620a:1a25:b0:772:6419:89ab with SMTP id bk37-20020a05620a1a2500b00772641989abmr13610179qkb.55.1698119542757; Mon, 23 Oct 2023 20:52:22 -0700 (PDT) Received: from localhost ([70.22.175.108]) by smtp.gmail.com with ESMTPSA id bi12-20020a05620a318c00b00772662b7804sm3164703qkb.100.2023.10.23.20.52.21 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 23 Oct 2023 20:52:22 -0700 (PDT) Date: Mon, 23 Oct 2023 23:52:21 -0400 Message-ID: <7c8c2a158c628a642078f746e5c42f2f.paul@paul-moore.com> From: Paul Moore To: Fan Wu , corbet@lwn.net, zohar@linux.ibm.com, jmorris@namei.org, serge@hallyn.com, tytso@mit.edu, ebiggers@kernel.org, axboe@kernel.dk, agk@redhat.com, snitzer@kernel.org, eparis@redhat.com Cc: linux-doc@vger.kernel.org, linux-integrity@vger.kernel.org, linux-security-module@vger.kernel.org, linux-fscrypt@vger.kernel.org, linux-block@vger.kernel.org, dm-devel@redhat.com, audit@vger.kernel.org, roberto.sassu@huawei.com, linux-kernel@vger.kernel.org, Deven Bowers , Fan Wu Subject: Re: [PATCH RFC v11 2/19] ipe: add policy parser References: <1696457386-3010-3-git-send-email-wufan@linux.microsoft.com> In-Reply-To: <1696457386-3010-3-git-send-email-wufan@linux.microsoft.com> X-Spam-Status: No, score=-0.8 required=5.0 tests=DKIM_SIGNED,DKIM_VALID, DKIM_VALID_AU,HEADER_FROM_DIFFERENT_DOMAINS,MAILING_LIST_MULTI, SPF_HELO_NONE,SPF_PASS autolearn=unavailable autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on agentk.vger.email Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org X-Greylist: Sender passed SPF test, not delayed by milter-greylist-4.6.4 (agentk.vger.email [0.0.0.0]); Mon, 23 Oct 2023 20:53:00 -0700 (PDT) On Oct 4, 2023 Fan Wu wrote: > > IPE's interpretation of the what the user trusts is accomplished through > its policy. IPE's design is to not provide support for a single trust > provider, but to support multiple providers to enable the end-user to > choose the best one to seek their needs. > > This requires the policy to be rather flexible and modular so that > integrity providers, like fs-verity, dm-verity, dm-integrity, or > some other system, can plug into the policy with minimal code changes. > > Signed-off-by: Deven Bowers > Signed-off-by: Fan Wu > --- > v2: > + Split evaluation loop, access control hooks, > and evaluation loop from policy parser and userspace > interface to pass mailing list character limit > > v3: > + Move policy load and activation audit event to 03/12 > + Fix a potential panic when a policy failed to load. > + use pr_warn for a failure to parse instead of an > audit record > + Remove comments from headers > + Add lockdep assertions to ipe_update_active_policy and > ipe_activate_policy > + Fix up warnings with checkpatch --strict > + Use file_ns_capable for CAP_MAC_ADMIN for securityfs > nodes. > + Use memdup_user instead of kzalloc+simple_write_to_buffer. > + Remove strict_parse command line parameter, as it is added > by the sysctl command line. > + Prefix extern variables with ipe_ > > v4: > + Remove securityfs to reverse-dependency > + Add SHA1 reverse dependency. > + Add versioning scheme for IPE properties, and associated > interface to query the versioning scheme. > + Cause a parser to always return an error on unknown syntax. > + Remove strict_parse option > + Change active_policy interface from sysctl, to securityfs, > and change scheme. > > v5: > + Cause an error if a default action is not defined for each > operation. > + Minor function renames > > v6: > + No changes > > v7: > + Further split parser and userspace interface into two > separate commits, for easier review. > + Refactor policy parser to make code cleaner via introducing a > more modular design, for easier extension of policy, and > easier review. > > v8: > + remove unnecessary pr_info emission on parser loading > + add explicit newline to the pr_err emitted when a parser > fails to load. > > v9: > + switch to match table to parse policy > + remove quote syntax and KERNEL_READ operation > > v10: > + Fix memory leaks in parser > + Fix typos and change code styles > > v11: > + Fix code style issues > --- > security/ipe/Makefile | 2 + > security/ipe/policy.c | 101 ++++++++ > security/ipe/policy.h | 83 ++++++ > security/ipe/policy_parser.c | 487 +++++++++++++++++++++++++++++++++++ > security/ipe/policy_parser.h | 11 + > 5 files changed, 684 insertions(+) > create mode 100644 security/ipe/policy.c > create mode 100644 security/ipe/policy.h > create mode 100644 security/ipe/policy_parser.c > create mode 100644 security/ipe/policy_parser.h ... > diff --git a/security/ipe/policy.c b/security/ipe/policy.c > new file mode 100644 > index 000000000000..3a529c7950a1 > --- /dev/null > +++ b/security/ipe/policy.c > @@ -0,0 +1,101 @@ > +// SPDX-License-Identifier: GPL-2.0 > +/* > + * Copyright (C) Microsoft Corporation. All rights reserved. > + */ ... > +static int set_pkcs7_data(void *ctx, const void *data, size_t len, > + size_t asn1hdrlen) > +{ > + struct ipe_policy *p = ctx; > + > + p->text = (const char *)data; > + p->textlen = len; > + > + return 0; > +} The @asn1hdrlen parameter isn't used in this function, at least at this point in the patchset, so you really should remove it. If it is needed later in the patchset you can always update the function. > diff --git a/security/ipe/policy_parser.c b/security/ipe/policy_parser.c > new file mode 100644 > index 000000000000..c09458bd348d > --- /dev/null > +++ b/security/ipe/policy_parser.c > @@ -0,0 +1,487 @@ > +// SPDX-License-Identifier: GPL-2.0 > +/* > + * Copyright (C) Microsoft Corporation. All rights reserved. > + */ ... > +/** > + * remove_trailing_spaces - Truncate all trailing spaces in a string. > + * > + * @line: Supplies a poilcy line string for preprocessing. > + * > + * Return: The length of truncated string. > + */ > +static size_t remove_trailing_spaces(char *line) > +{ > + size_t i = 0; > + > + for (i = strlen(line); i > 0 && (line[i - 1] == ' ' || line[i - 1] == '\t'); --i) > + ; Maybe I've asked this before, I can't remember: could you use the isspace() macro here instead of explicitly checking for ' ' and '\t'? i = strlen(line); while (i > 0 && isspace(line[i - 1])) i--; line[i] = '\0'; > + 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 - OK > + * * !0 - Standard errno > + */ > +static int parse_version(char *ver, struct ipe_parsed_policy *p) > +{ > + int rc = 0; > + size_t sep_count = 0; > + char *token; > + u16 *const cv[] = { &p->version.major, &p->version.minor, &p->version.rev }; > + > + 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)) > + rc = -EBADMSG; You could always just 'return -EBADMSG' here and 'return 0' below to simplify things a little. > + return rc; > +} > + > +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 - OK > + * * !0 - Standard errno > + */ > +static int parse_header(char *line, struct ipe_parsed_policy *p) > +{ > + int rc = 0; > + char *t, *ver = NULL; > + substring_t args[MAX_OPT_ARGS]; > + size_t idx = 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; > + goto out; You probably don't need to 'goto out' here. > + } > + > +out: > + kfree(ver); > + return rc; > +} ... > +/** > + * parse_rule - parse a policy rule line. > + * @line: Supplies rule line to be parsed. > + * @p: Supplies the partial parsed policy. > + * > + * Return: > + * * !IS_ERR - OK > + * * -ENOMEM - Out of memory > + * * -EBADMSG - Policy syntax error > + */ > +static int parse_rule(char *line, struct ipe_parsed_policy *p) > +{ > + int rc = 0; > + bool first_token = true, is_default_rule = false; > + bool op_parsed = false; > + enum ipe_op_type op = IPE_OP_INVALID; > + enum ipe_action_type action = IPE_ACTION_INVALID; > + struct ipe_rule *r = NULL; > + char *t; > + > + 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; > + } I might be missing something important in the policy syntac, but could this function, and perhaps the ipe_parsed_policy struct, be simplified if the default action was an explicit rule? "op=DEFAULT action=ALLOW" > + 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; > +} > + > +/** > + * free_parsed_policy - free a parsed policy structure. > + * @p: Supplies the parsed policy. > + */ > +void free_parsed_policy(struct ipe_parsed_policy *p) > +{ > + size_t i = 0; > + struct ipe_rule *pp, *t; > + > + 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 > + * necessary fields are present, initialized correctly. > + * > + * A parsed policy can be in an invalid state for use (a default was > + * undefined) by just parsing the policy. > + * > + * Return: > + * * 0 - OK > + * * -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; Should the if conditional above be "==" and not "!="? > + for (i = 0; i < ARRAY_SIZE(p->rules); ++i) { > + if (p->rules[i].default_action == IPE_ACTION_INVALID) > + return -EBADMSG; > + } > + > + return 0; > +} -- paul-moore.com