Janis Danisevskis | 93927dd | 2020-12-23 12:23:08 -0800 | [diff] [blame] | 1 | // Copyright 2020, 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 | |
| 15 | //! This module implements the key garbage collector. |
| 16 | //! The key garbage collector has one public function `notify_gc()`. This will create |
| 17 | //! a thread on demand which will query the database for unreferenced key entries, |
| 18 | //! optionally dispose of sensitive key material appropriately, and then delete |
| 19 | //! the key entry from the database. |
| 20 | |
Janis Danisevskis | 7e8b462 | 2021-02-13 10:01:59 -0800 | [diff] [blame^] | 21 | use crate::{ |
| 22 | async_task, |
| 23 | database::{KeystoreDB, Uuid}, |
| 24 | }; |
| 25 | use anyhow::{Context, Result}; |
| 26 | use async_task::AsyncTask; |
| 27 | use std::sync::Arc; |
Janis Danisevskis | 93927dd | 2020-12-23 12:23:08 -0800 | [diff] [blame] | 28 | |
Janis Danisevskis | 93927dd | 2020-12-23 12:23:08 -0800 | [diff] [blame] | 29 | pub struct Gc { |
Janis Danisevskis | 7e8b462 | 2021-02-13 10:01:59 -0800 | [diff] [blame^] | 30 | async_task: Arc<AsyncTask>, |
Janis Danisevskis | 93927dd | 2020-12-23 12:23:08 -0800 | [diff] [blame] | 31 | } |
| 32 | |
| 33 | impl Gc { |
Janis Danisevskis | 7e8b462 | 2021-02-13 10:01:59 -0800 | [diff] [blame^] | 34 | /// Creates a garbage collector using the given async_task. |
| 35 | /// The garbage collector needs a function to invalidate key blobs and a database connection. |
| 36 | /// Both are obtained from the init function. The function is only called if this is first |
| 37 | /// time a garbage collector was initialized with the given AsyncTask instance. |
| 38 | pub fn new_init_with<F>(async_task: Arc<AsyncTask>, init: F) -> Self |
| 39 | where |
| 40 | F: FnOnce() -> (Box<dyn Fn(&Uuid, &[u8]) -> Result<()> + Send + 'static>, KeystoreDB) |
| 41 | + Send |
| 42 | + 'static, |
| 43 | { |
| 44 | let weak_at = Arc::downgrade(&async_task); |
| 45 | // Initialize the task's shelf. |
| 46 | async_task.queue_hi(move |shelf| { |
| 47 | let (invalidate_key, db) = init(); |
| 48 | shelf.get_or_put_with(|| GcInternal { |
| 49 | blob_id_to_delete: None, |
| 50 | invalidate_key, |
| 51 | db, |
| 52 | async_task: weak_at, |
| 53 | }); |
| 54 | }); |
| 55 | Self { async_task } |
| 56 | } |
Janis Danisevskis | 93927dd | 2020-12-23 12:23:08 -0800 | [diff] [blame] | 57 | |
Janis Danisevskis | 7e8b462 | 2021-02-13 10:01:59 -0800 | [diff] [blame^] | 58 | /// Notifies the key garbage collector to iterate through orphaned and superseded blobs and |
| 59 | /// attempts their deletion. We only process one key at a time and then schedule another |
| 60 | /// attempt by queueing it in the async_task (low priority) queue. |
| 61 | pub fn notify_gc(&self) { |
| 62 | self.async_task.queue_lo(|shelf| shelf.get_downcast_mut::<GcInternal>().unwrap().step()) |
| 63 | } |
| 64 | } |
| 65 | |
| 66 | struct GcInternal { |
| 67 | blob_id_to_delete: Option<i64>, |
| 68 | invalidate_key: Box<dyn Fn(&Uuid, &[u8]) -> Result<()> + Send + 'static>, |
| 69 | db: KeystoreDB, |
| 70 | async_task: std::sync::Weak<AsyncTask>, |
| 71 | } |
| 72 | |
| 73 | impl GcInternal { |
| 74 | /// Attempts to process one blob from the database. |
Janis Danisevskis | 93927dd | 2020-12-23 12:23:08 -0800 | [diff] [blame] | 75 | /// We process one key at a time, because deleting a key is a time consuming process which |
| 76 | /// may involve calling into the KeyMint backend and we don't want to hog neither the backend |
| 77 | /// nor the database for extended periods of time. |
Janis Danisevskis | 7e8b462 | 2021-02-13 10:01:59 -0800 | [diff] [blame^] | 78 | fn process_one_key(&mut self) -> Result<()> { |
| 79 | if let Some((blob_id, blob, blob_metadata)) = self |
| 80 | .db |
| 81 | .handle_next_superseded_blob(self.blob_id_to_delete.take()) |
| 82 | .context("In process_one_key: Trying to handle superseded blob.")? |
| 83 | { |
| 84 | // Set the blob_id as the next to be deleted blob. So it will be |
| 85 | // removed from the database regardless of whether the following |
| 86 | // succeeds or not. |
| 87 | self.blob_id_to_delete = Some(blob_id); |
Janis Danisevskis | 93927dd | 2020-12-23 12:23:08 -0800 | [diff] [blame] | 88 | |
Janis Danisevskis | 7e8b462 | 2021-02-13 10:01:59 -0800 | [diff] [blame^] | 89 | // If the key has a km_uuid we try to get the corresponding device |
| 90 | // and delete the key, unwrapping if necessary and possible. |
| 91 | // (At this time keys may get deleted without having the super encryption |
| 92 | // key in this case we can only delete the key from the database.) |
| 93 | if let Some(uuid) = blob_metadata.km_uuid() { |
| 94 | (self.invalidate_key)(&uuid, &*blob) |
| 95 | .context("In process_one_key: Trying to invalidate key.")?; |
Janis Danisevskis | 93927dd | 2020-12-23 12:23:08 -0800 | [diff] [blame] | 96 | } |
| 97 | } |
Janis Danisevskis | 7e8b462 | 2021-02-13 10:01:59 -0800 | [diff] [blame^] | 98 | Ok(()) |
Janis Danisevskis | 93927dd | 2020-12-23 12:23:08 -0800 | [diff] [blame] | 99 | } |
| 100 | |
Janis Danisevskis | 7e8b462 | 2021-02-13 10:01:59 -0800 | [diff] [blame^] | 101 | /// Processes one key and then schedules another attempt until it runs out of blobs to delete. |
| 102 | fn step(&mut self) { |
| 103 | if let Err(e) = self.process_one_key() { |
| 104 | log::error!("Error trying to delete blob entry. {:?}", e); |
| 105 | } |
| 106 | // Schedule the next step. This gives high priority requests a chance to interleave. |
| 107 | if self.blob_id_to_delete.is_some() { |
| 108 | if let Some(at) = self.async_task.upgrade() { |
| 109 | at.queue_lo(move |shelf| shelf.get_downcast_mut::<GcInternal>().unwrap().step()); |
| 110 | } |
| 111 | } |
Janis Danisevskis | 93927dd | 2020-12-23 12:23:08 -0800 | [diff] [blame] | 112 | } |
| 113 | } |