All of lore.kernel.org
 help / color / mirror / Atom feed
From: Roberto Sassu <roberto.sassu@huaweicloud.com>
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 <roberto.sassu@huawei.com>
Subject: [RFC][PATCH v2 05/13] integrity/digest_cache: Parse tlv digest lists
Date: Sat, 12 Aug 2023 12:46:08 +0200	[thread overview]
Message-ID: <20230812104616.2190095-6-roberto.sassu@huaweicloud.com> (raw)
In-Reply-To: <20230812104616.2190095-1-roberto.sassu@huaweicloud.com>

From: Roberto Sassu <roberto.sassu@huawei.com>

Add a parser for TLV-formatted (Type Length Value) digest lists. Their
structure is:

[header: DIGEST_LIST_FILE, num fields, total len]
[field: DIGEST_LIST_ALGO, length, value]
[field: DIGEST_LIST_ENTRY#1, length, value (below)]
 |- [header: DIGEST_LIST_FILE, num fields, total len]
 |- [ENTRY#1_DIGEST, length, file digest]
 |- [ENTRY#1_PATH, length, file path]
[field: DIGEST_LIST_ENTRY#N, length, value (below)]
 |- [header: DIGEST_LIST_FILE, num fields, total len]
 |- [ENTRY#N_DIGEST, length, file digest]
 |- [ENTRY#N_PATH, length, file path]

Defined fields are sufficient for measurement/appraisal of file content.
More fields can be introduced later (e.g. for appraisal of file metadata).

This patch defines only the callbacks (handlers) for the defined fields.
The parsing logic is already introduced in lib/tlv_parser.c.

Signed-off-by: Roberto Sassu <roberto.sassu@huawei.com>
---
 include/uapi/linux/tlv_digest_list.h          |  59 ++++++
 security/integrity/Makefile                   |   3 +-
 security/integrity/digest_cache.c             |   4 +
 .../integrity/digest_list_parsers/parsers.h   |  13 ++
 security/integrity/digest_list_parsers/tlv.c  | 188 ++++++++++++++++++
 5 files changed, 266 insertions(+), 1 deletion(-)
 create mode 100644 include/uapi/linux/tlv_digest_list.h
 create mode 100644 security/integrity/digest_list_parsers/parsers.h
 create mode 100644 security/integrity/digest_list_parsers/tlv.c

diff --git a/include/uapi/linux/tlv_digest_list.h b/include/uapi/linux/tlv_digest_list.h
new file mode 100644
index 00000000000..52987b63877
--- /dev/null
+++ b/include/uapi/linux/tlv_digest_list.h
@@ -0,0 +1,59 @@
+/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
+/*
+ * Copyright (C) 2017-2023 Huawei Technologies Duesseldorf GmbH
+ *
+ * Author: Roberto Sassu <roberto.sassu@huawei.com>
+ *
+ * Export definitions of the tlv digest list.
+ */
+
+#ifndef _UAPI_LINUX_TLV_DIGEST_LIST_H
+#define _UAPI_LINUX_TLV_DIGEST_LIST_H
+
+#include <linux/types.h>
+
+#define FOR_EACH_DIGEST_LIST_TYPE(DIGEST_LIST_TYPE) \
+	DIGEST_LIST_TYPE(DIGEST_LIST_FILE) \
+	DIGEST_LIST_TYPE(DIGEST_LIST__LAST)
+
+#define FOR_EACH_FIELD(FIELD) \
+	FIELD(DIGEST_LIST_ALGO) \
+	FIELD(DIGEST_LIST_ENTRY) \
+	FIELD(FIELD__LAST)
+
+#define FOR_EACH_ENTRY_FIELD(ENTRY_FIELD) \
+	ENTRY_FIELD(ENTRY_DIGEST) \
+	ENTRY_FIELD(ENTRY_PATH) \
+	ENTRY_FIELD(ENTRY__LAST)
+
+#define GENERATE_ENUM(ENUM) ENUM,
+#define GENERATE_STRING(STRING) #STRING,
+
+/**
+ * enum digest_list_types - Type of digest list
+ *
+ * Enumerates the types of digest lists to parse.
+ */
+enum digest_list_types {
+	FOR_EACH_DIGEST_LIST_TYPE(GENERATE_ENUM)
+};
+
+/**
+ * enum fields - Digest list fields
+ *
+ * Enumerates the digest list fields.
+ */
+enum digest_list_fields {
+	FOR_EACH_FIELD(GENERATE_ENUM)
+};
+
+/**
+ * enum entry_fields - Entry-specific fields
+ *
+ * Enumerates the digest list entry-specific fields.
+ */
+enum entry_fields {
+	FOR_EACH_ENTRY_FIELD(GENERATE_ENUM)
+};
+
+#endif /* _UAPI_LINUX_TLV_DIGEST_LIST_H */
diff --git a/security/integrity/Makefile b/security/integrity/Makefile
index c856ed10fba..3765b004e66 100644
--- a/security/integrity/Makefile
+++ b/security/integrity/Makefile
@@ -12,7 +12,8 @@ integrity-$(CONFIG_INTEGRITY_ASYMMETRIC_KEYS) += digsig_asymmetric.o
 integrity-$(CONFIG_INTEGRITY_PLATFORM_KEYRING) += platform_certs/platform_keyring.o
 integrity-$(CONFIG_INTEGRITY_MACHINE_KEYRING) += platform_certs/machine_keyring.o
 integrity-$(CONFIG_INTEGRITY_DIGEST_CACHE) += digest_cache.o \
-					      digest_cache_iter.o
+					      digest_cache_iter.o \
+					      digest_list_parsers/tlv.o
 integrity-$(CONFIG_LOAD_UEFI_KEYS) += 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 d14d84b804b..818ac0ac0bf 100644
--- a/security/integrity/digest_cache.c
+++ b/security/integrity/digest_cache.c
@@ -18,6 +18,7 @@
 #include <linux/module_signature.h>
 
 #include "integrity.h"
+#include "digest_list_parsers/parsers.h"
 
 #ifdef pr_fmt
 #undef pr_fmt
@@ -141,6 +142,9 @@ static int digest_cache_parse_digest_list(struct digest_cache *digest_cache,
 parse:
 	pr_debug("Parsing %s, size: %ld\n", digest_cache->path_str, data_len);
 
+	if (!strncmp(digest_list_path->dentry->d_name.name, "tlv-", 4))
+		ret = digest_list_parse_tlv(digest_cache, data, data_len);
+
 	return ret;
 }
 
diff --git a/security/integrity/digest_list_parsers/parsers.h b/security/integrity/digest_list_parsers/parsers.h
new file mode 100644
index 00000000000..e8fff2374d8
--- /dev/null
+++ b/security/integrity/digest_list_parsers/parsers.h
@@ -0,0 +1,13 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2023 Huawei Technologies Duesseldorf GmbH
+ *
+ * Author: Roberto Sassu <roberto.sassu@huawei.com>
+ *
+ * Digest list parsers.
+ */
+
+#include "../digest_cache.h"
+
+int digest_list_parse_tlv(struct digest_cache *digest_cache, const u8 *data,
+			  size_t data_len);
diff --git a/security/integrity/digest_list_parsers/tlv.c b/security/integrity/digest_list_parsers/tlv.c
new file mode 100644
index 00000000000..239400f5786
--- /dev/null
+++ b/security/integrity/digest_list_parsers/tlv.c
@@ -0,0 +1,188 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2017-2023 Huawei Technologies Duesseldorf GmbH
+ *
+ * Author: Roberto Sassu <roberto.sassu@huawei.com>
+ *
+ * Parse a tlv digest list.
+ */
+
+#define pr_fmt(fmt) "TLV DIGEST LIST: "fmt
+#include <linux/fs.h>
+#include <linux/hash_info.h>
+#include <linux/tlv_parser.h>
+#include <uapi/linux/tlv_digest_list.h>
+
+#include "parsers.h"
+
+#define kenter(FMT, ...) \
+	pr_debug("==> %s("FMT")\n", __func__, ##__VA_ARGS__)
+#define kleave(FMT, ...) \
+	pr_debug("<== %s()"FMT"\n", __func__, ##__VA_ARGS__)
+
+const char *digest_list_types_str[] = {
+	FOR_EACH_DIGEST_LIST_TYPE(GENERATE_STRING)
+};
+
+const char *digest_list_fields_str[] = {
+	FOR_EACH_FIELD(GENERATE_STRING)
+};
+
+const char *entry_fields_str[] = {
+	FOR_EACH_ENTRY_FIELD(GENERATE_STRING)
+};
+
+static int parse_digest_list_algo(struct digest_cache *digest_cache,
+				  enum digest_list_fields field,
+				  const u8 *field_data, u64 field_data_len)
+{
+	u8 algo;
+	int ret = 0;
+
+	kenter(",%u,%llu", field, field_data_len);
+
+	if (digest_cache->algo != HASH_ALGO__LAST) {
+		pr_debug("Digest algorithm already set to %s\n",
+			 hash_algo_name[digest_cache->algo]);
+		ret = -EBADMSG;
+		goto out;
+	}
+
+	if (field_data_len != sizeof(u8)) {
+		pr_debug("Unexpected data length %llu, expected %lu\n",
+			 field_data_len, sizeof(u8));
+		ret = -EBADMSG;
+		goto out;
+	}
+
+	algo = *field_data;
+
+	if (algo >= HASH_ALGO__LAST) {
+		pr_debug("Unexpected digest algo %u\n", algo);
+		ret = -EBADMSG;
+		goto out;
+	}
+
+	digest_cache->algo = algo;
+	pr_debug("Digest algo: %s\n", hash_algo_name[algo]);
+out:
+	kleave(" = %d", ret);
+	return ret;
+}
+
+static int parse_entry_digest(struct digest_cache *digest_cache,
+			      enum entry_fields field, const u8 *field_data,
+			      u64 field_data_len)
+{
+	int ret = 0;
+
+	kenter(",%u,%llu", field, field_data_len);
+
+	if (field_data_len != hash_digest_size[digest_cache->algo]) {
+		pr_debug("Unexpected data length %llu, expected %d\n",
+			 field_data_len, hash_digest_size[digest_cache->algo]);
+		ret = -EBADMSG;
+		goto out;
+	}
+
+	digest_cache_add(digest_cache, (u8 *)field_data);
+out:
+	kleave(" = %d", ret);
+	return ret;
+}
+
+static int entry_callback(void *callback_data, u64 field, const u8 *field_data,
+			  u64 field_data_len)
+{
+	struct digest_cache *digest_cache;
+	int ret;
+
+	digest_cache = (struct digest_cache *)callback_data;
+
+	switch (field) {
+	case ENTRY_DIGEST:
+		ret = parse_entry_digest(digest_cache, field, field_data,
+					 field_data_len);
+		break;
+	case ENTRY_PATH:
+		ret = 0;
+		break;
+	default:
+		pr_debug("Unhandled field %s\n", entry_fields_str[field]);
+		/* Just ignore non-relevant fields. */
+		ret = 0;
+		break;
+	}
+
+	return ret;
+}
+
+static int parse_digest_list_entry(struct digest_cache *digest_cache,
+				   enum digest_list_fields field,
+				   const u8 *field_data, u64 field_data_len)
+{
+	int ret;
+
+	kenter(",%u,%llu", field, field_data_len);
+
+	ret = tlv_parse(DIGEST_LIST_FILE, entry_callback, digest_cache,
+			field_data, field_data_len, digest_list_types_str,
+			DIGEST_LIST__LAST, entry_fields_str, ENTRY__LAST);
+
+	kleave(" = %d", ret);
+	return ret;
+}
+
+static int digest_list_callback(void *callback_data, u64 field,
+				const u8 *field_data, u64 field_data_len)
+{
+	struct digest_cache *digest_cache;
+	int ret;
+
+	digest_cache = (struct digest_cache *)callback_data;
+
+	switch (field) {
+	case DIGEST_LIST_ALGO:
+		ret = parse_digest_list_algo(digest_cache, field, field_data,
+					     field_data_len);
+		break;
+	case DIGEST_LIST_ENTRY:
+		ret = parse_digest_list_entry(digest_cache, field, field_data,
+					      field_data_len);
+		break;
+	default:
+		pr_debug("Unhandled field %s\n",
+			 digest_list_fields_str[field]);
+		/* Just ignore non-relevant fields. */
+		ret = 0;
+		break;
+	}
+
+	return ret;
+}
+
+int digest_list_parse_tlv(struct digest_cache *digest_cache, const u8 *data,
+			  size_t data_len)
+{
+	u64 parsed_data_type;
+	u64 parsed_num_fields;
+	u64 parsed_total_len;
+	int ret;
+
+	ret = tlv_parse_hdr(&data, &data_len, &parsed_data_type,
+			    &parsed_num_fields, &parsed_total_len,
+			    digest_list_types_str, DIGEST_LIST__LAST);
+	if (ret < 0)
+		return ret;
+
+	if (parsed_data_type != DIGEST_LIST_FILE)
+		return 0;
+
+	ret = digest_cache_init_htable(digest_cache, parsed_num_fields);
+	if (ret < 0)
+		return ret;
+
+	return tlv_parse_data(digest_list_callback, digest_cache,
+			      parsed_num_fields, data, data_len,
+			      digest_list_fields_str, FIELD__LAST);
+}
-- 
2.34.1


  parent reply	other threads:[~2023-08-12 10:48 UTC|newest]

Thread overview: 21+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2023-08-12 10:46 [RFC][PATCH v2 00/13] integrity: Introduce a digest cache Roberto Sassu
2023-08-12 10:46 ` [RFC][PATCH v2 01/13] ima: Introduce hook DIGEST_LIST_CHECK Roberto Sassu
2023-08-12 10:46 ` [RFC][PATCH v2 02/13] integrity: Introduce a digest cache Roberto Sassu
2023-08-14 17:03   ` Jarkko Sakkinen
2023-08-16  8:21     ` Roberto Sassu
2023-08-16 20:39       ` Jarkko Sakkinen
2023-08-12 10:46 ` [RFC][PATCH v2 03/13] integrity/digest_cache: Add functions to populate and search Roberto Sassu
2023-08-14 17:13   ` Jarkko Sakkinen
2023-08-16  8:35     ` Roberto Sassu
2023-08-16 21:00       ` Jarkko Sakkinen
2023-08-12 10:46 ` [RFC][PATCH v2 04/13] integrity/digest_cache: Prefetch digest lists in a directory Roberto Sassu
2023-08-12 10:46 ` Roberto Sassu [this message]
2023-08-12 10:46 ` [RFC][PATCH v2 06/13] integrity/digest_cache: Parse rpm digest lists Roberto Sassu
2023-08-12 10:46 ` [RFC][PATCH v2 07/13] ima: Add digest_cache policy keyword Roberto Sassu
2023-08-12 10:46 ` [RFC][PATCH v2 08/13] ima: Use digest cache for measurement Roberto Sassu
2023-08-12 10:46 ` [RFC][PATCH v2 09/13] ima: Use digest cache for appraisal Roberto Sassu
2023-08-12 10:46 ` [RFC][PATCH v2 10/13] tools: Add tool to manage digest lists Roberto Sassu
2023-08-12 10:46 ` [RFC][PATCH v2 11/13] tools/digest-lists: Add tlv digest list generator and parser Roberto Sassu
2023-08-12 10:46 ` [RFC][PATCH v2 12/13] tools/digest-lists: Add rpm " Roberto Sassu
2023-08-12 10:46 ` [RFC][PATCH v2 13/13] docs: Add documentation of the integrity digest cache Roberto Sassu
2023-09-05 15:46 ` [RFC][PATCH v2 00/13] integrity: Introduce a " Roberto Sassu

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20230812104616.2190095-6-roberto.sassu@huaweicloud.com \
    --to=roberto.sassu@huaweicloud.com \
    --cc=bpf@vger.kernel.org \
    --cc=corbet@lwn.net \
    --cc=dmitry.kasatkin@gmail.com \
    --cc=hch@lst.de \
    --cc=jannh@google.com \
    --cc=jarkko@kernel.org \
    --cc=jmorris@namei.org \
    --cc=linux-doc@vger.kernel.org \
    --cc=linux-integrity@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-security-module@vger.kernel.org \
    --cc=mjg59@srcf.ucam.org \
    --cc=paul@paul-moore.com \
    --cc=pbrobinson@gmail.com \
    --cc=pmatilai@redhat.com \
    --cc=roberto.sassu@huawei.com \
    --cc=serge@hallyn.com \
    --cc=zbyszek@in.waw.pl \
    --cc=zohar@linux.ibm.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.