policy_applicator.cc revision 4e180b6a0b4720a9b8e9e959a882386f690f08ff
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/policy_applicator.h"
6
7#include <utility>
8
9#include "base/bind.h"
10#include "base/location.h"
11#include "base/logging.h"
12#include "base/stl_util.h"
13#include "base/values.h"
14#include "chromeos/dbus/dbus_thread_manager.h"
15#include "chromeos/dbus/shill_profile_client.h"
16#include "chromeos/network/network_ui_data.h"
17#include "chromeos/network/onc/onc_signature.h"
18#include "chromeos/network/onc/onc_translator.h"
19#include "chromeos/network/policy_util.h"
20#include "chromeos/network/shill_property_util.h"
21#include "components/onc/onc_constants.h"
22#include "dbus/object_path.h"
23#include "third_party/cros_system_api/dbus/service_constants.h"
24
25namespace chromeos {
26
27namespace {
28
29void LogErrorMessage(const tracked_objects::Location& from_where,
30                     const std::string& error_name,
31                     const std::string& error_message) {
32  LOG(ERROR) << from_where.ToString() << ": " << error_message;
33}
34
35const base::DictionaryValue* GetByGUID(
36    const PolicyApplicator::GuidToPolicyMap& policies,
37    const std::string& guid) {
38  PolicyApplicator::GuidToPolicyMap::const_iterator it = policies.find(guid);
39  if (it == policies.end())
40    return NULL;
41  return it->second;
42}
43
44}  // namespace
45
46PolicyApplicator::PolicyApplicator(base::WeakPtr<ConfigurationHandler> handler,
47                                   const NetworkProfile& profile,
48                                   const GuidToPolicyMap& all_policies,
49                                   std::set<std::string>* modified_policies)
50    : handler_(handler), profile_(profile) {
51  remaining_policies_.swap(*modified_policies);
52  for (GuidToPolicyMap::const_iterator it = all_policies.begin();
53       it != all_policies.end(); ++it) {
54    all_policies_.insert(std::make_pair(it->first, it->second->DeepCopy()));
55  }
56}
57
58void PolicyApplicator::Run() {
59  DBusThreadManager::Get()->GetShillProfileClient()->GetProperties(
60      dbus::ObjectPath(profile_.path),
61      base::Bind(&PolicyApplicator::GetProfilePropertiesCallback, this),
62      base::Bind(&LogErrorMessage, FROM_HERE));
63}
64
65void PolicyApplicator::GetProfilePropertiesCallback(
66    const base::DictionaryValue& profile_properties) {
67  if (!handler_) {
68    LOG(WARNING) << "Handler destructed during policy application to profile "
69                 << profile_.ToDebugString();
70    return;
71  }
72
73  VLOG(2) << "Received properties for profile " << profile_.ToDebugString();
74  const base::ListValue* entries = NULL;
75  if (!profile_properties.GetListWithoutPathExpansion(
76           shill::kEntriesProperty, &entries)) {
77    LOG(ERROR) << "Profile " << profile_.ToDebugString()
78               << " doesn't contain the property "
79               << shill::kEntriesProperty;
80    return;
81  }
82
83  for (base::ListValue::const_iterator it = entries->begin();
84       it != entries->end(); ++it) {
85    std::string entry;
86    (*it)->GetAsString(&entry);
87
88    DBusThreadManager::Get()->GetShillProfileClient()->GetEntry(
89        dbus::ObjectPath(profile_.path),
90        entry,
91        base::Bind(&PolicyApplicator::GetEntryCallback, this, entry),
92        base::Bind(&LogErrorMessage, FROM_HERE));
93  }
94}
95
96void PolicyApplicator::GetEntryCallback(
97    const std::string& entry,
98    const base::DictionaryValue& entry_properties) {
99  if (!handler_) {
100    LOG(WARNING) << "Handler destructed during policy application to profile "
101                 << profile_.ToDebugString();
102    return;
103  }
104
105  VLOG(2) << "Received properties for entry " << entry << " of profile "
106          << profile_.ToDebugString();
107
108  scoped_ptr<base::DictionaryValue> onc_part(
109      onc::TranslateShillServiceToONCPart(entry_properties,
110                                          &onc::kNetworkWithStateSignature));
111
112  std::string old_guid;
113  if (!onc_part->GetStringWithoutPathExpansion(::onc::network_config::kGUID,
114                                               &old_guid)) {
115    VLOG(1) << "Entry " << entry << " of profile " << profile_.ToDebugString()
116            << " doesn't contain a GUID.";
117    // This might be an entry of an older ChromeOS version. Assume it to be
118    // unmanaged.
119  }
120
121  scoped_ptr<NetworkUIData> ui_data =
122      shill_property_util::GetUIDataFromProperties(entry_properties);
123  if (!ui_data) {
124    VLOG(1) << "Entry " << entry << " of profile " << profile_.ToDebugString()
125            << " contains no or no valid UIData.";
126    // This might be an entry of an older ChromeOS version. Assume it to be
127    // unmanaged. It's an inconsistency if there is a GUID but no UIData, thus
128    // clear the GUID just in case.
129    old_guid.clear();
130  }
131
132  bool was_managed = !old_guid.empty() && ui_data &&
133                     (ui_data->onc_source() ==
134                          ::onc::ONC_SOURCE_DEVICE_POLICY ||
135                      ui_data->onc_source() == ::onc::ONC_SOURCE_USER_POLICY);
136
137  const base::DictionaryValue* new_policy = NULL;
138  if (was_managed) {
139    // If we have a GUID that might match a current policy, do a lookup using
140    // that GUID at first. In particular this is necessary, as some networks
141    // can't be matched to policies by properties (e.g. VPN).
142    new_policy = GetByGUID(all_policies_, old_guid);
143  }
144
145  if (!new_policy) {
146    // If we didn't find a policy by GUID, still a new policy might match.
147    new_policy = policy_util::FindMatchingPolicy(all_policies_, *onc_part);
148  }
149
150  if (new_policy) {
151    std::string new_guid;
152    new_policy->GetStringWithoutPathExpansion(::onc::network_config::kGUID,
153                                              &new_guid);
154
155    VLOG_IF(1, was_managed && old_guid != new_guid)
156        << "Updating configuration previously managed by policy " << old_guid
157        << " with new policy " << new_guid << ".";
158    VLOG_IF(1, !was_managed) << "Applying policy " << new_guid
159                             << " to previously unmanaged "
160                             << "configuration.";
161
162    if (old_guid == new_guid &&
163        remaining_policies_.find(new_guid) == remaining_policies_.end()) {
164      VLOG(1) << "Not updating existing managed configuration with guid "
165              << new_guid << " because the policy didn't change.";
166    } else {
167      // Delete the entry to ensure that no old configuration remains.
168      // Don't do this if a policy is reapplied (e.g. after reboot) or updated
169      // (i.e. the GUID didn't change), in order to keep implicit state of
170      // Shill like "connected successfully before".
171      if (old_guid == new_guid) {
172        VLOG(1) << "Updating previously managed configuration with the "
173                << "updated policy " << new_guid << ".";
174      } else {
175        DeleteEntry(entry);
176      }
177
178      const base::DictionaryValue* user_settings =
179          ui_data ? ui_data->user_settings() : NULL;
180
181      // Write the new configuration.
182      CreateAndWriteNewShillConfiguration(new_guid, *new_policy, user_settings);
183      remaining_policies_.erase(new_guid);
184    }
185  } else if (was_managed) {
186    VLOG(1) << "Removing configuration previously managed by policy "
187            << old_guid << ", because the policy was removed.";
188
189    // Remove the entry, because the network was managed but isn't anymore.
190    // Note: An alternative might be to preserve the user settings, but it's
191    // unclear which values originating the policy should be removed.
192    DeleteEntry(entry);
193  } else {
194    VLOG(2) << "Ignore unmanaged entry.";
195
196    // The entry wasn't managed and doesn't match any current policy. Thus
197    // leave it as it is.
198  }
199}
200
201void PolicyApplicator::DeleteEntry(const std::string& entry) {
202  DBusThreadManager::Get()->GetShillProfileClient()->DeleteEntry(
203      dbus::ObjectPath(profile_.path),
204      entry,
205      base::Bind(&base::DoNothing),
206      base::Bind(&LogErrorMessage, FROM_HERE));
207}
208
209void PolicyApplicator::CreateAndWriteNewShillConfiguration(
210    const std::string& guid,
211    const base::DictionaryValue& policy,
212    const base::DictionaryValue* user_settings) {
213  // Ethernet (non EAP) settings, like GUID or UIData, cannot be stored per
214  // user. Abort in that case.
215  std::string type;
216  policy.GetStringWithoutPathExpansion(::onc::network_config::kType, &type);
217  if (type == ::onc::network_type::kEthernet &&
218      profile_.type() == NetworkProfile::TYPE_USER) {
219    const base::DictionaryValue* ethernet = NULL;
220    policy.GetDictionaryWithoutPathExpansion(::onc::network_config::kEthernet,
221                                             &ethernet);
222    std::string auth;
223    ethernet->GetStringWithoutPathExpansion(::onc::ethernet::kAuthentication,
224                                            &auth);
225    if (auth == ::onc::ethernet::kNone)
226      return;
227  }
228
229  scoped_ptr<base::DictionaryValue> shill_dictionary =
230      policy_util::CreateShillConfiguration(
231          profile_, guid, &policy, user_settings);
232  handler_->CreateConfigurationFromPolicy(*shill_dictionary);
233}
234
235PolicyApplicator::~PolicyApplicator() {
236  ApplyRemainingPolicies();
237  STLDeleteValues(&all_policies_);
238}
239
240void PolicyApplicator::ApplyRemainingPolicies() {
241  if (!handler_) {
242    LOG(WARNING) << "Handler destructed during policy application to profile "
243                 << profile_.ToDebugString();
244    return;
245  }
246
247  if (remaining_policies_.empty())
248    return;
249
250  VLOG(2) << "Create new managed network configurations in profile"
251          << profile_.ToDebugString() << ".";
252  // All profile entries were compared to policies. |remaining_policies_|
253  // contains all modified policies that didn't match any entry. For these
254  // remaining policies, new configurations have to be created.
255  for (std::set<std::string>::iterator it = remaining_policies_.begin();
256       it != remaining_policies_.end(); ++it) {
257    const base::DictionaryValue* policy = GetByGUID(all_policies_, *it);
258    DCHECK(policy);
259
260    VLOG(1) << "Creating new configuration managed by policy " << *it
261            << " in profile " << profile_.ToDebugString() << ".";
262
263    CreateAndWriteNewShillConfiguration(
264        *it, *policy, NULL /* no user settings */);
265  }
266}
267
268}  // namespace chromeos
269