blob: 7629291a53c080e118a027f18684f88f8c892b4a [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
Inseob Kime379e7d2022-07-22 18:55:18 +090021use crate::instance::{ApexData, 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;
Alan Stokes2bead0d2022-09-05 16:58:34 +010026use android_system_virtualizationcommon::aidl::android::system::virtualizationcommon::ErrorCode::ErrorCode;
27use android_system_virtualmachineservice::aidl::android::system::virtualmachineservice::IVirtualMachineService::{
28 VM_BINDER_SERVICE_PORT, VM_STREAM_SERVICE_PORT, IVirtualMachineService,
29};
Jooyung Handd0a1732021-11-23 15:26:20 +090030use anyhow::{anyhow, bail, ensure, Context, Error, Result};
Jiyong Parka41535b2021-09-10 19:31:48 +090031use apkverify::{get_public_key_der, verify};
Andrew Walbranc4ce7872022-07-29 11:26:41 +000032use binder::{wait_for_interface, Strong};
Andrew Scullb2f44472022-01-21 14:41:34 +000033use diced_utils::cbor::encode_header;
Inseob Kim197748b2021-12-01 19:49:00 +090034use glob::glob;
Jiyong Park21ce2c52021-08-28 02:32:17 +090035use idsig::V4Signature;
Inseob Kim197748b2021-12-01 19:49:00 +090036use itertools::sorted;
Andrew Scull684590f2022-01-27 16:14:26 +000037use log::{error, info};
Jooyung Han4a9b3bf2021-09-10 17:19:00 +090038use microdroid_metadata::{write_metadata, Metadata};
Jooyung Han634e2d72021-06-10 16:27:38 +090039use microdroid_payload_config::{Task, TaskType, VmPayloadConfig};
Andrew Sculla0d1b1a2022-05-24 19:32:47 +000040use openssl::sha::Sha512;
Jooyung Han4a9b3bf2021-09-10 17:19:00 +090041use payload::{get_apex_data_from_payload, load_metadata, to_metadata};
Andrew Scull34916a72022-01-30 21:34:24 +000042use rand::Fill;
Andrew Walbran7eb5ca42022-08-08 15:33:34 +000043use rpcbinder::get_vsock_rpc_interface;
Jiyong Parkbb4a9872021-09-06 15:59:21 +090044use rustutils::system_properties;
Joel Galenson482704c2021-07-29 15:53:53 -070045use rustutils::system_properties::PropertyWatcher;
Andrew Scullb2f44472022-01-21 14:41:34 +000046use std::convert::TryInto;
Inseob Kim197748b2021-12-01 19:49:00 +090047use std::fs::{self, create_dir, File, OpenOptions};
Inseob Kim11f40d02022-06-13 17:16:00 +090048use std::io::Write;
Inseob Kimc7d28c72021-10-25 14:28:10 +000049use std::os::unix::io::{FromRawFd, IntoRawFd};
Jooyung Hanf48ceb42021-06-01 18:00:04 +090050use std::path::Path;
Inseob Kim217038e2021-11-25 11:15:06 +090051use std::process::{Child, Command, Stdio};
Jiyong Park8611a6c2021-07-09 18:17:44 +090052use std::str;
Jiyong Parkbb4a9872021-09-06 15:59:21 +090053use std::time::{Duration, SystemTime};
Jiyong Park8611a6c2021-07-09 18:17:44 +090054use vsock::VsockStream;
Jooyung Han634e2d72021-06-10 16:27:38 +090055
56const 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";
Inseob Kime379e7d2022-07-22 18:55:18 +090067const DEBUG_MICRODROID_NO_VERIFIED_BOOT: &str =
68 "/sys/firmware/devicetree/base/virtualization/guest/debug-microdroid,no-verified-boot";
Jooyung Han347d9f22021-05-28 00:05:14 +090069
Inseob Kim1b95f2e2021-08-19 13:17:40 +090070/// The CID representing the host VM
71const VMADDR_CID_HOST: u32 = 2;
72
Jiyong Parkbb4a9872021-09-06 15:59:21 +090073const APEX_CONFIG_DONE_PROP: &str = "apex_config.done";
Andrew Scull65ddfc42022-02-14 21:03:58 +000074const APP_DEBUGGABLE_PROP: &str = "ro.boot.microdroid.app_debuggable";
Jiyong Parkbb4a9872021-09-06 15:59:21 +090075
Inseob Kim11f40d02022-06-13 17:16:00 +090076// SYNC WITH virtualizationservice/src/crosvm.rs
77const FAILURE_SERIAL_DEVICE: &str = "/dev/ttyS1";
78
Jooyung Handd0a1732021-11-23 15:26:20 +090079#[derive(thiserror::Error, Debug)]
80enum MicrodroidError {
Inseob Kim11f40d02022-06-13 17:16:00 +090081 #[error("Cannot connect to virtualization service: {0}")]
82 FailedToConnectToVirtualizationService(String),
Jooyung Handd0a1732021-11-23 15:26:20 +090083 #[error("Payload has changed: {0}")]
84 PayloadChanged(String),
85 #[error("Payload verification has failed: {0}")]
86 PayloadVerificationFailed(String),
Jooyung Han5c6d4172021-12-06 14:17:52 +090087 #[error("Payload config is invalid: {0}")]
88 InvalidConfig(String),
Jooyung Handd0a1732021-11-23 15:26:20 +090089}
90
Alan Stokes2bead0d2022-09-05 16:58:34 +010091fn translate_error(err: &Error) -> (ErrorCode, String) {
Jooyung Handd0a1732021-11-23 15:26:20 +090092 if let Some(e) = err.downcast_ref::<MicrodroidError>() {
93 match e {
Alan Stokes2bead0d2022-09-05 16:58:34 +010094 MicrodroidError::PayloadChanged(msg) => (ErrorCode::PAYLOAD_CHANGED, msg.to_string()),
Jooyung Handd0a1732021-11-23 15:26:20 +090095 MicrodroidError::PayloadVerificationFailed(msg) => {
Alan Stokes2bead0d2022-09-05 16:58:34 +010096 (ErrorCode::PAYLOAD_VERIFICATION_FAILED, msg.to_string())
Jooyung Handd0a1732021-11-23 15:26:20 +090097 }
Alan Stokes2bead0d2022-09-05 16:58:34 +010098 MicrodroidError::InvalidConfig(msg) => {
99 (ErrorCode::PAYLOAD_CONFIG_INVALID, msg.to_string())
100 }
Inseob Kim11f40d02022-06-13 17:16:00 +0900101
102 // Connection failure won't be reported to VS; return the default value
103 MicrodroidError::FailedToConnectToVirtualizationService(msg) => {
Alan Stokes2bead0d2022-09-05 16:58:34 +0100104 (ErrorCode::UNKNOWN, msg.to_string())
Inseob Kim11f40d02022-06-13 17:16:00 +0900105 }
Jooyung Handd0a1732021-11-23 15:26:20 +0900106 }
107 } else {
Alan Stokes2bead0d2022-09-05 16:58:34 +0100108 (ErrorCode::UNKNOWN, err.to_string())
Jooyung Handd0a1732021-11-23 15:26:20 +0900109 }
110}
111
Inseob Kim11f40d02022-06-13 17:16:00 +0900112fn write_death_reason_to_serial(err: &Error) -> Result<()> {
113 let death_reason = if let Some(e) = err.downcast_ref::<MicrodroidError>() {
114 match e {
115 MicrodroidError::FailedToConnectToVirtualizationService(_) => {
116 "MICRODROID_FAILED_TO_CONNECT_TO_VIRTUALIZATION_SERVICE"
117 }
118 MicrodroidError::PayloadChanged(_) => "MICRODROID_PAYLOAD_HAS_CHANGED",
119 MicrodroidError::PayloadVerificationFailed(_) => {
120 "MICRODROID_PAYLOAD_VERIFICATION_FAILED"
121 }
122 MicrodroidError::InvalidConfig(_) => "MICRODROID_INVALID_PAYLOAD_CONFIG",
123 }
124 } else {
125 "MICRODROID_UNKNOWN_RUNTIME_ERROR"
126 };
127
128 let death_reason_bytes = death_reason.as_bytes();
129 let mut sent_total = 0;
130 while sent_total < death_reason_bytes.len() {
131 // TODO(b/220071963): Sometimes, sending more than 16 bytes at once makes MM hang.
132 let begin = sent_total;
133 let end = std::cmp::min(begin.saturating_add(16), death_reason_bytes.len());
134 OpenOptions::new()
135 .read(false)
136 .write(true)
137 .open(FAILURE_SERIAL_DEVICE)?
138 .write_all(&death_reason_bytes[begin..end])?;
139 sent_total = end;
140 }
141
142 Ok(())
143}
144
Inseob Kim1b95f2e2021-08-19 13:17:40 +0900145fn get_vms_rpc_binder() -> Result<Strong<dyn IVirtualMachineService>> {
Andrew Walbran7eb5ca42022-08-08 15:33:34 +0000146 get_vsock_rpc_interface(VMADDR_CID_HOST, VM_BINDER_SERVICE_PORT as u32)
Andrew Walbranc4ce7872022-07-29 11:26:41 +0000147 .context("Cannot connect to RPC service")
Inseob Kim1b95f2e2021-08-19 13:17:40 +0900148}
149
Inseob Kim437f1052022-06-21 11:30:22 +0900150fn main() -> Result<()> {
151 scopeguard::defer! {
152 info!("Shutting down...");
Jooyung Hanbfe086f2021-10-28 10:15:45 +0900153 if let Err(e) = system_properties::write("sys.powerctl", "shutdown") {
154 error!("failed to shutdown {:?}", e);
155 }
Jooyung Han311b1202021-09-14 22:00:16 +0900156 }
Inseob Kim437f1052022-06-21 11:30:22 +0900157
158 try_main().map_err(|e| {
159 error!("Failed with {:?}.", e);
160 if let Err(e) = write_death_reason_to_serial(&e) {
161 error!("Failed to write death reason {:?}", e);
162 }
163 e
164 })
Jooyung Han311b1202021-09-14 22:00:16 +0900165}
166
167fn try_main() -> Result<()> {
Andrew Sculle127cff2022-02-15 15:34:04 +0000168 let _ = kernlog::init();
Jooyung Han347d9f22021-05-28 00:05:14 +0900169 info!("started.");
170
Jiyong Park202856e2022-08-22 16:04:26 +0900171 load_crashkernel_if_supported().context("Failed to load crashkernel")?;
172
Inseob Kim11f40d02022-06-13 17:16:00 +0900173 let service = get_vms_rpc_binder()
174 .context("cannot connect to VirtualMachineService")
175 .map_err(|e| MicrodroidError::FailedToConnectToVirtualizationService(e.to_string()))?;
Jooyung Han5c6d4172021-12-06 14:17:52 +0900176 match try_run_payload(&service) {
177 Ok(code) => {
178 info!("notifying payload finished");
179 service.notifyPayloadFinished(code)?;
180 if code == 0 {
181 info!("task successfully finished");
182 } else {
183 error!("task exited with exit code: {}", code);
184 }
185 Ok(())
186 }
187 Err(err) => {
Jooyung Han5c6d4172021-12-06 14:17:52 +0900188 let (error_code, message) = translate_error(&err);
189 service.notifyError(error_code, &message)?;
190 Err(err)
191 }
Jooyung Handd0a1732021-11-23 15:26:20 +0900192 }
193}
194
Inseob Kimb2519c52022-04-14 02:10:09 +0900195fn dice_derivation(verified_data: &MicrodroidData, payload_config_path: &str) -> Result<()> {
Andrew Scullb2f44472022-01-21 14:41:34 +0000196 // Calculate compound digests of code and authorities
Andrew Sculla0d1b1a2022-05-24 19:32:47 +0000197 let mut code_hash_ctx = Sha512::new();
198 let mut authority_hash_ctx = Sha512::new();
Andrew Scullb2f44472022-01-21 14:41:34 +0000199 code_hash_ctx.update(verified_data.apk_data.root_hash.as_ref());
200 authority_hash_ctx.update(verified_data.apk_data.pubkey.as_ref());
Inseob Kimb2519c52022-04-14 02:10:09 +0900201 for extra_apk in &verified_data.extra_apks_data {
Andrew Scullb2f44472022-01-21 14:41:34 +0000202 code_hash_ctx.update(extra_apk.root_hash.as_ref());
203 authority_hash_ctx.update(extra_apk.pubkey.as_ref());
204 }
Inseob Kimb2519c52022-04-14 02:10:09 +0900205 for apex in &verified_data.apex_data {
Andrew Scullb2f44472022-01-21 14:41:34 +0000206 code_hash_ctx.update(apex.root_digest.as_ref());
207 authority_hash_ctx.update(apex.public_key.as_ref());
208 }
Andrew Sculla0d1b1a2022-05-24 19:32:47 +0000209 let code_hash = code_hash_ctx.finish();
210 let authority_hash = authority_hash_ctx.finish();
Andrew Scullb2f44472022-01-21 14:41:34 +0000211
212 // {
213 // -70002: "Microdroid payload",
214 // -71000: payload_config_path
215 // }
216 let mut config_desc = vec![
217 0xa2, 0x3a, 0x00, 0x01, 0x11, 0x71, 0x72, 0x4d, 0x69, 0x63, 0x72, 0x6f, 0x64, 0x72, 0x6f,
218 0x69, 0x64, 0x20, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x3a, 0x00, 0x01, 0x15, 0x57,
219 ];
220 let config_path_bytes = payload_config_path.as_bytes();
221 encode_header(3, config_path_bytes.len().try_into().unwrap(), &mut config_desc)?;
222 config_desc.extend_from_slice(config_path_bytes);
223
Andrew Scull65ddfc42022-02-14 21:03:58 +0000224 // Check app debuggability, conervatively assuming it is debuggable
225 let app_debuggable = system_properties::read_bool(APP_DEBUGGABLE_PROP, true)?;
226
Andrew Scullb2f44472022-01-21 14:41:34 +0000227 // Send the details to diced
228 let diced =
229 wait_for_interface::<dyn IDiceMaintenance>("android.security.dice.IDiceMaintenance")
230 .context("IDiceMaintenance service not found")?;
231 diced
232 .demoteSelf(&[InputValues {
233 codeHash: code_hash,
234 config: Config { desc: config_desc },
235 authorityHash: authority_hash,
236 authorityDescriptor: None,
Andrew Scull65ddfc42022-02-14 21:03:58 +0000237 mode: if app_debuggable { Mode::DEBUG } else { Mode::NORMAL },
Inseob Kimb2519c52022-04-14 02:10:09 +0900238 hidden: verified_data.salt.clone().try_into().unwrap(),
Andrew Scullb2f44472022-01-21 14:41:34 +0000239 }])
240 .context("IDiceMaintenance::demoteSelf failed")?;
241 Ok(())
242}
243
Andrew Scullab72ec52022-03-14 09:10:52 +0000244fn is_strict_boot() -> bool {
245 Path::new(AVF_STRICT_BOOT).exists()
246}
247
248fn is_new_instance() -> bool {
249 Path::new(AVF_NEW_INSTANCE).exists()
250}
251
Inseob Kime379e7d2022-07-22 18:55:18 +0900252fn is_verified_boot() -> bool {
253 !Path::new(DEBUG_MICRODROID_NO_VERIFIED_BOOT).exists()
254}
255
Jooyung Han5c6d4172021-12-06 14:17:52 +0900256fn try_run_payload(service: &Strong<dyn IVirtualMachineService>) -> Result<i32> {
Jooyung Han311b1202021-09-14 22:00:16 +0900257 let metadata = load_metadata().context("Failed to load payload metadata")?;
Jooyung Han19c1d6c2021-08-06 14:08:16 +0900258
Jooyung Han311b1202021-09-14 22:00:16 +0900259 let mut instance = InstanceDisk::new().context("Failed to load instance.img")?;
Jooyung Han7a343f92021-09-08 22:53:11 +0900260 let saved_data = instance.read_microdroid_data().context("Failed to read identity data")?;
Jiyong Parkbb4a9872021-09-06 15:59:21 +0900261
Andrew Scullab72ec52022-03-14 09:10:52 +0000262 if is_strict_boot() {
263 // Provisioning must happen on the first boot and never again.
264 if is_new_instance() {
265 ensure!(
266 saved_data.is_none(),
267 MicrodroidError::InvalidConfig("Found instance data on first boot.".to_string())
268 );
269 } else {
270 ensure!(
271 saved_data.is_some(),
272 MicrodroidError::InvalidConfig("Instance data not found.".to_string())
273 );
274 };
275 }
276
Jiyong Parkbb4a9872021-09-06 15:59:21 +0900277 // Verify the payload before using it.
Inseob Kim11f40d02022-06-13 17:16:00 +0900278 let verified_data = verify_payload(&metadata, saved_data.as_ref())
279 .context("Payload verification failed")
280 .map_err(|e| MicrodroidError::PayloadVerificationFailed(e.to_string()))?;
Inseob Kime379e7d2022-07-22 18:55:18 +0900281
282 // In case identity is ignored (by debug policy), we should reuse existing payload data, even
283 // when the payload is changed. This is to keep the derived secret same as before.
284 let verified_data = if let Some(saved_data) = saved_data {
285 if !is_verified_boot() {
286 if saved_data != verified_data {
287 info!("Detected an update of the payload, but continue (regarding debug policy)")
288 }
289 } else {
290 ensure!(
291 saved_data == verified_data,
292 MicrodroidError::PayloadChanged(String::from(
293 "Detected an update of the payload which isn't supported yet."
294 ))
295 );
296 info!("Saved data is verified.");
297 }
298 saved_data
Jiyong Parkbb4a9872021-09-06 15:59:21 +0900299 } else {
Jooyung Han7a343f92021-09-08 22:53:11 +0900300 info!("Saving verified data.");
301 instance.write_microdroid_data(&verified_data).context("Failed to write identity data")?;
Inseob Kime379e7d2022-07-22 18:55:18 +0900302 verified_data
303 };
Jooyung Han19c1d6c2021-08-06 14:08:16 +0900304
Inseob Kimb2519c52022-04-14 02:10:09 +0900305 // To minimize the exposure to untrusted data, derive dice profile as soon as possible.
306 info!("DICE derivation for payload");
307 dice_derivation(&verified_data, &metadata.payload_config_path)?;
308
Jooyung Hana6d11eb2021-09-10 11:48:05 +0900309 // Before reading a file from the APK, start zipfuse
Andrew Scullcc339a12022-07-04 12:44:19 +0000310 let noexec = false;
Inseob Kim217038e2021-11-25 11:15:06 +0900311 run_zipfuse(
Andrew Scullcc339a12022-07-04 12:44:19 +0000312 noexec,
Inseob Kim217038e2021-11-25 11:15:06 +0900313 "fscontext=u:object_r:zipfusefs:s0,context=u:object_r:system_file:s0",
314 Path::new("/dev/block/mapper/microdroid-apk"),
315 Path::new("/mnt/apk"),
316 )
317 .context("Failed to run zipfuse")?;
Jiyong Park21ce2c52021-08-28 02:32:17 +0900318
Jooyung Han5c6d4172021-12-06 14:17:52 +0900319 ensure!(
320 !metadata.payload_config_path.is_empty(),
321 MicrodroidError::InvalidConfig("No payload_config_path in metadata".to_string())
322 );
Inseob Kim197748b2021-12-01 19:49:00 +0900323
Jooyung Han5c6d4172021-12-06 14:17:52 +0900324 let config = load_config(Path::new(&metadata.payload_config_path))?;
Shikha Panwar6f03c942022-04-13 20:26:50 +0000325
Alan Stokes01b3ef02022-09-22 17:43:24 +0100326 let task = config
327 .task
328 .as_ref()
329 .ok_or_else(|| MicrodroidError::InvalidConfig("No task in VM config".to_string()))?;
330
Inseob Kim197748b2021-12-01 19:49:00 +0900331 if config.extra_apks.len() != verified_data.extra_apks_data.len() {
332 return Err(anyhow!(
333 "config expects {} extra apks, but found only {}",
334 config.extra_apks.len(),
335 verified_data.extra_apks_data.len()
336 ));
337 }
338 mount_extra_apks(&config)?;
Jooyung Han634e2d72021-06-10 16:27:38 +0900339
Jooyung Han5c6d4172021-12-06 14:17:52 +0900340 // Wait until apex config is done. (e.g. linker configuration for apexes)
341 // TODO(jooyung): wait until sys.boot_completed?
342 wait_for_apex_config_done()?;
343
Inseob Kimcd9c1dd2022-07-13 17:13:45 +0900344 // Start tombstone_transmit if enabled
345 if config.export_tombstones {
Alan Stokes01b3ef02022-09-22 17:43:24 +0100346 control_service("start", "tombstone_transmit")?;
Inseob Kimcd9c1dd2022-07-13 17:13:45 +0900347 } else {
Alan Stokes01b3ef02022-09-22 17:43:24 +0100348 control_service("stop", "tombstoned")?;
Inseob Kimcd9c1dd2022-07-13 17:13:45 +0900349 }
350
Alan Stokes01b3ef02022-09-22 17:43:24 +0100351 // Start authfs if enabled
352 if config.enable_authfs {
353 control_service("start", "authfs_service")?;
354 }
355
Victor Hsieh8d006ba2022-05-02 09:42:02 -0700356 system_properties::write("dev.bootcomplete", "1").context("set dev.bootcomplete")?;
Alan Stokes01b3ef02022-09-22 17:43:24 +0100357 exec_task(task, service)
358}
359
360fn control_service(action: &str, service: &str) -> Result<()> {
361 system_properties::write(&format!("ctl.{}", action), service)
362 .with_context(|| format!("Failed to {} {}", action, service))
Jooyung Han347d9f22021-05-28 00:05:14 +0900363}
364
Inseob Kim217038e2021-11-25 11:15:06 +0900365struct ApkDmverityArgument<'a> {
366 apk: &'a str,
367 idsig: &'a str,
368 name: &'a str,
Inseob Kim197748b2021-12-01 19:49:00 +0900369 saved_root_hash: Option<&'a RootHash>,
Inseob Kim217038e2021-11-25 11:15:06 +0900370}
371
372fn run_apkdmverity(args: &[ApkDmverityArgument]) -> Result<Child> {
373 let mut cmd = Command::new(APKDMVERITY_BIN);
374
375 cmd.stdin(Stdio::null()).stdout(Stdio::null()).stderr(Stdio::null());
376
377 for argument in args {
378 cmd.arg("--apk").arg(argument.apk).arg(argument.idsig).arg(argument.name);
Inseob Kim197748b2021-12-01 19:49:00 +0900379 if let Some(root_hash) = argument.saved_root_hash {
380 cmd.arg(&to_hex_string(root_hash));
381 } else {
382 cmd.arg("none");
383 }
Inseob Kim217038e2021-11-25 11:15:06 +0900384 }
385
386 cmd.spawn().context("Spawn apkdmverity")
387}
388
Andrew Scullcc339a12022-07-04 12:44:19 +0000389fn run_zipfuse(noexec: bool, option: &str, zip_path: &Path, mount_dir: &Path) -> Result<Child> {
390 let mut cmd = Command::new(ZIPFUSE_BIN);
391 if noexec {
392 cmd.arg("--noexec");
393 }
394 cmd.arg("-o")
Inseob Kim217038e2021-11-25 11:15:06 +0900395 .arg(option)
396 .arg(zip_path)
397 .arg(mount_dir)
398 .stdin(Stdio::null())
399 .stdout(Stdio::null())
400 .stderr(Stdio::null())
401 .spawn()
402 .context("Spawn zipfuse")
403}
404
Inseob Kime379e7d2022-07-22 18:55:18 +0900405fn write_apex_payload_data(
406 saved_data: Option<&MicrodroidData>,
407 apex_data_from_payload: &[ApexData],
408) -> Result<()> {
409 if let Some(saved_apex_data) = saved_data.map(|d| &d.apex_data) {
410 // We don't support APEX updates. (assuming that update will change root digest)
411 ensure!(
412 saved_apex_data == apex_data_from_payload,
413 MicrodroidError::PayloadChanged(String::from("APEXes have changed."))
414 );
415 let apex_metadata = to_metadata(apex_data_from_payload);
416 // Pass metadata(with public keys and root digests) to apexd so that it uses the passed
417 // metadata instead of the default one (/dev/block/by-name/payload-metadata)
418 OpenOptions::new()
419 .create_new(true)
420 .write(true)
421 .open("/apex/vm-payload-metadata")
422 .context("Failed to open /apex/vm-payload-metadata")
423 .and_then(|f| write_metadata(&apex_metadata, f))?;
424 }
425 Ok(())
426}
427
Jooyung Han7a343f92021-09-08 22:53:11 +0900428// Verify payload before executing it. For APK payload, Full verification (which is slow) is done
429// when the root_hash values from the idsig file and the instance disk are different. This function
430// returns the verified root hash (for APK payload) and pubkeys (for APEX payloads) that can be
431// saved to the instance disk.
432fn verify_payload(
433 metadata: &Metadata,
434 saved_data: Option<&MicrodroidData>,
435) -> Result<MicrodroidData> {
Jiyong Parkbb4a9872021-09-06 15:59:21 +0900436 let start_time = SystemTime::now();
437
Inseob Kim197748b2021-12-01 19:49:00 +0900438 // Verify main APK
Jooyung Han7a343f92021-09-08 22:53:11 +0900439 let root_hash = saved_data.map(|d| &d.apk_data.root_hash);
Inseob Kim197748b2021-12-01 19:49:00 +0900440 let root_hash_from_idsig = get_apk_root_hash_from_idsig(MAIN_APK_IDSIG_PATH)?;
Jiyong Parkf7dea252021-09-08 01:42:54 +0900441 let root_hash_trustful = root_hash == Some(&root_hash_from_idsig);
Jiyong Parkbb4a9872021-09-06 15:59:21 +0900442
Jiyong Parkf7dea252021-09-08 01:42:54 +0900443 // 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 +0900444 // instead of the value read from the idsig file.
Inseob Kim197748b2021-12-01 19:49:00 +0900445 let main_apk_argument = {
446 ApkDmverityArgument {
447 apk: MAIN_APK_PATH,
448 idsig: MAIN_APK_IDSIG_PATH,
449 name: MAIN_APK_DEVICE_NAME,
450 saved_root_hash: if root_hash_trustful {
451 Some(root_hash_from_idsig.as_ref())
452 } else {
453 None
454 },
455 }
456 };
457 let mut apkdmverity_arguments = vec![main_apk_argument];
458
459 // Verify extra APKs
460 // For now, we can't read the payload config, so glob APKs and idsigs.
461 // Later, we'll see if it matches with the payload config.
462
463 // sort globbed paths to match apks (extra-apk-{idx}) and idsigs (extra-idsig-{idx})
464 // e.g. "extra-apk-0" corresponds to "extra-idsig-0"
465 let extra_apks =
466 sorted(glob(EXTRA_APK_PATH_PATTERN)?.collect::<Result<Vec<_>, _>>()?).collect::<Vec<_>>();
467 let extra_idsigs =
468 sorted(glob(EXTRA_IDSIG_PATH_PATTERN)?.collect::<Result<Vec<_>, _>>()?).collect::<Vec<_>>();
469 if extra_apks.len() != extra_idsigs.len() {
470 return Err(anyhow!(
471 "Extra apks/idsigs mismatch: {} apks but {} idsigs",
472 extra_apks.len(),
473 extra_idsigs.len()
474 ));
475 }
476 let extra_apks_count = extra_apks.len();
477
478 let (extra_apk_names, extra_root_hashes_from_idsig): (Vec<_>, Vec<_>) = extra_idsigs
479 .iter()
480 .enumerate()
481 .map(|(i, extra_idsig)| {
482 (
483 format!("extra-apk-{}", i),
484 get_apk_root_hash_from_idsig(extra_idsig.to_str().unwrap())
485 .expect("Can't find root hash from extra idsig"),
486 )
487 })
488 .unzip();
489
490 let saved_extra_root_hashes: Vec<_> = saved_data
491 .map(|d| d.extra_apks_data.iter().map(|apk_data| &apk_data.root_hash).collect())
492 .unwrap_or_else(Vec::new);
493 let extra_root_hashes_trustful: Vec<_> = extra_root_hashes_from_idsig
494 .iter()
495 .enumerate()
496 .map(|(i, root_hash_from_idsig)| {
497 saved_extra_root_hashes.get(i).copied() == Some(root_hash_from_idsig)
498 })
499 .collect();
500
501 for i in 0..extra_apks_count {
502 apkdmverity_arguments.push({
503 ApkDmverityArgument {
504 apk: extra_apks[i].to_str().unwrap(),
505 idsig: extra_idsigs[i].to_str().unwrap(),
506 name: &extra_apk_names[i],
507 saved_root_hash: if extra_root_hashes_trustful[i] {
508 Some(&extra_root_hashes_from_idsig[i])
509 } else {
510 None
511 },
512 }
513 });
Jiyong Parkbb4a9872021-09-06 15:59:21 +0900514 }
515
516 // Start apkdmverity and wait for the dm-verify block
Inseob Kim197748b2021-12-01 19:49:00 +0900517 let mut apkdmverity_child = run_apkdmverity(&apkdmverity_arguments)?;
Jooyung Han7a343f92021-09-08 22:53:11 +0900518
Jooyung Hanc8deb472021-09-13 13:48:25 +0900519 // While waiting for apkdmverity to mount APK, gathers public keys and root digests from
520 // APEX payload.
Jooyung Han7a343f92021-09-08 22:53:11 +0900521 let apex_data_from_payload = get_apex_data_from_payload(metadata)?;
Inseob Kime379e7d2022-07-22 18:55:18 +0900522
523 // Writing /apex/vm-payload-metadata is to verify that the payload isn't changed.
524 // Skip writing it if the debug policy ignoring identity is on
525 if is_verified_boot() {
526 write_apex_payload_data(saved_data, &apex_data_from_payload)?;
Jooyung Han4a9b3bf2021-09-10 17:19:00 +0900527 }
Inseob Kime379e7d2022-07-22 18:55:18 +0900528
Jooyung Han4a9b3bf2021-09-10 17:19:00 +0900529 // Start apexd to activate APEXes
530 system_properties::write("ctl.start", "apexd-vm")?;
Jooyung Han7a343f92021-09-08 22:53:11 +0900531
Inseob Kim217038e2021-11-25 11:15:06 +0900532 // TODO(inseob): add timeout
533 apkdmverity_child.wait()?;
Jooyung Han19c1d6c2021-08-06 14:08:16 +0900534
Jiyong Parkf7dea252021-09-08 01:42:54 +0900535 // Do the full verification if the root_hash is un-trustful. This requires the full scanning of
Jiyong Parkbb4a9872021-09-06 15:59:21 +0900536 // 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 +0900537 // 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 +0900538 // of the VM or APK was updated in the host.
539 // TODO(jooyung): consider multithreading to make this faster
Inseob Kim197748b2021-12-01 19:49:00 +0900540 let main_apk_pubkey = get_public_key_from_apk(DM_MOUNTED_APK_PATH, root_hash_trustful)?;
541 let extra_apks_data = extra_root_hashes_from_idsig
542 .into_iter()
543 .enumerate()
544 .map(|(i, extra_root_hash)| {
545 let mount_path = format!("/dev/block/mapper/{}", &extra_apk_names[i]);
546 let apk_pubkey = get_public_key_from_apk(&mount_path, extra_root_hashes_trustful[i])?;
547 Ok(ApkData { root_hash: extra_root_hash, pubkey: apk_pubkey })
548 })
549 .collect::<Result<Vec<_>>>()?;
Jiyong Parkbb4a9872021-09-06 15:59:21 +0900550
551 info!("payload verification successful. took {:#?}", start_time.elapsed().unwrap());
552
Andrew Scull34916a72022-01-30 21:34:24 +0000553 // Use the salt from a verified instance, or generate a salt for a new instance.
554 let salt = if let Some(saved_data) = saved_data {
555 saved_data.salt.clone()
556 } else {
557 let mut salt = vec![0u8; 64];
558 salt.as_mut_slice().try_fill(&mut rand::thread_rng())?;
559 salt
560 };
561
Jiyong Parkf7dea252021-09-08 01:42:54 +0900562 // At this point, we can ensure that the root_hash from the idsig file is trusted, either by
563 // fully verifying the APK or by comparing it with the saved root_hash.
Jooyung Han7a343f92021-09-08 22:53:11 +0900564 Ok(MicrodroidData {
Andrew Scull34916a72022-01-30 21:34:24 +0000565 salt,
Inseob Kim197748b2021-12-01 19:49:00 +0900566 apk_data: ApkData { root_hash: root_hash_from_idsig, pubkey: main_apk_pubkey },
567 extra_apks_data,
Jooyung Han7a343f92021-09-08 22:53:11 +0900568 apex_data: apex_data_from_payload,
569 })
Jiyong Parkbb4a9872021-09-06 15:59:21 +0900570}
571
Inseob Kim197748b2021-12-01 19:49:00 +0900572fn mount_extra_apks(config: &VmPayloadConfig) -> Result<()> {
573 // For now, only the number of apks is important, as the mount point and dm-verity name is fixed
574 for i in 0..config.extra_apks.len() {
575 let mount_dir = format!("/mnt/extra-apk/{}", i);
576 create_dir(Path::new(&mount_dir)).context("Failed to create mount dir for extra apks")?;
577
578 // don't wait, just detach
Andrew Scullcc339a12022-07-04 12:44:19 +0000579 let noexec = true;
Inseob Kim197748b2021-12-01 19:49:00 +0900580 run_zipfuse(
Andrew Scullcc339a12022-07-04 12:44:19 +0000581 noexec,
Inseob Kim197748b2021-12-01 19:49:00 +0900582 "fscontext=u:object_r:zipfusefs:s0,context=u:object_r:extra_apk_file:s0",
583 Path::new(&format!("/dev/block/mapper/extra-apk-{}", i)),
584 Path::new(&mount_dir),
585 )
586 .context("Failed to zipfuse extra apks")?;
587 }
588
589 Ok(())
590}
591
Jiyong Parkbb4a9872021-09-06 15:59:21 +0900592// Waits until linker config is generated
593fn wait_for_apex_config_done() -> Result<()> {
594 let mut prop = PropertyWatcher::new(APEX_CONFIG_DONE_PROP)?;
595 loop {
596 prop.wait()?;
Andrew Scull4b5369f2022-01-28 13:47:20 +0000597 if system_properties::read_bool(APEX_CONFIG_DONE_PROP, false)? {
Jiyong Parkbb4a9872021-09-06 15:59:21 +0900598 break;
599 }
600 }
Jooyung Han19c1d6c2021-08-06 14:08:16 +0900601 Ok(())
602}
603
Inseob Kim197748b2021-12-01 19:49:00 +0900604fn get_apk_root_hash_from_idsig(path: &str) -> Result<Box<RootHash>> {
605 let mut idsig = File::open(path)?;
Jiyong Park21ce2c52021-08-28 02:32:17 +0900606 let idsig = V4Signature::from(&mut idsig)?;
607 Ok(idsig.hashing_info.raw_root_hash)
608}
609
Inseob Kim197748b2021-12-01 19:49:00 +0900610fn get_public_key_from_apk(apk: &str, root_hash_trustful: bool) -> Result<Box<[u8]>> {
611 if !root_hash_trustful {
612 verify(apk).context(MicrodroidError::PayloadVerificationFailed(format!(
613 "failed to verify {}",
614 apk
615 )))
616 } else {
617 get_public_key_der(apk)
618 }
619}
620
Jooyung Han634e2d72021-06-10 16:27:38 +0900621fn load_config(path: &Path) -> Result<VmPayloadConfig> {
622 info!("loading config from {:?}...", path);
623 let file = ioutil::wait_for_file(path, WAIT_TIMEOUT)?;
624 Ok(serde_json::from_reader(file)?)
625}
626
Jiyong Park202856e2022-08-22 16:04:26 +0900627/// Loads the crashkernel into memory using kexec if the VM is loaded with `crashkernel=' parameter
628/// in the cmdline.
629fn load_crashkernel_if_supported() -> Result<()> {
630 let supported = std::fs::read_to_string("/proc/cmdline")?.contains(" crashkernel=");
631 info!("ramdump supported: {}", supported);
632 if supported {
633 let status = Command::new("/system/bin/kexec_load").status()?;
634 if !status.success() {
635 return Err(anyhow!("Failed to load crashkernel: {:?}", status));
636 }
637 }
638 Ok(())
639}
640
Jiyong Park8611a6c2021-07-09 18:17:44 +0900641/// Executes the given task. Stdout of the task is piped into the vsock stream to the
642/// virtualizationservice in the host side.
Jooyung Han5c6d4172021-12-06 14:17:52 +0900643fn exec_task(task: &Task, service: &Strong<dyn IVirtualMachineService>) -> Result<i32> {
Jiyong Park8611a6c2021-07-09 18:17:44 +0900644 info!("executing main task {:?}...", task);
Inseob Kim86ca0162021-10-20 02:21:02 +0000645 let mut command = build_command(task)?;
Inseob Kim7f61fe72021-08-20 20:50:47 +0900646
647 info!("notifying payload started");
Inseob Kimc7d28c72021-10-25 14:28:10 +0000648 service.notifyPayloadStarted()?;
Inseob Kim7f61fe72021-08-20 20:50:47 +0900649
Inseob Kim86ca0162021-10-20 02:21:02 +0000650 let exit_status = command.spawn()?.wait()?;
Jooyung Han5c6d4172021-12-06 14:17:52 +0900651 exit_status.code().ok_or_else(|| anyhow!("Failed to get exit_code from the paylaod."))
Jooyung Han347d9f22021-05-28 00:05:14 +0900652}
Jooyung Han634e2d72021-06-10 16:27:38 +0900653
654fn build_command(task: &Task) -> Result<Command> {
Inseob Kim7f61fe72021-08-20 20:50:47 +0900655 const VMADDR_CID_HOST: u32 = 2;
Inseob Kim7f61fe72021-08-20 20:50:47 +0900656
657 let mut command = match task.type_ {
Jooyung Han634e2d72021-06-10 16:27:38 +0900658 TaskType::Executable => {
659 let mut command = Command::new(&task.command);
660 command.args(&task.args);
661 command
662 }
663 TaskType::MicrodroidLauncher => {
664 let mut command = Command::new("/system/bin/microdroid_launcher");
665 command.arg(find_library_path(&task.command)?).args(&task.args);
666 command
667 }
Inseob Kim7f61fe72021-08-20 20:50:47 +0900668 };
669
Inseob Kimd0587562021-09-01 21:27:32 +0900670 match VsockStream::connect_with_cid_port(VMADDR_CID_HOST, VM_STREAM_SERVICE_PORT as u32) {
Inseob Kim7f61fe72021-08-20 20:50:47 +0900671 Ok(stream) => {
672 // SAFETY: the ownership of the underlying file descriptor is transferred from stream
673 // to the file object, and then into the Command object. When the command is finished,
674 // the file descriptor is closed.
675 let file = unsafe { File::from_raw_fd(stream.into_raw_fd()) };
676 command
677 .stdin(Stdio::from(file.try_clone()?))
678 .stdout(Stdio::from(file.try_clone()?))
679 .stderr(Stdio::from(file));
680 }
681 Err(e) => {
682 error!("failed to connect to virtualization service: {}", e);
683 // Don't fail hard here. Even if we failed to connect to the virtualizationservice,
684 // we keep executing the task. This can happen if the owner of the VM doesn't register
685 // callback to accept the stream. Use /dev/null as the stream so that the task can
686 // make progress without waiting for someone to consume the output.
687 command.stdin(Stdio::null()).stdout(Stdio::null()).stderr(Stdio::null());
688 }
689 }
690
691 Ok(command)
Jooyung Han634e2d72021-06-10 16:27:38 +0900692}
693
694fn find_library_path(name: &str) -> Result<String> {
695 let mut watcher = PropertyWatcher::new("ro.product.cpu.abilist")?;
696 let value = watcher.read(|_name, value| Ok(value.trim().to_string()))?;
697 let abi = value.split(',').next().ok_or_else(|| anyhow!("no abilist"))?;
698 let path = format!("/mnt/apk/lib/{}/{}", abi, name);
699
700 let metadata = fs::metadata(&path)?;
701 if !metadata.is_file() {
702 bail!("{} is not a file", &path);
703 }
704
705 Ok(path)
706}
Jiyong Park21ce2c52021-08-28 02:32:17 +0900707
708fn to_hex_string(buf: &[u8]) -> String {
709 buf.iter().map(|b| format!("{:02X}", b)).collect()
710}