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

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: Self review Created 5 years, 2 months 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 28 matching lines...) Expand all
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
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
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
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
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