forked from chromium/chromium
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add D-Bus interface for retrieving VM permissions
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
Showing
6 changed files
with
505 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
25 changes: 25 additions & 0 deletions
25
chrome/browser/chromeos/dbus/vm/org.chromium.VmPermissionService.conf
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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
303
chrome/browser/chromeos/dbus/vm/vm_permission_service_provider.cc
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
Oops, something went wrong.