fs_mgr: trigger dm-verity error handling for invalid signatures

Currently, the device doesn't mount verified partitions if the
verity table signature is invalid, which usually means it fails to
boot. This change instead sets up dm-verity with an invalid root
hash and triggers device-specific error handling to recover from
the corruption.

Bug: 24256506
Change-Id: I6d693306fa0e7459c5500b028e433df61ecea6fb
(cherry picked from commit 47caa5c386b436ba13de9f2ef356380f39afaf3f)
diff --git a/fs_mgr/fs_mgr_verity.c b/fs_mgr/fs_mgr_verity.c
index 60f5398..a9e3358 100644
--- a/fs_mgr/fs_mgr_verity.c
+++ b/fs_mgr/fs_mgr_verity.c
@@ -47,6 +47,8 @@
 
 #define VERITY_METADATA_SIZE 32768
 #define VERITY_TABLE_RSA_KEY "/verity_key"
+#define VERITY_TABLE_HASH_IDX 8
+#define VERITY_TABLE_SALT_IDX 9
 
 #define METADATA_MAGIC 0x01564c54
 #define METADATA_TAG_MAX_LENGTH 63
@@ -141,6 +143,33 @@
     return retval;
 }
 
+static int invalidate_table(char *table, int table_length)
+{
+    int n = 0;
+    int idx = 0;
+    int cleared = 0;
+
+    while (n < table_length) {
+        if (table[n++] == ' ') {
+            ++idx;
+        }
+
+        if (idx != VERITY_TABLE_HASH_IDX && idx != VERITY_TABLE_SALT_IDX) {
+            continue;
+        }
+
+        while (n < table_length && table[n] != ' ') {
+            table[n++] = '0';
+        }
+
+        if (++cleared == 2) {
+            return 0;
+        }
+    }
+
+    return -1;
+}
+
 static int squashfs_get_target_device_size(char *blk_device, uint64_t *device_size)
 {
     struct squashfs_info sq_info;
@@ -957,6 +986,7 @@
     char *verity_blk_name = 0;
     char *verity_table = 0;
     char *verity_table_signature = 0;
+    int verity_table_length = 0;
     uint64_t device_size = 0;
 
     _Alignas(struct dm_ioctl) char buffer[DM_BUF_SIZE];
@@ -983,6 +1013,7 @@
     }
 
     retval = FS_MGR_SETUP_VERITY_FAIL;
+    verity_table_length = strlen(verity_table);
 
     // get the device mapper fd
     if ((fd = open("/dev/device-mapper", O_RDWR)) < 0) {
@@ -1002,13 +1033,6 @@
         goto out;
     }
 
-    // verify the signature on the table
-    if (verify_table(verity_table_signature,
-                            verity_table,
-                            strlen(verity_table)) < 0) {
-        goto out;
-    }
-
     if (load_verity_state(fstab, &mode) < 0) {
         /* if accessing or updating the state failed, switch to the default
          * safe mode. This makes sure the device won't end up in an endless
@@ -1017,6 +1041,22 @@
         mode = VERITY_MODE_EIO;
     }
 
+    // verify the signature on the table
+    if (verify_table(verity_table_signature,
+                            verity_table,
+                            verity_table_length) < 0) {
+        if (mode == VERITY_MODE_LOGGING) {
+            // the user has been warned, allow mounting without dm-verity
+            retval = FS_MGR_SETUP_VERITY_SUCCESS;
+            goto out;
+        }
+
+        // invalidate root hash and salt to trigger device-specific recovery
+        if (invalidate_table(verity_table, verity_table_length) < 0) {
+            goto out;
+        }
+    }
+
     INFO("Enabling dm-verity for %s (mode %d)\n",  mount_point, mode);
 
     // load the verity mapping table