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/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"
     35 #include "url/gurl.h"
     36 
     37 namespace chromeos {
     38 namespace onc {
     39 
     40 namespace {
     41 
     42 net::ProxyServer ConvertOncProxyLocationToHostPort(
     43     net::ProxyServer::Scheme default_proxy_scheme,
     44     const base::DictionaryValue& onc_proxy_location) {
     45   std::string host;
     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);
     50   int port = 0;
     51   onc_proxy_location.GetIntegerWithoutPathExpansion(::onc::proxy::kPort, &port);
     52 
     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)));
     58 }
     59 
     60 void AppendProxyServerForScheme(
     61     const base::DictionaryValue& onc_manual,
     62     const std::string& onc_scheme,
     63     std::string* spec) {
     64   const base::DictionaryValue* onc_proxy_location = NULL;
     65   if (!onc_manual.GetDictionaryWithoutPathExpansion(onc_scheme,
     66                                                     &onc_proxy_location)) {
     67     return;
     68   }
     69 
     70   net::ProxyServer::Scheme default_proxy_scheme = net::ProxyServer::SCHEME_HTTP;
     71   std::string url_scheme;
     72   if (onc_scheme == ::onc::proxy::kFtp) {
     73     url_scheme = "ftp";
     74   } else if (onc_scheme == ::onc::proxy::kHttp) {
     75     url_scheme = "http";
     76   } else if (onc_scheme == ::onc::proxy::kHttps) {
     77     url_scheme = "https";
     78   } else if (onc_scheme == ::onc::proxy::kSocks) {
     79     default_proxy_scheme = net::ProxyServer::SCHEME_SOCKS4;
     80     url_scheme = "socks";
     81   } else {
     82     NOTREACHED();
     83   }
     84 
     85   net::ProxyServer proxy_server = ConvertOncProxyLocationToHostPort(
     86       default_proxy_scheme, *onc_proxy_location);
     87 
     88   UIProxyConfig::EncodeAndAppendProxyServer(url_scheme, proxy_server, spec);
     89 }
     90 
     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) {
     96     std::string rule;
     97     (*it)->GetAsString(&rule);
     98     rules.AddRuleFromString(rule);
     99   }
    100   return rules;
    101 }
    102 
    103 }  // namespace
    104 
    105 scoped_ptr<base::DictionaryValue> ConvertOncProxySettingsToProxyConfig(
    106     const base::DictionaryValue& onc_proxy_settings) {
    107   std::string type;
    108   onc_proxy_settings.GetStringWithoutPathExpansion(::onc::proxy::kType, &type);
    109   scoped_ptr<DictionaryValue> proxy_dict;
    110 
    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) {
    116     std::string pac_url;
    117     onc_proxy_settings.GetStringWithoutPathExpansion(::onc::proxy::kPAC,
    118                                                      &pac_url);
    119     GURL url(pac_url);
    120     DCHECK(url.is_valid())
    121         << "PAC field is invalid for this ProxySettings.Type";
    122     proxy_dict.reset(ProxyConfigDictionary::CreatePacScript(url.spec(),
    123                                                             false));
    124   } else if (type == ::onc::proxy::kManual) {
    125     const base::DictionaryValue* manual_dict = NULL;
    126     onc_proxy_settings.GetDictionaryWithoutPathExpansion(::onc::proxy::kManual,
    127                                                          &manual_dict);
    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,
    132                                &manual_spec);
    133     AppendProxyServerForScheme(*manual_dict, ::onc::proxy::kHttps,
    134                                &manual_spec);
    135 
    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));
    142     }
    143     proxy_dict.reset(ProxyConfigDictionary::CreateFixedServers(
    144         manual_spec, bypass_rules.ToString()));
    145   } else {
    146     NOTREACHED();
    147   }
    148   return proxy_dict.Pass();
    149 }
    150 
    151 namespace {
    152 
    153 // This class defines which string placeholders of ONC are replaced by which
    154 // user attribute.
    155 class UserStringSubstitution : public chromeos::onc::StringSubstitution {
    156  public:
    157   explicit UserStringSubstitution(const chromeos::User* user) : user_(user) {}
    158   virtual ~UserStringSubstitution() {}
    159 
    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();
    166     else
    167       return false;
    168     return true;
    169   }
    170 
    171  private:
    172   const chromeos::User* user_;
    173 
    174   DISALLOW_COPY_AND_ASSIGN(UserStringSubstitution);
    175 };
    176 
    177 }  // namespace
    178 
    179 void ExpandStringPlaceholdersInNetworksForUser(
    180     const chromeos::User* user,
    181     base::ListValue* network_configs) {
    182   if (!user) {
    183     // In tests no user may be logged in. It's not harmful if we just don't
    184     // expand the strings.
    185     return;
    186   }
    187   UserStringSubstitution substitution(user);
    188   chromeos::onc::ExpandStringsInNetworks(substitution, network_configs);
    189 }
    190 
    191 void ImportNetworksForUser(const chromeos::User* user,
    192                            const base::ListValue& network_configs,
    193                            std::string* error) {
    194   error->clear();
    195 
    196   scoped_ptr<base::ListValue> expanded_networks(network_configs.DeepCopy());
    197   ExpandStringPlaceholdersInNetworksForUser(user, expanded_networks.get());
    198 
    199   const NetworkProfile* profile =
    200       NetworkHandler::Get()->network_profile_handler()->GetProfileForUserhash(
    201           user->username_hash());
    202   if (!profile) {
    203     *error = "User profile doesn't exist.";
    204     return;
    205   }
    206 
    207   bool ethernet_not_found = false;
    208   for (base::ListValue::const_iterator it = expanded_networks->begin();
    209        it != expanded_networks->end();
    210        ++it) {
    211     const base::DictionaryValue* network = NULL;
    212     (*it)->GetAsDictionary(&network);
    213     DCHECK(network);
    214 
    215     // Remove irrelevant fields.
    216     onc::Normalizer normalizer(true /* remove recommended fields */);
    217     scoped_ptr<base::DictionaryValue> normalized_network =
    218         normalizer.NormalizeObject(&onc::kNetworkConfigurationSignature,
    219                                    *network);
    220 
    221     scoped_ptr<base::DictionaryValue> shill_dict =
    222         onc::TranslateONCObjectToShill(&onc::kNetworkConfigurationSignature,
    223                                        *normalized_network);
    224 
    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,
    232                                               ui_data_json);
    233 
    234     shill_dict->SetStringWithoutPathExpansion(shill::kProfileProperty,
    235                                               profile->path);
    236 
    237     std::string type;
    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());
    246       if (ethernet) {
    247         config_handler->SetProperties(ethernet->path(),
    248                                       *shill_dict,
    249                                       base::Closure(),
    250                                       network_handler::ErrorCallback());
    251       } else {
    252         ethernet_not_found = true;
    253       }
    254 
    255     } else {
    256       config_handler->CreateConfiguration(
    257           *shill_dict,
    258           network_handler::StringResultCallback(),
    259           network_handler::ErrorCallback());
    260     }
    261   }
    262 
    263   if (ethernet_not_found)
    264     *error = "No Ethernet available to configure.";
    265 }
    266 
    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);
    274 }
    275 
    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();
    280     if (!user) {
    281       LOG(ERROR) << "No user logged in yet.";
    282       return NULL;
    283     }
    284     username_hash = user->username_hash();
    285   }
    286   return NetworkHandler::Get()->managed_network_configuration_handler()->
    287       GetGlobalConfigFromPolicy(username_hash);
    288 }
    289 
    290 bool PolicyAllowsOnlyPolicyNetworksToAutoconnect(bool for_active_user) {
    291   const base::DictionaryValue* global_config =
    292       GetGlobalConfigFromPolicy(for_active_user);
    293   if (!global_config)
    294     return false;  // By default, all networks are allowed to autoconnect.
    295 
    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;
    301 }
    302 
    303 namespace {
    304 
    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);
    312     DCHECK(network);
    313 
    314     std::string current_guid;
    315     network->GetStringWithoutPathExpansion(::onc::network_config::kGUID,
    316                                            &current_guid);
    317     if (current_guid == guid)
    318       return network;
    319   }
    320   return NULL;
    321 }
    322 
    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);
    330     DCHECK(network);
    331 
    332     std::string type;
    333     network->GetStringWithoutPathExpansion(::onc::network_config::kType, &type);
    334     if (type != ::onc::network_type::kEthernet)
    335       continue;
    336 
    337     const base::DictionaryValue* ethernet = NULL;
    338     network->GetDictionaryWithoutPathExpansion(::onc::network_config::kEthernet,
    339                                                &ethernet);
    340 
    341     std::string auth;
    342     ethernet->GetStringWithoutPathExpansion(::onc::ethernet::kAuthentication,
    343                                             &auth);
    344     if (auth == ::onc::ethernet::kNone)
    345       return network;
    346   }
    347   return NULL;
    348 }
    349 
    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());
    356 
    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()) {
    365     ethernet_eap =
    366         NetworkHandler::Get()->network_state_handler()->GetEAPForEthernet(
    367             favorite.path());
    368   }
    369 
    370   // The GUID associated with the EthernetEAP service refers to the ONC policy
    371   // with "Authentication: 8021X".
    372   if (ethernet_eap)
    373     return GetNetworkConfigByGUID(network_configs, ethernet_eap->guid());
    374 
    375   // Otherwise, EAP is not used and instead the Ethernet policy with
    376   // "Authentication: None" applies.
    377   return GetNetworkConfigForEthernetWithoutEAP(network_configs);
    378 }
    379 
    380 const base::DictionaryValue* GetPolicyForNetworkFromPref(
    381     const PrefService* pref_service,
    382     const char* pref_name,
    383     const FavoriteState& favorite) {
    384   if (!pref_service) {
    385     VLOG(2) << "No pref service";
    386     return NULL;
    387   }
    388 
    389   const PrefService::Preference* preference =
    390       pref_service->FindPreference(pref_name);
    391   if (!preference) {
    392     VLOG(2) << "No preference " << pref_name;
    393     // The preference may not exist in tests.
    394     return NULL;
    395   }
    396 
    397   // User prefs are not stored in this Preference yet but only the policy.
    398   //
    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.";
    406     // No policy set.
    407     return NULL;
    408   }
    409   VLOG(2) << "Preference with policy found.";
    410   const base::Value* onc_policy_value = preference->GetValue();
    411   DCHECK(onc_policy_value);
    412 
    413   const base::ListValue* onc_policy = NULL;
    414   onc_policy_value->GetAsList(&onc_policy);
    415   DCHECK(onc_policy);
    416 
    417   return GetNetworkConfigForNetworkFromOnc(*onc_policy, favorite);
    418 }
    419 
    420 }  // namespace
    421 
    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;
    429 
    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;
    436   }
    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;
    443   }
    444   VLOG(2) << "Network " << favorite.path() << " is unmanaged.";
    445   return NULL;
    446 }
    447 
    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;
    455 }
    456 
    457 }  // namespace onc
    458 }  // namespace chromeos
    459