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