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/ui_proxy_config.h"
13#include "chrome/browser/prefs/proxy_config_dictionary.h"
14#include "chrome/common/pref_names.h"
15#include "chromeos/network/managed_network_configuration_handler.h"
16#include "chromeos/network/network_configuration_handler.h"
17#include "chromeos/network/network_handler.h"
18#include "chromeos/network/network_profile.h"
19#include "chromeos/network/network_profile_handler.h"
20#include "chromeos/network/network_state.h"
21#include "chromeos/network/network_state_handler.h"
22#include "chromeos/network/network_ui_data.h"
23#include "chromeos/network/onc/onc_normalizer.h"
24#include "chromeos/network/onc/onc_signature.h"
25#include "chromeos/network/onc/onc_translator.h"
26#include "chromeos/network/onc/onc_utils.h"
27#include "components/user_manager/user.h"
28#include "components/user_manager/user_manager.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
35namespace chromeos {
36namespace onc {
37
38namespace {
39
40net::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
58void 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
89net::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
103scoped_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
149namespace {
150
151// This class defines which string placeholders of ONC are replaced by which
152// user attribute.
153class UserStringSubstitution : public chromeos::onc::StringSubstitution {
154 public:
155  explicit UserStringSubstitution(const user_manager::User* user)
156      : user_(user) {}
157  virtual ~UserStringSubstitution() {}
158
159  virtual bool GetSubstitute(const std::string& placeholder,
160                             std::string* substitute) const OVERRIDE {
161    if (placeholder == ::onc::substitutes::kLoginIDField)
162      *substitute = user_->GetAccountName(false);
163    else if (placeholder == ::onc::substitutes::kEmailField)
164      *substitute = user_->email();
165    else
166      return false;
167    return true;
168  }
169
170 private:
171  const user_manager::User* user_;
172
173  DISALLOW_COPY_AND_ASSIGN(UserStringSubstitution);
174};
175
176}  // namespace
177
178void ExpandStringPlaceholdersInNetworksForUser(
179    const user_manager::User* user,
180    base::ListValue* network_configs) {
181  if (!user) {
182    // In tests no user may be logged in. It's not harmful if we just don't
183    // expand the strings.
184    return;
185  }
186  UserStringSubstitution substitution(user);
187  chromeos::onc::ExpandStringsInNetworks(substitution, network_configs);
188}
189
190void ImportNetworksForUser(const user_manager::User* user,
191                           const base::ListValue& network_configs,
192                           std::string* error) {
193  error->clear();
194
195  scoped_ptr<base::ListValue> expanded_networks(network_configs.DeepCopy());
196  ExpandStringPlaceholdersInNetworksForUser(user, expanded_networks.get());
197
198  const NetworkProfile* profile =
199      NetworkHandler::Get()->network_profile_handler()->GetProfileForUserhash(
200          user->username_hash());
201  if (!profile) {
202    *error = "User profile doesn't exist.";
203    return;
204  }
205
206  bool ethernet_not_found = false;
207  for (base::ListValue::const_iterator it = expanded_networks->begin();
208       it != expanded_networks->end();
209       ++it) {
210    const base::DictionaryValue* network = NULL;
211    (*it)->GetAsDictionary(&network);
212    DCHECK(network);
213
214    // Remove irrelevant fields.
215    onc::Normalizer normalizer(true /* remove recommended fields */);
216    scoped_ptr<base::DictionaryValue> normalized_network =
217        normalizer.NormalizeObject(&onc::kNetworkConfigurationSignature,
218                                   *network);
219
220    scoped_ptr<base::DictionaryValue> shill_dict =
221        onc::TranslateONCObjectToShill(&onc::kNetworkConfigurationSignature,
222                                       *normalized_network);
223
224    scoped_ptr<NetworkUIData> ui_data(
225        NetworkUIData::CreateFromONC(::onc::ONC_SOURCE_USER_IMPORT));
226    base::DictionaryValue ui_data_dict;
227    ui_data->FillDictionary(&ui_data_dict);
228    std::string ui_data_json;
229    base::JSONWriter::Write(&ui_data_dict, &ui_data_json);
230    shill_dict->SetStringWithoutPathExpansion(shill::kUIDataProperty,
231                                              ui_data_json);
232
233    shill_dict->SetStringWithoutPathExpansion(shill::kProfileProperty,
234                                              profile->path);
235
236    std::string type;
237    shill_dict->GetStringWithoutPathExpansion(shill::kTypeProperty, &type);
238    NetworkConfigurationHandler* config_handler =
239        NetworkHandler::Get()->network_configuration_handler();
240    if (NetworkTypePattern::Ethernet().MatchesType(type)) {
241      // Ethernet has to be configured using an existing Ethernet service.
242      const NetworkState* ethernet =
243          NetworkHandler::Get()->network_state_handler()->FirstNetworkByType(
244              NetworkTypePattern::Ethernet());
245      if (ethernet) {
246        config_handler->SetProperties(ethernet->path(),
247                                      *shill_dict,
248                                      base::Closure(),
249                                      network_handler::ErrorCallback());
250      } else {
251        ethernet_not_found = true;
252      }
253
254    } else {
255      config_handler->CreateConfiguration(
256          *shill_dict,
257          network_handler::StringResultCallback(),
258          network_handler::ErrorCallback());
259    }
260  }
261
262  if (ethernet_not_found)
263    *error = "No Ethernet available to configure.";
264}
265
266const base::DictionaryValue* FindPolicyForActiveUser(
267    const std::string& guid,
268    ::onc::ONCSource* onc_source) {
269  const user_manager::User* user =
270      user_manager::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);
274}
275
276const base::DictionaryValue* GetGlobalConfigFromPolicy(bool for_active_user) {
277  std::string username_hash;
278  if (for_active_user) {
279    const user_manager::User* user =
280        user_manager::UserManager::Get()->GetActiveUser();
281    if (!user) {
282      LOG(ERROR) << "No user logged in yet.";
283      return NULL;
284    }
285    username_hash = user->username_hash();
286  }
287  return NetworkHandler::Get()->managed_network_configuration_handler()->
288      GetGlobalConfigFromPolicy(username_hash);
289}
290
291bool PolicyAllowsOnlyPolicyNetworksToAutoconnect(bool for_active_user) {
292  const base::DictionaryValue* global_config =
293      GetGlobalConfigFromPolicy(for_active_user);
294  if (!global_config)
295    return false;  // By default, all networks are allowed to autoconnect.
296
297  bool only_policy_autoconnect = false;
298  global_config->GetBooleanWithoutPathExpansion(
299      ::onc::global_network_config::kAllowOnlyPolicyNetworksToAutoconnect,
300      &only_policy_autoconnect);
301  return only_policy_autoconnect;
302}
303
304namespace {
305
306const base::DictionaryValue* GetNetworkConfigByGUID(
307    const base::ListValue& network_configs,
308    const std::string& guid) {
309  for (base::ListValue::const_iterator it = network_configs.begin();
310       it != network_configs.end(); ++it) {
311    const base::DictionaryValue* network = NULL;
312    (*it)->GetAsDictionary(&network);
313    DCHECK(network);
314
315    std::string current_guid;
316    network->GetStringWithoutPathExpansion(::onc::network_config::kGUID,
317                                           &current_guid);
318    if (current_guid == guid)
319      return network;
320  }
321  return NULL;
322}
323
324const base::DictionaryValue* GetNetworkConfigForEthernetWithoutEAP(
325    const base::ListValue& network_configs) {
326  VLOG(2) << "Search for ethernet policy without EAP.";
327  for (base::ListValue::const_iterator it = network_configs.begin();
328       it != network_configs.end(); ++it) {
329    const base::DictionaryValue* network = NULL;
330    (*it)->GetAsDictionary(&network);
331    DCHECK(network);
332
333    std::string type;
334    network->GetStringWithoutPathExpansion(::onc::network_config::kType, &type);
335    if (type != ::onc::network_type::kEthernet)
336      continue;
337
338    const base::DictionaryValue* ethernet = NULL;
339    network->GetDictionaryWithoutPathExpansion(::onc::network_config::kEthernet,
340                                               &ethernet);
341
342    std::string auth;
343    ethernet->GetStringWithoutPathExpansion(::onc::ethernet::kAuthentication,
344                                            &auth);
345    if (auth == ::onc::ethernet::kAuthenticationNone)
346      return network;
347  }
348  return NULL;
349}
350
351const base::DictionaryValue* GetNetworkConfigForNetworkFromOnc(
352    const base::ListValue& network_configs,
353    const NetworkState& network) {
354  // In all cases except Ethernet, we use the GUID of |network|.
355  if (!network.Matches(NetworkTypePattern::Ethernet()))
356    return GetNetworkConfigByGUID(network_configs, network.guid());
357
358  // Ethernet is always shared and thus cannot store a GUID per user. Thus we
359  // search for any Ethernet policy intead of a matching GUID.
360  // EthernetEAP service contains only the EAP parameters and stores the GUID of
361  // the respective ONC policy. The EthernetEAP service itself is however never
362  // in state "connected". An EthernetEAP policy must be applied, if an Ethernet
363  // service is connected using the EAP parameters.
364  const NetworkState* ethernet_eap = NULL;
365  if (NetworkHandler::IsInitialized()) {
366    ethernet_eap =
367        NetworkHandler::Get()->network_state_handler()->GetEAPForEthernet(
368            network.path());
369  }
370
371  // The GUID associated with the EthernetEAP service refers to the ONC policy
372  // with "Authentication: 8021X".
373  if (ethernet_eap)
374    return GetNetworkConfigByGUID(network_configs, ethernet_eap->guid());
375
376  // Otherwise, EAP is not used and instead the Ethernet policy with
377  // "Authentication: None" applies.
378  return GetNetworkConfigForEthernetWithoutEAP(network_configs);
379}
380
381const base::DictionaryValue* GetPolicyForNetworkFromPref(
382    const PrefService* pref_service,
383    const char* pref_name,
384    const NetworkState& network) {
385  if (!pref_service) {
386    VLOG(2) << "No pref service";
387    return NULL;
388  }
389
390  const PrefService::Preference* preference =
391      pref_service->FindPreference(pref_name);
392  if (!preference) {
393    VLOG(2) << "No preference " << pref_name;
394    // The preference may not exist in tests.
395    return NULL;
396  }
397
398  // User prefs are not stored in this Preference yet but only the policy.
399  //
400  // The policy server incorrectly configures the OpenNetworkConfiguration user
401  // policy as Recommended. To work around that, we handle the Recommended and
402  // the Mandatory value in the same way.
403  // TODO(pneubeck): Remove this workaround, once the server is fixed. See
404  // http://crbug.com/280553 .
405  if (preference->IsDefaultValue()) {
406    VLOG(2) << "Preference has no recommended or mandatory value.";
407    // No policy set.
408    return NULL;
409  }
410  VLOG(2) << "Preference with policy found.";
411  const base::Value* onc_policy_value = preference->GetValue();
412  DCHECK(onc_policy_value);
413
414  const base::ListValue* onc_policy = NULL;
415  onc_policy_value->GetAsList(&onc_policy);
416  DCHECK(onc_policy);
417
418  return GetNetworkConfigForNetworkFromOnc(*onc_policy, network);
419}
420
421}  // namespace
422
423const base::DictionaryValue* GetPolicyForNetwork(
424    const PrefService* profile_prefs,
425    const PrefService* local_state_prefs,
426    const NetworkState& network,
427    ::onc::ONCSource* onc_source) {
428  VLOG(2) << "GetPolicyForNetwork: " << network.path();
429  *onc_source = ::onc::ONC_SOURCE_NONE;
430
431  const base::DictionaryValue* network_policy = GetPolicyForNetworkFromPref(
432      profile_prefs, prefs::kOpenNetworkConfiguration, network);
433  if (network_policy) {
434    VLOG(1) << "Network " << network.path() << " is managed by user policy.";
435    *onc_source = ::onc::ONC_SOURCE_USER_POLICY;
436    return network_policy;
437  }
438  network_policy = GetPolicyForNetworkFromPref(
439      local_state_prefs, prefs::kDeviceOpenNetworkConfiguration, network);
440  if (network_policy) {
441    VLOG(1) << "Network " << network.path() << " is managed by device policy.";
442    *onc_source = ::onc::ONC_SOURCE_DEVICE_POLICY;
443    return network_policy;
444  }
445  VLOG(2) << "Network " << network.path() << " is unmanaged.";
446  return NULL;
447}
448
449bool HasPolicyForNetwork(const PrefService* profile_prefs,
450                         const PrefService* local_state_prefs,
451                         const NetworkState& network) {
452  ::onc::ONCSource ignored_onc_source;
453  const base::DictionaryValue* policy = onc::GetPolicyForNetwork(
454      profile_prefs, local_state_prefs, network, &ignored_onc_source);
455  return policy != NULL;
456}
457
458}  // namespace onc
459}  // namespace chromeos
460