blob: 8c85d3e40196425e5201e6cc2a67c16887b11bd1 [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";
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
Andrew Scullb2f44472022-01-21 14:41:34 +0000149fn dice_derivation(verified_data: MicrodroidData, payload_config_path: &str) -> Result<()> {
150 // Calculate compound digests of code and authorities
151 let mut code_hash_ctx = digest::Context::new(&digest::SHA512);
152 let mut authority_hash_ctx = digest::Context::new(&digest::SHA512);
153 code_hash_ctx.update(verified_data.apk_data.root_hash.as_ref());
154 authority_hash_ctx.update(verified_data.apk_data.pubkey.as_ref());
155 for extra_apk in verified_data.extra_apks_data {
156 code_hash_ctx.update(extra_apk.root_hash.as_ref());
157 authority_hash_ctx.update(extra_apk.pubkey.as_ref());
158 }
159 for apex in verified_data.apex_data {
160 code_hash_ctx.update(apex.root_digest.as_ref());
161 authority_hash_ctx.update(apex.public_key.as_ref());
162 }
163 let code_hash = code_hash_ctx.finish().as_ref().try_into().unwrap();
164 let authority_hash = authority_hash_ctx.finish().as_ref().try_into().unwrap();
165
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 },
Andrew Scull34916a72022-01-30 21:34:24 +0000192 hidden: verified_data.salt.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
Jooyung Hana6d11eb2021-09-10 11:48:05 +0900243 // Before reading a file from the APK, start zipfuse
Inseob Kim217038e2021-11-25 11:15:06 +0900244 run_zipfuse(
245 "fscontext=u:object_r:zipfusefs:s0,context=u:object_r:system_file:s0",
246 Path::new("/dev/block/mapper/microdroid-apk"),
247 Path::new("/mnt/apk"),
248 )
249 .context("Failed to run zipfuse")?;
Jiyong Park21ce2c52021-08-28 02:32:17 +0900250
Jooyung Han5c6d4172021-12-06 14:17:52 +0900251 ensure!(
252 !metadata.payload_config_path.is_empty(),
253 MicrodroidError::InvalidConfig("No payload_config_path in metadata".to_string())
254 );
Inseob Kim197748b2021-12-01 19:49:00 +0900255
Jooyung Han5c6d4172021-12-06 14:17:52 +0900256 let config = load_config(Path::new(&metadata.payload_config_path))?;
Shikha Panwar6f03c942022-04-13 20:26:50 +0000257
258 // Start tombstone_transmit if enabled
259 if config.export_tombstones {
260 system_properties::write("ctl.start", "tombstone_transmit")
261 .context("Failed to start tombstone_transmit")?;
262 }
263
Inseob Kim197748b2021-12-01 19:49:00 +0900264 if config.extra_apks.len() != verified_data.extra_apks_data.len() {
265 return Err(anyhow!(
266 "config expects {} extra apks, but found only {}",
267 config.extra_apks.len(),
268 verified_data.extra_apks_data.len()
269 ));
270 }
271 mount_extra_apks(&config)?;
Jooyung Han634e2d72021-06-10 16:27:38 +0900272
Andrew Scullb2f44472022-01-21 14:41:34 +0000273 info!("DICE derivation for payload");
274 dice_derivation(verified_data, &metadata.payload_config_path)?;
275
Jooyung Han5c6d4172021-12-06 14:17:52 +0900276 // Wait until apex config is done. (e.g. linker configuration for apexes)
277 // TODO(jooyung): wait until sys.boot_completed?
278 wait_for_apex_config_done()?;
279
280 ensure!(
281 config.task.is_some(),
282 MicrodroidError::InvalidConfig("No task in VM config".to_string())
283 );
284 exec_task(&config.task.unwrap(), service)
Jooyung Han347d9f22021-05-28 00:05:14 +0900285}
286
Inseob Kim217038e2021-11-25 11:15:06 +0900287struct ApkDmverityArgument<'a> {
288 apk: &'a str,
289 idsig: &'a str,
290 name: &'a str,
Inseob Kim197748b2021-12-01 19:49:00 +0900291 saved_root_hash: Option<&'a RootHash>,
Inseob Kim217038e2021-11-25 11:15:06 +0900292}
293
294fn run_apkdmverity(args: &[ApkDmverityArgument]) -> Result<Child> {
295 let mut cmd = Command::new(APKDMVERITY_BIN);
296
297 cmd.stdin(Stdio::null()).stdout(Stdio::null()).stderr(Stdio::null());
298
299 for argument in args {
300 cmd.arg("--apk").arg(argument.apk).arg(argument.idsig).arg(argument.name);
Inseob Kim197748b2021-12-01 19:49:00 +0900301 if let Some(root_hash) = argument.saved_root_hash {
302 cmd.arg(&to_hex_string(root_hash));
303 } else {
304 cmd.arg("none");
305 }
Inseob Kim217038e2021-11-25 11:15:06 +0900306 }
307
308 cmd.spawn().context("Spawn apkdmverity")
309}
310
311fn run_zipfuse(option: &str, zip_path: &Path, mount_dir: &Path) -> Result<Child> {
312 Command::new(ZIPFUSE_BIN)
313 .arg("-o")
314 .arg(option)
315 .arg(zip_path)
316 .arg(mount_dir)
317 .stdin(Stdio::null())
318 .stdout(Stdio::null())
319 .stderr(Stdio::null())
320 .spawn()
321 .context("Spawn zipfuse")
322}
323
Jooyung Han7a343f92021-09-08 22:53:11 +0900324// Verify payload before executing it. For APK payload, Full verification (which is slow) is done
325// when the root_hash values from the idsig file and the instance disk are different. This function
326// returns the verified root hash (for APK payload) and pubkeys (for APEX payloads) that can be
327// saved to the instance disk.
328fn verify_payload(
329 metadata: &Metadata,
330 saved_data: Option<&MicrodroidData>,
331) -> Result<MicrodroidData> {
Jiyong Parkbb4a9872021-09-06 15:59:21 +0900332 let start_time = SystemTime::now();
333
Inseob Kim197748b2021-12-01 19:49:00 +0900334 // Verify main APK
Jooyung Han7a343f92021-09-08 22:53:11 +0900335 let root_hash = saved_data.map(|d| &d.apk_data.root_hash);
Inseob Kim197748b2021-12-01 19:49:00 +0900336 let root_hash_from_idsig = get_apk_root_hash_from_idsig(MAIN_APK_IDSIG_PATH)?;
Jiyong Parkf7dea252021-09-08 01:42:54 +0900337 let root_hash_trustful = root_hash == Some(&root_hash_from_idsig);
Jiyong Parkbb4a9872021-09-06 15:59:21 +0900338
Jiyong Parkf7dea252021-09-08 01:42:54 +0900339 // 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 +0900340 // instead of the value read from the idsig file.
Inseob Kim197748b2021-12-01 19:49:00 +0900341 let main_apk_argument = {
342 ApkDmverityArgument {
343 apk: MAIN_APK_PATH,
344 idsig: MAIN_APK_IDSIG_PATH,
345 name: MAIN_APK_DEVICE_NAME,
346 saved_root_hash: if root_hash_trustful {
347 Some(root_hash_from_idsig.as_ref())
348 } else {
349 None
350 },
351 }
352 };
353 let mut apkdmverity_arguments = vec![main_apk_argument];
354
355 // Verify extra APKs
356 // For now, we can't read the payload config, so glob APKs and idsigs.
357 // Later, we'll see if it matches with the payload config.
358
359 // sort globbed paths to match apks (extra-apk-{idx}) and idsigs (extra-idsig-{idx})
360 // e.g. "extra-apk-0" corresponds to "extra-idsig-0"
361 let extra_apks =
362 sorted(glob(EXTRA_APK_PATH_PATTERN)?.collect::<Result<Vec<_>, _>>()?).collect::<Vec<_>>();
363 let extra_idsigs =
364 sorted(glob(EXTRA_IDSIG_PATH_PATTERN)?.collect::<Result<Vec<_>, _>>()?).collect::<Vec<_>>();
365 if extra_apks.len() != extra_idsigs.len() {
366 return Err(anyhow!(
367 "Extra apks/idsigs mismatch: {} apks but {} idsigs",
368 extra_apks.len(),
369 extra_idsigs.len()
370 ));
371 }
372 let extra_apks_count = extra_apks.len();
373
374 let (extra_apk_names, extra_root_hashes_from_idsig): (Vec<_>, Vec<_>) = extra_idsigs
375 .iter()
376 .enumerate()
377 .map(|(i, extra_idsig)| {
378 (
379 format!("extra-apk-{}", i),
380 get_apk_root_hash_from_idsig(extra_idsig.to_str().unwrap())
381 .expect("Can't find root hash from extra idsig"),
382 )
383 })
384 .unzip();
385
386 let saved_extra_root_hashes: Vec<_> = saved_data
387 .map(|d| d.extra_apks_data.iter().map(|apk_data| &apk_data.root_hash).collect())
388 .unwrap_or_else(Vec::new);
389 let extra_root_hashes_trustful: Vec<_> = extra_root_hashes_from_idsig
390 .iter()
391 .enumerate()
392 .map(|(i, root_hash_from_idsig)| {
393 saved_extra_root_hashes.get(i).copied() == Some(root_hash_from_idsig)
394 })
395 .collect();
396
397 for i in 0..extra_apks_count {
398 apkdmverity_arguments.push({
399 ApkDmverityArgument {
400 apk: extra_apks[i].to_str().unwrap(),
401 idsig: extra_idsigs[i].to_str().unwrap(),
402 name: &extra_apk_names[i],
403 saved_root_hash: if extra_root_hashes_trustful[i] {
404 Some(&extra_root_hashes_from_idsig[i])
405 } else {
406 None
407 },
408 }
409 });
Jiyong Parkbb4a9872021-09-06 15:59:21 +0900410 }
411
412 // Start apkdmverity and wait for the dm-verify block
Inseob Kim197748b2021-12-01 19:49:00 +0900413 let mut apkdmverity_child = run_apkdmverity(&apkdmverity_arguments)?;
Jooyung Han7a343f92021-09-08 22:53:11 +0900414
Jooyung Hanc8deb472021-09-13 13:48:25 +0900415 // While waiting for apkdmverity to mount APK, gathers public keys and root digests from
416 // APEX payload.
Jooyung Han7a343f92021-09-08 22:53:11 +0900417 let apex_data_from_payload = get_apex_data_from_payload(metadata)?;
Jooyung Han4a9b3bf2021-09-10 17:19:00 +0900418 if let Some(saved_data) = saved_data.map(|d| &d.apex_data) {
Jooyung Hanc8deb472021-09-13 13:48:25 +0900419 // We don't support APEX updates. (assuming that update will change root digest)
Jooyung Handd0a1732021-11-23 15:26:20 +0900420 ensure!(
421 saved_data == &apex_data_from_payload,
422 MicrodroidError::PayloadChanged(String::from("APEXes have changed."))
423 );
Jooyung Han4a9b3bf2021-09-10 17:19:00 +0900424 let apex_metadata = to_metadata(&apex_data_from_payload);
Jooyung Hanc8deb472021-09-13 13:48:25 +0900425 // Pass metadata(with public keys and root digests) to apexd so that it uses the passed
426 // metadata instead of the default one (/dev/block/by-name/payload-metadata)
Jooyung Han4a9b3bf2021-09-10 17:19:00 +0900427 OpenOptions::new()
428 .create_new(true)
429 .write(true)
430 .open("/apex/vm-payload-metadata")
431 .context("Failed to open /apex/vm-payload-metadata")
432 .and_then(|f| write_metadata(&apex_metadata, f))?;
433 }
434 // Start apexd to activate APEXes
435 system_properties::write("ctl.start", "apexd-vm")?;
Jooyung Han7a343f92021-09-08 22:53:11 +0900436
Inseob Kim217038e2021-11-25 11:15:06 +0900437 // TODO(inseob): add timeout
438 apkdmverity_child.wait()?;
Jooyung Han19c1d6c2021-08-06 14:08:16 +0900439
Jiyong Parkf7dea252021-09-08 01:42:54 +0900440 // Do the full verification if the root_hash is un-trustful. This requires the full scanning of
Jiyong Parkbb4a9872021-09-06 15:59:21 +0900441 // 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 +0900442 // 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 +0900443 // of the VM or APK was updated in the host.
444 // TODO(jooyung): consider multithreading to make this faster
Inseob Kim197748b2021-12-01 19:49:00 +0900445 let main_apk_pubkey = get_public_key_from_apk(DM_MOUNTED_APK_PATH, root_hash_trustful)?;
446 let extra_apks_data = extra_root_hashes_from_idsig
447 .into_iter()
448 .enumerate()
449 .map(|(i, extra_root_hash)| {
450 let mount_path = format!("/dev/block/mapper/{}", &extra_apk_names[i]);
451 let apk_pubkey = get_public_key_from_apk(&mount_path, extra_root_hashes_trustful[i])?;
452 Ok(ApkData { root_hash: extra_root_hash, pubkey: apk_pubkey })
453 })
454 .collect::<Result<Vec<_>>>()?;
Jiyong Parkbb4a9872021-09-06 15:59:21 +0900455
456 info!("payload verification successful. took {:#?}", start_time.elapsed().unwrap());
457
Andrew Scull34916a72022-01-30 21:34:24 +0000458 // Use the salt from a verified instance, or generate a salt for a new instance.
459 let salt = if let Some(saved_data) = saved_data {
460 saved_data.salt.clone()
461 } else {
462 let mut salt = vec![0u8; 64];
463 salt.as_mut_slice().try_fill(&mut rand::thread_rng())?;
464 salt
465 };
466
Jiyong Parkf7dea252021-09-08 01:42:54 +0900467 // At this point, we can ensure that the root_hash from the idsig file is trusted, either by
468 // fully verifying the APK or by comparing it with the saved root_hash.
Jooyung Han7a343f92021-09-08 22:53:11 +0900469 Ok(MicrodroidData {
Andrew Scull34916a72022-01-30 21:34:24 +0000470 salt,
Inseob Kim197748b2021-12-01 19:49:00 +0900471 apk_data: ApkData { root_hash: root_hash_from_idsig, pubkey: main_apk_pubkey },
472 extra_apks_data,
Jooyung Han7a343f92021-09-08 22:53:11 +0900473 apex_data: apex_data_from_payload,
474 })
Jiyong Parkbb4a9872021-09-06 15:59:21 +0900475}
476
Inseob Kim197748b2021-12-01 19:49:00 +0900477fn mount_extra_apks(config: &VmPayloadConfig) -> Result<()> {
478 // For now, only the number of apks is important, as the mount point and dm-verity name is fixed
479 for i in 0..config.extra_apks.len() {
480 let mount_dir = format!("/mnt/extra-apk/{}", i);
481 create_dir(Path::new(&mount_dir)).context("Failed to create mount dir for extra apks")?;
482
483 // don't wait, just detach
484 run_zipfuse(
485 "fscontext=u:object_r:zipfusefs:s0,context=u:object_r:extra_apk_file:s0",
486 Path::new(&format!("/dev/block/mapper/extra-apk-{}", i)),
487 Path::new(&mount_dir),
488 )
489 .context("Failed to zipfuse extra apks")?;
490 }
491
492 Ok(())
493}
494
Jiyong Parkbb4a9872021-09-06 15:59:21 +0900495// Waits until linker config is generated
496fn wait_for_apex_config_done() -> Result<()> {
497 let mut prop = PropertyWatcher::new(APEX_CONFIG_DONE_PROP)?;
498 loop {
499 prop.wait()?;
Andrew Scull4b5369f2022-01-28 13:47:20 +0000500 if system_properties::read_bool(APEX_CONFIG_DONE_PROP, false)? {
Jiyong Parkbb4a9872021-09-06 15:59:21 +0900501 break;
502 }
503 }
Jooyung Han19c1d6c2021-08-06 14:08:16 +0900504 Ok(())
505}
506
Inseob Kim197748b2021-12-01 19:49:00 +0900507fn get_apk_root_hash_from_idsig(path: &str) -> Result<Box<RootHash>> {
508 let mut idsig = File::open(path)?;
Jiyong Park21ce2c52021-08-28 02:32:17 +0900509 let idsig = V4Signature::from(&mut idsig)?;
510 Ok(idsig.hashing_info.raw_root_hash)
511}
512
Inseob Kim197748b2021-12-01 19:49:00 +0900513fn get_public_key_from_apk(apk: &str, root_hash_trustful: bool) -> Result<Box<[u8]>> {
514 if !root_hash_trustful {
515 verify(apk).context(MicrodroidError::PayloadVerificationFailed(format!(
516 "failed to verify {}",
517 apk
518 )))
519 } else {
520 get_public_key_der(apk)
521 }
522}
523
Jooyung Han634e2d72021-06-10 16:27:38 +0900524fn load_config(path: &Path) -> Result<VmPayloadConfig> {
525 info!("loading config from {:?}...", path);
526 let file = ioutil::wait_for_file(path, WAIT_TIMEOUT)?;
527 Ok(serde_json::from_reader(file)?)
528}
529
Jiyong Park8611a6c2021-07-09 18:17:44 +0900530/// Executes the given task. Stdout of the task is piped into the vsock stream to the
531/// virtualizationservice in the host side.
Jooyung Han5c6d4172021-12-06 14:17:52 +0900532fn exec_task(task: &Task, service: &Strong<dyn IVirtualMachineService>) -> Result<i32> {
Jiyong Park8611a6c2021-07-09 18:17:44 +0900533 info!("executing main task {:?}...", task);
Inseob Kim86ca0162021-10-20 02:21:02 +0000534 let mut command = build_command(task)?;
Inseob Kim7f61fe72021-08-20 20:50:47 +0900535
536 info!("notifying payload started");
Inseob Kimc7d28c72021-10-25 14:28:10 +0000537 service.notifyPayloadStarted()?;
Inseob Kim7f61fe72021-08-20 20:50:47 +0900538
Jiyong Parkfa91d702021-10-18 23:51:39 +0900539 // Start logging if enabled
540 // TODO(b/200914564) set filterspec if debug_level is app_only
Andrew Scull4b5369f2022-01-28 13:47:20 +0000541 if system_properties::read_bool(LOGD_ENABLED_PROP, false)? {
Jiyong Parkfa91d702021-10-18 23:51:39 +0900542 system_properties::write("ctl.start", "seriallogging")?;
543 }
544
Inseob Kim86ca0162021-10-20 02:21:02 +0000545 let exit_status = command.spawn()?.wait()?;
Jooyung Han5c6d4172021-12-06 14:17:52 +0900546 exit_status.code().ok_or_else(|| anyhow!("Failed to get exit_code from the paylaod."))
Jooyung Han347d9f22021-05-28 00:05:14 +0900547}
Jooyung Han634e2d72021-06-10 16:27:38 +0900548
549fn build_command(task: &Task) -> Result<Command> {
Inseob Kim7f61fe72021-08-20 20:50:47 +0900550 const VMADDR_CID_HOST: u32 = 2;
Inseob Kim7f61fe72021-08-20 20:50:47 +0900551
552 let mut command = match task.type_ {
Jooyung Han634e2d72021-06-10 16:27:38 +0900553 TaskType::Executable => {
554 let mut command = Command::new(&task.command);
555 command.args(&task.args);
556 command
557 }
558 TaskType::MicrodroidLauncher => {
559 let mut command = Command::new("/system/bin/microdroid_launcher");
560 command.arg(find_library_path(&task.command)?).args(&task.args);
561 command
562 }
Inseob Kim7f61fe72021-08-20 20:50:47 +0900563 };
564
Inseob Kimd0587562021-09-01 21:27:32 +0900565 match VsockStream::connect_with_cid_port(VMADDR_CID_HOST, VM_STREAM_SERVICE_PORT as u32) {
Inseob Kim7f61fe72021-08-20 20:50:47 +0900566 Ok(stream) => {
567 // SAFETY: the ownership of the underlying file descriptor is transferred from stream
568 // to the file object, and then into the Command object. When the command is finished,
569 // the file descriptor is closed.
570 let file = unsafe { File::from_raw_fd(stream.into_raw_fd()) };
571 command
572 .stdin(Stdio::from(file.try_clone()?))
573 .stdout(Stdio::from(file.try_clone()?))
574 .stderr(Stdio::from(file));
575 }
576 Err(e) => {
577 error!("failed to connect to virtualization service: {}", e);
578 // Don't fail hard here. Even if we failed to connect to the virtualizationservice,
579 // we keep executing the task. This can happen if the owner of the VM doesn't register
580 // callback to accept the stream. Use /dev/null as the stream so that the task can
581 // make progress without waiting for someone to consume the output.
582 command.stdin(Stdio::null()).stdout(Stdio::null()).stderr(Stdio::null());
583 }
584 }
585
586 Ok(command)
Jooyung Han634e2d72021-06-10 16:27:38 +0900587}
588
589fn find_library_path(name: &str) -> Result<String> {
590 let mut watcher = PropertyWatcher::new("ro.product.cpu.abilist")?;
591 let value = watcher.read(|_name, value| Ok(value.trim().to_string()))?;
592 let abi = value.split(',').next().ok_or_else(|| anyhow!("no abilist"))?;
593 let path = format!("/mnt/apk/lib/{}/{}", abi, name);
594
595 let metadata = fs::metadata(&path)?;
596 if !metadata.is_file() {
597 bail!("{} is not a file", &path);
598 }
599
600 Ok(path)
601}
Jiyong Park21ce2c52021-08-28 02:32:17 +0900602
603fn to_hex_string(buf: &[u8]) -> String {
604 buf.iter().map(|b| format!("{:02X}", b)).collect()
605}