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