blob: ac8e1e1bd40c3e17fa91c9bf6020938eb9425243 [file] [log] [blame]
// 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 <grp.h>
#include <pwd.h>
#include <signal.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.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 <libminijail.h>
#include <scoped_minijail.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.";
}
void SetProcessUserAndGroup(const char* user_name,
const char* group_name) {
// Make the umask more restrictive: u + rwx, g + rx.
umask(0027);
ScopedMinijail j(minijail_new());
minijail_change_user(j.get(), user_name);
minijail_change_group(j.get(), group_name);
minijail_inherit_usergroups(j.get());
minijail_no_new_privs(j.get());
minijail_enter(j.get());
}
} // 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'.
SetProcessUserAndGroup(chaps::kChapsdProcessUser, chaps::kChapsdProcessGroup);
// 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;
}