dm ima: measure data on device resume
authorTushar Sugandhi <tusharsu@linux.microsoft.com>
Tue, 13 Jul 2021 00:48:59 +0000 (17:48 -0700)
committerMike Snitzer <snitzer@redhat.com>
Tue, 10 Aug 2021 17:34:22 +0000 (13:34 -0400)
A given block device can load a table multiple times, with different
input parameters, before eventually resuming it.  Further, a device may
be suspended and then resumed.  The device may never resume after a
table-load.  Because of the above valid scenarios for a given device,
it is important to measure and log the device resume event using IMA.

Also, if the table is large, measuring it in clear-text each time the
device changes state, will unnecessarily increase the size of IMA log.
Since the table clear-text is already measured during table-load event,
measuring the hash during resume should be sufficient to validate the
table contents.

Measure the device parameters, and hash of the active table, when the
device is resumed.

Signed-off-by: Tushar Sugandhi <tusharsu@linux.microsoft.com>
Signed-off-by: Mike Snitzer <snitzer@redhat.com>
drivers/md/dm-ima.c
drivers/md/dm-ima.h
drivers/md/dm-ioctl.c

index c8f54d9f6c8d9b85b699aea656c53282e1003a70..30b9875be63a1f3aef8807ab7cd243855c19125f 100644 (file)
@@ -142,6 +142,26 @@ static void dm_ima_measure_data(const char *event_name, const void *buf, size_t
                memalloc_noio_restore(noio_flag);
 }
 
+/*
+ * Internal function to allocate and copy current device capacity for IMA measurements.
+ */
+static int dm_ima_alloc_and_copy_capacity_str(struct mapped_device *md, char **capacity_str,
+                                             bool noio)
+{
+       sector_t capacity;
+
+       capacity = get_capacity(md->disk);
+
+       *capacity_str = dm_ima_alloc(DM_IMA_DEVICE_CAPACITY_BUF_LEN, GFP_KERNEL, noio);
+       if (!(*capacity_str))
+               return -ENOMEM;
+
+       scnprintf(*capacity_str, DM_IMA_DEVICE_BUF_LEN, "current_device_capacity=%llu;",
+                 capacity);
+
+       return 0;
+}
+
 /*
  * Initialize/reset the dm ima related data structure variables.
  */
@@ -328,3 +348,99 @@ exit:
        kfree(target_metadata_buf);
        kfree(target_data_buf);
 }
+
+/*
+ * Measure IMA data on device resume.
+ */
+void dm_ima_measure_on_device_resume(struct mapped_device *md, bool swap)
+{
+       char *device_table_data, *dev_name = NULL, *dev_uuid = NULL, *capacity_str = NULL;
+       char active[] = "active_table_hash=";
+       unsigned int active_len = strlen(active), capacity_len = 0;
+       unsigned int l = 0;
+       bool noio = true;
+       int r;
+
+       device_table_data = dm_ima_alloc(DM_IMA_DEVICE_BUF_LEN, GFP_KERNEL, noio);
+       if (!device_table_data)
+               return;
+
+       r = dm_ima_alloc_and_copy_capacity_str(md, &capacity_str, noio);
+       if (r)
+               goto error;
+
+       if (swap) {
+               if (md->ima.active_table.hash != md->ima.inactive_table.hash)
+                       kfree(md->ima.active_table.hash);
+
+               md->ima.active_table.hash = NULL;
+               md->ima.active_table.hash_len = 0;
+
+               if (md->ima.active_table.device_metadata !=
+                   md->ima.inactive_table.device_metadata)
+                       kfree(md->ima.active_table.device_metadata);
+
+               md->ima.active_table.device_metadata = NULL;
+               md->ima.active_table.device_metadata_len = 0;
+               md->ima.active_table.num_targets = 0;
+
+               if (md->ima.inactive_table.hash) {
+                       md->ima.active_table.hash = md->ima.inactive_table.hash;
+                       md->ima.active_table.hash_len = md->ima.inactive_table.hash_len;
+                       md->ima.inactive_table.hash = NULL;
+                       md->ima.inactive_table.hash_len = 0;
+               }
+
+               if (md->ima.inactive_table.device_metadata) {
+                       md->ima.active_table.device_metadata =
+                               md->ima.inactive_table.device_metadata;
+                       md->ima.active_table.device_metadata_len =
+                               md->ima.inactive_table.device_metadata_len;
+                       md->ima.active_table.num_targets = md->ima.inactive_table.num_targets;
+                       md->ima.inactive_table.device_metadata = NULL;
+                       md->ima.inactive_table.device_metadata_len = 0;
+                       md->ima.inactive_table.num_targets = 0;
+               }
+       }
+
+       if (md->ima.active_table.device_metadata) {
+               l = md->ima.active_table.device_metadata_len;
+               memcpy(device_table_data, md->ima.active_table.device_metadata, l);
+       }
+
+       if (md->ima.active_table.hash) {
+               memcpy(device_table_data + l, active, active_len);
+               l += active_len;
+
+               memcpy(device_table_data + l, md->ima.active_table.hash,
+                      md->ima.active_table.hash_len);
+               l += md->ima.active_table.hash_len;
+
+               memcpy(device_table_data + l, ";", 1);
+               l++;
+       }
+
+       if (!l) {
+               r = dm_ima_alloc_and_copy_name_uuid(md, &dev_name, &dev_uuid, noio);
+               if (r)
+                       goto error;
+
+               scnprintf(device_table_data, DM_IMA_DEVICE_BUF_LEN,
+                         "name=%s,uuid=%s;device_resume=no_data;",
+                         dev_name, dev_uuid);
+               l += strlen(device_table_data);
+
+       }
+
+       capacity_len = strlen(capacity_str);
+       memcpy(device_table_data + l, capacity_str, capacity_len);
+       l += capacity_len;
+
+       dm_ima_measure_data("device_resume", device_table_data, l, noio);
+
+       kfree(dev_name);
+       kfree(dev_uuid);
+error:
+       kfree(capacity_str);
+       kfree(device_table_data);
+}
index 16afd9a8c0b2ae4de5e33c4be03e9d9d7e2061f4..78c36b877ccf42c10c56cee72f5c844eb8a8ead0 100644 (file)
@@ -15,6 +15,7 @@
 #define DM_IMA_DEVICE_BUF_LEN          1024
 #define DM_IMA_TARGET_METADATA_BUF_LEN 128
 #define DM_IMA_TARGET_DATA_BUF_LEN     2048
+#define DM_IMA_DEVICE_CAPACITY_BUF_LEN 128
 
 #ifdef CONFIG_IMA
 
@@ -48,11 +49,13 @@ struct dm_ima_measurements {
 
 void dm_ima_reset_data(struct mapped_device *md);
 void dm_ima_measure_on_table_load(struct dm_table *table, unsigned int status_flags);
+void dm_ima_measure_on_device_resume(struct mapped_device *md, bool swap);
 
 #else
 
 static inline void dm_ima_reset_data(struct mapped_device *md) {}
 static inline void dm_ima_measure_on_table_load(struct dm_table *table, unsigned int status_flags) {}
+static inline void dm_ima_measure_on_device_resume(struct mapped_device *md, bool swap) {}
 
 #endif /* CONFIG_IMA */
 
index d7c3456bf85829a80cfad93241d1dfb23c2b6949..100745cd8f014630a3036d43335478e932742c5e 100644 (file)
@@ -1160,8 +1160,12 @@ static int do_resume(struct dm_ioctl *param)
 
        if (dm_suspended_md(md)) {
                r = dm_resume(md);
-               if (!r && !dm_kobject_uevent(md, KOBJ_CHANGE, param->event_nr))
-                       param->flags |= DM_UEVENT_GENERATED_FLAG;
+               if (!r) {
+                       dm_ima_measure_on_device_resume(md, new_map ? true : false);
+
+                       if (!dm_kobject_uevent(md, KOBJ_CHANGE, param->event_nr))
+                               param->flags |= DM_UEVENT_GENERATED_FLAG;
+               }
        }
 
        /*