| // 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 "chrome/browser/ash/dbus/dlp_files_policy_service_provider.h" |
| |
| #include <memory> |
| #include <vector> |
| |
| #include "base/files/file_path.h" |
| #include "base/functional/bind.h" |
| #include "base/logging.h" |
| #include "chrome/browser/ash/policy/dlp/dlp_files_controller_ash.h" |
| #include "chrome/browser/chromeos/policy/dlp/dlp_file_destination.h" |
| #include "chrome/browser/chromeos/policy/dlp/dlp_files_utils.h" |
| #include "chrome/browser/profiles/profile.h" |
| #include "chromeos/dbus/dlp/dlp_service.pb.h" |
| #include "dbus/message.h" |
| #include "third_party/cros_system_api/dbus/dlp/dbus-constants.h" |
| #include "url/gurl.h" |
| |
| namespace ash { |
| |
| namespace { |
| |
| // Maps dlp::FileAction proto enum to DlpFilesController::FileAction enum. |
| policy::dlp::FileAction MapProtoToFileAction(dlp::FileAction file_action) { |
| switch (file_action) { |
| case dlp::FileAction::UPLOAD: |
| return policy::dlp::FileAction::kUpload; |
| case dlp::FileAction::COPY: |
| return policy::dlp::FileAction::kCopy; |
| case dlp::FileAction::MOVE: |
| return policy::dlp::FileAction::kMove; |
| case dlp::FileAction::OPEN: |
| return policy::dlp::FileAction::kOpen; |
| case dlp::FileAction::SHARE: |
| return policy::dlp::FileAction::kShare; |
| case dlp::FileAction::TRANSFER: |
| return policy::dlp::FileAction::kTransfer; |
| } |
| } |
| |
| // Maps |component| to data_controls::Component. |
| data_controls::Component MapProtoToPolicyComponent( |
| ::dlp::DlpComponent component) { |
| switch (component) { |
| case ::dlp::DlpComponent::ARC: |
| return data_controls::Component::kArc; |
| case ::dlp::DlpComponent::CROSTINI: |
| return data_controls::Component::kCrostini; |
| case ::dlp::DlpComponent::PLUGIN_VM: |
| return data_controls::Component::kPluginVm; |
| case ::dlp::DlpComponent::USB: |
| return data_controls::Component::kUsb; |
| case ::dlp::DlpComponent::GOOGLE_DRIVE: |
| return data_controls::Component::kDrive; |
| case ::dlp::DlpComponent::MICROSOFT_ONEDRIVE: |
| return data_controls::Component::kOneDrive; |
| case ::dlp::DlpComponent::UNKNOWN_COMPONENT: |
| case ::dlp::DlpComponent::SYSTEM: |
| return data_controls::Component::kUnknownComponent; |
| } |
| } |
| |
| // Called when restricted files sources are obtained. |
| void RespondWithRestrictedFilesTransfer( |
| dbus::MethodCall* method_call, |
| dbus::ExportedObject::ResponseSender response_sender, |
| const std::vector<std::pair<policy::DlpFilesControllerAsh::FileDaemonInfo, |
| dlp::RestrictionLevel>>& requested_files) { |
| dlp::IsFilesTransferRestrictedResponse response_proto; |
| |
| for (const auto& [file, level] : requested_files) { |
| dlp::FileRestriction* files_restriction = |
| response_proto.add_files_restrictions(); |
| files_restriction->mutable_file_metadata()->set_inode(file.inode); |
| files_restriction->mutable_file_metadata()->set_crtime(file.crtime); |
| files_restriction->mutable_file_metadata()->set_path(file.path.value()); |
| files_restriction->mutable_file_metadata()->set_source_url( |
| file.source_url.spec()); |
| files_restriction->mutable_file_metadata()->set_referrer_url( |
| file.referrer_url.spec()); |
| files_restriction->set_restriction_level(level); |
| } |
| std::unique_ptr<dbus::Response> response = |
| dbus::Response::FromMethodCall(method_call); |
| dbus::MessageWriter writer(response.get()); |
| writer.AppendProtoAsArrayOfBytes(response_proto); |
| std::move(response_sender).Run(std::move(response)); |
| } |
| |
| // Respond with a single restriction level. |
| void DirectRespondWithRestrictedFilesTransfer( |
| dbus::MethodCall* method_call, |
| dbus::ExportedObject::ResponseSender response_sender, |
| const std::vector<policy::DlpFilesControllerAsh::FileDaemonInfo>& |
| files_info, |
| ::dlp::RestrictionLevel restriction_level) { |
| std::vector<std::pair<policy::DlpFilesControllerAsh::FileDaemonInfo, |
| dlp::RestrictionLevel>> |
| response_files; |
| for (const auto& file : files_info) { |
| response_files.emplace_back(file, restriction_level); |
| } |
| RespondWithRestrictedFilesTransfer(method_call, std::move(response_sender), |
| std::move(response_files)); |
| } |
| |
| } // namespace |
| |
| DlpFilesPolicyServiceProvider::DlpFilesPolicyServiceProvider() = default; |
| DlpFilesPolicyServiceProvider::~DlpFilesPolicyServiceProvider() = default; |
| |
| void DlpFilesPolicyServiceProvider::Start( |
| scoped_refptr<dbus::ExportedObject> exported_object) { |
| exported_object->ExportMethod( |
| dlp::kDlpFilesPolicyServiceInterface, |
| dlp::kDlpFilesPolicyServiceIsDlpPolicyMatchedMethod, |
| base::BindRepeating(&DlpFilesPolicyServiceProvider::IsDlpPolicyMatched, |
| weak_ptr_factory_.GetWeakPtr()), |
| base::BindOnce(&DlpFilesPolicyServiceProvider::OnExported, |
| weak_ptr_factory_.GetWeakPtr())); |
| |
| exported_object->ExportMethod( |
| dlp::kDlpFilesPolicyServiceInterface, |
| dlp::kDlpFilesPolicyServiceIsFilesTransferRestrictedMethod, |
| base::BindRepeating( |
| &DlpFilesPolicyServiceProvider::IsFilesTransferRestricted, |
| weak_ptr_factory_.GetWeakPtr()), |
| base::BindOnce(&DlpFilesPolicyServiceProvider::OnExported, |
| weak_ptr_factory_.GetWeakPtr())); |
| } |
| |
| void DlpFilesPolicyServiceProvider::OnExported( |
| const std::string& interface_name, |
| const std::string& method_name, |
| bool success) { |
| if (!success) { |
| LOG(ERROR) << "Failed to export " << interface_name << "." << method_name; |
| } |
| } |
| |
| void DlpFilesPolicyServiceProvider::IsDlpPolicyMatched( |
| dbus::MethodCall* method_call, |
| dbus::ExportedObject::ResponseSender response_sender) { |
| dbus::MessageReader reader(method_call); |
| dlp::IsDlpPolicyMatchedRequest request; |
| if (!reader.PopArrayOfBytesAsProto(&request)) { |
| std::move(response_sender) |
| .Run(dbus::ErrorResponse::FromMethodCall( |
| method_call, DBUS_ERROR_INVALID_ARGS, |
| "Unable to parse IsDlpPolicyMatchedRequest")); |
| return; |
| } |
| |
| policy::DlpFilesControllerAsh* files_controller = |
| policy::DlpFilesControllerAsh::GetForPrimaryProfile(); |
| |
| bool restricted = |
| files_controller |
| ? files_controller->IsDlpPolicyMatched( |
| policy::DlpFilesControllerAsh::FileDaemonInfo( |
| request.file_metadata().inode(), |
| request.file_metadata().crtime(), base::FilePath(), |
| request.file_metadata().source_url(), |
| request.file_metadata().referrer_url())) |
| : false; |
| |
| dlp::IsDlpPolicyMatchedResponse response_proto; |
| response_proto.set_restricted(restricted); |
| std::unique_ptr<dbus::Response> response = |
| dbus::Response::FromMethodCall(method_call); |
| dbus::MessageWriter writer(response.get()); |
| writer.AppendProtoAsArrayOfBytes(response_proto); |
| std::move(response_sender).Run(std::move(response)); |
| } |
| |
| void DlpFilesPolicyServiceProvider::IsFilesTransferRestricted( |
| dbus::MethodCall* method_call, |
| dbus::ExportedObject::ResponseSender response_sender) { |
| dbus::MessageReader reader(method_call); |
| dlp::IsFilesTransferRestrictedRequest request; |
| if (!reader.PopArrayOfBytesAsProto(&request)) { |
| std::move(response_sender) |
| .Run(dbus::ErrorResponse::FromMethodCall( |
| method_call, DBUS_ERROR_INVALID_ARGS, |
| "Unable to parse IsFilesTransferRestrictedRequest")); |
| return; |
| } |
| if (!request.has_destination_url() && !request.has_destination_component()) { |
| std::move(response_sender) |
| .Run(dbus::ErrorResponse::FromMethodCall( |
| method_call, DBUS_ERROR_INVALID_ARGS, |
| "Missing both destination url and component in request")); |
| return; |
| } |
| |
| std::vector<policy::DlpFilesControllerAsh::FileDaemonInfo> files_info; |
| for (const auto& file : request.transferred_files()) { |
| if (!file.has_inode() || !file.has_path() || !file.has_source_url()) { |
| LOG(ERROR) << "Missing file path or file source url"; |
| continue; |
| } |
| files_info.emplace_back(file.inode(), file.crtime(), |
| base::FilePath(file.path()), file.source_url(), |
| file.referrer_url()); |
| } |
| |
| // Transfer to local file system or access by system components is always |
| // allowed. |
| if (request.has_destination_component() && |
| request.destination_component() == dlp::SYSTEM) { |
| DirectRespondWithRestrictedFilesTransfer( |
| method_call, std::move(response_sender), files_info, |
| ::dlp::RestrictionLevel::LEVEL_ALLOW); |
| return; |
| } |
| |
| policy::DlpFilesControllerAsh* files_controller = |
| policy::DlpFilesControllerAsh::GetForPrimaryProfile(); |
| if (!files_controller) { |
| DirectRespondWithRestrictedFilesTransfer( |
| method_call, std::move(response_sender), files_info, |
| ::dlp::RestrictionLevel::LEVEL_UNSPECIFIED); |
| return; |
| } |
| |
| std::optional<policy::DlpFileDestination> destination; |
| if (request.has_destination_component()) { |
| destination.emplace( |
| MapProtoToPolicyComponent(request.destination_component())); |
| } else { |
| destination.emplace(GURL(request.destination_url())); |
| } |
| |
| policy::dlp::FileAction files_action = policy::dlp::FileAction::kTransfer; |
| if (request.has_file_action()) { |
| files_action = MapProtoToFileAction(request.file_action()); |
| } |
| |
| std::optional<file_manager::io_task::IOTaskId> task_id = std::nullopt; |
| if (request.has_io_task_id()) { |
| task_id = request.io_task_id(); |
| } |
| |
| files_controller->IsFilesTransferRestricted( |
| std::move(task_id), std::move(files_info), std::move(destination.value()), |
| files_action, |
| base::BindOnce(&RespondWithRestrictedFilesTransfer, method_call, |
| std::move(response_sender))); |
| } |
| |
| } // namespace ash |