blob: 219df7de1cd0d1cada3332f730dd95078ac13e2c [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 Drysdalee64de8e2024-02-29 11:54:29 +000099 /// Record a new VM ID.
100 pub fn add_id(&mut self, vm_id: &VmId, user_id: u32, app_id: u32) -> Result<()> {
101 let user_id: i32 = user_id.try_into().context(format!("user_id {user_id} out of range"))?;
102 let app_id: i32 = app_id.try_into().context(format!("app_id {app_id} out of range"))?;
103 self.vm_id_db.add_vm_id(vm_id, user_id, app_id)
104 }
105
David Drysdale79af2662024-02-19 14:50:31 +0000106 /// Delete the VM IDs associated with Android user ID `user_id`.
107 pub fn delete_ids_for_user(&mut self, user_id: i32) -> Result<()> {
108 let vm_ids = self.vm_id_db.vm_ids_for_user(user_id)?;
109 info!(
110 "delete_ids_for_user(user_id={user_id}) triggers deletion of {} secrets",
111 vm_ids.len()
112 );
113 self.delete_ids(&vm_ids);
114 Ok(())
115 }
116
117 /// Delete the VM IDs associated with `(user_id, app_id)`.
118 pub fn delete_ids_for_app(&mut self, user_id: i32, app_id: i32) -> Result<()> {
119 let vm_ids = self.vm_id_db.vm_ids_for_app(user_id, app_id)?;
120 info!(
121 "delete_ids_for_app(user_id={user_id}, app_id={app_id}) removes {} secrets",
122 vm_ids.len()
123 );
124 self.delete_ids(&vm_ids);
125 Ok(())
126 }
127
128 /// Delete the provided VM IDs from both Secretkeeper and the database.
129 pub fn delete_ids(&mut self, mut vm_ids: &[VmId]) {
130 while !vm_ids.is_empty() {
131 let len = std::cmp::min(vm_ids.len(), self.batch_size);
132 let batch = &vm_ids[..len];
133 self.delete_ids_batch(batch);
134 vm_ids = &vm_ids[len..];
135 }
136 }
137
138 /// Delete a batch of VM IDs from both Secretkeeper and the database. The batch is assumed
139 /// to be smaller than both:
140 /// - the corresponding limit for number of database parameters
141 /// - the corresponding limit for maximum size of a single AIDL message for `ISecretkeeper`.
142 fn delete_ids_batch(&mut self, vm_ids: &[VmId]) {
143 let secret_ids: Vec<SecretId> = vm_ids.iter().map(|id| SecretId { id: *id }).collect();
144 if let Err(e) = self.sk.deleteIds(&secret_ids) {
145 error!("failed to delete all secrets from Secretkeeper: {e:?}");
146 }
147 if let Err(e) = self.vm_id_db.delete_vm_ids(vm_ids) {
148 error!("failed to remove secret IDs from database: {e:?}");
149 }
150 }
David Drysdale1138fa02024-03-19 13:06:23 +0000151
152 /// Perform reconciliation to allow for possibly missed notifications of user or app removal.
153 pub fn reconcile(
154 &mut self,
155 callback: &Strong<dyn IVirtualizationReconciliationCallback>,
156 ) -> Result<()> {
157 // First, retrieve all (user_id, app_id) pairs that own a VM.
158 let owners = self.vm_id_db.get_all_owners().context("failed to retrieve owners from DB")?;
159 if owners.is_empty() {
160 info!("no VM owners, nothing to do");
161 return Ok(());
162 }
163
164 // Look for absent users.
165 let mut users: Vec<i32> = owners.iter().map(|(u, _a)| *u).collect();
166 users.sort();
167 users.dedup();
168 let users_exist = callback
169 .doUsersExist(&users)
170 .context(format!("failed to determine if {} users exist", users.len()))?;
171 if users_exist.len() != users.len() {
172 error!("callback returned {} bools for {} inputs!", users_exist.len(), users.len());
173 return Err(anyhow!("unexpected number of results from callback"));
174 }
175
176 for (user_id, present) in users.into_iter().zip(users_exist.into_iter()) {
177 if present {
178 // User is still present, but are all of the associated apps?
179 let mut apps: Vec<i32> = owners
180 .iter()
181 .filter_map(|(u, a)| if *u == user_id { Some(*a) } else { None })
182 .collect();
183 apps.sort();
184 apps.dedup();
185
186 let apps_exist = callback
187 .doAppsExist(user_id, &apps)
188 .context(format!("failed to check apps for user {user_id}"))?;
189 if apps_exist.len() != apps.len() {
190 error!(
191 "callback returned {} bools for {} inputs!",
192 apps_exist.len(),
193 apps.len()
194 );
195 return Err(anyhow!("unexpected number of results from callback"));
196 }
197
198 let missing_apps: Vec<i32> = apps
199 .iter()
200 .zip(apps_exist.into_iter())
201 .filter_map(|(app_id, present)| if present { None } else { Some(*app_id) })
202 .collect();
203
204 for app_id in missing_apps {
205 if core_app_id(app_id) {
206 info!("Skipping deletion for core app {app_id} for user {user_id}");
207 continue;
208 }
209 info!("App {app_id} for user {user_id} absent, deleting associated VM IDs");
210 if let Err(err) = self.delete_ids_for_app(user_id, app_id) {
211 error!("Failed to delete VM ID for user {user_id} app {app_id}: {err:?}");
212 }
213 }
214 } else {
215 info!("user {user_id} no longer present, deleting associated VM IDs");
216 if let Err(err) = self.delete_ids_for_user(user_id) {
217 error!("Failed to delete VM IDs for user {user_id} : {err:?}");
218 }
219 }
220 }
221
222 Ok(())
223 }
David Drysdale79af2662024-02-19 14:50:31 +0000224}
225
226#[cfg(test)]
227mod tests {
228 use super::*;
229 use std::sync::{Arc, Mutex};
230 use android_hardware_security_authgraph::aidl::android::hardware::security::authgraph::{
231 IAuthGraphKeyExchange::IAuthGraphKeyExchange,
232 };
233 use android_hardware_security_secretkeeper::aidl::android::hardware::security::secretkeeper::{
234 ISecretkeeper::BnSecretkeeper
235 };
David Drysdale1138fa02024-03-19 13:06:23 +0000236 use virtualizationmaintenance::IVirtualizationReconciliationCallback::{
237 BnVirtualizationReconciliationCallback
238 };
David Drysdale79af2662024-02-19 14:50:31 +0000239
240 /// Fake implementation of Secretkeeper that keeps a history of what operations were invoked.
241 #[derive(Default)]
242 struct FakeSk {
243 history: Arc<Mutex<Vec<SkOp>>>,
244 }
245
246 #[derive(Clone, PartialEq, Eq, Debug)]
247 enum SkOp {
248 Management,
249 DeleteIds(Vec<VmId>),
250 DeleteAll,
251 }
252
253 impl ISecretkeeper for FakeSk {
254 fn processSecretManagementRequest(&self, _req: &[u8]) -> binder::Result<Vec<u8>> {
255 self.history.lock().unwrap().push(SkOp::Management);
256 Ok(vec![])
257 }
258
259 fn getAuthGraphKe(&self) -> binder::Result<binder::Strong<dyn IAuthGraphKeyExchange>> {
260 unimplemented!()
261 }
262
263 fn deleteIds(&self, ids: &[SecretId]) -> binder::Result<()> {
264 self.history.lock().unwrap().push(SkOp::DeleteIds(ids.iter().map(|s| s.id).collect()));
265 Ok(())
266 }
267
268 fn deleteAll(&self) -> binder::Result<()> {
269 self.history.lock().unwrap().push(SkOp::DeleteAll);
270 Ok(())
271 }
272 }
273 impl binder::Interface for FakeSk {}
274
275 fn new_test_state(history: Arc<Mutex<Vec<SkOp>>>, batch_size: usize) -> State {
276 let vm_id_db = vmdb::new_test_db();
277 let sk = FakeSk { history };
278 let sk = BnSecretkeeper::new_binder(sk, binder::BinderFeatures::default());
279 State { sk, vm_id_db, batch_size }
280 }
281
David Drysdale1138fa02024-03-19 13:06:23 +0000282 struct Reconciliation {
283 gone_users: Vec<i32>,
284 gone_apps: Vec<i32>,
285 }
286
287 impl IVirtualizationReconciliationCallback for Reconciliation {
288 fn doUsersExist(&self, user_ids: &[i32]) -> binder::Result<Vec<bool>> {
289 Ok(user_ids.iter().map(|user_id| !self.gone_users.contains(user_id)).collect())
290 }
291 fn doAppsExist(&self, _user_id: i32, app_ids: &[i32]) -> binder::Result<Vec<bool>> {
292 Ok(app_ids.iter().map(|app_id| !self.gone_apps.contains(app_id)).collect())
293 }
294 }
295 impl binder::Interface for Reconciliation {}
296
David Drysdale79af2662024-02-19 14:50:31 +0000297 const VM_ID1: VmId = [1u8; 64];
298 const VM_ID2: VmId = [2u8; 64];
299 const VM_ID3: VmId = [3u8; 64];
300 const VM_ID4: VmId = [4u8; 64];
301 const VM_ID5: VmId = [5u8; 64];
302
David Drysdale1138fa02024-03-19 13:06:23 +0000303 const USER1: i32 = 1;
304 const USER2: i32 = 2;
305 const USER3: i32 = 3;
306 const APP_A: i32 = 10050;
307 const APP_B: i32 = 10060;
308 const APP_C: i32 = 10070;
309 const CORE_APP_A: i32 = 45;
310
David Drysdale79af2662024-02-19 14:50:31 +0000311 #[test]
312 fn test_sk_state_batching() {
313 let history = Arc::new(Mutex::new(Vec::new()));
314 let mut sk_state = new_test_state(history.clone(), 2);
315 sk_state.delete_ids(&[VM_ID1, VM_ID2, VM_ID3, VM_ID4, VM_ID5]);
316 let got = (*history.lock().unwrap()).clone();
317 assert_eq!(
318 got,
319 vec![
320 SkOp::DeleteIds(vec![VM_ID1, VM_ID2]),
321 SkOp::DeleteIds(vec![VM_ID3, VM_ID4]),
322 SkOp::DeleteIds(vec![VM_ID5]),
323 ]
324 );
325 }
326
327 #[test]
328 fn test_sk_state_no_batching() {
329 let history = Arc::new(Mutex::new(Vec::new()));
330 let mut sk_state = new_test_state(history.clone(), 6);
331 sk_state.delete_ids(&[VM_ID1, VM_ID2, VM_ID3, VM_ID4, VM_ID5]);
332 let got = (*history.lock().unwrap()).clone();
333 assert_eq!(got, vec![SkOp::DeleteIds(vec![VM_ID1, VM_ID2, VM_ID3, VM_ID4, VM_ID5])]);
334 }
335
336 #[test]
337 fn test_sk_state() {
David Drysdale79af2662024-02-19 14:50:31 +0000338 let history = Arc::new(Mutex::new(Vec::new()));
339 let mut sk_state = new_test_state(history.clone(), 2);
340
341 sk_state.vm_id_db.add_vm_id(&VM_ID1, USER1, APP_A).unwrap();
342 sk_state.vm_id_db.add_vm_id(&VM_ID2, USER1, APP_A).unwrap();
343 sk_state.vm_id_db.add_vm_id(&VM_ID3, USER2, APP_B).unwrap();
344 sk_state.vm_id_db.add_vm_id(&VM_ID4, USER3, APP_A).unwrap();
David Drysdale1138fa02024-03-19 13:06:23 +0000345 sk_state.vm_id_db.add_vm_id(&VM_ID5, USER3, APP_C).unwrap(); // Overwrites APP_A
David Drysdale79af2662024-02-19 14:50:31 +0000346 assert_eq!((*history.lock().unwrap()).clone(), vec![]);
347
348 sk_state.delete_ids_for_app(USER2, APP_B).unwrap();
349 assert_eq!((*history.lock().unwrap()).clone(), vec![SkOp::DeleteIds(vec![VM_ID3])]);
350
351 sk_state.delete_ids_for_user(USER3).unwrap();
352 assert_eq!(
353 (*history.lock().unwrap()).clone(),
354 vec![SkOp::DeleteIds(vec![VM_ID3]), SkOp::DeleteIds(vec![VM_ID4, VM_ID5]),]
355 );
356
357 assert_eq!(vec![VM_ID1, VM_ID2], sk_state.vm_id_db.vm_ids_for_user(USER1).unwrap());
358 assert_eq!(vec![VM_ID1, VM_ID2], sk_state.vm_id_db.vm_ids_for_app(USER1, APP_A).unwrap());
359 let empty: Vec<VmId> = Vec::new();
360 assert_eq!(empty, sk_state.vm_id_db.vm_ids_for_app(USER2, APP_B).unwrap());
361 assert_eq!(empty, sk_state.vm_id_db.vm_ids_for_user(USER3).unwrap());
Alan Stokesea1f0462024-02-19 16:25:47 +0000362 }
David Drysdale1138fa02024-03-19 13:06:23 +0000363
364 #[test]
365 fn test_sk_state_reconcile() {
366 let history = Arc::new(Mutex::new(Vec::new()));
367 let mut sk_state = new_test_state(history.clone(), 20);
368
369 sk_state.vm_id_db.add_vm_id(&VM_ID1, USER1, APP_A).unwrap();
370 sk_state.vm_id_db.add_vm_id(&VM_ID2, USER1, APP_A).unwrap();
371 sk_state.vm_id_db.add_vm_id(&VM_ID3, USER2, APP_B).unwrap();
372 sk_state.vm_id_db.add_vm_id(&VM_ID4, USER2, CORE_APP_A).unwrap();
373 sk_state.vm_id_db.add_vm_id(&VM_ID5, USER3, APP_C).unwrap();
374
375 assert_eq!(vec![VM_ID1, VM_ID2], sk_state.vm_id_db.vm_ids_for_user(USER1).unwrap());
376 assert_eq!(vec![VM_ID1, VM_ID2], sk_state.vm_id_db.vm_ids_for_app(USER1, APP_A).unwrap());
377 assert_eq!(vec![VM_ID3], sk_state.vm_id_db.vm_ids_for_app(USER2, APP_B).unwrap());
378 assert_eq!(vec![VM_ID5], sk_state.vm_id_db.vm_ids_for_user(USER3).unwrap());
379
380 // Perform a reconciliation and pretend that USER1 and [CORE_APP_A, APP_B] are gone.
381 let reconciliation =
382 Reconciliation { gone_users: vec![USER1], gone_apps: vec![CORE_APP_A, APP_B] };
383 let callback = BnVirtualizationReconciliationCallback::new_binder(
384 reconciliation,
385 binder::BinderFeatures::default(),
386 );
387 sk_state.reconcile(&callback).unwrap();
388
389 let empty: Vec<VmId> = Vec::new();
390 assert_eq!(empty, sk_state.vm_id_db.vm_ids_for_user(USER1).unwrap());
391 assert_eq!(empty, sk_state.vm_id_db.vm_ids_for_app(USER1, APP_A).unwrap());
392 // VM for core app stays even though it's reported as absent.
393 assert_eq!(vec![VM_ID4], sk_state.vm_id_db.vm_ids_for_user(USER2).unwrap());
394 assert_eq!(empty, sk_state.vm_id_db.vm_ids_for_app(USER2, APP_B).unwrap());
395 assert_eq!(vec![VM_ID5], sk_state.vm_id_db.vm_ids_for_user(USER3).unwrap());
396 }
397
398 struct Irreconcilable;
399
400 impl IVirtualizationReconciliationCallback for Irreconcilable {
401 fn doUsersExist(&self, user_ids: &[i32]) -> binder::Result<Vec<bool>> {
402 panic!("doUsersExist called with {user_ids:?}");
403 }
404 fn doAppsExist(&self, user_id: i32, app_ids: &[i32]) -> binder::Result<Vec<bool>> {
405 panic!("doAppsExist called with {user_id:?}, {app_ids:?}");
406 }
407 }
408 impl binder::Interface for Irreconcilable {}
409
410 #[test]
411 fn test_sk_state_reconcile_not_needed() {
412 let history = Arc::new(Mutex::new(Vec::new()));
413 let mut sk_state = new_test_state(history.clone(), 20);
414
415 sk_state.vm_id_db.add_vm_id(&VM_ID1, USER1, APP_A).unwrap();
416 sk_state.vm_id_db.add_vm_id(&VM_ID2, USER1, APP_A).unwrap();
417 sk_state.vm_id_db.add_vm_id(&VM_ID3, USER2, APP_B).unwrap();
418 sk_state.vm_id_db.add_vm_id(&VM_ID5, USER3, APP_C).unwrap();
419 sk_state.delete_ids_for_user(USER1).unwrap();
420 sk_state.delete_ids_for_user(USER2).unwrap();
421 sk_state.delete_ids_for_user(USER3).unwrap();
422
423 // No extant secrets, so reconciliation should not trigger the callback.
424 let callback = BnVirtualizationReconciliationCallback::new_binder(
425 Irreconcilable,
426 binder::BinderFeatures::default(),
427 );
428 sk_state.reconcile(&callback).unwrap();
429 }
Alan Stokesea1f0462024-02-19 16:25:47 +0000430}