Home | History | Annotate | Download | only in network
      1 // Copyright 2013 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/shill_property_util.h"
      6 
      7 #include "base/i18n/icu_encoding_detection.h"
      8 #include "base/i18n/icu_string_conversions.h"
      9 #include "base/json/json_writer.h"
     10 #include "base/strings/string_number_conversions.h"
     11 #include "base/strings/string_util.h"
     12 #include "base/strings/stringprintf.h"
     13 #include "base/strings/utf_string_conversion_utils.h"
     14 #include "base/values.h"
     15 #include "chromeos/network/network_event_log.h"
     16 #include "chromeos/network/network_ui_data.h"
     17 #include "chromeos/network/onc/onc_utils.h"
     18 #include "third_party/cros_system_api/dbus/service_constants.h"
     19 
     20 namespace chromeos {
     21 
     22 namespace shill_property_util {
     23 
     24 namespace {
     25 
     26 // Replace non UTF8 characters in |str| with a replacement character.
     27 std::string ValidateUTF8(const std::string& str) {
     28   std::string result;
     29   for (int32 index = 0; index < static_cast<int32>(str.size()); ++index) {
     30     uint32 code_point_out;
     31     bool is_unicode_char = base::ReadUnicodeCharacter(
     32         str.c_str(), str.size(), &index, &code_point_out);
     33     const uint32 kFirstNonControlChar = 0x20;
     34     if (is_unicode_char && (code_point_out >= kFirstNonControlChar)) {
     35       base::WriteUnicodeCharacter(code_point_out, &result);
     36     } else {
     37       const uint32 kReplacementChar = 0xFFFD;
     38       // Puts kReplacementChar if character is a control character [0,0x20)
     39       // or is not readable UTF8.
     40       base::WriteUnicodeCharacter(kReplacementChar, &result);
     41     }
     42   }
     43   return result;
     44 }
     45 
     46 // If existent and non-empty, copies the string at |key| from |source| to
     47 // |dest|. Returns true if the string was copied.
     48 bool CopyStringFromDictionary(const base::DictionaryValue& source,
     49                               const std::string& key,
     50                               base::DictionaryValue* dest) {
     51   std::string string_value;
     52   if (!source.GetStringWithoutPathExpansion(key, &string_value) ||
     53       string_value.empty()) {
     54     return false;
     55   }
     56   dest->SetStringWithoutPathExpansion(key, string_value);
     57   return true;
     58 }
     59 
     60 // This is the same normalization that Shill applies to security types for the
     61 // sake of comparing/identifying WiFi networks. See Shill's
     62 // WiFiService::GetSecurityClass.
     63 std::string GetSecurityClass(const std::string& security) {
     64   if (security == shill::kSecurityRsn || security == shill::kSecurityWpa)
     65     return shill::kSecurityPsk;
     66   else
     67     return security;
     68 }
     69 
     70 }  // namespace
     71 
     72 void SetSSID(const std::string ssid, base::DictionaryValue* properties) {
     73   std::string hex_ssid = base::HexEncode(ssid.c_str(), ssid.size());
     74   properties->SetStringWithoutPathExpansion(shill::kWifiHexSsid, hex_ssid);
     75 }
     76 
     77 std::string GetSSIDFromProperties(const base::DictionaryValue& properties,
     78                                   bool* unknown_encoding) {
     79   if (unknown_encoding)
     80     *unknown_encoding = false;
     81 
     82   // Get name for debugging.
     83   std::string name;
     84   properties.GetStringWithoutPathExpansion(shill::kNameProperty, &name);
     85 
     86   std::string hex_ssid;
     87   properties.GetStringWithoutPathExpansion(shill::kWifiHexSsid, &hex_ssid);
     88 
     89   if (hex_ssid.empty()) {
     90     NET_LOG_DEBUG("GetSSIDFromProperties: No HexSSID set.", name);
     91     return std::string();
     92   }
     93 
     94   std::string ssid;
     95   std::vector<uint8> raw_ssid_bytes;
     96   if (base::HexStringToBytes(hex_ssid, &raw_ssid_bytes)) {
     97     ssid = std::string(raw_ssid_bytes.begin(), raw_ssid_bytes.end());
     98     NET_LOG_DEBUG(
     99         "GetSSIDFromProperties: " +
    100             base::StringPrintf("%s, SSID: %s", hex_ssid.c_str(), ssid.c_str()),
    101         name);
    102   } else {
    103     NET_LOG_ERROR(
    104         "GetSSIDFromProperties: " +
    105             base::StringPrintf("Error processing: %s", hex_ssid.c_str()),
    106         name);
    107     return std::string();
    108   }
    109 
    110   if (base::IsStringUTF8(ssid))
    111     return ssid;
    112 
    113   // Detect encoding and convert to UTF-8.
    114   std::string encoding;
    115   if (!base::DetectEncoding(ssid, &encoding)) {
    116     // TODO(stevenjb): This is currently experimental. If we find a case where
    117     // base::DetectEncoding() fails, we need to figure out whether we can use
    118     // country_code with ConvertToUtf8(). crbug.com/233267.
    119     properties.GetStringWithoutPathExpansion(shill::kCountryProperty,
    120                                              &encoding);
    121   }
    122   std::string utf8_ssid;
    123   if (!encoding.empty() &&
    124       base::ConvertToUtf8AndNormalize(ssid, encoding, &utf8_ssid)) {
    125     if (utf8_ssid != ssid) {
    126       NET_LOG_DEBUG(
    127           "GetSSIDFromProperties",
    128           base::StringPrintf(
    129               "Encoding=%s: %s", encoding.c_str(), utf8_ssid.c_str()));
    130     }
    131     return utf8_ssid;
    132   }
    133 
    134   if (unknown_encoding)
    135     *unknown_encoding = true;
    136   NET_LOG_DEBUG(
    137       "GetSSIDFromProperties: " +
    138           base::StringPrintf("Unrecognized Encoding=%s", encoding.c_str()),
    139       name);
    140   return ssid;
    141 }
    142 
    143 std::string GetNetworkIdFromProperties(
    144     const base::DictionaryValue& properties) {
    145   if (properties.empty())
    146     return "EmptyProperties";
    147   std::string result;
    148   if (properties.GetStringWithoutPathExpansion(shill::kGuidProperty, &result))
    149     return result;
    150   if (properties.GetStringWithoutPathExpansion(shill::kSSIDProperty, &result))
    151     return result;
    152   if (properties.GetStringWithoutPathExpansion(shill::kNameProperty, &result))
    153     return result;
    154   std::string type = "UnknownType";
    155   properties.GetStringWithoutPathExpansion(shill::kTypeProperty, &type);
    156   return "Unidentified " + type;
    157 }
    158 
    159 std::string GetNameFromProperties(const std::string& service_path,
    160                                   const base::DictionaryValue& properties) {
    161   std::string name;
    162   properties.GetStringWithoutPathExpansion(shill::kNameProperty, &name);
    163 
    164   std::string validated_name = ValidateUTF8(name);
    165   if (validated_name != name) {
    166     NET_LOG_DEBUG("GetNameFromProperties",
    167                   base::StringPrintf("Validated name %s: UTF8: %s",
    168                                      service_path.c_str(),
    169                                      validated_name.c_str()));
    170   }
    171 
    172   std::string type;
    173   properties.GetStringWithoutPathExpansion(shill::kTypeProperty, &type);
    174   if (type.empty()) {
    175     NET_LOG_ERROR("GetNameFromProperties: No type", service_path);
    176     return validated_name;
    177   }
    178   if (!NetworkTypePattern::WiFi().MatchesType(type))
    179     return validated_name;
    180 
    181   bool unknown_ssid_encoding = false;
    182   std::string ssid = GetSSIDFromProperties(properties, &unknown_ssid_encoding);
    183   if (ssid.empty())
    184     NET_LOG_ERROR("GetNameFromProperties", "No SSID set: " + service_path);
    185 
    186   // Use |validated_name| if |ssid| is empty.
    187   // And if the encoding of the SSID is unknown, use |ssid|, which contains raw
    188   // bytes in that case, only if |validated_name| is empty.
    189   if (ssid.empty() || (unknown_ssid_encoding && !validated_name.empty()))
    190     return validated_name;
    191 
    192   if (ssid != validated_name) {
    193     NET_LOG_DEBUG("GetNameFromProperties",
    194                   base::StringPrintf("%s: SSID: %s, Name: %s",
    195                                      service_path.c_str(),
    196                                      ssid.c_str(),
    197                                      validated_name.c_str()));
    198   }
    199   return ssid;
    200 }
    201 
    202 scoped_ptr<NetworkUIData> GetUIDataFromValue(const base::Value& ui_data_value) {
    203   std::string ui_data_str;
    204   if (!ui_data_value.GetAsString(&ui_data_str))
    205     return scoped_ptr<NetworkUIData>();
    206   if (ui_data_str.empty())
    207     return make_scoped_ptr(new NetworkUIData());
    208   scoped_ptr<base::DictionaryValue> ui_data_dict(
    209       chromeos::onc::ReadDictionaryFromJson(ui_data_str));
    210   if (!ui_data_dict)
    211     return scoped_ptr<NetworkUIData>();
    212   return make_scoped_ptr(new NetworkUIData(*ui_data_dict));
    213 }
    214 
    215 scoped_ptr<NetworkUIData> GetUIDataFromProperties(
    216     const base::DictionaryValue& shill_dictionary) {
    217   const base::Value* ui_data_value = NULL;
    218   shill_dictionary.GetWithoutPathExpansion(shill::kUIDataProperty,
    219                                            &ui_data_value);
    220   if (!ui_data_value) {
    221     VLOG(2) << "Dictionary has no UIData entry.";
    222     return scoped_ptr<NetworkUIData>();
    223   }
    224   scoped_ptr<NetworkUIData> ui_data = GetUIDataFromValue(*ui_data_value);
    225   if (!ui_data)
    226     LOG(ERROR) << "UIData is not a valid JSON dictionary.";
    227   return ui_data.Pass();
    228 }
    229 
    230 void SetUIData(const NetworkUIData& ui_data,
    231                base::DictionaryValue* shill_dictionary) {
    232   base::DictionaryValue ui_data_dict;
    233   ui_data.FillDictionary(&ui_data_dict);
    234   std::string ui_data_blob;
    235   base::JSONWriter::Write(&ui_data_dict, &ui_data_blob);
    236   shill_dictionary->SetStringWithoutPathExpansion(shill::kUIDataProperty,
    237                                                   ui_data_blob);
    238 }
    239 
    240 bool CopyIdentifyingProperties(const base::DictionaryValue& service_properties,
    241                                const bool properties_read_from_shill,
    242                                base::DictionaryValue* dest) {
    243   bool success = true;
    244 
    245   // GUID is optional.
    246   CopyStringFromDictionary(service_properties, shill::kGuidProperty, dest);
    247 
    248   std::string type;
    249   service_properties.GetStringWithoutPathExpansion(shill::kTypeProperty, &type);
    250   success &= !type.empty();
    251   dest->SetStringWithoutPathExpansion(shill::kTypeProperty, type);
    252   if (type == shill::kTypeWifi) {
    253     std::string security;
    254     service_properties.GetStringWithoutPathExpansion(shill::kSecurityProperty,
    255                                                      &security);
    256     if (security.empty()) {
    257       success = false;
    258     } else {
    259       dest->SetStringWithoutPathExpansion(shill::kSecurityProperty,
    260                                           GetSecurityClass(security));
    261     }
    262     success &=
    263         CopyStringFromDictionary(service_properties, shill::kWifiHexSsid, dest);
    264     success &= CopyStringFromDictionary(
    265         service_properties, shill::kModeProperty, dest);
    266   } else if (type == shill::kTypeVPN) {
    267     success &= CopyStringFromDictionary(
    268         service_properties, shill::kNameProperty, dest);
    269 
    270     // VPN Provider values are read from the "Provider" dictionary, but written
    271     // with the keys "Provider.Type" and "Provider.Host".
    272     // TODO(pneubeck): Simplify this once http://crbug.com/381135 is fixed.
    273     std::string vpn_provider_type;
    274     std::string vpn_provider_host;
    275     if (properties_read_from_shill) {
    276       const base::DictionaryValue* provider_properties = NULL;
    277       if (!service_properties.GetDictionaryWithoutPathExpansion(
    278               shill::kProviderProperty, &provider_properties)) {
    279         NET_LOG_ERROR("Missing VPN provider dict",
    280                       GetNetworkIdFromProperties(service_properties));
    281       }
    282       provider_properties->GetStringWithoutPathExpansion(shill::kTypeProperty,
    283                                                          &vpn_provider_type);
    284       provider_properties->GetStringWithoutPathExpansion(shill::kHostProperty,
    285                                                          &vpn_provider_host);
    286     } else {
    287       service_properties.GetStringWithoutPathExpansion(
    288           shill::kProviderTypeProperty, &vpn_provider_type);
    289       service_properties.GetStringWithoutPathExpansion(
    290           shill::kProviderHostProperty, &vpn_provider_host);
    291     }
    292     success &= !vpn_provider_type.empty();
    293     dest->SetStringWithoutPathExpansion(shill::kProviderTypeProperty,
    294                                         vpn_provider_type);
    295 
    296     success &= !vpn_provider_host.empty();
    297     dest->SetStringWithoutPathExpansion(shill::kProviderHostProperty,
    298                                         vpn_provider_host);
    299   } else if (type == shill::kTypeEthernet || type == shill::kTypeEthernetEap) {
    300     // Ethernet and EthernetEAP don't have any additional identifying
    301     // properties.
    302   } else {
    303     NOTREACHED() << "Unsupported network type " << type;
    304     success = false;
    305   }
    306   if (!success) {
    307     NET_LOG_ERROR("Missing required properties",
    308                   GetNetworkIdFromProperties(service_properties));
    309   }
    310   return success;
    311 }
    312 
    313 bool DoIdentifyingPropertiesMatch(const base::DictionaryValue& new_properties,
    314                                   const base::DictionaryValue& old_properties) {
    315   base::DictionaryValue new_identifying;
    316   if (!CopyIdentifyingProperties(
    317           new_properties,
    318           false /* properties were not read from Shill */,
    319           &new_identifying)) {
    320     return false;
    321   }
    322   base::DictionaryValue old_identifying;
    323   if (!CopyIdentifyingProperties(old_properties,
    324                                  true /* properties were read from Shill */,
    325                                  &old_identifying)) {
    326     return false;
    327   }
    328 
    329   return new_identifying.Equals(&old_identifying);
    330 }
    331 
    332 bool IsPassphraseKey(const std::string& key) {
    333   return key == shill::kEapPrivateKeyPasswordProperty ||
    334       key == shill::kEapPasswordProperty ||
    335       key == shill::kL2tpIpsecPasswordProperty ||
    336       key == shill::kOpenVPNPasswordProperty ||
    337       key == shill::kOpenVPNAuthUserPassProperty ||
    338       key == shill::kOpenVPNTLSAuthContentsProperty ||
    339       key == shill::kPassphraseProperty ||
    340       key == shill::kOpenVPNOTPProperty ||
    341       key == shill::kEapPrivateKeyProperty ||
    342       key == shill::kEapPinProperty ||
    343       key == shill::kApnPasswordProperty;
    344 }
    345 
    346 bool GetHomeProviderFromProperty(const base::Value& value,
    347                                  std::string* home_provider_id) {
    348   const base::DictionaryValue* dict = NULL;
    349   if (!value.GetAsDictionary(&dict))
    350     return false;
    351   std::string home_provider_country;
    352   std::string home_provider_name;
    353   dict->GetStringWithoutPathExpansion(shill::kOperatorCountryKey,
    354                                       &home_provider_country);
    355   dict->GetStringWithoutPathExpansion(shill::kOperatorNameKey,
    356                                       &home_provider_name);
    357   // Set home_provider_id
    358   if (!home_provider_name.empty() && !home_provider_country.empty()) {
    359     *home_provider_id = base::StringPrintf(
    360         "%s (%s)", home_provider_name.c_str(), home_provider_country.c_str());
    361   } else {
    362     if (!dict->GetStringWithoutPathExpansion(shill::kOperatorCodeKey,
    363                                              home_provider_id)) {
    364       return false;
    365     }
    366     LOG(WARNING)
    367         << "Provider name and country not defined, using code instead: "
    368         << *home_provider_id;
    369   }
    370   return true;
    371 }
    372 
    373 }  // namespace shill_property_util
    374 
    375 }  // namespace chromeos
    376