blob: f950db9bad66088abc2b550176b69c75a475d32c [file] [log] [blame]
Alan Stokesea1f0462024-02-19 16:25:47 +00001// Copyright 2024 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
David Drysdale79af2662024-02-19 14:50:31 +000015use android_hardware_security_secretkeeper::aidl::android::hardware::security::secretkeeper::{
16 ISecretkeeper::ISecretkeeper, SecretId::SecretId,
Alan Stokesea1f0462024-02-19 16:25:47 +000017};
David Drysdale1138fa02024-03-19 13:06:23 +000018use android_system_virtualizationmaintenance::aidl::android::system::virtualizationmaintenance;
19use anyhow::{anyhow, Context, Result};
20use binder::Strong;
David Drysdale79af2662024-02-19 14:50:31 +000021use log::{error, info, warn};
David Drysdale1138fa02024-03-19 13:06:23 +000022use virtualizationmaintenance::IVirtualizationReconciliationCallback::IVirtualizationReconciliationCallback;
Alan Stokesea1f0462024-02-19 16:25:47 +000023
David Drysdale79af2662024-02-19 14:50:31 +000024mod vmdb;
25use vmdb::{VmId, VmIdDb};
26
David Drysdale1138fa02024-03-19 13:06:23 +000027/// Indicate whether an app ID belongs to a system core component.
28fn core_app_id(app_id: i32) -> bool {
29 app_id < 10000
30}
31
David Drysdale79af2662024-02-19 14:50:31 +000032/// Interface name for the Secretkeeper HAL.
33const SECRETKEEPER_SERVICE: &str = "android.hardware.security.secretkeeper.ISecretkeeper/default";
34
35/// Directory in which to write persistent state.
36const PERSISTENT_DIRECTORY: &str = "/data/misc/apexdata/com.android.virt";
37
38/// Maximum number of VM IDs to delete at once. Needs to be smaller than both the maximum
39/// number of SQLite parameters (999) and also small enough that an ISecretkeeper::deleteIds
40/// parcel fits within max AIDL message size.
41const DELETE_MAX_BATCH_SIZE: usize = 100;
42
43/// State related to VM secrets.
44pub struct State {
45 sk: binder::Strong<dyn ISecretkeeper>,
46 /// Database of VM IDs,
47 vm_id_db: VmIdDb,
48 batch_size: usize,
Alan Stokesea1f0462024-02-19 16:25:47 +000049}
50
David Drysdale79af2662024-02-19 14:50:31 +000051impl State {
52 pub fn new() -> Option<Self> {
53 let sk = match Self::find_sk() {
54 Some(sk) => sk,
55 None => {
56 warn!("failed to find a Secretkeeper instance; skipping secret management");
57 return None;
58 }
59 };
60 let (vm_id_db, created) = match VmIdDb::new(PERSISTENT_DIRECTORY) {
61 Ok(v) => v,
62 Err(e) => {
63 error!("skipping secret management, failed to connect to database: {e:?}");
64 return None;
65 }
66 };
67 if created {
68 // If the database did not previously exist, then this appears to be the first run of
69 // `virtualizationservice` since device setup or factory reset. In case of the latter,
70 // delete any secrets that may be left over from before reset, thus ensuring that the
71 // local database state matches that of the TA (i.e. empty).
72 warn!("no existing VM ID DB; clearing any previous secrets to match fresh DB");
73 if let Err(e) = sk.deleteAll() {
74 error!("failed to delete previous secrets, dropping database: {e:?}");
75 vm_id_db.delete_db_file(PERSISTENT_DIRECTORY);
76 return None;
77 }
78 } else {
79 info!("re-using existing VM ID DB");
80 }
81 Some(Self { sk, vm_id_db, batch_size: DELETE_MAX_BATCH_SIZE })
Alan Stokesea1f0462024-02-19 16:25:47 +000082 }
83
David Drysdale79af2662024-02-19 14:50:31 +000084 fn find_sk() -> Option<binder::Strong<dyn ISecretkeeper>> {
85 if let Ok(true) = binder::is_declared(SECRETKEEPER_SERVICE) {
86 match binder::get_interface(SECRETKEEPER_SERVICE) {
87 Ok(sk) => Some(sk),
88 Err(e) => {
89 error!("failed to connect to {SECRETKEEPER_SERVICE}: {e:?}");
90 None
91 }
92 }
93 } else {
94 info!("instance {SECRETKEEPER_SERVICE} not declared");
95 None
96 }
97 }
98
David Drysdale3aa62b32024-03-25 12:31:48 +000099 /// Record a new VM ID. If there is an existing owner (user_id, app_id) for the VM ID,
100 /// it will be replaced.
David Drysdalee64de8e2024-02-29 11:54:29 +0000101 pub fn add_id(&mut self, vm_id: &VmId, user_id: u32, app_id: u32) -> Result<()> {
102 let user_id: i32 = user_id.try_into().context(format!("user_id {user_id} out of range"))?;
103 let app_id: i32 = app_id.try_into().context(format!("app_id {app_id} out of range"))?;
104 self.vm_id_db.add_vm_id(vm_id, user_id, app_id)
105 }
106
David Drysdale79af2662024-02-19 14:50:31 +0000107 /// Delete the VM IDs associated with Android user ID `user_id`.
108 pub fn delete_ids_for_user(&mut self, user_id: i32) -> Result<()> {
109 let vm_ids = self.vm_id_db.vm_ids_for_user(user_id)?;
110 info!(
111 "delete_ids_for_user(user_id={user_id}) triggers deletion of {} secrets",
112 vm_ids.len()
113 );
114 self.delete_ids(&vm_ids);
115 Ok(())
116 }
117
118 /// Delete the VM IDs associated with `(user_id, app_id)`.
119 pub fn delete_ids_for_app(&mut self, user_id: i32, app_id: i32) -> Result<()> {
120 let vm_ids = self.vm_id_db.vm_ids_for_app(user_id, app_id)?;
121 info!(
122 "delete_ids_for_app(user_id={user_id}, app_id={app_id}) removes {} secrets",
123 vm_ids.len()
124 );
125 self.delete_ids(&vm_ids);
126 Ok(())
127 }
128
129 /// Delete the provided VM IDs from both Secretkeeper and the database.
130 pub fn delete_ids(&mut self, mut vm_ids: &[VmId]) {
131 while !vm_ids.is_empty() {
132 let len = std::cmp::min(vm_ids.len(), self.batch_size);
133 let batch = &vm_ids[..len];
134 self.delete_ids_batch(batch);
135 vm_ids = &vm_ids[len..];
136 }
137 }
138
139 /// Delete a batch of VM IDs from both Secretkeeper and the database. The batch is assumed
140 /// to be smaller than both:
141 /// - the corresponding limit for number of database parameters
142 /// - the corresponding limit for maximum size of a single AIDL message for `ISecretkeeper`.
143 fn delete_ids_batch(&mut self, vm_ids: &[VmId]) {
144 let secret_ids: Vec<SecretId> = vm_ids.iter().map(|id| SecretId { id: *id }).collect();
145 if let Err(e) = self.sk.deleteIds(&secret_ids) {
146 error!("failed to delete all secrets from Secretkeeper: {e:?}");
147 }
148 if let Err(e) = self.vm_id_db.delete_vm_ids(vm_ids) {
149 error!("failed to remove secret IDs from database: {e:?}");
150 }
151 }
David Drysdale1138fa02024-03-19 13:06:23 +0000152
153 /// Perform reconciliation to allow for possibly missed notifications of user or app removal.
154 pub fn reconcile(
155 &mut self,
156 callback: &Strong<dyn IVirtualizationReconciliationCallback>,
157 ) -> Result<()> {
158 // First, retrieve all (user_id, app_id) pairs that own a VM.
159 let owners = self.vm_id_db.get_all_owners().context("failed to retrieve owners from DB")?;
160 if owners.is_empty() {
161 info!("no VM owners, nothing to do");
162 return Ok(());
163 }
164
165 // Look for absent users.
166 let mut users: Vec<i32> = owners.iter().map(|(u, _a)| *u).collect();
167 users.sort();
168 users.dedup();
169 let users_exist = callback
170 .doUsersExist(&users)
171 .context(format!("failed to determine if {} users exist", users.len()))?;
172 if users_exist.len() != users.len() {
173 error!("callback returned {} bools for {} inputs!", users_exist.len(), users.len());
174 return Err(anyhow!("unexpected number of results from callback"));
175 }
176
177 for (user_id, present) in users.into_iter().zip(users_exist.into_iter()) {
178 if present {
179 // User is still present, but are all of the associated apps?
180 let mut apps: Vec<i32> = owners
181 .iter()
182 .filter_map(|(u, a)| if *u == user_id { Some(*a) } else { None })
183 .collect();
184 apps.sort();
185 apps.dedup();
186
187 let apps_exist = callback
188 .doAppsExist(user_id, &apps)
189 .context(format!("failed to check apps for user {user_id}"))?;
190 if apps_exist.len() != apps.len() {
191 error!(
192 "callback returned {} bools for {} inputs!",
193 apps_exist.len(),
194 apps.len()
195 );
196 return Err(anyhow!("unexpected number of results from callback"));
197 }
198
199 let missing_apps: Vec<i32> = apps
200 .iter()
201 .zip(apps_exist.into_iter())
202 .filter_map(|(app_id, present)| if present { None } else { Some(*app_id) })
203 .collect();
204
205 for app_id in missing_apps {
206 if core_app_id(app_id) {
207 info!("Skipping deletion for core app {app_id} for user {user_id}");
208 continue;
209 }
210 info!("App {app_id} for user {user_id} absent, deleting associated VM IDs");
211 if let Err(err) = self.delete_ids_for_app(user_id, app_id) {
212 error!("Failed to delete VM ID for user {user_id} app {app_id}: {err:?}");
213 }
214 }
215 } else {
216 info!("user {user_id} no longer present, deleting associated VM IDs");
217 if let Err(err) = self.delete_ids_for_user(user_id) {
218 error!("Failed to delete VM IDs for user {user_id} : {err:?}");
219 }
220 }
221 }
222
223 Ok(())
224 }
David Drysdale79af2662024-02-19 14:50:31 +0000225}
226
227#[cfg(test)]
228mod tests {
229 use super::*;
230 use std::sync::{Arc, Mutex};
231 use android_hardware_security_authgraph::aidl::android::hardware::security::authgraph::{
232 IAuthGraphKeyExchange::IAuthGraphKeyExchange,
233 };
234 use android_hardware_security_secretkeeper::aidl::android::hardware::security::secretkeeper::{
235 ISecretkeeper::BnSecretkeeper
236 };
David Drysdale1138fa02024-03-19 13:06:23 +0000237 use virtualizationmaintenance::IVirtualizationReconciliationCallback::{
238 BnVirtualizationReconciliationCallback
239 };
David Drysdale79af2662024-02-19 14:50:31 +0000240
241 /// Fake implementation of Secretkeeper that keeps a history of what operations were invoked.
242 #[derive(Default)]
243 struct FakeSk {
244 history: Arc<Mutex<Vec<SkOp>>>,
245 }
246
247 #[derive(Clone, PartialEq, Eq, Debug)]
248 enum SkOp {
249 Management,
250 DeleteIds(Vec<VmId>),
251 DeleteAll,
252 }
253
254 impl ISecretkeeper for FakeSk {
255 fn processSecretManagementRequest(&self, _req: &[u8]) -> binder::Result<Vec<u8>> {
256 self.history.lock().unwrap().push(SkOp::Management);
257 Ok(vec![])
258 }
259
260 fn getAuthGraphKe(&self) -> binder::Result<binder::Strong<dyn IAuthGraphKeyExchange>> {
261 unimplemented!()
262 }
263
264 fn deleteIds(&self, ids: &[SecretId]) -> binder::Result<()> {
265 self.history.lock().unwrap().push(SkOp::DeleteIds(ids.iter().map(|s| s.id).collect()));
266 Ok(())
267 }
268
269 fn deleteAll(&self) -> binder::Result<()> {
270 self.history.lock().unwrap().push(SkOp::DeleteAll);
271 Ok(())
272 }
273 }
274 impl binder::Interface for FakeSk {}
275
276 fn new_test_state(history: Arc<Mutex<Vec<SkOp>>>, batch_size: usize) -> State {
277 let vm_id_db = vmdb::new_test_db();
278 let sk = FakeSk { history };
279 let sk = BnSecretkeeper::new_binder(sk, binder::BinderFeatures::default());
280 State { sk, vm_id_db, batch_size }
281 }
282
David Drysdale1138fa02024-03-19 13:06:23 +0000283 struct Reconciliation {
284 gone_users: Vec<i32>,
285 gone_apps: Vec<i32>,
286 }
287
288 impl IVirtualizationReconciliationCallback for Reconciliation {
289 fn doUsersExist(&self, user_ids: &[i32]) -> binder::Result<Vec<bool>> {
290 Ok(user_ids.iter().map(|user_id| !self.gone_users.contains(user_id)).collect())
291 }
292 fn doAppsExist(&self, _user_id: i32, app_ids: &[i32]) -> binder::Result<Vec<bool>> {
293 Ok(app_ids.iter().map(|app_id| !self.gone_apps.contains(app_id)).collect())
294 }
295 }
296 impl binder::Interface for Reconciliation {}
297
David Drysdale79af2662024-02-19 14:50:31 +0000298 const VM_ID1: VmId = [1u8; 64];
299 const VM_ID2: VmId = [2u8; 64];
300 const VM_ID3: VmId = [3u8; 64];
301 const VM_ID4: VmId = [4u8; 64];
302 const VM_ID5: VmId = [5u8; 64];
303
David Drysdale1138fa02024-03-19 13:06:23 +0000304 const USER1: i32 = 1;
305 const USER2: i32 = 2;
306 const USER3: i32 = 3;
307 const APP_A: i32 = 10050;
308 const APP_B: i32 = 10060;
309 const APP_C: i32 = 10070;
310 const CORE_APP_A: i32 = 45;
311
David Drysdale79af2662024-02-19 14:50:31 +0000312 #[test]
313 fn test_sk_state_batching() {
314 let history = Arc::new(Mutex::new(Vec::new()));
315 let mut sk_state = new_test_state(history.clone(), 2);
316 sk_state.delete_ids(&[VM_ID1, VM_ID2, VM_ID3, VM_ID4, VM_ID5]);
317 let got = (*history.lock().unwrap()).clone();
318 assert_eq!(
319 got,
320 vec![
321 SkOp::DeleteIds(vec![VM_ID1, VM_ID2]),
322 SkOp::DeleteIds(vec![VM_ID3, VM_ID4]),
323 SkOp::DeleteIds(vec![VM_ID5]),
324 ]
325 );
326 }
327
328 #[test]
329 fn test_sk_state_no_batching() {
330 let history = Arc::new(Mutex::new(Vec::new()));
331 let mut sk_state = new_test_state(history.clone(), 6);
332 sk_state.delete_ids(&[VM_ID1, VM_ID2, VM_ID3, VM_ID4, VM_ID5]);
333 let got = (*history.lock().unwrap()).clone();
334 assert_eq!(got, vec![SkOp::DeleteIds(vec![VM_ID1, VM_ID2, VM_ID3, VM_ID4, VM_ID5])]);
335 }
336
337 #[test]
338 fn test_sk_state() {
David Drysdale79af2662024-02-19 14:50:31 +0000339 let history = Arc::new(Mutex::new(Vec::new()));
340 let mut sk_state = new_test_state(history.clone(), 2);
341
342 sk_state.vm_id_db.add_vm_id(&VM_ID1, USER1, APP_A).unwrap();
343 sk_state.vm_id_db.add_vm_id(&VM_ID2, USER1, APP_A).unwrap();
344 sk_state.vm_id_db.add_vm_id(&VM_ID3, USER2, APP_B).unwrap();
345 sk_state.vm_id_db.add_vm_id(&VM_ID4, USER3, APP_A).unwrap();
David Drysdale1138fa02024-03-19 13:06:23 +0000346 sk_state.vm_id_db.add_vm_id(&VM_ID5, USER3, APP_C).unwrap(); // Overwrites APP_A
David Drysdale79af2662024-02-19 14:50:31 +0000347 assert_eq!((*history.lock().unwrap()).clone(), vec![]);
348
349 sk_state.delete_ids_for_app(USER2, APP_B).unwrap();
350 assert_eq!((*history.lock().unwrap()).clone(), vec![SkOp::DeleteIds(vec![VM_ID3])]);
351
352 sk_state.delete_ids_for_user(USER3).unwrap();
353 assert_eq!(
354 (*history.lock().unwrap()).clone(),
355 vec![SkOp::DeleteIds(vec![VM_ID3]), SkOp::DeleteIds(vec![VM_ID4, VM_ID5]),]
356 );
357
358 assert_eq!(vec![VM_ID1, VM_ID2], sk_state.vm_id_db.vm_ids_for_user(USER1).unwrap());
359 assert_eq!(vec![VM_ID1, VM_ID2], sk_state.vm_id_db.vm_ids_for_app(USER1, APP_A).unwrap());
360 let empty: Vec<VmId> = Vec::new();
361 assert_eq!(empty, sk_state.vm_id_db.vm_ids_for_app(USER2, APP_B).unwrap());
362 assert_eq!(empty, sk_state.vm_id_db.vm_ids_for_user(USER3).unwrap());
Alan Stokesea1f0462024-02-19 16:25:47 +0000363 }
David Drysdale1138fa02024-03-19 13:06:23 +0000364
365 #[test]
366 fn test_sk_state_reconcile() {
367 let history = Arc::new(Mutex::new(Vec::new()));
368 let mut sk_state = new_test_state(history.clone(), 20);
369
370 sk_state.vm_id_db.add_vm_id(&VM_ID1, USER1, APP_A).unwrap();
371 sk_state.vm_id_db.add_vm_id(&VM_ID2, USER1, APP_A).unwrap();
372 sk_state.vm_id_db.add_vm_id(&VM_ID3, USER2, APP_B).unwrap();
373 sk_state.vm_id_db.add_vm_id(&VM_ID4, USER2, CORE_APP_A).unwrap();
374 sk_state.vm_id_db.add_vm_id(&VM_ID5, USER3, APP_C).unwrap();
375
376 assert_eq!(vec![VM_ID1, VM_ID2], sk_state.vm_id_db.vm_ids_for_user(USER1).unwrap());
377 assert_eq!(vec![VM_ID1, VM_ID2], sk_state.vm_id_db.vm_ids_for_app(USER1, APP_A).unwrap());
378 assert_eq!(vec![VM_ID3], sk_state.vm_id_db.vm_ids_for_app(USER2, APP_B).unwrap());
379 assert_eq!(vec![VM_ID5], sk_state.vm_id_db.vm_ids_for_user(USER3).unwrap());
380
381 // Perform a reconciliation and pretend that USER1 and [CORE_APP_A, APP_B] are gone.
382 let reconciliation =
383 Reconciliation { gone_users: vec![USER1], gone_apps: vec![CORE_APP_A, APP_B] };
384 let callback = BnVirtualizationReconciliationCallback::new_binder(
385 reconciliation,
386 binder::BinderFeatures::default(),
387 );
388 sk_state.reconcile(&callback).unwrap();
389
390 let empty: Vec<VmId> = Vec::new();
391 assert_eq!(empty, sk_state.vm_id_db.vm_ids_for_user(USER1).unwrap());
392 assert_eq!(empty, sk_state.vm_id_db.vm_ids_for_app(USER1, APP_A).unwrap());
393 // VM for core app stays even though it's reported as absent.
394 assert_eq!(vec![VM_ID4], sk_state.vm_id_db.vm_ids_for_user(USER2).unwrap());
395 assert_eq!(empty, sk_state.vm_id_db.vm_ids_for_app(USER2, APP_B).unwrap());
396 assert_eq!(vec![VM_ID5], sk_state.vm_id_db.vm_ids_for_user(USER3).unwrap());
397 }
398
399 struct Irreconcilable;
400
401 impl IVirtualizationReconciliationCallback for Irreconcilable {
402 fn doUsersExist(&self, user_ids: &[i32]) -> binder::Result<Vec<bool>> {
403 panic!("doUsersExist called with {user_ids:?}");
404 }
405 fn doAppsExist(&self, user_id: i32, app_ids: &[i32]) -> binder::Result<Vec<bool>> {
406 panic!("doAppsExist called with {user_id:?}, {app_ids:?}");
407 }
408 }
409 impl binder::Interface for Irreconcilable {}
410
411 #[test]
412 fn test_sk_state_reconcile_not_needed() {
413 let history = Arc::new(Mutex::new(Vec::new()));
414 let mut sk_state = new_test_state(history.clone(), 20);
415
416 sk_state.vm_id_db.add_vm_id(&VM_ID1, USER1, APP_A).unwrap();
417 sk_state.vm_id_db.add_vm_id(&VM_ID2, USER1, APP_A).unwrap();
418 sk_state.vm_id_db.add_vm_id(&VM_ID3, USER2, APP_B).unwrap();
419 sk_state.vm_id_db.add_vm_id(&VM_ID5, USER3, APP_C).unwrap();
420 sk_state.delete_ids_for_user(USER1).unwrap();
421 sk_state.delete_ids_for_user(USER2).unwrap();
422 sk_state.delete_ids_for_user(USER3).unwrap();
423
424 // No extant secrets, so reconciliation should not trigger the callback.
425 let callback = BnVirtualizationReconciliationCallback::new_binder(
426 Irreconcilable,
427 binder::BinderFeatures::default(),
428 );
429 sk_state.reconcile(&callback).unwrap();
430 }
Alan Stokesea1f0462024-02-19 16:25:47 +0000431}