1 /*
2  * Copyright 2024 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 //! Rust wrapper for the VM Payload API, allowing virtual machine payload code to be written in
18 //! Rust. This wraps the raw C API, accessed via bindgen, into a more idiomatic Rust interface.
19 //!
20 //! See `https://cs.android.com/android/platform/superproject/main/+/main:packages/modules/Virtualization/libs/libvm_payload/README.md`
21 //! for more information on the VM Payload API.
22 
23 mod attestation;
24 
25 pub use attestation::{request_attestation, AttestationError, AttestationResult};
26 use binder::unstable_api::AsNative;
27 use binder::{FromIBinder, Strong};
28 use std::ffi::{c_void, CStr, OsStr};
29 use std::os::unix::ffi::OsStrExt;
30 use std::path::Path;
31 use std::ptr;
32 use vm_payload_bindgen::{
33     AIBinder, AVmPayload_getApkContentsPath, AVmPayload_getEncryptedStoragePath,
34     AVmPayload_getVmInstanceSecret, AVmPayload_notifyPayloadReady, AVmPayload_runVsockRpcServer,
35 };
36 
37 /// The functions declared here are restricted to VMs created with a config file;
38 /// they will fail, or panic, if called in other VMs. The ability to create such VMs
39 /// requires the android.permission.USE_CUSTOM_VIRTUAL_MACHINE permission, and is
40 /// therefore not available to privileged or third party apps.
41 ///
42 /// These functions can be used by tests, if the permission is granted via shell.
43 pub mod restricted {
44     pub use crate::attestation::request_attestation_for_testing;
45 }
46 
47 /// Marks the main function of the VM payload.
48 ///
49 /// When the VM is run, this function is called. If it returns, the VM ends normally with a 0 exit
50 /// code.
51 ///
52 /// Example:
53 ///
54 /// ```rust
55 /// use log::info;
56 ///
57 /// vm_payload::main!(vm_main);
58 ///
59 /// fn vm_main() {
60 ///     android_logger::init_once(
61 ///          android_logger::Config::default()
62 ///             .with_tag("example_vm_payload")
63 ///             .with_max_level(log::LevelFilter::Info),
64 ///     );
65 ///     info!("Hello world");
66 /// }
67 /// ```
68 #[macro_export]
69 macro_rules! main {
70     ($name:path) => {
71         // Export a symbol with a name matching the extern declaration below.
72         #[export_name = "rust_main"]
73         fn __main() {
74             // Ensure that the main function provided by the application has the correct type.
75             $name()
76         }
77     };
78 }
79 
80 // This is the real C entry point for the VM; we just forward to the Rust entry point.
81 #[allow(non_snake_case)]
82 #[no_mangle]
AVmPayload_main()83 extern "C" fn AVmPayload_main() {
84     extern "Rust" {
85         fn rust_main();
86     }
87 
88     // SAFETY: rust_main is provided by the application using the `main!` macro above, which makes
89     // sure it has the right type.
90     unsafe { rust_main() }
91 }
92 
93 /// Notifies the host that the payload is ready.
94 ///
95 /// If the host app has set a `VirtualMachineCallback` for the VM, its
96 /// `onPayloadReady` method will be called.
97 ///
98 /// Note that subsequent calls to this function after the first have no effect;
99 /// `onPayloadReady` is never called more than once.
notify_payload_ready()100 pub fn notify_payload_ready() {
101     // SAFETY: Invokes a method from the bindgen library `vm_payload_bindgen` which is safe to
102     // call at any time.
103     unsafe { AVmPayload_notifyPayloadReady() };
104 }
105 
106 /// Runs a binder RPC server, serving the supplied binder service implementation on the given vsock
107 /// port.
108 ///
109 /// If and when the server is ready for connections (i.e. it is listening on the port),
110 /// [`notify_payload_ready`] is called to notify the host that the server is ready. This is
111 /// appropriate for VM payloads that serve a single binder service - which is common.
112 ///
113 /// Note that this function does not return. The calling thread joins the binder
114 /// thread pool to handle incoming messages.
run_single_vsock_service<T>(service: Strong<T>, port: u32) -> ! where T: FromIBinder + ?Sized,115 pub fn run_single_vsock_service<T>(service: Strong<T>, port: u32) -> !
116 where
117     T: FromIBinder + ?Sized,
118 {
119     extern "C" fn on_ready(_param: *mut c_void) {
120         notify_payload_ready();
121     }
122 
123     let mut service = service.as_binder();
124     // The cast here is needed because the compiler doesn't know that our vm_payload_bindgen
125     // AIBinder is the same type as binder_ndk_sys::AIBinder.
126     let service = service.as_native_mut() as *mut AIBinder;
127     let param = ptr::null_mut();
128     // SAFETY: We have a strong reference to the service, so the raw pointer remains valid. It is
129     // safe for on_ready to be invoked at any time, with any parameter.
130     unsafe { AVmPayload_runVsockRpcServer(service, port, Some(on_ready), param) }
131 }
132 
133 /// Gets the path to the contents of the APK containing the VM payload. It is a directory, under
134 /// which are the unzipped contents of the APK containing the payload, all read-only
135 /// but accessible to the payload.
apk_contents_path() -> &'static Path136 pub fn apk_contents_path() -> &'static Path {
137     // SAFETY: AVmPayload_getApkContentsPath always returns a non-null pointer to a
138     // nul-terminated C string with static lifetime.
139     let c_str = unsafe { CStr::from_ptr(AVmPayload_getApkContentsPath()) };
140     Path::new(OsStr::from_bytes(c_str.to_bytes()))
141 }
142 
143 /// Gets the path to the encrypted persistent storage for the VM, if any. This is
144 /// a directory under which any files or directories created will be stored on
145 /// behalf of the VM by the host app. All data is encrypted using a key known
146 /// only to the VM, so the host cannot decrypt it, but may delete it.
147 ///
148 /// Returns `None` if no encrypted storage was requested in the VM configuration.
encrypted_storage_path() -> Option<&'static Path>149 pub fn encrypted_storage_path() -> Option<&'static Path> {
150     // SAFETY: AVmPayload_getEncryptedStoragePath returns either null or a pointer to a
151     // nul-terminated C string with static lifetime.
152     let ptr = unsafe { AVmPayload_getEncryptedStoragePath() };
153     if ptr.is_null() {
154         None
155     } else {
156         // SAFETY: We know the pointer is not null, and so it is a valid C string.
157         let c_str = unsafe { CStr::from_ptr(ptr) };
158         Some(Path::new(OsStr::from_bytes(c_str.to_bytes())))
159     }
160 }
161 
162 /// Retrieves all or part of a 32-byte secret that is bound to this unique VM
163 /// instance and the supplied identifier. The secret can be used e.g. as an
164 /// encryption key.
165 ///
166 /// Every VM has a secret that is derived from a device-specific value known to
167 /// the hypervisor, the code that runs in the VM and its non-modifiable
168 /// configuration; it is not made available to the host OS.
169 ///
170 /// This function performs a further derivation from the VM secret and the
171 /// supplied identifier. As long as the VM identity doesn't change the same value
172 /// will be returned for the same identifier, even if the VM is stopped &
173 /// restarted or the device rebooted.
174 ///
175 /// If multiple secrets are required for different purposes, a different
176 /// identifier should be used for each. The identifiers otherwise are arbitrary
177 /// byte sequences and do not need to be kept secret; typically they are
178 /// hardcoded in the calling code.
179 ///
180 /// The secret is returned in [`secret`], truncated to its size, which must be between
181 /// 1 and 32 bytes (inclusive) or the function will panic.
get_vm_instance_secret(identifier: &[u8], secret: &mut [u8])182 pub fn get_vm_instance_secret(identifier: &[u8], secret: &mut [u8]) {
183     let secret_size = secret.len();
184     assert!((1..=32).contains(&secret_size), "VM instance secrets can be up to 32 bytes long");
185 
186     // SAFETY: The function only reads from `[identifier]` within its bounds, and only writes to
187     // `[secret]` within its bounds. Neither reference is retained, and we know neither is null.
188     unsafe {
189         AVmPayload_getVmInstanceSecret(
190             identifier.as_ptr() as *const c_void,
191             identifier.len(),
192             secret.as_mut_ptr() as *mut c_void,
193             secret_size,
194         )
195     }
196 }
197