// Copyright 2012 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/onc/onc_normalizer.h"
#include <string>
#include "base/logging.h"
#include "base/values.h"
#include "chromeos/ash/components/network/network_event_log.h"
#include "chromeos/ash/components/network/onc/network_onc_utils.h"
#include "chromeos/components/onc/onc_signature.h"
#include "chromeos/components/onc/onc_utils.h"
#include "components/onc/onc_constants.h"
namespace ash::onc {
Normalizer::Normalizer(bool remove_recommended_fields)
: remove_recommended_fields_(remove_recommended_fields) {}
Normalizer::~Normalizer() = default;
base::Value::Dict Normalizer::NormalizeObject(
const chromeos::onc::OncValueSignature* object_signature,
const base::Value::Dict& onc_object) {
CHECK(object_signature != nullptr);
bool error = false;
base::Value::Dict result = MapObject(*object_signature, onc_object, &error);
DCHECK(!error);
return result;
}
base::Value::Dict Normalizer::MapObject(
const chromeos::onc::OncValueSignature& signature,
const base::Value::Dict& onc_object,
bool* error) {
base::Value::Dict normalized =
chromeos::onc::Mapper::MapObject(signature, onc_object, error);
if (*error) {
return {};
}
if (remove_recommended_fields_)
normalized.Remove(::onc::kRecommended);
if (&signature == &chromeos::onc::kCertificateSignature)
NormalizeCertificate(&normalized);
else if (&signature == &chromeos::onc::kEAPSignature)
NormalizeEAP(&normalized);
else if (&signature == &chromeos::onc::kEthernetSignature)
NormalizeEthernet(&normalized);
else if (&signature == &chromeos::onc::kIPsecSignature)
NormalizeIPsec(&normalized);
else if (&signature == &chromeos::onc::kNetworkConfigurationSignature)
NormalizeNetworkConfiguration(&normalized);
else if (&signature == &chromeos::onc::kOpenVPNSignature)
NormalizeOpenVPN(&normalized);
else if (&signature == &chromeos::onc::kProxySettingsSignature)
NormalizeProxySettings(&normalized);
else if (&signature == &chromeos::onc::kVPNSignature)
NormalizeVPN(&normalized);
else if (&signature == &chromeos::onc::kWiFiSignature)
NormalizeWiFi(&normalized);
return normalized;
}
namespace {
void RemoveEntryUnless(base::Value::Dict* dict,
const std::string& path,
bool condition) {
if (!condition && dict->contains(path)) {
NET_LOG(ERROR) << "onc::Normalizer:Removing: " << path;
dict->Remove(path);
}
}
bool IsIpConfigTypeStatic(base::Value::Dict* network,
const std::string& ip_config_type_key) {
std::string* ip_config_type = network->FindString(ip_config_type_key);
return ip_config_type &&
(*ip_config_type) == ::onc::network_config::kIPConfigTypeStatic;
}
std::string GetString(const base::Value::Dict& dict, const char* key) {
const std::string* value = dict.FindString(key);
return value ? *value : std::string();
}
} // namespace
void Normalizer::NormalizeCertificate(base::Value::Dict* cert) {
std::string type = GetString(*cert, ::onc::certificate::kType);
RemoveEntryUnless(cert, ::onc::certificate::kPKCS12,
type == ::onc::certificate::kClient);
RemoveEntryUnless(cert, ::onc::certificate::kTrustBits,
type == ::onc::certificate::kServer ||
type == ::onc::certificate::kAuthority);
RemoveEntryUnless(cert, ::onc::certificate::kX509,
type == ::onc::certificate::kServer ||
type == ::onc::certificate::kAuthority);
}
void Normalizer::NormalizeEthernet(base::Value::Dict* ethernet) {
std::string auth = GetString(*ethernet, ::onc::ethernet::kAuthentication);
RemoveEntryUnless(ethernet, ::onc::ethernet::kEAP,
auth == ::onc::ethernet::k8021X);
}
void Normalizer::NormalizeEAP(base::Value::Dict* eap) {
std::string clientcert_type =
GetString(*eap, ::onc::client_cert::kClientCertType);
RemoveEntryUnless(eap, ::onc::client_cert::kClientCertPattern,
clientcert_type == ::onc::client_cert::kPattern);
RemoveEntryUnless(eap, ::onc::client_cert::kClientCertRef,
clientcert_type == ::onc::client_cert::kRef);
RemoveEntryUnless(
eap, ::onc::client_cert::kClientCertProvisioningProfileId,
clientcert_type == ::onc::client_cert::kProvisioningProfileId);
std::string outer = GetString(*eap, ::onc::eap::kOuter);
RemoveEntryUnless(
eap, ::onc::eap::kAnonymousIdentity,
outer == ::onc::eap::kPEAP || outer == ::onc::eap::kEAP_TTLS);
RemoveEntryUnless(eap, ::onc::eap::kInner,
outer == ::onc::eap::kPEAP ||
outer == ::onc::eap::kEAP_TTLS ||
outer == ::onc::eap::kEAP_FAST);
}
void Normalizer::NormalizeIPsec(base::Value::Dict* ipsec) {
std::string auth_type = GetString(*ipsec, ::onc::ipsec::kAuthenticationType);
RemoveEntryUnless(ipsec, ::onc::client_cert::kClientCertType,
auth_type == ::onc::ipsec::kCert);
RemoveEntryUnless(ipsec, ::onc::ipsec::kServerCARef,
auth_type == ::onc::ipsec::kCert);
RemoveEntryUnless(ipsec, ::onc::ipsec::kPSK, auth_type == ::onc::ipsec::kPSK);
RemoveEntryUnless(ipsec, ::onc::vpn::kSaveCredentials,
auth_type == ::onc::ipsec::kPSK);
std::string clientcert_type =
GetString(*ipsec, ::onc::client_cert::kClientCertType);
RemoveEntryUnless(ipsec, ::onc::client_cert::kClientCertPattern,
clientcert_type == ::onc::client_cert::kPattern);
RemoveEntryUnless(ipsec, ::onc::client_cert::kClientCertRef,
clientcert_type == ::onc::client_cert::kRef);
RemoveEntryUnless(
ipsec, ::onc::client_cert::kClientCertProvisioningProfileId,
clientcert_type == ::onc::client_cert::kProvisioningProfileId);
int ike_version = ipsec->FindInt(::onc::ipsec::kIKEVersion).value_or(-1);
RemoveEntryUnless(ipsec, ::onc::ipsec::kEAP, ike_version == 2);
RemoveEntryUnless(ipsec, ::onc::ipsec::kGroup, ike_version == 1);
RemoveEntryUnless(ipsec, ::onc::ipsec::kXAUTH, ike_version == 1);
}
void Normalizer::NormalizeNetworkConfiguration(base::Value::Dict* network) {
bool remove = network->FindBool(::onc::kRemove).value_or(false);
if (remove) {
network->Remove(::onc::network_config::kStaticIPConfig);
network->Remove(::onc::network_config::kName);
network->Remove(::onc::network_config::kProxySettings);
network->Remove(::onc::network_config::kType);
// Fields dependent on kType are removed afterwards, too.
}
std::string type = GetString(*network, ::onc::network_config::kType);
RemoveEntryUnless(network, ::onc::network_config::kEthernet,
type == ::onc::network_type::kEthernet);
RemoveEntryUnless(network, ::onc::network_config::kVPN,
type == ::onc::network_type::kVPN);
RemoveEntryUnless(network, ::onc::network_config::kWiFi,
type == ::onc::network_type::kWiFi);
NormalizeStaticIPConfigForNetwork(network);
}
void Normalizer::NormalizeOpenVPN(base::Value::Dict* openvpn) {
std::string clientcert_type =
GetString(*openvpn, ::onc::client_cert::kClientCertType);
RemoveEntryUnless(openvpn, ::onc::client_cert::kClientCertPattern,
clientcert_type == ::onc::client_cert::kPattern);
RemoveEntryUnless(openvpn, ::onc::client_cert::kClientCertRef,
clientcert_type == ::onc::client_cert::kRef);
RemoveEntryUnless(
openvpn, ::onc::client_cert::kClientCertProvisioningProfileId,
clientcert_type == ::onc::client_cert::kProvisioningProfileId);
const std::string* user_auth_type =
openvpn->FindString(::onc::openvpn::kUserAuthenticationType);
// If UserAuthenticationType is unspecified, do not strip Password and OTP.
if (user_auth_type) {
RemoveEntryUnless(
openvpn, ::onc::openvpn::kPassword,
*user_auth_type == ::onc::openvpn_user_auth_type::kPassword ||
*user_auth_type == ::onc::openvpn_user_auth_type::kPasswordAndOTP);
RemoveEntryUnless(
openvpn, ::onc::openvpn::kOTP,
*user_auth_type == ::onc::openvpn_user_auth_type::kOTP ||
*user_auth_type == ::onc::openvpn_user_auth_type::kPasswordAndOTP);
}
const std::string* compression_algorithm =
openvpn->FindString(::onc::openvpn::kCompressionAlgorithm);
if (compression_algorithm) {
RemoveEntryUnless(
openvpn, ::onc::openvpn::kCompressionAlgorithm,
*compression_algorithm != ::onc::openvpn_compression_algorithm::kNone);
}
}
void Normalizer::NormalizeProxySettings(base::Value::Dict* proxy) {
std::string type = GetString(*proxy, ::onc::proxy::kType);
RemoveEntryUnless(proxy, ::onc::proxy::kManual,
type == ::onc::proxy::kManual);
RemoveEntryUnless(proxy, ::onc::proxy::kExcludeDomains,
type == ::onc::proxy::kManual);
RemoveEntryUnless(proxy, ::onc::proxy::kPAC, type == ::onc::proxy::kPAC);
}
void Normalizer::NormalizeVPN(base::Value::Dict* vpn) {
std::string type = GetString(*vpn, ::onc::vpn::kType);
RemoveEntryUnless(vpn, ::onc::vpn::kOpenVPN, type == ::onc::vpn::kOpenVPN);
RemoveEntryUnless(vpn, ::onc::vpn::kWireGuard,
type == ::onc::vpn::kWireGuard);
RemoveEntryUnless(
vpn, ::onc::vpn::kIPsec,
type == ::onc::vpn::kIPsec || type == ::onc::vpn::kTypeL2TP_IPsec);
RemoveEntryUnless(vpn, ::onc::vpn::kL2TP,
type == ::onc::vpn::kTypeL2TP_IPsec);
RemoveEntryUnless(vpn, ::onc::vpn::kThirdPartyVpn,
type == ::onc::vpn::kThirdPartyVpn);
RemoveEntryUnless(vpn, ::onc::vpn::kArcVpn, type == ::onc::vpn::kArcVpn);
}
void Normalizer::NormalizeWiFi(base::Value::Dict* wifi) {
std::string security = GetString(*wifi, ::onc::wifi::kSecurity);
RemoveEntryUnless(
wifi, ::onc::wifi::kEAP,
security == ::onc::wifi::kWEP_8021X || security == ::onc::wifi::kWPA_EAP);
RemoveEntryUnless(
wifi, ::onc::wifi::kPassphrase,
security == ::onc::wifi::kWEP_PSK || security == ::onc::wifi::kWPA_PSK);
chromeos::onc::FillInHexSSIDField(*wifi);
}
void Normalizer::NormalizeStaticIPConfigForNetwork(base::Value::Dict* network) {
const bool ip_config_type_is_static = IsIpConfigTypeStatic(
network, ::onc::network_config::kIPAddressConfigType);
const bool name_servers_type_is_static = IsIpConfigTypeStatic(
network, ::onc::network_config::kNameServersConfigType);
base::Value::Dict* static_ip_config =
network->FindDict(::onc::network_config::kStaticIPConfig);
bool all_ip_fields_exist = false;
bool name_servers_exist = false;
if (static_ip_config) {
all_ip_fields_exist =
static_ip_config->contains(::onc::ipconfig::kIPAddress) &&
static_ip_config->contains(::onc::ipconfig::kGateway) &&
static_ip_config->contains(::onc::ipconfig::kRoutingPrefix);
name_servers_exist =
static_ip_config->contains(::onc::ipconfig::kNameServers);
RemoveEntryUnless(static_ip_config, ::onc::ipconfig::kIPAddress,
all_ip_fields_exist && ip_config_type_is_static);
RemoveEntryUnless(static_ip_config, ::onc::ipconfig::kGateway,
all_ip_fields_exist && ip_config_type_is_static);
RemoveEntryUnless(static_ip_config, ::onc::ipconfig::kRoutingPrefix,
all_ip_fields_exist && ip_config_type_is_static);
RemoveEntryUnless(static_ip_config, ::onc::ipconfig::kNameServers,
name_servers_type_is_static);
RemoveEntryUnless(network, ::onc::network_config::kStaticIPConfig,
!static_ip_config->empty());
}
RemoveEntryUnless(network, ::onc::network_config::kIPAddressConfigType,
!ip_config_type_is_static || all_ip_fields_exist);
RemoveEntryUnless(network, ::onc::network_config::kNameServersConfigType,
!name_servers_type_is_static || name_servers_exist);
}
} // namespace ash::onc