// Copyright 2013 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "chromeos/network/shill_property_util.h" #include #include "base/i18n/icu_encoding_detection.h" #include "base/i18n/icu_string_conversions.h" #include "base/json/json_writer.h" #include "base/strings/string_number_conversions.h" #include "base/strings/string_util.h" #include "base/strings/stringprintf.h" #include "base/strings/utf_string_conversion_utils.h" #include "base/values.h" #include "chromeos/network/network_ui_data.h" #include "chromeos/network/onc/onc_utils.h" #include "components/device_event_log/device_event_log.h" #include "third_party/cros_system_api/dbus/service_constants.h" namespace chromeos { namespace shill_property_util { namespace { // Replace non UTF8 characters in |str| with a replacement character. std::string ValidateUTF8(const std::string& str) { std::string result; for (int32 index = 0; index < static_cast(str.size()); ++index) { uint32 code_point_out; bool is_unicode_char = base::ReadUnicodeCharacter( str.c_str(), str.size(), &index, &code_point_out); const uint32 kFirstNonControlChar = 0x20; if (is_unicode_char && (code_point_out >= kFirstNonControlChar)) { base::WriteUnicodeCharacter(code_point_out, &result); } else { const uint32 kReplacementChar = 0xFFFD; // Puts kReplacementChar if character is a control character [0,0x20) // or is not readable UTF8. base::WriteUnicodeCharacter(kReplacementChar, &result); } } return result; } // If existent and non-empty, copies the string at |key| from |source| to // |dest|. Returns true if the string was copied. bool CopyStringFromDictionary(const base::DictionaryValue& source, const std::string& key, base::DictionaryValue* dest) { std::string string_value; if (!source.GetStringWithoutPathExpansion(key, &string_value) || string_value.empty()) { return false; } dest->SetStringWithoutPathExpansion(key, string_value); return true; } } // namespace void SetSSID(const std::string& ssid, base::DictionaryValue* properties) { std::string hex_ssid = base::HexEncode(ssid.c_str(), ssid.size()); properties->SetStringWithoutPathExpansion(shill::kWifiHexSsid, hex_ssid); } std::string GetSSIDFromProperties(const base::DictionaryValue& properties, bool verbose_logging, bool* unknown_encoding) { if (unknown_encoding) *unknown_encoding = false; // Get name for debugging. std::string name; properties.GetStringWithoutPathExpansion(shill::kNameProperty, &name); std::string hex_ssid; properties.GetStringWithoutPathExpansion(shill::kWifiHexSsid, &hex_ssid); if (hex_ssid.empty()) { if (verbose_logging) NET_LOG(DEBUG) << "GetSSIDFromProperties: No HexSSID set: " << name; return std::string(); } std::string ssid; std::vector raw_ssid_bytes; if (base::HexStringToBytes(hex_ssid, &raw_ssid_bytes)) { ssid = std::string(raw_ssid_bytes.begin(), raw_ssid_bytes.end()); VLOG(2) << "GetSSIDFromProperties: " << name << " HexSsid=" << hex_ssid << " SSID=" << ssid; } else { NET_LOG(ERROR) << "GetSSIDFromProperties: " << name << " Error processing HexSsid: " << hex_ssid; return std::string(); } if (base::IsStringUTF8(ssid)) return ssid; // Detect encoding and convert to UTF-8. std::string encoding; if (!base::DetectEncoding(ssid, &encoding)) { // TODO(stevenjb): This is currently experimental. If we find a case where // base::DetectEncoding() fails, we need to figure out whether we can use // country_code with ConvertToUtf8(). crbug.com/233267. properties.GetStringWithoutPathExpansion(shill::kCountryProperty, &encoding); } std::string utf8_ssid; if (!encoding.empty() && base::ConvertToUtf8AndNormalize(ssid, encoding, &utf8_ssid)) { if (utf8_ssid != ssid) { if (verbose_logging) { NET_LOG(DEBUG) << "GetSSIDFromProperties: " << name << " Encoding=" << encoding << " SSID=" << ssid << " UTF8 SSID=" << utf8_ssid; } } return utf8_ssid; } if (unknown_encoding) *unknown_encoding = true; if (verbose_logging) { NET_LOG(DEBUG) << "GetSSIDFromProperties: " << name << " Unrecognized Encoding=" << encoding; } return ssid; } std::string GetNetworkIdFromProperties( const base::DictionaryValue& properties) { if (properties.empty()) return "EmptyProperties"; std::string result; if (properties.GetStringWithoutPathExpansion(shill::kGuidProperty, &result)) return result; if (properties.GetStringWithoutPathExpansion(shill::kSSIDProperty, &result)) return result; if (properties.GetStringWithoutPathExpansion(shill::kNameProperty, &result)) return result; std::string type = "UnknownType"; properties.GetStringWithoutPathExpansion(shill::kTypeProperty, &type); return "Unidentified " + type; } std::string GetNameFromProperties(const std::string& service_path, const base::DictionaryValue& properties) { std::string name; properties.GetStringWithoutPathExpansion(shill::kNameProperty, &name); std::string validated_name = ValidateUTF8(name); if (validated_name != name) { NET_LOG(DEBUG) << "GetNameFromProperties: " << service_path << " Validated name=" << validated_name << " name=" << name; } std::string type; properties.GetStringWithoutPathExpansion(shill::kTypeProperty, &type); if (type.empty()) { NET_LOG(ERROR) << "GetNameFromProperties: " << service_path << " No type."; return validated_name; } if (!NetworkTypePattern::WiFi().MatchesType(type)) return validated_name; bool unknown_ssid_encoding = false; std::string ssid = GetSSIDFromProperties( properties, true /* verbose_logging */, &unknown_ssid_encoding); if (ssid.empty()) { NET_LOG(ERROR) << "GetNameFromProperties: " << service_path << " No SSID set"; } // Use |validated_name| if |ssid| is empty. // And if the encoding of the SSID is unknown, use |ssid|, which contains raw // bytes in that case, only if |validated_name| is empty. if (ssid.empty() || (unknown_ssid_encoding && !validated_name.empty())) return validated_name; if (ssid != validated_name) { NET_LOG(DEBUG) << "GetNameFromProperties: " << service_path << " SSID=" << ssid << " Validated name=" << validated_name; } return ssid; } scoped_ptr GetUIDataFromValue(const base::Value& ui_data_value) { std::string ui_data_str; if (!ui_data_value.GetAsString(&ui_data_str)) return scoped_ptr(); if (ui_data_str.empty()) return make_scoped_ptr(new NetworkUIData()); scoped_ptr ui_data_dict( chromeos::onc::ReadDictionaryFromJson(ui_data_str)); if (!ui_data_dict) return scoped_ptr(); return make_scoped_ptr(new NetworkUIData(*ui_data_dict)); } scoped_ptr GetUIDataFromProperties( const base::DictionaryValue& shill_dictionary) { const base::Value* ui_data_value = NULL; shill_dictionary.GetWithoutPathExpansion(shill::kUIDataProperty, &ui_data_value); if (!ui_data_value) { VLOG(2) << "Dictionary has no UIData entry."; return scoped_ptr(); } scoped_ptr ui_data = GetUIDataFromValue(*ui_data_value); if (!ui_data) LOG(ERROR) << "UIData is not a valid JSON dictionary."; return ui_data.Pass(); } void SetUIData(const NetworkUIData& ui_data, base::DictionaryValue* shill_dictionary) { base::DictionaryValue ui_data_dict; ui_data.FillDictionary(&ui_data_dict); std::string ui_data_blob; base::JSONWriter::Write(ui_data_dict, &ui_data_blob); shill_dictionary->SetStringWithoutPathExpansion(shill::kUIDataProperty, ui_data_blob); } bool CopyIdentifyingProperties(const base::DictionaryValue& service_properties, const bool properties_read_from_shill, base::DictionaryValue* dest) { bool success = true; // GUID is optional. CopyStringFromDictionary(service_properties, shill::kGuidProperty, dest); std::string type; service_properties.GetStringWithoutPathExpansion(shill::kTypeProperty, &type); success &= !type.empty(); dest->SetStringWithoutPathExpansion(shill::kTypeProperty, type); if (type == shill::kTypeWifi) { success &= CopyStringFromDictionary( service_properties, shill::kSecurityClassProperty, dest); success &= CopyStringFromDictionary(service_properties, shill::kWifiHexSsid, dest); success &= CopyStringFromDictionary( service_properties, shill::kModeProperty, dest); } else if (type == shill::kTypeCellular) { success &= CopyStringFromDictionary( service_properties, shill::kNetworkTechnologyProperty, dest); } else if (type == shill::kTypeVPN) { success &= CopyStringFromDictionary( service_properties, shill::kNameProperty, dest); // VPN Provider values are read from the "Provider" dictionary, but written // with the keys "Provider.Type" and "Provider.Host". // TODO(pneubeck): Simplify this once http://crbug.com/381135 is fixed. std::string vpn_provider_type; std::string vpn_provider_host; if (properties_read_from_shill) { const base::DictionaryValue* provider_properties = NULL; if (!service_properties.GetDictionaryWithoutPathExpansion( shill::kProviderProperty, &provider_properties)) { NET_LOG(ERROR) << "Missing VPN provider dict: " << GetNetworkIdFromProperties(service_properties); } provider_properties->GetStringWithoutPathExpansion(shill::kTypeProperty, &vpn_provider_type); provider_properties->GetStringWithoutPathExpansion(shill::kHostProperty, &vpn_provider_host); } else { service_properties.GetStringWithoutPathExpansion( shill::kProviderTypeProperty, &vpn_provider_type); service_properties.GetStringWithoutPathExpansion( shill::kProviderHostProperty, &vpn_provider_host); } success &= !vpn_provider_type.empty(); dest->SetStringWithoutPathExpansion(shill::kProviderTypeProperty, vpn_provider_type); success &= !vpn_provider_host.empty(); dest->SetStringWithoutPathExpansion(shill::kProviderHostProperty, vpn_provider_host); } else if (type == shill::kTypeEthernet || type == shill::kTypeEthernetEap) { // Ethernet and EthernetEAP don't have any additional identifying // properties. } else { NOTREACHED() << "Unsupported network type " << type; success = false; } if (!success) { NET_LOG(ERROR) << "Missing required properties: " << GetNetworkIdFromProperties(service_properties); } return success; } bool DoIdentifyingPropertiesMatch(const base::DictionaryValue& new_properties, const base::DictionaryValue& old_properties) { base::DictionaryValue new_identifying; if (!CopyIdentifyingProperties( new_properties, false /* properties were not read from Shill */, &new_identifying)) { return false; } base::DictionaryValue old_identifying; if (!CopyIdentifyingProperties(old_properties, true /* properties were read from Shill */, &old_identifying)) { return false; } return new_identifying.Equals(&old_identifying); } bool IsLoggableShillProperty(const std::string& key) { static std::set* s_skip_properties = nullptr; if (!s_skip_properties) { s_skip_properties = new std::set; s_skip_properties->insert(shill::kApnPasswordProperty); s_skip_properties->insert(shill::kEapCaCertNssProperty); s_skip_properties->insert(shill::kEapCaCertPemProperty); s_skip_properties->insert(shill::kEapCaCertProperty); s_skip_properties->insert(shill::kEapClientCertNssProperty); s_skip_properties->insert(shill::kEapClientCertProperty); s_skip_properties->insert(shill::kEapPasswordProperty); s_skip_properties->insert(shill::kEapPinProperty); s_skip_properties->insert(shill::kEapPrivateKeyPasswordProperty); s_skip_properties->insert(shill::kEapPrivateKeyProperty); s_skip_properties->insert(shill::kL2tpIpsecCaCertPemProperty); s_skip_properties->insert(shill::kL2tpIpsecPasswordProperty); s_skip_properties->insert(shill::kL2tpIpsecPinProperty); s_skip_properties->insert(shill::kL2tpIpsecPskProperty); s_skip_properties->insert(shill::kOpenVPNAuthUserPassProperty); s_skip_properties->insert(shill::kOpenVPNCaCertNSSProperty); s_skip_properties->insert(shill::kOpenVPNCaCertPemProperty); s_skip_properties->insert(shill::kOpenVPNCaCertProperty); s_skip_properties->insert(shill::kOpenVPNCertProperty); s_skip_properties->insert(shill::kOpenVPNExtraCertPemProperty); s_skip_properties->insert(shill::kOpenVPNOTPProperty); s_skip_properties->insert(shill::kOpenVPNPasswordProperty); s_skip_properties->insert(shill::kOpenVPNPinProperty); s_skip_properties->insert(shill::kOpenVPNTLSAuthContentsProperty); s_skip_properties->insert(shill::kPPPoEPasswordProperty); s_skip_properties->insert(shill::kPassphraseProperty); } return s_skip_properties->count(key) == 0; } bool GetHomeProviderFromProperty(const base::Value& value, std::string* home_provider_id) { const base::DictionaryValue* dict = NULL; if (!value.GetAsDictionary(&dict)) return false; std::string home_provider_country; std::string home_provider_name; dict->GetStringWithoutPathExpansion(shill::kOperatorCountryKey, &home_provider_country); dict->GetStringWithoutPathExpansion(shill::kOperatorNameKey, &home_provider_name); // Set home_provider_id if (!home_provider_name.empty() && !home_provider_country.empty()) { *home_provider_id = base::StringPrintf( "%s (%s)", home_provider_name.c_str(), home_provider_country.c_str()); } else { if (!dict->GetStringWithoutPathExpansion(shill::kOperatorCodeKey, home_provider_id)) { return false; } LOG(WARNING) << "Provider name and country not defined, using code instead: " << *home_provider_id; } return true; } } // namespace shill_property_util } // namespace chromeos