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