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