1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | * Copyright (C) 2016 IBM Corporation |
4 | * |
5 | * Authors: |
6 | * Thiago Jung Bauermann <bauerman@linux.vnet.ibm.com> |
7 | * Mimi Zohar <zohar@linux.vnet.ibm.com> |
8 | */ |
9 | |
10 | #include <linux/seq_file.h> |
11 | #include <linux/vmalloc.h> |
12 | #include <linux/kexec.h> |
13 | #include <linux/of.h> |
14 | #include <linux/ima.h> |
15 | #include "ima.h" |
16 | |
17 | #ifdef CONFIG_IMA_KEXEC |
18 | static int ima_dump_measurement_list(unsigned long *buffer_size, void **buffer, |
19 | unsigned long segment_size) |
20 | { |
21 | struct ima_queue_entry *qe; |
22 | struct seq_file file; |
23 | struct ima_kexec_hdr khdr; |
24 | int ret = 0; |
25 | |
26 | /* segment size can't change between kexec load and execute */ |
27 | file.buf = vmalloc(size: segment_size); |
28 | if (!file.buf) { |
29 | ret = -ENOMEM; |
30 | goto out; |
31 | } |
32 | |
33 | file.size = segment_size; |
34 | file.read_pos = 0; |
35 | file.count = sizeof(khdr); /* reserved space */ |
36 | |
37 | memset(&khdr, 0, sizeof(khdr)); |
38 | khdr.version = 1; |
39 | list_for_each_entry_rcu(qe, &ima_measurements, later) { |
40 | if (file.count < file.size) { |
41 | khdr.count++; |
42 | ima_measurements_show(m: &file, v: 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 | if (ima_canonical_fmt) { |
58 | khdr.version = cpu_to_le16(khdr.version); |
59 | khdr.count = cpu_to_le64(khdr.count); |
60 | khdr.buffer_size = cpu_to_le64(khdr.buffer_size); |
61 | } |
62 | memcpy(file.buf, &khdr, sizeof(khdr)); |
63 | |
64 | print_hex_dump_debug("ima dump: " , DUMP_PREFIX_NONE, 16, 1, |
65 | file.buf, file.count < 100 ? file.count : 100, |
66 | true); |
67 | |
68 | *buffer_size = file.count; |
69 | *buffer = file.buf; |
70 | out: |
71 | if (ret == -EINVAL) |
72 | vfree(addr: file.buf); |
73 | return ret; |
74 | } |
75 | |
76 | /* |
77 | * Called during kexec_file_load so that IMA can add a segment to the kexec |
78 | * image for the measurement list for the next kernel. |
79 | * |
80 | * This function assumes that kexec_lock is held. |
81 | */ |
82 | void ima_add_kexec_buffer(struct kimage *image) |
83 | { |
84 | struct kexec_buf kbuf = { .image = image, .buf_align = PAGE_SIZE, |
85 | .buf_min = 0, .buf_max = ULONG_MAX, |
86 | .top_down = true }; |
87 | unsigned long binary_runtime_size; |
88 | |
89 | /* use more understandable variable names than defined in kbuf */ |
90 | void *kexec_buffer = NULL; |
91 | size_t kexec_buffer_size; |
92 | size_t kexec_segment_size; |
93 | int ret; |
94 | |
95 | /* |
96 | * Reserve an extra half page of memory for additional measurements |
97 | * added during the kexec load. |
98 | */ |
99 | binary_runtime_size = ima_get_binary_runtime_size(); |
100 | if (binary_runtime_size >= ULONG_MAX - PAGE_SIZE) |
101 | kexec_segment_size = ULONG_MAX; |
102 | else |
103 | kexec_segment_size = ALIGN(ima_get_binary_runtime_size() + |
104 | PAGE_SIZE / 2, PAGE_SIZE); |
105 | if ((kexec_segment_size == ULONG_MAX) || |
106 | ((kexec_segment_size >> PAGE_SHIFT) > totalram_pages() / 2)) { |
107 | pr_err("Binary measurement list too large.\n" ); |
108 | return; |
109 | } |
110 | |
111 | ima_dump_measurement_list(buffer_size: &kexec_buffer_size, buffer: &kexec_buffer, |
112 | segment_size: kexec_segment_size); |
113 | if (!kexec_buffer) { |
114 | pr_err("Not enough memory for the kexec measurement buffer.\n" ); |
115 | return; |
116 | } |
117 | |
118 | kbuf.buffer = kexec_buffer; |
119 | kbuf.bufsz = kexec_buffer_size; |
120 | kbuf.memsz = kexec_segment_size; |
121 | ret = kexec_add_buffer(kbuf: &kbuf); |
122 | if (ret) { |
123 | pr_err("Error passing over kexec measurement buffer.\n" ); |
124 | vfree(addr: kexec_buffer); |
125 | return; |
126 | } |
127 | |
128 | image->ima_buffer_addr = kbuf.mem; |
129 | image->ima_buffer_size = kexec_segment_size; |
130 | image->ima_buffer = kexec_buffer; |
131 | |
132 | kexec_dprintk("kexec measurement buffer for the loaded kernel at 0x%lx.\n" , |
133 | kbuf.mem); |
134 | } |
135 | #endif /* IMA_KEXEC */ |
136 | |
137 | /* |
138 | * Restore the measurement list from the previous kernel. |
139 | */ |
140 | void __init ima_load_kexec_buffer(void) |
141 | { |
142 | void *kexec_buffer = NULL; |
143 | size_t kexec_buffer_size = 0; |
144 | int rc; |
145 | |
146 | rc = ima_get_kexec_buffer(addr: &kexec_buffer, size: &kexec_buffer_size); |
147 | switch (rc) { |
148 | case 0: |
149 | rc = ima_restore_measurement_list(bufsize: kexec_buffer_size, |
150 | buf: kexec_buffer); |
151 | if (rc != 0) |
152 | pr_err("Failed to restore the measurement list: %d\n" , |
153 | rc); |
154 | |
155 | ima_free_kexec_buffer(); |
156 | break; |
157 | case -ENOTSUPP: |
158 | pr_debug("Restoring the measurement list not supported\n" ); |
159 | break; |
160 | case -ENOENT: |
161 | pr_debug("No measurement list to restore\n" ); |
162 | break; |
163 | default: |
164 | pr_debug("Error restoring the measurement list: %d\n" , rc); |
165 | } |
166 | } |
167 | |