Microdroid stores APEX pubkeys in instance.img
Just like APK's root hash, APEX pubkeys are stored in instance.img so
that in subsequent boots, pubkeys from payload APEXes are checked
against ones in instance.img.
Bug: 198361718
Test: MicrodroidHostTestCases
Change-Id: I5385700e86b4962133df80b750208ce45cec2655
diff --git a/microdroid_manager/src/instance.rs b/microdroid_manager/src/instance.rs
index 51d74b0..73983a7 100644
--- a/microdroid_manager/src/instance.rs
+++ b/microdroid_manager/src/instance.rs
@@ -311,16 +311,22 @@
ret
}
-#[derive(Debug, Serialize, Deserialize)]
+#[derive(Debug, Serialize, Deserialize, PartialEq)]
pub struct MicrodroidData {
pub apk_data: ApkData,
- // TODO(b/197053593) add data for APEXes
+ pub apex_data: Vec<ApexData>,
}
-#[derive(Debug, Serialize, Deserialize)]
+#[derive(Debug, Serialize, Deserialize, PartialEq)]
pub struct ApkData {
pub root_hash: Box<RootHash>,
// TODO(b/199143508) add cert
}
pub type RootHash = [u8];
+
+#[derive(Debug, Serialize, Deserialize, PartialEq)]
+pub struct ApexData {
+ pub name: String,
+ pub pubkey: Vec<u8>,
+}
diff --git a/microdroid_manager/src/main.rs b/microdroid_manager/src/main.rs
index 4f192dc..2e80b90 100644
--- a/microdroid_manager/src/main.rs
+++ b/microdroid_manager/src/main.rs
@@ -16,7 +16,7 @@
mod instance;
mod ioutil;
-mod metadata;
+mod payload;
use crate::instance::{ApkData, InstanceDisk, MicrodroidData, RootHash};
use anyhow::{anyhow, bail, Context, Result};
@@ -25,8 +25,10 @@
use binder::{FromIBinder, Strong};
use idsig::V4Signature;
use log::{error, info, warn};
+use microdroid_metadata::Metadata;
use microdroid_payload_config::{Task, TaskType, VmPayloadConfig};
use nix::ioctl_read_bad;
+use payload::{get_apex_data_from_payload, load_metadata};
use rustutils::system_properties;
use rustutils::system_properties::PropertyWatcher;
use std::fs::{self, File, OpenOptions};
@@ -90,26 +92,23 @@
kernlog::init()?;
info!("started.");
- let metadata = metadata::load()?;
+ let metadata = load_metadata()?;
let mut instance = InstanceDisk::new()?;
- let data = instance.read_microdroid_data().context("Failed to read identity data")?;
- let saved_root_hash: Option<&[u8]> =
- if let Some(data) = data.as_ref() { Some(&data.apk_data.root_hash) } else { None };
+ let saved_data = instance.read_microdroid_data().context("Failed to read identity data")?;
// Verify the payload before using it.
- let verified_root_hash =
- verify_payload(saved_root_hash).context("Payload verification failed")?;
- if let Some(saved_root_hash) = saved_root_hash {
- if saved_root_hash == verified_root_hash.as_ref() {
- info!("Saved root_hash is verified.");
+ let verified_data =
+ verify_payload(&metadata, saved_data.as_ref()).context("Payload verification failed")?;
+ if let Some(saved_data) = saved_data {
+ if saved_data == verified_data {
+ info!("Saved data is verified.");
} else {
- bail!("Detected an update of the APK which isn't supported yet.");
+ bail!("Detected an update of the payload which isn't supported yet.");
}
} else {
- info!("Saving APK root_hash: {}", to_hex_string(verified_root_hash.as_ref()));
- let data = MicrodroidData { apk_data: ApkData { root_hash: verified_root_hash } };
- instance.write_microdroid_data(&data).context("Failed to write identity data")?;
+ info!("Saving verified data.");
+ instance.write_microdroid_data(&verified_data).context("Failed to write identity data")?;
}
wait_for_apex_config_done()?;
@@ -138,12 +137,17 @@
Ok(())
}
-// Verify payload before executing it. Full verification (which is slow) is done when the root_hash
-// values from the idsig file and the instance disk are different. This function returns the
-// verified root hash that can be saved to the instance disk.
-fn verify_payload(root_hash: Option<&RootHash>) -> Result<Box<RootHash>> {
+// Verify payload before executing it. For APK payload, Full verification (which is slow) is done
+// when the root_hash values from the idsig file and the instance disk are different. This function
+// returns the verified root hash (for APK payload) and pubkeys (for APEX payloads) that can be
+// saved to the instance disk.
+fn verify_payload(
+ metadata: &Metadata,
+ saved_data: Option<&MicrodroidData>,
+) -> Result<MicrodroidData> {
let start_time = SystemTime::now();
+ let root_hash = saved_data.map(|d| &d.apk_data.root_hash);
let root_hash_from_idsig = get_apk_root_hash_from_idsig()?;
let root_hash_trustful = root_hash == Some(&root_hash_from_idsig);
@@ -156,6 +160,11 @@
// Start apkdmverity and wait for the dm-verify block
system_properties::write("ctl.start", "apkdmverity")?;
+
+ // While waiting for apkdmverity to mount APK, gathers APEX pubkeys used by APEXd.
+ // These will be compared ones from instance.img.
+ let apex_data_from_payload = get_apex_data_from_payload(metadata)?;
+
ioutil::wait_for_file(DM_MOUNTED_APK_PATH, WAIT_TIMEOUT)?;
// Do the full verification if the root_hash is un-trustful. This requires the full scanning of
@@ -171,7 +180,10 @@
// At this point, we can ensure that the root_hash from the idsig file is trusted, either by
// fully verifying the APK or by comparing it with the saved root_hash.
- Ok(root_hash_from_idsig)
+ Ok(MicrodroidData {
+ apk_data: ApkData { root_hash: root_hash_from_idsig },
+ apex_data: apex_data_from_payload,
+ })
}
// Waits until linker config is generated
diff --git a/microdroid_manager/src/metadata.rs b/microdroid_manager/src/metadata.rs
deleted file mode 100644
index 432a134..0000000
--- a/microdroid_manager/src/metadata.rs
+++ /dev/null
@@ -1,32 +0,0 @@
-// Copyright 2021, 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.
-
-//! Payload metadata from /dev/block/by-name/payload-metadata
-
-use crate::ioutil;
-
-use anyhow::Result;
-use log::info;
-use microdroid_metadata::{read_metadata, Metadata};
-use std::time::Duration;
-
-const WAIT_TIMEOUT: Duration = Duration::from_secs(10);
-const PAYLOAD_METADATA_PATH: &str = "/dev/block/by-name/payload-metadata";
-
-/// loads payload metadata from /dev/block/by-name/paylaod-metadata
-pub fn load() -> Result<Metadata> {
- info!("loading payload metadata...");
- let file = ioutil::wait_for_file(PAYLOAD_METADATA_PATH, WAIT_TIMEOUT)?;
- read_metadata(file)
-}
diff --git a/microdroid_manager/src/payload.rs b/microdroid_manager/src/payload.rs
new file mode 100644
index 0000000..bfc6c09
--- /dev/null
+++ b/microdroid_manager/src/payload.rs
@@ -0,0 +1,61 @@
+// Copyright 2021, 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.
+
+//! Routines for handling payload
+
+use crate::instance::ApexData;
+use crate::ioutil::wait_for_file;
+use anyhow::Result;
+use log::info;
+use microdroid_metadata::{read_metadata, Metadata};
+use std::fs::File;
+use std::io::Read;
+use std::time::Duration;
+use zip::ZipArchive;
+
+const APEX_PUBKEY_ENTRY: &str = "apex_pubkey";
+const PAYLOAD_METADATA_PATH: &str = "/dev/block/by-name/payload-metadata";
+const WAIT_TIMEOUT: Duration = Duration::from_secs(10);
+
+/// Loads payload metadata from /dev/block/by-name/payload-metadata
+pub fn load_metadata() -> Result<Metadata> {
+ info!("loading payload metadata...");
+ let file = wait_for_file(PAYLOAD_METADATA_PATH, WAIT_TIMEOUT)?;
+ read_metadata(file)
+}
+
+/// Loads (name, pubkey) from payload apexes and returns them as sorted by name.
+pub fn get_apex_data_from_payload(metadata: &Metadata) -> Result<Vec<ApexData>> {
+ let mut apex_data: Vec<ApexData> = metadata
+ .apexes
+ .iter()
+ .map(|apex| {
+ let name = apex.name.clone();
+ let partition = format!("/dev/block/by-name/{}", apex.partition_name);
+ let pubkey = get_pubkey_from_apex(&partition)?;
+ Ok(ApexData { name, pubkey })
+ })
+ .collect::<Result<Vec<_>>>()?;
+ apex_data.sort_by(|a, b| a.name.cmp(&b.name));
+ Ok(apex_data)
+}
+
+fn get_pubkey_from_apex(path: &str) -> Result<Vec<u8>> {
+ let f = File::open(path)?;
+ let mut z = ZipArchive::new(f)?;
+ let mut pubkey_file = z.by_name(APEX_PUBKEY_ENTRY)?;
+ let mut pubkey = Vec::new();
+ pubkey_file.read_to_end(&mut pubkey)?;
+ Ok(pubkey)
+}