// Copyright (c) 2011 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/browser/net/ssl_config_service_manager.h"

#include "base/command_line.h"
#include "base/memory/ref_counted.h"
#include "base/message_loop.h"
#include "base/values.h"
#include "chrome/browser/prefs/pref_service_mock_builder.h"
#include "chrome/browser/prefs/testing_pref_store.h"
#include "chrome/common/chrome_switches.h"
#include "chrome/common/pref_names.h"
#include "chrome/test/base/testing_pref_service.h"
#include "content/test/test_browser_thread.h"
#include "net/base/ssl_config_service.h"
#include "testing/gtest/include/gtest/gtest.h"

using base::ListValue;
using base::Value;
using content::BrowserThread;
using net::SSLConfig;
using net::SSLConfigService;

class SSLConfigServiceManagerPrefTest : public testing::Test {
 public:
  SSLConfigServiceManagerPrefTest()
      : ui_thread_(BrowserThread::UI, &message_loop_),
        io_thread_(BrowserThread::IO, &message_loop_) {}

 protected:
  MessageLoop message_loop_;
  content::TestBrowserThread ui_thread_;
  content::TestBrowserThread io_thread_;
};

// Test that cipher suites can be disabled. "Good" refers to the fact that
// every value is expected to be successfully parsed into a cipher suite.
TEST_F(SSLConfigServiceManagerPrefTest, GoodDisabledCipherSuites) {
  TestingPrefService pref_service;
  SSLConfigServiceManager::RegisterPrefs(&pref_service);

  scoped_ptr<SSLConfigServiceManager> config_manager(
      SSLConfigServiceManager::CreateDefaultManager(&pref_service));
  ASSERT_TRUE(config_manager.get());
  scoped_refptr<SSLConfigService> config_service(config_manager->Get());
  ASSERT_TRUE(config_service.get());

  SSLConfig old_config;
  config_service->GetSSLConfig(&old_config);
  EXPECT_TRUE(old_config.disabled_cipher_suites.empty());

  ListValue* list_value = new ListValue();
  list_value->Append(Value::CreateStringValue("0x0004"));
  list_value->Append(Value::CreateStringValue("0x0005"));
  pref_service.SetUserPref(prefs::kCipherSuiteBlacklist, list_value);

  // Pump the message loop to notify the SSLConfigServiceManagerPref that the
  // preferences changed.
  message_loop_.RunAllPending();

  SSLConfig config;
  config_service->GetSSLConfig(&config);

  EXPECT_NE(old_config.disabled_cipher_suites, config.disabled_cipher_suites);
  ASSERT_EQ(2u, config.disabled_cipher_suites.size());
  EXPECT_EQ(0x0004, config.disabled_cipher_suites[0]);
  EXPECT_EQ(0x0005, config.disabled_cipher_suites[1]);
}

// Test that cipher suites can be disabled. "Bad" refers to the fact that
// there are one or more non-cipher suite strings in the preference. They
// should be ignored.
TEST_F(SSLConfigServiceManagerPrefTest, BadDisabledCipherSuites) {
  TestingPrefService pref_service;
  SSLConfigServiceManager::RegisterPrefs(&pref_service);

  scoped_ptr<SSLConfigServiceManager> config_manager(
      SSLConfigServiceManager::CreateDefaultManager(&pref_service));
  ASSERT_TRUE(config_manager.get());
  scoped_refptr<SSLConfigService> config_service(config_manager->Get());
  ASSERT_TRUE(config_service.get());

  SSLConfig old_config;
  config_service->GetSSLConfig(&old_config);
  EXPECT_TRUE(old_config.disabled_cipher_suites.empty());

  ListValue* list_value = new ListValue();
  list_value->Append(Value::CreateStringValue("0x0004"));
  list_value->Append(Value::CreateStringValue("TLS_NOT_WITH_A_CIPHER_SUITE"));
  list_value->Append(Value::CreateStringValue("0x0005"));
  list_value->Append(Value::CreateStringValue("0xBEEFY"));
  pref_service.SetUserPref(prefs::kCipherSuiteBlacklist, list_value);

  // Pump the message loop to notify the SSLConfigServiceManagerPref that the
  // preferences changed.
  message_loop_.RunAllPending();

  SSLConfig config;
  config_service->GetSSLConfig(&config);

  EXPECT_NE(old_config.disabled_cipher_suites, config.disabled_cipher_suites);
  ASSERT_EQ(2u, config.disabled_cipher_suites.size());
  EXPECT_EQ(0x0004, config.disabled_cipher_suites[0]);
  EXPECT_EQ(0x0005, config.disabled_cipher_suites[1]);
}

// Test that existing user settings for TLS1.0/SSL3.0 are both ignored and
// cleared from user preferences.
TEST_F(SSLConfigServiceManagerPrefTest, IgnoreLegacySSLSettings) {
  scoped_refptr<TestingPrefStore> user_prefs(new TestingPrefStore());

  // SSL3.0 and TLS1.0 used to be user-definable prefs. They are now used as
  // command-line options. Ensure any existing user prefs are ignored in
  // favour of the command-line flags.
  user_prefs->SetBoolean(prefs::kSSL3Enabled, false);
  user_prefs->SetBoolean(prefs::kTLS1Enabled, false);

  // Ensure the preferences exist initially.
  bool is_ssl3_enabled = true;
  EXPECT_TRUE(user_prefs->GetBoolean(prefs::kSSL3Enabled, &is_ssl3_enabled));
  EXPECT_FALSE(is_ssl3_enabled);

  bool is_tls1_enabled = true;
  EXPECT_TRUE(user_prefs->GetBoolean(prefs::kTLS1Enabled, &is_tls1_enabled));
  EXPECT_FALSE(is_tls1_enabled);

  PrefServiceMockBuilder builder;
  builder.WithUserPrefs(user_prefs.get());
  scoped_ptr<PrefService> pref_service(builder.Create());

  SSLConfigServiceManager::RegisterPrefs(pref_service.get());

  scoped_ptr<SSLConfigServiceManager> config_manager(
      SSLConfigServiceManager::CreateDefaultManager(pref_service.get()));
  ASSERT_TRUE(config_manager.get());
  scoped_refptr<SSLConfigService> config_service(config_manager->Get());
  ASSERT_TRUE(config_service.get());

  SSLConfig ssl_config;
  config_service->GetSSLConfig(&ssl_config);
  // The default value in the absence of command-line options is that both
  // protocols are enabled.
  EXPECT_TRUE(ssl_config.ssl3_enabled);
  EXPECT_TRUE(ssl_config.tls1_enabled);

  // The existing user settings should be removed from the pref_service.
  EXPECT_FALSE(pref_service->HasPrefPath(prefs::kSSL3Enabled));
  EXPECT_FALSE(pref_service->HasPrefPath(prefs::kTLS1Enabled));

  // Explicitly double-check the settings are not in the user preference
  // store.
  EXPECT_FALSE(user_prefs->GetBoolean(prefs::kSSL3Enabled, &is_ssl3_enabled));
  EXPECT_FALSE(user_prefs->GetBoolean(prefs::kTLS1Enabled, &is_tls1_enabled));
}

// Test that command-line settings for TLS1.0/SSL3.0 are respected, that they
// disregard any existing user preferences, and that they do not persist to
// the user preferences files.
TEST_F(SSLConfigServiceManagerPrefTest, CommandLineOverridesUserPrefs) {
  scoped_refptr<TestingPrefStore> user_prefs(new TestingPrefStore());

  // Explicitly enable SSL3.0/TLS1.0 in the user preferences, to mirror the
  // more common legacy file.
  user_prefs->SetBoolean(prefs::kSSL3Enabled, true);
  user_prefs->SetBoolean(prefs::kTLS1Enabled, true);

  // Ensure the preferences exist initially.
  bool is_ssl3_enabled = false;
  EXPECT_TRUE(user_prefs->GetBoolean(prefs::kSSL3Enabled, &is_ssl3_enabled));
  EXPECT_TRUE(is_ssl3_enabled);

  bool is_tls1_enabled = false;
  EXPECT_TRUE(user_prefs->GetBoolean(prefs::kTLS1Enabled, &is_tls1_enabled));
  EXPECT_TRUE(is_tls1_enabled);

  CommandLine command_line(CommandLine::NO_PROGRAM);
  command_line.AppendSwitch(switches::kDisableSSL3);
  command_line.AppendSwitch(switches::kDisableTLS1);

  PrefServiceMockBuilder builder;
  builder.WithUserPrefs(user_prefs.get());
  builder.WithCommandLine(&command_line);
  scoped_ptr<PrefService> pref_service(builder.Create());

  SSLConfigServiceManager::RegisterPrefs(pref_service.get());

  scoped_ptr<SSLConfigServiceManager> config_manager(
      SSLConfigServiceManager::CreateDefaultManager(pref_service.get()));
  ASSERT_TRUE(config_manager.get());
  scoped_refptr<SSLConfigService> config_service(config_manager->Get());
  ASSERT_TRUE(config_service.get());

  SSLConfig ssl_config;
  config_service->GetSSLConfig(&ssl_config);
  // Command-line flags to disable should override the user preferences to
  // enable.
  EXPECT_FALSE(ssl_config.ssl3_enabled);
  EXPECT_FALSE(ssl_config.tls1_enabled);

  // Explicitly double-check the settings are not in the user preference
  // store.
  const PrefService::Preference* ssl3_enabled_pref =
      pref_service->FindPreference(prefs::kSSL3Enabled);
  EXPECT_FALSE(ssl3_enabled_pref->IsUserModifiable());

  const PrefService::Preference* tls1_enabled_pref =
      pref_service->FindPreference(prefs::kTLS1Enabled);
  EXPECT_FALSE(tls1_enabled_pref->IsUserModifiable());

  EXPECT_FALSE(user_prefs->GetBoolean(prefs::kSSL3Enabled, &is_ssl3_enabled));
  EXPECT_FALSE(user_prefs->GetBoolean(prefs::kTLS1Enabled, &is_tls1_enabled));
}