blob: 1fca613572075af8a8838103dec1967e8751f07e [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.
// The implementation of TranslateONCObjectToShill is structured in two parts:
// - The recursion through the existing ONC hierarchy
// see TranslateONCHierarchy
// - The local translation of an object depending on the associated signature
// see LocalTranslator::TranslateFields
#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/memory/ptr_util.h"
#include "base/strings/string_util.h"
#include "base/values.h"
#include "chromeos/network/client_cert_util.h"
#include "chromeos/network/network_event_log.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/onc/onc_constants.h"
#include "third_party/cros_system_api/dbus/service_constants.h"
namespace chromeos {
namespace onc {
namespace {
std::unique_ptr<base::Value> ConvertValueToString(const base::Value& value) {
std::string str;
if (!value.GetAsString(&str))
base::JSONWriter::Write(value, &str);
return std::make_unique<base::Value>(str);
}
// Sets any client cert properties when ClientCertType is PKCS11Id.
void SetClientCertProperties(client_cert::ConfigType config_type,
const base::DictionaryValue* onc_object,
base::DictionaryValue* shill_dictionary) {
std::string cert_type;
onc_object->GetStringWithoutPathExpansion(::onc::client_cert::kClientCertType,
&cert_type);
if (cert_type != ::onc::client_cert::kPKCS11Id)
return;
std::string pkcs11_id;
onc_object->GetStringWithoutPathExpansion(
::onc::client_cert::kClientCertPKCS11Id, &pkcs11_id);
if (pkcs11_id.empty()) {
// If the cert type is PKCS11 but the pkcs11 id is empty, set empty
// properties to indicate 'no certificate'.
client_cert::SetEmptyShillProperties(config_type, shill_dictionary);
return;
}
int slot_id;
std::string cert_id =
client_cert::GetPkcs11AndSlotIdFromEapCertId(pkcs11_id, &slot_id);
client_cert::SetShillProperties(config_type, slot_id, cert_id,
shill_dictionary);
}
// This class is responsible to translate the local fields of the given
// |onc_object| according to |onc_signature| into |shill_dictionary|. This
// translation should consider (if possible) only fields of this ONC object and
// not nested objects because recursion is handled by the calling function
// TranslateONCHierarchy.
class LocalTranslator {
public:
LocalTranslator(const OncValueSignature& onc_signature,
const base::DictionaryValue& onc_object,
base::DictionaryValue* shill_dictionary)
: onc_signature_(&onc_signature),
onc_object_(&onc_object),
shill_dictionary_(shill_dictionary) {
field_translation_table_ = GetFieldTranslationTable(onc_signature);
}
void TranslateFields();
private:
void TranslateEthernet();
void TranslateOpenVPN();
void TranslateIPsec();
void TranslateL2TP();
void TranslateVPN();
void TranslateWiFi();
void TranslateEAP();
void TranslateNetworkConfiguration();
// Copies all entries from |onc_object_| to |shill_dictionary_| for which a
// translation (shill_property_name) is defined by the translation table for
// |onc_signature_|.
void CopyFieldsAccordingToSignature();
// If existent, copies the value of field |onc_field_name| from |onc_object_|
// to the property |shill_property_name| in |shill_dictionary_|.
void CopyFieldFromONCToShill(const std::string& onc_field_name,
const std::string& shill_property_name);
// Adds |value| to |shill_dictionary| at the field shill_property_name given
// by the associated signature. Takes ownership of |value|. Does nothing if
// |value| is NULL or the property name cannot be read from the signature.
void AddValueAccordingToSignature(const std::string& onc_field_name,
std::unique_ptr<base::Value> value);
// Translates the value |onc_value| using |table|. It is an error if no
// matching table entry is found. Writes the result as entry at
// |shill_property_name| in |shill_dictionary_|.
void TranslateWithTableAndSet(const std::string& onc_value,
const StringTranslationEntry table[],
const std::string& shill_property_name);
const OncValueSignature* onc_signature_;
const FieldTranslationEntry* field_translation_table_;
const base::DictionaryValue* onc_object_;
base::DictionaryValue* shill_dictionary_;
DISALLOW_COPY_AND_ASSIGN(LocalTranslator);
};
void LocalTranslator::TranslateFields() {
if (onc_signature_ == &kNetworkConfigurationSignature)
TranslateNetworkConfiguration();
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_ == &kWiFiSignature)
TranslateWiFi();
else if (onc_signature_ == &kEAPSignature)
TranslateEAP();
else
CopyFieldsAccordingToSignature();
}
void LocalTranslator::TranslateEthernet() {
std::string authentication;
onc_object_->GetStringWithoutPathExpansion(::onc::ethernet::kAuthentication,
&authentication);
const char* shill_type = shill::kTypeEthernet;
if (authentication == ::onc::ethernet::k8021X)
shill_type = shill::kTypeEthernetEap;
shill_dictionary_->SetKey(shill::kTypeProperty, base::Value(shill_type));
CopyFieldsAccordingToSignature();
}
void LocalTranslator::TranslateOpenVPN() {
// SaveCredentials needs special handling when translating from Shill -> ONC
// so handle it explicitly here.
CopyFieldFromONCToShill(::onc::vpn::kSaveCredentials,
shill::kSaveCredentialsProperty);
std::string user_auth_type;
onc_object_->GetStringWithoutPathExpansion(
::onc::openvpn::kUserAuthenticationType, &user_auth_type);
// The default behavior (if user_auth_type is empty) is to use both password
// and OTP in a static challenge and only the password otherwise. As long as
// Shill doe not know about the exact user authentication type, this is
// identical to kPasswordAndOTP.
if (user_auth_type.empty())
user_auth_type = ::onc::openvpn_user_auth_type::kPasswordAndOTP;
NET_LOG(DEBUG) << "USER AUTH TYPE: " << user_auth_type;
if (user_auth_type == ::onc::openvpn_user_auth_type::kPassword ||
user_auth_type == ::onc::openvpn_user_auth_type::kPasswordAndOTP) {
CopyFieldFromONCToShill(::onc::openvpn::kPassword,
shill::kOpenVPNPasswordProperty);
}
if (user_auth_type == ::onc::openvpn_user_auth_type::kPasswordAndOTP)
CopyFieldFromONCToShill(::onc::openvpn::kOTP, shill::kOpenVPNOTPProperty);
if (user_auth_type == ::onc::openvpn_user_auth_type::kOTP)
CopyFieldFromONCToShill(::onc::openvpn::kOTP, shill::kOpenVPNTokenProperty);
// Shill supports only one RemoteCertKU but ONC specifies a list, so copy only
// the first entry if the lists exists. Otherwise copy an empty string to
// reset any previous configuration.
const base::ListValue* cert_kus = nullptr;
std::string cert_ku;
if (onc_object_->GetListWithoutPathExpansion(::onc::openvpn::kRemoteCertKU,
&cert_kus)) {
cert_kus->GetString(0, &cert_ku);
}
shill_dictionary_->SetKey(shill::kOpenVPNRemoteCertKUProperty,
base::Value(cert_ku));
SetClientCertProperties(client_cert::CONFIG_TYPE_OPENVPN, onc_object_,
shill_dictionary_);
// Modified CopyFieldsAccordingToSignature to handle RemoteCertKU and
// ServerCAPEMs and handle all other fields as strings.
for (base::DictionaryValue::Iterator it(*onc_object_); !it.IsAtEnd();
it.Advance()) {
std::string key = it.key();
std::unique_ptr<base::Value> translated;
if (key == ::onc::openvpn::kRemoteCertKU ||
key == ::onc::openvpn::kServerCAPEMs ||
key == ::onc::openvpn::kExtraHosts) {
translated.reset(it.value().DeepCopy());
} else {
// Shill wants all Provider/VPN fields to be strings.
translated = ConvertValueToString(it.value());
}
AddValueAccordingToSignature(key, std::move(translated));
}
}
void LocalTranslator::TranslateIPsec() {
SetClientCertProperties(client_cert::CONFIG_TYPE_IPSEC, onc_object_,
shill_dictionary_);
// SaveCredentials needs special handling when translating from Shill -> ONC
// so handle it explicitly here.
CopyFieldFromONCToShill(::onc::vpn::kSaveCredentials,
shill::kSaveCredentialsProperty);
CopyFieldsAccordingToSignature();
}
void LocalTranslator::TranslateL2TP() {
// SaveCredentials needs special handling when translating from Shill -> ONC
// so handle it explicitly here.
CopyFieldFromONCToShill(::onc::vpn::kSaveCredentials,
shill::kSaveCredentialsProperty);
CopyFieldsAccordingToSignature();
}
void LocalTranslator::TranslateVPN() {
std::string onc_type;
if (onc_object_->GetStringWithoutPathExpansion(::onc::vpn::kType,
&onc_type)) {
TranslateWithTableAndSet(onc_type, kVPNTypeTable,
shill::kProviderTypeProperty);
}
if (onc_type == ::onc::vpn::kThirdPartyVpn) {
// For third-party VPNs, |shill::kProviderHostProperty| is used to store the
// provider's extension ID.
const base::DictionaryValue* onc_third_party_vpn = nullptr;
onc_object_->GetDictionaryWithoutPathExpansion(::onc::vpn::kThirdPartyVpn,
&onc_third_party_vpn);
std::string onc_extension_id;
if (onc_third_party_vpn &&
onc_third_party_vpn->GetStringWithoutPathExpansion(
::onc::third_party_vpn::kExtensionID, &onc_extension_id)) {
shill_dictionary_->SetKey(shill::kProviderHostProperty,
base::Value(onc_extension_id));
}
} else {
CopyFieldFromONCToShill(::onc::vpn::kHost, shill::kProviderHostProperty);
}
CopyFieldsAccordingToSignature();
}
void LocalTranslator::TranslateWiFi() {
std::string security;
if (onc_object_->GetStringWithoutPathExpansion(::onc::wifi::kSecurity,
&security)) {
TranslateWithTableAndSet(security, kWiFiSecurityTable,
shill::kSecurityClassProperty);
if (security == ::onc::wifi::kWEP_8021X) {
shill_dictionary_->SetKey(shill::kEapKeyMgmtProperty,
base::Value(shill::kKeyManagementIEEE8021X));
}
}
// We currently only support managed and no adhoc networks.
shill_dictionary_->SetKey(shill::kModeProperty,
base::Value(shill::kModeManaged));
bool allow_gateway_arp_polling;
if (onc_object_->GetBooleanWithoutPathExpansion(
::onc::wifi::kAllowGatewayARPPolling, &allow_gateway_arp_polling)) {
shill_dictionary_->SetKey(shill::kLinkMonitorDisableProperty,
base::Value(!allow_gateway_arp_polling));
}
CopyFieldsAccordingToSignature();
}
void LocalTranslator::TranslateEAP() {
// Note: EAP.Outer may be empty for WiMAX configurations.
std::string outer;
onc_object_->GetStringWithoutPathExpansion(::onc::eap::kOuter, &outer);
if (!outer.empty())
TranslateWithTableAndSet(outer, kEAPOuterTable, shill::kEapMethodProperty);
// Translate the inner protocol only for outer tunneling protocols.
if (outer == ::onc::eap::kPEAP || outer == ::onc::eap::kEAP_TTLS) {
// In ONC the Inner protocol defaults to "Automatic".
std::string inner = ::onc::eap::kAutomatic;
// ONC's Inner == "Automatic" translates to omitting the Phase2 property in
// Shill.
onc_object_->GetStringWithoutPathExpansion(::onc::eap::kInner, &inner);
if (inner != ::onc::eap::kAutomatic) {
const StringTranslationEntry* table = outer == ::onc::eap::kPEAP
? kEAP_PEAP_InnerTable
: kEAP_TTLS_InnerTable;
TranslateWithTableAndSet(inner, table, shill::kEapPhase2AuthProperty);
}
}
SetClientCertProperties(client_cert::CONFIG_TYPE_EAP, onc_object_,
shill_dictionary_);
// Set shill::kEapUseLoginPasswordProperty according to whether or not the
// password substitution variable is set.
const base::Value* password_field =
onc_object_->FindKey(::onc::eap::kPassword);
if (password_field && password_field->GetString() ==
::onc::substitutes::kPasswordPlaceholderVerbatim) {
shill_dictionary_->SetKey(shill::kEapUseLoginPasswordProperty,
base::Value(true));
}
CopyFieldsAccordingToSignature();
}
void LocalTranslator::TranslateNetworkConfiguration() {
std::string type;
onc_object_->GetStringWithoutPathExpansion(::onc::network_config::kType,
&type);
// Note; The Ethernet type might be overridden to EthernetEap in
// TranslateEthernet if Ethernet specific properties are provided.
TranslateWithTableAndSet(type, kNetworkTypeTable, shill::kTypeProperty);
// Shill doesn't allow setting the name for non-VPN networks.
if (type == ::onc::network_type::kVPN)
CopyFieldFromONCToShill(::onc::network_config::kName, shill::kNameProperty);
std::string ip_address_config_type, name_servers_config_type;
onc_object_->GetStringWithoutPathExpansion(
::onc::network_config::kIPAddressConfigType, &ip_address_config_type);
onc_object_->GetStringWithoutPathExpansion(
::onc::network_config::kNameServersConfigType, &name_servers_config_type);
if ((ip_address_config_type == ::onc::network_config::kIPConfigTypeDHCP) ||
(name_servers_config_type == ::onc::network_config::kIPConfigTypeDHCP)) {
// If either type is set to DHCP, provide an empty dictionary to ensure
// that any unset properties are cleared. Note: if either type is specified,
// the other type defaults to DHCP if not specified.
shill_dictionary_->SetWithoutPathExpansion(
shill::kStaticIPConfigProperty,
std::make_unique<base::DictionaryValue>());
}
const base::DictionaryValue* proxy_settings = nullptr;
if (onc_object_->GetDictionaryWithoutPathExpansion(
::onc::network_config::kProxySettings, &proxy_settings)) {
base::Value proxy_config =
ConvertOncProxySettingsToProxyConfig(*proxy_settings);
std::string proxy_config_str;
base::JSONWriter::Write(proxy_config, &proxy_config_str);
shill_dictionary_->SetKey(shill::kProxyConfigProperty,
base::Value(proxy_config_str));
}
CopyFieldsAccordingToSignature();
}
void LocalTranslator::CopyFieldsAccordingToSignature() {
for (base::DictionaryValue::Iterator it(*onc_object_); !it.IsAtEnd();
it.Advance()) {
AddValueAccordingToSignature(it.key(),
base::WrapUnique(it.value().DeepCopy()));
}
}
void LocalTranslator::CopyFieldFromONCToShill(
const std::string& onc_field_name,
const std::string& shill_property_name) {
const base::Value* value = NULL;
if (!onc_object_->GetWithoutPathExpansion(onc_field_name, &value))
return;
const OncFieldSignature* field_signature =
GetFieldSignature(*onc_signature_, onc_field_name);
if (field_signature) {
base::Value::Type expected_type =
field_signature->value_signature->onc_type;
if (value->type() != expected_type) {
NET_LOG(ERROR) << "Found field " << onc_field_name << " of type "
<< value->type() << " but expected type " << expected_type;
return;
}
} else {
NET_LOG(ERROR)
<< "Attempt to translate a field that is not part of the ONC format.";
return;
}
shill_dictionary_->SetKey(shill_property_name, value->Clone());
}
void LocalTranslator::AddValueAccordingToSignature(
const std::string& onc_name,
std::unique_ptr<base::Value> value) {
if (!value || !field_translation_table_)
return;
std::string shill_property_name;
if (!GetShillPropertyName(onc_name, field_translation_table_,
&shill_property_name)) {
return;
}
shill_dictionary_->SetWithoutPathExpansion(shill_property_name,
std::move(value));
}
void LocalTranslator::TranslateWithTableAndSet(
const std::string& onc_value,
const StringTranslationEntry table[],
const std::string& shill_property_name) {
std::string shill_value;
if (TranslateStringToShill(table, onc_value, &shill_value)) {
shill_dictionary_->SetKey(shill_property_name, base::Value(shill_value));
return;
}
// As we previously validate ONC, this case should never occur. If it still
// occurs, we should check here. Otherwise the failure will only show up much
// later in Shill.
NET_LOG(ERROR) << "Value '" << onc_value
<< "' cannot be translated to Shill property: "
<< shill_property_name;
}
// Iterates recursively over |onc_object| and its |signature|. At each object
// applies the local translation using LocalTranslator::TranslateFields. The
// results are written to |shill_dictionary|.
void TranslateONCHierarchy(const OncValueSignature& signature,
const base::DictionaryValue& onc_object,
base::Value* shill_dictionary) {
const std::vector<std::string> path =
GetPathToNestedShillDictionary(signature);
const std::vector<base::StringPiece> path_pieces(path.begin(), path.end());
base::Value* target_shill_dictionary = shill_dictionary->FindPathOfType(
path_pieces, base::Value::Type::DICTIONARY);
if (!target_shill_dictionary) {
target_shill_dictionary = shill_dictionary->SetPath(
path_pieces, base::Value(base::Value::Type::DICTIONARY));
}
// Translates fields of |onc_object| and writes them to
// |target_shill_dictionary_| nested in |shill_dictionary|.
LocalTranslator translator(
signature, onc_object,
static_cast<base::DictionaryValue*>(target_shill_dictionary));
translator.TranslateFields();
// Recurse into nested objects.
for (base::DictionaryValue::Iterator it(onc_object); !it.IsAtEnd();
it.Advance()) {
const base::DictionaryValue* inner_object = NULL;
if (!it.value().GetAsDictionary(&inner_object))
continue;
const OncFieldSignature* field_signature =
GetFieldSignature(signature, it.key());
TranslateONCHierarchy(*field_signature->value_signature, *inner_object,
shill_dictionary);
}
}
} // namespace
std::unique_ptr<base::DictionaryValue> TranslateONCObjectToShill(
const OncValueSignature* onc_signature,
const base::DictionaryValue& onc_object) {
CHECK(onc_signature != NULL);
std::unique_ptr<base::DictionaryValue> shill_dictionary(
new base::DictionaryValue);
TranslateONCHierarchy(*onc_signature, onc_object, shill_dictionary.get());
return shill_dictionary;
}
} // namespace onc
} // namespace chromeos