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