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