1 // Copyright 2021, 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 a per-boot, shared, in-memory storage of auth tokens 16 //! for the main Keystore 2.0 database module. 17 18 use super::AuthTokenEntry; 19 use android_hardware_security_keymint::aidl::android::hardware::security::keymint::{ 20 HardwareAuthToken::HardwareAuthToken, HardwareAuthenticatorType::HardwareAuthenticatorType, 21 }; 22 use lazy_static::lazy_static; 23 use std::collections::HashSet; 24 use std::sync::Arc; 25 use std::sync::RwLock; 26 27 #[derive(PartialEq, PartialOrd, Ord, Eq, Hash)] 28 struct AuthTokenId { 29 user_id: i64, 30 auth_id: i64, 31 authenticator_type: HardwareAuthenticatorType, 32 } 33 34 impl AuthTokenId { from_auth_token(tok: &HardwareAuthToken) -> Self35 fn from_auth_token(tok: &HardwareAuthToken) -> Self { 36 AuthTokenId { 37 user_id: tok.userId, 38 auth_id: tok.authenticatorId, 39 authenticator_type: tok.authenticatorType, 40 } 41 } 42 } 43 44 //Implements Eq/Hash to only operate on the AuthTokenId portion 45 //of the AuthTokenEntry. This allows a HashSet to DTRT. 46 #[derive(Clone)] 47 struct AuthTokenEntryWrap(AuthTokenEntry); 48 49 impl std::hash::Hash for AuthTokenEntryWrap { hash<H: std::hash::Hasher>(&self, state: &mut H)50 fn hash<H: std::hash::Hasher>(&self, state: &mut H) { 51 AuthTokenId::from_auth_token(&self.0.auth_token).hash(state) 52 } 53 } 54 55 impl PartialEq<AuthTokenEntryWrap> for AuthTokenEntryWrap { eq(&self, other: &AuthTokenEntryWrap) -> bool56 fn eq(&self, other: &AuthTokenEntryWrap) -> bool { 57 AuthTokenId::from_auth_token(&self.0.auth_token) 58 == AuthTokenId::from_auth_token(&other.0.auth_token) 59 } 60 } 61 62 impl Eq for AuthTokenEntryWrap {} 63 64 /// Per-boot state structure. Currently only used to track auth tokens. 65 #[derive(Default)] 66 pub struct PerbootDB { 67 // We can use a .unwrap() discipline on this lock, because only panicking 68 // while holding a .write() lock will poison it. The only write usage is 69 // an insert call which inserts a pre-constructed pair. 70 auth_tokens: RwLock<HashSet<AuthTokenEntryWrap>>, 71 } 72 73 lazy_static! { 74 /// The global instance of the perboot DB. Located here rather than in globals 75 /// in order to restrict access to the database module. 76 pub static ref PERBOOT_DB: Arc<PerbootDB> = Arc::new(PerbootDB::new()); 77 } 78 79 impl PerbootDB { 80 /// Construct a new perboot database. Currently just uses default values. new() -> Self81 pub fn new() -> Self { 82 Default::default() 83 } 84 /// Add a new auth token + timestamp to the database, replacing any which 85 /// match all of user_id, auth_id, and auth_type. insert_auth_token_entry(&self, entry: AuthTokenEntry)86 pub fn insert_auth_token_entry(&self, entry: AuthTokenEntry) { 87 self.auth_tokens.write().unwrap().replace(AuthTokenEntryWrap(entry)); 88 } 89 /// Locate an auth token entry which matches the predicate with the most 90 /// recent update time. find_auth_token_entry<P: Fn(&AuthTokenEntry) -> bool>( &self, p: P, ) -> Option<AuthTokenEntry>91 pub fn find_auth_token_entry<P: Fn(&AuthTokenEntry) -> bool>( 92 &self, 93 p: P, 94 ) -> Option<AuthTokenEntry> { 95 let reader = self.auth_tokens.read().unwrap(); 96 let mut matches: Vec<_> = reader.iter().filter(|x| p(&x.0)).collect(); 97 matches.sort_by_key(|x| x.0.time_received); 98 matches.last().map(|x| x.0.clone()) 99 } 100 /// Return how many auth tokens are currently tracked. auth_tokens_len(&self) -> usize101 pub fn auth_tokens_len(&self) -> usize { 102 self.auth_tokens.read().unwrap().len() 103 } 104 #[cfg(test)] 105 /// For testing, return all auth tokens currently tracked. get_all_auth_token_entries(&self) -> Vec<AuthTokenEntry>106 pub fn get_all_auth_token_entries(&self) -> Vec<AuthTokenEntry> { 107 self.auth_tokens.read().unwrap().iter().cloned().map(|x| x.0).collect() 108 } 109 } 110