blob: b6442857cd8c1916dd5b04d355ee7416569955e7 [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};
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 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};
Inseob Kimc7d28c72021-10-25 14:28:10 +000044use std::os::unix::io::{FromRawFd, IntoRawFd};
Jooyung Hanf48ceb42021-06-01 18:00:04 +090045use std::path::Path;
Inseob Kim217038e2021-11-25 11:15:06 +090046use std::process::{Child, Command, Stdio};
Jiyong Park8611a6c2021-07-09 18:17:44 +090047use std::str;
Jiyong Parkbb4a9872021-09-06 15:59:21 +090048use std::time::{Duration, SystemTime};
Jiyong Park8611a6c2021-07-09 18:17:44 +090049use vsock::VsockStream;
Jooyung Han634e2d72021-06-10 16:27:38 +090050
Inseob Kimd0587562021-09-01 21:27:32 +090051use android_system_virtualmachineservice::aidl::android::system::virtualmachineservice::IVirtualMachineService::{
Jooyung Han5c6d4172021-12-06 14:17:52 +090052 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 +090053};
Inseob Kim1b95f2e2021-08-19 13:17:40 +090054
Jooyung Han634e2d72021-06-10 16:27:38 +090055const WAIT_TIMEOUT: Duration = Duration::from_secs(10);
Inseob Kim197748b2021-12-01 19:49:00 +090056const MAIN_APK_PATH: &str = "/dev/block/by-name/microdroid-apk";
57const MAIN_APK_IDSIG_PATH: &str = "/dev/block/by-name/microdroid-apk-idsig";
58const MAIN_APK_DEVICE_NAME: &str = "microdroid-apk";
59const EXTRA_APK_PATH_PATTERN: &str = "/dev/block/by-name/extra-apk-*";
60const EXTRA_IDSIG_PATH_PATTERN: &str = "/dev/block/by-name/extra-idsig-*";
Jooyung Han19c1d6c2021-08-06 14:08:16 +090061const DM_MOUNTED_APK_PATH: &str = "/dev/block/mapper/microdroid-apk";
Inseob Kim217038e2021-11-25 11:15:06 +090062const APKDMVERITY_BIN: &str = "/system/bin/apkdmverity";
63const ZIPFUSE_BIN: &str = "/system/bin/zipfuse";
Jooyung Han347d9f22021-05-28 00:05:14 +090064
Inseob Kim1b95f2e2021-08-19 13:17:40 +090065/// The CID representing the host VM
66const VMADDR_CID_HOST: u32 = 2;
67
Jiyong Parkbb4a9872021-09-06 15:59:21 +090068const APEX_CONFIG_DONE_PROP: &str = "apex_config.done";
Jiyong Parkfa91d702021-10-18 23:51:39 +090069const LOGD_ENABLED_PROP: &str = "ro.boot.logd.enabled";
Andrew Scull65ddfc42022-02-14 21:03:58 +000070const APP_DEBUGGABLE_PROP: &str = "ro.boot.microdroid.app_debuggable";
Jiyong Parkbb4a9872021-09-06 15:59:21 +090071
Jooyung Handd0a1732021-11-23 15:26:20 +090072#[derive(thiserror::Error, Debug)]
73enum MicrodroidError {
74 #[error("Payload has changed: {0}")]
75 PayloadChanged(String),
76 #[error("Payload verification has failed: {0}")]
77 PayloadVerificationFailed(String),
Jooyung Han5c6d4172021-12-06 14:17:52 +090078 #[error("Payload config is invalid: {0}")]
79 InvalidConfig(String),
Jooyung Handd0a1732021-11-23 15:26:20 +090080}
81
82fn translate_error(err: &Error) -> (i32, String) {
83 if let Some(e) = err.downcast_ref::<MicrodroidError>() {
84 match e {
85 MicrodroidError::PayloadChanged(msg) => (ERROR_PAYLOAD_CHANGED, msg.to_string()),
86 MicrodroidError::PayloadVerificationFailed(msg) => {
87 (ERROR_PAYLOAD_VERIFICATION_FAILED, msg.to_string())
88 }
Jooyung Han5c6d4172021-12-06 14:17:52 +090089 MicrodroidError::InvalidConfig(msg) => (ERROR_PAYLOAD_INVALID_CONFIG, msg.to_string()),
Jooyung Handd0a1732021-11-23 15:26:20 +090090 }
91 } else {
92 (ERROR_UNKNOWN, err.to_string())
93 }
94}
95
Inseob Kim1b95f2e2021-08-19 13:17:40 +090096fn get_vms_rpc_binder() -> Result<Strong<dyn IVirtualMachineService>> {
97 // SAFETY: AIBinder returned by RpcClient has correct reference count, and the ownership can be
98 // safely taken by new_spibinder.
99 let ibinder = unsafe {
100 new_spibinder(binder_rpc_unstable_bindgen::RpcClient(
101 VMADDR_CID_HOST,
Inseob Kimd0587562021-09-01 21:27:32 +0900102 VM_BINDER_SERVICE_PORT as u32,
Inseob Kim1b95f2e2021-08-19 13:17:40 +0900103 ) as *mut AIBinder)
104 };
105 if let Some(ibinder) = ibinder {
106 <dyn IVirtualMachineService>::try_from(ibinder).context("Cannot connect to RPC service")
107 } else {
108 bail!("Invalid raw AIBinder")
109 }
110}
111
Jooyung Han311b1202021-09-14 22:00:16 +0900112fn main() {
113 if let Err(e) = try_main() {
Jooyung Hanbfe086f2021-10-28 10:15:45 +0900114 error!("Failed with {:?}. Shutting down...", e);
115 if let Err(e) = system_properties::write("sys.powerctl", "shutdown") {
116 error!("failed to shutdown {:?}", e);
117 }
Jooyung Han311b1202021-09-14 22:00:16 +0900118 std::process::exit(1);
119 }
120}
121
122fn try_main() -> Result<()> {
Andrew Sculle127cff2022-02-15 15:34:04 +0000123 let _ = kernlog::init();
Jooyung Han347d9f22021-05-28 00:05:14 +0900124 info!("started.");
125
Jooyung Handd0a1732021-11-23 15:26:20 +0900126 let service = get_vms_rpc_binder().context("cannot connect to VirtualMachineService")?;
Jooyung Han5c6d4172021-12-06 14:17:52 +0900127 match try_run_payload(&service) {
128 Ok(code) => {
129 info!("notifying payload finished");
130 service.notifyPayloadFinished(code)?;
131 if code == 0 {
132 info!("task successfully finished");
133 } else {
134 error!("task exited with exit code: {}", code);
135 }
136 Ok(())
137 }
138 Err(err) => {
139 error!("task terminated: {:?}", err);
140 let (error_code, message) = translate_error(&err);
141 service.notifyError(error_code, &message)?;
142 Err(err)
143 }
Jooyung Handd0a1732021-11-23 15:26:20 +0900144 }
145}
146
Andrew Scullb2f44472022-01-21 14:41:34 +0000147fn dice_derivation(verified_data: MicrodroidData, payload_config_path: &str) -> Result<()> {
148 // Calculate compound digests of code and authorities
149 let mut code_hash_ctx = digest::Context::new(&digest::SHA512);
150 let mut authority_hash_ctx = digest::Context::new(&digest::SHA512);
151 code_hash_ctx.update(verified_data.apk_data.root_hash.as_ref());
152 authority_hash_ctx.update(verified_data.apk_data.pubkey.as_ref());
153 for extra_apk in verified_data.extra_apks_data {
154 code_hash_ctx.update(extra_apk.root_hash.as_ref());
155 authority_hash_ctx.update(extra_apk.pubkey.as_ref());
156 }
157 for apex in verified_data.apex_data {
158 code_hash_ctx.update(apex.root_digest.as_ref());
159 authority_hash_ctx.update(apex.public_key.as_ref());
160 }
161 let code_hash = code_hash_ctx.finish().as_ref().try_into().unwrap();
162 let authority_hash = authority_hash_ctx.finish().as_ref().try_into().unwrap();
163
164 // {
165 // -70002: "Microdroid payload",
166 // -71000: payload_config_path
167 // }
168 let mut config_desc = vec![
169 0xa2, 0x3a, 0x00, 0x01, 0x11, 0x71, 0x72, 0x4d, 0x69, 0x63, 0x72, 0x6f, 0x64, 0x72, 0x6f,
170 0x69, 0x64, 0x20, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x3a, 0x00, 0x01, 0x15, 0x57,
171 ];
172 let config_path_bytes = payload_config_path.as_bytes();
173 encode_header(3, config_path_bytes.len().try_into().unwrap(), &mut config_desc)?;
174 config_desc.extend_from_slice(config_path_bytes);
175
Andrew Scull65ddfc42022-02-14 21:03:58 +0000176 // Check app debuggability, conervatively assuming it is debuggable
177 let app_debuggable = system_properties::read_bool(APP_DEBUGGABLE_PROP, true)?;
178
Andrew Scullb2f44472022-01-21 14:41:34 +0000179 // Send the details to diced
180 let diced =
181 wait_for_interface::<dyn IDiceMaintenance>("android.security.dice.IDiceMaintenance")
182 .context("IDiceMaintenance service not found")?;
183 diced
184 .demoteSelf(&[InputValues {
185 codeHash: code_hash,
186 config: Config { desc: config_desc },
187 authorityHash: authority_hash,
188 authorityDescriptor: None,
Andrew Scull65ddfc42022-02-14 21:03:58 +0000189 mode: if app_debuggable { Mode::DEBUG } else { Mode::NORMAL },
Andrew Scull34916a72022-01-30 21:34:24 +0000190 hidden: verified_data.salt.try_into().unwrap(),
Andrew Scullb2f44472022-01-21 14:41:34 +0000191 }])
192 .context("IDiceMaintenance::demoteSelf failed")?;
193 Ok(())
194}
195
Jooyung Han5c6d4172021-12-06 14:17:52 +0900196fn try_run_payload(service: &Strong<dyn IVirtualMachineService>) -> Result<i32> {
Jooyung Han311b1202021-09-14 22:00:16 +0900197 let metadata = load_metadata().context("Failed to load payload metadata")?;
Jooyung Han19c1d6c2021-08-06 14:08:16 +0900198
Jooyung Han311b1202021-09-14 22:00:16 +0900199 let mut instance = InstanceDisk::new().context("Failed to load instance.img")?;
Jooyung Han7a343f92021-09-08 22:53:11 +0900200 let saved_data = instance.read_microdroid_data().context("Failed to read identity data")?;
Jiyong Parkbb4a9872021-09-06 15:59:21 +0900201
202 // Verify the payload before using it.
Jooyung Han7a343f92021-09-08 22:53:11 +0900203 let verified_data =
204 verify_payload(&metadata, saved_data.as_ref()).context("Payload verification failed")?;
205 if let Some(saved_data) = saved_data {
Jooyung Handd0a1732021-11-23 15:26:20 +0900206 ensure!(
207 saved_data == verified_data,
208 MicrodroidError::PayloadChanged(String::from(
209 "Detected an update of the payload which isn't supported yet."
210 ))
211 );
212 info!("Saved data is verified.");
Jiyong Parkbb4a9872021-09-06 15:59:21 +0900213 } else {
Jooyung Han7a343f92021-09-08 22:53:11 +0900214 info!("Saving verified data.");
215 instance.write_microdroid_data(&verified_data).context("Failed to write identity data")?;
Jooyung Han19c1d6c2021-08-06 14:08:16 +0900216 }
217
Jooyung Hana6d11eb2021-09-10 11:48:05 +0900218 // Before reading a file from the APK, start zipfuse
Inseob Kim217038e2021-11-25 11:15:06 +0900219 run_zipfuse(
220 "fscontext=u:object_r:zipfusefs:s0,context=u:object_r:system_file:s0",
221 Path::new("/dev/block/mapper/microdroid-apk"),
222 Path::new("/mnt/apk"),
223 )
224 .context("Failed to run zipfuse")?;
Jiyong Park21ce2c52021-08-28 02:32:17 +0900225
Jooyung Han5c6d4172021-12-06 14:17:52 +0900226 ensure!(
227 !metadata.payload_config_path.is_empty(),
228 MicrodroidError::InvalidConfig("No payload_config_path in metadata".to_string())
229 );
Inseob Kim197748b2021-12-01 19:49:00 +0900230
Jooyung Han5c6d4172021-12-06 14:17:52 +0900231 let config = load_config(Path::new(&metadata.payload_config_path))?;
Inseob Kim197748b2021-12-01 19:49:00 +0900232 if config.extra_apks.len() != verified_data.extra_apks_data.len() {
233 return Err(anyhow!(
234 "config expects {} extra apks, but found only {}",
235 config.extra_apks.len(),
236 verified_data.extra_apks_data.len()
237 ));
238 }
239 mount_extra_apks(&config)?;
Jooyung Han634e2d72021-06-10 16:27:38 +0900240
Andrew Scullb2f44472022-01-21 14:41:34 +0000241 info!("DICE derivation for payload");
242 dice_derivation(verified_data, &metadata.payload_config_path)?;
243
Jooyung Han5c6d4172021-12-06 14:17:52 +0900244 // Wait until apex config is done. (e.g. linker configuration for apexes)
245 // TODO(jooyung): wait until sys.boot_completed?
246 wait_for_apex_config_done()?;
247
248 ensure!(
249 config.task.is_some(),
250 MicrodroidError::InvalidConfig("No task in VM config".to_string())
251 );
252 exec_task(&config.task.unwrap(), service)
Jooyung Han347d9f22021-05-28 00:05:14 +0900253}
254
Inseob Kim217038e2021-11-25 11:15:06 +0900255struct ApkDmverityArgument<'a> {
256 apk: &'a str,
257 idsig: &'a str,
258 name: &'a str,
Inseob Kim197748b2021-12-01 19:49:00 +0900259 saved_root_hash: Option<&'a RootHash>,
Inseob Kim217038e2021-11-25 11:15:06 +0900260}
261
262fn run_apkdmverity(args: &[ApkDmverityArgument]) -> Result<Child> {
263 let mut cmd = Command::new(APKDMVERITY_BIN);
264
265 cmd.stdin(Stdio::null()).stdout(Stdio::null()).stderr(Stdio::null());
266
267 for argument in args {
268 cmd.arg("--apk").arg(argument.apk).arg(argument.idsig).arg(argument.name);
Inseob Kim197748b2021-12-01 19:49:00 +0900269 if let Some(root_hash) = argument.saved_root_hash {
270 cmd.arg(&to_hex_string(root_hash));
271 } else {
272 cmd.arg("none");
273 }
Inseob Kim217038e2021-11-25 11:15:06 +0900274 }
275
276 cmd.spawn().context("Spawn apkdmverity")
277}
278
279fn run_zipfuse(option: &str, zip_path: &Path, mount_dir: &Path) -> Result<Child> {
280 Command::new(ZIPFUSE_BIN)
281 .arg("-o")
282 .arg(option)
283 .arg(zip_path)
284 .arg(mount_dir)
285 .stdin(Stdio::null())
286 .stdout(Stdio::null())
287 .stderr(Stdio::null())
288 .spawn()
289 .context("Spawn zipfuse")
290}
291
Jooyung Han7a343f92021-09-08 22:53:11 +0900292// Verify payload before executing it. For APK payload, Full verification (which is slow) is done
293// when the root_hash values from the idsig file and the instance disk are different. This function
294// returns the verified root hash (for APK payload) and pubkeys (for APEX payloads) that can be
295// saved to the instance disk.
296fn verify_payload(
297 metadata: &Metadata,
298 saved_data: Option<&MicrodroidData>,
299) -> Result<MicrodroidData> {
Jiyong Parkbb4a9872021-09-06 15:59:21 +0900300 let start_time = SystemTime::now();
301
Inseob Kim197748b2021-12-01 19:49:00 +0900302 // Verify main APK
Jooyung Han7a343f92021-09-08 22:53:11 +0900303 let root_hash = saved_data.map(|d| &d.apk_data.root_hash);
Inseob Kim197748b2021-12-01 19:49:00 +0900304 let root_hash_from_idsig = get_apk_root_hash_from_idsig(MAIN_APK_IDSIG_PATH)?;
Jiyong Parkf7dea252021-09-08 01:42:54 +0900305 let root_hash_trustful = root_hash == Some(&root_hash_from_idsig);
Jiyong Parkbb4a9872021-09-06 15:59:21 +0900306
Jiyong Parkf7dea252021-09-08 01:42:54 +0900307 // 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 +0900308 // instead of the value read from the idsig file.
Inseob Kim197748b2021-12-01 19:49:00 +0900309 let main_apk_argument = {
310 ApkDmverityArgument {
311 apk: MAIN_APK_PATH,
312 idsig: MAIN_APK_IDSIG_PATH,
313 name: MAIN_APK_DEVICE_NAME,
314 saved_root_hash: if root_hash_trustful {
315 Some(root_hash_from_idsig.as_ref())
316 } else {
317 None
318 },
319 }
320 };
321 let mut apkdmverity_arguments = vec![main_apk_argument];
322
323 // Verify extra APKs
324 // For now, we can't read the payload config, so glob APKs and idsigs.
325 // Later, we'll see if it matches with the payload config.
326
327 // sort globbed paths to match apks (extra-apk-{idx}) and idsigs (extra-idsig-{idx})
328 // e.g. "extra-apk-0" corresponds to "extra-idsig-0"
329 let extra_apks =
330 sorted(glob(EXTRA_APK_PATH_PATTERN)?.collect::<Result<Vec<_>, _>>()?).collect::<Vec<_>>();
331 let extra_idsigs =
332 sorted(glob(EXTRA_IDSIG_PATH_PATTERN)?.collect::<Result<Vec<_>, _>>()?).collect::<Vec<_>>();
333 if extra_apks.len() != extra_idsigs.len() {
334 return Err(anyhow!(
335 "Extra apks/idsigs mismatch: {} apks but {} idsigs",
336 extra_apks.len(),
337 extra_idsigs.len()
338 ));
339 }
340 let extra_apks_count = extra_apks.len();
341
342 let (extra_apk_names, extra_root_hashes_from_idsig): (Vec<_>, Vec<_>) = extra_idsigs
343 .iter()
344 .enumerate()
345 .map(|(i, extra_idsig)| {
346 (
347 format!("extra-apk-{}", i),
348 get_apk_root_hash_from_idsig(extra_idsig.to_str().unwrap())
349 .expect("Can't find root hash from extra idsig"),
350 )
351 })
352 .unzip();
353
354 let saved_extra_root_hashes: Vec<_> = saved_data
355 .map(|d| d.extra_apks_data.iter().map(|apk_data| &apk_data.root_hash).collect())
356 .unwrap_or_else(Vec::new);
357 let extra_root_hashes_trustful: Vec<_> = extra_root_hashes_from_idsig
358 .iter()
359 .enumerate()
360 .map(|(i, root_hash_from_idsig)| {
361 saved_extra_root_hashes.get(i).copied() == Some(root_hash_from_idsig)
362 })
363 .collect();
364
365 for i in 0..extra_apks_count {
366 apkdmverity_arguments.push({
367 ApkDmverityArgument {
368 apk: extra_apks[i].to_str().unwrap(),
369 idsig: extra_idsigs[i].to_str().unwrap(),
370 name: &extra_apk_names[i],
371 saved_root_hash: if extra_root_hashes_trustful[i] {
372 Some(&extra_root_hashes_from_idsig[i])
373 } else {
374 None
375 },
376 }
377 });
Jiyong Parkbb4a9872021-09-06 15:59:21 +0900378 }
379
380 // Start apkdmverity and wait for the dm-verify block
Inseob Kim197748b2021-12-01 19:49:00 +0900381 let mut apkdmverity_child = run_apkdmverity(&apkdmverity_arguments)?;
Jooyung Han7a343f92021-09-08 22:53:11 +0900382
Jooyung Hanc8deb472021-09-13 13:48:25 +0900383 // While waiting for apkdmverity to mount APK, gathers public keys and root digests from
384 // APEX payload.
Jooyung Han7a343f92021-09-08 22:53:11 +0900385 let apex_data_from_payload = get_apex_data_from_payload(metadata)?;
Jooyung Han4a9b3bf2021-09-10 17:19:00 +0900386 if let Some(saved_data) = saved_data.map(|d| &d.apex_data) {
Jooyung Hanc8deb472021-09-13 13:48:25 +0900387 // We don't support APEX updates. (assuming that update will change root digest)
Jooyung Handd0a1732021-11-23 15:26:20 +0900388 ensure!(
389 saved_data == &apex_data_from_payload,
390 MicrodroidError::PayloadChanged(String::from("APEXes have changed."))
391 );
Jooyung Han4a9b3bf2021-09-10 17:19:00 +0900392 let apex_metadata = to_metadata(&apex_data_from_payload);
Jooyung Hanc8deb472021-09-13 13:48:25 +0900393 // Pass metadata(with public keys and root digests) to apexd so that it uses the passed
394 // metadata instead of the default one (/dev/block/by-name/payload-metadata)
Jooyung Han4a9b3bf2021-09-10 17:19:00 +0900395 OpenOptions::new()
396 .create_new(true)
397 .write(true)
398 .open("/apex/vm-payload-metadata")
399 .context("Failed to open /apex/vm-payload-metadata")
400 .and_then(|f| write_metadata(&apex_metadata, f))?;
401 }
402 // Start apexd to activate APEXes
403 system_properties::write("ctl.start", "apexd-vm")?;
Jooyung Han7a343f92021-09-08 22:53:11 +0900404
Inseob Kim217038e2021-11-25 11:15:06 +0900405 // TODO(inseob): add timeout
406 apkdmverity_child.wait()?;
Jooyung Han19c1d6c2021-08-06 14:08:16 +0900407
Jiyong Parkf7dea252021-09-08 01:42:54 +0900408 // Do the full verification if the root_hash is un-trustful. This requires the full scanning of
Jiyong Parkbb4a9872021-09-06 15:59:21 +0900409 // 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 +0900410 // 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 +0900411 // of the VM or APK was updated in the host.
412 // TODO(jooyung): consider multithreading to make this faster
Inseob Kim197748b2021-12-01 19:49:00 +0900413 let main_apk_pubkey = get_public_key_from_apk(DM_MOUNTED_APK_PATH, root_hash_trustful)?;
414 let extra_apks_data = extra_root_hashes_from_idsig
415 .into_iter()
416 .enumerate()
417 .map(|(i, extra_root_hash)| {
418 let mount_path = format!("/dev/block/mapper/{}", &extra_apk_names[i]);
419 let apk_pubkey = get_public_key_from_apk(&mount_path, extra_root_hashes_trustful[i])?;
420 Ok(ApkData { root_hash: extra_root_hash, pubkey: apk_pubkey })
421 })
422 .collect::<Result<Vec<_>>>()?;
Jiyong Parkbb4a9872021-09-06 15:59:21 +0900423
424 info!("payload verification successful. took {:#?}", start_time.elapsed().unwrap());
425
Andrew Scull34916a72022-01-30 21:34:24 +0000426 // Use the salt from a verified instance, or generate a salt for a new instance.
427 let salt = if let Some(saved_data) = saved_data {
428 saved_data.salt.clone()
429 } else {
430 let mut salt = vec![0u8; 64];
431 salt.as_mut_slice().try_fill(&mut rand::thread_rng())?;
432 salt
433 };
434
Jiyong Parkf7dea252021-09-08 01:42:54 +0900435 // At this point, we can ensure that the root_hash from the idsig file is trusted, either by
436 // fully verifying the APK or by comparing it with the saved root_hash.
Jooyung Han7a343f92021-09-08 22:53:11 +0900437 Ok(MicrodroidData {
Andrew Scull34916a72022-01-30 21:34:24 +0000438 salt,
Inseob Kim197748b2021-12-01 19:49:00 +0900439 apk_data: ApkData { root_hash: root_hash_from_idsig, pubkey: main_apk_pubkey },
440 extra_apks_data,
Jooyung Han7a343f92021-09-08 22:53:11 +0900441 apex_data: apex_data_from_payload,
442 })
Jiyong Parkbb4a9872021-09-06 15:59:21 +0900443}
444
Inseob Kim197748b2021-12-01 19:49:00 +0900445fn mount_extra_apks(config: &VmPayloadConfig) -> Result<()> {
446 // For now, only the number of apks is important, as the mount point and dm-verity name is fixed
447 for i in 0..config.extra_apks.len() {
448 let mount_dir = format!("/mnt/extra-apk/{}", i);
449 create_dir(Path::new(&mount_dir)).context("Failed to create mount dir for extra apks")?;
450
451 // don't wait, just detach
452 run_zipfuse(
453 "fscontext=u:object_r:zipfusefs:s0,context=u:object_r:extra_apk_file:s0",
454 Path::new(&format!("/dev/block/mapper/extra-apk-{}", i)),
455 Path::new(&mount_dir),
456 )
457 .context("Failed to zipfuse extra apks")?;
458 }
459
460 Ok(())
461}
462
Jiyong Parkbb4a9872021-09-06 15:59:21 +0900463// Waits until linker config is generated
464fn wait_for_apex_config_done() -> Result<()> {
465 let mut prop = PropertyWatcher::new(APEX_CONFIG_DONE_PROP)?;
466 loop {
467 prop.wait()?;
Andrew Scull4b5369f2022-01-28 13:47:20 +0000468 if system_properties::read_bool(APEX_CONFIG_DONE_PROP, false)? {
Jiyong Parkbb4a9872021-09-06 15:59:21 +0900469 break;
470 }
471 }
Jooyung Han19c1d6c2021-08-06 14:08:16 +0900472 Ok(())
473}
474
Inseob Kim197748b2021-12-01 19:49:00 +0900475fn get_apk_root_hash_from_idsig(path: &str) -> Result<Box<RootHash>> {
476 let mut idsig = File::open(path)?;
Jiyong Park21ce2c52021-08-28 02:32:17 +0900477 let idsig = V4Signature::from(&mut idsig)?;
478 Ok(idsig.hashing_info.raw_root_hash)
479}
480
Inseob Kim197748b2021-12-01 19:49:00 +0900481fn get_public_key_from_apk(apk: &str, root_hash_trustful: bool) -> Result<Box<[u8]>> {
482 if !root_hash_trustful {
483 verify(apk).context(MicrodroidError::PayloadVerificationFailed(format!(
484 "failed to verify {}",
485 apk
486 )))
487 } else {
488 get_public_key_der(apk)
489 }
490}
491
Jooyung Han634e2d72021-06-10 16:27:38 +0900492fn load_config(path: &Path) -> Result<VmPayloadConfig> {
493 info!("loading config from {:?}...", path);
494 let file = ioutil::wait_for_file(path, WAIT_TIMEOUT)?;
495 Ok(serde_json::from_reader(file)?)
496}
497
Jiyong Park8611a6c2021-07-09 18:17:44 +0900498/// Executes the given task. Stdout of the task is piped into the vsock stream to the
499/// virtualizationservice in the host side.
Jooyung Han5c6d4172021-12-06 14:17:52 +0900500fn exec_task(task: &Task, service: &Strong<dyn IVirtualMachineService>) -> Result<i32> {
Jiyong Park8611a6c2021-07-09 18:17:44 +0900501 info!("executing main task {:?}...", task);
Inseob Kim86ca0162021-10-20 02:21:02 +0000502 let mut command = build_command(task)?;
Inseob Kim7f61fe72021-08-20 20:50:47 +0900503
504 info!("notifying payload started");
Inseob Kimc7d28c72021-10-25 14:28:10 +0000505 service.notifyPayloadStarted()?;
Inseob Kim7f61fe72021-08-20 20:50:47 +0900506
Jiyong Parkfa91d702021-10-18 23:51:39 +0900507 // Start logging if enabled
508 // TODO(b/200914564) set filterspec if debug_level is app_only
Andrew Scull4b5369f2022-01-28 13:47:20 +0000509 if system_properties::read_bool(LOGD_ENABLED_PROP, false)? {
Jiyong Parkfa91d702021-10-18 23:51:39 +0900510 system_properties::write("ctl.start", "seriallogging")?;
511 }
512
Inseob Kim86ca0162021-10-20 02:21:02 +0000513 let exit_status = command.spawn()?.wait()?;
Jooyung Han5c6d4172021-12-06 14:17:52 +0900514 exit_status.code().ok_or_else(|| anyhow!("Failed to get exit_code from the paylaod."))
Jooyung Han347d9f22021-05-28 00:05:14 +0900515}
Jooyung Han634e2d72021-06-10 16:27:38 +0900516
517fn build_command(task: &Task) -> Result<Command> {
Inseob Kim7f61fe72021-08-20 20:50:47 +0900518 const VMADDR_CID_HOST: u32 = 2;
Inseob Kim7f61fe72021-08-20 20:50:47 +0900519
520 let mut command = match task.type_ {
Jooyung Han634e2d72021-06-10 16:27:38 +0900521 TaskType::Executable => {
522 let mut command = Command::new(&task.command);
523 command.args(&task.args);
524 command
525 }
526 TaskType::MicrodroidLauncher => {
527 let mut command = Command::new("/system/bin/microdroid_launcher");
528 command.arg(find_library_path(&task.command)?).args(&task.args);
529 command
530 }
Inseob Kim7f61fe72021-08-20 20:50:47 +0900531 };
532
Inseob Kimd0587562021-09-01 21:27:32 +0900533 match VsockStream::connect_with_cid_port(VMADDR_CID_HOST, VM_STREAM_SERVICE_PORT as u32) {
Inseob Kim7f61fe72021-08-20 20:50:47 +0900534 Ok(stream) => {
535 // SAFETY: the ownership of the underlying file descriptor is transferred from stream
536 // to the file object, and then into the Command object. When the command is finished,
537 // the file descriptor is closed.
538 let file = unsafe { File::from_raw_fd(stream.into_raw_fd()) };
539 command
540 .stdin(Stdio::from(file.try_clone()?))
541 .stdout(Stdio::from(file.try_clone()?))
542 .stderr(Stdio::from(file));
543 }
544 Err(e) => {
545 error!("failed to connect to virtualization service: {}", e);
546 // Don't fail hard here. Even if we failed to connect to the virtualizationservice,
547 // we keep executing the task. This can happen if the owner of the VM doesn't register
548 // callback to accept the stream. Use /dev/null as the stream so that the task can
549 // make progress without waiting for someone to consume the output.
550 command.stdin(Stdio::null()).stdout(Stdio::null()).stderr(Stdio::null());
551 }
552 }
553
554 Ok(command)
Jooyung Han634e2d72021-06-10 16:27:38 +0900555}
556
557fn find_library_path(name: &str) -> Result<String> {
558 let mut watcher = PropertyWatcher::new("ro.product.cpu.abilist")?;
559 let value = watcher.read(|_name, value| Ok(value.trim().to_string()))?;
560 let abi = value.split(',').next().ok_or_else(|| anyhow!("no abilist"))?;
561 let path = format!("/mnt/apk/lib/{}/{}", abi, name);
562
563 let metadata = fs::metadata(&path)?;
564 if !metadata.is_file() {
565 bail!("{} is not a file", &path);
566 }
567
568 Ok(path)
569}
Jiyong Park21ce2c52021-08-28 02:32:17 +0900570
571fn to_hex_string(buf: &[u8]) -> String {
572 buf.iter().map(|b| format!("{:02X}", b)).collect()
573}