| // Copyright 2014 The ChromiumOS Authors | |
| // Use of this source code is governed by a BSD-style license that can be | |
| // found in the LICENSE file. | |
| #include <signal.h> | |
| #include <sysexits.h> | |
| #include <base/at_exit.h> | |
| #include <base/check.h> | |
| #include <base/check_op.h> | |
| #include <base/command_line.h> | |
| #include <base/files/file_util.h> | |
| #include <base/functional/bind.h> | |
| #include <base/logging.h> | |
| #include <base/threading/thread.h> | |
| #include <brillo/syslog_logging.h> | |
| #include <brillo/userdb_utils.h> | |
| #include <libhwsec-foundation/profiling/profiling.h> | |
| #include <libminijail.h> | |
| #include <scoped_minijail.h> | |
| #include "trunks/background_command_transceiver.h" | |
| #include "trunks/power_manager.h" | |
| #include "trunks/resilience/write_error_tracker_impl.h" | |
| #include "trunks/resource_manager.h" | |
| #include "trunks/tpm_handle.h" | |
| #include "trunks/trunks_dbus_service.h" | |
| #include "trunks/trunks_factory_impl.h" | |
| #include "trunks/trunks_ftdi_spi.h" | |
| #include "trunks/trunks_metrics.h" | |
| namespace { | |
| namespace switches { | |
| constexpr char kNoCloseOnDaemonize[] = "noclose"; | |
| constexpr char kNoDaemonize[] = "nodaemonize"; | |
| constexpr char kLogToStderr[] = "log_to_stderr"; | |
| } // namespace switches | |
| const uid_t kRootUID = 0; | |
| const char kTrunksUser[] = "trunks"; | |
| const char kTrunksGroup[] = "trunks"; | |
| const char kTrunksSeccompPath[] = "/usr/share/policy/trunksd-seccomp.policy"; | |
| const char kBackgroundThreadName[] = "trunksd_background_thread"; | |
| const char kWriteErrorTrackingPath[] = "/run/trunks/last-write-error"; | |
| void InitMinijailSandbox() { | |
| uid_t trunks_uid; | |
| gid_t trunks_gid; | |
| CHECK(brillo::userdb::GetUserInfo(kTrunksUser, &trunks_uid, &trunks_gid)) | |
| << "Error getting trunks uid and gid."; | |
| CHECK_EQ(getuid(), kRootUID) << "trunksd not initialized as root."; | |
| ScopedMinijail j(minijail_new()); | |
| minijail_set_seccomp_filter_tsync(j.get()); | |
| minijail_no_new_privs(j.get()); | |
| minijail_bind(j.get(), "/run/trunks", "/run/trunks", 1); | |
| minijail_use_seccomp_filter(j.get()); | |
| minijail_parse_seccomp_filters(j.get(), kTrunksSeccompPath); | |
| minijail_change_user(j.get(), kTrunksUser); | |
| minijail_change_group(j.get(), kTrunksGroup); | |
| minijail_enter(j.get()); | |
| CHECK_EQ(getuid(), trunks_uid) | |
| << "trunksd was not able to drop user privilege."; | |
| CHECK_EQ(getgid(), trunks_gid) | |
| << "trunksd was not able to drop group privilege."; | |
| } | |
| // Add the signals, for which the handlers are added by brillo::Daemon | |
| // to the blocked mask. | |
| 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)); | |
| VLOG(2) << "Signal mask set."; | |
| } | |
| } // namespace | |
| int main(int argc, char** argv) { | |
| base::CommandLine::Init(argc, argv); | |
| base::CommandLine* cl = base::CommandLine::ForCurrentProcess(); | |
| int flags = brillo::kLogToSyslog; | |
| if (cl->HasSwitch(switches::kLogToStderr)) { | |
| flags |= brillo::kLogToStderr; | |
| } | |
| brillo::InitLog(flags); | |
| bool noclose = cl->HasSwitch(switches::kNoCloseOnDaemonize); | |
| bool daemonize = !cl->HasSwitch(switches::kNoDaemonize); | |
| trunks::TrunksMetrics metrics; | |
| trunks::WriteErrorTrackerImpl write_error_tracker(kWriteErrorTrackingPath); | |
| // Chain together command transceivers: | |
| // [IPC] --> BackgroundCommandTransceiver | |
| // --> ResourceManager | |
| // --> TpmHandle | |
| // --> [TPM] | |
| trunks::CommandTransceiver* low_level_transceiver = nullptr; | |
| if (cl->HasSwitch("ftdi")) { | |
| LOG(INFO) << "Sending commands to FTDI SPI."; | |
| low_level_transceiver = new trunks::TrunksFtdiSpi(); | |
| } else { | |
| low_level_transceiver = new trunks::TpmHandle(metrics, write_error_tracker); | |
| } | |
| CHECK(low_level_transceiver->Init()) | |
| << "Error initializing TPM communication."; | |
| // Upstart would know trunksd is ready after trunksd daemonized. | |
| if (daemonize) { | |
| PLOG_IF(FATAL, daemon(0, noclose) == -1) << "Failed to daemonize"; | |
| } | |
| // This needs to be *after* opening the TPM handle and *before* starting the | |
| // background thread. | |
| InitMinijailSandbox(); | |
| // Make sure signals handled by the server are blocked in all threads, | |
| // otherwise the process still dies. | |
| // This needs to be *before* starting the background thread. | |
| MaskSignals(); | |
| base::Thread background_thread(kBackgroundThreadName); | |
| CHECK(background_thread.Start()) << "Failed to start background thread."; | |
| trunks::TrunksFactoryImpl factory(low_level_transceiver); | |
| CHECK(factory.Initialize()) << "Failed to initialize trunks factory."; | |
| trunks::ResourceManager resource_manager(factory, low_level_transceiver); | |
| background_thread.task_runner()->PostNonNestableTask( | |
| FROM_HERE, base::BindOnce(&trunks::ResourceManager::Initialize, | |
| base::Unretained(&resource_manager))); | |
| trunks::BackgroundCommandTransceiver background_transceiver( | |
| &resource_manager, background_thread.task_runner()); | |
| // Create a service instance so objects like AtExitManager exist. | |
| trunks::TrunksDBusService service(background_transceiver, metrics, | |
| write_error_tracker); | |
| trunks::PowerManager power_manager(&resource_manager, | |
| background_thread.task_runner()); | |
| service.set_power_manager(&power_manager); | |
| LOG(INFO) << "Trunks service started."; | |
| // Start profiling. | |
| hwsec_foundation::SetUpProfiling(); | |
| int exit_code = service.Run(); | |
| if (!write_error_tracker.Write()) { | |
| LOG(WARNING) << "Failed to write the write errorno."; | |
| } | |
| // Need to stop the background thread before destroying ResourceManager | |
| // and PowerManager. Otherwise, a task posted by BackgroundCommandTransceiver | |
| // may attempt to access those destroyed objects. | |
| background_thread.Stop(); | |
| return exit_code; | |
| } |