Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / ios / web / net / crw_cert_verification_controller.mm
bloba0180d0f978a7f525bcb3ad8fff64a55669586e0
1 // Copyright 2015 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.
5 #import "ios/web/net/crw_cert_verification_controller.h"
7 #include "base/logging.h"
8 #include "base/mac/bind_objc_block.h"
9 #import "base/memory/ref_counted.h"
10 #import "base/memory/scoped_ptr.h"
11 #include "base/strings/sys_string_conversions.h"
12 #include "ios/web/net/cert_verifier_block_adapter.h"
13 #include "ios/web/public/browser_state.h"
14 #include "ios/web/public/web_thread.h"
15 #include "net/cert/cert_verify_result.h"
16 #include "net/ssl/ssl_config_service.h"
17 #include "net/url_request/url_request_context.h"
18 #include "net/url_request/url_request_context_getter.h"
20 namespace {
22 // This class takes ownership of block and releases it on UI thread, even if
23 // |BlockHolder| is destructed on a background thread.
24 template <class T>
25 class BlockHolder : public base::RefCountedThreadSafe<BlockHolder<T>> {
26  public:
27   // Takes ownership of |block|, which must not be null.
28   explicit BlockHolder(T block) : block_([block copy]) { DCHECK(block_); }
30   // Calls underlying block with the given variadic arguments.
31   template <typename... Arguments>
32   void call(Arguments... Args) {
33     block_(Args...);
34   }
36  private:
37   BlockHolder() = delete;
38   friend class base::RefCountedThreadSafe<BlockHolder>;
40   // Releases the given block, must be called on UI thread.
41   static void ReleaseBlock(id block) {
42     DCHECK_CURRENTLY_ON_WEB_THREAD(web::WebThread::UI);
43     [block release];
44   }
46   // Releases underlying |block_| on UI thread.
47   ~BlockHolder() {
48     if (web::WebThread::CurrentlyOn(web::WebThread::UI)) {
49       ReleaseBlock(block_);
50     } else {
51       web::WebThread::PostTask(web::WebThread::UI, FROM_HERE,
52                                base::Bind(&BlockHolder::ReleaseBlock, block_));
53     }
54   }
56   T block_;
59 }  // namespace
61 @interface CRWCertVerificationController () {
62   // Cert verification object which wraps |net::CertVerifier|. Must be created,
63   // used and destroyed on IO Thread.
64   scoped_ptr<web::CertVerifierBlockAdapter> _certVerifier;
66   // URLRequestContextGetter for obtaining net layer objects.
67   net::URLRequestContextGetter* _contextGetter;
70 // Cert verification flags. Must be used on IO Thread.
71 @property(nonatomic, readonly) int certVerifyFlags;
73 // Creates _certVerifier object on IO thread.
74 - (void)createCertVerifier;
76 // Verifies the given |cert| for the given |host| and calls |completionHandler|
77 // on completion. |completionHandler| cannot be null and will be called
78 // synchronously or asynchronously on IO thread.
79 - (void)verifyCert:(const scoped_refptr<net::X509Certificate>&)cert
80               forHost:(NSString*)host
81     completionHandler:(void (^)(net::CertVerifyResult, int))completionHandler;
83 @end
85 @implementation CRWCertVerificationController
87 #pragma mark - Superclass
89 - (void)dealloc {
90   DCHECK(!_certVerifier);
91   [super dealloc];
94 #pragma mark - Public
96 - (instancetype)init {
97   NOTREACHED();
98   return nil;
101 - (instancetype)initWithBrowserState:(web::BrowserState*)browserState {
102   DCHECK(browserState);
103   DCHECK_CURRENTLY_ON_WEB_THREAD(web::WebThread::UI);
104   self = [super init];
105   if (self) {
106     _contextGetter = browserState->GetRequestContext();
107     DCHECK(_contextGetter);
108     [self createCertVerifier];
109   }
110   return self;
113 - (void)decidePolicyForCert:(const scoped_refptr<net::X509Certificate>&)cert
114                        host:(NSString*)host
115           completionHandler:(web::PolicyDecisionHandler)handler {
116   DCHECK_CURRENTLY_ON_WEB_THREAD(web::WebThread::UI);
117   // completionHandler of |verifyCert:forHost:completionHandler:| is called on
118   // IO thread and then bounces back to UI thread. As a result all objects
119   // captured by completionHandler may be released on either UI or IO thread.
120   // Since |handler| can potentially capture multiple thread unsafe objects
121   // (like Web Controller) |handler| itself should never be released on
122   // background thread and |BlockHolder| ensures that.
123   __block scoped_refptr<BlockHolder<web::PolicyDecisionHandler>> handlerHolder(
124       new BlockHolder<web::PolicyDecisionHandler>(handler));
125   [self verifyCert:cert
126                 forHost:host
127       completionHandler:^(net::CertVerifyResult result, int error) {
128         web::CertAcceptPolicy policy =
129             web::CERT_ACCEPT_POLICY_NON_RECOVERABLE_ERROR;
130         if (error == net::OK) {
131           policy = web::CERT_ACCEPT_POLICY_ALLOW;
132         } else if (net::IsCertStatusError(result.cert_status)) {
133           policy = net::IsCertStatusMinorError(result.cert_status)
134                        ? web::CERT_ACCEPT_POLICY_ALLOW
135                        : web::CERT_ACCEPT_POLICY_RECOVERABLE_ERROR;
136         }
138         dispatch_async(dispatch_get_main_queue(), ^{
139           handlerHolder->call(policy, result.cert_status);
140         });
141       }];
144 - (void)shutDown {
145   DCHECK_CURRENTLY_ON_WEB_THREAD(web::WebThread::UI);
146   web::WebThread::PostTask(web::WebThread::IO, FROM_HERE, base::BindBlock(^{
147     // This block captures |self| delaying its deallocation and causing dealloc
148     // to happen on either IO or UI thread (which is fine for this class).
149     _certVerifier.reset();
150   }));
153 #pragma mark - Private
155 - (int)certVerifyFlags {
156   DCHECK(web::WebThread::CurrentlyOn(web::WebThread::IO));
157   DCHECK(_contextGetter);
158   // |net::URLRequestContextGetter| lifetime is expected to be at least the same
159   // or longer than |BrowserState| lifetime.
160   net::URLRequestContext* context = _contextGetter->GetURLRequestContext();
161   DCHECK(context);
162   net::SSLConfigService* SSLConfigService = context->ssl_config_service();
163   DCHECK(SSLConfigService);
164   net::SSLConfig config;
165   SSLConfigService->GetSSLConfig(&config);
166   return config.GetCertVerifyFlags();
169 - (void)createCertVerifier {
170   web::WebThread::PostTask(web::WebThread::IO, FROM_HERE, base::BindBlock(^{
171     net::URLRequestContext* context = _contextGetter->GetURLRequestContext();
172     _certVerifier.reset(new web::CertVerifierBlockAdapter(
173         context->cert_verifier(), context->net_log()));
174   }));
177 - (void)verifyCert:(const scoped_refptr<net::X509Certificate>&)cert
178               forHost:(NSString*)host
179     completionHandler:(void (^)(net::CertVerifyResult, int))completionHandler {
180   DCHECK(completionHandler);
181   __block scoped_refptr<net::X509Certificate> blockCert = cert;
182   web::WebThread::PostTask(
183       web::WebThread::IO, FROM_HERE, base::BindBlock(^{
184         // WeakNSObject does not work across different threads, hence this block
185         // retains self.
186         if (!_certVerifier) {
187           completionHandler(net::CertVerifyResult(), net::ERR_FAILED);
188           return;
189         }
191         web::CertVerifierBlockAdapter::Params params(
192             blockCert.Pass(), base::SysNSStringToUTF8(host));
193         params.flags = self.certVerifyFlags;
194         params.crl_set = net::SSLConfigService::GetCRLSet();
195         // OCSP response is not provided by iOS API.
196         _certVerifier->Verify(params, completionHandler);
197       }));
200 @end