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