blob: c0024f1473d61dce7afa5552b2b2e9ffd4ec02b6 [file] [log] [blame]
David Brazdilafc9a9e2023-01-12 16:08:10 +00001// 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//! Implementation of the AIDL interface of the VirtualizationService.
16
David Brazdil33a31022023-01-12 16:55:16 +000017use crate::atom::{forward_vm_booted_atom, forward_vm_creation_atom, forward_vm_exited_atom};
David Drysdale79af2662024-02-19 14:50:31 +000018use crate::maintenance;
Alice Wange64dd182024-01-17 15:57:55 +000019use crate::remote_provisioning;
Alan Stokesac667072024-02-19 16:26:00 +000020use crate::rkpvm::{generate_ecdsa_p256_key_pair, request_attestation};
21use crate::{get_calling_pid, get_calling_uid, REMOTELY_PROVISIONED_COMPONENT_SERVICE_NAME};
David Brazdilafc9a9e2023-01-12 16:08:10 +000022use android_os_permissions_aidl::aidl::android::os::IPermissionController;
Alan Stokesac667072024-02-19 16:26:00 +000023use android_system_virtualizationcommon::aidl::android::system::virtualizationcommon;
David Drysdale79af2662024-02-19 14:50:31 +000024use android_system_virtualizationmaintenance::aidl::android::system::virtualizationmaintenance;
Alan Stokesac667072024-02-19 16:26:00 +000025use android_system_virtualizationservice::aidl::android::system::virtualizationservice;
26use android_system_virtualizationservice_internal as android_vs_internal;
27use android_system_virtualmachineservice::aidl::android::system::virtualmachineservice;
28use android_vs_internal::aidl::android::system::virtualizationservice_internal;
Alice Wangd1b11a02023-04-18 12:30:20 +000029use anyhow::{anyhow, ensure, Context, Result};
Jiyong Parkd7bd2f22023-08-10 20:41:19 +090030use avflog::LogResult;
Alan Stokesac667072024-02-19 16:26:00 +000031use binder::{
32 self, wait_for_interface, BinderFeatures, ExceptionCode, Interface, IntoBinderResult,
33 LazyServiceGuard, ParcelFileDescriptor, Status, Strong,
34};
Jakob Vukalovicd42aa2c2023-11-09 16:04:00 +000035use lazy_static::lazy_static;
David Brazdilafc9a9e2023-01-12 16:08:10 +000036use libc::VMADDR_CID_HOST;
37use log::{error, info, warn};
Alan Stokesac667072024-02-19 16:26:00 +000038use nix::unistd::{chown, Uid};
39use openssl::x509::X509;
Shikha Panwar61a74b52024-02-16 13:17:01 +000040use rand::Fill;
Alice Wangbff017f2023-11-09 14:43:28 +000041use rkpd_client::get_rkpd_attestation_key;
David Brazdilafc9a9e2023-01-12 16:08:10 +000042use rustutils::system_properties;
Inseob Kimc4a774d2023-08-30 12:48:43 +090043use serde::Deserialize;
Alan Stokesac667072024-02-19 16:26:00 +000044use service_vm_comm::Response;
Inseob Kimc4a774d2023-08-30 12:48:43 +090045use std::collections::{HashMap, HashSet};
David Brazdil2dfefd12023-11-17 14:07:36 +000046use std::fs::{self, create_dir, remove_dir_all, remove_file, set_permissions, File, Permissions};
David Brazdilafc9a9e2023-01-12 16:08:10 +000047use std::io::{Read, Write};
48use std::os::unix::fs::PermissionsExt;
49use std::os::unix::raw::{pid_t, uid_t};
Inseob Kim55438b22023-08-09 20:16:01 +090050use std::path::{Path, PathBuf};
David Brazdilafc9a9e2023-01-12 16:08:10 +000051use std::sync::{Arc, Mutex, Weak};
52use tombstoned_client::{DebuggerdDumpType, TombstonedConnection};
Alan Stokesac667072024-02-19 16:26:00 +000053use virtualizationcommon::Certificate::Certificate;
David Drysdale79af2662024-02-19 14:50:31 +000054use virtualizationmaintenance::IVirtualizationMaintenance::IVirtualizationMaintenance;
Alan Stokesac667072024-02-19 16:26:00 +000055use virtualizationservice::{
56 AssignableDevice::AssignableDevice, VirtualMachineDebugInfo::VirtualMachineDebugInfo,
57};
58use virtualizationservice_internal::{
59 AtomVmBooted::AtomVmBooted,
60 AtomVmCreationRequested::AtomVmCreationRequested,
61 AtomVmExited::AtomVmExited,
62 IBoundDevice::IBoundDevice,
63 IGlobalVmContext::{BnGlobalVmContext, IGlobalVmContext},
64 IVfioHandler::VfioDev::VfioDev,
65 IVfioHandler::{BpVfioHandler, IVfioHandler},
David Drysdale79af2662024-02-19 14:50:31 +000066 IVirtualizationServiceInternal::IVirtualizationServiceInternal,
Alan Stokesac667072024-02-19 16:26:00 +000067};
68use virtualmachineservice::IVirtualMachineService::VM_TOMBSTONES_SERVICE_PORT;
David Brazdilafc9a9e2023-01-12 16:08:10 +000069use vsock::{VsockListener, VsockStream};
David Brazdilafc9a9e2023-01-12 16:08:10 +000070
71/// The unique ID of a VM used (together with a port number) for vsock communication.
72pub type Cid = u32;
73
David Brazdilafc9a9e2023-01-12 16:08:10 +000074/// Directory in which to write disk image files used while running VMs.
75pub const TEMPORARY_DIRECTORY: &str = "/data/misc/virtualizationservice";
76
77/// The first CID to assign to a guest VM managed by the VirtualizationService. CIDs lower than this
78/// are reserved for the host or other usage.
79const GUEST_CID_MIN: Cid = 2048;
80const GUEST_CID_MAX: Cid = 65535;
81
82const SYSPROP_LAST_CID: &str = "virtualizationservice.state.last_cid";
83
84const CHUNK_RECV_MAX_LEN: usize = 1024;
85
Alice Wange64dd182024-01-17 15:57:55 +000086/// The fake certificate is used for testing only when a client VM requests attestation in test
87/// mode, it is a single certificate extracted on an unregistered device for testing.
88/// Here is the snapshot of the certificate:
89///
90/// ```
91/// Certificate:
92/// Data:
93/// Version: 3 (0x2)
94/// Serial Number:
95/// 59:ae:50:98:95:e1:34:25:f1:21:93:c0:4c:e5:24:66
96/// Signature Algorithm: ecdsa-with-SHA256
97/// Issuer: CN = Droid Unregistered Device CA, O = Google Test LLC
98/// Validity
99/// Not Before: Feb 5 14:39:39 2024 GMT
100/// Not After : Feb 14 14:39:39 2024 GMT
101/// Subject: CN = 59ae509895e13425f12193c04ce52466, O = TEE
102/// Subject Public Key Info:
103/// Public Key Algorithm: id-ecPublicKey
104/// Public-Key: (256 bit)
105/// pub:
106/// 04:30:32:cd:95:12:b0:71:8b:b7:14:44:26:58:d5:
107/// 82:8c:25:55:2c:6d:ef:98:e3:4f:88:d0:74:82:09:
108/// 3e:8d:6c:f0:f2:18:d5:83:0e:0d:f2:ce:c5:15:38:
109/// e5:6a:e6:4d:4d:95:15:b7:24:e7:cb:4b:63:42:21:
110/// bc:36:c6:0a:d8
111/// ASN1 OID: prime256v1
112/// NIST CURVE: P-256
113/// X509v3 extensions:
114/// ...
115/// ```
116const FAKE_CERTIFICATE_FOR_TESTING: &[u8] = &[
117 0x30, 0x82, 0x01, 0xee, 0x30, 0x82, 0x01, 0x94, 0xa0, 0x03, 0x02, 0x01, 0x02, 0x02, 0x10, 0x59,
118 0xae, 0x50, 0x98, 0x95, 0xe1, 0x34, 0x25, 0xf1, 0x21, 0x93, 0xc0, 0x4c, 0xe5, 0x24, 0x66, 0x30,
119 0x0a, 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x04, 0x03, 0x02, 0x30, 0x41, 0x31, 0x25, 0x30,
120 0x23, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x1c, 0x44, 0x72, 0x6f, 0x69, 0x64, 0x20, 0x55, 0x6e,
121 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x65, 0x64, 0x20, 0x44, 0x65, 0x76, 0x69, 0x63,
122 0x65, 0x20, 0x43, 0x41, 0x31, 0x18, 0x30, 0x16, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0f, 0x47,
123 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x20, 0x54, 0x65, 0x73, 0x74, 0x20, 0x4c, 0x4c, 0x43, 0x30, 0x1e,
124 0x17, 0x0d, 0x32, 0x34, 0x30, 0x32, 0x30, 0x35, 0x31, 0x34, 0x33, 0x39, 0x33, 0x39, 0x5a, 0x17,
125 0x0d, 0x32, 0x34, 0x30, 0x32, 0x31, 0x34, 0x31, 0x34, 0x33, 0x39, 0x33, 0x39, 0x5a, 0x30, 0x39,
126 0x31, 0x29, 0x30, 0x27, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x20, 0x35, 0x39, 0x61, 0x65, 0x35,
127 0x30, 0x39, 0x38, 0x39, 0x35, 0x65, 0x31, 0x33, 0x34, 0x32, 0x35, 0x66, 0x31, 0x32, 0x31, 0x39,
128 0x33, 0x63, 0x30, 0x34, 0x63, 0x65, 0x35, 0x32, 0x34, 0x36, 0x36, 0x31, 0x0c, 0x30, 0x0a, 0x06,
129 0x03, 0x55, 0x04, 0x0a, 0x13, 0x03, 0x54, 0x45, 0x45, 0x30, 0x59, 0x30, 0x13, 0x06, 0x07, 0x2a,
130 0x86, 0x48, 0xce, 0x3d, 0x02, 0x01, 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x03, 0x01, 0x07,
131 0x03, 0x42, 0x00, 0x04, 0x30, 0x32, 0xcd, 0x95, 0x12, 0xb0, 0x71, 0x8b, 0xb7, 0x14, 0x44, 0x26,
132 0x58, 0xd5, 0x82, 0x8c, 0x25, 0x55, 0x2c, 0x6d, 0xef, 0x98, 0xe3, 0x4f, 0x88, 0xd0, 0x74, 0x82,
133 0x09, 0x3e, 0x8d, 0x6c, 0xf0, 0xf2, 0x18, 0xd5, 0x83, 0x0e, 0x0d, 0xf2, 0xce, 0xc5, 0x15, 0x38,
134 0xe5, 0x6a, 0xe6, 0x4d, 0x4d, 0x95, 0x15, 0xb7, 0x24, 0xe7, 0xcb, 0x4b, 0x63, 0x42, 0x21, 0xbc,
135 0x36, 0xc6, 0x0a, 0xd8, 0xa3, 0x76, 0x30, 0x74, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04,
136 0x16, 0x04, 0x14, 0x39, 0x81, 0x41, 0x0a, 0xb9, 0xf3, 0xf4, 0x5b, 0x75, 0x97, 0x4a, 0x46, 0xd6,
137 0x30, 0x9e, 0x1d, 0x7a, 0x3b, 0xec, 0xa8, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18,
138 0x30, 0x16, 0x80, 0x14, 0x82, 0xbd, 0x00, 0xde, 0xcb, 0xc5, 0xe7, 0x72, 0x87, 0x3d, 0x1c, 0x0a,
139 0x1e, 0x78, 0x4f, 0xf5, 0xd3, 0xc1, 0x3e, 0xb8, 0x30, 0x0f, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01,
140 0x01, 0xff, 0x04, 0x05, 0x30, 0x03, 0x01, 0x01, 0xff, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x1d, 0x0f,
141 0x01, 0x01, 0xff, 0x04, 0x04, 0x03, 0x02, 0x02, 0x04, 0x30, 0x11, 0x06, 0x0a, 0x2b, 0x06, 0x01,
142 0x04, 0x01, 0xd6, 0x79, 0x02, 0x01, 0x1e, 0x04, 0x03, 0xa1, 0x01, 0x08, 0x30, 0x0a, 0x06, 0x08,
143 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x04, 0x03, 0x02, 0x03, 0x48, 0x00, 0x30, 0x45, 0x02, 0x21, 0x00,
144 0xae, 0xd8, 0x40, 0x9e, 0x37, 0x3e, 0x5c, 0x9c, 0xe2, 0x93, 0x3d, 0x8c, 0xf7, 0x05, 0x10, 0xe7,
145 0xd1, 0x2b, 0x87, 0x8a, 0xee, 0xd6, 0x1e, 0x6c, 0x3b, 0xd2, 0x91, 0x3e, 0xa5, 0xdf, 0x91, 0x20,
146 0x02, 0x20, 0x7f, 0x0f, 0x29, 0x54, 0x60, 0x80, 0x07, 0x50, 0x5f, 0x56, 0x6b, 0x9f, 0xe0, 0x94,
147 0xb4, 0x3f, 0x3b, 0x0f, 0x61, 0xa0, 0x33, 0x40, 0xe6, 0x1a, 0x42, 0xda, 0x4b, 0xa4, 0xfd, 0x92,
148 0xb9, 0x0f,
149];
150
Jakob Vukalovicd42aa2c2023-11-09 16:04:00 +0000151lazy_static! {
Alice Wange64dd182024-01-17 15:57:55 +0000152 static ref FAKE_PROVISIONED_KEY_BLOB_FOR_TESTING: Mutex<Option<Vec<u8>>> = Mutex::new(None);
Jakob Vukalovicd42aa2c2023-11-09 16:04:00 +0000153 static ref VFIO_SERVICE: Strong<dyn IVfioHandler> =
154 wait_for_interface(<BpVfioHandler as IVfioHandler>::get_descriptor())
155 .expect("Could not connect to VfioHandler");
156}
157
David Brazdilafc9a9e2023-01-12 16:08:10 +0000158fn is_valid_guest_cid(cid: Cid) -> bool {
159 (GUEST_CID_MIN..=GUEST_CID_MAX).contains(&cid)
160}
161
162/// Singleton service for allocating globally-unique VM resources, such as the CID, and running
163/// singleton servers, like tombstone receiver.
David Drysdale79af2662024-02-19 14:50:31 +0000164#[derive(Clone)]
David Brazdilafc9a9e2023-01-12 16:08:10 +0000165pub struct VirtualizationServiceInternal {
166 state: Arc<Mutex<GlobalState>>,
167}
168
169impl VirtualizationServiceInternal {
David Drysdale79af2662024-02-19 14:50:31 +0000170 pub fn init() -> VirtualizationServiceInternal {
171 let service =
172 VirtualizationServiceInternal { state: Arc::new(Mutex::new(GlobalState::new())) };
David Brazdilafc9a9e2023-01-12 16:08:10 +0000173
174 std::thread::spawn(|| {
175 if let Err(e) = handle_stream_connection_tombstoned() {
176 warn!("Error receiving tombstone from guest or writing them. Error: {:?}", e);
177 }
178 });
179
David Drysdale79af2662024-02-19 14:50:31 +0000180 service
David Brazdilafc9a9e2023-01-12 16:08:10 +0000181 }
182}
183
184impl Interface for VirtualizationServiceInternal {}
185
186impl IVirtualizationServiceInternal for VirtualizationServiceInternal {
187 fn removeMemlockRlimit(&self) -> binder::Result<()> {
188 let pid = get_calling_pid();
189 let lim = libc::rlimit { rlim_cur: libc::RLIM_INFINITY, rlim_max: libc::RLIM_INFINITY };
190
Andrew Walbranb58d1b42023-07-07 13:54:49 +0100191 // SAFETY: borrowing the new limit struct only
David Brazdilafc9a9e2023-01-12 16:08:10 +0000192 let ret = unsafe { libc::prlimit(pid, libc::RLIMIT_MEMLOCK, &lim, std::ptr::null_mut()) };
193
194 match ret {
195 0 => Ok(()),
Jiyong Park2227eaa2023-08-04 11:59:18 +0900196 -1 => Err(std::io::Error::last_os_error().into()),
197 n => Err(anyhow!("Unexpected return value from prlimit(): {n}")),
David Brazdilafc9a9e2023-01-12 16:08:10 +0000198 }
Jiyong Park2227eaa2023-08-04 11:59:18 +0900199 .or_binder_exception(ExceptionCode::ILLEGAL_STATE)
David Brazdilafc9a9e2023-01-12 16:08:10 +0000200 }
201
202 fn allocateGlobalVmContext(
203 &self,
204 requester_debug_pid: i32,
205 ) -> binder::Result<Strong<dyn IGlobalVmContext>> {
206 check_manage_access()?;
207
208 let requester_uid = get_calling_uid();
209 let requester_debug_pid = requester_debug_pid as pid_t;
210 let state = &mut *self.state.lock().unwrap();
Jiyong Park2227eaa2023-08-04 11:59:18 +0900211 state
212 .allocate_vm_context(requester_uid, requester_debug_pid)
213 .or_binder_exception(ExceptionCode::ILLEGAL_STATE)
David Brazdilafc9a9e2023-01-12 16:08:10 +0000214 }
215
216 fn atomVmBooted(&self, atom: &AtomVmBooted) -> Result<(), Status> {
217 forward_vm_booted_atom(atom);
218 Ok(())
219 }
220
221 fn atomVmCreationRequested(&self, atom: &AtomVmCreationRequested) -> Result<(), Status> {
222 forward_vm_creation_atom(atom);
223 Ok(())
224 }
225
226 fn atomVmExited(&self, atom: &AtomVmExited) -> Result<(), Status> {
227 forward_vm_exited_atom(atom);
228 Ok(())
229 }
230
231 fn debugListVms(&self) -> binder::Result<Vec<VirtualMachineDebugInfo>> {
232 check_debug_access()?;
233
234 let state = &mut *self.state.lock().unwrap();
235 let cids = state
236 .held_contexts
237 .iter()
238 .filter_map(|(_, inst)| Weak::upgrade(inst))
239 .map(|vm| VirtualMachineDebugInfo {
240 cid: vm.cid as i32,
241 temporaryDirectory: vm.get_temp_dir().to_string_lossy().to_string(),
242 requesterUid: vm.requester_uid as i32,
Charisee96113f32023-01-26 09:00:42 +0000243 requesterPid: vm.requester_debug_pid,
David Brazdilafc9a9e2023-01-12 16:08:10 +0000244 })
245 .collect();
246 Ok(cids)
247 }
Alice Wangc2fec932023-02-23 16:24:02 +0000248
Alice Wange64dd182024-01-17 15:57:55 +0000249 fn enableTestAttestation(&self) -> binder::Result<()> {
250 check_manage_access()?;
251 check_use_custom_virtual_machine()?;
252 if !cfg!(remote_attestation) {
253 return Err(Status::new_exception_str(
254 ExceptionCode::UNSUPPORTED_OPERATION,
255 Some(
256 "enableTestAttestation is not supported with the remote_attestation \
257 feature disabled",
258 ),
259 ))
260 .with_log();
261 }
262 let res = generate_ecdsa_p256_key_pair()
263 .context("Failed to generate ECDSA P-256 key pair for testing")
264 .with_log()
265 .or_service_specific_exception(-1)?;
266 match res {
267 Response::GenerateEcdsaP256KeyPair(key_pair) => {
268 FAKE_PROVISIONED_KEY_BLOB_FOR_TESTING
269 .lock()
270 .unwrap()
271 .replace(key_pair.key_blob.to_vec());
272 Ok(())
273 }
274 _ => Err(remote_provisioning::to_service_specific_error(res)),
275 }
276 .with_log()
277 }
278
Alice Wangbff017f2023-11-09 14:43:28 +0000279 fn requestAttestation(
280 &self,
281 csr: &[u8],
282 requester_uid: i32,
Alice Wange64dd182024-01-17 15:57:55 +0000283 test_mode: bool,
Alice Wangbff017f2023-11-09 14:43:28 +0000284 ) -> binder::Result<Vec<Certificate>> {
Alice Wangc2fec932023-02-23 16:24:02 +0000285 check_manage_access()?;
Alice Wang4c6c5582023-11-23 15:07:18 +0000286 if !cfg!(remote_attestation) {
287 return Err(Status::new_exception_str(
Alice Wange9ac2db2023-09-08 15:13:13 +0000288 ExceptionCode::UNSUPPORTED_OPERATION,
289 Some(
Alice Wanga410b642023-10-18 09:05:15 +0000290 "requestAttestation is not supported with the remote_attestation feature \
291 disabled",
Alice Wange9ac2db2023-09-08 15:13:13 +0000292 ),
293 ))
Alice Wang4c6c5582023-11-23 15:07:18 +0000294 .with_log();
Alice Wange9ac2db2023-09-08 15:13:13 +0000295 }
Alice Wang4c6c5582023-11-23 15:07:18 +0000296 info!("Received csr. Requestting attestation...");
Alice Wange64dd182024-01-17 15:57:55 +0000297 let (key_blob, certificate_chain) = if test_mode {
298 check_use_custom_virtual_machine()?;
299 info!("Using the fake key blob for testing...");
300 (
301 FAKE_PROVISIONED_KEY_BLOB_FOR_TESTING
302 .lock()
303 .unwrap()
304 .clone()
305 .ok_or_else(|| anyhow!("No key blob for testing"))
306 .with_log()
307 .or_service_specific_exception(-1)?,
308 FAKE_CERTIFICATE_FOR_TESTING.to_vec(),
309 )
310 } else {
311 info!("Retrieving the remotely provisioned keys from RKPD...");
312 let attestation_key = get_rkpd_attestation_key(
313 REMOTELY_PROVISIONED_COMPONENT_SERVICE_NAME,
314 requester_uid as u32,
315 )
316 .context("Failed to retrieve the remotely provisioned keys")
317 .with_log()
318 .or_service_specific_exception(-1)?;
319 (attestation_key.keyBlob, attestation_key.encodedCertChain)
320 };
321 let mut certificate_chain = split_x509_certificate_chain(&certificate_chain)
Alice Wang4c6c5582023-11-23 15:07:18 +0000322 .context("Failed to split the remotely provisioned certificate chain")
323 .with_log()
324 .or_service_specific_exception(-1)?;
325 if certificate_chain.is_empty() {
326 return Err(Status::new_service_specific_error_str(
327 -1,
328 Some("The certificate chain should contain at least 1 certificate"),
329 ))
330 .with_log();
331 }
Alice Wang20b8ebc2023-11-17 09:54:47 +0000332 let certificate = request_attestation(
333 csr.to_vec(),
Alice Wange64dd182024-01-17 15:57:55 +0000334 key_blob,
Alice Wang20b8ebc2023-11-17 09:54:47 +0000335 certificate_chain[0].encodedCertificate.clone(),
336 )
337 .context("Failed to request attestation")
338 .with_log()
339 .or_service_specific_exception(-1)?;
Alice Wang4c6c5582023-11-23 15:07:18 +0000340 certificate_chain.insert(0, Certificate { encodedCertificate: certificate });
341
342 Ok(certificate_chain)
Alice Wangc2fec932023-02-23 16:24:02 +0000343 }
Inseob Kim53d0b212023-07-20 16:58:37 +0900344
345 fn getAssignableDevices(&self) -> binder::Result<Vec<AssignableDevice>> {
346 check_use_custom_virtual_machine()?;
347
Inseob Kim7307a892023-09-14 13:37:58 +0900348 Ok(get_assignable_devices()?
349 .device
350 .into_iter()
351 .map(|x| AssignableDevice { node: x.sysfs_path, kind: x.kind })
352 .collect::<Vec<_>>())
Inseob Kim53d0b212023-07-20 16:58:37 +0900353 }
Inseob Kim1ca0f652023-07-20 17:18:12 +0900354
Jakob Vukalovicd42aa2c2023-11-09 16:04:00 +0000355 fn bindDevicesToVfioDriver(
356 &self,
357 devices: &[String],
358 ) -> binder::Result<Vec<Strong<dyn IBoundDevice>>> {
Inseob Kim1ca0f652023-07-20 17:18:12 +0900359 check_use_custom_virtual_machine()?;
360
Jakob Vukalovicd42aa2c2023-11-09 16:04:00 +0000361 let devices = get_assignable_devices()?
Inseob Kim7307a892023-09-14 13:37:58 +0900362 .device
363 .into_iter()
364 .filter_map(|x| {
365 if devices.contains(&x.sysfs_path) {
Jakob Vukalovicd42aa2c2023-11-09 16:04:00 +0000366 Some(VfioDev { sysfsPath: x.sysfs_path, dtboLabel: x.dtbo_label })
Inseob Kim7307a892023-09-14 13:37:58 +0900367 } else {
Jakob Vukalovicd42aa2c2023-11-09 16:04:00 +0000368 warn!("device {} is not assignable", x.sysfs_path);
Inseob Kim7307a892023-09-14 13:37:58 +0900369 None
370 }
371 })
Jakob Vukalovicd42aa2c2023-11-09 16:04:00 +0000372 .collect::<Vec<VfioDev>>();
373
374 VFIO_SERVICE.bindDevicesToVfioDriver(devices.as_slice())
Inseob Kim1ca0f652023-07-20 17:18:12 +0900375 }
David Brazdil2dfefd12023-11-17 14:07:36 +0000376
377 fn getDtboFile(&self) -> binder::Result<ParcelFileDescriptor> {
378 check_use_custom_virtual_machine()?;
379
380 let state = &mut *self.state.lock().unwrap();
381 let file = state.get_dtbo_file().or_service_specific_exception(-1)?;
382 Ok(ParcelFileDescriptor::new(file))
383 }
Shikha Panwar61a74b52024-02-16 13:17:01 +0000384
385 // TODO(b/294177871) Persist this Id, along with client uuid.
386 fn allocateInstanceId(&self) -> binder::Result<[u8; 64]> {
387 let mut id = [0u8; 64];
388 id.try_fill(&mut rand::thread_rng())
389 .context("Failed to allocate instance_id")
390 .or_service_specific_exception(-1)?;
391 let uid = get_calling_uid();
392 info!("Allocated a VM's instance_id: {:?}, for uid: {:?}", hex::encode(id), uid);
393 Ok(id)
394 }
David Drysdale79af2662024-02-19 14:50:31 +0000395
396 fn removeVmInstance(&self, instance_id: &[u8; 64]) -> binder::Result<()> {
397 let state = &mut *self.state.lock().unwrap();
398 if let Some(sk_state) = &mut state.sk_state {
399 info!("removeVmInstance(): delete secret");
400 sk_state.delete_ids(&[*instance_id]);
401 } else {
402 info!("ignoring removeVmInstance() as no ISecretkeeper");
403 }
404 Ok(())
405 }
406}
407
408impl IVirtualizationMaintenance for VirtualizationServiceInternal {
409 fn appRemoved(&self, user_id: i32, app_id: i32) -> binder::Result<()> {
410 let state = &mut *self.state.lock().unwrap();
411 if let Some(sk_state) = &mut state.sk_state {
412 info!("packageRemoved(user_id={user_id}, app_id={app_id})");
413 sk_state.delete_ids_for_app(user_id, app_id).or_service_specific_exception(-1)?;
414 } else {
415 info!("ignoring packageRemoved(user_id={user_id}, app_id={app_id})");
416 }
417 Ok(())
418 }
419
420 fn userRemoved(&self, user_id: i32) -> binder::Result<()> {
421 let state = &mut *self.state.lock().unwrap();
422 if let Some(sk_state) = &mut state.sk_state {
423 info!("userRemoved({user_id})");
424 sk_state.delete_ids_for_user(user_id).or_service_specific_exception(-1)?;
425 } else {
426 info!("ignoring userRemoved(user_id={user_id})");
427 }
428 Ok(())
429 }
Inseob Kim1ca0f652023-07-20 17:18:12 +0900430}
431
Inseob Kimc4a774d2023-08-30 12:48:43 +0900432// KEEP IN SYNC WITH assignable_devices.xsd
433#[derive(Debug, Deserialize)]
434struct Device {
435 kind: String,
Jaewan Kim35e818d2023-10-18 05:36:38 +0000436 dtbo_label: String,
Inseob Kimc4a774d2023-08-30 12:48:43 +0900437 sysfs_path: String,
438}
439
Inseob Kim7307a892023-09-14 13:37:58 +0900440#[derive(Debug, Default, Deserialize)]
Inseob Kimc4a774d2023-08-30 12:48:43 +0900441struct Devices {
442 device: Vec<Device>,
443}
444
Inseob Kim7307a892023-09-14 13:37:58 +0900445fn get_assignable_devices() -> binder::Result<Devices> {
446 let xml_path = Path::new("/vendor/etc/avf/assignable_devices.xml");
447 if !xml_path.exists() {
448 return Ok(Devices { ..Default::default() });
449 }
450
451 let xml = fs::read(xml_path)
452 .context("Failed to read assignable_devices.xml")
453 .with_log()
454 .or_service_specific_exception(-1)?;
455
456 let xml = String::from_utf8(xml)
457 .context("assignable_devices.xml is not a valid UTF-8 file")
458 .with_log()
459 .or_service_specific_exception(-1)?;
460
461 let mut devices: Devices = serde_xml_rs::from_str(&xml)
462 .context("can't parse assignable_devices.xml")
463 .with_log()
464 .or_service_specific_exception(-1)?;
465
466 let mut device_set = HashSet::new();
467 devices.device.retain(move |device| {
468 if device_set.contains(&device.sysfs_path) {
469 warn!("duplicated assignable device {device:?}; ignoring...");
470 return false;
471 }
472
473 if !Path::new(&device.sysfs_path).exists() {
474 warn!("assignable device {device:?} doesn't exist; ignoring...");
475 return false;
476 }
477
478 device_set.insert(device.sysfs_path.clone());
479 true
480 });
481 Ok(devices)
482}
483
Alice Wang4c6c5582023-11-23 15:07:18 +0000484fn split_x509_certificate_chain(mut cert_chain: &[u8]) -> Result<Vec<Certificate>> {
485 let mut out = Vec::new();
486 while !cert_chain.is_empty() {
Alice Wangfc5a44a2023-12-21 12:22:40 +0000487 let cert = X509::from_der(cert_chain)?;
488 let end = cert.to_der()?.len();
Alice Wang4c6c5582023-11-23 15:07:18 +0000489 out.push(Certificate { encodedCertificate: cert_chain[..end].to_vec() });
Alice Wangfc5a44a2023-12-21 12:22:40 +0000490 cert_chain = &cert_chain[end..];
Alice Wang4c6c5582023-11-23 15:07:18 +0000491 }
492 Ok(out)
493}
494
David Brazdilafc9a9e2023-01-12 16:08:10 +0000495#[derive(Debug, Default)]
496struct GlobalVmInstance {
497 /// The unique CID assigned to the VM for vsock communication.
498 cid: Cid,
499 /// UID of the client who requested this VM instance.
500 requester_uid: uid_t,
501 /// PID of the client who requested this VM instance.
502 requester_debug_pid: pid_t,
503}
504
505impl GlobalVmInstance {
506 fn get_temp_dir(&self) -> PathBuf {
507 let cid = self.cid;
508 format!("{TEMPORARY_DIRECTORY}/{cid}").into()
509 }
510}
511
512/// The mutable state of the VirtualizationServiceInternal. There should only be one instance
513/// of this struct.
David Brazdilafc9a9e2023-01-12 16:08:10 +0000514struct GlobalState {
515 /// VM contexts currently allocated to running VMs. A CID is never recycled as long
516 /// as there is a strong reference held by a GlobalVmContext.
517 held_contexts: HashMap<Cid, Weak<GlobalVmInstance>>,
David Brazdil2dfefd12023-11-17 14:07:36 +0000518
519 /// Cached read-only FD of VM DTBO file. Also serves as a lock for creating the file.
520 dtbo_file: Mutex<Option<File>>,
David Drysdale79af2662024-02-19 14:50:31 +0000521
522 /// State relating to secrets held by (optional) Secretkeeper instance on behalf of VMs.
523 sk_state: Option<maintenance::State>,
David Brazdilafc9a9e2023-01-12 16:08:10 +0000524}
525
526impl GlobalState {
David Drysdale79af2662024-02-19 14:50:31 +0000527 fn new() -> Self {
528 Self {
529 held_contexts: HashMap::new(),
530 dtbo_file: Mutex::new(None),
531 sk_state: maintenance::State::new(),
532 }
533 }
534
David Brazdilafc9a9e2023-01-12 16:08:10 +0000535 /// Get the next available CID, or an error if we have run out. The last CID used is stored in
536 /// a system property so that restart of virtualizationservice doesn't reuse CID while the host
537 /// Android is up.
538 fn get_next_available_cid(&mut self) -> Result<Cid> {
539 // Start trying to find a CID from the last used CID + 1. This ensures
540 // that we do not eagerly recycle CIDs. It makes debugging easier but
541 // also means that retrying to allocate a CID, eg. because it is
542 // erroneously occupied by a process, will not recycle the same CID.
543 let last_cid_prop =
544 system_properties::read(SYSPROP_LAST_CID)?.and_then(|val| match val.parse::<Cid>() {
545 Ok(num) => {
546 if is_valid_guest_cid(num) {
547 Some(num)
548 } else {
549 error!("Invalid value '{}' of property '{}'", num, SYSPROP_LAST_CID);
550 None
551 }
552 }
553 Err(_) => {
554 error!("Invalid value '{}' of property '{}'", val, SYSPROP_LAST_CID);
555 None
556 }
557 });
558
559 let first_cid = if let Some(last_cid) = last_cid_prop {
560 if last_cid == GUEST_CID_MAX {
561 GUEST_CID_MIN
562 } else {
563 last_cid + 1
564 }
565 } else {
566 GUEST_CID_MIN
567 };
568
569 let cid = self
570 .find_available_cid(first_cid..=GUEST_CID_MAX)
571 .or_else(|| self.find_available_cid(GUEST_CID_MIN..first_cid))
572 .ok_or_else(|| anyhow!("Could not find an available CID."))?;
573
574 system_properties::write(SYSPROP_LAST_CID, &format!("{}", cid))?;
575 Ok(cid)
576 }
577
578 fn find_available_cid<I>(&self, mut range: I) -> Option<Cid>
579 where
580 I: Iterator<Item = Cid>,
581 {
582 range.find(|cid| !self.held_contexts.contains_key(cid))
583 }
584
585 fn allocate_vm_context(
586 &mut self,
587 requester_uid: uid_t,
588 requester_debug_pid: pid_t,
589 ) -> Result<Strong<dyn IGlobalVmContext>> {
590 // Garbage collect unused VM contexts.
591 self.held_contexts.retain(|_, instance| instance.strong_count() > 0);
592
593 let cid = self.get_next_available_cid()?;
594 let instance = Arc::new(GlobalVmInstance { cid, requester_uid, requester_debug_pid });
David Brazdil2dfefd12023-11-17 14:07:36 +0000595 create_temporary_directory(&instance.get_temp_dir(), Some(requester_uid))?;
David Brazdilafc9a9e2023-01-12 16:08:10 +0000596
597 self.held_contexts.insert(cid, Arc::downgrade(&instance));
598 let binder = GlobalVmContext { instance, ..Default::default() };
599 Ok(BnGlobalVmContext::new_binder(binder, BinderFeatures::default()))
600 }
David Brazdil2dfefd12023-11-17 14:07:36 +0000601
602 fn get_dtbo_file(&mut self) -> Result<File> {
603 let mut file = self.dtbo_file.lock().unwrap();
604
605 let fd = if let Some(ref_fd) = &*file {
606 ref_fd.try_clone()?
607 } else {
608 let path = get_or_create_common_dir()?.join("vm.dtbo");
609 if path.exists() {
610 // All temporary files are deleted when the service is started.
611 // If the file exists but the FD is not cached, the file is
612 // likely corrupted.
613 remove_file(&path).context("Failed to clone cached VM DTBO file descriptor")?;
614 }
615
616 // Open a write-only file descriptor for vfio_handler.
617 let write_fd = File::create(&path).context("Failed to create VM DTBO file")?;
Jakob Vukalovicd42aa2c2023-11-09 16:04:00 +0000618 VFIO_SERVICE.writeVmDtbo(&ParcelFileDescriptor::new(write_fd))?;
David Brazdil2dfefd12023-11-17 14:07:36 +0000619
620 // Open read-only. This FD will be cached and returned to clients.
621 let read_fd = File::open(&path).context("Failed to open VM DTBO file")?;
622 let read_fd_clone =
623 read_fd.try_clone().context("Failed to clone VM DTBO file descriptor")?;
624 *file = Some(read_fd);
625 read_fd_clone
626 };
627
628 Ok(fd)
629 }
David Brazdilafc9a9e2023-01-12 16:08:10 +0000630}
631
David Brazdil2dfefd12023-11-17 14:07:36 +0000632fn create_temporary_directory(path: &PathBuf, requester_uid: Option<uid_t>) -> Result<()> {
633 // Directory may exist if previous attempt to create it had failed.
634 // Delete it before trying again.
David Brazdilafc9a9e2023-01-12 16:08:10 +0000635 if path.as_path().exists() {
636 remove_temporary_dir(path).unwrap_or_else(|e| {
637 warn!("Could not delete temporary directory {:?}: {}", path, e);
638 });
639 }
David Brazdil2dfefd12023-11-17 14:07:36 +0000640 // Create directory.
641 create_dir(path).with_context(|| format!("Could not create temporary directory {:?}", path))?;
642 // If provided, change ownership to client's UID but system's GID, and permissions 0700.
David Brazdilafc9a9e2023-01-12 16:08:10 +0000643 // If the chown() fails, this will leave behind an empty directory that will get removed
644 // at the next attempt, or if virtualizationservice is restarted.
David Brazdil2dfefd12023-11-17 14:07:36 +0000645 if let Some(uid) = requester_uid {
646 chown(path, Some(Uid::from_raw(uid)), None).with_context(|| {
647 format!("Could not set ownership of temporary directory {:?}", path)
648 })?;
649 }
David Brazdilafc9a9e2023-01-12 16:08:10 +0000650 Ok(())
651}
652
653/// Removes a directory owned by a different user by first changing its owner back
654/// to VirtualizationService.
655pub fn remove_temporary_dir(path: &PathBuf) -> Result<()> {
Alice Wangd1b11a02023-04-18 12:30:20 +0000656 ensure!(path.as_path().is_dir(), "Path {:?} is not a directory", path);
David Brazdilafc9a9e2023-01-12 16:08:10 +0000657 chown(path, Some(Uid::current()), None)?;
658 set_permissions(path, Permissions::from_mode(0o700))?;
Alice Wangd1b11a02023-04-18 12:30:20 +0000659 remove_dir_all(path)?;
David Brazdilafc9a9e2023-01-12 16:08:10 +0000660 Ok(())
661}
662
David Brazdil2dfefd12023-11-17 14:07:36 +0000663fn get_or_create_common_dir() -> Result<PathBuf> {
664 let path = Path::new(TEMPORARY_DIRECTORY).join("common");
665 if !path.exists() {
666 create_temporary_directory(&path, None)?;
667 }
668 Ok(path)
669}
670
David Brazdilafc9a9e2023-01-12 16:08:10 +0000671/// Implementation of the AIDL `IGlobalVmContext` interface.
672#[derive(Debug, Default)]
673struct GlobalVmContext {
674 /// Strong reference to the context's instance data structure.
675 instance: Arc<GlobalVmInstance>,
676 /// Keeps our service process running as long as this VM context exists.
677 #[allow(dead_code)]
678 lazy_service_guard: LazyServiceGuard,
679}
680
681impl Interface for GlobalVmContext {}
682
683impl IGlobalVmContext for GlobalVmContext {
684 fn getCid(&self) -> binder::Result<i32> {
685 Ok(self.instance.cid as i32)
686 }
687
688 fn getTemporaryDirectory(&self) -> binder::Result<String> {
689 Ok(self.instance.get_temp_dir().to_string_lossy().to_string())
690 }
691}
692
693fn handle_stream_connection_tombstoned() -> Result<()> {
694 // Should not listen for tombstones on a guest VM's port.
695 assert!(!is_valid_guest_cid(VM_TOMBSTONES_SERVICE_PORT as Cid));
696 let listener =
697 VsockListener::bind_with_cid_port(VMADDR_CID_HOST, VM_TOMBSTONES_SERVICE_PORT as Cid)?;
698 for incoming_stream in listener.incoming() {
699 let mut incoming_stream = match incoming_stream {
700 Err(e) => {
701 warn!("invalid incoming connection: {:?}", e);
702 continue;
703 }
704 Ok(s) => s,
705 };
706 std::thread::spawn(move || {
707 if let Err(e) = handle_tombstone(&mut incoming_stream) {
708 error!("Failed to write tombstone- {:?}", e);
709 }
710 });
711 }
712 Ok(())
713}
714
715fn handle_tombstone(stream: &mut VsockStream) -> Result<()> {
716 if let Ok(addr) = stream.peer_addr() {
717 info!("Vsock Stream connected to cid={} for tombstones", addr.cid());
718 }
719 let tb_connection =
720 TombstonedConnection::connect(std::process::id() as i32, DebuggerdDumpType::Tombstone)
721 .context("Failed to connect to tombstoned")?;
722 let mut text_output = tb_connection
723 .text_output
724 .as_ref()
725 .ok_or_else(|| anyhow!("Could not get file to write the tombstones on"))?;
726 let mut num_bytes_read = 0;
727 loop {
728 let mut chunk_recv = [0; CHUNK_RECV_MAX_LEN];
729 let n = stream
730 .read(&mut chunk_recv)
731 .context("Failed to read tombstone data from Vsock stream")?;
732 if n == 0 {
733 break;
734 }
735 num_bytes_read += n;
736 text_output.write_all(&chunk_recv[0..n]).context("Failed to write guests tombstones")?;
737 }
738 info!("Received {} bytes from guest & wrote to tombstone file", num_bytes_read);
739 tb_connection.notify_completion()?;
740 Ok(())
741}
742
743/// Checks whether the caller has a specific permission
744fn check_permission(perm: &str) -> binder::Result<()> {
745 let calling_pid = get_calling_pid();
746 let calling_uid = get_calling_uid();
747 // Root can do anything
748 if calling_uid == 0 {
749 return Ok(());
750 }
751 let perm_svc: Strong<dyn IPermissionController::IPermissionController> =
752 binder::get_interface("permission")?;
753 if perm_svc.checkPermission(perm, calling_pid, calling_uid as i32)? {
754 Ok(())
755 } else {
Jiyong Park2227eaa2023-08-04 11:59:18 +0900756 Err(anyhow!("does not have the {} permission", perm))
757 .or_binder_exception(ExceptionCode::SECURITY)
David Brazdilafc9a9e2023-01-12 16:08:10 +0000758 }
759}
760
761/// Check whether the caller of the current Binder method is allowed to call debug methods.
762fn check_debug_access() -> binder::Result<()> {
763 check_permission("android.permission.DEBUG_VIRTUAL_MACHINE")
764}
765
766/// Check whether the caller of the current Binder method is allowed to manage VMs
767fn check_manage_access() -> binder::Result<()> {
768 check_permission("android.permission.MANAGE_VIRTUAL_MACHINE")
769}
Inseob Kim53d0b212023-07-20 16:58:37 +0900770
771/// Check whether the caller of the current Binder method is allowed to use custom VMs
772fn check_use_custom_virtual_machine() -> binder::Result<()> {
773 check_permission("android.permission.USE_CUSTOM_VIRTUAL_MACHINE")
774}
Alice Wang4c6c5582023-11-23 15:07:18 +0000775
776#[cfg(test)]
777mod tests {
778 use super::*;
Alice Wang4c6c5582023-11-23 15:07:18 +0000779
780 const TEST_RKP_CERT_CHAIN_PATH: &str = "testdata/rkp_cert_chain.der";
781
782 #[test]
783 fn splitting_x509_certificate_chain_succeeds() -> Result<()> {
784 let bytes = fs::read(TEST_RKP_CERT_CHAIN_PATH)?;
785 let cert_chain = split_x509_certificate_chain(&bytes)?;
786
787 assert_eq!(4, cert_chain.len());
788 for cert in cert_chain {
Alice Wangfc5a44a2023-12-21 12:22:40 +0000789 let x509_cert = X509::from_der(&cert.encodedCertificate)?;
790 assert_eq!(x509_cert.to_der()?.len(), cert.encodedCertificate.len());
Alice Wang4c6c5582023-11-23 15:07:18 +0000791 }
792 Ok(())
793 }
794}