* [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).