2021-09-15 16:35:56

by Roberto Sassu

[permalink] [raw]
Subject: [RFC][PATCH 5/9] diglim: Compact digest list generator

Introduce a generator for the compact digest lists, which can be directly
uploaded to the kernel.

This tool can be used to generate a digest list from a file or the files in
the specified directory.

Files with execute permissions, without write permissions, those in
/lib/modules (not files starting with modules.) and in /lib/firmware are
marked as immutable (IMA with appraisal in enforcing mode will deny
writes).

Signed-off-by: Roberto Sassu <[email protected]>
---
MAINTAINERS | 4 +
tools/diglim/Makefile | 18 ++
tools/diglim/common.c | 79 +++++++++
tools/diglim/common.h | 59 +++++++
tools/diglim/compact_gen.c | 349 +++++++++++++++++++++++++++++++++++++
5 files changed, 509 insertions(+)
create mode 100644 tools/diglim/Makefile
create mode 100644 tools/diglim/common.c
create mode 100644 tools/diglim/common.h
create mode 100644 tools/diglim/compact_gen.c

diff --git a/MAINTAINERS b/MAINTAINERS
index 94220e40b7e2..b752790c06ea 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -5522,6 +5522,10 @@ F: security/integrity/diglim/ima.c
F: security/integrity/diglim/loader.c
F: security/integrity/diglim/methods.c
F: security/integrity/diglim/parser.c
+F: tools/diglim/Makefile
+F: tools/diglim/common.c
+F: tools/diglim/common.h
+F: tools/diglim/compact_gen.c
F: tools/testing/selftests/diglim/

DIOLAN U2C-12 I2C DRIVER
diff --git a/tools/diglim/Makefile b/tools/diglim/Makefile
new file mode 100644
index 000000000000..45efa554449d
--- /dev/null
+++ b/tools/diglim/Makefile
@@ -0,0 +1,18 @@
+# SPDX-License-Identifier: GPL-2.0
+
+CC := $(CROSS_COMPILE)gcc
+CFLAGS += -O2 -Wall -g -I./ -I../../usr/include/ -ggdb
+
+PROGS := compact_gen
+PROGS_EXTENDED := common.o
+
+all: $(PROGS)
+
+clean:
+ rm -fr $(PROGS) $(PROGS_EXTENDED)
+
+common.o: common.c
+ $(CC) -c $(CFLAGS) $< -o $@
+
+compact_gen: compact_gen.c $(PROGS_EXTENDED)
+ $(CC) $(CFLAGS) $< $(PROGS_EXTENDED) -o $@ $(LDFLAGS) -lcrypto
diff --git a/tools/diglim/common.c b/tools/diglim/common.c
new file mode 100644
index 000000000000..dd5ff4b186b3
--- /dev/null
+++ b/tools/diglim/common.c
@@ -0,0 +1,79 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2005,2006,2007,2008 IBM Corporation
+ * Copyright (C) 2017-2021 Huawei Technologies Duesseldorf GmbH
+ *
+ * Author: Roberto Sassu <[email protected]>
+ *
+ * Common functions and data.
+ */
+
+#include <sys/random.h>
+#include <errno.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <ctype.h>
+#include <malloc.h>
+#include <unistd.h>
+#include <string.h>
+#include <limits.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <linux/types.h>
+#include <linux/hash_info.h>
+
+#include "common.h"
+
+char *compact_types_str[COMPACT__LAST] = {
+ [COMPACT_PARSER] = "parser",
+ [COMPACT_FILE] = "file",
+ [COMPACT_METADATA] = "metadata",
+ [COMPACT_DIGEST_LIST] = "digest_list",
+};
+
+const char *const hash_algo_name[HASH_ALGO__LAST] = {
+ [HASH_ALGO_MD4] = "md4",
+ [HASH_ALGO_MD5] = "md5",
+ [HASH_ALGO_SHA1] = "sha1",
+ [HASH_ALGO_RIPE_MD_160] = "rmd160",
+ [HASH_ALGO_SHA256] = "sha256",
+ [HASH_ALGO_SHA384] = "sha384",
+ [HASH_ALGO_SHA512] = "sha512",
+ [HASH_ALGO_SHA224] = "sha224",
+ [HASH_ALGO_RIPE_MD_128] = "rmd128",
+ [HASH_ALGO_RIPE_MD_256] = "rmd256",
+ [HASH_ALGO_RIPE_MD_320] = "rmd320",
+ [HASH_ALGO_WP_256] = "wp256",
+ [HASH_ALGO_WP_384] = "wp384",
+ [HASH_ALGO_WP_512] = "wp512",
+ [HASH_ALGO_TGR_128] = "tgr128",
+ [HASH_ALGO_TGR_160] = "tgr160",
+ [HASH_ALGO_TGR_192] = "tgr192",
+ [HASH_ALGO_SM3_256] = "sm3",
+ [HASH_ALGO_STREEBOG_256] = "streebog256",
+ [HASH_ALGO_STREEBOG_512] = "streebog512",
+};
+
+const int hash_digest_size[HASH_ALGO__LAST] = {
+ [HASH_ALGO_MD4] = MD5_DIGEST_SIZE,
+ [HASH_ALGO_MD5] = MD5_DIGEST_SIZE,
+ [HASH_ALGO_SHA1] = SHA1_DIGEST_SIZE,
+ [HASH_ALGO_RIPE_MD_160] = RMD160_DIGEST_SIZE,
+ [HASH_ALGO_SHA256] = SHA256_DIGEST_SIZE,
+ [HASH_ALGO_SHA384] = SHA384_DIGEST_SIZE,
+ [HASH_ALGO_SHA512] = SHA512_DIGEST_SIZE,
+ [HASH_ALGO_SHA224] = SHA224_DIGEST_SIZE,
+ [HASH_ALGO_RIPE_MD_128] = RMD128_DIGEST_SIZE,
+ [HASH_ALGO_RIPE_MD_256] = RMD256_DIGEST_SIZE,
+ [HASH_ALGO_RIPE_MD_320] = RMD320_DIGEST_SIZE,
+ [HASH_ALGO_WP_256] = WP256_DIGEST_SIZE,
+ [HASH_ALGO_WP_384] = WP384_DIGEST_SIZE,
+ [HASH_ALGO_WP_512] = WP512_DIGEST_SIZE,
+ [HASH_ALGO_TGR_128] = TGR128_DIGEST_SIZE,
+ [HASH_ALGO_TGR_160] = TGR160_DIGEST_SIZE,
+ [HASH_ALGO_TGR_192] = TGR192_DIGEST_SIZE,
+ [HASH_ALGO_SM3_256] = SM3256_DIGEST_SIZE,
+ [HASH_ALGO_STREEBOG_256] = STREEBOG256_DIGEST_SIZE,
+ [HASH_ALGO_STREEBOG_512] = STREEBOG512_DIGEST_SIZE,
+};
diff --git a/tools/diglim/common.h b/tools/diglim/common.h
new file mode 100644
index 000000000000..d33e5082c17c
--- /dev/null
+++ b/tools/diglim/common.h
@@ -0,0 +1,59 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2005,2006,2007,2008 IBM Corporation
+ * Copyright (C) 2017-2021 Huawei Technologies Duesseldorf GmbH
+ *
+ * Author: Roberto Sassu <[email protected]>
+ *
+ * Header of common.c
+ */
+
+#include <sys/random.h>
+#include <errno.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <ctype.h>
+#include <malloc.h>
+#include <unistd.h>
+#include <string.h>
+#include <limits.h>
+#include <stdbool.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <linux/types.h>
+#include <linux/hash_info.h>
+
+#include "../../usr/include/linux/diglim.h"
+
+#define MD5_DIGEST_SIZE 16
+#define SHA1_DIGEST_SIZE 20
+#define RMD160_DIGEST_SIZE 20
+#define SHA256_DIGEST_SIZE 32
+#define SHA384_DIGEST_SIZE 48
+#define SHA512_DIGEST_SIZE 64
+#define SHA224_DIGEST_SIZE 28
+#define RMD128_DIGEST_SIZE 16
+#define RMD256_DIGEST_SIZE 32
+#define RMD320_DIGEST_SIZE 40
+#define WP256_DIGEST_SIZE 32
+#define WP384_DIGEST_SIZE 48
+#define WP512_DIGEST_SIZE 64
+#define TGR128_DIGEST_SIZE 16
+#define TGR160_DIGEST_SIZE 20
+#define TGR192_DIGEST_SIZE 24
+#define SM3256_DIGEST_SIZE 32
+#define STREEBOG256_DIGEST_SIZE 32
+#define STREEBOG512_DIGEST_SIZE 64
+
+#define COMPACT_LIST_SIZE_MAX (64 * 1024 * 1024 - 1)
+
+/* kernel types */
+typedef u_int8_t u8;
+typedef u_int16_t u16;
+typedef u_int32_t u32;
+typedef u_int64_t u64;
+
+extern char *compact_types_str[COMPACT__LAST];
+extern const char *const hash_algo_name[HASH_ALGO__LAST];
+extern const int hash_digest_size[HASH_ALGO__LAST];
diff --git a/tools/diglim/compact_gen.c b/tools/diglim/compact_gen.c
new file mode 100644
index 000000000000..0bfe8584de46
--- /dev/null
+++ b/tools/diglim/compact_gen.c
@@ -0,0 +1,349 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2017-2021 Huawei Technologies Duesseldorf GmbH
+ *
+ * Author: Roberto Sassu <[email protected]>
+ *
+ * Generate compact digest lists.
+ */
+
+#include <stdio.h>
+#include <fcntl.h>
+#include <errno.h>
+
+#include <limits.h>
+#include <openssl/sha.h>
+#include <openssl/evp.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <fts.h>
+#include <string.h>
+#include <getopt.h>
+#include <linux/hash_info.h>
+#include <bits/endianness.h>
+
+#if __BYTE_ORDER == __BIG_ENDIAN
+#include <linux/byteorder/big_endian.h>
+#else
+#include <linux/byteorder/little_endian.h>
+#endif
+
+#include "common.h"
+
+static int gen_filename_prefix(char *filename, int filename_len, int pos,
+ const char *format, enum compact_types type)
+{
+ return snprintf(filename, filename_len, "%d-%s_list-%s-",
+ (pos >= 0) ? pos : 0, compact_types_str[type], format);
+}
+
+static int calc_digest(u8 *digest, void *data, u64 len, enum hash_algo algo)
+{
+ EVP_MD_CTX *mdctx;
+ const EVP_MD *md;
+ int ret = -EINVAL;
+
+ OpenSSL_add_all_algorithms();
+
+ md = EVP_get_digestbyname(hash_algo_name[algo]);
+ if (!md)
+ goto out;
+
+ mdctx = EVP_MD_CTX_create();
+ if (!mdctx)
+ goto out;
+
+ if (EVP_DigestInit_ex(mdctx, md, NULL) != 1)
+ goto out_mdctx;
+
+ if (EVP_DigestUpdate(mdctx, data, len) != 1)
+ goto out_mdctx;
+
+ if (EVP_DigestFinal_ex(mdctx, digest, NULL) != 1)
+ goto out_mdctx;
+
+ ret = 0;
+out_mdctx:
+ EVP_MD_CTX_destroy(mdctx);
+out:
+ EVP_cleanup();
+ return ret;
+}
+
+static int calc_file_digest(u8 *digest, char *path, enum hash_algo algo)
+{
+ void *data = MAP_FAILED;
+ struct stat st;
+ int fd, ret = 0;
+
+ if (stat(path, &st) == -1)
+ return -EACCES;
+
+ fd = open(path, O_RDONLY);
+ if (fd < 0)
+ return -EACCES;
+
+ if (st.st_size) {
+ data = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
+ if (data == MAP_FAILED) {
+ ret = -ENOMEM;
+ goto out;
+ }
+ }
+
+ ret = calc_digest(digest, data, st.st_size, algo);
+out:
+ if (data != MAP_FAILED)
+ munmap(data, st.st_size);
+
+ close(fd);
+ return ret;
+}
+
+static u8 *new_digest_list(enum hash_algo algo, enum compact_types type,
+ u16 modifiers)
+{
+ u8 *digest_list;
+ struct compact_list_hdr *hdr;
+
+ digest_list = mmap(NULL, COMPACT_LIST_SIZE_MAX, PROT_READ | PROT_WRITE,
+ MAP_PRIVATE | MAP_ANONYMOUS, 0, 0);
+ if (digest_list == MAP_FAILED) {
+ printf("Cannot allocate buffer\n");
+ return NULL;
+ }
+
+ hdr = (struct compact_list_hdr *)digest_list;
+ memset(hdr, 0, sizeof(*hdr));
+
+ hdr->version = 1;
+ hdr->type = __cpu_to_le16(type);
+ hdr->modifiers = __cpu_to_le16(modifiers);
+ hdr->algo = __cpu_to_le16(algo);
+ return digest_list;
+}
+
+static int write_digest_list(int fd, u8 *digest_list)
+{
+ struct compact_list_hdr *hdr;
+ u32 datalen;
+ ssize_t ret;
+
+ hdr = (struct compact_list_hdr *)digest_list;
+ if (!hdr->count)
+ return 0;
+
+ datalen = hdr->datalen;
+ hdr->count = __cpu_to_le32(hdr->count);
+ hdr->datalen = __cpu_to_le32(hdr->datalen);
+
+ ret = write(fd, digest_list, sizeof(*hdr) + datalen);
+ if (ret != sizeof(*hdr) + datalen)
+ return -EIO;
+
+ return ret;
+}
+
+static int gen_compact_digest_list(char *input, enum hash_algo algo,
+ u8 *digest_list, u8 *digest_list_immutable)
+{
+ FTS *fts = NULL;
+ FTSENT *ftsent;
+ int fts_flags = (FTS_PHYSICAL | FTS_COMFOLLOW | FTS_NOCHDIR | FTS_XDEV);
+ char *paths[2] = { input, NULL };
+ u8 *digest_list_ptr = digest_list;
+ struct compact_list_hdr *cur_hdr;
+ int ret;
+
+ if (!digest_list)
+ digest_list_ptr = digest_list_immutable;
+
+ fts = fts_open(paths, fts_flags, NULL);
+ if (!fts) {
+ printf("Unable to open %s\n", input);
+ return -EACCES;
+ }
+
+ while ((ftsent = fts_read(fts)) != NULL) {
+ switch (ftsent->fts_info) {
+ case FTS_F:
+ if (((ftsent->fts_statp->st_mode & 0111) ||
+ !(ftsent->fts_statp->st_mode & 0222)) &&
+ ftsent->fts_statp->st_size)
+ digest_list_ptr = digest_list_immutable;
+
+ if ((strstr(ftsent->fts_path, "/lib/modules") &&
+ strncmp(ftsent->fts_name, "modules.", 8)) ||
+ strstr(ftsent->fts_path, "/lib/firmware"))
+ digest_list_ptr = digest_list_immutable;
+
+ cur_hdr = (struct compact_list_hdr *)digest_list_ptr;
+
+ ret = calc_file_digest(digest_list_ptr +
+ sizeof(*cur_hdr) + cur_hdr->datalen,
+ ftsent->fts_path, algo);
+ if (ret < 0) {
+ printf("Cannot calculate digest of %s\n",
+ ftsent->fts_path);
+ continue;
+ }
+
+ cur_hdr->count++;
+ cur_hdr->datalen += hash_digest_size[algo];
+ break;
+ default:
+ break;
+ }
+ }
+
+ return 0;
+}
+
+static void usage(char *progname)
+{
+ printf("Usage: %s <options>\n", progname);
+ printf("Options:\n");
+ printf("\t-d <output directory>: directory digest lists are written to\n"
+ "\t-i <path>: file/directory the digest list is generated from\n"
+ "\t-t <type>: type of compact list to generate\n"
+ "\t-a <algo>: digest algorithm\n"
+ "\t-f: force the digest list to be immutable\n"
+ "\t-h: display help\n");
+}
+
+int main(int argc, char *argv[])
+{
+ char path[PATH_MAX];
+ char filename[NAME_MAX + 1];
+ char *output_dir = NULL, *input = NULL;
+ enum compact_types type = COMPACT_FILE;
+ enum hash_algo algo = HASH_ALGO_SHA256;
+ u8 *digest_list = NULL, *digest_list_immutable = NULL;
+ char *input_ptr;
+ struct stat st;
+ int c;
+ int ret, fd = -1, force_immutable = 0;
+
+ while ((c = getopt(argc, argv, "d:i:t:a:fh")) != -1) {
+ switch (c) {
+ case 'd':
+ output_dir = optarg;
+ break;
+ case 'i':
+ input = optarg;
+ break;
+ case 't':
+ for (type = 0; type < COMPACT__LAST; type++)
+ if (!strcmp(compact_types_str[type], optarg))
+ break;
+ if (type == COMPACT__LAST) {
+ printf("Invalid type %s\n", optarg);
+ exit(1);
+ }
+ break;
+ case 'a':
+ for (algo = 0; algo < HASH_ALGO__LAST; algo++)
+ if (!strcmp(hash_algo_name[algo], optarg))
+ break;
+ if (algo == HASH_ALGO__LAST) {
+ printf("Invalid algo %s\n", optarg);
+ exit(1);
+ }
+ break;
+ case 'f':
+ force_immutable = 1;
+ break;
+ case 'h':
+ usage(argv[0]);
+ exit(0);
+ default:
+ printf("Invalid option %c\n", c);
+ exit(1);
+ }
+ }
+
+ if (!output_dir) {
+ printf("Output directory not specified\n");
+ exit(1);
+ }
+
+ if (!input) {
+ printf("Input file/directory not specified\n");
+ exit(1);
+ }
+
+ if (stat(input, &st) == -1) {
+ printf("Input file/directory not found or not accessible\n");
+ exit(1);
+ }
+
+ if (stat(output_dir, &st) == -1)
+ mkdir(output_dir, 0755);
+
+ gen_filename_prefix(filename, sizeof(filename), 0, "compact", type);
+
+ input_ptr = strrchr(input, '/');
+ if (input_ptr)
+ input_ptr++;
+ else
+ input_ptr = input;
+
+ snprintf(path, sizeof(path), "%s/%s%s", output_dir, filename,
+ input_ptr);
+
+ if (!force_immutable) {
+ digest_list = new_digest_list(algo, type, 0);
+ if (!digest_list) {
+ ret = -ENOMEM;
+ goto out;
+ }
+ }
+
+ digest_list_immutable = new_digest_list(algo, type,
+ (1 << COMPACT_MOD_IMMUTABLE));
+ if (!digest_list_immutable) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ fd = open(path, O_WRONLY | O_CREAT | O_TRUNC, 0644);
+ if (fd < 0) {
+ printf("Unable to create %s\n", path);
+ ret = -errno;
+ goto out;
+ }
+
+ ret = gen_compact_digest_list(input, algo, digest_list,
+ digest_list_immutable);
+ if (ret < 0) {
+ printf("Unable to generate the digest list from %s\n", input);
+ goto out;
+ }
+
+ if (!force_immutable) {
+ ret = write_digest_list(fd, digest_list);
+ if (ret < 0) {
+ printf("Unable to write the digest list to %s\n", path);
+ goto out;
+ }
+ }
+
+ ret = write_digest_list(fd, digest_list_immutable);
+ if (ret < 0)
+ printf("Unable to write the digest list to %s\n", path);
+out:
+ if (digest_list)
+ munmap(digest_list, COMPACT_LIST_SIZE_MAX);
+ if (digest_list_immutable)
+ munmap(digest_list_immutable, COMPACT_LIST_SIZE_MAX);
+
+ if (fd >= 0)
+ close(fd);
+
+ if (ret < 0)
+ unlink(path);
+
+ return ret;
+}
--
2.25.1