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 
201 impl Interface for VirtualizationServiceInternal {}
202 
203 impl IVirtualizationServiceInternal for VirtualizationServiceInternal {
setDisplayService( &self, ibinder: &binder::SpIBinder, ) -> std::result::Result<(), binder::Status>204     fn setDisplayService(
205         &self,
206         ibinder: &binder::SpIBinder,
207     ) -> std::result::Result<(), binder::Status> {
208         check_manage_access()?;
209         check_use_custom_virtual_machine()?;
210         let state = &mut *self.state.lock().unwrap();
211         state.display_service = Some(ibinder.clone());
212         self.display_service_set.notify_all();
213         Ok(())
214     }
215 
clearDisplayService(&self) -> std::result::Result<(), binder::Status>216     fn clearDisplayService(&self) -> std::result::Result<(), binder::Status> {
217         check_manage_access()?;
218         check_use_custom_virtual_machine()?;
219         let state = &mut *self.state.lock().unwrap();
220         state.display_service = None;
221         self.display_service_set.notify_all();
222         Ok(())
223     }
224 
waitDisplayService(&self) -> std::result::Result<binder::SpIBinder, binder::Status>225     fn waitDisplayService(&self) -> std::result::Result<binder::SpIBinder, binder::Status> {
226         check_manage_access()?;
227         check_use_custom_virtual_machine()?;
228         let state = self
229             .display_service_set
230             .wait_while(self.state.lock().unwrap(), |state| state.display_service.is_none())
231             .unwrap();
232         Ok((state.display_service)
233             .as_ref()
234             .cloned()
235             .expect("Display service cannot be None in this context"))
236     }
removeMemlockRlimit(&self) -> binder::Result<()>237     fn removeMemlockRlimit(&self) -> binder::Result<()> {
238         let pid = get_calling_pid();
239         let lim = libc::rlimit { rlim_cur: libc::RLIM_INFINITY, rlim_max: libc::RLIM_INFINITY };
240 
241         // SAFETY: borrowing the new limit struct only
242         let ret = unsafe { libc::prlimit(pid, libc::RLIMIT_MEMLOCK, &lim, std::ptr::null_mut()) };
243 
244         match ret {
245             0 => Ok(()),
246             -1 => Err(std::io::Error::last_os_error().into()),
247             n => Err(anyhow!("Unexpected return value from prlimit(): {n}")),
248         }
249         .or_binder_exception(ExceptionCode::ILLEGAL_STATE)
250     }
251 
allocateGlobalVmContext( &self, requester_debug_pid: i32, ) -> binder::Result<Strong<dyn IGlobalVmContext>>252     fn allocateGlobalVmContext(
253         &self,
254         requester_debug_pid: i32,
255     ) -> binder::Result<Strong<dyn IGlobalVmContext>> {
256         check_manage_access()?;
257 
258         let requester_uid = get_calling_uid();
259         let requester_debug_pid = requester_debug_pid as pid_t;
260         let state = &mut *self.state.lock().unwrap();
261         state
262             .allocate_vm_context(requester_uid, requester_debug_pid)
263             .or_binder_exception(ExceptionCode::ILLEGAL_STATE)
264     }
265 
atomVmBooted(&self, atom: &AtomVmBooted) -> Result<(), Status>266     fn atomVmBooted(&self, atom: &AtomVmBooted) -> Result<(), Status> {
267         forward_vm_booted_atom(atom);
268         Ok(())
269     }
270 
atomVmCreationRequested(&self, atom: &AtomVmCreationRequested) -> Result<(), Status>271     fn atomVmCreationRequested(&self, atom: &AtomVmCreationRequested) -> Result<(), Status> {
272         forward_vm_creation_atom(atom);
273         Ok(())
274     }
275 
atomVmExited(&self, atom: &AtomVmExited) -> Result<(), Status>276     fn atomVmExited(&self, atom: &AtomVmExited) -> Result<(), Status> {
277         forward_vm_exited_atom(atom);
278         Ok(())
279     }
280 
debugListVms(&self) -> binder::Result<Vec<VirtualMachineDebugInfo>>281     fn debugListVms(&self) -> binder::Result<Vec<VirtualMachineDebugInfo>> {
282         check_debug_access()?;
283 
284         let state = &mut *self.state.lock().unwrap();
285         let cids = state
286             .held_contexts
287             .iter()
288             .filter_map(|(_, inst)| Weak::upgrade(inst))
289             .map(|vm| {
290                 let vm = vm.lock().unwrap();
291                 VirtualMachineDebugInfo {
292                     cid: vm.cid as i32,
293                     temporaryDirectory: vm.get_temp_dir().to_string_lossy().to_string(),
294                     requesterUid: vm.requester_uid as i32,
295                     requesterPid: vm.requester_debug_pid,
296                     hostConsoleName: vm.host_console_name.clone(),
297                 }
298             })
299             .collect();
300         Ok(cids)
301     }
302 
enableTestAttestation(&self) -> binder::Result<()>303     fn enableTestAttestation(&self) -> binder::Result<()> {
304         check_manage_access()?;
305         check_use_custom_virtual_machine()?;
306         if !cfg!(remote_attestation) {
307             return Err(Status::new_exception_str(
308                 ExceptionCode::UNSUPPORTED_OPERATION,
309                 Some(
310                     "enableTestAttestation is not supported with the remote_attestation \
311                      feature disabled",
312                 ),
313             ))
314             .with_log();
315         }
316         let res = generate_ecdsa_p256_key_pair()
317             .context("Failed to generate ECDSA P-256 key pair for testing")
318             .with_log()
319             .or_service_specific_exception(-1)?;
320         // Wait until the service VM shuts down, so that the Service VM will be restarted when
321         // the key generated in the current session will be used for attestation.
322         // This ensures that different Service VM sessions have the same KEK for the key blob.
323         service_vm_manager::wait_until_service_vm_shuts_down()
324             .context("Failed to wait until the service VM shuts down")
325             .with_log()
326             .or_service_specific_exception(-1)?;
327         match res {
328             Response::GenerateEcdsaP256KeyPair(key_pair) => {
329                 FAKE_PROVISIONED_KEY_BLOB_FOR_TESTING
330                     .lock()
331                     .unwrap()
332                     .replace(key_pair.key_blob.to_vec());
333                 Ok(())
334             }
335             _ => Err(remote_provisioning::to_service_specific_error(res)),
336         }
337         .with_log()
338     }
339 
requestAttestation( &self, csr: &[u8], requester_uid: i32, test_mode: bool, ) -> binder::Result<Vec<Certificate>>340     fn requestAttestation(
341         &self,
342         csr: &[u8],
343         requester_uid: i32,
344         test_mode: bool,
345     ) -> binder::Result<Vec<Certificate>> {
346         check_manage_access()?;
347         if !cfg!(remote_attestation) {
348             return Err(Status::new_exception_str(
349                 ExceptionCode::UNSUPPORTED_OPERATION,
350                 Some(
351                     "requestAttestation is not supported with the remote_attestation feature \
352                      disabled",
353                 ),
354             ))
355             .with_log();
356         }
357         if !is_remote_provisioning_hal_declared()? {
358             return Err(Status::new_exception_str(
359                 ExceptionCode::UNSUPPORTED_OPERATION,
360                 Some("AVF remotely provisioned component service is not declared"),
361             ))
362             .with_log();
363         }
364         remote_provisioning::check_remote_attestation_is_supported()?;
365         info!("Received csr. Requestting attestation...");
366         let (key_blob, certificate_chain) = if test_mode {
367             check_use_custom_virtual_machine()?;
368             info!("Using the fake key blob for testing...");
369             (
370                 FAKE_PROVISIONED_KEY_BLOB_FOR_TESTING
371                     .lock()
372                     .unwrap()
373                     .clone()
374                     .ok_or_else(|| anyhow!("No key blob for testing"))
375                     .with_log()
376                     .or_service_specific_exception(-1)?,
377                 FAKE_CERTIFICATE_FOR_TESTING.to_vec(),
378             )
379         } else {
380             info!("Retrieving the remotely provisioned keys from RKPD...");
381             let attestation_key = get_rkpd_attestation_key(
382                 REMOTELY_PROVISIONED_COMPONENT_SERVICE_NAME,
383                 requester_uid as u32,
384             )
385             .context("Failed to retrieve the remotely provisioned keys")
386             .with_log()
387             .or_service_specific_exception(-1)?;
388             (attestation_key.keyBlob, attestation_key.encodedCertChain)
389         };
390         let mut certificate_chain = split_x509_certificate_chain(&certificate_chain)
391             .context("Failed to split the remotely provisioned certificate chain")
392             .with_log()
393             .or_service_specific_exception(-1)?;
394         if certificate_chain.is_empty() {
395             return Err(Status::new_service_specific_error_str(
396                 -1,
397                 Some("The certificate chain should contain at least 1 certificate"),
398             ))
399             .with_log();
400         }
401         let certificate = request_attestation(
402             csr.to_vec(),
403             key_blob,
404             certificate_chain[0].encodedCertificate.clone(),
405         )
406         .context("Failed to request attestation")
407         .with_log()
408         .or_service_specific_exception(-1)?;
409         certificate_chain.insert(0, Certificate { encodedCertificate: certificate });
410 
411         Ok(certificate_chain)
412     }
413 
isRemoteAttestationSupported(&self) -> binder::Result<bool>414     fn isRemoteAttestationSupported(&self) -> binder::Result<bool> {
415         Ok(is_remote_provisioning_hal_declared()?
416             && remote_provisioning::is_remote_attestation_supported())
417     }
418 
getAssignableDevices(&self) -> binder::Result<Vec<AssignableDevice>>419     fn getAssignableDevices(&self) -> binder::Result<Vec<AssignableDevice>> {
420         check_use_custom_virtual_machine()?;
421 
422         Ok(get_assignable_devices()?
423             .device
424             .into_iter()
425             .map(|x| AssignableDevice { node: x.sysfs_path, dtbo_label: x.dtbo_label })
426             .collect::<Vec<_>>())
427     }
428 
bindDevicesToVfioDriver( &self, devices: &[String], ) -> binder::Result<Vec<Strong<dyn IBoundDevice>>>429     fn bindDevicesToVfioDriver(
430         &self,
431         devices: &[String],
432     ) -> binder::Result<Vec<Strong<dyn IBoundDevice>>> {
433         check_use_custom_virtual_machine()?;
434 
435         let devices = get_assignable_devices()?
436             .device
437             .into_iter()
438             .filter_map(|x| {
439                 if devices.contains(&x.sysfs_path) {
440                     Some(VfioDev { sysfsPath: x.sysfs_path, dtboLabel: x.dtbo_label })
441                 } else {
442                     warn!("device {} is not assignable", x.sysfs_path);
443                     None
444                 }
445             })
446             .collect::<Vec<VfioDev>>();
447 
448         VFIO_SERVICE.bindDevicesToVfioDriver(devices.as_slice())
449     }
450 
getDtboFile(&self) -> binder::Result<ParcelFileDescriptor>451     fn getDtboFile(&self) -> binder::Result<ParcelFileDescriptor> {
452         check_use_custom_virtual_machine()?;
453 
454         let state = &mut *self.state.lock().unwrap();
455         let file = state.get_dtbo_file().or_service_specific_exception(-1)?;
456         Ok(ParcelFileDescriptor::new(file))
457     }
458 
allocateInstanceId(&self) -> binder::Result<[u8; 64]>459     fn allocateInstanceId(&self) -> binder::Result<[u8; 64]> {
460         let mut id = [0u8; 64];
461         id.try_fill(&mut rand::thread_rng())
462             .context("Failed to allocate instance_id")
463             .or_service_specific_exception(-1)?;
464         let uid = get_calling_uid();
465         info!("Allocated a VM's instance_id: {:?}..., for uid: {:?}", &hex::encode(id)[..8], uid);
466         let state = &mut *self.state.lock().unwrap();
467         if let Some(sk_state) = &mut state.sk_state {
468             let user_id = multiuser_get_user_id(uid);
469             let app_id = multiuser_get_app_id(uid);
470             info!("Recording possible existence of state for (user_id={user_id}, app_id={app_id})");
471             if let Err(e) = sk_state.add_id(&id, user_id, app_id) {
472                 error!("Failed to record the instance_id: {e:?}");
473             }
474         }
475 
476         Ok(id)
477     }
478 
removeVmInstance(&self, instance_id: &[u8; 64]) -> binder::Result<()>479     fn removeVmInstance(&self, instance_id: &[u8; 64]) -> binder::Result<()> {
480         let state = &mut *self.state.lock().unwrap();
481         if let Some(sk_state) = &mut state.sk_state {
482             let uid = get_calling_uid();
483             info!(
484                 "Removing a VM's instance_id: {:?}, for uid: {:?}",
485                 hex::encode(instance_id),
486                 uid
487             );
488 
489             let user_id = multiuser_get_user_id(uid);
490             let app_id = multiuser_get_app_id(uid);
491             sk_state.delete_id(instance_id, user_id, app_id);
492         } else {
493             info!("ignoring removeVmInstance() as no ISecretkeeper");
494         }
495         Ok(())
496     }
497 
claimVmInstance(&self, instance_id: &[u8; 64]) -> binder::Result<()>498     fn claimVmInstance(&self, instance_id: &[u8; 64]) -> binder::Result<()> {
499         let state = &mut *self.state.lock().unwrap();
500         if let Some(sk_state) = &mut state.sk_state {
501             let uid = get_calling_uid();
502             info!(
503                 "Claiming a VM's instance_id: {:?}, for uid: {:?}",
504                 hex::encode(instance_id),
505                 uid
506             );
507 
508             let user_id = multiuser_get_user_id(uid);
509             let app_id = multiuser_get_app_id(uid);
510             info!("Recording possible new owner of state for (user_id={user_id}, app_id={app_id})");
511             if let Err(e) = sk_state.add_id(instance_id, user_id, app_id) {
512                 error!("Failed to update the instance_id owner: {e:?}");
513             }
514         } else {
515             info!("ignoring claimVmInstance() as no ISecretkeeper");
516         }
517         Ok(())
518     }
519 
createTapInterface(&self, _iface_name_suffix: &str) -> binder::Result<ParcelFileDescriptor>520     fn createTapInterface(&self, _iface_name_suffix: &str) -> binder::Result<ParcelFileDescriptor> {
521         check_internet_permission()?;
522         check_use_custom_virtual_machine()?;
523         if !cfg!(network) {
524             return Err(Status::new_exception_str(
525                 ExceptionCode::UNSUPPORTED_OPERATION,
526                 Some("createTapInterface is not supported with the network feature disabled"),
527             ))
528             .with_log();
529         }
530         // TODO(340377643): Use iface_name_suffix after introducing bridge interface, not fixed
531         // value.
532         let tap_fd = NETWORK_SERVICE.createTapInterface("fixed")?;
533 
534         // TODO(340377643): Due to lack of implementation of creating bridge interface, tethering is
535         // enabled for TAP interface instead of bridge interface. After introducing creation of
536         // bridge interface in AVF, we should modify it.
537         TETHERING_SERVICE.enableVmTethering()?;
538 
539         Ok(tap_fd)
540     }
541 
deleteTapInterface(&self, tap_fd: &ParcelFileDescriptor) -> binder::Result<()>542     fn deleteTapInterface(&self, tap_fd: &ParcelFileDescriptor) -> binder::Result<()> {
543         check_internet_permission()?;
544         check_use_custom_virtual_machine()?;
545         if !cfg!(network) {
546             return Err(Status::new_exception_str(
547                 ExceptionCode::UNSUPPORTED_OPERATION,
548                 Some("deleteTapInterface is not supported with the network feature disabled"),
549             ))
550             .with_log();
551         }
552 
553         // TODO(340377643): Disabling tethering should be for bridge interface, not TAP interface.
554         TETHERING_SERVICE.disableVmTethering()?;
555 
556         NETWORK_SERVICE.deleteTapInterface(tap_fd)
557     }
558 }
559 
560 impl IVirtualizationMaintenance for VirtualizationServiceInternal {
appRemoved(&self, user_id: i32, app_id: i32) -> binder::Result<()>561     fn appRemoved(&self, user_id: i32, app_id: i32) -> binder::Result<()> {
562         let state = &mut *self.state.lock().unwrap();
563         if let Some(sk_state) = &mut state.sk_state {
564             info!("packageRemoved(user_id={user_id}, app_id={app_id})");
565             sk_state.delete_ids_for_app(user_id, app_id).or_service_specific_exception(-1)?;
566         } else {
567             info!("ignoring packageRemoved(user_id={user_id}, app_id={app_id})");
568         }
569         Ok(())
570     }
571 
userRemoved(&self, user_id: i32) -> binder::Result<()>572     fn userRemoved(&self, user_id: i32) -> binder::Result<()> {
573         let state = &mut *self.state.lock().unwrap();
574         if let Some(sk_state) = &mut state.sk_state {
575             info!("userRemoved({user_id})");
576             sk_state.delete_ids_for_user(user_id).or_service_specific_exception(-1)?;
577         } else {
578             info!("ignoring userRemoved(user_id={user_id})");
579         }
580         Ok(())
581     }
582 
performReconciliation( &self, callback: &Strong<dyn IVirtualizationReconciliationCallback>, ) -> binder::Result<()>583     fn performReconciliation(
584         &self,
585         callback: &Strong<dyn IVirtualizationReconciliationCallback>,
586     ) -> binder::Result<()> {
587         let state = &mut *self.state.lock().unwrap();
588         if let Some(sk_state) = &mut state.sk_state {
589             info!("performReconciliation()");
590             sk_state.reconcile(callback).or_service_specific_exception(-1)?;
591         } else {
592             info!("ignoring performReconciliation()");
593         }
594         Ok(())
595     }
596 }
597 
598 #[derive(Debug, Deserialize)]
599 struct Device {
600     dtbo_label: String,
601     sysfs_path: String,
602 }
603 
604 #[derive(Debug, Default, Deserialize)]
605 struct Devices {
606     device: Vec<Device>,
607 }
608 
get_assignable_devices() -> binder::Result<Devices>609 fn get_assignable_devices() -> binder::Result<Devices> {
610     let xml_path = Path::new("/vendor/etc/avf/assignable_devices.xml");
611     if !xml_path.exists() {
612         return Ok(Devices { ..Default::default() });
613     }
614 
615     let xml = fs::read(xml_path)
616         .context("Failed to read assignable_devices.xml")
617         .with_log()
618         .or_service_specific_exception(-1)?;
619 
620     let xml = String::from_utf8(xml)
621         .context("assignable_devices.xml is not a valid UTF-8 file")
622         .with_log()
623         .or_service_specific_exception(-1)?;
624 
625     let mut devices: Devices = serde_xml_rs::from_str(&xml)
626         .context("can't parse assignable_devices.xml")
627         .with_log()
628         .or_service_specific_exception(-1)?;
629 
630     let mut device_set = HashSet::new();
631     devices.device.retain(move |device| {
632         if device_set.contains(&device.sysfs_path) {
633             warn!("duplicated assignable device {device:?}; ignoring...");
634             return false;
635         }
636 
637         if !Path::new(&device.sysfs_path).exists() {
638             warn!("assignable device {device:?} doesn't exist; ignoring...");
639             return false;
640         }
641 
642         device_set.insert(device.sysfs_path.clone());
643         true
644     });
645     Ok(devices)
646 }
647 
split_x509_certificate_chain(mut cert_chain: &[u8]) -> Result<Vec<Certificate>>648 fn split_x509_certificate_chain(mut cert_chain: &[u8]) -> Result<Vec<Certificate>> {
649     let mut out = Vec::new();
650     while !cert_chain.is_empty() {
651         let cert = X509::from_der(cert_chain)?;
652         let end = cert.to_der()?.len();
653         out.push(Certificate { encodedCertificate: cert_chain[..end].to_vec() });
654         cert_chain = &cert_chain[end..];
655     }
656     Ok(out)
657 }
658 
659 #[derive(Debug, Default)]
660 struct GlobalVmInstance {
661     /// The unique CID assigned to the VM for vsock communication.
662     cid: Cid,
663     /// UID of the client who requested this VM instance.
664     requester_uid: uid_t,
665     /// PID of the client who requested this VM instance.
666     requester_debug_pid: pid_t,
667     /// Name of the host console.
668     host_console_name: Option<String>,
669 }
670 
671 impl GlobalVmInstance {
get_temp_dir(&self) -> PathBuf672     fn get_temp_dir(&self) -> PathBuf {
673         let cid = self.cid;
674         format!("{TEMPORARY_DIRECTORY}/{cid}").into()
675     }
676 }
677 
678 /// The mutable state of the VirtualizationServiceInternal. There should only be one instance
679 /// of this struct.
680 struct GlobalState {
681     /// VM contexts currently allocated to running VMs. A CID is never recycled as long
682     /// as there is a strong reference held by a GlobalVmContext.
683     held_contexts: HashMap<Cid, Weak<Mutex<GlobalVmInstance>>>,
684 
685     /// Cached read-only FD of VM DTBO file. Also serves as a lock for creating the file.
686     dtbo_file: Mutex<Option<File>>,
687 
688     /// State relating to secrets held by (optional) Secretkeeper instance on behalf of VMs.
689     sk_state: Option<maintenance::State>,
690 
691     display_service: Option<binder::SpIBinder>,
692 }
693 
694 impl GlobalState {
new() -> Self695     fn new() -> Self {
696         Self {
697             held_contexts: HashMap::new(),
698             dtbo_file: Mutex::new(None),
699             sk_state: maintenance::State::new(),
700             display_service: None,
701         }
702     }
703 
704     /// Get the next available CID, or an error if we have run out. The last CID used is stored in
705     /// a system property so that restart of virtualizationservice doesn't reuse CID while the host
706     /// Android is up.
get_next_available_cid(&mut self) -> Result<Cid>707     fn get_next_available_cid(&mut self) -> Result<Cid> {
708         // Start trying to find a CID from the last used CID + 1. This ensures
709         // that we do not eagerly recycle CIDs. It makes debugging easier but
710         // also means that retrying to allocate a CID, eg. because it is
711         // erroneously occupied by a process, will not recycle the same CID.
712         let last_cid_prop =
713             system_properties::read(SYSPROP_LAST_CID)?.and_then(|val| match val.parse::<Cid>() {
714                 Ok(num) => {
715                     if is_valid_guest_cid(num) {
716                         Some(num)
717                     } else {
718                         error!("Invalid value '{}' of property '{}'", num, SYSPROP_LAST_CID);
719                         None
720                     }
721                 }
722                 Err(_) => {
723                     error!("Invalid value '{}' of property '{}'", val, SYSPROP_LAST_CID);
724                     None
725                 }
726             });
727 
728         let first_cid = if let Some(last_cid) = last_cid_prop {
729             if last_cid == GUEST_CID_MAX {
730                 GUEST_CID_MIN
731             } else {
732                 last_cid + 1
733             }
734         } else {
735             GUEST_CID_MIN
736         };
737 
738         let cid = self
739             .find_available_cid(first_cid..=GUEST_CID_MAX)
740             .or_else(|| self.find_available_cid(GUEST_CID_MIN..first_cid))
741             .ok_or_else(|| anyhow!("Could not find an available CID."))?;
742 
743         system_properties::write(SYSPROP_LAST_CID, &format!("{}", cid))?;
744         Ok(cid)
745     }
746 
find_available_cid<I>(&self, mut range: I) -> Option<Cid> where I: Iterator<Item = Cid>,747     fn find_available_cid<I>(&self, mut range: I) -> Option<Cid>
748     where
749         I: Iterator<Item = Cid>,
750     {
751         range.find(|cid| !self.held_contexts.contains_key(cid))
752     }
753 
allocate_vm_context( &mut self, requester_uid: uid_t, requester_debug_pid: pid_t, ) -> Result<Strong<dyn IGlobalVmContext>>754     fn allocate_vm_context(
755         &mut self,
756         requester_uid: uid_t,
757         requester_debug_pid: pid_t,
758     ) -> Result<Strong<dyn IGlobalVmContext>> {
759         // Garbage collect unused VM contexts.
760         self.held_contexts.retain(|_, instance| instance.strong_count() > 0);
761 
762         let cid = self.get_next_available_cid()?;
763         let instance = Arc::new(Mutex::new(GlobalVmInstance {
764             cid,
765             requester_uid,
766             requester_debug_pid,
767             ..Default::default()
768         }));
769         create_temporary_directory(&instance.lock().unwrap().get_temp_dir(), Some(requester_uid))?;
770 
771         self.held_contexts.insert(cid, Arc::downgrade(&instance));
772         let binder = GlobalVmContext { instance, ..Default::default() };
773         Ok(BnGlobalVmContext::new_binder(binder, BinderFeatures::default()))
774     }
775 
get_dtbo_file(&mut self) -> Result<File>776     fn get_dtbo_file(&mut self) -> Result<File> {
777         let mut file = self.dtbo_file.lock().unwrap();
778 
779         let fd = if let Some(ref_fd) = &*file {
780             ref_fd.try_clone()?
781         } else {
782             let path = get_or_create_common_dir()?.join("vm.dtbo");
783             if path.exists() {
784                 // All temporary files are deleted when the service is started.
785                 // If the file exists but the FD is not cached, the file is
786                 // likely corrupted.
787                 remove_file(&path).context("Failed to clone cached VM DTBO file descriptor")?;
788             }
789 
790             // Open a write-only file descriptor for vfio_handler.
791             let write_fd = File::create(&path).context("Failed to create VM DTBO file")?;
792             VFIO_SERVICE.writeVmDtbo(&ParcelFileDescriptor::new(write_fd))?;
793 
794             // Open read-only. This FD will be cached and returned to clients.
795             let read_fd = File::open(&path).context("Failed to open VM DTBO file")?;
796             let read_fd_clone =
797                 read_fd.try_clone().context("Failed to clone VM DTBO file descriptor")?;
798             *file = Some(read_fd);
799             read_fd_clone
800         };
801 
802         Ok(fd)
803     }
804 }
805 
create_temporary_directory(path: &PathBuf, requester_uid: Option<uid_t>) -> Result<()>806 fn create_temporary_directory(path: &PathBuf, requester_uid: Option<uid_t>) -> Result<()> {
807     // Directory may exist if previous attempt to create it had failed.
808     // Delete it before trying again.
809     if path.as_path().exists() {
810         remove_temporary_dir(path).unwrap_or_else(|e| {
811             warn!("Could not delete temporary directory {:?}: {}", path, e);
812         });
813     }
814     // Create directory.
815     create_dir(path).with_context(|| format!("Could not create temporary directory {:?}", path))?;
816     // If provided, change ownership to client's UID but system's GID, and permissions 0700.
817     // If the chown() fails, this will leave behind an empty directory that will get removed
818     // at the next attempt, or if virtualizationservice is restarted.
819     if let Some(uid) = requester_uid {
820         chown(path, Some(Uid::from_raw(uid)), None).with_context(|| {
821             format!("Could not set ownership of temporary directory {:?}", path)
822         })?;
823     }
824     Ok(())
825 }
826 
827 /// Removes a directory owned by a different user by first changing its owner back
828 /// to VirtualizationService.
remove_temporary_dir(path: &PathBuf) -> Result<()>829 pub fn remove_temporary_dir(path: &PathBuf) -> Result<()> {
830     ensure!(path.as_path().is_dir(), "Path {:?} is not a directory", path);
831     chown(path, Some(Uid::current()), None)?;
832     set_permissions(path, Permissions::from_mode(0o700))?;
833     remove_dir_all(path)?;
834     Ok(())
835 }
836 
get_or_create_common_dir() -> Result<PathBuf>837 fn get_or_create_common_dir() -> Result<PathBuf> {
838     let path = Path::new(TEMPORARY_DIRECTORY).join("common");
839     if !path.exists() {
840         create_temporary_directory(&path, None)?;
841     }
842     Ok(path)
843 }
844 
845 /// Implementation of the AIDL `IGlobalVmContext` interface.
846 #[derive(Debug, Default)]
847 struct GlobalVmContext {
848     /// Strong reference to the context's instance data structure.
849     instance: Arc<Mutex<GlobalVmInstance>>,
850     /// Keeps our service process running as long as this VM context exists.
851     #[allow(dead_code)]
852     lazy_service_guard: LazyServiceGuard,
853 }
854 
855 impl Interface for GlobalVmContext {}
856 
857 impl IGlobalVmContext for GlobalVmContext {
getCid(&self) -> binder::Result<i32>858     fn getCid(&self) -> binder::Result<i32> {
859         Ok(self.instance.lock().unwrap().cid as i32)
860     }
861 
getTemporaryDirectory(&self) -> binder::Result<String>862     fn getTemporaryDirectory(&self) -> binder::Result<String> {
863         Ok(self.instance.lock().unwrap().get_temp_dir().to_string_lossy().to_string())
864     }
865 
setHostConsoleName(&self, pathname: &str) -> binder::Result<()>866     fn setHostConsoleName(&self, pathname: &str) -> binder::Result<()> {
867         self.instance.lock().unwrap().host_console_name = Some(pathname.to_string());
868         Ok(())
869     }
870 }
871 
handle_stream_connection_tombstoned() -> Result<()>872 fn handle_stream_connection_tombstoned() -> Result<()> {
873     // Should not listen for tombstones on a guest VM's port.
874     assert!(!is_valid_guest_cid(VM_TOMBSTONES_SERVICE_PORT as Cid));
875     let listener =
876         VsockListener::bind_with_cid_port(VMADDR_CID_HOST, VM_TOMBSTONES_SERVICE_PORT as Cid)?;
877     for incoming_stream in listener.incoming() {
878         let mut incoming_stream = match incoming_stream {
879             Err(e) => {
880                 warn!("invalid incoming connection: {e:?}");
881                 continue;
882             }
883             Ok(s) => s,
884         };
885         if let Ok(addr) = incoming_stream.peer_addr() {
886             let cid = addr.cid();
887             match cid {
888                 VMADDR_CID_LOCAL | VMADDR_CID_HOST | VMADDR_CID_HYPERVISOR => {
889                     warn!("Rejecting non-guest tombstone vsock connection from cid={cid}");
890                     continue;
891                 }
892                 _ => info!("Vsock Stream connected to cid={cid} for tombstones"),
893             }
894         }
895         std::thread::spawn(move || {
896             if let Err(e) = handle_tombstone(&mut incoming_stream) {
897                 error!("Failed to write tombstone- {:?}", e);
898             }
899         });
900     }
901     Ok(())
902 }
903 
handle_tombstone(stream: &mut VsockStream) -> Result<()>904 fn handle_tombstone(stream: &mut VsockStream) -> Result<()> {
905     let tb_connection =
906         TombstonedConnection::connect(std::process::id() as i32, DebuggerdDumpType::Tombstone)
907             .context("Failed to connect to tombstoned")?;
908     let mut text_output = tb_connection
909         .text_output
910         .as_ref()
911         .ok_or_else(|| anyhow!("Could not get file to write the tombstones on"))?;
912     let mut num_bytes_read = 0;
913     loop {
914         let mut chunk_recv = [0; CHUNK_RECV_MAX_LEN];
915         let n = stream
916             .read(&mut chunk_recv)
917             .context("Failed to read tombstone data from Vsock stream")?;
918         if n == 0 {
919             break;
920         }
921         num_bytes_read += n;
922         text_output.write_all(&chunk_recv[0..n]).context("Failed to write guests tombstones")?;
923     }
924     info!("Received {} bytes from guest & wrote to tombstone file", num_bytes_read);
925     tb_connection.notify_completion()?;
926     Ok(())
927 }
928 
929 /// Returns true if the AVF remotely provisioned component service is declared in the
930 /// VINTF manifest.
is_remote_provisioning_hal_declared() -> binder::Result<bool>931 pub(crate) fn is_remote_provisioning_hal_declared() -> binder::Result<bool> {
932     Ok(binder::is_declared(REMOTELY_PROVISIONED_COMPONENT_SERVICE_NAME)?)
933 }
934 
935 /// Checks whether the caller has a specific permission
check_permission(perm: &str) -> binder::Result<()>936 fn check_permission(perm: &str) -> binder::Result<()> {
937     let calling_pid = get_calling_pid();
938     let calling_uid = get_calling_uid();
939     // Root can do anything
940     if calling_uid == 0 {
941         return Ok(());
942     }
943     let perm_svc: Strong<dyn IPermissionController::IPermissionController> =
944         binder::wait_for_interface("permission")?;
945     if perm_svc.checkPermission(perm, calling_pid, calling_uid as i32)? {
946         Ok(())
947     } else {
948         Err(anyhow!("does not have the {} permission", perm))
949             .or_binder_exception(ExceptionCode::SECURITY)
950     }
951 }
952 
953 /// Check whether the caller of the current Binder method is allowed to call debug methods.
check_debug_access() -> binder::Result<()>954 fn check_debug_access() -> binder::Result<()> {
955     check_permission("android.permission.DEBUG_VIRTUAL_MACHINE")
956 }
957 
958 /// Check whether the caller of the current Binder method is allowed to manage VMs
check_manage_access() -> binder::Result<()>959 fn check_manage_access() -> binder::Result<()> {
960     check_permission("android.permission.MANAGE_VIRTUAL_MACHINE")
961 }
962 
963 /// Check whether the caller of the current Binder method is allowed to use custom VMs
check_use_custom_virtual_machine() -> binder::Result<()>964 fn check_use_custom_virtual_machine() -> binder::Result<()> {
965     check_permission("android.permission.USE_CUSTOM_VIRTUAL_MACHINE")
966 }
967 
968 /// Check whether the caller of the current Binder method is allowed to create socket and
969 /// establish connection between the VM and the Internet.
check_internet_permission() -> binder::Result<()>970 fn check_internet_permission() -> binder::Result<()> {
971     check_permission("android.permission.INTERNET")
972 }
973 
974 #[cfg(test)]
975 mod tests {
976     use super::*;
977 
978     const TEST_RKP_CERT_CHAIN_PATH: &str = "testdata/rkp_cert_chain.der";
979 
980     #[test]
splitting_x509_certificate_chain_succeeds() -> Result<()>981     fn splitting_x509_certificate_chain_succeeds() -> Result<()> {
982         let bytes = fs::read(TEST_RKP_CERT_CHAIN_PATH)?;
983         let cert_chain = split_x509_certificate_chain(&bytes)?;
984 
985         assert_eq!(4, cert_chain.len());
986         for cert in cert_chain {
987             let x509_cert = X509::from_der(&cert.encodedCertificate)?;
988             assert_eq!(x509_cert.to_der()?.len(), cert.encodedCertificate.len());
989         }
990         Ok(())
991     }
992 }
993