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/network_configuration_handler.h" 6 7 #include <string> 8 #include <vector> 9 10 #include "base/bind.h" 11 #include "base/format_macros.h" 12 #include "base/json/json_writer.h" 13 #include "base/logging.h" 14 #include "base/memory/ref_counted.h" 15 #include "base/memory/scoped_ptr.h" 16 #include "base/stl_util.h" 17 #include "base/strings/stringprintf.h" 18 #include "base/values.h" 19 #include "chromeos/dbus/dbus_thread_manager.h" 20 #include "chromeos/dbus/shill_manager_client.h" 21 #include "chromeos/dbus/shill_profile_client.h" 22 #include "chromeos/dbus/shill_service_client.h" 23 #include "chromeos/network/network_event_log.h" 24 #include "chromeos/network/network_state_handler.h" 25 #include "chromeos/network/shill_property_util.h" 26 #include "dbus/object_path.h" 27 #include "third_party/cros_system_api/dbus/service_constants.h" 28 29 namespace chromeos { 30 31 namespace { 32 33 // Strip surrounding "" from keys (if present). 34 std::string StripQuotations(const std::string& in_str) { 35 size_t len = in_str.length(); 36 if (len >= 2 && in_str[0] == '"' && in_str[len-1] == '"') 37 return in_str.substr(1, len-2); 38 return in_str; 39 } 40 41 void InvokeErrorCallback(const std::string& service_path, 42 const network_handler::ErrorCallback& error_callback, 43 const std::string& error_name) { 44 std::string error_msg = "Config Error: " + error_name; 45 NET_LOG_ERROR(error_msg, service_path); 46 network_handler::RunErrorCallback( 47 error_callback, service_path, error_name, error_msg); 48 } 49 50 void GetPropertiesCallback( 51 const network_handler::DictionaryResultCallback& callback, 52 const network_handler::ErrorCallback& error_callback, 53 const std::string& service_path, 54 DBusMethodCallStatus call_status, 55 const base::DictionaryValue& properties) { 56 // Get the correct name from WifiHex if necessary. 57 scoped_ptr<base::DictionaryValue> properties_copy(properties.DeepCopy()); 58 std::string name = 59 shill_property_util::GetNameFromProperties(service_path, properties); 60 if (!name.empty()) { 61 properties_copy->SetStringWithoutPathExpansion(shill::kNameProperty, name); 62 } 63 if (call_status != DBUS_METHOD_CALL_SUCCESS) { 64 // Because network services are added and removed frequently, we will see 65 // failures regularly, so don't log these. 66 network_handler::RunErrorCallback(error_callback, 67 service_path, 68 network_handler::kDBusFailedError, 69 network_handler::kDBusFailedErrorMessage); 70 } else if (!callback.is_null()) { 71 callback.Run(service_path, *properties_copy.get()); 72 } 73 } 74 75 void SetNetworkProfileErrorCallback( 76 const std::string& service_path, 77 const std::string& profile_path, 78 const network_handler::ErrorCallback& error_callback, 79 const std::string& dbus_error_name, 80 const std::string& dbus_error_message) { 81 network_handler::ShillErrorCallbackFunction( 82 "Config.SetNetworkProfile Failed: " + profile_path, 83 service_path, error_callback, 84 dbus_error_name, dbus_error_message); 85 } 86 87 bool IsPassphrase(const std::string& key) { 88 return key == shill::kEapPrivateKeyPasswordProperty || 89 key == shill::kEapPasswordProperty || 90 key == shill::kL2tpIpsecPasswordProperty || 91 key == shill::kOpenVPNPasswordProperty || 92 key == shill::kPassphraseProperty || 93 key == shill::kOpenVPNOTPProperty || 94 key == shill::kEapPrivateKeyProperty || 95 key == shill::kEapPinProperty || 96 key == shill::kApnPasswordProperty; 97 } 98 99 void LogConfigProperties(const std::string& desc, 100 const std::string& path, 101 const base::DictionaryValue& properties) { 102 for (base::DictionaryValue::Iterator iter(properties); 103 !iter.IsAtEnd(); iter.Advance()) { 104 std::string v = "******"; 105 if (!IsPassphrase(iter.key())) 106 base::JSONWriter::Write(&iter.value(), &v); 107 NET_LOG_DEBUG(desc, path + "." + iter.key() + "=" + v); 108 } 109 } 110 111 } // namespace 112 113 // Helper class to request from Shill the profile entries associated with a 114 // Service and delete the service from each profile. Triggers either 115 // |callback| on success or |error_callback| on failure, and calls 116 // |handler|->ProfileEntryDeleterCompleted() on completion to delete itself. 117 class NetworkConfigurationHandler::ProfileEntryDeleter 118 : public base::SupportsWeakPtr<ProfileEntryDeleter> { 119 public: 120 ProfileEntryDeleter(NetworkConfigurationHandler* handler, 121 const std::string& service_path, 122 const base::Closure& callback, 123 const network_handler::ErrorCallback& error_callback) 124 : owner_(handler), 125 service_path_(service_path), 126 callback_(callback), 127 error_callback_(error_callback) { 128 } 129 130 void Run() { 131 DBusThreadManager::Get()->GetShillServiceClient()-> 132 GetLoadableProfileEntries( 133 dbus::ObjectPath(service_path_), 134 base::Bind(&ProfileEntryDeleter::GetProfileEntriesToDeleteCallback, 135 AsWeakPtr())); 136 } 137 138 private: 139 void GetProfileEntriesToDeleteCallback( 140 DBusMethodCallStatus call_status, 141 const base::DictionaryValue& profile_entries) { 142 if (call_status != DBUS_METHOD_CALL_SUCCESS) { 143 InvokeErrorCallback( 144 service_path_, error_callback_, "GetLoadableProfileEntriesFailed"); 145 owner_->ProfileEntryDeleterCompleted(service_path_); // Deletes this. 146 return; 147 } 148 149 for (base::DictionaryValue::Iterator iter(profile_entries); 150 !iter.IsAtEnd(); iter.Advance()) { 151 std::string profile_path = StripQuotations(iter.key()); 152 std::string entry_path; 153 iter.value().GetAsString(&entry_path); 154 if (profile_path.empty() || entry_path.empty()) { 155 NET_LOG_ERROR("Failed to parse Profile Entry", base::StringPrintf( 156 "%s: %s", profile_path.c_str(), entry_path.c_str())); 157 continue; 158 } 159 if (profile_delete_entries_.count(profile_path) != 0) { 160 NET_LOG_ERROR("Multiple Profile Entries", base::StringPrintf( 161 "%s: %s", profile_path.c_str(), entry_path.c_str())); 162 continue; 163 } 164 NET_LOG_DEBUG("Delete Profile Entry", base::StringPrintf( 165 "%s: %s", profile_path.c_str(), entry_path.c_str())); 166 profile_delete_entries_[profile_path] = entry_path; 167 DBusThreadManager::Get()->GetShillProfileClient()->DeleteEntry( 168 dbus::ObjectPath(profile_path), 169 entry_path, 170 base::Bind(&ProfileEntryDeleter::ProfileEntryDeletedCallback, 171 AsWeakPtr(), profile_path, entry_path), 172 base::Bind(&ProfileEntryDeleter::ShillErrorCallback, 173 AsWeakPtr(), profile_path, entry_path)); 174 } 175 } 176 177 void ProfileEntryDeletedCallback(const std::string& profile_path, 178 const std::string& entry) { 179 NET_LOG_DEBUG("Profile Entry Deleted", base::StringPrintf( 180 "%s: %s", profile_path.c_str(), entry.c_str())); 181 profile_delete_entries_.erase(profile_path); 182 if (!profile_delete_entries_.empty()) 183 return; 184 // Run the callback if this is the last pending deletion. 185 if (!callback_.is_null()) 186 callback_.Run(); 187 // Request NetworkStateHandler manager update to update ServiceCompleteList. 188 owner_->network_state_handler_->UpdateManagerProperties(); 189 owner_->ProfileEntryDeleterCompleted(service_path_); // Deletes this. 190 } 191 192 void ShillErrorCallback(const std::string& profile_path, 193 const std::string& entry, 194 const std::string& dbus_error_name, 195 const std::string& dbus_error_message) { 196 // Any Shill Error triggers a failure / error. 197 network_handler::ShillErrorCallbackFunction( 198 "GetLoadableProfileEntries Failed", profile_path, error_callback_, 199 dbus_error_name, dbus_error_message); 200 // Delete this even if there are pending deletions; any callbacks will 201 // safely become no-ops (by invalidating the WeakPtrs). 202 owner_->ProfileEntryDeleterCompleted(service_path_); // Deletes this. 203 } 204 205 NetworkConfigurationHandler* owner_; // Unowned 206 std::string service_path_; 207 base::Closure callback_; 208 network_handler::ErrorCallback error_callback_; 209 210 // Map of pending profile entry deletions, indexed by profile path. 211 std::map<std::string, std::string> profile_delete_entries_; 212 213 DISALLOW_COPY_AND_ASSIGN(ProfileEntryDeleter); 214 }; 215 216 // NetworkConfigurationHandler 217 218 void NetworkConfigurationHandler::GetProperties( 219 const std::string& service_path, 220 const network_handler::DictionaryResultCallback& callback, 221 const network_handler::ErrorCallback& error_callback) const { 222 DBusThreadManager::Get()->GetShillServiceClient()->GetProperties( 223 dbus::ObjectPath(service_path), 224 base::Bind(&GetPropertiesCallback, 225 callback, error_callback, service_path)); 226 } 227 228 void NetworkConfigurationHandler::SetProperties( 229 const std::string& service_path, 230 const base::DictionaryValue& properties, 231 const base::Closure& callback, 232 const network_handler::ErrorCallback& error_callback) { 233 if (properties.empty()) { 234 if (!callback.is_null()) 235 callback.Run(); 236 return; 237 } 238 NET_LOG_USER("SetProperties", service_path); 239 LogConfigProperties("SetProperty", service_path, properties); 240 241 DBusThreadManager::Get()->GetShillServiceClient()->SetProperties( 242 dbus::ObjectPath(service_path), 243 properties, 244 base::Bind(&NetworkConfigurationHandler::SetPropertiesSuccessCallback, 245 AsWeakPtr(), service_path, callback), 246 base::Bind(&NetworkConfigurationHandler::SetPropertiesErrorCallback, 247 AsWeakPtr(), service_path, error_callback)); 248 } 249 250 void NetworkConfigurationHandler::ClearProperties( 251 const std::string& service_path, 252 const std::vector<std::string>& names, 253 const base::Closure& callback, 254 const network_handler::ErrorCallback& error_callback) { 255 if (names.empty()) { 256 if (!callback.is_null()) 257 callback.Run(); 258 return; 259 } 260 NET_LOG_USER("ClearProperties", service_path); 261 for (std::vector<std::string>::const_iterator iter = names.begin(); 262 iter != names.end(); ++iter) { 263 NET_LOG_DEBUG("ClearProperty", service_path + "." + *iter); 264 } 265 DBusThreadManager::Get()->GetShillServiceClient()->ClearProperties( 266 dbus::ObjectPath(service_path), 267 names, 268 base::Bind(&NetworkConfigurationHandler::ClearPropertiesSuccessCallback, 269 AsWeakPtr(), service_path, names, callback, error_callback), 270 base::Bind(&NetworkConfigurationHandler::ClearPropertiesErrorCallback, 271 AsWeakPtr(), service_path, error_callback)); 272 } 273 274 void NetworkConfigurationHandler::CreateConfiguration( 275 const base::DictionaryValue& properties, 276 const network_handler::StringResultCallback& callback, 277 const network_handler::ErrorCallback& error_callback) { 278 ShillManagerClient* manager = 279 DBusThreadManager::Get()->GetShillManagerClient(); 280 std::string type; 281 properties.GetStringWithoutPathExpansion(shill::kTypeProperty, &type); 282 if (NetworkTypePattern::Ethernet().MatchesType(type)) { 283 InvokeErrorCallback( 284 "" /* no service path */, 285 error_callback, 286 "ConfigureServiceForProfile is not implemented for Ethernet"); 287 return; 288 } 289 290 NET_LOG_USER("CreateConfiguration", type); 291 LogConfigProperties("Configure", type, properties); 292 293 std::string profile; 294 properties.GetStringWithoutPathExpansion(shill::kProfileProperty, 295 &profile); 296 DCHECK(!profile.empty()); 297 manager->ConfigureServiceForProfile( 298 dbus::ObjectPath(profile), 299 properties, 300 base::Bind(&NetworkConfigurationHandler::RunCreateNetworkCallback, 301 AsWeakPtr(), 302 callback), 303 base::Bind(&network_handler::ShillErrorCallbackFunction, 304 "Config.CreateConfiguration Failed", 305 "", 306 error_callback)); 307 } 308 309 void NetworkConfigurationHandler::RemoveConfiguration( 310 const std::string& service_path, 311 const base::Closure& callback, 312 const network_handler::ErrorCallback& error_callback) { 313 // Service.Remove is not reliable. Instead, request the profile entries 314 // for the service and remove each entry. 315 if (ContainsKey(profile_entry_deleters_,service_path)) { 316 InvokeErrorCallback( 317 service_path, error_callback, "RemoveConfigurationInProgress"); 318 return; 319 } 320 NET_LOG_USER("Remove Configuration", service_path); 321 ProfileEntryDeleter* deleter = 322 new ProfileEntryDeleter(this, service_path, callback, error_callback); 323 profile_entry_deleters_[service_path] = deleter; 324 deleter->Run(); 325 } 326 327 void NetworkConfigurationHandler::SetNetworkProfile( 328 const std::string& service_path, 329 const std::string& profile_path, 330 const base::Closure& callback, 331 const network_handler::ErrorCallback& error_callback) { 332 NET_LOG_USER("SetNetworkProfile", service_path + ": " + profile_path); 333 base::StringValue profile_path_value(profile_path); 334 DBusThreadManager::Get()->GetShillServiceClient()->SetProperty( 335 dbus::ObjectPath(service_path), 336 shill::kProfileProperty, 337 profile_path_value, 338 callback, 339 base::Bind(&SetNetworkProfileErrorCallback, 340 service_path, profile_path, error_callback)); 341 } 342 343 // NetworkConfigurationHandler Private methods 344 345 NetworkConfigurationHandler::NetworkConfigurationHandler() 346 : network_state_handler_(NULL) { 347 } 348 349 NetworkConfigurationHandler::~NetworkConfigurationHandler() { 350 STLDeleteContainerPairSecondPointers( 351 profile_entry_deleters_.begin(), profile_entry_deleters_.end()); 352 } 353 354 void NetworkConfigurationHandler::Init( 355 NetworkStateHandler* network_state_handler) { 356 network_state_handler_ = network_state_handler; 357 } 358 359 void NetworkConfigurationHandler::RunCreateNetworkCallback( 360 const network_handler::StringResultCallback& callback, 361 const dbus::ObjectPath& service_path) { 362 if (!callback.is_null()) 363 callback.Run(service_path.value()); 364 // This may also get called when CreateConfiguration is used to update an 365 // existing configuration, so request a service update just in case. 366 // TODO(pneubeck): Separate 'Create' and 'Update' calls and only trigger 367 // this on an update. 368 network_state_handler_->RequestUpdateForNetwork(service_path.value()); 369 } 370 371 void NetworkConfigurationHandler::ProfileEntryDeleterCompleted( 372 const std::string& service_path) { 373 std::map<std::string, ProfileEntryDeleter*>::iterator iter = 374 profile_entry_deleters_.find(service_path); 375 DCHECK(iter != profile_entry_deleters_.end()); 376 delete iter->second; 377 profile_entry_deleters_.erase(iter); 378 } 379 380 void NetworkConfigurationHandler::SetPropertiesSuccessCallback( 381 const std::string& service_path, 382 const base::Closure& callback) { 383 if (!callback.is_null()) 384 callback.Run(); 385 network_state_handler_->RequestUpdateForNetwork(service_path); 386 } 387 388 void NetworkConfigurationHandler::SetPropertiesErrorCallback( 389 const std::string& service_path, 390 const network_handler::ErrorCallback& error_callback, 391 const std::string& dbus_error_name, 392 const std::string& dbus_error_message) { 393 network_handler::ShillErrorCallbackFunction( 394 "Config.SetProperties Failed", 395 service_path, error_callback, 396 dbus_error_name, dbus_error_message); 397 // Some properties may have changed so request an update regardless. 398 network_state_handler_->RequestUpdateForNetwork(service_path); 399 } 400 401 void NetworkConfigurationHandler::ClearPropertiesSuccessCallback( 402 const std::string& service_path, 403 const std::vector<std::string>& names, 404 const base::Closure& callback, 405 const network_handler::ErrorCallback& error_callback, 406 const base::ListValue& result) { 407 const std::string kClearPropertiesFailedError("Error.ClearPropertiesFailed"); 408 DCHECK(names.size() == result.GetSize()) 409 << "Incorrect result size from ClearProperties."; 410 411 bool some_failed = false; 412 for (size_t i = 0; i < result.GetSize(); ++i) { 413 bool success = false; 414 result.GetBoolean(i, &success); 415 if (!success) { 416 NET_LOG_ERROR("ClearProperties Failed: " + names[i], service_path); 417 some_failed = true; 418 } 419 } 420 421 if (some_failed) { 422 if (!error_callback.is_null()) { 423 scoped_ptr<base::DictionaryValue> error_data( 424 network_handler::CreateErrorData( 425 service_path, kClearPropertiesFailedError, 426 base::StringPrintf("Errors: %" PRIuS, result.GetSize()))); 427 error_data->Set("errors", result.DeepCopy()); 428 scoped_ptr<base::ListValue> name_list(new base::ListValue); 429 name_list->AppendStrings(names); 430 error_data->Set("names", name_list.release()); 431 error_callback.Run(kClearPropertiesFailedError, error_data.Pass()); 432 } 433 } else if (!callback.is_null()) { 434 callback.Run(); 435 } 436 network_state_handler_->RequestUpdateForNetwork(service_path); 437 } 438 439 void NetworkConfigurationHandler::ClearPropertiesErrorCallback( 440 const std::string& service_path, 441 const network_handler::ErrorCallback& error_callback, 442 const std::string& dbus_error_name, 443 const std::string& dbus_error_message) { 444 network_handler::ShillErrorCallbackFunction( 445 "Config.ClearProperties Failed", 446 service_path, error_callback, 447 dbus_error_name, dbus_error_message); 448 // Some properties may have changed so request an update regardless. 449 network_state_handler_->RequestUpdateForNetwork(service_path); 450 } 451 452 // static 453 NetworkConfigurationHandler* NetworkConfigurationHandler::InitializeForTest( 454 NetworkStateHandler* network_state_handler) { 455 NetworkConfigurationHandler* handler = new NetworkConfigurationHandler(); 456 handler->Init(network_state_handler); 457 return handler; 458 } 459 460 } // namespace chromeos 461