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