blob: eb5333718bb8b77337caa98511fbfb0b97aea091 [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;
Jooyung Handd0a1732021-11-23 15:26:20 +090026use anyhow::{anyhow, bail, ensure, Context, Error, Result};
Jiyong Parka41535b2021-09-10 19:31:48 +090027use apkverify::{get_public_key_der, verify};
Andrew Walbranc4ce7872022-07-29 11:26:41 +000028use binder::{wait_for_interface, Strong};
29use binder_common::rpc_client::connect_rpc_binder;
Andrew Scullb2f44472022-01-21 14:41:34 +000030use diced_utils::cbor::encode_header;
Inseob Kim197748b2021-12-01 19:49:00 +090031use glob::glob;
Jiyong Park21ce2c52021-08-28 02:32:17 +090032use idsig::V4Signature;
Inseob Kim197748b2021-12-01 19:49:00 +090033use itertools::sorted;
Andrew Scull684590f2022-01-27 16:14:26 +000034use log::{error, info};
Jooyung Han4a9b3bf2021-09-10 17:19:00 +090035use microdroid_metadata::{write_metadata, Metadata};
Jooyung Han634e2d72021-06-10 16:27:38 +090036use microdroid_payload_config::{Task, TaskType, VmPayloadConfig};
Andrew Sculla0d1b1a2022-05-24 19:32:47 +000037use openssl::sha::Sha512;
Jooyung Han4a9b3bf2021-09-10 17:19:00 +090038use payload::{get_apex_data_from_payload, load_metadata, to_metadata};
Andrew Scull34916a72022-01-30 21:34:24 +000039use rand::Fill;
Jiyong Parkbb4a9872021-09-06 15:59:21 +090040use rustutils::system_properties;
Joel Galenson482704c2021-07-29 15:53:53 -070041use rustutils::system_properties::PropertyWatcher;
Andrew Scullb2f44472022-01-21 14:41:34 +000042use std::convert::TryInto;
Inseob Kim197748b2021-12-01 19:49:00 +090043use std::fs::{self, create_dir, File, OpenOptions};
Inseob Kim11f40d02022-06-13 17:16:00 +090044use std::io::Write;
Inseob Kimc7d28c72021-10-25 14:28:10 +000045use std::os::unix::io::{FromRawFd, IntoRawFd};
Jooyung Hanf48ceb42021-06-01 18:00:04 +090046use std::path::Path;
Inseob Kim217038e2021-11-25 11:15:06 +090047use std::process::{Child, Command, Stdio};
Jiyong Park8611a6c2021-07-09 18:17:44 +090048use std::str;
Jiyong Parkbb4a9872021-09-06 15:59:21 +090049use std::time::{Duration, SystemTime};
Jiyong Park8611a6c2021-07-09 18:17:44 +090050use vsock::VsockStream;
Jooyung Han634e2d72021-06-10 16:27:38 +090051
Inseob Kimd0587562021-09-01 21:27:32 +090052use android_system_virtualmachineservice::aidl::android::system::virtualmachineservice::IVirtualMachineService::{
Jooyung Han5c6d4172021-12-06 14:17:52 +090053 ERROR_PAYLOAD_CHANGED, ERROR_PAYLOAD_VERIFICATION_FAILED, ERROR_PAYLOAD_INVALID_CONFIG, ERROR_UNKNOWN, VM_BINDER_SERVICE_PORT, VM_STREAM_SERVICE_PORT, IVirtualMachineService,
Inseob Kimd0587562021-09-01 21:27:32 +090054};
Inseob Kim1b95f2e2021-08-19 13:17:40 +090055
Jooyung Han634e2d72021-06-10 16:27:38 +090056const WAIT_TIMEOUT: Duration = Duration::from_secs(10);
Inseob Kim197748b2021-12-01 19:49:00 +090057const MAIN_APK_PATH: &str = "/dev/block/by-name/microdroid-apk";
58const MAIN_APK_IDSIG_PATH: &str = "/dev/block/by-name/microdroid-apk-idsig";
59const MAIN_APK_DEVICE_NAME: &str = "microdroid-apk";
60const EXTRA_APK_PATH_PATTERN: &str = "/dev/block/by-name/extra-apk-*";
61const EXTRA_IDSIG_PATH_PATTERN: &str = "/dev/block/by-name/extra-idsig-*";
Jooyung Han19c1d6c2021-08-06 14:08:16 +090062const DM_MOUNTED_APK_PATH: &str = "/dev/block/mapper/microdroid-apk";
Inseob Kim217038e2021-11-25 11:15:06 +090063const APKDMVERITY_BIN: &str = "/system/bin/apkdmverity";
64const ZIPFUSE_BIN: &str = "/system/bin/zipfuse";
Andrew Scullab72ec52022-03-14 09:10:52 +000065const AVF_STRICT_BOOT: &str = "/sys/firmware/devicetree/base/chosen/avf,strict-boot";
66const AVF_NEW_INSTANCE: &str = "/sys/firmware/devicetree/base/chosen/avf,new-instance";
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
91fn translate_error(err: &Error) -> (i32, String) {
92 if let Some(e) = err.downcast_ref::<MicrodroidError>() {
93 match e {
94 MicrodroidError::PayloadChanged(msg) => (ERROR_PAYLOAD_CHANGED, msg.to_string()),
95 MicrodroidError::PayloadVerificationFailed(msg) => {
96 (ERROR_PAYLOAD_VERIFICATION_FAILED, msg.to_string())
97 }
Jooyung Han5c6d4172021-12-06 14:17:52 +090098 MicrodroidError::InvalidConfig(msg) => (ERROR_PAYLOAD_INVALID_CONFIG, msg.to_string()),
Inseob Kim11f40d02022-06-13 17:16:00 +090099
100 // Connection failure won't be reported to VS; return the default value
101 MicrodroidError::FailedToConnectToVirtualizationService(msg) => {
102 (ERROR_UNKNOWN, msg.to_string())
103 }
Jooyung Handd0a1732021-11-23 15:26:20 +0900104 }
105 } else {
106 (ERROR_UNKNOWN, err.to_string())
107 }
108}
109
Inseob Kim11f40d02022-06-13 17:16:00 +0900110fn write_death_reason_to_serial(err: &Error) -> Result<()> {
111 let death_reason = if let Some(e) = err.downcast_ref::<MicrodroidError>() {
112 match e {
113 MicrodroidError::FailedToConnectToVirtualizationService(_) => {
114 "MICRODROID_FAILED_TO_CONNECT_TO_VIRTUALIZATION_SERVICE"
115 }
116 MicrodroidError::PayloadChanged(_) => "MICRODROID_PAYLOAD_HAS_CHANGED",
117 MicrodroidError::PayloadVerificationFailed(_) => {
118 "MICRODROID_PAYLOAD_VERIFICATION_FAILED"
119 }
120 MicrodroidError::InvalidConfig(_) => "MICRODROID_INVALID_PAYLOAD_CONFIG",
121 }
122 } else {
123 "MICRODROID_UNKNOWN_RUNTIME_ERROR"
124 };
125
126 let death_reason_bytes = death_reason.as_bytes();
127 let mut sent_total = 0;
128 while sent_total < death_reason_bytes.len() {
129 // TODO(b/220071963): Sometimes, sending more than 16 bytes at once makes MM hang.
130 let begin = sent_total;
131 let end = std::cmp::min(begin.saturating_add(16), death_reason_bytes.len());
132 OpenOptions::new()
133 .read(false)
134 .write(true)
135 .open(FAILURE_SERIAL_DEVICE)?
136 .write_all(&death_reason_bytes[begin..end])?;
137 sent_total = end;
138 }
139
140 Ok(())
141}
142
Inseob Kim1b95f2e2021-08-19 13:17:40 +0900143fn get_vms_rpc_binder() -> Result<Strong<dyn IVirtualMachineService>> {
Andrew Walbranc4ce7872022-07-29 11:26:41 +0000144 connect_rpc_binder(VMADDR_CID_HOST, VM_BINDER_SERVICE_PORT as u32)
145 .context("Cannot connect to RPC service")
Inseob Kim1b95f2e2021-08-19 13:17:40 +0900146}
147
Inseob Kim437f1052022-06-21 11:30:22 +0900148fn main() -> Result<()> {
149 scopeguard::defer! {
150 info!("Shutting down...");
Jooyung Hanbfe086f2021-10-28 10:15:45 +0900151 if let Err(e) = system_properties::write("sys.powerctl", "shutdown") {
152 error!("failed to shutdown {:?}", e);
153 }
Jooyung Han311b1202021-09-14 22:00:16 +0900154 }
Inseob Kim437f1052022-06-21 11:30:22 +0900155
156 try_main().map_err(|e| {
157 error!("Failed with {:?}.", e);
158 if let Err(e) = write_death_reason_to_serial(&e) {
159 error!("Failed to write death reason {:?}", e);
160 }
161 e
162 })
Jooyung Han311b1202021-09-14 22:00:16 +0900163}
164
165fn try_main() -> Result<()> {
Andrew Sculle127cff2022-02-15 15:34:04 +0000166 let _ = kernlog::init();
Jooyung Han347d9f22021-05-28 00:05:14 +0900167 info!("started.");
168
Jiyong Park202856e2022-08-22 16:04:26 +0900169 load_crashkernel_if_supported().context("Failed to load crashkernel")?;
170
Inseob Kim11f40d02022-06-13 17:16:00 +0900171 let service = get_vms_rpc_binder()
172 .context("cannot connect to VirtualMachineService")
173 .map_err(|e| MicrodroidError::FailedToConnectToVirtualizationService(e.to_string()))?;
Jooyung Han5c6d4172021-12-06 14:17:52 +0900174 match try_run_payload(&service) {
175 Ok(code) => {
176 info!("notifying payload finished");
177 service.notifyPayloadFinished(code)?;
178 if code == 0 {
179 info!("task successfully finished");
180 } else {
181 error!("task exited with exit code: {}", code);
182 }
183 Ok(())
184 }
185 Err(err) => {
Jooyung Han5c6d4172021-12-06 14:17:52 +0900186 let (error_code, message) = translate_error(&err);
187 service.notifyError(error_code, &message)?;
188 Err(err)
189 }
Jooyung Handd0a1732021-11-23 15:26:20 +0900190 }
191}
192
Inseob Kimb2519c52022-04-14 02:10:09 +0900193fn dice_derivation(verified_data: &MicrodroidData, payload_config_path: &str) -> Result<()> {
Andrew Scullb2f44472022-01-21 14:41:34 +0000194 // Calculate compound digests of code and authorities
Andrew Sculla0d1b1a2022-05-24 19:32:47 +0000195 let mut code_hash_ctx = Sha512::new();
196 let mut authority_hash_ctx = Sha512::new();
Andrew Scullb2f44472022-01-21 14:41:34 +0000197 code_hash_ctx.update(verified_data.apk_data.root_hash.as_ref());
198 authority_hash_ctx.update(verified_data.apk_data.pubkey.as_ref());
Inseob Kimb2519c52022-04-14 02:10:09 +0900199 for extra_apk in &verified_data.extra_apks_data {
Andrew Scullb2f44472022-01-21 14:41:34 +0000200 code_hash_ctx.update(extra_apk.root_hash.as_ref());
201 authority_hash_ctx.update(extra_apk.pubkey.as_ref());
202 }
Inseob Kimb2519c52022-04-14 02:10:09 +0900203 for apex in &verified_data.apex_data {
Andrew Scullb2f44472022-01-21 14:41:34 +0000204 code_hash_ctx.update(apex.root_digest.as_ref());
205 authority_hash_ctx.update(apex.public_key.as_ref());
206 }
Andrew Sculla0d1b1a2022-05-24 19:32:47 +0000207 let code_hash = code_hash_ctx.finish();
208 let authority_hash = authority_hash_ctx.finish();
Andrew Scullb2f44472022-01-21 14:41:34 +0000209
210 // {
211 // -70002: "Microdroid payload",
212 // -71000: payload_config_path
213 // }
214 let mut config_desc = vec![
215 0xa2, 0x3a, 0x00, 0x01, 0x11, 0x71, 0x72, 0x4d, 0x69, 0x63, 0x72, 0x6f, 0x64, 0x72, 0x6f,
216 0x69, 0x64, 0x20, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x3a, 0x00, 0x01, 0x15, 0x57,
217 ];
218 let config_path_bytes = payload_config_path.as_bytes();
219 encode_header(3, config_path_bytes.len().try_into().unwrap(), &mut config_desc)?;
220 config_desc.extend_from_slice(config_path_bytes);
221
Andrew Scull65ddfc42022-02-14 21:03:58 +0000222 // Check app debuggability, conervatively assuming it is debuggable
223 let app_debuggable = system_properties::read_bool(APP_DEBUGGABLE_PROP, true)?;
224
Andrew Scullb2f44472022-01-21 14:41:34 +0000225 // Send the details to diced
226 let diced =
227 wait_for_interface::<dyn IDiceMaintenance>("android.security.dice.IDiceMaintenance")
228 .context("IDiceMaintenance service not found")?;
229 diced
230 .demoteSelf(&[InputValues {
231 codeHash: code_hash,
232 config: Config { desc: config_desc },
233 authorityHash: authority_hash,
234 authorityDescriptor: None,
Andrew Scull65ddfc42022-02-14 21:03:58 +0000235 mode: if app_debuggable { Mode::DEBUG } else { Mode::NORMAL },
Inseob Kimb2519c52022-04-14 02:10:09 +0900236 hidden: verified_data.salt.clone().try_into().unwrap(),
Andrew Scullb2f44472022-01-21 14:41:34 +0000237 }])
238 .context("IDiceMaintenance::demoteSelf failed")?;
239 Ok(())
240}
241
Andrew Scullab72ec52022-03-14 09:10:52 +0000242fn is_strict_boot() -> bool {
243 Path::new(AVF_STRICT_BOOT).exists()
244}
245
246fn is_new_instance() -> bool {
247 Path::new(AVF_NEW_INSTANCE).exists()
248}
249
Inseob Kime379e7d2022-07-22 18:55:18 +0900250fn is_verified_boot() -> bool {
251 !Path::new(DEBUG_MICRODROID_NO_VERIFIED_BOOT).exists()
252}
253
Jooyung Han5c6d4172021-12-06 14:17:52 +0900254fn try_run_payload(service: &Strong<dyn IVirtualMachineService>) -> Result<i32> {
Jooyung Han311b1202021-09-14 22:00:16 +0900255 let metadata = load_metadata().context("Failed to load payload metadata")?;
Jooyung Han19c1d6c2021-08-06 14:08:16 +0900256
Jooyung Han311b1202021-09-14 22:00:16 +0900257 let mut instance = InstanceDisk::new().context("Failed to load instance.img")?;
Jooyung Han7a343f92021-09-08 22:53:11 +0900258 let saved_data = instance.read_microdroid_data().context("Failed to read identity data")?;
Jiyong Parkbb4a9872021-09-06 15:59:21 +0900259
Andrew Scullab72ec52022-03-14 09:10:52 +0000260 if is_strict_boot() {
261 // Provisioning must happen on the first boot and never again.
262 if is_new_instance() {
263 ensure!(
264 saved_data.is_none(),
265 MicrodroidError::InvalidConfig("Found instance data on first boot.".to_string())
266 );
267 } else {
268 ensure!(
269 saved_data.is_some(),
270 MicrodroidError::InvalidConfig("Instance data not found.".to_string())
271 );
272 };
273 }
274
Jiyong Parkbb4a9872021-09-06 15:59:21 +0900275 // Verify the payload before using it.
Inseob Kim11f40d02022-06-13 17:16:00 +0900276 let verified_data = verify_payload(&metadata, saved_data.as_ref())
277 .context("Payload verification failed")
278 .map_err(|e| MicrodroidError::PayloadVerificationFailed(e.to_string()))?;
Inseob Kime379e7d2022-07-22 18:55:18 +0900279
280 // In case identity is ignored (by debug policy), we should reuse existing payload data, even
281 // when the payload is changed. This is to keep the derived secret same as before.
282 let verified_data = if let Some(saved_data) = saved_data {
283 if !is_verified_boot() {
284 if saved_data != verified_data {
285 info!("Detected an update of the payload, but continue (regarding debug policy)")
286 }
287 } else {
288 ensure!(
289 saved_data == verified_data,
290 MicrodroidError::PayloadChanged(String::from(
291 "Detected an update of the payload which isn't supported yet."
292 ))
293 );
294 info!("Saved data is verified.");
295 }
296 saved_data
Jiyong Parkbb4a9872021-09-06 15:59:21 +0900297 } else {
Jooyung Han7a343f92021-09-08 22:53:11 +0900298 info!("Saving verified data.");
299 instance.write_microdroid_data(&verified_data).context("Failed to write identity data")?;
Inseob Kime379e7d2022-07-22 18:55:18 +0900300 verified_data
301 };
Jooyung Han19c1d6c2021-08-06 14:08:16 +0900302
Inseob Kimb2519c52022-04-14 02:10:09 +0900303 // To minimize the exposure to untrusted data, derive dice profile as soon as possible.
304 info!("DICE derivation for payload");
305 dice_derivation(&verified_data, &metadata.payload_config_path)?;
306
Jooyung Hana6d11eb2021-09-10 11:48:05 +0900307 // Before reading a file from the APK, start zipfuse
Andrew Scullcc339a12022-07-04 12:44:19 +0000308 let noexec = false;
Inseob Kim217038e2021-11-25 11:15:06 +0900309 run_zipfuse(
Andrew Scullcc339a12022-07-04 12:44:19 +0000310 noexec,
Inseob Kim217038e2021-11-25 11:15:06 +0900311 "fscontext=u:object_r:zipfusefs:s0,context=u:object_r:system_file:s0",
312 Path::new("/dev/block/mapper/microdroid-apk"),
313 Path::new("/mnt/apk"),
314 )
315 .context("Failed to run zipfuse")?;
Jiyong Park21ce2c52021-08-28 02:32:17 +0900316
Jooyung Han5c6d4172021-12-06 14:17:52 +0900317 ensure!(
318 !metadata.payload_config_path.is_empty(),
319 MicrodroidError::InvalidConfig("No payload_config_path in metadata".to_string())
320 );
Inseob Kim197748b2021-12-01 19:49:00 +0900321
Jooyung Han5c6d4172021-12-06 14:17:52 +0900322 let config = load_config(Path::new(&metadata.payload_config_path))?;
Shikha Panwar6f03c942022-04-13 20:26:50 +0000323
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
Inseob Kimcd9c1dd2022-07-13 17:13:45 +0900337 // Start tombstone_transmit if enabled
338 if config.export_tombstones {
339 system_properties::write("ctl.start", "tombstone_transmit")
340 .context("Failed to start tombstone_transmit")?;
341 } else {
342 system_properties::write("ctl.stop", "tombstoned").context("Failed to stop tombstoned")?;
343 }
344
Jooyung Han5c6d4172021-12-06 14:17:52 +0900345 ensure!(
346 config.task.is_some(),
347 MicrodroidError::InvalidConfig("No task in VM config".to_string())
348 );
Victor Hsieh8d006ba2022-05-02 09:42:02 -0700349 system_properties::write("dev.bootcomplete", "1").context("set dev.bootcomplete")?;
Jooyung Han5c6d4172021-12-06 14:17:52 +0900350 exec_task(&config.task.unwrap(), service)
Jooyung Han347d9f22021-05-28 00:05:14 +0900351}
352
Inseob Kim217038e2021-11-25 11:15:06 +0900353struct ApkDmverityArgument<'a> {
354 apk: &'a str,
355 idsig: &'a str,
356 name: &'a str,
Inseob Kim197748b2021-12-01 19:49:00 +0900357 saved_root_hash: Option<&'a RootHash>,
Inseob Kim217038e2021-11-25 11:15:06 +0900358}
359
360fn run_apkdmverity(args: &[ApkDmverityArgument]) -> Result<Child> {
361 let mut cmd = Command::new(APKDMVERITY_BIN);
362
363 cmd.stdin(Stdio::null()).stdout(Stdio::null()).stderr(Stdio::null());
364
365 for argument in args {
366 cmd.arg("--apk").arg(argument.apk).arg(argument.idsig).arg(argument.name);
Inseob Kim197748b2021-12-01 19:49:00 +0900367 if let Some(root_hash) = argument.saved_root_hash {
368 cmd.arg(&to_hex_string(root_hash));
369 } else {
370 cmd.arg("none");
371 }
Inseob Kim217038e2021-11-25 11:15:06 +0900372 }
373
374 cmd.spawn().context("Spawn apkdmverity")
375}
376
Andrew Scullcc339a12022-07-04 12:44:19 +0000377fn run_zipfuse(noexec: bool, option: &str, zip_path: &Path, mount_dir: &Path) -> Result<Child> {
378 let mut cmd = Command::new(ZIPFUSE_BIN);
379 if noexec {
380 cmd.arg("--noexec");
381 }
382 cmd.arg("-o")
Inseob Kim217038e2021-11-25 11:15:06 +0900383 .arg(option)
384 .arg(zip_path)
385 .arg(mount_dir)
386 .stdin(Stdio::null())
387 .stdout(Stdio::null())
388 .stderr(Stdio::null())
389 .spawn()
390 .context("Spawn zipfuse")
391}
392
Inseob Kime379e7d2022-07-22 18:55:18 +0900393fn write_apex_payload_data(
394 saved_data: Option<&MicrodroidData>,
395 apex_data_from_payload: &[ApexData],
396) -> Result<()> {
397 if let Some(saved_apex_data) = saved_data.map(|d| &d.apex_data) {
398 // We don't support APEX updates. (assuming that update will change root digest)
399 ensure!(
400 saved_apex_data == apex_data_from_payload,
401 MicrodroidError::PayloadChanged(String::from("APEXes have changed."))
402 );
403 let apex_metadata = to_metadata(apex_data_from_payload);
404 // Pass metadata(with public keys and root digests) to apexd so that it uses the passed
405 // metadata instead of the default one (/dev/block/by-name/payload-metadata)
406 OpenOptions::new()
407 .create_new(true)
408 .write(true)
409 .open("/apex/vm-payload-metadata")
410 .context("Failed to open /apex/vm-payload-metadata")
411 .and_then(|f| write_metadata(&apex_metadata, f))?;
412 }
413 Ok(())
414}
415
Jooyung Han7a343f92021-09-08 22:53:11 +0900416// Verify payload before executing it. For APK payload, Full verification (which is slow) is done
417// when the root_hash values from the idsig file and the instance disk are different. This function
418// returns the verified root hash (for APK payload) and pubkeys (for APEX payloads) that can be
419// saved to the instance disk.
420fn verify_payload(
421 metadata: &Metadata,
422 saved_data: Option<&MicrodroidData>,
423) -> Result<MicrodroidData> {
Jiyong Parkbb4a9872021-09-06 15:59:21 +0900424 let start_time = SystemTime::now();
425
Inseob Kim197748b2021-12-01 19:49:00 +0900426 // Verify main APK
Jooyung Han7a343f92021-09-08 22:53:11 +0900427 let root_hash = saved_data.map(|d| &d.apk_data.root_hash);
Inseob Kim197748b2021-12-01 19:49:00 +0900428 let root_hash_from_idsig = get_apk_root_hash_from_idsig(MAIN_APK_IDSIG_PATH)?;
Jiyong Parkf7dea252021-09-08 01:42:54 +0900429 let root_hash_trustful = root_hash == Some(&root_hash_from_idsig);
Jiyong Parkbb4a9872021-09-06 15:59:21 +0900430
Jiyong Parkf7dea252021-09-08 01:42:54 +0900431 // 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 +0900432 // instead of the value read from the idsig file.
Inseob Kim197748b2021-12-01 19:49:00 +0900433 let main_apk_argument = {
434 ApkDmverityArgument {
435 apk: MAIN_APK_PATH,
436 idsig: MAIN_APK_IDSIG_PATH,
437 name: MAIN_APK_DEVICE_NAME,
438 saved_root_hash: if root_hash_trustful {
439 Some(root_hash_from_idsig.as_ref())
440 } else {
441 None
442 },
443 }
444 };
445 let mut apkdmverity_arguments = vec![main_apk_argument];
446
447 // Verify extra APKs
448 // For now, we can't read the payload config, so glob APKs and idsigs.
449 // Later, we'll see if it matches with the payload config.
450
451 // sort globbed paths to match apks (extra-apk-{idx}) and idsigs (extra-idsig-{idx})
452 // e.g. "extra-apk-0" corresponds to "extra-idsig-0"
453 let extra_apks =
454 sorted(glob(EXTRA_APK_PATH_PATTERN)?.collect::<Result<Vec<_>, _>>()?).collect::<Vec<_>>();
455 let extra_idsigs =
456 sorted(glob(EXTRA_IDSIG_PATH_PATTERN)?.collect::<Result<Vec<_>, _>>()?).collect::<Vec<_>>();
457 if extra_apks.len() != extra_idsigs.len() {
458 return Err(anyhow!(
459 "Extra apks/idsigs mismatch: {} apks but {} idsigs",
460 extra_apks.len(),
461 extra_idsigs.len()
462 ));
463 }
464 let extra_apks_count = extra_apks.len();
465
466 let (extra_apk_names, extra_root_hashes_from_idsig): (Vec<_>, Vec<_>) = extra_idsigs
467 .iter()
468 .enumerate()
469 .map(|(i, extra_idsig)| {
470 (
471 format!("extra-apk-{}", i),
472 get_apk_root_hash_from_idsig(extra_idsig.to_str().unwrap())
473 .expect("Can't find root hash from extra idsig"),
474 )
475 })
476 .unzip();
477
478 let saved_extra_root_hashes: Vec<_> = saved_data
479 .map(|d| d.extra_apks_data.iter().map(|apk_data| &apk_data.root_hash).collect())
480 .unwrap_or_else(Vec::new);
481 let extra_root_hashes_trustful: Vec<_> = extra_root_hashes_from_idsig
482 .iter()
483 .enumerate()
484 .map(|(i, root_hash_from_idsig)| {
485 saved_extra_root_hashes.get(i).copied() == Some(root_hash_from_idsig)
486 })
487 .collect();
488
489 for i in 0..extra_apks_count {
490 apkdmverity_arguments.push({
491 ApkDmverityArgument {
492 apk: extra_apks[i].to_str().unwrap(),
493 idsig: extra_idsigs[i].to_str().unwrap(),
494 name: &extra_apk_names[i],
495 saved_root_hash: if extra_root_hashes_trustful[i] {
496 Some(&extra_root_hashes_from_idsig[i])
497 } else {
498 None
499 },
500 }
501 });
Jiyong Parkbb4a9872021-09-06 15:59:21 +0900502 }
503
504 // Start apkdmverity and wait for the dm-verify block
Inseob Kim197748b2021-12-01 19:49:00 +0900505 let mut apkdmverity_child = run_apkdmverity(&apkdmverity_arguments)?;
Jooyung Han7a343f92021-09-08 22:53:11 +0900506
Jooyung Hanc8deb472021-09-13 13:48:25 +0900507 // While waiting for apkdmverity to mount APK, gathers public keys and root digests from
508 // APEX payload.
Jooyung Han7a343f92021-09-08 22:53:11 +0900509 let apex_data_from_payload = get_apex_data_from_payload(metadata)?;
Inseob Kime379e7d2022-07-22 18:55:18 +0900510
511 // Writing /apex/vm-payload-metadata is to verify that the payload isn't changed.
512 // Skip writing it if the debug policy ignoring identity is on
513 if is_verified_boot() {
514 write_apex_payload_data(saved_data, &apex_data_from_payload)?;
Jooyung Han4a9b3bf2021-09-10 17:19:00 +0900515 }
Inseob Kime379e7d2022-07-22 18:55:18 +0900516
Jooyung Han4a9b3bf2021-09-10 17:19:00 +0900517 // Start apexd to activate APEXes
518 system_properties::write("ctl.start", "apexd-vm")?;
Jooyung Han7a343f92021-09-08 22:53:11 +0900519
Inseob Kim217038e2021-11-25 11:15:06 +0900520 // TODO(inseob): add timeout
521 apkdmverity_child.wait()?;
Jooyung Han19c1d6c2021-08-06 14:08:16 +0900522
Jiyong Parkf7dea252021-09-08 01:42:54 +0900523 // Do the full verification if the root_hash is un-trustful. This requires the full scanning of
Jiyong Parkbb4a9872021-09-06 15:59:21 +0900524 // 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 +0900525 // 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 +0900526 // of the VM or APK was updated in the host.
527 // TODO(jooyung): consider multithreading to make this faster
Inseob Kim197748b2021-12-01 19:49:00 +0900528 let main_apk_pubkey = get_public_key_from_apk(DM_MOUNTED_APK_PATH, root_hash_trustful)?;
529 let extra_apks_data = extra_root_hashes_from_idsig
530 .into_iter()
531 .enumerate()
532 .map(|(i, extra_root_hash)| {
533 let mount_path = format!("/dev/block/mapper/{}", &extra_apk_names[i]);
534 let apk_pubkey = get_public_key_from_apk(&mount_path, extra_root_hashes_trustful[i])?;
535 Ok(ApkData { root_hash: extra_root_hash, pubkey: apk_pubkey })
536 })
537 .collect::<Result<Vec<_>>>()?;
Jiyong Parkbb4a9872021-09-06 15:59:21 +0900538
539 info!("payload verification successful. took {:#?}", start_time.elapsed().unwrap());
540
Andrew Scull34916a72022-01-30 21:34:24 +0000541 // Use the salt from a verified instance, or generate a salt for a new instance.
542 let salt = if let Some(saved_data) = saved_data {
543 saved_data.salt.clone()
544 } else {
545 let mut salt = vec![0u8; 64];
546 salt.as_mut_slice().try_fill(&mut rand::thread_rng())?;
547 salt
548 };
549
Jiyong Parkf7dea252021-09-08 01:42:54 +0900550 // At this point, we can ensure that the root_hash from the idsig file is trusted, either by
551 // fully verifying the APK or by comparing it with the saved root_hash.
Jooyung Han7a343f92021-09-08 22:53:11 +0900552 Ok(MicrodroidData {
Andrew Scull34916a72022-01-30 21:34:24 +0000553 salt,
Inseob Kim197748b2021-12-01 19:49:00 +0900554 apk_data: ApkData { root_hash: root_hash_from_idsig, pubkey: main_apk_pubkey },
555 extra_apks_data,
Jooyung Han7a343f92021-09-08 22:53:11 +0900556 apex_data: apex_data_from_payload,
557 })
Jiyong Parkbb4a9872021-09-06 15:59:21 +0900558}
559
Inseob Kim197748b2021-12-01 19:49:00 +0900560fn mount_extra_apks(config: &VmPayloadConfig) -> Result<()> {
561 // For now, only the number of apks is important, as the mount point and dm-verity name is fixed
562 for i in 0..config.extra_apks.len() {
563 let mount_dir = format!("/mnt/extra-apk/{}", i);
564 create_dir(Path::new(&mount_dir)).context("Failed to create mount dir for extra apks")?;
565
566 // don't wait, just detach
Andrew Scullcc339a12022-07-04 12:44:19 +0000567 let noexec = true;
Inseob Kim197748b2021-12-01 19:49:00 +0900568 run_zipfuse(
Andrew Scullcc339a12022-07-04 12:44:19 +0000569 noexec,
Inseob Kim197748b2021-12-01 19:49:00 +0900570 "fscontext=u:object_r:zipfusefs:s0,context=u:object_r:extra_apk_file:s0",
571 Path::new(&format!("/dev/block/mapper/extra-apk-{}", i)),
572 Path::new(&mount_dir),
573 )
574 .context("Failed to zipfuse extra apks")?;
575 }
576
577 Ok(())
578}
579
Jiyong Parkbb4a9872021-09-06 15:59:21 +0900580// Waits until linker config is generated
581fn wait_for_apex_config_done() -> Result<()> {
582 let mut prop = PropertyWatcher::new(APEX_CONFIG_DONE_PROP)?;
583 loop {
584 prop.wait()?;
Andrew Scull4b5369f2022-01-28 13:47:20 +0000585 if system_properties::read_bool(APEX_CONFIG_DONE_PROP, false)? {
Jiyong Parkbb4a9872021-09-06 15:59:21 +0900586 break;
587 }
588 }
Jooyung Han19c1d6c2021-08-06 14:08:16 +0900589 Ok(())
590}
591
Inseob Kim197748b2021-12-01 19:49:00 +0900592fn get_apk_root_hash_from_idsig(path: &str) -> Result<Box<RootHash>> {
593 let mut idsig = File::open(path)?;
Jiyong Park21ce2c52021-08-28 02:32:17 +0900594 let idsig = V4Signature::from(&mut idsig)?;
595 Ok(idsig.hashing_info.raw_root_hash)
596}
597
Inseob Kim197748b2021-12-01 19:49:00 +0900598fn get_public_key_from_apk(apk: &str, root_hash_trustful: bool) -> Result<Box<[u8]>> {
599 if !root_hash_trustful {
600 verify(apk).context(MicrodroidError::PayloadVerificationFailed(format!(
601 "failed to verify {}",
602 apk
603 )))
604 } else {
605 get_public_key_der(apk)
606 }
607}
608
Jooyung Han634e2d72021-06-10 16:27:38 +0900609fn load_config(path: &Path) -> Result<VmPayloadConfig> {
610 info!("loading config from {:?}...", path);
611 let file = ioutil::wait_for_file(path, WAIT_TIMEOUT)?;
612 Ok(serde_json::from_reader(file)?)
613}
614
Jiyong Park202856e2022-08-22 16:04:26 +0900615/// Loads the crashkernel into memory using kexec if the VM is loaded with `crashkernel=' parameter
616/// in the cmdline.
617fn load_crashkernel_if_supported() -> Result<()> {
618 let supported = std::fs::read_to_string("/proc/cmdline")?.contains(" crashkernel=");
619 info!("ramdump supported: {}", supported);
620 if supported {
621 let status = Command::new("/system/bin/kexec_load").status()?;
622 if !status.success() {
623 return Err(anyhow!("Failed to load crashkernel: {:?}", status));
624 }
625 }
626 Ok(())
627}
628
Jiyong Park8611a6c2021-07-09 18:17:44 +0900629/// Executes the given task. Stdout of the task is piped into the vsock stream to the
630/// virtualizationservice in the host side.
Jooyung Han5c6d4172021-12-06 14:17:52 +0900631fn exec_task(task: &Task, service: &Strong<dyn IVirtualMachineService>) -> Result<i32> {
Jiyong Park8611a6c2021-07-09 18:17:44 +0900632 info!("executing main task {:?}...", task);
Inseob Kim86ca0162021-10-20 02:21:02 +0000633 let mut command = build_command(task)?;
Inseob Kim7f61fe72021-08-20 20:50:47 +0900634
635 info!("notifying payload started");
Inseob Kimc7d28c72021-10-25 14:28:10 +0000636 service.notifyPayloadStarted()?;
Inseob Kim7f61fe72021-08-20 20:50:47 +0900637
Inseob Kim86ca0162021-10-20 02:21:02 +0000638 let exit_status = command.spawn()?.wait()?;
Jooyung Han5c6d4172021-12-06 14:17:52 +0900639 exit_status.code().ok_or_else(|| anyhow!("Failed to get exit_code from the paylaod."))
Jooyung Han347d9f22021-05-28 00:05:14 +0900640}
Jooyung Han634e2d72021-06-10 16:27:38 +0900641
642fn build_command(task: &Task) -> Result<Command> {
Inseob Kim7f61fe72021-08-20 20:50:47 +0900643 const VMADDR_CID_HOST: u32 = 2;
Inseob Kim7f61fe72021-08-20 20:50:47 +0900644
645 let mut command = match task.type_ {
Jooyung Han634e2d72021-06-10 16:27:38 +0900646 TaskType::Executable => {
647 let mut command = Command::new(&task.command);
648 command.args(&task.args);
649 command
650 }
651 TaskType::MicrodroidLauncher => {
652 let mut command = Command::new("/system/bin/microdroid_launcher");
653 command.arg(find_library_path(&task.command)?).args(&task.args);
654 command
655 }
Inseob Kim7f61fe72021-08-20 20:50:47 +0900656 };
657
Inseob Kimd0587562021-09-01 21:27:32 +0900658 match VsockStream::connect_with_cid_port(VMADDR_CID_HOST, VM_STREAM_SERVICE_PORT as u32) {
Inseob Kim7f61fe72021-08-20 20:50:47 +0900659 Ok(stream) => {
660 // SAFETY: the ownership of the underlying file descriptor is transferred from stream
661 // to the file object, and then into the Command object. When the command is finished,
662 // the file descriptor is closed.
663 let file = unsafe { File::from_raw_fd(stream.into_raw_fd()) };
664 command
665 .stdin(Stdio::from(file.try_clone()?))
666 .stdout(Stdio::from(file.try_clone()?))
667 .stderr(Stdio::from(file));
668 }
669 Err(e) => {
670 error!("failed to connect to virtualization service: {}", e);
671 // Don't fail hard here. Even if we failed to connect to the virtualizationservice,
672 // we keep executing the task. This can happen if the owner of the VM doesn't register
673 // callback to accept the stream. Use /dev/null as the stream so that the task can
674 // make progress without waiting for someone to consume the output.
675 command.stdin(Stdio::null()).stdout(Stdio::null()).stderr(Stdio::null());
676 }
677 }
678
679 Ok(command)
Jooyung Han634e2d72021-06-10 16:27:38 +0900680}
681
682fn find_library_path(name: &str) -> Result<String> {
683 let mut watcher = PropertyWatcher::new("ro.product.cpu.abilist")?;
684 let value = watcher.read(|_name, value| Ok(value.trim().to_string()))?;
685 let abi = value.split(',').next().ok_or_else(|| anyhow!("no abilist"))?;
686 let path = format!("/mnt/apk/lib/{}/{}", abi, name);
687
688 let metadata = fs::metadata(&path)?;
689 if !metadata.is_file() {
690 bail!("{} is not a file", &path);
691 }
692
693 Ok(path)
694}
Jiyong Park21ce2c52021-08-28 02:32:17 +0900695
696fn to_hex_string(buf: &[u8]) -> String {
697 buf.iter().map(|b| format!("{:02X}", b)).collect()
698}