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