1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "chrome/browser/chromeos/net/onc_utils.h"
7 #include "base/bind_helpers.h"
8 #include "base/json/json_writer.h"
9 #include "base/logging.h"
10 #include "base/prefs/pref_service.h"
11 #include "base/values.h"
12 #include "chrome/browser/chromeos/login/user.h"
13 #include "chrome/browser/chromeos/login/user_manager.h"
14 #include "chrome/browser/chromeos/ui_proxy_config.h"
15 #include "chrome/browser/prefs/proxy_config_dictionary.h"
16 #include "chrome/common/pref_names.h"
17 #include "chromeos/network/favorite_state.h"
18 #include "chromeos/network/managed_network_configuration_handler.h"
19 #include "chromeos/network/network_configuration_handler.h"
20 #include "chromeos/network/network_handler.h"
21 #include "chromeos/network/network_profile.h"
22 #include "chromeos/network/network_profile_handler.h"
23 #include "chromeos/network/network_state.h"
24 #include "chromeos/network/network_state_handler.h"
25 #include "chromeos/network/network_ui_data.h"
26 #include "chromeos/network/onc/onc_normalizer.h"
27 #include "chromeos/network/onc/onc_signature.h"
28 #include "chromeos/network/onc/onc_translator.h"
29 #include "chromeos/network/onc/onc_utils.h"
30 #include "chromeos/network/shill_property_util.h"
31 #include "net/base/host_port_pair.h"
32 #include "net/proxy/proxy_bypass_rules.h"
33 #include "net/proxy/proxy_server.h"
34 #include "third_party/cros_system_api/dbus/service_constants.h"
42 net::ProxyServer
ConvertOncProxyLocationToHostPort(
43 net::ProxyServer::Scheme default_proxy_scheme
,
44 const base::DictionaryValue
& onc_proxy_location
) {
46 onc_proxy_location
.GetStringWithoutPathExpansion(::onc::proxy::kHost
, &host
);
47 // Parse |host| according to the format [<scheme>"://"]<server>[":"<port>].
48 net::ProxyServer proxy_server
=
49 net::ProxyServer::FromURI(host
, default_proxy_scheme
);
51 onc_proxy_location
.GetIntegerWithoutPathExpansion(::onc::proxy::kPort
, &port
);
53 // Replace the port parsed from |host| by the provided |port|.
54 return net::ProxyServer(
55 proxy_server
.scheme(),
56 net::HostPortPair(proxy_server
.host_port_pair().host(),
57 static_cast<uint16
>(port
)));
60 void AppendProxyServerForScheme(
61 const base::DictionaryValue
& onc_manual
,
62 const std::string
& onc_scheme
,
64 const base::DictionaryValue
* onc_proxy_location
= NULL
;
65 if (!onc_manual
.GetDictionaryWithoutPathExpansion(onc_scheme
,
66 &onc_proxy_location
)) {
70 net::ProxyServer::Scheme default_proxy_scheme
= net::ProxyServer::SCHEME_HTTP
;
71 std::string url_scheme
;
72 if (onc_scheme
== ::onc::proxy::kFtp
) {
74 } else if (onc_scheme
== ::onc::proxy::kHttp
) {
76 } else if (onc_scheme
== ::onc::proxy::kHttps
) {
78 } else if (onc_scheme
== ::onc::proxy::kSocks
) {
79 default_proxy_scheme
= net::ProxyServer::SCHEME_SOCKS4
;
85 net::ProxyServer proxy_server
= ConvertOncProxyLocationToHostPort(
86 default_proxy_scheme
, *onc_proxy_location
);
88 UIProxyConfig::EncodeAndAppendProxyServer(url_scheme
, proxy_server
, spec
);
91 net::ProxyBypassRules
ConvertOncExcludeDomainsToBypassRules(
92 const base::ListValue
& onc_exclude_domains
) {
93 net::ProxyBypassRules rules
;
94 for (base::ListValue::const_iterator it
= onc_exclude_domains
.begin();
95 it
!= onc_exclude_domains
.end(); ++it
) {
97 (*it
)->GetAsString(&rule
);
98 rules
.AddRuleFromString(rule
);
105 scoped_ptr
<base::DictionaryValue
> ConvertOncProxySettingsToProxyConfig(
106 const base::DictionaryValue
& onc_proxy_settings
) {
108 onc_proxy_settings
.GetStringWithoutPathExpansion(::onc::proxy::kType
, &type
);
109 scoped_ptr
<base::DictionaryValue
> proxy_dict
;
111 if (type
== ::onc::proxy::kDirect
) {
112 proxy_dict
.reset(ProxyConfigDictionary::CreateDirect());
113 } else if (type
== ::onc::proxy::kWPAD
) {
114 proxy_dict
.reset(ProxyConfigDictionary::CreateAutoDetect());
115 } else if (type
== ::onc::proxy::kPAC
) {
117 onc_proxy_settings
.GetStringWithoutPathExpansion(::onc::proxy::kPAC
,
120 DCHECK(url
.is_valid())
121 << "PAC field is invalid for this ProxySettings.Type";
122 proxy_dict
.reset(ProxyConfigDictionary::CreatePacScript(url
.spec(),
124 } else if (type
== ::onc::proxy::kManual
) {
125 const base::DictionaryValue
* manual_dict
= NULL
;
126 onc_proxy_settings
.GetDictionaryWithoutPathExpansion(::onc::proxy::kManual
,
128 std::string manual_spec
;
129 AppendProxyServerForScheme(*manual_dict
, ::onc::proxy::kFtp
, &manual_spec
);
130 AppendProxyServerForScheme(*manual_dict
, ::onc::proxy::kHttp
, &manual_spec
);
131 AppendProxyServerForScheme(*manual_dict
, ::onc::proxy::kSocks
,
133 AppendProxyServerForScheme(*manual_dict
, ::onc::proxy::kHttps
,
136 const base::ListValue
* exclude_domains
= NULL
;
137 net::ProxyBypassRules bypass_rules
;
138 if (onc_proxy_settings
.GetListWithoutPathExpansion(
139 ::onc::proxy::kExcludeDomains
, &exclude_domains
)) {
140 bypass_rules
.AssignFrom(
141 ConvertOncExcludeDomainsToBypassRules(*exclude_domains
));
143 proxy_dict
.reset(ProxyConfigDictionary::CreateFixedServers(
144 manual_spec
, bypass_rules
.ToString()));
148 return proxy_dict
.Pass();
153 // This class defines which string placeholders of ONC are replaced by which
155 class UserStringSubstitution
: public chromeos::onc::StringSubstitution
{
157 explicit UserStringSubstitution(const chromeos::User
* user
) : user_(user
) {}
158 virtual ~UserStringSubstitution() {}
160 virtual bool GetSubstitute(const std::string
& placeholder
,
161 std::string
* substitute
) const OVERRIDE
{
162 if (placeholder
== ::onc::substitutes::kLoginIDField
)
163 *substitute
= user_
->GetAccountName(false);
164 else if (placeholder
== ::onc::substitutes::kEmailField
)
165 *substitute
= user_
->email();
172 const chromeos::User
* user_
;
174 DISALLOW_COPY_AND_ASSIGN(UserStringSubstitution
);
179 void ExpandStringPlaceholdersInNetworksForUser(
180 const chromeos::User
* user
,
181 base::ListValue
* network_configs
) {
183 // In tests no user may be logged in. It's not harmful if we just don't
184 // expand the strings.
187 UserStringSubstitution
substitution(user
);
188 chromeos::onc::ExpandStringsInNetworks(substitution
, network_configs
);
191 void ImportNetworksForUser(const chromeos::User
* user
,
192 const base::ListValue
& network_configs
,
193 std::string
* error
) {
196 scoped_ptr
<base::ListValue
> expanded_networks(network_configs
.DeepCopy());
197 ExpandStringPlaceholdersInNetworksForUser(user
, expanded_networks
.get());
199 const NetworkProfile
* profile
=
200 NetworkHandler::Get()->network_profile_handler()->GetProfileForUserhash(
201 user
->username_hash());
203 *error
= "User profile doesn't exist.";
207 bool ethernet_not_found
= false;
208 for (base::ListValue::const_iterator it
= expanded_networks
->begin();
209 it
!= expanded_networks
->end();
211 const base::DictionaryValue
* network
= NULL
;
212 (*it
)->GetAsDictionary(&network
);
215 // Remove irrelevant fields.
216 onc::Normalizer
normalizer(true /* remove recommended fields */);
217 scoped_ptr
<base::DictionaryValue
> normalized_network
=
218 normalizer
.NormalizeObject(&onc::kNetworkConfigurationSignature
,
221 scoped_ptr
<base::DictionaryValue
> shill_dict
=
222 onc::TranslateONCObjectToShill(&onc::kNetworkConfigurationSignature
,
223 *normalized_network
);
225 scoped_ptr
<NetworkUIData
> ui_data
= NetworkUIData::CreateFromONC(
226 ::onc::ONC_SOURCE_USER_IMPORT
, *normalized_network
);
227 base::DictionaryValue ui_data_dict
;
228 ui_data
->FillDictionary(&ui_data_dict
);
229 std::string ui_data_json
;
230 base::JSONWriter::Write(&ui_data_dict
, &ui_data_json
);
231 shill_dict
->SetStringWithoutPathExpansion(shill::kUIDataProperty
,
234 shill_dict
->SetStringWithoutPathExpansion(shill::kProfileProperty
,
238 shill_dict
->GetStringWithoutPathExpansion(shill::kTypeProperty
, &type
);
239 NetworkConfigurationHandler
* config_handler
=
240 NetworkHandler::Get()->network_configuration_handler();
241 if (NetworkTypePattern::Ethernet().MatchesType(type
)) {
242 // Ethernet has to be configured using an existing Ethernet service.
243 const NetworkState
* ethernet
=
244 NetworkHandler::Get()->network_state_handler()->FirstNetworkByType(
245 NetworkTypePattern::Ethernet());
247 config_handler
->SetProperties(ethernet
->path(),
250 network_handler::ErrorCallback());
252 ethernet_not_found
= true;
256 config_handler
->CreateConfiguration(
258 network_handler::StringResultCallback(),
259 network_handler::ErrorCallback());
263 if (ethernet_not_found
)
264 *error
= "No Ethernet available to configure.";
267 const base::DictionaryValue
* FindPolicyForActiveUser(
268 const std::string
& guid
,
269 ::onc::ONCSource
* onc_source
) {
270 const User
* user
= UserManager::Get()->GetActiveUser();
271 std::string username_hash
= user
? user
->username_hash() : std::string();
272 return NetworkHandler::Get()->managed_network_configuration_handler()->
273 FindPolicyByGUID(username_hash
, guid
, onc_source
);
276 const base::DictionaryValue
* GetGlobalConfigFromPolicy(bool for_active_user
) {
277 std::string username_hash
;
278 if (for_active_user
) {
279 const User
* user
= UserManager::Get()->GetActiveUser();
281 LOG(ERROR
) << "No user logged in yet.";
284 username_hash
= user
->username_hash();
286 return NetworkHandler::Get()->managed_network_configuration_handler()->
287 GetGlobalConfigFromPolicy(username_hash
);
290 bool PolicyAllowsOnlyPolicyNetworksToAutoconnect(bool for_active_user
) {
291 const base::DictionaryValue
* global_config
=
292 GetGlobalConfigFromPolicy(for_active_user
);
294 return false; // By default, all networks are allowed to autoconnect.
296 bool only_policy_autoconnect
= false;
297 global_config
->GetBooleanWithoutPathExpansion(
298 ::onc::global_network_config::kAllowOnlyPolicyNetworksToAutoconnect
,
299 &only_policy_autoconnect
);
300 return only_policy_autoconnect
;
305 const base::DictionaryValue
* GetNetworkConfigByGUID(
306 const base::ListValue
& network_configs
,
307 const std::string
& guid
) {
308 for (base::ListValue::const_iterator it
= network_configs
.begin();
309 it
!= network_configs
.end(); ++it
) {
310 const base::DictionaryValue
* network
= NULL
;
311 (*it
)->GetAsDictionary(&network
);
314 std::string current_guid
;
315 network
->GetStringWithoutPathExpansion(::onc::network_config::kGUID
,
317 if (current_guid
== guid
)
323 const base::DictionaryValue
* GetNetworkConfigForEthernetWithoutEAP(
324 const base::ListValue
& network_configs
) {
325 VLOG(2) << "Search for ethernet policy without EAP.";
326 for (base::ListValue::const_iterator it
= network_configs
.begin();
327 it
!= network_configs
.end(); ++it
) {
328 const base::DictionaryValue
* network
= NULL
;
329 (*it
)->GetAsDictionary(&network
);
333 network
->GetStringWithoutPathExpansion(::onc::network_config::kType
, &type
);
334 if (type
!= ::onc::network_type::kEthernet
)
337 const base::DictionaryValue
* ethernet
= NULL
;
338 network
->GetDictionaryWithoutPathExpansion(::onc::network_config::kEthernet
,
342 ethernet
->GetStringWithoutPathExpansion(::onc::ethernet::kAuthentication
,
344 if (auth
== ::onc::ethernet::kNone
)
350 const base::DictionaryValue
* GetNetworkConfigForNetworkFromOnc(
351 const base::ListValue
& network_configs
,
352 const FavoriteState
& favorite
) {
353 // In all cases except Ethernet, we use the GUID of |network|.
354 if (!favorite
.Matches(NetworkTypePattern::Ethernet()))
355 return GetNetworkConfigByGUID(network_configs
, favorite
.guid());
357 // Ethernet is always shared and thus cannot store a GUID per user. Thus we
358 // search for any Ethernet policy intead of a matching GUID.
359 // EthernetEAP service contains only the EAP parameters and stores the GUID of
360 // the respective ONC policy. The EthernetEAP service itself is however never
361 // in state "connected". An EthernetEAP policy must be applied, if an Ethernet
362 // service is connected using the EAP parameters.
363 const FavoriteState
* ethernet_eap
= NULL
;
364 if (NetworkHandler::IsInitialized()) {
366 NetworkHandler::Get()->network_state_handler()->GetEAPForEthernet(
370 // The GUID associated with the EthernetEAP service refers to the ONC policy
371 // with "Authentication: 8021X".
373 return GetNetworkConfigByGUID(network_configs
, ethernet_eap
->guid());
375 // Otherwise, EAP is not used and instead the Ethernet policy with
376 // "Authentication: None" applies.
377 return GetNetworkConfigForEthernetWithoutEAP(network_configs
);
380 const base::DictionaryValue
* GetPolicyForNetworkFromPref(
381 const PrefService
* pref_service
,
382 const char* pref_name
,
383 const FavoriteState
& favorite
) {
385 VLOG(2) << "No pref service";
389 const PrefService::Preference
* preference
=
390 pref_service
->FindPreference(pref_name
);
392 VLOG(2) << "No preference " << pref_name
;
393 // The preference may not exist in tests.
397 // User prefs are not stored in this Preference yet but only the policy.
399 // The policy server incorrectly configures the OpenNetworkConfiguration user
400 // policy as Recommended. To work around that, we handle the Recommended and
401 // the Mandatory value in the same way.
402 // TODO(pneubeck): Remove this workaround, once the server is fixed. See
403 // http://crbug.com/280553 .
404 if (preference
->IsDefaultValue()) {
405 VLOG(2) << "Preference has no recommended or mandatory value.";
409 VLOG(2) << "Preference with policy found.";
410 const base::Value
* onc_policy_value
= preference
->GetValue();
411 DCHECK(onc_policy_value
);
413 const base::ListValue
* onc_policy
= NULL
;
414 onc_policy_value
->GetAsList(&onc_policy
);
417 return GetNetworkConfigForNetworkFromOnc(*onc_policy
, favorite
);
422 const base::DictionaryValue
* GetPolicyForFavoriteNetwork(
423 const PrefService
* profile_prefs
,
424 const PrefService
* local_state_prefs
,
425 const FavoriteState
& favorite
,
426 ::onc::ONCSource
* onc_source
) {
427 VLOG(2) << "GetPolicyForFavoriteNetwork: " << favorite
.path();
428 *onc_source
= ::onc::ONC_SOURCE_NONE
;
430 const base::DictionaryValue
* network_policy
= GetPolicyForNetworkFromPref(
431 profile_prefs
, prefs::kOpenNetworkConfiguration
, favorite
);
432 if (network_policy
) {
433 VLOG(1) << "Network " << favorite
.path() << " is managed by user policy.";
434 *onc_source
= ::onc::ONC_SOURCE_USER_POLICY
;
435 return network_policy
;
437 network_policy
= GetPolicyForNetworkFromPref(
438 local_state_prefs
, prefs::kDeviceOpenNetworkConfiguration
, favorite
);
439 if (network_policy
) {
440 VLOG(1) << "Network " << favorite
.path() << " is managed by device policy.";
441 *onc_source
= ::onc::ONC_SOURCE_DEVICE_POLICY
;
442 return network_policy
;
444 VLOG(2) << "Network " << favorite
.path() << " is unmanaged.";
448 bool HasPolicyForFavoriteNetwork(const PrefService
* profile_prefs
,
449 const PrefService
* local_state_prefs
,
450 const FavoriteState
& network
) {
451 ::onc::ONCSource ignored_onc_source
;
452 const base::DictionaryValue
* policy
= onc::GetPolicyForFavoriteNetwork(
453 profile_prefs
, local_state_prefs
, network
, &ignored_onc_source
);
454 return policy
!= NULL
;
458 } // namespace chromeos