blob: 998b94b4ae9b3475d9971fdcde7640bab0e6152b [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
Jooyung Han311b1202021-09-14 22:00:16 +0900158fn main() {
159 if let Err(e) = try_main() {
Jooyung Hanbfe086f2021-10-28 10:15:45 +0900160 error!("Failed with {:?}. Shutting down...", e);
Inseob Kim11f40d02022-06-13 17:16:00 +0900161 if let Err(e) = write_death_reason_to_serial(&e) {
162 error!("Failed to write death reason {:?}", e);
163 }
Jooyung Hanbfe086f2021-10-28 10:15:45 +0900164 if let Err(e) = system_properties::write("sys.powerctl", "shutdown") {
165 error!("failed to shutdown {:?}", e);
166 }
Jooyung Han311b1202021-09-14 22:00:16 +0900167 std::process::exit(1);
168 }
169}
170
171fn try_main() -> Result<()> {
Andrew Sculle127cff2022-02-15 15:34:04 +0000172 let _ = kernlog::init();
Jooyung Han347d9f22021-05-28 00:05:14 +0900173 info!("started.");
174
Inseob Kim11f40d02022-06-13 17:16:00 +0900175 let service = get_vms_rpc_binder()
176 .context("cannot connect to VirtualMachineService")
177 .map_err(|e| MicrodroidError::FailedToConnectToVirtualizationService(e.to_string()))?;
Jooyung Han5c6d4172021-12-06 14:17:52 +0900178 match try_run_payload(&service) {
179 Ok(code) => {
180 info!("notifying payload finished");
181 service.notifyPayloadFinished(code)?;
182 if code == 0 {
183 info!("task successfully finished");
184 } else {
185 error!("task exited with exit code: {}", code);
186 }
187 Ok(())
188 }
189 Err(err) => {
190 error!("task terminated: {:?}", err);
191 let (error_code, message) = translate_error(&err);
192 service.notifyError(error_code, &message)?;
193 Err(err)
194 }
Jooyung Handd0a1732021-11-23 15:26:20 +0900195 }
196}
197
Inseob Kimb2519c52022-04-14 02:10:09 +0900198fn dice_derivation(verified_data: &MicrodroidData, payload_config_path: &str) -> Result<()> {
Andrew Scullb2f44472022-01-21 14:41:34 +0000199 // Calculate compound digests of code and authorities
Andrew Sculla0d1b1a2022-05-24 19:32:47 +0000200 let mut code_hash_ctx = Sha512::new();
201 let mut authority_hash_ctx = Sha512::new();
Andrew Scullb2f44472022-01-21 14:41:34 +0000202 code_hash_ctx.update(verified_data.apk_data.root_hash.as_ref());
203 authority_hash_ctx.update(verified_data.apk_data.pubkey.as_ref());
Inseob Kimb2519c52022-04-14 02:10:09 +0900204 for extra_apk in &verified_data.extra_apks_data {
Andrew Scullb2f44472022-01-21 14:41:34 +0000205 code_hash_ctx.update(extra_apk.root_hash.as_ref());
206 authority_hash_ctx.update(extra_apk.pubkey.as_ref());
207 }
Inseob Kimb2519c52022-04-14 02:10:09 +0900208 for apex in &verified_data.apex_data {
Andrew Scullb2f44472022-01-21 14:41:34 +0000209 code_hash_ctx.update(apex.root_digest.as_ref());
210 authority_hash_ctx.update(apex.public_key.as_ref());
211 }
Andrew Sculla0d1b1a2022-05-24 19:32:47 +0000212 let code_hash = code_hash_ctx.finish();
213 let authority_hash = authority_hash_ctx.finish();
Andrew Scullb2f44472022-01-21 14:41:34 +0000214
215 // {
216 // -70002: "Microdroid payload",
217 // -71000: payload_config_path
218 // }
219 let mut config_desc = vec![
220 0xa2, 0x3a, 0x00, 0x01, 0x11, 0x71, 0x72, 0x4d, 0x69, 0x63, 0x72, 0x6f, 0x64, 0x72, 0x6f,
221 0x69, 0x64, 0x20, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x3a, 0x00, 0x01, 0x15, 0x57,
222 ];
223 let config_path_bytes = payload_config_path.as_bytes();
224 encode_header(3, config_path_bytes.len().try_into().unwrap(), &mut config_desc)?;
225 config_desc.extend_from_slice(config_path_bytes);
226
Andrew Scull65ddfc42022-02-14 21:03:58 +0000227 // Check app debuggability, conervatively assuming it is debuggable
228 let app_debuggable = system_properties::read_bool(APP_DEBUGGABLE_PROP, true)?;
229
Andrew Scullb2f44472022-01-21 14:41:34 +0000230 // Send the details to diced
231 let diced =
232 wait_for_interface::<dyn IDiceMaintenance>("android.security.dice.IDiceMaintenance")
233 .context("IDiceMaintenance service not found")?;
234 diced
235 .demoteSelf(&[InputValues {
236 codeHash: code_hash,
237 config: Config { desc: config_desc },
238 authorityHash: authority_hash,
239 authorityDescriptor: None,
Andrew Scull65ddfc42022-02-14 21:03:58 +0000240 mode: if app_debuggable { Mode::DEBUG } else { Mode::NORMAL },
Inseob Kimb2519c52022-04-14 02:10:09 +0900241 hidden: verified_data.salt.clone().try_into().unwrap(),
Andrew Scullb2f44472022-01-21 14:41:34 +0000242 }])
243 .context("IDiceMaintenance::demoteSelf failed")?;
244 Ok(())
245}
246
Andrew Scullab72ec52022-03-14 09:10:52 +0000247fn is_strict_boot() -> bool {
248 Path::new(AVF_STRICT_BOOT).exists()
249}
250
251fn is_new_instance() -> bool {
252 Path::new(AVF_NEW_INSTANCE).exists()
253}
254
Jooyung Han5c6d4172021-12-06 14:17:52 +0900255fn try_run_payload(service: &Strong<dyn IVirtualMachineService>) -> Result<i32> {
Jooyung Han311b1202021-09-14 22:00:16 +0900256 let metadata = load_metadata().context("Failed to load payload metadata")?;
Jooyung Han19c1d6c2021-08-06 14:08:16 +0900257
Jooyung Han311b1202021-09-14 22:00:16 +0900258 let mut instance = InstanceDisk::new().context("Failed to load instance.img")?;
Jooyung Han7a343f92021-09-08 22:53:11 +0900259 let saved_data = instance.read_microdroid_data().context("Failed to read identity data")?;
Jiyong Parkbb4a9872021-09-06 15:59:21 +0900260
Andrew Scullab72ec52022-03-14 09:10:52 +0000261 if is_strict_boot() {
262 // Provisioning must happen on the first boot and never again.
263 if is_new_instance() {
264 ensure!(
265 saved_data.is_none(),
266 MicrodroidError::InvalidConfig("Found instance data on first boot.".to_string())
267 );
268 } else {
269 ensure!(
270 saved_data.is_some(),
271 MicrodroidError::InvalidConfig("Instance data not found.".to_string())
272 );
273 };
274 }
275
Jiyong Parkbb4a9872021-09-06 15:59:21 +0900276 // Verify the payload before using it.
Inseob Kim11f40d02022-06-13 17:16:00 +0900277 let verified_data = verify_payload(&metadata, saved_data.as_ref())
278 .context("Payload verification failed")
279 .map_err(|e| MicrodroidError::PayloadVerificationFailed(e.to_string()))?;
Jooyung Han7a343f92021-09-08 22:53:11 +0900280 if let Some(saved_data) = saved_data {
Jooyung Handd0a1732021-11-23 15:26:20 +0900281 ensure!(
282 saved_data == verified_data,
283 MicrodroidError::PayloadChanged(String::from(
284 "Detected an update of the payload which isn't supported yet."
285 ))
286 );
287 info!("Saved data is verified.");
Jiyong Parkbb4a9872021-09-06 15:59:21 +0900288 } else {
Jooyung Han7a343f92021-09-08 22:53:11 +0900289 info!("Saving verified data.");
290 instance.write_microdroid_data(&verified_data).context("Failed to write identity data")?;
Jooyung Han19c1d6c2021-08-06 14:08:16 +0900291 }
292
Inseob Kimb2519c52022-04-14 02:10:09 +0900293 // To minimize the exposure to untrusted data, derive dice profile as soon as possible.
294 info!("DICE derivation for payload");
295 dice_derivation(&verified_data, &metadata.payload_config_path)?;
296
Jooyung Hana6d11eb2021-09-10 11:48:05 +0900297 // Before reading a file from the APK, start zipfuse
Inseob Kim217038e2021-11-25 11:15:06 +0900298 run_zipfuse(
299 "fscontext=u:object_r:zipfusefs:s0,context=u:object_r:system_file:s0",
300 Path::new("/dev/block/mapper/microdroid-apk"),
301 Path::new("/mnt/apk"),
302 )
303 .context("Failed to run zipfuse")?;
Jiyong Park21ce2c52021-08-28 02:32:17 +0900304
Jooyung Han5c6d4172021-12-06 14:17:52 +0900305 ensure!(
306 !metadata.payload_config_path.is_empty(),
307 MicrodroidError::InvalidConfig("No payload_config_path in metadata".to_string())
308 );
Inseob Kim197748b2021-12-01 19:49:00 +0900309
Jooyung Han5c6d4172021-12-06 14:17:52 +0900310 let config = load_config(Path::new(&metadata.payload_config_path))?;
Shikha Panwar6f03c942022-04-13 20:26:50 +0000311
312 // Start tombstone_transmit if enabled
313 if config.export_tombstones {
314 system_properties::write("ctl.start", "tombstone_transmit")
315 .context("Failed to start tombstone_transmit")?;
316 }
317
Inseob Kim197748b2021-12-01 19:49:00 +0900318 if config.extra_apks.len() != verified_data.extra_apks_data.len() {
319 return Err(anyhow!(
320 "config expects {} extra apks, but found only {}",
321 config.extra_apks.len(),
322 verified_data.extra_apks_data.len()
323 ));
324 }
325 mount_extra_apks(&config)?;
Jooyung Han634e2d72021-06-10 16:27:38 +0900326
Jooyung Han5c6d4172021-12-06 14:17:52 +0900327 // Wait until apex config is done. (e.g. linker configuration for apexes)
328 // TODO(jooyung): wait until sys.boot_completed?
329 wait_for_apex_config_done()?;
330
331 ensure!(
332 config.task.is_some(),
333 MicrodroidError::InvalidConfig("No task in VM config".to_string())
334 );
Victor Hsieh8d006ba2022-05-02 09:42:02 -0700335 system_properties::write("dev.bootcomplete", "1").context("set dev.bootcomplete")?;
Jooyung Han5c6d4172021-12-06 14:17:52 +0900336 exec_task(&config.task.unwrap(), service)
Jooyung Han347d9f22021-05-28 00:05:14 +0900337}
338
Inseob Kim217038e2021-11-25 11:15:06 +0900339struct ApkDmverityArgument<'a> {
340 apk: &'a str,
341 idsig: &'a str,
342 name: &'a str,
Inseob Kim197748b2021-12-01 19:49:00 +0900343 saved_root_hash: Option<&'a RootHash>,
Inseob Kim217038e2021-11-25 11:15:06 +0900344}
345
346fn run_apkdmverity(args: &[ApkDmverityArgument]) -> Result<Child> {
347 let mut cmd = Command::new(APKDMVERITY_BIN);
348
349 cmd.stdin(Stdio::null()).stdout(Stdio::null()).stderr(Stdio::null());
350
351 for argument in args {
352 cmd.arg("--apk").arg(argument.apk).arg(argument.idsig).arg(argument.name);
Inseob Kim197748b2021-12-01 19:49:00 +0900353 if let Some(root_hash) = argument.saved_root_hash {
354 cmd.arg(&to_hex_string(root_hash));
355 } else {
356 cmd.arg("none");
357 }
Inseob Kim217038e2021-11-25 11:15:06 +0900358 }
359
360 cmd.spawn().context("Spawn apkdmverity")
361}
362
363fn run_zipfuse(option: &str, zip_path: &Path, mount_dir: &Path) -> Result<Child> {
364 Command::new(ZIPFUSE_BIN)
365 .arg("-o")
366 .arg(option)
367 .arg(zip_path)
368 .arg(mount_dir)
369 .stdin(Stdio::null())
370 .stdout(Stdio::null())
371 .stderr(Stdio::null())
372 .spawn()
373 .context("Spawn zipfuse")
374}
375
Jooyung Han7a343f92021-09-08 22:53:11 +0900376// Verify payload before executing it. For APK payload, Full verification (which is slow) is done
377// when the root_hash values from the idsig file and the instance disk are different. This function
378// returns the verified root hash (for APK payload) and pubkeys (for APEX payloads) that can be
379// saved to the instance disk.
380fn verify_payload(
381 metadata: &Metadata,
382 saved_data: Option<&MicrodroidData>,
383) -> Result<MicrodroidData> {
Jiyong Parkbb4a9872021-09-06 15:59:21 +0900384 let start_time = SystemTime::now();
385
Inseob Kim197748b2021-12-01 19:49:00 +0900386 // Verify main APK
Jooyung Han7a343f92021-09-08 22:53:11 +0900387 let root_hash = saved_data.map(|d| &d.apk_data.root_hash);
Inseob Kim197748b2021-12-01 19:49:00 +0900388 let root_hash_from_idsig = get_apk_root_hash_from_idsig(MAIN_APK_IDSIG_PATH)?;
Jiyong Parkf7dea252021-09-08 01:42:54 +0900389 let root_hash_trustful = root_hash == Some(&root_hash_from_idsig);
Jiyong Parkbb4a9872021-09-06 15:59:21 +0900390
Jiyong Parkf7dea252021-09-08 01:42:54 +0900391 // 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 +0900392 // instead of the value read from the idsig file.
Inseob Kim197748b2021-12-01 19:49:00 +0900393 let main_apk_argument = {
394 ApkDmverityArgument {
395 apk: MAIN_APK_PATH,
396 idsig: MAIN_APK_IDSIG_PATH,
397 name: MAIN_APK_DEVICE_NAME,
398 saved_root_hash: if root_hash_trustful {
399 Some(root_hash_from_idsig.as_ref())
400 } else {
401 None
402 },
403 }
404 };
405 let mut apkdmverity_arguments = vec![main_apk_argument];
406
407 // Verify extra APKs
408 // For now, we can't read the payload config, so glob APKs and idsigs.
409 // Later, we'll see if it matches with the payload config.
410
411 // sort globbed paths to match apks (extra-apk-{idx}) and idsigs (extra-idsig-{idx})
412 // e.g. "extra-apk-0" corresponds to "extra-idsig-0"
413 let extra_apks =
414 sorted(glob(EXTRA_APK_PATH_PATTERN)?.collect::<Result<Vec<_>, _>>()?).collect::<Vec<_>>();
415 let extra_idsigs =
416 sorted(glob(EXTRA_IDSIG_PATH_PATTERN)?.collect::<Result<Vec<_>, _>>()?).collect::<Vec<_>>();
417 if extra_apks.len() != extra_idsigs.len() {
418 return Err(anyhow!(
419 "Extra apks/idsigs mismatch: {} apks but {} idsigs",
420 extra_apks.len(),
421 extra_idsigs.len()
422 ));
423 }
424 let extra_apks_count = extra_apks.len();
425
426 let (extra_apk_names, extra_root_hashes_from_idsig): (Vec<_>, Vec<_>) = extra_idsigs
427 .iter()
428 .enumerate()
429 .map(|(i, extra_idsig)| {
430 (
431 format!("extra-apk-{}", i),
432 get_apk_root_hash_from_idsig(extra_idsig.to_str().unwrap())
433 .expect("Can't find root hash from extra idsig"),
434 )
435 })
436 .unzip();
437
438 let saved_extra_root_hashes: Vec<_> = saved_data
439 .map(|d| d.extra_apks_data.iter().map(|apk_data| &apk_data.root_hash).collect())
440 .unwrap_or_else(Vec::new);
441 let extra_root_hashes_trustful: Vec<_> = extra_root_hashes_from_idsig
442 .iter()
443 .enumerate()
444 .map(|(i, root_hash_from_idsig)| {
445 saved_extra_root_hashes.get(i).copied() == Some(root_hash_from_idsig)
446 })
447 .collect();
448
449 for i in 0..extra_apks_count {
450 apkdmverity_arguments.push({
451 ApkDmverityArgument {
452 apk: extra_apks[i].to_str().unwrap(),
453 idsig: extra_idsigs[i].to_str().unwrap(),
454 name: &extra_apk_names[i],
455 saved_root_hash: if extra_root_hashes_trustful[i] {
456 Some(&extra_root_hashes_from_idsig[i])
457 } else {
458 None
459 },
460 }
461 });
Jiyong Parkbb4a9872021-09-06 15:59:21 +0900462 }
463
464 // Start apkdmverity and wait for the dm-verify block
Inseob Kim197748b2021-12-01 19:49:00 +0900465 let mut apkdmverity_child = run_apkdmverity(&apkdmverity_arguments)?;
Jooyung Han7a343f92021-09-08 22:53:11 +0900466
Jooyung Hanc8deb472021-09-13 13:48:25 +0900467 // While waiting for apkdmverity to mount APK, gathers public keys and root digests from
468 // APEX payload.
Jooyung Han7a343f92021-09-08 22:53:11 +0900469 let apex_data_from_payload = get_apex_data_from_payload(metadata)?;
Jooyung Han4a9b3bf2021-09-10 17:19:00 +0900470 if let Some(saved_data) = saved_data.map(|d| &d.apex_data) {
Jooyung Hanc8deb472021-09-13 13:48:25 +0900471 // We don't support APEX updates. (assuming that update will change root digest)
Jooyung Handd0a1732021-11-23 15:26:20 +0900472 ensure!(
473 saved_data == &apex_data_from_payload,
474 MicrodroidError::PayloadChanged(String::from("APEXes have changed."))
475 );
Jooyung Han4a9b3bf2021-09-10 17:19:00 +0900476 let apex_metadata = to_metadata(&apex_data_from_payload);
Jooyung Hanc8deb472021-09-13 13:48:25 +0900477 // Pass metadata(with public keys and root digests) to apexd so that it uses the passed
478 // metadata instead of the default one (/dev/block/by-name/payload-metadata)
Jooyung Han4a9b3bf2021-09-10 17:19:00 +0900479 OpenOptions::new()
480 .create_new(true)
481 .write(true)
482 .open("/apex/vm-payload-metadata")
483 .context("Failed to open /apex/vm-payload-metadata")
484 .and_then(|f| write_metadata(&apex_metadata, f))?;
485 }
486 // Start apexd to activate APEXes
487 system_properties::write("ctl.start", "apexd-vm")?;
Jooyung Han7a343f92021-09-08 22:53:11 +0900488
Inseob Kim217038e2021-11-25 11:15:06 +0900489 // TODO(inseob): add timeout
490 apkdmverity_child.wait()?;
Jooyung Han19c1d6c2021-08-06 14:08:16 +0900491
Jiyong Parkf7dea252021-09-08 01:42:54 +0900492 // Do the full verification if the root_hash is un-trustful. This requires the full scanning of
Jiyong Parkbb4a9872021-09-06 15:59:21 +0900493 // 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 +0900494 // 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 +0900495 // of the VM or APK was updated in the host.
496 // TODO(jooyung): consider multithreading to make this faster
Inseob Kim197748b2021-12-01 19:49:00 +0900497 let main_apk_pubkey = get_public_key_from_apk(DM_MOUNTED_APK_PATH, root_hash_trustful)?;
498 let extra_apks_data = extra_root_hashes_from_idsig
499 .into_iter()
500 .enumerate()
501 .map(|(i, extra_root_hash)| {
502 let mount_path = format!("/dev/block/mapper/{}", &extra_apk_names[i]);
503 let apk_pubkey = get_public_key_from_apk(&mount_path, extra_root_hashes_trustful[i])?;
504 Ok(ApkData { root_hash: extra_root_hash, pubkey: apk_pubkey })
505 })
506 .collect::<Result<Vec<_>>>()?;
Jiyong Parkbb4a9872021-09-06 15:59:21 +0900507
508 info!("payload verification successful. took {:#?}", start_time.elapsed().unwrap());
509
Andrew Scull34916a72022-01-30 21:34:24 +0000510 // Use the salt from a verified instance, or generate a salt for a new instance.
511 let salt = if let Some(saved_data) = saved_data {
512 saved_data.salt.clone()
513 } else {
514 let mut salt = vec![0u8; 64];
515 salt.as_mut_slice().try_fill(&mut rand::thread_rng())?;
516 salt
517 };
518
Jiyong Parkf7dea252021-09-08 01:42:54 +0900519 // At this point, we can ensure that the root_hash from the idsig file is trusted, either by
520 // fully verifying the APK or by comparing it with the saved root_hash.
Jooyung Han7a343f92021-09-08 22:53:11 +0900521 Ok(MicrodroidData {
Andrew Scull34916a72022-01-30 21:34:24 +0000522 salt,
Inseob Kim197748b2021-12-01 19:49:00 +0900523 apk_data: ApkData { root_hash: root_hash_from_idsig, pubkey: main_apk_pubkey },
524 extra_apks_data,
Jooyung Han7a343f92021-09-08 22:53:11 +0900525 apex_data: apex_data_from_payload,
526 })
Jiyong Parkbb4a9872021-09-06 15:59:21 +0900527}
528
Inseob Kim197748b2021-12-01 19:49:00 +0900529fn mount_extra_apks(config: &VmPayloadConfig) -> Result<()> {
530 // For now, only the number of apks is important, as the mount point and dm-verity name is fixed
531 for i in 0..config.extra_apks.len() {
532 let mount_dir = format!("/mnt/extra-apk/{}", i);
533 create_dir(Path::new(&mount_dir)).context("Failed to create mount dir for extra apks")?;
534
535 // don't wait, just detach
536 run_zipfuse(
537 "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
Jiyong Parkfa91d702021-10-18 23:51:39 +0900591 // Start logging if enabled
592 // TODO(b/200914564) set filterspec if debug_level is app_only
Andrew Scull4b5369f2022-01-28 13:47:20 +0000593 if system_properties::read_bool(LOGD_ENABLED_PROP, false)? {
Jiyong Parkfa91d702021-10-18 23:51:39 +0900594 system_properties::write("ctl.start", "seriallogging")?;
595 }
596
Inseob Kim86ca0162021-10-20 02:21:02 +0000597 let exit_status = command.spawn()?.wait()?;
Jooyung Han5c6d4172021-12-06 14:17:52 +0900598 exit_status.code().ok_or_else(|| anyhow!("Failed to get exit_code from the paylaod."))
Jooyung Han347d9f22021-05-28 00:05:14 +0900599}
Jooyung Han634e2d72021-06-10 16:27:38 +0900600
601fn build_command(task: &Task) -> Result<Command> {
Inseob Kim7f61fe72021-08-20 20:50:47 +0900602 const VMADDR_CID_HOST: u32 = 2;
Inseob Kim7f61fe72021-08-20 20:50:47 +0900603
604 let mut command = match task.type_ {
Jooyung Han634e2d72021-06-10 16:27:38 +0900605 TaskType::Executable => {
606 let mut command = Command::new(&task.command);
607 command.args(&task.args);
608 command
609 }
610 TaskType::MicrodroidLauncher => {
611 let mut command = Command::new("/system/bin/microdroid_launcher");
612 command.arg(find_library_path(&task.command)?).args(&task.args);
613 command
614 }
Inseob Kim7f61fe72021-08-20 20:50:47 +0900615 };
616
Inseob Kimd0587562021-09-01 21:27:32 +0900617 match VsockStream::connect_with_cid_port(VMADDR_CID_HOST, VM_STREAM_SERVICE_PORT as u32) {
Inseob Kim7f61fe72021-08-20 20:50:47 +0900618 Ok(stream) => {
619 // SAFETY: the ownership of the underlying file descriptor is transferred from stream
620 // to the file object, and then into the Command object. When the command is finished,
621 // the file descriptor is closed.
622 let file = unsafe { File::from_raw_fd(stream.into_raw_fd()) };
623 command
624 .stdin(Stdio::from(file.try_clone()?))
625 .stdout(Stdio::from(file.try_clone()?))
626 .stderr(Stdio::from(file));
627 }
628 Err(e) => {
629 error!("failed to connect to virtualization service: {}", e);
630 // Don't fail hard here. Even if we failed to connect to the virtualizationservice,
631 // we keep executing the task. This can happen if the owner of the VM doesn't register
632 // callback to accept the stream. Use /dev/null as the stream so that the task can
633 // make progress without waiting for someone to consume the output.
634 command.stdin(Stdio::null()).stdout(Stdio::null()).stderr(Stdio::null());
635 }
636 }
637
638 Ok(command)
Jooyung Han634e2d72021-06-10 16:27:38 +0900639}
640
641fn find_library_path(name: &str) -> Result<String> {
642 let mut watcher = PropertyWatcher::new("ro.product.cpu.abilist")?;
643 let value = watcher.read(|_name, value| Ok(value.trim().to_string()))?;
644 let abi = value.split(',').next().ok_or_else(|| anyhow!("no abilist"))?;
645 let path = format!("/mnt/apk/lib/{}/{}", abi, name);
646
647 let metadata = fs::metadata(&path)?;
648 if !metadata.is_file() {
649 bail!("{} is not a file", &path);
650 }
651
652 Ok(path)
653}
Jiyong Park21ce2c52021-08-28 02:32:17 +0900654
655fn to_hex_string(buf: &[u8]) -> String {
656 buf.iter().map(|b| format!("{:02X}", b)).collect()
657}