Upstream version 7.36.149.0
[platform/framework/web/crosswalk.git] / src / chromeos / network / managed_network_configuration_handler_impl.cc
1 // Copyright 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "chromeos/network/managed_network_configuration_handler_impl.h"
6
7 #include <set>
8 #include <vector>
9
10 #include "base/bind.h"
11 #include "base/guid.h"
12 #include "base/location.h"
13 #include "base/logging.h"
14 #include "base/memory/ref_counted.h"
15 #include "base/memory/scoped_ptr.h"
16 #include "base/stl_util.h"
17 #include "base/values.h"
18 #include "chromeos/dbus/shill_manager_client.h"
19 #include "chromeos/dbus/shill_profile_client.h"
20 #include "chromeos/dbus/shill_service_client.h"
21 #include "chromeos/network/device_state.h"
22 #include "chromeos/network/network_configuration_handler.h"
23 #include "chromeos/network/network_event_log.h"
24 #include "chromeos/network/network_policy_observer.h"
25 #include "chromeos/network/network_profile.h"
26 #include "chromeos/network/network_profile_handler.h"
27 #include "chromeos/network/network_state.h"
28 #include "chromeos/network/network_state_handler.h"
29 #include "chromeos/network/network_ui_data.h"
30 #include "chromeos/network/onc/onc_merger.h"
31 #include "chromeos/network/onc/onc_signature.h"
32 #include "chromeos/network/onc/onc_translator.h"
33 #include "chromeos/network/onc/onc_validator.h"
34 #include "chromeos/network/policy_util.h"
35 #include "chromeos/network/shill_property_util.h"
36 #include "components/onc/onc_constants.h"
37 #include "third_party/cros_system_api/dbus/service_constants.h"
38
39 namespace chromeos {
40
41 namespace {
42
43 typedef std::map<std::string, const base::DictionaryValue*> GuidToPolicyMap;
44
45 // These are error strings used for error callbacks. None of these error
46 // messages are user-facing: they should only appear in logs.
47 const char kInvalidUserSettings[] = "InvalidUserSettings";
48 const char kNetworkAlreadyConfigured[] = "NetworkAlreadyConfigured";
49 const char kPoliciesNotInitialized[] = "PoliciesNotInitialized";
50 const char kProfileNotInitialized[] = "ProflieNotInitialized";
51 const char kSetOnUnconfiguredNetwork[] = "SetCalledOnUnconfiguredNetwork";
52 const char kUnknownProfilePath[] = "UnknownProfilePath";
53 const char kUnknownServicePath[] = "UnknownServicePath";
54
55 std::string ToDebugString(::onc::ONCSource source,
56                           const std::string& userhash) {
57   return source == ::onc::ONC_SOURCE_USER_POLICY ?
58       ("user policy of " + userhash) : "device policy";
59 }
60
61 void InvokeErrorCallback(const std::string& service_path,
62                          const network_handler::ErrorCallback& error_callback,
63                          const std::string& error_name) {
64   std::string error_msg = "ManagedConfig Error: " + error_name;
65   NET_LOG_ERROR(error_msg, service_path);
66   network_handler::RunErrorCallback(
67       error_callback, service_path, error_name, error_msg);
68 }
69
70 void LogErrorWithDict(const tracked_objects::Location& from_where,
71                       const std::string& error_name,
72                       scoped_ptr<base::DictionaryValue> error_data) {
73   network_event_log::internal::AddEntry(
74        from_where.file_name(), from_where.line_number(),
75        network_event_log::LOG_LEVEL_ERROR,
76        error_name, "");
77 }
78
79 const base::DictionaryValue* GetByGUID(const GuidToPolicyMap& policies,
80                                        const std::string& guid) {
81   GuidToPolicyMap::const_iterator it = policies.find(guid);
82   if (it == policies.end())
83     return NULL;
84   return it->second;
85 }
86
87 }  // namespace
88
89 struct ManagedNetworkConfigurationHandlerImpl::Policies {
90   ~Policies();
91
92   GuidToPolicyMap per_network_config;
93   base::DictionaryValue global_network_config;
94 };
95
96 ManagedNetworkConfigurationHandlerImpl::Policies::~Policies() {
97   STLDeleteValues(&per_network_config);
98 }
99
100 void ManagedNetworkConfigurationHandlerImpl::AddObserver(
101     NetworkPolicyObserver* observer) {
102   observers_.AddObserver(observer);
103 }
104
105 void ManagedNetworkConfigurationHandlerImpl::RemoveObserver(
106     NetworkPolicyObserver* observer) {
107   observers_.RemoveObserver(observer);
108 }
109
110 void ManagedNetworkConfigurationHandlerImpl::GetManagedProperties(
111     const std::string& userhash,
112     const std::string& service_path,
113     const network_handler::DictionaryResultCallback& callback,
114     const network_handler::ErrorCallback& error_callback) {
115   if (!GetPoliciesForUser(userhash) || !GetPoliciesForUser(std::string())) {
116     InvokeErrorCallback(service_path, error_callback, kPoliciesNotInitialized);
117     return;
118   }
119   network_configuration_handler_->GetProperties(
120       service_path,
121       base::Bind(
122           &ManagedNetworkConfigurationHandlerImpl::GetManagedPropertiesCallback,
123           weak_ptr_factory_.GetWeakPtr(),
124           callback,
125           error_callback),
126       error_callback);
127 }
128
129 void ManagedNetworkConfigurationHandlerImpl::GetManagedPropertiesCallback(
130     const network_handler::DictionaryResultCallback& callback,
131     const network_handler::ErrorCallback& error_callback,
132     const std::string& service_path,
133     const base::DictionaryValue& shill_properties) {
134   std::string profile_path;
135   shill_properties.GetStringWithoutPathExpansion(shill::kProfileProperty,
136                                                  &profile_path);
137   const NetworkProfile* profile =
138       network_profile_handler_->GetProfileForPath(profile_path);
139   if (!profile)
140     NET_LOG_ERROR("No profile for service: " + profile_path, service_path);
141
142   scoped_ptr<NetworkUIData> ui_data =
143       shill_property_util::GetUIDataFromProperties(shill_properties);
144
145   const base::DictionaryValue* user_settings = NULL;
146   const base::DictionaryValue* shared_settings = NULL;
147
148   if (ui_data && profile) {
149     if (profile->type() == NetworkProfile::TYPE_SHARED)
150       shared_settings = ui_data->user_settings();
151     else if (profile->type() == NetworkProfile::TYPE_USER)
152       user_settings = ui_data->user_settings();
153     else
154       NOTREACHED();
155   } else if (profile) {
156     NET_LOG_ERROR("Service contains empty or invalid UIData", service_path);
157     // TODO(pneubeck): add a conversion of user configured entries of old
158     // ChromeOS versions. We will have to use a heuristic to determine which
159     // properties _might_ be user configured.
160   }
161
162   scoped_ptr<base::DictionaryValue> properties_copy(
163       shill_properties.DeepCopy());
164   // Add the IPConfigs to the dictionary before the ONC translation.
165   GetIPConfigs(service_path, properties_copy.get());
166
167   scoped_ptr<base::DictionaryValue> active_settings(
168       onc::TranslateShillServiceToONCPart(
169           *properties_copy,
170           &onc::kNetworkWithStateSignature));
171
172   std::string guid;
173   active_settings->GetStringWithoutPathExpansion(::onc::network_config::kGUID,
174                                                  &guid);
175
176   const base::DictionaryValue* user_policy = NULL;
177   const base::DictionaryValue* device_policy = NULL;
178   if (!guid.empty() && profile) {
179     const Policies* policies = GetPoliciesForProfile(*profile);
180     if (!policies) {
181       InvokeErrorCallback(
182           service_path, error_callback, kPoliciesNotInitialized);
183       return;
184     }
185     const base::DictionaryValue* policy =
186         GetByGUID(policies->per_network_config, guid);
187     if (profile->type() == NetworkProfile::TYPE_SHARED)
188       device_policy = policy;
189     else if (profile->type() == NetworkProfile::TYPE_USER)
190       user_policy = policy;
191     else
192       NOTREACHED();
193   }
194
195   // This call also removes credentials from policies.
196   scoped_ptr<base::DictionaryValue> augmented_properties =
197       onc::MergeSettingsAndPoliciesToAugmented(
198           onc::kNetworkConfigurationSignature,
199           user_policy,
200           device_policy,
201           user_settings,
202           shared_settings,
203           active_settings.get());
204   callback.Run(service_path, *augmented_properties);
205 }
206
207 void ManagedNetworkConfigurationHandlerImpl::GetProperties(
208     const std::string& service_path,
209     const network_handler::DictionaryResultCallback& callback,
210     const network_handler::ErrorCallback& error_callback) {
211   network_configuration_handler_->GetProperties(
212       service_path,
213       base::Bind(&ManagedNetworkConfigurationHandlerImpl::GetPropertiesCallback,
214                  weak_ptr_factory_.GetWeakPtr(),
215                  callback),
216       error_callback);
217 }
218
219 void ManagedNetworkConfigurationHandlerImpl::GetPropertiesCallback(
220     const network_handler::DictionaryResultCallback& callback,
221     const std::string& service_path,
222     const base::DictionaryValue& shill_properties) {
223   scoped_ptr<base::DictionaryValue> properties_copy(
224       shill_properties.DeepCopy());
225   // Add the IPConfigs to the dictionary before the ONC translation.
226   GetIPConfigs(service_path, properties_copy.get());
227
228   scoped_ptr<base::DictionaryValue> onc_network(
229       onc::TranslateShillServiceToONCPart(
230           *properties_copy,
231           &onc::kNetworkWithStateSignature));
232   callback.Run(service_path, *onc_network);
233 }
234
235 void ManagedNetworkConfigurationHandlerImpl::SetProperties(
236     const std::string& service_path,
237     const base::DictionaryValue& user_settings,
238     const base::Closure& callback,
239     const network_handler::ErrorCallback& error_callback) const {
240   const NetworkState* state =
241       network_state_handler_->GetNetworkState(service_path);
242
243   if (!state) {
244     InvokeErrorCallback(service_path, error_callback, kUnknownServicePath);
245     return;
246   }
247
248   std::string guid = state->guid();
249   if (guid.empty()) {
250     // TODO(pneubeck): create an initial configuration in this case. As for
251     // CreateConfiguration, user settings from older ChromeOS versions have to
252     // determined here.
253     InvokeErrorCallback(
254         service_path, error_callback, kSetOnUnconfiguredNetwork);
255     return;
256   }
257
258   const std::string& profile_path = state->profile_path();
259   const NetworkProfile *profile =
260       network_profile_handler_->GetProfileForPath(profile_path);
261   if (!profile) {
262     InvokeErrorCallback(service_path, error_callback, kUnknownProfilePath);
263     return;
264   }
265
266   VLOG(2) << "SetProperties: Found GUID " << guid << " and profile "
267           << profile->ToDebugString();
268
269   const Policies* policies = GetPoliciesForProfile(*profile);
270   if (!policies) {
271     InvokeErrorCallback(service_path, error_callback, kPoliciesNotInitialized);
272     return;
273   }
274
275   // Validate the ONC dictionary. We are liberal and ignore unknown field
276   // names. User settings are only partial ONC, thus we ignore missing fields.
277   onc::Validator validator(false,  // Ignore unknown fields.
278                            false,  // Ignore invalid recommended field names.
279                            false,  // Ignore missing fields.
280                            false);  // This ONC does not come from policy.
281
282   onc::Validator::Result validation_result;
283   scoped_ptr<base::DictionaryValue> validated_user_settings =
284       validator.ValidateAndRepairObject(
285           &onc::kNetworkConfigurationSignature,
286           user_settings,
287           &validation_result);
288
289   if (validation_result == onc::Validator::INVALID) {
290     InvokeErrorCallback(service_path, error_callback, kInvalidUserSettings);
291     return;
292   }
293   if (validation_result == onc::Validator::VALID_WITH_WARNINGS)
294     LOG(WARNING) << "Validation of ONC user settings produced warnings.";
295
296   const base::DictionaryValue* policy =
297       GetByGUID(policies->per_network_config, guid);
298   VLOG(2) << "This configuration is " << (policy ? "" : "not ") << "managed.";
299
300   scoped_ptr<base::DictionaryValue> shill_dictionary(
301       policy_util::CreateShillConfiguration(
302           *profile, guid, policy, validated_user_settings.get()));
303
304   network_configuration_handler_->SetProperties(
305       service_path, *shill_dictionary, callback, error_callback);
306 }
307
308 void ManagedNetworkConfigurationHandlerImpl::CreateConfiguration(
309     const std::string& userhash,
310     const base::DictionaryValue& properties,
311     const network_handler::StringResultCallback& callback,
312     const network_handler::ErrorCallback& error_callback) const {
313   const Policies* policies = GetPoliciesForUser(userhash);
314   if (!policies) {
315     InvokeErrorCallback("", error_callback, kPoliciesNotInitialized);
316     return;
317   }
318
319   if (policy_util::FindMatchingPolicy(policies->per_network_config,
320                                       properties)) {
321     InvokeErrorCallback("", error_callback, kNetworkAlreadyConfigured);
322     return;
323   }
324
325   const NetworkProfile* profile =
326       network_profile_handler_->GetProfileForUserhash(userhash);
327   if (!profile) {
328     InvokeErrorCallback("", error_callback, kProfileNotInitialized);
329     return;
330   }
331
332   // TODO(pneubeck): In case of WiFi, check that no other configuration for the
333   // same {SSID, mode, security} exists. We don't support such multiple
334   // configurations, yet.
335
336   // Generate a new GUID for this configuration. Ignore the maybe provided GUID
337   // in |properties| as it is not our own and from an untrusted source.
338   std::string guid = base::GenerateGUID();
339   scoped_ptr<base::DictionaryValue> shill_dictionary(
340       policy_util::CreateShillConfiguration(
341           *profile, guid, NULL /*no policy*/, &properties));
342
343   network_configuration_handler_->CreateConfiguration(
344       *shill_dictionary, callback, error_callback);
345 }
346
347 void ManagedNetworkConfigurationHandlerImpl::RemoveConfiguration(
348     const std::string& service_path,
349     const base::Closure& callback,
350     const network_handler::ErrorCallback& error_callback) const {
351   network_configuration_handler_->RemoveConfiguration(
352       service_path, callback, error_callback);
353 }
354
355 void ManagedNetworkConfigurationHandlerImpl::SetPolicy(
356     ::onc::ONCSource onc_source,
357     const std::string& userhash,
358     const base::ListValue& network_configs_onc,
359     const base::DictionaryValue& global_network_config) {
360   VLOG(1) << "Setting policies from " << ToDebugString(onc_source, userhash)
361           << ".";
362
363   // |userhash| must be empty for device policies.
364   DCHECK(onc_source != ::onc::ONC_SOURCE_DEVICE_POLICY ||
365          userhash.empty());
366   Policies* policies = NULL;
367   if (ContainsKey(policies_by_user_, userhash)) {
368     policies = policies_by_user_[userhash].get();
369   } else {
370     policies = new Policies;
371     policies_by_user_[userhash] = make_linked_ptr(policies);
372   }
373
374   policies->global_network_config.MergeDictionary(&global_network_config);
375
376   GuidToPolicyMap old_per_network_config;
377   policies->per_network_config.swap(old_per_network_config);
378
379   // This stores all GUIDs of policies that have changed or are new.
380   std::set<std::string> modified_policies;
381
382   for (base::ListValue::const_iterator it = network_configs_onc.begin();
383        it != network_configs_onc.end(); ++it) {
384     const base::DictionaryValue* network = NULL;
385     (*it)->GetAsDictionary(&network);
386     DCHECK(network);
387
388     std::string guid;
389     network->GetStringWithoutPathExpansion(::onc::network_config::kGUID, &guid);
390     DCHECK(!guid.empty());
391
392     if (policies->per_network_config.count(guid) > 0) {
393       NET_LOG_ERROR("ONC from " + ToDebugString(onc_source, userhash) +
394                     " contains several entries for the same GUID ", guid);
395       delete policies->per_network_config[guid];
396     }
397     const base::DictionaryValue* new_entry = network->DeepCopy();
398     policies->per_network_config[guid] = new_entry;
399
400     const base::DictionaryValue* old_entry = old_per_network_config[guid];
401     if (!old_entry || !old_entry->Equals(new_entry))
402       modified_policies.insert(guid);
403   }
404
405   STLDeleteValues(&old_per_network_config);
406
407   const NetworkProfile* profile =
408       network_profile_handler_->GetProfileForUserhash(userhash);
409   if (!profile) {
410     VLOG(1) << "The relevant Shill profile isn't initialized yet, postponing "
411             << "policy application.";
412     return;
413   }
414
415   scoped_refptr<PolicyApplicator> applicator =
416       new PolicyApplicator(weak_ptr_factory_.GetWeakPtr(),
417                            *profile,
418                            policies->per_network_config,
419                            policies->global_network_config,
420                            &modified_policies);
421   applicator->Run();
422 }
423
424 void ManagedNetworkConfigurationHandlerImpl::OnProfileAdded(
425     const NetworkProfile& profile) {
426   VLOG(1) << "Adding profile " << profile.ToDebugString() << "'.";
427
428   const Policies* policies = GetPoliciesForProfile(profile);
429   if (!policies) {
430     VLOG(1) << "The relevant policy is not initialized, "
431             << "postponing policy application.";
432     return;
433   }
434
435   std::set<std::string> policy_guids;
436   for (GuidToPolicyMap::const_iterator it =
437            policies->per_network_config.begin();
438        it != policies->per_network_config.end(); ++it) {
439     policy_guids.insert(it->first);
440   }
441
442   scoped_refptr<PolicyApplicator> applicator =
443       new PolicyApplicator(weak_ptr_factory_.GetWeakPtr(),
444                            profile,
445                            policies->per_network_config,
446                            policies->global_network_config,
447                            &policy_guids);
448   applicator->Run();
449 }
450
451 void ManagedNetworkConfigurationHandlerImpl::OnProfileRemoved(
452     const NetworkProfile& profile) {
453   // Nothing to do in this case.
454 }
455
456 void ManagedNetworkConfigurationHandlerImpl::CreateConfigurationFromPolicy(
457     const base::DictionaryValue& shill_properties) {
458   network_configuration_handler_->CreateConfiguration(
459       shill_properties,
460       base::Bind(
461           &ManagedNetworkConfigurationHandlerImpl::OnPolicyAppliedToNetwork,
462           weak_ptr_factory_.GetWeakPtr()),
463       base::Bind(&LogErrorWithDict, FROM_HERE));
464 }
465
466 void ManagedNetworkConfigurationHandlerImpl::
467     UpdateExistingConfigurationWithPropertiesFromPolicy(
468         const base::DictionaryValue& existing_properties,
469         const base::DictionaryValue& new_properties) {
470   base::DictionaryValue shill_properties;
471
472   std::string profile;
473   existing_properties.GetStringWithoutPathExpansion(shill::kProfileProperty,
474                                                     &profile);
475   if (profile.empty()) {
476     NET_LOG_ERROR("Missing profile property",
477                   shill_property_util::GetNetworkIdFromProperties(
478                       existing_properties));
479     return;
480   }
481   shill_properties.SetStringWithoutPathExpansion(shill::kProfileProperty,
482                                                  profile);
483
484   if (!shill_property_util::CopyIdentifyingProperties(existing_properties,
485                                                       &shill_properties)) {
486     NET_LOG_ERROR("Missing identifying properties",
487                   shill_property_util::GetNetworkIdFromProperties(
488                       existing_properties));
489   }
490
491   shill_properties.MergeDictionary(&new_properties);
492
493   network_configuration_handler_->CreateConfiguration(
494       shill_properties,
495       base::Bind(
496           &ManagedNetworkConfigurationHandlerImpl::OnPolicyAppliedToNetwork,
497           weak_ptr_factory_.GetWeakPtr()),
498       base::Bind(&LogErrorWithDict, FROM_HERE));
499 }
500
501 void ManagedNetworkConfigurationHandlerImpl::OnPoliciesApplied() {
502   // After all policies were applied, trigger an update of the network lists.
503   if (network_state_handler_)
504     network_state_handler_->UpdateManagerProperties();
505 }
506
507 const base::DictionaryValue*
508 ManagedNetworkConfigurationHandlerImpl::FindPolicyByGUID(
509     const std::string userhash,
510     const std::string& guid,
511     ::onc::ONCSource* onc_source) const {
512   *onc_source = ::onc::ONC_SOURCE_NONE;
513
514   if (!userhash.empty()) {
515     const Policies* user_policies = GetPoliciesForUser(userhash);
516     if (user_policies) {
517       const base::DictionaryValue* policy =
518           GetByGUID(user_policies->per_network_config, guid);
519       if (policy) {
520         *onc_source = ::onc::ONC_SOURCE_USER_POLICY;
521         return policy;
522       }
523     }
524   }
525
526   const Policies* device_policies = GetPoliciesForUser(std::string());
527   if (device_policies) {
528     const base::DictionaryValue* policy =
529         GetByGUID(device_policies->per_network_config, guid);
530     if (policy) {
531       *onc_source = ::onc::ONC_SOURCE_DEVICE_POLICY;
532       return policy;
533     }
534   }
535
536   return NULL;
537 }
538
539 const base::DictionaryValue*
540 ManagedNetworkConfigurationHandlerImpl::GetGlobalConfigFromPolicy(
541     const std::string userhash) const {
542   const Policies* policies = GetPoliciesForUser(userhash);
543   if (!policies)
544     return NULL;
545
546   return &policies->global_network_config;
547 }
548 const base::DictionaryValue*
549 ManagedNetworkConfigurationHandlerImpl::FindPolicyByGuidAndProfile(
550     const std::string& guid,
551     const std::string& profile_path) const {
552   const NetworkProfile* profile =
553       network_profile_handler_->GetProfileForPath(profile_path);
554   if (!profile) {
555     NET_LOG_ERROR("Profile path unknown:" + profile_path, guid);
556     return NULL;
557   }
558
559   const Policies* policies = GetPoliciesForProfile(*profile);
560   if (!policies)
561     return NULL;
562
563   return GetByGUID(policies->per_network_config, guid);
564 }
565
566 const ManagedNetworkConfigurationHandlerImpl::Policies*
567 ManagedNetworkConfigurationHandlerImpl::GetPoliciesForUser(
568     const std::string& userhash) const {
569   UserToPoliciesMap::const_iterator it = policies_by_user_.find(userhash);
570   if (it == policies_by_user_.end())
571     return NULL;
572   return it->second.get();
573 }
574
575 const ManagedNetworkConfigurationHandlerImpl::Policies*
576 ManagedNetworkConfigurationHandlerImpl::GetPoliciesForProfile(
577     const NetworkProfile& profile) const {
578   DCHECK(profile.type() != NetworkProfile::TYPE_SHARED ||
579          profile.userhash.empty());
580   return GetPoliciesForUser(profile.userhash);
581 }
582
583 ManagedNetworkConfigurationHandlerImpl::ManagedNetworkConfigurationHandlerImpl()
584     : network_state_handler_(NULL),
585       network_profile_handler_(NULL),
586       network_configuration_handler_(NULL),
587       weak_ptr_factory_(this) {}
588
589 ManagedNetworkConfigurationHandlerImpl::
590     ~ManagedNetworkConfigurationHandlerImpl() {
591   network_profile_handler_->RemoveObserver(this);
592 }
593
594 void ManagedNetworkConfigurationHandlerImpl::Init(
595     NetworkStateHandler* network_state_handler,
596     NetworkProfileHandler* network_profile_handler,
597     NetworkConfigurationHandler* network_configuration_handler) {
598   network_state_handler_ = network_state_handler;
599   network_profile_handler_ = network_profile_handler;
600   network_configuration_handler_ = network_configuration_handler;
601   network_profile_handler_->AddObserver(this);
602 }
603
604 void ManagedNetworkConfigurationHandlerImpl::OnPolicyAppliedToNetwork(
605     const std::string& service_path) {
606   if (service_path.empty())
607     return;
608   FOR_EACH_OBSERVER(
609       NetworkPolicyObserver, observers_, PolicyApplied(service_path));
610 }
611
612 void ManagedNetworkConfigurationHandlerImpl::GetIPConfigs(
613     const std::string& service_path,
614     base::DictionaryValue* properties) {
615   std::string connection_state;
616   properties->GetStringWithoutPathExpansion(
617       shill::kStateProperty, &connection_state);
618   if (!NetworkState::StateIsConnected(connection_state))
619     return;
620
621   // Get the IPConfig properties from the device and store them in "IPConfigs"
622   // (plural) in the properties dictionary. (Note: Shill only provides a single
623   // "IPConfig" property for a network service, but a consumer of this API may
624   // want information about all ipv4 and ipv6 IPConfig properties.
625   std::string device;
626   properties->GetStringWithoutPathExpansion(shill::kDeviceProperty, &device);
627   const DeviceState* device_state =
628       network_state_handler_->GetDeviceState(device);
629   if (!device_state) {
630     NET_LOG_ERROR("GetIPConfigs: no device: " + device, service_path);
631     return;
632   }
633   // Convert IPConfig dictionary to a ListValue.
634   base::ListValue* ip_configs = new base::ListValue;
635   for (base::DictionaryValue::Iterator iter(device_state->ip_configs());
636        !iter.IsAtEnd(); iter.Advance()) {
637     ip_configs->Append(iter.value().DeepCopy());
638   }
639   properties->SetWithoutPathExpansion(shill::kIPConfigsProperty, ip_configs);
640 }
641
642 }  // namespace chromeos