diff options
| author | Mimi Zohar <zohar@linux.vnet.ibm.com> | 2016-12-19 19:22:48 -0500 |
|---|---|---|
| committer | Linus Torvalds <torvalds@linux-foundation.org> | 2016-12-20 12:48:44 -0500 |
| commit | 7b8589cc29e7c35dcfd2d5138979f17b48f90110 (patch) | |
| tree | 034068b5b20ee7e55247197c5df5d94e6aeadee6 | |
| parent | ab6b1d1fc4aae6b8bd6fb1422405568094c9b40f (diff) | |
ima: on soft reboot, save the measurement list
The TPM PCRs are only reset on a hard reboot. In order to validate a
TPM's quote after a soft reboot (eg. kexec -e), the IMA measurement
list of the running kernel must be saved and restored on boot.
This patch uses the kexec buffer passing mechanism to pass the
serialized IMA binary_runtime_measurements to the next kernel.
Link: http://lkml.kernel.org/r/1480554346-29071-7-git-send-email-zohar@linux.vnet.ibm.com
Signed-off-by: Thiago Jung Bauermann <bauerman@linux.vnet.ibm.com>
Signed-off-by: Mimi Zohar <zohar@linux.vnet.ibm.com>
Acked-by: "Eric W. Biederman" <ebiederm@xmission.com>
Acked-by: Dmitry Kasatkin <dmitry.kasatkin@gmail.com>
Cc: Andreas Steffen <andreas.steffen@strongswan.org>
Cc: Josh Sklar <sklar@linux.vnet.ibm.com>
Cc: Dave Young <dyoung@redhat.com>
Cc: Vivek Goyal <vgoyal@redhat.com>
Cc: Baoquan He <bhe@redhat.com>
Cc: Michael Ellerman <mpe@ellerman.id.au>
Cc: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Cc: Paul Mackerras <paulus@samba.org>
Cc: Stewart Smith <stewart@linux.vnet.ibm.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
| -rw-r--r-- | include/linux/ima.h | 12 | ||||
| -rw-r--r-- | kernel/kexec_file.c | 4 | ||||
| -rw-r--r-- | security/integrity/ima/ima.h | 1 | ||||
| -rw-r--r-- | security/integrity/ima/ima_fs.c | 2 | ||||
| -rw-r--r-- | security/integrity/ima/ima_kexec.c | 117 |
5 files changed, 135 insertions, 1 deletions
diff --git a/include/linux/ima.h b/include/linux/ima.h index 0eb7c2e7f0d6..7f6952f8d6aa 100644 --- a/include/linux/ima.h +++ b/include/linux/ima.h | |||
| @@ -11,6 +11,7 @@ | |||
| 11 | #define _LINUX_IMA_H | 11 | #define _LINUX_IMA_H |
| 12 | 12 | ||
| 13 | #include <linux/fs.h> | 13 | #include <linux/fs.h> |
| 14 | #include <linux/kexec.h> | ||
| 14 | struct linux_binprm; | 15 | struct linux_binprm; |
| 15 | 16 | ||
| 16 | #ifdef CONFIG_IMA | 17 | #ifdef CONFIG_IMA |
| @@ -23,6 +24,10 @@ extern int ima_post_read_file(struct file *file, void *buf, loff_t size, | |||
| 23 | enum kernel_read_file_id id); | 24 | enum kernel_read_file_id id); |
| 24 | extern void ima_post_path_mknod(struct dentry *dentry); | 25 | extern void ima_post_path_mknod(struct dentry *dentry); |
| 25 | 26 | ||
| 27 | #ifdef CONFIG_IMA_KEXEC | ||
| 28 | extern void ima_add_kexec_buffer(struct kimage *image); | ||
| 29 | #endif | ||
| 30 | |||
| 26 | #else | 31 | #else |
| 27 | static inline int ima_bprm_check(struct linux_binprm *bprm) | 32 | static inline int ima_bprm_check(struct linux_binprm *bprm) |
| 28 | { | 33 | { |
| @@ -62,6 +67,13 @@ static inline void ima_post_path_mknod(struct dentry *dentry) | |||
| 62 | 67 | ||
| 63 | #endif /* CONFIG_IMA */ | 68 | #endif /* CONFIG_IMA */ |
| 64 | 69 | ||
| 70 | #ifndef CONFIG_IMA_KEXEC | ||
| 71 | struct kimage; | ||
| 72 | |||
| 73 | static inline void ima_add_kexec_buffer(struct kimage *image) | ||
| 74 | {} | ||
| 75 | #endif | ||
| 76 | |||
| 65 | #ifdef CONFIG_IMA_APPRAISE | 77 | #ifdef CONFIG_IMA_APPRAISE |
| 66 | extern void ima_inode_post_setattr(struct dentry *dentry); | 78 | extern void ima_inode_post_setattr(struct dentry *dentry); |
| 67 | extern int ima_inode_setxattr(struct dentry *dentry, const char *xattr_name, | 79 | extern int ima_inode_setxattr(struct dentry *dentry, const char *xattr_name, |
diff --git a/kernel/kexec_file.c b/kernel/kexec_file.c index 0c2df7f73792..b56a558e406d 100644 --- a/kernel/kexec_file.c +++ b/kernel/kexec_file.c | |||
| @@ -19,6 +19,7 @@ | |||
| 19 | #include <linux/mutex.h> | 19 | #include <linux/mutex.h> |
| 20 | #include <linux/list.h> | 20 | #include <linux/list.h> |
| 21 | #include <linux/fs.h> | 21 | #include <linux/fs.h> |
| 22 | #include <linux/ima.h> | ||
| 22 | #include <crypto/hash.h> | 23 | #include <crypto/hash.h> |
| 23 | #include <crypto/sha.h> | 24 | #include <crypto/sha.h> |
| 24 | #include <linux/syscalls.h> | 25 | #include <linux/syscalls.h> |
| @@ -132,6 +133,9 @@ kimage_file_prepare_segments(struct kimage *image, int kernel_fd, int initrd_fd, | |||
| 132 | return ret; | 133 | return ret; |
| 133 | image->kernel_buf_len = size; | 134 | image->kernel_buf_len = size; |
| 134 | 135 | ||
| 136 | /* IMA needs to pass the measurement list to the next kernel. */ | ||
| 137 | ima_add_kexec_buffer(image); | ||
| 138 | |||
| 135 | /* Call arch image probe handlers */ | 139 | /* Call arch image probe handlers */ |
| 136 | ret = arch_kexec_kernel_image_probe(image, image->kernel_buf, | 140 | ret = arch_kexec_kernel_image_probe(image, image->kernel_buf, |
| 137 | image->kernel_buf_len); | 141 | image->kernel_buf_len); |
diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h index ea1dcc452911..139dec67dcbf 100644 --- a/security/integrity/ima/ima.h +++ b/security/integrity/ima/ima.h | |||
| @@ -143,6 +143,7 @@ void ima_print_digest(struct seq_file *m, u8 *digest, u32 size); | |||
| 143 | struct ima_template_desc *ima_template_desc_current(void); | 143 | struct ima_template_desc *ima_template_desc_current(void); |
| 144 | int ima_restore_measurement_entry(struct ima_template_entry *entry); | 144 | int ima_restore_measurement_entry(struct ima_template_entry *entry); |
| 145 | int ima_restore_measurement_list(loff_t bufsize, void *buf); | 145 | int ima_restore_measurement_list(loff_t bufsize, void *buf); |
| 146 | int ima_measurements_show(struct seq_file *m, void *v); | ||
| 146 | unsigned long ima_get_binary_runtime_size(void); | 147 | unsigned long ima_get_binary_runtime_size(void); |
| 147 | int ima_init_template(void); | 148 | int ima_init_template(void); |
| 148 | 149 | ||
diff --git a/security/integrity/ima/ima_fs.c b/security/integrity/ima/ima_fs.c index 3df46906492d..10bea0125fa1 100644 --- a/security/integrity/ima/ima_fs.c +++ b/security/integrity/ima/ima_fs.c | |||
| @@ -116,7 +116,7 @@ void ima_putc(struct seq_file *m, void *data, int datalen) | |||
| 116 | * [eventdata length] | 116 | * [eventdata length] |
| 117 | * eventdata[n]=template specific data | 117 | * eventdata[n]=template specific data |
| 118 | */ | 118 | */ |
| 119 | static int ima_measurements_show(struct seq_file *m, void *v) | 119 | int ima_measurements_show(struct seq_file *m, void *v) |
| 120 | { | 120 | { |
| 121 | /* the list never shrinks, so we don't need a lock here */ | 121 | /* the list never shrinks, so we don't need a lock here */ |
| 122 | struct ima_queue_entry *qe = v; | 122 | struct ima_queue_entry *qe = v; |
diff --git a/security/integrity/ima/ima_kexec.c b/security/integrity/ima/ima_kexec.c index 36afd0fe9747..2c4824ac1ce1 100644 --- a/security/integrity/ima/ima_kexec.c +++ b/security/integrity/ima/ima_kexec.c | |||
| @@ -10,8 +10,125 @@ | |||
| 10 | * the Free Software Foundation; either version 2 of the License, or | 10 | * the Free Software Foundation; either version 2 of the License, or |
| 11 | * (at your option) any later version. | 11 | * (at your option) any later version. |
| 12 | */ | 12 | */ |
| 13 | #include <linux/seq_file.h> | ||
| 14 | #include <linux/vmalloc.h> | ||
| 15 | #include <linux/kexec.h> | ||
| 13 | #include "ima.h" | 16 | #include "ima.h" |
| 14 | 17 | ||
| 18 | #ifdef CONFIG_IMA_KEXEC | ||
| 19 | static int ima_dump_measurement_list(unsigned long *buffer_size, void **buffer, | ||
| 20 | unsigned long segment_size) | ||
| 21 | { | ||
| 22 | struct ima_queue_entry *qe; | ||
| 23 | struct seq_file file; | ||
| 24 | struct ima_kexec_hdr khdr = { | ||
| 25 | .version = 1, .buffer_size = 0, .count = 0}; | ||
| 26 | int ret = 0; | ||
| 27 | |||
| 28 | /* segment size can't change between kexec load and execute */ | ||
| 29 | file.buf = vmalloc(segment_size); | ||
| 30 | if (!file.buf) { | ||
| 31 | ret = -ENOMEM; | ||
| 32 | goto out; | ||
| 33 | } | ||
| 34 | |||
| 35 | file.size = segment_size; | ||
| 36 | file.read_pos = 0; | ||
| 37 | file.count = sizeof(khdr); /* reserved space */ | ||
| 38 | |||
| 39 | list_for_each_entry_rcu(qe, &ima_measurements, later) { | ||
| 40 | if (file.count < file.size) { | ||
| 41 | khdr.count++; | ||
| 42 | ima_measurements_show(&file, qe); | ||
| 43 | } else { | ||
| 44 | ret = -EINVAL; | ||
| 45 | break; | ||
| 46 | } | ||
| 47 | } | ||
| 48 | |||
| 49 | if (ret < 0) | ||
| 50 | goto out; | ||
| 51 | |||
| 52 | /* | ||
| 53 | * fill in reserved space with some buffer details | ||
| 54 | * (eg. version, buffer size, number of measurements) | ||
| 55 | */ | ||
| 56 | khdr.buffer_size = file.count; | ||
| 57 | memcpy(file.buf, &khdr, sizeof(khdr)); | ||
| 58 | print_hex_dump(KERN_DEBUG, "ima dump: ", DUMP_PREFIX_NONE, | ||
| 59 | 16, 1, file.buf, | ||
| 60 | file.count < 100 ? file.count : 100, true); | ||
| 61 | |||
| 62 | *buffer_size = file.count; | ||
| 63 | *buffer = file.buf; | ||
| 64 | out: | ||
| 65 | if (ret == -EINVAL) | ||
| 66 | vfree(file.buf); | ||
| 67 | return ret; | ||
| 68 | } | ||
| 69 | |||
| 70 | /* | ||
| 71 | * Called during kexec_file_load so that IMA can add a segment to the kexec | ||
| 72 | * image for the measurement list for the next kernel. | ||
| 73 | * | ||
| 74 | * This function assumes that kexec_mutex is held. | ||
| 75 | */ | ||
| 76 | void ima_add_kexec_buffer(struct kimage *image) | ||
| 77 | { | ||
| 78 | struct kexec_buf kbuf = { .image = image, .buf_align = PAGE_SIZE, | ||
| 79 | .buf_min = 0, .buf_max = ULONG_MAX, | ||
| 80 | .top_down = true }; | ||
| 81 | unsigned long binary_runtime_size; | ||
| 82 | |||
| 83 | /* use more understandable variable names than defined in kbuf */ | ||
| 84 | void *kexec_buffer = NULL; | ||
| 85 | size_t kexec_buffer_size; | ||
| 86 | size_t kexec_segment_size; | ||
| 87 | int ret; | ||
| 88 | |||
| 89 | /* | ||
| 90 | * Reserve an extra half page of memory for additional measurements | ||
| 91 | * added during the kexec load. | ||
| 92 | */ | ||
| 93 | binary_runtime_size = ima_get_binary_runtime_size(); | ||
| 94 | if (binary_runtime_size >= ULONG_MAX - PAGE_SIZE) | ||
| 95 | kexec_segment_size = ULONG_MAX; | ||
| 96 | else | ||
| 97 | kexec_segment_size = ALIGN(ima_get_binary_runtime_size() + | ||
| 98 | PAGE_SIZE / 2, PAGE_SIZE); | ||
| 99 | if ((kexec_segment_size == ULONG_MAX) || | ||
| 100 | ((kexec_segment_size >> PAGE_SHIFT) > totalram_pages / 2)) { | ||
| 101 | pr_err("Binary measurement list too large.\n"); | ||
| 102 | return; | ||
| 103 | } | ||
| 104 | |||
| 105 | ima_dump_measurement_list(&kexec_buffer_size, &kexec_buffer, | ||
| 106 | kexec_segment_size); | ||
| 107 | if (!kexec_buffer) { | ||
| 108 | pr_err("Not enough memory for the kexec measurement buffer.\n"); | ||
| 109 | return; | ||
| 110 | } | ||
| 111 | |||
| 112 | kbuf.buffer = kexec_buffer; | ||
| 113 | kbuf.bufsz = kexec_buffer_size; | ||
| 114 | kbuf.memsz = kexec_segment_size; | ||
| 115 | ret = kexec_add_buffer(&kbuf); | ||
| 116 | if (ret) { | ||
| 117 | pr_err("Error passing over kexec measurement buffer.\n"); | ||
| 118 | return; | ||
| 119 | } | ||
| 120 | |||
| 121 | ret = arch_ima_add_kexec_buffer(image, kbuf.mem, kexec_segment_size); | ||
| 122 | if (ret) { | ||
| 123 | pr_err("Error passing over kexec measurement buffer.\n"); | ||
| 124 | return; | ||
| 125 | } | ||
| 126 | |||
| 127 | pr_debug("kexec measurement buffer for the loaded kernel at 0x%lx.\n", | ||
| 128 | kbuf.mem); | ||
| 129 | } | ||
| 130 | #endif /* IMA_KEXEC */ | ||
| 131 | |||
| 15 | /* | 132 | /* |
| 16 | * Restore the measurement list from the previous kernel. | 133 | * Restore the measurement list from the previous kernel. |
| 17 | */ | 134 | */ |
