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