• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.
4 
5 #include "chromeos/network/onc/onc_translator.h"
6 
7 #include <string>
8 
9 #include "base/basictypes.h"
10 #include "base/json/json_reader.h"
11 #include "base/json/json_writer.h"
12 #include "base/logging.h"
13 #include "base/values.h"
14 #include "chromeos/network/network_state.h"
15 #include "chromeos/network/network_util.h"
16 #include "chromeos/network/onc/onc_signature.h"
17 #include "chromeos/network/onc/onc_translation_tables.h"
18 #include "chromeos/network/shill_property_util.h"
19 #include "components/onc/onc_constants.h"
20 #include "third_party/cros_system_api/dbus/service_constants.h"
21 
22 namespace chromeos {
23 namespace onc {
24 
25 namespace {
26 
27 // Converts |str| to a base::Value of the given |type|. If the conversion fails,
28 // returns NULL.
ConvertStringToValue(const std::string & str,base::Value::Type type)29 scoped_ptr<base::Value> ConvertStringToValue(const std::string& str,
30                                              base::Value::Type type) {
31   base::Value* value;
32   if (type == base::Value::TYPE_STRING) {
33     value = base::Value::CreateStringValue(str);
34   } else {
35     value = base::JSONReader::Read(str);
36   }
37 
38   if (value == NULL || value->GetType() != type) {
39     delete value;
40     value = NULL;
41   }
42   return make_scoped_ptr(value);
43 }
44 
45 // This class implements the translation of properties from the given
46 // |shill_dictionary| to a new ONC object of signature |onc_signature|. Using
47 // recursive calls to CreateTranslatedONCObject of new instances, nested objects
48 // are translated.
49 class ShillToONCTranslator {
50  public:
ShillToONCTranslator(const base::DictionaryValue & shill_dictionary,const OncValueSignature & onc_signature)51   ShillToONCTranslator(const base::DictionaryValue& shill_dictionary,
52                        const OncValueSignature& onc_signature)
53       : shill_dictionary_(&shill_dictionary),
54         onc_signature_(&onc_signature) {
55     field_translation_table_ = GetFieldTranslationTable(onc_signature);
56   }
57 
58   // Translates the associated Shill dictionary and creates an ONC object of the
59   // given signature.
60   scoped_ptr<base::DictionaryValue> CreateTranslatedONCObject();
61 
62  private:
63   void TranslateEthernet();
64   void TranslateOpenVPN();
65   void TranslateIPsec();
66   void TranslateVPN();
67   void TranslateWiFiWithState();
68   void TranslateCellularWithState();
69   void TranslateNetworkWithState();
70   void TranslateIPConfig();
71 
72   // Creates an ONC object from |dictionary| according to the signature
73   // associated to |onc_field_name| and adds it to |onc_object_| at
74   // |onc_field_name|.
75   void TranslateAndAddNestedObject(const std::string& onc_field_name,
76                                    const base::DictionaryValue& dictionary);
77 
78   // Creates an ONC object from |shill_dictionary_| according to the signature
79   // associated to |onc_field_name| and adds it to |onc_object_| at
80   // |onc_field_name|.
81   void TranslateAndAddNestedObject(const std::string& onc_field_name);
82 
83   // Translates a list of nested objects and adds the list to |onc_object_| at
84   // |onc_field_name|. If there are errors while parsing individual objects or
85   // if the resulting list contains no entries, the result will not be added to
86   // |onc_object_|.
87   void TranslateAndAddListOfObjects(const std::string& onc_field_name,
88                                     const base::ListValue& list);
89 
90   // Applies function CopyProperty to each field of |value_signature| and its
91   // base signatures.
92   void CopyPropertiesAccordingToSignature(
93       const OncValueSignature* value_signature);
94 
95   // Applies function CopyProperty to each field of |onc_signature_| and its
96   // base signatures.
97   void CopyPropertiesAccordingToSignature();
98 
99   // If |shill_property_name| is defined in |field_signature|, copies this
100   // entry from |shill_dictionary_| to |onc_object_| if it exists.
101   void CopyProperty(const OncFieldSignature* field_signature);
102 
103   // If existent, translates the entry at |shill_property_name| in
104   // |shill_dictionary_| using |table|. It is an error if no matching table
105   // entry is found. Writes the result as entry at |onc_field_name| in
106   // |onc_object_|.
107   void TranslateWithTableAndSet(const std::string& shill_property_name,
108                                 const StringTranslationEntry table[],
109                                 const std::string& onc_field_name);
110 
111   const base::DictionaryValue* shill_dictionary_;
112   const OncValueSignature* onc_signature_;
113   const FieldTranslationEntry* field_translation_table_;
114   scoped_ptr<base::DictionaryValue> onc_object_;
115 
116   DISALLOW_COPY_AND_ASSIGN(ShillToONCTranslator);
117 };
118 
119 scoped_ptr<base::DictionaryValue>
CreateTranslatedONCObject()120 ShillToONCTranslator::CreateTranslatedONCObject() {
121   onc_object_.reset(new base::DictionaryValue);
122   if (onc_signature_ == &kNetworkWithStateSignature) {
123     TranslateNetworkWithState();
124   } else if (onc_signature_ == &kEthernetSignature) {
125     TranslateEthernet();
126   } else if (onc_signature_ == &kVPNSignature) {
127     TranslateVPN();
128   } else if (onc_signature_ == &kOpenVPNSignature) {
129     TranslateOpenVPN();
130   } else if (onc_signature_ == &kIPsecSignature) {
131     TranslateIPsec();
132   } else if (onc_signature_ == &kWiFiWithStateSignature) {
133     TranslateWiFiWithState();
134   } else if (onc_signature_ == &kCellularWithStateSignature) {
135     TranslateCellularWithState();
136   } else if (onc_signature_ == &kIPConfigSignature) {
137     TranslateIPConfig();
138   } else {
139     CopyPropertiesAccordingToSignature();
140   }
141   return onc_object_.Pass();
142 }
143 
TranslateEthernet()144 void ShillToONCTranslator::TranslateEthernet() {
145   std::string shill_network_type;
146   shill_dictionary_->GetStringWithoutPathExpansion(shill::kTypeProperty,
147                                                    &shill_network_type);
148   const char* onc_auth = ::onc::ethernet::kNone;
149   if (shill_network_type == shill::kTypeEthernetEap)
150     onc_auth = ::onc::ethernet::k8021X;
151   onc_object_->SetStringWithoutPathExpansion(::onc::ethernet::kAuthentication,
152                                              onc_auth);
153 }
154 
TranslateOpenVPN()155 void ShillToONCTranslator::TranslateOpenVPN() {
156   if (shill_dictionary_->HasKey(shill::kOpenVPNVerifyX509NameProperty))
157     TranslateAndAddNestedObject(::onc::openvpn::kVerifyX509);
158 
159   // Shill supports only one RemoteCertKU but ONC requires a list. If existing,
160   // wraps the value into a list.
161   std::string certKU;
162   if (shill_dictionary_->GetStringWithoutPathExpansion(
163           shill::kOpenVPNRemoteCertKUProperty, &certKU)) {
164     scoped_ptr<base::ListValue> certKUs(new base::ListValue);
165     certKUs->AppendString(certKU);
166     onc_object_->SetWithoutPathExpansion(::onc::openvpn::kRemoteCertKU,
167                                          certKUs.release());
168   }
169 
170   for (const OncFieldSignature* field_signature = onc_signature_->fields;
171        field_signature->onc_field_name != NULL; ++field_signature) {
172     const std::string& onc_field_name = field_signature->onc_field_name;
173     if (onc_field_name == ::onc::vpn::kSaveCredentials ||
174         onc_field_name == ::onc::openvpn::kRemoteCertKU ||
175         onc_field_name == ::onc::openvpn::kServerCAPEMs) {
176       CopyProperty(field_signature);
177       continue;
178     }
179 
180     std::string shill_property_name;
181     const base::Value* shill_value = NULL;
182     if (!field_translation_table_ ||
183         !GetShillPropertyName(field_signature->onc_field_name,
184                               field_translation_table_,
185                               &shill_property_name) ||
186         !shill_dictionary_->GetWithoutPathExpansion(shill_property_name,
187                                                     &shill_value)) {
188       continue;
189     }
190 
191     scoped_ptr<base::Value> translated;
192     std::string shill_str;
193     if (shill_value->GetAsString(&shill_str)) {
194       // Shill wants all Provider/VPN fields to be strings. Translates these
195       // strings back to the correct ONC type.
196       translated = ConvertStringToValue(
197           shill_str,
198           field_signature->value_signature->onc_type);
199 
200       if (translated.get() == NULL) {
201         LOG(ERROR) << "Shill property '" << shill_property_name
202                    << "' with value " << *shill_value
203                    << " couldn't be converted to base::Value::Type "
204                    << field_signature->value_signature->onc_type;
205       } else {
206         onc_object_->SetWithoutPathExpansion(onc_field_name,
207                                              translated.release());
208       }
209     } else {
210       LOG(ERROR) << "Shill property '" << shill_property_name
211                  << "' has value " << *shill_value
212                  << ", but expected a string";
213     }
214   }
215 }
216 
TranslateIPsec()217 void ShillToONCTranslator::TranslateIPsec() {
218   CopyPropertiesAccordingToSignature();
219   if (shill_dictionary_->HasKey(shill::kL2tpIpsecXauthUserProperty))
220     TranslateAndAddNestedObject(::onc::ipsec::kXAUTH);
221 }
222 
TranslateVPN()223 void ShillToONCTranslator::TranslateVPN() {
224   TranslateWithTableAndSet(
225       shill::kProviderTypeProperty, kVPNTypeTable, ::onc::vpn::kType);
226   CopyPropertiesAccordingToSignature();
227 
228   std::string vpn_type;
229   if (onc_object_->GetStringWithoutPathExpansion(::onc::vpn::kType,
230                                                  &vpn_type)) {
231     if (vpn_type == ::onc::vpn::kTypeL2TP_IPsec) {
232       TranslateAndAddNestedObject(::onc::vpn::kIPsec);
233       TranslateAndAddNestedObject(::onc::vpn::kL2TP);
234     } else {
235       TranslateAndAddNestedObject(vpn_type);
236     }
237   }
238 }
239 
TranslateWiFiWithState()240 void ShillToONCTranslator::TranslateWiFiWithState() {
241   TranslateWithTableAndSet(
242       shill::kSecurityProperty, kWiFiSecurityTable, ::onc::wifi::kSecurity);
243   std::string ssid = shill_property_util::GetSSIDFromProperties(
244       *shill_dictionary_, NULL /* ignore unknown encoding */);
245   if (!ssid.empty())
246     onc_object_->SetStringWithoutPathExpansion(::onc::wifi::kSSID, ssid);
247   CopyPropertiesAccordingToSignature();
248 }
249 
TranslateCellularWithState()250 void ShillToONCTranslator::TranslateCellularWithState() {
251   CopyPropertiesAccordingToSignature();
252   const base::DictionaryValue* dictionary = NULL;
253   if (shill_dictionary_->GetDictionaryWithoutPathExpansion(
254         shill::kServingOperatorProperty, &dictionary)) {
255     TranslateAndAddNestedObject(::onc::cellular::kServingOperator, *dictionary);
256   }
257   if (shill_dictionary_->GetDictionaryWithoutPathExpansion(
258         shill::kCellularApnProperty, &dictionary)) {
259     TranslateAndAddNestedObject(::onc::cellular::kAPN, *dictionary);
260   }
261   const base::ListValue* shill_apns = NULL;
262   if (shill_dictionary_->GetListWithoutPathExpansion(
263           shill::kCellularApnListProperty, &shill_apns)) {
264     TranslateAndAddListOfObjects(::onc::cellular::kAPNList, *shill_apns);
265   }
266 
267   const base::DictionaryValue* device_dictionary = NULL;
268   if (!shill_dictionary_->GetDictionaryWithoutPathExpansion(
269           shill::kDeviceProperty, &device_dictionary)) {
270     return;
271   }
272 
273   // Iterate through all fields of the CellularWithState signature and copy
274   // values from the device properties according to the separate
275   // CellularDeviceTable.
276   for (const OncFieldSignature* field_signature = onc_signature_->fields;
277        field_signature->onc_field_name != NULL; ++field_signature) {
278     const std::string& onc_field_name = field_signature->onc_field_name;
279 
280     std::string shill_property_name;
281     const base::Value* shill_value = NULL;
282     if (!GetShillPropertyName(field_signature->onc_field_name,
283                               kCellularDeviceTable,
284                               &shill_property_name) ||
285         !device_dictionary->GetWithoutPathExpansion(shill_property_name,
286                                                     &shill_value)) {
287       continue;
288     }
289     onc_object_->SetWithoutPathExpansion(onc_field_name,
290                                          shill_value->DeepCopy());
291   }
292 }
293 
TranslateNetworkWithState()294 void ShillToONCTranslator::TranslateNetworkWithState() {
295   CopyPropertiesAccordingToSignature();
296 
297   std::string shill_network_type;
298   shill_dictionary_->GetStringWithoutPathExpansion(shill::kTypeProperty,
299                                                    &shill_network_type);
300   std::string onc_network_type = ::onc::network_type::kEthernet;
301   if (shill_network_type != shill::kTypeEthernet &&
302       shill_network_type != shill::kTypeEthernetEap) {
303     TranslateStringToONC(
304         kNetworkTypeTable, shill_network_type, &onc_network_type);
305   }
306   // Translate nested Cellular, WiFi, etc. properties.
307   if (!onc_network_type.empty()) {
308     onc_object_->SetStringWithoutPathExpansion(::onc::network_config::kType,
309                                                onc_network_type);
310     TranslateAndAddNestedObject(onc_network_type);
311   }
312 
313   // Since Name is a read only field in Shill unless it's a VPN, it is copied
314   // here, but not when going the other direction (if it's not a VPN).
315   std::string name;
316   shill_dictionary_->GetStringWithoutPathExpansion(shill::kNameProperty,
317                                                    &name);
318   onc_object_->SetStringWithoutPathExpansion(::onc::network_config::kName,
319                                              name);
320 
321   // Limit ONC state to "NotConnected", "Connected", or "Connecting".
322   std::string state;
323   if (shill_dictionary_->GetStringWithoutPathExpansion(shill::kStateProperty,
324                                                        &state)) {
325     std::string onc_state = ::onc::connection_state::kNotConnected;
326     if (NetworkState::StateIsConnected(state)) {
327       onc_state = ::onc::connection_state::kConnected;
328     } else if (NetworkState::StateIsConnecting(state)) {
329       onc_state = ::onc::connection_state::kConnecting;
330     }
331     onc_object_->SetStringWithoutPathExpansion(
332         ::onc::network_config::kConnectionState, onc_state);
333   }
334 
335   // Use a human-readable aa:bb format for any hardware MAC address. Note:
336   // this property is provided by the caller but is not part of the Shill
337   // Service properties (it is copied from the Device properties).
338   std::string address;
339   if (shill_dictionary_->GetStringWithoutPathExpansion(shill::kAddressProperty,
340                                                        &address)) {
341     onc_object_->SetStringWithoutPathExpansion(
342         ::onc::network_config::kMacAddress,
343         network_util::FormattedMacAddress(address));
344   }
345 
346   // Shill's Service has an IPConfig property (note the singular), not an
347   // IPConfigs property. However, we require the caller of the translation to
348   // patch the Shill dictionary before passing it to the translator.
349   const base::ListValue* shill_ipconfigs = NULL;
350   if (shill_dictionary_->GetListWithoutPathExpansion(shill::kIPConfigsProperty,
351                                                      &shill_ipconfigs)) {
352     TranslateAndAddListOfObjects(::onc::network_config::kIPConfigs,
353                                  *shill_ipconfigs);
354   }
355 }
356 
TranslateIPConfig()357 void ShillToONCTranslator::TranslateIPConfig() {
358   CopyPropertiesAccordingToSignature();
359   std::string shill_ip_method;
360   shill_dictionary_->GetStringWithoutPathExpansion(shill::kMethodProperty,
361                                                    &shill_ip_method);
362   std::string type;
363   if (shill_ip_method == shill::kTypeIPv4 ||
364       shill_ip_method == shill::kTypeDHCP) {
365     type = ::onc::ipconfig::kIPv4;
366   } else if (shill_ip_method == shill::kTypeIPv6 ||
367              shill_ip_method == shill::kTypeDHCP6) {
368     type = ::onc::ipconfig::kIPv6;
369   } else {
370     return;  // Ignore unhandled IPConfig types, e.g. bootp, zeroconf, ppp
371   }
372 
373   onc_object_->SetStringWithoutPathExpansion(::onc::ipconfig::kType, type);
374 }
375 
TranslateAndAddNestedObject(const std::string & onc_field_name)376 void ShillToONCTranslator::TranslateAndAddNestedObject(
377     const std::string& onc_field_name) {
378   TranslateAndAddNestedObject(onc_field_name, *shill_dictionary_);
379 }
380 
TranslateAndAddNestedObject(const std::string & onc_field_name,const base::DictionaryValue & dictionary)381 void ShillToONCTranslator::TranslateAndAddNestedObject(
382     const std::string& onc_field_name,
383     const base::DictionaryValue& dictionary) {
384   const OncFieldSignature* field_signature =
385       GetFieldSignature(*onc_signature_, onc_field_name);
386   DCHECK(field_signature) << "Unable to find signature for field "
387                           << onc_field_name << ".";
388   ShillToONCTranslator nested_translator(dictionary,
389                                          *field_signature->value_signature);
390   scoped_ptr<base::DictionaryValue> nested_object =
391       nested_translator.CreateTranslatedONCObject();
392   if (nested_object->empty())
393     return;
394   onc_object_->SetWithoutPathExpansion(onc_field_name, nested_object.release());
395 }
396 
TranslateAndAddListOfObjects(const std::string & onc_field_name,const base::ListValue & list)397 void ShillToONCTranslator::TranslateAndAddListOfObjects(
398     const std::string& onc_field_name,
399     const base::ListValue& list) {
400   const OncFieldSignature* field_signature =
401       GetFieldSignature(*onc_signature_, onc_field_name);
402   if (field_signature->value_signature->onc_type != base::Value::TYPE_LIST) {
403     LOG(ERROR) << "ONC Field name: '" << onc_field_name << "' has type '"
404                << field_signature->value_signature->onc_type
405                << "', expected: base::Value::TYPE_LIST.";
406     return;
407   }
408   DCHECK(field_signature->value_signature->onc_array_entry_signature);
409   scoped_ptr<base::ListValue> result(new base::ListValue());
410   for (base::ListValue::const_iterator it = list.begin();
411        it != list.end(); ++it) {
412     const base::DictionaryValue* shill_value = NULL;
413     if (!(*it)->GetAsDictionary(&shill_value))
414       continue;
415     ShillToONCTranslator nested_translator(
416         *shill_value,
417         *field_signature->value_signature->onc_array_entry_signature);
418     scoped_ptr<base::DictionaryValue> nested_object =
419         nested_translator.CreateTranslatedONCObject();
420     // If the nested object couldn't be parsed, simply omit it.
421     if (nested_object->empty())
422       continue;
423     result->Append(nested_object.release());
424   }
425   // If there are no entries in the list, there is no need to expose this field.
426   if (result->empty())
427     return;
428   onc_object_->SetWithoutPathExpansion(onc_field_name, result.release());
429 }
430 
CopyPropertiesAccordingToSignature()431 void ShillToONCTranslator::CopyPropertiesAccordingToSignature() {
432   CopyPropertiesAccordingToSignature(onc_signature_);
433 }
434 
CopyPropertiesAccordingToSignature(const OncValueSignature * value_signature)435 void ShillToONCTranslator::CopyPropertiesAccordingToSignature(
436     const OncValueSignature* value_signature) {
437   if (value_signature->base_signature)
438     CopyPropertiesAccordingToSignature(value_signature->base_signature);
439   for (const OncFieldSignature* field_signature = value_signature->fields;
440        field_signature->onc_field_name != NULL; ++field_signature) {
441     CopyProperty(field_signature);
442   }
443 }
444 
CopyProperty(const OncFieldSignature * field_signature)445 void ShillToONCTranslator::CopyProperty(
446     const OncFieldSignature* field_signature) {
447   std::string shill_property_name;
448   const base::Value* shill_value = NULL;
449   if (!field_translation_table_ ||
450       !GetShillPropertyName(field_signature->onc_field_name,
451                             field_translation_table_,
452                             &shill_property_name) ||
453       !shill_dictionary_->GetWithoutPathExpansion(shill_property_name,
454                                                   &shill_value)) {
455     return;
456   }
457 
458   if (shill_value->GetType() != field_signature->value_signature->onc_type) {
459     LOG(ERROR) << "Shill property '" << shill_property_name
460                << "' with value " << *shill_value
461                << " has base::Value::Type " << shill_value->GetType()
462                << " but ONC field '" << field_signature->onc_field_name
463                << "' requires type "
464                << field_signature->value_signature->onc_type << ".";
465     return;
466   }
467 
468  onc_object_->SetWithoutPathExpansion(field_signature->onc_field_name,
469                                       shill_value->DeepCopy());
470 }
471 
TranslateWithTableAndSet(const std::string & shill_property_name,const StringTranslationEntry table[],const std::string & onc_field_name)472 void ShillToONCTranslator::TranslateWithTableAndSet(
473     const std::string& shill_property_name,
474     const StringTranslationEntry table[],
475     const std::string& onc_field_name) {
476   std::string shill_value;
477   if (!shill_dictionary_->GetStringWithoutPathExpansion(shill_property_name,
478                                                         &shill_value)) {
479     return;
480   }
481   std::string onc_value;
482   if (TranslateStringToONC(table, shill_value, &onc_value)) {
483     onc_object_->SetStringWithoutPathExpansion(onc_field_name, onc_value);
484     return;
485   }
486   LOG(ERROR) << "Shill property '" << shill_property_name << "' with value "
487              << shill_value << " couldn't be translated to ONC";
488 }
489 
490 }  // namespace
491 
TranslateShillServiceToONCPart(const base::DictionaryValue & shill_dictionary,const OncValueSignature * onc_signature)492 scoped_ptr<base::DictionaryValue> TranslateShillServiceToONCPart(
493     const base::DictionaryValue& shill_dictionary,
494     const OncValueSignature* onc_signature) {
495   CHECK(onc_signature != NULL);
496 
497   ShillToONCTranslator translator(shill_dictionary, *onc_signature);
498   return translator.CreateTranslatedONCObject();
499 }
500 
501 }  // namespace onc
502 }  // namespace chromeos
503