blob: 5f434a512f5364241390296cda72f117f97f6cb5 [file] [log] [blame]
// Copyright 2018 The Chromium OS 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 "shill/vpn/vpn_driver.h"
#include <string>
#include <vector>
#include <base/strings/string_util.h>
#include <chromeos/dbus/service_constants.h>
#include "shill/accessor_interface.h"
#include "shill/connection.h"
#include "shill/event_dispatcher.h"
#include "shill/logging.h"
#include "shill/manager.h"
#include "shill/property_accessor.h"
#include "shill/property_store.h"
#include "shill/store_interface.h"
using std::string;
using std::vector;
namespace shill {
namespace Logging {
static auto kModuleLogScope = ScopeLogger::kVPN;
static string ObjectID(const VPNDriver* v) {
return "(vpn_driver)";
}
} // namespace Logging
// TODO(crbug.com/1084279) Migrate back to storing property names after crypto
// code is removed.
const char VPNDriver::kCredentialPrefix[] = "Credential.";
VPNDriver::VPNDriver(Manager* manager,
ProcessManager* process_manager,
const Property* properties,
size_t property_count)
: manager_(manager),
process_manager_(process_manager),
properties_(properties),
property_count_(property_count),
connect_timeout_seconds_(0),
weak_ptr_factory_(this) {}
VPNDriver::~VPNDriver() = default;
bool VPNDriver::Load(const StoreInterface* storage, const string& storage_id) {
SLOG(this, 2) << __func__;
for (size_t i = 0; i < property_count_; i++) {
if ((properties_[i].flags & Property::kEphemeral)) {
continue;
}
const string property = properties_[i].property;
if (properties_[i].flags & Property::kArray) {
CHECK(!(properties_[i].flags & Property::kCredential))
<< "Property cannot be both an array and a credential";
vector<string> value;
if (storage->GetStringList(storage_id, property, &value)) {
args_.Set<Strings>(property, value);
} else {
args_.Remove(property);
}
} else {
string value;
bool loaded = (properties_[i].flags & Property::kCredential)
? storage->GetCryptedString(
storage_id, property,
string(kCredentialPrefix) + property, &value)
: storage->GetString(storage_id, property, &value);
if (loaded) {
args_.Set<string>(property, value);
} else {
args_.Remove(property);
}
}
}
return true;
}
void VPNDriver::MigrateDeprecatedStorage(StoreInterface* storage,
const string& storage_id) {
SLOG(this, 2) << __func__;
// Migrate from ROT47 to plaintext.
// TODO(crbug.com/1084279) Migrate back to not using kCredentialPrefix once
// ROT47 migration is complete.
for (size_t i = 0; i < property_count_; i++) {
if (!(properties_[i].flags & Property::kCredential)) {
continue;
}
CHECK(!(properties_[i].flags & Property::kArray))
<< "Property cannot be both an array and a credential";
string deprecated_key = properties_[i].property;
string credentials_key = string(kCredentialPrefix) + deprecated_key;
if (storage->DeleteKey(storage_id, deprecated_key)) {
string value = args_.Get<string>(properties_[i].property);
storage->SetString(storage_id, credentials_key, value);
}
}
}
bool VPNDriver::Save(StoreInterface* storage,
const string& storage_id,
bool save_credentials) {
SLOG(this, 2) << __func__;
for (size_t i = 0; i < property_count_; i++) {
if ((properties_[i].flags & Property::kEphemeral)) {
continue;
}
bool credential = (properties_[i].flags & Property::kCredential);
const string property = properties_[i].property;
if (properties_[i].flags & Property::kArray) {
CHECK(!credential) << "Property cannot be both an array and a credential";
if (!args_.Contains<Strings>(property)) {
storage->DeleteKey(storage_id, property);
continue;
}
Strings value = args_.Get<Strings>(property);
storage->SetStringList(storage_id, property, value);
} else {
string storage_key = property;
if (credential) {
storage_key = string(kCredentialPrefix) + storage_key;
}
if (!args_.Contains<string>(property) ||
(credential && !save_credentials)) {
storage->DeleteKey(storage_id, storage_key);
continue;
}
string value = args_.Get<string>(property);
storage->SetString(storage_id, storage_key, value);
}
}
return true;
}
void VPNDriver::UnloadCredentials() {
SLOG(this, 2) << __func__;
for (size_t i = 0; i < property_count_; i++) {
if ((properties_[i].flags &
(Property::kEphemeral | Property::kCredential))) {
args_.Remove(properties_[i].property);
}
}
}
void VPNDriver::InitPropertyStore(PropertyStore* store) {
SLOG(this, 2) << __func__;
for (size_t i = 0; i < property_count_; i++) {
if (properties_[i].flags & Property::kArray) {
store->RegisterDerivedStrings(
properties_[i].property,
StringsAccessor(new CustomMappedAccessor<VPNDriver, Strings, size_t>(
this, &VPNDriver::ClearMappedStringsProperty,
&VPNDriver::GetMappedStringsProperty,
&VPNDriver::SetMappedStringsProperty, i)));
} else {
store->RegisterDerivedString(
properties_[i].property,
StringAccessor(new CustomMappedAccessor<VPNDriver, string, size_t>(
this, &VPNDriver::ClearMappedStringProperty,
&VPNDriver::GetMappedStringProperty,
&VPNDriver::SetMappedStringProperty, i)));
}
}
store->RegisterDerivedKeyValueStore(
kProviderProperty,
KeyValueStoreAccessor(new CustomAccessor<VPNDriver, KeyValueStore>(
this, &VPNDriver::GetProvider, nullptr)));
}
void VPNDriver::ClearMappedStringProperty(const size_t& index, Error* error) {
CHECK(index < property_count_);
if (args_.Contains<string>(properties_[index].property)) {
args_.Remove(properties_[index].property);
} else {
error->Populate(Error::kNotFound, "Property is not set");
}
}
void VPNDriver::ClearMappedStringsProperty(const size_t& index, Error* error) {
CHECK(index < property_count_);
if (args_.Contains<Strings>(properties_[index].property)) {
args_.Remove(properties_[index].property);
} else {
error->Populate(Error::kNotFound, "Property is not set");
}
}
string VPNDriver::GetMappedStringProperty(const size_t& index, Error* error) {
// Provider properties are set via SetProperty calls to "Provider.XXX",
// however, they are retrieved via a GetProperty call, which returns all
// properties in a single "Provider" dict. Therefore, none of the individual
// properties in the kProperties are available for enumeration in
// GetProperties. Instead, they are retrieved via GetProvider below.
error->Populate(Error::kInvalidArguments,
"Provider properties are not read back in this manner");
return string();
}
Strings VPNDriver::GetMappedStringsProperty(const size_t& index, Error* error) {
// Provider properties are set via SetProperty calls to "Provider.XXX",
// however, they are retrieved via a GetProperty call, which returns all
// properties in a single "Provider" dict. Therefore, none of the individual
// properties in the kProperties are available for enumeration in
// GetProperties. Instead, they are retrieved via GetProvider below.
error->Populate(Error::kInvalidArguments,
"Provider properties are not read back in this manner");
return Strings();
}
bool VPNDriver::SetMappedStringProperty(const size_t& index,
const string& value,
Error* error) {
CHECK(index < property_count_);
if (args_.Contains<string>(properties_[index].property) &&
args_.Get<string>(properties_[index].property) == value) {
return false;
}
args_.Set<string>(properties_[index].property, value);
return true;
}
bool VPNDriver::SetMappedStringsProperty(const size_t& index,
const Strings& value,
Error* error) {
CHECK(index < property_count_);
if (args_.Contains<Strings>(properties_[index].property) &&
args_.Get<Strings>(properties_[index].property) == value) {
return false;
}
args_.Set<Strings>(properties_[index].property, value);
return true;
}
KeyValueStore VPNDriver::GetProvider(Error* error) {
SLOG(this, 2) << __func__;
string provider_prefix = string(kProviderProperty) + ".";
KeyValueStore provider_properties;
for (size_t i = 0; i < property_count_; i++) {
if ((properties_[i].flags & Property::kWriteOnly)) {
continue;
}
string prop = properties_[i].property;
// Chomp off leading "Provider." from properties that have this prefix.
string chopped_prop;
if (base::StartsWith(prop, provider_prefix,
base::CompareCase::INSENSITIVE_ASCII)) {
chopped_prop = prop.substr(provider_prefix.length());
} else {
chopped_prop = prop;
}
if (properties_[i].flags & Property::kArray) {
if (!args_.Contains<Strings>(prop)) {
continue;
}
Strings value = args_.Get<Strings>(prop);
provider_properties.Set<Strings>(chopped_prop, value);
} else {
if (!args_.Contains<string>(prop)) {
continue;
}
string value = args_.Get<string>(prop);
provider_properties.Set<string>(chopped_prop, value);
}
}
return provider_properties;
}
void VPNDriver::StartConnectTimeout(int timeout_seconds) {
if (IsConnectTimeoutStarted()) {
return;
}
LOG(INFO) << "Schedule VPN connect timeout: " << timeout_seconds
<< " seconds.";
connect_timeout_seconds_ = timeout_seconds;
connect_timeout_callback_.Reset(
Bind(&VPNDriver::OnConnectTimeout, weak_ptr_factory_.GetWeakPtr()));
dispatcher()->PostDelayedTask(FROM_HERE, connect_timeout_callback_.callback(),
timeout_seconds * 1000);
}
void VPNDriver::StopConnectTimeout() {
SLOG(this, 2) << __func__;
connect_timeout_callback_.Cancel();
connect_timeout_seconds_ = 0;
}
bool VPNDriver::IsConnectTimeoutStarted() const {
return !connect_timeout_callback_.IsCancelled();
}
void VPNDriver::OnConnectTimeout() {
LOG(INFO) << "VPN connect timeout.";
StopConnectTimeout();
}
void VPNDriver::OnBeforeSuspend(const ResultCallback& callback) {
// Nothing to be done in the general case, so immediately report success.
callback.Run(Error(Error::kSuccess));
}
void VPNDriver::OnAfterResume() {}
void VPNDriver::OnDefaultPhysicalServiceEvent(
DefaultPhysicalServiceEvent event) {}
string VPNDriver::GetHost() const {
return args_.Lookup<string>(kProviderHostProperty, "");
}
ControlInterface* VPNDriver::control_interface() const {
return manager_->control_interface();
}
EventDispatcher* VPNDriver::dispatcher() const {
return manager_->dispatcher();
}
Metrics* VPNDriver::metrics() const {
return manager_->metrics();
}
} // namespace shill