virtmgr: loosen kernel/initrd SELinux requirements for vendor VMs

Bug: 393087663
Test: TODO
Change-Id: I3559b2b0b46d7259005202cc72a813f266d313ba
diff --git a/android/virtmgr/src/aidl.rs b/android/virtmgr/src/aidl.rs
index 91a05e3..5a52921 100644
--- a/android/virtmgr/src/aidl.rs
+++ b/android/virtmgr/src/aidl.rs
@@ -508,12 +508,12 @@
         &self,
         config: &VirtualMachineConfig,
         calling_exe_path: Option<&Path>,
+        calling_partition: CallingPartition,
     ) -> binder::Result<(VmContext, Cid, PathBuf)> {
         let name = match config {
             VirtualMachineConfig::RawConfig(config) => &config.name,
             VirtualMachineConfig::AppConfig(config) => &config.name,
         };
-        let calling_partition = find_partition(calling_exe_path)?;
         let early_vm = find_early_vm_for_partition(calling_partition, name)
             .or_service_specific_exception(-1)?;
         let calling_exe_path = match calling_exe_path {
@@ -603,6 +603,8 @@
         let requester_uid = get_calling_uid();
         let requester_debug_pid = get_calling_pid();
 
+        let calling_partition = find_partition(CALLING_EXE_PATH.as_deref())?;
+
         check_config_features(config)?;
 
         if cfg!(early) {
@@ -614,7 +616,7 @@
 
         // Allocating VM context checks the MANAGE_VIRTUAL_MACHINE permission.
         let (vm_context, cid, temporary_directory) = if cfg!(early) {
-            self.create_early_vm_context(config, CALLING_EXE_PATH.as_deref())?
+            self.create_early_vm_context(config, CALLING_EXE_PATH.as_deref(), calling_partition)?
         } else {
             self.create_vm_context(requester_debug_pid, config)?
         };
@@ -704,7 +706,8 @@
 
             // In a protected VM, we require custom kernels to come from a trusted source
             // (b/237054515).
-            check_label_for_kernel_files(&kernel, &initrd).or_service_specific_exception(-1)?;
+            check_label_for_kernel_files(&kernel, &initrd, calling_partition)
+                .or_service_specific_exception(-1)?;
 
             // Check if partition images are labeled incorrectly. This is to prevent random images
             // which are not protected by the Android Verified Boot (e.g. bits downloaded by apps)
@@ -722,15 +725,14 @@
                         !is_safe_raw_partition(&partition.label)
                     }
                 })
-                .try_for_each(check_label_for_partition)
+                .try_for_each(|partition| check_label_for_partition(partition, calling_partition))
                 .or_service_specific_exception(-1)?;
         }
 
         // Check if files for payloads and bases are NOT coming from /vendor and /odm, as they may
         // have unstable interfaces.
         // TODO(b/316431494): remove once Treble interfaces are stabilized.
-        check_partitions_for_files(config, find_partition(CALLING_EXE_PATH.as_deref())?)
-            .or_service_specific_exception(-1)?;
+        check_partitions_for_files(config, calling_partition).or_service_specific_exception(-1)?;
 
         let zero_filler_path = temporary_directory.join("zero.img");
         write_zero_filler(&zero_filler_path)
@@ -1551,7 +1553,7 @@
 ///
 /// App private data files are deliberately excluded, to avoid arbitrary payloads being run on
 /// user devices (W^X).
-fn check_label_is_allowed(context: &SeContext) -> Result<()> {
+fn check_label_is_allowed(context: &SeContext, calling_partition: CallingPartition) -> Result<()> {
     match context.selinux_type()? {
         | "apk_data_file" // APKs of an installed app
         | "shell_data_file" // test files created via adb shell
@@ -1560,27 +1562,42 @@
         | "virtualizationservice_data_file" // files created by VS / VirtMgr
         | "vendor_microdroid_file" // immutable dm-verity protected partition (/vendor/etc/avf/microdroid/.*)
          => Ok(()),
+        // It is difficult to require specific label types for vendor initiated VM's files, so we
+        // allow anything with a vendor prefix.
+        t if calling_partition == CallingPartition::Vendor && t.starts_with("vendor_")  => Ok(()),
         _ => bail!("Label {} is not allowed", context),
     }
 }
 
-fn check_label_for_partition(partition: &Partition) -> Result<()> {
+fn check_label_for_partition(
+    partition: &Partition,
+    calling_partition: CallingPartition,
+) -> Result<()> {
     let file = partition.image.as_ref().unwrap().as_ref();
-    check_label_is_allowed(&getfilecon(file)?)
+    check_label_is_allowed(&getfilecon(file)?, calling_partition)
         .with_context(|| format!("Partition {} invalid", &partition.label))
 }
 
-fn check_label_for_kernel_files(kernel: &Option<File>, initrd: &Option<File>) -> Result<()> {
+fn check_label_for_kernel_files(
+    kernel: &Option<File>,
+    initrd: &Option<File>,
+    calling_partition: CallingPartition,
+) -> Result<()> {
     if let Some(f) = kernel {
-        check_label_for_file(f, "kernel")?;
+        check_label_for_file(f, "kernel", calling_partition)?;
     }
     if let Some(f) = initrd {
-        check_label_for_file(f, "initrd")?;
+        check_label_for_file(f, "initrd", calling_partition)?;
     }
     Ok(())
 }
-fn check_label_for_file(file: &File, name: &str) -> Result<()> {
-    check_label_is_allowed(&getfilecon(file)?).with_context(|| format!("{} file invalid", name))
+fn check_label_for_file(
+    file: &File,
+    name: &str,
+    calling_partition: CallingPartition,
+) -> Result<()> {
+    check_label_is_allowed(&getfilecon(file)?, calling_partition)
+        .with_context(|| format!("{} file invalid", name))
 }
 
 /// Implementation of the AIDL `IVirtualMachine` interface. Used as a handle to a VM.
@@ -2399,23 +2416,30 @@
     #[test]
     fn test_is_allowed_label_for_partition() -> Result<()> {
         let expected_results = vec![
-            ("u:object_r:system_file:s0", true),
-            ("u:object_r:apk_data_file:s0", true),
-            ("u:object_r:app_data_file:s0", false),
-            ("u:object_r:app_data_file:s0:c512,c768", false),
-            ("u:object_r:privapp_data_file:s0:c512,c768", false),
-            ("invalid", false),
-            ("user:role:apk_data_file:severity:categories", true),
-            ("user:role:apk_data_file:severity:categories:extraneous", false),
+            (CallingPartition::System, "u:object_r:system_file:s0", true),
+            (CallingPartition::System, "u:object_r:apk_data_file:s0", true),
+            (CallingPartition::System, "u:object_r:app_data_file:s0", false),
+            (CallingPartition::System, "u:object_r:app_data_file:s0:c512,c768", false),
+            (CallingPartition::System, "u:object_r:privapp_data_file:s0:c512,c768", false),
+            (CallingPartition::System, "invalid", false),
+            (CallingPartition::System, "user:role:apk_data_file:severity:categories", true),
+            (
+                CallingPartition::System,
+                "user:role:apk_data_file:severity:categories:extraneous",
+                false,
+            ),
+            (CallingPartition::System, "u:object_r:vendor_unknowable:s0", false),
+            (CallingPartition::Vendor, "u:object_r:vendor_unknowable:s0", true),
         ];
 
-        for (label, expected_valid) in expected_results {
+        for (calling_partition, label, expected_valid) in expected_results {
             let context = SeContext::new(label)?;
-            let result = check_label_is_allowed(&context);
-            if expected_valid {
-                assert!(result.is_ok(), "Expected label {} to be allowed, got {:?}", label, result);
-            } else if result.is_ok() {
-                bail!("Expected label {} to be disallowed", label);
+            let result = check_label_is_allowed(&context, calling_partition);
+            if expected_valid != result.is_ok() {
+                bail!(
+                    "Expected label {label} to be {} for {calling_partition} partition",
+                    if expected_valid { "allowed" } else { "disallowed" }
+                );
             }
         }
         Ok(())