Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(176)

Side by Side Diff: ios/web/net/crw_cert_verification_controller.mm

Issue 1357773002: WKWebView: Implemented recoverable SSL interstitials. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@lock_coloring
Patch Set: Addressed unit tests review comments Created 5 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
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 35 matching lines...) Expand 10 before | Expand all | Expand 10 after
60 61
61 } // namespace 62 } // namespace
62 63
63 @interface CRWCertVerificationController () { 64 @interface CRWCertVerificationController () {
64 // Cert verification object which wraps |net::CertVerifier|. Must be created, 65 // Cert verification object which wraps |net::CertVerifier|. Must be created,
65 // used and destroyed on IO Thread. 66 // used and destroyed on IO Thread.
66 scoped_ptr<web::CertVerifierBlockAdapter> _certVerifier; 67 scoped_ptr<web::CertVerifierBlockAdapter> _certVerifier;
67 68
68 // URLRequestContextGetter for obtaining net layer objects. 69 // URLRequestContextGetter for obtaining net layer objects.
69 net::URLRequestContextGetter* _contextGetter; 70 net::URLRequestContextGetter* _contextGetter;
71
72 // Used to remember user exceptions to invalid certs.
73 scoped_refptr<web::CertificatePolicyCache> _certPolicyCache;
70 } 74 }
71 75
72 // Cert verification flags. Must be used on IO Thread. 76 // Cert verification flags. Must be used on IO Thread.
73 @property(nonatomic, readonly) int certVerifyFlags; 77 @property(nonatomic, readonly) int certVerifyFlags;
74 78
75 // Creates _certVerifier object on IO thread. 79 // Creates _certVerifier object on IO thread.
76 - (void)createCertVerifier; 80 - (void)createCertVerifier;
77 81
78 // Verifies the given |cert| for the given |host| using |net::CertVerifier| and 82 // Verifies the given |cert| for the given |host| using |net::CertVerifier| and
79 // calls |completionHandler| on completion. This method can be called on any 83 // calls |completionHandler| on completion. This method can be called on any
80 // thread. |completionHandler| cannot be null and will be called asynchronously 84 // thread. |completionHandler| cannot be null and will be called asynchronously
81 // on IO thread. 85 // on IO thread or synchronously on current thread if IO task can't start (in
86 // this case |dispatched| argument will be NO).
82 - (void)verifyCert:(const scoped_refptr<net::X509Certificate>&)cert 87 - (void)verifyCert:(const scoped_refptr<net::X509Certificate>&)cert
83 forHost:(NSString*)host 88 forHost:(NSString*)host
84 completionHandler:(void (^)(net::CertVerifyResult, int))completionHandler; 89 completionHandler:(void (^)(net::CertVerifyResult, BOOL dispatched))handler;
85 90
86 // Verifies the given |trust| using SecTrustRef API. |completionHandler| cannot 91 // Verifies the given |trust| using SecTrustRef API. |completionHandler| cannot
87 // be null and will be either called asynchronously on Worker thread or 92 // be null and will be either called asynchronously on Worker thread or
88 // synchronously on current thread if the worker task can't start (in this 93 // synchronously on current thread if the worker task can't start (in this
89 // case |dispatched| argument will be NO). 94 // case |dispatched| argument will be NO).
90 - (void)verifyTrust:(base::ScopedCFTypeRef<SecTrustRef>)trust 95 - (void)verifyTrust:(base::ScopedCFTypeRef<SecTrustRef>)trust
91 completionHandler:(void (^)(SecTrustResultType, BOOL dispatched))handler; 96 completionHandler:(void (^)(SecTrustResultType, BOOL dispatched))handler;
92 97
98 // Returns cert accept policy for the given SecTrust result. |trustResult| must
99 // not be for a valid cert. Must be called on IO thread.
100 - (web::CertAcceptPolicy)
101 loadPolicyForBadTrustResult:(SecTrustResultType)trustResult
102 certVerifierResult:(net::CertVerifyResult)certVerifierResult
103 serverTrust:(SecTrustRef)trust
104 host:(NSString*)host;
105
93 @end 106 @end
94 107
95 @implementation CRWCertVerificationController 108 @implementation CRWCertVerificationController
96 109
97 #pragma mark - Superclass 110 #pragma mark - Superclass
98 111
99 - (void)dealloc { 112 - (void)dealloc {
100 DCHECK(!_certVerifier); 113 DCHECK(!_certVerifier);
101 [super dealloc]; 114 [super dealloc];
102 } 115 }
103 116
104 #pragma mark - Public 117 #pragma mark - Public
105 118
106 - (instancetype)init { 119 - (instancetype)init {
107 NOTREACHED(); 120 NOTREACHED();
108 return nil; 121 return nil;
109 } 122 }
110 123
111 - (instancetype)initWithBrowserState:(web::BrowserState*)browserState { 124 - (instancetype)initWithBrowserState:(web::BrowserState*)browserState {
112 DCHECK(browserState); 125 DCHECK(browserState);
113 DCHECK_CURRENTLY_ON_WEB_THREAD(web::WebThread::UI); 126 DCHECK_CURRENTLY_ON_WEB_THREAD(web::WebThread::UI);
114 self = [super init]; 127 self = [super init];
115 if (self) { 128 if (self) {
116 _contextGetter = browserState->GetRequestContext(); 129 _contextGetter = browserState->GetRequestContext();
117 DCHECK(_contextGetter); 130 DCHECK(_contextGetter);
131 _certPolicyCache =
132 web::BrowserState::GetCertificatePolicyCache(browserState);
133
118 [self createCertVerifier]; 134 [self createCertVerifier];
119 } 135 }
120 return self; 136 return self;
121 } 137 }
122 138
123 - (void)decidePolicyForCert:(const scoped_refptr<net::X509Certificate>&)cert 139 - (void)decideLoadPolicyForTrust:(base::ScopedCFTypeRef<SecTrustRef>)trust
124 host:(NSString*)host 140 host:(NSString*)host
125 completionHandler:(web::PolicyDecisionHandler)completionHandler { 141 completionHandler:(web::PolicyDecisionHandler)completionHandler {
126 DCHECK_CURRENTLY_ON_WEB_THREAD(web::WebThread::UI); 142 DCHECK_CURRENTLY_ON_WEB_THREAD(web::WebThread::UI);
127 // completionHandler of |verifyCert:forHost:completionHandler:| is called on 143 // completionHandler of |verifyCert:forHost:completionHandler:| is called on
128 // IO thread and then bounces back to UI thread. As a result all objects 144 // IO thread and then bounces back to UI thread. As a result all objects
129 // captured by completionHandler may be released on either UI or IO thread. 145 // captured by completionHandler may be released on either UI or IO thread.
130 // Since |completionHandler| can potentially capture multiple thread unsafe 146 // Since |completionHandler| can potentially capture multiple thread unsafe
131 // objects (like Web Controller) |completionHandler| itself should never be 147 // objects (like Web Controller) |completionHandler| itself should never be
132 // released on background thread and |BlockHolder| ensures that. 148 // released on background thread and |BlockHolder| ensures that.
133 __block scoped_refptr<BlockHolder<web::PolicyDecisionHandler>> handlerHolder( 149 __block scoped_refptr<BlockHolder<web::PolicyDecisionHandler>> handlerHolder(
134 new BlockHolder<web::PolicyDecisionHandler>(completionHandler)); 150 new BlockHolder<web::PolicyDecisionHandler>(completionHandler));
135 [self verifyCert:cert 151 [self verifyTrust:trust
136 forHost:host 152 completionHandler:^(SecTrustResultType trustResult, BOOL dispatched) {
137 completionHandler:^(net::CertVerifyResult result, int error) { 153 if (!dispatched) {
138 web::CertAcceptPolicy policy = 154 // Cert verification task did not start.
139 web::CERT_ACCEPT_POLICY_NON_RECOVERABLE_ERROR; 155 dispatch_async(dispatch_get_main_queue(), ^{
140 if (error == net::OK) { 156 handlerHolder->call(web::CERT_ACCEPT_POLICY_NON_RECOVERABLE_ERROR,
141 policy = web::CERT_ACCEPT_POLICY_ALLOW; 157 net::CertStatus());
142 } else if (net::IsCertStatusError(result.cert_status)) { 158 });
143 policy = net::IsCertStatusMinorError(result.cert_status) 159 return;
144 ? web::CERT_ACCEPT_POLICY_ALLOW
145 : web::CERT_ACCEPT_POLICY_RECOVERABLE_ERROR;
146 } 160 }
147 161
148 dispatch_async(dispatch_get_main_queue(), ^{ 162 if (web::GetSecurityStyleFromTrustResult(trustResult) ==
149 handlerHolder->call(policy, result.cert_status); 163 web::SECURITY_STYLE_AUTHENTICATED) {
150 }); 164 // SecTrust API considers this cert as valid.
165 dispatch_async(dispatch_get_main_queue(), ^{
166 handlerHolder->call(web::CERT_ACCEPT_POLICY_ALLOW,
167 net::CertStatus());
168 });
169 return;
170 }
171
172 // SecTrust API considers this cert as invalid. Check the reason and
173 // whether or not user has decided to proceed with this bad cert.
174 scoped_refptr<net::X509Certificate> cert(
175 web::CreateCertFromTrust(trust));
176 [self verifyCert:cert
177 forHost:host
178 completionHandler:^(net::CertVerifyResult certVerifierResult,
179 BOOL dispatched) {
180 if (!dispatched) {
181 // Cert verification task did not start.
182 dispatch_async(dispatch_get_main_queue(), ^{
183 handlerHolder->call(
184 web::CERT_ACCEPT_POLICY_NON_RECOVERABLE_ERROR,
185 net::CertStatus());
186 });
187 return;
188 }
189
190 web::CertAcceptPolicy policy =
191 [self loadPolicyForBadTrustResult:trustResult
192 certVerifierResult:certVerifierResult
193 serverTrust:trust.get()
194 host:host];
195
196 dispatch_async(dispatch_get_main_queue(), ^{
197 handlerHolder->call(policy, certVerifierResult.cert_status);
198 });
199 }];
151 }]; 200 }];
152 } 201 }
153 202
154 - (void)querySSLStatusForTrust:(base::ScopedCFTypeRef<SecTrustRef>)trust 203 - (void)querySSLStatusForTrust:(base::ScopedCFTypeRef<SecTrustRef>)trust
155 host:(NSString*)host 204 host:(NSString*)host
156 completionHandler:(web::StatusQueryHandler)completionHandler { 205 completionHandler:(web::StatusQueryHandler)completionHandler {
157 DCHECK_CURRENTLY_ON_WEB_THREAD(web::WebThread::UI); 206 DCHECK_CURRENTLY_ON_WEB_THREAD(web::WebThread::UI);
158 207
159 // The completion handlers of |verifyCert:forHost:completionHandler:| and 208 // The completion handlers of |verifyCert:forHost:completionHandler:| and
160 // |verifyTrust:completionHandler:| will be deallocated on background thread. 209 // |verifyTrust:completionHandler:| will be deallocated on background thread.
(...skipping 23 matching lines...) Expand all
184 233
185 // Retrieve the net::CertStatus for invalid certificates to determine 234 // Retrieve the net::CertStatus for invalid certificates to determine
186 // the rejection reason, it is possible that rejection reason could not 235 // the rejection reason, it is possible that rejection reason could not
187 // be determined and |cert_status| will be empty. 236 // be determined and |cert_status| will be empty.
188 // TODO(eugenebut): Add UMA for CertVerifier and SecTrust verification 237 // TODO(eugenebut): Add UMA for CertVerifier and SecTrust verification
189 // mismatch (crbug.com/535699). 238 // mismatch (crbug.com/535699).
190 scoped_refptr<net::X509Certificate> cert( 239 scoped_refptr<net::X509Certificate> cert(
191 web::CreateCertFromTrust(trust)); 240 web::CreateCertFromTrust(trust));
192 [self verifyCert:cert 241 [self verifyCert:cert
193 forHost:host 242 forHost:host
194 completionHandler:^(net::CertVerifyResult certVerifierResult, int) { 243 completionHandler:^(net::CertVerifyResult certVerifierResult,
244 BOOL) {
195 dispatch_async(dispatch_get_main_queue(), ^{ 245 dispatch_async(dispatch_get_main_queue(), ^{
196 handlerHolder->call(securityStyle, 246 handlerHolder->call(securityStyle,
197 certVerifierResult.cert_status); 247 certVerifierResult.cert_status);
198 }); 248 });
199 }]; 249 }];
200 }]; 250 }];
201 } 251 }
202 252
253 - (void)allowCert:(scoped_refptr<net::X509Certificate>)cert
254 forHost:(NSString*)host
255 status:(net::CertStatus)status {
256 DCHECK_CURRENTLY_ON_WEB_THREAD(web::WebThread::UI);
257 // Store user decisions with the leaf cert, ignoring any intermediates.
258 // This is because WKWebView returns the verified certificate chain in
259 // |webView:didReceiveAuthenticationChallenge:completionHandler:|,
260 // but the server-supplied chain in
261 // |webView:didFailProvisionalNavigation:withError:|.
262 if (!cert->GetIntermediateCertificates().empty()) {
263 cert = net::X509Certificate::CreateFromHandle(
264 cert->os_cert_handle(), net::X509Certificate::OSCertHandles());
265 }
266 DCHECK(cert->GetIntermediateCertificates().empty());
267 web::WebThread::PostTask(web::WebThread::IO, FROM_HERE, base::BindBlock(^{
268 _certPolicyCache->AllowCertForHost(
269 cert.get(), base::SysNSStringToUTF8(host), status);
270 }));
271 }
272
203 - (void)shutDown { 273 - (void)shutDown {
204 DCHECK_CURRENTLY_ON_WEB_THREAD(web::WebThread::UI); 274 DCHECK_CURRENTLY_ON_WEB_THREAD(web::WebThread::UI);
205 web::WebThread::PostTask(web::WebThread::IO, FROM_HERE, base::BindBlock(^{ 275 web::WebThread::PostTask(web::WebThread::IO, FROM_HERE, base::BindBlock(^{
206 // This block captures |self| delaying its deallocation and causing dealloc 276 // This block captures |self| delaying its deallocation and causing dealloc
207 // to happen on either IO or UI thread (which is fine for this class). 277 // to happen on either IO or UI thread (which is fine for this class).
208 _certVerifier.reset(); 278 _certVerifier.reset();
209 })); 279 }));
210 } 280 }
211 281
212 #pragma mark - Private 282 #pragma mark - Private
(...skipping 15 matching lines...) Expand all
228 - (void)createCertVerifier { 298 - (void)createCertVerifier {
229 web::WebThread::PostTask(web::WebThread::IO, FROM_HERE, base::BindBlock(^{ 299 web::WebThread::PostTask(web::WebThread::IO, FROM_HERE, base::BindBlock(^{
230 net::URLRequestContext* context = _contextGetter->GetURLRequestContext(); 300 net::URLRequestContext* context = _contextGetter->GetURLRequestContext();
231 _certVerifier.reset(new web::CertVerifierBlockAdapter( 301 _certVerifier.reset(new web::CertVerifierBlockAdapter(
232 context->cert_verifier(), context->net_log())); 302 context->cert_verifier(), context->net_log()));
233 })); 303 }));
234 } 304 }
235 305
236 - (void)verifyCert:(const scoped_refptr<net::X509Certificate>&)cert 306 - (void)verifyCert:(const scoped_refptr<net::X509Certificate>&)cert
237 forHost:(NSString*)host 307 forHost:(NSString*)host
238 completionHandler:(void (^)(net::CertVerifyResult, int))completionHandler { 308 completionHandler:(void (^)(net::CertVerifyResult, BOOL))completionHandler {
239 DCHECK(completionHandler); 309 DCHECK(completionHandler);
240 __block scoped_refptr<net::X509Certificate> blockCert = cert; 310 __block scoped_refptr<net::X509Certificate> blockCert = cert;
241 web::WebThread::PostTask( 311 bool dispatched = web::WebThread::PostTask(
242 web::WebThread::IO, FROM_HERE, base::BindBlock(^{ 312 web::WebThread::IO, FROM_HERE, base::BindBlock(^{
243 // WeakNSObject does not work across different threads, hence this block 313 // WeakNSObject does not work across different threads, hence this block
244 // retains self. 314 // retains self.
245 if (!_certVerifier) { 315 if (!_certVerifier) {
246 completionHandler(net::CertVerifyResult(), net::ERR_FAILED); 316 completionHandler(net::CertVerifyResult(), net::ERR_FAILED);
247 return; 317 return;
248 } 318 }
249 319
250 web::CertVerifierBlockAdapter::Params params( 320 web::CertVerifierBlockAdapter::Params params(
251 blockCert.Pass(), base::SysNSStringToUTF8(host)); 321 blockCert.Pass(), base::SysNSStringToUTF8(host));
252 params.flags = self.certVerifyFlags; 322 params.flags = self.certVerifyFlags;
253 params.crl_set = net::SSLConfigService::GetCRLSet(); 323 params.crl_set = net::SSLConfigService::GetCRLSet();
254 // OCSP response is not provided by iOS API. 324 // OCSP response is not provided by iOS API.
255 _certVerifier->Verify(params, completionHandler); 325 _certVerifier->Verify(params, ^(net::CertVerifyResult result, int) {
326 completionHandler(result, YES);
327 });
256 })); 328 }));
329
330 if (!dispatched) {
331 completionHandler(net::CertVerifyResult(), NO);
332 }
257 } 333 }
258 334
259 - (void)verifyTrust:(base::ScopedCFTypeRef<SecTrustRef>)trust 335 - (void)verifyTrust:(base::ScopedCFTypeRef<SecTrustRef>)trust
260 completionHandler:(void (^)(SecTrustResultType, BOOL))completionHandler { 336 completionHandler:(void (^)(SecTrustResultType, BOOL))completionHandler {
261 DCHECK(completionHandler); 337 DCHECK(completionHandler);
262 // SecTrustEvaluate performs trust evaluation synchronously, possibly making 338 // SecTrustEvaluate performs trust evaluation synchronously, possibly making
263 // network requests. The UI thread should not be blocked by that operation. 339 // network requests. The UI thread should not be blocked by that operation.
264 bool dispatched = base::WorkerPool::PostTask(FROM_HERE, base::BindBlock(^{ 340 bool dispatched = base::WorkerPool::PostTask(FROM_HERE, base::BindBlock(^{
265 SecTrustResultType trustResult = kSecTrustResultInvalid; 341 SecTrustResultType trustResult = kSecTrustResultInvalid;
266 if (SecTrustEvaluate(trust.get(), &trustResult) != errSecSuccess) { 342 if (SecTrustEvaluate(trust.get(), &trustResult) != errSecSuccess) {
267 trustResult = kSecTrustResultInvalid; 343 trustResult = kSecTrustResultInvalid;
268 } 344 }
269 completionHandler(trustResult, YES); 345 completionHandler(trustResult, YES);
270 }), false /* task_is_slow */); 346 }), false /* task_is_slow */);
271 347
272 if (!dispatched) { 348 if (!dispatched) {
273 completionHandler(kSecTrustResultInvalid, NO); 349 completionHandler(kSecTrustResultInvalid, NO);
274 } 350 }
275 } 351 }
276 352
353 - (web::CertAcceptPolicy)
354 loadPolicyForBadTrustResult:(SecTrustResultType)trustResult
355 certVerifierResult:(net::CertVerifyResult)certVerifierResult
356 serverTrust:(SecTrustRef)trust
357 host:(NSString*)host {
358 DCHECK_CURRENTLY_ON_WEB_THREAD(web::WebThread::IO);
359 DCHECK_NE(web::SECURITY_STYLE_AUTHENTICATED,
360 web::GetSecurityStyleFromTrustResult(trustResult));
361
362 if (trustResult != kSecTrustResultRecoverableTrustFailure ||
363 SecTrustGetCertificateCount(trust) == 0) {
364 // Trust result is not recoverable or leaf cert is missing.
365 return web::CERT_ACCEPT_POLICY_NON_RECOVERABLE_ERROR;
366 }
367
368 // Check if user has decided to proceed with this bad cert.
369 scoped_refptr<net::X509Certificate> leafCert =
370 net::X509Certificate::CreateFromHandle(
371 SecTrustGetCertificateAtIndex(trust, 0),
372 net::X509Certificate::OSCertHandles());
373
374 web::CertPolicy::Judgment judgment = _certPolicyCache->QueryPolicy(
375 leafCert.get(), base::SysNSStringToUTF8(host),
376 certVerifierResult.cert_status);
377
378 return (judgment == web::CertPolicy::ALLOWED)
379 ? web::CERT_ACCEPT_POLICY_RECOVERABLE_ERROR_ACCEPTED_BY_USER
380 : web::CERT_ACCEPT_POLICY_RECOVERABLE_ERROR_UNDECIDED_BY_USER;
381 }
382
277 @end 383 @end
OLDNEW
« no previous file with comments | « ios/web/net/crw_cert_verification_controller.h ('k') | ios/web/net/crw_cert_verification_controller_unittest.mm » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698