@@ -13,7 +13,8 @@ integrity-$(CONFIG_INTEGRITY_PLATFORM_KEYRING) += platform_certs/platform_keyrin
integrity-$(CONFIG_INTEGRITY_MACHINE_KEYRING) += platform_certs/machine_keyring.o
integrity-$(CONFIG_INTEGRITY_DIGEST_CACHE) += 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) += platform_certs/efi_parser.o \
platform_certs/load_uefi.o \
platform_certs/keyring_handler.o
@@ -129,6 +129,8 @@ static int digest_cache_parse_digest_list(struct digest_cache *digest_cache,
if (!strncmp(digest_list_path->dentry->d_name.name, "tlv-", 4))
ret = digest_list_parse_tlv(digest_cache, data, data_len);
+ else if (!strncmp(digest_list_path->dentry->d_name.name, "rpm-", 4))
+ ret = digest_list_parse_rpm(digest_cache, data, data_len);
return ret;
}
@@ -11,3 +11,5 @@
int digest_list_parse_tlv(struct digest_cache *digest_cache, const u8 *data,
size_t data_len);
+int digest_list_parse_rpm(struct digest_cache *digest_cache, const u8 *data,
+ size_t data_len);
new file mode 100644
@@ -0,0 +1,174 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2017-2023 Huawei Technologies Duesseldorf GmbH
+ *
+ * Author: Roberto Sassu <roberto.sassu@huawei.com>
+ *
+ * Parse an rpm digest list (RPM package header).
+ */
+
+#define pr_fmt(fmt) "RPM DIGEST LIST: "fmt
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/init_task.h>
+#include <linux/fs.h>
+#include <linux/namei.h>
+
+#define RPMTAG_FILEDIGESTS 1035
+#define RPMTAG_FILEDIGESTALGO 5011
+
+#include "parsers.h"
+
+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 = 1,
+ DIGEST_ALGO_SHA1 = 2,
+ DIGEST_ALGO_RMD160 = 3,
+ /* 4, 5, 6, and 7 are reserved. */
+ DIGEST_ALGO_SHA256 = 8,
+ DIGEST_ALGO_SHA384 = 9,
+ DIGEST_ALGO_SHA512 = 10,
+ DIGEST_ALGO_SHA224 = 11,
+};
+
+static const enum hash_algo pgp_algo_mapping[DIGEST_ALGO_SHA224 + 1] = {
+ [DIGEST_ALGO_MD5] = HASH_ALGO_MD5,
+ [DIGEST_ALGO_SHA1] = HASH_ALGO_SHA1,
+ [DIGEST_ALGO_RMD160] = HASH_ALGO_RIPE_MD_160,
+ [4] = HASH_ALGO__LAST,
+ [5] = HASH_ALGO__LAST,
+ [6] = HASH_ALGO__LAST,
+ [7] = HASH_ALGO__LAST,
+ [DIGEST_ALGO_SHA256] = HASH_ALGO_SHA256,
+ [DIGEST_ALGO_SHA384] = HASH_ALGO_SHA384,
+ [DIGEST_ALGO_SHA512] = HASH_ALGO_SHA512,
+ [DIGEST_ALGO_SHA224] = HASH_ALGO_SHA224,
+};
+
+int digest_list_parse_rpm(struct digest_cache *digest_cache, const u8 *data,
+ size_t data_len)
+{
+ const u8 *bufp = data, *bufendp = data + data_len;
+ const u8 *datap, *digests = NULL, *algo_buf = NULL;
+ struct rpm_hdr *hdr = (struct rpm_hdr *)bufp;
+ struct rpm_entryinfo *entry;
+ u32 tags = be32_to_cpu(hdr->tags);
+ u32 digests_count = 0;
+ enum hash_algo pkg_kernel_algo = HASH_ALGO_MD5;
+ enum pgp_algos pkg_pgp_algo;
+ u8 rpm_digest[SHA512_DIGEST_SIZE];
+ int ret, i, digest_len;
+
+ const unsigned char rpm_header_magic[8] = {
+ 0x8e, 0xad, 0xe8, 0x01, 0x00, 0x00, 0x00, 0x00
+ };
+
+ 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(bufp, rpm_header_magic, sizeof(rpm_header_magic))) {
+ pr_debug("RPM header magic mismatch\n");
+ return -EINVAL;
+ }
+
+ bufp += sizeof(*hdr);
+ datap = bufp + tags * sizeof(struct rpm_entryinfo);
+
+ pr_debug("Scanning %d RPM header sections\n", tags);
+
+ for (i = 0; i < tags && (bufp + sizeof(*entry)) <= bufendp;
+ i++, bufp += sizeof(*entry)) {
+ entry = (struct rpm_entryinfo *)bufp;
+
+ switch (be32_to_cpu(entry->tag)) {
+ case RPMTAG_FILEDIGESTS:
+ digests = datap + be32_to_cpu(entry->offset);
+ digests_count = be32_to_cpu(entry->count);
+ pr_debug("Found RPMTAG_FILEDIGESTS at offset %ld, count: %d\n",
+ digests - data, digests_count);
+ break;
+ case RPMTAG_FILEDIGESTALGO:
+ algo_buf = datap + be32_to_cpu(entry->offset);
+ pr_debug("Found RPMTAG_FILEDIGESTALGO at offset %ld\n",
+ algo_buf - data);
+ break;
+ }
+
+ if (digests && algo_buf)
+ break;
+ }
+
+ if (!digests)
+ return 0;
+
+ if (algo_buf && algo_buf + sizeof(u32) <= bufendp) {
+ pkg_pgp_algo = be32_to_cpu(*(u32 *)algo_buf);
+ if (pkg_pgp_algo > DIGEST_ALGO_SHA224) {
+ pr_debug("Unknown PGP algo %d\n", pkg_pgp_algo);
+ return -EINVAL;
+ }
+
+ pkg_kernel_algo = pgp_algo_mapping[pkg_pgp_algo];
+ if (pkg_kernel_algo >= 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 = pkg_kernel_algo;
+ digest_len = hash_digest_size[pkg_kernel_algo];
+
+ ret = digest_cache_init_htable(digest_cache, digests_count);
+ if (ret < 0)
+ return ret;
+
+ ret = -ENOENT;
+
+ for (i = 0; i < digests_count && digests < bufendp; i++) {
+ if (!*digests) {
+ digests++;
+ continue;
+ }
+
+ if (digests + digest_len * 2 + 1 > bufendp) {
+ pr_debug("Read beyond end\n");
+ ret = -EINVAL;
+ break;
+ }
+
+ ret = hex2bin(rpm_digest, digests, digest_len);
+ if (ret < 0) {
+ pr_debug("Invalid hex format for digest %s\n", digests);
+ ret = -EINVAL;
+ break;
+ }
+
+ ret = digest_cache_add(digest_cache, rpm_digest);
+ if (ret < 0)
+ return ret;
+
+ digests += digest_len * 2 + 1;
+ }
+
+ return ret;
+}