tpm_manager: add option to disable pre-initialization

Before this change, tpm_managerd always performed pre-initialization
if it found an unowned TPM upon start. That may not work for special
workflows like factory initialization.

This CL adds a check for a "no_preinit" flag file in /run/tpm_manager.
If that file exists, pre-initialization is not done.

Having this flag file in tpmfs allows for various control schemes,
including disabling pre-initialization for specific boots only and
copying this flag from stateful before starting tpm_managerd for
disabling for all boots at image level.

BUG=b:68671587
TEST=unit tests; check pre-init with and without the flag file

Change-Id: I69991896cc79977694bd328de1e716a54de7ab96
Reviewed-on: https://chromium-review.googlesource.com/756207
Commit-Ready: Andrey Pronin <apronin@chromium.org>
Tested-by: Andrey Pronin <apronin@chromium.org>
Reviewed-by: Hung-Te Lin <hungte@chromium.org>
diff --git a/tpm_manager/server/main.cc b/tpm_manager/server/main.cc
index 88a1bf0..5ededc3 100644
--- a/tpm_manager/server/main.cc
+++ b/tpm_manager/server/main.cc
@@ -17,6 +17,8 @@
 #include <string>
 
 #include <base/command_line.h>
+#include <base/files/file_path.h>
+#include <base/files/file_util.h>
 #include <brillo/syslog_logging.h>
 #if defined(USE_TPM2)
 #include <trunks/trunks_factory_impl.h>
@@ -34,6 +36,7 @@
 
 constexpr char kWaitForOwnershipTriggerSwitch[] = "wait_for_ownership_trigger";
 constexpr char kLogToStderrSwitch[] = "log_to_stderr";
+constexpr char kNoPreinitFlagFile[] = "/run/tpm_manager/no_preinit";
 
 }  // namespace
 
@@ -46,8 +49,10 @@
   }
   brillo::InitLog(flags);
 
+  bool perform_preinit = !base::PathExists(base::FilePath(kNoPreinitFlagFile));
   tpm_manager::TpmManagerService tpm_manager_service(
-      cl->HasSwitch(kWaitForOwnershipTriggerSwitch));
+      cl->HasSwitch(kWaitForOwnershipTriggerSwitch),
+      perform_preinit);
 #if defined(USE_BINDER_IPC)
   tpm_manager::BinderService ipc_service(&tpm_manager_service,
                                          &tpm_manager_service);
diff --git a/tpm_manager/server/tpm_manager_service.cc b/tpm_manager/server/tpm_manager_service.cc
index 8a93732..dc5ab03 100644
--- a/tpm_manager/server/tpm_manager_service.cc
+++ b/tpm_manager/server/tpm_manager_service.cc
@@ -49,10 +49,13 @@
 
 namespace tpm_manager {
 
-TpmManagerService::TpmManagerService(bool wait_for_ownership)
-    : wait_for_ownership_(wait_for_ownership) {}
+TpmManagerService::TpmManagerService(bool wait_for_ownership,
+                                     bool perform_preinit)
+    : wait_for_ownership_(wait_for_ownership),
+      perform_preinit_(perform_preinit) {}
 
 TpmManagerService::TpmManagerService(bool wait_for_ownership,
+                                     bool perform_preinit,
                                      LocalDataStore* local_data_store,
                                      TpmStatus* tpm_status,
                                      TpmInitializer* tpm_initializer,
@@ -61,7 +64,8 @@
       tpm_status_(tpm_status),
       tpm_initializer_(tpm_initializer),
       tpm_nvram_(tpm_nvram),
-      wait_for_ownership_(wait_for_ownership) {}
+      wait_for_ownership_(wait_for_ownership),
+      perform_preinit_(perform_preinit) {}
 
 bool TpmManagerService::Initialize() {
   worker_thread_.reset(new base::Thread("TpmManager Service Worker"));
@@ -117,7 +121,7 @@
       LOG(WARNING) << __func__ << ": TPM initialization failed.";
       return;
     }
-  } else {
+  } else if (perform_preinit_) {
     VLOG(1) << "Pre-initializing TPM.";
     tpm_initializer_->PreInitializeTpm();
   }
diff --git a/tpm_manager/server/tpm_manager_service.h b/tpm_manager/server/tpm_manager_service.h
index d20b6ea..2af5872 100644
--- a/tpm_manager/server/tpm_manager_service.h
+++ b/tpm_manager/server/tpm_manager_service.h
@@ -69,13 +69,19 @@
                           public TpmOwnershipInterface {
  public:
   // If |wait_for_ownership| is set, TPM initialization will be postponed until
-  // an explicit TakeOwnership request is received.
-  explicit TpmManagerService(bool wait_for_ownership);
+  // an explicit TakeOwnership request is received. If |perform_preinit| is
+  // additionally set, TPM pre-initialization will be performed in case TPM
+  // initialization is postponed.
+  explicit TpmManagerService(bool wait_for_ownership, bool perform_preinit);
 
   // If |wait_for_ownership| is set, TPM initialization will be postponed until
-  // an explicit TakeOwnership request is received. Does not take ownership of
-  // |local_data_store|, |tpm_status|, |tpm_initializer|, or |tpm_nvram|.
+  // an explicit TakeOwnership request is received. If |perform_preinit| is
+  // additionally set, TPM pre-initialization will be performed in case TPM
+  // initialization is postponed.
+  // Does not take ownership of |local_data_store|, |tpm_status|,
+  // |tpm_initializer|, or |tpm_nvram|.
   TpmManagerService(bool wait_for_ownership,
+                    bool perform_preinit,
                     LocalDataStore* local_data_store,
                     TpmStatus* tpm_status,
                     TpmInitializer* tpm_initializer,
@@ -232,6 +238,9 @@
   // Whether to wait for an explicit call to 'TakeOwnership' before initializing
   // the TPM. Normally tracks the --wait_for_ownership command line option.
   bool wait_for_ownership_;
+  // Whether to perform pre-initialization (where available) if initialization
+  // itself needs to wait for 'TakeOwnership' first.
+  bool perform_preinit_;
   // Background thread to allow processing of potentially lengthy TPM requests
   // in the background.
   std::unique_ptr<base::Thread> worker_thread_;
diff --git a/tpm_manager/server/tpm_manager_service_test.cc b/tpm_manager/server/tpm_manager_service_test.cc
index c04cb0f..abd00d2 100644
--- a/tpm_manager/server/tpm_manager_service_test.cc
+++ b/tpm_manager/server/tpm_manager_service_test.cc
@@ -50,8 +50,9 @@
   ~TpmManagerServiceTest() override = default;
   void SetUp() override {
     service_.reset(new TpmManagerService(
-        true /*wait_for_ownership*/, &mock_local_data_store_, &mock_tpm_status_,
-        &mock_tpm_initializer_, &mock_tpm_nvram_));
+        true /*wait_for_ownership*/, true /*perform_preinit*/,
+        &mock_local_data_store_, &mock_tpm_status_, &mock_tpm_initializer_,
+        &mock_tpm_nvram_));
     SetupService();
   }
 
@@ -88,8 +89,33 @@
   ~TpmManagerServiceTest_NoWaitForOwnership() override = default;
   void SetUp() override {
     service_.reset(new TpmManagerService(
-        false /*wait_for_ownership*/, &mock_local_data_store_,
-        &mock_tpm_status_, &mock_tpm_initializer_, &mock_tpm_nvram_));
+        false /*wait_for_ownership*/, false /*perform_preinit*/,
+        &mock_local_data_store_, &mock_tpm_status_, &mock_tpm_initializer_,
+        &mock_tpm_nvram_));
+  }
+};
+
+// Tests must call SetupService().
+class TpmManagerServiceTest_NoPreinit : public TpmManagerServiceTest {
+ public:
+  ~TpmManagerServiceTest_NoPreinit() override = default;
+  void SetUp() override {
+    service_.reset(new TpmManagerService(
+        true /*wait_for_ownership*/, false /*perform_preinit*/,
+        &mock_local_data_store_, &mock_tpm_status_, &mock_tpm_initializer_,
+        &mock_tpm_nvram_));
+  }
+};
+
+// Tests must call SetupService().
+class TpmManagerServiceTest_Preinit : public TpmManagerServiceTest {
+ public:
+  ~TpmManagerServiceTest_Preinit() override = default;
+  void SetUp() override {
+    service_.reset(new TpmManagerService(
+        true /*wait_for_ownership*/, true /*perform_preinit*/,
+        &mock_local_data_store_, &mock_tpm_status_, &mock_tpm_initializer_,
+        &mock_tpm_nvram_));
   }
 };
 
@@ -129,8 +155,17 @@
   Run();
 }
 
-TEST_F(TpmManagerServiceTest, NoAutoInitialize) {
+TEST_F(TpmManagerServiceTest_Preinit, NoAutoInitialize) {
   EXPECT_CALL(mock_tpm_initializer_, InitializeTpm()).Times(0);
+  EXPECT_CALL(mock_tpm_initializer_, PreInitializeTpm()).Times(1);
+  SetupService();
+  RunServiceWorkerAndQuit();
+}
+
+TEST_F(TpmManagerServiceTest_NoPreinit, NoPreInitialize) {
+  EXPECT_CALL(mock_tpm_initializer_, InitializeTpm()).Times(0);
+  EXPECT_CALL(mock_tpm_initializer_, PreInitializeTpm()).Times(0);
+  SetupService();
   RunServiceWorkerAndQuit();
 }