From: Roberto Sassu <roberto.sassu@xxxxxxxxxx> 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@xxxxxxxxxx> --- 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@xxxxxxxxxx> + * + * 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@xxxxxxxxxx> + * + * 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@xxxxxxxxxx> + * + * 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@xxxxxxxxxx> + * + * 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@xxxxxxxxxx> + * + * 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