blob: 1b3aa7fcd5477c3a18c3aed8a104841cf0be5d97 [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 Scullb2f44472022-01-21 14:41:34 +000070const ADBD_ENABLED_PROP: &str = "ro.boot.adb.enabled";
71const DEBUGGABLE_PROP: &str = "ro.boot.microdroid.debuggable";
Jiyong Parkbb4a9872021-09-06 15:59:21 +090072
Jooyung Handd0a1732021-11-23 15:26:20 +090073#[derive(thiserror::Error, Debug)]
74enum MicrodroidError {
75 #[error("Payload has changed: {0}")]
76 PayloadChanged(String),
77 #[error("Payload verification has failed: {0}")]
78 PayloadVerificationFailed(String),
Jooyung Han5c6d4172021-12-06 14:17:52 +090079 #[error("Payload config is invalid: {0}")]
80 InvalidConfig(String),
Jooyung Handd0a1732021-11-23 15:26:20 +090081}
82
83fn translate_error(err: &Error) -> (i32, String) {
84 if let Some(e) = err.downcast_ref::<MicrodroidError>() {
85 match e {
86 MicrodroidError::PayloadChanged(msg) => (ERROR_PAYLOAD_CHANGED, msg.to_string()),
87 MicrodroidError::PayloadVerificationFailed(msg) => {
88 (ERROR_PAYLOAD_VERIFICATION_FAILED, msg.to_string())
89 }
Jooyung Han5c6d4172021-12-06 14:17:52 +090090 MicrodroidError::InvalidConfig(msg) => (ERROR_PAYLOAD_INVALID_CONFIG, msg.to_string()),
Jooyung Handd0a1732021-11-23 15:26:20 +090091 }
92 } else {
93 (ERROR_UNKNOWN, err.to_string())
94 }
95}
96
Inseob Kim1b95f2e2021-08-19 13:17:40 +090097fn get_vms_rpc_binder() -> Result<Strong<dyn IVirtualMachineService>> {
98 // SAFETY: AIBinder returned by RpcClient has correct reference count, and the ownership can be
99 // safely taken by new_spibinder.
100 let ibinder = unsafe {
101 new_spibinder(binder_rpc_unstable_bindgen::RpcClient(
102 VMADDR_CID_HOST,
Inseob Kimd0587562021-09-01 21:27:32 +0900103 VM_BINDER_SERVICE_PORT as u32,
Inseob Kim1b95f2e2021-08-19 13:17:40 +0900104 ) as *mut AIBinder)
105 };
106 if let Some(ibinder) = ibinder {
107 <dyn IVirtualMachineService>::try_from(ibinder).context("Cannot connect to RPC service")
108 } else {
109 bail!("Invalid raw AIBinder")
110 }
111}
112
Jooyung Han311b1202021-09-14 22:00:16 +0900113fn main() {
114 if let Err(e) = try_main() {
Jooyung Hanbfe086f2021-10-28 10:15:45 +0900115 error!("Failed with {:?}. Shutting down...", e);
116 if let Err(e) = system_properties::write("sys.powerctl", "shutdown") {
117 error!("failed to shutdown {:?}", e);
118 }
Jooyung Han311b1202021-09-14 22:00:16 +0900119 std::process::exit(1);
120 }
121}
122
123fn try_main() -> Result<()> {
Andrew Sculle127cff2022-02-15 15:34:04 +0000124 let _ = kernlog::init();
Jooyung Han347d9f22021-05-28 00:05:14 +0900125 info!("started.");
126
Jooyung Handd0a1732021-11-23 15:26:20 +0900127 let service = get_vms_rpc_binder().context("cannot connect to VirtualMachineService")?;
Jooyung Han5c6d4172021-12-06 14:17:52 +0900128 match try_run_payload(&service) {
129 Ok(code) => {
130 info!("notifying payload finished");
131 service.notifyPayloadFinished(code)?;
132 if code == 0 {
133 info!("task successfully finished");
134 } else {
135 error!("task exited with exit code: {}", code);
136 }
137 Ok(())
138 }
139 Err(err) => {
140 error!("task terminated: {:?}", err);
141 let (error_code, message) = translate_error(&err);
142 service.notifyError(error_code, &message)?;
143 Err(err)
144 }
Jooyung Handd0a1732021-11-23 15:26:20 +0900145 }
146}
147
Andrew Scullb2f44472022-01-21 14:41:34 +0000148fn is_debuggable() -> Result<bool> {
149 // Read all the properties so the behaviour is most similar between debug and non-debug boots.
150 // Defensively default to debug enabled for unrecognised values.
151 let adb = system_properties::read_bool(ADBD_ENABLED_PROP, true)?;
152 let logd = system_properties::read_bool(LOGD_ENABLED_PROP, true)?;
153 let debuggable = system_properties::read_bool(DEBUGGABLE_PROP, true)?;
154 Ok(adb || logd || debuggable)
155}
156
157fn dice_derivation(verified_data: MicrodroidData, payload_config_path: &str) -> Result<()> {
158 // Calculate compound digests of code and authorities
159 let mut code_hash_ctx = digest::Context::new(&digest::SHA512);
160 let mut authority_hash_ctx = digest::Context::new(&digest::SHA512);
161 code_hash_ctx.update(verified_data.apk_data.root_hash.as_ref());
162 authority_hash_ctx.update(verified_data.apk_data.pubkey.as_ref());
163 for extra_apk in verified_data.extra_apks_data {
164 code_hash_ctx.update(extra_apk.root_hash.as_ref());
165 authority_hash_ctx.update(extra_apk.pubkey.as_ref());
166 }
167 for apex in verified_data.apex_data {
168 code_hash_ctx.update(apex.root_digest.as_ref());
169 authority_hash_ctx.update(apex.public_key.as_ref());
170 }
171 let code_hash = code_hash_ctx.finish().as_ref().try_into().unwrap();
172 let authority_hash = authority_hash_ctx.finish().as_ref().try_into().unwrap();
173
174 // {
175 // -70002: "Microdroid payload",
176 // -71000: payload_config_path
177 // }
178 let mut config_desc = vec![
179 0xa2, 0x3a, 0x00, 0x01, 0x11, 0x71, 0x72, 0x4d, 0x69, 0x63, 0x72, 0x6f, 0x64, 0x72, 0x6f,
180 0x69, 0x64, 0x20, 0x70, 0x61, 0x79, 0x6c, 0x6f, 0x61, 0x64, 0x3a, 0x00, 0x01, 0x15, 0x57,
181 ];
182 let config_path_bytes = payload_config_path.as_bytes();
183 encode_header(3, config_path_bytes.len().try_into().unwrap(), &mut config_desc)?;
184 config_desc.extend_from_slice(config_path_bytes);
185
186 // Send the details to diced
187 let diced =
188 wait_for_interface::<dyn IDiceMaintenance>("android.security.dice.IDiceMaintenance")
189 .context("IDiceMaintenance service not found")?;
190 diced
191 .demoteSelf(&[InputValues {
192 codeHash: code_hash,
193 config: Config { desc: config_desc },
194 authorityHash: authority_hash,
195 authorityDescriptor: None,
196 mode: if is_debuggable()? { Mode::DEBUG } else { Mode::NORMAL },
Andrew Scull34916a72022-01-30 21:34:24 +0000197 hidden: verified_data.salt.try_into().unwrap(),
Andrew Scullb2f44472022-01-21 14:41:34 +0000198 }])
199 .context("IDiceMaintenance::demoteSelf failed")?;
200 Ok(())
201}
202
Jooyung Han5c6d4172021-12-06 14:17:52 +0900203fn try_run_payload(service: &Strong<dyn IVirtualMachineService>) -> Result<i32> {
Jooyung Han311b1202021-09-14 22:00:16 +0900204 let metadata = load_metadata().context("Failed to load payload metadata")?;
Jooyung Han19c1d6c2021-08-06 14:08:16 +0900205
Jooyung Han311b1202021-09-14 22:00:16 +0900206 let mut instance = InstanceDisk::new().context("Failed to load instance.img")?;
Jooyung Han7a343f92021-09-08 22:53:11 +0900207 let saved_data = instance.read_microdroid_data().context("Failed to read identity data")?;
Jiyong Parkbb4a9872021-09-06 15:59:21 +0900208
209 // Verify the payload before using it.
Jooyung Han7a343f92021-09-08 22:53:11 +0900210 let verified_data =
211 verify_payload(&metadata, saved_data.as_ref()).context("Payload verification failed")?;
212 if let Some(saved_data) = saved_data {
Jooyung Handd0a1732021-11-23 15:26:20 +0900213 ensure!(
214 saved_data == verified_data,
215 MicrodroidError::PayloadChanged(String::from(
216 "Detected an update of the payload which isn't supported yet."
217 ))
218 );
219 info!("Saved data is verified.");
Jiyong Parkbb4a9872021-09-06 15:59:21 +0900220 } else {
Jooyung Han7a343f92021-09-08 22:53:11 +0900221 info!("Saving verified data.");
222 instance.write_microdroid_data(&verified_data).context("Failed to write identity data")?;
Jooyung Han19c1d6c2021-08-06 14:08:16 +0900223 }
224
Jooyung Hana6d11eb2021-09-10 11:48:05 +0900225 // Before reading a file from the APK, start zipfuse
Inseob Kim217038e2021-11-25 11:15:06 +0900226 run_zipfuse(
227 "fscontext=u:object_r:zipfusefs:s0,context=u:object_r:system_file:s0",
228 Path::new("/dev/block/mapper/microdroid-apk"),
229 Path::new("/mnt/apk"),
230 )
231 .context("Failed to run zipfuse")?;
Jiyong Park21ce2c52021-08-28 02:32:17 +0900232
Jooyung Han5c6d4172021-12-06 14:17:52 +0900233 ensure!(
234 !metadata.payload_config_path.is_empty(),
235 MicrodroidError::InvalidConfig("No payload_config_path in metadata".to_string())
236 );
Inseob Kim197748b2021-12-01 19:49:00 +0900237
Jooyung Han5c6d4172021-12-06 14:17:52 +0900238 let config = load_config(Path::new(&metadata.payload_config_path))?;
Inseob Kim197748b2021-12-01 19:49:00 +0900239 if config.extra_apks.len() != verified_data.extra_apks_data.len() {
240 return Err(anyhow!(
241 "config expects {} extra apks, but found only {}",
242 config.extra_apks.len(),
243 verified_data.extra_apks_data.len()
244 ));
245 }
246 mount_extra_apks(&config)?;
Jooyung Han634e2d72021-06-10 16:27:38 +0900247
Andrew Scullb2f44472022-01-21 14:41:34 +0000248 info!("DICE derivation for payload");
249 dice_derivation(verified_data, &metadata.payload_config_path)?;
250
Jooyung Han5c6d4172021-12-06 14:17:52 +0900251 // Wait until apex config is done. (e.g. linker configuration for apexes)
252 // TODO(jooyung): wait until sys.boot_completed?
253 wait_for_apex_config_done()?;
254
255 ensure!(
256 config.task.is_some(),
257 MicrodroidError::InvalidConfig("No task in VM config".to_string())
258 );
259 exec_task(&config.task.unwrap(), service)
Jooyung Han347d9f22021-05-28 00:05:14 +0900260}
261
Inseob Kim217038e2021-11-25 11:15:06 +0900262struct ApkDmverityArgument<'a> {
263 apk: &'a str,
264 idsig: &'a str,
265 name: &'a str,
Inseob Kim197748b2021-12-01 19:49:00 +0900266 saved_root_hash: Option<&'a RootHash>,
Inseob Kim217038e2021-11-25 11:15:06 +0900267}
268
269fn run_apkdmverity(args: &[ApkDmverityArgument]) -> Result<Child> {
270 let mut cmd = Command::new(APKDMVERITY_BIN);
271
272 cmd.stdin(Stdio::null()).stdout(Stdio::null()).stderr(Stdio::null());
273
274 for argument in args {
275 cmd.arg("--apk").arg(argument.apk).arg(argument.idsig).arg(argument.name);
Inseob Kim197748b2021-12-01 19:49:00 +0900276 if let Some(root_hash) = argument.saved_root_hash {
277 cmd.arg(&to_hex_string(root_hash));
278 } else {
279 cmd.arg("none");
280 }
Inseob Kim217038e2021-11-25 11:15:06 +0900281 }
282
283 cmd.spawn().context("Spawn apkdmverity")
284}
285
286fn run_zipfuse(option: &str, zip_path: &Path, mount_dir: &Path) -> Result<Child> {
287 Command::new(ZIPFUSE_BIN)
288 .arg("-o")
289 .arg(option)
290 .arg(zip_path)
291 .arg(mount_dir)
292 .stdin(Stdio::null())
293 .stdout(Stdio::null())
294 .stderr(Stdio::null())
295 .spawn()
296 .context("Spawn zipfuse")
297}
298
Jooyung Han7a343f92021-09-08 22:53:11 +0900299// Verify payload before executing it. For APK payload, Full verification (which is slow) is done
300// when the root_hash values from the idsig file and the instance disk are different. This function
301// returns the verified root hash (for APK payload) and pubkeys (for APEX payloads) that can be
302// saved to the instance disk.
303fn verify_payload(
304 metadata: &Metadata,
305 saved_data: Option<&MicrodroidData>,
306) -> Result<MicrodroidData> {
Jiyong Parkbb4a9872021-09-06 15:59:21 +0900307 let start_time = SystemTime::now();
308
Inseob Kim197748b2021-12-01 19:49:00 +0900309 // Verify main APK
Jooyung Han7a343f92021-09-08 22:53:11 +0900310 let root_hash = saved_data.map(|d| &d.apk_data.root_hash);
Inseob Kim197748b2021-12-01 19:49:00 +0900311 let root_hash_from_idsig = get_apk_root_hash_from_idsig(MAIN_APK_IDSIG_PATH)?;
Jiyong Parkf7dea252021-09-08 01:42:54 +0900312 let root_hash_trustful = root_hash == Some(&root_hash_from_idsig);
Jiyong Parkbb4a9872021-09-06 15:59:21 +0900313
Jiyong Parkf7dea252021-09-08 01:42:54 +0900314 // 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 +0900315 // instead of the value read from the idsig file.
Inseob Kim197748b2021-12-01 19:49:00 +0900316 let main_apk_argument = {
317 ApkDmverityArgument {
318 apk: MAIN_APK_PATH,
319 idsig: MAIN_APK_IDSIG_PATH,
320 name: MAIN_APK_DEVICE_NAME,
321 saved_root_hash: if root_hash_trustful {
322 Some(root_hash_from_idsig.as_ref())
323 } else {
324 None
325 },
326 }
327 };
328 let mut apkdmverity_arguments = vec![main_apk_argument];
329
330 // Verify extra APKs
331 // For now, we can't read the payload config, so glob APKs and idsigs.
332 // Later, we'll see if it matches with the payload config.
333
334 // sort globbed paths to match apks (extra-apk-{idx}) and idsigs (extra-idsig-{idx})
335 // e.g. "extra-apk-0" corresponds to "extra-idsig-0"
336 let extra_apks =
337 sorted(glob(EXTRA_APK_PATH_PATTERN)?.collect::<Result<Vec<_>, _>>()?).collect::<Vec<_>>();
338 let extra_idsigs =
339 sorted(glob(EXTRA_IDSIG_PATH_PATTERN)?.collect::<Result<Vec<_>, _>>()?).collect::<Vec<_>>();
340 if extra_apks.len() != extra_idsigs.len() {
341 return Err(anyhow!(
342 "Extra apks/idsigs mismatch: {} apks but {} idsigs",
343 extra_apks.len(),
344 extra_idsigs.len()
345 ));
346 }
347 let extra_apks_count = extra_apks.len();
348
349 let (extra_apk_names, extra_root_hashes_from_idsig): (Vec<_>, Vec<_>) = extra_idsigs
350 .iter()
351 .enumerate()
352 .map(|(i, extra_idsig)| {
353 (
354 format!("extra-apk-{}", i),
355 get_apk_root_hash_from_idsig(extra_idsig.to_str().unwrap())
356 .expect("Can't find root hash from extra idsig"),
357 )
358 })
359 .unzip();
360
361 let saved_extra_root_hashes: Vec<_> = saved_data
362 .map(|d| d.extra_apks_data.iter().map(|apk_data| &apk_data.root_hash).collect())
363 .unwrap_or_else(Vec::new);
364 let extra_root_hashes_trustful: Vec<_> = extra_root_hashes_from_idsig
365 .iter()
366 .enumerate()
367 .map(|(i, root_hash_from_idsig)| {
368 saved_extra_root_hashes.get(i).copied() == Some(root_hash_from_idsig)
369 })
370 .collect();
371
372 for i in 0..extra_apks_count {
373 apkdmverity_arguments.push({
374 ApkDmverityArgument {
375 apk: extra_apks[i].to_str().unwrap(),
376 idsig: extra_idsigs[i].to_str().unwrap(),
377 name: &extra_apk_names[i],
378 saved_root_hash: if extra_root_hashes_trustful[i] {
379 Some(&extra_root_hashes_from_idsig[i])
380 } else {
381 None
382 },
383 }
384 });
Jiyong Parkbb4a9872021-09-06 15:59:21 +0900385 }
386
387 // Start apkdmverity and wait for the dm-verify block
Inseob Kim197748b2021-12-01 19:49:00 +0900388 let mut apkdmverity_child = run_apkdmverity(&apkdmverity_arguments)?;
Jooyung Han7a343f92021-09-08 22:53:11 +0900389
Jooyung Hanc8deb472021-09-13 13:48:25 +0900390 // While waiting for apkdmverity to mount APK, gathers public keys and root digests from
391 // APEX payload.
Jooyung Han7a343f92021-09-08 22:53:11 +0900392 let apex_data_from_payload = get_apex_data_from_payload(metadata)?;
Jooyung Han4a9b3bf2021-09-10 17:19:00 +0900393 if let Some(saved_data) = saved_data.map(|d| &d.apex_data) {
Jooyung Hanc8deb472021-09-13 13:48:25 +0900394 // We don't support APEX updates. (assuming that update will change root digest)
Jooyung Handd0a1732021-11-23 15:26:20 +0900395 ensure!(
396 saved_data == &apex_data_from_payload,
397 MicrodroidError::PayloadChanged(String::from("APEXes have changed."))
398 );
Jooyung Han4a9b3bf2021-09-10 17:19:00 +0900399 let apex_metadata = to_metadata(&apex_data_from_payload);
Jooyung Hanc8deb472021-09-13 13:48:25 +0900400 // Pass metadata(with public keys and root digests) to apexd so that it uses the passed
401 // metadata instead of the default one (/dev/block/by-name/payload-metadata)
Jooyung Han4a9b3bf2021-09-10 17:19:00 +0900402 OpenOptions::new()
403 .create_new(true)
404 .write(true)
405 .open("/apex/vm-payload-metadata")
406 .context("Failed to open /apex/vm-payload-metadata")
407 .and_then(|f| write_metadata(&apex_metadata, f))?;
408 }
409 // Start apexd to activate APEXes
410 system_properties::write("ctl.start", "apexd-vm")?;
Jooyung Han7a343f92021-09-08 22:53:11 +0900411
Inseob Kim217038e2021-11-25 11:15:06 +0900412 // TODO(inseob): add timeout
413 apkdmverity_child.wait()?;
Jooyung Han19c1d6c2021-08-06 14:08:16 +0900414
Jiyong Parkf7dea252021-09-08 01:42:54 +0900415 // Do the full verification if the root_hash is un-trustful. This requires the full scanning of
Jiyong Parkbb4a9872021-09-06 15:59:21 +0900416 // 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 +0900417 // 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 +0900418 // of the VM or APK was updated in the host.
419 // TODO(jooyung): consider multithreading to make this faster
Inseob Kim197748b2021-12-01 19:49:00 +0900420 let main_apk_pubkey = get_public_key_from_apk(DM_MOUNTED_APK_PATH, root_hash_trustful)?;
421 let extra_apks_data = extra_root_hashes_from_idsig
422 .into_iter()
423 .enumerate()
424 .map(|(i, extra_root_hash)| {
425 let mount_path = format!("/dev/block/mapper/{}", &extra_apk_names[i]);
426 let apk_pubkey = get_public_key_from_apk(&mount_path, extra_root_hashes_trustful[i])?;
427 Ok(ApkData { root_hash: extra_root_hash, pubkey: apk_pubkey })
428 })
429 .collect::<Result<Vec<_>>>()?;
Jiyong Parkbb4a9872021-09-06 15:59:21 +0900430
431 info!("payload verification successful. took {:#?}", start_time.elapsed().unwrap());
432
Andrew Scull34916a72022-01-30 21:34:24 +0000433 // Use the salt from a verified instance, or generate a salt for a new instance.
434 let salt = if let Some(saved_data) = saved_data {
435 saved_data.salt.clone()
436 } else {
437 let mut salt = vec![0u8; 64];
438 salt.as_mut_slice().try_fill(&mut rand::thread_rng())?;
439 salt
440 };
441
Jiyong Parkf7dea252021-09-08 01:42:54 +0900442 // At this point, we can ensure that the root_hash from the idsig file is trusted, either by
443 // fully verifying the APK or by comparing it with the saved root_hash.
Jooyung Han7a343f92021-09-08 22:53:11 +0900444 Ok(MicrodroidData {
Andrew Scull34916a72022-01-30 21:34:24 +0000445 salt,
Inseob Kim197748b2021-12-01 19:49:00 +0900446 apk_data: ApkData { root_hash: root_hash_from_idsig, pubkey: main_apk_pubkey },
447 extra_apks_data,
Jooyung Han7a343f92021-09-08 22:53:11 +0900448 apex_data: apex_data_from_payload,
449 })
Jiyong Parkbb4a9872021-09-06 15:59:21 +0900450}
451
Inseob Kim197748b2021-12-01 19:49:00 +0900452fn mount_extra_apks(config: &VmPayloadConfig) -> Result<()> {
453 // For now, only the number of apks is important, as the mount point and dm-verity name is fixed
454 for i in 0..config.extra_apks.len() {
455 let mount_dir = format!("/mnt/extra-apk/{}", i);
456 create_dir(Path::new(&mount_dir)).context("Failed to create mount dir for extra apks")?;
457
458 // don't wait, just detach
459 run_zipfuse(
460 "fscontext=u:object_r:zipfusefs:s0,context=u:object_r:extra_apk_file:s0",
461 Path::new(&format!("/dev/block/mapper/extra-apk-{}", i)),
462 Path::new(&mount_dir),
463 )
464 .context("Failed to zipfuse extra apks")?;
465 }
466
467 Ok(())
468}
469
Jiyong Parkbb4a9872021-09-06 15:59:21 +0900470// Waits until linker config is generated
471fn wait_for_apex_config_done() -> Result<()> {
472 let mut prop = PropertyWatcher::new(APEX_CONFIG_DONE_PROP)?;
473 loop {
474 prop.wait()?;
Andrew Scull4b5369f2022-01-28 13:47:20 +0000475 if system_properties::read_bool(APEX_CONFIG_DONE_PROP, false)? {
Jiyong Parkbb4a9872021-09-06 15:59:21 +0900476 break;
477 }
478 }
Jooyung Han19c1d6c2021-08-06 14:08:16 +0900479 Ok(())
480}
481
Inseob Kim197748b2021-12-01 19:49:00 +0900482fn get_apk_root_hash_from_idsig(path: &str) -> Result<Box<RootHash>> {
483 let mut idsig = File::open(path)?;
Jiyong Park21ce2c52021-08-28 02:32:17 +0900484 let idsig = V4Signature::from(&mut idsig)?;
485 Ok(idsig.hashing_info.raw_root_hash)
486}
487
Inseob Kim197748b2021-12-01 19:49:00 +0900488fn get_public_key_from_apk(apk: &str, root_hash_trustful: bool) -> Result<Box<[u8]>> {
489 if !root_hash_trustful {
490 verify(apk).context(MicrodroidError::PayloadVerificationFailed(format!(
491 "failed to verify {}",
492 apk
493 )))
494 } else {
495 get_public_key_der(apk)
496 }
497}
498
Jooyung Han634e2d72021-06-10 16:27:38 +0900499fn load_config(path: &Path) -> Result<VmPayloadConfig> {
500 info!("loading config from {:?}...", path);
501 let file = ioutil::wait_for_file(path, WAIT_TIMEOUT)?;
502 Ok(serde_json::from_reader(file)?)
503}
504
Jiyong Park8611a6c2021-07-09 18:17:44 +0900505/// Executes the given task. Stdout of the task is piped into the vsock stream to the
506/// virtualizationservice in the host side.
Jooyung Han5c6d4172021-12-06 14:17:52 +0900507fn exec_task(task: &Task, service: &Strong<dyn IVirtualMachineService>) -> Result<i32> {
Jiyong Park8611a6c2021-07-09 18:17:44 +0900508 info!("executing main task {:?}...", task);
Inseob Kim86ca0162021-10-20 02:21:02 +0000509 let mut command = build_command(task)?;
Inseob Kim7f61fe72021-08-20 20:50:47 +0900510
511 info!("notifying payload started");
Inseob Kimc7d28c72021-10-25 14:28:10 +0000512 service.notifyPayloadStarted()?;
Inseob Kim7f61fe72021-08-20 20:50:47 +0900513
Jiyong Parkfa91d702021-10-18 23:51:39 +0900514 // Start logging if enabled
515 // TODO(b/200914564) set filterspec if debug_level is app_only
Andrew Scull4b5369f2022-01-28 13:47:20 +0000516 if system_properties::read_bool(LOGD_ENABLED_PROP, false)? {
Jiyong Parkfa91d702021-10-18 23:51:39 +0900517 system_properties::write("ctl.start", "seriallogging")?;
518 }
519
Inseob Kim86ca0162021-10-20 02:21:02 +0000520 let exit_status = command.spawn()?.wait()?;
Jooyung Han5c6d4172021-12-06 14:17:52 +0900521 exit_status.code().ok_or_else(|| anyhow!("Failed to get exit_code from the paylaod."))
Jooyung Han347d9f22021-05-28 00:05:14 +0900522}
Jooyung Han634e2d72021-06-10 16:27:38 +0900523
524fn build_command(task: &Task) -> Result<Command> {
Inseob Kim7f61fe72021-08-20 20:50:47 +0900525 const VMADDR_CID_HOST: u32 = 2;
Inseob Kim7f61fe72021-08-20 20:50:47 +0900526
527 let mut command = match task.type_ {
Jooyung Han634e2d72021-06-10 16:27:38 +0900528 TaskType::Executable => {
529 let mut command = Command::new(&task.command);
530 command.args(&task.args);
531 command
532 }
533 TaskType::MicrodroidLauncher => {
534 let mut command = Command::new("/system/bin/microdroid_launcher");
535 command.arg(find_library_path(&task.command)?).args(&task.args);
536 command
537 }
Inseob Kim7f61fe72021-08-20 20:50:47 +0900538 };
539
Inseob Kimd0587562021-09-01 21:27:32 +0900540 match VsockStream::connect_with_cid_port(VMADDR_CID_HOST, VM_STREAM_SERVICE_PORT as u32) {
Inseob Kim7f61fe72021-08-20 20:50:47 +0900541 Ok(stream) => {
542 // SAFETY: the ownership of the underlying file descriptor is transferred from stream
543 // to the file object, and then into the Command object. When the command is finished,
544 // the file descriptor is closed.
545 let file = unsafe { File::from_raw_fd(stream.into_raw_fd()) };
546 command
547 .stdin(Stdio::from(file.try_clone()?))
548 .stdout(Stdio::from(file.try_clone()?))
549 .stderr(Stdio::from(file));
550 }
551 Err(e) => {
552 error!("failed to connect to virtualization service: {}", e);
553 // Don't fail hard here. Even if we failed to connect to the virtualizationservice,
554 // we keep executing the task. This can happen if the owner of the VM doesn't register
555 // callback to accept the stream. Use /dev/null as the stream so that the task can
556 // make progress without waiting for someone to consume the output.
557 command.stdin(Stdio::null()).stdout(Stdio::null()).stderr(Stdio::null());
558 }
559 }
560
561 Ok(command)
Jooyung Han634e2d72021-06-10 16:27:38 +0900562}
563
564fn find_library_path(name: &str) -> Result<String> {
565 let mut watcher = PropertyWatcher::new("ro.product.cpu.abilist")?;
566 let value = watcher.read(|_name, value| Ok(value.trim().to_string()))?;
567 let abi = value.split(',').next().ok_or_else(|| anyhow!("no abilist"))?;
568 let path = format!("/mnt/apk/lib/{}/{}", abi, name);
569
570 let metadata = fs::metadata(&path)?;
571 if !metadata.is_file() {
572 bail!("{} is not a file", &path);
573 }
574
575 Ok(path)
576}
Jiyong Park21ce2c52021-08-28 02:32:17 +0900577
578fn to_hex_string(buf: &[u8]) -> String {
579 buf.iter().map(|b| format!("{:02X}", b)).collect()
580}