blob: 417232991e291491362bb6887299166a85888fd3 [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
Andrew Sculld64ae7d2022-10-05 17:41:43 +000017mod dice;
Jiyong Park21ce2c52021-08-28 02:32:17 +090018mod instance;
Jooyung Hanf48ceb42021-06-01 18:00:04 +090019mod ioutil;
Jooyung Han7a343f92021-09-08 22:53:11 +090020mod payload;
Keir Fraser933f0ac2022-10-12 08:23:28 +000021mod swap;
Alice Wang59a9e562022-10-04 15:24:10 +000022mod vm_payload_service;
Shikha Panwar95084df2023-07-22 11:47:45 +000023mod vm_secret;
Jooyung Han347d9f22021-05-28 00:05:14 +090024
Shikha Panwar95084df2023-07-22 11:47:45 +000025use crate::dice::{DiceDriver, format_payload_config_descriptor};
Inseob Kime379e7d2022-07-22 18:55:18 +090026use crate::instance::{ApexData, ApkData, InstanceDisk, MicrodroidData, RootHash};
Alice Wang59a9e562022-10-04 15:24:10 +000027use crate::vm_payload_service::register_vm_payload_service;
Alan Stokes2bead0d2022-09-05 16:58:34 +010028use android_system_virtualizationcommon::aidl::android::system::virtualizationcommon::ErrorCode::ErrorCode;
David Brazdil73988ea2022-11-11 15:10:32 +000029use android_system_virtualmachineservice::aidl::android::system::virtualmachineservice::IVirtualMachineService::IVirtualMachineService;
Inseob Kim090b70b2022-11-16 20:01:14 +090030use android_system_virtualization_payload::aidl::android::system::virtualization::payload::IVmPayloadService::{
31 VM_APK_CONTENTS_PATH,
32 VM_PAYLOAD_SERVICE_SOCKET_NAME,
Shikha Panwarddc124b2022-11-28 19:17:54 +000033 ENCRYPTEDSTORE_MOUNTPOINT,
Inseob Kim090b70b2022-11-16 20:01:14 +090034};
Jooyung Handd0a1732021-11-23 15:26:20 +090035use anyhow::{anyhow, bail, ensure, Context, Error, Result};
Alice Wang1bf3d782022-09-28 07:56:36 +000036use apkverify::{get_public_key_der, verify, V4Signature};
Alice Wang43c884b2022-10-24 09:42:40 +000037use binder::Strong;
Alice Wang62f7e642023-02-10 09:55:13 +000038use diced_open_dice::OwnedDiceArtifacts;
Inseob Kim197748b2021-12-01 19:49:00 +090039use glob::glob;
Inseob Kim197748b2021-12-01 19:49:00 +090040use itertools::sorted;
David Brazdila07a1792022-10-25 13:37:57 +010041use libc::VMADDR_CID_HOST;
Alice Wang2a5306e2023-06-05 09:18:32 +000042use log::{error, info};
Alice Wang7e6c9352023-02-15 15:44:13 +000043use keystore2_crypto::ZVec;
Alan Stokes0d1ef782022-09-27 13:46:35 +010044use microdroid_metadata::{write_metadata, Metadata, PayloadMetadata};
Seungjae Yoofd9a0622022-10-14 10:01:29 +090045use microdroid_payload_config::{OsConfig, Task, TaskType, VmPayloadConfig};
Frederick Mayleb5f7b6b2022-11-11 15:24:03 -080046use nix::sys::signal::Signal;
Andrew Sculla0d1b1a2022-05-24 19:32:47 +000047use openssl::sha::Sha512;
Jooyung Han4a9b3bf2021-09-10 17:19:00 +090048use payload::{get_apex_data_from_payload, load_metadata, to_metadata};
Andrew Scull34916a72022-01-30 21:34:24 +000049use rand::Fill;
David Brazdila2125dd2022-12-14 16:37:44 +000050use rpcbinder::RpcSession;
Inseob Kim090b70b2022-11-16 20:01:14 +090051use rustutils::sockets::android_get_control_socket;
Jiyong Parkbb4a9872021-09-06 15:59:21 +090052use rustutils::system_properties;
Joel Galenson482704c2021-07-29 15:53:53 -070053use rustutils::system_properties::PropertyWatcher;
Alan Stokes3ba10fd2022-10-06 15:46:51 +010054use std::borrow::Cow::{Borrowed, Owned};
Andrew Scullb2f44472022-01-21 14:41:34 +000055use std::convert::TryInto;
Inseob Kim7ff121c2022-11-14 18:13:23 +090056use std::env;
Shikha Panwardef7ef92023-01-06 08:35:48 +000057use std::ffi::CString;
Jaewan Kim3124ef02023-03-23 19:25:20 +090058use std::fs::{self, create_dir, OpenOptions, File};
59use std::io::{Read, Write};
Nikita Ioffe3452ee22022-12-15 00:31:56 +000060use std::os::unix::process::CommandExt;
Frederick Mayleb5f7b6b2022-11-11 15:24:03 -080061use std::os::unix::process::ExitStatusExt;
Alice Wangfd222fd2023-05-25 09:37:38 +000062use std::os::unix::io::{FromRawFd, OwnedFd};
Jooyung Hanf48ceb42021-06-01 18:00:04 +090063use std::path::Path;
Inseob Kim217038e2021-11-25 11:15:06 +090064use std::process::{Child, Command, Stdio};
Jiyong Park8611a6c2021-07-09 18:17:44 +090065use std::str;
Jiyong Parkbb4a9872021-09-06 15:59:21 +090066use std::time::{Duration, SystemTime};
Shikha Panwar95084df2023-07-22 11:47:45 +000067use vm_secret::VmSecret;
Jooyung Han634e2d72021-06-10 16:27:38 +090068
69const WAIT_TIMEOUT: Duration = Duration::from_secs(10);
Inseob Kim197748b2021-12-01 19:49:00 +090070const MAIN_APK_PATH: &str = "/dev/block/by-name/microdroid-apk";
71const MAIN_APK_IDSIG_PATH: &str = "/dev/block/by-name/microdroid-apk-idsig";
72const MAIN_APK_DEVICE_NAME: &str = "microdroid-apk";
73const EXTRA_APK_PATH_PATTERN: &str = "/dev/block/by-name/extra-apk-*";
74const EXTRA_IDSIG_PATH_PATTERN: &str = "/dev/block/by-name/extra-idsig-*";
Jooyung Han19c1d6c2021-08-06 14:08:16 +090075const DM_MOUNTED_APK_PATH: &str = "/dev/block/mapper/microdroid-apk";
Andrew Scullab72ec52022-03-14 09:10:52 +000076const AVF_STRICT_BOOT: &str = "/sys/firmware/devicetree/base/chosen/avf,strict-boot";
77const AVF_NEW_INSTANCE: &str = "/sys/firmware/devicetree/base/chosen/avf,new-instance";
Jaewan Kim3124ef02023-03-23 19:25:20 +090078const AVF_DEBUG_POLICY_RAMDUMP: &str = "/sys/firmware/devicetree/base/avf/guest/common/ramdump";
Inseob Kime379e7d2022-07-22 18:55:18 +090079const DEBUG_MICRODROID_NO_VERIFIED_BOOT: &str =
80 "/sys/firmware/devicetree/base/virtualization/guest/debug-microdroid,no-verified-boot";
Jooyung Han347d9f22021-05-28 00:05:14 +090081
Alan Stokes4fb201c2023-02-08 17:39:05 +000082const APKDMVERITY_BIN: &str = "/system/bin/apkdmverity";
83const ENCRYPTEDSTORE_BIN: &str = "/system/bin/encryptedstore";
84const ZIPFUSE_BIN: &str = "/system/bin/zipfuse";
85
Jiyong Parkbb4a9872021-09-06 15:59:21 +090086const APEX_CONFIG_DONE_PROP: &str = "apex_config.done";
Seungjae Yoofa22bb02022-12-08 16:38:42 +090087const DEBUGGABLE_PROP: &str = "ro.boot.microdroid.debuggable";
Jiyong Parkbb4a9872021-09-06 15:59:21 +090088
Inseob Kim11f40d02022-06-13 17:16:00 +090089// SYNC WITH virtualizationservice/src/crosvm.rs
90const FAILURE_SERIAL_DEVICE: &str = "/dev/ttyS1";
91
Shikha Panwar566c9672022-11-15 14:39:58 +000092const ENCRYPTEDSTORE_BACKING_DEVICE: &str = "/dev/block/by-name/encryptedstore";
Shikha Panwar566c9672022-11-15 14:39:58 +000093const ENCRYPTEDSTORE_KEY_IDENTIFIER: &str = "encryptedstore_key";
Alice Wang62f7e642023-02-10 09:55:13 +000094const ENCRYPTEDSTORE_KEYSIZE: usize = 32;
Shikha Panwar566c9672022-11-15 14:39:58 +000095
Jooyung Handd0a1732021-11-23 15:26:20 +090096#[derive(thiserror::Error, Debug)]
97enum MicrodroidError {
Inseob Kim11f40d02022-06-13 17:16:00 +090098 #[error("Cannot connect to virtualization service: {0}")]
99 FailedToConnectToVirtualizationService(String),
Jooyung Handd0a1732021-11-23 15:26:20 +0900100 #[error("Payload has changed: {0}")]
101 PayloadChanged(String),
102 #[error("Payload verification has failed: {0}")]
103 PayloadVerificationFailed(String),
Jooyung Han5c6d4172021-12-06 14:17:52 +0900104 #[error("Payload config is invalid: {0}")]
105 InvalidConfig(String),
Jooyung Handd0a1732021-11-23 15:26:20 +0900106}
107
Alan Stokes2bead0d2022-09-05 16:58:34 +0100108fn translate_error(err: &Error) -> (ErrorCode, String) {
Jooyung Handd0a1732021-11-23 15:26:20 +0900109 if let Some(e) = err.downcast_ref::<MicrodroidError>() {
110 match e {
Alan Stokes2bead0d2022-09-05 16:58:34 +0100111 MicrodroidError::PayloadChanged(msg) => (ErrorCode::PAYLOAD_CHANGED, msg.to_string()),
Jooyung Handd0a1732021-11-23 15:26:20 +0900112 MicrodroidError::PayloadVerificationFailed(msg) => {
Alan Stokes2bead0d2022-09-05 16:58:34 +0100113 (ErrorCode::PAYLOAD_VERIFICATION_FAILED, msg.to_string())
Jooyung Handd0a1732021-11-23 15:26:20 +0900114 }
Alan Stokes2bead0d2022-09-05 16:58:34 +0100115 MicrodroidError::InvalidConfig(msg) => {
116 (ErrorCode::PAYLOAD_CONFIG_INVALID, msg.to_string())
117 }
Inseob Kim11f40d02022-06-13 17:16:00 +0900118
119 // Connection failure won't be reported to VS; return the default value
120 MicrodroidError::FailedToConnectToVirtualizationService(msg) => {
Alan Stokes2bead0d2022-09-05 16:58:34 +0100121 (ErrorCode::UNKNOWN, msg.to_string())
Inseob Kim11f40d02022-06-13 17:16:00 +0900122 }
Jooyung Handd0a1732021-11-23 15:26:20 +0900123 }
124 } else {
Alan Stokes2bead0d2022-09-05 16:58:34 +0100125 (ErrorCode::UNKNOWN, err.to_string())
Jooyung Handd0a1732021-11-23 15:26:20 +0900126 }
127}
128
Inseob Kim11f40d02022-06-13 17:16:00 +0900129fn write_death_reason_to_serial(err: &Error) -> Result<()> {
130 let death_reason = if let Some(e) = err.downcast_ref::<MicrodroidError>() {
Alan Stokes3ba10fd2022-10-06 15:46:51 +0100131 Borrowed(match e {
Inseob Kim11f40d02022-06-13 17:16:00 +0900132 MicrodroidError::FailedToConnectToVirtualizationService(_) => {
133 "MICRODROID_FAILED_TO_CONNECT_TO_VIRTUALIZATION_SERVICE"
134 }
135 MicrodroidError::PayloadChanged(_) => "MICRODROID_PAYLOAD_HAS_CHANGED",
136 MicrodroidError::PayloadVerificationFailed(_) => {
137 "MICRODROID_PAYLOAD_VERIFICATION_FAILED"
138 }
139 MicrodroidError::InvalidConfig(_) => "MICRODROID_INVALID_PAYLOAD_CONFIG",
Alan Stokes3ba10fd2022-10-06 15:46:51 +0100140 })
Inseob Kim11f40d02022-06-13 17:16:00 +0900141 } else {
Alan Stokes3ba10fd2022-10-06 15:46:51 +0100142 // Send context information back after a separator, to ease diagnosis.
143 // These errors occur before the payload runs, so this should not leak sensitive
144 // information.
145 Owned(format!("MICRODROID_UNKNOWN_RUNTIME_ERROR|{:?}", err))
Inseob Kim11f40d02022-06-13 17:16:00 +0900146 };
147
148 let death_reason_bytes = death_reason.as_bytes();
149 let mut sent_total = 0;
150 while sent_total < death_reason_bytes.len() {
151 // TODO(b/220071963): Sometimes, sending more than 16 bytes at once makes MM hang.
152 let begin = sent_total;
153 let end = std::cmp::min(begin.saturating_add(16), death_reason_bytes.len());
154 OpenOptions::new()
155 .read(false)
156 .write(true)
157 .open(FAILURE_SERIAL_DEVICE)?
158 .write_all(&death_reason_bytes[begin..end])?;
159 sent_total = end;
160 }
161
162 Ok(())
163}
164
Inseob Kim1b95f2e2021-08-19 13:17:40 +0900165fn get_vms_rpc_binder() -> Result<Strong<dyn IVirtualMachineService>> {
David Brazdil73988ea2022-11-11 15:10:32 +0000166 // The host is running a VirtualMachineService for this VM on a port equal
167 // to the CID of this VM.
168 let port = vsock::get_local_cid().context("Could not determine local CID")?;
David Brazdila2125dd2022-12-14 16:37:44 +0000169 RpcSession::new()
170 .setup_vsock_client(VMADDR_CID_HOST, port)
David Brazdil73988ea2022-11-11 15:10:32 +0000171 .context("Could not connect to IVirtualMachineService")
Inseob Kim1b95f2e2021-08-19 13:17:40 +0900172}
173
Inseob Kim437f1052022-06-21 11:30:22 +0900174fn main() -> Result<()> {
Inseob Kim7ff121c2022-11-14 18:13:23 +0900175 // If debuggable, print full backtrace to console log with stdio_to_kmsg
Seungjae Yoofa22bb02022-12-08 16:38:42 +0900176 if system_properties::read_bool(DEBUGGABLE_PROP, true)? {
Inseob Kim7ff121c2022-11-14 18:13:23 +0900177 env::set_var("RUST_BACKTRACE", "full");
178 }
179
Inseob Kim437f1052022-06-21 11:30:22 +0900180 scopeguard::defer! {
181 info!("Shutting down...");
Jooyung Hanbfe086f2021-10-28 10:15:45 +0900182 if let Err(e) = system_properties::write("sys.powerctl", "shutdown") {
183 error!("failed to shutdown {:?}", e);
184 }
Jooyung Han311b1202021-09-14 22:00:16 +0900185 }
Inseob Kim437f1052022-06-21 11:30:22 +0900186
187 try_main().map_err(|e| {
188 error!("Failed with {:?}.", e);
189 if let Err(e) = write_death_reason_to_serial(&e) {
190 error!("Failed to write death reason {:?}", e);
191 }
192 e
193 })
Jooyung Han311b1202021-09-14 22:00:16 +0900194}
195
Alice Wangfd222fd2023-05-25 09:37:38 +0000196/// Prepares a socket file descriptor for the vm payload service.
197///
Andrew Walbranae3350d2023-07-21 19:01:18 +0100198/// # Safety
Alice Wangfd222fd2023-05-25 09:37:38 +0000199///
200/// The caller must ensure that this function is the only place that claims ownership
201/// of the file descriptor and it is called only once.
202unsafe fn prepare_vm_payload_service_socket() -> Result<OwnedFd> {
203 let raw_fd = android_get_control_socket(VM_PAYLOAD_SERVICE_SOCKET_NAME)?;
Inseob Kim090b70b2022-11-16 20:01:14 +0900204
Alice Wangfd222fd2023-05-25 09:37:38 +0000205 // Creating OwnedFd for stdio FDs is not safe.
206 if [libc::STDIN_FILENO, libc::STDOUT_FILENO, libc::STDERR_FILENO].contains(&raw_fd) {
207 bail!("File descriptor {raw_fd} is standard I/O descriptor");
208 }
209 // SAFETY: Initializing OwnedFd for a RawFd created by the init.
210 // We checked that the integer value corresponds to a valid FD and that the caller
211 // ensures that this is the only place to claim its ownership.
212 Ok(unsafe { OwnedFd::from_raw_fd(raw_fd) })
Inseob Kim090b70b2022-11-16 20:01:14 +0900213}
214
Jooyung Han311b1202021-09-14 22:00:16 +0900215fn try_main() -> Result<()> {
Jiyong Park2b6346d2023-06-19 13:37:42 +0900216 android_logger::init_once(
217 android_logger::Config::default()
218 .with_tag("microdroid_manager")
219 .with_min_level(log::Level::Info),
220 );
Jooyung Han347d9f22021-05-28 00:05:14 +0900221 info!("started.");
222
Alice Wangfd222fd2023-05-25 09:37:38 +0000223 // SAFETY: This is the only place we take the ownership of the fd of the vm payload service.
224 //
Alice Wang2a5306e2023-06-05 09:18:32 +0000225 // To ensure that the CLOEXEC flag is set on the file descriptor as early as possible,
226 // it is necessary to fetch the socket corresponding to vm_payload_service at the
227 // very beginning, as android_get_control_socket() sets the CLOEXEC flag on the file
228 // descriptor.
Alice Wangfd222fd2023-05-25 09:37:38 +0000229 let vm_payload_service_fd = unsafe { prepare_vm_payload_service_socket()? };
Inseob Kim090b70b2022-11-16 20:01:14 +0900230
Jiyong Park202856e2022-08-22 16:04:26 +0900231 load_crashkernel_if_supported().context("Failed to load crashkernel")?;
232
Alice Wangeff58392023-07-04 13:32:09 +0000233 swap::init_swap().context("Failed to initialize swap")?;
Keir Fraser933f0ac2022-10-12 08:23:28 +0000234 info!("swap enabled.");
235
Inseob Kim11f40d02022-06-13 17:16:00 +0900236 let service = get_vms_rpc_binder()
237 .context("cannot connect to VirtualMachineService")
238 .map_err(|e| MicrodroidError::FailedToConnectToVirtualizationService(e.to_string()))?;
Seungjae Yoofd9a0622022-10-14 10:01:29 +0900239
Alice Wangfd222fd2023-05-25 09:37:38 +0000240 match try_run_payload(&service, vm_payload_service_fd) {
Jooyung Han5c6d4172021-12-06 14:17:52 +0900241 Ok(code) => {
Jooyung Han5c6d4172021-12-06 14:17:52 +0900242 if code == 0 {
243 info!("task successfully finished");
244 } else {
245 error!("task exited with exit code: {}", code);
246 }
Shikha Panwardef7ef92023-01-06 08:35:48 +0000247 if let Err(e) = post_payload_work() {
248 error!(
249 "Failed to run post payload work. It is possible that certain tasks
250 like syncing encrypted store might be incomplete. Error: {:?}",
251 e
252 );
253 };
254
255 info!("notifying payload finished");
256 service.notifyPayloadFinished(code)?;
Jooyung Han5c6d4172021-12-06 14:17:52 +0900257 Ok(())
258 }
259 Err(err) => {
Jooyung Han5c6d4172021-12-06 14:17:52 +0900260 let (error_code, message) = translate_error(&err);
261 service.notifyError(error_code, &message)?;
262 Err(err)
263 }
Jooyung Handd0a1732021-11-23 15:26:20 +0900264 }
265}
266
Shikha Panwardef7ef92023-01-06 08:35:48 +0000267fn post_payload_work() -> Result<()> {
268 // Sync the encrypted storage filesystem (flushes the filesystem caches).
269 if Path::new(ENCRYPTEDSTORE_BACKING_DEVICE).exists() {
270 let mountpoint = CString::new(ENCRYPTEDSTORE_MOUNTPOINT).unwrap();
271
Andrew Walbranae3350d2023-07-21 19:01:18 +0100272 // SAFETY: `mountpoint` is a valid C string. `syncfs` and `close` are safe for any parameter
273 // values.
Shikha Panwardef7ef92023-01-06 08:35:48 +0000274 let ret = unsafe {
275 let dirfd = libc::open(
276 mountpoint.as_ptr(),
277 libc::O_DIRECTORY | libc::O_RDONLY | libc::O_CLOEXEC,
278 );
279 ensure!(dirfd >= 0, "Unable to open {:?}", mountpoint);
280 let ret = libc::syncfs(dirfd);
281 libc::close(dirfd);
282 ret
283 };
284 if ret != 0 {
285 error!("failed to sync encrypted storage.");
286 return Err(anyhow!(std::io::Error::last_os_error()));
287 }
288 }
289 Ok(())
290}
Alan Stokes1f417c92022-09-29 15:13:28 +0100291fn dice_derivation(
Andrew Sculld64ae7d2022-10-05 17:41:43 +0000292 dice: DiceDriver,
Alan Stokes1f417c92022-09-29 15:13:28 +0100293 verified_data: &MicrodroidData,
294 payload_metadata: &PayloadMetadata,
Alice Wang62f7e642023-02-10 09:55:13 +0000295) -> Result<OwnedDiceArtifacts> {
Andrew Scullb2f44472022-01-21 14:41:34 +0000296 // Calculate compound digests of code and authorities
Andrew Sculla0d1b1a2022-05-24 19:32:47 +0000297 let mut code_hash_ctx = Sha512::new();
298 let mut authority_hash_ctx = Sha512::new();
Andrew Scullb2f44472022-01-21 14:41:34 +0000299 code_hash_ctx.update(verified_data.apk_data.root_hash.as_ref());
300 authority_hash_ctx.update(verified_data.apk_data.pubkey.as_ref());
Inseob Kimb2519c52022-04-14 02:10:09 +0900301 for extra_apk in &verified_data.extra_apks_data {
Andrew Scullb2f44472022-01-21 14:41:34 +0000302 code_hash_ctx.update(extra_apk.root_hash.as_ref());
303 authority_hash_ctx.update(extra_apk.pubkey.as_ref());
304 }
Inseob Kimb2519c52022-04-14 02:10:09 +0900305 for apex in &verified_data.apex_data {
Andrew Scullb2f44472022-01-21 14:41:34 +0000306 code_hash_ctx.update(apex.root_digest.as_ref());
307 authority_hash_ctx.update(apex.public_key.as_ref());
308 }
Andrew Sculla0d1b1a2022-05-24 19:32:47 +0000309 let code_hash = code_hash_ctx.finish();
310 let authority_hash = authority_hash_ctx.finish();
Andrew Scullb2f44472022-01-21 14:41:34 +0000311
Alice Wang285a3d22023-03-01 11:36:29 +0000312 let config_descriptor = format_payload_config_descriptor(payload_metadata)?;
Andrew Scullb2f44472022-01-21 14:41:34 +0000313
Seungjae Yoofa22bb02022-12-08 16:38:42 +0900314 // Check debuggability, conservatively assuming it is debuggable
315 let debuggable = system_properties::read_bool(DEBUGGABLE_PROP, true)?;
Andrew Scull65ddfc42022-02-14 21:03:58 +0000316
Andrew Scullb2f44472022-01-21 14:41:34 +0000317 // Send the details to diced
Andrew Sculld64ae7d2022-10-05 17:41:43 +0000318 let hidden = verified_data.salt.clone().try_into().unwrap();
Alice Wang285a3d22023-03-01 11:36:29 +0000319 dice.derive(code_hash, &config_descriptor, authority_hash, debuggable, hidden)
Alan Stokes1f417c92022-09-29 15:13:28 +0100320}
321
Andrew Scullab72ec52022-03-14 09:10:52 +0000322fn is_strict_boot() -> bool {
323 Path::new(AVF_STRICT_BOOT).exists()
324}
325
326fn is_new_instance() -> bool {
327 Path::new(AVF_NEW_INSTANCE).exists()
328}
329
Inseob Kime379e7d2022-07-22 18:55:18 +0900330fn is_verified_boot() -> bool {
331 !Path::new(DEBUG_MICRODROID_NO_VERIFIED_BOOT).exists()
332}
333
Inseob Kimab1037d2023-02-08 17:03:31 +0900334fn should_export_tombstones(config: &VmPayloadConfig) -> bool {
335 match config.export_tombstones {
336 Some(b) => b,
337 None => system_properties::read_bool(DEBUGGABLE_PROP, true).unwrap_or(false),
338 }
339}
340
Jaewan Kim3124ef02023-03-23 19:25:20 +0900341/// Get debug policy value in bool. It's true iff the value is explicitly set to <1>.
342fn get_debug_policy_bool(path: &'static str) -> Result<Option<bool>> {
343 let mut file = match File::open(path) {
344 Ok(dp) => dp,
345 Err(e) => {
Jaewan Kim476ecd42023-08-28 05:57:00 +0000346 info!(
347 "Assumes that debug policy is disabled because failed to read debug policy ({e:?})"
348 );
Jaewan Kim3124ef02023-03-23 19:25:20 +0900349 return Ok(Some(false));
350 }
351 };
352 let mut log: [u8; 4] = Default::default();
353 file.read_exact(&mut log).context("Malformed data in {path}")?;
354 // DT spec uses big endian although Android is always little endian.
355 Ok(Some(u32::from_be_bytes(log) == 1))
356}
357
Alice Wangfd222fd2023-05-25 09:37:38 +0000358fn try_run_payload(
359 service: &Strong<dyn IVirtualMachineService>,
360 vm_payload_service_fd: OwnedFd,
361) -> Result<i32> {
Jooyung Han311b1202021-09-14 22:00:16 +0900362 let metadata = load_metadata().context("Failed to load payload metadata")?;
Andrew Sculld64ae7d2022-10-05 17:41:43 +0000363 let dice = DiceDriver::new(Path::new("/dev/open-dice0")).context("Failed to load DICE")?;
Jooyung Han19c1d6c2021-08-06 14:08:16 +0900364
Jooyung Han311b1202021-09-14 22:00:16 +0900365 let mut instance = InstanceDisk::new().context("Failed to load instance.img")?;
Andrew Sculld64ae7d2022-10-05 17:41:43 +0000366 let saved_data =
367 instance.read_microdroid_data(&dice).context("Failed to read identity data")?;
Jiyong Parkbb4a9872021-09-06 15:59:21 +0900368
Andrew Scullab72ec52022-03-14 09:10:52 +0000369 if is_strict_boot() {
370 // Provisioning must happen on the first boot and never again.
371 if is_new_instance() {
372 ensure!(
373 saved_data.is_none(),
374 MicrodroidError::InvalidConfig("Found instance data on first boot.".to_string())
375 );
376 } else {
377 ensure!(
378 saved_data.is_some(),
379 MicrodroidError::InvalidConfig("Instance data not found.".to_string())
380 );
381 };
382 }
383
Jiyong Parkbb4a9872021-09-06 15:59:21 +0900384 // Verify the payload before using it.
Inseob Kim11f40d02022-06-13 17:16:00 +0900385 let verified_data = verify_payload(&metadata, saved_data.as_ref())
386 .context("Payload verification failed")
387 .map_err(|e| MicrodroidError::PayloadVerificationFailed(e.to_string()))?;
Inseob Kime379e7d2022-07-22 18:55:18 +0900388
389 // In case identity is ignored (by debug policy), we should reuse existing payload data, even
390 // when the payload is changed. This is to keep the derived secret same as before.
391 let verified_data = if let Some(saved_data) = saved_data {
392 if !is_verified_boot() {
393 if saved_data != verified_data {
394 info!("Detected an update of the payload, but continue (regarding debug policy)")
395 }
396 } else {
397 ensure!(
398 saved_data == verified_data,
399 MicrodroidError::PayloadChanged(String::from(
400 "Detected an update of the payload which isn't supported yet."
401 ))
402 );
403 info!("Saved data is verified.");
404 }
405 saved_data
Jiyong Parkbb4a9872021-09-06 15:59:21 +0900406 } else {
Jooyung Han7a343f92021-09-08 22:53:11 +0900407 info!("Saving verified data.");
Andrew Sculld64ae7d2022-10-05 17:41:43 +0000408 instance
409 .write_microdroid_data(&verified_data, &dice)
410 .context("Failed to write identity data")?;
Inseob Kime379e7d2022-07-22 18:55:18 +0900411 verified_data
412 };
Jooyung Han19c1d6c2021-08-06 14:08:16 +0900413
Alan Stokes1f417c92022-09-29 15:13:28 +0100414 let payload_metadata = metadata.payload.ok_or_else(|| {
415 MicrodroidError::InvalidConfig("No payload config in metadata".to_string())
416 })?;
Alan Stokes0d1ef782022-09-27 13:46:35 +0100417
Inseob Kimb2519c52022-04-14 02:10:09 +0900418 // To minimize the exposure to untrusted data, derive dice profile as soon as possible.
419 info!("DICE derivation for payload");
Alice Wang62f7e642023-02-10 09:55:13 +0000420 let dice_artifacts = dice_derivation(dice, &verified_data, &payload_metadata)?;
Shikha Panwar95084df2023-07-22 11:47:45 +0000421 let vm_secret = VmSecret::new(dice_artifacts).context("Failed to create VM secrets")?;
Shikha Panwar566c9672022-11-15 14:39:58 +0000422
423 // Run encryptedstore binary to prepare the storage
424 let encryptedstore_child = if Path::new(ENCRYPTEDSTORE_BACKING_DEVICE).exists() {
425 info!("Preparing encryptedstore ...");
Shikha Panwar95084df2023-07-22 11:47:45 +0000426 Some(prepare_encryptedstore(&vm_secret).context("encryptedstore run")?)
Shikha Panwar566c9672022-11-15 14:39:58 +0000427 } else {
428 None
429 };
Inseob Kimb2519c52022-04-14 02:10:09 +0900430
Alan Stokes960c9032022-12-07 16:53:45 +0000431 let mut zipfuse = Zipfuse::default();
432
Jooyung Hana6d11eb2021-09-10 11:48:05 +0900433 // Before reading a file from the APK, start zipfuse
Alan Stokes960c9032022-12-07 16:53:45 +0000434 zipfuse.mount(
Alan Stokes60f82202022-10-07 16:40:07 +0100435 MountForExec::Allowed,
Inseob Kim217038e2021-11-25 11:15:06 +0900436 "fscontext=u:object_r:zipfusefs:s0,context=u:object_r:system_file:s0",
437 Path::new("/dev/block/mapper/microdroid-apk"),
Alice Wang6bbb6da2022-10-26 12:44:06 +0000438 Path::new(VM_APK_CONTENTS_PATH),
Alan Stokes960c9032022-12-07 16:53:45 +0000439 "microdroid_manager.apk.mounted".to_owned(),
440 )?;
Jiyong Park21ce2c52021-08-28 02:32:17 +0900441
Andrew Scull4d262dc2022-10-21 13:14:33 +0000442 // Restricted APIs are only allowed to be used by platform or test components. Infer this from
443 // the use of a VM config file since those can only be used by platform and test components.
444 let allow_restricted_apis = match payload_metadata {
Ludovic Barman93ee3082023-06-20 12:18:43 +0000445 PayloadMetadata::ConfigPath(_) => true,
446 PayloadMetadata::Config(_) => false,
447 _ => false, // default is false for safety
Andrew Scull4d262dc2022-10-21 13:14:33 +0000448 };
449
Alan Stokes3ba10fd2022-10-06 15:46:51 +0100450 let config = load_config(payload_metadata).context("Failed to load payload metadata")?;
Shikha Panwar6f03c942022-04-13 20:26:50 +0000451
Alan Stokes01b3ef02022-09-22 17:43:24 +0100452 let task = config
453 .task
454 .as_ref()
455 .ok_or_else(|| MicrodroidError::InvalidConfig("No task in VM config".to_string()))?;
456
Alice Wang061478b2023-04-11 13:26:17 +0000457 ensure!(
458 config.extra_apks.len() == verified_data.extra_apks_data.len(),
459 "config expects {} extra apks, but found {}",
460 config.extra_apks.len(),
461 verified_data.extra_apks_data.len()
462 );
Alan Stokes960c9032022-12-07 16:53:45 +0000463 mount_extra_apks(&config, &mut zipfuse)?;
Jooyung Han634e2d72021-06-10 16:27:38 +0900464
Jooyung Han5c6d4172021-12-06 14:17:52 +0900465 // Wait until apex config is done. (e.g. linker configuration for apexes)
Jooyung Han5c6d4172021-12-06 14:17:52 +0900466 wait_for_apex_config_done()?;
467
Nikita Ioffe57bc8d72022-11-27 00:50:50 +0000468 setup_config_sysprops(&config)?;
469
Shikha Panwar1a6efcd2023-02-03 19:23:43 +0000470 // Set export_tombstones if enabled
Inseob Kimab1037d2023-02-08 17:03:31 +0900471 if should_export_tombstones(&config) {
Shikha Panwar1a6efcd2023-02-03 19:23:43 +0000472 // This property is read by tombstone_handler.
473 system_properties::write("microdroid_manager.export_tombstones.enabled", "1")
474 .context("set microdroid_manager.export_tombstones.enabled")?;
Inseob Kimcd9c1dd2022-07-13 17:13:45 +0900475 }
476
Alan Stokes960c9032022-12-07 16:53:45 +0000477 // Wait until zipfuse has mounted the APKs so we can access the payload
478 zipfuse.wait_until_done()?;
Alan Stokes60f82202022-10-07 16:40:07 +0100479
Alice Wangfd222fd2023-05-25 09:37:38 +0000480 register_vm_payload_service(
481 allow_restricted_apis,
482 service.clone(),
Shikha Panwar95084df2023-07-22 11:47:45 +0000483 vm_secret,
Alice Wangfd222fd2023-05-25 09:37:38 +0000484 vm_payload_service_fd,
485 )?;
Shikha Panwar566c9672022-11-15 14:39:58 +0000486
Shikha Panwarddc124b2022-11-28 19:17:54 +0000487 // Wait for encryptedstore to finish mounting the storage (if enabled) before setting
488 // microdroid_manager.init_done. Reason is init stops uneventd after that.
489 // Encryptedstore, however requires ueventd
Shikha Panwar566c9672022-11-15 14:39:58 +0000490 if let Some(mut child) = encryptedstore_child {
491 let exitcode = child.wait().context("Wait for encryptedstore child")?;
492 ensure!(exitcode.success(), "Unable to prepare encrypted storage. Exitcode={}", exitcode);
493 }
Alan Stokes3ba10fd2022-10-06 15:46:51 +0100494
Nikita Ioffe57bc8d72022-11-27 00:50:50 +0000495 wait_for_property_true("dev.bootcomplete").context("failed waiting for dev.bootcomplete")?;
Shikha Panwar3f6f6a52022-11-29 17:28:36 +0000496 system_properties::write("microdroid_manager.init_done", "1")
497 .context("set microdroid_manager.init_done")?;
Inseob Kimc16b0cc2023-01-26 14:57:24 +0900498
Nikita Ioffe57bc8d72022-11-27 00:50:50 +0000499 info!("boot completed, time to run payload");
Alan Stokes3ba10fd2022-10-06 15:46:51 +0100500 exec_task(task, service).context("Failed to run payload")
Alan Stokes01b3ef02022-09-22 17:43:24 +0100501}
502
Inseob Kim217038e2021-11-25 11:15:06 +0900503struct ApkDmverityArgument<'a> {
504 apk: &'a str,
505 idsig: &'a str,
506 name: &'a str,
Inseob Kim197748b2021-12-01 19:49:00 +0900507 saved_root_hash: Option<&'a RootHash>,
Inseob Kim217038e2021-11-25 11:15:06 +0900508}
509
510fn run_apkdmverity(args: &[ApkDmverityArgument]) -> Result<Child> {
511 let mut cmd = Command::new(APKDMVERITY_BIN);
512
Inseob Kim217038e2021-11-25 11:15:06 +0900513 for argument in args {
514 cmd.arg("--apk").arg(argument.apk).arg(argument.idsig).arg(argument.name);
Inseob Kim197748b2021-12-01 19:49:00 +0900515 if let Some(root_hash) = argument.saved_root_hash {
516 cmd.arg(&to_hex_string(root_hash));
517 } else {
518 cmd.arg("none");
519 }
Inseob Kim217038e2021-11-25 11:15:06 +0900520 }
521
522 cmd.spawn().context("Spawn apkdmverity")
523}
524
Alan Stokes60f82202022-10-07 16:40:07 +0100525enum MountForExec {
526 Allowed,
527 Disallowed,
528}
529
Alan Stokes960c9032022-12-07 16:53:45 +0000530#[derive(Default)]
531struct Zipfuse {
532 ready_properties: Vec<String>,
533}
534
535impl Zipfuse {
536 fn mount(
537 &mut self,
538 noexec: MountForExec,
539 option: &str,
540 zip_path: &Path,
541 mount_dir: &Path,
542 ready_prop: String,
543 ) -> Result<Child> {
544 let mut cmd = Command::new(ZIPFUSE_BIN);
545 if let MountForExec::Disallowed = noexec {
546 cmd.arg("--noexec");
547 }
Alan Stokes1294f942023-08-21 14:34:12 +0100548 // Let root own the files in APK, so we can access them, but set the group to
549 // allow all payloads to have access too.
550 let (uid, gid) = (microdroid_uids::ROOT_UID, microdroid_uids::MICRODROID_PAYLOAD_GID);
551
Alan Stokes960c9032022-12-07 16:53:45 +0000552 cmd.args(["-p", &ready_prop, "-o", option]);
Alan Stokes1294f942023-08-21 14:34:12 +0100553 cmd.args(["-u", &uid.to_string()]);
554 cmd.args(["-g", &gid.to_string()]);
Alan Stokes960c9032022-12-07 16:53:45 +0000555 cmd.arg(zip_path).arg(mount_dir);
556 self.ready_properties.push(ready_prop);
557 cmd.spawn().with_context(|| format!("Failed to run zipfuse for {mount_dir:?}"))
Andrew Scullcc339a12022-07-04 12:44:19 +0000558 }
Alan Stokes960c9032022-12-07 16:53:45 +0000559
560 fn wait_until_done(self) -> Result<()> {
561 // We check the last-started check first in the hope that by the time it is done
562 // all or most of the others will also be done, minimising the number of times we
563 // block on a property.
564 for property in self.ready_properties.into_iter().rev() {
565 wait_for_property_true(&property)
566 .with_context(|| format!("Failed waiting for {property}"))?;
567 }
568 Ok(())
Alan Stokes60f82202022-10-07 16:40:07 +0100569 }
Inseob Kim217038e2021-11-25 11:15:06 +0900570}
571
Inseob Kime379e7d2022-07-22 18:55:18 +0900572fn write_apex_payload_data(
573 saved_data: Option<&MicrodroidData>,
574 apex_data_from_payload: &[ApexData],
575) -> Result<()> {
576 if let Some(saved_apex_data) = saved_data.map(|d| &d.apex_data) {
577 // We don't support APEX updates. (assuming that update will change root digest)
578 ensure!(
579 saved_apex_data == apex_data_from_payload,
580 MicrodroidError::PayloadChanged(String::from("APEXes have changed."))
581 );
582 let apex_metadata = to_metadata(apex_data_from_payload);
583 // Pass metadata(with public keys and root digests) to apexd so that it uses the passed
584 // metadata instead of the default one (/dev/block/by-name/payload-metadata)
585 OpenOptions::new()
586 .create_new(true)
587 .write(true)
588 .open("/apex/vm-payload-metadata")
589 .context("Failed to open /apex/vm-payload-metadata")
590 .and_then(|f| write_metadata(&apex_metadata, f))?;
591 }
592 Ok(())
593}
594
Jooyung Han7a343f92021-09-08 22:53:11 +0900595// Verify payload before executing it. For APK payload, Full verification (which is slow) is done
596// when the root_hash values from the idsig file and the instance disk are different. This function
597// returns the verified root hash (for APK payload) and pubkeys (for APEX payloads) that can be
598// saved to the instance disk.
599fn verify_payload(
600 metadata: &Metadata,
601 saved_data: Option<&MicrodroidData>,
602) -> Result<MicrodroidData> {
Jiyong Parkbb4a9872021-09-06 15:59:21 +0900603 let start_time = SystemTime::now();
604
Inseob Kim197748b2021-12-01 19:49:00 +0900605 // Verify main APK
Inseob Kim197748b2021-12-01 19:49:00 +0900606 let root_hash_from_idsig = get_apk_root_hash_from_idsig(MAIN_APK_IDSIG_PATH)?;
Alice Wang061478b2023-04-11 13:26:17 +0000607 let root_hash_trustful =
608 saved_data.map(|d| d.apk_data.root_hash_eq(root_hash_from_idsig.as_ref())).unwrap_or(false);
Jiyong Parkbb4a9872021-09-06 15:59:21 +0900609
Jiyong Parkf7dea252021-09-08 01:42:54 +0900610 // 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 +0900611 // instead of the value read from the idsig file.
Inseob Kim197748b2021-12-01 19:49:00 +0900612 let main_apk_argument = {
613 ApkDmverityArgument {
614 apk: MAIN_APK_PATH,
615 idsig: MAIN_APK_IDSIG_PATH,
616 name: MAIN_APK_DEVICE_NAME,
617 saved_root_hash: if root_hash_trustful {
618 Some(root_hash_from_idsig.as_ref())
619 } else {
620 None
621 },
622 }
623 };
624 let mut apkdmverity_arguments = vec![main_apk_argument];
625
626 // Verify extra APKs
627 // For now, we can't read the payload config, so glob APKs and idsigs.
628 // Later, we'll see if it matches with the payload config.
629
630 // sort globbed paths to match apks (extra-apk-{idx}) and idsigs (extra-idsig-{idx})
631 // e.g. "extra-apk-0" corresponds to "extra-idsig-0"
632 let extra_apks =
633 sorted(glob(EXTRA_APK_PATH_PATTERN)?.collect::<Result<Vec<_>, _>>()?).collect::<Vec<_>>();
634 let extra_idsigs =
635 sorted(glob(EXTRA_IDSIG_PATH_PATTERN)?.collect::<Result<Vec<_>, _>>()?).collect::<Vec<_>>();
Alice Wang061478b2023-04-11 13:26:17 +0000636 ensure!(
637 extra_apks.len() == extra_idsigs.len(),
638 "Extra apks/idsigs mismatch: {} apks but {} idsigs",
639 extra_apks.len(),
640 extra_idsigs.len()
641 );
Inseob Kim197748b2021-12-01 19:49:00 +0900642
Alice Wang061478b2023-04-11 13:26:17 +0000643 let extra_root_hashes_from_idsig: Vec<_> = extra_idsigs
Inseob Kim197748b2021-12-01 19:49:00 +0900644 .iter()
Alice Wang061478b2023-04-11 13:26:17 +0000645 .map(|idsig| {
646 get_apk_root_hash_from_idsig(idsig).expect("Can't find root hash from extra idsig")
Inseob Kim197748b2021-12-01 19:49:00 +0900647 })
648 .collect();
649
Alice Wang061478b2023-04-11 13:26:17 +0000650 let extra_root_hashes_trustful: Vec<_> = if let Some(data) = saved_data {
651 extra_root_hashes_from_idsig
652 .iter()
653 .enumerate()
654 .map(|(i, root_hash)| data.extra_apk_root_hash_eq(i, root_hash))
655 .collect()
656 } else {
657 vec![false; extra_root_hashes_from_idsig.len()]
658 };
659 let extra_apk_names: Vec<_> =
660 (0..extra_apks.len()).map(|i| format!("extra-apk-{}", i)).collect();
661
662 for (i, extra_apk) in extra_apks.iter().enumerate() {
Inseob Kim197748b2021-12-01 19:49:00 +0900663 apkdmverity_arguments.push({
664 ApkDmverityArgument {
Alice Wang061478b2023-04-11 13:26:17 +0000665 apk: extra_apk.to_str().unwrap(),
Inseob Kim197748b2021-12-01 19:49:00 +0900666 idsig: extra_idsigs[i].to_str().unwrap(),
667 name: &extra_apk_names[i],
668 saved_root_hash: if extra_root_hashes_trustful[i] {
669 Some(&extra_root_hashes_from_idsig[i])
670 } else {
671 None
672 },
673 }
674 });
Jiyong Parkbb4a9872021-09-06 15:59:21 +0900675 }
676
677 // Start apkdmverity and wait for the dm-verify block
Inseob Kim197748b2021-12-01 19:49:00 +0900678 let mut apkdmverity_child = run_apkdmverity(&apkdmverity_arguments)?;
Jooyung Han7a343f92021-09-08 22:53:11 +0900679
Jooyung Hanc8deb472021-09-13 13:48:25 +0900680 // While waiting for apkdmverity to mount APK, gathers public keys and root digests from
681 // APEX payload.
Jooyung Han7a343f92021-09-08 22:53:11 +0900682 let apex_data_from_payload = get_apex_data_from_payload(metadata)?;
Inseob Kime379e7d2022-07-22 18:55:18 +0900683
684 // Writing /apex/vm-payload-metadata is to verify that the payload isn't changed.
685 // Skip writing it if the debug policy ignoring identity is on
686 if is_verified_boot() {
687 write_apex_payload_data(saved_data, &apex_data_from_payload)?;
Jooyung Han4a9b3bf2021-09-10 17:19:00 +0900688 }
Inseob Kime379e7d2022-07-22 18:55:18 +0900689
Jooyung Han4a9b3bf2021-09-10 17:19:00 +0900690 // Start apexd to activate APEXes
691 system_properties::write("ctl.start", "apexd-vm")?;
Jooyung Han7a343f92021-09-08 22:53:11 +0900692
Inseob Kim217038e2021-11-25 11:15:06 +0900693 // TODO(inseob): add timeout
694 apkdmverity_child.wait()?;
Jooyung Han19c1d6c2021-08-06 14:08:16 +0900695
Jiyong Parkf7dea252021-09-08 01:42:54 +0900696 // Do the full verification if the root_hash is un-trustful. This requires the full scanning of
Jiyong Parkbb4a9872021-09-06 15:59:21 +0900697 // 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 +0900698 // 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 +0900699 // of the VM or APK was updated in the host.
700 // TODO(jooyung): consider multithreading to make this faster
Inseob Kim197748b2021-12-01 19:49:00 +0900701 let main_apk_pubkey = get_public_key_from_apk(DM_MOUNTED_APK_PATH, root_hash_trustful)?;
702 let extra_apks_data = extra_root_hashes_from_idsig
703 .into_iter()
704 .enumerate()
705 .map(|(i, extra_root_hash)| {
706 let mount_path = format!("/dev/block/mapper/{}", &extra_apk_names[i]);
707 let apk_pubkey = get_public_key_from_apk(&mount_path, extra_root_hashes_trustful[i])?;
708 Ok(ApkData { root_hash: extra_root_hash, pubkey: apk_pubkey })
709 })
710 .collect::<Result<Vec<_>>>()?;
Jiyong Parkbb4a9872021-09-06 15:59:21 +0900711
712 info!("payload verification successful. took {:#?}", start_time.elapsed().unwrap());
713
Andrew Scull34916a72022-01-30 21:34:24 +0000714 // Use the salt from a verified instance, or generate a salt for a new instance.
715 let salt = if let Some(saved_data) = saved_data {
716 saved_data.salt.clone()
Pierre-Clément Tosicecb0aa2023-02-08 16:57:54 +0000717 } else if is_strict_boot() {
718 // No need to add more entropy as a previous stage must have used a new, random salt.
719 vec![0u8; 64]
Andrew Scull34916a72022-01-30 21:34:24 +0000720 } else {
721 let mut salt = vec![0u8; 64];
722 salt.as_mut_slice().try_fill(&mut rand::thread_rng())?;
723 salt
724 };
725
Jiyong Parkf7dea252021-09-08 01:42:54 +0900726 // At this point, we can ensure that the root_hash from the idsig file is trusted, either by
727 // fully verifying the APK or by comparing it with the saved root_hash.
Jooyung Han7a343f92021-09-08 22:53:11 +0900728 Ok(MicrodroidData {
Andrew Scull34916a72022-01-30 21:34:24 +0000729 salt,
Inseob Kim197748b2021-12-01 19:49:00 +0900730 apk_data: ApkData { root_hash: root_hash_from_idsig, pubkey: main_apk_pubkey },
731 extra_apks_data,
Jooyung Han7a343f92021-09-08 22:53:11 +0900732 apex_data: apex_data_from_payload,
733 })
Jiyong Parkbb4a9872021-09-06 15:59:21 +0900734}
735
Alan Stokes960c9032022-12-07 16:53:45 +0000736fn mount_extra_apks(config: &VmPayloadConfig, zipfuse: &mut Zipfuse) -> Result<()> {
Inseob Kim197748b2021-12-01 19:49:00 +0900737 // For now, only the number of apks is important, as the mount point and dm-verity name is fixed
738 for i in 0..config.extra_apks.len() {
Alan Stokes960c9032022-12-07 16:53:45 +0000739 let mount_dir = format!("/mnt/extra-apk/{i}");
Inseob Kim197748b2021-12-01 19:49:00 +0900740 create_dir(Path::new(&mount_dir)).context("Failed to create mount dir for extra apks")?;
741
742 // don't wait, just detach
Alan Stokes960c9032022-12-07 16:53:45 +0000743 zipfuse.mount(
Alan Stokes60f82202022-10-07 16:40:07 +0100744 MountForExec::Disallowed,
Inseob Kim197748b2021-12-01 19:49:00 +0900745 "fscontext=u:object_r:zipfusefs:s0,context=u:object_r:extra_apk_file:s0",
Alan Stokes960c9032022-12-07 16:53:45 +0000746 Path::new(&format!("/dev/block/mapper/extra-apk-{i}")),
Inseob Kim197748b2021-12-01 19:49:00 +0900747 Path::new(&mount_dir),
Alan Stokes960c9032022-12-07 16:53:45 +0000748 format!("microdroid_manager.extra_apk.mounted.{i}"),
749 )?;
Inseob Kim197748b2021-12-01 19:49:00 +0900750 }
751
752 Ok(())
753}
754
Nikita Ioffe57bc8d72022-11-27 00:50:50 +0000755fn setup_config_sysprops(config: &VmPayloadConfig) -> Result<()> {
756 if config.enable_authfs {
757 system_properties::write("microdroid_manager.authfs.enabled", "1")
758 .context("failed to write microdroid_manager.authfs.enabled")?;
759 }
760 system_properties::write("microdroid_manager.config_done", "1")
761 .context("failed to write microdroid_manager.config_done")?;
762 Ok(())
763}
764
Jiyong Parkbb4a9872021-09-06 15:59:21 +0900765// Waits until linker config is generated
766fn wait_for_apex_config_done() -> Result<()> {
Alan Stokes60f82202022-10-07 16:40:07 +0100767 wait_for_property_true(APEX_CONFIG_DONE_PROP).context("Failed waiting for apex config done")
768}
769
770fn wait_for_property_true(property_name: &str) -> Result<()> {
771 let mut prop = PropertyWatcher::new(property_name)?;
Jiyong Parkbb4a9872021-09-06 15:59:21 +0900772 loop {
Andrew Walbrand9c766e2023-05-10 15:15:39 +0000773 prop.wait(None)?;
Alan Stokes60f82202022-10-07 16:40:07 +0100774 if system_properties::read_bool(property_name, false)? {
Jiyong Parkbb4a9872021-09-06 15:59:21 +0900775 break;
776 }
777 }
Jooyung Han19c1d6c2021-08-06 14:08:16 +0900778 Ok(())
779}
780
Alice Wang89cff012022-09-26 10:05:16 +0000781fn get_apk_root_hash_from_idsig<P: AsRef<Path>>(idsig_path: P) -> Result<Box<RootHash>> {
782 Ok(V4Signature::from_idsig_path(idsig_path)?.hashing_info.raw_root_hash)
Jiyong Park21ce2c52021-08-28 02:32:17 +0900783}
784
Inseob Kim197748b2021-12-01 19:49:00 +0900785fn get_public_key_from_apk(apk: &str, root_hash_trustful: bool) -> Result<Box<[u8]>> {
Alan Stokes25f69362023-03-06 16:51:54 +0000786 let current_sdk = get_current_sdk()?;
Inseob Kim197748b2021-12-01 19:49:00 +0900787 if !root_hash_trustful {
Alan Stokes25f69362023-03-06 16:51:54 +0000788 verify(apk, current_sdk).context(MicrodroidError::PayloadVerificationFailed(format!(
Inseob Kim197748b2021-12-01 19:49:00 +0900789 "failed to verify {}",
790 apk
791 )))
792 } else {
Alan Stokes25f69362023-03-06 16:51:54 +0000793 get_public_key_der(apk, current_sdk)
Inseob Kim197748b2021-12-01 19:49:00 +0900794 }
795}
796
Alan Stokes25f69362023-03-06 16:51:54 +0000797fn get_current_sdk() -> Result<u32> {
798 let current_sdk = system_properties::read("ro.build.version.sdk")?;
799 let current_sdk = current_sdk.ok_or_else(|| anyhow!("SDK version missing"))?;
800 current_sdk.parse().context("Malformed SDK version")
801}
802
Alan Stokes1f417c92022-09-29 15:13:28 +0100803fn load_config(payload_metadata: PayloadMetadata) -> Result<VmPayloadConfig> {
804 match payload_metadata {
Ludovic Barman93ee3082023-06-20 12:18:43 +0000805 PayloadMetadata::ConfigPath(path) => {
Alan Stokes1f417c92022-09-29 15:13:28 +0100806 let path = Path::new(&path);
807 info!("loading config from {:?}...", path);
Alan Stokes3ba10fd2022-10-06 15:46:51 +0100808 let file = ioutil::wait_for_file(path, WAIT_TIMEOUT)
809 .with_context(|| format!("Failed to read {:?}", path))?;
Alan Stokes1f417c92022-09-29 15:13:28 +0100810 Ok(serde_json::from_reader(file)?)
811 }
Ludovic Barman93ee3082023-06-20 12:18:43 +0000812 PayloadMetadata::Config(payload_config) => {
Alan Stokes1f417c92022-09-29 15:13:28 +0100813 let task = Task {
814 type_: TaskType::MicrodroidLauncher,
Alan Stokes8f12f2b2023-01-09 09:19:20 +0000815 command: payload_config.payload_binary_name,
Alan Stokes1f417c92022-09-29 15:13:28 +0100816 };
817 Ok(VmPayloadConfig {
818 os: OsConfig { name: "microdroid".to_owned() },
819 task: Some(task),
820 apexes: vec![],
821 extra_apks: vec![],
822 prefer_staged: false,
Inseob Kimab1037d2023-02-08 17:03:31 +0900823 export_tombstones: None,
Alan Stokes1f417c92022-09-29 15:13:28 +0100824 enable_authfs: false,
825 })
826 }
Ludovic Barman93ee3082023-06-20 12:18:43 +0000827 _ => bail!("Failed to match config against a config type."),
Alan Stokes1f417c92022-09-29 15:13:28 +0100828 }
Jooyung Han634e2d72021-06-10 16:27:38 +0900829}
830
Jaewan Kim3124ef02023-03-23 19:25:20 +0900831/// Loads the crashkernel into memory using kexec if debuggable or debug policy says so.
832/// The VM should be loaded with `crashkernel=' parameter in the cmdline to allocate memory
833/// for crashkernel.
Jiyong Park202856e2022-08-22 16:04:26 +0900834fn load_crashkernel_if_supported() -> Result<()> {
835 let supported = std::fs::read_to_string("/proc/cmdline")?.contains(" crashkernel=");
836 info!("ramdump supported: {}", supported);
Jaewan Kim3124ef02023-03-23 19:25:20 +0900837
838 if !supported {
839 return Ok(());
840 }
841
842 let debuggable = system_properties::read_bool(DEBUGGABLE_PROP, true)?;
843 let ramdump = get_debug_policy_bool(AVF_DEBUG_POLICY_RAMDUMP)?.unwrap_or_default();
844 let requested = debuggable | ramdump;
845
846 if requested {
Jiyong Park202856e2022-08-22 16:04:26 +0900847 let status = Command::new("/system/bin/kexec_load").status()?;
848 if !status.success() {
849 return Err(anyhow!("Failed to load crashkernel: {:?}", status));
850 }
Jaewan Kim3124ef02023-03-23 19:25:20 +0900851 info!("ramdump is loaded: debuggable={debuggable}, ramdump={ramdump}");
Jiyong Park202856e2022-08-22 16:04:26 +0900852 }
853 Ok(())
854}
855
Inseob Kim090b70b2022-11-16 20:01:14 +0900856/// Executes the given task.
Jooyung Han5c6d4172021-12-06 14:17:52 +0900857fn exec_task(task: &Task, service: &Strong<dyn IVirtualMachineService>) -> Result<i32> {
Jiyong Park8611a6c2021-07-09 18:17:44 +0900858 info!("executing main task {:?}...", task);
David Brazdil451cc962022-10-14 14:08:12 +0100859 let mut command = match task.type_ {
Alan Stokes1294f942023-08-21 14:34:12 +0100860 TaskType::Executable => {
Alan Stokes679ddf32023-09-01 11:14:48 +0100861 // TODO(b/297501338): Figure out how to handle non-root for system payloads.
Alan Stokes1294f942023-08-21 14:34:12 +0100862 Command::new(&task.command)
863 }
David Brazdil451cc962022-10-14 14:08:12 +0100864 TaskType::MicrodroidLauncher => {
865 let mut command = Command::new("/system/bin/microdroid_launcher");
866 command.arg(find_library_path(&task.command)?);
Alan Stokes1294f942023-08-21 14:34:12 +0100867 command.uid(microdroid_uids::MICRODROID_PAYLOAD_UID);
868 command.gid(microdroid_uids::MICRODROID_PAYLOAD_GID);
David Brazdil451cc962022-10-14 14:08:12 +0100869 command
870 }
871 };
Nikita Ioffe3452ee22022-12-15 00:31:56 +0000872
Andrew Walbranaac68302023-07-21 19:05:34 +0100873 // SAFETY: We are not accessing any resource of the parent process. This means we can't make any
874 // log calls inside the closure.
Nikita Ioffe3452ee22022-12-15 00:31:56 +0000875 unsafe {
Nikita Ioffe3452ee22022-12-15 00:31:56 +0000876 command.pre_exec(|| {
Nikita Ioffe3452ee22022-12-15 00:31:56 +0000877 // It is OK to continue with payload execution even if the calls below fail, since
878 // whether process can use a capability is controlled by the SELinux. Dropping the
879 // capabilities here is just another defense-in-depth layer.
Andrew Walbranaac68302023-07-21 19:05:34 +0100880 let _ = cap::drop_inheritable_caps();
881 let _ = cap::drop_bounding_set();
Nikita Ioffe3452ee22022-12-15 00:31:56 +0000882 Ok(())
883 });
884 }
885
Inseob Kim090b70b2022-11-16 20:01:14 +0900886 command.stdin(Stdio::null()).stdout(Stdio::null()).stderr(Stdio::null());
Inseob Kim7f61fe72021-08-20 20:50:47 +0900887
888 info!("notifying payload started");
Inseob Kimc7d28c72021-10-25 14:28:10 +0000889 service.notifyPayloadStarted()?;
Inseob Kim7f61fe72021-08-20 20:50:47 +0900890
Inseob Kim86ca0162021-10-20 02:21:02 +0000891 let exit_status = command.spawn()?.wait()?;
Frederick Mayleb5f7b6b2022-11-11 15:24:03 -0800892 match exit_status.code() {
893 Some(exit_code) => Ok(exit_code),
894 None => Err(match exit_status.signal() {
895 Some(signal) => anyhow!(
896 "Payload exited due to signal: {} ({})",
897 signal,
898 Signal::try_from(signal).map_or("unknown", |s| s.as_str())
899 ),
900 None => anyhow!("Payload has neither exit code nor signal"),
901 }),
902 }
Jooyung Han347d9f22021-05-28 00:05:14 +0900903}
Jooyung Han634e2d72021-06-10 16:27:38 +0900904
Jooyung Han634e2d72021-06-10 16:27:38 +0900905fn find_library_path(name: &str) -> Result<String> {
906 let mut watcher = PropertyWatcher::new("ro.product.cpu.abilist")?;
907 let value = watcher.read(|_name, value| Ok(value.trim().to_string()))?;
908 let abi = value.split(',').next().ok_or_else(|| anyhow!("no abilist"))?;
Alice Wang6bbb6da2022-10-26 12:44:06 +0000909 let path = format!("{}/lib/{}/{}", VM_APK_CONTENTS_PATH, abi, name);
Jooyung Han634e2d72021-06-10 16:27:38 +0900910
Alan Stokes3ba10fd2022-10-06 15:46:51 +0100911 let metadata = fs::metadata(&path).with_context(|| format!("Unable to access {}", path))?;
Jooyung Han634e2d72021-06-10 16:27:38 +0900912 if !metadata.is_file() {
913 bail!("{} is not a file", &path);
914 }
915
916 Ok(path)
917}
Jiyong Park21ce2c52021-08-28 02:32:17 +0900918
919fn to_hex_string(buf: &[u8]) -> String {
920 buf.iter().map(|b| format!("{:02X}", b)).collect()
921}
Shikha Panwar566c9672022-11-15 14:39:58 +0000922
Shikha Panwar95084df2023-07-22 11:47:45 +0000923fn prepare_encryptedstore(vm_secret: &VmSecret) -> Result<Child> {
Shikha Panwar566c9672022-11-15 14:39:58 +0000924 // Use a fixed salt to scope the derivation to this API.
925 // Generated using hexdump -vn32 -e'14/1 "0x%02X, " 1 "\n"' /dev/urandom
926 // TODO(b/241541860) : Move this (& other salts) to a salt container, i.e. a global enum
927 let salt = [
928 0xFC, 0x1D, 0x35, 0x7B, 0x96, 0xF3, 0xEF, 0x17, 0x78, 0x7D, 0x70, 0xED, 0xEA, 0xFE, 0x1D,
929 0x6F, 0xB3, 0xF9, 0x40, 0xCE, 0xDD, 0x99, 0x40, 0xAA, 0xA7, 0x0E, 0x92, 0x73, 0x90, 0x86,
930 0x4A, 0x75,
931 ];
Alice Wang7e6c9352023-02-15 15:44:13 +0000932 let mut key = ZVec::new(ENCRYPTEDSTORE_KEYSIZE)?;
Shikha Panwar95084df2023-07-22 11:47:45 +0000933 vm_secret.derive_sealing_key(&salt, ENCRYPTEDSTORE_KEY_IDENTIFIER.as_bytes(), &mut key)?;
Shikha Panwar566c9672022-11-15 14:39:58 +0000934 let mut cmd = Command::new(ENCRYPTEDSTORE_BIN);
935 cmd.arg("--blkdevice")
936 .arg(ENCRYPTEDSTORE_BACKING_DEVICE)
937 .arg("--key")
938 .arg(hex::encode(&*key))
Shikha Panwar9fd198f2022-11-18 17:43:43 +0000939 .args(["--mountpoint", ENCRYPTEDSTORE_MOUNTPOINT])
Shikha Panwar566c9672022-11-15 14:39:58 +0000940 .spawn()
941 .context("encryptedstore failed")
942}