blob: 350fbc59e41e2e21263bd4baefd4fdb8b72a5622 [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};
Inseob Kim1b95f2e2021-08-19 13:17:40 +090028use binder::unstable_api::{new_spibinder, AIBinder};
Andrew Scullb2f44472022-01-21 14:41:34 +000029use binder::{wait_for_interface, FromIBinder, Strong};
30use 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";
Jiyong Parkfa91d702021-10-18 23:51:39 +090072const LOGD_ENABLED_PROP: &str = "ro.boot.logd.enabled";
Andrew Scull65ddfc42022-02-14 21:03:58 +000073const APP_DEBUGGABLE_PROP: &str = "ro.boot.microdroid.app_debuggable";
Jiyong Parkbb4a9872021-09-06 15:59:21 +090074
Inseob Kim11f40d02022-06-13 17:16:00 +090075// SYNC WITH virtualizationservice/src/crosvm.rs
76const FAILURE_SERIAL_DEVICE: &str = "/dev/ttyS1";
77
Jooyung Handd0a1732021-11-23 15:26:20 +090078#[derive(thiserror::Error, Debug)]
79enum MicrodroidError {
Inseob Kim11f40d02022-06-13 17:16:00 +090080 #[error("Cannot connect to virtualization service: {0}")]
81 FailedToConnectToVirtualizationService(String),
Jooyung Handd0a1732021-11-23 15:26:20 +090082 #[error("Payload has changed: {0}")]
83 PayloadChanged(String),
84 #[error("Payload verification has failed: {0}")]
85 PayloadVerificationFailed(String),
Jooyung Han5c6d4172021-12-06 14:17:52 +090086 #[error("Payload config is invalid: {0}")]
87 InvalidConfig(String),
Jooyung Handd0a1732021-11-23 15:26:20 +090088}
89
90fn translate_error(err: &Error) -> (i32, String) {
91 if let Some(e) = err.downcast_ref::<MicrodroidError>() {
92 match e {
93 MicrodroidError::PayloadChanged(msg) => (ERROR_PAYLOAD_CHANGED, msg.to_string()),
94 MicrodroidError::PayloadVerificationFailed(msg) => {
95 (ERROR_PAYLOAD_VERIFICATION_FAILED, msg.to_string())
96 }
Jooyung Han5c6d4172021-12-06 14:17:52 +090097 MicrodroidError::InvalidConfig(msg) => (ERROR_PAYLOAD_INVALID_CONFIG, msg.to_string()),
Inseob Kim11f40d02022-06-13 17:16:00 +090098
99 // Connection failure won't be reported to VS; return the default value
100 MicrodroidError::FailedToConnectToVirtualizationService(msg) => {
101 (ERROR_UNKNOWN, msg.to_string())
102 }
Jooyung Handd0a1732021-11-23 15:26:20 +0900103 }
104 } else {
105 (ERROR_UNKNOWN, err.to_string())
106 }
107}
108
Inseob Kim11f40d02022-06-13 17:16:00 +0900109fn write_death_reason_to_serial(err: &Error) -> Result<()> {
110 let death_reason = if let Some(e) = err.downcast_ref::<MicrodroidError>() {
111 match e {
112 MicrodroidError::FailedToConnectToVirtualizationService(_) => {
113 "MICRODROID_FAILED_TO_CONNECT_TO_VIRTUALIZATION_SERVICE"
114 }
115 MicrodroidError::PayloadChanged(_) => "MICRODROID_PAYLOAD_HAS_CHANGED",
116 MicrodroidError::PayloadVerificationFailed(_) => {
117 "MICRODROID_PAYLOAD_VERIFICATION_FAILED"
118 }
119 MicrodroidError::InvalidConfig(_) => "MICRODROID_INVALID_PAYLOAD_CONFIG",
120 }
121 } else {
122 "MICRODROID_UNKNOWN_RUNTIME_ERROR"
123 };
124
125 let death_reason_bytes = death_reason.as_bytes();
126 let mut sent_total = 0;
127 while sent_total < death_reason_bytes.len() {
128 // TODO(b/220071963): Sometimes, sending more than 16 bytes at once makes MM hang.
129 let begin = sent_total;
130 let end = std::cmp::min(begin.saturating_add(16), death_reason_bytes.len());
131 OpenOptions::new()
132 .read(false)
133 .write(true)
134 .open(FAILURE_SERIAL_DEVICE)?
135 .write_all(&death_reason_bytes[begin..end])?;
136 sent_total = end;
137 }
138
139 Ok(())
140}
141
Inseob Kim1b95f2e2021-08-19 13:17:40 +0900142fn get_vms_rpc_binder() -> Result<Strong<dyn IVirtualMachineService>> {
143 // SAFETY: AIBinder returned by RpcClient has correct reference count, and the ownership can be
144 // safely taken by new_spibinder.
145 let ibinder = unsafe {
146 new_spibinder(binder_rpc_unstable_bindgen::RpcClient(
147 VMADDR_CID_HOST,
Inseob Kimd0587562021-09-01 21:27:32 +0900148 VM_BINDER_SERVICE_PORT as u32,
Inseob Kim1b95f2e2021-08-19 13:17:40 +0900149 ) as *mut AIBinder)
150 };
151 if let Some(ibinder) = ibinder {
152 <dyn IVirtualMachineService>::try_from(ibinder).context("Cannot connect to RPC service")
153 } else {
154 bail!("Invalid raw AIBinder")
155 }
156}
157
Inseob Kim437f1052022-06-21 11:30:22 +0900158fn main() -> Result<()> {
159 scopeguard::defer! {
160 info!("Shutting down...");
Jooyung Hanbfe086f2021-10-28 10:15:45 +0900161 if let Err(e) = system_properties::write("sys.powerctl", "shutdown") {
162 error!("failed to shutdown {:?}", e);
163 }
Jooyung Han311b1202021-09-14 22:00:16 +0900164 }
Inseob Kim437f1052022-06-21 11:30:22 +0900165
166 try_main().map_err(|e| {
167 error!("Failed with {:?}.", e);
168 if let Err(e) = write_death_reason_to_serial(&e) {
169 error!("Failed to write death reason {:?}", e);
170 }
171 e
172 })
Jooyung Han311b1202021-09-14 22:00:16 +0900173}
174
175fn try_main() -> Result<()> {
Andrew Sculle127cff2022-02-15 15:34:04 +0000176 let _ = kernlog::init();
Jooyung Han347d9f22021-05-28 00:05:14 +0900177 info!("started.");
178
Inseob Kim11f40d02022-06-13 17:16:00 +0900179 let service = get_vms_rpc_binder()
180 .context("cannot connect to VirtualMachineService")
181 .map_err(|e| MicrodroidError::FailedToConnectToVirtualizationService(e.to_string()))?;
Jooyung Han5c6d4172021-12-06 14:17:52 +0900182 match try_run_payload(&service) {
183 Ok(code) => {
184 info!("notifying payload finished");
185 service.notifyPayloadFinished(code)?;
186 if code == 0 {
187 info!("task successfully finished");
188 } else {
189 error!("task exited with exit code: {}", code);
190 }
191 Ok(())
192 }
193 Err(err) => {
194 error!("task terminated: {:?}", err);
195 let (error_code, message) = translate_error(&err);
196 service.notifyError(error_code, &message)?;
197 Err(err)
198 }
Jooyung Handd0a1732021-11-23 15:26:20 +0900199 }
200}
201
Inseob Kimb2519c52022-04-14 02:10:09 +0900202fn dice_derivation(verified_data: &MicrodroidData, payload_config_path: &str) -> Result<()> {
Andrew Scullb2f44472022-01-21 14:41:34 +0000203 // Calculate compound digests of code and authorities
Andrew Sculla0d1b1a2022-05-24 19:32:47 +0000204 let mut code_hash_ctx = Sha512::new();
205 let mut authority_hash_ctx = Sha512::new();
Andrew Scullb2f44472022-01-21 14:41:34 +0000206 code_hash_ctx.update(verified_data.apk_data.root_hash.as_ref());
207 authority_hash_ctx.update(verified_data.apk_data.pubkey.as_ref());
Inseob Kimb2519c52022-04-14 02:10:09 +0900208 for extra_apk in &verified_data.extra_apks_data {
Andrew Scullb2f44472022-01-21 14:41:34 +0000209 code_hash_ctx.update(extra_apk.root_hash.as_ref());
210 authority_hash_ctx.update(extra_apk.pubkey.as_ref());
211 }
Inseob Kimb2519c52022-04-14 02:10:09 +0900212 for apex in &verified_data.apex_data {
Andrew Scullb2f44472022-01-21 14:41:34 +0000213 code_hash_ctx.update(apex.root_digest.as_ref());
214 authority_hash_ctx.update(apex.public_key.as_ref());
215 }
Andrew Sculla0d1b1a2022-05-24 19:32:47 +0000216 let code_hash = code_hash_ctx.finish();
217 let authority_hash = authority_hash_ctx.finish();
Andrew Scullb2f44472022-01-21 14:41:34 +0000218
219 // {
220 // -70002: "Microdroid payload",
221 // -71000: payload_config_path
222 // }
223 let mut config_desc = vec![
224 0xa2, 0x3a, 0x00, 0x01, 0x11, 0x71, 0x72, 0x4d, 0x69, 0x63, 0x72, 0x6f, 0x64, 0x72, 0x6f,
225 0x69, 0x64, 0x20, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x3a, 0x00, 0x01, 0x15, 0x57,
226 ];
227 let config_path_bytes = payload_config_path.as_bytes();
228 encode_header(3, config_path_bytes.len().try_into().unwrap(), &mut config_desc)?;
229 config_desc.extend_from_slice(config_path_bytes);
230
Andrew Scull65ddfc42022-02-14 21:03:58 +0000231 // Check app debuggability, conervatively assuming it is debuggable
232 let app_debuggable = system_properties::read_bool(APP_DEBUGGABLE_PROP, true)?;
233
Andrew Scullb2f44472022-01-21 14:41:34 +0000234 // Send the details to diced
235 let diced =
236 wait_for_interface::<dyn IDiceMaintenance>("android.security.dice.IDiceMaintenance")
237 .context("IDiceMaintenance service not found")?;
238 diced
239 .demoteSelf(&[InputValues {
240 codeHash: code_hash,
241 config: Config { desc: config_desc },
242 authorityHash: authority_hash,
243 authorityDescriptor: None,
Andrew Scull65ddfc42022-02-14 21:03:58 +0000244 mode: if app_debuggable { Mode::DEBUG } else { Mode::NORMAL },
Inseob Kimb2519c52022-04-14 02:10:09 +0900245 hidden: verified_data.salt.clone().try_into().unwrap(),
Andrew Scullb2f44472022-01-21 14:41:34 +0000246 }])
247 .context("IDiceMaintenance::demoteSelf failed")?;
248 Ok(())
249}
250
Andrew Scullab72ec52022-03-14 09:10:52 +0000251fn is_strict_boot() -> bool {
252 Path::new(AVF_STRICT_BOOT).exists()
253}
254
255fn is_new_instance() -> bool {
256 Path::new(AVF_NEW_INSTANCE).exists()
257}
258
Jooyung Han5c6d4172021-12-06 14:17:52 +0900259fn try_run_payload(service: &Strong<dyn IVirtualMachineService>) -> Result<i32> {
Jooyung Han311b1202021-09-14 22:00:16 +0900260 let metadata = load_metadata().context("Failed to load payload metadata")?;
Jooyung Han19c1d6c2021-08-06 14:08:16 +0900261
Jooyung Han311b1202021-09-14 22:00:16 +0900262 let mut instance = InstanceDisk::new().context("Failed to load instance.img")?;
Jooyung Han7a343f92021-09-08 22:53:11 +0900263 let saved_data = instance.read_microdroid_data().context("Failed to read identity data")?;
Jiyong Parkbb4a9872021-09-06 15:59:21 +0900264
Andrew Scullab72ec52022-03-14 09:10:52 +0000265 if is_strict_boot() {
266 // Provisioning must happen on the first boot and never again.
267 if is_new_instance() {
268 ensure!(
269 saved_data.is_none(),
270 MicrodroidError::InvalidConfig("Found instance data on first boot.".to_string())
271 );
272 } else {
273 ensure!(
274 saved_data.is_some(),
275 MicrodroidError::InvalidConfig("Instance data not found.".to_string())
276 );
277 };
278 }
279
Jiyong Parkbb4a9872021-09-06 15:59:21 +0900280 // Verify the payload before using it.
Inseob Kim11f40d02022-06-13 17:16:00 +0900281 let verified_data = verify_payload(&metadata, saved_data.as_ref())
282 .context("Payload verification failed")
283 .map_err(|e| MicrodroidError::PayloadVerificationFailed(e.to_string()))?;
Jooyung Han7a343f92021-09-08 22:53:11 +0900284 if let Some(saved_data) = saved_data {
Jooyung Handd0a1732021-11-23 15:26:20 +0900285 ensure!(
286 saved_data == verified_data,
287 MicrodroidError::PayloadChanged(String::from(
288 "Detected an update of the payload which isn't supported yet."
289 ))
290 );
291 info!("Saved data is verified.");
Jiyong Parkbb4a9872021-09-06 15:59:21 +0900292 } else {
Jooyung Han7a343f92021-09-08 22:53:11 +0900293 info!("Saving verified data.");
294 instance.write_microdroid_data(&verified_data).context("Failed to write identity data")?;
Jooyung Han19c1d6c2021-08-06 14:08:16 +0900295 }
296
Inseob Kimb2519c52022-04-14 02:10:09 +0900297 // To minimize the exposure to untrusted data, derive dice profile as soon as possible.
298 info!("DICE derivation for payload");
299 dice_derivation(&verified_data, &metadata.payload_config_path)?;
300
Jooyung Hana6d11eb2021-09-10 11:48:05 +0900301 // Before reading a file from the APK, start zipfuse
Andrew Scullcc339a12022-07-04 12:44:19 +0000302 let noexec = false;
Inseob Kim217038e2021-11-25 11:15:06 +0900303 run_zipfuse(
Andrew Scullcc339a12022-07-04 12:44:19 +0000304 noexec,
Inseob Kim217038e2021-11-25 11:15:06 +0900305 "fscontext=u:object_r:zipfusefs:s0,context=u:object_r:system_file:s0",
306 Path::new("/dev/block/mapper/microdroid-apk"),
307 Path::new("/mnt/apk"),
308 )
309 .context("Failed to run zipfuse")?;
Jiyong Park21ce2c52021-08-28 02:32:17 +0900310
Jooyung Han5c6d4172021-12-06 14:17:52 +0900311 ensure!(
312 !metadata.payload_config_path.is_empty(),
313 MicrodroidError::InvalidConfig("No payload_config_path in metadata".to_string())
314 );
Inseob Kim197748b2021-12-01 19:49:00 +0900315
Jooyung Han5c6d4172021-12-06 14:17:52 +0900316 let config = load_config(Path::new(&metadata.payload_config_path))?;
Shikha Panwar6f03c942022-04-13 20:26:50 +0000317
318 // 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 }
323
Inseob Kim197748b2021-12-01 19:49:00 +0900324 if config.extra_apks.len() != verified_data.extra_apks_data.len() {
325 return Err(anyhow!(
326 "config expects {} extra apks, but found only {}",
327 config.extra_apks.len(),
328 verified_data.extra_apks_data.len()
329 ));
330 }
331 mount_extra_apks(&config)?;
Jooyung Han634e2d72021-06-10 16:27:38 +0900332
Jooyung Han5c6d4172021-12-06 14:17:52 +0900333 // Wait until apex config is done. (e.g. linker configuration for apexes)
334 // TODO(jooyung): wait until sys.boot_completed?
335 wait_for_apex_config_done()?;
336
337 ensure!(
338 config.task.is_some(),
339 MicrodroidError::InvalidConfig("No task in VM config".to_string())
340 );
Victor Hsieh8d006ba2022-05-02 09:42:02 -0700341 system_properties::write("dev.bootcomplete", "1").context("set dev.bootcomplete")?;
Jooyung Han5c6d4172021-12-06 14:17:52 +0900342 exec_task(&config.task.unwrap(), service)
Jooyung Han347d9f22021-05-28 00:05:14 +0900343}
344
Inseob Kim217038e2021-11-25 11:15:06 +0900345struct ApkDmverityArgument<'a> {
346 apk: &'a str,
347 idsig: &'a str,
348 name: &'a str,
Inseob Kim197748b2021-12-01 19:49:00 +0900349 saved_root_hash: Option<&'a RootHash>,
Inseob Kim217038e2021-11-25 11:15:06 +0900350}
351
352fn run_apkdmverity(args: &[ApkDmverityArgument]) -> Result<Child> {
353 let mut cmd = Command::new(APKDMVERITY_BIN);
354
355 cmd.stdin(Stdio::null()).stdout(Stdio::null()).stderr(Stdio::null());
356
357 for argument in args {
358 cmd.arg("--apk").arg(argument.apk).arg(argument.idsig).arg(argument.name);
Inseob Kim197748b2021-12-01 19:49:00 +0900359 if let Some(root_hash) = argument.saved_root_hash {
360 cmd.arg(&to_hex_string(root_hash));
361 } else {
362 cmd.arg("none");
363 }
Inseob Kim217038e2021-11-25 11:15:06 +0900364 }
365
366 cmd.spawn().context("Spawn apkdmverity")
367}
368
Andrew Scullcc339a12022-07-04 12:44:19 +0000369fn run_zipfuse(noexec: bool, option: &str, zip_path: &Path, mount_dir: &Path) -> Result<Child> {
370 let mut cmd = Command::new(ZIPFUSE_BIN);
371 if noexec {
372 cmd.arg("--noexec");
373 }
374 cmd.arg("-o")
Inseob Kim217038e2021-11-25 11:15:06 +0900375 .arg(option)
376 .arg(zip_path)
377 .arg(mount_dir)
378 .stdin(Stdio::null())
379 .stdout(Stdio::null())
380 .stderr(Stdio::null())
381 .spawn()
382 .context("Spawn zipfuse")
383}
384
Jooyung Han7a343f92021-09-08 22:53:11 +0900385// Verify payload before executing it. For APK payload, Full verification (which is slow) is done
386// when the root_hash values from the idsig file and the instance disk are different. This function
387// returns the verified root hash (for APK payload) and pubkeys (for APEX payloads) that can be
388// saved to the instance disk.
389fn verify_payload(
390 metadata: &Metadata,
391 saved_data: Option<&MicrodroidData>,
392) -> Result<MicrodroidData> {
Jiyong Parkbb4a9872021-09-06 15:59:21 +0900393 let start_time = SystemTime::now();
394
Inseob Kim197748b2021-12-01 19:49:00 +0900395 // Verify main APK
Jooyung Han7a343f92021-09-08 22:53:11 +0900396 let root_hash = saved_data.map(|d| &d.apk_data.root_hash);
Inseob Kim197748b2021-12-01 19:49:00 +0900397 let root_hash_from_idsig = get_apk_root_hash_from_idsig(MAIN_APK_IDSIG_PATH)?;
Jiyong Parkf7dea252021-09-08 01:42:54 +0900398 let root_hash_trustful = root_hash == Some(&root_hash_from_idsig);
Jiyong Parkbb4a9872021-09-06 15:59:21 +0900399
Jiyong Parkf7dea252021-09-08 01:42:54 +0900400 // 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 +0900401 // instead of the value read from the idsig file.
Inseob Kim197748b2021-12-01 19:49:00 +0900402 let main_apk_argument = {
403 ApkDmverityArgument {
404 apk: MAIN_APK_PATH,
405 idsig: MAIN_APK_IDSIG_PATH,
406 name: MAIN_APK_DEVICE_NAME,
407 saved_root_hash: if root_hash_trustful {
408 Some(root_hash_from_idsig.as_ref())
409 } else {
410 None
411 },
412 }
413 };
414 let mut apkdmverity_arguments = vec![main_apk_argument];
415
416 // Verify extra APKs
417 // For now, we can't read the payload config, so glob APKs and idsigs.
418 // Later, we'll see if it matches with the payload config.
419
420 // sort globbed paths to match apks (extra-apk-{idx}) and idsigs (extra-idsig-{idx})
421 // e.g. "extra-apk-0" corresponds to "extra-idsig-0"
422 let extra_apks =
423 sorted(glob(EXTRA_APK_PATH_PATTERN)?.collect::<Result<Vec<_>, _>>()?).collect::<Vec<_>>();
424 let extra_idsigs =
425 sorted(glob(EXTRA_IDSIG_PATH_PATTERN)?.collect::<Result<Vec<_>, _>>()?).collect::<Vec<_>>();
426 if extra_apks.len() != extra_idsigs.len() {
427 return Err(anyhow!(
428 "Extra apks/idsigs mismatch: {} apks but {} idsigs",
429 extra_apks.len(),
430 extra_idsigs.len()
431 ));
432 }
433 let extra_apks_count = extra_apks.len();
434
435 let (extra_apk_names, extra_root_hashes_from_idsig): (Vec<_>, Vec<_>) = extra_idsigs
436 .iter()
437 .enumerate()
438 .map(|(i, extra_idsig)| {
439 (
440 format!("extra-apk-{}", i),
441 get_apk_root_hash_from_idsig(extra_idsig.to_str().unwrap())
442 .expect("Can't find root hash from extra idsig"),
443 )
444 })
445 .unzip();
446
447 let saved_extra_root_hashes: Vec<_> = saved_data
448 .map(|d| d.extra_apks_data.iter().map(|apk_data| &apk_data.root_hash).collect())
449 .unwrap_or_else(Vec::new);
450 let extra_root_hashes_trustful: Vec<_> = extra_root_hashes_from_idsig
451 .iter()
452 .enumerate()
453 .map(|(i, root_hash_from_idsig)| {
454 saved_extra_root_hashes.get(i).copied() == Some(root_hash_from_idsig)
455 })
456 .collect();
457
458 for i in 0..extra_apks_count {
459 apkdmverity_arguments.push({
460 ApkDmverityArgument {
461 apk: extra_apks[i].to_str().unwrap(),
462 idsig: extra_idsigs[i].to_str().unwrap(),
463 name: &extra_apk_names[i],
464 saved_root_hash: if extra_root_hashes_trustful[i] {
465 Some(&extra_root_hashes_from_idsig[i])
466 } else {
467 None
468 },
469 }
470 });
Jiyong Parkbb4a9872021-09-06 15:59:21 +0900471 }
472
473 // Start apkdmverity and wait for the dm-verify block
Inseob Kim197748b2021-12-01 19:49:00 +0900474 let mut apkdmverity_child = run_apkdmverity(&apkdmverity_arguments)?;
Jooyung Han7a343f92021-09-08 22:53:11 +0900475
Jooyung Hanc8deb472021-09-13 13:48:25 +0900476 // While waiting for apkdmverity to mount APK, gathers public keys and root digests from
477 // APEX payload.
Jooyung Han7a343f92021-09-08 22:53:11 +0900478 let apex_data_from_payload = get_apex_data_from_payload(metadata)?;
Jooyung Han4a9b3bf2021-09-10 17:19:00 +0900479 if let Some(saved_data) = saved_data.map(|d| &d.apex_data) {
Jooyung Hanc8deb472021-09-13 13:48:25 +0900480 // We don't support APEX updates. (assuming that update will change root digest)
Jooyung Handd0a1732021-11-23 15:26:20 +0900481 ensure!(
482 saved_data == &apex_data_from_payload,
483 MicrodroidError::PayloadChanged(String::from("APEXes have changed."))
484 );
Jooyung Han4a9b3bf2021-09-10 17:19:00 +0900485 let apex_metadata = to_metadata(&apex_data_from_payload);
Jooyung Hanc8deb472021-09-13 13:48:25 +0900486 // Pass metadata(with public keys and root digests) to apexd so that it uses the passed
487 // metadata instead of the default one (/dev/block/by-name/payload-metadata)
Jooyung Han4a9b3bf2021-09-10 17:19:00 +0900488 OpenOptions::new()
489 .create_new(true)
490 .write(true)
491 .open("/apex/vm-payload-metadata")
492 .context("Failed to open /apex/vm-payload-metadata")
493 .and_then(|f| write_metadata(&apex_metadata, f))?;
494 }
495 // Start apexd to activate APEXes
496 system_properties::write("ctl.start", "apexd-vm")?;
Jooyung Han7a343f92021-09-08 22:53:11 +0900497
Inseob Kim217038e2021-11-25 11:15:06 +0900498 // TODO(inseob): add timeout
499 apkdmverity_child.wait()?;
Jooyung Han19c1d6c2021-08-06 14:08:16 +0900500
Jiyong Parkf7dea252021-09-08 01:42:54 +0900501 // Do the full verification if the root_hash is un-trustful. This requires the full scanning of
Jiyong Parkbb4a9872021-09-06 15:59:21 +0900502 // 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 +0900503 // 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 +0900504 // of the VM or APK was updated in the host.
505 // TODO(jooyung): consider multithreading to make this faster
Inseob Kim197748b2021-12-01 19:49:00 +0900506 let main_apk_pubkey = get_public_key_from_apk(DM_MOUNTED_APK_PATH, root_hash_trustful)?;
507 let extra_apks_data = extra_root_hashes_from_idsig
508 .into_iter()
509 .enumerate()
510 .map(|(i, extra_root_hash)| {
511 let mount_path = format!("/dev/block/mapper/{}", &extra_apk_names[i]);
512 let apk_pubkey = get_public_key_from_apk(&mount_path, extra_root_hashes_trustful[i])?;
513 Ok(ApkData { root_hash: extra_root_hash, pubkey: apk_pubkey })
514 })
515 .collect::<Result<Vec<_>>>()?;
Jiyong Parkbb4a9872021-09-06 15:59:21 +0900516
517 info!("payload verification successful. took {:#?}", start_time.elapsed().unwrap());
518
Andrew Scull34916a72022-01-30 21:34:24 +0000519 // Use the salt from a verified instance, or generate a salt for a new instance.
520 let salt = if let Some(saved_data) = saved_data {
521 saved_data.salt.clone()
522 } else {
523 let mut salt = vec![0u8; 64];
524 salt.as_mut_slice().try_fill(&mut rand::thread_rng())?;
525 salt
526 };
527
Jiyong Parkf7dea252021-09-08 01:42:54 +0900528 // At this point, we can ensure that the root_hash from the idsig file is trusted, either by
529 // fully verifying the APK or by comparing it with the saved root_hash.
Jooyung Han7a343f92021-09-08 22:53:11 +0900530 Ok(MicrodroidData {
Andrew Scull34916a72022-01-30 21:34:24 +0000531 salt,
Inseob Kim197748b2021-12-01 19:49:00 +0900532 apk_data: ApkData { root_hash: root_hash_from_idsig, pubkey: main_apk_pubkey },
533 extra_apks_data,
Jooyung Han7a343f92021-09-08 22:53:11 +0900534 apex_data: apex_data_from_payload,
535 })
Jiyong Parkbb4a9872021-09-06 15:59:21 +0900536}
537
Inseob Kim197748b2021-12-01 19:49:00 +0900538fn mount_extra_apks(config: &VmPayloadConfig) -> Result<()> {
539 // For now, only the number of apks is important, as the mount point and dm-verity name is fixed
540 for i in 0..config.extra_apks.len() {
541 let mount_dir = format!("/mnt/extra-apk/{}", i);
542 create_dir(Path::new(&mount_dir)).context("Failed to create mount dir for extra apks")?;
543
544 // don't wait, just detach
Andrew Scullcc339a12022-07-04 12:44:19 +0000545 let noexec = true;
Inseob Kim197748b2021-12-01 19:49:00 +0900546 run_zipfuse(
Andrew Scullcc339a12022-07-04 12:44:19 +0000547 noexec,
Inseob Kim197748b2021-12-01 19:49:00 +0900548 "fscontext=u:object_r:zipfusefs:s0,context=u:object_r:extra_apk_file:s0",
549 Path::new(&format!("/dev/block/mapper/extra-apk-{}", i)),
550 Path::new(&mount_dir),
551 )
552 .context("Failed to zipfuse extra apks")?;
553 }
554
555 Ok(())
556}
557
Jiyong Parkbb4a9872021-09-06 15:59:21 +0900558// Waits until linker config is generated
559fn wait_for_apex_config_done() -> Result<()> {
560 let mut prop = PropertyWatcher::new(APEX_CONFIG_DONE_PROP)?;
561 loop {
562 prop.wait()?;
Andrew Scull4b5369f2022-01-28 13:47:20 +0000563 if system_properties::read_bool(APEX_CONFIG_DONE_PROP, false)? {
Jiyong Parkbb4a9872021-09-06 15:59:21 +0900564 break;
565 }
566 }
Jooyung Han19c1d6c2021-08-06 14:08:16 +0900567 Ok(())
568}
569
Inseob Kim197748b2021-12-01 19:49:00 +0900570fn get_apk_root_hash_from_idsig(path: &str) -> Result<Box<RootHash>> {
571 let mut idsig = File::open(path)?;
Jiyong Park21ce2c52021-08-28 02:32:17 +0900572 let idsig = V4Signature::from(&mut idsig)?;
573 Ok(idsig.hashing_info.raw_root_hash)
574}
575
Inseob Kim197748b2021-12-01 19:49:00 +0900576fn get_public_key_from_apk(apk: &str, root_hash_trustful: bool) -> Result<Box<[u8]>> {
577 if !root_hash_trustful {
578 verify(apk).context(MicrodroidError::PayloadVerificationFailed(format!(
579 "failed to verify {}",
580 apk
581 )))
582 } else {
583 get_public_key_der(apk)
584 }
585}
586
Jooyung Han634e2d72021-06-10 16:27:38 +0900587fn load_config(path: &Path) -> Result<VmPayloadConfig> {
588 info!("loading config from {:?}...", path);
589 let file = ioutil::wait_for_file(path, WAIT_TIMEOUT)?;
590 Ok(serde_json::from_reader(file)?)
591}
592
Jiyong Park8611a6c2021-07-09 18:17:44 +0900593/// Executes the given task. Stdout of the task is piped into the vsock stream to the
594/// virtualizationservice in the host side.
Jooyung Han5c6d4172021-12-06 14:17:52 +0900595fn exec_task(task: &Task, service: &Strong<dyn IVirtualMachineService>) -> Result<i32> {
Jiyong Park8611a6c2021-07-09 18:17:44 +0900596 info!("executing main task {:?}...", task);
Inseob Kim86ca0162021-10-20 02:21:02 +0000597 let mut command = build_command(task)?;
Inseob Kim7f61fe72021-08-20 20:50:47 +0900598
599 info!("notifying payload started");
Inseob Kimc7d28c72021-10-25 14:28:10 +0000600 service.notifyPayloadStarted()?;
Inseob Kim7f61fe72021-08-20 20:50:47 +0900601
Jiyong Parkfa91d702021-10-18 23:51:39 +0900602 // Start logging if enabled
603 // TODO(b/200914564) set filterspec if debug_level is app_only
Andrew Scull4b5369f2022-01-28 13:47:20 +0000604 if system_properties::read_bool(LOGD_ENABLED_PROP, false)? {
Jiyong Parkfa91d702021-10-18 23:51:39 +0900605 system_properties::write("ctl.start", "seriallogging")?;
606 }
607
Inseob Kim86ca0162021-10-20 02:21:02 +0000608 let exit_status = command.spawn()?.wait()?;
Jooyung Han5c6d4172021-12-06 14:17:52 +0900609 exit_status.code().ok_or_else(|| anyhow!("Failed to get exit_code from the paylaod."))
Jooyung Han347d9f22021-05-28 00:05:14 +0900610}
Jooyung Han634e2d72021-06-10 16:27:38 +0900611
612fn build_command(task: &Task) -> Result<Command> {
Inseob Kim7f61fe72021-08-20 20:50:47 +0900613 const VMADDR_CID_HOST: u32 = 2;
Inseob Kim7f61fe72021-08-20 20:50:47 +0900614
615 let mut command = match task.type_ {
Jooyung Han634e2d72021-06-10 16:27:38 +0900616 TaskType::Executable => {
617 let mut command = Command::new(&task.command);
618 command.args(&task.args);
619 command
620 }
621 TaskType::MicrodroidLauncher => {
622 let mut command = Command::new("/system/bin/microdroid_launcher");
623 command.arg(find_library_path(&task.command)?).args(&task.args);
624 command
625 }
Inseob Kim7f61fe72021-08-20 20:50:47 +0900626 };
627
Inseob Kimd0587562021-09-01 21:27:32 +0900628 match VsockStream::connect_with_cid_port(VMADDR_CID_HOST, VM_STREAM_SERVICE_PORT as u32) {
Inseob Kim7f61fe72021-08-20 20:50:47 +0900629 Ok(stream) => {
630 // SAFETY: the ownership of the underlying file descriptor is transferred from stream
631 // to the file object, and then into the Command object. When the command is finished,
632 // the file descriptor is closed.
633 let file = unsafe { File::from_raw_fd(stream.into_raw_fd()) };
634 command
635 .stdin(Stdio::from(file.try_clone()?))
636 .stdout(Stdio::from(file.try_clone()?))
637 .stderr(Stdio::from(file));
638 }
639 Err(e) => {
640 error!("failed to connect to virtualization service: {}", e);
641 // Don't fail hard here. Even if we failed to connect to the virtualizationservice,
642 // we keep executing the task. This can happen if the owner of the VM doesn't register
643 // callback to accept the stream. Use /dev/null as the stream so that the task can
644 // make progress without waiting for someone to consume the output.
645 command.stdin(Stdio::null()).stdout(Stdio::null()).stderr(Stdio::null());
646 }
647 }
648
649 Ok(command)
Jooyung Han634e2d72021-06-10 16:27:38 +0900650}
651
652fn find_library_path(name: &str) -> Result<String> {
653 let mut watcher = PropertyWatcher::new("ro.product.cpu.abilist")?;
654 let value = watcher.read(|_name, value| Ok(value.trim().to_string()))?;
655 let abi = value.split(',').next().ok_or_else(|| anyhow!("no abilist"))?;
656 let path = format!("/mnt/apk/lib/{}/{}", abi, name);
657
658 let metadata = fs::metadata(&path)?;
659 if !metadata.is_file() {
660 bail!("{} is not a file", &path);
661 }
662
663 Ok(path)
664}
Jiyong Park21ce2c52021-08-28 02:32:17 +0900665
666fn to_hex_string(buf: &[u8]) -> String {
667 buf.iter().map(|b| format!("{:02X}", b)).collect()
668}