blob: 6b48c5cad2e4674e212ac93c91b0ab9fe2633da9 [file] [log] [blame]
// Copyright 2013 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 "base/callback.h"
#include "base/files/file_path.h"
#include "base/memory/scoped_ptr.h"
#include "base/run_loop.h"
#include "base/values.h"
#include "chrome/browser/chromeos/login/user.h"
#include "chrome/browser/chromeos/policy/policy_cert_verifier.h"
#include "chrome/browser/chromeos/policy/user_network_configuration_updater.h"
#include "chrome/browser/policy/external_data_fetcher.h"
#include "chrome/browser/policy/mock_configuration_policy_provider.h"
#include "chrome/browser/policy/policy_map.h"
#include "chrome/browser/policy/policy_service_impl.h"
#include "chromeos/network/mock_managed_network_configuration_handler.h"
#include "chromeos/network/onc/mock_certificate_importer.h"
#include "chromeos/network/onc/onc_test_utils.h"
#include "chromeos/network/onc/onc_utils.h"
#include "components/onc/onc_constants.h"
#include "content/public/test/test_browser_thread_bundle.h"
#include "content/public/test/test_utils.h"
#include "net/base/test_data_directory.h"
#include "net/cert/x509_certificate.h"
#include "net/test/cert_test_util.h"
#include "policy/policy_constants.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
using testing::AnyNumber;
using testing::AtLeast;
using testing::Mock;
using testing::Ne;
using testing::Return;
using testing::StrictMock;
using testing::_;
namespace policy {
namespace {
const char kFakeUserEmail[] = "fake email";
const char kFakeUsernameHash[] = "fake hash";
class FakeUser : public chromeos::User {
public:
FakeUser() : User(kFakeUserEmail) {
set_display_email(kFakeUserEmail);
set_username_hash(kFakeUsernameHash);
}
virtual ~FakeUser() {}
// User overrides
virtual UserType GetType() const OVERRIDE {
return USER_TYPE_REGULAR;
}
private:
DISALLOW_COPY_AND_ASSIGN(FakeUser);
};
const char kFakeONC[] =
"{ \"NetworkConfigurations\": ["
" { \"GUID\": \"{485d6076-dd44-6b6d-69787465725f5040}\","
" \"Type\": \"WiFi\","
" \"Name\": \"My WiFi Network\","
" \"WiFi\": {"
" \"SSID\": \"ssid-none\","
" \"Security\": \"None\" }"
" }"
" ],"
" \"Certificates\": ["
" { \"GUID\": \"{f998f760-272b-6939-4c2beffe428697ac}\","
" \"PKCS12\": \"abc\","
" \"Type\": \"Client\" }"
" ],"
" \"Type\": \"UnencryptedConfiguration\""
"}";
std::string ValueToString(const base::Value* value) {
std::stringstream str;
str << *value;
return str.str();
}
// Matcher to match base::Value.
MATCHER_P(IsEqualTo,
value,
std::string(negation ? "isn't" : "is") + " equal to " +
ValueToString(value)) {
return value->Equals(&arg);
}
ACTION_P(SetCertificateList, list) {
if (arg2)
*arg2 = list;
return true;
}
} // namespace
class NetworkConfigurationUpdaterTest : public testing::Test {
protected:
NetworkConfigurationUpdaterTest() {
}
virtual void SetUp() OVERRIDE {
EXPECT_CALL(provider_, IsInitializationComplete(_))
.WillRepeatedly(Return(true));
provider_.Init();
PolicyServiceImpl::Providers providers;
providers.push_back(&provider_);
policy_service_.reset(new PolicyServiceImpl(providers));
empty_network_configs_.reset(new base::ListValue);
empty_certificates_.reset(new base::ListValue);
scoped_ptr<base::DictionaryValue> fake_toplevel_onc =
chromeos::onc::ReadDictionaryFromJson(kFakeONC);
scoped_ptr<base::Value> network_configs_value;
base::ListValue* network_configs = NULL;
fake_toplevel_onc->RemoveWithoutPathExpansion(
onc::toplevel_config::kNetworkConfigurations, &network_configs_value);
network_configs_value.release()->GetAsList(&network_configs);
fake_network_configs_.reset(network_configs);
scoped_ptr<base::Value> certs_value;
base::ListValue* certs = NULL;
fake_toplevel_onc->RemoveWithoutPathExpansion(
onc::toplevel_config::kCertificates, &certs_value);
certs_value.release()->GetAsList(&certs);
fake_certificates_.reset(certs);
certificate_importer_ =
new StrictMock<chromeos::onc::MockCertificateImporter>();
certificate_importer_owned_.reset(certificate_importer_);
}
virtual void TearDown() OVERRIDE {
network_configuration_updater_.reset();
provider_.Shutdown();
base::RunLoop().RunUntilIdle();
}
void UpdateProviderPolicy(const PolicyMap& policy) {
provider_.UpdateChromePolicy(policy);
base::RunLoop().RunUntilIdle();
}
UserNetworkConfigurationUpdater*
CreateNetworkConfigurationUpdaterForUserPolicy(
bool allow_trusted_certs_from_policy) {
UserNetworkConfigurationUpdater* updater =
UserNetworkConfigurationUpdater::CreateForUserPolicy(
allow_trusted_certs_from_policy,
fake_user_,
certificate_importer_owned_.Pass(),
policy_service_.get(),
&network_config_handler_).release();
network_configuration_updater_.reset(updater);
return updater;
}
void CreateNetworkConfigurationUpdaterForDevicePolicy() {
network_configuration_updater_ =
NetworkConfigurationUpdater::CreateForDevicePolicy(
certificate_importer_owned_.Pass(),
policy_service_.get(),
&network_config_handler_);
}
scoped_ptr<base::ListValue> empty_network_configs_;
scoped_ptr<base::ListValue> empty_certificates_;
scoped_ptr<base::ListValue> fake_network_configs_;
scoped_ptr<base::ListValue> fake_certificates_;
StrictMock<chromeos::MockManagedNetworkConfigurationHandler>
network_config_handler_;
// Ownership of certificate_importer_owned_ is passed to the
// NetworkConfigurationUpdater. When that happens, |certificate_importer_|
// continues to point to that instance but |certificate_importer_owned_| is
// released.
StrictMock<chromeos::onc::MockCertificateImporter>* certificate_importer_;
scoped_ptr<chromeos::onc::CertificateImporter> certificate_importer_owned_;
StrictMock<MockConfigurationPolicyProvider> provider_;
scoped_ptr<PolicyServiceImpl> policy_service_;
FakeUser fake_user_;
scoped_ptr<NetworkConfigurationUpdater> network_configuration_updater_;
content::TestBrowserThreadBundle thread_bundle_;
};
TEST_F(NetworkConfigurationUpdaterTest, PolicyIsValidatedAndRepaired) {
std::string onc_policy =
chromeos::onc::test_utils::ReadTestData("toplevel_partially_invalid.onc");
scoped_ptr<base::DictionaryValue> onc_repaired =
chromeos::onc::test_utils::ReadTestDictionary(
"repaired_toplevel_partially_invalid.onc");
base::ListValue* network_configs_repaired = NULL;
onc_repaired->GetListWithoutPathExpansion(
onc::toplevel_config::kNetworkConfigurations, &network_configs_repaired);
ASSERT_TRUE(network_configs_repaired);
PolicyMap policy;
policy.Set(key::kOpenNetworkConfiguration,
POLICY_LEVEL_MANDATORY,
POLICY_SCOPE_USER,
new base::StringValue(onc_policy),
NULL);
UpdateProviderPolicy(policy);
EXPECT_CALL(
network_config_handler_,
SetPolicy(
onc::ONC_SOURCE_USER_POLICY, _, IsEqualTo(network_configs_repaired)));
EXPECT_CALL(*certificate_importer_,
ImportCertificates(_, onc::ONC_SOURCE_USER_POLICY, _));
CreateNetworkConfigurationUpdaterForUserPolicy(
false /* do not allow trusted certs from policy */ );
}
TEST_F(NetworkConfigurationUpdaterTest,
DoNotAllowTrustedCertificatesFromPolicy) {
net::CertificateList cert_list;
cert_list =
net::CreateCertificateListFromFile(net::GetTestCertsDirectory(),
"ok_cert.pem",
net::X509Certificate::FORMAT_AUTO);
ASSERT_EQ(1u, cert_list.size());
EXPECT_CALL(network_config_handler_,
SetPolicy(onc::ONC_SOURCE_USER_POLICY, _, _));
EXPECT_CALL(*certificate_importer_, ImportCertificates(_, _, _))
.WillRepeatedly(SetCertificateList(cert_list));
UserNetworkConfigurationUpdater* updater =
CreateNetworkConfigurationUpdaterForUserPolicy(
false /* do not allow trusted certs from policy */);
// Certificates with the "Web" trust flag set should not be forwarded to the
// trust provider.
policy::PolicyCertVerifier cert_verifier((
base::Closure() /* no policy cert trusted callback */));
updater->SetPolicyCertVerifier(&cert_verifier);
base::RunLoop().RunUntilIdle();
EXPECT_TRUE(cert_verifier.GetAdditionalTrustAnchors().empty());
// |cert_verifier| must outlive the updater.
network_configuration_updater_.reset();
}
TEST_F(NetworkConfigurationUpdaterTest, AllowTrustedCertificatesFromPolicy) {
net::CertificateList cert_list;
cert_list =
net::CreateCertificateListFromFile(net::GetTestCertsDirectory(),
"ok_cert.pem",
net::X509Certificate::FORMAT_AUTO);
ASSERT_EQ(1u, cert_list.size());
EXPECT_CALL(network_config_handler_,
SetPolicy(onc::ONC_SOURCE_USER_POLICY, _, _));
EXPECT_CALL(*certificate_importer_,
ImportCertificates(_, onc::ONC_SOURCE_USER_POLICY, _))
.WillRepeatedly(SetCertificateList(cert_list));
UserNetworkConfigurationUpdater* updater =
CreateNetworkConfigurationUpdaterForUserPolicy(
true /* allow trusted certs from policy */);
// Certificates with the "Web" trust flag set should be forwarded to the
// trust provider.
policy::PolicyCertVerifier cert_verifier((
base::Closure() /* no policy cert trusted callback */));
updater->SetPolicyCertVerifier(&cert_verifier);
base::RunLoop().RunUntilIdle();
EXPECT_EQ(1u, cert_verifier.GetAdditionalTrustAnchors().size());
// |cert_verifier| must outlive the updater.
network_configuration_updater_.reset();
}
class NetworkConfigurationUpdaterTestWithParam
: public NetworkConfigurationUpdaterTest,
public testing::WithParamInterface<const char*> {
protected:
// Returns the currently tested ONC source.
onc::ONCSource CurrentONCSource() {
if (GetParam() == key::kOpenNetworkConfiguration)
return onc::ONC_SOURCE_USER_POLICY;
DCHECK(GetParam() == key::kDeviceOpenNetworkConfiguration);
return onc::ONC_SOURCE_DEVICE_POLICY;
}
// Returns the expected username hash to push policies to
// ManagedNetworkConfigurationHandler.
std::string ExpectedUsernameHash() {
if (GetParam() == key::kOpenNetworkConfiguration)
return kFakeUsernameHash;
return std::string();
}
void CreateNetworkConfigurationUpdater() {
if (GetParam() == key::kOpenNetworkConfiguration) {
CreateNetworkConfigurationUpdaterForUserPolicy(
false /* do not allow trusted certs from policy */);
} else {
CreateNetworkConfigurationUpdaterForDevicePolicy();
}
}
};
TEST_P(NetworkConfigurationUpdaterTestWithParam, InitialUpdates) {
PolicyMap policy;
policy.Set(GetParam(), POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
new base::StringValue(kFakeONC), NULL);
UpdateProviderPolicy(policy);
EXPECT_CALL(network_config_handler_,
SetPolicy(CurrentONCSource(),
ExpectedUsernameHash(),
IsEqualTo(fake_network_configs_.get())));
EXPECT_CALL(*certificate_importer_,
ImportCertificates(
IsEqualTo(fake_certificates_.get()), CurrentONCSource(), _));
CreateNetworkConfigurationUpdater();
}
TEST_P(NetworkConfigurationUpdaterTestWithParam, PolicyChange) {
// Ignore the initial updates.
EXPECT_CALL(network_config_handler_, SetPolicy(_, _, _)).Times(AtLeast(1));
EXPECT_CALL(*certificate_importer_, ImportCertificates(_, _, _))
.Times(AtLeast(1));
CreateNetworkConfigurationUpdater();
Mock::VerifyAndClearExpectations(&network_config_handler_);
Mock::VerifyAndClearExpectations(certificate_importer_);
// The Updater should update if policy changes.
EXPECT_CALL(
network_config_handler_,
SetPolicy(CurrentONCSource(), _, IsEqualTo(fake_network_configs_.get())));
EXPECT_CALL(*certificate_importer_,
ImportCertificates(
IsEqualTo(fake_certificates_.get()), CurrentONCSource(), _));
PolicyMap policy;
policy.Set(GetParam(), POLICY_LEVEL_MANDATORY, POLICY_SCOPE_USER,
new base::StringValue(kFakeONC), NULL);
UpdateProviderPolicy(policy);
Mock::VerifyAndClearExpectations(&network_config_handler_);
Mock::VerifyAndClearExpectations(certificate_importer_);
// Another update is expected if the policy goes away.
EXPECT_CALL(
network_config_handler_,
SetPolicy(
CurrentONCSource(), _, IsEqualTo(empty_network_configs_.get())));
EXPECT_CALL(*certificate_importer_,
ImportCertificates(
IsEqualTo(empty_certificates_.get()), CurrentONCSource(), _));
policy.Erase(GetParam());
UpdateProviderPolicy(policy);
}
INSTANTIATE_TEST_CASE_P(NetworkConfigurationUpdaterTestWithParamInstance,
NetworkConfigurationUpdaterTestWithParam,
testing::Values(key::kDeviceOpenNetworkConfiguration,
key::kOpenNetworkConfiguration));
} // namespace policy