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