• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.
4 
5 #include "chrome/browser/chromeos/net/onc_utils.h"
6 
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/users/user.h"
13 #include "chrome/browser/chromeos/login/users/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/managed_network_configuration_handler.h"
18 #include "chromeos/network/network_configuration_handler.h"
19 #include "chromeos/network/network_handler.h"
20 #include "chromeos/network/network_profile.h"
21 #include "chromeos/network/network_profile_handler.h"
22 #include "chromeos/network/network_state.h"
23 #include "chromeos/network/network_state_handler.h"
24 #include "chromeos/network/network_ui_data.h"
25 #include "chromeos/network/onc/onc_normalizer.h"
26 #include "chromeos/network/onc/onc_signature.h"
27 #include "chromeos/network/onc/onc_translator.h"
28 #include "chromeos/network/onc/onc_utils.h"
29 #include "net/base/host_port_pair.h"
30 #include "net/proxy/proxy_bypass_rules.h"
31 #include "net/proxy/proxy_server.h"
32 #include "third_party/cros_system_api/dbus/service_constants.h"
33 #include "url/gurl.h"
34 
35 namespace chromeos {
36 namespace onc {
37 
38 namespace {
39 
ConvertOncProxyLocationToHostPort(net::ProxyServer::Scheme default_proxy_scheme,const base::DictionaryValue & onc_proxy_location)40 net::ProxyServer ConvertOncProxyLocationToHostPort(
41     net::ProxyServer::Scheme default_proxy_scheme,
42     const base::DictionaryValue& onc_proxy_location) {
43   std::string host;
44   onc_proxy_location.GetStringWithoutPathExpansion(::onc::proxy::kHost, &host);
45   // Parse |host| according to the format [<scheme>"://"]<server>[":"<port>].
46   net::ProxyServer proxy_server =
47       net::ProxyServer::FromURI(host, default_proxy_scheme);
48   int port = 0;
49   onc_proxy_location.GetIntegerWithoutPathExpansion(::onc::proxy::kPort, &port);
50 
51   // Replace the port parsed from |host| by the provided |port|.
52   return net::ProxyServer(
53       proxy_server.scheme(),
54       net::HostPortPair(proxy_server.host_port_pair().host(),
55                         static_cast<uint16>(port)));
56 }
57 
AppendProxyServerForScheme(const base::DictionaryValue & onc_manual,const std::string & onc_scheme,std::string * spec)58 void AppendProxyServerForScheme(
59     const base::DictionaryValue& onc_manual,
60     const std::string& onc_scheme,
61     std::string* spec) {
62   const base::DictionaryValue* onc_proxy_location = NULL;
63   if (!onc_manual.GetDictionaryWithoutPathExpansion(onc_scheme,
64                                                     &onc_proxy_location)) {
65     return;
66   }
67 
68   net::ProxyServer::Scheme default_proxy_scheme = net::ProxyServer::SCHEME_HTTP;
69   std::string url_scheme;
70   if (onc_scheme == ::onc::proxy::kFtp) {
71     url_scheme = "ftp";
72   } else if (onc_scheme == ::onc::proxy::kHttp) {
73     url_scheme = "http";
74   } else if (onc_scheme == ::onc::proxy::kHttps) {
75     url_scheme = "https";
76   } else if (onc_scheme == ::onc::proxy::kSocks) {
77     default_proxy_scheme = net::ProxyServer::SCHEME_SOCKS4;
78     url_scheme = "socks";
79   } else {
80     NOTREACHED();
81   }
82 
83   net::ProxyServer proxy_server = ConvertOncProxyLocationToHostPort(
84       default_proxy_scheme, *onc_proxy_location);
85 
86   UIProxyConfig::EncodeAndAppendProxyServer(url_scheme, proxy_server, spec);
87 }
88 
ConvertOncExcludeDomainsToBypassRules(const base::ListValue & onc_exclude_domains)89 net::ProxyBypassRules ConvertOncExcludeDomainsToBypassRules(
90     const base::ListValue& onc_exclude_domains) {
91   net::ProxyBypassRules rules;
92   for (base::ListValue::const_iterator it = onc_exclude_domains.begin();
93        it != onc_exclude_domains.end(); ++it) {
94     std::string rule;
95     (*it)->GetAsString(&rule);
96     rules.AddRuleFromString(rule);
97   }
98   return rules;
99 }
100 
101 }  // namespace
102 
ConvertOncProxySettingsToProxyConfig(const base::DictionaryValue & onc_proxy_settings)103 scoped_ptr<base::DictionaryValue> ConvertOncProxySettingsToProxyConfig(
104     const base::DictionaryValue& onc_proxy_settings) {
105   std::string type;
106   onc_proxy_settings.GetStringWithoutPathExpansion(::onc::proxy::kType, &type);
107   scoped_ptr<base::DictionaryValue> proxy_dict;
108 
109   if (type == ::onc::proxy::kDirect) {
110     proxy_dict.reset(ProxyConfigDictionary::CreateDirect());
111   } else if (type == ::onc::proxy::kWPAD) {
112     proxy_dict.reset(ProxyConfigDictionary::CreateAutoDetect());
113   } else if (type == ::onc::proxy::kPAC) {
114     std::string pac_url;
115     onc_proxy_settings.GetStringWithoutPathExpansion(::onc::proxy::kPAC,
116                                                      &pac_url);
117     GURL url(pac_url);
118     DCHECK(url.is_valid())
119         << "PAC field is invalid for this ProxySettings.Type";
120     proxy_dict.reset(ProxyConfigDictionary::CreatePacScript(url.spec(),
121                                                             false));
122   } else if (type == ::onc::proxy::kManual) {
123     const base::DictionaryValue* manual_dict = NULL;
124     onc_proxy_settings.GetDictionaryWithoutPathExpansion(::onc::proxy::kManual,
125                                                          &manual_dict);
126     std::string manual_spec;
127     AppendProxyServerForScheme(*manual_dict, ::onc::proxy::kFtp, &manual_spec);
128     AppendProxyServerForScheme(*manual_dict, ::onc::proxy::kHttp, &manual_spec);
129     AppendProxyServerForScheme(*manual_dict, ::onc::proxy::kSocks,
130                                &manual_spec);
131     AppendProxyServerForScheme(*manual_dict, ::onc::proxy::kHttps,
132                                &manual_spec);
133 
134     const base::ListValue* exclude_domains = NULL;
135     net::ProxyBypassRules bypass_rules;
136     if (onc_proxy_settings.GetListWithoutPathExpansion(
137             ::onc::proxy::kExcludeDomains, &exclude_domains)) {
138       bypass_rules.AssignFrom(
139           ConvertOncExcludeDomainsToBypassRules(*exclude_domains));
140     }
141     proxy_dict.reset(ProxyConfigDictionary::CreateFixedServers(
142         manual_spec, bypass_rules.ToString()));
143   } else {
144     NOTREACHED();
145   }
146   return proxy_dict.Pass();
147 }
148 
149 namespace {
150 
151 // This class defines which string placeholders of ONC are replaced by which
152 // user attribute.
153 class UserStringSubstitution : public chromeos::onc::StringSubstitution {
154  public:
UserStringSubstitution(const chromeos::User * user)155   explicit UserStringSubstitution(const chromeos::User* user) : user_(user) {}
~UserStringSubstitution()156   virtual ~UserStringSubstitution() {}
157 
GetSubstitute(const std::string & placeholder,std::string * substitute) const158   virtual bool GetSubstitute(const std::string& placeholder,
159                              std::string* substitute) const OVERRIDE {
160     if (placeholder == ::onc::substitutes::kLoginIDField)
161       *substitute = user_->GetAccountName(false);
162     else if (placeholder == ::onc::substitutes::kEmailField)
163       *substitute = user_->email();
164     else
165       return false;
166     return true;
167   }
168 
169  private:
170   const chromeos::User* user_;
171 
172   DISALLOW_COPY_AND_ASSIGN(UserStringSubstitution);
173 };
174 
175 }  // namespace
176 
ExpandStringPlaceholdersInNetworksForUser(const chromeos::User * user,base::ListValue * network_configs)177 void ExpandStringPlaceholdersInNetworksForUser(
178     const chromeos::User* user,
179     base::ListValue* network_configs) {
180   if (!user) {
181     // In tests no user may be logged in. It's not harmful if we just don't
182     // expand the strings.
183     return;
184   }
185   UserStringSubstitution substitution(user);
186   chromeos::onc::ExpandStringsInNetworks(substitution, network_configs);
187 }
188 
ImportNetworksForUser(const chromeos::User * user,const base::ListValue & network_configs,std::string * error)189 void ImportNetworksForUser(const chromeos::User* user,
190                            const base::ListValue& network_configs,
191                            std::string* error) {
192   error->clear();
193 
194   scoped_ptr<base::ListValue> expanded_networks(network_configs.DeepCopy());
195   ExpandStringPlaceholdersInNetworksForUser(user, expanded_networks.get());
196 
197   const NetworkProfile* profile =
198       NetworkHandler::Get()->network_profile_handler()->GetProfileForUserhash(
199           user->username_hash());
200   if (!profile) {
201     *error = "User profile doesn't exist.";
202     return;
203   }
204 
205   bool ethernet_not_found = false;
206   for (base::ListValue::const_iterator it = expanded_networks->begin();
207        it != expanded_networks->end();
208        ++it) {
209     const base::DictionaryValue* network = NULL;
210     (*it)->GetAsDictionary(&network);
211     DCHECK(network);
212 
213     // Remove irrelevant fields.
214     onc::Normalizer normalizer(true /* remove recommended fields */);
215     scoped_ptr<base::DictionaryValue> normalized_network =
216         normalizer.NormalizeObject(&onc::kNetworkConfigurationSignature,
217                                    *network);
218 
219     scoped_ptr<base::DictionaryValue> shill_dict =
220         onc::TranslateONCObjectToShill(&onc::kNetworkConfigurationSignature,
221                                        *normalized_network);
222 
223     scoped_ptr<NetworkUIData> ui_data = NetworkUIData::CreateFromONC(
224         ::onc::ONC_SOURCE_USER_IMPORT, *normalized_network);
225     base::DictionaryValue ui_data_dict;
226     ui_data->FillDictionary(&ui_data_dict);
227     std::string ui_data_json;
228     base::JSONWriter::Write(&ui_data_dict, &ui_data_json);
229     shill_dict->SetStringWithoutPathExpansion(shill::kUIDataProperty,
230                                               ui_data_json);
231 
232     shill_dict->SetStringWithoutPathExpansion(shill::kProfileProperty,
233                                               profile->path);
234 
235     std::string type;
236     shill_dict->GetStringWithoutPathExpansion(shill::kTypeProperty, &type);
237     NetworkConfigurationHandler* config_handler =
238         NetworkHandler::Get()->network_configuration_handler();
239     if (NetworkTypePattern::Ethernet().MatchesType(type)) {
240       // Ethernet has to be configured using an existing Ethernet service.
241       const NetworkState* ethernet =
242           NetworkHandler::Get()->network_state_handler()->FirstNetworkByType(
243               NetworkTypePattern::Ethernet());
244       if (ethernet) {
245         config_handler->SetProperties(ethernet->path(),
246                                       *shill_dict,
247                                       base::Closure(),
248                                       network_handler::ErrorCallback());
249       } else {
250         ethernet_not_found = true;
251       }
252 
253     } else {
254       config_handler->CreateConfiguration(
255           *shill_dict,
256           network_handler::StringResultCallback(),
257           network_handler::ErrorCallback());
258     }
259   }
260 
261   if (ethernet_not_found)
262     *error = "No Ethernet available to configure.";
263 }
264 
FindPolicyForActiveUser(const std::string & guid,::onc::ONCSource * onc_source)265 const base::DictionaryValue* FindPolicyForActiveUser(
266     const std::string& guid,
267     ::onc::ONCSource* onc_source) {
268   const User* user = UserManager::Get()->GetActiveUser();
269   std::string username_hash = user ? user->username_hash() : std::string();
270   return NetworkHandler::Get()->managed_network_configuration_handler()->
271       FindPolicyByGUID(username_hash, guid, onc_source);
272 }
273 
GetGlobalConfigFromPolicy(bool for_active_user)274 const base::DictionaryValue* GetGlobalConfigFromPolicy(bool for_active_user) {
275   std::string username_hash;
276   if (for_active_user) {
277     const User* user = UserManager::Get()->GetActiveUser();
278     if (!user) {
279       LOG(ERROR) << "No user logged in yet.";
280       return NULL;
281     }
282     username_hash = user->username_hash();
283   }
284   return NetworkHandler::Get()->managed_network_configuration_handler()->
285       GetGlobalConfigFromPolicy(username_hash);
286 }
287 
PolicyAllowsOnlyPolicyNetworksToAutoconnect(bool for_active_user)288 bool PolicyAllowsOnlyPolicyNetworksToAutoconnect(bool for_active_user) {
289   const base::DictionaryValue* global_config =
290       GetGlobalConfigFromPolicy(for_active_user);
291   if (!global_config)
292     return false;  // By default, all networks are allowed to autoconnect.
293 
294   bool only_policy_autoconnect = false;
295   global_config->GetBooleanWithoutPathExpansion(
296       ::onc::global_network_config::kAllowOnlyPolicyNetworksToAutoconnect,
297       &only_policy_autoconnect);
298   return only_policy_autoconnect;
299 }
300 
301 namespace {
302 
GetNetworkConfigByGUID(const base::ListValue & network_configs,const std::string & guid)303 const base::DictionaryValue* GetNetworkConfigByGUID(
304     const base::ListValue& network_configs,
305     const std::string& guid) {
306   for (base::ListValue::const_iterator it = network_configs.begin();
307        it != network_configs.end(); ++it) {
308     const base::DictionaryValue* network = NULL;
309     (*it)->GetAsDictionary(&network);
310     DCHECK(network);
311 
312     std::string current_guid;
313     network->GetStringWithoutPathExpansion(::onc::network_config::kGUID,
314                                            &current_guid);
315     if (current_guid == guid)
316       return network;
317   }
318   return NULL;
319 }
320 
GetNetworkConfigForEthernetWithoutEAP(const base::ListValue & network_configs)321 const base::DictionaryValue* GetNetworkConfigForEthernetWithoutEAP(
322     const base::ListValue& network_configs) {
323   VLOG(2) << "Search for ethernet policy without EAP.";
324   for (base::ListValue::const_iterator it = network_configs.begin();
325        it != network_configs.end(); ++it) {
326     const base::DictionaryValue* network = NULL;
327     (*it)->GetAsDictionary(&network);
328     DCHECK(network);
329 
330     std::string type;
331     network->GetStringWithoutPathExpansion(::onc::network_config::kType, &type);
332     if (type != ::onc::network_type::kEthernet)
333       continue;
334 
335     const base::DictionaryValue* ethernet = NULL;
336     network->GetDictionaryWithoutPathExpansion(::onc::network_config::kEthernet,
337                                                &ethernet);
338 
339     std::string auth;
340     ethernet->GetStringWithoutPathExpansion(::onc::ethernet::kAuthentication,
341                                             &auth);
342     if (auth == ::onc::ethernet::kNone)
343       return network;
344   }
345   return NULL;
346 }
347 
GetNetworkConfigForNetworkFromOnc(const base::ListValue & network_configs,const NetworkState & network)348 const base::DictionaryValue* GetNetworkConfigForNetworkFromOnc(
349     const base::ListValue& network_configs,
350     const NetworkState& network) {
351   // In all cases except Ethernet, we use the GUID of |network|.
352   if (!network.Matches(NetworkTypePattern::Ethernet()))
353     return GetNetworkConfigByGUID(network_configs, network.guid());
354 
355   // Ethernet is always shared and thus cannot store a GUID per user. Thus we
356   // search for any Ethernet policy intead of a matching GUID.
357   // EthernetEAP service contains only the EAP parameters and stores the GUID of
358   // the respective ONC policy. The EthernetEAP service itself is however never
359   // in state "connected". An EthernetEAP policy must be applied, if an Ethernet
360   // service is connected using the EAP parameters.
361   const NetworkState* ethernet_eap = NULL;
362   if (NetworkHandler::IsInitialized()) {
363     ethernet_eap =
364         NetworkHandler::Get()->network_state_handler()->GetEAPForEthernet(
365             network.path());
366   }
367 
368   // The GUID associated with the EthernetEAP service refers to the ONC policy
369   // with "Authentication: 8021X".
370   if (ethernet_eap)
371     return GetNetworkConfigByGUID(network_configs, ethernet_eap->guid());
372 
373   // Otherwise, EAP is not used and instead the Ethernet policy with
374   // "Authentication: None" applies.
375   return GetNetworkConfigForEthernetWithoutEAP(network_configs);
376 }
377 
GetPolicyForNetworkFromPref(const PrefService * pref_service,const char * pref_name,const NetworkState & network)378 const base::DictionaryValue* GetPolicyForNetworkFromPref(
379     const PrefService* pref_service,
380     const char* pref_name,
381     const NetworkState& network) {
382   if (!pref_service) {
383     VLOG(2) << "No pref service";
384     return NULL;
385   }
386 
387   const PrefService::Preference* preference =
388       pref_service->FindPreference(pref_name);
389   if (!preference) {
390     VLOG(2) << "No preference " << pref_name;
391     // The preference may not exist in tests.
392     return NULL;
393   }
394 
395   // User prefs are not stored in this Preference yet but only the policy.
396   //
397   // The policy server incorrectly configures the OpenNetworkConfiguration user
398   // policy as Recommended. To work around that, we handle the Recommended and
399   // the Mandatory value in the same way.
400   // TODO(pneubeck): Remove this workaround, once the server is fixed. See
401   // http://crbug.com/280553 .
402   if (preference->IsDefaultValue()) {
403     VLOG(2) << "Preference has no recommended or mandatory value.";
404     // No policy set.
405     return NULL;
406   }
407   VLOG(2) << "Preference with policy found.";
408   const base::Value* onc_policy_value = preference->GetValue();
409   DCHECK(onc_policy_value);
410 
411   const base::ListValue* onc_policy = NULL;
412   onc_policy_value->GetAsList(&onc_policy);
413   DCHECK(onc_policy);
414 
415   return GetNetworkConfigForNetworkFromOnc(*onc_policy, network);
416 }
417 
418 }  // namespace
419 
GetPolicyForNetwork(const PrefService * profile_prefs,const PrefService * local_state_prefs,const NetworkState & network,::onc::ONCSource * onc_source)420 const base::DictionaryValue* GetPolicyForNetwork(
421     const PrefService* profile_prefs,
422     const PrefService* local_state_prefs,
423     const NetworkState& network,
424     ::onc::ONCSource* onc_source) {
425   VLOG(2) << "GetPolicyForNetwork: " << network.path();
426   *onc_source = ::onc::ONC_SOURCE_NONE;
427 
428   const base::DictionaryValue* network_policy = GetPolicyForNetworkFromPref(
429       profile_prefs, prefs::kOpenNetworkConfiguration, network);
430   if (network_policy) {
431     VLOG(1) << "Network " << network.path() << " is managed by user policy.";
432     *onc_source = ::onc::ONC_SOURCE_USER_POLICY;
433     return network_policy;
434   }
435   network_policy = GetPolicyForNetworkFromPref(
436       local_state_prefs, prefs::kDeviceOpenNetworkConfiguration, network);
437   if (network_policy) {
438     VLOG(1) << "Network " << network.path() << " is managed by device policy.";
439     *onc_source = ::onc::ONC_SOURCE_DEVICE_POLICY;
440     return network_policy;
441   }
442   VLOG(2) << "Network " << network.path() << " is unmanaged.";
443   return NULL;
444 }
445 
HasPolicyForNetwork(const PrefService * profile_prefs,const PrefService * local_state_prefs,const NetworkState & network)446 bool HasPolicyForNetwork(const PrefService* profile_prefs,
447                          const PrefService* local_state_prefs,
448                          const NetworkState& network) {
449   ::onc::ONCSource ignored_onc_source;
450   const base::DictionaryValue* policy = onc::GetPolicyForNetwork(
451       profile_prefs, local_state_prefs, network, &ignored_onc_source);
452   return policy != NULL;
453 }
454 
455 }  // namespace onc
456 }  // namespace chromeos
457