• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // 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 //! Implements the android.security.legacykeystore interface.
16 
17 use android_security_legacykeystore::aidl::android::security::legacykeystore::{
18     ILegacyKeystore::BnLegacyKeystore, ILegacyKeystore::ILegacyKeystore,
19     ILegacyKeystore::ERROR_ENTRY_NOT_FOUND, ILegacyKeystore::ERROR_PERMISSION_DENIED,
20     ILegacyKeystore::ERROR_SYSTEM_ERROR, ILegacyKeystore::UID_SELF,
21 };
22 use android_security_legacykeystore::binder::{
23     BinderFeatures, ExceptionCode, Result as BinderResult, Status as BinderStatus, Strong,
24     ThreadState,
25 };
26 use anyhow::{Context, Result};
27 use keystore2::{
28     async_task::AsyncTask, error::anyhow_error_to_cstring, globals::SUPER_KEY,
29     legacy_blob::LegacyBlobLoader, maintenance::DeleteListener, maintenance::Domain,
30     utils::uid_to_android_user, utils::watchdog as wd,
31 };
32 use rusqlite::{
33     params, Connection, OptionalExtension, Transaction, TransactionBehavior, NO_PARAMS,
34 };
35 use std::sync::Arc;
36 use std::{
37     collections::HashSet,
38     path::{Path, PathBuf},
39 };
40 
41 struct DB {
42     conn: Connection,
43 }
44 
45 impl DB {
new(db_file: &Path) -> Result<Self>46     fn new(db_file: &Path) -> Result<Self> {
47         let mut db = Self {
48             conn: Connection::open(db_file).context("Failed to initialize SQLite connection.")?,
49         };
50 
51         db.init_tables().context("Trying to initialize legacy keystore db.")?;
52         Ok(db)
53     }
54 
with_transaction<T, F>(&mut self, behavior: TransactionBehavior, f: F) -> Result<T> where F: Fn(&Transaction) -> Result<T>,55     fn with_transaction<T, F>(&mut self, behavior: TransactionBehavior, f: F) -> Result<T>
56     where
57         F: Fn(&Transaction) -> Result<T>,
58     {
59         loop {
60             match self
61                 .conn
62                 .transaction_with_behavior(behavior)
63                 .context("In with_transaction.")
64                 .and_then(|tx| f(&tx).map(|result| (result, tx)))
65                 .and_then(|(result, tx)| {
66                     tx.commit().context("In with_transaction: Failed to commit transaction.")?;
67                     Ok(result)
68                 }) {
69                 Ok(result) => break Ok(result),
70                 Err(e) => {
71                     if Self::is_locked_error(&e) {
72                         std::thread::sleep(std::time::Duration::from_micros(500));
73                         continue;
74                     } else {
75                         return Err(e).context("In with_transaction.");
76                     }
77                 }
78             }
79         }
80     }
81 
is_locked_error(e: &anyhow::Error) -> bool82     fn is_locked_error(e: &anyhow::Error) -> bool {
83         matches!(
84             e.root_cause().downcast_ref::<rusqlite::ffi::Error>(),
85             Some(rusqlite::ffi::Error { code: rusqlite::ErrorCode::DatabaseBusy, .. })
86                 | Some(rusqlite::ffi::Error { code: rusqlite::ErrorCode::DatabaseLocked, .. })
87         )
88     }
89 
init_tables(&mut self) -> Result<()>90     fn init_tables(&mut self) -> Result<()> {
91         self.with_transaction(TransactionBehavior::Immediate, |tx| {
92             tx.execute(
93                 "CREATE TABLE IF NOT EXISTS profiles (
94                      owner INTEGER,
95                      alias BLOB,
96                      profile BLOB,
97                      UNIQUE(owner, alias));",
98                 NO_PARAMS,
99             )
100             .context("Failed to initialize \"profiles\" table.")?;
101             Ok(())
102         })
103     }
104 
list(&mut self, caller_uid: u32) -> Result<Vec<String>>105     fn list(&mut self, caller_uid: u32) -> Result<Vec<String>> {
106         self.with_transaction(TransactionBehavior::Deferred, |tx| {
107             let mut stmt = tx
108                 .prepare("SELECT alias FROM profiles WHERE owner = ? ORDER BY alias ASC;")
109                 .context("In list: Failed to prepare statement.")?;
110 
111             // This allow is necessary to avoid the following error:
112             //
113             // error[E0597]: `stmt` does not live long enough
114             //
115             // See: https://github.com/rust-lang/rust-clippy/issues/8114
116             #[allow(clippy::let_and_return)]
117             let aliases = stmt
118                 .query_map(params![caller_uid], |row| row.get(0))?
119                 .collect::<rusqlite::Result<Vec<String>>>()
120                 .context("In list: query_map failed.");
121             aliases
122         })
123     }
124 
put(&mut self, caller_uid: u32, alias: &str, entry: &[u8]) -> Result<()>125     fn put(&mut self, caller_uid: u32, alias: &str, entry: &[u8]) -> Result<()> {
126         self.with_transaction(TransactionBehavior::Immediate, |tx| {
127             tx.execute(
128                 "INSERT OR REPLACE INTO profiles (owner, alias, profile) values (?, ?, ?)",
129                 params![caller_uid, alias, entry,],
130             )
131             .context("In put: Failed to insert or replace.")?;
132             Ok(())
133         })
134     }
135 
get(&mut self, caller_uid: u32, alias: &str) -> Result<Option<Vec<u8>>>136     fn get(&mut self, caller_uid: u32, alias: &str) -> Result<Option<Vec<u8>>> {
137         self.with_transaction(TransactionBehavior::Deferred, |tx| {
138             tx.query_row(
139                 "SELECT profile FROM profiles WHERE owner = ? AND alias = ?;",
140                 params![caller_uid, alias],
141                 |row| row.get(0),
142             )
143             .optional()
144             .context("In get: failed loading entry.")
145         })
146     }
147 
remove(&mut self, caller_uid: u32, alias: &str) -> Result<bool>148     fn remove(&mut self, caller_uid: u32, alias: &str) -> Result<bool> {
149         let removed = self.with_transaction(TransactionBehavior::Immediate, |tx| {
150             tx.execute(
151                 "DELETE FROM profiles WHERE owner = ? AND alias = ?;",
152                 params![caller_uid, alias],
153             )
154             .context("In remove: Failed to delete row.")
155         })?;
156         Ok(removed == 1)
157     }
158 
remove_uid(&mut self, uid: u32) -> Result<()>159     fn remove_uid(&mut self, uid: u32) -> Result<()> {
160         self.with_transaction(TransactionBehavior::Immediate, |tx| {
161             tx.execute("DELETE FROM profiles WHERE owner = ?;", params![uid])
162                 .context("In remove_uid: Failed to delete.")
163         })?;
164         Ok(())
165     }
166 
remove_user(&mut self, user_id: u32) -> Result<()>167     fn remove_user(&mut self, user_id: u32) -> Result<()> {
168         self.with_transaction(TransactionBehavior::Immediate, |tx| {
169             tx.execute(
170                 "DELETE FROM profiles WHERE cast ( ( owner/? ) as int) = ?;",
171                 params![rustutils::users::AID_USER_OFFSET, user_id],
172             )
173             .context("In remove_uid: Failed to delete.")
174         })?;
175         Ok(())
176     }
177 }
178 
179 /// This is the main LegacyKeystore error type, it wraps binder exceptions and the
180 /// LegacyKeystore errors.
181 #[derive(Debug, thiserror::Error, PartialEq, Eq)]
182 pub enum Error {
183     /// Wraps a LegacyKeystore error code.
184     #[error("Error::Error({0:?})")]
185     Error(i32),
186     /// Wraps a Binder exception code other than a service specific exception.
187     #[error("Binder exception code {0:?}, {1:?}")]
188     Binder(ExceptionCode, i32),
189 }
190 
191 impl Error {
192     /// Short hand for `Error::Error(ERROR_SYSTEM_ERROR)`
sys() -> Self193     pub fn sys() -> Self {
194         Error::Error(ERROR_SYSTEM_ERROR)
195     }
196 
197     /// Short hand for `Error::Error(ERROR_ENTRY_NOT_FOUND)`
not_found() -> Self198     pub fn not_found() -> Self {
199         Error::Error(ERROR_ENTRY_NOT_FOUND)
200     }
201 
202     /// Short hand for `Error::Error(ERROR_PERMISSION_DENIED)`
perm() -> Self203     pub fn perm() -> Self {
204         Error::Error(ERROR_PERMISSION_DENIED)
205     }
206 }
207 
208 /// This function should be used by legacykeystore service calls to translate error conditions
209 /// into service specific exceptions.
210 ///
211 /// All error conditions get logged by this function, except for ERROR_ENTRY_NOT_FOUND error.
212 ///
213 /// `Error::Error(x)` variants get mapped onto a service specific error code of `x`.
214 ///
215 /// All non `Error` error conditions get mapped onto `ERROR_SYSTEM_ERROR`.
216 ///
217 /// `handle_ok` will be called if `result` is `Ok(value)` where `value` will be passed
218 /// as argument to `handle_ok`. `handle_ok` must generate a `BinderResult<T>`, but it
219 /// typically returns Ok(value).
map_or_log_err<T, U, F>(result: Result<U>, handle_ok: F) -> BinderResult<T> where F: FnOnce(U) -> BinderResult<T>,220 fn map_or_log_err<T, U, F>(result: Result<U>, handle_ok: F) -> BinderResult<T>
221 where
222     F: FnOnce(U) -> BinderResult<T>,
223 {
224     result.map_or_else(
225         |e| {
226             let root_cause = e.root_cause();
227             let (rc, log_error) = match root_cause.downcast_ref::<Error>() {
228                 // Make the entry not found errors silent.
229                 Some(Error::Error(ERROR_ENTRY_NOT_FOUND)) => (ERROR_ENTRY_NOT_FOUND, false),
230                 Some(Error::Error(e)) => (*e, true),
231                 Some(Error::Binder(_, _)) | None => (ERROR_SYSTEM_ERROR, true),
232             };
233             if log_error {
234                 log::error!("{:?}", e);
235             }
236             Err(BinderStatus::new_service_specific_error(
237                 rc,
238                 anyhow_error_to_cstring(&e).as_deref(),
239             ))
240         },
241         handle_ok,
242     )
243 }
244 
245 struct LegacyKeystoreDeleteListener {
246     legacy_keystore: Arc<LegacyKeystore>,
247 }
248 
249 impl DeleteListener for LegacyKeystoreDeleteListener {
delete_namespace(&self, domain: Domain, namespace: i64) -> Result<()>250     fn delete_namespace(&self, domain: Domain, namespace: i64) -> Result<()> {
251         self.legacy_keystore.delete_namespace(domain, namespace)
252     }
delete_user(&self, user_id: u32) -> Result<()>253     fn delete_user(&self, user_id: u32) -> Result<()> {
254         self.legacy_keystore.delete_user(user_id)
255     }
256 }
257 
258 /// Implements ILegacyKeystore AIDL interface.
259 pub struct LegacyKeystore {
260     db_path: PathBuf,
261     async_task: AsyncTask,
262 }
263 
264 struct AsyncState {
265     recently_imported: HashSet<(u32, String)>,
266     legacy_loader: LegacyBlobLoader,
267     db_path: PathBuf,
268 }
269 
270 impl LegacyKeystore {
271     /// Note: The filename was chosen before the purpose of this module was extended.
272     ///       It is kept for backward compatibility with early adopters.
273     const LEGACY_KEYSTORE_FILE_NAME: &'static str = "vpnprofilestore.sqlite";
274 
275     const WIFI_NAMESPACE: i64 = 102;
276     const AID_WIFI: u32 = 1010;
277 
278     /// Creates a new LegacyKeystore instance.
new_native_binder( path: &Path, ) -> (Box<dyn DeleteListener + Send + Sync + 'static>, Strong<dyn ILegacyKeystore>)279     pub fn new_native_binder(
280         path: &Path,
281     ) -> (Box<dyn DeleteListener + Send + Sync + 'static>, Strong<dyn ILegacyKeystore>) {
282         let mut db_path = path.to_path_buf();
283         db_path.push(Self::LEGACY_KEYSTORE_FILE_NAME);
284 
285         let legacy_keystore = Arc::new(Self { db_path, async_task: Default::default() });
286         legacy_keystore.init_shelf(path);
287         let service = LegacyKeystoreService { legacy_keystore: legacy_keystore.clone() };
288         (
289             Box::new(LegacyKeystoreDeleteListener { legacy_keystore }),
290             BnLegacyKeystore::new_binder(service, BinderFeatures::default()),
291         )
292     }
293 
open_db(&self) -> Result<DB>294     fn open_db(&self) -> Result<DB> {
295         DB::new(&self.db_path).context("In open_db: Failed to open db.")
296     }
297 
get_effective_uid(uid: i32) -> Result<u32>298     fn get_effective_uid(uid: i32) -> Result<u32> {
299         const AID_SYSTEM: u32 = 1000;
300         let calling_uid = ThreadState::get_calling_uid();
301         let uid = uid as u32;
302 
303         if uid == UID_SELF as u32 || uid == calling_uid {
304             Ok(calling_uid)
305         } else if calling_uid == AID_SYSTEM && uid == Self::AID_WIFI {
306             // The only exception for legacy reasons is allowing SYSTEM to access
307             // the WIFI namespace.
308             // IMPORTANT: If you attempt to add more exceptions, it means you are adding
309             // more callers to this deprecated feature. DON'T!
310             Ok(Self::AID_WIFI)
311         } else {
312             Err(Error::perm()).with_context(|| {
313                 format!("In get_effective_uid: caller: {}, requested uid: {}.", calling_uid, uid)
314             })
315         }
316     }
317 
get(&self, alias: &str, uid: i32) -> Result<Vec<u8>>318     fn get(&self, alias: &str, uid: i32) -> Result<Vec<u8>> {
319         let mut db = self.open_db().context("In get.")?;
320         let uid = Self::get_effective_uid(uid).context("In get.")?;
321 
322         if let Some(entry) = db.get(uid, alias).context("In get: Trying to load entry from DB.")? {
323             return Ok(entry);
324         }
325         if self.get_legacy(uid, alias).context("In get: Trying to import legacy blob.")? {
326             // If we were able to import a legacy blob try again.
327             if let Some(entry) =
328                 db.get(uid, alias).context("In get: Trying to load entry from DB.")?
329             {
330                 return Ok(entry);
331             }
332         }
333         Err(Error::not_found()).context("In get: No such entry.")
334     }
335 
put(&self, alias: &str, uid: i32, entry: &[u8]) -> Result<()>336     fn put(&self, alias: &str, uid: i32, entry: &[u8]) -> Result<()> {
337         let uid = Self::get_effective_uid(uid).context("In put.")?;
338         let mut db = self.open_db().context("In put.")?;
339         db.put(uid, alias, entry).context("In put: Trying to insert entry into DB.")?;
340         // When replacing an entry, make sure that there is no stale legacy file entry.
341         let _ = self.remove_legacy(uid, alias);
342         Ok(())
343     }
344 
remove(&self, alias: &str, uid: i32) -> Result<()>345     fn remove(&self, alias: &str, uid: i32) -> Result<()> {
346         let uid = Self::get_effective_uid(uid).context("In remove.")?;
347         let mut db = self.open_db().context("In remove.")?;
348 
349         if self.remove_legacy(uid, alias).context("In remove: trying to remove legacy entry")? {
350             return Ok(());
351         }
352         let removed =
353             db.remove(uid, alias).context("In remove: Trying to remove entry from DB.")?;
354         if removed {
355             Ok(())
356         } else {
357             Err(Error::not_found()).context("In remove: No such entry.")
358         }
359     }
360 
delete_namespace(&self, domain: Domain, namespace: i64) -> Result<()>361     fn delete_namespace(&self, domain: Domain, namespace: i64) -> Result<()> {
362         let uid = match domain {
363             Domain::APP => namespace as u32,
364             Domain::SELINUX => {
365                 if namespace == Self::WIFI_NAMESPACE {
366                     // Namespace WIFI gets mapped to AID_WIFI.
367                     Self::AID_WIFI
368                 } else {
369                     // Nothing to do for any other namespace.
370                     return Ok(());
371                 }
372             }
373             _ => return Ok(()),
374         };
375 
376         if let Err(e) = self.bulk_delete_uid(uid) {
377             log::warn!("In LegacyKeystore::delete_namespace: {:?}", e);
378         }
379         let mut db = self.open_db().context("In LegacyKeystore::delete_namespace.")?;
380         db.remove_uid(uid).context("In LegacyKeystore::delete_namespace.")
381     }
382 
delete_user(&self, user_id: u32) -> Result<()>383     fn delete_user(&self, user_id: u32) -> Result<()> {
384         if let Err(e) = self.bulk_delete_user(user_id) {
385             log::warn!("In LegacyKeystore::delete_user: {:?}", e);
386         }
387         let mut db = self.open_db().context("In LegacyKeystore::delete_user.")?;
388         db.remove_user(user_id).context("In LegacyKeystore::delete_user.")
389     }
390 
list(&self, prefix: &str, uid: i32) -> Result<Vec<String>>391     fn list(&self, prefix: &str, uid: i32) -> Result<Vec<String>> {
392         let mut db = self.open_db().context("In list.")?;
393         let uid = Self::get_effective_uid(uid).context("In list.")?;
394         let mut result = self.list_legacy(uid).context("In list.")?;
395         result.append(&mut db.list(uid).context("In list: Trying to get list of entries.")?);
396         result.retain(|s| s.starts_with(prefix));
397         result.sort_unstable();
398         result.dedup();
399         Ok(result)
400     }
401 
init_shelf(&self, path: &Path)402     fn init_shelf(&self, path: &Path) {
403         let mut db_path = path.to_path_buf();
404         self.async_task.queue_hi(move |shelf| {
405             let legacy_loader = LegacyBlobLoader::new(&db_path);
406             db_path.push(Self::LEGACY_KEYSTORE_FILE_NAME);
407 
408             shelf.put(AsyncState { legacy_loader, db_path, recently_imported: Default::default() });
409         })
410     }
411 
do_serialized<F, T: Send + 'static>(&self, f: F) -> Result<T> where F: FnOnce(&mut AsyncState) -> Result<T> + Send + 'static,412     fn do_serialized<F, T: Send + 'static>(&self, f: F) -> Result<T>
413     where
414         F: FnOnce(&mut AsyncState) -> Result<T> + Send + 'static,
415     {
416         let (sender, receiver) = std::sync::mpsc::channel::<Result<T>>();
417         self.async_task.queue_hi(move |shelf| {
418             let state = shelf.get_downcast_mut::<AsyncState>().expect("Failed to get shelf.");
419             sender.send(f(state)).expect("Failed to send result.");
420         });
421         receiver.recv().context("In do_serialized: Failed to receive result.")?
422     }
423 
list_legacy(&self, uid: u32) -> Result<Vec<String>>424     fn list_legacy(&self, uid: u32) -> Result<Vec<String>> {
425         self.do_serialized(move |state| {
426             state
427                 .legacy_loader
428                 .list_legacy_keystore_entries_for_uid(uid)
429                 .context("Trying to list legacy keystore entries.")
430         })
431         .context("In list_legacy.")
432     }
433 
get_legacy(&self, uid: u32, alias: &str) -> Result<bool>434     fn get_legacy(&self, uid: u32, alias: &str) -> Result<bool> {
435         let alias = alias.to_string();
436         self.do_serialized(move |state| {
437             if state.recently_imported.contains(&(uid, alias.clone())) {
438                 return Ok(true);
439             }
440             let mut db = DB::new(&state.db_path).context("In open_db: Failed to open db.")?;
441             let imported =
442                 Self::import_one_legacy_entry(uid, &alias, &state.legacy_loader, &mut db)
443                     .context("Trying to import legacy keystore entries.")?;
444             if imported {
445                 state.recently_imported.insert((uid, alias));
446             }
447             Ok(imported)
448         })
449         .context("In get_legacy.")
450     }
451 
remove_legacy(&self, uid: u32, alias: &str) -> Result<bool>452     fn remove_legacy(&self, uid: u32, alias: &str) -> Result<bool> {
453         let alias = alias.to_string();
454         self.do_serialized(move |state| {
455             if state.recently_imported.contains(&(uid, alias.clone())) {
456                 return Ok(false);
457             }
458             state
459                 .legacy_loader
460                 .remove_legacy_keystore_entry(uid, &alias)
461                 .context("Trying to remove legacy entry.")
462         })
463     }
464 
bulk_delete_uid(&self, uid: u32) -> Result<()>465     fn bulk_delete_uid(&self, uid: u32) -> Result<()> {
466         self.do_serialized(move |state| {
467             let entries = state
468                 .legacy_loader
469                 .list_legacy_keystore_entries_for_uid(uid)
470                 .context("In bulk_delete_uid: Trying to list entries.")?;
471             for alias in entries.iter() {
472                 if let Err(e) = state.legacy_loader.remove_legacy_keystore_entry(uid, alias) {
473                     log::warn!("In bulk_delete_uid: Failed to delete legacy entry. {:?}", e);
474                 }
475             }
476             Ok(())
477         })
478     }
479 
bulk_delete_user(&self, user_id: u32) -> Result<()>480     fn bulk_delete_user(&self, user_id: u32) -> Result<()> {
481         self.do_serialized(move |state| {
482             let entries = state
483                 .legacy_loader
484                 .list_legacy_keystore_entries_for_user(user_id)
485                 .context("In bulk_delete_user: Trying to list entries.")?;
486             for (uid, entries) in entries.iter() {
487                 for alias in entries.iter() {
488                     if let Err(e) = state.legacy_loader.remove_legacy_keystore_entry(*uid, alias) {
489                         log::warn!("In bulk_delete_user: Failed to delete legacy entry. {:?}", e);
490                     }
491                 }
492             }
493             Ok(())
494         })
495     }
496 
import_one_legacy_entry( uid: u32, alias: &str, legacy_loader: &LegacyBlobLoader, db: &mut DB, ) -> Result<bool>497     fn import_one_legacy_entry(
498         uid: u32,
499         alias: &str,
500         legacy_loader: &LegacyBlobLoader,
501         db: &mut DB,
502     ) -> Result<bool> {
503         let blob = legacy_loader
504             .read_legacy_keystore_entry(uid, alias, |ciphertext, iv, tag, _salt, _key_size| {
505                 if let Some(key) =
506                     SUPER_KEY.read().unwrap().get_per_boot_key_by_user_id(uid_to_android_user(uid))
507                 {
508                     key.decrypt(ciphertext, iv, tag)
509                 } else {
510                     Err(Error::sys()).context("No key found for user. Device may be locked.")
511                 }
512             })
513             .context("In import_one_legacy_entry: Trying to read legacy keystore entry.")?;
514         if let Some(entry) = blob {
515             db.put(uid, alias, &entry)
516                 .context("In import_one_legacy_entry: Trying to insert entry into DB.")?;
517             legacy_loader
518                 .remove_legacy_keystore_entry(uid, alias)
519                 .context("In import_one_legacy_entry: Trying to delete legacy keystore entry.")?;
520             Ok(true)
521         } else {
522             Ok(false)
523         }
524     }
525 }
526 
527 struct LegacyKeystoreService {
528     legacy_keystore: Arc<LegacyKeystore>,
529 }
530 
531 impl binder::Interface for LegacyKeystoreService {}
532 
533 impl ILegacyKeystore for LegacyKeystoreService {
get(&self, alias: &str, uid: i32) -> BinderResult<Vec<u8>>534     fn get(&self, alias: &str, uid: i32) -> BinderResult<Vec<u8>> {
535         let _wp = wd::watch_millis("ILegacyKeystore::get", 500);
536         map_or_log_err(self.legacy_keystore.get(alias, uid), Ok)
537     }
put(&self, alias: &str, uid: i32, entry: &[u8]) -> BinderResult<()>538     fn put(&self, alias: &str, uid: i32, entry: &[u8]) -> BinderResult<()> {
539         let _wp = wd::watch_millis("ILegacyKeystore::put", 500);
540         map_or_log_err(self.legacy_keystore.put(alias, uid, entry), Ok)
541     }
remove(&self, alias: &str, uid: i32) -> BinderResult<()>542     fn remove(&self, alias: &str, uid: i32) -> BinderResult<()> {
543         let _wp = wd::watch_millis("ILegacyKeystore::remove", 500);
544         map_or_log_err(self.legacy_keystore.remove(alias, uid), Ok)
545     }
list(&self, prefix: &str, uid: i32) -> BinderResult<Vec<String>>546     fn list(&self, prefix: &str, uid: i32) -> BinderResult<Vec<String>> {
547         let _wp = wd::watch_millis("ILegacyKeystore::list", 500);
548         map_or_log_err(self.legacy_keystore.list(prefix, uid), Ok)
549     }
550 }
551 
552 #[cfg(test)]
553 mod db_test {
554     use super::*;
555     use keystore2_test_utils::TempDir;
556     use std::sync::Arc;
557     use std::thread;
558     use std::time::Duration;
559     use std::time::Instant;
560 
561     static TEST_ALIAS: &str = "test_alias";
562     static TEST_BLOB1: &[u8] = &[1, 2, 3, 4, 5, 6, 7, 8, 9, 0];
563     static TEST_BLOB2: &[u8] = &[2, 2, 3, 4, 5, 6, 7, 8, 9, 0];
564     static TEST_BLOB3: &[u8] = &[3, 2, 3, 4, 5, 6, 7, 8, 9, 0];
565     static TEST_BLOB4: &[u8] = &[3, 2, 3, 4, 5, 6, 7, 8, 9, 0];
566 
567     #[test]
test_entry_db()568     fn test_entry_db() {
569         let test_dir = TempDir::new("entrydb_test_").expect("Failed to create temp dir.");
570         let mut db = DB::new(&test_dir.build().push(LegacyKeystore::LEGACY_KEYSTORE_FILE_NAME))
571             .expect("Failed to open database.");
572 
573         // Insert three entries for owner 2.
574         db.put(2, "test1", TEST_BLOB1).expect("Failed to insert test1.");
575         db.put(2, "test2", TEST_BLOB2).expect("Failed to insert test2.");
576         db.put(2, "test3", TEST_BLOB3).expect("Failed to insert test3.");
577 
578         // Check list returns all inserted aliases.
579         assert_eq!(
580             vec!["test1".to_string(), "test2".to_string(), "test3".to_string(),],
581             db.list(2).expect("Failed to list entries.")
582         );
583 
584         // There should be no entries for owner 1.
585         assert_eq!(Vec::<String>::new(), db.list(1).expect("Failed to list entries."));
586 
587         // Check the content of the three entries.
588         assert_eq!(Some(TEST_BLOB1), db.get(2, "test1").expect("Failed to get entry.").as_deref());
589         assert_eq!(Some(TEST_BLOB2), db.get(2, "test2").expect("Failed to get entry.").as_deref());
590         assert_eq!(Some(TEST_BLOB3), db.get(2, "test3").expect("Failed to get entry.").as_deref());
591 
592         // Remove test2 and check and check that it is no longer retrievable.
593         assert!(db.remove(2, "test2").expect("Failed to remove entry."));
594         assert!(db.get(2, "test2").expect("Failed to get entry.").is_none());
595 
596         // test2 should now no longer be in the list.
597         assert_eq!(
598             vec!["test1".to_string(), "test3".to_string(),],
599             db.list(2).expect("Failed to list entries.")
600         );
601 
602         // Put on existing alias replaces it.
603         // Verify test1 is TEST_BLOB1.
604         assert_eq!(Some(TEST_BLOB1), db.get(2, "test1").expect("Failed to get entry.").as_deref());
605         db.put(2, "test1", TEST_BLOB4).expect("Failed to replace test1.");
606         // Verify test1 is TEST_BLOB4.
607         assert_eq!(Some(TEST_BLOB4), db.get(2, "test1").expect("Failed to get entry.").as_deref());
608     }
609 
610     #[test]
test_delete_uid()611     fn test_delete_uid() {
612         let test_dir = TempDir::new("test_delete_uid_").expect("Failed to create temp dir.");
613         let mut db = DB::new(&test_dir.build().push(LegacyKeystore::LEGACY_KEYSTORE_FILE_NAME))
614             .expect("Failed to open database.");
615 
616         // Insert three entries for owner 2.
617         db.put(2, "test1", TEST_BLOB1).expect("Failed to insert test1.");
618         db.put(2, "test2", TEST_BLOB2).expect("Failed to insert test2.");
619         db.put(3, "test3", TEST_BLOB3).expect("Failed to insert test3.");
620 
621         db.remove_uid(2).expect("Failed to remove uid 2");
622 
623         assert_eq!(Vec::<String>::new(), db.list(2).expect("Failed to list entries."));
624 
625         assert_eq!(vec!["test3".to_string(),], db.list(3).expect("Failed to list entries."));
626     }
627 
628     #[test]
test_delete_user()629     fn test_delete_user() {
630         let test_dir = TempDir::new("test_delete_user_").expect("Failed to create temp dir.");
631         let mut db = DB::new(&test_dir.build().push(LegacyKeystore::LEGACY_KEYSTORE_FILE_NAME))
632             .expect("Failed to open database.");
633 
634         // Insert three entries for owner 2.
635         db.put(2 + 2 * rustutils::users::AID_USER_OFFSET, "test1", TEST_BLOB1)
636             .expect("Failed to insert test1.");
637         db.put(4 + 2 * rustutils::users::AID_USER_OFFSET, "test2", TEST_BLOB2)
638             .expect("Failed to insert test2.");
639         db.put(3, "test3", TEST_BLOB3).expect("Failed to insert test3.");
640 
641         db.remove_user(2).expect("Failed to remove user 2");
642 
643         assert_eq!(
644             Vec::<String>::new(),
645             db.list(2 + 2 * rustutils::users::AID_USER_OFFSET).expect("Failed to list entries.")
646         );
647 
648         assert_eq!(
649             Vec::<String>::new(),
650             db.list(4 + 2 * rustutils::users::AID_USER_OFFSET).expect("Failed to list entries.")
651         );
652 
653         assert_eq!(vec!["test3".to_string(),], db.list(3).expect("Failed to list entries."));
654     }
655 
656     #[test]
concurrent_legacy_keystore_entry_test() -> Result<()>657     fn concurrent_legacy_keystore_entry_test() -> Result<()> {
658         let temp_dir = Arc::new(
659             TempDir::new("concurrent_legacy_keystore_entry_test_")
660                 .expect("Failed to create temp dir."),
661         );
662 
663         let db_path = temp_dir.build().push(LegacyKeystore::LEGACY_KEYSTORE_FILE_NAME).to_owned();
664 
665         let test_begin = Instant::now();
666 
667         let mut db = DB::new(&db_path).expect("Failed to open database.");
668         const ENTRY_COUNT: u32 = 5000u32;
669         const ENTRY_DB_COUNT: u32 = 5000u32;
670 
671         let mut actual_entry_count = ENTRY_COUNT;
672         // First insert ENTRY_COUNT entries.
673         for count in 0..ENTRY_COUNT {
674             if Instant::now().duration_since(test_begin) >= Duration::from_secs(15) {
675                 actual_entry_count = count;
676                 break;
677             }
678             let alias = format!("test_alias_{}", count);
679             db.put(1, &alias, TEST_BLOB1).expect("Failed to add entry (1).");
680         }
681 
682         // Insert more keys from a different thread and into a different namespace.
683         let db_path1 = db_path.clone();
684         let handle1 = thread::spawn(move || {
685             let mut db = DB::new(&db_path1).expect("Failed to open database.");
686 
687             for count in 0..actual_entry_count {
688                 if Instant::now().duration_since(test_begin) >= Duration::from_secs(40) {
689                     return;
690                 }
691                 let alias = format!("test_alias_{}", count);
692                 db.put(2, &alias, TEST_BLOB2).expect("Failed to add entry (2).");
693             }
694 
695             // Then delete them again.
696             for count in 0..actual_entry_count {
697                 if Instant::now().duration_since(test_begin) >= Duration::from_secs(40) {
698                     return;
699                 }
700                 let alias = format!("test_alias_{}", count);
701                 db.remove(2, &alias).expect("Remove Failed (2).");
702             }
703         });
704 
705         // And start deleting the first set of entries.
706         let db_path2 = db_path.clone();
707         let handle2 = thread::spawn(move || {
708             let mut db = DB::new(&db_path2).expect("Failed to open database.");
709 
710             for count in 0..actual_entry_count {
711                 if Instant::now().duration_since(test_begin) >= Duration::from_secs(40) {
712                     return;
713                 }
714                 let alias = format!("test_alias_{}", count);
715                 db.remove(1, &alias).expect("Remove Failed (1)).");
716             }
717         });
718 
719         // While a lot of inserting and deleting is going on we have to open database connections
720         // successfully and then insert and delete a specific entry.
721         let db_path3 = db_path.clone();
722         let handle3 = thread::spawn(move || {
723             for _count in 0..ENTRY_DB_COUNT {
724                 if Instant::now().duration_since(test_begin) >= Duration::from_secs(40) {
725                     return;
726                 }
727                 let mut db = DB::new(&db_path3).expect("Failed to open database.");
728 
729                 db.put(3, TEST_ALIAS, TEST_BLOB3).expect("Failed to add entry (3).");
730 
731                 db.remove(3, TEST_ALIAS).expect("Remove failed (3).");
732             }
733         });
734 
735         // While thread 3 is inserting and deleting TEST_ALIAS, we try to get the alias.
736         // This may yield an entry or none, but it must not fail.
737         let handle4 = thread::spawn(move || {
738             for _count in 0..ENTRY_DB_COUNT {
739                 if Instant::now().duration_since(test_begin) >= Duration::from_secs(40) {
740                     return;
741                 }
742                 let mut db = DB::new(&db_path).expect("Failed to open database.");
743 
744                 // This may return Some or None but it must not fail.
745                 db.get(3, TEST_ALIAS).expect("Failed to get entry (4).");
746             }
747         });
748 
749         handle1.join().expect("Thread 1 panicked.");
750         handle2.join().expect("Thread 2 panicked.");
751         handle3.join().expect("Thread 3 panicked.");
752         handle4.join().expect("Thread 4 panicked.");
753 
754         Ok(())
755     }
756 }
757