From nobody Tue Feb 10 17:03:00 2026 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 vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 6C8D0C001B0 for ; Sat, 12 Aug 2023 10:48:59 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S237065AbjHLKsw (ORCPT ); Sat, 12 Aug 2023 06:48:52 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:49366 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S234144AbjHLKss (ORCPT ); Sat, 12 Aug 2023 06:48:48 -0400 Received: from frasgout11.his.huawei.com (unknown [14.137.139.23]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 1844A2D5B; Sat, 12 Aug 2023 03:48:32 -0700 (PDT) Received: from mail02.huawei.com (unknown [172.18.147.229]) by frasgout11.his.huawei.com (SkyGuard) with ESMTP id 4RNHCP2p8vz9yyf1; Sat, 12 Aug 2023 18:36:49 +0800 (CST) Received: from huaweicloud.com (unknown [10.204.63.22]) by APP1 (Coremail) with SMTP id LxC2BwBXC7scY9dkThi9AA--.8440S8; Sat, 12 Aug 2023 11:48:04 +0100 (CET) From: Roberto Sassu To: corbet@lwn.net, zohar@linux.ibm.com, dmitry.kasatkin@gmail.com, paul@paul-moore.com, jmorris@namei.org, serge@hallyn.com Cc: linux-kernel@vger.kernel.org, linux-doc@vger.kernel.org, linux-integrity@vger.kernel.org, linux-security-module@vger.kernel.org, bpf@vger.kernel.org, jarkko@kernel.org, pbrobinson@gmail.com, zbyszek@in.waw.pl, hch@lst.de, mjg59@srcf.ucam.org, pmatilai@redhat.com, jannh@google.com, Roberto Sassu Subject: [RFC][PATCH v2 06/13] integrity/digest_cache: Parse rpm digest lists Date: Sat, 12 Aug 2023 12:46:09 +0200 Message-Id: <20230812104616.2190095-7-roberto.sassu@huaweicloud.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20230812104616.2190095-1-roberto.sassu@huaweicloud.com> References: <20230812104616.2190095-1-roberto.sassu@huaweicloud.com> MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-CM-TRANSID: LxC2BwBXC7scY9dkThi9AA--.8440S8 X-Coremail-Antispam: 1UD129KBjvJXoW3Gry7tFW5KryrWw1rXry8uFg_yoW3trWkpa 4DKFy8trWkXF1Skws7AF12kr1Sq3yqgFnFqrZ8uFn0yFZIvryjva18AryxZryrJr4DZFy7 Gr4YqF129F4DtaDanT9S1TB71UUUUUUqnTZGkaVYY2UrUUUUjbIjqfuFe4nvWSU5nxnvy2 9KBjDU0xBIdaVrnRJUUUBvb4IE77IF4wAFF20E14v26rWj6s0DM7CY07I20VC2zVCF04k2 6cxKx2IYs7xG6rWj6s0DM7CIcVAFz4kK6r1j6r18M28IrcIa0xkI8VA2jI8067AKxVWUAV Cq3wA2048vs2IY020Ec7CjxVAFwI0_Xr0E3s1l8cAvFVAK0II2c7xJM28CjxkF64kEwVA0 rcxSw2x7M28EF7xvwVC0I7IYx2IY67AKxVWUCVW8JwA2z4x0Y4vE2Ix0cI8IcVCY1x0267 AKxVW8Jr0_Cr1UM28EF7xvwVC2z280aVAFwI0_Gr0_Cr1l84ACjcxK6I8E87Iv6xkF7I0E 14v26r4UJVWxJr1le2I262IYc4CY6c8Ij28IcVAaY2xG8wAqx4xG64xvF2IEw4CE5I8CrV C2j2WlYx0E2Ix0cI8IcVAFwI0_Jr0_Jr4lYx0Ex4A2jsIE14v26r1j6r4UMcvjeVCFs4IE 7xkEbVWUJVW8JwACjcxG0xvY0x0EwIxGrwACI402YVCY1x02628vn2kIc2xKxwCF04k20x vY0x0EwIxGrwCFx2IqxVCFs4IE7xkEbVWUJVW8JwC20s026c02F40E14v26r1j6r18MI8I 3I0E7480Y4vE14v26r106r1rMI8E67AF67kF1VAFwI0_GFv_WrylIxkGc2Ij64vIr41lIx AIcVC0I7IYx2IY67AKxVWUCVW8JwCI42IY6xIIjxv20xvEc7CjxVAFwI0_Gr1j6F4UJwCI 42IY6xAIw20EY4v20xvaj40_Jr0_JF4lIxAIcVC2z280aVAFwI0_Gr0_Cr1lIxAIcVC2z2 80aVCY1x0267AKxVW8Jr0_Cr1UYxBIdaVFxhVjvjDU0xZFpf9x07UZo7tUUUUU= X-CM-SenderInfo: purev21wro2thvvxqx5xdzvxpfor3voofrz/1tbiAQAHBF1jj5KVZQADsS X-CFilter-Loop: Reflected Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Content-Type: text/plain; charset="utf-8" From: Roberto Sassu Implement a simple parser of RPM headers, that extracts the digest and the algorithm of the packaged files from the RPMTAG_FILEDIGESTS and RPMTAG_FILEDIGESTALGO section, and add them to the digest cache. Signed-off-by: Roberto Sassu --- security/integrity/Makefile | 3 +- security/integrity/digest_cache.c | 2 + .../integrity/digest_list_parsers/parsers.h | 2 + security/integrity/digest_list_parsers/rpm.c | 215 ++++++++++++++++++ 4 files changed, 221 insertions(+), 1 deletion(-) create mode 100644 security/integrity/digest_list_parsers/rpm.c diff --git a/security/integrity/Makefile b/security/integrity/Makefile index 3765b004e66..c4c17a57d84 100644 --- a/security/integrity/Makefile +++ b/security/integrity/Makefile @@ -13,7 +13,8 @@ integrity-$(CONFIG_INTEGRITY_PLATFORM_KEYRING) +=3D platf= orm_certs/platform_keyrin integrity-$(CONFIG_INTEGRITY_MACHINE_KEYRING) +=3D platform_certs/machine_= keyring.o integrity-$(CONFIG_INTEGRITY_DIGEST_CACHE) +=3D digest_cache.o \ digest_cache_iter.o \ - digest_list_parsers/tlv.o + digest_list_parsers/tlv.o \ + digest_list_parsers/rpm.o integrity-$(CONFIG_LOAD_UEFI_KEYS) +=3D platform_certs/efi_parser.o \ platform_certs/load_uefi.o \ platform_certs/keyring_handler.o diff --git a/security/integrity/digest_cache.c b/security/integrity/digest_= cache.c index 818ac0ac0bf..fc392b925a5 100644 --- a/security/integrity/digest_cache.c +++ b/security/integrity/digest_cache.c @@ -144,6 +144,8 @@ static int digest_cache_parse_digest_list(struct digest= _cache *digest_cache, =20 if (!strncmp(digest_list_path->dentry->d_name.name, "tlv-", 4)) ret =3D digest_list_parse_tlv(digest_cache, data, data_len); + else if (!strncmp(digest_list_path->dentry->d_name.name, "rpm-", 4)) + ret =3D digest_list_parse_rpm(digest_cache, data, data_len); =20 return ret; } diff --git a/security/integrity/digest_list_parsers/parsers.h b/security/in= tegrity/digest_list_parsers/parsers.h index e8fff2374d8..f86e58e9806 100644 --- a/security/integrity/digest_list_parsers/parsers.h +++ b/security/integrity/digest_list_parsers/parsers.h @@ -11,3 +11,5 @@ =20 int digest_list_parse_tlv(struct digest_cache *digest_cache, const u8 *dat= a, size_t data_len); +int digest_list_parse_rpm(struct digest_cache *digest_cache, const u8 *dat= a, + size_t data_len); diff --git a/security/integrity/digest_list_parsers/rpm.c b/security/integr= ity/digest_list_parsers/rpm.c new file mode 100644 index 00000000000..df2029d042f --- /dev/null +++ b/security/integrity/digest_list_parsers/rpm.c @@ -0,0 +1,215 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2017-2023 Huawei Technologies Duesseldorf GmbH + * + * Author: Roberto Sassu + * + * Parse an rpm digest list (RPM package header). + */ + +#define pr_fmt(fmt) "RPM DIGEST LIST: "fmt +#include + +#include "parsers.h" + +#define RPMTAG_FILEDIGESTS 1035 +#define RPMTAG_FILEDIGESTALGO 5011 + +#define RPM_INT32_TYPE 4 +#define RPM_STRING_ARRAY_TYPE 8 + +struct rpm_hdr { + u32 magic; + u32 reserved; + u32 tags; + u32 datasize; +} __packed; + +struct rpm_entryinfo { + s32 tag; + u32 type; + s32 offset; + u32 count; +} __packed; + +enum pgp_algos { + DIGEST_ALGO_MD5 =3D 1, + DIGEST_ALGO_SHA1 =3D 2, + DIGEST_ALGO_RMD160 =3D 3, + /* 4, 5, 6, and 7 are reserved. */ + DIGEST_ALGO_SHA256 =3D 8, + DIGEST_ALGO_SHA384 =3D 9, + DIGEST_ALGO_SHA512 =3D 10, + DIGEST_ALGO_SHA224 =3D 11, +}; + +static const enum hash_algo pgp_algo_mapping[DIGEST_ALGO_SHA224 + 1] =3D { + [DIGEST_ALGO_MD5] =3D HASH_ALGO_MD5, + [DIGEST_ALGO_SHA1] =3D HASH_ALGO_SHA1, + [DIGEST_ALGO_RMD160] =3D HASH_ALGO_RIPE_MD_160, + [4] =3D HASH_ALGO__LAST, + [5] =3D HASH_ALGO__LAST, + [6] =3D HASH_ALGO__LAST, + [7] =3D HASH_ALGO__LAST, + [DIGEST_ALGO_SHA256] =3D HASH_ALGO_SHA256, + [DIGEST_ALGO_SHA384] =3D HASH_ALGO_SHA384, + [DIGEST_ALGO_SHA512] =3D HASH_ALGO_SHA512, + [DIGEST_ALGO_SHA224] =3D HASH_ALGO_SHA224, +}; + +int digest_list_parse_rpm(struct digest_cache *digest_cache, const u8 *dat= a, + size_t data_len) +{ + const unsigned char rpm_header_magic[8] =3D { + 0x8e, 0xad, 0xe8, 0x01, 0x00, 0x00, 0x00, 0x00 + }; + const struct rpm_hdr *hdr; + const struct rpm_entryinfo *entry; + uint32_t tags, max_tags, datasize; + uint32_t digests_count, max_digests_count; + uint32_t digests_offset, algo_offset; + uint32_t digest_len, pkg_pgp_algo, i; + bool algo_offset_set =3D false, digests_offset_set =3D false; + enum hash_algo pkg_kernel_algo =3D HASH_ALGO_MD5; + u8 rpm_digest[SHA512_DIGEST_SIZE]; + int ret; + + if (data_len < sizeof(*hdr)) { + pr_debug("Not enough data for RPM header, current %ld, expected: %ld\n", + data_len, sizeof(*hdr)); + return -EINVAL; + } + + if (memcmp(data, rpm_header_magic, sizeof(rpm_header_magic))) { + pr_debug("RPM header magic mismatch\n"); + return -EINVAL; + } + + hdr =3D (const struct rpm_hdr *)data; + data +=3D sizeof(*hdr); + data_len -=3D sizeof(*hdr); + + tags =3D __be32_to_cpu(hdr->tags); + max_tags =3D data_len / sizeof(*entry); + + /* Finite termination on tags loop. */ + if (tags > max_tags) + return -EINVAL; + + datasize =3D __be32_to_cpu(hdr->datasize); + if (datasize !=3D data_len - tags * sizeof(*entry)) + return -EINVAL; + + pr_debug("Scanning %d RPM header sections\n", tags); + for (i =3D 0; i < tags; i++) { + if (data_len < sizeof(*entry)) + return -EINVAL; + + entry =3D (const struct rpm_entryinfo *)data; + data +=3D sizeof(*entry); + data_len -=3D sizeof(*entry); + + switch (__be32_to_cpu(entry->tag)) { + case RPMTAG_FILEDIGESTS: + if (__be32_to_cpu(entry->type) !=3D RPM_STRING_ARRAY_TYPE) + return -EINVAL; + + digests_offset =3D __be32_to_cpu(entry->offset); + digests_count =3D __be32_to_cpu(entry->count); + digests_offset_set =3D true; + + pr_debug("Found RPMTAG_FILEDIGESTS at offset %u, count: %u\n", + digests_offset, digests_count); + break; + case RPMTAG_FILEDIGESTALGO: + if (__be32_to_cpu(entry->type) !=3D RPM_INT32_TYPE) + return -EINVAL; + + algo_offset =3D __be32_to_cpu(entry->offset); + algo_offset_set =3D true; + + pr_debug("Found RPMTAG_FILEDIGESTALGO at offset %u\n", + algo_offset); + break; + default: + break; + } + } + + if (!digests_offset_set) + return -EINVAL; + + if (algo_offset_set) { + if (algo_offset >=3D data_len) + return -EINVAL; + + if (data_len - algo_offset < sizeof(uint32_t)) + return -EINVAL; + + pkg_pgp_algo =3D *(uint32_t *)&data[algo_offset]; + pkg_pgp_algo =3D __be32_to_cpu(pkg_pgp_algo); + if (pkg_pgp_algo > DIGEST_ALGO_SHA224) { + pr_debug("Unknown PGP algo %d\n", pkg_pgp_algo); + return -EINVAL; + } + + pkg_kernel_algo =3D pgp_algo_mapping[pkg_pgp_algo]; + if (pkg_kernel_algo >=3D HASH_ALGO__LAST) { + pr_debug("Unknown mapping for PGP algo %d\n", + pkg_pgp_algo); + return -EINVAL; + } + + pr_debug("Found mapping for PGP algo %d: %s\n", pkg_pgp_algo, + hash_algo_name[pkg_kernel_algo]); + } + + digest_cache->algo =3D pkg_kernel_algo; + digest_len =3D hash_digest_size[pkg_kernel_algo]; + + if (digests_offset > data_len) + return -EINVAL; + + /* Worst case, every digest is a \0. */ + max_digests_count =3D data_len - digests_offset; + + /* Finite termination on digests_count loop. */ + if (digests_count > max_digests_count) + return -EINVAL; + + ret =3D digest_cache_init_htable(digest_cache, digests_count); + if (ret < 0) + return ret; + + ret =3D -ENOENT; + + for (i =3D 0; i < digests_count; i++) { + if (digests_offset =3D=3D data_len) + return -EINVAL; + + if (!data[digests_offset]) { + digests_offset++; + continue; + } + + if (data_len - digests_offset < digest_len * 2 + 1) + return -EINVAL; + + ret =3D hex2bin(rpm_digest, (const char *)&data[digests_offset], + digest_len); + if (ret < 0) { + pr_debug("Invalid hex format for digest %s\n", + &data[digests_offset]); + ret =3D -EINVAL; + break; + } + + ret =3D digest_cache_add(digest_cache, rpm_digest); + if (ret < 0) + return ret; + + digests_offset +=3D digest_len * 2 + 1; + } + + return ret; +} --=20 2.34.1