1 // Copyright (c) 2012 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/onc/onc_translator.h" 6 7 #include <string> 8 9 #include "base/basictypes.h" 10 #include "base/json/json_reader.h" 11 #include "base/json/json_writer.h" 12 #include "base/logging.h" 13 #include "base/values.h" 14 #include "chromeos/network/network_state.h" 15 #include "chromeos/network/onc/onc_signature.h" 16 #include "chromeos/network/onc/onc_translation_tables.h" 17 #include "chromeos/network/shill_property_util.h" 18 #include "components/onc/onc_constants.h" 19 #include "third_party/cros_system_api/dbus/service_constants.h" 20 21 namespace chromeos { 22 namespace onc { 23 24 namespace { 25 26 // Converts |str| to a base::Value of the given |type|. If the conversion fails, 27 // returns NULL. 28 scoped_ptr<base::Value> ConvertStringToValue(const std::string& str, 29 base::Value::Type type) { 30 base::Value* value; 31 if (type == base::Value::TYPE_STRING) { 32 value = base::Value::CreateStringValue(str); 33 } else { 34 value = base::JSONReader::Read(str); 35 } 36 37 if (value == NULL || value->GetType() != type) { 38 delete value; 39 value = NULL; 40 } 41 return make_scoped_ptr(value); 42 } 43 44 // This class implements the translation of properties from the given 45 // |shill_dictionary| to a new ONC object of signature |onc_signature|. Using 46 // recursive calls to CreateTranslatedONCObject of new instances, nested objects 47 // are translated. 48 class ShillToONCTranslator { 49 public: 50 ShillToONCTranslator(const base::DictionaryValue& shill_dictionary, 51 const OncValueSignature& onc_signature) 52 : shill_dictionary_(&shill_dictionary), 53 onc_signature_(&onc_signature) { 54 field_translation_table_ = GetFieldTranslationTable(onc_signature); 55 } 56 57 // Translates the associated Shill dictionary and creates an ONC object of the 58 // given signature. 59 scoped_ptr<base::DictionaryValue> CreateTranslatedONCObject(); 60 61 private: 62 void TranslateEthernet(); 63 void TranslateOpenVPN(); 64 void TranslateVPN(); 65 void TranslateWiFiWithState(); 66 void TranslateCellularWithState(); 67 void TranslateNetworkWithState(); 68 69 // Creates an ONC object from |dictionary| according to the signature 70 // associated to |onc_field_name| and adds it to |onc_object_| at 71 // |onc_field_name|. 72 void TranslateAndAddNestedObject(const std::string& onc_field_name, 73 const base::DictionaryValue& dictionary); 74 75 // Creates an ONC object from |shill_dictionary_| according to the signature 76 // associated to |onc_field_name| and adds it to |onc_object_| at 77 // |onc_field_name|. 78 void TranslateAndAddNestedObject(const std::string& onc_field_name); 79 80 // Translates a list of nested objects and adds the list to |onc_object_| at 81 // |onc_field_name|. If there are errors while parsing individual objects or 82 // if the resulting list contains no entries, the result will not be added to 83 // |onc_object_|. 84 void TranslateAndAddListOfObjects(const std::string& onc_field_name, 85 const base::ListValue& list); 86 87 // Applies function CopyProperty to each field of |value_signature| and its 88 // base signatures. 89 void CopyPropertiesAccordingToSignature( 90 const OncValueSignature* value_signature); 91 92 // Applies function CopyProperty to each field of |onc_signature_| and its 93 // base signatures. 94 void CopyPropertiesAccordingToSignature(); 95 96 // If |shill_property_name| is defined in |field_signature|, copies this 97 // entry from |shill_dictionary_| to |onc_object_| if it exists. 98 void CopyProperty(const OncFieldSignature* field_signature); 99 100 // If existent, translates the entry at |shill_property_name| in 101 // |shill_dictionary_| using |table|. It is an error if no matching table 102 // entry is found. Writes the result as entry at |onc_field_name| in 103 // |onc_object_|. 104 void TranslateWithTableAndSet(const std::string& shill_property_name, 105 const StringTranslationEntry table[], 106 const std::string& onc_field_name); 107 108 const base::DictionaryValue* shill_dictionary_; 109 const OncValueSignature* onc_signature_; 110 const FieldTranslationEntry* field_translation_table_; 111 scoped_ptr<base::DictionaryValue> onc_object_; 112 113 DISALLOW_COPY_AND_ASSIGN(ShillToONCTranslator); 114 }; 115 116 scoped_ptr<base::DictionaryValue> 117 ShillToONCTranslator::CreateTranslatedONCObject() { 118 onc_object_.reset(new base::DictionaryValue); 119 if (onc_signature_ == &kNetworkWithStateSignature) { 120 TranslateNetworkWithState(); 121 } else if (onc_signature_ == &kEthernetSignature) { 122 TranslateEthernet(); 123 } else if (onc_signature_ == &kVPNSignature) { 124 TranslateVPN(); 125 } else if (onc_signature_ == &kOpenVPNSignature) { 126 TranslateOpenVPN(); 127 } else if (onc_signature_ == &kWiFiWithStateSignature) { 128 TranslateWiFiWithState(); 129 } else if (onc_signature_ == &kCellularWithStateSignature) { 130 TranslateCellularWithState(); 131 } else { 132 CopyPropertiesAccordingToSignature(); 133 } 134 return onc_object_.Pass(); 135 } 136 137 void ShillToONCTranslator::TranslateEthernet() { 138 std::string shill_network_type; 139 shill_dictionary_->GetStringWithoutPathExpansion(shill::kTypeProperty, 140 &shill_network_type); 141 const char* onc_auth = ::onc::ethernet::kNone; 142 if (shill_network_type == shill::kTypeEthernetEap) 143 onc_auth = ::onc::ethernet::k8021X; 144 onc_object_->SetStringWithoutPathExpansion(::onc::ethernet::kAuthentication, 145 onc_auth); 146 } 147 148 void ShillToONCTranslator::TranslateOpenVPN() { 149 if (shill_dictionary_->HasKey(shill::kOpenVPNVerifyX509NameProperty)) 150 TranslateAndAddNestedObject(::onc::openvpn::kVerifyX509); 151 152 // Shill supports only one RemoteCertKU but ONC requires a list. If existing, 153 // wraps the value into a list. 154 std::string certKU; 155 if (shill_dictionary_->GetStringWithoutPathExpansion( 156 shill::kOpenVPNRemoteCertKUProperty, &certKU)) { 157 scoped_ptr<base::ListValue> certKUs(new base::ListValue); 158 certKUs->AppendString(certKU); 159 onc_object_->SetWithoutPathExpansion(::onc::openvpn::kRemoteCertKU, 160 certKUs.release()); 161 } 162 163 for (const OncFieldSignature* field_signature = onc_signature_->fields; 164 field_signature->onc_field_name != NULL; ++field_signature) { 165 const std::string& onc_field_name = field_signature->onc_field_name; 166 if (onc_field_name == ::onc::vpn::kSaveCredentials || 167 onc_field_name == ::onc::openvpn::kRemoteCertKU || 168 onc_field_name == ::onc::openvpn::kServerCAPEMs) { 169 CopyProperty(field_signature); 170 continue; 171 } 172 173 std::string shill_property_name; 174 const base::Value* shill_value = NULL; 175 if (!field_translation_table_ || 176 !GetShillPropertyName(field_signature->onc_field_name, 177 field_translation_table_, 178 &shill_property_name) || 179 !shill_dictionary_->GetWithoutPathExpansion(shill_property_name, 180 &shill_value)) { 181 continue; 182 } 183 184 scoped_ptr<base::Value> translated; 185 std::string shill_str; 186 if (shill_value->GetAsString(&shill_str)) { 187 // Shill wants all Provider/VPN fields to be strings. Translates these 188 // strings back to the correct ONC type. 189 translated = ConvertStringToValue( 190 shill_str, 191 field_signature->value_signature->onc_type); 192 193 if (translated.get() == NULL) { 194 LOG(ERROR) << "Shill property '" << shill_property_name 195 << "' with value " << *shill_value 196 << " couldn't be converted to base::Value::Type " 197 << field_signature->value_signature->onc_type; 198 } else { 199 onc_object_->SetWithoutPathExpansion(onc_field_name, 200 translated.release()); 201 } 202 } else { 203 LOG(ERROR) << "Shill property '" << shill_property_name 204 << "' has value " << *shill_value 205 << ", but expected a string"; 206 } 207 } 208 } 209 210 void ShillToONCTranslator::TranslateVPN() { 211 TranslateWithTableAndSet( 212 shill::kProviderTypeProperty, kVPNTypeTable, ::onc::vpn::kType); 213 CopyPropertiesAccordingToSignature(); 214 215 std::string vpn_type; 216 if (onc_object_->GetStringWithoutPathExpansion(::onc::vpn::kType, 217 &vpn_type)) { 218 if (vpn_type == ::onc::vpn::kTypeL2TP_IPsec) { 219 TranslateAndAddNestedObject(::onc::vpn::kIPsec); 220 TranslateAndAddNestedObject(::onc::vpn::kL2TP); 221 } else { 222 TranslateAndAddNestedObject(vpn_type); 223 } 224 } 225 } 226 227 void ShillToONCTranslator::TranslateWiFiWithState() { 228 TranslateWithTableAndSet( 229 shill::kSecurityProperty, kWiFiSecurityTable, ::onc::wifi::kSecurity); 230 std::string ssid = shill_property_util::GetSSIDFromProperties( 231 *shill_dictionary_, NULL /* ignore unknown encoding */); 232 if (!ssid.empty()) 233 onc_object_->SetStringWithoutPathExpansion(::onc::wifi::kSSID, ssid); 234 CopyPropertiesAccordingToSignature(); 235 } 236 237 void ShillToONCTranslator::TranslateCellularWithState() { 238 CopyPropertiesAccordingToSignature(); 239 const base::DictionaryValue* dictionary = NULL; 240 if (shill_dictionary_->GetDictionaryWithoutPathExpansion( 241 shill::kServingOperatorProperty, &dictionary)) { 242 TranslateAndAddNestedObject(::onc::cellular::kServingOperator, *dictionary); 243 } 244 if (shill_dictionary_->GetDictionaryWithoutPathExpansion( 245 shill::kCellularApnProperty, &dictionary)) { 246 TranslateAndAddNestedObject(::onc::cellular::kAPN, *dictionary); 247 } 248 const base::ListValue* list = NULL; 249 if (shill_dictionary_->GetListWithoutPathExpansion( 250 shill::kCellularApnListProperty, &list)) { 251 TranslateAndAddListOfObjects(::onc::cellular::kAPNList, *list); 252 } 253 } 254 255 void ShillToONCTranslator::TranslateNetworkWithState() { 256 CopyPropertiesAccordingToSignature(); 257 258 std::string shill_network_type; 259 shill_dictionary_->GetStringWithoutPathExpansion(shill::kTypeProperty, 260 &shill_network_type); 261 std::string onc_network_type = ::onc::network_type::kEthernet; 262 if (shill_network_type != shill::kTypeEthernet && 263 shill_network_type != shill::kTypeEthernetEap) { 264 TranslateStringToONC( 265 kNetworkTypeTable, shill_network_type, &onc_network_type); 266 } 267 if (!onc_network_type.empty()) { 268 onc_object_->SetStringWithoutPathExpansion(::onc::network_config::kType, 269 onc_network_type); 270 TranslateAndAddNestedObject(onc_network_type); 271 } 272 273 // Since Name is a read only field in Shill unless it's a VPN, it is copied 274 // here, but not when going the other direction (if it's not a VPN). 275 std::string name; 276 shill_dictionary_->GetStringWithoutPathExpansion(shill::kNameProperty, 277 &name); 278 onc_object_->SetStringWithoutPathExpansion(::onc::network_config::kName, 279 name); 280 281 std::string state; 282 if (shill_dictionary_->GetStringWithoutPathExpansion(shill::kStateProperty, 283 &state)) { 284 std::string onc_state = ::onc::connection_state::kNotConnected; 285 if (NetworkState::StateIsConnected(state)) { 286 onc_state = ::onc::connection_state::kConnected; 287 } else if (NetworkState::StateIsConnecting(state)) { 288 onc_state = ::onc::connection_state::kConnecting; 289 } 290 onc_object_->SetStringWithoutPathExpansion( 291 ::onc::network_config::kConnectionState, onc_state); 292 } 293 } 294 295 void ShillToONCTranslator::TranslateAndAddNestedObject( 296 const std::string& onc_field_name) { 297 TranslateAndAddNestedObject(onc_field_name, *shill_dictionary_); 298 } 299 300 void ShillToONCTranslator::TranslateAndAddNestedObject( 301 const std::string& onc_field_name, 302 const base::DictionaryValue& dictionary) { 303 const OncFieldSignature* field_signature = 304 GetFieldSignature(*onc_signature_, onc_field_name); 305 ShillToONCTranslator nested_translator(dictionary, 306 *field_signature->value_signature); 307 scoped_ptr<base::DictionaryValue> nested_object = 308 nested_translator.CreateTranslatedONCObject(); 309 if (nested_object->empty()) 310 return; 311 onc_object_->SetWithoutPathExpansion(onc_field_name, nested_object.release()); 312 } 313 314 void ShillToONCTranslator::TranslateAndAddListOfObjects( 315 const std::string& onc_field_name, 316 const base::ListValue& list) { 317 const OncFieldSignature* field_signature = 318 GetFieldSignature(*onc_signature_, onc_field_name); 319 if (field_signature->value_signature->onc_type != Value::TYPE_LIST) { 320 LOG(ERROR) << "ONC Field name: '" << onc_field_name << "' has type '" 321 << field_signature->value_signature->onc_type 322 << "', expected: base::Value::TYPE_LIST."; 323 return; 324 } 325 DCHECK(field_signature->value_signature->onc_array_entry_signature); 326 scoped_ptr<base::ListValue> result(new base::ListValue()); 327 for (base::ListValue::const_iterator it = list.begin(); 328 it != list.end(); ++it) { 329 const base::DictionaryValue* shill_value = NULL; 330 if (!(*it)->GetAsDictionary(&shill_value)) 331 continue; 332 ShillToONCTranslator nested_translator( 333 *shill_value, 334 *field_signature->value_signature->onc_array_entry_signature); 335 scoped_ptr<base::DictionaryValue> nested_object = 336 nested_translator.CreateTranslatedONCObject(); 337 if (nested_object->empty()) 338 // The nested object couldn't be parsed, so simply omit it. 339 continue; 340 result->Append(nested_object.release()); 341 } 342 if (result->empty()) 343 // There are no entries in the list, so there is no need to expose this 344 // field. 345 return; 346 onc_object_->SetWithoutPathExpansion(onc_field_name, result.release()); 347 } 348 349 void ShillToONCTranslator::CopyPropertiesAccordingToSignature() { 350 CopyPropertiesAccordingToSignature(onc_signature_); 351 } 352 353 void ShillToONCTranslator::CopyPropertiesAccordingToSignature( 354 const OncValueSignature* value_signature) { 355 if (value_signature->base_signature) 356 CopyPropertiesAccordingToSignature(value_signature->base_signature); 357 for (const OncFieldSignature* field_signature = value_signature->fields; 358 field_signature->onc_field_name != NULL; ++field_signature) { 359 CopyProperty(field_signature); 360 } 361 } 362 363 void ShillToONCTranslator::CopyProperty( 364 const OncFieldSignature* field_signature) { 365 std::string shill_property_name; 366 const base::Value* shill_value = NULL; 367 if (!field_translation_table_ || 368 !GetShillPropertyName(field_signature->onc_field_name, 369 field_translation_table_, 370 &shill_property_name) || 371 !shill_dictionary_->GetWithoutPathExpansion(shill_property_name, 372 &shill_value)) { 373 return; 374 } 375 376 if (shill_value->GetType() != field_signature->value_signature->onc_type) { 377 LOG(ERROR) << "Shill property '" << shill_property_name 378 << "' with value " << *shill_value 379 << " has base::Value::Type " << shill_value->GetType() 380 << " but ONC field '" << field_signature->onc_field_name 381 << "' requires type " 382 << field_signature->value_signature->onc_type << "."; 383 return; 384 } 385 386 onc_object_->SetWithoutPathExpansion(field_signature->onc_field_name, 387 shill_value->DeepCopy()); 388 } 389 390 void ShillToONCTranslator::TranslateWithTableAndSet( 391 const std::string& shill_property_name, 392 const StringTranslationEntry table[], 393 const std::string& onc_field_name) { 394 std::string shill_value; 395 if (!shill_dictionary_->GetStringWithoutPathExpansion(shill_property_name, 396 &shill_value)) { 397 return; 398 } 399 std::string onc_value; 400 if (TranslateStringToONC(table, shill_value, &onc_value)) { 401 onc_object_->SetStringWithoutPathExpansion(onc_field_name, onc_value); 402 return; 403 } 404 LOG(ERROR) << "Shill property '" << shill_property_name << "' with value " 405 << shill_value << " couldn't be translated to ONC"; 406 } 407 408 } // namespace 409 410 scoped_ptr<base::DictionaryValue> TranslateShillServiceToONCPart( 411 const base::DictionaryValue& shill_dictionary, 412 const OncValueSignature* onc_signature) { 413 CHECK(onc_signature != NULL); 414 415 ShillToONCTranslator translator(shill_dictionary, *onc_signature); 416 return translator.CreateTranslatedONCObject(); 417 } 418 419 } // namespace onc 420 } // namespace chromeos 421