xref: /aosp_15_r20/system/security/keystore2/src/gc.rs (revision e1997b9af69e3155ead6e072d106a0077849ffba)
1*e1997b9aSAndroid Build Coastguard Worker // Copyright 2020, The Android Open Source Project
2*e1997b9aSAndroid Build Coastguard Worker //
3*e1997b9aSAndroid Build Coastguard Worker // Licensed under the Apache License, Version 2.0 (the "License");
4*e1997b9aSAndroid Build Coastguard Worker // you may not use this file except in compliance with the License.
5*e1997b9aSAndroid Build Coastguard Worker // You may obtain a copy of the License at
6*e1997b9aSAndroid Build Coastguard Worker //
7*e1997b9aSAndroid Build Coastguard Worker //     http://www.apache.org/licenses/LICENSE-2.0
8*e1997b9aSAndroid Build Coastguard Worker //
9*e1997b9aSAndroid Build Coastguard Worker // Unless required by applicable law or agreed to in writing, software
10*e1997b9aSAndroid Build Coastguard Worker // distributed under the License is distributed on an "AS IS" BASIS,
11*e1997b9aSAndroid Build Coastguard Worker // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12*e1997b9aSAndroid Build Coastguard Worker // See the License for the specific language governing permissions and
13*e1997b9aSAndroid Build Coastguard Worker // limitations under the License.
14*e1997b9aSAndroid Build Coastguard Worker 
15*e1997b9aSAndroid Build Coastguard Worker //! This module implements the key garbage collector.
16*e1997b9aSAndroid Build Coastguard Worker //! The key garbage collector has one public function `notify_gc()`. This will create
17*e1997b9aSAndroid Build Coastguard Worker //! a thread on demand which will query the database for unreferenced key entries,
18*e1997b9aSAndroid Build Coastguard Worker //! optionally dispose of sensitive key material appropriately, and then delete
19*e1997b9aSAndroid Build Coastguard Worker //! the key entry from the database.
20*e1997b9aSAndroid Build Coastguard Worker 
21*e1997b9aSAndroid Build Coastguard Worker use crate::ks_err;
22*e1997b9aSAndroid Build Coastguard Worker use crate::{
23*e1997b9aSAndroid Build Coastguard Worker     async_task,
24*e1997b9aSAndroid Build Coastguard Worker     database::{KeystoreDB, SupersededBlob, Uuid},
25*e1997b9aSAndroid Build Coastguard Worker     super_key::SuperKeyManager,
26*e1997b9aSAndroid Build Coastguard Worker };
27*e1997b9aSAndroid Build Coastguard Worker use anyhow::{Context, Result};
28*e1997b9aSAndroid Build Coastguard Worker use async_task::AsyncTask;
29*e1997b9aSAndroid Build Coastguard Worker use std::sync::{
30*e1997b9aSAndroid Build Coastguard Worker     atomic::{AtomicU8, Ordering},
31*e1997b9aSAndroid Build Coastguard Worker     Arc, RwLock,
32*e1997b9aSAndroid Build Coastguard Worker };
33*e1997b9aSAndroid Build Coastguard Worker 
34*e1997b9aSAndroid Build Coastguard Worker pub struct Gc {
35*e1997b9aSAndroid Build Coastguard Worker     async_task: Arc<AsyncTask>,
36*e1997b9aSAndroid Build Coastguard Worker     notified: Arc<AtomicU8>,
37*e1997b9aSAndroid Build Coastguard Worker }
38*e1997b9aSAndroid Build Coastguard Worker 
39*e1997b9aSAndroid Build Coastguard Worker impl Gc {
40*e1997b9aSAndroid Build Coastguard Worker     /// Creates a garbage collector using the given async_task.
41*e1997b9aSAndroid Build Coastguard Worker     /// The garbage collector needs a function to invalidate key blobs, a database connection,
42*e1997b9aSAndroid Build Coastguard Worker     /// and a reference to the `SuperKeyManager`. They are obtained from the init function.
43*e1997b9aSAndroid Build Coastguard Worker     /// The function is only called if this is first time a garbage collector was initialized
44*e1997b9aSAndroid Build Coastguard Worker     /// with the given AsyncTask instance.
45*e1997b9aSAndroid Build Coastguard Worker     /// Note: It is a logical error to initialize different Gc instances with the same `AsyncTask`.
new_init_with<F>(async_task: Arc<AsyncTask>, init: F) -> Self where F: FnOnce() -> ( Box<dyn Fn(&Uuid, &[u8]) -> Result<()> + Send + 'static>, KeystoreDB, Arc<RwLock<SuperKeyManager>>, ) + Send + 'static,46*e1997b9aSAndroid Build Coastguard Worker     pub fn new_init_with<F>(async_task: Arc<AsyncTask>, init: F) -> Self
47*e1997b9aSAndroid Build Coastguard Worker     where
48*e1997b9aSAndroid Build Coastguard Worker         F: FnOnce() -> (
49*e1997b9aSAndroid Build Coastguard Worker                 Box<dyn Fn(&Uuid, &[u8]) -> Result<()> + Send + 'static>,
50*e1997b9aSAndroid Build Coastguard Worker                 KeystoreDB,
51*e1997b9aSAndroid Build Coastguard Worker                 Arc<RwLock<SuperKeyManager>>,
52*e1997b9aSAndroid Build Coastguard Worker             ) + Send
53*e1997b9aSAndroid Build Coastguard Worker             + 'static,
54*e1997b9aSAndroid Build Coastguard Worker     {
55*e1997b9aSAndroid Build Coastguard Worker         let weak_at = Arc::downgrade(&async_task);
56*e1997b9aSAndroid Build Coastguard Worker         let notified = Arc::new(AtomicU8::new(0));
57*e1997b9aSAndroid Build Coastguard Worker         let notified_clone = notified.clone();
58*e1997b9aSAndroid Build Coastguard Worker         // Initialize the task's shelf.
59*e1997b9aSAndroid Build Coastguard Worker         async_task.queue_hi(move |shelf| {
60*e1997b9aSAndroid Build Coastguard Worker             let (invalidate_key, db, super_key) = init();
61*e1997b9aSAndroid Build Coastguard Worker             let notified = notified_clone;
62*e1997b9aSAndroid Build Coastguard Worker             shelf.get_or_put_with(|| GcInternal {
63*e1997b9aSAndroid Build Coastguard Worker                 deleted_blob_ids: vec![],
64*e1997b9aSAndroid Build Coastguard Worker                 superseded_blobs: vec![],
65*e1997b9aSAndroid Build Coastguard Worker                 invalidate_key,
66*e1997b9aSAndroid Build Coastguard Worker                 db,
67*e1997b9aSAndroid Build Coastguard Worker                 async_task: weak_at,
68*e1997b9aSAndroid Build Coastguard Worker                 super_key,
69*e1997b9aSAndroid Build Coastguard Worker                 notified,
70*e1997b9aSAndroid Build Coastguard Worker             });
71*e1997b9aSAndroid Build Coastguard Worker         });
72*e1997b9aSAndroid Build Coastguard Worker         Self { async_task, notified }
73*e1997b9aSAndroid Build Coastguard Worker     }
74*e1997b9aSAndroid Build Coastguard Worker 
75*e1997b9aSAndroid Build Coastguard Worker     /// Notifies the key garbage collector to iterate through orphaned and superseded blobs and
76*e1997b9aSAndroid Build Coastguard Worker     /// attempts their deletion. We only process one key at a time and then schedule another
77*e1997b9aSAndroid Build Coastguard Worker     /// attempt by queueing it in the async_task (low priority) queue.
notify_gc(&self)78*e1997b9aSAndroid Build Coastguard Worker     pub fn notify_gc(&self) {
79*e1997b9aSAndroid Build Coastguard Worker         if let Ok(0) = self.notified.compare_exchange(0, 1, Ordering::Relaxed, Ordering::Relaxed) {
80*e1997b9aSAndroid Build Coastguard Worker             self.async_task.queue_lo(|shelf| shelf.get_downcast_mut::<GcInternal>().unwrap().step())
81*e1997b9aSAndroid Build Coastguard Worker         }
82*e1997b9aSAndroid Build Coastguard Worker     }
83*e1997b9aSAndroid Build Coastguard Worker }
84*e1997b9aSAndroid Build Coastguard Worker 
85*e1997b9aSAndroid Build Coastguard Worker struct GcInternal {
86*e1997b9aSAndroid Build Coastguard Worker     deleted_blob_ids: Vec<i64>,
87*e1997b9aSAndroid Build Coastguard Worker     superseded_blobs: Vec<SupersededBlob>,
88*e1997b9aSAndroid Build Coastguard Worker     invalidate_key: Box<dyn Fn(&Uuid, &[u8]) -> Result<()> + Send + 'static>,
89*e1997b9aSAndroid Build Coastguard Worker     db: KeystoreDB,
90*e1997b9aSAndroid Build Coastguard Worker     async_task: std::sync::Weak<AsyncTask>,
91*e1997b9aSAndroid Build Coastguard Worker     super_key: Arc<RwLock<SuperKeyManager>>,
92*e1997b9aSAndroid Build Coastguard Worker     notified: Arc<AtomicU8>,
93*e1997b9aSAndroid Build Coastguard Worker }
94*e1997b9aSAndroid Build Coastguard Worker 
95*e1997b9aSAndroid Build Coastguard Worker impl GcInternal {
96*e1997b9aSAndroid Build Coastguard Worker     /// Attempts to process one blob from the database.
97*e1997b9aSAndroid Build Coastguard Worker     /// We process one key at a time, because deleting a key is a time consuming process which
98*e1997b9aSAndroid Build Coastguard Worker     /// may involve calling into the KeyMint backend and we don't want to hog neither the backend
99*e1997b9aSAndroid Build Coastguard Worker     /// nor the database for extended periods of time.
100*e1997b9aSAndroid Build Coastguard Worker     /// To limit the number of database transactions, which are also expensive and competing
101*e1997b9aSAndroid Build Coastguard Worker     /// with threads on the critical path, deleted blobs are loaded in batches.
process_one_key(&mut self) -> Result<()>102*e1997b9aSAndroid Build Coastguard Worker     fn process_one_key(&mut self) -> Result<()> {
103*e1997b9aSAndroid Build Coastguard Worker         if self.superseded_blobs.is_empty() {
104*e1997b9aSAndroid Build Coastguard Worker             let blobs = self
105*e1997b9aSAndroid Build Coastguard Worker                 .db
106*e1997b9aSAndroid Build Coastguard Worker                 .handle_next_superseded_blobs(&self.deleted_blob_ids, 20)
107*e1997b9aSAndroid Build Coastguard Worker                 .context(ks_err!("Trying to handle superseded blob."))?;
108*e1997b9aSAndroid Build Coastguard Worker             self.deleted_blob_ids = vec![];
109*e1997b9aSAndroid Build Coastguard Worker             self.superseded_blobs = blobs;
110*e1997b9aSAndroid Build Coastguard Worker         }
111*e1997b9aSAndroid Build Coastguard Worker 
112*e1997b9aSAndroid Build Coastguard Worker         if let Some(SupersededBlob { blob_id, blob, metadata }) = self.superseded_blobs.pop() {
113*e1997b9aSAndroid Build Coastguard Worker             // Add the next blob_id to the deleted blob ids list. So it will be
114*e1997b9aSAndroid Build Coastguard Worker             // removed from the database regardless of whether the following
115*e1997b9aSAndroid Build Coastguard Worker             // succeeds or not.
116*e1997b9aSAndroid Build Coastguard Worker             self.deleted_blob_ids.push(blob_id);
117*e1997b9aSAndroid Build Coastguard Worker 
118*e1997b9aSAndroid Build Coastguard Worker             // If the key has a km_uuid we try to get the corresponding device
119*e1997b9aSAndroid Build Coastguard Worker             // and delete the key, unwrapping if necessary and possible.
120*e1997b9aSAndroid Build Coastguard Worker             // (At this time keys may get deleted without having the super encryption
121*e1997b9aSAndroid Build Coastguard Worker             // key in this case we can only delete the key from the database.)
122*e1997b9aSAndroid Build Coastguard Worker             if let Some(uuid) = metadata.km_uuid() {
123*e1997b9aSAndroid Build Coastguard Worker                 let blob = self
124*e1997b9aSAndroid Build Coastguard Worker                     .super_key
125*e1997b9aSAndroid Build Coastguard Worker                     .read()
126*e1997b9aSAndroid Build Coastguard Worker                     .unwrap()
127*e1997b9aSAndroid Build Coastguard Worker                     .unwrap_key_if_required(&metadata, &blob)
128*e1997b9aSAndroid Build Coastguard Worker                     .context(ks_err!("Trying to unwrap to-be-deleted blob.",))?;
129*e1997b9aSAndroid Build Coastguard Worker                 (self.invalidate_key)(uuid, &blob).context(ks_err!("Trying to invalidate key."))?;
130*e1997b9aSAndroid Build Coastguard Worker             }
131*e1997b9aSAndroid Build Coastguard Worker         }
132*e1997b9aSAndroid Build Coastguard Worker         Ok(())
133*e1997b9aSAndroid Build Coastguard Worker     }
134*e1997b9aSAndroid Build Coastguard Worker 
135*e1997b9aSAndroid Build Coastguard Worker     /// Processes one key and then schedules another attempt until it runs out of blobs to delete.
step(&mut self)136*e1997b9aSAndroid Build Coastguard Worker     fn step(&mut self) {
137*e1997b9aSAndroid Build Coastguard Worker         self.notified.store(0, Ordering::Relaxed);
138*e1997b9aSAndroid Build Coastguard Worker         if let Err(e) = self.process_one_key() {
139*e1997b9aSAndroid Build Coastguard Worker             log::error!("Error trying to delete blob entry. {:?}", e);
140*e1997b9aSAndroid Build Coastguard Worker         }
141*e1997b9aSAndroid Build Coastguard Worker         // Schedule the next step. This gives high priority requests a chance to interleave.
142*e1997b9aSAndroid Build Coastguard Worker         if !self.deleted_blob_ids.is_empty() {
143*e1997b9aSAndroid Build Coastguard Worker             if let Some(at) = self.async_task.upgrade() {
144*e1997b9aSAndroid Build Coastguard Worker                 if let Ok(0) =
145*e1997b9aSAndroid Build Coastguard Worker                     self.notified.compare_exchange(0, 1, Ordering::Relaxed, Ordering::Relaxed)
146*e1997b9aSAndroid Build Coastguard Worker                 {
147*e1997b9aSAndroid Build Coastguard Worker                     at.queue_lo(move |shelf| {
148*e1997b9aSAndroid Build Coastguard Worker                         shelf.get_downcast_mut::<GcInternal>().unwrap().step()
149*e1997b9aSAndroid Build Coastguard Worker                     });
150*e1997b9aSAndroid Build Coastguard Worker                 }
151*e1997b9aSAndroid Build Coastguard Worker             }
152*e1997b9aSAndroid Build Coastguard Worker         }
153*e1997b9aSAndroid Build Coastguard Worker     }
154*e1997b9aSAndroid Build Coastguard Worker }
155