blob: 6cc0f275d2b44108506c8a9e60ac7a3c6fa503f0 [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},
Hasini Gunasinghedeab85d2021-02-01 21:10:02 +000024 super_key::SuperKeyManager,
Janis Danisevskis7e8b4622021-02-13 10:01:59 -080025};
26use anyhow::{Context, Result};
27use async_task::AsyncTask;
28use std::sync::Arc;
Janis Danisevskis93927dd2020-12-23 12:23:08 -080029
Janis Danisevskis93927dd2020-12-23 12:23:08 -080030pub struct Gc {
Janis Danisevskis7e8b4622021-02-13 10:01:59 -080031 async_task: Arc<AsyncTask>,
Janis Danisevskis93927dd2020-12-23 12:23:08 -080032}
33
34impl Gc {
Janis Danisevskis7e8b4622021-02-13 10:01:59 -080035 /// Creates a garbage collector using the given async_task.
36 /// The garbage collector needs a function to invalidate key blobs and a database connection.
37 /// Both are obtained from the init function. The function is only called if this is first
38 /// time a garbage collector was initialized with the given AsyncTask instance.
39 pub fn new_init_with<F>(async_task: Arc<AsyncTask>, init: F) -> Self
40 where
Hasini Gunasinghedeab85d2021-02-01 21:10:02 +000041 F: FnOnce() -> (
42 Box<dyn Fn(&Uuid, &[u8]) -> Result<()> + Send + 'static>,
43 KeystoreDB,
44 Arc<SuperKeyManager>,
45 ) + Send
Janis Danisevskis7e8b4622021-02-13 10:01:59 -080046 + 'static,
47 {
48 let weak_at = Arc::downgrade(&async_task);
49 // Initialize the task's shelf.
50 async_task.queue_hi(move |shelf| {
Hasini Gunasinghedeab85d2021-02-01 21:10:02 +000051 let (invalidate_key, db, super_key) = init();
Janis Danisevskis7e8b4622021-02-13 10:01:59 -080052 shelf.get_or_put_with(|| GcInternal {
53 blob_id_to_delete: None,
54 invalidate_key,
55 db,
56 async_task: weak_at,
Hasini Gunasinghedeab85d2021-02-01 21:10:02 +000057 super_key,
Janis Danisevskis7e8b4622021-02-13 10:01:59 -080058 });
59 });
60 Self { async_task }
61 }
Janis Danisevskis93927dd2020-12-23 12:23:08 -080062
Janis Danisevskis7e8b4622021-02-13 10:01:59 -080063 /// Notifies the key garbage collector to iterate through orphaned and superseded blobs and
64 /// attempts their deletion. We only process one key at a time and then schedule another
65 /// attempt by queueing it in the async_task (low priority) queue.
66 pub fn notify_gc(&self) {
67 self.async_task.queue_lo(|shelf| shelf.get_downcast_mut::<GcInternal>().unwrap().step())
68 }
69}
70
71struct GcInternal {
72 blob_id_to_delete: Option<i64>,
73 invalidate_key: Box<dyn Fn(&Uuid, &[u8]) -> Result<()> + Send + 'static>,
74 db: KeystoreDB,
75 async_task: std::sync::Weak<AsyncTask>,
Hasini Gunasinghedeab85d2021-02-01 21:10:02 +000076 super_key: Arc<SuperKeyManager>,
Janis Danisevskis7e8b4622021-02-13 10:01:59 -080077}
78
79impl GcInternal {
80 /// Attempts to process one blob from the database.
Janis Danisevskis93927dd2020-12-23 12:23:08 -080081 /// We process one key at a time, because deleting a key is a time consuming process which
82 /// may involve calling into the KeyMint backend and we don't want to hog neither the backend
83 /// nor the database for extended periods of time.
Janis Danisevskis7e8b4622021-02-13 10:01:59 -080084 fn process_one_key(&mut self) -> Result<()> {
85 if let Some((blob_id, blob, blob_metadata)) = self
86 .db
87 .handle_next_superseded_blob(self.blob_id_to_delete.take())
88 .context("In process_one_key: Trying to handle superseded blob.")?
89 {
90 // Set the blob_id as the next to be deleted blob. So it will be
91 // removed from the database regardless of whether the following
92 // succeeds or not.
93 self.blob_id_to_delete = Some(blob_id);
Janis Danisevskis93927dd2020-12-23 12:23:08 -080094
Janis Danisevskis7e8b4622021-02-13 10:01:59 -080095 // If the key has a km_uuid we try to get the corresponding device
96 // and delete the key, unwrapping if necessary and possible.
97 // (At this time keys may get deleted without having the super encryption
98 // key in this case we can only delete the key from the database.)
99 if let Some(uuid) = blob_metadata.km_uuid() {
Hasini Gunasinghedeab85d2021-02-01 21:10:02 +0000100 let blob = self
101 .super_key
102 .unwrap_key_if_required(&blob_metadata, &blob)
103 .context("In process_one_key: Trying to unwrap to-be-deleted blob.")?;
Janis Danisevskis7e8b4622021-02-13 10:01:59 -0800104 (self.invalidate_key)(&uuid, &*blob)
105 .context("In process_one_key: Trying to invalidate key.")?;
Janis Danisevskis93927dd2020-12-23 12:23:08 -0800106 }
107 }
Janis Danisevskis7e8b4622021-02-13 10:01:59 -0800108 Ok(())
Janis Danisevskis93927dd2020-12-23 12:23:08 -0800109 }
110
Janis Danisevskis7e8b4622021-02-13 10:01:59 -0800111 /// Processes one key and then schedules another attempt until it runs out of blobs to delete.
112 fn step(&mut self) {
113 if let Err(e) = self.process_one_key() {
114 log::error!("Error trying to delete blob entry. {:?}", e);
115 }
116 // Schedule the next step. This gives high priority requests a chance to interleave.
117 if self.blob_id_to_delete.is_some() {
118 if let Some(at) = self.async_task.upgrade() {
119 at.queue_lo(move |shelf| shelf.get_downcast_mut::<GcInternal>().unwrap().step());
120 }
121 }
Janis Danisevskis93927dd2020-12-23 12:23:08 -0800122 }
123}