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"