• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // 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 
17 use crate::atom::{forward_vm_booted_atom, forward_vm_creation_atom, forward_vm_exited_atom};
18 use crate::maintenance;
19 use crate::remote_provisioning;
20 use crate::rkpvm::{generate_ecdsa_p256_key_pair, request_attestation};
21 use crate::{get_calling_pid, get_calling_uid, REMOTELY_PROVISIONED_COMPONENT_SERVICE_NAME};
22 use android_os_permissions_aidl::aidl::android::os::IPermissionController;
23 use android_system_virtualizationcommon::aidl::android::system::virtualizationcommon;
24 use android_system_virtualizationmaintenance::aidl::android::system::virtualizationmaintenance;
25 use android_system_virtualizationservice::aidl::android::system::virtualizationservice;
26 use android_system_virtualizationservice_internal as android_vs_internal;
27 use android_system_virtualmachineservice::aidl::android::system::virtualmachineservice;
28 use android_system_vmtethering::aidl::android::system::vmtethering;
29 use android_vs_internal::aidl::android::system::virtualizationservice_internal;
30 use anyhow::{anyhow, ensure, Context, Result};
31 use avflog::LogResult;
32 use binder::{
33     self, wait_for_interface, BinderFeatures, ExceptionCode, Interface, IntoBinderResult,
34     LazyServiceGuard, ParcelFileDescriptor, Status, Strong,
35 };
36 use libc::{VMADDR_CID_HOST, VMADDR_CID_HYPERVISOR, VMADDR_CID_LOCAL};
37 use log::{error, info, warn};
38 use nix::unistd::{chown, Uid};
39 use openssl::x509::X509;
40 use rand::Fill;
41 use rkpd_client::get_rkpd_attestation_key;
42 use rustutils::{
43     system_properties,
44     users::{multiuser_get_app_id, multiuser_get_user_id},
45 };
46 use serde::Deserialize;
47 use service_vm_comm::Response;
48 use std::collections::{HashMap, HashSet};
49 use std::fs::{self, create_dir, remove_dir_all, remove_file, set_permissions, File, Permissions};
50 use std::io::{Read, Write};
51 use std::os::unix::fs::PermissionsExt;
52 use std::os::unix::raw::{pid_t, uid_t};
53 use std::path::{Path, PathBuf};
54 use std::sync::{Arc, Condvar, LazyLock, Mutex, Weak};
55 use tombstoned_client::{DebuggerdDumpType, TombstonedConnection};
56 use virtualizationcommon::Certificate::Certificate;
57 use virtualizationmaintenance::{
58     IVirtualizationMaintenance::IVirtualizationMaintenance,
59     IVirtualizationReconciliationCallback::IVirtualizationReconciliationCallback,
60 };
61 use virtualizationservice::{
62     AssignableDevice::AssignableDevice, VirtualMachineDebugInfo::VirtualMachineDebugInfo,
63 };
64 use virtualizationservice_internal::{
65     AtomVmBooted::AtomVmBooted,
66     AtomVmCreationRequested::AtomVmCreationRequested,
67     AtomVmExited::AtomVmExited,
68     IBoundDevice::IBoundDevice,
69     IGlobalVmContext::{BnGlobalVmContext, IGlobalVmContext},
70     IVfioHandler::VfioDev::VfioDev,
71     IVfioHandler::{BpVfioHandler, IVfioHandler},
72     IVirtualizationServiceInternal::IVirtualizationServiceInternal,
73     IVmnic::{BpVmnic, IVmnic},
74 };
75 use virtualmachineservice::IVirtualMachineService::VM_TOMBSTONES_SERVICE_PORT;
76 use vmtethering::IVmTethering::{BpVmTethering, IVmTethering};
77 use vsock::{VsockListener, VsockStream};
78 
79 /// The unique ID of a VM used (together with a port number) for vsock communication.
80 pub type Cid = u32;
81 
82 /// Directory in which to write disk image files used while running VMs.
83 pub const TEMPORARY_DIRECTORY: &str = "/data/misc/virtualizationservice";
84 
85 /// The first CID to assign to a guest VM managed by the VirtualizationService. CIDs lower than this
86 /// are reserved for the host or other usage.
87 const GUEST_CID_MIN: Cid = 2048;
88 const GUEST_CID_MAX: Cid = 65535;
89 
90 const SYSPROP_LAST_CID: &str = "virtualizationservice.state.last_cid";
91 
92 const CHUNK_RECV_MAX_LEN: usize = 1024;
93 
94 /// The fake certificate is used for testing only when a client VM requests attestation in test
95 /// mode, it is a single certificate extracted on an unregistered device for testing.
96 /// Here is the snapshot of the certificate:
97 ///
98 /// ```
99 /// Certificate:
100 /// Data:
101 /// Version: 3 (0x2)
102 /// Serial Number:
103 ///     59:ae:50:98:95:e1:34:25:f1:21:93:c0:4c:e5:24:66
104 /// Signature Algorithm: ecdsa-with-SHA256
105 /// Issuer: CN = Droid Unregistered Device CA, O = Google Test LLC
106 /// Validity
107 ///     Not Before: Feb  5 14:39:39 2024 GMT
108 ///     Not After : Feb 14 14:39:39 2024 GMT
109 /// Subject: CN = 59ae509895e13425f12193c04ce52466, O = TEE
110 /// Subject Public Key Info:
111 ///     Public Key Algorithm: id-ecPublicKey
112 ///         Public-Key: (256 bit)
113 ///         pub:
114 ///             04:30:32:cd:95:12:b0:71:8b:b7:14:44:26:58:d5:
115 ///             82:8c:25:55:2c:6d:ef:98:e3:4f:88:d0:74:82:09:
116 ///             3e:8d:6c:f0:f2:18:d5:83:0e:0d:f2:ce:c5:15:38:
117 ///             e5:6a:e6:4d:4d:95:15:b7:24:e7:cb:4b:63:42:21:
118 ///             bc:36:c6:0a:d8
119 ///         ASN1 OID: prime256v1
120 ///         NIST CURVE: P-256
121 /// X509v3 extensions:
122 ///  ...
123 /// ```
124 const FAKE_CERTIFICATE_FOR_TESTING: &[u8] = &[
125     0x30, 0x82, 0x01, 0xee, 0x30, 0x82, 0x01, 0x94, 0xa0, 0x03, 0x02, 0x01, 0x02, 0x02, 0x10, 0x59,
126     0xae, 0x50, 0x98, 0x95, 0xe1, 0x34, 0x25, 0xf1, 0x21, 0x93, 0xc0, 0x4c, 0xe5, 0x24, 0x66, 0x30,
127     0x0a, 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x04, 0x03, 0x02, 0x30, 0x41, 0x31, 0x25, 0x30,
128     0x23, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x1c, 0x44, 0x72, 0x6f, 0x69, 0x64, 0x20, 0x55, 0x6e,
129     0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x65, 0x64, 0x20, 0x44, 0x65, 0x76, 0x69, 0x63,
130     0x65, 0x20, 0x43, 0x41, 0x31, 0x18, 0x30, 0x16, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0f, 0x47,
131     0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x20, 0x54, 0x65, 0x73, 0x74, 0x20, 0x4c, 0x4c, 0x43, 0x30, 0x1e,
132     0x17, 0x0d, 0x32, 0x34, 0x30, 0x32, 0x30, 0x35, 0x31, 0x34, 0x33, 0x39, 0x33, 0x39, 0x5a, 0x17,
133     0x0d, 0x32, 0x34, 0x30, 0x32, 0x31, 0x34, 0x31, 0x34, 0x33, 0x39, 0x33, 0x39, 0x5a, 0x30, 0x39,
134     0x31, 0x29, 0x30, 0x27, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x20, 0x35, 0x39, 0x61, 0x65, 0x35,
135     0x30, 0x39, 0x38, 0x39, 0x35, 0x65, 0x31, 0x33, 0x34, 0x32, 0x35, 0x66, 0x31, 0x32, 0x31, 0x39,
136     0x33, 0x63, 0x30, 0x34, 0x63, 0x65, 0x35, 0x32, 0x34, 0x36, 0x36, 0x31, 0x0c, 0x30, 0x0a, 0x06,
137     0x03, 0x55, 0x04, 0x0a, 0x13, 0x03, 0x54, 0x45, 0x45, 0x30, 0x59, 0x30, 0x13, 0x06, 0x07, 0x2a,
138     0x86, 0x48, 0xce, 0x3d, 0x02, 0x01, 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x03, 0x01, 0x07,
139     0x03, 0x42, 0x00, 0x04, 0x30, 0x32, 0xcd, 0x95, 0x12, 0xb0, 0x71, 0x8b, 0xb7, 0x14, 0x44, 0x26,
140     0x58, 0xd5, 0x82, 0x8c, 0x25, 0x55, 0x2c, 0x6d, 0xef, 0x98, 0xe3, 0x4f, 0x88, 0xd0, 0x74, 0x82,
141     0x09, 0x3e, 0x8d, 0x6c, 0xf0, 0xf2, 0x18, 0xd5, 0x83, 0x0e, 0x0d, 0xf2, 0xce, 0xc5, 0x15, 0x38,
142     0xe5, 0x6a, 0xe6, 0x4d, 0x4d, 0x95, 0x15, 0xb7, 0x24, 0xe7, 0xcb, 0x4b, 0x63, 0x42, 0x21, 0xbc,
143     0x36, 0xc6, 0x0a, 0xd8, 0xa3, 0x76, 0x30, 0x74, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04,
144     0x16, 0x04, 0x14, 0x39, 0x81, 0x41, 0x0a, 0xb9, 0xf3, 0xf4, 0x5b, 0x75, 0x97, 0x4a, 0x46, 0xd6,
145     0x30, 0x9e, 0x1d, 0x7a, 0x3b, 0xec, 0xa8, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18,
146     0x30, 0x16, 0x80, 0x14, 0x82, 0xbd, 0x00, 0xde, 0xcb, 0xc5, 0xe7, 0x72, 0x87, 0x3d, 0x1c, 0x0a,
147     0x1e, 0x78, 0x4f, 0xf5, 0xd3, 0xc1, 0x3e, 0xb8, 0x30, 0x0f, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x01,
148     0x01, 0xff, 0x04, 0x05, 0x30, 0x03, 0x01, 0x01, 0xff, 0x30, 0x0e, 0x06, 0x03, 0x55, 0x1d, 0x0f,
149     0x01, 0x01, 0xff, 0x04, 0x04, 0x03, 0x02, 0x02, 0x04, 0x30, 0x11, 0x06, 0x0a, 0x2b, 0x06, 0x01,
150     0x04, 0x01, 0xd6, 0x79, 0x02, 0x01, 0x1e, 0x04, 0x03, 0xa1, 0x01, 0x08, 0x30, 0x0a, 0x06, 0x08,
151     0x2a, 0x86, 0x48, 0xce, 0x3d, 0x04, 0x03, 0x02, 0x03, 0x48, 0x00, 0x30, 0x45, 0x02, 0x21, 0x00,
152     0xae, 0xd8, 0x40, 0x9e, 0x37, 0x3e, 0x5c, 0x9c, 0xe2, 0x93, 0x3d, 0x8c, 0xf7, 0x05, 0x10, 0xe7,
153     0xd1, 0x2b, 0x87, 0x8a, 0xee, 0xd6, 0x1e, 0x6c, 0x3b, 0xd2, 0x91, 0x3e, 0xa5, 0xdf, 0x91, 0x20,
154     0x02, 0x20, 0x7f, 0x0f, 0x29, 0x54, 0x60, 0x80, 0x07, 0x50, 0x5f, 0x56, 0x6b, 0x9f, 0xe0, 0x94,
155     0xb4, 0x3f, 0x3b, 0x0f, 0x61, 0xa0, 0x33, 0x40, 0xe6, 0x1a, 0x42, 0xda, 0x4b, 0xa4, 0xfd, 0x92,
156     0xb9, 0x0f,
157 ];
158 
159 static FAKE_PROVISIONED_KEY_BLOB_FOR_TESTING: Mutex<Option<Vec<u8>>> = Mutex::new(None);
160 static VFIO_SERVICE: LazyLock<Strong<dyn IVfioHandler>> = LazyLock::new(|| {
161     wait_for_interface(<BpVfioHandler as IVfioHandler>::get_descriptor())
162         .expect("Could not connect to VfioHandler")
163 });
164 static NETWORK_SERVICE: LazyLock<Strong<dyn IVmnic>> = LazyLock::new(|| {
165     wait_for_interface(<BpVmnic as IVmnic>::get_descriptor()).expect("Could not connect to Vmnic")
166 });
167 static TETHERING_SERVICE: LazyLock<Strong<dyn IVmTethering>> = LazyLock::new(|| {
168     wait_for_interface(<BpVmTethering as IVmTethering>::get_descriptor())
169         .expect("Could not connect to VmTethering")
170 });
171 
is_valid_guest_cid(cid: Cid) -> bool172 fn is_valid_guest_cid(cid: Cid) -> bool {
173     (GUEST_CID_MIN..=GUEST_CID_MAX).contains(&cid)
174 }
175 
176 /// Singleton service for allocating globally-unique VM resources, such as the CID, and running
177 /// singleton servers, like tombstone receiver.
178 #[derive(Clone)]
179 pub struct VirtualizationServiceInternal {
180     state: Arc<Mutex<GlobalState>>,
181     display_service_set: Arc<Condvar>,
182 }
183 
184 impl VirtualizationServiceInternal {
init() -> VirtualizationServiceInternal185     pub fn init() -> VirtualizationServiceInternal {
186         let service = VirtualizationServiceInternal {
187             state: Arc::new(Mutex::new(GlobalState::new())),
188             display_service_set: Arc::new(Condvar::new()),
189         };
190 
191         std::thread::spawn(|| {
192             if let Err(e) = handle_stream_connection_tombstoned() {
193                 warn!("Error receiving tombstone from guest or writing them. Error: {:?}", e);
194             }
195         });
196 
197         service
198     }
199 
200     // Attempt to update the sk_state maintenance database. Errors are ignored - calling app
201     // can not really do much to fix the errors & letting AVF VMs run irrespective of such internal
202     // error is acceptable.
try_updating_sk_state(&self, id: &[u8; 64])203     fn try_updating_sk_state(&self, id: &[u8; 64]) {
204         let state = &mut *self.state.lock().unwrap();
205         if let Some(sk_state) = &mut state.sk_state {
206             let uid = get_calling_uid();
207             let user_id = multiuser_get_user_id(uid);
208             let app_id = multiuser_get_app_id(uid);
209             info!(
210                 "Recording possible new owner of Secretkeeper entry={:?}:
211                  (user_id={user_id}, app_id={app_id},)",
212                 hex::encode(id)
213             );
214             if let Err(e) = sk_state.add_id(id, user_id, app_id) {
215                 error!("Failed to update the Secretkeeper entry owner: {e:?}");
216             }
217         } else {
218             info!("ignoring update of Secretkeeper entry as no ISecretkeeper");
219         }
220     }
221 }
222 
223 impl Interface for VirtualizationServiceInternal {}
224 
225 impl IVirtualizationServiceInternal for VirtualizationServiceInternal {
setDisplayService( &self, ibinder: &binder::SpIBinder, ) -> std::result::Result<(), binder::Status>226     fn setDisplayService(
227         &self,
228         ibinder: &binder::SpIBinder,
229     ) -> std::result::Result<(), binder::Status> {
230         check_manage_access()?;
231         check_use_custom_virtual_machine()?;
232         let state = &mut *self.state.lock().unwrap();
233         state.display_service = Some(ibinder.clone());
234         self.display_service_set.notify_all();
235         Ok(())
236     }
237 
clearDisplayService(&self) -> std::result::Result<(), binder::Status>238     fn clearDisplayService(&self) -> std::result::Result<(), binder::Status> {
239         check_manage_access()?;
240         check_use_custom_virtual_machine()?;
241         let state = &mut *self.state.lock().unwrap();
242         state.display_service = None;
243         self.display_service_set.notify_all();
244         Ok(())
245     }
246 
waitDisplayService(&self) -> std::result::Result<binder::SpIBinder, binder::Status>247     fn waitDisplayService(&self) -> std::result::Result<binder::SpIBinder, binder::Status> {
248         check_manage_access()?;
249         check_use_custom_virtual_machine()?;
250         let state = self
251             .display_service_set
252             .wait_while(self.state.lock().unwrap(), |state| state.display_service.is_none())
253             .unwrap();
254         Ok((state.display_service)
255             .as_ref()
256             .cloned()
257             .expect("Display service cannot be None in this context"))
258     }
removeMemlockRlimit(&self) -> binder::Result<()>259     fn removeMemlockRlimit(&self) -> binder::Result<()> {
260         let pid = get_calling_pid();
261         let lim = libc::rlimit { rlim_cur: libc::RLIM_INFINITY, rlim_max: libc::RLIM_INFINITY };
262 
263         // SAFETY: borrowing the new limit struct only
264         let ret = unsafe { libc::prlimit(pid, libc::RLIMIT_MEMLOCK, &lim, std::ptr::null_mut()) };
265 
266         match ret {
267             0 => Ok(()),
268             -1 => Err(std::io::Error::last_os_error().into()),
269             n => Err(anyhow!("Unexpected return value from prlimit(): {n}")),
270         }
271         .or_binder_exception(ExceptionCode::ILLEGAL_STATE)
272     }
273 
allocateGlobalVmContext( &self, name: &str, requester_debug_pid: i32, ) -> binder::Result<Strong<dyn IGlobalVmContext>>274     fn allocateGlobalVmContext(
275         &self,
276         name: &str,
277         requester_debug_pid: i32,
278     ) -> binder::Result<Strong<dyn IGlobalVmContext>> {
279         check_manage_access()?;
280 
281         let requester_uid = get_calling_uid();
282         let requester_debug_pid = requester_debug_pid as pid_t;
283         let state = &mut *self.state.lock().unwrap();
284         state
285             .allocate_vm_context(name, requester_uid, requester_debug_pid)
286             .or_binder_exception(ExceptionCode::ILLEGAL_STATE)
287     }
288 
atomVmBooted(&self, atom: &AtomVmBooted) -> Result<(), Status>289     fn atomVmBooted(&self, atom: &AtomVmBooted) -> Result<(), Status> {
290         forward_vm_booted_atom(atom);
291         Ok(())
292     }
293 
atomVmCreationRequested(&self, atom: &AtomVmCreationRequested) -> Result<(), Status>294     fn atomVmCreationRequested(&self, atom: &AtomVmCreationRequested) -> Result<(), Status> {
295         forward_vm_creation_atom(atom);
296         Ok(())
297     }
298 
atomVmExited(&self, atom: &AtomVmExited) -> Result<(), Status>299     fn atomVmExited(&self, atom: &AtomVmExited) -> Result<(), Status> {
300         forward_vm_exited_atom(atom);
301         Ok(())
302     }
303 
debugListVms(&self) -> binder::Result<Vec<VirtualMachineDebugInfo>>304     fn debugListVms(&self) -> binder::Result<Vec<VirtualMachineDebugInfo>> {
305         check_debug_access()?;
306 
307         let state = &mut *self.state.lock().unwrap();
308         let cids = state
309             .held_contexts
310             .iter()
311             .filter_map(|(_, inst)| Weak::upgrade(inst))
312             .map(|vm| {
313                 let vm = vm.lock().unwrap();
314                 VirtualMachineDebugInfo {
315                     name: vm.name.clone(),
316                     cid: vm.cid as i32,
317                     temporaryDirectory: vm.get_temp_dir().to_string_lossy().to_string(),
318                     requesterUid: vm.requester_uid as i32,
319                     requesterPid: vm.requester_debug_pid,
320                     hostConsoleName: vm.host_console_name.clone(),
321                 }
322             })
323             .collect();
324         Ok(cids)
325     }
326 
enableTestAttestation(&self) -> binder::Result<()>327     fn enableTestAttestation(&self) -> binder::Result<()> {
328         check_manage_access()?;
329         check_use_custom_virtual_machine()?;
330         if !cfg!(remote_attestation) {
331             return Err(Status::new_exception_str(
332                 ExceptionCode::UNSUPPORTED_OPERATION,
333                 Some(
334                     "enableTestAttestation is not supported with the remote_attestation \
335                      feature disabled",
336                 ),
337             ))
338             .with_log();
339         }
340         let res = generate_ecdsa_p256_key_pair()
341             .context("Failed to generate ECDSA P-256 key pair for testing")
342             .with_log()
343             .or_service_specific_exception(-1)?;
344         // Wait until the service VM shuts down, so that the Service VM will be restarted when
345         // the key generated in the current session will be used for attestation.
346         // This ensures that different Service VM sessions have the same KEK for the key blob.
347         service_vm_manager::wait_until_service_vm_shuts_down()
348             .context("Failed to wait until the service VM shuts down")
349             .with_log()
350             .or_service_specific_exception(-1)?;
351         match res {
352             Response::GenerateEcdsaP256KeyPair(key_pair) => {
353                 FAKE_PROVISIONED_KEY_BLOB_FOR_TESTING
354                     .lock()
355                     .unwrap()
356                     .replace(key_pair.key_blob.to_vec());
357                 Ok(())
358             }
359             _ => Err(remote_provisioning::to_service_specific_error(res)),
360         }
361         .with_log()
362     }
363 
requestAttestation( &self, csr: &[u8], requester_uid: i32, test_mode: bool, ) -> binder::Result<Vec<Certificate>>364     fn requestAttestation(
365         &self,
366         csr: &[u8],
367         requester_uid: i32,
368         test_mode: bool,
369     ) -> binder::Result<Vec<Certificate>> {
370         check_manage_access()?;
371         if !cfg!(remote_attestation) {
372             return Err(Status::new_exception_str(
373                 ExceptionCode::UNSUPPORTED_OPERATION,
374                 Some(
375                     "requestAttestation is not supported with the remote_attestation feature \
376                      disabled",
377                 ),
378             ))
379             .with_log();
380         }
381         if !is_remote_provisioning_hal_declared()? {
382             return Err(Status::new_exception_str(
383                 ExceptionCode::UNSUPPORTED_OPERATION,
384                 Some("AVF remotely provisioned component service is not declared"),
385             ))
386             .with_log();
387         }
388         remote_provisioning::check_remote_attestation_is_supported()?;
389         info!("Received csr. Requestting attestation...");
390         let (key_blob, certificate_chain) = if test_mode {
391             check_use_custom_virtual_machine()?;
392             info!("Using the fake key blob for testing...");
393             (
394                 FAKE_PROVISIONED_KEY_BLOB_FOR_TESTING
395                     .lock()
396                     .unwrap()
397                     .clone()
398                     .ok_or_else(|| anyhow!("No key blob for testing"))
399                     .with_log()
400                     .or_service_specific_exception(-1)?,
401                 FAKE_CERTIFICATE_FOR_TESTING.to_vec(),
402             )
403         } else {
404             info!("Retrieving the remotely provisioned keys from RKPD...");
405             let attestation_key = get_rkpd_attestation_key(
406                 REMOTELY_PROVISIONED_COMPONENT_SERVICE_NAME,
407                 requester_uid as u32,
408             )
409             .context("Failed to retrieve the remotely provisioned keys")
410             .with_log()
411             .or_service_specific_exception(-1)?;
412             (attestation_key.keyBlob, attestation_key.encodedCertChain)
413         };
414         let mut certificate_chain = split_x509_certificate_chain(&certificate_chain)
415             .context("Failed to split the remotely provisioned certificate chain")
416             .with_log()
417             .or_service_specific_exception(-1)?;
418         if certificate_chain.is_empty() {
419             return Err(Status::new_service_specific_error_str(
420                 -1,
421                 Some("The certificate chain should contain at least 1 certificate"),
422             ))
423             .with_log();
424         }
425         let certificate = request_attestation(
426             csr.to_vec(),
427             key_blob,
428             certificate_chain[0].encodedCertificate.clone(),
429         )
430         .context("Failed to request attestation")
431         .with_log()
432         .or_service_specific_exception(-1)?;
433         certificate_chain.insert(0, Certificate { encodedCertificate: certificate });
434 
435         Ok(certificate_chain)
436     }
437 
isRemoteAttestationSupported(&self) -> binder::Result<bool>438     fn isRemoteAttestationSupported(&self) -> binder::Result<bool> {
439         if is_remote_provisioning_hal_declared()? {
440             Ok(remote_provisioning::is_remote_attestation_supported())
441         } else {
442             warn!("AVF IRemotelyProvisionedComponent HAL is not declared");
443             Ok(false)
444         }
445     }
446 
getAssignableDevices(&self) -> binder::Result<Vec<AssignableDevice>>447     fn getAssignableDevices(&self) -> binder::Result<Vec<AssignableDevice>> {
448         check_use_custom_virtual_machine()?;
449 
450         Ok(get_assignable_devices()?
451             .device
452             .into_iter()
453             .map(|x| AssignableDevice { node: x.sysfs_path, dtbo_label: x.dtbo_label })
454             .collect::<Vec<_>>())
455     }
456 
bindDevicesToVfioDriver( &self, devices: &[String], ) -> binder::Result<Vec<Strong<dyn IBoundDevice>>>457     fn bindDevicesToVfioDriver(
458         &self,
459         devices: &[String],
460     ) -> binder::Result<Vec<Strong<dyn IBoundDevice>>> {
461         check_use_custom_virtual_machine()?;
462 
463         let devices = get_assignable_devices()?
464             .device
465             .into_iter()
466             .filter_map(|x| {
467                 if devices.contains(&x.sysfs_path) {
468                     Some(VfioDev { sysfsPath: x.sysfs_path, dtboLabel: x.dtbo_label })
469                 } else {
470                     warn!("device {} is not assignable", x.sysfs_path);
471                     None
472                 }
473             })
474             .collect::<Vec<VfioDev>>();
475 
476         VFIO_SERVICE.bindDevicesToVfioDriver(devices.as_slice())
477     }
478 
getDtboFile(&self) -> binder::Result<ParcelFileDescriptor>479     fn getDtboFile(&self) -> binder::Result<ParcelFileDescriptor> {
480         check_use_custom_virtual_machine()?;
481 
482         let state = &mut *self.state.lock().unwrap();
483         let file = state.get_dtbo_file().or_service_specific_exception(-1)?;
484         Ok(ParcelFileDescriptor::new(file))
485     }
486 
allocateInstanceId(&self) -> binder::Result<[u8; 64]>487     fn allocateInstanceId(&self) -> binder::Result<[u8; 64]> {
488         let mut id = [0u8; 64];
489         id.try_fill(&mut rand::thread_rng())
490             .context("Failed to allocate instance_id")
491             .or_service_specific_exception(-1)?;
492         // Randomly allocated IDs always start with all 7s to avoid colliding with statically
493         // assigned IDs.
494         id[..4].fill(0x77);
495         let uid = get_calling_uid();
496         info!("Allocated a VM's instance_id: {:?}..., for uid: {:?}", &hex::encode(id)[..8], uid);
497         self.try_updating_sk_state(&id);
498         Ok(id)
499     }
500 
removeVmInstance(&self, instance_id: &[u8; 64]) -> binder::Result<()>501     fn removeVmInstance(&self, instance_id: &[u8; 64]) -> binder::Result<()> {
502         let state = &mut *self.state.lock().unwrap();
503         if let Some(sk_state) = &mut state.sk_state {
504             let uid = get_calling_uid();
505             info!(
506                 "Removing a VM's instance_id: {:?}, for uid: {:?}",
507                 hex::encode(instance_id),
508                 uid
509             );
510 
511             let user_id = multiuser_get_user_id(uid);
512             let app_id = multiuser_get_app_id(uid);
513             sk_state.delete_id(instance_id, user_id, app_id);
514         } else {
515             info!("ignoring removeVmInstance() as no ISecretkeeper");
516         }
517         Ok(())
518     }
519 
claimVmInstance(&self, instance_id: &[u8; 64]) -> binder::Result<()>520     fn claimVmInstance(&self, instance_id: &[u8; 64]) -> binder::Result<()> {
521         info!("Claiming a VM's instance_id: {:?}", hex::encode(instance_id));
522         self.try_updating_sk_state(instance_id);
523         Ok(())
524     }
525 
createTapInterface(&self, _iface_name_suffix: &str) -> binder::Result<ParcelFileDescriptor>526     fn createTapInterface(&self, _iface_name_suffix: &str) -> binder::Result<ParcelFileDescriptor> {
527         check_internet_permission()?;
528         check_use_custom_virtual_machine()?;
529         if !cfg!(network) {
530             return Err(Status::new_exception_str(
531                 ExceptionCode::UNSUPPORTED_OPERATION,
532                 Some("createTapInterface is not supported with the network feature disabled"),
533             ))
534             .with_log();
535         }
536         // TODO(340377643): Use iface_name_suffix after introducing bridge interface, not fixed
537         // value.
538         let tap_fd = NETWORK_SERVICE.createTapInterface("fixed")?;
539 
540         // TODO(340377643): Due to lack of implementation of creating bridge interface, tethering is
541         // enabled for TAP interface instead of bridge interface. After introducing creation of
542         // bridge interface in AVF, we should modify it.
543         TETHERING_SERVICE.enableVmTethering()?;
544 
545         Ok(tap_fd)
546     }
547 
deleteTapInterface(&self, tap_fd: &ParcelFileDescriptor) -> binder::Result<()>548     fn deleteTapInterface(&self, tap_fd: &ParcelFileDescriptor) -> binder::Result<()> {
549         check_internet_permission()?;
550         check_use_custom_virtual_machine()?;
551         if !cfg!(network) {
552             return Err(Status::new_exception_str(
553                 ExceptionCode::UNSUPPORTED_OPERATION,
554                 Some("deleteTapInterface is not supported with the network feature disabled"),
555             ))
556             .with_log();
557         }
558 
559         // TODO(340377643): Disabling tethering should be for bridge interface, not TAP interface.
560         TETHERING_SERVICE.disableVmTethering()?;
561 
562         NETWORK_SERVICE.deleteTapInterface(tap_fd)
563     }
564 
claimSecretkeeperEntry(&self, id: &[u8; 64]) -> binder::Result<()>565     fn claimSecretkeeperEntry(&self, id: &[u8; 64]) -> binder::Result<()> {
566         info!("Claiming Secretkeeper entry: {:?}", hex::encode(id));
567         self.try_updating_sk_state(id);
568         Ok(())
569     }
570 }
571 
572 impl IVirtualizationMaintenance for VirtualizationServiceInternal {
appRemoved(&self, user_id: i32, app_id: i32) -> binder::Result<()>573     fn appRemoved(&self, user_id: i32, app_id: i32) -> binder::Result<()> {
574         let state = &mut *self.state.lock().unwrap();
575         if let Some(sk_state) = &mut state.sk_state {
576             info!("packageRemoved(user_id={user_id}, app_id={app_id})");
577             sk_state.delete_ids_for_app(user_id, app_id).or_service_specific_exception(-1)?;
578         } else {
579             info!("ignoring packageRemoved(user_id={user_id}, app_id={app_id})");
580         }
581         Ok(())
582     }
583 
userRemoved(&self, user_id: i32) -> binder::Result<()>584     fn userRemoved(&self, user_id: i32) -> binder::Result<()> {
585         let state = &mut *self.state.lock().unwrap();
586         if let Some(sk_state) = &mut state.sk_state {
587             info!("userRemoved({user_id})");
588             sk_state.delete_ids_for_user(user_id).or_service_specific_exception(-1)?;
589         } else {
590             info!("ignoring userRemoved(user_id={user_id})");
591         }
592         Ok(())
593     }
594 
performReconciliation( &self, callback: &Strong<dyn IVirtualizationReconciliationCallback>, ) -> binder::Result<()>595     fn performReconciliation(
596         &self,
597         callback: &Strong<dyn IVirtualizationReconciliationCallback>,
598     ) -> binder::Result<()> {
599         let state = &mut *self.state.lock().unwrap();
600         if let Some(sk_state) = &mut state.sk_state {
601             info!("performReconciliation()");
602             sk_state.reconcile(callback).or_service_specific_exception(-1)?;
603         } else {
604             info!("ignoring performReconciliation()");
605         }
606         Ok(())
607     }
608 }
609 
610 #[derive(Debug, Deserialize)]
611 struct Device {
612     dtbo_label: String,
613     sysfs_path: String,
614 }
615 
616 #[derive(Debug, Default, Deserialize)]
617 struct Devices {
618     device: Vec<Device>,
619 }
620 
get_assignable_devices() -> binder::Result<Devices>621 fn get_assignable_devices() -> binder::Result<Devices> {
622     let xml_path = Path::new("/vendor/etc/avf/assignable_devices.xml");
623     if !xml_path.exists() {
624         return Ok(Devices { ..Default::default() });
625     }
626 
627     let xml = fs::read(xml_path)
628         .context("Failed to read assignable_devices.xml")
629         .with_log()
630         .or_service_specific_exception(-1)?;
631 
632     let xml = String::from_utf8(xml)
633         .context("assignable_devices.xml is not a valid UTF-8 file")
634         .with_log()
635         .or_service_specific_exception(-1)?;
636 
637     let mut devices: Devices = serde_xml_rs::from_str(&xml)
638         .context("can't parse assignable_devices.xml")
639         .with_log()
640         .or_service_specific_exception(-1)?;
641 
642     let mut device_set = HashSet::new();
643     devices.device.retain(move |device| {
644         if device_set.contains(&device.sysfs_path) {
645             warn!("duplicated assignable device {device:?}; ignoring...");
646             return false;
647         }
648 
649         if !Path::new(&device.sysfs_path).exists() {
650             warn!("assignable device {device:?} doesn't exist; ignoring...");
651             return false;
652         }
653 
654         device_set.insert(device.sysfs_path.clone());
655         true
656     });
657     Ok(devices)
658 }
659 
split_x509_certificate_chain(mut cert_chain: &[u8]) -> Result<Vec<Certificate>>660 fn split_x509_certificate_chain(mut cert_chain: &[u8]) -> Result<Vec<Certificate>> {
661     let mut out = Vec::new();
662     while !cert_chain.is_empty() {
663         let cert = X509::from_der(cert_chain)?;
664         let end = cert.to_der()?.len();
665         out.push(Certificate { encodedCertificate: cert_chain[..end].to_vec() });
666         cert_chain = &cert_chain[end..];
667     }
668     Ok(out)
669 }
670 
671 #[derive(Debug, Default)]
672 struct GlobalVmInstance {
673     /// Name of the VM
674     name: String,
675     /// The unique CID assigned to the VM for vsock communication.
676     cid: Cid,
677     /// UID of the client who requested this VM instance.
678     requester_uid: uid_t,
679     /// PID of the client who requested this VM instance.
680     requester_debug_pid: pid_t,
681     /// Name of the host console.
682     host_console_name: Option<String>,
683 }
684 
685 impl GlobalVmInstance {
get_temp_dir(&self) -> PathBuf686     fn get_temp_dir(&self) -> PathBuf {
687         let cid = self.cid;
688         format!("{TEMPORARY_DIRECTORY}/{cid}").into()
689     }
690 }
691 
692 /// The mutable state of the VirtualizationServiceInternal. There should only be one instance
693 /// of this struct.
694 struct GlobalState {
695     /// VM contexts currently allocated to running VMs. A CID is never recycled as long
696     /// as there is a strong reference held by a GlobalVmContext.
697     held_contexts: HashMap<Cid, Weak<Mutex<GlobalVmInstance>>>,
698 
699     /// Cached read-only FD of VM DTBO file. Also serves as a lock for creating the file.
700     dtbo_file: Mutex<Option<File>>,
701 
702     /// State relating to secrets held by (optional) Secretkeeper instance on behalf of VMs.
703     sk_state: Option<maintenance::State>,
704 
705     display_service: Option<binder::SpIBinder>,
706 }
707 
708 impl GlobalState {
new() -> Self709     fn new() -> Self {
710         Self {
711             held_contexts: HashMap::new(),
712             dtbo_file: Mutex::new(None),
713             sk_state: maintenance::State::new(),
714             display_service: None,
715         }
716     }
717 
718     /// Get the next available CID, or an error if we have run out. The last CID used is stored in
719     /// a system property so that restart of virtualizationservice doesn't reuse CID while the host
720     /// Android is up.
get_next_available_cid(&mut self) -> Result<Cid>721     fn get_next_available_cid(&mut self) -> Result<Cid> {
722         // Start trying to find a CID from the last used CID + 1. This ensures
723         // that we do not eagerly recycle CIDs. It makes debugging easier but
724         // also means that retrying to allocate a CID, eg. because it is
725         // erroneously occupied by a process, will not recycle the same CID.
726         let last_cid_prop =
727             system_properties::read(SYSPROP_LAST_CID)?.and_then(|val| match val.parse::<Cid>() {
728                 Ok(num) => {
729                     if is_valid_guest_cid(num) {
730                         Some(num)
731                     } else {
732                         error!("Invalid value '{}' of property '{}'", num, SYSPROP_LAST_CID);
733                         None
734                     }
735                 }
736                 Err(_) => {
737                     error!("Invalid value '{}' of property '{}'", val, SYSPROP_LAST_CID);
738                     None
739                 }
740             });
741 
742         let first_cid = if let Some(last_cid) = last_cid_prop {
743             if last_cid == GUEST_CID_MAX {
744                 GUEST_CID_MIN
745             } else {
746                 last_cid + 1
747             }
748         } else {
749             GUEST_CID_MIN
750         };
751 
752         let cid = self
753             .find_available_cid(first_cid..=GUEST_CID_MAX)
754             .or_else(|| self.find_available_cid(GUEST_CID_MIN..first_cid))
755             .ok_or_else(|| anyhow!("Could not find an available CID."))?;
756 
757         system_properties::write(SYSPROP_LAST_CID, &format!("{}", cid))?;
758         Ok(cid)
759     }
760 
find_available_cid<I>(&self, mut range: I) -> Option<Cid> where I: Iterator<Item = Cid>,761     fn find_available_cid<I>(&self, mut range: I) -> Option<Cid>
762     where
763         I: Iterator<Item = Cid>,
764     {
765         range.find(|cid| !self.held_contexts.contains_key(cid))
766     }
767 
allocate_vm_context( &mut self, name: &str, requester_uid: uid_t, requester_debug_pid: pid_t, ) -> Result<Strong<dyn IGlobalVmContext>>768     fn allocate_vm_context(
769         &mut self,
770         name: &str,
771         requester_uid: uid_t,
772         requester_debug_pid: pid_t,
773     ) -> Result<Strong<dyn IGlobalVmContext>> {
774         // Garbage collect unused VM contexts.
775         self.held_contexts.retain(|_, instance| instance.strong_count() > 0);
776 
777         let cid = self.get_next_available_cid()?;
778         let instance = Arc::new(Mutex::new(GlobalVmInstance {
779             name: name.to_owned(),
780             cid,
781             requester_uid,
782             requester_debug_pid,
783             ..Default::default()
784         }));
785         create_temporary_directory(&instance.lock().unwrap().get_temp_dir(), Some(requester_uid))?;
786 
787         self.held_contexts.insert(cid, Arc::downgrade(&instance));
788         let binder = GlobalVmContext { instance, ..Default::default() };
789         Ok(BnGlobalVmContext::new_binder(binder, BinderFeatures::default()))
790     }
791 
get_dtbo_file(&mut self) -> Result<File>792     fn get_dtbo_file(&mut self) -> Result<File> {
793         let mut file = self.dtbo_file.lock().unwrap();
794 
795         let fd = if let Some(ref_fd) = &*file {
796             ref_fd.try_clone()?
797         } else {
798             let path = get_or_create_common_dir()?.join("vm.dtbo");
799             if path.exists() {
800                 // All temporary files are deleted when the service is started.
801                 // If the file exists but the FD is not cached, the file is
802                 // likely corrupted.
803                 remove_file(&path).context("Failed to clone cached VM DTBO file descriptor")?;
804             }
805 
806             // Open a write-only file descriptor for vfio_handler.
807             let write_fd = File::create(&path).context("Failed to create VM DTBO file")?;
808             VFIO_SERVICE.writeVmDtbo(&ParcelFileDescriptor::new(write_fd))?;
809 
810             // Open read-only. This FD will be cached and returned to clients.
811             let read_fd = File::open(&path).context("Failed to open VM DTBO file")?;
812             let read_fd_clone =
813                 read_fd.try_clone().context("Failed to clone VM DTBO file descriptor")?;
814             *file = Some(read_fd);
815             read_fd_clone
816         };
817 
818         Ok(fd)
819     }
820 }
821 
create_temporary_directory(path: &PathBuf, requester_uid: Option<uid_t>) -> Result<()>822 fn create_temporary_directory(path: &PathBuf, requester_uid: Option<uid_t>) -> Result<()> {
823     // Directory may exist if previous attempt to create it had failed.
824     // Delete it before trying again.
825     if path.as_path().exists() {
826         remove_temporary_dir(path).unwrap_or_else(|e| {
827             warn!("Could not delete temporary directory {:?}: {}", path, e);
828         });
829     }
830     // Create directory.
831     create_dir(path).with_context(|| format!("Could not create temporary directory {:?}", path))?;
832     // If provided, change ownership to client's UID but system's GID, and permissions 0700.
833     // If the chown() fails, this will leave behind an empty directory that will get removed
834     // at the next attempt, or if virtualizationservice is restarted.
835     if let Some(uid) = requester_uid {
836         chown(path, Some(Uid::from_raw(uid)), None).with_context(|| {
837             format!("Could not set ownership of temporary directory {:?}", path)
838         })?;
839     }
840     Ok(())
841 }
842 
843 /// Removes a directory owned by a different user by first changing its owner back
844 /// to VirtualizationService.
remove_temporary_dir(path: &PathBuf) -> Result<()>845 pub fn remove_temporary_dir(path: &PathBuf) -> Result<()> {
846     ensure!(path.as_path().is_dir(), "Path {:?} is not a directory", path);
847     chown(path, Some(Uid::current()), None)?;
848     set_permissions(path, Permissions::from_mode(0o700))?;
849     remove_dir_all(path)?;
850     Ok(())
851 }
852 
get_or_create_common_dir() -> Result<PathBuf>853 fn get_or_create_common_dir() -> Result<PathBuf> {
854     let path = Path::new(TEMPORARY_DIRECTORY).join("common");
855     if !path.exists() {
856         create_temporary_directory(&path, None)?;
857     }
858     Ok(path)
859 }
860 
861 /// Implementation of the AIDL `IGlobalVmContext` interface.
862 #[derive(Debug, Default)]
863 struct GlobalVmContext {
864     /// Strong reference to the context's instance data structure.
865     instance: Arc<Mutex<GlobalVmInstance>>,
866     /// Keeps our service process running as long as this VM context exists.
867     #[allow(dead_code)]
868     lazy_service_guard: LazyServiceGuard,
869 }
870 
871 impl Interface for GlobalVmContext {}
872 
873 impl IGlobalVmContext for GlobalVmContext {
getCid(&self) -> binder::Result<i32>874     fn getCid(&self) -> binder::Result<i32> {
875         Ok(self.instance.lock().unwrap().cid as i32)
876     }
877 
getTemporaryDirectory(&self) -> binder::Result<String>878     fn getTemporaryDirectory(&self) -> binder::Result<String> {
879         Ok(self.instance.lock().unwrap().get_temp_dir().to_string_lossy().to_string())
880     }
881 
setHostConsoleName(&self, pathname: &str) -> binder::Result<()>882     fn setHostConsoleName(&self, pathname: &str) -> binder::Result<()> {
883         self.instance.lock().unwrap().host_console_name = Some(pathname.to_string());
884         Ok(())
885     }
886 }
887 
handle_stream_connection_tombstoned() -> Result<()>888 fn handle_stream_connection_tombstoned() -> Result<()> {
889     // Should not listen for tombstones on a guest VM's port.
890     assert!(!is_valid_guest_cid(VM_TOMBSTONES_SERVICE_PORT as Cid));
891     let listener =
892         VsockListener::bind_with_cid_port(VMADDR_CID_HOST, VM_TOMBSTONES_SERVICE_PORT as Cid)?;
893     for incoming_stream in listener.incoming() {
894         let mut incoming_stream = match incoming_stream {
895             Err(e) => {
896                 warn!("invalid incoming connection: {e:?}");
897                 continue;
898             }
899             Ok(s) => s,
900         };
901         if let Ok(addr) = incoming_stream.peer_addr() {
902             let cid = addr.cid();
903             match cid {
904                 VMADDR_CID_LOCAL | VMADDR_CID_HOST | VMADDR_CID_HYPERVISOR => {
905                     warn!("Rejecting non-guest tombstone vsock connection from cid={cid}");
906                     continue;
907                 }
908                 _ => info!("Vsock Stream connected to cid={cid} for tombstones"),
909             }
910         }
911         std::thread::spawn(move || {
912             if let Err(e) = handle_tombstone(&mut incoming_stream) {
913                 error!("Failed to write tombstone- {:?}", e);
914             }
915         });
916     }
917     Ok(())
918 }
919 
handle_tombstone(stream: &mut VsockStream) -> Result<()>920 fn handle_tombstone(stream: &mut VsockStream) -> Result<()> {
921     let tb_connection =
922         TombstonedConnection::connect(std::process::id() as i32, DebuggerdDumpType::Tombstone)
923             .context("Failed to connect to tombstoned")?;
924     let mut text_output = tb_connection
925         .text_output
926         .as_ref()
927         .ok_or_else(|| anyhow!("Could not get file to write the tombstones on"))?;
928     let mut num_bytes_read = 0;
929     loop {
930         let mut chunk_recv = [0; CHUNK_RECV_MAX_LEN];
931         let n = stream
932             .read(&mut chunk_recv)
933             .context("Failed to read tombstone data from Vsock stream")?;
934         if n == 0 {
935             break;
936         }
937         num_bytes_read += n;
938         text_output.write_all(&chunk_recv[0..n]).context("Failed to write guests tombstones")?;
939     }
940     info!("Received {} bytes from guest & wrote to tombstone file", num_bytes_read);
941     tb_connection.notify_completion()?;
942     Ok(())
943 }
944 
945 /// Returns true if the AVF remotely provisioned component service is declared in the
946 /// VINTF manifest.
is_remote_provisioning_hal_declared() -> binder::Result<bool>947 pub(crate) fn is_remote_provisioning_hal_declared() -> binder::Result<bool> {
948     Ok(binder::is_declared(REMOTELY_PROVISIONED_COMPONENT_SERVICE_NAME)?)
949 }
950 
951 /// Checks whether the caller has a specific permission
check_permission(perm: &str) -> binder::Result<()>952 fn check_permission(perm: &str) -> binder::Result<()> {
953     let calling_pid = get_calling_pid();
954     let calling_uid = get_calling_uid();
955     // Root can do anything
956     if calling_uid == 0 {
957         return Ok(());
958     }
959     let perm_svc: Strong<dyn IPermissionController::IPermissionController> =
960         binder::wait_for_interface("permission")?;
961     if perm_svc.checkPermission(perm, calling_pid, calling_uid as i32)? {
962         Ok(())
963     } else {
964         Err(anyhow!("does not have the {} permission", perm))
965             .or_binder_exception(ExceptionCode::SECURITY)
966     }
967 }
968 
969 /// Check whether the caller of the current Binder method is allowed to call debug methods.
check_debug_access() -> binder::Result<()>970 fn check_debug_access() -> binder::Result<()> {
971     check_permission("android.permission.DEBUG_VIRTUAL_MACHINE")
972 }
973 
974 /// Check whether the caller of the current Binder method is allowed to manage VMs
check_manage_access() -> binder::Result<()>975 fn check_manage_access() -> binder::Result<()> {
976     check_permission("android.permission.MANAGE_VIRTUAL_MACHINE")
977 }
978 
979 /// Check whether the caller of the current Binder method is allowed to use custom VMs
check_use_custom_virtual_machine() -> binder::Result<()>980 fn check_use_custom_virtual_machine() -> binder::Result<()> {
981     check_permission("android.permission.USE_CUSTOM_VIRTUAL_MACHINE")
982 }
983 
984 /// Check whether the caller of the current Binder method is allowed to create socket and
985 /// establish connection between the VM and the Internet.
check_internet_permission() -> binder::Result<()>986 fn check_internet_permission() -> binder::Result<()> {
987     check_permission("android.permission.INTERNET")
988 }
989 
990 #[cfg(test)]
991 mod tests {
992     use super::*;
993 
994     const TEST_RKP_CERT_CHAIN_PATH: &str = "testdata/rkp_cert_chain.der";
995 
996     #[test]
splitting_x509_certificate_chain_succeeds() -> Result<()>997     fn splitting_x509_certificate_chain_succeeds() -> Result<()> {
998         let bytes = fs::read(TEST_RKP_CERT_CHAIN_PATH)?;
999         let cert_chain = split_x509_certificate_chain(&bytes)?;
1000 
1001         assert_eq!(4, cert_chain.len());
1002         for cert in cert_chain {
1003             let x509_cert = X509::from_der(&cert.encodedCertificate)?;
1004             assert_eq!(x509_cert.to_der()?.len(), cert.encodedCertificate.len());
1005         }
1006         Ok(())
1007     }
1008 }
1009