1 // 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
17 mod dice;
18 mod instance;
19 mod ioutil;
20 mod payload;
21 mod swap;
22 mod vm_payload_service;
23
24 use crate::dice::{DiceDriver, derive_sealing_key, format_payload_config_descriptor};
25 use crate::instance::{ApexData, ApkData, InstanceDisk, MicrodroidData, RootHash};
26 use crate::vm_payload_service::register_vm_payload_service;
27 use android_system_virtualizationcommon::aidl::android::system::virtualizationcommon::ErrorCode::ErrorCode;
28 use android_system_virtualmachineservice::aidl::android::system::virtualmachineservice::IVirtualMachineService::IVirtualMachineService;
29 use android_system_virtualization_payload::aidl::android::system::virtualization::payload::IVmPayloadService::{
30 VM_APK_CONTENTS_PATH,
31 VM_PAYLOAD_SERVICE_SOCKET_NAME,
32 ENCRYPTEDSTORE_MOUNTPOINT,
33 };
34 use anyhow::{anyhow, bail, ensure, Context, Error, Result};
35 use apkverify::{get_public_key_der, verify, V4Signature};
36 use binder::Strong;
37 use diced_open_dice::OwnedDiceArtifacts;
38 use glob::glob;
39 use itertools::sorted;
40 use libc::VMADDR_CID_HOST;
41 use log::{error, info, warn};
42 use keystore2_crypto::ZVec;
43 use microdroid_metadata::{write_metadata, Metadata, PayloadMetadata};
44 use microdroid_payload_config::{OsConfig, Task, TaskType, VmPayloadConfig};
45 use nix::fcntl::{fcntl, F_SETFD, FdFlag};
46 use nix::sys::signal::Signal;
47 use openssl::sha::Sha512;
48 use payload::{get_apex_data_from_payload, load_metadata, to_metadata};
49 use rand::Fill;
50 use rpcbinder::RpcSession;
51 use rustutils::sockets::android_get_control_socket;
52 use rustutils::system_properties;
53 use rustutils::system_properties::PropertyWatcher;
54 use std::borrow::Cow::{Borrowed, Owned};
55 use std::convert::TryInto;
56 use std::env;
57 use std::ffi::CString;
58 use std::fs::{self, create_dir, OpenOptions, File};
59 use std::io::{Read, Write};
60 use std::os::unix::process::CommandExt;
61 use std::os::unix::process::ExitStatusExt;
62 use std::path::Path;
63 use std::process::{Child, Command, Stdio};
64 use std::str;
65 use std::time::{Duration, SystemTime};
66
67 const WAIT_TIMEOUT: Duration = Duration::from_secs(10);
68 const MAIN_APK_PATH: &str = "/dev/block/by-name/microdroid-apk";
69 const MAIN_APK_IDSIG_PATH: &str = "/dev/block/by-name/microdroid-apk-idsig";
70 const MAIN_APK_DEVICE_NAME: &str = "microdroid-apk";
71 const EXTRA_APK_PATH_PATTERN: &str = "/dev/block/by-name/extra-apk-*";
72 const EXTRA_IDSIG_PATH_PATTERN: &str = "/dev/block/by-name/extra-idsig-*";
73 const DM_MOUNTED_APK_PATH: &str = "/dev/block/mapper/microdroid-apk";
74 const AVF_STRICT_BOOT: &str = "/sys/firmware/devicetree/base/chosen/avf,strict-boot";
75 const AVF_NEW_INSTANCE: &str = "/sys/firmware/devicetree/base/chosen/avf,new-instance";
76 const AVF_DEBUG_POLICY_RAMDUMP: &str = "/sys/firmware/devicetree/base/avf/guest/common/ramdump";
77 const DEBUG_MICRODROID_NO_VERIFIED_BOOT: &str =
78 "/sys/firmware/devicetree/base/virtualization/guest/debug-microdroid,no-verified-boot";
79
80 const APKDMVERITY_BIN: &str = "/system/bin/apkdmverity";
81 const ENCRYPTEDSTORE_BIN: &str = "/system/bin/encryptedstore";
82 const ZIPFUSE_BIN: &str = "/system/bin/zipfuse";
83
84 const APEX_CONFIG_DONE_PROP: &str = "apex_config.done";
85 const DEBUGGABLE_PROP: &str = "ro.boot.microdroid.debuggable";
86
87 // SYNC WITH virtualizationservice/src/crosvm.rs
88 const FAILURE_SERIAL_DEVICE: &str = "/dev/ttyS1";
89
90 const ENCRYPTEDSTORE_BACKING_DEVICE: &str = "/dev/block/by-name/encryptedstore";
91 const ENCRYPTEDSTORE_KEY_IDENTIFIER: &str = "encryptedstore_key";
92 const ENCRYPTEDSTORE_KEYSIZE: usize = 32;
93
94 #[derive(thiserror::Error, Debug)]
95 enum MicrodroidError {
96 #[error("Cannot connect to virtualization service: {0}")]
97 FailedToConnectToVirtualizationService(String),
98 #[error("Payload has changed: {0}")]
99 PayloadChanged(String),
100 #[error("Payload verification has failed: {0}")]
101 PayloadVerificationFailed(String),
102 #[error("Payload config is invalid: {0}")]
103 InvalidConfig(String),
104 }
105
translate_error(err: &Error) -> (ErrorCode, String)106 fn translate_error(err: &Error) -> (ErrorCode, String) {
107 if let Some(e) = err.downcast_ref::<MicrodroidError>() {
108 match e {
109 MicrodroidError::PayloadChanged(msg) => (ErrorCode::PAYLOAD_CHANGED, msg.to_string()),
110 MicrodroidError::PayloadVerificationFailed(msg) => {
111 (ErrorCode::PAYLOAD_VERIFICATION_FAILED, msg.to_string())
112 }
113 MicrodroidError::InvalidConfig(msg) => {
114 (ErrorCode::PAYLOAD_CONFIG_INVALID, msg.to_string())
115 }
116
117 // Connection failure won't be reported to VS; return the default value
118 MicrodroidError::FailedToConnectToVirtualizationService(msg) => {
119 (ErrorCode::UNKNOWN, msg.to_string())
120 }
121 }
122 } else {
123 (ErrorCode::UNKNOWN, err.to_string())
124 }
125 }
126
write_death_reason_to_serial(err: &Error) -> Result<()>127 fn write_death_reason_to_serial(err: &Error) -> Result<()> {
128 let death_reason = if let Some(e) = err.downcast_ref::<MicrodroidError>() {
129 Borrowed(match e {
130 MicrodroidError::FailedToConnectToVirtualizationService(_) => {
131 "MICRODROID_FAILED_TO_CONNECT_TO_VIRTUALIZATION_SERVICE"
132 }
133 MicrodroidError::PayloadChanged(_) => "MICRODROID_PAYLOAD_HAS_CHANGED",
134 MicrodroidError::PayloadVerificationFailed(_) => {
135 "MICRODROID_PAYLOAD_VERIFICATION_FAILED"
136 }
137 MicrodroidError::InvalidConfig(_) => "MICRODROID_INVALID_PAYLOAD_CONFIG",
138 })
139 } else {
140 // Send context information back after a separator, to ease diagnosis.
141 // These errors occur before the payload runs, so this should not leak sensitive
142 // information.
143 Owned(format!("MICRODROID_UNKNOWN_RUNTIME_ERROR|{:?}", err))
144 };
145
146 let death_reason_bytes = death_reason.as_bytes();
147 let mut sent_total = 0;
148 while sent_total < death_reason_bytes.len() {
149 // TODO(b/220071963): Sometimes, sending more than 16 bytes at once makes MM hang.
150 let begin = sent_total;
151 let end = std::cmp::min(begin.saturating_add(16), death_reason_bytes.len());
152 OpenOptions::new()
153 .read(false)
154 .write(true)
155 .open(FAILURE_SERIAL_DEVICE)?
156 .write_all(&death_reason_bytes[begin..end])?;
157 sent_total = end;
158 }
159
160 Ok(())
161 }
162
get_vms_rpc_binder() -> Result<Strong<dyn IVirtualMachineService>>163 fn get_vms_rpc_binder() -> Result<Strong<dyn IVirtualMachineService>> {
164 // The host is running a VirtualMachineService for this VM on a port equal
165 // to the CID of this VM.
166 let port = vsock::get_local_cid().context("Could not determine local CID")?;
167 RpcSession::new()
168 .setup_vsock_client(VMADDR_CID_HOST, port)
169 .context("Could not connect to IVirtualMachineService")
170 }
171
main() -> Result<()>172 fn main() -> Result<()> {
173 // If debuggable, print full backtrace to console log with stdio_to_kmsg
174 if system_properties::read_bool(DEBUGGABLE_PROP, true)? {
175 env::set_var("RUST_BACKTRACE", "full");
176 }
177
178 scopeguard::defer! {
179 info!("Shutting down...");
180 if let Err(e) = system_properties::write("sys.powerctl", "shutdown") {
181 error!("failed to shutdown {:?}", e);
182 }
183 }
184
185 try_main().map_err(|e| {
186 error!("Failed with {:?}.", e);
187 if let Err(e) = write_death_reason_to_serial(&e) {
188 error!("Failed to write death reason {:?}", e);
189 }
190 e
191 })
192 }
193
set_cloexec_on_vm_payload_service_socket() -> Result<()>194 fn set_cloexec_on_vm_payload_service_socket() -> Result<()> {
195 let fd = android_get_control_socket(VM_PAYLOAD_SERVICE_SOCKET_NAME)?;
196
197 fcntl(fd, F_SETFD(FdFlag::FD_CLOEXEC))?;
198
199 Ok(())
200 }
201
try_main() -> Result<()>202 fn try_main() -> Result<()> {
203 let _ = kernlog::init();
204 info!("started.");
205
206 if let Err(e) = set_cloexec_on_vm_payload_service_socket() {
207 warn!("Failed to set cloexec on vm payload socket: {:?}", e);
208 }
209
210 load_crashkernel_if_supported().context("Failed to load crashkernel")?;
211
212 swap::init_swap().context("Failed to initialise swap")?;
213 info!("swap enabled.");
214
215 let service = get_vms_rpc_binder()
216 .context("cannot connect to VirtualMachineService")
217 .map_err(|e| MicrodroidError::FailedToConnectToVirtualizationService(e.to_string()))?;
218
219 match try_run_payload(&service) {
220 Ok(code) => {
221 if code == 0 {
222 info!("task successfully finished");
223 } else {
224 error!("task exited with exit code: {}", code);
225 }
226 if let Err(e) = post_payload_work() {
227 error!(
228 "Failed to run post payload work. It is possible that certain tasks
229 like syncing encrypted store might be incomplete. Error: {:?}",
230 e
231 );
232 };
233
234 info!("notifying payload finished");
235 service.notifyPayloadFinished(code)?;
236 Ok(())
237 }
238 Err(err) => {
239 let (error_code, message) = translate_error(&err);
240 service.notifyError(error_code, &message)?;
241 Err(err)
242 }
243 }
244 }
245
post_payload_work() -> Result<()>246 fn post_payload_work() -> Result<()> {
247 // Sync the encrypted storage filesystem (flushes the filesystem caches).
248 if Path::new(ENCRYPTEDSTORE_BACKING_DEVICE).exists() {
249 let mountpoint = CString::new(ENCRYPTEDSTORE_MOUNTPOINT).unwrap();
250
251 let ret = unsafe {
252 let dirfd = libc::open(
253 mountpoint.as_ptr(),
254 libc::O_DIRECTORY | libc::O_RDONLY | libc::O_CLOEXEC,
255 );
256 ensure!(dirfd >= 0, "Unable to open {:?}", mountpoint);
257 let ret = libc::syncfs(dirfd);
258 libc::close(dirfd);
259 ret
260 };
261 if ret != 0 {
262 error!("failed to sync encrypted storage.");
263 return Err(anyhow!(std::io::Error::last_os_error()));
264 }
265 }
266 Ok(())
267 }
dice_derivation( dice: DiceDriver, verified_data: &MicrodroidData, payload_metadata: &PayloadMetadata, ) -> Result<OwnedDiceArtifacts>268 fn dice_derivation(
269 dice: DiceDriver,
270 verified_data: &MicrodroidData,
271 payload_metadata: &PayloadMetadata,
272 ) -> Result<OwnedDiceArtifacts> {
273 // Calculate compound digests of code and authorities
274 let mut code_hash_ctx = Sha512::new();
275 let mut authority_hash_ctx = Sha512::new();
276 code_hash_ctx.update(verified_data.apk_data.root_hash.as_ref());
277 authority_hash_ctx.update(verified_data.apk_data.pubkey.as_ref());
278 for extra_apk in &verified_data.extra_apks_data {
279 code_hash_ctx.update(extra_apk.root_hash.as_ref());
280 authority_hash_ctx.update(extra_apk.pubkey.as_ref());
281 }
282 for apex in &verified_data.apex_data {
283 code_hash_ctx.update(apex.root_digest.as_ref());
284 authority_hash_ctx.update(apex.public_key.as_ref());
285 }
286 let code_hash = code_hash_ctx.finish();
287 let authority_hash = authority_hash_ctx.finish();
288
289 let config_descriptor = format_payload_config_descriptor(payload_metadata)?;
290
291 // Check debuggability, conservatively assuming it is debuggable
292 let debuggable = system_properties::read_bool(DEBUGGABLE_PROP, true)?;
293
294 // Send the details to diced
295 let hidden = verified_data.salt.clone().try_into().unwrap();
296 dice.derive(code_hash, &config_descriptor, authority_hash, debuggable, hidden)
297 }
298
is_strict_boot() -> bool299 fn is_strict_boot() -> bool {
300 Path::new(AVF_STRICT_BOOT).exists()
301 }
302
is_new_instance() -> bool303 fn is_new_instance() -> bool {
304 Path::new(AVF_NEW_INSTANCE).exists()
305 }
306
is_verified_boot() -> bool307 fn is_verified_boot() -> bool {
308 !Path::new(DEBUG_MICRODROID_NO_VERIFIED_BOOT).exists()
309 }
310
should_export_tombstones(config: &VmPayloadConfig) -> bool311 fn should_export_tombstones(config: &VmPayloadConfig) -> bool {
312 match config.export_tombstones {
313 Some(b) => b,
314 None => system_properties::read_bool(DEBUGGABLE_PROP, true).unwrap_or(false),
315 }
316 }
317
318 /// Get debug policy value in bool. It's true iff the value is explicitly set to <1>.
get_debug_policy_bool(path: &'static str) -> Result<Option<bool>>319 fn get_debug_policy_bool(path: &'static str) -> Result<Option<bool>> {
320 let mut file = match File::open(path) {
321 Ok(dp) => dp,
322 Err(e) => {
323 info!("{e:?}. Assumes <0>");
324 return Ok(Some(false));
325 }
326 };
327 let mut log: [u8; 4] = Default::default();
328 file.read_exact(&mut log).context("Malformed data in {path}")?;
329 // DT spec uses big endian although Android is always little endian.
330 Ok(Some(u32::from_be_bytes(log) == 1))
331 }
332
try_run_payload(service: &Strong<dyn IVirtualMachineService>) -> Result<i32>333 fn try_run_payload(service: &Strong<dyn IVirtualMachineService>) -> Result<i32> {
334 let metadata = load_metadata().context("Failed to load payload metadata")?;
335 let dice = DiceDriver::new(Path::new("/dev/open-dice0")).context("Failed to load DICE")?;
336
337 let mut instance = InstanceDisk::new().context("Failed to load instance.img")?;
338 let saved_data =
339 instance.read_microdroid_data(&dice).context("Failed to read identity data")?;
340
341 if is_strict_boot() {
342 // Provisioning must happen on the first boot and never again.
343 if is_new_instance() {
344 ensure!(
345 saved_data.is_none(),
346 MicrodroidError::InvalidConfig("Found instance data on first boot.".to_string())
347 );
348 } else {
349 ensure!(
350 saved_data.is_some(),
351 MicrodroidError::InvalidConfig("Instance data not found.".to_string())
352 );
353 };
354 }
355
356 // Verify the payload before using it.
357 let verified_data = verify_payload(&metadata, saved_data.as_ref())
358 .context("Payload verification failed")
359 .map_err(|e| MicrodroidError::PayloadVerificationFailed(e.to_string()))?;
360
361 // In case identity is ignored (by debug policy), we should reuse existing payload data, even
362 // when the payload is changed. This is to keep the derived secret same as before.
363 let verified_data = if let Some(saved_data) = saved_data {
364 if !is_verified_boot() {
365 if saved_data != verified_data {
366 info!("Detected an update of the payload, but continue (regarding debug policy)")
367 }
368 } else {
369 ensure!(
370 saved_data == verified_data,
371 MicrodroidError::PayloadChanged(String::from(
372 "Detected an update of the payload which isn't supported yet."
373 ))
374 );
375 info!("Saved data is verified.");
376 }
377 saved_data
378 } else {
379 info!("Saving verified data.");
380 instance
381 .write_microdroid_data(&verified_data, &dice)
382 .context("Failed to write identity data")?;
383 verified_data
384 };
385
386 let payload_metadata = metadata.payload.ok_or_else(|| {
387 MicrodroidError::InvalidConfig("No payload config in metadata".to_string())
388 })?;
389
390 // To minimize the exposure to untrusted data, derive dice profile as soon as possible.
391 info!("DICE derivation for payload");
392 let dice_artifacts = dice_derivation(dice, &verified_data, &payload_metadata)?;
393
394 // Run encryptedstore binary to prepare the storage
395 let encryptedstore_child = if Path::new(ENCRYPTEDSTORE_BACKING_DEVICE).exists() {
396 info!("Preparing encryptedstore ...");
397 Some(prepare_encryptedstore(&dice_artifacts).context("encryptedstore run")?)
398 } else {
399 None
400 };
401
402 let mut zipfuse = Zipfuse::default();
403
404 // Before reading a file from the APK, start zipfuse
405 zipfuse.mount(
406 MountForExec::Allowed,
407 "fscontext=u:object_r:zipfusefs:s0,context=u:object_r:system_file:s0",
408 Path::new("/dev/block/mapper/microdroid-apk"),
409 Path::new(VM_APK_CONTENTS_PATH),
410 "microdroid_manager.apk.mounted".to_owned(),
411 )?;
412
413 // Restricted APIs are only allowed to be used by platform or test components. Infer this from
414 // the use of a VM config file since those can only be used by platform and test components.
415 let allow_restricted_apis = match payload_metadata {
416 PayloadMetadata::config_path(_) => true,
417 PayloadMetadata::config(_) => false,
418 };
419
420 let config = load_config(payload_metadata).context("Failed to load payload metadata")?;
421
422 let task = config
423 .task
424 .as_ref()
425 .ok_or_else(|| MicrodroidError::InvalidConfig("No task in VM config".to_string()))?;
426
427 ensure!(
428 config.extra_apks.len() == verified_data.extra_apks_data.len(),
429 "config expects {} extra apks, but found {}",
430 config.extra_apks.len(),
431 verified_data.extra_apks_data.len()
432 );
433 mount_extra_apks(&config, &mut zipfuse)?;
434
435 // Wait until apex config is done. (e.g. linker configuration for apexes)
436 wait_for_apex_config_done()?;
437
438 setup_config_sysprops(&config)?;
439
440 // Set export_tombstones if enabled
441 if should_export_tombstones(&config) {
442 // This property is read by tombstone_handler.
443 system_properties::write("microdroid_manager.export_tombstones.enabled", "1")
444 .context("set microdroid_manager.export_tombstones.enabled")?;
445 }
446
447 // Wait until zipfuse has mounted the APKs so we can access the payload
448 zipfuse.wait_until_done()?;
449
450 register_vm_payload_service(allow_restricted_apis, service.clone(), dice_artifacts)?;
451
452 // Wait for encryptedstore to finish mounting the storage (if enabled) before setting
453 // microdroid_manager.init_done. Reason is init stops uneventd after that.
454 // Encryptedstore, however requires ueventd
455 if let Some(mut child) = encryptedstore_child {
456 let exitcode = child.wait().context("Wait for encryptedstore child")?;
457 ensure!(exitcode.success(), "Unable to prepare encrypted storage. Exitcode={}", exitcode);
458 }
459
460 wait_for_property_true("dev.bootcomplete").context("failed waiting for dev.bootcomplete")?;
461 system_properties::write("microdroid_manager.init_done", "1")
462 .context("set microdroid_manager.init_done")?;
463
464 info!("boot completed, time to run payload");
465 exec_task(task, service).context("Failed to run payload")
466 }
467
468 struct ApkDmverityArgument<'a> {
469 apk: &'a str,
470 idsig: &'a str,
471 name: &'a str,
472 saved_root_hash: Option<&'a RootHash>,
473 }
474
run_apkdmverity(args: &[ApkDmverityArgument]) -> Result<Child>475 fn run_apkdmverity(args: &[ApkDmverityArgument]) -> Result<Child> {
476 let mut cmd = Command::new(APKDMVERITY_BIN);
477
478 for argument in args {
479 cmd.arg("--apk").arg(argument.apk).arg(argument.idsig).arg(argument.name);
480 if let Some(root_hash) = argument.saved_root_hash {
481 cmd.arg(&to_hex_string(root_hash));
482 } else {
483 cmd.arg("none");
484 }
485 }
486
487 cmd.spawn().context("Spawn apkdmverity")
488 }
489
490 enum MountForExec {
491 Allowed,
492 Disallowed,
493 }
494
495 #[derive(Default)]
496 struct Zipfuse {
497 ready_properties: Vec<String>,
498 }
499
500 impl Zipfuse {
501 const MICRODROID_PAYLOAD_UID: u32 = 0; // TODO(b/264861173) should be non-root
502 const MICRODROID_PAYLOAD_GID: u32 = 0; // TODO(b/264861173) should be non-root
mount( &mut self, noexec: MountForExec, option: &str, zip_path: &Path, mount_dir: &Path, ready_prop: String, ) -> Result<Child>503 fn mount(
504 &mut self,
505 noexec: MountForExec,
506 option: &str,
507 zip_path: &Path,
508 mount_dir: &Path,
509 ready_prop: String,
510 ) -> Result<Child> {
511 let mut cmd = Command::new(ZIPFUSE_BIN);
512 if let MountForExec::Disallowed = noexec {
513 cmd.arg("--noexec");
514 }
515 cmd.args(["-p", &ready_prop, "-o", option]);
516 cmd.args(["-u", &Self::MICRODROID_PAYLOAD_UID.to_string()]);
517 cmd.args(["-g", &Self::MICRODROID_PAYLOAD_GID.to_string()]);
518 cmd.arg(zip_path).arg(mount_dir);
519 self.ready_properties.push(ready_prop);
520 cmd.spawn().with_context(|| format!("Failed to run zipfuse for {mount_dir:?}"))
521 }
522
wait_until_done(self) -> Result<()>523 fn wait_until_done(self) -> Result<()> {
524 // We check the last-started check first in the hope that by the time it is done
525 // all or most of the others will also be done, minimising the number of times we
526 // block on a property.
527 for property in self.ready_properties.into_iter().rev() {
528 wait_for_property_true(&property)
529 .with_context(|| format!("Failed waiting for {property}"))?;
530 }
531 Ok(())
532 }
533 }
534
write_apex_payload_data( saved_data: Option<&MicrodroidData>, apex_data_from_payload: &[ApexData], ) -> Result<()>535 fn write_apex_payload_data(
536 saved_data: Option<&MicrodroidData>,
537 apex_data_from_payload: &[ApexData],
538 ) -> Result<()> {
539 if let Some(saved_apex_data) = saved_data.map(|d| &d.apex_data) {
540 // We don't support APEX updates. (assuming that update will change root digest)
541 ensure!(
542 saved_apex_data == apex_data_from_payload,
543 MicrodroidError::PayloadChanged(String::from("APEXes have changed."))
544 );
545 let apex_metadata = to_metadata(apex_data_from_payload);
546 // Pass metadata(with public keys and root digests) to apexd so that it uses the passed
547 // metadata instead of the default one (/dev/block/by-name/payload-metadata)
548 OpenOptions::new()
549 .create_new(true)
550 .write(true)
551 .open("/apex/vm-payload-metadata")
552 .context("Failed to open /apex/vm-payload-metadata")
553 .and_then(|f| write_metadata(&apex_metadata, f))?;
554 }
555 Ok(())
556 }
557
558 // Verify payload before executing it. For APK payload, Full verification (which is slow) is done
559 // when the root_hash values from the idsig file and the instance disk are different. This function
560 // returns the verified root hash (for APK payload) and pubkeys (for APEX payloads) that can be
561 // saved to the instance disk.
verify_payload( metadata: &Metadata, saved_data: Option<&MicrodroidData>, ) -> Result<MicrodroidData>562 fn verify_payload(
563 metadata: &Metadata,
564 saved_data: Option<&MicrodroidData>,
565 ) -> Result<MicrodroidData> {
566 let start_time = SystemTime::now();
567
568 // Verify main APK
569 let root_hash_from_idsig = get_apk_root_hash_from_idsig(MAIN_APK_IDSIG_PATH)?;
570 let root_hash_trustful =
571 saved_data.map(|d| d.apk_data.root_hash_eq(root_hash_from_idsig.as_ref())).unwrap_or(false);
572
573 // If root_hash can be trusted, pass it to apkdmverity so that it uses the passed root_hash
574 // instead of the value read from the idsig file.
575 let main_apk_argument = {
576 ApkDmverityArgument {
577 apk: MAIN_APK_PATH,
578 idsig: MAIN_APK_IDSIG_PATH,
579 name: MAIN_APK_DEVICE_NAME,
580 saved_root_hash: if root_hash_trustful {
581 Some(root_hash_from_idsig.as_ref())
582 } else {
583 None
584 },
585 }
586 };
587 let mut apkdmverity_arguments = vec![main_apk_argument];
588
589 // Verify extra APKs
590 // For now, we can't read the payload config, so glob APKs and idsigs.
591 // Later, we'll see if it matches with the payload config.
592
593 // sort globbed paths to match apks (extra-apk-{idx}) and idsigs (extra-idsig-{idx})
594 // e.g. "extra-apk-0" corresponds to "extra-idsig-0"
595 let extra_apks =
596 sorted(glob(EXTRA_APK_PATH_PATTERN)?.collect::<Result<Vec<_>, _>>()?).collect::<Vec<_>>();
597 let extra_idsigs =
598 sorted(glob(EXTRA_IDSIG_PATH_PATTERN)?.collect::<Result<Vec<_>, _>>()?).collect::<Vec<_>>();
599 ensure!(
600 extra_apks.len() == extra_idsigs.len(),
601 "Extra apks/idsigs mismatch: {} apks but {} idsigs",
602 extra_apks.len(),
603 extra_idsigs.len()
604 );
605
606 let extra_root_hashes_from_idsig: Vec<_> = extra_idsigs
607 .iter()
608 .map(|idsig| {
609 get_apk_root_hash_from_idsig(idsig).expect("Can't find root hash from extra idsig")
610 })
611 .collect();
612
613 let extra_root_hashes_trustful: Vec<_> = if let Some(data) = saved_data {
614 extra_root_hashes_from_idsig
615 .iter()
616 .enumerate()
617 .map(|(i, root_hash)| data.extra_apk_root_hash_eq(i, root_hash))
618 .collect()
619 } else {
620 vec![false; extra_root_hashes_from_idsig.len()]
621 };
622 let extra_apk_names: Vec<_> =
623 (0..extra_apks.len()).map(|i| format!("extra-apk-{}", i)).collect();
624
625 for (i, extra_apk) in extra_apks.iter().enumerate() {
626 apkdmverity_arguments.push({
627 ApkDmverityArgument {
628 apk: extra_apk.to_str().unwrap(),
629 idsig: extra_idsigs[i].to_str().unwrap(),
630 name: &extra_apk_names[i],
631 saved_root_hash: if extra_root_hashes_trustful[i] {
632 Some(&extra_root_hashes_from_idsig[i])
633 } else {
634 None
635 },
636 }
637 });
638 }
639
640 // Start apkdmverity and wait for the dm-verify block
641 let mut apkdmverity_child = run_apkdmverity(&apkdmverity_arguments)?;
642
643 // While waiting for apkdmverity to mount APK, gathers public keys and root digests from
644 // APEX payload.
645 let apex_data_from_payload = get_apex_data_from_payload(metadata)?;
646
647 // Writing /apex/vm-payload-metadata is to verify that the payload isn't changed.
648 // Skip writing it if the debug policy ignoring identity is on
649 if is_verified_boot() {
650 write_apex_payload_data(saved_data, &apex_data_from_payload)?;
651 }
652
653 // Start apexd to activate APEXes
654 system_properties::write("ctl.start", "apexd-vm")?;
655
656 // TODO(inseob): add timeout
657 apkdmverity_child.wait()?;
658
659 // Do the full verification if the root_hash is un-trustful. This requires the full scanning of
660 // the APK file and therefore can be very slow if the APK is large. Note that this step is
661 // taken only when the root_hash is un-trustful which can be either when this is the first boot
662 // of the VM or APK was updated in the host.
663 // TODO(jooyung): consider multithreading to make this faster
664 let main_apk_pubkey = get_public_key_from_apk(DM_MOUNTED_APK_PATH, root_hash_trustful)?;
665 let extra_apks_data = extra_root_hashes_from_idsig
666 .into_iter()
667 .enumerate()
668 .map(|(i, extra_root_hash)| {
669 let mount_path = format!("/dev/block/mapper/{}", &extra_apk_names[i]);
670 let apk_pubkey = get_public_key_from_apk(&mount_path, extra_root_hashes_trustful[i])?;
671 Ok(ApkData { root_hash: extra_root_hash, pubkey: apk_pubkey })
672 })
673 .collect::<Result<Vec<_>>>()?;
674
675 info!("payload verification successful. took {:#?}", start_time.elapsed().unwrap());
676
677 // Use the salt from a verified instance, or generate a salt for a new instance.
678 let salt = if let Some(saved_data) = saved_data {
679 saved_data.salt.clone()
680 } else if is_strict_boot() {
681 // No need to add more entropy as a previous stage must have used a new, random salt.
682 vec![0u8; 64]
683 } else {
684 let mut salt = vec![0u8; 64];
685 salt.as_mut_slice().try_fill(&mut rand::thread_rng())?;
686 salt
687 };
688
689 // At this point, we can ensure that the root_hash from the idsig file is trusted, either by
690 // fully verifying the APK or by comparing it with the saved root_hash.
691 Ok(MicrodroidData {
692 salt,
693 apk_data: ApkData { root_hash: root_hash_from_idsig, pubkey: main_apk_pubkey },
694 extra_apks_data,
695 apex_data: apex_data_from_payload,
696 })
697 }
698
mount_extra_apks(config: &VmPayloadConfig, zipfuse: &mut Zipfuse) -> Result<()>699 fn mount_extra_apks(config: &VmPayloadConfig, zipfuse: &mut Zipfuse) -> Result<()> {
700 // For now, only the number of apks is important, as the mount point and dm-verity name is fixed
701 for i in 0..config.extra_apks.len() {
702 let mount_dir = format!("/mnt/extra-apk/{i}");
703 create_dir(Path::new(&mount_dir)).context("Failed to create mount dir for extra apks")?;
704
705 // don't wait, just detach
706 zipfuse.mount(
707 MountForExec::Disallowed,
708 "fscontext=u:object_r:zipfusefs:s0,context=u:object_r:extra_apk_file:s0",
709 Path::new(&format!("/dev/block/mapper/extra-apk-{i}")),
710 Path::new(&mount_dir),
711 format!("microdroid_manager.extra_apk.mounted.{i}"),
712 )?;
713 }
714
715 Ok(())
716 }
717
setup_config_sysprops(config: &VmPayloadConfig) -> Result<()>718 fn setup_config_sysprops(config: &VmPayloadConfig) -> Result<()> {
719 if config.enable_authfs {
720 system_properties::write("microdroid_manager.authfs.enabled", "1")
721 .context("failed to write microdroid_manager.authfs.enabled")?;
722 }
723 system_properties::write("microdroid_manager.config_done", "1")
724 .context("failed to write microdroid_manager.config_done")?;
725 Ok(())
726 }
727
728 // Waits until linker config is generated
wait_for_apex_config_done() -> Result<()>729 fn wait_for_apex_config_done() -> Result<()> {
730 wait_for_property_true(APEX_CONFIG_DONE_PROP).context("Failed waiting for apex config done")
731 }
732
wait_for_property_true(property_name: &str) -> Result<()>733 fn wait_for_property_true(property_name: &str) -> Result<()> {
734 let mut prop = PropertyWatcher::new(property_name)?;
735 loop {
736 prop.wait()?;
737 if system_properties::read_bool(property_name, false)? {
738 break;
739 }
740 }
741 Ok(())
742 }
743
get_apk_root_hash_from_idsig<P: AsRef<Path>>(idsig_path: P) -> Result<Box<RootHash>>744 fn get_apk_root_hash_from_idsig<P: AsRef<Path>>(idsig_path: P) -> Result<Box<RootHash>> {
745 Ok(V4Signature::from_idsig_path(idsig_path)?.hashing_info.raw_root_hash)
746 }
747
get_public_key_from_apk(apk: &str, root_hash_trustful: bool) -> Result<Box<[u8]>>748 fn get_public_key_from_apk(apk: &str, root_hash_trustful: bool) -> Result<Box<[u8]>> {
749 let current_sdk = get_current_sdk()?;
750 if !root_hash_trustful {
751 verify(apk, current_sdk).context(MicrodroidError::PayloadVerificationFailed(format!(
752 "failed to verify {}",
753 apk
754 )))
755 } else {
756 get_public_key_der(apk, current_sdk)
757 }
758 }
759
get_current_sdk() -> Result<u32>760 fn get_current_sdk() -> Result<u32> {
761 let current_sdk = system_properties::read("ro.build.version.sdk")?;
762 let current_sdk = current_sdk.ok_or_else(|| anyhow!("SDK version missing"))?;
763 current_sdk.parse().context("Malformed SDK version")
764 }
765
load_config(payload_metadata: PayloadMetadata) -> Result<VmPayloadConfig>766 fn load_config(payload_metadata: PayloadMetadata) -> Result<VmPayloadConfig> {
767 match payload_metadata {
768 PayloadMetadata::config_path(path) => {
769 let path = Path::new(&path);
770 info!("loading config from {:?}...", path);
771 let file = ioutil::wait_for_file(path, WAIT_TIMEOUT)
772 .with_context(|| format!("Failed to read {:?}", path))?;
773 Ok(serde_json::from_reader(file)?)
774 }
775 PayloadMetadata::config(payload_config) => {
776 let task = Task {
777 type_: TaskType::MicrodroidLauncher,
778 command: payload_config.payload_binary_name,
779 };
780 Ok(VmPayloadConfig {
781 os: OsConfig { name: "microdroid".to_owned() },
782 task: Some(task),
783 apexes: vec![],
784 extra_apks: vec![],
785 prefer_staged: false,
786 export_tombstones: None,
787 enable_authfs: false,
788 })
789 }
790 }
791 }
792
793 /// Loads the crashkernel into memory using kexec if debuggable or debug policy says so.
794 /// The VM should be loaded with `crashkernel=' parameter in the cmdline to allocate memory
795 /// for crashkernel.
load_crashkernel_if_supported() -> Result<()>796 fn load_crashkernel_if_supported() -> Result<()> {
797 let supported = std::fs::read_to_string("/proc/cmdline")?.contains(" crashkernel=");
798 info!("ramdump supported: {}", supported);
799
800 if !supported {
801 return Ok(());
802 }
803
804 let debuggable = system_properties::read_bool(DEBUGGABLE_PROP, true)?;
805 let ramdump = get_debug_policy_bool(AVF_DEBUG_POLICY_RAMDUMP)?.unwrap_or_default();
806 let requested = debuggable | ramdump;
807
808 if requested {
809 let status = Command::new("/system/bin/kexec_load").status()?;
810 if !status.success() {
811 return Err(anyhow!("Failed to load crashkernel: {:?}", status));
812 }
813 info!("ramdump is loaded: debuggable={debuggable}, ramdump={ramdump}");
814 }
815 Ok(())
816 }
817
818 /// Executes the given task.
exec_task(task: &Task, service: &Strong<dyn IVirtualMachineService>) -> Result<i32>819 fn exec_task(task: &Task, service: &Strong<dyn IVirtualMachineService>) -> Result<i32> {
820 info!("executing main task {:?}...", task);
821 let mut command = match task.type_ {
822 TaskType::Executable => Command::new(&task.command),
823 TaskType::MicrodroidLauncher => {
824 let mut command = Command::new("/system/bin/microdroid_launcher");
825 command.arg(find_library_path(&task.command)?);
826 command
827 }
828 };
829
830 unsafe {
831 // SAFETY: we are not accessing any resource of the parent process.
832 command.pre_exec(|| {
833 info!("dropping capabilities before executing payload");
834 // It is OK to continue with payload execution even if the calls below fail, since
835 // whether process can use a capability is controlled by the SELinux. Dropping the
836 // capabilities here is just another defense-in-depth layer.
837 if let Err(e) = cap::drop_inheritable_caps() {
838 error!("failed to drop inheritable capabilities: {:?}", e);
839 }
840 if let Err(e) = cap::drop_bounding_set() {
841 error!("failed to drop bounding set: {:?}", e);
842 }
843 Ok(())
844 });
845 }
846
847 command.stdin(Stdio::null()).stdout(Stdio::null()).stderr(Stdio::null());
848
849 info!("notifying payload started");
850 service.notifyPayloadStarted()?;
851
852 let exit_status = command.spawn()?.wait()?;
853 match exit_status.code() {
854 Some(exit_code) => Ok(exit_code),
855 None => Err(match exit_status.signal() {
856 Some(signal) => anyhow!(
857 "Payload exited due to signal: {} ({})",
858 signal,
859 Signal::try_from(signal).map_or("unknown", |s| s.as_str())
860 ),
861 None => anyhow!("Payload has neither exit code nor signal"),
862 }),
863 }
864 }
865
find_library_path(name: &str) -> Result<String>866 fn find_library_path(name: &str) -> Result<String> {
867 let mut watcher = PropertyWatcher::new("ro.product.cpu.abilist")?;
868 let value = watcher.read(|_name, value| Ok(value.trim().to_string()))?;
869 let abi = value.split(',').next().ok_or_else(|| anyhow!("no abilist"))?;
870 let path = format!("{}/lib/{}/{}", VM_APK_CONTENTS_PATH, abi, name);
871
872 let metadata = fs::metadata(&path).with_context(|| format!("Unable to access {}", path))?;
873 if !metadata.is_file() {
874 bail!("{} is not a file", &path);
875 }
876
877 Ok(path)
878 }
879
to_hex_string(buf: &[u8]) -> String880 fn to_hex_string(buf: &[u8]) -> String {
881 buf.iter().map(|b| format!("{:02X}", b)).collect()
882 }
883
prepare_encryptedstore(dice_artifacts: &OwnedDiceArtifacts) -> Result<Child>884 fn prepare_encryptedstore(dice_artifacts: &OwnedDiceArtifacts) -> Result<Child> {
885 // Use a fixed salt to scope the derivation to this API.
886 // Generated using hexdump -vn32 -e'14/1 "0x%02X, " 1 "\n"' /dev/urandom
887 // TODO(b/241541860) : Move this (& other salts) to a salt container, i.e. a global enum
888 let salt = [
889 0xFC, 0x1D, 0x35, 0x7B, 0x96, 0xF3, 0xEF, 0x17, 0x78, 0x7D, 0x70, 0xED, 0xEA, 0xFE, 0x1D,
890 0x6F, 0xB3, 0xF9, 0x40, 0xCE, 0xDD, 0x99, 0x40, 0xAA, 0xA7, 0x0E, 0x92, 0x73, 0x90, 0x86,
891 0x4A, 0x75,
892 ];
893 let mut key = ZVec::new(ENCRYPTEDSTORE_KEYSIZE)?;
894 derive_sealing_key(dice_artifacts, &salt, ENCRYPTEDSTORE_KEY_IDENTIFIER.as_bytes(), &mut key)?;
895
896 let mut cmd = Command::new(ENCRYPTEDSTORE_BIN);
897 cmd.arg("--blkdevice")
898 .arg(ENCRYPTEDSTORE_BACKING_DEVICE)
899 .arg("--key")
900 .arg(hex::encode(&*key))
901 .args(["--mountpoint", ENCRYPTEDSTORE_MOUNTPOINT])
902 .spawn()
903 .context("encryptedstore failed")
904 }
905