onc_utils.cc revision 4e180b6a0b4720a9b8e9e959a882386f690f08ff
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_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 "chromeos/network/shill_property_util.h" 30#include "net/base/host_port_pair.h" 31#include "net/proxy/proxy_bypass_rules.h" 32#include "net/proxy/proxy_server.h" 33#include "third_party/cros_system_api/dbus/service_constants.h" 34#include "url/gurl.h" 35 36namespace chromeos { 37namespace onc { 38 39namespace { 40 41net::ProxyServer ConvertOncProxyLocationToHostPort( 42 net::ProxyServer::Scheme default_proxy_scheme, 43 const base::DictionaryValue& onc_proxy_location) { 44 std::string host; 45 onc_proxy_location.GetStringWithoutPathExpansion(::onc::proxy::kHost, &host); 46 // Parse |host| according to the format [<scheme>"://"]<server>[":"<port>]. 47 net::ProxyServer proxy_server = 48 net::ProxyServer::FromURI(host, default_proxy_scheme); 49 int port = 0; 50 onc_proxy_location.GetIntegerWithoutPathExpansion(::onc::proxy::kPort, &port); 51 52 // Replace the port parsed from |host| by the provided |port|. 53 return net::ProxyServer( 54 proxy_server.scheme(), 55 net::HostPortPair(proxy_server.host_port_pair().host(), 56 static_cast<uint16>(port))); 57} 58 59void AppendProxyServerForScheme( 60 const base::DictionaryValue& onc_manual, 61 const std::string& onc_scheme, 62 std::string* spec) { 63 const base::DictionaryValue* onc_proxy_location = NULL; 64 if (!onc_manual.GetDictionaryWithoutPathExpansion(onc_scheme, 65 &onc_proxy_location)) { 66 return; 67 } 68 69 net::ProxyServer::Scheme default_proxy_scheme = net::ProxyServer::SCHEME_HTTP; 70 std::string url_scheme; 71 if (onc_scheme == ::onc::proxy::kFtp) { 72 url_scheme = "ftp"; 73 } else if (onc_scheme == ::onc::proxy::kHttp) { 74 url_scheme = "http"; 75 } else if (onc_scheme == ::onc::proxy::kHttps) { 76 url_scheme = "https"; 77 } else if (onc_scheme == ::onc::proxy::kSocks) { 78 default_proxy_scheme = net::ProxyServer::SCHEME_SOCKS4; 79 url_scheme = "socks"; 80 } else { 81 NOTREACHED(); 82 } 83 84 net::ProxyServer proxy_server = ConvertOncProxyLocationToHostPort( 85 default_proxy_scheme, *onc_proxy_location); 86 87 UIProxyConfig::EncodeAndAppendProxyServer(url_scheme, proxy_server, spec); 88} 89 90net::ProxyBypassRules ConvertOncExcludeDomainsToBypassRules( 91 const base::ListValue& onc_exclude_domains) { 92 net::ProxyBypassRules rules; 93 for (base::ListValue::const_iterator it = onc_exclude_domains.begin(); 94 it != onc_exclude_domains.end(); ++it) { 95 std::string rule; 96 (*it)->GetAsString(&rule); 97 rules.AddRuleFromString(rule); 98 } 99 return rules; 100} 101 102} // namespace 103 104scoped_ptr<base::DictionaryValue> ConvertOncProxySettingsToProxyConfig( 105 const base::DictionaryValue& onc_proxy_settings) { 106 std::string type; 107 onc_proxy_settings.GetStringWithoutPathExpansion(::onc::proxy::kType, &type); 108 scoped_ptr<DictionaryValue> proxy_dict; 109 110 if (type == ::onc::proxy::kDirect) { 111 proxy_dict.reset(ProxyConfigDictionary::CreateDirect()); 112 } else if (type == ::onc::proxy::kWPAD) { 113 proxy_dict.reset(ProxyConfigDictionary::CreateAutoDetect()); 114 } else if (type == ::onc::proxy::kPAC) { 115 std::string pac_url; 116 onc_proxy_settings.GetStringWithoutPathExpansion(::onc::proxy::kPAC, 117 &pac_url); 118 GURL url(pac_url); 119 DCHECK(url.is_valid()) 120 << "PAC field is invalid for this ProxySettings.Type"; 121 proxy_dict.reset(ProxyConfigDictionary::CreatePacScript(url.spec(), 122 false)); 123 } else if (type == ::onc::proxy::kManual) { 124 const base::DictionaryValue* manual_dict = NULL; 125 onc_proxy_settings.GetDictionaryWithoutPathExpansion(::onc::proxy::kManual, 126 &manual_dict); 127 std::string manual_spec; 128 AppendProxyServerForScheme(*manual_dict, ::onc::proxy::kFtp, &manual_spec); 129 AppendProxyServerForScheme(*manual_dict, ::onc::proxy::kHttp, &manual_spec); 130 AppendProxyServerForScheme(*manual_dict, ::onc::proxy::kSocks, 131 &manual_spec); 132 AppendProxyServerForScheme(*manual_dict, ::onc::proxy::kHttps, 133 &manual_spec); 134 135 const base::ListValue* exclude_domains = NULL; 136 net::ProxyBypassRules bypass_rules; 137 if (onc_proxy_settings.GetListWithoutPathExpansion( 138 ::onc::proxy::kExcludeDomains, &exclude_domains)) { 139 bypass_rules.AssignFrom( 140 ConvertOncExcludeDomainsToBypassRules(*exclude_domains)); 141 } 142 proxy_dict.reset(ProxyConfigDictionary::CreateFixedServers( 143 manual_spec, bypass_rules.ToString())); 144 } else { 145 NOTREACHED(); 146 } 147 return proxy_dict.Pass(); 148} 149 150namespace { 151 152// This class defines which string placeholders of ONC are replaced by which 153// user attribute. 154class UserStringSubstitution : public chromeos::onc::StringSubstitution { 155 public: 156 explicit UserStringSubstitution(const chromeos::User* user) : 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 chromeos::User* user_; 172 173 DISALLOW_COPY_AND_ASSIGN(UserStringSubstitution); 174}; 175 176} // namespace 177 178void ExpandStringPlaceholdersInNetworksForUser( 179 const chromeos::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 chromeos::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 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 NetworkHandler::Get()->network_configuration_handler()->CreateConfiguration( 236 *shill_dict, 237 network_handler::StringResultCallback(), 238 network_handler::ErrorCallback()); 239 } 240} 241 242const base::DictionaryValue* FindPolicyForActiveUser( 243 const std::string& guid, 244 ::onc::ONCSource* onc_source) { 245 const User* user = UserManager::Get()->GetActiveUser(); 246 std::string username_hash = user ? user->username_hash() : std::string(); 247 return NetworkHandler::Get()->managed_network_configuration_handler()-> 248 FindPolicyByGUID(username_hash, guid, onc_source); 249} 250 251namespace { 252 253const base::DictionaryValue* GetNetworkConfigByGUID( 254 const base::ListValue& network_configs, 255 const std::string& guid) { 256 for (base::ListValue::const_iterator it = network_configs.begin(); 257 it != network_configs.end(); ++it) { 258 const base::DictionaryValue* network = NULL; 259 (*it)->GetAsDictionary(&network); 260 DCHECK(network); 261 262 std::string current_guid; 263 network->GetStringWithoutPathExpansion(::onc::network_config::kGUID, 264 ¤t_guid); 265 if (current_guid == guid) 266 return network; 267 } 268 return NULL; 269} 270 271const base::DictionaryValue* GetNetworkConfigForEthernetWithoutEAP( 272 const base::ListValue& network_configs) { 273 VLOG(2) << "Search for ethernet policy without EAP."; 274 for (base::ListValue::const_iterator it = network_configs.begin(); 275 it != network_configs.end(); ++it) { 276 const base::DictionaryValue* network = NULL; 277 (*it)->GetAsDictionary(&network); 278 DCHECK(network); 279 280 std::string type; 281 network->GetStringWithoutPathExpansion(::onc::network_config::kType, &type); 282 if (type != ::onc::network_type::kEthernet) 283 continue; 284 285 const base::DictionaryValue* ethernet = NULL; 286 network->GetDictionaryWithoutPathExpansion(::onc::network_config::kEthernet, 287 ðernet); 288 289 std::string auth; 290 ethernet->GetStringWithoutPathExpansion(::onc::ethernet::kAuthentication, 291 &auth); 292 if (auth == ::onc::ethernet::kNone) 293 return network; 294 } 295 return NULL; 296} 297 298const base::DictionaryValue* GetNetworkConfigForNetworkFromOnc( 299 const base::ListValue& network_configs, 300 const FavoriteState& favorite) { 301 // In all cases except Ethernet, we use the GUID of |network|. 302 if (!favorite.Matches(NetworkTypePattern::Ethernet())) 303 return GetNetworkConfigByGUID(network_configs, favorite.guid()); 304 305 // Ethernet is always shared and thus cannot store a GUID per user. Thus we 306 // search for any Ethernet policy intead of a matching GUID. 307 // EthernetEAP service contains only the EAP parameters and stores the GUID of 308 // the respective ONC policy. The EthernetEAP service itself is however never 309 // in state "connected". An EthernetEAP policy must be applied, if an Ethernet 310 // service is connected using the EAP parameters. 311 const FavoriteState* ethernet_eap = NULL; 312 if (NetworkHandler::IsInitialized()) { 313 ethernet_eap = 314 NetworkHandler::Get()->network_state_handler()->GetEAPForEthernet( 315 favorite.path()); 316 } 317 318 // The GUID associated with the EthernetEAP service refers to the ONC policy 319 // with "Authentication: 8021X". 320 if (ethernet_eap) 321 return GetNetworkConfigByGUID(network_configs, ethernet_eap->guid()); 322 323 // Otherwise, EAP is not used and instead the Ethernet policy with 324 // "Authentication: None" applies. 325 return GetNetworkConfigForEthernetWithoutEAP(network_configs); 326} 327 328const base::DictionaryValue* GetPolicyForNetworkFromPref( 329 const PrefService* pref_service, 330 const char* pref_name, 331 const FavoriteState& favorite) { 332 if (!pref_service) { 333 VLOG(2) << "No pref service"; 334 return NULL; 335 } 336 337 const PrefService::Preference* preference = 338 pref_service->FindPreference(pref_name); 339 if (!preference) { 340 VLOG(2) << "No preference " << pref_name; 341 // The preference may not exist in tests. 342 return NULL; 343 } 344 345 // User prefs are not stored in this Preference yet but only the policy. 346 // 347 // The policy server incorrectly configures the OpenNetworkConfiguration user 348 // policy as Recommended. To work around that, we handle the Recommended and 349 // the Mandatory value in the same way. 350 // TODO(pneubeck): Remove this workaround, once the server is fixed. See 351 // http://crbug.com/280553 . 352 if (preference->IsDefaultValue()) { 353 VLOG(2) << "Preference has no recommended or mandatory value."; 354 // No policy set. 355 return NULL; 356 } 357 VLOG(2) << "Preference with policy found."; 358 const base::Value* onc_policy_value = preference->GetValue(); 359 DCHECK(onc_policy_value); 360 361 const base::ListValue* onc_policy = NULL; 362 onc_policy_value->GetAsList(&onc_policy); 363 DCHECK(onc_policy); 364 365 return GetNetworkConfigForNetworkFromOnc(*onc_policy, favorite); 366} 367 368} // namespace 369 370const base::DictionaryValue* GetPolicyForFavoriteNetwork( 371 const PrefService* profile_prefs, 372 const PrefService* local_state_prefs, 373 const FavoriteState& favorite, 374 ::onc::ONCSource* onc_source) { 375 VLOG(2) << "GetPolicyForFavoriteNetwork: " << favorite.path(); 376 *onc_source = ::onc::ONC_SOURCE_NONE; 377 378 const base::DictionaryValue* network_policy = GetPolicyForNetworkFromPref( 379 profile_prefs, prefs::kOpenNetworkConfiguration, favorite); 380 if (network_policy) { 381 VLOG(1) << "Network " << favorite.path() << " is managed by user policy."; 382 *onc_source = ::onc::ONC_SOURCE_USER_POLICY; 383 return network_policy; 384 } 385 network_policy = GetPolicyForNetworkFromPref( 386 local_state_prefs, prefs::kDeviceOpenNetworkConfiguration, favorite); 387 if (network_policy) { 388 VLOG(1) << "Network " << favorite.path() << " is managed by device policy."; 389 *onc_source = ::onc::ONC_SOURCE_DEVICE_POLICY; 390 return network_policy; 391 } 392 VLOG(2) << "Network " << favorite.path() << " is unmanaged."; 393 return NULL; 394} 395 396bool HasPolicyForFavoriteNetwork(const PrefService* profile_prefs, 397 const PrefService* local_state_prefs, 398 const FavoriteState& network) { 399 ::onc::ONCSource ignored_onc_source; 400 const base::DictionaryValue* policy = onc::GetPolicyForFavoriteNetwork( 401 profile_prefs, local_state_prefs, network, &ignored_onc_source); 402 return policy != NULL; 403} 404 405} // namespace onc 406} // namespace chromeos 407