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