From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from us-smtp-delivery-124.mimecast.com (us-smtp-delivery-124.mimecast.com [170.10.133.124]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.lore.kernel.org (Postfix) with ESMTPS id A16B3C636D4 for ; Tue, 31 Jan 2023 13:51:45 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1675173104; h=from:from:sender:sender:reply-to:subject:subject:date:date: message-id:message-id:to:to:cc:cc:mime-version:mime-version: content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references:list-id:list-help: list-unsubscribe:list-subscribe:list-post; bh=gq380JoM9Mq2zGNy7v5G9mvI3Y9JxdU6sLHOEtErEtQ=; b=PixqKFTpDx9m9Ezu5hL9JWkNEDJY2xTIHgu7ePqAtGGagcIwAeODtyHCFbwsN6YirLNRoL az+0j0iDEYxRhNQolxk+hGeF+m4XdBnO/cUPeeprPiKFJ3fRYMDkHXWJKrxTNg1L/SGvJ3 vzTQ6g2wfLncI51hht0ErWsfsREg31E= Received: from mimecast-mx02.redhat.com (mx3-rdu2.redhat.com [66.187.233.73]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id us-mta-54-_y8YJw7HNhGIKna0HKI5Ow-1; Tue, 31 Jan 2023 08:51:41 -0500 X-MC-Unique: _y8YJw7HNhGIKna0HKI5Ow-1 Received: from smtp.corp.redhat.com (int-mx07.intmail.prod.int.rdu2.redhat.com [10.11.54.7]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx02.redhat.com (Postfix) with ESMTPS id 2DE741C08975; Tue, 31 Jan 2023 13:51:29 +0000 (UTC) Received: from mm-prod-listman-01.mail-001.prod.us-east-1.aws.redhat.com (unknown [10.30.29.100]) by smtp.corp.redhat.com (Postfix) with ESMTP id 1E25B1431C4F; Tue, 31 Jan 2023 13:51:29 +0000 (UTC) Received: from mm-prod-listman-01.mail-001.prod.us-east-1.aws.redhat.com (localhost [IPv6:::1]) by mm-prod-listman-01.mail-001.prod.us-east-1.aws.redhat.com (Postfix) with ESMTP id 1CBB21946A63; Tue, 31 Jan 2023 13:51:27 +0000 (UTC) Received: from smtp.corp.redhat.com (int-mx03.intmail.prod.int.rdu2.redhat.com [10.11.54.3]) by mm-prod-listman-01.mail-001.prod.us-east-1.aws.redhat.com (Postfix) with ESMTP id E2E3D1946589 for ; Mon, 30 Jan 2023 23:06:39 +0000 (UTC) Received: by smtp.corp.redhat.com (Postfix) id C423D1121318; Mon, 30 Jan 2023 23:06:39 +0000 (UTC) Received: from mimecast-mx02.redhat.com (mimecast06.extmail.prod.ext.rdu2.redhat.com [10.11.55.22]) by smtp.corp.redhat.com (Postfix) with ESMTPS id BCFC01121314 for ; Mon, 30 Jan 2023 23:06:39 +0000 (UTC) Received: from us-smtp-1.mimecast.com (us-smtp-delivery-1.mimecast.com [205.139.110.120]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mimecast-mx02.redhat.com (Postfix) with ESMTPS id 982DD1871D9C for ; Mon, 30 Jan 2023 23:06:39 +0000 (UTC) Received: from linux.microsoft.com (linux.microsoft.com [13.77.154.182]) by relay.mimecast.com with ESMTP id us-mta-296-j6iH2RyVPDuSHUPYEcG1ug-1; Mon, 30 Jan 2023 18:06:37 -0500 X-MC-Unique: j6iH2RyVPDuSHUPYEcG1ug-1 Received: by linux.microsoft.com (Postfix, from userid 1052) id 089C720EA1E2; Mon, 30 Jan 2023 14:58:41 -0800 (PST) DKIM-Filter: OpenDKIM Filter v2.11.0 linux.microsoft.com 089C720EA1E2 From: Fan Wu To: 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, paul@paul-moore.com Subject: [RFC PATCH v9 02/16] ipe: add policy parser Date: Mon, 30 Jan 2023 14:57:17 -0800 Message-Id: <1675119451-23180-3-git-send-email-wufan@linux.microsoft.com> In-Reply-To: <1675119451-23180-1-git-send-email-wufan@linux.microsoft.com> References: <1675119451-23180-1-git-send-email-wufan@linux.microsoft.com> X-Mimecast-Impersonation-Protect: Policy=CLT - Impersonation Protection Definition; Similar Internal Domain=false; Similar Monitored External Domain=false; Custom External Domain=false; Mimecast External Domain=false; Newly Observed Domain=false; Internal User Name=false; Custom Display Name List=false; Reply-to Address Mismatch=false; Targeted Threat Dictionary=false; Mimecast Threat Dictionary=false; Custom Threat Dictionary=false X-Scanned-By: MIMEDefang 3.1 on 10.11.54.3 X-Mailman-Approved-At: Tue, 31 Jan 2023 13:51:24 +0000 X-BeenThere: linux-audit@redhat.com X-Mailman-Version: 2.1.29 Precedence: list List-Id: Linux Audit Discussion List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: Fan Wu , dm-devel@redhat.com, linux-doc@vger.kernel.org, Deven Bowers , roberto.sassu@huawei.com, linux-security-module@vger.kernel.org, linux-kernel@vger.kernel.org, linux-block@vger.kernel.org, linux-fscrypt@vger.kernel.org, linux-audit@redhat.com, linux-integrity@vger.kernel.org MIME-Version: 1.0 Errors-To: linux-audit-bounces@redhat.com Sender: "Linux-audit" X-Scanned-By: MIMEDefang 3.1 on 10.11.54.7 X-Mimecast-Spam-Score: 0 X-Mimecast-Originator: redhat.com Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 7bit From: Deven Bowers 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 operaiton. + 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 --- security/ipe/Makefile | 2 + security/ipe/policy.c | 99 +++++++ security/ipe/policy.h | 77 ++++++ security/ipe/policy_parser.c | 515 +++++++++++++++++++++++++++++++++++ security/ipe/policy_parser.h | 11 + 5 files changed, 704 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/Makefile b/security/ipe/Makefile index 571648579991..16bbe80991f1 100644 --- a/security/ipe/Makefile +++ b/security/ipe/Makefile @@ -8,3 +8,5 @@ obj-$(CONFIG_SECURITY_IPE) += \ hooks.o \ ipe.o \ + policy.o \ + policy_parser.o \ diff --git a/security/ipe/policy.c b/security/ipe/policy.c new file mode 100644 index 000000000000..e446f4b84152 --- /dev/null +++ b/security/ipe/policy.c @@ -0,0 +1,99 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) Microsoft Corporation. All rights reserved. + */ + +#include "ipe.h" +#include "policy.h" +#include "policy_parser.h" +#include "digest.h" + +#include + +/** + * ipe_free_policy - Deallocate a given IPE policy. + * @p: Supplies the policy to free. + * + * Safe to call on IS_ERR/NULL. + */ +void ipe_free_policy(struct ipe_policy *p) +{ + if (IS_ERR_OR_NULL(p)) + return; + + free_parsed_policy(p->parsed); + if (!p->pkcs7) + kfree(p->text); + kfree(p->pkcs7); + kfree(p); +} + +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; +} + +/** + * ipe_new_policy - Allocate and parse an ipe_policy structure. + * + * @text: Supplies a pointer to the plain-text policy to parse. + * @textlen: Supplies the length of @text. + * @pkcs7: Supplies a pointer to a pkcs7-signed IPE policy. + * @pkcs7len: Supplies the length of @pkcs7. + * + * @text/@textlen Should be NULL/0 if @pkcs7/@pkcs7len is set. + * + * The result will still need to be associated with a context via + * ipe_add_policy. + * + * Return: + * * !IS_ERR - Success + * * -EBADMSG - Policy is invalid + * * -ENOMEM - Out of memory + */ +struct ipe_policy *ipe_new_policy(const char *text, size_t textlen, + const char *pkcs7, size_t pkcs7len) +{ + int rc = 0; + struct ipe_policy *new = NULL; + + new = kzalloc(sizeof(*new), GFP_KERNEL); + if (!new) + return ERR_PTR(-ENOMEM); + + if (!text) { + new->pkcs7len = pkcs7len; + new->pkcs7 = kmemdup(pkcs7, pkcs7len, GFP_KERNEL); + if (!new->pkcs7) { + rc = -ENOMEM; + goto err; + } + + rc = verify_pkcs7_signature(NULL, 0, new->pkcs7, pkcs7len, NULL, + VERIFYING_UNSPECIFIED_SIGNATURE, + set_pkcs7_data, new); + if (rc) + goto err; + } else { + new->textlen = textlen; + new->text = kstrdup(text, GFP_KERNEL); + if (!new->text) { + rc = -ENOMEM; + goto err; + } + } + + rc = parse_policy(new); + if (rc) + goto err; + + return new; +err: + return ERR_PTR(rc); +} diff --git a/security/ipe/policy.h b/security/ipe/policy.h new file mode 100644 index 000000000000..6af2d9a811ec --- /dev/null +++ b/security/ipe/policy.h @@ -0,0 +1,77 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) Microsoft Corporation. All rights reserved. + */ +#ifndef IPE_POLICY_H +#define IPE_POLICY_H + +#include +#include + +enum ipe_op_type { + ipe_op_exec = 0, + ipe_op_firmware, + ipe_op_kernel_module, + ipe_op_kexec_image, + ipe_op_kexec_initramfs, + ipe_op_ima_policy, + ipe_op_ima_x509, + ipe_op_max +}; + +enum ipe_action_type { + ipe_action_allow = 0, + ipe_action_deny, + ipe_action_max +}; + +enum ipe_prop_type { + ipe_prop_max +}; + +struct ipe_prop { + struct list_head next; + enum ipe_prop_type type; + void *value; +}; + +struct ipe_rule { + enum ipe_op_type op; + enum ipe_action_type action; + struct list_head props; + struct list_head next; +}; + +struct ipe_op_table { + struct list_head rules; + enum ipe_action_type default_action; +}; + +struct ipe_parsed_policy { + const char *name; + struct { + u16 major; + u16 minor; + u16 rev; + } version; + + enum ipe_action_type global_default_action; + + struct ipe_op_table rules[ipe_op_max]; +}; + +struct ipe_policy { + const char *pkcs7; + size_t pkcs7len; + + const char *text; + size_t textlen; + + struct ipe_parsed_policy *parsed; +}; + +struct ipe_policy *ipe_new_policy(const char *text, size_t textlen, + const char *pkcs7, size_t pkcs7len); +void ipe_free_policy(struct ipe_policy *pol); + +#endif /* IPE_POLICY_H */ diff --git a/security/ipe/policy_parser.c b/security/ipe/policy_parser.c new file mode 100644 index 000000000000..c7ba0e865366 --- /dev/null +++ b/security/ipe/policy_parser.c @@ -0,0 +1,515 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) Microsoft Corporation. All rights reserved. + */ + +#include "policy.h" +#include "policy_parser.h" +#include "digest.h" + +#include + +#define START_COMMENT '#' + +/** + * new_parsed_policy - Allocate and initialize a parsed policy. + * + * Return: + * * !IS_ERR - OK + * * -ENOMEM - Out of memory + */ +static struct ipe_parsed_policy *new_parsed_policy(void) +{ + size_t i = 0; + struct ipe_parsed_policy *p = NULL; + struct ipe_op_table *t = NULL; + + p = kzalloc(sizeof(*p), GFP_KERNEL); + if (!p) + return ERR_PTR(-ENOMEM); + + p->global_default_action = ipe_action_max; + + for (i = 0; i < ARRAY_SIZE(p->rules); ++i) { + t = &p->rules[i]; + + t->default_action = ipe_action_max; + INIT_LIST_HEAD(&t->rules); + } + + return p; +} + +/** + * remove_comment - Truncate all chars following START_COMMENT in a string. + * + * @line: Supplies a poilcy line string for preprocessing. + */ +static void remove_comment(char *line) +{ + size_t i, len = 0; + + len = strlen(line); + for (i = 0; i < len && line[i] != START_COMMENT; ++i) + ; + + line[i] = '\0'; +} + +/** + * remove_trailing_spaces - Truncate all trailing spaces in a string. + * + * @line: Supplies a poilcy line string for preprocessing. + */ +static void remove_trailing_spaces(char *line) +{ + size_t i, len = 0; + + len = strlen(line); + for (i = len; i > 0 && (line[i - 1] == ' ' || line[i - 1] == '\t'); --i) + ; + + line[i] = '\0'; +} + +/** + * 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)) { + rc = -EBADMSG; + goto err; + } + + rc = kstrtou16(token, 10, cv[sep_count]); + if (rc) + goto err; + + ++sep_count; + } + + /* prevent underflow */ + if (sep_count != ARRAY_SIZE(cv)) + rc = -EBADMSG; + +err: + 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, " \t")) != NULL) { + int token; + + if (*t == '\0') + continue; + if (idx >= ipe_header_max) { + rc = -EBADMSG; + goto err; + } + + token = match_token(t, header_tokens, args); + if (token != idx) { + rc = -EBADMSG; + goto err; + } + + 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 err; + ++idx; + } + + if (idx != ipe_header_max) { + rc = -EBADMSG; + goto err; + } + goto out; + +err: + kfree(p->name); + p->name = NULL; +out: + kfree(ver); + return rc; +} + +/** + * is_default - Determine if the given token is "DEFAULT". + * @token: Supplies the token string to be compared. + * + * Return: + * * 0 - The token is not "DEFAULT" + * * !0 - The token is "DEFAULT" + */ +static bool is_default(char *token) +{ + return !strcmp(token, "DEFAULT"); +} + +/** + * free_rule - Free the supplied ipe_rule struct. + * @r: Supplies the ipe_rule struct to be freed. + */ +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) { + 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_ima_policy, "op=IMA_POLICY"}, + {ipe_op_ima_x509, "op=IMA_X509_CERT"}, + {ipe_op_max, NULL} +}; + +/** + * parse_operation - Parse the opeartion type given a token string. + * @t: Supplies the token string to be parsed. + * + * Return: The parsed opeartion 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_max, 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_max, NULL} +}; + +/** + * parse_property - Parse the property type given a token string. + * @t: Supplies the token string to be parsed. + * @r: Supplies the ipe_rule the parsed property will be associated with. + * + * Return: + * * !IS_ERR - OK + * * -ENOMEM - Out of memory + * * -EBADMSG - The supplied token cannot be parsed + */ +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) { + rc = -ENOMEM; + goto err; + } + + token = match_token(t, property_tokens, args); + + switch (token) { + case ipe_prop_max: + default: + rc = -EBADMSG; + break; + } + list_add_tail(&p->next, &r->props); + +err: + kfree(dup); + 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_max; + enum ipe_action_type action = ipe_action_max; + struct ipe_rule *r = NULL; + char *t; + + r = kzalloc(sizeof(*r), GFP_KERNEL); + if (!r) { + rc = -ENOMEM; + goto err; + } + + INIT_LIST_HEAD(&r->next); + INIT_LIST_HEAD(&r->props); + + while (t = strsep(&line, " \t"), line) { + if (*t == '\0') + continue; + if (first_token && is_default(t)) { + is_default_rule = true; + } else { + if (!op_parsed) { + op = parse_operation(t); + if (op == ipe_op_max) + 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_max) { + rc = -EBADMSG; + goto err; + } + + if (is_default_rule) { + if (op == ipe_op_max) { + if (p->global_default_action != ipe_action_max) + rc = -EBADMSG; + else + p->global_default_action = action; + } else { + if (p->rules[op].default_action != ipe_action_max) + rc = -EBADMSG; + else + p->rules[op].default_action = action; + } + free_rule(r); + } else if (op != ipe_op_max && action != ipe_action_max) { + r->op = op; + r->action = action; + list_add_tail(&r->next, &p->rules[op].rules); + } else { + rc = -EBADMSG; + } + + if (rc) + goto err; + + goto out; + +err: + free_rule(r); +out: + 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) + free_rule(pp); + + 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, and all lines + * parsed are have been consumed. + * + * A parsed policy can be an invalid state for use (a default was + * undefined, a header was undefined) by just parsing the policy. + * + * Return: + * * 0 - OK + * * -EBADMSG - Policy is invalid + */ +static int validate_policy(const struct ipe_parsed_policy *p) +{ + int i = 0; + + if (p->global_default_action != ipe_action_max) + return 0; + + for (i = 0; i < ARRAY_SIZE(p->rules); ++i) { + if (p->rules[i].default_action == ipe_action_max) + return -EBADMSG; + } + + return 0; +} + +/** + * 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 - OK + * * -EBADMSG - Policy is invalid + * * -ENOMEM - Out of Memory + */ +int parse_policy(struct ipe_policy *p) +{ + int rc = 0; + size_t len; + char *policy = NULL, *dup = NULL; + char *line = NULL; + bool header_parsed = false; + struct ipe_parsed_policy *pp = NULL; + + 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, "\n\r")) != NULL) { + remove_comment(line); + remove_trailing_spaces(line); + len = strlen(line); + if (!len) + continue; + + if (!header_parsed) { + rc = parse_header(line, pp); + if (rc) + goto err; + header_parsed = true; + continue; + } + + rc = parse_rule(line, pp); + if (rc) + goto err; + } + + if (!header_parsed || validate_policy(pp)) { + rc = -EBADMSG; + goto err; + } + + p->parsed = pp; + + goto out; +err: + free_parsed_policy(pp); +out: + kfree(dup); + + return rc; +} diff --git a/security/ipe/policy_parser.h b/security/ipe/policy_parser.h new file mode 100644 index 000000000000..699ca58a5a32 --- /dev/null +++ b/security/ipe/policy_parser.h @@ -0,0 +1,11 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (C) Microsoft Corporation. All rights reserved. + */ +#ifndef IPE_POLICY_PARSER_H +#define IPE_POLICY_PARSER_H + +int parse_policy(struct ipe_policy *p); +void free_parsed_policy(struct ipe_parsed_policy *p); + +#endif /* IPE_POLICY_PARSER */ -- 2.39.0 -- Linux-audit mailing list Linux-audit@redhat.com https://listman.redhat.com/mailman/listinfo/linux-audit