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   bool verbose_logging = false;
     80   if (unknown_encoding) {
     81     *unknown_encoding = false;
     82     verbose_logging = true;
     83   }
     84 
     85   // Get name for debugging.
     86   std::string name;
     87   properties.GetStringWithoutPathExpansion(shill::kNameProperty, &name);
     88 
     89   std::string hex_ssid;
     90   properties.GetStringWithoutPathExpansion(shill::kWifiHexSsid, &hex_ssid);
     91 
     92   if (hex_ssid.empty()) {
     93     if (verbose_logging)
     94       NET_LOG_DEBUG("GetSSIDFromProperties: No HexSSID set.", name);
     95     return std::string();
     96   }
     97 
     98   std::string ssid;
     99   std::vector<uint8> raw_ssid_bytes;
    100   if (base::HexStringToBytes(hex_ssid, &raw_ssid_bytes)) {
    101     ssid = std::string(raw_ssid_bytes.begin(), raw_ssid_bytes.end());
    102     if (verbose_logging) {
    103       NET_LOG_DEBUG(base::StringPrintf("GetSSIDFromProperties: %s, SSID: %s",
    104                                        hex_ssid.c_str(), ssid.c_str()), name);
    105     }
    106   } else {
    107     NET_LOG_ERROR(
    108         base::StringPrintf("GetSSIDFromProperties: Error processing: %s",
    109                            hex_ssid.c_str()), name);
    110     return std::string();
    111   }
    112 
    113   if (base::IsStringUTF8(ssid))
    114     return ssid;
    115 
    116   // Detect encoding and convert to UTF-8.
    117   std::string encoding;
    118   if (!base::DetectEncoding(ssid, &encoding)) {
    119     // TODO(stevenjb): This is currently experimental. If we find a case where
    120     // base::DetectEncoding() fails, we need to figure out whether we can use
    121     // country_code with ConvertToUtf8(). crbug.com/233267.
    122     properties.GetStringWithoutPathExpansion(shill::kCountryProperty,
    123                                              &encoding);
    124   }
    125   std::string utf8_ssid;
    126   if (!encoding.empty() &&
    127       base::ConvertToUtf8AndNormalize(ssid, encoding, &utf8_ssid)) {
    128     if (utf8_ssid != ssid) {
    129       if (verbose_logging) {
    130         NET_LOG_DEBUG(
    131             base::StringPrintf("GetSSIDFromProperties: Encoding=%s: %s",
    132                                encoding.c_str(), utf8_ssid.c_str()), name);
    133       }
    134     }
    135     return utf8_ssid;
    136   }
    137 
    138   if (unknown_encoding)
    139     *unknown_encoding = true;
    140   if (verbose_logging) {
    141     NET_LOG_DEBUG(
    142         base::StringPrintf("GetSSIDFromProperties: Unrecognized Encoding=%s",
    143                            encoding.c_str()), name);
    144   }
    145   return ssid;
    146 }
    147 
    148 std::string GetNetworkIdFromProperties(
    149     const base::DictionaryValue& properties) {
    150   if (properties.empty())
    151     return "EmptyProperties";
    152   std::string result;
    153   if (properties.GetStringWithoutPathExpansion(shill::kGuidProperty, &result))
    154     return result;
    155   if (properties.GetStringWithoutPathExpansion(shill::kSSIDProperty, &result))
    156     return result;
    157   if (properties.GetStringWithoutPathExpansion(shill::kNameProperty, &result))
    158     return result;
    159   std::string type = "UnknownType";
    160   properties.GetStringWithoutPathExpansion(shill::kTypeProperty, &type);
    161   return "Unidentified " + type;
    162 }
    163 
    164 std::string GetNameFromProperties(const std::string& service_path,
    165                                   const base::DictionaryValue& properties) {
    166   std::string name;
    167   properties.GetStringWithoutPathExpansion(shill::kNameProperty, &name);
    168 
    169   std::string validated_name = ValidateUTF8(name);
    170   if (validated_name != name) {
    171     NET_LOG_DEBUG("GetNameFromProperties",
    172                   base::StringPrintf("Validated name %s: UTF8: %s",
    173                                      service_path.c_str(),
    174                                      validated_name.c_str()));
    175   }
    176 
    177   std::string type;
    178   properties.GetStringWithoutPathExpansion(shill::kTypeProperty, &type);
    179   if (type.empty()) {
    180     NET_LOG_ERROR("GetNameFromProperties: No type", service_path);
    181     return validated_name;
    182   }
    183   if (!NetworkTypePattern::WiFi().MatchesType(type))
    184     return validated_name;
    185 
    186   bool unknown_ssid_encoding = false;
    187   std::string ssid = GetSSIDFromProperties(properties, &unknown_ssid_encoding);
    188   if (ssid.empty())
    189     NET_LOG_ERROR("GetNameFromProperties", "No SSID set: " + service_path);
    190 
    191   // Use |validated_name| if |ssid| is empty.
    192   // And if the encoding of the SSID is unknown, use |ssid|, which contains raw
    193   // bytes in that case, only if |validated_name| is empty.
    194   if (ssid.empty() || (unknown_ssid_encoding && !validated_name.empty()))
    195     return validated_name;
    196 
    197   if (ssid != validated_name) {
    198     NET_LOG_DEBUG("GetNameFromProperties",
    199                   base::StringPrintf("%s: SSID: %s, Name: %s",
    200                                      service_path.c_str(),
    201                                      ssid.c_str(),
    202                                      validated_name.c_str()));
    203   }
    204   return ssid;
    205 }
    206 
    207 scoped_ptr<NetworkUIData> GetUIDataFromValue(const base::Value& ui_data_value) {
    208   std::string ui_data_str;
    209   if (!ui_data_value.GetAsString(&ui_data_str))
    210     return scoped_ptr<NetworkUIData>();
    211   if (ui_data_str.empty())
    212     return make_scoped_ptr(new NetworkUIData());
    213   scoped_ptr<base::DictionaryValue> ui_data_dict(
    214       chromeos::onc::ReadDictionaryFromJson(ui_data_str));
    215   if (!ui_data_dict)
    216     return scoped_ptr<NetworkUIData>();
    217   return make_scoped_ptr(new NetworkUIData(*ui_data_dict));
    218 }
    219 
    220 scoped_ptr<NetworkUIData> GetUIDataFromProperties(
    221     const base::DictionaryValue& shill_dictionary) {
    222   const base::Value* ui_data_value = NULL;
    223   shill_dictionary.GetWithoutPathExpansion(shill::kUIDataProperty,
    224                                            &ui_data_value);
    225   if (!ui_data_value) {
    226     VLOG(2) << "Dictionary has no UIData entry.";
    227     return scoped_ptr<NetworkUIData>();
    228   }
    229   scoped_ptr<NetworkUIData> ui_data = GetUIDataFromValue(*ui_data_value);
    230   if (!ui_data)
    231     LOG(ERROR) << "UIData is not a valid JSON dictionary.";
    232   return ui_data.Pass();
    233 }
    234 
    235 void SetUIData(const NetworkUIData& ui_data,
    236                base::DictionaryValue* shill_dictionary) {
    237   base::DictionaryValue ui_data_dict;
    238   ui_data.FillDictionary(&ui_data_dict);
    239   std::string ui_data_blob;
    240   base::JSONWriter::Write(&ui_data_dict, &ui_data_blob);
    241   shill_dictionary->SetStringWithoutPathExpansion(shill::kUIDataProperty,
    242                                                   ui_data_blob);
    243 }
    244 
    245 bool CopyIdentifyingProperties(const base::DictionaryValue& service_properties,
    246                                const bool properties_read_from_shill,
    247                                base::DictionaryValue* dest) {
    248   bool success = true;
    249 
    250   // GUID is optional.
    251   CopyStringFromDictionary(service_properties, shill::kGuidProperty, dest);
    252 
    253   std::string type;
    254   service_properties.GetStringWithoutPathExpansion(shill::kTypeProperty, &type);
    255   success &= !type.empty();
    256   dest->SetStringWithoutPathExpansion(shill::kTypeProperty, type);
    257   if (type == shill::kTypeWifi) {
    258     std::string security;
    259     service_properties.GetStringWithoutPathExpansion(shill::kSecurityProperty,
    260                                                      &security);
    261     if (security.empty()) {
    262       success = false;
    263     } else {
    264       dest->SetStringWithoutPathExpansion(shill::kSecurityProperty,
    265                                           GetSecurityClass(security));
    266     }
    267     success &=
    268         CopyStringFromDictionary(service_properties, shill::kWifiHexSsid, dest);
    269     success &= CopyStringFromDictionary(
    270         service_properties, shill::kModeProperty, dest);
    271   } else if (type == shill::kTypeVPN) {
    272     success &= CopyStringFromDictionary(
    273         service_properties, shill::kNameProperty, dest);
    274 
    275     // VPN Provider values are read from the "Provider" dictionary, but written
    276     // with the keys "Provider.Type" and "Provider.Host".
    277     // TODO(pneubeck): Simplify this once http://crbug.com/381135 is fixed.
    278     std::string vpn_provider_type;
    279     std::string vpn_provider_host;
    280     if (properties_read_from_shill) {
    281       const base::DictionaryValue* provider_properties = NULL;
    282       if (!service_properties.GetDictionaryWithoutPathExpansion(
    283               shill::kProviderProperty, &provider_properties)) {
    284         NET_LOG_ERROR("Missing VPN provider dict",
    285                       GetNetworkIdFromProperties(service_properties));
    286       }
    287       provider_properties->GetStringWithoutPathExpansion(shill::kTypeProperty,
    288                                                          &vpn_provider_type);
    289       provider_properties->GetStringWithoutPathExpansion(shill::kHostProperty,
    290                                                          &vpn_provider_host);
    291     } else {
    292       service_properties.GetStringWithoutPathExpansion(
    293           shill::kProviderTypeProperty, &vpn_provider_type);
    294       service_properties.GetStringWithoutPathExpansion(
    295           shill::kProviderHostProperty, &vpn_provider_host);
    296     }
    297     success &= !vpn_provider_type.empty();
    298     dest->SetStringWithoutPathExpansion(shill::kProviderTypeProperty,
    299                                         vpn_provider_type);
    300 
    301     success &= !vpn_provider_host.empty();
    302     dest->SetStringWithoutPathExpansion(shill::kProviderHostProperty,
    303                                         vpn_provider_host);
    304   } else if (type == shill::kTypeEthernet || type == shill::kTypeEthernetEap) {
    305     // Ethernet and EthernetEAP don't have any additional identifying
    306     // properties.
    307   } else {
    308     NOTREACHED() << "Unsupported network type " << type;
    309     success = false;
    310   }
    311   if (!success) {
    312     NET_LOG_ERROR("Missing required properties",
    313                   GetNetworkIdFromProperties(service_properties));
    314   }
    315   return success;
    316 }
    317 
    318 bool DoIdentifyingPropertiesMatch(const base::DictionaryValue& new_properties,
    319                                   const base::DictionaryValue& old_properties) {
    320   base::DictionaryValue new_identifying;
    321   if (!CopyIdentifyingProperties(
    322           new_properties,
    323           false /* properties were not read from Shill */,
    324           &new_identifying)) {
    325     return false;
    326   }
    327   base::DictionaryValue old_identifying;
    328   if (!CopyIdentifyingProperties(old_properties,
    329                                  true /* properties were read from Shill */,
    330                                  &old_identifying)) {
    331     return false;
    332   }
    333 
    334   return new_identifying.Equals(&old_identifying);
    335 }
    336 
    337 bool IsPassphraseKey(const std::string& key) {
    338   return key == shill::kEapPrivateKeyPasswordProperty ||
    339       key == shill::kEapPasswordProperty ||
    340       key == shill::kL2tpIpsecPasswordProperty ||
    341       key == shill::kOpenVPNPasswordProperty ||
    342       key == shill::kOpenVPNAuthUserPassProperty ||
    343       key == shill::kOpenVPNTLSAuthContentsProperty ||
    344       key == shill::kPassphraseProperty ||
    345       key == shill::kOpenVPNOTPProperty ||
    346       key == shill::kEapPrivateKeyProperty ||
    347       key == shill::kEapPinProperty ||
    348       key == shill::kApnPasswordProperty;
    349 }
    350 
    351 bool GetHomeProviderFromProperty(const base::Value& value,
    352                                  std::string* home_provider_id) {
    353   const base::DictionaryValue* dict = NULL;
    354   if (!value.GetAsDictionary(&dict))
    355     return false;
    356   std::string home_provider_country;
    357   std::string home_provider_name;
    358   dict->GetStringWithoutPathExpansion(shill::kOperatorCountryKey,
    359                                       &home_provider_country);
    360   dict->GetStringWithoutPathExpansion(shill::kOperatorNameKey,
    361                                       &home_provider_name);
    362   // Set home_provider_id
    363   if (!home_provider_name.empty() && !home_provider_country.empty()) {
    364     *home_provider_id = base::StringPrintf(
    365         "%s (%s)", home_provider_name.c_str(), home_provider_country.c_str());
    366   } else {
    367     if (!dict->GetStringWithoutPathExpansion(shill::kOperatorCodeKey,
    368                                              home_provider_id)) {
    369       return false;
    370     }
    371     LOG(WARNING)
    372         << "Provider name and country not defined, using code instead: "
    373         << *home_provider_id;
    374   }
    375   return true;
    376 }
    377 
    378 }  // namespace shill_property_util
    379 
    380 }  // namespace chromeos
    381