blob: 46c9f830f485fbe64434b41b643387f54743aa9a [file] [log] [blame]
// Copyright (c) 2012 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 <memory>
#include <string>
#include <utility>
#include "base/json/json_reader.h"
#include "base/json/json_writer.h"
#include "base/logging.h"
#include "base/macros.h"
#include "base/strings/string_util.h"
#include "base/values.h"
#include "chromeos/network/network_profile_handler.h"
#include "chromeos/network/network_state.h"
#include "chromeos/network/network_util.h"
#include "chromeos/network/onc/onc_signature.h"
#include "chromeos/network/onc/onc_translation_tables.h"
#include "chromeos/network/onc/onc_translator.h"
#include "chromeos/network/onc/onc_utils.h"
#include "chromeos/network/shill_property_util.h"
#include "components/device_event_log/device_event_log.h"
#include "components/onc/onc_constants.h"
#include "third_party/cros_system_api/dbus/service_constants.h"
#include "third_party/cros_system_api/dbus/shill/dbus-constants.h"
namespace chromeos {
namespace onc {
namespace {
// Converts a VPN string to a base::Value of the given |type|. If the conversion
// fails, returns a default value for |type|.
base::Value ConvertVpnStringToValue(const std::string& str,
base::Value::Type type) {
if (type == base::Value::Type::STRING)
return base::Value(str);
base::Optional<base::Value> value = base::JSONReader::Read(str);
if (!value || value->type() != type)
return base::Value(type);
return std::move(*value);
}
// Returns the string value of |key| from |dict| if found, or the empty string
// otherwise.
std::string FindStringKeyOrEmpty(const base::Value* dict,
base::StringPiece key) {
const std::string* value = dict->FindStringKey(key);
return value ? *value : std::string();
}
// If the network is configured with an installed certificate, a PKCS11 id will
// be set which is provided for the UI to display certificate information.
// Returns true if the PKCS11 id is available and set.
bool SetPKCS11Id(const base::Value* shill_dictionary,
const char* cert_id_property,
const char* cert_slot_property,
base::Value* onc_object) {
const std::string* shill_cert_id =
shill_dictionary->FindStringKey(cert_id_property);
if (!shill_cert_id || shill_cert_id->empty()) {
return false;
}
const std::string* shill_slot =
shill_dictionary->FindStringKey(cert_slot_property);
std::string pkcs11_id;
if (shill_slot && !shill_slot->empty()) {
pkcs11_id = *shill_slot + ":" + *shill_cert_id;
} else {
pkcs11_id = *shill_cert_id;
}
onc_object->SetKey(::onc::client_cert::kClientCertType,
base::Value(::onc::client_cert::kPKCS11Id));
onc_object->SetKey(::onc::client_cert::kClientCertPKCS11Id,
base::Value(pkcs11_id));
return true;
}
// This class implements the translation of properties from the given
// |shill_dictionary| to a new ONC object of signature |onc_signature|. Using
// recursive calls to CreateTranslatedONCObject of new instances, nested objects
// are translated.
class ShillToONCTranslator {
public:
ShillToONCTranslator(const base::Value& shill_dictionary,
::onc::ONCSource onc_source,
const OncValueSignature& onc_signature,
const NetworkState* network_state)
: shill_dictionary_(&shill_dictionary),
onc_source_(onc_source),
onc_signature_(&onc_signature),
network_state_(network_state) {
field_translation_table_ = GetFieldTranslationTable(onc_signature);
}
ShillToONCTranslator(const base::Value& shill_dictionary,
::onc::ONCSource onc_source,
const OncValueSignature& onc_signature,
const FieldTranslationEntry* field_translation_table,
const NetworkState* network_state)
: shill_dictionary_(&shill_dictionary),
onc_source_(onc_source),
onc_signature_(&onc_signature),
field_translation_table_(field_translation_table),
network_state_(network_state) {}
// Translates the associated Shill dictionary and creates an ONC object of the
// given signature.
std::unique_ptr<base::DictionaryValue> CreateTranslatedONCObject();
private:
void TranslateEthernet();
void TranslateOpenVPN();
void TranslateIPsec();
void TranslateL2TP();
void TranslateThirdPartyVPN();
void TranslateVPN();
void TranslateWiFiWithState();
void TranslateCellularWithState();
void TranslateCellularDevice();
void TranslateNetworkWithState();
void TranslateIPConfig();
void TranslateSavedOrStaticIPConfig();
void TranslateSavedIPConfig();
void TranslateStaticIPConfig();
void TranslateEap();
// Creates an ONC object from |dictionary| according to the signature
// associated to |onc_field_name| and adds it to |onc_object_| at
// |onc_field_name|.
void TranslateAndAddNestedObject(const std::string& onc_field_name,
const base::Value& dictionary);
// Creates an ONC object from |shill_dictionary_| according to the signature
// associated to |onc_field_name| and adds it to |onc_object_| at
// |onc_field_name|.
void TranslateAndAddNestedObject(const std::string& onc_field_name);
// Sets |onc_field_name| in dictionary |onc_dictionary_name| in |onc_object_|
// to |value| if the dictionary exists.
void SetNestedOncValue(const std::string& onc_dictionary_name,
const std::string& onc_field_name,
const base::Value& value);
// Translates a list of nested objects and adds the list to |onc_object_| at
// |onc_field_name|. If there are errors while parsing individual objects or
// if the resulting list contains no entries, the result will not be added to
// |onc_object_|.
void TranslateAndAddListOfObjects(const std::string& onc_field_name,
const base::Value& list);
// Applies function CopyProperty to each field of |value_signature| and its
// base signatures.
void CopyPropertiesAccordingToSignature(
const OncValueSignature* value_signature);
// Applies function CopyProperty to each field of |onc_signature_| and its
// base signatures.
void CopyPropertiesAccordingToSignature();
// If |shill_property_name| is defined in |field_signature|, copies this
// entry from |shill_dictionary_| to |onc_object_| if it exists.
void CopyProperty(const OncFieldSignature* field_signature);
// If existent, translates the entry at |shill_property_name| in
// |shill_dictionary_| using |table|. It is an error if no matching table
// entry is found. Writes the result as entry at |onc_field_name| in
// |onc_object_|.
void TranslateWithTableAndSet(const std::string& shill_property_name,
const StringTranslationEntry table[],
const std::string& onc_field_name);
// Returns the name of the Shill service provided in |shill_dictionary_|
// for debugging.
std::string GetName();
const base::Value* shill_dictionary_;
::onc::ONCSource onc_source_;
const OncValueSignature* onc_signature_;
const FieldTranslationEntry* field_translation_table_;
std::unique_ptr<base::Value> onc_object_;
const NetworkState* network_state_;
DISALLOW_COPY_AND_ASSIGN(ShillToONCTranslator);
};
std::unique_ptr<base::DictionaryValue>
ShillToONCTranslator::CreateTranslatedONCObject() {
onc_object_.reset(new base::Value(base::Value::Type::DICTIONARY));
if (onc_signature_ == &kNetworkWithStateSignature) {
TranslateNetworkWithState();
} else if (onc_signature_ == &kEthernetSignature) {
TranslateEthernet();
} else if (onc_signature_ == &kVPNSignature) {
TranslateVPN();
} else if (onc_signature_ == &kOpenVPNSignature) {
TranslateOpenVPN();
} else if (onc_signature_ == &kIPsecSignature) {
TranslateIPsec();
} else if (onc_signature_ == &kL2TPSignature) {
TranslateL2TP();
} else if (onc_signature_ == &kThirdPartyVPNSignature) {
TranslateThirdPartyVPN();
} else if (onc_signature_ == &kWiFiWithStateSignature) {
TranslateWiFiWithState();
} else if (onc_signature_ == &kCellularWithStateSignature) {
if (field_translation_table_ == kCellularDeviceTable)
TranslateCellularDevice();
else
TranslateCellularWithState();
} else if (onc_signature_ == &kIPConfigSignature) {
TranslateIPConfig();
} else if (onc_signature_ == &kSavedIPConfigSignature) {
TranslateSavedIPConfig();
} else if (onc_signature_ == &kStaticIPConfigSignature) {
TranslateStaticIPConfig();
} else if (onc_signature_ == &kEAPSignature) {
TranslateEap();
} else {
CopyPropertiesAccordingToSignature();
}
return base::DictionaryValue::From(std::move(onc_object_));
}
void ShillToONCTranslator::TranslateEthernet() {
const std::string* shill_network_type =
shill_dictionary_->FindStringKey(shill::kTypeProperty);
const char* onc_auth = ::onc::ethernet::kAuthenticationNone;
if (shill_network_type && *shill_network_type == shill::kTypeEthernetEap)
onc_auth = ::onc::ethernet::k8021X;
onc_object_->SetKey(::onc::ethernet::kAuthentication, base::Value(onc_auth));
if (shill_network_type && *shill_network_type == shill::kTypeEthernetEap)
TranslateAndAddNestedObject(::onc::ethernet::kEAP);
}
void ShillToONCTranslator::TranslateOpenVPN() {
if (shill_dictionary_->FindKey(shill::kOpenVPNVerifyX509NameProperty))
TranslateAndAddNestedObject(::onc::openvpn::kVerifyX509);
// Shill supports only one RemoteCertKU but ONC requires a list. If existing,
// wraps the value into a list.
const std::string* certKU =
shill_dictionary_->FindStringKey(shill::kOpenVPNRemoteCertKUProperty);
if (certKU) {
base::Value certKUs(base::Value::Type::LIST);
certKUs.Append(base::Value(*certKU));
onc_object_->SetKey(::onc::openvpn::kRemoteCertKU, std::move(certKUs));
}
SetPKCS11Id(shill_dictionary_, shill::kOpenVPNClientCertIdProperty, "",
onc_object_.get());
TranslateWithTableAndSet(shill::kOpenVPNCompressProperty,
kOpenVpnCompressionAlgorithmTable,
::onc::openvpn::kCompressionAlgorithm);
for (const OncFieldSignature* field_signature = onc_signature_->fields;
field_signature->onc_field_name != NULL; ++field_signature) {
const std::string& onc_field_name = field_signature->onc_field_name;
if (onc_field_name == ::onc::openvpn::kRemoteCertKU ||
onc_field_name == ::onc::openvpn::kServerCAPEMs) {
CopyProperty(field_signature);
continue;
}
std::string shill_property_name;
if (!field_translation_table_ ||
!GetShillPropertyName(field_signature->onc_field_name,
field_translation_table_, &shill_property_name)) {
continue;
}
const base::Value* shill_value =
shill_dictionary_->FindKey(shill_property_name);
if (!shill_value) {
continue;
}
std::string shill_str;
if (shill_value->GetAsString(&shill_str)) {
// Shill wants all Provider/VPN fields to be strings. Translates these
// strings back to the correct ONC type.
base::Value translated = ConvertVpnStringToValue(
shill_str, field_signature->value_signature->onc_type);
if (translated.is_none()) {
NET_LOG(ERROR) << "Shill property '" << shill_property_name
<< "' with value " << *shill_value
<< " couldn't be converted to base::Value::Type "
<< field_signature->value_signature->onc_type << ": "
<< GetName();
} else {
onc_object_->SetKey(onc_field_name, std::move(translated));
}
} else {
NET_LOG(ERROR) << "Shill property '" << shill_property_name
<< "' has value " << *shill_value
<< ", but expected a string: " << GetName();
}
}
}
void ShillToONCTranslator::TranslateIPsec() {
CopyPropertiesAccordingToSignature();
if (shill_dictionary_->FindKey(shill::kL2tpIpsecXauthUserProperty))
TranslateAndAddNestedObject(::onc::ipsec::kXAUTH);
std::string authentication_type;
if (SetPKCS11Id(shill_dictionary_, shill::kL2tpIpsecClientCertIdProperty,
shill::kL2tpIpsecClientCertSlotProperty, onc_object_.get())) {
authentication_type = ::onc::ipsec::kCert;
} else {
authentication_type = ::onc::ipsec::kPSK;
}
onc_object_->SetKey(::onc::ipsec::kAuthenticationType,
base::Value(authentication_type));
}
void ShillToONCTranslator::TranslateL2TP() {
CopyPropertiesAccordingToSignature();
const base::Value* lcp_echo_disabled =
shill_dictionary_->FindKey(shill::kL2tpIpsecLcpEchoDisabledProperty);
if (lcp_echo_disabled && lcp_echo_disabled->is_string()) {
base::Value lcp_echo_disabled_value = ConvertVpnStringToValue(
lcp_echo_disabled->GetString(), base::Value::Type::BOOLEAN);
onc_object_->SetKey(::onc::l2tp::kLcpEchoDisabled,
std::move(lcp_echo_disabled_value));
}
}
void ShillToONCTranslator::TranslateThirdPartyVPN() {
CopyPropertiesAccordingToSignature();
// For third-party VPNs, |shill::kProviderHostProperty| is used to store the
// provider's extension ID.
const std::string* shill_extension_id =
shill_dictionary_->FindStringKey(shill::kHostProperty);
onc_object_->SetKey(
::onc::third_party_vpn::kExtensionID,
base::Value(shill_extension_id ? *shill_extension_id : std::string()));
}
void ShillToONCTranslator::TranslateVPN() {
CopyPropertiesAccordingToSignature();
// Parse Shill Provider dictionary. Note, this may not exist, e.g. if we are
// just translating network state in network_util::TranslateNetworkStateToONC.
const base::Value* provider =
shill_dictionary_->FindDictKey(shill::kProviderProperty);
if (!provider) {
return;
}
std::string shill_provider_type =
FindStringKeyOrEmpty(provider, shill::kTypeProperty);
std::string onc_provider_type;
if (!TranslateStringToONC(kVPNTypeTable, shill_provider_type,
&onc_provider_type)) {
return;
}
onc_object_->SetKey(::onc::vpn::kType, base::Value(onc_provider_type));
const std::string* shill_provider_host =
provider->FindStringKey(shill::kHostProperty);
if (onc_provider_type != ::onc::vpn::kThirdPartyVpn && shill_provider_host) {
onc_object_->SetKey(::onc::vpn::kHost, base::Value(*shill_provider_host));
}
// Translate the nested dictionary.
std::string provider_type_dictionary;
if (onc_provider_type == ::onc::vpn::kTypeL2TP_IPsec) {
TranslateAndAddNestedObject(::onc::vpn::kIPsec, *provider);
TranslateAndAddNestedObject(::onc::vpn::kL2TP, *provider);
provider_type_dictionary = ::onc::vpn::kIPsec;
} else {
TranslateAndAddNestedObject(onc_provider_type, *provider);
provider_type_dictionary = onc_provider_type;
}
base::Optional<bool> save_credentials =
shill_dictionary_->FindBoolKey(shill::kSaveCredentialsProperty);
if (onc_provider_type != ::onc::vpn::kThirdPartyVpn &&
onc_provider_type != ::onc::vpn::kArcVpn && save_credentials) {
SetNestedOncValue(provider_type_dictionary, ::onc::vpn::kSaveCredentials,
base::Value(*save_credentials));
}
}
void ShillToONCTranslator::TranslateWiFiWithState() {
const std::string* shill_security =
shill_dictionary_->FindStringKey(shill::kSecurityClassProperty);
const std::string* shill_key_mgmt =
shill_dictionary_->FindStringKey(shill::kEapKeyMgmtProperty);
if (shill_security && *shill_security == shill::kSecurityWep &&
shill_key_mgmt && *shill_key_mgmt == shill::kKeyManagementIEEE8021X) {
onc_object_->SetKey(::onc::wifi::kSecurity,
base::Value(::onc::wifi::kWEP_8021X));
} else {
TranslateWithTableAndSet(shill::kSecurityClassProperty, kWiFiSecurityTable,
::onc::wifi::kSecurity);
}
bool unknown_encoding = true;
std::string ssid = shill_property_util::GetSSIDFromProperties(
*shill_dictionary_, false /* verbose_logging */, &unknown_encoding);
if (!unknown_encoding && !ssid.empty())
onc_object_->SetKey(::onc::wifi::kSSID, base::Value(ssid));
base::Optional<bool> link_monitor_disable =
shill_dictionary_->FindBoolKey(shill::kLinkMonitorDisableProperty);
if (link_monitor_disable) {
onc_object_->SetKey(::onc::wifi::kAllowGatewayARPPolling,
base::Value(!*link_monitor_disable));
}
CopyPropertiesAccordingToSignature();
TranslateAndAddNestedObject(::onc::wifi::kEAP);
}
void ShillToONCTranslator::TranslateCellularWithState() {
CopyPropertiesAccordingToSignature();
TranslateWithTableAndSet(shill::kActivationStateProperty,
kActivationStateTable,
::onc::cellular::kActivationState);
TranslateWithTableAndSet(shill::kNetworkTechnologyProperty,
kNetworkTechnologyTable,
::onc::cellular::kNetworkTechnology);
const base::Value* dictionary =
shill_dictionary_->FindDictKey(shill::kServingOperatorProperty);
if (dictionary) {
TranslateAndAddNestedObject(::onc::cellular::kServingOperator, *dictionary);
}
dictionary = shill_dictionary_->FindDictKey(shill::kCellularApnProperty);
if (dictionary) {
TranslateAndAddNestedObject(::onc::cellular::kAPN, *dictionary);
}
dictionary =
shill_dictionary_->FindDictKey(shill::kCellularLastGoodApnProperty);
if (dictionary) {
TranslateAndAddNestedObject(::onc::cellular::kLastGoodAPN, *dictionary);
}
dictionary = shill_dictionary_->FindDictKey(shill::kPaymentPortalProperty);
if (dictionary) {
TranslateAndAddNestedObject(::onc::cellular::kPaymentPortal, *dictionary);
}
const base::Value* device_dictionary =
shill_dictionary_->FindDictKey(shill::kDeviceProperty);
bool requires_roaming = false;
bool scanning = false;
if (device_dictionary) {
// Merge the Device dictionary with this one (Cellular) using the
// CellularDevice signature.
ShillToONCTranslator nested_translator(
*device_dictionary, onc_source_, kCellularWithStateSignature,
kCellularDeviceTable, network_state_);
std::unique_ptr<base::DictionaryValue> nested_object =
nested_translator.CreateTranslatedONCObject();
onc_object_->MergeDictionary(nested_object.get());
// Copy ICCID and IMSI from the Device dictionary only if not provied in the
// Service properties.
if (!onc_object_->FindKey(::onc::cellular::kICCID)) {
const base::Value* iccid =
device_dictionary->FindKey(shill::kIccidProperty);
if (iccid)
onc_object_->SetKey(::onc::cellular::kICCID, iccid->Clone());
}
if (!onc_object_->FindKey(::onc::cellular::kIMSI)) {
const base::Value* imsi =
device_dictionary->FindKey(shill::kImsiProperty);
if (imsi)
onc_object_->SetKey(::onc::cellular::kIMSI, imsi->Clone());
}
// Get requires_roaming and scanning from the Device dictionary.
requires_roaming =
device_dictionary->FindBoolKey(shill::kProviderRequiresRoamingProperty)
.value_or(false);
scanning = device_dictionary->FindBoolKey(shill::kScanningProperty)
.value_or(false);
}
if (requires_roaming) {
onc_object_->SetKey(::onc::cellular::kRoamingState,
base::Value(::onc::cellular::kRoamingRequired));
} else {
TranslateWithTableAndSet(shill::kRoamingStateProperty, kRoamingStateTable,
::onc::cellular::kRoamingState);
}
onc_object_->SetKey(::onc::cellular::kScanning, base::Value(scanning));
}
void ShillToONCTranslator::TranslateCellularDevice() {
CopyPropertiesAccordingToSignature();
const base::Value* shill_sim_lock_status =
shill_dictionary_->FindDictKey(shill::kSIMLockStatusProperty);
if (shill_sim_lock_status) {
TranslateAndAddNestedObject(::onc::cellular::kSIMLockStatus,
*shill_sim_lock_status);
}
const base::Value* shill_home_provider =
shill_dictionary_->FindDictKey(shill::kHomeProviderProperty);
if (shill_home_provider) {
TranslateAndAddNestedObject(::onc::cellular::kHomeProvider,
*shill_home_provider);
}
const base::Value* shill_apns =
shill_dictionary_->FindListKey(shill::kCellularApnListProperty);
if (shill_apns) {
TranslateAndAddListOfObjects(::onc::cellular::kAPNList, *shill_apns);
}
const base::Value* shill_found_networks =
shill_dictionary_->FindListKey(shill::kFoundNetworksProperty);
if (shill_found_networks) {
TranslateAndAddListOfObjects(::onc::cellular::kFoundNetworks,
*shill_found_networks);
}
}
void ShillToONCTranslator::TranslateNetworkWithState() {
CopyPropertiesAccordingToSignature();
std::string shill_network_type =
FindStringKeyOrEmpty(shill_dictionary_, shill::kTypeProperty);
std::string onc_network_type = ::onc::network_type::kEthernet;
if (shill_network_type != shill::kTypeEthernet &&
shill_network_type != shill::kTypeEthernetEap) {
TranslateStringToONC(kNetworkTypeTable, shill_network_type,
&onc_network_type);
}
// Translate nested Cellular, WiFi, etc. properties.
if (!onc_network_type.empty()) {
onc_object_->SetKey(::onc::network_config::kType,
base::Value(onc_network_type));
TranslateAndAddNestedObject(onc_network_type);
}
// Since Name is a read only field in Shill unless it's a VPN, it is copied
// here, but not when going the other direction (if it's not a VPN).
std::string name =
FindStringKeyOrEmpty(shill_dictionary_, shill::kNameProperty);
onc_object_->SetKey(::onc::network_config::kName, base::Value(name));
// Limit ONC state to "NotConnected", "Connected", or "Connecting".
const std::string* state =
shill_dictionary_->FindStringKey(shill::kStateProperty);
if (state) {
std::string onc_state = ::onc::connection_state::kNotConnected;
if (NetworkState::StateIsConnected(*state)) {
onc_state = ::onc::connection_state::kConnected;
} else if (NetworkState::StateIsConnecting(*state)) {
onc_state = ::onc::connection_state::kConnecting;
}
onc_object_->SetKey(::onc::network_config::kConnectionState,
base::Value(onc_state));
// Only set 'RestrictedConnectivity' if captive portal state is true.
if (NetworkState::NetworkStateIsCaptivePortal(*shill_dictionary_)) {
onc_object_->SetKey(::onc::network_config::kRestrictedConnectivity,
base::Value(true));
}
}
// Non-visible networks (with null network_state_) do not set ErrorState.
if (network_state_) {
if (!network_state_->GetError().empty()) {
onc_object_->SetKey(::onc::network_config::kErrorState,
base::Value(network_state_->GetError()));
}
}
const std::string* profile_path =
shill_dictionary_->FindStringKey(shill::kProfileProperty);
if (onc_source_ != ::onc::ONC_SOURCE_UNKNOWN && profile_path) {
std::string source;
if (onc_source_ == ::onc::ONC_SOURCE_DEVICE_POLICY)
source = ::onc::network_config::kSourceDevicePolicy;
else if (onc_source_ == ::onc::ONC_SOURCE_USER_POLICY)
source = ::onc::network_config::kSourceUserPolicy;
else if (*profile_path == NetworkProfileHandler::GetSharedProfilePath())
source = ::onc::network_config::kSourceDevice;
else if (!profile_path->empty())
source = ::onc::network_config::kSourceUser;
else
source = ::onc::network_config::kSourceNone;
onc_object_->SetKey(::onc::network_config::kSource, base::Value(source));
}
// Use a human-readable aa:bb format for any hardware MAC address. Note:
// this property is provided by the caller but is not part of the Shill
// Service properties (it is copied from the Device properties).
const std::string* address =
shill_dictionary_->FindStringKey(shill::kAddressProperty);
if (address) {
onc_object_->SetKey(
::onc::network_config::kMacAddress,
base::Value(network_util::FormattedMacAddress(*address)));
}
// Shill's Service has an IPConfig property (note the singular), not an
// IPConfigs property. However, we require the caller of the translation to
// patch the Shill dictionary before passing it to the translator.
const base::Value* shill_ipconfigs =
shill_dictionary_->FindListKey(shill::kIPConfigsProperty);
if (shill_ipconfigs) {
TranslateAndAddListOfObjects(::onc::network_config::kIPConfigs,
*shill_ipconfigs);
}
const base::Value* saved_ipconfig =
shill_dictionary_->FindDictKey(shill::kSavedIPConfigProperty);
if (saved_ipconfig) {
TranslateAndAddNestedObject(::onc::network_config::kSavedIPConfig,
*saved_ipconfig);
}
// Translate the StaticIPConfig object and set the IP config types.
const base::Value* static_ipconfig =
shill_dictionary_->FindDictKey(shill::kStaticIPConfigProperty);
if (static_ipconfig) {
const std::string* ip_address =
static_ipconfig->FindStringKey(shill::kAddressProperty);
if (ip_address && !ip_address->empty()) {
onc_object_->SetKey(
::onc::network_config::kIPAddressConfigType,
base::Value(::onc::network_config::kIPConfigTypeStatic));
}
const base::Value* name_servers =
static_ipconfig->FindListKey(shill::kNameServersProperty);
if (name_servers && !name_servers->GetList().empty()) {
onc_object_->SetKey(
::onc::network_config::kNameServersConfigType,
base::Value(::onc::network_config::kIPConfigTypeStatic));
}
if ((ip_address && !ip_address->empty()) ||
(name_servers && !name_servers->GetList().empty())) {
TranslateAndAddNestedObject(::onc::network_config::kStaticIPConfig,
*static_ipconfig);
}
}
const std::string* proxy_config_str =
shill_dictionary_->FindStringKey(shill::kProxyConfigProperty);
if (proxy_config_str && !proxy_config_str->empty()) {
base::Value proxy_config_value = ReadDictionaryFromJson(*proxy_config_str);
if (!proxy_config_value.is_none()) {
base::Value proxy_settings =
ConvertProxyConfigToOncProxySettings(proxy_config_value);
if (!proxy_settings.is_none()) {
onc_object_->SetKey(::onc::network_config::kProxySettings,
std::move(proxy_settings));
}
}
}
}
void ShillToONCTranslator::TranslateIPConfig() {
CopyPropertiesAccordingToSignature();
std::string shill_ip_method =
FindStringKeyOrEmpty(shill_dictionary_, shill::kMethodProperty);
std::string type;
if (shill_ip_method == shill::kTypeIPv4 ||
shill_ip_method == shill::kTypeDHCP) {
type = ::onc::ipconfig::kIPv4;
} else if (shill_ip_method == shill::kTypeIPv6 ||
shill_ip_method == shill::kTypeDHCP6) {
type = ::onc::ipconfig::kIPv6;
} else {
return; // Ignore unhandled IPConfig types, e.g. bootp, zeroconf, ppp
}
onc_object_->SetKey(::onc::ipconfig::kType, base::Value(type));
}
void ShillToONCTranslator::TranslateSavedOrStaticIPConfig() {
CopyPropertiesAccordingToSignature();
// Static and Saved IPConfig in Shill are always of type IPv4. Set this type
// in ONC, but not if the object would be empty except the type.
if (!onc_object_->DictEmpty()) {
onc_object_->SetKey(::onc::ipconfig::kType,
base::Value(::onc::ipconfig::kIPv4));
}
}
void ShillToONCTranslator::TranslateSavedIPConfig() {
TranslateSavedOrStaticIPConfig();
}
void ShillToONCTranslator::TranslateStaticIPConfig() {
TranslateSavedOrStaticIPConfig();
}
void ShillToONCTranslator::TranslateEap() {
CopyPropertiesAccordingToSignature();
// Translate EAP Outer and Inner values if EAP.EAP exists and is not empty.
const std::string* shill_eap_method =
shill_dictionary_->FindStringKey(shill::kEapMethodProperty);
if (shill_eap_method && !shill_eap_method->empty()) {
TranslateWithTableAndSet(shill::kEapMethodProperty, kEAPOuterTable,
::onc::eap::kOuter);
const std::string* shill_phase2_auth =
shill_dictionary_->FindStringKey(shill::kEapPhase2AuthProperty);
if (shill_phase2_auth && !shill_phase2_auth->empty()) {
TranslateWithTableAndSet(shill::kEapPhase2AuthProperty,
kEAP_TTLS_InnerTable, ::onc::eap::kInner);
}
}
const std::string* shill_cert_id =
shill_dictionary_->FindStringKey(shill::kEapCertIdProperty);
if (shill_cert_id) {
onc_object_->SetKey(::onc::client_cert::kClientCertType,
base::Value(::onc::client_cert::kPKCS11Id));
// Note: shill::kEapCertIdProperty is already in the format slot:key_id.
// Note: shill::kEapKeyIdProperty has the same value as
// shill::kEapCertIdProperty and is ignored.
onc_object_->SetKey(::onc::client_cert::kClientCertPKCS11Id,
base::Value(*shill_cert_id));
}
bool use_login_password =
shill_dictionary_->FindBoolKey(shill::kEapUseLoginPasswordProperty)
.value_or(false);
if (use_login_password) {
onc_object_->SetKey(
::onc::eap::kPassword,
base::Value(::onc::substitutes::kPasswordPlaceholderVerbatim));
}
}
void ShillToONCTranslator::TranslateAndAddNestedObject(
const std::string& onc_field_name) {
TranslateAndAddNestedObject(onc_field_name, *shill_dictionary_);
}
void ShillToONCTranslator::TranslateAndAddNestedObject(
const std::string& onc_field_name,
const base::Value& dictionary) {
const OncFieldSignature* field_signature =
GetFieldSignature(*onc_signature_, onc_field_name);
if (!field_signature) {
NET_LOG(ERROR) << "Unable to find signature for field: " << onc_field_name;
return;
}
ShillToONCTranslator nested_translator(dictionary, onc_source_,
*field_signature->value_signature,
network_state_);
std::unique_ptr<base::DictionaryValue> nested_object =
nested_translator.CreateTranslatedONCObject();
if (nested_object->empty())
return;
onc_object_->SetKey(onc_field_name, std::move(*nested_object));
}
void ShillToONCTranslator::SetNestedOncValue(
const std::string& onc_dictionary_name,
const std::string& onc_field_name,
const base::Value& value) {
onc_object_->SetPath({onc_dictionary_name, onc_field_name}, value.Clone());
}
void ShillToONCTranslator::TranslateAndAddListOfObjects(
const std::string& onc_field_name,
const base::Value& list) {
const OncFieldSignature* field_signature =
GetFieldSignature(*onc_signature_, onc_field_name);
if (field_signature->value_signature->onc_type != base::Value::Type::LIST) {
NET_LOG(ERROR) << "ONC Field name: '" << onc_field_name << "' has type '"
<< field_signature->value_signature->onc_type
<< "', expected: base::Value::Type::LIST: " << GetName();
return;
}
DCHECK(field_signature->value_signature->onc_array_entry_signature);
base::Value result(base::Value::Type::LIST);
for (const auto& it : list.GetList()) {
if (!it.is_dict())
continue;
ShillToONCTranslator nested_translator(
it, onc_source_,
*field_signature->value_signature->onc_array_entry_signature,
network_state_);
std::unique_ptr<base::DictionaryValue> nested_object =
nested_translator.CreateTranslatedONCObject();
// If the nested object couldn't be parsed, simply omit it.
if (nested_object->empty())
continue;
result.Append(std::move(*nested_object));
}
// If there are no entries in the list, there is no need to expose this field.
if (result.GetList().empty())
return;
onc_object_->SetKey(onc_field_name, std::move(result));
}
void ShillToONCTranslator::CopyPropertiesAccordingToSignature() {
CopyPropertiesAccordingToSignature(onc_signature_);
}
void ShillToONCTranslator::CopyPropertiesAccordingToSignature(
const OncValueSignature* value_signature) {
if (value_signature->base_signature)
CopyPropertiesAccordingToSignature(value_signature->base_signature);
if (!value_signature->fields)
return;
for (const OncFieldSignature* field_signature = value_signature->fields;
field_signature->onc_field_name != NULL; ++field_signature) {
CopyProperty(field_signature);
}
}
void ShillToONCTranslator::CopyProperty(
const OncFieldSignature* field_signature) {
std::string shill_property_name;
if (!field_translation_table_ ||
!GetShillPropertyName(field_signature->onc_field_name,
field_translation_table_, &shill_property_name)) {
return;
}
const base::Value* shill_value =
shill_dictionary_->FindKey(shill_property_name);
if (!shill_value) {
return;
}
if (shill_value->type() != field_signature->value_signature->onc_type) {
NET_LOG(ERROR) << "Shill property '" << shill_property_name
<< "' with value " << *shill_value
<< " has base::Value::Type " << shill_value->type()
<< " but ONC field '" << field_signature->onc_field_name
<< "' requires type "
<< field_signature->value_signature->onc_type << ": "
<< GetName();
return;
}
onc_object_->SetKey(field_signature->onc_field_name, shill_value->Clone());
}
void ShillToONCTranslator::TranslateWithTableAndSet(
const std::string& shill_property_name,
const StringTranslationEntry table[],
const std::string& onc_field_name) {
const std::string* shill_value =
shill_dictionary_->FindStringKey(shill_property_name);
if (!shill_value || shill_value->empty()) {
return;
}
std::string onc_value;
if (TranslateStringToONC(table, *shill_value, &onc_value)) {
onc_object_->SetKey(onc_field_name, base::Value(onc_value));
return;
}
NET_LOG(ERROR) << "Shill property '" << shill_property_name << "' with value "
<< *shill_value
<< " couldn't be translated to ONC: " << GetName();
}
std::string ShillToONCTranslator::GetName() {
DCHECK(shill_dictionary_);
const std::string* name =
shill_dictionary_->FindStringKey(shill::kNameProperty);
return name ? *name : std::string();
}
} // namespace
std::unique_ptr<base::DictionaryValue> TranslateShillServiceToONCPart(
const base::Value& shill_dictionary,
::onc::ONCSource onc_source,
const OncValueSignature* onc_signature,
const NetworkState* network_state) {
CHECK(onc_signature != NULL);
ShillToONCTranslator translator(shill_dictionary, onc_source, *onc_signature,
network_state);
return translator.CreateTranslatedONCObject();
}
} // namespace onc
} // namespace chromeos