• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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