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