Skip to content

Commit

Permalink
Add D-Bus interface for retrieving VM permissions
Browse files Browse the repository at this point in the history
This adds the Chrome D-Bus interface that will allow querying set of
permissions (Microphone, Camera, etc) that are available to the VM.

Current implementation returns the same set of permissions for all VMs
of given kind (Plugin VM, Crostini VM) and only Plugin VMs are actually
hooked up.

Bug: 1071872,1016193
Test: dbus-send --system --type=method_call --print-reply --dest=org.chromium.VmPermissionService /org/chromium/VmPermissionService org.chromium.VmPermissionServiceInterface... ....
Change-Id: I972e8a8b467773f473ab0152444fc1b4533c8631
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2253119
Commit-Queue: Dmitry Torokhov <dtor@chromium.org>
Reviewed-by: Mattias Nissler <mnissler@chromium.org>
Reviewed-by: Daniel Ng <danielng@google.com>
Reviewed-by: Steven Bennetts <stevenjb@chromium.org>
Cr-Commit-Position: refs/heads/master@{#784087}
  • Loading branch information
dtor authored and Commit Bot committed Jun 30, 2020
1 parent 07ea0fe commit 0bb5f3a
Show file tree
Hide file tree
Showing 6 changed files with 505 additions and 0 deletions.
4 changes: 4 additions & 0 deletions chrome/browser/chromeos/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ source_set("chromeos") {
"//chromeos/dbus:runtime_probe_proto",
"//chromeos/dbus:seneschal_proto",
"//chromeos/dbus:vm_applications_apps_proto",
"//chromeos/dbus:vm_permission_service_proto",
"//chromeos/dbus/power:power_manager_proto",
"//chromeos/services/assistant/public/mojom",
"//chromeos/strings",
Expand Down Expand Up @@ -1013,6 +1014,8 @@ source_set("chromeos") {
"dbus/smb_fs_service_provider.h",
"dbus/virtual_file_request_service_provider.cc",
"dbus/virtual_file_request_service_provider.h",
"dbus/vm/vm_permission_service_provider.cc",
"dbus/vm/vm_permission_service_provider.h",
"dbus/vm_applications_service_provider.cc",
"dbus/vm_applications_service_provider.h",
"device_sync/device_sync_client_factory.cc",
Expand Down Expand Up @@ -2742,6 +2745,7 @@ action("dbus_service_files") {
"dbus/org.chromium.SmbFsService.conf",
"dbus/org.chromium.VirtualFileRequestService.conf",
"dbus/org.chromium.VmApplicationsService.conf",
"dbus/vm/org.chromium.VmPermissionService.conf",
]
output_conf_file = "$root_out_dir/dbus/chrome_dbus_services.conf"
outputs = [ output_conf_file ]
Expand Down
9 changes: 9 additions & 0 deletions chrome/browser/chromeos/chrome_browser_main_chromeos.cc
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@
#include "chrome/browser/chromeos/dbus/screen_lock_service_provider.h"
#include "chrome/browser/chromeos/dbus/smb_fs_service_provider.h"
#include "chrome/browser/chromeos/dbus/virtual_file_request_service_provider.h"
#include "chrome/browser/chromeos/dbus/vm/vm_permission_service_provider.h"
#include "chrome/browser/chromeos/dbus/vm_applications_service_provider.h"
#include "chrome/browser/chromeos/display/quirks_manager_delegate_impl.h"
#include "chrome/browser/chromeos/events/event_rewriter_delegate_impl.h"
Expand Down Expand Up @@ -323,6 +324,12 @@ class DBusServices {
CrosDBusService::CreateServiceProviderList(
std::make_unique<VmApplicationsServiceProvider>()));

vm_permission_service_ = CrosDBusService::Create(
system_bus, kVmPermissionServiceName,
dbus::ObjectPath(kVmPermissionServicePath),
CrosDBusService::CreateServiceProviderList(
std::make_unique<VmPermissionServiceProvider>()));

drive_file_stream_service_ = CrosDBusService::Create(
system_bus, drivefs::kDriveFileStreamServiceName,
dbus::ObjectPath(drivefs::kDriveFileStreamServicePath),
Expand Down Expand Up @@ -402,6 +409,7 @@ class DBusServices {
component_updater_service_.reset();
chrome_features_service_.reset();
vm_applications_service_.reset();
vm_permission_service_.reset();
drive_file_stream_service_.reset();
cryptohome_key_delegate_service_.reset();
lock_to_single_user_service_.reset();
Expand All @@ -427,6 +435,7 @@ class DBusServices {
std::unique_ptr<CrosDBusService> component_updater_service_;
std::unique_ptr<CrosDBusService> chrome_features_service_;
std::unique_ptr<CrosDBusService> vm_applications_service_;
std::unique_ptr<CrosDBusService> vm_permission_service_;
std::unique_ptr<CrosDBusService> drive_file_stream_service_;
std::unique_ptr<CrosDBusService> cryptohome_key_delegate_service_;
std::unique_ptr<CrosDBusService> libvda_service_;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<!DOCTYPE busconfig PUBLIC "-//freedesktop//DTD D-BUS Bus Configuration 1.0//EN"
"http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd">
<!--
Copyright 2020 The Chromium Authors. All rights reserved.
Use of this source code is governed by a BSD-style license that can be
found in the LICENSE file.
-->

<busconfig>
<policy user="chronos">
<allow own="org.chromium.VmPermissionService"/>
</policy>

<policy user="crosvm">
<allow send_destination="org.chromium.VmPermissionService"
send_interface="org.chromium.VmPermissionServiceInterface"
send_member="RegisterVm"/>
<allow send_destination="org.chromium.VmPermissionService"
send_interface="org.chromium.VmPermissionServiceInterface"
send_member="UnregisterVm"/>
<allow send_destination="org.chromium.VmPermissionService"
send_interface="org.chromium.VmPermissionServiceInterface"
send_member="GetPermissions"/>
</policy>
</busconfig>
303 changes: 303 additions & 0 deletions chrome/browser/chromeos/dbus/vm/vm_permission_service_provider.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,303 @@
// Copyright 2020 The Chromium 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 "chrome/browser/chromeos/dbus/vm/vm_permission_service_provider.h"

#include <memory>
#include <utility>

#include "base/guid.h"
#include "base/logging.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/chromeos/plugin_vm/plugin_vm_pref_names.h"
#include "chrome/browser/chromeos/profiles/profile_helper.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/profiles/profile_manager.h"
#include "chrome/common/pref_names.h"
#include "chromeos/dbus/vm_permission_service/vm_permission_service.pb.h"
#include "components/prefs/pref_service.h"
#include "dbus/message.h"
#include "third_party/cros_system_api/dbus/service_constants.h"

namespace chromeos {

VmPermissionServiceProvider::VmInfo::VmInfo(std::string owner_id,
std::string name,
VmType type)
: owner_id_(std::move(owner_id)), name_(std::move(name)), type_(type) {}

VmPermissionServiceProvider::VmInfo::~VmInfo() = default;

VmPermissionServiceProvider::VmPermissionServiceProvider() {}

VmPermissionServiceProvider::~VmPermissionServiceProvider() = default;

VmPermissionServiceProvider::VmMap::iterator
VmPermissionServiceProvider::FindVm(const std::string& owner_id,
const std::string& name) {
return std::find_if(vms_.begin(), vms_.end(), [&](const auto& vm) {
return vm.second->owner_id_ == owner_id && vm.second->name_ == name;
});
}

void VmPermissionServiceProvider::Start(
scoped_refptr<dbus::ExportedObject> exported_object) {
exported_object->ExportMethod(
kVmPermissionServiceInterface, kVmPermissionServiceRegisterVmMethod,
base::BindRepeating(&VmPermissionServiceProvider::RegisterVm,
weak_ptr_factory_.GetWeakPtr()),
base::BindOnce(&VmPermissionServiceProvider::OnExported,
weak_ptr_factory_.GetWeakPtr()));
exported_object->ExportMethod(
kVmPermissionServiceInterface, kVmPermissionServiceUnregisterVmMethod,
base::BindRepeating(&VmPermissionServiceProvider::UnregisterVm,
weak_ptr_factory_.GetWeakPtr()),
base::BindOnce(&VmPermissionServiceProvider::OnExported,
weak_ptr_factory_.GetWeakPtr()));
exported_object->ExportMethod(
kVmPermissionServiceInterface, kVmPermissionServiceGetPermissionsMethod,
base::BindRepeating(&VmPermissionServiceProvider::GetPermissions,
weak_ptr_factory_.GetWeakPtr()),
base::BindOnce(&VmPermissionServiceProvider::OnExported,
weak_ptr_factory_.GetWeakPtr()));
exported_object->ExportMethod(
kVmPermissionServiceInterface, kVmPermissionServiceSetPermissionsMethod,
base::BindRepeating(&VmPermissionServiceProvider::SetPermissions,
weak_ptr_factory_.GetWeakPtr()),
base::BindOnce(&VmPermissionServiceProvider::OnExported,
weak_ptr_factory_.GetWeakPtr()));
}

void VmPermissionServiceProvider::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 VmPermissionServiceProvider::RegisterVm(
dbus::MethodCall* method_call,
dbus::ExportedObject::ResponseSender response_sender) {
std::unique_ptr<dbus::Response> response =
dbus::Response::FromMethodCall(method_call);

dbus::MessageReader reader(method_call);
vm_permission_service::RegisterVmRequest request;
if (!reader.PopArrayOfBytesAsProto(&request)) {
constexpr char error_message[] =
"Unable to parse RegisterVmRequest from message";
LOG(ERROR) << error_message;
std::move(response_sender)
.Run(dbus::ErrorResponse::FromMethodCall(
method_call, DBUS_ERROR_INVALID_ARGS, error_message));
return;
}

if (FindVm(request.owner_id(), request.name()) != vms_.end()) {
LOG(ERROR) << "VM " << request.owner_id() << "/" << request.name()
<< " is already registered with permission service";
std::move(response_sender)
.Run(dbus::ErrorResponse::FromMethodCall(
method_call, DBUS_ERROR_INVALID_ARGS, "VM is already registered"));
return;
}

VmInfo::VmType vm_type;
if (request.type() == vm_permission_service::RegisterVmRequest::PLUGIN_VM) {
vm_type = VmInfo::VmType::PluginVm;
} else {
LOG(ERROR) << "Unsupported VM " << request.owner_id() << "/"
<< request.name() << " type: " << request.type();
std::move(response_sender)
.Run(dbus::ErrorResponse::FromMethodCall(
method_call, DBUS_ERROR_INVALID_ARGS, "Unsupported VM type"));
return;
}

auto vm =
base::WrapUnique(new VmInfo(request.owner_id(), request.name(), vm_type));

// Seed the initial set of permission. Because in the initial release we
// only support static permissions, i.e. for changes to take effect we need
// to re-launch the VM, we do not need to update them after this.
UpdateVmPermissions(vm.get());

const std::string token(base::GenerateGUID());
vms_[token] = std::move(vm);

vm_permission_service::RegisterVmResponse payload;
payload.set_token(token);

dbus::MessageWriter writer(response.get());
writer.AppendProtoAsArrayOfBytes(payload);
std::move(response_sender).Run(std::move(response));
}

void VmPermissionServiceProvider::UnregisterVm(
dbus::MethodCall* method_call,
dbus::ExportedObject::ResponseSender response_sender) {
std::unique_ptr<dbus::Response> response =
dbus::Response::FromMethodCall(method_call);

dbus::MessageReader reader(method_call);
vm_permission_service::UnregisterVmRequest request;
if (!reader.PopArrayOfBytesAsProto(&request)) {
constexpr char error_message[] =
"Unable to parse RegisterVmRequest from message";
LOG(ERROR) << error_message;
std::move(response_sender)
.Run(dbus::ErrorResponse::FromMethodCall(
method_call, DBUS_ERROR_INVALID_ARGS, error_message));
return;
}

auto iter = FindVm(request.owner_id(), request.name());
if (iter == vms_.end()) {
LOG(ERROR) << "VM " << request.owner_id() << "/" << request.name()
<< " is not registered with permission service";
std::move(response_sender)
.Run(dbus::ErrorResponse::FromMethodCall(
method_call, DBUS_ERROR_INVALID_ARGS, "VM is not registered"));
return;
}

vms_.erase(iter);

std::move(response_sender).Run(std::move(response));
}

void VmPermissionServiceProvider::SetPermissions(
dbus::MethodCall* method_call,
dbus::ExportedObject::ResponseSender response_sender) {
std::unique_ptr<dbus::Response> response =
dbus::Response::FromMethodCall(method_call);

dbus::MessageReader reader(method_call);
vm_permission_service::SetPermissionsRequest request;
if (!reader.PopArrayOfBytesAsProto(&request)) {
constexpr char error_message[] =
"Unable to parse SetPermissionsRequest from message";
LOG(ERROR) << error_message;
std::move(response_sender)
.Run(dbus::ErrorResponse::FromMethodCall(
method_call, DBUS_ERROR_INVALID_ARGS, error_message));
return;
}

auto iter = FindVm(request.owner_id(), request.name());
if (iter == vms_.end()) {
LOG(ERROR) << "VM " << request.owner_id() << "/" << request.name()
<< " is not registered with permission service";
std::move(response_sender)
.Run(dbus::ErrorResponse::FromMethodCall(
method_call, DBUS_ERROR_INVALID_ARGS, "VM is not registered"));
return;
}

std::set<VmInfo::PermissionType> new_permissions(iter->second->permissions_);
for (const auto& p : request.permissions()) {
VmInfo::PermissionType kind;
if (p.kind() == vm_permission_service::Permission::CAMERA) {
kind = VmInfo::PermissionCamera;
} else if (p.kind() == vm_permission_service::Permission::MICROPHONE) {
kind = VmInfo::PermissionMicrophone;
} else {
constexpr char error_message[] = "Unknown permission type";
LOG(ERROR) << error_message;
std::move(response_sender)
.Run(dbus::ErrorResponse::FromMethodCall(
method_call, DBUS_ERROR_INVALID_ARGS, error_message));
return;
}

if (p.allowed()) {
new_permissions.insert(kind);
} else {
new_permissions.erase(kind);
}
}

// Commit final version of permissions.
iter->second->permissions_ = std::move(new_permissions);

std::move(response_sender).Run(std::move(response));
}

void VmPermissionServiceProvider::GetPermissions(
dbus::MethodCall* method_call,
dbus::ExportedObject::ResponseSender response_sender) {
std::unique_ptr<dbus::Response> response =
dbus::Response::FromMethodCall(method_call);

dbus::MessageReader reader(method_call);
vm_permission_service::GetPermissionsRequest request;
if (!reader.PopArrayOfBytesAsProto(&request)) {
constexpr char error_message[] =
"Unable to parse GetPermissionsRequest from message";
LOG(ERROR) << error_message;
std::move(response_sender)
.Run(dbus::ErrorResponse::FromMethodCall(
method_call, DBUS_ERROR_INVALID_ARGS, error_message));
return;
}

auto iter = vms_.find(request.token());
if (iter == vms_.end()) {
LOG(ERROR) << "Invalid token " << request.token();
std::move(response_sender)
.Run(dbus::ErrorResponse::FromMethodCall(
method_call, DBUS_ERROR_INVALID_ARGS, "Invalid token"));
return;
}

vm_permission_service::GetPermissionsResponse payload;
for (auto permission : iter->second->permissions_) {
auto* p = payload.add_permissions();
p->set_allowed(true);
switch (permission) {
case VmInfo::PermissionCamera:
p->set_kind(vm_permission_service::Permission::CAMERA);
break;
case VmInfo::PermissionMicrophone:
p->set_kind(vm_permission_service::Permission::MICROPHONE);
break;
}
}

dbus::MessageWriter writer(response.get());
writer.AppendProtoAsArrayOfBytes(payload);
std::move(response_sender).Run(std::move(response));
}

void VmPermissionServiceProvider::UpdateVmPermissions(VmInfo* vm) {
vm->permissions_.clear();
switch (vm->type_) {
case VmInfo::PluginVm:
UpdatePluginVmPermissions(vm);
break;
case VmInfo::CrostiniVm:
NOTREACHED();
}
}

void VmPermissionServiceProvider::UpdatePluginVmPermissions(VmInfo* vm) {
Profile* profile = ProfileManager::GetPrimaryUserProfile();
if (!profile || chromeos::ProfileHelper::GetUserIdHashFromProfile(profile) !=
vm->owner_id_) {
return;
}

const PrefService* prefs = profile->GetPrefs();
if (prefs->GetBoolean(prefs::kVideoCaptureAllowed) &&
prefs->GetBoolean(plugin_vm::prefs::kPluginVmCameraAllowed)) {
vm->permissions_.insert(VmInfo::PermissionCamera);
}

if (prefs->GetBoolean(prefs::kAudioCaptureAllowed) &&
prefs->GetBoolean(plugin_vm::prefs::kPluginVmMicrophoneAllowed)) {
vm->permissions_.insert(VmInfo::PermissionMicrophone);
}
}

} // namespace chromeos
Loading

0 comments on commit 0bb5f3a

Please sign in to comment.