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 ¤t_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 ðernet); 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