1 | The current kernel behavior is IMA measurements snapshot is taken at | 1 | The current kernel behavior is IMA measurements snapshot is taken at |
---|---|---|---|
2 | kexec 'load' and not at kexec 'execute'. IMA log is then carried | 2 | kexec 'load' and not at kexec 'execute'. IMA log is then carried |
3 | over to the new kernel after kexec 'execute'. | 3 | over to the new kernel after kexec 'execute'. |
4 | 4 | ||
5 | New events can be measured during/after the IMA log snapshot at kexec | 5 | Currently, the kernel behavior during kexec load is to fetch the IMA |
6 | 'load' and before the system boots to the new kernel. In this scenario, | 6 | measurements log from TPM PCRs and store it in a buffer. When a kexec |
7 | the TPM PCRs are extended with these events, but they are not carried | 7 | reboot is triggered, this stored log buffer is carried over to the second |
8 | over to the new kernel after kexec soft reboot since the snapshot is | 8 | kernel. However, the time gap between kexec load and kexec reboot can be |
9 | already taken. This results in mismatch between TPM PCR quotes and the | 9 | very long. During this time window, new events extended into TPM PCRs miss |
10 | actual IMA measurements list after kexec soft reboot, which in turn | 10 | the chance to be carried over to the second kernel. This results in |
11 | results in remote attestation failure. | 11 | mismatch between TPM PCR quotes and the actual IMA measurements list after |
12 | kexec soft reboot, which in turn results in remote attestation failure. | ||
12 | 13 | ||
13 | To solve this problem - | 14 | To solve this problem - |
14 | - allocate the necessary buffer at kexec 'load' time, | 15 | - allocate the necessary buffer at kexec 'load' time, |
15 | - populate the buffer with the IMA measurements at kexec 'execute' time, | 16 | - populate the buffer with the IMA measurements at kexec 'execute' time, |
16 | - and measure two new IMA events 'kexec_load' and 'kexec_execute' as | 17 | - and measure two new IMA events 'kexec_load' and 'kexec_execute' as |
... | ... | ||
19 | The solution details include: | 20 | The solution details include: |
20 | - refactoring the existing code to allocate a buffer to hold IMA | 21 | - refactoring the existing code to allocate a buffer to hold IMA |
21 | measurements at kexec 'load', and dump the measurements at kexec | 22 | measurements at kexec 'load', and dump the measurements at kexec |
22 | 'execute' | 23 | 'execute' |
23 | 24 | ||
24 | - IMA functionality to suspend and resume measurements as needed during | ||
25 | buffer copy at kexec 'execute', | ||
26 | |||
27 | - kexec functionality for mapping the segments from the current kernel | 25 | - kexec functionality for mapping the segments from the current kernel |
28 | to the subsequent one, | 26 | to the subsequent one, |
29 | 27 | ||
30 | - necessary changes to the kexec_file_load syscall, enabling it to call | 28 | - necessary changes to the kexec_file_load syscall, enabling it to call |
31 | the ima functions, | 29 | the ima functions, |
32 | 30 | ||
33 | - registering a reboot notifier which gets called during kexec | 31 | - registering a reboot notifier which gets called during kexec 'execute', |
34 | 'execute', | ||
35 | 32 | ||
36 | - introducing a new Kconfig option to configure the extra memory to be | 33 | - introducing a new Kconfig option to configure the extra memory to be |
37 | allocated for passing IMA log from the current Kernel to the next, | 34 | allocated for passing IMA log from the current Kernel to the next, |
38 | 35 | ||
39 | - introducing two new events to be measured by IMA during kexec, to | 36 | - introducing two new events to be measured by IMA during kexec, to |
40 | help diagnose if the IMA log was copied fully or partially, from the | 37 | help diagnose if the IMA log was copied fully or partially, from the |
41 | current Kernel to the next, | 38 | current Kernel to the next, |
42 | 39 | ||
43 | - excluding IMA segment while calculating and storing digest in function | 40 | - excluding IMA segment while calculating and storing digest in function |
44 | kexec_calculate_store_digests(), since IMA segment can be modified | 41 | kexec_calculate_store_digests(), since IMA segment can be modified |
45 | after the digest is computed during kexec 'load'. This will ensure | 42 | after the digest is computed during kexec 'load'. This will ensure |
46 | that the segment is not added to the 'purgatory_sha_regions', and thus | 43 | that the segment is not added to the 'purgatory_sha_regions', and thus |
... | ... | ||
60 | 57 | ||
61 | V5 of this series is available here[7] for reference. | 58 | V5 of this series is available here[7] for reference. |
62 | 59 | ||
63 | V6 of this series is available here[8] for reference. | 60 | V6 of this series is available here[8] for reference. |
64 | 61 | ||
62 | V7 of this series is available here[9] for reference. | ||
63 | |||
64 | V8 of this series is available here[10] for reference. | ||
65 | |||
66 | V9 of this series is available here[11] for reference. | ||
67 | |||
68 | V10 of this series is available here[12] for reference. | ||
69 | |||
65 | References: | 70 | References: |
66 | ----------- | 71 | ----------- |
67 | 72 | ||
68 | [1] [PATHC v2 5/9] ima: on soft reboot, save the measurement list | 73 | [1] [PATHC v2 5/9] ima: on soft reboot, save the measurement list |
69 | https://lore.kernel.org/lkml/1472596811-9596-6-git-send-email-zohar@linux.vnet.ibm.com/ | 74 | https://lore.kernel.org/lkml/1472596811-9596-6-git-send-email-zohar@linux.vnet.ibm.com/ |
... | ... | ||
86 | [7] [PATCH v5 0/8] ima: kexec: measure events between kexec load and execute | 91 | [7] [PATCH v5 0/8] ima: kexec: measure events between kexec load and execute |
87 | https://lore.kernel.org/all/20240214153827.1087657-1-tusharsu@linux.microsoft.com/ | 92 | https://lore.kernel.org/all/20240214153827.1087657-1-tusharsu@linux.microsoft.com/ |
88 | 93 | ||
89 | [8] [PATCH v6 0/7] ima: kexec: measure events between kexec load and execute | 94 | [8] [PATCH v6 0/7] ima: kexec: measure events between kexec load and execute |
90 | https://lore.kernel.org/all/20250124225547.22684-1-chenste@linux.microsoft.com/ | 95 | https://lore.kernel.org/all/20250124225547.22684-1-chenste@linux.microsoft.com/ |
96 | |||
97 | [9] [PATCH v7 0/7] ima: kexec: measure events between kexec load and execute | ||
98 | https://lore.kernel.org/all/20250203232033.64123-1-chenste@linux.microsoft.com/ | ||
99 | |||
100 | [10] [PATCH v8 0/7] ima: kexec: measure events between kexec load and execute | ||
101 | https://lore.kernel.org/all/20250218225502.747963-1-chenste@linux.microsoft.com/ | ||
102 | |||
103 | [11] [PATCH v9 0/7] ima: kexec: measure events between kexec load and execute | ||
104 | https://lore.kernel.org/all/20250304190351.96975-1-chenste@linux.microsoft.com/ | ||
105 | |||
106 | [12] [PATCH v10 0/8] ima: kexec: measure events between kexec load and execute | ||
107 | https://lore.kernel.org/all/20250318010448.954-1-chenste@linux.microsoft.com/ | ||
108 | |||
109 | Change Log v11: | ||
110 | - Incorporated feedback from the community (Mimi Zohar and Baoquan He) on | ||
111 | v10 of this series[12]. | ||
112 | - [PATCH V10 2/8] was splited into two [PATCH V11 2/9] and [PATCH V11 7/9]. | ||
113 | Per Mimi comment on [PATCH V10 2/8]. | ||
114 | - Verified all the patches are bisect-safe by booting into each | ||
115 | patch and verifying multiple kexec 'load' operations work, | ||
116 | and also verifying kexec soft reboot works, and IMA log gets | ||
117 | carried over for each patch. | ||
118 | - Updated patch descriptions as necessary. | ||
119 | |||
120 | Change Log v10: | ||
121 | - Incorporated feedback from the community (Mimi Zohar, Baoquan He, and | ||
122 | kernel test robot) on v9 of this series[11]. | ||
123 | - [PATCH V9 1/7] was splited into two [PATCH V10 1/8] and [PATCH V10 2/8]. | ||
124 | Per Mimi comment on [PATCH V9 1/7]. | ||
125 | - Verified all the patches are bisect-safe by booting into each | ||
126 | patch and verifying multiple kexec 'load' operations work, | ||
127 | and also verifying kexec soft reboot works, and IMA log gets | ||
128 | carried over for each patch. | ||
129 | |||
130 | Change Log v9: | ||
131 | - Incorporated feedback from the community (Stefan Berger, Mimi Zohar, | ||
132 | and kernel test robot) on v8 of this series[10]. | ||
133 | - Rebased the patch series to mainline 6.14.0-rc3. | ||
134 | - Verified all the patches are bisect-safe by booting into each | ||
135 | patch and verifying multiple kexec 'load' operations work, | ||
136 | and also verifying kexec soft reboot works, and IMA log gets | ||
137 | carried over for each patch. | ||
138 | |||
139 | Change Log v8: | ||
140 | - Incorporated feedback from the community (Stefan Berger, Mimi Zohar) | ||
141 | on v7 of this series[9]. | ||
142 | - Rebased the patch series to mainline 6.14.0-rc1. | ||
143 | - Verified all the patches are bisect-safe by booting into each | ||
144 | patch and verifying multiple kexec 'load' operations work, | ||
145 | and also verifying kexec soft reboot works, and IMA log gets | ||
146 | carried over for each patch. | ||
91 | 147 | ||
92 | Change Log v7: | 148 | Change Log v7: |
93 | - Incorporated feedback from the community (Stefan Berger, Tyler Hicks) | 149 | - Incorporated feedback from the community (Stefan Berger, Tyler Hicks) |
94 | on v6 of this series[8]. | 150 | on v6 of this series[8]. |
95 | - Verified all the patches are bisect-safe by booting into each | 151 | - Verified all the patches are bisect-safe by booting into each |
... | ... | ||
190 | - Refactored patches to ensure no warnings during individual patch | 246 | - Refactored patches to ensure no warnings during individual patch |
191 | compilation. | 247 | compilation. |
192 | - Used virt_to_page instead of phys_to_page. | 248 | - Used virt_to_page instead of phys_to_page. |
193 | - Updated patch descriptions as necessary. | 249 | - Updated patch descriptions as necessary. |
194 | 250 | ||
195 | steven chen (7): | 251 | |
196 | ima: define and call ima_alloc_kexec_file_buf | 252 | steven chen (9): |
253 | ima: rename variable the set_file "file" to "ima_kexec_file" | ||
254 | ima: define and call ima_alloc_kexec_file_buf() | ||
197 | kexec: define functions to map and unmap segments | 255 | kexec: define functions to map and unmap segments |
198 | ima: kexec: skip IMA segment validation after kexec soft reboot | 256 | ima: kexec: skip IMA segment validation after kexec soft reboot |
199 | ima: kexec: define functions to copy IMA log at soft boot | 257 | ima: kexec: define functions to copy IMA log at soft boot |
200 | ima: kexec: move IMA log copy from kexec load to execute | 258 | ima: kexec: move IMA log copy from kexec load to execute |
259 | ima: verify if the segment size has changed | ||
201 | ima: make the kexec extra memory configurable | 260 | ima: make the kexec extra memory configurable |
202 | ima: measure kexec load and exec events as critical data | 261 | ima: measure kexec load and exec events as critical data |
203 | 262 | ||
204 | include/linux/ima.h | 3 + | 263 | include/linux/ima.h | 3 + |
205 | include/linux/kexec.h | 10 ++ | 264 | include/linux/kexec.h | 9 ++ |
206 | kernel/kexec_core.c | 54 ++++++++ | 265 | kernel/kexec_core.c | 54 ++++++++ |
207 | kernel/kexec_file.c | 31 +++++ | 266 | kernel/kexec_file.c | 33 ++++- |
208 | security/integrity/ima/Kconfig | 10 ++ | 267 | security/integrity/ima/Kconfig | 10 ++ |
209 | security/integrity/ima/ima.h | 1 + | 268 | security/integrity/ima/ima.h | 6 + |
210 | security/integrity/ima/ima_kexec.c | 208 ++++++++++++++++++++++++----- | 269 | security/integrity/ima/ima_kexec.c | 193 ++++++++++++++++++++++++----- |
211 | security/integrity/ima/ima_queue.c | 4 +- | 270 | security/integrity/ima/ima_queue.c | 5 + |
212 | 8 files changed, 284 insertions(+), 37 deletions(-) | 271 | 8 files changed, 279 insertions(+), 34 deletions(-) |
213 | 272 | ||
214 | -- | 273 | -- |
215 | 2.25.1 | 274 | 2.25.1 |
216 | 275 | diff view generated by jsdifflib |
New patch | |||
---|---|---|---|
1 | The current kernel behavior is IMA measurements snapshot is taken at | ||
2 | kexec 'load' and not at kexec 'execute'. IMA log is then carried | ||
3 | over to the new kernel after kexec 'execute'. However, the time gap | ||
4 | between kexec load and kexec reboot can be very long. During this | ||
5 | time window, new events extended into TPM PCRs miss the chance | ||
6 | to be carried over to the second kernel. | ||
7 | |||
8 | To address the above, the following approach is proposed: | ||
9 | - Allocate the necessary buffer during the kexec load phase. | ||
10 | - Populate this buffer with the IMA measurements during | ||
11 | the kexec execute phase. | ||
1 | 12 | ||
13 | In the current implementation, a local variable "file" of type seq_file | ||
14 | is used in the API ima_dump_measurement_list() to store the IMA measurements | ||
15 | to be carried over across kexec system call. To make this buffer accessible | ||
16 | at kexec 'execute' time, rename it to "ima_kexec_file" before making it | ||
17 | a file variable to better reflect its purpose. | ||
18 | |||
19 | Renaming the local variable "file" of type seq_file defined in the | ||
20 | ima_dump_measurement_list function to "ima_kexec_file" will improve code | ||
21 | readability and maintainability by making the variable's role more explicit. | ||
22 | |||
23 | Suggested-by: Mimi Zohar <zohar@linux.ibm.com> | ||
24 | Signed-off-by: steven chen <chenste@linux.microsoft.com> | ||
25 | Reviewed-by: Stefan Berger <stefanb@linux.ibm.com> | ||
26 | --- | ||
27 | security/integrity/ima/ima_kexec.c | 31 +++++++++++++++--------------- | ||
28 | 1 file changed, 16 insertions(+), 15 deletions(-) | ||
29 | |||
30 | diff --git a/security/integrity/ima/ima_kexec.c b/security/integrity/ima/ima_kexec.c | ||
31 | index XXXXXXX..XXXXXXX 100644 | ||
32 | --- a/security/integrity/ima/ima_kexec.c | ||
33 | +++ b/security/integrity/ima/ima_kexec.c | ||
34 | @@ -XXX,XX +XXX,XX @@ | ||
35 | static int ima_dump_measurement_list(unsigned long *buffer_size, void **buffer, | ||
36 | unsigned long segment_size) | ||
37 | { | ||
38 | + struct seq_file ima_kexec_file; | ||
39 | struct ima_queue_entry *qe; | ||
40 | - struct seq_file file; | ||
41 | struct ima_kexec_hdr khdr; | ||
42 | int ret = 0; | ||
43 | |||
44 | /* segment size can't change between kexec load and execute */ | ||
45 | - file.buf = vmalloc(segment_size); | ||
46 | - if (!file.buf) { | ||
47 | + ima_kexec_file.buf = vmalloc(segment_size); | ||
48 | + if (!ima_kexec_file.buf) { | ||
49 | ret = -ENOMEM; | ||
50 | goto out; | ||
51 | } | ||
52 | |||
53 | - file.file = NULL; | ||
54 | - file.size = segment_size; | ||
55 | - file.read_pos = 0; | ||
56 | - file.count = sizeof(khdr); /* reserved space */ | ||
57 | + ima_kexec_file.file = NULL; | ||
58 | + ima_kexec_file.size = segment_size; | ||
59 | + ima_kexec_file.read_pos = 0; | ||
60 | + ima_kexec_file.count = sizeof(khdr); /* reserved space */ | ||
61 | |||
62 | memset(&khdr, 0, sizeof(khdr)); | ||
63 | khdr.version = 1; | ||
64 | /* This is an append-only list, no need to hold the RCU read lock */ | ||
65 | list_for_each_entry_rcu(qe, &ima_measurements, later, true) { | ||
66 | - if (file.count < file.size) { | ||
67 | + if (ima_kexec_file.count < ima_kexec_file.size) { | ||
68 | khdr.count++; | ||
69 | - ima_measurements_show(&file, qe); | ||
70 | + ima_measurements_show(&ima_kexec_file, qe); | ||
71 | } else { | ||
72 | ret = -EINVAL; | ||
73 | break; | ||
74 | @@ -XXX,XX +XXX,XX @@ static int ima_dump_measurement_list(unsigned long *buffer_size, void **buffer, | ||
75 | * fill in reserved space with some buffer details | ||
76 | * (eg. version, buffer size, number of measurements) | ||
77 | */ | ||
78 | - khdr.buffer_size = file.count; | ||
79 | + khdr.buffer_size = ima_kexec_file.count; | ||
80 | if (ima_canonical_fmt) { | ||
81 | khdr.version = cpu_to_le16(khdr.version); | ||
82 | khdr.count = cpu_to_le64(khdr.count); | ||
83 | khdr.buffer_size = cpu_to_le64(khdr.buffer_size); | ||
84 | } | ||
85 | - memcpy(file.buf, &khdr, sizeof(khdr)); | ||
86 | + memcpy(ima_kexec_file.buf, &khdr, sizeof(khdr)); | ||
87 | |||
88 | print_hex_dump_debug("ima dump: ", DUMP_PREFIX_NONE, 16, 1, | ||
89 | - file.buf, file.count < 100 ? file.count : 100, | ||
90 | + ima_kexec_file.buf, ima_kexec_file.count < 100 ? | ||
91 | + ima_kexec_file.count : 100, | ||
92 | true); | ||
93 | |||
94 | - *buffer_size = file.count; | ||
95 | - *buffer = file.buf; | ||
96 | + *buffer_size = ima_kexec_file.count; | ||
97 | + *buffer = ima_kexec_file.buf; | ||
98 | out: | ||
99 | if (ret == -EINVAL) | ||
100 | - vfree(file.buf); | ||
101 | + vfree(ima_kexec_file.buf); | ||
102 | return ret; | ||
103 | } | ||
104 | |||
105 | -- | ||
106 | 2.25.1 | diff view generated by jsdifflib |
1 | In the current implementation, the ima_dump_measurement_list() API is | ||
---|---|---|---|
2 | called during the kexec "load" phase, where a buffer is allocated and | ||
3 | the measurement records are copied. Due to this, new events added after | ||
4 | kexec load but before kexec execute are not carried over to the new kernel | ||
5 | during kexec operation | ||
6 | |||
7 | To allow the buffer allocation and population to be separated into distinct | ||
8 | steps, make the function local seq_file "ima_kexec_file" to a file variable. | ||
9 | |||
1 | Carrying the IMA measurement list across kexec requires allocating a | 10 | Carrying the IMA measurement list across kexec requires allocating a |
2 | buffer and copying the measurement records. Separate allocating the | 11 | buffer and copying the measurement records. Separate allocating the |
3 | buffer and copying the measurement records into separate functions in | 12 | buffer and copying the measurement records into separate functions in |
4 | order to allocate the buffer at kexec 'load' and copy the measurements | 13 | order to allocate the buffer at kexec 'load' and copy the measurements |
5 | at kexec 'execute'. | 14 | at kexec 'execute'. |
6 | 15 | ||
7 | This patch includes the following changes: | ||
8 | - Refactor ima_dump_measurement_list() to move the memory allocation | ||
9 | to a separate function ima_alloc_kexec_file_buf() which allocates | ||
10 | buffer of size 'kexec_segment_size' at kexec 'load'. | ||
11 | - Make the local variable ima_kexec_file in ima_dump_measurement_list() | ||
12 | a local static to the file, so that it can be accessed from | ||
13 | ima_alloc_kexec_file_buf(). Compare actual memory required to ensure | ||
14 | there is enough memory for the entire measurement record. | ||
15 | - Copy as many measurement events as possible. | ||
16 | - Make necessary changes to the function ima_add_kexec_buffer() to call | ||
17 | the above two functions. | ||
18 | - Compared the memory size allocated with memory size of the entire | ||
19 | measurement record. If there is not enough memory, it will copy as many | ||
20 | IMA measurement records as possible, and this situation will result | ||
21 | in a failure of remote attestation. | ||
22 | |||
23 | Author: Tushar Sugandhi <tusharsu@linux.microsoft.com> | ||
24 | Reviewed-by: Stefan Berger <stefanb@linux.ibm.com> | ||
25 | Suggested-by: Mimi Zohar <zohar@linux.ibm.com> | ||
26 | Signed-off-by: Tushar Sugandhi <tusharsu@linux.microsoft.com> | 16 | Signed-off-by: Tushar Sugandhi <tusharsu@linux.microsoft.com> |
27 | Signed-off-by: steven chen <chenste@linux.microsoft.com> | 17 | Signed-off-by: steven chen <chenste@linux.microsoft.com> |
28 | --- | 18 | --- |
29 | security/integrity/ima/ima.h | 1 + | 19 | security/integrity/ima/ima_kexec.c | 46 +++++++++++++++++++++++------- |
30 | security/integrity/ima/ima_kexec.c | 105 +++++++++++++++++++++-------- | 20 | 1 file changed, 35 insertions(+), 11 deletions(-) |
31 | security/integrity/ima/ima_queue.c | 4 +- | ||
32 | 3 files changed, 80 insertions(+), 30 deletions(-) | ||
33 | 21 | ||
34 | diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h | ||
35 | index XXXXXXX..XXXXXXX 100644 | ||
36 | --- a/security/integrity/ima/ima.h | ||
37 | +++ b/security/integrity/ima/ima.h | ||
38 | @@ -XXX,XX +XXX,XX @@ bool ima_template_has_modsig(const struct ima_template_desc *ima_template); | ||
39 | int ima_restore_measurement_entry(struct ima_template_entry *entry); | ||
40 | int ima_restore_measurement_list(loff_t bufsize, void *buf); | ||
41 | int ima_measurements_show(struct seq_file *m, void *v); | ||
42 | +int ima_get_binary_runtime_entry_size(struct ima_template_entry *entry); | ||
43 | unsigned long ima_get_binary_runtime_size(void); | ||
44 | int ima_init_template(void); | ||
45 | void ima_init_template_list(void); | ||
46 | diff --git a/security/integrity/ima/ima_kexec.c b/security/integrity/ima/ima_kexec.c | 22 | diff --git a/security/integrity/ima/ima_kexec.c b/security/integrity/ima/ima_kexec.c |
47 | index XXXXXXX..XXXXXXX 100644 | 23 | index XXXXXXX..XXXXXXX 100644 |
48 | --- a/security/integrity/ima/ima_kexec.c | 24 | --- a/security/integrity/ima/ima_kexec.c |
49 | +++ b/security/integrity/ima/ima_kexec.c | 25 | +++ b/security/integrity/ima/ima_kexec.c |
50 | @@ -XXX,XX +XXX,XX @@ | 26 | @@ -XXX,XX +XXX,XX @@ |
51 | #include "ima.h" | 27 | #include "ima.h" |
52 | 28 | ||
53 | #ifdef CONFIG_IMA_KEXEC | 29 | #ifdef CONFIG_IMA_KEXEC |
54 | +static struct seq_file ima_kexec_file; | 30 | +static struct seq_file ima_kexec_file; |
55 | + | 31 | + |
56 | +static void ima_reset_kexec_file(struct seq_file *sf) | 32 | +static void ima_free_kexec_file_buf(struct seq_file *sf) |
57 | +{ | 33 | +{ |
34 | + vfree(sf->buf); | ||
58 | + sf->buf = NULL; | 35 | + sf->buf = NULL; |
59 | + sf->size = 0; | 36 | + sf->size = 0; |
60 | + sf->read_pos = 0; | 37 | + sf->read_pos = 0; |
61 | + sf->count = 0; | 38 | + sf->count = 0; |
62 | +} | 39 | +} |
63 | + | 40 | + |
64 | +static void ima_free_kexec_file_buf(struct seq_file *sf) | ||
65 | +{ | ||
66 | + vfree(sf->buf); | ||
67 | + ima_reset_kexec_file(sf); | ||
68 | +} | ||
69 | + | ||
70 | +static int ima_alloc_kexec_file_buf(size_t segment_size) | 41 | +static int ima_alloc_kexec_file_buf(size_t segment_size) |
71 | +{ | 42 | +{ |
72 | + /* | ||
73 | + * kexec 'load' may be called multiple times. | ||
74 | + * Free and realloc the buffer only if the segment_size is | ||
75 | + * changed from the previous kexec 'load' call. | ||
76 | + */ | ||
77 | + if (ima_kexec_file.buf && | ||
78 | + (ima_kexec_file.size == segment_size)) { | ||
79 | + goto out; | ||
80 | + } | ||
81 | + | ||
82 | + ima_free_kexec_file_buf(&ima_kexec_file); | 43 | + ima_free_kexec_file_buf(&ima_kexec_file); |
83 | + | 44 | + |
84 | + /* segment size can't change between kexec load and execute */ | 45 | + /* segment size can't change between kexec load and execute */ |
85 | + ima_kexec_file.buf = vmalloc(segment_size); | 46 | + ima_kexec_file.buf = vmalloc(segment_size); |
86 | + if (!ima_kexec_file.buf) | 47 | + if (!ima_kexec_file.buf) |
87 | + return -ENOMEM; | 48 | + return -ENOMEM; |
88 | + | 49 | + |
89 | + ima_kexec_file.size = segment_size; | 50 | + ima_kexec_file.size = segment_size; |
90 | + | ||
91 | +out: | ||
92 | + ima_kexec_file.read_pos = 0; | 51 | + ima_kexec_file.read_pos = 0; |
93 | + ima_kexec_file.count = sizeof(struct ima_kexec_hdr); /* reserved space */ | 52 | + ima_kexec_file.count = sizeof(struct ima_kexec_hdr); /* reserved space */ |
94 | + | 53 | + |
95 | + return 0; | 54 | + return 0; |
96 | +} | 55 | +} |
97 | + | 56 | + |
98 | static int ima_dump_measurement_list(unsigned long *buffer_size, void **buffer, | 57 | static int ima_dump_measurement_list(unsigned long *buffer_size, void **buffer, |
99 | unsigned long segment_size) | 58 | unsigned long segment_size) |
100 | { | 59 | { |
60 | - struct seq_file ima_kexec_file; | ||
101 | struct ima_queue_entry *qe; | 61 | struct ima_queue_entry *qe; |
102 | - struct seq_file file; | ||
103 | struct ima_kexec_hdr khdr; | 62 | struct ima_kexec_hdr khdr; |
104 | int ret = 0; | 63 | int ret = 0; |
105 | + size_t entry_size = 0; | 64 | |
106 | 65 | /* segment size can't change between kexec load and execute */ | |
107 | - /* segment size can't change between kexec load and execute */ | 66 | - ima_kexec_file.buf = vmalloc(segment_size); |
108 | - file.buf = vmalloc(segment_size); | 67 | if (!ima_kexec_file.buf) { |
109 | - if (!file.buf) { | ||
110 | - ret = -ENOMEM; | 68 | - ret = -ENOMEM; |
111 | - goto out; | 69 | - goto out; |
112 | + if (!ima_kexec_file.buf) { | ||
113 | + pr_err("Kexec file buf not allocated\n"); | 70 | + pr_err("Kexec file buf not allocated\n"); |
114 | + return -EINVAL; | 71 | + return -EINVAL; |
115 | } | 72 | } |
116 | 73 | ||
117 | - file.file = NULL; | 74 | - ima_kexec_file.file = NULL; |
118 | - file.size = segment_size; | 75 | - ima_kexec_file.size = segment_size; |
119 | - file.read_pos = 0; | 76 | - ima_kexec_file.read_pos = 0; |
120 | - file.count = sizeof(khdr); /* reserved space */ | 77 | - ima_kexec_file.count = sizeof(khdr); /* reserved space */ |
121 | - | 78 | - |
122 | memset(&khdr, 0, sizeof(khdr)); | 79 | memset(&khdr, 0, sizeof(khdr)); |
123 | khdr.version = 1; | 80 | khdr.version = 1; |
124 | + | 81 | /* This is an append-only list, no need to hold the RCU read lock */ |
125 | + /* Copy as many IMA measurements list records as possible */ | 82 | @@ -XXX,XX +XXX,XX @@ static int ima_dump_measurement_list(unsigned long *buffer_size, void **buffer, |
126 | list_for_each_entry_rcu(qe, &ima_measurements, later) { | 83 | *buffer_size = ima_kexec_file.count; |
127 | - if (file.count < file.size) { | 84 | *buffer = ima_kexec_file.buf; |
128 | + entry_size += ima_get_binary_runtime_entry_size(qe->entry); | 85 | out: |
129 | + if (entry_size <= segment_size) { | ||
130 | khdr.count++; | ||
131 | - ima_measurements_show(&file, qe); | ||
132 | + ima_measurements_show(&ima_kexec_file, qe); | ||
133 | } else { | ||
134 | ret = -EINVAL; | ||
135 | + pr_err("IMA log file is too big for Kexec buf\n"); | ||
136 | break; | ||
137 | } | ||
138 | } | ||
139 | |||
140 | - if (ret < 0) | ||
141 | - goto out; | ||
142 | - | ||
143 | /* | ||
144 | * fill in reserved space with some buffer details | ||
145 | * (eg. version, buffer size, number of measurements) | ||
146 | */ | ||
147 | - khdr.buffer_size = file.count; | ||
148 | + khdr.buffer_size = ima_kexec_file.count; | ||
149 | if (ima_canonical_fmt) { | ||
150 | khdr.version = cpu_to_le16(khdr.version); | ||
151 | khdr.count = cpu_to_le64(khdr.count); | ||
152 | khdr.buffer_size = cpu_to_le64(khdr.buffer_size); | ||
153 | } | ||
154 | - memcpy(file.buf, &khdr, sizeof(khdr)); | ||
155 | + memcpy(ima_kexec_file.buf, &khdr, sizeof(khdr)); | ||
156 | |||
157 | print_hex_dump_debug("ima dump: ", DUMP_PREFIX_NONE, 16, 1, | ||
158 | - file.buf, file.count < 100 ? file.count : 100, | ||
159 | + ima_kexec_file.buf, ima_kexec_file.count < 100 ? | ||
160 | + ima_kexec_file.count : 100, | ||
161 | true); | ||
162 | |||
163 | - *buffer_size = file.count; | ||
164 | - *buffer = file.buf; | ||
165 | -out: | ||
166 | - if (ret == -EINVAL) | 86 | - if (ret == -EINVAL) |
167 | - vfree(file.buf); | 87 | - vfree(ima_kexec_file.buf); |
168 | + *buffer_size = ima_kexec_file.count; | ||
169 | + *buffer = ima_kexec_file.buf; | ||
170 | + | ||
171 | return ret; | 88 | return ret; |
172 | } | 89 | } |
173 | |||
174 | @@ -XXX,XX +XXX,XX @@ void ima_add_kexec_buffer(struct kimage *image) | ||
175 | |||
176 | /* use more understandable variable names than defined in kbuf */ | ||
177 | void *kexec_buffer = NULL; | ||
178 | - size_t kexec_buffer_size; | ||
179 | + size_t kexec_buffer_size = 0; | ||
180 | size_t kexec_segment_size; | ||
181 | int ret; | ||
182 | 90 | ||
183 | @@ -XXX,XX +XXX,XX @@ void ima_add_kexec_buffer(struct kimage *image) | 91 | @@ -XXX,XX +XXX,XX @@ void ima_add_kexec_buffer(struct kimage *image) |
184 | return; | 92 | return; |
185 | } | 93 | } |
186 | 94 | ||
187 | - ima_dump_measurement_list(&kexec_buffer_size, &kexec_buffer, | ||
188 | - kexec_segment_size); | ||
189 | - if (!kexec_buffer) { | ||
190 | + ret = ima_alloc_kexec_file_buf(kexec_segment_size); | 95 | + ret = ima_alloc_kexec_file_buf(kexec_segment_size); |
191 | + if (ret < 0) { | 96 | + if (ret < 0) { |
192 | pr_err("Not enough memory for the kexec measurement buffer.\n"); | 97 | + pr_err("Not enough memory for the kexec measurement buffer.\n"); |
193 | return; | ||
194 | } | ||
195 | |||
196 | + ret = ima_dump_measurement_list(&kexec_buffer_size, &kexec_buffer, | ||
197 | + kexec_segment_size); | ||
198 | + if (ret < 0) { | ||
199 | + pr_err("Failed to dump IMA measurements. Error:%d.\n", ret); | ||
200 | + return; | 98 | + return; |
201 | + } | 99 | + } |
202 | + | 100 | + |
203 | kbuf.buffer = kexec_buffer; | 101 | ima_dump_measurement_list(&kexec_buffer_size, &kexec_buffer, |
204 | kbuf.bufsz = kexec_buffer_size; | 102 | kexec_segment_size); |
205 | kbuf.memsz = kexec_segment_size; | 103 | if (!kexec_buffer) { |
206 | @@ -XXX,XX +XXX,XX @@ void ima_add_kexec_buffer(struct kimage *image) | ||
207 | image->ima_buffer_size = kexec_segment_size; | ||
208 | image->ima_buffer = kexec_buffer; | ||
209 | |||
210 | + /* | ||
211 | + * kexec owns kexec_buffer after kexec_add_buffer() is called | ||
212 | + * and it will vfree() that buffer. | ||
213 | + */ | ||
214 | + ima_reset_kexec_file(&ima_kexec_file); | ||
215 | + | ||
216 | kexec_dprintk("kexec measurement buffer for the loaded kernel at 0x%lx.\n", | ||
217 | kbuf.mem); | ||
218 | } | ||
219 | diff --git a/security/integrity/ima/ima_queue.c b/security/integrity/ima/ima_queue.c | ||
220 | index XXXXXXX..XXXXXXX 100644 | ||
221 | --- a/security/integrity/ima/ima_queue.c | ||
222 | +++ b/security/integrity/ima/ima_queue.c | ||
223 | @@ -XXX,XX +XXX,XX @@ static struct ima_queue_entry *ima_lookup_digest_entry(u8 *digest_value, | ||
224 | * binary_runtime_measurement list entry, which contains a | ||
225 | * couple of variable length fields (e.g template name and data). | ||
226 | */ | ||
227 | -static int get_binary_runtime_size(struct ima_template_entry *entry) | ||
228 | +int ima_get_binary_runtime_entry_size(struct ima_template_entry *entry) | ||
229 | { | ||
230 | int size = 0; | ||
231 | |||
232 | @@ -XXX,XX +XXX,XX @@ static int ima_add_digest_entry(struct ima_template_entry *entry, | ||
233 | if (binary_runtime_size != ULONG_MAX) { | ||
234 | int size; | ||
235 | |||
236 | - size = get_binary_runtime_size(entry); | ||
237 | + size = ima_get_binary_runtime_entry_size(entry); | ||
238 | binary_runtime_size = (binary_runtime_size < ULONG_MAX - size) ? | ||
239 | binary_runtime_size + size : ULONG_MAX; | ||
240 | } | ||
241 | -- | 104 | -- |
242 | 2.25.1 | 105 | 2.25.1 | diff view generated by jsdifflib |
1 | Currently, the mechanism to map and unmap segments to the kimage | 1 | Currently, the kernel behavior during kexec load is to fetch the IMA |
---|---|---|---|
2 | structure is not available to the subsystems outside of kexec. This | 2 | measurements logs and store logs in kernel memory. When a kexec reboot is |
3 | functionality is needed when IMA is allocating the memory segments | 3 | triggered, these stored logs in the kernel memory are carried over to the |
4 | during kexec 'load' operation. Implement functions to map and unmap | 4 | second kernel. However, the time gap between kexec load and kexec reboot |
5 | segments to kimage. | 5 | can be very long. During this time window, new events extended into TPM |
6 | PCRs miss the chance to be carried over to the second kernel. This results | ||
7 | in a mismatch between TPM PCR quotes and the actual IMA measurements list | ||
8 | after kexec reboot, leading to remote attestation failure. | ||
6 | 9 | ||
7 | Implement kimage_map_segment() to enable mapping of IMA buffer source | 10 | To solve this problem, the new design defers reading the IMA measurements |
8 | pages to the kimage structure post kexec 'load'. This function, | 11 | logs into the kexec buffer to the kexec reboot phase, while still allocating |
9 | accepting a kimage pointer, an address, and a size, will gather the | 12 | the necessary buffer at kexec load time because it is not appropriate to |
10 | source pages within the specified address range, create an array of page | 13 | allocate memory at the kexec reboot moment. |
11 | pointers, and map these to a contiguous virtual address range. The | ||
12 | function returns the start of this range if successful, or NULL if | ||
13 | unsuccessful. | ||
14 | 14 | ||
15 | Implement kimage_unmap_segment() for unmapping segments | 15 | The content of memory segments carried over to the new kernel during the |
16 | using vunmap(). | 16 | kexec system call can be changed at the kexec 'execute' stage, but the size |
17 | of the memory segments cannot be changed at the kexec 'execute' stage. | ||
18 | |||
19 | To copy IMA measurement logs during the kexec operation, IMA allocates | ||
20 | memory at the kexec 'load' stage and map the segments to the kimage | ||
21 | structure. The mapped address will then be used to copy IMA measurements | ||
22 | during the kexec 'execute' stage. | ||
23 | |||
24 | Currently, the mechanism to map and unmap segments to the kimage structure | ||
25 | is not available to subsystems outside of kexec. | ||
26 | |||
27 | Implement kimage_map_segment() to enable IMA to map the measurement log | ||
28 | list to the kimage structure during the kexec 'load' stage. This function | ||
29 | takes a kimage pointer, a memory address, and a size, then gathers the | ||
30 | source pages within the specified address range, creates an array of page | ||
31 | pointers, and maps these to a contiguous virtual address range. The | ||
32 | function returns the start virtual address of this range if successful, | ||
33 | or NULL on failure. | ||
34 | |||
35 | Implement kimage_unmap_segment() for unmapping segments using vunmap(). | ||
17 | 36 | ||
18 | From: Tushar Sugandhi <tusharsu@linux.microsoft.com> | 37 | From: Tushar Sugandhi <tusharsu@linux.microsoft.com> |
19 | Author: Tushar Sugandhi <tusharsu@linux.microsoft.com> | ||
20 | Reviewed-by: Stefan Berger <stefanb@linux.ibm.com> | ||
21 | Reviewed-by: Mimi Zohar <zohar@linux.ibm.com> | ||
22 | Signed-off-by: Tushar Sugandhi <tusharsu@linux.microsoft.com> | 38 | Signed-off-by: Tushar Sugandhi <tusharsu@linux.microsoft.com> |
39 | Cc: Eric Biederman <ebiederm@xmission.com> | ||
40 | Cc: Baoquan He <bhe@redhat.com> | ||
41 | Cc: Vivek Goyal <vgoyal@redhat.com> | ||
42 | Cc: Dave Young <dyoung@redhat.com> | ||
23 | Signed-off-by: steven chen <chenste@linux.microsoft.com> | 43 | Signed-off-by: steven chen <chenste@linux.microsoft.com> |
44 | Acked-by: Baoquan He <bhe@redhat.com> | ||
24 | --- | 45 | --- |
25 | include/linux/kexec.h | 7 ++++++ | 46 | include/linux/kexec.h | 6 +++++ |
26 | kernel/kexec_core.c | 54 +++++++++++++++++++++++++++++++++++++++++++ | 47 | kernel/kexec_core.c | 54 +++++++++++++++++++++++++++++++++++++++++++ |
27 | 2 files changed, 61 insertions(+) | 48 | 2 files changed, 60 insertions(+) |
28 | 49 | ||
29 | diff --git a/include/linux/kexec.h b/include/linux/kexec.h | 50 | diff --git a/include/linux/kexec.h b/include/linux/kexec.h |
30 | index XXXXXXX..XXXXXXX 100644 | 51 | index XXXXXXX..XXXXXXX 100644 |
31 | --- a/include/linux/kexec.h | 52 | --- a/include/linux/kexec.h |
32 | +++ b/include/linux/kexec.h | 53 | +++ b/include/linux/kexec.h |
33 | @@ -XXX,XX +XXX,XX @@ extern bool kexec_file_dbg_print; | 54 | @@ -XXX,XX +XXX,XX @@ extern bool kexec_file_dbg_print; |
34 | #define kexec_dprintk(fmt, arg...) \ | 55 | #define kexec_dprintk(fmt, arg...) \ |
35 | do { if (kexec_file_dbg_print) pr_info(fmt, ##arg); } while (0) | 56 | do { if (kexec_file_dbg_print) pr_info(fmt, ##arg); } while (0) |
36 | 57 | ||
37 | +extern void *kimage_map_segment(struct kimage *image, | 58 | +extern void *kimage_map_segment(struct kimage *image, unsigned long addr, unsigned long size); |
38 | + unsigned long addr, unsigned long size); | ||
39 | +extern void kimage_unmap_segment(void *buffer); | 59 | +extern void kimage_unmap_segment(void *buffer); |
40 | #else /* !CONFIG_KEXEC_CORE */ | 60 | #else /* !CONFIG_KEXEC_CORE */ |
41 | struct pt_regs; | 61 | struct pt_regs; |
42 | struct task_struct; | 62 | struct task_struct; |
43 | @@ -XXX,XX +XXX,XX @@ static inline void __crash_kexec(struct pt_regs *regs) { } | 63 | +struct kimage; |
64 | static inline void __crash_kexec(struct pt_regs *regs) { } | ||
44 | static inline void crash_kexec(struct pt_regs *regs) { } | 65 | static inline void crash_kexec(struct pt_regs *regs) { } |
45 | static inline int kexec_should_crash(struct task_struct *p) { return 0; } | 66 | static inline int kexec_should_crash(struct task_struct *p) { return 0; } |
46 | static inline int kexec_crash_loaded(void) { return 0; } | 67 | static inline int kexec_crash_loaded(void) { return 0; } |
47 | +static inline void *kimage_map_segment(struct kimage *image, | 68 | +static inline void *kimage_map_segment(struct kimage *image, unsigned long addr, unsigned long size) |
48 | + unsigned long addr, unsigned long size) | ||
49 | +{ return NULL; } | 69 | +{ return NULL; } |
50 | +static inline void kimage_unmap_segment(void *buffer) { } | 70 | +static inline void kimage_unmap_segment(void *buffer) { } |
51 | #define kexec_in_progress false | 71 | #define kexec_in_progress false |
52 | #endif /* CONFIG_KEXEC_CORE */ | 72 | #endif /* CONFIG_KEXEC_CORE */ |
53 | 73 | ||
... | ... | ||
60 | } | 80 | } |
61 | 81 | ||
62 | +void *kimage_map_segment(struct kimage *image, | 82 | +void *kimage_map_segment(struct kimage *image, |
63 | + unsigned long addr, unsigned long size) | 83 | + unsigned long addr, unsigned long size) |
64 | +{ | 84 | +{ |
85 | + unsigned long src_page_addr, dest_page_addr = 0; | ||
65 | + unsigned long eaddr = addr + size; | 86 | + unsigned long eaddr = addr + size; |
66 | + unsigned long src_page_addr, dest_page_addr; | 87 | + kimage_entry_t *ptr, entry; |
88 | + struct page **src_pages; | ||
67 | + unsigned int npages; | 89 | + unsigned int npages; |
68 | + struct page **src_pages; | 90 | + void *vaddr = NULL; |
69 | + int i; | 91 | + int i; |
70 | + kimage_entry_t *ptr, entry; | ||
71 | + void *vaddr = NULL; | ||
72 | + | 92 | + |
73 | + /* | 93 | + /* |
74 | + * Collect the source pages and map them in a contiguous VA range. | 94 | + * Collect the source pages and map them in a contiguous VA range. |
75 | + */ | 95 | + */ |
76 | + npages = PFN_UP(eaddr) - PFN_DOWN(addr); | 96 | + npages = PFN_UP(eaddr) - PFN_DOWN(addr); |
... | ... | ||
80 | + return NULL; | 100 | + return NULL; |
81 | + } | 101 | + } |
82 | + | 102 | + |
83 | + i = 0; | 103 | + i = 0; |
84 | + for_each_kimage_entry(image, ptr, entry) { | 104 | + for_each_kimage_entry(image, ptr, entry) { |
85 | + if (entry & IND_DESTINATION) | 105 | + if (entry & IND_DESTINATION) { |
86 | + dest_page_addr = entry & PAGE_MASK; | 106 | + dest_page_addr = entry & PAGE_MASK; |
87 | + else if (entry & IND_SOURCE) { | 107 | + } else if (entry & IND_SOURCE) { |
88 | + if (dest_page_addr >= addr && dest_page_addr < eaddr) { | 108 | + if (dest_page_addr >= addr && dest_page_addr < eaddr) { |
89 | + src_page_addr = entry & PAGE_MASK; | 109 | + src_page_addr = entry & PAGE_MASK; |
90 | + src_pages[i++] = | 110 | + src_pages[i++] = |
91 | + virt_to_page(__va(src_page_addr)); | 111 | + virt_to_page(__va(src_page_addr)); |
92 | + if (i == npages) | 112 | + if (i == npages) |
... | ... | diff view generated by jsdifflib |
1 | kexec_calculate_store_digests() calculates and stores the digest of the | 1 | The kexec_calculate_store_digests() function calculates and stores the |
---|---|---|---|
2 | segment at kexec_file_load syscall where the IMA segment is also | 2 | digest of the segment during the kexec_file_load syscall, where the |
3 | allocated. With this series, the IMA segment will be updated with the | 3 | IMA segment is also allocated. |
4 | measurement log at kexec excute stage when soft reboot is initiated. | 4 | |
5 | Therefore, it may fail digest verification in verify_sha256_digest() | 5 | With this series, the IMA segment will be updated with the measurement |
6 | after kexec soft reboot into the new kernel. Therefore, the digest | 6 | log at the kexec execute stage when a soft reboot is initiated. |
7 | Therefore, the digests should be updated for the IMA segment in the | ||
8 | normal case. | ||
9 | |||
10 | The content of memory segments carried over to the new kernel during the | ||
11 | kexec systemcall can be changed at kexec 'execute' stage, but the size | ||
12 | and the location of the memory segments cannot be changed at kexec | ||
13 | 'execute' stage. | ||
14 | |||
15 | However, during the kexec execute stage, if kexec_calculate_store_digests() | ||
16 | API is called to update the digest, it does not reuse the same memory | ||
17 | segment allocated during the kexec 'load' stage and the new memory segment | ||
18 | required cannot be transferred/mapped to the new kernel. | ||
19 | |||
20 | As a result, digest verification will fail in verify_sha256_digest() | ||
21 | after a kexec soft reboot into the new kernel. Therefore, the digest | ||
7 | calculation/verification of the IMA segment needs to be skipped. | 22 | calculation/verification of the IMA segment needs to be skipped. |
8 | 23 | ||
9 | Skip IMA segment from calculating and storing digest in function | 24 | To address this, skip the calculation and storage of the digest for the |
10 | kexec_calculate_store_digests() so that it is not added to the | 25 | IMA segment in kexec_calculate_store_digests() so that it is not added |
11 | 'purgatory_sha_regions'. | 26 | to the purgatory_sha_regions. |
12 | 27 | ||
13 | Since verify_sha256_digest() only verifies 'purgatory_sha_regions', | 28 | Since verify_sha256_digest() only verifies 'purgatory_sha_regions', |
14 | no change is needed in verify_sha256_digest() in this context. | 29 | no change is needed in verify_sha256_digest() in this context. |
15 | 30 | ||
16 | With this change, the IMA segment is not included in the digest | 31 | With this change, the IMA segment is not included in the digest |
17 | calculation, storage, and verification. | 32 | calculation, storage, and verification. |
18 | 33 | ||
19 | Author: Tushar Sugandhi <tusharsu@linux.microsoft.com> | ||
20 | Signed-off-by: Tushar Sugandhi <tusharsu@linux.microsoft.com> | 34 | Signed-off-by: Tushar Sugandhi <tusharsu@linux.microsoft.com> |
35 | Cc: Eric Biederman <ebiederm@xmission.com> | ||
36 | Cc: Baoquan He <bhe@redhat.com> | ||
37 | Cc: Vivek Goyal <vgoyal@redhat.com> | ||
38 | Cc: Dave Young <dyoung@redhat.com> | ||
21 | Signed-off-by: steven chen <chenste@linux.microsoft.com> | 39 | Signed-off-by: steven chen <chenste@linux.microsoft.com> |
40 | Reviewed-by: Stefan Berger <stefanb@linux.ibm.com> | ||
41 | Reviewed-by: Mimi Zohar <zohar@linux.ibm.com> | ||
42 | Acked-by: Baoquan He <bhe@redhat.com> | ||
22 | --- | 43 | --- |
23 | include/linux/kexec.h | 3 +++ | 44 | include/linux/kexec.h | 3 +++ |
24 | kernel/kexec_file.c | 23 +++++++++++++++++++++++ | 45 | kernel/kexec_file.c | 22 ++++++++++++++++++++++ |
25 | security/integrity/ima/ima_kexec.c | 3 +++ | 46 | security/integrity/ima/ima_kexec.c | 3 +++ |
26 | 3 files changed, 29 insertions(+) | 47 | 3 files changed, 28 insertions(+) |
27 | 48 | ||
28 | diff --git a/include/linux/kexec.h b/include/linux/kexec.h | 49 | diff --git a/include/linux/kexec.h b/include/linux/kexec.h |
29 | index XXXXXXX..XXXXXXX 100644 | 50 | index XXXXXXX..XXXXXXX 100644 |
30 | --- a/include/linux/kexec.h | 51 | --- a/include/linux/kexec.h |
31 | +++ b/include/linux/kexec.h | 52 | +++ b/include/linux/kexec.h |
... | ... | ||
46 | @@ -XXX,XX +XXX,XX @@ void set_kexec_sig_enforced(void) | 67 | @@ -XXX,XX +XXX,XX @@ void set_kexec_sig_enforced(void) |
47 | } | 68 | } |
48 | #endif | 69 | #endif |
49 | 70 | ||
50 | +#ifdef CONFIG_IMA_KEXEC | 71 | +#ifdef CONFIG_IMA_KEXEC |
51 | +static bool check_ima_segment_index(struct kimage *image, int i) | 72 | +static bool check_ima_segment_index(struct kimage *image, int i) |
52 | +{ | 73 | +{ |
53 | + if (image->is_ima_segment_index_set && | 74 | + if (image->is_ima_segment_index_set && i == image->ima_segment_index) |
54 | + i == image->ima_segment_index) | ||
55 | + return true; | 75 | + return true; |
56 | + else | 76 | + else |
57 | + return false; | 77 | + return false; |
58 | +} | 78 | +} |
59 | +#else | 79 | +#else |
... | ... | ||
97 | image->ima_buffer_size = kexec_segment_size; | 117 | image->ima_buffer_size = kexec_segment_size; |
98 | image->ima_buffer = kexec_buffer; | 118 | image->ima_buffer = kexec_buffer; |
99 | + image->ima_segment_index = image->nr_segments - 1; | 119 | + image->ima_segment_index = image->nr_segments - 1; |
100 | + image->is_ima_segment_index_set = true; | 120 | + image->is_ima_segment_index_set = true; |
101 | 121 | ||
102 | /* | 122 | kexec_dprintk("kexec measurement buffer for the loaded kernel at 0x%lx.\n", |
103 | * kexec owns kexec_buffer after kexec_add_buffer() is called | 123 | kbuf.mem); |
104 | -- | 124 | -- |
105 | 2.25.1 | 125 | 2.25.1 | diff view generated by jsdifflib |
1 | IMA log is copied to the new Kernel during kexec 'load' using | 1 | The IMA log is currently copied to the new kernel during kexec 'load' |
---|---|---|---|
2 | ima_dump_measurement_list(). The log copy at kexec 'load' may result in | 2 | using ima_dump_measurement_list(). However, the log copied at kexec |
3 | loss of IMA measurements during kexec soft reboot. It needs to be copied | 3 | 'load' may result in loss of IMA measurements that only occurred after |
4 | over during kexec 'execute'. Setup the needed infrastructure to move the | 4 | kexec "load'. Therefore, the log needs to be copied during kexec |
5 | IMA log copy from kexec 'load' to 'execute'. | 5 | 'execute'. Setup the needed infrastructure to move the IMA log copy from |
6 | kexec 'load' to 'execute'. | ||
6 | 7 | ||
7 | Define a new IMA hook ima_update_kexec_buffer() as a stub function. | 8 | Define a new IMA hook ima_update_kexec_buffer() as a stub function. |
8 | It will be used to call ima_dump_measurement_list() during kexec | 9 | It will be used to call ima_dump_measurement_list() during kexec 'execute'. |
9 | 'execute'. | ||
10 | 10 | ||
11 | Implement ima_kexec_post_load() function to be invoked after the new | 11 | Implement ima_kexec_post_load() function to be invoked after the new |
12 | Kernel image has been loaded for kexec. ima_kexec_post_load() maps the | 12 | Kernel image has been loaded for kexec. ima_kexec_post_load() maps the |
13 | IMA buffer to a segment in the newly loaded Kernel. It also registers | 13 | IMA buffer to a segment in the newly loaded Kernel. It also registers |
14 | the reboot notifier_block to trigger ima_update_kexec_buffer() at | 14 | the reboot notifier_block to trigger ima_update_kexec_buffer() at |
15 | exec 'execute'. | 15 | kexec 'execute'. |
16 | 16 | ||
17 | Author: Tushar Sugandhi <tusharsu@linux.microsoft.com> | 17 | Set the priority of register_reboot_notifier to INT_MIN to ensure that the |
18 | IMA log copy operation will happen at the end of the operation chain, which | ||
19 | is crucial for maintaining the integrity of the logs | ||
20 | |||
21 | Signed-off-by: Tushar Sugandhi <tusharsu@linux.microsoft.com> | ||
22 | Cc: Eric Biederman <ebiederm@xmission.com> | ||
23 | Cc: Baoquan He <bhe@redhat.com> | ||
24 | Cc: Vivek Goyal <vgoyal@redhat.com> | ||
25 | Cc: Dave Young <dyoung@redhat.com> | ||
26 | Signed-off-by: steven chen <chenste@linux.microsoft.com> | ||
18 | Reviewed-by: Stefan Berger <stefanb@linux.ibm.com> | 27 | Reviewed-by: Stefan Berger <stefanb@linux.ibm.com> |
19 | Suggested-by: Mimi Zohar <zohar@linux.ibm.com> | ||
20 | Reviewed-by: "Petr Tesařík" <petr@tesarici.cz> | ||
21 | Signed-off-by: Tushar Sugandhi <tusharsu@linux.microsoft.com> | ||
22 | Signed-off-by: steven chen <chenste@linux.microsoft.com> | ||
23 | --- | 28 | --- |
24 | include/linux/ima.h | 3 ++ | 29 | include/linux/ima.h | 3 ++ |
25 | security/integrity/ima/ima_kexec.c | 46 ++++++++++++++++++++++++++++++ | 30 | security/integrity/ima/ima_kexec.c | 47 ++++++++++++++++++++++++++++++ |
26 | 2 files changed, 49 insertions(+) | 31 | 2 files changed, 50 insertions(+) |
27 | 32 | ||
28 | diff --git a/include/linux/ima.h b/include/linux/ima.h | 33 | diff --git a/include/linux/ima.h b/include/linux/ima.h |
29 | index XXXXXXX..XXXXXXX 100644 | 34 | index XXXXXXX..XXXXXXX 100644 |
30 | --- a/include/linux/ima.h | 35 | --- a/include/linux/ima.h |
31 | +++ b/include/linux/ima.h | 36 | +++ b/include/linux/ima.h |
... | ... | ||
50 | +#include <linux/reboot.h> | 55 | +#include <linux/reboot.h> |
51 | +#include <asm/page.h> | 56 | +#include <asm/page.h> |
52 | #include "ima.h" | 57 | #include "ima.h" |
53 | 58 | ||
54 | #ifdef CONFIG_IMA_KEXEC | 59 | #ifdef CONFIG_IMA_KEXEC |
60 | +static bool ima_kexec_update_registered; | ||
55 | static struct seq_file ima_kexec_file; | 61 | static struct seq_file ima_kexec_file; |
56 | +static void *ima_kexec_buffer; | 62 | +static void *ima_kexec_buffer; |
57 | +static bool ima_kexec_update_registered; | 63 | |
58 | 64 | static void ima_free_kexec_file_buf(struct seq_file *sf) | |
59 | static void ima_reset_kexec_file(struct seq_file *sf) | ||
60 | { | 65 | { |
61 | @@ -XXX,XX +XXX,XX @@ void ima_add_kexec_buffer(struct kimage *image) | 66 | @@ -XXX,XX +XXX,XX @@ void ima_add_kexec_buffer(struct kimage *image) |
62 | kexec_dprintk("kexec measurement buffer for the loaded kernel at 0x%lx.\n", | 67 | kexec_dprintk("kexec measurement buffer for the loaded kernel at 0x%lx.\n", |
63 | kbuf.mem); | 68 | kbuf.mem); |
64 | } | 69 | } |
... | ... | ||
70 | + unsigned long action, void *data) | 75 | + unsigned long action, void *data) |
71 | +{ | 76 | +{ |
72 | + return NOTIFY_OK; | 77 | + return NOTIFY_OK; |
73 | +} | 78 | +} |
74 | + | 79 | + |
75 | +struct notifier_block update_buffer_nb = { | 80 | +static struct notifier_block update_buffer_nb = { |
76 | + .notifier_call = ima_update_kexec_buffer, | 81 | + .notifier_call = ima_update_kexec_buffer, |
82 | + .priority = INT_MIN | ||
77 | +}; | 83 | +}; |
78 | + | 84 | + |
79 | +/* | 85 | +/* |
80 | + * Create a mapping for the source pages that contain the IMA buffer | 86 | + * Create a mapping for the source pages that contain the IMA buffer |
81 | + * so we can update it later. | 87 | + * so we can update it later. |
... | ... | ||
107 | #endif /* IMA_KEXEC */ | 113 | #endif /* IMA_KEXEC */ |
108 | 114 | ||
109 | /* | 115 | /* |
110 | -- | 116 | -- |
111 | 2.25.1 | 117 | 2.25.1 |
112 | diff view generated by jsdifflib |
1 | ima_dump_measurement_list() is called during kexec 'load', which may | 1 | ima_dump_measurement_list() is called during kexec 'load', which may |
---|---|---|---|
2 | result in loss of IMA measurements during kexec soft reboot. It needs | 2 | result in loss of IMA measurements during kexec soft reboot. Due to |
3 | to be called during kexec 'execute'. | 3 | missed measurements that only occurred after kexec 'load', this function |
4 | needs to be called during kexec 'execute'. | ||
4 | 5 | ||
5 | This patch includes the following changes: | 6 | Make the kexec_segment_size variable a local static variable within the |
6 | - Implement kimage_file_post_load() function to be invoked after the new | 7 | file, so it can be accessed during both kexec 'load' and 'execute'. |
7 | Kernel image has been loaded for kexec. | 8 | |
8 | - Call kimage_file_post_load() from kexec_file_load() syscall only for | 9 | Implement the kexec_post_load() function to be invoked after the new kernel |
9 | kexec soft reboot scenarios and not for KEXEC_FILE_ON_CRASH. It will | 10 | image has been loaded for kexec. Instead of calling machine_kexec_post_load() |
10 | map the IMA segment, and register reboot notifier for the function | 11 | directly from the kexec_file_load() syscall, call kexec_post_load(), which in |
11 | ima_update_kexec_buffer() which would copy the IMA log at kexec soft | 12 | turn calls machine_kexec_post_load() to maintain the original image processing. |
12 | reboot. | 13 | |
13 | - Make kexec_segment_size variable local static to the file, for it to be | 14 | Invoke ima_kexec_post_load() within the kexec_post_load() API only for kexec |
14 | accessible both during kexec 'load' and 'execute'. | 15 | soft reboot scenarios, excluding KEXEC_FILE_ON_CRASH. |
15 | - Move ima_dump_measurement_list() call from ima_add_kexec_buffer() | 16 | |
16 | to ima_update_kexec_buffer(). | 17 | Register a reboot notifier for the ima_update_kexec_buffer() API within |
17 | - Remove ima_reset_kexec_file() call from ima_add_kexec_buffer(), now | 18 | ima_kexec_post_load() to ensure it is called upon receiving a reboot |
18 | that the buffer is being copied at kexec 'execute', and resetting the | 19 | notification. |
19 | file at kexec 'load' will corrupt the buffer. | 20 | |
21 | Move the ima_dump_measurement_list() call from ima_add_kexec_buffer() to | ||
22 | ima_update_kexec_buffer() to copy the IMA log at the kexec 'execute' stage. | ||
23 | |||
24 | When there is insufficient memory to copy all the measurement logs, copy as | ||
25 | much of the measurement list as possible. | ||
20 | 26 | ||
21 | Author: Tushar Sugandhi <tusharsu@linux.microsoft.com> | ||
22 | Reviewed-by: Tyler Hicks <code@tyhicks.com> | ||
23 | Signed-off-by: Tushar Sugandhi <tusharsu@linux.microsoft.com> | 27 | Signed-off-by: Tushar Sugandhi <tusharsu@linux.microsoft.com> |
28 | Cc: Eric Biederman <ebiederm@xmission.com> | ||
29 | Cc: Baoquan He <bhe@redhat.com> | ||
30 | Cc: Vivek Goyal <vgoyal@redhat.com> | ||
31 | Cc: Dave Young <dyoung@redhat.com> | ||
24 | Signed-off-by: steven chen <chenste@linux.microsoft.com> | 32 | Signed-off-by: steven chen <chenste@linux.microsoft.com> |
33 | Reviewed-by: Stefan Berger <stefanb@linux.ibm.com> | ||
25 | --- | 34 | --- |
26 | kernel/kexec_file.c | 8 ++++++ | 35 | kernel/kexec_file.c | 11 +++++++- |
27 | security/integrity/ima/ima_kexec.c | 43 +++++++++++++++++++----------- | 36 | security/integrity/ima/ima_kexec.c | 43 ++++++++++++++++++++---------- |
28 | 2 files changed, 36 insertions(+), 15 deletions(-) | 37 | 2 files changed, 39 insertions(+), 15 deletions(-) |
29 | 38 | ||
30 | diff --git a/kernel/kexec_file.c b/kernel/kexec_file.c | 39 | diff --git a/kernel/kexec_file.c b/kernel/kexec_file.c |
31 | index XXXXXXX..XXXXXXX 100644 | 40 | index XXXXXXX..XXXXXXX 100644 |
32 | --- a/kernel/kexec_file.c | 41 | --- a/kernel/kexec_file.c |
33 | +++ b/kernel/kexec_file.c | 42 | +++ b/kernel/kexec_file.c |
34 | @@ -XXX,XX +XXX,XX @@ kimage_validate_signature(struct kimage *image) | 43 | @@ -XXX,XX +XXX,XX @@ kimage_validate_signature(struct kimage *image) |
35 | } | 44 | } |
36 | #endif | 45 | #endif |
37 | 46 | ||
38 | +static void kimage_file_post_load(struct kimage *image) | 47 | +static int kexec_post_load(struct kimage *image, unsigned long flags) |
39 | +{ | 48 | +{ |
40 | + ima_kexec_post_load(image); | 49 | +#ifdef CONFIG_IMA_KEXEC |
50 | + if (!(flags & KEXEC_FILE_ON_CRASH)) | ||
51 | + ima_kexec_post_load(image); | ||
52 | +#endif | ||
53 | + return machine_kexec_post_load(image); | ||
41 | +} | 54 | +} |
42 | + | 55 | + |
43 | /* | 56 | /* |
44 | * In file mode list of segments is prepared by kernel. Copy relevant | 57 | * In file mode list of segments is prepared by kernel. Copy relevant |
45 | * data from user space, do error checking, prepare segment list | 58 | * data from user space, do error checking, prepare segment list |
46 | @@ -XXX,XX +XXX,XX @@ SYSCALL_DEFINE5(kexec_file_load, int, kernel_fd, int, initrd_fd, | 59 | @@ -XXX,XX +XXX,XX @@ SYSCALL_DEFINE5(kexec_file_load, int, kernel_fd, int, initrd_fd, |
47 | 60 | ||
48 | kimage_terminate(image); | 61 | kimage_terminate(image); |
49 | 62 | ||
50 | + if (!(flags & KEXEC_FILE_ON_CRASH)) | 63 | - ret = machine_kexec_post_load(image); |
51 | + kimage_file_post_load(image); | 64 | + ret = kexec_post_load(image, flags); |
52 | + | ||
53 | ret = machine_kexec_post_load(image); | ||
54 | if (ret) | 65 | if (ret) |
55 | goto out; | 66 | goto out; |
67 | |||
56 | diff --git a/security/integrity/ima/ima_kexec.c b/security/integrity/ima/ima_kexec.c | 68 | diff --git a/security/integrity/ima/ima_kexec.c b/security/integrity/ima/ima_kexec.c |
57 | index XXXXXXX..XXXXXXX 100644 | 69 | index XXXXXXX..XXXXXXX 100644 |
58 | --- a/security/integrity/ima/ima_kexec.c | 70 | --- a/security/integrity/ima/ima_kexec.c |
59 | +++ b/security/integrity/ima/ima_kexec.c | 71 | +++ b/security/integrity/ima/ima_kexec.c |
60 | @@ -XXX,XX +XXX,XX @@ | 72 | @@ -XXX,XX +XXX,XX @@ |
61 | #ifdef CONFIG_IMA_KEXEC | 73 | #ifdef CONFIG_IMA_KEXEC |
74 | static bool ima_kexec_update_registered; | ||
62 | static struct seq_file ima_kexec_file; | 75 | static struct seq_file ima_kexec_file; |
76 | +static size_t kexec_segment_size; | ||
63 | static void *ima_kexec_buffer; | 77 | static void *ima_kexec_buffer; |
64 | +static size_t kexec_segment_size; | 78 | |
65 | static bool ima_kexec_update_registered; | 79 | static void ima_free_kexec_file_buf(struct seq_file *sf) |
66 | 80 | @@ -XXX,XX +XXX,XX @@ static int ima_dump_measurement_list(unsigned long *buffer_size, void **buffer, | |
67 | static void ima_reset_kexec_file(struct seq_file *sf) | 81 | } |
82 | } | ||
83 | |||
84 | - if (ret < 0) | ||
85 | - goto out; | ||
86 | - | ||
87 | /* | ||
88 | * fill in reserved space with some buffer details | ||
89 | * (eg. version, buffer size, number of measurements) | ||
90 | @@ -XXX,XX +XXX,XX @@ static int ima_dump_measurement_list(unsigned long *buffer_size, void **buffer, | ||
91 | |||
92 | *buffer_size = ima_kexec_file.count; | ||
93 | *buffer = ima_kexec_file.buf; | ||
94 | -out: | ||
95 | + | ||
96 | return ret; | ||
97 | } | ||
98 | |||
68 | @@ -XXX,XX +XXX,XX @@ void ima_add_kexec_buffer(struct kimage *image) | 99 | @@ -XXX,XX +XXX,XX @@ void ima_add_kexec_buffer(struct kimage *image) |
100 | unsigned long binary_runtime_size; | ||
101 | |||
69 | /* use more understandable variable names than defined in kbuf */ | 102 | /* use more understandable variable names than defined in kbuf */ |
103 | + size_t kexec_buffer_size = 0; | ||
70 | void *kexec_buffer = NULL; | 104 | void *kexec_buffer = NULL; |
71 | size_t kexec_buffer_size = 0; | 105 | - size_t kexec_buffer_size; |
72 | - size_t kexec_segment_size; | 106 | - size_t kexec_segment_size; |
73 | int ret; | 107 | int ret; |
74 | 108 | ||
75 | /* | 109 | /* |
76 | @@ -XXX,XX +XXX,XX @@ void ima_add_kexec_buffer(struct kimage *image) | 110 | @@ -XXX,XX +XXX,XX @@ void ima_add_kexec_buffer(struct kimage *image) |
77 | return; | 111 | return; |
78 | } | 112 | } |
79 | 113 | ||
80 | - ret = ima_dump_measurement_list(&kexec_buffer_size, &kexec_buffer, | 114 | - ima_dump_measurement_list(&kexec_buffer_size, &kexec_buffer, |
81 | - kexec_segment_size); | 115 | - kexec_segment_size); |
82 | - if (ret < 0) { | 116 | - if (!kexec_buffer) { |
83 | - pr_err("Failed to dump IMA measurements. Error:%d.\n", ret); | 117 | - pr_err("Not enough memory for the kexec measurement buffer.\n"); |
84 | - return; | 118 | - return; |
85 | - } | 119 | - } |
86 | - | 120 | - |
87 | kbuf.buffer = kexec_buffer; | 121 | kbuf.buffer = kexec_buffer; |
88 | kbuf.bufsz = kexec_buffer_size; | 122 | kbuf.bufsz = kexec_buffer_size; |
89 | kbuf.memsz = kexec_segment_size; | 123 | kbuf.memsz = kexec_segment_size; |
90 | @@ -XXX,XX +XXX,XX @@ void ima_add_kexec_buffer(struct kimage *image) | 124 | @@ -XXX,XX +XXX,XX @@ void ima_add_kexec_buffer(struct kimage *image) |
91 | image->ima_segment_index = image->nr_segments - 1; | ||
92 | image->is_ima_segment_index_set = true; | ||
93 | |||
94 | - /* | ||
95 | - * kexec owns kexec_buffer after kexec_add_buffer() is called | ||
96 | - * and it will vfree() that buffer. | ||
97 | - */ | ||
98 | - ima_reset_kexec_file(&ima_kexec_file); | ||
99 | - | ||
100 | kexec_dprintk("kexec measurement buffer for the loaded kernel at 0x%lx.\n", | ||
101 | kbuf.mem); | ||
102 | } | ||
103 | @@ -XXX,XX +XXX,XX @@ void ima_add_kexec_buffer(struct kimage *image) | ||
104 | static int ima_update_kexec_buffer(struct notifier_block *self, | 125 | static int ima_update_kexec_buffer(struct notifier_block *self, |
105 | unsigned long action, void *data) | 126 | unsigned long action, void *data) |
106 | { | 127 | { |
107 | - return NOTIFY_OK; | 128 | - return NOTIFY_OK; |
108 | + void *buf = NULL; | ||
109 | + size_t buf_size = 0; | 129 | + size_t buf_size = 0; |
110 | + int ret = NOTIFY_OK; | 130 | + int ret = NOTIFY_OK; |
131 | + void *buf = NULL; | ||
111 | + | 132 | + |
112 | + if (!kexec_in_progress) { | 133 | + if (!kexec_in_progress) { |
113 | + pr_info("No kexec in progress.\n"); | 134 | + pr_info("No kexec in progress.\n"); |
114 | + return ret; | 135 | + return ret; |
115 | + } | 136 | + } |
116 | + | 137 | + |
117 | + if (!ima_kexec_buffer) { | 138 | + if (!ima_kexec_buffer) { |
118 | + pr_err("Kexec buffer not set.\n"); | 139 | + pr_err("Kexec buffer not set.\n"); |
119 | + return ret; | 140 | + return ret; |
120 | + } | 141 | + } |
121 | + | 142 | + |
122 | + ret = ima_dump_measurement_list(&buf_size, &buf, | 143 | + ret = ima_dump_measurement_list(&buf_size, &buf, kexec_segment_size); |
123 | + kexec_segment_size); | ||
124 | + | 144 | + |
125 | + if (ret) | 145 | + if (ret) |
126 | + pr_err("Dump measurements failed. Error:%d\n", ret); | 146 | + pr_err("Dump measurements failed. Error:%d\n", ret); |
127 | + | 147 | + |
128 | + if (buf_size != 0) | 148 | + if (buf_size != 0) |
... | ... | ||
132 | + ima_kexec_buffer = NULL; | 152 | + ima_kexec_buffer = NULL; |
133 | + | 153 | + |
134 | + return ret; | 154 | + return ret; |
135 | } | 155 | } |
136 | 156 | ||
137 | struct notifier_block update_buffer_nb = { | 157 | static struct notifier_block update_buffer_nb = { |
138 | -- | 158 | -- |
139 | 2.25.1 | 159 | 2.25.1 | diff view generated by jsdifflib |
New patch | |||
---|---|---|---|
1 | kexec 'load' may be called multiple times. Free and realloc the buffer | ||
2 | only if the segment_size is changed from the previous kexec 'load' call. | ||
1 | 3 | ||
4 | Signed-off-by: steven chen <chenste@linux.microsoft.com> | ||
5 | --- | ||
6 | security/integrity/ima/ima_kexec.c | 10 ++++++++++ | ||
7 | 1 file changed, 10 insertions(+) | ||
8 | |||
9 | diff --git a/security/integrity/ima/ima_kexec.c b/security/integrity/ima/ima_kexec.c | ||
10 | index XXXXXXX..XXXXXXX 100644 | ||
11 | --- a/security/integrity/ima/ima_kexec.c | ||
12 | +++ b/security/integrity/ima/ima_kexec.c | ||
13 | @@ -XXX,XX +XXX,XX @@ static void ima_free_kexec_file_buf(struct seq_file *sf) | ||
14 | |||
15 | static int ima_alloc_kexec_file_buf(size_t segment_size) | ||
16 | { | ||
17 | + /* | ||
18 | + * kexec 'load' may be called multiple times. | ||
19 | + * Free and realloc the buffer only if the segment_size is | ||
20 | + * changed from the previous kexec 'load' call. | ||
21 | + */ | ||
22 | + if (ima_kexec_file.buf && ima_kexec_file.size == segment_size) | ||
23 | + goto out; | ||
24 | + | ||
25 | ima_free_kexec_file_buf(&ima_kexec_file); | ||
26 | |||
27 | /* segment size can't change between kexec load and execute */ | ||
28 | @@ -XXX,XX +XXX,XX @@ static int ima_alloc_kexec_file_buf(size_t segment_size) | ||
29 | return -ENOMEM; | ||
30 | |||
31 | ima_kexec_file.size = segment_size; | ||
32 | + | ||
33 | +out: | ||
34 | ima_kexec_file.read_pos = 0; | ||
35 | ima_kexec_file.count = sizeof(struct ima_kexec_hdr); /* reserved space */ | ||
36 | |||
37 | -- | ||
38 | 2.25.1 | diff view generated by jsdifflib |
... | ... | ||
---|---|---|---|
8 | for the additional measurements. | 8 | for the additional measurements. |
9 | 9 | ||
10 | Update ima_add_kexec_buffer() function to allocate memory based on the | 10 | Update ima_add_kexec_buffer() function to allocate memory based on the |
11 | Kconfig option value, rather than the currently hard-coded one. | 11 | Kconfig option value, rather than the currently hard-coded one. |
12 | 12 | ||
13 | From: Tushar Sugandhi <tusharsu@linux.microsoft.com> | ||
14 | Author: Tushar Sugandhi <tusharsu@linux.microsoft.com> | ||
15 | Suggested-by: Stefan Berger <stefanb@linux.ibm.com> | 13 | Suggested-by: Stefan Berger <stefanb@linux.ibm.com> |
16 | Signed-off-by: Tushar Sugandhi <tusharsu@linux.microsoft.com> | 14 | Signed-off-by: Tushar Sugandhi <tusharsu@linux.microsoft.com> |
17 | Signed-off-by: steven chen <chenste@linux.microsoft.com> | 15 | Signed-off-by: steven chen <chenste@linux.microsoft.com> |
16 | Reviewed-by: Stefan Berger <stefanb@linux.ibm.com> | ||
17 | Reviewed-by: Mimi Zohar <zohar@linux.ibm.com> | ||
18 | --- | 18 | --- |
19 | security/integrity/ima/Kconfig | 10 ++++++++++ | 19 | security/integrity/ima/Kconfig | 10 ++++++++++ |
20 | security/integrity/ima/ima_kexec.c | 16 ++++++++++------ | 20 | security/integrity/ima/ima_kexec.c | 16 +++++++++++----- |
21 | 2 files changed, 20 insertions(+), 6 deletions(-) | 21 | 2 files changed, 21 insertions(+), 5 deletions(-) |
22 | 22 | ||
23 | diff --git a/security/integrity/ima/Kconfig b/security/integrity/ima/Kconfig | 23 | diff --git a/security/integrity/ima/Kconfig b/security/integrity/ima/Kconfig |
24 | index XXXXXXX..XXXXXXX 100644 | 24 | index XXXXXXX..XXXXXXX 100644 |
25 | --- a/security/integrity/ima/Kconfig | 25 | --- a/security/integrity/ima/Kconfig |
26 | +++ b/security/integrity/ima/Kconfig | 26 | +++ b/security/integrity/ima/Kconfig |
... | ... | ||
33 | + depends on IMA_KEXEC | 33 | + depends on IMA_KEXEC |
34 | + default 0 | 34 | + default 0 |
35 | + help | 35 | + help |
36 | + IMA_KEXEC_EXTRA_MEMORY_KB determines the extra memory to be | 36 | + IMA_KEXEC_EXTRA_MEMORY_KB determines the extra memory to be |
37 | + allocated (in kb) for IMA measurements added during kexec soft reboot. | 37 | + allocated (in kb) for IMA measurements added during kexec soft reboot. |
38 | + If set to the default value, an extra half a page of memory for those | 38 | + If set to the default value of 0, an extra half page of memory for those |
39 | + additional measurements will be allocated. | 39 | + additional measurements will be allocated. |
40 | + | 40 | + |
41 | endif | 41 | endif |
42 | diff --git a/security/integrity/ima/ima_kexec.c b/security/integrity/ima/ima_kexec.c | 42 | diff --git a/security/integrity/ima/ima_kexec.c b/security/integrity/ima/ima_kexec.c |
43 | index XXXXXXX..XXXXXXX 100644 | 43 | index XXXXXXX..XXXXXXX 100644 |
44 | --- a/security/integrity/ima/ima_kexec.c | 44 | --- a/security/integrity/ima/ima_kexec.c |
45 | +++ b/security/integrity/ima/ima_kexec.c | 45 | +++ b/security/integrity/ima/ima_kexec.c |
46 | @@ -XXX,XX +XXX,XX @@ void ima_add_kexec_buffer(struct kimage *image) | 46 | @@ -XXX,XX +XXX,XX @@ void ima_add_kexec_buffer(struct kimage *image) |
47 | .buf_min = 0, .buf_max = ULONG_MAX, | 47 | .buf_min = 0, .buf_max = ULONG_MAX, |
48 | .top_down = true }; | 48 | .top_down = true }; |
49 | unsigned long binary_runtime_size; | 49 | unsigned long binary_runtime_size; |
50 | - | 50 | + unsigned long extra_memory; |
51 | + unsigned long extra_size; | 51 | |
52 | /* use more understandable variable names than defined in kbuf */ | 52 | /* use more understandable variable names than defined in kbuf */ |
53 | void *kexec_buffer = NULL; | ||
54 | size_t kexec_buffer_size = 0; | 53 | size_t kexec_buffer_size = 0; |
54 | @@ -XXX,XX +XXX,XX @@ void ima_add_kexec_buffer(struct kimage *image) | ||
55 | int ret; | 55 | int ret; |
56 | 56 | ||
57 | /* | 57 | /* |
58 | - * Reserve an extra half page of memory for additional measurements | 58 | - * Reserve an extra half page of memory for additional measurements |
59 | - * added during the kexec load. | 59 | - * added during the kexec load. |
60 | + * Reserve extra memory for measurements added during kexec. | 60 | + * Reserve extra memory for measurements added during kexec. |
61 | */ | 61 | */ |
62 | - binary_runtime_size = ima_get_binary_runtime_size(); | 62 | - binary_runtime_size = ima_get_binary_runtime_size(); |
63 | + if (CONFIG_IMA_KEXEC_EXTRA_MEMORY_KB <= 0) | 63 | + if (CONFIG_IMA_KEXEC_EXTRA_MEMORY_KB <= 0) |
64 | + extra_size = PAGE_SIZE / 2; | 64 | + extra_memory = PAGE_SIZE / 2; |
65 | + else | 65 | + else |
66 | + extra_size = CONFIG_IMA_KEXEC_EXTRA_MEMORY_KB * 1024; | 66 | + extra_memory = CONFIG_IMA_KEXEC_EXTRA_MEMORY_KB * 1024; |
67 | + binary_runtime_size = ima_get_binary_runtime_size() + extra_size; | 67 | + |
68 | + binary_runtime_size = ima_get_binary_runtime_size() + extra_memory; | ||
68 | + | 69 | + |
69 | if (binary_runtime_size >= ULONG_MAX - PAGE_SIZE) | 70 | if (binary_runtime_size >= ULONG_MAX - PAGE_SIZE) |
70 | kexec_segment_size = ULONG_MAX; | 71 | kexec_segment_size = ULONG_MAX; |
71 | else | 72 | else |
72 | - kexec_segment_size = ALIGN(ima_get_binary_runtime_size() + | 73 | - kexec_segment_size = ALIGN(ima_get_binary_runtime_size() + |
... | ... | diff view generated by jsdifflib |
... | ... | ||
---|---|---|---|
15 | reboot, and not a cold-boot. And the absence of 'kexec_execute' event | 15 | reboot, and not a cold-boot. And the absence of 'kexec_execute' event |
16 | after kexec soft reboot implies missing events in that window which | 16 | after kexec soft reboot implies missing events in that window which |
17 | results in inconsistency with TPM PCR quotes, necessitating a cold boot | 17 | results in inconsistency with TPM PCR quotes, necessitating a cold boot |
18 | for a successful remote attestation. | 18 | for a successful remote attestation. |
19 | 19 | ||
20 | Reviewed-by: Stefan Berger <stefanb@linux.ibm.com> | 20 | These critical data events are displayed as hex encoded ascii in the |
21 | Author: Tushar Sugandhi <tusharsu@linux.microsoft.com> | 21 | ascii_runtime_measurement_list. Verifying the critical data hash requires |
22 | calculating the hash of the decoded ascii string. | ||
23 | |||
24 | For example, to verify the 'kexec_load' data hash: | ||
25 | |||
26 | sudo cat /sys/kernel/security/integrity/ima/ascii_runtime_measurements | ||
27 | | grep kexec_load | cut -d' ' -f 6 | xxd -r -p | sha256sum | ||
28 | |||
29 | |||
30 | To verify the 'kexec_execute' data hash: | ||
31 | |||
32 | sudo cat /sys/kernel/security/integrity/ima/ascii_runtime_measurements | ||
33 | | grep kexec_execute | cut -d' ' -f 6 | xxd -r -p | sha256sum | ||
34 | |||
22 | Signed-off-by: Tushar Sugandhi <tusharsu@linux.microsoft.com> | 35 | Signed-off-by: Tushar Sugandhi <tusharsu@linux.microsoft.com> |
23 | Signed-off-by: steven chen <chenste@linux.microsoft.com> | 36 | Signed-off-by: steven chen <chenste@linux.microsoft.com> |
37 | Reviewed-by: Stefan Berger <stefanb@linux.ibm.com> | ||
24 | --- | 38 | --- |
25 | security/integrity/ima/ima_kexec.c | 23 +++++++++++++++++++++++ | 39 | security/integrity/ima/ima.h | 6 ++++++ |
26 | 1 file changed, 23 insertions(+) | 40 | security/integrity/ima/ima_kexec.c | 21 +++++++++++++++++++++ |
41 | security/integrity/ima/ima_queue.c | 5 +++++ | ||
42 | 3 files changed, 32 insertions(+) | ||
27 | 43 | ||
44 | diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h | ||
45 | index XXXXXXX..XXXXXXX 100644 | ||
46 | --- a/security/integrity/ima/ima.h | ||
47 | +++ b/security/integrity/ima/ima.h | ||
48 | @@ -XXX,XX +XXX,XX @@ void ima_post_key_create_or_update(struct key *keyring, struct key *key, | ||
49 | unsigned long flags, bool create); | ||
50 | #endif | ||
51 | |||
52 | +#ifdef CONFIG_IMA_KEXEC | ||
53 | +void ima_measure_kexec_event(const char *event_name); | ||
54 | +#else | ||
55 | +static inline void ima_measure_kexec_event(const char *event_name) {} | ||
56 | +#endif | ||
57 | + | ||
58 | /* | ||
59 | * The default binary_runtime_measurements list format is defined as the | ||
60 | * platform native format. The canonical format is defined as little-endian. | ||
28 | diff --git a/security/integrity/ima/ima_kexec.c b/security/integrity/ima/ima_kexec.c | 61 | diff --git a/security/integrity/ima/ima_kexec.c b/security/integrity/ima/ima_kexec.c |
29 | index XXXXXXX..XXXXXXX 100644 | 62 | index XXXXXXX..XXXXXXX 100644 |
30 | --- a/security/integrity/ima/ima_kexec.c | 63 | --- a/security/integrity/ima/ima_kexec.c |
31 | +++ b/security/integrity/ima/ima_kexec.c | 64 | +++ b/security/integrity/ima/ima_kexec.c |
32 | @@ -XXX,XX +XXX,XX @@ | 65 | @@ -XXX,XX +XXX,XX @@ |
33 | #include "ima.h" | 66 | #include "ima.h" |
34 | 67 | ||
35 | #ifdef CONFIG_IMA_KEXEC | 68 | #ifdef CONFIG_IMA_KEXEC |
36 | +#define IMA_KEXEC_EVENT_LEN 256 | 69 | +#define IMA_KEXEC_EVENT_LEN 256 |
37 | + | 70 | + |
71 | static bool ima_kexec_update_registered; | ||
38 | static struct seq_file ima_kexec_file; | 72 | static struct seq_file ima_kexec_file; |
39 | static void *ima_kexec_buffer; | ||
40 | static size_t kexec_segment_size; | 73 | static size_t kexec_segment_size; |
41 | @@ -XXX,XX +XXX,XX @@ static void ima_free_kexec_file_buf(struct seq_file *sf) | 74 | @@ -XXX,XX +XXX,XX @@ static void ima_free_kexec_file_buf(struct seq_file *sf) |
42 | ima_reset_kexec_file(sf); | 75 | sf->count = 0; |
43 | } | 76 | } |
44 | 77 | ||
45 | +static void ima_measure_kexec_event(const char *event_name) | 78 | +void ima_measure_kexec_event(const char *event_name) |
46 | +{ | 79 | +{ |
47 | + char ima_kexec_event[IMA_KEXEC_EVENT_LEN]; | 80 | + char ima_kexec_event[IMA_KEXEC_EVENT_LEN]; |
48 | + size_t buf_size = 0; | 81 | + size_t buf_size = 0; |
49 | + long len; | 82 | + long len; |
83 | + int n; | ||
50 | + | 84 | + |
51 | + buf_size = ima_get_binary_runtime_size(); | 85 | + buf_size = ima_get_binary_runtime_size(); |
52 | + len = atomic_long_read(&ima_htable.len); | 86 | + len = atomic_long_read(&ima_htable.len); |
53 | + | 87 | + |
54 | + scnprintf(ima_kexec_event, IMA_KEXEC_EVENT_LEN, | 88 | + n = scnprintf(ima_kexec_event, IMA_KEXEC_EVENT_LEN, |
55 | + "kexec_segment_size=%lu;ima_binary_runtime_size=%lu;" | 89 | + "kexec_segment_size=%lu;ima_binary_runtime_size=%lu;" |
56 | + "ima_runtime_measurements_count=%ld;", | 90 | + "ima_runtime_measurements_count=%ld;", |
57 | + kexec_segment_size, buf_size, len); | 91 | + kexec_segment_size, buf_size, len); |
58 | + | 92 | + |
59 | + ima_measure_critical_data("ima_kexec", event_name, ima_kexec_event, | 93 | + ima_measure_critical_data("ima_kexec", event_name, ima_kexec_event, n, false, NULL, 0); |
60 | + strlen(ima_kexec_event), false, NULL, 0); | ||
61 | +} | 94 | +} |
62 | + | 95 | + |
63 | static int ima_alloc_kexec_file_buf(size_t segment_size) | 96 | static int ima_alloc_kexec_file_buf(size_t segment_size) |
64 | { | 97 | { |
65 | /* | 98 | /* |
... | ... | ||
69 | ima_kexec_file.count = sizeof(struct ima_kexec_hdr); /* reserved space */ | 102 | ima_kexec_file.count = sizeof(struct ima_kexec_hdr); /* reserved space */ |
70 | + ima_measure_kexec_event("kexec_load"); | 103 | + ima_measure_kexec_event("kexec_load"); |
71 | 104 | ||
72 | return 0; | 105 | return 0; |
73 | } | 106 | } |
74 | @@ -XXX,XX +XXX,XX @@ static int ima_update_kexec_buffer(struct notifier_block *self, | 107 | diff --git a/security/integrity/ima/ima_queue.c b/security/integrity/ima/ima_queue.c |
75 | return ret; | 108 | index XXXXXXX..XXXXXXX 100644 |
76 | } | 109 | --- a/security/integrity/ima/ima_queue.c |
77 | 110 | +++ b/security/integrity/ima/ima_queue.c | |
78 | + ima_measure_kexec_event("kexec_execute"); | 111 | @@ -XXX,XX +XXX,XX @@ static int ima_reboot_notifier(struct notifier_block *nb, |
112 | unsigned long action, | ||
113 | void *data) | ||
114 | { | ||
115 | +#ifdef CONFIG_IMA_KEXEC | ||
116 | + if (action == SYS_RESTART && data && !strcmp(data, "kexec reboot")) | ||
117 | + ima_measure_kexec_event("kexec_execute"); | ||
118 | +#endif | ||
79 | + | 119 | + |
80 | ret = ima_dump_measurement_list(&buf_size, &buf, | 120 | ima_measurements_suspend(); |
81 | kexec_segment_size); | 121 | |
82 | 122 | return NOTIFY_DONE; | |
83 | -- | 123 | -- |
84 | 2.25.1 | 124 | 2.25.1 | diff view generated by jsdifflib |