blob: 8fe01a5ad8f55db33f626602aafa8bc9ce8b86ea [file] [log] [blame]
// Copyright 2014 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 "extensions/browser/api/vpn_provider/vpn_service.h"
#include <stdint.h>
#include <utility>
#include <vector>
#include "base/bind.h"
#include "base/callback_helpers.h"
#include "base/containers/contains.h"
#include "base/guid.h"
#include "base/location.h"
#include "base/logging.h"
#include "base/macros.h"
#include "base/memory/ptr_util.h"
#include "base/single_thread_task_runner.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_util.h"
#include "base/threading/thread_task_runner_handle.h"
#include "base/values.h"
#include "chromeos/dbus/shill/shill_third_party_vpn_driver_client.h"
#include "chromeos/dbus/shill/shill_third_party_vpn_observer.h"
#include "chromeos/network/network_configuration_handler.h"
#include "chromeos/network/network_profile.h"
#include "chromeos/network/network_profile_handler.h"
#include "chromeos/network/network_state.h"
#include "chromeos/network/network_state_handler.h"
#include "chromeos/network/network_type_pattern.h"
#include "content/public/browser/pepper_vpn_provider_resource_host_proxy.h"
#include "content/public/browser/vpn_service_proxy.h"
#include "crypto/sha2.h"
#include "extensions/browser/event_router.h"
#include "extensions/browser/extension_registry.h"
#include "extensions/browser/unloaded_extension_reason.h"
#include "third_party/abseil-cpp/absl/types/optional.h"
#include "third_party/cros_system_api/dbus/service_constants.h"
namespace chromeos {
namespace {
namespace api_vpn = extensions::api::vpn_provider;
void DoNothingFailureCallback(const std::string& error_name,
const std::string& error_message) {
LOG(ERROR) << error_name << ": " << error_message;
}
} // namespace
class VpnService::VpnConfiguration : public ShillThirdPartyVpnObserver {
public:
VpnConfiguration(const std::string& extension_id,
const std::string& configuration_name,
const std::string& key,
base::WeakPtr<VpnService> vpn_service);
VpnConfiguration(const VpnConfiguration&) = delete;
VpnConfiguration& operator=(const VpnConfiguration&) = delete;
~VpnConfiguration() override;
const std::string& extension_id() const { return extension_id_; }
const std::string& configuration_name() const { return configuration_name_; }
const std::string& key() const { return key_; }
const std::string& service_path() const { return service_path_; }
void set_service_path(const std::string& service_path) {
service_path_ = service_path;
}
const std::string& object_path() const { return object_path_; }
void set_pepper_proxy(
std::unique_ptr<content::PepperVpnProviderResourceHostProxy>
pepper_vpn_provider_proxy) {
pepper_vpn_provider_proxy_ = std::move(pepper_vpn_provider_proxy);
}
// ShillThirdPartyVpnObserver:
void OnPacketReceived(const std::vector<char>& data) override;
void OnPlatformMessage(uint32_t message) override;
private:
const std::string extension_id_;
const std::string configuration_name_;
const std::string key_;
const std::string object_path_;
std::unique_ptr<content::PepperVpnProviderResourceHostProxy>
pepper_vpn_provider_proxy_;
std::string service_path_;
base::WeakPtr<VpnService> vpn_service_;
};
VpnService::VpnConfiguration::VpnConfiguration(
const std::string& extension_id,
const std::string& configuration_name,
const std::string& key,
base::WeakPtr<VpnService> vpn_service)
: extension_id_(extension_id),
configuration_name_(configuration_name),
key_(key),
object_path_(shill::kObjectPathBase + key_),
vpn_service_(vpn_service) {}
VpnService::VpnConfiguration::~VpnConfiguration() {}
void VpnService::VpnConfiguration::OnPacketReceived(
const std::vector<char>& data) {
if (!vpn_service_) {
return;
}
// Pass packet to the Pepper API if the connection is bound to it.
if (pepper_vpn_provider_proxy_) {
pepper_vpn_provider_proxy_->SendOnPacketReceived(data);
} else {
auto event_args = api_vpn::OnPacketReceived::Create(
std::vector<uint8_t>(data.begin(), data.end()));
vpn_service_->SendSignalToExtension(
extension_id_, extensions::events::VPN_PROVIDER_ON_PACKET_RECEIVED,
api_vpn::OnPacketReceived::kEventName, std::move(event_args));
}
}
void VpnService::VpnConfiguration::OnPlatformMessage(uint32_t message) {
if (!vpn_service_) {
return;
}
DCHECK_GE(api_vpn::PLATFORM_MESSAGE_LAST, message);
api_vpn::PlatformMessage platform_message =
static_cast<api_vpn::PlatformMessage>(message);
if (platform_message == api_vpn::PLATFORM_MESSAGE_CONNECTED) {
vpn_service_->SetActiveConfiguration(this);
} else if (platform_message == api_vpn::PLATFORM_MESSAGE_DISCONNECTED ||
platform_message == api_vpn::PLATFORM_MESSAGE_ERROR) {
vpn_service_->SetActiveConfiguration(nullptr);
// Disconnect Pepper-bound configuration.
if (pepper_vpn_provider_proxy_) {
pepper_vpn_provider_proxy_->SendOnUnbind();
pepper_vpn_provider_proxy_.reset();
}
}
// TODO(kaliamoorthi): Update the lower layers to get the error message and
// pass in the error instead of std::string().
auto event_args = api_vpn::OnPlatformMessage::Create(
configuration_name_, platform_message, std::string());
vpn_service_->SendSignalToExtension(
extension_id_, extensions::events::VPN_PROVIDER_ON_PLATFORM_MESSAGE,
api_vpn::OnPlatformMessage::kEventName, std::move(event_args));
}
class VpnService::VpnServiceProxyImpl : public content::VpnServiceProxy {
public:
explicit VpnServiceProxyImpl(base::WeakPtr<VpnService> vpn_service);
void Bind(const std::string& extension_id,
const std::string& configuration_id,
const std::string& configuration_name,
SuccessCallback success,
FailureCallback failure,
std::unique_ptr<content::PepperVpnProviderResourceHostProxy>
pepper_vpn_provider_proxy) override;
void SendPacket(const std::string& extension_id,
const std::vector<char>& data,
SuccessCallback success,
FailureCallback failure) override;
private:
base::WeakPtr<VpnService> vpn_service_;
DISALLOW_COPY_AND_ASSIGN(VpnServiceProxyImpl);
};
VpnService::VpnServiceProxyImpl::VpnServiceProxyImpl(
base::WeakPtr<VpnService> vpn_service)
: vpn_service_(vpn_service) {}
void VpnService::VpnServiceProxyImpl::Bind(
const std::string& extension_id,
const std::string& configuration_id,
const std::string& configuration_name,
SuccessCallback success,
FailureCallback failure,
std::unique_ptr<content::PepperVpnProviderResourceHostProxy>
pepper_vpn_provider_proxy) {
if (!vpn_service_) {
NOTREACHED();
return;
}
vpn_service_->Bind(extension_id, configuration_id, configuration_name,
std::move(success), std::move(failure),
std::move(pepper_vpn_provider_proxy));
}
void VpnService::VpnServiceProxyImpl::SendPacket(
const std::string& extension_id,
const std::vector<char>& data,
SuccessCallback success,
FailureCallback failure) {
if (!vpn_service_) {
NOTREACHED();
return;
}
vpn_service_->SendPacket(extension_id, data, std::move(success),
std::move(failure));
}
VpnService::VpnService(
content::BrowserContext* browser_context,
const std::string& userid_hash,
extensions::ExtensionRegistry* extension_registry,
extensions::EventRouter* event_router,
ShillThirdPartyVpnDriverClient* shill_client,
NetworkConfigurationHandler* network_configuration_handler,
NetworkProfileHandler* network_profile_handler,
NetworkStateHandler* network_state_handler)
: browser_context_(browser_context),
userid_hash_(userid_hash),
extension_registry_(extension_registry),
event_router_(event_router),
shill_client_(shill_client),
network_configuration_handler_(network_configuration_handler),
network_profile_handler_(network_profile_handler),
network_state_handler_(network_state_handler),
active_configuration_(nullptr) {
extension_registry_->AddObserver(this);
network_state_handler_->AddObserver(this, FROM_HERE);
network_configuration_handler_->AddObserver(this);
base::ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE, base::BindOnce(&VpnService::NetworkListChanged,
weak_factory_.GetWeakPtr()));
}
VpnService::~VpnService() {
network_configuration_handler_->RemoveObserver(this);
network_state_handler_->RemoveObserver(this, FROM_HERE);
extension_registry_->RemoveObserver(this);
}
void VpnService::SendShowAddDialogToExtension(const std::string& extension_id) {
SendSignalToExtension(extension_id,
extensions::events::VPN_PROVIDER_ON_UI_EVENT,
api_vpn::OnUIEvent::kEventName,
api_vpn::OnUIEvent::Create(
api_vpn::UI_EVENT_SHOWADDDIALOG, std::string()));
}
void VpnService::SendShowConfigureDialogToExtension(
const std::string& extension_id,
const std::string& configuration_id) {
SendSignalToExtension(
extension_id, extensions::events::VPN_PROVIDER_ON_UI_EVENT,
api_vpn::OnUIEvent::kEventName,
api_vpn::OnUIEvent::Create(api_vpn::UI_EVENT_SHOWCONFIGUREDIALOG,
configuration_id));
}
void VpnService::SendPlatformError(const std::string& extension_id,
const std::string& configuration_id,
const std::string& error_message) {
SendSignalToExtension(
extension_id, extensions::events::VPN_PROVIDER_ON_PLATFORM_MESSAGE,
api_vpn::OnPlatformMessage::kEventName,
api_vpn::OnPlatformMessage::Create(
configuration_id, api_vpn::PLATFORM_MESSAGE_ERROR, error_message));
}
std::string VpnService::GetKey(const std::string& extension_id,
const std::string& name) {
const std::string key = crypto::SHA256HashString(extension_id + name);
return base::HexEncode(key.data(), key.size());
}
void VpnService::OnConfigurationRemoved(const std::string& service_path,
const std::string& guid) {
if (service_path_to_configuration_map_.find(service_path) ==
service_path_to_configuration_map_.end()) {
// Ignore removal of a configuration unknown to VPN service, which means the
// configuration was created internally by the platform or already removed
// by the extension.
return;
}
VpnConfiguration* configuration =
service_path_to_configuration_map_[service_path];
auto event_args =
api_vpn::OnConfigRemoved::Create(configuration->configuration_name());
SendSignalToExtension(configuration->extension_id(),
extensions::events::VPN_PROVIDER_ON_CONFIG_REMOVED,
api_vpn::OnConfigRemoved::kEventName,
std::move(event_args));
DestroyConfigurationInternal(configuration);
}
void VpnService::OnGetShillProperties(const std::string& service_path,
absl::optional<base::Value> dictionary) {
if (!dictionary || service_path_to_configuration_map_.find(service_path) !=
service_path_to_configuration_map_.end()) {
return;
}
const std::string* vpn_type =
dictionary->FindStringPath(shill::kProviderTypeProperty);
const std::string* extension_id =
dictionary->FindStringPath(shill::kProviderHostProperty);
const std::string* type = dictionary->FindStringPath(shill::kTypeProperty);
const std::string* configuration_name =
dictionary->FindStringPath(shill::kNameProperty);
if (!vpn_type || !extension_id || !type || !configuration_name ||
*vpn_type != shill::kProviderThirdPartyVpn || *type != shill::kTypeVPN) {
return;
}
if (!extension_registry_->GetExtensionById(
*extension_id, extensions::ExtensionRegistry::ENABLED)) {
// Does not belong to this instance of VpnService.
return;
}
const std::string key = GetKey(*extension_id, *configuration_name);
VpnConfiguration* configuration =
CreateConfigurationInternal(*extension_id, *configuration_name, key);
configuration->set_service_path(service_path);
service_path_to_configuration_map_[service_path] = configuration;
shill_client_->AddShillThirdPartyVpnObserver(configuration->object_path(),
configuration);
}
void VpnService::NetworkListChanged() {
NetworkStateHandler::NetworkStateList network_list;
network_state_handler_->GetVisibleNetworkListByType(NetworkTypePattern::VPN(),
&network_list);
for (auto* iter : network_list) {
if (service_path_to_configuration_map_.find(iter->path()) !=
service_path_to_configuration_map_.end()) {
continue;
}
network_configuration_handler_->GetShillProperties(
iter->path(), base::BindOnce(&VpnService::OnGetShillProperties,
weak_factory_.GetWeakPtr()));
}
}
void VpnService::CreateConfiguration(const std::string& extension_id,
const std::string& extension_name,
const std::string& configuration_name,
SuccessCallback success,
FailureCallback failure) {
if (configuration_name.empty()) {
std::move(failure).Run(std::string(),
std::string("Empty name not supported."));
return;
}
const std::string key = GetKey(extension_id, configuration_name);
if (base::Contains(key_to_configuration_map_, key)) {
std::move(failure).Run(std::string(), std::string("Name not unique."));
return;
}
const NetworkProfile* profile =
network_profile_handler_->GetProfileForUserhash(userid_hash_);
if (!profile) {
std::move(failure).Run(
std::string(),
std::string("No user profile for unshared network configuration."));
return;
}
VpnConfiguration* configuration =
CreateConfigurationInternal(extension_id, configuration_name, key);
base::DictionaryValue properties;
properties.SetKey(shill::kTypeProperty, base::Value(shill::kTypeVPN));
properties.SetKey(shill::kNameProperty, base::Value(configuration_name));
properties.SetKey(shill::kProviderHostProperty, base::Value(extension_id));
properties.SetKey(shill::kObjectPathSuffixProperty,
base::Value(configuration->key()));
properties.SetKey(shill::kProviderTypeProperty,
base::Value(shill::kProviderThirdPartyVpn));
properties.SetKey(shill::kProfileProperty, base::Value(profile->path));
// Note: This will not create an entry in |policy_util|. TODO(pneubeck):
// Determine the correct thing to do here, crbug.com/459278.
std::string guid = base::GenerateGUID();
properties.SetKey(shill::kGuidProperty, base::Value(guid));
network_configuration_handler_->CreateShillConfiguration(
properties,
base::BindOnce(&VpnService::OnCreateConfigurationSuccess,
weak_factory_.GetWeakPtr(), std::move(success),
configuration),
base::BindOnce(&VpnService::OnCreateConfigurationFailure,
weak_factory_.GetWeakPtr(), std::move(failure),
configuration));
}
void VpnService::DestroyConfiguration(const std::string& extension_id,
const std::string& configuration_id,
SuccessCallback success,
FailureCallback failure) {
// The ID is the configuration name for now. This may change in the future.
const std::string key = GetKey(extension_id, configuration_id);
if (!base::Contains(key_to_configuration_map_, key)) {
std::move(failure).Run(std::string(), std::string("Unauthorized access."));
return;
}
VpnConfiguration* configuration = key_to_configuration_map_[key].get();
const std::string service_path = configuration->service_path();
if (service_path.empty()) {
std::move(failure).Run(std::string(), std::string("Pending create."));
return;
}
if (active_configuration_ == configuration) {
configuration->OnPlatformMessage(api_vpn::PLATFORM_MESSAGE_DISCONNECTED);
}
DestroyConfigurationInternal(configuration);
network_configuration_handler_->RemoveConfiguration(
service_path,
/*remove_confirmer=*/absl::nullopt,
base::BindOnce(&VpnService::OnRemoveConfigurationSuccess,
weak_factory_.GetWeakPtr(), std::move(success)),
base::BindOnce(&VpnService::OnRemoveConfigurationFailure,
weak_factory_.GetWeakPtr(), std::move(failure)));
}
void VpnService::SetParameters(const std::string& extension_id,
const base::DictionaryValue& parameters,
StringCallback success,
FailureCallback failure) {
if (!DoesActiveConfigurationExistAndIsAccessAuthorized(extension_id)) {
std::move(failure).Run(std::string(), std::string("Unauthorized access."));
return;
}
shill_client_->SetParameters(active_configuration_->object_path(), parameters,
std::move(success), std::move(failure));
}
void VpnService::SendPacket(const std::string& extension_id,
const std::vector<char>& data,
SuccessCallback success,
FailureCallback failure) {
if (!DoesActiveConfigurationExistAndIsAccessAuthorized(extension_id)) {
std::move(failure).Run(std::string(), std::string("Unauthorized access."));
return;
}
if (data.empty()) {
std::move(failure).Run(std::string(),
std::string("Can't send an empty packet."));
return;
}
shill_client_->SendPacket(active_configuration_->object_path(), data,
std::move(success), std::move(failure));
}
void VpnService::NotifyConnectionStateChanged(const std::string& extension_id,
api_vpn::VpnConnectionState state,
SuccessCallback success,
FailureCallback failure) {
if (!DoesActiveConfigurationExistAndIsAccessAuthorized(extension_id)) {
std::move(failure).Run(std::string(), std::string("Unauthorized access."));
return;
}
shill_client_->UpdateConnectionState(active_configuration_->object_path(),
static_cast<uint32_t>(state),
std::move(success), std::move(failure));
}
bool VpnService::VerifyConfigExistsForTesting(
const std::string& extension_id,
const std::string& configuration_name) {
const std::string key = GetKey(extension_id, configuration_name);
return base::Contains(key_to_configuration_map_, key);
}
bool VpnService::VerifyConfigIsConnectedForTesting(
const std::string& extension_id) {
return DoesActiveConfigurationExistAndIsAccessAuthorized(extension_id);
}
void VpnService::DestroyConfigurationsForExtension(
const extensions::Extension* extension) {
std::vector<VpnConfiguration*> to_be_destroyed;
for (const auto& iter : key_to_configuration_map_) {
if (iter.second->extension_id() == extension->id()) {
to_be_destroyed.push_back(iter.second.get());
}
}
for (auto* iter : to_be_destroyed) {
DestroyConfiguration(extension->id(), // Extension ID
iter->configuration_name(), // Configuration name
base::DoNothing(),
base::BindOnce(DoNothingFailureCallback));
}
}
void VpnService::OnExtensionUninstalled(
content::BrowserContext* browser_context,
const extensions::Extension* extension,
extensions::UninstallReason reason) {
if (browser_context != browser_context_) {
NOTREACHED();
return;
}
DestroyConfigurationsForExtension(extension);
}
void VpnService::OnExtensionUnloaded(
content::BrowserContext* browser_context,
const extensions::Extension* extension,
extensions::UnloadedExtensionReason reason) {
if (browser_context != browser_context_) {
NOTREACHED();
return;
}
if (active_configuration_ &&
active_configuration_->extension_id() == extension->id()) {
shill_client_->UpdateConnectionState(
active_configuration_->object_path(),
static_cast<uint32_t>(api_vpn::VPN_CONNECTION_STATE_FAILURE),
base::DoNothing(), base::BindOnce(DoNothingFailureCallback));
}
if (reason == extensions::UnloadedExtensionReason::DISABLE ||
reason == extensions::UnloadedExtensionReason::BLOCKLIST) {
DestroyConfigurationsForExtension(extension);
}
}
void VpnService::OnCreateConfigurationSuccess(
VpnService::SuccessCallback callback,
VpnConfiguration* configuration,
const std::string& service_path,
const std::string& guid) {
configuration->set_service_path(service_path);
service_path_to_configuration_map_[service_path] = configuration;
shill_client_->AddShillThirdPartyVpnObserver(configuration->object_path(),
configuration);
std::move(callback).Run();
}
void VpnService::OnCreateConfigurationFailure(
VpnService::FailureCallback callback,
VpnConfiguration* configuration,
const std::string& error_name,
std::unique_ptr<base::DictionaryValue> error_data) {
DestroyConfigurationInternal(configuration);
std::move(callback).Run(error_name, std::string());
}
void VpnService::OnRemoveConfigurationSuccess(
VpnService::SuccessCallback callback) {
std::move(callback).Run();
}
void VpnService::OnRemoveConfigurationFailure(
VpnService::FailureCallback callback,
const std::string& error_name,
std::unique_ptr<base::DictionaryValue> error_data) {
std::move(callback).Run(error_name, std::string());
}
void VpnService::SendSignalToExtension(
const std::string& extension_id,
extensions::events::HistogramValue histogram_value,
const std::string& event_name,
std::vector<base::Value> event_args) {
std::unique_ptr<extensions::Event> event(new extensions::Event(
histogram_value, event_name, std::move(event_args), browser_context_));
event_router_->DispatchEventToExtension(extension_id, std::move(event));
}
void VpnService::SetActiveConfiguration(
VpnService::VpnConfiguration* configuration) {
active_configuration_ = configuration;
}
VpnService::VpnConfiguration* VpnService::CreateConfigurationInternal(
const std::string& extension_id,
const std::string& configuration_name,
const std::string& key) {
VpnConfiguration* configuration = new VpnConfiguration(
extension_id, configuration_name, key, weak_factory_.GetWeakPtr());
key_to_configuration_map_[key] = base::WrapUnique(configuration);
return configuration;
}
void VpnService::DestroyConfigurationInternal(VpnConfiguration* configuration) {
std::unique_ptr<VpnConfiguration> configuration_ptr =
std::move(key_to_configuration_map_[configuration->key()]);
key_to_configuration_map_.erase(configuration->key());
if (active_configuration_ == configuration) {
active_configuration_ = nullptr;
}
if (!configuration->service_path().empty()) {
shill_client_->RemoveShillThirdPartyVpnObserver(
configuration->object_path());
service_path_to_configuration_map_.erase(configuration->service_path());
}
}
bool VpnService::DoesActiveConfigurationExistAndIsAccessAuthorized(
const std::string& extension_id) {
return active_configuration_ &&
active_configuration_->extension_id() == extension_id;
}
void VpnService::Bind(
const std::string& extension_id,
const std::string& configuration_id,
const std::string& configuration_name,
SuccessCallback success,
FailureCallback failure,
std::unique_ptr<content::PepperVpnProviderResourceHostProxy>
pepper_vpn_provider_proxy) {
// The ID is the configuration name for now. This may change in the future.
const std::string key = GetKey(extension_id, configuration_id);
if (!base::Contains(key_to_configuration_map_, key)) {
std::move(failure).Run(std::string(),
std::string("Unauthorized access. "
"The configuration does not exist."));
return;
}
VpnConfiguration* configuration = key_to_configuration_map_[key].get();
if (active_configuration_ != configuration) {
std::move(failure).Run(std::string(),
std::string("Unauthorized access. "
"The configuration is not active."));
return;
}
if (configuration->extension_id() != extension_id ||
configuration->configuration_name() != configuration_name) {
std::move(failure).Run(
std::string(),
std::string("Unauthorized access. "
"Configuration name or extension ID mismatch."));
return;
}
const std::string service_path = configuration->service_path();
if (service_path.empty()) {
std::move(failure).Run(std::string(), std::string("Pending create."));
return;
}
// Connection authorized. All packets will be routed through the Pepper API.
configuration->set_pepper_proxy(std::move(pepper_vpn_provider_proxy));
std::move(success).Run();
}
std::unique_ptr<content::VpnServiceProxy> VpnService::GetVpnServiceProxy() {
return base::WrapUnique(new VpnServiceProxyImpl(weak_factory_.GetWeakPtr()));
}
const std::string VpnService::GetSingleServicepathForTesting() {
if (service_path_to_configuration_map_.size() == 1)
return service_path_to_configuration_map_.begin()->first;
return std::string();
}
} // namespace chromeos