| 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 |  | 
| Shaquille Johnson | 9da2e1c | 2022-09-19 12:39:01 +0000 | [diff] [blame] | 21 | use crate::ks_err; | 
| Janis Danisevskis | 7e8b462 | 2021-02-13 10:01:59 -0800 | [diff] [blame] | 22 | use crate::{ | 
|  | 23 | async_task, | 
| Janis Danisevskis | 3395f86 | 2021-05-06 10:54:17 -0700 | [diff] [blame] | 24 | database::{BlobMetaData, KeystoreDB, Uuid}, | 
| Hasini Gunasinghe | deab85d | 2021-02-01 21:10:02 +0000 | [diff] [blame] | 25 | super_key::SuperKeyManager, | 
| Janis Danisevskis | 7e8b462 | 2021-02-13 10:01:59 -0800 | [diff] [blame] | 26 | }; | 
|  | 27 | use anyhow::{Context, Result}; | 
|  | 28 | use async_task::AsyncTask; | 
| Janis Danisevskis | 3395f86 | 2021-05-06 10:54:17 -0700 | [diff] [blame] | 29 | use std::sync::{ | 
|  | 30 | atomic::{AtomicU8, Ordering}, | 
| Janis Danisevskis | 0fd25a6 | 2022-01-04 19:53:37 -0800 | [diff] [blame] | 31 | Arc, RwLock, | 
| Janis Danisevskis | 3395f86 | 2021-05-06 10:54:17 -0700 | [diff] [blame] | 32 | }; | 
| Janis Danisevskis | 93927dd | 2020-12-23 12:23:08 -0800 | [diff] [blame] | 33 |  | 
| Janis Danisevskis | 93927dd | 2020-12-23 12:23:08 -0800 | [diff] [blame] | 34 | pub struct Gc { | 
| Janis Danisevskis | 7e8b462 | 2021-02-13 10:01:59 -0800 | [diff] [blame] | 35 | async_task: Arc<AsyncTask>, | 
| Janis Danisevskis | 3395f86 | 2021-05-06 10:54:17 -0700 | [diff] [blame] | 36 | notified: Arc<AtomicU8>, | 
| Janis Danisevskis | 93927dd | 2020-12-23 12:23:08 -0800 | [diff] [blame] | 37 | } | 
|  | 38 |  | 
|  | 39 | impl Gc { | 
| Janis Danisevskis | 7e8b462 | 2021-02-13 10:01:59 -0800 | [diff] [blame] | 40 | /// Creates a garbage collector using the given async_task. | 
| Janis Danisevskis | 3395f86 | 2021-05-06 10:54:17 -0700 | [diff] [blame] | 41 | /// The garbage collector needs a function to invalidate key blobs, a database connection, | 
|  | 42 | /// and a reference to the `SuperKeyManager`. They are obtained from the init function. | 
|  | 43 | /// The function is only called if this is first time a garbage collector was initialized | 
|  | 44 | /// with the given AsyncTask instance. | 
|  | 45 | /// Note: It is a logical error to initialize different Gc instances with the same `AsyncTask`. | 
| Janis Danisevskis | 7e8b462 | 2021-02-13 10:01:59 -0800 | [diff] [blame] | 46 | pub fn new_init_with<F>(async_task: Arc<AsyncTask>, init: F) -> Self | 
|  | 47 | where | 
| Hasini Gunasinghe | deab85d | 2021-02-01 21:10:02 +0000 | [diff] [blame] | 48 | F: FnOnce() -> ( | 
|  | 49 | Box<dyn Fn(&Uuid, &[u8]) -> Result<()> + Send + 'static>, | 
|  | 50 | KeystoreDB, | 
| Janis Danisevskis | 0fd25a6 | 2022-01-04 19:53:37 -0800 | [diff] [blame] | 51 | Arc<RwLock<SuperKeyManager>>, | 
| Hasini Gunasinghe | deab85d | 2021-02-01 21:10:02 +0000 | [diff] [blame] | 52 | ) + Send | 
| Janis Danisevskis | 7e8b462 | 2021-02-13 10:01:59 -0800 | [diff] [blame] | 53 | + 'static, | 
|  | 54 | { | 
|  | 55 | let weak_at = Arc::downgrade(&async_task); | 
| Janis Danisevskis | 3395f86 | 2021-05-06 10:54:17 -0700 | [diff] [blame] | 56 | let notified = Arc::new(AtomicU8::new(0)); | 
|  | 57 | let notified_clone = notified.clone(); | 
| Janis Danisevskis | 7e8b462 | 2021-02-13 10:01:59 -0800 | [diff] [blame] | 58 | // Initialize the task's shelf. | 
|  | 59 | async_task.queue_hi(move |shelf| { | 
| Hasini Gunasinghe | deab85d | 2021-02-01 21:10:02 +0000 | [diff] [blame] | 60 | let (invalidate_key, db, super_key) = init(); | 
| Janis Danisevskis | 3395f86 | 2021-05-06 10:54:17 -0700 | [diff] [blame] | 61 | let notified = notified_clone; | 
| Janis Danisevskis | 7e8b462 | 2021-02-13 10:01:59 -0800 | [diff] [blame] | 62 | shelf.get_or_put_with(|| GcInternal { | 
| Janis Danisevskis | 3395f86 | 2021-05-06 10:54:17 -0700 | [diff] [blame] | 63 | deleted_blob_ids: vec![], | 
|  | 64 | superseded_blobs: vec![], | 
| Janis Danisevskis | 7e8b462 | 2021-02-13 10:01:59 -0800 | [diff] [blame] | 65 | invalidate_key, | 
|  | 66 | db, | 
|  | 67 | async_task: weak_at, | 
| Hasini Gunasinghe | deab85d | 2021-02-01 21:10:02 +0000 | [diff] [blame] | 68 | super_key, | 
| Janis Danisevskis | 3395f86 | 2021-05-06 10:54:17 -0700 | [diff] [blame] | 69 | notified, | 
| Janis Danisevskis | 7e8b462 | 2021-02-13 10:01:59 -0800 | [diff] [blame] | 70 | }); | 
|  | 71 | }); | 
| Janis Danisevskis | 3395f86 | 2021-05-06 10:54:17 -0700 | [diff] [blame] | 72 | Self { async_task, notified } | 
| Janis Danisevskis | 7e8b462 | 2021-02-13 10:01:59 -0800 | [diff] [blame] | 73 | } | 
| Janis Danisevskis | 93927dd | 2020-12-23 12:23:08 -0800 | [diff] [blame] | 74 |  | 
| Janis Danisevskis | 7e8b462 | 2021-02-13 10:01:59 -0800 | [diff] [blame] | 75 | /// Notifies the key garbage collector to iterate through orphaned and superseded blobs and | 
|  | 76 | /// attempts their deletion. We only process one key at a time and then schedule another | 
|  | 77 | /// attempt by queueing it in the async_task (low priority) queue. | 
|  | 78 | pub fn notify_gc(&self) { | 
| Janis Danisevskis | 3395f86 | 2021-05-06 10:54:17 -0700 | [diff] [blame] | 79 | if let Ok(0) = self.notified.compare_exchange(0, 1, Ordering::Relaxed, Ordering::Relaxed) { | 
|  | 80 | self.async_task.queue_lo(|shelf| shelf.get_downcast_mut::<GcInternal>().unwrap().step()) | 
|  | 81 | } | 
| Janis Danisevskis | 7e8b462 | 2021-02-13 10:01:59 -0800 | [diff] [blame] | 82 | } | 
|  | 83 | } | 
|  | 84 |  | 
|  | 85 | struct GcInternal { | 
| Janis Danisevskis | 3395f86 | 2021-05-06 10:54:17 -0700 | [diff] [blame] | 86 | deleted_blob_ids: Vec<i64>, | 
|  | 87 | superseded_blobs: Vec<(i64, Vec<u8>, BlobMetaData)>, | 
| Janis Danisevskis | 7e8b462 | 2021-02-13 10:01:59 -0800 | [diff] [blame] | 88 | invalidate_key: Box<dyn Fn(&Uuid, &[u8]) -> Result<()> + Send + 'static>, | 
|  | 89 | db: KeystoreDB, | 
|  | 90 | async_task: std::sync::Weak<AsyncTask>, | 
| Janis Danisevskis | 0fd25a6 | 2022-01-04 19:53:37 -0800 | [diff] [blame] | 91 | super_key: Arc<RwLock<SuperKeyManager>>, | 
| Janis Danisevskis | 3395f86 | 2021-05-06 10:54:17 -0700 | [diff] [blame] | 92 | notified: Arc<AtomicU8>, | 
| Janis Danisevskis | 7e8b462 | 2021-02-13 10:01:59 -0800 | [diff] [blame] | 93 | } | 
|  | 94 |  | 
|  | 95 | impl GcInternal { | 
|  | 96 | /// Attempts to process one blob from the database. | 
| Janis Danisevskis | 93927dd | 2020-12-23 12:23:08 -0800 | [diff] [blame] | 97 | /// We process one key at a time, because deleting a key is a time consuming process which | 
|  | 98 | /// may involve calling into the KeyMint backend and we don't want to hog neither the backend | 
|  | 99 | /// nor the database for extended periods of time. | 
| Janis Danisevskis | 3395f86 | 2021-05-06 10:54:17 -0700 | [diff] [blame] | 100 | /// To limit the number of database transactions, which are also expensive and competing | 
|  | 101 | /// with threads on the critical path, deleted blobs are loaded in batches. | 
| Janis Danisevskis | 7e8b462 | 2021-02-13 10:01:59 -0800 | [diff] [blame] | 102 | fn process_one_key(&mut self) -> Result<()> { | 
| Janis Danisevskis | 3395f86 | 2021-05-06 10:54:17 -0700 | [diff] [blame] | 103 | if self.superseded_blobs.is_empty() { | 
|  | 104 | let blobs = self | 
|  | 105 | .db | 
|  | 106 | .handle_next_superseded_blobs(&self.deleted_blob_ids, 20) | 
| Shaquille Johnson | 9da2e1c | 2022-09-19 12:39:01 +0000 | [diff] [blame] | 107 | .context(ks_err!("Trying to handle superseded blob."))?; | 
| Janis Danisevskis | 3395f86 | 2021-05-06 10:54:17 -0700 | [diff] [blame] | 108 | self.deleted_blob_ids = vec![]; | 
|  | 109 | self.superseded_blobs = blobs; | 
|  | 110 | } | 
|  | 111 |  | 
|  | 112 | if let Some((blob_id, blob, blob_metadata)) = self.superseded_blobs.pop() { | 
|  | 113 | // Add the next blob_id to the deleted blob ids list. So it will be | 
| Janis Danisevskis | 7e8b462 | 2021-02-13 10:01:59 -0800 | [diff] [blame] | 114 | // removed from the database regardless of whether the following | 
|  | 115 | // succeeds or not. | 
| Janis Danisevskis | 3395f86 | 2021-05-06 10:54:17 -0700 | [diff] [blame] | 116 | self.deleted_blob_ids.push(blob_id); | 
| Janis Danisevskis | 93927dd | 2020-12-23 12:23:08 -0800 | [diff] [blame] | 117 |  | 
| Janis Danisevskis | 7e8b462 | 2021-02-13 10:01:59 -0800 | [diff] [blame] | 118 | // If the key has a km_uuid we try to get the corresponding device | 
|  | 119 | // and delete the key, unwrapping if necessary and possible. | 
|  | 120 | // (At this time keys may get deleted without having the super encryption | 
|  | 121 | // key in this case we can only delete the key from the database.) | 
|  | 122 | if let Some(uuid) = blob_metadata.km_uuid() { | 
| Hasini Gunasinghe | deab85d | 2021-02-01 21:10:02 +0000 | [diff] [blame] | 123 | let blob = self | 
|  | 124 | .super_key | 
| Janis Danisevskis | 0fd25a6 | 2022-01-04 19:53:37 -0800 | [diff] [blame] | 125 | .read() | 
|  | 126 | .unwrap() | 
| Hasini Gunasinghe | deab85d | 2021-02-01 21:10:02 +0000 | [diff] [blame] | 127 | .unwrap_key_if_required(&blob_metadata, &blob) | 
| Shaquille Johnson | 9da2e1c | 2022-09-19 12:39:01 +0000 | [diff] [blame] | 128 | .context(ks_err!("Trying to unwrap to-be-deleted blob.",))?; | 
| Chris Wailes | dabb6fe | 2022-11-16 15:56:19 -0800 | [diff] [blame] | 129 | (self.invalidate_key)(uuid, &blob).context(ks_err!("Trying to invalidate key."))?; | 
| Janis Danisevskis | 93927dd | 2020-12-23 12:23:08 -0800 | [diff] [blame] | 130 | } | 
|  | 131 | } | 
| Janis Danisevskis | 7e8b462 | 2021-02-13 10:01:59 -0800 | [diff] [blame] | 132 | Ok(()) | 
| Janis Danisevskis | 93927dd | 2020-12-23 12:23:08 -0800 | [diff] [blame] | 133 | } | 
|  | 134 |  | 
| Janis Danisevskis | 7e8b462 | 2021-02-13 10:01:59 -0800 | [diff] [blame] | 135 | /// Processes one key and then schedules another attempt until it runs out of blobs to delete. | 
|  | 136 | fn step(&mut self) { | 
| Janis Danisevskis | 3395f86 | 2021-05-06 10:54:17 -0700 | [diff] [blame] | 137 | self.notified.store(0, Ordering::Relaxed); | 
| Janis Danisevskis | 7e8b462 | 2021-02-13 10:01:59 -0800 | [diff] [blame] | 138 | if let Err(e) = self.process_one_key() { | 
|  | 139 | log::error!("Error trying to delete blob entry. {:?}", e); | 
|  | 140 | } | 
|  | 141 | // Schedule the next step. This gives high priority requests a chance to interleave. | 
| Janis Danisevskis | 3395f86 | 2021-05-06 10:54:17 -0700 | [diff] [blame] | 142 | if !self.deleted_blob_ids.is_empty() { | 
| Janis Danisevskis | 7e8b462 | 2021-02-13 10:01:59 -0800 | [diff] [blame] | 143 | if let Some(at) = self.async_task.upgrade() { | 
| Janis Danisevskis | 3395f86 | 2021-05-06 10:54:17 -0700 | [diff] [blame] | 144 | if let Ok(0) = | 
|  | 145 | self.notified.compare_exchange(0, 1, Ordering::Relaxed, Ordering::Relaxed) | 
|  | 146 | { | 
|  | 147 | at.queue_lo(move |shelf| { | 
|  | 148 | shelf.get_downcast_mut::<GcInternal>().unwrap().step() | 
|  | 149 | }); | 
|  | 150 | } | 
| Janis Danisevskis | 7e8b462 | 2021-02-13 10:01:59 -0800 | [diff] [blame] | 151 | } | 
|  | 152 | } | 
| Janis Danisevskis | 93927dd | 2020-12-23 12:23:08 -0800 | [diff] [blame] | 153 | } | 
|  | 154 | } |