[RFC][PATCH v2 11/13] tools/digest-lists: Add tlv digest list generator and parser

Roberto Sassu roberto.sassu at huaweicloud.com
Sat Aug 12 10:46:14 UTC 2023


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

Add a generator of tlv digest lists. It will store the digest
algorithm, the digest and path of each file provided as input.

Also add a parser of tlv digest lists. It will display the content (digest
algorithm and value, and file path), and will add/remove the
security.digest_list xattr to/from each file in the digest list.

Signed-off-by: Roberto Sassu <roberto.sassu at huawei.com>
---
 tools/digest-lists/.gitignore              |   2 +
 tools/digest-lists/Makefile                |  22 ++-
 tools/digest-lists/generators/generators.h |  16 ++
 tools/digest-lists/generators/tlv.c        | 168 ++++++++++++++++++
 tools/digest-lists/manage_digest_lists.c   |   5 +
 tools/digest-lists/parsers/parsers.h       |  14 ++
 tools/digest-lists/parsers/tlv.c           | 195 +++++++++++++++++++++
 tools/digest-lists/parsers/tlv_parser.h    |  38 ++++
 8 files changed, 458 insertions(+), 2 deletions(-)
 create mode 100644 tools/digest-lists/generators/generators.h
 create mode 100644 tools/digest-lists/generators/tlv.c
 create mode 100644 tools/digest-lists/parsers/parsers.h
 create mode 100644 tools/digest-lists/parsers/tlv.c
 create mode 100644 tools/digest-lists/parsers/tlv_parser.h

diff --git a/tools/digest-lists/.gitignore b/tools/digest-lists/.gitignore
index 1b8a7b9c205..9a75ae766ff 100644
--- a/tools/digest-lists/.gitignore
+++ b/tools/digest-lists/.gitignore
@@ -1,3 +1,5 @@
 # SPDX-License-Identifier: GPL-2.0
 manage_digest_lists
 manage_digest_lists.1
+libgen-tlv-list.so
+libparse-tlv-list.so
diff --git a/tools/digest-lists/Makefile b/tools/digest-lists/Makefile
index 05af3a91c06..23f9fa3b588 100644
--- a/tools/digest-lists/Makefile
+++ b/tools/digest-lists/Makefile
@@ -1,13 +1,23 @@
 # SPDX-License-Identifier: GPL-2.0
 include ../scripts/Makefile.include
+include ../scripts/Makefile.arch
 include ../scripts/utilities.mak
+
 BINDIR=usr/bin
+ifeq ($(LP64), 1)
+  LIBDIR=usr/lib64
+else
+  LIBDIR=usr/lib
+endif
 MANDIR=usr/share/man
 MAN1DIR=$(MANDIR)/man1
 CFLAGS=-ggdb -Wall
 
 PROGS=manage_digest_lists
 
+GENERATORS=libgen-tlv-list.so
+PARSERS=libparse-tlv-list.so
+
 MAN1=manage_digest_lists.1
 
 A2X=a2x
@@ -15,9 +25,15 @@ a2x_path := $(call get-executable,$(A2X))
 
 all: man $(PROGS)
 
-manage_digest_lists: manage_digest_lists.c common.c
+manage_digest_lists: manage_digest_lists.c common.c $(GENERATORS) $(PARSERS)
 	$(CC) $(CFLAGS) $^ -o $@ $(LDFLAGS) -lcrypto
 
+libgen-tlv-list.so: generators/tlv.c common.c
+	$(CC) $(CFLAGS) -fPIC --shared -Wl,-soname,libgen-tlv-list.so $^ -o $@
+
+libparse-tlv-list.so: parsers/tlv.c common.c ../../lib/tlv_parser.c
+	$(CC) $(CFLAGS) -fPIC --shared -Wl,-soname,libparse-tlv-list.so $^ -o $@ -I parsers
+
 ifneq ($(findstring $(MAKEFLAGS),s),s)
   ifneq ($(V),1)
      QUIET_A2X = @echo '  A2X     '$@;
@@ -32,7 +48,7 @@ else
 endif
 
 clean:
-	rm -f $(MAN1) $(PROGS)
+	rm -f $(MAN1) $(PROGS) $(GENERATORS) $(PARSERS)
 
 man: $(MAN1)
 
@@ -43,6 +59,8 @@ install-man: man
 install-tools: $(PROGS)
 	install -d -m 755 $(INSTALL_ROOT)/$(BINDIR)
 	install -m 755 -p $(PROGS) "$(INSTALL_ROOT)/$(BINDIR)/$(TARGET)"
+	install -m 755 -p $(GENERATORS) "$(INSTALL_ROOT)/$(LIBDIR)/$(TARGET)"
+	install -m 755 -p $(PARSERS) "$(INSTALL_ROOT)/$(LIBDIR)/$(TARGET)"
 
 install: install-tools install-man
 .PHONY: all clean man install-tools install-man install
diff --git a/tools/digest-lists/generators/generators.h b/tools/digest-lists/generators/generators.h
new file mode 100644
index 00000000000..9830b791667
--- /dev/null
+++ b/tools/digest-lists/generators/generators.h
@@ -0,0 +1,16 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2017-2023 Huawei Technologies Duesseldorf GmbH
+ *
+ * Author: Roberto Sassu <roberto.sassu at huawei.com>
+ *
+ * Header for all digest list generators.
+ */
+
+#include <stdio.h>
+#include <fcntl.h>
+#include <errno.h>
+
+void *tlv_list_gen_new(int dirfd, char *input, enum hash_algo algo);
+int tlv_list_gen_add(int dirfd, void *ptr, char *input);
+void tlv_list_gen_close(void *ptr);
diff --git a/tools/digest-lists/generators/tlv.c b/tools/digest-lists/generators/tlv.c
new file mode 100644
index 00000000000..cbc29a49f51
--- /dev/null
+++ b/tools/digest-lists/generators/tlv.c
@@ -0,0 +1,168 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2017-2023 Huawei Technologies Duesseldorf GmbH
+ *
+ * Author: Roberto Sassu <roberto.sassu at huawei.com>
+ *
+ * Generate tlv digest lists.
+ */
+
+#include <stdio.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <limits.h>
+#include <sys/mman.h>
+#include <sys/xattr.h>
+#include <linux/xattr.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <string.h>
+#include <linux/hash_info.h>
+#include <asm/byteorder.h>
+
+#ifndef __packed
+#define __packed __attribute__((packed))
+#endif
+
+#include "../../../include/uapi/linux/tlv_parser.h"
+#include "../../../include/uapi/linux/tlv_digest_list.h"
+#include "../common.h"
+
+struct tlv_struct {
+	__u8 *digest_list;
+	struct tlv_hdr *outer_hdr;
+	struct tlv_entry *outer_entry;
+	__u8 algo;
+	int fd;
+};
+
+static int new_digest_list(int dirfd, const char *input, struct tlv_struct *tlv)
+{
+	char filename[NAME_MAX + 1];
+	struct tlv_hdr *hdr;
+	const char *input_ptr;
+
+	input_ptr = strrchr(input, '/');
+	if (input_ptr)
+		input_ptr++;
+	else
+		input_ptr = input;
+
+	snprintf(filename, sizeof(filename), "tlv-%s", input_ptr);
+
+	tlv->fd = openat(dirfd, filename, O_RDWR | O_CREAT | O_TRUNC, 0644);
+	if (tlv->fd < 0) {
+		printf("Unable to create %s\n", filename);
+		return -errno;
+	}
+
+	ftruncate(tlv->fd, DIGEST_LIST_SIZE_MAX);
+	tlv->digest_list = mmap(NULL, DIGEST_LIST_SIZE_MAX,
+				PROT_READ | PROT_WRITE, MAP_SHARED, tlv->fd, 0);
+
+	if (tlv->digest_list == MAP_FAILED) {
+		printf("Cannot allocate buffer\n");
+		close(tlv->fd);
+		return -ENOMEM;
+	}
+
+	hdr = (struct tlv_hdr *)tlv->digest_list;
+	memset(hdr, 0, sizeof(*hdr));
+
+	hdr->data_type = __cpu_to_be64(DIGEST_LIST_FILE);
+	hdr->num_fields = 0;
+	hdr->total_len = 0;
+	return 0;
+}
+
+static void write_entry(struct tlv_hdr *hdr, struct tlv_entry **entry,
+			__u16 field, __u8 *data, __u32 data_len,
+			bool update_data)
+{
+	__u16 num_fields;
+	__u64 total_len;
+	__u64 entry_len;
+
+	num_fields = __be64_to_cpu(hdr->num_fields);
+	total_len = __be64_to_cpu(hdr->total_len);
+
+	(*entry)->field = __cpu_to_be64(field);
+	(*entry)->length = __cpu_to_be64(data_len);
+
+	if (update_data)
+		memcpy((*entry)->data, data, data_len);
+
+	num_fields++;
+	entry_len = sizeof(*(*entry)) + data_len;
+	total_len += entry_len;
+
+	hdr->num_fields = __cpu_to_be64(num_fields);
+	hdr->total_len = __cpu_to_be64(total_len);
+	(*entry) = (struct tlv_entry *)((__u8 *)*entry + entry_len);
+}
+
+void *tlv_list_gen_new(int dirfd, char *input, enum hash_algo algo)
+{
+	struct tlv_struct *tlv;
+	int ret;
+
+	tlv = malloc(sizeof(*tlv));
+	if (!tlv)
+		return NULL;
+
+	ret = new_digest_list(dirfd, input, tlv);
+	if (ret < 0) {
+		free(tlv);
+		return NULL;
+	}
+
+	tlv->outer_hdr = (struct tlv_hdr *)tlv->digest_list;
+	tlv->outer_entry = (struct tlv_entry *)(tlv->outer_hdr + 1);
+	tlv->algo = algo;
+
+	write_entry(tlv->outer_hdr, &tlv->outer_entry, DIGEST_LIST_ALGO,
+		    &tlv->algo, sizeof(tlv->algo), true);
+	return tlv;
+}
+
+int tlv_list_gen_add(int dirfd, void *ptr, char *input)
+{
+	struct tlv_struct *tlv = (struct tlv_struct *)ptr;
+	__u8 digest[SHA512_DIGEST_SIZE];
+	struct tlv_hdr *inner_hdr;
+	struct tlv_entry *inner_entry;
+	int ret;
+
+	ret = calc_file_digest(digest, input, tlv->algo);
+	if (ret < 0) {
+		printf("Cannot calculate digest of %s\n", input);
+		return ret;
+	}
+
+	inner_hdr = (struct tlv_hdr *)(tlv->outer_entry + 1);
+	inner_hdr->data_type = __cpu_to_be64(DIGEST_LIST_FILE);
+
+	inner_entry = (struct tlv_entry *)(inner_hdr + 1);
+
+	write_entry(inner_hdr, &inner_entry, ENTRY_DIGEST, digest,
+		    hash_digest_size[tlv->algo], true);
+	write_entry(inner_hdr, &inner_entry, ENTRY_PATH, (__u8 *)input,
+		    strlen(input) + 1, true);
+
+	write_entry(tlv->outer_hdr, &tlv->outer_entry, DIGEST_LIST_ENTRY, NULL,
+		    (__u8 *)inner_entry - (__u8 *)inner_hdr, false);
+	return 0;
+}
+
+void tlv_list_gen_close(void *ptr)
+{
+	struct tlv_struct *tlv = (struct tlv_struct *)ptr;
+
+	munmap(tlv->digest_list, DIGEST_LIST_SIZE_MAX);
+	ftruncate(tlv->fd, (__u8 *)tlv->outer_entry - (__u8 *)tlv->outer_hdr);
+	close(tlv->fd);
+	free(tlv);
+}
diff --git a/tools/digest-lists/manage_digest_lists.c b/tools/digest-lists/manage_digest_lists.c
index bc425da5317..7caad681eee 100644
--- a/tools/digest-lists/manage_digest_lists.c
+++ b/tools/digest-lists/manage_digest_lists.c
@@ -20,6 +20,8 @@
 #include <fts.h>
 
 #include "common.h"
+#include "generators/generators.h"
+#include "parsers/parsers.h"
 
 const char *ops_str[OP__LAST] = {
 	[OP_GEN] = "gen",
@@ -29,9 +31,12 @@ const char *ops_str[OP__LAST] = {
 };
 
 struct generator generators[] = {
+	{ .name = "tlv", .new = tlv_list_gen_new, .add = tlv_list_gen_add,
+	  .close = tlv_list_gen_close },
 };
 
 struct parser parsers[] = {
+	{ .name = "tlv", .parse = tlv_list_parse },
 };
 
 static int generator_add(struct generator *generator, int dirfd,
diff --git a/tools/digest-lists/parsers/parsers.h b/tools/digest-lists/parsers/parsers.h
new file mode 100644
index 00000000000..708da7eac3b
--- /dev/null
+++ b/tools/digest-lists/parsers/parsers.h
@@ -0,0 +1,14 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2017-2023 Huawei Technologies Duesseldorf GmbH
+ *
+ * Author: Roberto Sassu <roberto.sassu at huawei.com>
+ *
+ * Header for all digest list parsers.
+ */
+
+#include <stdio.h>
+#include <fcntl.h>
+#include <errno.h>
+
+int tlv_list_parse(const char *digest_list_path, enum ops op);
diff --git a/tools/digest-lists/parsers/tlv.c b/tools/digest-lists/parsers/tlv.c
new file mode 100644
index 00000000000..1c9909e80b9
--- /dev/null
+++ b/tools/digest-lists/parsers/tlv.c
@@ -0,0 +1,195 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2017-2023 Huawei Technologies Duesseldorf GmbH
+ *
+ * Author: Roberto Sassu <roberto.sassu at huawei.com>
+ *
+ * Parse tlv digest lists.
+ */
+
+#include <stdio.h>
+#include <fcntl.h>
+#include <errno.h>
+
+#include <limits.h>
+#include <sys/mman.h>
+#include <sys/xattr.h>
+#include <linux/xattr.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <string.h>
+#include <linux/hash_info.h>
+#include <asm/byteorder.h>
+#include <tlv_parser.h>
+
+#ifndef __packed
+#define __packed __attribute__((packed))
+#endif
+
+#include "../../../include/uapi/linux/tlv_digest_list.h"
+#include "../common.h"
+
+struct tlv_parse_ctx {
+	const char *digest_list_path;
+	size_t digest_list_path_len;
+	enum hash_algo algo;
+	enum ops op;
+};
+
+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 tlv_parse_ctx *ctx,
+				  enum digest_list_fields field,
+				  const __u8 *field_data, __u64 field_data_len)
+{
+	ctx->algo = *field_data;
+	return 0;
+}
+
+static int parse_entry_digest(struct tlv_parse_ctx *ctx,
+			      enum entry_fields field, const __u8 *field_data,
+			      __u64 field_data_len)
+{
+	int i;
+
+	if (ctx->op != OP_SHOW)
+		return 0;
+
+	printf("%s:", hash_algo_name[ctx->algo]);
+
+	for (i = 0; i < hash_digest_size[ctx->algo]; i++)
+		printf("%02x", field_data[i]);
+
+	return 0;
+}
+
+static int parse_entry_path(struct tlv_parse_ctx *ctx, enum entry_fields field,
+			    const __u8 *field_data, __u64 field_data_len)
+{
+	char *entry_path = (char *)field_data;
+	int ret;
+
+	switch (ctx->op) {
+	case OP_SHOW:
+		printf(" %s\n", entry_path);
+		ret = 0;
+		break;
+	case OP_ADD_XATTR:
+		ret = lsetxattr(entry_path, XATTR_NAME_DIGEST_LIST,
+				ctx->digest_list_path,
+				ctx->digest_list_path_len, 0);
+		if (ret < 0 && errno == ENODATA)
+			ret = 0;
+
+		if (ret < 0)
+			printf("Error setting %s on %s, %s\n",
+			       XATTR_NAME_DIGEST_LIST, entry_path,
+			       strerror(errno));
+		break;
+	case OP_RM_XATTR:
+		ret = lremovexattr(entry_path, XATTR_NAME_DIGEST_LIST);
+		if (ret < 0 && errno == ENODATA)
+			ret = 0;
+
+		if (ret < 0)
+			printf("Error removing %s from %s, %s\n",
+			       XATTR_NAME_DIGEST_LIST, entry_path,
+			       strerror(errno));
+		break;
+	default:
+		break;
+	}
+
+	return 0;
+}
+
+static int entry_callback(void *callback_data, __u64 field,
+			  const __u8 *field_data, __u64 field_data_len)
+{
+	struct tlv_parse_ctx *ctx = (struct tlv_parse_ctx *)callback_data;
+	int ret;
+
+	switch (field) {
+	case ENTRY_DIGEST:
+		ret = parse_entry_digest(ctx, field, field_data,
+					 field_data_len);
+		break;
+	case ENTRY_PATH:
+		ret = parse_entry_path(ctx, field, field_data, field_data_len);
+		break;
+	default:
+		pr_debug("Unhandled field %llu\n", field);
+		/* Just ignore non-relevant fields. */
+		ret = 0;
+		break;
+	}
+
+	return ret;
+}
+
+static int parse_digest_list_entry(struct tlv_parse_ctx *ctx,
+				   enum digest_list_fields field,
+				   const __u8 *field_data, __u64 field_data_len)
+{
+	return tlv_parse(DIGEST_LIST_FILE, entry_callback, ctx, field_data,
+			 field_data_len, digest_list_types_str,
+			 DIGEST_LIST__LAST, entry_fields_str, ENTRY__LAST);
+}
+
+static int digest_list_callback(void *callback_data, __u64 field,
+				const __u8 *field_data, __u64 field_data_len)
+{
+	struct tlv_parse_ctx *ctx = (struct tlv_parse_ctx *)callback_data;
+	int ret;
+
+	switch (field) {
+	case DIGEST_LIST_ALGO:
+		ret = parse_digest_list_algo(ctx, field, field_data,
+					     field_data_len);
+		break;
+	case DIGEST_LIST_ENTRY:
+		ret = parse_digest_list_entry(ctx, field, field_data,
+					      field_data_len);
+		break;
+	default:
+		pr_debug("Unhandled field %llu\n", field);
+		/* Just ignore non-relevant fields. */
+		ret = 0;
+		break;
+	}
+
+	return ret;
+}
+
+int tlv_list_parse(const char *digest_list_path, enum ops op)
+{
+	struct tlv_parse_ctx ctx = {
+		.op = op, .digest_list_path = digest_list_path,
+		.digest_list_path_len = strlen(digest_list_path)
+	};
+	unsigned char *data;
+	size_t data_len;
+	int ret;
+
+	ret = read_file(digest_list_path, &data_len, &data);
+	if (ret < 0)
+		return ret;
+
+	ret = tlv_parse(DIGEST_LIST_FILE, digest_list_callback, &ctx, data,
+			data_len, digest_list_types_str, DIGEST_LIST__LAST,
+			digest_list_fields_str, FIELD__LAST);
+
+	munmap(data, data_len);
+	return ret;
+}
diff --git a/tools/digest-lists/parsers/tlv_parser.h b/tools/digest-lists/parsers/tlv_parser.h
new file mode 100644
index 00000000000..3c9f54a97b3
--- /dev/null
+++ b/tools/digest-lists/parsers/tlv_parser.h
@@ -0,0 +1,38 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2023 Huawei Technologies Duesseldorf GmbH
+ *
+ * Author: Roberto Sassu <roberto.sassu at huawei.com>
+ *
+ * Header file of TLV parser.
+ */
+
+#ifndef _TLV_PARSER_H
+#define _TLV_PARSER_H
+
+#include <stdio.h>
+#include <errno.h>
+#include <stddef.h>
+#include <asm/byteorder.h>
+#include <linux/tlv_parser.h>
+
+#ifdef TLV_DEBUG
+#define pr_debug(fmt, ...) printf(fmt, ##__VA_ARGS__)
+#else
+#define pr_debug(fmt, ...) { }
+#endif
+
+typedef int (*parse_callback)(void *, __u64, const __u8 *, __u64);
+
+int tlv_parse_hdr(const __u8 **data, size_t *data_len, __u64 *parsed_data_type,
+		  __u64 *parsed_num_fields, __u64 *parsed_total_len,
+		  const char **data_types, __u64 num_data_types);
+int tlv_parse_data(parse_callback callback, void *callback_data,
+		   __u64 parsed_num_fields, const __u8 *data, size_t data_len,
+		   const char **fields, __u64 num_fields);
+int tlv_parse(__u64 expected_data_type, parse_callback callback,
+	      void *callback_data, const __u8 *data, size_t data_len,
+	      const char **data_types, __u64 num_data_types,
+	      const char **fields, __u64 num_fields);
+
+#endif /* _TLV_PARSER_H */
-- 
2.34.1



More information about the Linux-security-module-archive mailing list