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/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 
     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 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 
    178 void 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 
    190 void 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 
    266 const 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 
    276 const 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 
    291 bool 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 
    304 namespace {
    305 
    306 const 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 
    324 const 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 
    351 const 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 
    381 const 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 
    423 const 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 
    449 bool 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