linux-security-module.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [USER SPACE][RFC][PATCH 0/5] ima: digest-list-tools package
@ 2017-11-15 13:39 Roberto Sassu
  2017-11-15 13:39 ` [USER SPACE][RFC][PATCH 1/5] digest-list-tools: headers Roberto Sassu
                   ` (3 more replies)
  0 siblings, 4 replies; 5+ messages in thread
From: Roberto Sassu @ 2017-11-15 13:39 UTC (permalink / raw)
  To: linux-security-module

Recently, I published a patch set with title 'ima: digest list feature',
which allows users to upload to IMA a set of digests, that will be used as
reference values for measurement, appraisal and audit.

Although this patch set does not contain kernel patches, I would like to
publish it in the kernel mailing lists, so that people can have a better
understanding on how the digest list feature works, and can try it. The
kernel patches can be applied on top of the mainline kernel
(commit 37cb8e1f8) and https://patchwork.kernel.org/patch/10013259/.

I would also like to ask if it is fine to take code from the kernel without
including the original file. I tried to reuse as much code as possible, to
avoid two different implementations for kernel space and user space.

Thanks

Roberto


Roberto Sassu (5):
  digest-list-tools: headers
  digest-list-tools: library
  digest-list-tools: tools
  digest-list-tools: documentation
  digest-list-tools: package

-- 
2.11.0

--
To unsubscribe from this list: send the line "unsubscribe linux-security-module" in
the body of a message to majordomo at vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 5+ messages in thread

* [USER SPACE][RFC][PATCH 1/5] digest-list-tools: headers
  2017-11-15 13:39 [USER SPACE][RFC][PATCH 0/5] ima: digest-list-tools package Roberto Sassu
@ 2017-11-15 13:39 ` Roberto Sassu
  2017-11-15 13:39 ` [USER SPACE][RFC][PATCH 2/5] digest-list-tools: library Roberto Sassu
                   ` (2 subsequent siblings)
  3 siblings, 0 replies; 5+ messages in thread
From: Roberto Sassu @ 2017-11-15 13:39 UTC (permalink / raw)
  To: linux-security-module

This patch adds the headers used by the library. kernel_lib.h contains
definitions taken from the Linux kernel.

Signed-off-by: Roberto Sassu <roberto.sassu@huawei.com>
---
 include/compact_list.h |  25 +++++++++
 include/kernel_ima.h   |  54 ++++++++++++++++++++
 include/kernel_lib.h   | 135 +++++++++++++++++++++++++++++++++++++++++++++++++
 include/lib.h          |  32 ++++++++++++
 include/metadata.h     |  32 ++++++++++++
 include/rpm.h          |  36 +++++++++++++
 6 files changed, 314 insertions(+)
 create mode 100644 include/compact_list.h
 create mode 100644 include/kernel_ima.h
 create mode 100644 include/kernel_lib.h
 create mode 100644 include/lib.h
 create mode 100644 include/metadata.h
 create mode 100644 include/rpm.h

diff --git a/include/compact_list.h b/include/compact_list.h
new file mode 100644
index 0000000..99066b0
--- /dev/null
+++ b/include/compact_list.h
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2017 Huawei Technologies Duesseldorf GmbH
+ *
+ * Author: Roberto Sassu <roberto.sassu@huawei.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, version 2 of the
+ * License.
+ *
+ * File: compact_list.h
+ *      Header of compact_list.c.
+ */
+
+#ifndef _COMPACT_LIST_H
+#define _COMPACT_LIST_H
+
+#include "kernel_ima.h"
+#include "rpm.h"
+
+int compact_list_from_rpm(Header rpm, char *outdir, char *output_filename);
+int compact_list_from_digest_list_ascii(char *input_filename, char *outdir,
+					char *output_filename, int is_mutable);
+
+#endif /*_COMPACT_LIST_H*/
diff --git a/include/kernel_ima.h b/include/kernel_ima.h
new file mode 100644
index 0000000..1b5fe1b
--- /dev/null
+++ b/include/kernel_ima.h
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2017 Huawei Technologies Duesseldorf GmbH
+ *
+ * Author: Roberto Sassu <roberto.sassu@huawei.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, version 2 of the
+ * License.
+ *
+ * File: kernel_ima.h
+ *      IMA functions header
+ */
+
+#ifndef _KERNEL_IMA_H
+#define _KERNEL_IMA_H
+
+#include "kernel_lib.h"
+#include "lib.h"
+
+#define ENFORCE_FIELDS 0x00000001
+#define ENFORCE_BUFEND 0x00000002
+
+extern int digests;
+extern int ima_hash_algo;
+
+struct compact_list_hdr {
+	u16 entry_id;
+	u32 count;
+	u32 datalen;
+} __attribute__((packed));
+
+struct ima_field_data {
+	u8 *data;
+	u_int32_t len;
+};
+
+enum digest_metadata_fields {DATA_ALGO, DATA_DIGEST, DATA_SIGNATURE,
+			     DATA_FILE_PATH, DATA_REF_ID, DATA_TYPE,
+			     DATA__LAST};
+
+enum digest_data_types {DATA_TYPE_COMPACT_LIST, DATA_TYPE_RPM};
+
+enum compact_list_entry_ids {COMPACT_DIGEST, COMPACT_DIGEST_MUTABLE};
+
+int ima_hash_setup(char *str);
+int ima_get_buflen(int maxfields, struct ima_field_data *fields,
+		   unsigned long *len_mask);
+int ima_write_buf(void *bufstartp, void *bufendp, void **bufcurp,
+		  int maxfields, struct ima_field_data *fields, int *curfields,
+		  unsigned long *len_mask, int enforce_mask, char *bufname);
+ssize_t ima_parse_digest_list_metadata(loff_t size, void *buf);
+
+#endif /* _KERNEL_IMA_H */
diff --git a/include/kernel_lib.h b/include/kernel_lib.h
new file mode 100644
index 0000000..63ca9f2
--- /dev/null
+++ b/include/kernel_lib.h
@@ -0,0 +1,135 @@
+/*
+ * Copyright (C) 1991, 1992 Linus Torvalds
+ * Copyright 2007 rPath, Inc. - All Rights Reserved
+ * Copyright (c) 2013 Dmitry Kasatkin <d.kasatkin@samsung.com>
+ * Copyright (C) 2017 Huawei Technologies Duesseldorf GmbH
+ *
+ * Author: Roberto Sassu <roberto.sassu@huawei.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, version 2 of the
+ * License.
+ *
+ * File: kernel_lib.h
+ *      Header of kernel_lib.c
+ */
+
+#ifndef _KERNEL_LIB_H
+#define _KERNEL_LIB_H
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <string.h>
+#include <linux/byteorder/little_endian.h>
+
+/* kernel types */
+typedef u_int8_t u8;
+typedef u_int16_t u16;
+typedef u_int32_t u32;
+typedef u_int64_t u64;
+typedef int bool;
+typedef long loff_t;
+
+enum kernel_read_file_id {READING_DIGEST_LIST_METADATA, READING_DIGEST_LIST};
+
+#define true 1
+#define false 0
+
+#define S_IWUGO         (S_IWUSR|S_IWGRP|S_IWOTH)
+#define S_IXUGO         (S_IXUSR|S_IXGRP|S_IXOTH)
+
+/* bitmap */
+#define __KERNEL_DIV_ROUND_UP(n, d) (((n) + (d) - 1) / (d))
+#define DIV_ROUND_UP __KERNEL_DIV_ROUND_UP
+#define BITS_PER_BYTE           8
+#define BITS_TO_LONGS(nr)       DIV_ROUND_UP(nr, BITS_PER_BYTE * sizeof(long))
+#define BITS_PER_LONG 64
+#define BIT_WORD(nr)            ((nr) / BITS_PER_LONG)
+#define DECLARE_BITMAP(name,bits) \
+	unsigned long name[BITS_TO_LONGS(bits)]
+
+#define small_const_nbits(nbits) \
+	(__builtin_constant_p(nbits) && (nbits) <= BITS_PER_LONG)
+
+#define BITMAP_FIRST_WORD_MASK(start) (~0UL << ((start) & (BITS_PER_LONG - 1)))
+#define BITMAP_LAST_WORD_MASK(nbits) (~0UL >> (-(nbits) & (BITS_PER_LONG - 1)))
+
+static inline bool constant_test_bit(int nr, const void *addr)
+{
+	const u32 *p = (const u32 *)addr;
+	return ((1UL << (nr & 31)) & (p[nr >> 5])) != 0;
+}
+
+#define test_bit(nr,addr) constant_test_bit((nr),(addr))
+
+/* errors */
+#define ENOENT           2      /* No such file or directory */
+#define ENOMEM          12      /* Out of memory */
+#define EACCES          13      /* Permission denied */
+#define EEXIST          17      /* File exists */
+#define EINVAL          22      /* Invalid argument */
+
+#define pr_err printf
+
+/* endianness conversion */
+#define be32_to_cpu __be32_to_cpu
+#define be16_to_cpu __be16_to_cpu
+#define le16_to_cpu __le16_to_cpu
+#define le32_to_cpu __le32_to_cpu
+#define cpu_to_le16 __cpu_to_le16
+#define cpu_to_le32 __cpu_to_le32
+
+/* crypto */
+#define CRYPTO_MAX_ALG_NAME             128
+
+#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 WP512_DIGEST_SIZE 64
+#define WP384_DIGEST_SIZE 48
+#define WP256_DIGEST_SIZE 32
+#define TGR192_DIGEST_SIZE 24
+#define TGR160_DIGEST_SIZE 20
+#define TGR128_DIGEST_SIZE 16
+#define SM3256_DIGEST_SIZE 32
+
+enum hash_algo {
+	HASH_ALGO_MD4,
+	HASH_ALGO_MD5,
+	HASH_ALGO_SHA1,
+	HASH_ALGO_RIPE_MD_160,
+	HASH_ALGO_SHA256,
+	HASH_ALGO_SHA384,
+	HASH_ALGO_SHA512,
+	HASH_ALGO_SHA224,
+	HASH_ALGO_RIPE_MD_128,
+	HASH_ALGO_RIPE_MD_256,
+	HASH_ALGO_RIPE_MD_320,
+	HASH_ALGO_WP_256,
+	HASH_ALGO_WP_384,
+	HASH_ALGO_WP_512,
+	HASH_ALGO_TGR_128,
+	HASH_ALGO_TGR_160,
+	HASH_ALGO_TGR_192,
+	HASH_ALGO_SM3_256,
+	HASH_ALGO__LAST
+};
+
+extern const char *const hash_algo_name[HASH_ALGO__LAST];
+extern const int hash_digest_size[HASH_ALGO__LAST];
+
+void bitmap_zero(unsigned long *dst, unsigned int nbits);
+void bitmap_set(unsigned long *map, unsigned int start, int len);
+
+int hex2bin(u8 *dst, const char *src, size_t count);
+
+#endif /* _KERNEL_LIB_H */
diff --git a/include/lib.h b/include/lib.h
new file mode 100644
index 0000000..2205cfd
--- /dev/null
+++ b/include/lib.h
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2017 Huawei Technologies Duesseldorf GmbH
+ *
+ * Author: Roberto Sassu <roberto.sassu@huawei.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, version 2 of the
+ * License.
+ *
+ * File: lib.h
+ *      Header of lib.h.
+ */
+
+#ifndef _LIB_H
+#define _LIB_H
+
+#include <openssl/sha.h>
+#include <openssl/evp.h>
+
+#include "kernel_lib.h"
+
+#define MAX_FILENAME_LENGTH 256
+
+extern char *digest_list_path;
+
+int calc_digest(u8 *digest, void *data, int len, enum hash_algo algo);
+int calc_file_digest(char *path, u8 *digest, enum hash_algo algo);
+int kernel_read_file_from_path(const char *path, void **buf, loff_t *size,
+			       loff_t max_size, enum kernel_read_file_id id);
+
+#endif /* _LIB_H */
diff --git a/include/metadata.h b/include/metadata.h
new file mode 100644
index 0000000..5658325
--- /dev/null
+++ b/include/metadata.h
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2017 Huawei Technologies Duesseldorf GmbH
+ *
+ * Author: Roberto Sassu <roberto.sassu@huawei.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, version 2 of the
+ * License.
+ *
+ * File: metadata.h
+ *      Header of metadata.c.
+ */
+
+#ifndef _METADATA_H
+#define _METADATA_H
+
+#include "compact_list.h"
+#include "rpm.h"
+#include "lib.h"
+
+enum input_formats { INPUT_FMT_RPMDB, INPUT_FMT_RPMPKG,
+		     INPUT_FMT_DIGEST_LIST_ASCII, INPUT_FMT__LAST };
+
+int write_digests_and_metadata(Header hdr, char *outdir,
+			       char *metadata_filename,
+			       enum input_formats input_fmt,
+			       char *input_filename,
+			       enum digest_data_types output_fmt,
+			       int is_mutable);
+
+#endif /*_METADATA_H*/
diff --git a/include/rpm.h b/include/rpm.h
new file mode 100644
index 0000000..dad557e
--- /dev/null
+++ b/include/rpm.h
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2017 Huawei Technologies Duesseldorf GmbH
+ *
+ * Author: Roberto Sassu <roberto.sassu@huawei.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, version 2 of the
+ * License.
+ *
+ * File: rpm.h
+ *      Header of rpm.c.
+ */
+
+#ifndef _RPM_H
+#define _RPM_H
+
+#include <rpm/rpmlib.h>
+#include <rpm/header.h>
+#include <rpm/rpmts.h>
+#include <rpm/rpmdb.h>
+#include <rpm/rpmlog.h>
+
+#include "kernel_ima.h"
+
+/* rpmlegacy.h */
+int headerGetEntry(Header h, rpm_tag_t tag, rpm_tagtype_t *type,
+		   rpm_data_t *p, rpm_count_t *c);
+void get_rpm_filename(Header rpm, char *outdir, char *output_filename,
+		      enum digest_data_types output_fmt);
+int check_rpm_digest_algo(Header rpm, char *output_filename);
+void get_rpm_header_signature(Header rpm, u8 **signature,
+			      rpm_count_t *signature_len);
+int write_rpm_header(Header rpm, char *outdir, char *output_filename);
+
+#endif /* _RPM_H */
-- 
2.11.0

--
To unsubscribe from this list: send the line "unsubscribe linux-security-module" in
the body of a message to majordomo at vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply related	[flat|nested] 5+ messages in thread

* [USER SPACE][RFC][PATCH 2/5] digest-list-tools: library
  2017-11-15 13:39 [USER SPACE][RFC][PATCH 0/5] ima: digest-list-tools package Roberto Sassu
  2017-11-15 13:39 ` [USER SPACE][RFC][PATCH 1/5] digest-list-tools: headers Roberto Sassu
@ 2017-11-15 13:39 ` Roberto Sassu
  2017-11-15 13:39 ` [USER SPACE][RFC][PATCH 3/5] digest-list-tools: tools Roberto Sassu
  2017-11-15 13:39 ` [USER SPACE][RFC][PATCH 4/5] digest-list-tools: documentation Roberto Sassu
  3 siblings, 0 replies; 5+ messages in thread
From: Roberto Sassu @ 2017-11-15 13:39 UTC (permalink / raw)
  To: linux-security-module

This patch adds the library necessary to generate/verify the compact and
RPM digest lists and digest list metadata. Some functions have been taken
from the Linux kernel.

Signed-off-by: Roberto Sassu <roberto.sassu@huawei.com>
---
 lib/compact_list.c | 160 +++++++++++++++++++++
 lib/kernel_ima.c   | 409 +++++++++++++++++++++++++++++++++++++++++++++++++++++
 lib/kernel_lib.c   | 130 +++++++++++++++++
 lib/lib.c          | 101 +++++++++++++
 lib/metadata.c     | 125 ++++++++++++++++
 lib/rpm.c          |  99 +++++++++++++
 6 files changed, 1024 insertions(+)
 create mode 100644 lib/compact_list.c
 create mode 100644 lib/kernel_ima.c
 create mode 100644 lib/kernel_lib.c
 create mode 100644 lib/lib.c
 create mode 100644 lib/metadata.c
 create mode 100644 lib/rpm.c

diff --git a/lib/compact_list.c b/lib/compact_list.c
new file mode 100644
index 0000000..26f6260
--- /dev/null
+++ b/lib/compact_list.c
@@ -0,0 +1,160 @@
+/*
+ * Copyright (C) 2017 Huawei Technologies Duesseldorf GmbH
+ *
+ * Author: Roberto Sassu <roberto.sassu@huawei.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, version 2 of the
+ * License.
+ *
+ * File: compact_list.c
+ *      Writes compact digest lists.
+ */
+
+#include <stdio.h>
+#include <fcntl.h>
+#include <sys/mman.h>
+
+#include "compact_list.h"
+
+int compact_list_from_rpm(Header rpm, char *outdir, char *output_filename)
+{
+	int digest_len = hash_digest_size[ima_hash_algo];
+	int ret, fd, datalen = 0, i;
+
+	rpm_tagtype_t data_type;
+	rpm_count_t data_count;
+	char **data;
+
+	u8 *output;
+
+	struct compact_list_hdr hdr = {0, 0, 0};
+
+	get_rpm_filename(rpm, outdir, output_filename, DATA_TYPE_COMPACT_LIST);
+
+	ret = check_rpm_digest_algo(rpm, output_filename);
+	if (ret < 0)
+		return ret;
+
+	ret = headerGetEntry(rpm, RPMTAG_FILEDIGESTS, &data_type,
+			     (void **)&data, &data_count);
+	if (ret < 0)
+		return -EINVAL;
+
+	output = malloc(digest_len * data_count);
+	if (output == NULL)
+		return -ENOMEM;
+
+	for (i = 0; i < data_count; i++) {
+		if (strlen(data[i]) == 0)
+			continue;
+
+		hex2bin(output + datalen, data[i], digest_len);
+		hdr.count++;
+		datalen += digest_len;
+	}
+
+	hdr.entry_id = COMPACT_DIGEST;
+	hdr.entry_id = cpu_to_le16(hdr.entry_id);
+	hdr.count = cpu_to_le32(hdr.count);
+	hdr.datalen = cpu_to_le32(datalen);
+
+	fd = open(output_filename, O_WRONLY | O_CREAT | O_TRUNC, 0600);
+	if (fd < 0)
+		return -EACCES;
+
+	write(fd, &hdr, sizeof(hdr));
+	write(fd, output, datalen);
+
+	close(fd);
+	free(output);
+	return 0;
+}
+
+int compact_list_from_digest_list_ascii(char *input_filename, char *outdir,
+					char *output_filename, int is_mutable)
+{
+	const char *algo_name = hash_algo_name[ima_hash_algo];
+	int algo_prefix_len = strlen(algo_name) + 1;
+	int digest_len = hash_digest_size[ima_hash_algo];
+	u8 digest[digest_len];
+	char *data, *datap, *line, *input_basename;
+	int datalen = 0;
+	struct stat st;
+	int ret = 0, inputfd = -1, outputfd;
+
+	struct compact_list_hdr hdr = {0, 0, 0};
+
+	if (stat(input_filename, &st) != 0)
+		return -EACCES;
+
+	if (st.st_size == 0)
+		return -EINVAL;
+
+	inputfd = open(input_filename, O_RDONLY);
+	if (inputfd < 0)
+		return -EACCES;
+
+	data = mmap(NULL, st.st_size, PROT_READ | PROT_WRITE,
+		    MAP_PRIVATE, inputfd, 0);
+	if (data == NULL) {
+		ret = -ENOMEM;
+		goto out;
+	}
+
+	datap = data;
+
+	input_basename = rindex(input_filename, '/');
+	if (input_basename == NULL)
+		input_basename = input_filename;
+	else
+		input_basename += 1;
+
+	snprintf(output_filename, MAX_FILENAME_LENGTH, "%s/compact-%s",
+		 outdir, input_basename);
+
+	outputfd = open(output_filename, O_WRONLY | O_CREAT | O_TRUNC, 0600);
+	if (outputfd < 0) {
+		printf("Unable to write %s\n", output_filename);
+		ret = -EACCES;
+		goto out;
+	}
+
+	lseek(outputfd, sizeof(hdr), SEEK_SET);
+
+	while (true) {
+		line = strsep(&datap, "\n");
+		if (line == NULL)
+			break;
+
+		if (strlen(line) < algo_prefix_len + digest_len * 2)
+			continue;
+
+		if (strncmp(line, algo_name, algo_prefix_len - 1)) {
+			printf("Digest algorithm mismatch, expected: %s\n",
+			       algo_name);
+			return -EINVAL;
+		}
+
+		hex2bin(digest, line + algo_prefix_len, digest_len);
+		write(outputfd, digest, digest_len);
+		hdr.count++;
+		datalen += digest_len;
+	}
+
+	hdr.entry_id = is_mutable ? COMPACT_DIGEST_MUTABLE : COMPACT_DIGEST;
+	hdr.entry_id = cpu_to_le16(hdr.entry_id);
+	hdr.count = cpu_to_le32(hdr.count);
+	hdr.datalen = cpu_to_le32(datalen);
+
+	lseek(outputfd, 0, SEEK_SET);
+	write(outputfd, &hdr, sizeof(hdr));
+	close(outputfd);
+out:
+	close(inputfd);
+	if (data)
+		munmap(data, st.st_size);
+
+	return ret;
+}
diff --git a/lib/kernel_ima.c b/lib/kernel_ima.c
new file mode 100644
index 0000000..38bc49a
--- /dev/null
+++ b/lib/kernel_ima.c
@@ -0,0 +1,409 @@
+/*
+ * Copyright (C) 2017 Huawei Technologies Duesseldorf GmbH
+ *
+ * Author: Roberto Sassu <roberto.sassu@huawei.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, version 2 of the
+ * License.
+ *
+ * File: kernel_ima.c
+ *      Includes IMA functions.
+ */
+#include <stdio.h>
+#include <unistd.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+
+#include "kernel_ima.h"
+
+int digests;
+
+int ima_add_digest_data_entry(u8 *digest, u8 is_mutable)
+{
+	digests++;
+	return 0;
+}
+
+int ima_get_buflen(int maxfields, struct ima_field_data *fields,
+		   unsigned long *len_mask)
+{
+	int len = 0, i;
+
+	for (i = 0; i < maxfields; i++) {
+		if (len_mask == NULL || !test_bit(i, len_mask))
+			len += sizeof(u32);
+
+		len += fields[i].len;
+	}
+
+	return len;
+}
+
+int ima_hash_algo = HASH_ALGO_SHA256;
+
+int ima_hash_setup(char *str)
+{
+	int i;
+
+	for (i = 0; i < HASH_ALGO__LAST; i++) {
+		if (strcmp(str, hash_algo_name[i]) == 0) {
+			ima_hash_algo = i;
+			break;
+		}
+	}
+
+	if (i == HASH_ALGO__LAST)
+		return -EINVAL;
+
+	return 0;
+}
+
+#define RPMTAG_FILEDIGESTS 1035
+#define RPMTAG_FILEMODES 1030
+
+struct rpm_hdr {
+	u32 magic;
+	u32 reserved;
+	u32 tags;
+	u32 datasize;
+} __attribute__((packed));
+
+struct rpm_entryinfo {
+	int32_t tag;
+	u32 type;
+	int32_t offset;
+	u32 count;
+} __attribute__((packed));
+
+
+int ima_parse_buf(void *bufstartp, void *bufendp, void **bufcurp,
+		  int maxfields, struct ima_field_data *fields, int *curfields,
+		  unsigned long *len_mask, int enforce_mask, char *bufname)
+{
+	void *bufp = bufstartp;
+	int i;
+
+	for (i = 0; i < maxfields; i++) {
+		if (len_mask == NULL || !test_bit(i, len_mask)) {
+			if (bufp > (bufendp - sizeof(u32)))
+				break;
+
+			fields[i].len = le32_to_cpu(*(u32 *)bufp);
+
+			bufp += sizeof(u32);
+		}
+
+		if (bufp > (bufendp - fields[i].len))
+			break;
+
+		fields[i].data = bufp;
+		bufp += fields[i].len;
+	}
+
+	if ((enforce_mask & ENFORCE_FIELDS) && i != maxfields) {
+		pr_err("%s: nr of fields mismatch: expected: %d, current: %d\n",
+		       bufname, maxfields, i);
+		return -EINVAL;
+	}
+
+	if ((enforce_mask & ENFORCE_BUFEND) && bufp != bufendp) {
+		pr_err("%s: buf end mismatch: expected: %p, current: %p\n",
+		       bufname, bufendp, bufp);
+		return -EINVAL;
+	}
+
+	if (curfields)
+		*curfields = i;
+
+	if (bufcurp)
+		*bufcurp = bufp;
+
+	return 0;
+}
+
+int ima_write_buf(void *bufstartp, void *bufendp, void **bufcurp,
+		  int maxfields, struct ima_field_data *fields, int *curfields,
+		  unsigned long *len_mask, int enforce_mask, char *bufname)
+{
+	void *bufp = bufstartp;
+	int i;
+
+	for (i = 0; i < maxfields; i++) {
+		if (len_mask == NULL || !test_bit(i, len_mask)) {
+			u32 field_len = fields[i].len;
+
+			if (bufp > (bufendp - sizeof(u32)))
+				break;
+
+			field_len = cpu_to_le32(field_len);
+
+			memcpy(bufp, &field_len, sizeof(field_len));
+
+			bufp += sizeof(u32);
+		}
+
+		if (bufp > (bufendp - fields[i].len))
+			break;
+
+		memcpy(bufp, fields[i].data, fields[i].len);
+		bufp += fields[i].len;
+	}
+
+	if ((enforce_mask & ENFORCE_FIELDS) && i != maxfields) {
+		pr_err("%s: nr of fields mismatch: expected: %d, current: %d\n",
+		       bufname, maxfields, i);
+		return -EINVAL;
+	}
+
+	if ((enforce_mask & ENFORCE_BUFEND) && bufp != bufendp) {
+		pr_err("%s: buf end mismatch: expected: %p, current: %p\n",
+		       bufname, bufendp, bufp);
+		return -EINVAL;
+	}
+
+	if (curfields)
+		*curfields = i;
+
+	if (bufcurp)
+		*bufcurp = bufp;
+
+	return 0;
+}
+
+static int ima_parse_compact_list(loff_t size, void *buf)
+{
+	void *bufp = buf, *bufendp = buf + size;
+	int digest_len = hash_digest_size[ima_hash_algo];
+	struct compact_list_hdr *hdr;
+	u8 is_mutable = 0;
+	int ret, i;
+
+	while (bufp < bufendp) {
+		if (bufp + sizeof(*hdr) > bufendp) {
+			pr_err("compact list, missing header\n");
+			return -EINVAL;
+		}
+
+		hdr = bufp;
+
+		hdr->entry_id = le16_to_cpu(hdr->entry_id);
+		hdr->count = le32_to_cpu(hdr->count);
+		hdr->datalen = le32_to_cpu(hdr->datalen);
+
+		switch (hdr->entry_id) {
+		case COMPACT_DIGEST_MUTABLE:
+			is_mutable = 1;
+		case COMPACT_DIGEST:
+			break;
+		default:
+			pr_err("compact list, invalid data type\n");
+			return -EINVAL;
+		}
+
+		bufp += sizeof(*hdr);
+
+		for (i = 0; i < hdr->count &&
+		     bufp + digest_len <= bufendp; i++) {
+			ret = ima_add_digest_data_entry(bufp, is_mutable);
+			if (ret < 0 && ret != -EEXIST)
+				return ret;
+
+			bufp += digest_len;
+		}
+
+		if (i != hdr->count ||
+		    bufp != (void *)hdr + sizeof(*hdr) + hdr->datalen) {
+			pr_err("compact list, invalid data\n");
+			return -EINVAL;
+		}
+	}
+
+	return 0;
+}
+
+static int ima_parse_rpm(loff_t size, void *buf)
+{
+	void *bufp = buf, *bufendp = buf + size;
+	struct rpm_hdr *hdr = bufp;
+	u32 tags = be32_to_cpu(hdr->tags);
+	struct rpm_entryinfo *entry;
+	void *datap = bufp + sizeof(*hdr) + tags * sizeof(struct rpm_entryinfo);
+	void *digests = NULL, *modes = NULL;
+	u32 digests_count, modes_count;
+	int digest_len = hash_digest_size[ima_hash_algo];
+	u8 digest[digest_len];
+	int ret, i;
+
+	const unsigned char rpm_header_magic[8] = {
+		0x8e, 0xad, 0xe8, 0x01, 0x00, 0x00, 0x00, 0x00
+	};
+
+	if (size < sizeof(*hdr)) {
+		pr_err("Missing RPM header\n");
+		return -EINVAL;
+	}
+
+	if (memcmp(bufp, rpm_header_magic, sizeof(rpm_header_magic))) {
+		pr_err("Invalid RPM header\n");
+		return -EINVAL;
+	}
+
+	bufp += sizeof(*hdr);
+
+	for (i = 0; i < tags && (bufp + sizeof(*entry)) <= bufendp;
+	     i++, bufp += sizeof(*entry)) {
+		entry = bufp;
+
+		if (be32_to_cpu(entry->tag) == RPMTAG_FILEDIGESTS) {
+			digests = datap + be32_to_cpu(entry->offset);
+			digests_count = be32_to_cpu(entry->count);
+		}
+		if (be32_to_cpu(entry->tag) == RPMTAG_FILEMODES) {
+			modes = datap + be32_to_cpu(entry->offset);
+			modes_count = be32_to_cpu(entry->count);
+		}
+		if (digests && modes)
+			break;
+	}
+
+	if (digests == NULL)
+		return 0;
+
+	for (i = 0; i < digests_count && digests < bufendp; i++) {
+		u8 is_mutable = 0;
+		u16 mode;
+
+		if (strlen(digests) == 0) {
+			digests++;
+			continue;
+		}
+
+		if (modes) {
+			if (i < modes_count &&
+			    modes + (i + 1) * sizeof(mode) > bufendp) {
+				pr_err("RPM header read at invalid offset\n");
+				return -EINVAL;
+			}
+
+			mode = be16_to_cpu(*(u16 *)(modes + i * sizeof(mode)));
+			if (!(mode & (S_IXUGO | S_ISUID | S_ISVTX)) &&
+			    (mode & S_IWUGO))
+				is_mutable = 1;
+		}
+
+		if (digests + digest_len * 2 + 1 > bufendp) {
+			pr_err("RPM header read at invalid offset\n");
+			return -EINVAL;
+		}
+
+		ret = hex2bin(digest, digests, digest_len);
+		if (ret < 0)
+			return -EINVAL;
+
+		ret = ima_add_digest_data_entry(digest, is_mutable);
+		if (ret < 0 && ret != -EEXIST)
+			return ret;
+
+		digests += digest_len * 2 + 1;
+	}
+
+	return 0;
+}
+
+static int ima_parse_digest_list_data(struct ima_field_data *data)
+{
+	int digest_len = hash_digest_size[ima_hash_algo];
+	u8 digest[digest_len];
+	void *digest_list;
+	loff_t digest_list_size;
+	u16 data_algo = le16_to_cpu(*(u16 *)data[DATA_ALGO].data);
+	u16 data_type = le16_to_cpu(*(u16 *)data[DATA_TYPE].data);
+	int ret, fd;
+
+	if (data_algo != ima_hash_algo) {
+		pr_err("Incompatible digest algorithm, expected %s\n",
+		       hash_algo_name[ima_hash_algo]);
+		return -EINVAL;
+	}
+
+	fd = kernel_read_file_from_path((char *)data[DATA_FILE_PATH].data,
+					&digest_list, &digest_list_size,
+					0, READING_DIGEST_LIST);
+	if (fd < 0) {
+		pr_err("Unable to open file: %s (%d)\n",
+		       data[DATA_FILE_PATH].data, fd);
+		return fd;
+	}
+
+	calc_digest(digest, digest_list, digest_list_size, ima_hash_algo);
+	if (memcmp(digest, data[DATA_DIGEST].data, data[DATA_DIGEST].len)) {
+		pr_err("Digest verification for %s failed\n",
+		       data[DATA_FILE_PATH].data);
+		ret = -EINVAL;
+		goto out;
+	}
+
+	ret = ima_add_digest_data_entry(digest, 0);
+	if (ret < 0) {
+		if (ret == -EEXIST)
+			ret = 1;
+
+		goto out;
+	}
+
+	switch (data_type) {
+	case DATA_TYPE_COMPACT_LIST:
+		ret = ima_parse_compact_list(digest_list_size, digest_list);
+		break;
+	case DATA_TYPE_RPM:
+		ret = ima_parse_rpm(digest_list_size, digest_list);
+		break;
+	default:
+		pr_err("Parser for data type %d not implemented\n", data_type);
+		ret = -EINVAL;
+	}
+
+	if (ret < 0)
+		pr_err("Error parsing file: %s (%d)\n",
+		       data[DATA_FILE_PATH].data, ret);
+out:
+	munmap(digest_list, digest_list_size);
+	close(fd);
+	return ret;
+}
+
+ssize_t ima_parse_digest_list_metadata(loff_t size, void *buf)
+{
+	struct ima_field_data entry;
+
+	struct ima_field_data entry_data[DATA__LAST] = {
+		[DATA_ALGO] = {.len = sizeof(u16)},
+		[DATA_TYPE] = {.len = sizeof(u16)},
+	};
+
+	DECLARE_BITMAP(data_mask, DATA__LAST);
+	void *bufp = buf, *bufendp = buf + size;
+	int ret;
+
+	bitmap_zero(data_mask, DATA__LAST);
+	bitmap_set(data_mask, DATA_ALGO, 1);
+	bitmap_set(data_mask, DATA_TYPE, 1);
+
+	ret = ima_parse_buf(bufp, bufendp, &bufp, 1, &entry, NULL, NULL,
+			    ENFORCE_FIELDS, "digest list entry");
+	if (ret < 0)
+		goto out;
+
+	ret = ima_parse_buf(entry.data, entry.data + entry.len, NULL,
+			    DATA__LAST, entry_data, NULL, data_mask,
+			    ENFORCE_FIELDS | ENFORCE_BUFEND,
+			    "digest list entry data");
+	if (ret < 0)
+		goto out;
+
+	ret = ima_parse_digest_list_data(entry_data);
+out:
+	return ret < 0 ? ret : bufp - buf;
+}
diff --git a/lib/kernel_lib.c b/lib/kernel_lib.c
new file mode 100644
index 0000000..2ea4650
--- /dev/null
+++ b/lib/kernel_lib.c
@@ -0,0 +1,130 @@
+/*
+ * Copyright (c) 2013 Dmitry Kasatkin <d.kasatkin@samsung.com>
+ * Copyright (C) 2017 Huawei Technologies Duesseldorf GmbH
+ *
+ * Author: Roberto Sassu <roberto.sassu@huawei.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, version 2 of the
+ * License.
+ *
+ * File: kernel_lib.c
+ *      Libraries from the Linux kernel.
+ */
+#include "kernel_lib.h"
+
+/* from lib/bitmap.c */
+void bitmap_zero(unsigned long *dst, unsigned int nbits)
+{
+	if (small_const_nbits(nbits))
+		*dst = 0UL;
+	else {
+		unsigned int len = BITS_TO_LONGS(nbits) * sizeof(unsigned long);
+		memset(dst, 0, len);
+	}
+}
+
+void bitmap_set(unsigned long *map, unsigned int start, int len)
+{
+	unsigned long *p = map + BIT_WORD(start);
+	const unsigned int size = start + len;
+	int bits_to_set = BITS_PER_LONG - (start % BITS_PER_LONG);
+	unsigned long mask_to_set = BITMAP_FIRST_WORD_MASK(start);
+
+	while (len - bits_to_set >= 0) {
+		*p |= mask_to_set;
+		len -= bits_to_set;
+		bits_to_set = BITS_PER_LONG;
+		mask_to_set = ~0UL;
+		p++;
+	}
+	if (len) {
+		mask_to_set &= BITMAP_LAST_WORD_MASK(size);
+		*p |= mask_to_set;
+	}
+}
+
+/* from crypto/hash_info.c */
+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-256",
+};
+
+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,
+};
+
+/* from lib/hexdump.c */
+
+/**
+ * hex_to_bin - convert a hex digit to its real value
+ * @ch: ascii character represents hex digit
+ *
+ * hex_to_bin() converts one hex digit to its actual value or -1 in case of bad
+ * input.
+ */
+int hex_to_bin(char ch)
+{
+	if ((ch >= '0') && (ch <= '9'))
+		return ch - '0';
+	ch = tolower(ch);
+	if ((ch >= 'a') && (ch <= 'f'))
+		return ch - 'a' + 10;
+	return -1;
+}
+
+/**
+ * hex2bin - convert an ascii hexadecimal string to its binary representation
+ * @dst: binary result
+ * @src: ascii hexadecimal string
+ * @count: result length
+ *
+ * Return 0 on success, -1 in case of bad input.
+ */
+int hex2bin(u8 *dst, const char *src, size_t count)
+{
+	while (count--) {
+		int hi = hex_to_bin(*src++);
+		int lo = hex_to_bin(*src++);
+
+		if ((hi < 0) || (lo < 0))
+			return -1;
+
+		*dst++ = (hi << 4) | lo;
+	}
+	return 0;
+}
diff --git a/lib/lib.c b/lib/lib.c
new file mode 100644
index 0000000..ad4b852
--- /dev/null
+++ b/lib/lib.c
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2017 Huawei Technologies Duesseldorf GmbH
+ *
+ * Author: Roberto Sassu <roberto.sassu@huawei.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, version 2 of the
+ * License.
+ *
+ * File: lib.c
+ *      Includes libraries.
+ */
+
+#include <stdio.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+
+#include "lib.h"
+
+char *digest_list_path;
+
+int calc_digest(u8 *digest, void *data, int len, enum hash_algo algo)
+{
+	EVP_MD_CTX *mdctx = EVP_MD_CTX_create();
+	const EVP_MD *md = EVP_get_digestbyname(hash_algo_name[algo]);
+
+	if (mdctx == NULL)
+		return -EINVAL;
+
+	if (EVP_DigestInit_ex(mdctx, md, NULL) != 1)
+		return -EINVAL;
+
+	if (EVP_DigestUpdate(mdctx, data, len) != 1)
+		return -EINVAL;
+
+	if (EVP_DigestFinal_ex(mdctx, digest, NULL) != 1)
+		return -EINVAL;
+
+	EVP_MD_CTX_destroy(mdctx);
+	return 0;
+}
+
+int calc_file_digest(char *path, u8 *digest, enum hash_algo algo)
+{
+	void *data;
+	struct stat st;
+	int fd, ret = 0;
+
+	if (stat(path, &st) != 0)
+		return -EACCES;
+
+	fd = open(path, O_RDONLY);
+	if (fd < 0)
+		return -EACCES;
+
+	data = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
+	if (data == NULL) {
+		ret = -ENOMEM;
+		goto out;
+	}
+
+	ret = calc_digest(digest, data, st.st_size, algo);
+out:
+	if (data)
+		munmap(data, st.st_size);
+
+	close(fd);
+	return ret;
+}
+
+int kernel_read_file_from_path(const char *path, void **buf, loff_t *size,
+			       loff_t max_size, enum kernel_read_file_id id)
+{
+	struct stat st;
+	const char *cur_path = path, *basename;
+	char tmp_path[256];
+	int fd;
+
+	if (digest_list_path) {
+		basename = rindex(path, '/');
+		snprintf(tmp_path, sizeof(tmp_path), "%s/%s",
+			 digest_list_path, basename ? basename : path);
+		cur_path = tmp_path;
+	}
+
+	fd = open(cur_path, O_RDONLY);
+	if (fd < 0)
+		return -EINVAL;
+
+	stat(cur_path, &st);
+	*buf = mmap(NULL, st.st_size, PROT_READ | PROT_WRITE, MAP_PRIVATE,
+		    fd, 0);
+	if (*buf == NULL)
+		return -ENOMEM;
+
+	*size = st.st_size;
+	return fd;
+}
diff --git a/lib/metadata.c b/lib/metadata.c
new file mode 100644
index 0000000..74618cf
--- /dev/null
+++ b/lib/metadata.c
@@ -0,0 +1,125 @@
+/*
+ * Copyright (C) 2017 Huawei Technologies Duesseldorf GmbH
+ *
+ * Author: Roberto Sassu <roberto.sassu@huawei.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, version 2 of the
+ * License.
+ *
+ * File: metadata.c
+ *      Writes digest list metadata.
+ */
+
+#include <stdio.h>
+#include <fcntl.h>
+
+#include "metadata.h"
+
+static int ima_add_list_metadata(char *metadata_filename, u16 data_algo,
+				 u32 digest_len, u8 *digest, u32 signature_len,
+				 u8 *signature, u32 file_path_len,
+				 char *file_path, u16 data_type)
+{
+	struct ima_field_data entry_data[DATA__LAST] = {
+		[DATA_ALGO] = {.len = sizeof(u16)},
+		[DATA_TYPE] = {.len = sizeof(u16)},
+	};
+
+	DECLARE_BITMAP(data_mask, DATA__LAST);
+	int ret, metadata_len, fd;
+	u8 *data;
+
+	bitmap_zero(data_mask, DATA__LAST);
+	bitmap_set(data_mask, DATA_ALGO, 1);
+	bitmap_set(data_mask, DATA_TYPE, 1);
+
+	entry_data[DATA_ALGO].data = (u8 *)&data_algo;
+	entry_data[DATA_DIGEST].len = digest_len;
+	entry_data[DATA_DIGEST].data = digest;
+	entry_data[DATA_SIGNATURE].len = signature_len;
+	entry_data[DATA_SIGNATURE].data = signature;
+	entry_data[DATA_FILE_PATH].len = file_path_len;
+	entry_data[DATA_FILE_PATH].data = (unsigned char *)file_path;
+	entry_data[DATA_REF_ID].len = 0;
+	entry_data[DATA_TYPE].data = (u8 *)&data_type;
+
+	metadata_len = ima_get_buflen(DATA__LAST, entry_data, data_mask);
+	data = malloc(metadata_len);
+	if (data == NULL) {
+		printf("Out of memory\n");
+		return -ENOMEM;
+	}
+
+	ret = ima_write_buf(data, data + metadata_len, NULL,
+			    DATA__LAST, entry_data, NULL, data_mask,
+			    ENFORCE_FIELDS | ENFORCE_BUFEND, "entry data");
+	if (ret < 0)
+		goto out;
+
+	fd = open(metadata_filename, O_WRONLY | O_APPEND);
+	if (fd < 0)
+		goto out;
+
+	metadata_len = cpu_to_le32(metadata_len);
+	write(fd, &metadata_len, sizeof(u32));
+	write(fd, data, metadata_len);
+	close(fd);
+out:
+	free(data);
+	return ret;
+}
+
+int write_digests_and_metadata(Header hdr, char *outdir,
+			       char *metadata_filename,
+			       enum input_formats input_fmt,
+			       char *input_filename,
+			       enum digest_data_types output_fmt,
+			       int is_mutable)
+{
+	int ret;
+	char digest_list_filename[MAX_FILENAME_LENGTH];
+	int digest_len = hash_digest_size[ima_hash_algo];
+	u16 data_algo = cpu_to_le16(ima_hash_algo);
+	u16 data_type = cpu_to_le16(output_fmt);
+	u8 digest[digest_len];
+	unsigned int signature_len = 0;
+	u8 *signature;
+
+	if (input_fmt == INPUT_FMT_DIGEST_LIST_ASCII)
+		ret = compact_list_from_digest_list_ascii(input_filename,
+							  outdir,
+							  digest_list_filename,
+							  is_mutable);
+	else if (output_fmt == DATA_TYPE_COMPACT_LIST)
+		ret = compact_list_from_rpm(hdr, outdir, digest_list_filename);
+	else if (output_fmt == DATA_TYPE_RPM)
+		ret = write_rpm_header(hdr, outdir, digest_list_filename);
+
+	if (ret < 0) {
+		if (ret == -ENOENT)
+			return 0;
+
+		printf("Failed to write digest list, ret: %d\n", ret);
+		return ret;
+	}
+
+	ret = calc_file_digest(digest_list_filename, digest, ima_hash_algo);
+	if (ret < 0) {
+		printf("Failed to calculate metadata digest, ret: %d\n", ret);
+		return ret;
+	}
+
+	if (output_fmt == DATA_TYPE_RPM)
+		get_rpm_header_signature(hdr, &signature, &signature_len);
+
+	ret = ima_add_list_metadata(metadata_filename, data_algo,
+				    digest_len, digest, signature_len,
+				    signature, strlen(digest_list_filename) + 1,
+				    digest_list_filename, data_type);
+	if (ret < 0)
+		printf("Failed to write metadata, ret: %d\n", ret);
+
+	return ret;
+}
diff --git a/lib/rpm.c b/lib/rpm.c
new file mode 100644
index 0000000..f717b3a
--- /dev/null
+++ b/lib/rpm.c
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2017 Huawei Technologies Duesseldorf GmbH
+ *
+ * Author: Roberto Sassu <roberto.sassu@huawei.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, version 2 of the
+ * License.
+ *
+ * File: rpm.c
+ *      Writes RPM digest lists.
+ */
+
+#include <stdio.h>
+#include <fcntl.h>
+
+#include "rpm.h"
+
+static int algo_mapping[HASH_ALGO__LAST] = {
+	[PGPHASHALGO_SHA1] = HASH_ALGO_SHA1,
+	[PGPHASHALGO_SHA256] = HASH_ALGO_SHA256,
+};
+
+void get_rpm_filename(Header rpm, char *outdir, char *output_filename,
+		      enum digest_data_types output_fmt)
+{
+	char *pkg_name, *pkg_version, *pkg_release, *pkg_arch;
+	char *prefix = (output_fmt == DATA_TYPE_RPM) ? "rpm" : "compact";
+
+	headerGetEntry(rpm, RPMTAG_NAME, NULL, (void **)&pkg_name, NULL);
+	headerGetEntry(rpm, RPMTAG_VERSION, NULL, (void **)&pkg_version, NULL);
+	headerGetEntry(rpm, RPMTAG_RELEASE, NULL, (void **)&pkg_release, NULL);
+	headerGetEntry(rpm, RPMTAG_ARCH, NULL, (void **)&pkg_arch, NULL);
+
+	snprintf(output_filename, MAX_FILENAME_LENGTH, "%s/%s-%s-%s-%s.%s",
+		 outdir, prefix, pkg_name, pkg_version, pkg_release, pkg_arch);
+}
+
+int check_rpm_digest_algo(Header rpm, char *output_filename)
+{
+	u32 *rpm_digestalgo;
+	rpm_tagtype_t data_type;
+	rpm_count_t data_count;
+	int ret;
+
+	ret = headerGetEntry(rpm, RPMTAG_FILEDIGESTALGO, &data_type,
+			     (void **)&rpm_digestalgo, &data_count);
+	if (ret < 0) {
+		printf("%s: unable to retrieve digest algorithm\n",
+		       output_filename);
+		return -EINVAL;
+	}
+
+	if (strstr(output_filename, "gpg-pubkey") != NULL)
+		return -ENOENT;
+
+	if (algo_mapping[*rpm_digestalgo] != ima_hash_algo) {
+		printf("%s: digest algorithm mismatch, expected: %s, "
+		       "current: %s\n", output_filename,
+		       hash_algo_name[ima_hash_algo],
+		       hash_algo_name[algo_mapping[*rpm_digestalgo]]);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+void get_rpm_header_signature(Header rpm, u8 **signature,
+			      rpm_count_t *signature_len)
+{
+	headerGetEntry(rpm, RPMTAG_RSAHEADER, NULL, (void **)signature,
+		       signature_len);
+}
+
+int write_rpm_header(Header rpm, char *outdir, char *output_filename)
+{
+	char **data;
+	rpm_count_t data_size;
+	int ret, fd;
+
+	get_rpm_filename(rpm, outdir, output_filename, DATA_TYPE_RPM);
+
+	ret = check_rpm_digest_algo(rpm, output_filename);
+	if (ret < 0)
+		return ret;
+
+	headerGetEntry(rpm, RPMTAG_HEADERIMMUTABLE, NULL,
+		       (void **)&data, &data_size);
+
+	fd = open(output_filename, O_WRONLY | O_CREAT | O_TRUNC, 0600);
+	if (fd < 0)
+		return -EACCES;
+
+	write(fd, rpm_header_magic, sizeof(rpm_header_magic));
+	write(fd, data, data_size);
+	close(fd);
+	return 0;
+}
-- 
2.11.0

--
To unsubscribe from this list: send the line "unsubscribe linux-security-module" in
the body of a message to majordomo at vger.kernel.org
More majordomo info@ http://vger.kernel.org/majordomo-info.html

^ permalink raw reply related	[flat|nested] 5+ messages in thread

* [USER SPACE][RFC][PATCH 3/5] digest-list-tools: tools
  2017-11-15 13:39 [USER SPACE][RFC][PATCH 0/5] ima: digest-list-tools package Roberto Sassu
  2017-11-15 13:39 ` [USER SPACE][RFC][PATCH 1/5] digest-list-tools: headers Roberto Sassu
  2017-11-15 13:39 ` [USER SPACE][RFC][PATCH 2/5] digest-list-tools: library Roberto Sassu
@ 2017-11-15 13:39 ` Roberto Sassu
  2017-11-15 13:39 ` [USER SPACE][RFC][PATCH 4/5] digest-list-tools: documentation Roberto Sassu
  3 siblings, 0 replies; 5+ messages in thread
From: Roberto Sassu @ 2017-11-15 13:39 UTC (permalink / raw)
  To: linux-security-module

This patch adds the tools necessary to generate/verify digest lists and
metadata.

Signed-off-by: Roberto Sassu <roberto.sassu@huawei.com>
---
 scripts/setup_ima_digest_list | 116 ++++++++++++++++++++
 src/gen_digest_lists.c        | 240 ++++++++++++++++++++++++++++++++++++++++++
 src/verify_digest_lists.c     | 135 ++++++++++++++++++++++++
 3 files changed, 491 insertions(+)
 create mode 100644 scripts/setup_ima_digest_list
 create mode 100644 src/gen_digest_lists.c
 create mode 100644 src/verify_digest_lists.c

diff --git a/scripts/setup_ima_digest_list b/scripts/setup_ima_digest_list
new file mode 100644
index 0000000..38953f6
--- /dev/null
+++ b/scripts/setup_ima_digest_list
@@ -0,0 +1,116 @@
+#! /bin/bash
+
+# Copyright (C) 2017 Huawei Technologies Duesseldorf GmbH
+#
+# Author: Roberto Sassu <roberto.sassu@huawei.com>
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License as
+# published by the Free Software Foundation, version 2 of the
+# License.
+#
+# File: setup_ima_digest_list
+#      Configure digest lists
+
+set -f
+
+function usage() {
+    echo "Usage: $0 initial|immutable|mutable [options]"
+    echo "Options:"
+    echo -e "\t-d <directory>: directory where digest lists and metadata are stored"
+    echo -e "\t-e <algorithm>: digest algorithm"
+    echo -e "\t-a: append metadata"
+}
+
+if [ "$1" != "initial" ] && [ "$1" != "immutable" ] && [ "$1" != "mutable" ]; then
+    usage
+    exit 1
+fi
+
+OPTIND=2
+digest_lists_dir="/etc/ima/digest_lists"
+algorithm="sha256"
+gen_digest_lists_result=0
+
+while getopts "h?d:e:a" opt; do
+    case "$opt" in
+    h|\?)
+        usage
+        exit 0
+        ;;
+    d)  digest_lists_dir=$OPTARG
+        ;;
+    e)  algorithm=$OPTARG
+        ;;
+    a)  gen_digest_lists_opt="-a"
+        ;;
+    esac
+done
+
+if [ -z "$gen_digest_lists_opt" ] && [ -d "$digest_lists_dir" ]; then
+    ls_output=$(ls $digest_lists_dir)
+    if [ -n "$ls_output" ]; then
+        echo "$digest_lists_dir not empty, files will be overwritten. Do you want to continue? [y/N]"
+        read answer
+
+        if [ "$answer" != "y" ]; then
+            echo "Exiting."
+            exit 0
+        fi
+    fi
+else
+    mkdir -p $digest_lists_dir
+fi
+
+if [ "$1" = "initial" ]; then
+    # generate digest lists from RPM database
+    echo "Generate initial digest list from RPM database"
+    gen_digest_lists $gen_digest_lists_opt -e $algorithm -d $digest_lists_dir -o rpm
+    gen_digest_lists_result=$?
+elif [ "$1" = "immutable" ]; then
+    filename="$digest_lists_dir/unknown_digests_immutable"
+    find_opt="! -path /var/* ! -path /boot/*"
+    awk_opt='$5 !~ /^\/var/'
+elif [ "$1" = "mutable" ]; then
+    # required if root filesystem is mounted as read-only
+    mount -t tmpfs none /var/tmp
+    cp -a /etc/ima/digest_lists /var/tmp
+    mount -t tmpfs none /etc/ima/digest_lists
+    cp -a /var/tmp/digest_lists /etc/ima
+
+    filename="/etc/ima/digest_lists/unknown_digests_mutable"
+    gen_digest_lists_opt="$gen_digest_lists_opt -w"
+    awk_opt='{print $0}'
+fi
+
+if [ -n "$filename" ]; then
+    # find unknown files in the root filesystem
+    echo "Read files from / and /boot"
+    find / /boot -xdev -type f -uid 0 $find_opt -exec head -c0 \{} \;
+
+    # create an ASCII file containing the digests of unknown measurements
+    echo "Create $filename with digests of unknown files"
+    cat /sys/kernel/security/ima/ascii_runtime_measurements | awk "$awk_opt" | \
+        awk '$4 != "sha1:0000000000000000000000000000000000000000" {print $4, $5}' > $filename
+
+    # edit the list of unknown digests
+    vi $filename
+
+    # create a digest list with the digest of immutable or mutable files
+    echo "Generate compact list from $filename"
+    gen_digest_lists -e $algorithm -d /etc/ima/digest_lists -f ascii -i $filename $gen_digest_lists_opt
+    gen_digest_lists_result=$?
+fi
+
+if [ $gen_digest_lists_result -eq 0 ]; then
+    # update initial ram disk
+    echo "Update initial ram disk"
+    dracut -f -i /etc/ima /etc/ima
+fi
+
+if [ "$1" = "mutable" ]; then
+    umount /var/tmp
+    umount /etc/ima/digest_lists
+fi
+
+set +f
diff --git a/src/gen_digest_lists.c b/src/gen_digest_lists.c
new file mode 100644
index 0000000..d212942
--- /dev/null
+++ b/src/gen_digest_lists.c
@@ -0,0 +1,240 @@
+/*
+ * Copyright (C) 2017 Huawei Technologies Duesseldorf GmbH
+ *
+ * Author: Roberto Sassu <roberto.sassu@huawei.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, version 2 of the
+ * License.
+ *
+ * File: gen_digest_lists.c
+ *      Handles command line options and retrieve digests.
+ */
+
+#include <stdio.h>
+#include <fcntl.h>
+
+#include "metadata.h"
+
+static int digest_list_from_rpmdb(char *outdir, char *metadata_filename,
+				  enum digest_data_types output_fmt)
+{
+	rpmts ts = NULL;
+	Header hdr;
+	rpmdbMatchIterator mi;
+	int ret;
+
+	ts = rpmtsCreate();
+	ret = rpmReadConfigFiles(NULL, NULL);
+	if (ret != RPMRC_OK) {
+		rpmlog(RPMLOG_NOTICE, "Unable to read RPM configuration.\n");
+		exit(1);
+	}
+
+	mi = rpmtsInitIterator(ts, RPMDBI_PACKAGES, NULL, 0);
+	while ((hdr = rpmdbNextIterator(mi)) != NULL) {
+		hdr = headerLink(hdr);
+
+		ret = write_digests_and_metadata(hdr, outdir, metadata_filename,
+						 INPUT_FMT_RPMDB, NULL,
+						 output_fmt, 0);
+		if (ret < 0)
+			break;
+
+		headerFree(hdr);
+	}
+
+	rpmdbFreeIterator(mi);
+	rpmtsFree(ts);
+	return ret;
+}
+
+int digest_lists_from_rpmpkg(char *outdir, char *metadata_filename,
+			     char *package_path,
+			     enum digest_data_types output_fmt)
+{
+	Header hdr;
+	rpmts ts = NULL;
+	FD_t fd;
+	int ret;
+
+	fd = Fopen(package_path, "r.ufdio");
+	if ((!fd) || Ferror(fd)) {
+		rpmlog(RPMLOG_NOTICE, "Failed to open package file (%s)\n",
+		       Fstrerror(fd));
+		if (fd)
+			Fclose(fd);
+
+		return -EINVAL;
+	}
+
+	ret = rpmReadPackageFile(ts, fd, package_path, &hdr);
+	if (ret != RPMRC_OK) {
+		rpmlog(RPMLOG_NOTICE, "Could not read package file\n");
+			Fclose(fd);
+			exit(1);
+	}
+
+	Fclose(fd);
+	ret = write_digests_and_metadata(hdr, outdir, metadata_filename,
+					 INPUT_FMT_RPMPKG, NULL, output_fmt, 0);
+	rpmtsFree(ts);
+	return ret;
+}
+
+int write_digest_lists(char *outdir, char *metadata_filename,
+		       int add_metadata, enum input_formats input_fmt,
+		       char *input_filename, enum digest_data_types output_fmt,
+		       int is_mutable)
+{
+	char filename[MAX_FILENAME_LENGTH];
+	int ret = 0, fd;
+
+	snprintf(filename, sizeof(filename), "%s/%s", outdir,
+		 metadata_filename);
+
+	fd = open(filename, O_WRONLY | O_CREAT, 0600);
+	if (fd < 0) {
+		printf("Unable to write metadata file %s\n", filename);
+		return -EACCES;
+	}
+
+	if (!add_metadata)
+		ftruncate(fd, 0);
+
+	switch (input_fmt) {
+	case INPUT_FMT_RPMDB:
+		ret = digest_list_from_rpmdb(outdir, filename, output_fmt);
+		break;
+	case INPUT_FMT_RPMPKG:
+		ret = digest_lists_from_rpmpkg(outdir, filename, input_filename,
+					       output_fmt);
+		break;
+	case INPUT_FMT_DIGEST_LIST_ASCII:
+		ret = write_digests_and_metadata(NULL, outdir, filename,
+						 INPUT_FMT_DIGEST_LIST_ASCII,
+						 input_filename, output_fmt,
+						 is_mutable);
+		break;
+	default:
+		ret = -EINVAL;
+		break;
+	}
+
+	return ret;
+}
+
+void usage(char *progname)
+{
+	printf("Usage: %s <options>\n", progname);
+	printf("Options:\n");
+	printf("\t-a: append metadata to an existing file\n"
+	       "\t-d <directory>: directory where digest lists and metadata "
+	       "are stored\n"
+	       "\t-f <input format>: format of the input where digests "
+	       "are taken from\n"
+	       "\t\trpmdb: RPM database\n"
+	       "\t\trpmpkg: RPM package\n"
+	       "\t\tascii: file containing ASCII digests for each line\n"
+	       "\t-h: display help\n"
+	       "\t-i <path>: path of the file where digests are taken from\n"
+	       "\t-m <file name>: metadata file name\n"
+	       "\t-o <output format>: output format of the digest list\n"
+	       "\t\tcompact: compact digest list\n"
+	       "\t\trpm: RPM package header\n"
+	       "\t-w: files are mutable\n"
+	       "\t-e <algorithm>: digest algorithm\n");
+}
+
+int main(int argc, char **argv)
+{
+	int add_metadata = 0, is_mutable = 0;
+	char *input_filename = NULL, *metadata_filename = "metadata";
+	char *outdir = NULL;
+	enum input_formats input_fmt = INPUT_FMT_RPMDB;
+	enum digest_data_types output_fmt = DATA_TYPE_COMPACT_LIST;
+	int c, ret;
+
+	while ((c = getopt(argc, argv, "ad:f:i:m:o:hwe:")) != -1) {
+		switch (c) {
+		case 'a':
+			add_metadata = 1;
+			break;
+		case 'd':
+			outdir = optarg;
+			break;
+		case 'f':
+			if (strcmp(optarg, "rpmdb") == 0) {
+				input_fmt = INPUT_FMT_RPMDB;
+			} else if (strcmp(optarg, "rpmpkg") == 0) {
+				input_fmt = INPUT_FMT_RPMPKG;
+			} else if (strcmp(optarg, "ascii") == 0) {
+				input_fmt = INPUT_FMT_DIGEST_LIST_ASCII;
+			} else {
+				printf("Unknown input format %s\n", optarg);
+				return -EINVAL;
+			}
+			break;
+		case 'h':
+			usage(argv[0]);
+			return -EINVAL;
+		case 'i':
+			input_filename = optarg;
+			break;
+		case 'm':
+			metadata_filename = optarg;
+			break;
+		case 'o':
+			if (strcmp(optarg, "compact") == 0) {
+				output_fmt = DATA_TYPE_COMPACT_LIST;
+			} else if (strcmp(optarg, "rpm") == 0) {
+				output_fmt = DATA_TYPE_RPM;
+			} else {
+				printf("Unknown output format %s\n", optarg);
+				return -EINVAL;
+			}
+			break;
+		case 'w':
+			is_mutable = 1;
+			break;
+		case 'e':
+			if (ima_hash_setup(optarg)) {
+				printf("Unknown algorithm %s\n", optarg);
+				return -EINVAL;
+			}
+			break;
+		default:
+			printf("Unknown option %c\n", optopt);
+			return -EINVAL;
+		}
+	}
+
+	if (input_fmt != INPUT_FMT_RPMDB && input_filename == NULL) {
+		printf("Input file not specified\n");
+		return -EINVAL;
+	}
+
+	if (input_fmt == INPUT_FMT_RPMDB && input_filename != NULL) {
+		printf("Input file format not specified\n");
+		return -EINVAL;
+	}
+
+	if (outdir == NULL) {
+		printf("Output directory not specified\n");
+		return -EINVAL;
+	}
+
+	if (outdir[0] != '/') {
+		printf("Absolute path of output directory must be specified\n");
+		return -EINVAL;
+	}
+
+	OpenSSL_add_all_digests();
+
+	ret = write_digest_lists(outdir, metadata_filename, add_metadata,
+				 input_fmt, input_filename, output_fmt,
+				 is_mutable);
+	EVP_cleanup();
+	return ret;
+}
diff --git a/src/verify_digest_lists.c b/src/verify_digest_lists.c
new file mode 100644
index 0000000..dfe7162
--- /dev/null
+++ b/src/verify_digest_lists.c
@@ -0,0 +1,135 @@
+/*
+ * Copyright (C) 2017 Huawei Technologies Duesseldorf GmbH
+ *
+ * Author: Roberto Sassu <roberto.sassu@huawei.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation, version 2 of the
+ * License.
+ *
+ * File: verify_digest_lists.c
+ *      Verify digest list metadata and digest lists
+ */
+
+#include <stdio.h>
+#include <unistd.h>
+#include <sys/mman.h>
+
+#include "kernel_ima.h"
+#include "lib.h"
+
+int verify_list_metadata(char *path, u8 *digest, int *num_digest_lists,
+			 int *num_digests)
+{
+	int digest_len = hash_digest_size[ima_hash_algo];
+	u8 metadata_digest[digest_len];
+	void *data, *datap;
+	loff_t size, mmap_size, cur_size = 0;
+	int digest_lists = 0;
+	int ret, fd;
+
+	fd = kernel_read_file_from_path(path, &data, &size, 0,
+					READING_DIGEST_LIST_METADATA);
+	if (fd < 0) {
+		pr_err("Unable to read: %s (%d)\n", path, fd);
+		return fd;
+	}
+
+	mmap_size = size;
+
+	ret = calc_digest(metadata_digest, data, size, ima_hash_algo);
+	if (ret < 0)
+		goto out;
+
+	if (memcmp(metadata_digest, digest, digest_len) != 0) {
+		pr_err("%s: integrity check failed\n", path);
+		ret = -EINVAL;
+		goto out;
+	}
+
+	datap = data;
+	while (size > 0) {
+		cur_size = ima_parse_digest_list_metadata(size, datap);
+		if (cur_size < 0) {
+			ret = -EINVAL;
+			goto out;
+		}
+
+		size -= cur_size;
+		datap += cur_size;
+		digest_lists++;
+	}
+
+	*num_digest_lists = digest_lists;
+	*num_digests = digests;
+out:
+	munmap(data, mmap_size);
+	return ret;
+}
+
+void usage(char *progname)
+{
+	printf("Usage: %s <options>\n", progname);
+	printf("Options:\n");
+	printf("\t-d: directory containing metadata and digest lists\n"
+	       "\t-m <file name>: metadata file name\n"
+	       "\t-i <digest>: expected digest of metadata\n"
+	       "\t-h: display help\n"
+	       "\t-e <algorithm>: digest algorithm\n");
+}
+
+int main(int argc, char *argv[])
+{
+	int c, digest_len, num_digest_lists, num_digests, ret = -EINVAL;
+	u8 input_digest[SHA512_DIGEST_SIZE];
+	char *digest_ptr = NULL, *cur_dir = "./";
+	char *metadata_filename = "metadata";
+
+	while ((c = getopt(argc, argv, "d:m:i:he:")) != -1) {
+		switch (c) {
+		case 'd':
+			cur_dir = optarg;
+			break;
+		case 'm':
+			metadata_filename = optarg;
+			break;
+		case 'i':
+			digest_ptr = optarg;
+			break;
+		case 'h':
+			usage(argv[0]);
+			return -EINVAL;
+		case 'e':
+			if (ima_hash_setup(optarg)) {
+				printf("Unknown algorithm %s\n", optarg);
+				return -EINVAL;
+			}
+			break;
+		default:
+			printf("Unknown option %c\n", optopt);
+			return -EINVAL;
+		}
+	}
+
+	if (digest_ptr == NULL) {
+		printf("Expected metadata digest not specified\n");
+		return -EINVAL;
+	}
+
+	digest_list_path = cur_dir;
+
+	OpenSSL_add_all_digests();
+
+	digest_len = hash_digest_size[ima_hash_algo];
+	hex2bin(input_digest, digest_ptr, digest_len);
+
+	ret = verify_list_metadata(metadata_filename, input_digest,
+				   &num_digest_lists, &num_digests);
+	if (ret == 0)
+		printf("num_digest_lists: %d, num_digests: %d\n",
+		       num_digest_lists, num_digests);
+
+	EVP_cleanup();
+	return ret;
+}
-- 
2.11.0

--
To unsubscribe from this list: send the line "unsubscribe linux-security-module" in
the body of a message to majordomo at vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply related	[flat|nested] 5+ messages in thread

* [USER SPACE][RFC][PATCH 4/5] digest-list-tools: documentation
  2017-11-15 13:39 [USER SPACE][RFC][PATCH 0/5] ima: digest-list-tools package Roberto Sassu
                   ` (2 preceding siblings ...)
  2017-11-15 13:39 ` [USER SPACE][RFC][PATCH 3/5] digest-list-tools: tools Roberto Sassu
@ 2017-11-15 13:39 ` Roberto Sassu
  3 siblings, 0 replies; 5+ messages in thread
From: Roberto Sassu @ 2017-11-15 13:39 UTC (permalink / raw)
  To: linux-security-module

This patch adds the documentation of the digest-list-tools package.

Signed-off-by: Roberto Sassu <roberto.sassu@huawei.com>
---
 README                         | 143 +++++++++++++++++++++++++++++++++++++++++
 docs/gen_digest_lists.txt      |  87 +++++++++++++++++++++++++
 docs/setup_ima_digest_list.txt |  51 +++++++++++++++
 docs/verify_digest_lists.txt   |  52 +++++++++++++++
 4 files changed, 333 insertions(+)
 create mode 100644 README
 create mode 100644 docs/gen_digest_lists.txt
 create mode 100644 docs/setup_ima_digest_list.txt
 create mode 100644 docs/verify_digest_lists.txt

diff --git a/README b/README
new file mode 100644
index 0000000..e0a2a34
--- /dev/null
+++ b/README
@@ -0,0 +1,143 @@
+======
+README
+======
+
+IMA Boot-time Configuration
+===========================
+
+This section explains how to generate digest lists and how to include them into
+an initial ram disk, so that generated digest lists are loaded early in the boot
+process.
+
+Measurement
+-----------
+
+Follow these steps to create digest lists from installed packages:
+
+1) execute setup_ima_digest_list
+
+# setup_ima_digest_list initial -e <value of ima_hash kernel parameter>
+
+This command creates a file containing the RPM header for each installed package
+and writes them to /etc/ima/digest_lists. Metadata are saved to
+/etc/ima/digest_lists/metadata. If the RPM database is not available, digest
+lists can be generated directly from the measurement list. Follow the steps
+below, for generating the digest list of immutable and mutable files (the -a
+option of setup_ima_digest_list can be removed for immutable files).
+
+2) edit the bootloader configuration file and add 'ima_policy=tcb' to the kernel
+   command line
+
+3) reboot
+
+After reboot, the measurement list will contain only unknown files. It is
+possible to create the initial digest list without the RPM database with the
+steps below. The initial digest list will be created from the measurement list.
+
+
+Follow these steps to add an additional digest list with digests of immutable
+files:
+
+1) execute setup_ima_digest_list
+
+# setup_ima_digest_list immutable -a -e <value of ima_hash kernel parameter>
+
+This command reads files from the root filesystem, so that IMA creates a new
+measurement entry if the calculate digest is not included in the currently
+loaded digest lists. Afterwards, the vi editor is automatically opened so that
+users can select the digests to include in the new digest list. The new list
+will be saved to /etc/ima/digest_lists/compact-unknown_digests_immutable, and a
+new record will be added to /etc/ima/digest_lists/metadata.
+
+2) reboot
+
+
+
+Appraisal
+---------
+
+To enable appraisal with digest list, it is necessary to mount the filesystem
+as read-only, to calculate the correct digest of mutable files. Otherwise, if
+those files are written before reboot, IMA will deny access to them, because the
+new digest will not be found.
+
+1) copy /etc/fstab to /etc/fstab.orig
+
+2) edit /etc/fstab and add 'ro' mount option for the root filesystem
+
+3) remove security.ima and security.evm extended attributes
+
+# find / -xdev -uid 0 -type f -exec attr -S -r ima 2> /dev/null \{} \;
+
+# find / -xdev -uid 0 -type f -exec attr -S -r evm 2> /dev/null \{} \;
+
+4) reboot
+
+5) execute setup_ima_digest_list
+
+# setup_ima_digest_list mutable -a -e <value of ima_hash kernel parameter>
+
+This command reads files in / and /boot. Since the initial and immutable digest
+lists have been loaded, IMA creates a new measurement entry for each mutable
+file. The user can select the digests after the vi editor is opened, and a new
+digest list is saved to /etc/ima/digest_lists/compact-unknown_digests_mutable.
+/etc/ima/digest_lists/metadata is also updated.
+
+6) edit the bootloader configuration file and add 'ima_policy=appraise_tcb
+   evm_xattrs=security.ima' to the kernel command line
+
+7) reboot
+
+8) remount the root filesystem as read-write
+
+# mount -o remount,rw /
+
+9) restore the original /etc/fstab
+
+10) reboot
+
+'evm_xattrs=security.ima' should be used only if no additional metadata should
+be protected by EVM. If it has been specified, security.ima will be created only
+for mutable files. Otherwise, they will be created also for immutable files.
+
+
+
+
+IMA Run-time Configuration
+==========================
+
+This section explains how to generate additional digest lists and how to upload
+them to IMA, when the system is running.
+
+Generation
+----------
+
+Digest lists can be generated with the gen_digest_lists tool. A description of
+this command can be found in docs/gen_digest_lists.txt.
+
+
+
+Upload
+------
+
+After digest lists have been generated, they can be uploaded by writing the path
+of digest list metadata to /sys/kernel/security/ima/digest_lists. For example:
+
+echo "/etc/ima/digest_lists/metadata" > /sys/kernel/security/ima/digest_lists
+
+
+
+Digest List Integrity Verification
+==================================
+
+The measurement list, after loading the digest lists will look like:
+
+10 <template digest> ima-ng sha1:<digest> boot_aggregate
+10 <template digest> ima-ng sha1:<digest> /etc/ima/digest_lists/metadata
+<measurement entries for modified mutable files>
+
+An attestation server can use the verify_digest_lists tool to verify the
+integrity of metadata and digest lists. For example, it can execute:
+
+$ verify_digest_lists -d /etc/ima/digest_lists -m metadata -e sha256 \
+                      -i <metadata digest>
diff --git a/docs/gen_digest_lists.txt b/docs/gen_digest_lists.txt
new file mode 100644
index 0000000..1d3c39e
--- /dev/null
+++ b/docs/gen_digest_lists.txt
@@ -0,0 +1,87 @@
+gen_digest_lists
+================
+
+gen_digest_lists
+----------------
+
+gen_digest_lists - generate a digest list
+
+
+SYNOPSIS
+--------
+
+gen_digest_lists [options]
+
+
+DESCRIPTION
+-----------
+
+gen_digest_lists can be used to generate digest lists from the RPM database,
+from an RPM package, or from a list of digests in ASCII format.
+
+
+OPTIONS
+-------
+
+-a: append metadata to an existing file
+-d <directory>: directory where digest lists and metadata are stored
+-f <input format>: format of the input where digests are taken from
+                   - rpmdb: RPM database (default)
+                   - rpmpkg: RPM package
+                   - ascii: ASCII file with format <algo>:<digest> for each line
+-h: display help
+-i <path>: path of the file where digests are taken from
+-m <file name>: metadata file name (default: metadata)
+-o <output format>: output format of the digest list
+                    - compact: compact digest list (default)
+                    - rpm: RPM package header
+-w: files are mutable
+-e <algorithm>: digest algorithm
+
+
+EXAMPLES
+--------
+
+Generate an ASCII digest list for each installed RPM package and copy it to
+/etc/ima/digest_lists.
+
+$ gen_digest_lists -d /etc/ima/digest_lists
+
+Generate an RPM digest list for each installed RPM package and copy it to
+/etc/ima/digest_lists.
+
+$ gen_digest_lists -d /etc/ima/digest_lists -o rpm
+
+Generate an RPM digest list from an RPM package and copy it to
+/etc/ima/digest_lists.
+
+$ gen_digest_lists -d /etc/ima/digest_lists -f rpmpkg -i <RPM package>
+
+Generate an ASCII digest list from a file containing digests of immutable files
+and copy it to /etc/ima/digest_lists.
+
+$ gen_digest_lists -d /etc/ima/digest_lists -f ascii -i <ASCII file>
+
+Generate an ASCII digest list from a file containing digests of mutable files
+and copy it to /etc/ima/digest_lists.
+
+$ gen_digest_lists -d /etc/ima/digest_lists -f ascii -i <ASCII file> -w
+
+Generate an ASCII digest list from a file containing digests of mutable files
+and copy it to /etc/ima/digest_lists. Append digest list metadata to
+/etc/ima/digest_lists/metadata.
+
+$ gen_digest_lists -d /etc/ima/digest_lists -f ascii -i <ASCII file> -w -a
+
+
+AUTHOR
+------
+
+Written by Roberto Sassu, <roberto.sassu@huawei.com>.
+
+
+COPYING
+-------
+
+Copyright (C) 2017 Huawei Technologies Duesseldorf GmbH. Free use of this
+software is granted under the terms of the GNU Public License (GPL).
diff --git a/docs/setup_ima_digest_list.txt b/docs/setup_ima_digest_list.txt
new file mode 100644
index 0000000..cec17a2
--- /dev/null
+++ b/docs/setup_ima_digest_list.txt
@@ -0,0 +1,51 @@
+setup_ima_digest_list
+=====================
+
+setup_ima_digest_list
+---------------------
+
+setup_ima_digest_list - generate digest lists for measurement and appraisal
+
+
+SYNOPSIS
+--------
+
+setup_ima_digest_list initial|immutable|mutable [options]
+
+
+DESCRIPTION
+-----------
+
+setup_ima_digest_list can be used to generate digest lists for measurement and
+appraisal. Digest lists can be generated from the RPM database, or from the
+IMA measurement list.
+
+
+COMMANDS
+--------
+
+initial: generate digest lists from the RPM database
+immutable: generate a digest list of immutable files from the measurement list
+mutable: generate a digest list of mutable files from the measurement list
+
+
+OPTIONS
+-------
+
+-h -?: display help
+-d <directory>: directory where digest lists and metadata are stored
+-e <algorithm>: digest algorithm
+-a: append metadata
+
+
+AUTHOR
+------
+
+Written by Roberto Sassu, <roberto.sassu@huawei.com>.
+
+
+COPYING
+-------
+
+Copyright (C) 2017 Huawei Technologies Duesseldorf GmbH. Free use of this
+software is granted under the terms of the GNU Public License (GPL).
diff --git a/docs/verify_digest_lists.txt b/docs/verify_digest_lists.txt
new file mode 100644
index 0000000..7d5543d
--- /dev/null
+++ b/docs/verify_digest_lists.txt
@@ -0,0 +1,52 @@
+verify_digest_lists
+===================
+
+verify_digest_lists
+-------------------
+
+verify_digest_lists - verify integrity of digest lists metadata and digest lists
+
+
+SYNOPSIS
+--------
+
+verify_digest_lists [options]
+
+
+DESCRIPTION
+-----------
+
+verify_digest_lists can be used to verify the integrity of digest lists.
+
+
+OPTIONS
+-------
+
+-d <directory>: directory where digest lists and metadata are stored
+                (default: current directory)
+-m <file name>: metadata file name (default: metadata)
+-i <digest>: expected metadata digest
+-h: display help
+-e <algorithm>: digest algorithm
+
+
+EXAMPLES
+--------
+
+Verify digest lists metadata and digest list stored in /etc/ima/digest_lists.
+
+$ verify_digest_lists -d /etc/ima/digest_lists -m metadata -e sha256 \
+                      -i <metadata digest>
+
+
+AUTHOR
+------
+
+Written by Roberto Sassu, <roberto.sassu@huawei.com>.
+
+
+COPYING
+-------
+
+Copyright (C) 2017 Huawei Technologies Duesseldorf GmbH. Free use of this
+software is granted under the terms of the GNU Public License (GPL).
-- 
2.11.0

--
To unsubscribe from this list: send the line "unsubscribe linux-security-module" in
the body of a message to majordomo at vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply related	[flat|nested] 5+ messages in thread

end of thread, other threads:[~2017-11-15 13:39 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2017-11-15 13:39 [USER SPACE][RFC][PATCH 0/5] ima: digest-list-tools package Roberto Sassu
2017-11-15 13:39 ` [USER SPACE][RFC][PATCH 1/5] digest-list-tools: headers Roberto Sassu
2017-11-15 13:39 ` [USER SPACE][RFC][PATCH 2/5] digest-list-tools: library Roberto Sassu
2017-11-15 13:39 ` [USER SPACE][RFC][PATCH 3/5] digest-list-tools: tools Roberto Sassu
2017-11-15 13:39 ` [USER SPACE][RFC][PATCH 4/5] digest-list-tools: documentation Roberto Sassu

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).