diff options
author | gspencer@chromium.org <gspencer@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-12-11 23:02:54 +0000 |
---|---|---|
committer | gspencer@chromium.org <gspencer@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-12-11 23:02:54 +0000 |
commit | 01c988df2e091c8c1c7b2d0fe652124d8e2d0101 (patch) | |
tree | a4c2c68a29d0500d6422f72dc9a709919dbf38a3 /chromeos | |
parent | 6ba4d65ab5e3dd39bb7098a4a19265cb7593f482 (diff) | |
download | chromium_src-01c988df2e091c8c1c7b2d0fe652124d8e2d0101.zip chromium_src-01c988df2e091c8c1c7b2d0fe652124d8e2d0101.tar.gz chromium_src-01c988df2e091c8c1c7b2d0fe652124d8e2d0101.tar.bz2 |
This moves the ONC parsing code into chromeos/network/onc
so that it can be used there without violating dependency
rules.
(Using the "refactoring" OWNERS TBR rule...)
BUG=none
TEST=ran unit tests
TBR=jhawkins@chromium.org
Review URL: https://chromiumcodereview.appspot.com/11299236
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@172446 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chromeos')
58 files changed, 4710 insertions, 11 deletions
diff --git a/chromeos/DEPS b/chromeos/DEPS index bc7030d2..a88fc06 100644 --- a/chromeos/DEPS +++ b/chromeos/DEPS @@ -1,4 +1,5 @@ include_rules = [ + "+crypto", "+net", "+third_party/cros_system_api", "+third_party/libxml", diff --git a/chromeos/chromeos.gyp b/chromeos/chromeos.gyp index d8ce661..fbe4497 100644 --- a/chromeos/chromeos.gyp +++ b/chromeos/chromeos.gyp @@ -13,6 +13,7 @@ 'dependencies': [ '../base/base.gyp:base', '../build/linux/system.gyp:dbus', + '../build/linux/system.gyp:ssl', '../dbus/dbus.gyp:dbus', '../net/net.gyp:net', '../third_party/libxml/libxml.gyp:libxml', @@ -142,6 +143,27 @@ 'network/network_state_handler.h', 'network/network_state_handler_observer.cc', 'network/network_state_handler_observer.h', + 'network/onc/onc_certificate_importer.cc', + 'network/onc/onc_certificate_importer.h', + 'network/onc/onc_constants.cc', + 'network/onc/onc_constants.h', + 'network/onc/onc_mapper.cc', + 'network/onc/onc_mapper.h', + 'network/onc/onc_merger.cc', + 'network/onc/onc_merger.h', + 'network/onc/onc_normalizer.cc', + 'network/onc/onc_normalizer.h', + 'network/onc/onc_signature.cc', + 'network/onc/onc_signature.h', + 'network/onc/onc_translation_tables.cc', + 'network/onc/onc_translation_tables.h', + 'network/onc/onc_translator.h', + 'network/onc/onc_translator_onc_to_shill.cc', + 'network/onc/onc_translator_shill_to_onc.cc', + 'network/onc/onc_utils.cc', + 'network/onc/onc_utils.h', + 'network/onc/onc_validator.cc', + 'network/onc/onc_validator.h', 'network/shill_property_handler.cc', 'network/shill_property_handler.h', 'network/shill_service_observer.cc', @@ -166,6 +188,8 @@ 'chromeos', ], 'sources': [ + 'chromeos_test_utils.cc', + 'chromeos_test_utils.h', 'cryptohome/mock_async_method_caller.cc', 'cryptohome/mock_async_method_caller.h', 'dbus/mock_bluetooth_adapter_client.cc', @@ -263,7 +287,10 @@ '../base/base.gyp:run_all_unittests', '../base/base.gyp:test_support_base', '../build/linux/system.gyp:dbus', + '../build/linux/system.gyp:ssl', + '../crypto/crypto.gyp:crypto', '../dbus/dbus.gyp:dbus_test_support', + '../net/net.gyp:net', '../testing/gmock.gyp:gmock', '../testing/gtest.gyp:gtest', 'chromeos_test_support', @@ -298,6 +325,14 @@ 'network/network_event_log_unittest.cc', 'network/network_sms_handler_unittest.cc', 'network/network_state_handler_unittest.cc', + 'network/onc/onc_certificate_importer_unittest.cc', + 'network/onc/onc_merger_unittest.cc', + 'network/onc/onc_normalizer_unittest.cc', + 'network/onc/onc_test_utils.cc', + 'network/onc/onc_test_utils.h', + 'network/onc/onc_translator_unittest.cc', + 'network/onc/onc_utils_unittest.cc', + 'network/onc/onc_validator_unittest.cc', 'network/shill_property_handler_unittest.cc', 'power/power_state_override_unittest.cc', ], diff --git a/chromeos/chromeos_test_utils.cc b/chromeos/chromeos_test_utils.cc new file mode 100644 index 0000000..3455644 --- /dev/null +++ b/chromeos/chromeos_test_utils.cc @@ -0,0 +1,32 @@ +// 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/chromeos_test_utils.h" + +#include "base/file_path.h" +#include "base/file_util.h" +#include "base/path_service.h" + +namespace chromeos { +namespace test_utils { + +bool GetTestDataPath(const std::string& component, + const std::string& filename, + FilePath* data_dir) { + FilePath path; + if (!PathService::Get(base::DIR_SOURCE_ROOT, &path)) + return false; + path = path.Append(FILE_PATH_LITERAL("chromeos")); + path = path.Append(FILE_PATH_LITERAL("test")); + path = path.Append(FILE_PATH_LITERAL("data")); + if (!file_util::PathExists(path)) // We don't want to create this. + return false; + DCHECK(data_dir); + path = path.Append(component); + *data_dir = path.Append(filename); + return true; +} + +} // namespace test_utils +} // namespace chromeos diff --git a/chromeos/chromeos_test_utils.h b/chromeos/chromeos_test_utils.h new file mode 100644 index 0000000..b424580 --- /dev/null +++ b/chromeos/chromeos_test_utils.h @@ -0,0 +1,23 @@ +// 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. + +#ifndef CHROMEOS_CHROMEOS_TEST_UTILS_H_ +#define CHROMEOS_CHROMEOS_TEST_UTILS_H_ + +#include <string> + +class FilePath; + +namespace chromeos { +namespace test_utils { + +// Returns the path to the given test data file for this library. +bool GetTestDataPath(const std::string& component, + const std::string& filename, + FilePath* data_dir); + +} // namespace test_utils +} // namespace chromeos + +#endif // CHROMEOS_CHROMEOS_TEST_UTILS_H_ diff --git a/chromeos/network/network_event_log.cc b/chromeos/network/network_event_log.cc index bf1669d..3ed5144 100644 --- a/chromeos/network/network_event_log.cc +++ b/chromeos/network/network_event_log.cc @@ -43,7 +43,7 @@ LogEntry::LogEntry(const std::string& module, std::string LogEntry::ToString() const { std::string line; line += "[" + UTF16ToUTF8(base::TimeFormatShortDateAndTime(time)) + "]"; - line += " " + module + "." + event; + line += " " + module + ":" + event; if (!description.empty()) line += ": " + description; if (count > 1) diff --git a/chromeos/network/network_event_log.h b/chromeos/network/network_event_log.h index be5a2e4..293144c 100644 --- a/chromeos/network/network_event_log.h +++ b/chromeos/network/network_event_log.h @@ -9,6 +9,7 @@ #include <string> #include "base/basictypes.h" +#include "base/stringprintf.h" #include "base/time.h" #include "chromeos/chromeos_export.h" @@ -45,6 +46,19 @@ CHROMEOS_EXPORT void AddEntry(const std::string& module, // output the events. If |max_events| > 0, limits how many events are output. CHROMEOS_EXPORT std::string GetAsString(StringOrder order, size_t max_events); +// Macros to make logging format more consistent. +#define NET_LOG(module, message) \ + ::chromeos::network_event_log::AddEntry( \ + module, \ + std::string(__FILE__) + ":" + ::base::StringPrintf("%d",__LINE__) + \ + " (" + std::string(__func__) + ")", \ + message) + +#define NET_LOG_WARNING(module, message) \ + NET_LOG(module, std::string("WARNING:") + message) +#define NET_LOG_ERROR(module, message) \ + NET_LOG(module, std::string("ERROR:") + message) + } // namespace network_event_log } // namespace chromeos diff --git a/chromeos/network/network_event_log_unittest.cc b/chromeos/network/network_event_log_unittest.cc index 3d44f6c..ed575bb 100644 --- a/chromeos/network/network_event_log_unittest.cc +++ b/chromeos/network/network_event_log_unittest.cc @@ -60,34 +60,34 @@ TEST_F(NetworkEventLogTest, TestNetworkEvents) { network_event_log::AddEntry("module3", "event3", "description3"); const std::string expected_output_oldest_first( - "module1.event1: description1\n" - "module2.event2: description2\n" - "module3.event3: description3 (2)\n"); + "module1:event1: description1\n" + "module2:event2: description2\n" + "module3:event3: description3 (2)\n"); std::string output_oldest_first = network_event_log::GetAsString( network_event_log::OLDEST_FIRST, 0); output_oldest_first = SkipTime(output_oldest_first); EXPECT_EQ(expected_output_oldest_first, output_oldest_first); const std::string expected_output_oldest_first_short( - "module2.event2: description2\n" - "module3.event3: description3 (2)\n"); + "module2:event2: description2\n" + "module3:event3: description3 (2)\n"); std::string output_oldest_first_short = network_event_log::GetAsString( network_event_log::OLDEST_FIRST, 2); output_oldest_first_short = SkipTime(output_oldest_first_short); EXPECT_EQ(expected_output_oldest_first_short, output_oldest_first_short); const std::string expected_output_newest_first( - "module3.event3: description3 (2)\n" - "module2.event2: description2\n" - "module1.event1: description1\n"); + "module3:event3: description3 (2)\n" + "module2:event2: description2\n" + "module1:event1: description1\n"); std::string output_newest_first = network_event_log::GetAsString( network_event_log::NEWEST_FIRST, 0); output_newest_first = SkipTime(output_newest_first); EXPECT_EQ(expected_output_newest_first, output_newest_first); const std::string expected_output_newest_first_short( - "module3.event3: description3 (2)\n" - "module2.event2: description2\n"); + "module3:event3: description3 (2)\n" + "module2:event2: description2\n"); std::string output_newest_first_short = network_event_log::GetAsString( network_event_log::NEWEST_FIRST, 2); output_newest_first_short = SkipTime(output_newest_first_short); diff --git a/chromeos/network/onc/onc_certificate_importer.cc b/chromeos/network/onc/onc_certificate_importer.cc new file mode 100644 index 0000000..b8d4711 --- /dev/null +++ b/chromeos/network/onc/onc_certificate_importer.cc @@ -0,0 +1,368 @@ +// 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_certificate_importer.h" + +#include <cert.h> +#include <keyhi.h> +#include <pk11pub.h> + +#include "base/base64.h" +#include "base/logging.h" +#include "base/values.h" +#include "chromeos/network/network_event_log.h" +#include "chromeos/network/onc/onc_constants.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" + +#define ONC_LOG_WARNING(message) NET_LOG_WARNING("ONC", message) +#define ONC_LOG_ERROR(message) NET_LOG_ERROR("ONC", message) + +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"; + +} // namespace + +namespace chromeos { +namespace onc { + +CertificateImporter::CertificateImporter( + ONCSource onc_source, + bool allow_web_trust_from_policy) + : onc_source_(onc_source), + allow_web_trust_from_policy_(allow_web_trust_from_policy) { +} + +CertificateImporter::ParseResult CertificateImporter::ParseAndStoreCertificates( + const base::ListValue& certificates) { + for (size_t i = 0; i < certificates.GetSize(); ++i) { + const base::DictionaryValue* certificate = NULL; + if (!certificates.GetDictionary(i, &certificate)) { + ONC_LOG_ERROR("Certificate data malformed"); + return i > 0 ? IMPORT_INCOMPLETE : IMPORT_FAILED; + } + + if (VLOG_IS_ON(2)) + VLOG(2) << "Parsing certificate at index " << i << ": " << *certificate; + + if (!ParseAndStoreCertificate(*certificate)) { + ONC_LOG_ERROR( + base::StringPrintf("Cannot parse certificate at index %zu", i)); + return i > 0 ? IMPORT_INCOMPLETE : IMPORT_FAILED; + } + + VLOG(2) << "Successfully imported certificate at index " << i; + } + return IMPORT_OK; +} + +bool CertificateImporter::ParseAndStoreCertificate( + const base::DictionaryValue& certificate) { + // Get out the attributes of the given certificate. + std::string guid; + if (!certificate.GetString(kGUID, &guid) || guid.empty()) { + ONC_LOG_ERROR("Certificate missing GUID identifier"); + return false; + } + + bool remove = false; + if (certificate.GetBoolean(kRemove, &remove) && remove) { + if (!DeleteCertAndKeyByNickname(guid)) { + ONC_LOG_WARNING("Unable to delete certificate"); + return false; + } else { + return true; + } + } + + // Not removing, so let's get the data we need to add this certificate. + std::string cert_type; + certificate.GetString(certificate::kType, &cert_type); + if (cert_type == certificate::kServer || cert_type == certificate::kAuthority) + return ParseServerOrCaCertificate(cert_type, guid, certificate); + + if (cert_type == certificate::kClient) + return ParseClientCertificate(guid, certificate); + + ONC_LOG_ERROR("Certificate of unknown type: " + cert_type); + return false; +} + +// static +void CertificateImporter::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 && std::string(label) == private_key_nickname) + result->push_back(*iter); + PORT_Free(private_key_nickname); + SECKEY_DestroyPrivateKey(private_key); + } + } +} + +// static +bool CertificateImporter::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; +} + +bool CertificateImporter::ParseServerOrCaCertificate( + const std::string& cert_type, + const std::string& guid, + const base::DictionaryValue& certificate) { + // Device policy can't import certificates. + if (onc_source_ == ONC_SOURCE_DEVICE_POLICY) { + // This isn't a parsing error. + ONC_LOG_WARNING("Refusing to import certificate from device policy."); + return true; + } + + bool web_trust = false; + const base::ListValue* trust_list = NULL; + if (certificate.GetList(certificate::kTrust, &trust_list)) { + for (size_t i = 0; i < trust_list->GetSize(); ++i) { + std::string trust_type; + if (!trust_list->GetString(i, &trust_type)) { + ONC_LOG_ERROR("Certificate trust is invalid"); + return false; + } + if (trust_type == certificate::kWeb) { + // "Web" implies that the certificate is to be trusted for SSL + // identification. + web_trust = true; + } else { + ONC_LOG_ERROR("Certificate contains unknown trust type " + trust_type); + return false; + } + } + } + + // Web trust is only granted to certificates imported for a managed user + // on a managed device. + if (onc_source_ == 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(certificate::kX509, &x509_data) || + x509_data.empty()) { + ONC_LOG_ERROR( + "Certificate missing appropriate certificate data for type: " + + cert_type); + return false; + } + + // 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)) { + ONC_LOG_ERROR("Unable to base64 decode X509 data: " + x509_data); + return false; + } + } 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()) { + ONC_LOG_ERROR("Unable to create X509 certificate from bytes."); + return false; + } + + // 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())) { + ONC_LOG_ERROR("Unable to delete X509 certificate."); + return false; + } + + // 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()) { + ONC_LOG_ERROR("Unable to create X509 certificate from bytes."); + return false; + } + 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()) { + ONC_LOG_ERROR("Certificate GUID is already in use: " + guid); + return false; + } + + 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 == certificate::kServer) + success = cert_database->ImportServerCert(cert_list, trust, &failures); + else // Authority cert + success = cert_database->ImportCACerts(cert_list, trust, &failures); + + if (!failures.empty()) { + ONC_LOG_ERROR("Error (" + net::ErrorToString(failures[0].net_error) + + ") importing " + cert_type + " certificate"); + return false; + } + if (!success) { + ONC_LOG_ERROR("Unknown error importing " + cert_type + " certificate."); + return false; + } + + return true; +} + +bool CertificateImporter::ParseClientCertificate( + const std::string& guid, + const base::DictionaryValue& certificate) { + std::string pkcs12_data; + if (!certificate.GetString(certificate::kPKCS12, &pkcs12_data) || + pkcs12_data.empty()) { + ONC_LOG_ERROR("PKCS12 data is missing for client certificate."); + return false; + } + + std::string decoded_pkcs12; + if (!base::Base64Decode(pkcs12_data, &decoded_pkcs12)) { + ONC_LOG_ERROR( + "Unable to base64 decode PKCS#12 data: \"" + pkcs12_data + "\"."); + return false; + } + + // 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 import_result = cert_database->ImportFromPKCS12( + module.get(), decoded_pkcs12, string16(), false, &imported_certs); + if (import_result != net::OK) { + ONC_LOG_ERROR("Unable to import client certificate (error " + + net::ErrorToString(import_result) + ")."); + return false; + } + + if (imported_certs.size() == 0) { + ONC_LOG_WARNING("PKCS12 data contains no importable certificates."); + return true; + } + + if (imported_certs.size() != 1) { + ONC_LOG_WARNING("ONC File: PKCS12 data 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 { + ONC_LOG_WARNING("Unable to find private key for certificate."); + } + return true; +} + +} // chromeos +} // onc diff --git a/chromeos/network/onc/onc_certificate_importer.h b/chromeos/network/onc/onc_certificate_importer.h new file mode 100644 index 0000000..bb64f81 --- /dev/null +++ b/chromeos/network/onc/onc_certificate_importer.h @@ -0,0 +1,91 @@ +// 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. + +#ifndef CHROMEOS_NETWORK_ONC_ONC_CERTIFICATE_IMPORTER_H_ +#define CHROMEOS_NETWORK_ONC_ONC_CERTIFICATE_IMPORTER_H_ + +#include <string> +#include <vector> + +#include "base/basictypes.h" +#include "base/memory/ref_counted.h" +#include "base/memory/scoped_ptr.h" +#include "chromeos/chromeos_export.h" +#include "chromeos/network/onc/onc_constants.h" + +namespace base { +class DictionaryValue; +class ListValue; +} + +namespace net { +class X509Certificate; +typedef std::vector<scoped_refptr<X509Certificate> > CertificateList; +} + +namespace chromeos { +namespace onc { + +// This class handles certificate imports from ONC (both policy and user +// imports) into the certificate store. In particular, the GUID of certificates +// is stored together with the certificate as Nickname. +class CHROMEOS_EXPORT CertificateImporter { + public: + enum ParseResult { + IMPORT_OK, + IMPORT_INCOMPLETE, + IMPORT_FAILED, + }; + + // Certificates pushed from a policy source with Web trust are only imported + // with ParseCertificate() if the |allow_web_trust_from_policy| permission is + // granted. + CertificateImporter(ONCSource onc_source, + bool allow_web_trust_from_policy); + + // Parses and stores the certificates in |onc_certificates| into the + // certificate store. If the "Remove" field of a certificate is enabled, then + // removes the certificate from the store instead of importing. Returns the + // result of the parse operation. In case of IMPORT_INCOMPLETE, some of the + // certificates may be stored/removed successfully while others had errors. + // If no error occurred, returns IMPORT_OK. + ParseResult ParseAndStoreCertificates( + const base::ListValue& onc_certificates); + + // Parses and stores/removes |certificate| in/from the certificate + // store. Returns true if the operation succeeded. + bool ParseAndStoreCertificate(const base::DictionaryValue& certificate); + + // Lists the certificates that have the string |label| as their certificate + // nickname (exact match). + static void ListCertsWithNickname(const std::string& label, + net::CertificateList* result); + + protected: + // Deletes any certificate that has the string |label| as its nickname (exact + // match). + static bool DeleteCertAndKeyByNickname(const std::string& label); + + private: + bool ParseServerOrCaCertificate(const std::string& cert_type, + const std::string& guid, + const base::DictionaryValue& certificate); + + bool ParseClientCertificate(const std::string& guid, + const base::DictionaryValue& certificate); + + // Where the ONC blob comes from. + ONCSource onc_source_; + + // Whether certificates with Web trust should be stored when pushed from a + // policy source. + bool allow_web_trust_from_policy_; + + DISALLOW_COPY_AND_ASSIGN(CertificateImporter); +}; + +} // chromeos +} // onc + +#endif // CHROMEOS_NETWORK_ONC_ONC_CERTIFICATE_IMPORTER_H_ diff --git a/chromeos/network/onc/onc_certificate_importer_unittest.cc b/chromeos/network/onc/onc_certificate_importer_unittest.cc new file mode 100644 index 0000000..736c3a9 --- /dev/null +++ b/chromeos/network/onc/onc_certificate_importer_unittest.cc @@ -0,0 +1,261 @@ +// 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_certificate_importer.h" + +#include <cert.h> +#include <certdb.h> +#include <keyhi.h> +#include <pk11pub.h> +#include <string> + +#include "base/logging.h" +#include "base/string_number_conversions.h" +#include "base/values.h" +#include "chromeos/network/onc/onc_constants.h" +#include "chromeos/network/onc/onc_test_utils.h" +#include "crypto/nss_util.h" +#include "net/base/cert_type.h" +#include "net/base/crypto_module.h" +#include "net/base/nss_cert_database.h" +#include "net/base/x509_certificate.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace chromeos { +namespace onc { + +#if defined(USE_NSS) +// In NSS 3.13, CERTDB_VALID_PEER was renamed CERTDB_TERMINAL_RECORD. So we use +// the new name of the macro. +#if !defined(CERTDB_TERMINAL_RECORD) +#define CERTDB_TERMINAL_RECORD CERTDB_VALID_PEER +#endif + +net::CertType GetCertType(net::X509Certificate::OSCertHandle cert) { + CERTCertTrust trust = {0}; + CERT_GetCertTrust(cert, &trust); + + unsigned all_flags = trust.sslFlags | trust.emailFlags | + trust.objectSigningFlags; + + if (cert->nickname && (all_flags & CERTDB_USER)) + return net::USER_CERT; + if ((all_flags & CERTDB_VALID_CA) || CERT_IsCACert(cert, NULL)) + return net::CA_CERT; + // TODO(mattm): http://crbug.com/128633. + if (trust.sslFlags & CERTDB_TERMINAL_RECORD) + return net::SERVER_CERT; + return net::UNKNOWN_CERT; +} +#else +net::CertType GetCertType(net::X509Certificate::OSCertHandle cert) { + NOTIMPLEMENTED(); + return net::UNKNOWN_CERT; +} +#endif // USE_NSS + +class ONCCertificateImporterTest : public testing::Test { + public: + virtual void SetUp() { + ASSERT_TRUE(test_nssdb_.is_open()); + + slot_ = net::NSSCertDatabase::GetInstance()->GetPublicModule(); + + // Don't run the test if the setup failed. + ASSERT_TRUE(slot_->os_module_handle()); + + // Test db should be empty at start of test. + EXPECT_EQ(0ul, ListCertsInSlot(slot_->os_module_handle()).size()); + } + + virtual void TearDown() { + EXPECT_TRUE(CleanupSlotContents(slot_->os_module_handle())); + EXPECT_EQ(0ul, ListCertsInSlot(slot_->os_module_handle()).size()); + } + + virtual ~ONCCertificateImporterTest() {} + + protected: + void AddCertificateFromFile(std::string filename, + net::CertType expected_type, + std::string* guid) { + scoped_ptr<base::DictionaryValue> onc = + test_utils::ReadTestDictionary(filename); + base::ListValue* certificates; + onc->GetListWithoutPathExpansion(kCertificates, &certificates); + + base::DictionaryValue* certificate; + certificates->GetDictionary(0, &certificate); + certificate->GetStringWithoutPathExpansion(kGUID, guid); + + CertificateImporter importer(ONC_SOURCE_USER_IMPORT, + false /* don't allow web trust */); + EXPECT_EQ(CertificateImporter::IMPORT_OK, + importer.ParseAndStoreCertificates(*certificates)); + + net::CertificateList result_list; + CertificateImporter::ListCertsWithNickname(*guid, &result_list); + ASSERT_EQ(1ul, result_list.size()); + EXPECT_EQ(expected_type, GetCertType(result_list[0]->os_cert_handle())); + } + + scoped_refptr<net::CryptoModule> slot_; + + private: + net::CertificateList ListCertsInSlot(PK11SlotInfo* slot) { + net::CertificateList result; + CERTCertList* cert_list = PK11_ListCertsInSlot(slot); + for (CERTCertListNode* node = CERT_LIST_HEAD(cert_list); + !CERT_LIST_END(node, cert_list); + node = CERT_LIST_NEXT(node)) { + result.push_back(net::X509Certificate::CreateFromHandle( + node->cert, net::X509Certificate::OSCertHandles())); + } + CERT_DestroyCertList(cert_list); + + // Sort the result so that test comparisons can be deterministic. + std::sort(result.begin(), result.end(), net::X509Certificate::LessThan()); + return result; + } + + bool CleanupSlotContents(PK11SlotInfo* slot) { + bool ok = true; + net::CertificateList certs = ListCertsInSlot(slot); + for (size_t i = 0; i < certs.size(); ++i) { + if (!net::NSSCertDatabase::GetInstance()->DeleteCertAndKey(certs[i])) + ok = false; + } + return ok; + } + + crypto::ScopedTestNSSDB test_nssdb_; +}; + +TEST_F(ONCCertificateImporterTest, AddClientCertificate) { + std::string guid; + AddCertificateFromFile("certificate-client.onc", net::USER_CERT, &guid); + + SECKEYPrivateKeyList* privkey_list = + PK11_ListPrivKeysInSlot(slot_->os_module_handle(), NULL, NULL); + EXPECT_TRUE(privkey_list); + if (privkey_list) { + SECKEYPrivateKeyListNode* node = PRIVKEY_LIST_HEAD(privkey_list); + int count = 0; + while (!PRIVKEY_LIST_END(node, privkey_list)) { + char* name = PK11_GetPrivateKeyNickname(node->key); + EXPECT_STREQ(guid.c_str(), name); + PORT_Free(name); + count++; + node = PRIVKEY_LIST_NEXT(node); + } + EXPECT_EQ(1, count); + SECKEY_DestroyPrivateKeyList(privkey_list); + } + + SECKEYPublicKeyList* pubkey_list = + PK11_ListPublicKeysInSlot(slot_->os_module_handle(), NULL); + EXPECT_TRUE(pubkey_list); + if (pubkey_list) { + SECKEYPublicKeyListNode* node = PUBKEY_LIST_HEAD(pubkey_list); + int count = 0; + while (!PUBKEY_LIST_END(node, pubkey_list)) { + count++; + node = PUBKEY_LIST_NEXT(node); + } + EXPECT_EQ(1, count); + SECKEY_DestroyPublicKeyList(pubkey_list); + } +} + +TEST_F(ONCCertificateImporterTest, AddServerCertificate) { + std::string guid; + AddCertificateFromFile("certificate-server.onc", net::SERVER_CERT, &guid); + + SECKEYPrivateKeyList* privkey_list = + PK11_ListPrivKeysInSlot(slot_->os_module_handle(), NULL, NULL); + EXPECT_FALSE(privkey_list); + + SECKEYPublicKeyList* pubkey_list = + PK11_ListPublicKeysInSlot(slot_->os_module_handle(), NULL); + EXPECT_FALSE(pubkey_list); +} + +TEST_F(ONCCertificateImporterTest, AddWebAuthorityCertificate) { + std::string guid; + AddCertificateFromFile("certificate-web-authority.onc", net::CA_CERT, &guid); + + SECKEYPrivateKeyList* privkey_list = + PK11_ListPrivKeysInSlot(slot_->os_module_handle(), NULL, NULL); + EXPECT_FALSE(privkey_list); + + SECKEYPublicKeyList* pubkey_list = + PK11_ListPublicKeysInSlot(slot_->os_module_handle(), NULL); + EXPECT_FALSE(pubkey_list); +} + +class ONCCertificateImporterTestWithParam : + public ONCCertificateImporterTest, + public testing::WithParamInterface< + std::pair<net::CertType, std::pair<const char*, const char*> > > { + protected: + net::CertType GetCertTypeParam() { + return GetParam().first; + } + + std::string GetOriginalFilename() { + return GetParam().second.first; + } + + std::string GetUpdatedFilename() { + return GetParam().second.second; + } +}; + +TEST_P(ONCCertificateImporterTestWithParam, UpdateCertificate) { + // First we import a certificate. + { + SCOPED_TRACE("Import original certificate"); + std::string guid_original; + AddCertificateFromFile(GetOriginalFilename(), GetCertTypeParam(), + &guid_original); + } + + // Now we import the same certificate with a different GUID. The cert should + // be retrievable via the new GUID. + { + SCOPED_TRACE("Import updated certificate"); + std::string guid_updated; + AddCertificateFromFile(GetUpdatedFilename(), GetCertTypeParam(), + &guid_updated); + } +} + +TEST_P(ONCCertificateImporterTestWithParam, ReimportCertificate) { + // Verify that reimporting a client certificate works. + for (int i = 0; i < 2; ++i) { + SCOPED_TRACE("Import certificate, iteration " + base::IntToString(i)); + + std::string guid_original; + AddCertificateFromFile(GetOriginalFilename(), GetCertTypeParam(), + &guid_original); + } +} + +INSTANTIATE_TEST_CASE_P( + ONCCertificateImporterTestWithParam, + ONCCertificateImporterTestWithParam, + ::testing::Values( + std::make_pair(net::USER_CERT, + std::make_pair("certificate-client.onc", + "certificate-client-update.onc")), + std::make_pair(net::SERVER_CERT, + std::make_pair("certificate-server.onc", + "certificate-server-update.onc")), + std::make_pair( + net::CA_CERT, + std::make_pair("certificate-web-authority.onc", + "certificate-web-authority-update.onc")))); + +} // namespace onc +} // namespace chromeos diff --git a/chromeos/network/onc/onc_constants.cc b/chromeos/network/onc/onc_constants.cc new file mode 100644 index 0000000..f77e991 --- /dev/null +++ b/chromeos/network/onc/onc_constants.cc @@ -0,0 +1,204 @@ +// 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_constants.h" + +namespace chromeos { + +// Constants for ONC properties. +namespace onc { + +// Top Level ONC +const char kCertificates[] = "Certificates"; +const char kEncryptedConfiguration[] = "EncryptedConfiguration"; +const char kNetworkConfigurations[] = "NetworkConfigurations"; +const char kUnencryptedConfiguration[] = "UnencryptedConfiguration"; +const char kNetworkConfiguration[] = "NetworkConfiguration"; + +// Common keys/values. +const char kRecommended[] = "Recommended"; +const char kRemove[] = "Remove"; + +// Network Configuration +const char kCellular[] = "Cellular"; +const char kEthernet[] = "Ethernet"; +const char kGUID[] = "GUID"; +const char kIPConfigs[] = "IPConfigs"; +const char kName[] = "Name"; +const char kNameServers[] = "NameServers"; +const char kProxySettings[] = "ProxySettings"; +const char kSearchDomains[] = "SearchDomains"; +const char kType[] = "Type"; +const char kVPN[] = "VPN"; +const char kWiFi[] = "WiFi"; + +namespace ethernet { +const char kAuthentication[] = "Authentication"; +const char kEAP[] = "EAP"; +const char kNone[] = "None"; +const char k8021X[] = "8021X"; +} // namespace ethernet + +namespace ipconfig { +const char kGateway[] = "Gateway"; +const char kIPAddress[] = "IPAddress"; +const char kIPv4[] = "IPv4"; +const char kIPv6[] = "IPv6"; +const char kRoutingPrefix[] = "RoutingPrefix"; +const char kType[] = "Type"; +} // namespace ipconfig + +namespace wifi { +const char kAutoConnect[] = "AutoConnect"; +const char kEAP[] = "EAP"; +const char kHiddenSSID[] = "HiddenSSID"; +const char kNone[] = "None"; +const char kPassphrase[] = "Passphrase"; +const char kProxyURL[] = "ProxyURL"; +const char kSSID[] = "SSID"; +const char kSecurity[] = "Security"; +const char kWEP_PSK[] = "WEP-PSK"; +const char kWEP_8021X[] = "WEP-8021X"; +const char kWPA_PSK[] = "WPA-PSK"; +const char kWPA_EAP[] = "WPA-EAP"; +} // namespace wifi + +namespace certificate { +const char kAuthority[] = "Authority"; +const char kClient[] = "Client"; +const char kCommonName[] = "CommonName"; +const char kEmailAddress[] = "EmailAddress"; +const char kEnrollmentURI[] = "EnrollmentURI"; +const char kIssuerCARef[] = "IssuerCARef"; +const char kIssuer[] = "Issuer"; +const char kLocality[] = "Locality"; +const char kNone[] = "None"; +const char kOrganization[] = "Organization"; +const char kOrganizationalUnit[] = "OrganizationalUnit"; +const char kPKCS12[] = "PKCS12"; +const char kPattern[] = "Pattern"; +const char kRef[] = "Ref"; +const char kServer[] = "Server"; +const char kSubject[] = "Subject"; +const char kTrust[] = "Trust"; +const char kType[] = "Type"; +const char kWeb[] = "Web"; +const char kX509[] = "X509"; +} // namespace certificate + +namespace encrypted { +const char kAES256[] = "AES256"; +const char kCipher[] = "Cipher"; +const char kCiphertext[] = "Ciphertext"; +const char kHMACMethod[] = "HMACMethod"; +const char kHMAC[] = "HMAC"; +const char kIV[] = "IV"; +const char kIterations[] = "Iterations"; +const char kPBKDF2[] = "PBKDF2"; +const char kSHA1[] = "SHA1"; +const char kSalt[] = "Salt"; +const char kStretch[] = "Stretch"; +const char kType[] = "Type"; +} // namespace encrypted + +namespace eap { +const char kAnonymousIdentity[] = "AnonymousIdentity"; +const char kAutomatic[] = "Automatic"; +const char kClientCertPattern[] = "ClientCertPattern"; +const char kClientCertRef[] = "ClientCertRef"; +const char kClientCertType[] = "ClientCertType"; +const char kEAP_AKA[] = "EAP-AKA"; +const char kEAP_FAST[] = "EAP-FAST"; +const char kEAP_SIM[] = "EAP-SIM"; +const char kEAP_TLS[] = "EAP-TLS"; +const char kEAP_TTLS[] = "EAP-TTLS"; +const char kIdentity[] = "Identity"; +const char kInner[] = "Inner"; +const char kLEAP[] = "LEAP"; +const char kMD5[] = "MD5"; +const char kMSCHAPv2[] = "MSCHAPv2"; +const char kOuter[] = "Outer"; +const char kPAP[] = "PAP"; +const char kPEAP[] = "PEAP"; +const char kPassword[] = "Password"; +const char kSaveCredentials[] = "SaveCredentials"; +const char kServerCARef[] = "ServerCARef"; +const char kUseSystemCAs[] = "UseSystemCAs"; +} // namespace eap + +namespace vpn { +const char kAuthNoCache[] = "AuthNoCache"; +const char kAuthRetry[] = "AuthRetry"; +const char kAuth[] = "Auth"; +const char kAuthenticationType[] = "AuthenticationType"; +const char kCert[] = "Cert"; +const char kCipher[] = "Cipher"; +const char kClientCertPattern[] = "ClientCertPattern"; +const char kClientCertRef[] = "ClientCertRef"; +const char kClientCertType[] = "ClientCertType"; +const char kCompLZO[] = "CompLZO"; +const char kCompNoAdapt[] = "CompNoAdapt"; +const char kEAP[] = "EAP"; +const char kGroup[] = "Group"; +const char kHost[] = "Host"; +const char kIKEVersion[] = "IKEVersion"; +const char kIPsec[] = "IPsec"; +const char kKeyDirection[] = "KeyDirection"; +const char kL2TP[] = "L2TP"; +const char kNsCertType[] = "NsCertType"; +const char kOpenVPN[] = "OpenVPN"; +const char kPSK[] = "PSK"; +const char kPassword[] = "Password"; +const char kPort[] = "Port"; +const char kProto[] = "Proto"; +const char kPushPeerInfo[] = "PushPeerInfo"; +const char kRemoteCertEKU[] = "RemoteCertEKU"; +const char kRemoteCertKU[] = "RemoteCertKU"; +const char kRemoteCertTLS[] = "RemoteCertTLS"; +const char kRenegSec[] = "RenegSec"; +const char kSaveCredentials[] = "SaveCredentials"; +const char kServerCARef[] = "ServerCARef"; +const char kServerCertRef[] = "ServerCertRef"; +const char kServerPollTimeout[] = "ServerPollTimeout"; +const char kShaper[] = "Shaper"; +const char kStaticChallenge[] = "StaticChallenge"; +const char kTLSAuthContents[] = "TLSAuthContents"; +const char kTLSRemote[] = "TLSRemote"; +const char kTypeL2TP_IPsec[] = "L2TP-IPsec"; +const char kType[] = "Type"; +const char kUsername[] = "Username"; +const char kVerb[] = "Verb"; +const char kXAUTH[] = "XAUTH"; +} // namespace vpn + +namespace openvpn { +const char kNone[] = "none"; +const char kInteract[] = "interact"; +const char kNoInteract[] = "nointeract"; +const char kServer[] = "server"; +} // namespace openvpn + +namespace proxy { +const char kDirect[] = "Direct"; +const char kExcludeDomains[] = "ExcludeDomains"; +const char kFtp[] = "FTPProxy"; +const char kHost[] = "Host"; +const char kHttp[] = "HTTPProxy"; +const char kHttps[] = "SecureHTTPProxy"; +const char kManual[] = "Manual"; +const char kPAC[] = "PAC"; +const char kPort[] = "Port"; +const char kSocks[] = "SOCKS"; +const char kType[] = "Type"; +const char kWPAD[] = "WPAD"; +} // namespace proxy + +namespace substitutes { +const char kLoginIDField[] = "${LOGIN_ID}"; +const char kEmailField[] = "${LOGIN_EMAIL}"; +} // namespace substitutes + +} // namespace onc + +} // namespace chromeos diff --git a/chromeos/network/onc/onc_constants.h b/chromeos/network/onc/onc_constants.h new file mode 100644 index 0000000..579049c --- /dev/null +++ b/chromeos/network/onc/onc_constants.h @@ -0,0 +1,220 @@ +// 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. +#ifndef CHROMEOS_NETWORK_ONC_ONC_CONSTANTS_H_ +#define CHROMEOS_NETWORK_ONC_ONC_CONSTANTS_H_ + +#include "chromeos/chromeos_export.h" + +namespace chromeos { + +// Constants for ONC properties. +namespace onc { + +// Indicates from which source an ONC blob comes from. +enum ONCSource { + ONC_SOURCE_NONE, + ONC_SOURCE_USER_IMPORT, + ONC_SOURCE_DEVICE_POLICY, + ONC_SOURCE_USER_POLICY, +}; + +// Top Level ONC. +CHROMEOS_EXPORT extern const char kCertificates[]; +CHROMEOS_EXPORT extern const char kEncryptedConfiguration[]; +CHROMEOS_EXPORT extern const char kNetworkConfigurations[]; +CHROMEOS_EXPORT extern const char kUnencryptedConfiguration[]; + +// This is no ONC key or value but used for logging only. +// TODO(pneubeck): Remove. +CHROMEOS_EXPORT extern const char kNetworkConfiguration[]; + +// Common keys/values. +CHROMEOS_EXPORT extern const char kRecommended[]; +CHROMEOS_EXPORT extern const char kRemove[]; + +// NetworkConfiguration. +// TODO(pneubeck): Put into namespace. +CHROMEOS_EXPORT extern const char kCellular[]; +CHROMEOS_EXPORT extern const char kEthernet[]; +CHROMEOS_EXPORT extern const char kGUID[]; +CHROMEOS_EXPORT extern const char kIPConfigs[]; +CHROMEOS_EXPORT extern const char kName[]; +CHROMEOS_EXPORT extern const char kNameServers[]; +CHROMEOS_EXPORT extern const char kProxySettings[]; +CHROMEOS_EXPORT extern const char kSearchDomains[]; +CHROMEOS_EXPORT extern const char kType[]; +CHROMEOS_EXPORT extern const char kVPN[]; +CHROMEOS_EXPORT extern const char kWiFi[]; + +namespace ipconfig { +CHROMEOS_EXPORT extern const char kGateway[]; +CHROMEOS_EXPORT extern const char kIPAddress[]; +CHROMEOS_EXPORT extern const char kIPv4[]; +CHROMEOS_EXPORT extern const char kIPv6[]; +CHROMEOS_EXPORT extern const char kRoutingPrefix[]; +CHROMEOS_EXPORT extern const char kType[]; +} // namespace ipconfig + +namespace ethernet { +CHROMEOS_EXPORT extern const char kAuthentication[]; +CHROMEOS_EXPORT extern const char kEAP[]; +CHROMEOS_EXPORT extern const char kNone[]; +CHROMEOS_EXPORT extern const char k8021X[]; +} // namespace ethernet + +namespace wifi { +CHROMEOS_EXPORT extern const char kAutoConnect[]; +CHROMEOS_EXPORT extern const char kEAP[]; +CHROMEOS_EXPORT extern const char kHiddenSSID[]; +CHROMEOS_EXPORT extern const char kNone[]; +CHROMEOS_EXPORT extern const char kPassphrase[]; +CHROMEOS_EXPORT extern const char kProxyURL[]; +CHROMEOS_EXPORT extern const char kSecurity[]; +CHROMEOS_EXPORT extern const char kSSID[]; +CHROMEOS_EXPORT extern const char kWEP_PSK[]; +CHROMEOS_EXPORT extern const char kWEP_8021X[]; +CHROMEOS_EXPORT extern const char kWPA_PSK[]; +CHROMEOS_EXPORT extern const char kWPA_EAP[]; +} // namespace wifi + +namespace certificate { +CHROMEOS_EXPORT extern const char kAuthority[]; +CHROMEOS_EXPORT extern const char kClient[]; +CHROMEOS_EXPORT extern const char kCommonName[]; +CHROMEOS_EXPORT extern const char kEmailAddress[]; +CHROMEOS_EXPORT extern const char kEnrollmentURI[]; +CHROMEOS_EXPORT extern const char kIssuerCARef[]; +CHROMEOS_EXPORT extern const char kIssuer[]; +CHROMEOS_EXPORT extern const char kLocality[]; +CHROMEOS_EXPORT extern const char kNone[]; +CHROMEOS_EXPORT extern const char kOrganization[]; +CHROMEOS_EXPORT extern const char kOrganizationalUnit[]; +CHROMEOS_EXPORT extern const char kPKCS12[]; +CHROMEOS_EXPORT extern const char kPattern[]; +CHROMEOS_EXPORT extern const char kRef[]; +CHROMEOS_EXPORT extern const char kServer[]; +CHROMEOS_EXPORT extern const char kSubject[]; +CHROMEOS_EXPORT extern const char kTrust[]; +CHROMEOS_EXPORT extern const char kType[]; +CHROMEOS_EXPORT extern const char kWeb[]; +CHROMEOS_EXPORT extern const char kX509[]; +} // namespace certificate + +namespace encrypted { +CHROMEOS_EXPORT extern const char kAES256[]; +CHROMEOS_EXPORT extern const char kCipher[]; +CHROMEOS_EXPORT extern const char kCiphertext[]; +CHROMEOS_EXPORT extern const char kHMACMethod[]; +CHROMEOS_EXPORT extern const char kHMAC[]; +CHROMEOS_EXPORT extern const char kIV[]; +CHROMEOS_EXPORT extern const char kIterations[]; +CHROMEOS_EXPORT extern const char kPBKDF2[]; +CHROMEOS_EXPORT extern const char kSHA1[]; +CHROMEOS_EXPORT extern const char kSalt[]; +CHROMEOS_EXPORT extern const char kStretch[]; +CHROMEOS_EXPORT extern const char kType[]; +} // namespace encrypted + +namespace eap { +CHROMEOS_EXPORT extern const char kAnonymousIdentity[]; +CHROMEOS_EXPORT extern const char kAutomatic[]; +CHROMEOS_EXPORT extern const char kClientCertPattern[]; +CHROMEOS_EXPORT extern const char kClientCertRef[]; +CHROMEOS_EXPORT extern const char kClientCertType[]; +CHROMEOS_EXPORT extern const char kEAP_AKA[]; +CHROMEOS_EXPORT extern const char kEAP_FAST[]; +CHROMEOS_EXPORT extern const char kEAP_SIM[]; +CHROMEOS_EXPORT extern const char kEAP_TLS[]; +CHROMEOS_EXPORT extern const char kEAP_TTLS[]; +CHROMEOS_EXPORT extern const char kIdentity[]; +CHROMEOS_EXPORT extern const char kInner[]; +CHROMEOS_EXPORT extern const char kLEAP[]; +CHROMEOS_EXPORT extern const char kMD5[]; +CHROMEOS_EXPORT extern const char kMSCHAPv2[]; +CHROMEOS_EXPORT extern const char kOuter[]; +CHROMEOS_EXPORT extern const char kPAP[]; +CHROMEOS_EXPORT extern const char kPEAP[]; +CHROMEOS_EXPORT extern const char kPassword[]; +CHROMEOS_EXPORT extern const char kSaveCredentials[]; +CHROMEOS_EXPORT extern const char kServerCARef[]; +CHROMEOS_EXPORT extern const char kUseSystemCAs[]; +} // namespace eap + +namespace vpn { +CHROMEOS_EXPORT extern const char kAuthNoCache[]; +CHROMEOS_EXPORT extern const char kAuthRetry[]; +CHROMEOS_EXPORT extern const char kAuth[]; +CHROMEOS_EXPORT extern const char kAuthenticationType[]; +CHROMEOS_EXPORT extern const char kCert[]; +CHROMEOS_EXPORT extern const char kCipher[]; +CHROMEOS_EXPORT extern const char kClientCertPattern[]; +CHROMEOS_EXPORT extern const char kClientCertRef[]; +CHROMEOS_EXPORT extern const char kClientCertType[]; +CHROMEOS_EXPORT extern const char kCompLZO[]; +CHROMEOS_EXPORT extern const char kCompNoAdapt[]; +CHROMEOS_EXPORT extern const char kEAP[]; +CHROMEOS_EXPORT extern const char kGroup[]; +CHROMEOS_EXPORT extern const char kHost[]; +CHROMEOS_EXPORT extern const char kIKEVersion[]; +CHROMEOS_EXPORT extern const char kIPsec[]; +CHROMEOS_EXPORT extern const char kKeyDirection[]; +CHROMEOS_EXPORT extern const char kL2TP[]; +CHROMEOS_EXPORT extern const char kNsCertType[]; +CHROMEOS_EXPORT extern const char kOpenVPN[]; +CHROMEOS_EXPORT extern const char kPSK[]; +CHROMEOS_EXPORT extern const char kPassword[]; +CHROMEOS_EXPORT extern const char kPort[]; +CHROMEOS_EXPORT extern const char kProto[]; +CHROMEOS_EXPORT extern const char kPushPeerInfo[]; +CHROMEOS_EXPORT extern const char kRemoteCertEKU[]; +CHROMEOS_EXPORT extern const char kRemoteCertKU[]; +CHROMEOS_EXPORT extern const char kRemoteCertTLS[]; +CHROMEOS_EXPORT extern const char kRenegSec[]; +CHROMEOS_EXPORT extern const char kSaveCredentials[]; +CHROMEOS_EXPORT extern const char kServerCARef[]; +CHROMEOS_EXPORT extern const char kServerCertRef[]; +CHROMEOS_EXPORT extern const char kServerPollTimeout[]; +CHROMEOS_EXPORT extern const char kShaper[]; +CHROMEOS_EXPORT extern const char kStaticChallenge[]; +CHROMEOS_EXPORT extern const char kTLSAuthContents[]; +CHROMEOS_EXPORT extern const char kTLSRemote[]; +CHROMEOS_EXPORT extern const char kTypeL2TP_IPsec[]; +CHROMEOS_EXPORT extern const char kType[]; +CHROMEOS_EXPORT extern const char kUsername[]; +CHROMEOS_EXPORT extern const char kVerb[]; +CHROMEOS_EXPORT extern const char kXAUTH[]; +} // namespace vpn + +namespace openvpn { +CHROMEOS_EXPORT extern const char kNone[]; +CHROMEOS_EXPORT extern const char kInteract[]; +CHROMEOS_EXPORT extern const char kNoInteract[]; +CHROMEOS_EXPORT extern const char kServer[]; +} // namespace openvpn + +namespace substitutes { +CHROMEOS_EXPORT extern const char kEmailField[]; +CHROMEOS_EXPORT extern const char kLoginIDField[]; +} // namespace substitutes + +namespace proxy { +CHROMEOS_EXPORT extern const char kDirect[]; +CHROMEOS_EXPORT extern const char kExcludeDomains[]; +CHROMEOS_EXPORT extern const char kFtp[]; +CHROMEOS_EXPORT extern const char kHost[]; +CHROMEOS_EXPORT extern const char kHttp[]; +CHROMEOS_EXPORT extern const char kHttps[]; +CHROMEOS_EXPORT extern const char kManual[]; +CHROMEOS_EXPORT extern const char kPAC[]; +CHROMEOS_EXPORT extern const char kPort[]; +CHROMEOS_EXPORT extern const char kSocks[]; +CHROMEOS_EXPORT extern const char kType[]; +CHROMEOS_EXPORT extern const char kWPAD[]; +} // namespace proxy + +} // namespace onc + +} // namespace chromeos + +#endif // CHROMEOS_NETWORK_ONC_ONC_CONSTANTS_H_ diff --git a/chromeos/network/onc/onc_mapper.cc b/chromeos/network/onc/onc_mapper.cc new file mode 100644 index 0000000..8d5d723 --- /dev/null +++ b/chromeos/network/onc/onc_mapper.cc @@ -0,0 +1,140 @@ +// 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_mapper.h" + +#include "base/logging.h" +#include "base/values.h" +#include "chromeos/network/onc/onc_signature.h" + +namespace chromeos { +namespace onc { + +Mapper::Mapper() { +} + +Mapper::~Mapper() { +} + +scoped_ptr<base::Value> Mapper::MapValue( + const OncValueSignature& signature, + const base::Value& onc_value) { + scoped_ptr<base::Value> result_value; + switch (onc_value.GetType()) { + case base::Value::TYPE_DICTIONARY: { + const base::DictionaryValue* dict = NULL; + onc_value.GetAsDictionary(&dict); + result_value = MapObject(signature, *dict); + break; + } + case base::Value::TYPE_LIST: { + const base::ListValue* list = NULL; + onc_value.GetAsList(&list); + bool nested_error_occured = false; + result_value = MapArray(signature, *list, &nested_error_occured); + if (nested_error_occured) + result_value.reset(); + break; + } + default: { + result_value = MapPrimitive(signature, onc_value); + break; + } + } + + return result_value.Pass(); +} + +scoped_ptr<base::DictionaryValue> Mapper::MapObject( + const OncValueSignature& signature, + const base::DictionaryValue& onc_object) { + scoped_ptr<base::DictionaryValue> result(new base::DictionaryValue); + + bool found_unknown_field = false; + bool nested_error_occured = false; + MapFields(signature, onc_object, &found_unknown_field, &nested_error_occured, + result.get()); + if (!nested_error_occured && !found_unknown_field) + return result.Pass(); + else + return scoped_ptr<base::DictionaryValue>(); +} + +scoped_ptr<base::Value> Mapper::MapPrimitive( + const OncValueSignature& signature, + const base::Value& onc_primitive) { + return make_scoped_ptr(onc_primitive.DeepCopy()); +} + +void Mapper::MapFields( + const OncValueSignature& object_signature, + const base::DictionaryValue& onc_object, + bool* found_unknown_field, + bool* nested_error_occured, + base::DictionaryValue* result) { + + for (base::DictionaryValue::Iterator it(onc_object); it.HasNext(); + it.Advance()) { + bool current_field_unknown = false; + scoped_ptr<base::Value> result_value = MapField( + it.key(), object_signature, it.value(), ¤t_field_unknown); + + if (current_field_unknown) + *found_unknown_field = true; + else if (result_value.get() != NULL) + result->SetWithoutPathExpansion(it.key(), result_value.release()); + else + *nested_error_occured = true; + } +} + +scoped_ptr<base::Value> Mapper::MapField( + const std::string& field_name, + const OncValueSignature& object_signature, + const base::Value& onc_value, + bool* found_unknown_field) { + const OncFieldSignature* field_signature = + GetFieldSignature(object_signature, field_name); + + if (field_signature != NULL) { + if (field_signature->value_signature == NULL) { + NOTREACHED() << "Found missing value signature at field '" + << field_name << "'."; + return scoped_ptr<base::Value>(); + } + + return MapValue(*field_signature->value_signature, onc_value); + } else { + DVLOG(1) << "Found unknown field name: '" << field_name << "'"; + *found_unknown_field = true; + return scoped_ptr<base::Value>(); + } +} + +scoped_ptr<base::ListValue> Mapper::MapArray( + const OncValueSignature& array_signature, + const base::ListValue& onc_array, + bool* nested_error_occured) { + if (array_signature.onc_array_entry_signature == NULL) { + NOTREACHED() << "Found missing onc_array_entry_signature."; + return scoped_ptr<base::ListValue>(); + } + + scoped_ptr<base::ListValue> result_array(new base::ListValue); + for (base::ListValue::const_iterator it = onc_array.begin(); + it != onc_array.end(); ++it) { + const base::Value* entry = *it; + + scoped_ptr<base::Value> result_entry; + result_entry = MapValue(*array_signature.onc_array_entry_signature, *entry); + if (result_entry.get() != NULL) + result_array->Append(result_entry.release()); + else + *nested_error_occured = true; + } + return result_array.Pass(); +} + +} // namespace onc +} // namespace chromeos diff --git a/chromeos/network/onc/onc_mapper.h b/chromeos/network/onc/onc_mapper.h new file mode 100644 index 0000000..e8d42a9 --- /dev/null +++ b/chromeos/network/onc/onc_mapper.h @@ -0,0 +1,104 @@ +// 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. + +#ifndef CHROMEOS_NETWORK_ONC_ONC_MAPPER_H_ +#define CHROMEOS_NETWORK_ONC_ONC_MAPPER_H_ + +#include <string> + +#include "base/memory/scoped_ptr.h" +#include "chromeos/chromeos_export.h" + +namespace base { +class Value; +class DictionaryValue; +class ListValue; +} + +namespace chromeos { +namespace onc { + +struct OncValueSignature; + +// This class implements a DeepCopy of base::Values for ONC objects that +// iterates over both the ONC signature and the object hierarchy. DCHECKs if a +// field signature without value signature or an array signature without entry +// signature is reached. +// +// The general term "map" is used here, as this class is meant as base class and +// the copy behavior can be adapted by overriding the methods. By comparing the +// address of a signature object to the list of signatures in "onc_signature.h", +// accurate signature-specific translations or validations can be applied in the +// overriding methods. +// +// The ONC validator and normalizer derive from this class and adapt the default +// copy behavior. +class Mapper { + public: + Mapper(); + virtual ~Mapper(); + + protected: + // Calls |MapObject|, |MapArray| and |MapPrimitive| according to |onc_value|'s + // type. By default aborts on nested errors in arrays. Result of the mapping + // is returned. On error returns NULL. + virtual scoped_ptr<base::Value> MapValue( + const OncValueSignature& signature, + const base::Value& onc_value); + + // Maps objects/dictionaries. By default calls |MapFields|, which recurses + // into each field of |onc_object|, and aborts on unknown fields. Result of + // the mapping is returned. On error returns NULL. + virtual scoped_ptr<base::DictionaryValue> MapObject( + const OncValueSignature& signature, + const base::DictionaryValue& onc_object); + + // Maps primitive values like BinaryValue, StringValue, IntegerValue... (all + // but dictionaries and lists). By default copies |onc_primitive|. Result of + // the mapping is returned. On error returns NULL. + virtual scoped_ptr<base::Value> MapPrimitive( + const OncValueSignature& signature, + const base::Value& onc_primitive); + + // Maps each field of the given |onc_object| according to + // |object_signature|. Adds the mapping of each field to |result| using + // |MapField| and drops unknown fields by default. Sets + // |found_unknown_field| to true if this dictionary contains any unknown + // fields. Set |nested_error_occured| to true if nested errors occured. + virtual void MapFields( + const OncValueSignature& object_signature, + const base::DictionaryValue& onc_object, + bool* found_unknown_field, + bool* nested_error_occured, + base::DictionaryValue* result); + + // Maps the value |onc_value| of field |field_name| according to its field + // signature in |object_signature| using |MapValue|. Sets + // |found_unknown_field| to true if |field_name| cannot be found in + // |object_signature|, which by default is an error. Result of the mapping is + // returned. On error returns NULL. + virtual scoped_ptr<base::Value> MapField( + const std::string& field_name, + const OncValueSignature& object_signature, + const base::Value& onc_value, + bool* found_unknown_field); + + // Maps the array |onc_array| according to |array_signature|, which defines + // the type of the entries. Maps each entry by calling |MapValue|. If any of + // the nested mappings failed, the flag |nested_error_occured| is set to true + // and the entry is dropped from the result. The resulting array is + // returned. On error returns NULL. + virtual scoped_ptr<base::ListValue> MapArray( + const OncValueSignature& array_signature, + const base::ListValue& onc_array, + bool* nested_error_occured); + + private: + DISALLOW_COPY_AND_ASSIGN(Mapper); +}; + +} // namespace onc +} // namespace chromeos + +#endif // CHROMEOS_NETWORK_ONC_ONC_MAPPER_H_ diff --git a/chromeos/network/onc/onc_merger.cc b/chromeos/network/onc/onc_merger.cc new file mode 100644 index 0000000..fca8d37 --- /dev/null +++ b/chromeos/network/onc/onc_merger.cc @@ -0,0 +1,174 @@ +// 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_merger.h" + +#include <set> +#include <string> + +#include "base/basictypes.h" +#include "base/logging.h" +#include "base/values.h" +#include "chromeos/network/onc/onc_constants.h" + +namespace chromeos { +namespace onc { +namespace { + +typedef scoped_ptr<base::DictionaryValue> DictionaryPtr; + +// Inserts |true| at every field name in |result| that is recommended in policy. +void MarkRecommendedFieldnames(const base::DictionaryValue& policy, + base::DictionaryValue* result) { + const ListValue* recommended_value = NULL; + if (!policy.GetListWithoutPathExpansion(kRecommended, &recommended_value)) + return; + for (ListValue::const_iterator it = recommended_value->begin(); + it != recommended_value->end(); ++it) { + std::string entry; + if ((*it)->GetAsString(&entry)) + result->SetBooleanWithoutPathExpansion(entry, true); + } +} + +// Split the given |policy| into its recommended and mandatory settings. The +// dictionary |result_editable| contains a |true| value at each path that is +// editable by the user. +void SplitPolicy(const base::DictionaryValue& policy, + DictionaryPtr* result_recommended, + DictionaryPtr* result_editable, + DictionaryPtr* result_mandatory) { + result_recommended->reset(new base::DictionaryValue); + result_editable->reset(new base::DictionaryValue); + result_mandatory->reset(new base::DictionaryValue); + + MarkRecommendedFieldnames(policy, result_editable->get()); + + // Distribute policy's entries into |result_recommended| and |result_editable| + // according to |result_editable| and recurse into nested dictionaries. + for (base::DictionaryValue::Iterator it(policy); it.HasNext(); it.Advance()) { + if (it.key() == kRecommended) + continue; + + const base::DictionaryValue* child_policy = NULL; + if (it.value().GetAsDictionary(&child_policy)) { + DictionaryPtr child_recommended, child_editable, child_mandatory; + SplitPolicy(*child_policy, &child_recommended, &child_editable, + &child_mandatory); + + (*result_recommended)->SetWithoutPathExpansion( + it.key(), child_recommended.release()); + (*result_editable)->SetWithoutPathExpansion( + it.key(), child_editable.release()); + (*result_mandatory)->SetWithoutPathExpansion( + it.key(), child_mandatory.release()); + } else { + bool is_recommended = false; + (*result_editable)->GetBooleanWithoutPathExpansion(it.key(), + &is_recommended); + if (is_recommended) { + (*result_recommended)->SetWithoutPathExpansion( + it.key(), it.value().DeepCopy()); + } else { + (*result_mandatory)->SetWithoutPathExpansion( + it.key(), it.value().DeepCopy()); + } + } + } +} + +// Copy values from |user| at paths that are assigned true in |editable|. The +// values are copied to a new dictionary which is returned. +DictionaryPtr DeepCopyIf( + const base::DictionaryValue& user, + const base::DictionaryValue& editable) { + DictionaryPtr result(new base::DictionaryValue); + + for (base::DictionaryValue::Iterator it(user); it.HasNext(); it.Advance()) { + const base::DictionaryValue* user_child = NULL; + if (it.value().GetAsDictionary(&user_child)) { + const base::DictionaryValue* editable_child = NULL; + if (editable.GetDictionaryWithoutPathExpansion(it.key(), + &editable_child)) { + result->SetWithoutPathExpansion( + it.key(), DeepCopyIf(*user_child, *editable_child).release()); + } + } else { + bool editable_flag; + if (editable.GetBooleanWithoutPathExpansion(it.key(), &editable_flag) && + editable_flag) { + result->SetWithoutPathExpansion(it.key(), it.value().DeepCopy()); + } + } + } + + return result.Pass(); +} + +void MergeDictionaryIfNotNULL(const base::DictionaryValue* update, + base::DictionaryValue* result) { + if (update != NULL) + result->MergeDictionary(update); +} + +} // namespace + +DictionaryPtr MergeSettingsWithPolicies( + const base::DictionaryValue* user_policy, + const base::DictionaryValue* device_policy, + const base::DictionaryValue* user_onc, + const base::DictionaryValue* shared_onc) { + DictionaryPtr user_mandatory; + DictionaryPtr user_editable; + DictionaryPtr user_recommended; + if (user_policy != NULL) { + SplitPolicy(*user_policy, &user_recommended, &user_editable, + &user_mandatory); + } else { + user_mandatory.reset(new base::DictionaryValue); + user_recommended.reset(new base::DictionaryValue); + // 'user_editable == NULL' means everything is editable + } + + DictionaryPtr device_mandatory; + DictionaryPtr device_editable; + DictionaryPtr device_recommended; + if (device_policy != NULL) { + SplitPolicy(*device_policy, &device_recommended, &device_editable, + &device_mandatory); + } else { + device_mandatory.reset(new base::DictionaryValue); + device_recommended.reset(new base::DictionaryValue); + // 'device_editable == NULL' means everything is editable + } + + DictionaryPtr reduced_user_onc( + user_onc != NULL ? user_onc->DeepCopy() : new base::DictionaryValue); + if (user_editable.get() != NULL) + reduced_user_onc = DeepCopyIf(*reduced_user_onc, *user_editable); + if (device_editable.get() != NULL) + reduced_user_onc = DeepCopyIf(*reduced_user_onc, *device_editable); + + DictionaryPtr reduced_shared_onc( + shared_onc != NULL ? shared_onc->DeepCopy() : new base::DictionaryValue); + if (user_editable.get() != NULL) + reduced_shared_onc = DeepCopyIf(*reduced_shared_onc, *user_editable); + if (device_editable.get() != NULL) + reduced_shared_onc = DeepCopyIf(*reduced_shared_onc, *device_editable); + + // Merge the settings layers according to their priority: From lowest to + // highest priority. + DictionaryPtr result(new base::DictionaryValue); + MergeDictionaryIfNotNULL(device_recommended.get(), result.get()); + MergeDictionaryIfNotNULL(user_recommended.get(), result.get()); + MergeDictionaryIfNotNULL(reduced_shared_onc.get(), result.get()); + MergeDictionaryIfNotNULL(reduced_user_onc.get(), result.get()); + MergeDictionaryIfNotNULL(device_mandatory.get(), result.get()); + MergeDictionaryIfNotNULL(user_mandatory.get(), result.get()); + + return result.Pass(); +} + +} // namespace onc +} // namespace chromeos diff --git a/chromeos/network/onc/onc_merger.h b/chromeos/network/onc/onc_merger.h new file mode 100644 index 0000000..8797f85 --- /dev/null +++ b/chromeos/network/onc/onc_merger.h @@ -0,0 +1,36 @@ +// 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. + +#ifndef CHROMEOS_NETWORK_ONC_ONC_MERGER_H_ +#define CHROMEOS_NETWORK_ONC_ONC_MERGER_H_ + +#include "base/memory/scoped_ptr.h" +#include "chromeos/chromeos_export.h" + +namespace base { +class DictionaryValue; +} + +namespace chromeos { +namespace onc { + +// Merges the given |user_onc| and |shared_onc| settings with the given +// |user_policy| and |device_policy| settings. Each can be omitted by providing +// a NULL pointer. Each dictionary has to be a valid ONC dictionary. They don't +// have to describe top-level ONC but should refer to the same section in +// ONC. |user_onc| and |shared_onc| should not contain kRecommended fields. The +// resulting dictionary is valid ONC but may contain dispensable fields (e.g. in +// a network with type: "WiFi", the field "VPN" is dispensable) that can be +// removed by the caller using the ONC normalizer. ONC conformance of the +// arguments is not checked. Use ONC validator for that. +CHROMEOS_EXPORT scoped_ptr<base::DictionaryValue> MergeSettingsWithPolicies( + const base::DictionaryValue* user_policy, + const base::DictionaryValue* device_policy, + const base::DictionaryValue* user_onc, + const base::DictionaryValue* shared_onc); + +} // namespace onc +} // namespace chromeos + +#endif // CHROMEOS_NETWORK_ONC_ONC_MERGER_H_ diff --git a/chromeos/network/onc/onc_merger_unittest.cc b/chromeos/network/onc/onc_merger_unittest.cc new file mode 100644 index 0000000..c7d5190 --- /dev/null +++ b/chromeos/network/onc/onc_merger_unittest.cc @@ -0,0 +1,146 @@ +// 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_merger.h" + +#include <string> + +#include "base/logging.h" +#include "base/values.h" +#include "chromeos/network/onc/onc_constants.h" +#include "chromeos/network/onc/onc_test_utils.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace chromeos { +namespace onc { +namespace { + +// Checks that both dictionaries contain an entry at |path| with the same value. +::testing::AssertionResult HaveSameValueAt(const base::DictionaryValue& a, + const base::DictionaryValue& b, + const std::string& path) { + const base::Value* a_value = NULL; + if (!a.Get(path, &a_value)) { + return ::testing::AssertionFailure() + << "First dictionary '" << a << "' doesn't contain " << path; + } + + const base::Value* b_value = NULL; + if (!b.Get(path, &b_value)) { + return ::testing::AssertionFailure() + << "Second dictionary '" << b << "' doesn't contain " << path; + } + + if (base::Value::Equals(a_value, b_value)) { + return ::testing::AssertionSuccess() + << "Entries at '" << path << "' are equal"; + } else { + return ::testing::AssertionFailure() + << "Entries at '" << path << "' not equal but are '" + << *a_value << "' and '" << *b_value << "'"; + } +} + +} // namespace + +namespace merger { + +class ONCMergerTest : public testing::Test { + public: + scoped_ptr<const base::DictionaryValue> user_; + scoped_ptr<const base::DictionaryValue> policy_; + scoped_ptr<const base::DictionaryValue> policy_without_recommended_; + scoped_ptr<const base::DictionaryValue> device_policy_; + + virtual void SetUp() { + policy_ = test_utils::ReadTestDictionary("policy.onc"); + policy_without_recommended_ = + test_utils::ReadTestDictionary("policy_without_recommended.onc"); + user_ = test_utils::ReadTestDictionary("user.onc"); + device_policy_ = test_utils::ReadTestDictionary("device_policy.onc"); + } +}; + +TEST_F(ONCMergerTest, MandatoryValueOverwritesUserValue) { + scoped_ptr<base::DictionaryValue> merged( + MergeSettingsWithPolicies(policy_.get(), NULL, user_.get(), NULL)); + EXPECT_TRUE(HaveSameValueAt(*merged, *policy_, "Type")); + EXPECT_TRUE(HaveSameValueAt(*merged, *policy_, "IPConfigs")); +} + +TEST_F(ONCMergerTest, MandatoryValueAndNoUserValue) { + scoped_ptr<base::DictionaryValue> merged( + MergeSettingsWithPolicies(policy_.get(), NULL, user_.get(), NULL)); + EXPECT_TRUE(HaveSameValueAt(*merged, *policy_, "GUID")); + EXPECT_TRUE(HaveSameValueAt(*merged, *policy_, "VPN.OpenVPN.Username")); +} + +TEST_F(ONCMergerTest, MandatoryDictionaryAndNoUserValue) { + scoped_ptr<base::DictionaryValue> merged( + MergeSettingsWithPolicies(policy_.get(), NULL, user_.get(), NULL)); + EXPECT_TRUE(HaveSameValueAt(*merged, *policy_without_recommended_, + "VPN.OpenVPN.ClientCertPattern")); +} + +TEST_F(ONCMergerTest, UserValueOverwritesRecommendedValue) { + scoped_ptr<base::DictionaryValue> merged( + MergeSettingsWithPolicies(policy_.get(), NULL, user_.get(), NULL)); + EXPECT_TRUE(HaveSameValueAt(*merged, *user_, "VPN.Host")); +} + +TEST_F(ONCMergerTest, UserValueAndRecommendedUnset) { + scoped_ptr<base::DictionaryValue> merged( + MergeSettingsWithPolicies(policy_.get(), NULL, user_.get(), NULL)); + EXPECT_TRUE(HaveSameValueAt(*merged, *user_, "VPN.OpenVPN.Password")); +} + +TEST_F(ONCMergerTest, UserDictionaryAndNoPolicyValue) { + scoped_ptr<base::DictionaryValue> merged( + MergeSettingsWithPolicies(policy_.get(), NULL, user_.get(), NULL)); + const base::Value* value = NULL; + EXPECT_FALSE(merged->Get("ProxySettings", &value)); +} + +TEST_F(ONCMergerTest, MergeWithEmptyPolicyProhibitsEverything) { + base::DictionaryValue emptyDict; + scoped_ptr<base::DictionaryValue> merged( + MergeSettingsWithPolicies(&emptyDict, NULL, user_.get(), NULL)); + EXPECT_TRUE(merged->empty()); +} + +TEST_F(ONCMergerTest, MergeWithoutPolicyAllowsAnything) { + scoped_ptr<base::DictionaryValue> merged( + MergeSettingsWithPolicies(NULL, NULL, user_.get(), NULL)); + EXPECT_TRUE(test_utils::Equals(user_.get(), merged.get())); +} + +TEST_F(ONCMergerTest, MergeWithoutUserSettings) { + base::DictionaryValue emptyDict; + scoped_ptr<base::DictionaryValue> merged; + + merged = MergeSettingsWithPolicies(policy_.get(), NULL, &emptyDict, NULL); + EXPECT_TRUE(test_utils::Equals(policy_without_recommended_.get(), + merged.get())); + + merged = MergeSettingsWithPolicies(policy_.get(), NULL, NULL, NULL); + EXPECT_TRUE(test_utils::Equals(policy_without_recommended_.get(), + merged.get())); +} + +TEST_F(ONCMergerTest, MandatoryUserPolicyOverwriteDevicePolicy) { + scoped_ptr<base::DictionaryValue> merged(MergeSettingsWithPolicies( + policy_.get(), device_policy_.get(), user_.get(), NULL)); + EXPECT_TRUE(HaveSameValueAt(*merged, *policy_, "VPN.OpenVPN.Port")); +} + +TEST_F(ONCMergerTest, MandatoryDevicePolicyOverwritesRecommendedUserPolicy) { + scoped_ptr<base::DictionaryValue> merged(MergeSettingsWithPolicies( + policy_.get(), device_policy_.get(), user_.get(), NULL)); + EXPECT_TRUE(HaveSameValueAt(*merged, *device_policy_, + "VPN.OpenVPN.Username")); +} + +} // namespace merger +} // namespace onc +} // namespace chromeos diff --git a/chromeos/network/onc/onc_normalizer.cc b/chromeos/network/onc/onc_normalizer.cc new file mode 100644 index 0000000..4f99867 --- /dev/null +++ b/chromeos/network/onc/onc_normalizer.cc @@ -0,0 +1,104 @@ +// 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_normalizer.h" + +#include <string> + +#include "base/logging.h" +#include "base/values.h" +#include "chromeos/network/onc/onc_constants.h" +#include "chromeos/network/onc/onc_signature.h" + +namespace chromeos { +namespace onc { + +Normalizer::Normalizer(bool remove_recommended_fields) + : remove_recommended_fields_(remove_recommended_fields) { +} + +Normalizer::~Normalizer() { +} + +scoped_ptr<base::DictionaryValue> Normalizer::NormalizeObject( + const OncValueSignature* object_signature, + const base::DictionaryValue& onc_object) { + CHECK(object_signature != NULL); + return MapObject(*object_signature, onc_object); +} + +scoped_ptr<base::DictionaryValue> Normalizer::MapObject( + const OncValueSignature& signature, + const base::DictionaryValue& onc_object) { + scoped_ptr<base::DictionaryValue> normalized = + Mapper::MapObject(signature, onc_object); + + if (normalized.get() == NULL) + return scoped_ptr<base::DictionaryValue>(); + + if (remove_recommended_fields_) + normalized->RemoveWithoutPathExpansion(kRecommended, NULL); + + if (&signature == &kNetworkConfigurationSignature) + NormalizeNetworkConfiguration(normalized.get()); + else if (&signature == &kVPNSignature) + NormalizeVPN(normalized.get()); + else if (&signature == &kIPsecSignature) + NormalizeIPsec(normalized.get()); + + return normalized.Pass(); +} + +namespace { +void RemoveEntryUnless(base::DictionaryValue* dict, + const std::string path, + bool condition) { + if (!condition) + dict->RemoveWithoutPathExpansion(path, NULL); +} +} // namespace + +void Normalizer::NormalizeIPsec(base::DictionaryValue* ipsec) { + using namespace vpn; + + std::string auth_type; + ipsec->GetStringWithoutPathExpansion(kAuthenticationType, &auth_type); + RemoveEntryUnless(ipsec, kClientCertType, auth_type == kCert); + RemoveEntryUnless(ipsec, kServerCARef, auth_type == kCert); + RemoveEntryUnless(ipsec, kPSK, auth_type == kPSK); + RemoveEntryUnless(ipsec, kSaveCredentials, auth_type == kPSK); + + std::string clientcert_type; + ipsec->GetStringWithoutPathExpansion(kClientCertType, &clientcert_type); + RemoveEntryUnless(ipsec, kClientCertPattern, + clientcert_type == certificate::kPattern); + RemoveEntryUnless(ipsec, kClientCertRef, + clientcert_type == certificate::kRef); + + int ike_version = -1; + ipsec->GetIntegerWithoutPathExpansion(kIKEVersion, &ike_version); + RemoveEntryUnless(ipsec, kEAP, ike_version == 2); + RemoveEntryUnless(ipsec, kGroup, ike_version == 1); + RemoveEntryUnless(ipsec, kXAUTH, ike_version == 1); +} + +void Normalizer::NormalizeVPN(base::DictionaryValue* vpn) { + using namespace vpn; + std::string type; + vpn->GetStringWithoutPathExpansion(vpn::kType, &type); + RemoveEntryUnless(vpn, kOpenVPN, type == kOpenVPN); + RemoveEntryUnless(vpn, kIPsec, type == kIPsec || type == kTypeL2TP_IPsec); + RemoveEntryUnless(vpn, kL2TP, type == kTypeL2TP_IPsec); +} + +void Normalizer::NormalizeNetworkConfiguration(base::DictionaryValue* network) { + std::string type; + network->GetStringWithoutPathExpansion(kType, &type); + RemoveEntryUnless(network, kEthernet, type == kEthernet); + RemoveEntryUnless(network, kVPN, type == kVPN); + RemoveEntryUnless(network, kWiFi, type == kWiFi); +} + +} // namespace onc +} // namespace chromeos diff --git a/chromeos/network/onc/onc_normalizer.h b/chromeos/network/onc/onc_normalizer.h new file mode 100644 index 0000000..5d00f00 --- /dev/null +++ b/chromeos/network/onc/onc_normalizer.h @@ -0,0 +1,50 @@ +// 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. + +#ifndef CHROMEOS_NETWORK_ONC_ONC_NORMALIZER_H_ +#define CHROMEOS_NETWORK_ONC_ONC_NORMALIZER_H_ + +#include "base/memory/scoped_ptr.h" +#include "chromeos/chromeos_export.h" +#include "chromeos/network/onc/onc_mapper.h" + +namespace chromeos { +namespace onc { + +struct OncValueSignature; + +class CHROMEOS_EXPORT Normalizer : public Mapper { + public: + explicit Normalizer(bool remove_recommended_fields); + virtual ~Normalizer(); + + // Removes all fields that are ignored/irrelevant because of the value of + // other fields. E.g. the "WiFi" field is irrelevant if the configurations + // type is "Ethernet". If |remove_recommended_fields| is true, kRecommended + // arrays are removed (the array itself and not the field names listed + // there). |object_signature| must point to one of the signatures in + // |onc_signature.h|. + scoped_ptr<base::DictionaryValue> NormalizeObject( + const OncValueSignature* object_signature, + const base::DictionaryValue& onc_object); + + private: + // Dispatch to the right normalization function according to |signature|. + virtual scoped_ptr<base::DictionaryValue> MapObject( + const OncValueSignature& signature, + const base::DictionaryValue& onc_object) OVERRIDE; + + void NormalizeIPsec(base::DictionaryValue* ipsec); + void NormalizeVPN(base::DictionaryValue* vpn); + void NormalizeNetworkConfiguration(base::DictionaryValue* network); + + const bool remove_recommended_fields_; + + DISALLOW_COPY_AND_ASSIGN(Normalizer); +}; + +} // namespace onc +} // namespace chromeos + +#endif // CHROMEOS_NETWORK_ONC_ONC_NORMALIZER_H_ diff --git a/chromeos/network/onc/onc_normalizer_unittest.cc b/chromeos/network/onc/onc_normalizer_unittest.cc new file mode 100644 index 0000000..d0c5601 --- /dev/null +++ b/chromeos/network/onc/onc_normalizer_unittest.cc @@ -0,0 +1,33 @@ +// 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_normalizer.h" + +#include "base/memory/scoped_ptr.h" +#include "base/values.h" +#include "chromeos/network/onc/onc_signature.h" +#include "chromeos/network/onc/onc_test_utils.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace chromeos { +namespace onc { + +// This test case is about validating valid ONC objects. +TEST(ONCNormalizerTest, NormalizeNetworkConfiguration) { + Normalizer normalizer(true); + scoped_ptr<const base::DictionaryValue> data( + test_utils::ReadTestDictionary("settings_with_normalization.json")); + + const base::DictionaryValue* original; + const base::DictionaryValue* expected_normalized; + data->GetDictionary("ethernet-and-vpn", &original); + data->GetDictionary("ethernet-and-vpn-normalized", &expected_normalized); + + scoped_ptr<base::DictionaryValue> actual_normalized = + normalizer.NormalizeObject(&kNetworkConfigurationSignature, *original); + EXPECT_TRUE(test_utils::Equals(expected_normalized, actual_normalized.get())); +} + +} // namespace onc +} // namespace chromeos diff --git a/chromeos/network/onc/onc_signature.cc b/chromeos/network/onc/onc_signature.cc new file mode 100644 index 0000000..f71328c --- /dev/null +++ b/chromeos/network/onc/onc_signature.cc @@ -0,0 +1,313 @@ +// 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_signature.h" + +#include "chromeos/network/onc/onc_constants.h" +#include "third_party/cros_system_api/dbus/service_constants.h" + +using base::Value; + +namespace chromeos { +namespace onc { +namespace { + +const OncValueSignature kBoolSignature = { + Value::TYPE_BOOLEAN, NULL +}; +const OncValueSignature kStringSignature = { + Value::TYPE_STRING, NULL +}; +const OncValueSignature kIntegerSignature = { + Value::TYPE_INTEGER, NULL +}; +const OncValueSignature kStringListSignature = { + Value::TYPE_LIST, NULL, &kStringSignature +}; +const OncValueSignature kIPConfigListSignature = { + Value::TYPE_LIST, NULL, &kIPConfigSignature +}; +const OncValueSignature kCertificateListSignature = { + Value::TYPE_LIST, NULL, &kCertificateSignature +}; +const OncValueSignature kNetworkConfigurationListSignature = { + Value::TYPE_LIST, NULL, &kNetworkConfigurationSignature +}; + +const OncFieldSignature issuer_subject_pattern_fields[] = { + { certificate::kCommonName, NULL, &kStringSignature }, + { certificate::kLocality, NULL, &kStringSignature }, + { certificate::kOrganization, NULL, &kStringSignature }, + { certificate::kOrganizationalUnit, NULL, &kStringSignature }, + { NULL } +}; + +const OncFieldSignature certificate_pattern_fields[] = { + { kRecommended, NULL, &kRecommendedSignature }, + { certificate::kEnrollmentURI, NULL, &kStringListSignature }, + { certificate::kIssuer, NULL, &kIssuerSubjectPatternSignature }, + { certificate::kIssuerCARef, NULL, &kStringListSignature }, + { certificate::kSubject, NULL, &kIssuerSubjectPatternSignature }, + { NULL } +}; + +const OncFieldSignature eap_fields[] = { + { kRecommended, NULL, &kRecommendedSignature }, + { eap::kAnonymousIdentity, flimflam::kEapAnonymousIdentityProperty, + &kStringSignature }, + { eap::kClientCertPattern, NULL, &kCertificatePatternSignature }, + { eap::kClientCertRef, NULL, &kStringSignature }, + { eap::kClientCertType, NULL, &kStringSignature }, + { eap::kIdentity, flimflam::kEapIdentityProperty, &kStringSignature }, + { eap::kInner, flimflam::kEapPhase2AuthProperty, &kStringSignature }, + { eap::kOuter, flimflam::kEapMethodProperty, &kStringSignature }, + { eap::kPassword, flimflam::kEapPasswordProperty, &kStringSignature }, + { eap::kSaveCredentials, flimflam::kSaveCredentialsProperty, + &kBoolSignature }, + { eap::kServerCARef, flimflam::kEapCaCertNssProperty, &kStringSignature }, + { eap::kUseSystemCAs, flimflam::kEapUseSystemCasProperty, &kBoolSignature }, + { NULL } +}; + +const OncFieldSignature ipsec_fields[] = { + { kRecommended, NULL, &kRecommendedSignature }, + // Ignored by Shill, not necessary to synchronize. + // Would be: flimflam::kL2tpIpsecAuthenticationType + { vpn::kAuthenticationType, NULL, &kStringSignature }, + { vpn::kClientCertPattern, NULL, &kCertificatePatternSignature }, + { vpn::kClientCertRef, NULL, &kStringSignature }, + { vpn::kClientCertType, NULL, &kStringSignature }, + { vpn::kGroup, flimflam::kL2tpIpsecGroupNameProperty, &kStringSignature }, + // Ignored by Shill, not necessary to synchronize. + // Would be: flimflam::kL2tpIpsecIkeVersion + { vpn::kIKEVersion, NULL, &kIntegerSignature }, + { vpn::kPSK, flimflam::kL2tpIpsecPskProperty, &kStringSignature }, + { vpn::kSaveCredentials, flimflam::kSaveCredentialsProperty, + &kBoolSignature }, + { vpn::kServerCARef, flimflam::kL2tpIpsecCaCertNssProperty, + &kStringSignature }, + // Not yet supported. + // { vpn::kEAP, NULL, &kEAPSignature }, + // { vpn::kXAUTH, NULL, &kXAUTHSignature }, + { NULL } +}; + +const OncFieldSignature l2tp_fields[] = { + { kRecommended, NULL, &kRecommendedSignature }, + { vpn::kPassword, flimflam::kL2tpIpsecPasswordProperty, &kStringSignature }, + // We don't synchronize l2tp's SaveCredentials field for now, as Shill doesn't + // support separate settings for ipsec and l2tp. + { vpn::kSaveCredentials, NULL, &kBoolSignature }, + { vpn::kUsername, flimflam::kL2tpIpsecUserProperty, &kStringSignature }, + { NULL } +}; + +const OncFieldSignature openvpn_fields[] = { + { kRecommended, NULL, &kRecommendedSignature }, + { vpn::kAuth, flimflam::kOpenVPNAuthProperty, &kStringSignature }, + { vpn::kAuthNoCache, flimflam::kOpenVPNAuthNoCacheProperty, &kBoolSignature }, + { vpn::kAuthRetry, flimflam::kOpenVPNAuthRetryProperty, &kStringSignature }, + { vpn::kCipher, flimflam::kOpenVPNCipherProperty, &kStringSignature }, + { vpn::kClientCertPattern, NULL, &kCertificatePatternSignature }, + { vpn::kClientCertRef, NULL, &kStringSignature }, + { vpn::kClientCertType, NULL, &kStringSignature }, + { vpn::kCompLZO, flimflam::kOpenVPNCompLZOProperty, &kStringSignature }, + { vpn::kCompNoAdapt, flimflam::kOpenVPNCompNoAdaptProperty, &kBoolSignature }, + { vpn::kKeyDirection, flimflam::kOpenVPNKeyDirectionProperty, + &kStringSignature }, + { vpn::kNsCertType, flimflam::kOpenVPNNsCertTypeProperty, &kStringSignature }, + { vpn::kPassword, flimflam::kOpenVPNPasswordProperty, &kStringSignature }, + { vpn::kPort, flimflam::kOpenVPNPortProperty, &kIntegerSignature }, + { vpn::kProto, flimflam::kOpenVPNProtoProperty, &kStringSignature }, + { vpn::kPushPeerInfo, flimflam::kOpenVPNPushPeerInfoProperty, + &kBoolSignature }, + { vpn::kRemoteCertEKU, flimflam::kOpenVPNRemoteCertEKUProperty, + &kStringSignature }, + // This field is converted during translation, see onc_translator_*. + { vpn::kRemoteCertKU, NULL, &kStringListSignature }, + { vpn::kRemoteCertTLS, flimflam::kOpenVPNRemoteCertTLSProperty, + &kStringSignature }, + { vpn::kRenegSec, flimflam::kOpenVPNRenegSecProperty, &kIntegerSignature }, + { vpn::kSaveCredentials, flimflam::kSaveCredentialsProperty, + &kBoolSignature }, + { vpn::kServerCARef, flimflam::kOpenVPNCaCertNSSProperty, &kStringSignature }, + { vpn::kServerCertRef, NULL, &kStringSignature }, + { vpn::kServerPollTimeout, flimflam::kOpenVPNServerPollTimeoutProperty, + &kIntegerSignature }, + { vpn::kShaper, flimflam::kOpenVPNShaperProperty, &kIntegerSignature }, + { vpn::kStaticChallenge, flimflam::kOpenVPNStaticChallengeProperty, + &kStringSignature }, + { vpn::kTLSAuthContents, flimflam::kOpenVPNTLSAuthContentsProperty, + &kStringSignature }, + { vpn::kTLSRemote, flimflam::kOpenVPNTLSRemoteProperty, &kStringSignature }, + { vpn::kUsername, flimflam::kOpenVPNUserProperty, &kStringSignature }, + { vpn::kVerb, NULL, &kStringSignature }, + { NULL } +}; + +const OncFieldSignature vpn_fields[] = { + { kRecommended, NULL, &kRecommendedSignature }, + { vpn::kHost, flimflam::kProviderHostProperty, &kStringSignature }, + { vpn::kIPsec, NULL, &kIPsecSignature }, + { vpn::kL2TP, NULL, &kL2TPSignature }, + { vpn::kOpenVPN, NULL, &kOpenVPNSignature }, + // This field is converted during translation, see onc_translator_*. + { kType, NULL, &kStringSignature }, + { NULL } +}; + +const OncFieldSignature ethernet_fields[] = { + { kRecommended, NULL, &kRecommendedSignature }, + { ethernet::kAuthentication, NULL, &kStringSignature }, + { ethernet::kEAP, NULL, &kEAPSignature }, + { NULL } +}; + +const OncFieldSignature ipconfig_fields[] = { + { ipconfig::kGateway, NULL, &kStringSignature }, + { ipconfig::kIPAddress, NULL, &kStringSignature }, + { kNameServers, NULL, &kStringSignature }, + { ipconfig::kRoutingPrefix, NULL, &kIntegerSignature }, + { kSearchDomains, NULL, &kStringListSignature }, + // This field is converted during translation, see onc_translator_*. + { kType, NULL, &kStringSignature }, + { NULL } +}; + +const OncFieldSignature proxy_location_fields[] = { + { proxy::kHost, NULL, &kStringSignature }, + { proxy::kPort, NULL, &kIntegerSignature }, + { NULL } +}; + +const OncFieldSignature proxy_manual_fields[] = { + { proxy::kFtp, NULL, &kProxyLocationSignature }, + { proxy::kHttp, NULL, &kProxyLocationSignature }, + { proxy::kHttps, NULL, &kProxyLocationSignature }, + { proxy::kSocks, NULL, &kProxyLocationSignature }, + { NULL } +}; + +const OncFieldSignature proxy_settings_fields[] = { + { kRecommended, NULL, &kRecommendedSignature }, + { proxy::kExcludeDomains, NULL, &kStringListSignature }, + { proxy::kManual, NULL, &kProxyManualSignature }, + { proxy::kPAC, NULL, &kStringSignature }, + { kType, NULL, &kStringSignature }, + { NULL } +}; + +const OncFieldSignature wifi_fields[] = { + { kRecommended, NULL, &kRecommendedSignature }, + { wifi::kAutoConnect, flimflam::kAutoConnectProperty, &kBoolSignature }, + { wifi::kEAP, NULL, &kEAPSignature }, + { wifi::kHiddenSSID, flimflam::kWifiHiddenSsid, &kBoolSignature }, + { wifi::kPassphrase, flimflam::kPassphraseProperty, &kStringSignature }, + { wifi::kSSID, flimflam::kSSIDProperty, &kStringSignature }, + { wifi::kSecurity, flimflam::kSecurityProperty, &kStringSignature }, + { NULL } +}; + +const OncFieldSignature network_configuration_fields[] = { + { kRecommended, NULL, &kRecommendedSignature }, + { kEthernet, NULL, &kEthernetSignature }, + { kGUID, flimflam::kGuidProperty, &kStringSignature }, + { kIPConfigs, NULL, &kIPConfigListSignature }, + { kName, flimflam::kNameProperty, &kStringSignature }, + { kNameServers, NULL, &kStringListSignature }, + { kProxySettings, NULL, &kProxySettingsSignature }, + { kRemove, NULL, &kBoolSignature }, + { kSearchDomains, NULL, &kStringListSignature }, + // This field is converted during translation, see onc_translator_*. + { kType, NULL, &kStringSignature }, + { kVPN, NULL, &kVPNSignature }, + { kWiFi, NULL, &kWiFiSignature }, + { NULL } +}; + +const OncFieldSignature certificate_fields[] = { + { kGUID, flimflam::kGuidProperty, &kStringSignature }, + { certificate::kPKCS12, NULL, &kStringSignature }, + { kRemove, NULL, &kBoolSignature }, + { certificate::kTrust, NULL, &kStringListSignature }, + { kType, NULL, &kStringSignature }, + { certificate::kX509, NULL, &kStringSignature }, + { NULL } +}; + +const OncFieldSignature unencrypted_configuration_fields[] = { + { kCertificates, NULL, &kCertificateListSignature }, + { kNetworkConfigurations, NULL, &kNetworkConfigurationListSignature }, + { kType, NULL, &kStringSignature }, + { NULL } +}; + +} // namespace + +const OncValueSignature kRecommendedSignature = { + Value::TYPE_LIST, NULL, &kStringSignature +}; +const OncValueSignature kEAPSignature = { + Value::TYPE_DICTIONARY, eap_fields, NULL +}; +const OncValueSignature kIssuerSubjectPatternSignature = { + Value::TYPE_DICTIONARY, issuer_subject_pattern_fields, NULL +}; +const OncValueSignature kCertificatePatternSignature = { + Value::TYPE_DICTIONARY, certificate_pattern_fields, NULL +}; +const OncValueSignature kIPsecSignature = { + Value::TYPE_DICTIONARY, ipsec_fields, NULL +}; +const OncValueSignature kL2TPSignature = { + Value::TYPE_DICTIONARY, l2tp_fields, NULL +}; +const OncValueSignature kOpenVPNSignature = { + Value::TYPE_DICTIONARY, openvpn_fields, NULL +}; +const OncValueSignature kVPNSignature = { + Value::TYPE_DICTIONARY, vpn_fields, NULL +}; +const OncValueSignature kEthernetSignature = { + Value::TYPE_DICTIONARY, ethernet_fields, NULL +}; +const OncValueSignature kIPConfigSignature = { + Value::TYPE_DICTIONARY, ipconfig_fields, NULL +}; +const OncValueSignature kProxyLocationSignature = { + Value::TYPE_DICTIONARY, proxy_location_fields, NULL +}; +const OncValueSignature kProxyManualSignature = { + Value::TYPE_DICTIONARY, proxy_manual_fields, NULL +}; +const OncValueSignature kProxySettingsSignature = { + Value::TYPE_DICTIONARY, proxy_settings_fields, NULL +}; +const OncValueSignature kWiFiSignature = { + Value::TYPE_DICTIONARY, wifi_fields, NULL +}; +const OncValueSignature kCertificateSignature = { + Value::TYPE_DICTIONARY, certificate_fields, NULL +}; +const OncValueSignature kNetworkConfigurationSignature = { + Value::TYPE_DICTIONARY, network_configuration_fields, NULL +}; +const OncValueSignature kUnencryptedConfigurationSignature = { + Value::TYPE_DICTIONARY, unencrypted_configuration_fields, NULL +}; + +const OncFieldSignature* GetFieldSignature(const OncValueSignature& signature, + const std::string& onc_field_name) { + if (!signature.fields) + return NULL; + for (const OncFieldSignature* field_signature = signature.fields; + field_signature->onc_field_name != NULL; ++field_signature) { + if (onc_field_name == field_signature->onc_field_name) + return field_signature; + } + return NULL; +} + +} // namespace onc +} // namespace chromeos diff --git a/chromeos/network/onc/onc_signature.h b/chromeos/network/onc/onc_signature.h new file mode 100644 index 0000000..1a9b507 --- /dev/null +++ b/chromeos/network/onc/onc_signature.h @@ -0,0 +1,55 @@ +// 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. + +#ifndef CHROMEOS_NETWORK_ONC_ONC_SIGNATURE_H_ +#define CHROMEOS_NETWORK_ONC_ONC_SIGNATURE_H_ + +#include <string> + +#include "base/values.h" +#include "chromeos/chromeos_export.h" + +namespace chromeos { +namespace onc { + +struct OncValueSignature; + +struct OncFieldSignature { + const char* onc_field_name; + const char* shill_property_name; + const OncValueSignature* value_signature; +}; + +struct CHROMEOS_EXPORT OncValueSignature { + base::Value::Type onc_type; + const OncFieldSignature* fields; + const OncValueSignature* onc_array_entry_signature; +}; + +const OncFieldSignature* GetFieldSignature(const OncValueSignature& signature, + const std::string& onc_field_name); + +CHROMEOS_EXPORT extern const OncValueSignature kRecommendedSignature; +CHROMEOS_EXPORT extern const OncValueSignature kEAPSignature; +CHROMEOS_EXPORT extern const OncValueSignature kIssuerSubjectPatternSignature; +CHROMEOS_EXPORT extern const OncValueSignature kCertificatePatternSignature; +CHROMEOS_EXPORT extern const OncValueSignature kIPsecSignature; +CHROMEOS_EXPORT extern const OncValueSignature kL2TPSignature; +CHROMEOS_EXPORT extern const OncValueSignature kOpenVPNSignature; +CHROMEOS_EXPORT extern const OncValueSignature kVPNSignature; +CHROMEOS_EXPORT extern const OncValueSignature kEthernetSignature; +CHROMEOS_EXPORT extern const OncValueSignature kIPConfigSignature; +CHROMEOS_EXPORT extern const OncValueSignature kProxyLocationSignature; +CHROMEOS_EXPORT extern const OncValueSignature kProxyManualSignature; +CHROMEOS_EXPORT extern const OncValueSignature kProxySettingsSignature; +CHROMEOS_EXPORT extern const OncValueSignature kWiFiSignature; +CHROMEOS_EXPORT extern const OncValueSignature kCertificateSignature; +CHROMEOS_EXPORT extern const OncValueSignature kNetworkConfigurationSignature; +CHROMEOS_EXPORT extern const OncValueSignature + kUnencryptedConfigurationSignature; + +} // namespace onc +} // namespace chromeos + +#endif // CHROMEOS_NETWORK_ONC_ONC_SIGNATURE_H_ diff --git a/chromeos/network/onc/onc_test_utils.cc b/chromeos/network/onc/onc_test_utils.cc new file mode 100644 index 0000000..0a948bf --- /dev/null +++ b/chromeos/network/onc/onc_test_utils.cc @@ -0,0 +1,68 @@ +// 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_test_utils.h" + +#include "base/file_path.h" +#include "base/json/json_file_value_serializer.h" +#include "base/logging.h" +#include "base/path_service.h" +#include "base/values.h" +#include "chromeos/chromeos_test_utils.h" + +namespace chromeos { +namespace onc { +namespace test_utils { + +namespace { + +// The name of the component directory to get the test data from. +const char kNetworkComponentDirectory[] = "network"; + +} // namespace + +scoped_ptr<base::DictionaryValue> ReadTestDictionary( + const std::string& filename) { + base::DictionaryValue* dict = NULL; + FilePath path; + if (!chromeos::test_utils::GetTestDataPath(kNetworkComponentDirectory, + filename, + &path)) { + NOTREACHED() << "Unable to get test dictionary path for " + << kNetworkComponentDirectory << "/" << filename; + return make_scoped_ptr(dict); + } + + JSONFileValueSerializer serializer(path); + serializer.set_allow_trailing_comma(true); + + std::string error_message; + base::Value* content = serializer.Deserialize(NULL, &error_message); + CHECK(content != NULL) << "Couldn't json-deserialize file '" + << filename << "': " << error_message; + + CHECK(content->GetAsDictionary(&dict)) + << "File '" << filename + << "' does not contain a dictionary as expected, but type " + << content->GetType(); + return make_scoped_ptr(dict); +} + +::testing::AssertionResult Equals(const base::DictionaryValue* expected, + const base::DictionaryValue* actual) { + CHECK(expected != NULL); + if (actual == NULL) + return ::testing::AssertionFailure() << "Actual dictionary pointer is NULL"; + + if (expected->Equals(actual)) + return ::testing::AssertionSuccess() << "Dictionaries are equal"; + + return ::testing::AssertionFailure() << "Dictionaries are unequal.\n" + << "Expected dictionary:\n" << *expected + << "Actual dictionary:\n" << *actual; +} + +} // namespace test_utils +} // namespace onc +} // namespace chromeos diff --git a/chromeos/network/onc/onc_test_utils.h b/chromeos/network/onc/onc_test_utils.h new file mode 100644 index 0000000..bc0d80e --- /dev/null +++ b/chromeos/network/onc/onc_test_utils.h @@ -0,0 +1,36 @@ +// 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. + +#ifndef CHROMEOS_NETWORK_ONC_ONC_TEST_UTILS_H_ +#define CHROMEOS_NETWORK_ONC_ONC_TEST_UTILS_H_ + +#include <string> + +#include "base/memory/scoped_ptr.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace base { +class DictionaryValue; +} + +namespace chromeos { +namespace onc { +namespace test_utils { + +// Read a JSON dictionary from |filename| and return it as a +// DictionaryValue. CHECKs if any error occurs. +scoped_ptr<base::DictionaryValue> ReadTestDictionary( + const std::string& filename); + +// Checks that the pointer |actual| is not NULL but points to a dictionary that +// equals |expected| (using Value::Equals). The intended use case is: +// EXPECT_TRUE(test_utils::Equals(expected, actual)); +::testing::AssertionResult Equals(const base::DictionaryValue* expected, + const base::DictionaryValue* actual); + +} // namespace test_utils +} // namespace onc +} // namespace chromeos + +#endif // CHROMEOS_NETWORK_ONC_ONC_TEST_UTILS_H_ diff --git a/chromeos/network/onc/onc_translation_tables.cc b/chromeos/network/onc/onc_translation_tables.cc new file mode 100644 index 0000000..abe978f --- /dev/null +++ b/chromeos/network/onc/onc_translation_tables.cc @@ -0,0 +1,30 @@ +// 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_translation_tables.h" + +#include <cstddef> + +#include "chromeos/network/onc/onc_constants.h" +#include "third_party/cros_system_api/dbus/service_constants.h" + +namespace chromeos { +namespace onc { + +const StringTranslationEntry kNetworkTypeTable[] = { + { kEthernet, flimflam::kTypeEthernet }, + { kWiFi, flimflam::kTypeWifi }, + { kCellular, flimflam::kTypeCellular }, + { kVPN, flimflam::kTypeVPN }, + { NULL } +}; + +const StringTranslationEntry kVPNTypeTable[] = { + { vpn::kTypeL2TP_IPsec, flimflam::kProviderL2tpIpsec }, + { vpn::kOpenVPN, flimflam::kProviderOpenVpn }, + { NULL } +}; + +} // namespace onc +} // namespace chromeos diff --git a/chromeos/network/onc/onc_translation_tables.h b/chromeos/network/onc/onc_translation_tables.h new file mode 100644 index 0000000..f9b54a8 --- /dev/null +++ b/chromeos/network/onc/onc_translation_tables.h @@ -0,0 +1,24 @@ +// 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. + +#ifndef CHROMEOS_NETWORK_ONC_ONC_TRANSLATION_TABLES_H_ +#define CHROMEOS_NETWORK_ONC_ONC_TRANSLATION_TABLES_H_ + +namespace chromeos { +namespace onc { + +struct StringTranslationEntry { + const char* onc_value; + const char* shill_value; +}; + +// These tables contain the mapping from ONC strings to Shill strings. +// These are NULL-terminated arrays. +extern const StringTranslationEntry kNetworkTypeTable[]; +extern const StringTranslationEntry kVPNTypeTable[]; + +} // namespace onc +} // namespace chromeos + +#endif // CHROMEOS_NETWORK_ONC_ONC_TRANSLATION_TABLES_H_ diff --git a/chromeos/network/onc/onc_translator.h b/chromeos/network/onc/onc_translator.h new file mode 100644 index 0000000..270247c --- /dev/null +++ b/chromeos/network/onc/onc_translator.h @@ -0,0 +1,48 @@ +// 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. + +#ifndef CHROMEOS_NETWORK_ONC_ONC_TRANSLATOR_H_ +#define CHROMEOS_NETWORK_ONC_ONC_TRANSLATOR_H_ + +#include "base/memory/scoped_ptr.h" +#include "chromeos/chromeos_export.h" + +namespace base { +class DictionaryValue; +} + +namespace chromeos { +namespace onc { + +struct OncValueSignature; + +// Translates a hierarchical ONC dictionary |onc_object| to a flat Shill +// dictionary. The |signature| declares the type of |onc_object| and must point +// to one of the signature objects in "onc_signature.h". The resulting Shill +// dictionary is returned. +// +// This function is used to translate network settings from ONC to Shill's +// format before sending them to Shill. +CHROMEOS_EXPORT +scoped_ptr<base::DictionaryValue> TranslateONCObjectToShill( + const OncValueSignature* signature, + const base::DictionaryValue& onc_object); + +// Translates a |shill_dictionary| to an ONC object according to the given +// |onc_signature|. |onc_signature| must point to a signature object in +// "onc_signature.h". The resulting ONC object is returned. +// +// This function is used to translate network settings coming from Shill to ONC +// before sending them to the UI. The result doesn't have to be valid ONC, but +// only a subset of it and includes only the values that are actually required +// by the UI. +CHROMEOS_EXPORT +scoped_ptr<base::DictionaryValue> TranslateShillServiceToONCPart( + const base::DictionaryValue& shill_dictionary, + const OncValueSignature* onc_signature); + +} // namespace onc +} // namespace chromeos + +#endif // CHROMEOS_NETWORK_ONC_ONC_TRANSLATOR_H_ diff --git a/chromeos/network/onc/onc_translator_onc_to_shill.cc b/chromeos/network/onc/onc_translator_onc_to_shill.cc new file mode 100644 index 0000000..cd8a4eb --- /dev/null +++ b/chromeos/network/onc/onc_translator_onc_to_shill.cc @@ -0,0 +1,210 @@ +// 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. + +// The implementation of TranslateONCObjectToShill is structured in two parts: +// - The recursion through the existing ONC hierarchy +// see TranslateONCHierarchy +// - The local translation of an object depending on the associated signature +// see LocalTranslator::TranslateFields + +#include "chromeos/network/onc/onc_translator.h" + +#include <string> + +#include "base/json/json_reader.h" +#include "base/json/json_writer.h" +#include "base/logging.h" +#include "base/values.h" +#include "chromeos/network/onc/onc_constants.h" +#include "chromeos/network/onc/onc_signature.h" +#include "chromeos/network/onc/onc_translation_tables.h" +#include "third_party/cros_system_api/dbus/service_constants.h" + +namespace chromeos { +namespace onc { + +namespace { + +scoped_ptr<base::StringValue> ConvertValueToString(const base::Value& value) { + std::string str; + if (!value.GetAsString(&str)) + base::JSONWriter::Write(&value, &str); + return make_scoped_ptr(base::Value::CreateStringValue(str)); +} + +// This class is responsible to translate the local fields of the given +// |onc_object| according to |onc_signature| into |shill_dictionary|. This +// translation should consider (if possible) only fields of this ONC object and +// not nested objects because recursion is handled by the calling function +// TranslateONCHierarchy. +class LocalTranslator { + public: + LocalTranslator(const OncValueSignature& onc_signature, + const base::DictionaryValue& onc_object, + base::DictionaryValue* shill_dictionary) + : onc_signature_(&onc_signature), + onc_object_(&onc_object), + shill_dictionary_(shill_dictionary) { + } + + void TranslateFields(); + + private: + void TranslateOpenVPN(); + void TranslateVPN(); + void TranslateNetworkConfiguration(); + + // Copies all entries from |onc_object_| to |shill_dictionary_| for which a + // translation (shill_property_name) is defined by |onc_signature_|. + void CopyFieldsAccordingToSignature(); + + // Adds |value| to |shill_dictionary| at the field shill_property_name given + // by the associated signature. Takes ownership of |value|. Does nothing if + // |value| is NULL or the property name cannot be read from the signature. + void AddValueAccordingToSignature(const std::string& onc_field_name, + scoped_ptr<base::Value> value); + + // If existent, translates the entry at |onc_field_name| in |onc_object_| + // using |table|. It is an error if no matching table entry is found. Writes + // the result as entry at |shill_property_name| in |shill_dictionary_|. + void TranslateWithTableAndSet(const std::string& onc_field_name, + const StringTranslationEntry table[], + const std::string& shill_property_name); + + const OncValueSignature* onc_signature_; + const base::DictionaryValue* onc_object_; + base::DictionaryValue* shill_dictionary_; + + DISALLOW_COPY_AND_ASSIGN(LocalTranslator); +}; + +void LocalTranslator::TranslateFields() { + if (onc_signature_ == &kNetworkConfigurationSignature) + TranslateNetworkConfiguration(); + else if (onc_signature_ == &kVPNSignature) + TranslateVPN(); + else if (onc_signature_ == &kOpenVPNSignature) + TranslateOpenVPN(); + else + CopyFieldsAccordingToSignature(); +} + +void LocalTranslator::TranslateOpenVPN() { + // Shill supports only one RemoteCertKU but ONC a list. + // Copy only the first entry if existing. + const base::ListValue* certKUs; + std::string certKU; + if (onc_object_->GetListWithoutPathExpansion(vpn::kRemoteCertKU, &certKUs) && + certKUs->GetString(0, &certKU)) { + shill_dictionary_->SetStringWithoutPathExpansion( + flimflam::kOpenVPNRemoteCertKUProperty, certKU); + } + + for (base::DictionaryValue::Iterator it(*onc_object_); it.HasNext(); + it.Advance()) { + scoped_ptr<base::Value> translated; + if (it.key() == vpn::kSaveCredentials || it.key() == vpn::kRemoteCertKU) { + translated.reset(it.value().DeepCopy()); + } else { + // Shill wants all Provider/VPN fields to be strings. + translated = ConvertValueToString(it.value()); + } + AddValueAccordingToSignature(it.key(), translated.Pass()); + } +} + +void LocalTranslator::TranslateVPN() { + TranslateWithTableAndSet(kType, kVPNTypeTable, + flimflam::kProviderTypeProperty); + CopyFieldsAccordingToSignature(); +} + +void LocalTranslator::TranslateNetworkConfiguration() { + TranslateWithTableAndSet(kType, kNetworkTypeTable, flimflam::kTypeProperty); + CopyFieldsAccordingToSignature(); +} + +void LocalTranslator::CopyFieldsAccordingToSignature() { + for (base::DictionaryValue::Iterator it(*onc_object_); it.HasNext(); + it.Advance()) { + AddValueAccordingToSignature(it.key(), + make_scoped_ptr(it.value().DeepCopy())); + } +} + +void LocalTranslator::AddValueAccordingToSignature( + const std::string& onc_name, + scoped_ptr<base::Value> value) { + if (value.get() == NULL) + return; + const OncFieldSignature* field_signature = + GetFieldSignature(*onc_signature_, onc_name); + DCHECK(field_signature != NULL); + if (field_signature == NULL || field_signature->shill_property_name == NULL) + return; + + shill_dictionary_->SetWithoutPathExpansion( + field_signature->shill_property_name, value.release()); +} + +void LocalTranslator::TranslateWithTableAndSet( + const std::string& onc_field_name, + const StringTranslationEntry table[], + const std::string& shill_property_name) { + std::string onc_value; + if (!onc_object_->GetStringWithoutPathExpansion(onc_field_name, &onc_value)) + return; + + for (int i = 0; table[i].onc_value != NULL; ++i) { + if (onc_value != table[i].onc_value) + continue; + shill_dictionary_->SetStringWithoutPathExpansion(shill_property_name, + table[i].shill_value); + return; + } + // As we previously validate ONC, this case should never occur. If it still + // occurs, we should check here. Otherwise the failure will only show up much + // later in Shill. + LOG(ERROR) << "Value '" << onc_value << "' for field '" + << onc_field_name << "' cannot be translated to Shill"; +} + +// Iterates recursively over |onc_object| and its |signature|. At each object +// applies the local translation using LocalTranslator::TranslateFields. The +// results are written to |shill_dictionary|. +void TranslateONCHierarchy(const OncValueSignature& signature, + const base::DictionaryValue& onc_object, + base::DictionaryValue* shill_dictionary) { + // Translates fields of |onc_object| and writes them to |shill_dictionary_|. + LocalTranslator translator(signature, onc_object, shill_dictionary); + translator.TranslateFields(); + + // Recurse into nested objects. + for (base::DictionaryValue::Iterator it(onc_object); it.HasNext(); + it.Advance()) { + const base::DictionaryValue* inner_object; + if (!it.value().GetAsDictionary(&inner_object)) + continue; + + const OncFieldSignature* field_signature = + GetFieldSignature(signature, it.key()); + + TranslateONCHierarchy(*field_signature->value_signature, *inner_object, + shill_dictionary); + } +} + +} // namespace + +scoped_ptr<base::DictionaryValue> TranslateONCObjectToShill( + const OncValueSignature* onc_signature, + const base::DictionaryValue& onc_object) { + CHECK(onc_signature != NULL); + scoped_ptr<base::DictionaryValue> shill_dictionary(new base::DictionaryValue); + TranslateONCHierarchy(*onc_signature, onc_object, shill_dictionary.get()); + return shill_dictionary.Pass(); +} + +} // namespace onc +} // namespace chromeos diff --git a/chromeos/network/onc/onc_translator_shill_to_onc.cc b/chromeos/network/onc/onc_translator_shill_to_onc.cc new file mode 100644 index 0000000..c33e0a9 --- /dev/null +++ b/chromeos/network/onc/onc_translator_shill_to_onc.cc @@ -0,0 +1,231 @@ +// 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_translator.h" + +#include <string> + +#include "base/basictypes.h" +#include "base/json/json_reader.h" +#include "base/json/json_writer.h" +#include "base/logging.h" +#include "base/values.h" +#include "chromeos/network/onc/onc_constants.h" +#include "chromeos/network/onc/onc_signature.h" +#include "chromeos/network/onc/onc_translation_tables.h" +#include "third_party/cros_system_api/dbus/service_constants.h" + +namespace chromeos { +namespace onc { + +namespace { + +// Converts |str| to a base::Value of the given |type|. If the conversion fails, +// returns NULL. +scoped_ptr<base::Value> ConvertStringToValue(const std::string& str, + base::Value::Type type) { + base::Value* value; + if (type == base::Value::TYPE_STRING) + value = base::Value::CreateStringValue(str); + else + value = base::JSONReader::Read(str); + + if (value == NULL || value->GetType() != type) { + delete value; + value = NULL; + } + return make_scoped_ptr(value); +} + +// This class implements the translation of properties from the given +// |shill_dictionary| to a new ONC object of signature |onc_signature|. Using +// recursive calls to TranslateShillServiceToONCPart, nested objects are +// translated. +class ShillToONCTranslator { + public: + ShillToONCTranslator(const base::DictionaryValue& shill_dictionary, + const OncValueSignature& onc_signature) + : shill_dictionary_(&shill_dictionary), + onc_signature_(&onc_signature) { + } + + // Translates the associated Shill dictionary and creates an ONC object of the + // given signature. + scoped_ptr<base::DictionaryValue> CreateTranslatedONCObject(); + + private: + void TranslateOpenVPN(); + void TranslateVPN(); + void TranslateNetworkConfiguration(); + + // Creates an ONC object from |shill_dictionary| according to the signature + // associated to |onc_field_name| and adds it to |onc_object_| at + // |onc_field_name|. + void TranslateAndAddNestedObject(const std::string& onc_field_name); + + // Copies all entries from |shill_dictionary_| to |onc_object_| for which a + // translation (shill_property_name) is defined by |onc_signature_|. + void CopyPropertiesAccordingToSignature(); + + // If existent, translates the entry at |shill_property_name| in + // |shill_dictionary_| using |table|. It is an error if no matching table + // entry is found. Writes the result as entry at |onc_field_name| in + // |onc_object_|. + void TranslateWithTableAndSet(const std::string& shill_property_name, + const StringTranslationEntry table[], + const std::string& onc_field_name); + + const base::DictionaryValue* shill_dictionary_; + const OncValueSignature* onc_signature_; + scoped_ptr<base::DictionaryValue> onc_object_; + + DISALLOW_COPY_AND_ASSIGN(ShillToONCTranslator); +}; + +scoped_ptr<base::DictionaryValue> +ShillToONCTranslator::CreateTranslatedONCObject() { + onc_object_.reset(new base::DictionaryValue); + if (onc_signature_ == &kNetworkConfigurationSignature) + TranslateNetworkConfiguration(); + else if (onc_signature_ == &kVPNSignature) + TranslateVPN(); + else if (onc_signature_ == &kOpenVPNSignature) + TranslateOpenVPN(); + else + CopyPropertiesAccordingToSignature(); + return onc_object_.Pass(); +} + +void ShillToONCTranslator::TranslateOpenVPN() { + // Shill supports only one RemoteCertKU but ONC requires a list. If existing, + // wraps the value into a list. + std::string certKU; + if (shill_dictionary_->GetStringWithoutPathExpansion( + flimflam::kOpenVPNRemoteCertKUProperty, &certKU)) { + scoped_ptr<base::ListValue> certKUs(new base::ListValue); + certKUs->AppendString(certKU); + onc_object_->SetWithoutPathExpansion(vpn::kRemoteCertKU, certKUs.release()); + } + + for (const OncFieldSignature* field_signature = onc_signature_->fields; + field_signature->onc_field_name != NULL; ++field_signature) { + const base::Value* shill_value; + if (field_signature->shill_property_name == NULL || + !shill_dictionary_->GetWithoutPathExpansion( + field_signature->shill_property_name, &shill_value)) { + continue; + } + + scoped_ptr<base::Value> translated; + std::string shill_str; + const std::string& onc_field_name = field_signature->onc_field_name; + if (onc_field_name == vpn::kSaveCredentials || + onc_field_name == vpn::kRemoteCertKU) { + translated.reset(shill_value->DeepCopy()); + } else if (shill_value->GetAsString(&shill_str)) { + // Shill wants all Provider/VPN fields to be strings. Translates these + // strings back to the correct ONC type. + translated = ConvertStringToValue( + shill_str, + field_signature->value_signature->onc_type); + + if (translated.get() == NULL) { + LOG(ERROR) << "Shill property '" << field_signature->shill_property_name + << "' with value '" << shill_value + << "' couldn't be converted to base::Value::Type " + << field_signature->value_signature->onc_type; + } + } else { + LOG(ERROR) << "Shill property '" << field_signature->shill_property_name + << "' has value '" << shill_value + << "', but expected a string"; + } + onc_object_->SetWithoutPathExpansion(onc_field_name, translated.release()); + } +} + +void ShillToONCTranslator::TranslateVPN() { + TranslateWithTableAndSet(flimflam::kProviderTypeProperty, kVPNTypeTable, + kType); + CopyPropertiesAccordingToSignature(); + + std::string vpn_type; + if (onc_object_->GetStringWithoutPathExpansion(kType, &vpn_type)) { + if (vpn_type == vpn::kTypeL2TP_IPsec) { + TranslateAndAddNestedObject(vpn::kIPsec); + TranslateAndAddNestedObject(vpn::kL2TP); + } else { + TranslateAndAddNestedObject(vpn_type); + } + } +} + +void ShillToONCTranslator::TranslateAndAddNestedObject( + const std::string& onc_field_name) { + const OncFieldSignature* field_signature = + GetFieldSignature(*onc_signature_, onc_field_name); + ShillToONCTranslator nested_translator(*shill_dictionary_, + *field_signature->value_signature); + scoped_ptr<base::DictionaryValue> nested_object = + nested_translator.CreateTranslatedONCObject(); + onc_object_->SetWithoutPathExpansion(onc_field_name, nested_object.release()); +} + +void ShillToONCTranslator::TranslateNetworkConfiguration() { + TranslateWithTableAndSet(flimflam::kTypeProperty, kNetworkTypeTable, kType); + CopyPropertiesAccordingToSignature(); + + std::string network_type; + if (onc_object_->GetStringWithoutPathExpansion(kType, &network_type)) + TranslateAndAddNestedObject(network_type); +} + +void ShillToONCTranslator::CopyPropertiesAccordingToSignature() { + for (const OncFieldSignature* field_signature = onc_signature_->fields; + field_signature->onc_field_name != NULL; ++field_signature) { + const base::Value* shill_value; + if (field_signature->shill_property_name == NULL || + !shill_dictionary_->GetWithoutPathExpansion( + field_signature->shill_property_name, &shill_value)) { + continue; + } + onc_object_->SetWithoutPathExpansion( + field_signature->onc_field_name, shill_value->DeepCopy()); + } +} + +void ShillToONCTranslator::TranslateWithTableAndSet( + const std::string& shill_property_name, + const StringTranslationEntry table[], + const std::string& onc_field_name) { + std::string shill_value; + if (!shill_dictionary_->GetStringWithoutPathExpansion(shill_property_name, + &shill_value)) { + return; + } + + for (int i = 0; table[i].onc_value != NULL; ++i) { + if (shill_value != table[i].shill_value) + continue; + onc_object_->SetStringWithoutPathExpansion(onc_field_name, + table[i].onc_value); + return; + } + LOG(ERROR) << "Shill property '" << shill_property_name << "' with value '" + << shill_value << "' couldn't be translated to ONC"; +} + +} // namespace + +scoped_ptr<base::DictionaryValue> TranslateShillServiceToONCPart( + const base::DictionaryValue& shill_dictionary, + const OncValueSignature* onc_signature) { + CHECK(onc_signature != NULL); + + ShillToONCTranslator translator(shill_dictionary, *onc_signature); + return translator.CreateTranslatedONCObject(); +} + +} // namespace onc +} // namespace chromeos diff --git a/chromeos/network/onc/onc_translator_unittest.cc b/chromeos/network/onc/onc_translator_unittest.cc new file mode 100644 index 0000000..a9b8fb5 --- /dev/null +++ b/chromeos/network/onc/onc_translator_unittest.cc @@ -0,0 +1,86 @@ +// 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_translator.h" + +#include "base/memory/scoped_ptr.h" +#include "base/values.h" +#include "chromeos/network/onc/onc_constants.h" +#include "chromeos/network/onc/onc_signature.h" +#include "chromeos/network/onc/onc_test_utils.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace chromeos { +namespace onc { + +// First parameter: Filename of source ONC. +// Second parameter: Filename of expected translated Shill json. +class ONCTranslatorOncToShillTest + : public ::testing::TestWithParam<std::pair<std::string, std::string> > { +}; + +// Test the translation from ONC to Shill json. +TEST_P(ONCTranslatorOncToShillTest, Translate) { + std::string source_onc_filename = GetParam().first; + scoped_ptr<const base::DictionaryValue> onc_network( + test_utils::ReadTestDictionary(source_onc_filename)); + std::string result_json_filename = GetParam().second; + scoped_ptr<const base::DictionaryValue> shill_network( + test_utils::ReadTestDictionary(result_json_filename)); + + scoped_ptr<base::DictionaryValue> translation(TranslateONCObjectToShill( + &kNetworkConfigurationSignature, *onc_network)); + + EXPECT_TRUE(test_utils::Equals(shill_network.get(), translation.get())); +} + +// Test different network types, such that each ONC object type is tested at +// least once. +INSTANTIATE_TEST_CASE_P( + ONCTranslatorOncToShillTest, + ONCTranslatorOncToShillTest, + ::testing::Values( + std::make_pair("valid.onc", "shill_ethernet.json"), + std::make_pair("valid_l2tpipsec.onc", "shill_l2tpipsec.json"), + std::make_pair("valid_openvpn.onc", "shill_openvpn.json"))); + +// Test the translation from Shill json to ONC. +// +// Note: This translation direction doesn't have to reconstruct all of the ONC +// fields, as Chrome doesn't need all of a Service's properties. +TEST(ONCTranslatorShillToOncTest, L2TPIPsec) { + scoped_ptr<base::DictionaryValue> onc_network( + test_utils::ReadTestDictionary("valid_l2tpipsec.onc")); + + // These two fields are part of the ONC (and are required). However, they + // don't exist explicitly in the Shill dictionary. As there is no use-case + // yet, that requires to reconstruct these fields from a Shill dictionary, we + // don't require their translation. + onc_network->Remove("VPN.IPsec.AuthenticationType", NULL); + onc_network->Remove("VPN.IPsec.IKEVersion", NULL); + + scoped_ptr<const base::DictionaryValue> shill_network( + test_utils::ReadTestDictionary("shill_l2tpipsec.json")); + + scoped_ptr<base::DictionaryValue> translation(TranslateShillServiceToONCPart( + *shill_network, &kNetworkConfigurationSignature)); + + EXPECT_TRUE(test_utils::Equals(onc_network.get(), translation.get())); +} + +TEST(ONCTranslatorShillToOncTest, OpenVPN) { + scoped_ptr<const base::DictionaryValue> onc_network( + test_utils::ReadTestDictionary("valid_openvpn.onc")); + + scoped_ptr<const base::DictionaryValue> shill_network( + test_utils::ReadTestDictionary("shill_openvpn.json")); + + scoped_ptr<base::DictionaryValue> translation(TranslateShillServiceToONCPart( + *shill_network, &kNetworkConfigurationSignature)); + + EXPECT_TRUE(test_utils::Equals(onc_network.get(), translation.get())); +} + +} // namespace onc +} // namespace chromeos diff --git a/chromeos/network/onc/onc_utils.cc b/chromeos/network/onc/onc_utils.cc new file mode 100644 index 0000000..62e0d46 --- /dev/null +++ b/chromeos/network/onc/onc_utils.cc @@ -0,0 +1,164 @@ +// 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_utils.h" + +#include "base/base64.h" +#include "base/json/json_reader.h" +#include "base/logging.h" +#include "base/values.h" +#include "chromeos/network/network_event_log.h" +#include "chromeos/network/onc/onc_constants.h" +#include "crypto/encryptor.h" +#include "crypto/hmac.h" +#include "crypto/symmetric_key.h" + +#define ONC_LOG_WARNING(message) NET_LOG_WARNING("ONC", message) +#define ONC_LOG_ERROR(message) NET_LOG_ERROR("ONC", message) + +namespace chromeos { +namespace onc { + +namespace { + +const char kUnableToDecrypt[] = "Unable to decrypt encrypted ONC"; +const char kUnableToDecode[] = "Unable to decode encrypted ONC"; + +} // namespace + +scoped_ptr<base::DictionaryValue> ReadDictionaryFromJson( + const std::string& json) { + std::string error; + base::Value* root = base::JSONReader::ReadAndReturnError( + json, base::JSON_ALLOW_TRAILING_COMMAS, NULL, &error); + + base::DictionaryValue* dict_ptr = NULL; + if (!root || !root->GetAsDictionary(&dict_ptr)) { + ONC_LOG_ERROR("Invalid JSON Dictionary: " + error); + delete root; + } + + return make_scoped_ptr(dict_ptr); +} + +scoped_ptr<base::DictionaryValue> Decrypt(const std::string& passphrase, + const 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(encrypted::kCiphertext, &ciphertext) || + !root.GetString(encrypted::kCipher, &cipher) || + !root.GetString(encrypted::kHMAC, &hmac) || + !root.GetString(encrypted::kHMACMethod, &hmac_method) || + !root.GetString(encrypted::kIV, &initial_vector) || + !root.GetInteger(encrypted::kIterations, &iterations) || + !root.GetString(encrypted::kSalt, &salt) || + !root.GetString(encrypted::kStretch, &stretch_method) || + !root.GetString(encrypted::kType, &onc_type) || + onc_type != kEncryptedConfiguration) { + + ONC_LOG_ERROR("Encrypted ONC malformed."); + return scoped_ptr<base::DictionaryValue>(); + } + + if (hmac_method != encrypted::kSHA1 || + cipher != encrypted::kAES256 || + stretch_method != encrypted::kPBKDF2) { + ONC_LOG_ERROR("Encrypted ONC unsupported encryption scheme."); + return scoped_ptr<base::DictionaryValue>(); + } + + // Make sure iterations != 0, since that's not valid. + if (iterations == 0) { + ONC_LOG_ERROR(kUnableToDecrypt); + return scoped_ptr<base::DictionaryValue>(); + } + + // 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) { + ONC_LOG_ERROR("Too many iterations in encrypted ONC"); + return scoped_ptr<base::DictionaryValue>(); + } + + if (!base::Base64Decode(salt, &salt)) { + ONC_LOG_ERROR(kUnableToDecode); + return scoped_ptr<base::DictionaryValue>(); + } + + scoped_ptr<crypto::SymmetricKey> key( + crypto::SymmetricKey::DeriveKeyFromPassword(crypto::SymmetricKey::AES, + passphrase, + salt, + iterations, + kKeySizeInBits)); + + if (!base::Base64Decode(initial_vector, &initial_vector)) { + ONC_LOG_ERROR(kUnableToDecode); + return scoped_ptr<base::DictionaryValue>(); + } + if (!base::Base64Decode(ciphertext, &ciphertext)) { + ONC_LOG_ERROR(kUnableToDecode); + return scoped_ptr<base::DictionaryValue>(); + } + if (!base::Base64Decode(hmac, &hmac)) { + ONC_LOG_ERROR(kUnableToDecode); + return scoped_ptr<base::DictionaryValue>(); + } + + crypto::HMAC hmac_verifier(crypto::HMAC::SHA1); + if (!hmac_verifier.Init(key.get()) || + !hmac_verifier.Verify(ciphertext, hmac)) { + ONC_LOG_ERROR(kUnableToDecrypt); + return scoped_ptr<base::DictionaryValue>(); + } + + crypto::Encryptor decryptor; + if (!decryptor.Init(key.get(), crypto::Encryptor::CBC, initial_vector)) { + ONC_LOG_ERROR(kUnableToDecrypt); + return scoped_ptr<base::DictionaryValue>(); + } + + std::string plaintext; + if (!decryptor.Decrypt(ciphertext, &plaintext)) { + ONC_LOG_ERROR(kUnableToDecrypt); + return scoped_ptr<base::DictionaryValue>(); + } + + scoped_ptr<base::DictionaryValue> new_root = + ReadDictionaryFromJson(plaintext); + if (new_root.get() == NULL) { + ONC_LOG_ERROR("Property dictionary malformed."); + return scoped_ptr<base::DictionaryValue>(); + } + + return new_root.Pass(); +} + +std::string GetSourceAsString(ONCSource source) { + switch (source) { + case ONC_SOURCE_DEVICE_POLICY: + return "device policy"; + case ONC_SOURCE_USER_POLICY: + return "user policy"; + case ONC_SOURCE_NONE: + return "none"; + case ONC_SOURCE_USER_IMPORT: + return "user import"; + } + NOTREACHED() << "unknown ONC source " << source; + return "unknown"; +} + +} // chromeos +} // onc diff --git a/chromeos/network/onc/onc_utils.h b/chromeos/network/onc/onc_utils.h new file mode 100644 index 0000000..96b8a1e --- /dev/null +++ b/chromeos/network/onc/onc_utils.h @@ -0,0 +1,40 @@ +// 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. + +#ifndef CHROMEOS_NETWORK_ONC_ONC_UTILS_H_ +#define CHROMEOS_NETWORK_ONC_ONC_UTILS_H_ + +#include <string> + +#include "base/memory/scoped_ptr.h" +#include "chromeos/chromeos_export.h" +#include "chromeos/network/onc/onc_constants.h" + +namespace base { +class DictionaryValue; +} + +namespace chromeos { +namespace onc { + +// Parses |json| according to the JSON format. If |json| is a JSON formatted +// dictionary, the function returns the dictionary as a DictionaryValue. +// Otherwise returns NULL. +CHROMEOS_EXPORT scoped_ptr<base::DictionaryValue> ReadDictionaryFromJson( + const std::string& json); + +// Decrypt the given EncryptedConfiguration |onc| (see the ONC specification) +// using |passphrase|. The resulting UnencryptedConfiguration is returned. If an +// error occurs, returns NULL. +CHROMEOS_EXPORT scoped_ptr<base::DictionaryValue> Decrypt( + const std::string& passphrase, + const base::DictionaryValue& onc); + +// For logging only: strings not user facing. +CHROMEOS_EXPORT std::string GetSourceAsString(ONCSource source); + +} // chromeos +} // onc + +#endif // CHROMEOS_NETWORK_ONC_ONC_UTILS_H_ diff --git a/chromeos/network/onc/onc_utils_unittest.cc b/chromeos/network/onc/onc_utils_unittest.cc new file mode 100644 index 0000000..93ed390 --- /dev/null +++ b/chromeos/network/onc/onc_utils_unittest.cc @@ -0,0 +1,53 @@ +// 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_utils.h" + +#include <string> + +#include "base/values.h" +#include "chromeos/network/onc/onc_test_utils.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace chromeos { +namespace onc { + +TEST(ONCDecrypterTest, BrokenEncryptionIterations) { + scoped_ptr<base::DictionaryValue> encrypted_onc = + test_utils::ReadTestDictionary("broken-encrypted-iterations.onc"); + + scoped_ptr<base::DictionaryValue> decrypted_onc = + Decrypt("test0000", *encrypted_onc); + + EXPECT_EQ(NULL, decrypted_onc.get()); +} + +TEST(ONCDecrypterTest, BrokenEncryptionZeroIterations) { + scoped_ptr<base::DictionaryValue> encrypted_onc = + test_utils::ReadTestDictionary("broken-encrypted-zero-iterations.onc"); + + std::string error; + scoped_ptr<base::DictionaryValue> decrypted_onc = + Decrypt("test0000", *encrypted_onc); + + EXPECT_EQ(NULL, decrypted_onc.get()); +} + +TEST(ONCDecrypterTest, LoadEncryptedOnc) { + scoped_ptr<base::DictionaryValue> encrypted_onc = + test_utils::ReadTestDictionary("encrypted.onc"); + scoped_ptr<base::DictionaryValue> expected_decrypted_onc = + test_utils::ReadTestDictionary("decrypted.onc"); + + std::string error; + scoped_ptr<base::DictionaryValue> actual_decrypted_onc = + Decrypt("test0000", *encrypted_onc); + + base::DictionaryValue emptyDict; + EXPECT_TRUE(test_utils::Equals(expected_decrypted_onc.get(), + actual_decrypted_onc.get())); +} + +} // namespace onc +} // namespace chromeos diff --git a/chromeos/network/onc/onc_validator.cc b/chromeos/network/onc/onc_validator.cc new file mode 100644 index 0000000..684eca3 --- /dev/null +++ b/chromeos/network/onc/onc_validator.cc @@ -0,0 +1,562 @@ +// 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 <algorithm> +#include <string> + +#include "base/logging.h" +#include "base/string_util.h" +#include "base/values.h" +#include "chromeos/network/onc/onc_constants.h" +#include "chromeos/network/onc/onc_signature.h" + +namespace chromeos { +namespace onc { + +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) { +} + +Validator::~Validator() { +} + +scoped_ptr<base::DictionaryValue> Validator::ValidateAndRepairObject( + const OncValueSignature* object_signature, + const base::DictionaryValue& onc_object) { + CHECK(object_signature != NULL); + scoped_ptr<base::Value> result_value = + MapValue(*object_signature, onc_object); + base::DictionaryValue* result_dict = NULL; + if (result_value.get() != NULL) { + result_value.release()->GetAsDictionary(&result_dict); + CHECK(result_dict != NULL); + } + + return make_scoped_ptr(result_dict); +} + +scoped_ptr<base::Value> Validator::MapValue( + const OncValueSignature& signature, + const base::Value& onc_value) { + if (onc_value.GetType() != signature.onc_type) { + DVLOG(1) << "Wrong type. Expected " << signature.onc_type + << ", but found " << onc_value.GetType(); + return scoped_ptr<base::Value>(); + } + + scoped_ptr<base::Value> repaired = Mapper::MapValue(signature, onc_value); + if (repaired.get() != NULL) + CHECK_EQ(repaired->GetType(), signature.onc_type); + return repaired.Pass(); +} + +scoped_ptr<base::DictionaryValue> Validator::MapObject( + const OncValueSignature& signature, + const base::DictionaryValue& onc_object) { + scoped_ptr<base::DictionaryValue> repaired(new base::DictionaryValue); + + bool valid; + if (&signature == &kNetworkConfigurationSignature) + valid = ValidateNetworkConfiguration(onc_object, repaired.get()); + else if (&signature == &kEthernetSignature) + valid = ValidateEthernet(onc_object, repaired.get()); + else if (&signature == &kIPConfigSignature) + valid = ValidateIPConfig(onc_object, repaired.get()); + else if (&signature == &kWiFiSignature) + valid = ValidateWiFi(onc_object, repaired.get()); + else if (&signature == &kVPNSignature) + valid = ValidateVPN(onc_object, repaired.get()); + else if (&signature == &kIPsecSignature) + valid = ValidateIPsec(onc_object, repaired.get()); + else if (&signature == &kOpenVPNSignature) + valid = ValidateOpenVPN(onc_object, repaired.get()); + else if (&signature == &kCertificatePatternSignature) + valid = ValidateCertificatePattern(onc_object, repaired.get()); + else if (&signature == &kProxySettingsSignature) + valid = ValidateProxySettings(onc_object, repaired.get()); + else if (&signature == &kProxyLocationSignature) + valid = ValidateProxyLocation(onc_object, repaired.get()); + else if (&signature == &kEAPSignature) + valid = ValidateEAP(onc_object, repaired.get()); + else if (&signature == &kCertificateSignature) + valid = ValidateCertificate(onc_object, repaired.get()); + else + valid = ValidateObjectDefault(signature, onc_object, repaired.get()); + + if (valid) + return repaired.Pass(); + else + return scoped_ptr<base::DictionaryValue>(); +} + +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 (nested_error_occured) + return false; + + if (found_unknown_field) { + if (error_on_unknown_field_) { + DVLOG(1) << "Unknown field name. Aborting."; + return false; + } + DVLOG(1) << "Unknown field name. Ignoring."; + } + + return ValidateRecommendedField(signature, result); +} + +bool Validator::ValidateRecommendedField( + const OncValueSignature& object_signature, + base::DictionaryValue* result) { + CHECK(result != NULL); + + scoped_ptr<base::ListValue> recommended; + base::Value* recommended_value; + // This remove passes ownership to |recommended_value|. + if (!result->RemoveWithoutPathExpansion(onc::kRecommended, + &recommended_value)) { + return true; + } + base::ListValue* recommended_list; + recommended_value->GetAsList(&recommended_list); + CHECK(recommended_list != NULL); + + recommended.reset(recommended_list); + + if (!managed_onc_) { + DVLOG(1) << "Found a " << onc::kRecommended + << " field in unmanaged ONC. Removing it."; + return true; + } + + scoped_ptr<base::ListValue> repaired_recommended(new base::ListValue); + for (base::ListValue::iterator it = recommended->begin(); + it != recommended->end(); ++it) { + std::string field_name; + if (!(*it)->GetAsString(&field_name)) { + NOTREACHED(); + continue; + } + + const OncFieldSignature* field_signature = + GetFieldSignature(object_signature, field_name); + + bool found_error = false; + std::string error_cause; + if (field_signature == NULL) { + 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) { + DVLOG(1) << "Found " << error_cause << " field name '" << field_name + << "' in kRecommended array. " + << (error_on_wrong_recommended_ ? "Aborting." : "Ignoring."); + if (error_on_wrong_recommended_) + return false; + else + continue; + } + + repaired_recommended->Append((*it)->DeepCopy()); + } + + result->Set(onc::kRecommended, repaired_recommended.release()); + return true; +} + +namespace { + +std::string JoinStringRange(const char** range_begin, + const char** range_end, + const std::string& separator) { + std::vector<std::string> string_vector; + std::copy(range_begin, range_end, std::back_inserter(string_vector)); + return JoinString(string_vector, separator); +} + +bool RequireAnyOf(const std::string &actual, const char** valid_values) { + const char** it = valid_values; + for (; *it != NULL; ++it) { + if (actual == *it) + return true; + } + DVLOG(1) << "Found " << actual << ", but expected one of " + << JoinStringRange(valid_values, it, ", "); + return false; +} + +bool IsInRange(int actual, int lower_bound, int upper_bound) { + if (lower_bound <= actual && actual <= upper_bound) + return true; + DVLOG(1) << "Found " << actual << ", which is out of range [" << lower_bound + << ", " << upper_bound << "]"; + return false; +} + +bool RequireField(const base::DictionaryValue& dict, std::string key) { + if (dict.HasKey(key)) + return true; + DVLOG(1) << "Required field " << key << " missing."; + return false; +} + +} // namespace + +bool Validator::ValidateNetworkConfiguration( + const base::DictionaryValue& onc_object, + base::DictionaryValue* result) { + if (!ValidateObjectDefault(kNetworkConfigurationSignature, + onc_object, result)) { + return false; + } + + std::string type; + static const char* kValidTypes[] = { kEthernet, kVPN, kWiFi, NULL }; + if (result->GetStringWithoutPathExpansion(kType, &type) && + !RequireAnyOf(type, kValidTypes)) { + return false; + } + + bool allRequiredExist = RequireField(*result, kGUID); + + bool remove = false; + result->GetBooleanWithoutPathExpansion(kRemove, &remove); + if (!remove) { + allRequiredExist &= RequireField(*result, kName); + allRequiredExist &= RequireField(*result, kType); + allRequiredExist &= type.empty() || RequireField(*result, type); + } + + return !error_on_missing_field_ || allRequiredExist; +} + +bool Validator::ValidateEthernet( + const base::DictionaryValue& onc_object, + base::DictionaryValue* result) { + using namespace onc::ethernet; + if (!ValidateObjectDefault(kEthernetSignature, onc_object, result)) + return false; + + std::string auth; + static const char* kValidAuthentications[] = { kNone, k8021X, NULL }; + if (result->GetStringWithoutPathExpansion(kAuthentication, &auth) && + !RequireAnyOf(auth, kValidAuthentications)) { + return false; + } + + bool allRequiredExist = true; + if (auth == k8021X) + allRequiredExist &= RequireField(*result, kEAP); + + return !error_on_missing_field_ || allRequiredExist; +} + +bool Validator::ValidateIPConfig( + const base::DictionaryValue& onc_object, + base::DictionaryValue* result) { + using namespace onc::ipconfig; + if (!ValidateObjectDefault(kIPConfigSignature, onc_object, result)) + return false; + + std::string type; + static const char* kValidTypes[] = { kIPv4, kIPv6, NULL }; + if (result->GetStringWithoutPathExpansion(ipconfig::kType, &type) && + !RequireAnyOf(type, kValidTypes)) { + return false; + } + + int routing_prefix; + int lower_bound = 1; + // In case of missing type, choose higher upper_bound. + int upper_bound = (type == kIPv4) ? 32 : 128; + if (result->GetIntegerWithoutPathExpansion(kRoutingPrefix, &routing_prefix) && + !IsInRange(routing_prefix, lower_bound, upper_bound)) { + return false; + } + + bool allRequiredExist = RequireField(*result, kIPAddress) & + RequireField(*result, kRoutingPrefix) & + RequireField(*result, ipconfig::kType); + + return !error_on_missing_field_ || allRequiredExist; +} + +bool Validator::ValidateWiFi( + const base::DictionaryValue& onc_object, + base::DictionaryValue* result) { + using namespace onc::wifi; + if (!ValidateObjectDefault(kWiFiSignature, onc_object, result)) + return false; + + std::string security; + static const char* kValidSecurities[] = + { kNone, kWEP_PSK, kWEP_8021X, kWPA_PSK, kWPA_EAP, NULL }; + if (result->GetStringWithoutPathExpansion(kSecurity, &security) && + !RequireAnyOf(security, kValidSecurities)) { + return false; + } + + bool allRequiredExist = RequireField(*result, kSecurity) & + RequireField(*result, kSSID); + if (security == kWEP_8021X || security == kWPA_EAP) + allRequiredExist &= RequireField(*result, kEAP); + else if (security == kWEP_PSK || security == kWPA_PSK) + allRequiredExist &= RequireField(*result, kPassphrase); + + return !error_on_missing_field_ || allRequiredExist; +} + +bool Validator::ValidateVPN( + const base::DictionaryValue& onc_object, + base::DictionaryValue* result) { + using namespace vpn; + if (!ValidateObjectDefault(kVPNSignature, onc_object, result)) + return false; + + std::string type; + static const char* kValidTypes[] = + { kIPsec, kTypeL2TP_IPsec, kOpenVPN, NULL }; + if (result->GetStringWithoutPathExpansion(vpn::kType, &type) && + !RequireAnyOf(type, kValidTypes)) { + return false; + } + + bool allRequiredExist = RequireField(*result, vpn::kType); + + if (type == kOpenVPN) { + allRequiredExist &= RequireField(*result, kOpenVPN); + } else if (type == kIPsec) { + allRequiredExist &= RequireField(*result, kIPsec); + } else if (type == kTypeL2TP_IPsec) { + allRequiredExist &= RequireField(*result, kIPsec) & + RequireField(*result, kL2TP); + } + + return !error_on_missing_field_ || allRequiredExist; +} + +bool Validator::ValidateIPsec( + const base::DictionaryValue& onc_object, + base::DictionaryValue* result) { + using namespace onc::vpn; + using namespace onc::certificate; + if (!ValidateObjectDefault(kIPsecSignature, onc_object, result)) + return false; + + std::string auth; + static const char* kValidAuthentications[] = { kPSK, kCert, NULL }; + if (result->GetStringWithoutPathExpansion(kAuthenticationType, &auth) && + !RequireAnyOf(auth, kValidAuthentications)) { + return false; + } + + std::string cert_type; + static const char* kValidCertTypes[] = { kRef, kPattern, NULL }; + if (result->GetStringWithoutPathExpansion(kClientCertType, &cert_type) && + !RequireAnyOf(cert_type, kValidCertTypes)) { + return false; + } + + bool allRequiredExist = RequireField(*result, kAuthenticationType) & + RequireField(*result, kIKEVersion); + if (auth == kCert) { + allRequiredExist &= RequireField(*result, kClientCertType) & + RequireField(*result, kServerCARef); + } + if (cert_type == kPattern) + allRequiredExist &= RequireField(*result, kClientCertPattern); + else if (cert_type == kRef) + allRequiredExist &= RequireField(*result, kClientCertRef); + + return !error_on_missing_field_ || allRequiredExist; +} + +bool Validator::ValidateOpenVPN( + const base::DictionaryValue& onc_object, + base::DictionaryValue* result) { + using namespace onc::vpn; + using namespace onc::openvpn; + using namespace onc::certificate; + if (!ValidateObjectDefault(kOpenVPNSignature, onc_object, result)) + return false; + + std::string auth_retry; + static const char* kValidAuthRetryValues[] = + { openvpn::kNone, kInteract, kNoInteract, NULL }; + if (result->GetStringWithoutPathExpansion(kAuthRetry, &auth_retry) && + !RequireAnyOf(auth_retry, kValidAuthRetryValues)) { + return false; + } + + std::string cert_type; + static const char* kValidCertTypes[] = + { certificate::kNone, kRef, kPattern, NULL }; + if (result->GetStringWithoutPathExpansion(kClientCertType, &cert_type) && + !RequireAnyOf(cert_type, kValidCertTypes)) { + return false; + } + + std::string cert_tls; + static const char* kValidCertTlsValues[] = + { openvpn::kNone, openvpn::kServer, NULL }; + if (result->GetStringWithoutPathExpansion(kRemoteCertTLS, &cert_tls) && + !RequireAnyOf(cert_tls, kValidCertTlsValues)) { + return false; + } + + bool allRequiredExist = RequireField(*result, kClientCertType); + if (cert_type == kPattern) + allRequiredExist &= RequireField(*result, kClientCertPattern); + else if (cert_type == kRef) + allRequiredExist &= RequireField(*result, kClientCertRef); + + return !error_on_missing_field_ || allRequiredExist; +} + +bool Validator::ValidateCertificatePattern( + const base::DictionaryValue& onc_object, + base::DictionaryValue* result) { + using namespace onc::certificate; + if (!ValidateObjectDefault(kCertificatePatternSignature, onc_object, result)) + return false; + + bool allRequiredExist = true; + if (!result->HasKey(kSubject) && !result->HasKey(kIssuer) && + !result->HasKey(kIssuerCARef)) { + allRequiredExist = false; + DVLOG(1) << "None of the fields " << kSubject << ", " << kIssuer << ", and " + << kIssuerCARef << " exists, but at least one is required."; + } + + return !error_on_missing_field_ || allRequiredExist; +} + +bool Validator::ValidateProxySettings(const base::DictionaryValue& onc_object, + base::DictionaryValue* result) { + using namespace onc::proxy; + if (!ValidateObjectDefault(kProxySettingsSignature, onc_object, result)) + return false; + + std::string type; + static const char* kValidTypes[] = { kDirect, kManual, kPAC, kWPAD, NULL }; + if (result->GetStringWithoutPathExpansion(proxy::kType, &type) && + !RequireAnyOf(type, kValidTypes)) { + return false; + } + + bool allRequiredExist = RequireField(*result, proxy::kType); + + if (type == kManual) + allRequiredExist &= RequireField(*result, kManual); + else if (type == kPAC) + allRequiredExist &= RequireField(*result, kPAC); + + return !error_on_missing_field_ || allRequiredExist; +} + +bool Validator::ValidateProxyLocation(const base::DictionaryValue& onc_object, + base::DictionaryValue* result) { + using namespace onc::proxy; + if (!ValidateObjectDefault(kProxyLocationSignature, onc_object, result)) + return false; + + bool allRequiredExist = RequireField(*result, kHost) & + RequireField(*result, kPort); + + return !error_on_missing_field_ || allRequiredExist; +} + +bool Validator::ValidateEAP(const base::DictionaryValue& onc_object, + base::DictionaryValue* result) { + using namespace onc::eap; + using namespace onc::certificate; + if (!ValidateObjectDefault(kEAPSignature, onc_object, result)) + return false; + + std::string inner; + static const char* kValidInnerValues[] = + { kAutomatic, kMD5, kMSCHAPv2, kPAP, NULL }; + if (result->GetStringWithoutPathExpansion(kInner, &inner) && + !RequireAnyOf(inner, kValidInnerValues)) { + return false; + } + + std::string outer; + static const char* kValidOuterValues[] = + { kPEAP, kEAP_TLS, kEAP_TTLS, kLEAP, kEAP_SIM, kEAP_FAST, kEAP_AKA, + NULL }; + if (result->GetStringWithoutPathExpansion(kOuter, &outer) && + !RequireAnyOf(outer, kValidOuterValues)) { + return false; + } + + std::string cert_type; + static const char* kValidCertTypes[] = { kRef, kPattern, NULL }; + if (result->GetStringWithoutPathExpansion(kClientCertType, &cert_type) && + !RequireAnyOf(cert_type, kValidCertTypes )) { + return false; + } + + bool allRequiredExist = RequireField(*result, kOuter); + + if (cert_type == kPattern) + allRequiredExist &= RequireField(*result, kClientCertPattern); + else if (cert_type == kRef) + allRequiredExist &= RequireField(*result, kClientCertRef); + + return !error_on_missing_field_ || allRequiredExist; +} + +bool Validator::ValidateCertificate( + const base::DictionaryValue& onc_object, + base::DictionaryValue* result) { + using namespace onc::certificate; + if (!ValidateObjectDefault(kCertificateSignature, onc_object, result)) + return false; + + std::string type; + static const char* kValidTypes[] = { kClient, kServer, kAuthority, NULL }; + if (result->GetStringWithoutPathExpansion(certificate::kType, &type) && + !RequireAnyOf(type, kValidTypes)) { + return false; + } + + bool allRequiredExist = RequireField(*result, kGUID); + + bool remove = false; + result->GetBooleanWithoutPathExpansion(kRemove, &remove); + if (!remove) { + allRequiredExist &= RequireField(*result, certificate::kType); + + if (type == kClient) + allRequiredExist &= RequireField(*result, kPKCS12); + else if (type == kServer || type == kAuthority) + allRequiredExist &= RequireField(*result, kX509); + } + + return !error_on_missing_field_ || allRequiredExist; +} + +} // namespace onc +} // namespace chromeos diff --git a/chromeos/network/onc/onc_validator.h b/chromeos/network/onc/onc_validator.h new file mode 100644 index 0000000..4a74272 --- /dev/null +++ b/chromeos/network/onc/onc_validator.h @@ -0,0 +1,153 @@ +// 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. + +#ifndef CHROMEOS_NETWORK_ONC_ONC_VALIDATOR_H_ +#define CHROMEOS_NETWORK_ONC_ONC_VALIDATOR_H_ + +#include "base/memory/scoped_ptr.h" +#include "chromeos/chromeos_export.h" +#include "chromeos/network/onc/onc_mapper.h" + +namespace base { +class Value; +class DictionaryValue; +} + +namespace chromeos { +namespace onc { + +struct OncValueSignature; + +class CHROMEOS_EXPORT Validator : public Mapper { + public: + // Creates a Validator that searches for the following invalid cases: + // - a field name is found that is not part of the signature + // (controlled by |error_on_unknown_field|) + // + // - a kRecommended array contains a field name that is not part of the + // enclosing object's signature or if that field is dictionary typed + // (controlled by |error_on_wrong_recommended|) + // + // - |managed_onc| is false and a field with name kRecommended is found + // (always ignored) + // + // - a required field is missing (controlled by |error_on_missing_field|) + // + // If one of these invalid cases occurs and the controlling flag is true, then + // it is an error and the validation stops. The function + // ValidateAndRepairObject returns NULL. + // + // If no error occurred, then a DeepCopy of the validated object is created, + // which contains all but the invalid fields and values. + Validator(bool error_on_unknown_field, + bool error_on_wrong_recommended, + bool error_on_missing_field, + bool managed_onc); + + virtual ~Validator(); + + // Validate the given |onc_object| according to |object_signature|. The + // |object_signature| has to be a pointer to one of the signatures in + // |onc_signature.h|. If an error is found, the function returns NULL. If + // possible (no error encountered) a DeepCopy is created that contains all but + // the invalid fields and values and returns this "repaired" object. + // That means, if not handled as an error, then the following are ignored: + // - unknown fields + // - invalid field names in kRecommended arrays + // - kRecommended fields in an unmanaged ONC + // For details, see the comment at the Constructor. + scoped_ptr<base::DictionaryValue> ValidateAndRepairObject( + const OncValueSignature* object_signature, + const base::DictionaryValue& onc_object); + + private: + // Overriden from Mapper: + // Compare |onc_value|s type with |onc_type| and validate/repair according to + // |signature|. On error returns NULL. + virtual scoped_ptr<base::Value> MapValue( + const OncValueSignature& signature, + const base::Value& onc_value) OVERRIDE; + + // Dispatch to the right validation function according to + // |signature|. Iterates over all fields and recursively validates/repairs + // these. All valid fields are added to the result dictionary. Returns the + // repaired dictionary. On error returns NULL. + virtual scoped_ptr<base::DictionaryValue> MapObject( + const OncValueSignature& signature, + const base::DictionaryValue& onc_object) OVERRIDE; + + // This is the default validation of objects/dictionaries. Validates + // |onc_object| according to |object_signature|. |result| must point to a + // dictionary into which the repaired fields are written. + bool ValidateObjectDefault( + const OncValueSignature& object_signature, + const base::DictionaryValue& onc_object, + base::DictionaryValue* result); + + // Validates/repairs the kRecommended array in |result| according to + // |object_signature| of the enclosing object. + bool ValidateRecommendedField( + const OncValueSignature& object_signature, + base::DictionaryValue* result); + + bool ValidateNetworkConfiguration( + const base::DictionaryValue& onc_object, + base::DictionaryValue* result); + + bool ValidateEthernet( + const base::DictionaryValue& onc_object, + base::DictionaryValue* result); + + bool ValidateIPConfig( + const base::DictionaryValue& onc_object, + base::DictionaryValue* result); + + bool ValidateWiFi( + const base::DictionaryValue& onc_object, + base::DictionaryValue* result); + + bool ValidateVPN( + const base::DictionaryValue& onc_object, + base::DictionaryValue* result); + + bool ValidateIPsec( + const base::DictionaryValue& onc_object, + base::DictionaryValue* result); + + bool ValidateOpenVPN( + const base::DictionaryValue& onc_object, + base::DictionaryValue* result); + + bool ValidateCertificatePattern( + const base::DictionaryValue& onc_object, + base::DictionaryValue* result); + + bool ValidateProxySettings( + const base::DictionaryValue& onc_object, + base::DictionaryValue* result); + + bool ValidateProxyLocation( + const base::DictionaryValue& onc_object, + base::DictionaryValue* result); + + bool ValidateEAP( + const base::DictionaryValue& onc_object, + base::DictionaryValue* result); + + bool ValidateCertificate( + const base::DictionaryValue& onc_object, + base::DictionaryValue* result); + + const bool error_on_unknown_field_; + const bool error_on_wrong_recommended_; + const bool error_on_missing_field_; + const bool managed_onc_; + + DISALLOW_COPY_AND_ASSIGN(Validator); +}; + +} // namespace onc +} // namespace chromeos + +#endif // CHROMEOS_NETWORK_ONC_ONC_VALIDATOR_H_ diff --git a/chromeos/network/onc/onc_validator_unittest.cc b/chromeos/network/onc/onc_validator_unittest.cc new file mode 100644 index 0000000..f02e9cf --- /dev/null +++ b/chromeos/network/onc/onc_validator_unittest.cc @@ -0,0 +1,143 @@ +// 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 <string> + +#include "base/memory/scoped_ptr.h" +#include "base/values.h" +#include "chromeos/network/onc/onc_constants.h" +#include "chromeos/network/onc/onc_signature.h" +#include "chromeos/network/onc/onc_test_utils.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace chromeos { +namespace onc { +namespace { + +// Create a strict validator that complains about every error. +scoped_ptr<Validator> CreateStrictValidator(bool managed_onc) { + return make_scoped_ptr(new Validator(true, true, true, managed_onc)); +} + +// Create a liberal validator that ignores or repairs non-critical errors. +scoped_ptr<Validator> CreateLiberalValidator(bool managed_onc) { + return make_scoped_ptr(new Validator(false, false, false, managed_onc)); +} +} // namespace + +// This test case is about validating valid ONC objects. +TEST(ONCValidatorValidTest, ValidPolicyOnc) { + scoped_ptr<Validator> validator(CreateStrictValidator(true)); + scoped_ptr<const base::DictionaryValue> network; + scoped_ptr<base::DictionaryValue> repaired; + + network = test_utils::ReadTestDictionary("policy.onc"); + repaired = validator->ValidateAndRepairObject( + &kNetworkConfigurationSignature, + *network); + EXPECT_TRUE(test_utils::Equals(network.get(), repaired.get())); + + network = test_utils::ReadTestDictionary("valid.onc"); + repaired = validator->ValidateAndRepairObject( + &kNetworkConfigurationSignature, + *network); + EXPECT_TRUE(test_utils::Equals(network.get(), repaired.get())); +} + +// Validate invalid ONC objects and check the resulting repaired object. This +// test fixture loads a test json file into |invalid_| containing several test +// objects which can be accessed by their path. The test boolean parameter +// determines wether to use the strict or the liberal validator. +class ONCValidatorInvalidTest : public ::testing::TestWithParam<bool> { + public: + // Validate the entry at |path_to_object| with the given + // |signature|. Depending on |managed_onc| the object is interpreted as a + // managed onc (with recommended fields) or not. The resulting repaired object + // must match the entry at |path_to_repaired| if the liberal validator is + // used. + void ValidateInvalid(const std::string& path_to_object, + const std::string& path_to_repaired, + const OncValueSignature* signature, + bool managed_onc) { + scoped_ptr<Validator> validator; + if (GetParam()) + validator = CreateStrictValidator(managed_onc); + else + validator = CreateLiberalValidator(managed_onc); + + const base::DictionaryValue* object = NULL; + ASSERT_TRUE(invalid_->GetDictionary(path_to_object, &object)); + + scoped_ptr<base::DictionaryValue> actual_repaired = + validator->ValidateAndRepairObject(signature, *object); + if (GetParam() || path_to_repaired == "") { + EXPECT_EQ(NULL, actual_repaired.get()); + } else { + const base::DictionaryValue* expected_repaired = NULL; + invalid_->GetDictionary(path_to_repaired, &expected_repaired); + EXPECT_TRUE(test_utils::Equals(expected_repaired, actual_repaired.get())); + } + } + + virtual void SetUp() { + invalid_ = + test_utils::ReadTestDictionary("invalid_settings_with_repairs.json"); + } + + scoped_ptr<const base::DictionaryValue> invalid_; +}; + +TEST_P(ONCValidatorInvalidTest, UnknownFieldName) { + ValidateInvalid("network-unknown-fieldname", + "network-repaired", + &kNetworkConfigurationSignature, false); + ValidateInvalid("managed-network-unknown-fieldname", + "managed-network-repaired", + &kNetworkConfigurationSignature, true); +} + +TEST_P(ONCValidatorInvalidTest, UnknownType) { + ValidateInvalid("network-unknown-type", + "", + &kNetworkConfigurationSignature, false); + ValidateInvalid("managed-network-unknown-type", + "", + &kNetworkConfigurationSignature, true); +} + +TEST_P(ONCValidatorInvalidTest, UnknownRecommendedFieldName) { + ValidateInvalid("managed-network-unknown-recommended", + "managed-network-repaired", + &kNetworkConfigurationSignature, true); +} + +TEST_P(ONCValidatorInvalidTest, DictionaryRecommended) { + ValidateInvalid("managed-network-dict-recommended", + "managed-network-repaired", + &kNetworkConfigurationSignature, true); +} + +TEST_P(ONCValidatorInvalidTest, MissingRequiredField) { + ValidateInvalid("network-missing-required", + "network-missing-required", + &kNetworkConfigurationSignature, false); + ValidateInvalid("managed-network-missing-required", + "managed-network-missing-required", + &kNetworkConfigurationSignature, true); +} + +TEST_P(ONCValidatorInvalidTest, RecommendedInUnmanaged) { + ValidateInvalid("network-illegal-recommended", + "network-repaired", + &kNetworkConfigurationSignature, false); +} + +INSTANTIATE_TEST_CASE_P(ONCValidatorInvalidTest, + ONCValidatorInvalidTest, + ::testing::Bool()); + +} // namespace onc +} // namespace chromeos diff --git a/chromeos/test/data/network/broken-encrypted-iterations.onc b/chromeos/test/data/network/broken-encrypted-iterations.onc new file mode 100644 index 0000000..3bbbce4 --- /dev/null +++ b/chromeos/test/data/network/broken-encrypted-iterations.onc @@ -0,0 +1,11 @@ +{ + "Cipher": "AES256", + "Ciphertext": "bnJmCTSw4SJJB3+3SnlAsd+7kmtDOjpxXfiXOK7XEhKh3uvJ3ntO5eleN+cvG2EAxubzkzp/y7siQKolLqFMvz63sYWEYYRY250j5b3Tjjaqk+PJidhyIwT9DiuSYTqPMip8cRBfQtj1dHNGADWcq7NwrmjW2EAL8/G+s8YC4w2Lnw3pzlNRKginUutRdJ4ShorXkFey6LDvS3A1WofGfr6C+//Da0aXYJiNaNm/qOxAuoxJBEXDNhgNuGrbCviZkJOAviYvYGFhWF2lZMvwwiWSjmhy8yzDefjyJxNl7XB9iZeqh1Lqh8Uh6/pVRpVq3fNUpRZwY2SdT9VvYVOtlesXrONKpTHKRXArzsjK2py5mux7DcmVRqj9XeDIiPH2aTHA51T+PPo851HktSrrmlwMUVwrMlH2BpkaibCo+AevekHWYYv0uMY8xLAYI4T5bQ3O9HwN3DmTzjjkcOKkTSyfq68bHUvLInHWoh+k3W/+9l3SuuCAelfWu0XyoWzRr+C66tCO5Ugn9hjMZIE4NQiEsPigg3RQh+JpT5wCBPxpsxGWi/GhxEadb1ZF4CKnpqPVhJ7uOFgIT1z2Ieh8lw==", + "HMAC": "vFzwTgOCx3OOWwYUOSxWx0iu1LM=", + "HMACMethod": "SHA1", + "IV": "ugpkaONKt34MfDu511WSqQ==", + "Iterations": -1, + "Salt": "LvO2ljvR4LY=", + "Stretch": "PBKDF2", + "Type": "EncryptedConfiguration" +}
\ No newline at end of file diff --git a/chromeos/test/data/network/broken-encrypted-zero-iterations.onc b/chromeos/test/data/network/broken-encrypted-zero-iterations.onc new file mode 100644 index 0000000..f8977a4 --- /dev/null +++ b/chromeos/test/data/network/broken-encrypted-zero-iterations.onc @@ -0,0 +1,11 @@ +{ + "Cipher": "AES256", + "Ciphertext": "bnJmCTSw4SJJB3+3SnlAsd+7kmtDOjpxXfiXOK7XEhKh3uvJ3ntO5eleN+cvG2EAxubzkzp/y7siQKolLqFMvz63sYWEYYRY250j5b3Tjjaqk+PJidhyIwT9DiuSYTqPMip8cRBfQtj1dHNGADWcq7NwrmjW2EAL8/G+s8YC4w2Lnw3pzlNRKginUutRdJ4ShorXkFey6LDvS3A1WofGfr6C+//Da0aXYJiNaNm/qOxAuoxJBEXDNhgNuGrbCviZkJOAviYvYGFhWF2lZMvwwiWSjmhy8yzDefjyJxNl7XB9iZeqh1Lqh8Uh6/pVRpVq3fNUpRZwY2SdT9VvYVOtlesXrONKpTHKRXArzsjK2py5mux7DcmVRqj9XeDIiPH2aTHA51T+PPo851HktSrrmlwMUVwrMlH2BpkaibCo+AevekHWYYv0uMY8xLAYI4T5bQ3O9HwN3DmTzjjkcOKkTSyfq68bHUvLInHWoh+k3W/+9l3SuuCAelfWu0XyoWzRr+C66tCO5Ugn9hjMZIE4NQiEsPigg3RQh+JpT5wCBPxpsxGWi/GhxEadb1ZF4CKnpqPVhJ7uOFgIT1z2Ieh8lw==", + "HMAC": "vFzwTgOCx3OOWwYUOSxWx0iu1LM=", + "HMACMethod": "SHA1", + "IV": "ugpkaONKt34MfDu511WSqQ==", + "Iterations": 0, + "Salt": "LvO2ljvR4LY=", + "Stretch": "PBKDF2", + "Type": "EncryptedConfiguration" +}
\ No newline at end of file diff --git a/chromeos/test/data/network/certificate-client-update.onc b/chromeos/test/data/network/certificate-client-update.onc new file mode 100644 index 0000000..8d2ff44 --- /dev/null +++ b/chromeos/test/data/network/certificate-client-update.onc @@ -0,0 +1,9 @@ +{ + "Certificates": [ + { + "GUID": "{f998f760-272b-6939-4c2beffe428697ad}", + "PKCS12": "MIIGUQIBAzCCBhcGCSqGSIb3DQEHAaCCBggEggYEMIIGADCCAv8GCSqGSIb3DQEHBqCCAvAwggLsAgEAMIIC5QYJKoZIhvcNAQcBMBwGCiqGSIb3DQEMAQYwDgQIHnFaWM2Y0BgCAggAgIICuG4ou9mxkhpus8WictLJe+JOnSQrdNXV3FMQr4pPJ6aJJFBMKZ80W2GpR8XNY/SSKkdaNr1puDm1bDBFGaHQuCKXYcWO8ynBQ1uoZaFaTTFxWbbHo89Jrvw+gIrgpoOHQ0KECEbh5vOZCjGHoaQb4QZOkw/6Cuc4QRoCPJAI3pbSPG44kRbOuOaTZvBHSIPkGf3+R6byTvZ3Yiuw7IIzxUp2fYjtpCWd/NvtI70heJCWdb5hwCeNafIEpX+MTVuhUegysIFkOMMlUBIQSI5ky8kjx0Yi82BT/dpz9QgrqFL8NnTMXp0JlKFGLQwsIQhvGjw/E52fEWRy85B5eezgNsD4QOLeZkF0bQAz8kXfLi+0djxsHvH9W9X2pwaFiAveXR15/v+wfCwQGSsRhISGLzg/gO1agbQdaexI9GlEeZW0FEY7TblarKh8TVGNrauU7GCGDmD2w7wx2HTXfo9SbViFoYVKuxcrpHGGEtBffnIeAwN6BBee4v11jxv0i/QUdK5G6FbHqlD1AhHsm0YvidYKqJ0cnN262xIJH7dhKq/qUiAT+qk3+d3/obqxbvVY+bDoJQ10Gzj1ASMy4zcSL7KW1l99xxMr6OlKr4Sr23oGw4BIN73FB8S8qMzz/VzL4azDUyGpPkzWl0yXPsHpFWh1nZlsQehyknyWDH/waKrrG8tVWxHZLgq+zrFxQTh63UHXSD+TXB+AQg2xmQMeWlfvRcsKL8titZ6PnWCHTmZY+3ibv5avDsg7He6OcZOi9ZmYMx82QHuzb4aZ/T+OC05oA97nVNbTN6t8okkRtBamMvVhtTJANVpsdPi8saEaVF8e9liwmpq2w7pqXnzgdzvjSUpPAa4dZBjWnZJvFOHuxZqiRzQdZbeh9+bXwsQJhRNe+d4EgFwuqebQOczeUi4NVTHTFiuPEjCCAvkGCSqGSIb3DQEHAaCCAuoEggLmMIIC4jCCAt4GCyqGSIb3DQEMCgECoIICpjCCAqIwHAYKKoZIhvcNAQwBAzAOBAi0znbEekG/MgICCAAEggKAJfFPaQyYYLohEA1ruAZfepwMVrR8eLMx00kkfXN9EoZeFPj2q7TGdqmbkUSqXnZK1ums7pFCPLgP1CsPlsq/4ZPDT2LLVFZNLOgmdQBOSTvycfsj0iKYrwRC55wJI2OXsc062sT7oa99apkgrEyHq7JbOhszfnv5+aVy/6O115dncqFPW2ei4CBzLEZyYa+Mka6CGqSdm97WVmv0emDKTFEP/FN4TH/tS8Qm6Y7DTKGCujC+hb6lTRFYJAD4uld132dv0xQFkwDZGfdnuGJuNZBDC0gZk3BYvOaCUD8Y9UB5IjfGJax2yrurY1wSGSlTurafDTPrKqIdBovwCPsad2xz1YHC2Yy0h1FyR+2uitDyNfTiETfug3bFbjwodu9wmt31A2ZFn4JpUrTYoZ3LZXngC3nNTayU0Tkd1ICMep2GbCReL3ajOlgOKGFVoOm/qDnhiH6W/ebtAQXqVpuKut8uY0X0Ocmx7mTpmxlfDSRiBY9rvnrGfnpfLMxtFeF9jv3n8vSwvA0Xn0okAv1FWYLStiCpNxnD6lmXQvcmL/skAlJJpHY9/58qt/e5sGYrkKBw3jnX40zaK4W7GeJvhij0MRr6yUL2lvaEcWDnK6K1F90G/ybKRCTHBCJzyBe7yHhZCc+ZcvKK6DTi83fELTyupy08BkXt7oPdapxmKlZxTldo9FpPXSqrdRtAWhDkEkIEf8dMf8QrQr3glCWfbcQ047URYX45AHRnLTLLkJfdY8+Y3KsHoqL2UrOrct+J1u0mmnLbonN3pB2B4nd9X9vf9/uSFrgvk0iPO0Ro3UPRUIIYEP2Kx51pZZVDd++hl5gXtqe0NIpphGhxLycIdzElMCMGCSqGSIb3DQEJFTEWBBR1uVpGjHRddIEYuJhz/FgG4Onh6jAxMCEwCQYFKw4DAhoFAAQU1M+0WRDkoVGbGg1jj7q2fI67qHIECBzRYESpgt5iAgIIAA==", + "Type": "Client" + } + ] +} diff --git a/chromeos/test/data/network/certificate-client.onc b/chromeos/test/data/network/certificate-client.onc new file mode 100644 index 0000000..75a244a --- /dev/null +++ b/chromeos/test/data/network/certificate-client.onc @@ -0,0 +1,9 @@ +{ + "Certificates": [ + { + "GUID": "{f998f760-272b-6939-4c2beffe428697ac}", + "PKCS12": "MIIGUQIBAzCCBhcGCSqGSIb3DQEHAaCCBggEggYEMIIGADCCAv8GCSqGSIb3DQEHBqCCAvAwggLsAgEAMIIC5QYJKoZIhvcNAQcBMBwGCiqGSIb3DQEMAQYwDgQIHnFaWM2Y0BgCAggAgIICuG4ou9mxkhpus8WictLJe+JOnSQrdNXV3FMQr4pPJ6aJJFBMKZ80W2GpR8XNY/SSKkdaNr1puDm1bDBFGaHQuCKXYcWO8ynBQ1uoZaFaTTFxWbbHo89Jrvw+gIrgpoOHQ0KECEbh5vOZCjGHoaQb4QZOkw/6Cuc4QRoCPJAI3pbSPG44kRbOuOaTZvBHSIPkGf3+R6byTvZ3Yiuw7IIzxUp2fYjtpCWd/NvtI70heJCWdb5hwCeNafIEpX+MTVuhUegysIFkOMMlUBIQSI5ky8kjx0Yi82BT/dpz9QgrqFL8NnTMXp0JlKFGLQwsIQhvGjw/E52fEWRy85B5eezgNsD4QOLeZkF0bQAz8kXfLi+0djxsHvH9W9X2pwaFiAveXR15/v+wfCwQGSsRhISGLzg/gO1agbQdaexI9GlEeZW0FEY7TblarKh8TVGNrauU7GCGDmD2w7wx2HTXfo9SbViFoYVKuxcrpHGGEtBffnIeAwN6BBee4v11jxv0i/QUdK5G6FbHqlD1AhHsm0YvidYKqJ0cnN262xIJH7dhKq/qUiAT+qk3+d3/obqxbvVY+bDoJQ10Gzj1ASMy4zcSL7KW1l99xxMr6OlKr4Sr23oGw4BIN73FB8S8qMzz/VzL4azDUyGpPkzWl0yXPsHpFWh1nZlsQehyknyWDH/waKrrG8tVWxHZLgq+zrFxQTh63UHXSD+TXB+AQg2xmQMeWlfvRcsKL8titZ6PnWCHTmZY+3ibv5avDsg7He6OcZOi9ZmYMx82QHuzb4aZ/T+OC05oA97nVNbTN6t8okkRtBamMvVhtTJANVpsdPi8saEaVF8e9liwmpq2w7pqXnzgdzvjSUpPAa4dZBjWnZJvFOHuxZqiRzQdZbeh9+bXwsQJhRNe+d4EgFwuqebQOczeUi4NVTHTFiuPEjCCAvkGCSqGSIb3DQEHAaCCAuoEggLmMIIC4jCCAt4GCyqGSIb3DQEMCgECoIICpjCCAqIwHAYKKoZIhvcNAQwBAzAOBAi0znbEekG/MgICCAAEggKAJfFPaQyYYLohEA1ruAZfepwMVrR8eLMx00kkfXN9EoZeFPj2q7TGdqmbkUSqXnZK1ums7pFCPLgP1CsPlsq/4ZPDT2LLVFZNLOgmdQBOSTvycfsj0iKYrwRC55wJI2OXsc062sT7oa99apkgrEyHq7JbOhszfnv5+aVy/6O115dncqFPW2ei4CBzLEZyYa+Mka6CGqSdm97WVmv0emDKTFEP/FN4TH/tS8Qm6Y7DTKGCujC+hb6lTRFYJAD4uld132dv0xQFkwDZGfdnuGJuNZBDC0gZk3BYvOaCUD8Y9UB5IjfGJax2yrurY1wSGSlTurafDTPrKqIdBovwCPsad2xz1YHC2Yy0h1FyR+2uitDyNfTiETfug3bFbjwodu9wmt31A2ZFn4JpUrTYoZ3LZXngC3nNTayU0Tkd1ICMep2GbCReL3ajOlgOKGFVoOm/qDnhiH6W/ebtAQXqVpuKut8uY0X0Ocmx7mTpmxlfDSRiBY9rvnrGfnpfLMxtFeF9jv3n8vSwvA0Xn0okAv1FWYLStiCpNxnD6lmXQvcmL/skAlJJpHY9/58qt/e5sGYrkKBw3jnX40zaK4W7GeJvhij0MRr6yUL2lvaEcWDnK6K1F90G/ybKRCTHBCJzyBe7yHhZCc+ZcvKK6DTi83fELTyupy08BkXt7oPdapxmKlZxTldo9FpPXSqrdRtAWhDkEkIEf8dMf8QrQr3glCWfbcQ047URYX45AHRnLTLLkJfdY8+Y3KsHoqL2UrOrct+J1u0mmnLbonN3pB2B4nd9X9vf9/uSFrgvk0iPO0Ro3UPRUIIYEP2Kx51pZZVDd++hl5gXtqe0NIpphGhxLycIdzElMCMGCSqGSIb3DQEJFTEWBBR1uVpGjHRddIEYuJhz/FgG4Onh6jAxMCEwCQYFKw4DAhoFAAQU1M+0WRDkoVGbGg1jj7q2fI67qHIECBzRYESpgt5iAgIIAA==", + "Type": "Client" + } + ] +} diff --git a/chromeos/test/data/network/certificate-server-update.onc b/chromeos/test/data/network/certificate-server-update.onc new file mode 100644 index 0000000..45db3bd --- /dev/null +++ b/chromeos/test/data/network/certificate-server-update.onc @@ -0,0 +1,14 @@ +{ + "Certificates": [ + { + "GUID": "{f998f760-272b-6939-4c2beffe428697ab}", + "Trust": [ + "Web" + ], + "Type": "Server", + "X509": "MIICWDCCAcECAxAAATANBgkqhkiG9w0BAQQFADCBkzEVMBMGA1UEChMMR29vZ2xlLCBJbmMuMREwDwYDVQQLEwhDaHJvbWVPUzEiMCAGCSqGSIb3DQEJARYTZ3NwZW5jZXJAZ29vZ2xlLmNvbTEaMBgGA1UEBxMRTW91bnRhaW4gVmlldywgQ0ExCzAJBgNVBAgTAkNBMQswCQYDVQQGEwJVUzENMAsGA1UEAxMEbG1hbzAeFw0xMTAzMTYyMzQ5MzhaFw0xMjAzMTUyMzQ5MzhaMFMxCzAJBgNVBAYTAlVTMQswCQYDVQQIEwJDQTEVMBMGA1UEChMMR29vZ2xlLCBJbmMuMREwDwYDVQQLEwhDaHJvbWVPUzENMAsGA1UEAxMEbG1hbzCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEA31WiJ9LvprrhKtDlW0RdLFAO7Qjkvs+sG6j2Vp2aBSrlhALG/0BVHUhWi4F/HHJho+ncLHAg5AGO0sdAjYUdQG6tfPqjLsIALtoKEZZdFe/JhmqOEaxWsSdu2S2RdPgCQOsP79EH58gXwu2gejCkJDmU22WL4YLuqOc17nxbDC8CAwEAATANBgkqhkiG9w0BAQQFAAOBgQCv4vMD+PMlfnftu4/6Yf/oMLE8yCOqZTQ/dWCxB9PiJnOefiBeSzSZE6Uv3G7qnblZPVZaFeJMd+ostt0viCyPucFsFgLMyyoV1dMVPVwJT5Iq1AHehWXnTBbxUK9wioA5jOEKdroKjuSSsg/Q8Wx6cpJmttQz5olGPgstmACRWA==" + } + ], + "NetworkConfigurations": [], + "Type": "UnencryptedConfiguration" +} diff --git a/chromeos/test/data/network/certificate-server.onc b/chromeos/test/data/network/certificate-server.onc new file mode 100644 index 0000000..2a820e3 --- /dev/null +++ b/chromeos/test/data/network/certificate-server.onc @@ -0,0 +1,14 @@ +{ + "Certificates": [ + { + "GUID": "{f998f760-272b-6939-4c2beffe428697aa}", + "Trust": [ + "Web" + ], + "Type": "Server", + "X509": "leading junk \n-----BEGIN CERTIFICATE----- \nMIICWDCCAcECAxAAATANBgkqhkiG9w0BAQQFADCBkzEVMBMGA1UEChMMR29vZ2xlLCBJbm\nMuMREwDwYDVQQLEwhDaHJvbWVPUzEiMCAGCSqGSIb3DQEJARYTZ3NwZW5jZXJAZ29vZ2xl\nLmNvbTEaMBgGA1UEBxMRTW91bnRhaW4gVmlldywgQ0ExCzAJBgNVBAgTAkNBMQswCQYDVQ\nQGEwJVUzENMAsGA1UEAxMEbG1hbzAeFw0xMTAzMTYyMzQ5MzhaFw0xMjAzMTUyMzQ5Mzha\nMFMxCzAJBgNVBAYTAlVTMQswCQYDVQQIEwJDQTEVMBMGA1UEChMMR29vZ2xlLCBJbmMuMR\nEwDwYDVQQLEwhDaHJvbWVPUzENMAsGA1UEAxMEbG1hbzCBnzANBgkqhkiG9w0BAQEFAAOB\njQAwgYkCgYEA31WiJ9LvprrhKtDlW0RdLFAO7Qjkvs+sG6j2Vp2aBSrlhALG/0BVHUhWi4\nF/HHJho+ncLHAg5AGO0sdAjYUdQG6tfPqjLsIALtoKEZZdFe/JhmqOEaxWsSdu2S2RdPgC\nQOsP79EH58gXwu2gejCkJDmU22WL4YLuqOc17nxbDC8CAwEAATANBgkqhkiG9w0BAQQFAA\nOBgQCv4vMD+PMlfnftu4/6Yf/oMLE8yCOqZTQ/dWCxB9PiJnOefiBeSzSZE6Uv3G7qnblZ\nPVZaFeJMd+ostt0viCyPucFsFgLMyyoV1dMVPVwJT5Iq1AHehWXnTBbxUK9wioA5jOEKdr\noKjuSSsg/Q8Wx6cpJmttQz5olGPgstmACRWA==\n-----END CERTIFICATE----- \ntrailing junk" + } + ], + "NetworkConfigurations": [], + "Type": "UnencryptedConfiguration" +} diff --git a/chromeos/test/data/network/certificate-web-authority-update.onc b/chromeos/test/data/network/certificate-web-authority-update.onc new file mode 100644 index 0000000..ca0de2d --- /dev/null +++ b/chromeos/test/data/network/certificate-web-authority-update.onc @@ -0,0 +1,14 @@ +{ + "Certificates": [ + { + "GUID": "{f998f760-272b-6939-4c2beffe428697ac}", + "Trust": [ + "Web" + ], + "Type": "Authority", + "X509": "MIIDojCCAwugAwIBAgIJAKGvi5ZgEWDVMA0GCSqGSIb3DQEBBAUAMIGTMRUwEwYDVQQKEwxHb29nbGUsIEluYy4xETAPBgNVBAsTCENocm9tZU9TMSIwIAYJKoZIhvcNAQkBFhNnc3BlbmNlckBnb29nbGUuY29tMRowGAYDVQQHExFNb3VudGFpbiBWaWV3LCBDQTELMAkGA1UECBMCQ0ExCzAJBgNVBAYTAlVTMQ0wCwYDVQQDEwRsbWFvMB4XDTExMDMxNjIzNDcxMFoXDTEyMDMxNTIzNDcxMFowgZMxFTATBgNVBAoTDEdvb2dsZSwgSW5jLjERMA8GA1UECxMIQ2hyb21lT1MxIjAgBgkqhkiG9w0BCQEWE2dzcGVuY2VyQGdvb2dsZS5jb20xGjAYBgNVBAcTEU1vdW50YWluIFZpZXcsIENBMQswCQYDVQQIEwJDQTELMAkGA1UEBhMCVVMxDTALBgNVBAMTBGxtYW8wgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAMDX6BQz2JUzIAVjetiXxDznd2wdqVqVHfNkbSRW+xBywgqUaIXmFEGUol7VzPfmeFV8o8ok/eFlQB0h6ycqgwwMd0KjtJs2ys/k0F5GuN0G7fsgr+NRnhVgxj21yF6gYTN/8a9kscla/svdmp8ekexbALFnghbLBx3CgcqUxT+tAgMBAAGjgfswgfgwDAYDVR0TBAUwAwEB/zAdBgNVHQ4EFgQUbYygbSkl4kpjCNuxoezFGupA97UwgcgGA1UdIwSBwDCBvYAUbYygbSkl4kpjCNuxoezFGupA97WhgZmkgZYwgZMxFTATBgNVBAoTDEdvb2dsZSwgSW5jLjERMA8GA1UECxMIQ2hyb21lT1MxIjAgBgkqhkiG9w0BCQEWE2dzcGVuY2VyQGdvb2dsZS5jb20xGjAYBgNVBAcTEU1vdW50YWluIFZpZXcsIENBMQswCQYDVQQIEwJDQTELMAkGA1UEBhMCVVMxDTALBgNVBAMTBGxtYW+CCQChr4uWYBFg1TANBgkqhkiG9w0BAQQFAAOBgQCDq9wiQ4uVuf1CQU3sXfXCy1yqi5m8AsO9FxHvah5/SVFNwKllqTfedpCaWEswJ55YAojW9e+pY2Fh3Fo/Y9YkF88KCtLuBjjqDKCRLxF4LycjHODKyQQ7mN/t5AtP9yKOsNvWF+M4IfReg51kohau6FauQx87by5NIRPdkNPvkQ==" + } + ], + "NetworkConfigurations": [], + "Type": "UnencryptedConfiguration" +} diff --git a/chromeos/test/data/network/certificate-web-authority.onc b/chromeos/test/data/network/certificate-web-authority.onc new file mode 100644 index 0000000..81e316d --- /dev/null +++ b/chromeos/test/data/network/certificate-web-authority.onc @@ -0,0 +1,14 @@ +{ + "Certificates": [ + { + "GUID": "{f998f760-272b-6939-4c2beffe428697ab}", + "Trust": [ + "Web" + ], + "Type": "Authority", + "X509": "MIIDojCCAwugAwIBAgIJAKGvi5ZgEWDVMA0GCSqGSIb3DQEBBAUAMIGTMRUwEwYDVQQKEwxHb29nbGUsIEluYy4xETAPBgNVBAsTCENocm9tZU9TMSIwIAYJKoZIhvcNAQkBFhNnc3BlbmNlckBnb29nbGUuY29tMRowGAYDVQQHExFNb3VudGFpbiBWaWV3LCBDQTELMAkGA1UECBMCQ0ExCzAJBgNVBAYTAlVTMQ0wCwYDVQQDEwRsbWFvMB4XDTExMDMxNjIzNDcxMFoXDTEyMDMxNTIzNDcxMFowgZMxFTATBgNVBAoTDEdvb2dsZSwgSW5jLjERMA8GA1UECxMIQ2hyb21lT1MxIjAgBgkqhkiG9w0BCQEWE2dzcGVuY2VyQGdvb2dsZS5jb20xGjAYBgNVBAcTEU1vdW50YWluIFZpZXcsIENBMQswCQYDVQQIEwJDQTELMAkGA1UEBhMCVVMxDTALBgNVBAMTBGxtYW8wgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAMDX6BQz2JUzIAVjetiXxDznd2wdqVqVHfNkbSRW+xBywgqUaIXmFEGUol7VzPfmeFV8o8ok/eFlQB0h6ycqgwwMd0KjtJs2ys/k0F5GuN0G7fsgr+NRnhVgxj21yF6gYTN/8a9kscla/svdmp8ekexbALFnghbLBx3CgcqUxT+tAgMBAAGjgfswgfgwDAYDVR0TBAUwAwEB/zAdBgNVHQ4EFgQUbYygbSkl4kpjCNuxoezFGupA97UwgcgGA1UdIwSBwDCBvYAUbYygbSkl4kpjCNuxoezFGupA97WhgZmkgZYwgZMxFTATBgNVBAoTDEdvb2dsZSwgSW5jLjERMA8GA1UECxMIQ2hyb21lT1MxIjAgBgkqhkiG9w0BCQEWE2dzcGVuY2VyQGdvb2dsZS5jb20xGjAYBgNVBAcTEU1vdW50YWluIFZpZXcsIENBMQswCQYDVQQIEwJDQTELMAkGA1UEBhMCVVMxDTALBgNVBAMTBGxtYW+CCQChr4uWYBFg1TANBgkqhkiG9w0BAQQFAAOBgQCDq9wiQ4uVuf1CQU3sXfXCy1yqi5m8AsO9FxHvah5/SVFNwKllqTfedpCaWEswJ55YAojW9e+pY2Fh3Fo/Y9YkF88KCtLuBjjqDKCRLxF4LycjHODKyQQ7mN/t5AtP9yKOsNvWF+M4IfReg51kohau6FauQx87by5NIRPdkNPvkQ==" + } + ], + "NetworkConfigurations": [], + "Type": "UnencryptedConfiguration" +}
\ No newline at end of file diff --git a/chromeos/test/data/network/decrypted.onc b/chromeos/test/data/network/decrypted.onc new file mode 100644 index 0000000..b0bfee4 --- /dev/null +++ b/chromeos/test/data/network/decrypted.onc @@ -0,0 +1,18 @@ +{ + "Certificates": [ ], + "NetworkConfigurations": [ { + "GUID": "{64369ad3-9aec-0d1e-e7bb495970da2f33}", + "Name": "WirelessNetwork", + "ProxySettings": { + "PAC": "http://www.youtube.com/watch?v=oHg5SJYRHA0", + "Type": "PAC" + }, + "Type": "WiFi", + "WiFi": { + "AutoConnect": false, + "HiddenSSID": false, + "SSID": "WirelessNetwork", + "Security": "None" + } + } ] +} diff --git a/chromeos/test/data/network/device_policy.onc b/chromeos/test/data/network/device_policy.onc new file mode 100644 index 0000000..663af5b --- /dev/null +++ b/chromeos/test/data/network/device_policy.onc @@ -0,0 +1,26 @@ +{ "GUID": "123", + "Type": "VPN", + "Name": "testopenvpn", + "IPConfigs": [ + { "Type": "IPv4", + "IPAddress": "127.0.0.1" } + ], + "VPN": { + "Host": "device policys host", + "Recommended": ["Host"], + "Type": "OpenVPN", + "OpenVPN": { + "Port": 0, + "Username": "device user", + "Recommended": [ "Password", "Port" ], + "ClientCertType": "Pattern", + "ClientCertPattern": { + "IssuerCARef": [ "openvpn-test-ca" ], + "Recommended": [ "EnrollmentURI", "IssuerCARef" ] + } + }, + "IPsec": { + "PSK": "sharedkey" + } + } +} diff --git a/chromeos/test/data/network/encrypted.onc b/chromeos/test/data/network/encrypted.onc new file mode 100644 index 0000000..82da98f --- /dev/null +++ b/chromeos/test/data/network/encrypted.onc @@ -0,0 +1,11 @@ +{ + "Cipher": "AES256", + "Ciphertext": "bnJmCTSw4SJJB3+3SnlAsd+7kmtDOjpxXfiXOK7XEhKh3uvJ3ntO5eleN+cvG2EAxubzkzp/y7siQKolLqFMvz63sYWEYYRY250j5b3Tjjaqk+PJidhyIwT9DiuSYTqPMip8cRBfQtj1dHNGADWcq7NwrmjW2EAL8/G+s8YC4w2Lnw3pzlNRKginUutRdJ4ShorXkFey6LDvS3A1WofGfr6C+//Da0aXYJiNaNm/qOxAuoxJBEXDNhgNuGrbCviZkJOAviYvYGFhWF2lZMvwwiWSjmhy8yzDefjyJxNl7XB9iZeqh1Lqh8Uh6/pVRpVq3fNUpRZwY2SdT9VvYVOtlesXrONKpTHKRXArzsjK2py5mux7DcmVRqj9XeDIiPH2aTHA51T+PPo851HktSrrmlwMUVwrMlH2BpkaibCo+AevekHWYYv0uMY8xLAYI4T5bQ3O9HwN3DmTzjjkcOKkTSyfq68bHUvLInHWoh+k3W/+9l3SuuCAelfWu0XyoWzRr+C66tCO5Ugn9hjMZIE4NQiEsPigg3RQh+JpT5wCBPxpsxGWi/GhxEadb1ZF4CKnpqPVhJ7uOFgIT1z2Ieh8lw==", + "HMAC": "vFzwTgOCx3OOWwYUOSxWx0iu1LM=", + "HMACMethod": "SHA1", + "IV": "ugpkaONKt34MfDu511WSqQ==", + "Iterations": 20000, + "Salt": "LvO2ljvR4LY=", + "Stretch": "PBKDF2", + "Type": "EncryptedConfiguration" +}
\ No newline at end of file diff --git a/chromeos/test/data/network/invalid_settings_with_repairs.json b/chromeos/test/data/network/invalid_settings_with_repairs.json new file mode 100644 index 0000000..34f521f --- /dev/null +++ b/chromeos/test/data/network/invalid_settings_with_repairs.json @@ -0,0 +1,92 @@ +{ + "managed-network-repaired": { + "Recommended": [], + "GUID": "guid", + "Type": "Ethernet", + "Name": "name", + "Ethernet": { + "Authentication": "None" + } + }, + "managed-network-unknown-fieldname": { + "abc": "def", + "Recommended": [], + "GUID": "guid", + "Type": "Ethernet", + "Name": "name", + "Ethernet": { + "Authentication": "None" + } + }, + "managed-network-unknown-type": { + "GUID": "guid", + "Type": "LTE", + "Name": "name", + "Ethernet": { + "Authentication": "None" + } + }, + "managed-network-unknown-recommended": { + "Recommended": ["abc"], + "GUID": "guid", + "Type": "Ethernet", + "Name": "name", + "Ethernet": { + "Authentication": "None" + } + }, + "managed-network-dict-recommended": { + "Recommended": ["Ethernet"], + "GUID": "guid", + "Type": "Ethernet", + "Name": "name", + "Ethernet": { + "Authentication": "None" + } + }, + "managed-network-missing-required": { + "Recommended": [], + "Type": "Ethernet", + "Name": "name", + "Ethernet": { + "Authentication": "None" + } + }, + "network-repaired": { + "Type": "Ethernet", + "Name": "name", + "Ethernet": { + "Authentication": "None" + } + }, + "network-unknown-fieldname": { + "abc": "def", + "Type": "Ethernet", + "Name": "name", + "Ethernet": { + "Authentication": "None" + } + }, + "network-unknown-type": { + "Type": "LTE", + "Name": "name", + "Ethernet": { + "Authentication": "None" + } + }, + "network-missing-required": { + "Type": "Ethernet", + "Name": "name", + "Ethernet": { + "Authentication": "None" + } + }, + "network-illegal-recommended": { + "Recommended": [], + "Type": "Ethernet", + "Name": "name", + "Ethernet": { + "Authentication": "None" + } + }, +} diff --git a/chromeos/test/data/network/policy.onc b/chromeos/test/data/network/policy.onc new file mode 100644 index 0000000..4c52e97 --- /dev/null +++ b/chromeos/test/data/network/policy.onc @@ -0,0 +1,29 @@ +{ "GUID": "123", + "Type": "VPN", + "Name": "testopenvpn", + "IPConfigs": [ + { "Type": "IPv4", + "IPAddress": "127.0.0.1", + "RoutingPrefix": 32 } + ], + "VPN": { + "Host": "policys host", + "Recommended": ["Host"], + "Type": "OpenVPN", + "OpenVPN": { + "Port": 1194, + "Username": "policy user", + "Recommended": [ "Username", "Password" ], + "ClientCertType": "Pattern", + "ClientCertPattern": { + "IssuerCARef": [ "openvpn-test-ca" ], + "Recommended": [ "EnrollmentURI", "IssuerCARef" ] + } + }, + "IPsec": { + "AuthenticationType": "PSK", + "PSK": "sharedkey", + "IKEVersion": 1 + } + } +} diff --git a/chromeos/test/data/network/policy_without_recommended.onc b/chromeos/test/data/network/policy_without_recommended.onc new file mode 100644 index 0000000..037bf53 --- /dev/null +++ b/chromeos/test/data/network/policy_without_recommended.onc @@ -0,0 +1,26 @@ +{ "GUID": "123", + "Type": "VPN", + "Name": "testopenvpn", + "IPConfigs": [ + { "Type": "IPv4", + "IPAddress": "127.0.0.1", + "RoutingPrefix": 32 } + ], + "VPN": { + "Host": "policys host", + "Type": "OpenVPN", + "OpenVPN": { + "Port": 1194, + "Username": "policy user", + "ClientCertType": "Pattern", + "ClientCertPattern": { + "IssuerCARef": [ "openvpn-test-ca" ], + } + }, + "IPsec": { + "AuthenticationType": "PSK", + "PSK": "sharedkey", + "IKEVersion": 1 + } + } +} diff --git a/chromeos/test/data/network/settings_with_normalization.json b/chromeos/test/data/network/settings_with_normalization.json new file mode 100644 index 0000000..08a78a4 --- /dev/null +++ b/chromeos/test/data/network/settings_with_normalization.json @@ -0,0 +1,33 @@ +{ + "ethernet-and-vpn": { + "Recommended": [], + "GUID": "guid", + "Type": "Ethernet", + "Name": "name", + "Ethernet": { + "Authentication": "None" + }, + "VPN": { + "Type": "L2TP-IPsec", + "Host": "some.host.org", + "IPsec": { + "AuthenticationType": "PSK", + "IKEVersion": 1, + "PSK": "some_preshared_key", + "SaveCredentials": true + }, + "L2TP": { + "Username": "some username", + "Password": "some password" + } + } + }, + "ethernet-and-vpn-normalized": { + "GUID": "guid", + "Type": "Ethernet", + "Name": "name", + "Ethernet": { + "Authentication": "None" + } + }, +} diff --git a/chromeos/test/data/network/shill_ethernet.json b/chromeos/test/data/network/shill_ethernet.json new file mode 100644 index 0000000..7754932 --- /dev/null +++ b/chromeos/test/data/network/shill_ethernet.json @@ -0,0 +1,4 @@ +{ "GUID": "guid", + "Type": "ethernet", + "Name": "name", +} diff --git a/chromeos/test/data/network/shill_l2tpipsec.json b/chromeos/test/data/network/shill_l2tpipsec.json new file mode 100644 index 0000000..f529a97 --- /dev/null +++ b/chromeos/test/data/network/shill_l2tpipsec.json @@ -0,0 +1,10 @@ +{ "GUID": "guid", + "Type": "vpn", + "Name": "MyL2TPVPN", + "Provider.Host": "some.host.org", + "Provider.Type": "l2tpipsec", + "L2TPIPsec.PSK": "some_preshared_key", + "L2TPIPsec.User": "some username", + "L2TPIPsec.Password": "some password", + "SaveCredentials": true +} diff --git a/chromeos/test/data/network/shill_openvpn.json b/chromeos/test/data/network/shill_openvpn.json new file mode 100644 index 0000000..1bf84ef --- /dev/null +++ b/chromeos/test/data/network/shill_openvpn.json @@ -0,0 +1,25 @@ +{ + "GUID": "{a3860e83-f03d-4cb1-bafa-b22c9e746950}", + "Type": "vpn", + "Name": "google-dogfood", + "SaveCredentials": false, + "Provider.Type": "openvpn", + "Provider.Host": "v.ext.google.com", + "OpenVPN.AuthRetry": "interact", + "OpenVPN.User": "some username", + "OpenVPN.Password": "some password", + "OpenVPN.StaticChallenge": "Please enter token OTP", + "OpenVPN.CompLZO": "true", + "OpenVPN.ServerPollTimeout": "10", + "OpenVPN.RemoteCertKU": "e0", + "OpenVPN.CACertNSS": "{14ff4d51-64c1-4c86-a622-054d8e55e750}", + "OpenVPN.RemoteCertTLS": "server", + "OpenVPN.Port": "443", + "OpenVPN.TLSRemote": "v.ext.google.com", + "OpenVPN.KeyDirection": "1", + "OpenVPN.RenegSec": "0", + "OpenVPN.RemoteCertEKU": "TLS Web Server Authentication", + "OpenVPN.Proto": "udp", + "OpenVPN.PushPeerInfo": "true", + "OpenVPN.TLSAuthContents": "-----BEGIN OpenVPN Static key V1-----\nf8a19b8fddda3a38e06b883c48c83474\n0d1dfe7e5cadb2891387ac2d85198cdf\ne14274d03d12c4128c6c8967f84b1b39\n22452fb869280cde868bcf3932615927\nab1948d951c19183276fa249fd6e1e5f\nfc1c41400b59493a39cad872f5c7e777\n310d685d9c30abff7f206afc09cc99d9\ne40b2091e29af5b37d191628fba6d86e\n3a239b45b1b5a6be3229663ee7a7dc67\n2fa5be59fe7ceb41d2c78f94237c2185\n0aea1b06d739bca83240671167ddd2c2\n9cfd931df468536bfd1be99cd05160d8\n09ebfe4b8da88fb1923b19ad1abfc45e\n0402321e41af54e7d19ac95b1a7cec9a\nbf60a8eabcd5eecc84773af211477ee9\n9c988269638f9f1bb668028bb688dea2\nEND OpenVPN Static key V1-----\n" +} diff --git a/chromeos/test/data/network/user.onc b/chromeos/test/data/network/user.onc new file mode 100644 index 0000000..bd97bb3 --- /dev/null +++ b/chromeos/test/data/network/user.onc @@ -0,0 +1,21 @@ +{ "Type": "Ethernet", + "Name": "testopenvpn", + "VPN": { + "Host": "users host", + "Type": "OpenVPN", + "OpenVPN": { + "Port": 1195, + "Password": "users password" + } + }, + "ProxySettings": { + "Type": "Direct" + }, + "IPConfigs": [ + { "Type": "IPv4", + "IPAddress": "127.0.0.1", + "SearchDomains": [ "acme.org" ] }, + { "Type": "IPv4", + "IPAddress": "1.2.3.4" } + ] +} diff --git a/chromeos/test/data/network/valid.onc b/chromeos/test/data/network/valid.onc new file mode 100644 index 0000000..56828bc --- /dev/null +++ b/chromeos/test/data/network/valid.onc @@ -0,0 +1,7 @@ +{ "GUID": "guid", + "Type": "Ethernet", + "Name": "name", + "Ethernet": { + "Authentication": "None" + } +} diff --git a/chromeos/test/data/network/valid_l2tpipsec.onc b/chromeos/test/data/network/valid_l2tpipsec.onc new file mode 100644 index 0000000..0e0bc5f --- /dev/null +++ b/chromeos/test/data/network/valid_l2tpipsec.onc @@ -0,0 +1,18 @@ +{ "GUID": "guid", + "Type": "VPN", + "Name": "MyL2TPVPN", + "VPN": { + "Type": "L2TP-IPsec", + "Host": "some.host.org", + "IPsec": { + "AuthenticationType": "PSK", + "IKEVersion": 1, + "PSK": "some_preshared_key", + "SaveCredentials": true + }, + "L2TP": { + "Username": "some username", + "Password": "some password" + } + } +} diff --git a/chromeos/test/data/network/valid_openvpn.onc b/chromeos/test/data/network/valid_openvpn.onc new file mode 100644 index 0000000..512cb0e --- /dev/null +++ b/chromeos/test/data/network/valid_openvpn.onc @@ -0,0 +1,31 @@ +{ + "GUID": "{a3860e83-f03d-4cb1-bafa-b22c9e746950}", + "Name": "google-dogfood", + "Type": "VPN", + "VPN": { + "Host": "v.ext.google.com", + "Type": "OpenVPN", + "OpenVPN": { + "AuthRetry": "interact", + "CompLZO": "true", + "KeyDirection": "1", + "Password": "some password", + "Port": 443, + "Proto": "udp", + "PushPeerInfo": true, + "RemoteCertEKU": "TLS Web Server Authentication", + "RemoteCertKU": [ + "e0" + ], + "RemoteCertTLS": "server", + "RenegSec": 0, + "SaveCredentials": false, + "ServerCARef": "{14ff4d51-64c1-4c86-a622-054d8e55e750}", + "ServerPollTimeout": 10, + "StaticChallenge": "Please enter token OTP", + "TLSAuthContents": "-----BEGIN OpenVPN Static key V1-----\nf8a19b8fddda3a38e06b883c48c83474\n0d1dfe7e5cadb2891387ac2d85198cdf\ne14274d03d12c4128c6c8967f84b1b39\n22452fb869280cde868bcf3932615927\nab1948d951c19183276fa249fd6e1e5f\nfc1c41400b59493a39cad872f5c7e777\n310d685d9c30abff7f206afc09cc99d9\ne40b2091e29af5b37d191628fba6d86e\n3a239b45b1b5a6be3229663ee7a7dc67\n2fa5be59fe7ceb41d2c78f94237c2185\n0aea1b06d739bca83240671167ddd2c2\n9cfd931df468536bfd1be99cd05160d8\n09ebfe4b8da88fb1923b19ad1abfc45e\n0402321e41af54e7d19ac95b1a7cec9a\nbf60a8eabcd5eecc84773af211477ee9\n9c988269638f9f1bb668028bb688dea2\nEND OpenVPN Static key V1-----\n", + "TLSRemote": "v.ext.google.com", + "Username": "some username", + } + } +} |