blob: b3a25b79f03d6dc558f8496142793995e7eff931 [file] [log] [blame]
// Copyright (c) 2012 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/onc/onc_validator.h"
#include <stddef.h>
#include <stdint.h>
#include <algorithm>
#include "base/json/json_writer.h"
#include "base/logging.h"
#include "base/memory/ptr_util.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_util.h"
#include "base/values.h"
#include "chromeos/network/onc/onc_signature.h"
namespace chromeos {
namespace onc {
namespace {
// According to the IEEE 802.11 standard the SSID is a series of 0 to 32 octets.
const int kMaximumSSIDLengthInBytes = 32;
template <typename T, size_t N>
std::vector<T> toVector(T const (&array)[N]) {
return std::vector<T>(array, array + N);
}
// Copied from policy/configuration_policy_handler.cc.
// TODO(pneubeck): move to a common place like base/.
std::string ValueTypeToString(base::Value::Type type) {
const char* const strings[] = {"null", "boolean", "integer", "double",
"string", "binary", "dictionary", "list"};
CHECK(static_cast<size_t>(type) < arraysize(strings));
return strings[type];
}
} // namespace
Validator::Validator(bool error_on_unknown_field,
bool error_on_wrong_recommended,
bool error_on_missing_field,
bool managed_onc)
: error_on_unknown_field_(error_on_unknown_field),
error_on_wrong_recommended_(error_on_wrong_recommended),
error_on_missing_field_(error_on_missing_field),
managed_onc_(managed_onc),
onc_source_(::onc::ONC_SOURCE_NONE) {}
Validator::~Validator() {}
std::unique_ptr<base::DictionaryValue> Validator::ValidateAndRepairObject(
const OncValueSignature* object_signature,
const base::DictionaryValue& onc_object,
Result* result) {
CHECK(object_signature);
*result = VALID;
error_or_warning_found_ = false;
bool error = false;
std::unique_ptr<base::Value> result_value =
MapValue(*object_signature, onc_object, &error);
if (error) {
*result = INVALID;
result_value.reset();
} else if (error_or_warning_found_) {
*result = VALID_WITH_WARNINGS;
}
// The return value should be NULL if, and only if, |result| equals INVALID.
DCHECK_EQ(result_value.get() == NULL, *result == INVALID);
base::DictionaryValue* result_dict = NULL;
if (result_value) {
result_value.release()->GetAsDictionary(&result_dict);
CHECK(result_dict);
}
return base::WrapUnique(result_dict);
}
std::unique_ptr<base::Value> Validator::MapValue(
const OncValueSignature& signature,
const base::Value& onc_value,
bool* error) {
if (onc_value.GetType() != signature.onc_type) {
LOG(ERROR) << MessageHeader() << "Found value '" << onc_value
<< "' of type '" << ValueTypeToString(onc_value.GetType())
<< "', but type '" << ValueTypeToString(signature.onc_type)
<< "' is required.";
error_or_warning_found_ = *error = true;
return std::unique_ptr<base::Value>();
}
std::unique_ptr<base::Value> repaired =
Mapper::MapValue(signature, onc_value, error);
if (repaired)
CHECK_EQ(repaired->GetType(), signature.onc_type);
return repaired;
}
std::unique_ptr<base::DictionaryValue> Validator::MapObject(
const OncValueSignature& signature,
const base::DictionaryValue& onc_object,
bool* error) {
std::unique_ptr<base::DictionaryValue> repaired(new base::DictionaryValue);
bool valid = ValidateObjectDefault(signature, onc_object, repaired.get());
if (valid) {
if (&signature == &kToplevelConfigurationSignature) {
valid = ValidateToplevelConfiguration(repaired.get());
} else if (&signature == &kNetworkConfigurationSignature) {
valid = ValidateNetworkConfiguration(repaired.get());
} else if (&signature == &kEthernetSignature) {
valid = ValidateEthernet(repaired.get());
} else if (&signature == &kIPConfigSignature ||
&signature == &kSavedIPConfigSignature ||
&signature == &kStaticIPConfigSignature) {
valid = ValidateIPConfig(repaired.get());
} else if (&signature == &kWiFiSignature) {
valid = ValidateWiFi(repaired.get());
} else if (&signature == &kVPNSignature) {
valid = ValidateVPN(repaired.get());
} else if (&signature == &kIPsecSignature) {
valid = ValidateIPsec(repaired.get());
} else if (&signature == &kOpenVPNSignature) {
valid = ValidateOpenVPN(repaired.get());
} else if (&signature == &kThirdPartyVPNSignature) {
valid = ValidateThirdPartyVPN(repaired.get());
} else if (&signature == &kVerifyX509Signature) {
valid = ValidateVerifyX509(repaired.get());
} else if (&signature == &kCertificatePatternSignature) {
valid = ValidateCertificatePattern(repaired.get());
} else if (&signature == &kGlobalNetworkConfigurationSignature) {
valid = ValidateGlobalNetworkConfiguration(repaired.get());
} else if (&signature == &kProxySettingsSignature) {
valid = ValidateProxySettings(repaired.get());
} else if (&signature == &kProxyLocationSignature) {
valid = ValidateProxyLocation(repaired.get());
} else if (&signature == &kEAPSignature) {
valid = ValidateEAP(repaired.get());
} else if (&signature == &kCertificateSignature) {
valid = ValidateCertificate(repaired.get());
}
}
if (valid) {
return repaired;
} else {
DCHECK(error_or_warning_found_);
error_or_warning_found_ = *error = true;
return std::unique_ptr<base::DictionaryValue>();
}
}
std::unique_ptr<base::Value> Validator::MapField(
const std::string& field_name,
const OncValueSignature& object_signature,
const base::Value& onc_value,
bool* found_unknown_field,
bool* error) {
path_.push_back(field_name);
bool current_field_unknown = false;
std::unique_ptr<base::Value> result = Mapper::MapField(
field_name, object_signature, onc_value, &current_field_unknown, error);
DCHECK_EQ(field_name, path_.back());
path_.pop_back();
if (current_field_unknown) {
error_or_warning_found_ = *found_unknown_field = true;
std::string message = MessageHeader() + "Field name '" + field_name +
"' is unknown.";
if (error_on_unknown_field_)
LOG(ERROR) << message;
else
LOG(WARNING) << message;
}
return result;
}
std::unique_ptr<base::ListValue> Validator::MapArray(
const OncValueSignature& array_signature,
const base::ListValue& onc_array,
bool* nested_error) {
bool nested_error_in_current_array = false;
std::unique_ptr<base::ListValue> result = Mapper::MapArray(
array_signature, onc_array, &nested_error_in_current_array);
// Drop individual networks and certificates instead of rejecting all of
// the configuration.
if (nested_error_in_current_array &&
&array_signature != &kNetworkConfigurationListSignature &&
&array_signature != &kCertificateListSignature) {
*nested_error = nested_error_in_current_array;
}
return result;
}
std::unique_ptr<base::Value> Validator::MapEntry(
int index,
const OncValueSignature& signature,
const base::Value& onc_value,
bool* error) {
std::string str = base::IntToString(index);
path_.push_back(str);
std::unique_ptr<base::Value> result =
Mapper::MapEntry(index, signature, onc_value, error);
DCHECK_EQ(str, path_.back());
path_.pop_back();
return result;
}
bool Validator::ValidateObjectDefault(const OncValueSignature& signature,
const base::DictionaryValue& onc_object,
base::DictionaryValue* result) {
bool found_unknown_field = false;
bool nested_error_occured = false;
MapFields(signature, onc_object, &found_unknown_field, &nested_error_occured,
result);
if (found_unknown_field && error_on_unknown_field_) {
DVLOG(1) << "Unknown field names are errors: Aborting.";
return false;
}
if (nested_error_occured)
return false;
return ValidateRecommendedField(signature, result);
}
bool Validator::ValidateRecommendedField(
const OncValueSignature& object_signature,
base::DictionaryValue* result) {
CHECK(result);
std::unique_ptr<base::Value> recommended_value;
// This remove passes ownership to |recommended_value|.
if (!result->RemoveWithoutPathExpansion(::onc::kRecommended,
&recommended_value)) {
return true;
}
base::ListValue* recommended_list = nullptr;
recommended_value->GetAsList(&recommended_list);
DCHECK(recommended_list); // The types of field values are already verified.
if (!managed_onc_) {
error_or_warning_found_ = true;
LOG(WARNING) << MessageHeader() << "Found the field '"
<< ::onc::kRecommended
<< "' in an unmanaged ONC. Removing it.";
return true;
}
std::unique_ptr<base::ListValue> repaired_recommended(new base::ListValue);
for (const base::Value* entry : *recommended_list) {
std::string field_name;
if (!entry->GetAsString(&field_name)) {
NOTREACHED(); // The types of field values are already verified.
continue;
}
const OncFieldSignature* field_signature =
GetFieldSignature(object_signature, field_name);
bool found_error = false;
std::string error_cause;
if (!field_signature) {
found_error = true;
error_cause = "unknown";
} else if (field_signature->value_signature->onc_type ==
base::Value::TYPE_DICTIONARY) {
found_error = true;
error_cause = "dictionary-typed";
}
if (found_error) {
error_or_warning_found_ = true;
path_.push_back(::onc::kRecommended);
std::string message = MessageHeader() + "The " + error_cause +
" field '" + field_name + "' cannot be recommended.";
path_.pop_back();
if (error_on_wrong_recommended_) {
LOG(ERROR) << message;
return false;
} else {
LOG(WARNING) << message;
continue;
}
}
repaired_recommended->AppendString(field_name);
}
result->Set(::onc::kRecommended, repaired_recommended.release());
return true;
}
bool Validator::ValidateClientCertFields(bool allow_cert_type_none,
base::DictionaryValue* result) {
using namespace ::onc::client_cert;
const char* const kValidCertTypes[] = {kRef, kPattern};
std::vector<const char*> valid_cert_types(toVector(kValidCertTypes));
if (allow_cert_type_none)
valid_cert_types.push_back(kClientCertTypeNone);
if (FieldExistsAndHasNoValidValue(*result, kClientCertType, valid_cert_types))
return false;
std::string cert_type;
result->GetStringWithoutPathExpansion(kClientCertType, &cert_type);
if (IsCertPatternInDevicePolicy(cert_type))
return false;
bool all_required_exist = true;
if (cert_type == kPattern)
all_required_exist &= RequireField(*result, kClientCertPattern);
else if (cert_type == kRef)
all_required_exist &= RequireField(*result, kClientCertRef);
return !error_on_missing_field_ || all_required_exist;
}
namespace {
std::string JoinStringRange(const std::vector<const char*>& strings,
const std::string& separator) {
std::vector<std::string> string_vector;
std::copy(strings.begin(), strings.end(), std::back_inserter(string_vector));
return base::JoinString(string_vector, separator);
}
} // namespace
bool Validator::IsValidValue(const std::string& field_value,
const std::vector<const char*>& valid_values) {
for (const char* it : valid_values) {
if (field_value == it)
return true;
}
error_or_warning_found_ = true;
const std::string valid_values_str =
"[" + JoinStringRange(valid_values, ", ") + "]";
LOG(ERROR) << MessageHeader() << "Found value '" << field_value
<< "', but expected one of the values " << valid_values_str;
return false;
}
bool Validator::FieldExistsAndHasNoValidValue(
const base::DictionaryValue& object,
const std::string& field_name,
const std::vector<const char*>& valid_values) {
std::string actual_value;
if (!object.GetStringWithoutPathExpansion(field_name, &actual_value))
return false;
path_.push_back(field_name);
const bool valid = IsValidValue(actual_value, valid_values);
path_.pop_back();
return !valid;
}
bool Validator::FieldExistsAndIsNotInRange(const base::DictionaryValue& object,
const std::string& field_name,
int lower_bound,
int upper_bound) {
int actual_value;
if (!object.GetIntegerWithoutPathExpansion(field_name, &actual_value) ||
(lower_bound <= actual_value && actual_value <= upper_bound)) {
return false;
}
error_or_warning_found_ = true;
path_.push_back(field_name);
LOG(ERROR) << MessageHeader() << "Found value '" << actual_value
<< "', but expected a value in the range [" << lower_bound
<< ", " << upper_bound << "] (boundaries inclusive)";
path_.pop_back();
return true;
}
bool Validator::FieldExistsAndIsEmpty(const base::DictionaryValue& object,
const std::string& field_name) {
const base::Value* value = NULL;
if (!object.GetWithoutPathExpansion(field_name, &value))
return false;
std::string str;
const base::ListValue* list = NULL;
if (value->GetAsString(&str)) {
if (!str.empty())
return false;
} else if (value->GetAsList(&list)) {
if (!list->empty())
return false;
} else {
NOTREACHED();
return false;
}
error_or_warning_found_ = true;
path_.push_back(field_name);
LOG(ERROR) << MessageHeader() << "Found an empty string, but expected a "
<< "non-empty string.";
path_.pop_back();
return true;
}
bool Validator::ListFieldContainsValidValues(
const base::DictionaryValue& object,
const std::string& field_name,
const std::vector<const char*>& valid_values) {
const base::ListValue* list = NULL;
if (object.GetListWithoutPathExpansion(field_name, &list)) {
path_.push_back(field_name);
for (const base::Value* entry : *list) {
std::string value;
if (!entry->GetAsString(&value)) {
NOTREACHED(); // The types of field values are already verified.
continue;
}
if (!IsValidValue(value, valid_values)) {
path_.pop_back();
return false;
}
}
path_.pop_back();
}
return true;
}
bool Validator::ValidateSSIDAndHexSSID(base::DictionaryValue* object) {
// Check SSID validity.
std::string ssid_string;
if (object->GetStringWithoutPathExpansion(::onc::wifi::kSSID, &ssid_string) &&
(ssid_string.size() <= 0 ||
ssid_string.size() > kMaximumSSIDLengthInBytes)) {
error_or_warning_found_ = true;
const std::string msg =
MessageHeader() + ::onc::wifi::kSSID + " has an invalid length.";
// If the HexSSID field is present, ignore errors in SSID because these
// might be caused by the usage of a non-UTF-8 encoding when the SSID
// field was automatically added (see FillInHexSSIDField).
if (object->HasKey(::onc::wifi::kHexSSID)) {
LOG(WARNING) << msg;
} else {
LOG(ERROR) << msg;
return false;
}
}
// Check HexSSID validity.
std::string hex_ssid_string;
if (object->GetStringWithoutPathExpansion(::onc::wifi::kHexSSID,
&hex_ssid_string)) {
std::vector<uint8_t> decoded_ssid;
if (!base::HexStringToBytes(hex_ssid_string, &decoded_ssid)) {
LOG(ERROR) << MessageHeader() << "Field " << ::onc::wifi::kHexSSID
<< " is not a valid hex representation: \"" << hex_ssid_string
<< "\"";
error_or_warning_found_ = true;
return false;
}
if (decoded_ssid.size() <= 0 ||
decoded_ssid.size() > kMaximumSSIDLengthInBytes) {
LOG(ERROR) << MessageHeader() << ::onc::wifi::kHexSSID
<< " has an invalid length.";
error_or_warning_found_ = true;
return false;
}
// If both SSID and HexSSID are set, check whether they are consistent, i.e.
// HexSSID contains the UTF-8 encoding of SSID. If not, remove the SSID
// field.
if (ssid_string.length() > 0) {
std::string decoded_ssid_string(
reinterpret_cast<const char*>(&decoded_ssid[0]), decoded_ssid.size());
if (ssid_string != decoded_ssid_string) {
LOG(WARNING) << MessageHeader() << "Fields " << ::onc::wifi::kSSID
<< " and " << ::onc::wifi::kHexSSID
<< " contain inconsistent values. Removing "
<< ::onc::wifi::kSSID << ".";
error_or_warning_found_ = true;
object->RemoveWithoutPathExpansion(::onc::wifi::kSSID, nullptr);
}
}
}
return true;
}
bool Validator::RequireField(const base::DictionaryValue& dict,
const std::string& field_name) {
if (dict.HasKey(field_name))
return true;
std::string message = MessageHeader() + "The required field '" + field_name +
"' is missing.";
if (error_on_missing_field_) {
error_or_warning_found_ = true;
LOG(ERROR) << message;
} else {
VLOG(1) << message;
}
return false;
}
bool Validator::CheckGuidIsUniqueAndAddToSet(const base::DictionaryValue& dict,
const std::string& key_guid,
std::set<std::string> *guids) {
std::string guid;
if (dict.GetStringWithoutPathExpansion(key_guid, &guid)) {
if (guids->count(guid) != 0) {
error_or_warning_found_ = true;
LOG(ERROR) << MessageHeader() << "Found a duplicate GUID " << guid << ".";
return false;
}
guids->insert(guid);
}
return true;
}
bool Validator::IsCertPatternInDevicePolicy(const std::string& cert_type) {
if (cert_type == ::onc::client_cert::kPattern &&
onc_source_ == ::onc::ONC_SOURCE_DEVICE_POLICY) {
error_or_warning_found_ = true;
LOG(ERROR) << MessageHeader() << "Client certificate patterns are "
<< "prohibited in ONC device policies.";
return true;
}
return false;
}
bool Validator::IsGlobalNetworkConfigInUserImport(
const base::DictionaryValue& onc_object) {
if (onc_source_ == ::onc::ONC_SOURCE_USER_IMPORT &&
onc_object.HasKey(::onc::toplevel_config::kGlobalNetworkConfiguration)) {
error_or_warning_found_ = true;
LOG(ERROR) << MessageHeader() << "GlobalNetworkConfiguration is prohibited "
<< "in ONC user imports";
return true;
}
return false;
}
bool Validator::ValidateToplevelConfiguration(base::DictionaryValue* result) {
using namespace ::onc::toplevel_config;
const char* const kValidTypes[] = {kUnencryptedConfiguration,
kEncryptedConfiguration};
const std::vector<const char*> valid_types(toVector(kValidTypes));
if (FieldExistsAndHasNoValidValue(*result, kType, valid_types))
return false;
if (IsGlobalNetworkConfigInUserImport(*result))
return false;
return true;
}
bool Validator::ValidateNetworkConfiguration(base::DictionaryValue* result) {
using namespace ::onc::network_config;
const char* const kValidTypes[] = {::onc::network_type::kEthernet,
::onc::network_type::kVPN,
::onc::network_type::kWiFi,
::onc::network_type::kCellular,
::onc::network_type::kWimax};
const std::vector<const char*> valid_types(toVector(kValidTypes));
const char* const kValidIPConfigTypes[] = {kIPConfigTypeDHCP,
kIPConfigTypeStatic};
const std::vector<const char*> valid_ipconfig_types(
toVector(kValidIPConfigTypes));
if (FieldExistsAndHasNoValidValue(*result, kType, valid_types) ||
FieldExistsAndHasNoValidValue(*result, kIPAddressConfigType,
valid_ipconfig_types) ||
FieldExistsAndHasNoValidValue(*result, kNameServersConfigType,
valid_ipconfig_types) ||
FieldExistsAndIsEmpty(*result, kGUID)) {
return false;
}
if (!CheckGuidIsUniqueAndAddToSet(*result, kGUID, &network_guids_))
return false;
bool all_required_exist = RequireField(*result, kGUID);
bool remove = false;
result->GetBooleanWithoutPathExpansion(::onc::kRemove, &remove);
if (!remove) {
all_required_exist &=
RequireField(*result, kName) && RequireField(*result, kType);
std::string ip_address_config_type, name_servers_config_type;
result->GetStringWithoutPathExpansion(kIPAddressConfigType,
&ip_address_config_type);
result->GetStringWithoutPathExpansion(kNameServersConfigType,
&name_servers_config_type);
if (ip_address_config_type == kIPConfigTypeStatic ||
name_servers_config_type == kIPConfigTypeStatic) {
// TODO(pneubeck): Add ValidateStaticIPConfig and confirm that the
// correct properties are provided based on the config type.
all_required_exist &= RequireField(*result, kStaticIPConfig);
}
std::string type;
result->GetStringWithoutPathExpansion(kType, &type);
// Prohibit anything but WiFi and Ethernet for device-level policy (which
// corresponds to shared networks). See also http://crosbug.com/28741.
if (onc_source_ == ::onc::ONC_SOURCE_DEVICE_POLICY && !type.empty() &&
type != ::onc::network_type::kWiFi &&
type != ::onc::network_type::kEthernet) {
error_or_warning_found_ = true;
LOG(ERROR) << MessageHeader() << "Networks of type '"
<< type << "' are prohibited in ONC device policies.";
return false;
}
if (type == ::onc::network_type::kWiFi) {
all_required_exist &= RequireField(*result, ::onc::network_config::kWiFi);
} else if (type == ::onc::network_type::kEthernet) {
all_required_exist &=
RequireField(*result, ::onc::network_config::kEthernet);
} else if (type == ::onc::network_type::kCellular) {
all_required_exist &=
RequireField(*result, ::onc::network_config::kCellular);
} else if (type == ::onc::network_type::kWimax) {
all_required_exist &=
RequireField(*result, ::onc::network_config::kWimax);
} else if (type == ::onc::network_type::kVPN) {
all_required_exist &= RequireField(*result, ::onc::network_config::kVPN);
}
}
return !error_on_missing_field_ || all_required_exist;
}
bool Validator::ValidateEthernet(base::DictionaryValue* result) {
using namespace ::onc::ethernet;
const char* const kValidAuthentications[] = {kAuthenticationNone, k8021X};
const std::vector<const char*> valid_authentications(
toVector(kValidAuthentications));
if (FieldExistsAndHasNoValidValue(
*result, kAuthentication, valid_authentications)) {
return false;
}
bool all_required_exist = true;
std::string auth;
result->GetStringWithoutPathExpansion(kAuthentication, &auth);
if (auth == k8021X)
all_required_exist &= RequireField(*result, kEAP);
return !error_on_missing_field_ || all_required_exist;
}
bool Validator::ValidateIPConfig(base::DictionaryValue* result) {
using namespace ::onc::ipconfig;
const char* const kValidTypes[] = {kIPv4, kIPv6};
const std::vector<const char*> valid_types(toVector(kValidTypes));
if (FieldExistsAndHasNoValidValue(
*result, ::onc::ipconfig::kType, valid_types))
return false;
std::string type;
result->GetStringWithoutPathExpansion(::onc::ipconfig::kType, &type);
int lower_bound = 1;
// In case of missing type, choose higher upper_bound.
int upper_bound = (type == kIPv4) ? 32 : 128;
if (FieldExistsAndIsNotInRange(
*result, kRoutingPrefix, lower_bound, upper_bound)) {
return false;
}
bool all_required_exist = RequireField(*result, kIPAddress) &&
RequireField(*result, ::onc::ipconfig::kType);
if (result->HasKey(kIPAddress))
all_required_exist &= RequireField(*result, kRoutingPrefix);
return !error_on_missing_field_ || all_required_exist;
}
bool Validator::ValidateWiFi(base::DictionaryValue* result) {
using namespace ::onc::wifi;
const char* const kValidSecurities[] = {kSecurityNone, kWEP_PSK, kWEP_8021X,
kWPA_PSK, kWPA_EAP};
const std::vector<const char*> valid_securities(toVector(kValidSecurities));
if (FieldExistsAndHasNoValidValue(*result, kSecurity, valid_securities))
return false;
if (!ValidateSSIDAndHexSSID(result))
return false;
bool all_required_exist = RequireField(*result, kSecurity);
// One of {kSSID, kHexSSID} must be present.
if (!result->HasKey(kSSID))
all_required_exist &= RequireField(*result, kHexSSID);
if (!result->HasKey(kHexSSID))
all_required_exist &= RequireField(*result, kSSID);
std::string security;
result->GetStringWithoutPathExpansion(kSecurity, &security);
if (security == kWEP_8021X || security == kWPA_EAP)
all_required_exist &= RequireField(*result, kEAP);
else if (security == kWEP_PSK || security == kWPA_PSK)
all_required_exist &= RequireField(*result, kPassphrase);
return !error_on_missing_field_ || all_required_exist;
}
bool Validator::ValidateVPN(base::DictionaryValue* result) {
using namespace ::onc::vpn;
const char* const kValidTypes[] = {
kIPsec, kTypeL2TP_IPsec, kOpenVPN, kThirdPartyVpn};
const std::vector<const char*> valid_types(toVector(kValidTypes));
if (FieldExistsAndHasNoValidValue(*result, ::onc::vpn::kType, valid_types))
return false;
bool all_required_exist = RequireField(*result, ::onc::vpn::kType);
std::string type;
result->GetStringWithoutPathExpansion(::onc::vpn::kType, &type);
if (type == kOpenVPN) {
all_required_exist &= RequireField(*result, kOpenVPN);
} else if (type == kIPsec) {
all_required_exist &= RequireField(*result, kIPsec);
} else if (type == kTypeL2TP_IPsec) {
all_required_exist &=
RequireField(*result, kIPsec) && RequireField(*result, kL2TP);
} else if (type == kThirdPartyVpn) {
all_required_exist &= RequireField(*result, kThirdPartyVpn);
}
return !error_on_missing_field_ || all_required_exist;
}
bool Validator::ValidateIPsec(base::DictionaryValue* result) {
using namespace ::onc::ipsec;
const char* const kValidAuthentications[] = {kPSK, kCert};
const std::vector<const char*> valid_authentications(
toVector(kValidAuthentications));
if (FieldExistsAndHasNoValidValue(
*result, kAuthenticationType, valid_authentications) ||
FieldExistsAndIsEmpty(*result, kServerCARefs)) {
return false;
}
if (result->HasKey(kServerCARefs) && result->HasKey(kServerCARef)) {
error_or_warning_found_ = true;
LOG(ERROR) << MessageHeader() << "At most one of " << kServerCARefs
<< " and " << kServerCARef << " can be set.";
return false;
}
if (!ValidateClientCertFields(false, // don't allow ClientCertType None
result)) {
return false;
}
bool all_required_exist = RequireField(*result, kAuthenticationType) &&
RequireField(*result, kIKEVersion);
std::string auth;
result->GetStringWithoutPathExpansion(kAuthenticationType, &auth);
bool has_server_ca_cert =
result->HasKey(kServerCARefs) || result->HasKey(kServerCARef);
if (auth == kCert) {
all_required_exist &=
RequireField(*result, ::onc::client_cert::kClientCertType);
if (!has_server_ca_cert) {
all_required_exist = false;
error_or_warning_found_ = true;
std::string message = MessageHeader() + "The required field '" +
kServerCARefs + "' is missing.";
if (error_on_missing_field_)
LOG(ERROR) << message;
else
LOG(WARNING) << message;
}
} else if (has_server_ca_cert) {
error_or_warning_found_ = true;
LOG(ERROR) << MessageHeader() << kServerCARefs << " (or " << kServerCARef
<< ") can only be set if " << kAuthenticationType
<< " is set to " << kCert << ".";
return false;
}
return !error_on_missing_field_ || all_required_exist;
}
bool Validator::ValidateOpenVPN(base::DictionaryValue* result) {
using namespace ::onc::openvpn;
const char* const kValidAuthRetryValues[] = {::onc::openvpn::kNone, kInteract,
kNoInteract};
const std::vector<const char*> valid_auth_retry_values(
toVector(kValidAuthRetryValues));
const char* const kValidCertTlsValues[] = {::onc::openvpn::kNone,
::onc::openvpn::kServer};
const std::vector<const char*> valid_cert_tls_values(
toVector(kValidCertTlsValues));
const char* const kValidUserAuthTypes[] = {
::onc::openvpn_user_auth_type::kNone,
::onc::openvpn_user_auth_type::kOTP,
::onc::openvpn_user_auth_type::kPassword,
::onc::openvpn_user_auth_type::kPasswordAndOTP};
const std::vector<const char*> valid_user_auth_types(
toVector(kValidUserAuthTypes));
if (FieldExistsAndHasNoValidValue(
*result, kAuthRetry, valid_auth_retry_values) ||
FieldExistsAndHasNoValidValue(
*result, kRemoteCertTLS, valid_cert_tls_values) ||
FieldExistsAndHasNoValidValue(
*result, kUserAuthenticationType, valid_user_auth_types) ||
FieldExistsAndIsEmpty(*result, kServerCARefs)) {
return false;
}
if (result->HasKey(kServerCARefs) && result->HasKey(kServerCARef)) {
error_or_warning_found_ = true;
LOG(ERROR) << MessageHeader() << "At most one of " << kServerCARefs
<< " and " << kServerCARef << " can be set.";
return false;
}
if (!ValidateClientCertFields(true /* allow ClientCertType None */, result))
return false;
bool all_required_exist =
RequireField(*result, ::onc::client_cert::kClientCertType);
return !error_on_missing_field_ || all_required_exist;
}
bool Validator::ValidateThirdPartyVPN(base::DictionaryValue* result) {
const bool all_required_exist =
RequireField(*result, ::onc::third_party_vpn::kExtensionID);
return !error_on_missing_field_ || all_required_exist;
}
bool Validator::ValidateVerifyX509(base::DictionaryValue* result) {
using namespace ::onc::verify_x509;
const char* const kValidTypes[] = {types::kName, types::kNamePrefix,
types::kSubject};
const std::vector<const char*> valid_types(toVector(kValidTypes));
if (FieldExistsAndHasNoValidValue(*result, kType, valid_types))
return false;
bool all_required_exist = RequireField(*result, kName);
return !error_on_missing_field_ || all_required_exist;
}
bool Validator::ValidateCertificatePattern(base::DictionaryValue* result) {
using namespace ::onc::client_cert;
bool all_required_exist = true;
if (!result->HasKey(kSubject) && !result->HasKey(kIssuer) &&
!result->HasKey(kIssuerCARef)) {
error_or_warning_found_ = true;
all_required_exist = false;
std::string message = MessageHeader() + "None of the fields '" + kSubject +
"', '" + kIssuer + "', and '" + kIssuerCARef +
"' is present, but at least one is required.";
if (error_on_missing_field_)
LOG(ERROR) << message;
else
LOG(WARNING) << message;
}
return !error_on_missing_field_ || all_required_exist;
}
bool Validator::ValidateGlobalNetworkConfiguration(
base::DictionaryValue* result) {
using namespace ::onc::global_network_config;
using namespace ::onc::network_config;
// Validate kDisableNetworkTypes field.
const base::ListValue* disabled_network_types = NULL;
if (result->GetListWithoutPathExpansion(kDisableNetworkTypes,
&disabled_network_types)) {
// The kDisableNetworkTypes field is only allowed in device policy.
if (!disabled_network_types->empty() &&
onc_source_ != ::onc::ONC_SOURCE_DEVICE_POLICY) {
error_or_warning_found_ = true;
LOG(ERROR) << "Disabled network types only allowed in device policy.";
return false;
}
}
if (result->HasKey(kAllowOnlyPolicyNetworksToConnect)) {
// The kAllowOnlyPolicyNetworksToConnect field is only allowed in device
// policy.
if (onc_source_ != ::onc::ONC_SOURCE_DEVICE_POLICY) {
error_or_warning_found_ = true;
LOG(ERROR)
<< "AllowOnlyPolicyNetworksToConnect only allowed in device policy.";
return false;
}
}
// Ensure the list contains only legitimate network type identifiers.
const char* const kValidNetworkTypeValues[] = {kCellular, kEthernet, kWiFi,
kWimax};
const std::vector<const char*> valid_network_type_values(
toVector(kValidNetworkTypeValues));
if (!ListFieldContainsValidValues(*result, kDisableNetworkTypes,
valid_network_type_values)) {
return false;
}
return true;
}
bool Validator::ValidateProxySettings(base::DictionaryValue* result) {
using namespace ::onc::proxy;
const char* const kValidTypes[] = {kDirect, kManual, kPAC, kWPAD};
const std::vector<const char*> valid_types(toVector(kValidTypes));
if (FieldExistsAndHasNoValidValue(*result, ::onc::proxy::kType, valid_types))
return false;
bool all_required_exist = RequireField(*result, ::onc::proxy::kType);
std::string type;
result->GetStringWithoutPathExpansion(::onc::proxy::kType, &type);
if (type == kManual)
all_required_exist &= RequireField(*result, kManual);
else if (type == kPAC)
all_required_exist &= RequireField(*result, kPAC);
return !error_on_missing_field_ || all_required_exist;
}
bool Validator::ValidateProxyLocation(base::DictionaryValue* result) {
using namespace ::onc::proxy;
bool all_required_exist =
RequireField(*result, kHost) && RequireField(*result, kPort);
return !error_on_missing_field_ || all_required_exist;
}
bool Validator::ValidateEAP(base::DictionaryValue* result) {
using namespace ::onc::eap;
const char* const kValidInnerValues[] = {
kAutomatic, kGTC, kMD5, kMSCHAPv2, kPAP};
const std::vector<const char*> valid_inner_values(
toVector(kValidInnerValues));
const char* const kValidOuterValues[] = {
kPEAP, kEAP_TLS, kEAP_TTLS, kLEAP, kEAP_SIM, kEAP_FAST, kEAP_AKA};
const std::vector<const char*> valid_outer_values(
toVector(kValidOuterValues));
if (FieldExistsAndHasNoValidValue(*result, kInner, valid_inner_values) ||
FieldExistsAndHasNoValidValue(*result, kOuter, valid_outer_values) ||
FieldExistsAndIsEmpty(*result, kServerCARefs)) {
return false;
}
if (result->HasKey(kServerCARefs) && result->HasKey(kServerCARef)) {
error_or_warning_found_ = true;
LOG(ERROR) << MessageHeader() << "At most one of " << kServerCARefs
<< " and " << kServerCARef << " can be set.";
return false;
}
if (!ValidateClientCertFields(false, // don't allow ClientCertType None
result)) {
return false;
}
bool all_required_exist = RequireField(*result, kOuter);
return !error_on_missing_field_ || all_required_exist;
}
bool Validator::ValidateCertificate(base::DictionaryValue* result) {
using namespace ::onc::certificate;
const char* const kValidTypes[] = {kClient, kServer, kAuthority};
const std::vector<const char*> valid_types(toVector(kValidTypes));
if (FieldExistsAndHasNoValidValue(*result, kType, valid_types) ||
FieldExistsAndIsEmpty(*result, kGUID)) {
return false;
}
std::string type;
result->GetStringWithoutPathExpansion(kType, &type);
if (onc_source_ == ::onc::ONC_SOURCE_DEVICE_POLICY &&
(type == kServer || type == kAuthority)) {
error_or_warning_found_ = true;
LOG(ERROR) << MessageHeader() << "Server and authority certificates are "
<< "prohibited in ONC device policies.";
return false;
}
if (!CheckGuidIsUniqueAndAddToSet(*result, kGUID, &certificate_guids_))
return false;
bool all_required_exist = RequireField(*result, kGUID);
bool remove = false;
result->GetBooleanWithoutPathExpansion(::onc::kRemove, &remove);
if (remove) {
error_or_warning_found_ = true;
LOG(ERROR) << MessageHeader()
<< "Removal of certificates is not supported.";
return false;
}
all_required_exist &= RequireField(*result, kType);
if (type == kClient)
all_required_exist &= RequireField(*result, kPKCS12);
else if (type == kServer || type == kAuthority)
all_required_exist &= RequireField(*result, kX509);
return !error_on_missing_field_ || all_required_exist;
}
std::string Validator::MessageHeader() {
std::string path = path_.empty() ? "toplevel" : base::JoinString(path_, ".");
std::string message = "At " + path + ": ";
return message;
}
} // namespace onc
} // namespace chromeos