blob: 064933d8bdc6be82c2325ea019714c9709f88ce6 [file] [log] [blame]
Jooyung Han347d9f22021-05-28 00:05:14 +09001// Copyright 2021, The Android Open Source Project
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15//! Microdroid Manager
16
Jiyong Park21ce2c52021-08-28 02:32:17 +090017mod instance;
Jooyung Hanf48ceb42021-06-01 18:00:04 +090018mod ioutil;
Jooyung Han7a343f92021-09-08 22:53:11 +090019mod payload;
Jooyung Han347d9f22021-05-28 00:05:14 +090020
Jiyong Parkf7dea252021-09-08 01:42:54 +090021use crate::instance::{ApkData, InstanceDisk, MicrodroidData, RootHash};
Andrew Scullb2f44472022-01-21 14:41:34 +000022use android_hardware_security_dice::aidl::android::hardware::security::dice::{
23 Config::Config, InputValues::InputValues, Mode::Mode,
24};
25use android_security_dice::aidl::android::security::dice::IDiceMaintenance::IDiceMaintenance;
Jooyung Handd0a1732021-11-23 15:26:20 +090026use anyhow::{anyhow, bail, ensure, Context, Error, Result};
Jiyong Parka41535b2021-09-10 19:31:48 +090027use apkverify::{get_public_key_der, verify};
Inseob Kim1b95f2e2021-08-19 13:17:40 +090028use binder::unstable_api::{new_spibinder, AIBinder};
Andrew Scullb2f44472022-01-21 14:41:34 +000029use binder::{wait_for_interface, FromIBinder, Strong};
30use diced_utils::cbor::encode_header;
Inseob Kim197748b2021-12-01 19:49:00 +090031use glob::glob;
Jiyong Park21ce2c52021-08-28 02:32:17 +090032use idsig::V4Signature;
Inseob Kim197748b2021-12-01 19:49:00 +090033use itertools::sorted;
Andrew Scull684590f2022-01-27 16:14:26 +000034use log::{error, info};
Jooyung Han4a9b3bf2021-09-10 17:19:00 +090035use microdroid_metadata::{write_metadata, Metadata};
Jooyung Han634e2d72021-06-10 16:27:38 +090036use microdroid_payload_config::{Task, TaskType, VmPayloadConfig};
Jiyong Park9f72ea62021-12-06 21:18:38 +090037use once_cell::sync::OnceCell;
Jooyung Han4a9b3bf2021-09-10 17:19:00 +090038use payload::{get_apex_data_from_payload, load_metadata, to_metadata};
Andrew Scullb2f44472022-01-21 14:41:34 +000039use ring::digest;
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};
Jiyong Park44e967d2021-12-21 13:53:42 +090044use std::io::BufRead;
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";
Jooyung Han347d9f22021-05-28 00:05:14 +090065
Inseob Kim1b95f2e2021-08-19 13:17:40 +090066/// The CID representing the host VM
67const VMADDR_CID_HOST: u32 = 2;
68
Jiyong Parkbb4a9872021-09-06 15:59:21 +090069const APEX_CONFIG_DONE_PROP: &str = "apex_config.done";
Jiyong Parkfa91d702021-10-18 23:51:39 +090070const LOGD_ENABLED_PROP: &str = "ro.boot.logd.enabled";
Andrew Scullb2f44472022-01-21 14:41:34 +000071const ADBD_ENABLED_PROP: &str = "ro.boot.adb.enabled";
72const DEBUGGABLE_PROP: &str = "ro.boot.microdroid.debuggable";
Jiyong Parkbb4a9872021-09-06 15:59:21 +090073
Jooyung Handd0a1732021-11-23 15:26:20 +090074#[derive(thiserror::Error, Debug)]
75enum MicrodroidError {
76 #[error("Payload has changed: {0}")]
77 PayloadChanged(String),
78 #[error("Payload verification has failed: {0}")]
79 PayloadVerificationFailed(String),
Jooyung Han5c6d4172021-12-06 14:17:52 +090080 #[error("Payload config is invalid: {0}")]
81 InvalidConfig(String),
Jooyung Handd0a1732021-11-23 15:26:20 +090082}
83
84fn translate_error(err: &Error) -> (i32, String) {
85 if let Some(e) = err.downcast_ref::<MicrodroidError>() {
86 match e {
87 MicrodroidError::PayloadChanged(msg) => (ERROR_PAYLOAD_CHANGED, msg.to_string()),
88 MicrodroidError::PayloadVerificationFailed(msg) => {
89 (ERROR_PAYLOAD_VERIFICATION_FAILED, msg.to_string())
90 }
Jooyung Han5c6d4172021-12-06 14:17:52 +090091 MicrodroidError::InvalidConfig(msg) => (ERROR_PAYLOAD_INVALID_CONFIG, msg.to_string()),
Jooyung Handd0a1732021-11-23 15:26:20 +090092 }
93 } else {
94 (ERROR_UNKNOWN, err.to_string())
95 }
96}
97
Inseob Kim1b95f2e2021-08-19 13:17:40 +090098fn get_vms_rpc_binder() -> Result<Strong<dyn IVirtualMachineService>> {
99 // SAFETY: AIBinder returned by RpcClient has correct reference count, and the ownership can be
100 // safely taken by new_spibinder.
101 let ibinder = unsafe {
102 new_spibinder(binder_rpc_unstable_bindgen::RpcClient(
103 VMADDR_CID_HOST,
Inseob Kimd0587562021-09-01 21:27:32 +0900104 VM_BINDER_SERVICE_PORT as u32,
Inseob Kim1b95f2e2021-08-19 13:17:40 +0900105 ) as *mut AIBinder)
106 };
107 if let Some(ibinder) = ibinder {
108 <dyn IVirtualMachineService>::try_from(ibinder).context("Cannot connect to RPC service")
109 } else {
110 bail!("Invalid raw AIBinder")
111 }
112}
113
Jooyung Han311b1202021-09-14 22:00:16 +0900114fn main() {
115 if let Err(e) = try_main() {
Jooyung Hanbfe086f2021-10-28 10:15:45 +0900116 error!("Failed with {:?}. Shutting down...", e);
117 if let Err(e) = system_properties::write("sys.powerctl", "shutdown") {
118 error!("failed to shutdown {:?}", e);
119 }
Jooyung Han311b1202021-09-14 22:00:16 +0900120 std::process::exit(1);
121 }
122}
123
124fn try_main() -> Result<()> {
Jiyong Park79b88012021-06-25 13:06:25 +0900125 kernlog::init()?;
Jooyung Han347d9f22021-05-28 00:05:14 +0900126 info!("started.");
127
Jooyung Handd0a1732021-11-23 15:26:20 +0900128 let service = get_vms_rpc_binder().context("cannot connect to VirtualMachineService")?;
Jooyung Han5c6d4172021-12-06 14:17:52 +0900129 match try_run_payload(&service) {
130 Ok(code) => {
131 info!("notifying payload finished");
132 service.notifyPayloadFinished(code)?;
133 if code == 0 {
134 info!("task successfully finished");
135 } else {
136 error!("task exited with exit code: {}", code);
137 }
138 Ok(())
139 }
140 Err(err) => {
141 error!("task terminated: {:?}", err);
142 let (error_code, message) = translate_error(&err);
143 service.notifyError(error_code, &message)?;
144 Err(err)
145 }
Jooyung Handd0a1732021-11-23 15:26:20 +0900146 }
147}
148
Andrew Scullb2f44472022-01-21 14:41:34 +0000149fn is_debuggable() -> Result<bool> {
150 // Read all the properties so the behaviour is most similar between debug and non-debug boots.
151 // Defensively default to debug enabled for unrecognised values.
152 let adb = system_properties::read_bool(ADBD_ENABLED_PROP, true)?;
153 let logd = system_properties::read_bool(LOGD_ENABLED_PROP, true)?;
154 let debuggable = system_properties::read_bool(DEBUGGABLE_PROP, true)?;
155 Ok(adb || logd || debuggable)
156}
157
158fn dice_derivation(verified_data: MicrodroidData, payload_config_path: &str) -> Result<()> {
159 // Calculate compound digests of code and authorities
160 let mut code_hash_ctx = digest::Context::new(&digest::SHA512);
161 let mut authority_hash_ctx = digest::Context::new(&digest::SHA512);
162 code_hash_ctx.update(verified_data.apk_data.root_hash.as_ref());
163 authority_hash_ctx.update(verified_data.apk_data.pubkey.as_ref());
164 for extra_apk in verified_data.extra_apks_data {
165 code_hash_ctx.update(extra_apk.root_hash.as_ref());
166 authority_hash_ctx.update(extra_apk.pubkey.as_ref());
167 }
168 for apex in verified_data.apex_data {
169 code_hash_ctx.update(apex.root_digest.as_ref());
170 authority_hash_ctx.update(apex.public_key.as_ref());
171 }
172 let code_hash = code_hash_ctx.finish().as_ref().try_into().unwrap();
173 let authority_hash = authority_hash_ctx.finish().as_ref().try_into().unwrap();
174
175 // {
176 // -70002: "Microdroid payload",
177 // -71000: payload_config_path
178 // }
179 let mut config_desc = vec![
180 0xa2, 0x3a, 0x00, 0x01, 0x11, 0x71, 0x72, 0x4d, 0x69, 0x63, 0x72, 0x6f, 0x64, 0x72, 0x6f,
181 0x69, 0x64, 0x20, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x3a, 0x00, 0x01, 0x15, 0x57,
182 ];
183 let config_path_bytes = payload_config_path.as_bytes();
184 encode_header(3, config_path_bytes.len().try_into().unwrap(), &mut config_desc)?;
185 config_desc.extend_from_slice(config_path_bytes);
186
187 // Send the details to diced
188 let diced =
189 wait_for_interface::<dyn IDiceMaintenance>("android.security.dice.IDiceMaintenance")
190 .context("IDiceMaintenance service not found")?;
191 diced
192 .demoteSelf(&[InputValues {
193 codeHash: code_hash,
194 config: Config { desc: config_desc },
195 authorityHash: authority_hash,
196 authorityDescriptor: None,
197 mode: if is_debuggable()? { Mode::DEBUG } else { Mode::NORMAL },
198 hidden: [0; 64],
199 }])
200 .context("IDiceMaintenance::demoteSelf failed")?;
201 Ok(())
202}
203
Jooyung Han5c6d4172021-12-06 14:17:52 +0900204fn try_run_payload(service: &Strong<dyn IVirtualMachineService>) -> Result<i32> {
Jooyung Han311b1202021-09-14 22:00:16 +0900205 let metadata = load_metadata().context("Failed to load payload metadata")?;
Jooyung Han19c1d6c2021-08-06 14:08:16 +0900206
Jooyung Han311b1202021-09-14 22:00:16 +0900207 let mut instance = InstanceDisk::new().context("Failed to load instance.img")?;
Jooyung Han7a343f92021-09-08 22:53:11 +0900208 let saved_data = instance.read_microdroid_data().context("Failed to read identity data")?;
Jiyong Parkbb4a9872021-09-06 15:59:21 +0900209
210 // Verify the payload before using it.
Jooyung Han7a343f92021-09-08 22:53:11 +0900211 let verified_data =
212 verify_payload(&metadata, saved_data.as_ref()).context("Payload verification failed")?;
213 if let Some(saved_data) = saved_data {
Jooyung Handd0a1732021-11-23 15:26:20 +0900214 ensure!(
215 saved_data == verified_data,
216 MicrodroidError::PayloadChanged(String::from(
217 "Detected an update of the payload which isn't supported yet."
218 ))
219 );
220 info!("Saved data is verified.");
Jiyong Parkbb4a9872021-09-06 15:59:21 +0900221 } else {
Jooyung Han7a343f92021-09-08 22:53:11 +0900222 info!("Saving verified data.");
223 instance.write_microdroid_data(&verified_data).context("Failed to write identity data")?;
Jooyung Han19c1d6c2021-08-06 14:08:16 +0900224 }
225
Jooyung Hana6d11eb2021-09-10 11:48:05 +0900226 // Before reading a file from the APK, start zipfuse
Inseob Kim217038e2021-11-25 11:15:06 +0900227 run_zipfuse(
228 "fscontext=u:object_r:zipfusefs:s0,context=u:object_r:system_file:s0",
229 Path::new("/dev/block/mapper/microdroid-apk"),
230 Path::new("/mnt/apk"),
231 )
232 .context("Failed to run zipfuse")?;
Jiyong Park21ce2c52021-08-28 02:32:17 +0900233
Jooyung Han5c6d4172021-12-06 14:17:52 +0900234 ensure!(
235 !metadata.payload_config_path.is_empty(),
236 MicrodroidError::InvalidConfig("No payload_config_path in metadata".to_string())
237 );
Inseob Kim197748b2021-12-01 19:49:00 +0900238
Jooyung Han5c6d4172021-12-06 14:17:52 +0900239 let config = load_config(Path::new(&metadata.payload_config_path))?;
Inseob Kim197748b2021-12-01 19:49:00 +0900240 if config.extra_apks.len() != verified_data.extra_apks_data.len() {
241 return Err(anyhow!(
242 "config expects {} extra apks, but found only {}",
243 config.extra_apks.len(),
244 verified_data.extra_apks_data.len()
245 ));
246 }
247 mount_extra_apks(&config)?;
Jooyung Han634e2d72021-06-10 16:27:38 +0900248
Andrew Scullb2f44472022-01-21 14:41:34 +0000249 info!("DICE derivation for payload");
250 dice_derivation(verified_data, &metadata.payload_config_path)?;
251
Jooyung Han5c6d4172021-12-06 14:17:52 +0900252 // Wait until apex config is done. (e.g. linker configuration for apexes)
253 // TODO(jooyung): wait until sys.boot_completed?
254 wait_for_apex_config_done()?;
255
256 ensure!(
257 config.task.is_some(),
258 MicrodroidError::InvalidConfig("No task in VM config".to_string())
259 );
260 exec_task(&config.task.unwrap(), service)
Jooyung Han347d9f22021-05-28 00:05:14 +0900261}
262
Inseob Kim217038e2021-11-25 11:15:06 +0900263struct ApkDmverityArgument<'a> {
264 apk: &'a str,
265 idsig: &'a str,
266 name: &'a str,
Inseob Kim197748b2021-12-01 19:49:00 +0900267 saved_root_hash: Option<&'a RootHash>,
Inseob Kim217038e2021-11-25 11:15:06 +0900268}
269
270fn run_apkdmverity(args: &[ApkDmverityArgument]) -> Result<Child> {
271 let mut cmd = Command::new(APKDMVERITY_BIN);
272
273 cmd.stdin(Stdio::null()).stdout(Stdio::null()).stderr(Stdio::null());
274
275 for argument in args {
276 cmd.arg("--apk").arg(argument.apk).arg(argument.idsig).arg(argument.name);
Inseob Kim197748b2021-12-01 19:49:00 +0900277 if let Some(root_hash) = argument.saved_root_hash {
278 cmd.arg(&to_hex_string(root_hash));
279 } else {
280 cmd.arg("none");
281 }
Inseob Kim217038e2021-11-25 11:15:06 +0900282 }
283
284 cmd.spawn().context("Spawn apkdmverity")
285}
286
287fn run_zipfuse(option: &str, zip_path: &Path, mount_dir: &Path) -> Result<Child> {
288 Command::new(ZIPFUSE_BIN)
289 .arg("-o")
290 .arg(option)
291 .arg(zip_path)
292 .arg(mount_dir)
293 .stdin(Stdio::null())
294 .stdout(Stdio::null())
295 .stderr(Stdio::null())
296 .spawn()
297 .context("Spawn zipfuse")
298}
299
Jooyung Han7a343f92021-09-08 22:53:11 +0900300// Verify payload before executing it. For APK payload, Full verification (which is slow) is done
301// when the root_hash values from the idsig file and the instance disk are different. This function
302// returns the verified root hash (for APK payload) and pubkeys (for APEX payloads) that can be
303// saved to the instance disk.
304fn verify_payload(
305 metadata: &Metadata,
306 saved_data: Option<&MicrodroidData>,
307) -> Result<MicrodroidData> {
Jiyong Parkbb4a9872021-09-06 15:59:21 +0900308 let start_time = SystemTime::now();
309
Jiyong Park9f72ea62021-12-06 21:18:38 +0900310 if let Some(saved_bootconfig) = saved_data.map(|d| &d.bootconfig) {
311 ensure!(
312 saved_bootconfig.as_ref() == get_bootconfig()?.as_slice(),
313 MicrodroidError::PayloadChanged(String::from("Bootconfig has changed."))
314 );
315 }
316
Inseob Kim197748b2021-12-01 19:49:00 +0900317 // Verify main APK
Jooyung Han7a343f92021-09-08 22:53:11 +0900318 let root_hash = saved_data.map(|d| &d.apk_data.root_hash);
Inseob Kim197748b2021-12-01 19:49:00 +0900319 let root_hash_from_idsig = get_apk_root_hash_from_idsig(MAIN_APK_IDSIG_PATH)?;
Jiyong Parkf7dea252021-09-08 01:42:54 +0900320 let root_hash_trustful = root_hash == Some(&root_hash_from_idsig);
Jiyong Parkbb4a9872021-09-06 15:59:21 +0900321
Jiyong Parkf7dea252021-09-08 01:42:54 +0900322 // 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 +0900323 // instead of the value read from the idsig file.
Inseob Kim197748b2021-12-01 19:49:00 +0900324 let main_apk_argument = {
325 ApkDmverityArgument {
326 apk: MAIN_APK_PATH,
327 idsig: MAIN_APK_IDSIG_PATH,
328 name: MAIN_APK_DEVICE_NAME,
329 saved_root_hash: if root_hash_trustful {
330 Some(root_hash_from_idsig.as_ref())
331 } else {
332 None
333 },
334 }
335 };
336 let mut apkdmverity_arguments = vec![main_apk_argument];
337
338 // Verify extra APKs
339 // For now, we can't read the payload config, so glob APKs and idsigs.
340 // Later, we'll see if it matches with the payload config.
341
342 // sort globbed paths to match apks (extra-apk-{idx}) and idsigs (extra-idsig-{idx})
343 // e.g. "extra-apk-0" corresponds to "extra-idsig-0"
344 let extra_apks =
345 sorted(glob(EXTRA_APK_PATH_PATTERN)?.collect::<Result<Vec<_>, _>>()?).collect::<Vec<_>>();
346 let extra_idsigs =
347 sorted(glob(EXTRA_IDSIG_PATH_PATTERN)?.collect::<Result<Vec<_>, _>>()?).collect::<Vec<_>>();
348 if extra_apks.len() != extra_idsigs.len() {
349 return Err(anyhow!(
350 "Extra apks/idsigs mismatch: {} apks but {} idsigs",
351 extra_apks.len(),
352 extra_idsigs.len()
353 ));
354 }
355 let extra_apks_count = extra_apks.len();
356
357 let (extra_apk_names, extra_root_hashes_from_idsig): (Vec<_>, Vec<_>) = extra_idsigs
358 .iter()
359 .enumerate()
360 .map(|(i, extra_idsig)| {
361 (
362 format!("extra-apk-{}", i),
363 get_apk_root_hash_from_idsig(extra_idsig.to_str().unwrap())
364 .expect("Can't find root hash from extra idsig"),
365 )
366 })
367 .unzip();
368
369 let saved_extra_root_hashes: Vec<_> = saved_data
370 .map(|d| d.extra_apks_data.iter().map(|apk_data| &apk_data.root_hash).collect())
371 .unwrap_or_else(Vec::new);
372 let extra_root_hashes_trustful: Vec<_> = extra_root_hashes_from_idsig
373 .iter()
374 .enumerate()
375 .map(|(i, root_hash_from_idsig)| {
376 saved_extra_root_hashes.get(i).copied() == Some(root_hash_from_idsig)
377 })
378 .collect();
379
380 for i in 0..extra_apks_count {
381 apkdmverity_arguments.push({
382 ApkDmverityArgument {
383 apk: extra_apks[i].to_str().unwrap(),
384 idsig: extra_idsigs[i].to_str().unwrap(),
385 name: &extra_apk_names[i],
386 saved_root_hash: if extra_root_hashes_trustful[i] {
387 Some(&extra_root_hashes_from_idsig[i])
388 } else {
389 None
390 },
391 }
392 });
Jiyong Parkbb4a9872021-09-06 15:59:21 +0900393 }
394
395 // Start apkdmverity and wait for the dm-verify block
Inseob Kim197748b2021-12-01 19:49:00 +0900396 let mut apkdmverity_child = run_apkdmverity(&apkdmverity_arguments)?;
Jooyung Han7a343f92021-09-08 22:53:11 +0900397
Jooyung Hanc8deb472021-09-13 13:48:25 +0900398 // While waiting for apkdmverity to mount APK, gathers public keys and root digests from
399 // APEX payload.
Jooyung Han7a343f92021-09-08 22:53:11 +0900400 let apex_data_from_payload = get_apex_data_from_payload(metadata)?;
Jooyung Han4a9b3bf2021-09-10 17:19:00 +0900401 if let Some(saved_data) = saved_data.map(|d| &d.apex_data) {
Jooyung Hanc8deb472021-09-13 13:48:25 +0900402 // We don't support APEX updates. (assuming that update will change root digest)
Jooyung Handd0a1732021-11-23 15:26:20 +0900403 ensure!(
404 saved_data == &apex_data_from_payload,
405 MicrodroidError::PayloadChanged(String::from("APEXes have changed."))
406 );
Jooyung Han4a9b3bf2021-09-10 17:19:00 +0900407 let apex_metadata = to_metadata(&apex_data_from_payload);
Jooyung Hanc8deb472021-09-13 13:48:25 +0900408 // Pass metadata(with public keys and root digests) to apexd so that it uses the passed
409 // metadata instead of the default one (/dev/block/by-name/payload-metadata)
Jooyung Han4a9b3bf2021-09-10 17:19:00 +0900410 OpenOptions::new()
411 .create_new(true)
412 .write(true)
413 .open("/apex/vm-payload-metadata")
414 .context("Failed to open /apex/vm-payload-metadata")
415 .and_then(|f| write_metadata(&apex_metadata, f))?;
416 }
417 // Start apexd to activate APEXes
418 system_properties::write("ctl.start", "apexd-vm")?;
Jooyung Han7a343f92021-09-08 22:53:11 +0900419
Inseob Kim217038e2021-11-25 11:15:06 +0900420 // TODO(inseob): add timeout
421 apkdmverity_child.wait()?;
Jooyung Han19c1d6c2021-08-06 14:08:16 +0900422
Jiyong Parkf7dea252021-09-08 01:42:54 +0900423 // Do the full verification if the root_hash is un-trustful. This requires the full scanning of
Jiyong Parkbb4a9872021-09-06 15:59:21 +0900424 // 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 +0900425 // 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 +0900426 // of the VM or APK was updated in the host.
427 // TODO(jooyung): consider multithreading to make this faster
Inseob Kim197748b2021-12-01 19:49:00 +0900428 let main_apk_pubkey = get_public_key_from_apk(DM_MOUNTED_APK_PATH, root_hash_trustful)?;
429 let extra_apks_data = extra_root_hashes_from_idsig
430 .into_iter()
431 .enumerate()
432 .map(|(i, extra_root_hash)| {
433 let mount_path = format!("/dev/block/mapper/{}", &extra_apk_names[i]);
434 let apk_pubkey = get_public_key_from_apk(&mount_path, extra_root_hashes_trustful[i])?;
435 Ok(ApkData { root_hash: extra_root_hash, pubkey: apk_pubkey })
436 })
437 .collect::<Result<Vec<_>>>()?;
Jiyong Parkbb4a9872021-09-06 15:59:21 +0900438
439 info!("payload verification successful. took {:#?}", start_time.elapsed().unwrap());
440
Jiyong Parkf7dea252021-09-08 01:42:54 +0900441 // At this point, we can ensure that the root_hash from the idsig file is trusted, either by
442 // fully verifying the APK or by comparing it with the saved root_hash.
Jooyung Han7a343f92021-09-08 22:53:11 +0900443 Ok(MicrodroidData {
Inseob Kim197748b2021-12-01 19:49:00 +0900444 apk_data: ApkData { root_hash: root_hash_from_idsig, pubkey: main_apk_pubkey },
445 extra_apks_data,
Jooyung Han7a343f92021-09-08 22:53:11 +0900446 apex_data: apex_data_from_payload,
Jiyong Park9f72ea62021-12-06 21:18:38 +0900447 bootconfig: get_bootconfig()?.clone().into_boxed_slice(),
Jooyung Han7a343f92021-09-08 22:53:11 +0900448 })
Jiyong Parkbb4a9872021-09-06 15:59:21 +0900449}
450
Inseob Kim197748b2021-12-01 19:49:00 +0900451fn mount_extra_apks(config: &VmPayloadConfig) -> Result<()> {
452 // For now, only the number of apks is important, as the mount point and dm-verity name is fixed
453 for i in 0..config.extra_apks.len() {
454 let mount_dir = format!("/mnt/extra-apk/{}", i);
455 create_dir(Path::new(&mount_dir)).context("Failed to create mount dir for extra apks")?;
456
457 // don't wait, just detach
458 run_zipfuse(
459 "fscontext=u:object_r:zipfusefs:s0,context=u:object_r:extra_apk_file:s0",
460 Path::new(&format!("/dev/block/mapper/extra-apk-{}", i)),
461 Path::new(&mount_dir),
462 )
463 .context("Failed to zipfuse extra apks")?;
464 }
465
466 Ok(())
467}
468
Jiyong Parkbb4a9872021-09-06 15:59:21 +0900469// Waits until linker config is generated
470fn wait_for_apex_config_done() -> Result<()> {
471 let mut prop = PropertyWatcher::new(APEX_CONFIG_DONE_PROP)?;
472 loop {
473 prop.wait()?;
Andrew Scull4b5369f2022-01-28 13:47:20 +0000474 if system_properties::read_bool(APEX_CONFIG_DONE_PROP, false)? {
Jiyong Parkbb4a9872021-09-06 15:59:21 +0900475 break;
476 }
477 }
Jooyung Han19c1d6c2021-08-06 14:08:16 +0900478 Ok(())
479}
480
Inseob Kim197748b2021-12-01 19:49:00 +0900481fn get_apk_root_hash_from_idsig(path: &str) -> Result<Box<RootHash>> {
482 let mut idsig = File::open(path)?;
Jiyong Park21ce2c52021-08-28 02:32:17 +0900483 let idsig = V4Signature::from(&mut idsig)?;
484 Ok(idsig.hashing_info.raw_root_hash)
485}
486
Inseob Kim197748b2021-12-01 19:49:00 +0900487fn get_public_key_from_apk(apk: &str, root_hash_trustful: bool) -> Result<Box<[u8]>> {
488 if !root_hash_trustful {
489 verify(apk).context(MicrodroidError::PayloadVerificationFailed(format!(
490 "failed to verify {}",
491 apk
492 )))
493 } else {
494 get_public_key_der(apk)
495 }
496}
497
Jiyong Park9f72ea62021-12-06 21:18:38 +0900498fn get_bootconfig() -> Result<&'static Vec<u8>> {
499 static VAL: OnceCell<Vec<u8>> = OnceCell::new();
Jiyong Park44e967d2021-12-21 13:53:42 +0900500 VAL.get_or_try_init(|| -> Result<Vec<u8>> {
501 let f = File::open("/proc/bootconfig")?;
502
503 // Filter-out androidboot.vbmeta.device which contains UUID of the vbmeta partition. That
504 // UUID could change everytime when the same VM is started because the composite disk image
505 // is ephemeral. A change in UUID is okay as long as other configs (e.g.
506 // androidboot.vbmeta.digest) remain same.
507 Ok(std::io::BufReader::new(f)
508 .lines()
509 // note: this try_fold is to early return when we fail to read a line from the file
510 .try_fold(Vec::new(), |mut lines, line| {
511 line.map(|s| {
512 lines.push(s);
513 lines
514 })
515 })?
516 .into_iter()
517 .filter(|line| {
518 let tokens: Vec<&str> = line.splitn(2, '=').collect();
519 // note: if `line` doesn't have =, tokens[0] is the entire line.
520 tokens[0].trim() != "androidboot.vbmeta.device"
521 })
522 .flat_map(|line| (line + "\n").into_bytes())
523 .collect())
524 })
Jiyong Park9f72ea62021-12-06 21:18:38 +0900525}
526
Jooyung Han634e2d72021-06-10 16:27:38 +0900527fn load_config(path: &Path) -> Result<VmPayloadConfig> {
528 info!("loading config from {:?}...", path);
529 let file = ioutil::wait_for_file(path, WAIT_TIMEOUT)?;
530 Ok(serde_json::from_reader(file)?)
531}
532
Jiyong Park8611a6c2021-07-09 18:17:44 +0900533/// Executes the given task. Stdout of the task is piped into the vsock stream to the
534/// virtualizationservice in the host side.
Jooyung Han5c6d4172021-12-06 14:17:52 +0900535fn exec_task(task: &Task, service: &Strong<dyn IVirtualMachineService>) -> Result<i32> {
Jiyong Park8611a6c2021-07-09 18:17:44 +0900536 info!("executing main task {:?}...", task);
Inseob Kim86ca0162021-10-20 02:21:02 +0000537 let mut command = build_command(task)?;
Inseob Kim7f61fe72021-08-20 20:50:47 +0900538
539 info!("notifying payload started");
Inseob Kimc7d28c72021-10-25 14:28:10 +0000540 service.notifyPayloadStarted()?;
Inseob Kim7f61fe72021-08-20 20:50:47 +0900541
Jiyong Parkfa91d702021-10-18 23:51:39 +0900542 // Start logging if enabled
543 // TODO(b/200914564) set filterspec if debug_level is app_only
Andrew Scull4b5369f2022-01-28 13:47:20 +0000544 if system_properties::read_bool(LOGD_ENABLED_PROP, false)? {
Jiyong Parkfa91d702021-10-18 23:51:39 +0900545 system_properties::write("ctl.start", "seriallogging")?;
546 }
547
Inseob Kim86ca0162021-10-20 02:21:02 +0000548 let exit_status = command.spawn()?.wait()?;
Jooyung Han5c6d4172021-12-06 14:17:52 +0900549 exit_status.code().ok_or_else(|| anyhow!("Failed to get exit_code from the paylaod."))
Jooyung Han347d9f22021-05-28 00:05:14 +0900550}
Jooyung Han634e2d72021-06-10 16:27:38 +0900551
552fn build_command(task: &Task) -> Result<Command> {
Inseob Kim7f61fe72021-08-20 20:50:47 +0900553 const VMADDR_CID_HOST: u32 = 2;
Inseob Kim7f61fe72021-08-20 20:50:47 +0900554
555 let mut command = match task.type_ {
Jooyung Han634e2d72021-06-10 16:27:38 +0900556 TaskType::Executable => {
557 let mut command = Command::new(&task.command);
558 command.args(&task.args);
559 command
560 }
561 TaskType::MicrodroidLauncher => {
562 let mut command = Command::new("/system/bin/microdroid_launcher");
563 command.arg(find_library_path(&task.command)?).args(&task.args);
564 command
565 }
Inseob Kim7f61fe72021-08-20 20:50:47 +0900566 };
567
Inseob Kimd0587562021-09-01 21:27:32 +0900568 match VsockStream::connect_with_cid_port(VMADDR_CID_HOST, VM_STREAM_SERVICE_PORT as u32) {
Inseob Kim7f61fe72021-08-20 20:50:47 +0900569 Ok(stream) => {
570 // SAFETY: the ownership of the underlying file descriptor is transferred from stream
571 // to the file object, and then into the Command object. When the command is finished,
572 // the file descriptor is closed.
573 let file = unsafe { File::from_raw_fd(stream.into_raw_fd()) };
574 command
575 .stdin(Stdio::from(file.try_clone()?))
576 .stdout(Stdio::from(file.try_clone()?))
577 .stderr(Stdio::from(file));
578 }
579 Err(e) => {
580 error!("failed to connect to virtualization service: {}", e);
581 // Don't fail hard here. Even if we failed to connect to the virtualizationservice,
582 // we keep executing the task. This can happen if the owner of the VM doesn't register
583 // callback to accept the stream. Use /dev/null as the stream so that the task can
584 // make progress without waiting for someone to consume the output.
585 command.stdin(Stdio::null()).stdout(Stdio::null()).stderr(Stdio::null());
586 }
587 }
588
589 Ok(command)
Jooyung Han634e2d72021-06-10 16:27:38 +0900590}
591
592fn find_library_path(name: &str) -> Result<String> {
593 let mut watcher = PropertyWatcher::new("ro.product.cpu.abilist")?;
594 let value = watcher.read(|_name, value| Ok(value.trim().to_string()))?;
595 let abi = value.split(',').next().ok_or_else(|| anyhow!("no abilist"))?;
596 let path = format!("/mnt/apk/lib/{}/{}", abi, name);
597
598 let metadata = fs::metadata(&path)?;
599 if !metadata.is_file() {
600 bail!("{} is not a file", &path);
601 }
602
603 Ok(path)
604}
Jiyong Park21ce2c52021-08-28 02:32:17 +0900605
606fn to_hex_string(buf: &[u8]) -> String {
607 buf.iter().map(|b| format!("{:02X}", b)).collect()
608}