Stop linking tcmalloc into shared library components.
[chromium-blink-merge.git] / chromeos / network / managed_network_configuration_handler.cc
blob122bdcdad337281c660890c541434d55031c1183
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.
5 #include "chromeos/network/managed_network_configuration_handler.h"
7 #include <string>
8 #include <vector>
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"
42 namespace chromeos {
44 namespace {
46 const char kLogModule[] = "ManagedNetworkConfigurationHandler";
48 // These are error strings used for error callbacks. None of these error
49 // messages are user-facing: they should only appear in logs.
50 const char kInvalidUserSettingsMessage[] = "User settings are invalid.";
51 const char kInvalidUserSettings[] = "Error.InvalidUserSettings";
52 const char kNetworkAlreadyConfiguredMessage[] =
53 "Network is already configured.";
54 const char kNetworkAlreadyConfigured[] = "Error.NetworkAlreadyConfigured";
55 const char kPoliciesNotInitializedMessage[] = "Policies not initialized.";
56 const char kPoliciesNotInitialized[] = "Error.PoliciesNotInitialized";
57 const char kProfileNotInitializedMessage[] = "Profile not initialized.";
58 const char kProfileNotInitialized[] = "Error.ProflieNotInitialized";
59 const char kSetOnUnconfiguredNetworkMessage[] =
60 "Unable to modify properties of an unconfigured network.";
61 const char kSetOnUnconfiguredNetwork[] = "Error.SetCalledOnUnconfiguredNetwork";
62 const char kUnknownProfilePathMessage[] = "Profile path is unknown.";
63 const char kUnknownProfilePath[] = "Error.UnknownProfilePath";
64 const char kUnknownServicePathMessage[] = "Service path is unknown.";
65 const char kUnknownServicePath[] = "Error.UnknownServicePath";
67 // This fake credential contains a random postfix which is extremly unlikely to
68 // be used by any user.
69 const char kFakeCredential[] = "FAKE_CREDENTIAL_VPaJDV9x";
71 std::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";
77 void 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)));
90 // Sets the UIData property in |shill_dictionary| to the serialization of
91 // |ui_data|.
92 void 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);
102 // A dummy callback to ignore the result of Shill calls.
103 void IgnoreString(const std::string& str) {
106 void 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;
112 void 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;
118 // Removes all kFakeCredential values from sensitive fields (determined by
119 // onc::FieldIsCredential) of |onc_object|.
120 void 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();
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);
139 RemoveFakeCredentials(*field_signature->value_signature,
140 nested_object);
141 continue;
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);
153 // Otherwise, the value is set and modified by the UI, thus we keep that
154 // value to overwrite whatever is stored in Shill.
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.
163 scoped_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();
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;
197 RemoveFakeCredentials(onc::kNetworkConfigurationSignature,
198 effective.get());
200 effective->SetStringWithoutPathExpansion(onc::network_config::kGUID, guid);
202 scoped_ptr<base::DictionaryValue> shill_dictionary(
203 onc::TranslateONCObjectToShill(&onc::kNetworkConfigurationSignature,
204 *effective));
206 shill_dictionary->SetStringWithoutPathExpansion(flimflam::kProfileProperty,
207 profile.path);
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());
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.
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());
230 SetUIData(*ui_data, shill_dictionary.get());
232 VLOG(2) << "Created Shill properties: " << *shill_dictionary;
234 return shill_dictionary.Pass();
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.
241 bool 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;
252 if (network_type != onc::network_type::kWiFi)
253 return false;
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);
263 // Returns the policy of |policies| matching |onc_network_part|, if any
264 // exists. Returns NULL otherwise.
265 const 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;
273 return NULL;
276 const 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;
286 void 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);
297 } // namespace
299 static ManagedNetworkConfigurationHandler*
300 g_configuration_handler_instance = NULL;
302 // static
303 void ManagedNetworkConfigurationHandler::Initialize(
304 NetworkProfileHandler* profile_handler) {
305 CHECK(!g_configuration_handler_instance);
306 g_configuration_handler_instance =
307 new ManagedNetworkConfigurationHandler(profile_handler);
310 // static
311 bool ManagedNetworkConfigurationHandler::IsInitialized() {
312 return g_configuration_handler_instance;
315 // static
316 void ManagedNetworkConfigurationHandler::Shutdown() {
317 CHECK(g_configuration_handler_instance);
318 delete g_configuration_handler_instance;
319 g_configuration_handler_instance = NULL;
322 // static
323 ManagedNetworkConfigurationHandler* ManagedNetworkConfigurationHandler::Get() {
324 CHECK(g_configuration_handler_instance);
325 return g_configuration_handler_instance;
328 // static
329 scoped_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.";
343 VLOG(2) << "JSON dictionary has no UIData blob: " << shill_dictionary;
344 return scoped_ptr<NetworkUIData>();
347 void 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;
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);
369 void 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 << ".";
384 scoped_ptr<NetworkUIData> ui_data = GetUIData(shill_properties);
386 const base::DictionaryValue* user_settings = NULL;
387 const base::DictionaryValue* shared_settings = NULL;
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.
404 scoped_ptr<base::DictionaryValue> active_settings(
405 onc::TranslateShillServiceToONCPart(
406 shill_properties,
407 &onc::kNetworkWithStateSignature));
409 std::string guid;
410 active_settings->GetStringWithoutPathExpansion(onc::network_config::kGUID,
411 &guid);
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;
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();
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);
445 void 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);
455 void 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);
463 if (!state) {
464 RunErrorCallback(service_path,
465 kUnknownServicePath,
466 kUnknownServicePathMessage,
467 error_callback);
468 return;
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;
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;
494 VLOG(2) << "SetProperties: Found GUID " << guid << " and profile "
495 << profile->ToDebugString();
497 const GuidToPolicyMap* policies = GetPoliciesForProfile(*profile);
498 if (!policies) {
499 RunErrorCallback(service_path,
500 kPoliciesNotInitialized,
501 kPoliciesNotInitializedMessage,
502 error_callback);
503 return;
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.
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);
520 if (validation_result == onc::Validator::INVALID) {
521 RunErrorCallback(service_path,
522 kInvalidUserSettings,
523 kInvalidUserSettingsMessage,
524 error_callback);
525 return;
527 if (validation_result == onc::Validator::VALID_WITH_WARNINGS)
528 LOG(WARNING) << "Validation of ONC user settings produced warnings.";
530 const base::DictionaryValue* policy = GetByGUID(*policies, guid);
531 VLOG(2) << "This configuration is " << (policy ? "" : "not ") << "managed.";
533 scoped_ptr<base::DictionaryValue> shill_dictionary(
534 CreateShillConfiguration(*profile, guid, policy, &user_settings));
536 NetworkConfigurationHandler::Get()->SetProperties(service_path,
537 *shill_dictionary,
538 callback,
539 error_callback);
542 void 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;
556 if (FindMatchingPolicy(*policies, properties)) {
557 RunErrorCallback("",
558 kNetworkAlreadyConfigured,
559 kNetworkAlreadyConfiguredMessage,
560 error_callback);
563 const NetworkProfile* profile =
564 profile_handler_->GetProfileForUserhash(userhash);
565 if (!profile) {
566 RunErrorCallback("",
567 kProfileNotInitialized,
568 kProfileNotInitializedMessage,
569 error_callback);
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.
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));
583 NetworkConfigurationHandler::Get()->CreateConfiguration(*shill_dictionary,
584 callback,
585 error_callback);
588 void 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);
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).
602 class ManagedNetworkConfigurationHandler::PolicyApplicator
603 : public base::RefCounted<PolicyApplicator> {
604 public:
605 typedef ManagedNetworkConfigurationHandler::GuidToPolicyMap GuidToPolicyMap;
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);
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));
623 private:
624 friend class base::RefCounted<PolicyApplicator>;
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;
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;
643 for (base::ListValue::const_iterator it = entries->begin();
644 it != entries->end(); ++it) {
645 std::string entry;
646 (*it)->GetAsString(&entry);
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));
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;
665 VLOG(2) << "Received properties for entry " << entry << " of profile "
666 << profile_.ToDebugString();
668 scoped_ptr<base::DictionaryValue> onc_part(
669 onc::TranslateShillServiceToONCPart(
670 entry_properties,
671 &onc::kNetworkWithStateSignature));
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;
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;
693 bool was_managed =
694 (ui_data->onc_source() == onc::ONC_SOURCE_DEVICE_POLICY ||
695 ui_data->onc_source() == onc::ONC_SOURCE_USER_POLICY);
697 // The relevant policy must have been initialized, otherwise we hadn't Run
698 // this PolicyApplicator.
699 const GuidToPolicyMap& policies =
700 *handler_->GetPoliciesForProfile(profile_);
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);
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);
715 if (new_policy) {
716 std::string new_guid;
717 new_policy->GetStringWithoutPathExpansion(onc::network_config::kGUID,
718 &new_guid);
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.";
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 << ".";
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);
747 } else if (was_managed) {
748 VLOG(1) << "Removing configuration previously managed by policy "
749 << old_guid << ", because the policy was removed.";
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.";
758 // The entry wasn't managed and doesn't match any current policy. Thus
759 // leave it as it is.
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));
771 virtual ~PolicyApplicator() {
772 if (!handler_) {
773 LOG(WARNING) << "Handler destructed during policy application to profile "
774 << profile_.ToDebugString();
775 return;
778 if (remaining_policies_.empty())
779 return;
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.
787 // The relevant policy must have been initialized, otherwise we hadn't Run
788 // this PolicyApplicator.
789 const GuidToPolicyMap& policies =
790 *handler_->GetPoliciesForProfile(profile_);
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;
800 VLOG(1) << "Creating new configuration managed by policy " << *it
801 << " in profile " << profile_.ToDebugString() << ".";
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));
813 std::set<std::string> remaining_policies_;
814 base::WeakPtr<ManagedNetworkConfigurationHandler> handler_;
815 NetworkProfile profile_;
817 DISALLOW_COPY_AND_ASSIGN(PolicyApplicator);
820 void 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 << ".";
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];
832 GuidToPolicyMap old_policies;
833 policies.swap(old_policies);
835 // This stores all GUIDs of policies that have changed or are new.
836 std::set<std::string> modified_policies;
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);
844 std::string guid;
845 network->GetStringWithoutPathExpansion(onc::network_config::kGUID, &guid);
846 DCHECK(!guid.empty());
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];
854 const base::DictionaryValue* new_entry = network->DeepCopy();
855 policies[guid] = new_entry;
857 const base::DictionaryValue* old_entry = old_policies[guid];
858 if (!old_entry || !old_entry->Equals(new_entry))
859 modified_policies.insert(guid);
862 STLDeleteValues(&old_policies);
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;
872 scoped_refptr<PolicyApplicator> applicator = new PolicyApplicator(
873 weak_ptr_factory_.GetWeakPtr(),
874 *profile,
875 &modified_policies);
876 applicator->Run();
879 void ManagedNetworkConfigurationHandler::OnProfileAdded(
880 const NetworkProfile& profile) {
881 VLOG(1) << "Adding profile " << profile.ToDebugString() << "'.";
883 const GuidToPolicyMap* policies = GetPoliciesForProfile(profile);
884 if (!policies) {
885 VLOG(1) << "The relevant policy is not initialized, "
886 << "postponing policy application.";
887 return;
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);
896 scoped_refptr<PolicyApplicator> applicator = new PolicyApplicator(
897 weak_ptr_factory_.GetWeakPtr(),
898 profile,
899 &policy_guids);
900 applicator->Run();
903 void ManagedNetworkConfigurationHandler::OnProfileRemoved(
904 const NetworkProfile& profile) {
905 // Nothing to do in this case.
908 const ManagedNetworkConfigurationHandler::GuidToPolicyMap*
909 ManagedNetworkConfigurationHandler::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;
917 const ManagedNetworkConfigurationHandler::GuidToPolicyMap*
918 ManagedNetworkConfigurationHandler::GetPoliciesForProfile(
919 const NetworkProfile& profile) const {
920 DCHECK(profile.type() != NetworkProfile::TYPE_SHARED ||
921 profile.userhash.empty());
922 return GetPoliciesForUser(profile.userhash);
925 ManagedNetworkConfigurationHandler::ManagedNetworkConfigurationHandler(
926 NetworkProfileHandler* profile_handler)
927 : profile_handler_(profile_handler),
928 weak_ptr_factory_(this) {
929 profile_handler_->AddObserver(this);
932 ManagedNetworkConfigurationHandler::~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);
940 } // namespace chromeos