Chromium Code Reviews| Index: chromeos/network/onc/onc_utils.cc |
| diff --git a/chromeos/network/onc/onc_utils.cc b/chromeos/network/onc/onc_utils.cc |
| index 6abcd759799bd24e85838797ab1e251dd07aa81d..fc8f0db6b5622b60e613fb640ab7f1d7480ae27f 100644 |
| --- a/chromeos/network/onc/onc_utils.cc |
| +++ b/chromeos/network/onc/onc_utils.cc |
| @@ -9,6 +9,7 @@ |
| #include "base/base64.h" |
| #include "base/json/json_reader.h" |
| +#include "base/json/json_writer.h" |
| #include "base/logging.h" |
| #include "base/macros.h" |
| #include "base/memory/ptr_util.h" |
| @@ -16,13 +17,26 @@ |
| #include "base/strings/string_number_conversions.h" |
| #include "base/strings/string_util.h" |
| #include "base/values.h" |
| +#include "chromeos/network/managed_network_configuration_handler.h" |
| +#include "chromeos/network/network_configuration_handler.h" |
| #include "chromeos/network/network_event_log.h" |
| +#include "chromeos/network/network_profile.h" |
| +#include "chromeos/network/network_profile_handler.h" |
| +#include "chromeos/network/network_state.h" |
| +#include "chromeos/network/network_state_handler.h" |
| +#include "chromeos/network/network_ui_data.h" |
| #include "chromeos/network/onc/onc_mapper.h" |
| +#include "chromeos/network/onc/onc_normalizer.h" |
| #include "chromeos/network/onc/onc_signature.h" |
| +#include "chromeos/network/onc/onc_translator.h" |
| #include "chromeos/network/onc/onc_utils.h" |
| #include "chromeos/network/onc/onc_validator.h" |
| #include "components/device_event_log/device_event_log.h" |
| +#include "components/onc/onc_pref_names.h" |
| +#include "components/prefs/pref_service.h" |
| #include "components/proxy_config/proxy_config_dictionary.h" |
| +#include "components/user_manager/user.h" |
| +#include "components/user_manager/user_manager.h" |
| #include "crypto/encryptor.h" |
| #include "crypto/hmac.h" |
| #include "crypto/symmetric_key.h" |
| @@ -32,6 +46,8 @@ |
| #include "net/proxy/proxy_bypass_rules.h" |
| #include "net/proxy/proxy_config.h" |
| #include "net/proxy/proxy_server.h" |
| +#include "third_party/cros_system_api/dbus/service_constants.h" |
| +#include "url/gurl.h" |
| #include "url/url_constants.h" |
| using namespace ::onc; |
| @@ -44,6 +60,31 @@ namespace { |
| const char kUnableToDecrypt[] = "Unable to decrypt encrypted ONC"; |
| const char kUnableToDecode[] = "Unable to decode encrypted ONC"; |
| +// This class defines which string placeholders of ONC are replaced by which |
| +// user attribute. |
| +class UserStringSubstitution : public chromeos::onc::StringSubstitution { |
| + public: |
| + explicit UserStringSubstitution(const user_manager::User* user) |
|
Mattias Nissler (ping if slow)
2016/10/25 09:53:16
Not sure whether this use of User justifies pullin
stevenjb
2016/10/25 17:26:22
As already noted, the UserManager dependency alrea
|
| + : user_(user) {} |
| + ~UserStringSubstitution() override {} |
| + |
| + bool GetSubstitute(const std::string& placeholder, |
| + std::string* substitute) const override { |
| + if (placeholder == ::onc::substitutes::kLoginIDField) |
| + *substitute = user_->GetAccountName(false); |
| + else if (placeholder == ::onc::substitutes::kEmailField) |
| + *substitute = user_->email(); |
| + else |
| + return false; |
| + return true; |
| + } |
| + |
| + private: |
| + const user_manager::User* user_; |
| + |
| + DISALLOW_COPY_AND_ASSIGN(UserStringSubstitution); |
| +}; |
| + |
| } // namespace |
| const char kEmptyUnencryptedConfiguration[] = |
| @@ -992,5 +1033,294 @@ std::unique_ptr<base::DictionaryValue> ConvertProxyConfigToOncProxySettings( |
| return proxy_settings; |
| } |
| +namespace { |
| + |
| +const base::DictionaryValue* GetNetworkConfigByGUID( |
| + const base::ListValue& network_configs, |
| + const std::string& guid) { |
| + for (base::ListValue::const_iterator it = network_configs.begin(); |
| + it != network_configs.end(); ++it) { |
| + const base::DictionaryValue* network = NULL; |
| + (*it)->GetAsDictionary(&network); |
| + DCHECK(network); |
| + |
| + std::string current_guid; |
| + network->GetStringWithoutPathExpansion(::onc::network_config::kGUID, |
| + ¤t_guid); |
| + if (current_guid == guid) |
| + return network; |
| + } |
| + return NULL; |
| +} |
| + |
| +const base::DictionaryValue* GetNetworkConfigForEthernetWithoutEAP( |
| + const base::ListValue& network_configs) { |
| + VLOG(2) << "Search for ethernet policy without EAP."; |
| + for (base::ListValue::const_iterator it = network_configs.begin(); |
| + it != network_configs.end(); ++it) { |
| + const base::DictionaryValue* network = NULL; |
| + (*it)->GetAsDictionary(&network); |
| + DCHECK(network); |
| + |
| + std::string type; |
| + network->GetStringWithoutPathExpansion(::onc::network_config::kType, &type); |
| + if (type != ::onc::network_type::kEthernet) |
| + continue; |
| + |
| + const base::DictionaryValue* ethernet = NULL; |
| + network->GetDictionaryWithoutPathExpansion(::onc::network_config::kEthernet, |
| + ðernet); |
| + |
| + std::string auth; |
| + ethernet->GetStringWithoutPathExpansion(::onc::ethernet::kAuthentication, |
| + &auth); |
| + if (auth == ::onc::ethernet::kAuthenticationNone) |
| + return network; |
| + } |
| + return NULL; |
| +} |
| + |
| +const base::DictionaryValue* GetNetworkConfigForNetworkFromOnc( |
| + const base::ListValue& network_configs, |
| + const NetworkState& network) { |
| + // In all cases except Ethernet, we use the GUID of |network|. |
| + if (!network.Matches(NetworkTypePattern::Ethernet())) |
| + return GetNetworkConfigByGUID(network_configs, network.guid()); |
| + |
| + // Ethernet is always shared and thus cannot store a GUID per user. Thus we |
| + // search for any Ethernet policy intead of a matching GUID. |
| + // EthernetEAP service contains only the EAP parameters and stores the GUID of |
| + // the respective ONC policy. The EthernetEAP service itself is however never |
| + // in state "connected". An EthernetEAP policy must be applied, if an Ethernet |
| + // service is connected using the EAP parameters. |
| + const NetworkState* ethernet_eap = NULL; |
| + if (NetworkHandler::IsInitialized()) { |
| + ethernet_eap = |
| + NetworkHandler::Get()->network_state_handler()->GetEAPForEthernet( |
| + network.path()); |
| + } |
| + |
| + // The GUID associated with the EthernetEAP service refers to the ONC policy |
| + // with "Authentication: 8021X". |
| + if (ethernet_eap) |
| + return GetNetworkConfigByGUID(network_configs, ethernet_eap->guid()); |
| + |
| + // Otherwise, EAP is not used and instead the Ethernet policy with |
| + // "Authentication: None" applies. |
| + return GetNetworkConfigForEthernetWithoutEAP(network_configs); |
| +} |
| + |
| +const base::DictionaryValue* GetPolicyForNetworkFromPref( |
| + const PrefService* pref_service, |
| + const char* pref_name, |
| + const NetworkState& network) { |
| + if (!pref_service) { |
| + VLOG(2) << "No pref service"; |
| + return NULL; |
| + } |
| + |
| + const PrefService::Preference* preference = |
| + pref_service->FindPreference(pref_name); |
| + if (!preference) { |
| + VLOG(2) << "No preference " << pref_name; |
| + // The preference may not exist in tests. |
| + return NULL; |
| + } |
| + |
| + // User prefs are not stored in this Preference yet but only the policy. |
| + // |
| + // The policy server incorrectly configures the OpenNetworkConfiguration user |
| + // policy as Recommended. To work around that, we handle the Recommended and |
| + // the Mandatory value in the same way. |
| + // TODO(pneubeck): Remove this workaround, once the server is fixed. See |
| + // http://crbug.com/280553 . |
| + if (preference->IsDefaultValue()) { |
| + VLOG(2) << "Preference has no recommended or mandatory value."; |
| + // No policy set. |
| + return NULL; |
| + } |
| + VLOG(2) << "Preference with policy found."; |
| + const base::Value* onc_policy_value = preference->GetValue(); |
| + DCHECK(onc_policy_value); |
| + |
| + const base::ListValue* onc_policy = NULL; |
| + onc_policy_value->GetAsList(&onc_policy); |
| + DCHECK(onc_policy); |
| + |
| + return GetNetworkConfigForNetworkFromOnc(*onc_policy, network); |
| +} |
| + |
| +} // namespace |
| + |
| +void ExpandStringPlaceholdersInNetworksForUser( |
| + const user_manager::User* user, |
| + base::ListValue* network_configs) { |
| + if (!user) { |
| + // In tests no user may be logged in. It's not harmful if we just don't |
| + // expand the strings. |
| + return; |
| + } |
| + UserStringSubstitution substitution(user); |
| + chromeos::onc::ExpandStringsInNetworks(substitution, network_configs); |
| +} |
| + |
| +void ImportNetworksForUser(const user_manager::User* user, |
| + const base::ListValue& network_configs, |
| + std::string* error) { |
| + error->clear(); |
| + |
| + std::unique_ptr<base::ListValue> expanded_networks( |
| + network_configs.DeepCopy()); |
| + ExpandStringPlaceholdersInNetworksForUser(user, expanded_networks.get()); |
| + |
| + const NetworkProfile* profile = |
| + NetworkHandler::Get()->network_profile_handler()->GetProfileForUserhash( |
| + user->username_hash()); |
| + if (!profile) { |
| + *error = "User profile doesn't exist."; |
| + return; |
| + } |
| + |
| + bool ethernet_not_found = false; |
| + for (base::ListValue::const_iterator it = expanded_networks->begin(); |
| + it != expanded_networks->end(); ++it) { |
| + const base::DictionaryValue* network = NULL; |
| + (*it)->GetAsDictionary(&network); |
| + DCHECK(network); |
| + |
| + // Remove irrelevant fields. |
| + onc::Normalizer normalizer(true /* remove recommended fields */); |
| + std::unique_ptr<base::DictionaryValue> normalized_network = |
| + normalizer.NormalizeObject(&onc::kNetworkConfigurationSignature, |
| + *network); |
| + |
| + // TODO(pneubeck): Use ONC and ManagedNetworkConfigurationHandler instead. |
| + // crbug.com/457936 |
| + std::unique_ptr<base::DictionaryValue> shill_dict = |
| + onc::TranslateONCObjectToShill(&onc::kNetworkConfigurationSignature, |
| + *normalized_network); |
| + |
| + std::unique_ptr<NetworkUIData> ui_data( |
| + NetworkUIData::CreateFromONC(::onc::ONC_SOURCE_USER_IMPORT)); |
| + base::DictionaryValue ui_data_dict; |
| + ui_data->FillDictionary(&ui_data_dict); |
| + std::string ui_data_json; |
| + base::JSONWriter::Write(ui_data_dict, &ui_data_json); |
| + shill_dict->SetStringWithoutPathExpansion(shill::kUIDataProperty, |
| + ui_data_json); |
| + |
| + shill_dict->SetStringWithoutPathExpansion(shill::kProfileProperty, |
| + profile->path); |
| + |
| + std::string type; |
| + shill_dict->GetStringWithoutPathExpansion(shill::kTypeProperty, &type); |
| + NetworkConfigurationHandler* config_handler = |
| + NetworkHandler::Get()->network_configuration_handler(); |
| + if (NetworkTypePattern::Ethernet().MatchesType(type)) { |
| + // Ethernet has to be configured using an existing Ethernet service. |
| + const NetworkState* ethernet = |
| + NetworkHandler::Get()->network_state_handler()->FirstNetworkByType( |
| + NetworkTypePattern::Ethernet()); |
| + if (ethernet) { |
| + config_handler->SetShillProperties( |
| + ethernet->path(), *shill_dict, |
| + NetworkConfigurationObserver::SOURCE_USER_ACTION, base::Closure(), |
| + network_handler::ErrorCallback()); |
| + } else { |
| + ethernet_not_found = true; |
| + } |
| + |
| + } else { |
| + config_handler->CreateShillConfiguration( |
| + *shill_dict, NetworkConfigurationObserver::SOURCE_USER_ACTION, |
| + network_handler::ServiceResultCallback(), |
| + network_handler::ErrorCallback()); |
| + } |
| + } |
| + |
| + if (ethernet_not_found) |
| + *error = "No Ethernet available to configure."; |
| +} |
| + |
| +const base::DictionaryValue* FindPolicyForActiveUser( |
| + const std::string& guid, |
| + ::onc::ONCSource* onc_source) { |
| + const user_manager::User* user = |
| + user_manager::UserManager::Get()->GetActiveUser(); |
| + std::string username_hash = user ? user->username_hash() : std::string(); |
| + return NetworkHandler::Get() |
| + ->managed_network_configuration_handler() |
| + ->FindPolicyByGUID(username_hash, guid, onc_source); |
| +} |
| + |
| +namespace { |
| + |
| +const base::DictionaryValue* GetGlobalConfigFromPolicy(bool for_active_user) { |
| + std::string username_hash; |
| + if (for_active_user) { |
| + const user_manager::User* user = |
| + user_manager::UserManager::Get()->GetActiveUser(); |
|
Mattias Nissler (ping if slow)
2016/10/25 09:53:16
Ah, so here there are more references to UserManag
stevenjb
2016/10/25 17:26:22
It's possible that this could be re-factored to re
|
| + if (!user) { |
| + LOG(ERROR) << "No user logged in yet."; |
| + return NULL; |
| + } |
| + username_hash = user->username_hash(); |
| + } |
| + return NetworkHandler::Get() |
| + ->managed_network_configuration_handler() |
| + ->GetGlobalConfigFromPolicy(username_hash); |
| +} |
| + |
| +} // namespace |
| + |
| +bool PolicyAllowsOnlyPolicyNetworksToAutoconnect(bool for_active_user) { |
| + const base::DictionaryValue* global_config = |
| + GetGlobalConfigFromPolicy(for_active_user); |
| + if (!global_config) |
| + return false; // By default, all networks are allowed to autoconnect. |
| + |
| + bool only_policy_autoconnect = false; |
| + global_config->GetBooleanWithoutPathExpansion( |
| + ::onc::global_network_config::kAllowOnlyPolicyNetworksToAutoconnect, |
| + &only_policy_autoconnect); |
| + return only_policy_autoconnect; |
| +} |
| + |
| +const base::DictionaryValue* GetPolicyForNetwork( |
| + const PrefService* profile_prefs, |
| + const PrefService* local_state_prefs, |
| + const NetworkState& network, |
| + ::onc::ONCSource* onc_source) { |
| + VLOG(2) << "GetPolicyForNetwork: " << network.path(); |
| + *onc_source = ::onc::ONC_SOURCE_NONE; |
| + |
| + const base::DictionaryValue* network_policy = GetPolicyForNetworkFromPref( |
| + profile_prefs, ::onc::prefs::kOpenNetworkConfiguration, network); |
| + if (network_policy) { |
| + VLOG(1) << "Network " << network.path() << " is managed by user policy."; |
| + *onc_source = ::onc::ONC_SOURCE_USER_POLICY; |
| + return network_policy; |
| + } |
| + network_policy = GetPolicyForNetworkFromPref( |
| + local_state_prefs, ::onc::prefs::kDeviceOpenNetworkConfiguration, |
| + network); |
| + if (network_policy) { |
| + VLOG(1) << "Network " << network.path() << " is managed by device policy."; |
| + *onc_source = ::onc::ONC_SOURCE_DEVICE_POLICY; |
| + return network_policy; |
| + } |
| + VLOG(2) << "Network " << network.path() << " is unmanaged."; |
| + return NULL; |
| +} |
| + |
| +bool HasPolicyForNetwork(const PrefService* profile_prefs, |
| + const PrefService* local_state_prefs, |
| + const NetworkState& network) { |
| + ::onc::ONCSource ignored_onc_source; |
| + const base::DictionaryValue* policy = onc::GetPolicyForNetwork( |
| + profile_prefs, local_state_prefs, network, &ignored_onc_source); |
| + return policy != NULL; |
| +} |
| + |
| } // namespace onc |
| } // namespace chromeos |