blob: 8e9eb9d5bfd404c99faaccb8ce815ec2dac741cd [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 Scull34916a72022-01-30 21:34:24 +000039use rand::Fill;
Andrew Scullb2f44472022-01-21 14:41:34 +000040use ring::digest;
Jiyong Parkbb4a9872021-09-06 15:59:21 +090041use rustutils::system_properties;
Joel Galenson482704c2021-07-29 15:53:53 -070042use rustutils::system_properties::PropertyWatcher;
Andrew Scullb2f44472022-01-21 14:41:34 +000043use std::convert::TryInto;
Inseob Kim197748b2021-12-01 19:49:00 +090044use std::fs::{self, create_dir, File, OpenOptions};
Jiyong Park44e967d2021-12-21 13:53:42 +090045use std::io::BufRead;
Inseob Kimc7d28c72021-10-25 14:28:10 +000046use std::os::unix::io::{FromRawFd, IntoRawFd};
Jooyung Hanf48ceb42021-06-01 18:00:04 +090047use std::path::Path;
Inseob Kim217038e2021-11-25 11:15:06 +090048use std::process::{Child, Command, Stdio};
Jiyong Park8611a6c2021-07-09 18:17:44 +090049use std::str;
Jiyong Parkbb4a9872021-09-06 15:59:21 +090050use std::time::{Duration, SystemTime};
Jiyong Park8611a6c2021-07-09 18:17:44 +090051use vsock::VsockStream;
Jooyung Han634e2d72021-06-10 16:27:38 +090052
Inseob Kimd0587562021-09-01 21:27:32 +090053use android_system_virtualmachineservice::aidl::android::system::virtualmachineservice::IVirtualMachineService::{
Jooyung Han5c6d4172021-12-06 14:17:52 +090054 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 +090055};
Inseob Kim1b95f2e2021-08-19 13:17:40 +090056
Jooyung Han634e2d72021-06-10 16:27:38 +090057const WAIT_TIMEOUT: Duration = Duration::from_secs(10);
Inseob Kim197748b2021-12-01 19:49:00 +090058const MAIN_APK_PATH: &str = "/dev/block/by-name/microdroid-apk";
59const MAIN_APK_IDSIG_PATH: &str = "/dev/block/by-name/microdroid-apk-idsig";
60const MAIN_APK_DEVICE_NAME: &str = "microdroid-apk";
61const EXTRA_APK_PATH_PATTERN: &str = "/dev/block/by-name/extra-apk-*";
62const EXTRA_IDSIG_PATH_PATTERN: &str = "/dev/block/by-name/extra-idsig-*";
Jooyung Han19c1d6c2021-08-06 14:08:16 +090063const DM_MOUNTED_APK_PATH: &str = "/dev/block/mapper/microdroid-apk";
Inseob Kim217038e2021-11-25 11:15:06 +090064const APKDMVERITY_BIN: &str = "/system/bin/apkdmverity";
65const ZIPFUSE_BIN: &str = "/system/bin/zipfuse";
Jooyung Han347d9f22021-05-28 00:05:14 +090066
Inseob Kim1b95f2e2021-08-19 13:17:40 +090067/// The CID representing the host VM
68const VMADDR_CID_HOST: u32 = 2;
69
Jiyong Parkbb4a9872021-09-06 15:59:21 +090070const APEX_CONFIG_DONE_PROP: &str = "apex_config.done";
Jiyong Parkfa91d702021-10-18 23:51:39 +090071const LOGD_ENABLED_PROP: &str = "ro.boot.logd.enabled";
Andrew Scullb2f44472022-01-21 14:41:34 +000072const ADBD_ENABLED_PROP: &str = "ro.boot.adb.enabled";
73const DEBUGGABLE_PROP: &str = "ro.boot.microdroid.debuggable";
Jiyong Parkbb4a9872021-09-06 15:59:21 +090074
Jooyung Handd0a1732021-11-23 15:26:20 +090075#[derive(thiserror::Error, Debug)]
76enum MicrodroidError {
77 #[error("Payload has changed: {0}")]
78 PayloadChanged(String),
79 #[error("Payload verification has failed: {0}")]
80 PayloadVerificationFailed(String),
Jooyung Han5c6d4172021-12-06 14:17:52 +090081 #[error("Payload config is invalid: {0}")]
82 InvalidConfig(String),
Jooyung Handd0a1732021-11-23 15:26:20 +090083}
84
85fn translate_error(err: &Error) -> (i32, String) {
86 if let Some(e) = err.downcast_ref::<MicrodroidError>() {
87 match e {
88 MicrodroidError::PayloadChanged(msg) => (ERROR_PAYLOAD_CHANGED, msg.to_string()),
89 MicrodroidError::PayloadVerificationFailed(msg) => {
90 (ERROR_PAYLOAD_VERIFICATION_FAILED, msg.to_string())
91 }
Jooyung Han5c6d4172021-12-06 14:17:52 +090092 MicrodroidError::InvalidConfig(msg) => (ERROR_PAYLOAD_INVALID_CONFIG, msg.to_string()),
Jooyung Handd0a1732021-11-23 15:26:20 +090093 }
94 } else {
95 (ERROR_UNKNOWN, err.to_string())
96 }
97}
98
Inseob Kim1b95f2e2021-08-19 13:17:40 +090099fn get_vms_rpc_binder() -> Result<Strong<dyn IVirtualMachineService>> {
100 // SAFETY: AIBinder returned by RpcClient has correct reference count, and the ownership can be
101 // safely taken by new_spibinder.
102 let ibinder = unsafe {
103 new_spibinder(binder_rpc_unstable_bindgen::RpcClient(
104 VMADDR_CID_HOST,
Inseob Kimd0587562021-09-01 21:27:32 +0900105 VM_BINDER_SERVICE_PORT as u32,
Inseob Kim1b95f2e2021-08-19 13:17:40 +0900106 ) as *mut AIBinder)
107 };
108 if let Some(ibinder) = ibinder {
109 <dyn IVirtualMachineService>::try_from(ibinder).context("Cannot connect to RPC service")
110 } else {
111 bail!("Invalid raw AIBinder")
112 }
113}
114
Jooyung Han311b1202021-09-14 22:00:16 +0900115fn main() {
116 if let Err(e) = try_main() {
Jooyung Hanbfe086f2021-10-28 10:15:45 +0900117 error!("Failed with {:?}. Shutting down...", e);
118 if let Err(e) = system_properties::write("sys.powerctl", "shutdown") {
119 error!("failed to shutdown {:?}", e);
120 }
Jooyung Han311b1202021-09-14 22:00:16 +0900121 std::process::exit(1);
122 }
123}
124
125fn try_main() -> Result<()> {
Jiyong Park79b88012021-06-25 13:06:25 +0900126 kernlog::init()?;
Jooyung Han347d9f22021-05-28 00:05:14 +0900127 info!("started.");
128
Jooyung Handd0a1732021-11-23 15:26:20 +0900129 let service = get_vms_rpc_binder().context("cannot connect to VirtualMachineService")?;
Jooyung Han5c6d4172021-12-06 14:17:52 +0900130 match try_run_payload(&service) {
131 Ok(code) => {
132 info!("notifying payload finished");
133 service.notifyPayloadFinished(code)?;
134 if code == 0 {
135 info!("task successfully finished");
136 } else {
137 error!("task exited with exit code: {}", code);
138 }
139 Ok(())
140 }
141 Err(err) => {
142 error!("task terminated: {:?}", err);
143 let (error_code, message) = translate_error(&err);
144 service.notifyError(error_code, &message)?;
145 Err(err)
146 }
Jooyung Handd0a1732021-11-23 15:26:20 +0900147 }
148}
149
Andrew Scullb2f44472022-01-21 14:41:34 +0000150fn is_debuggable() -> Result<bool> {
151 // Read all the properties so the behaviour is most similar between debug and non-debug boots.
152 // Defensively default to debug enabled for unrecognised values.
153 let adb = system_properties::read_bool(ADBD_ENABLED_PROP, true)?;
154 let logd = system_properties::read_bool(LOGD_ENABLED_PROP, true)?;
155 let debuggable = system_properties::read_bool(DEBUGGABLE_PROP, true)?;
156 Ok(adb || logd || debuggable)
157}
158
159fn dice_derivation(verified_data: MicrodroidData, payload_config_path: &str) -> Result<()> {
160 // Calculate compound digests of code and authorities
161 let mut code_hash_ctx = digest::Context::new(&digest::SHA512);
162 let mut authority_hash_ctx = digest::Context::new(&digest::SHA512);
163 code_hash_ctx.update(verified_data.apk_data.root_hash.as_ref());
164 authority_hash_ctx.update(verified_data.apk_data.pubkey.as_ref());
165 for extra_apk in verified_data.extra_apks_data {
166 code_hash_ctx.update(extra_apk.root_hash.as_ref());
167 authority_hash_ctx.update(extra_apk.pubkey.as_ref());
168 }
169 for apex in verified_data.apex_data {
170 code_hash_ctx.update(apex.root_digest.as_ref());
171 authority_hash_ctx.update(apex.public_key.as_ref());
172 }
173 let code_hash = code_hash_ctx.finish().as_ref().try_into().unwrap();
174 let authority_hash = authority_hash_ctx.finish().as_ref().try_into().unwrap();
175
176 // {
177 // -70002: "Microdroid payload",
178 // -71000: payload_config_path
179 // }
180 let mut config_desc = vec![
181 0xa2, 0x3a, 0x00, 0x01, 0x11, 0x71, 0x72, 0x4d, 0x69, 0x63, 0x72, 0x6f, 0x64, 0x72, 0x6f,
182 0x69, 0x64, 0x20, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x3a, 0x00, 0x01, 0x15, 0x57,
183 ];
184 let config_path_bytes = payload_config_path.as_bytes();
185 encode_header(3, config_path_bytes.len().try_into().unwrap(), &mut config_desc)?;
186 config_desc.extend_from_slice(config_path_bytes);
187
188 // Send the details to diced
189 let diced =
190 wait_for_interface::<dyn IDiceMaintenance>("android.security.dice.IDiceMaintenance")
191 .context("IDiceMaintenance service not found")?;
192 diced
193 .demoteSelf(&[InputValues {
194 codeHash: code_hash,
195 config: Config { desc: config_desc },
196 authorityHash: authority_hash,
197 authorityDescriptor: None,
198 mode: if is_debuggable()? { Mode::DEBUG } else { Mode::NORMAL },
Andrew Scull34916a72022-01-30 21:34:24 +0000199 hidden: verified_data.salt.try_into().unwrap(),
Andrew Scullb2f44472022-01-21 14:41:34 +0000200 }])
201 .context("IDiceMaintenance::demoteSelf failed")?;
202 Ok(())
203}
204
Jooyung Han5c6d4172021-12-06 14:17:52 +0900205fn try_run_payload(service: &Strong<dyn IVirtualMachineService>) -> Result<i32> {
Jooyung Han311b1202021-09-14 22:00:16 +0900206 let metadata = load_metadata().context("Failed to load payload metadata")?;
Jooyung Han19c1d6c2021-08-06 14:08:16 +0900207
Jooyung Han311b1202021-09-14 22:00:16 +0900208 let mut instance = InstanceDisk::new().context("Failed to load instance.img")?;
Jooyung Han7a343f92021-09-08 22:53:11 +0900209 let saved_data = instance.read_microdroid_data().context("Failed to read identity data")?;
Jiyong Parkbb4a9872021-09-06 15:59:21 +0900210
211 // Verify the payload before using it.
Jooyung Han7a343f92021-09-08 22:53:11 +0900212 let verified_data =
213 verify_payload(&metadata, saved_data.as_ref()).context("Payload verification failed")?;
214 if let Some(saved_data) = saved_data {
Jooyung Handd0a1732021-11-23 15:26:20 +0900215 ensure!(
216 saved_data == verified_data,
217 MicrodroidError::PayloadChanged(String::from(
218 "Detected an update of the payload which isn't supported yet."
219 ))
220 );
221 info!("Saved data is verified.");
Jiyong Parkbb4a9872021-09-06 15:59:21 +0900222 } else {
Jooyung Han7a343f92021-09-08 22:53:11 +0900223 info!("Saving verified data.");
224 instance.write_microdroid_data(&verified_data).context("Failed to write identity data")?;
Jooyung Han19c1d6c2021-08-06 14:08:16 +0900225 }
226
Jooyung Hana6d11eb2021-09-10 11:48:05 +0900227 // Before reading a file from the APK, start zipfuse
Inseob Kim217038e2021-11-25 11:15:06 +0900228 run_zipfuse(
229 "fscontext=u:object_r:zipfusefs:s0,context=u:object_r:system_file:s0",
230 Path::new("/dev/block/mapper/microdroid-apk"),
231 Path::new("/mnt/apk"),
232 )
233 .context("Failed to run zipfuse")?;
Jiyong Park21ce2c52021-08-28 02:32:17 +0900234
Jooyung Han5c6d4172021-12-06 14:17:52 +0900235 ensure!(
236 !metadata.payload_config_path.is_empty(),
237 MicrodroidError::InvalidConfig("No payload_config_path in metadata".to_string())
238 );
Inseob Kim197748b2021-12-01 19:49:00 +0900239
Jooyung Han5c6d4172021-12-06 14:17:52 +0900240 let config = load_config(Path::new(&metadata.payload_config_path))?;
Inseob Kim197748b2021-12-01 19:49:00 +0900241 if config.extra_apks.len() != verified_data.extra_apks_data.len() {
242 return Err(anyhow!(
243 "config expects {} extra apks, but found only {}",
244 config.extra_apks.len(),
245 verified_data.extra_apks_data.len()
246 ));
247 }
248 mount_extra_apks(&config)?;
Jooyung Han634e2d72021-06-10 16:27:38 +0900249
Andrew Scullb2f44472022-01-21 14:41:34 +0000250 info!("DICE derivation for payload");
251 dice_derivation(verified_data, &metadata.payload_config_path)?;
252
Jooyung Han5c6d4172021-12-06 14:17:52 +0900253 // Wait until apex config is done. (e.g. linker configuration for apexes)
254 // TODO(jooyung): wait until sys.boot_completed?
255 wait_for_apex_config_done()?;
256
257 ensure!(
258 config.task.is_some(),
259 MicrodroidError::InvalidConfig("No task in VM config".to_string())
260 );
261 exec_task(&config.task.unwrap(), service)
Jooyung Han347d9f22021-05-28 00:05:14 +0900262}
263
Inseob Kim217038e2021-11-25 11:15:06 +0900264struct ApkDmverityArgument<'a> {
265 apk: &'a str,
266 idsig: &'a str,
267 name: &'a str,
Inseob Kim197748b2021-12-01 19:49:00 +0900268 saved_root_hash: Option<&'a RootHash>,
Inseob Kim217038e2021-11-25 11:15:06 +0900269}
270
271fn run_apkdmverity(args: &[ApkDmverityArgument]) -> Result<Child> {
272 let mut cmd = Command::new(APKDMVERITY_BIN);
273
274 cmd.stdin(Stdio::null()).stdout(Stdio::null()).stderr(Stdio::null());
275
276 for argument in args {
277 cmd.arg("--apk").arg(argument.apk).arg(argument.idsig).arg(argument.name);
Inseob Kim197748b2021-12-01 19:49:00 +0900278 if let Some(root_hash) = argument.saved_root_hash {
279 cmd.arg(&to_hex_string(root_hash));
280 } else {
281 cmd.arg("none");
282 }
Inseob Kim217038e2021-11-25 11:15:06 +0900283 }
284
285 cmd.spawn().context("Spawn apkdmverity")
286}
287
288fn run_zipfuse(option: &str, zip_path: &Path, mount_dir: &Path) -> Result<Child> {
289 Command::new(ZIPFUSE_BIN)
290 .arg("-o")
291 .arg(option)
292 .arg(zip_path)
293 .arg(mount_dir)
294 .stdin(Stdio::null())
295 .stdout(Stdio::null())
296 .stderr(Stdio::null())
297 .spawn()
298 .context("Spawn zipfuse")
299}
300
Jooyung Han7a343f92021-09-08 22:53:11 +0900301// Verify payload before executing it. For APK payload, Full verification (which is slow) is done
302// when the root_hash values from the idsig file and the instance disk are different. This function
303// returns the verified root hash (for APK payload) and pubkeys (for APEX payloads) that can be
304// saved to the instance disk.
305fn verify_payload(
306 metadata: &Metadata,
307 saved_data: Option<&MicrodroidData>,
308) -> Result<MicrodroidData> {
Jiyong Parkbb4a9872021-09-06 15:59:21 +0900309 let start_time = SystemTime::now();
310
Jiyong Park9f72ea62021-12-06 21:18:38 +0900311 if let Some(saved_bootconfig) = saved_data.map(|d| &d.bootconfig) {
312 ensure!(
313 saved_bootconfig.as_ref() == get_bootconfig()?.as_slice(),
314 MicrodroidError::PayloadChanged(String::from("Bootconfig has changed."))
315 );
316 }
317
Inseob Kim197748b2021-12-01 19:49:00 +0900318 // Verify main APK
Jooyung Han7a343f92021-09-08 22:53:11 +0900319 let root_hash = saved_data.map(|d| &d.apk_data.root_hash);
Inseob Kim197748b2021-12-01 19:49:00 +0900320 let root_hash_from_idsig = get_apk_root_hash_from_idsig(MAIN_APK_IDSIG_PATH)?;
Jiyong Parkf7dea252021-09-08 01:42:54 +0900321 let root_hash_trustful = root_hash == Some(&root_hash_from_idsig);
Jiyong Parkbb4a9872021-09-06 15:59:21 +0900322
Jiyong Parkf7dea252021-09-08 01:42:54 +0900323 // 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 +0900324 // instead of the value read from the idsig file.
Inseob Kim197748b2021-12-01 19:49:00 +0900325 let main_apk_argument = {
326 ApkDmverityArgument {
327 apk: MAIN_APK_PATH,
328 idsig: MAIN_APK_IDSIG_PATH,
329 name: MAIN_APK_DEVICE_NAME,
330 saved_root_hash: if root_hash_trustful {
331 Some(root_hash_from_idsig.as_ref())
332 } else {
333 None
334 },
335 }
336 };
337 let mut apkdmverity_arguments = vec![main_apk_argument];
338
339 // Verify extra APKs
340 // For now, we can't read the payload config, so glob APKs and idsigs.
341 // Later, we'll see if it matches with the payload config.
342
343 // sort globbed paths to match apks (extra-apk-{idx}) and idsigs (extra-idsig-{idx})
344 // e.g. "extra-apk-0" corresponds to "extra-idsig-0"
345 let extra_apks =
346 sorted(glob(EXTRA_APK_PATH_PATTERN)?.collect::<Result<Vec<_>, _>>()?).collect::<Vec<_>>();
347 let extra_idsigs =
348 sorted(glob(EXTRA_IDSIG_PATH_PATTERN)?.collect::<Result<Vec<_>, _>>()?).collect::<Vec<_>>();
349 if extra_apks.len() != extra_idsigs.len() {
350 return Err(anyhow!(
351 "Extra apks/idsigs mismatch: {} apks but {} idsigs",
352 extra_apks.len(),
353 extra_idsigs.len()
354 ));
355 }
356 let extra_apks_count = extra_apks.len();
357
358 let (extra_apk_names, extra_root_hashes_from_idsig): (Vec<_>, Vec<_>) = extra_idsigs
359 .iter()
360 .enumerate()
361 .map(|(i, extra_idsig)| {
362 (
363 format!("extra-apk-{}", i),
364 get_apk_root_hash_from_idsig(extra_idsig.to_str().unwrap())
365 .expect("Can't find root hash from extra idsig"),
366 )
367 })
368 .unzip();
369
370 let saved_extra_root_hashes: Vec<_> = saved_data
371 .map(|d| d.extra_apks_data.iter().map(|apk_data| &apk_data.root_hash).collect())
372 .unwrap_or_else(Vec::new);
373 let extra_root_hashes_trustful: Vec<_> = extra_root_hashes_from_idsig
374 .iter()
375 .enumerate()
376 .map(|(i, root_hash_from_idsig)| {
377 saved_extra_root_hashes.get(i).copied() == Some(root_hash_from_idsig)
378 })
379 .collect();
380
381 for i in 0..extra_apks_count {
382 apkdmverity_arguments.push({
383 ApkDmverityArgument {
384 apk: extra_apks[i].to_str().unwrap(),
385 idsig: extra_idsigs[i].to_str().unwrap(),
386 name: &extra_apk_names[i],
387 saved_root_hash: if extra_root_hashes_trustful[i] {
388 Some(&extra_root_hashes_from_idsig[i])
389 } else {
390 None
391 },
392 }
393 });
Jiyong Parkbb4a9872021-09-06 15:59:21 +0900394 }
395
396 // Start apkdmverity and wait for the dm-verify block
Inseob Kim197748b2021-12-01 19:49:00 +0900397 let mut apkdmverity_child = run_apkdmverity(&apkdmverity_arguments)?;
Jooyung Han7a343f92021-09-08 22:53:11 +0900398
Jooyung Hanc8deb472021-09-13 13:48:25 +0900399 // While waiting for apkdmverity to mount APK, gathers public keys and root digests from
400 // APEX payload.
Jooyung Han7a343f92021-09-08 22:53:11 +0900401 let apex_data_from_payload = get_apex_data_from_payload(metadata)?;
Jooyung Han4a9b3bf2021-09-10 17:19:00 +0900402 if let Some(saved_data) = saved_data.map(|d| &d.apex_data) {
Jooyung Hanc8deb472021-09-13 13:48:25 +0900403 // We don't support APEX updates. (assuming that update will change root digest)
Jooyung Handd0a1732021-11-23 15:26:20 +0900404 ensure!(
405 saved_data == &apex_data_from_payload,
406 MicrodroidError::PayloadChanged(String::from("APEXes have changed."))
407 );
Jooyung Han4a9b3bf2021-09-10 17:19:00 +0900408 let apex_metadata = to_metadata(&apex_data_from_payload);
Jooyung Hanc8deb472021-09-13 13:48:25 +0900409 // Pass metadata(with public keys and root digests) to apexd so that it uses the passed
410 // metadata instead of the default one (/dev/block/by-name/payload-metadata)
Jooyung Han4a9b3bf2021-09-10 17:19:00 +0900411 OpenOptions::new()
412 .create_new(true)
413 .write(true)
414 .open("/apex/vm-payload-metadata")
415 .context("Failed to open /apex/vm-payload-metadata")
416 .and_then(|f| write_metadata(&apex_metadata, f))?;
417 }
418 // Start apexd to activate APEXes
419 system_properties::write("ctl.start", "apexd-vm")?;
Jooyung Han7a343f92021-09-08 22:53:11 +0900420
Inseob Kim217038e2021-11-25 11:15:06 +0900421 // TODO(inseob): add timeout
422 apkdmverity_child.wait()?;
Jooyung Han19c1d6c2021-08-06 14:08:16 +0900423
Jiyong Parkf7dea252021-09-08 01:42:54 +0900424 // Do the full verification if the root_hash is un-trustful. This requires the full scanning of
Jiyong Parkbb4a9872021-09-06 15:59:21 +0900425 // 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 +0900426 // 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 +0900427 // of the VM or APK was updated in the host.
428 // TODO(jooyung): consider multithreading to make this faster
Inseob Kim197748b2021-12-01 19:49:00 +0900429 let main_apk_pubkey = get_public_key_from_apk(DM_MOUNTED_APK_PATH, root_hash_trustful)?;
430 let extra_apks_data = extra_root_hashes_from_idsig
431 .into_iter()
432 .enumerate()
433 .map(|(i, extra_root_hash)| {
434 let mount_path = format!("/dev/block/mapper/{}", &extra_apk_names[i]);
435 let apk_pubkey = get_public_key_from_apk(&mount_path, extra_root_hashes_trustful[i])?;
436 Ok(ApkData { root_hash: extra_root_hash, pubkey: apk_pubkey })
437 })
438 .collect::<Result<Vec<_>>>()?;
Jiyong Parkbb4a9872021-09-06 15:59:21 +0900439
440 info!("payload verification successful. took {:#?}", start_time.elapsed().unwrap());
441
Andrew Scull34916a72022-01-30 21:34:24 +0000442 // Use the salt from a verified instance, or generate a salt for a new instance.
443 let salt = if let Some(saved_data) = saved_data {
444 saved_data.salt.clone()
445 } else {
446 let mut salt = vec![0u8; 64];
447 salt.as_mut_slice().try_fill(&mut rand::thread_rng())?;
448 salt
449 };
450
Jiyong Parkf7dea252021-09-08 01:42:54 +0900451 // At this point, we can ensure that the root_hash from the idsig file is trusted, either by
452 // fully verifying the APK or by comparing it with the saved root_hash.
Jooyung Han7a343f92021-09-08 22:53:11 +0900453 Ok(MicrodroidData {
Andrew Scull34916a72022-01-30 21:34:24 +0000454 salt,
Inseob Kim197748b2021-12-01 19:49:00 +0900455 apk_data: ApkData { root_hash: root_hash_from_idsig, pubkey: main_apk_pubkey },
456 extra_apks_data,
Jooyung Han7a343f92021-09-08 22:53:11 +0900457 apex_data: apex_data_from_payload,
Jiyong Park9f72ea62021-12-06 21:18:38 +0900458 bootconfig: get_bootconfig()?.clone().into_boxed_slice(),
Jooyung Han7a343f92021-09-08 22:53:11 +0900459 })
Jiyong Parkbb4a9872021-09-06 15:59:21 +0900460}
461
Inseob Kim197748b2021-12-01 19:49:00 +0900462fn mount_extra_apks(config: &VmPayloadConfig) -> Result<()> {
463 // For now, only the number of apks is important, as the mount point and dm-verity name is fixed
464 for i in 0..config.extra_apks.len() {
465 let mount_dir = format!("/mnt/extra-apk/{}", i);
466 create_dir(Path::new(&mount_dir)).context("Failed to create mount dir for extra apks")?;
467
468 // don't wait, just detach
469 run_zipfuse(
470 "fscontext=u:object_r:zipfusefs:s0,context=u:object_r:extra_apk_file:s0",
471 Path::new(&format!("/dev/block/mapper/extra-apk-{}", i)),
472 Path::new(&mount_dir),
473 )
474 .context("Failed to zipfuse extra apks")?;
475 }
476
477 Ok(())
478}
479
Jiyong Parkbb4a9872021-09-06 15:59:21 +0900480// Waits until linker config is generated
481fn wait_for_apex_config_done() -> Result<()> {
482 let mut prop = PropertyWatcher::new(APEX_CONFIG_DONE_PROP)?;
483 loop {
484 prop.wait()?;
485 let val = system_properties::read(APEX_CONFIG_DONE_PROP)?;
486 if val == "true" {
487 break;
488 }
489 }
Jooyung Han19c1d6c2021-08-06 14:08:16 +0900490 Ok(())
491}
492
Inseob Kim197748b2021-12-01 19:49:00 +0900493fn get_apk_root_hash_from_idsig(path: &str) -> Result<Box<RootHash>> {
494 let mut idsig = File::open(path)?;
Jiyong Park21ce2c52021-08-28 02:32:17 +0900495 let idsig = V4Signature::from(&mut idsig)?;
496 Ok(idsig.hashing_info.raw_root_hash)
497}
498
Inseob Kim197748b2021-12-01 19:49:00 +0900499fn get_public_key_from_apk(apk: &str, root_hash_trustful: bool) -> Result<Box<[u8]>> {
500 if !root_hash_trustful {
501 verify(apk).context(MicrodroidError::PayloadVerificationFailed(format!(
502 "failed to verify {}",
503 apk
504 )))
505 } else {
506 get_public_key_der(apk)
507 }
508}
509
Jiyong Park9f72ea62021-12-06 21:18:38 +0900510fn get_bootconfig() -> Result<&'static Vec<u8>> {
511 static VAL: OnceCell<Vec<u8>> = OnceCell::new();
Jiyong Park44e967d2021-12-21 13:53:42 +0900512 VAL.get_or_try_init(|| -> Result<Vec<u8>> {
513 let f = File::open("/proc/bootconfig")?;
514
515 // Filter-out androidboot.vbmeta.device which contains UUID of the vbmeta partition. That
516 // UUID could change everytime when the same VM is started because the composite disk image
517 // is ephemeral. A change in UUID is okay as long as other configs (e.g.
518 // androidboot.vbmeta.digest) remain same.
519 Ok(std::io::BufReader::new(f)
520 .lines()
521 // note: this try_fold is to early return when we fail to read a line from the file
522 .try_fold(Vec::new(), |mut lines, line| {
523 line.map(|s| {
524 lines.push(s);
525 lines
526 })
527 })?
528 .into_iter()
529 .filter(|line| {
530 let tokens: Vec<&str> = line.splitn(2, '=').collect();
531 // note: if `line` doesn't have =, tokens[0] is the entire line.
532 tokens[0].trim() != "androidboot.vbmeta.device"
533 })
534 .flat_map(|line| (line + "\n").into_bytes())
535 .collect())
536 })
Jiyong Park9f72ea62021-12-06 21:18:38 +0900537}
538
Jooyung Han634e2d72021-06-10 16:27:38 +0900539fn load_config(path: &Path) -> Result<VmPayloadConfig> {
540 info!("loading config from {:?}...", path);
541 let file = ioutil::wait_for_file(path, WAIT_TIMEOUT)?;
542 Ok(serde_json::from_reader(file)?)
543}
544
Jiyong Park8611a6c2021-07-09 18:17:44 +0900545/// Executes the given task. Stdout of the task is piped into the vsock stream to the
546/// virtualizationservice in the host side.
Jooyung Han5c6d4172021-12-06 14:17:52 +0900547fn exec_task(task: &Task, service: &Strong<dyn IVirtualMachineService>) -> Result<i32> {
Jiyong Park8611a6c2021-07-09 18:17:44 +0900548 info!("executing main task {:?}...", task);
Inseob Kim86ca0162021-10-20 02:21:02 +0000549 let mut command = build_command(task)?;
Inseob Kim7f61fe72021-08-20 20:50:47 +0900550
551 info!("notifying payload started");
Inseob Kimc7d28c72021-10-25 14:28:10 +0000552 service.notifyPayloadStarted()?;
Inseob Kim7f61fe72021-08-20 20:50:47 +0900553
Jiyong Parkfa91d702021-10-18 23:51:39 +0900554 // Start logging if enabled
555 // TODO(b/200914564) set filterspec if debug_level is app_only
556 if system_properties::read(LOGD_ENABLED_PROP)? == "1" {
557 system_properties::write("ctl.start", "seriallogging")?;
558 }
559
Inseob Kim86ca0162021-10-20 02:21:02 +0000560 let exit_status = command.spawn()?.wait()?;
Jooyung Han5c6d4172021-12-06 14:17:52 +0900561 exit_status.code().ok_or_else(|| anyhow!("Failed to get exit_code from the paylaod."))
Jooyung Han347d9f22021-05-28 00:05:14 +0900562}
Jooyung Han634e2d72021-06-10 16:27:38 +0900563
564fn build_command(task: &Task) -> Result<Command> {
Inseob Kim7f61fe72021-08-20 20:50:47 +0900565 const VMADDR_CID_HOST: u32 = 2;
Inseob Kim7f61fe72021-08-20 20:50:47 +0900566
567 let mut command = match task.type_ {
Jooyung Han634e2d72021-06-10 16:27:38 +0900568 TaskType::Executable => {
569 let mut command = Command::new(&task.command);
570 command.args(&task.args);
571 command
572 }
573 TaskType::MicrodroidLauncher => {
574 let mut command = Command::new("/system/bin/microdroid_launcher");
575 command.arg(find_library_path(&task.command)?).args(&task.args);
576 command
577 }
Inseob Kim7f61fe72021-08-20 20:50:47 +0900578 };
579
Inseob Kimd0587562021-09-01 21:27:32 +0900580 match VsockStream::connect_with_cid_port(VMADDR_CID_HOST, VM_STREAM_SERVICE_PORT as u32) {
Inseob Kim7f61fe72021-08-20 20:50:47 +0900581 Ok(stream) => {
582 // SAFETY: the ownership of the underlying file descriptor is transferred from stream
583 // to the file object, and then into the Command object. When the command is finished,
584 // the file descriptor is closed.
585 let file = unsafe { File::from_raw_fd(stream.into_raw_fd()) };
586 command
587 .stdin(Stdio::from(file.try_clone()?))
588 .stdout(Stdio::from(file.try_clone()?))
589 .stderr(Stdio::from(file));
590 }
591 Err(e) => {
592 error!("failed to connect to virtualization service: {}", e);
593 // Don't fail hard here. Even if we failed to connect to the virtualizationservice,
594 // we keep executing the task. This can happen if the owner of the VM doesn't register
595 // callback to accept the stream. Use /dev/null as the stream so that the task can
596 // make progress without waiting for someone to consume the output.
597 command.stdin(Stdio::null()).stdout(Stdio::null()).stderr(Stdio::null());
598 }
599 }
600
601 Ok(command)
Jooyung Han634e2d72021-06-10 16:27:38 +0900602}
603
604fn find_library_path(name: &str) -> Result<String> {
605 let mut watcher = PropertyWatcher::new("ro.product.cpu.abilist")?;
606 let value = watcher.read(|_name, value| Ok(value.trim().to_string()))?;
607 let abi = value.split(',').next().ok_or_else(|| anyhow!("no abilist"))?;
608 let path = format!("/mnt/apk/lib/{}/{}", abi, name);
609
610 let metadata = fs::metadata(&path)?;
611 if !metadata.is_file() {
612 bail!("{} is not a file", &path);
613 }
614
615 Ok(path)
616}
Jiyong Park21ce2c52021-08-28 02:32:17 +0900617
618fn to_hex_string(buf: &[u8]) -> String {
619 buf.iter().map(|b| format!("{:02X}", b)).collect()
620}