blob: cf61482b197e3ae0b4e94257a4232c96a9eb63fa [file] [log] [blame]
Janis Danisevskis77d72042021-01-20 15:36:30 -08001// 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
Janis Danisevskis3eb829d2021-06-14 14:18:20 -070015//! Implements the android.security.legacykeystore interface.
Janis Danisevskis77d72042021-01-20 15:36:30 -080016
Janis Danisevskis3eb829d2021-06-14 14:18:20 -070017use 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,
Janis Danisevskis77d72042021-01-20 15:36:30 -080021};
Janis Danisevskis3eb829d2021-06-14 14:18:20 -070022use android_security_legacykeystore::binder::{
Andrew Walbrande45c8b2021-04-13 14:42:38 +000023 BinderFeatures, ExceptionCode, Result as BinderResult, Status as BinderStatus, Strong,
24 ThreadState,
25};
Seth Moorefbe5cf52021-06-09 15:59:00 -070026use anyhow::{Context, Result};
Janis Danisevskis5898d152021-06-15 08:23:46 -070027use keystore2::{
Janis Danisevskisf84d0b02022-01-26 14:11:14 -080028 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,
Janis Danisevskis5898d152021-06-15 08:23:46 -070031};
Andrew Walbran78abb1e2023-05-30 16:20:56 +000032use rusqlite::{params, Connection, OptionalExtension, Transaction, TransactionBehavior};
Janis Danisevskis5898d152021-06-15 08:23:46 -070033use std::sync::Arc;
Janis Danisevskis06891072021-02-11 10:28:17 -080034use std::{
35 collections::HashSet,
36 path::{Path, PathBuf},
37};
Janis Danisevskis77d72042021-01-20 15:36:30 -080038
39struct DB {
40 conn: Connection,
41}
42
43impl DB {
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 };
Janis Danisevskis1be7e182021-04-12 14:31:12 -070048
Shaquille Johnsond28f5cb2023-11-23 11:12:18 +000049 if keystore2_flags::wal_db_journalmode_v2() {
50 // Update journal mode to WAL
51 db.conn
52 .pragma_update(None, "journal_mode", "WAL")
53 .context("Failed to connect in WAL mode for persistent db")?;
54 }
Janis Danisevskis3eb829d2021-06-14 14:18:20 -070055 db.init_tables().context("Trying to initialize legacy keystore db.")?;
Janis Danisevskis77d72042021-01-20 15:36:30 -080056 Ok(db)
57 }
58
59 fn with_transaction<T, F>(&mut self, behavior: TransactionBehavior, f: F) -> Result<T>
60 where
61 F: Fn(&Transaction) -> Result<T>,
62 {
63 loop {
64 match self
65 .conn
66 .transaction_with_behavior(behavior)
67 .context("In with_transaction.")
68 .and_then(|tx| f(&tx).map(|result| (result, tx)))
69 .and_then(|(result, tx)| {
70 tx.commit().context("In with_transaction: Failed to commit transaction.")?;
71 Ok(result)
72 }) {
73 Ok(result) => break Ok(result),
74 Err(e) => {
75 if Self::is_locked_error(&e) {
76 std::thread::sleep(std::time::Duration::from_micros(500));
77 continue;
78 } else {
79 return Err(e).context("In with_transaction.");
80 }
81 }
82 }
83 }
84 }
85
86 fn is_locked_error(e: &anyhow::Error) -> bool {
Janis Danisevskis13f09152021-04-19 09:55:15 -070087 matches!(
88 e.root_cause().downcast_ref::<rusqlite::ffi::Error>(),
89 Some(rusqlite::ffi::Error { code: rusqlite::ErrorCode::DatabaseBusy, .. })
90 | Some(rusqlite::ffi::Error { code: rusqlite::ErrorCode::DatabaseLocked, .. })
91 )
Janis Danisevskis77d72042021-01-20 15:36:30 -080092 }
93
94 fn init_tables(&mut self) -> Result<()> {
95 self.with_transaction(TransactionBehavior::Immediate, |tx| {
96 tx.execute(
97 "CREATE TABLE IF NOT EXISTS profiles (
98 owner INTEGER,
99 alias BLOB,
100 profile BLOB,
101 UNIQUE(owner, alias));",
Andrew Walbran78abb1e2023-05-30 16:20:56 +0000102 [],
Janis Danisevskis77d72042021-01-20 15:36:30 -0800103 )
104 .context("Failed to initialize \"profiles\" table.")?;
105 Ok(())
106 })
107 }
108
109 fn list(&mut self, caller_uid: u32) -> Result<Vec<String>> {
110 self.with_transaction(TransactionBehavior::Deferred, |tx| {
111 let mut stmt = tx
112 .prepare("SELECT alias FROM profiles WHERE owner = ? ORDER BY alias ASC;")
113 .context("In list: Failed to prepare statement.")?;
114
Chris Wailes263de9f2022-08-11 15:00:51 -0700115 // This allow is necessary to avoid the following error:
116 //
117 // error[E0597]: `stmt` does not live long enough
118 //
119 // See: https://github.com/rust-lang/rust-clippy/issues/8114
120 #[allow(clippy::let_and_return)]
Janis Danisevskis77d72042021-01-20 15:36:30 -0800121 let aliases = stmt
122 .query_map(params![caller_uid], |row| row.get(0))?
123 .collect::<rusqlite::Result<Vec<String>>>()
124 .context("In list: query_map failed.");
125 aliases
126 })
127 }
128
Janis Danisevskis3eb829d2021-06-14 14:18:20 -0700129 fn put(&mut self, caller_uid: u32, alias: &str, entry: &[u8]) -> Result<()> {
Shaquille Johnsonbe6e91d2023-10-21 19:09:17 +0100130 if keystore2_flags::disable_legacy_keystore_put_v2() {
131 return Err(Error::deprecated()).context(concat!(
132 "Storing into Keystore's legacy database is ",
133 "no longer supported, store in an app-specific database instead"
134 ));
135 }
Janis Danisevskis77d72042021-01-20 15:36:30 -0800136 self.with_transaction(TransactionBehavior::Immediate, |tx| {
137 tx.execute(
138 "INSERT OR REPLACE INTO profiles (owner, alias, profile) values (?, ?, ?)",
Janis Danisevskis3eb829d2021-06-14 14:18:20 -0700139 params![caller_uid, alias, entry,],
Janis Danisevskis77d72042021-01-20 15:36:30 -0800140 )
141 .context("In put: Failed to insert or replace.")?;
142 Ok(())
143 })
144 }
145
146 fn get(&mut self, caller_uid: u32, alias: &str) -> Result<Option<Vec<u8>>> {
147 self.with_transaction(TransactionBehavior::Deferred, |tx| {
148 tx.query_row(
149 "SELECT profile FROM profiles WHERE owner = ? AND alias = ?;",
150 params![caller_uid, alias],
151 |row| row.get(0),
152 )
153 .optional()
Janis Danisevskis3eb829d2021-06-14 14:18:20 -0700154 .context("In get: failed loading entry.")
Janis Danisevskis77d72042021-01-20 15:36:30 -0800155 })
156 }
157
158 fn remove(&mut self, caller_uid: u32, alias: &str) -> Result<bool> {
159 let removed = self.with_transaction(TransactionBehavior::Immediate, |tx| {
160 tx.execute(
161 "DELETE FROM profiles WHERE owner = ? AND alias = ?;",
162 params![caller_uid, alias],
163 )
164 .context("In remove: Failed to delete row.")
165 })?;
166 Ok(removed == 1)
167 }
Janis Danisevskis5898d152021-06-15 08:23:46 -0700168
169 fn remove_uid(&mut self, uid: u32) -> Result<()> {
170 self.with_transaction(TransactionBehavior::Immediate, |tx| {
171 tx.execute("DELETE FROM profiles WHERE owner = ?;", params![uid])
172 .context("In remove_uid: Failed to delete.")
173 })?;
174 Ok(())
175 }
176
177 fn remove_user(&mut self, user_id: u32) -> Result<()> {
178 self.with_transaction(TransactionBehavior::Immediate, |tx| {
179 tx.execute(
180 "DELETE FROM profiles WHERE cast ( ( owner/? ) as int) = ?;",
Joel Galenson81a50f22021-07-29 15:39:10 -0700181 params![rustutils::users::AID_USER_OFFSET, user_id],
Janis Danisevskis5898d152021-06-15 08:23:46 -0700182 )
183 .context("In remove_uid: Failed to delete.")
184 })?;
185 Ok(())
186 }
Janis Danisevskis77d72042021-01-20 15:36:30 -0800187}
188
Janis Danisevskis3eb829d2021-06-14 14:18:20 -0700189/// This is the main LegacyKeystore error type, it wraps binder exceptions and the
190/// LegacyKeystore errors.
Chris Wailes263de9f2022-08-11 15:00:51 -0700191#[derive(Debug, thiserror::Error, PartialEq, Eq)]
Janis Danisevskis77d72042021-01-20 15:36:30 -0800192pub enum Error {
Janis Danisevskis3eb829d2021-06-14 14:18:20 -0700193 /// Wraps a LegacyKeystore error code.
Janis Danisevskis77d72042021-01-20 15:36:30 -0800194 #[error("Error::Error({0:?})")]
195 Error(i32),
196 /// Wraps a Binder exception code other than a service specific exception.
197 #[error("Binder exception code {0:?}, {1:?}")]
198 Binder(ExceptionCode, i32),
199}
200
201impl Error {
202 /// Short hand for `Error::Error(ERROR_SYSTEM_ERROR)`
203 pub fn sys() -> Self {
204 Error::Error(ERROR_SYSTEM_ERROR)
205 }
206
Janis Danisevskis3eb829d2021-06-14 14:18:20 -0700207 /// Short hand for `Error::Error(ERROR_ENTRY_NOT_FOUND)`
Janis Danisevskis77d72042021-01-20 15:36:30 -0800208 pub fn not_found() -> Self {
Janis Danisevskis3eb829d2021-06-14 14:18:20 -0700209 Error::Error(ERROR_ENTRY_NOT_FOUND)
210 }
211
212 /// Short hand for `Error::Error(ERROR_PERMISSION_DENIED)`
213 pub fn perm() -> Self {
214 Error::Error(ERROR_PERMISSION_DENIED)
Janis Danisevskis77d72042021-01-20 15:36:30 -0800215 }
Shaquille Johnsonbe6e91d2023-10-21 19:09:17 +0100216
217 /// Short hand for `Error::Error(ERROR_SYSTEM_ERROR)`
218 pub fn deprecated() -> Self {
219 Error::Error(ERROR_SYSTEM_ERROR)
220 }
Janis Danisevskis77d72042021-01-20 15:36:30 -0800221}
222
Janis Danisevskis3eb829d2021-06-14 14:18:20 -0700223/// This function should be used by legacykeystore service calls to translate error conditions
Janis Danisevskis77d72042021-01-20 15:36:30 -0800224/// into service specific exceptions.
225///
Janis Danisevskis3eb829d2021-06-14 14:18:20 -0700226/// All error conditions get logged by this function, except for ERROR_ENTRY_NOT_FOUND error.
Janis Danisevskis77d72042021-01-20 15:36:30 -0800227///
228/// `Error::Error(x)` variants get mapped onto a service specific error code of `x`.
229///
230/// All non `Error` error conditions get mapped onto `ERROR_SYSTEM_ERROR`.
231///
232/// `handle_ok` will be called if `result` is `Ok(value)` where `value` will be passed
233/// as argument to `handle_ok`. `handle_ok` must generate a `BinderResult<T>`, but it
234/// typically returns Ok(value).
235fn map_or_log_err<T, U, F>(result: Result<U>, handle_ok: F) -> BinderResult<T>
236where
237 F: FnOnce(U) -> BinderResult<T>,
238{
239 result.map_or_else(
240 |e| {
Janis Danisevskis77d72042021-01-20 15:36:30 -0800241 let root_cause = e.root_cause();
Hasini Gunasinghee1d1bbd2021-04-20 18:13:25 +0000242 let (rc, log_error) = match root_cause.downcast_ref::<Error>() {
Janis Danisevskis3eb829d2021-06-14 14:18:20 -0700243 // Make the entry not found errors silent.
244 Some(Error::Error(ERROR_ENTRY_NOT_FOUND)) => (ERROR_ENTRY_NOT_FOUND, false),
Hasini Gunasinghee1d1bbd2021-04-20 18:13:25 +0000245 Some(Error::Error(e)) => (*e, true),
246 Some(Error::Binder(_, _)) | None => (ERROR_SYSTEM_ERROR, true),
Janis Danisevskis77d72042021-01-20 15:36:30 -0800247 };
Hasini Gunasinghee1d1bbd2021-04-20 18:13:25 +0000248 if log_error {
249 log::error!("{:?}", e);
250 }
Janis Danisevskisea03cff2021-12-16 08:10:17 -0800251 Err(BinderStatus::new_service_specific_error(
252 rc,
253 anyhow_error_to_cstring(&e).as_deref(),
254 ))
Janis Danisevskis77d72042021-01-20 15:36:30 -0800255 },
256 handle_ok,
257 )
258}
259
Janis Danisevskis5898d152021-06-15 08:23:46 -0700260struct LegacyKeystoreDeleteListener {
261 legacy_keystore: Arc<LegacyKeystore>,
262}
263
264impl DeleteListener for LegacyKeystoreDeleteListener {
265 fn delete_namespace(&self, domain: Domain, namespace: i64) -> Result<()> {
266 self.legacy_keystore.delete_namespace(domain, namespace)
267 }
268 fn delete_user(&self, user_id: u32) -> Result<()> {
269 self.legacy_keystore.delete_user(user_id)
270 }
271}
272
Janis Danisevskis3eb829d2021-06-14 14:18:20 -0700273/// Implements ILegacyKeystore AIDL interface.
274pub struct LegacyKeystore {
Janis Danisevskis77d72042021-01-20 15:36:30 -0800275 db_path: PathBuf,
Janis Danisevskis06891072021-02-11 10:28:17 -0800276 async_task: AsyncTask,
277}
278
279struct AsyncState {
280 recently_imported: HashSet<(u32, String)>,
281 legacy_loader: LegacyBlobLoader,
282 db_path: PathBuf,
Janis Danisevskis77d72042021-01-20 15:36:30 -0800283}
284
Janis Danisevskis3eb829d2021-06-14 14:18:20 -0700285impl LegacyKeystore {
286 /// Note: The filename was chosen before the purpose of this module was extended.
287 /// It is kept for backward compatibility with early adopters.
288 const LEGACY_KEYSTORE_FILE_NAME: &'static str = "vpnprofilestore.sqlite";
289
Janis Danisevskis5898d152021-06-15 08:23:46 -0700290 const WIFI_NAMESPACE: i64 = 102;
291 const AID_WIFI: u32 = 1010;
292
Janis Danisevskis3eb829d2021-06-14 14:18:20 -0700293 /// Creates a new LegacyKeystore instance.
Janis Danisevskis5898d152021-06-15 08:23:46 -0700294 pub fn new_native_binder(
295 path: &Path,
296 ) -> (Box<dyn DeleteListener + Send + Sync + 'static>, Strong<dyn ILegacyKeystore>) {
Janis Danisevskis77d72042021-01-20 15:36:30 -0800297 let mut db_path = path.to_path_buf();
Janis Danisevskis3eb829d2021-06-14 14:18:20 -0700298 db_path.push(Self::LEGACY_KEYSTORE_FILE_NAME);
Janis Danisevskis06891072021-02-11 10:28:17 -0800299
Janis Danisevskis5898d152021-06-15 08:23:46 -0700300 let legacy_keystore = Arc::new(Self { db_path, async_task: Default::default() });
301 legacy_keystore.init_shelf(path);
302 let service = LegacyKeystoreService { legacy_keystore: legacy_keystore.clone() };
303 (
304 Box::new(LegacyKeystoreDeleteListener { legacy_keystore }),
305 BnLegacyKeystore::new_binder(service, BinderFeatures::default()),
306 )
Janis Danisevskis77d72042021-01-20 15:36:30 -0800307 }
308
309 fn open_db(&self) -> Result<DB> {
310 DB::new(&self.db_path).context("In open_db: Failed to open db.")
311 }
312
Janis Danisevskis3eb829d2021-06-14 14:18:20 -0700313 fn get_effective_uid(uid: i32) -> Result<u32> {
314 const AID_SYSTEM: u32 = 1000;
Janis Danisevskis77d72042021-01-20 15:36:30 -0800315 let calling_uid = ThreadState::get_calling_uid();
Janis Danisevskis3eb829d2021-06-14 14:18:20 -0700316 let uid = uid as u32;
Janis Danisevskis06891072021-02-11 10:28:17 -0800317
Janis Danisevskis3eb829d2021-06-14 14:18:20 -0700318 if uid == UID_SELF as u32 || uid == calling_uid {
319 Ok(calling_uid)
Janis Danisevskis5898d152021-06-15 08:23:46 -0700320 } else if calling_uid == AID_SYSTEM && uid == Self::AID_WIFI {
Janis Danisevskis3eb829d2021-06-14 14:18:20 -0700321 // The only exception for legacy reasons is allowing SYSTEM to access
322 // the WIFI namespace.
323 // IMPORTANT: If you attempt to add more exceptions, it means you are adding
324 // more callers to this deprecated feature. DON'T!
Janis Danisevskis5898d152021-06-15 08:23:46 -0700325 Ok(Self::AID_WIFI)
Janis Danisevskis3eb829d2021-06-14 14:18:20 -0700326 } else {
327 Err(Error::perm()).with_context(|| {
328 format!("In get_effective_uid: caller: {}, requested uid: {}.", calling_uid, uid)
329 })
Janis Danisevskis06891072021-02-11 10:28:17 -0800330 }
Janis Danisevskis3eb829d2021-06-14 14:18:20 -0700331 }
332
333 fn get(&self, alias: &str, uid: i32) -> Result<Vec<u8>> {
334 let mut db = self.open_db().context("In get.")?;
335 let uid = Self::get_effective_uid(uid).context("In get.")?;
336
337 if let Some(entry) = db.get(uid, alias).context("In get: Trying to load entry from DB.")? {
338 return Ok(entry);
339 }
Janis Danisevskisf84d0b02022-01-26 14:11:14 -0800340 if self.get_legacy(uid, alias).context("In get: Trying to import legacy blob.")? {
341 // If we were able to import a legacy blob try again.
Janis Danisevskis3eb829d2021-06-14 14:18:20 -0700342 if let Some(entry) =
343 db.get(uid, alias).context("In get: Trying to load entry from DB.")?
Janis Danisevskis06891072021-02-11 10:28:17 -0800344 {
Janis Danisevskis3eb829d2021-06-14 14:18:20 -0700345 return Ok(entry);
Janis Danisevskis06891072021-02-11 10:28:17 -0800346 }
347 }
Janis Danisevskis3eb829d2021-06-14 14:18:20 -0700348 Err(Error::not_found()).context("In get: No such entry.")
Janis Danisevskis77d72042021-01-20 15:36:30 -0800349 }
350
Janis Danisevskis3eb829d2021-06-14 14:18:20 -0700351 fn put(&self, alias: &str, uid: i32, entry: &[u8]) -> Result<()> {
Shaquille Johnsonbe6e91d2023-10-21 19:09:17 +0100352 if keystore2_flags::disable_legacy_keystore_put_v2() {
353 return Err(Error::deprecated()).context(concat!(
354 "Storing into Keystore's legacy database is ",
355 "no longer supported, store in an app-specific database instead"
356 ));
357 }
Janis Danisevskis3eb829d2021-06-14 14:18:20 -0700358 let uid = Self::get_effective_uid(uid).context("In put.")?;
Janis Danisevskis06891072021-02-11 10:28:17 -0800359 let mut db = self.open_db().context("In put.")?;
Janis Danisevskisf84d0b02022-01-26 14:11:14 -0800360 db.put(uid, alias, entry).context("In put: Trying to insert entry into DB.")?;
361 // When replacing an entry, make sure that there is no stale legacy file entry.
362 let _ = self.remove_legacy(uid, alias);
363 Ok(())
Janis Danisevskis77d72042021-01-20 15:36:30 -0800364 }
365
Janis Danisevskis3eb829d2021-06-14 14:18:20 -0700366 fn remove(&self, alias: &str, uid: i32) -> Result<()> {
367 let uid = Self::get_effective_uid(uid).context("In remove.")?;
Janis Danisevskis06891072021-02-11 10:28:17 -0800368 let mut db = self.open_db().context("In remove.")?;
Janis Danisevskisf84d0b02022-01-26 14:11:14 -0800369
370 if self.remove_legacy(uid, alias).context("In remove: trying to remove legacy entry")? {
371 return Ok(());
372 }
Janis Danisevskis3eb829d2021-06-14 14:18:20 -0700373 let removed =
374 db.remove(uid, alias).context("In remove: Trying to remove entry from DB.")?;
Janis Danisevskis77d72042021-01-20 15:36:30 -0800375 if removed {
376 Ok(())
377 } else {
Janis Danisevskis3eb829d2021-06-14 14:18:20 -0700378 Err(Error::not_found()).context("In remove: No such entry.")
Janis Danisevskis77d72042021-01-20 15:36:30 -0800379 }
380 }
381
Janis Danisevskis5898d152021-06-15 08:23:46 -0700382 fn delete_namespace(&self, domain: Domain, namespace: i64) -> Result<()> {
383 let uid = match domain {
384 Domain::APP => namespace as u32,
385 Domain::SELINUX => {
386 if namespace == Self::WIFI_NAMESPACE {
387 // Namespace WIFI gets mapped to AID_WIFI.
388 Self::AID_WIFI
389 } else {
390 // Nothing to do for any other namespace.
391 return Ok(());
392 }
393 }
394 _ => return Ok(()),
395 };
396
397 if let Err(e) = self.bulk_delete_uid(uid) {
398 log::warn!("In LegacyKeystore::delete_namespace: {:?}", e);
399 }
400 let mut db = self.open_db().context("In LegacyKeystore::delete_namespace.")?;
401 db.remove_uid(uid).context("In LegacyKeystore::delete_namespace.")
402 }
403
404 fn delete_user(&self, user_id: u32) -> Result<()> {
405 if let Err(e) = self.bulk_delete_user(user_id) {
406 log::warn!("In LegacyKeystore::delete_user: {:?}", e);
407 }
408 let mut db = self.open_db().context("In LegacyKeystore::delete_user.")?;
409 db.remove_user(user_id).context("In LegacyKeystore::delete_user.")
410 }
411
Janis Danisevskis3eb829d2021-06-14 14:18:20 -0700412 fn list(&self, prefix: &str, uid: i32) -> Result<Vec<String>> {
Janis Danisevskis77d72042021-01-20 15:36:30 -0800413 let mut db = self.open_db().context("In list.")?;
Janis Danisevskis3eb829d2021-06-14 14:18:20 -0700414 let uid = Self::get_effective_uid(uid).context("In list.")?;
415 let mut result = self.list_legacy(uid).context("In list.")?;
416 result.append(&mut db.list(uid).context("In list: Trying to get list of entries.")?);
Charisee28e6f0b2022-09-15 01:07:46 +0000417 result.retain(|s| s.starts_with(prefix));
Janis Danisevskis06891072021-02-11 10:28:17 -0800418 result.sort_unstable();
419 result.dedup();
420 Ok(result)
421 }
422
423 fn init_shelf(&self, path: &Path) {
424 let mut db_path = path.to_path_buf();
425 self.async_task.queue_hi(move |shelf| {
426 let legacy_loader = LegacyBlobLoader::new(&db_path);
Janis Danisevskis3eb829d2021-06-14 14:18:20 -0700427 db_path.push(Self::LEGACY_KEYSTORE_FILE_NAME);
Janis Danisevskis06891072021-02-11 10:28:17 -0800428
429 shelf.put(AsyncState { legacy_loader, db_path, recently_imported: Default::default() });
430 })
431 }
432
433 fn do_serialized<F, T: Send + 'static>(&self, f: F) -> Result<T>
434 where
435 F: FnOnce(&mut AsyncState) -> Result<T> + Send + 'static,
436 {
437 let (sender, receiver) = std::sync::mpsc::channel::<Result<T>>();
438 self.async_task.queue_hi(move |shelf| {
439 let state = shelf.get_downcast_mut::<AsyncState>().expect("Failed to get shelf.");
440 sender.send(f(state)).expect("Failed to send result.");
441 });
442 receiver.recv().context("In do_serialized: Failed to receive result.")?
443 }
444
445 fn list_legacy(&self, uid: u32) -> Result<Vec<String>> {
446 self.do_serialized(move |state| {
447 state
448 .legacy_loader
Janis Danisevskis5898d152021-06-15 08:23:46 -0700449 .list_legacy_keystore_entries_for_uid(uid)
Janis Danisevskis3eb829d2021-06-14 14:18:20 -0700450 .context("Trying to list legacy keystore entries.")
Janis Danisevskis06891072021-02-11 10:28:17 -0800451 })
452 .context("In list_legacy.")
453 }
454
455 fn get_legacy(&self, uid: u32, alias: &str) -> Result<bool> {
456 let alias = alias.to_string();
457 self.do_serialized(move |state| {
458 if state.recently_imported.contains(&(uid, alias.clone())) {
459 return Ok(true);
460 }
461 let mut db = DB::new(&state.db_path).context("In open_db: Failed to open db.")?;
Janis Danisevskisf84d0b02022-01-26 14:11:14 -0800462 let imported =
463 Self::import_one_legacy_entry(uid, &alias, &state.legacy_loader, &mut db)
464 .context("Trying to import legacy keystore entries.")?;
465 if imported {
Janis Danisevskis06891072021-02-11 10:28:17 -0800466 state.recently_imported.insert((uid, alias));
467 }
Janis Danisevskisf84d0b02022-01-26 14:11:14 -0800468 Ok(imported)
Janis Danisevskis06891072021-02-11 10:28:17 -0800469 })
470 .context("In get_legacy.")
471 }
472
Janis Danisevskisf84d0b02022-01-26 14:11:14 -0800473 fn remove_legacy(&self, uid: u32, alias: &str) -> Result<bool> {
474 let alias = alias.to_string();
475 self.do_serialized(move |state| {
476 if state.recently_imported.contains(&(uid, alias.clone())) {
477 return Ok(false);
478 }
479 state
480 .legacy_loader
481 .remove_legacy_keystore_entry(uid, &alias)
482 .context("Trying to remove legacy entry.")
483 })
484 }
485
Janis Danisevskis5898d152021-06-15 08:23:46 -0700486 fn bulk_delete_uid(&self, uid: u32) -> Result<()> {
487 self.do_serialized(move |state| {
488 let entries = state
489 .legacy_loader
490 .list_legacy_keystore_entries_for_uid(uid)
491 .context("In bulk_delete_uid: Trying to list entries.")?;
492 for alias in entries.iter() {
493 if let Err(e) = state.legacy_loader.remove_legacy_keystore_entry(uid, alias) {
494 log::warn!("In bulk_delete_uid: Failed to delete legacy entry. {:?}", e);
495 }
496 }
497 Ok(())
498 })
499 }
500
501 fn bulk_delete_user(&self, user_id: u32) -> Result<()> {
502 self.do_serialized(move |state| {
503 let entries = state
504 .legacy_loader
505 .list_legacy_keystore_entries_for_user(user_id)
506 .context("In bulk_delete_user: Trying to list entries.")?;
507 for (uid, entries) in entries.iter() {
508 for alias in entries.iter() {
509 if let Err(e) = state.legacy_loader.remove_legacy_keystore_entry(*uid, alias) {
510 log::warn!("In bulk_delete_user: Failed to delete legacy entry. {:?}", e);
511 }
512 }
513 }
514 Ok(())
515 })
516 }
517
Janis Danisevskisf84d0b02022-01-26 14:11:14 -0800518 fn import_one_legacy_entry(
Janis Danisevskis06891072021-02-11 10:28:17 -0800519 uid: u32,
520 alias: &str,
521 legacy_loader: &LegacyBlobLoader,
522 db: &mut DB,
523 ) -> Result<bool> {
524 let blob = legacy_loader
Janis Danisevskisf84d0b02022-01-26 14:11:14 -0800525 .read_legacy_keystore_entry(uid, alias, |ciphertext, iv, tag, _salt, _key_size| {
Eric Biggers673d34a2023-10-18 01:54:18 +0000526 if let Some(key) = SUPER_KEY
527 .read()
528 .unwrap()
529 .get_after_first_unlock_key_by_user_id(uid_to_android_user(uid))
Janis Danisevskisf84d0b02022-01-26 14:11:14 -0800530 {
531 key.decrypt(ciphertext, iv, tag)
532 } else {
533 Err(Error::sys()).context("No key found for user. Device may be locked.")
534 }
535 })
536 .context("In import_one_legacy_entry: Trying to read legacy keystore entry.")?;
Janis Danisevskis3eb829d2021-06-14 14:18:20 -0700537 if let Some(entry) = blob {
538 db.put(uid, alias, &entry)
Janis Danisevskisf84d0b02022-01-26 14:11:14 -0800539 .context("In import_one_legacy_entry: Trying to insert entry into DB.")?;
Janis Danisevskis06891072021-02-11 10:28:17 -0800540 legacy_loader
Janis Danisevskis3eb829d2021-06-14 14:18:20 -0700541 .remove_legacy_keystore_entry(uid, alias)
Janis Danisevskisf84d0b02022-01-26 14:11:14 -0800542 .context("In import_one_legacy_entry: Trying to delete legacy keystore entry.")?;
Janis Danisevskis06891072021-02-11 10:28:17 -0800543 Ok(true)
544 } else {
545 Ok(false)
546 }
Janis Danisevskis77d72042021-01-20 15:36:30 -0800547 }
548}
549
Janis Danisevskis5898d152021-06-15 08:23:46 -0700550struct LegacyKeystoreService {
551 legacy_keystore: Arc<LegacyKeystore>,
552}
Janis Danisevskis77d72042021-01-20 15:36:30 -0800553
Janis Danisevskis5898d152021-06-15 08:23:46 -0700554impl binder::Interface for LegacyKeystoreService {}
555
556impl ILegacyKeystore for LegacyKeystoreService {
Janis Danisevskis3eb829d2021-06-14 14:18:20 -0700557 fn get(&self, alias: &str, uid: i32) -> BinderResult<Vec<u8>> {
558 let _wp = wd::watch_millis("ILegacyKeystore::get", 500);
Janis Danisevskis5898d152021-06-15 08:23:46 -0700559 map_or_log_err(self.legacy_keystore.get(alias, uid), Ok)
Janis Danisevskis77d72042021-01-20 15:36:30 -0800560 }
Janis Danisevskis3eb829d2021-06-14 14:18:20 -0700561 fn put(&self, alias: &str, uid: i32, entry: &[u8]) -> BinderResult<()> {
562 let _wp = wd::watch_millis("ILegacyKeystore::put", 500);
Janis Danisevskis5898d152021-06-15 08:23:46 -0700563 map_or_log_err(self.legacy_keystore.put(alias, uid, entry), Ok)
Janis Danisevskis77d72042021-01-20 15:36:30 -0800564 }
Janis Danisevskis3eb829d2021-06-14 14:18:20 -0700565 fn remove(&self, alias: &str, uid: i32) -> BinderResult<()> {
566 let _wp = wd::watch_millis("ILegacyKeystore::remove", 500);
Janis Danisevskis5898d152021-06-15 08:23:46 -0700567 map_or_log_err(self.legacy_keystore.remove(alias, uid), Ok)
Janis Danisevskis77d72042021-01-20 15:36:30 -0800568 }
Janis Danisevskis3eb829d2021-06-14 14:18:20 -0700569 fn list(&self, prefix: &str, uid: i32) -> BinderResult<Vec<String>> {
570 let _wp = wd::watch_millis("ILegacyKeystore::list", 500);
Janis Danisevskis5898d152021-06-15 08:23:46 -0700571 map_or_log_err(self.legacy_keystore.list(prefix, uid), Ok)
Janis Danisevskis77d72042021-01-20 15:36:30 -0800572 }
573}
574
575#[cfg(test)]
576mod db_test {
577 use super::*;
578 use keystore2_test_utils::TempDir;
Janis Danisevskis1be7e182021-04-12 14:31:12 -0700579 use std::sync::Arc;
580 use std::thread;
581 use std::time::Duration;
582 use std::time::Instant;
Janis Danisevskis77d72042021-01-20 15:36:30 -0800583
Chris Wailesd5aaaef2021-07-27 16:04:33 -0700584 static TEST_ALIAS: &str = "test_alias";
Janis Danisevskis77d72042021-01-20 15:36:30 -0800585 static TEST_BLOB1: &[u8] = &[1, 2, 3, 4, 5, 6, 7, 8, 9, 0];
586 static TEST_BLOB2: &[u8] = &[2, 2, 3, 4, 5, 6, 7, 8, 9, 0];
587 static TEST_BLOB3: &[u8] = &[3, 2, 3, 4, 5, 6, 7, 8, 9, 0];
588 static TEST_BLOB4: &[u8] = &[3, 2, 3, 4, 5, 6, 7, 8, 9, 0];
589
590 #[test]
Janis Danisevskis3eb829d2021-06-14 14:18:20 -0700591 fn test_entry_db() {
592 let test_dir = TempDir::new("entrydb_test_").expect("Failed to create temp dir.");
593 let mut db = DB::new(&test_dir.build().push(LegacyKeystore::LEGACY_KEYSTORE_FILE_NAME))
594 .expect("Failed to open database.");
Janis Danisevskis77d72042021-01-20 15:36:30 -0800595
Janis Danisevskis3eb829d2021-06-14 14:18:20 -0700596 // Insert three entries for owner 2.
Janis Danisevskis77d72042021-01-20 15:36:30 -0800597 db.put(2, "test1", TEST_BLOB1).expect("Failed to insert test1.");
598 db.put(2, "test2", TEST_BLOB2).expect("Failed to insert test2.");
599 db.put(2, "test3", TEST_BLOB3).expect("Failed to insert test3.");
600
601 // Check list returns all inserted aliases.
602 assert_eq!(
603 vec!["test1".to_string(), "test2".to_string(), "test3".to_string(),],
Janis Danisevskis3eb829d2021-06-14 14:18:20 -0700604 db.list(2).expect("Failed to list entries.")
Janis Danisevskis77d72042021-01-20 15:36:30 -0800605 );
606
Janis Danisevskis3eb829d2021-06-14 14:18:20 -0700607 // There should be no entries for owner 1.
608 assert_eq!(Vec::<String>::new(), db.list(1).expect("Failed to list entries."));
Janis Danisevskis77d72042021-01-20 15:36:30 -0800609
610 // Check the content of the three entries.
Janis Danisevskis3eb829d2021-06-14 14:18:20 -0700611 assert_eq!(Some(TEST_BLOB1), db.get(2, "test1").expect("Failed to get entry.").as_deref());
612 assert_eq!(Some(TEST_BLOB2), db.get(2, "test2").expect("Failed to get entry.").as_deref());
613 assert_eq!(Some(TEST_BLOB3), db.get(2, "test3").expect("Failed to get entry.").as_deref());
Janis Danisevskis77d72042021-01-20 15:36:30 -0800614
615 // Remove test2 and check and check that it is no longer retrievable.
Janis Danisevskis3eb829d2021-06-14 14:18:20 -0700616 assert!(db.remove(2, "test2").expect("Failed to remove entry."));
617 assert!(db.get(2, "test2").expect("Failed to get entry.").is_none());
Janis Danisevskis77d72042021-01-20 15:36:30 -0800618
619 // test2 should now no longer be in the list.
620 assert_eq!(
621 vec!["test1".to_string(), "test3".to_string(),],
Janis Danisevskis3eb829d2021-06-14 14:18:20 -0700622 db.list(2).expect("Failed to list entries.")
Janis Danisevskis77d72042021-01-20 15:36:30 -0800623 );
624
625 // Put on existing alias replaces it.
626 // Verify test1 is TEST_BLOB1.
Janis Danisevskis3eb829d2021-06-14 14:18:20 -0700627 assert_eq!(Some(TEST_BLOB1), db.get(2, "test1").expect("Failed to get entry.").as_deref());
Janis Danisevskis77d72042021-01-20 15:36:30 -0800628 db.put(2, "test1", TEST_BLOB4).expect("Failed to replace test1.");
629 // Verify test1 is TEST_BLOB4.
Janis Danisevskis3eb829d2021-06-14 14:18:20 -0700630 assert_eq!(Some(TEST_BLOB4), db.get(2, "test1").expect("Failed to get entry.").as_deref());
Janis Danisevskis77d72042021-01-20 15:36:30 -0800631 }
Janis Danisevskis1be7e182021-04-12 14:31:12 -0700632
633 #[test]
Janis Danisevskis5898d152021-06-15 08:23:46 -0700634 fn test_delete_uid() {
635 let test_dir = TempDir::new("test_delete_uid_").expect("Failed to create temp dir.");
636 let mut db = DB::new(&test_dir.build().push(LegacyKeystore::LEGACY_KEYSTORE_FILE_NAME))
637 .expect("Failed to open database.");
638
639 // Insert three entries for owner 2.
640 db.put(2, "test1", TEST_BLOB1).expect("Failed to insert test1.");
641 db.put(2, "test2", TEST_BLOB2).expect("Failed to insert test2.");
642 db.put(3, "test3", TEST_BLOB3).expect("Failed to insert test3.");
643
644 db.remove_uid(2).expect("Failed to remove uid 2");
645
646 assert_eq!(Vec::<String>::new(), db.list(2).expect("Failed to list entries."));
647
648 assert_eq!(vec!["test3".to_string(),], db.list(3).expect("Failed to list entries."));
649 }
650
651 #[test]
652 fn test_delete_user() {
653 let test_dir = TempDir::new("test_delete_user_").expect("Failed to create temp dir.");
654 let mut db = DB::new(&test_dir.build().push(LegacyKeystore::LEGACY_KEYSTORE_FILE_NAME))
655 .expect("Failed to open database.");
656
657 // Insert three entries for owner 2.
Joel Galenson81a50f22021-07-29 15:39:10 -0700658 db.put(2 + 2 * rustutils::users::AID_USER_OFFSET, "test1", TEST_BLOB1)
Janis Danisevskis5898d152021-06-15 08:23:46 -0700659 .expect("Failed to insert test1.");
Joel Galenson81a50f22021-07-29 15:39:10 -0700660 db.put(4 + 2 * rustutils::users::AID_USER_OFFSET, "test2", TEST_BLOB2)
Janis Danisevskis5898d152021-06-15 08:23:46 -0700661 .expect("Failed to insert test2.");
662 db.put(3, "test3", TEST_BLOB3).expect("Failed to insert test3.");
663
664 db.remove_user(2).expect("Failed to remove user 2");
665
666 assert_eq!(
667 Vec::<String>::new(),
Joel Galenson81a50f22021-07-29 15:39:10 -0700668 db.list(2 + 2 * rustutils::users::AID_USER_OFFSET).expect("Failed to list entries.")
Janis Danisevskis5898d152021-06-15 08:23:46 -0700669 );
670
671 assert_eq!(
672 Vec::<String>::new(),
Joel Galenson81a50f22021-07-29 15:39:10 -0700673 db.list(4 + 2 * rustutils::users::AID_USER_OFFSET).expect("Failed to list entries.")
Janis Danisevskis5898d152021-06-15 08:23:46 -0700674 );
675
676 assert_eq!(vec!["test3".to_string(),], db.list(3).expect("Failed to list entries."));
677 }
678
679 #[test]
Janis Danisevskis3eb829d2021-06-14 14:18:20 -0700680 fn concurrent_legacy_keystore_entry_test() -> Result<()> {
Janis Danisevskis1be7e182021-04-12 14:31:12 -0700681 let temp_dir = Arc::new(
Janis Danisevskis3eb829d2021-06-14 14:18:20 -0700682 TempDir::new("concurrent_legacy_keystore_entry_test_")
683 .expect("Failed to create temp dir."),
Janis Danisevskis1be7e182021-04-12 14:31:12 -0700684 );
685
Janis Danisevskis3eb829d2021-06-14 14:18:20 -0700686 let db_path = temp_dir.build().push(LegacyKeystore::LEGACY_KEYSTORE_FILE_NAME).to_owned();
Janis Danisevskis1be7e182021-04-12 14:31:12 -0700687
688 let test_begin = Instant::now();
689
690 let mut db = DB::new(&db_path).expect("Failed to open database.");
Janis Danisevskis3eb829d2021-06-14 14:18:20 -0700691 const ENTRY_COUNT: u32 = 5000u32;
692 const ENTRY_DB_COUNT: u32 = 5000u32;
Janis Danisevskis1be7e182021-04-12 14:31:12 -0700693
Janis Danisevskis3eb829d2021-06-14 14:18:20 -0700694 let mut actual_entry_count = ENTRY_COUNT;
695 // First insert ENTRY_COUNT entries.
696 for count in 0..ENTRY_COUNT {
Janis Danisevskis1be7e182021-04-12 14:31:12 -0700697 if Instant::now().duration_since(test_begin) >= Duration::from_secs(15) {
Janis Danisevskis3eb829d2021-06-14 14:18:20 -0700698 actual_entry_count = count;
Janis Danisevskis1be7e182021-04-12 14:31:12 -0700699 break;
700 }
701 let alias = format!("test_alias_{}", count);
Janis Danisevskis3eb829d2021-06-14 14:18:20 -0700702 db.put(1, &alias, TEST_BLOB1).expect("Failed to add entry (1).");
Janis Danisevskis1be7e182021-04-12 14:31:12 -0700703 }
704
705 // Insert more keys from a different thread and into a different namespace.
706 let db_path1 = db_path.clone();
707 let handle1 = thread::spawn(move || {
708 let mut db = DB::new(&db_path1).expect("Failed to open database.");
709
Janis Danisevskis3eb829d2021-06-14 14:18:20 -0700710 for count in 0..actual_entry_count {
Janis Danisevskis1be7e182021-04-12 14:31:12 -0700711 if Instant::now().duration_since(test_begin) >= Duration::from_secs(40) {
712 return;
713 }
714 let alias = format!("test_alias_{}", count);
Janis Danisevskis3eb829d2021-06-14 14:18:20 -0700715 db.put(2, &alias, TEST_BLOB2).expect("Failed to add entry (2).");
Janis Danisevskis1be7e182021-04-12 14:31:12 -0700716 }
717
718 // Then delete them again.
Janis Danisevskis3eb829d2021-06-14 14:18:20 -0700719 for count in 0..actual_entry_count {
Janis Danisevskis1be7e182021-04-12 14:31:12 -0700720 if Instant::now().duration_since(test_begin) >= Duration::from_secs(40) {
721 return;
722 }
723 let alias = format!("test_alias_{}", count);
724 db.remove(2, &alias).expect("Remove Failed (2).");
725 }
726 });
727
Janis Danisevskis3eb829d2021-06-14 14:18:20 -0700728 // And start deleting the first set of entries.
Janis Danisevskis1be7e182021-04-12 14:31:12 -0700729 let db_path2 = db_path.clone();
730 let handle2 = thread::spawn(move || {
731 let mut db = DB::new(&db_path2).expect("Failed to open database.");
732
Janis Danisevskis3eb829d2021-06-14 14:18:20 -0700733 for count in 0..actual_entry_count {
Janis Danisevskis1be7e182021-04-12 14:31:12 -0700734 if Instant::now().duration_since(test_begin) >= Duration::from_secs(40) {
735 return;
736 }
737 let alias = format!("test_alias_{}", count);
738 db.remove(1, &alias).expect("Remove Failed (1)).");
739 }
740 });
741
742 // While a lot of inserting and deleting is going on we have to open database connections
Janis Danisevskis3eb829d2021-06-14 14:18:20 -0700743 // successfully and then insert and delete a specific entry.
Janis Danisevskis1be7e182021-04-12 14:31:12 -0700744 let db_path3 = db_path.clone();
745 let handle3 = thread::spawn(move || {
Janis Danisevskis3eb829d2021-06-14 14:18:20 -0700746 for _count in 0..ENTRY_DB_COUNT {
Janis Danisevskis1be7e182021-04-12 14:31:12 -0700747 if Instant::now().duration_since(test_begin) >= Duration::from_secs(40) {
748 return;
749 }
750 let mut db = DB::new(&db_path3).expect("Failed to open database.");
751
Chris Wailesd5aaaef2021-07-27 16:04:33 -0700752 db.put(3, TEST_ALIAS, TEST_BLOB3).expect("Failed to add entry (3).");
Janis Danisevskis1be7e182021-04-12 14:31:12 -0700753
Chris Wailesd5aaaef2021-07-27 16:04:33 -0700754 db.remove(3, TEST_ALIAS).expect("Remove failed (3).");
Janis Danisevskis1be7e182021-04-12 14:31:12 -0700755 }
756 });
757
758 // While thread 3 is inserting and deleting TEST_ALIAS, we try to get the alias.
759 // This may yield an entry or none, but it must not fail.
760 let handle4 = thread::spawn(move || {
Janis Danisevskis3eb829d2021-06-14 14:18:20 -0700761 for _count in 0..ENTRY_DB_COUNT {
Janis Danisevskis1be7e182021-04-12 14:31:12 -0700762 if Instant::now().duration_since(test_begin) >= Duration::from_secs(40) {
763 return;
764 }
765 let mut db = DB::new(&db_path).expect("Failed to open database.");
766
767 // This may return Some or None but it must not fail.
Chris Wailesd5aaaef2021-07-27 16:04:33 -0700768 db.get(3, TEST_ALIAS).expect("Failed to get entry (4).");
Janis Danisevskis1be7e182021-04-12 14:31:12 -0700769 }
770 });
771
772 handle1.join().expect("Thread 1 panicked.");
773 handle2.join().expect("Thread 2 panicked.");
774 handle3.join().expect("Thread 3 panicked.");
775 handle4.join().expect("Thread 4 panicked.");
776
777 Ok(())
778 }
Janis Danisevskis77d72042021-01-20 15:36:30 -0800779}