blob: c226e9dcfca1c6b9cc0c5d09ae8f9c72201599bc [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};
Andrew Scullb2f44472022-01-21 14:41:34 +000029use diced_utils::cbor::encode_header;
Inseob Kim197748b2021-12-01 19:49:00 +090030use glob::glob;
Jiyong Park21ce2c52021-08-28 02:32:17 +090031use idsig::V4Signature;
Inseob Kim197748b2021-12-01 19:49:00 +090032use itertools::sorted;
Andrew Scull684590f2022-01-27 16:14:26 +000033use log::{error, info};
Jooyung Han4a9b3bf2021-09-10 17:19:00 +090034use microdroid_metadata::{write_metadata, Metadata};
Jooyung Han634e2d72021-06-10 16:27:38 +090035use microdroid_payload_config::{Task, TaskType, VmPayloadConfig};
Andrew Sculla0d1b1a2022-05-24 19:32:47 +000036use openssl::sha::Sha512;
Jooyung Han4a9b3bf2021-09-10 17:19:00 +090037use payload::{get_apex_data_from_payload, load_metadata, to_metadata};
Andrew Scull34916a72022-01-30 21:34:24 +000038use rand::Fill;
Andrew Walbran7eb5ca42022-08-08 15:33:34 +000039use rpcbinder::get_vsock_rpc_interface;
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 Walbran7eb5ca42022-08-08 15:33:34 +0000144 get_vsock_rpc_interface(VMADDR_CID_HOST, VM_BINDER_SERVICE_PORT as u32)
Andrew Walbranc4ce7872022-07-29 11:26:41 +0000145 .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
Inseob Kim11f40d02022-06-13 17:16:00 +0900169 let service = get_vms_rpc_binder()
170 .context("cannot connect to VirtualMachineService")
171 .map_err(|e| MicrodroidError::FailedToConnectToVirtualizationService(e.to_string()))?;
Jooyung Han5c6d4172021-12-06 14:17:52 +0900172 match try_run_payload(&service) {
173 Ok(code) => {
174 info!("notifying payload finished");
175 service.notifyPayloadFinished(code)?;
176 if code == 0 {
177 info!("task successfully finished");
178 } else {
179 error!("task exited with exit code: {}", code);
180 }
181 Ok(())
182 }
183 Err(err) => {
Jooyung Han5c6d4172021-12-06 14:17:52 +0900184 let (error_code, message) = translate_error(&err);
185 service.notifyError(error_code, &message)?;
186 Err(err)
187 }
Jooyung Handd0a1732021-11-23 15:26:20 +0900188 }
189}
190
Inseob Kimb2519c52022-04-14 02:10:09 +0900191fn dice_derivation(verified_data: &MicrodroidData, payload_config_path: &str) -> Result<()> {
Andrew Scullb2f44472022-01-21 14:41:34 +0000192 // Calculate compound digests of code and authorities
Andrew Sculla0d1b1a2022-05-24 19:32:47 +0000193 let mut code_hash_ctx = Sha512::new();
194 let mut authority_hash_ctx = Sha512::new();
Andrew Scullb2f44472022-01-21 14:41:34 +0000195 code_hash_ctx.update(verified_data.apk_data.root_hash.as_ref());
196 authority_hash_ctx.update(verified_data.apk_data.pubkey.as_ref());
Inseob Kimb2519c52022-04-14 02:10:09 +0900197 for extra_apk in &verified_data.extra_apks_data {
Andrew Scullb2f44472022-01-21 14:41:34 +0000198 code_hash_ctx.update(extra_apk.root_hash.as_ref());
199 authority_hash_ctx.update(extra_apk.pubkey.as_ref());
200 }
Inseob Kimb2519c52022-04-14 02:10:09 +0900201 for apex in &verified_data.apex_data {
Andrew Scullb2f44472022-01-21 14:41:34 +0000202 code_hash_ctx.update(apex.root_digest.as_ref());
203 authority_hash_ctx.update(apex.public_key.as_ref());
204 }
Andrew Sculla0d1b1a2022-05-24 19:32:47 +0000205 let code_hash = code_hash_ctx.finish();
206 let authority_hash = authority_hash_ctx.finish();
Andrew Scullb2f44472022-01-21 14:41:34 +0000207
208 // {
209 // -70002: "Microdroid payload",
210 // -71000: payload_config_path
211 // }
212 let mut config_desc = vec![
213 0xa2, 0x3a, 0x00, 0x01, 0x11, 0x71, 0x72, 0x4d, 0x69, 0x63, 0x72, 0x6f, 0x64, 0x72, 0x6f,
214 0x69, 0x64, 0x20, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x3a, 0x00, 0x01, 0x15, 0x57,
215 ];
216 let config_path_bytes = payload_config_path.as_bytes();
217 encode_header(3, config_path_bytes.len().try_into().unwrap(), &mut config_desc)?;
218 config_desc.extend_from_slice(config_path_bytes);
219
Andrew Scull65ddfc42022-02-14 21:03:58 +0000220 // Check app debuggability, conervatively assuming it is debuggable
221 let app_debuggable = system_properties::read_bool(APP_DEBUGGABLE_PROP, true)?;
222
Andrew Scullb2f44472022-01-21 14:41:34 +0000223 // Send the details to diced
224 let diced =
225 wait_for_interface::<dyn IDiceMaintenance>("android.security.dice.IDiceMaintenance")
226 .context("IDiceMaintenance service not found")?;
227 diced
228 .demoteSelf(&[InputValues {
229 codeHash: code_hash,
230 config: Config { desc: config_desc },
231 authorityHash: authority_hash,
232 authorityDescriptor: None,
Andrew Scull65ddfc42022-02-14 21:03:58 +0000233 mode: if app_debuggable { Mode::DEBUG } else { Mode::NORMAL },
Inseob Kimb2519c52022-04-14 02:10:09 +0900234 hidden: verified_data.salt.clone().try_into().unwrap(),
Andrew Scullb2f44472022-01-21 14:41:34 +0000235 }])
236 .context("IDiceMaintenance::demoteSelf failed")?;
237 Ok(())
238}
239
Andrew Scullab72ec52022-03-14 09:10:52 +0000240fn is_strict_boot() -> bool {
241 Path::new(AVF_STRICT_BOOT).exists()
242}
243
244fn is_new_instance() -> bool {
245 Path::new(AVF_NEW_INSTANCE).exists()
246}
247
Inseob Kime379e7d2022-07-22 18:55:18 +0900248fn is_verified_boot() -> bool {
249 !Path::new(DEBUG_MICRODROID_NO_VERIFIED_BOOT).exists()
250}
251
Jooyung Han5c6d4172021-12-06 14:17:52 +0900252fn try_run_payload(service: &Strong<dyn IVirtualMachineService>) -> Result<i32> {
Jooyung Han311b1202021-09-14 22:00:16 +0900253 let metadata = load_metadata().context("Failed to load payload metadata")?;
Jooyung Han19c1d6c2021-08-06 14:08:16 +0900254
Jooyung Han311b1202021-09-14 22:00:16 +0900255 let mut instance = InstanceDisk::new().context("Failed to load instance.img")?;
Jooyung Han7a343f92021-09-08 22:53:11 +0900256 let saved_data = instance.read_microdroid_data().context("Failed to read identity data")?;
Jiyong Parkbb4a9872021-09-06 15:59:21 +0900257
Andrew Scullab72ec52022-03-14 09:10:52 +0000258 if is_strict_boot() {
259 // Provisioning must happen on the first boot and never again.
260 if is_new_instance() {
261 ensure!(
262 saved_data.is_none(),
263 MicrodroidError::InvalidConfig("Found instance data on first boot.".to_string())
264 );
265 } else {
266 ensure!(
267 saved_data.is_some(),
268 MicrodroidError::InvalidConfig("Instance data not found.".to_string())
269 );
270 };
271 }
272
Jiyong Parkbb4a9872021-09-06 15:59:21 +0900273 // Verify the payload before using it.
Inseob Kim11f40d02022-06-13 17:16:00 +0900274 let verified_data = verify_payload(&metadata, saved_data.as_ref())
275 .context("Payload verification failed")
276 .map_err(|e| MicrodroidError::PayloadVerificationFailed(e.to_string()))?;
Inseob Kime379e7d2022-07-22 18:55:18 +0900277
278 // In case identity is ignored (by debug policy), we should reuse existing payload data, even
279 // when the payload is changed. This is to keep the derived secret same as before.
280 let verified_data = if let Some(saved_data) = saved_data {
281 if !is_verified_boot() {
282 if saved_data != verified_data {
283 info!("Detected an update of the payload, but continue (regarding debug policy)")
284 }
285 } else {
286 ensure!(
287 saved_data == verified_data,
288 MicrodroidError::PayloadChanged(String::from(
289 "Detected an update of the payload which isn't supported yet."
290 ))
291 );
292 info!("Saved data is verified.");
293 }
294 saved_data
Jiyong Parkbb4a9872021-09-06 15:59:21 +0900295 } else {
Jooyung Han7a343f92021-09-08 22:53:11 +0900296 info!("Saving verified data.");
297 instance.write_microdroid_data(&verified_data).context("Failed to write identity data")?;
Inseob Kime379e7d2022-07-22 18:55:18 +0900298 verified_data
299 };
Jooyung Han19c1d6c2021-08-06 14:08:16 +0900300
Inseob Kimb2519c52022-04-14 02:10:09 +0900301 // To minimize the exposure to untrusted data, derive dice profile as soon as possible.
302 info!("DICE derivation for payload");
303 dice_derivation(&verified_data, &metadata.payload_config_path)?;
304
Jooyung Hana6d11eb2021-09-10 11:48:05 +0900305 // Before reading a file from the APK, start zipfuse
Andrew Scullcc339a12022-07-04 12:44:19 +0000306 let noexec = false;
Inseob Kim217038e2021-11-25 11:15:06 +0900307 run_zipfuse(
Andrew Scullcc339a12022-07-04 12:44:19 +0000308 noexec,
Inseob Kim217038e2021-11-25 11:15:06 +0900309 "fscontext=u:object_r:zipfusefs:s0,context=u:object_r:system_file:s0",
310 Path::new("/dev/block/mapper/microdroid-apk"),
311 Path::new("/mnt/apk"),
312 )
313 .context("Failed to run zipfuse")?;
Jiyong Park21ce2c52021-08-28 02:32:17 +0900314
Jooyung Han5c6d4172021-12-06 14:17:52 +0900315 ensure!(
316 !metadata.payload_config_path.is_empty(),
317 MicrodroidError::InvalidConfig("No payload_config_path in metadata".to_string())
318 );
Inseob Kim197748b2021-12-01 19:49:00 +0900319
Jooyung Han5c6d4172021-12-06 14:17:52 +0900320 let config = load_config(Path::new(&metadata.payload_config_path))?;
Shikha Panwar6f03c942022-04-13 20:26:50 +0000321
Inseob Kim197748b2021-12-01 19:49:00 +0900322 if config.extra_apks.len() != verified_data.extra_apks_data.len() {
323 return Err(anyhow!(
324 "config expects {} extra apks, but found only {}",
325 config.extra_apks.len(),
326 verified_data.extra_apks_data.len()
327 ));
328 }
329 mount_extra_apks(&config)?;
Jooyung Han634e2d72021-06-10 16:27:38 +0900330
Jooyung Han5c6d4172021-12-06 14:17:52 +0900331 // Wait until apex config is done. (e.g. linker configuration for apexes)
332 // TODO(jooyung): wait until sys.boot_completed?
333 wait_for_apex_config_done()?;
334
Inseob Kimcd9c1dd2022-07-13 17:13:45 +0900335 // Start tombstone_transmit if enabled
336 if config.export_tombstones {
337 system_properties::write("ctl.start", "tombstone_transmit")
338 .context("Failed to start tombstone_transmit")?;
339 } else {
340 system_properties::write("ctl.stop", "tombstoned").context("Failed to stop tombstoned")?;
341 }
342
Jooyung Han5c6d4172021-12-06 14:17:52 +0900343 ensure!(
344 config.task.is_some(),
345 MicrodroidError::InvalidConfig("No task in VM config".to_string())
346 );
Victor Hsieh8d006ba2022-05-02 09:42:02 -0700347 system_properties::write("dev.bootcomplete", "1").context("set dev.bootcomplete")?;
Jooyung Han5c6d4172021-12-06 14:17:52 +0900348 exec_task(&config.task.unwrap(), service)
Jooyung Han347d9f22021-05-28 00:05:14 +0900349}
350
Inseob Kim217038e2021-11-25 11:15:06 +0900351struct ApkDmverityArgument<'a> {
352 apk: &'a str,
353 idsig: &'a str,
354 name: &'a str,
Inseob Kim197748b2021-12-01 19:49:00 +0900355 saved_root_hash: Option<&'a RootHash>,
Inseob Kim217038e2021-11-25 11:15:06 +0900356}
357
358fn run_apkdmverity(args: &[ApkDmverityArgument]) -> Result<Child> {
359 let mut cmd = Command::new(APKDMVERITY_BIN);
360
361 cmd.stdin(Stdio::null()).stdout(Stdio::null()).stderr(Stdio::null());
362
363 for argument in args {
364 cmd.arg("--apk").arg(argument.apk).arg(argument.idsig).arg(argument.name);
Inseob Kim197748b2021-12-01 19:49:00 +0900365 if let Some(root_hash) = argument.saved_root_hash {
366 cmd.arg(&to_hex_string(root_hash));
367 } else {
368 cmd.arg("none");
369 }
Inseob Kim217038e2021-11-25 11:15:06 +0900370 }
371
372 cmd.spawn().context("Spawn apkdmverity")
373}
374
Andrew Scullcc339a12022-07-04 12:44:19 +0000375fn run_zipfuse(noexec: bool, option: &str, zip_path: &Path, mount_dir: &Path) -> Result<Child> {
376 let mut cmd = Command::new(ZIPFUSE_BIN);
377 if noexec {
378 cmd.arg("--noexec");
379 }
380 cmd.arg("-o")
Inseob Kim217038e2021-11-25 11:15:06 +0900381 .arg(option)
382 .arg(zip_path)
383 .arg(mount_dir)
384 .stdin(Stdio::null())
385 .stdout(Stdio::null())
386 .stderr(Stdio::null())
387 .spawn()
388 .context("Spawn zipfuse")
389}
390
Inseob Kime379e7d2022-07-22 18:55:18 +0900391fn write_apex_payload_data(
392 saved_data: Option<&MicrodroidData>,
393 apex_data_from_payload: &[ApexData],
394) -> Result<()> {
395 if let Some(saved_apex_data) = saved_data.map(|d| &d.apex_data) {
396 // We don't support APEX updates. (assuming that update will change root digest)
397 ensure!(
398 saved_apex_data == apex_data_from_payload,
399 MicrodroidError::PayloadChanged(String::from("APEXes have changed."))
400 );
401 let apex_metadata = to_metadata(apex_data_from_payload);
402 // Pass metadata(with public keys and root digests) to apexd so that it uses the passed
403 // metadata instead of the default one (/dev/block/by-name/payload-metadata)
404 OpenOptions::new()
405 .create_new(true)
406 .write(true)
407 .open("/apex/vm-payload-metadata")
408 .context("Failed to open /apex/vm-payload-metadata")
409 .and_then(|f| write_metadata(&apex_metadata, f))?;
410 }
411 Ok(())
412}
413
Jooyung Han7a343f92021-09-08 22:53:11 +0900414// Verify payload before executing it. For APK payload, Full verification (which is slow) is done
415// when the root_hash values from the idsig file and the instance disk are different. This function
416// returns the verified root hash (for APK payload) and pubkeys (for APEX payloads) that can be
417// saved to the instance disk.
418fn verify_payload(
419 metadata: &Metadata,
420 saved_data: Option<&MicrodroidData>,
421) -> Result<MicrodroidData> {
Jiyong Parkbb4a9872021-09-06 15:59:21 +0900422 let start_time = SystemTime::now();
423
Inseob Kim197748b2021-12-01 19:49:00 +0900424 // Verify main APK
Jooyung Han7a343f92021-09-08 22:53:11 +0900425 let root_hash = saved_data.map(|d| &d.apk_data.root_hash);
Inseob Kim197748b2021-12-01 19:49:00 +0900426 let root_hash_from_idsig = get_apk_root_hash_from_idsig(MAIN_APK_IDSIG_PATH)?;
Jiyong Parkf7dea252021-09-08 01:42:54 +0900427 let root_hash_trustful = root_hash == Some(&root_hash_from_idsig);
Jiyong Parkbb4a9872021-09-06 15:59:21 +0900428
Jiyong Parkf7dea252021-09-08 01:42:54 +0900429 // 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 +0900430 // instead of the value read from the idsig file.
Inseob Kim197748b2021-12-01 19:49:00 +0900431 let main_apk_argument = {
432 ApkDmverityArgument {
433 apk: MAIN_APK_PATH,
434 idsig: MAIN_APK_IDSIG_PATH,
435 name: MAIN_APK_DEVICE_NAME,
436 saved_root_hash: if root_hash_trustful {
437 Some(root_hash_from_idsig.as_ref())
438 } else {
439 None
440 },
441 }
442 };
443 let mut apkdmverity_arguments = vec![main_apk_argument];
444
445 // Verify extra APKs
446 // For now, we can't read the payload config, so glob APKs and idsigs.
447 // Later, we'll see if it matches with the payload config.
448
449 // sort globbed paths to match apks (extra-apk-{idx}) and idsigs (extra-idsig-{idx})
450 // e.g. "extra-apk-0" corresponds to "extra-idsig-0"
451 let extra_apks =
452 sorted(glob(EXTRA_APK_PATH_PATTERN)?.collect::<Result<Vec<_>, _>>()?).collect::<Vec<_>>();
453 let extra_idsigs =
454 sorted(glob(EXTRA_IDSIG_PATH_PATTERN)?.collect::<Result<Vec<_>, _>>()?).collect::<Vec<_>>();
455 if extra_apks.len() != extra_idsigs.len() {
456 return Err(anyhow!(
457 "Extra apks/idsigs mismatch: {} apks but {} idsigs",
458 extra_apks.len(),
459 extra_idsigs.len()
460 ));
461 }
462 let extra_apks_count = extra_apks.len();
463
464 let (extra_apk_names, extra_root_hashes_from_idsig): (Vec<_>, Vec<_>) = extra_idsigs
465 .iter()
466 .enumerate()
467 .map(|(i, extra_idsig)| {
468 (
469 format!("extra-apk-{}", i),
470 get_apk_root_hash_from_idsig(extra_idsig.to_str().unwrap())
471 .expect("Can't find root hash from extra idsig"),
472 )
473 })
474 .unzip();
475
476 let saved_extra_root_hashes: Vec<_> = saved_data
477 .map(|d| d.extra_apks_data.iter().map(|apk_data| &apk_data.root_hash).collect())
478 .unwrap_or_else(Vec::new);
479 let extra_root_hashes_trustful: Vec<_> = extra_root_hashes_from_idsig
480 .iter()
481 .enumerate()
482 .map(|(i, root_hash_from_idsig)| {
483 saved_extra_root_hashes.get(i).copied() == Some(root_hash_from_idsig)
484 })
485 .collect();
486
487 for i in 0..extra_apks_count {
488 apkdmverity_arguments.push({
489 ApkDmverityArgument {
490 apk: extra_apks[i].to_str().unwrap(),
491 idsig: extra_idsigs[i].to_str().unwrap(),
492 name: &extra_apk_names[i],
493 saved_root_hash: if extra_root_hashes_trustful[i] {
494 Some(&extra_root_hashes_from_idsig[i])
495 } else {
496 None
497 },
498 }
499 });
Jiyong Parkbb4a9872021-09-06 15:59:21 +0900500 }
501
502 // Start apkdmverity and wait for the dm-verify block
Inseob Kim197748b2021-12-01 19:49:00 +0900503 let mut apkdmverity_child = run_apkdmverity(&apkdmverity_arguments)?;
Jooyung Han7a343f92021-09-08 22:53:11 +0900504
Jooyung Hanc8deb472021-09-13 13:48:25 +0900505 // While waiting for apkdmverity to mount APK, gathers public keys and root digests from
506 // APEX payload.
Jooyung Han7a343f92021-09-08 22:53:11 +0900507 let apex_data_from_payload = get_apex_data_from_payload(metadata)?;
Inseob Kime379e7d2022-07-22 18:55:18 +0900508
509 // Writing /apex/vm-payload-metadata is to verify that the payload isn't changed.
510 // Skip writing it if the debug policy ignoring identity is on
511 if is_verified_boot() {
512 write_apex_payload_data(saved_data, &apex_data_from_payload)?;
Jooyung Han4a9b3bf2021-09-10 17:19:00 +0900513 }
Inseob Kime379e7d2022-07-22 18:55:18 +0900514
Jooyung Han4a9b3bf2021-09-10 17:19:00 +0900515 // Start apexd to activate APEXes
516 system_properties::write("ctl.start", "apexd-vm")?;
Jooyung Han7a343f92021-09-08 22:53:11 +0900517
Inseob Kim217038e2021-11-25 11:15:06 +0900518 // TODO(inseob): add timeout
519 apkdmverity_child.wait()?;
Jooyung Han19c1d6c2021-08-06 14:08:16 +0900520
Jiyong Parkf7dea252021-09-08 01:42:54 +0900521 // Do the full verification if the root_hash is un-trustful. This requires the full scanning of
Jiyong Parkbb4a9872021-09-06 15:59:21 +0900522 // 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 +0900523 // 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 +0900524 // of the VM or APK was updated in the host.
525 // TODO(jooyung): consider multithreading to make this faster
Inseob Kim197748b2021-12-01 19:49:00 +0900526 let main_apk_pubkey = get_public_key_from_apk(DM_MOUNTED_APK_PATH, root_hash_trustful)?;
527 let extra_apks_data = extra_root_hashes_from_idsig
528 .into_iter()
529 .enumerate()
530 .map(|(i, extra_root_hash)| {
531 let mount_path = format!("/dev/block/mapper/{}", &extra_apk_names[i]);
532 let apk_pubkey = get_public_key_from_apk(&mount_path, extra_root_hashes_trustful[i])?;
533 Ok(ApkData { root_hash: extra_root_hash, pubkey: apk_pubkey })
534 })
535 .collect::<Result<Vec<_>>>()?;
Jiyong Parkbb4a9872021-09-06 15:59:21 +0900536
537 info!("payload verification successful. took {:#?}", start_time.elapsed().unwrap());
538
Andrew Scull34916a72022-01-30 21:34:24 +0000539 // Use the salt from a verified instance, or generate a salt for a new instance.
540 let salt = if let Some(saved_data) = saved_data {
541 saved_data.salt.clone()
542 } else {
543 let mut salt = vec![0u8; 64];
544 salt.as_mut_slice().try_fill(&mut rand::thread_rng())?;
545 salt
546 };
547
Jiyong Parkf7dea252021-09-08 01:42:54 +0900548 // At this point, we can ensure that the root_hash from the idsig file is trusted, either by
549 // fully verifying the APK or by comparing it with the saved root_hash.
Jooyung Han7a343f92021-09-08 22:53:11 +0900550 Ok(MicrodroidData {
Andrew Scull34916a72022-01-30 21:34:24 +0000551 salt,
Inseob Kim197748b2021-12-01 19:49:00 +0900552 apk_data: ApkData { root_hash: root_hash_from_idsig, pubkey: main_apk_pubkey },
553 extra_apks_data,
Jooyung Han7a343f92021-09-08 22:53:11 +0900554 apex_data: apex_data_from_payload,
555 })
Jiyong Parkbb4a9872021-09-06 15:59:21 +0900556}
557
Inseob Kim197748b2021-12-01 19:49:00 +0900558fn mount_extra_apks(config: &VmPayloadConfig) -> Result<()> {
559 // For now, only the number of apks is important, as the mount point and dm-verity name is fixed
560 for i in 0..config.extra_apks.len() {
561 let mount_dir = format!("/mnt/extra-apk/{}", i);
562 create_dir(Path::new(&mount_dir)).context("Failed to create mount dir for extra apks")?;
563
564 // don't wait, just detach
Andrew Scullcc339a12022-07-04 12:44:19 +0000565 let noexec = true;
Inseob Kim197748b2021-12-01 19:49:00 +0900566 run_zipfuse(
Andrew Scullcc339a12022-07-04 12:44:19 +0000567 noexec,
Inseob Kim197748b2021-12-01 19:49:00 +0900568 "fscontext=u:object_r:zipfusefs:s0,context=u:object_r:extra_apk_file:s0",
569 Path::new(&format!("/dev/block/mapper/extra-apk-{}", i)),
570 Path::new(&mount_dir),
571 )
572 .context("Failed to zipfuse extra apks")?;
573 }
574
575 Ok(())
576}
577
Jiyong Parkbb4a9872021-09-06 15:59:21 +0900578// Waits until linker config is generated
579fn wait_for_apex_config_done() -> Result<()> {
580 let mut prop = PropertyWatcher::new(APEX_CONFIG_DONE_PROP)?;
581 loop {
582 prop.wait()?;
Andrew Scull4b5369f2022-01-28 13:47:20 +0000583 if system_properties::read_bool(APEX_CONFIG_DONE_PROP, false)? {
Jiyong Parkbb4a9872021-09-06 15:59:21 +0900584 break;
585 }
586 }
Jooyung Han19c1d6c2021-08-06 14:08:16 +0900587 Ok(())
588}
589
Inseob Kim197748b2021-12-01 19:49:00 +0900590fn get_apk_root_hash_from_idsig(path: &str) -> Result<Box<RootHash>> {
591 let mut idsig = File::open(path)?;
Jiyong Park21ce2c52021-08-28 02:32:17 +0900592 let idsig = V4Signature::from(&mut idsig)?;
593 Ok(idsig.hashing_info.raw_root_hash)
594}
595
Inseob Kim197748b2021-12-01 19:49:00 +0900596fn get_public_key_from_apk(apk: &str, root_hash_trustful: bool) -> Result<Box<[u8]>> {
597 if !root_hash_trustful {
598 verify(apk).context(MicrodroidError::PayloadVerificationFailed(format!(
599 "failed to verify {}",
600 apk
601 )))
602 } else {
603 get_public_key_der(apk)
604 }
605}
606
Jooyung Han634e2d72021-06-10 16:27:38 +0900607fn load_config(path: &Path) -> Result<VmPayloadConfig> {
608 info!("loading config from {:?}...", path);
609 let file = ioutil::wait_for_file(path, WAIT_TIMEOUT)?;
610 Ok(serde_json::from_reader(file)?)
611}
612
Jiyong Park8611a6c2021-07-09 18:17:44 +0900613/// Executes the given task. Stdout of the task is piped into the vsock stream to the
614/// virtualizationservice in the host side.
Jooyung Han5c6d4172021-12-06 14:17:52 +0900615fn exec_task(task: &Task, service: &Strong<dyn IVirtualMachineService>) -> Result<i32> {
Jiyong Park8611a6c2021-07-09 18:17:44 +0900616 info!("executing main task {:?}...", task);
Inseob Kim86ca0162021-10-20 02:21:02 +0000617 let mut command = build_command(task)?;
Inseob Kim7f61fe72021-08-20 20:50:47 +0900618
619 info!("notifying payload started");
Inseob Kimc7d28c72021-10-25 14:28:10 +0000620 service.notifyPayloadStarted()?;
Inseob Kim7f61fe72021-08-20 20:50:47 +0900621
Inseob Kim86ca0162021-10-20 02:21:02 +0000622 let exit_status = command.spawn()?.wait()?;
Jooyung Han5c6d4172021-12-06 14:17:52 +0900623 exit_status.code().ok_or_else(|| anyhow!("Failed to get exit_code from the paylaod."))
Jooyung Han347d9f22021-05-28 00:05:14 +0900624}
Jooyung Han634e2d72021-06-10 16:27:38 +0900625
626fn build_command(task: &Task) -> Result<Command> {
Inseob Kim7f61fe72021-08-20 20:50:47 +0900627 const VMADDR_CID_HOST: u32 = 2;
Inseob Kim7f61fe72021-08-20 20:50:47 +0900628
629 let mut command = match task.type_ {
Jooyung Han634e2d72021-06-10 16:27:38 +0900630 TaskType::Executable => {
631 let mut command = Command::new(&task.command);
632 command.args(&task.args);
633 command
634 }
635 TaskType::MicrodroidLauncher => {
636 let mut command = Command::new("/system/bin/microdroid_launcher");
637 command.arg(find_library_path(&task.command)?).args(&task.args);
638 command
639 }
Inseob Kim7f61fe72021-08-20 20:50:47 +0900640 };
641
Inseob Kimd0587562021-09-01 21:27:32 +0900642 match VsockStream::connect_with_cid_port(VMADDR_CID_HOST, VM_STREAM_SERVICE_PORT as u32) {
Inseob Kim7f61fe72021-08-20 20:50:47 +0900643 Ok(stream) => {
644 // SAFETY: the ownership of the underlying file descriptor is transferred from stream
645 // to the file object, and then into the Command object. When the command is finished,
646 // the file descriptor is closed.
647 let file = unsafe { File::from_raw_fd(stream.into_raw_fd()) };
648 command
649 .stdin(Stdio::from(file.try_clone()?))
650 .stdout(Stdio::from(file.try_clone()?))
651 .stderr(Stdio::from(file));
652 }
653 Err(e) => {
654 error!("failed to connect to virtualization service: {}", e);
655 // Don't fail hard here. Even if we failed to connect to the virtualizationservice,
656 // we keep executing the task. This can happen if the owner of the VM doesn't register
657 // callback to accept the stream. Use /dev/null as the stream so that the task can
658 // make progress without waiting for someone to consume the output.
659 command.stdin(Stdio::null()).stdout(Stdio::null()).stderr(Stdio::null());
660 }
661 }
662
663 Ok(command)
Jooyung Han634e2d72021-06-10 16:27:38 +0900664}
665
666fn find_library_path(name: &str) -> Result<String> {
667 let mut watcher = PropertyWatcher::new("ro.product.cpu.abilist")?;
668 let value = watcher.read(|_name, value| Ok(value.trim().to_string()))?;
669 let abi = value.split(',').next().ok_or_else(|| anyhow!("no abilist"))?;
670 let path = format!("/mnt/apk/lib/{}/{}", abi, name);
671
672 let metadata = fs::metadata(&path)?;
673 if !metadata.is_file() {
674 bail!("{} is not a file", &path);
675 }
676
677 Ok(path)
678}
Jiyong Park21ce2c52021-08-28 02:32:17 +0900679
680fn to_hex_string(buf: &[u8]) -> String {
681 buf.iter().map(|b| format!("{:02X}", b)).collect()
682}