| Joel Galenson | 26f4d01 | 2020-07-17 14:57:21 -0700 | [diff] [blame] | 1 | // Copyright 2020, The Android Open Source Project | 
|  | 2 | // | 
|  | 3 | // Licensed under the Apache License, Version 2.0 (the "License"); | 
|  | 4 | // you may not use this file except in compliance with the License. | 
|  | 5 | // You may obtain a copy of the License at | 
|  | 6 | // | 
|  | 7 | //     http://www.apache.org/licenses/LICENSE-2.0 | 
|  | 8 | // | 
|  | 9 | // Unless required by applicable law or agreed to in writing, software | 
|  | 10 | // distributed under the License is distributed on an "AS IS" BASIS, | 
|  | 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 
|  | 12 | // See the License for the specific language governing permissions and | 
|  | 13 | // limitations under the License. | 
|  | 14 |  | 
| Janis Danisevskis | 63f7bc8 | 2020-09-03 10:12:56 -0700 | [diff] [blame] | 15 | //! 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 Galenson | 26f4d01 | 2020-07-17 14:57:21 -0700 | [diff] [blame] | 43 |  | 
| Janis Danisevskis | b42fc18 | 2020-12-15 08:41:27 -0800 | [diff] [blame] | 44 | #![allow(dead_code)] | 
|  | 45 |  | 
| Janis Danisevskis | c5b210b | 2020-09-11 13:27:37 -0700 | [diff] [blame] | 46 | use crate::error::{Error as KsError, ResponseCode}; | 
| Janis Danisevskis | b42fc18 | 2020-12-15 08:41:27 -0800 | [diff] [blame] | 47 | use crate::impl_metadata; // This is in db_utils.rs | 
| Janis Danisevskis | 4522c2b | 2020-11-27 18:04:58 -0800 | [diff] [blame] | 48 | use crate::key_parameter::{KeyParameter, Tag}; | 
| Janis Danisevskis | c5b210b | 2020-09-11 13:27:37 -0700 | [diff] [blame] | 49 | use crate::permission::KeyPermSet; | 
| Hasini Gunasinghe | 557b103 | 2020-11-10 01:35:30 +0000 | [diff] [blame] | 50 | use crate::utils::get_current_time_in_seconds; | 
| Janis Danisevskis | 93927dd | 2020-12-23 12:23:08 -0800 | [diff] [blame] | 51 | use crate::{ | 
|  | 52 | db_utils::{self, SqlField}, | 
|  | 53 | gc::Gc, | 
|  | 54 | }; | 
| Janis Danisevskis | b42fc18 | 2020-12-15 08:41:27 -0800 | [diff] [blame] | 55 | use anyhow::{anyhow, Context, Result}; | 
|  | 56 | use std::{convert::TryFrom, convert::TryInto, time::SystemTimeError}; | 
| Janis Danisevskis | 60400fe | 2020-08-26 15:24:42 -0700 | [diff] [blame] | 57 |  | 
| Hasini Gunasinghe | 52333ba | 2020-11-06 01:24:16 +0000 | [diff] [blame] | 58 | use android_hardware_security_keymint::aidl::android::hardware::security::keymint::{ | 
| Janis Danisevskis | 5ed8c53 | 2021-01-11 14:19:42 -0800 | [diff] [blame^] | 59 | HardwareAuthToken::HardwareAuthToken, | 
| Hasini Gunasinghe | f70cf8e | 2020-11-11 01:02:41 +0000 | [diff] [blame] | 60 | HardwareAuthenticatorType::HardwareAuthenticatorType, SecurityLevel::SecurityLevel, | 
| Janis Danisevskis | c3a496b | 2021-01-05 10:37:22 -0800 | [diff] [blame] | 61 | }; | 
|  | 62 | use android_hardware_security_secureclock::aidl::android::hardware::security::secureclock::{ | 
| Hasini Gunasinghe | f70cf8e | 2020-11-11 01:02:41 +0000 | [diff] [blame] | 63 | Timestamp::Timestamp, | 
| Hasini Gunasinghe | 52333ba | 2020-11-06 01:24:16 +0000 | [diff] [blame] | 64 | }; | 
| Janis Danisevskis | c5b210b | 2020-09-11 13:27:37 -0700 | [diff] [blame] | 65 | use android_system_keystore2::aidl::android::system::keystore2::{ | 
| Janis Danisevskis | 04b0283 | 2020-10-26 09:21:40 -0700 | [diff] [blame] | 66 | Domain::Domain, KeyDescriptor::KeyDescriptor, | 
| Janis Danisevskis | 60400fe | 2020-08-26 15:24:42 -0700 | [diff] [blame] | 67 | }; | 
| Janis Danisevskis | aec1459 | 2020-11-12 09:41:49 -0800 | [diff] [blame] | 68 | use lazy_static::lazy_static; | 
| Hasini Gunasinghe | f70cf8e | 2020-11-11 01:02:41 +0000 | [diff] [blame] | 69 | use log::error; | 
| Joel Galenson | 0891bc1 | 2020-07-20 10:37:03 -0700 | [diff] [blame] | 70 | #[cfg(not(test))] | 
|  | 71 | use rand::prelude::random; | 
| Janis Danisevskis | 63f7bc8 | 2020-09-03 10:12:56 -0700 | [diff] [blame] | 72 | use rusqlite::{ | 
| Janis Danisevskis | b42fc18 | 2020-12-15 08:41:27 -0800 | [diff] [blame] | 73 | params, | 
|  | 74 | types::FromSql, | 
|  | 75 | types::FromSqlResult, | 
|  | 76 | types::ToSqlOutput, | 
|  | 77 | types::{FromSqlError, Value, ValueRef}, | 
| Janis Danisevskis | 5ed8c53 | 2021-01-11 14:19:42 -0800 | [diff] [blame^] | 78 | Connection, OptionalExtension, ToSql, Transaction, TransactionBehavior, NO_PARAMS, | 
| Janis Danisevskis | 63f7bc8 | 2020-09-03 10:12:56 -0700 | [diff] [blame] | 79 | }; | 
| Janis Danisevskis | aec1459 | 2020-11-12 09:41:49 -0800 | [diff] [blame] | 80 | use std::{ | 
| Janis Danisevskis | b42fc18 | 2020-12-15 08:41:27 -0800 | [diff] [blame] | 81 | collections::{HashMap, HashSet}, | 
| Janis Danisevskis | bf15d73 | 2020-12-08 10:35:26 -0800 | [diff] [blame] | 82 | path::Path, | 
|  | 83 | sync::{Condvar, Mutex}, | 
| Janis Danisevskis | b42fc18 | 2020-12-15 08:41:27 -0800 | [diff] [blame] | 84 | time::{Duration, SystemTime}, | 
| Janis Danisevskis | aec1459 | 2020-11-12 09:41:49 -0800 | [diff] [blame] | 85 | }; | 
| Joel Galenson | 0891bc1 | 2020-07-20 10:37:03 -0700 | [diff] [blame] | 86 | #[cfg(test)] | 
|  | 87 | use tests::random; | 
| Joel Galenson | 26f4d01 | 2020-07-17 14:57:21 -0700 | [diff] [blame] | 88 |  | 
| Janis Danisevskis | b42fc18 | 2020-12-15 08:41:27 -0800 | [diff] [blame] | 89 | impl_metadata!( | 
|  | 90 | /// A set of metadata for key entries. | 
|  | 91 | #[derive(Debug, Default, Eq, PartialEq)] | 
|  | 92 | pub struct KeyMetaData; | 
|  | 93 | /// A metadata entry for key entries. | 
|  | 94 | #[derive(Debug, Eq, PartialEq, Ord, PartialOrd)] | 
|  | 95 | pub enum KeyMetaEntry { | 
|  | 96 | /// If present, indicates that the sensitive part of key | 
|  | 97 | /// is encrypted with another key or a key derived from a password. | 
|  | 98 | EncryptedBy(EncryptedBy) with accessor encrypted_by, | 
|  | 99 | /// If the blob is password encrypted this field is set to the | 
|  | 100 | /// salt used for the key derivation. | 
|  | 101 | Salt(Vec<u8>) with accessor salt, | 
|  | 102 | /// If the blob is encrypted, this field is set to the initialization vector. | 
|  | 103 | Iv(Vec<u8>) with accessor iv, | 
|  | 104 | /// If the blob is encrypted, this field holds the AEAD TAG. | 
|  | 105 | AeadTag(Vec<u8>) with accessor aead_tag, | 
|  | 106 | /// Creation date of a the key entry. | 
|  | 107 | CreationDate(DateTime) with accessor creation_date, | 
|  | 108 | /// Expiration date for attestation keys. | 
|  | 109 | AttestationExpirationDate(DateTime) with accessor attestation_expiration_date, | 
|  | 110 | //  --- ADD NEW META DATA FIELDS HERE --- | 
|  | 111 | // For backwards compatibility add new entries only to | 
|  | 112 | // end of this list and above this comment. | 
|  | 113 | }; | 
|  | 114 | ); | 
|  | 115 |  | 
|  | 116 | impl KeyMetaData { | 
|  | 117 | fn load_from_db(key_id: i64, tx: &Transaction) -> Result<Self> { | 
|  | 118 | let mut stmt = tx | 
|  | 119 | .prepare( | 
|  | 120 | "SELECT tag, data from persistent.keymetadata | 
|  | 121 | WHERE keyentryid = ?;", | 
|  | 122 | ) | 
|  | 123 | .context("In KeyMetaData::load_from_db: prepare statement failed.")?; | 
|  | 124 |  | 
|  | 125 | let mut metadata: HashMap<i64, KeyMetaEntry> = Default::default(); | 
|  | 126 |  | 
|  | 127 | let mut rows = | 
|  | 128 | stmt.query(params![key_id]).context("In KeyMetaData::load_from_db: query failed.")?; | 
|  | 129 | db_utils::with_rows_extract_all(&mut rows, |row| { | 
|  | 130 | let db_tag: i64 = row.get(0).context("Failed to read tag.")?; | 
|  | 131 | metadata.insert( | 
|  | 132 | db_tag, | 
|  | 133 | KeyMetaEntry::new_from_sql(db_tag, &SqlField::new(1, &row)) | 
|  | 134 | .context("Failed to read KeyMetaEntry.")?, | 
|  | 135 | ); | 
|  | 136 | Ok(()) | 
|  | 137 | }) | 
|  | 138 | .context("In KeyMetaData::load_from_db.")?; | 
|  | 139 |  | 
|  | 140 | Ok(Self { data: metadata }) | 
|  | 141 | } | 
|  | 142 |  | 
|  | 143 | fn store_in_db(&self, key_id: i64, tx: &Transaction) -> Result<()> { | 
|  | 144 | let mut stmt = tx | 
|  | 145 | .prepare( | 
|  | 146 | "INSERT into persistent.keymetadata (keyentryid, tag, data) | 
|  | 147 | VALUES (?, ?, ?);", | 
|  | 148 | ) | 
|  | 149 | .context("In KeyMetaData::store_in_db: Failed to prepare statement.")?; | 
|  | 150 |  | 
|  | 151 | let iter = self.data.iter(); | 
|  | 152 | for (tag, entry) in iter { | 
|  | 153 | stmt.insert(params![key_id, tag, entry,]).with_context(|| { | 
|  | 154 | format!("In KeyMetaData::store_in_db: Failed to insert {:?}", entry) | 
|  | 155 | })?; | 
|  | 156 | } | 
|  | 157 | Ok(()) | 
|  | 158 | } | 
|  | 159 | } | 
|  | 160 |  | 
|  | 161 | /// Indicates the type of the keyentry. | 
|  | 162 | #[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd)] | 
|  | 163 | pub enum KeyType { | 
|  | 164 | /// This is a client key type. These keys are created or imported through the Keystore 2.0 | 
|  | 165 | /// AIDL interface android.system.keystore2. | 
|  | 166 | Client, | 
|  | 167 | /// This is a super key type. These keys are created by keystore itself and used to encrypt | 
|  | 168 | /// other key blobs to provide LSKF binding. | 
|  | 169 | Super, | 
|  | 170 | /// This is an attestation key. These keys are created by the remote provisioning mechanism. | 
|  | 171 | Attestation, | 
|  | 172 | } | 
|  | 173 |  | 
|  | 174 | impl ToSql for KeyType { | 
|  | 175 | fn to_sql(&self) -> rusqlite::Result<ToSqlOutput> { | 
|  | 176 | Ok(ToSqlOutput::Owned(Value::Integer(match self { | 
|  | 177 | KeyType::Client => 0, | 
|  | 178 | KeyType::Super => 1, | 
|  | 179 | KeyType::Attestation => 2, | 
|  | 180 | }))) | 
|  | 181 | } | 
|  | 182 | } | 
|  | 183 |  | 
|  | 184 | impl FromSql for KeyType { | 
|  | 185 | fn column_result(value: ValueRef) -> FromSqlResult<Self> { | 
|  | 186 | match i64::column_result(value)? { | 
|  | 187 | 0 => Ok(KeyType::Client), | 
|  | 188 | 1 => Ok(KeyType::Super), | 
|  | 189 | 2 => Ok(KeyType::Attestation), | 
|  | 190 | v => Err(FromSqlError::OutOfRange(v)), | 
|  | 191 | } | 
|  | 192 | } | 
|  | 193 | } | 
|  | 194 |  | 
|  | 195 | /// Indicates how the sensitive part of this key blob is encrypted. | 
|  | 196 | #[derive(Debug, Eq, PartialEq, Ord, PartialOrd)] | 
|  | 197 | pub enum EncryptedBy { | 
|  | 198 | /// The keyblob is encrypted by a user password. | 
|  | 199 | /// In the database this variant is represented as NULL. | 
|  | 200 | Password, | 
|  | 201 | /// The keyblob is encrypted by another key with wrapped key id. | 
|  | 202 | /// In the database this variant is represented as non NULL value | 
|  | 203 | /// that is convertible to i64, typically NUMERIC. | 
|  | 204 | KeyId(i64), | 
|  | 205 | } | 
|  | 206 |  | 
|  | 207 | impl ToSql for EncryptedBy { | 
|  | 208 | fn to_sql(&self) -> rusqlite::Result<ToSqlOutput> { | 
|  | 209 | match self { | 
|  | 210 | Self::Password => Ok(ToSqlOutput::Owned(Value::Null)), | 
|  | 211 | Self::KeyId(id) => id.to_sql(), | 
|  | 212 | } | 
|  | 213 | } | 
|  | 214 | } | 
|  | 215 |  | 
|  | 216 | impl FromSql for EncryptedBy { | 
|  | 217 | fn column_result(value: ValueRef) -> FromSqlResult<Self> { | 
|  | 218 | match value { | 
|  | 219 | ValueRef::Null => Ok(Self::Password), | 
|  | 220 | _ => Ok(Self::KeyId(i64::column_result(value)?)), | 
|  | 221 | } | 
|  | 222 | } | 
|  | 223 | } | 
|  | 224 |  | 
|  | 225 | /// A database representation of wall clock time. DateTime stores unix epoch time as | 
|  | 226 | /// i64 in milliseconds. | 
|  | 227 | #[derive(Debug, Copy, Clone, Default, Eq, PartialEq, Ord, PartialOrd)] | 
|  | 228 | pub struct DateTime(i64); | 
|  | 229 |  | 
|  | 230 | /// Error type returned when creating DateTime or converting it from and to | 
|  | 231 | /// SystemTime. | 
|  | 232 | #[derive(thiserror::Error, Debug)] | 
|  | 233 | pub enum DateTimeError { | 
|  | 234 | /// This is returned when SystemTime and Duration computations fail. | 
|  | 235 | #[error(transparent)] | 
|  | 236 | SystemTimeError(#[from] SystemTimeError), | 
|  | 237 |  | 
|  | 238 | /// This is returned when type conversions fail. | 
|  | 239 | #[error(transparent)] | 
|  | 240 | TypeConversion(#[from] std::num::TryFromIntError), | 
|  | 241 |  | 
|  | 242 | /// This is returned when checked time arithmetic failed. | 
|  | 243 | #[error("Time arithmetic failed.")] | 
|  | 244 | TimeArithmetic, | 
|  | 245 | } | 
|  | 246 |  | 
|  | 247 | impl DateTime { | 
|  | 248 | /// Constructs a new DateTime object denoting the current time. This may fail during | 
|  | 249 | /// conversion to unix epoch time and during conversion to the internal i64 representation. | 
|  | 250 | pub fn now() -> Result<Self, DateTimeError> { | 
|  | 251 | Ok(Self(SystemTime::now().duration_since(SystemTime::UNIX_EPOCH)?.as_millis().try_into()?)) | 
|  | 252 | } | 
|  | 253 |  | 
|  | 254 | /// Constructs a new DateTime object from milliseconds. | 
|  | 255 | pub fn from_millis_epoch(millis: i64) -> Self { | 
|  | 256 | Self(millis) | 
|  | 257 | } | 
|  | 258 |  | 
|  | 259 | /// Returns unix epoch time in milliseconds. | 
|  | 260 | pub fn to_millis_epoch(&self) -> i64 { | 
|  | 261 | self.0 | 
|  | 262 | } | 
|  | 263 |  | 
|  | 264 | /// Returns unix epoch time in seconds. | 
|  | 265 | pub fn to_secs_epoch(&self) -> i64 { | 
|  | 266 | self.0 / 1000 | 
|  | 267 | } | 
|  | 268 | } | 
|  | 269 |  | 
|  | 270 | impl ToSql for DateTime { | 
|  | 271 | fn to_sql(&self) -> rusqlite::Result<ToSqlOutput> { | 
|  | 272 | Ok(ToSqlOutput::Owned(Value::Integer(self.0))) | 
|  | 273 | } | 
|  | 274 | } | 
|  | 275 |  | 
|  | 276 | impl FromSql for DateTime { | 
|  | 277 | fn column_result(value: ValueRef) -> FromSqlResult<Self> { | 
|  | 278 | Ok(Self(i64::column_result(value)?)) | 
|  | 279 | } | 
|  | 280 | } | 
|  | 281 |  | 
|  | 282 | impl TryInto<SystemTime> for DateTime { | 
|  | 283 | type Error = DateTimeError; | 
|  | 284 |  | 
|  | 285 | fn try_into(self) -> Result<SystemTime, Self::Error> { | 
|  | 286 | // We want to construct a SystemTime representation equivalent to self, denoting | 
|  | 287 | // a point in time THEN, but we cannot set the time directly. We can only construct | 
|  | 288 | // a SystemTime denoting NOW, and we can get the duration between EPOCH and NOW, | 
|  | 289 | // and between EPOCH and THEN. With this common reference we can construct the | 
|  | 290 | // duration between NOW and THEN which we can add to our SystemTime representation | 
|  | 291 | // of NOW to get a SystemTime representation of THEN. | 
|  | 292 | // Durations can only be positive, thus the if statement below. | 
|  | 293 | let now = SystemTime::now(); | 
|  | 294 | let now_epoch = now.duration_since(SystemTime::UNIX_EPOCH)?; | 
|  | 295 | let then_epoch = Duration::from_millis(self.0.try_into()?); | 
|  | 296 | Ok(if now_epoch > then_epoch { | 
|  | 297 | // then = now - (now_epoch - then_epoch) | 
|  | 298 | now_epoch | 
|  | 299 | .checked_sub(then_epoch) | 
|  | 300 | .and_then(|d| now.checked_sub(d)) | 
|  | 301 | .ok_or(DateTimeError::TimeArithmetic)? | 
|  | 302 | } else { | 
|  | 303 | // then = now + (then_epoch - now_epoch) | 
|  | 304 | then_epoch | 
|  | 305 | .checked_sub(now_epoch) | 
|  | 306 | .and_then(|d| now.checked_add(d)) | 
|  | 307 | .ok_or(DateTimeError::TimeArithmetic)? | 
|  | 308 | }) | 
|  | 309 | } | 
|  | 310 | } | 
|  | 311 |  | 
|  | 312 | impl TryFrom<SystemTime> for DateTime { | 
|  | 313 | type Error = DateTimeError; | 
|  | 314 |  | 
|  | 315 | fn try_from(t: SystemTime) -> Result<Self, Self::Error> { | 
|  | 316 | Ok(Self(t.duration_since(SystemTime::UNIX_EPOCH)?.as_millis().try_into()?)) | 
|  | 317 | } | 
|  | 318 | } | 
|  | 319 |  | 
| Janis Danisevskis | 93927dd | 2020-12-23 12:23:08 -0800 | [diff] [blame] | 320 | #[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Copy, Clone)] | 
|  | 321 | enum KeyLifeCycle { | 
|  | 322 | /// Existing keys have a key ID but are not fully populated yet. | 
|  | 323 | /// This is a transient state. If Keystore finds any such keys when it starts up, it must move | 
|  | 324 | /// them to Unreferenced for garbage collection. | 
|  | 325 | Existing, | 
|  | 326 | /// A live key is fully populated and usable by clients. | 
|  | 327 | Live, | 
|  | 328 | /// An unreferenced key is scheduled for garbage collection. | 
|  | 329 | Unreferenced, | 
|  | 330 | } | 
|  | 331 |  | 
|  | 332 | impl ToSql for KeyLifeCycle { | 
|  | 333 | fn to_sql(&self) -> rusqlite::Result<ToSqlOutput> { | 
|  | 334 | match self { | 
|  | 335 | Self::Existing => Ok(ToSqlOutput::Owned(Value::Integer(0))), | 
|  | 336 | Self::Live => Ok(ToSqlOutput::Owned(Value::Integer(1))), | 
|  | 337 | Self::Unreferenced => Ok(ToSqlOutput::Owned(Value::Integer(2))), | 
|  | 338 | } | 
|  | 339 | } | 
|  | 340 | } | 
|  | 341 |  | 
|  | 342 | impl FromSql for KeyLifeCycle { | 
|  | 343 | fn column_result(value: ValueRef) -> FromSqlResult<Self> { | 
|  | 344 | match i64::column_result(value)? { | 
|  | 345 | 0 => Ok(KeyLifeCycle::Existing), | 
|  | 346 | 1 => Ok(KeyLifeCycle::Live), | 
|  | 347 | 2 => Ok(KeyLifeCycle::Unreferenced), | 
|  | 348 | v => Err(FromSqlError::OutOfRange(v)), | 
|  | 349 | } | 
|  | 350 | } | 
|  | 351 | } | 
|  | 352 |  | 
| Janis Danisevskis | 63f7bc8 | 2020-09-03 10:12:56 -0700 | [diff] [blame] | 353 | /// Keys have a KeyMint blob component and optional public certificate and | 
|  | 354 | /// certificate chain components. | 
|  | 355 | /// KeyEntryLoadBits is a bitmap that indicates to `KeystoreDB::load_key_entry` | 
|  | 356 | /// which components shall be loaded from the database if present. | 
|  | 357 | #[derive(Debug, Eq, PartialEq, Ord, PartialOrd)] | 
|  | 358 | pub struct KeyEntryLoadBits(u32); | 
|  | 359 |  | 
|  | 360 | impl KeyEntryLoadBits { | 
|  | 361 | /// Indicate to `KeystoreDB::load_key_entry` that no component shall be loaded. | 
|  | 362 | pub const NONE: KeyEntryLoadBits = Self(0); | 
|  | 363 | /// Indicate to `KeystoreDB::load_key_entry` that the KeyMint component shall be loaded. | 
|  | 364 | pub const KM: KeyEntryLoadBits = Self(1); | 
|  | 365 | /// Indicate to `KeystoreDB::load_key_entry` that the Public components shall be loaded. | 
|  | 366 | pub const PUBLIC: KeyEntryLoadBits = Self(2); | 
|  | 367 | /// Indicate to `KeystoreDB::load_key_entry` that both components shall be loaded. | 
|  | 368 | pub const BOTH: KeyEntryLoadBits = Self(3); | 
|  | 369 |  | 
|  | 370 | /// Returns true if this object indicates that the public components shall be loaded. | 
|  | 371 | pub const fn load_public(&self) -> bool { | 
|  | 372 | self.0 & Self::PUBLIC.0 != 0 | 
|  | 373 | } | 
|  | 374 |  | 
|  | 375 | /// Returns true if the object indicates that the KeyMint component shall be loaded. | 
|  | 376 | pub const fn load_km(&self) -> bool { | 
|  | 377 | self.0 & Self::KM.0 != 0 | 
|  | 378 | } | 
|  | 379 | } | 
|  | 380 |  | 
| Janis Danisevskis | aec1459 | 2020-11-12 09:41:49 -0800 | [diff] [blame] | 381 | lazy_static! { | 
|  | 382 | static ref KEY_ID_LOCK: KeyIdLockDb = KeyIdLockDb::new(); | 
|  | 383 | } | 
|  | 384 |  | 
|  | 385 | struct KeyIdLockDb { | 
|  | 386 | locked_keys: Mutex<HashSet<i64>>, | 
|  | 387 | cond_var: Condvar, | 
|  | 388 | } | 
|  | 389 |  | 
|  | 390 | /// A locked key. While a guard exists for a given key id, the same key cannot be loaded | 
|  | 391 | /// from the database a second time. Most functions manipulating the key blob database | 
|  | 392 | /// require a KeyIdGuard. | 
|  | 393 | #[derive(Debug)] | 
|  | 394 | pub struct KeyIdGuard(i64); | 
|  | 395 |  | 
|  | 396 | impl KeyIdLockDb { | 
|  | 397 | fn new() -> Self { | 
|  | 398 | Self { locked_keys: Mutex::new(HashSet::new()), cond_var: Condvar::new() } | 
|  | 399 | } | 
|  | 400 |  | 
|  | 401 | /// This function blocks until an exclusive lock for the given key entry id can | 
|  | 402 | /// be acquired. It returns a guard object, that represents the lifecycle of the | 
|  | 403 | /// acquired lock. | 
|  | 404 | pub fn get(&self, key_id: i64) -> KeyIdGuard { | 
|  | 405 | let mut locked_keys = self.locked_keys.lock().unwrap(); | 
|  | 406 | while locked_keys.contains(&key_id) { | 
|  | 407 | locked_keys = self.cond_var.wait(locked_keys).unwrap(); | 
|  | 408 | } | 
|  | 409 | locked_keys.insert(key_id); | 
|  | 410 | KeyIdGuard(key_id) | 
|  | 411 | } | 
|  | 412 |  | 
|  | 413 | /// This function attempts to acquire an exclusive lock on a given key id. If the | 
|  | 414 | /// given key id is already taken the function returns None immediately. If a lock | 
|  | 415 | /// can be acquired this function returns a guard object, that represents the | 
|  | 416 | /// lifecycle of the acquired lock. | 
|  | 417 | pub fn try_get(&self, key_id: i64) -> Option<KeyIdGuard> { | 
|  | 418 | let mut locked_keys = self.locked_keys.lock().unwrap(); | 
|  | 419 | if locked_keys.insert(key_id) { | 
|  | 420 | Some(KeyIdGuard(key_id)) | 
|  | 421 | } else { | 
|  | 422 | None | 
|  | 423 | } | 
|  | 424 | } | 
|  | 425 | } | 
|  | 426 |  | 
|  | 427 | impl KeyIdGuard { | 
|  | 428 | /// Get the numeric key id of the locked key. | 
|  | 429 | pub fn id(&self) -> i64 { | 
|  | 430 | self.0 | 
|  | 431 | } | 
|  | 432 | } | 
|  | 433 |  | 
|  | 434 | impl Drop for KeyIdGuard { | 
|  | 435 | fn drop(&mut self) { | 
|  | 436 | let mut locked_keys = KEY_ID_LOCK.locked_keys.lock().unwrap(); | 
|  | 437 | locked_keys.remove(&self.0); | 
| Janis Danisevskis | 7fd5358 | 2020-11-23 13:40:34 -0800 | [diff] [blame] | 438 | drop(locked_keys); | 
| Janis Danisevskis | aec1459 | 2020-11-12 09:41:49 -0800 | [diff] [blame] | 439 | KEY_ID_LOCK.cond_var.notify_all(); | 
|  | 440 | } | 
|  | 441 | } | 
|  | 442 |  | 
| Janis Danisevskis | 63f7bc8 | 2020-09-03 10:12:56 -0700 | [diff] [blame] | 443 | /// This type represents a Keystore 2.0 key entry. | 
|  | 444 | /// An entry has a unique `id` by which it can be found in the database. | 
|  | 445 | /// It has a security level field, key parameters, and three optional fields | 
|  | 446 | /// for the KeyMint blob, public certificate and a public certificate chain. | 
| Janis Danisevskis | b42fc18 | 2020-12-15 08:41:27 -0800 | [diff] [blame] | 447 | #[derive(Debug, Default, Eq, PartialEq)] | 
| Janis Danisevskis | 63f7bc8 | 2020-09-03 10:12:56 -0700 | [diff] [blame] | 448 | pub struct KeyEntry { | 
|  | 449 | id: i64, | 
|  | 450 | km_blob: Option<Vec<u8>>, | 
|  | 451 | cert: Option<Vec<u8>>, | 
|  | 452 | cert_chain: Option<Vec<u8>>, | 
| Janis Danisevskis | c5b210b | 2020-09-11 13:27:37 -0700 | [diff] [blame] | 453 | sec_level: SecurityLevel, | 
| Janis Danisevskis | 3f322cb | 2020-09-03 14:46:22 -0700 | [diff] [blame] | 454 | parameters: Vec<KeyParameter>, | 
| Janis Danisevskis | b42fc18 | 2020-12-15 08:41:27 -0800 | [diff] [blame] | 455 | metadata: KeyMetaData, | 
| Janis Danisevskis | 63f7bc8 | 2020-09-03 10:12:56 -0700 | [diff] [blame] | 456 | } | 
|  | 457 |  | 
|  | 458 | impl KeyEntry { | 
|  | 459 | /// Returns the unique id of the Key entry. | 
|  | 460 | pub fn id(&self) -> i64 { | 
|  | 461 | self.id | 
|  | 462 | } | 
|  | 463 | /// Exposes the optional KeyMint blob. | 
|  | 464 | pub fn km_blob(&self) -> &Option<Vec<u8>> { | 
|  | 465 | &self.km_blob | 
|  | 466 | } | 
|  | 467 | /// Extracts the Optional KeyMint blob. | 
|  | 468 | pub fn take_km_blob(&mut self) -> Option<Vec<u8>> { | 
|  | 469 | self.km_blob.take() | 
|  | 470 | } | 
|  | 471 | /// Exposes the optional public certificate. | 
|  | 472 | pub fn cert(&self) -> &Option<Vec<u8>> { | 
|  | 473 | &self.cert | 
|  | 474 | } | 
|  | 475 | /// Extracts the optional public certificate. | 
|  | 476 | pub fn take_cert(&mut self) -> Option<Vec<u8>> { | 
|  | 477 | self.cert.take() | 
|  | 478 | } | 
|  | 479 | /// Exposes the optional public certificate chain. | 
|  | 480 | pub fn cert_chain(&self) -> &Option<Vec<u8>> { | 
|  | 481 | &self.cert_chain | 
|  | 482 | } | 
|  | 483 | /// Extracts the optional public certificate_chain. | 
|  | 484 | pub fn take_cert_chain(&mut self) -> Option<Vec<u8>> { | 
|  | 485 | self.cert_chain.take() | 
|  | 486 | } | 
|  | 487 | /// Returns the security level of the key entry. | 
| Janis Danisevskis | c5b210b | 2020-09-11 13:27:37 -0700 | [diff] [blame] | 488 | pub fn sec_level(&self) -> SecurityLevel { | 
| Janis Danisevskis | 63f7bc8 | 2020-09-03 10:12:56 -0700 | [diff] [blame] | 489 | self.sec_level | 
|  | 490 | } | 
| Janis Danisevskis | 04b0283 | 2020-10-26 09:21:40 -0700 | [diff] [blame] | 491 | /// Exposes the key parameters of this key entry. | 
|  | 492 | pub fn key_parameters(&self) -> &Vec<KeyParameter> { | 
|  | 493 | &self.parameters | 
|  | 494 | } | 
|  | 495 | /// Consumes this key entry and extracts the keyparameters from it. | 
|  | 496 | pub fn into_key_parameters(self) -> Vec<KeyParameter> { | 
|  | 497 | self.parameters | 
|  | 498 | } | 
| Janis Danisevskis | b42fc18 | 2020-12-15 08:41:27 -0800 | [diff] [blame] | 499 | /// Exposes the key metadata of this key entry. | 
|  | 500 | pub fn metadata(&self) -> &KeyMetaData { | 
|  | 501 | &self.metadata | 
|  | 502 | } | 
| Janis Danisevskis | 63f7bc8 | 2020-09-03 10:12:56 -0700 | [diff] [blame] | 503 | } | 
|  | 504 |  | 
|  | 505 | /// Indicates the sub component of a key entry for persistent storage. | 
|  | 506 | #[derive(Debug, Eq, PartialEq, Ord, PartialOrd)] | 
|  | 507 | pub struct SubComponentType(u32); | 
|  | 508 | impl SubComponentType { | 
| Janis Danisevskis | b42fc18 | 2020-12-15 08:41:27 -0800 | [diff] [blame] | 509 | /// Persistent identifier for a key blob. | 
|  | 510 | pub const KEY_BLOB: SubComponentType = Self(0); | 
| Janis Danisevskis | 63f7bc8 | 2020-09-03 10:12:56 -0700 | [diff] [blame] | 511 | /// Persistent identifier for a certificate blob. | 
|  | 512 | pub const CERT: SubComponentType = Self(1); | 
|  | 513 | /// Persistent identifier for a certificate chain blob. | 
|  | 514 | pub const CERT_CHAIN: SubComponentType = Self(2); | 
|  | 515 | } | 
|  | 516 |  | 
|  | 517 | impl ToSql for SubComponentType { | 
|  | 518 | fn to_sql(&self) -> rusqlite::Result<ToSqlOutput> { | 
|  | 519 | self.0.to_sql() | 
|  | 520 | } | 
|  | 521 | } | 
|  | 522 |  | 
|  | 523 | impl FromSql for SubComponentType { | 
|  | 524 | fn column_result(value: ValueRef) -> FromSqlResult<Self> { | 
|  | 525 | Ok(Self(u32::column_result(value)?)) | 
|  | 526 | } | 
|  | 527 | } | 
|  | 528 |  | 
| Janis Danisevskis | 63f7bc8 | 2020-09-03 10:12:56 -0700 | [diff] [blame] | 529 | /// KeystoreDB wraps a connection to an SQLite database and tracks its | 
|  | 530 | /// ownership. It also implements all of Keystore 2.0's database functionality. | 
| Joel Galenson | 26f4d01 | 2020-07-17 14:57:21 -0700 | [diff] [blame] | 531 | pub struct KeystoreDB { | 
| Joel Galenson | 26f4d01 | 2020-07-17 14:57:21 -0700 | [diff] [blame] | 532 | conn: Connection, | 
|  | 533 | } | 
|  | 534 |  | 
| Hasini Gunasinghe | 557b103 | 2020-11-10 01:35:30 +0000 | [diff] [blame] | 535 | /// Database representation of the monotonic time retrieved from the system call clock_gettime with | 
|  | 536 | /// CLOCK_MONOTONIC_RAW. Stores monotonic time as i64 in seconds. | 
|  | 537 | #[derive(Debug, Copy, Clone, Default, Eq, PartialEq, Ord, PartialOrd)] | 
|  | 538 | pub struct MonotonicRawTime(i64); | 
|  | 539 |  | 
|  | 540 | impl MonotonicRawTime { | 
|  | 541 | /// Constructs a new MonotonicRawTime | 
|  | 542 | pub fn now() -> Self { | 
|  | 543 | Self(get_current_time_in_seconds()) | 
|  | 544 | } | 
|  | 545 |  | 
|  | 546 | /// Returns the integer value of MonotonicRawTime as i64 | 
|  | 547 | pub fn seconds(&self) -> i64 { | 
|  | 548 | self.0 | 
|  | 549 | } | 
| Janis Danisevskis | 5ed8c53 | 2021-01-11 14:19:42 -0800 | [diff] [blame^] | 550 |  | 
|  | 551 | /// Like i64::checked_sub. | 
|  | 552 | pub fn checked_sub(&self, other: &Self) -> Option<Self> { | 
|  | 553 | self.0.checked_sub(other.0).map(Self) | 
|  | 554 | } | 
| Hasini Gunasinghe | 557b103 | 2020-11-10 01:35:30 +0000 | [diff] [blame] | 555 | } | 
|  | 556 |  | 
|  | 557 | impl ToSql for MonotonicRawTime { | 
|  | 558 | fn to_sql(&self) -> rusqlite::Result<ToSqlOutput> { | 
|  | 559 | Ok(ToSqlOutput::Owned(Value::Integer(self.0))) | 
|  | 560 | } | 
|  | 561 | } | 
|  | 562 |  | 
|  | 563 | impl FromSql for MonotonicRawTime { | 
|  | 564 | fn column_result(value: ValueRef) -> FromSqlResult<Self> { | 
|  | 565 | Ok(Self(i64::column_result(value)?)) | 
|  | 566 | } | 
|  | 567 | } | 
|  | 568 |  | 
| Hasini Gunasinghe | 52333ba | 2020-11-06 01:24:16 +0000 | [diff] [blame] | 569 | /// This struct encapsulates the information to be stored in the database about the auth tokens | 
|  | 570 | /// received by keystore. | 
|  | 571 | pub struct AuthTokenEntry { | 
|  | 572 | auth_token: HardwareAuthToken, | 
| Hasini Gunasinghe | 557b103 | 2020-11-10 01:35:30 +0000 | [diff] [blame] | 573 | time_received: MonotonicRawTime, | 
| Hasini Gunasinghe | 52333ba | 2020-11-06 01:24:16 +0000 | [diff] [blame] | 574 | } | 
|  | 575 |  | 
|  | 576 | impl AuthTokenEntry { | 
| Hasini Gunasinghe | 557b103 | 2020-11-10 01:35:30 +0000 | [diff] [blame] | 577 | fn new(auth_token: HardwareAuthToken, time_received: MonotonicRawTime) -> Self { | 
| Hasini Gunasinghe | 52333ba | 2020-11-06 01:24:16 +0000 | [diff] [blame] | 578 | AuthTokenEntry { auth_token, time_received } | 
|  | 579 | } | 
|  | 580 |  | 
|  | 581 | /// Checks if this auth token satisfies the given authentication information. | 
| Janis Danisevskis | 5ed8c53 | 2021-01-11 14:19:42 -0800 | [diff] [blame^] | 582 | pub fn satisfies(&self, user_secure_ids: &[i64], auth_type: HardwareAuthenticatorType) -> bool { | 
| Hasini Gunasinghe | 52333ba | 2020-11-06 01:24:16 +0000 | [diff] [blame] | 583 | user_secure_ids.iter().any(|&sid| { | 
| Janis Danisevskis | 5ed8c53 | 2021-01-11 14:19:42 -0800 | [diff] [blame^] | 584 | (sid == self.auth_token.userId || sid == self.auth_token.authenticatorId) | 
|  | 585 | && (((auth_type.0 as i32) & (self.auth_token.authenticatorType.0 as i32)) != 0) | 
| Hasini Gunasinghe | 52333ba | 2020-11-06 01:24:16 +0000 | [diff] [blame] | 586 | }) | 
|  | 587 | } | 
|  | 588 |  | 
| Hasini Gunasinghe | 52333ba | 2020-11-06 01:24:16 +0000 | [diff] [blame] | 589 | /// Returns the auth token wrapped by the AuthTokenEntry | 
| Janis Danisevskis | 5ed8c53 | 2021-01-11 14:19:42 -0800 | [diff] [blame^] | 590 | pub fn auth_token(&self) -> &HardwareAuthToken { | 
|  | 591 | &self.auth_token | 
|  | 592 | } | 
|  | 593 |  | 
|  | 594 | /// Returns the auth token wrapped by the AuthTokenEntry | 
|  | 595 | pub fn take_auth_token(self) -> HardwareAuthToken { | 
| Hasini Gunasinghe | 52333ba | 2020-11-06 01:24:16 +0000 | [diff] [blame] | 596 | self.auth_token | 
|  | 597 | } | 
| Janis Danisevskis | 5ed8c53 | 2021-01-11 14:19:42 -0800 | [diff] [blame^] | 598 |  | 
|  | 599 | /// Returns the time that this auth token was received. | 
|  | 600 | pub fn time_received(&self) -> MonotonicRawTime { | 
|  | 601 | self.time_received | 
|  | 602 | } | 
| Hasini Gunasinghe | 52333ba | 2020-11-06 01:24:16 +0000 | [diff] [blame] | 603 | } | 
|  | 604 |  | 
| Joel Galenson | 26f4d01 | 2020-07-17 14:57:21 -0700 | [diff] [blame] | 605 | impl KeystoreDB { | 
| Janis Danisevskis | 63f7bc8 | 2020-09-03 10:12:56 -0700 | [diff] [blame] | 606 | /// This will create a new database connection connecting the two | 
| Janis Danisevskis | bf15d73 | 2020-12-08 10:35:26 -0800 | [diff] [blame] | 607 | /// files persistent.sqlite and perboot.sqlite in the given directory. | 
|  | 608 | /// It also attempts to initialize all of the tables. | 
|  | 609 | /// KeystoreDB cannot be used by multiple threads. | 
| Janis Danisevskis | 63f7bc8 | 2020-09-03 10:12:56 -0700 | [diff] [blame] | 610 | /// Each thread should open their own connection using `thread_local!`. | 
| Janis Danisevskis | bf15d73 | 2020-12-08 10:35:26 -0800 | [diff] [blame] | 611 | pub fn new(db_root: &Path) -> Result<Self> { | 
|  | 612 | // Build the path to the sqlite files. | 
|  | 613 | let mut persistent_path = db_root.to_path_buf(); | 
|  | 614 | persistent_path.push("persistent.sqlite"); | 
|  | 615 | let mut perboot_path = db_root.to_path_buf(); | 
|  | 616 | perboot_path.push("perboot.sqlite"); | 
| Janis Danisevskis | 4df44f4 | 2020-08-26 14:40:03 -0700 | [diff] [blame] | 617 |  | 
| Janis Danisevskis | bf15d73 | 2020-12-08 10:35:26 -0800 | [diff] [blame] | 618 | // Now convert them to strings prefixed with "file:" | 
|  | 619 | let mut persistent_path_str = "file:".to_owned(); | 
|  | 620 | persistent_path_str.push_str(&persistent_path.to_string_lossy()); | 
|  | 621 | let mut perboot_path_str = "file:".to_owned(); | 
|  | 622 | perboot_path_str.push_str(&perboot_path.to_string_lossy()); | 
|  | 623 |  | 
|  | 624 | let conn = Self::make_connection(&persistent_path_str, &perboot_path_str)?; | 
|  | 625 |  | 
|  | 626 | Self::init_tables(&conn)?; | 
| Janis Danisevskis | 4df44f4 | 2020-08-26 14:40:03 -0700 | [diff] [blame] | 627 | Ok(Self { conn }) | 
| Joel Galenson | 2aab443 | 2020-07-22 15:27:57 -0700 | [diff] [blame] | 628 | } | 
|  | 629 |  | 
| Janis Danisevskis | 4df44f4 | 2020-08-26 14:40:03 -0700 | [diff] [blame] | 630 | fn init_tables(conn: &Connection) -> Result<()> { | 
|  | 631 | conn.execute( | 
|  | 632 | "CREATE TABLE IF NOT EXISTS persistent.keyentry ( | 
| Joel Galenson | 0891bc1 | 2020-07-20 10:37:03 -0700 | [diff] [blame] | 633 | id INTEGER UNIQUE, | 
| Janis Danisevskis | b42fc18 | 2020-12-15 08:41:27 -0800 | [diff] [blame] | 634 | key_type INTEGER, | 
| Joel Galenson | 0891bc1 | 2020-07-20 10:37:03 -0700 | [diff] [blame] | 635 | domain INTEGER, | 
|  | 636 | namespace INTEGER, | 
| Janis Danisevskis | 93927dd | 2020-12-23 12:23:08 -0800 | [diff] [blame] | 637 | alias BLOB, | 
|  | 638 | state INTEGER);", | 
| Janis Danisevskis | 4df44f4 | 2020-08-26 14:40:03 -0700 | [diff] [blame] | 639 | NO_PARAMS, | 
|  | 640 | ) | 
|  | 641 | .context("Failed to initialize \"keyentry\" table.")?; | 
|  | 642 |  | 
|  | 643 | conn.execute( | 
| Janis Danisevskis | 63f7bc8 | 2020-09-03 10:12:56 -0700 | [diff] [blame] | 644 | "CREATE TABLE IF NOT EXISTS persistent.blobentry ( | 
|  | 645 | id INTEGER PRIMARY KEY, | 
|  | 646 | subcomponent_type INTEGER, | 
|  | 647 | keyentryid INTEGER, | 
| Janis Danisevskis | 93927dd | 2020-12-23 12:23:08 -0800 | [diff] [blame] | 648 | blob BLOB);", | 
| Janis Danisevskis | 63f7bc8 | 2020-09-03 10:12:56 -0700 | [diff] [blame] | 649 | NO_PARAMS, | 
|  | 650 | ) | 
|  | 651 | .context("Failed to initialize \"blobentry\" table.")?; | 
|  | 652 |  | 
|  | 653 | conn.execute( | 
| Janis Danisevskis | 4df44f4 | 2020-08-26 14:40:03 -0700 | [diff] [blame] | 654 | "CREATE TABLE IF NOT EXISTS persistent.keyparameter ( | 
| Hasini Gunasinghe | af99366 | 2020-07-24 18:40:20 +0000 | [diff] [blame] | 655 | keyentryid INTEGER, | 
|  | 656 | tag INTEGER, | 
|  | 657 | data ANY, | 
|  | 658 | security_level INTEGER);", | 
| Janis Danisevskis | 4df44f4 | 2020-08-26 14:40:03 -0700 | [diff] [blame] | 659 | NO_PARAMS, | 
|  | 660 | ) | 
|  | 661 | .context("Failed to initialize \"keyparameter\" table.")?; | 
|  | 662 |  | 
| Janis Danisevskis | 63f7bc8 | 2020-09-03 10:12:56 -0700 | [diff] [blame] | 663 | conn.execute( | 
| Janis Danisevskis | b42fc18 | 2020-12-15 08:41:27 -0800 | [diff] [blame] | 664 | "CREATE TABLE IF NOT EXISTS persistent.keymetadata ( | 
|  | 665 | keyentryid INTEGER, | 
|  | 666 | tag INTEGER, | 
|  | 667 | data ANY);", | 
|  | 668 | NO_PARAMS, | 
|  | 669 | ) | 
|  | 670 | .context("Failed to initialize \"keymetadata\" table.")?; | 
|  | 671 |  | 
|  | 672 | conn.execute( | 
| Janis Danisevskis | bf15d73 | 2020-12-08 10:35:26 -0800 | [diff] [blame] | 673 | "CREATE TABLE IF NOT EXISTS persistent.grant ( | 
| Janis Danisevskis | 63f7bc8 | 2020-09-03 10:12:56 -0700 | [diff] [blame] | 674 | id INTEGER UNIQUE, | 
|  | 675 | grantee INTEGER, | 
|  | 676 | keyentryid INTEGER, | 
|  | 677 | access_vector INTEGER);", | 
|  | 678 | NO_PARAMS, | 
|  | 679 | ) | 
|  | 680 | .context("Failed to initialize \"grant\" table.")?; | 
|  | 681 |  | 
| Hasini Gunasinghe | 557b103 | 2020-11-10 01:35:30 +0000 | [diff] [blame] | 682 | //TODO: only drop the following two perboot tables if this is the first start up | 
|  | 683 | //during the boot (b/175716626). | 
|  | 684 | // conn.execute("DROP TABLE IF EXISTS perboot.authtoken;", NO_PARAMS) | 
|  | 685 | //     .context("Failed to drop perboot.authtoken table")?; | 
|  | 686 | conn.execute( | 
|  | 687 | "CREATE TABLE IF NOT EXISTS perboot.authtoken ( | 
|  | 688 | id INTEGER PRIMARY KEY, | 
|  | 689 | challenge INTEGER, | 
|  | 690 | user_id INTEGER, | 
|  | 691 | auth_id INTEGER, | 
|  | 692 | authenticator_type INTEGER, | 
|  | 693 | timestamp INTEGER, | 
|  | 694 | mac BLOB, | 
|  | 695 | time_received INTEGER, | 
|  | 696 | UNIQUE(user_id, auth_id, authenticator_type));", | 
|  | 697 | NO_PARAMS, | 
|  | 698 | ) | 
|  | 699 | .context("Failed to initialize \"authtoken\" table.")?; | 
|  | 700 |  | 
|  | 701 | // conn.execute("DROP TABLE IF EXISTS perboot.metadata;", NO_PARAMS) | 
|  | 702 | //     .context("Failed to drop perboot.metadata table")?; | 
|  | 703 | // metadata table stores certain miscellaneous information required for keystore functioning | 
|  | 704 | // during a boot cycle, as key-value pairs. | 
|  | 705 | conn.execute( | 
|  | 706 | "CREATE TABLE IF NOT EXISTS perboot.metadata ( | 
|  | 707 | key TEXT, | 
|  | 708 | value BLOB, | 
|  | 709 | UNIQUE(key));", | 
|  | 710 | NO_PARAMS, | 
|  | 711 | ) | 
|  | 712 | .context("Failed to initialize \"metadata\" table.")?; | 
| Joel Galenson | 0891bc1 | 2020-07-20 10:37:03 -0700 | [diff] [blame] | 713 | Ok(()) | 
|  | 714 | } | 
|  | 715 |  | 
| Janis Danisevskis | 4df44f4 | 2020-08-26 14:40:03 -0700 | [diff] [blame] | 716 | fn make_connection(persistent_file: &str, perboot_file: &str) -> Result<Connection> { | 
|  | 717 | let conn = | 
|  | 718 | Connection::open_in_memory().context("Failed to initialize SQLite connection.")?; | 
|  | 719 |  | 
|  | 720 | conn.execute("ATTACH DATABASE ? as persistent;", params![persistent_file]) | 
|  | 721 | .context("Failed to attach database persistent.")?; | 
|  | 722 | conn.execute("ATTACH DATABASE ? as perboot;", params![perboot_file]) | 
|  | 723 | .context("Failed to attach database perboot.")?; | 
|  | 724 |  | 
|  | 725 | Ok(conn) | 
|  | 726 | } | 
|  | 727 |  | 
| Janis Danisevskis | 93927dd | 2020-12-23 12:23:08 -0800 | [diff] [blame] | 728 | /// Get one unreferenced key. There is no particular order in which the keys are returned. | 
|  | 729 | fn get_unreferenced_key_id(tx: &Transaction) -> Result<Option<i64>> { | 
|  | 730 | tx.query_row( | 
|  | 731 | "SELECT id FROM persistent.keyentry WHERE state = ?", | 
|  | 732 | params![KeyLifeCycle::Unreferenced], | 
|  | 733 | |row| row.get(0), | 
|  | 734 | ) | 
|  | 735 | .optional() | 
|  | 736 | .context("In get_unreferenced_key_id: Trying to get unreferenced key id.") | 
|  | 737 | } | 
|  | 738 |  | 
|  | 739 | /// Returns a key id guard and key entry for one unreferenced key entry. Of the optional | 
|  | 740 | /// fields of the key entry only the km_blob field will be populated. This is required | 
|  | 741 | /// to subject the blob to its KeyMint instance for deletion. | 
|  | 742 | pub fn get_unreferenced_key(&mut self) -> Result<Option<(KeyIdGuard, KeyEntry)>> { | 
|  | 743 | self.with_transaction(TransactionBehavior::Deferred, |tx| { | 
|  | 744 | let key_id = match Self::get_unreferenced_key_id(tx) | 
|  | 745 | .context("Trying to get unreferenced key id")? | 
|  | 746 | { | 
|  | 747 | None => return Ok(None), | 
|  | 748 | Some(id) => KEY_ID_LOCK.try_get(id).ok_or_else(KsError::sys).context(concat!( | 
|  | 749 | "A key id lock was held for an unreferenced key. ", | 
|  | 750 | "This should never happen." | 
|  | 751 | ))?, | 
|  | 752 | }; | 
|  | 753 | let key_entry = Self::load_key_components(tx, KeyEntryLoadBits::KM, key_id.id()) | 
|  | 754 | .context("Trying to get key components.")?; | 
|  | 755 | Ok(Some((key_id, key_entry))) | 
|  | 756 | }) | 
|  | 757 | .context("In get_unreferenced_key.") | 
|  | 758 | } | 
|  | 759 |  | 
|  | 760 | /// This function purges all remnants of a key entry from the database. | 
|  | 761 | /// Important: This does not check if the key was unreferenced, nor does it | 
|  | 762 | /// subject the key to its KeyMint instance for permanent invalidation. | 
|  | 763 | /// This function should only be called by the garbage collector. | 
|  | 764 | /// To delete a key call `mark_unreferenced`, which transitions the key to the unreferenced | 
|  | 765 | /// state, deletes all grants to the key, and notifies the garbage collector. | 
|  | 766 | /// The garbage collector will: | 
|  | 767 | ///  1. Call get_unreferenced_key. | 
|  | 768 | ///  2. Determine the proper way to dispose of sensitive key material, e.g., call | 
|  | 769 | ///     `KeyMintDevice::delete()`. | 
|  | 770 | ///  3. Call `purge_key_entry`. | 
|  | 771 | pub fn purge_key_entry(&mut self, key_id: KeyIdGuard) -> Result<()> { | 
|  | 772 | self.with_transaction(TransactionBehavior::Immediate, |tx| { | 
|  | 773 | tx.execute("DELETE FROM persistent.keyentry WHERE id = ?;", params![key_id.id()]) | 
|  | 774 | .context("Trying to delete keyentry.")?; | 
|  | 775 | tx.execute( | 
|  | 776 | "DELETE FROM persistent.blobentry WHERE keyentryid = ?;", | 
|  | 777 | params![key_id.id()], | 
|  | 778 | ) | 
|  | 779 | .context("Trying to delete blobentries.")?; | 
|  | 780 | tx.execute( | 
|  | 781 | "DELETE FROM persistent.keymetadata WHERE keyentryid = ?;", | 
|  | 782 | params![key_id.id()], | 
|  | 783 | ) | 
|  | 784 | .context("Trying to delete keymetadata.")?; | 
|  | 785 | tx.execute( | 
|  | 786 | "DELETE FROM persistent.keyparameter WHERE keyentryid = ?;", | 
|  | 787 | params![key_id.id()], | 
|  | 788 | ) | 
|  | 789 | .context("Trying to delete keyparameters.")?; | 
|  | 790 | let grants_deleted = tx | 
|  | 791 | .execute("DELETE FROM persistent.grant WHERE keyentryid = ?;", params![key_id.id()]) | 
|  | 792 | .context("Trying to delete grants.")?; | 
|  | 793 | if grants_deleted != 0 { | 
|  | 794 | log::error!("Purged key that still had grants. This should not happen."); | 
|  | 795 | } | 
|  | 796 | Ok(()) | 
|  | 797 | }) | 
|  | 798 | .context("In purge_key_entry.") | 
|  | 799 | } | 
|  | 800 |  | 
|  | 801 | /// This maintenance function should be called only once before the database is used for the | 
|  | 802 | /// first time. It restores the invariant that `KeyLifeCycle::Existing` is a transient state. | 
|  | 803 | /// The function transitions all key entries from Existing to Unreferenced unconditionally and | 
|  | 804 | /// returns the number of rows affected. If this returns a value greater than 0, it means that | 
|  | 805 | /// Keystore crashed at some point during key generation. Callers may want to log such | 
|  | 806 | /// occurrences. | 
|  | 807 | /// Unlike with `mark_unreferenced`, we don't need to purge grants, because only keys that made | 
|  | 808 | /// it to `KeyLifeCycle::Live` may have grants. | 
|  | 809 | pub fn cleanup_leftovers(&mut self) -> Result<usize> { | 
|  | 810 | self.conn | 
|  | 811 | .execute( | 
|  | 812 | "UPDATE persistent.keyentry SET state = ? WHERE state = ?;", | 
|  | 813 | params![KeyLifeCycle::Unreferenced, KeyLifeCycle::Existing], | 
|  | 814 | ) | 
|  | 815 | .context("In cleanup_leftovers.") | 
|  | 816 | } | 
|  | 817 |  | 
| Janis Danisevskis | b42fc18 | 2020-12-15 08:41:27 -0800 | [diff] [blame] | 818 | /// Atomically loads a key entry and associated metadata or creates it using the | 
|  | 819 | /// callback create_new_key callback. The callback is called during a database | 
|  | 820 | /// transaction. This means that implementers should be mindful about using | 
|  | 821 | /// blocking operations such as IPC or grabbing mutexes. | 
|  | 822 | pub fn get_or_create_key_with<F>( | 
|  | 823 | &mut self, | 
|  | 824 | domain: Domain, | 
|  | 825 | namespace: i64, | 
|  | 826 | alias: &str, | 
|  | 827 | create_new_key: F, | 
|  | 828 | ) -> Result<(KeyIdGuard, KeyEntry)> | 
|  | 829 | where | 
|  | 830 | F: FnOnce() -> Result<(Vec<u8>, KeyMetaData)>, | 
|  | 831 | { | 
|  | 832 | let tx = self | 
|  | 833 | .conn | 
|  | 834 | .transaction_with_behavior(TransactionBehavior::Immediate) | 
|  | 835 | .context("In get_or_create_key_with: Failed to initialize transaction.")?; | 
|  | 836 |  | 
|  | 837 | let id = { | 
|  | 838 | let mut stmt = tx | 
|  | 839 | .prepare( | 
|  | 840 | "SELECT id FROM persistent.keyentry | 
|  | 841 | WHERE | 
|  | 842 | key_type = ? | 
|  | 843 | AND domain = ? | 
|  | 844 | AND namespace = ? | 
| Janis Danisevskis | 93927dd | 2020-12-23 12:23:08 -0800 | [diff] [blame] | 845 | AND alias = ? | 
|  | 846 | AND state = ?;", | 
| Janis Danisevskis | b42fc18 | 2020-12-15 08:41:27 -0800 | [diff] [blame] | 847 | ) | 
|  | 848 | .context("In get_or_create_key_with: Failed to select from keyentry table.")?; | 
|  | 849 | let mut rows = stmt | 
| Janis Danisevskis | 93927dd | 2020-12-23 12:23:08 -0800 | [diff] [blame] | 850 | .query(params![KeyType::Super, domain.0, namespace, alias, KeyLifeCycle::Live]) | 
| Janis Danisevskis | b42fc18 | 2020-12-15 08:41:27 -0800 | [diff] [blame] | 851 | .context("In get_or_create_key_with: Failed to query from keyentry table.")?; | 
|  | 852 |  | 
|  | 853 | db_utils::with_rows_extract_one(&mut rows, |row| { | 
|  | 854 | Ok(match row { | 
|  | 855 | Some(r) => r.get(0).context("Failed to unpack id.")?, | 
|  | 856 | None => None, | 
|  | 857 | }) | 
|  | 858 | }) | 
|  | 859 | .context("In get_or_create_key_with.")? | 
|  | 860 | }; | 
|  | 861 |  | 
|  | 862 | let (id, entry) = match id { | 
|  | 863 | Some(id) => ( | 
|  | 864 | id, | 
|  | 865 | Self::load_key_components(&tx, KeyEntryLoadBits::KM, id) | 
|  | 866 | .context("In get_or_create_key_with.")?, | 
|  | 867 | ), | 
|  | 868 |  | 
|  | 869 | None => { | 
|  | 870 | let id = Self::insert_with_retry(|id| { | 
|  | 871 | tx.execute( | 
|  | 872 | "INSERT into persistent.keyentry | 
| Janis Danisevskis | 93927dd | 2020-12-23 12:23:08 -0800 | [diff] [blame] | 873 | (id, key_type, domain, namespace, alias, state) | 
|  | 874 | VALUES(?, ?, ?, ?, ?, ?);", | 
|  | 875 | params![id, KeyType::Super, domain.0, namespace, alias, KeyLifeCycle::Live], | 
| Janis Danisevskis | b42fc18 | 2020-12-15 08:41:27 -0800 | [diff] [blame] | 876 | ) | 
|  | 877 | }) | 
|  | 878 | .context("In get_or_create_key_with.")?; | 
|  | 879 |  | 
|  | 880 | let (blob, metadata) = create_new_key().context("In get_or_create_key_with.")?; | 
| Janis Danisevskis | 93927dd | 2020-12-23 12:23:08 -0800 | [diff] [blame] | 881 | Self::insert_blob_internal(&tx, id, SubComponentType::KEY_BLOB, &blob) | 
|  | 882 | .context("In get_of_create_key_with.")?; | 
| Janis Danisevskis | b42fc18 | 2020-12-15 08:41:27 -0800 | [diff] [blame] | 883 | metadata.store_in_db(id, &tx).context("In get_or_create_key_with.")?; | 
|  | 884 | (id, KeyEntry { id, km_blob: Some(blob), metadata, ..Default::default() }) | 
|  | 885 | } | 
|  | 886 | }; | 
|  | 887 | tx.commit().context("In get_or_create_key_with: Failed to commit transaction.")?; | 
|  | 888 | Ok((KEY_ID_LOCK.get(id), entry)) | 
|  | 889 | } | 
|  | 890 |  | 
| Janis Danisevskis | 93927dd | 2020-12-23 12:23:08 -0800 | [diff] [blame] | 891 | /// Creates a transaction with the given behavior and executes f with the new transaction. | 
|  | 892 | /// The transaction is committed only if f returns Ok. | 
|  | 893 | fn with_transaction<T, F>(&mut self, behavior: TransactionBehavior, f: F) -> Result<T> | 
|  | 894 | where | 
|  | 895 | F: FnOnce(&Transaction) -> Result<T>, | 
|  | 896 | { | 
|  | 897 | let tx = self | 
|  | 898 | .conn | 
|  | 899 | .transaction_with_behavior(behavior) | 
|  | 900 | .context("In with_transaction: Failed to initialize transaction.")?; | 
|  | 901 | f(&tx).and_then(|result| { | 
|  | 902 | tx.commit().context("In with_transaction: Failed to commit transaction.")?; | 
|  | 903 | Ok(result) | 
|  | 904 | }) | 
|  | 905 | } | 
|  | 906 |  | 
| Janis Danisevskis | 63f7bc8 | 2020-09-03 10:12:56 -0700 | [diff] [blame] | 907 | /// Creates a new key entry and allocates a new randomized id for the new key. | 
|  | 908 | /// The key id gets associated with a domain and namespace but not with an alias. | 
|  | 909 | /// To complete key generation `rebind_alias` should be called after all of the | 
|  | 910 | /// key artifacts, i.e., blobs and parameters have been associated with the new | 
|  | 911 | /// key id. Finalizing with `rebind_alias` makes the creation of a new key entry | 
|  | 912 | /// atomic even if key generation is not. | 
| Janis Danisevskis | 93927dd | 2020-12-23 12:23:08 -0800 | [diff] [blame] | 913 | pub fn create_key_entry(&mut self, domain: Domain, namespace: i64) -> Result<KeyIdGuard> { | 
|  | 914 | self.with_transaction(TransactionBehavior::Immediate, |tx| { | 
|  | 915 | Self::create_key_entry_internal(tx, domain, namespace) | 
|  | 916 | }) | 
|  | 917 | .context("In create_key_entry.") | 
|  | 918 | } | 
|  | 919 |  | 
|  | 920 | fn create_key_entry_internal( | 
|  | 921 | tx: &Transaction, | 
|  | 922 | domain: Domain, | 
|  | 923 | namespace: i64, | 
|  | 924 | ) -> Result<KeyIdGuard> { | 
| Joel Galenson | 0891bc1 | 2020-07-20 10:37:03 -0700 | [diff] [blame] | 925 | match domain { | 
| Janis Danisevskis | c5b210b | 2020-09-11 13:27:37 -0700 | [diff] [blame] | 926 | Domain::APP | Domain::SELINUX => {} | 
| Joel Galenson | 0891bc1 | 2020-07-20 10:37:03 -0700 | [diff] [blame] | 927 | _ => { | 
|  | 928 | return Err(KsError::sys()) | 
|  | 929 | .context(format!("Domain {:?} must be either App or SELinux.", domain)); | 
|  | 930 | } | 
|  | 931 | } | 
| Janis Danisevskis | aec1459 | 2020-11-12 09:41:49 -0800 | [diff] [blame] | 932 | Ok(KEY_ID_LOCK.get( | 
|  | 933 | Self::insert_with_retry(|id| { | 
| Janis Danisevskis | 93927dd | 2020-12-23 12:23:08 -0800 | [diff] [blame] | 934 | tx.execute( | 
| Janis Danisevskis | b42fc18 | 2020-12-15 08:41:27 -0800 | [diff] [blame] | 935 | "INSERT into persistent.keyentry | 
| Janis Danisevskis | 93927dd | 2020-12-23 12:23:08 -0800 | [diff] [blame] | 936 | (id, key_type, domain, namespace, alias, state) | 
|  | 937 | VALUES(?, ?, ?, ?, NULL, ?);", | 
|  | 938 | params![ | 
|  | 939 | id, | 
|  | 940 | KeyType::Client, | 
|  | 941 | domain.0 as u32, | 
|  | 942 | namespace, | 
|  | 943 | KeyLifeCycle::Existing | 
|  | 944 | ], | 
| Janis Danisevskis | aec1459 | 2020-11-12 09:41:49 -0800 | [diff] [blame] | 945 | ) | 
|  | 946 | }) | 
| Janis Danisevskis | 93927dd | 2020-12-23 12:23:08 -0800 | [diff] [blame] | 947 | .context("In create_key_entry_internal")?, | 
| Janis Danisevskis | aec1459 | 2020-11-12 09:41:49 -0800 | [diff] [blame] | 948 | )) | 
| Joel Galenson | 26f4d01 | 2020-07-17 14:57:21 -0700 | [diff] [blame] | 949 | } | 
| Joel Galenson | 33c04ad | 2020-08-03 11:04:38 -0700 | [diff] [blame] | 950 |  | 
| Janis Danisevskis | 63f7bc8 | 2020-09-03 10:12:56 -0700 | [diff] [blame] | 951 | /// Inserts a new blob and associates it with the given key id. Each blob | 
|  | 952 | /// has a sub component type and a security level. | 
|  | 953 | /// Each key can have one of each sub component type associated. If more | 
|  | 954 | /// are added only the most recent can be retrieved, and superseded blobs | 
|  | 955 | /// will get garbage collected. The security level field of components | 
| Janis Danisevskis | b42fc18 | 2020-12-15 08:41:27 -0800 | [diff] [blame] | 956 | /// other than `SubComponentType::KEY_BLOB` are ignored. | 
| Janis Danisevskis | 63f7bc8 | 2020-09-03 10:12:56 -0700 | [diff] [blame] | 957 | pub fn insert_blob( | 
|  | 958 | &mut self, | 
| Janis Danisevskis | aec1459 | 2020-11-12 09:41:49 -0800 | [diff] [blame] | 959 | key_id: &KeyIdGuard, | 
| Janis Danisevskis | 63f7bc8 | 2020-09-03 10:12:56 -0700 | [diff] [blame] | 960 | sc_type: SubComponentType, | 
|  | 961 | blob: &[u8], | 
| Janis Danisevskis | 63f7bc8 | 2020-09-03 10:12:56 -0700 | [diff] [blame] | 962 | ) -> Result<()> { | 
| Janis Danisevskis | 93927dd | 2020-12-23 12:23:08 -0800 | [diff] [blame] | 963 | self.with_transaction(TransactionBehavior::Immediate, |tx| { | 
|  | 964 | Self::insert_blob_internal(&tx, key_id.0, sc_type, blob) | 
|  | 965 | }) | 
|  | 966 | .context("In insert_blob.") | 
| Janis Danisevskis | b42fc18 | 2020-12-15 08:41:27 -0800 | [diff] [blame] | 967 | } | 
|  | 968 |  | 
|  | 969 | fn insert_blob_internal( | 
|  | 970 | tx: &Transaction, | 
|  | 971 | key_id: i64, | 
|  | 972 | sc_type: SubComponentType, | 
|  | 973 | blob: &[u8], | 
| Janis Danisevskis | b42fc18 | 2020-12-15 08:41:27 -0800 | [diff] [blame] | 974 | ) -> Result<()> { | 
|  | 975 | tx.execute( | 
| Janis Danisevskis | 93927dd | 2020-12-23 12:23:08 -0800 | [diff] [blame] | 976 | "INSERT into persistent.blobentry (subcomponent_type, keyentryid, blob) | 
|  | 977 | VALUES (?, ?, ?);", | 
|  | 978 | params![sc_type, key_id, blob], | 
| Janis Danisevskis | b42fc18 | 2020-12-15 08:41:27 -0800 | [diff] [blame] | 979 | ) | 
|  | 980 | .context("In insert_blob_internal: Failed to insert blob.")?; | 
| Janis Danisevskis | 63f7bc8 | 2020-09-03 10:12:56 -0700 | [diff] [blame] | 981 | Ok(()) | 
|  | 982 | } | 
|  | 983 |  | 
| Janis Danisevskis | 3f322cb | 2020-09-03 14:46:22 -0700 | [diff] [blame] | 984 | /// Inserts a collection of key parameters into the `persistent.keyparameter` table | 
|  | 985 | /// and associates them with the given `key_id`. | 
|  | 986 | pub fn insert_keyparameter<'a>( | 
|  | 987 | &mut self, | 
| Janis Danisevskis | aec1459 | 2020-11-12 09:41:49 -0800 | [diff] [blame] | 988 | key_id: &KeyIdGuard, | 
| Janis Danisevskis | 3f322cb | 2020-09-03 14:46:22 -0700 | [diff] [blame] | 989 | params: impl IntoIterator<Item = &'a KeyParameter>, | 
|  | 990 | ) -> Result<()> { | 
| Janis Danisevskis | 93927dd | 2020-12-23 12:23:08 -0800 | [diff] [blame] | 991 | self.with_transaction(TransactionBehavior::Immediate, |tx| { | 
|  | 992 | Self::insert_keyparameter_internal(tx, key_id, params) | 
|  | 993 | }) | 
|  | 994 | .context("In insert_keyparameter.") | 
|  | 995 | } | 
| Janis Danisevskis | 3f322cb | 2020-09-03 14:46:22 -0700 | [diff] [blame] | 996 |  | 
| Janis Danisevskis | 93927dd | 2020-12-23 12:23:08 -0800 | [diff] [blame] | 997 | fn insert_keyparameter_internal<'a>( | 
|  | 998 | tx: &Transaction, | 
|  | 999 | key_id: &KeyIdGuard, | 
|  | 1000 | params: impl IntoIterator<Item = &'a KeyParameter>, | 
|  | 1001 | ) -> Result<()> { | 
|  | 1002 | let mut stmt = tx | 
|  | 1003 | .prepare( | 
|  | 1004 | "INSERT into persistent.keyparameter (keyentryid, tag, data, security_level) | 
|  | 1005 | VALUES (?, ?, ?, ?);", | 
|  | 1006 | ) | 
|  | 1007 | .context("In insert_keyparameter_internal: Failed to prepare statement.")?; | 
|  | 1008 |  | 
|  | 1009 | let iter = params.into_iter(); | 
|  | 1010 | for p in iter { | 
|  | 1011 | stmt.insert(params![ | 
|  | 1012 | key_id.0, | 
|  | 1013 | p.get_tag().0, | 
|  | 1014 | p.key_parameter_value(), | 
|  | 1015 | p.security_level().0 | 
|  | 1016 | ]) | 
|  | 1017 | .with_context(|| { | 
|  | 1018 | format!("In insert_keyparameter_internal: Failed to insert {:?}", p) | 
|  | 1019 | })?; | 
| Janis Danisevskis | 3f322cb | 2020-09-03 14:46:22 -0700 | [diff] [blame] | 1020 | } | 
|  | 1021 | Ok(()) | 
|  | 1022 | } | 
|  | 1023 |  | 
| Janis Danisevskis | b42fc18 | 2020-12-15 08:41:27 -0800 | [diff] [blame] | 1024 | /// Insert a set of key entry specific metadata into the database. | 
|  | 1025 | pub fn insert_key_metadata( | 
|  | 1026 | &mut self, | 
|  | 1027 | key_id: &KeyIdGuard, | 
|  | 1028 | metadata: &KeyMetaData, | 
|  | 1029 | ) -> Result<()> { | 
| Janis Danisevskis | 93927dd | 2020-12-23 12:23:08 -0800 | [diff] [blame] | 1030 | self.with_transaction(TransactionBehavior::Immediate, |tx| { | 
|  | 1031 | metadata.store_in_db(key_id.0, &tx) | 
|  | 1032 | }) | 
|  | 1033 | .context("In insert_key_metadata.") | 
|  | 1034 | } | 
|  | 1035 |  | 
|  | 1036 | fn rebind_alias( | 
|  | 1037 | &mut self, | 
|  | 1038 | newid: &KeyIdGuard, | 
|  | 1039 | alias: &str, | 
|  | 1040 | domain: Domain, | 
|  | 1041 | namespace: i64, | 
|  | 1042 | ) -> Result<()> { | 
|  | 1043 | self.with_transaction(TransactionBehavior::Immediate, |tx| { | 
|  | 1044 | Self::rebind_alias_internal(tx, newid, alias, domain, namespace) | 
|  | 1045 | }) | 
|  | 1046 | .context("In rebind_alias.") | 
| Janis Danisevskis | b42fc18 | 2020-12-15 08:41:27 -0800 | [diff] [blame] | 1047 | } | 
|  | 1048 |  | 
| Janis Danisevskis | 63f7bc8 | 2020-09-03 10:12:56 -0700 | [diff] [blame] | 1049 | /// Updates the alias column of the given key id `newid` with the given alias, | 
|  | 1050 | /// and atomically, removes the alias, domain, and namespace from another row | 
|  | 1051 | /// with the same alias-domain-namespace tuple if such row exits. | 
| Janis Danisevskis | 93927dd | 2020-12-23 12:23:08 -0800 | [diff] [blame] | 1052 | fn rebind_alias_internal( | 
|  | 1053 | tx: &Transaction, | 
| Janis Danisevskis | aec1459 | 2020-11-12 09:41:49 -0800 | [diff] [blame] | 1054 | newid: &KeyIdGuard, | 
| Joel Galenson | 33c04ad | 2020-08-03 11:04:38 -0700 | [diff] [blame] | 1055 | alias: &str, | 
| Janis Danisevskis | c5b210b | 2020-09-11 13:27:37 -0700 | [diff] [blame] | 1056 | domain: Domain, | 
| Joel Galenson | 33c04ad | 2020-08-03 11:04:38 -0700 | [diff] [blame] | 1057 | namespace: i64, | 
|  | 1058 | ) -> Result<()> { | 
|  | 1059 | match domain { | 
| Janis Danisevskis | c5b210b | 2020-09-11 13:27:37 -0700 | [diff] [blame] | 1060 | Domain::APP | Domain::SELINUX => {} | 
| Joel Galenson | 33c04ad | 2020-08-03 11:04:38 -0700 | [diff] [blame] | 1061 | _ => { | 
| Janis Danisevskis | 63f7bc8 | 2020-09-03 10:12:56 -0700 | [diff] [blame] | 1062 | return Err(KsError::sys()).context(format!( | 
| Janis Danisevskis | 93927dd | 2020-12-23 12:23:08 -0800 | [diff] [blame] | 1063 | "In rebind_alias_internal: Domain {:?} must be either App or SELinux.", | 
| Janis Danisevskis | 63f7bc8 | 2020-09-03 10:12:56 -0700 | [diff] [blame] | 1064 | domain | 
|  | 1065 | )); | 
| Joel Galenson | 33c04ad | 2020-08-03 11:04:38 -0700 | [diff] [blame] | 1066 | } | 
|  | 1067 | } | 
| Janis Danisevskis | 93927dd | 2020-12-23 12:23:08 -0800 | [diff] [blame] | 1068 | let updated = tx | 
|  | 1069 | .execute( | 
|  | 1070 | "UPDATE persistent.keyentry | 
|  | 1071 | SET alias = NULL, domain = NULL, namespace = NULL, state = ? | 
| Joel Galenson | 33c04ad | 2020-08-03 11:04:38 -0700 | [diff] [blame] | 1072 | WHERE alias = ? AND domain = ? AND namespace = ?;", | 
| Janis Danisevskis | 93927dd | 2020-12-23 12:23:08 -0800 | [diff] [blame] | 1073 | params![KeyLifeCycle::Unreferenced, alias, domain.0 as u32, namespace], | 
|  | 1074 | ) | 
|  | 1075 | .context("In rebind_alias_internal: Failed to rebind existing entry.")?; | 
|  | 1076 | if updated != 0 { | 
|  | 1077 | Gc::notify_gc(); | 
|  | 1078 | } | 
| Joel Galenson | 33c04ad | 2020-08-03 11:04:38 -0700 | [diff] [blame] | 1079 | let result = tx | 
|  | 1080 | .execute( | 
|  | 1081 | "UPDATE persistent.keyentry | 
| Janis Danisevskis | 93927dd | 2020-12-23 12:23:08 -0800 | [diff] [blame] | 1082 | SET alias = ?, state = ? | 
|  | 1083 | WHERE id = ? AND domain = ? AND namespace = ? AND state = ?;", | 
|  | 1084 | params![ | 
|  | 1085 | alias, | 
|  | 1086 | KeyLifeCycle::Live, | 
|  | 1087 | newid.0, | 
|  | 1088 | domain.0 as u32, | 
|  | 1089 | namespace, | 
|  | 1090 | KeyLifeCycle::Existing | 
|  | 1091 | ], | 
| Joel Galenson | 33c04ad | 2020-08-03 11:04:38 -0700 | [diff] [blame] | 1092 | ) | 
| Janis Danisevskis | 93927dd | 2020-12-23 12:23:08 -0800 | [diff] [blame] | 1093 | .context("In rebind_alias_internal: Failed to set alias.")?; | 
| Joel Galenson | 33c04ad | 2020-08-03 11:04:38 -0700 | [diff] [blame] | 1094 | if result != 1 { | 
| Joel Galenson | 33c04ad | 2020-08-03 11:04:38 -0700 | [diff] [blame] | 1095 | return Err(KsError::sys()).context(format!( | 
| Janis Danisevskis | 93927dd | 2020-12-23 12:23:08 -0800 | [diff] [blame] | 1096 | "In rebind_alias_internal: Expected to update a single entry but instead updated {}.", | 
| Joel Galenson | 33c04ad | 2020-08-03 11:04:38 -0700 | [diff] [blame] | 1097 | result | 
|  | 1098 | )); | 
|  | 1099 | } | 
| Janis Danisevskis | 93927dd | 2020-12-23 12:23:08 -0800 | [diff] [blame] | 1100 | Ok(()) | 
|  | 1101 | } | 
|  | 1102 |  | 
|  | 1103 | /// Store a new key in a single transaction. | 
|  | 1104 | /// The function creates a new key entry, populates the blob, key parameter, and metadata | 
|  | 1105 | /// fields, and rebinds the given alias to the new key. | 
|  | 1106 | pub fn store_new_key<'a>( | 
|  | 1107 | &mut self, | 
|  | 1108 | key: KeyDescriptor, | 
|  | 1109 | params: impl IntoIterator<Item = &'a KeyParameter>, | 
|  | 1110 | blob: &[u8], | 
|  | 1111 | cert: Option<&[u8]>, | 
|  | 1112 | cert_chain: Option<&[u8]>, | 
|  | 1113 | metadata: &KeyMetaData, | 
|  | 1114 | ) -> Result<KeyIdGuard> { | 
|  | 1115 | let (alias, domain, namespace) = match key { | 
|  | 1116 | KeyDescriptor { alias: Some(alias), domain: Domain::APP, nspace, blob: None } | 
|  | 1117 | | KeyDescriptor { alias: Some(alias), domain: Domain::SELINUX, nspace, blob: None } => { | 
|  | 1118 | (alias, key.domain, nspace) | 
|  | 1119 | } | 
|  | 1120 | _ => { | 
|  | 1121 | return Err(KsError::Rc(ResponseCode::INVALID_ARGUMENT)) | 
|  | 1122 | .context("In store_new_key: Need alias and domain must be APP or SELINUX.") | 
|  | 1123 | } | 
|  | 1124 | }; | 
|  | 1125 | self.with_transaction(TransactionBehavior::Immediate, |tx| { | 
|  | 1126 | let key_id = Self::create_key_entry_internal(tx, domain, namespace) | 
|  | 1127 | .context("Trying to create new key entry.")?; | 
|  | 1128 | Self::insert_blob_internal(tx, key_id.id(), SubComponentType::KEY_BLOB, blob) | 
|  | 1129 | .context("Trying to insert the key blob.")?; | 
|  | 1130 | if let Some(cert) = cert { | 
|  | 1131 | Self::insert_blob_internal(tx, key_id.id(), SubComponentType::CERT, cert) | 
|  | 1132 | .context("Trying to insert the certificate.")?; | 
|  | 1133 | } | 
|  | 1134 | if let Some(cert_chain) = cert_chain { | 
|  | 1135 | Self::insert_blob_internal( | 
|  | 1136 | tx, | 
|  | 1137 | key_id.id(), | 
|  | 1138 | SubComponentType::CERT_CHAIN, | 
|  | 1139 | cert_chain, | 
|  | 1140 | ) | 
|  | 1141 | .context("Trying to insert the certificate chain.")?; | 
|  | 1142 | } | 
|  | 1143 | Self::insert_keyparameter_internal(tx, &key_id, params) | 
|  | 1144 | .context("Trying to insert key parameters.")?; | 
|  | 1145 | metadata.store_in_db(key_id.id(), tx).context("Tryin to insert key metadata.")?; | 
|  | 1146 | Self::rebind_alias_internal(tx, &key_id, &alias, domain, namespace) | 
|  | 1147 | .context("Trying to rebind alias.")?; | 
|  | 1148 | Ok(key_id) | 
|  | 1149 | }) | 
|  | 1150 | .context("In store_new_key.") | 
| Janis Danisevskis | 63f7bc8 | 2020-09-03 10:12:56 -0700 | [diff] [blame] | 1151 | } | 
|  | 1152 |  | 
|  | 1153 | // Helper function loading the key_id given the key descriptor | 
|  | 1154 | // tuple comprising domain, namespace, and alias. | 
|  | 1155 | // Requires a valid transaction. | 
| Janis Danisevskis | b42fc18 | 2020-12-15 08:41:27 -0800 | [diff] [blame] | 1156 | fn load_key_entry_id(tx: &Transaction, key: &KeyDescriptor, key_type: KeyType) -> Result<i64> { | 
| Janis Danisevskis | 63f7bc8 | 2020-09-03 10:12:56 -0700 | [diff] [blame] | 1157 | let alias = key | 
|  | 1158 | .alias | 
|  | 1159 | .as_ref() | 
|  | 1160 | .map_or_else(|| Err(KsError::sys()), Ok) | 
|  | 1161 | .context("In load_key_entry_id: Alias must be specified.")?; | 
|  | 1162 | let mut stmt = tx | 
|  | 1163 | .prepare( | 
|  | 1164 | "SELECT id FROM persistent.keyentry | 
|  | 1165 | WHERE | 
| Janis Danisevskis | b42fc18 | 2020-12-15 08:41:27 -0800 | [diff] [blame] | 1166 | key_type =  ? | 
|  | 1167 | AND domain = ? | 
| Janis Danisevskis | 63f7bc8 | 2020-09-03 10:12:56 -0700 | [diff] [blame] | 1168 | AND namespace = ? | 
| Janis Danisevskis | 93927dd | 2020-12-23 12:23:08 -0800 | [diff] [blame] | 1169 | AND alias = ? | 
|  | 1170 | AND state = ?;", | 
| Janis Danisevskis | 63f7bc8 | 2020-09-03 10:12:56 -0700 | [diff] [blame] | 1171 | ) | 
|  | 1172 | .context("In load_key_entry_id: Failed to select from keyentry table.")?; | 
|  | 1173 | let mut rows = stmt | 
| Janis Danisevskis | 93927dd | 2020-12-23 12:23:08 -0800 | [diff] [blame] | 1174 | .query(params![key_type, key.domain.0 as u32, key.nspace, alias, KeyLifeCycle::Live]) | 
| Janis Danisevskis | 63f7bc8 | 2020-09-03 10:12:56 -0700 | [diff] [blame] | 1175 | .context("In load_key_entry_id: Failed to read from keyentry table.")?; | 
| Janis Danisevskis | bf15d73 | 2020-12-08 10:35:26 -0800 | [diff] [blame] | 1176 | db_utils::with_rows_extract_one(&mut rows, |row| { | 
| Janis Danisevskis | c5b210b | 2020-09-11 13:27:37 -0700 | [diff] [blame] | 1177 | row.map_or_else(|| Err(KsError::Rc(ResponseCode::KEY_NOT_FOUND)), Ok)? | 
| Janis Danisevskis | 63f7bc8 | 2020-09-03 10:12:56 -0700 | [diff] [blame] | 1178 | .get(0) | 
|  | 1179 | .context("Failed to unpack id.") | 
|  | 1180 | }) | 
|  | 1181 | .context("In load_key_entry_id.") | 
|  | 1182 | } | 
|  | 1183 |  | 
|  | 1184 | /// This helper function completes the access tuple of a key, which is required | 
|  | 1185 | /// to perform access control. The strategy depends on the `domain` field in the | 
|  | 1186 | /// key descriptor. | 
| Janis Danisevskis | c5b210b | 2020-09-11 13:27:37 -0700 | [diff] [blame] | 1187 | /// * Domain::SELINUX: The access tuple is complete and this function only loads | 
| Janis Danisevskis | 63f7bc8 | 2020-09-03 10:12:56 -0700 | [diff] [blame] | 1188 | ///       the key_id for further processing. | 
| Janis Danisevskis | c5b210b | 2020-09-11 13:27:37 -0700 | [diff] [blame] | 1189 | /// * Domain::APP: Like Domain::SELINUX, but the tuple is completed by `caller_uid` | 
| Janis Danisevskis | 63f7bc8 | 2020-09-03 10:12:56 -0700 | [diff] [blame] | 1190 | ///       which serves as the namespace. | 
| Janis Danisevskis | c5b210b | 2020-09-11 13:27:37 -0700 | [diff] [blame] | 1191 | /// * Domain::GRANT: The grant table is queried for the `key_id` and the | 
| Janis Danisevskis | 63f7bc8 | 2020-09-03 10:12:56 -0700 | [diff] [blame] | 1192 | ///       `access_vector`. | 
| Janis Danisevskis | c5b210b | 2020-09-11 13:27:37 -0700 | [diff] [blame] | 1193 | /// * Domain::KEY_ID: The keyentry table is queried for the owning `domain` and | 
| Janis Danisevskis | 63f7bc8 | 2020-09-03 10:12:56 -0700 | [diff] [blame] | 1194 | ///       `namespace`. | 
|  | 1195 | /// In each case the information returned is sufficient to perform the access | 
|  | 1196 | /// check and the key id can be used to load further key artifacts. | 
|  | 1197 | fn load_access_tuple( | 
|  | 1198 | tx: &Transaction, | 
|  | 1199 | key: KeyDescriptor, | 
| Janis Danisevskis | b42fc18 | 2020-12-15 08:41:27 -0800 | [diff] [blame] | 1200 | key_type: KeyType, | 
| Janis Danisevskis | 63f7bc8 | 2020-09-03 10:12:56 -0700 | [diff] [blame] | 1201 | caller_uid: u32, | 
|  | 1202 | ) -> Result<(i64, KeyDescriptor, Option<KeyPermSet>)> { | 
|  | 1203 | match key.domain { | 
|  | 1204 | // Domain App or SELinux. In this case we load the key_id from | 
|  | 1205 | // the keyentry database for further loading of key components. | 
|  | 1206 | // We already have the full access tuple to perform access control. | 
|  | 1207 | // The only distinction is that we use the caller_uid instead | 
|  | 1208 | // of the caller supplied namespace if the domain field is | 
| Janis Danisevskis | c5b210b | 2020-09-11 13:27:37 -0700 | [diff] [blame] | 1209 | // Domain::APP. | 
|  | 1210 | Domain::APP | Domain::SELINUX => { | 
| Janis Danisevskis | 63f7bc8 | 2020-09-03 10:12:56 -0700 | [diff] [blame] | 1211 | let mut access_key = key; | 
| Janis Danisevskis | c5b210b | 2020-09-11 13:27:37 -0700 | [diff] [blame] | 1212 | if access_key.domain == Domain::APP { | 
|  | 1213 | access_key.nspace = caller_uid as i64; | 
| Janis Danisevskis | 63f7bc8 | 2020-09-03 10:12:56 -0700 | [diff] [blame] | 1214 | } | 
| Janis Danisevskis | b42fc18 | 2020-12-15 08:41:27 -0800 | [diff] [blame] | 1215 | let key_id = Self::load_key_entry_id(&tx, &access_key, key_type) | 
| Janis Danisevskis | c5b210b | 2020-09-11 13:27:37 -0700 | [diff] [blame] | 1216 | .with_context(|| format!("With key.domain = {:?}.", access_key.domain))?; | 
| Janis Danisevskis | 63f7bc8 | 2020-09-03 10:12:56 -0700 | [diff] [blame] | 1217 |  | 
|  | 1218 | Ok((key_id, access_key, None)) | 
|  | 1219 | } | 
|  | 1220 |  | 
| Janis Danisevskis | c5b210b | 2020-09-11 13:27:37 -0700 | [diff] [blame] | 1221 | // Domain::GRANT. In this case we load the key_id and the access_vector | 
| Janis Danisevskis | 63f7bc8 | 2020-09-03 10:12:56 -0700 | [diff] [blame] | 1222 | // from the grant table. | 
| Janis Danisevskis | c5b210b | 2020-09-11 13:27:37 -0700 | [diff] [blame] | 1223 | Domain::GRANT => { | 
| Janis Danisevskis | 63f7bc8 | 2020-09-03 10:12:56 -0700 | [diff] [blame] | 1224 | let mut stmt = tx | 
|  | 1225 | .prepare( | 
| Janis Danisevskis | bf15d73 | 2020-12-08 10:35:26 -0800 | [diff] [blame] | 1226 | "SELECT keyentryid, access_vector FROM persistent.grant | 
| Janis Danisevskis | 63f7bc8 | 2020-09-03 10:12:56 -0700 | [diff] [blame] | 1227 | WHERE grantee = ? AND id = ?;", | 
|  | 1228 | ) | 
| Janis Danisevskis | c5b210b | 2020-09-11 13:27:37 -0700 | [diff] [blame] | 1229 | .context("Domain::GRANT prepare statement failed")?; | 
| Janis Danisevskis | 63f7bc8 | 2020-09-03 10:12:56 -0700 | [diff] [blame] | 1230 | let mut rows = stmt | 
| Janis Danisevskis | c5b210b | 2020-09-11 13:27:37 -0700 | [diff] [blame] | 1231 | .query(params![caller_uid as i64, key.nspace]) | 
| Janis Danisevskis | 63f7bc8 | 2020-09-03 10:12:56 -0700 | [diff] [blame] | 1232 | .context("Domain:Grant: query failed.")?; | 
|  | 1233 | let (key_id, access_vector): (i64, i32) = | 
| Janis Danisevskis | bf15d73 | 2020-12-08 10:35:26 -0800 | [diff] [blame] | 1234 | db_utils::with_rows_extract_one(&mut rows, |row| { | 
| Janis Danisevskis | c5b210b | 2020-09-11 13:27:37 -0700 | [diff] [blame] | 1235 | let r = | 
|  | 1236 | row.map_or_else(|| Err(KsError::Rc(ResponseCode::KEY_NOT_FOUND)), Ok)?; | 
| Janis Danisevskis | 63f7bc8 | 2020-09-03 10:12:56 -0700 | [diff] [blame] | 1237 | Ok(( | 
|  | 1238 | r.get(0).context("Failed to unpack key_id.")?, | 
|  | 1239 | r.get(1).context("Failed to unpack access_vector.")?, | 
|  | 1240 | )) | 
|  | 1241 | }) | 
| Janis Danisevskis | c5b210b | 2020-09-11 13:27:37 -0700 | [diff] [blame] | 1242 | .context("Domain::GRANT.")?; | 
| Janis Danisevskis | 63f7bc8 | 2020-09-03 10:12:56 -0700 | [diff] [blame] | 1243 | Ok((key_id, key, Some(access_vector.into()))) | 
|  | 1244 | } | 
|  | 1245 |  | 
| Janis Danisevskis | c5b210b | 2020-09-11 13:27:37 -0700 | [diff] [blame] | 1246 | // Domain::KEY_ID. In this case we load the domain and namespace from the | 
| Janis Danisevskis | 63f7bc8 | 2020-09-03 10:12:56 -0700 | [diff] [blame] | 1247 | // keyentry database because we need them for access control. | 
| Janis Danisevskis | c5b210b | 2020-09-11 13:27:37 -0700 | [diff] [blame] | 1248 | Domain::KEY_ID => { | 
| Janis Danisevskis | 63f7bc8 | 2020-09-03 10:12:56 -0700 | [diff] [blame] | 1249 | let mut stmt = tx | 
|  | 1250 | .prepare( | 
|  | 1251 | "SELECT domain, namespace FROM persistent.keyentry | 
|  | 1252 | WHERE | 
| Janis Danisevskis | 93927dd | 2020-12-23 12:23:08 -0800 | [diff] [blame] | 1253 | id = ? | 
|  | 1254 | AND state = ?;", | 
| Janis Danisevskis | 63f7bc8 | 2020-09-03 10:12:56 -0700 | [diff] [blame] | 1255 | ) | 
| Janis Danisevskis | c5b210b | 2020-09-11 13:27:37 -0700 | [diff] [blame] | 1256 | .context("Domain::KEY_ID: prepare statement failed")?; | 
| Janis Danisevskis | 93927dd | 2020-12-23 12:23:08 -0800 | [diff] [blame] | 1257 | let mut rows = stmt | 
|  | 1258 | .query(params![key.nspace, KeyLifeCycle::Live]) | 
|  | 1259 | .context("Domain::KEY_ID: query failed.")?; | 
| Janis Danisevskis | c5b210b | 2020-09-11 13:27:37 -0700 | [diff] [blame] | 1260 | let (domain, namespace): (Domain, i64) = | 
| Janis Danisevskis | bf15d73 | 2020-12-08 10:35:26 -0800 | [diff] [blame] | 1261 | db_utils::with_rows_extract_one(&mut rows, |row| { | 
| Janis Danisevskis | c5b210b | 2020-09-11 13:27:37 -0700 | [diff] [blame] | 1262 | let r = | 
|  | 1263 | row.map_or_else(|| Err(KsError::Rc(ResponseCode::KEY_NOT_FOUND)), Ok)?; | 
| Janis Danisevskis | 63f7bc8 | 2020-09-03 10:12:56 -0700 | [diff] [blame] | 1264 | Ok(( | 
| Janis Danisevskis | c5b210b | 2020-09-11 13:27:37 -0700 | [diff] [blame] | 1265 | Domain(r.get(0).context("Failed to unpack domain.")?), | 
| Janis Danisevskis | 63f7bc8 | 2020-09-03 10:12:56 -0700 | [diff] [blame] | 1266 | r.get(1).context("Failed to unpack namespace.")?, | 
|  | 1267 | )) | 
|  | 1268 | }) | 
| Janis Danisevskis | c5b210b | 2020-09-11 13:27:37 -0700 | [diff] [blame] | 1269 | .context("Domain::KEY_ID.")?; | 
|  | 1270 | let key_id = key.nspace; | 
| Janis Danisevskis | 63f7bc8 | 2020-09-03 10:12:56 -0700 | [diff] [blame] | 1271 | let mut access_key = key; | 
|  | 1272 | access_key.domain = domain; | 
| Janis Danisevskis | c5b210b | 2020-09-11 13:27:37 -0700 | [diff] [blame] | 1273 | access_key.nspace = namespace; | 
| Janis Danisevskis | 63f7bc8 | 2020-09-03 10:12:56 -0700 | [diff] [blame] | 1274 |  | 
|  | 1275 | Ok((key_id, access_key, None)) | 
|  | 1276 | } | 
|  | 1277 | _ => Err(anyhow!(KsError::sys())), | 
|  | 1278 | } | 
|  | 1279 | } | 
|  | 1280 |  | 
| Janis Danisevskis | 3f322cb | 2020-09-03 14:46:22 -0700 | [diff] [blame] | 1281 | fn load_blob_components( | 
|  | 1282 | key_id: i64, | 
|  | 1283 | load_bits: KeyEntryLoadBits, | 
|  | 1284 | tx: &Transaction, | 
| Janis Danisevskis | 93927dd | 2020-12-23 12:23:08 -0800 | [diff] [blame] | 1285 | ) -> Result<(Option<Vec<u8>>, Option<Vec<u8>>, Option<Vec<u8>>)> { | 
| Janis Danisevskis | 3f322cb | 2020-09-03 14:46:22 -0700 | [diff] [blame] | 1286 | let mut stmt = tx | 
|  | 1287 | .prepare( | 
| Janis Danisevskis | 93927dd | 2020-12-23 12:23:08 -0800 | [diff] [blame] | 1288 | "SELECT MAX(id), subcomponent_type, blob FROM persistent.blobentry | 
| Janis Danisevskis | 3f322cb | 2020-09-03 14:46:22 -0700 | [diff] [blame] | 1289 | WHERE keyentryid = ? GROUP BY subcomponent_type;", | 
|  | 1290 | ) | 
|  | 1291 | .context("In load_blob_components: prepare statement failed.")?; | 
|  | 1292 |  | 
|  | 1293 | let mut rows = | 
|  | 1294 | stmt.query(params![key_id]).context("In load_blob_components: query failed.")?; | 
|  | 1295 |  | 
| Janis Danisevskis | 3f322cb | 2020-09-03 14:46:22 -0700 | [diff] [blame] | 1296 | let mut km_blob: Option<Vec<u8>> = None; | 
|  | 1297 | let mut cert_blob: Option<Vec<u8>> = None; | 
|  | 1298 | let mut cert_chain_blob: Option<Vec<u8>> = None; | 
| Janis Danisevskis | bf15d73 | 2020-12-08 10:35:26 -0800 | [diff] [blame] | 1299 | db_utils::with_rows_extract_all(&mut rows, |row| { | 
| Janis Danisevskis | 3f322cb | 2020-09-03 14:46:22 -0700 | [diff] [blame] | 1300 | let sub_type: SubComponentType = | 
| Janis Danisevskis | 93927dd | 2020-12-23 12:23:08 -0800 | [diff] [blame] | 1301 | row.get(1).context("Failed to extract subcomponent_type.")?; | 
|  | 1302 | match (sub_type, load_bits.load_public(), load_bits.load_km()) { | 
|  | 1303 | (SubComponentType::KEY_BLOB, _, true) => { | 
|  | 1304 | km_blob = Some(row.get(2).context("Failed to extract KM blob.")?); | 
| Janis Danisevskis | 3f322cb | 2020-09-03 14:46:22 -0700 | [diff] [blame] | 1305 | } | 
| Janis Danisevskis | 93927dd | 2020-12-23 12:23:08 -0800 | [diff] [blame] | 1306 | (SubComponentType::CERT, true, _) => { | 
| Janis Danisevskis | 3f322cb | 2020-09-03 14:46:22 -0700 | [diff] [blame] | 1307 | cert_blob = | 
| Janis Danisevskis | 93927dd | 2020-12-23 12:23:08 -0800 | [diff] [blame] | 1308 | Some(row.get(2).context("Failed to extract public certificate blob.")?); | 
| Janis Danisevskis | 3f322cb | 2020-09-03 14:46:22 -0700 | [diff] [blame] | 1309 | } | 
| Janis Danisevskis | 93927dd | 2020-12-23 12:23:08 -0800 | [diff] [blame] | 1310 | (SubComponentType::CERT_CHAIN, true, _) => { | 
| Janis Danisevskis | 3f322cb | 2020-09-03 14:46:22 -0700 | [diff] [blame] | 1311 | cert_chain_blob = | 
| Janis Danisevskis | 93927dd | 2020-12-23 12:23:08 -0800 | [diff] [blame] | 1312 | Some(row.get(2).context("Failed to extract certificate chain blob.")?); | 
| Janis Danisevskis | 3f322cb | 2020-09-03 14:46:22 -0700 | [diff] [blame] | 1313 | } | 
| Janis Danisevskis | 93927dd | 2020-12-23 12:23:08 -0800 | [diff] [blame] | 1314 | (SubComponentType::CERT, _, _) | 
|  | 1315 | | (SubComponentType::CERT_CHAIN, _, _) | 
|  | 1316 | | (SubComponentType::KEY_BLOB, _, _) => {} | 
| Janis Danisevskis | 3f322cb | 2020-09-03 14:46:22 -0700 | [diff] [blame] | 1317 | _ => Err(KsError::sys()).context("Unknown subcomponent type.")?, | 
|  | 1318 | } | 
|  | 1319 | Ok(()) | 
|  | 1320 | }) | 
|  | 1321 | .context("In load_blob_components.")?; | 
|  | 1322 |  | 
| Janis Danisevskis | 93927dd | 2020-12-23 12:23:08 -0800 | [diff] [blame] | 1323 | Ok((km_blob, cert_blob, cert_chain_blob)) | 
| Janis Danisevskis | 3f322cb | 2020-09-03 14:46:22 -0700 | [diff] [blame] | 1324 | } | 
|  | 1325 |  | 
|  | 1326 | fn load_key_parameters(key_id: i64, tx: &Transaction) -> Result<Vec<KeyParameter>> { | 
|  | 1327 | let mut stmt = tx | 
|  | 1328 | .prepare( | 
|  | 1329 | "SELECT tag, data, security_level from persistent.keyparameter | 
|  | 1330 | WHERE keyentryid = ?;", | 
|  | 1331 | ) | 
|  | 1332 | .context("In load_key_parameters: prepare statement failed.")?; | 
|  | 1333 |  | 
|  | 1334 | let mut parameters: Vec<KeyParameter> = Vec::new(); | 
|  | 1335 |  | 
|  | 1336 | let mut rows = | 
|  | 1337 | stmt.query(params![key_id]).context("In load_key_parameters: query failed.")?; | 
| Janis Danisevskis | bf15d73 | 2020-12-08 10:35:26 -0800 | [diff] [blame] | 1338 | db_utils::with_rows_extract_all(&mut rows, |row| { | 
| Janis Danisevskis | c5b210b | 2020-09-11 13:27:37 -0700 | [diff] [blame] | 1339 | let tag = Tag(row.get(0).context("Failed to read tag.")?); | 
|  | 1340 | let sec_level = SecurityLevel(row.get(2).context("Failed to read sec_level.")?); | 
| Janis Danisevskis | 3f322cb | 2020-09-03 14:46:22 -0700 | [diff] [blame] | 1341 | parameters.push( | 
|  | 1342 | KeyParameter::new_from_sql(tag, &SqlField::new(1, &row), sec_level) | 
|  | 1343 | .context("Failed to read KeyParameter.")?, | 
|  | 1344 | ); | 
|  | 1345 | Ok(()) | 
|  | 1346 | }) | 
|  | 1347 | .context("In load_key_parameters.")?; | 
|  | 1348 |  | 
|  | 1349 | Ok(parameters) | 
|  | 1350 | } | 
|  | 1351 |  | 
| Janis Danisevskis | 63f7bc8 | 2020-09-03 10:12:56 -0700 | [diff] [blame] | 1352 | /// Load a key entry by the given key descriptor. | 
|  | 1353 | /// It uses the `check_permission` callback to verify if the access is allowed | 
|  | 1354 | /// given the key access tuple read from the database using `load_access_tuple`. | 
|  | 1355 | /// With `load_bits` the caller may specify which blobs shall be loaded from | 
|  | 1356 | /// the blob database. | 
|  | 1357 | pub fn load_key_entry( | 
|  | 1358 | &mut self, | 
|  | 1359 | key: KeyDescriptor, | 
| Janis Danisevskis | b42fc18 | 2020-12-15 08:41:27 -0800 | [diff] [blame] | 1360 | key_type: KeyType, | 
| Janis Danisevskis | 63f7bc8 | 2020-09-03 10:12:56 -0700 | [diff] [blame] | 1361 | load_bits: KeyEntryLoadBits, | 
|  | 1362 | caller_uid: u32, | 
|  | 1363 | check_permission: impl FnOnce(&KeyDescriptor, Option<KeyPermSet>) -> Result<()>, | 
| Janis Danisevskis | aec1459 | 2020-11-12 09:41:49 -0800 | [diff] [blame] | 1364 | ) -> Result<(KeyIdGuard, KeyEntry)> { | 
|  | 1365 | // KEY ID LOCK 1/2 | 
|  | 1366 | // If we got a key descriptor with a key id we can get the lock right away. | 
|  | 1367 | // Otherwise we have to defer it until we know the key id. | 
|  | 1368 | let key_id_guard = match key.domain { | 
|  | 1369 | Domain::KEY_ID => Some(KEY_ID_LOCK.get(key.nspace)), | 
|  | 1370 | _ => None, | 
|  | 1371 | }; | 
|  | 1372 |  | 
| Janis Danisevskis | 63f7bc8 | 2020-09-03 10:12:56 -0700 | [diff] [blame] | 1373 | let tx = self | 
|  | 1374 | .conn | 
| Janis Danisevskis | aec1459 | 2020-11-12 09:41:49 -0800 | [diff] [blame] | 1375 | .unchecked_transaction() | 
| Janis Danisevskis | 63f7bc8 | 2020-09-03 10:12:56 -0700 | [diff] [blame] | 1376 | .context("In load_key_entry: Failed to initialize transaction.")?; | 
|  | 1377 |  | 
|  | 1378 | // Load the key_id and complete the access control tuple. | 
|  | 1379 | let (key_id, access_key_descriptor, access_vector) = | 
| Janis Danisevskis | b42fc18 | 2020-12-15 08:41:27 -0800 | [diff] [blame] | 1380 | Self::load_access_tuple(&tx, key, key_type, caller_uid) | 
|  | 1381 | .context("In load_key_entry.")?; | 
| Janis Danisevskis | 63f7bc8 | 2020-09-03 10:12:56 -0700 | [diff] [blame] | 1382 |  | 
|  | 1383 | // Perform access control. It is vital that we return here if the permission is denied. | 
|  | 1384 | // So do not touch that '?' at the end. | 
| Janis Danisevskis | 3f322cb | 2020-09-03 14:46:22 -0700 | [diff] [blame] | 1385 | check_permission(&access_key_descriptor, access_vector).context("In load_key_entry.")?; | 
| Janis Danisevskis | 63f7bc8 | 2020-09-03 10:12:56 -0700 | [diff] [blame] | 1386 |  | 
| Janis Danisevskis | aec1459 | 2020-11-12 09:41:49 -0800 | [diff] [blame] | 1387 | // KEY ID LOCK 2/2 | 
|  | 1388 | // If we did not get a key id lock by now, it was because we got a key descriptor | 
|  | 1389 | // without a key id. At this point we got the key id, so we can try and get a lock. | 
|  | 1390 | // However, we cannot block here, because we are in the middle of the transaction. | 
|  | 1391 | // So first we try to get the lock non blocking. If that fails, we roll back the | 
|  | 1392 | // transaction and block until we get the lock. After we successfully got the lock, | 
|  | 1393 | // we start a new transaction and load the access tuple again. | 
|  | 1394 | // | 
|  | 1395 | // We don't need to perform access control again, because we already established | 
|  | 1396 | // that the caller had access to the given key. But we need to make sure that the | 
|  | 1397 | // key id still exists. So we have to load the key entry by key id this time. | 
|  | 1398 | let (key_id_guard, tx) = match key_id_guard { | 
|  | 1399 | None => match KEY_ID_LOCK.try_get(key_id) { | 
|  | 1400 | None => { | 
|  | 1401 | // Roll back the transaction. | 
|  | 1402 | tx.rollback().context("In load_key_entry: Failed to roll back transaction.")?; | 
| Janis Danisevskis | 63f7bc8 | 2020-09-03 10:12:56 -0700 | [diff] [blame] | 1403 |  | 
| Janis Danisevskis | aec1459 | 2020-11-12 09:41:49 -0800 | [diff] [blame] | 1404 | // Block until we have a key id lock. | 
|  | 1405 | let key_id_guard = KEY_ID_LOCK.get(key_id); | 
|  | 1406 |  | 
|  | 1407 | // Create a new transaction. | 
|  | 1408 | let tx = self.conn.unchecked_transaction().context( | 
|  | 1409 | "In load_key_entry: Failed to initialize transaction. (deferred key lock)", | 
|  | 1410 | )?; | 
|  | 1411 |  | 
|  | 1412 | Self::load_access_tuple( | 
|  | 1413 | &tx, | 
|  | 1414 | // This time we have to load the key by the retrieved key id, because the | 
|  | 1415 | // alias may have been rebound after we rolled back the transaction. | 
|  | 1416 | KeyDescriptor { | 
|  | 1417 | domain: Domain::KEY_ID, | 
|  | 1418 | nspace: key_id, | 
|  | 1419 | ..Default::default() | 
|  | 1420 | }, | 
| Janis Danisevskis | b42fc18 | 2020-12-15 08:41:27 -0800 | [diff] [blame] | 1421 | key_type, | 
| Janis Danisevskis | aec1459 | 2020-11-12 09:41:49 -0800 | [diff] [blame] | 1422 | caller_uid, | 
|  | 1423 | ) | 
|  | 1424 | .context("In load_key_entry. (deferred key lock)")?; | 
|  | 1425 | (key_id_guard, tx) | 
|  | 1426 | } | 
|  | 1427 | Some(l) => (l, tx), | 
|  | 1428 | }, | 
|  | 1429 | Some(key_id_guard) => (key_id_guard, tx), | 
|  | 1430 | }; | 
|  | 1431 |  | 
| Janis Danisevskis | b42fc18 | 2020-12-15 08:41:27 -0800 | [diff] [blame] | 1432 | let key_entry = Self::load_key_components(&tx, load_bits, key_id_guard.id()) | 
|  | 1433 | .context("In load_key_entry.")?; | 
| Janis Danisevskis | 63f7bc8 | 2020-09-03 10:12:56 -0700 | [diff] [blame] | 1434 |  | 
| Janis Danisevskis | 3f322cb | 2020-09-03 14:46:22 -0700 | [diff] [blame] | 1435 | tx.commit().context("In load_key_entry: Failed to commit transaction.")?; | 
|  | 1436 |  | 
| Janis Danisevskis | b42fc18 | 2020-12-15 08:41:27 -0800 | [diff] [blame] | 1437 | Ok((key_id_guard, key_entry)) | 
|  | 1438 | } | 
|  | 1439 |  | 
| Janis Danisevskis | 93927dd | 2020-12-23 12:23:08 -0800 | [diff] [blame] | 1440 | fn mark_unreferenced(tx: &Transaction, key_id: i64) -> Result<()> { | 
|  | 1441 | let updated = tx | 
|  | 1442 | .execute( | 
|  | 1443 | "UPDATE persistent.keyentry SET state = ? WHERE id = ?;", | 
|  | 1444 | params![KeyLifeCycle::Unreferenced, key_id], | 
|  | 1445 | ) | 
|  | 1446 | .context("In mark_unreferenced: Failed to update state of key entry.")?; | 
|  | 1447 | if updated != 0 { | 
|  | 1448 | Gc::notify_gc(); | 
|  | 1449 | } | 
|  | 1450 | tx.execute("DELETE from persistent.grant WHERE keyentryid = ?;", params![key_id]) | 
|  | 1451 | .context("In mark_unreferenced: Failed to drop grants.")?; | 
|  | 1452 | Ok(()) | 
|  | 1453 | } | 
|  | 1454 |  | 
|  | 1455 | /// Marks the given key as unreferenced and removes all of the grants to this key. | 
|  | 1456 | pub fn unbind_key( | 
|  | 1457 | &mut self, | 
|  | 1458 | key: KeyDescriptor, | 
|  | 1459 | key_type: KeyType, | 
|  | 1460 | caller_uid: u32, | 
|  | 1461 | check_permission: impl FnOnce(&KeyDescriptor, Option<KeyPermSet>) -> Result<()>, | 
|  | 1462 | ) -> Result<()> { | 
|  | 1463 | self.with_transaction(TransactionBehavior::Immediate, |tx| { | 
|  | 1464 | let (key_id, access_key_descriptor, access_vector) = | 
|  | 1465 | Self::load_access_tuple(tx, key, key_type, caller_uid) | 
|  | 1466 | .context("Trying to get access tuple.")?; | 
|  | 1467 |  | 
|  | 1468 | // Perform access control. It is vital that we return here if the permission is denied. | 
|  | 1469 | // So do not touch that '?' at the end. | 
|  | 1470 | check_permission(&access_key_descriptor, access_vector) | 
|  | 1471 | .context("While checking permission.")?; | 
|  | 1472 |  | 
|  | 1473 | Self::mark_unreferenced(tx, key_id).context("Trying to mark the key unreferenced.") | 
|  | 1474 | }) | 
|  | 1475 | .context("In unbind_key.") | 
|  | 1476 | } | 
|  | 1477 |  | 
| Janis Danisevskis | b42fc18 | 2020-12-15 08:41:27 -0800 | [diff] [blame] | 1478 | fn load_key_components( | 
|  | 1479 | tx: &Transaction, | 
|  | 1480 | load_bits: KeyEntryLoadBits, | 
|  | 1481 | key_id: i64, | 
|  | 1482 | ) -> Result<KeyEntry> { | 
|  | 1483 | let metadata = KeyMetaData::load_from_db(key_id, &tx).context("In load_key_components.")?; | 
|  | 1484 |  | 
| Janis Danisevskis | 93927dd | 2020-12-23 12:23:08 -0800 | [diff] [blame] | 1485 | let (km_blob, cert_blob, cert_chain_blob) = | 
| Janis Danisevskis | b42fc18 | 2020-12-15 08:41:27 -0800 | [diff] [blame] | 1486 | Self::load_blob_components(key_id, load_bits, &tx) | 
|  | 1487 | .context("In load_key_components.")?; | 
|  | 1488 |  | 
|  | 1489 | let parameters = | 
|  | 1490 | Self::load_key_parameters(key_id, &tx).context("In load_key_components.")?; | 
|  | 1491 |  | 
| Janis Danisevskis | 93927dd | 2020-12-23 12:23:08 -0800 | [diff] [blame] | 1492 | // Extract the security level by checking the security level of the origin tag. | 
|  | 1493 | // Super keys don't have key parameters so we use security_level software by default. | 
|  | 1494 | let sec_level = parameters | 
|  | 1495 | .iter() | 
|  | 1496 | .find_map(|k| match k.get_tag() { | 
|  | 1497 | Tag::ORIGIN => Some(*k.security_level()), | 
|  | 1498 | _ => None, | 
|  | 1499 | }) | 
|  | 1500 | .unwrap_or(SecurityLevel::SOFTWARE); | 
|  | 1501 |  | 
| Janis Danisevskis | b42fc18 | 2020-12-15 08:41:27 -0800 | [diff] [blame] | 1502 | Ok(KeyEntry { | 
|  | 1503 | id: key_id, | 
|  | 1504 | km_blob, | 
|  | 1505 | cert: cert_blob, | 
|  | 1506 | cert_chain: cert_chain_blob, | 
|  | 1507 | sec_level, | 
|  | 1508 | parameters, | 
|  | 1509 | metadata, | 
|  | 1510 | }) | 
| Janis Danisevskis | 63f7bc8 | 2020-09-03 10:12:56 -0700 | [diff] [blame] | 1511 | } | 
|  | 1512 |  | 
| Janis Danisevskis | e92a5e6 | 2020-12-02 12:57:41 -0800 | [diff] [blame] | 1513 | /// Returns a list of KeyDescriptors in the selected domain/namespace. | 
|  | 1514 | /// The key descriptors will have the domain, nspace, and alias field set. | 
|  | 1515 | /// Domain must be APP or SELINUX, the caller must make sure of that. | 
|  | 1516 | pub fn list(&mut self, domain: Domain, namespace: i64) -> Result<Vec<KeyDescriptor>> { | 
|  | 1517 | let mut stmt = self | 
|  | 1518 | .conn | 
|  | 1519 | .prepare( | 
|  | 1520 | "SELECT alias FROM persistent.keyentry | 
| Janis Danisevskis | 93927dd | 2020-12-23 12:23:08 -0800 | [diff] [blame] | 1521 | WHERE domain = ? AND namespace = ? AND alias IS NOT NULL AND state = ?;", | 
| Janis Danisevskis | e92a5e6 | 2020-12-02 12:57:41 -0800 | [diff] [blame] | 1522 | ) | 
|  | 1523 | .context("In list: Failed to prepare.")?; | 
|  | 1524 |  | 
| Janis Danisevskis | 93927dd | 2020-12-23 12:23:08 -0800 | [diff] [blame] | 1525 | let mut rows = stmt | 
|  | 1526 | .query(params![domain.0 as u32, namespace, KeyLifeCycle::Live]) | 
|  | 1527 | .context("In list: Failed to query.")?; | 
| Janis Danisevskis | e92a5e6 | 2020-12-02 12:57:41 -0800 | [diff] [blame] | 1528 |  | 
|  | 1529 | let mut descriptors: Vec<KeyDescriptor> = Vec::new(); | 
|  | 1530 | db_utils::with_rows_extract_all(&mut rows, |row| { | 
|  | 1531 | descriptors.push(KeyDescriptor { | 
|  | 1532 | domain, | 
|  | 1533 | nspace: namespace, | 
|  | 1534 | alias: Some(row.get(0).context("Trying to extract alias.")?), | 
|  | 1535 | blob: None, | 
|  | 1536 | }); | 
|  | 1537 | Ok(()) | 
|  | 1538 | }) | 
|  | 1539 | .context("In list.")?; | 
|  | 1540 | Ok(descriptors) | 
|  | 1541 | } | 
|  | 1542 |  | 
| Janis Danisevskis | 63f7bc8 | 2020-09-03 10:12:56 -0700 | [diff] [blame] | 1543 | /// Adds a grant to the grant table. | 
|  | 1544 | /// Like `load_key_entry` this function loads the access tuple before | 
|  | 1545 | /// it uses the callback for a permission check. Upon success, | 
|  | 1546 | /// it inserts the `grantee_uid`, `key_id`, and `access_vector` into the | 
|  | 1547 | /// grant table. The new row will have a randomized id, which is used as | 
|  | 1548 | /// grant id in the namespace field of the resulting KeyDescriptor. | 
|  | 1549 | pub fn grant( | 
|  | 1550 | &mut self, | 
|  | 1551 | key: KeyDescriptor, | 
|  | 1552 | caller_uid: u32, | 
|  | 1553 | grantee_uid: u32, | 
|  | 1554 | access_vector: KeyPermSet, | 
|  | 1555 | check_permission: impl FnOnce(&KeyDescriptor, &KeyPermSet) -> Result<()>, | 
|  | 1556 | ) -> Result<KeyDescriptor> { | 
|  | 1557 | let tx = self | 
|  | 1558 | .conn | 
|  | 1559 | .transaction_with_behavior(TransactionBehavior::Immediate) | 
|  | 1560 | .context("In grant: Failed to initialize transaction.")?; | 
|  | 1561 |  | 
|  | 1562 | // Load the key_id and complete the access control tuple. | 
|  | 1563 | // We ignore the access vector here because grants cannot be granted. | 
|  | 1564 | // The access vector returned here expresses the permissions the | 
| Janis Danisevskis | c5b210b | 2020-09-11 13:27:37 -0700 | [diff] [blame] | 1565 | // grantee has if key.domain == Domain::GRANT. But this vector | 
| Janis Danisevskis | 63f7bc8 | 2020-09-03 10:12:56 -0700 | [diff] [blame] | 1566 | // cannot include the grant permission by design, so there is no way the | 
|  | 1567 | // subsequent permission check can pass. | 
| Janis Danisevskis | c5b210b | 2020-09-11 13:27:37 -0700 | [diff] [blame] | 1568 | // We could check key.domain == Domain::GRANT and fail early. | 
| Janis Danisevskis | 63f7bc8 | 2020-09-03 10:12:56 -0700 | [diff] [blame] | 1569 | // But even if we load the access tuple by grant here, the permission | 
|  | 1570 | // check denies the attempt to create a grant by grant descriptor. | 
|  | 1571 | let (key_id, access_key_descriptor, _) = | 
| Janis Danisevskis | b42fc18 | 2020-12-15 08:41:27 -0800 | [diff] [blame] | 1572 | Self::load_access_tuple(&tx, key, KeyType::Client, caller_uid).context("In grant")?; | 
| Janis Danisevskis | 63f7bc8 | 2020-09-03 10:12:56 -0700 | [diff] [blame] | 1573 |  | 
|  | 1574 | // Perform access control. It is vital that we return here if the permission | 
|  | 1575 | // was denied. So do not touch that '?' at the end of the line. | 
|  | 1576 | // This permission check checks if the caller has the grant permission | 
|  | 1577 | // for the given key and in addition to all of the permissions | 
|  | 1578 | // expressed in `access_vector`. | 
|  | 1579 | check_permission(&access_key_descriptor, &access_vector) | 
|  | 1580 | .context("In grant: check_permission failed.")?; | 
|  | 1581 |  | 
|  | 1582 | let grant_id = if let Some(grant_id) = tx | 
|  | 1583 | .query_row( | 
| Janis Danisevskis | bf15d73 | 2020-12-08 10:35:26 -0800 | [diff] [blame] | 1584 | "SELECT id FROM persistent.grant | 
| Janis Danisevskis | 63f7bc8 | 2020-09-03 10:12:56 -0700 | [diff] [blame] | 1585 | WHERE keyentryid = ? AND grantee = ?;", | 
|  | 1586 | params![key_id, grantee_uid], | 
|  | 1587 | |row| row.get(0), | 
|  | 1588 | ) | 
|  | 1589 | .optional() | 
|  | 1590 | .context("In grant: Failed get optional existing grant id.")? | 
|  | 1591 | { | 
|  | 1592 | tx.execute( | 
| Janis Danisevskis | bf15d73 | 2020-12-08 10:35:26 -0800 | [diff] [blame] | 1593 | "UPDATE persistent.grant | 
| Janis Danisevskis | 63f7bc8 | 2020-09-03 10:12:56 -0700 | [diff] [blame] | 1594 | SET access_vector = ? | 
|  | 1595 | WHERE id = ?;", | 
|  | 1596 | params![i32::from(access_vector), grant_id], | 
|  | 1597 | ) | 
|  | 1598 | .context("In grant: Failed to update existing grant.")?; | 
|  | 1599 | grant_id | 
|  | 1600 | } else { | 
| Joel Galenson | 845f74b | 2020-09-09 14:11:55 -0700 | [diff] [blame] | 1601 | Self::insert_with_retry(|id| { | 
|  | 1602 | tx.execute( | 
| Janis Danisevskis | bf15d73 | 2020-12-08 10:35:26 -0800 | [diff] [blame] | 1603 | "INSERT INTO persistent.grant (id, grantee, keyentryid, access_vector) | 
| Janis Danisevskis | 63f7bc8 | 2020-09-03 10:12:56 -0700 | [diff] [blame] | 1604 | VALUES (?, ?, ?, ?);", | 
| Joel Galenson | 845f74b | 2020-09-09 14:11:55 -0700 | [diff] [blame] | 1605 | params![id, grantee_uid, key_id, i32::from(access_vector)], | 
|  | 1606 | ) | 
|  | 1607 | }) | 
|  | 1608 | .context("In grant")? | 
| Janis Danisevskis | 63f7bc8 | 2020-09-03 10:12:56 -0700 | [diff] [blame] | 1609 | }; | 
|  | 1610 | tx.commit().context("In grant: failed to commit transaction.")?; | 
|  | 1611 |  | 
| Janis Danisevskis | c5b210b | 2020-09-11 13:27:37 -0700 | [diff] [blame] | 1612 | Ok(KeyDescriptor { domain: Domain::GRANT, nspace: grant_id, alias: None, blob: None }) | 
| Janis Danisevskis | 63f7bc8 | 2020-09-03 10:12:56 -0700 | [diff] [blame] | 1613 | } | 
|  | 1614 |  | 
|  | 1615 | /// This function checks permissions like `grant` and `load_key_entry` | 
|  | 1616 | /// before removing a grant from the grant table. | 
|  | 1617 | pub fn ungrant( | 
|  | 1618 | &mut self, | 
|  | 1619 | key: KeyDescriptor, | 
|  | 1620 | caller_uid: u32, | 
|  | 1621 | grantee_uid: u32, | 
|  | 1622 | check_permission: impl FnOnce(&KeyDescriptor) -> Result<()>, | 
|  | 1623 | ) -> Result<()> { | 
|  | 1624 | let tx = self | 
|  | 1625 | .conn | 
|  | 1626 | .transaction_with_behavior(TransactionBehavior::Immediate) | 
|  | 1627 | .context("In ungrant: Failed to initialize transaction.")?; | 
|  | 1628 |  | 
|  | 1629 | // Load the key_id and complete the access control tuple. | 
|  | 1630 | // We ignore the access vector here because grants cannot be granted. | 
|  | 1631 | let (key_id, access_key_descriptor, _) = | 
| Janis Danisevskis | b42fc18 | 2020-12-15 08:41:27 -0800 | [diff] [blame] | 1632 | Self::load_access_tuple(&tx, key, KeyType::Client, caller_uid) | 
|  | 1633 | .context("In ungrant.")?; | 
| Janis Danisevskis | 63f7bc8 | 2020-09-03 10:12:56 -0700 | [diff] [blame] | 1634 |  | 
|  | 1635 | // Perform access control. We must return here if the permission | 
|  | 1636 | // was denied. So do not touch the '?' at the end of this line. | 
|  | 1637 | check_permission(&access_key_descriptor).context("In grant: check_permission failed.")?; | 
|  | 1638 |  | 
|  | 1639 | tx.execute( | 
| Janis Danisevskis | bf15d73 | 2020-12-08 10:35:26 -0800 | [diff] [blame] | 1640 | "DELETE FROM persistent.grant | 
| Janis Danisevskis | 63f7bc8 | 2020-09-03 10:12:56 -0700 | [diff] [blame] | 1641 | WHERE keyentryid = ? AND grantee = ?;", | 
|  | 1642 | params![key_id, grantee_uid], | 
|  | 1643 | ) | 
|  | 1644 | .context("Failed to delete grant.")?; | 
|  | 1645 |  | 
|  | 1646 | tx.commit().context("In ungrant: failed to commit transaction.")?; | 
|  | 1647 |  | 
|  | 1648 | Ok(()) | 
|  | 1649 | } | 
|  | 1650 |  | 
| Joel Galenson | 845f74b | 2020-09-09 14:11:55 -0700 | [diff] [blame] | 1651 | // Generates a random id and passes it to the given function, which will | 
|  | 1652 | // try to insert it into a database.  If that insertion fails, retry; | 
|  | 1653 | // otherwise return the id. | 
|  | 1654 | fn insert_with_retry(inserter: impl Fn(i64) -> rusqlite::Result<usize>) -> Result<i64> { | 
|  | 1655 | loop { | 
|  | 1656 | let newid: i64 = random(); | 
|  | 1657 | match inserter(newid) { | 
|  | 1658 | // If the id already existed, try again. | 
|  | 1659 | Err(rusqlite::Error::SqliteFailure( | 
|  | 1660 | libsqlite3_sys::Error { | 
|  | 1661 | code: libsqlite3_sys::ErrorCode::ConstraintViolation, | 
|  | 1662 | extended_code: libsqlite3_sys::SQLITE_CONSTRAINT_UNIQUE, | 
|  | 1663 | }, | 
|  | 1664 | _, | 
|  | 1665 | )) => (), | 
|  | 1666 | Err(e) => { | 
|  | 1667 | return Err(e).context("In insert_with_retry: failed to insert into database.") | 
|  | 1668 | } | 
|  | 1669 | _ => return Ok(newid), | 
|  | 1670 | } | 
|  | 1671 | } | 
|  | 1672 | } | 
| Hasini Gunasinghe | 557b103 | 2020-11-10 01:35:30 +0000 | [diff] [blame] | 1673 |  | 
|  | 1674 | /// Insert or replace the auth token based on the UNIQUE constraint of the auth token table | 
|  | 1675 | pub fn insert_auth_token(&mut self, auth_token: &HardwareAuthToken) -> Result<()> { | 
|  | 1676 | self.conn | 
|  | 1677 | .execute( | 
|  | 1678 | "INSERT OR REPLACE INTO perboot.authtoken (challenge, user_id, auth_id, | 
|  | 1679 | authenticator_type, timestamp, mac, time_received) VALUES(?, ?, ?, ?, ?, ?, ?);", | 
|  | 1680 | params![ | 
|  | 1681 | auth_token.challenge, | 
|  | 1682 | auth_token.userId, | 
|  | 1683 | auth_token.authenticatorId, | 
|  | 1684 | auth_token.authenticatorType.0 as i32, | 
|  | 1685 | auth_token.timestamp.milliSeconds as i64, | 
|  | 1686 | auth_token.mac, | 
|  | 1687 | MonotonicRawTime::now(), | 
|  | 1688 | ], | 
|  | 1689 | ) | 
|  | 1690 | .context("In insert_auth_token: failed to insert auth token into the database")?; | 
|  | 1691 | Ok(()) | 
|  | 1692 | } | 
| Hasini Gunasinghe | f70cf8e | 2020-11-11 01:02:41 +0000 | [diff] [blame] | 1693 |  | 
| Janis Danisevskis | 5ed8c53 | 2021-01-11 14:19:42 -0800 | [diff] [blame^] | 1694 | /// Find the newest auth token matching the given predicate. | 
|  | 1695 | pub fn find_auth_token_entry<F>( | 
| Hasini Gunasinghe | f70cf8e | 2020-11-11 01:02:41 +0000 | [diff] [blame] | 1696 | &mut self, | 
| Janis Danisevskis | 5ed8c53 | 2021-01-11 14:19:42 -0800 | [diff] [blame^] | 1697 | p: F, | 
|  | 1698 | ) -> Result<Option<(AuthTokenEntry, MonotonicRawTime)>> | 
|  | 1699 | where | 
|  | 1700 | F: Fn(&AuthTokenEntry) -> bool, | 
|  | 1701 | { | 
|  | 1702 | self.with_transaction(TransactionBehavior::Deferred, |tx| { | 
|  | 1703 | let mut stmt = tx | 
|  | 1704 | .prepare("SELECT * from perboot.authtoken ORDER BY time_received DESC;") | 
|  | 1705 | .context("Prepare statement failed.")?; | 
| Hasini Gunasinghe | f70cf8e | 2020-11-11 01:02:41 +0000 | [diff] [blame] | 1706 |  | 
| Janis Danisevskis | 5ed8c53 | 2021-01-11 14:19:42 -0800 | [diff] [blame^] | 1707 | let mut rows = stmt.query(NO_PARAMS).context("Failed to query.")?; | 
| Hasini Gunasinghe | f70cf8e | 2020-11-11 01:02:41 +0000 | [diff] [blame] | 1708 |  | 
| Janis Danisevskis | 5ed8c53 | 2021-01-11 14:19:42 -0800 | [diff] [blame^] | 1709 | while let Some(row) = rows.next().context("Failed to get next row.")? { | 
|  | 1710 | let entry = AuthTokenEntry::new( | 
| Hasini Gunasinghe | f70cf8e | 2020-11-11 01:02:41 +0000 | [diff] [blame] | 1711 | HardwareAuthToken { | 
|  | 1712 | challenge: row.get(1)?, | 
|  | 1713 | userId: row.get(2)?, | 
|  | 1714 | authenticatorId: row.get(3)?, | 
|  | 1715 | authenticatorType: HardwareAuthenticatorType(row.get(4)?), | 
|  | 1716 | timestamp: Timestamp { milliSeconds: row.get(5)? }, | 
|  | 1717 | mac: row.get(6)?, | 
|  | 1718 | }, | 
|  | 1719 | row.get(7)?, | 
| Janis Danisevskis | 5ed8c53 | 2021-01-11 14:19:42 -0800 | [diff] [blame^] | 1720 | ); | 
|  | 1721 | if p(&entry) { | 
|  | 1722 | return Ok(Some(( | 
|  | 1723 | entry, | 
|  | 1724 | Self::get_last_off_body(tx) | 
|  | 1725 | .context("In find_auth_token_entry: Trying to get last off body")?, | 
|  | 1726 | ))); | 
|  | 1727 | } | 
|  | 1728 | } | 
|  | 1729 | Ok(None) | 
|  | 1730 | }) | 
|  | 1731 | .context("In find_auth_token_entry.") | 
| Hasini Gunasinghe | f70cf8e | 2020-11-11 01:02:41 +0000 | [diff] [blame] | 1732 | } | 
|  | 1733 |  | 
| Janis Danisevskis | 5ed8c53 | 2021-01-11 14:19:42 -0800 | [diff] [blame^] | 1734 | /// Insert last_off_body into the metadata table at the initialization of auth token table | 
|  | 1735 | pub fn insert_last_off_body(&self, last_off_body: MonotonicRawTime) -> Result<()> { | 
|  | 1736 | self.conn | 
|  | 1737 | .execute( | 
|  | 1738 | "INSERT OR REPLACE INTO perboot.metadata (key, value) VALUES (?, ?);", | 
|  | 1739 | params!["last_off_body", last_off_body], | 
|  | 1740 | ) | 
|  | 1741 | .context("In insert_last_off_body: failed to insert.")?; | 
| Hasini Gunasinghe | f70cf8e | 2020-11-11 01:02:41 +0000 | [diff] [blame] | 1742 | Ok(()) | 
|  | 1743 | } | 
|  | 1744 |  | 
| Janis Danisevskis | 5ed8c53 | 2021-01-11 14:19:42 -0800 | [diff] [blame^] | 1745 | /// Update last_off_body when on_device_off_body is called | 
| Hasini Gunasinghe | f70cf8e | 2020-11-11 01:02:41 +0000 | [diff] [blame] | 1746 | pub fn update_last_off_body(&self, last_off_body: MonotonicRawTime) -> Result<()> { | 
|  | 1747 | self.conn | 
|  | 1748 | .execute( | 
|  | 1749 | "UPDATE perboot.metadata SET value = ? WHERE key = ?;", | 
|  | 1750 | params![last_off_body, "last_off_body"], | 
|  | 1751 | ) | 
|  | 1752 | .context("In update_last_off_body: failed to update.")?; | 
|  | 1753 | Ok(()) | 
|  | 1754 | } | 
|  | 1755 |  | 
| Janis Danisevskis | 5ed8c53 | 2021-01-11 14:19:42 -0800 | [diff] [blame^] | 1756 | /// Get last_off_body time when finding auth tokens | 
| Hasini Gunasinghe | f70cf8e | 2020-11-11 01:02:41 +0000 | [diff] [blame] | 1757 | fn get_last_off_body(tx: &Transaction) -> Result<MonotonicRawTime> { | 
| Janis Danisevskis | 5ed8c53 | 2021-01-11 14:19:42 -0800 | [diff] [blame^] | 1758 | tx.query_row( | 
|  | 1759 | "SELECT value from perboot.metadata WHERE key = ?;", | 
|  | 1760 | params!["last_off_body"], | 
|  | 1761 | |row| Ok(row.get(0)?), | 
|  | 1762 | ) | 
|  | 1763 | .context("In get_last_off_body: query_row failed.") | 
| Hasini Gunasinghe | f70cf8e | 2020-11-11 01:02:41 +0000 | [diff] [blame] | 1764 | } | 
| Joel Galenson | 26f4d01 | 2020-07-17 14:57:21 -0700 | [diff] [blame] | 1765 | } | 
|  | 1766 |  | 
|  | 1767 | #[cfg(test)] | 
|  | 1768 | mod tests { | 
|  | 1769 |  | 
|  | 1770 | use super::*; | 
| Janis Danisevskis | 3f322cb | 2020-09-03 14:46:22 -0700 | [diff] [blame] | 1771 | use crate::key_parameter::{ | 
|  | 1772 | Algorithm, BlockMode, Digest, EcCurve, HardwareAuthenticatorType, KeyOrigin, KeyParameter, | 
|  | 1773 | KeyParameterValue, KeyPurpose, PaddingMode, SecurityLevel, | 
|  | 1774 | }; | 
| Janis Danisevskis | 63f7bc8 | 2020-09-03 10:12:56 -0700 | [diff] [blame] | 1775 | use crate::key_perm_set; | 
|  | 1776 | use crate::permission::{KeyPerm, KeyPermSet}; | 
| Janis Danisevskis | bf15d73 | 2020-12-08 10:35:26 -0800 | [diff] [blame] | 1777 | use crate::test::utils::TempDir; | 
| Hasini Gunasinghe | 557b103 | 2020-11-10 01:35:30 +0000 | [diff] [blame] | 1778 | use android_hardware_security_keymint::aidl::android::hardware::security::keymint::{ | 
|  | 1779 | HardwareAuthToken::HardwareAuthToken, | 
|  | 1780 | HardwareAuthenticatorType::HardwareAuthenticatorType as kmhw_authenticator_type, | 
| Janis Danisevskis | c3a496b | 2021-01-05 10:37:22 -0800 | [diff] [blame] | 1781 | }; | 
|  | 1782 | use android_hardware_security_secureclock::aidl::android::hardware::security::secureclock::{ | 
| Hasini Gunasinghe | 557b103 | 2020-11-10 01:35:30 +0000 | [diff] [blame] | 1783 | Timestamp::Timestamp, | 
|  | 1784 | }; | 
| Janis Danisevskis | 63f7bc8 | 2020-09-03 10:12:56 -0700 | [diff] [blame] | 1785 | use rusqlite::NO_PARAMS; | 
| Hasini Gunasinghe | f70cf8e | 2020-11-11 01:02:41 +0000 | [diff] [blame] | 1786 | use rusqlite::{Error, TransactionBehavior}; | 
| Joel Galenson | 0891bc1 | 2020-07-20 10:37:03 -0700 | [diff] [blame] | 1787 | use std::cell::RefCell; | 
| Janis Danisevskis | aec1459 | 2020-11-12 09:41:49 -0800 | [diff] [blame] | 1788 | use std::sync::atomic::{AtomicU8, Ordering}; | 
|  | 1789 | use std::sync::Arc; | 
|  | 1790 | use std::thread; | 
| Hasini Gunasinghe | f70cf8e | 2020-11-11 01:02:41 +0000 | [diff] [blame] | 1791 | use std::time::{Duration, SystemTime}; | 
| Joel Galenson | 0891bc1 | 2020-07-20 10:37:03 -0700 | [diff] [blame] | 1792 |  | 
| Janis Danisevskis | 4df44f4 | 2020-08-26 14:40:03 -0700 | [diff] [blame] | 1793 | fn new_test_db() -> Result<KeystoreDB> { | 
|  | 1794 | let conn = KeystoreDB::make_connection("file::memory:", "file::memory:")?; | 
|  | 1795 |  | 
|  | 1796 | KeystoreDB::init_tables(&conn).context("Failed to initialize tables.")?; | 
|  | 1797 | Ok(KeystoreDB { conn }) | 
|  | 1798 | } | 
|  | 1799 |  | 
| Janis Danisevskis | b42fc18 | 2020-12-15 08:41:27 -0800 | [diff] [blame] | 1800 | #[test] | 
|  | 1801 | fn datetime() -> Result<()> { | 
|  | 1802 | let conn = Connection::open_in_memory()?; | 
|  | 1803 | conn.execute("CREATE TABLE test (ts DATETIME);", NO_PARAMS)?; | 
|  | 1804 | let now = SystemTime::now(); | 
|  | 1805 | let duration = Duration::from_secs(1000); | 
|  | 1806 | let then = now.checked_sub(duration).unwrap(); | 
|  | 1807 | let soon = now.checked_add(duration).unwrap(); | 
|  | 1808 | conn.execute( | 
|  | 1809 | "INSERT INTO test (ts) VALUES (?), (?), (?);", | 
|  | 1810 | params![DateTime::try_from(now)?, DateTime::try_from(then)?, DateTime::try_from(soon)?], | 
|  | 1811 | )?; | 
|  | 1812 | let mut stmt = conn.prepare("SELECT ts FROM test ORDER BY ts ASC;")?; | 
|  | 1813 | let mut rows = stmt.query(NO_PARAMS)?; | 
|  | 1814 | assert_eq!(DateTime::try_from(then)?, rows.next()?.unwrap().get(0)?); | 
|  | 1815 | assert_eq!(DateTime::try_from(now)?, rows.next()?.unwrap().get(0)?); | 
|  | 1816 | assert_eq!(DateTime::try_from(soon)?, rows.next()?.unwrap().get(0)?); | 
|  | 1817 | assert!(rows.next()?.is_none()); | 
|  | 1818 | assert!(DateTime::try_from(then)? < DateTime::try_from(now)?); | 
|  | 1819 | assert!(DateTime::try_from(then)? < DateTime::try_from(soon)?); | 
|  | 1820 | assert!(DateTime::try_from(now)? < DateTime::try_from(soon)?); | 
|  | 1821 | Ok(()) | 
|  | 1822 | } | 
|  | 1823 |  | 
| Joel Galenson | 0891bc1 | 2020-07-20 10:37:03 -0700 | [diff] [blame] | 1824 | // Ensure that we're using the "injected" random function, not the real one. | 
|  | 1825 | #[test] | 
|  | 1826 | fn test_mocked_random() { | 
|  | 1827 | let rand1 = random(); | 
|  | 1828 | let rand2 = random(); | 
|  | 1829 | let rand3 = random(); | 
|  | 1830 | if rand1 == rand2 { | 
|  | 1831 | assert_eq!(rand2 + 1, rand3); | 
|  | 1832 | } else { | 
|  | 1833 | assert_eq!(rand1 + 1, rand2); | 
|  | 1834 | assert_eq!(rand2, rand3); | 
|  | 1835 | } | 
|  | 1836 | } | 
| Joel Galenson | 26f4d01 | 2020-07-17 14:57:21 -0700 | [diff] [blame] | 1837 |  | 
| Joel Galenson | 26f4d01 | 2020-07-17 14:57:21 -0700 | [diff] [blame] | 1838 | // Test that we have the correct tables. | 
|  | 1839 | #[test] | 
|  | 1840 | fn test_tables() -> Result<()> { | 
| Janis Danisevskis | 4df44f4 | 2020-08-26 14:40:03 -0700 | [diff] [blame] | 1841 | let db = new_test_db()?; | 
| Joel Galenson | 26f4d01 | 2020-07-17 14:57:21 -0700 | [diff] [blame] | 1842 | let tables = db | 
|  | 1843 | .conn | 
| Joel Galenson | 2aab443 | 2020-07-22 15:27:57 -0700 | [diff] [blame] | 1844 | .prepare("SELECT name from persistent.sqlite_master WHERE type='table' ORDER BY name;")? | 
| Joel Galenson | 26f4d01 | 2020-07-17 14:57:21 -0700 | [diff] [blame] | 1845 | .query_map(params![], |row| row.get(0))? | 
|  | 1846 | .collect::<rusqlite::Result<Vec<String>>>()?; | 
| Janis Danisevskis | b42fc18 | 2020-12-15 08:41:27 -0800 | [diff] [blame] | 1847 | assert_eq!(tables.len(), 5); | 
| Janis Danisevskis | 63f7bc8 | 2020-09-03 10:12:56 -0700 | [diff] [blame] | 1848 | assert_eq!(tables[0], "blobentry"); | 
| Janis Danisevskis | bf15d73 | 2020-12-08 10:35:26 -0800 | [diff] [blame] | 1849 | assert_eq!(tables[1], "grant"); | 
|  | 1850 | assert_eq!(tables[2], "keyentry"); | 
| Janis Danisevskis | b42fc18 | 2020-12-15 08:41:27 -0800 | [diff] [blame] | 1851 | assert_eq!(tables[3], "keymetadata"); | 
|  | 1852 | assert_eq!(tables[4], "keyparameter"); | 
| Janis Danisevskis | 63f7bc8 | 2020-09-03 10:12:56 -0700 | [diff] [blame] | 1853 | let tables = db | 
|  | 1854 | .conn | 
|  | 1855 | .prepare("SELECT name from perboot.sqlite_master WHERE type='table' ORDER BY name;")? | 
|  | 1856 | .query_map(params![], |row| row.get(0))? | 
|  | 1857 | .collect::<rusqlite::Result<Vec<String>>>()?; | 
| Hasini Gunasinghe | 557b103 | 2020-11-10 01:35:30 +0000 | [diff] [blame] | 1858 |  | 
|  | 1859 | assert_eq!(tables.len(), 2); | 
|  | 1860 | assert_eq!(tables[0], "authtoken"); | 
|  | 1861 | assert_eq!(tables[1], "metadata"); | 
| Joel Galenson | 2aab443 | 2020-07-22 15:27:57 -0700 | [diff] [blame] | 1862 | Ok(()) | 
|  | 1863 | } | 
|  | 1864 |  | 
|  | 1865 | #[test] | 
| Hasini Gunasinghe | 557b103 | 2020-11-10 01:35:30 +0000 | [diff] [blame] | 1866 | fn test_auth_token_table_invariant() -> Result<()> { | 
|  | 1867 | let mut db = new_test_db()?; | 
|  | 1868 | let auth_token1 = HardwareAuthToken { | 
|  | 1869 | challenge: i64::MAX, | 
|  | 1870 | userId: 200, | 
|  | 1871 | authenticatorId: 200, | 
|  | 1872 | authenticatorType: kmhw_authenticator_type(kmhw_authenticator_type::PASSWORD.0), | 
|  | 1873 | timestamp: Timestamp { milliSeconds: 500 }, | 
|  | 1874 | mac: String::from("mac").into_bytes(), | 
|  | 1875 | }; | 
|  | 1876 | db.insert_auth_token(&auth_token1)?; | 
|  | 1877 | let auth_tokens_returned = get_auth_tokens(&mut db)?; | 
|  | 1878 | assert_eq!(auth_tokens_returned.len(), 1); | 
|  | 1879 |  | 
|  | 1880 | // insert another auth token with the same values for the columns in the UNIQUE constraint | 
|  | 1881 | // of the auth token table and different value for timestamp | 
|  | 1882 | let auth_token2 = HardwareAuthToken { | 
|  | 1883 | challenge: i64::MAX, | 
|  | 1884 | userId: 200, | 
|  | 1885 | authenticatorId: 200, | 
|  | 1886 | authenticatorType: kmhw_authenticator_type(kmhw_authenticator_type::PASSWORD.0), | 
|  | 1887 | timestamp: Timestamp { milliSeconds: 600 }, | 
|  | 1888 | mac: String::from("mac").into_bytes(), | 
|  | 1889 | }; | 
|  | 1890 |  | 
|  | 1891 | db.insert_auth_token(&auth_token2)?; | 
|  | 1892 | let mut auth_tokens_returned = get_auth_tokens(&mut db)?; | 
|  | 1893 | assert_eq!(auth_tokens_returned.len(), 1); | 
|  | 1894 |  | 
|  | 1895 | if let Some(auth_token) = auth_tokens_returned.pop() { | 
|  | 1896 | assert_eq!(auth_token.auth_token.timestamp.milliSeconds, 600); | 
|  | 1897 | } | 
|  | 1898 |  | 
|  | 1899 | // insert another auth token with the different values for the columns in the UNIQUE | 
|  | 1900 | // constraint of the auth token table | 
|  | 1901 | let auth_token3 = HardwareAuthToken { | 
|  | 1902 | challenge: i64::MAX, | 
|  | 1903 | userId: 201, | 
|  | 1904 | authenticatorId: 200, | 
|  | 1905 | authenticatorType: kmhw_authenticator_type(kmhw_authenticator_type::PASSWORD.0), | 
|  | 1906 | timestamp: Timestamp { milliSeconds: 600 }, | 
|  | 1907 | mac: String::from("mac").into_bytes(), | 
|  | 1908 | }; | 
|  | 1909 |  | 
|  | 1910 | db.insert_auth_token(&auth_token3)?; | 
|  | 1911 | let auth_tokens_returned = get_auth_tokens(&mut db)?; | 
|  | 1912 | assert_eq!(auth_tokens_returned.len(), 2); | 
|  | 1913 |  | 
|  | 1914 | Ok(()) | 
|  | 1915 | } | 
|  | 1916 |  | 
|  | 1917 | // utility function for test_auth_token_table_invariant() | 
|  | 1918 | fn get_auth_tokens(db: &mut KeystoreDB) -> Result<Vec<AuthTokenEntry>> { | 
|  | 1919 | let mut stmt = db.conn.prepare("SELECT * from perboot.authtoken;")?; | 
|  | 1920 |  | 
|  | 1921 | let auth_token_entries: Vec<AuthTokenEntry> = stmt | 
|  | 1922 | .query_map(NO_PARAMS, |row| { | 
|  | 1923 | Ok(AuthTokenEntry::new( | 
|  | 1924 | HardwareAuthToken { | 
|  | 1925 | challenge: row.get(1)?, | 
|  | 1926 | userId: row.get(2)?, | 
|  | 1927 | authenticatorId: row.get(3)?, | 
|  | 1928 | authenticatorType: HardwareAuthenticatorType(row.get(4)?), | 
|  | 1929 | timestamp: Timestamp { milliSeconds: row.get(5)? }, | 
|  | 1930 | mac: row.get(6)?, | 
|  | 1931 | }, | 
|  | 1932 | row.get(7)?, | 
|  | 1933 | )) | 
|  | 1934 | })? | 
|  | 1935 | .collect::<Result<Vec<AuthTokenEntry>, Error>>()?; | 
|  | 1936 | Ok(auth_token_entries) | 
|  | 1937 | } | 
|  | 1938 |  | 
|  | 1939 | #[test] | 
| Joel Galenson | 2aab443 | 2020-07-22 15:27:57 -0700 | [diff] [blame] | 1940 | fn test_persistence_for_files() -> Result<()> { | 
| Janis Danisevskis | bf15d73 | 2020-12-08 10:35:26 -0800 | [diff] [blame] | 1941 | let temp_dir = TempDir::new("persistent_db_test")?; | 
| Janis Danisevskis | 93927dd | 2020-12-23 12:23:08 -0800 | [diff] [blame] | 1942 | let mut db = KeystoreDB::new(temp_dir.path())?; | 
| Joel Galenson | 2aab443 | 2020-07-22 15:27:57 -0700 | [diff] [blame] | 1943 |  | 
| Janis Danisevskis | c5b210b | 2020-09-11 13:27:37 -0700 | [diff] [blame] | 1944 | db.create_key_entry(Domain::APP, 100)?; | 
| Joel Galenson | 2aab443 | 2020-07-22 15:27:57 -0700 | [diff] [blame] | 1945 | let entries = get_keyentry(&db)?; | 
|  | 1946 | assert_eq!(entries.len(), 1); | 
| Janis Danisevskis | bf15d73 | 2020-12-08 10:35:26 -0800 | [diff] [blame] | 1947 |  | 
|  | 1948 | let db = KeystoreDB::new(temp_dir.path())?; | 
| Joel Galenson | 2aab443 | 2020-07-22 15:27:57 -0700 | [diff] [blame] | 1949 |  | 
|  | 1950 | let entries_new = get_keyentry(&db)?; | 
|  | 1951 | assert_eq!(entries, entries_new); | 
|  | 1952 | Ok(()) | 
|  | 1953 | } | 
|  | 1954 |  | 
|  | 1955 | #[test] | 
| Joel Galenson | 0891bc1 | 2020-07-20 10:37:03 -0700 | [diff] [blame] | 1956 | fn test_create_key_entry() -> Result<()> { | 
| Janis Danisevskis | c5b210b | 2020-09-11 13:27:37 -0700 | [diff] [blame] | 1957 | fn extractor(ke: &KeyEntryRow) -> (Domain, i64, Option<&str>) { | 
| Joel Galenson | 0891bc1 | 2020-07-20 10:37:03 -0700 | [diff] [blame] | 1958 | (ke.domain.unwrap(), ke.namespace.unwrap(), ke.alias.as_deref()) | 
|  | 1959 | } | 
|  | 1960 |  | 
| Janis Danisevskis | 93927dd | 2020-12-23 12:23:08 -0800 | [diff] [blame] | 1961 | let mut db = new_test_db()?; | 
| Joel Galenson | 0891bc1 | 2020-07-20 10:37:03 -0700 | [diff] [blame] | 1962 |  | 
| Janis Danisevskis | c5b210b | 2020-09-11 13:27:37 -0700 | [diff] [blame] | 1963 | db.create_key_entry(Domain::APP, 100)?; | 
|  | 1964 | db.create_key_entry(Domain::SELINUX, 101)?; | 
| Joel Galenson | 0891bc1 | 2020-07-20 10:37:03 -0700 | [diff] [blame] | 1965 |  | 
|  | 1966 | let entries = get_keyentry(&db)?; | 
|  | 1967 | assert_eq!(entries.len(), 2); | 
| Janis Danisevskis | c5b210b | 2020-09-11 13:27:37 -0700 | [diff] [blame] | 1968 | assert_eq!(extractor(&entries[0]), (Domain::APP, 100, None)); | 
|  | 1969 | assert_eq!(extractor(&entries[1]), (Domain::SELINUX, 101, None)); | 
| Joel Galenson | 0891bc1 | 2020-07-20 10:37:03 -0700 | [diff] [blame] | 1970 |  | 
|  | 1971 | // Test that we must pass in a valid Domain. | 
|  | 1972 | check_result_is_error_containing_string( | 
| Janis Danisevskis | c5b210b | 2020-09-11 13:27:37 -0700 | [diff] [blame] | 1973 | db.create_key_entry(Domain::GRANT, 102), | 
|  | 1974 | "Domain Domain(1) must be either App or SELinux.", | 
| Joel Galenson | 0891bc1 | 2020-07-20 10:37:03 -0700 | [diff] [blame] | 1975 | ); | 
|  | 1976 | check_result_is_error_containing_string( | 
| Janis Danisevskis | c5b210b | 2020-09-11 13:27:37 -0700 | [diff] [blame] | 1977 | db.create_key_entry(Domain::BLOB, 103), | 
|  | 1978 | "Domain Domain(3) must be either App or SELinux.", | 
| Joel Galenson | 0891bc1 | 2020-07-20 10:37:03 -0700 | [diff] [blame] | 1979 | ); | 
|  | 1980 | check_result_is_error_containing_string( | 
| Janis Danisevskis | c5b210b | 2020-09-11 13:27:37 -0700 | [diff] [blame] | 1981 | db.create_key_entry(Domain::KEY_ID, 104), | 
|  | 1982 | "Domain Domain(4) must be either App or SELinux.", | 
| Joel Galenson | 0891bc1 | 2020-07-20 10:37:03 -0700 | [diff] [blame] | 1983 | ); | 
|  | 1984 |  | 
|  | 1985 | Ok(()) | 
|  | 1986 | } | 
|  | 1987 |  | 
| Joel Galenson | 33c04ad | 2020-08-03 11:04:38 -0700 | [diff] [blame] | 1988 | #[test] | 
|  | 1989 | fn test_rebind_alias() -> Result<()> { | 
| Janis Danisevskis | c5b210b | 2020-09-11 13:27:37 -0700 | [diff] [blame] | 1990 | fn extractor(ke: &KeyEntryRow) -> (Option<Domain>, Option<i64>, Option<&str>) { | 
| Joel Galenson | 33c04ad | 2020-08-03 11:04:38 -0700 | [diff] [blame] | 1991 | (ke.domain, ke.namespace, ke.alias.as_deref()) | 
|  | 1992 | } | 
|  | 1993 |  | 
| Janis Danisevskis | 4df44f4 | 2020-08-26 14:40:03 -0700 | [diff] [blame] | 1994 | let mut db = new_test_db()?; | 
| Janis Danisevskis | c5b210b | 2020-09-11 13:27:37 -0700 | [diff] [blame] | 1995 | db.create_key_entry(Domain::APP, 42)?; | 
|  | 1996 | db.create_key_entry(Domain::APP, 42)?; | 
| Joel Galenson | 33c04ad | 2020-08-03 11:04:38 -0700 | [diff] [blame] | 1997 | let entries = get_keyentry(&db)?; | 
|  | 1998 | assert_eq!(entries.len(), 2); | 
| Janis Danisevskis | c5b210b | 2020-09-11 13:27:37 -0700 | [diff] [blame] | 1999 | assert_eq!(extractor(&entries[0]), (Some(Domain::APP), Some(42), None)); | 
|  | 2000 | assert_eq!(extractor(&entries[1]), (Some(Domain::APP), Some(42), None)); | 
| Joel Galenson | 33c04ad | 2020-08-03 11:04:38 -0700 | [diff] [blame] | 2001 |  | 
|  | 2002 | // Test that the first call to rebind_alias sets the alias. | 
| Janis Danisevskis | aec1459 | 2020-11-12 09:41:49 -0800 | [diff] [blame] | 2003 | db.rebind_alias(&KEY_ID_LOCK.get(entries[0].id), "foo", Domain::APP, 42)?; | 
| Joel Galenson | 33c04ad | 2020-08-03 11:04:38 -0700 | [diff] [blame] | 2004 | let entries = get_keyentry(&db)?; | 
|  | 2005 | assert_eq!(entries.len(), 2); | 
| Janis Danisevskis | c5b210b | 2020-09-11 13:27:37 -0700 | [diff] [blame] | 2006 | assert_eq!(extractor(&entries[0]), (Some(Domain::APP), Some(42), Some("foo"))); | 
|  | 2007 | assert_eq!(extractor(&entries[1]), (Some(Domain::APP), Some(42), None)); | 
| Joel Galenson | 33c04ad | 2020-08-03 11:04:38 -0700 | [diff] [blame] | 2008 |  | 
|  | 2009 | // Test that the second call to rebind_alias also empties the old one. | 
| Janis Danisevskis | aec1459 | 2020-11-12 09:41:49 -0800 | [diff] [blame] | 2010 | db.rebind_alias(&KEY_ID_LOCK.get(entries[1].id), "foo", Domain::APP, 42)?; | 
| Joel Galenson | 33c04ad | 2020-08-03 11:04:38 -0700 | [diff] [blame] | 2011 | let entries = get_keyentry(&db)?; | 
|  | 2012 | assert_eq!(entries.len(), 2); | 
| Joel Galenson | 7fa5c41 | 2020-11-19 10:56:54 -0800 | [diff] [blame] | 2013 | assert_eq!(extractor(&entries[0]), (None, None, None)); | 
| Janis Danisevskis | c5b210b | 2020-09-11 13:27:37 -0700 | [diff] [blame] | 2014 | assert_eq!(extractor(&entries[1]), (Some(Domain::APP), Some(42), Some("foo"))); | 
| Joel Galenson | 33c04ad | 2020-08-03 11:04:38 -0700 | [diff] [blame] | 2015 |  | 
|  | 2016 | // Test that we must pass in a valid Domain. | 
|  | 2017 | check_result_is_error_containing_string( | 
| Janis Danisevskis | aec1459 | 2020-11-12 09:41:49 -0800 | [diff] [blame] | 2018 | db.rebind_alias(&KEY_ID_LOCK.get(0), "foo", Domain::GRANT, 42), | 
| Janis Danisevskis | c5b210b | 2020-09-11 13:27:37 -0700 | [diff] [blame] | 2019 | "Domain Domain(1) must be either App or SELinux.", | 
| Joel Galenson | 33c04ad | 2020-08-03 11:04:38 -0700 | [diff] [blame] | 2020 | ); | 
|  | 2021 | check_result_is_error_containing_string( | 
| Janis Danisevskis | aec1459 | 2020-11-12 09:41:49 -0800 | [diff] [blame] | 2022 | db.rebind_alias(&KEY_ID_LOCK.get(0), "foo", Domain::BLOB, 42), | 
| Janis Danisevskis | c5b210b | 2020-09-11 13:27:37 -0700 | [diff] [blame] | 2023 | "Domain Domain(3) must be either App or SELinux.", | 
| Joel Galenson | 33c04ad | 2020-08-03 11:04:38 -0700 | [diff] [blame] | 2024 | ); | 
|  | 2025 | check_result_is_error_containing_string( | 
| Janis Danisevskis | aec1459 | 2020-11-12 09:41:49 -0800 | [diff] [blame] | 2026 | db.rebind_alias(&KEY_ID_LOCK.get(0), "foo", Domain::KEY_ID, 42), | 
| Janis Danisevskis | c5b210b | 2020-09-11 13:27:37 -0700 | [diff] [blame] | 2027 | "Domain Domain(4) must be either App or SELinux.", | 
| Joel Galenson | 33c04ad | 2020-08-03 11:04:38 -0700 | [diff] [blame] | 2028 | ); | 
|  | 2029 |  | 
|  | 2030 | // Test that we correctly handle setting an alias for something that does not exist. | 
|  | 2031 | check_result_is_error_containing_string( | 
| Janis Danisevskis | aec1459 | 2020-11-12 09:41:49 -0800 | [diff] [blame] | 2032 | db.rebind_alias(&KEY_ID_LOCK.get(0), "foo", Domain::SELINUX, 42), | 
| Joel Galenson | 33c04ad | 2020-08-03 11:04:38 -0700 | [diff] [blame] | 2033 | "Expected to update a single entry but instead updated 0", | 
|  | 2034 | ); | 
|  | 2035 | // Test that we correctly abort the transaction in this case. | 
|  | 2036 | let entries = get_keyentry(&db)?; | 
|  | 2037 | assert_eq!(entries.len(), 2); | 
| Joel Galenson | 7fa5c41 | 2020-11-19 10:56:54 -0800 | [diff] [blame] | 2038 | assert_eq!(extractor(&entries[0]), (None, None, None)); | 
| Janis Danisevskis | c5b210b | 2020-09-11 13:27:37 -0700 | [diff] [blame] | 2039 | assert_eq!(extractor(&entries[1]), (Some(Domain::APP), Some(42), Some("foo"))); | 
| Joel Galenson | 33c04ad | 2020-08-03 11:04:38 -0700 | [diff] [blame] | 2040 |  | 
|  | 2041 | Ok(()) | 
|  | 2042 | } | 
|  | 2043 |  | 
| Janis Danisevskis | 63f7bc8 | 2020-09-03 10:12:56 -0700 | [diff] [blame] | 2044 | #[test] | 
|  | 2045 | fn test_grant_ungrant() -> Result<()> { | 
|  | 2046 | const CALLER_UID: u32 = 15; | 
|  | 2047 | const GRANTEE_UID: u32 = 12; | 
|  | 2048 | const SELINUX_NAMESPACE: i64 = 7; | 
|  | 2049 |  | 
|  | 2050 | let mut db = new_test_db()?; | 
|  | 2051 | db.conn.execute( | 
| Janis Danisevskis | 93927dd | 2020-12-23 12:23:08 -0800 | [diff] [blame] | 2052 | "INSERT INTO persistent.keyentry (id, key_type, domain, namespace, alias, state) | 
|  | 2053 | VALUES (1, 0, 0, 15, 'key', 1), (2, 0, 2, 7, 'yek', 1);", | 
| Janis Danisevskis | 63f7bc8 | 2020-09-03 10:12:56 -0700 | [diff] [blame] | 2054 | NO_PARAMS, | 
|  | 2055 | )?; | 
|  | 2056 | let app_key = KeyDescriptor { | 
| Janis Danisevskis | c5b210b | 2020-09-11 13:27:37 -0700 | [diff] [blame] | 2057 | domain: super::Domain::APP, | 
|  | 2058 | nspace: 0, | 
| Janis Danisevskis | 63f7bc8 | 2020-09-03 10:12:56 -0700 | [diff] [blame] | 2059 | alias: Some("key".to_string()), | 
|  | 2060 | blob: None, | 
|  | 2061 | }; | 
|  | 2062 | const PVEC1: KeyPermSet = key_perm_set![KeyPerm::use_(), KeyPerm::get_info()]; | 
|  | 2063 | const PVEC2: KeyPermSet = key_perm_set![KeyPerm::use_()]; | 
|  | 2064 |  | 
|  | 2065 | // Reset totally predictable random number generator in case we | 
|  | 2066 | // are not the first test running on this thread. | 
|  | 2067 | reset_random(); | 
|  | 2068 | let next_random = 0i64; | 
|  | 2069 |  | 
| Janis Danisevskis | 93927dd | 2020-12-23 12:23:08 -0800 | [diff] [blame] | 2070 | let app_granted_key = db | 
|  | 2071 | .grant(app_key.clone(), CALLER_UID, GRANTEE_UID, PVEC1, |k, a| { | 
| Janis Danisevskis | 63f7bc8 | 2020-09-03 10:12:56 -0700 | [diff] [blame] | 2072 | assert_eq!(*a, PVEC1); | 
|  | 2073 | assert_eq!( | 
|  | 2074 | *k, | 
|  | 2075 | KeyDescriptor { | 
| Janis Danisevskis | c5b210b | 2020-09-11 13:27:37 -0700 | [diff] [blame] | 2076 | domain: super::Domain::APP, | 
| Janis Danisevskis | 63f7bc8 | 2020-09-03 10:12:56 -0700 | [diff] [blame] | 2077 | // namespace must be set to the caller_uid. | 
| Janis Danisevskis | c5b210b | 2020-09-11 13:27:37 -0700 | [diff] [blame] | 2078 | nspace: CALLER_UID as i64, | 
| Janis Danisevskis | 63f7bc8 | 2020-09-03 10:12:56 -0700 | [diff] [blame] | 2079 | alias: Some("key".to_string()), | 
|  | 2080 | blob: None, | 
|  | 2081 | } | 
|  | 2082 | ); | 
|  | 2083 | Ok(()) | 
| Janis Danisevskis | 93927dd | 2020-12-23 12:23:08 -0800 | [diff] [blame] | 2084 | }) | 
|  | 2085 | .unwrap(); | 
| Janis Danisevskis | 63f7bc8 | 2020-09-03 10:12:56 -0700 | [diff] [blame] | 2086 |  | 
|  | 2087 | assert_eq!( | 
|  | 2088 | app_granted_key, | 
|  | 2089 | KeyDescriptor { | 
| Janis Danisevskis | c5b210b | 2020-09-11 13:27:37 -0700 | [diff] [blame] | 2090 | domain: super::Domain::GRANT, | 
| Janis Danisevskis | 63f7bc8 | 2020-09-03 10:12:56 -0700 | [diff] [blame] | 2091 | // The grantid is next_random due to the mock random number generator. | 
| Janis Danisevskis | c5b210b | 2020-09-11 13:27:37 -0700 | [diff] [blame] | 2092 | nspace: next_random, | 
| Janis Danisevskis | 63f7bc8 | 2020-09-03 10:12:56 -0700 | [diff] [blame] | 2093 | alias: None, | 
|  | 2094 | blob: None, | 
|  | 2095 | } | 
|  | 2096 | ); | 
|  | 2097 |  | 
|  | 2098 | let selinux_key = KeyDescriptor { | 
| Janis Danisevskis | c5b210b | 2020-09-11 13:27:37 -0700 | [diff] [blame] | 2099 | domain: super::Domain::SELINUX, | 
|  | 2100 | nspace: SELINUX_NAMESPACE, | 
| Janis Danisevskis | 63f7bc8 | 2020-09-03 10:12:56 -0700 | [diff] [blame] | 2101 | alias: Some("yek".to_string()), | 
|  | 2102 | blob: None, | 
|  | 2103 | }; | 
|  | 2104 |  | 
| Janis Danisevskis | 93927dd | 2020-12-23 12:23:08 -0800 | [diff] [blame] | 2105 | let selinux_granted_key = db | 
|  | 2106 | .grant(selinux_key.clone(), CALLER_UID, 12, PVEC1, |k, a| { | 
| Janis Danisevskis | 63f7bc8 | 2020-09-03 10:12:56 -0700 | [diff] [blame] | 2107 | assert_eq!(*a, PVEC1); | 
|  | 2108 | assert_eq!( | 
|  | 2109 | *k, | 
|  | 2110 | KeyDescriptor { | 
| Janis Danisevskis | c5b210b | 2020-09-11 13:27:37 -0700 | [diff] [blame] | 2111 | domain: super::Domain::SELINUX, | 
| Janis Danisevskis | 63f7bc8 | 2020-09-03 10:12:56 -0700 | [diff] [blame] | 2112 | // namespace must be the supplied SELinux | 
|  | 2113 | // namespace. | 
| Janis Danisevskis | c5b210b | 2020-09-11 13:27:37 -0700 | [diff] [blame] | 2114 | nspace: SELINUX_NAMESPACE, | 
| Janis Danisevskis | 63f7bc8 | 2020-09-03 10:12:56 -0700 | [diff] [blame] | 2115 | alias: Some("yek".to_string()), | 
|  | 2116 | blob: None, | 
|  | 2117 | } | 
|  | 2118 | ); | 
|  | 2119 | Ok(()) | 
| Janis Danisevskis | 93927dd | 2020-12-23 12:23:08 -0800 | [diff] [blame] | 2120 | }) | 
|  | 2121 | .unwrap(); | 
| Janis Danisevskis | 63f7bc8 | 2020-09-03 10:12:56 -0700 | [diff] [blame] | 2122 |  | 
|  | 2123 | assert_eq!( | 
|  | 2124 | selinux_granted_key, | 
|  | 2125 | KeyDescriptor { | 
| Janis Danisevskis | c5b210b | 2020-09-11 13:27:37 -0700 | [diff] [blame] | 2126 | domain: super::Domain::GRANT, | 
| Janis Danisevskis | 63f7bc8 | 2020-09-03 10:12:56 -0700 | [diff] [blame] | 2127 | // The grantid is next_random + 1 due to the mock random number generator. | 
| Janis Danisevskis | c5b210b | 2020-09-11 13:27:37 -0700 | [diff] [blame] | 2128 | nspace: next_random + 1, | 
| Janis Danisevskis | 63f7bc8 | 2020-09-03 10:12:56 -0700 | [diff] [blame] | 2129 | alias: None, | 
|  | 2130 | blob: None, | 
|  | 2131 | } | 
|  | 2132 | ); | 
|  | 2133 |  | 
|  | 2134 | // This should update the existing grant with PVEC2. | 
| Janis Danisevskis | 93927dd | 2020-12-23 12:23:08 -0800 | [diff] [blame] | 2135 | let selinux_granted_key = db | 
|  | 2136 | .grant(selinux_key.clone(), CALLER_UID, 12, PVEC2, |k, a| { | 
| Janis Danisevskis | 63f7bc8 | 2020-09-03 10:12:56 -0700 | [diff] [blame] | 2137 | assert_eq!(*a, PVEC2); | 
|  | 2138 | assert_eq!( | 
|  | 2139 | *k, | 
|  | 2140 | KeyDescriptor { | 
| Janis Danisevskis | c5b210b | 2020-09-11 13:27:37 -0700 | [diff] [blame] | 2141 | domain: super::Domain::SELINUX, | 
| Janis Danisevskis | 63f7bc8 | 2020-09-03 10:12:56 -0700 | [diff] [blame] | 2142 | // namespace must be the supplied SELinux | 
|  | 2143 | // namespace. | 
| Janis Danisevskis | c5b210b | 2020-09-11 13:27:37 -0700 | [diff] [blame] | 2144 | nspace: SELINUX_NAMESPACE, | 
| Janis Danisevskis | 63f7bc8 | 2020-09-03 10:12:56 -0700 | [diff] [blame] | 2145 | alias: Some("yek".to_string()), | 
|  | 2146 | blob: None, | 
|  | 2147 | } | 
|  | 2148 | ); | 
|  | 2149 | Ok(()) | 
| Janis Danisevskis | 93927dd | 2020-12-23 12:23:08 -0800 | [diff] [blame] | 2150 | }) | 
|  | 2151 | .unwrap(); | 
| Janis Danisevskis | 63f7bc8 | 2020-09-03 10:12:56 -0700 | [diff] [blame] | 2152 |  | 
|  | 2153 | assert_eq!( | 
|  | 2154 | selinux_granted_key, | 
|  | 2155 | KeyDescriptor { | 
| Janis Danisevskis | c5b210b | 2020-09-11 13:27:37 -0700 | [diff] [blame] | 2156 | domain: super::Domain::GRANT, | 
| Janis Danisevskis | 63f7bc8 | 2020-09-03 10:12:56 -0700 | [diff] [blame] | 2157 | // Same grant id as before. The entry was only updated. | 
| Janis Danisevskis | c5b210b | 2020-09-11 13:27:37 -0700 | [diff] [blame] | 2158 | nspace: next_random + 1, | 
| Janis Danisevskis | 63f7bc8 | 2020-09-03 10:12:56 -0700 | [diff] [blame] | 2159 | alias: None, | 
|  | 2160 | blob: None, | 
|  | 2161 | } | 
|  | 2162 | ); | 
|  | 2163 |  | 
|  | 2164 | { | 
|  | 2165 | // Limiting scope of stmt, because it borrows db. | 
|  | 2166 | let mut stmt = db | 
|  | 2167 | .conn | 
| Janis Danisevskis | bf15d73 | 2020-12-08 10:35:26 -0800 | [diff] [blame] | 2168 | .prepare("SELECT id, grantee, keyentryid, access_vector FROM persistent.grant;")?; | 
| Janis Danisevskis | ee10b5f | 2020-09-22 16:42:35 -0700 | [diff] [blame] | 2169 | let mut rows = | 
|  | 2170 | stmt.query_map::<(i64, u32, i64, KeyPermSet), _, _>(NO_PARAMS, |row| { | 
|  | 2171 | Ok(( | 
|  | 2172 | row.get(0)?, | 
|  | 2173 | row.get(1)?, | 
|  | 2174 | row.get(2)?, | 
|  | 2175 | KeyPermSet::from(row.get::<_, i32>(3)?), | 
|  | 2176 | )) | 
|  | 2177 | })?; | 
| Janis Danisevskis | 63f7bc8 | 2020-09-03 10:12:56 -0700 | [diff] [blame] | 2178 |  | 
|  | 2179 | let r = rows.next().unwrap().unwrap(); | 
| Janis Danisevskis | ee10b5f | 2020-09-22 16:42:35 -0700 | [diff] [blame] | 2180 | assert_eq!(r, (next_random, GRANTEE_UID, 1, PVEC1)); | 
| Janis Danisevskis | 63f7bc8 | 2020-09-03 10:12:56 -0700 | [diff] [blame] | 2181 | let r = rows.next().unwrap().unwrap(); | 
| Janis Danisevskis | ee10b5f | 2020-09-22 16:42:35 -0700 | [diff] [blame] | 2182 | assert_eq!(r, (next_random + 1, GRANTEE_UID, 2, PVEC2)); | 
| Janis Danisevskis | 63f7bc8 | 2020-09-03 10:12:56 -0700 | [diff] [blame] | 2183 | assert!(rows.next().is_none()); | 
|  | 2184 | } | 
|  | 2185 |  | 
|  | 2186 | debug_dump_keyentry_table(&mut db)?; | 
|  | 2187 | println!("app_key {:?}", app_key); | 
|  | 2188 | println!("selinux_key {:?}", selinux_key); | 
|  | 2189 |  | 
|  | 2190 | db.ungrant(app_key, CALLER_UID, GRANTEE_UID, |_| Ok(()))?; | 
|  | 2191 | db.ungrant(selinux_key, CALLER_UID, GRANTEE_UID, |_| Ok(()))?; | 
|  | 2192 |  | 
|  | 2193 | Ok(()) | 
|  | 2194 | } | 
|  | 2195 |  | 
| Janis Danisevskis | b42fc18 | 2020-12-15 08:41:27 -0800 | [diff] [blame] | 2196 | static TEST_KEY_BLOB: &[u8] = b"my test blob"; | 
| Janis Danisevskis | 63f7bc8 | 2020-09-03 10:12:56 -0700 | [diff] [blame] | 2197 | static TEST_CERT_BLOB: &[u8] = b"my test cert"; | 
|  | 2198 | static TEST_CERT_CHAIN_BLOB: &[u8] = b"my test cert_chain"; | 
|  | 2199 |  | 
|  | 2200 | #[test] | 
|  | 2201 | fn test_insert_blob() -> Result<()> { | 
| Janis Danisevskis | 93927dd | 2020-12-23 12:23:08 -0800 | [diff] [blame] | 2202 | let key_id = KEY_ID_LOCK.get(3000); | 
| Janis Danisevskis | 63f7bc8 | 2020-09-03 10:12:56 -0700 | [diff] [blame] | 2203 | let mut db = new_test_db()?; | 
| Janis Danisevskis | 93927dd | 2020-12-23 12:23:08 -0800 | [diff] [blame] | 2204 | db.insert_blob(&key_id, SubComponentType::KEY_BLOB, TEST_KEY_BLOB)?; | 
|  | 2205 | db.insert_blob(&key_id, SubComponentType::CERT, TEST_CERT_BLOB)?; | 
|  | 2206 | db.insert_blob(&key_id, SubComponentType::CERT_CHAIN, TEST_CERT_CHAIN_BLOB)?; | 
|  | 2207 | drop(key_id); | 
| Janis Danisevskis | 63f7bc8 | 2020-09-03 10:12:56 -0700 | [diff] [blame] | 2208 |  | 
|  | 2209 | let mut stmt = db.conn.prepare( | 
| Janis Danisevskis | 93927dd | 2020-12-23 12:23:08 -0800 | [diff] [blame] | 2210 | "SELECT subcomponent_type, keyentryid, blob FROM persistent.blobentry | 
|  | 2211 | ORDER BY subcomponent_type ASC;", | 
| Janis Danisevskis | 63f7bc8 | 2020-09-03 10:12:56 -0700 | [diff] [blame] | 2212 | )?; | 
|  | 2213 | let mut rows = stmt | 
| Janis Danisevskis | 93927dd | 2020-12-23 12:23:08 -0800 | [diff] [blame] | 2214 | .query_map::<(SubComponentType, i64, Vec<u8>), _, _>(NO_PARAMS, |row| { | 
|  | 2215 | Ok((row.get(0)?, row.get(1)?, row.get(2)?)) | 
| Janis Danisevskis | 63f7bc8 | 2020-09-03 10:12:56 -0700 | [diff] [blame] | 2216 | })?; | 
|  | 2217 | let r = rows.next().unwrap().unwrap(); | 
| Janis Danisevskis | 93927dd | 2020-12-23 12:23:08 -0800 | [diff] [blame] | 2218 | assert_eq!(r, (SubComponentType::KEY_BLOB, 3000, TEST_KEY_BLOB.to_vec())); | 
| Janis Danisevskis | 63f7bc8 | 2020-09-03 10:12:56 -0700 | [diff] [blame] | 2219 | let r = rows.next().unwrap().unwrap(); | 
| Janis Danisevskis | 93927dd | 2020-12-23 12:23:08 -0800 | [diff] [blame] | 2220 | assert_eq!(r, (SubComponentType::CERT, 3000, TEST_CERT_BLOB.to_vec())); | 
| Janis Danisevskis | 63f7bc8 | 2020-09-03 10:12:56 -0700 | [diff] [blame] | 2221 | let r = rows.next().unwrap().unwrap(); | 
| Janis Danisevskis | 93927dd | 2020-12-23 12:23:08 -0800 | [diff] [blame] | 2222 | assert_eq!(r, (SubComponentType::CERT_CHAIN, 3000, TEST_CERT_CHAIN_BLOB.to_vec())); | 
| Janis Danisevskis | 63f7bc8 | 2020-09-03 10:12:56 -0700 | [diff] [blame] | 2223 |  | 
|  | 2224 | Ok(()) | 
|  | 2225 | } | 
|  | 2226 |  | 
|  | 2227 | static TEST_ALIAS: &str = "my super duper key"; | 
|  | 2228 |  | 
|  | 2229 | #[test] | 
|  | 2230 | fn test_insert_and_load_full_keyentry_domain_app() -> Result<()> { | 
|  | 2231 | let mut db = new_test_db()?; | 
| Janis Danisevskis | c5b210b | 2020-09-11 13:27:37 -0700 | [diff] [blame] | 2232 | let key_id = make_test_key_entry(&mut db, Domain::APP, 1, TEST_ALIAS) | 
| Janis Danisevskis | aec1459 | 2020-11-12 09:41:49 -0800 | [diff] [blame] | 2233 | .context("test_insert_and_load_full_keyentry_domain_app")? | 
|  | 2234 | .0; | 
| Janis Danisevskis | 93927dd | 2020-12-23 12:23:08 -0800 | [diff] [blame] | 2235 | let (_key_guard, key_entry) = db | 
|  | 2236 | .load_key_entry( | 
|  | 2237 | KeyDescriptor { | 
|  | 2238 | domain: Domain::APP, | 
|  | 2239 | nspace: 0, | 
|  | 2240 | alias: Some(TEST_ALIAS.to_string()), | 
|  | 2241 | blob: None, | 
|  | 2242 | }, | 
|  | 2243 | KeyType::Client, | 
|  | 2244 | KeyEntryLoadBits::BOTH, | 
|  | 2245 | 1, | 
|  | 2246 | |_k, _av| Ok(()), | 
|  | 2247 | ) | 
|  | 2248 | .unwrap(); | 
|  | 2249 | assert_eq!(key_entry, make_test_key_entry_test_vector(key_id)); | 
|  | 2250 |  | 
|  | 2251 | db.unbind_key( | 
| Janis Danisevskis | 63f7bc8 | 2020-09-03 10:12:56 -0700 | [diff] [blame] | 2252 | KeyDescriptor { | 
| Janis Danisevskis | c5b210b | 2020-09-11 13:27:37 -0700 | [diff] [blame] | 2253 | domain: Domain::APP, | 
|  | 2254 | nspace: 0, | 
| Janis Danisevskis | 63f7bc8 | 2020-09-03 10:12:56 -0700 | [diff] [blame] | 2255 | alias: Some(TEST_ALIAS.to_string()), | 
|  | 2256 | blob: None, | 
|  | 2257 | }, | 
| Janis Danisevskis | b42fc18 | 2020-12-15 08:41:27 -0800 | [diff] [blame] | 2258 | KeyType::Client, | 
| Janis Danisevskis | 63f7bc8 | 2020-09-03 10:12:56 -0700 | [diff] [blame] | 2259 | 1, | 
| Janis Danisevskis | 93927dd | 2020-12-23 12:23:08 -0800 | [diff] [blame] | 2260 | |_, _| Ok(()), | 
|  | 2261 | ) | 
|  | 2262 | .unwrap(); | 
|  | 2263 |  | 
|  | 2264 | assert_eq!( | 
|  | 2265 | Some(&KsError::Rc(ResponseCode::KEY_NOT_FOUND)), | 
|  | 2266 | db.load_key_entry( | 
|  | 2267 | KeyDescriptor { | 
|  | 2268 | domain: Domain::APP, | 
|  | 2269 | nspace: 0, | 
|  | 2270 | alias: Some(TEST_ALIAS.to_string()), | 
|  | 2271 | blob: None, | 
|  | 2272 | }, | 
|  | 2273 | KeyType::Client, | 
|  | 2274 | KeyEntryLoadBits::NONE, | 
|  | 2275 | 1, | 
|  | 2276 | |_k, _av| Ok(()), | 
|  | 2277 | ) | 
|  | 2278 | .unwrap_err() | 
|  | 2279 | .root_cause() | 
|  | 2280 | .downcast_ref::<KsError>() | 
|  | 2281 | ); | 
|  | 2282 |  | 
| Janis Danisevskis | 63f7bc8 | 2020-09-03 10:12:56 -0700 | [diff] [blame] | 2283 | Ok(()) | 
|  | 2284 | } | 
|  | 2285 |  | 
|  | 2286 | #[test] | 
|  | 2287 | fn test_insert_and_load_full_keyentry_domain_selinux() -> Result<()> { | 
|  | 2288 | let mut db = new_test_db()?; | 
| Janis Danisevskis | c5b210b | 2020-09-11 13:27:37 -0700 | [diff] [blame] | 2289 | let key_id = make_test_key_entry(&mut db, Domain::SELINUX, 1, TEST_ALIAS) | 
| Janis Danisevskis | aec1459 | 2020-11-12 09:41:49 -0800 | [diff] [blame] | 2290 | .context("test_insert_and_load_full_keyentry_domain_selinux")? | 
|  | 2291 | .0; | 
| Janis Danisevskis | 93927dd | 2020-12-23 12:23:08 -0800 | [diff] [blame] | 2292 | let (_key_guard, key_entry) = db | 
|  | 2293 | .load_key_entry( | 
|  | 2294 | KeyDescriptor { | 
|  | 2295 | domain: Domain::SELINUX, | 
|  | 2296 | nspace: 1, | 
|  | 2297 | alias: Some(TEST_ALIAS.to_string()), | 
|  | 2298 | blob: None, | 
|  | 2299 | }, | 
|  | 2300 | KeyType::Client, | 
|  | 2301 | KeyEntryLoadBits::BOTH, | 
|  | 2302 | 1, | 
|  | 2303 | |_k, _av| Ok(()), | 
|  | 2304 | ) | 
|  | 2305 | .unwrap(); | 
|  | 2306 | assert_eq!(key_entry, make_test_key_entry_test_vector(key_id)); | 
|  | 2307 |  | 
|  | 2308 | db.unbind_key( | 
| Janis Danisevskis | 63f7bc8 | 2020-09-03 10:12:56 -0700 | [diff] [blame] | 2309 | KeyDescriptor { | 
| Janis Danisevskis | c5b210b | 2020-09-11 13:27:37 -0700 | [diff] [blame] | 2310 | domain: Domain::SELINUX, | 
|  | 2311 | nspace: 1, | 
| Janis Danisevskis | 63f7bc8 | 2020-09-03 10:12:56 -0700 | [diff] [blame] | 2312 | alias: Some(TEST_ALIAS.to_string()), | 
|  | 2313 | blob: None, | 
|  | 2314 | }, | 
| Janis Danisevskis | b42fc18 | 2020-12-15 08:41:27 -0800 | [diff] [blame] | 2315 | KeyType::Client, | 
| Janis Danisevskis | 63f7bc8 | 2020-09-03 10:12:56 -0700 | [diff] [blame] | 2316 | 1, | 
| Janis Danisevskis | 93927dd | 2020-12-23 12:23:08 -0800 | [diff] [blame] | 2317 | |_, _| Ok(()), | 
|  | 2318 | ) | 
|  | 2319 | .unwrap(); | 
|  | 2320 |  | 
|  | 2321 | assert_eq!( | 
|  | 2322 | Some(&KsError::Rc(ResponseCode::KEY_NOT_FOUND)), | 
|  | 2323 | db.load_key_entry( | 
|  | 2324 | KeyDescriptor { | 
|  | 2325 | domain: Domain::SELINUX, | 
|  | 2326 | nspace: 1, | 
|  | 2327 | alias: Some(TEST_ALIAS.to_string()), | 
|  | 2328 | blob: None, | 
|  | 2329 | }, | 
|  | 2330 | KeyType::Client, | 
|  | 2331 | KeyEntryLoadBits::NONE, | 
|  | 2332 | 1, | 
|  | 2333 | |_k, _av| Ok(()), | 
|  | 2334 | ) | 
|  | 2335 | .unwrap_err() | 
|  | 2336 | .root_cause() | 
|  | 2337 | .downcast_ref::<KsError>() | 
|  | 2338 | ); | 
|  | 2339 |  | 
| Janis Danisevskis | 63f7bc8 | 2020-09-03 10:12:56 -0700 | [diff] [blame] | 2340 | Ok(()) | 
|  | 2341 | } | 
|  | 2342 |  | 
|  | 2343 | #[test] | 
|  | 2344 | fn test_insert_and_load_full_keyentry_domain_key_id() -> Result<()> { | 
|  | 2345 | let mut db = new_test_db()?; | 
| Janis Danisevskis | c5b210b | 2020-09-11 13:27:37 -0700 | [diff] [blame] | 2346 | let key_id = make_test_key_entry(&mut db, Domain::SELINUX, 1, TEST_ALIAS) | 
| Janis Danisevskis | aec1459 | 2020-11-12 09:41:49 -0800 | [diff] [blame] | 2347 | .context("test_insert_and_load_full_keyentry_domain_key_id")? | 
|  | 2348 | .0; | 
| Janis Danisevskis | 93927dd | 2020-12-23 12:23:08 -0800 | [diff] [blame] | 2349 | let (_, key_entry) = db | 
|  | 2350 | .load_key_entry( | 
|  | 2351 | KeyDescriptor { domain: Domain::KEY_ID, nspace: key_id, alias: None, blob: None }, | 
|  | 2352 | KeyType::Client, | 
|  | 2353 | KeyEntryLoadBits::BOTH, | 
|  | 2354 | 1, | 
|  | 2355 | |_k, _av| Ok(()), | 
|  | 2356 | ) | 
|  | 2357 | .unwrap(); | 
|  | 2358 |  | 
|  | 2359 | assert_eq!(key_entry, make_test_key_entry_test_vector(key_id)); | 
|  | 2360 |  | 
|  | 2361 | db.unbind_key( | 
| Janis Danisevskis | c5b210b | 2020-09-11 13:27:37 -0700 | [diff] [blame] | 2362 | KeyDescriptor { domain: Domain::KEY_ID, nspace: key_id, alias: None, blob: None }, | 
| Janis Danisevskis | b42fc18 | 2020-12-15 08:41:27 -0800 | [diff] [blame] | 2363 | KeyType::Client, | 
| Janis Danisevskis | 63f7bc8 | 2020-09-03 10:12:56 -0700 | [diff] [blame] | 2364 | 1, | 
| Janis Danisevskis | 93927dd | 2020-12-23 12:23:08 -0800 | [diff] [blame] | 2365 | |_, _| Ok(()), | 
|  | 2366 | ) | 
|  | 2367 | .unwrap(); | 
|  | 2368 |  | 
|  | 2369 | assert_eq!( | 
|  | 2370 | Some(&KsError::Rc(ResponseCode::KEY_NOT_FOUND)), | 
|  | 2371 | db.load_key_entry( | 
|  | 2372 | KeyDescriptor { domain: Domain::KEY_ID, nspace: key_id, alias: None, blob: None }, | 
|  | 2373 | KeyType::Client, | 
|  | 2374 | KeyEntryLoadBits::NONE, | 
|  | 2375 | 1, | 
|  | 2376 | |_k, _av| Ok(()), | 
|  | 2377 | ) | 
|  | 2378 | .unwrap_err() | 
|  | 2379 | .root_cause() | 
|  | 2380 | .downcast_ref::<KsError>() | 
|  | 2381 | ); | 
| Janis Danisevskis | 63f7bc8 | 2020-09-03 10:12:56 -0700 | [diff] [blame] | 2382 |  | 
|  | 2383 | Ok(()) | 
|  | 2384 | } | 
|  | 2385 |  | 
|  | 2386 | #[test] | 
|  | 2387 | fn test_insert_and_load_full_keyentry_from_grant() -> Result<()> { | 
|  | 2388 | let mut db = new_test_db()?; | 
| Janis Danisevskis | c5b210b | 2020-09-11 13:27:37 -0700 | [diff] [blame] | 2389 | let key_id = make_test_key_entry(&mut db, Domain::APP, 1, TEST_ALIAS) | 
| Janis Danisevskis | aec1459 | 2020-11-12 09:41:49 -0800 | [diff] [blame] | 2390 | .context("test_insert_and_load_full_keyentry_from_grant")? | 
|  | 2391 | .0; | 
| Janis Danisevskis | 63f7bc8 | 2020-09-03 10:12:56 -0700 | [diff] [blame] | 2392 |  | 
| Janis Danisevskis | 93927dd | 2020-12-23 12:23:08 -0800 | [diff] [blame] | 2393 | let granted_key = db | 
|  | 2394 | .grant( | 
|  | 2395 | KeyDescriptor { | 
|  | 2396 | domain: Domain::APP, | 
|  | 2397 | nspace: 0, | 
|  | 2398 | alias: Some(TEST_ALIAS.to_string()), | 
|  | 2399 | blob: None, | 
|  | 2400 | }, | 
|  | 2401 | 1, | 
|  | 2402 | 2, | 
|  | 2403 | key_perm_set![KeyPerm::use_()], | 
|  | 2404 | |_k, _av| Ok(()), | 
|  | 2405 | ) | 
|  | 2406 | .unwrap(); | 
| Janis Danisevskis | 63f7bc8 | 2020-09-03 10:12:56 -0700 | [diff] [blame] | 2407 |  | 
|  | 2408 | debug_dump_grant_table(&mut db)?; | 
|  | 2409 |  | 
| Janis Danisevskis | 93927dd | 2020-12-23 12:23:08 -0800 | [diff] [blame] | 2410 | let (_key_guard, key_entry) = db | 
|  | 2411 | .load_key_entry( | 
|  | 2412 | granted_key.clone(), | 
|  | 2413 | KeyType::Client, | 
|  | 2414 | KeyEntryLoadBits::BOTH, | 
|  | 2415 | 2, | 
|  | 2416 | |k, av| { | 
|  | 2417 | assert_eq!(Domain::GRANT, k.domain); | 
|  | 2418 | assert!(av.unwrap().includes(KeyPerm::use_())); | 
|  | 2419 | Ok(()) | 
|  | 2420 | }, | 
|  | 2421 | ) | 
|  | 2422 | .unwrap(); | 
| Janis Danisevskis | 63f7bc8 | 2020-09-03 10:12:56 -0700 | [diff] [blame] | 2423 |  | 
| Janis Danisevskis | b42fc18 | 2020-12-15 08:41:27 -0800 | [diff] [blame] | 2424 | assert_eq!(key_entry, make_test_key_entry_test_vector(key_id)); | 
| Janis Danisevskis | 93927dd | 2020-12-23 12:23:08 -0800 | [diff] [blame] | 2425 |  | 
|  | 2426 | db.unbind_key(granted_key.clone(), KeyType::Client, 2, |_, _| Ok(())).unwrap(); | 
|  | 2427 |  | 
|  | 2428 | assert_eq!( | 
|  | 2429 | Some(&KsError::Rc(ResponseCode::KEY_NOT_FOUND)), | 
|  | 2430 | db.load_key_entry( | 
|  | 2431 | granted_key, | 
|  | 2432 | KeyType::Client, | 
|  | 2433 | KeyEntryLoadBits::NONE, | 
|  | 2434 | 2, | 
|  | 2435 | |_k, _av| Ok(()), | 
|  | 2436 | ) | 
|  | 2437 | .unwrap_err() | 
|  | 2438 | .root_cause() | 
|  | 2439 | .downcast_ref::<KsError>() | 
|  | 2440 | ); | 
|  | 2441 |  | 
| Janis Danisevskis | 63f7bc8 | 2020-09-03 10:12:56 -0700 | [diff] [blame] | 2442 | Ok(()) | 
|  | 2443 | } | 
|  | 2444 |  | 
| Janis Danisevskis | aec1459 | 2020-11-12 09:41:49 -0800 | [diff] [blame] | 2445 | static KEY_LOCK_TEST_ALIAS: &str = "my super duper locked key"; | 
|  | 2446 |  | 
| Janis Danisevskis | aec1459 | 2020-11-12 09:41:49 -0800 | [diff] [blame] | 2447 | #[test] | 
|  | 2448 | fn test_insert_and_load_full_keyentry_domain_app_concurrently() -> Result<()> { | 
|  | 2449 | let handle = { | 
| Janis Danisevskis | bf15d73 | 2020-12-08 10:35:26 -0800 | [diff] [blame] | 2450 | let temp_dir = Arc::new(TempDir::new("id_lock_test")?); | 
|  | 2451 | let temp_dir_clone = temp_dir.clone(); | 
|  | 2452 | let mut db = KeystoreDB::new(temp_dir.path())?; | 
| Janis Danisevskis | aec1459 | 2020-11-12 09:41:49 -0800 | [diff] [blame] | 2453 | let key_id = make_test_key_entry(&mut db, Domain::APP, 33, KEY_LOCK_TEST_ALIAS) | 
|  | 2454 | .context("test_insert_and_load_full_keyentry_domain_app")? | 
|  | 2455 | .0; | 
| Janis Danisevskis | 93927dd | 2020-12-23 12:23:08 -0800 | [diff] [blame] | 2456 | let (_key_guard, key_entry) = db | 
|  | 2457 | .load_key_entry( | 
|  | 2458 | KeyDescriptor { | 
|  | 2459 | domain: Domain::APP, | 
|  | 2460 | nspace: 0, | 
|  | 2461 | alias: Some(KEY_LOCK_TEST_ALIAS.to_string()), | 
|  | 2462 | blob: None, | 
|  | 2463 | }, | 
|  | 2464 | KeyType::Client, | 
|  | 2465 | KeyEntryLoadBits::BOTH, | 
|  | 2466 | 33, | 
|  | 2467 | |_k, _av| Ok(()), | 
|  | 2468 | ) | 
|  | 2469 | .unwrap(); | 
| Janis Danisevskis | b42fc18 | 2020-12-15 08:41:27 -0800 | [diff] [blame] | 2470 | assert_eq!(key_entry, make_test_key_entry_test_vector(key_id)); | 
| Janis Danisevskis | aec1459 | 2020-11-12 09:41:49 -0800 | [diff] [blame] | 2471 | let state = Arc::new(AtomicU8::new(1)); | 
|  | 2472 | let state2 = state.clone(); | 
|  | 2473 |  | 
|  | 2474 | // Spawning a second thread that attempts to acquire the key id lock | 
|  | 2475 | // for the same key as the primary thread. The primary thread then | 
|  | 2476 | // waits, thereby forcing the secondary thread into the second stage | 
|  | 2477 | // of acquiring the lock (see KEY ID LOCK 2/2 above). | 
|  | 2478 | // The test succeeds if the secondary thread observes the transition | 
|  | 2479 | // of `state` from 1 to 2, despite having a whole second to overtake | 
|  | 2480 | // the primary thread. | 
|  | 2481 | let handle = thread::spawn(move || { | 
| Janis Danisevskis | bf15d73 | 2020-12-08 10:35:26 -0800 | [diff] [blame] | 2482 | let temp_dir = temp_dir_clone; | 
|  | 2483 | let mut db = KeystoreDB::new(temp_dir.path()).unwrap(); | 
| Janis Danisevskis | aec1459 | 2020-11-12 09:41:49 -0800 | [diff] [blame] | 2484 | assert!(db | 
|  | 2485 | .load_key_entry( | 
|  | 2486 | KeyDescriptor { | 
|  | 2487 | domain: Domain::APP, | 
|  | 2488 | nspace: 0, | 
|  | 2489 | alias: Some(KEY_LOCK_TEST_ALIAS.to_string()), | 
|  | 2490 | blob: None, | 
|  | 2491 | }, | 
| Janis Danisevskis | b42fc18 | 2020-12-15 08:41:27 -0800 | [diff] [blame] | 2492 | KeyType::Client, | 
| Janis Danisevskis | aec1459 | 2020-11-12 09:41:49 -0800 | [diff] [blame] | 2493 | KeyEntryLoadBits::BOTH, | 
|  | 2494 | 33, | 
|  | 2495 | |_k, _av| Ok(()), | 
|  | 2496 | ) | 
|  | 2497 | .is_ok()); | 
|  | 2498 | // We should only see a 2 here because we can only return | 
|  | 2499 | // from load_key_entry when the `_key_guard` expires, | 
|  | 2500 | // which happens at the end of the scope. | 
|  | 2501 | assert_eq!(2, state2.load(Ordering::Relaxed)); | 
|  | 2502 | }); | 
|  | 2503 |  | 
|  | 2504 | thread::sleep(std::time::Duration::from_millis(1000)); | 
|  | 2505 |  | 
|  | 2506 | assert_eq!(Ok(1), state.compare_exchange(1, 2, Ordering::Relaxed, Ordering::Relaxed)); | 
|  | 2507 |  | 
|  | 2508 | // Return the handle from this scope so we can join with the | 
|  | 2509 | // secondary thread after the key id lock has expired. | 
|  | 2510 | handle | 
|  | 2511 | // This is where the `_key_guard` goes out of scope, | 
|  | 2512 | // which is the reason for concurrent load_key_entry on the same key | 
|  | 2513 | // to unblock. | 
|  | 2514 | }; | 
|  | 2515 | // Join with the secondary thread and unwrap, to propagate failing asserts to the | 
|  | 2516 | // main test thread. We will not see failing asserts in secondary threads otherwise. | 
|  | 2517 | handle.join().unwrap(); | 
|  | 2518 | Ok(()) | 
|  | 2519 | } | 
|  | 2520 |  | 
| Janis Danisevskis | e92a5e6 | 2020-12-02 12:57:41 -0800 | [diff] [blame] | 2521 | #[test] | 
|  | 2522 | fn list() -> Result<()> { | 
|  | 2523 | let temp_dir = TempDir::new("list_test")?; | 
|  | 2524 | let mut db = KeystoreDB::new(temp_dir.path())?; | 
|  | 2525 | static LIST_O_ENTRIES: &[(Domain, i64, &str)] = &[ | 
|  | 2526 | (Domain::APP, 1, "test1"), | 
|  | 2527 | (Domain::APP, 1, "test2"), | 
|  | 2528 | (Domain::APP, 1, "test3"), | 
|  | 2529 | (Domain::APP, 1, "test4"), | 
|  | 2530 | (Domain::APP, 1, "test5"), | 
|  | 2531 | (Domain::APP, 1, "test6"), | 
|  | 2532 | (Domain::APP, 1, "test7"), | 
|  | 2533 | (Domain::APP, 2, "test1"), | 
|  | 2534 | (Domain::APP, 2, "test2"), | 
|  | 2535 | (Domain::APP, 2, "test3"), | 
|  | 2536 | (Domain::APP, 2, "test4"), | 
|  | 2537 | (Domain::APP, 2, "test5"), | 
|  | 2538 | (Domain::APP, 2, "test6"), | 
|  | 2539 | (Domain::APP, 2, "test8"), | 
|  | 2540 | (Domain::SELINUX, 100, "test1"), | 
|  | 2541 | (Domain::SELINUX, 100, "test2"), | 
|  | 2542 | (Domain::SELINUX, 100, "test3"), | 
|  | 2543 | (Domain::SELINUX, 100, "test4"), | 
|  | 2544 | (Domain::SELINUX, 100, "test5"), | 
|  | 2545 | (Domain::SELINUX, 100, "test6"), | 
|  | 2546 | (Domain::SELINUX, 100, "test9"), | 
|  | 2547 | ]; | 
|  | 2548 |  | 
|  | 2549 | let list_o_keys: Vec<(i64, i64)> = LIST_O_ENTRIES | 
|  | 2550 | .iter() | 
|  | 2551 | .map(|(domain, ns, alias)| { | 
|  | 2552 | let entry = | 
|  | 2553 | make_test_key_entry(&mut db, *domain, *ns, *alias).unwrap_or_else(|e| { | 
|  | 2554 | panic!("Failed to insert {:?} {} {}. Error {:?}", domain, ns, alias, e) | 
|  | 2555 | }); | 
|  | 2556 | (entry.id(), *ns) | 
|  | 2557 | }) | 
|  | 2558 | .collect(); | 
|  | 2559 |  | 
|  | 2560 | for (domain, namespace) in | 
|  | 2561 | &[(Domain::APP, 1i64), (Domain::APP, 2i64), (Domain::SELINUX, 100i64)] | 
|  | 2562 | { | 
|  | 2563 | let mut list_o_descriptors: Vec<KeyDescriptor> = LIST_O_ENTRIES | 
|  | 2564 | .iter() | 
|  | 2565 | .filter_map(|(domain, ns, alias)| match ns { | 
|  | 2566 | ns if *ns == *namespace => Some(KeyDescriptor { | 
|  | 2567 | domain: *domain, | 
|  | 2568 | nspace: *ns, | 
|  | 2569 | alias: Some(alias.to_string()), | 
|  | 2570 | blob: None, | 
|  | 2571 | }), | 
|  | 2572 | _ => None, | 
|  | 2573 | }) | 
|  | 2574 | .collect(); | 
|  | 2575 | list_o_descriptors.sort(); | 
|  | 2576 | let mut list_result = db.list(*domain, *namespace)?; | 
|  | 2577 | list_result.sort(); | 
|  | 2578 | assert_eq!(list_o_descriptors, list_result); | 
|  | 2579 |  | 
|  | 2580 | let mut list_o_ids: Vec<i64> = list_o_descriptors | 
|  | 2581 | .into_iter() | 
|  | 2582 | .map(|d| { | 
|  | 2583 | let (_, entry) = db | 
| Janis Danisevskis | b42fc18 | 2020-12-15 08:41:27 -0800 | [diff] [blame] | 2584 | .load_key_entry( | 
|  | 2585 | d, | 
|  | 2586 | KeyType::Client, | 
|  | 2587 | KeyEntryLoadBits::NONE, | 
|  | 2588 | *namespace as u32, | 
|  | 2589 | |_, _| Ok(()), | 
|  | 2590 | ) | 
| Janis Danisevskis | e92a5e6 | 2020-12-02 12:57:41 -0800 | [diff] [blame] | 2591 | .unwrap(); | 
|  | 2592 | entry.id() | 
|  | 2593 | }) | 
|  | 2594 | .collect(); | 
|  | 2595 | list_o_ids.sort_unstable(); | 
|  | 2596 | let mut loaded_entries: Vec<i64> = list_o_keys | 
|  | 2597 | .iter() | 
|  | 2598 | .filter_map(|(id, ns)| match ns { | 
|  | 2599 | ns if *ns == *namespace => Some(*id), | 
|  | 2600 | _ => None, | 
|  | 2601 | }) | 
|  | 2602 | .collect(); | 
|  | 2603 | loaded_entries.sort_unstable(); | 
|  | 2604 | assert_eq!(list_o_ids, loaded_entries); | 
|  | 2605 | } | 
|  | 2606 | assert_eq!(Vec::<KeyDescriptor>::new(), db.list(Domain::SELINUX, 101)?); | 
|  | 2607 |  | 
|  | 2608 | Ok(()) | 
|  | 2609 | } | 
|  | 2610 |  | 
| Joel Galenson | 0891bc1 | 2020-07-20 10:37:03 -0700 | [diff] [blame] | 2611 | // Helpers | 
|  | 2612 |  | 
|  | 2613 | // Checks that the given result is an error containing the given string. | 
|  | 2614 | fn check_result_is_error_containing_string<T>(result: Result<T>, target: &str) { | 
|  | 2615 | let error_str = format!( | 
|  | 2616 | "{:#?}", | 
|  | 2617 | result.err().unwrap_or_else(|| panic!("Expected the error: {}", target)) | 
|  | 2618 | ); | 
|  | 2619 | assert!( | 
|  | 2620 | error_str.contains(target), | 
|  | 2621 | "The string \"{}\" should contain \"{}\"", | 
|  | 2622 | error_str, | 
|  | 2623 | target | 
|  | 2624 | ); | 
|  | 2625 | } | 
|  | 2626 |  | 
| Joel Galenson | 2aab443 | 2020-07-22 15:27:57 -0700 | [diff] [blame] | 2627 | #[derive(Debug, PartialEq)] | 
| Joel Galenson | 0891bc1 | 2020-07-20 10:37:03 -0700 | [diff] [blame] | 2628 | #[allow(dead_code)] | 
|  | 2629 | struct KeyEntryRow { | 
| Janis Danisevskis | 63f7bc8 | 2020-09-03 10:12:56 -0700 | [diff] [blame] | 2630 | id: i64, | 
| Janis Danisevskis | b42fc18 | 2020-12-15 08:41:27 -0800 | [diff] [blame] | 2631 | key_type: KeyType, | 
| Janis Danisevskis | c5b210b | 2020-09-11 13:27:37 -0700 | [diff] [blame] | 2632 | domain: Option<Domain>, | 
| Joel Galenson | 0891bc1 | 2020-07-20 10:37:03 -0700 | [diff] [blame] | 2633 | namespace: Option<i64>, | 
|  | 2634 | alias: Option<String>, | 
| Janis Danisevskis | 93927dd | 2020-12-23 12:23:08 -0800 | [diff] [blame] | 2635 | state: KeyLifeCycle, | 
| Joel Galenson | 0891bc1 | 2020-07-20 10:37:03 -0700 | [diff] [blame] | 2636 | } | 
|  | 2637 |  | 
|  | 2638 | fn get_keyentry(db: &KeystoreDB) -> Result<Vec<KeyEntryRow>> { | 
|  | 2639 | db.conn | 
| Joel Galenson | 2aab443 | 2020-07-22 15:27:57 -0700 | [diff] [blame] | 2640 | .prepare("SELECT * FROM persistent.keyentry;")? | 
| Joel Galenson | 0891bc1 | 2020-07-20 10:37:03 -0700 | [diff] [blame] | 2641 | .query_map(NO_PARAMS, |row| { | 
| Joel Galenson | 0891bc1 | 2020-07-20 10:37:03 -0700 | [diff] [blame] | 2642 | Ok(KeyEntryRow { | 
|  | 2643 | id: row.get(0)?, | 
| Janis Danisevskis | b42fc18 | 2020-12-15 08:41:27 -0800 | [diff] [blame] | 2644 | key_type: row.get(1)?, | 
| Janis Danisevskis | c5b210b | 2020-09-11 13:27:37 -0700 | [diff] [blame] | 2645 | domain: match row.get(2)? { | 
|  | 2646 | Some(i) => Some(Domain(i)), | 
|  | 2647 | None => None, | 
|  | 2648 | }, | 
| Joel Galenson | 0891bc1 | 2020-07-20 10:37:03 -0700 | [diff] [blame] | 2649 | namespace: row.get(3)?, | 
|  | 2650 | alias: row.get(4)?, | 
| Janis Danisevskis | 93927dd | 2020-12-23 12:23:08 -0800 | [diff] [blame] | 2651 | state: row.get(5)?, | 
| Joel Galenson | 0891bc1 | 2020-07-20 10:37:03 -0700 | [diff] [blame] | 2652 | }) | 
|  | 2653 | })? | 
|  | 2654 | .map(|r| r.context("Could not read keyentry row.")) | 
|  | 2655 | .collect::<Result<Vec<_>>>() | 
|  | 2656 | } | 
|  | 2657 |  | 
| Janis Danisevskis | 3f322cb | 2020-09-03 14:46:22 -0700 | [diff] [blame] | 2658 | // Note: The parameters and SecurityLevel associations are nonsensical. This | 
|  | 2659 | // collection is only used to check if the parameters are preserved as expected by the | 
|  | 2660 | // database. | 
|  | 2661 | fn make_test_params() -> Vec<KeyParameter> { | 
|  | 2662 | vec![ | 
|  | 2663 | KeyParameter::new(KeyParameterValue::Invalid, SecurityLevel::TRUSTED_ENVIRONMENT), | 
|  | 2664 | KeyParameter::new( | 
|  | 2665 | KeyParameterValue::KeyPurpose(KeyPurpose::SIGN), | 
|  | 2666 | SecurityLevel::TRUSTED_ENVIRONMENT, | 
|  | 2667 | ), | 
|  | 2668 | KeyParameter::new( | 
|  | 2669 | KeyParameterValue::KeyPurpose(KeyPurpose::DECRYPT), | 
|  | 2670 | SecurityLevel::TRUSTED_ENVIRONMENT, | 
|  | 2671 | ), | 
|  | 2672 | KeyParameter::new( | 
|  | 2673 | KeyParameterValue::Algorithm(Algorithm::RSA), | 
|  | 2674 | SecurityLevel::TRUSTED_ENVIRONMENT, | 
|  | 2675 | ), | 
|  | 2676 | KeyParameter::new(KeyParameterValue::KeySize(1024), SecurityLevel::TRUSTED_ENVIRONMENT), | 
|  | 2677 | KeyParameter::new( | 
|  | 2678 | KeyParameterValue::BlockMode(BlockMode::ECB), | 
|  | 2679 | SecurityLevel::TRUSTED_ENVIRONMENT, | 
|  | 2680 | ), | 
|  | 2681 | KeyParameter::new( | 
|  | 2682 | KeyParameterValue::BlockMode(BlockMode::GCM), | 
|  | 2683 | SecurityLevel::TRUSTED_ENVIRONMENT, | 
|  | 2684 | ), | 
|  | 2685 | KeyParameter::new(KeyParameterValue::Digest(Digest::NONE), SecurityLevel::STRONGBOX), | 
|  | 2686 | KeyParameter::new( | 
|  | 2687 | KeyParameterValue::Digest(Digest::MD5), | 
|  | 2688 | SecurityLevel::TRUSTED_ENVIRONMENT, | 
|  | 2689 | ), | 
|  | 2690 | KeyParameter::new( | 
|  | 2691 | KeyParameterValue::Digest(Digest::SHA_2_224), | 
|  | 2692 | SecurityLevel::TRUSTED_ENVIRONMENT, | 
|  | 2693 | ), | 
|  | 2694 | KeyParameter::new( | 
|  | 2695 | KeyParameterValue::Digest(Digest::SHA_2_256), | 
|  | 2696 | SecurityLevel::STRONGBOX, | 
|  | 2697 | ), | 
|  | 2698 | KeyParameter::new( | 
|  | 2699 | KeyParameterValue::PaddingMode(PaddingMode::NONE), | 
|  | 2700 | SecurityLevel::TRUSTED_ENVIRONMENT, | 
|  | 2701 | ), | 
|  | 2702 | KeyParameter::new( | 
|  | 2703 | KeyParameterValue::PaddingMode(PaddingMode::RSA_OAEP), | 
|  | 2704 | SecurityLevel::TRUSTED_ENVIRONMENT, | 
|  | 2705 | ), | 
|  | 2706 | KeyParameter::new( | 
|  | 2707 | KeyParameterValue::PaddingMode(PaddingMode::RSA_PSS), | 
|  | 2708 | SecurityLevel::STRONGBOX, | 
|  | 2709 | ), | 
|  | 2710 | KeyParameter::new( | 
|  | 2711 | KeyParameterValue::PaddingMode(PaddingMode::RSA_PKCS1_1_5_SIGN), | 
|  | 2712 | SecurityLevel::TRUSTED_ENVIRONMENT, | 
|  | 2713 | ), | 
|  | 2714 | KeyParameter::new(KeyParameterValue::CallerNonce, SecurityLevel::TRUSTED_ENVIRONMENT), | 
|  | 2715 | KeyParameter::new(KeyParameterValue::MinMacLength(256), SecurityLevel::STRONGBOX), | 
|  | 2716 | KeyParameter::new( | 
|  | 2717 | KeyParameterValue::EcCurve(EcCurve::P_224), | 
|  | 2718 | SecurityLevel::TRUSTED_ENVIRONMENT, | 
|  | 2719 | ), | 
|  | 2720 | KeyParameter::new(KeyParameterValue::EcCurve(EcCurve::P_256), SecurityLevel::STRONGBOX), | 
|  | 2721 | KeyParameter::new( | 
|  | 2722 | KeyParameterValue::EcCurve(EcCurve::P_384), | 
|  | 2723 | SecurityLevel::TRUSTED_ENVIRONMENT, | 
|  | 2724 | ), | 
|  | 2725 | KeyParameter::new( | 
|  | 2726 | KeyParameterValue::EcCurve(EcCurve::P_521), | 
|  | 2727 | SecurityLevel::TRUSTED_ENVIRONMENT, | 
|  | 2728 | ), | 
|  | 2729 | KeyParameter::new( | 
|  | 2730 | KeyParameterValue::RSAPublicExponent(3), | 
|  | 2731 | SecurityLevel::TRUSTED_ENVIRONMENT, | 
|  | 2732 | ), | 
|  | 2733 | KeyParameter::new( | 
|  | 2734 | KeyParameterValue::IncludeUniqueID, | 
|  | 2735 | SecurityLevel::TRUSTED_ENVIRONMENT, | 
|  | 2736 | ), | 
|  | 2737 | KeyParameter::new(KeyParameterValue::BootLoaderOnly, SecurityLevel::STRONGBOX), | 
|  | 2738 | KeyParameter::new(KeyParameterValue::RollbackResistance, SecurityLevel::STRONGBOX), | 
|  | 2739 | KeyParameter::new( | 
|  | 2740 | KeyParameterValue::ActiveDateTime(1234567890), | 
|  | 2741 | SecurityLevel::STRONGBOX, | 
|  | 2742 | ), | 
|  | 2743 | KeyParameter::new( | 
|  | 2744 | KeyParameterValue::OriginationExpireDateTime(1234567890), | 
|  | 2745 | SecurityLevel::TRUSTED_ENVIRONMENT, | 
|  | 2746 | ), | 
|  | 2747 | KeyParameter::new( | 
|  | 2748 | KeyParameterValue::UsageExpireDateTime(1234567890), | 
|  | 2749 | SecurityLevel::TRUSTED_ENVIRONMENT, | 
|  | 2750 | ), | 
|  | 2751 | KeyParameter::new( | 
|  | 2752 | KeyParameterValue::MinSecondsBetweenOps(1234567890), | 
|  | 2753 | SecurityLevel::TRUSTED_ENVIRONMENT, | 
|  | 2754 | ), | 
|  | 2755 | KeyParameter::new( | 
|  | 2756 | KeyParameterValue::MaxUsesPerBoot(1234567890), | 
|  | 2757 | SecurityLevel::TRUSTED_ENVIRONMENT, | 
|  | 2758 | ), | 
|  | 2759 | KeyParameter::new(KeyParameterValue::UserID(1), SecurityLevel::STRONGBOX), | 
|  | 2760 | KeyParameter::new(KeyParameterValue::UserSecureID(42), SecurityLevel::STRONGBOX), | 
|  | 2761 | KeyParameter::new( | 
|  | 2762 | KeyParameterValue::NoAuthRequired, | 
|  | 2763 | SecurityLevel::TRUSTED_ENVIRONMENT, | 
|  | 2764 | ), | 
|  | 2765 | KeyParameter::new( | 
|  | 2766 | KeyParameterValue::HardwareAuthenticatorType(HardwareAuthenticatorType::PASSWORD), | 
|  | 2767 | SecurityLevel::TRUSTED_ENVIRONMENT, | 
|  | 2768 | ), | 
|  | 2769 | KeyParameter::new(KeyParameterValue::AuthTimeout(1234567890), SecurityLevel::SOFTWARE), | 
|  | 2770 | KeyParameter::new(KeyParameterValue::AllowWhileOnBody, SecurityLevel::SOFTWARE), | 
|  | 2771 | KeyParameter::new( | 
|  | 2772 | KeyParameterValue::TrustedUserPresenceRequired, | 
|  | 2773 | SecurityLevel::TRUSTED_ENVIRONMENT, | 
|  | 2774 | ), | 
|  | 2775 | KeyParameter::new( | 
|  | 2776 | KeyParameterValue::TrustedConfirmationRequired, | 
|  | 2777 | SecurityLevel::TRUSTED_ENVIRONMENT, | 
|  | 2778 | ), | 
|  | 2779 | KeyParameter::new( | 
|  | 2780 | KeyParameterValue::UnlockedDeviceRequired, | 
|  | 2781 | SecurityLevel::TRUSTED_ENVIRONMENT, | 
|  | 2782 | ), | 
|  | 2783 | KeyParameter::new( | 
|  | 2784 | KeyParameterValue::ApplicationID(vec![1u8, 2u8, 3u8, 4u8]), | 
|  | 2785 | SecurityLevel::SOFTWARE, | 
|  | 2786 | ), | 
|  | 2787 | KeyParameter::new( | 
|  | 2788 | KeyParameterValue::ApplicationData(vec![4u8, 3u8, 2u8, 1u8]), | 
|  | 2789 | SecurityLevel::SOFTWARE, | 
|  | 2790 | ), | 
|  | 2791 | KeyParameter::new( | 
|  | 2792 | KeyParameterValue::CreationDateTime(12345677890), | 
|  | 2793 | SecurityLevel::SOFTWARE, | 
|  | 2794 | ), | 
|  | 2795 | KeyParameter::new( | 
|  | 2796 | KeyParameterValue::KeyOrigin(KeyOrigin::GENERATED), | 
|  | 2797 | SecurityLevel::TRUSTED_ENVIRONMENT, | 
|  | 2798 | ), | 
|  | 2799 | KeyParameter::new( | 
|  | 2800 | KeyParameterValue::RootOfTrust(vec![3u8, 2u8, 1u8, 4u8]), | 
|  | 2801 | SecurityLevel::TRUSTED_ENVIRONMENT, | 
|  | 2802 | ), | 
|  | 2803 | KeyParameter::new(KeyParameterValue::OSVersion(1), SecurityLevel::TRUSTED_ENVIRONMENT), | 
|  | 2804 | KeyParameter::new(KeyParameterValue::OSPatchLevel(2), SecurityLevel::SOFTWARE), | 
|  | 2805 | KeyParameter::new( | 
|  | 2806 | KeyParameterValue::UniqueID(vec![4u8, 3u8, 1u8, 2u8]), | 
|  | 2807 | SecurityLevel::SOFTWARE, | 
|  | 2808 | ), | 
|  | 2809 | KeyParameter::new( | 
|  | 2810 | KeyParameterValue::AttestationChallenge(vec![4u8, 3u8, 1u8, 2u8]), | 
|  | 2811 | SecurityLevel::TRUSTED_ENVIRONMENT, | 
|  | 2812 | ), | 
|  | 2813 | KeyParameter::new( | 
|  | 2814 | KeyParameterValue::AttestationApplicationID(vec![4u8, 3u8, 1u8, 2u8]), | 
|  | 2815 | SecurityLevel::TRUSTED_ENVIRONMENT, | 
|  | 2816 | ), | 
|  | 2817 | KeyParameter::new( | 
|  | 2818 | KeyParameterValue::AttestationIdBrand(vec![4u8, 3u8, 1u8, 2u8]), | 
|  | 2819 | SecurityLevel::TRUSTED_ENVIRONMENT, | 
|  | 2820 | ), | 
|  | 2821 | KeyParameter::new( | 
|  | 2822 | KeyParameterValue::AttestationIdDevice(vec![4u8, 3u8, 1u8, 2u8]), | 
|  | 2823 | SecurityLevel::TRUSTED_ENVIRONMENT, | 
|  | 2824 | ), | 
|  | 2825 | KeyParameter::new( | 
|  | 2826 | KeyParameterValue::AttestationIdProduct(vec![4u8, 3u8, 1u8, 2u8]), | 
|  | 2827 | SecurityLevel::TRUSTED_ENVIRONMENT, | 
|  | 2828 | ), | 
|  | 2829 | KeyParameter::new( | 
|  | 2830 | KeyParameterValue::AttestationIdSerial(vec![4u8, 3u8, 1u8, 2u8]), | 
|  | 2831 | SecurityLevel::TRUSTED_ENVIRONMENT, | 
|  | 2832 | ), | 
|  | 2833 | KeyParameter::new( | 
|  | 2834 | KeyParameterValue::AttestationIdIMEI(vec![4u8, 3u8, 1u8, 2u8]), | 
|  | 2835 | SecurityLevel::TRUSTED_ENVIRONMENT, | 
|  | 2836 | ), | 
|  | 2837 | KeyParameter::new( | 
|  | 2838 | KeyParameterValue::AttestationIdMEID(vec![4u8, 3u8, 1u8, 2u8]), | 
|  | 2839 | SecurityLevel::TRUSTED_ENVIRONMENT, | 
|  | 2840 | ), | 
|  | 2841 | KeyParameter::new( | 
|  | 2842 | KeyParameterValue::AttestationIdManufacturer(vec![4u8, 3u8, 1u8, 2u8]), | 
|  | 2843 | SecurityLevel::TRUSTED_ENVIRONMENT, | 
|  | 2844 | ), | 
|  | 2845 | KeyParameter::new( | 
|  | 2846 | KeyParameterValue::AttestationIdModel(vec![4u8, 3u8, 1u8, 2u8]), | 
|  | 2847 | SecurityLevel::TRUSTED_ENVIRONMENT, | 
|  | 2848 | ), | 
|  | 2849 | KeyParameter::new( | 
|  | 2850 | KeyParameterValue::VendorPatchLevel(3), | 
|  | 2851 | SecurityLevel::TRUSTED_ENVIRONMENT, | 
|  | 2852 | ), | 
|  | 2853 | KeyParameter::new( | 
|  | 2854 | KeyParameterValue::BootPatchLevel(4), | 
|  | 2855 | SecurityLevel::TRUSTED_ENVIRONMENT, | 
|  | 2856 | ), | 
|  | 2857 | KeyParameter::new( | 
|  | 2858 | KeyParameterValue::AssociatedData(vec![4u8, 3u8, 1u8, 2u8]), | 
|  | 2859 | SecurityLevel::TRUSTED_ENVIRONMENT, | 
|  | 2860 | ), | 
|  | 2861 | KeyParameter::new( | 
|  | 2862 | KeyParameterValue::Nonce(vec![4u8, 3u8, 1u8, 2u8]), | 
|  | 2863 | SecurityLevel::TRUSTED_ENVIRONMENT, | 
|  | 2864 | ), | 
|  | 2865 | KeyParameter::new( | 
|  | 2866 | KeyParameterValue::MacLength(256), | 
|  | 2867 | SecurityLevel::TRUSTED_ENVIRONMENT, | 
|  | 2868 | ), | 
|  | 2869 | KeyParameter::new( | 
|  | 2870 | KeyParameterValue::ResetSinceIdRotation, | 
|  | 2871 | SecurityLevel::TRUSTED_ENVIRONMENT, | 
|  | 2872 | ), | 
|  | 2873 | KeyParameter::new( | 
|  | 2874 | KeyParameterValue::ConfirmationToken(vec![5u8, 5u8, 5u8, 5u8]), | 
|  | 2875 | SecurityLevel::TRUSTED_ENVIRONMENT, | 
|  | 2876 | ), | 
|  | 2877 | ] | 
|  | 2878 | } | 
|  | 2879 |  | 
| Janis Danisevskis | 63f7bc8 | 2020-09-03 10:12:56 -0700 | [diff] [blame] | 2880 | fn make_test_key_entry( | 
|  | 2881 | db: &mut KeystoreDB, | 
| Janis Danisevskis | c5b210b | 2020-09-11 13:27:37 -0700 | [diff] [blame] | 2882 | domain: Domain, | 
| Janis Danisevskis | 63f7bc8 | 2020-09-03 10:12:56 -0700 | [diff] [blame] | 2883 | namespace: i64, | 
|  | 2884 | alias: &str, | 
| Janis Danisevskis | aec1459 | 2020-11-12 09:41:49 -0800 | [diff] [blame] | 2885 | ) -> Result<KeyIdGuard> { | 
| Janis Danisevskis | 63f7bc8 | 2020-09-03 10:12:56 -0700 | [diff] [blame] | 2886 | let key_id = db.create_key_entry(domain, namespace)?; | 
| Janis Danisevskis | 93927dd | 2020-12-23 12:23:08 -0800 | [diff] [blame] | 2887 | db.insert_blob(&key_id, SubComponentType::KEY_BLOB, TEST_KEY_BLOB)?; | 
|  | 2888 | db.insert_blob(&key_id, SubComponentType::CERT, TEST_CERT_BLOB)?; | 
|  | 2889 | db.insert_blob(&key_id, SubComponentType::CERT_CHAIN, TEST_CERT_CHAIN_BLOB)?; | 
| Janis Danisevskis | aec1459 | 2020-11-12 09:41:49 -0800 | [diff] [blame] | 2890 | db.insert_keyparameter(&key_id, &make_test_params())?; | 
| Janis Danisevskis | b42fc18 | 2020-12-15 08:41:27 -0800 | [diff] [blame] | 2891 | let mut metadata = KeyMetaData::new(); | 
|  | 2892 | metadata.add(KeyMetaEntry::EncryptedBy(EncryptedBy::Password)); | 
|  | 2893 | metadata.add(KeyMetaEntry::Salt(vec![1, 2, 3])); | 
|  | 2894 | metadata.add(KeyMetaEntry::Iv(vec![2, 3, 1])); | 
|  | 2895 | metadata.add(KeyMetaEntry::AeadTag(vec![3, 1, 2])); | 
|  | 2896 | db.insert_key_metadata(&key_id, &metadata)?; | 
| Janis Danisevskis | aec1459 | 2020-11-12 09:41:49 -0800 | [diff] [blame] | 2897 | db.rebind_alias(&key_id, alias, domain, namespace)?; | 
| Janis Danisevskis | 63f7bc8 | 2020-09-03 10:12:56 -0700 | [diff] [blame] | 2898 | Ok(key_id) | 
|  | 2899 | } | 
|  | 2900 |  | 
| Janis Danisevskis | b42fc18 | 2020-12-15 08:41:27 -0800 | [diff] [blame] | 2901 | fn make_test_key_entry_test_vector(key_id: i64) -> KeyEntry { | 
|  | 2902 | let mut metadata = KeyMetaData::new(); | 
|  | 2903 | metadata.add(KeyMetaEntry::EncryptedBy(EncryptedBy::Password)); | 
|  | 2904 | metadata.add(KeyMetaEntry::Salt(vec![1, 2, 3])); | 
|  | 2905 | metadata.add(KeyMetaEntry::Iv(vec![2, 3, 1])); | 
|  | 2906 | metadata.add(KeyMetaEntry::AeadTag(vec![3, 1, 2])); | 
|  | 2907 |  | 
|  | 2908 | KeyEntry { | 
|  | 2909 | id: key_id, | 
|  | 2910 | km_blob: Some(TEST_KEY_BLOB.to_vec()), | 
|  | 2911 | cert: Some(TEST_CERT_BLOB.to_vec()), | 
|  | 2912 | cert_chain: Some(TEST_CERT_CHAIN_BLOB.to_vec()), | 
|  | 2913 | sec_level: SecurityLevel::TRUSTED_ENVIRONMENT, | 
|  | 2914 | parameters: make_test_params(), | 
|  | 2915 | metadata, | 
|  | 2916 | } | 
|  | 2917 | } | 
|  | 2918 |  | 
| Janis Danisevskis | 63f7bc8 | 2020-09-03 10:12:56 -0700 | [diff] [blame] | 2919 | fn debug_dump_keyentry_table(db: &mut KeystoreDB) -> Result<()> { | 
| Janis Danisevskis | 93927dd | 2020-12-23 12:23:08 -0800 | [diff] [blame] | 2920 | let mut stmt = db.conn.prepare( | 
|  | 2921 | "SELECT id, key_type, domain, namespace, alias, state FROM persistent.keyentry;", | 
|  | 2922 | )?; | 
|  | 2923 | let rows = stmt.query_map::<(i64, KeyType, i32, i64, String, KeyLifeCycle), _, _>( | 
|  | 2924 | NO_PARAMS, | 
|  | 2925 | |row| { | 
|  | 2926 | Ok((row.get(0)?, row.get(1)?, row.get(2)?, row.get(3)?, row.get(4)?, row.get(5)?)) | 
|  | 2927 | }, | 
|  | 2928 | )?; | 
| Janis Danisevskis | 63f7bc8 | 2020-09-03 10:12:56 -0700 | [diff] [blame] | 2929 |  | 
|  | 2930 | println!("Key entry table rows:"); | 
|  | 2931 | for r in rows { | 
| Janis Danisevskis | 93927dd | 2020-12-23 12:23:08 -0800 | [diff] [blame] | 2932 | let (id, key_type, domain, namespace, alias, state) = r.unwrap(); | 
| Janis Danisevskis | 63f7bc8 | 2020-09-03 10:12:56 -0700 | [diff] [blame] | 2933 | println!( | 
| Janis Danisevskis | 93927dd | 2020-12-23 12:23:08 -0800 | [diff] [blame] | 2934 | "    id: {} KeyType: {:?} Domain: {} Namespace: {} Alias: {} State: {:?}", | 
|  | 2935 | id, key_type, domain, namespace, alias, state | 
| Janis Danisevskis | 63f7bc8 | 2020-09-03 10:12:56 -0700 | [diff] [blame] | 2936 | ); | 
|  | 2937 | } | 
|  | 2938 | Ok(()) | 
|  | 2939 | } | 
|  | 2940 |  | 
|  | 2941 | fn debug_dump_grant_table(db: &mut KeystoreDB) -> Result<()> { | 
| Janis Danisevskis | bf15d73 | 2020-12-08 10:35:26 -0800 | [diff] [blame] | 2942 | let mut stmt = db | 
|  | 2943 | .conn | 
|  | 2944 | .prepare("SELECT id, grantee, keyentryid, access_vector FROM persistent.grant;")?; | 
| Janis Danisevskis | 63f7bc8 | 2020-09-03 10:12:56 -0700 | [diff] [blame] | 2945 | let rows = stmt.query_map::<(i64, i64, i64, i64), _, _>(NO_PARAMS, |row| { | 
|  | 2946 | Ok((row.get(0)?, row.get(1)?, row.get(2)?, row.get(3)?)) | 
|  | 2947 | })?; | 
|  | 2948 |  | 
|  | 2949 | println!("Grant table rows:"); | 
|  | 2950 | for r in rows { | 
|  | 2951 | let (id, gt, ki, av) = r.unwrap(); | 
|  | 2952 | println!("    id: {} grantee: {} key_id: {} access_vector: {}", id, gt, ki, av); | 
|  | 2953 | } | 
|  | 2954 | Ok(()) | 
|  | 2955 | } | 
|  | 2956 |  | 
| Joel Galenson | 0891bc1 | 2020-07-20 10:37:03 -0700 | [diff] [blame] | 2957 | // Use a custom random number generator that repeats each number once. | 
|  | 2958 | // This allows us to test repeated elements. | 
|  | 2959 |  | 
|  | 2960 | thread_local! { | 
|  | 2961 | static RANDOM_COUNTER: RefCell<i64> = RefCell::new(0); | 
|  | 2962 | } | 
|  | 2963 |  | 
| Janis Danisevskis | 63f7bc8 | 2020-09-03 10:12:56 -0700 | [diff] [blame] | 2964 | fn reset_random() { | 
|  | 2965 | RANDOM_COUNTER.with(|counter| { | 
|  | 2966 | *counter.borrow_mut() = 0; | 
|  | 2967 | }) | 
|  | 2968 | } | 
|  | 2969 |  | 
| Joel Galenson | 0891bc1 | 2020-07-20 10:37:03 -0700 | [diff] [blame] | 2970 | pub fn random() -> i64 { | 
|  | 2971 | RANDOM_COUNTER.with(|counter| { | 
|  | 2972 | let result = *counter.borrow() / 2; | 
|  | 2973 | *counter.borrow_mut() += 1; | 
|  | 2974 | result | 
|  | 2975 | }) | 
|  | 2976 | } | 
| Hasini Gunasinghe | f70cf8e | 2020-11-11 01:02:41 +0000 | [diff] [blame] | 2977 |  | 
|  | 2978 | #[test] | 
|  | 2979 | fn test_last_off_body() -> Result<()> { | 
|  | 2980 | let mut db = new_test_db()?; | 
| Janis Danisevskis | 5ed8c53 | 2021-01-11 14:19:42 -0800 | [diff] [blame^] | 2981 | db.insert_last_off_body(MonotonicRawTime::now())?; | 
| Hasini Gunasinghe | f70cf8e | 2020-11-11 01:02:41 +0000 | [diff] [blame] | 2982 | let tx = db.conn.transaction_with_behavior(TransactionBehavior::Immediate)?; | 
|  | 2983 | let last_off_body_1 = KeystoreDB::get_last_off_body(&tx)?; | 
|  | 2984 | tx.commit()?; | 
|  | 2985 | let one_second = Duration::from_secs(1); | 
|  | 2986 | thread::sleep(one_second); | 
|  | 2987 | db.update_last_off_body(MonotonicRawTime::now())?; | 
|  | 2988 | let tx2 = db.conn.transaction_with_behavior(TransactionBehavior::Immediate)?; | 
|  | 2989 | let last_off_body_2 = KeystoreDB::get_last_off_body(&tx2)?; | 
|  | 2990 | tx2.commit()?; | 
|  | 2991 | assert!(last_off_body_1.seconds() < last_off_body_2.seconds()); | 
|  | 2992 | Ok(()) | 
|  | 2993 | } | 
| Joel Galenson | 26f4d01 | 2020-07-17 14:57:21 -0700 | [diff] [blame] | 2994 | } |