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"
22 // This class takes ownership of block and releases it on UI thread, even if
23 // |BlockHolder| is destructed on a background thread.
25 class BlockHolder : public base::RefCountedThreadSafe<BlockHolder<T>> {
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) {
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);
46 // Releases underlying |block_| on UI thread.
48 if (web::WebThread::CurrentlyOn(web::WebThread::UI)) {
51 web::WebThread::PostTask(web::WebThread::UI, FROM_HERE,
52 base::Bind(&BlockHolder::ReleaseBlock, block_));
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;
85 @implementation CRWCertVerificationController
87 #pragma mark - Superclass
90 DCHECK(!_certVerifier);
96 - (instancetype)init {
101 - (instancetype)initWithBrowserState:(web::BrowserState*)browserState {
102 DCHECK(browserState);
103 DCHECK_CURRENTLY_ON_WEB_THREAD(web::WebThread::UI);
106 _contextGetter = browserState->GetRequestContext();
107 DCHECK(_contextGetter);
108 [self createCertVerifier];
113 - (void)decidePolicyForCert:(const scoped_refptr<net::X509Certificate>&)cert
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
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;
138 dispatch_async(dispatch_get_main_queue(), ^{
139 handlerHolder->call(policy, result.cert_status);
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();
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();
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()));
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
186 if (!_certVerifier) {
187 completionHandler(net::CertVerifyResult(), net::ERR_FAILED);
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);