[USER SPACE][RFC][PATCH 2/5] digest-list-tools: library
Roberto Sassu
roberto.sassu at huawei.com
Wed Nov 15 13:39:23 UTC 2017
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 at 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 at 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 at 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 at samsung.com>
+ * Copyright (C) 2017 Huawei Technologies Duesseldorf GmbH
+ *
+ * Author: Roberto Sassu <roberto.sassu at 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 at 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 at 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 at 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 at http://vger.kernel.org/majordomo-info.html
More information about the Linux-security-module-archive
mailing list