Tenant APK: Validate apks against TenancyConfig
MM should ensure that the tenant apks presented by Android are a subset
of those present in TenancyConfig - which is a signed declaration of
tenancy & part of DICE based secret derivation in VM. This ensures
Android is not able to plug arbitrary tenant packages into the VM.
Test: MicrodroidTests.tenantApk
Bug: 429639517
Flag: build.RELEASE_AVF_ENABLE_ADVANCE_MULTITENANCY
Change-Id: Id1480bcc146b1e6be4130bc7fbeba85b9671281c
diff --git a/guest/microdroid_manager/src/main.rs b/guest/microdroid_manager/src/main.rs
index 739d9c8..9659950 100644
--- a/guest/microdroid_manager/src/main.rs
+++ b/guest/microdroid_manager/src/main.rs
@@ -45,7 +45,9 @@
use crate::dice::dice_derivation;
use crate::encrypted_store_kek::{decrypt_kek, encrypt_kek};
use crate::instance::{EncryptedStoreMode, InstanceDisk, MicrodroidData};
-use crate::verify::{integrity_protect_tenant_apks, verify_payload};
+use crate::verify::{
+ integrity_protect_tenant_apks, validate_tenant_apks_against_tenant_config, verify_payload,
+};
use crate::vm_internal_service::VmInternalService;
use crate::vm_payload_service::VmPayloadService;
use anyhow::{anyhow, bail, ensure, Context, Error, Result};
@@ -579,7 +581,11 @@
.context("Failed to mount extra apks")?;
// TODO(b/429639517): Verify the tenant packages against`VmPayloadConfig` from main_apk
- integrity_protect_tenant_apks()?;
+ let tenant_apk_data_extracted_from_manifest = integrity_protect_tenant_apks()?;
+ validate_tenant_apks_against_tenant_config(
+ &tenant_apk_data_extracted_from_manifest,
+ &config.tenants,
+ )?;
let tenant_apk_count =
config.tenants.iter().filter(|t| matches!(t, TenantConfig::Apk(_))).count();
mount_additional_apks(&mut zipfuse, tenant_apk_count, AdditionalApkType::TenantApk)
diff --git a/guest/microdroid_manager/src/verify.rs b/guest/microdroid_manager/src/verify.rs
index 3a1dacf..1623382 100644
--- a/guest/microdroid_manager/src/verify.rs
+++ b/guest/microdroid_manager/src/verify.rs
@@ -22,9 +22,12 @@
use itertools::sorted;
use log::{info, warn};
use microdroid_metadata::{write_metadata, Metadata};
+use microdroid_payload_config::TenantConfig;
use openssl::sha::sha512;
use rustutils::system_properties;
+use std::collections::HashSet;
use std::fs::OpenOptions;
+use std::hash::Hash;
use std::path::Path;
use std::process::{Child, Command};
use std::str;
@@ -175,7 +178,7 @@
})
}
-pub fn integrity_protect_tenant_apks() -> Result<()> {
+pub(crate) fn integrity_protect_tenant_apks() -> Result<Vec<ApkData>> {
// sort globbed paths to match apks (tenant-{idx}) and idsigs (tenant-{idx})
// e.g. "tenant-0" corresponds to "tenant-idsig-0"
let tenant_apks =
@@ -188,8 +191,14 @@
tenant_apks.len(),
tenant_idsigs.len()
);
+ let tenant_hashes_from_idsig: Vec<_> = tenant_idsigs
+ .iter()
+ .map(|idsig| {
+ get_apk_root_hash_from_idsig(idsig).expect("Can't find root hash from tenant idsig")
+ })
+ .collect();
- let tenant_apk_names: Vec<_> =
+ let tenant_apk_block_dev: Vec<_> =
(0..tenant_apks.len()).map(|i| format!("tenant-apk-{}", i)).collect();
let mut apkdmverity_arguments: Vec<ApkDmverityArgument> = vec![];
for (i, tenant_apk) in tenant_apks.iter().enumerate() {
@@ -197,7 +206,7 @@
ApkDmverityArgument {
apk: tenant_apk.to_str().unwrap(),
idsig: tenant_idsigs[i].to_str().unwrap(),
- name: &tenant_apk_names[i],
+ name: &tenant_apk_block_dev[i],
saved_root_hash: None,
}
});
@@ -206,9 +215,50 @@
let mut apkdmverity_child = run_apkdmverity(&apkdmverity_arguments)?;
apkdmverity_child.wait()?;
+ let tenant_apks_data = tenant_hashes_from_idsig
+ .into_iter()
+ .enumerate()
+ .map(|(i, root_hash)| {
+ let mount_path = format!("/dev/block/mapper/{}", &tenant_apk_block_dev[i]);
+ get_data_from_apk(&mount_path, root_hash, false)
+ })
+ .collect::<Result<Vec<_>>>()?;
+
+ Ok(tenant_apks_data)
+}
+
+// Validation logic includes:
+// 1. The tenant_apk a subset of apks described in tenant_config (comparison is by package name)
+// 2. The order of description in tenant_config is irrelevant.
+pub(crate) fn validate_tenant_apks_against_tenant_config(
+ tenant_apk: &[ApkData], // data extracted from the apk passed from host
+ tenant_config: &[TenantConfig],
+) -> Result<()> {
+ let apk_names_in_config = tenant_config
+ .iter()
+ .filter_map(|config| {
+ if let TenantConfig::Apk(config) = config {
+ Some(config.name.clone())
+ } else {
+ None
+ }
+ })
+ .collect::<Vec<_>>();
+ let tenant_apk = tenant_apk.iter().map(|apk| apk.package_name.clone()).collect::<Vec<_>>();
+ ensure!(
+ is_subset(&tenant_apk, &apk_names_in_config),
+ MicrodroidError::PayloadVerificationFailed(format!(
+ "Tenant apk {tenant_apk:?} must be subset of TenancyConfig {tenant_config:?}"
+ ))
+ );
Ok(())
}
+fn is_subset<T: Eq + Hash + Clone>(a: &[T], b: &[T]) -> bool {
+ let b_set: HashSet<_> = b.iter().collect();
+ a.iter().all(|item| b_set.contains(item))
+}
+
fn validate_manifest_info(info: &ApkManifestInfo) -> Result<()> {
ensure!(
info.has_relaxed_rollback_protection_permission == info.rollback_index.is_some(),
diff --git a/libs/libmicrodroid_payload_metadata/config/src/lib.rs b/libs/libmicrodroid_payload_metadata/config/src/lib.rs
index cf338aa..50555ca 100644
--- a/libs/libmicrodroid_payload_metadata/config/src/lib.rs
+++ b/libs/libmicrodroid_payload_metadata/config/src/lib.rs
@@ -133,6 +133,8 @@
Apk(TenantConfiguration),
}
+// TODO: More (optional) configuration need to be added for tenant
+// such as min_version, expected signer. Ensure as they are added, verification code matches these.
/// Tenant config
#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
pub struct TenantConfiguration {