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