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