Merge "Keystore 2.0: Enforce MAX_BOOT_LEVEL in software"
diff --git a/keystore2/Android.bp b/keystore2/Android.bp
index 77ab83c..d480244 100644
--- a/keystore2/Android.bp
+++ b/keystore2/Android.bp
@@ -21,8 +21,8 @@
default_applicable_licenses: ["system_security_license"],
}
-rust_library {
- name: "libkeystore2",
+rust_defaults {
+ name: "libkeystore2_defaults",
crate_name: "keystore2",
srcs: ["src/lib.rs"],
@@ -42,6 +42,7 @@
"libkeystore2_crypto_rust",
"libkeystore2_km_compat",
"libkeystore2_selinux",
+ "libkeystore2_system_property-rust",
"libkeystore2_vintf_rust",
"liblazy_static",
"liblibc",
@@ -54,6 +55,11 @@
}
rust_library {
+ name: "libkeystore2",
+ defaults: ["libkeystore2_defaults"],
+}
+
+rust_library {
name: "libkeystore2_test_utils",
crate_name: "keystore2_test_utils",
srcs: ["test_utils/lib.rs"],
@@ -66,36 +72,13 @@
rust_test {
name: "keystore2_test",
crate_name: "keystore2",
- srcs: ["src/lib.rs"],
test_suites: ["general-tests"],
auto_gen_config: true,
compile_multilib: "first",
+ defaults: ["libkeystore2_defaults"],
rustlibs: [
- "android.hardware.security.keymint-V1-rust",
- "android.hardware.security.secureclock-V1-rust",
- "android.security.apc-rust",
- "android.security.authorization-rust",
- "android.security.compat-rust",
- "android.security.remoteprovisioning-rust",
- "android.security.usermanager-rust",
- "android.system.keystore2-V1-rust",
"libandroid_logger",
- "libanyhow",
- "libbinder_rs",
- "libkeystore2_aaid-rust",
- "libkeystore2_apc_compat-rust",
- "libkeystore2_crypto_rust",
- "libkeystore2_km_compat",
- "libkeystore2_selinux",
"libkeystore2_test_utils",
- "libkeystore2_vintf_rust",
- "liblazy_static",
- "liblibc",
- "liblibsqlite3_sys",
- "liblog_rust",
- "librand",
- "librusqlite",
- "libthiserror",
],
}
diff --git a/keystore2/src/enforcements.rs b/keystore2/src/enforcements.rs
index cc59c32..aa852f4 100644
--- a/keystore2/src/enforcements.rs
+++ b/keystore2/src/enforcements.rs
@@ -32,14 +32,15 @@
};
use android_system_keystore2::binder::Strong;
use anyhow::{Context, Result};
-use std::sync::{
- mpsc::{channel, Receiver, Sender},
- Arc, Mutex, Weak,
-};
-use std::time::SystemTime;
+use keystore2_system_property::PropertyWatcher;
use std::{
collections::{HashMap, HashSet},
- sync::mpsc::TryRecvError,
+ sync::{
+ atomic::{AtomicI32, Ordering},
+ mpsc::{channel, Receiver, Sender, TryRecvError},
+ Arc, Mutex, Weak,
+ },
+ time::SystemTime,
};
#[derive(Debug)]
@@ -352,6 +353,7 @@
}
/// Enforcements data structure
+#[derive(Default)]
pub struct Enforcements {
/// This hash set contains the user ids for whom the device is currently unlocked. If a user id
/// is not in the set, it implies that the device is locked for the user.
@@ -365,18 +367,11 @@
/// The enforcement module will try to get a confirmation token from this channel whenever
/// an operation that requires confirmation finishes.
confirmation_token_receiver: Arc<Mutex<Option<Receiver<Vec<u8>>>>>,
+ /// Highest boot level seen in keystore.boot_level; used to enforce MAX_BOOT_LEVEL tag.
+ boot_level: AtomicI32,
}
impl Enforcements {
- /// Creates an enforcement object with the two data structures it holds and the sender as None.
- pub fn new() -> Self {
- Enforcements {
- device_unlocked_set: Mutex::new(HashSet::new()),
- op_auth_map: Default::default(),
- confirmation_token_receiver: Default::default(),
- }
- }
-
/// Install the confirmation token receiver. The enforcement module will try to get a
/// confirmation token from this channel whenever an operation that requires confirmation
/// finishes.
@@ -475,6 +470,7 @@
let mut unlocked_device_required = false;
let mut key_usage_limited: Option<i64> = None;
let mut confirmation_token_receiver: Option<Arc<Mutex<Option<Receiver<Vec<u8>>>>>> = None;
+ let mut max_boot_level: Option<i32> = None;
// iterate through key parameters, recording information we need for authorization
// enforcements later, or enforcing authorizations in place, where applicable
@@ -541,6 +537,9 @@
KeyParameterValue::TrustedConfirmationRequired => {
confirmation_token_receiver = Some(self.confirmation_token_receiver.clone());
}
+ KeyParameterValue::MaxBootLevel(level) => {
+ max_boot_level = Some(*level);
+ }
// NOTE: as per offline discussion, sanitizing key parameters and rejecting
// create operation if any non-allowed tags are present, is not done in
// authorize_create (unlike in legacy keystore where AuthorizeBegin is rejected if
@@ -594,6 +593,13 @@
}
}
+ if let Some(level) = max_boot_level {
+ if level < self.boot_level.load(Ordering::SeqCst) {
+ return Err(Error::Km(Ec::BOOT_LEVEL_EXCEEDED))
+ .context("In authorize_create: boot level is too late.");
+ }
+ }
+
if !unlocked_device_required && no_auth_required {
return Ok((
None,
@@ -760,11 +766,34 @@
auth_bound && !skip_lskf_binding
}
-}
-impl Default for Enforcements {
- fn default() -> Self {
- Self::new()
+ /// Watch the `keystore.boot_level` system property, and keep self.boot_level up to date.
+ /// Blocks waiting for system property changes, so must be run in its own thread.
+ pub fn watch_boot_level(&self) -> Result<()> {
+ let mut w = PropertyWatcher::new("keystore.boot_level")?;
+ loop {
+ fn parse_value(_name: &str, value: &str) -> Result<Option<i32>> {
+ Ok(if value == "end" { None } else { Some(value.parse::<i32>()?) })
+ }
+ match w.read(parse_value)? {
+ Some(level) => {
+ let old = self.boot_level.fetch_max(level, Ordering::SeqCst);
+ log::info!(
+ "Read keystore.boot_level: {}; boot level {} -> {}",
+ level,
+ old,
+ std::cmp::max(old, level)
+ );
+ }
+ None => {
+ log::info!("keystore.boot_level is `end`, finishing.");
+ self.boot_level.fetch_max(i32::MAX, Ordering::SeqCst);
+ break;
+ }
+ }
+ w.wait()?;
+ }
+ Ok(())
}
}
diff --git a/keystore2/src/globals.rs b/keystore2/src/globals.rs
index e1b41b5..04bfbc9 100644
--- a/keystore2/src/globals.rs
+++ b/keystore2/src/globals.rs
@@ -160,7 +160,7 @@
/// priorities.
pub static ref ASYNC_TASK: Arc<AsyncTask> = Default::default();
/// Singleton for enforcements.
- pub static ref ENFORCEMENTS: Enforcements = Enforcements::new();
+ pub static ref ENFORCEMENTS: Enforcements = Default::default();
/// LegacyBlobLoader is initialized and exists globally.
/// The same directory used by the database is used by the LegacyBlobLoader as well.
pub static ref LEGACY_BLOB_LOADER: Arc<LegacyBlobLoader> = Arc::new(LegacyBlobLoader::new(
diff --git a/keystore2/src/key_parameter.rs b/keystore2/src/key_parameter.rs
index 117dea8..c10da95 100644
--- a/keystore2/src/key_parameter.rs
+++ b/keystore2/src/key_parameter.rs
@@ -965,6 +965,9 @@
/// Used to deliver the not after date in milliseconds to KeyMint during key generation/import.
#[key_param(tag = CERTIFICATE_NOT_AFTER, field = DateTime)]
CertificateNotAfter(i64),
+ /// Specifies a maximum boot level at which a key should function
+ #[key_param(tag = MAX_BOOT_LEVEL, field = Integer)]
+ MaxBootLevel(i32),
}
}
diff --git a/keystore2/src/keystore2_main.rs b/keystore2/src/keystore2_main.rs
index 0d14f84..7739f5e 100644
--- a/keystore2/src/keystore2_main.rs
+++ b/keystore2/src/keystore2_main.rs
@@ -67,6 +67,13 @@
ENFORCEMENTS.install_confirmation_token_receiver(confirmation_token_receiver);
+ info!("Starting boot level watcher.");
+ std::thread::spawn(|| {
+ keystore2::globals::ENFORCEMENTS
+ .watch_boot_level()
+ .unwrap_or_else(|e| error!("watch_boot_level failed: {}", e));
+ });
+
info!("Starting thread pool now.");
binder::ProcessState::start_thread_pool();
diff --git a/keystore2/src/km_compat/km_compat_type_conversion.h b/keystore2/src/km_compat/km_compat_type_conversion.h
index b36b78a..c2b4669 100644
--- a/keystore2/src/km_compat/km_compat_type_conversion.h
+++ b/keystore2/src/km_compat/km_compat_type_conversion.h
@@ -740,6 +740,9 @@
case KMV1::Tag::CERTIFICATE_NOT_AFTER:
// These tags do not exist in KM < KeyMint 1.0.
break;
+ case KMV1::Tag::MAX_BOOT_LEVEL:
+ // Does not exist in API level 30 or below.
+ break;
}
return V4_0::KeyParameter{.tag = V4_0::Tag::INVALID};
}
diff --git a/keystore2/system_property/Android.bp b/keystore2/system_property/Android.bp
new file mode 100644
index 0000000..f6a810b
--- /dev/null
+++ b/keystore2/system_property/Android.bp
@@ -0,0 +1,43 @@
+// Copyright 2021, The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+rust_bindgen {
+ name: "libkeystore2_system_property_bindgen",
+ wrapper_src: "system_property_bindgen.hpp",
+ crate_name: "keystore2_system_property_bindgen",
+ source_stem: "bindings",
+
+ bindgen_flags: [
+ "--size_t-is-usize",
+ "--whitelist-function=__system_property_find",
+ "--whitelist-function=__system_property_read_callback",
+ "--whitelist-function=__system_property_wait",
+ ],
+}
+
+rust_library {
+ name: "libkeystore2_system_property-rust",
+ crate_name: "keystore2_system_property",
+ srcs: [
+ "lib.rs",
+ ],
+ rustlibs: [
+ "libanyhow",
+ "libkeystore2_system_property_bindgen",
+ "libthiserror",
+ ],
+ shared_libs: [
+ "libbase",
+ ],
+}
diff --git a/keystore2/system_property/lib.rs b/keystore2/system_property/lib.rs
new file mode 100644
index 0000000..f14cf0e
--- /dev/null
+++ b/keystore2/system_property/lib.rs
@@ -0,0 +1,148 @@
+// Copyright 2021, The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+//! This crate provides the PropertyWatcher type, which watches for changes
+//! in Android system properties.
+
+use std::os::raw::c_char;
+use std::ptr::null_mut;
+use std::{
+ ffi::{c_void, CStr, CString},
+ str::Utf8Error,
+};
+use thiserror::Error;
+
+/// Errors this crate can generate
+#[derive(Error, Debug)]
+pub enum PropertyWatcherError {
+ /// We can't watch for a property whose name contains a NUL character.
+ #[error("Cannot convert name to C string")]
+ BadNameError(#[from] std::ffi::NulError),
+ /// We can only watch for properties that exist when the watcher is created.
+ #[error("System property is absent")]
+ SystemPropertyAbsent,
+ /// __system_property_wait timed out despite being given no timeout.
+ #[error("Wait failed")]
+ WaitFailed,
+ /// read callback was not called
+ #[error("__system_property_read_callback did not call callback")]
+ ReadCallbackNotCalled,
+ /// read callback gave us a NULL pointer
+ #[error("__system_property_read_callback gave us a NULL pointer instead of a string")]
+ MissingCString,
+ /// read callback gave us a bad C string
+ #[error("__system_property_read_callback gave us a non-UTF8 C string")]
+ BadCString(#[from] Utf8Error),
+ /// read callback returned an error
+ #[error("Callback failed")]
+ CallbackError(#[from] anyhow::Error),
+}
+
+/// Result type specific for this crate.
+pub type Result<T> = std::result::Result<T, PropertyWatcherError>;
+
+/// PropertyWatcher takes the name of an Android system property such
+/// as `keystore.boot_level`; it can report the current value of this
+/// property, or wait for it to change.
+pub struct PropertyWatcher {
+ prop_info: *const keystore2_system_property_bindgen::prop_info,
+ serial: keystore2_system_property_bindgen::__uint32_t,
+}
+
+impl PropertyWatcher {
+ /// Create a PropertyWatcher for the named system property.
+ pub fn new(name: &str) -> Result<Self> {
+ let cstr = CString::new(name)?;
+ // Unsafe FFI call. We generate the CStr in this function
+ // and so ensure it is valid during call.
+ // Returned pointer is valid for the lifetime of the program.
+ let prop_info =
+ unsafe { keystore2_system_property_bindgen::__system_property_find(cstr.as_ptr()) };
+ if prop_info.is_null() {
+ Err(PropertyWatcherError::SystemPropertyAbsent)
+ } else {
+ Ok(Self { prop_info, serial: 0 })
+ }
+ }
+
+ fn read_raw(&self, mut f: impl FnOnce(Option<&CStr>, Option<&CStr>)) {
+ // Unsafe function converts values passed to us by
+ // __system_property_read_callback to Rust form
+ // and pass them to inner callback.
+ unsafe extern "C" fn callback(
+ res_p: *mut c_void,
+ name: *const c_char,
+ value: *const c_char,
+ _: keystore2_system_property_bindgen::__uint32_t,
+ ) {
+ let name = if name.is_null() { None } else { Some(CStr::from_ptr(name)) };
+ let value = if value.is_null() { None } else { Some(CStr::from_ptr(value)) };
+ let f = &mut *res_p.cast::<&mut dyn FnMut(Option<&CStr>, Option<&CStr>)>();
+ f(name, value);
+ }
+
+ let mut f: &mut dyn FnOnce(Option<&CStr>, Option<&CStr>) = &mut f;
+
+ // Unsafe block for FFI call. We convert the FnOnce
+ // to a void pointer, and unwrap it in our callback.
+ unsafe {
+ keystore2_system_property_bindgen::__system_property_read_callback(
+ self.prop_info,
+ Some(callback),
+ &mut f as *mut _ as *mut c_void,
+ )
+ }
+ }
+
+ /// Call the passed function, passing it the name and current value
+ /// of this system property. See documentation for
+ /// `__system_property_read_callback` for details.
+ pub fn read<T, F>(&self, f: F) -> Result<T>
+ where
+ F: FnOnce(&str, &str) -> anyhow::Result<T>,
+ {
+ let mut result = Err(PropertyWatcherError::ReadCallbackNotCalled);
+ self.read_raw(|name, value| {
+ // use a wrapping closure as an erzatz try block.
+ result = (|| {
+ let name = name.ok_or(PropertyWatcherError::MissingCString)?.to_str()?;
+ let value = value.ok_or(PropertyWatcherError::MissingCString)?.to_str()?;
+ f(name, value).map_err(PropertyWatcherError::CallbackError)
+ })()
+ });
+ result
+ }
+
+ /// Wait for the system property to change. This
+ /// records the serial number of the last change, so
+ /// race conditions are avoided.
+ pub fn wait(&mut self) -> Result<()> {
+ let mut new_serial = self.serial;
+ // Unsafe block to call __system_property_wait.
+ // All arguments are private to PropertyWatcher so we
+ // can be confident they are valid.
+ if !unsafe {
+ keystore2_system_property_bindgen::__system_property_wait(
+ self.prop_info,
+ self.serial,
+ &mut new_serial,
+ null_mut(),
+ )
+ } {
+ return Err(PropertyWatcherError::WaitFailed);
+ }
+ self.serial = new_serial;
+ Ok(())
+ }
+}
diff --git a/keystore2/system_property/system_property_bindgen.hpp b/keystore2/system_property/system_property_bindgen.hpp
new file mode 100644
index 0000000..e3c1ade
--- /dev/null
+++ b/keystore2/system_property/system_property_bindgen.hpp
@@ -0,0 +1,18 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#pragma once
+
+#include "sys/system_properties.h"