blob: 75f20271fc682115aea26161c658791722151656 [file] [log] [blame]
Joel Galenson26f4d012020-07-17 14:57:21 -07001// 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 Danisevskis63f7bc82020-09-03 10:12:56 -070015//! This is the Keystore 2.0 database module.
16//! The database module provides a connection to the backing SQLite store.
17//! We have two databases one for persistent key blob storage and one for
18//! items that have a per boot life cycle.
19//!
20//! ## Persistent database
21//! The persistent database has tables for key blobs. They are organized
22//! as follows:
23//! The `keyentry` table is the primary table for key entries. It is
24//! accompanied by two tables for blobs and parameters.
25//! Each key entry occupies exactly one row in the `keyentry` table and
26//! zero or more rows in the tables `blobentry` and `keyparameter`.
27//!
28//! ## Per boot database
29//! The per boot database stores items with a per boot lifecycle.
30//! Currently, there is only the `grant` table in this database.
31//! Grants are references to a key that can be used to access a key by
32//! clients that don't own that key. Grants can only be created by the
33//! owner of a key. And only certain components can create grants.
34//! This is governed by SEPolicy.
35//!
36//! ## Access control
37//! Some database functions that load keys or create grants perform
38//! access control. This is because in some cases access control
39//! can only be performed after some information about the designated
40//! key was loaded from the database. To decouple the permission checks
41//! from the database module these functions take permission check
42//! callbacks.
Joel Galenson26f4d012020-07-17 14:57:21 -070043
Joel Galenson0891bc12020-07-20 10:37:03 -070044use crate::error::Error as KsError;
Janis Danisevskis63f7bc82020-09-03 10:12:56 -070045use crate::{error, permission::KeyPermSet};
46use anyhow::{anyhow, Context, Result};
Janis Danisevskis60400fe2020-08-26 15:24:42 -070047
Janis Danisevskis63f7bc82020-09-03 10:12:56 -070048use android_hardware_keymint::aidl::android::hardware::keymint::SecurityLevel::SecurityLevel;
Janis Danisevskis60400fe2020-08-26 15:24:42 -070049use android_security_keystore2::aidl::android::security::keystore2::{
Janis Danisevskis63f7bc82020-09-03 10:12:56 -070050 Domain, Domain::Domain as DomainType, KeyDescriptor::KeyDescriptor,
Janis Danisevskis60400fe2020-08-26 15:24:42 -070051};
52
Joel Galenson0891bc12020-07-20 10:37:03 -070053#[cfg(not(test))]
54use rand::prelude::random;
Janis Danisevskis63f7bc82020-09-03 10:12:56 -070055use rusqlite::{
56 params, types::FromSql, types::FromSqlResult, types::ToSqlOutput, types::ValueRef, Connection,
57 OptionalExtension, Row, Rows, ToSql, Transaction, TransactionBehavior, NO_PARAMS,
58};
Janis Danisevskis4df44f42020-08-26 14:40:03 -070059use std::sync::Once;
Joel Galenson0891bc12020-07-20 10:37:03 -070060#[cfg(test)]
61use tests::random;
Joel Galenson26f4d012020-07-17 14:57:21 -070062
Janis Danisevskis63f7bc82020-09-03 10:12:56 -070063/// Keys have a KeyMint blob component and optional public certificate and
64/// certificate chain components.
65/// KeyEntryLoadBits is a bitmap that indicates to `KeystoreDB::load_key_entry`
66/// which components shall be loaded from the database if present.
67#[derive(Debug, Eq, PartialEq, Ord, PartialOrd)]
68pub struct KeyEntryLoadBits(u32);
69
70impl KeyEntryLoadBits {
71 /// Indicate to `KeystoreDB::load_key_entry` that no component shall be loaded.
72 pub const NONE: KeyEntryLoadBits = Self(0);
73 /// Indicate to `KeystoreDB::load_key_entry` that the KeyMint component shall be loaded.
74 pub const KM: KeyEntryLoadBits = Self(1);
75 /// Indicate to `KeystoreDB::load_key_entry` that the Public components shall be loaded.
76 pub const PUBLIC: KeyEntryLoadBits = Self(2);
77 /// Indicate to `KeystoreDB::load_key_entry` that both components shall be loaded.
78 pub const BOTH: KeyEntryLoadBits = Self(3);
79
80 /// Returns true if this object indicates that the public components shall be loaded.
81 pub const fn load_public(&self) -> bool {
82 self.0 & Self::PUBLIC.0 != 0
83 }
84
85 /// Returns true if the object indicates that the KeyMint component shall be loaded.
86 pub const fn load_km(&self) -> bool {
87 self.0 & Self::KM.0 != 0
88 }
89}
90
91/// This type represents a Keystore 2.0 key entry.
92/// An entry has a unique `id` by which it can be found in the database.
93/// It has a security level field, key parameters, and three optional fields
94/// for the KeyMint blob, public certificate and a public certificate chain.
95#[derive(Debug, Default, Clone, Eq, PartialEq, Ord, PartialOrd)]
96pub struct KeyEntry {
97 id: i64,
98 km_blob: Option<Vec<u8>>,
99 cert: Option<Vec<u8>>,
100 cert_chain: Option<Vec<u8>>,
101 sec_level: SecurityLevel,
102 // parameters: Vec<KeyParameters>,
103}
104
105impl KeyEntry {
106 /// Returns the unique id of the Key entry.
107 pub fn id(&self) -> i64 {
108 self.id
109 }
110 /// Exposes the optional KeyMint blob.
111 pub fn km_blob(&self) -> &Option<Vec<u8>> {
112 &self.km_blob
113 }
114 /// Extracts the Optional KeyMint blob.
115 pub fn take_km_blob(&mut self) -> Option<Vec<u8>> {
116 self.km_blob.take()
117 }
118 /// Exposes the optional public certificate.
119 pub fn cert(&self) -> &Option<Vec<u8>> {
120 &self.cert
121 }
122 /// Extracts the optional public certificate.
123 pub fn take_cert(&mut self) -> Option<Vec<u8>> {
124 self.cert.take()
125 }
126 /// Exposes the optional public certificate chain.
127 pub fn cert_chain(&self) -> &Option<Vec<u8>> {
128 &self.cert_chain
129 }
130 /// Extracts the optional public certificate_chain.
131 pub fn take_cert_chain(&mut self) -> Option<Vec<u8>> {
132 self.cert_chain.take()
133 }
134 /// Returns the security level of the key entry.
135 pub fn sec_level(&self) -> SecurityLevel {
136 self.sec_level
137 }
138}
139
140/// Indicates the sub component of a key entry for persistent storage.
141#[derive(Debug, Eq, PartialEq, Ord, PartialOrd)]
142pub struct SubComponentType(u32);
143impl SubComponentType {
144 /// Persistent identifier for a KeyMint blob.
145 pub const KM_BLOB: SubComponentType = Self(0);
146 /// Persistent identifier for a certificate blob.
147 pub const CERT: SubComponentType = Self(1);
148 /// Persistent identifier for a certificate chain blob.
149 pub const CERT_CHAIN: SubComponentType = Self(2);
150}
151
152impl ToSql for SubComponentType {
153 fn to_sql(&self) -> rusqlite::Result<ToSqlOutput> {
154 self.0.to_sql()
155 }
156}
157
158impl FromSql for SubComponentType {
159 fn column_result(value: ValueRef) -> FromSqlResult<Self> {
160 Ok(Self(u32::column_result(value)?))
161 }
162}
163
Janis Danisevskis4df44f42020-08-26 14:40:03 -0700164static INIT_TABLES: Once = Once::new();
165
Janis Danisevskis63f7bc82020-09-03 10:12:56 -0700166/// KeystoreDB wraps a connection to an SQLite database and tracks its
167/// ownership. It also implements all of Keystore 2.0's database functionality.
Joel Galenson26f4d012020-07-17 14:57:21 -0700168pub struct KeystoreDB {
Joel Galenson26f4d012020-07-17 14:57:21 -0700169 conn: Connection,
170}
171
172impl KeystoreDB {
Janis Danisevskis63f7bc82020-09-03 10:12:56 -0700173 /// This will create a new database connection connecting the two
174 /// files persistent.sqlite and perboot.sqlite in the current working
175 /// directory, which is usually `/data/misc/keystore/`.
176 /// It also attempts to initialize all of the tables on the first instantiation
177 /// per service startup. KeystoreDB cannot be used by multiple threads.
178 /// Each thread should open their own connection using `thread_local!`.
Janis Danisevskis4df44f42020-08-26 14:40:03 -0700179 pub fn new() -> Result<Self> {
180 let conn = Self::make_connection("file:persistent.sqlite", "file:perboot.sqlite")?;
181
182 INIT_TABLES.call_once(|| Self::init_tables(&conn).expect("Failed to initialize tables."));
183 Ok(Self { conn })
Joel Galenson2aab4432020-07-22 15:27:57 -0700184 }
185
Janis Danisevskis4df44f42020-08-26 14:40:03 -0700186 fn init_tables(conn: &Connection) -> Result<()> {
187 conn.execute(
188 "CREATE TABLE IF NOT EXISTS persistent.keyentry (
Joel Galenson0891bc12020-07-20 10:37:03 -0700189 id INTEGER UNIQUE,
190 creation_date DATETIME,
191 domain INTEGER,
192 namespace INTEGER,
193 alias TEXT);",
Janis Danisevskis4df44f42020-08-26 14:40:03 -0700194 NO_PARAMS,
195 )
196 .context("Failed to initialize \"keyentry\" table.")?;
197
198 conn.execute(
Janis Danisevskis63f7bc82020-09-03 10:12:56 -0700199 "CREATE VIEW IF NOT EXISTS persistent.orphaned AS
200 SELECT id FROM persistent.keyentry WHERE domain IS NULL;",
201 NO_PARAMS,
202 )
203 .context("Failed to initialize \"orphaned\" view")?;
204
205 conn.execute(
206 "CREATE TABLE IF NOT EXISTS persistent.blobentry (
207 id INTEGER PRIMARY KEY,
208 subcomponent_type INTEGER,
209 keyentryid INTEGER,
210 blob BLOB,
211 sec_level INTEGER);",
212 NO_PARAMS,
213 )
214 .context("Failed to initialize \"blobentry\" table.")?;
215
216 conn.execute(
Janis Danisevskis4df44f42020-08-26 14:40:03 -0700217 "CREATE TABLE IF NOT EXISTS persistent.keyparameter (
Hasini Gunasingheaf993662020-07-24 18:40:20 +0000218 keyentryid INTEGER,
219 tag INTEGER,
220 data ANY,
221 security_level INTEGER);",
Janis Danisevskis4df44f42020-08-26 14:40:03 -0700222 NO_PARAMS,
223 )
224 .context("Failed to initialize \"keyparameter\" table.")?;
225
Janis Danisevskis63f7bc82020-09-03 10:12:56 -0700226 // TODO only drop the perboot table if we start up for the first time per boot.
227 // Right now this is done once per startup which will lose some information
228 // upon a crash.
229 // Note: This is no regression with respect to the legacy Keystore.
230 conn.execute("DROP TABLE IF EXISTS perboot.grant;", NO_PARAMS)
231 .context("Failed to drop perboot.grant table")?;
232 conn.execute(
233 "CREATE TABLE perboot.grant (
234 id INTEGER UNIQUE,
235 grantee INTEGER,
236 keyentryid INTEGER,
237 access_vector INTEGER);",
238 NO_PARAMS,
239 )
240 .context("Failed to initialize \"grant\" table.")?;
241
Joel Galenson0891bc12020-07-20 10:37:03 -0700242 Ok(())
243 }
244
Janis Danisevskis4df44f42020-08-26 14:40:03 -0700245 fn make_connection(persistent_file: &str, perboot_file: &str) -> Result<Connection> {
246 let conn =
247 Connection::open_in_memory().context("Failed to initialize SQLite connection.")?;
248
249 conn.execute("ATTACH DATABASE ? as persistent;", params![persistent_file])
250 .context("Failed to attach database persistent.")?;
251 conn.execute("ATTACH DATABASE ? as perboot;", params![perboot_file])
252 .context("Failed to attach database perboot.")?;
253
254 Ok(conn)
255 }
256
Janis Danisevskis63f7bc82020-09-03 10:12:56 -0700257 /// Creates a new key entry and allocates a new randomized id for the new key.
258 /// The key id gets associated with a domain and namespace but not with an alias.
259 /// To complete key generation `rebind_alias` should be called after all of the
260 /// key artifacts, i.e., blobs and parameters have been associated with the new
261 /// key id. Finalizing with `rebind_alias` makes the creation of a new key entry
262 /// atomic even if key generation is not.
Janis Danisevskis60400fe2020-08-26 15:24:42 -0700263 pub fn create_key_entry(&self, domain: DomainType, namespace: i64) -> Result<i64> {
Joel Galenson0891bc12020-07-20 10:37:03 -0700264 match domain {
Janis Danisevskis60400fe2020-08-26 15:24:42 -0700265 Domain::App | Domain::SELinux => {}
Joel Galenson0891bc12020-07-20 10:37:03 -0700266 _ => {
267 return Err(KsError::sys())
268 .context(format!("Domain {:?} must be either App or SELinux.", domain));
269 }
270 }
271 // Loop until we get a unique id.
272 loop {
273 let newid: i64 = random();
274 let ret = self.conn.execute(
Joel Galenson2aab4432020-07-22 15:27:57 -0700275 "INSERT into persistent.keyentry (id, creation_date, domain, namespace, alias)
Joel Galenson0891bc12020-07-20 10:37:03 -0700276 VALUES(?, datetime('now'), ?, ?, NULL);",
277 params![newid, domain as i64, namespace],
278 );
279 match ret {
280 // If the id already existed, try again.
281 Err(rusqlite::Error::SqliteFailure(
282 libsqlite3_sys::Error {
283 code: libsqlite3_sys::ErrorCode::ConstraintViolation,
284 extended_code: libsqlite3_sys::SQLITE_CONSTRAINT_UNIQUE,
285 },
286 _,
287 )) => (),
Janis Danisevskis63f7bc82020-09-03 10:12:56 -0700288 Err(e) => return Err(e).context("Failed to create key entry."),
Joel Galenson0891bc12020-07-20 10:37:03 -0700289 _ => return Ok(newid),
290 }
291 }
Joel Galenson26f4d012020-07-17 14:57:21 -0700292 }
Joel Galenson33c04ad2020-08-03 11:04:38 -0700293
Janis Danisevskis63f7bc82020-09-03 10:12:56 -0700294 /// Inserts a new blob and associates it with the given key id. Each blob
295 /// has a sub component type and a security level.
296 /// Each key can have one of each sub component type associated. If more
297 /// are added only the most recent can be retrieved, and superseded blobs
298 /// will get garbage collected. The security level field of components
299 /// other than `SubComponentType::KM_BLOB` are ignored.
300 pub fn insert_blob(
301 &mut self,
302 key_id: i64,
303 sc_type: SubComponentType,
304 blob: &[u8],
305 sec_level: SecurityLevel,
306 ) -> Result<()> {
307 self.conn
308 .execute(
309 "INSERT into persistent.blobentry (subcomponent_type, keyentryid, blob, sec_level)
310 VALUES (?, ?, ?, ?);",
311 params![sc_type, key_id, blob, sec_level],
312 )
313 .context("Failed to insert blob.")?;
314 Ok(())
315 }
316
317 /// Updates the alias column of the given key id `newid` with the given alias,
318 /// and atomically, removes the alias, domain, and namespace from another row
319 /// with the same alias-domain-namespace tuple if such row exits.
Joel Galenson33c04ad2020-08-03 11:04:38 -0700320 pub fn rebind_alias(
321 &mut self,
Janis Danisevskis63f7bc82020-09-03 10:12:56 -0700322 newid: i64,
Joel Galenson33c04ad2020-08-03 11:04:38 -0700323 alias: &str,
Janis Danisevskis60400fe2020-08-26 15:24:42 -0700324 domain: DomainType,
Joel Galenson33c04ad2020-08-03 11:04:38 -0700325 namespace: i64,
326 ) -> Result<()> {
327 match domain {
Janis Danisevskis60400fe2020-08-26 15:24:42 -0700328 Domain::App | Domain::SELinux => {}
Joel Galenson33c04ad2020-08-03 11:04:38 -0700329 _ => {
Janis Danisevskis63f7bc82020-09-03 10:12:56 -0700330 return Err(KsError::sys()).context(format!(
331 "In rebind_alias: Domain {:?} must be either App or SELinux.",
332 domain
333 ));
Joel Galenson33c04ad2020-08-03 11:04:38 -0700334 }
335 }
336 let tx = self
337 .conn
338 .transaction_with_behavior(TransactionBehavior::Immediate)
Janis Danisevskis63f7bc82020-09-03 10:12:56 -0700339 .context("In rebind_alias: Failed to initialize transaction.")?;
Joel Galenson33c04ad2020-08-03 11:04:38 -0700340 tx.execute(
341 "UPDATE persistent.keyentry
342 SET alias = NULL, domain = NULL, namespace = NULL
343 WHERE alias = ? AND domain = ? AND namespace = ?;",
344 params![alias, domain as i64, namespace],
345 )
Janis Danisevskis63f7bc82020-09-03 10:12:56 -0700346 .context("In rebind_alias: Failed to rebind existing entry.")?;
Joel Galenson33c04ad2020-08-03 11:04:38 -0700347 let result = tx
348 .execute(
349 "UPDATE persistent.keyentry
Janis Danisevskis63f7bc82020-09-03 10:12:56 -0700350 SET alias = ?
351 WHERE id = ? AND domain = ? AND namespace = ?;",
Joel Galenson33c04ad2020-08-03 11:04:38 -0700352 params![alias, newid, domain as i64, namespace],
353 )
Janis Danisevskis63f7bc82020-09-03 10:12:56 -0700354 .context("In rebind_alias: Failed to set alias.")?;
Joel Galenson33c04ad2020-08-03 11:04:38 -0700355 if result != 1 {
356 // Note that this explicit rollback is not required, as
357 // the transaction should rollback if we do not commit it.
358 // We leave it here for readability.
Janis Danisevskis63f7bc82020-09-03 10:12:56 -0700359 tx.rollback().context("In rebind_alias: Failed to rollback a failed transaction.")?;
Joel Galenson33c04ad2020-08-03 11:04:38 -0700360 return Err(KsError::sys()).context(format!(
Janis Danisevskis63f7bc82020-09-03 10:12:56 -0700361 "In rebind_alias: Expected to update a single entry but instead updated {}.",
Joel Galenson33c04ad2020-08-03 11:04:38 -0700362 result
363 ));
364 }
Janis Danisevskis63f7bc82020-09-03 10:12:56 -0700365 tx.commit().context("In rebind_alias: Failed to commit transaction.")
366 }
367
368 // Helper function loading the key_id given the key descriptor
369 // tuple comprising domain, namespace, and alias.
370 // Requires a valid transaction.
371 fn load_key_entry_id(key: &KeyDescriptor, tx: &Transaction) -> Result<i64> {
372 let alias = key
373 .alias
374 .as_ref()
375 .map_or_else(|| Err(KsError::sys()), Ok)
376 .context("In load_key_entry_id: Alias must be specified.")?;
377 let mut stmt = tx
378 .prepare(
379 "SELECT id FROM persistent.keyentry
380 WHERE
381 domain = ?
382 AND namespace = ?
383 AND alias = ?;",
384 )
385 .context("In load_key_entry_id: Failed to select from keyentry table.")?;
386 let mut rows = stmt
387 .query(params![key.domain, key.namespace_, alias])
388 .context("In load_key_entry_id: Failed to read from keyentry table.")?;
389 Self::with_rows_extract_one(&mut rows, |row| {
390 row.map_or_else(|| Err(KsError::Rc(error::Rc::KeyNotFound)), Ok)?
391 .get(0)
392 .context("Failed to unpack id.")
393 })
394 .context("In load_key_entry_id.")
395 }
396
397 /// This helper function completes the access tuple of a key, which is required
398 /// to perform access control. The strategy depends on the `domain` field in the
399 /// key descriptor.
400 /// * Domain::SELinux: The access tuple is complete and this function only loads
401 /// the key_id for further processing.
402 /// * Domain::App: Like Domain::SELinux, but the tuple is completed by `caller_uid`
403 /// which serves as the namespace.
404 /// * Domain::Grant: The grant table is queried for the `key_id` and the
405 /// `access_vector`.
406 /// * Domain::KeyId: The keyentry table is queried for the owning `domain` and
407 /// `namespace`.
408 /// In each case the information returned is sufficient to perform the access
409 /// check and the key id can be used to load further key artifacts.
410 fn load_access_tuple(
411 tx: &Transaction,
412 key: KeyDescriptor,
413 caller_uid: u32,
414 ) -> Result<(i64, KeyDescriptor, Option<KeyPermSet>)> {
415 match key.domain {
416 // Domain App or SELinux. In this case we load the key_id from
417 // the keyentry database for further loading of key components.
418 // We already have the full access tuple to perform access control.
419 // The only distinction is that we use the caller_uid instead
420 // of the caller supplied namespace if the domain field is
421 // Domain::App.
422 Domain::App | Domain::SELinux => {
423 let mut access_key = key;
424 if access_key.domain == Domain::App {
425 access_key.namespace_ = caller_uid as i64;
426 }
427 let key_id = Self::load_key_entry_id(&access_key, &tx)
428 .with_context(|| format!("With key.domain = {}.", access_key.domain))?;
429
430 Ok((key_id, access_key, None))
431 }
432
433 // Domain::Grant. In this case we load the key_id and the access_vector
434 // from the grant table.
435 Domain::Grant => {
436 let mut stmt = tx
437 .prepare(
438 "SELECT keyentryid, access_vector FROM perboot.grant
439 WHERE grantee = ? AND id = ?;",
440 )
441 .context("Domain::Grant prepare statement failed")?;
442 let mut rows = stmt
443 .query(params![caller_uid as i64, key.namespace_])
444 .context("Domain:Grant: query failed.")?;
445 let (key_id, access_vector): (i64, i32) =
446 Self::with_rows_extract_one(&mut rows, |row| {
447 let r = row.map_or_else(|| Err(KsError::Rc(error::Rc::KeyNotFound)), Ok)?;
448 Ok((
449 r.get(0).context("Failed to unpack key_id.")?,
450 r.get(1).context("Failed to unpack access_vector.")?,
451 ))
452 })
453 .context("Domain::Grant.")?;
454 Ok((key_id, key, Some(access_vector.into())))
455 }
456
457 // Domain::KeyId. In this case we load the domain and namespace from the
458 // keyentry database because we need them for access control.
459 Domain::KeyId => {
460 let mut stmt = tx
461 .prepare(
462 "SELECT domain, namespace FROM persistent.keyentry
463 WHERE
464 id = ?;",
465 )
466 .context("Domain::KeyId: prepare statement failed")?;
467 let mut rows =
468 stmt.query(params![key.namespace_]).context("Domain::KeyId: query failed.")?;
469 let (domain, namespace): (DomainType, i64) =
470 Self::with_rows_extract_one(&mut rows, |row| {
471 let r = row.map_or_else(|| Err(KsError::Rc(error::Rc::KeyNotFound)), Ok)?;
472 Ok((
473 r.get(0).context("Failed to unpack domain.")?,
474 r.get(1).context("Failed to unpack namespace.")?,
475 ))
476 })
477 .context("Domain::KeyId.")?;
478 let key_id = key.namespace_;
479 let mut access_key = key;
480 access_key.domain = domain;
481 access_key.namespace_ = namespace;
482
483 Ok((key_id, access_key, None))
484 }
485 _ => Err(anyhow!(KsError::sys())),
486 }
487 }
488
489 /// Load a key entry by the given key descriptor.
490 /// It uses the `check_permission` callback to verify if the access is allowed
491 /// given the key access tuple read from the database using `load_access_tuple`.
492 /// With `load_bits` the caller may specify which blobs shall be loaded from
493 /// the blob database.
494 pub fn load_key_entry(
495 &mut self,
496 key: KeyDescriptor,
497 load_bits: KeyEntryLoadBits,
498 caller_uid: u32,
499 check_permission: impl FnOnce(&KeyDescriptor, Option<KeyPermSet>) -> Result<()>,
500 ) -> Result<KeyEntry> {
501 let tx = self
502 .conn
503 .transaction_with_behavior(TransactionBehavior::Deferred)
504 .context("In load_key_entry: Failed to initialize transaction.")?;
505
506 // Load the key_id and complete the access control tuple.
507 let (key_id, access_key_descriptor, access_vector) =
508 Self::load_access_tuple(&tx, key, caller_uid).context("In load_key_entry:")?;
509
510 // Perform access control. It is vital that we return here if the permission is denied.
511 // So do not touch that '?' at the end.
512 check_permission(&access_key_descriptor, access_vector).context("In load_key_entry")?;
513
514 let mut result =
515 KeyEntry { id: key_id, km_blob: None, cert: None, cert_chain: None, sec_level: 0 };
516
517 let mut stmt = tx
518 .prepare(
519 "SELECT MAX(id), sec_level, subcomponent_type, blob FROM persistent.blobentry
520 WHERE keyentryid = ? GROUP BY subcomponent_type;",
521 )
522 .context("In load_key_entry: blobentry: prepare statement failed.")?;
523
524 let mut rows =
525 stmt.query(params![key_id]).context("In load_key_entry: blobentry: query failed.")?;
526 Self::with_rows_extract_all(&mut rows, |row| {
527 let sub_type: SubComponentType =
528 row.get(2).context("Failed to extract subcomponent_type.")?;
529 match (sub_type, load_bits.load_public()) {
530 (SubComponentType::KM_BLOB, _) => {
531 result.sec_level = row.get(1).context("Failed to extract security level.")?;
532 if load_bits.load_km() {
533 result.km_blob = Some(row.get(3).context("Failed to extract KM blob.")?);
534 }
535 }
536 (SubComponentType::CERT, true) => {
537 result.cert =
538 Some(row.get(3).context("Failed to extract public certificate blob.")?);
539 }
540 (SubComponentType::CERT_CHAIN, true) => {
541 result.cert_chain =
542 Some(row.get(3).context("Failed to extract certificate chain blob.")?);
543 }
544 (SubComponentType::CERT, _) | (SubComponentType::CERT_CHAIN, _) => {}
545 _ => Err(KsError::sys()).context("Unknown subcomponent type.")?,
546 }
547 Ok(())
548 })
549 .context("In load_key_entry")?;
550
551 // TODO load key parameters.
552
553 Ok(result)
554 }
555
556 /// Adds a grant to the grant table.
557 /// Like `load_key_entry` this function loads the access tuple before
558 /// it uses the callback for a permission check. Upon success,
559 /// it inserts the `grantee_uid`, `key_id`, and `access_vector` into the
560 /// grant table. The new row will have a randomized id, which is used as
561 /// grant id in the namespace field of the resulting KeyDescriptor.
562 pub fn grant(
563 &mut self,
564 key: KeyDescriptor,
565 caller_uid: u32,
566 grantee_uid: u32,
567 access_vector: KeyPermSet,
568 check_permission: impl FnOnce(&KeyDescriptor, &KeyPermSet) -> Result<()>,
569 ) -> Result<KeyDescriptor> {
570 let tx = self
571 .conn
572 .transaction_with_behavior(TransactionBehavior::Immediate)
573 .context("In grant: Failed to initialize transaction.")?;
574
575 // Load the key_id and complete the access control tuple.
576 // We ignore the access vector here because grants cannot be granted.
577 // The access vector returned here expresses the permissions the
578 // grantee has if key.domain == Domain::Grant. But this vector
579 // cannot include the grant permission by design, so there is no way the
580 // subsequent permission check can pass.
581 // We could check key.domain == Domain::Grant and fail early.
582 // But even if we load the access tuple by grant here, the permission
583 // check denies the attempt to create a grant by grant descriptor.
584 let (key_id, access_key_descriptor, _) =
585 Self::load_access_tuple(&tx, key, caller_uid).context("In grant")?;
586
587 // Perform access control. It is vital that we return here if the permission
588 // was denied. So do not touch that '?' at the end of the line.
589 // This permission check checks if the caller has the grant permission
590 // for the given key and in addition to all of the permissions
591 // expressed in `access_vector`.
592 check_permission(&access_key_descriptor, &access_vector)
593 .context("In grant: check_permission failed.")?;
594
595 let grant_id = if let Some(grant_id) = tx
596 .query_row(
597 "SELECT id FROM perboot.grant
598 WHERE keyentryid = ? AND grantee = ?;",
599 params![key_id, grantee_uid],
600 |row| row.get(0),
601 )
602 .optional()
603 .context("In grant: Failed get optional existing grant id.")?
604 {
605 tx.execute(
606 "UPDATE perboot.grant
607 SET access_vector = ?
608 WHERE id = ?;",
609 params![i32::from(access_vector), grant_id],
610 )
611 .context("In grant: Failed to update existing grant.")?;
612 grant_id
613 } else {
614 loop {
615 let newid: i64 = random();
616 let ret = tx.execute(
617 "INSERT INTO perboot.grant (id, grantee, keyentryid, access_vector)
618 VALUES (?, ?, ?, ?);",
619 params![newid, grantee_uid, key_id, i32::from(access_vector)],
620 );
621 match ret {
622 // If the id already existed, try again.
623 Err(rusqlite::Error::SqliteFailure(
624 libsqlite3_sys::Error {
625 code: libsqlite3_sys::ErrorCode::ConstraintViolation,
626 extended_code: libsqlite3_sys::SQLITE_CONSTRAINT_UNIQUE,
627 },
628 _,
629 )) => (),
630 Err(e) => return Err(e).context("Failed to insert grant."),
631 Ok(_) => break newid,
632 }
633 }
634 };
635 tx.commit().context("In grant: failed to commit transaction.")?;
636
637 Ok(KeyDescriptor { domain: Domain::Grant, namespace_: grant_id, alias: None, blob: None })
638 }
639
640 /// This function checks permissions like `grant` and `load_key_entry`
641 /// before removing a grant from the grant table.
642 pub fn ungrant(
643 &mut self,
644 key: KeyDescriptor,
645 caller_uid: u32,
646 grantee_uid: u32,
647 check_permission: impl FnOnce(&KeyDescriptor) -> Result<()>,
648 ) -> Result<()> {
649 let tx = self
650 .conn
651 .transaction_with_behavior(TransactionBehavior::Immediate)
652 .context("In ungrant: Failed to initialize transaction.")?;
653
654 // Load the key_id and complete the access control tuple.
655 // We ignore the access vector here because grants cannot be granted.
656 let (key_id, access_key_descriptor, _) =
657 Self::load_access_tuple(&tx, key, caller_uid).context("In ungrant.")?;
658
659 // Perform access control. We must return here if the permission
660 // was denied. So do not touch the '?' at the end of this line.
661 check_permission(&access_key_descriptor).context("In grant: check_permission failed.")?;
662
663 tx.execute(
664 "DELETE FROM perboot.grant
665 WHERE keyentryid = ? AND grantee = ?;",
666 params![key_id, grantee_uid],
667 )
668 .context("Failed to delete grant.")?;
669
670 tx.commit().context("In ungrant: failed to commit transaction.")?;
671
672 Ok(())
673 }
674
675 // Takes Rows as returned by a query call on prepared statement.
676 // Extracts exactly one row with the `row_extractor` and fails if more
677 // rows are available.
678 // If no row was found, `None` is passed to the `row_extractor`.
679 // This allows the row extractor to decide on an error condition or
680 // a different default behavior.
681 fn with_rows_extract_one<'a, T, F>(rows: &mut Rows<'a>, row_extractor: F) -> Result<T>
682 where
683 F: FnOnce(Option<&Row<'a>>) -> Result<T>,
684 {
685 let result =
686 row_extractor(rows.next().context("with_rows_extract_one: Failed to unpack row.")?);
687
688 rows.next()
689 .context("In with_rows_extract_one: Failed to unpack unexpected row.")?
690 .map_or_else(|| Ok(()), |_| Err(KsError::sys()))
691 .context("In with_rows_extract_one: Unexpected row.")?;
692
693 result
694 }
695
696 fn with_rows_extract_all<'a, F>(rows: &mut Rows<'a>, mut row_extractor: F) -> Result<()>
697 where
698 F: FnMut(&Row<'a>) -> Result<()>,
699 {
700 loop {
701 match rows.next().context("In with_rows_extract_all: Failed to unpack row")? {
702 Some(row) => {
703 row_extractor(&row).context("In with_rows_extract_all.")?;
704 }
705 None => break Ok(()),
706 }
707 }
Joel Galenson33c04ad2020-08-03 11:04:38 -0700708 }
Joel Galenson26f4d012020-07-17 14:57:21 -0700709}
710
711#[cfg(test)]
712mod tests {
713
714 use super::*;
Janis Danisevskis63f7bc82020-09-03 10:12:56 -0700715 use crate::key_perm_set;
716 use crate::permission::{KeyPerm, KeyPermSet};
717 use rusqlite::NO_PARAMS;
Joel Galenson0891bc12020-07-20 10:37:03 -0700718 use std::cell::RefCell;
719
Janis Danisevskis4df44f42020-08-26 14:40:03 -0700720 static PERSISTENT_TEST_SQL: &str = "/data/local/tmp/persistent.sqlite";
721 static PERBOOT_TEST_SQL: &str = "/data/local/tmp/perboot.sqlite";
722
723 fn new_test_db() -> Result<KeystoreDB> {
724 let conn = KeystoreDB::make_connection("file::memory:", "file::memory:")?;
725
726 KeystoreDB::init_tables(&conn).context("Failed to initialize tables.")?;
727 Ok(KeystoreDB { conn })
728 }
729
730 fn new_test_db_with_persistent_file() -> Result<KeystoreDB> {
731 let conn = KeystoreDB::make_connection(PERSISTENT_TEST_SQL, PERBOOT_TEST_SQL)?;
732
733 KeystoreDB::init_tables(&conn).context("Failed to initialize tables.")?;
734 Ok(KeystoreDB { conn })
735 }
736
Joel Galenson0891bc12020-07-20 10:37:03 -0700737 // Ensure that we're using the "injected" random function, not the real one.
738 #[test]
739 fn test_mocked_random() {
740 let rand1 = random();
741 let rand2 = random();
742 let rand3 = random();
743 if rand1 == rand2 {
744 assert_eq!(rand2 + 1, rand3);
745 } else {
746 assert_eq!(rand1 + 1, rand2);
747 assert_eq!(rand2, rand3);
748 }
749 }
Joel Galenson26f4d012020-07-17 14:57:21 -0700750
Joel Galenson26f4d012020-07-17 14:57:21 -0700751 // Test that we have the correct tables.
752 #[test]
753 fn test_tables() -> Result<()> {
Janis Danisevskis4df44f42020-08-26 14:40:03 -0700754 let db = new_test_db()?;
Joel Galenson26f4d012020-07-17 14:57:21 -0700755 let tables = db
756 .conn
Joel Galenson2aab4432020-07-22 15:27:57 -0700757 .prepare("SELECT name from persistent.sqlite_master WHERE type='table' ORDER BY name;")?
Joel Galenson26f4d012020-07-17 14:57:21 -0700758 .query_map(params![], |row| row.get(0))?
759 .collect::<rusqlite::Result<Vec<String>>>()?;
Janis Danisevskis63f7bc82020-09-03 10:12:56 -0700760 assert_eq!(tables.len(), 3);
761 assert_eq!(tables[0], "blobentry");
762 assert_eq!(tables[1], "keyentry");
763 assert_eq!(tables[2], "keyparameter");
764 let tables = db
765 .conn
766 .prepare("SELECT name from perboot.sqlite_master WHERE type='table' ORDER BY name;")?
767 .query_map(params![], |row| row.get(0))?
768 .collect::<rusqlite::Result<Vec<String>>>()?;
769 assert_eq!(tables.len(), 1);
770 assert_eq!(tables[0], "grant");
Joel Galenson26f4d012020-07-17 14:57:21 -0700771 Ok(())
772 }
Joel Galenson0891bc12020-07-20 10:37:03 -0700773
774 #[test]
Joel Galenson2aab4432020-07-22 15:27:57 -0700775 fn test_no_persistence_for_tests() -> Result<()> {
Janis Danisevskis4df44f42020-08-26 14:40:03 -0700776 let db = new_test_db()?;
Joel Galenson2aab4432020-07-22 15:27:57 -0700777
Janis Danisevskis60400fe2020-08-26 15:24:42 -0700778 db.create_key_entry(Domain::App, 100)?;
Joel Galenson2aab4432020-07-22 15:27:57 -0700779 let entries = get_keyentry(&db)?;
780 assert_eq!(entries.len(), 1);
Janis Danisevskis4df44f42020-08-26 14:40:03 -0700781 let db = new_test_db()?;
Joel Galenson2aab4432020-07-22 15:27:57 -0700782
783 let entries = get_keyentry(&db)?;
784 assert_eq!(entries.len(), 0);
785 Ok(())
786 }
787
788 #[test]
789 fn test_persistence_for_files() -> Result<()> {
Janis Danisevskis4df44f42020-08-26 14:40:03 -0700790 let _file_guard_persistent = TempFile { filename: PERSISTENT_TEST_SQL };
791 let _file_guard_perboot = TempFile { filename: PERBOOT_TEST_SQL };
792 let db = new_test_db_with_persistent_file()?;
Joel Galenson2aab4432020-07-22 15:27:57 -0700793
Janis Danisevskis60400fe2020-08-26 15:24:42 -0700794 db.create_key_entry(Domain::App, 100)?;
Joel Galenson2aab4432020-07-22 15:27:57 -0700795 let entries = get_keyentry(&db)?;
796 assert_eq!(entries.len(), 1);
Janis Danisevskis4df44f42020-08-26 14:40:03 -0700797 let db = new_test_db_with_persistent_file()?;
Joel Galenson2aab4432020-07-22 15:27:57 -0700798
799 let entries_new = get_keyentry(&db)?;
800 assert_eq!(entries, entries_new);
801 Ok(())
802 }
803
804 #[test]
Joel Galenson0891bc12020-07-20 10:37:03 -0700805 fn test_create_key_entry() -> Result<()> {
Janis Danisevskis60400fe2020-08-26 15:24:42 -0700806 fn extractor(ke: &KeyEntryRow) -> (DomainType, i64, Option<&str>) {
Joel Galenson0891bc12020-07-20 10:37:03 -0700807 (ke.domain.unwrap(), ke.namespace.unwrap(), ke.alias.as_deref())
808 }
809
Janis Danisevskis4df44f42020-08-26 14:40:03 -0700810 let db = new_test_db()?;
Joel Galenson0891bc12020-07-20 10:37:03 -0700811
812 db.create_key_entry(Domain::App, 100)?;
813 db.create_key_entry(Domain::SELinux, 101)?;
814
815 let entries = get_keyentry(&db)?;
816 assert_eq!(entries.len(), 2);
817 assert_eq!(extractor(&entries[0]), (Domain::App, 100, None));
818 assert_eq!(extractor(&entries[1]), (Domain::SELinux, 101, None));
819
820 // Test that we must pass in a valid Domain.
821 check_result_is_error_containing_string(
822 db.create_key_entry(Domain::Grant, 102),
Janis Danisevskis60400fe2020-08-26 15:24:42 -0700823 "Domain 1 must be either App or SELinux.",
Joel Galenson0891bc12020-07-20 10:37:03 -0700824 );
825 check_result_is_error_containing_string(
826 db.create_key_entry(Domain::Blob, 103),
Janis Danisevskis60400fe2020-08-26 15:24:42 -0700827 "Domain 3 must be either App or SELinux.",
Joel Galenson0891bc12020-07-20 10:37:03 -0700828 );
829 check_result_is_error_containing_string(
830 db.create_key_entry(Domain::KeyId, 104),
Janis Danisevskis60400fe2020-08-26 15:24:42 -0700831 "Domain 4 must be either App or SELinux.",
Joel Galenson0891bc12020-07-20 10:37:03 -0700832 );
833
834 Ok(())
835 }
836
Joel Galenson33c04ad2020-08-03 11:04:38 -0700837 #[test]
838 fn test_rebind_alias() -> Result<()> {
Janis Danisevskis60400fe2020-08-26 15:24:42 -0700839 fn extractor(ke: &KeyEntryRow) -> (Option<DomainType>, Option<i64>, Option<&str>) {
Joel Galenson33c04ad2020-08-03 11:04:38 -0700840 (ke.domain, ke.namespace, ke.alias.as_deref())
841 }
842
Janis Danisevskis4df44f42020-08-26 14:40:03 -0700843 let mut db = new_test_db()?;
Joel Galenson33c04ad2020-08-03 11:04:38 -0700844 db.create_key_entry(Domain::App, 42)?;
845 db.create_key_entry(Domain::App, 42)?;
846 let entries = get_keyentry(&db)?;
847 assert_eq!(entries.len(), 2);
848 assert_eq!(extractor(&entries[0]), (Some(Domain::App), Some(42), None));
849 assert_eq!(extractor(&entries[1]), (Some(Domain::App), Some(42), None));
850
851 // Test that the first call to rebind_alias sets the alias.
852 db.rebind_alias(entries[0].id, "foo", Domain::App, 42)?;
853 let entries = get_keyentry(&db)?;
854 assert_eq!(entries.len(), 2);
855 assert_eq!(extractor(&entries[0]), (Some(Domain::App), Some(42), Some("foo")));
856 assert_eq!(extractor(&entries[1]), (Some(Domain::App), Some(42), None));
857
858 // Test that the second call to rebind_alias also empties the old one.
859 db.rebind_alias(entries[1].id, "foo", Domain::App, 42)?;
860 let entries = get_keyentry(&db)?;
861 assert_eq!(entries.len(), 2);
862 assert_eq!(extractor(&entries[0]), (None, None, None));
863 assert_eq!(extractor(&entries[1]), (Some(Domain::App), Some(42), Some("foo")));
864
865 // Test that we must pass in a valid Domain.
866 check_result_is_error_containing_string(
867 db.rebind_alias(0, "foo", Domain::Grant, 42),
Janis Danisevskis60400fe2020-08-26 15:24:42 -0700868 "Domain 1 must be either App or SELinux.",
Joel Galenson33c04ad2020-08-03 11:04:38 -0700869 );
870 check_result_is_error_containing_string(
871 db.rebind_alias(0, "foo", Domain::Blob, 42),
Janis Danisevskis60400fe2020-08-26 15:24:42 -0700872 "Domain 3 must be either App or SELinux.",
Joel Galenson33c04ad2020-08-03 11:04:38 -0700873 );
874 check_result_is_error_containing_string(
875 db.rebind_alias(0, "foo", Domain::KeyId, 42),
Janis Danisevskis60400fe2020-08-26 15:24:42 -0700876 "Domain 4 must be either App or SELinux.",
Joel Galenson33c04ad2020-08-03 11:04:38 -0700877 );
878
879 // Test that we correctly handle setting an alias for something that does not exist.
880 check_result_is_error_containing_string(
881 db.rebind_alias(0, "foo", Domain::SELinux, 42),
882 "Expected to update a single entry but instead updated 0",
883 );
884 // Test that we correctly abort the transaction in this case.
885 let entries = get_keyentry(&db)?;
886 assert_eq!(entries.len(), 2);
887 assert_eq!(extractor(&entries[0]), (None, None, None));
888 assert_eq!(extractor(&entries[1]), (Some(Domain::App), Some(42), Some("foo")));
889
890 Ok(())
891 }
892
Janis Danisevskis63f7bc82020-09-03 10:12:56 -0700893 #[test]
894 fn test_grant_ungrant() -> Result<()> {
895 const CALLER_UID: u32 = 15;
896 const GRANTEE_UID: u32 = 12;
897 const SELINUX_NAMESPACE: i64 = 7;
898
899 let mut db = new_test_db()?;
900 db.conn.execute(
901 "INSERT INTO persistent.keyentry (id, creation_date, domain, namespace, alias)
902 VALUES (1, '1980', 0, 15, 'key'), (2, '1980', 2, 7, 'yek');",
903 NO_PARAMS,
904 )?;
905 let app_key = KeyDescriptor {
906 domain: super::Domain::App,
907 namespace_: 0,
908 alias: Some("key".to_string()),
909 blob: None,
910 };
911 const PVEC1: KeyPermSet = key_perm_set![KeyPerm::use_(), KeyPerm::get_info()];
912 const PVEC2: KeyPermSet = key_perm_set![KeyPerm::use_()];
913
914 // Reset totally predictable random number generator in case we
915 // are not the first test running on this thread.
916 reset_random();
917 let next_random = 0i64;
918
919 let app_granted_key =
920 db.grant(app_key.clone(), CALLER_UID, GRANTEE_UID, PVEC1, |k, a| {
921 assert_eq!(*a, PVEC1);
922 assert_eq!(
923 *k,
924 KeyDescriptor {
925 domain: super::Domain::App,
926 // namespace must be set to the caller_uid.
927 namespace_: CALLER_UID as i64,
928 alias: Some("key".to_string()),
929 blob: None,
930 }
931 );
932 Ok(())
933 })?;
934
935 assert_eq!(
936 app_granted_key,
937 KeyDescriptor {
938 domain: super::Domain::Grant,
939 // The grantid is next_random due to the mock random number generator.
940 namespace_: next_random,
941 alias: None,
942 blob: None,
943 }
944 );
945
946 let selinux_key = KeyDescriptor {
947 domain: super::Domain::SELinux,
948 namespace_: SELINUX_NAMESPACE,
949 alias: Some("yek".to_string()),
950 blob: None,
951 };
952
953 let selinux_granted_key =
954 db.grant(selinux_key.clone(), CALLER_UID, 12, PVEC1, |k, a| {
955 assert_eq!(*a, PVEC1);
956 assert_eq!(
957 *k,
958 KeyDescriptor {
959 domain: super::Domain::SELinux,
960 // namespace must be the supplied SELinux
961 // namespace.
962 namespace_: SELINUX_NAMESPACE,
963 alias: Some("yek".to_string()),
964 blob: None,
965 }
966 );
967 Ok(())
968 })?;
969
970 assert_eq!(
971 selinux_granted_key,
972 KeyDescriptor {
973 domain: super::Domain::Grant,
974 // The grantid is next_random + 1 due to the mock random number generator.
975 namespace_: next_random + 1,
976 alias: None,
977 blob: None,
978 }
979 );
980
981 // This should update the existing grant with PVEC2.
982 let selinux_granted_key =
983 db.grant(selinux_key.clone(), CALLER_UID, 12, PVEC2, |k, a| {
984 assert_eq!(*a, PVEC2);
985 assert_eq!(
986 *k,
987 KeyDescriptor {
988 domain: super::Domain::SELinux,
989 // namespace must be the supplied SELinux
990 // namespace.
991 namespace_: SELINUX_NAMESPACE,
992 alias: Some("yek".to_string()),
993 blob: None,
994 }
995 );
996 Ok(())
997 })?;
998
999 assert_eq!(
1000 selinux_granted_key,
1001 KeyDescriptor {
1002 domain: super::Domain::Grant,
1003 // Same grant id as before. The entry was only updated.
1004 namespace_: next_random + 1,
1005 alias: None,
1006 blob: None,
1007 }
1008 );
1009
1010 {
1011 // Limiting scope of stmt, because it borrows db.
1012 let mut stmt = db
1013 .conn
1014 .prepare("SELECT id, grantee, keyentryid, access_vector FROM perboot.grant;")?;
1015 let mut rows = stmt.query_map::<(i64, u32, i64, i32), _, _>(NO_PARAMS, |row| {
1016 Ok((row.get(0)?, row.get(1)?, row.get(2)?, row.get(3)?))
1017 })?;
1018
1019 let r = rows.next().unwrap().unwrap();
1020 assert_eq!(r, (next_random, GRANTEE_UID, 1, 516));
1021 let r = rows.next().unwrap().unwrap();
1022 assert_eq!(r, (next_random + 1, GRANTEE_UID, 2, 512));
1023 assert!(rows.next().is_none());
1024 }
1025
1026 debug_dump_keyentry_table(&mut db)?;
1027 println!("app_key {:?}", app_key);
1028 println!("selinux_key {:?}", selinux_key);
1029
1030 db.ungrant(app_key, CALLER_UID, GRANTEE_UID, |_| Ok(()))?;
1031 db.ungrant(selinux_key, CALLER_UID, GRANTEE_UID, |_| Ok(()))?;
1032
1033 Ok(())
1034 }
1035
1036 static TEST_KM_BLOB: &[u8] = b"my test blob";
1037 static TEST_CERT_BLOB: &[u8] = b"my test cert";
1038 static TEST_CERT_CHAIN_BLOB: &[u8] = b"my test cert_chain";
1039
1040 #[test]
1041 fn test_insert_blob() -> Result<()> {
1042 let mut db = new_test_db()?;
1043 db.insert_blob(1, SubComponentType::KM_BLOB, TEST_KM_BLOB, 1)?;
1044 db.insert_blob(1, SubComponentType::CERT, TEST_CERT_BLOB, 2)?;
1045 db.insert_blob(1, SubComponentType::CERT_CHAIN, TEST_CERT_CHAIN_BLOB, 3)?;
1046
1047 let mut stmt = db.conn.prepare(
1048 "SELECT subcomponent_type, keyentryid, blob, sec_level FROM persistent.blobentry
1049 ORDER BY sec_level ASC;",
1050 )?;
1051 let mut rows = stmt
1052 .query_map::<(SubComponentType, i64, Vec<u8>, i64), _, _>(NO_PARAMS, |row| {
1053 Ok((row.get(0)?, row.get(1)?, row.get(2)?, row.get(3)?))
1054 })?;
1055 let r = rows.next().unwrap().unwrap();
1056 assert_eq!(r, (SubComponentType::KM_BLOB, 1, TEST_KM_BLOB.to_vec(), 1));
1057 let r = rows.next().unwrap().unwrap();
1058 assert_eq!(r, (SubComponentType::CERT, 1, TEST_CERT_BLOB.to_vec(), 2));
1059 let r = rows.next().unwrap().unwrap();
1060 assert_eq!(r, (SubComponentType::CERT_CHAIN, 1, TEST_CERT_CHAIN_BLOB.to_vec(), 3));
1061
1062 Ok(())
1063 }
1064
1065 static TEST_ALIAS: &str = "my super duper key";
1066
1067 #[test]
1068 fn test_insert_and_load_full_keyentry_domain_app() -> Result<()> {
1069 let mut db = new_test_db()?;
1070 let key_id = make_test_key_entry(&mut db, Domain::App, 1, TEST_ALIAS)
1071 .context("test_insert_and_load_full_keyentry_domain_app")?;
1072 let key_entry = db.load_key_entry(
1073 KeyDescriptor {
1074 domain: Domain::App,
1075 namespace_: 0,
1076 alias: Some(TEST_ALIAS.to_string()),
1077 blob: None,
1078 },
1079 KeyEntryLoadBits::BOTH,
1080 1,
1081 |_k, _av| Ok(()),
1082 )?;
1083 assert_eq!(
1084 key_entry,
1085 KeyEntry {
1086 id: key_id,
1087 km_blob: Some(TEST_KM_BLOB.to_vec()),
1088 cert: Some(TEST_CERT_BLOB.to_vec()),
1089 cert_chain: Some(TEST_CERT_CHAIN_BLOB.to_vec()),
1090 sec_level: 1,
1091 }
1092 );
1093 Ok(())
1094 }
1095
1096 #[test]
1097 fn test_insert_and_load_full_keyentry_domain_selinux() -> Result<()> {
1098 let mut db = new_test_db()?;
1099 let key_id = make_test_key_entry(&mut db, Domain::SELinux, 1, TEST_ALIAS)
1100 .context("test_insert_and_load_full_keyentry_domain_selinux")?;
1101 let key_entry = db.load_key_entry(
1102 KeyDescriptor {
1103 domain: Domain::SELinux,
1104 namespace_: 1,
1105 alias: Some(TEST_ALIAS.to_string()),
1106 blob: None,
1107 },
1108 KeyEntryLoadBits::BOTH,
1109 1,
1110 |_k, _av| Ok(()),
1111 )?;
1112 assert_eq!(
1113 key_entry,
1114 KeyEntry {
1115 id: key_id,
1116 km_blob: Some(TEST_KM_BLOB.to_vec()),
1117 cert: Some(TEST_CERT_BLOB.to_vec()),
1118 cert_chain: Some(TEST_CERT_CHAIN_BLOB.to_vec()),
1119 sec_level: 1,
1120 }
1121 );
1122 Ok(())
1123 }
1124
1125 #[test]
1126 fn test_insert_and_load_full_keyentry_domain_key_id() -> Result<()> {
1127 let mut db = new_test_db()?;
1128 let key_id = make_test_key_entry(&mut db, Domain::SELinux, 1, TEST_ALIAS)
1129 .context("test_insert_and_load_full_keyentry_domain_key_id")?;
1130 let key_entry = db.load_key_entry(
1131 KeyDescriptor { domain: Domain::KeyId, namespace_: key_id, alias: None, blob: None },
1132 KeyEntryLoadBits::BOTH,
1133 1,
1134 |_k, _av| Ok(()),
1135 )?;
1136 assert_eq!(
1137 key_entry,
1138 KeyEntry {
1139 id: key_id,
1140 km_blob: Some(TEST_KM_BLOB.to_vec()),
1141 cert: Some(TEST_CERT_BLOB.to_vec()),
1142 cert_chain: Some(TEST_CERT_CHAIN_BLOB.to_vec()),
1143 sec_level: 1,
1144 }
1145 );
1146
1147 Ok(())
1148 }
1149
1150 #[test]
1151 fn test_insert_and_load_full_keyentry_from_grant() -> Result<()> {
1152 let mut db = new_test_db()?;
1153 let key_id = make_test_key_entry(&mut db, Domain::App, 1, TEST_ALIAS)
1154 .context("test_insert_and_load_full_keyentry_from_grant")?;
1155
1156 let granted_key = db.grant(
1157 KeyDescriptor {
1158 domain: Domain::App,
1159 namespace_: 0,
1160 alias: Some(TEST_ALIAS.to_string()),
1161 blob: None,
1162 },
1163 1,
1164 2,
1165 key_perm_set![KeyPerm::use_()],
1166 |_k, _av| Ok(()),
1167 )?;
1168
1169 debug_dump_grant_table(&mut db)?;
1170
1171 let key_entry = db.load_key_entry(granted_key, KeyEntryLoadBits::BOTH, 2, |k, av| {
1172 assert_eq!(Domain::Grant, k.domain);
1173 assert!(av.unwrap().includes(KeyPerm::use_()));
1174 Ok(())
1175 })?;
1176
1177 assert_eq!(
1178 key_entry,
1179 KeyEntry {
1180 id: key_id,
1181 km_blob: Some(TEST_KM_BLOB.to_vec()),
1182 cert: Some(TEST_CERT_BLOB.to_vec()),
1183 cert_chain: Some(TEST_CERT_CHAIN_BLOB.to_vec()),
1184 sec_level: 1,
1185 }
1186 );
1187 Ok(())
1188 }
1189
Joel Galenson0891bc12020-07-20 10:37:03 -07001190 // Helpers
1191
1192 // Checks that the given result is an error containing the given string.
1193 fn check_result_is_error_containing_string<T>(result: Result<T>, target: &str) {
1194 let error_str = format!(
1195 "{:#?}",
1196 result.err().unwrap_or_else(|| panic!("Expected the error: {}", target))
1197 );
1198 assert!(
1199 error_str.contains(target),
1200 "The string \"{}\" should contain \"{}\"",
1201 error_str,
1202 target
1203 );
1204 }
1205
Joel Galenson2aab4432020-07-22 15:27:57 -07001206 #[derive(Debug, PartialEq)]
Joel Galenson0891bc12020-07-20 10:37:03 -07001207 #[allow(dead_code)]
1208 struct KeyEntryRow {
Janis Danisevskis63f7bc82020-09-03 10:12:56 -07001209 id: i64,
Joel Galenson0891bc12020-07-20 10:37:03 -07001210 creation_date: String,
Janis Danisevskis60400fe2020-08-26 15:24:42 -07001211 domain: Option<DomainType>,
Joel Galenson0891bc12020-07-20 10:37:03 -07001212 namespace: Option<i64>,
1213 alias: Option<String>,
1214 }
1215
1216 fn get_keyentry(db: &KeystoreDB) -> Result<Vec<KeyEntryRow>> {
1217 db.conn
Joel Galenson2aab4432020-07-22 15:27:57 -07001218 .prepare("SELECT * FROM persistent.keyentry;")?
Joel Galenson0891bc12020-07-20 10:37:03 -07001219 .query_map(NO_PARAMS, |row| {
Joel Galenson0891bc12020-07-20 10:37:03 -07001220 Ok(KeyEntryRow {
1221 id: row.get(0)?,
1222 creation_date: row.get(1)?,
Janis Danisevskis60400fe2020-08-26 15:24:42 -07001223 domain: row.get(2)?,
Joel Galenson0891bc12020-07-20 10:37:03 -07001224 namespace: row.get(3)?,
1225 alias: row.get(4)?,
1226 })
1227 })?
1228 .map(|r| r.context("Could not read keyentry row."))
1229 .collect::<Result<Vec<_>>>()
1230 }
1231
Janis Danisevskis63f7bc82020-09-03 10:12:56 -07001232 fn make_test_key_entry(
1233 db: &mut KeystoreDB,
1234 domain: DomainType,
1235 namespace: i64,
1236 alias: &str,
1237 ) -> Result<i64> {
1238 let key_id = db.create_key_entry(domain, namespace)?;
1239 db.insert_blob(key_id, SubComponentType::KM_BLOB, TEST_KM_BLOB, 1)?;
1240 db.insert_blob(key_id, SubComponentType::CERT, TEST_CERT_BLOB, 1)?;
1241 db.insert_blob(key_id, SubComponentType::CERT_CHAIN, TEST_CERT_CHAIN_BLOB, 1)?;
1242 db.rebind_alias(key_id, alias, domain, namespace)?;
1243 Ok(key_id)
1244 }
1245
1246 fn debug_dump_keyentry_table(db: &mut KeystoreDB) -> Result<()> {
1247 let mut stmt = db.conn.prepare(
1248 "SELECT id, creation_date, domain, namespace, alias FROM persistent.keyentry;",
1249 )?;
1250 let rows = stmt.query_map::<(i64, i64, i32, i64, String), _, _>(NO_PARAMS, |row| {
1251 Ok((row.get(0)?, row.get(1)?, row.get(2)?, row.get(3)?, row.get(4)?))
1252 })?;
1253
1254 println!("Key entry table rows:");
1255 for r in rows {
1256 let (id, cdate, domain, namespace, alias) = r.unwrap();
1257 println!(
1258 " id: {} Creation date: {} Domain: {} Namespace: {} Alias: {}",
1259 id, cdate, domain, namespace, alias
1260 );
1261 }
1262 Ok(())
1263 }
1264
1265 fn debug_dump_grant_table(db: &mut KeystoreDB) -> Result<()> {
1266 let mut stmt =
1267 db.conn.prepare("SELECT id, grantee, keyentryid, access_vector FROM perboot.grant;")?;
1268 let rows = stmt.query_map::<(i64, i64, i64, i64), _, _>(NO_PARAMS, |row| {
1269 Ok((row.get(0)?, row.get(1)?, row.get(2)?, row.get(3)?))
1270 })?;
1271
1272 println!("Grant table rows:");
1273 for r in rows {
1274 let (id, gt, ki, av) = r.unwrap();
1275 println!(" id: {} grantee: {} key_id: {} access_vector: {}", id, gt, ki, av);
1276 }
1277 Ok(())
1278 }
1279
Joel Galenson2aab4432020-07-22 15:27:57 -07001280 // A class that deletes a file when it is dropped.
1281 // TODO: If we ever add a crate that does this, we can use it instead.
1282 struct TempFile {
1283 filename: &'static str,
1284 }
1285
1286 impl Drop for TempFile {
1287 fn drop(&mut self) {
1288 std::fs::remove_file(self.filename).expect("Cannot delete temporary file");
1289 }
1290 }
1291
Joel Galenson0891bc12020-07-20 10:37:03 -07001292 // Use a custom random number generator that repeats each number once.
1293 // This allows us to test repeated elements.
1294
1295 thread_local! {
1296 static RANDOM_COUNTER: RefCell<i64> = RefCell::new(0);
1297 }
1298
Janis Danisevskis63f7bc82020-09-03 10:12:56 -07001299 fn reset_random() {
1300 RANDOM_COUNTER.with(|counter| {
1301 *counter.borrow_mut() = 0;
1302 })
1303 }
1304
Joel Galenson0891bc12020-07-20 10:37:03 -07001305 pub fn random() -> i64 {
1306 RANDOM_COUNTER.with(|counter| {
1307 let result = *counter.borrow() / 2;
1308 *counter.borrow_mut() += 1;
1309 result
1310 })
1311 }
Joel Galenson26f4d012020-07-17 14:57:21 -07001312}