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