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