blob: 224717aae5aefd62653faa02c5dc27abf64b8f6f [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 <signal.h>
#include <string>
#include <base/command_line.h>
#include <base/logging.h>
#include <base/memory/scoped_ptr.h>
#include <base/string_number_conversions.h>
#include <base/synchronization/lock.h>
#include <base/synchronization/waitable_event.h>
#include <base/threading/platform_thread.h>
#include <chromeos/syslog_logging.h>
#include <dbus-c++/dbus.h>
#include "chaps/chaps_adaptor.h"
#include "chaps/chaps_factory_impl.h"
#include "chaps/chaps_service.h"
#include "chaps/chaps_service_redirect.h"
#include "chaps/chaps_utility.h"
#include "chaps/platform_globals.h"
#include "chaps/slot_manager_impl.h"
#include "chaps/tpm_utility_impl.h"
using base::AutoLock;
using base::Lock;
using base::PlatformThread;
using base::PlatformThreadHandle;
using base::WaitableEvent;
using std::string;
namespace chaps {
class AsyncInitThread : public PlatformThread::Delegate {
public:
AsyncInitThread(Lock* lock,
TPMUtilityImpl* tpm,
SlotManagerImpl* slot_manager,
ChapsServiceImpl* service)
: started_event_(true, false),
lock_(lock),
tpm_(tpm),
slot_manager_(slot_manager),
service_(service) {}
void ThreadMain() {
// 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 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.";
if (!service_->Init())
LOG(FATAL) << "Service initialization failed.";
}
void WaitUntilStarted() {
started_event_.Wait();
}
private:
WaitableEvent started_event_;
Lock* lock_;
TPMUtilityImpl* tpm_;
SlotManagerImpl* slot_manager_;
ChapsServiceImpl* service_;
};
scoped_ptr<DBus::BusDispatcher> g_dispatcher;
void RunDispatcher(Lock* lock,
chaps::ChapsInterface* service,
chaps::TokenManagerInterface* token_manager) {
CHECK(service) << "Failed to initialize service.";
try {
ChapsAdaptor adaptor(lock, service, token_manager);
g_dispatcher->enter();
} catch (DBus::Error err) {
LOG(FATAL) << "DBus::Error - " << err.what();
}
}
} // namespace chaps
int main(int argc, char** argv) {
CommandLine::Init(argc, argv);
CommandLine* cl = CommandLine::ForCurrentProcess();
chromeos::InitLog(chromeos::kLogToSyslog | chromeos::kLogToStderr);
chaps::ScopedOpenSSL openssl;
chaps::g_dispatcher.reset(new DBus::BusDispatcher());
CHECK(chaps::g_dispatcher.get());
DBus::default_dispatcher = chaps::g_dispatcher.get();
Lock lock;
if (!cl->HasSwitch("lib")) {
// We're using chaps (i.e. not passing through to another PKCS #11 library).
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.";
}
}
chaps::TPMUtilityImpl tpm(srk_auth_data);
chaps::ChapsFactoryImpl factory;
chaps::SlotManagerImpl slot_manager(
&factory, &tpm, cl->HasSwitch("auto_load_system_token"));
chaps::ChapsServiceImpl service(&slot_manager);
chaps::AsyncInitThread init_thread(&lock, &tpm, &slot_manager, &service);
PlatformThreadHandle init_thread_handle;
if (!PlatformThread::Create(0, &init_thread, &init_thread_handle))
LOG(FATAL) << "Failed to create initialization thread.";
// We don't want to start the dispatcher until the initialization thread has
// had a chance to acquire the lock.
init_thread.WaitUntilStarted();
LOG(INFO) << "Starting D-Bus dispatcher.";
RunDispatcher(&lock, &service, &slot_manager);
PlatformThread::Join(init_thread_handle);
} else {
// We're passing through to another PKCS #11 library.
string lib = cl->GetSwitchValueASCII("lib");
LOG(INFO) << "Starting PKCS #11 services with " << lib << ".";
chaps::ChapsServiceRedirect service(lib.c_str());
if (!service.Init())
LOG(FATAL) << "Failed to initialize PKCS #11 library: " << lib;
RunDispatcher(&lock, &service, NULL);
}
return 0;
}