blob: 91a0e6154d686b40f488ac477d5dbf71cc665244 [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};
Alan Stokes9ca14ca2021-10-20 14:25:57 +010024use binder_common::lazy_service::LazyServiceGuard;
Alan Stokes6b2d0a82021-09-29 11:30:39 +010025use compos_aidl_interface::aidl::com::android::compos::ICompOsService::ICompOsService;
26use compos_aidl_interface::binder::{ParcelFileDescriptor, Strong};
Alan Stokesd21764c2021-10-25 15:33:40 +010027use compos_common::compos_client::{VmInstance, VmParameters};
Alan Stokes6b2d0a82021-09-29 11:30:39 +010028use compos_common::{
Jooyung Hanccae66d2021-12-20 11:54:36 +090029 COMPOS_DATA_ROOT, IDSIG_FILE, INSTANCE_IMAGE_FILE, PRIVATE_KEY_BLOB_FILE, PUBLIC_KEY_FILE,
Alan Stokes6b2d0a82021-09-29 11:30:39 +010030};
31use log::{info, warn};
Victor Hsieh18775b12021-10-12 17:42:48 -070032use std::env;
Alan Stokes6b2d0a82021-09-29 11:30:39 +010033use std::fs;
34use std::path::{Path, PathBuf};
35
36pub struct CompOsInstance {
Alan Stokes9ca14ca2021-10-20 14:25:57 +010037 service: Strong<dyn ICompOsService>,
Alan Stokes6b2d0a82021-09-29 11:30:39 +010038 #[allow(dead_code)] // Keeps VirtualizationService & the VM alive
39 vm_instance: VmInstance,
Alan Stokes9ca14ca2021-10-20 14:25:57 +010040 #[allow(dead_code)] // Keeps composd process alive
41 lazy_service_guard: LazyServiceGuard,
Alan Stokes6b2d0a82021-09-29 11:30:39 +010042}
43
44impl CompOsInstance {
45 pub fn get_service(&self) -> Strong<dyn ICompOsService> {
46 self.service.clone()
47 }
48}
49
50pub struct InstanceStarter {
51 instance_name: String,
52 instance_root: PathBuf,
53 instance_image: PathBuf,
Jooyung Hanccae66d2021-12-20 11:54:36 +090054 idsig: PathBuf,
Alan Stokes6b2d0a82021-09-29 11:30:39 +010055 key_blob: PathBuf,
56 public_key: PathBuf,
Alan Stokesd21764c2021-10-25 15:33:40 +010057 vm_parameters: VmParameters,
Alan Stokes6b2d0a82021-09-29 11:30:39 +010058}
59
60impl InstanceStarter {
Alan Stokesd21764c2021-10-25 15:33:40 +010061 pub fn new(instance_name: &str, vm_parameters: VmParameters) -> Self {
Alan Stokes6b2d0a82021-09-29 11:30:39 +010062 let instance_root = Path::new(COMPOS_DATA_ROOT).join(instance_name);
Jooyung Hanccae66d2021-12-20 11:54:36 +090063 let instance_root_path = instance_root.as_path();
64 let instance_image = instance_root_path.join(INSTANCE_IMAGE_FILE);
65 let idsig = instance_root_path.join(IDSIG_FILE);
66 let key_blob = instance_root_path.join(PRIVATE_KEY_BLOB_FILE);
67 let public_key = instance_root_path.join(PUBLIC_KEY_FILE);
Alan Stokes6b2d0a82021-09-29 11:30:39 +010068 Self {
69 instance_name: instance_name.to_owned(),
70 instance_root,
71 instance_image,
Jooyung Hanccae66d2021-12-20 11:54:36 +090072 idsig,
Alan Stokes6b2d0a82021-09-29 11:30:39 +010073 key_blob,
74 public_key,
Alan Stokesd21764c2021-10-25 15:33:40 +010075 vm_parameters,
Alan Stokes6b2d0a82021-09-29 11:30:39 +010076 }
77 }
78
79 pub fn create_or_start_instance(
80 &self,
Alan Stokesd21764c2021-10-25 15:33:40 +010081 virtualization_service: &dyn IVirtualizationService,
Alan Stokes6b2d0a82021-09-29 11:30:39 +010082 ) -> Result<CompOsInstance> {
Alan Stokesd21764c2021-10-25 15:33:40 +010083 let compos_instance = self.start_existing_instance(virtualization_service);
Alan Stokes6b2d0a82021-09-29 11:30:39 +010084 match compos_instance {
85 Ok(_) => return compos_instance,
Alan Stokes14f07392021-09-27 14:03:31 +010086 Err(e) => warn!("Failed to start: {}", e),
Alan Stokes6b2d0a82021-09-29 11:30:39 +010087 }
88
Alan Stokesd21764c2021-10-25 15:33:40 +010089 self.start_new_instance(virtualization_service)
Alan Stokes6b2d0a82021-09-29 11:30:39 +010090 }
91
Alan Stokesd21764c2021-10-25 15:33:40 +010092 fn start_existing_instance(
93 &self,
94 virtualization_service: &dyn IVirtualizationService,
95 ) -> Result<CompOsInstance> {
Alan Stokes6b2d0a82021-09-29 11:30:39 +010096 // No point even trying if the files we need aren't there.
97 self.check_files_exist()?;
98
Alan Stokes14f07392021-09-27 14:03:31 +010099 info!("Starting {} CompOs instance", self.instance_name);
100
Alan Stokes6b2d0a82021-09-29 11:30:39 +0100101 let key_blob = fs::read(&self.key_blob).context("Reading private key blob")?;
102 let public_key = fs::read(&self.public_key).context("Reading public key")?;
103
Alan Stokesd21764c2021-10-25 15:33:40 +0100104 let compos_instance = self.start_vm(virtualization_service)?;
Alan Stokes16e027f2021-10-04 17:57:31 +0100105 let service = &compos_instance.service;
Alan Stokes6b2d0a82021-09-29 11:30:39 +0100106
107 if !service.verifySigningKey(&key_blob, &public_key).context("Verifying key pair")? {
108 bail!("Key pair invalid");
109 }
110
111 // If we get this far then the instance image is valid in the current context (e.g. the
112 // current set of APEXes) and the key blob can be successfully decrypted by the VM. So the
113 // files have not been tampered with and we're good to go.
114
Victor Hsieh18775b12021-10-12 17:42:48 -0700115 Self::initialize_service(service, &key_blob)?;
Alan Stokes6b2d0a82021-09-29 11:30:39 +0100116
Alan Stokes16e027f2021-10-04 17:57:31 +0100117 Ok(compos_instance)
Alan Stokes6b2d0a82021-09-29 11:30:39 +0100118 }
119
120 fn start_new_instance(
121 &self,
122 virtualization_service: &dyn IVirtualizationService,
123 ) -> Result<CompOsInstance> {
124 info!("Creating {} CompOs instance", self.instance_name);
125
126 // Ignore failure here - the directory may already exist.
127 let _ = fs::create_dir(&self.instance_root);
128
129 self.create_instance_image(virtualization_service)?;
Jooyung Hanccae66d2021-12-20 11:54:36 +0900130 // Delete existing idsig file. Ignore error in case idsig doesn't exist.
131 let _ = fs::remove_file(&self.idsig);
Alan Stokes6b2d0a82021-09-29 11:30:39 +0100132
Alan Stokesd21764c2021-10-25 15:33:40 +0100133 let compos_instance = self.start_vm(virtualization_service)?;
Alan Stokes16e027f2021-10-04 17:57:31 +0100134 let service = &compos_instance.service;
Alan Stokes6b2d0a82021-09-29 11:30:39 +0100135
136 let key_data = service.generateSigningKey().context("Generating signing key")?;
137 fs::write(&self.key_blob, &key_data.keyBlob).context("Writing key blob")?;
Alan Stokes14f07392021-09-27 14:03:31 +0100138
139 let key_result = composd_native::extract_rsa_public_key(&key_data.certificate);
140 let rsa_public_key = key_result.key;
141 if rsa_public_key.is_empty() {
142 bail!("Failed to extract public key from certificate: {}", key_result.error);
143 }
144 fs::write(&self.public_key, &rsa_public_key).context("Writing public key")?;
Alan Stokes6b2d0a82021-09-29 11:30:39 +0100145
Victor Hsieh18775b12021-10-12 17:42:48 -0700146 // Unlike when starting an existing instance, we don't need to verify the key, since we
147 // just generated it and have it in memory.
Alan Stokes6b2d0a82021-09-29 11:30:39 +0100148
Victor Hsieh18775b12021-10-12 17:42:48 -0700149 Self::initialize_service(service, &key_data.keyBlob)?;
Alan Stokes6b2d0a82021-09-29 11:30:39 +0100150
Alan Stokes16e027f2021-10-04 17:57:31 +0100151 Ok(compos_instance)
152 }
153
Victor Hsieh18775b12021-10-12 17:42:48 -0700154 fn initialize_service(service: &Strong<dyn ICompOsService>, key_blob: &[u8]) -> Result<()> {
155 // Key blob is assumed to be verified/trusted.
156 service.initializeSigningKey(key_blob).context("Loading signing key")?;
157
158 // TODO(198211396): Implement correctly.
159 service
Victor Hsieh64290a52021-11-17 13:34:46 -0800160 .initializeClasspaths(
161 &env::var("BOOTCLASSPATH")?,
162 &env::var("DEX2OATBOOTCLASSPATH")?,
163 &env::var("SYSTEMSERVERCLASSPATH")?,
164 )
Victor Hsieh18775b12021-10-12 17:42:48 -0700165 .context("Initializing *CLASSPATH")?;
166 Ok(())
167 }
168
Alan Stokesd21764c2021-10-25 15:33:40 +0100169 fn start_vm(
170 &self,
171 virtualization_service: &dyn IVirtualizationService,
172 ) -> Result<CompOsInstance> {
Alan Stokes16e027f2021-10-04 17:57:31 +0100173 let instance_image = fs::OpenOptions::new()
174 .read(true)
175 .write(true)
176 .open(&self.instance_image)
177 .context("Failed to open instance image")?;
Jooyung Hanccae66d2021-12-20 11:54:36 +0900178 let vm_instance = VmInstance::start(
179 virtualization_service,
180 instance_image,
181 &self.idsig,
182 &self.vm_parameters,
183 )
184 .context("Starting VM")?;
Alan Stokes16e027f2021-10-04 17:57:31 +0100185 let service = vm_instance.get_service().context("Connecting to CompOS")?;
Alan Stokes9ca14ca2021-10-20 14:25:57 +0100186 Ok(CompOsInstance { vm_instance, service, lazy_service_guard: Default::default() })
Alan Stokes6b2d0a82021-09-29 11:30:39 +0100187 }
188
189 fn create_instance_image(
190 &self,
191 virtualization_service: &dyn IVirtualizationService,
192 ) -> Result<()> {
193 let instance_image = fs::OpenOptions::new()
194 .create(true)
Alan Stokes23b90ee2021-10-28 11:36:14 +0100195 .truncate(true)
Alan Stokes6b2d0a82021-09-29 11:30:39 +0100196 .read(true)
197 .write(true)
198 .open(&self.instance_image)
199 .context("Creating instance image file")?;
200 let instance_image = ParcelFileDescriptor::new(instance_image);
201 // TODO: Where does this number come from?
202 let size = 10 * 1024 * 1024;
203 virtualization_service
204 .initializeWritablePartition(&instance_image, size, PartitionType::ANDROID_VM_INSTANCE)
205 .context("Writing instance image file")?;
206 Ok(())
207 }
208
209 fn check_files_exist(&self) -> Result<()> {
210 if !self.instance_root.is_dir() {
Alan Stokes6fc18372021-11-25 17:50:27 +0000211 bail!("Directory {:?} not found", self.instance_root)
Alan Stokes6b2d0a82021-09-29 11:30:39 +0100212 };
213 Self::check_file_exists(&self.instance_image)?;
214 Self::check_file_exists(&self.key_blob)?;
215 Self::check_file_exists(&self.public_key)?;
216 Ok(())
217 }
218
219 fn check_file_exists(file: &Path) -> Result<()> {
220 if !file.is_file() {
Alan Stokes6fc18372021-11-25 17:50:27 +0000221 bail!("File {:?} not found", file)
Alan Stokes6b2d0a82021-09-29 11:30:39 +0100222 };
223 Ok(())
224 }
225}