ART: Boot integrity checks for dalvik cache

Add a boot rc file that checks for boot classpath components
in the dalvik-cache and ensures they are fsverity-protected.

Bug: 125474642
Test: m
Test: manual
Change-Id: I3e7c2926e549c88934d86eb2d1d5264c5930b674
diff --git a/Android.mk b/Android.mk
index de3e2b3..65b904f 100644
--- a/Android.mk
+++ b/Android.mk
@@ -364,6 +364,7 @@
 ifneq ($(HOST_OS),darwin)
   LOCAL_REQUIRED_MODULES += $(APEX_TEST_MODULE)
 endif
+LOCAL_REQUIRED_MODULES += art_apex_boot_integrity
 
 # Clear locally used variable.
 art_target_include_debug_build :=
diff --git a/build/apex/Android.bp b/build/apex/Android.bp
index 128a079..dcabfc6 100644
--- a/build/apex/Android.bp
+++ b/build/apex/Android.bp
@@ -188,6 +188,7 @@
     prebuilts: art_runtime_data_file_prebuilts
         + ["com.android.runtime.ld.config.txt"],
     key: "com.android.runtime.key",
+    required: ["art_apex_boot_integrity"],
 }
 
 // Release version of the Runtime APEX module (not containing debug
@@ -358,3 +359,9 @@
     name: "art_postinstall_hook",
     src: "art_postinstall_hook.sh",
 }
+
+sh_binary {
+    name: "art_apex_boot_integrity",
+    src: "art_apex_boot_integrity.sh",
+    init_rc: ["art_apex_boot_integrity.rc"],
+}
diff --git a/build/apex/art_apex_boot_integrity.rc b/build/apex/art_apex_boot_integrity.rc
new file mode 100644
index 0000000..92f616b
--- /dev/null
+++ b/build/apex/art_apex_boot_integrity.rc
@@ -0,0 +1,21 @@
+# Copyright (C) 2019 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+# Check that boot classpath files in /data/dalvik-cache have fsverity
+# protection
+
+on post-fs-data
+    # TODO: Use apex path once feature is implemented.
+    exec - root -- /system/bin/art_apex_boot_integrity
diff --git a/build/apex/art_apex_boot_integrity.sh b/build/apex/art_apex_boot_integrity.sh
new file mode 100644
index 0000000..c21377b
--- /dev/null
+++ b/build/apex/art_apex_boot_integrity.sh
@@ -0,0 +1,55 @@
+#!/system/bin/sh
+
+# Copyright (C) 2019 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+alias log_info="log -t art_apex -p i"
+alias log_error="log -t art_apex -p f"
+
+log_info "=== ART pre-boot integrity checks ==="
+
+# Measure (and enable) fsverity to see if things are installed. Enable is not
+# idempotent, and we'd need to parse the error string to see whether it says
+# data was installed. Rather do a two-step.
+FILES=`find /data/dalvik-cache -type f -a -name 'system@framework@boot*'`
+
+if [ ! -f "/system/bin/fsverity" ] ; then
+  log_error "Device is not fsverity-enabled."
+  rm -f $FILES
+  exit 0
+fi
+
+for FILE in $FILES ; do
+  if [ ! -f "$FILE" ] ; then
+    continue # May have deleted already.
+  fi
+
+  # Check for fsverity protection.
+  fsverity measure $FILE || \
+    ENABLE_MSG=`fsverity enable $FILE 2>&1` || \
+      {
+        # No installed data, can't enable - clean up.
+        # Note: to avoid side effects, only delete the tested files. To avoid
+        #       understanding arches here, delete all, even if that may delete
+        #       too aggressively.
+        log_error "Enable failed: $ENABLE_MSG" ;
+        rm -f $FILES ;
+        exit 1 ;
+      }
+
+  # Check for integrity.
+  INTEGRITY_MSG=`dd if=$FILE of=/dev/null bs=4k 2>&1` || \
+    { log_error "Integrity failed: $INTEGRITY_MSG" ; rm -f $FILES ; exit 2 ; }
+done