blob: dbd8601016209cd1ecac9841ef44154d1e2a10fb [file] [log] [blame]
// Copyright 2022 The ChromiumOS Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <unistd.h>
#include <cstdlib>
#include <iomanip>
#include <memory>
#include <sysexits.h>
#include <utility>
#include "absl/status/status.h"
#include "attestation/proto_bindings/interface.pb.h"
#include "attestation-client/attestation/dbus-proxies.h"
#include "base/functional/callback_forward.h"
#include "base/functional/callback_helpers.h"
#include "base/logging.h"
#include "base/memory/scoped_refptr.h"
#include "base/task/thread_pool/thread_pool_instance.h"
#include "base/time/time.h"
#include "brillo/daemons/dbus_daemon.h"
#include "featured/c_feature_library.h"
#include "featured/feature_library.h"
#include "missive/client/missive_client.h"
#include "policy/device_policy.h"
#include "policy/libpolicy.h"
#include "secagentd/daemon.h"
#include "secagentd/message_sender.h"
#include "secagentd/metrics_sender.h"
#include "secagentd/plugins.h"
#include "secagentd/process_cache.h"
namespace secagentd {
static const struct VariationsFeature kCrOSLateBootSecagentdXDRReporting = {
.name = "CrOSLateBootSecagentdXDRReporting",
.default_state = FEATURE_ENABLED_BY_DEFAULT};
Daemon::Daemon(struct Inject injected) : weak_ptr_factory_(this) {
plugin_factory_ = std::move(injected.plugin_factory_);
message_sender_ = std::move(injected.message_sender_);
process_cache_ = std::move(injected.process_cache_);
policy_provider_ = std::move(injected.policy_provider_);
}
Daemon::Daemon(bool bypass_policy_for_testing,
bool bypass_enq_ok_wait_for_testing,
uint32_t set_heartbeat_period_s_for_testing)
: bypass_policy_for_testing_(bypass_policy_for_testing),
bypass_enq_ok_wait_for_testing_(bypass_enq_ok_wait_for_testing),
set_heartbeat_period_s_for_testing_(set_heartbeat_period_s_for_testing),
weak_ptr_factory_(this) {}
int Daemon::OnInit() {
int rv = brillo::DBusDaemon::OnInit();
if (rv != EX_OK) {
return rv;
}
// Finch feature interface which allows us to run experiments.
if (features_ == nullptr) {
features_ = feature::PlatformFeatures::New(bus_);
}
if (plugin_factory_ == nullptr) {
plugin_factory_ = std::make_unique<PluginFactory>();
}
if (message_sender_ == nullptr) {
// Set up ERP.
base::ThreadPoolInstance::CreateAndStartWithDefaultParams(
"missive_thread_pool");
reporting::MissiveClient::Initialize(bus_.get());
message_sender_ = base::MakeRefCounted<MessageSender>();
absl::Status result = message_sender_->Initialize();
if (result != absl::OkStatus()) {
LOG(ERROR) << result.message();
return EX_SOFTWARE;
}
}
if (process_cache_ == nullptr) {
process_cache_ = base::MakeRefCounted<ProcessCache>();
process_cache_->InitializeFilter();
}
if (policy_provider_ == nullptr) {
policy_provider_ = std::make_unique<policy::PolicyProvider>();
}
return EX_OK;
}
void Daemon::CheckXDRFlags(bool isXDREnabled) {
if (!isXDREnabled) {
LOG(INFO) << "XDR reporting feature on this device is disabled."
" Not sending events.";
} else if (!xdr_reporting_policy_) {
LOG(INFO) << "XDR reporting policy is disabled. Not sending events.";
}
// If either policy is false do not report.
if (!isXDREnabled || !xdr_reporting_policy_) {
if (reporting_events_) {
LOG(INFO) << "Stopping event reporting and quitting";
// Will exit and restart secagentd.
Quit();
}
return;
} else {
if (!reporting_events_) {
LOG(INFO) << "Starting event reporting";
// This is emitted at most once per daemon lifetime.
MetricsSender::GetInstance().SendEnumMetricToUMA(
metrics::kPolicy, metrics::Policy::kEnabled);
reporting_events_ = true;
StartXDRReporting();
} else {
return;
}
}
}
void Daemon::StartXDRReporting() {
using CbType = base::OnceCallback<void()>;
CbType cb_for_agent =
base::BindOnce(&Daemon::RunPlugins, weak_ptr_factory_.GetWeakPtr());
CbType cb_for_now = base::DoNothing();
if (bypass_enq_ok_wait_for_testing_) {
std::swap(cb_for_agent, cb_for_now);
}
agent_plugin_ = plugin_factory_->Create(
Types::Plugin::kAgent, message_sender_,
std::make_unique<org::chromium::AttestationProxy>(bus_),
std::make_unique<org::chromium::TpmManagerProxy>(bus_),
std::move(cb_for_agent), set_heartbeat_period_s_for_testing_);
if (!agent_plugin_) {
QuitWithExitCode(EX_SOFTWARE);
}
absl::Status result = agent_plugin_->Activate();
if (!result.ok()) {
LOG(ERROR) << result.message();
QuitWithExitCode(EX_SOFTWARE);
}
base::SequencedTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE, std::move(cb_for_now));
}
void Daemon::RunPlugins() {
if (CreatePlugin(PluginFactory::PluginType::kProcess) != EX_OK) {
QuitWithExitCode(EX_SOFTWARE);
}
for (auto& plugin : plugins_) {
absl::Status result = plugin->Activate();
if (!result.ok()) {
LOG(ERROR) << result.message();
QuitWithExitCode(EX_SOFTWARE);
}
}
}
int Daemon::CreatePlugin(PluginFactory::PluginType type) {
std::unique_ptr<PluginInterface> plugin;
switch (type) {
case PluginFactory::PluginType::kProcess: {
plugin = plugin_factory_->Create(Types::Plugin::kProcess, message_sender_,
process_cache_);
break;
}
default:
CHECK(false) << "Unsupported plugin type";
}
if (!plugin) {
return EX_SOFTWARE;
}
plugins_.push_back(std::move(plugin));
return EX_OK;
}
int Daemon::OnEventLoopStarted() {
check_flags_timer_.Start(
FROM_HERE, base::Minutes(10),
base::BindRepeating(&Daemon::PollFlags, weak_ptr_factory_.GetWeakPtr()));
// Delay before first timer invocation so add check.
// We emit this metric here and not inside the polled method so that we do it
// exactly once per daemon lifetime.
MetricsSender::GetInstance().SendEnumMetricToUMA(metrics::kPolicy,
metrics::Policy::kChecked);
PollFlags();
return EX_OK;
}
void Daemon::PollFlags() {
GetXDRReportingIsEnabled();
features_->IsEnabled(
kCrOSLateBootSecagentdXDRReporting,
base::BindOnce(&Daemon::CheckXDRFlags, weak_ptr_factory_.GetWeakPtr()));
}
void Daemon::GetXDRReportingIsEnabled() {
// Bypasses the policy check for testing.
if (bypass_policy_for_testing_) {
xdr_reporting_policy_ = true;
return;
}
policy_provider_->Reload();
if (policy_provider_->device_policy_is_loaded()) {
xdr_reporting_policy_ =
policy_provider_->GetDevicePolicy().GetDeviceReportXDREvents().value_or(
false);
} else {
// Default value is do not report.
xdr_reporting_policy_ = false;
}
}
void Daemon::OnShutdown(int* exit_code) {
// Disconnect missive.
reporting::MissiveClient::Shutdown();
brillo::DBusDaemon::OnShutdown(exit_code);
}
} // namespace secagentd