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