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