blob: fbb67780d27920c0527bde0570151e7e6971e8c8 [file] [log] [blame]
// Copyright 2020, The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//! This module implements the handling of background tasks such as obtaining timestamp tokens from
//! the timestamp service (or TEE KeyMint in legacy devices), via a separate thread.
use android_hardware_security_keymint::aidl::android::hardware::security::keymint::{
HardwareAuthToken::HardwareAuthToken, IKeyMintDevice::IKeyMintDevice,
SecurityLevel::SecurityLevel, VerificationToken::VerificationToken,
};
use android_system_keystore2::aidl::android::system::keystore2::OperationChallenge::OperationChallenge;
use anyhow::Result;
use log::error;
use std::sync::mpsc::{Receiver, Sender};
use std::sync::Mutex;
use std::thread::{spawn, JoinHandle};
/// This is the struct encapsulating the thread which handles background tasks such as
/// obtaining verification tokens.
pub struct BackgroundTaskHandler {
task_handler: Mutex<Option<JoinHandle<()>>>,
}
/// This enum defines the two variants of a message that can be passed down to the
/// BackgroundTaskHandler via the channel.
pub enum Message {
///This variant represents a message sent down the channel when requesting a timestamp token.
Inputs((HardwareAuthToken, OperationChallenge, Sender<(HardwareAuthToken, VerificationToken)>)),
///This variant represents a message sent down the channel when signalling the thread to stop.
Shutdown,
}
impl BackgroundTaskHandler {
/// Initialize the BackgroundTaskHandler with the task_handler field set to None.
/// The thread is not started during initialization, as it needs the receiver end of a channel
/// to function.
pub fn new() -> Self {
BackgroundTaskHandler { task_handler: Mutex::new(None) }
}
/// Start the background task handler (bth) by passing in the receiver end of a channel, through
/// which the enforcement module can send messages to the bth thread.
pub fn start_bth(&self, receiver: Receiver<Message>) -> Result<()> {
let task_handler = Self::start_thread(receiver)?;
// it is ok to unwrap here because there is no way that this lock can get poisoned.
let mut thread_guard = self.task_handler.lock().unwrap();
*thread_guard = Some(task_handler);
Ok(())
}
fn start_thread(receiver: Receiver<Message>) -> Result<JoinHandle<()>> {
// TODO: initialize timestamp service/keymint instances.
// First lookup timestamp token service, if this is not a legacy device.
// Otherwise, lookup keymaster 4.1 or 4.0 (previous ones are not relevant, because strongbox
// was introduced with keymaster 4.0).
// If either a timestamp service or a keymint instance is expected to be found and neither
// is found, an error is returned.
// If neither is expected to be found, make timestamp_service field None, and in the thread,
// send a default verification token down the channel to the operation.
// Until timestamp service is available and proper probing of legacy keymaster devices are
// done, the keymint service is initialized here as it is done in security_level module.
Ok(spawn(move || {
while let Message::Inputs((auth_token, op_challenge, op_sender)) = receiver
.recv()
.expect(
"In background task handler thread. Failed to receive message over the channel.",
) {
// TODO: call the timestamp service/old TEE keymaster to get
// timestamp/verification tokens and pass it down the sender that is
// coupled with a particular operation's receiver.
// If none of the services are available, pass the authtoken and a default
// verification token down the channel.
let km_dev: Box<dyn IKeyMintDevice> =
crate::globals::get_keymint_device(SecurityLevel::TRUSTED_ENVIRONMENT)
.expect("A TEE Keymint must be present.")
.get_interface()
.expect("Fatal: The keymint device does not implement IKeyMintDevice.");
let result = km_dev.verifyAuthorization(op_challenge.challenge, &auth_token);
match result {
Ok(verification_token) => {
// this can fail if the operation is dropped and hence the channel
// is hung up.
op_sender.send((auth_token, verification_token)).unwrap_or_else(|e| {
error!(
"In background task handler thread. Failed to send
verification token to operation {} due to error {:?}.",
op_challenge.challenge, e
)
});
}
Err(e) => {
// log error
error!(
"In background task handler thread. Failed to receive
verification token for operation {} due to error {:?}.",
op_challenge.challenge, e
);
// send default verification token
// this can fail if the operation is dropped and the channel is
// hung up.
op_sender.send((auth_token, VerificationToken::default())).unwrap_or_else(
|e| {
error!(
"In background task handler thread. Failed to send default
verification token to operation {} due to error {:?}.",
op_challenge.challenge, e
)
},
);
}
}
}
}))
}
}
impl Default for BackgroundTaskHandler {
fn default() -> Self {
Self::new()
}
}
// TODO: Verify if we want the thread to finish the requests they are working on, during drop.
impl Drop for BackgroundTaskHandler {
fn drop(&mut self) {
// it is ok to unwrap here as there is no way this lock can get poisoned.
let mut thread_guard = self.task_handler.lock().unwrap();
if let Some(thread) = (*thread_guard).take() {
// TODO: Verify how best to handle the error in this case.
thread.join().unwrap_or_else(|e| {
panic!("Failed to join the background task handling thread because of {:?}.", e);
});
}
}
}