| // Copyright (c) 2011 The Chromium OS Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| // This is the Chaps daemon. It handles calls from multiple processes via D-Bus. |
| // |
| |
| #include <signal.h> |
| |
| #include <memory> |
| #include <string> |
| |
| #include <base/command_line.h> |
| #include <base/location.h> |
| #include <base/logging.h> |
| #include <base/memory/ptr_util.h> |
| #include <base/strings/string_number_conversions.h> |
| #include <base/synchronization/lock.h> |
| #include <base/synchronization/waitable_event.h> |
| #include <base/threading/platform_thread.h> |
| #include <base/threading/thread.h> |
| #include <brillo/daemons/dbus_daemon.h> |
| #include <brillo/syslog_logging.h> |
| |
| #include "chaps/chaps_adaptor.h" |
| #include "chaps/chaps_factory_impl.h" |
| #include "chaps/chaps_service.h" |
| #include "chaps/chaps_utility.h" |
| #include "chaps/dbus_bindings/constants.h" |
| #include "chaps/platform_globals.h" |
| #include "chaps/slot_manager_impl.h" |
| |
| #if USE_TPM2 |
| #include "chaps/tpm2_utility_impl.h" |
| #else |
| #include "chaps/tpm_utility_impl.h" |
| #endif |
| |
| using base::AutoLock; |
| using base::Lock; |
| using base::PlatformThread; |
| using base::PlatformThreadHandle; |
| using base::WaitableEvent; |
| using std::string; |
| |
| namespace { |
| |
| const char kTpmThreadName[] = "tpm_background_thread"; |
| const char kInitThreadName[] = "async_init_thread"; |
| |
| void MaskSignals() { |
| sigset_t signal_mask; |
| CHECK_EQ(0, sigemptyset(&signal_mask)); |
| for (int signal : {SIGTERM, SIGINT, SIGHUP}) { |
| CHECK_EQ(0, sigaddset(&signal_mask, signal)); |
| } |
| CHECK_EQ(0, sigprocmask(SIG_BLOCK, &signal_mask, nullptr)); |
| } |
| |
| void InitAsync(WaitableEvent* started_event, |
| Lock* lock, |
| chaps::TPMUtility* tpm, |
| chaps::SlotManagerImpl* slot_manager) { |
| // It's important that we acquire 'lock' before signaling 'started_event'. |
| // This will prevent any D-Bus requests from being processed until we've |
| // finished initialization. |
| AutoLock auto_lock(*lock); |
| started_event->Signal(); |
| LOG(INFO) << "Starting asynchronous initialization."; |
| if (!tpm->Init()) |
| // Just warn and continue in this case. The effect will be a functional |
| // daemon which handles dbus requests but any attempt to load a token will |
| // fail. To a PKCS #11 client this will look like a library with a few |
| // empty slots. |
| LOG(WARNING) << "TPM initialization failed (this is expected if no TPM is" |
| << " available). PKCS #11 tokens will not be available."; |
| if (!slot_manager->Init()) |
| LOG(FATAL) << "Slot initialization failed."; |
| } |
| |
| } // namespace |
| |
| namespace chaps { |
| |
| class Daemon : public brillo::DBusServiceDaemon { |
| public: |
| Daemon(const std::string& srk_auth_data, bool auto_load_system_token) |
| : DBusServiceDaemon(kChapsServiceName, |
| dbus::ObjectPath(kObjectManagerPath)), |
| srk_auth_data_(srk_auth_data), |
| auto_load_system_token_(auto_load_system_token), |
| tpm_background_thread_(kTpmThreadName), |
| async_init_thread_(kInitThreadName) {} |
| |
| protected: |
| int OnInit() override { |
| #if USE_TPM2 |
| CHECK(tpm_background_thread_.StartWithOptions( |
| base::Thread::Options(base::MessageLoop::TYPE_IO, |
| 0 /* use default stack size */))); |
| tpm_.reset(new TPM2UtilityImpl(tpm_background_thread_.task_runner())); |
| #else |
| // Instantiate a TPM1.2 Utility. |
| tpm_.reset(new TPMUtilityImpl(srk_auth_data_)); |
| #endif |
| |
| factory_.reset(new ChapsFactoryImpl); |
| slot_manager_.reset(new SlotManagerImpl( |
| factory_.get(), tpm_.get(), auto_load_system_token_)); |
| service_.reset(new ChapsServiceImpl(slot_manager_.get())); |
| |
| // Initialize the TPM utility and slot manager asynchronously because |
| // we might be able to serve some requests while they are being |
| // initialized. |
| WaitableEvent init_started(true, false); |
| CHECK(async_init_thread_.StartWithOptions( |
| base::Thread::Options(base::MessageLoop::TYPE_IO, |
| 0 /* use default stack size */))); |
| async_init_thread_.task_runner()->PostTask( |
| FROM_HERE, |
| base::Bind(&InitAsync, |
| &init_started, &lock_, tpm_.get(), slot_manager_.get())); |
| // We're not finished with initialization until the initialization thread |
| // has had a chance to acquire the lock. |
| init_started.Wait(); |
| // Now we can export D-Bus objects. |
| return DBusServiceDaemon::OnInit(); |
| } |
| |
| void RegisterDBusObjectsAsync( |
| brillo::dbus_utils::AsyncEventSequencer* sequencer) override { |
| adaptor_.reset(new ChapsAdaptor(object_manager_.get(), |
| &lock_, |
| service_.get(), |
| slot_manager_.get())); |
| adaptor_->RegisterAsync( |
| sequencer->GetHandler("RegisterAsync() failed", true)); |
| } |
| |
| private: |
| std::string srk_auth_data_; |
| bool auto_load_system_token_; |
| base::Thread tpm_background_thread_; |
| base::Thread async_init_thread_; |
| Lock lock_; |
| |
| std::unique_ptr<TPMUtility> tpm_; |
| std::unique_ptr<ChapsFactory> factory_; |
| std::unique_ptr<SlotManagerImpl> slot_manager_; |
| std::unique_ptr<ChapsInterface> service_; |
| std::unique_ptr<ChapsAdaptor> adaptor_; |
| |
| DISALLOW_COPY_AND_ASSIGN(Daemon); |
| }; |
| |
| } // namespace chaps |
| |
| int main(int argc, char** argv) { |
| base::CommandLine::Init(argc, argv); |
| base::CommandLine* cl = base::CommandLine::ForCurrentProcess(); |
| brillo::InitLog(brillo::kLogToSyslog | brillo::kLogToStderr); |
| chaps::ScopedOpenSSL openssl; |
| |
| LOG(INFO) << "Starting PKCS #11 services."; |
| // Run as 'chaps'. |
| chaps::SetProcessUserAndGroup(chaps::kChapsdProcessUser, |
| chaps::kChapsdProcessGroup, |
| true); |
| // Determine SRK authorization data from the command line. |
| string srk_auth_data; |
| if (cl->HasSwitch("srk_password")) { |
| srk_auth_data = cl->GetSwitchValueASCII("srk_password"); |
| } else if (cl->HasSwitch("srk_zeros")) { |
| int zero_count = 0; |
| if (base::StringToInt(cl->GetSwitchValueASCII("srk_zeros"), |
| &zero_count)) { |
| srk_auth_data = string(zero_count, 0); |
| } else { |
| LOG(WARNING) << "Invalid value for srk_zeros: using empty string."; |
| } |
| } |
| bool auto_load_system_token = cl->HasSwitch("auto_load_system_token"); |
| // Mask signals handled by the daemon thread. This makes sure we |
| // won't handle shutdown signals on one of the other threads spawned |
| // below. |
| MaskSignals(); |
| LOG(INFO) << "Starting D-Bus dispatcher."; |
| chaps::Daemon(srk_auth_data, auto_load_system_token).Run(); |
| return 0; |
| } |