1// Copyright 2017 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#import "ios/web/session/session_certificate_policy_cache_impl.h"
6
7#include "base/bind.h"
8#include "base/task/post_task.h"
9#include "ios/web/public/browser_state.h"
10#include "ios/web/public/security/certificate_policy_cache.h"
11#import "ios/web/public/session/crw_session_certificate_policy_cache_storage.h"
12#include "ios/web/public/thread/web_task_traits.h"
13#include "ios/web/public/thread/web_thread.h"
14#include "net/cert/x509_util.h"
15#include "net/cert/x509_util_ios.h"
16
17#if !defined(__has_feature) || !__has_feature(objc_arc)
18#error "This file requires ARC support."
19#endif
20
21// Break if CertStatus values changed, as they are persisted on disk and thus
22// must be consistent.
23static_assert(net::CERT_STATUS_ALL_ERRORS == 0xFF00FFFF,
24              "The value of CERT_STATUS_ALL_ERRORS changed!");
25static_assert(net::CERT_STATUS_COMMON_NAME_INVALID == 1 << 0,
26              "The value of CERT_STATUS_COMMON_NAME_INVALID changed!");
27static_assert(net::CERT_STATUS_DATE_INVALID == 1 << 1,
28              "The value of CERT_STATUS_DATE_INVALID changed!");
29static_assert(net::CERT_STATUS_AUTHORITY_INVALID == 1 << 2,
30              "The value of CERT_STATUS_AUTHORITY_INVALID changed!");
31static_assert(net::CERT_STATUS_NO_REVOCATION_MECHANISM == 1 << 4,
32              "The value of CERT_STATUS_NO_REVOCATION_MECHANISM changed!");
33static_assert(net::CERT_STATUS_UNABLE_TO_CHECK_REVOCATION == 1 << 5,
34              "The value of CERT_STATUS_UNABLE_TO_CHECK_REVOCATION changed!");
35static_assert(net::CERT_STATUS_REVOKED == 1 << 6,
36              "The value of CERT_STATUS_REVOKED changed!");
37static_assert(net::CERT_STATUS_INVALID == 1 << 7,
38              "The value of CERT_STATUS_INVALID changed!");
39static_assert(net::CERT_STATUS_WEAK_SIGNATURE_ALGORITHM == 1 << 8,
40              "The value of CERT_STATUS_WEAK_SIGNATURE_ALGORITHM changed!");
41static_assert(net::CERT_STATUS_NON_UNIQUE_NAME == 1 << 10,
42              "The value of CERT_STATUS_NON_UNIQUE_NAME changed!");
43static_assert(net::CERT_STATUS_WEAK_KEY == 1 << 11,
44              "The value of CERT_STATUS_WEAK_KEY changed!");
45static_assert(net::CERT_STATUS_IS_EV == 1 << 16,
46              "The value of CERT_STATUS_IS_EV changed!");
47static_assert(net::CERT_STATUS_REV_CHECKING_ENABLED == 1 << 17,
48              "The value of CERT_STATUS_REV_CHECKING_ENABLED changed!");
49
50namespace web {
51
52SessionCertificatePolicyCacheImpl::SessionCertificatePolicyCacheImpl(
53    BrowserState* browser_state)
54    : SessionCertificatePolicyCache(browser_state),
55      allowed_certs_([[NSMutableSet alloc] init]) {}
56
57SessionCertificatePolicyCacheImpl::~SessionCertificatePolicyCacheImpl() {}
58
59void SessionCertificatePolicyCacheImpl::UpdateCertificatePolicyCache(
60    const scoped_refptr<web::CertificatePolicyCache>& cache) const {
61  DCHECK_CURRENTLY_ON(WebThread::UI);
62  DCHECK(cache.get());
63  NSSet* allowed_certs = [NSSet setWithSet:allowed_certs_];
64  const scoped_refptr<CertificatePolicyCache> cache_copy = cache;
65  base::PostTask(FROM_HERE, {WebThread::IO}, base::BindOnce(^{
66                   for (CRWSessionCertificateStorage* cert in allowed_certs) {
67                     cache_copy->AllowCertForHost(cert.certificate, cert.host,
68                                                  cert.status);
69                   }
70                 }));
71}
72
73void SessionCertificatePolicyCacheImpl::RegisterAllowedCertificate(
74    scoped_refptr<net::X509Certificate> certificate,
75    const std::string& host,
76    net::CertStatus status) {
77  DCHECK_CURRENTLY_ON(WebThread::UI);
78  // Store user decisions with the leaf cert, ignoring any intermediates.
79  // This is because WKWebView returns the verified certificate chain in
80  // |webView:didReceiveAuthenticationChallenge:completionHandler:|,
81  // but the server-supplied chain in
82  // |webView:didFailProvisionalNavigation:withError:|.
83  if (!certificate->intermediate_buffers().empty()) {
84    certificate = net::X509Certificate::CreateFromBuffer(
85        bssl::UpRef(certificate->cert_buffer()), {});
86    DCHECK(certificate);
87  }
88  DCHECK(certificate->intermediate_buffers().empty());
89
90  [allowed_certs_ addObject:[[CRWSessionCertificateStorage alloc]
91                                initWithCertificate:certificate
92                                               host:host
93                                             status:status]];
94  const scoped_refptr<CertificatePolicyCache> cache =
95      GetCertificatePolicyCache();
96  base::PostTask(
97      FROM_HERE, {WebThread::IO},
98      base::BindOnce(&CertificatePolicyCache::AllowCertForHost, cache,
99                     base::RetainedRef(certificate.get()), host, status));
100}
101
102void SessionCertificatePolicyCacheImpl::SetAllowedCerts(NSSet* allowed_certs) {
103  allowed_certs_ = [allowed_certs mutableCopy];
104}
105
106NSSet* SessionCertificatePolicyCacheImpl::GetAllowedCerts() const {
107  return allowed_certs_;
108}
109
110}  // namespace web
111