// Copyright 2021 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/ash/net/rollback_network_config/rollback_onc_util.h"
#include "base/check.h"
#include "base/notreached.h"
#include "base/values.h"
#include "components/onc/onc_constants.h"
namespace ash::rollback_network_config {
namespace {
static const char* const kAugmentationKeys[] = {
onc::kAugmentationActiveSetting, onc::kAugmentationEffectiveSetting,
onc::kAugmentationUserPolicy, onc::kAugmentationDevicePolicy,
onc::kAugmentationUserSetting, onc::kAugmentationSharedSetting,
onc::kAugmentationUserEditable, onc::kAugmentationDeviceEditable,
onc::kAugmentationActiveExtension};
const base::Value::Dict* OncGetWiFi(const base::Value::Dict& network) {
const base::Value::Dict* wifi = network.FindDict(onc::network_config::kWiFi);
DCHECK(wifi);
return wifi;
}
base::Value::Dict* OncGetWiFi(base::Value::Dict* network) {
return const_cast<base::Value::Dict*>(OncGetWiFi(*network));
}
const base::Value::Dict* OncGetEthernet(const base::Value::Dict& network) {
const base::Value::Dict* ethernet =
network.FindDict(onc::network_config::kEthernet);
DCHECK(ethernet);
return ethernet;
}
const base::Value::Dict* OncGetEap(const base::Value::Dict& network) {
if (OncIsWiFi(network)) {
const base::Value::Dict* wifi = OncGetWiFi(network);
const base::Value::Dict* eap = wifi->FindDict(onc::wifi::kEAP);
DCHECK(eap);
return eap;
}
if (OncIsEthernet(network)) {
const base::Value::Dict* ethernet = OncGetEthernet(network);
const base::Value::Dict* eap = ethernet->FindDict(onc::ethernet::kEAP);
DCHECK(eap);
return eap;
}
NOTREACHED_IN_MIGRATION();
return nullptr;
}
base::Value::Dict* OncGetEap(base::Value::Dict* network) {
return const_cast<base::Value::Dict*>(OncGetEap(*network));
}
base::Value::Dict ManagedOncCreatePasswordDict(const base::Value::Dict& network,
const std::string& password) {
std::string source = onc::kAugmentationDevicePolicy;
if (OncIsSourceDevice(network)) {
source = onc::kAugmentationSharedSetting;
}
return base::Value::Dict()
.Set(onc::kAugmentationActiveSetting, password)
.Set(onc::kAugmentationEffectiveSetting, source)
.Set(source, password);
}
} // namespace
std::string GetStringValue(const base::Value::Dict& network,
const std::string& key) {
const std::string* value = network.FindString(key);
DCHECK(value);
return *value;
}
bool GetBoolValue(const base::Value::Dict& network, const std::string& key) {
std::optional<bool> value = network.FindBool(key);
DCHECK(value);
return *value;
}
void ManagedOncCollapseToActive(base::Value* network) {
DCHECK(network);
base::Value::Dict* network_dict = network->GetIfDict();
if (!network_dict) {
return;
}
// FYI: don't fail to notice the fact that this out value assigned to
// `network` might not be a dictionary. That is why the argument to this
// function is not of `base::Value::Dict` type.
base::Value* active = network_dict->Find(onc::kAugmentationActiveSetting);
if (active) {
*network = active->Clone();
return;
}
std::vector<std::string> empty_dictionaries;
for (const auto property : *network_dict) {
ManagedOncCollapseToActive(&property.second);
if (property.second.is_dict() && property.second.GetDict().empty()) {
empty_dictionaries.push_back(property.first);
}
}
for (const std::string& key : empty_dictionaries) {
network_dict->Remove(key);
}
for (const std::string& key : kAugmentationKeys) {
network_dict->Remove(key);
}
}
void ManagedOncCollapseToUiData(base::Value* network) {
DCHECK(network);
base::Value::Dict& network_dict = network->GetDict();
// FYI: don't fail to notice the fact that this out value assigned to
// `network` might not be a dictionary. That is why the argument to this
// function is not of `base::Value::Dict` type.
base::Value* shared = network_dict.Find(onc::kAugmentationSharedSetting);
if (shared) {
*network = shared->Clone();
return;
}
std::vector<std::string> to_remove;
for (const auto property : network_dict) {
if (!property.second.is_dict()) {
to_remove.push_back(property.first);
} else {
// The call below may change the type of `property.second`, that's why we
// need to check again (see above).
ManagedOncCollapseToUiData(&property.second);
base::Value::Dict* property_value_dict = property.second.GetIfDict();
if (property_value_dict && property_value_dict->empty()) {
to_remove.push_back(property.first);
}
}
}
for (const std::string& key : to_remove) {
network_dict.Remove(key);
}
}
void ManagedOncSetEapPassword(base::Value::Dict* network,
const std::string& password) {
base::Value::Dict* eap = OncGetEap(network);
eap->Set(onc::eap::kPassword,
ManagedOncCreatePasswordDict(*network, password));
}
void ManagedOncWiFiSetPskPassword(base::Value::Dict* network,
const std::string& password) {
base::Value::Dict* wifi = OncGetWiFi(network);
wifi->Set(onc::wifi::kPassphrase,
ManagedOncCreatePasswordDict(*network, password));
}
bool OncIsWiFi(const base::Value::Dict& network) {
return GetStringValue(network, onc::network_config::kType) ==
onc::network_type::kWiFi;
}
bool OncIsEthernet(const base::Value::Dict& network) {
return GetStringValue(network, onc::network_config::kType) ==
onc::network_type::kEthernet;
}
bool OncIsSourceDevicePolicy(const base::Value::Dict& network) {
return GetStringValue(network, onc::network_config::kSource) ==
onc::network_config::kSourceDevicePolicy;
}
bool OncIsSourceDevice(const base::Value::Dict& network) {
return GetStringValue(network, onc::network_config::kSource) ==
onc::network_config::kSourceDevice;
}
bool OncHasNoSecurity(const base::Value::Dict& network) {
if (OncIsWiFi(network)) {
return OncWiFiGetSecurity(network) == onc::wifi::kSecurityNone;
}
if (OncIsEthernet(network)) {
return OncEthernetGetAuthentication(network) ==
onc::ethernet::kAuthenticationNone;
}
return false;
}
bool OncIsEap(const base::Value::Dict& network) {
if (OncIsWiFi(network)) {
const std::string security_type = OncWiFiGetSecurity(network);
return security_type == onc::wifi::kWEP_8021X ||
security_type == onc::wifi::kWPA_EAP;
}
if (OncIsEthernet(network)) {
const std::string authentication_type =
OncEthernetGetAuthentication(network);
return authentication_type == onc::ethernet::k8021X;
}
return false;
}
bool OncHasEapConfiguration(const base::Value::Dict& network) {
if (OncIsWiFi(network)) {
const base::Value::Dict* wifi = OncGetWiFi(network);
return wifi->FindDict(onc::wifi::kEAP);
}
if (OncIsEthernet(network)) {
const base::Value::Dict* ethernet = OncGetEthernet(network);
return ethernet->FindDict(onc::ethernet::kEAP);
}
return false;
}
bool OncIsEapWithoutClientCertificate(const base::Value::Dict& network) {
return OncIsEap(network) && !OncEapRequiresClientCertificate(network);
}
std::string OncGetEapIdentity(const base::Value::Dict& network) {
const base::Value::Dict* eap = OncGetEap(network);
return GetStringValue(*eap, onc::eap::kIdentity);
}
std::string OncGetEapInner(const base::Value::Dict& network) {
const base::Value::Dict* eap = OncGetEap(network);
return GetStringValue(*eap, onc::eap::kInner);
}
std::string OncGetEapOuter(const base::Value::Dict& network) {
const base::Value::Dict* eap = OncGetEap(network);
return GetStringValue(*eap, onc::eap::kOuter);
}
bool OncGetEapSaveCredentials(const base::Value::Dict& network) {
const base::Value::Dict* eap = OncGetEap(network);
return GetBoolValue(*eap, onc::eap::kSaveCredentials);
}
std::string OncGetEapPassword(const base::Value::Dict& network) {
const base::Value::Dict* eap = OncGetEap(network);
return GetStringValue(*eap, onc::eap::kPassword);
}
std::string OncGetEapClientCertType(const base::Value::Dict& network) {
const base::Value::Dict* eap = OncGetEap(network);
return GetStringValue(*eap, onc::client_cert::kClientCertType);
}
std::string OncGetEapClientCertPKCS11Id(const base::Value::Dict& network) {
const base::Value::Dict* eap = OncGetEap(network);
return GetStringValue(*eap, onc::client_cert::kClientCertPKCS11Id);
}
bool OncEapRequiresClientCertificate(const base::Value::Dict& network) {
// TODO(crbug/1225560) There may be unexpected client cert fields, so we
// cannot rely on them. Simply check for EAP-TLS for now, which is the only
// type for which a user may configure a client cert in the UI.
return OncGetEapOuter(network) == onc::eap::kEAP_TLS;
}
void OncSetEapPassword(base::Value::Dict* network,
const std::string& password) {
base::Value::Dict* eap = OncGetEap(network);
eap->Set(onc::eap::kPassword, password);
}
std::string OncWiFiGetSecurity(const base::Value::Dict& network) {
const base::Value::Dict* wifi = OncGetWiFi(network);
const std::string* security_type = wifi->FindString(onc::wifi::kSecurity);
DCHECK(security_type);
return *security_type;
}
std::string OncWiFiGetPassword(const base::Value::Dict& network) {
const base::Value::Dict* wifi = OncGetWiFi(network);
const std::string* password = wifi->FindString(onc::wifi::kPassphrase);
DCHECK(password);
return *password;
}
bool OncWiFiIsPsk(const base::Value::Dict& network) {
const std::string security_type = OncWiFiGetSecurity(network);
return security_type == onc::wifi::kWEP_PSK ||
security_type == onc::wifi::kWPA_PSK ||
security_type == onc::wifi::kWPA2_PSK;
}
void OncWiFiSetPskPassword(base::Value::Dict* network,
const std::string& password) {
base::Value::Dict* wifi = OncGetWiFi(network);
wifi->Set(onc::wifi::kPassphrase, password);
}
std::string OncEthernetGetAuthentication(const base::Value::Dict& network) {
const std::string* type = network.FindDict(onc::network_config::kEthernet)
->FindString(onc::ethernet::kAuthentication);
DCHECK(type);
return *type;
}
} // namespace ash::rollback_network_config