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.
5 #include "chromeos/network/policy_applicator.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_type_pattern.h"
18 #include "chromeos/network/network_ui_data.h"
19 #include "chromeos/network/onc/onc_signature.h"
20 #include "chromeos/network/onc/onc_translator.h"
21 #include "chromeos/network/policy_util.h"
22 #include "chromeos/network/shill_property_util.h"
23 #include "components/onc/onc_constants.h"
24 #include "dbus/object_path.h"
25 #include "third_party/cros_system_api/dbus/service_constants.h"
31 void LogErrorMessage(const tracked_objects::Location
& from_where
,
32 const std::string
& error_name
,
33 const std::string
& error_message
) {
34 LOG(ERROR
) << from_where
.ToString() << ": " << error_message
;
37 const base::DictionaryValue
* GetByGUID(
38 const PolicyApplicator::GuidToPolicyMap
& policies
,
39 const std::string
& guid
) {
40 PolicyApplicator::GuidToPolicyMap::const_iterator it
= policies
.find(guid
);
41 if (it
== policies
.end())
48 PolicyApplicator::PolicyApplicator(
49 base::WeakPtr
<ConfigurationHandler
> handler
,
50 const NetworkProfile
& profile
,
51 const GuidToPolicyMap
& all_policies
,
52 const base::DictionaryValue
& global_network_config
,
53 std::set
<std::string
>* modified_policies
)
54 : handler_(handler
), profile_(profile
) {
55 global_network_config_
.MergeDictionary(&global_network_config
);
56 remaining_policies_
.swap(*modified_policies
);
57 for (GuidToPolicyMap::const_iterator it
= all_policies
.begin();
58 it
!= all_policies
.end(); ++it
) {
59 all_policies_
.insert(std::make_pair(it
->first
, it
->second
->DeepCopy()));
63 void PolicyApplicator::Run() {
64 DBusThreadManager::Get()->GetShillProfileClient()->GetProperties(
65 dbus::ObjectPath(profile_
.path
),
66 base::Bind(&PolicyApplicator::GetProfilePropertiesCallback
, this),
67 base::Bind(&LogErrorMessage
, FROM_HERE
));
70 void PolicyApplicator::GetProfilePropertiesCallback(
71 const base::DictionaryValue
& profile_properties
) {
73 LOG(WARNING
) << "Handler destructed during policy application to profile "
74 << profile_
.ToDebugString();
78 VLOG(2) << "Received properties for profile " << profile_
.ToDebugString();
79 const base::ListValue
* entries
= NULL
;
80 if (!profile_properties
.GetListWithoutPathExpansion(
81 shill::kEntriesProperty
, &entries
)) {
82 LOG(ERROR
) << "Profile " << profile_
.ToDebugString()
83 << " doesn't contain the property "
84 << shill::kEntriesProperty
;
88 for (base::ListValue::const_iterator it
= entries
->begin();
89 it
!= entries
->end(); ++it
) {
91 (*it
)->GetAsString(&entry
);
93 DBusThreadManager::Get()->GetShillProfileClient()->GetEntry(
94 dbus::ObjectPath(profile_
.path
),
96 base::Bind(&PolicyApplicator::GetEntryCallback
, this, entry
),
97 base::Bind(&LogErrorMessage
, FROM_HERE
));
101 void PolicyApplicator::GetEntryCallback(
102 const std::string
& entry
,
103 const base::DictionaryValue
& entry_properties
) {
105 LOG(WARNING
) << "Handler destructed during policy application to profile "
106 << profile_
.ToDebugString();
110 VLOG(2) << "Received properties for entry " << entry
<< " of profile "
111 << profile_
.ToDebugString();
113 scoped_ptr
<base::DictionaryValue
> onc_part(
114 onc::TranslateShillServiceToONCPart(entry_properties
,
115 &onc::kNetworkWithStateSignature
));
117 std::string old_guid
;
118 if (!onc_part
->GetStringWithoutPathExpansion(::onc::network_config::kGUID
,
120 VLOG(1) << "Entry " << entry
<< " of profile " << profile_
.ToDebugString()
121 << " doesn't contain a GUID.";
122 // This might be an entry of an older ChromeOS version. Assume it to be
126 scoped_ptr
<NetworkUIData
> ui_data
=
127 shill_property_util::GetUIDataFromProperties(entry_properties
);
129 VLOG(1) << "Entry " << entry
<< " of profile " << profile_
.ToDebugString()
130 << " contains no or no valid UIData.";
131 // This might be an entry of an older ChromeOS version. Assume it to be
132 // unmanaged. It's an inconsistency if there is a GUID but no UIData, thus
133 // clear the GUID just in case.
137 bool was_managed
= !old_guid
.empty() && ui_data
&&
138 (ui_data
->onc_source() ==
139 ::onc::ONC_SOURCE_DEVICE_POLICY
||
140 ui_data
->onc_source() == ::onc::ONC_SOURCE_USER_POLICY
);
142 const base::DictionaryValue
* new_policy
= NULL
;
144 // If we have a GUID that might match a current policy, do a lookup using
145 // that GUID at first. In particular this is necessary, as some networks
146 // can't be matched to policies by properties (e.g. VPN).
147 new_policy
= GetByGUID(all_policies_
, old_guid
);
151 // If we didn't find a policy by GUID, still a new policy might match.
152 new_policy
= policy_util::FindMatchingPolicy(all_policies_
, *onc_part
);
156 std::string new_guid
;
157 new_policy
->GetStringWithoutPathExpansion(::onc::network_config::kGUID
,
160 VLOG_IF(1, was_managed
&& old_guid
!= new_guid
)
161 << "Updating configuration previously managed by policy " << old_guid
162 << " with new policy " << new_guid
<< ".";
163 VLOG_IF(1, !was_managed
) << "Applying policy " << new_guid
164 << " to previously unmanaged "
167 if (old_guid
== new_guid
&&
168 remaining_policies_
.find(new_guid
) == remaining_policies_
.end()) {
169 VLOG(1) << "Not updating existing managed configuration with guid "
170 << new_guid
<< " because the policy didn't change.";
172 const base::DictionaryValue
* user_settings
=
173 ui_data
? ui_data
->user_settings() : NULL
;
174 scoped_ptr
<base::DictionaryValue
> new_shill_properties
=
175 policy_util::CreateShillConfiguration(
176 profile_
, new_guid
, new_policy
, user_settings
);
177 // A new policy has to be applied to this profile entry. In order to keep
178 // implicit state of Shill like "connected successfully before", keep the
179 // entry if a policy is reapplied (e.g. after reboot) or is updated.
180 // However, some Shill properties are used to identify the network and
181 // cannot be modified after initial configuration, so we have to delete
182 // the profile entry in these cases. Also, keeping Shill's state if the
183 // SSID changed might not be a good idea anyways. If the policy GUID
184 // changed, or there was no policy before, we delete the entry at first to
185 // ensure that no old configuration remains.
186 if (old_guid
== new_guid
&&
187 shill_property_util::DoIdentifyingPropertiesMatch(
188 *new_shill_properties
, entry_properties
)) {
189 VLOG(1) << "Updating previously managed configuration with the "
190 << "updated policy " << new_guid
<< ".";
192 VLOG(1) << "Deleting profile entry before writing new policy "
193 << new_guid
<< " because of identifying properties changed.";
197 // In general, old entries should at first be deleted before new
198 // configurations are written to prevent inconsistencies. Therefore, we
199 // delay the writing of the new config here until ~PolicyApplicator.
200 // E.g. one problematic case is if a policy { {GUID=X, SSID=Y} } is
201 // applied to the profile entries
202 // { ENTRY1 = {GUID=X, SSID=X, USER_SETTINGS=X},
203 // ENTRY2 = {SSID=Y, ... } }.
204 // At first ENTRY1 and ENTRY2 should be removed, then the new config be
205 // written and the result should be:
206 // { {GUID=X, SSID=Y, USER_SETTINGS=X} }
207 WriteNewShillConfiguration(*new_shill_properties
, *new_policy
, true);
208 remaining_policies_
.erase(new_guid
);
210 } else if (was_managed
) {
211 VLOG(1) << "Removing configuration previously managed by policy "
212 << old_guid
<< ", because the policy was removed.";
214 // Remove the entry, because the network was managed but isn't anymore.
215 // Note: An alternative might be to preserve the user settings, but it's
216 // unclear which values originating the policy should be removed.
219 // The entry wasn't managed and doesn't match any current policy. Global
220 // network settings have to be applied.
221 base::DictionaryValue shill_properties_to_update
;
222 GetPropertiesForUnmanagedEntry(entry_properties
,
223 &shill_properties_to_update
);
224 if (shill_properties_to_update
.empty()) {
225 VLOG(2) << "Ignore unmanaged entry.";
226 // Calling a SetProperties of Shill with an empty dictionary is a no op.
228 VLOG(2) << "Apply global network config to unmanaged entry.";
229 handler_
->UpdateExistingConfigurationWithPropertiesFromPolicy(
230 entry_properties
, shill_properties_to_update
);
235 void PolicyApplicator::DeleteEntry(const std::string
& entry
) {
236 DBusThreadManager::Get()->GetShillProfileClient()->DeleteEntry(
237 dbus::ObjectPath(profile_
.path
),
239 base::Bind(&base::DoNothing
),
240 base::Bind(&LogErrorMessage
, FROM_HERE
));
243 void PolicyApplicator::WriteNewShillConfiguration(
244 const base::DictionaryValue
& shill_dictionary
,
245 const base::DictionaryValue
& policy
,
247 // Ethernet (non EAP) settings, like GUID or UIData, cannot be stored per
248 // user. Abort in that case.
250 policy
.GetStringWithoutPathExpansion(::onc::network_config::kType
, &type
);
251 if (type
== ::onc::network_type::kEthernet
&&
252 profile_
.type() == NetworkProfile::TYPE_USER
) {
253 const base::DictionaryValue
* ethernet
= NULL
;
254 policy
.GetDictionaryWithoutPathExpansion(::onc::network_config::kEthernet
,
257 ethernet
->GetStringWithoutPathExpansion(::onc::ethernet::kAuthentication
,
259 if (auth
== ::onc::ethernet::kAuthenticationNone
)
264 new_shill_configurations_
.push_back(shill_dictionary
.DeepCopy());
266 handler_
->CreateConfigurationFromPolicy(shill_dictionary
);
269 void PolicyApplicator::GetPropertiesForUnmanagedEntry(
270 const base::DictionaryValue
& entry_properties
,
271 base::DictionaryValue
* properties_to_update
) const {
272 // kAllowOnlyPolicyNetworksToAutoconnect is currently the only global config.
275 entry_properties
.GetStringWithoutPathExpansion(shill::kTypeProperty
, &type
);
276 if (NetworkTypePattern::Ethernet().MatchesType(type
))
277 return; // Autoconnect for Ethernet cannot be configured.
279 // By default all networks are allowed to autoconnect.
280 bool only_policy_autoconnect
= false;
281 global_network_config_
.GetBooleanWithoutPathExpansion(
282 ::onc::global_network_config::kAllowOnlyPolicyNetworksToAutoconnect
,
283 &only_policy_autoconnect
);
284 if (!only_policy_autoconnect
)
287 bool old_autoconnect
= false;
288 if (entry_properties
.GetBooleanWithoutPathExpansion(
289 shill::kAutoConnectProperty
, &old_autoconnect
) &&
291 // Autoconnect is already explictly disabled. No need to set it again.
294 // If autconnect is not explicitly set yet, it might automatically be enabled
295 // by Shill. To prevent that, disable it explicitly.
296 properties_to_update
->SetBooleanWithoutPathExpansion(
297 shill::kAutoConnectProperty
, false);
300 PolicyApplicator::~PolicyApplicator() {
301 ApplyRemainingPolicies();
302 STLDeleteValues(&all_policies_
);
303 // Notify the handler about all policies being applied, so that the network
304 // lists can be updated.
306 handler_
->OnPoliciesApplied();
309 void PolicyApplicator::ApplyRemainingPolicies() {
311 LOG(WARNING
) << "Handler destructed during policy application to profile "
312 << profile_
.ToDebugString();
316 // Write all queued configurations now.
317 for (ScopedVector
<base::DictionaryValue
>::const_iterator it
=
318 new_shill_configurations_
.begin();
319 it
!= new_shill_configurations_
.end();
321 handler_
->CreateConfigurationFromPolicy(**it
);
324 if (remaining_policies_
.empty())
327 VLOG(2) << "Create new managed network configurations in profile"
328 << profile_
.ToDebugString() << ".";
329 // All profile entries were compared to policies. |remaining_policies_|
330 // contains all modified policies that didn't match any entry. For these
331 // remaining policies, new configurations have to be created.
332 for (std::set
<std::string
>::iterator it
= remaining_policies_
.begin();
333 it
!= remaining_policies_
.end(); ++it
) {
334 const base::DictionaryValue
* policy
= GetByGUID(all_policies_
, *it
);
337 VLOG(1) << "Creating new configuration managed by policy " << *it
338 << " in profile " << profile_
.ToDebugString() << ".";
340 scoped_ptr
<base::DictionaryValue
> shill_dictionary
=
341 policy_util::CreateShillConfiguration(profile_
, *it
, policy
, NULL
);
342 WriteNewShillConfiguration(*shill_dictionary
, *policy
, false);
346 } // namespace chromeos