1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * Copyright (C) 2021 Microsoft Corporation |
4 | * |
5 | * Author: Tushar Sugandhi <tusharsu@linux.microsoft.com> |
6 | * |
7 | * Enables IMA measurements for DM targets |
8 | */ |
9 | |
10 | #include "dm-core.h" |
11 | #include "dm-ima.h" |
12 | |
13 | #include <linux/ima.h> |
14 | #include <linux/sched/mm.h> |
15 | #include <crypto/hash.h> |
16 | #include <linux/crypto.h> |
17 | #include <crypto/hash_info.h> |
18 | |
19 | #define DM_MSG_PREFIX "ima" |
20 | |
21 | /* |
22 | * Internal function to prefix separator characters in input buffer with escape |
23 | * character, so that they don't interfere with the construction of key-value pairs, |
24 | * and clients can split the key1=val1,key2=val2,key3=val3; pairs properly. |
25 | */ |
26 | static void fix_separator_chars(char **buf) |
27 | { |
28 | int l = strlen(*buf); |
29 | int i, j, sp = 0; |
30 | |
31 | for (i = 0; i < l; i++) |
32 | if ((*buf)[i] == '\\' || (*buf)[i] == ';' || (*buf)[i] == '=' || (*buf)[i] == ',') |
33 | sp++; |
34 | |
35 | if (!sp) |
36 | return; |
37 | |
38 | for (i = l-1, j = i+sp; i >= 0; i--) { |
39 | (*buf)[j--] = (*buf)[i]; |
40 | if ((*buf)[i] == '\\' || (*buf)[i] == ';' || (*buf)[i] == '=' || (*buf)[i] == ',') |
41 | (*buf)[j--] = '\\'; |
42 | } |
43 | } |
44 | |
45 | /* |
46 | * Internal function to allocate memory for IMA measurements. |
47 | */ |
48 | static void *dm_ima_alloc(size_t len, gfp_t flags, bool noio) |
49 | { |
50 | unsigned int noio_flag; |
51 | void *ptr; |
52 | |
53 | if (noio) |
54 | noio_flag = memalloc_noio_save(); |
55 | |
56 | ptr = kzalloc(size: len, flags); |
57 | |
58 | if (noio) |
59 | memalloc_noio_restore(flags: noio_flag); |
60 | |
61 | return ptr; |
62 | } |
63 | |
64 | /* |
65 | * Internal function to allocate and copy name and uuid for IMA measurements. |
66 | */ |
67 | static int dm_ima_alloc_and_copy_name_uuid(struct mapped_device *md, char **dev_name, |
68 | char **dev_uuid, bool noio) |
69 | { |
70 | int r; |
71 | *dev_name = dm_ima_alloc(DM_NAME_LEN*2, GFP_KERNEL, noio); |
72 | if (!(*dev_name)) { |
73 | r = -ENOMEM; |
74 | goto error; |
75 | } |
76 | |
77 | *dev_uuid = dm_ima_alloc(DM_UUID_LEN*2, GFP_KERNEL, noio); |
78 | if (!(*dev_uuid)) { |
79 | r = -ENOMEM; |
80 | goto error; |
81 | } |
82 | |
83 | r = dm_copy_name_and_uuid(md, name: *dev_name, uuid: *dev_uuid); |
84 | if (r) |
85 | goto error; |
86 | |
87 | fix_separator_chars(buf: dev_name); |
88 | fix_separator_chars(buf: dev_uuid); |
89 | |
90 | return 0; |
91 | error: |
92 | kfree(objp: *dev_name); |
93 | kfree(objp: *dev_uuid); |
94 | *dev_name = NULL; |
95 | *dev_uuid = NULL; |
96 | return r; |
97 | } |
98 | |
99 | /* |
100 | * Internal function to allocate and copy device data for IMA measurements. |
101 | */ |
102 | static int dm_ima_alloc_and_copy_device_data(struct mapped_device *md, char **device_data, |
103 | unsigned int num_targets, bool noio) |
104 | { |
105 | char *dev_name = NULL, *dev_uuid = NULL; |
106 | int r; |
107 | |
108 | r = dm_ima_alloc_and_copy_name_uuid(md, dev_name: &dev_name, dev_uuid: &dev_uuid, noio); |
109 | if (r) |
110 | return r; |
111 | |
112 | *device_data = dm_ima_alloc(DM_IMA_DEVICE_BUF_LEN, GFP_KERNEL, noio); |
113 | if (!(*device_data)) { |
114 | r = -ENOMEM; |
115 | goto error; |
116 | } |
117 | |
118 | scnprintf(buf: *device_data, DM_IMA_DEVICE_BUF_LEN, |
119 | fmt: "name=%s,uuid=%s,major=%d,minor=%d,minor_count=%d,num_targets=%u;" , |
120 | dev_name, dev_uuid, md->disk->major, md->disk->first_minor, |
121 | md->disk->minors, num_targets); |
122 | error: |
123 | kfree(objp: dev_name); |
124 | kfree(objp: dev_uuid); |
125 | return r; |
126 | } |
127 | |
128 | /* |
129 | * Internal wrapper function to call IMA to measure DM data. |
130 | */ |
131 | static void dm_ima_measure_data(const char *event_name, const void *buf, size_t buf_len, |
132 | bool noio) |
133 | { |
134 | unsigned int noio_flag; |
135 | |
136 | if (noio) |
137 | noio_flag = memalloc_noio_save(); |
138 | |
139 | ima_measure_critical_data(DM_NAME, event_name, buf, buf_len, |
140 | hash: false, NULL, digest_len: 0); |
141 | |
142 | if (noio) |
143 | memalloc_noio_restore(flags: noio_flag); |
144 | } |
145 | |
146 | /* |
147 | * Internal function to allocate and copy current device capacity for IMA measurements. |
148 | */ |
149 | static int dm_ima_alloc_and_copy_capacity_str(struct mapped_device *md, char **capacity_str, |
150 | bool noio) |
151 | { |
152 | sector_t capacity; |
153 | |
154 | capacity = get_capacity(disk: md->disk); |
155 | |
156 | *capacity_str = dm_ima_alloc(DM_IMA_DEVICE_CAPACITY_BUF_LEN, GFP_KERNEL, noio); |
157 | if (!(*capacity_str)) |
158 | return -ENOMEM; |
159 | |
160 | scnprintf(buf: *capacity_str, DM_IMA_DEVICE_BUF_LEN, fmt: "current_device_capacity=%llu;" , |
161 | capacity); |
162 | |
163 | return 0; |
164 | } |
165 | |
166 | /* |
167 | * Initialize/reset the dm ima related data structure variables. |
168 | */ |
169 | void dm_ima_reset_data(struct mapped_device *md) |
170 | { |
171 | memset(&(md->ima), 0, sizeof(md->ima)); |
172 | md->ima.dm_version_str_len = strlen(DM_IMA_VERSION_STR); |
173 | } |
174 | |
175 | /* |
176 | * Build up the IMA data for each target, and finally measure. |
177 | */ |
178 | void dm_ima_measure_on_table_load(struct dm_table *table, unsigned int status_flags) |
179 | { |
180 | size_t device_data_buf_len, target_metadata_buf_len, target_data_buf_len, l = 0; |
181 | char *target_metadata_buf = NULL, *target_data_buf = NULL, *digest_buf = NULL; |
182 | char *ima_buf = NULL, *device_data_buf = NULL; |
183 | int digest_size, last_target_measured = -1, r; |
184 | status_type_t type = STATUSTYPE_IMA; |
185 | size_t cur_total_buf_len = 0; |
186 | unsigned int num_targets, i; |
187 | SHASH_DESC_ON_STACK(shash, NULL); |
188 | struct crypto_shash *tfm = NULL; |
189 | u8 *digest = NULL; |
190 | bool noio = false; |
191 | /* |
192 | * In below hash_alg_prefix_len assignment +1 is for the additional char (':'), |
193 | * when prefixing the hash value with the hash algorithm name. e.g. sha256:<hash_value>. |
194 | */ |
195 | const size_t hash_alg_prefix_len = strlen(DM_IMA_TABLE_HASH_ALG) + 1; |
196 | char table_load_event_name[] = "dm_table_load" ; |
197 | |
198 | ima_buf = dm_ima_alloc(DM_IMA_MEASUREMENT_BUF_LEN, GFP_KERNEL, noio); |
199 | if (!ima_buf) |
200 | return; |
201 | |
202 | target_metadata_buf = dm_ima_alloc(DM_IMA_TARGET_METADATA_BUF_LEN, GFP_KERNEL, noio); |
203 | if (!target_metadata_buf) |
204 | goto error; |
205 | |
206 | target_data_buf = dm_ima_alloc(DM_IMA_TARGET_DATA_BUF_LEN, GFP_KERNEL, noio); |
207 | if (!target_data_buf) |
208 | goto error; |
209 | |
210 | num_targets = table->num_targets; |
211 | |
212 | if (dm_ima_alloc_and_copy_device_data(md: table->md, device_data: &device_data_buf, num_targets, noio)) |
213 | goto error; |
214 | |
215 | tfm = crypto_alloc_shash(DM_IMA_TABLE_HASH_ALG, type: 0, mask: 0); |
216 | if (IS_ERR(ptr: tfm)) |
217 | goto error; |
218 | |
219 | shash->tfm = tfm; |
220 | digest_size = crypto_shash_digestsize(tfm); |
221 | digest = dm_ima_alloc(len: digest_size, GFP_KERNEL, noio); |
222 | if (!digest) |
223 | goto error; |
224 | |
225 | r = crypto_shash_init(desc: shash); |
226 | if (r) |
227 | goto error; |
228 | |
229 | memcpy(ima_buf + l, DM_IMA_VERSION_STR, table->md->ima.dm_version_str_len); |
230 | l += table->md->ima.dm_version_str_len; |
231 | |
232 | device_data_buf_len = strlen(device_data_buf); |
233 | memcpy(ima_buf + l, device_data_buf, device_data_buf_len); |
234 | l += device_data_buf_len; |
235 | |
236 | for (i = 0; i < num_targets; i++) { |
237 | struct dm_target *ti = dm_table_get_target(t: table, index: i); |
238 | |
239 | last_target_measured = 0; |
240 | |
241 | /* |
242 | * First retrieve the target metadata. |
243 | */ |
244 | scnprintf(buf: target_metadata_buf, DM_IMA_TARGET_METADATA_BUF_LEN, |
245 | fmt: "target_index=%d,target_begin=%llu,target_len=%llu," , |
246 | i, ti->begin, ti->len); |
247 | target_metadata_buf_len = strlen(target_metadata_buf); |
248 | |
249 | /* |
250 | * Then retrieve the actual target data. |
251 | */ |
252 | if (ti->type->status) |
253 | ti->type->status(ti, type, status_flags, target_data_buf, |
254 | DM_IMA_TARGET_DATA_BUF_LEN); |
255 | else |
256 | target_data_buf[0] = '\0'; |
257 | |
258 | target_data_buf_len = strlen(target_data_buf); |
259 | |
260 | /* |
261 | * Check if the total data can fit into the IMA buffer. |
262 | */ |
263 | cur_total_buf_len = l + target_metadata_buf_len + target_data_buf_len; |
264 | |
265 | /* |
266 | * IMA measurements for DM targets are best-effort. |
267 | * If the total data buffered so far, including the current target, |
268 | * is too large to fit into DM_IMA_MEASUREMENT_BUF_LEN, measure what |
269 | * we have in the current buffer, and continue measuring the remaining |
270 | * targets by prefixing the device metadata again. |
271 | */ |
272 | if (unlikely(cur_total_buf_len >= DM_IMA_MEASUREMENT_BUF_LEN)) { |
273 | dm_ima_measure_data(event_name: table_load_event_name, buf: ima_buf, buf_len: l, noio); |
274 | r = crypto_shash_update(desc: shash, data: (const u8 *)ima_buf, len: l); |
275 | if (r < 0) |
276 | goto error; |
277 | |
278 | memset(ima_buf, 0, DM_IMA_MEASUREMENT_BUF_LEN); |
279 | l = 0; |
280 | |
281 | /* |
282 | * Each new "dm_table_load" entry in IMA log should have device data |
283 | * prefix, so that multiple records from the same "dm_table_load" for |
284 | * a given device can be linked together. |
285 | */ |
286 | memcpy(ima_buf + l, DM_IMA_VERSION_STR, table->md->ima.dm_version_str_len); |
287 | l += table->md->ima.dm_version_str_len; |
288 | |
289 | memcpy(ima_buf + l, device_data_buf, device_data_buf_len); |
290 | l += device_data_buf_len; |
291 | |
292 | /* |
293 | * If this iteration of the for loop turns out to be the last target |
294 | * in the table, dm_ima_measure_data("dm_table_load", ...) doesn't need |
295 | * to be called again, just the hash needs to be finalized. |
296 | * "last_target_measured" tracks this state. |
297 | */ |
298 | last_target_measured = 1; |
299 | } |
300 | |
301 | /* |
302 | * Fill-in all the target metadata, so that multiple targets for the same |
303 | * device can be linked together. |
304 | */ |
305 | memcpy(ima_buf + l, target_metadata_buf, target_metadata_buf_len); |
306 | l += target_metadata_buf_len; |
307 | |
308 | memcpy(ima_buf + l, target_data_buf, target_data_buf_len); |
309 | l += target_data_buf_len; |
310 | } |
311 | |
312 | if (!last_target_measured) { |
313 | dm_ima_measure_data(event_name: table_load_event_name, buf: ima_buf, buf_len: l, noio); |
314 | |
315 | r = crypto_shash_update(desc: shash, data: (const u8 *)ima_buf, len: l); |
316 | if (r < 0) |
317 | goto error; |
318 | } |
319 | |
320 | /* |
321 | * Finalize the table hash, and store it in table->md->ima.inactive_table.hash, |
322 | * so that the table data can be verified against the future device state change |
323 | * events, e.g. resume, rename, remove, table-clear etc. |
324 | */ |
325 | r = crypto_shash_final(desc: shash, out: digest); |
326 | if (r < 0) |
327 | goto error; |
328 | |
329 | digest_buf = dm_ima_alloc(len: (digest_size*2) + hash_alg_prefix_len + 1, GFP_KERNEL, noio); |
330 | |
331 | if (!digest_buf) |
332 | goto error; |
333 | |
334 | snprintf(buf: digest_buf, size: hash_alg_prefix_len + 1, fmt: "%s:" , DM_IMA_TABLE_HASH_ALG); |
335 | |
336 | for (i = 0; i < digest_size; i++) |
337 | snprintf(buf: (digest_buf + hash_alg_prefix_len + (i*2)), size: 3, fmt: "%02x" , digest[i]); |
338 | |
339 | if (table->md->ima.active_table.hash != table->md->ima.inactive_table.hash) |
340 | kfree(objp: table->md->ima.inactive_table.hash); |
341 | |
342 | table->md->ima.inactive_table.hash = digest_buf; |
343 | table->md->ima.inactive_table.hash_len = strlen(digest_buf); |
344 | table->md->ima.inactive_table.num_targets = num_targets; |
345 | |
346 | if (table->md->ima.active_table.device_metadata != |
347 | table->md->ima.inactive_table.device_metadata) |
348 | kfree(objp: table->md->ima.inactive_table.device_metadata); |
349 | |
350 | table->md->ima.inactive_table.device_metadata = device_data_buf; |
351 | table->md->ima.inactive_table.device_metadata_len = device_data_buf_len; |
352 | |
353 | goto exit; |
354 | error: |
355 | kfree(objp: digest_buf); |
356 | kfree(objp: device_data_buf); |
357 | exit: |
358 | kfree(objp: digest); |
359 | if (tfm) |
360 | crypto_free_shash(tfm); |
361 | kfree(objp: ima_buf); |
362 | kfree(objp: target_metadata_buf); |
363 | kfree(objp: target_data_buf); |
364 | } |
365 | |
366 | /* |
367 | * Measure IMA data on device resume. |
368 | */ |
369 | void dm_ima_measure_on_device_resume(struct mapped_device *md, bool swap) |
370 | { |
371 | char *device_table_data, *dev_name = NULL, *dev_uuid = NULL, *capacity_str = NULL; |
372 | char active[] = "active_table_hash=" ; |
373 | unsigned int active_len = strlen(active), capacity_len = 0; |
374 | unsigned int l = 0; |
375 | bool noio = true; |
376 | bool nodata = true; |
377 | int r; |
378 | |
379 | device_table_data = dm_ima_alloc(DM_IMA_DEVICE_BUF_LEN, GFP_KERNEL, noio); |
380 | if (!device_table_data) |
381 | return; |
382 | |
383 | r = dm_ima_alloc_and_copy_capacity_str(md, capacity_str: &capacity_str, noio); |
384 | if (r) |
385 | goto error; |
386 | |
387 | memcpy(device_table_data + l, DM_IMA_VERSION_STR, md->ima.dm_version_str_len); |
388 | l += md->ima.dm_version_str_len; |
389 | |
390 | if (swap) { |
391 | if (md->ima.active_table.hash != md->ima.inactive_table.hash) |
392 | kfree(objp: md->ima.active_table.hash); |
393 | |
394 | md->ima.active_table.hash = NULL; |
395 | md->ima.active_table.hash_len = 0; |
396 | |
397 | if (md->ima.active_table.device_metadata != |
398 | md->ima.inactive_table.device_metadata) |
399 | kfree(objp: md->ima.active_table.device_metadata); |
400 | |
401 | md->ima.active_table.device_metadata = NULL; |
402 | md->ima.active_table.device_metadata_len = 0; |
403 | md->ima.active_table.num_targets = 0; |
404 | |
405 | if (md->ima.inactive_table.hash) { |
406 | md->ima.active_table.hash = md->ima.inactive_table.hash; |
407 | md->ima.active_table.hash_len = md->ima.inactive_table.hash_len; |
408 | md->ima.inactive_table.hash = NULL; |
409 | md->ima.inactive_table.hash_len = 0; |
410 | } |
411 | |
412 | if (md->ima.inactive_table.device_metadata) { |
413 | md->ima.active_table.device_metadata = |
414 | md->ima.inactive_table.device_metadata; |
415 | md->ima.active_table.device_metadata_len = |
416 | md->ima.inactive_table.device_metadata_len; |
417 | md->ima.active_table.num_targets = md->ima.inactive_table.num_targets; |
418 | md->ima.inactive_table.device_metadata = NULL; |
419 | md->ima.inactive_table.device_metadata_len = 0; |
420 | md->ima.inactive_table.num_targets = 0; |
421 | } |
422 | } |
423 | |
424 | if (md->ima.active_table.device_metadata) { |
425 | memcpy(device_table_data + l, md->ima.active_table.device_metadata, |
426 | md->ima.active_table.device_metadata_len); |
427 | l += md->ima.active_table.device_metadata_len; |
428 | |
429 | nodata = false; |
430 | } |
431 | |
432 | if (md->ima.active_table.hash) { |
433 | memcpy(device_table_data + l, active, active_len); |
434 | l += active_len; |
435 | |
436 | memcpy(device_table_data + l, md->ima.active_table.hash, |
437 | md->ima.active_table.hash_len); |
438 | l += md->ima.active_table.hash_len; |
439 | |
440 | memcpy(device_table_data + l, ";" , 1); |
441 | l++; |
442 | |
443 | nodata = false; |
444 | } |
445 | |
446 | if (nodata) { |
447 | r = dm_ima_alloc_and_copy_name_uuid(md, dev_name: &dev_name, dev_uuid: &dev_uuid, noio); |
448 | if (r) |
449 | goto error; |
450 | |
451 | scnprintf(buf: device_table_data, DM_IMA_DEVICE_BUF_LEN, |
452 | fmt: "%sname=%s,uuid=%s;device_resume=no_data;" , |
453 | DM_IMA_VERSION_STR, dev_name, dev_uuid); |
454 | l = strlen(device_table_data); |
455 | |
456 | } |
457 | |
458 | capacity_len = strlen(capacity_str); |
459 | memcpy(device_table_data + l, capacity_str, capacity_len); |
460 | l += capacity_len; |
461 | |
462 | dm_ima_measure_data(event_name: "dm_device_resume" , buf: device_table_data, buf_len: l, noio); |
463 | |
464 | kfree(objp: dev_name); |
465 | kfree(objp: dev_uuid); |
466 | error: |
467 | kfree(objp: capacity_str); |
468 | kfree(objp: device_table_data); |
469 | } |
470 | |
471 | /* |
472 | * Measure IMA data on remove. |
473 | */ |
474 | void dm_ima_measure_on_device_remove(struct mapped_device *md, bool remove_all) |
475 | { |
476 | char *device_table_data, *dev_name = NULL, *dev_uuid = NULL, *capacity_str = NULL; |
477 | char active_table_str[] = "active_table_hash=" ; |
478 | char inactive_table_str[] = "inactive_table_hash=" ; |
479 | char device_active_str[] = "device_active_metadata=" ; |
480 | char device_inactive_str[] = "device_inactive_metadata=" ; |
481 | char remove_all_str[] = "remove_all=" ; |
482 | unsigned int active_table_len = strlen(active_table_str); |
483 | unsigned int inactive_table_len = strlen(inactive_table_str); |
484 | unsigned int device_active_len = strlen(device_active_str); |
485 | unsigned int device_inactive_len = strlen(device_inactive_str); |
486 | unsigned int remove_all_len = strlen(remove_all_str); |
487 | unsigned int capacity_len = 0; |
488 | unsigned int l = 0; |
489 | bool noio = true; |
490 | bool nodata = true; |
491 | int r; |
492 | |
493 | device_table_data = dm_ima_alloc(DM_IMA_DEVICE_BUF_LEN*2, GFP_KERNEL, noio); |
494 | if (!device_table_data) |
495 | goto exit; |
496 | |
497 | r = dm_ima_alloc_and_copy_capacity_str(md, capacity_str: &capacity_str, noio); |
498 | if (r) { |
499 | kfree(objp: device_table_data); |
500 | goto exit; |
501 | } |
502 | |
503 | memcpy(device_table_data + l, DM_IMA_VERSION_STR, md->ima.dm_version_str_len); |
504 | l += md->ima.dm_version_str_len; |
505 | |
506 | if (md->ima.active_table.device_metadata) { |
507 | memcpy(device_table_data + l, device_active_str, device_active_len); |
508 | l += device_active_len; |
509 | |
510 | memcpy(device_table_data + l, md->ima.active_table.device_metadata, |
511 | md->ima.active_table.device_metadata_len); |
512 | l += md->ima.active_table.device_metadata_len; |
513 | |
514 | nodata = false; |
515 | } |
516 | |
517 | if (md->ima.inactive_table.device_metadata) { |
518 | memcpy(device_table_data + l, device_inactive_str, device_inactive_len); |
519 | l += device_inactive_len; |
520 | |
521 | memcpy(device_table_data + l, md->ima.inactive_table.device_metadata, |
522 | md->ima.inactive_table.device_metadata_len); |
523 | l += md->ima.inactive_table.device_metadata_len; |
524 | |
525 | nodata = false; |
526 | } |
527 | |
528 | if (md->ima.active_table.hash) { |
529 | memcpy(device_table_data + l, active_table_str, active_table_len); |
530 | l += active_table_len; |
531 | |
532 | memcpy(device_table_data + l, md->ima.active_table.hash, |
533 | md->ima.active_table.hash_len); |
534 | l += md->ima.active_table.hash_len; |
535 | |
536 | memcpy(device_table_data + l, "," , 1); |
537 | l++; |
538 | |
539 | nodata = false; |
540 | } |
541 | |
542 | if (md->ima.inactive_table.hash) { |
543 | memcpy(device_table_data + l, inactive_table_str, inactive_table_len); |
544 | l += inactive_table_len; |
545 | |
546 | memcpy(device_table_data + l, md->ima.inactive_table.hash, |
547 | md->ima.inactive_table.hash_len); |
548 | l += md->ima.inactive_table.hash_len; |
549 | |
550 | memcpy(device_table_data + l, "," , 1); |
551 | l++; |
552 | |
553 | nodata = false; |
554 | } |
555 | /* |
556 | * In case both active and inactive tables, and corresponding |
557 | * device metadata is cleared/missing - record the name and uuid |
558 | * in IMA measurements. |
559 | */ |
560 | if (nodata) { |
561 | if (dm_ima_alloc_and_copy_name_uuid(md, dev_name: &dev_name, dev_uuid: &dev_uuid, noio)) |
562 | goto error; |
563 | |
564 | scnprintf(buf: device_table_data, DM_IMA_DEVICE_BUF_LEN, |
565 | fmt: "%sname=%s,uuid=%s;device_remove=no_data;" , |
566 | DM_IMA_VERSION_STR, dev_name, dev_uuid); |
567 | l = strlen(device_table_data); |
568 | } |
569 | |
570 | memcpy(device_table_data + l, remove_all_str, remove_all_len); |
571 | l += remove_all_len; |
572 | memcpy(device_table_data + l, remove_all ? "y;" : "n;" , 2); |
573 | l += 2; |
574 | |
575 | capacity_len = strlen(capacity_str); |
576 | memcpy(device_table_data + l, capacity_str, capacity_len); |
577 | l += capacity_len; |
578 | |
579 | dm_ima_measure_data(event_name: "dm_device_remove" , buf: device_table_data, buf_len: l, noio); |
580 | |
581 | error: |
582 | kfree(objp: device_table_data); |
583 | kfree(objp: capacity_str); |
584 | exit: |
585 | kfree(objp: md->ima.active_table.device_metadata); |
586 | |
587 | if (md->ima.active_table.device_metadata != |
588 | md->ima.inactive_table.device_metadata) |
589 | kfree(objp: md->ima.inactive_table.device_metadata); |
590 | |
591 | kfree(objp: md->ima.active_table.hash); |
592 | |
593 | if (md->ima.active_table.hash != md->ima.inactive_table.hash) |
594 | kfree(objp: md->ima.inactive_table.hash); |
595 | |
596 | dm_ima_reset_data(md); |
597 | |
598 | kfree(objp: dev_name); |
599 | kfree(objp: dev_uuid); |
600 | } |
601 | |
602 | /* |
603 | * Measure ima data on table clear. |
604 | */ |
605 | void dm_ima_measure_on_table_clear(struct mapped_device *md, bool new_map) |
606 | { |
607 | unsigned int l = 0, capacity_len = 0; |
608 | char *device_table_data = NULL, *dev_name = NULL, *dev_uuid = NULL, *capacity_str = NULL; |
609 | char inactive_str[] = "inactive_table_hash=" ; |
610 | unsigned int inactive_len = strlen(inactive_str); |
611 | bool noio = true; |
612 | bool nodata = true; |
613 | int r; |
614 | |
615 | device_table_data = dm_ima_alloc(DM_IMA_DEVICE_BUF_LEN, GFP_KERNEL, noio); |
616 | if (!device_table_data) |
617 | return; |
618 | |
619 | r = dm_ima_alloc_and_copy_capacity_str(md, capacity_str: &capacity_str, noio); |
620 | if (r) |
621 | goto error1; |
622 | |
623 | memcpy(device_table_data + l, DM_IMA_VERSION_STR, md->ima.dm_version_str_len); |
624 | l += md->ima.dm_version_str_len; |
625 | |
626 | if (md->ima.inactive_table.device_metadata_len && |
627 | md->ima.inactive_table.hash_len) { |
628 | memcpy(device_table_data + l, md->ima.inactive_table.device_metadata, |
629 | md->ima.inactive_table.device_metadata_len); |
630 | l += md->ima.inactive_table.device_metadata_len; |
631 | |
632 | memcpy(device_table_data + l, inactive_str, inactive_len); |
633 | l += inactive_len; |
634 | |
635 | memcpy(device_table_data + l, md->ima.inactive_table.hash, |
636 | md->ima.inactive_table.hash_len); |
637 | |
638 | l += md->ima.inactive_table.hash_len; |
639 | |
640 | memcpy(device_table_data + l, ";" , 1); |
641 | l++; |
642 | |
643 | nodata = false; |
644 | } |
645 | |
646 | if (nodata) { |
647 | if (dm_ima_alloc_and_copy_name_uuid(md, dev_name: &dev_name, dev_uuid: &dev_uuid, noio)) |
648 | goto error2; |
649 | |
650 | scnprintf(buf: device_table_data, DM_IMA_DEVICE_BUF_LEN, |
651 | fmt: "%sname=%s,uuid=%s;table_clear=no_data;" , |
652 | DM_IMA_VERSION_STR, dev_name, dev_uuid); |
653 | l = strlen(device_table_data); |
654 | } |
655 | |
656 | capacity_len = strlen(capacity_str); |
657 | memcpy(device_table_data + l, capacity_str, capacity_len); |
658 | l += capacity_len; |
659 | |
660 | dm_ima_measure_data(event_name: "dm_table_clear" , buf: device_table_data, buf_len: l, noio); |
661 | |
662 | if (new_map) { |
663 | if (md->ima.inactive_table.hash && |
664 | md->ima.inactive_table.hash != md->ima.active_table.hash) |
665 | kfree(objp: md->ima.inactive_table.hash); |
666 | |
667 | md->ima.inactive_table.hash = NULL; |
668 | md->ima.inactive_table.hash_len = 0; |
669 | |
670 | if (md->ima.inactive_table.device_metadata && |
671 | md->ima.inactive_table.device_metadata != md->ima.active_table.device_metadata) |
672 | kfree(objp: md->ima.inactive_table.device_metadata); |
673 | |
674 | md->ima.inactive_table.device_metadata = NULL; |
675 | md->ima.inactive_table.device_metadata_len = 0; |
676 | md->ima.inactive_table.num_targets = 0; |
677 | |
678 | if (md->ima.active_table.hash) { |
679 | md->ima.inactive_table.hash = md->ima.active_table.hash; |
680 | md->ima.inactive_table.hash_len = md->ima.active_table.hash_len; |
681 | } |
682 | |
683 | if (md->ima.active_table.device_metadata) { |
684 | md->ima.inactive_table.device_metadata = |
685 | md->ima.active_table.device_metadata; |
686 | md->ima.inactive_table.device_metadata_len = |
687 | md->ima.active_table.device_metadata_len; |
688 | md->ima.inactive_table.num_targets = |
689 | md->ima.active_table.num_targets; |
690 | } |
691 | } |
692 | |
693 | kfree(objp: dev_name); |
694 | kfree(objp: dev_uuid); |
695 | error2: |
696 | kfree(objp: capacity_str); |
697 | error1: |
698 | kfree(objp: device_table_data); |
699 | } |
700 | |
701 | /* |
702 | * Measure IMA data on device rename. |
703 | */ |
704 | void dm_ima_measure_on_device_rename(struct mapped_device *md) |
705 | { |
706 | char *old_device_data = NULL, *new_device_data = NULL, *combined_device_data = NULL; |
707 | char *new_dev_name = NULL, *new_dev_uuid = NULL, *capacity_str = NULL; |
708 | bool noio = true; |
709 | int r; |
710 | |
711 | if (dm_ima_alloc_and_copy_device_data(md, device_data: &new_device_data, |
712 | num_targets: md->ima.active_table.num_targets, noio)) |
713 | return; |
714 | |
715 | if (dm_ima_alloc_and_copy_name_uuid(md, dev_name: &new_dev_name, dev_uuid: &new_dev_uuid, noio)) |
716 | goto error; |
717 | |
718 | combined_device_data = dm_ima_alloc(DM_IMA_DEVICE_BUF_LEN * 2, GFP_KERNEL, noio); |
719 | if (!combined_device_data) |
720 | goto error; |
721 | |
722 | r = dm_ima_alloc_and_copy_capacity_str(md, capacity_str: &capacity_str, noio); |
723 | if (r) |
724 | goto error; |
725 | |
726 | old_device_data = md->ima.active_table.device_metadata; |
727 | |
728 | md->ima.active_table.device_metadata = new_device_data; |
729 | md->ima.active_table.device_metadata_len = strlen(new_device_data); |
730 | |
731 | scnprintf(buf: combined_device_data, DM_IMA_DEVICE_BUF_LEN * 2, |
732 | fmt: "%s%snew_name=%s,new_uuid=%s;%s" , DM_IMA_VERSION_STR, old_device_data, |
733 | new_dev_name, new_dev_uuid, capacity_str); |
734 | |
735 | dm_ima_measure_data(event_name: "dm_device_rename" , buf: combined_device_data, strlen(combined_device_data), |
736 | noio); |
737 | |
738 | goto exit; |
739 | |
740 | error: |
741 | kfree(objp: new_device_data); |
742 | exit: |
743 | kfree(objp: capacity_str); |
744 | kfree(objp: combined_device_data); |
745 | kfree(objp: old_device_data); |
746 | kfree(objp: new_dev_name); |
747 | kfree(objp: new_dev_uuid); |
748 | } |
749 | |