chrome_ssl_host_state_delegate_test.cc revision 5f1c94371a64b3196d4be9466099bb892df9b88e
1// Copyright 2014 The Chromium Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5#include <stdint.h> 6 7#include "base/command_line.h" 8#include "base/strings/string_number_conversions.h" 9#include "base/test/simple_test_clock.h" 10#include "chrome/browser/browsing_data/browsing_data_helper.h" 11#include "chrome/browser/browsing_data/browsing_data_remover.h" 12#include "chrome/browser/browsing_data/browsing_data_remover_test_util.h" 13#include "chrome/browser/profiles/profile.h" 14#include "chrome/browser/ssl/chrome_ssl_host_state_delegate.h" 15#include "chrome/browser/ui/browser.h" 16#include "chrome/browser/ui/tabs/tab_strip_model.h" 17#include "chrome/common/chrome_switches.h" 18#include "chrome/test/base/in_process_browser_test.h" 19#include "content/public/browser/ssl_host_state_delegate.h" 20#include "content/public/browser/web_contents.h" 21#include "content/public/test/browser_test_utils.h" 22#include "net/base/test_data_directory.h" 23#include "net/test/cert_test_util.h" 24#include "testing/gtest/include/gtest/gtest.h" 25 26namespace { 27 28const char kGoogleCertFile[] = "google.single.der"; 29 30const char kWWWGoogleHost[] = "www.google.com"; 31const char kGoogleHost[] = "google.com"; 32const char kExampleHost[] = "example.com"; 33 34const char* kForgetAtSessionEnd = "-1"; 35const char* kForgetInstantly = "0"; 36const char* kDeltaSecondsString = "86400"; 37const uint64_t kDeltaOneDayInSeconds = UINT64_C(86400); 38 39scoped_refptr<net::X509Certificate> GetGoogleCert() { 40 return net::ImportCertFromFile(net::GetTestCertsDirectory(), kGoogleCertFile); 41} 42 43} // namespace 44 45class ChromeSSLHostStateDelegateTest : public InProcessBrowserTest {}; 46 47// ChromeSSLHostStateDelegateTest tests basic unit test functionality of the 48// SSLHostStateDelegate class. For example, tests that if a certificate is 49// accepted, then it is added to queryable, and if it is revoked, it is not 50// queryable. Even though it is effectively a unit test, in needs to be an 51// InProcessBrowserTest because the actual functionality is provided by 52// ChromeSSLHostStateDelegate which is provided per-profile. 53// 54// QueryPolicy unit tests the expected behavior of calling QueryPolicy on the 55// SSLHostStateDelegate class after various SSL cert decisions have been made. 56IN_PROC_BROWSER_TEST_F(ChromeSSLHostStateDelegateTest, QueryPolicy) { 57 scoped_refptr<net::X509Certificate> google_cert = GetGoogleCert(); 58 content::WebContents* tab = 59 browser()->tab_strip_model()->GetActiveWebContents(); 60 Profile* profile = Profile::FromBrowserContext(tab->GetBrowserContext()); 61 content::SSLHostStateDelegate* state = profile->GetSSLHostStateDelegate(); 62 63 // Verifying that all three of the certs we will be looking at are unknown 64 // before any action has been taken. 65 EXPECT_EQ( 66 net::CertPolicy::UNKNOWN, 67 state->QueryPolicy( 68 kWWWGoogleHost, google_cert.get(), net::CERT_STATUS_DATE_INVALID)); 69 EXPECT_EQ(net::CertPolicy::UNKNOWN, 70 state->QueryPolicy( 71 kGoogleHost, google_cert.get(), net::CERT_STATUS_DATE_INVALID)); 72 EXPECT_EQ( 73 net::CertPolicy::UNKNOWN, 74 state->QueryPolicy( 75 kExampleHost, google_cert.get(), net::CERT_STATUS_DATE_INVALID)); 76 77 // Simulate a user decision to allow an invalid certificate exception for 78 // kWWWGoogleHost. 79 state->AllowCert( 80 kWWWGoogleHost, google_cert.get(), net::CERT_STATUS_DATE_INVALID); 81 82 // Verify that only kWWWGoogleHost is allowed and that the other two certs 83 // being tested still have no decision associated with them. 84 EXPECT_EQ( 85 net::CertPolicy::ALLOWED, 86 state->QueryPolicy( 87 kWWWGoogleHost, google_cert.get(), net::CERT_STATUS_DATE_INVALID)); 88 EXPECT_EQ(net::CertPolicy::UNKNOWN, 89 state->QueryPolicy( 90 kGoogleHost, google_cert.get(), net::CERT_STATUS_DATE_INVALID)); 91 EXPECT_EQ( 92 net::CertPolicy::UNKNOWN, 93 state->QueryPolicy( 94 kExampleHost, google_cert.get(), net::CERT_STATUS_DATE_INVALID)); 95 96 // Simulate a user decision to allow an invalid certificate exception for 97 // kExampleHost. 98 state->AllowCert( 99 kExampleHost, google_cert.get(), net::CERT_STATUS_DATE_INVALID); 100 101 // Verify that both kWWWGoogleHost and kExampleHost have allow exceptions 102 // while kGoogleHost still has no associated decision. 103 EXPECT_EQ( 104 net::CertPolicy::ALLOWED, 105 state->QueryPolicy( 106 kWWWGoogleHost, google_cert.get(), net::CERT_STATUS_DATE_INVALID)); 107 EXPECT_EQ(net::CertPolicy::UNKNOWN, 108 state->QueryPolicy( 109 kGoogleHost, google_cert.get(), net::CERT_STATUS_DATE_INVALID)); 110 EXPECT_EQ( 111 net::CertPolicy::ALLOWED, 112 state->QueryPolicy( 113 kExampleHost, google_cert.get(), net::CERT_STATUS_DATE_INVALID)); 114 115 // Simulate a user decision to deny an invalid certificate for kExampleHost. 116 state->DenyCert( 117 kExampleHost, google_cert.get(), net::CERT_STATUS_DATE_INVALID); 118 119 // Verify that kWWWGoogleHost is allowed and kExampleHost is denied while 120 // kGoogleHost still has no associated decision. 121 EXPECT_EQ( 122 net::CertPolicy::ALLOWED, 123 state->QueryPolicy( 124 kWWWGoogleHost, google_cert.get(), net::CERT_STATUS_DATE_INVALID)); 125 EXPECT_EQ(net::CertPolicy::UNKNOWN, 126 state->QueryPolicy( 127 kGoogleHost, google_cert.get(), net::CERT_STATUS_DATE_INVALID)); 128 EXPECT_EQ( 129 net::CertPolicy::DENIED, 130 state->QueryPolicy( 131 kExampleHost, google_cert.get(), net::CERT_STATUS_DATE_INVALID)); 132} 133 134// HasPolicyAndRevoke unit tests the expected behavior of calling 135// HasAllowedOrDeniedCert before and after calling RevokeAllowAndDenyPreferences 136// on the SSLHostStateDelegate class. 137IN_PROC_BROWSER_TEST_F(ChromeSSLHostStateDelegateTest, HasPolicyAndRevoke) { 138 scoped_refptr<net::X509Certificate> google_cert = GetGoogleCert(); 139 content::WebContents* tab = 140 browser()->tab_strip_model()->GetActiveWebContents(); 141 Profile* profile = Profile::FromBrowserContext(tab->GetBrowserContext()); 142 content::SSLHostStateDelegate* state = profile->GetSSLHostStateDelegate(); 143 144 // Simulate a user decision to allow an invalid certificate exception for 145 // kWWWGoogleHost and for kExampleHost. 146 state->AllowCert( 147 kWWWGoogleHost, google_cert.get(), net::CERT_STATUS_DATE_INVALID); 148 state->AllowCert( 149 kExampleHost, google_cert.get(), net::CERT_STATUS_DATE_INVALID); 150 151 // Verify that HasAllowedOrDeniedCert correctly acknowledges that a user 152 // decision has been made about kWWWGoogleHost. Then verify that 153 // HasAllowedOrDeniedCert correctly identifies that the decision has been 154 // revoked. 155 EXPECT_TRUE(state->HasAllowedOrDeniedCert(kWWWGoogleHost)); 156 state->RevokeAllowAndDenyPreferences(kWWWGoogleHost); 157 EXPECT_FALSE(state->HasAllowedOrDeniedCert(kWWWGoogleHost)); 158 EXPECT_EQ( 159 net::CertPolicy::UNKNOWN, 160 state->QueryPolicy( 161 kWWWGoogleHost, google_cert.get(), net::CERT_STATUS_DATE_INVALID)); 162 163 // Verify that the revocation of the kWWWGoogleHost decision does not affect 164 // the Allow for kExampleHost. 165 EXPECT_TRUE(state->HasAllowedOrDeniedCert(kExampleHost)); 166 167 // Verify the revocation of the kWWWGoogleHost decision does not affect the 168 // non-decision for kGoogleHost. Then verify that a revocation of a URL with 169 // no decision has no effect. 170 EXPECT_FALSE(state->HasAllowedOrDeniedCert(kGoogleHost)); 171 state->RevokeAllowAndDenyPreferences(kGoogleHost); 172 EXPECT_FALSE(state->HasAllowedOrDeniedCert(kGoogleHost)); 173} 174 175// Clear unit tests the expected behavior of calling Clear to forget all cert 176// decision state on the SSLHostStateDelegate class. 177IN_PROC_BROWSER_TEST_F(ChromeSSLHostStateDelegateTest, Clear) { 178 scoped_refptr<net::X509Certificate> google_cert = GetGoogleCert(); 179 content::WebContents* tab = 180 browser()->tab_strip_model()->GetActiveWebContents(); 181 Profile* profile = Profile::FromBrowserContext(tab->GetBrowserContext()); 182 content::SSLHostStateDelegate* state = profile->GetSSLHostStateDelegate(); 183 184 // Simulate a user decision to allow an invalid certificate exception for 185 // kWWWGoogleHost and for kExampleHost. 186 state->AllowCert( 187 kWWWGoogleHost, google_cert.get(), net::CERT_STATUS_DATE_INVALID); 188 189 // Do a full clear, then make sure that both kWWWGoogleHost, which had a 190 // decision made, and kExampleHost, which was untouched, are now in a 191 // non-decision state. 192 state->Clear(); 193 EXPECT_FALSE(state->HasAllowedOrDeniedCert(kWWWGoogleHost)); 194 EXPECT_EQ( 195 net::CertPolicy::UNKNOWN, 196 state->QueryPolicy( 197 kWWWGoogleHost, google_cert.get(), net::CERT_STATUS_DATE_INVALID)); 198 EXPECT_FALSE(state->HasAllowedOrDeniedCert(kExampleHost)); 199 EXPECT_EQ( 200 net::CertPolicy::UNKNOWN, 201 state->QueryPolicy( 202 kExampleHost, google_cert.get(), net::CERT_STATUS_DATE_INVALID)); 203} 204 205// Tests the basic behavior of cert memory in incognito. 206class IncognitoSSLHostStateDelegateTest 207 : public ChromeSSLHostStateDelegateTest { 208 protected: 209 virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE { 210 ChromeSSLHostStateDelegateTest::SetUpCommandLine(command_line); 211 command_line->AppendSwitchASCII(switches::kRememberCertErrorDecisions, 212 kDeltaSecondsString); 213 } 214}; 215 216IN_PROC_BROWSER_TEST_F(IncognitoSSLHostStateDelegateTest, PRE_AfterRestart) { 217 scoped_refptr<net::X509Certificate> google_cert = GetGoogleCert(); 218 content::WebContents* tab = 219 browser()->tab_strip_model()->GetActiveWebContents(); 220 Profile* profile = Profile::FromBrowserContext(tab->GetBrowserContext()); 221 content::SSLHostStateDelegate* state = profile->GetSSLHostStateDelegate(); 222 223 // Add a cert exception to the profile and then verify that it still exists 224 // in the incognito profile. 225 state->AllowCert( 226 kWWWGoogleHost, google_cert.get(), net::CERT_STATUS_DATE_INVALID); 227 228 scoped_ptr<Profile> incognito(profile->CreateOffTheRecordProfile()); 229 content::SSLHostStateDelegate* incognito_state = 230 incognito->GetSSLHostStateDelegate(); 231 232 EXPECT_EQ( 233 net::CertPolicy::ALLOWED, 234 incognito_state->QueryPolicy( 235 kWWWGoogleHost, google_cert.get(), net::CERT_STATUS_DATE_INVALID)); 236 237 // Add a cert exception to the incognito profile. It will be checked after 238 // restart that this exception does not exist. Note the different cert URL and 239 // error than above thus mapping to a second exception. Also validate that it 240 // was not added as an exception to the regular profile. 241 incognito_state->AllowCert( 242 kGoogleHost, google_cert.get(), net::CERT_STATUS_COMMON_NAME_INVALID); 243 244 EXPECT_EQ(net::CertPolicy::UNKNOWN, 245 state->QueryPolicy(kGoogleHost, 246 google_cert.get(), 247 net::CERT_STATUS_COMMON_NAME_INVALID)); 248} 249 250// AfterRestart ensures that any cert decisions made in an incognito profile are 251// forgetten after a session restart even if given a command line flag to 252// remember cert decisions after restart. 253IN_PROC_BROWSER_TEST_F(IncognitoSSLHostStateDelegateTest, AfterRestart) { 254 scoped_refptr<net::X509Certificate> google_cert = GetGoogleCert(); 255 content::WebContents* tab = 256 browser()->tab_strip_model()->GetActiveWebContents(); 257 Profile* profile = Profile::FromBrowserContext(tab->GetBrowserContext()); 258 content::SSLHostStateDelegate* state = profile->GetSSLHostStateDelegate(); 259 260 // Verify that the exception added before restart to the regular 261 // (non-incognito) profile still exists and was not cleared after the 262 // incognito session ended. 263 EXPECT_EQ( 264 net::CertPolicy::ALLOWED, 265 state->QueryPolicy( 266 kWWWGoogleHost, google_cert.get(), net::CERT_STATUS_DATE_INVALID)); 267 268 scoped_ptr<Profile> incognito(profile->CreateOffTheRecordProfile()); 269 content::SSLHostStateDelegate* incognito_state = 270 incognito->GetSSLHostStateDelegate(); 271 272 // Verify that the exception added before restart to the incognito profile was 273 // cleared when the incognito session ended. 274 EXPECT_EQ(net::CertPolicy::UNKNOWN, 275 incognito_state->QueryPolicy(kGoogleHost, 276 google_cert.get(), 277 net::CERT_STATUS_COMMON_NAME_INVALID)); 278} 279 280// Tests to make sure that if the remember value is set to -1, any decisions 281// won't be remembered over a restart. 282class ForGetSSLHostStateDelegateTest : public ChromeSSLHostStateDelegateTest { 283 protected: 284 virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE { 285 ChromeSSLHostStateDelegateTest::SetUpCommandLine(command_line); 286 command_line->AppendSwitchASCII(switches::kRememberCertErrorDecisions, 287 kForgetAtSessionEnd); 288 } 289}; 290 291IN_PROC_BROWSER_TEST_F(ForGetSSLHostStateDelegateTest, PRE_AfterRestart) { 292 scoped_refptr<net::X509Certificate> google_cert = GetGoogleCert(); 293 content::WebContents* tab = 294 browser()->tab_strip_model()->GetActiveWebContents(); 295 Profile* profile = Profile::FromBrowserContext(tab->GetBrowserContext()); 296 content::SSLHostStateDelegate* state = profile->GetSSLHostStateDelegate(); 297 298 state->AllowCert( 299 kWWWGoogleHost, google_cert.get(), net::CERT_STATUS_DATE_INVALID); 300 EXPECT_EQ( 301 net::CertPolicy::ALLOWED, 302 state->QueryPolicy( 303 kWWWGoogleHost, google_cert.get(), net::CERT_STATUS_DATE_INVALID)); 304} 305 306IN_PROC_BROWSER_TEST_F(ForGetSSLHostStateDelegateTest, AfterRestart) { 307 scoped_refptr<net::X509Certificate> google_cert = GetGoogleCert(); 308 content::WebContents* tab = 309 browser()->tab_strip_model()->GetActiveWebContents(); 310 Profile* profile = Profile::FromBrowserContext(tab->GetBrowserContext()); 311 content::SSLHostStateDelegate* state = profile->GetSSLHostStateDelegate(); 312 313 // The cert should now be |UNKONWN| because the profile is set to forget cert 314 // exceptions after session end. 315 EXPECT_EQ( 316 net::CertPolicy::UNKNOWN, 317 state->QueryPolicy( 318 kWWWGoogleHost, google_cert.get(), net::CERT_STATUS_DATE_INVALID)); 319} 320 321// Tests to make sure that if the remember value is set to 0, any decisions made 322// will be forgetten immediately. 323class ForgetInstantlySSLHostStateDelegateTest 324 : public ChromeSSLHostStateDelegateTest { 325 protected: 326 virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE { 327 ChromeSSLHostStateDelegateTest::SetUpCommandLine(command_line); 328 command_line->AppendSwitchASCII(switches::kRememberCertErrorDecisions, 329 kForgetInstantly); 330 } 331}; 332 333IN_PROC_BROWSER_TEST_F(ForgetInstantlySSLHostStateDelegateTest, 334 MakeAndForgetException) { 335 scoped_refptr<net::X509Certificate> google_cert = GetGoogleCert(); 336 content::WebContents* tab = 337 browser()->tab_strip_model()->GetActiveWebContents(); 338 Profile* profile = Profile::FromBrowserContext(tab->GetBrowserContext()); 339 content::SSLHostStateDelegate* state = profile->GetSSLHostStateDelegate(); 340 341 // chrome_state takes ownership of this clock 342 base::SimpleTestClock* clock = new base::SimpleTestClock(); 343 ChromeSSLHostStateDelegate* chrome_state = 344 static_cast<ChromeSSLHostStateDelegate*>(state); 345 chrome_state->SetClock(scoped_ptr<base::Clock>(clock)); 346 347 // Start the clock at standard system time but do not advance at all to 348 // emphasize that instant forget works. 349 clock->SetNow(base::Time::NowFromSystemTime()); 350 351 state->AllowCert( 352 kWWWGoogleHost, google_cert.get(), net::CERT_STATUS_DATE_INVALID); 353 EXPECT_EQ( 354 net::CertPolicy::UNKNOWN, 355 state->QueryPolicy( 356 kWWWGoogleHost, google_cert.get(), net::CERT_STATUS_DATE_INVALID)); 357} 358 359// Tests to make sure that if the remember value is set to a non-zero value0, 360// any decisions will be remembered over a restart, but only for the length 361// specified. 362class RememberSSLHostStateDelegateTest : public ChromeSSLHostStateDelegateTest { 363 protected: 364 virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE { 365 ChromeSSLHostStateDelegateTest::SetUpCommandLine(command_line); 366 command_line->AppendSwitchASCII(switches::kRememberCertErrorDecisions, 367 kDeltaSecondsString); 368 } 369}; 370 371IN_PROC_BROWSER_TEST_F(RememberSSLHostStateDelegateTest, PRE_AfterRestart) { 372 scoped_refptr<net::X509Certificate> google_cert = GetGoogleCert(); 373 content::WebContents* tab = 374 browser()->tab_strip_model()->GetActiveWebContents(); 375 Profile* profile = Profile::FromBrowserContext(tab->GetBrowserContext()); 376 content::SSLHostStateDelegate* state = profile->GetSSLHostStateDelegate(); 377 378 state->AllowCert( 379 kWWWGoogleHost, google_cert.get(), net::CERT_STATUS_DATE_INVALID); 380 EXPECT_EQ( 381 net::CertPolicy::ALLOWED, 382 state->QueryPolicy( 383 kWWWGoogleHost, google_cert.get(), net::CERT_STATUS_DATE_INVALID)); 384} 385 386IN_PROC_BROWSER_TEST_F(RememberSSLHostStateDelegateTest, AfterRestart) { 387 scoped_refptr<net::X509Certificate> google_cert = GetGoogleCert(); 388 content::WebContents* tab = 389 browser()->tab_strip_model()->GetActiveWebContents(); 390 Profile* profile = Profile::FromBrowserContext(tab->GetBrowserContext()); 391 content::SSLHostStateDelegate* state = profile->GetSSLHostStateDelegate(); 392 393 // chrome_state takes ownership of this clock 394 base::SimpleTestClock* clock = new base::SimpleTestClock(); 395 ChromeSSLHostStateDelegate* chrome_state = 396 static_cast<ChromeSSLHostStateDelegate*>(state); 397 chrome_state->SetClock(scoped_ptr<base::Clock>(clock)); 398 399 // Start the clock at standard system time. 400 clock->SetNow(base::Time::NowFromSystemTime()); 401 402 // This should only pass if the cert was allowed before the test was restart 403 // and thus has now been rememebered across browser restarts. 404 EXPECT_EQ( 405 net::CertPolicy::ALLOWED, 406 state->QueryPolicy( 407 kWWWGoogleHost, google_cert.get(), net::CERT_STATUS_DATE_INVALID)); 408 409 // Simulate the clock advancing by the specified delta. 410 clock->Advance(base::TimeDelta::FromSeconds(kDeltaOneDayInSeconds + 1)); 411 412 // The cert should now be |UNKONWN| because the specified delta has passed. 413 EXPECT_EQ( 414 net::CertPolicy::UNKNOWN, 415 state->QueryPolicy( 416 kWWWGoogleHost, google_cert.get(), net::CERT_STATUS_DATE_INVALID)); 417} 418 419// Tests to make sure that if the user deletes their browser history, SSL 420// exceptions will be deleted as well. 421class RemoveBrowsingHistorySSLHostStateDelegateTest 422 : public ChromeSSLHostStateDelegateTest { 423 public: 424 void RemoveAndWait(Profile* profile) { 425 BrowsingDataRemover* remover = BrowsingDataRemover::CreateForPeriod( 426 profile, BrowsingDataRemover::LAST_HOUR); 427 BrowsingDataRemoverCompletionObserver completion_observer(remover); 428 remover->Remove(BrowsingDataRemover::REMOVE_HISTORY, 429 BrowsingDataHelper::UNPROTECTED_WEB); 430 completion_observer.BlockUntilCompletion(); 431 } 432}; 433 434IN_PROC_BROWSER_TEST_F(RemoveBrowsingHistorySSLHostStateDelegateTest, 435 DeleteHistory) { 436 scoped_refptr<net::X509Certificate> google_cert = GetGoogleCert(); 437 content::WebContents* tab = 438 browser()->tab_strip_model()->GetActiveWebContents(); 439 Profile* profile = Profile::FromBrowserContext(tab->GetBrowserContext()); 440 content::SSLHostStateDelegate* state = profile->GetSSLHostStateDelegate(); 441 442 // Add an exception for an invalid certificate. Then remove the last hour's 443 // worth of browsing history and verify that the exception has been deleted. 444 state->AllowCert( 445 kGoogleHost, google_cert.get(), net::CERT_STATUS_DATE_INVALID); 446 RemoveAndWait(profile); 447 EXPECT_EQ(net::CertPolicy::UNKNOWN, 448 state->QueryPolicy( 449 kGoogleHost, google_cert.get(), net::CERT_STATUS_DATE_INVALID)); 450} 451