// Copyright 2013 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chromeos/ash/components/network/managed_network_configuration_handler_impl.h"
#include <iterator>
#include <memory>
#include <string>
#include <utility>
#include <vector>
#include "ash/components/arc/arc_prefs.h"
#include "ash/constants/ash_features.h"
#include "ash/constants/ash_pref_names.h"
#include "base/containers/contains.h"
#include "base/functional/bind.h"
#include "base/functional/callback_helpers.h"
#include "base/location.h"
#include "base/logging.h"
#include "base/memory/ptr_util.h"
#include "base/task/single_thread_task_runner.h"
#include "base/uuid.h"
#include "base/values.h"
#include "chromeos/ash/components/dbus/shill/shill_manager_client.h"
#include "chromeos/ash/components/dbus/shill/shill_profile_client.h"
#include "chromeos/ash/components/dbus/shill/shill_service_client.h"
#include "chromeos/ash/components/network/cellular_policy_handler.h"
#include "chromeos/ash/components/network/client_cert_util.h"
#include "chromeos/ash/components/network/device_state.h"
#include "chromeos/ash/components/network/hotspot_controller.h"
#include "chromeos/ash/components/network/metrics/esim_policy_login_metrics_logger.h"
#include "chromeos/ash/components/network/metrics/wifi_network_metrics_helper.h"
#include "chromeos/ash/components/network/network_configuration_handler.h"
#include "chromeos/ash/components/network/network_device_handler.h"
#include "chromeos/ash/components/network/network_event_log.h"
#include "chromeos/ash/components/network/network_metadata_store.h"
#include "chromeos/ash/components/network/network_policy_observer.h"
#include "chromeos/ash/components/network/network_profile.h"
#include "chromeos/ash/components/network/network_profile_handler.h"
#include "chromeos/ash/components/network/network_state.h"
#include "chromeos/ash/components/network/network_state_handler.h"
#include "chromeos/ash/components/network/network_ui_data.h"
#include "chromeos/ash/components/network/network_util.h"
#include "chromeos/ash/components/network/onc/onc_merger.h"
#include "chromeos/ash/components/network/onc/onc_translator.h"
#include "chromeos/ash/components/network/policy_util.h"
#include "chromeos/ash/components/network/prohibited_technologies_handler.h"
#include "chromeos/ash/components/network/proxy/ui_proxy_config_service.h"
#include "chromeos/ash/components/network/shill_property_util.h"
#include "chromeos/ash/components/network/tether_constants.h"
#include "chromeos/ash/components/network/text_message_suppression_state.h"
#include "chromeos/components/onc/onc_signature.h"
#include "chromeos/components/onc/onc_utils.h"
#include "chromeos/components/onc/onc_validator.h"
#include "components/onc/onc_constants.h"
#include "third_party/cros_system_api/dbus/service_constants.h"
namespace ash {
namespace {
const char kEmptyServicePath[] = "/";
// 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,
network_handler::ErrorCallback error_callback,
const std::string& error_name) {
NET_LOG(ERROR) << "ManagedConfig Error: " << error_name
<< " For: " << NetworkPathId(service_path);
network_handler::RunErrorCallback(std::move(error_callback), error_name);
}
void LogErrorWithDictAndCallCallback(base::OnceClosure callback,
const base::Location& from_where,
const std::string& error_name) {
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);
std::move(callback).Run();
}
void OnResetDnsPropertiesFailure(const std::string& error_name) {
NET_LOG(ERROR) << "Failed to clear DNS Configurations, error name: "
<< error_name;
}
void OnSetCustomApnFailure(const std::string& error_name) {
NET_LOG(ERROR) << "Failed to set custom APNs, error name: " << error_name;
}
std::string GetStringFromDictionary(const base::Value::Dict& dict,
const char* key) {
const std::string* v = dict.FindString(key);
return v ? *v : std::string();
}
bool MatchesExistingNetworkState(const base::Value::Dict& 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: " << NetworkId(network_state)
<< ", type: " << type
<< " does not match: " << network_state->type();
return false;
}
if (type != ::onc::network_type::kWiFi)
return true;
const base::Value::Dict* wifi =
properties.FindDict(::onc::network_config::kWiFi);
if (!wifi) {
NET_LOG(ERROR) << "WiFi network configuration missing is WiFi properties: "
<< NetworkId(network_state);
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: "
<< NetworkId(network_state) << " 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: "
<< NetworkId(network_state) << " hex_ssid: " << hex_ssid
<< " does not match: " << network_state->GetHexSsid();
return false;
}
return true;
}
// Checks if |onc| is an unmanaged wifi network that has AutoConnect=true.
bool EnablesUnmanagedWifiAutoconnect(const base::Value::Dict& onc_dict) {
const std::string* type = onc_dict.FindString(::onc::network_config::kType);
if (!type || *type != ::onc::network_type::kWiFi) {
return false;
}
const std::string* source =
onc_dict.FindString(::onc::network_config::kSource);
if (!source || *source == ::onc::network_config::kSourceDevicePolicy ||
*source == ::onc::network_config::kSourceUserPolicy) {
return false;
}
const base::Value::Dict* wifi_config =
onc_dict.FindDict(::onc::network_config::kWiFi);
if (!wifi_config) {
return false;
}
std::optional<bool> autoconnect =
wifi_config->FindBool(::onc::wifi::kAutoConnect);
return autoconnect.has_value() && autoconnect.value();
}
} // namespace
ManagedNetworkConfigurationHandlerImpl::PolicyApplicationInfo::
PolicyApplicationInfo() = default;
ManagedNetworkConfigurationHandlerImpl::PolicyApplicationInfo::
~PolicyApplicationInfo() = default;
ManagedNetworkConfigurationHandlerImpl::PolicyApplicationInfo::
PolicyApplicationInfo(PolicyApplicationInfo&& other) = default;
ManagedNetworkConfigurationHandlerImpl::PolicyApplicationInfo&
ManagedNetworkConfigurationHandlerImpl::PolicyApplicationInfo::operator=(
PolicyApplicationInfo&& other) = default;
void ManagedNetworkConfigurationHandlerImpl::AddObserver(
NetworkPolicyObserver* observer) {
observers_.AddObserver(observer);
}
void ManagedNetworkConfigurationHandlerImpl::RemoveObserver(
NetworkPolicyObserver* observer) {
observers_.RemoveObserver(observer);
}
bool ManagedNetworkConfigurationHandlerImpl::HasObserver(
NetworkPolicyObserver* observer) const {
return observers_.HasObserver(observer);
}
void ManagedNetworkConfigurationHandlerImpl::Shutdown() {
if (did_shutdown_)
return; // May get called twice in tests.
did_shutdown_ = true;
if (network_profile_handler_ && network_profile_handler_->HasObserver(this))
network_profile_handler_->RemoveObserver(this);
network_profile_handler_ = nullptr;
for (auto& observer : observers_)
observer.OnManagedNetworkConfigurationHandlerShuttingDown();
}
void ManagedNetworkConfigurationHandlerImpl::GetManagedProperties(
const std::string& userhash,
const std::string& service_path,
network_handler::PropertiesCallback callback) {
if (!GetPoliciesForUser(userhash) || !GetPoliciesForUser(std::string())) {
NET_LOG(ERROR) << "GetManagedProperties failed: "
<< kPoliciesNotInitialized;
std::move(callback).Run(service_path, std::nullopt,
kPoliciesNotInitialized);
return;
}
NET_LOG(USER) << "GetManagedProperties: " << NetworkPathId(service_path);
network_configuration_handler_->GetShillProperties(
service_path,
base::BindOnce(
&ManagedNetworkConfigurationHandlerImpl::GetPropertiesCallback,
weak_ptr_factory_.GetWeakPtr(), PropertiesType::kManaged, userhash,
std::move(callback)));
}
void ManagedNetworkConfigurationHandlerImpl::GetProperties(
const std::string& userhash,
const std::string& service_path,
network_handler::PropertiesCallback callback) {
NET_LOG(USER) << "GetProperties for: " << NetworkPathId(service_path);
network_configuration_handler_->GetShillProperties(
service_path,
base::BindOnce(
&ManagedNetworkConfigurationHandlerImpl::GetPropertiesCallback,
weak_ptr_factory_.GetWeakPtr(), PropertiesType::kUnmanaged, userhash,
std::move(callback)));
}
void ManagedNetworkConfigurationHandlerImpl::SetProperties(
const std::string& service_path,
const base::Value::Dict& user_settings,
base::OnceClosure callback,
network_handler::ErrorCallback error_callback) {
const NetworkState* state =
network_state_handler_->GetNetworkStateFromServicePath(
service_path, true /* configured_only */);
if (!state) {
InvokeErrorCallback(service_path, std::move(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, std::move(error_callback),
kUnconfiguredNetwork);
return;
}
NET_LOG(DEBUG) << "Set Managed Properties for: "
<< NetworkPathId(service_path)
<< ". Profile: " << profile->ToDebugString();
const ProfilePolicies* policies = GetPoliciesForProfile(*profile);
if (!policies) {
InvokeErrorCallback(service_path, std::move(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.
base::Value::Dict user_settings_copy = user_settings.Clone();
if (!user_settings_copy.contains(::onc::network_config::kType)) {
user_settings_copy.Set(
::onc::network_config::kType,
network_util::TranslateShillTypeToONC(state->type()));
}
// Validate the ONC dictionary. We are liberal and ignore unknown field
// names. User settings are only partial ONC, thus we ignore missing fields.
chromeos::onc::Validator validator(
/*error_on_unknown_field=*/false,
/*error_on_wrong_recommended=*/false,
/*error_on_missing_field=*/false,
/*managed_onc=*/false,
/*log_warnings=*/true);
chromeos::onc::Validator::Result validation_result;
std::optional<base::Value::Dict> validated_user_settings =
validator.ValidateAndRepairObject(
&chromeos::onc::kNetworkConfigurationSignature, user_settings_copy,
&validation_result);
if (validation_result == chromeos::onc::Validator::INVALID) {
InvokeErrorCallback(service_path, std::move(error_callback),
kInvalidUserSettings);
return;
}
if (validation_result == chromeos::onc::Validator::VALID_WITH_WARNINGS) {
NET_LOG(USER) << "Validation of ONC user settings produced warnings.";
}
// Don't allow AutoConnect=true for unmanaged wifi networks if
// 'AllowOnlyPolicyNetworksToAutoconnect' policy is active.
if (EnablesUnmanagedWifiAutoconnect(validated_user_settings.value()) &&
AllowOnlyPolicyNetworksToAutoconnect()) {
InvokeErrorCallback(service_path, std::move(error_callback),
kInvalidUserSettings);
return;
}
// Fill in HexSSID field from contents of SSID field if not set already.
chromeos::onc::FillInHexSSIDFieldsInOncObject(
chromeos::onc::kNetworkConfigurationSignature,
validated_user_settings.value());
const base::Value::Dict* network_policy = policies->GetPolicyByGuid(guid);
if (network_policy) {
NET_LOG(DEBUG) << "Configuration is managed: " << NetworkId(state);
}
base::Value::Dict shill_dictionary = policy_util::CreateShillConfiguration(
*profile, guid, policies->GetGlobalNetworkConfig(), network_policy,
&validated_user_settings.value());
SetShillProperties(service_path, std::move(shill_dictionary),
std::move(callback), std::move(error_callback));
}
void ManagedNetworkConfigurationHandlerImpl::ClearShillProperties(
const std::string& service_path,
const std::vector<std::string>& names,
base::OnceClosure callback,
network_handler::ErrorCallback error_callback) {
network_configuration_handler_->ClearShillProperties(
service_path, names, std::move(callback), std::move(error_callback));
}
void ManagedNetworkConfigurationHandlerImpl::SetManagedActiveProxyValues(
const std::string& guid,
base::Value::Dict* dictionary) {
DCHECK(ui_proxy_config_service_);
const std::string proxy_settings_key = ::onc::network_config::kProxySettings;
base::Value::Dict* proxy_settings =
dictionary->EnsureDict(proxy_settings_key);
ui_proxy_config_service_->MergeEnforcedProxyConfig(guid, proxy_settings);
if (proxy_settings->empty()) {
dictionary->Remove(proxy_settings_key);
}
}
void ManagedNetworkConfigurationHandlerImpl::SetShillProperties(
const std::string& service_path,
base::Value::Dict shill_dictionary,
base::OnceClosure callback,
network_handler::ErrorCallback error_callback) {
network_configuration_handler_->SetShillProperties(
service_path, shill_dictionary, std::move(callback),
std::move(error_callback));
}
void ManagedNetworkConfigurationHandlerImpl::CreateConfiguration(
const std::string& userhash,
const base::Value::Dict& properties,
network_handler::ServiceResultCallback callback,
network_handler::ErrorCallback error_callback) const {
std::string guid =
GetStringFromDictionary(properties, ::onc::network_config::kGUID);
const NetworkState* network_state = nullptr;
if (!guid.empty()) {
network_state = network_state_handler_->GetNetworkStateFromGuid(guid);
}
if (network_state) {
NET_LOG(USER) << "CreateConfiguration for: " << NetworkId(network_state);
} else {
std::string type =
GetStringFromDictionary(properties, ::onc::network_config::kType);
NET_LOG(USER) << "Create new network configuration, Type: " << type;
if (type == ::onc::network_type::kWiFi) {
const base::Value::Dict* type_dict =
properties.FindDict(::onc::network_config::kWiFi);
const std::optional<bool> is_hidden =
type_dict ? type_dict->FindBool(::onc::wifi::kHiddenSSID)
: std::nullopt;
if (is_hidden.has_value()) {
WifiNetworkMetricsHelper::LogInitiallyConfiguredAsHidden(*is_hidden);
}
}
}
// Validate the ONC dictionary. We are liberal and ignore unknown field
// names. User settings are only partial ONC, thus we ignore missing fields.
chromeos::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.
chromeos::onc::Validator::Result validation_result;
std::optional<base::Value::Dict> validated_properties =
validator.ValidateAndRepairObject(
&chromeos::onc::kNetworkConfigurationSignature, properties,
&validation_result);
if (validation_result == chromeos::onc::Validator::INVALID) {
InvokeErrorCallback("", std::move(error_callback), kInvalidUserSettings);
return;
}
if (validation_result == chromeos::onc::Validator::VALID_WITH_WARNINGS) {
NET_LOG(DEBUG) << "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.
chromeos::onc::FillInHexSSIDFieldsInOncObject(
chromeos::onc::kNetworkConfigurationSignature,
validated_properties.value());
// Make sure the network is not configured through a user policy.
const ProfilePolicies* policies = nullptr;
if (!userhash.empty()) {
policies = GetPoliciesForUser(userhash);
if (!policies) {
InvokeErrorCallback("", std::move(error_callback),
kPoliciesNotInitialized);
return;
}
if (policies->HasPolicyMatchingShillProperties(
validated_properties.value())) {
InvokeErrorCallback("", std::move(error_callback),
kNetworkAlreadyConfigured);
return;
}
}
// Make user the network is not configured through a device policy.
policies = GetPoliciesForUser("");
if (!policies) {
InvokeErrorCallback("", std::move(error_callback), kPoliciesNotInitialized);
return;
}
if (policies->HasPolicyMatchingShillProperties(
validated_properties.value())) {
InvokeErrorCallback("", std::move(error_callback),
kNetworkAlreadyConfigured);
return;
}
const NetworkProfile* profile =
network_profile_handler_->GetProfileForUserhash(userhash);
if (!profile) {
InvokeErrorCallback("", std::move(error_callback), kProfileNotInitialized);
return;
}
// If a GUID was provided, verify that the new configuration matches an
// existing NetworkState for an unconfigured (i.e. visible) network.
// Requires HexSSID to be set first for comparing SSIDs.
if (!guid.empty()) {
// |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.value(),
network_state)) {
InvokeErrorCallback(network_state->path(), std::move(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: " << NetworkId(network_state)
<< " Profile: " << network_state->profile_path();
}
}
} else {
guid = base::Uuid::GenerateRandomV4().AsLowercaseString();
}
base::Value::Dict shill_dictionary =
policy_util::CreateShillConfiguration(*profile, guid,
nullptr, // no global policy
nullptr, // no network policy
&validated_properties.value());
network_configuration_handler_->CreateShillConfiguration(
shill_dictionary, std::move(callback), std::move(error_callback));
}
NetworkMetadataStore*
ManagedNetworkConfigurationHandlerImpl::GetNetworkMetadataStore() {
if (network_metadata_store_for_testing_) {
return network_metadata_store_for_testing_;
}
return NetworkHandler::Get()->network_metadata_store();
}
void ManagedNetworkConfigurationHandlerImpl::ConfigurePolicyNetwork(
const base::Value::Dict& shill_properties,
base::OnceClosure callback) const {
auto split_callback = base::SplitOnceCallback(std::move(callback));
network_configuration_handler_->CreateShillConfiguration(
shill_properties,
base::BindOnce(
&ManagedNetworkConfigurationHandlerImpl::OnPolicyAppliedToNetwork,
weak_ptr_factory_.GetWeakPtr(), std::move(split_callback.first)),
base::BindOnce(&LogErrorWithDictAndCallCallback,
std::move(split_callback.second), FROM_HERE));
}
void ManagedNetworkConfigurationHandlerImpl::RemoveConfiguration(
const std::string& service_path,
base::OnceClosure callback,
network_handler::ErrorCallback error_callback) const {
network_configuration_handler_->RemoveConfiguration(
service_path,
base::BindRepeating(
&ManagedNetworkConfigurationHandlerImpl::CanRemoveNetworkConfig,
base::Unretained(this)),
std::move(callback), std::move(error_callback));
}
void ManagedNetworkConfigurationHandlerImpl::
RemoveConfigurationFromCurrentProfile(
const std::string& service_path,
base::OnceClosure callback,
network_handler::ErrorCallback error_callback) const {
network_configuration_handler_->RemoveConfigurationFromCurrentProfile(
service_path, std::move(callback), std::move(error_callback));
}
void ManagedNetworkConfigurationHandlerImpl::SetPolicy(
::onc::ONCSource onc_source,
const std::string& userhash,
const base::Value::List& network_configs_onc,
const base::Value::Dict& 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());
ProfilePolicies* policies = GetOrCreatePoliciesForUser(userhash);
policies->SetGlobalNetworkConfig(global_network_config);
// Update prohibited technologies if this is a device policy.
if (onc_source == ::onc::ONC_SOURCE_DEVICE_POLICY &&
prohibited_technologies_handler_) {
const base::Value::List* prohibited_list =
policies->GetGlobalNetworkConfig()->FindList(
::onc::global_network_config::kDisableNetworkTypes);
if (prohibited_list) {
prohibited_technologies_handler_->SetProhibitedTechnologies(
*prohibited_list);
} else {
// An empty list is provided to guarantee that all technologies are
// explicitly allowed if the policy being applied is a device policy that
// does not specifically prohibit any technologies.
prohibited_technologies_handler_->SetProhibitedTechnologies(
base::Value::List());
}
}
ApplyOrQueuePolicies(
userhash, policies->ApplyOncNetworkConfigurationList(network_configs_onc),
/*can_affect_other_networks=*/true, /*options=*/{});
ApplyDisconnectWiFiOnEthernetPolicy();
for (auto& observer : observers_)
observer.PoliciesChanged(userhash);
}
void ManagedNetworkConfigurationHandlerImpl::
ApplyDisconnectWiFiOnEthernetPolicy() {
const std::string* disconnect_wifi_policy = FindGlobalPolicyString(
::onc::global_network_config::kDisconnectWiFiOnEthernet);
if (disconnect_wifi_policy) {
base::Value shill_property_value =
base::Value(shill::kDisconnectWiFiOnEthernetOff);
if (*disconnect_wifi_policy ==
::onc::global_network_config::kDisconnectWiFiOnEthernetWhenConnected) {
shill_property_value =
base::Value(shill::kDisconnectWiFiOnEthernetConnected);
}
if (*disconnect_wifi_policy ==
::onc::global_network_config::kDisconnectWiFiOnEthernetWhenOnline) {
shill_property_value =
base::Value(shill::kDisconnectWiFiOnEthernetOnline);
}
network_configuration_handler_->SetManagerProperty(
shill::kDisconnectWiFiOnEthernetProperty, shill_property_value);
}
}
bool ManagedNetworkConfigurationHandlerImpl::IsAnyPolicyApplicationRunning()
const {
for (const auto& [_, policy_application_info] :
policy_application_info_map_) {
if (policy_application_info.IsRunningOrRequired()) {
return true;
}
}
return false;
}
void ManagedNetworkConfigurationHandlerImpl::ApplyOrQueuePolicies(
const std::string& userhash,
base::flat_set<std::string> modified_policies,
bool can_affect_other_networks,
PolicyApplicator::Options options) {
// Note that this will default-construct a PolicyApplicationInfo if none
// exists for the shill profile identifier by |userhash| yet.
PolicyApplicationInfo& policy_application_info =
policy_application_info_map_[userhash];
policy_application_info.options.Merge(options);
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;
}
if (!can_affect_other_networks && modified_policies.empty()) {
// The change can not affect managed networks (e.g. because it only
// affected resolved client certificates) but no policy
// NetworkConfigurations have changed, so there can be no effective change.
// Avoid scheduling a policy application for this.
// This can happen e.g. if a ONC variable changes which is not used by any
// NetworkConfiguration.
return;
}
policy_application_info.modified_policy_guids.insert(
std::make_move_iterator(modified_policies.begin()),
std::make_move_iterator(modified_policies.end()));
SchedulePolicyApplication(userhash);
}
void ManagedNetworkConfigurationHandlerImpl::SchedulePolicyApplication(
const std::string& userhash) {
PolicyApplicationInfo& policy_application_info =
policy_application_info_map_[userhash];
policy_application_info.application_required = true;
if (policy_application_info.task_scheduled) {
return;
}
policy_application_info.task_scheduled = true;
base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE,
base::BindOnce(
&ManagedNetworkConfigurationHandlerImpl::StartPolicyApplication,
weak_ptr_factory_.GetWeakPtr(), userhash));
}
void ManagedNetworkConfigurationHandlerImpl::StartPolicyApplication(
const std::string& userhash) {
PolicyApplicationInfo& policy_application_info =
policy_application_info_map_[userhash];
DCHECK(policy_application_info.task_scheduled);
DCHECK(policy_application_info.application_required);
policy_application_info.task_scheduled = false;
policy_application_info.application_required = false;
const ProfilePolicies* policies = policies_by_user_[userhash].get();
DCHECK(policies);
const NetworkProfile* profile =
network_profile_handler_->GetProfileForUserhash(userhash);
if (!profile) {
// The shill profile has been removed in the meantime. This could happen
// e.g. if the user session is exiting or the device is shutting down.
// See b/240237232 for context.
return;
}
base::flat_set<std::string> modified_guids;
policy_application_info.modified_policy_guids.swap(modified_guids);
PolicyApplicator::Options options =
std::move(policy_application_info.options);
policy_application_info.options = {};
policy_application_info.running_policy_applicator =
std::make_unique<PolicyApplicator>(
*profile, policies->GetGuidToPolicyMap(),
policies->GetGlobalNetworkConfig()->Clone(), this,
managed_cellular_pref_handler_, std::move(modified_guids),
std::move(options));
policy_application_info.running_policy_applicator->Run();
}
void ManagedNetworkConfigurationHandlerImpl::SetProfileWideVariableExpansions(
const std::string& userhash,
base::flat_map<std::string, std::string> expansions) {
ApplyOrQueuePolicies(
userhash,
GetOrCreatePoliciesForUser(userhash)->SetProfileWideExpansions(
std::move(expansions)),
/*can_affect_other_networks=*/false,
/*options=*/{});
}
bool ManagedNetworkConfigurationHandlerImpl::SetResolvedClientCertificate(
const std::string& userhash,
const std::string& guid,
client_cert::ResolvedCert resolved_cert) {
bool change_had_effect =
GetOrCreatePoliciesForUser(userhash)->SetResolvedClientCertificate(
guid, std::move(resolved_cert));
if (!change_had_effect)
return false;
ApplyOrQueuePolicies(userhash, {guid},
/*can_affect_other_networks=*/false, /*options=*/{});
return true;
}
void ManagedNetworkConfigurationHandlerImpl::set_ui_proxy_config_service(
UIProxyConfigService* ui_proxy_config_service) {
ui_proxy_config_service_ = ui_proxy_config_service;
}
void ManagedNetworkConfigurationHandlerImpl::set_user_prefs(
PrefService* user_prefs) {
user_prefs_ = user_prefs;
}
void ManagedNetworkConfigurationHandlerImpl::OnProfileAdded(
const NetworkProfile& profile) {
VLOG(1) << "Adding profile: " << profile.ToDebugString();
const ProfilePolicies* policies = GetPoliciesForProfile(profile);
if (!policies) {
VLOG(1) << "The relevant policy is not initialized, "
<< "postponing policy application.";
// See SetPolicy.
return;
}
// The profile's network policy may have a GlobalNetworkConfiguration which
// can affect unmanaged networks (see ApplyGlobalPolicyOnUnmanagedEntry in
// PolicyApplicator), so set can_affect_other_networks to true.
ApplyOrQueuePolicies(profile.userhash, policies->GetAllPolicyGuids(),
/*can_affect_other_networks=*/true, /*options=*/{});
}
void ManagedNetworkConfigurationHandlerImpl::OnProfileRemoved(
const NetworkProfile& profile) {
// Nothing to do in this case.
}
void ManagedNetworkConfigurationHandlerImpl::CreateConfigurationFromPolicy(
const base::Value::Dict& shill_properties,
base::OnceClosure callback) {
ConfigurePolicyNetwork(shill_properties, std::move(callback));
}
void ManagedNetworkConfigurationHandlerImpl::
UpdateExistingConfigurationWithPropertiesFromPolicy(
const base::Value::Dict& existing_properties,
const base::Value::Dict& new_properties,
base::OnceClosure callback) {
base::Value::Dict shill_properties;
const std::string* profile =
existing_properties.FindString(shill::kProfileProperty);
if (!profile || profile->empty()) {
// TODO(b/258782165): Figure out how to deal with entries that don't have a
// Profile property properly.
NET_LOG(ERROR) << "Missing profile property: "
<< shill_property_util::GetNetworkIdFromProperties(
existing_properties);
std::move(callback).Run();
return;
}
shill_properties.Set(shill::kProfileProperty, *profile);
if (!shill_property_util::CopyIdentifyingProperties(
existing_properties,
/*properties_read_from_shill=*/true, &shill_properties)) {
NET_LOG(ERROR) << "Missing identifying properties",
shill_property_util::GetNetworkIdFromProperties(existing_properties);
}
shill_properties.Merge(new_properties.Clone());
auto split_callback = base::SplitOnceCallback(std::move(callback));
network_configuration_handler_->CreateShillConfiguration(
shill_properties,
base::BindOnce(
&ManagedNetworkConfigurationHandlerImpl::OnPolicyAppliedToNetwork,
weak_ptr_factory_.GetWeakPtr(), std::move(split_callback.first)),
base::BindOnce(&LogErrorWithDictAndCallCallback,
std::move(split_callback.second), FROM_HERE));
}
void ManagedNetworkConfigurationHandlerImpl::TriggerCellularPolicyApplication(
const NetworkProfile& profile,
const base::flat_set<std::string>& new_cellular_policy_guids) {
const ProfilePolicies* policies = GetPoliciesForUser(profile.userhash);
DCHECK(policies);
if (!cellular_policy_handler_) {
NET_LOG(ERROR) << "Unable to attempt policy eSIM installation since "
<< "CellularPolicyHandler has not been initialized";
return;
}
for (const std::string& guid : new_cellular_policy_guids) {
const base::Value::Dict* network_policy = policies->GetPolicyByGuid(guid);
DCHECK(network_policy);
cellular_policy_handler_->InstallESim(*network_policy);
}
}
void ManagedNetworkConfigurationHandlerImpl::OnCellularPoliciesApplied(
const NetworkProfile& profile) {
const std::string& userhash = profile.userhash;
bool is_device_policy = userhash.empty();
// Inform observers that cellular policy application has finished.
// Only do this if non-cellular policy application is already done for
// `profile`, otherwise wait for OnPoliciesApplied to send the notification.
// Note that currently there is no separate observer signal for this.
if ((is_device_policy && device_policy_applied_) ||
(!is_device_policy && user_policy_applied_)) {
for (auto& observer : observers_)
observer.PoliciesApplied(userhash);
}
}
void ManagedNetworkConfigurationHandlerImpl::
OnEnterpriseMonitoredWebPoliciesApplied() const {
for (auto& observer : observers_) {
observer.PoliciesApplied(std::string());
}
}
void ManagedNetworkConfigurationHandlerImpl::OnPoliciesApplied(
const NetworkProfile& profile,
const base::flat_set<std::string>& new_cellular_policy_guids) {
const std::string& userhash = profile.userhash;
VLOG(1) << "Policy application for user '" << userhash << "' finished.";
PolicyApplicationInfo& policy_application_info =
policy_application_info_map_[userhash];
base::SingleThreadTaskRunner::GetCurrentDefault()->DeleteSoon(
FROM_HERE, std::move(policy_application_info.running_policy_applicator));
TriggerCellularPolicyApplication(profile, new_cellular_policy_guids);
if (policy_application_info.application_required) {
// This means that a network policy change happened while policy was being
// applied. Schedule another network policy application run.
SchedulePolicyApplication(userhash);
return;
}
if (userhash.empty())
device_policy_applied_ = true;
else
user_policy_applied_ = true;
ESimPolicyLoginMetricsLogger::RecordBlockNonManagedCellularBehavior(
AllowOnlyPolicyCellularNetworks());
// Call UpdateBlockCellularNetworks when either device policy applied or
// user policy applied so that so that unmanaged cellular networks are
// blocked correctly if the policy appears in either.
network_state_handler_->UpdateBlockedCellularNetworks(
AllowOnlyPolicyCellularNetworks());
if (network_device_handler_) {
network_device_handler_->SetAllowCellularSimLock(AllowCellularSimLock());
}
if (features::IsApnRevampAndAllowApnModificationPolicyEnabled()) {
ModifyCustomAPNs();
}
if (hotspot_controller_) {
hotspot_controller_->SetPolicyAllowHotspot(AllowCellularHotspot());
}
if (device_policy_applied_ && user_policy_applied_) {
network_state_handler_->UpdateBlockedWifiNetworks(
AllowOnlyPolicyWiFiToConnect(),
AllowOnlyPolicyWiFiToConnectIfAvailable(), GetBlockedHexSSIDs());
}
for (auto& observer : observers_)
observer.PoliciesApplied(userhash);
}
void ManagedNetworkConfigurationHandlerImpl::ModifyCustomAPNs() {
NetworkStateHandler::NetworkStateList networks;
network_state_handler_->GetNetworkListByType(NetworkTypePattern::Cellular(),
/*configured_only=*/false,
/*visible_only=*/false,
/*limit=*/0, &networks);
for (const NetworkState* network : networks) {
if (network->IsManagedByPolicy()) {
continue;
}
const base::Value::List* existing_custom_apn_list =
GetNetworkMetadataStore()->GetCustomApnList(network->guid());
if (existing_custom_apn_list) {
base::Value::Dict onc;
onc.Set(::onc::network_config::kGUID, network->guid());
onc.Set(::onc::network_config::kType, ::onc::network_type::kCellular);
base::Value::Dict type_dict;
type_dict.Set(::onc::cellular::kCustomAPNList,
AllowApnModification() ? existing_custom_apn_list->Clone()
: base::Value::List());
onc.Set(::onc::network_type::kCellular, std::move(type_dict));
SetProperties(network->path(), onc, base::DoNothing(),
base::BindOnce(&OnSetCustomApnFailure));
}
}
}
const base::Value::Dict*
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 ProfilePolicies* user_policies = GetPoliciesForUser(userhash);
if (user_policies) {
const base::Value::Dict* policy = user_policies->GetPolicyByGuid(guid);
if (policy) {
*onc_source = ::onc::ONC_SOURCE_USER_POLICY;
return policy;
}
}
}
const ProfilePolicies* device_policies =
GetPoliciesForUser(/*userhash=*/std::string());
if (device_policies) {
const base::Value::Dict* policy = device_policies->GetPolicyByGuid(guid);
if (policy) {
*onc_source = ::onc::ONC_SOURCE_DEVICE_POLICY;
return policy;
}
}
return nullptr;
}
void ManagedNetworkConfigurationHandlerImpl::ResetDNSPropertiesCallback(
const std::string& service_path,
std::optional<base::Value::Dict> network_properties,
std::optional<std::string> error) {
if (!network_properties) {
return;
}
// Create a dictionary of the relevant properties to set.
base::Value::Dict reset_dns_properties;
// NameServersConfigType - to be deterined by DHCP.
reset_dns_properties.Set(::onc::network_config::kNameServersConfigType,
::onc::network_config::kIPConfigTypeDHCP);
// IPAddressConfigType - set to whatever previously existed.
base::Value* ip_address_config_type =
network_properties->Find(::onc::network_config::kIPAddressConfigType);
reset_dns_properties.Set(::onc::network_config::kIPAddressConfigType,
std::move(*ip_address_config_type));
// StaticIPConfig - set to what previously existed if either
// kNameServersConfigType or kIPAddressConfigType was previously set to
// "Static" - the NameServers field is cleared later through the ONC
// Validator in SetProperties.
base::Value* static_ip_config =
network_properties->Find(::onc::network_config::kStaticIPConfig);
if (static_ip_config) {
reset_dns_properties.Set(::onc::network_config::kStaticIPConfig,
std::move(*static_ip_config));
}
// Type - set to the existing network type, (wifi, ethernet, etc).
base::Value* type = network_properties->Find(::onc::network_config::kType);
reset_dns_properties.Set(::onc::network_config::kType, std::move(*type));
// The policy is then translated and applied to the shill service.
SetProperties(service_path, std::move(reset_dns_properties),
base::DoNothing(),
base::BindOnce(&OnResetDnsPropertiesFailure));
}
void ManagedNetworkConfigurationHandlerImpl::ResetDNSProperties(
const std::string& service_path) {
GetProperties(
/*userhash*/ std::string(), service_path,
base::BindOnce(
&ManagedNetworkConfigurationHandlerImpl::ResetDNSPropertiesCallback,
weak_ptr_factory_.GetWeakPtr()));
}
bool ManagedNetworkConfigurationHandlerImpl::HasAnyPolicyNetwork(
const std::string& userhash) const {
const ProfilePolicies* policies = GetPoliciesForUser(userhash);
if (!policies)
return false;
return !policies->GetAllPolicyGuids().empty();
}
const base::Value::Dict*
ManagedNetworkConfigurationHandlerImpl::GetGlobalConfigFromPolicy(
const std::string& userhash) const {
const ProfilePolicies* policies = GetPoliciesForUser(userhash);
if (!policies)
return nullptr;
return policies->GetGlobalNetworkConfig();
}
const base::Value::Dict*
ManagedNetworkConfigurationHandlerImpl::FindPolicyByGuidAndProfile(
const std::string& guid,
const std::string& profile_path,
PolicyType policy_type,
::onc::ONCSource* out_onc_source,
std::string* out_userhash) 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
<< " For: " << NetworkGuidId(guid);
return nullptr;
}
const ProfilePolicies* policies = GetPoliciesForProfile(*profile);
if (!policies)
return nullptr;
const base::Value::Dict* policy =
(policy_type == PolicyType::kOriginal)
? policies->GetOriginalPolicyByGuid(guid)
: policies->GetPolicyByGuid(guid);
if (policy && out_onc_source) {
*out_onc_source =
(profile->userhash.empty() ? ::onc::ONC_SOURCE_DEVICE_POLICY
: ::onc::ONC_SOURCE_USER_POLICY);
}
if (policy && out_userhash) {
*out_userhash = profile->userhash;
}
return policy;
}
bool ManagedNetworkConfigurationHandlerImpl::IsNetworkConfiguredByPolicy(
const std::string& guid,
const std::string& profile_path) const {
::onc::ONCSource onc_source = ::onc::ONC_SOURCE_UNKNOWN;
return FindPolicyByGUID(guid, profile_path, &onc_source) != nullptr;
}
bool ManagedNetworkConfigurationHandlerImpl::CanRemoveNetworkConfig(
const std::string& guid,
const std::string& profile_path) const {
return !IsNetworkConfiguredByPolicy(guid, profile_path);
}
PolicyTextMessageSuppressionState
ManagedNetworkConfigurationHandlerImpl::GetAllowTextMessages() const {
const std::string* allow_text_messages =
FindGlobalPolicyString(::onc::global_network_config::kAllowTextMessages);
if (!allow_text_messages) {
return PolicyTextMessageSuppressionState::kUnset;
}
if (*allow_text_messages == ::onc::cellular::kTextMessagesAllow) {
return PolicyTextMessageSuppressionState::kAllow;
}
if (*allow_text_messages == ::onc::cellular::kTextMessagesSuppress) {
return PolicyTextMessageSuppressionState::kSuppress;
}
return PolicyTextMessageSuppressionState::kUnset;
}
bool ManagedNetworkConfigurationHandlerImpl::AllowApnModification() const {
return FindGlobalPolicyBool(
::onc::global_network_config::kAllowAPNModification)
.value_or(true);
}
bool ManagedNetworkConfigurationHandlerImpl::AllowCellularSimLock() const {
return FindGlobalPolicyBool(
::onc::global_network_config::kAllowCellularSimLock)
.value_or(true);
}
bool ManagedNetworkConfigurationHandlerImpl::AllowCellularHotspot() const {
return FindGlobalPolicyBool(
::onc::global_network_config::kAllowCellularHotspot)
.value_or(true);
}
bool ManagedNetworkConfigurationHandlerImpl::AllowOnlyPolicyCellularNetworks()
const {
return FindGlobalPolicyBool(
::onc::global_network_config::kAllowOnlyPolicyCellularNetworks)
.value_or(false);
}
bool ManagedNetworkConfigurationHandlerImpl::AllowOnlyPolicyWiFiToConnect()
const {
return FindGlobalPolicyBool(
::onc::global_network_config::kAllowOnlyPolicyWiFiToConnect)
.value_or(false);
}
bool ManagedNetworkConfigurationHandlerImpl::
AllowOnlyPolicyWiFiToConnectIfAvailable() const {
return FindGlobalPolicyBool(::onc::global_network_config::
kAllowOnlyPolicyWiFiToConnectIfAvailable)
.value_or(false);
}
bool ManagedNetworkConfigurationHandlerImpl::
AllowOnlyPolicyNetworksToAutoconnect() const {
return FindGlobalPolicyBool(::onc::global_network_config::
kAllowOnlyPolicyNetworksToAutoconnect)
.value_or(false);
}
bool ManagedNetworkConfigurationHandlerImpl::RecommendedValuesAreEphemeral()
const {
DCHECK(policy_util::AreEphemeralNetworkPoliciesEnabled());
return FindGlobalPolicyBool(
::onc::global_network_config::kRecommendedValuesAreEphemeral)
.value_or(false);
}
bool ManagedNetworkConfigurationHandlerImpl::
UserCreatedNetworkConfigurationsAreEphemeral() const {
DCHECK(policy_util::AreEphemeralNetworkPoliciesEnabled());
return FindGlobalPolicyBool(::onc::global_network_config::
kUserCreatedNetworkConfigurationsAreEphemeral)
.value_or(false);
}
bool ManagedNetworkConfigurationHandlerImpl::IsProhibitedFromConfiguringVpn()
const {
if (!user_prefs_ ||
!user_prefs_->FindPreference(arc::prefs::kAlwaysOnVpnPackage) ||
!user_prefs_->FindPreference(prefs::kVpnConfigAllowed)) {
return false;
}
// When an admin Activate Always ON VPN for all user traffic with an Android
// VPN, arc::prefs::kAlwaysOnVpnPackage will be non empty. If additionally,
// the admin prohibits users from disconnecting from a VPN manually,
// prefs::kVpnConfigAllowed becomes false. See go/test-cros-vpn-policies.
return !user_prefs_->GetString(arc::prefs::kAlwaysOnVpnPackage).empty() &&
!user_prefs_->GetBoolean(prefs::kVpnConfigAllowed);
}
std::vector<std::string>
ManagedNetworkConfigurationHandlerImpl::GetBlockedHexSSIDs() const {
const base::Value::List* blocked_value =
FindGlobalPolicyList(::onc::global_network_config::kBlockedHexSSIDs);
if (!blocked_value) {
return std::vector<std::string>();
}
std::vector<std::string> blocked_hex_ssids;
for (const base::Value& entry : *blocked_value) {
blocked_hex_ssids.push_back(entry.GetString());
}
return blocked_hex_ssids;
}
ProfilePolicies*
ManagedNetworkConfigurationHandlerImpl::GetOrCreatePoliciesForUser(
const std::string& userhash) {
auto it = policies_by_user_.find(userhash);
ProfilePolicies* policies = nullptr;
if (it != policies_by_user_.end()) {
policies = it->second.get();
} else {
auto policies_owned = std::make_unique<ProfilePolicies>();
policies = policies_owned.get();
policies_by_user_[userhash] = std::move(policies_owned);
}
return policies;
}
const ProfilePolicies*
ManagedNetworkConfigurationHandlerImpl::GetPoliciesForUser(
const std::string& userhash) const {
auto it = policies_by_user_.find(userhash);
return it != policies_by_user_.end() ? it->second.get() : nullptr;
}
const ProfilePolicies*
ManagedNetworkConfigurationHandlerImpl::GetPoliciesForProfile(
const NetworkProfile& profile) const {
DCHECK(profile.type() != NetworkProfile::TYPE_SHARED ||
profile.userhash.empty());
return GetPoliciesForUser(profile.userhash);
}
ManagedNetworkConfigurationHandlerImpl::
ManagedNetworkConfigurationHandlerImpl() {
CHECK(base::SingleThreadTaskRunner::HasCurrentDefault());
}
ManagedNetworkConfigurationHandlerImpl::
~ManagedNetworkConfigurationHandlerImpl() {
if (network_profile_handler_ && network_profile_handler_->HasObserver(this))
network_profile_handler_->RemoveObserver(this);
Shutdown();
}
void ManagedNetworkConfigurationHandlerImpl::Init(
CellularPolicyHandler* cellular_policy_handler,
ManagedCellularPrefHandler* managed_cellular_pref_handler,
NetworkStateHandler* network_state_handler,
NetworkProfileHandler* network_profile_handler,
NetworkConfigurationHandler* network_configuration_handler,
NetworkDeviceHandler* network_device_handler,
ProhibitedTechnologiesHandler* prohibited_technologies_handler,
HotspotController* hotspot_controller) {
cellular_policy_handler_ = cellular_policy_handler;
managed_cellular_pref_handler_ = managed_cellular_pref_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;
if (network_profile_handler_)
network_profile_handler_->AddObserver(this);
prohibited_technologies_handler_ = prohibited_technologies_handler;
hotspot_controller_ = hotspot_controller;
}
void ManagedNetworkConfigurationHandlerImpl::OnPolicyAppliedToNetwork(
base::OnceClosure callback,
const std::string& service_path,
const std::string& guid) const {
// When this is called, the policy has been fully applied and is reflected in
// NetworkStateHandler, so it is safe to notify observers.
// Notifying observers is the last step of policy application to
// |service_path|.
NotifyPolicyAppliedToNetwork(service_path);
// Inform the caller that has requested policy application that it has
// finished.
std::move(callback).Run();
}
// Get{Managed}Properties helpers
void ManagedNetworkConfigurationHandlerImpl::GetDeviceStateProperties(
const std::string& service_path,
base::Value::Dict* properties) {
const NetworkState* network =
network_state_handler_->GetNetworkState(service_path);
if (!network) {
NET_LOG(ERROR) << "GetDeviceStateProperties: no network for: "
<< NetworkPathId(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->Set(shill::kAddressProperty, 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.
base::Value::List ip_configs;
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: "
<< NetworkId(network);
if (network->ipv4_config().has_value()) {
ip_configs.Append(network->ipv4_config()->Clone());
}
} else {
// Convert the DeviceState IPConfigs dictionary to a list.
for (const auto iter : device_state->ip_configs()) {
ip_configs.Append(iter.second.Clone());
}
}
if (!ip_configs.empty()) {
properties->Set(shill::kIPConfigsProperty, std::move(ip_configs));
}
}
void ManagedNetworkConfigurationHandlerImpl::GetPropertiesCallback(
PropertiesType properties_type,
const std::string& userhash,
network_handler::PropertiesCallback callback,
const std::string& service_path,
std::optional<base::Value::Dict> shill_properties) {
if (!shill_properties) {
SendProperties(properties_type, userhash, service_path, std::move(callback),
std::nullopt);
return;
}
const std::string* guid = shill_properties->FindString(shill::kGuidProperty);
if (!guid || 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()) {
shill_properties->Set(shill::kGuidProperty, state->guid());
} else {
NET_LOG(ERROR) << "Network has no GUID specified: "
<< NetworkPathId(service_path);
}
}
const std::string* type = shill_properties->FindString(shill::kTypeProperty);
// Add any associated DeviceState properties.
GetDeviceStateProperties(service_path, &shill_properties.value());
// Only request additional Device properties for Cellular networks with a
// valid device.
if (network_device_handler_ && *type == shill::kTypeCellular) {
std::string* device_path =
shill_properties->FindString(shill::kDeviceProperty);
if (device_path && !device_path->empty() &&
*device_path != kEmptyServicePath) {
// Request the device properties. On success or failure pass (a possibly
// modified) |shill_properties| to |send_callback|.
network_device_handler_->GetDeviceProperties(
*device_path,
base::BindOnce(
&ManagedNetworkConfigurationHandlerImpl::OnGetDeviceProperties,
weak_ptr_factory_.GetWeakPtr(), properties_type, userhash,
service_path, std::move(callback), std::move(shill_properties)));
return;
}
}
SendProperties(properties_type, userhash, service_path, std::move(callback),
std::move(shill_properties));
}
void ManagedNetworkConfigurationHandlerImpl::OnGetDeviceProperties(
PropertiesType properties_type,
const std::string& userhash,
const std::string& service_path,
network_handler::PropertiesCallback callback,
std::optional<base::Value::Dict> network_properties,
const std::string& device_path,
std::optional<base::Value::Dict> device_properties) {
DCHECK(network_properties);
if (!device_properties) {
NET_LOG(ERROR) << "Error getting device properties: "
<< NetworkPathId(service_path);
} else {
// Create a "Device" dictionary in |network_properties|.
network_properties->Set(shill::kDeviceProperty,
std::move(*device_properties));
}
SendProperties(properties_type, userhash, service_path, std::move(callback),
std::move(network_properties));
}
void ManagedNetworkConfigurationHandlerImpl::SendProperties(
PropertiesType properties_type,
const std::string& userhash,
const std::string& service_path,
network_handler::PropertiesCallback callback,
std::optional<base::Value::Dict> shill_properties) {
auto get_name = [](PropertiesType properties_type) {
switch (properties_type) {
case PropertiesType::kUnmanaged:
return "GetProperties";
case PropertiesType::kManaged:
return "GetManagedProperties";
}
return "";
};
if (!shill_properties) {
NET_LOG(ERROR) << get_name(properties_type) << " Failed.";
std::move(callback).Run(service_path, std::nullopt,
network_handler::kDBusFailedError);
return;
}
const std::string* guid = shill_properties->FindString(shill::kGuidProperty);
if (!guid) {
NET_LOG(ERROR) << get_name(properties_type) << " Missing GUID.";
std::move(callback).Run(service_path, std::nullopt, kUnknownNetwork);
return;
}
const NetworkState* network_state =
network_state_handler_->GetNetworkState(service_path);
::onc::ONCSource onc_source;
FindPolicyByGUID(userhash, *guid, &onc_source);
base::Value::Dict onc_network = onc::TranslateShillServiceToONCPart(
shill_properties.value(), onc_source,
&chromeos::onc::kNetworkWithStateSignature, network_state);
if (properties_type == PropertiesType::kUnmanaged) {
std::move(callback).Run(
service_path, std::make_optional(std::move(onc_network)), std::nullopt);
return;
}
const std::string* profile_path =
shill_properties->FindString(shill::kProfileProperty);
const NetworkProfile* profile =
profile_path && network_profile_handler_
? network_profile_handler_->GetProfileForPath(*profile_path)
: nullptr;
if (!profile && !(network_state && network_state->IsNonProfileType())) {
// Visible but unsaved (not known) networks will not have a profile.
NET_LOG(DEBUG) << "No profile for: " << NetworkId(network_state)
<< " Profile path: " << profile_path;
}
std::unique_ptr<NetworkUIData> ui_data =
shill_property_util::GetUIDataFromProperties(shill_properties.value());
const base::Value::Dict* user_settings = nullptr;
if (ui_data && profile) {
user_settings = ui_data->GetUserSettingsDictionary();
} else if (profile) {
NET_LOG(DEBUG) << "Network contains empty or invalid UIData: "
<< NetworkId(network_state);
// 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.
}
const base::Value::Dict* network_policy = nullptr;
const base::Value::Dict* global_policy = nullptr;
if (profile) {
const ProfilePolicies* policies = GetPoliciesForProfile(*profile);
if (!policies) {
NET_LOG(ERROR) << "GetManagedProperties failed: "
<< kPoliciesNotInitialized;
std::move(callback).Run(service_path, std::nullopt,
kPoliciesNotInitialized);
return;
}
if (!guid->empty()) {
network_policy = policies->GetPolicyByGuid(*guid);
}
global_policy = policies->GetGlobalNetworkConfig();
}
base::Value::Dict augmented_properties = policy_util::CreateManagedONC(
global_policy, network_policy, user_settings, &onc_network, profile);
SetManagedActiveProxyValues(*guid, &augmented_properties);
std::move(callback).Run(service_path,
std::make_optional(std::move(augmented_properties)),
std::nullopt);
}
void ManagedNetworkConfigurationHandlerImpl::NotifyPolicyAppliedToNetwork(
const std::string& service_path) const {
DCHECK(!service_path.empty());
for (auto& observer : observers_)
observer.PolicyAppliedToNetwork(service_path);
}
std::optional<bool>
ManagedNetworkConfigurationHandlerImpl::FindGlobalPolicyBool(
std::string_view key) const {
const base::Value::Dict* global_network_config = GetGlobalConfigFromPolicy(
std::string() /* no username hash, device policy */);
if (!global_network_config) {
return {};
}
return global_network_config->FindBool(key);
}
const base::Value::List*
ManagedNetworkConfigurationHandlerImpl::FindGlobalPolicyList(
std::string_view key) const {
const base::Value::Dict* global_network_config = GetGlobalConfigFromPolicy(
std::string() /* no username hash, device policy */);
if (!global_network_config) {
return nullptr;
}
return global_network_config->FindList(key);
}
const std::string*
ManagedNetworkConfigurationHandlerImpl::FindGlobalPolicyString(
std::string_view key) const {
const base::Value::Dict* global_network_config = GetGlobalConfigFromPolicy(
std::string() /* no username hash, device policy */);
if (!global_network_config) {
return nullptr;
}
return global_network_config->FindString(key);
}
void ManagedNetworkConfigurationHandlerImpl::
TriggerEphemeralNetworkConfigActions() {
DCHECK(policy_util::AreEphemeralNetworkPoliciesEnabled());
PolicyApplicator::Options options;
options.reset_recommended_managed_configs = RecommendedValuesAreEphemeral();
options.remove_unmanaged_configs =
UserCreatedNetworkConfigurationsAreEphemeral();
ApplyOrQueuePolicies(
/*userhash=*/std::string(), /*modified_policies=*/{},
/*can_affect_other_networks=*/true, std::move(options));
}
} // namespace ash