• 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   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 
GetNetworkIdFromProperties(const base::DictionaryValue & properties)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 
GetNameFromProperties(const std::string & service_path,const base::DictionaryValue & properties)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 
GetUIDataFromValue(const base::Value & ui_data_value)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 
GetUIDataFromProperties(const base::DictionaryValue & shill_dictionary)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 
SetUIData(const NetworkUIData & ui_data,base::DictionaryValue * shill_dictionary)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 
CopyIdentifyingProperties(const base::DictionaryValue & service_properties,const bool properties_read_from_shill,base::DictionaryValue * dest)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 
DoIdentifyingPropertiesMatch(const base::DictionaryValue & new_properties,const base::DictionaryValue & old_properties)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 
IsPassphraseKey(const std::string & key)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 
GetHomeProviderFromProperty(const base::Value & value,std::string * home_provider_id)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