blob: 692cb7ef9447bfd102ecba70bb65c6931ad39f63 [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
Max Bires8e93d2b2021-01-14 13:17:59 -080021use crate::globals::{get_keymint_dev_by_uuid, DB};
Janis Danisevskis93927dd2020-12-23 12:23:08 -080022use crate::{error::map_km_error, globals::ASYNC_TASK};
23use android_hardware_security_keymint::aidl::android::hardware::security::keymint::IKeyMintDevice::IKeyMintDevice;
24use anyhow::Result;
25
26#[derive(Clone, Copy)]
27pub struct Gc {
28 remaining_tries: u32,
29}
30
31impl Gc {
32 const MAX_ERROR_RETRIES: u32 = 3u32;
33
34 /// Attempts to process one unreferenced key from the database.
35 /// Returns Ok(true) if a key was deleted and Ok(false) if there were no more keys to process.
36 /// We process one key at a time, because deleting a key is a time consuming process which
37 /// may involve calling into the KeyMint backend and we don't want to hog neither the backend
38 /// nor the database for extended periods of time.
39 fn process_one_key() -> Result<bool> {
40 DB.with(|db| {
41 let mut db = db.borrow_mut();
42 if let Some((key_id, mut key_entry)) = db.get_unreferenced_key()? {
43 if let Some(blob) = key_entry.take_km_blob() {
44 let km_dev: Box<dyn IKeyMintDevice> =
Max Bires8e93d2b2021-01-14 13:17:59 -080045 get_keymint_dev_by_uuid(key_entry.km_uuid())
46 .map(|(dev, _)| dev)?
47 .get_interface()?;
Janis Danisevskis93927dd2020-12-23 12:23:08 -080048 if let Err(e) = map_km_error(km_dev.deleteKey(&blob)) {
49 // Log but ignore error.
50 log::error!("Error trying to delete key. {:?}", e);
51 }
52 }
53 db.purge_key_entry(key_id)?;
54 return Ok(true);
55 }
56 Ok(false)
57 })
58 }
59
60 /// Processes one key and then schedules another attempt until it runs out of tries or keys
61 /// to delete.
62 fn process_all(mut self) {
63 match Self::process_one_key() {
64 // We successfully removed a key.
65 Ok(true) => self.remaining_tries = Self::MAX_ERROR_RETRIES,
66 // There were no more keys to remove. We may exit.
67 Ok(false) => self.remaining_tries = 0,
68 // An error occurred. We retry in case the error was transient, but
69 // we also count down the number of tries so that we don't spin
70 // indefinitely.
71 Err(e) => {
72 self.remaining_tries -= 1;
73 log::error!(
74 concat!(
75 "Failed to delete key. Retrying in case this error was transient. ",
76 "(Tries remaining {}) {:?}"
77 ),
78 self.remaining_tries,
79 e
80 )
81 }
82 }
83 if self.remaining_tries != 0 {
84 ASYNC_TASK.queue_lo(move || {
85 self.process_all();
86 })
87 }
88 }
89
90 /// Notifies the key garbage collector to iterate through unreferenced keys and attempt
91 /// their deletion. We only process one key at a time and then schedule another
92 /// attempt by queueing it in the async_task (low priority) queue.
93 pub fn notify_gc() {
94 ASYNC_TASK.queue_lo(|| Self { remaining_tries: Self::MAX_ERROR_RETRIES }.process_all())
95 }
96}