blob: b5b1c6cf6109660a1c6329a7c02f73f101e139bb [file] [log] [blame]
Janis Danisevskis93927dd2020-12-23 12:23:08 -08001// 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 Danisevskis7e8b4622021-02-13 10:01:59 -080021use crate::{
22 async_task,
23 database::{KeystoreDB, Uuid},
24};
25use anyhow::{Context, Result};
26use async_task::AsyncTask;
27use std::sync::Arc;
Janis Danisevskis93927dd2020-12-23 12:23:08 -080028
Janis Danisevskis93927dd2020-12-23 12:23:08 -080029pub struct Gc {
Janis Danisevskis7e8b4622021-02-13 10:01:59 -080030 async_task: Arc<AsyncTask>,
Janis Danisevskis93927dd2020-12-23 12:23:08 -080031}
32
33impl Gc {
Janis Danisevskis7e8b4622021-02-13 10:01:59 -080034 /// 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 Danisevskis93927dd2020-12-23 12:23:08 -080057
Janis Danisevskis7e8b4622021-02-13 10:01:59 -080058 /// 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
66struct 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
73impl GcInternal {
74 /// Attempts to process one blob from the database.
Janis Danisevskis93927dd2020-12-23 12:23:08 -080075 /// 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 Danisevskis7e8b4622021-02-13 10:01:59 -080078 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 Danisevskis93927dd2020-12-23 12:23:08 -080088
Janis Danisevskis7e8b4622021-02-13 10:01:59 -080089 // 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 Danisevskis93927dd2020-12-23 12:23:08 -080096 }
97 }
Janis Danisevskis7e8b4622021-02-13 10:01:59 -080098 Ok(())
Janis Danisevskis93927dd2020-12-23 12:23:08 -080099 }
100
Janis Danisevskis7e8b4622021-02-13 10:01:59 -0800101 /// 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 Danisevskis93927dd2020-12-23 12:23:08 -0800112 }
113}