ima: calculate and verify the serialized IMA measurement list buffer hash
Bit flips and errant DMA transfers happen routinely and can easily
result in a corrupt data structure which may be non-trivial to verify.
The kexec hash, which would normally detect these sorts of changes, is
calculated during the kexec load. Unfortunately, new measurements can
take place between the kexec load and execute, while other measurements
are the result of the kexec execute itself.
Instead of including the hash of the serialized IMA measurement list
buffer as part of the kexec hash, defer calculating and verifying the
serialized IMA measurement list buffer hash to IMA.
This patch calculates a hash of the serialized measurement list
buffer, including the ima_kexec_hdr structure, except for the digest
itself. Before restoring the IMA measurement list, verify the hash.
On hash verification failure, for now, allow the boot to continue
normally without restoring the measurement list.
Signed-off-by: Mimi Zohar <zohar@linux.vnet.ibm.com>
diff --git a/security/integrity/ima/ima_kexec.c b/security/integrity/ima/ima_kexec.c
index 878c062..7fabfc5 100644
--- a/security/integrity/ima/ima_kexec.c
+++ b/security/integrity/ima/ima_kexec.c
@@ -34,6 +34,10 @@
struct ima_queue_entry *qe;
struct seq_file file;
struct ima_kexec_hdr khdr;
+ struct {
+ struct ima_digest_data hdr;
+ char digest[SHA256_DIGEST_SIZE];
+ } hash = {.hdr.algo = HASH_ALGO_SHA256};
int ret = 0;
/* segment size can't change between kexec load and execute */
@@ -64,9 +68,11 @@
/*
* fill in reserved space with some buffer details
- * (eg. version, buffer size, number of measurements)
+ * (eg. buffer hash, version, buffer size, number of measurements)
*/
khdr.buffer_size = file.count;
+ memset(file.buf + file.count, 0, file.size - file.count);
+
if (ima_canonical_fmt) {
khdr.version = cpu_to_le16(khdr.version);
khdr.count = cpu_to_le64(khdr.count);
@@ -74,6 +80,14 @@
}
memcpy(file.buf, &khdr, sizeof(khdr));
+ ret = ima_calc_buffer_hash(file.buf + sizeof(khdr.digest),
+ file.size - sizeof(khdr.digest),
+ &hash.hdr);
+ if (ret < 0)
+ goto out;
+
+ memcpy(file.buf, &hash.digest, sizeof(hash.digest));
+
print_hex_dump(KERN_DEBUG, "ima dump: ", DUMP_PREFIX_NONE,
16, 1, file.buf,
file.count < 100 ? file.count : 100, true);
@@ -81,7 +95,7 @@
*buffer_size = file.count;
*buffer = file.buf;
out:
- if (ret == -EINVAL)
+ if (ret < 0)
vfree(file.buf);
return ret;
}
diff --git a/security/integrity/ima/ima_template.c b/security/integrity/ima/ima_template.c
index 24775f3..97e265b 100644
--- a/security/integrity/ima/ima_template.c
+++ b/security/integrity/ima/ima_template.c
@@ -352,6 +352,11 @@
char template_data[0];
} __packed;
+ struct {
+ struct ima_digest_data hdr;
+ char digest[SHA256_DIGEST_SIZE];
+ } hash = {.hdr.algo = HASH_ALGO_SHA256};
+
struct ima_kexec_hdr *khdr = buf;
struct binary_hdr_v1 *hdr_v1;
struct binary_data_v1 *data_v1;
@@ -366,6 +371,20 @@
if (!buf || size < sizeof(*khdr))
return 0;
+ ret = ima_calc_buffer_hash(buf + sizeof(khdr->digest),
+ size - sizeof(khdr->digest),
+ &hash.hdr);
+ if (ret < 0) {
+ pr_err("verifying measurement list digest failed");
+ return 0;
+ }
+
+ ret = memcmp(buf, &hash.digest, sizeof(khdr->digest));
+ if (ret) {
+ pr_err("verifying measurement list digest failed");
+ return 0;
+ }
+
if (ima_canonical_fmt) {
khdr->version = le16_to_cpu(khdr->version);
khdr->count = le64_to_cpu(khdr->count);
@@ -378,7 +397,7 @@
}
/*
- * ima kexec buffer prefix: version, buffer size, count
+ * ima kexec buffer prefix: buffer hash, version, buffer size, count
* v1 format: pcr, digest, template-name-len, template-name,
* template-data-size, template-data
*/