blob: 929a96b7c81509c7fbd9c3397cec441adc9ee0de [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};
Andrew Sculla0d1b1a2022-05-24 19:32:47 +000037use openssl::sha::Sha512;
Jooyung Han4a9b3bf2021-09-10 17:19:00 +090038use payload::{get_apex_data_from_payload, load_metadata, to_metadata};
Andrew Scull34916a72022-01-30 21:34:24 +000039use rand::Fill;
Jiyong Parkbb4a9872021-09-06 15:59:21 +090040use rustutils::system_properties;
Joel Galenson482704c2021-07-29 15:53:53 -070041use rustutils::system_properties::PropertyWatcher;
Andrew Scullb2f44472022-01-21 14:41:34 +000042use std::convert::TryInto;
Inseob Kim197748b2021-12-01 19:49:00 +090043use std::fs::{self, create_dir, File, OpenOptions};
Inseob 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";
Andrew Scullab72ec52022-03-14 09:10:52 +000064const AVF_STRICT_BOOT: &str = "/sys/firmware/devicetree/base/chosen/avf,strict-boot";
65const AVF_NEW_INSTANCE: &str = "/sys/firmware/devicetree/base/chosen/avf,new-instance";
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 Scull65ddfc42022-02-14 21:03:58 +000072const APP_DEBUGGABLE_PROP: &str = "ro.boot.microdroid.app_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<()> {
Andrew Sculle127cff2022-02-15 15:34:04 +0000125 let _ = 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
Inseob Kimb2519c52022-04-14 02:10:09 +0900149fn dice_derivation(verified_data: &MicrodroidData, payload_config_path: &str) -> Result<()> {
Andrew Scullb2f44472022-01-21 14:41:34 +0000150 // Calculate compound digests of code and authorities
Andrew Sculla0d1b1a2022-05-24 19:32:47 +0000151 let mut code_hash_ctx = Sha512::new();
152 let mut authority_hash_ctx = Sha512::new();
Andrew Scullb2f44472022-01-21 14:41:34 +0000153 code_hash_ctx.update(verified_data.apk_data.root_hash.as_ref());
154 authority_hash_ctx.update(verified_data.apk_data.pubkey.as_ref());
Inseob Kimb2519c52022-04-14 02:10:09 +0900155 for extra_apk in &verified_data.extra_apks_data {
Andrew Scullb2f44472022-01-21 14:41:34 +0000156 code_hash_ctx.update(extra_apk.root_hash.as_ref());
157 authority_hash_ctx.update(extra_apk.pubkey.as_ref());
158 }
Inseob Kimb2519c52022-04-14 02:10:09 +0900159 for apex in &verified_data.apex_data {
Andrew Scullb2f44472022-01-21 14:41:34 +0000160 code_hash_ctx.update(apex.root_digest.as_ref());
161 authority_hash_ctx.update(apex.public_key.as_ref());
162 }
Andrew Sculla0d1b1a2022-05-24 19:32:47 +0000163 let code_hash = code_hash_ctx.finish();
164 let authority_hash = authority_hash_ctx.finish();
Andrew Scullb2f44472022-01-21 14:41:34 +0000165
166 // {
167 // -70002: "Microdroid payload",
168 // -71000: payload_config_path
169 // }
170 let mut config_desc = vec![
171 0xa2, 0x3a, 0x00, 0x01, 0x11, 0x71, 0x72, 0x4d, 0x69, 0x63, 0x72, 0x6f, 0x64, 0x72, 0x6f,
172 0x69, 0x64, 0x20, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x3a, 0x00, 0x01, 0x15, 0x57,
173 ];
174 let config_path_bytes = payload_config_path.as_bytes();
175 encode_header(3, config_path_bytes.len().try_into().unwrap(), &mut config_desc)?;
176 config_desc.extend_from_slice(config_path_bytes);
177
Andrew Scull65ddfc42022-02-14 21:03:58 +0000178 // Check app debuggability, conervatively assuming it is debuggable
179 let app_debuggable = system_properties::read_bool(APP_DEBUGGABLE_PROP, true)?;
180
Andrew Scullb2f44472022-01-21 14:41:34 +0000181 // Send the details to diced
182 let diced =
183 wait_for_interface::<dyn IDiceMaintenance>("android.security.dice.IDiceMaintenance")
184 .context("IDiceMaintenance service not found")?;
185 diced
186 .demoteSelf(&[InputValues {
187 codeHash: code_hash,
188 config: Config { desc: config_desc },
189 authorityHash: authority_hash,
190 authorityDescriptor: None,
Andrew Scull65ddfc42022-02-14 21:03:58 +0000191 mode: if app_debuggable { Mode::DEBUG } else { Mode::NORMAL },
Inseob Kimb2519c52022-04-14 02:10:09 +0900192 hidden: verified_data.salt.clone().try_into().unwrap(),
Andrew Scullb2f44472022-01-21 14:41:34 +0000193 }])
194 .context("IDiceMaintenance::demoteSelf failed")?;
195 Ok(())
196}
197
Andrew Scullab72ec52022-03-14 09:10:52 +0000198fn is_strict_boot() -> bool {
199 Path::new(AVF_STRICT_BOOT).exists()
200}
201
202fn is_new_instance() -> bool {
203 Path::new(AVF_NEW_INSTANCE).exists()
204}
205
Jooyung Han5c6d4172021-12-06 14:17:52 +0900206fn try_run_payload(service: &Strong<dyn IVirtualMachineService>) -> Result<i32> {
Jooyung Han311b1202021-09-14 22:00:16 +0900207 let metadata = load_metadata().context("Failed to load payload metadata")?;
Jooyung Han19c1d6c2021-08-06 14:08:16 +0900208
Jooyung Han311b1202021-09-14 22:00:16 +0900209 let mut instance = InstanceDisk::new().context("Failed to load instance.img")?;
Jooyung Han7a343f92021-09-08 22:53:11 +0900210 let saved_data = instance.read_microdroid_data().context("Failed to read identity data")?;
Jiyong Parkbb4a9872021-09-06 15:59:21 +0900211
Andrew Scullab72ec52022-03-14 09:10:52 +0000212 if is_strict_boot() {
213 // Provisioning must happen on the first boot and never again.
214 if is_new_instance() {
215 ensure!(
216 saved_data.is_none(),
217 MicrodroidError::InvalidConfig("Found instance data on first boot.".to_string())
218 );
219 } else {
220 ensure!(
221 saved_data.is_some(),
222 MicrodroidError::InvalidConfig("Instance data not found.".to_string())
223 );
224 };
225 }
226
Jiyong Parkbb4a9872021-09-06 15:59:21 +0900227 // Verify the payload before using it.
Jooyung Han7a343f92021-09-08 22:53:11 +0900228 let verified_data =
229 verify_payload(&metadata, saved_data.as_ref()).context("Payload verification failed")?;
230 if let Some(saved_data) = saved_data {
Jooyung Handd0a1732021-11-23 15:26:20 +0900231 ensure!(
232 saved_data == verified_data,
233 MicrodroidError::PayloadChanged(String::from(
234 "Detected an update of the payload which isn't supported yet."
235 ))
236 );
237 info!("Saved data is verified.");
Jiyong Parkbb4a9872021-09-06 15:59:21 +0900238 } else {
Jooyung Han7a343f92021-09-08 22:53:11 +0900239 info!("Saving verified data.");
240 instance.write_microdroid_data(&verified_data).context("Failed to write identity data")?;
Jooyung Han19c1d6c2021-08-06 14:08:16 +0900241 }
242
Inseob Kimb2519c52022-04-14 02:10:09 +0900243 // To minimize the exposure to untrusted data, derive dice profile as soon as possible.
244 info!("DICE derivation for payload");
245 dice_derivation(&verified_data, &metadata.payload_config_path)?;
246
Jooyung Hana6d11eb2021-09-10 11:48:05 +0900247 // Before reading a file from the APK, start zipfuse
Inseob Kim217038e2021-11-25 11:15:06 +0900248 run_zipfuse(
249 "fscontext=u:object_r:zipfusefs:s0,context=u:object_r:system_file:s0",
250 Path::new("/dev/block/mapper/microdroid-apk"),
251 Path::new("/mnt/apk"),
252 )
253 .context("Failed to run zipfuse")?;
Jiyong Park21ce2c52021-08-28 02:32:17 +0900254
Jooyung Han5c6d4172021-12-06 14:17:52 +0900255 ensure!(
256 !metadata.payload_config_path.is_empty(),
257 MicrodroidError::InvalidConfig("No payload_config_path in metadata".to_string())
258 );
Inseob Kim197748b2021-12-01 19:49:00 +0900259
Jooyung Han5c6d4172021-12-06 14:17:52 +0900260 let config = load_config(Path::new(&metadata.payload_config_path))?;
Shikha Panwar6f03c942022-04-13 20:26:50 +0000261
262 // Start tombstone_transmit if enabled
263 if config.export_tombstones {
264 system_properties::write("ctl.start", "tombstone_transmit")
265 .context("Failed to start tombstone_transmit")?;
266 }
267
Inseob Kim197748b2021-12-01 19:49:00 +0900268 if config.extra_apks.len() != verified_data.extra_apks_data.len() {
269 return Err(anyhow!(
270 "config expects {} extra apks, but found only {}",
271 config.extra_apks.len(),
272 verified_data.extra_apks_data.len()
273 ));
274 }
275 mount_extra_apks(&config)?;
Jooyung Han634e2d72021-06-10 16:27:38 +0900276
Jooyung Han5c6d4172021-12-06 14:17:52 +0900277 // Wait until apex config is done. (e.g. linker configuration for apexes)
278 // TODO(jooyung): wait until sys.boot_completed?
279 wait_for_apex_config_done()?;
280
281 ensure!(
282 config.task.is_some(),
283 MicrodroidError::InvalidConfig("No task in VM config".to_string())
284 );
Victor Hsieh8d006ba2022-05-02 09:42:02 -0700285 system_properties::write("dev.bootcomplete", "1").context("set dev.bootcomplete")?;
Jooyung Han5c6d4172021-12-06 14:17:52 +0900286 exec_task(&config.task.unwrap(), service)
Jooyung Han347d9f22021-05-28 00:05:14 +0900287}
288
Inseob Kim217038e2021-11-25 11:15:06 +0900289struct ApkDmverityArgument<'a> {
290 apk: &'a str,
291 idsig: &'a str,
292 name: &'a str,
Inseob Kim197748b2021-12-01 19:49:00 +0900293 saved_root_hash: Option<&'a RootHash>,
Inseob Kim217038e2021-11-25 11:15:06 +0900294}
295
296fn run_apkdmverity(args: &[ApkDmverityArgument]) -> Result<Child> {
297 let mut cmd = Command::new(APKDMVERITY_BIN);
298
299 cmd.stdin(Stdio::null()).stdout(Stdio::null()).stderr(Stdio::null());
300
301 for argument in args {
302 cmd.arg("--apk").arg(argument.apk).arg(argument.idsig).arg(argument.name);
Inseob Kim197748b2021-12-01 19:49:00 +0900303 if let Some(root_hash) = argument.saved_root_hash {
304 cmd.arg(&to_hex_string(root_hash));
305 } else {
306 cmd.arg("none");
307 }
Inseob Kim217038e2021-11-25 11:15:06 +0900308 }
309
310 cmd.spawn().context("Spawn apkdmverity")
311}
312
313fn run_zipfuse(option: &str, zip_path: &Path, mount_dir: &Path) -> Result<Child> {
314 Command::new(ZIPFUSE_BIN)
315 .arg("-o")
316 .arg(option)
317 .arg(zip_path)
318 .arg(mount_dir)
319 .stdin(Stdio::null())
320 .stdout(Stdio::null())
321 .stderr(Stdio::null())
322 .spawn()
323 .context("Spawn zipfuse")
324}
325
Jooyung Han7a343f92021-09-08 22:53:11 +0900326// Verify payload before executing it. For APK payload, Full verification (which is slow) is done
327// when the root_hash values from the idsig file and the instance disk are different. This function
328// returns the verified root hash (for APK payload) and pubkeys (for APEX payloads) that can be
329// saved to the instance disk.
330fn verify_payload(
331 metadata: &Metadata,
332 saved_data: Option<&MicrodroidData>,
333) -> Result<MicrodroidData> {
Jiyong Parkbb4a9872021-09-06 15:59:21 +0900334 let start_time = SystemTime::now();
335
Inseob Kim197748b2021-12-01 19:49:00 +0900336 // Verify main APK
Jooyung Han7a343f92021-09-08 22:53:11 +0900337 let root_hash = saved_data.map(|d| &d.apk_data.root_hash);
Inseob Kim197748b2021-12-01 19:49:00 +0900338 let root_hash_from_idsig = get_apk_root_hash_from_idsig(MAIN_APK_IDSIG_PATH)?;
Jiyong Parkf7dea252021-09-08 01:42:54 +0900339 let root_hash_trustful = root_hash == Some(&root_hash_from_idsig);
Jiyong Parkbb4a9872021-09-06 15:59:21 +0900340
Jiyong Parkf7dea252021-09-08 01:42:54 +0900341 // 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 +0900342 // instead of the value read from the idsig file.
Inseob Kim197748b2021-12-01 19:49:00 +0900343 let main_apk_argument = {
344 ApkDmverityArgument {
345 apk: MAIN_APK_PATH,
346 idsig: MAIN_APK_IDSIG_PATH,
347 name: MAIN_APK_DEVICE_NAME,
348 saved_root_hash: if root_hash_trustful {
349 Some(root_hash_from_idsig.as_ref())
350 } else {
351 None
352 },
353 }
354 };
355 let mut apkdmverity_arguments = vec![main_apk_argument];
356
357 // Verify extra APKs
358 // For now, we can't read the payload config, so glob APKs and idsigs.
359 // Later, we'll see if it matches with the payload config.
360
361 // sort globbed paths to match apks (extra-apk-{idx}) and idsigs (extra-idsig-{idx})
362 // e.g. "extra-apk-0" corresponds to "extra-idsig-0"
363 let extra_apks =
364 sorted(glob(EXTRA_APK_PATH_PATTERN)?.collect::<Result<Vec<_>, _>>()?).collect::<Vec<_>>();
365 let extra_idsigs =
366 sorted(glob(EXTRA_IDSIG_PATH_PATTERN)?.collect::<Result<Vec<_>, _>>()?).collect::<Vec<_>>();
367 if extra_apks.len() != extra_idsigs.len() {
368 return Err(anyhow!(
369 "Extra apks/idsigs mismatch: {} apks but {} idsigs",
370 extra_apks.len(),
371 extra_idsigs.len()
372 ));
373 }
374 let extra_apks_count = extra_apks.len();
375
376 let (extra_apk_names, extra_root_hashes_from_idsig): (Vec<_>, Vec<_>) = extra_idsigs
377 .iter()
378 .enumerate()
379 .map(|(i, extra_idsig)| {
380 (
381 format!("extra-apk-{}", i),
382 get_apk_root_hash_from_idsig(extra_idsig.to_str().unwrap())
383 .expect("Can't find root hash from extra idsig"),
384 )
385 })
386 .unzip();
387
388 let saved_extra_root_hashes: Vec<_> = saved_data
389 .map(|d| d.extra_apks_data.iter().map(|apk_data| &apk_data.root_hash).collect())
390 .unwrap_or_else(Vec::new);
391 let extra_root_hashes_trustful: Vec<_> = extra_root_hashes_from_idsig
392 .iter()
393 .enumerate()
394 .map(|(i, root_hash_from_idsig)| {
395 saved_extra_root_hashes.get(i).copied() == Some(root_hash_from_idsig)
396 })
397 .collect();
398
399 for i in 0..extra_apks_count {
400 apkdmverity_arguments.push({
401 ApkDmverityArgument {
402 apk: extra_apks[i].to_str().unwrap(),
403 idsig: extra_idsigs[i].to_str().unwrap(),
404 name: &extra_apk_names[i],
405 saved_root_hash: if extra_root_hashes_trustful[i] {
406 Some(&extra_root_hashes_from_idsig[i])
407 } else {
408 None
409 },
410 }
411 });
Jiyong Parkbb4a9872021-09-06 15:59:21 +0900412 }
413
414 // Start apkdmverity and wait for the dm-verify block
Inseob Kim197748b2021-12-01 19:49:00 +0900415 let mut apkdmverity_child = run_apkdmverity(&apkdmverity_arguments)?;
Jooyung Han7a343f92021-09-08 22:53:11 +0900416
Jooyung Hanc8deb472021-09-13 13:48:25 +0900417 // While waiting for apkdmverity to mount APK, gathers public keys and root digests from
418 // APEX payload.
Jooyung Han7a343f92021-09-08 22:53:11 +0900419 let apex_data_from_payload = get_apex_data_from_payload(metadata)?;
Jooyung Han4a9b3bf2021-09-10 17:19:00 +0900420 if let Some(saved_data) = saved_data.map(|d| &d.apex_data) {
Jooyung Hanc8deb472021-09-13 13:48:25 +0900421 // We don't support APEX updates. (assuming that update will change root digest)
Jooyung Handd0a1732021-11-23 15:26:20 +0900422 ensure!(
423 saved_data == &apex_data_from_payload,
424 MicrodroidError::PayloadChanged(String::from("APEXes have changed."))
425 );
Jooyung Han4a9b3bf2021-09-10 17:19:00 +0900426 let apex_metadata = to_metadata(&apex_data_from_payload);
Jooyung Hanc8deb472021-09-13 13:48:25 +0900427 // Pass metadata(with public keys and root digests) to apexd so that it uses the passed
428 // metadata instead of the default one (/dev/block/by-name/payload-metadata)
Jooyung Han4a9b3bf2021-09-10 17:19:00 +0900429 OpenOptions::new()
430 .create_new(true)
431 .write(true)
432 .open("/apex/vm-payload-metadata")
433 .context("Failed to open /apex/vm-payload-metadata")
434 .and_then(|f| write_metadata(&apex_metadata, f))?;
435 }
436 // Start apexd to activate APEXes
437 system_properties::write("ctl.start", "apexd-vm")?;
Jooyung Han7a343f92021-09-08 22:53:11 +0900438
Inseob Kim217038e2021-11-25 11:15:06 +0900439 // TODO(inseob): add timeout
440 apkdmverity_child.wait()?;
Jooyung Han19c1d6c2021-08-06 14:08:16 +0900441
Jiyong Parkf7dea252021-09-08 01:42:54 +0900442 // Do the full verification if the root_hash is un-trustful. This requires the full scanning of
Jiyong Parkbb4a9872021-09-06 15:59:21 +0900443 // 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 +0900444 // 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 +0900445 // of the VM or APK was updated in the host.
446 // TODO(jooyung): consider multithreading to make this faster
Inseob Kim197748b2021-12-01 19:49:00 +0900447 let main_apk_pubkey = get_public_key_from_apk(DM_MOUNTED_APK_PATH, root_hash_trustful)?;
448 let extra_apks_data = extra_root_hashes_from_idsig
449 .into_iter()
450 .enumerate()
451 .map(|(i, extra_root_hash)| {
452 let mount_path = format!("/dev/block/mapper/{}", &extra_apk_names[i]);
453 let apk_pubkey = get_public_key_from_apk(&mount_path, extra_root_hashes_trustful[i])?;
454 Ok(ApkData { root_hash: extra_root_hash, pubkey: apk_pubkey })
455 })
456 .collect::<Result<Vec<_>>>()?;
Jiyong Parkbb4a9872021-09-06 15:59:21 +0900457
458 info!("payload verification successful. took {:#?}", start_time.elapsed().unwrap());
459
Andrew Scull34916a72022-01-30 21:34:24 +0000460 // Use the salt from a verified instance, or generate a salt for a new instance.
461 let salt = if let Some(saved_data) = saved_data {
462 saved_data.salt.clone()
463 } else {
464 let mut salt = vec![0u8; 64];
465 salt.as_mut_slice().try_fill(&mut rand::thread_rng())?;
466 salt
467 };
468
Jiyong Parkf7dea252021-09-08 01:42:54 +0900469 // At this point, we can ensure that the root_hash from the idsig file is trusted, either by
470 // fully verifying the APK or by comparing it with the saved root_hash.
Jooyung Han7a343f92021-09-08 22:53:11 +0900471 Ok(MicrodroidData {
Andrew Scull34916a72022-01-30 21:34:24 +0000472 salt,
Inseob Kim197748b2021-12-01 19:49:00 +0900473 apk_data: ApkData { root_hash: root_hash_from_idsig, pubkey: main_apk_pubkey },
474 extra_apks_data,
Jooyung Han7a343f92021-09-08 22:53:11 +0900475 apex_data: apex_data_from_payload,
476 })
Jiyong Parkbb4a9872021-09-06 15:59:21 +0900477}
478
Inseob Kim197748b2021-12-01 19:49:00 +0900479fn mount_extra_apks(config: &VmPayloadConfig) -> Result<()> {
480 // For now, only the number of apks is important, as the mount point and dm-verity name is fixed
481 for i in 0..config.extra_apks.len() {
482 let mount_dir = format!("/mnt/extra-apk/{}", i);
483 create_dir(Path::new(&mount_dir)).context("Failed to create mount dir for extra apks")?;
484
485 // don't wait, just detach
486 run_zipfuse(
487 "fscontext=u:object_r:zipfusefs:s0,context=u:object_r:extra_apk_file:s0",
488 Path::new(&format!("/dev/block/mapper/extra-apk-{}", i)),
489 Path::new(&mount_dir),
490 )
491 .context("Failed to zipfuse extra apks")?;
492 }
493
494 Ok(())
495}
496
Jiyong Parkbb4a9872021-09-06 15:59:21 +0900497// Waits until linker config is generated
498fn wait_for_apex_config_done() -> Result<()> {
499 let mut prop = PropertyWatcher::new(APEX_CONFIG_DONE_PROP)?;
500 loop {
501 prop.wait()?;
Andrew Scull4b5369f2022-01-28 13:47:20 +0000502 if system_properties::read_bool(APEX_CONFIG_DONE_PROP, false)? {
Jiyong Parkbb4a9872021-09-06 15:59:21 +0900503 break;
504 }
505 }
Jooyung Han19c1d6c2021-08-06 14:08:16 +0900506 Ok(())
507}
508
Inseob Kim197748b2021-12-01 19:49:00 +0900509fn get_apk_root_hash_from_idsig(path: &str) -> Result<Box<RootHash>> {
510 let mut idsig = File::open(path)?;
Jiyong Park21ce2c52021-08-28 02:32:17 +0900511 let idsig = V4Signature::from(&mut idsig)?;
512 Ok(idsig.hashing_info.raw_root_hash)
513}
514
Inseob Kim197748b2021-12-01 19:49:00 +0900515fn get_public_key_from_apk(apk: &str, root_hash_trustful: bool) -> Result<Box<[u8]>> {
516 if !root_hash_trustful {
517 verify(apk).context(MicrodroidError::PayloadVerificationFailed(format!(
518 "failed to verify {}",
519 apk
520 )))
521 } else {
522 get_public_key_der(apk)
523 }
524}
525
Jooyung Han634e2d72021-06-10 16:27:38 +0900526fn load_config(path: &Path) -> Result<VmPayloadConfig> {
527 info!("loading config from {:?}...", path);
528 let file = ioutil::wait_for_file(path, WAIT_TIMEOUT)?;
529 Ok(serde_json::from_reader(file)?)
530}
531
Jiyong Park8611a6c2021-07-09 18:17:44 +0900532/// Executes the given task. Stdout of the task is piped into the vsock stream to the
533/// virtualizationservice in the host side.
Jooyung Han5c6d4172021-12-06 14:17:52 +0900534fn exec_task(task: &Task, service: &Strong<dyn IVirtualMachineService>) -> Result<i32> {
Jiyong Park8611a6c2021-07-09 18:17:44 +0900535 info!("executing main task {:?}...", task);
Inseob Kim86ca0162021-10-20 02:21:02 +0000536 let mut command = build_command(task)?;
Inseob Kim7f61fe72021-08-20 20:50:47 +0900537
538 info!("notifying payload started");
Inseob Kimc7d28c72021-10-25 14:28:10 +0000539 service.notifyPayloadStarted()?;
Inseob Kim7f61fe72021-08-20 20:50:47 +0900540
Jiyong Parkfa91d702021-10-18 23:51:39 +0900541 // Start logging if enabled
542 // TODO(b/200914564) set filterspec if debug_level is app_only
Andrew Scull4b5369f2022-01-28 13:47:20 +0000543 if system_properties::read_bool(LOGD_ENABLED_PROP, false)? {
Jiyong Parkfa91d702021-10-18 23:51:39 +0900544 system_properties::write("ctl.start", "seriallogging")?;
545 }
546
Inseob Kim86ca0162021-10-20 02:21:02 +0000547 let exit_status = command.spawn()?.wait()?;
Jooyung Han5c6d4172021-12-06 14:17:52 +0900548 exit_status.code().ok_or_else(|| anyhow!("Failed to get exit_code from the paylaod."))
Jooyung Han347d9f22021-05-28 00:05:14 +0900549}
Jooyung Han634e2d72021-06-10 16:27:38 +0900550
551fn build_command(task: &Task) -> Result<Command> {
Inseob Kim7f61fe72021-08-20 20:50:47 +0900552 const VMADDR_CID_HOST: u32 = 2;
Inseob Kim7f61fe72021-08-20 20:50:47 +0900553
554 let mut command = match task.type_ {
Jooyung Han634e2d72021-06-10 16:27:38 +0900555 TaskType::Executable => {
556 let mut command = Command::new(&task.command);
557 command.args(&task.args);
558 command
559 }
560 TaskType::MicrodroidLauncher => {
561 let mut command = Command::new("/system/bin/microdroid_launcher");
562 command.arg(find_library_path(&task.command)?).args(&task.args);
563 command
564 }
Inseob Kim7f61fe72021-08-20 20:50:47 +0900565 };
566
Inseob Kimd0587562021-09-01 21:27:32 +0900567 match VsockStream::connect_with_cid_port(VMADDR_CID_HOST, VM_STREAM_SERVICE_PORT as u32) {
Inseob Kim7f61fe72021-08-20 20:50:47 +0900568 Ok(stream) => {
569 // SAFETY: the ownership of the underlying file descriptor is transferred from stream
570 // to the file object, and then into the Command object. When the command is finished,
571 // the file descriptor is closed.
572 let file = unsafe { File::from_raw_fd(stream.into_raw_fd()) };
573 command
574 .stdin(Stdio::from(file.try_clone()?))
575 .stdout(Stdio::from(file.try_clone()?))
576 .stderr(Stdio::from(file));
577 }
578 Err(e) => {
579 error!("failed to connect to virtualization service: {}", e);
580 // Don't fail hard here. Even if we failed to connect to the virtualizationservice,
581 // we keep executing the task. This can happen if the owner of the VM doesn't register
582 // callback to accept the stream. Use /dev/null as the stream so that the task can
583 // make progress without waiting for someone to consume the output.
584 command.stdin(Stdio::null()).stdout(Stdio::null()).stderr(Stdio::null());
585 }
586 }
587
588 Ok(command)
Jooyung Han634e2d72021-06-10 16:27:38 +0900589}
590
591fn find_library_path(name: &str) -> Result<String> {
592 let mut watcher = PropertyWatcher::new("ro.product.cpu.abilist")?;
593 let value = watcher.read(|_name, value| Ok(value.trim().to_string()))?;
594 let abi = value.split(',').next().ok_or_else(|| anyhow!("no abilist"))?;
595 let path = format!("/mnt/apk/lib/{}/{}", abi, name);
596
597 let metadata = fs::metadata(&path)?;
598 if !metadata.is_file() {
599 bail!("{} is not a file", &path);
600 }
601
602 Ok(path)
603}
Jiyong Park21ce2c52021-08-28 02:32:17 +0900604
605fn to_hex_string(buf: &[u8]) -> String {
606 buf.iter().map(|b| format!("{:02X}", b)).collect()
607}