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