blob: 81fb06c5a27ec510d0d2441ce957adf923d42195 [file] [log] [blame]
Hasini Gunasinghe3ed5da72021-02-04 15:18:54 +00001// Copyright 2021, 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//! This module acts as a bridge between the legacy key database and the keystore2 database.
16
Janis Danisevskisf84d0b02022-01-26 14:11:14 -080017use crate::database::{
18 BlobInfo, BlobMetaData, BlobMetaEntry, CertificateInfo, DateTime, EncryptedBy, KeyMetaData,
19 KeyMetaEntry, KeyType, KeystoreDB, Uuid, KEYSTORE_UUID,
Paul Crowley7a658392021-03-18 17:08:20 -070020};
Janis Danisevskisf84d0b02022-01-26 14:11:14 -080021use crate::error::{map_km_error, Error};
22use crate::key_parameter::{KeyParameter, KeyParameterValue};
23use crate::legacy_blob::{self, Blob, BlobValue, LegacyKeyCharacteristics};
24use crate::super_key::USER_SUPER_KEY;
25use crate::utils::{
26 key_characteristics_to_internal, uid_to_android_user, upgrade_keyblob_if_required_with,
27 watchdog as wd, AesGcm,
28};
29use crate::{async_task::AsyncTask, legacy_blob::LegacyBlobLoader};
Hasini Gunasinghe3ed5da72021-02-04 15:18:54 +000030use android_hardware_security_keymint::aidl::android::hardware::security::keymint::SecurityLevel::SecurityLevel;
31use android_system_keystore2::aidl::android::system::keystore2::{
32 Domain::Domain, KeyDescriptor::KeyDescriptor, ResponseCode::ResponseCode,
33};
34use anyhow::{Context, Result};
35use core::ops::Deref;
Paul Crowleyf61fee72021-03-17 14:38:44 -070036use keystore2_crypto::{Password, ZVec};
Hasini Gunasinghe3ed5da72021-02-04 15:18:54 +000037use std::collections::{HashMap, HashSet};
Hasini Gunasinghe3ed5da72021-02-04 15:18:54 +000038use std::sync::atomic::{AtomicU8, Ordering};
39use std::sync::mpsc::channel;
40use std::sync::{Arc, Mutex};
41
Janis Danisevskis0ffb8a82022-02-06 22:37:21 -080042/// Represents LegacyImporter.
43pub struct LegacyImporter {
Hasini Gunasinghe3ed5da72021-02-04 15:18:54 +000044 async_task: Arc<AsyncTask>,
45 initializer: Mutex<
46 Option<
47 Box<
48 dyn FnOnce() -> (KeystoreDB, HashMap<SecurityLevel, Uuid>, Arc<LegacyBlobLoader>)
49 + Send
50 + 'static,
51 >,
52 >,
53 >,
54 /// This atomic is used for cheap interior mutability. It is intended to prevent
Janis Danisevskis0ffb8a82022-02-06 22:37:21 -080055 /// expensive calls into the legacy importer when the legacy database is empty.
Hasini Gunasinghe3ed5da72021-02-04 15:18:54 +000056 /// When transitioning from READY to EMPTY, spurious calls may occur for a brief period
57 /// of time. This is tolerable in favor of the common case.
58 state: AtomicU8,
59}
60
61#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
Janis Danisevskis0ffb8a82022-02-06 22:37:21 -080062struct RecentImport {
Hasini Gunasinghe3ed5da72021-02-04 15:18:54 +000063 uid: u32,
64 alias: String,
65}
66
Janis Danisevskis0ffb8a82022-02-06 22:37:21 -080067impl RecentImport {
Hasini Gunasinghe3ed5da72021-02-04 15:18:54 +000068 fn new(uid: u32, alias: String) -> Self {
69 Self { uid, alias }
70 }
71}
72
Janis Danisevskiseed69842021-02-18 20:04:10 -080073enum BulkDeleteRequest {
74 Uid(u32),
75 User(u32),
76}
77
Janis Danisevskis0ffb8a82022-02-06 22:37:21 -080078struct LegacyImporterState {
79 recently_imported: HashSet<RecentImport>,
80 recently_imported_super_key: HashSet<u32>,
Hasini Gunasinghe3ed5da72021-02-04 15:18:54 +000081 legacy_loader: Arc<LegacyBlobLoader>,
82 sec_level_to_km_uuid: HashMap<SecurityLevel, Uuid>,
83 db: KeystoreDB,
84}
85
Janis Danisevskis0ffb8a82022-02-06 22:37:21 -080086impl LegacyImporter {
Hasini Gunasinghe3ed5da72021-02-04 15:18:54 +000087 const WIFI_NAMESPACE: i64 = 102;
88 const AID_WIFI: u32 = 1010;
89
90 const STATE_UNINITIALIZED: u8 = 0;
91 const STATE_READY: u8 = 1;
92 const STATE_EMPTY: u8 = 2;
93
Janis Danisevskis0ffb8a82022-02-06 22:37:21 -080094 /// Constructs a new LegacyImporter using the given AsyncTask object as import
Hasini Gunasinghe3ed5da72021-02-04 15:18:54 +000095 /// worker.
96 pub fn new(async_task: Arc<AsyncTask>) -> Self {
97 Self {
98 async_task,
99 initializer: Default::default(),
100 state: AtomicU8::new(Self::STATE_UNINITIALIZED),
101 }
102 }
103
Janis Danisevskis0ffb8a82022-02-06 22:37:21 -0800104 /// The legacy importer must be initialized deferred, because keystore starts very early.
Hasini Gunasinghe3ed5da72021-02-04 15:18:54 +0000105 /// At this time the data partition may not be mounted. So we cannot open database connections
106 /// until we get actual key load requests. This sets the function that the legacy loader
107 /// uses to connect to the database.
108 pub fn set_init<F>(&self, f_init: F) -> Result<()>
109 where
110 F: FnOnce() -> (KeystoreDB, HashMap<SecurityLevel, Uuid>, Arc<LegacyBlobLoader>)
111 + Send
112 + 'static,
113 {
114 let mut initializer = self.initializer.lock().expect("Failed to lock initializer.");
115
116 // If we are not uninitialized we have no business setting the initializer.
117 if self.state.load(Ordering::Relaxed) != Self::STATE_UNINITIALIZED {
118 return Ok(());
119 }
120
121 // Only set the initializer if it hasn't been set before.
122 if initializer.is_none() {
123 *initializer = Some(Box::new(f_init))
124 }
125
126 Ok(())
127 }
128
Janis Danisevskis0ffb8a82022-02-06 22:37:21 -0800129 /// This function is called by the import requestor to check if it is worth
130 /// making an import request. It also transitions the state from UNINITIALIZED
Hasini Gunasinghe3ed5da72021-02-04 15:18:54 +0000131 /// to READY or EMPTY on first use. The deferred initialization is necessary, because
132 /// Keystore 2.0 runs early during boot, where data may not yet be mounted.
Janis Danisevskis0ffb8a82022-02-06 22:37:21 -0800133 /// Returns Ok(STATE_READY) if an import request is worth undertaking and
Hasini Gunasinghe3ed5da72021-02-04 15:18:54 +0000134 /// Ok(STATE_EMPTY) if the database is empty. An error is returned if the loader
135 /// was not initialized and cannot be initialized.
136 fn check_state(&self) -> Result<u8> {
137 let mut first_try = true;
138 loop {
139 match (self.state.load(Ordering::Relaxed), first_try) {
140 (Self::STATE_EMPTY, _) => {
141 return Ok(Self::STATE_EMPTY);
142 }
143 (Self::STATE_UNINITIALIZED, true) => {
144 // If we find the legacy loader uninitialized, we grab the initializer lock,
145 // check if the legacy database is empty, and if not, schedule an initialization
146 // request. Coming out of the initializer lock, the state is either EMPTY or
147 // READY.
148 let mut initializer = self.initializer.lock().unwrap();
149
150 if let Some(initializer) = initializer.take() {
151 let (db, sec_level_to_km_uuid, legacy_loader) = (initializer)();
152
153 if legacy_loader.is_empty().context(
154 "In check_state: Trying to check if the legacy database is empty.",
155 )? {
156 self.state.store(Self::STATE_EMPTY, Ordering::Relaxed);
157 return Ok(Self::STATE_EMPTY);
158 }
159
160 self.async_task.queue_hi(move |shelf| {
Janis Danisevskis0ffb8a82022-02-06 22:37:21 -0800161 shelf.get_or_put_with(|| LegacyImporterState {
162 recently_imported: Default::default(),
163 recently_imported_super_key: Default::default(),
Hasini Gunasinghe3ed5da72021-02-04 15:18:54 +0000164 legacy_loader,
165 sec_level_to_km_uuid,
166 db,
167 });
168 });
169
170 // It is safe to set this here even though the async task may not yet have
171 // run because any thread observing this will not be able to schedule a
172 // task that can run before the initialization.
173 // Also we can only transition out of this state while having the
174 // initializer lock and having found an initializer.
175 self.state.store(Self::STATE_READY, Ordering::Relaxed);
176 return Ok(Self::STATE_READY);
177 } else {
178 // There is a chance that we just lost the race from state.load() to
179 // grabbing the initializer mutex. If that is the case the state must
180 // be EMPTY or READY after coming out of the lock. So we can give it
181 // one more try.
182 first_try = false;
183 continue;
184 }
185 }
186 (Self::STATE_UNINITIALIZED, false) => {
187 // Okay, tough luck. The legacy loader was really completely uninitialized.
188 return Err(Error::sys()).context(
189 "In check_state: Legacy loader should not be called uninitialized.",
190 );
191 }
192 (Self::STATE_READY, _) => return Ok(Self::STATE_READY),
Janis Danisevskis0ffb8a82022-02-06 22:37:21 -0800193 (s, _) => panic!("Unknown legacy importer state. {} ", s),
Hasini Gunasinghe3ed5da72021-02-04 15:18:54 +0000194 }
195 }
196 }
197
198 /// List all aliases for uid in the legacy database.
199 pub fn list_uid(&self, domain: Domain, namespace: i64) -> Result<Vec<KeyDescriptor>> {
Janis Danisevskis0ffb8a82022-02-06 22:37:21 -0800200 let _wp = wd::watch_millis("LegacyImporter::list_uid", 500);
Janis Danisevskis850d4862021-05-05 08:41:14 -0700201
Hasini Gunasinghe3ed5da72021-02-04 15:18:54 +0000202 let uid = match (domain, namespace) {
203 (Domain::APP, namespace) => namespace as u32,
204 (Domain::SELINUX, Self::WIFI_NAMESPACE) => Self::AID_WIFI,
205 _ => return Ok(Vec::new()),
206 };
207 self.do_serialized(move |state| state.list_uid(uid)).unwrap_or_else(|| Ok(Vec::new())).map(
208 |v| {
209 v.into_iter()
210 .map(|alias| KeyDescriptor {
211 domain,
212 nspace: namespace,
213 alias: Some(alias),
214 blob: None,
215 })
216 .collect()
217 },
218 )
219 }
220
Janis Danisevskis0ffb8a82022-02-06 22:37:21 -0800221 /// Sends the given closure to the importer thread for execution after calling check_state.
Hasini Gunasinghe3ed5da72021-02-04 15:18:54 +0000222 /// Returns None if the database was empty and the request was not executed.
Janis Danisevskis0ffb8a82022-02-06 22:37:21 -0800223 /// Otherwise returns Some with the result produced by the import request.
Hasini Gunasinghe3ed5da72021-02-04 15:18:54 +0000224 /// The loader state may transition to STATE_EMPTY during the execution of this function.
225 fn do_serialized<F, T: Send + 'static>(&self, f: F) -> Option<Result<T>>
226 where
Janis Danisevskis0ffb8a82022-02-06 22:37:21 -0800227 F: FnOnce(&mut LegacyImporterState) -> Result<T> + Send + 'static,
Hasini Gunasinghe3ed5da72021-02-04 15:18:54 +0000228 {
229 // Short circuit if the database is empty or not initialized (error case).
230 match self.check_state().context("In do_serialized: Checking state.") {
Janis Danisevskis0ffb8a82022-02-06 22:37:21 -0800231 Ok(LegacyImporter::STATE_EMPTY) => return None,
232 Ok(LegacyImporter::STATE_READY) => {}
Hasini Gunasinghe3ed5da72021-02-04 15:18:54 +0000233 Err(e) => return Some(Err(e)),
Janis Danisevskis0ffb8a82022-02-06 22:37:21 -0800234 Ok(s) => panic!("Unknown legacy importer state. {} ", s),
Hasini Gunasinghe3ed5da72021-02-04 15:18:54 +0000235 }
236
237 // We have established that there may be a key in the legacy database.
Janis Danisevskis0ffb8a82022-02-06 22:37:21 -0800238 // Now we schedule an import request.
Hasini Gunasinghe3ed5da72021-02-04 15:18:54 +0000239 let (sender, receiver) = channel();
240 self.async_task.queue_hi(move |shelf| {
Janis Danisevskis0ffb8a82022-02-06 22:37:21 -0800241 // Get the importer state from the shelf.
242 // There may not be a state. This can happen if this import request was scheduled
Hasini Gunasinghe3ed5da72021-02-04 15:18:54 +0000243 // before a previous request established that the legacy database was empty
244 // and removed the state from the shelf. Since we know now that the database
245 // is empty, we can return None here.
Janis Danisevskis0ffb8a82022-02-06 22:37:21 -0800246 let (new_state, result) = if let Some(legacy_importer_state) =
247 shelf.get_downcast_mut::<LegacyImporterState>()
Hasini Gunasinghe3ed5da72021-02-04 15:18:54 +0000248 {
Janis Danisevskis0ffb8a82022-02-06 22:37:21 -0800249 let result = f(legacy_importer_state);
250 (legacy_importer_state.check_empty(), Some(result))
Hasini Gunasinghe3ed5da72021-02-04 15:18:54 +0000251 } else {
252 (Self::STATE_EMPTY, None)
253 };
254
Janis Danisevskis0ffb8a82022-02-06 22:37:21 -0800255 // If the import request determined that the database is now empty, we discard
Hasini Gunasinghe3ed5da72021-02-04 15:18:54 +0000256 // the state from the shelf to free up the resources we won't need any longer.
257 if result.is_some() && new_state == Self::STATE_EMPTY {
Janis Danisevskis0ffb8a82022-02-06 22:37:21 -0800258 shelf.remove_downcast_ref::<LegacyImporterState>();
Hasini Gunasinghe3ed5da72021-02-04 15:18:54 +0000259 }
260
261 // Send the result to the requester.
262 if let Err(e) = sender.send((new_state, result)) {
263 log::error!("In do_serialized. Error in sending the result. {:?}", e);
264 }
265 });
266
267 let (new_state, result) = match receiver.recv() {
268 Err(e) => {
269 return Some(Err(e).context("In do_serialized. Failed to receive from the sender."))
270 }
271 Ok(r) => r,
272 };
273
274 // We can only transition to EMPTY but never back.
Janis Danisevskis0ffb8a82022-02-06 22:37:21 -0800275 // The importer never creates any legacy blobs.
Hasini Gunasinghe3ed5da72021-02-04 15:18:54 +0000276 if new_state == Self::STATE_EMPTY {
277 self.state.store(Self::STATE_EMPTY, Ordering::Relaxed)
278 }
279
280 result
281 }
282
Janis Danisevskisf84d0b02022-01-26 14:11:14 -0800283 /// This function behaves exactly like with_try_import unless the src_key has an encrypted
284 /// component (other than the key blob itself [1]) and super_key is None.
285 /// In that case the files belonging to the src_key will be renamed to be moved to the
286 /// namespace indicated by dst_key. The destination domain must be in Domain::APP.
287 ///
288 /// [1] Components that cannot be encrypted with the super key in keystore2 include the
289 /// characteristics file, which was encrypted before Android Q, and certificate entries
290 /// added by KeyChain before Android Q.
291 pub fn with_try_import_or_migrate_namespaces<F, T>(
292 &self,
293 src: (u32, &KeyDescriptor),
294 dest: (u32, &KeyDescriptor),
295 super_key: Option<Arc<dyn AesGcm + Send + Sync>>,
296 has_migrate_any_permission: bool,
297 key_accessor: F,
298 ) -> Result<Option<T>>
299 where
300 F: Fn() -> Result<T>,
301 {
302 let _wp = wd::watch_millis("LegacyImporter::with_try_import_or_migrate_namespaces", 500);
303
304 let (src_uid, src_key) = src;
305 let (dest_uid, dest_key) = dest;
306
307 // Access the key and return on success.
308 match key_accessor() {
309 Ok(result) => return Ok(Some(result)),
310 Err(e) => {
311 if e.root_cause().downcast_ref::<Error>()
312 != Some(&Error::Rc(ResponseCode::KEY_NOT_FOUND))
313 {
314 return Err(e);
315 }
316 }
317 }
318
319 // Filter inputs. We can only load legacy app domain keys as well
320 // as the SELINUX WIFI_NAMESPACE, which will be populated from AID_WIFI.
321 let src_uid = match src_key {
322 KeyDescriptor { domain: Domain::APP, alias: Some(_), .. } => src_uid,
323 KeyDescriptor { domain: Domain::SELINUX, nspace, alias: Some(_), .. } => {
324 match *nspace {
325 Self::WIFI_NAMESPACE => Self::AID_WIFI,
326 _ => {
327 return Err(Error::Rc(ResponseCode::KEY_NOT_FOUND))
328 .context(format!("No legacy keys for namespace {}", nspace))
329 }
330 }
331 }
332 _ => {
333 return Err(Error::Rc(ResponseCode::KEY_NOT_FOUND))
334 .context("No legacy keys for key descriptor.")
335 }
336 };
337
338 let dest_uid = match dest_key {
339 KeyDescriptor { domain: Domain::APP, alias: Some(_), .. } => Some(dest_uid),
340 KeyDescriptor { domain: Domain::SELINUX, alias: Some(_), .. } => {
341 // Domain::SELINUX cannot be migrated in place, but we cannot fail at this point
342 // because the import may succeed at which point the actual migration will
343 // be performed by the caller.
344 None
345 }
346 _ => {
347 return Err(Error::Rc(ResponseCode::KEY_NOT_FOUND))
348 .context("No legacy keys for key descriptor.")
349 }
350 };
351
352 let src_key_clone = src_key.clone();
353 let dest_key_clone = dest_key.clone();
354 let result = self.do_serialized(move |importer_state| {
355 let super_key = super_key.map(|sk| -> Arc<dyn AesGcm> { sk });
356 match (
357 importer_state.check_and_import(src_uid, src_key_clone.clone(), super_key),
358 dest_uid,
359 ) {
360 // The import into the database was successful. Return Ok(true)
361 (Ok(()), _) => Ok(true),
362 // The import failed because a certificate and/or characteristics
363 // file was encrypted and no super_key was available. Migration within the
364 // legacy database is attempted and Ok(false) is returned on success.
365 (Err(e), Some(dest_uid))
366 if has_migrate_any_permission
367 && e.root_cause().downcast_ref::<Error>()
368 == Some(&Error::Rc(ResponseCode::LOCKED)) =>
369 {
370 importer_state
371 .migrate_namespaces(src_uid, dest_uid, src_key_clone, dest_key_clone)
372 .map(|_| false)
373 }
374 (Err(e), _) => Err(e),
375 }
376 });
377
378 match result {
379 None => {
380 Err(Error::Rc(ResponseCode::KEY_NOT_FOUND)).context("Legacy database is empty.")
381 }
382
383 Some(Ok(true)) => {
384 // After successful import try again.
385 key_accessor().map(|v| Some(v))
386 }
387 // The entry was successfully migrated within the legacy database.
388 Some(Ok(false)) => Ok(None),
389 Some(Err(e)) => Err(e),
390 }
391 }
392
Hasini Gunasinghe3ed5da72021-02-04 15:18:54 +0000393 /// Runs the key_accessor function and returns its result. If it returns an error and the
Janis Danisevskis0ffb8a82022-02-06 22:37:21 -0800394 /// root cause was KEY_NOT_FOUND, tries to import a key with the given parameters from
Hasini Gunasinghe3ed5da72021-02-04 15:18:54 +0000395 /// the legacy database to the new database and runs the key_accessor function again if
Janis Danisevskis0ffb8a82022-02-06 22:37:21 -0800396 /// the import request was successful.
397 pub fn with_try_import<F, T>(
Hasini Gunasinghe3ed5da72021-02-04 15:18:54 +0000398 &self,
399 key: &KeyDescriptor,
400 caller_uid: u32,
Janis Danisevskisf84d0b02022-01-26 14:11:14 -0800401 super_key: Option<Arc<dyn AesGcm + Send + Sync>>,
Hasini Gunasinghe3ed5da72021-02-04 15:18:54 +0000402 key_accessor: F,
403 ) -> Result<T>
404 where
405 F: Fn() -> Result<T>,
406 {
Janis Danisevskis0ffb8a82022-02-06 22:37:21 -0800407 let _wp = wd::watch_millis("LegacyImporter::with_try_import", 500);
Janis Danisevskis850d4862021-05-05 08:41:14 -0700408
Hasini Gunasinghe3ed5da72021-02-04 15:18:54 +0000409 // Access the key and return on success.
410 match key_accessor() {
411 Ok(result) => return Ok(result),
412 Err(e) => match e.root_cause().downcast_ref::<Error>() {
413 Some(&Error::Rc(ResponseCode::KEY_NOT_FOUND)) => {}
414 _ => return Err(e),
415 },
416 }
417
418 // Filter inputs. We can only load legacy app domain keys and some special rules due
Janis Danisevskis0ffb8a82022-02-06 22:37:21 -0800419 // to which we import keys transparently to an SELINUX domain.
Hasini Gunasinghe3ed5da72021-02-04 15:18:54 +0000420 let uid = match key {
421 KeyDescriptor { domain: Domain::APP, alias: Some(_), .. } => caller_uid,
422 KeyDescriptor { domain: Domain::SELINUX, nspace, alias: Some(_), .. } => {
423 match *nspace {
424 Self::WIFI_NAMESPACE => Self::AID_WIFI,
425 _ => {
426 return Err(Error::Rc(ResponseCode::KEY_NOT_FOUND))
427 .context(format!("No legacy keys for namespace {}", nspace))
428 }
429 }
430 }
431 _ => {
432 return Err(Error::Rc(ResponseCode::KEY_NOT_FOUND))
433 .context("No legacy keys for key descriptor.")
434 }
435 };
436
437 let key_clone = key.clone();
Janis Danisevskisf84d0b02022-01-26 14:11:14 -0800438 let result = self.do_serialized(move |importer_state| {
439 let super_key = super_key.map(|sk| -> Arc<dyn AesGcm> { sk });
440 importer_state.check_and_import(uid, key_clone, super_key)
441 });
Hasini Gunasinghe3ed5da72021-02-04 15:18:54 +0000442
443 if let Some(result) = result {
444 result?;
Janis Danisevskis0ffb8a82022-02-06 22:37:21 -0800445 // After successful import try again.
Hasini Gunasinghe3ed5da72021-02-04 15:18:54 +0000446 key_accessor()
447 } else {
448 Err(Error::Rc(ResponseCode::KEY_NOT_FOUND)).context("Legacy database is empty.")
449 }
450 }
451
452 /// Calls key_accessor and returns the result on success. In the case of a KEY_NOT_FOUND error
Janis Danisevskis0ffb8a82022-02-06 22:37:21 -0800453 /// this function makes an import request and on success retries the key_accessor.
454 pub fn with_try_import_super_key<F, T>(
Hasini Gunasinghe3ed5da72021-02-04 15:18:54 +0000455 &self,
456 user_id: u32,
Paul Crowleyf61fee72021-03-17 14:38:44 -0700457 pw: &Password,
Hasini Gunasinghe3ed5da72021-02-04 15:18:54 +0000458 mut key_accessor: F,
459 ) -> Result<Option<T>>
460 where
461 F: FnMut() -> Result<Option<T>>,
462 {
Janis Danisevskis0ffb8a82022-02-06 22:37:21 -0800463 let _wp = wd::watch_millis("LegacyImporter::with_try_import_super_key", 500);
Janis Danisevskis850d4862021-05-05 08:41:14 -0700464
Hasini Gunasinghe3ed5da72021-02-04 15:18:54 +0000465 match key_accessor() {
466 Ok(Some(result)) => return Ok(Some(result)),
467 Ok(None) => {}
468 Err(e) => return Err(e),
469 }
Janis Danisevskis0ffb8a82022-02-06 22:37:21 -0800470 let pw = pw.try_clone().context("In with_try_import_super_key: Cloning password.")?;
471 let result = self.do_serialized(move |importer_state| {
472 importer_state.check_and_import_super_key(user_id, &pw)
Hasini Gunasinghe3ed5da72021-02-04 15:18:54 +0000473 });
474
475 if let Some(result) = result {
476 result?;
Janis Danisevskis0ffb8a82022-02-06 22:37:21 -0800477 // After successful import try again.
Hasini Gunasinghe3ed5da72021-02-04 15:18:54 +0000478 key_accessor()
479 } else {
480 Ok(None)
481 }
482 }
483
Janis Danisevskis0ffb8a82022-02-06 22:37:21 -0800484 /// Deletes all keys belonging to the given namespace, importing them into the database
Janis Danisevskiseed69842021-02-18 20:04:10 -0800485 /// for subsequent garbage collection if necessary.
Janis Danisevskisddd6e752021-02-22 18:46:55 -0800486 pub fn bulk_delete_uid(&self, domain: Domain, nspace: i64) -> Result<()> {
Janis Danisevskis0ffb8a82022-02-06 22:37:21 -0800487 let _wp = wd::watch_millis("LegacyImporter::bulk_delete_uid", 500);
Janis Danisevskis850d4862021-05-05 08:41:14 -0700488
Janis Danisevskisddd6e752021-02-22 18:46:55 -0800489 let uid = match (domain, nspace) {
490 (Domain::APP, nspace) => nspace as u32,
491 (Domain::SELINUX, Self::WIFI_NAMESPACE) => Self::AID_WIFI,
492 // Nothing to do.
493 _ => return Ok(()),
494 };
495
Janis Danisevskis0ffb8a82022-02-06 22:37:21 -0800496 let result = self.do_serialized(move |importer_state| {
497 importer_state.bulk_delete(BulkDeleteRequest::Uid(uid), false)
Janis Danisevskiseed69842021-02-18 20:04:10 -0800498 });
499
500 result.unwrap_or(Ok(()))
501 }
502
Janis Danisevskis0ffb8a82022-02-06 22:37:21 -0800503 /// Deletes all keys belonging to the given android user, importing them into the database
Janis Danisevskiseed69842021-02-18 20:04:10 -0800504 /// for subsequent garbage collection if necessary.
505 pub fn bulk_delete_user(
506 &self,
507 user_id: u32,
508 keep_non_super_encrypted_keys: bool,
509 ) -> Result<()> {
Janis Danisevskis0ffb8a82022-02-06 22:37:21 -0800510 let _wp = wd::watch_millis("LegacyImporter::bulk_delete_user", 500);
Janis Danisevskis850d4862021-05-05 08:41:14 -0700511
Janis Danisevskis0ffb8a82022-02-06 22:37:21 -0800512 let result = self.do_serialized(move |importer_state| {
513 importer_state
Janis Danisevskiseed69842021-02-18 20:04:10 -0800514 .bulk_delete(BulkDeleteRequest::User(user_id), keep_non_super_encrypted_keys)
515 });
516
517 result.unwrap_or(Ok(()))
518 }
519
Hasini Gunasinghe3ed5da72021-02-04 15:18:54 +0000520 /// Queries the legacy database for the presence of a super key for the given user.
521 pub fn has_super_key(&self, user_id: u32) -> Result<bool> {
522 let result =
Janis Danisevskis0ffb8a82022-02-06 22:37:21 -0800523 self.do_serialized(move |importer_state| importer_state.has_super_key(user_id));
Hasini Gunasinghe3ed5da72021-02-04 15:18:54 +0000524 result.unwrap_or(Ok(false))
525 }
526}
527
Janis Danisevskis0ffb8a82022-02-06 22:37:21 -0800528impl LegacyImporterState {
Hasini Gunasinghe3ed5da72021-02-04 15:18:54 +0000529 fn get_km_uuid(&self, is_strongbox: bool) -> Result<Uuid> {
530 let sec_level = if is_strongbox {
531 SecurityLevel::STRONGBOX
532 } else {
533 SecurityLevel::TRUSTED_ENVIRONMENT
534 };
535
536 self.sec_level_to_km_uuid.get(&sec_level).copied().ok_or_else(|| {
537 anyhow::anyhow!(Error::sys()).context("In get_km_uuid: No KM instance for blob.")
538 })
539 }
540
541 fn list_uid(&mut self, uid: u32) -> Result<Vec<String>> {
542 self.legacy_loader
543 .list_keystore_entries_for_uid(uid)
544 .context("In list_uid: Trying to list legacy entries.")
545 }
546
Janis Danisevskisf84d0b02022-01-26 14:11:14 -0800547 fn migrate_namespaces(
548 &mut self,
549 src_uid: u32,
550 dest_uid: u32,
551 src_key: KeyDescriptor,
552 dest_key: KeyDescriptor,
553 ) -> Result<()> {
554 let src_alias = src_key.alias.ok_or_else(|| {
555 anyhow::anyhow!(Error::sys()).context(
556 "In legacy_migrator::migrate_namespace: src_key.alias must be Some because \
557 our caller must not have called us otherwise.",
558 )
559 })?;
560
561 if dest_key.domain != Domain::APP {
562 return Err(Error::Rc(ResponseCode::INVALID_ARGUMENT)).context(
563 "In legacy_migrator::migrate_namespace: \
564 Legacy in-place migration to SELinux namespace is not supported.",
565 );
566 }
567
568 let dest_alias = dest_key.alias.ok_or_else(|| {
569 anyhow::anyhow!(Error::sys()).context(concat!(
570 "In legacy_migrator::migrate_namespace: dest_key.alias must be Some because ",
571 "our caller must not have called us otherwise."
572 ))
573 })?;
574
575 self.legacy_loader
576 .move_keystore_entry(src_uid, dest_uid, &src_alias, &dest_alias)
577 .context("In legacy_migrator::migrate_namespace: Moving key entry files.")
578 }
579
580 /// Checks if the key can potentially be unlocked. And deletes the key entry otherwise.
581 /// If the super_key has already been imported, the super key database id is returned.
582 fn get_super_key_id_check_unlockable_or_delete(
583 &mut self,
584 uid: u32,
585 alias: &str,
586 ) -> Result<i64> {
587 let user_id = uid_to_android_user(uid);
588
589 match self
590 .db
591 .load_super_key(&USER_SUPER_KEY, user_id)
592 .context("In get_super_key_id_check_unlockable_or_delete: Failed to load super key")?
593 {
594 Some((_, entry)) => Ok(entry.id()),
595 None => {
596 // This might be the first time we access the super key,
597 // and it may not have been imported. We cannot import
598 // the legacy super_key key now, because we need to reencrypt
599 // it which we cannot do if we are not unlocked, which we are
600 // not because otherwise the key would have been imported.
601 // We can check though if the key exists. If it does,
602 // we can return Locked. Otherwise, we can delete the
603 // key and return NotFound, because the key will never
604 // be unlocked again.
605 if self.legacy_loader.has_super_key(user_id) {
606 Err(Error::Rc(ResponseCode::LOCKED)).context(
607 "In get_super_key_id_check_unlockable_or_delete: \
608 Cannot import super key of this key while user is locked.",
609 )
610 } else {
611 self.legacy_loader.remove_keystore_entry(uid, alias).context(
612 "In get_super_key_id_check_unlockable_or_delete: \
613 Trying to remove obsolete key.",
614 )?;
615 Err(Error::Rc(ResponseCode::KEY_NOT_FOUND))
616 .context("In get_super_key_id_check_unlockable_or_delete: Obsolete key.")
617 }
618 }
619 }
620 }
621
622 fn characteristics_file_to_cache(
623 &mut self,
624 km_blob_params: Option<(Blob, LegacyKeyCharacteristics)>,
625 super_key: &Option<Arc<dyn AesGcm>>,
626 uid: u32,
627 alias: &str,
628 ) -> Result<(Option<(Blob, Vec<KeyParameter>)>, Option<(LegacyBlob<'static>, BlobMetaData)>)>
629 {
630 let (km_blob, params) = match km_blob_params {
631 Some((km_blob, LegacyKeyCharacteristics::File(params))) => (km_blob, params),
632 Some((km_blob, LegacyKeyCharacteristics::Cache(params))) => {
633 return Ok((Some((km_blob, params)), None))
634 }
635 None => return Ok((None, None)),
636 };
637
638 let km_uuid = self
639 .get_km_uuid(km_blob.is_strongbox())
640 .context("In characteristics_file_to_cache: Trying to get KM UUID")?;
641
642 let blob = match (&km_blob.value(), super_key.as_ref()) {
643 (BlobValue::Encrypted { iv, tag, data }, Some(super_key)) => {
644 let blob = super_key
645 .decrypt(data, iv, tag)
646 .context("In characteristics_file_to_cache: Decryption failed.")?;
647 LegacyBlob::ZVec(blob)
648 }
649 (BlobValue::Encrypted { .. }, None) => {
650 return Err(Error::Rc(ResponseCode::LOCKED)).context(
651 "In characteristics_file_to_cache: Oh uh, so close. \
652 This ancient key cannot be imported unless the user is unlocked.",
653 );
654 }
655 (BlobValue::Decrypted(data), _) => LegacyBlob::Ref(data),
656 _ => {
657 return Err(Error::sys())
658 .context("In characteristics_file_to_cache: Unexpected blob type.")
659 }
660 };
661
662 let (km_params, upgraded_blob) = get_key_characteristics_without_app_data(&km_uuid, &*blob)
663 .context(
664 "In characteristics_file_to_cache: Failed to get key characteristics from device.",
665 )?;
666
667 let flags = km_blob.get_flags();
668
669 let (current_blob, superseded_blob) = if let Some(upgraded_blob) = upgraded_blob {
670 match (km_blob.take_value(), super_key.as_ref()) {
671 (BlobValue::Encrypted { iv, tag, data }, Some(super_key)) => {
672 let super_key_id =
673 self.get_super_key_id_check_unlockable_or_delete(uid, alias).context(
674 "In characteristics_file_to_cache: \
675 How is there a super key but no super key id?",
676 )?;
677
678 let mut superseded_metadata = BlobMetaData::new();
679 superseded_metadata.add(BlobMetaEntry::Iv(iv.to_vec()));
680 superseded_metadata.add(BlobMetaEntry::AeadTag(tag.to_vec()));
681 superseded_metadata
682 .add(BlobMetaEntry::EncryptedBy(EncryptedBy::KeyId(super_key_id)));
683 superseded_metadata.add(BlobMetaEntry::KmUuid(km_uuid));
684 let superseded_blob = (LegacyBlob::Vec(data), superseded_metadata);
685
686 let (data, iv, tag) = super_key.encrypt(&upgraded_blob).context(
687 "In characteristics_file_to_cache: \
688 Failed to encrypt upgraded key blob.",
689 )?;
690 (
691 Blob::new(flags, BlobValue::Encrypted { data, iv, tag }),
692 Some(superseded_blob),
693 )
694 }
695 (BlobValue::Encrypted { .. }, None) => {
696 return Err(Error::sys()).context(
697 "In characteristics_file_to_cache: This should not be reachable. \
698 The blob could not have been decrypted above.",
699 );
700 }
701 (BlobValue::Decrypted(data), _) => {
702 let mut superseded_metadata = BlobMetaData::new();
703 superseded_metadata.add(BlobMetaEntry::KmUuid(km_uuid));
704 let superseded_blob = (LegacyBlob::ZVec(data), superseded_metadata);
705 (
706 Blob::new(
707 flags,
708 BlobValue::Decrypted(upgraded_blob.try_into().context(
709 "In characteristics_file_to_cache: \
710 Failed to convert upgraded blob to ZVec.",
711 )?),
712 ),
713 Some(superseded_blob),
714 )
715 }
716 _ => {
717 return Err(Error::sys()).context(
718 "In characteristics_file_to_cache: This should not be reachable. \
719 Any other variant should have resulted in a different error.",
720 )
721 }
722 }
723 } else {
724 (km_blob, None)
725 };
726
727 let params =
728 augment_legacy_characteristics_file_with_key_characteristics(km_params, params);
729 Ok((Some((current_blob, params)), superseded_blob))
730 }
731
Janis Danisevskis0ffb8a82022-02-06 22:37:21 -0800732 /// This is a key import request that must run in the importer thread. This must
Hasini Gunasinghe3ed5da72021-02-04 15:18:54 +0000733 /// be passed to do_serialized.
Janis Danisevskisf84d0b02022-01-26 14:11:14 -0800734 fn check_and_import(
735 &mut self,
736 uid: u32,
737 mut key: KeyDescriptor,
738 super_key: Option<Arc<dyn AesGcm>>,
739 ) -> Result<()> {
Hasini Gunasinghe3ed5da72021-02-04 15:18:54 +0000740 let alias = key.alias.clone().ok_or_else(|| {
Janis Danisevskis0ffb8a82022-02-06 22:37:21 -0800741 anyhow::anyhow!(Error::sys()).context(
742 "In check_and_import: Must be Some because \
743 our caller must not have called us otherwise.",
744 )
Hasini Gunasinghe3ed5da72021-02-04 15:18:54 +0000745 })?;
746
Janis Danisevskis0ffb8a82022-02-06 22:37:21 -0800747 if self.recently_imported.contains(&RecentImport::new(uid, alias.clone())) {
Hasini Gunasinghe3ed5da72021-02-04 15:18:54 +0000748 return Ok(());
749 }
750
751 if key.domain == Domain::APP {
752 key.nspace = uid as i64;
753 }
754
755 // If the key is not found in the cache, try to load from the legacy database.
756 let (km_blob_params, user_cert, ca_cert) = self
757 .legacy_loader
Janis Danisevskisf84d0b02022-01-26 14:11:14 -0800758 .load_by_uid_alias(uid, &alias, &super_key)
759 .map_err(|e| {
760 if e.root_cause().downcast_ref::<legacy_blob::Error>()
761 == Some(&legacy_blob::Error::LockedComponent)
762 {
763 // There is no chance to succeed at this point. We just check if there is
764 // a super key so that this entry might be unlockable in the future.
765 // If not the entry will be deleted and KEY_NOT_FOUND is returned.
766 // If a super key id was returned we still have to return LOCKED but the key
767 // may be imported when the user unlocks the device.
768 self.get_super_key_id_check_unlockable_or_delete(uid, &alias)
769 .and_then::<i64, _>(|_| {
770 Err(Error::Rc(ResponseCode::LOCKED))
771 .context("Super key present but locked.")
772 })
773 .unwrap_err()
774 } else {
775 e
776 }
777 })
Janis Danisevskis0ffb8a82022-02-06 22:37:21 -0800778 .context("In check_and_import: Trying to load legacy blob.")?;
Janis Danisevskisf84d0b02022-01-26 14:11:14 -0800779
780 let (km_blob_params, superseded_blob) = self
781 .characteristics_file_to_cache(km_blob_params, &super_key, uid, &alias)
782 .context("In check_and_import: Trying to update legacy charateristics.")?;
783
Hasini Gunasinghe3ed5da72021-02-04 15:18:54 +0000784 let result = match km_blob_params {
785 Some((km_blob, params)) => {
786 let is_strongbox = km_blob.is_strongbox();
Janis Danisevskisf84d0b02022-01-26 14:11:14 -0800787
Hasini Gunasinghe3ed5da72021-02-04 15:18:54 +0000788 let (blob, mut blob_metadata) = match km_blob.take_value() {
789 BlobValue::Encrypted { iv, tag, data } => {
790 // Get super key id for user id.
Janis Danisevskisf84d0b02022-01-26 14:11:14 -0800791 let super_key_id = self
792 .get_super_key_id_check_unlockable_or_delete(uid, &alias)
793 .context("In check_and_import: Failed to get super key id.")?;
Hasini Gunasinghe3ed5da72021-02-04 15:18:54 +0000794
795 let mut blob_metadata = BlobMetaData::new();
796 blob_metadata.add(BlobMetaEntry::Iv(iv.to_vec()));
797 blob_metadata.add(BlobMetaEntry::AeadTag(tag.to_vec()));
798 blob_metadata
799 .add(BlobMetaEntry::EncryptedBy(EncryptedBy::KeyId(super_key_id)));
800 (LegacyBlob::Vec(data), blob_metadata)
801 }
802 BlobValue::Decrypted(data) => (LegacyBlob::ZVec(data), BlobMetaData::new()),
803 _ => {
804 return Err(Error::Rc(ResponseCode::KEY_NOT_FOUND))
Janis Danisevskis0ffb8a82022-02-06 22:37:21 -0800805 .context("In check_and_import: Legacy key has unexpected type.")
Hasini Gunasinghe3ed5da72021-02-04 15:18:54 +0000806 }
807 };
808
809 let km_uuid = self
810 .get_km_uuid(is_strongbox)
Janis Danisevskis0ffb8a82022-02-06 22:37:21 -0800811 .context("In check_and_import: Trying to get KM UUID")?;
Hasini Gunasinghe3ed5da72021-02-04 15:18:54 +0000812 blob_metadata.add(BlobMetaEntry::KmUuid(km_uuid));
813
814 let mut metadata = KeyMetaData::new();
815 let creation_date = DateTime::now()
Janis Danisevskis0ffb8a82022-02-06 22:37:21 -0800816 .context("In check_and_import: Trying to make creation time.")?;
Hasini Gunasinghe3ed5da72021-02-04 15:18:54 +0000817 metadata.add(KeyMetaEntry::CreationDate(creation_date));
818
Janis Danisevskisf84d0b02022-01-26 14:11:14 -0800819 let blob_info = BlobInfo::new_with_superseded(
820 &blob,
821 &blob_metadata,
822 superseded_blob.as_ref().map(|(b, m)| (&**b, m)),
823 );
Hasini Gunasinghe3ed5da72021-02-04 15:18:54 +0000824 // Store legacy key in the database.
825 self.db
826 .store_new_key(
827 &key,
Janis Danisevskis0cabd712021-05-25 11:07:10 -0700828 KeyType::Client,
Hasini Gunasinghe3ed5da72021-02-04 15:18:54 +0000829 &params,
Janis Danisevskisf84d0b02022-01-26 14:11:14 -0800830 &blob_info,
Hasini Gunasinghe3ed5da72021-02-04 15:18:54 +0000831 &CertificateInfo::new(user_cert, ca_cert),
832 &metadata,
833 &km_uuid,
834 )
Janis Danisevskis0ffb8a82022-02-06 22:37:21 -0800835 .context("In check_and_import.")?;
Hasini Gunasinghe3ed5da72021-02-04 15:18:54 +0000836 Ok(())
837 }
838 None => {
839 if let Some(ca_cert) = ca_cert {
840 self.db
Janis Danisevskis0cabd712021-05-25 11:07:10 -0700841 .store_new_certificate(&key, KeyType::Client, &ca_cert, &KEYSTORE_UUID)
Janis Danisevskis0ffb8a82022-02-06 22:37:21 -0800842 .context("In check_and_import: Failed to insert new certificate.")?;
Hasini Gunasinghe3ed5da72021-02-04 15:18:54 +0000843 Ok(())
844 } else {
845 Err(Error::Rc(ResponseCode::KEY_NOT_FOUND))
Janis Danisevskis0ffb8a82022-02-06 22:37:21 -0800846 .context("In check_and_import: Legacy key not found.")
Hasini Gunasinghe3ed5da72021-02-04 15:18:54 +0000847 }
848 }
849 };
850
851 match result {
852 Ok(()) => {
Janis Danisevskis0ffb8a82022-02-06 22:37:21 -0800853 // Add the key to the imported_keys list.
854 self.recently_imported.insert(RecentImport::new(uid, alias.clone()));
Hasini Gunasinghe3ed5da72021-02-04 15:18:54 +0000855 // Delete legacy key from the file system
856 self.legacy_loader
857 .remove_keystore_entry(uid, &alias)
Janis Danisevskis0ffb8a82022-02-06 22:37:21 -0800858 .context("In check_and_import: Trying to remove imported key.")?;
Hasini Gunasinghe3ed5da72021-02-04 15:18:54 +0000859 Ok(())
860 }
861 Err(e) => Err(e),
862 }
863 }
864
Janis Danisevskis0ffb8a82022-02-06 22:37:21 -0800865 fn check_and_import_super_key(&mut self, user_id: u32, pw: &Password) -> Result<()> {
866 if self.recently_imported_super_key.contains(&user_id) {
Hasini Gunasinghe3ed5da72021-02-04 15:18:54 +0000867 return Ok(());
868 }
869
870 if let Some(super_key) = self
871 .legacy_loader
Chris Wailesd5aaaef2021-07-27 16:04:33 -0700872 .load_super_key(user_id, pw)
Janis Danisevskis0ffb8a82022-02-06 22:37:21 -0800873 .context("In check_and_import_super_key: Trying to load legacy super key.")?
Hasini Gunasinghe3ed5da72021-02-04 15:18:54 +0000874 {
875 let (blob, blob_metadata) =
Paul Crowleyf61fee72021-03-17 14:38:44 -0700876 crate::super_key::SuperKeyManager::encrypt_with_password(&super_key, pw)
Janis Danisevskis0ffb8a82022-02-06 22:37:21 -0800877 .context("In check_and_import_super_key: Trying to encrypt super key.")?;
Hasini Gunasinghe3ed5da72021-02-04 15:18:54 +0000878
Paul Crowley8d5b2532021-03-19 10:53:07 -0700879 self.db
880 .store_super_key(
881 user_id,
882 &USER_SUPER_KEY,
883 &blob,
884 &blob_metadata,
885 &KeyMetaData::new(),
886 )
887 .context(concat!(
Janis Danisevskis0ffb8a82022-02-06 22:37:21 -0800888 "In check_and_import_super_key: ",
Paul Crowley7a658392021-03-18 17:08:20 -0700889 "Trying to insert legacy super_key into the database."
Paul Crowley8d5b2532021-03-19 10:53:07 -0700890 ))?;
Hasini Gunasinghe3ed5da72021-02-04 15:18:54 +0000891 self.legacy_loader.remove_super_key(user_id);
Janis Danisevskis0ffb8a82022-02-06 22:37:21 -0800892 self.recently_imported_super_key.insert(user_id);
Hasini Gunasinghe3ed5da72021-02-04 15:18:54 +0000893 Ok(())
894 } else {
895 Err(Error::Rc(ResponseCode::KEY_NOT_FOUND))
Janis Danisevskis0ffb8a82022-02-06 22:37:21 -0800896 .context("In check_and_import_super_key: No key found do import.")
Hasini Gunasinghe3ed5da72021-02-04 15:18:54 +0000897 }
898 }
899
Janis Danisevskis0ffb8a82022-02-06 22:37:21 -0800900 /// Key importer request to be run by do_serialized.
901 /// See LegacyImporter::bulk_delete_uid and LegacyImporter::bulk_delete_user.
Janis Danisevskiseed69842021-02-18 20:04:10 -0800902 fn bulk_delete(
903 &mut self,
904 bulk_delete_request: BulkDeleteRequest,
905 keep_non_super_encrypted_keys: bool,
906 ) -> Result<()> {
907 let (aliases, user_id) = match bulk_delete_request {
908 BulkDeleteRequest::Uid(uid) => (
909 self.legacy_loader
910 .list_keystore_entries_for_uid(uid)
911 .context("In bulk_delete: Trying to get aliases for uid.")
912 .map(|aliases| {
913 let mut h = HashMap::<u32, HashSet<String>>::new();
914 h.insert(uid, aliases.into_iter().collect());
915 h
916 })?,
917 uid_to_android_user(uid),
918 ),
919 BulkDeleteRequest::User(user_id) => (
920 self.legacy_loader
921 .list_keystore_entries_for_user(user_id)
922 .context("In bulk_delete: Trying to get aliases for user_id.")?,
923 user_id,
924 ),
925 };
926
927 let super_key_id = self
928 .db
Paul Crowley7a658392021-03-18 17:08:20 -0700929 .load_super_key(&USER_SUPER_KEY, user_id)
Janis Danisevskiseed69842021-02-18 20:04:10 -0800930 .context("In bulk_delete: Failed to load super key")?
931 .map(|(_, entry)| entry.id());
932
933 for (uid, alias) in aliases
934 .into_iter()
935 .map(|(uid, aliases)| aliases.into_iter().map(move |alias| (uid, alias)))
936 .flatten()
937 {
938 let (km_blob_params, _, _) = self
939 .legacy_loader
Janis Danisevskisf84d0b02022-01-26 14:11:14 -0800940 .load_by_uid_alias(uid, &alias, &None)
Janis Danisevskiseed69842021-02-18 20:04:10 -0800941 .context("In bulk_delete: Trying to load legacy blob.")?;
942
943 // Determine if the key needs special handling to be deleted.
944 let (need_gc, is_super_encrypted) = km_blob_params
945 .as_ref()
946 .map(|(blob, params)| {
Janis Danisevskisf84d0b02022-01-26 14:11:14 -0800947 let params = match params {
948 LegacyKeyCharacteristics::Cache(params)
949 | LegacyKeyCharacteristics::File(params) => params,
950 };
Janis Danisevskiseed69842021-02-18 20:04:10 -0800951 (
952 params.iter().any(|kp| {
953 KeyParameterValue::RollbackResistance == *kp.key_parameter_value()
954 }),
955 blob.is_encrypted(),
956 )
957 })
958 .unwrap_or((false, false));
959
960 if keep_non_super_encrypted_keys && !is_super_encrypted {
961 continue;
962 }
963
964 if need_gc {
965 let mark_deleted = match km_blob_params
966 .map(|(blob, _)| (blob.is_strongbox(), blob.take_value()))
967 {
968 Some((is_strongbox, BlobValue::Encrypted { iv, tag, data })) => {
969 let mut blob_metadata = BlobMetaData::new();
970 if let (Ok(km_uuid), Some(super_key_id)) =
971 (self.get_km_uuid(is_strongbox), super_key_id)
972 {
973 blob_metadata.add(BlobMetaEntry::KmUuid(km_uuid));
974 blob_metadata.add(BlobMetaEntry::Iv(iv.to_vec()));
975 blob_metadata.add(BlobMetaEntry::AeadTag(tag.to_vec()));
976 blob_metadata
977 .add(BlobMetaEntry::EncryptedBy(EncryptedBy::KeyId(super_key_id)));
978 Some((LegacyBlob::Vec(data), blob_metadata))
979 } else {
980 // Oh well - we tried our best, but if we cannot determine which
981 // KeyMint instance we have to send this blob to, we cannot
982 // do more than delete the key from the file system.
983 // And if we don't know which key wraps this key we cannot
984 // unwrap it for KeyMint either.
985 None
986 }
987 }
988 Some((_, BlobValue::Decrypted(data))) => {
989 Some((LegacyBlob::ZVec(data), BlobMetaData::new()))
990 }
991 _ => None,
992 };
993
994 if let Some((blob, blob_metadata)) = mark_deleted {
995 self.db.set_deleted_blob(&blob, &blob_metadata).context(concat!(
996 "In bulk_delete: Trying to insert deleted ",
997 "blob into the database for garbage collection."
998 ))?;
999 }
1000 }
1001
1002 self.legacy_loader
1003 .remove_keystore_entry(uid, &alias)
Janis Danisevskis0ffb8a82022-02-06 22:37:21 -08001004 .context("In bulk_delete: Trying to remove imported key.")?;
Janis Danisevskiseed69842021-02-18 20:04:10 -08001005 }
1006 Ok(())
1007 }
1008
Hasini Gunasinghe3ed5da72021-02-04 15:18:54 +00001009 fn has_super_key(&mut self, user_id: u32) -> Result<bool> {
Janis Danisevskis0ffb8a82022-02-06 22:37:21 -08001010 Ok(self.recently_imported_super_key.contains(&user_id)
Hasini Gunasinghe3ed5da72021-02-04 15:18:54 +00001011 || self.legacy_loader.has_super_key(user_id))
1012 }
1013
1014 fn check_empty(&self) -> u8 {
1015 if self.legacy_loader.is_empty().unwrap_or(false) {
Janis Danisevskis0ffb8a82022-02-06 22:37:21 -08001016 LegacyImporter::STATE_EMPTY
Hasini Gunasinghe3ed5da72021-02-04 15:18:54 +00001017 } else {
Janis Danisevskis0ffb8a82022-02-06 22:37:21 -08001018 LegacyImporter::STATE_READY
Hasini Gunasinghe3ed5da72021-02-04 15:18:54 +00001019 }
1020 }
1021}
1022
Janis Danisevskisf84d0b02022-01-26 14:11:14 -08001023enum LegacyBlob<'a> {
Hasini Gunasinghe3ed5da72021-02-04 15:18:54 +00001024 Vec(Vec<u8>),
1025 ZVec(ZVec),
Janis Danisevskisf84d0b02022-01-26 14:11:14 -08001026 Ref(&'a [u8]),
Hasini Gunasinghe3ed5da72021-02-04 15:18:54 +00001027}
1028
Janis Danisevskisf84d0b02022-01-26 14:11:14 -08001029impl Deref for LegacyBlob<'_> {
Hasini Gunasinghe3ed5da72021-02-04 15:18:54 +00001030 type Target = [u8];
1031
1032 fn deref(&self) -> &Self::Target {
1033 match self {
Chris Wailesd5aaaef2021-07-27 16:04:33 -07001034 Self::Vec(v) => v,
1035 Self::ZVec(v) => v,
Janis Danisevskisf84d0b02022-01-26 14:11:14 -08001036 Self::Ref(v) => v,
Hasini Gunasinghe3ed5da72021-02-04 15:18:54 +00001037 }
1038 }
1039}
Janis Danisevskisf84d0b02022-01-26 14:11:14 -08001040
1041/// This function takes two KeyParameter lists. The first is assumed to have been retrieved from the
1042/// KM back end using km_dev.getKeyCharacteristics. The second is assumed to have been retrieved
1043/// from a legacy key characteristics file (not cache) as used in Android P and older. The function
1044/// augments the former with entries from the latter only if no equivalent entry is present ignoring.
1045/// the security level of enforcement. All entries in the latter are assumed to have security level
1046/// KEYSTORE.
1047fn augment_legacy_characteristics_file_with_key_characteristics<T>(
1048 mut from_km: Vec<KeyParameter>,
1049 legacy: T,
1050) -> Vec<KeyParameter>
1051where
1052 T: IntoIterator<Item = KeyParameter>,
1053{
1054 for legacy_kp in legacy.into_iter() {
1055 if !from_km
1056 .iter()
1057 .any(|km_kp| km_kp.key_parameter_value() == legacy_kp.key_parameter_value())
1058 {
1059 from_km.push(legacy_kp);
1060 }
1061 }
1062 from_km
1063}
1064
1065/// Attempts to retrieve the key characteristics for the given blob from the KM back end with the
1066/// given UUID. It may upgrade the key blob in the process. In that case the upgraded blob is
1067/// returned as the second tuple member.
1068fn get_key_characteristics_without_app_data(
1069 uuid: &Uuid,
1070 blob: &[u8],
1071) -> Result<(Vec<KeyParameter>, Option<Vec<u8>>)> {
1072 let (km_dev, _) = crate::globals::get_keymint_dev_by_uuid(uuid)
1073 .with_context(|| format!("In foo: Trying to get km device for id {:?}", uuid))?;
1074
1075 let (characteristics, upgraded_blob) = upgrade_keyblob_if_required_with(
1076 &*km_dev,
1077 blob,
1078 &[],
1079 |blob| {
1080 let _wd = wd::watch_millis("In foo: Calling GetKeyCharacteristics.", 500);
1081 map_km_error(km_dev.getKeyCharacteristics(blob, &[], &[]))
1082 },
1083 |_| Ok(()),
1084 )
1085 .context("In foo.")?;
1086 Ok((key_characteristics_to_internal(characteristics), upgraded_blob))
1087}