1 // Copyright (c) 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/managed_network_configuration_handler.h" 6 7 #include <string> 8 #include <vector> 9 10 #include "base/bind.h" 11 #include "base/guid.h" 12 #include "base/json/json_writer.h" 13 #include "base/location.h" 14 #include "base/logging.h" 15 #include "base/memory/ref_counted.h" 16 #include "base/memory/scoped_ptr.h" 17 #include "base/stl_util.h" 18 #include "base/strings/string_util.h" 19 #include "base/values.h" 20 #include "chromeos/dbus/dbus_method_call_status.h" 21 #include "chromeos/dbus/dbus_thread_manager.h" 22 #include "chromeos/dbus/shill_manager_client.h" 23 #include "chromeos/dbus/shill_profile_client.h" 24 #include "chromeos/dbus/shill_service_client.h" 25 #include "chromeos/network/network_configuration_handler.h" 26 #include "chromeos/network/network_event_log.h" 27 #include "chromeos/network/network_handler_callbacks.h" 28 #include "chromeos/network/network_profile.h" 29 #include "chromeos/network/network_profile_handler.h" 30 #include "chromeos/network/network_state.h" 31 #include "chromeos/network/network_state_handler.h" 32 #include "chromeos/network/network_ui_data.h" 33 #include "chromeos/network/onc/onc_constants.h" 34 #include "chromeos/network/onc/onc_merger.h" 35 #include "chromeos/network/onc/onc_normalizer.h" 36 #include "chromeos/network/onc/onc_signature.h" 37 #include "chromeos/network/onc/onc_translator.h" 38 #include "chromeos/network/onc/onc_utils.h" 39 #include "chromeos/network/onc/onc_validator.h" 40 #include "dbus/object_path.h" 41 #include "third_party/cros_system_api/dbus/service_constants.h" 42 43 namespace chromeos { 44 45 namespace { 46 47 // These are error strings used for error callbacks. None of these error 48 // messages are user-facing: they should only appear in logs. 49 const char kInvalidUserSettingsMessage[] = "User settings are invalid."; 50 const char kInvalidUserSettings[] = "Error.InvalidUserSettings"; 51 const char kNetworkAlreadyConfiguredMessage[] = 52 "Network is already configured."; 53 const char kNetworkAlreadyConfigured[] = "Error.NetworkAlreadyConfigured"; 54 const char kPoliciesNotInitializedMessage[] = "Policies not initialized."; 55 const char kPoliciesNotInitialized[] = "Error.PoliciesNotInitialized"; 56 const char kProfileNotInitializedMessage[] = "Profile not initialized."; 57 const char kProfileNotInitialized[] = "Error.ProflieNotInitialized"; 58 const char kSetOnUnconfiguredNetworkMessage[] = 59 "Unable to modify properties of an unconfigured network."; 60 const char kSetOnUnconfiguredNetwork[] = "Error.SetCalledOnUnconfiguredNetwork"; 61 const char kUnknownProfilePathMessage[] = "Profile path is unknown."; 62 const char kUnknownProfilePath[] = "Error.UnknownProfilePath"; 63 const char kUnknownServicePathMessage[] = "Service path is unknown."; 64 const char kUnknownServicePath[] = "Error.UnknownServicePath"; 65 66 // This fake credential contains a random postfix which is extremly unlikely to 67 // be used by any user. 68 const char kFakeCredential[] = "FAKE_CREDENTIAL_VPaJDV9x"; 69 70 std::string ToDebugString(onc::ONCSource source, 71 const std::string& userhash) { 72 return source == onc::ONC_SOURCE_USER_POLICY ? 73 ("user policy of " + userhash) : "device policy"; 74 } 75 76 void RunErrorCallback(const std::string& service_path, 77 const std::string& error_name, 78 const std::string& error_message, 79 const network_handler::ErrorCallback& error_callback) { 80 NET_LOG_ERROR(error_name, error_message); 81 error_callback.Run( 82 error_name, 83 make_scoped_ptr( 84 network_handler::CreateErrorData(service_path, 85 error_name, 86 error_message))); 87 } 88 89 // Sets the UIData property in |shill_dictionary| to the serialization of 90 // |ui_data|. 91 void SetUIData(const NetworkUIData& ui_data, 92 base::DictionaryValue* shill_dictionary) { 93 base::DictionaryValue ui_data_dict; 94 ui_data.FillDictionary(&ui_data_dict); 95 std::string ui_data_blob; 96 base::JSONWriter::Write(&ui_data_dict, &ui_data_blob); 97 shill_dictionary->SetStringWithoutPathExpansion(flimflam::kUIDataProperty, 98 ui_data_blob); 99 } 100 101 // A dummy callback to ignore the result of Shill calls. 102 void IgnoreString(const std::string& str) { 103 } 104 105 void LogErrorWithDict(const tracked_objects::Location& from_where, 106 const std::string& error_name, 107 scoped_ptr<base::DictionaryValue> error_data) { 108 LOG(ERROR) << from_where.ToString() << ": " << error_name; 109 } 110 111 void LogErrorMessage(const tracked_objects::Location& from_where, 112 const std::string& error_name, 113 const std::string& error_message) { 114 LOG(ERROR) << from_where.ToString() << ": " << error_message; 115 } 116 117 // Removes all kFakeCredential values from sensitive fields (determined by 118 // onc::FieldIsCredential) of |onc_object|. 119 void RemoveFakeCredentials( 120 const onc::OncValueSignature& signature, 121 base::DictionaryValue* onc_object) { 122 base::DictionaryValue::Iterator it(*onc_object); 123 while (!it.IsAtEnd()) { 124 base::Value* value = NULL; 125 std::string field_name = it.key(); 126 // We need the non-const entry to remove nested values but DictionaryValue 127 // has no non-const iterator. 128 onc_object->GetWithoutPathExpansion(field_name, &value); 129 // Advance before delete. 130 it.Advance(); 131 132 // If |value| is a dictionary, recurse. 133 base::DictionaryValue* nested_object = NULL; 134 if (value->GetAsDictionary(&nested_object)) { 135 const onc::OncFieldSignature* field_signature = 136 onc::GetFieldSignature(signature, field_name); 137 138 RemoveFakeCredentials(*field_signature->value_signature, 139 nested_object); 140 continue; 141 } 142 143 // If |value| is a string, check if it is a fake credential. 144 std::string string_value; 145 if (value->GetAsString(&string_value) && 146 onc::FieldIsCredential(signature, field_name)) { 147 if (string_value == kFakeCredential) { 148 // The value wasn't modified by the UI, thus we remove the field to keep 149 // the existing value that is stored in Shill. 150 onc_object->RemoveWithoutPathExpansion(field_name, NULL); 151 } 152 // Otherwise, the value is set and modified by the UI, thus we keep that 153 // value to overwrite whatever is stored in Shill. 154 } 155 } 156 } 157 158 // Creates a Shill property dictionary from the given arguments. The resulting 159 // dictionary will be sent to Shill by the caller. Depending on the profile 160 // type, |policy| is interpreted as the user or device policy and |settings| as 161 // the user or shared settings. 162 scoped_ptr<base::DictionaryValue> CreateShillConfiguration( 163 const NetworkProfile& profile, 164 const std::string& guid, 165 const base::DictionaryValue* policy, 166 const base::DictionaryValue* settings) { 167 scoped_ptr<base::DictionaryValue> effective; 168 onc::ONCSource onc_source = onc::ONC_SOURCE_NONE; 169 if (policy) { 170 if (profile.type() == NetworkProfile::TYPE_SHARED) { 171 effective = onc::MergeSettingsAndPoliciesToEffective( 172 NULL, // no user policy 173 policy, // device policy 174 NULL, // no user settings 175 settings); // shared settings 176 onc_source = onc::ONC_SOURCE_DEVICE_POLICY; 177 } else if (profile.type() == NetworkProfile::TYPE_USER) { 178 effective = onc::MergeSettingsAndPoliciesToEffective( 179 policy, // user policy 180 NULL, // no device policy 181 settings, // user settings 182 NULL); // no shared settings 183 onc_source = onc::ONC_SOURCE_USER_POLICY; 184 } else { 185 NOTREACHED(); 186 } 187 } else if (settings) { 188 effective.reset(settings->DeepCopy()); 189 // TODO(pneubeck): change to source ONC_SOURCE_USER 190 onc_source = onc::ONC_SOURCE_NONE; 191 } else { 192 NOTREACHED(); 193 onc_source = onc::ONC_SOURCE_NONE; 194 } 195 196 RemoveFakeCredentials(onc::kNetworkConfigurationSignature, 197 effective.get()); 198 199 effective->SetStringWithoutPathExpansion(onc::network_config::kGUID, guid); 200 201 // Remove irrelevant fields. 202 onc::Normalizer normalizer(true /* remove recommended fields */); 203 effective = normalizer.NormalizeObject(&onc::kNetworkConfigurationSignature, 204 *effective); 205 206 scoped_ptr<base::DictionaryValue> shill_dictionary( 207 onc::TranslateONCObjectToShill(&onc::kNetworkConfigurationSignature, 208 *effective)); 209 210 shill_dictionary->SetStringWithoutPathExpansion(flimflam::kProfileProperty, 211 profile.path); 212 213 scoped_ptr<NetworkUIData> ui_data; 214 if (policy) 215 ui_data = NetworkUIData::CreateFromONC(onc_source, *policy); 216 else 217 ui_data.reset(new NetworkUIData()); 218 219 if (settings) { 220 // Shill doesn't know that sensitive data is contained in the UIData 221 // property and might write it into logs or other insecure places. Thus, we 222 // have to remove or mask credentials. 223 // 224 // Shill's GetProperties doesn't return credentials. Masking credentials 225 // instead of just removing them, allows remembering if a credential is set 226 // or not. 227 scoped_ptr<base::DictionaryValue> sanitized_settings( 228 onc::MaskCredentialsInOncObject(onc::kNetworkConfigurationSignature, 229 *settings, 230 kFakeCredential)); 231 ui_data->set_user_settings(sanitized_settings.Pass()); 232 } 233 234 SetUIData(*ui_data, shill_dictionary.get()); 235 236 VLOG(2) << "Created Shill properties: " << *shill_dictionary; 237 238 return shill_dictionary.Pass(); 239 } 240 241 // Returns true if |policy| matches |actual_network|, which must be part of a 242 // ONC NetworkConfiguration. This should be the only such matching function 243 // within Chrome. Shill does such matching in several functions for network 244 // identification. For compatibility, we currently should stick to Shill's 245 // matching behavior. 246 bool IsPolicyMatching(const base::DictionaryValue& policy, 247 const base::DictionaryValue& actual_network) { 248 std::string policy_type; 249 policy.GetStringWithoutPathExpansion(onc::network_config::kType, 250 &policy_type); 251 std::string network_type; 252 actual_network.GetStringWithoutPathExpansion(onc::network_config::kType, 253 &network_type); 254 if (policy_type != network_type) 255 return false; 256 257 if (network_type != onc::network_type::kWiFi) 258 return false; 259 260 const base::DictionaryValue* policy_wifi = NULL; 261 policy.GetDictionaryWithoutPathExpansion(onc::network_config::kWiFi, 262 &policy_wifi); 263 const base::DictionaryValue* actual_wifi = NULL; 264 actual_network.GetDictionaryWithoutPathExpansion(onc::network_config::kWiFi, 265 &actual_wifi); 266 if (!policy_wifi || !actual_wifi) 267 return false; 268 269 std::string policy_ssid; 270 policy_wifi->GetStringWithoutPathExpansion(onc::wifi::kSSID, &policy_ssid); 271 std::string actual_ssid; 272 actual_wifi->GetStringWithoutPathExpansion(onc::wifi::kSSID, &actual_ssid); 273 return (policy_ssid == actual_ssid); 274 } 275 276 // Returns the policy from |policies| matching |actual_network|, if any exists. 277 // Returns NULL otherwise. |actual_network| must be part of a ONC 278 // NetworkConfiguration. 279 const base::DictionaryValue* FindMatchingPolicy( 280 const ManagedNetworkConfigurationHandler::GuidToPolicyMap &policies, 281 const base::DictionaryValue& actual_network) { 282 for (ManagedNetworkConfigurationHandler::GuidToPolicyMap::const_iterator it = 283 policies.begin(); it != policies.end(); ++it) { 284 if (IsPolicyMatching(*it->second, actual_network)) 285 return it->second; 286 } 287 return NULL; 288 } 289 290 const base::DictionaryValue* GetByGUID( 291 const ManagedNetworkConfigurationHandler::GuidToPolicyMap &policies, 292 const std::string& guid) { 293 ManagedNetworkConfigurationHandler::GuidToPolicyMap::const_iterator it = 294 policies.find(guid); 295 if (it == policies.end()) 296 return NULL; 297 return it->second; 298 } 299 300 void TranslatePropertiesToOncAndRunCallback( 301 const network_handler::DictionaryResultCallback& callback, 302 const std::string& service_path, 303 const base::DictionaryValue& shill_properties) { 304 scoped_ptr<base::DictionaryValue> onc_network( 305 onc::TranslateShillServiceToONCPart( 306 shill_properties, 307 &onc::kNetworkWithStateSignature)); 308 callback.Run(service_path, *onc_network); 309 } 310 311 } // namespace 312 313 // This class compares (entry point is Run()) |modified_policies| with the 314 // existing entries in the provided Shill profile |profile|. It fetches all 315 // entries in parallel (GetProfilePropertiesCallback), compares each entry with 316 // the current policies (GetEntryCallback) and adds all missing policies 317 // (~PolicyApplicator). 318 class ManagedNetworkConfigurationHandler::PolicyApplicator 319 : public base::RefCounted<PolicyApplicator> { 320 public: 321 typedef ManagedNetworkConfigurationHandler::GuidToPolicyMap GuidToPolicyMap; 322 323 // |modified_policies| must not be NULL and will be empty afterwards. 324 PolicyApplicator(base::WeakPtr<ManagedNetworkConfigurationHandler> handler, 325 const NetworkProfile& profile, 326 std::set<std::string>* modified_policies); 327 328 void Run(); 329 330 private: 331 friend class base::RefCounted<PolicyApplicator>; 332 333 // Called with the properties of the profile |profile_|. Requests the 334 // properties of each entry, which are processed by GetEntryCallback. 335 void GetProfilePropertiesCallback( 336 const base::DictionaryValue& profile_properties); 337 338 // Called with the properties of the profile entry |entry|. Checks whether the 339 // entry was previously managed, whether a current policy applies and then 340 // either updates, deletes or not touches the entry. 341 void GetEntryCallback(const std::string& entry, 342 const base::DictionaryValue& entry_properties); 343 344 // Sends Shill the command to delete profile entry |entry| from |profile_|. 345 void DeleteEntry(const std::string& entry); 346 347 // Creates new entries for all remaining policies, i.e. for which not matching 348 // entry was found. 349 virtual ~PolicyApplicator(); 350 351 std::set<std::string> remaining_policies_; 352 base::WeakPtr<ManagedNetworkConfigurationHandler> handler_; 353 NetworkProfile profile_; 354 355 DISALLOW_COPY_AND_ASSIGN(PolicyApplicator); 356 }; 357 358 // static 359 scoped_ptr<NetworkUIData> ManagedNetworkConfigurationHandler::GetUIData( 360 const base::DictionaryValue& shill_dictionary) { 361 std::string ui_data_blob; 362 if (shill_dictionary.GetStringWithoutPathExpansion( 363 flimflam::kUIDataProperty, 364 &ui_data_blob) && 365 !ui_data_blob.empty()) { 366 scoped_ptr<base::DictionaryValue> ui_data_dict = 367 onc::ReadDictionaryFromJson(ui_data_blob); 368 if (ui_data_dict) 369 return make_scoped_ptr(new NetworkUIData(*ui_data_dict)); 370 else 371 LOG(ERROR) << "UIData is not a valid JSON dictionary."; 372 } 373 VLOG(2) << "JSON dictionary has no UIData blob: " << shill_dictionary; 374 return scoped_ptr<NetworkUIData>(); 375 } 376 377 void ManagedNetworkConfigurationHandler::GetManagedProperties( 378 const std::string& userhash, 379 const std::string& service_path, 380 const network_handler::DictionaryResultCallback& callback, 381 const network_handler::ErrorCallback& error_callback) { 382 if (!GetPoliciesForUser(userhash) || !GetPoliciesForUser(std::string())) { 383 RunErrorCallback(service_path, 384 kPoliciesNotInitialized, 385 kPoliciesNotInitializedMessage, 386 error_callback); 387 return; 388 } 389 network_configuration_handler_->GetProperties( 390 service_path, 391 base::Bind( 392 &ManagedNetworkConfigurationHandler::GetManagedPropertiesCallback, 393 weak_ptr_factory_.GetWeakPtr(), 394 callback, 395 error_callback), 396 error_callback); 397 } 398 399 void ManagedNetworkConfigurationHandler::GetManagedPropertiesCallback( 400 const network_handler::DictionaryResultCallback& callback, 401 const network_handler::ErrorCallback& error_callback, 402 const std::string& service_path, 403 const base::DictionaryValue& shill_properties) { 404 std::string profile_path; 405 shill_properties.GetStringWithoutPathExpansion(flimflam::kProfileProperty, 406 &profile_path); 407 LOG(ERROR) << "Profile: " << profile_path; 408 const NetworkProfile* profile = 409 network_profile_handler_->GetProfileForPath(profile_path); 410 if (!profile) { 411 LOG(ERROR) << "No or no known profile received for service " 412 << service_path << "."; 413 } 414 415 scoped_ptr<NetworkUIData> ui_data = GetUIData(shill_properties); 416 417 const base::DictionaryValue* user_settings = NULL; 418 const base::DictionaryValue* shared_settings = NULL; 419 420 if (ui_data && profile) { 421 if (profile->type() == NetworkProfile::TYPE_SHARED) 422 shared_settings = ui_data->user_settings(); 423 else if (profile->type() == NetworkProfile::TYPE_USER) 424 user_settings = ui_data->user_settings(); 425 else 426 NOTREACHED(); 427 } else if (profile) { 428 LOG(WARNING) << "Service " << service_path << " of profile " 429 << profile_path << " contains no or no valid UIData."; 430 // TODO(pneubeck): add a conversion of user configured entries of old 431 // ChromeOS versions. We will have to use a heuristic to determine which 432 // properties _might_ be user configured. 433 } 434 435 scoped_ptr<base::DictionaryValue> active_settings( 436 onc::TranslateShillServiceToONCPart( 437 shill_properties, 438 &onc::kNetworkWithStateSignature)); 439 440 std::string guid; 441 active_settings->GetStringWithoutPathExpansion(onc::network_config::kGUID, 442 &guid); 443 444 const base::DictionaryValue* user_policy = NULL; 445 const base::DictionaryValue* device_policy = NULL; 446 if (!guid.empty() && profile) { 447 const GuidToPolicyMap* policies = GetPoliciesForProfile(*profile); 448 if (!policies) { 449 RunErrorCallback(service_path, 450 kPoliciesNotInitialized, 451 kPoliciesNotInitializedMessage, 452 error_callback); 453 return; 454 } 455 const base::DictionaryValue* policy = GetByGUID(*policies, guid); 456 if (profile->type() == NetworkProfile::TYPE_SHARED) 457 device_policy = policy; 458 else if (profile->type() == NetworkProfile::TYPE_USER) 459 user_policy = policy; 460 else 461 NOTREACHED(); 462 } 463 464 // This call also removes credentials from policies. 465 scoped_ptr<base::DictionaryValue> augmented_properties = 466 onc::MergeSettingsAndPoliciesToAugmented( 467 onc::kNetworkConfigurationSignature, 468 user_policy, 469 device_policy, 470 user_settings, 471 shared_settings, 472 active_settings.get()); 473 callback.Run(service_path, *augmented_properties); 474 } 475 476 void ManagedNetworkConfigurationHandler::GetProperties( 477 const std::string& service_path, 478 const network_handler::DictionaryResultCallback& callback, 479 const network_handler::ErrorCallback& error_callback) const { 480 network_configuration_handler_->GetProperties( 481 service_path, 482 base::Bind(&TranslatePropertiesToOncAndRunCallback, callback), 483 error_callback); 484 } 485 486 void ManagedNetworkConfigurationHandler::SetProperties( 487 const std::string& service_path, 488 const base::DictionaryValue& user_settings, 489 const base::Closure& callback, 490 const network_handler::ErrorCallback& error_callback) const { 491 const NetworkState* state = 492 network_state_handler_->GetNetworkState(service_path); 493 494 if (!state) { 495 RunErrorCallback(service_path, 496 kUnknownServicePath, 497 kUnknownServicePathMessage, 498 error_callback); 499 return; 500 } 501 502 std::string guid = state->guid(); 503 if (guid.empty()) { 504 // TODO(pneubeck): create an initial configuration in this case. As for 505 // CreateConfiguration, user settings from older ChromeOS versions have to 506 // determined here. 507 RunErrorCallback(service_path, 508 kSetOnUnconfiguredNetwork, 509 kSetOnUnconfiguredNetworkMessage, 510 error_callback); 511 return; 512 } 513 514 const std::string& profile_path = state->profile_path(); 515 const NetworkProfile *profile = 516 network_profile_handler_->GetProfileForPath(profile_path); 517 if (!profile) { 518 RunErrorCallback(service_path, 519 kUnknownProfilePath, 520 kUnknownProfilePathMessage, 521 error_callback); 522 return; 523 } 524 525 VLOG(2) << "SetProperties: Found GUID " << guid << " and profile " 526 << profile->ToDebugString(); 527 528 const GuidToPolicyMap* policies = GetPoliciesForProfile(*profile); 529 if (!policies) { 530 RunErrorCallback(service_path, 531 kPoliciesNotInitialized, 532 kPoliciesNotInitializedMessage, 533 error_callback); 534 return; 535 } 536 537 // Validate the ONC dictionary. We are liberal and ignore unknown field 538 // names. User settings are only partial ONC, thus we ignore missing fields. 539 onc::Validator validator(false, // Ignore unknown fields. 540 false, // Ignore invalid recommended field names. 541 false, // Ignore missing fields. 542 false); // This ONC does not come from policy. 543 544 onc::Validator::Result validation_result; 545 scoped_ptr<base::DictionaryValue> validated_user_settings = 546 validator.ValidateAndRepairObject( 547 &onc::kNetworkConfigurationSignature, 548 user_settings, 549 &validation_result); 550 551 if (validation_result == onc::Validator::INVALID) { 552 RunErrorCallback(service_path, 553 kInvalidUserSettings, 554 kInvalidUserSettingsMessage, 555 error_callback); 556 return; 557 } 558 if (validation_result == onc::Validator::VALID_WITH_WARNINGS) 559 LOG(WARNING) << "Validation of ONC user settings produced warnings."; 560 561 const base::DictionaryValue* policy = GetByGUID(*policies, guid); 562 VLOG(2) << "This configuration is " << (policy ? "" : "not ") << "managed."; 563 564 scoped_ptr<base::DictionaryValue> shill_dictionary(CreateShillConfiguration( 565 *profile, guid, policy, validated_user_settings.get())); 566 567 network_configuration_handler_->SetProperties( 568 service_path, *shill_dictionary, callback, error_callback); 569 } 570 571 void ManagedNetworkConfigurationHandler::CreateConfiguration( 572 const std::string& userhash, 573 const base::DictionaryValue& properties, 574 const network_handler::StringResultCallback& callback, 575 const network_handler::ErrorCallback& error_callback) const { 576 const GuidToPolicyMap* policies = GetPoliciesForUser(userhash); 577 if (!policies) { 578 RunErrorCallback("", 579 kPoliciesNotInitialized, 580 kPoliciesNotInitializedMessage, 581 error_callback); 582 return; 583 } 584 585 if (FindMatchingPolicy(*policies, properties)) { 586 RunErrorCallback("", 587 kNetworkAlreadyConfigured, 588 kNetworkAlreadyConfiguredMessage, 589 error_callback); 590 } 591 592 const NetworkProfile* profile = 593 network_profile_handler_->GetProfileForUserhash(userhash); 594 if (!profile) { 595 RunErrorCallback("", 596 kProfileNotInitialized, 597 kProfileNotInitializedMessage, 598 error_callback); 599 } 600 601 // TODO(pneubeck): In case of WiFi, check that no other configuration for the 602 // same {SSID, mode, security} exists. We don't support such multiple 603 // configurations, yet. 604 605 // Generate a new GUID for this configuration. Ignore the maybe provided GUID 606 // in |properties| as it is not our own and from an untrusted source. 607 std::string guid = base::GenerateGUID(); 608 scoped_ptr<base::DictionaryValue> shill_dictionary( 609 CreateShillConfiguration(*profile, guid, NULL /*no policy*/, 610 &properties)); 611 612 network_configuration_handler_->CreateConfiguration( 613 *shill_dictionary, callback, error_callback); 614 } 615 616 void ManagedNetworkConfigurationHandler::RemoveConfiguration( 617 const std::string& service_path, 618 const base::Closure& callback, 619 const network_handler::ErrorCallback& error_callback) const { 620 network_configuration_handler_->RemoveConfiguration( 621 service_path, callback, error_callback); 622 } 623 624 void ManagedNetworkConfigurationHandler::SetPolicy( 625 onc::ONCSource onc_source, 626 const std::string& userhash, 627 const base::ListValue& network_configs_onc) { 628 VLOG(1) << "Setting policies from " << ToDebugString(onc_source, userhash) 629 << "."; 630 631 // |userhash| must be empty for device policies. 632 DCHECK(onc_source != chromeos::onc::ONC_SOURCE_DEVICE_POLICY || 633 userhash.empty()); 634 GuidToPolicyMap& policies = policies_by_user_[userhash]; 635 636 GuidToPolicyMap old_policies; 637 policies.swap(old_policies); 638 639 // This stores all GUIDs of policies that have changed or are new. 640 std::set<std::string> modified_policies; 641 642 for (base::ListValue::const_iterator it = network_configs_onc.begin(); 643 it != network_configs_onc.end(); ++it) { 644 const base::DictionaryValue* network = NULL; 645 (*it)->GetAsDictionary(&network); 646 DCHECK(network); 647 648 std::string guid; 649 network->GetStringWithoutPathExpansion(onc::network_config::kGUID, &guid); 650 DCHECK(!guid.empty()); 651 652 if (policies.count(guid) > 0) { 653 LOG(ERROR) << "ONC from " << ToDebugString(onc_source, userhash) 654 << " contains several entries for the same GUID " 655 << guid << "."; 656 delete policies[guid]; 657 } 658 const base::DictionaryValue* new_entry = network->DeepCopy(); 659 policies[guid] = new_entry; 660 661 const base::DictionaryValue* old_entry = old_policies[guid]; 662 if (!old_entry || !old_entry->Equals(new_entry)) 663 modified_policies.insert(guid); 664 } 665 666 STLDeleteValues(&old_policies); 667 668 const NetworkProfile* profile = 669 network_profile_handler_->GetProfileForUserhash(userhash); 670 if (!profile) { 671 VLOG(1) << "The relevant Shill profile isn't initialized yet, postponing " 672 << "policy application."; 673 return; 674 } 675 676 scoped_refptr<PolicyApplicator> applicator = new PolicyApplicator( 677 weak_ptr_factory_.GetWeakPtr(), 678 *profile, 679 &modified_policies); 680 applicator->Run(); 681 } 682 683 void ManagedNetworkConfigurationHandler::OnProfileAdded( 684 const NetworkProfile& profile) { 685 VLOG(1) << "Adding profile " << profile.ToDebugString() << "'."; 686 687 const GuidToPolicyMap* policies = GetPoliciesForProfile(profile); 688 if (!policies) { 689 VLOG(1) << "The relevant policy is not initialized, " 690 << "postponing policy application."; 691 return; 692 } 693 694 std::set<std::string> policy_guids; 695 for (GuidToPolicyMap::const_iterator it = policies->begin(); 696 it != policies->end(); ++it) { 697 policy_guids.insert(it->first); 698 } 699 700 scoped_refptr<PolicyApplicator> applicator = new PolicyApplicator( 701 weak_ptr_factory_.GetWeakPtr(), 702 profile, 703 &policy_guids); 704 applicator->Run(); 705 } 706 707 const base::DictionaryValue* 708 ManagedNetworkConfigurationHandler::FindPolicyByGUID( 709 const std::string userhash, 710 const std::string& guid, 711 onc::ONCSource* onc_source) const { 712 *onc_source = onc::ONC_SOURCE_NONE; 713 714 if (!userhash.empty()) { 715 const GuidToPolicyMap* user_policies = GetPoliciesForUser(userhash); 716 if (user_policies) { 717 GuidToPolicyMap::const_iterator found = user_policies->find(guid); 718 if (found != user_policies->end()) { 719 *onc_source = onc::ONC_SOURCE_USER_POLICY; 720 return found->second; 721 } 722 } 723 } 724 725 const GuidToPolicyMap* device_policies = GetPoliciesForUser(std::string()); 726 if (device_policies) { 727 GuidToPolicyMap::const_iterator found = device_policies->find(guid); 728 if (found != device_policies->end()) { 729 *onc_source = onc::ONC_SOURCE_DEVICE_POLICY; 730 return found->second; 731 } 732 } 733 734 return NULL; 735 } 736 737 void ManagedNetworkConfigurationHandler::OnProfileRemoved( 738 const NetworkProfile& profile) { 739 // Nothing to do in this case. 740 } 741 742 const ManagedNetworkConfigurationHandler::GuidToPolicyMap* 743 ManagedNetworkConfigurationHandler::GetPoliciesForUser( 744 const std::string& userhash) const { 745 UserToPoliciesMap::const_iterator it = policies_by_user_.find(userhash); 746 if (it == policies_by_user_.end()) 747 return NULL; 748 return &it->second; 749 } 750 751 const ManagedNetworkConfigurationHandler::GuidToPolicyMap* 752 ManagedNetworkConfigurationHandler::GetPoliciesForProfile( 753 const NetworkProfile& profile) const { 754 DCHECK(profile.type() != NetworkProfile::TYPE_SHARED || 755 profile.userhash.empty()); 756 return GetPoliciesForUser(profile.userhash); 757 } 758 759 ManagedNetworkConfigurationHandler::ManagedNetworkConfigurationHandler() 760 : network_state_handler_(NULL), 761 network_profile_handler_(NULL), 762 network_configuration_handler_(NULL), 763 weak_ptr_factory_(this) { 764 } 765 766 ManagedNetworkConfigurationHandler::~ManagedNetworkConfigurationHandler() { 767 network_profile_handler_->RemoveObserver(this); 768 for (UserToPoliciesMap::iterator it = policies_by_user_.begin(); 769 it != policies_by_user_.end(); ++it) { 770 STLDeleteValues(&it->second); 771 } 772 } 773 774 void ManagedNetworkConfigurationHandler::Init( 775 NetworkStateHandler* network_state_handler, 776 NetworkProfileHandler* network_profile_handler, 777 NetworkConfigurationHandler* network_configuration_handler) { 778 network_state_handler_ = network_state_handler; 779 network_profile_handler_ = network_profile_handler; 780 network_configuration_handler_ = network_configuration_handler; 781 network_profile_handler_->AddObserver(this); 782 } 783 784 ManagedNetworkConfigurationHandler::PolicyApplicator::PolicyApplicator( 785 base::WeakPtr<ManagedNetworkConfigurationHandler> handler, 786 const NetworkProfile& profile, 787 std::set<std::string>* modified_policies) 788 : handler_(handler), profile_(profile) { 789 remaining_policies_.swap(*modified_policies); 790 } 791 792 void ManagedNetworkConfigurationHandler::PolicyApplicator::Run() { 793 DBusThreadManager::Get()->GetShillProfileClient()->GetProperties( 794 dbus::ObjectPath(profile_.path), 795 base::Bind(&PolicyApplicator::GetProfilePropertiesCallback, this), 796 base::Bind(&LogErrorMessage, FROM_HERE)); 797 } 798 799 void ManagedNetworkConfigurationHandler::PolicyApplicator:: 800 GetProfilePropertiesCallback( 801 const base::DictionaryValue& profile_properties) { 802 if (!handler_) { 803 LOG(WARNING) << "Handler destructed during policy application to profile " 804 << profile_.ToDebugString(); 805 return; 806 } 807 808 VLOG(2) << "Received properties for profile " << profile_.ToDebugString(); 809 const base::ListValue* entries = NULL; 810 if (!profile_properties.GetListWithoutPathExpansion( 811 flimflam::kEntriesProperty, &entries)) { 812 LOG(ERROR) << "Profile " << profile_.ToDebugString() 813 << " doesn't contain the property " 814 << flimflam::kEntriesProperty; 815 return; 816 } 817 818 for (base::ListValue::const_iterator it = entries->begin(); 819 it != entries->end(); 820 ++it) { 821 std::string entry; 822 (*it)->GetAsString(&entry); 823 824 std::ostringstream entry_failure; 825 DBusThreadManager::Get()->GetShillProfileClient() 826 ->GetEntry(dbus::ObjectPath(profile_.path), 827 entry, 828 base::Bind(&PolicyApplicator::GetEntryCallback, this, entry), 829 base::Bind(&LogErrorMessage, FROM_HERE)); 830 } 831 } 832 833 void ManagedNetworkConfigurationHandler::PolicyApplicator::GetEntryCallback( 834 const std::string& entry, 835 const base::DictionaryValue& entry_properties) { 836 if (!handler_) { 837 LOG(WARNING) << "Handler destructed during policy application to profile " 838 << profile_.ToDebugString(); 839 return; 840 } 841 842 VLOG(2) << "Received properties for entry " << entry << " of profile " 843 << profile_.ToDebugString(); 844 845 scoped_ptr<base::DictionaryValue> onc_part( 846 onc::TranslateShillServiceToONCPart(entry_properties, 847 &onc::kNetworkWithStateSignature)); 848 849 std::string old_guid; 850 if (!onc_part->GetStringWithoutPathExpansion(onc::network_config::kGUID, 851 &old_guid)) { 852 VLOG(1) << "Entry " << entry << " of profile " << profile_.ToDebugString() 853 << " doesn't contain a GUID."; 854 // This might be an entry of an older ChromeOS version. Assume it to be 855 // unmanaged. 856 } 857 858 scoped_ptr<NetworkUIData> ui_data = GetUIData(entry_properties); 859 if (!ui_data) { 860 VLOG(1) << "Entry " << entry << " of profile " << profile_.ToDebugString() 861 << " contains no or no valid UIData."; 862 // This might be an entry of an older ChromeOS version. Assume it to be 863 // unmanaged. It's an inconsistency if there is a GUID but no UIData, thus 864 // clear the GUID just in case. 865 old_guid.clear(); 866 } 867 868 bool was_managed = !old_guid.empty() && ui_data && 869 (ui_data->onc_source() == onc::ONC_SOURCE_DEVICE_POLICY || 870 ui_data->onc_source() == onc::ONC_SOURCE_USER_POLICY); 871 872 // The relevant policy must have been initialized, otherwise we hadn't Run 873 // this PolicyApplicator. 874 const GuidToPolicyMap& policies = *handler_->GetPoliciesForProfile(profile_); 875 876 const base::DictionaryValue* new_policy = NULL; 877 if (was_managed) { 878 // If we have a GUID that might match a current policy, do a lookup using 879 // that GUID at first. In particular this is necessary, as some networks 880 // can't be matched to policies by properties (e.g. VPN). 881 new_policy = GetByGUID(policies, old_guid); 882 } 883 884 if (!new_policy) { 885 // If we didn't find a policy by GUID, still a new policy might match. 886 new_policy = FindMatchingPolicy(policies, *onc_part); 887 } 888 889 if (new_policy) { 890 std::string new_guid; 891 new_policy->GetStringWithoutPathExpansion(onc::network_config::kGUID, 892 &new_guid); 893 894 VLOG_IF(1, was_managed && old_guid != new_guid) 895 << "Updating configuration previously managed by policy " << old_guid 896 << " with new policy " << new_guid << "."; 897 VLOG_IF(1, !was_managed) << "Applying policy " << new_guid 898 << " to previously unmanaged " 899 << "configuration."; 900 901 if (old_guid == new_guid && 902 remaining_policies_.find(new_guid) == remaining_policies_.end()) { 903 VLOG(1) << "Not updating existing managed configuration with guid " 904 << new_guid << " because the policy didn't change."; 905 } else { 906 // Delete the entry to ensure that no old configuration remains. 907 // Don't do this if a policy is reapplied (e.g. after reboot) or updated 908 // (i.e. the GUID didn't change), in order to keep implicit state of 909 // Shill like "connected successfully before". 910 if (old_guid == new_guid) { 911 VLOG(1) << "Updating previously managed configuration with the " 912 << "updated policy " << new_guid << "."; 913 } else { 914 DeleteEntry(entry); 915 } 916 917 const base::DictionaryValue* user_settings = 918 ui_data ? ui_data->user_settings() : NULL; 919 920 // Write the new configuration. 921 scoped_ptr<base::DictionaryValue> shill_dictionary = 922 CreateShillConfiguration( 923 profile_, new_guid, new_policy, user_settings); 924 handler_->network_configuration_handler() 925 ->CreateConfiguration(*shill_dictionary, 926 base::Bind(&IgnoreString), 927 base::Bind(&LogErrorWithDict, FROM_HERE)); 928 remaining_policies_.erase(new_guid); 929 } 930 } else if (was_managed) { 931 VLOG(1) << "Removing configuration previously managed by policy " 932 << old_guid << ", because the policy was removed."; 933 934 // Remove the entry, because the network was managed but isn't anymore. 935 // Note: An alternative might be to preserve the user settings, but it's 936 // unclear which values originating the policy should be removed. 937 DeleteEntry(entry); 938 } else { 939 VLOG(2) << "Ignore unmanaged entry."; 940 941 // The entry wasn't managed and doesn't match any current policy. Thus 942 // leave it as it is. 943 } 944 } 945 946 void ManagedNetworkConfigurationHandler::PolicyApplicator::DeleteEntry( 947 const std::string& entry) { 948 DBusThreadManager::Get()->GetShillProfileClient() 949 ->DeleteEntry(dbus::ObjectPath(profile_.path), 950 entry, 951 base::Bind(&base::DoNothing), 952 base::Bind(&LogErrorMessage, FROM_HERE)); 953 } 954 955 ManagedNetworkConfigurationHandler::PolicyApplicator::~PolicyApplicator() { 956 if (!handler_) { 957 LOG(WARNING) << "Handler destructed during policy application to profile " 958 << profile_.ToDebugString(); 959 return; 960 } 961 962 if (remaining_policies_.empty()) 963 return; 964 965 VLOG(2) << "Create new managed network configurations in profile" 966 << profile_.ToDebugString() << "."; 967 // All profile entries were compared to policies. |configureGUIDs_| contains 968 // all matched policies. From the remainder of policies, new configurations 969 // have to be created. 970 971 // The relevant policy must have been initialized, otherwise we hadn't Run 972 // this PolicyApplicator. 973 const GuidToPolicyMap& policies = *handler_->GetPoliciesForProfile(profile_); 974 975 for (std::set<std::string>::iterator it = remaining_policies_.begin(); 976 it != remaining_policies_.end(); 977 ++it) { 978 const base::DictionaryValue* policy = GetByGUID(policies, *it); 979 if (!policy) { 980 LOG(ERROR) << "Policy " << *it << " doesn't exist anymore."; 981 continue; 982 } 983 984 VLOG(1) << "Creating new configuration managed by policy " << *it 985 << " in profile " << profile_.ToDebugString() << "."; 986 987 scoped_ptr<base::DictionaryValue> shill_dictionary = 988 CreateShillConfiguration( 989 profile_, *it, policy, NULL /* no user settings */); 990 handler_->network_configuration_handler() 991 ->CreateConfiguration(*shill_dictionary, 992 base::Bind(&IgnoreString), 993 base::Bind(&LogErrorWithDict, FROM_HERE)); 994 } 995 } 996 997 } // namespace chromeos 998