blob: c7ae5f0e2fc44c9abf8b4370555864c64f62a514 [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};
Alice Wange64dd182024-01-17 15:57:55 +000018use crate::remote_provisioning;
Alan Stokesac667072024-02-19 16:26:00 +000019use crate::rkpvm::{generate_ecdsa_p256_key_pair, request_attestation};
20use crate::{get_calling_pid, get_calling_uid, REMOTELY_PROVISIONED_COMPONENT_SERVICE_NAME};
David Brazdilafc9a9e2023-01-12 16:08:10 +000021use android_os_permissions_aidl::aidl::android::os::IPermissionController;
Alan Stokesac667072024-02-19 16:26:00 +000022use android_system_virtualizationcommon::aidl::android::system::virtualizationcommon;
23use android_system_virtualizationservice::aidl::android::system::virtualizationservice;
24use android_system_virtualizationservice_internal as android_vs_internal;
25use android_system_virtualmachineservice::aidl::android::system::virtualmachineservice;
26use android_vs_internal::aidl::android::system::virtualizationservice_internal;
Alice Wangd1b11a02023-04-18 12:30:20 +000027use anyhow::{anyhow, ensure, Context, Result};
Jiyong Parkd7bd2f22023-08-10 20:41:19 +090028use avflog::LogResult;
Alan Stokesac667072024-02-19 16:26:00 +000029use binder::{
30 self, wait_for_interface, BinderFeatures, ExceptionCode, Interface, IntoBinderResult,
31 LazyServiceGuard, ParcelFileDescriptor, Status, Strong,
32};
Jakob Vukalovicd42aa2c2023-11-09 16:04:00 +000033use lazy_static::lazy_static;
David Brazdilafc9a9e2023-01-12 16:08:10 +000034use libc::VMADDR_CID_HOST;
35use log::{error, info, warn};
Alan Stokesac667072024-02-19 16:26:00 +000036use nix::unistd::{chown, Uid};
37use openssl::x509::X509;
Alice Wangbff017f2023-11-09 14:43:28 +000038use rkpd_client::get_rkpd_attestation_key;
David Brazdilafc9a9e2023-01-12 16:08:10 +000039use rustutils::system_properties;
Inseob Kimc4a774d2023-08-30 12:48:43 +090040use serde::Deserialize;
Alan Stokesac667072024-02-19 16:26:00 +000041use service_vm_comm::Response;
Inseob Kimc4a774d2023-08-30 12:48:43 +090042use std::collections::{HashMap, HashSet};
David Brazdil2dfefd12023-11-17 14:07:36 +000043use std::fs::{self, create_dir, remove_dir_all, remove_file, set_permissions, File, Permissions};
David Brazdilafc9a9e2023-01-12 16:08:10 +000044use std::io::{Read, Write};
45use std::os::unix::fs::PermissionsExt;
46use std::os::unix::raw::{pid_t, uid_t};
Inseob Kim55438b22023-08-09 20:16:01 +090047use std::path::{Path, PathBuf};
David Brazdilafc9a9e2023-01-12 16:08:10 +000048use std::sync::{Arc, Mutex, Weak};
49use tombstoned_client::{DebuggerdDumpType, TombstonedConnection};
Alan Stokesac667072024-02-19 16:26:00 +000050use virtualizationcommon::Certificate::Certificate;
51use virtualizationservice::{
52 AssignableDevice::AssignableDevice, VirtualMachineDebugInfo::VirtualMachineDebugInfo,
53};
54use virtualizationservice_internal::{
55 AtomVmBooted::AtomVmBooted,
56 AtomVmCreationRequested::AtomVmCreationRequested,
57 AtomVmExited::AtomVmExited,
58 IBoundDevice::IBoundDevice,
59 IGlobalVmContext::{BnGlobalVmContext, IGlobalVmContext},
60 IVfioHandler::VfioDev::VfioDev,
61 IVfioHandler::{BpVfioHandler, IVfioHandler},
62 IVirtualizationServiceInternal::IVirtualizationServiceInternal,
63};
64use virtualmachineservice::IVirtualMachineService::VM_TOMBSTONES_SERVICE_PORT;
David Brazdilafc9a9e2023-01-12 16:08:10 +000065use vsock::{VsockListener, VsockStream};
David Brazdilafc9a9e2023-01-12 16:08:10 +000066
67/// The unique ID of a VM used (together with a port number) for vsock communication.
68pub type Cid = u32;
69
70pub const BINDER_SERVICE_IDENTIFIER: &str = "android.system.virtualizationservice";
71
72/// Directory in which to write disk image files used while running VMs.
73pub const TEMPORARY_DIRECTORY: &str = "/data/misc/virtualizationservice";
74
75/// The first CID to assign to a guest VM managed by the VirtualizationService. CIDs lower than this
76/// are reserved for the host or other usage.
77const GUEST_CID_MIN: Cid = 2048;
78const GUEST_CID_MAX: Cid = 65535;
79
80const SYSPROP_LAST_CID: &str = "virtualizationservice.state.last_cid";
81
82const CHUNK_RECV_MAX_LEN: usize = 1024;
83
Alice Wange64dd182024-01-17 15:57:55 +000084/// The fake certificate is used for testing only when a client VM requests attestation in test
85/// mode, it is a single certificate extracted on an unregistered device for testing.
86/// Here is the snapshot of the certificate:
87///
88/// ```
89/// Certificate:
90/// Data:
91/// Version: 3 (0x2)
92/// Serial Number:
93/// 59:ae:50:98:95:e1:34:25:f1:21:93:c0:4c:e5:24:66
94/// Signature Algorithm: ecdsa-with-SHA256
95/// Issuer: CN = Droid Unregistered Device CA, O = Google Test LLC
96/// Validity
97/// Not Before: Feb 5 14:39:39 2024 GMT
98/// Not After : Feb 14 14:39:39 2024 GMT
99/// Subject: CN = 59ae509895e13425f12193c04ce52466, O = TEE
100/// Subject Public Key Info:
101/// Public Key Algorithm: id-ecPublicKey
102/// Public-Key: (256 bit)
103/// pub:
104/// 04:30:32:cd:95:12:b0:71:8b:b7:14:44:26:58:d5:
105/// 82:8c:25:55:2c:6d:ef:98:e3:4f:88:d0:74:82:09:
106/// 3e:8d:6c:f0:f2:18:d5:83:0e:0d:f2:ce:c5:15:38:
107/// e5:6a:e6:4d:4d:95:15:b7:24:e7:cb:4b:63:42:21:
108/// bc:36:c6:0a:d8
109/// ASN1 OID: prime256v1
110/// NIST CURVE: P-256
111/// X509v3 extensions:
112/// ...
113/// ```
114const FAKE_CERTIFICATE_FOR_TESTING: &[u8] = &[
115 0x30, 0x82, 0x01, 0xee, 0x30, 0x82, 0x01, 0x94, 0xa0, 0x03, 0x02, 0x01, 0x02, 0x02, 0x10, 0x59,
116 0xae, 0x50, 0x98, 0x95, 0xe1, 0x34, 0x25, 0xf1, 0x21, 0x93, 0xc0, 0x4c, 0xe5, 0x24, 0x66, 0x30,
117 0x0a, 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x04, 0x03, 0x02, 0x30, 0x41, 0x31, 0x25, 0x30,
118 0x23, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x1c, 0x44, 0x72, 0x6f, 0x69, 0x64, 0x20, 0x55, 0x6e,
119 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x65, 0x64, 0x20, 0x44, 0x65, 0x76, 0x69, 0x63,
120 0x65, 0x20, 0x43, 0x41, 0x31, 0x18, 0x30, 0x16, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0f, 0x47,
121 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x20, 0x54, 0x65, 0x73, 0x74, 0x20, 0x4c, 0x4c, 0x43, 0x30, 0x1e,
122 0x17, 0x0d, 0x32, 0x34, 0x30, 0x32, 0x30, 0x35, 0x31, 0x34, 0x33, 0x39, 0x33, 0x39, 0x5a, 0x17,
123 0x0d, 0x32, 0x34, 0x30, 0x32, 0x31, 0x34, 0x31, 0x34, 0x33, 0x39, 0x33, 0x39, 0x5a, 0x30, 0x39,
124 0x31, 0x29, 0x30, 0x27, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x20, 0x35, 0x39, 0x61, 0x65, 0x35,
125 0x30, 0x39, 0x38, 0x39, 0x35, 0x65, 0x31, 0x33, 0x34, 0x32, 0x35, 0x66, 0x31, 0x32, 0x31, 0x39,
126 0x33, 0x63, 0x30, 0x34, 0x63, 0x65, 0x35, 0x32, 0x34, 0x36, 0x36, 0x31, 0x0c, 0x30, 0x0a, 0x06,
127 0x03, 0x55, 0x04, 0x0a, 0x13, 0x03, 0x54, 0x45, 0x45, 0x30, 0x59, 0x30, 0x13, 0x06, 0x07, 0x2a,
128 0x86, 0x48, 0xce, 0x3d, 0x02, 0x01, 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x03, 0x01, 0x07,
129 0x03, 0x42, 0x00, 0x04, 0x30, 0x32, 0xcd, 0x95, 0x12, 0xb0, 0x71, 0x8b, 0xb7, 0x14, 0x44, 0x26,
130 0x58, 0xd5, 0x82, 0x8c, 0x25, 0x55, 0x2c, 0x6d, 0xef, 0x98, 0xe3, 0x4f, 0x88, 0xd0, 0x74, 0x82,
131 0x09, 0x3e, 0x8d, 0x6c, 0xf0, 0xf2, 0x18, 0xd5, 0x83, 0x0e, 0x0d, 0xf2, 0xce, 0xc5, 0x15, 0x38,
132 0xe5, 0x6a, 0xe6, 0x4d, 0x4d, 0x95, 0x15, 0xb7, 0x24, 0xe7, 0xcb, 0x4b, 0x63, 0x42, 0x21, 0xbc,
133 0x36, 0xc6, 0x0a, 0xd8, 0xa3, 0x76, 0x30, 0x74, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04,
134 0x16, 0x04, 0x14, 0x39, 0x81, 0x41, 0x0a, 0xb9, 0xf3, 0xf4, 0x5b, 0x75, 0x97, 0x4a, 0x46, 0xd6,
135 0x30, 0x9e, 0x1d, 0x7a, 0x3b, 0xec, 0xa8, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18,
136 0x30, 0x16, 0x80, 0x14, 0x82, 0xbd, 0x00, 0xde, 0xcb, 0xc5, 0xe7, 0x72, 0x87, 0x3d, 0x1c, 0x0a,
137 0x1e, 0x78, 0x4f, 0xf5, 0xd3, 0xc1, 0x3e, 0xb8, 0x30, 0x0f, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01,
138 0x01, 0xff, 0x04, 0x05, 0x30, 0x03, 0x01, 0x01, 0xff, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x1d, 0x0f,
139 0x01, 0x01, 0xff, 0x04, 0x04, 0x03, 0x02, 0x02, 0x04, 0x30, 0x11, 0x06, 0x0a, 0x2b, 0x06, 0x01,
140 0x04, 0x01, 0xd6, 0x79, 0x02, 0x01, 0x1e, 0x04, 0x03, 0xa1, 0x01, 0x08, 0x30, 0x0a, 0x06, 0x08,
141 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x04, 0x03, 0x02, 0x03, 0x48, 0x00, 0x30, 0x45, 0x02, 0x21, 0x00,
142 0xae, 0xd8, 0x40, 0x9e, 0x37, 0x3e, 0x5c, 0x9c, 0xe2, 0x93, 0x3d, 0x8c, 0xf7, 0x05, 0x10, 0xe7,
143 0xd1, 0x2b, 0x87, 0x8a, 0xee, 0xd6, 0x1e, 0x6c, 0x3b, 0xd2, 0x91, 0x3e, 0xa5, 0xdf, 0x91, 0x20,
144 0x02, 0x20, 0x7f, 0x0f, 0x29, 0x54, 0x60, 0x80, 0x07, 0x50, 0x5f, 0x56, 0x6b, 0x9f, 0xe0, 0x94,
145 0xb4, 0x3f, 0x3b, 0x0f, 0x61, 0xa0, 0x33, 0x40, 0xe6, 0x1a, 0x42, 0xda, 0x4b, 0xa4, 0xfd, 0x92,
146 0xb9, 0x0f,
147];
148
Jakob Vukalovicd42aa2c2023-11-09 16:04:00 +0000149lazy_static! {
Alice Wange64dd182024-01-17 15:57:55 +0000150 static ref FAKE_PROVISIONED_KEY_BLOB_FOR_TESTING: Mutex<Option<Vec<u8>>> = Mutex::new(None);
Jakob Vukalovicd42aa2c2023-11-09 16:04:00 +0000151 static ref VFIO_SERVICE: Strong<dyn IVfioHandler> =
152 wait_for_interface(<BpVfioHandler as IVfioHandler>::get_descriptor())
153 .expect("Could not connect to VfioHandler");
154}
155
David Brazdilafc9a9e2023-01-12 16:08:10 +0000156fn is_valid_guest_cid(cid: Cid) -> bool {
157 (GUEST_CID_MIN..=GUEST_CID_MAX).contains(&cid)
158}
159
160/// Singleton service for allocating globally-unique VM resources, such as the CID, and running
161/// singleton servers, like tombstone receiver.
162#[derive(Debug, Default)]
163pub struct VirtualizationServiceInternal {
164 state: Arc<Mutex<GlobalState>>,
165}
166
167impl VirtualizationServiceInternal {
168 pub fn init() -> VirtualizationServiceInternal {
169 let service = VirtualizationServiceInternal::default();
170
171 std::thread::spawn(|| {
172 if let Err(e) = handle_stream_connection_tombstoned() {
173 warn!("Error receiving tombstone from guest or writing them. Error: {:?}", e);
174 }
175 });
176
177 service
178 }
179}
180
181impl Interface for VirtualizationServiceInternal {}
182
183impl IVirtualizationServiceInternal for VirtualizationServiceInternal {
184 fn removeMemlockRlimit(&self) -> binder::Result<()> {
185 let pid = get_calling_pid();
186 let lim = libc::rlimit { rlim_cur: libc::RLIM_INFINITY, rlim_max: libc::RLIM_INFINITY };
187
Andrew Walbranb58d1b42023-07-07 13:54:49 +0100188 // SAFETY: borrowing the new limit struct only
David Brazdilafc9a9e2023-01-12 16:08:10 +0000189 let ret = unsafe { libc::prlimit(pid, libc::RLIMIT_MEMLOCK, &lim, std::ptr::null_mut()) };
190
191 match ret {
192 0 => Ok(()),
Jiyong Park2227eaa2023-08-04 11:59:18 +0900193 -1 => Err(std::io::Error::last_os_error().into()),
194 n => Err(anyhow!("Unexpected return value from prlimit(): {n}")),
David Brazdilafc9a9e2023-01-12 16:08:10 +0000195 }
Jiyong Park2227eaa2023-08-04 11:59:18 +0900196 .or_binder_exception(ExceptionCode::ILLEGAL_STATE)
David Brazdilafc9a9e2023-01-12 16:08:10 +0000197 }
198
199 fn allocateGlobalVmContext(
200 &self,
201 requester_debug_pid: i32,
202 ) -> binder::Result<Strong<dyn IGlobalVmContext>> {
203 check_manage_access()?;
204
205 let requester_uid = get_calling_uid();
206 let requester_debug_pid = requester_debug_pid as pid_t;
207 let state = &mut *self.state.lock().unwrap();
Jiyong Park2227eaa2023-08-04 11:59:18 +0900208 state
209 .allocate_vm_context(requester_uid, requester_debug_pid)
210 .or_binder_exception(ExceptionCode::ILLEGAL_STATE)
David Brazdilafc9a9e2023-01-12 16:08:10 +0000211 }
212
213 fn atomVmBooted(&self, atom: &AtomVmBooted) -> Result<(), Status> {
214 forward_vm_booted_atom(atom);
215 Ok(())
216 }
217
218 fn atomVmCreationRequested(&self, atom: &AtomVmCreationRequested) -> Result<(), Status> {
219 forward_vm_creation_atom(atom);
220 Ok(())
221 }
222
223 fn atomVmExited(&self, atom: &AtomVmExited) -> Result<(), Status> {
224 forward_vm_exited_atom(atom);
225 Ok(())
226 }
227
228 fn debugListVms(&self) -> binder::Result<Vec<VirtualMachineDebugInfo>> {
229 check_debug_access()?;
230
231 let state = &mut *self.state.lock().unwrap();
232 let cids = state
233 .held_contexts
234 .iter()
235 .filter_map(|(_, inst)| Weak::upgrade(inst))
236 .map(|vm| VirtualMachineDebugInfo {
237 cid: vm.cid as i32,
238 temporaryDirectory: vm.get_temp_dir().to_string_lossy().to_string(),
239 requesterUid: vm.requester_uid as i32,
Charisee96113f32023-01-26 09:00:42 +0000240 requesterPid: vm.requester_debug_pid,
David Brazdilafc9a9e2023-01-12 16:08:10 +0000241 })
242 .collect();
243 Ok(cids)
244 }
Alice Wangc2fec932023-02-23 16:24:02 +0000245
Alice Wange64dd182024-01-17 15:57:55 +0000246 fn enableTestAttestation(&self) -> binder::Result<()> {
247 check_manage_access()?;
248 check_use_custom_virtual_machine()?;
249 if !cfg!(remote_attestation) {
250 return Err(Status::new_exception_str(
251 ExceptionCode::UNSUPPORTED_OPERATION,
252 Some(
253 "enableTestAttestation is not supported with the remote_attestation \
254 feature disabled",
255 ),
256 ))
257 .with_log();
258 }
259 let res = generate_ecdsa_p256_key_pair()
260 .context("Failed to generate ECDSA P-256 key pair for testing")
261 .with_log()
262 .or_service_specific_exception(-1)?;
263 match res {
264 Response::GenerateEcdsaP256KeyPair(key_pair) => {
265 FAKE_PROVISIONED_KEY_BLOB_FOR_TESTING
266 .lock()
267 .unwrap()
268 .replace(key_pair.key_blob.to_vec());
269 Ok(())
270 }
271 _ => Err(remote_provisioning::to_service_specific_error(res)),
272 }
273 .with_log()
274 }
275
Alice Wangbff017f2023-11-09 14:43:28 +0000276 fn requestAttestation(
277 &self,
278 csr: &[u8],
279 requester_uid: i32,
Alice Wange64dd182024-01-17 15:57:55 +0000280 test_mode: bool,
Alice Wangbff017f2023-11-09 14:43:28 +0000281 ) -> binder::Result<Vec<Certificate>> {
Alice Wangc2fec932023-02-23 16:24:02 +0000282 check_manage_access()?;
Alice Wang4c6c5582023-11-23 15:07:18 +0000283 if !cfg!(remote_attestation) {
284 return Err(Status::new_exception_str(
Alice Wange9ac2db2023-09-08 15:13:13 +0000285 ExceptionCode::UNSUPPORTED_OPERATION,
286 Some(
Alice Wanga410b642023-10-18 09:05:15 +0000287 "requestAttestation is not supported with the remote_attestation feature \
288 disabled",
Alice Wange9ac2db2023-09-08 15:13:13 +0000289 ),
290 ))
Alice Wang4c6c5582023-11-23 15:07:18 +0000291 .with_log();
Alice Wange9ac2db2023-09-08 15:13:13 +0000292 }
Alice Wang4c6c5582023-11-23 15:07:18 +0000293 info!("Received csr. Requestting attestation...");
Alice Wange64dd182024-01-17 15:57:55 +0000294 let (key_blob, certificate_chain) = if test_mode {
295 check_use_custom_virtual_machine()?;
296 info!("Using the fake key blob for testing...");
297 (
298 FAKE_PROVISIONED_KEY_BLOB_FOR_TESTING
299 .lock()
300 .unwrap()
301 .clone()
302 .ok_or_else(|| anyhow!("No key blob for testing"))
303 .with_log()
304 .or_service_specific_exception(-1)?,
305 FAKE_CERTIFICATE_FOR_TESTING.to_vec(),
306 )
307 } else {
308 info!("Retrieving the remotely provisioned keys from RKPD...");
309 let attestation_key = get_rkpd_attestation_key(
310 REMOTELY_PROVISIONED_COMPONENT_SERVICE_NAME,
311 requester_uid as u32,
312 )
313 .context("Failed to retrieve the remotely provisioned keys")
314 .with_log()
315 .or_service_specific_exception(-1)?;
316 (attestation_key.keyBlob, attestation_key.encodedCertChain)
317 };
318 let mut certificate_chain = split_x509_certificate_chain(&certificate_chain)
Alice Wang4c6c5582023-11-23 15:07:18 +0000319 .context("Failed to split the remotely provisioned certificate chain")
320 .with_log()
321 .or_service_specific_exception(-1)?;
322 if certificate_chain.is_empty() {
323 return Err(Status::new_service_specific_error_str(
324 -1,
325 Some("The certificate chain should contain at least 1 certificate"),
326 ))
327 .with_log();
328 }
Alice Wang20b8ebc2023-11-17 09:54:47 +0000329 let certificate = request_attestation(
330 csr.to_vec(),
Alice Wange64dd182024-01-17 15:57:55 +0000331 key_blob,
Alice Wang20b8ebc2023-11-17 09:54:47 +0000332 certificate_chain[0].encodedCertificate.clone(),
333 )
334 .context("Failed to request attestation")
335 .with_log()
336 .or_service_specific_exception(-1)?;
Alice Wang4c6c5582023-11-23 15:07:18 +0000337 certificate_chain.insert(0, Certificate { encodedCertificate: certificate });
338
339 Ok(certificate_chain)
Alice Wangc2fec932023-02-23 16:24:02 +0000340 }
Inseob Kim53d0b212023-07-20 16:58:37 +0900341
342 fn getAssignableDevices(&self) -> binder::Result<Vec<AssignableDevice>> {
343 check_use_custom_virtual_machine()?;
344
Inseob Kim7307a892023-09-14 13:37:58 +0900345 Ok(get_assignable_devices()?
346 .device
347 .into_iter()
348 .map(|x| AssignableDevice { node: x.sysfs_path, kind: x.kind })
349 .collect::<Vec<_>>())
Inseob Kim53d0b212023-07-20 16:58:37 +0900350 }
Inseob Kim1ca0f652023-07-20 17:18:12 +0900351
Jakob Vukalovicd42aa2c2023-11-09 16:04:00 +0000352 fn bindDevicesToVfioDriver(
353 &self,
354 devices: &[String],
355 ) -> binder::Result<Vec<Strong<dyn IBoundDevice>>> {
Inseob Kim1ca0f652023-07-20 17:18:12 +0900356 check_use_custom_virtual_machine()?;
357
Jakob Vukalovicd42aa2c2023-11-09 16:04:00 +0000358 let devices = get_assignable_devices()?
Inseob Kim7307a892023-09-14 13:37:58 +0900359 .device
360 .into_iter()
361 .filter_map(|x| {
362 if devices.contains(&x.sysfs_path) {
Jakob Vukalovicd42aa2c2023-11-09 16:04:00 +0000363 Some(VfioDev { sysfsPath: x.sysfs_path, dtboLabel: x.dtbo_label })
Inseob Kim7307a892023-09-14 13:37:58 +0900364 } else {
Jakob Vukalovicd42aa2c2023-11-09 16:04:00 +0000365 warn!("device {} is not assignable", x.sysfs_path);
Inseob Kim7307a892023-09-14 13:37:58 +0900366 None
367 }
368 })
Jakob Vukalovicd42aa2c2023-11-09 16:04:00 +0000369 .collect::<Vec<VfioDev>>();
370
371 VFIO_SERVICE.bindDevicesToVfioDriver(devices.as_slice())
Inseob Kim1ca0f652023-07-20 17:18:12 +0900372 }
David Brazdil2dfefd12023-11-17 14:07:36 +0000373
374 fn getDtboFile(&self) -> binder::Result<ParcelFileDescriptor> {
375 check_use_custom_virtual_machine()?;
376
377 let state = &mut *self.state.lock().unwrap();
378 let file = state.get_dtbo_file().or_service_specific_exception(-1)?;
379 Ok(ParcelFileDescriptor::new(file))
380 }
Inseob Kim1ca0f652023-07-20 17:18:12 +0900381}
382
Inseob Kimc4a774d2023-08-30 12:48:43 +0900383// KEEP IN SYNC WITH assignable_devices.xsd
384#[derive(Debug, Deserialize)]
385struct Device {
386 kind: String,
Jaewan Kim35e818d2023-10-18 05:36:38 +0000387 dtbo_label: String,
Inseob Kimc4a774d2023-08-30 12:48:43 +0900388 sysfs_path: String,
389}
390
Inseob Kim7307a892023-09-14 13:37:58 +0900391#[derive(Debug, Default, Deserialize)]
Inseob Kimc4a774d2023-08-30 12:48:43 +0900392struct Devices {
393 device: Vec<Device>,
394}
395
Inseob Kim7307a892023-09-14 13:37:58 +0900396fn get_assignable_devices() -> binder::Result<Devices> {
397 let xml_path = Path::new("/vendor/etc/avf/assignable_devices.xml");
398 if !xml_path.exists() {
399 return Ok(Devices { ..Default::default() });
400 }
401
402 let xml = fs::read(xml_path)
403 .context("Failed to read assignable_devices.xml")
404 .with_log()
405 .or_service_specific_exception(-1)?;
406
407 let xml = String::from_utf8(xml)
408 .context("assignable_devices.xml is not a valid UTF-8 file")
409 .with_log()
410 .or_service_specific_exception(-1)?;
411
412 let mut devices: Devices = serde_xml_rs::from_str(&xml)
413 .context("can't parse assignable_devices.xml")
414 .with_log()
415 .or_service_specific_exception(-1)?;
416
417 let mut device_set = HashSet::new();
418 devices.device.retain(move |device| {
419 if device_set.contains(&device.sysfs_path) {
420 warn!("duplicated assignable device {device:?}; ignoring...");
421 return false;
422 }
423
424 if !Path::new(&device.sysfs_path).exists() {
425 warn!("assignable device {device:?} doesn't exist; ignoring...");
426 return false;
427 }
428
429 device_set.insert(device.sysfs_path.clone());
430 true
431 });
432 Ok(devices)
433}
434
Alice Wang4c6c5582023-11-23 15:07:18 +0000435fn split_x509_certificate_chain(mut cert_chain: &[u8]) -> Result<Vec<Certificate>> {
436 let mut out = Vec::new();
437 while !cert_chain.is_empty() {
Alice Wangfc5a44a2023-12-21 12:22:40 +0000438 let cert = X509::from_der(cert_chain)?;
439 let end = cert.to_der()?.len();
Alice Wang4c6c5582023-11-23 15:07:18 +0000440 out.push(Certificate { encodedCertificate: cert_chain[..end].to_vec() });
Alice Wangfc5a44a2023-12-21 12:22:40 +0000441 cert_chain = &cert_chain[end..];
Alice Wang4c6c5582023-11-23 15:07:18 +0000442 }
443 Ok(out)
444}
445
David Brazdilafc9a9e2023-01-12 16:08:10 +0000446#[derive(Debug, Default)]
447struct GlobalVmInstance {
448 /// The unique CID assigned to the VM for vsock communication.
449 cid: Cid,
450 /// UID of the client who requested this VM instance.
451 requester_uid: uid_t,
452 /// PID of the client who requested this VM instance.
453 requester_debug_pid: pid_t,
454}
455
456impl GlobalVmInstance {
457 fn get_temp_dir(&self) -> PathBuf {
458 let cid = self.cid;
459 format!("{TEMPORARY_DIRECTORY}/{cid}").into()
460 }
461}
462
463/// The mutable state of the VirtualizationServiceInternal. There should only be one instance
464/// of this struct.
465#[derive(Debug, Default)]
466struct GlobalState {
467 /// VM contexts currently allocated to running VMs. A CID is never recycled as long
468 /// as there is a strong reference held by a GlobalVmContext.
469 held_contexts: HashMap<Cid, Weak<GlobalVmInstance>>,
David Brazdil2dfefd12023-11-17 14:07:36 +0000470
471 /// Cached read-only FD of VM DTBO file. Also serves as a lock for creating the file.
472 dtbo_file: Mutex<Option<File>>,
David Brazdilafc9a9e2023-01-12 16:08:10 +0000473}
474
475impl GlobalState {
476 /// Get the next available CID, or an error if we have run out. The last CID used is stored in
477 /// a system property so that restart of virtualizationservice doesn't reuse CID while the host
478 /// Android is up.
479 fn get_next_available_cid(&mut self) -> Result<Cid> {
480 // Start trying to find a CID from the last used CID + 1. This ensures
481 // that we do not eagerly recycle CIDs. It makes debugging easier but
482 // also means that retrying to allocate a CID, eg. because it is
483 // erroneously occupied by a process, will not recycle the same CID.
484 let last_cid_prop =
485 system_properties::read(SYSPROP_LAST_CID)?.and_then(|val| match val.parse::<Cid>() {
486 Ok(num) => {
487 if is_valid_guest_cid(num) {
488 Some(num)
489 } else {
490 error!("Invalid value '{}' of property '{}'", num, SYSPROP_LAST_CID);
491 None
492 }
493 }
494 Err(_) => {
495 error!("Invalid value '{}' of property '{}'", val, SYSPROP_LAST_CID);
496 None
497 }
498 });
499
500 let first_cid = if let Some(last_cid) = last_cid_prop {
501 if last_cid == GUEST_CID_MAX {
502 GUEST_CID_MIN
503 } else {
504 last_cid + 1
505 }
506 } else {
507 GUEST_CID_MIN
508 };
509
510 let cid = self
511 .find_available_cid(first_cid..=GUEST_CID_MAX)
512 .or_else(|| self.find_available_cid(GUEST_CID_MIN..first_cid))
513 .ok_or_else(|| anyhow!("Could not find an available CID."))?;
514
515 system_properties::write(SYSPROP_LAST_CID, &format!("{}", cid))?;
516 Ok(cid)
517 }
518
519 fn find_available_cid<I>(&self, mut range: I) -> Option<Cid>
520 where
521 I: Iterator<Item = Cid>,
522 {
523 range.find(|cid| !self.held_contexts.contains_key(cid))
524 }
525
526 fn allocate_vm_context(
527 &mut self,
528 requester_uid: uid_t,
529 requester_debug_pid: pid_t,
530 ) -> Result<Strong<dyn IGlobalVmContext>> {
531 // Garbage collect unused VM contexts.
532 self.held_contexts.retain(|_, instance| instance.strong_count() > 0);
533
534 let cid = self.get_next_available_cid()?;
535 let instance = Arc::new(GlobalVmInstance { cid, requester_uid, requester_debug_pid });
David Brazdil2dfefd12023-11-17 14:07:36 +0000536 create_temporary_directory(&instance.get_temp_dir(), Some(requester_uid))?;
David Brazdilafc9a9e2023-01-12 16:08:10 +0000537
538 self.held_contexts.insert(cid, Arc::downgrade(&instance));
539 let binder = GlobalVmContext { instance, ..Default::default() };
540 Ok(BnGlobalVmContext::new_binder(binder, BinderFeatures::default()))
541 }
David Brazdil2dfefd12023-11-17 14:07:36 +0000542
543 fn get_dtbo_file(&mut self) -> Result<File> {
544 let mut file = self.dtbo_file.lock().unwrap();
545
546 let fd = if let Some(ref_fd) = &*file {
547 ref_fd.try_clone()?
548 } else {
549 let path = get_or_create_common_dir()?.join("vm.dtbo");
550 if path.exists() {
551 // All temporary files are deleted when the service is started.
552 // If the file exists but the FD is not cached, the file is
553 // likely corrupted.
554 remove_file(&path).context("Failed to clone cached VM DTBO file descriptor")?;
555 }
556
557 // Open a write-only file descriptor for vfio_handler.
558 let write_fd = File::create(&path).context("Failed to create VM DTBO file")?;
Jakob Vukalovicd42aa2c2023-11-09 16:04:00 +0000559 VFIO_SERVICE.writeVmDtbo(&ParcelFileDescriptor::new(write_fd))?;
David Brazdil2dfefd12023-11-17 14:07:36 +0000560
561 // Open read-only. This FD will be cached and returned to clients.
562 let read_fd = File::open(&path).context("Failed to open VM DTBO file")?;
563 let read_fd_clone =
564 read_fd.try_clone().context("Failed to clone VM DTBO file descriptor")?;
565 *file = Some(read_fd);
566 read_fd_clone
567 };
568
569 Ok(fd)
570 }
David Brazdilafc9a9e2023-01-12 16:08:10 +0000571}
572
David Brazdil2dfefd12023-11-17 14:07:36 +0000573fn create_temporary_directory(path: &PathBuf, requester_uid: Option<uid_t>) -> Result<()> {
574 // Directory may exist if previous attempt to create it had failed.
575 // Delete it before trying again.
David Brazdilafc9a9e2023-01-12 16:08:10 +0000576 if path.as_path().exists() {
577 remove_temporary_dir(path).unwrap_or_else(|e| {
578 warn!("Could not delete temporary directory {:?}: {}", path, e);
579 });
580 }
David Brazdil2dfefd12023-11-17 14:07:36 +0000581 // Create directory.
582 create_dir(path).with_context(|| format!("Could not create temporary directory {:?}", path))?;
583 // If provided, change ownership to client's UID but system's GID, and permissions 0700.
David Brazdilafc9a9e2023-01-12 16:08:10 +0000584 // If the chown() fails, this will leave behind an empty directory that will get removed
585 // at the next attempt, or if virtualizationservice is restarted.
David Brazdil2dfefd12023-11-17 14:07:36 +0000586 if let Some(uid) = requester_uid {
587 chown(path, Some(Uid::from_raw(uid)), None).with_context(|| {
588 format!("Could not set ownership of temporary directory {:?}", path)
589 })?;
590 }
David Brazdilafc9a9e2023-01-12 16:08:10 +0000591 Ok(())
592}
593
594/// Removes a directory owned by a different user by first changing its owner back
595/// to VirtualizationService.
596pub fn remove_temporary_dir(path: &PathBuf) -> Result<()> {
Alice Wangd1b11a02023-04-18 12:30:20 +0000597 ensure!(path.as_path().is_dir(), "Path {:?} is not a directory", path);
David Brazdilafc9a9e2023-01-12 16:08:10 +0000598 chown(path, Some(Uid::current()), None)?;
599 set_permissions(path, Permissions::from_mode(0o700))?;
Alice Wangd1b11a02023-04-18 12:30:20 +0000600 remove_dir_all(path)?;
David Brazdilafc9a9e2023-01-12 16:08:10 +0000601 Ok(())
602}
603
David Brazdil2dfefd12023-11-17 14:07:36 +0000604fn get_or_create_common_dir() -> Result<PathBuf> {
605 let path = Path::new(TEMPORARY_DIRECTORY).join("common");
606 if !path.exists() {
607 create_temporary_directory(&path, None)?;
608 }
609 Ok(path)
610}
611
David Brazdilafc9a9e2023-01-12 16:08:10 +0000612/// Implementation of the AIDL `IGlobalVmContext` interface.
613#[derive(Debug, Default)]
614struct GlobalVmContext {
615 /// Strong reference to the context's instance data structure.
616 instance: Arc<GlobalVmInstance>,
617 /// Keeps our service process running as long as this VM context exists.
618 #[allow(dead_code)]
619 lazy_service_guard: LazyServiceGuard,
620}
621
622impl Interface for GlobalVmContext {}
623
624impl IGlobalVmContext for GlobalVmContext {
625 fn getCid(&self) -> binder::Result<i32> {
626 Ok(self.instance.cid as i32)
627 }
628
629 fn getTemporaryDirectory(&self) -> binder::Result<String> {
630 Ok(self.instance.get_temp_dir().to_string_lossy().to_string())
631 }
632}
633
634fn handle_stream_connection_tombstoned() -> Result<()> {
635 // Should not listen for tombstones on a guest VM's port.
636 assert!(!is_valid_guest_cid(VM_TOMBSTONES_SERVICE_PORT as Cid));
637 let listener =
638 VsockListener::bind_with_cid_port(VMADDR_CID_HOST, VM_TOMBSTONES_SERVICE_PORT as Cid)?;
639 for incoming_stream in listener.incoming() {
640 let mut incoming_stream = match incoming_stream {
641 Err(e) => {
642 warn!("invalid incoming connection: {:?}", e);
643 continue;
644 }
645 Ok(s) => s,
646 };
647 std::thread::spawn(move || {
648 if let Err(e) = handle_tombstone(&mut incoming_stream) {
649 error!("Failed to write tombstone- {:?}", e);
650 }
651 });
652 }
653 Ok(())
654}
655
656fn handle_tombstone(stream: &mut VsockStream) -> Result<()> {
657 if let Ok(addr) = stream.peer_addr() {
658 info!("Vsock Stream connected to cid={} for tombstones", addr.cid());
659 }
660 let tb_connection =
661 TombstonedConnection::connect(std::process::id() as i32, DebuggerdDumpType::Tombstone)
662 .context("Failed to connect to tombstoned")?;
663 let mut text_output = tb_connection
664 .text_output
665 .as_ref()
666 .ok_or_else(|| anyhow!("Could not get file to write the tombstones on"))?;
667 let mut num_bytes_read = 0;
668 loop {
669 let mut chunk_recv = [0; CHUNK_RECV_MAX_LEN];
670 let n = stream
671 .read(&mut chunk_recv)
672 .context("Failed to read tombstone data from Vsock stream")?;
673 if n == 0 {
674 break;
675 }
676 num_bytes_read += n;
677 text_output.write_all(&chunk_recv[0..n]).context("Failed to write guests tombstones")?;
678 }
679 info!("Received {} bytes from guest & wrote to tombstone file", num_bytes_read);
680 tb_connection.notify_completion()?;
681 Ok(())
682}
683
684/// Checks whether the caller has a specific permission
685fn check_permission(perm: &str) -> binder::Result<()> {
686 let calling_pid = get_calling_pid();
687 let calling_uid = get_calling_uid();
688 // Root can do anything
689 if calling_uid == 0 {
690 return Ok(());
691 }
692 let perm_svc: Strong<dyn IPermissionController::IPermissionController> =
693 binder::get_interface("permission")?;
694 if perm_svc.checkPermission(perm, calling_pid, calling_uid as i32)? {
695 Ok(())
696 } else {
Jiyong Park2227eaa2023-08-04 11:59:18 +0900697 Err(anyhow!("does not have the {} permission", perm))
698 .or_binder_exception(ExceptionCode::SECURITY)
David Brazdilafc9a9e2023-01-12 16:08:10 +0000699 }
700}
701
702/// Check whether the caller of the current Binder method is allowed to call debug methods.
703fn check_debug_access() -> binder::Result<()> {
704 check_permission("android.permission.DEBUG_VIRTUAL_MACHINE")
705}
706
707/// Check whether the caller of the current Binder method is allowed to manage VMs
708fn check_manage_access() -> binder::Result<()> {
709 check_permission("android.permission.MANAGE_VIRTUAL_MACHINE")
710}
Inseob Kim53d0b212023-07-20 16:58:37 +0900711
712/// Check whether the caller of the current Binder method is allowed to use custom VMs
713fn check_use_custom_virtual_machine() -> binder::Result<()> {
714 check_permission("android.permission.USE_CUSTOM_VIRTUAL_MACHINE")
715}
Alice Wang4c6c5582023-11-23 15:07:18 +0000716
717#[cfg(test)]
718mod tests {
719 use super::*;
720 use std::fs;
721
722 const TEST_RKP_CERT_CHAIN_PATH: &str = "testdata/rkp_cert_chain.der";
723
724 #[test]
725 fn splitting_x509_certificate_chain_succeeds() -> Result<()> {
726 let bytes = fs::read(TEST_RKP_CERT_CHAIN_PATH)?;
727 let cert_chain = split_x509_certificate_chain(&bytes)?;
728
729 assert_eq!(4, cert_chain.len());
730 for cert in cert_chain {
Alice Wangfc5a44a2023-12-21 12:22:40 +0000731 let x509_cert = X509::from_der(&cert.encodedCertificate)?;
732 assert_eq!(x509_cert.to_der()?.len(), cert.encodedCertificate.len());
Alice Wang4c6c5582023-11-23 15:07:18 +0000733 }
734 Ok(())
735 }
736}