• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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/memory/scoped_ptr.h"
13 #include "base/stl_util.h"
14 #include "base/values.h"
15 #include "chromeos/dbus/dbus_thread_manager.h"
16 #include "chromeos/dbus/shill_profile_client.h"
17 #include "chromeos/network/network_ui_data.h"
18 #include "chromeos/network/onc/onc_signature.h"
19 #include "chromeos/network/onc/onc_translator.h"
20 #include "chromeos/network/policy_util.h"
21 #include "chromeos/network/shill_property_util.h"
22 #include "components/onc/onc_constants.h"
23 #include "dbus/object_path.h"
24 #include "third_party/cros_system_api/dbus/service_constants.h"
25 
26 namespace chromeos {
27 
28 namespace {
29 
LogErrorMessage(const tracked_objects::Location & from_where,const std::string & error_name,const std::string & error_message)30 void LogErrorMessage(const tracked_objects::Location& from_where,
31                      const std::string& error_name,
32                      const std::string& error_message) {
33   LOG(ERROR) << from_where.ToString() << ": " << error_message;
34 }
35 
GetByGUID(const PolicyApplicator::GuidToPolicyMap & policies,const std::string & guid)36 const base::DictionaryValue* GetByGUID(
37     const PolicyApplicator::GuidToPolicyMap& policies,
38     const std::string& guid) {
39   PolicyApplicator::GuidToPolicyMap::const_iterator it = policies.find(guid);
40   if (it == policies.end())
41     return NULL;
42   return it->second;
43 }
44 
45 }  // namespace
46 
PolicyApplicator(base::WeakPtr<ConfigurationHandler> handler,const NetworkProfile & profile,const GuidToPolicyMap & all_policies,const base::DictionaryValue & global_network_config,std::set<std::string> * modified_policies)47 PolicyApplicator::PolicyApplicator(
48     base::WeakPtr<ConfigurationHandler> handler,
49     const NetworkProfile& profile,
50     const GuidToPolicyMap& all_policies,
51     const base::DictionaryValue& global_network_config,
52     std::set<std::string>* modified_policies)
53     : handler_(handler), profile_(profile) {
54   global_network_config_.MergeDictionary(&global_network_config);
55   remaining_policies_.swap(*modified_policies);
56   for (GuidToPolicyMap::const_iterator it = all_policies.begin();
57        it != all_policies.end(); ++it) {
58     all_policies_.insert(std::make_pair(it->first, it->second->DeepCopy()));
59   }
60 }
61 
Run()62 void PolicyApplicator::Run() {
63   DBusThreadManager::Get()->GetShillProfileClient()->GetProperties(
64       dbus::ObjectPath(profile_.path),
65       base::Bind(&PolicyApplicator::GetProfilePropertiesCallback, this),
66       base::Bind(&LogErrorMessage, FROM_HERE));
67 }
68 
GetProfilePropertiesCallback(const base::DictionaryValue & profile_properties)69 void PolicyApplicator::GetProfilePropertiesCallback(
70     const base::DictionaryValue& profile_properties) {
71   if (!handler_) {
72     LOG(WARNING) << "Handler destructed during policy application to profile "
73                  << profile_.ToDebugString();
74     return;
75   }
76 
77   VLOG(2) << "Received properties for profile " << profile_.ToDebugString();
78   const base::ListValue* entries = NULL;
79   if (!profile_properties.GetListWithoutPathExpansion(
80            shill::kEntriesProperty, &entries)) {
81     LOG(ERROR) << "Profile " << profile_.ToDebugString()
82                << " doesn't contain the property "
83                << shill::kEntriesProperty;
84     return;
85   }
86 
87   for (base::ListValue::const_iterator it = entries->begin();
88        it != entries->end(); ++it) {
89     std::string entry;
90     (*it)->GetAsString(&entry);
91 
92     DBusThreadManager::Get()->GetShillProfileClient()->GetEntry(
93         dbus::ObjectPath(profile_.path),
94         entry,
95         base::Bind(&PolicyApplicator::GetEntryCallback, this, entry),
96         base::Bind(&LogErrorMessage, FROM_HERE));
97   }
98 }
99 
GetEntryCallback(const std::string & entry,const base::DictionaryValue & entry_properties)100 void PolicyApplicator::GetEntryCallback(
101     const std::string& entry,
102     const base::DictionaryValue& entry_properties) {
103   if (!handler_) {
104     LOG(WARNING) << "Handler destructed during policy application to profile "
105                  << profile_.ToDebugString();
106     return;
107   }
108 
109   VLOG(2) << "Received properties for entry " << entry << " of profile "
110           << profile_.ToDebugString();
111 
112   scoped_ptr<base::DictionaryValue> onc_part(
113       onc::TranslateShillServiceToONCPart(entry_properties,
114                                           &onc::kNetworkWithStateSignature));
115 
116   std::string old_guid;
117   if (!onc_part->GetStringWithoutPathExpansion(::onc::network_config::kGUID,
118                                                &old_guid)) {
119     VLOG(1) << "Entry " << entry << " of profile " << profile_.ToDebugString()
120             << " doesn't contain a GUID.";
121     // This might be an entry of an older ChromeOS version. Assume it to be
122     // unmanaged.
123   }
124 
125   scoped_ptr<NetworkUIData> ui_data =
126       shill_property_util::GetUIDataFromProperties(entry_properties);
127   if (!ui_data) {
128     VLOG(1) << "Entry " << entry << " of profile " << profile_.ToDebugString()
129             << " contains no or no valid UIData.";
130     // This might be an entry of an older ChromeOS version. Assume it to be
131     // unmanaged. It's an inconsistency if there is a GUID but no UIData, thus
132     // clear the GUID just in case.
133     old_guid.clear();
134   }
135 
136   bool was_managed = !old_guid.empty() && ui_data &&
137                      (ui_data->onc_source() ==
138                           ::onc::ONC_SOURCE_DEVICE_POLICY ||
139                       ui_data->onc_source() == ::onc::ONC_SOURCE_USER_POLICY);
140 
141   const base::DictionaryValue* new_policy = NULL;
142   if (was_managed) {
143     // If we have a GUID that might match a current policy, do a lookup using
144     // that GUID at first. In particular this is necessary, as some networks
145     // can't be matched to policies by properties (e.g. VPN).
146     new_policy = GetByGUID(all_policies_, old_guid);
147   }
148 
149   if (!new_policy) {
150     // If we didn't find a policy by GUID, still a new policy might match.
151     new_policy = policy_util::FindMatchingPolicy(all_policies_, *onc_part);
152   }
153 
154   if (new_policy) {
155     std::string new_guid;
156     new_policy->GetStringWithoutPathExpansion(::onc::network_config::kGUID,
157                                               &new_guid);
158 
159     VLOG_IF(1, was_managed && old_guid != new_guid)
160         << "Updating configuration previously managed by policy " << old_guid
161         << " with new policy " << new_guid << ".";
162     VLOG_IF(1, !was_managed) << "Applying policy " << new_guid
163                              << " to previously unmanaged "
164                              << "configuration.";
165 
166     if (old_guid == new_guid &&
167         remaining_policies_.find(new_guid) == remaining_policies_.end()) {
168       VLOG(1) << "Not updating existing managed configuration with guid "
169               << new_guid << " because the policy didn't change.";
170     } else {
171       const base::DictionaryValue* user_settings =
172           ui_data ? ui_data->user_settings() : NULL;
173       scoped_ptr<base::DictionaryValue> new_shill_properties =
174           policy_util::CreateShillConfiguration(
175               profile_, new_guid, new_policy, user_settings);
176       // A new policy has to be applied to this profile entry. In order to keep
177       // implicit state of Shill like "connected successfully before", keep the
178       // entry if a policy is reapplied (e.g. after reboot) or is updated.
179       // However, some Shill properties are used to identify the network and
180       // cannot be modified after initial configuration, so we have to delete
181       // the profile entry in these cases. Also, keeping Shill's state if the
182       // SSID changed might not be a good idea anyways. If the policy GUID
183       // changed, or there was no policy before, we delete the entry at first to
184       // ensure that no old configuration remains.
185       if (old_guid == new_guid &&
186           shill_property_util::DoIdentifyingPropertiesMatch(
187               *new_shill_properties, entry_properties)) {
188         VLOG(1) << "Updating previously managed configuration with the "
189                 << "updated policy " << new_guid << ".";
190       } else {
191         VLOG(1) << "Deleting profile entry before writing new policy "
192                 << new_guid << " because of identifying properties changed.";
193         DeleteEntry(entry);
194       }
195 
196       WriteNewShillConfiguration(*new_shill_properties, *new_policy);
197       remaining_policies_.erase(new_guid);
198     }
199   } else if (was_managed) {
200     VLOG(1) << "Removing configuration previously managed by policy "
201             << old_guid << ", because the policy was removed.";
202 
203     // Remove the entry, because the network was managed but isn't anymore.
204     // Note: An alternative might be to preserve the user settings, but it's
205     // unclear which values originating the policy should be removed.
206     DeleteEntry(entry);
207   } else {
208     // The entry wasn't managed and doesn't match any current policy. Global
209     // network settings have to be applied.
210     base::DictionaryValue shill_properties_to_update;
211     GetPropertiesForUnmanagedEntry(entry_properties,
212                                    &shill_properties_to_update);
213     if (shill_properties_to_update.empty()) {
214       VLOG(2) << "Ignore unmanaged entry.";
215       // Calling a SetProperties of Shill with an empty dictionary is a no op.
216     } else {
217       VLOG(2) << "Apply global network config to unmanaged entry.";
218       handler_->UpdateExistingConfigurationWithPropertiesFromPolicy(
219           entry_properties, shill_properties_to_update);
220     }
221   }
222 }
223 
DeleteEntry(const std::string & entry)224 void PolicyApplicator::DeleteEntry(const std::string& entry) {
225   DBusThreadManager::Get()->GetShillProfileClient()->DeleteEntry(
226       dbus::ObjectPath(profile_.path),
227       entry,
228       base::Bind(&base::DoNothing),
229       base::Bind(&LogErrorMessage, FROM_HERE));
230 }
231 
WriteNewShillConfiguration(const base::DictionaryValue & shill_dictionary,const base::DictionaryValue & policy)232 void PolicyApplicator::WriteNewShillConfiguration(
233     const base::DictionaryValue& shill_dictionary,
234     const base::DictionaryValue& policy) {
235   // Ethernet (non EAP) settings, like GUID or UIData, cannot be stored per
236   // user. Abort in that case.
237   std::string type;
238   policy.GetStringWithoutPathExpansion(::onc::network_config::kType, &type);
239   if (type == ::onc::network_type::kEthernet &&
240       profile_.type() == NetworkProfile::TYPE_USER) {
241     const base::DictionaryValue* ethernet = NULL;
242     policy.GetDictionaryWithoutPathExpansion(::onc::network_config::kEthernet,
243                                              &ethernet);
244     std::string auth;
245     ethernet->GetStringWithoutPathExpansion(::onc::ethernet::kAuthentication,
246                                             &auth);
247     if (auth == ::onc::ethernet::kNone)
248       return;
249   }
250 
251   handler_->CreateConfigurationFromPolicy(shill_dictionary);
252 }
253 
GetPropertiesForUnmanagedEntry(const base::DictionaryValue & entry_properties,base::DictionaryValue * properties_to_update) const254 void PolicyApplicator::GetPropertiesForUnmanagedEntry(
255     const base::DictionaryValue& entry_properties,
256     base::DictionaryValue* properties_to_update) const {
257   // kAllowOnlyPolicyNetworksToAutoconnect is currently the only global config.
258 
259   std::string type;
260   entry_properties.GetStringWithoutPathExpansion(shill::kTypeProperty, &type);
261   if (NetworkTypePattern::Ethernet().MatchesType(type))
262     return;  // Autoconnect for Ethernet cannot be configured.
263 
264   // By default all networks are allowed to autoconnect.
265   bool only_policy_autoconnect = false;
266   global_network_config_.GetBooleanWithoutPathExpansion(
267       ::onc::global_network_config::kAllowOnlyPolicyNetworksToAutoconnect,
268       &only_policy_autoconnect);
269   if (!only_policy_autoconnect)
270     return;
271 
272   bool old_autoconnect = false;
273   if (entry_properties.GetBooleanWithoutPathExpansion(
274           shill::kAutoConnectProperty, &old_autoconnect) &&
275       !old_autoconnect) {
276     // Autoconnect is already explictly disabled. No need to set it again.
277     return;
278   }
279   // If autconnect is not explicitly set yet, it might automatically be enabled
280   // by Shill. To prevent that, disable it explicitly.
281   properties_to_update->SetBooleanWithoutPathExpansion(
282       shill::kAutoConnectProperty, false);
283 }
284 
~PolicyApplicator()285 PolicyApplicator::~PolicyApplicator() {
286   ApplyRemainingPolicies();
287   STLDeleteValues(&all_policies_);
288 }
289 
ApplyRemainingPolicies()290 void PolicyApplicator::ApplyRemainingPolicies() {
291   if (!handler_) {
292     LOG(WARNING) << "Handler destructed during policy application to profile "
293                  << profile_.ToDebugString();
294     return;
295   }
296 
297   if (remaining_policies_.empty())
298     return;
299 
300   VLOG(2) << "Create new managed network configurations in profile"
301           << profile_.ToDebugString() << ".";
302   // All profile entries were compared to policies. |remaining_policies_|
303   // contains all modified policies that didn't match any entry. For these
304   // remaining policies, new configurations have to be created.
305   for (std::set<std::string>::iterator it = remaining_policies_.begin();
306        it != remaining_policies_.end(); ++it) {
307     const base::DictionaryValue* policy = GetByGUID(all_policies_, *it);
308     DCHECK(policy);
309 
310     VLOG(1) << "Creating new configuration managed by policy " << *it
311             << " in profile " << profile_.ToDebugString() << ".";
312 
313     scoped_ptr<base::DictionaryValue> shill_dictionary =
314         policy_util::CreateShillConfiguration(profile_, *it, policy, NULL);
315     WriteNewShillConfiguration(*shill_dictionary, *policy);
316   }
317 }
318 
319 }  // namespace chromeos
320