blob: 77bc28f0b9596996a2913ba737510f90b4439eed [file] [log] [blame]
// Copyright 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chromeos/network/managed_network_configuration_handler_impl.h"
#include <memory>
#include <utility>
#include <vector>
#include "base/bind.h"
#include "base/guid.h"
#include "base/location.h"
#include "base/logging.h"
#include "base/memory/ptr_util.h"
#include "base/single_thread_task_runner.h"
#include "base/threading/thread_task_runner_handle.h"
#include "base/values.h"
#include "chromeos/dbus/shill_manager_client.h"
#include "chromeos/dbus/shill_profile_client.h"
#include "chromeos/dbus/shill_service_client.h"
#include "chromeos/network/device_state.h"
#include "chromeos/network/network_configuration_handler.h"
#include "chromeos/network/network_device_handler.h"
#include "chromeos/network/network_event_log.h"
#include "chromeos/network/network_policy_observer.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/network_util.h"
#include "chromeos/network/onc/onc_merger.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 "chromeos/network/policy_util.h"
#include "chromeos/network/prohibited_technologies_handler.h"
#include "chromeos/network/shill_property_util.h"
#include "chromeos/network/tether_constants.h"
#include "components/onc/onc_constants.h"
#include "third_party/cros_system_api/dbus/service_constants.h"
namespace chromeos {
namespace {
using GuidToPolicyMap = ManagedNetworkConfigurationHandler::GuidToPolicyMap;
// These are error strings used for error callbacks. None of these error
// messages are user-facing: they should only appear in logs.
const char kInvalidUserSettings[] = "InvalidUserSettings";
const char kNetworkAlreadyConfigured[] = "NetworkAlreadyConfigured";
const char kPoliciesNotInitialized[] = "PoliciesNotInitialized";
const char kProfileNotInitialized[] = "ProfileNotInitialized";
const char kUnconfiguredNetwork[] = "UnconfiguredNetwork";
const char kUnknownNetwork[] = "UnknownNetwork";
std::string ToDebugString(::onc::ONCSource source,
const std::string& userhash) {
return source == ::onc::ONC_SOURCE_USER_POLICY ?
("user policy of " + userhash) : "device policy";
}
void InvokeErrorCallback(const std::string& service_path,
const network_handler::ErrorCallback& error_callback,
const std::string& error_name) {
std::string error_msg = "ManagedConfig Error: " + error_name;
NET_LOG_ERROR(error_msg, service_path);
network_handler::RunErrorCallback(
error_callback, service_path, error_name, error_msg);
}
void LogErrorWithDict(const base::Location& from_where,
const std::string& error_name,
std::unique_ptr<base::DictionaryValue> error_data) {
device_event_log::AddEntry(from_where.file_name(), from_where.line_number(),
device_event_log::LOG_TYPE_NETWORK,
device_event_log::LOG_LEVEL_ERROR, error_name);
}
const base::DictionaryValue* GetByGUID(const GuidToPolicyMap& policies,
const std::string& guid) {
auto it = policies.find(guid);
if (it == policies.end())
return NULL;
return it->second.get();
}
std::string GetStringFromDictionary(const base::Value& dict, const char* key) {
const base::Value* v = dict.FindKey(key);
return v ? v->GetString() : std::string();
}
bool MatchesExistingNetworkState(const base::DictionaryValue& properties,
const NetworkState* network_state) {
std::string type =
GetStringFromDictionary(properties, ::onc::network_config::kType);
if (network_util::TranslateONCTypeToShill(type) != network_state->type()) {
NET_LOG(ERROR) << "Network type mismatch for: " << network_state->guid()
<< " type: " << type
<< " does not match: " << network_state->type();
return false;
}
if (type != ::onc::network_type::kWiFi)
return true;
const base::Value* wifi = properties.FindKey(::onc::network_config::kWiFi);
if (!wifi) {
NET_LOG(ERROR) << "WiFi network configuration missing is WiFi properties: "
<< network_state->guid();
return false;
}
// For WiFi networks ensure that Security and SSID match.
std::string security = GetStringFromDictionary(*wifi, ::onc::wifi::kSecurity);
if (network_util::TranslateONCSecurityToShill(security) !=
network_state->security_class()) {
NET_LOG(ERROR) << "Network security mismatch for: " << network_state->guid()
<< " security: " << security
<< " does not match: " << network_state->security_class();
return false;
}
std::string hex_ssid = GetStringFromDictionary(*wifi, ::onc::wifi::kHexSSID);
if (hex_ssid != network_state->GetHexSsid()) {
NET_LOG(ERROR) << "Network HexSSID mismatch for: " << network_state->guid()
<< " hex_ssid: " << hex_ssid
<< " does not match: " << network_state->GetHexSsid();
return false;
}
return true;
}
} // namespace
struct ManagedNetworkConfigurationHandlerImpl::Policies {
~Policies();
GuidToPolicyMap per_network_config;
base::DictionaryValue global_network_config;
};
ManagedNetworkConfigurationHandlerImpl::Policies::~Policies() = default;
void ManagedNetworkConfigurationHandlerImpl::AddObserver(
NetworkPolicyObserver* observer) {
observers_.AddObserver(observer);
}
void ManagedNetworkConfigurationHandlerImpl::RemoveObserver(
NetworkPolicyObserver* observer) {
observers_.RemoveObserver(observer);
}
// GetManagedProperties
void ManagedNetworkConfigurationHandlerImpl::GetManagedProperties(
const std::string& userhash,
const std::string& service_path,
const network_handler::DictionaryResultCallback& callback,
const network_handler::ErrorCallback& error_callback) {
if (!GetPoliciesForUser(userhash) || !GetPoliciesForUser(std::string())) {
InvokeErrorCallback(service_path, error_callback, kPoliciesNotInitialized);
return;
}
NET_LOG_USER("GetManagedProperties", service_path);
network_configuration_handler_->GetShillProperties(
service_path,
base::Bind(
&ManagedNetworkConfigurationHandlerImpl::GetPropertiesCallback,
weak_ptr_factory_.GetWeakPtr(),
base::Bind(
&ManagedNetworkConfigurationHandlerImpl::SendManagedProperties,
weak_ptr_factory_.GetWeakPtr(), userhash, callback,
error_callback)),
error_callback);
}
void ManagedNetworkConfigurationHandlerImpl::SendManagedProperties(
const std::string& userhash,
const network_handler::DictionaryResultCallback& callback,
const network_handler::ErrorCallback& error_callback,
const std::string& service_path,
std::unique_ptr<base::DictionaryValue> shill_properties) {
std::string profile_path;
shill_properties->GetStringWithoutPathExpansion(shill::kProfileProperty,
&profile_path);
const NetworkState* network_state =
network_state_handler_->GetNetworkState(service_path);
const NetworkProfile* profile =
network_profile_handler_->GetProfileForPath(profile_path);
if (!profile && !(network_state && network_state->IsNonProfileType())) {
// Visible but unsaved (not known) networks will not have a profile.
NET_LOG_DEBUG("No profile for service: " + profile_path, service_path);
}
std::unique_ptr<NetworkUIData> ui_data =
shill_property_util::GetUIDataFromProperties(*shill_properties);
const base::DictionaryValue* user_settings = nullptr;
if (ui_data && profile) {
user_settings = ui_data->GetUserSettingsDictionary();
} else if (profile) {
NET_LOG_DEBUG("Service contains empty or invalid UIData", service_path);
// TODO(pneubeck): add a conversion of user configured entries of old
// ChromeOS versions. We will have to use a heuristic to determine which
// properties _might_ be user configured.
}
std::string guid;
shill_properties->GetStringWithoutPathExpansion(shill::kGuidProperty, &guid);
::onc::ONCSource onc_source;
FindPolicyByGUID(userhash, guid, &onc_source);
std::unique_ptr<base::DictionaryValue> active_settings(
onc::TranslateShillServiceToONCPart(*shill_properties, onc_source,
&onc::kNetworkWithStateSignature,
network_state));
const base::DictionaryValue* network_policy = nullptr;
const base::DictionaryValue* global_policy = nullptr;
if (profile) {
const Policies* policies = GetPoliciesForProfile(*profile);
if (!policies) {
InvokeErrorCallback(
service_path, error_callback, kPoliciesNotInitialized);
return;
}
if (!guid.empty())
network_policy = GetByGUID(policies->per_network_config, guid);
global_policy = &policies->global_network_config;
}
std::unique_ptr<base::DictionaryValue> augmented_properties(
policy_util::CreateManagedONC(global_policy, network_policy,
user_settings, active_settings.get(),
profile));
callback.Run(service_path, *augmented_properties);
}
// GetProperties
void ManagedNetworkConfigurationHandlerImpl::GetProperties(
const std::string& userhash,
const std::string& service_path,
const network_handler::DictionaryResultCallback& callback,
const network_handler::ErrorCallback& error_callback) {
NET_LOG_USER("GetProperties", service_path);
network_configuration_handler_->GetShillProperties(
service_path,
base::Bind(
&ManagedNetworkConfigurationHandlerImpl::GetPropertiesCallback,
weak_ptr_factory_.GetWeakPtr(),
base::Bind(&ManagedNetworkConfigurationHandlerImpl::SendProperties,
weak_ptr_factory_.GetWeakPtr(), userhash, callback,
error_callback)),
error_callback);
}
void ManagedNetworkConfigurationHandlerImpl::SendProperties(
const std::string& userhash,
const network_handler::DictionaryResultCallback& callback,
const network_handler::ErrorCallback& error_callback,
const std::string& service_path,
std::unique_ptr<base::DictionaryValue> shill_properties) {
const NetworkState* network_state =
network_state_handler_->GetNetworkState(service_path);
std::string guid;
shill_properties->GetStringWithoutPathExpansion(shill::kGuidProperty, &guid);
::onc::ONCSource onc_source;
FindPolicyByGUID(userhash, guid, &onc_source);
std::unique_ptr<base::DictionaryValue> onc_network(
onc::TranslateShillServiceToONCPart(*shill_properties, onc_source,
&onc::kNetworkWithStateSignature,
network_state));
callback.Run(service_path, *onc_network);
}
// SetProperties
void ManagedNetworkConfigurationHandlerImpl::SetProperties(
const std::string& service_path,
const base::DictionaryValue& user_settings,
const base::Closure& callback,
const network_handler::ErrorCallback& error_callback) {
const NetworkState* state =
network_state_handler_->GetNetworkStateFromServicePath(
service_path, true /* configured_only */);
if (!state) {
InvokeErrorCallback(service_path, error_callback, kUnknownNetwork);
return;
}
std::string guid = state->guid();
DCHECK(!guid.empty());
const std::string& profile_path = state->profile_path();
const NetworkProfile *profile =
network_profile_handler_->GetProfileForPath(profile_path);
if (!profile) {
// TODO(pneubeck): create an initial configuration in this case. As for
// CreateConfiguration, user settings from older ChromeOS versions have to
// be determined here.
InvokeErrorCallback(service_path, error_callback, kUnconfiguredNetwork);
return;
}
NET_LOG(DEBUG) << "Set Managed Properties for GUID: " << guid
<< ". Profile: " << profile->ToDebugString();
const Policies* policies = GetPoliciesForProfile(*profile);
if (!policies) {
InvokeErrorCallback(service_path, error_callback, kPoliciesNotInitialized);
return;
}
// We need to ensure that required configuration properties (e.g. Type) are
// included for ONC validation and translation to Shill properties.
std::unique_ptr<base::DictionaryValue> user_settings_copy(
user_settings.DeepCopy());
user_settings_copy->SetKey(
::onc::network_config::kType,
base::Value(network_util::TranslateShillTypeToONC(state->type())));
user_settings_copy->MergeDictionary(&user_settings);
// Validate the ONC dictionary. We are liberal and ignore unknown field
// names. User settings are only partial ONC, thus we ignore missing fields.
onc::Validator validator(false, // Ignore unknown fields.
false, // Ignore invalid recommended field names.
false, // Ignore missing fields.
false, // This ONC does not come from policy.
false); // Don't log warnings.
onc::Validator::Result validation_result;
std::unique_ptr<base::DictionaryValue> validated_user_settings =
validator.ValidateAndRepairObject(&onc::kNetworkConfigurationSignature,
*user_settings_copy,
&validation_result);
if (validation_result == onc::Validator::INVALID) {
InvokeErrorCallback(service_path, error_callback, kInvalidUserSettings);
return;
}
if (validation_result == onc::Validator::VALID_WITH_WARNINGS)
NET_LOG(ERROR) << "Validation of ONC user settings produced warnings.";
// Fill in HexSSID field from contents of SSID field if not set already.
onc::FillInHexSSIDFieldsInOncObject(onc::kNetworkConfigurationSignature,
validated_user_settings.get());
const base::DictionaryValue* network_policy =
GetByGUID(policies->per_network_config, guid);
if (network_policy)
NET_LOG(DEBUG) << "Configuration is managed. GUID: " << guid;
std::unique_ptr<base::DictionaryValue> shill_dictionary(
policy_util::CreateShillConfiguration(
*profile, guid, &policies->global_network_config, network_policy,
validated_user_settings.get()));
// 'Carrier' needs to be handled specially if set.
base::DictionaryValue* cellular = nullptr;
if (validated_user_settings->GetDictionaryWithoutPathExpansion(
::onc::network_config::kCellular, &cellular)) {
std::string carrier;
if (cellular->GetStringWithoutPathExpansion(::onc::cellular::kCarrier,
&carrier)) {
network_device_handler_->SetCarrier(
state->device_path(), carrier,
base::Bind(
&ManagedNetworkConfigurationHandlerImpl::SetShillProperties,
weak_ptr_factory_.GetWeakPtr(), service_path,
base::Passed(&shill_dictionary), callback, error_callback),
error_callback);
return;
}
}
SetShillProperties(service_path, std::move(shill_dictionary), callback,
error_callback);
}
void ManagedNetworkConfigurationHandlerImpl::SetManagerProperty(
const std::string& property_name,
const base::Value& value,
const base::Closure& callback,
const network_handler::ErrorCallback& error_callback) {
network_configuration_handler_->SetManagerProperty(property_name, value,
callback, error_callback);
}
void ManagedNetworkConfigurationHandlerImpl::SetShillProperties(
const std::string& service_path,
std::unique_ptr<base::DictionaryValue> shill_dictionary,
const base::Closure& callback,
const network_handler::ErrorCallback& error_callback) {
network_configuration_handler_->SetShillProperties(
service_path, *shill_dictionary, callback, error_callback);
}
void ManagedNetworkConfigurationHandlerImpl::CreateConfiguration(
const std::string& userhash,
const base::DictionaryValue& properties,
const network_handler::ServiceResultCallback& callback,
const network_handler::ErrorCallback& error_callback) const {
std::string guid =
GetStringFromDictionary(properties, ::onc::network_config::kGUID);
NET_LOG(USER) << "CreateConfiguration: " << guid;
// Validate the ONC dictionary. We are liberal and ignore unknown field
// names. User settings are only partial ONC, thus we ignore missing fields.
onc::Validator validator(false, // Ignore unknown fields.
false, // Ignore invalid recommended field names.
false, // Ignore missing fields.
false, // This ONC does not come from policy.
false); // Don't log warnings.
onc::Validator::Result validation_result;
std::unique_ptr<base::DictionaryValue> validated_properties =
validator.ValidateAndRepairObject(&onc::kNetworkConfigurationSignature,
properties, &validation_result);
if (validation_result == onc::Validator::INVALID) {
InvokeErrorCallback("", error_callback, kInvalidUserSettings);
return;
}
if (validation_result == onc::Validator::VALID_WITH_WARNINGS)
LOG(WARNING) << "Validation of ONC user settings produced warnings.";
// Fill in HexSSID field from contents of SSID field if not set already - this
// is required to properly match the configuration against existing policies.
onc::FillInHexSSIDFieldsInOncObject(onc::kNetworkConfigurationSignature,
validated_properties.get());
// Make sure the network is not configured through a user policy.
const Policies* policies = nullptr;
if (!userhash.empty()) {
policies = GetPoliciesForUser(userhash);
if (!policies) {
InvokeErrorCallback("", error_callback, kPoliciesNotInitialized);
return;
}
if (policy_util::FindMatchingPolicy(policies->per_network_config,
*validated_properties)) {
InvokeErrorCallback("", error_callback, kNetworkAlreadyConfigured);
return;
}
}
// Make user the network is not configured through a device policy.
policies = GetPoliciesForUser("");
if (!policies) {
InvokeErrorCallback("", error_callback, kPoliciesNotInitialized);
return;
}
if (policy_util::FindMatchingPolicy(policies->per_network_config,
*validated_properties)) {
InvokeErrorCallback("", error_callback, kNetworkAlreadyConfigured);
return;
}
const NetworkProfile* profile =
network_profile_handler_->GetProfileForUserhash(userhash);
if (!profile) {
InvokeErrorCallback("", error_callback, kProfileNotInitialized);
return;
}
// If a GUID was provided, verify that the new configuraiton matches an
// existing NetworkState for an unconfigured (i.e. visible) network.
// Requires HexSSID to be set first for comparing SSIDs.
if (!guid.empty()) {
const NetworkState* network_state =
network_state_handler_->GetNetworkStateFromGuid(guid);
// |network_state| can by null if a network went out of range or was
// forgotten while the UI is open. Configuration should succeed and the GUID
// can be reused.
if (network_state) {
if (!MatchesExistingNetworkState(*validated_properties, network_state)) {
InvokeErrorCallback(network_state->path(), error_callback,
kNetworkAlreadyConfigured);
return;
} else if (!network_state->profile_path().empty()) {
// Can occur after an invalid password or with multiple config UIs open.
// Configuration should succeed, so just log an event.
NET_LOG(EVENT) << "Reconfiguring network: " << guid
<< " Profile: " << network_state->profile_path();
}
}
} else {
guid = base::GenerateGUID();
}
std::unique_ptr<base::DictionaryValue> shill_dictionary(
policy_util::CreateShillConfiguration(*profile, guid,
nullptr, // no global policy
nullptr, // no network policy
validated_properties.get()));
network_configuration_handler_->CreateShillConfiguration(
*shill_dictionary, callback, error_callback);
}
void ManagedNetworkConfigurationHandlerImpl::RemoveConfiguration(
const std::string& service_path,
const base::Closure& callback,
const network_handler::ErrorCallback& error_callback) const {
network_configuration_handler_->RemoveConfiguration(service_path, callback,
error_callback);
}
void ManagedNetworkConfigurationHandlerImpl::
RemoveConfigurationFromCurrentProfile(
const std::string& service_path,
const base::Closure& callback,
const network_handler::ErrorCallback& error_callback) const {
network_configuration_handler_->RemoveConfigurationFromCurrentProfile(
service_path, callback, error_callback);
}
void ManagedNetworkConfigurationHandlerImpl::SetPolicy(
::onc::ONCSource onc_source,
const std::string& userhash,
const base::ListValue& network_configs_onc,
const base::DictionaryValue& global_network_config) {
VLOG(1) << "Setting policies from " << ToDebugString(onc_source, userhash)
<< ".";
// |userhash| must be empty for device policies.
DCHECK(onc_source != ::onc::ONC_SOURCE_DEVICE_POLICY ||
userhash.empty());
Policies* policies = NULL;
if (base::ContainsKey(policies_by_user_, userhash)) {
policies = policies_by_user_[userhash].get();
} else {
policies = new Policies;
policies_by_user_[userhash] = base::WrapUnique(policies);
}
policies->global_network_config.MergeDictionary(&global_network_config);
// Update prohibited technologies.
const base::ListValue* prohibited_list = nullptr;
if (policies->global_network_config.GetListWithoutPathExpansion(
::onc::global_network_config::kDisableNetworkTypes,
&prohibited_list) &&
prohibited_technologies_handler_) {
// Prohibited technologies are only allowed in user policy.
DCHECK_EQ(::onc::ONC_SOURCE_DEVICE_POLICY, onc_source);
prohibited_technologies_handler_->SetProhibitedTechnologies(
prohibited_list);
}
GuidToPolicyMap old_per_network_config;
policies->per_network_config.swap(old_per_network_config);
// This stores all GUIDs of policies that have changed or are new.
std::set<std::string> modified_policies;
for (base::ListValue::const_iterator it = network_configs_onc.begin();
it != network_configs_onc.end(); ++it) {
const base::DictionaryValue* network = NULL;
it->GetAsDictionary(&network);
DCHECK(network);
std::string guid;
network->GetStringWithoutPathExpansion(::onc::network_config::kGUID, &guid);
DCHECK(!guid.empty());
if (policies->per_network_config.count(guid) > 0) {
NET_LOG_ERROR("ONC from " + ToDebugString(onc_source, userhash) +
" contains several entries for the same GUID ", guid);
}
base::DictionaryValue* new_entry = network->DeepCopy();
policies->per_network_config[guid] = base::WrapUnique(new_entry);
base::DictionaryValue* old_entry = old_per_network_config[guid].get();
if (!old_entry || !old_entry->Equals(new_entry))
modified_policies.insert(guid);
}
old_per_network_config.clear();
ApplyOrQueuePolicies(userhash, &modified_policies);
for (auto& observer : observers_)
observer.PoliciesChanged(userhash);
}
bool ManagedNetworkConfigurationHandlerImpl::IsAnyPolicyApplicationRunning()
const {
return !policy_applicators_.empty() || !queued_modified_policies_.empty();
}
bool ManagedNetworkConfigurationHandlerImpl::ApplyOrQueuePolicies(
const std::string& userhash,
std::set<std::string>* modified_policies) {
DCHECK(modified_policies);
const NetworkProfile* profile =
network_profile_handler_->GetProfileForUserhash(userhash);
if (!profile) {
VLOG(1) << "The relevant Shill profile isn't initialized yet, postponing "
<< "policy application.";
// OnProfileAdded will apply all policies for this userhash.
return false;
}
if (base::ContainsKey(policy_applicators_, userhash)) {
// A previous policy application is still running. Queue the modified
// policies.
// Note, even if |modified_policies| is empty, this means that a policy
// application will be queued.
queued_modified_policies_[userhash].insert(modified_policies->begin(),
modified_policies->end());
VLOG(1) << "Previous PolicyApplicator still running. Postponing policy "
"application.";
return false;
}
const Policies* policies = policies_by_user_[userhash].get();
DCHECK(policies);
PolicyApplicator* applicator =
new PolicyApplicator(*profile,
policies->per_network_config,
policies->global_network_config,
this,
modified_policies);
policy_applicators_[userhash] = base::WrapUnique(applicator);
applicator->Run();
return true;
}
void ManagedNetworkConfigurationHandlerImpl::OnProfileAdded(
const NetworkProfile& profile) {
VLOG(1) << "Adding profile " << profile.ToDebugString() << "'.";
const Policies* policies = GetPoliciesForProfile(profile);
if (!policies) {
VLOG(1) << "The relevant policy is not initialized, "
<< "postponing policy application.";
// See SetPolicy.
return;
}
std::set<std::string> policy_guids;
for (auto it = policies->per_network_config.begin();
it != policies->per_network_config.end(); ++it) {
policy_guids.insert(it->first);
}
const bool started_policy_application =
ApplyOrQueuePolicies(profile.userhash, &policy_guids);
DCHECK(started_policy_application);
}
void ManagedNetworkConfigurationHandlerImpl::OnProfileRemoved(
const NetworkProfile& profile) {
// Nothing to do in this case.
}
void ManagedNetworkConfigurationHandlerImpl::CreateConfigurationFromPolicy(
const base::DictionaryValue& shill_properties) {
network_configuration_handler_->CreateShillConfiguration(
shill_properties,
base::Bind(
&ManagedNetworkConfigurationHandlerImpl::OnPolicyAppliedToNetwork,
weak_ptr_factory_.GetWeakPtr()),
base::Bind(&LogErrorWithDict, FROM_HERE));
}
void ManagedNetworkConfigurationHandlerImpl::
UpdateExistingConfigurationWithPropertiesFromPolicy(
const base::DictionaryValue& existing_properties,
const base::DictionaryValue& new_properties) {
base::DictionaryValue shill_properties;
std::string profile;
existing_properties.GetStringWithoutPathExpansion(shill::kProfileProperty,
&profile);
if (profile.empty()) {
NET_LOG_ERROR("Missing profile property",
shill_property_util::GetNetworkIdFromProperties(
existing_properties));
return;
}
shill_properties.SetKey(shill::kProfileProperty, base::Value(profile));
if (!shill_property_util::CopyIdentifyingProperties(
existing_properties,
true /* properties were read from Shill */,
&shill_properties)) {
NET_LOG_ERROR("Missing identifying properties",
shill_property_util::GetNetworkIdFromProperties(
existing_properties));
}
shill_properties.MergeDictionary(&new_properties);
network_configuration_handler_->CreateShillConfiguration(
shill_properties,
base::Bind(
&ManagedNetworkConfigurationHandlerImpl::OnPolicyAppliedToNetwork,
weak_ptr_factory_.GetWeakPtr()),
base::Bind(&LogErrorWithDict, FROM_HERE));
}
void ManagedNetworkConfigurationHandlerImpl::OnPoliciesApplied(
const NetworkProfile& profile) {
const std::string& userhash = profile.userhash;
VLOG(1) << "Policy application for user '" << userhash << "' finished.";
base::ThreadTaskRunnerHandle::Get()->DeleteSoon(
FROM_HERE, policy_applicators_[userhash].release());
policy_applicators_.erase(userhash);
if (base::ContainsKey(queued_modified_policies_, userhash)) {
std::set<std::string> modified_policies;
queued_modified_policies_[userhash].swap(modified_policies);
// Remove |userhash| from the queue.
queued_modified_policies_.erase(userhash);
ApplyOrQueuePolicies(userhash, &modified_policies);
} else {
if (userhash.empty())
device_policy_applied_ = true;
else
user_policy_applied_ = true;
if (device_policy_applied_ && user_policy_applied_) {
network_state_handler_->UpdateBlockedWifiNetworks(
AllowOnlyPolicyNetworksToConnect(),
AllowOnlyPolicyNetworksToConnectIfAvailable(),
GetBlacklistedHexSSIDs());
}
for (auto& observer : observers_)
observer.PoliciesApplied(userhash);
}
}
const base::DictionaryValue*
ManagedNetworkConfigurationHandlerImpl::FindPolicyByGUID(
const std::string userhash,
const std::string& guid,
::onc::ONCSource* onc_source) const {
*onc_source = ::onc::ONC_SOURCE_NONE;
if (!userhash.empty()) {
const Policies* user_policies = GetPoliciesForUser(userhash);
if (user_policies) {
const base::DictionaryValue* policy =
GetByGUID(user_policies->per_network_config, guid);
if (policy) {
*onc_source = ::onc::ONC_SOURCE_USER_POLICY;
return policy;
}
}
}
const Policies* device_policies = GetPoliciesForUser(std::string());
if (device_policies) {
const base::DictionaryValue* policy =
GetByGUID(device_policies->per_network_config, guid);
if (policy) {
*onc_source = ::onc::ONC_SOURCE_DEVICE_POLICY;
return policy;
}
}
return NULL;
}
const GuidToPolicyMap*
ManagedNetworkConfigurationHandlerImpl::GetNetworkConfigsFromPolicy(
const std::string& userhash) const {
const Policies* policies = GetPoliciesForUser(userhash);
if (!policies)
return NULL;
return &policies->per_network_config;
}
const base::DictionaryValue*
ManagedNetworkConfigurationHandlerImpl::GetGlobalConfigFromPolicy(
const std::string& userhash) const {
const Policies* policies = GetPoliciesForUser(userhash);
if (!policies)
return NULL;
return &policies->global_network_config;
}
const base::DictionaryValue*
ManagedNetworkConfigurationHandlerImpl::FindPolicyByGuidAndProfile(
const std::string& guid,
const std::string& profile_path,
::onc::ONCSource* onc_source) const {
if (profile_path.empty())
return nullptr;
const NetworkProfile* profile =
network_profile_handler_->GetProfileForPath(profile_path);
if (!profile) {
NET_LOG_ERROR("Profile path unknown:" + profile_path, guid);
return nullptr;
}
const Policies* policies = GetPoliciesForProfile(*profile);
if (!policies)
return nullptr;
const base::DictionaryValue* policy =
GetByGUID(policies->per_network_config, guid);
if (policy && onc_source) {
*onc_source = (profile->userhash.empty() ? ::onc::ONC_SOURCE_DEVICE_POLICY
: ::onc::ONC_SOURCE_USER_POLICY);
}
return policy;
}
bool ManagedNetworkConfigurationHandlerImpl::AllowOnlyPolicyNetworksToConnect()
const {
const base::DictionaryValue* global_network_config =
GetGlobalConfigFromPolicy(
std::string() /* no username hash, device policy */);
if (!global_network_config)
return false;
const base::Value* managed_only_value = global_network_config->FindKeyOfType(
::onc::global_network_config::kAllowOnlyPolicyNetworksToConnect,
base::Value::Type::BOOLEAN);
return managed_only_value && managed_only_value->GetBool();
}
bool ManagedNetworkConfigurationHandlerImpl::
AllowOnlyPolicyNetworksToConnectIfAvailable() const {
const base::DictionaryValue* global_network_config =
GetGlobalConfigFromPolicy(
std::string() /* no username hash, device policy */);
if (!global_network_config)
return false;
// Check if policy is enabled.
const base::Value* available_only_value =
global_network_config->FindKeyOfType(
::onc::global_network_config::
kAllowOnlyPolicyNetworksToConnectIfAvailable,
base::Value::Type::BOOLEAN);
return available_only_value && available_only_value->GetBool();
}
bool ManagedNetworkConfigurationHandlerImpl::
AllowOnlyPolicyNetworksToAutoconnect() const {
const base::DictionaryValue* global_network_config =
GetGlobalConfigFromPolicy(
std::string() /* no username hash, device policy */);
if (!global_network_config)
return false;
const base::Value* autoconnect_value = global_network_config->FindKeyOfType(
::onc::global_network_config::kAllowOnlyPolicyNetworksToAutoconnect,
base::Value::Type::BOOLEAN);
return autoconnect_value && autoconnect_value->GetBool();
}
std::vector<std::string>
ManagedNetworkConfigurationHandlerImpl::GetBlacklistedHexSSIDs() const {
const base::DictionaryValue* global_network_config =
GetGlobalConfigFromPolicy(
std::string() /* no username hash, device policy */);
if (!global_network_config)
return std::vector<std::string>();
const base::Value* blacklist_value = global_network_config->FindKeyOfType(
::onc::global_network_config::kBlacklistedHexSSIDs,
base::Value::Type::LIST);
if (!blacklist_value)
return std::vector<std::string>();
std::vector<std::string> blacklisted_hex_ssids;
for (const base::Value& entry : blacklist_value->GetList())
blacklisted_hex_ssids.push_back(entry.GetString());
return blacklisted_hex_ssids;
}
const ManagedNetworkConfigurationHandlerImpl::Policies*
ManagedNetworkConfigurationHandlerImpl::GetPoliciesForUser(
const std::string& userhash) const {
UserToPoliciesMap::const_iterator it = policies_by_user_.find(userhash);
if (it == policies_by_user_.end())
return NULL;
return it->second.get();
}
const ManagedNetworkConfigurationHandlerImpl::Policies*
ManagedNetworkConfigurationHandlerImpl::GetPoliciesForProfile(
const NetworkProfile& profile) const {
DCHECK(profile.type() != NetworkProfile::TYPE_SHARED ||
profile.userhash.empty());
return GetPoliciesForUser(profile.userhash);
}
ManagedNetworkConfigurationHandlerImpl::ManagedNetworkConfigurationHandlerImpl()
: network_state_handler_(NULL),
network_profile_handler_(NULL),
network_configuration_handler_(NULL),
network_device_handler_(NULL),
user_policy_applied_(false),
device_policy_applied_(false),
weak_ptr_factory_(this) {
CHECK(base::ThreadTaskRunnerHandle::IsSet());
}
ManagedNetworkConfigurationHandlerImpl::
~ManagedNetworkConfigurationHandlerImpl() {
if (network_profile_handler_)
network_profile_handler_->RemoveObserver(this);
}
void ManagedNetworkConfigurationHandlerImpl::Init(
NetworkStateHandler* network_state_handler,
NetworkProfileHandler* network_profile_handler,
NetworkConfigurationHandler* network_configuration_handler,
NetworkDeviceHandler* network_device_handler,
ProhibitedTechnologiesHandler* prohibited_technologies_handler) {
network_state_handler_ = network_state_handler;
network_profile_handler_ = network_profile_handler;
network_configuration_handler_ = network_configuration_handler;
network_device_handler_ = network_device_handler;
network_profile_handler_->AddObserver(this);
prohibited_technologies_handler_ = prohibited_technologies_handler;
}
void ManagedNetworkConfigurationHandlerImpl::OnPolicyAppliedToNetwork(
const std::string& service_path,
const std::string& guid) {
if (service_path.empty())
return;
for (auto& observer : observers_)
observer.PolicyAppliedToNetwork(service_path);
}
// Get{Managed}Properties helpers
void ManagedNetworkConfigurationHandlerImpl::GetDeviceStateProperties(
const std::string& service_path,
base::DictionaryValue* properties) {
const NetworkState* network =
network_state_handler_->GetNetworkState(service_path);
if (!network) {
NET_LOG(ERROR) << "GetDeviceStateProperties: no network for: "
<< service_path;
return;
}
if (!network->IsConnectedState())
return; // No (non saved) IP Configs for non connected networks.
const DeviceState* device_state =
network->device_path().empty()
? nullptr
: network_state_handler_->GetDeviceState(network->device_path());
// Get the hardware MAC address from the DeviceState.
if (device_state && !device_state->mac_address().empty()) {
properties->SetKey(shill::kAddressProperty,
base::Value(device_state->mac_address()));
}
// Get the IPConfig properties from the device and store them in "IPConfigs"
// (plural) in the properties dictionary. (Note: Shill only provides a single
// "IPConfig" property for a network service, but a consumer of this API may
// want information about all ipv4 and ipv6 IPConfig properties.
auto ip_configs = std::make_unique<base::ListValue>();
if (!device_state || device_state->ip_configs().empty()) {
// Shill may not provide IPConfigs for external Cellular devices/dongles
// (https://crbug.com/739314) or VPNs, so build a dictionary of ipv4
// properties from cached NetworkState properties .
NET_LOG(DEBUG)
<< "GetDeviceStateProperties: Setting IPv4 properties from network: "
<< service_path;
if (network->ipv4_config())
ip_configs->GetList().push_back(network->ipv4_config()->Clone());
} else {
// Convert the DeviceState IPConfigs dictionary to a ListValue.
for (const auto iter : device_state->ip_configs().DictItems())
ip_configs->GetList().push_back(iter.second.Clone());
}
if (!ip_configs->GetList().empty()) {
properties->SetWithoutPathExpansion(shill::kIPConfigsProperty,
std::move(ip_configs));
}
}
void ManagedNetworkConfigurationHandlerImpl::GetPropertiesCallback(
GetDevicePropertiesCallback send_callback,
const std::string& service_path,
const base::DictionaryValue& shill_properties) {
std::unique_ptr<base::DictionaryValue> shill_properties_copy(
shill_properties.DeepCopy());
std::string guid;
shill_properties.GetStringWithoutPathExpansion(shill::kGuidProperty, &guid);
if (guid.empty()) {
// Unmanaged networks are assigned a GUID in NetworkState. Provide this
// value in the ONC dictionary.
const NetworkState* state =
network_state_handler_->GetNetworkState(service_path);
if (state && !state->guid().empty()) {
guid = state->guid();
shill_properties_copy->SetKey(shill::kGuidProperty, base::Value(guid));
} else {
LOG(ERROR) << "Network has no GUID specified: " << service_path;
}
}
std::string type;
shill_properties_copy->GetStringWithoutPathExpansion(shill::kTypeProperty,
&type);
// Add any associated DeviceState properties.
GetDeviceStateProperties(service_path, shill_properties_copy.get());
// Only request additional Device properties for Cellular networks with a
// valid device.
std::string device_path;
if (!network_device_handler_ ||
type != shill::kTypeCellular ||
!shill_properties_copy->GetStringWithoutPathExpansion(
shill::kDeviceProperty, &device_path) ||
device_path.empty()) {
send_callback.Run(service_path, std::move(shill_properties_copy));
return;
}
// Request the device properties. On success or failure pass (a possibly
// modified) |shill_properties| to |send_callback|.
std::unique_ptr<base::DictionaryValue> shill_properties_copy_error_copy(
shill_properties_copy->DeepCopy());
network_device_handler_->GetDeviceProperties(
device_path,
base::Bind(&ManagedNetworkConfigurationHandlerImpl::
GetDevicePropertiesSuccess,
weak_ptr_factory_.GetWeakPtr(),
service_path,
base::Passed(&shill_properties_copy),
send_callback),
base::Bind(&ManagedNetworkConfigurationHandlerImpl::
GetDevicePropertiesFailure,
weak_ptr_factory_.GetWeakPtr(),
service_path,
base::Passed(&shill_properties_copy_error_copy),
send_callback));
}
void ManagedNetworkConfigurationHandlerImpl::GetDevicePropertiesSuccess(
const std::string& service_path,
std::unique_ptr<base::DictionaryValue> network_properties,
GetDevicePropertiesCallback send_callback,
const std::string& device_path,
const base::DictionaryValue& device_properties) {
// Create a "Device" dictionary in |network_properties|.
network_properties->SetKey(shill::kDeviceProperty, device_properties.Clone());
send_callback.Run(service_path, std::move(network_properties));
}
void ManagedNetworkConfigurationHandlerImpl::GetDevicePropertiesFailure(
const std::string& service_path,
std::unique_ptr<base::DictionaryValue> network_properties,
GetDevicePropertiesCallback send_callback,
const std::string& error_name,
std::unique_ptr<base::DictionaryValue> error_data) {
NET_LOG_ERROR("Error getting device properties", service_path);
send_callback.Run(service_path, std::move(network_properties));
}
} // namespace chromeos