Home | History | Annotate | Download | only in net
      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 
     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 
     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 
     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 
    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:
    155   explicit UserStringSubstitution(const chromeos::User* user) : user_(user) {}
    156   virtual ~UserStringSubstitution() {}
    157 
    158   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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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