blob: fa064a7f3e1d1dfe20a925552292e1589808bde0 [file] [log] [blame]
Jooyung Han347d9f22021-05-28 00:05:14 +09001// Copyright 2021, The Android Open Source Project
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15//! Microdroid Manager
16
Jiyong Park21ce2c52021-08-28 02:32:17 +090017mod instance;
Jooyung Hanf48ceb42021-06-01 18:00:04 +090018mod ioutil;
Jooyung Han7a343f92021-09-08 22:53:11 +090019mod payload;
Jooyung Han347d9f22021-05-28 00:05:14 +090020
Jiyong Parkf7dea252021-09-08 01:42:54 +090021use crate::instance::{ApkData, InstanceDisk, MicrodroidData, RootHash};
Andrew Scullb2f44472022-01-21 14:41:34 +000022use android_hardware_security_dice::aidl::android::hardware::security::dice::{
23 Config::Config, InputValues::InputValues, Mode::Mode,
24};
25use android_security_dice::aidl::android::security::dice::IDiceMaintenance::IDiceMaintenance;
Jooyung Handd0a1732021-11-23 15:26:20 +090026use anyhow::{anyhow, bail, ensure, Context, Error, Result};
Jiyong Parka41535b2021-09-10 19:31:48 +090027use apkverify::{get_public_key_der, verify};
Andrew Walbranc4ce7872022-07-29 11:26:41 +000028use binder::{wait_for_interface, Strong};
29use binder_common::rpc_client::connect_rpc_binder;
Andrew Scullb2f44472022-01-21 14:41:34 +000030use diced_utils::cbor::encode_header;
Inseob Kim197748b2021-12-01 19:49:00 +090031use glob::glob;
Jiyong Park21ce2c52021-08-28 02:32:17 +090032use idsig::V4Signature;
Inseob Kim197748b2021-12-01 19:49:00 +090033use itertools::sorted;
Andrew Scull684590f2022-01-27 16:14:26 +000034use log::{error, info};
Jooyung Han4a9b3bf2021-09-10 17:19:00 +090035use microdroid_metadata::{write_metadata, Metadata};
Jooyung Han634e2d72021-06-10 16:27:38 +090036use microdroid_payload_config::{Task, TaskType, VmPayloadConfig};
Andrew Sculla0d1b1a2022-05-24 19:32:47 +000037use openssl::sha::Sha512;
Jooyung Han4a9b3bf2021-09-10 17:19:00 +090038use payload::{get_apex_data_from_payload, load_metadata, to_metadata};
Andrew Scull34916a72022-01-30 21:34:24 +000039use rand::Fill;
Jiyong Parkbb4a9872021-09-06 15:59:21 +090040use rustutils::system_properties;
Joel Galenson482704c2021-07-29 15:53:53 -070041use rustutils::system_properties::PropertyWatcher;
Andrew Scullb2f44472022-01-21 14:41:34 +000042use std::convert::TryInto;
Inseob Kim197748b2021-12-01 19:49:00 +090043use std::fs::{self, create_dir, File, OpenOptions};
Inseob Kim11f40d02022-06-13 17:16:00 +090044use std::io::Write;
Inseob Kimc7d28c72021-10-25 14:28:10 +000045use std::os::unix::io::{FromRawFd, IntoRawFd};
Jooyung Hanf48ceb42021-06-01 18:00:04 +090046use std::path::Path;
Inseob Kim217038e2021-11-25 11:15:06 +090047use std::process::{Child, Command, Stdio};
Jiyong Park8611a6c2021-07-09 18:17:44 +090048use std::str;
Jiyong Parkbb4a9872021-09-06 15:59:21 +090049use std::time::{Duration, SystemTime};
Jiyong Park8611a6c2021-07-09 18:17:44 +090050use vsock::VsockStream;
Jooyung Han634e2d72021-06-10 16:27:38 +090051
Inseob Kimd0587562021-09-01 21:27:32 +090052use android_system_virtualmachineservice::aidl::android::system::virtualmachineservice::IVirtualMachineService::{
Jooyung Han5c6d4172021-12-06 14:17:52 +090053 ERROR_PAYLOAD_CHANGED, ERROR_PAYLOAD_VERIFICATION_FAILED, ERROR_PAYLOAD_INVALID_CONFIG, ERROR_UNKNOWN, VM_BINDER_SERVICE_PORT, VM_STREAM_SERVICE_PORT, IVirtualMachineService,
Inseob Kimd0587562021-09-01 21:27:32 +090054};
Inseob Kim1b95f2e2021-08-19 13:17:40 +090055
Jooyung Han634e2d72021-06-10 16:27:38 +090056const WAIT_TIMEOUT: Duration = Duration::from_secs(10);
Inseob Kim197748b2021-12-01 19:49:00 +090057const MAIN_APK_PATH: &str = "/dev/block/by-name/microdroid-apk";
58const MAIN_APK_IDSIG_PATH: &str = "/dev/block/by-name/microdroid-apk-idsig";
59const MAIN_APK_DEVICE_NAME: &str = "microdroid-apk";
60const EXTRA_APK_PATH_PATTERN: &str = "/dev/block/by-name/extra-apk-*";
61const EXTRA_IDSIG_PATH_PATTERN: &str = "/dev/block/by-name/extra-idsig-*";
Jooyung Han19c1d6c2021-08-06 14:08:16 +090062const DM_MOUNTED_APK_PATH: &str = "/dev/block/mapper/microdroid-apk";
Inseob Kim217038e2021-11-25 11:15:06 +090063const APKDMVERITY_BIN: &str = "/system/bin/apkdmverity";
64const ZIPFUSE_BIN: &str = "/system/bin/zipfuse";
Andrew Scullab72ec52022-03-14 09:10:52 +000065const AVF_STRICT_BOOT: &str = "/sys/firmware/devicetree/base/chosen/avf,strict-boot";
66const AVF_NEW_INSTANCE: &str = "/sys/firmware/devicetree/base/chosen/avf,new-instance";
Jooyung Han347d9f22021-05-28 00:05:14 +090067
Inseob Kim1b95f2e2021-08-19 13:17:40 +090068/// The CID representing the host VM
69const VMADDR_CID_HOST: u32 = 2;
70
Jiyong Parkbb4a9872021-09-06 15:59:21 +090071const APEX_CONFIG_DONE_PROP: &str = "apex_config.done";
Andrew Scull65ddfc42022-02-14 21:03:58 +000072const APP_DEBUGGABLE_PROP: &str = "ro.boot.microdroid.app_debuggable";
Jiyong Parkbb4a9872021-09-06 15:59:21 +090073
Inseob Kim11f40d02022-06-13 17:16:00 +090074// SYNC WITH virtualizationservice/src/crosvm.rs
75const FAILURE_SERIAL_DEVICE: &str = "/dev/ttyS1";
76
Jooyung Handd0a1732021-11-23 15:26:20 +090077#[derive(thiserror::Error, Debug)]
78enum MicrodroidError {
Inseob Kim11f40d02022-06-13 17:16:00 +090079 #[error("Cannot connect to virtualization service: {0}")]
80 FailedToConnectToVirtualizationService(String),
Jooyung Handd0a1732021-11-23 15:26:20 +090081 #[error("Payload has changed: {0}")]
82 PayloadChanged(String),
83 #[error("Payload verification has failed: {0}")]
84 PayloadVerificationFailed(String),
Jooyung Han5c6d4172021-12-06 14:17:52 +090085 #[error("Payload config is invalid: {0}")]
86 InvalidConfig(String),
Jooyung Handd0a1732021-11-23 15:26:20 +090087}
88
89fn translate_error(err: &Error) -> (i32, String) {
90 if let Some(e) = err.downcast_ref::<MicrodroidError>() {
91 match e {
92 MicrodroidError::PayloadChanged(msg) => (ERROR_PAYLOAD_CHANGED, msg.to_string()),
93 MicrodroidError::PayloadVerificationFailed(msg) => {
94 (ERROR_PAYLOAD_VERIFICATION_FAILED, msg.to_string())
95 }
Jooyung Han5c6d4172021-12-06 14:17:52 +090096 MicrodroidError::InvalidConfig(msg) => (ERROR_PAYLOAD_INVALID_CONFIG, msg.to_string()),
Inseob Kim11f40d02022-06-13 17:16:00 +090097
98 // Connection failure won't be reported to VS; return the default value
99 MicrodroidError::FailedToConnectToVirtualizationService(msg) => {
100 (ERROR_UNKNOWN, msg.to_string())
101 }
Jooyung Handd0a1732021-11-23 15:26:20 +0900102 }
103 } else {
104 (ERROR_UNKNOWN, err.to_string())
105 }
106}
107
Inseob Kim11f40d02022-06-13 17:16:00 +0900108fn write_death_reason_to_serial(err: &Error) -> Result<()> {
109 let death_reason = if let Some(e) = err.downcast_ref::<MicrodroidError>() {
110 match e {
111 MicrodroidError::FailedToConnectToVirtualizationService(_) => {
112 "MICRODROID_FAILED_TO_CONNECT_TO_VIRTUALIZATION_SERVICE"
113 }
114 MicrodroidError::PayloadChanged(_) => "MICRODROID_PAYLOAD_HAS_CHANGED",
115 MicrodroidError::PayloadVerificationFailed(_) => {
116 "MICRODROID_PAYLOAD_VERIFICATION_FAILED"
117 }
118 MicrodroidError::InvalidConfig(_) => "MICRODROID_INVALID_PAYLOAD_CONFIG",
119 }
120 } else {
121 "MICRODROID_UNKNOWN_RUNTIME_ERROR"
122 };
123
124 let death_reason_bytes = death_reason.as_bytes();
125 let mut sent_total = 0;
126 while sent_total < death_reason_bytes.len() {
127 // TODO(b/220071963): Sometimes, sending more than 16 bytes at once makes MM hang.
128 let begin = sent_total;
129 let end = std::cmp::min(begin.saturating_add(16), death_reason_bytes.len());
130 OpenOptions::new()
131 .read(false)
132 .write(true)
133 .open(FAILURE_SERIAL_DEVICE)?
134 .write_all(&death_reason_bytes[begin..end])?;
135 sent_total = end;
136 }
137
138 Ok(())
139}
140
Inseob Kim1b95f2e2021-08-19 13:17:40 +0900141fn get_vms_rpc_binder() -> Result<Strong<dyn IVirtualMachineService>> {
Andrew Walbranc4ce7872022-07-29 11:26:41 +0000142 connect_rpc_binder(VMADDR_CID_HOST, VM_BINDER_SERVICE_PORT as u32)
143 .context("Cannot connect to RPC service")
Inseob Kim1b95f2e2021-08-19 13:17:40 +0900144}
145
Inseob Kim437f1052022-06-21 11:30:22 +0900146fn main() -> Result<()> {
147 scopeguard::defer! {
148 info!("Shutting down...");
Jooyung Hanbfe086f2021-10-28 10:15:45 +0900149 if let Err(e) = system_properties::write("sys.powerctl", "shutdown") {
150 error!("failed to shutdown {:?}", e);
151 }
Jooyung Han311b1202021-09-14 22:00:16 +0900152 }
Inseob Kim437f1052022-06-21 11:30:22 +0900153
154 try_main().map_err(|e| {
155 error!("Failed with {:?}.", e);
156 if let Err(e) = write_death_reason_to_serial(&e) {
157 error!("Failed to write death reason {:?}", e);
158 }
159 e
160 })
Jooyung Han311b1202021-09-14 22:00:16 +0900161}
162
163fn try_main() -> Result<()> {
Andrew Sculle127cff2022-02-15 15:34:04 +0000164 let _ = kernlog::init();
Jooyung Han347d9f22021-05-28 00:05:14 +0900165 info!("started.");
166
Inseob Kim11f40d02022-06-13 17:16:00 +0900167 let service = get_vms_rpc_binder()
168 .context("cannot connect to VirtualMachineService")
169 .map_err(|e| MicrodroidError::FailedToConnectToVirtualizationService(e.to_string()))?;
Jooyung Han5c6d4172021-12-06 14:17:52 +0900170 match try_run_payload(&service) {
171 Ok(code) => {
172 info!("notifying payload finished");
173 service.notifyPayloadFinished(code)?;
174 if code == 0 {
175 info!("task successfully finished");
176 } else {
177 error!("task exited with exit code: {}", code);
178 }
179 Ok(())
180 }
181 Err(err) => {
Jooyung Han5c6d4172021-12-06 14:17:52 +0900182 let (error_code, message) = translate_error(&err);
183 service.notifyError(error_code, &message)?;
184 Err(err)
185 }
Jooyung Handd0a1732021-11-23 15:26:20 +0900186 }
187}
188
Inseob Kimb2519c52022-04-14 02:10:09 +0900189fn dice_derivation(verified_data: &MicrodroidData, payload_config_path: &str) -> Result<()> {
Andrew Scullb2f44472022-01-21 14:41:34 +0000190 // Calculate compound digests of code and authorities
Andrew Sculla0d1b1a2022-05-24 19:32:47 +0000191 let mut code_hash_ctx = Sha512::new();
192 let mut authority_hash_ctx = Sha512::new();
Andrew Scullb2f44472022-01-21 14:41:34 +0000193 code_hash_ctx.update(verified_data.apk_data.root_hash.as_ref());
194 authority_hash_ctx.update(verified_data.apk_data.pubkey.as_ref());
Inseob Kimb2519c52022-04-14 02:10:09 +0900195 for extra_apk in &verified_data.extra_apks_data {
Andrew Scullb2f44472022-01-21 14:41:34 +0000196 code_hash_ctx.update(extra_apk.root_hash.as_ref());
197 authority_hash_ctx.update(extra_apk.pubkey.as_ref());
198 }
Inseob Kimb2519c52022-04-14 02:10:09 +0900199 for apex in &verified_data.apex_data {
Andrew Scullb2f44472022-01-21 14:41:34 +0000200 code_hash_ctx.update(apex.root_digest.as_ref());
201 authority_hash_ctx.update(apex.public_key.as_ref());
202 }
Andrew Sculla0d1b1a2022-05-24 19:32:47 +0000203 let code_hash = code_hash_ctx.finish();
204 let authority_hash = authority_hash_ctx.finish();
Andrew Scullb2f44472022-01-21 14:41:34 +0000205
206 // {
207 // -70002: "Microdroid payload",
208 // -71000: payload_config_path
209 // }
210 let mut config_desc = vec![
211 0xa2, 0x3a, 0x00, 0x01, 0x11, 0x71, 0x72, 0x4d, 0x69, 0x63, 0x72, 0x6f, 0x64, 0x72, 0x6f,
212 0x69, 0x64, 0x20, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x3a, 0x00, 0x01, 0x15, 0x57,
213 ];
214 let config_path_bytes = payload_config_path.as_bytes();
215 encode_header(3, config_path_bytes.len().try_into().unwrap(), &mut config_desc)?;
216 config_desc.extend_from_slice(config_path_bytes);
217
Andrew Scull65ddfc42022-02-14 21:03:58 +0000218 // Check app debuggability, conervatively assuming it is debuggable
219 let app_debuggable = system_properties::read_bool(APP_DEBUGGABLE_PROP, true)?;
220
Andrew Scullb2f44472022-01-21 14:41:34 +0000221 // Send the details to diced
222 let diced =
223 wait_for_interface::<dyn IDiceMaintenance>("android.security.dice.IDiceMaintenance")
224 .context("IDiceMaintenance service not found")?;
225 diced
226 .demoteSelf(&[InputValues {
227 codeHash: code_hash,
228 config: Config { desc: config_desc },
229 authorityHash: authority_hash,
230 authorityDescriptor: None,
Andrew Scull65ddfc42022-02-14 21:03:58 +0000231 mode: if app_debuggable { Mode::DEBUG } else { Mode::NORMAL },
Inseob Kimb2519c52022-04-14 02:10:09 +0900232 hidden: verified_data.salt.clone().try_into().unwrap(),
Andrew Scullb2f44472022-01-21 14:41:34 +0000233 }])
234 .context("IDiceMaintenance::demoteSelf failed")?;
235 Ok(())
236}
237
Andrew Scullab72ec52022-03-14 09:10:52 +0000238fn is_strict_boot() -> bool {
239 Path::new(AVF_STRICT_BOOT).exists()
240}
241
242fn is_new_instance() -> bool {
243 Path::new(AVF_NEW_INSTANCE).exists()
244}
245
Jooyung Han5c6d4172021-12-06 14:17:52 +0900246fn try_run_payload(service: &Strong<dyn IVirtualMachineService>) -> Result<i32> {
Jooyung Han311b1202021-09-14 22:00:16 +0900247 let metadata = load_metadata().context("Failed to load payload metadata")?;
Jooyung Han19c1d6c2021-08-06 14:08:16 +0900248
Jooyung Han311b1202021-09-14 22:00:16 +0900249 let mut instance = InstanceDisk::new().context("Failed to load instance.img")?;
Jooyung Han7a343f92021-09-08 22:53:11 +0900250 let saved_data = instance.read_microdroid_data().context("Failed to read identity data")?;
Jiyong Parkbb4a9872021-09-06 15:59:21 +0900251
Andrew Scullab72ec52022-03-14 09:10:52 +0000252 if is_strict_boot() {
253 // Provisioning must happen on the first boot and never again.
254 if is_new_instance() {
255 ensure!(
256 saved_data.is_none(),
257 MicrodroidError::InvalidConfig("Found instance data on first boot.".to_string())
258 );
259 } else {
260 ensure!(
261 saved_data.is_some(),
262 MicrodroidError::InvalidConfig("Instance data not found.".to_string())
263 );
264 };
265 }
266
Jiyong Parkbb4a9872021-09-06 15:59:21 +0900267 // Verify the payload before using it.
Inseob Kim11f40d02022-06-13 17:16:00 +0900268 let verified_data = verify_payload(&metadata, saved_data.as_ref())
269 .context("Payload verification failed")
270 .map_err(|e| MicrodroidError::PayloadVerificationFailed(e.to_string()))?;
Jooyung Han7a343f92021-09-08 22:53:11 +0900271 if let Some(saved_data) = saved_data {
Jooyung Handd0a1732021-11-23 15:26:20 +0900272 ensure!(
273 saved_data == verified_data,
274 MicrodroidError::PayloadChanged(String::from(
275 "Detected an update of the payload which isn't supported yet."
276 ))
277 );
278 info!("Saved data is verified.");
Jiyong Parkbb4a9872021-09-06 15:59:21 +0900279 } else {
Jooyung Han7a343f92021-09-08 22:53:11 +0900280 info!("Saving verified data.");
281 instance.write_microdroid_data(&verified_data).context("Failed to write identity data")?;
Jooyung Han19c1d6c2021-08-06 14:08:16 +0900282 }
283
Inseob Kimb2519c52022-04-14 02:10:09 +0900284 // To minimize the exposure to untrusted data, derive dice profile as soon as possible.
285 info!("DICE derivation for payload");
286 dice_derivation(&verified_data, &metadata.payload_config_path)?;
287
Jooyung Hana6d11eb2021-09-10 11:48:05 +0900288 // Before reading a file from the APK, start zipfuse
Andrew Scullcc339a12022-07-04 12:44:19 +0000289 let noexec = false;
Inseob Kim217038e2021-11-25 11:15:06 +0900290 run_zipfuse(
Andrew Scullcc339a12022-07-04 12:44:19 +0000291 noexec,
Inseob Kim217038e2021-11-25 11:15:06 +0900292 "fscontext=u:object_r:zipfusefs:s0,context=u:object_r:system_file:s0",
293 Path::new("/dev/block/mapper/microdroid-apk"),
294 Path::new("/mnt/apk"),
295 )
296 .context("Failed to run zipfuse")?;
Jiyong Park21ce2c52021-08-28 02:32:17 +0900297
Jooyung Han5c6d4172021-12-06 14:17:52 +0900298 ensure!(
299 !metadata.payload_config_path.is_empty(),
300 MicrodroidError::InvalidConfig("No payload_config_path in metadata".to_string())
301 );
Inseob Kim197748b2021-12-01 19:49:00 +0900302
Jooyung Han5c6d4172021-12-06 14:17:52 +0900303 let config = load_config(Path::new(&metadata.payload_config_path))?;
Shikha Panwar6f03c942022-04-13 20:26:50 +0000304
Inseob Kim197748b2021-12-01 19:49:00 +0900305 if config.extra_apks.len() != verified_data.extra_apks_data.len() {
306 return Err(anyhow!(
307 "config expects {} extra apks, but found only {}",
308 config.extra_apks.len(),
309 verified_data.extra_apks_data.len()
310 ));
311 }
312 mount_extra_apks(&config)?;
Jooyung Han634e2d72021-06-10 16:27:38 +0900313
Jooyung Han5c6d4172021-12-06 14:17:52 +0900314 // Wait until apex config is done. (e.g. linker configuration for apexes)
315 // TODO(jooyung): wait until sys.boot_completed?
316 wait_for_apex_config_done()?;
317
Inseob Kimcd9c1dd2022-07-13 17:13:45 +0900318 // Start tombstone_transmit if enabled
319 if config.export_tombstones {
320 system_properties::write("ctl.start", "tombstone_transmit")
321 .context("Failed to start tombstone_transmit")?;
322 } else {
323 system_properties::write("ctl.stop", "tombstoned").context("Failed to stop tombstoned")?;
324 }
325
Jooyung Han5c6d4172021-12-06 14:17:52 +0900326 ensure!(
327 config.task.is_some(),
328 MicrodroidError::InvalidConfig("No task in VM config".to_string())
329 );
Victor Hsieh8d006ba2022-05-02 09:42:02 -0700330 system_properties::write("dev.bootcomplete", "1").context("set dev.bootcomplete")?;
Jooyung Han5c6d4172021-12-06 14:17:52 +0900331 exec_task(&config.task.unwrap(), service)
Jooyung Han347d9f22021-05-28 00:05:14 +0900332}
333
Inseob Kim217038e2021-11-25 11:15:06 +0900334struct ApkDmverityArgument<'a> {
335 apk: &'a str,
336 idsig: &'a str,
337 name: &'a str,
Inseob Kim197748b2021-12-01 19:49:00 +0900338 saved_root_hash: Option<&'a RootHash>,
Inseob Kim217038e2021-11-25 11:15:06 +0900339}
340
341fn run_apkdmverity(args: &[ApkDmverityArgument]) -> Result<Child> {
342 let mut cmd = Command::new(APKDMVERITY_BIN);
343
344 cmd.stdin(Stdio::null()).stdout(Stdio::null()).stderr(Stdio::null());
345
346 for argument in args {
347 cmd.arg("--apk").arg(argument.apk).arg(argument.idsig).arg(argument.name);
Inseob Kim197748b2021-12-01 19:49:00 +0900348 if let Some(root_hash) = argument.saved_root_hash {
349 cmd.arg(&to_hex_string(root_hash));
350 } else {
351 cmd.arg("none");
352 }
Inseob Kim217038e2021-11-25 11:15:06 +0900353 }
354
355 cmd.spawn().context("Spawn apkdmverity")
356}
357
Andrew Scullcc339a12022-07-04 12:44:19 +0000358fn run_zipfuse(noexec: bool, option: &str, zip_path: &Path, mount_dir: &Path) -> Result<Child> {
359 let mut cmd = Command::new(ZIPFUSE_BIN);
360 if noexec {
361 cmd.arg("--noexec");
362 }
363 cmd.arg("-o")
Inseob Kim217038e2021-11-25 11:15:06 +0900364 .arg(option)
365 .arg(zip_path)
366 .arg(mount_dir)
367 .stdin(Stdio::null())
368 .stdout(Stdio::null())
369 .stderr(Stdio::null())
370 .spawn()
371 .context("Spawn zipfuse")
372}
373
Jooyung Han7a343f92021-09-08 22:53:11 +0900374// Verify payload before executing it. For APK payload, Full verification (which is slow) is done
375// when the root_hash values from the idsig file and the instance disk are different. This function
376// returns the verified root hash (for APK payload) and pubkeys (for APEX payloads) that can be
377// saved to the instance disk.
378fn verify_payload(
379 metadata: &Metadata,
380 saved_data: Option<&MicrodroidData>,
381) -> Result<MicrodroidData> {
Jiyong Parkbb4a9872021-09-06 15:59:21 +0900382 let start_time = SystemTime::now();
383
Inseob Kim197748b2021-12-01 19:49:00 +0900384 // Verify main APK
Jooyung Han7a343f92021-09-08 22:53:11 +0900385 let root_hash = saved_data.map(|d| &d.apk_data.root_hash);
Inseob Kim197748b2021-12-01 19:49:00 +0900386 let root_hash_from_idsig = get_apk_root_hash_from_idsig(MAIN_APK_IDSIG_PATH)?;
Jiyong Parkf7dea252021-09-08 01:42:54 +0900387 let root_hash_trustful = root_hash == Some(&root_hash_from_idsig);
Jiyong Parkbb4a9872021-09-06 15:59:21 +0900388
Jiyong Parkf7dea252021-09-08 01:42:54 +0900389 // If root_hash can be trusted, pass it to apkdmverity so that it uses the passed root_hash
Jiyong Parkbb4a9872021-09-06 15:59:21 +0900390 // instead of the value read from the idsig file.
Inseob Kim197748b2021-12-01 19:49:00 +0900391 let main_apk_argument = {
392 ApkDmverityArgument {
393 apk: MAIN_APK_PATH,
394 idsig: MAIN_APK_IDSIG_PATH,
395 name: MAIN_APK_DEVICE_NAME,
396 saved_root_hash: if root_hash_trustful {
397 Some(root_hash_from_idsig.as_ref())
398 } else {
399 None
400 },
401 }
402 };
403 let mut apkdmverity_arguments = vec![main_apk_argument];
404
405 // Verify extra APKs
406 // For now, we can't read the payload config, so glob APKs and idsigs.
407 // Later, we'll see if it matches with the payload config.
408
409 // sort globbed paths to match apks (extra-apk-{idx}) and idsigs (extra-idsig-{idx})
410 // e.g. "extra-apk-0" corresponds to "extra-idsig-0"
411 let extra_apks =
412 sorted(glob(EXTRA_APK_PATH_PATTERN)?.collect::<Result<Vec<_>, _>>()?).collect::<Vec<_>>();
413 let extra_idsigs =
414 sorted(glob(EXTRA_IDSIG_PATH_PATTERN)?.collect::<Result<Vec<_>, _>>()?).collect::<Vec<_>>();
415 if extra_apks.len() != extra_idsigs.len() {
416 return Err(anyhow!(
417 "Extra apks/idsigs mismatch: {} apks but {} idsigs",
418 extra_apks.len(),
419 extra_idsigs.len()
420 ));
421 }
422 let extra_apks_count = extra_apks.len();
423
424 let (extra_apk_names, extra_root_hashes_from_idsig): (Vec<_>, Vec<_>) = extra_idsigs
425 .iter()
426 .enumerate()
427 .map(|(i, extra_idsig)| {
428 (
429 format!("extra-apk-{}", i),
430 get_apk_root_hash_from_idsig(extra_idsig.to_str().unwrap())
431 .expect("Can't find root hash from extra idsig"),
432 )
433 })
434 .unzip();
435
436 let saved_extra_root_hashes: Vec<_> = saved_data
437 .map(|d| d.extra_apks_data.iter().map(|apk_data| &apk_data.root_hash).collect())
438 .unwrap_or_else(Vec::new);
439 let extra_root_hashes_trustful: Vec<_> = extra_root_hashes_from_idsig
440 .iter()
441 .enumerate()
442 .map(|(i, root_hash_from_idsig)| {
443 saved_extra_root_hashes.get(i).copied() == Some(root_hash_from_idsig)
444 })
445 .collect();
446
447 for i in 0..extra_apks_count {
448 apkdmverity_arguments.push({
449 ApkDmverityArgument {
450 apk: extra_apks[i].to_str().unwrap(),
451 idsig: extra_idsigs[i].to_str().unwrap(),
452 name: &extra_apk_names[i],
453 saved_root_hash: if extra_root_hashes_trustful[i] {
454 Some(&extra_root_hashes_from_idsig[i])
455 } else {
456 None
457 },
458 }
459 });
Jiyong Parkbb4a9872021-09-06 15:59:21 +0900460 }
461
462 // Start apkdmverity and wait for the dm-verify block
Inseob Kim197748b2021-12-01 19:49:00 +0900463 let mut apkdmverity_child = run_apkdmverity(&apkdmverity_arguments)?;
Jooyung Han7a343f92021-09-08 22:53:11 +0900464
Jooyung Hanc8deb472021-09-13 13:48:25 +0900465 // While waiting for apkdmverity to mount APK, gathers public keys and root digests from
466 // APEX payload.
Jooyung Han7a343f92021-09-08 22:53:11 +0900467 let apex_data_from_payload = get_apex_data_from_payload(metadata)?;
Jooyung Han4a9b3bf2021-09-10 17:19:00 +0900468 if let Some(saved_data) = saved_data.map(|d| &d.apex_data) {
Jooyung Hanc8deb472021-09-13 13:48:25 +0900469 // We don't support APEX updates. (assuming that update will change root digest)
Jooyung Handd0a1732021-11-23 15:26:20 +0900470 ensure!(
471 saved_data == &apex_data_from_payload,
472 MicrodroidError::PayloadChanged(String::from("APEXes have changed."))
473 );
Jooyung Han4a9b3bf2021-09-10 17:19:00 +0900474 let apex_metadata = to_metadata(&apex_data_from_payload);
Jooyung Hanc8deb472021-09-13 13:48:25 +0900475 // Pass metadata(with public keys and root digests) to apexd so that it uses the passed
476 // metadata instead of the default one (/dev/block/by-name/payload-metadata)
Jooyung Han4a9b3bf2021-09-10 17:19:00 +0900477 OpenOptions::new()
478 .create_new(true)
479 .write(true)
480 .open("/apex/vm-payload-metadata")
481 .context("Failed to open /apex/vm-payload-metadata")
482 .and_then(|f| write_metadata(&apex_metadata, f))?;
483 }
484 // Start apexd to activate APEXes
485 system_properties::write("ctl.start", "apexd-vm")?;
Jooyung Han7a343f92021-09-08 22:53:11 +0900486
Inseob Kim217038e2021-11-25 11:15:06 +0900487 // TODO(inseob): add timeout
488 apkdmverity_child.wait()?;
Jooyung Han19c1d6c2021-08-06 14:08:16 +0900489
Jiyong Parkf7dea252021-09-08 01:42:54 +0900490 // Do the full verification if the root_hash is un-trustful. This requires the full scanning of
Jiyong Parkbb4a9872021-09-06 15:59:21 +0900491 // the APK file and therefore can be very slow if the APK is large. Note that this step is
Jiyong Parkf7dea252021-09-08 01:42:54 +0900492 // taken only when the root_hash is un-trustful which can be either when this is the first boot
Jiyong Parkbb4a9872021-09-06 15:59:21 +0900493 // of the VM or APK was updated in the host.
494 // TODO(jooyung): consider multithreading to make this faster
Inseob Kim197748b2021-12-01 19:49:00 +0900495 let main_apk_pubkey = get_public_key_from_apk(DM_MOUNTED_APK_PATH, root_hash_trustful)?;
496 let extra_apks_data = extra_root_hashes_from_idsig
497 .into_iter()
498 .enumerate()
499 .map(|(i, extra_root_hash)| {
500 let mount_path = format!("/dev/block/mapper/{}", &extra_apk_names[i]);
501 let apk_pubkey = get_public_key_from_apk(&mount_path, extra_root_hashes_trustful[i])?;
502 Ok(ApkData { root_hash: extra_root_hash, pubkey: apk_pubkey })
503 })
504 .collect::<Result<Vec<_>>>()?;
Jiyong Parkbb4a9872021-09-06 15:59:21 +0900505
506 info!("payload verification successful. took {:#?}", start_time.elapsed().unwrap());
507
Andrew Scull34916a72022-01-30 21:34:24 +0000508 // Use the salt from a verified instance, or generate a salt for a new instance.
509 let salt = if let Some(saved_data) = saved_data {
510 saved_data.salt.clone()
511 } else {
512 let mut salt = vec![0u8; 64];
513 salt.as_mut_slice().try_fill(&mut rand::thread_rng())?;
514 salt
515 };
516
Jiyong Parkf7dea252021-09-08 01:42:54 +0900517 // At this point, we can ensure that the root_hash from the idsig file is trusted, either by
518 // fully verifying the APK or by comparing it with the saved root_hash.
Jooyung Han7a343f92021-09-08 22:53:11 +0900519 Ok(MicrodroidData {
Andrew Scull34916a72022-01-30 21:34:24 +0000520 salt,
Inseob Kim197748b2021-12-01 19:49:00 +0900521 apk_data: ApkData { root_hash: root_hash_from_idsig, pubkey: main_apk_pubkey },
522 extra_apks_data,
Jooyung Han7a343f92021-09-08 22:53:11 +0900523 apex_data: apex_data_from_payload,
524 })
Jiyong Parkbb4a9872021-09-06 15:59:21 +0900525}
526
Inseob Kim197748b2021-12-01 19:49:00 +0900527fn mount_extra_apks(config: &VmPayloadConfig) -> Result<()> {
528 // For now, only the number of apks is important, as the mount point and dm-verity name is fixed
529 for i in 0..config.extra_apks.len() {
530 let mount_dir = format!("/mnt/extra-apk/{}", i);
531 create_dir(Path::new(&mount_dir)).context("Failed to create mount dir for extra apks")?;
532
533 // don't wait, just detach
Andrew Scullcc339a12022-07-04 12:44:19 +0000534 let noexec = true;
Inseob Kim197748b2021-12-01 19:49:00 +0900535 run_zipfuse(
Andrew Scullcc339a12022-07-04 12:44:19 +0000536 noexec,
Inseob Kim197748b2021-12-01 19:49:00 +0900537 "fscontext=u:object_r:zipfusefs:s0,context=u:object_r:extra_apk_file:s0",
538 Path::new(&format!("/dev/block/mapper/extra-apk-{}", i)),
539 Path::new(&mount_dir),
540 )
541 .context("Failed to zipfuse extra apks")?;
542 }
543
544 Ok(())
545}
546
Jiyong Parkbb4a9872021-09-06 15:59:21 +0900547// Waits until linker config is generated
548fn wait_for_apex_config_done() -> Result<()> {
549 let mut prop = PropertyWatcher::new(APEX_CONFIG_DONE_PROP)?;
550 loop {
551 prop.wait()?;
Andrew Scull4b5369f2022-01-28 13:47:20 +0000552 if system_properties::read_bool(APEX_CONFIG_DONE_PROP, false)? {
Jiyong Parkbb4a9872021-09-06 15:59:21 +0900553 break;
554 }
555 }
Jooyung Han19c1d6c2021-08-06 14:08:16 +0900556 Ok(())
557}
558
Inseob Kim197748b2021-12-01 19:49:00 +0900559fn get_apk_root_hash_from_idsig(path: &str) -> Result<Box<RootHash>> {
560 let mut idsig = File::open(path)?;
Jiyong Park21ce2c52021-08-28 02:32:17 +0900561 let idsig = V4Signature::from(&mut idsig)?;
562 Ok(idsig.hashing_info.raw_root_hash)
563}
564
Inseob Kim197748b2021-12-01 19:49:00 +0900565fn get_public_key_from_apk(apk: &str, root_hash_trustful: bool) -> Result<Box<[u8]>> {
566 if !root_hash_trustful {
567 verify(apk).context(MicrodroidError::PayloadVerificationFailed(format!(
568 "failed to verify {}",
569 apk
570 )))
571 } else {
572 get_public_key_der(apk)
573 }
574}
575
Jooyung Han634e2d72021-06-10 16:27:38 +0900576fn load_config(path: &Path) -> Result<VmPayloadConfig> {
577 info!("loading config from {:?}...", path);
578 let file = ioutil::wait_for_file(path, WAIT_TIMEOUT)?;
579 Ok(serde_json::from_reader(file)?)
580}
581
Jiyong Park8611a6c2021-07-09 18:17:44 +0900582/// Executes the given task. Stdout of the task is piped into the vsock stream to the
583/// virtualizationservice in the host side.
Jooyung Han5c6d4172021-12-06 14:17:52 +0900584fn exec_task(task: &Task, service: &Strong<dyn IVirtualMachineService>) -> Result<i32> {
Jiyong Park8611a6c2021-07-09 18:17:44 +0900585 info!("executing main task {:?}...", task);
Inseob Kim86ca0162021-10-20 02:21:02 +0000586 let mut command = build_command(task)?;
Inseob Kim7f61fe72021-08-20 20:50:47 +0900587
588 info!("notifying payload started");
Inseob Kimc7d28c72021-10-25 14:28:10 +0000589 service.notifyPayloadStarted()?;
Inseob Kim7f61fe72021-08-20 20:50:47 +0900590
Inseob Kim86ca0162021-10-20 02:21:02 +0000591 let exit_status = command.spawn()?.wait()?;
Jooyung Han5c6d4172021-12-06 14:17:52 +0900592 exit_status.code().ok_or_else(|| anyhow!("Failed to get exit_code from the paylaod."))
Jooyung Han347d9f22021-05-28 00:05:14 +0900593}
Jooyung Han634e2d72021-06-10 16:27:38 +0900594
595fn build_command(task: &Task) -> Result<Command> {
Inseob Kim7f61fe72021-08-20 20:50:47 +0900596 const VMADDR_CID_HOST: u32 = 2;
Inseob Kim7f61fe72021-08-20 20:50:47 +0900597
598 let mut command = match task.type_ {
Jooyung Han634e2d72021-06-10 16:27:38 +0900599 TaskType::Executable => {
600 let mut command = Command::new(&task.command);
601 command.args(&task.args);
602 command
603 }
604 TaskType::MicrodroidLauncher => {
605 let mut command = Command::new("/system/bin/microdroid_launcher");
606 command.arg(find_library_path(&task.command)?).args(&task.args);
607 command
608 }
Inseob Kim7f61fe72021-08-20 20:50:47 +0900609 };
610
Inseob Kimd0587562021-09-01 21:27:32 +0900611 match VsockStream::connect_with_cid_port(VMADDR_CID_HOST, VM_STREAM_SERVICE_PORT as u32) {
Inseob Kim7f61fe72021-08-20 20:50:47 +0900612 Ok(stream) => {
613 // SAFETY: the ownership of the underlying file descriptor is transferred from stream
614 // to the file object, and then into the Command object. When the command is finished,
615 // the file descriptor is closed.
616 let file = unsafe { File::from_raw_fd(stream.into_raw_fd()) };
617 command
618 .stdin(Stdio::from(file.try_clone()?))
619 .stdout(Stdio::from(file.try_clone()?))
620 .stderr(Stdio::from(file));
621 }
622 Err(e) => {
623 error!("failed to connect to virtualization service: {}", e);
624 // Don't fail hard here. Even if we failed to connect to the virtualizationservice,
625 // we keep executing the task. This can happen if the owner of the VM doesn't register
626 // callback to accept the stream. Use /dev/null as the stream so that the task can
627 // make progress without waiting for someone to consume the output.
628 command.stdin(Stdio::null()).stdout(Stdio::null()).stderr(Stdio::null());
629 }
630 }
631
632 Ok(command)
Jooyung Han634e2d72021-06-10 16:27:38 +0900633}
634
635fn find_library_path(name: &str) -> Result<String> {
636 let mut watcher = PropertyWatcher::new("ro.product.cpu.abilist")?;
637 let value = watcher.read(|_name, value| Ok(value.trim().to_string()))?;
638 let abi = value.split(',').next().ok_or_else(|| anyhow!("no abilist"))?;
639 let path = format!("/mnt/apk/lib/{}/{}", abi, name);
640
641 let metadata = fs::metadata(&path)?;
642 if !metadata.is_file() {
643 bail!("{} is not a file", &path);
644 }
645
646 Ok(path)
647}
Jiyong Park21ce2c52021-08-28 02:32:17 +0900648
649fn to_hex_string(buf: &[u8]) -> String {
650 buf.iter().map(|b| format!("{:02X}", b)).collect()
651}