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