Home | History | Annotate | Download | only in onc
      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.
     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:
     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>
    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 
    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 
    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 
    217 void ShillToONCTranslator::TranslateIPsec() {
    218   CopyPropertiesAccordingToSignature();
    219   if (shill_dictionary_->HasKey(shill::kL2tpIpsecXauthUserProperty))
    220     TranslateAndAddNestedObject(::onc::ipsec::kXAUTH);
    221 }
    222 
    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 
    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 
    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 
    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 
    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 
    376 void ShillToONCTranslator::TranslateAndAddNestedObject(
    377     const std::string& onc_field_name) {
    378   TranslateAndAddNestedObject(onc_field_name, *shill_dictionary_);
    379 }
    380 
    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 
    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 
    431 void ShillToONCTranslator::CopyPropertiesAccordingToSignature() {
    432   CopyPropertiesAccordingToSignature(onc_signature_);
    433 }
    434 
    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 
    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 
    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 
    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