blob: a1a1fb95eb9ac5adb6748b419b8235aed58d85f0 [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
Alice Wangbff017f2023-11-09 14:43:28 +000017use crate::{get_calling_pid, get_calling_uid, REMOTELY_PROVISIONED_COMPONENT_SERVICE_NAME};
David Brazdil33a31022023-01-12 16:55:16 +000018use crate::atom::{forward_vm_booted_atom, forward_vm_creation_atom, forward_vm_exited_atom};
Alice Wanga410b642023-10-18 09:05:15 +000019use crate::rkpvm::request_attestation;
David Brazdilafc9a9e2023-01-12 16:08:10 +000020use android_os_permissions_aidl::aidl::android::os::IPermissionController;
Alice Wang4e3015d2023-10-10 09:35:37 +000021use android_system_virtualizationcommon::aidl::android::system::virtualizationcommon::Certificate::Certificate;
Alice Wangc2fec932023-02-23 16:24:02 +000022use android_system_virtualizationservice::{
Inseob Kim53d0b212023-07-20 16:58:37 +090023 aidl::android::system::virtualizationservice::AssignableDevice::AssignableDevice,
Alice Wangc2fec932023-02-23 16:24:02 +000024 aidl::android::system::virtualizationservice::VirtualMachineDebugInfo::VirtualMachineDebugInfo,
25 binder::ParcelFileDescriptor,
26};
David Brazdilafc9a9e2023-01-12 16:08:10 +000027use android_system_virtualizationservice_internal::aidl::android::system::virtualizationservice_internal::{
28 AtomVmBooted::AtomVmBooted,
29 AtomVmCreationRequested::AtomVmCreationRequested,
30 AtomVmExited::AtomVmExited,
Jakob Vukalovicd42aa2c2023-11-09 16:04:00 +000031 IBoundDevice::IBoundDevice,
David Brazdilafc9a9e2023-01-12 16:08:10 +000032 IGlobalVmContext::{BnGlobalVmContext, IGlobalVmContext},
33 IVirtualizationServiceInternal::IVirtualizationServiceInternal,
Inseob Kimbdca0472023-07-28 19:20:56 +090034 IVfioHandler::{BpVfioHandler, IVfioHandler},
Jakob Vukalovicd42aa2c2023-11-09 16:04:00 +000035 IVfioHandler::VfioDev::VfioDev,
David Brazdilafc9a9e2023-01-12 16:08:10 +000036};
37use android_system_virtualmachineservice::aidl::android::system::virtualmachineservice::IVirtualMachineService::VM_TOMBSTONES_SERVICE_PORT;
Alice Wangd1b11a02023-04-18 12:30:20 +000038use anyhow::{anyhow, ensure, Context, Result};
Jiyong Parkd7bd2f22023-08-10 20:41:19 +090039use avflog::LogResult;
Jiyong Park2227eaa2023-08-04 11:59:18 +090040use binder::{self, wait_for_interface, BinderFeatures, ExceptionCode, Interface, LazyServiceGuard, Status, Strong, IntoBinderResult};
Jakob Vukalovicd42aa2c2023-11-09 16:04:00 +000041use lazy_static::lazy_static;
David Brazdilafc9a9e2023-01-12 16:08:10 +000042use libc::VMADDR_CID_HOST;
43use log::{error, info, warn};
Alice Wangbff017f2023-11-09 14:43:28 +000044use rkpd_client::get_rkpd_attestation_key;
David Brazdilafc9a9e2023-01-12 16:08:10 +000045use rustutils::system_properties;
Inseob Kimc4a774d2023-08-30 12:48:43 +090046use serde::Deserialize;
47use std::collections::{HashMap, HashSet};
David Brazdil2dfefd12023-11-17 14:07:36 +000048use std::fs::{self, create_dir, remove_dir_all, remove_file, set_permissions, File, Permissions};
David Brazdilafc9a9e2023-01-12 16:08:10 +000049use std::io::{Read, Write};
50use std::os::unix::fs::PermissionsExt;
51use std::os::unix::raw::{pid_t, uid_t};
Inseob Kim55438b22023-08-09 20:16:01 +090052use std::path::{Path, PathBuf};
David Brazdilafc9a9e2023-01-12 16:08:10 +000053use std::sync::{Arc, Mutex, Weak};
54use tombstoned_client::{DebuggerdDumpType, TombstonedConnection};
55use vsock::{VsockListener, VsockStream};
Inseob Kimbdca0472023-07-28 19:20:56 +090056use nix::unistd::{chown, Uid};
Alice Wangfc5a44a2023-12-21 12:22:40 +000057use openssl::x509::X509;
David Brazdilafc9a9e2023-01-12 16:08:10 +000058
59/// The unique ID of a VM used (together with a port number) for vsock communication.
60pub type Cid = u32;
61
62pub const BINDER_SERVICE_IDENTIFIER: &str = "android.system.virtualizationservice";
63
64/// Directory in which to write disk image files used while running VMs.
65pub const TEMPORARY_DIRECTORY: &str = "/data/misc/virtualizationservice";
66
67/// The first CID to assign to a guest VM managed by the VirtualizationService. CIDs lower than this
68/// are reserved for the host or other usage.
69const GUEST_CID_MIN: Cid = 2048;
70const GUEST_CID_MAX: Cid = 65535;
71
72const SYSPROP_LAST_CID: &str = "virtualizationservice.state.last_cid";
73
74const CHUNK_RECV_MAX_LEN: usize = 1024;
75
Jakob Vukalovicd42aa2c2023-11-09 16:04:00 +000076lazy_static! {
77 static ref VFIO_SERVICE: Strong<dyn IVfioHandler> =
78 wait_for_interface(<BpVfioHandler as IVfioHandler>::get_descriptor())
79 .expect("Could not connect to VfioHandler");
80}
81
David Brazdilafc9a9e2023-01-12 16:08:10 +000082fn is_valid_guest_cid(cid: Cid) -> bool {
83 (GUEST_CID_MIN..=GUEST_CID_MAX).contains(&cid)
84}
85
86/// Singleton service for allocating globally-unique VM resources, such as the CID, and running
87/// singleton servers, like tombstone receiver.
88#[derive(Debug, Default)]
89pub struct VirtualizationServiceInternal {
90 state: Arc<Mutex<GlobalState>>,
91}
92
93impl VirtualizationServiceInternal {
94 pub fn init() -> VirtualizationServiceInternal {
95 let service = VirtualizationServiceInternal::default();
96
97 std::thread::spawn(|| {
98 if let Err(e) = handle_stream_connection_tombstoned() {
99 warn!("Error receiving tombstone from guest or writing them. Error: {:?}", e);
100 }
101 });
102
103 service
104 }
105}
106
107impl Interface for VirtualizationServiceInternal {}
108
109impl IVirtualizationServiceInternal for VirtualizationServiceInternal {
110 fn removeMemlockRlimit(&self) -> binder::Result<()> {
111 let pid = get_calling_pid();
112 let lim = libc::rlimit { rlim_cur: libc::RLIM_INFINITY, rlim_max: libc::RLIM_INFINITY };
113
Andrew Walbranb58d1b42023-07-07 13:54:49 +0100114 // SAFETY: borrowing the new limit struct only
David Brazdilafc9a9e2023-01-12 16:08:10 +0000115 let ret = unsafe { libc::prlimit(pid, libc::RLIMIT_MEMLOCK, &lim, std::ptr::null_mut()) };
116
117 match ret {
118 0 => Ok(()),
Jiyong Park2227eaa2023-08-04 11:59:18 +0900119 -1 => Err(std::io::Error::last_os_error().into()),
120 n => Err(anyhow!("Unexpected return value from prlimit(): {n}")),
David Brazdilafc9a9e2023-01-12 16:08:10 +0000121 }
Jiyong Park2227eaa2023-08-04 11:59:18 +0900122 .or_binder_exception(ExceptionCode::ILLEGAL_STATE)
David Brazdilafc9a9e2023-01-12 16:08:10 +0000123 }
124
125 fn allocateGlobalVmContext(
126 &self,
127 requester_debug_pid: i32,
128 ) -> binder::Result<Strong<dyn IGlobalVmContext>> {
129 check_manage_access()?;
130
131 let requester_uid = get_calling_uid();
132 let requester_debug_pid = requester_debug_pid as pid_t;
133 let state = &mut *self.state.lock().unwrap();
Jiyong Park2227eaa2023-08-04 11:59:18 +0900134 state
135 .allocate_vm_context(requester_uid, requester_debug_pid)
136 .or_binder_exception(ExceptionCode::ILLEGAL_STATE)
David Brazdilafc9a9e2023-01-12 16:08:10 +0000137 }
138
139 fn atomVmBooted(&self, atom: &AtomVmBooted) -> Result<(), Status> {
140 forward_vm_booted_atom(atom);
141 Ok(())
142 }
143
144 fn atomVmCreationRequested(&self, atom: &AtomVmCreationRequested) -> Result<(), Status> {
145 forward_vm_creation_atom(atom);
146 Ok(())
147 }
148
149 fn atomVmExited(&self, atom: &AtomVmExited) -> Result<(), Status> {
150 forward_vm_exited_atom(atom);
151 Ok(())
152 }
153
154 fn debugListVms(&self) -> binder::Result<Vec<VirtualMachineDebugInfo>> {
155 check_debug_access()?;
156
157 let state = &mut *self.state.lock().unwrap();
158 let cids = state
159 .held_contexts
160 .iter()
161 .filter_map(|(_, inst)| Weak::upgrade(inst))
162 .map(|vm| VirtualMachineDebugInfo {
163 cid: vm.cid as i32,
164 temporaryDirectory: vm.get_temp_dir().to_string_lossy().to_string(),
165 requesterUid: vm.requester_uid as i32,
Charisee96113f32023-01-26 09:00:42 +0000166 requesterPid: vm.requester_debug_pid,
David Brazdilafc9a9e2023-01-12 16:08:10 +0000167 })
168 .collect();
169 Ok(cids)
170 }
Alice Wangc2fec932023-02-23 16:24:02 +0000171
Alice Wangbff017f2023-11-09 14:43:28 +0000172 fn requestAttestation(
173 &self,
174 csr: &[u8],
175 requester_uid: i32,
176 ) -> binder::Result<Vec<Certificate>> {
Alice Wangc2fec932023-02-23 16:24:02 +0000177 check_manage_access()?;
Alice Wang4c6c5582023-11-23 15:07:18 +0000178 if !cfg!(remote_attestation) {
179 return Err(Status::new_exception_str(
Alice Wange9ac2db2023-09-08 15:13:13 +0000180 ExceptionCode::UNSUPPORTED_OPERATION,
181 Some(
Alice Wanga410b642023-10-18 09:05:15 +0000182 "requestAttestation is not supported with the remote_attestation feature \
183 disabled",
Alice Wange9ac2db2023-09-08 15:13:13 +0000184 ),
185 ))
Alice Wang4c6c5582023-11-23 15:07:18 +0000186 .with_log();
Alice Wange9ac2db2023-09-08 15:13:13 +0000187 }
Alice Wang4c6c5582023-11-23 15:07:18 +0000188 info!("Received csr. Requestting attestation...");
189 let attestation_key = get_rkpd_attestation_key(
190 REMOTELY_PROVISIONED_COMPONENT_SERVICE_NAME,
191 requester_uid as u32,
192 )
193 .context("Failed to retrieve the remotely provisioned keys")
194 .with_log()
195 .or_service_specific_exception(-1)?;
196 let mut certificate_chain = split_x509_certificate_chain(&attestation_key.encodedCertChain)
197 .context("Failed to split the remotely provisioned certificate chain")
198 .with_log()
199 .or_service_specific_exception(-1)?;
200 if certificate_chain.is_empty() {
201 return Err(Status::new_service_specific_error_str(
202 -1,
203 Some("The certificate chain should contain at least 1 certificate"),
204 ))
205 .with_log();
206 }
Alice Wang20b8ebc2023-11-17 09:54:47 +0000207 let certificate = request_attestation(
208 csr.to_vec(),
209 attestation_key.keyBlob,
210 certificate_chain[0].encodedCertificate.clone(),
211 )
212 .context("Failed to request attestation")
213 .with_log()
214 .or_service_specific_exception(-1)?;
Alice Wang4c6c5582023-11-23 15:07:18 +0000215 certificate_chain.insert(0, Certificate { encodedCertificate: certificate });
216
217 Ok(certificate_chain)
Alice Wangc2fec932023-02-23 16:24:02 +0000218 }
Inseob Kim53d0b212023-07-20 16:58:37 +0900219
220 fn getAssignableDevices(&self) -> binder::Result<Vec<AssignableDevice>> {
221 check_use_custom_virtual_machine()?;
222
Inseob Kim7307a892023-09-14 13:37:58 +0900223 Ok(get_assignable_devices()?
224 .device
225 .into_iter()
226 .map(|x| AssignableDevice { node: x.sysfs_path, kind: x.kind })
227 .collect::<Vec<_>>())
Inseob Kim53d0b212023-07-20 16:58:37 +0900228 }
Inseob Kim1ca0f652023-07-20 17:18:12 +0900229
Jakob Vukalovicd42aa2c2023-11-09 16:04:00 +0000230 fn bindDevicesToVfioDriver(
231 &self,
232 devices: &[String],
233 ) -> binder::Result<Vec<Strong<dyn IBoundDevice>>> {
Inseob Kim1ca0f652023-07-20 17:18:12 +0900234 check_use_custom_virtual_machine()?;
235
Jakob Vukalovicd42aa2c2023-11-09 16:04:00 +0000236 let devices = get_assignable_devices()?
Inseob Kim7307a892023-09-14 13:37:58 +0900237 .device
238 .into_iter()
239 .filter_map(|x| {
240 if devices.contains(&x.sysfs_path) {
Jakob Vukalovicd42aa2c2023-11-09 16:04:00 +0000241 Some(VfioDev { sysfsPath: x.sysfs_path, dtboLabel: x.dtbo_label })
Inseob Kim7307a892023-09-14 13:37:58 +0900242 } else {
Jakob Vukalovicd42aa2c2023-11-09 16:04:00 +0000243 warn!("device {} is not assignable", x.sysfs_path);
Inseob Kim7307a892023-09-14 13:37:58 +0900244 None
245 }
246 })
Jakob Vukalovicd42aa2c2023-11-09 16:04:00 +0000247 .collect::<Vec<VfioDev>>();
248
249 VFIO_SERVICE.bindDevicesToVfioDriver(devices.as_slice())
Inseob Kim1ca0f652023-07-20 17:18:12 +0900250 }
David Brazdil2dfefd12023-11-17 14:07:36 +0000251
252 fn getDtboFile(&self) -> binder::Result<ParcelFileDescriptor> {
253 check_use_custom_virtual_machine()?;
254
255 let state = &mut *self.state.lock().unwrap();
256 let file = state.get_dtbo_file().or_service_specific_exception(-1)?;
257 Ok(ParcelFileDescriptor::new(file))
258 }
Inseob Kim1ca0f652023-07-20 17:18:12 +0900259}
260
Inseob Kimc4a774d2023-08-30 12:48:43 +0900261// KEEP IN SYNC WITH assignable_devices.xsd
262#[derive(Debug, Deserialize)]
263struct Device {
264 kind: String,
Jaewan Kim35e818d2023-10-18 05:36:38 +0000265 dtbo_label: String,
Inseob Kimc4a774d2023-08-30 12:48:43 +0900266 sysfs_path: String,
267}
268
Inseob Kim7307a892023-09-14 13:37:58 +0900269#[derive(Debug, Default, Deserialize)]
Inseob Kimc4a774d2023-08-30 12:48:43 +0900270struct Devices {
271 device: Vec<Device>,
272}
273
Inseob Kim7307a892023-09-14 13:37:58 +0900274fn get_assignable_devices() -> binder::Result<Devices> {
275 let xml_path = Path::new("/vendor/etc/avf/assignable_devices.xml");
276 if !xml_path.exists() {
277 return Ok(Devices { ..Default::default() });
278 }
279
280 let xml = fs::read(xml_path)
281 .context("Failed to read assignable_devices.xml")
282 .with_log()
283 .or_service_specific_exception(-1)?;
284
285 let xml = String::from_utf8(xml)
286 .context("assignable_devices.xml is not a valid UTF-8 file")
287 .with_log()
288 .or_service_specific_exception(-1)?;
289
290 let mut devices: Devices = serde_xml_rs::from_str(&xml)
291 .context("can't parse assignable_devices.xml")
292 .with_log()
293 .or_service_specific_exception(-1)?;
294
295 let mut device_set = HashSet::new();
296 devices.device.retain(move |device| {
297 if device_set.contains(&device.sysfs_path) {
298 warn!("duplicated assignable device {device:?}; ignoring...");
299 return false;
300 }
301
302 if !Path::new(&device.sysfs_path).exists() {
303 warn!("assignable device {device:?} doesn't exist; ignoring...");
304 return false;
305 }
306
307 device_set.insert(device.sysfs_path.clone());
308 true
309 });
310 Ok(devices)
311}
312
Alice Wang4c6c5582023-11-23 15:07:18 +0000313fn split_x509_certificate_chain(mut cert_chain: &[u8]) -> Result<Vec<Certificate>> {
314 let mut out = Vec::new();
315 while !cert_chain.is_empty() {
Alice Wangfc5a44a2023-12-21 12:22:40 +0000316 let cert = X509::from_der(cert_chain)?;
317 let end = cert.to_der()?.len();
Alice Wang4c6c5582023-11-23 15:07:18 +0000318 out.push(Certificate { encodedCertificate: cert_chain[..end].to_vec() });
Alice Wangfc5a44a2023-12-21 12:22:40 +0000319 cert_chain = &cert_chain[end..];
Alice Wang4c6c5582023-11-23 15:07:18 +0000320 }
321 Ok(out)
322}
323
David Brazdilafc9a9e2023-01-12 16:08:10 +0000324#[derive(Debug, Default)]
325struct GlobalVmInstance {
326 /// The unique CID assigned to the VM for vsock communication.
327 cid: Cid,
328 /// UID of the client who requested this VM instance.
329 requester_uid: uid_t,
330 /// PID of the client who requested this VM instance.
331 requester_debug_pid: pid_t,
332}
333
334impl GlobalVmInstance {
335 fn get_temp_dir(&self) -> PathBuf {
336 let cid = self.cid;
337 format!("{TEMPORARY_DIRECTORY}/{cid}").into()
338 }
339}
340
341/// The mutable state of the VirtualizationServiceInternal. There should only be one instance
342/// of this struct.
343#[derive(Debug, Default)]
344struct GlobalState {
345 /// VM contexts currently allocated to running VMs. A CID is never recycled as long
346 /// as there is a strong reference held by a GlobalVmContext.
347 held_contexts: HashMap<Cid, Weak<GlobalVmInstance>>,
David Brazdil2dfefd12023-11-17 14:07:36 +0000348
349 /// Cached read-only FD of VM DTBO file. Also serves as a lock for creating the file.
350 dtbo_file: Mutex<Option<File>>,
David Brazdilafc9a9e2023-01-12 16:08:10 +0000351}
352
353impl GlobalState {
354 /// Get the next available CID, or an error if we have run out. The last CID used is stored in
355 /// a system property so that restart of virtualizationservice doesn't reuse CID while the host
356 /// Android is up.
357 fn get_next_available_cid(&mut self) -> Result<Cid> {
358 // Start trying to find a CID from the last used CID + 1. This ensures
359 // that we do not eagerly recycle CIDs. It makes debugging easier but
360 // also means that retrying to allocate a CID, eg. because it is
361 // erroneously occupied by a process, will not recycle the same CID.
362 let last_cid_prop =
363 system_properties::read(SYSPROP_LAST_CID)?.and_then(|val| match val.parse::<Cid>() {
364 Ok(num) => {
365 if is_valid_guest_cid(num) {
366 Some(num)
367 } else {
368 error!("Invalid value '{}' of property '{}'", num, SYSPROP_LAST_CID);
369 None
370 }
371 }
372 Err(_) => {
373 error!("Invalid value '{}' of property '{}'", val, SYSPROP_LAST_CID);
374 None
375 }
376 });
377
378 let first_cid = if let Some(last_cid) = last_cid_prop {
379 if last_cid == GUEST_CID_MAX {
380 GUEST_CID_MIN
381 } else {
382 last_cid + 1
383 }
384 } else {
385 GUEST_CID_MIN
386 };
387
388 let cid = self
389 .find_available_cid(first_cid..=GUEST_CID_MAX)
390 .or_else(|| self.find_available_cid(GUEST_CID_MIN..first_cid))
391 .ok_or_else(|| anyhow!("Could not find an available CID."))?;
392
393 system_properties::write(SYSPROP_LAST_CID, &format!("{}", cid))?;
394 Ok(cid)
395 }
396
397 fn find_available_cid<I>(&self, mut range: I) -> Option<Cid>
398 where
399 I: Iterator<Item = Cid>,
400 {
401 range.find(|cid| !self.held_contexts.contains_key(cid))
402 }
403
404 fn allocate_vm_context(
405 &mut self,
406 requester_uid: uid_t,
407 requester_debug_pid: pid_t,
408 ) -> Result<Strong<dyn IGlobalVmContext>> {
409 // Garbage collect unused VM contexts.
410 self.held_contexts.retain(|_, instance| instance.strong_count() > 0);
411
412 let cid = self.get_next_available_cid()?;
413 let instance = Arc::new(GlobalVmInstance { cid, requester_uid, requester_debug_pid });
David Brazdil2dfefd12023-11-17 14:07:36 +0000414 create_temporary_directory(&instance.get_temp_dir(), Some(requester_uid))?;
David Brazdilafc9a9e2023-01-12 16:08:10 +0000415
416 self.held_contexts.insert(cid, Arc::downgrade(&instance));
417 let binder = GlobalVmContext { instance, ..Default::default() };
418 Ok(BnGlobalVmContext::new_binder(binder, BinderFeatures::default()))
419 }
David Brazdil2dfefd12023-11-17 14:07:36 +0000420
421 fn get_dtbo_file(&mut self) -> Result<File> {
422 let mut file = self.dtbo_file.lock().unwrap();
423
424 let fd = if let Some(ref_fd) = &*file {
425 ref_fd.try_clone()?
426 } else {
427 let path = get_or_create_common_dir()?.join("vm.dtbo");
428 if path.exists() {
429 // All temporary files are deleted when the service is started.
430 // If the file exists but the FD is not cached, the file is
431 // likely corrupted.
432 remove_file(&path).context("Failed to clone cached VM DTBO file descriptor")?;
433 }
434
435 // Open a write-only file descriptor for vfio_handler.
436 let write_fd = File::create(&path).context("Failed to create VM DTBO file")?;
Jakob Vukalovicd42aa2c2023-11-09 16:04:00 +0000437 VFIO_SERVICE.writeVmDtbo(&ParcelFileDescriptor::new(write_fd))?;
David Brazdil2dfefd12023-11-17 14:07:36 +0000438
439 // Open read-only. This FD will be cached and returned to clients.
440 let read_fd = File::open(&path).context("Failed to open VM DTBO file")?;
441 let read_fd_clone =
442 read_fd.try_clone().context("Failed to clone VM DTBO file descriptor")?;
443 *file = Some(read_fd);
444 read_fd_clone
445 };
446
447 Ok(fd)
448 }
David Brazdilafc9a9e2023-01-12 16:08:10 +0000449}
450
David Brazdil2dfefd12023-11-17 14:07:36 +0000451fn create_temporary_directory(path: &PathBuf, requester_uid: Option<uid_t>) -> Result<()> {
452 // Directory may exist if previous attempt to create it had failed.
453 // Delete it before trying again.
David Brazdilafc9a9e2023-01-12 16:08:10 +0000454 if path.as_path().exists() {
455 remove_temporary_dir(path).unwrap_or_else(|e| {
456 warn!("Could not delete temporary directory {:?}: {}", path, e);
457 });
458 }
David Brazdil2dfefd12023-11-17 14:07:36 +0000459 // Create directory.
460 create_dir(path).with_context(|| format!("Could not create temporary directory {:?}", path))?;
461 // If provided, change ownership to client's UID but system's GID, and permissions 0700.
David Brazdilafc9a9e2023-01-12 16:08:10 +0000462 // If the chown() fails, this will leave behind an empty directory that will get removed
463 // at the next attempt, or if virtualizationservice is restarted.
David Brazdil2dfefd12023-11-17 14:07:36 +0000464 if let Some(uid) = requester_uid {
465 chown(path, Some(Uid::from_raw(uid)), None).with_context(|| {
466 format!("Could not set ownership of temporary directory {:?}", path)
467 })?;
468 }
David Brazdilafc9a9e2023-01-12 16:08:10 +0000469 Ok(())
470}
471
472/// Removes a directory owned by a different user by first changing its owner back
473/// to VirtualizationService.
474pub fn remove_temporary_dir(path: &PathBuf) -> Result<()> {
Alice Wangd1b11a02023-04-18 12:30:20 +0000475 ensure!(path.as_path().is_dir(), "Path {:?} is not a directory", path);
David Brazdilafc9a9e2023-01-12 16:08:10 +0000476 chown(path, Some(Uid::current()), None)?;
477 set_permissions(path, Permissions::from_mode(0o700))?;
Alice Wangd1b11a02023-04-18 12:30:20 +0000478 remove_dir_all(path)?;
David Brazdilafc9a9e2023-01-12 16:08:10 +0000479 Ok(())
480}
481
David Brazdil2dfefd12023-11-17 14:07:36 +0000482fn get_or_create_common_dir() -> Result<PathBuf> {
483 let path = Path::new(TEMPORARY_DIRECTORY).join("common");
484 if !path.exists() {
485 create_temporary_directory(&path, None)?;
486 }
487 Ok(path)
488}
489
David Brazdilafc9a9e2023-01-12 16:08:10 +0000490/// Implementation of the AIDL `IGlobalVmContext` interface.
491#[derive(Debug, Default)]
492struct GlobalVmContext {
493 /// Strong reference to the context's instance data structure.
494 instance: Arc<GlobalVmInstance>,
495 /// Keeps our service process running as long as this VM context exists.
496 #[allow(dead_code)]
497 lazy_service_guard: LazyServiceGuard,
498}
499
500impl Interface for GlobalVmContext {}
501
502impl IGlobalVmContext for GlobalVmContext {
503 fn getCid(&self) -> binder::Result<i32> {
504 Ok(self.instance.cid as i32)
505 }
506
507 fn getTemporaryDirectory(&self) -> binder::Result<String> {
508 Ok(self.instance.get_temp_dir().to_string_lossy().to_string())
509 }
510}
511
512fn handle_stream_connection_tombstoned() -> Result<()> {
513 // Should not listen for tombstones on a guest VM's port.
514 assert!(!is_valid_guest_cid(VM_TOMBSTONES_SERVICE_PORT as Cid));
515 let listener =
516 VsockListener::bind_with_cid_port(VMADDR_CID_HOST, VM_TOMBSTONES_SERVICE_PORT as Cid)?;
517 for incoming_stream in listener.incoming() {
518 let mut incoming_stream = match incoming_stream {
519 Err(e) => {
520 warn!("invalid incoming connection: {:?}", e);
521 continue;
522 }
523 Ok(s) => s,
524 };
525 std::thread::spawn(move || {
526 if let Err(e) = handle_tombstone(&mut incoming_stream) {
527 error!("Failed to write tombstone- {:?}", e);
528 }
529 });
530 }
531 Ok(())
532}
533
534fn handle_tombstone(stream: &mut VsockStream) -> Result<()> {
535 if let Ok(addr) = stream.peer_addr() {
536 info!("Vsock Stream connected to cid={} for tombstones", addr.cid());
537 }
538 let tb_connection =
539 TombstonedConnection::connect(std::process::id() as i32, DebuggerdDumpType::Tombstone)
540 .context("Failed to connect to tombstoned")?;
541 let mut text_output = tb_connection
542 .text_output
543 .as_ref()
544 .ok_or_else(|| anyhow!("Could not get file to write the tombstones on"))?;
545 let mut num_bytes_read = 0;
546 loop {
547 let mut chunk_recv = [0; CHUNK_RECV_MAX_LEN];
548 let n = stream
549 .read(&mut chunk_recv)
550 .context("Failed to read tombstone data from Vsock stream")?;
551 if n == 0 {
552 break;
553 }
554 num_bytes_read += n;
555 text_output.write_all(&chunk_recv[0..n]).context("Failed to write guests tombstones")?;
556 }
557 info!("Received {} bytes from guest & wrote to tombstone file", num_bytes_read);
558 tb_connection.notify_completion()?;
559 Ok(())
560}
561
562/// Checks whether the caller has a specific permission
563fn check_permission(perm: &str) -> binder::Result<()> {
564 let calling_pid = get_calling_pid();
565 let calling_uid = get_calling_uid();
566 // Root can do anything
567 if calling_uid == 0 {
568 return Ok(());
569 }
570 let perm_svc: Strong<dyn IPermissionController::IPermissionController> =
571 binder::get_interface("permission")?;
572 if perm_svc.checkPermission(perm, calling_pid, calling_uid as i32)? {
573 Ok(())
574 } else {
Jiyong Park2227eaa2023-08-04 11:59:18 +0900575 Err(anyhow!("does not have the {} permission", perm))
576 .or_binder_exception(ExceptionCode::SECURITY)
David Brazdilafc9a9e2023-01-12 16:08:10 +0000577 }
578}
579
580/// Check whether the caller of the current Binder method is allowed to call debug methods.
581fn check_debug_access() -> binder::Result<()> {
582 check_permission("android.permission.DEBUG_VIRTUAL_MACHINE")
583}
584
585/// Check whether the caller of the current Binder method is allowed to manage VMs
586fn check_manage_access() -> binder::Result<()> {
587 check_permission("android.permission.MANAGE_VIRTUAL_MACHINE")
588}
Inseob Kim53d0b212023-07-20 16:58:37 +0900589
590/// Check whether the caller of the current Binder method is allowed to use custom VMs
591fn check_use_custom_virtual_machine() -> binder::Result<()> {
592 check_permission("android.permission.USE_CUSTOM_VIRTUAL_MACHINE")
593}
Alice Wang4c6c5582023-11-23 15:07:18 +0000594
595#[cfg(test)]
596mod tests {
597 use super::*;
598 use std::fs;
599
600 const TEST_RKP_CERT_CHAIN_PATH: &str = "testdata/rkp_cert_chain.der";
601
602 #[test]
603 fn splitting_x509_certificate_chain_succeeds() -> Result<()> {
604 let bytes = fs::read(TEST_RKP_CERT_CHAIN_PATH)?;
605 let cert_chain = split_x509_certificate_chain(&bytes)?;
606
607 assert_eq!(4, cert_chain.len());
608 for cert in cert_chain {
Alice Wangfc5a44a2023-12-21 12:22:40 +0000609 let x509_cert = X509::from_der(&cert.encodedCertificate)?;
610 assert_eq!(x509_cert.to_der()?.len(), cert.encodedCertificate.len());
Alice Wang4c6c5582023-11-23 15:07:18 +0000611 }
612 Ok(())
613 }
614}