blob: 1a6e5928662f6aeb14083d2f80a7d2f33dcc19e1 [file] [log] [blame]
Alan Stokes6b2d0a82021-09-29 11:30:39 +01001/*
2 * Copyright (C) 2021 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17//! Responsible for validating and starting an existing instance of the CompOS VM, or creating and
18//! starting a new instance if necessary.
19
20use android_system_virtualizationservice::aidl::android::system::virtualizationservice::{
21 IVirtualizationService::IVirtualizationService, PartitionType::PartitionType,
22};
23use anyhow::{bail, Context, Result};
24use compos_aidl_interface::aidl::com::android::compos::ICompOsService::ICompOsService;
25use compos_aidl_interface::binder::{ParcelFileDescriptor, Strong};
Alan Stokesd21764c2021-10-25 15:33:40 +010026use compos_common::compos_client::{VmInstance, VmParameters};
Alan Stokes6b2d0a82021-09-29 11:30:39 +010027use compos_common::{
28 COMPOS_DATA_ROOT, INSTANCE_IMAGE_FILE, PRIVATE_KEY_BLOB_FILE, PUBLIC_KEY_FILE,
29};
30use log::{info, warn};
Victor Hsieh18775b12021-10-12 17:42:48 -070031use std::env;
Alan Stokes6b2d0a82021-09-29 11:30:39 +010032use std::fs;
33use std::path::{Path, PathBuf};
34
35pub struct CompOsInstance {
36 #[allow(dead_code)] // Keeps VirtualizationService & the VM alive
37 vm_instance: VmInstance,
38 service: Strong<dyn ICompOsService>,
39}
40
41impl CompOsInstance {
42 pub fn get_service(&self) -> Strong<dyn ICompOsService> {
43 self.service.clone()
44 }
45}
46
47pub struct InstanceStarter {
48 instance_name: String,
49 instance_root: PathBuf,
50 instance_image: PathBuf,
51 key_blob: PathBuf,
52 public_key: PathBuf,
Alan Stokesd21764c2021-10-25 15:33:40 +010053 vm_parameters: VmParameters,
Alan Stokes6b2d0a82021-09-29 11:30:39 +010054}
55
56impl InstanceStarter {
Alan Stokesd21764c2021-10-25 15:33:40 +010057 pub fn new(instance_name: &str, vm_parameters: VmParameters) -> Self {
Alan Stokes6b2d0a82021-09-29 11:30:39 +010058 let instance_root = Path::new(COMPOS_DATA_ROOT).join(instance_name);
59 let instant_root_path = instance_root.as_path();
60 let instance_image = instant_root_path.join(INSTANCE_IMAGE_FILE);
61 let key_blob = instant_root_path.join(PRIVATE_KEY_BLOB_FILE);
62 let public_key = instant_root_path.join(PUBLIC_KEY_FILE);
63 Self {
64 instance_name: instance_name.to_owned(),
65 instance_root,
66 instance_image,
67 key_blob,
68 public_key,
Alan Stokesd21764c2021-10-25 15:33:40 +010069 vm_parameters,
Alan Stokes6b2d0a82021-09-29 11:30:39 +010070 }
71 }
72
73 pub fn create_or_start_instance(
74 &self,
Alan Stokesd21764c2021-10-25 15:33:40 +010075 virtualization_service: &dyn IVirtualizationService,
Alan Stokes6b2d0a82021-09-29 11:30:39 +010076 ) -> Result<CompOsInstance> {
Alan Stokesd21764c2021-10-25 15:33:40 +010077 let compos_instance = self.start_existing_instance(virtualization_service);
Alan Stokes6b2d0a82021-09-29 11:30:39 +010078 match compos_instance {
79 Ok(_) => return compos_instance,
Alan Stokes14f07392021-09-27 14:03:31 +010080 Err(e) => warn!("Failed to start: {}", e),
Alan Stokes6b2d0a82021-09-29 11:30:39 +010081 }
82
Alan Stokesd21764c2021-10-25 15:33:40 +010083 self.start_new_instance(virtualization_service)
Alan Stokes6b2d0a82021-09-29 11:30:39 +010084 }
85
Alan Stokesd21764c2021-10-25 15:33:40 +010086 fn start_existing_instance(
87 &self,
88 virtualization_service: &dyn IVirtualizationService,
89 ) -> Result<CompOsInstance> {
Alan Stokes6b2d0a82021-09-29 11:30:39 +010090 // No point even trying if the files we need aren't there.
91 self.check_files_exist()?;
92
Alan Stokes14f07392021-09-27 14:03:31 +010093 info!("Starting {} CompOs instance", self.instance_name);
94
Alan Stokes6b2d0a82021-09-29 11:30:39 +010095 let key_blob = fs::read(&self.key_blob).context("Reading private key blob")?;
96 let public_key = fs::read(&self.public_key).context("Reading public key")?;
97
Alan Stokesd21764c2021-10-25 15:33:40 +010098 let compos_instance = self.start_vm(virtualization_service)?;
Alan Stokes16e027f2021-10-04 17:57:31 +010099 let service = &compos_instance.service;
Alan Stokes6b2d0a82021-09-29 11:30:39 +0100100
101 if !service.verifySigningKey(&key_blob, &public_key).context("Verifying key pair")? {
102 bail!("Key pair invalid");
103 }
104
105 // If we get this far then the instance image is valid in the current context (e.g. the
106 // current set of APEXes) and the key blob can be successfully decrypted by the VM. So the
107 // files have not been tampered with and we're good to go.
108
Victor Hsieh18775b12021-10-12 17:42:48 -0700109 Self::initialize_service(service, &key_blob)?;
Alan Stokes6b2d0a82021-09-29 11:30:39 +0100110
Alan Stokes16e027f2021-10-04 17:57:31 +0100111 Ok(compos_instance)
Alan Stokes6b2d0a82021-09-29 11:30:39 +0100112 }
113
114 fn start_new_instance(
115 &self,
116 virtualization_service: &dyn IVirtualizationService,
117 ) -> Result<CompOsInstance> {
118 info!("Creating {} CompOs instance", self.instance_name);
119
120 // Ignore failure here - the directory may already exist.
121 let _ = fs::create_dir(&self.instance_root);
122
123 self.create_instance_image(virtualization_service)?;
124
Alan Stokesd21764c2021-10-25 15:33:40 +0100125 let compos_instance = self.start_vm(virtualization_service)?;
Alan Stokes16e027f2021-10-04 17:57:31 +0100126 let service = &compos_instance.service;
Alan Stokes6b2d0a82021-09-29 11:30:39 +0100127
128 let key_data = service.generateSigningKey().context("Generating signing key")?;
129 fs::write(&self.key_blob, &key_data.keyBlob).context("Writing key blob")?;
Alan Stokes14f07392021-09-27 14:03:31 +0100130
131 let key_result = composd_native::extract_rsa_public_key(&key_data.certificate);
132 let rsa_public_key = key_result.key;
133 if rsa_public_key.is_empty() {
134 bail!("Failed to extract public key from certificate: {}", key_result.error);
135 }
136 fs::write(&self.public_key, &rsa_public_key).context("Writing public key")?;
Alan Stokes6b2d0a82021-09-29 11:30:39 +0100137
Victor Hsieh18775b12021-10-12 17:42:48 -0700138 // Unlike when starting an existing instance, we don't need to verify the key, since we
139 // just generated it and have it in memory.
Alan Stokes6b2d0a82021-09-29 11:30:39 +0100140
Victor Hsieh18775b12021-10-12 17:42:48 -0700141 Self::initialize_service(service, &key_data.keyBlob)?;
Alan Stokes6b2d0a82021-09-29 11:30:39 +0100142
Alan Stokes16e027f2021-10-04 17:57:31 +0100143 Ok(compos_instance)
144 }
145
Victor Hsieh18775b12021-10-12 17:42:48 -0700146 fn initialize_service(service: &Strong<dyn ICompOsService>, key_blob: &[u8]) -> Result<()> {
147 // Key blob is assumed to be verified/trusted.
148 service.initializeSigningKey(key_blob).context("Loading signing key")?;
149
150 // TODO(198211396): Implement correctly.
151 service
152 .initializeClasspaths(&env::var("BOOTCLASSPATH")?, &env::var("DEX2OATBOOTCLASSPATH")?)
153 .context("Initializing *CLASSPATH")?;
154 Ok(())
155 }
156
Alan Stokesd21764c2021-10-25 15:33:40 +0100157 fn start_vm(
158 &self,
159 virtualization_service: &dyn IVirtualizationService,
160 ) -> Result<CompOsInstance> {
Alan Stokes16e027f2021-10-04 17:57:31 +0100161 let instance_image = fs::OpenOptions::new()
162 .read(true)
163 .write(true)
164 .open(&self.instance_image)
165 .context("Failed to open instance image")?;
Alan Stokesd21764c2021-10-25 15:33:40 +0100166 let vm_instance =
167 VmInstance::start(virtualization_service, instance_image, &self.vm_parameters)
168 .context("Starting VM")?;
Alan Stokes16e027f2021-10-04 17:57:31 +0100169 let service = vm_instance.get_service().context("Connecting to CompOS")?;
Alan Stokes6b2d0a82021-09-29 11:30:39 +0100170 Ok(CompOsInstance { vm_instance, service })
171 }
172
173 fn create_instance_image(
174 &self,
175 virtualization_service: &dyn IVirtualizationService,
176 ) -> Result<()> {
177 let instance_image = fs::OpenOptions::new()
178 .create(true)
179 .read(true)
180 .write(true)
181 .open(&self.instance_image)
182 .context("Creating instance image file")?;
183 let instance_image = ParcelFileDescriptor::new(instance_image);
184 // TODO: Where does this number come from?
185 let size = 10 * 1024 * 1024;
186 virtualization_service
187 .initializeWritablePartition(&instance_image, size, PartitionType::ANDROID_VM_INSTANCE)
188 .context("Writing instance image file")?;
189 Ok(())
190 }
191
192 fn check_files_exist(&self) -> Result<()> {
193 if !self.instance_root.is_dir() {
194 bail!("Directory {} not found", self.instance_root.display())
195 };
196 Self::check_file_exists(&self.instance_image)?;
197 Self::check_file_exists(&self.key_blob)?;
198 Self::check_file_exists(&self.public_key)?;
199 Ok(())
200 }
201
202 fn check_file_exists(file: &Path) -> Result<()> {
203 if !file.is_file() {
204 bail!("File {} not found", file.display())
205 };
206 Ok(())
207 }
208}