OLD | NEW |
---|---|
1 // Copyright 2015 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #import "ios/web/net/crw_cert_verification_controller.h" | 5 #import "ios/web/net/crw_cert_verification_controller.h" |
6 | 6 |
7 #include "base/logging.h" | 7 #include "base/logging.h" |
8 #include "base/mac/bind_objc_block.h" | 8 #include "base/mac/bind_objc_block.h" |
9 #import "base/memory/ref_counted.h" | 9 #import "base/memory/ref_counted.h" |
10 #import "base/memory/scoped_ptr.h" | 10 #import "base/memory/scoped_ptr.h" |
11 #include "base/strings/sys_string_conversions.h" | 11 #include "base/strings/sys_string_conversions.h" |
12 #include "base/threading/worker_pool.h" | 12 #include "base/threading/worker_pool.h" |
13 #include "ios/web/net/cert_verifier_block_adapter.h" | 13 #include "ios/web/net/cert_verifier_block_adapter.h" |
14 #include "ios/web/public/browser_state.h" | 14 #include "ios/web/public/browser_state.h" |
15 #include "ios/web/public/certificate_policy_cache.h" | |
15 #include "ios/web/public/web_thread.h" | 16 #include "ios/web/public/web_thread.h" |
16 #import "ios/web/web_state/wk_web_view_security_util.h" | 17 #import "ios/web/web_state/wk_web_view_security_util.h" |
17 #include "net/cert/cert_verify_result.h" | 18 #include "net/cert/cert_verify_result.h" |
18 #include "net/ssl/ssl_config_service.h" | 19 #include "net/ssl/ssl_config_service.h" |
19 #include "net/url_request/url_request_context.h" | 20 #include "net/url_request/url_request_context.h" |
20 #include "net/url_request/url_request_context_getter.h" | 21 #include "net/url_request/url_request_context_getter.h" |
21 | 22 |
22 namespace { | 23 namespace { |
23 | 24 |
24 // This class takes ownership of block and releases it on UI thread, even if | 25 // This class takes ownership of block and releases it on UI thread, even if |
(...skipping 28 matching lines...) Expand all Loading... | |
53 web::WebThread::PostTask(web::WebThread::UI, FROM_HERE, | 54 web::WebThread::PostTask(web::WebThread::UI, FROM_HERE, |
54 base::Bind(&BlockHolder::ReleaseBlock, block_)); | 55 base::Bind(&BlockHolder::ReleaseBlock, block_)); |
55 } | 56 } |
56 } | 57 } |
57 | 58 |
58 T block_; | 59 T block_; |
59 }; | 60 }; |
60 | 61 |
61 } // namespace | 62 } // namespace |
62 | 63 |
64 using web::CERT_ACCEPT_POLICY_RECOVERABLE_ERROR_ACCEPTED_BY_USER; | |
65 using web::CERT_ACCEPT_POLICY_RECOVERABLE_ERROR_NOT_ACCEPTED_BY_USER; | |
66 | |
63 @interface CRWCertVerificationController () { | 67 @interface CRWCertVerificationController () { |
64 // Cert verification object which wraps |net::CertVerifier|. Must be created, | 68 // Cert verification object which wraps |net::CertVerifier|. Must be created, |
65 // used and destroyed on IO Thread. | 69 // used and destroyed on IO Thread. |
66 scoped_ptr<web::CertVerifierBlockAdapter> _certVerifier; | 70 scoped_ptr<web::CertVerifierBlockAdapter> _certVerifier; |
67 | 71 |
68 // URLRequestContextGetter for obtaining net layer objects. | 72 // URLRequestContextGetter for obtaining net layer objects. |
69 net::URLRequestContextGetter* _contextGetter; | 73 net::URLRequestContextGetter* _contextGetter; |
74 | |
75 // Used to remember decisions about how to handle problematic certs. | |
76 scoped_refptr<web::CertificatePolicyCache> _certPolicyCache; | |
70 } | 77 } |
71 | 78 |
72 // Cert verification flags. Must be used on IO Thread. | 79 // Cert verification flags. Must be used on IO Thread. |
73 @property(nonatomic, readonly) int certVerifyFlags; | 80 @property(nonatomic, readonly) int certVerifyFlags; |
74 | 81 |
75 // Creates _certVerifier object on IO thread. | 82 // Creates _certVerifier object on IO thread. |
76 - (void)createCertVerifier; | 83 - (void)createCertVerifier; |
77 | 84 |
78 // Verifies the given |cert| for the given |host| using |net::CertVerifier| and | 85 // Verifies the given |cert| for the given |host| using |net::CertVerifier| and |
79 // calls |completionHandler| on completion. |completionHandler| cannot be null | 86 // calls |completionHandler| on completion. |completionHandler| cannot be null |
80 // and will be called asynchronously on IO thread. | 87 // and will be called asynchronously on IO thread or synchronously on current |
88 // thread if IO task can't start (in this case |success| argument will be NO). | |
81 - (void)verifyCert:(const scoped_refptr<net::X509Certificate>&)cert | 89 - (void)verifyCert:(const scoped_refptr<net::X509Certificate>&)cert |
82 forHost:(NSString*)host | 90 forHost:(NSString*)host |
83 completionHandler:(void (^)(net::CertVerifyResult, int))completionHandler; | 91 completionHandler:(void (^)(net::CertVerifyResult, BOOL success))handler; |
84 | 92 |
85 // Verifies the given |trust| using SecTrustRef API. |completionHandler| cannot | 93 // Verifies the given |trust| using SecTrustRef API. |completionHandler| cannot |
86 // be null and will be either called asynchronously on Worker thread or | 94 // be null and will be either called asynchronously on Worker thread or |
87 // synchronously on current thread if the worker task can't start (in this | 95 // synchronously on current thread if the worker task can't start (in this |
88 // case |success| argument will be NO). | 96 // case |success| argument will be NO). |
89 - (void)verifyTrust:(base::ScopedCFTypeRef<SecTrustRef>)trust | 97 - (void)verifyTrust:(base::ScopedCFTypeRef<SecTrustRef>)trust |
90 completionHandler:(void (^)(SecTrustResultType, BOOL success))handler; | 98 completionHandler:(void (^)(SecTrustResultType, BOOL success))handler; |
91 | 99 |
92 @end | 100 @end |
93 | 101 |
(...skipping 13 matching lines...) Expand all Loading... | |
107 return nil; | 115 return nil; |
108 } | 116 } |
109 | 117 |
110 - (instancetype)initWithBrowserState:(web::BrowserState*)browserState { | 118 - (instancetype)initWithBrowserState:(web::BrowserState*)browserState { |
111 DCHECK(browserState); | 119 DCHECK(browserState); |
112 DCHECK_CURRENTLY_ON_WEB_THREAD(web::WebThread::UI); | 120 DCHECK_CURRENTLY_ON_WEB_THREAD(web::WebThread::UI); |
113 self = [super init]; | 121 self = [super init]; |
114 if (self) { | 122 if (self) { |
115 _contextGetter = browserState->GetRequestContext(); | 123 _contextGetter = browserState->GetRequestContext(); |
116 DCHECK(_contextGetter); | 124 DCHECK(_contextGetter); |
125 _certPolicyCache = | |
126 web::BrowserState::GetCertificatePolicyCache(browserState); | |
127 | |
117 [self createCertVerifier]; | 128 [self createCertVerifier]; |
118 } | 129 } |
119 return self; | 130 return self; |
120 } | 131 } |
121 | 132 |
122 - (void)decidePolicyForCert:(const scoped_refptr<net::X509Certificate>&)cert | 133 - (void)decideLoadPolicyForTrust:(base::ScopedCFTypeRef<SecTrustRef>)trust |
123 host:(NSString*)host | 134 host:(NSString*)host |
124 completionHandler:(web::PolicyDecisionHandler)completionHandler { | 135 completionHandler:(web::PolicyDecisionHandler)completionHandler { |
125 DCHECK_CURRENTLY_ON_WEB_THREAD(web::WebThread::UI); | 136 DCHECK_CURRENTLY_ON_WEB_THREAD(web::WebThread::UI); |
126 // completionHandler of |verifyCert:forHost:completionHandler:| is called on | 137 // completionHandler of |verifyCert:forHost:completionHandler:| is called on |
127 // IO thread and then bounces back to UI thread. As a result all objects | 138 // IO thread and then bounces back to UI thread. As a result all objects |
128 // captured by completionHandler may be released on either UI or IO thread. | 139 // captured by completionHandler may be released on either UI or IO thread. |
129 // Since |completionHandler| can potentially capture multiple thread unsafe | 140 // Since |completionHandler| can potentially capture multiple thread unsafe |
130 // objects (like Web Controller) |completionHandler| itself should never be | 141 // objects (like Web Controller) |completionHandler| itself should never be |
131 // released on background thread and |BlockHolder| ensures that. | 142 // released on background thread and |BlockHolder| ensures that. |
132 __block scoped_refptr<BlockHolder<web::PolicyDecisionHandler>> handlerHolder( | 143 __block scoped_refptr<BlockHolder<web::PolicyDecisionHandler>> handlerHolder( |
133 new BlockHolder<web::PolicyDecisionHandler>(completionHandler)); | 144 new BlockHolder<web::PolicyDecisionHandler>(completionHandler)); |
134 [self verifyCert:cert | 145 [self verifyTrust:trust |
135 forHost:host | 146 completionHandler:^(SecTrustResultType trustResult, BOOL success) { |
136 completionHandler:^(net::CertVerifyResult result, int error) { | 147 if (!success) { |
137 web::CertAcceptPolicy policy = | 148 dispatch_async(dispatch_get_main_queue(), ^{ |
138 web::CERT_ACCEPT_POLICY_NON_RECOVERABLE_ERROR; | 149 handlerHolder->call(web::CERT_ACCEPT_POLICY_NON_RECOVERABLE_ERROR, |
139 if (error == net::OK) { | 150 net::CertStatus()); |
140 policy = web::CERT_ACCEPT_POLICY_ALLOW; | 151 }); |
141 } else if (net::IsCertStatusError(result.cert_status)) { | 152 return; |
142 policy = net::IsCertStatusMinorError(result.cert_status) | |
143 ? web::CERT_ACCEPT_POLICY_ALLOW | |
144 : web::CERT_ACCEPT_POLICY_RECOVERABLE_ERROR; | |
145 } | 153 } |
146 | 154 |
147 dispatch_async(dispatch_get_main_queue(), ^{ | 155 if (web::GetSecurityStyleFromTrustResult(trustResult) == |
148 handlerHolder->call(policy, result.cert_status); | 156 web::SECURITY_STYLE_AUTHENTICATED) { |
149 }); | 157 // SecTrust API considers this cert as valid. |
158 dispatch_async(dispatch_get_main_queue(), ^{ | |
159 handlerHolder->call(web::CERT_ACCEPT_POLICY_ALLOW, | |
160 net::CertStatus()); | |
161 }); | |
162 return; | |
163 } | |
164 | |
165 // SecTrust API considers this cert as invalid. Check the reason and | |
166 // whether or not user has decided to proceed with this bad cert. | |
167 scoped_refptr<net::X509Certificate> cert( | |
168 web::CreateCertFromTrust(trust)); | |
169 [self verifyCert:cert | |
170 forHost:host | |
171 completionHandler:^(net::CertVerifyResult certVerifierResult, | |
172 BOOL sucess) { | |
stuartmorgan
2015/10/09 21:30:00
Consider pulling everything in here into a helper
Eugene But (OOO till 7-30)
2015/10/12 18:19:40
Done.
| |
173 if (!sucess) { | |
174 dispatch_async(dispatch_get_main_queue(), ^{ | |
175 handlerHolder->call( | |
176 web::CERT_ACCEPT_POLICY_NON_RECOVERABLE_ERROR, | |
177 net::CertStatus()); | |
178 }); | |
179 return; | |
180 } | |
181 | |
182 web::CertAcceptPolicy policy = | |
183 web::CERT_ACCEPT_POLICY_NON_RECOVERABLE_ERROR; | |
184 if (trustResult == kSecTrustResultRecoverableTrustFailure && | |
185 SecTrustGetCertificateCount(trust)) { | |
186 // Check if user has decided to proceed with this bad cert. | |
187 scoped_refptr<net::X509Certificate> leafCert = | |
188 web::CreateCertFromChain(@[ | |
189 static_cast<id>(SecTrustGetCertificateAtIndex(trust, 0)) | |
190 ]); | |
191 | |
192 web::CertPolicy::Judgment judgment = | |
193 _certPolicyCache->QueryPolicy( | |
194 leafCert.get(), base::SysNSStringToUTF8(host), | |
195 certVerifierResult.cert_status); | |
196 | |
197 if (judgment == web::CertPolicy::ALLOWED) { | |
198 policy = | |
199 CERT_ACCEPT_POLICY_RECOVERABLE_ERROR_ACCEPTED_BY_USER; | |
200 } else { | |
201 policy = | |
202 CERT_ACCEPT_POLICY_RECOVERABLE_ERROR_NOT_ACCEPTED_BY_USER; | |
203 } | |
204 } | |
205 | |
206 dispatch_async(dispatch_get_main_queue(), ^{ | |
207 handlerHolder->call(policy, certVerifierResult.cert_status); | |
208 }); | |
209 }]; | |
150 }]; | 210 }]; |
151 } | 211 } |
152 | 212 |
153 - (void)querySSLStatusForTrust:(base::ScopedCFTypeRef<SecTrustRef>)trust | 213 - (void)querySSLStatusForTrust:(base::ScopedCFTypeRef<SecTrustRef>)trust |
154 host:(NSString*)host | 214 host:(NSString*)host |
155 completionHandler:(web::StatusQueryHandler)completionHandler { | 215 completionHandler:(web::StatusQueryHandler)completionHandler { |
156 DCHECK_CURRENTLY_ON_WEB_THREAD(web::WebThread::UI); | 216 DCHECK_CURRENTLY_ON_WEB_THREAD(web::WebThread::UI); |
157 | 217 |
158 // The completion handlers of |verifyCert:forHost:completionHandler:| and | 218 // The completion handlers of |verifyCert:forHost:completionHandler:| and |
159 // |verifyTrust:completionHandler:| will be deallocated on background thread. | 219 // |verifyTrust:completionHandler:| will be deallocated on background thread. |
(...skipping 21 matching lines...) Expand all Loading... | |
181 } | 241 } |
182 | 242 |
183 // Retrieve the net::CertStatus for invalid certificates to determine | 243 // Retrieve the net::CertStatus for invalid certificates to determine |
184 // the rejection reason. | 244 // the rejection reason. |
185 // TODO(eugenebut): Add UMA for CertVerifier and SecTrust verification | 245 // TODO(eugenebut): Add UMA for CertVerifier and SecTrust verification |
186 // mismatch (crbug.com/535699). | 246 // mismatch (crbug.com/535699). |
187 scoped_refptr<net::X509Certificate> cert( | 247 scoped_refptr<net::X509Certificate> cert( |
188 web::CreateCertFromTrust(trust)); | 248 web::CreateCertFromTrust(trust)); |
189 [self verifyCert:cert | 249 [self verifyCert:cert |
190 forHost:host | 250 forHost:host |
191 completionHandler:^(net::CertVerifyResult certVerifierResult, int) { | 251 completionHandler:^(net::CertVerifyResult certVerifierResult, |
252 BOOL) { | |
192 dispatch_async(dispatch_get_main_queue(), ^{ | 253 dispatch_async(dispatch_get_main_queue(), ^{ |
193 handlerHolder->call(securityStyle, | 254 handlerHolder->call(securityStyle, |
194 certVerifierResult.cert_status); | 255 certVerifierResult.cert_status); |
195 }); | 256 }); |
196 }]; | 257 }]; |
197 }]; | 258 }]; |
198 } | 259 } |
199 | 260 |
261 - (void)allowCert:(scoped_refptr<net::X509Certificate>)cert | |
262 forHost:(NSString*)host | |
263 status:(net::CertStatus)status { | |
264 DCHECK_CURRENTLY_ON_WEB_THREAD(web::WebThread::UI); | |
265 // Store user decision only for leaf server cert, and drop any intermediates. | |
266 // This is a workaround for WKWebView behavior, where different delegate | |
267 // methods receive different certificate chains for the same server cert. | |
268 if (cert->GetIntermediateCertificates().size() > 0) { | |
269 cert = net::X509Certificate::CreateFromHandle( | |
270 cert->os_cert_handle(), net::X509Certificate::OSCertHandles()); | |
271 } | |
272 DCHECK_EQ(0U, cert->GetIntermediateCertificates().size()); | |
273 web::WebThread::PostTask(web::WebThread::IO, FROM_HERE, base::BindBlock(^{ | |
274 _certPolicyCache->AllowCertForHost( | |
275 cert.get(), base::SysNSStringToUTF8(host), status); | |
276 })); | |
277 } | |
278 | |
200 - (void)shutDown { | 279 - (void)shutDown { |
201 DCHECK_CURRENTLY_ON_WEB_THREAD(web::WebThread::UI); | 280 DCHECK_CURRENTLY_ON_WEB_THREAD(web::WebThread::UI); |
202 web::WebThread::PostTask(web::WebThread::IO, FROM_HERE, base::BindBlock(^{ | 281 web::WebThread::PostTask(web::WebThread::IO, FROM_HERE, base::BindBlock(^{ |
203 // This block captures |self| delaying its deallocation and causing dealloc | 282 // This block captures |self| delaying its deallocation and causing dealloc |
204 // to happen on either IO or UI thread (which is fine for this class). | 283 // to happen on either IO or UI thread (which is fine for this class). |
205 _certVerifier.reset(); | 284 _certVerifier.reset(); |
206 })); | 285 })); |
207 } | 286 } |
208 | 287 |
209 #pragma mark - Private | 288 #pragma mark - Private |
(...skipping 15 matching lines...) Expand all Loading... | |
225 - (void)createCertVerifier { | 304 - (void)createCertVerifier { |
226 web::WebThread::PostTask(web::WebThread::IO, FROM_HERE, base::BindBlock(^{ | 305 web::WebThread::PostTask(web::WebThread::IO, FROM_HERE, base::BindBlock(^{ |
227 net::URLRequestContext* context = _contextGetter->GetURLRequestContext(); | 306 net::URLRequestContext* context = _contextGetter->GetURLRequestContext(); |
228 _certVerifier.reset(new web::CertVerifierBlockAdapter( | 307 _certVerifier.reset(new web::CertVerifierBlockAdapter( |
229 context->cert_verifier(), context->net_log())); | 308 context->cert_verifier(), context->net_log())); |
230 })); | 309 })); |
231 } | 310 } |
232 | 311 |
233 - (void)verifyCert:(const scoped_refptr<net::X509Certificate>&)cert | 312 - (void)verifyCert:(const scoped_refptr<net::X509Certificate>&)cert |
234 forHost:(NSString*)host | 313 forHost:(NSString*)host |
235 completionHandler:(void (^)(net::CertVerifyResult, int))completionHandler { | 314 completionHandler:(void (^)(net::CertVerifyResult, BOOL))completionHandler { |
236 DCHECK(completionHandler); | 315 DCHECK(completionHandler); |
237 __block scoped_refptr<net::X509Certificate> blockCert = cert; | 316 __block scoped_refptr<net::X509Certificate> blockCert = cert; |
238 web::WebThread::PostTask( | 317 bool started = web::WebThread::PostTask( |
239 web::WebThread::IO, FROM_HERE, base::BindBlock(^{ | 318 web::WebThread::IO, FROM_HERE, base::BindBlock(^{ |
240 // WeakNSObject does not work across different threads, hence this block | 319 // WeakNSObject does not work across different threads, hence this block |
241 // retains self. | 320 // retains self. |
242 if (!_certVerifier) { | 321 if (!_certVerifier) { |
243 completionHandler(net::CertVerifyResult(), net::ERR_FAILED); | 322 completionHandler(net::CertVerifyResult(), net::ERR_FAILED); |
244 return; | 323 return; |
245 } | 324 } |
246 | 325 |
247 web::CertVerifierBlockAdapter::Params params( | 326 web::CertVerifierBlockAdapter::Params params( |
248 blockCert.Pass(), base::SysNSStringToUTF8(host)); | 327 blockCert.Pass(), base::SysNSStringToUTF8(host)); |
249 params.flags = self.certVerifyFlags; | 328 params.flags = self.certVerifyFlags; |
250 params.crl_set = net::SSLConfigService::GetCRLSet(); | 329 params.crl_set = net::SSLConfigService::GetCRLSet(); |
251 // OCSP response is not provided by iOS API. | 330 // OCSP response is not provided by iOS API. |
252 _certVerifier->Verify(params, completionHandler); | 331 _certVerifier->Verify(params, ^(net::CertVerifyResult result, int) { |
332 completionHandler(result, YES); | |
333 }); | |
253 })); | 334 })); |
335 | |
336 if (!started) { | |
337 completionHandler(net::CertVerifyResult(), NO); | |
338 } | |
254 } | 339 } |
255 | 340 |
256 - (void)verifyTrust:(base::ScopedCFTypeRef<SecTrustRef>)trust | 341 - (void)verifyTrust:(base::ScopedCFTypeRef<SecTrustRef>)trust |
257 completionHandler:(void (^)(SecTrustResultType, BOOL))completionHandler { | 342 completionHandler:(void (^)(SecTrustResultType, BOOL))completionHandler { |
258 DCHECK(completionHandler); | 343 DCHECK(completionHandler); |
259 // SecTrustEvaluate performs trust evaluation synchronously, possibly making | 344 // SecTrustEvaluate performs trust evaluation synchronously, possibly making |
260 // network requests. The UI thread should not be blocked by that operation. | 345 // network requests. The UI thread should not be blocked by that operation. |
261 bool result = base::WorkerPool::PostTask(FROM_HERE, base::BindBlock(^{ | 346 bool started = base::WorkerPool::PostTask(FROM_HERE, base::BindBlock(^{ |
262 SecTrustResultType trustResult = kSecTrustResultInvalid; | 347 SecTrustResultType trustResult = kSecTrustResultInvalid; |
263 if (SecTrustEvaluate(trust.get(), &trustResult) != errSecSuccess) { | 348 if (SecTrustEvaluate(trust.get(), &trustResult) != errSecSuccess) { |
264 trustResult = kSecTrustResultInvalid; | 349 trustResult = kSecTrustResultInvalid; |
265 } | 350 } |
266 completionHandler(trustResult, YES); | 351 completionHandler(trustResult, YES); |
267 }), false /* task_is_slow */); | 352 }), false /* task_is_slow */); |
268 | 353 |
269 if (!result) { | 354 if (!started) { |
270 completionHandler(kSecTrustResultInvalid, NO); | 355 completionHandler(kSecTrustResultInvalid, NO); |
271 } | 356 } |
272 } | 357 } |
273 | 358 |
274 @end | 359 @end |
OLD | NEW |