// Copyright 2021 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chromeos/dbus/dlp/dlp_client.h"
#include <optional>
#include <utility>
#include "base/functional/bind.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/weak_ptr.h"
#include "base/observer_list.h"
#include "base/strings/strcat.h"
#include "base/task/single_thread_task_runner.h"
#include "chromeos/dbus/dlp/dlp_service.pb.h"
#include "chromeos/dbus/dlp/fake_dlp_client.h"
#include "dbus/bus.h"
#include "dbus/message.h"
#include "dbus/object_proxy.h"
#include "third_party/cros_system_api/dbus/dlp/dbus-constants.h"
namespace chromeos {
namespace {
DlpClient* g_instance = nullptr;
const char kDbusCallFailure[] = "Failed to call dlp.";
const char kProtoMessageParsingFailure[] =
"Failed to parse response message from dlp.";
// Tries to parse a proto message from |response| into |proto| and returns null
// if successful. If |response| is nullptr or the message cannot be parsed it
// will return an appropriate error message.
const char* DeserializeProto(dbus::Response* response,
google::protobuf::MessageLite* proto) {
if (!response) {
return kDbusCallFailure;
}
dbus::MessageReader reader(response);
if (!reader.PopArrayOfBytesAsProto(proto)) {
return kProtoMessageParsingFailure;
}
return nullptr;
}
// "Real" implementation of DlpClient talking to the Dlp daemon
// on the Chrome OS side.
class DlpClientImpl : public DlpClient {
public:
DlpClientImpl() = default;
DlpClientImpl(const DlpClientImpl&) = delete;
DlpClientImpl& operator=(const DlpClientImpl&) = delete;
~DlpClientImpl() override = default;
void Init(dbus::Bus* bus) {
proxy_ = bus->GetObjectProxy(dlp::kDlpServiceName,
dbus::ObjectPath(dlp::kDlpServicePath));
proxy_->SetNameOwnerChangedCallback(base::BindRepeating(
&DlpClientImpl::NameOwnerChangedReceived, weak_factory_.GetWeakPtr()));
}
void SetDlpFilesPolicy(const dlp::SetDlpFilesPolicyRequest request,
SetDlpFilesPolicyCallback callback) override {
dbus::MethodCall method_call(dlp::kDlpInterface,
dlp::kSetDlpFilesPolicyMethod);
dbus::MessageWriter writer(&method_call);
if (!writer.AppendProtoAsArrayOfBytes(request)) {
dlp::SetDlpFilesPolicyResponse response;
response.set_error_message(base::StrCat(
{"Failure to call d-bus method: ", dlp::kSetDlpFilesPolicyMethod}));
base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE, base::BindOnce(std::move(callback), response));
return;
}
proxy_->CallMethod(
&method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
base::BindOnce(&DlpClientImpl::HandleSetDlpFilesPolicyResponse,
weak_factory_.GetWeakPtr(), std::move(callback)));
}
void AddFiles(const dlp::AddFilesRequest request,
AddFilesCallback callback) override {
dbus::MethodCall method_call(dlp::kDlpInterface, dlp::kAddFilesMethod);
dbus::MessageWriter writer(&method_call);
if (!writer.AppendProtoAsArrayOfBytes(request)) {
dlp::AddFilesResponse response;
response.set_error_message(base::StrCat(
{"Failure to call d-bus method: ", dlp::kAddFilesMethod}));
base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE, base::BindOnce(std::move(callback), response));
return;
}
proxy_->CallMethod(&method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
base::BindOnce(&DlpClientImpl::HandleAddFilesResponse,
weak_factory_.GetWeakPtr(),
std::move(request), std::move(callback)));
}
void GetFilesSources(const dlp::GetFilesSourcesRequest request,
GetFilesSourcesCallback callback) override {
dbus::MethodCall method_call(dlp::kDlpInterface,
dlp::kGetFilesSourcesMethod);
dbus::MessageWriter writer(&method_call);
if (!writer.AppendProtoAsArrayOfBytes(request)) {
dlp::GetFilesSourcesResponse response;
response.set_error_message(base::StrCat(
{"Failure to call d-bus method: ", dlp::kGetFilesSourcesMethod}));
base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE, base::BindOnce(std::move(callback), response));
return;
}
proxy_->CallMethod(
&method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
base::BindOnce(&DlpClientImpl::HandleGetFilesSourcesResponse,
weak_factory_.GetWeakPtr(), std::move(callback)));
}
void CheckFilesTransfer(const dlp::CheckFilesTransferRequest request,
CheckFilesTransferCallback callback) override {
dbus::MethodCall method_call(dlp::kDlpInterface,
dlp::kCheckFilesTransferMethod);
dbus::MessageWriter writer(&method_call);
if (!writer.AppendProtoAsArrayOfBytes(request)) {
dlp::CheckFilesTransferResponse response;
response.set_error_message(base::StrCat(
{"Failure to call d-bus method: ", dlp::kCheckFilesTransferMethod}));
base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE, base::BindOnce(std::move(callback), response));
return;
}
proxy_->CallMethod(
&method_call, base::Minutes(6).InMilliseconds(),
base::BindOnce(&DlpClientImpl::HandleCheckFilesTransferResponse,
weak_factory_.GetWeakPtr(), std::move(callback)));
}
void RequestFileAccess(const dlp::RequestFileAccessRequest request,
RequestFileAccessCallback callback) override {
dbus::MethodCall method_call(dlp::kDlpInterface,
dlp::kRequestFileAccessMethod);
dbus::MessageWriter writer(&method_call);
if (!writer.AppendProtoAsArrayOfBytes(request)) {
dlp::RequestFileAccessResponse response;
response.set_error_message(base::StrCat(
{"Failure to call d-bus method: ", dlp::kRequestFileAccessMethod}));
base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE,
base::BindOnce(std::move(callback), response, base::ScopedFD()));
return;
}
proxy_->CallMethod(
&method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
base::BindOnce(&DlpClientImpl::HandleRequestFileAccessResponse,
weak_factory_.GetWeakPtr(), std::move(callback)));
}
void GetDatabaseEntries(GetDatabaseEntriesCallback callback) override {
dbus::MethodCall method_call(dlp::kDlpInterface,
dlp::kGetDatabaseEntriesMethod);
dbus::MessageWriter writer(&method_call);
proxy_->CallMethod(
&method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
base::BindOnce(&DlpClientImpl::HandleGetDatabaseEntriesResponse,
weak_factory_.GetWeakPtr(), std::move(callback)));
}
bool IsAlive() const override { return is_alive_; }
void AddObserver(Observer* observer) override {
observers_.AddObserver(observer);
}
void RemoveObserver(Observer* observer) override {
observers_.RemoveObserver(observer);
}
bool HasObserver(const Observer* observer) const override {
return observers_.HasObserver(observer);
}
private:
TestInterface* GetTestInterface() override { return nullptr; }
void HandleSetDlpFilesPolicyResponse(SetDlpFilesPolicyCallback callback,
dbus::Response* response) {
dlp::SetDlpFilesPolicyResponse response_proto;
const char* error_message = DeserializeProto(response, &response_proto);
if (error_message) {
response_proto.set_error_message(error_message);
}
if (!response_proto.has_error_message()) {
is_alive_ = true;
}
std::move(callback).Run(response_proto);
}
void HandleAddFilesResponse(const dlp::AddFilesRequest request,
AddFilesCallback callback,
dbus::Response* response) {
dlp::AddFilesResponse response_proto;
const char* error_message = DeserializeProto(response, &response_proto);
if (error_message) {
response_proto.set_error_message(error_message);
} else {
std::vector<base::FilePath> added_files;
for (const auto& add_file_request : request.add_file_requests()) {
added_files.emplace_back(add_file_request.file_path());
}
for (auto& observer : observers_) {
observer.OnFilesAddedToDlpDaemon(added_files);
}
}
std::move(callback).Run(response_proto);
}
void HandleGetFilesSourcesResponse(GetFilesSourcesCallback callback,
dbus::Response* response) {
dlp::GetFilesSourcesResponse response_proto;
const char* error_message = DeserializeProto(response, &response_proto);
if (error_message) {
response_proto.set_error_message(error_message);
}
std::move(callback).Run(response_proto);
}
void HandleCheckFilesTransferResponse(CheckFilesTransferCallback callback,
dbus::Response* response) {
dlp::CheckFilesTransferResponse response_proto;
const char* error_message = DeserializeProto(response, &response_proto);
if (error_message) {
response_proto.set_error_message(error_message);
}
std::move(callback).Run(response_proto);
}
void HandleRequestFileAccessResponse(RequestFileAccessCallback callback,
dbus::Response* response) {
dlp::RequestFileAccessResponse response_proto;
base::ScopedFD fd;
if (!response) {
response_proto.set_error_message(kDbusCallFailure);
std::move(callback).Run(response_proto, std::move(fd));
return;
}
dbus::MessageReader reader(response);
if (!reader.PopArrayOfBytesAsProto(&response_proto)) {
response_proto.set_error_message(kProtoMessageParsingFailure);
std::move(callback).Run(response_proto, std::move(fd));
return;
}
if (!reader.PopFileDescriptor(&fd)) {
response_proto.set_error_message(kProtoMessageParsingFailure);
std::move(callback).Run(response_proto, std::move(fd));
return;
}
std::move(callback).Run(response_proto, std::move(fd));
}
void HandleGetDatabaseEntriesResponse(GetDatabaseEntriesCallback callback,
dbus::Response* response) {
dlp::GetDatabaseEntriesResponse response_proto;
const char* error_message = DeserializeProto(response, &response_proto);
if (error_message) {
response_proto.set_error_message(error_message);
}
std::move(callback).Run(response_proto);
}
void NameOwnerChangedReceived(const std::string& old_owner,
const std::string& new_owner) {
is_alive_ = false;
// Do not notify if the service was shut down, only if a new one is started.
if (new_owner.empty()) {
return;
}
for (auto& observer : observers_) {
observer.DlpDaemonRestarted();
}
}
// D-Bus proxy for the Dlp daemon, not owned.
raw_ptr<dbus::ObjectProxy> proxy_ = nullptr;
// Indicates whether the daemon was started and DLP Files rules are enforced.
bool is_alive_ = false;
base::ObserverList<Observer> observers_;
base::WeakPtrFactory<DlpClientImpl> weak_factory_{this};
};
} // namespace
DlpClient::DlpClient() {
CHECK(!g_instance);
g_instance = this;
}
DlpClient::~DlpClient() {
CHECK_EQ(this, g_instance);
g_instance = nullptr;
}
// static
void DlpClient::Initialize(dbus::Bus* bus) {
CHECK(bus);
(new DlpClientImpl())->Init(bus);
}
// static
void DlpClient::InitializeFake() {
new FakeDlpClient();
}
// static
void DlpClient::Shutdown() {
CHECK(g_instance);
delete g_instance;
}
// static
DlpClient* DlpClient::Get() {
return g_instance;
}
} // namespace chromeos