1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 // The implementation of TranslateONCObjectToShill is structured in two parts:
6 // - The recursion through the existing ONC hierarchy
7 // see TranslateONCHierarchy
8 // - The local translation of an object depending on the associated signature
9 // see LocalTranslator::TranslateFields
11 #include "chromeos/network/onc/onc_translator.h"
15 #include "base/json/json_reader.h"
16 #include "base/json/json_writer.h"
17 #include "base/logging.h"
18 #include "base/strings/string_util.h"
19 #include "base/values.h"
20 #include "chromeos/network/onc/onc_signature.h"
21 #include "chromeos/network/onc/onc_translation_tables.h"
22 #include "chromeos/network/onc/onc_utils.h"
23 #include "chromeos/network/shill_property_util.h"
24 #include "components/onc/onc_constants.h"
25 #include "third_party/cros_system_api/dbus/service_constants.h"
32 scoped_ptr
<base::StringValue
> ConvertValueToString(const base::Value
& value
) {
34 if (!value
.GetAsString(&str
))
35 base::JSONWriter::Write(value
, &str
);
36 return make_scoped_ptr(new base::StringValue(str
));
39 // This class is responsible to translate the local fields of the given
40 // |onc_object| according to |onc_signature| into |shill_dictionary|. This
41 // translation should consider (if possible) only fields of this ONC object and
42 // not nested objects because recursion is handled by the calling function
43 // TranslateONCHierarchy.
44 class LocalTranslator
{
46 LocalTranslator(const OncValueSignature
& onc_signature
,
47 const base::DictionaryValue
& onc_object
,
48 base::DictionaryValue
* shill_dictionary
)
49 : onc_signature_(&onc_signature
),
50 onc_object_(&onc_object
),
51 shill_dictionary_(shill_dictionary
) {
52 field_translation_table_
= GetFieldTranslationTable(onc_signature
);
55 void TranslateFields();
58 void TranslateEthernet();
59 void TranslateOpenVPN();
60 void TranslateIPsec();
64 void TranslateNetworkConfiguration();
66 // Copies all entries from |onc_object_| to |shill_dictionary_| for which a
67 // translation (shill_property_name) is defined by the translation table for
69 void CopyFieldsAccordingToSignature();
71 // If existent, copies the value of field |onc_field_name| from |onc_object_|
72 // to the property |shill_property_name| in |shill_dictionary_|.
73 void CopyFieldFromONCToShill(const std::string
& onc_field_name
,
74 const std::string
& shill_property_name
);
76 // Adds |value| to |shill_dictionary| at the field shill_property_name given
77 // by the associated signature. Takes ownership of |value|. Does nothing if
78 // |value| is NULL or the property name cannot be read from the signature.
79 void AddValueAccordingToSignature(const std::string
& onc_field_name
,
80 scoped_ptr
<base::Value
> value
);
82 // Translates the value |onc_value| using |table|. It is an error if no
83 // matching table entry is found. Writes the result as entry at
84 // |shill_property_name| in |shill_dictionary_|.
85 void TranslateWithTableAndSet(const std::string
& onc_value
,
86 const StringTranslationEntry table
[],
87 const std::string
& shill_property_name
);
89 const OncValueSignature
* onc_signature_
;
90 const FieldTranslationEntry
* field_translation_table_
;
91 const base::DictionaryValue
* onc_object_
;
92 base::DictionaryValue
* shill_dictionary_
;
94 DISALLOW_COPY_AND_ASSIGN(LocalTranslator
);
97 void LocalTranslator::TranslateFields() {
98 if (onc_signature_
== &kNetworkConfigurationSignature
)
99 TranslateNetworkConfiguration();
100 else if (onc_signature_
== &kEthernetSignature
)
102 else if (onc_signature_
== &kVPNSignature
)
104 else if (onc_signature_
== &kOpenVPNSignature
)
106 else if (onc_signature_
== &kIPsecSignature
)
108 else if (onc_signature_
== &kWiFiSignature
)
110 else if (onc_signature_
== &kEAPSignature
)
113 CopyFieldsAccordingToSignature();
116 void LocalTranslator::TranslateEthernet() {
117 std::string authentication
;
118 onc_object_
->GetStringWithoutPathExpansion(::onc::ethernet::kAuthentication
,
121 const char* shill_type
= shill::kTypeEthernet
;
122 if (authentication
== ::onc::ethernet::k8021X
)
123 shill_type
= shill::kTypeEthernetEap
;
124 shill_dictionary_
->SetStringWithoutPathExpansion(shill::kTypeProperty
,
127 CopyFieldsAccordingToSignature();
130 void LocalTranslator::TranslateOpenVPN() {
131 // SaveCredentials needs special handling when translating from Shill -> ONC
132 // so handle it explicitly here.
133 CopyFieldFromONCToShill(::onc::vpn::kSaveCredentials
,
134 shill::kSaveCredentialsProperty
);
136 std::string user_auth_type
;
137 onc_object_
->GetStringWithoutPathExpansion(
138 ::onc::openvpn::kUserAuthenticationType
, &user_auth_type
);
139 // The default behavior (if user_auth_type is empty) is to use both password
140 // and OTP in a static challenge and only the password otherwise. As long as
141 // Shill doe not know about the exact user authentication type, this is
142 // identical to kPasswordAndOTP.
143 if (user_auth_type
.empty())
144 user_auth_type
= ::onc::openvpn_user_auth_type::kPasswordAndOTP
;
146 if (user_auth_type
== ::onc::openvpn_user_auth_type::kPassword
||
147 user_auth_type
== ::onc::openvpn_user_auth_type::kPasswordAndOTP
) {
148 CopyFieldFromONCToShill(::onc::openvpn::kPassword
,
149 shill::kOpenVPNPasswordProperty
);
151 if (user_auth_type
== ::onc::openvpn_user_auth_type::kPasswordAndOTP
)
152 CopyFieldFromONCToShill(::onc::openvpn::kOTP
, shill::kOpenVPNOTPProperty
);
153 if (user_auth_type
== ::onc::openvpn_user_auth_type::kOTP
)
154 CopyFieldFromONCToShill(::onc::openvpn::kOTP
, shill::kOpenVPNTokenProperty
);
156 // Shill supports only one RemoteCertKU but ONC a list.
157 // Copy only the first entry if existing.
158 const base::ListValue
* cert_kus
= NULL
;
160 if (onc_object_
->GetListWithoutPathExpansion(::onc::openvpn::kRemoteCertKU
,
162 cert_kus
->GetString(0, &cert_ku
)) {
163 shill_dictionary_
->SetStringWithoutPathExpansion(
164 shill::kOpenVPNRemoteCertKUProperty
, cert_ku
);
167 for (base::DictionaryValue::Iterator
it(*onc_object_
); !it
.IsAtEnd();
169 scoped_ptr
<base::Value
> translated
;
170 if (it
.key() == ::onc::openvpn::kRemoteCertKU
||
171 it
.key() == ::onc::openvpn::kServerCAPEMs
) {
172 translated
.reset(it
.value().DeepCopy());
174 // Shill wants all Provider/VPN fields to be strings.
175 translated
= ConvertValueToString(it
.value());
177 AddValueAccordingToSignature(it
.key(), translated
.Pass());
181 void LocalTranslator::TranslateIPsec() {
182 CopyFieldsAccordingToSignature();
184 // SaveCredentials needs special handling when translating from Shill -> ONC
185 // so handle it explicitly here.
186 CopyFieldFromONCToShill(::onc::vpn::kSaveCredentials
,
187 shill::kSaveCredentialsProperty
);
190 void LocalTranslator::TranslateVPN() {
191 std::string onc_type
;
192 if (onc_object_
->GetStringWithoutPathExpansion(::onc::vpn::kType
,
194 TranslateWithTableAndSet(onc_type
, kVPNTypeTable
,
195 shill::kProviderTypeProperty
);
197 if (onc_type
== ::onc::vpn::kThirdPartyVpn
) {
198 // For third-party VPNs, |shill::kProviderHostProperty| is used to store the
199 // provider's extension ID.
200 const base::DictionaryValue
* onc_third_party_vpn
= nullptr;
201 onc_object_
->GetDictionaryWithoutPathExpansion(::onc::vpn::kThirdPartyVpn
,
202 &onc_third_party_vpn
);
203 std::string onc_extension_id
;
204 if (onc_third_party_vpn
&&
205 onc_third_party_vpn
->GetStringWithoutPathExpansion(
206 ::onc::third_party_vpn::kExtensionID
, &onc_extension_id
)) {
207 shill_dictionary_
->SetStringWithoutPathExpansion(
208 shill::kProviderHostProperty
, onc_extension_id
);
211 CopyFieldFromONCToShill(::onc::vpn::kHost
, shill::kProviderHostProperty
);
214 CopyFieldsAccordingToSignature();
217 void LocalTranslator::TranslateWiFi() {
218 std::string security
;
219 if (onc_object_
->GetStringWithoutPathExpansion(::onc::wifi::kSecurity
,
221 TranslateWithTableAndSet(security
, kWiFiSecurityTable
,
222 shill::kSecurityClassProperty
);
225 // We currently only support managed and no adhoc networks.
226 shill_dictionary_
->SetStringWithoutPathExpansion(shill::kModeProperty
,
227 shill::kModeManaged
);
229 bool allow_gateway_arp_polling
;
230 if (onc_object_
->GetBooleanWithoutPathExpansion(
231 ::onc::wifi::kAllowGatewayARPPolling
, &allow_gateway_arp_polling
)) {
232 shill_dictionary_
->SetBooleanWithoutPathExpansion(
233 shill::kLinkMonitorDisableProperty
, !allow_gateway_arp_polling
);
236 CopyFieldsAccordingToSignature();
239 void LocalTranslator::TranslateEAP() {
241 onc_object_
->GetStringWithoutPathExpansion(::onc::eap::kOuter
, &outer
);
242 TranslateWithTableAndSet(outer
, kEAPOuterTable
, shill::kEapMethodProperty
);
244 // Translate the inner protocol only for outer tunneling protocols.
245 if (outer
== ::onc::eap::kPEAP
|| outer
== ::onc::eap::kEAP_TTLS
) {
246 // In ONC the Inner protocol defaults to "Automatic".
247 std::string inner
= ::onc::eap::kAutomatic
;
248 // ONC's Inner == "Automatic" translates to omitting the Phase2 property in
250 onc_object_
->GetStringWithoutPathExpansion(::onc::eap::kInner
, &inner
);
251 if (inner
!= ::onc::eap::kAutomatic
) {
252 const StringTranslationEntry
* table
= outer
== ::onc::eap::kPEAP
253 ? kEAP_PEAP_InnerTable
254 : kEAP_TTLS_InnerTable
;
255 TranslateWithTableAndSet(inner
, table
, shill::kEapPhase2AuthProperty
);
259 CopyFieldsAccordingToSignature();
262 void LocalTranslator::TranslateNetworkConfiguration() {
264 onc_object_
->GetStringWithoutPathExpansion(::onc::network_config::kType
,
267 // Set the type except for Ethernet which is set in TranslateEthernet.
268 if (type
!= ::onc::network_type::kEthernet
)
269 TranslateWithTableAndSet(type
, kNetworkTypeTable
, shill::kTypeProperty
);
271 // Shill doesn't allow setting the name for non-VPN networks.
272 if (type
== ::onc::network_type::kVPN
)
273 CopyFieldFromONCToShill(::onc::network_config::kName
, shill::kNameProperty
);
275 std::string ip_address_config_type
, name_servers_config_type
;
276 onc_object_
->GetStringWithoutPathExpansion(
277 ::onc::network_config::kIPAddressConfigType
, &ip_address_config_type
);
278 onc_object_
->GetStringWithoutPathExpansion(
279 ::onc::network_config::kNameServersConfigType
, &name_servers_config_type
);
280 if ((ip_address_config_type
== ::onc::network_config::kIPConfigTypeDHCP
) ||
281 (name_servers_config_type
== ::onc::network_config::kIPConfigTypeDHCP
)) {
282 // If either type is set to DHCP, provide an empty dictionary to ensure
283 // that any unset properties are cleared. Note: if either type is specified,
284 // the other type defaults to DHCP if not specified.
285 shill_dictionary_
->SetWithoutPathExpansion(shill::kStaticIPConfigProperty
,
286 new base::DictionaryValue
);
289 const base::DictionaryValue
* proxy_settings
= nullptr;
290 if (onc_object_
->GetDictionaryWithoutPathExpansion(
291 ::onc::network_config::kProxySettings
, &proxy_settings
)) {
292 scoped_ptr
<base::DictionaryValue
> proxy_config
=
293 ConvertOncProxySettingsToProxyConfig(*proxy_settings
);
294 std::string proxy_config_str
;
295 base::JSONWriter::Write(*proxy_config
.get(), &proxy_config_str
);
296 shill_dictionary_
->SetStringWithoutPathExpansion(
297 shill::kProxyConfigProperty
, proxy_config_str
);
300 CopyFieldsAccordingToSignature();
303 void LocalTranslator::CopyFieldsAccordingToSignature() {
304 for (base::DictionaryValue::Iterator
it(*onc_object_
); !it
.IsAtEnd();
306 AddValueAccordingToSignature(it
.key(),
307 make_scoped_ptr(it
.value().DeepCopy()));
311 void LocalTranslator::CopyFieldFromONCToShill(
312 const std::string
& onc_field_name
,
313 const std::string
& shill_property_name
) {
314 const base::Value
* value
= NULL
;
315 if (!onc_object_
->GetWithoutPathExpansion(onc_field_name
, &value
))
318 const OncFieldSignature
* field_signature
=
319 GetFieldSignature(*onc_signature_
, onc_field_name
);
320 if (field_signature
) {
321 base::Value::Type expected_type
=
322 field_signature
->value_signature
->onc_type
;
323 if (value
->GetType() != expected_type
) {
324 LOG(ERROR
) << "Found field " << onc_field_name
<< " of type "
325 << value
->GetType() << " but expected type " << expected_type
;
330 << "Attempt to translate a field that is not part of the ONC format.";
333 shill_dictionary_
->SetWithoutPathExpansion(shill_property_name
,
337 void LocalTranslator::AddValueAccordingToSignature(
338 const std::string
& onc_name
,
339 scoped_ptr
<base::Value
> value
) {
340 if (!value
|| !field_translation_table_
)
342 std::string shill_property_name
;
343 if (!GetShillPropertyName(onc_name
, field_translation_table_
,
344 &shill_property_name
)) {
348 shill_dictionary_
->SetWithoutPathExpansion(shill_property_name
,
352 void LocalTranslator::TranslateWithTableAndSet(
353 const std::string
& onc_value
,
354 const StringTranslationEntry table
[],
355 const std::string
& shill_property_name
) {
356 std::string shill_value
;
357 if (TranslateStringToShill(table
, onc_value
, &shill_value
)) {
358 shill_dictionary_
->SetStringWithoutPathExpansion(shill_property_name
,
362 // As we previously validate ONC, this case should never occur. If it still
363 // occurs, we should check here. Otherwise the failure will only show up much
365 LOG(ERROR
) << "Value '" << onc_value
366 << "' cannot be translated to Shill property: "
367 << shill_property_name
;
370 // Iterates recursively over |onc_object| and its |signature|. At each object
371 // applies the local translation using LocalTranslator::TranslateFields. The
372 // results are written to |shill_dictionary|.
373 void TranslateONCHierarchy(const OncValueSignature
& signature
,
374 const base::DictionaryValue
& onc_object
,
375 base::DictionaryValue
* shill_dictionary
) {
376 base::DictionaryValue
* target_shill_dictionary
= shill_dictionary
;
377 std::vector
<std::string
> path_to_shill_dictionary
=
378 GetPathToNestedShillDictionary(signature
);
379 for (std::vector
<std::string
>::const_iterator it
=
380 path_to_shill_dictionary
.begin();
381 it
!= path_to_shill_dictionary
.end(); ++it
) {
382 base::DictionaryValue
* nested_shill_dict
= NULL
;
383 target_shill_dictionary
->GetDictionaryWithoutPathExpansion(
384 *it
, &nested_shill_dict
);
385 if (!nested_shill_dict
) {
386 nested_shill_dict
= new base::DictionaryValue
;
387 target_shill_dictionary
->SetWithoutPathExpansion(*it
, nested_shill_dict
);
389 target_shill_dictionary
= nested_shill_dict
;
391 // Translates fields of |onc_object| and writes them to
392 // |target_shill_dictionary_| nested in |shill_dictionary|.
393 LocalTranslator
translator(signature
, onc_object
, target_shill_dictionary
);
394 translator
.TranslateFields();
396 // Recurse into nested objects.
397 for (base::DictionaryValue::Iterator
it(onc_object
); !it
.IsAtEnd();
399 const base::DictionaryValue
* inner_object
= NULL
;
400 if (!it
.value().GetAsDictionary(&inner_object
))
403 const OncFieldSignature
* field_signature
=
404 GetFieldSignature(signature
, it
.key());
406 TranslateONCHierarchy(*field_signature
->value_signature
, *inner_object
,
413 scoped_ptr
<base::DictionaryValue
> TranslateONCObjectToShill(
414 const OncValueSignature
* onc_signature
,
415 const base::DictionaryValue
& onc_object
) {
416 CHECK(onc_signature
!= NULL
);
417 scoped_ptr
<base::DictionaryValue
> shill_dictionary(new base::DictionaryValue
);
418 TranslateONCHierarchy(*onc_signature
, onc_object
, shill_dictionary
.get());
419 return shill_dictionary
.Pass();
423 } // namespace chromeos