blob: e7933ef5363a6b543bd08b9944982e20d1a6f1ec [file] [log] [blame]
// Copyright 2021 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.
#include "dlp/dlp_adaptor.h"
#include <cstdint>
#include <string>
#include <sys/stat.h>
#include <sys/types.h>
#include <utility>
#include <vector>
#include <base/check.h>
#include <base/files/file_path.h>
#include <base/location.h>
#include <base/strings/string_number_conversions.h>
#include <brillo/dbus/dbus_object.h>
#include <brillo/errors/error.h>
#include <google/protobuf/message_lite.h>
#include <session_manager/dbus-proxies.h>
#include "dlp/database.pb.h"
#include "dlp/proto_bindings/dlp_service.pb.h"
namespace dlp {
namespace {
// Serializes |proto| to a vector of bytes. CHECKs for success (should
// never fail if there are no required proto fields).
std::vector<uint8_t> SerializeProto(
const google::protobuf::MessageLite& proto) {
std::vector<uint8_t> proto_blob(proto.ByteSizeLong());
CHECK(proto.SerializeToArray(proto_blob.data(), proto_blob.size()));
return proto_blob;
}
// Parses a proto from an array of bytes |proto_blob|. Returns
// error message or empty string if no error.
std::string ParseProto(const base::Location& from_here,
google::protobuf::MessageLite* proto,
const std::vector<uint8_t>& proto_blob) {
if (!proto->ParseFromArray(proto_blob.data(), proto_blob.size())) {
const std::string error_message = "Failed to parse proto message.";
LOG(ERROR) << from_here.ToString() << " " << error_message;
return error_message;
}
return "";
}
// Calls Session Manager to get the user hash for the primary session. Returns
// an empty string and logs on error.
std::string GetSanitizedUsername(brillo::dbus_utils::DBusObject* dbus_object) {
std::string username;
std::string sanitized_username;
brillo::ErrorPtr error;
org::chromium::SessionManagerInterfaceProxy proxy(dbus_object->GetBus());
if (!proxy.RetrievePrimarySession(&username, &sanitized_username, &error)) {
const char* error_msg =
error ? error->GetMessage().c_str() : "Unknown error.";
LOG(ERROR) << "Call to RetrievePrimarySession failed. " << error_msg;
return std::string();
}
return sanitized_username;
}
ino_t GetInodeValue(const std::string& path) {
struct stat file_stats;
if (stat(path.c_str(), &file_stats) != 0) {
PLOG(ERROR) << "Could not access " << path;
return 0;
}
return file_stats.st_ino;
}
FileEntry ConvertToFileEntryProto(AddFileRequest request) {
FileEntry result;
if (request.has_source_url())
result.set_source_url(request.source_url());
if (request.has_referrer_url())
result.set_referrer_url(request.referrer_url());
return result;
}
} // namespace
DlpAdaptor::DlpAdaptor(
std::unique_ptr<brillo::dbus_utils::DBusObject> dbus_object)
: org::chromium::DlpAdaptor(this), dbus_object_(std::move(dbus_object)) {
InitDatabase();
}
DlpAdaptor::~DlpAdaptor() = default;
void DlpAdaptor::RegisterAsync(
const brillo::dbus_utils::AsyncEventSequencer::CompletionAction&
completion_callback) {
RegisterWithDBusObject(dbus_object_.get());
dbus_object_->RegisterAsync(completion_callback);
}
std::vector<uint8_t> DlpAdaptor::SetDlpFilesPolicy(
const std::vector<uint8_t>& request_blob) {
LOG(INFO) << "Received DLP files policy.";
SetDlpFilesPolicyRequest request;
std::string error_message = ParseProto(FROM_HERE, &request, request_blob);
SetDlpFilesPolicyResponse response;
if (!error_message.empty()) {
response.set_error_message(error_message);
return SerializeProto(response);
}
policy_rules_ =
std::vector<DlpFilesRule>(request.rules().begin(), request.rules().end());
return SerializeProto(response);
}
std::vector<uint8_t> DlpAdaptor::AddFile(
const std::vector<uint8_t>& request_blob) {
AddFileRequest request;
AddFileResponse response;
const std::string parse_error = ParseProto(FROM_HERE, &request, request_blob);
if (!parse_error.empty()) {
LOG(ERROR) << "Failed to parse AddFile request: " << parse_error;
response.set_error_message(parse_error);
return SerializeProto(response);
}
LOG(INFO) << "Adding file to the database: " << request.file_path();
leveldb::WriteOptions options;
options.sync = true;
const ino_t inode = GetInodeValue(request.file_path());
if (!inode) {
LOG(ERROR) << "Failed to get inode";
response.set_error_message("Failed to get inode");
return SerializeProto(response);
}
const std::string inode_s = base::NumberToString(inode);
FileEntry file_entry = ConvertToFileEntryProto(request);
std::string serialized_proto;
if (!file_entry.SerializeToString(&serialized_proto)) {
LOG(ERROR) << "Failed to serialize database entry to string";
response.set_error_message("Failed to serialize database entry to string");
return SerializeProto(response);
}
const leveldb::Status status = db_->Put(options, inode_s, serialized_proto);
if (!status.ok()) {
LOG(ERROR) << "Failed to write value to database: " << status.ToString();
response.set_error_message(status.ToString());
return SerializeProto(response);
}
return SerializeProto(response);
}
void DlpAdaptor::InitDatabase() {
const std::string sanitized_username =
GetSanitizedUsername(dbus_object_.get());
if (sanitized_username.empty()) {
LOG(ERROR) << "No active user, can't open the database";
return;
}
const base::FilePath database_path = base::FilePath("/run/daemon-store/dlp/")
.Append(sanitized_username)
.Append("database");
LOG(INFO) << "Opening database in: " << database_path.value();
leveldb::Options options;
options.create_if_missing = true;
options.paranoid_checks = true;
leveldb::DB* db = nullptr;
leveldb::Status status =
leveldb::DB::Open(options, database_path.value(), &db);
if (!status.ok()) {
LOG(ERROR) << "Failed to open database: " << status.ToString();
status = leveldb::RepairDB(database_path.value(), leveldb::Options());
if (status.ok())
status = leveldb::DB::Open(options, database_path.value(), &db);
}
if (!status.ok()) {
LOG(ERROR) << "Failed to repair database: " << status.ToString();
return;
}
db_.reset(db);
}
} // namespace dlp