• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.
ValidateUTF8(const std::string & str)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.
CopyStringFromDictionary(const base::DictionaryValue & source,const std::string & key,base::DictionaryValue * dest)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.
GetSecurityClass(const std::string & security)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 
SetSSID(const std::string ssid,base::DictionaryValue * properties)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 
GetSSIDFromProperties(const base::DictionaryValue & properties,bool * unknown_encoding)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 
GetNetworkIdFromProperties(const base::DictionaryValue & properties)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 
GetNameFromProperties(const std::string & service_path,const base::DictionaryValue & properties)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 
GetUIDataFromValue(const base::Value & ui_data_value)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 
GetUIDataFromProperties(const base::DictionaryValue & shill_dictionary)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 
SetUIData(const NetworkUIData & ui_data,base::DictionaryValue * shill_dictionary)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 
CopyIdentifyingProperties(const base::DictionaryValue & service_properties,const bool properties_read_from_shill,base::DictionaryValue * dest)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 
DoIdentifyingPropertiesMatch(const base::DictionaryValue & new_properties,const base::DictionaryValue & old_properties)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 
IsPassphraseKey(const std::string & key)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 
GetHomeProviderFromProperty(const base::Value & value,std::string * home_provider_id)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