onc_utils.cc revision cedac228d2dd51db4b79ea1e72c7f249408ee061
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/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 "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<base::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 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 = NetworkUIData::CreateFromONC( 225 ::onc::ONC_SOURCE_USER_IMPORT, *normalized_network); 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* user = UserManager::Get()->GetActiveUser(); 270 std::string username_hash = user ? user->username_hash() : std::string(); 271 return NetworkHandler::Get()->managed_network_configuration_handler()-> 272 FindPolicyByGUID(username_hash, guid, onc_source); 273} 274 275const base::DictionaryValue* GetGlobalConfigFromPolicy(bool for_active_user) { 276 std::string username_hash; 277 if (for_active_user) { 278 const User* user = UserManager::Get()->GetActiveUser(); 279 if (!user) { 280 LOG(ERROR) << "No user logged in yet."; 281 return NULL; 282 } 283 username_hash = user->username_hash(); 284 } 285 return NetworkHandler::Get()->managed_network_configuration_handler()-> 286 GetGlobalConfigFromPolicy(username_hash); 287} 288 289bool PolicyAllowsOnlyPolicyNetworksToAutoconnect(bool for_active_user) { 290 const base::DictionaryValue* global_config = 291 GetGlobalConfigFromPolicy(for_active_user); 292 if (!global_config) 293 return false; // By default, all networks are allowed to autoconnect. 294 295 bool only_policy_autoconnect = false; 296 global_config->GetBooleanWithoutPathExpansion( 297 ::onc::global_network_config::kAllowOnlyPolicyNetworksToAutoconnect, 298 &only_policy_autoconnect); 299 return only_policy_autoconnect; 300} 301 302namespace { 303 304const base::DictionaryValue* GetNetworkConfigByGUID( 305 const base::ListValue& network_configs, 306 const std::string& guid) { 307 for (base::ListValue::const_iterator it = network_configs.begin(); 308 it != network_configs.end(); ++it) { 309 const base::DictionaryValue* network = NULL; 310 (*it)->GetAsDictionary(&network); 311 DCHECK(network); 312 313 std::string current_guid; 314 network->GetStringWithoutPathExpansion(::onc::network_config::kGUID, 315 ¤t_guid); 316 if (current_guid == guid) 317 return network; 318 } 319 return NULL; 320} 321 322const base::DictionaryValue* GetNetworkConfigForEthernetWithoutEAP( 323 const base::ListValue& network_configs) { 324 VLOG(2) << "Search for ethernet policy without EAP."; 325 for (base::ListValue::const_iterator it = network_configs.begin(); 326 it != network_configs.end(); ++it) { 327 const base::DictionaryValue* network = NULL; 328 (*it)->GetAsDictionary(&network); 329 DCHECK(network); 330 331 std::string type; 332 network->GetStringWithoutPathExpansion(::onc::network_config::kType, &type); 333 if (type != ::onc::network_type::kEthernet) 334 continue; 335 336 const base::DictionaryValue* ethernet = NULL; 337 network->GetDictionaryWithoutPathExpansion(::onc::network_config::kEthernet, 338 ðernet); 339 340 std::string auth; 341 ethernet->GetStringWithoutPathExpansion(::onc::ethernet::kAuthentication, 342 &auth); 343 if (auth == ::onc::ethernet::kNone) 344 return network; 345 } 346 return NULL; 347} 348 349const base::DictionaryValue* GetNetworkConfigForNetworkFromOnc( 350 const base::ListValue& network_configs, 351 const FavoriteState& favorite) { 352 // In all cases except Ethernet, we use the GUID of |network|. 353 if (!favorite.Matches(NetworkTypePattern::Ethernet())) 354 return GetNetworkConfigByGUID(network_configs, favorite.guid()); 355 356 // Ethernet is always shared and thus cannot store a GUID per user. Thus we 357 // search for any Ethernet policy intead of a matching GUID. 358 // EthernetEAP service contains only the EAP parameters and stores the GUID of 359 // the respective ONC policy. The EthernetEAP service itself is however never 360 // in state "connected". An EthernetEAP policy must be applied, if an Ethernet 361 // service is connected using the EAP parameters. 362 const FavoriteState* ethernet_eap = NULL; 363 if (NetworkHandler::IsInitialized()) { 364 ethernet_eap = 365 NetworkHandler::Get()->network_state_handler()->GetEAPForEthernet( 366 favorite.path()); 367 } 368 369 // The GUID associated with the EthernetEAP service refers to the ONC policy 370 // with "Authentication: 8021X". 371 if (ethernet_eap) 372 return GetNetworkConfigByGUID(network_configs, ethernet_eap->guid()); 373 374 // Otherwise, EAP is not used and instead the Ethernet policy with 375 // "Authentication: None" applies. 376 return GetNetworkConfigForEthernetWithoutEAP(network_configs); 377} 378 379const base::DictionaryValue* GetPolicyForNetworkFromPref( 380 const PrefService* pref_service, 381 const char* pref_name, 382 const FavoriteState& favorite) { 383 if (!pref_service) { 384 VLOG(2) << "No pref service"; 385 return NULL; 386 } 387 388 const PrefService::Preference* preference = 389 pref_service->FindPreference(pref_name); 390 if (!preference) { 391 VLOG(2) << "No preference " << pref_name; 392 // The preference may not exist in tests. 393 return NULL; 394 } 395 396 // User prefs are not stored in this Preference yet but only the policy. 397 // 398 // The policy server incorrectly configures the OpenNetworkConfiguration user 399 // policy as Recommended. To work around that, we handle the Recommended and 400 // the Mandatory value in the same way. 401 // TODO(pneubeck): Remove this workaround, once the server is fixed. See 402 // http://crbug.com/280553 . 403 if (preference->IsDefaultValue()) { 404 VLOG(2) << "Preference has no recommended or mandatory value."; 405 // No policy set. 406 return NULL; 407 } 408 VLOG(2) << "Preference with policy found."; 409 const base::Value* onc_policy_value = preference->GetValue(); 410 DCHECK(onc_policy_value); 411 412 const base::ListValue* onc_policy = NULL; 413 onc_policy_value->GetAsList(&onc_policy); 414 DCHECK(onc_policy); 415 416 return GetNetworkConfigForNetworkFromOnc(*onc_policy, favorite); 417} 418 419} // namespace 420 421const base::DictionaryValue* GetPolicyForFavoriteNetwork( 422 const PrefService* profile_prefs, 423 const PrefService* local_state_prefs, 424 const FavoriteState& favorite, 425 ::onc::ONCSource* onc_source) { 426 VLOG(2) << "GetPolicyForFavoriteNetwork: " << favorite.path(); 427 *onc_source = ::onc::ONC_SOURCE_NONE; 428 429 const base::DictionaryValue* network_policy = GetPolicyForNetworkFromPref( 430 profile_prefs, prefs::kOpenNetworkConfiguration, favorite); 431 if (network_policy) { 432 VLOG(1) << "Network " << favorite.path() << " is managed by user policy."; 433 *onc_source = ::onc::ONC_SOURCE_USER_POLICY; 434 return network_policy; 435 } 436 network_policy = GetPolicyForNetworkFromPref( 437 local_state_prefs, prefs::kDeviceOpenNetworkConfiguration, favorite); 438 if (network_policy) { 439 VLOG(1) << "Network " << favorite.path() << " is managed by device policy."; 440 *onc_source = ::onc::ONC_SOURCE_DEVICE_POLICY; 441 return network_policy; 442 } 443 VLOG(2) << "Network " << favorite.path() << " is unmanaged."; 444 return NULL; 445} 446 447bool HasPolicyForFavoriteNetwork(const PrefService* profile_prefs, 448 const PrefService* local_state_prefs, 449 const FavoriteState& network) { 450 ::onc::ONCSource ignored_onc_source; 451 const base::DictionaryValue* policy = onc::GetPolicyForFavoriteNetwork( 452 profile_prefs, local_state_prefs, network, &ignored_onc_source); 453 return policy != NULL; 454} 455 456} // namespace onc 457} // namespace chromeos 458