blob: f0ac5dbbf87a9350985cb7cb5bcd4b844a6c7bd3 [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 "chrome/browser/chromeos/cros/onc_network_parser.h"
#include <keyhi.h>
#include <pk11pub.h>
#include "base/base64.h"
#include "base/json/json_string_value_serializer.h"
#include "chrome/browser/chromeos/login/user_manager.h"
#include "base/json/json_writer.h" // for debug output only.
#include "base/stringprintf.h"
#include "base/values.h"
#include "chrome/browser/chromeos/cros/certificate_pattern.h"
#include "chrome/browser/chromeos/cros/cros_library.h"
#include "chrome/browser/chromeos/cros/native_network_constants.h"
#include "chrome/browser/chromeos/cros/native_network_parser.h"
#include "chrome/browser/chromeos/cros/network_library.h"
#include "chrome/browser/chromeos/cros/onc_constants.h"
#include "chrome/browser/chromeos/proxy_config_service_impl.h"
#include "chrome/browser/prefs/proxy_config_dictionary.h"
#include "chrome/common/net/x509_certificate_model.h"
#include "content/public/browser/browser_thread.h"
#include "crypto/encryptor.h"
#include "crypto/hmac.h"
#include "crypto/scoped_nss_types.h"
#include "crypto/symmetric_key.h"
#include "grit/generated_resources.h"
#include "net/base/crypto_module.h"
#include "net/base/net_errors.h"
#include "net/base/nss_cert_database.h"
#include "net/base/pem_tokenizer.h"
#include "net/base/x509_certificate.h"
#include "net/proxy/proxy_bypass_rules.h"
#include "third_party/cros_system_api/dbus/service_constants.h"
#include "ui/base/l10n/l10n_util.h"
namespace chromeos {
// Local constants.
namespace {
// The PEM block header used for DER certificates
const char kCertificateHeader[] = "CERTIFICATE";
// This is an older PEM marker for DER certificates.
const char kX509CertificateHeader[] = "X509 CERTIFICATE";
const base::Value::Type TYPE_BOOLEAN = base::Value::TYPE_BOOLEAN;
const base::Value::Type TYPE_DICTIONARY = base::Value::TYPE_DICTIONARY;
const base::Value::Type TYPE_INTEGER = base::Value::TYPE_INTEGER;
const base::Value::Type TYPE_LIST = base::Value::TYPE_LIST;
const base::Value::Type TYPE_STRING = base::Value::TYPE_STRING;
// Only used currently to keep NetworkParser superclass happy.
EnumMapper<PropertyIndex>::Pair network_configuration_table[] = {
{ "GUID", PROPERTY_INDEX_GUID }
};
OncValueSignature network_configuration_signature[] = {
{ onc::kGUID, PROPERTY_INDEX_GUID, TYPE_STRING },
{ onc::kProxySettings, PROPERTY_INDEX_ONC_PROXY_SETTINGS, TYPE_DICTIONARY },
{ onc::kName, PROPERTY_INDEX_NAME, TYPE_STRING },
{ onc::kRemove, PROPERTY_INDEX_ONC_REMOVE, TYPE_BOOLEAN },
{ onc::kType, PROPERTY_INDEX_TYPE, TYPE_STRING },
{ onc::kEthernet, PROPERTY_INDEX_ONC_ETHERNET, TYPE_DICTIONARY },
{ onc::kWiFi, PROPERTY_INDEX_ONC_WIFI, TYPE_DICTIONARY },
{ onc::kVPN, PROPERTY_INDEX_ONC_VPN, TYPE_DICTIONARY },
{ NULL }
};
OncValueSignature ethernet_signature[] = {
{ onc::ethernet::kAuthentication, PROPERTY_INDEX_AUTHENTICATION,
TYPE_STRING },
{ onc::ethernet::kEAP, PROPERTY_INDEX_EAP, TYPE_DICTIONARY },
{ NULL }
};
OncValueSignature wifi_signature[] = {
{ onc::wifi::kAutoConnect, PROPERTY_INDEX_AUTO_CONNECT, TYPE_BOOLEAN },
{ onc::wifi::kEAP, PROPERTY_INDEX_EAP, TYPE_DICTIONARY },
{ onc::wifi::kHiddenSSID, PROPERTY_INDEX_WIFI_HIDDEN_SSID, TYPE_BOOLEAN },
{ onc::wifi::kPassphrase, PROPERTY_INDEX_PASSPHRASE, TYPE_STRING },
{ onc::wifi::kSecurity, PROPERTY_INDEX_SECURITY, TYPE_STRING },
{ onc::wifi::kSSID, PROPERTY_INDEX_SSID, TYPE_STRING },
{ NULL }
};
OncValueSignature issuer_subject_pattern_signature[] = {
{ onc::certificate::kCommonName,
PROPERTY_INDEX_ISSUER_SUBJECT_PATTERN_COMMON_NAME, TYPE_STRING },
{ onc::certificate::kLocality,
PROPERTY_INDEX_ISSUER_SUBJECT_PATTERN_LOCALITY, TYPE_STRING },
{ onc::certificate::kOrganization,
PROPERTY_INDEX_ISSUER_SUBJECT_PATTERN_ORGANIZATION, TYPE_STRING },
{ onc::certificate::kOrganizationalUnit,
PROPERTY_INDEX_ISSUER_SUBJECT_PATTERN_ORGANIZATIONAL_UNIT,
TYPE_STRING },
};
OncValueSignature certificate_pattern_signature[] = {
{ onc::certificate::kIssuerCARef,
PROPERTY_INDEX_ONC_CERTIFICATE_PATTERN_ISSUER_CA_REF, TYPE_LIST },
{ onc::certificate::kIssuer,
PROPERTY_INDEX_ONC_CERTIFICATE_PATTERN_ISSUER, TYPE_DICTIONARY },
{ onc::certificate::kSubject,
PROPERTY_INDEX_ONC_CERTIFICATE_PATTERN_SUBJECT, TYPE_DICTIONARY },
{ onc::certificate::kEnrollmentURI,
PROPERTY_INDEX_ONC_CERTIFICATE_PATTERN_ENROLLMENT_URI, TYPE_LIST },
};
OncValueSignature eap_signature[] = {
{ onc::eap::kAnonymousIdentity, PROPERTY_INDEX_EAP_ANONYMOUS_IDENTITY,
TYPE_STRING },
{ onc::eap::kClientCertPattern, PROPERTY_INDEX_ONC_CLIENT_CERT_PATTERN,
TYPE_DICTIONARY },
{ onc::eap::kClientCertRef, PROPERTY_INDEX_ONC_CLIENT_CERT_REF, TYPE_STRING },
{ onc::eap::kClientCertType, PROPERTY_INDEX_ONC_CLIENT_CERT_TYPE,
TYPE_STRING },
{ onc::eap::kIdentity, PROPERTY_INDEX_EAP_IDENTITY, TYPE_STRING },
{ onc::eap::kInner, PROPERTY_INDEX_EAP_PHASE_2_AUTH, TYPE_STRING },
{ onc::eap::kOuter, PROPERTY_INDEX_EAP_METHOD, TYPE_STRING },
{ onc::eap::kPassword, PROPERTY_INDEX_EAP_PASSWORD, TYPE_STRING },
{ onc::eap::kServerCARef, PROPERTY_INDEX_EAP_CA_CERT_NSS, TYPE_STRING },
{ onc::eap::kUseSystemCAs, PROPERTY_INDEX_EAP_USE_SYSTEM_CAS, TYPE_BOOLEAN },
{ onc::eap::kSaveCredentials, PROPERTY_INDEX_SAVE_CREDENTIALS, TYPE_BOOLEAN },
{ NULL }
};
OncValueSignature vpn_signature[] = {
{ onc::vpn::kHost, PROPERTY_INDEX_PROVIDER_HOST, TYPE_STRING },
{ onc::vpn::kIPsec, PROPERTY_INDEX_ONC_IPSEC, TYPE_DICTIONARY },
{ onc::vpn::kL2TP, PROPERTY_INDEX_ONC_L2TP, TYPE_DICTIONARY },
{ onc::vpn::kOpenVPN, PROPERTY_INDEX_ONC_OPENVPN, TYPE_DICTIONARY },
{ onc::vpn::kType, PROPERTY_INDEX_PROVIDER_TYPE, TYPE_STRING },
{ NULL }
};
OncValueSignature ipsec_signature[] = {
{ onc::vpn::kAuthenticationType, PROPERTY_INDEX_IPSEC_AUTHENTICATIONTYPE,
TYPE_STRING },
{ onc::vpn::kGroup, PROPERTY_INDEX_L2TPIPSEC_GROUP_NAME, TYPE_STRING },
{ onc::vpn::kIKEVersion, PROPERTY_INDEX_IPSEC_IKEVERSION, TYPE_INTEGER },
{ onc::vpn::kClientCertPattern, PROPERTY_INDEX_ONC_CLIENT_CERT_PATTERN,
TYPE_DICTIONARY },
{ onc::vpn::kClientCertRef, PROPERTY_INDEX_ONC_CLIENT_CERT_REF, TYPE_STRING },
{ onc::vpn::kClientCertType, PROPERTY_INDEX_ONC_CLIENT_CERT_TYPE,
TYPE_STRING },
// Note: EAP and XAUTH not yet supported.
{ onc::vpn::kPSK, PROPERTY_INDEX_L2TPIPSEC_PSK, TYPE_STRING },
{ onc::vpn::kSaveCredentials, PROPERTY_INDEX_SAVE_CREDENTIALS, TYPE_BOOLEAN },
{ onc::vpn::kServerCARef, PROPERTY_INDEX_L2TPIPSEC_CA_CERT_NSS, TYPE_STRING },
{ NULL }
};
OncValueSignature l2tp_signature[] = {
{ onc::vpn::kPassword, PROPERTY_INDEX_L2TPIPSEC_PASSWORD, TYPE_STRING },
{ onc::vpn::kSaveCredentials, PROPERTY_INDEX_SAVE_CREDENTIALS, TYPE_BOOLEAN },
{ onc::vpn::kUsername, PROPERTY_INDEX_L2TPIPSEC_USER, TYPE_STRING },
{ NULL }
};
OncValueSignature openvpn_signature[] = {
{ onc::vpn::kAuth, PROPERTY_INDEX_OPEN_VPN_AUTH, TYPE_STRING },
{ onc::vpn::kAuthRetry, PROPERTY_INDEX_OPEN_VPN_AUTHRETRY, TYPE_STRING },
{ onc::vpn::kAuthNoCache, PROPERTY_INDEX_OPEN_VPN_AUTHNOCACHE, TYPE_BOOLEAN },
{ onc::vpn::kCipher, PROPERTY_INDEX_OPEN_VPN_CIPHER, TYPE_STRING },
{ onc::vpn::kClientCertPattern, PROPERTY_INDEX_ONC_CLIENT_CERT_PATTERN,
TYPE_DICTIONARY },
{ onc::vpn::kClientCertRef, PROPERTY_INDEX_ONC_CLIENT_CERT_REF, TYPE_STRING },
{ onc::vpn::kClientCertType, PROPERTY_INDEX_ONC_CLIENT_CERT_TYPE,
TYPE_STRING },
{ onc::vpn::kCompLZO, PROPERTY_INDEX_OPEN_VPN_COMPLZO, TYPE_STRING },
{ onc::vpn::kCompNoAdapt, PROPERTY_INDEX_OPEN_VPN_COMPNOADAPT, TYPE_BOOLEAN },
{ onc::vpn::kKeyDirection, PROPERTY_INDEX_OPEN_VPN_KEYDIRECTION,
TYPE_STRING },
{ onc::vpn::kNsCertType, PROPERTY_INDEX_OPEN_VPN_NSCERTTYPE, TYPE_STRING },
{ onc::vpn::kPassword, PROPERTY_INDEX_OPEN_VPN_PASSWORD, TYPE_STRING },
{ onc::vpn::kPort, PROPERTY_INDEX_OPEN_VPN_PORT, TYPE_INTEGER },
{ onc::vpn::kProto, PROPERTY_INDEX_OPEN_VPN_PROTO, TYPE_STRING },
{ onc::vpn::kPushPeerInfo, PROPERTY_INDEX_OPEN_VPN_PUSHPEERINFO,
TYPE_BOOLEAN },
{ onc::vpn::kRemoteCertEKU, PROPERTY_INDEX_OPEN_VPN_REMOTECERTEKU,
TYPE_STRING },
{ onc::vpn::kRemoteCertKU, PROPERTY_INDEX_OPEN_VPN_REMOTECERTKU, TYPE_LIST },
{ onc::vpn::kRemoteCertTLS, PROPERTY_INDEX_OPEN_VPN_REMOTECERTTLS,
TYPE_STRING },
{ onc::vpn::kRenegSec, PROPERTY_INDEX_OPEN_VPN_RENEGSEC, TYPE_INTEGER },
{ onc::vpn::kSaveCredentials, PROPERTY_INDEX_SAVE_CREDENTIALS, TYPE_BOOLEAN },
{ onc::vpn::kServerCARef, PROPERTY_INDEX_OPEN_VPN_CACERT, TYPE_STRING },
{ onc::vpn::kServerCertRef, PROPERTY_INDEX_OPEN_VPN_CERT, TYPE_STRING },
{ onc::vpn::kServerPollTimeout, PROPERTY_INDEX_OPEN_VPN_SERVERPOLLTIMEOUT,
TYPE_INTEGER },
{ onc::vpn::kShaper, PROPERTY_INDEX_OPEN_VPN_SHAPER, TYPE_INTEGER },
{ onc::vpn::kStaticChallenge, PROPERTY_INDEX_OPEN_VPN_STATICCHALLENGE,
TYPE_STRING },
{ onc::vpn::kTLSAuthContents, PROPERTY_INDEX_OPEN_VPN_TLSAUTHCONTENTS,
TYPE_STRING },
{ onc::vpn::kTLSRemote, PROPERTY_INDEX_OPEN_VPN_TLSREMOTE, TYPE_STRING },
{ onc::vpn::kUsername, PROPERTY_INDEX_OPEN_VPN_USER, TYPE_STRING },
{ NULL }
};
OncValueSignature proxy_settings_signature[] = {
{ onc::proxy::kType, PROPERTY_INDEX_ONC_PROXY_TYPE, TYPE_STRING },
{ onc::proxy::kPAC, PROPERTY_INDEX_ONC_PROXY_PAC, TYPE_STRING },
{ onc::proxy::kManual, PROPERTY_INDEX_ONC_PROXY_MANUAL, TYPE_DICTIONARY },
{ onc::proxy::kExcludeDomains, PROPERTY_INDEX_ONC_PROXY_EXCLUDE_DOMAINS,
TYPE_LIST },
{ NULL },
};
OncValueSignature proxy_manual_signature[] = {
{ onc::proxy::kHttp, PROPERTY_INDEX_ONC_PROXY_HTTP, TYPE_DICTIONARY },
{ onc::proxy::kHttps, PROPERTY_INDEX_ONC_PROXY_HTTPS, TYPE_DICTIONARY },
{ onc::proxy::kFtp, PROPERTY_INDEX_ONC_PROXY_FTP, TYPE_DICTIONARY },
{ onc::proxy::kSocks, PROPERTY_INDEX_ONC_PROXY_SOCKS, TYPE_DICTIONARY },
{ NULL },
};
// Serve the singleton mapper instance.
const EnumMapper<PropertyIndex>* get_onc_mapper() {
CR_DEFINE_STATIC_LOCAL(const EnumMapper<PropertyIndex>, mapper,
(network_configuration_table,
arraysize(network_configuration_table),
PROPERTY_INDEX_UNKNOWN));
return &mapper;
}
ConnectionType ParseNetworkType(const std::string& type) {
static EnumMapper<ConnectionType>::Pair table[] = {
{ "Ethernet", TYPE_ETHERNET },
{ "WiFi", TYPE_WIFI },
{ "VPN", TYPE_VPN },
};
CR_DEFINE_STATIC_LOCAL(EnumMapper<ConnectionType>, parser,
(table, arraysize(table), TYPE_UNKNOWN));
return parser.Get(type);
}
std::string GetStringValue(const base::Value& value) {
std::string string_value;
value.GetAsString(&string_value);
return string_value;
}
const bool GetBooleanValue(const base::Value& value) {
bool bool_value = false;
value.GetAsBoolean(&bool_value);
return bool_value;
}
std::string ConvertValueToString(const base::Value& value) {
std::string value_json;
base::JSONWriter::Write(&value, &value_json);
return value_json;
}
bool GetAsListOfStrings(const base::Value& value,
std::vector<std::string>* result) {
const base::ListValue* list = NULL;
if (!value.GetAsList(&list))
return false;
result->clear();
result->reserve(list->GetSize());
for (size_t i = 0; i < list->GetSize(); i++) {
std::string item;
if (!list->GetString(i, &item))
return false;
result->push_back(item);
}
return true;
}
} // namespace
// -------------------- OncNetworkParser --------------------
OncNetworkParser::OncNetworkParser(const std::string& onc_blob,
const std::string& passphrase,
NetworkUIData::ONCSource onc_source)
: NetworkParser(get_onc_mapper()),
onc_source_(onc_source),
allow_web_trust_from_policy_(false),
network_configs_(NULL),
certificates_(NULL) {
VLOG(2) << __func__ << ": OncNetworkParser called on " << onc_blob;
JSONStringValueSerializer deserializer(onc_blob);
deserializer.set_allow_trailing_comma(true);
scoped_ptr<base::Value> root(deserializer.Deserialize(NULL, &parse_error_));
if (!root.get() || root->GetType() != base::Value::TYPE_DICTIONARY) {
LOG(WARNING) << "OncNetworkParser received bad ONC file: " << parse_error_;
} else {
root_dict_.reset(static_cast<DictionaryValue*>(root.release()));
// Check and see if this is an encrypted ONC file. If so, decrypt it.
std::string ciphertext_test;
if (root_dict_->GetString("Ciphertext", &ciphertext_test))
root_dict_.reset(Decrypt(passphrase, root_dict_.get()));
// Decryption failed, errors will be in parse_error_;
if (!root_dict_.get())
return;
// At least one of NetworkConfigurations or Certificates is required.
bool has_network_configurations =
root_dict_->GetList("NetworkConfigurations", &network_configs_);
bool has_certificates =
root_dict_->GetList("Certificates", &certificates_);
VLOG(2) << "ONC file has " << GetNetworkConfigsSize() << " networks and "
<< GetCertificatesSize() << " certificates";
LOG_IF(WARNING, (!has_network_configurations && !has_certificates))
<< "ONC file has no NetworkConfigurations or Certificates.";
}
}
OncNetworkParser::OncNetworkParser()
: NetworkParser(get_onc_mapper()),
network_configs_(NULL),
certificates_(NULL) {
}
OncNetworkParser::~OncNetworkParser() {
}
// static
const EnumMapper<PropertyIndex>* OncNetworkParser::property_mapper() {
return get_onc_mapper();
}
base::DictionaryValue* OncNetworkParser::Decrypt(
const std::string& passphrase,
base::DictionaryValue* root) {
const int kKeySizeInBits = 256;
const int kMaxIterationCount = 500000;
std::string onc_type;
std::string initial_vector;
std::string salt;
std::string cipher;
std::string stretch_method;
std::string hmac_method;
std::string hmac;
int iterations;
std::string ciphertext;
if (!root->GetString("Ciphertext", &ciphertext) ||
!root->GetString("Cipher", &cipher) ||
!root->GetString("HMAC", &hmac) ||
!root->GetString("HMACMethod", &hmac_method) ||
!root->GetString("IV", &initial_vector) ||
!root->GetInteger("Iterations", &iterations) ||
!root->GetString("Salt", &salt) ||
!root->GetString("Stretch", &stretch_method) ||
!root->GetString("Type", &onc_type) ||
onc_type != "EncryptedConfiguration") {
parse_error_ = l10n_util::GetStringUTF8(
IDS_NETWORK_CONFIG_ERROR_ENCRYPTED_ONC_MALFORMED);
return NULL;
}
if (hmac_method != "SHA1" ||
cipher != "AES256" ||
stretch_method != "PBKDF2") {
parse_error_ = l10n_util::GetStringUTF8(
IDS_NETWORK_CONFIG_ERROR_ENCRYPTED_ONC_UNSUPPORTED_ENCRYPTION);
return NULL;
}
// Make sure iterations != 0, since that's not valid.
if (iterations == 0) {
parse_error_ = l10n_util::GetStringUTF8(
IDS_NETWORK_CONFIG_ERROR_ENCRYPTED_ONC_UNABLE_TO_DECRYPT);
return NULL;
}
// Simply a sanity check to make sure we can't lock up the machine
// for too long with a huge number (or a negative number).
if (iterations < 0 || iterations > kMaxIterationCount) {
parse_error_ = l10n_util::GetStringUTF8(
IDS_NETWORK_CONFIG_ERROR_ENCRYPTED_ONC_TOO_MANY_ITERATIONS);
return NULL;
}
if (!base::Base64Decode(salt, &salt)) {
parse_error_ = l10n_util::GetStringUTF8(
IDS_NETWORK_CONFIG_ERROR_ENCRYPTED_ONC_UNABLE_TO_DECODE);
return NULL;
}
scoped_ptr<crypto::SymmetricKey> key(
crypto::SymmetricKey::DeriveKeyFromPassword(crypto::SymmetricKey::AES,
passphrase,
salt,
iterations,
kKeySizeInBits));
if (!base::Base64Decode(initial_vector, &initial_vector)) {
parse_error_ = l10n_util::GetStringUTF8(
IDS_NETWORK_CONFIG_ERROR_ENCRYPTED_ONC_UNABLE_TO_DECODE);
return NULL;
}
if (!base::Base64Decode(ciphertext, &ciphertext)) {
parse_error_ = l10n_util::GetStringUTF8(
IDS_NETWORK_CONFIG_ERROR_ENCRYPTED_ONC_UNABLE_TO_DECODE);
return NULL;
}
if (!base::Base64Decode(hmac, &hmac)) {
parse_error_ = l10n_util::GetStringUTF8(
IDS_NETWORK_CONFIG_ERROR_ENCRYPTED_ONC_UNABLE_TO_DECODE);
return NULL;
}
crypto::HMAC hmac_verifier(crypto::HMAC::SHA1);
if (!hmac_verifier.Init(key.get()) ||
!hmac_verifier.Verify(ciphertext, hmac)) {
parse_error_ = l10n_util::GetStringUTF8(
IDS_NETWORK_CONFIG_ERROR_ENCRYPTED_ONC_UNABLE_TO_DECRYPT);
return NULL;
}
crypto::Encryptor decryptor;
if (!decryptor.Init(key.get(), crypto::Encryptor::CBC, initial_vector)) {
parse_error_ = l10n_util::GetStringUTF8(
IDS_NETWORK_CONFIG_ERROR_ENCRYPTED_ONC_UNABLE_TO_DECRYPT);
return NULL;
}
std::string plaintext;
if (!decryptor.Decrypt(ciphertext, &plaintext)) {
parse_error_ = l10n_util::GetStringUTF8(
IDS_NETWORK_CONFIG_ERROR_ENCRYPTED_ONC_UNABLE_TO_DECRYPT);
return NULL;
}
// Now we've decrypted it, let's deserialize the decrypted data.
JSONStringValueSerializer deserializer(plaintext);
deserializer.set_allow_trailing_comma(true);
scoped_ptr<base::Value> new_root(deserializer.Deserialize(NULL,
&parse_error_));
if (!new_root.get() || !new_root->IsType(base::Value::TYPE_DICTIONARY)) {
if (parse_error_.empty())
parse_error_ = l10n_util::GetStringUTF8(
IDS_NETWORK_CONFIG_ERROR_NETWORK_PROP_DICT_MALFORMED);
return NULL;
}
return static_cast<base::DictionaryValue*>(new_root.release());
}
int OncNetworkParser::GetNetworkConfigsSize() const {
return network_configs_ ? network_configs_->GetSize() : 0;
}
const base::DictionaryValue* OncNetworkParser::GetNetworkConfig(int n) {
CHECK(network_configs_);
CHECK(static_cast<size_t>(n) < network_configs_->GetSize());
CHECK_GE(n, 0);
base::DictionaryValue* info = NULL;
if (!network_configs_->GetDictionary(n, &info)) {
parse_error_ = l10n_util::GetStringUTF8(
IDS_NETWORK_CONFIG_ERROR_NETWORK_PROP_DICT_MALFORMED);
return NULL;
}
return info;
}
Network* OncNetworkParser::ParseNetwork(int n, bool* marked_for_removal) {
const base::DictionaryValue* info = GetNetworkConfig(n);
if (!info)
return NULL;
if (VLOG_IS_ON(2)) {
std::string network_json;
base::JSONWriter::WriteWithOptions(static_cast<const base::Value*>(info),
base::JSONWriter::OPTIONS_PRETTY_PRINT,
&network_json);
VLOG(2) << "Parsing network at index " << n << ": " << network_json;
}
if (marked_for_removal) {
if (!info->GetBoolean(onc::kRemove, marked_for_removal))
*marked_for_removal = false;
}
return CreateNetworkFromInfo(std::string(), *info);
}
int OncNetworkParser::GetCertificatesSize() const {
return certificates_ ? certificates_->GetSize() : 0;
}
scoped_refptr<net::X509Certificate> OncNetworkParser::ParseCertificate(
int cert_index) {
CHECK(certificates_);
CHECK(static_cast<size_t>(cert_index) < certificates_->GetSize());
CHECK_GE(cert_index, 0);
base::DictionaryValue* certificate = NULL;
if (!certificates_->GetDictionary(cert_index, &certificate)) {
parse_error_ = l10n_util::GetStringUTF8(
IDS_NETWORK_CONFIG_ERROR_CERT_DATA_MALFORMED);
return NULL;
}
if (VLOG_IS_ON(2)) {
std::string certificate_json;
base::JSONWriter::WriteWithOptions(static_cast<base::Value*>(certificate),
base::JSONWriter::OPTIONS_PRETTY_PRINT,
&certificate_json);
VLOG(2) << "Parsing certificate at index " << cert_index
<< ": " << certificate_json;
}
// Get out the attributes of the given certificate.
std::string guid;
bool remove = false;
if (!certificate->GetString("GUID", &guid) || guid.empty()) {
LOG(WARNING) << "ONC File: certificate missing identifier at index"
<< cert_index;
parse_error_ = l10n_util::GetStringUTF8(
IDS_NETWORK_CONFIG_ERROR_CERT_GUID_MISSING);
return NULL;
}
if (!certificate->GetBoolean("Remove", &remove))
remove = false;
if (remove) {
if (!DeleteCertAndKeyByNickname(guid)) {
parse_error_ = l10n_util::GetStringUTF8(
IDS_NETWORK_CONFIG_ERROR_CERT_DELETE);
}
return NULL;
}
// Not removing, so let's get the data we need to add this certificate.
std::string cert_type;
certificate->GetString("Type", &cert_type);
if (cert_type == "Server" || cert_type == "Authority") {
return ParseServerOrCaCertificate(cert_index, cert_type, guid, certificate);
}
if (cert_type == "Client") {
return ParseClientCertificate(cert_index, guid, certificate);
}
LOG(WARNING) << "ONC File: certificate of unknown type: " << cert_type
<< " at index " << cert_index;
parse_error_ = l10n_util::GetStringUTF8(
IDS_NETWORK_CONFIG_ERROR_CERT_TYPE_MISSING);
return NULL;
}
Network* OncNetworkParser::CreateNetworkFromInfo(
const std::string& service_path,
const DictionaryValue& info) {
ConnectionType type = ParseTypeFromDictionary(info);
if (type == TYPE_UNKNOWN) { // Return NULL if cannot parse network type.
parse_error_ = l10n_util::GetStringUTF8(
IDS_NETWORK_CONFIG_ERROR_NETWORK_TYPE_MISSING);
return NULL;
}
scoped_ptr<Network> network(CreateNewNetwork(type, service_path));
// Initialize ONC source.
NetworkUIData ui_data = network->ui_data();
ui_data.set_onc_source(onc_source_);
network->set_ui_data(ui_data);
// Parse all properties recursively.
if (!ParseNestedObject(network.get(),
onc::kNetworkConfiguration,
static_cast<const base::Value&>(info),
network_configuration_signature,
ParseNetworkConfigurationValue)) {
LOG(WARNING) << "Network " << network->name() << " failed to parse.";
if (parse_error_.empty())
parse_error_ = l10n_util::GetStringUTF8(
IDS_NETWORK_CONFIG_ERROR_NETWORK_PROP_DICT_MALFORMED);
return NULL;
}
// Update the UI data property in shill.
std::string ui_data_json;
base::DictionaryValue ui_data_dict;
network->ui_data().FillDictionary(&ui_data_dict);
base::JSONWriter::Write(&ui_data_dict, &ui_data_json);
base::StringValue ui_data_string_value(ui_data_json);
network->UpdatePropertyMap(PROPERTY_INDEX_UI_DATA, &ui_data_string_value);
if (VLOG_IS_ON(2)) {
VLOG(2) << "Created Network '" << network->name()
<< "' from info. Path:" << service_path
<< " Type:" << ConnectionTypeToString(type);
}
return network.release();
}
bool OncNetworkParser::ParseNestedObject(Network* network,
const std::string& onc_type,
const base::Value& value,
OncValueSignature* signature,
ParserPointer parser) {
bool any_errors = false;
if (!value.IsType(base::Value::TYPE_DICTIONARY)) {
VLOG(1) << network->name() << ": expected object of type " << onc_type;
parse_error_ = l10n_util::GetStringUTF8(
IDS_NETWORK_CONFIG_ERROR_NETWORK_PROP_DICT_MALFORMED);
return false;
}
VLOG(2) << "Parsing nested object of type " << onc_type;
const DictionaryValue* dict = NULL;
value.GetAsDictionary(&dict);
for (DictionaryValue::key_iterator iter = dict->begin_keys();
iter != dict->end_keys(); ++iter) {
const std::string& key = *iter;
// Recommended keys are only of interest to the UI code and the UI reads it
// directly from the ONC blob.
if (key == onc::kRecommended)
continue;
const base::Value* inner_value = NULL;
dict->GetWithoutPathExpansion(key, &inner_value);
CHECK(inner_value != NULL);
int field_index;
for (field_index = 0; signature[field_index].field != NULL; ++field_index) {
if (key == signature[field_index].field)
break;
}
if (signature[field_index].field == NULL) {
LOG(WARNING) << network->name() << ": unexpected field: "
<< key << ", in type: " << onc_type;
continue;
}
if (!inner_value->IsType(signature[field_index].type)) {
LOG(ERROR) << network->name() << ": field with wrong type: " << key
<< ", actual type: " << inner_value->GetType()
<< ", expected type: " << signature[field_index].type;
any_errors = true;
continue;
}
PropertyIndex index = signature[field_index].index;
// We need to UpdatePropertyMap now since parser might want to
// change the mapped value.
network->UpdatePropertyMap(index, inner_value);
if (!parser(this, index, *inner_value, network)) {
LOG(ERROR) << network->name() << ": field in " << onc_type
<< " not parsed: " << key;
any_errors = true;
continue;
}
if (VLOG_IS_ON(2)) {
std::string value_json;
base::JSONWriter::WriteWithOptions(inner_value,
base::JSONWriter::OPTIONS_PRETTY_PRINT,
&value_json);
VLOG(2) << network->name() << ": Successfully parsed [" << key
<< "(" << index << ")] = " << value_json;
}
}
if (any_errors) {
parse_error_ = l10n_util::GetStringUTF8(
IDS_NETWORK_CONFIG_ERROR_NETWORK_PROP_DICT_MALFORMED);
}
return !any_errors;
}
// static
std::string OncNetworkParser::GetUserExpandedValue(
const base::Value& value,
NetworkUIData::ONCSource source) {
std::string string_value;
if (!value.GetAsString(&string_value))
return string_value;
// If running unit test, just return the original value.
if (!content::BrowserThread::IsMessageLoopValid(content::BrowserThread::UI))
return string_value;
if (source != NetworkUIData::ONC_SOURCE_USER_POLICY &&
source != NetworkUIData::ONC_SOURCE_USER_IMPORT) {
return string_value;
}
if (!UserManager::Get()->IsUserLoggedIn())
return string_value;
const User& logged_in_user(UserManager::Get()->GetLoggedInUser());
ReplaceSubstringsAfterOffset(&string_value, 0,
onc::substitutes::kLoginIDField,
logged_in_user.GetAccountName(false));
ReplaceSubstringsAfterOffset(&string_value, 0,
onc::substitutes::kEmailField,
logged_in_user.email());
return string_value;
}
Network* OncNetworkParser::CreateNewNetwork(
ConnectionType type, const std::string& service_path) {
Network* network = NetworkParser::CreateNewNetwork(type, service_path);
if (network) {
if (type == TYPE_ETHERNET)
network->SetNetworkParser(new OncEthernetNetworkParser());
else if (type == TYPE_WIFI)
network->SetNetworkParser(new OncWifiNetworkParser());
else if (type == TYPE_VPN)
network->SetNetworkParser(new OncVirtualNetworkParser());
}
return network;
}
ConnectionType OncNetworkParser::ParseType(const std::string& type) {
return ParseNetworkType(type);
}
ConnectionType OncNetworkParser::ParseTypeFromDictionary(
const DictionaryValue& info) {
return ParseType(GetTypeFromDictionary(info));
}
std::string OncNetworkParser::GetTypeFromDictionary(
const base::DictionaryValue& info) {
std::string type_string;
info.GetString("Type", &type_string);
return type_string;
}
std::string OncNetworkParser::GetGuidFromDictionary(
const base::DictionaryValue& info) {
std::string guid_string;
info.GetString("GUID", &guid_string);
return guid_string;
}
// static
bool OncNetworkParser::ParseNetworkConfigurationValue(
OncNetworkParser* parser,
PropertyIndex index,
const base::Value& value,
Network* network) {
switch (index) {
case PROPERTY_INDEX_ONC_ETHERNET:
return parser->ParseNestedObject(network, "Ethernet", value,
ethernet_signature, OncEthernetNetworkParser::ParseEthernetValue);
case PROPERTY_INDEX_ONC_WIFI:
return parser->ParseNestedObject(network,
onc::kWiFi,
value,
wifi_signature,
OncWifiNetworkParser::ParseWifiValue);
case PROPERTY_INDEX_ONC_VPN: {
if (!CheckNetworkType(network, TYPE_VPN, onc::kVPN))
return false;
VirtualNetwork* virtual_network = static_cast<VirtualNetwork*>(network);
// Got the "VPN" field. Immediately store the VPN.Type field
// value so that we can properly validate fields in the VPN
// object based on the type.
const DictionaryValue* dict = NULL;
CHECK(value.GetAsDictionary(&dict));
std::string provider_type_string;
if (!dict->GetString("Type", &provider_type_string)) {
VLOG(1) << network->name() << ": VPN.Type is missing";
return false;
}
ProviderType provider_type =
OncVirtualNetworkParser::ParseProviderType(provider_type_string);
virtual_network->set_provider_type(provider_type);
return parser->ParseNestedObject(network,
onc::kVPN,
value,
vpn_signature,
OncVirtualNetworkParser::ParseVPNValue);
}
case PROPERTY_INDEX_ONC_PROXY_SETTINGS:
return ProcessProxySettings(parser, value, network);
case PROPERTY_INDEX_ONC_REMOVE:
// Removal is handled in ParseNetwork, and so is ignored here.
return true;
case PROPERTY_INDEX_TYPE: {
// Update property with native value for type.
std::string str =
NativeNetworkParser::network_type_mapper()->GetKey(network->type());
base::StringValue val(str);
network->UpdatePropertyMap(PROPERTY_INDEX_TYPE, &val);
return true;
}
case PROPERTY_INDEX_GUID:
// Fall back to generic parser.
return parser->ParseValue(index, value, network);
case PROPERTY_INDEX_NAME:
// shill doesn't allow setting name for non-VPN networks.
if (network->type() != TYPE_VPN) {
network->UpdatePropertyMap(PROPERTY_INDEX_NAME, NULL);
return true;
}
// Fall back to generic parser.
return parser->ParseValue(index, value, network);
default:
break;
}
return false;
}
// static
bool OncNetworkParser::CheckNetworkType(Network* network,
ConnectionType expected,
const std::string& onc_type) {
if (expected != network->type()) {
LOG(WARNING) << network->name() << ": "
<< onc_type << " field unexpected for this type network";
return false;
}
return true;
}
scoped_refptr<net::X509Certificate>
OncNetworkParser::ParseServerOrCaCertificate(
int cert_index,
const std::string& cert_type,
const std::string& guid,
base::DictionaryValue* certificate) {
// Device policy can't import certificates.
if (onc_source_ == NetworkUIData::ONC_SOURCE_DEVICE_POLICY) {
LOG(WARNING) << "Refusing to import certificate from device policy";
// This isn't a parsing error, so just return NULL here.
return NULL;
}
bool web_trust = false;
base::ListValue* trust_list = NULL;
if (certificate->GetList("Trust", &trust_list)) {
for (size_t i = 0; i < trust_list->GetSize(); ++i) {
std::string trust_type;
if (!trust_list->GetString(i, &trust_type)) {
LOG(WARNING) << "ONC File: certificate trust is invalid at index "
<< cert_index;
parse_error_ = l10n_util::GetStringUTF8(
IDS_NETWORK_CONFIG_ERROR_CERT_TRUST_INVALID);
return NULL;
}
if (trust_type == "Web") {
// "Web" implies that the certificate is to be trusted for SSL
// identification.
web_trust = true;
} else {
LOG(WARNING) << "ONC File: certificate contains unknown "
<< "trust type: " << trust_type
<< " at index " << cert_index;
parse_error_ = l10n_util::GetStringUTF8(
IDS_NETWORK_CONFIG_ERROR_CERT_TRUST_UNKNOWN);
return NULL;
}
}
}
// Web trust is only granted to certificates imported for a managed user
// on a managed device.
if (onc_source_ == NetworkUIData::ONC_SOURCE_USER_POLICY &&
web_trust && !allow_web_trust_from_policy_) {
LOG(WARNING) << "Web trust not granted for certificate: " << guid;
web_trust = false;
}
std::string x509_data;
if (!certificate->GetString("X509", &x509_data) || x509_data.empty()) {
LOG(WARNING) << "ONC File: certificate missing appropriate "
<< "certificate data for type: " << cert_type
<< " at index " << cert_index;
parse_error_ = l10n_util::GetStringUTF8(
IDS_NETWORK_CONFIG_ERROR_CERT_DATA_MISSING);
return NULL;
}
// Parse PEM certificate, and get the decoded data for use in creating
// certificate below.
std::vector<std::string> pem_headers;
pem_headers.push_back(kCertificateHeader);
pem_headers.push_back(kX509CertificateHeader);
net::PEMTokenizer pem_tokenizer(x509_data, pem_headers);
std::string decoded_x509;
if (!pem_tokenizer.GetNext()) {
// If we failed to read the data as a PEM file, then let's just try plain
// base64 decode: some versions of Spigots didn't apply the PEM marker
// strings. For this to work, there has to be no white space, and it has to
// only contain the base64-encoded data.
if (!base::Base64Decode(x509_data, &decoded_x509)) {
LOG(WARNING) << "Unable to base64 decode X509 data: \""
<< x509_data << "\".";
parse_error_ = l10n_util::GetStringUTF8(
IDS_NETWORK_CONFIG_ERROR_CERT_DATA_MALFORMED);
return NULL;
}
} else {
decoded_x509 = pem_tokenizer.data();
}
scoped_refptr<net::X509Certificate> x509_cert =
net::X509Certificate::CreateFromBytesWithNickname(
decoded_x509.data(),
decoded_x509.size(),
guid.c_str());
if (!x509_cert.get()) {
LOG(WARNING) << "Unable to create X509 certificate from bytes.";
parse_error_ = l10n_util::GetStringUTF8(
IDS_NETWORK_CONFIG_ERROR_CERT_DATA_MALFORMED);
return NULL;
}
// Due to a mismatch regarding cert identity between NSS (cert identity is
// determined by the raw bytes) and ONC (cert identity is determined by
// GUIDs), we have to special-case a number of situations here:
//
// a) The cert bits we're trying to insert are already present in the NSS cert
// store. This is indicated by the isperm bit in CERTCertificateStr. Since
// we might have to update the nick name, we just delete the existing cert
// and reimport the cert bits.
// b) NSS gives us an actual temporary certificate. In this case, there is no
// identical certificate known to NSS, so we can safely import the
// certificate. The GUID being imported may still be on a different
// certificate, and we could jump through hoops to reimport the existing
// certificate with a different nickname. However, that would mean lots of
// effort for a case that's pretty much illegal (reusing GUIDs contradicts
// the intention of GUIDs), so we just report an error.
//
// TODO(mnissler, gspencer): We should probably switch to a mode where we
// keep our own database for mapping GUIDs to certs in order to enable several
// GUIDs to map to the same cert. See http://crosbug.com/26073.
net::NSSCertDatabase* cert_database = net::NSSCertDatabase::GetInstance();
if (x509_cert->os_cert_handle()->isperm) {
if (!cert_database->DeleteCertAndKey(x509_cert.get())) {
parse_error_ = l10n_util::GetStringUTF8(
IDS_NETWORK_CONFIG_ERROR_CERT_DELETE);
return NULL;
}
// Reload the cert here to get an actual temporary cert instance.
x509_cert =
net::X509Certificate::CreateFromBytesWithNickname(
decoded_x509.data(),
decoded_x509.size(),
guid.c_str());
if (!x509_cert.get()) {
LOG(WARNING) << "Unable to create X509 certificate from bytes.";
parse_error_ = l10n_util::GetStringUTF8(
IDS_NETWORK_CONFIG_ERROR_CERT_DATA_MALFORMED);
return NULL;
}
DCHECK(!x509_cert->os_cert_handle()->isperm);
DCHECK(x509_cert->os_cert_handle()->istemp);
}
// Make sure the GUID is not already taken. Note that for the reimport case we
// have removed the existing cert above.
net::CertificateList certs;
ListCertsWithNickname(guid, &certs);
if (!certs.empty()) {
LOG(WARNING) << "Cert GUID is already in use: " << guid;
parse_error_ = l10n_util::GetStringUTF8(
IDS_NETWORK_CONFIG_ERROR_CERT_GUID_COLLISION);
return NULL;
}
net::CertificateList cert_list;
cert_list.push_back(x509_cert);
net::NSSCertDatabase::ImportCertFailureList failures;
bool success = false;
net::NSSCertDatabase::TrustBits trust = web_trust ?
net::NSSCertDatabase::TRUSTED_SSL :
net::NSSCertDatabase::TRUST_DEFAULT;
if (cert_type == "Server") {
success = cert_database->ImportServerCert(cert_list, trust, &failures);
} else { // Authority cert
success = cert_database->ImportCACerts(cert_list, trust, &failures);
}
if (!failures.empty()) {
LOG(WARNING) << "ONC File: Error ("
<< net::ErrorToString(failures[0].net_error)
<< ") importing " << cert_type << " certificate at index "
<< cert_index;
parse_error_ = l10n_util::GetStringUTF8(
IDS_NETWORK_CONFIG_ERROR_CERT_IMPORT);
return NULL;
}
if (!success) {
LOG(WARNING) << "ONC File: Unknown error importing " << cert_type
<< " certificate at index " << cert_index;
parse_error_ = l10n_util::GetStringUTF8(
IDS_NETWORK_CONFIG_ERROR_UNKNOWN);
return NULL;
}
VLOG(2) << "Successfully imported server/ca certificate at index "
<< cert_index;
return x509_cert;
}
scoped_refptr<net::X509Certificate> OncNetworkParser::ParseClientCertificate(
int cert_index,
const std::string& guid,
base::DictionaryValue* certificate) {
std::string pkcs12_data;
if (!certificate->GetString("PKCS12", &pkcs12_data) ||
pkcs12_data.empty()) {
LOG(WARNING) << "ONC File: PKCS12 data is missing for Client "
<< "certificate at index " << cert_index;
parse_error_ = l10n_util::GetStringUTF8(
IDS_NETWORK_CONFIG_ERROR_CERT_DATA_MISSING);
return NULL;
}
std::string decoded_pkcs12;
if (!base::Base64Decode(pkcs12_data, &decoded_pkcs12)) {
LOG(WARNING) << "Unable to base64 decode PKCS#12 data: \""
<< pkcs12_data << "\".";
parse_error_ = l10n_util::GetStringUTF8(
IDS_NETWORK_CONFIG_ERROR_CERT_DATA_MALFORMED);
return NULL;
}
// Since this has a private key, always use the private module.
net::NSSCertDatabase* cert_database = net::NSSCertDatabase::GetInstance();
scoped_refptr<net::CryptoModule> module(cert_database->GetPrivateModule());
net::CertificateList imported_certs;
int result = cert_database->ImportFromPKCS12(
module.get(), decoded_pkcs12, string16(), false, &imported_certs);
if (result != net::OK) {
LOG(WARNING) << "ONC File: Unable to import Client certificate at index "
<< cert_index
<< " (error " << net::ErrorToString(result) << ").";
parse_error_ = l10n_util::GetStringUTF8(
IDS_NETWORK_CONFIG_ERROR_CERT_IMPORT);
return NULL;
}
if (imported_certs.size() == 0) {
LOG(WARNING) << "ONC File: PKCS12 data contains no importable certificates"
<< " at index " << cert_index;
return NULL;
}
if (imported_certs.size() != 1) {
LOG(WARNING) << "ONC File: PKCS12 data at index " << cert_index
<< " contains more than one certificate. Only the first one will"
<< " be imported.";
}
scoped_refptr<net::X509Certificate> cert_result = imported_certs[0];
// Find the private key associated with this certificate, and set the
// nickname on it.
SECKEYPrivateKey* private_key = PK11_FindPrivateKeyFromCert(
cert_result->os_cert_handle()->slot,
cert_result->os_cert_handle(),
NULL); // wincx
if (private_key) {
PK11_SetPrivateKeyNickname(private_key, const_cast<char*>(guid.c_str()));
SECKEY_DestroyPrivateKey(private_key);
} else {
LOG(WARNING) << "ONC File: Unable to find private key for cert at index"
<< cert_index;
}
VLOG(2) << "Successfully imported client certificate at index "
<< cert_index;
return cert_result;
}
// static
ClientCertType OncNetworkParser::ParseClientCertType(
const std::string& type) {
static EnumMapper<ClientCertType>::Pair table[] = {
{ onc::certificate::kNone, CLIENT_CERT_TYPE_NONE },
{ onc::certificate::kRef, CLIENT_CERT_TYPE_REF },
{ onc::certificate::kPattern, CLIENT_CERT_TYPE_PATTERN },
};
CR_DEFINE_STATIC_LOCAL(EnumMapper<ClientCertType>, parser,
(table, arraysize(table), CLIENT_CERT_TYPE_NONE));
return parser.Get(type);
}
// static
void OncNetworkParser::ListCertsWithNickname(const std::string& label,
net::CertificateList* result) {
net::CertificateList all_certs;
net::NSSCertDatabase::GetInstance()->ListCerts(&all_certs);
result->clear();
for (net::CertificateList::iterator iter = all_certs.begin();
iter != all_certs.end(); ++iter) {
if (iter->get()->os_cert_handle()->nickname) {
// Separate the nickname stored in the certificate at the colon, since
// NSS likes to store it as token:nickname.
const char* delimiter =
::strchr(iter->get()->os_cert_handle()->nickname, ':');
if (delimiter) {
delimiter++; // move past the colon.
if (strcmp(delimiter, label.c_str()) == 0) {
result->push_back(*iter);
continue;
}
}
}
// Now we find the private key for this certificate and see if it has a
// nickname that matches. If there is a private key, and it matches,
// then this is a client cert that we are looking for.
SECKEYPrivateKey* private_key = PK11_FindPrivateKeyFromCert(
iter->get()->os_cert_handle()->slot,
iter->get()->os_cert_handle(),
NULL); // wincx
if (private_key) {
char* private_key_nickname = PK11_GetPrivateKeyNickname(private_key);
if (private_key_nickname && private_key_nickname == label)
result->push_back(*iter);
PORT_Free(private_key_nickname);
SECKEY_DestroyPrivateKey(private_key);
}
}
}
// static
bool OncNetworkParser::DeleteCertAndKeyByNickname(const std::string& label) {
net::CertificateList cert_list;
ListCertsWithNickname(label, &cert_list);
bool result = true;
for (net::CertificateList::iterator iter = cert_list.begin();
iter != cert_list.end(); ++iter) {
// If we fail, we try and delete the rest still.
// TODO(gspencer): this isn't very "transactional". If we fail on some, but
// not all, then it's possible to leave things in a weird state.
// Luckily there should only be one cert with a particular
// label, and the cert not being found is one of the few reasons the
// delete could fail, but still... The other choice is to return
// failure immediately, but that doesn't seem to do what is intended.
if (!net::NSSCertDatabase::GetInstance()->DeleteCertAndKey(iter->get()))
result = false;
}
return result;
}
// static
std::string OncNetworkParser::GetPkcs11IdFromCertGuid(const std::string& guid) {
// We have to look up the GUID to find the PKCS#11 ID that is needed.
net::CertificateList cert_list;
ListCertsWithNickname(guid, &cert_list);
DCHECK_EQ(1ul, cert_list.size());
if (cert_list.size() == 1)
return x509_certificate_model::GetPkcs11Id(cert_list[0]->os_cert_handle());
return std::string();
}
// static
bool OncNetworkParser::ProcessProxySettings(OncNetworkParser* parser,
const base::Value& value,
Network* network) {
VLOG(1) << "Processing ProxySettings: " << ConvertValueToString(value);
// Got "ProxySettings" field. Immediately store the ProxySettings.Type
// field value so that we can properly validate fields in the ProxySettings
// object based on the type.
const DictionaryValue* dict = NULL;
CHECK(value.GetAsDictionary(&dict));
std::string proxy_type_string;
if (!dict->GetString(onc::proxy::kType, &proxy_type_string)) {
VLOG(1) << network->name() << ": ProxySettings.Type is missing";
return false;
}
Network::ProxyOncConfig& config = network->proxy_onc_config();
config.type = ParseProxyType(proxy_type_string);
// For Direct and WPAD, all other fields are ignored.
// Otherwise, recursively parse the children of ProxySettings dictionary.
if (config.type != PROXY_ONC_DIRECT && config.type != PROXY_ONC_WPAD) {
if (!parser->ParseNestedObject(network,
onc::kProxySettings,
value,
proxy_settings_signature,
OncNetworkParser::ParseProxySettingsValue)) {
return false;
}
}
// Create ProxyConfigDictionary based on parsed values.
scoped_ptr<DictionaryValue> proxy_dict;
switch (config.type) {
case PROXY_ONC_DIRECT: {
proxy_dict.reset(ProxyConfigDictionary::CreateDirect());
break;
}
case PROXY_ONC_WPAD: {
proxy_dict.reset(ProxyConfigDictionary::CreateAutoDetect());
break;
}
case PROXY_ONC_PAC: {
if (config.pac_url.empty()) {
VLOG(1) << "PAC field is required for this ProxySettings.Type";
return false;
}
GURL url(config.pac_url);
if (!url.is_valid()) {
VLOG(1) << "PAC field is invalid for this ProxySettings.Type";
return false;
}
proxy_dict.reset(ProxyConfigDictionary::CreatePacScript(url.spec(),
false));
break;
}
case PROXY_ONC_MANUAL: {
if (config.manual_spec.empty()) {
VLOG(1) << "Manual field is required for this ProxySettings.Type";
return false;
}
proxy_dict.reset(ProxyConfigDictionary::CreateFixedServers(
config.manual_spec, config.bypass_rules));
break;
}
default:
break;
}
if (!proxy_dict.get())
return false;
// Serialize ProxyConfigDictionary into a string.
std::string proxy_dict_str = ConvertValueToString(*proxy_dict.get());
// Add ProxyConfig property to property map so that it will be updated in
// shill in NetworkLibraryImplCros::CallConfigureService after all parsing
// has completed.
base::StringValue val(proxy_dict_str);
network->UpdatePropertyMap(PROPERTY_INDEX_PROXY_CONFIG, &val);
// If |network| is currently being connected to or it exists in memory,
// shill will fire PropertyChanged notification in ConfigureService,
// chromeos::ProxyConfigServiceImpl will get OnNetworkChanged notification
// and, if necessary, activate |proxy_dict_str| on network stack and reflect
// it in UI via "Change proxy settings" button.
network->set_proxy_config(proxy_dict_str);
VLOG(1) << "Parsed ProxyConfig: " << network->proxy_config();
return true;
}
// static
bool OncNetworkParser::ParseProxySettingsValue(OncNetworkParser* parser,
PropertyIndex index,
const base::Value& value,
Network* network) {
Network::ProxyOncConfig& config = network->proxy_onc_config();
switch (index) {
case PROPERTY_INDEX_ONC_PROXY_PAC: {
if (config.type == PROXY_ONC_PAC) {
// This is a string specifying the url.
config.pac_url = GetStringValue(value);
return true;
}
break;
}
case PROPERTY_INDEX_ONC_PROXY_MANUAL: {
if (config.type == PROXY_ONC_MANUAL) {
// Recursively parse the children of Manual dictionary.
return parser->ParseNestedObject(network,
onc::proxy::kManual,
value,
proxy_manual_signature,
ParseProxyManualValue);
}
break;
}
case PROPERTY_INDEX_ONC_PROXY_EXCLUDE_DOMAINS: {
if (config.type == PROXY_ONC_MANUAL) {
// This is a list of rules, parse them into ProxyBypassRules.
net::ProxyBypassRules rules;
const ListValue* list = NULL;
CHECK(value.GetAsList(&list));
for (size_t i = 0; i < list->GetSize(); ++i) {
std::string val;
if (!list->GetString(i, &val))
return false;
rules.AddRuleFromString(val);
}
config.bypass_rules = rules.ToString();
return true;
}
break;
}
default:
break;
}
return true;
}
// static
ProxyOncType OncNetworkParser::ParseProxyType(const std::string& type) {
static EnumMapper<ProxyOncType>::Pair table[] = {
{ onc::proxy::kDirect, PROXY_ONC_DIRECT },
{ onc::proxy::kWPAD, PROXY_ONC_WPAD },
{ onc::proxy::kPAC, PROXY_ONC_PAC },
{ onc::proxy::kManual, PROXY_ONC_MANUAL },
};
CR_DEFINE_STATIC_LOCAL(EnumMapper<ProxyOncType>, parser,
(table, arraysize(table), PROXY_ONC_MAX));
return parser.Get(type);
}
// static
bool OncNetworkParser::ParseProxyManualValue(OncNetworkParser* parser,
PropertyIndex index,
const base::Value& value,
Network* network) {
switch (index) {
case PROPERTY_INDEX_ONC_PROXY_HTTP:
return ParseProxyServer(index, value, "http", network);
break;
case PROPERTY_INDEX_ONC_PROXY_HTTPS:
return ParseProxyServer(index, value, "https", network);
break;
case PROPERTY_INDEX_ONC_PROXY_FTP:
return ParseProxyServer(index, value, "ftp", network);
break;
case PROPERTY_INDEX_ONC_PROXY_SOCKS:
return ParseProxyServer(index, value, "socks", network);
break;
default:
break;
}
return false;
}
// static
bool OncNetworkParser::ParseProxyServer(int property_index,
const base::Value& value,
const std::string& scheme,
Network* network) {
// Parse the ProxyLocation dictionary that specifies the proxy server.
net::ProxyServer server = ParseProxyLocationValue(property_index, value);
if (!server.is_valid())
return false;
// Append proxy server info to manual spec string.
ProxyConfigServiceImpl::ProxyConfig::EncodeAndAppendProxyServer(scheme,
server, &network->proxy_onc_config().manual_spec);
return true;
}
// static
net::ProxyServer OncNetworkParser::ParseProxyLocationValue(
int property_index,
const base::Value& value) {
// Extract the values of Host and Port keys in ProxyLocation dictionary.
const DictionaryValue* dict = NULL;
CHECK(value.GetAsDictionary(&dict));
std::string host;
int port = 0;
if (!(dict->GetString(onc::proxy::kHost, &host) &&
dict->GetInteger(onc::proxy::kPort, &port))) {
return net::ProxyServer();
}
// Determine the scheme for the proxy server.
net::ProxyServer::Scheme scheme = net::ProxyServer::SCHEME_HTTP;
if (property_index == PROPERTY_INDEX_ONC_PROXY_SOCKS) {
scheme = StartsWithASCII(host, "socks5://", false) ?
net::ProxyServer::SCHEME_SOCKS5 : net::ProxyServer::SCHEME_SOCKS4;
}
// Process the Host and Port values into net::HostPortPair, and then
// net::ProxyServer for the specific scheme.
net::HostPortPair host_port(host, static_cast<uint16>(port));
return net::ProxyServer(scheme, host_port);
}
// static
bool OncNetworkParser::ParseClientCertPattern(OncNetworkParser* parser,
PropertyIndex index,
const base::Value& value,
Network* network) {
// Ignore certificate patterns for device policy ONC so that an unmanaged user
// won't have a certificate presented for them involuntarily.
if (parser->onc_source() == NetworkUIData::ONC_SOURCE_DEVICE_POLICY)
return false;
// Only WiFi and VPN have this type.
if (network->type() != TYPE_WIFI &&
network->type() != TYPE_VPN) {
LOG(WARNING) << "Tried to parse a ClientCertPattern from something "
<< "that wasn't a WiFi or VPN network.";
return false;
}
switch (index) {
case PROPERTY_INDEX_ONC_CERTIFICATE_PATTERN_ENROLLMENT_URI: {
std::vector<std::string> resulting_list;
if (!GetAsListOfStrings(value, &resulting_list))
return false;
CertificatePattern pattern = network->client_cert_pattern();
pattern.set_enrollment_uri_list(resulting_list);
network->set_client_cert_pattern(pattern);
return true;
}
case PROPERTY_INDEX_ONC_CERTIFICATE_PATTERN_ISSUER_CA_REF: {
std::vector<std::string> resulting_list;
if (!GetAsListOfStrings(value, &resulting_list))
return false;
CertificatePattern pattern = network->client_cert_pattern();
pattern.set_issuer_ca_ref_list(resulting_list);
network->set_client_cert_pattern(pattern);
return true;
}
case PROPERTY_INDEX_ONC_CERTIFICATE_PATTERN_ISSUER:
return parser->ParseNestedObject(network,
onc::certificate::kIssuer,
value,
issuer_subject_pattern_signature,
ParseIssuerPattern);
case PROPERTY_INDEX_ONC_CERTIFICATE_PATTERN_SUBJECT:
return parser->ParseNestedObject(network,
onc::certificate::kSubject,
value,
issuer_subject_pattern_signature,
ParseSubjectPattern);
default:
break;
}
return false;
}
// static
bool OncNetworkParser::ParseIssuerPattern(OncNetworkParser* parser,
PropertyIndex index,
const base::Value& value,
Network* network) {
IssuerSubjectPattern pattern;
if (ParseIssuerSubjectPattern(&pattern, parser, index, value, network)) {
CertificatePattern cert_pattern = network->client_cert_pattern();
cert_pattern.set_issuer(pattern);
network->set_client_cert_pattern(cert_pattern);
return true;
}
return false;
}
// static
bool OncNetworkParser::ParseSubjectPattern(OncNetworkParser* parser,
PropertyIndex index,
const base::Value& value,
Network* network) {
IssuerSubjectPattern pattern;
if (ParseIssuerSubjectPattern(&pattern, parser, index, value, network)) {
CertificatePattern cert_pattern = network->client_cert_pattern();
cert_pattern.set_subject(pattern);
network->set_client_cert_pattern(cert_pattern);
return true;
}
return false;
}
// static
bool OncNetworkParser::ParseIssuerSubjectPattern(IssuerSubjectPattern* pattern,
OncNetworkParser* parser,
PropertyIndex index,
const base::Value& value,
Network* network) {
// Only WiFi and VPN have this type.
if (network->type() != TYPE_WIFI &&
network->type() != TYPE_VPN) {
LOG(WARNING) << "Tried to parse an IssuerSubjectPattern from something "
<< "that wasn't a WiFi or VPN network.";
return false;
}
std::string value_str;
if (!value.GetAsString(&value_str))
return false;
bool result = false;
switch (index) {
case PROPERTY_INDEX_ISSUER_SUBJECT_PATTERN_COMMON_NAME:
pattern->set_common_name(value_str);
result = true;
break;
case PROPERTY_INDEX_ISSUER_SUBJECT_PATTERN_LOCALITY:
pattern->set_locality(value_str);
result = true;
break;
case PROPERTY_INDEX_ISSUER_SUBJECT_PATTERN_ORGANIZATION:
pattern->set_organization(value_str);
result = true;
break;
case PROPERTY_INDEX_ISSUER_SUBJECT_PATTERN_ORGANIZATIONAL_UNIT:
pattern->set_organizational_unit(value_str);
result = true;
break;
default:
break;
}
return result;
}
// -------------------- OncEthernetNetworkParser --------------------
OncEthernetNetworkParser::OncEthernetNetworkParser() {}
OncEthernetNetworkParser::~OncEthernetNetworkParser() {}
bool OncEthernetNetworkParser::ParseEthernetValue(OncNetworkParser* parser,
PropertyIndex index,
const base::Value& value,
Network* network) {
if (!CheckNetworkType(network, TYPE_ETHERNET, "Ethernet"))
return false;
// EthernetNetwork* ethernet_network = static_cast<EthernetNetwork*>(network);
switch (index) {
case PROPERTY_INDEX_AUTHENTICATION:
// TODO(chocobo): Handle authentication.
return true;
case PROPERTY_INDEX_EAP:
// TODO(chocobo): Implement EAP authentication.
return true;
default:
break;
}
return false;
}
// -------------------- OncWirelessNetworkParser --------------------
OncWirelessNetworkParser::OncWirelessNetworkParser() {}
OncWirelessNetworkParser::~OncWirelessNetworkParser() {}
// -------------------- OncWifiNetworkParser --------------------
OncWifiNetworkParser::OncWifiNetworkParser() {}
OncWifiNetworkParser::~OncWifiNetworkParser() {}
// static
bool OncWifiNetworkParser::ParseWifiValue(OncNetworkParser* parser,
PropertyIndex index,
const base::Value& value,
Network* network) {
if (!CheckNetworkType(network, TYPE_WIFI, "WiFi"))
return false;
WifiNetwork* wifi_network = static_cast<WifiNetwork*>(network);
switch (index) {
case PROPERTY_INDEX_SSID:
wifi_network->SetName(GetStringValue(value));
return true;
case PROPERTY_INDEX_SECURITY: {
ConnectionSecurity security = ParseSecurity(GetStringValue(value));
wifi_network->set_encryption(security);
// Also update property with native value for security.
base::StringValue val(
NativeNetworkParser::network_security_mapper()->GetKey(security));
wifi_network->UpdatePropertyMap(index, &val);
return true;
}
case PROPERTY_INDEX_PASSPHRASE:
wifi_network->set_passphrase(GetStringValue(value));
return true;
case PROPERTY_INDEX_IDENTITY:
wifi_network->set_identity(GetStringValue(value));
return true;
case PROPERTY_INDEX_EAP:
parser->ParseNestedObject(wifi_network,
onc::wifi::kEAP,
value,
eap_signature,
ParseEAPValue);
return true;
case PROPERTY_INDEX_AUTO_CONNECT:
network->set_auto_connect(GetBooleanValue(value));
return true;
case PROPERTY_INDEX_WIFI_HIDDEN_SSID:
wifi_network->set_hidden_ssid(GetBooleanValue(value));
return true;
default:
break;
}
return false;
}
// static
bool OncWifiNetworkParser::ParseEAPValue(OncNetworkParser* parser,
PropertyIndex index,
const base::Value& value,
Network* network) {
if (!CheckNetworkType(network, TYPE_WIFI, onc::wifi::kEAP))
return false;
WifiNetwork* wifi_network = static_cast<WifiNetwork*>(network);
switch (index) {
case PROPERTY_INDEX_EAP_IDENTITY: {
const std::string expanded_identity(
GetUserExpandedValue(value, parser->onc_source()));
wifi_network->set_eap_identity(expanded_identity);
const StringValue expanded_identity_value(expanded_identity);
wifi_network->UpdatePropertyMap(index, &expanded_identity_value);
return true;
}
case PROPERTY_INDEX_EAP_METHOD: {
EAPMethod eap_method = ParseEAPMethod(GetStringValue(value));
wifi_network->set_eap_method(eap_method);
// Also update property with native value for EAP method.
base::StringValue val(
NativeNetworkParser::network_eap_method_mapper()->GetKey(eap_method));
wifi_network->UpdatePropertyMap(index, &val);
return true;
}
case PROPERTY_INDEX_EAP_PHASE_2_AUTH: {
EAPPhase2Auth eap_phase_2_auth = ParseEAPPhase2Auth(
GetStringValue(value));
wifi_network->set_eap_phase_2_auth(eap_phase_2_auth);
// Also update property with native value for EAP phase 2 auth.
base::StringValue val(
NativeNetworkParser::network_eap_auth_mapper()->GetKey(
eap_phase_2_auth));
wifi_network->UpdatePropertyMap(index, &val);
return true;
}
case PROPERTY_INDEX_EAP_ANONYMOUS_IDENTITY: {
const std::string expanded_identity(
GetUserExpandedValue(value, parser->onc_source()));
wifi_network->set_eap_anonymous_identity(expanded_identity);
const StringValue expanded_identity_value(expanded_identity);
wifi_network->UpdatePropertyMap(index, &expanded_identity_value);
return true;
}
case PROPERTY_INDEX_EAP_CERT_ID:
wifi_network->set_eap_client_cert_pkcs11_id(GetStringValue(value));
return true;
case PROPERTY_INDEX_EAP_CA_CERT_NSS:
wifi_network->set_eap_server_ca_cert_nss_nickname(GetStringValue(value));
return true;
case PROPERTY_INDEX_EAP_USE_SYSTEM_CAS:
wifi_network->set_eap_use_system_cas(GetBooleanValue(value));
return true;
case PROPERTY_INDEX_EAP_PASSWORD:
wifi_network->set_eap_passphrase(GetStringValue(value));
return true;
case PROPERTY_INDEX_ONC_CLIENT_CERT_REF: {
std::string cert_id = GetPkcs11IdFromCertGuid(GetStringValue(value));
if (cert_id.empty())
return false;
wifi_network->set_eap_client_cert_pkcs11_id(cert_id);
return true;
}
case PROPERTY_INDEX_ONC_CLIENT_CERT_PATTERN:
return parser->ParseNestedObject(
wifi_network,
onc::eap::kClientCertPattern,
value,
certificate_pattern_signature,
OncNetworkParser::ParseClientCertPattern);
case PROPERTY_INDEX_ONC_CLIENT_CERT_TYPE: {
ClientCertType type = ParseClientCertType(GetStringValue(value));
wifi_network->set_client_cert_type(type);
return true;
}
case PROPERTY_INDEX_SAVE_CREDENTIALS:
wifi_network->set_eap_save_credentials(GetBooleanValue(value));
return true;
default:
break;
}
return false;
}
// static
ConnectionSecurity OncWifiNetworkParser::ParseSecurity(
const std::string& security) {
static EnumMapper<ConnectionSecurity>::Pair table[] = {
{ "None", SECURITY_NONE },
{ "WEP-PSK", SECURITY_WEP },
{ "WPA-PSK", SECURITY_PSK },
{ "WPA-EAP", SECURITY_8021X },
};
CR_DEFINE_STATIC_LOCAL(EnumMapper<ConnectionSecurity>, parser,
(table, arraysize(table), SECURITY_UNKNOWN));
return parser.Get(security);
}
// static
EAPMethod OncWifiNetworkParser::ParseEAPMethod(const std::string& method) {
static EnumMapper<EAPMethod>::Pair table[] = {
{ "PEAP", EAP_METHOD_PEAP },
{ "EAP-TLS", EAP_METHOD_TLS },
{ "EAP-TTLS", EAP_METHOD_TTLS },
{ "LEAP", EAP_METHOD_LEAP },
};
CR_DEFINE_STATIC_LOCAL(EnumMapper<EAPMethod>, parser,
(table, arraysize(table), EAP_METHOD_UNKNOWN));
return parser.Get(method);
}
// static
EAPPhase2Auth OncWifiNetworkParser::ParseEAPPhase2Auth(
const std::string& auth) {
static EnumMapper<EAPPhase2Auth>::Pair table[] = {
{ "MD5", EAP_PHASE_2_AUTH_MD5 },
{ "MSCHAPV2", EAP_PHASE_2_AUTH_MSCHAPV2 },
{ "MSCHAP", EAP_PHASE_2_AUTH_MSCHAP },
{ "PAP", EAP_PHASE_2_AUTH_PAP },
};
CR_DEFINE_STATIC_LOCAL(EnumMapper<EAPPhase2Auth>, parser,
(table, arraysize(table), EAP_PHASE_2_AUTH_AUTO));
return parser.Get(auth);
}
// -------------------- OncVirtualNetworkParser --------------------
OncVirtualNetworkParser::OncVirtualNetworkParser() {}
OncVirtualNetworkParser::~OncVirtualNetworkParser() {}
bool OncVirtualNetworkParser::UpdateNetworkFromInfo(
const DictionaryValue& info,
Network* network) {
DCHECK_EQ(TYPE_VPN, network->type());
VirtualNetwork* virtual_network = static_cast<VirtualNetwork*>(network);
if (!OncNetworkParser::UpdateNetworkFromInfo(info, network))
return false;
VLOG(1) << "Updating VPN '" << virtual_network->name()
<< "': Server: " << virtual_network->server_hostname()
<< " Type: "
<< ProviderTypeToString(virtual_network->provider_type());
return true;
}
// static
bool OncVirtualNetworkParser::ParseVPNValue(OncNetworkParser* parser,
PropertyIndex index,
const base::Value& value,
Network* network) {
if (!CheckNetworkType(network, TYPE_VPN, onc::kVPN))
return false;
VirtualNetwork* virtual_network = static_cast<VirtualNetwork*>(network);
switch (index) {
case PROPERTY_INDEX_PROVIDER_HOST: {
base::StringValue empty_value("");
virtual_network->set_server_hostname(GetStringValue(value));
// Shill requires a domain property which is unused.
network->UpdatePropertyMap(PROPERTY_INDEX_VPN_DOMAIN, &empty_value);
return true;
}
case PROPERTY_INDEX_ONC_IPSEC:
if (virtual_network->provider_type() != PROVIDER_TYPE_L2TP_IPSEC_PSK &&
virtual_network->provider_type() !=
PROVIDER_TYPE_L2TP_IPSEC_USER_CERT) {
VLOG(1) << "IPsec field not allowed with this VPN type";
return false;
}
return parser->ParseNestedObject(network,
onc::vpn::kIPsec,
value,
ipsec_signature,
ParseIPsecValue);
case PROPERTY_INDEX_ONC_L2TP:
if (virtual_network->provider_type() != PROVIDER_TYPE_L2TP_IPSEC_PSK) {
VLOG(1) << "L2TP field not allowed with this VPN type";
return false;
}
return parser->ParseNestedObject(network,
onc::vpn::kL2TP,
value,
l2tp_signature,
ParseL2TPValue);
case PROPERTY_INDEX_ONC_OPENVPN: {
if (virtual_network->provider_type() != PROVIDER_TYPE_OPEN_VPN) {
VLOG(1) << "OpenVPN field not allowed with this VPN type";
return false;
}
// The following are needed by shill to set up the OpenVPN
// management channel which every ONC OpenVPN configuration will
// use.
base::StringValue empty_value("");
network->UpdatePropertyMap(PROPERTY_INDEX_OPEN_VPN_AUTHUSERPASS,
&empty_value);
network->UpdatePropertyMap(PROPERTY_INDEX_OPEN_VPN_MGMT_ENABLE,
&empty_value);
return parser->ParseNestedObject(network,
onc::vpn::kOpenVPN,
value,
openvpn_signature,
ParseOpenVPNValue);
}
case PROPERTY_INDEX_PROVIDER_TYPE: {
// Update property with native value for provider type.
ProviderType provider_type = GetCanonicalProviderType(
virtual_network->provider_type());
std::string str =
NativeVirtualNetworkParser::provider_type_mapper()->GetKey(
provider_type);
base::StringValue val(str);
network->UpdatePropertyMap(PROPERTY_INDEX_PROVIDER_TYPE, &val);
return true;
}
default:
break;
}
return false;
}
bool OncVirtualNetworkParser::ParseIPsecValue(OncNetworkParser* parser,
PropertyIndex index,
const base::Value& value,
Network* network) {
if (!CheckNetworkType(network, TYPE_VPN, onc::vpn::kIPsec))
return false;
VirtualNetwork* virtual_network = static_cast<VirtualNetwork*>(network);
switch (index) {
case PROPERTY_INDEX_IPSEC_AUTHENTICATIONTYPE:
virtual_network->set_provider_type(
UpdateProviderTypeWithAuthType(virtual_network->provider_type(),
GetStringValue(value)));
return true;
case PROPERTY_INDEX_L2TPIPSEC_CA_CERT_NSS:
virtual_network->set_ca_cert_nss(GetStringValue(value));
return true;
case PROPERTY_INDEX_L2TPIPSEC_PSK:
virtual_network->set_psk_passphrase(GetStringValue(value));
return true;
case PROPERTY_INDEX_L2TPIPSEC_GROUP_NAME:
virtual_network->set_group_name(GetStringValue(value));
return true;
case PROPERTY_INDEX_SAVE_CREDENTIALS:
// Note that the specification allows different settings for
// IPsec credentials (PSK) and L2TP credentials (username and
// password) but we merge them in our implementation as is required
// with the current connection manager.
virtual_network->set_save_credentials(GetBooleanValue(value));
return true;
case PROPERTY_INDEX_ONC_CLIENT_CERT_REF: {
std::string cert_id = GetPkcs11IdFromCertGuid(GetStringValue(value));
if (cert_id.empty())
return false;
virtual_network->set_client_cert_id(cert_id);
return true;
}
case PROPERTY_INDEX_ONC_CLIENT_CERT_PATTERN: {
return parser->ParseNestedObject(virtual_network,
onc::vpn::kClientCertPattern,
value,
certificate_pattern_signature,
ParseClientCertPattern);
}
case PROPERTY_INDEX_ONC_CLIENT_CERT_TYPE: {
ClientCertType type = ParseClientCertType(GetStringValue(value));
virtual_network->set_client_cert_type(type);
return true;
}
case PROPERTY_INDEX_IPSEC_IKEVERSION: {
if (!value.IsType(TYPE_STRING)) {
// Shill wants all provider properties to be strings.
base::StringValue string_value(ConvertValueToString(value));
virtual_network->UpdatePropertyMap(index, &string_value);
}
return true;
}
default:
break;
}
return false;
}
// static
ProviderType OncVirtualNetworkParser::UpdateProviderTypeWithAuthType(
ProviderType provider,
const std::string& auth_type) {
switch (provider) {
case PROVIDER_TYPE_L2TP_IPSEC_PSK:
case PROVIDER_TYPE_L2TP_IPSEC_USER_CERT:
if (auth_type == "Cert") {
return PROVIDER_TYPE_L2TP_IPSEC_USER_CERT;
} else {
if (auth_type != "PSK") {
VLOG(1) << "Unexpected authentication type " << auth_type;
break;
}
return PROVIDER_TYPE_L2TP_IPSEC_PSK;
}
default:
VLOG(1) << "Unexpected provider type with authentication type "
<< auth_type;
break;
}
return provider;
}
// static
ProviderType OncVirtualNetworkParser::GetCanonicalProviderType(
ProviderType provider_type) {
if (provider_type == PROVIDER_TYPE_L2TP_IPSEC_USER_CERT)
return PROVIDER_TYPE_L2TP_IPSEC_PSK;
return provider_type;
}
// static
bool OncVirtualNetworkParser::ParseL2TPValue(OncNetworkParser* parser,
PropertyIndex index,
const base::Value& value,
Network* network) {
if (!CheckNetworkType(network, TYPE_VPN, "L2TP"))
return false;
VirtualNetwork* virtual_network = static_cast<VirtualNetwork*>(network);
switch (index) {
case PROPERTY_INDEX_L2TPIPSEC_PASSWORD:
virtual_network->set_user_passphrase(GetStringValue(value));
return true;
case PROPERTY_INDEX_L2TPIPSEC_USER: {
const std::string expanded_user(
GetUserExpandedValue(value, parser->onc_source()));
virtual_network->set_username(expanded_user);
const StringValue expanded_user_value(expanded_user);
virtual_network->UpdatePropertyMap(index, &expanded_user_value);
return true;
}
case PROPERTY_INDEX_SAVE_CREDENTIALS:
// Note that the specification allows different settings for
// IPsec credentials (PSK) and L2TP credentials (username and
// password) but we merge them in our implementation as is required
// with the current connection manager.
virtual_network->set_save_credentials(GetBooleanValue(value));
return true;
default:
break;
}
return false;
}
// static
bool OncVirtualNetworkParser::ParseOpenVPNValue(OncNetworkParser* parser,
PropertyIndex index,
const base::Value& value,
Network* network) {
if (!CheckNetworkType(network, TYPE_VPN, "OpenVPN"))
return false;
VirtualNetwork* virtual_network = static_cast<VirtualNetwork*>(network);
switch (index) {
case PROPERTY_INDEX_OPEN_VPN_PASSWORD:
virtual_network->set_user_passphrase(GetStringValue(value));
return true;
case PROPERTY_INDEX_OPEN_VPN_USER: {
const std::string expanded_user(
GetUserExpandedValue(value, parser->onc_source()));
virtual_network->set_username(expanded_user);
const StringValue expanded_user_value(expanded_user);
virtual_network->UpdatePropertyMap(index, &expanded_user_value);
return true;
}
case PROPERTY_INDEX_SAVE_CREDENTIALS:
virtual_network->set_save_credentials(GetBooleanValue(value));
return true;
case PROPERTY_INDEX_OPEN_VPN_CACERT:
virtual_network->set_ca_cert_nss(GetStringValue(value));
return true;
case PROPERTY_INDEX_OPEN_VPN_REMOTECERTKU: {
// ONC supports a list of these, but we shill supports only one
// today. So extract the first.
const base::ListValue* value_list = NULL;
value.GetAsList(&value_list);
const base::Value* first_item = NULL;
if (!value_list->Get(0, &first_item) ||
!first_item->IsType(base::Value::TYPE_STRING)) {
VLOG(1) << "RemoteCertKU must be non-empty list of strings";
return false;
}
virtual_network->UpdatePropertyMap(index, first_item);
return true;
}
case PROPERTY_INDEX_OPEN_VPN_AUTH:
case PROPERTY_INDEX_OPEN_VPN_AUTHRETRY:
case PROPERTY_INDEX_OPEN_VPN_AUTHNOCACHE:
case PROPERTY_INDEX_OPEN_VPN_CERT:
case PROPERTY_INDEX_OPEN_VPN_CIPHER:
case PROPERTY_INDEX_OPEN_VPN_COMPLZO:
case PROPERTY_INDEX_OPEN_VPN_COMPNOADAPT:
case PROPERTY_INDEX_OPEN_VPN_KEYDIRECTION:
case PROPERTY_INDEX_OPEN_VPN_NSCERTTYPE:
case PROPERTY_INDEX_OPEN_VPN_PORT:
case PROPERTY_INDEX_OPEN_VPN_PROTO:
case PROPERTY_INDEX_OPEN_VPN_PUSHPEERINFO:
case PROPERTY_INDEX_OPEN_VPN_REMOTECERTEKU:
case PROPERTY_INDEX_OPEN_VPN_REMOTECERTTLS:
case PROPERTY_INDEX_OPEN_VPN_RENEGSEC:
case PROPERTY_INDEX_OPEN_VPN_SERVERPOLLTIMEOUT:
case PROPERTY_INDEX_OPEN_VPN_SHAPER:
case PROPERTY_INDEX_OPEN_VPN_STATICCHALLENGE:
case PROPERTY_INDEX_OPEN_VPN_TLSAUTHCONTENTS:
case PROPERTY_INDEX_OPEN_VPN_TLSREMOTE: {
if (!value.IsType(TYPE_STRING)) {
// Shill wants all provider properties to be strings.
base::StringValue string_value(ConvertValueToString(value));
virtual_network->UpdatePropertyMap(index, &string_value);
}
return true;
}
case PROPERTY_INDEX_ONC_CLIENT_CERT_REF: {
std::string cert_id = GetPkcs11IdFromCertGuid(GetStringValue(value));
if (cert_id.empty())
return false;
virtual_network->set_client_cert_id(cert_id);
return true;
}
case PROPERTY_INDEX_ONC_CLIENT_CERT_PATTERN:
return parser->ParseNestedObject(
virtual_network,
onc::eap::kClientCertPattern,
value,
certificate_pattern_signature,
OncNetworkParser::ParseClientCertPattern);
case PROPERTY_INDEX_ONC_CLIENT_CERT_TYPE: {
ClientCertType type = ParseClientCertType(GetStringValue(value));
virtual_network->set_client_cert_type(type);
return true;
}
default:
break;
}
return false;
}
// static
ProviderType OncVirtualNetworkParser::ParseProviderType(
const std::string& type) {
static EnumMapper<ProviderType>::Pair table[] = {
// We initially map to L2TP-IPsec PSK and then fix this up based
// on the value of AuthenticationType.
{ "L2TP-IPsec", PROVIDER_TYPE_L2TP_IPSEC_PSK },
{ "OpenVPN", PROVIDER_TYPE_OPEN_VPN },
};
CR_DEFINE_STATIC_LOCAL(EnumMapper<ProviderType>, parser,
(table, arraysize(table), PROVIDER_TYPE_MAX));
return parser.Get(type);
}
} // namespace chromeos