blob: 1214a3b89a7af02a9eb5d4aee8146513d78af9d4 [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/bind_helpers.h"
#include "base/guid.h"
#include "base/location.h"
#include "base/logging.h"
#include "base/macros.h"
#include "base/message_loop/message_loop.h"
#include "base/stl_util.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_util.h"
#include "base/values.h"
#include "chromeos/dbus/shill_third_party_vpn_driver_client.h"
#include "chromeos/dbus/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 "crypto/sha2.h"
#include "extensions/browser/event_router.h"
#include "extensions/browser/extension_registry.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() 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_; }
// 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::string service_path_;
base::WeakPtr<VpnService> vpn_service_;
DISALLOW_COPY_AND_ASSIGN(VpnConfiguration);
};
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;
}
scoped_ptr<base::ListValue> event_args =
api_vpn::OnPacketReceived::Create(data);
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);
vpn_service_->SetActiveConfiguration(
platform_message == api_vpn::PLATFORM_MESSAGE_CONNECTED ? this : nullptr);
// TODO(kaliamoorthi): Update the lower layers to get the error message and
// pass in the error instead of std::string().
scoped_ptr<base::ListValue> 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));
}
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),
weak_factory_(this) {
extension_registry_->AddObserver(this);
network_state_handler_->AddObserver(this, FROM_HERE);
network_configuration_handler_->AddObserver(this);
base::MessageLoop::current()->PostTask(
FROM_HERE,
base::Bind(&VpnService::NetworkListChanged, weak_factory_.GetWeakPtr()));
}
VpnService::~VpnService() {
network_configuration_handler_->RemoveObserver(this);
network_state_handler_->RemoveObserver(this, FROM_HERE);
extension_registry_->RemoveObserver(this);
STLDeleteContainerPairSecondPointers(key_to_configuration_map_.begin(),
key_to_configuration_map_.end());
}
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::OnConfigurationCreated(const std::string& service_path,
const std::string& profile_path,
const base::DictionaryValue& properties,
Source source) {
}
void VpnService::OnConfigurationRemoved(const std::string& service_path,
const std::string& guid,
Source source) {
if (source == SOURCE_EXTENSION_INSTALL) {
// No need to process if the configuration was removed using an extension
// API since the API would have already done the cleanup.
return;
}
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.
return;
}
VpnConfiguration* configuration =
service_path_to_configuration_map_[service_path];
scoped_ptr<base::ListValue> 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::OnPropertiesSet(const std::string& service_path,
const std::string& guid,
const base::DictionaryValue& set_properties,
Source source) {
}
void VpnService::OnConfigurationProfileChanged(const std::string& service_path,
const std::string& profile_path,
Source source) {
}
void VpnService::OnGetPropertiesSuccess(
const std::string& service_path,
const base::DictionaryValue& dictionary) {
if (service_path_to_configuration_map_.find(service_path) !=
service_path_to_configuration_map_.end()) {
return;
}
std::string vpn_type;
std::string extension_id;
std::string type;
std::string configuration_name;
if (!dictionary.GetString(shill::kProviderTypeProperty, &vpn_type) ||
!dictionary.GetString(shill::kProviderHostProperty, &extension_id) ||
!dictionary.GetString(shill::kTypeProperty, &type) ||
!dictionary.GetString(shill::kNameProperty, &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::OnGetPropertiesFailure(
const std::string& error_name,
scoped_ptr<base::DictionaryValue> error_data) {
}
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::Bind(&VpnService::OnGetPropertiesSuccess,
weak_factory_.GetWeakPtr()),
base::Bind(&VpnService::OnGetPropertiesFailure,
weak_factory_.GetWeakPtr()));
}
}
void VpnService::CreateConfiguration(const std::string& extension_id,
const std::string& extension_name,
const std::string& configuration_name,
const SuccessCallback& success,
const FailureCallback& failure) {
if (configuration_name.empty()) {
failure.Run(std::string(), std::string("Empty name not supported."));
return;
}
const std::string key = GetKey(extension_id, configuration_name);
if (ContainsKey(key_to_configuration_map_, key)) {
failure.Run(std::string(), std::string("Name not unique."));
return;
}
const NetworkProfile* profile =
network_profile_handler_->GetProfileForUserhash(userid_hash_);
if (!profile) {
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.SetStringWithoutPathExpansion(shill::kTypeProperty,
shill::kTypeVPN);
properties.SetStringWithoutPathExpansion(shill::kNameProperty,
configuration_name);
properties.SetStringWithoutPathExpansion(shill::kProviderHostProperty,
extension_id);
properties.SetStringWithoutPathExpansion(shill::kObjectPathSuffixProperty,
configuration->key());
properties.SetStringWithoutPathExpansion(shill::kProviderTypeProperty,
shill::kProviderThirdPartyVpn);
properties.SetStringWithoutPathExpansion(shill::kProfileProperty,
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.SetStringWithoutPathExpansion(shill::kGuidProperty, guid);
network_configuration_handler_->CreateShillConfiguration(
properties, NetworkConfigurationObserver::SOURCE_EXTENSION_INSTALL,
base::Bind(&VpnService::OnCreateConfigurationSuccess,
weak_factory_.GetWeakPtr(), success, configuration),
base::Bind(&VpnService::OnCreateConfigurationFailure,
weak_factory_.GetWeakPtr(), failure, configuration));
}
void VpnService::DestroyConfiguration(const std::string& extension_id,
const std::string& configuration_id,
const SuccessCallback& success,
const 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 (!ContainsKey(key_to_configuration_map_, key)) {
failure.Run(std::string(), std::string("Unauthorized access."));
return;
}
VpnConfiguration* configuration = key_to_configuration_map_[key];
const std::string service_path = configuration->service_path();
if (service_path.empty()) {
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, NetworkConfigurationObserver::SOURCE_EXTENSION_INSTALL,
base::Bind(&VpnService::OnRemoveConfigurationSuccess,
weak_factory_.GetWeakPtr(), success),
base::Bind(&VpnService::OnRemoveConfigurationFailure,
weak_factory_.GetWeakPtr(), failure));
}
void VpnService::SetParameters(const std::string& extension_id,
const base::DictionaryValue& parameters,
const StringCallback& success,
const FailureCallback& failure) {
if (!DoesActiveConfigurationExistAndIsAccessAuthorized(extension_id)) {
failure.Run(std::string(), std::string("Unauthorized access."));
return;
}
shill_client_->SetParameters(active_configuration_->object_path(), parameters,
success, failure);
}
void VpnService::SendPacket(const std::string& extension_id,
const std::vector<char>& data,
const SuccessCallback& success,
const FailureCallback& failure) {
if (!DoesActiveConfigurationExistAndIsAccessAuthorized(extension_id)) {
failure.Run(std::string(), std::string("Unauthorized access."));
return;
}
if (data.empty()) {
failure.Run(std::string(), std::string("Can't send an empty packet."));
return;
}
shill_client_->SendPacket(active_configuration_->object_path(), data, success,
failure);
}
void VpnService::NotifyConnectionStateChanged(const std::string& extension_id,
api_vpn::VpnConnectionState state,
const SuccessCallback& success,
const FailureCallback& failure) {
if (!DoesActiveConfigurationExistAndIsAccessAuthorized(extension_id)) {
failure.Run(std::string(), std::string("Unauthorized access."));
return;
}
shill_client_->UpdateConnectionState(active_configuration_->object_path(),
static_cast<uint32_t>(state), success,
failure);
}
bool VpnService::VerifyConfigExistsForTesting(
const std::string& extension_id,
const std::string& configuration_name) {
const std::string key = GetKey(extension_id, configuration_name);
return ContainsKey(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);
}
}
for (auto& iter : to_be_destroyed) {
DestroyConfiguration(extension->id(), // Extension ID
iter->configuration_name(), // Configuration name
base::Bind(base::DoNothing),
base::Bind(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::UnloadedExtensionInfo::Reason 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::Bind(base::DoNothing), base::Bind(DoNothingFailureCallback));
}
if (reason == extensions::UnloadedExtensionInfo::REASON_DISABLE ||
reason == extensions::UnloadedExtensionInfo::REASON_BLACKLIST) {
DestroyConfigurationsForExtension(extension);
}
}
void VpnService::OnCreateConfigurationSuccess(
const VpnService::SuccessCallback& callback,
VpnConfiguration* configuration,
const std::string& service_path) {
configuration->set_service_path(service_path);
service_path_to_configuration_map_[service_path] = configuration;
shill_client_->AddShillThirdPartyVpnObserver(configuration->object_path(),
configuration);
callback.Run();
}
void VpnService::OnCreateConfigurationFailure(
const VpnService::FailureCallback& callback,
VpnConfiguration* configuration,
const std::string& error_name,
scoped_ptr<base::DictionaryValue> error_data) {
DestroyConfigurationInternal(configuration);
callback.Run(error_name, std::string());
}
void VpnService::OnRemoveConfigurationSuccess(
const VpnService::SuccessCallback& callback) {
callback.Run();
}
void VpnService::OnRemoveConfigurationFailure(
const VpnService::FailureCallback& callback,
const std::string& error_name,
scoped_ptr<base::DictionaryValue> error_data) {
callback.Run(error_name, std::string());
}
void VpnService::SendSignalToExtension(
const std::string& extension_id,
extensions::events::HistogramValue histogram_value,
const std::string& event_name,
scoped_ptr<base::ListValue> event_args) {
scoped_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());
// The object is owned by key_to_configuration_map_ henceforth.
key_to_configuration_map_[key] = configuration;
return configuration;
}
void VpnService::DestroyConfigurationInternal(VpnConfiguration* configuration) {
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());
}
delete configuration;
}
bool VpnService::DoesActiveConfigurationExistAndIsAccessAuthorized(
const std::string& extension_id) {
return active_configuration_ &&
active_configuration_->extension_id() == extension_id;
}
} // namespace chromeos