blob: 41b86850e030e6faf44cf073396544b2d9ca4195 [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
Janis Danisevskis3eb829d2021-06-14 14:18:20 -070049 db.init_tables().context("Trying to initialize legacy keystore db.")?;
Janis Danisevskis77d72042021-01-20 15:36:30 -080050 Ok(db)
51 }
52
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 {
James Farrellefe1a2f2024-02-28 21:36:47 +000058 let result = self
Janis Danisevskis77d72042021-01-20 15:36:30 -080059 .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)
James Farrellefe1a2f2024-02-28 21:36:47 +000066 });
67 match result {
Janis Danisevskis77d72042021-01-20 15:36:30 -080068 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
81 fn is_locked_error(e: &anyhow::Error) -> bool {
Janis Danisevskis13f09152021-04-19 09:55:15 -070082 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 )
Janis Danisevskis77d72042021-01-20 15:36:30 -080087 }
88
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));",
Andrew Walbran78abb1e2023-05-30 16:20:56 +000097 [],
Janis Danisevskis77d72042021-01-20 15:36:30 -080098 )
99 .context("Failed to initialize \"profiles\" table.")?;
100 Ok(())
101 })
102 }
103
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
Chris Wailes263de9f2022-08-11 15:00:51 -0700110 // 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)]
Janis Danisevskis77d72042021-01-20 15:36:30 -0800116 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
Janis Danisevskis3eb829d2021-06-14 14:18:20 -0700124 fn put(&mut self, caller_uid: u32, alias: &str, entry: &[u8]) -> Result<()> {
Shaquille Johnsonf015af12023-11-30 15:22:19 +0000125 ensure_keystore_put_is_enabled()?;
Janis Danisevskis77d72042021-01-20 15:36:30 -0800126 self.with_transaction(TransactionBehavior::Immediate, |tx| {
127 tx.execute(
128 "INSERT OR REPLACE INTO profiles (owner, alias, profile) values (?, ?, ?)",
Janis Danisevskis3eb829d2021-06-14 14:18:20 -0700129 params![caller_uid, alias, entry,],
Janis Danisevskis77d72042021-01-20 15:36:30 -0800130 )
131 .context("In put: Failed to insert or replace.")?;
132 Ok(())
133 })
134 }
135
136 fn get(&mut self, caller_uid: u32, alias: &str) -> Result<Option<Vec<u8>>> {
137 self.with_transaction(TransactionBehavior::Deferred, |tx| {
138 tx.query_row(
139 "SELECT profile FROM profiles WHERE owner = ? AND alias = ?;",
140 params![caller_uid, alias],
141 |row| row.get(0),
142 )
143 .optional()
Janis Danisevskis3eb829d2021-06-14 14:18:20 -0700144 .context("In get: failed loading entry.")
Janis Danisevskis77d72042021-01-20 15:36:30 -0800145 })
146 }
147
148 fn remove(&mut self, caller_uid: u32, alias: &str) -> Result<bool> {
149 let removed = self.with_transaction(TransactionBehavior::Immediate, |tx| {
150 tx.execute(
151 "DELETE FROM profiles WHERE owner = ? AND alias = ?;",
152 params![caller_uid, alias],
153 )
154 .context("In remove: Failed to delete row.")
155 })?;
156 Ok(removed == 1)
157 }
Janis Danisevskis5898d152021-06-15 08:23:46 -0700158
159 fn remove_uid(&mut self, uid: u32) -> Result<()> {
160 self.with_transaction(TransactionBehavior::Immediate, |tx| {
161 tx.execute("DELETE FROM profiles WHERE owner = ?;", params![uid])
162 .context("In remove_uid: Failed to delete.")
163 })?;
164 Ok(())
165 }
166
167 fn remove_user(&mut self, user_id: u32) -> Result<()> {
168 self.with_transaction(TransactionBehavior::Immediate, |tx| {
169 tx.execute(
170 "DELETE FROM profiles WHERE cast ( ( owner/? ) as int) = ?;",
Joel Galenson81a50f22021-07-29 15:39:10 -0700171 params![rustutils::users::AID_USER_OFFSET, user_id],
Janis Danisevskis5898d152021-06-15 08:23:46 -0700172 )
173 .context("In remove_uid: Failed to delete.")
174 })?;
175 Ok(())
176 }
Janis Danisevskis77d72042021-01-20 15:36:30 -0800177}
178
Janis Danisevskis3eb829d2021-06-14 14:18:20 -0700179/// This is the main LegacyKeystore error type, it wraps binder exceptions and the
180/// LegacyKeystore errors.
Chris Wailes263de9f2022-08-11 15:00:51 -0700181#[derive(Debug, thiserror::Error, PartialEq, Eq)]
Janis Danisevskis77d72042021-01-20 15:36:30 -0800182pub enum Error {
Janis Danisevskis3eb829d2021-06-14 14:18:20 -0700183 /// Wraps a LegacyKeystore error code.
Janis Danisevskis77d72042021-01-20 15:36:30 -0800184 #[error("Error::Error({0:?})")]
185 Error(i32),
186 /// Wraps a Binder exception code other than a service specific exception.
187 #[error("Binder exception code {0:?}, {1:?}")]
188 Binder(ExceptionCode, i32),
189}
190
191impl Error {
192 /// Short hand for `Error::Error(ERROR_SYSTEM_ERROR)`
193 pub fn sys() -> Self {
194 Error::Error(ERROR_SYSTEM_ERROR)
195 }
196
Janis Danisevskis3eb829d2021-06-14 14:18:20 -0700197 /// Short hand for `Error::Error(ERROR_ENTRY_NOT_FOUND)`
Janis Danisevskis77d72042021-01-20 15:36:30 -0800198 pub fn not_found() -> Self {
Janis Danisevskis3eb829d2021-06-14 14:18:20 -0700199 Error::Error(ERROR_ENTRY_NOT_FOUND)
200 }
201
202 /// Short hand for `Error::Error(ERROR_PERMISSION_DENIED)`
203 pub fn perm() -> Self {
204 Error::Error(ERROR_PERMISSION_DENIED)
Janis Danisevskis77d72042021-01-20 15:36:30 -0800205 }
Shaquille Johnsonbe6e91d2023-10-21 19:09:17 +0100206
207 /// Short hand for `Error::Error(ERROR_SYSTEM_ERROR)`
208 pub fn deprecated() -> Self {
209 Error::Error(ERROR_SYSTEM_ERROR)
210 }
Janis Danisevskis77d72042021-01-20 15:36:30 -0800211}
212
David Drysdale5238d772024-06-07 15:12:10 +0100213/// Translate errors into service specific exceptions.
Janis Danisevskis77d72042021-01-20 15:36:30 -0800214///
David Drysdale5238d772024-06-07 15:12:10 +0100215/// All errors are logged, except for ERROR_ENTRY_NOT_FOUND error. `Error::Error(x)` variants get
216/// mapped onto a service specific error code of `x`, other errors are mapped to
217/// `ERROR_SYSTEM_ERROR`.
218fn map_or_log_err<T>(result: Result<T>) -> BinderResult<T> {
219 result.map_err(|e| {
220 let root_cause = e.root_cause();
221 let (rc, log_error) = match root_cause.downcast_ref::<Error>() {
222 // Make the entry not found errors silent.
223 Some(Error::Error(ERROR_ENTRY_NOT_FOUND)) => (ERROR_ENTRY_NOT_FOUND, false),
224 Some(Error::Error(e)) => (*e, true),
225 Some(Error::Binder(_, _)) | None => (ERROR_SYSTEM_ERROR, true),
226 };
227 if log_error {
228 log::error!("{:?}", e);
229 }
230 BinderStatus::new_service_specific_error(rc, anyhow_error_to_cstring(&e).as_deref())
231 })
Janis Danisevskis77d72042021-01-20 15:36:30 -0800232}
233
Shaquille Johnsonf015af12023-11-30 15:22:19 +0000234fn ensure_keystore_put_is_enabled() -> Result<()> {
235 if keystore2_flags::disable_legacy_keystore_put_v2() {
236 Err(Error::deprecated()).context(concat!(
237 "Storing into Keystore's legacy database is ",
238 "no longer supported, store in an app-specific database instead"
239 ))
240 } else {
241 Ok(())
242 }
243}
244
Janis Danisevskis5898d152021-06-15 08:23:46 -0700245struct LegacyKeystoreDeleteListener {
246 legacy_keystore: Arc<LegacyKeystore>,
247}
248
249impl DeleteListener for LegacyKeystoreDeleteListener {
250 fn delete_namespace(&self, domain: Domain, namespace: i64) -> Result<()> {
251 self.legacy_keystore.delete_namespace(domain, namespace)
252 }
253 fn delete_user(&self, user_id: u32) -> Result<()> {
254 self.legacy_keystore.delete_user(user_id)
255 }
256}
257
Janis Danisevskis3eb829d2021-06-14 14:18:20 -0700258/// Implements ILegacyKeystore AIDL interface.
259pub struct LegacyKeystore {
Janis Danisevskis77d72042021-01-20 15:36:30 -0800260 db_path: PathBuf,
Janis Danisevskis06891072021-02-11 10:28:17 -0800261 async_task: AsyncTask,
262}
263
264struct AsyncState {
265 recently_imported: HashSet<(u32, String)>,
266 legacy_loader: LegacyBlobLoader,
267 db_path: PathBuf,
Janis Danisevskis77d72042021-01-20 15:36:30 -0800268}
269
Janis Danisevskis3eb829d2021-06-14 14:18:20 -0700270impl LegacyKeystore {
271 /// Note: The filename was chosen before the purpose of this module was extended.
272 /// It is kept for backward compatibility with early adopters.
273 const LEGACY_KEYSTORE_FILE_NAME: &'static str = "vpnprofilestore.sqlite";
274
Janis Danisevskis5898d152021-06-15 08:23:46 -0700275 const WIFI_NAMESPACE: i64 = 102;
276 const AID_WIFI: u32 = 1010;
277
Janis Danisevskis3eb829d2021-06-14 14:18:20 -0700278 /// Creates a new LegacyKeystore instance.
Janis Danisevskis5898d152021-06-15 08:23:46 -0700279 pub fn new_native_binder(
280 path: &Path,
281 ) -> (Box<dyn DeleteListener + Send + Sync + 'static>, Strong<dyn ILegacyKeystore>) {
Janis Danisevskis77d72042021-01-20 15:36:30 -0800282 let mut db_path = path.to_path_buf();
Janis Danisevskis3eb829d2021-06-14 14:18:20 -0700283 db_path.push(Self::LEGACY_KEYSTORE_FILE_NAME);
Janis Danisevskis06891072021-02-11 10:28:17 -0800284
Janis Danisevskis5898d152021-06-15 08:23:46 -0700285 let legacy_keystore = Arc::new(Self { db_path, async_task: Default::default() });
286 legacy_keystore.init_shelf(path);
287 let service = LegacyKeystoreService { legacy_keystore: legacy_keystore.clone() };
288 (
289 Box::new(LegacyKeystoreDeleteListener { legacy_keystore }),
290 BnLegacyKeystore::new_binder(service, BinderFeatures::default()),
291 )
Janis Danisevskis77d72042021-01-20 15:36:30 -0800292 }
293
294 fn open_db(&self) -> Result<DB> {
295 DB::new(&self.db_path).context("In open_db: Failed to open db.")
296 }
297
Janis Danisevskis3eb829d2021-06-14 14:18:20 -0700298 fn get_effective_uid(uid: i32) -> Result<u32> {
299 const AID_SYSTEM: u32 = 1000;
Janis Danisevskis77d72042021-01-20 15:36:30 -0800300 let calling_uid = ThreadState::get_calling_uid();
Janis Danisevskis3eb829d2021-06-14 14:18:20 -0700301 let uid = uid as u32;
Janis Danisevskis06891072021-02-11 10:28:17 -0800302
Janis Danisevskis3eb829d2021-06-14 14:18:20 -0700303 if uid == UID_SELF as u32 || uid == calling_uid {
304 Ok(calling_uid)
Janis Danisevskis5898d152021-06-15 08:23:46 -0700305 } else if calling_uid == AID_SYSTEM && uid == Self::AID_WIFI {
Janis Danisevskis3eb829d2021-06-14 14:18:20 -0700306 // The only exception for legacy reasons is allowing SYSTEM to access
307 // the WIFI namespace.
308 // IMPORTANT: If you attempt to add more exceptions, it means you are adding
309 // more callers to this deprecated feature. DON'T!
Janis Danisevskis5898d152021-06-15 08:23:46 -0700310 Ok(Self::AID_WIFI)
Janis Danisevskis3eb829d2021-06-14 14:18:20 -0700311 } else {
312 Err(Error::perm()).with_context(|| {
313 format!("In get_effective_uid: caller: {}, requested uid: {}.", calling_uid, uid)
314 })
Janis Danisevskis06891072021-02-11 10:28:17 -0800315 }
Janis Danisevskis3eb829d2021-06-14 14:18:20 -0700316 }
317
318 fn get(&self, alias: &str, uid: i32) -> Result<Vec<u8>> {
319 let mut db = self.open_db().context("In get.")?;
320 let uid = Self::get_effective_uid(uid).context("In get.")?;
321
322 if let Some(entry) = db.get(uid, alias).context("In get: Trying to load entry from DB.")? {
323 return Ok(entry);
324 }
Janis Danisevskisf84d0b02022-01-26 14:11:14 -0800325 if self.get_legacy(uid, alias).context("In get: Trying to import legacy blob.")? {
326 // If we were able to import a legacy blob try again.
Janis Danisevskis3eb829d2021-06-14 14:18:20 -0700327 if let Some(entry) =
328 db.get(uid, alias).context("In get: Trying to load entry from DB.")?
Janis Danisevskis06891072021-02-11 10:28:17 -0800329 {
Janis Danisevskis3eb829d2021-06-14 14:18:20 -0700330 return Ok(entry);
Janis Danisevskis06891072021-02-11 10:28:17 -0800331 }
332 }
Janis Danisevskis3eb829d2021-06-14 14:18:20 -0700333 Err(Error::not_found()).context("In get: No such entry.")
Janis Danisevskis77d72042021-01-20 15:36:30 -0800334 }
335
Janis Danisevskis3eb829d2021-06-14 14:18:20 -0700336 fn put(&self, alias: &str, uid: i32, entry: &[u8]) -> Result<()> {
Shaquille Johnsonf015af12023-11-30 15:22:19 +0000337 ensure_keystore_put_is_enabled()?;
Janis Danisevskis3eb829d2021-06-14 14:18:20 -0700338 let uid = Self::get_effective_uid(uid).context("In put.")?;
Janis Danisevskis06891072021-02-11 10:28:17 -0800339 let mut db = self.open_db().context("In put.")?;
Janis Danisevskisf84d0b02022-01-26 14:11:14 -0800340 db.put(uid, alias, entry).context("In put: Trying to insert entry into DB.")?;
341 // When replacing an entry, make sure that there is no stale legacy file entry.
342 let _ = self.remove_legacy(uid, alias);
343 Ok(())
Janis Danisevskis77d72042021-01-20 15:36:30 -0800344 }
345
Janis Danisevskis3eb829d2021-06-14 14:18:20 -0700346 fn remove(&self, alias: &str, uid: i32) -> Result<()> {
347 let uid = Self::get_effective_uid(uid).context("In remove.")?;
Janis Danisevskis06891072021-02-11 10:28:17 -0800348 let mut db = self.open_db().context("In remove.")?;
Janis Danisevskisf84d0b02022-01-26 14:11:14 -0800349
350 if self.remove_legacy(uid, alias).context("In remove: trying to remove legacy entry")? {
351 return Ok(());
352 }
Janis Danisevskis3eb829d2021-06-14 14:18:20 -0700353 let removed =
354 db.remove(uid, alias).context("In remove: Trying to remove entry from DB.")?;
Janis Danisevskis77d72042021-01-20 15:36:30 -0800355 if removed {
356 Ok(())
357 } else {
Janis Danisevskis3eb829d2021-06-14 14:18:20 -0700358 Err(Error::not_found()).context("In remove: No such entry.")
Janis Danisevskis77d72042021-01-20 15:36:30 -0800359 }
360 }
361
Janis Danisevskis5898d152021-06-15 08:23:46 -0700362 fn delete_namespace(&self, domain: Domain, namespace: i64) -> Result<()> {
363 let uid = match domain {
364 Domain::APP => namespace as u32,
365 Domain::SELINUX => {
366 if namespace == Self::WIFI_NAMESPACE {
367 // Namespace WIFI gets mapped to AID_WIFI.
368 Self::AID_WIFI
369 } else {
370 // Nothing to do for any other namespace.
371 return Ok(());
372 }
373 }
374 _ => return Ok(()),
375 };
376
377 if let Err(e) = self.bulk_delete_uid(uid) {
378 log::warn!("In LegacyKeystore::delete_namespace: {:?}", e);
379 }
380 let mut db = self.open_db().context("In LegacyKeystore::delete_namespace.")?;
381 db.remove_uid(uid).context("In LegacyKeystore::delete_namespace.")
382 }
383
384 fn delete_user(&self, user_id: u32) -> Result<()> {
385 if let Err(e) = self.bulk_delete_user(user_id) {
386 log::warn!("In LegacyKeystore::delete_user: {:?}", e);
387 }
388 let mut db = self.open_db().context("In LegacyKeystore::delete_user.")?;
389 db.remove_user(user_id).context("In LegacyKeystore::delete_user.")
390 }
391
Janis Danisevskis3eb829d2021-06-14 14:18:20 -0700392 fn list(&self, prefix: &str, uid: i32) -> Result<Vec<String>> {
Janis Danisevskis77d72042021-01-20 15:36:30 -0800393 let mut db = self.open_db().context("In list.")?;
Janis Danisevskis3eb829d2021-06-14 14:18:20 -0700394 let uid = Self::get_effective_uid(uid).context("In list.")?;
395 let mut result = self.list_legacy(uid).context("In list.")?;
396 result.append(&mut db.list(uid).context("In list: Trying to get list of entries.")?);
Charisee28e6f0b2022-09-15 01:07:46 +0000397 result.retain(|s| s.starts_with(prefix));
Janis Danisevskis06891072021-02-11 10:28:17 -0800398 result.sort_unstable();
399 result.dedup();
400 Ok(result)
401 }
402
403 fn init_shelf(&self, path: &Path) {
404 let mut db_path = path.to_path_buf();
405 self.async_task.queue_hi(move |shelf| {
406 let legacy_loader = LegacyBlobLoader::new(&db_path);
Janis Danisevskis3eb829d2021-06-14 14:18:20 -0700407 db_path.push(Self::LEGACY_KEYSTORE_FILE_NAME);
Janis Danisevskis06891072021-02-11 10:28:17 -0800408
409 shelf.put(AsyncState { legacy_loader, db_path, recently_imported: Default::default() });
410 })
411 }
412
413 fn do_serialized<F, T: Send + 'static>(&self, f: F) -> Result<T>
414 where
415 F: FnOnce(&mut AsyncState) -> Result<T> + Send + 'static,
416 {
417 let (sender, receiver) = std::sync::mpsc::channel::<Result<T>>();
418 self.async_task.queue_hi(move |shelf| {
419 let state = shelf.get_downcast_mut::<AsyncState>().expect("Failed to get shelf.");
420 sender.send(f(state)).expect("Failed to send result.");
421 });
422 receiver.recv().context("In do_serialized: Failed to receive result.")?
423 }
424
425 fn list_legacy(&self, uid: u32) -> Result<Vec<String>> {
426 self.do_serialized(move |state| {
427 state
428 .legacy_loader
Janis Danisevskis5898d152021-06-15 08:23:46 -0700429 .list_legacy_keystore_entries_for_uid(uid)
Janis Danisevskis3eb829d2021-06-14 14:18:20 -0700430 .context("Trying to list legacy keystore entries.")
Janis Danisevskis06891072021-02-11 10:28:17 -0800431 })
432 .context("In list_legacy.")
433 }
434
435 fn get_legacy(&self, uid: u32, alias: &str) -> Result<bool> {
436 let alias = alias.to_string();
437 self.do_serialized(move |state| {
438 if state.recently_imported.contains(&(uid, alias.clone())) {
439 return Ok(true);
440 }
441 let mut db = DB::new(&state.db_path).context("In open_db: Failed to open db.")?;
Janis Danisevskisf84d0b02022-01-26 14:11:14 -0800442 let imported =
443 Self::import_one_legacy_entry(uid, &alias, &state.legacy_loader, &mut db)
444 .context("Trying to import legacy keystore entries.")?;
445 if imported {
Janis Danisevskis06891072021-02-11 10:28:17 -0800446 state.recently_imported.insert((uid, alias));
447 }
Janis Danisevskisf84d0b02022-01-26 14:11:14 -0800448 Ok(imported)
Janis Danisevskis06891072021-02-11 10:28:17 -0800449 })
450 .context("In get_legacy.")
451 }
452
Janis Danisevskisf84d0b02022-01-26 14:11:14 -0800453 fn remove_legacy(&self, uid: u32, alias: &str) -> Result<bool> {
454 let alias = alias.to_string();
455 self.do_serialized(move |state| {
456 if state.recently_imported.contains(&(uid, alias.clone())) {
457 return Ok(false);
458 }
459 state
460 .legacy_loader
461 .remove_legacy_keystore_entry(uid, &alias)
462 .context("Trying to remove legacy entry.")
463 })
464 }
465
Janis Danisevskis5898d152021-06-15 08:23:46 -0700466 fn bulk_delete_uid(&self, uid: u32) -> Result<()> {
467 self.do_serialized(move |state| {
468 let entries = state
469 .legacy_loader
470 .list_legacy_keystore_entries_for_uid(uid)
471 .context("In bulk_delete_uid: Trying to list entries.")?;
472 for alias in entries.iter() {
473 if let Err(e) = state.legacy_loader.remove_legacy_keystore_entry(uid, alias) {
474 log::warn!("In bulk_delete_uid: Failed to delete legacy entry. {:?}", e);
475 }
476 }
477 Ok(())
478 })
479 }
480
481 fn bulk_delete_user(&self, user_id: u32) -> Result<()> {
482 self.do_serialized(move |state| {
483 let entries = state
484 .legacy_loader
485 .list_legacy_keystore_entries_for_user(user_id)
486 .context("In bulk_delete_user: Trying to list entries.")?;
487 for (uid, entries) in entries.iter() {
488 for alias in entries.iter() {
489 if let Err(e) = state.legacy_loader.remove_legacy_keystore_entry(*uid, alias) {
490 log::warn!("In bulk_delete_user: Failed to delete legacy entry. {:?}", e);
491 }
492 }
493 }
494 Ok(())
495 })
496 }
497
Janis Danisevskisf84d0b02022-01-26 14:11:14 -0800498 fn import_one_legacy_entry(
Janis Danisevskis06891072021-02-11 10:28:17 -0800499 uid: u32,
500 alias: &str,
501 legacy_loader: &LegacyBlobLoader,
502 db: &mut DB,
503 ) -> Result<bool> {
504 let blob = legacy_loader
Janis Danisevskisf84d0b02022-01-26 14:11:14 -0800505 .read_legacy_keystore_entry(uid, alias, |ciphertext, iv, tag, _salt, _key_size| {
Eric Biggers673d34a2023-10-18 01:54:18 +0000506 if let Some(key) = SUPER_KEY
507 .read()
508 .unwrap()
509 .get_after_first_unlock_key_by_user_id(uid_to_android_user(uid))
Janis Danisevskisf84d0b02022-01-26 14:11:14 -0800510 {
511 key.decrypt(ciphertext, iv, tag)
512 } else {
513 Err(Error::sys()).context("No key found for user. Device may be locked.")
514 }
515 })
516 .context("In import_one_legacy_entry: Trying to read legacy keystore entry.")?;
Janis Danisevskis3eb829d2021-06-14 14:18:20 -0700517 if let Some(entry) = blob {
518 db.put(uid, alias, &entry)
Janis Danisevskisf84d0b02022-01-26 14:11:14 -0800519 .context("In import_one_legacy_entry: Trying to insert entry into DB.")?;
Janis Danisevskis06891072021-02-11 10:28:17 -0800520 legacy_loader
Janis Danisevskis3eb829d2021-06-14 14:18:20 -0700521 .remove_legacy_keystore_entry(uid, alias)
Janis Danisevskisf84d0b02022-01-26 14:11:14 -0800522 .context("In import_one_legacy_entry: Trying to delete legacy keystore entry.")?;
Janis Danisevskis06891072021-02-11 10:28:17 -0800523 Ok(true)
524 } else {
525 Ok(false)
526 }
Janis Danisevskis77d72042021-01-20 15:36:30 -0800527 }
528}
529
Janis Danisevskis5898d152021-06-15 08:23:46 -0700530struct LegacyKeystoreService {
531 legacy_keystore: Arc<LegacyKeystore>,
532}
Janis Danisevskis77d72042021-01-20 15:36:30 -0800533
Janis Danisevskis5898d152021-06-15 08:23:46 -0700534impl binder::Interface for LegacyKeystoreService {}
535
536impl ILegacyKeystore for LegacyKeystoreService {
Janis Danisevskis3eb829d2021-06-14 14:18:20 -0700537 fn get(&self, alias: &str, uid: i32) -> BinderResult<Vec<u8>> {
David Drysdale541846b2024-05-23 13:16:07 +0100538 let _wp = wd::watch("ILegacyKeystore::get");
David Drysdale5238d772024-06-07 15:12:10 +0100539 map_or_log_err(self.legacy_keystore.get(alias, uid))
Janis Danisevskis77d72042021-01-20 15:36:30 -0800540 }
Janis Danisevskis3eb829d2021-06-14 14:18:20 -0700541 fn put(&self, alias: &str, uid: i32, entry: &[u8]) -> BinderResult<()> {
David Drysdale541846b2024-05-23 13:16:07 +0100542 let _wp = wd::watch("ILegacyKeystore::put");
David Drysdale5238d772024-06-07 15:12:10 +0100543 map_or_log_err(self.legacy_keystore.put(alias, uid, entry))
Janis Danisevskis77d72042021-01-20 15:36:30 -0800544 }
Janis Danisevskis3eb829d2021-06-14 14:18:20 -0700545 fn remove(&self, alias: &str, uid: i32) -> BinderResult<()> {
David Drysdale541846b2024-05-23 13:16:07 +0100546 let _wp = wd::watch("ILegacyKeystore::remove");
David Drysdale5238d772024-06-07 15:12:10 +0100547 map_or_log_err(self.legacy_keystore.remove(alias, uid))
Janis Danisevskis77d72042021-01-20 15:36:30 -0800548 }
Janis Danisevskis3eb829d2021-06-14 14:18:20 -0700549 fn list(&self, prefix: &str, uid: i32) -> BinderResult<Vec<String>> {
David Drysdale541846b2024-05-23 13:16:07 +0100550 let _wp = wd::watch("ILegacyKeystore::list");
David Drysdale5238d772024-06-07 15:12:10 +0100551 map_or_log_err(self.legacy_keystore.list(prefix, uid))
Janis Danisevskis77d72042021-01-20 15:36:30 -0800552 }
553}
554
555#[cfg(test)]
556mod db_test {
557 use super::*;
558 use keystore2_test_utils::TempDir;
Janis Danisevskis1be7e182021-04-12 14:31:12 -0700559 use std::sync::Arc;
560 use std::thread;
561 use std::time::Duration;
562 use std::time::Instant;
Janis Danisevskis77d72042021-01-20 15:36:30 -0800563
Chris Wailesd5aaaef2021-07-27 16:04:33 -0700564 static TEST_ALIAS: &str = "test_alias";
Janis Danisevskis77d72042021-01-20 15:36:30 -0800565 static TEST_BLOB1: &[u8] = &[1, 2, 3, 4, 5, 6, 7, 8, 9, 0];
566 static TEST_BLOB2: &[u8] = &[2, 2, 3, 4, 5, 6, 7, 8, 9, 0];
567 static TEST_BLOB3: &[u8] = &[3, 2, 3, 4, 5, 6, 7, 8, 9, 0];
568 static TEST_BLOB4: &[u8] = &[3, 2, 3, 4, 5, 6, 7, 8, 9, 0];
569
570 #[test]
Janis Danisevskis3eb829d2021-06-14 14:18:20 -0700571 fn test_entry_db() {
572 let test_dir = TempDir::new("entrydb_test_").expect("Failed to create temp dir.");
573 let mut db = DB::new(&test_dir.build().push(LegacyKeystore::LEGACY_KEYSTORE_FILE_NAME))
574 .expect("Failed to open database.");
Janis Danisevskis77d72042021-01-20 15:36:30 -0800575
Janis Danisevskis3eb829d2021-06-14 14:18:20 -0700576 // Insert three entries for owner 2.
Janis Danisevskis77d72042021-01-20 15:36:30 -0800577 db.put(2, "test1", TEST_BLOB1).expect("Failed to insert test1.");
578 db.put(2, "test2", TEST_BLOB2).expect("Failed to insert test2.");
579 db.put(2, "test3", TEST_BLOB3).expect("Failed to insert test3.");
580
581 // Check list returns all inserted aliases.
582 assert_eq!(
583 vec!["test1".to_string(), "test2".to_string(), "test3".to_string(),],
Janis Danisevskis3eb829d2021-06-14 14:18:20 -0700584 db.list(2).expect("Failed to list entries.")
Janis Danisevskis77d72042021-01-20 15:36:30 -0800585 );
586
Janis Danisevskis3eb829d2021-06-14 14:18:20 -0700587 // There should be no entries for owner 1.
588 assert_eq!(Vec::<String>::new(), db.list(1).expect("Failed to list entries."));
Janis Danisevskis77d72042021-01-20 15:36:30 -0800589
590 // Check the content of the three entries.
Janis Danisevskis3eb829d2021-06-14 14:18:20 -0700591 assert_eq!(Some(TEST_BLOB1), db.get(2, "test1").expect("Failed to get entry.").as_deref());
592 assert_eq!(Some(TEST_BLOB2), db.get(2, "test2").expect("Failed to get entry.").as_deref());
593 assert_eq!(Some(TEST_BLOB3), db.get(2, "test3").expect("Failed to get entry.").as_deref());
Janis Danisevskis77d72042021-01-20 15:36:30 -0800594
595 // Remove test2 and check and check that it is no longer retrievable.
Janis Danisevskis3eb829d2021-06-14 14:18:20 -0700596 assert!(db.remove(2, "test2").expect("Failed to remove entry."));
597 assert!(db.get(2, "test2").expect("Failed to get entry.").is_none());
Janis Danisevskis77d72042021-01-20 15:36:30 -0800598
599 // test2 should now no longer be in the list.
600 assert_eq!(
601 vec!["test1".to_string(), "test3".to_string(),],
Janis Danisevskis3eb829d2021-06-14 14:18:20 -0700602 db.list(2).expect("Failed to list entries.")
Janis Danisevskis77d72042021-01-20 15:36:30 -0800603 );
604
605 // Put on existing alias replaces it.
606 // Verify test1 is TEST_BLOB1.
Janis Danisevskis3eb829d2021-06-14 14:18:20 -0700607 assert_eq!(Some(TEST_BLOB1), db.get(2, "test1").expect("Failed to get entry.").as_deref());
Janis Danisevskis77d72042021-01-20 15:36:30 -0800608 db.put(2, "test1", TEST_BLOB4).expect("Failed to replace test1.");
609 // Verify test1 is TEST_BLOB4.
Janis Danisevskis3eb829d2021-06-14 14:18:20 -0700610 assert_eq!(Some(TEST_BLOB4), db.get(2, "test1").expect("Failed to get entry.").as_deref());
Janis Danisevskis77d72042021-01-20 15:36:30 -0800611 }
Janis Danisevskis1be7e182021-04-12 14:31:12 -0700612
613 #[test]
Janis Danisevskis5898d152021-06-15 08:23:46 -0700614 fn test_delete_uid() {
615 let test_dir = TempDir::new("test_delete_uid_").expect("Failed to create temp dir.");
616 let mut db = DB::new(&test_dir.build().push(LegacyKeystore::LEGACY_KEYSTORE_FILE_NAME))
617 .expect("Failed to open database.");
618
619 // Insert three entries for owner 2.
620 db.put(2, "test1", TEST_BLOB1).expect("Failed to insert test1.");
621 db.put(2, "test2", TEST_BLOB2).expect("Failed to insert test2.");
622 db.put(3, "test3", TEST_BLOB3).expect("Failed to insert test3.");
623
624 db.remove_uid(2).expect("Failed to remove uid 2");
625
626 assert_eq!(Vec::<String>::new(), db.list(2).expect("Failed to list entries."));
627
628 assert_eq!(vec!["test3".to_string(),], db.list(3).expect("Failed to list entries."));
629 }
630
631 #[test]
632 fn test_delete_user() {
633 let test_dir = TempDir::new("test_delete_user_").expect("Failed to create temp dir.");
634 let mut db = DB::new(&test_dir.build().push(LegacyKeystore::LEGACY_KEYSTORE_FILE_NAME))
635 .expect("Failed to open database.");
636
637 // Insert three entries for owner 2.
Joel Galenson81a50f22021-07-29 15:39:10 -0700638 db.put(2 + 2 * rustutils::users::AID_USER_OFFSET, "test1", TEST_BLOB1)
Janis Danisevskis5898d152021-06-15 08:23:46 -0700639 .expect("Failed to insert test1.");
Joel Galenson81a50f22021-07-29 15:39:10 -0700640 db.put(4 + 2 * rustutils::users::AID_USER_OFFSET, "test2", TEST_BLOB2)
Janis Danisevskis5898d152021-06-15 08:23:46 -0700641 .expect("Failed to insert test2.");
642 db.put(3, "test3", TEST_BLOB3).expect("Failed to insert test3.");
643
644 db.remove_user(2).expect("Failed to remove user 2");
645
646 assert_eq!(
647 Vec::<String>::new(),
Joel Galenson81a50f22021-07-29 15:39:10 -0700648 db.list(2 + 2 * rustutils::users::AID_USER_OFFSET).expect("Failed to list entries.")
Janis Danisevskis5898d152021-06-15 08:23:46 -0700649 );
650
651 assert_eq!(
652 Vec::<String>::new(),
Joel Galenson81a50f22021-07-29 15:39:10 -0700653 db.list(4 + 2 * rustutils::users::AID_USER_OFFSET).expect("Failed to list entries.")
Janis Danisevskis5898d152021-06-15 08:23:46 -0700654 );
655
656 assert_eq!(vec!["test3".to_string(),], db.list(3).expect("Failed to list entries."));
657 }
658
659 #[test]
Janis Danisevskis3eb829d2021-06-14 14:18:20 -0700660 fn concurrent_legacy_keystore_entry_test() -> Result<()> {
Janis Danisevskis1be7e182021-04-12 14:31:12 -0700661 let temp_dir = Arc::new(
Janis Danisevskis3eb829d2021-06-14 14:18:20 -0700662 TempDir::new("concurrent_legacy_keystore_entry_test_")
663 .expect("Failed to create temp dir."),
Janis Danisevskis1be7e182021-04-12 14:31:12 -0700664 );
665
Janis Danisevskis3eb829d2021-06-14 14:18:20 -0700666 let db_path = temp_dir.build().push(LegacyKeystore::LEGACY_KEYSTORE_FILE_NAME).to_owned();
Janis Danisevskis1be7e182021-04-12 14:31:12 -0700667
668 let test_begin = Instant::now();
669
670 let mut db = DB::new(&db_path).expect("Failed to open database.");
Janis Danisevskis3eb829d2021-06-14 14:18:20 -0700671 const ENTRY_COUNT: u32 = 5000u32;
672 const ENTRY_DB_COUNT: u32 = 5000u32;
Janis Danisevskis1be7e182021-04-12 14:31:12 -0700673
Janis Danisevskis3eb829d2021-06-14 14:18:20 -0700674 let mut actual_entry_count = ENTRY_COUNT;
675 // First insert ENTRY_COUNT entries.
676 for count in 0..ENTRY_COUNT {
Janis Danisevskis1be7e182021-04-12 14:31:12 -0700677 if Instant::now().duration_since(test_begin) >= Duration::from_secs(15) {
Janis Danisevskis3eb829d2021-06-14 14:18:20 -0700678 actual_entry_count = count;
Janis Danisevskis1be7e182021-04-12 14:31:12 -0700679 break;
680 }
681 let alias = format!("test_alias_{}", count);
Janis Danisevskis3eb829d2021-06-14 14:18:20 -0700682 db.put(1, &alias, TEST_BLOB1).expect("Failed to add entry (1).");
Janis Danisevskis1be7e182021-04-12 14:31:12 -0700683 }
684
685 // Insert more keys from a different thread and into a different namespace.
686 let db_path1 = db_path.clone();
687 let handle1 = thread::spawn(move || {
688 let mut db = DB::new(&db_path1).expect("Failed to open database.");
689
Janis Danisevskis3eb829d2021-06-14 14:18:20 -0700690 for count in 0..actual_entry_count {
Janis Danisevskis1be7e182021-04-12 14:31:12 -0700691 if Instant::now().duration_since(test_begin) >= Duration::from_secs(40) {
692 return;
693 }
694 let alias = format!("test_alias_{}", count);
Janis Danisevskis3eb829d2021-06-14 14:18:20 -0700695 db.put(2, &alias, TEST_BLOB2).expect("Failed to add entry (2).");
Janis Danisevskis1be7e182021-04-12 14:31:12 -0700696 }
697
698 // Then delete them again.
Janis Danisevskis3eb829d2021-06-14 14:18:20 -0700699 for count in 0..actual_entry_count {
Janis Danisevskis1be7e182021-04-12 14:31:12 -0700700 if Instant::now().duration_since(test_begin) >= Duration::from_secs(40) {
701 return;
702 }
703 let alias = format!("test_alias_{}", count);
704 db.remove(2, &alias).expect("Remove Failed (2).");
705 }
706 });
707
Janis Danisevskis3eb829d2021-06-14 14:18:20 -0700708 // And start deleting the first set of entries.
Janis Danisevskis1be7e182021-04-12 14:31:12 -0700709 let db_path2 = db_path.clone();
710 let handle2 = thread::spawn(move || {
711 let mut db = DB::new(&db_path2).expect("Failed to open database.");
712
Janis Danisevskis3eb829d2021-06-14 14:18:20 -0700713 for count in 0..actual_entry_count {
Janis Danisevskis1be7e182021-04-12 14:31:12 -0700714 if Instant::now().duration_since(test_begin) >= Duration::from_secs(40) {
715 return;
716 }
717 let alias = format!("test_alias_{}", count);
718 db.remove(1, &alias).expect("Remove Failed (1)).");
719 }
720 });
721
722 // While a lot of inserting and deleting is going on we have to open database connections
Janis Danisevskis3eb829d2021-06-14 14:18:20 -0700723 // successfully and then insert and delete a specific entry.
Janis Danisevskis1be7e182021-04-12 14:31:12 -0700724 let db_path3 = db_path.clone();
725 let handle3 = thread::spawn(move || {
Janis Danisevskis3eb829d2021-06-14 14:18:20 -0700726 for _count in 0..ENTRY_DB_COUNT {
Janis Danisevskis1be7e182021-04-12 14:31:12 -0700727 if Instant::now().duration_since(test_begin) >= Duration::from_secs(40) {
728 return;
729 }
730 let mut db = DB::new(&db_path3).expect("Failed to open database.");
731
Chris Wailesd5aaaef2021-07-27 16:04:33 -0700732 db.put(3, TEST_ALIAS, TEST_BLOB3).expect("Failed to add entry (3).");
Janis Danisevskis1be7e182021-04-12 14:31:12 -0700733
Chris Wailesd5aaaef2021-07-27 16:04:33 -0700734 db.remove(3, TEST_ALIAS).expect("Remove failed (3).");
Janis Danisevskis1be7e182021-04-12 14:31:12 -0700735 }
736 });
737
738 // While thread 3 is inserting and deleting TEST_ALIAS, we try to get the alias.
739 // This may yield an entry or none, but it must not fail.
740 let handle4 = thread::spawn(move || {
Janis Danisevskis3eb829d2021-06-14 14:18:20 -0700741 for _count in 0..ENTRY_DB_COUNT {
Janis Danisevskis1be7e182021-04-12 14:31:12 -0700742 if Instant::now().duration_since(test_begin) >= Duration::from_secs(40) {
743 return;
744 }
745 let mut db = DB::new(&db_path).expect("Failed to open database.");
746
747 // This may return Some or None but it must not fail.
Chris Wailesd5aaaef2021-07-27 16:04:33 -0700748 db.get(3, TEST_ALIAS).expect("Failed to get entry (4).");
Janis Danisevskis1be7e182021-04-12 14:31:12 -0700749 }
750 });
751
752 handle1.join().expect("Thread 1 panicked.");
753 handle2.join().expect("Thread 2 panicked.");
754 handle3.join().expect("Thread 3 panicked.");
755 handle4.join().expect("Thread 4 panicked.");
756
757 Ok(())
758 }
Janis Danisevskis77d72042021-01-20 15:36:30 -0800759}