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

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: Resolved Stuart's review comments 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
(...skipping 26 matching lines...) Expand all
106 return nil; 113 return nil;
107 } 114 }
108 115
109 - (instancetype)initWithBrowserState:(web::BrowserState*)browserState { 116 - (instancetype)initWithBrowserState:(web::BrowserState*)browserState {
110 DCHECK(browserState); 117 DCHECK(browserState);
111 DCHECK_CURRENTLY_ON_WEB_THREAD(web::WebThread::UI); 118 DCHECK_CURRENTLY_ON_WEB_THREAD(web::WebThread::UI);
112 self = [super init]; 119 self = [super init];
113 if (self) { 120 if (self) {
114 _contextGetter = browserState->GetRequestContext(); 121 _contextGetter = browserState->GetRequestContext();
115 DCHECK(_contextGetter); 122 DCHECK(_contextGetter);
123 _certPolicyCache =
124 web::BrowserState::GetCertificatePolicyCache(browserState);
125
116 [self createCertVerifier]; 126 [self createCertVerifier];
117 } 127 }
118 return self; 128 return self;
119 } 129 }
120 130
121 - (void)decidePolicyForCert:(const scoped_refptr<net::X509Certificate>&)cert 131 - (void)decideLoadPolicyForTrust:(SecTrustRef)serverTrust
122 host:(NSString*)host 132 host:(NSString*)host
123 completionHandler:(web::PolicyDecisionHandler)completionHandler { 133 completionHandler:(web::PolicyDecisionHandler)completionHandler {
124 DCHECK_CURRENTLY_ON_WEB_THREAD(web::WebThread::UI); 134 DCHECK_CURRENTLY_ON_WEB_THREAD(web::WebThread::UI);
125 // completionHandler of |verifyCert:forHost:completionHandler:| is called on 135 // completionHandler of |verifyCert:forHost:completionHandler:| is called on
126 // IO thread and then bounces back to UI thread. As a result all objects 136 // IO thread and then bounces back to UI thread. As a result all objects
127 // captured by completionHandler may be released on either UI or IO thread. 137 // captured by completionHandler may be released on either UI or IO thread.
128 // Since |completionHandler| can potentially capture multiple thread unsafe 138 // Since |completionHandler| can potentially capture multiple thread unsafe
129 // objects (like Web Controller) |completionHandler| itself should never be 139 // objects (like Web Controller) |completionHandler| itself should never be
130 // released on background thread and |BlockHolder| ensures that. 140 // released on background thread and |BlockHolder| ensures that.
131 __block scoped_refptr<BlockHolder<web::PolicyDecisionHandler>> handlerHolder( 141 __block scoped_refptr<BlockHolder<web::PolicyDecisionHandler>> handlerHolder(
132 new BlockHolder<web::PolicyDecisionHandler>(completionHandler)); 142 new BlockHolder<web::PolicyDecisionHandler>(completionHandler));
133 [self verifyCert:cert
134 forHost:host
135 completionHandler:^(net::CertVerifyResult result, int error) {
136 web::CertAcceptPolicy policy =
137 web::CERT_ACCEPT_POLICY_NON_RECOVERABLE_ERROR;
138 if (error == net::OK) {
139 policy = web::CERT_ACCEPT_POLICY_ALLOW;
140 } else if (net::IsCertStatusError(result.cert_status)) {
141 policy = net::IsCertStatusMinorError(result.cert_status)
142 ? web::CERT_ACCEPT_POLICY_ALLOW
143 : web::CERT_ACCEPT_POLICY_RECOVERABLE_ERROR;
144 }
145 143
144 web::WebThread::PostTask(web::WebThread::IO, FROM_HERE, base::BindBlock(^{
145 base::ScopedCFTypeRef<SecTrustRef> trust(
146 serverTrust, base::scoped_policy::RETAIN);
147 [self verifyTrust:trust completionHandler:^(SecTrustResultType result) {
148 if (result == kSecTrustResultProceed ||
149 result == kSecTrustResultUnspecified) {
150 // SecTrust API considers this cert as valid.
146 dispatch_async(dispatch_get_main_queue(), ^{ 151 dispatch_async(dispatch_get_main_queue(), ^{
147 handlerHolder->call(policy, result.cert_status); 152 handlerHolder->call(web::CERT_ACCEPT_POLICY_ALLOW, net::CertStatus());
148 }); 153 });
149 }]; 154 return;
155 }
156
157 // SecTrust API considers this cert as invalid. Check the reason and
158 // whether or not user has decided to proceed with this bad cert.
159 scoped_refptr<net::X509Certificate> cert(web::CreateCertFromTrust(trust));
160 [self verifyCert:cert
161 forHost:host
162 completionHandler:^(net::CertVerifyResult certVerifierResult, int) {
163 web::CertAcceptPolicy policy =
164 web::CERT_ACCEPT_POLICY_NON_RECOVERABLE_ERROR;
165 scoped_refptr<net::X509Certificate> leafCert;
166 if (result == kSecTrustResultRecoverableTrustFailure &&
167 SecTrustGetCertificateCount(trust)) {
168 // Check if user has decided to proceed with this bad cert.
169 leafCert = web::CreateCertFromChain(@[
170 static_cast<id>(SecTrustGetCertificateAtIndex(trust, 0))
171 ]);
172
173 web::CertPolicy::Judgment judgment =
174 _certPolicyCache->QueryPolicy(
175 leafCert.get(), base::SysNSStringToUTF8(host),
176 certVerifierResult.cert_status);
177
178 policy = (judgment == web::CertPolicy::ALLOWED)
179 ? CERT_ACCEPT_POLICY_RECOVERABLE_ERROR_ACCEPTED_BY_USER
180 : CERT_ACCEPT_POLICY_RECOVERABLE_ERROR_NOT_ACCEPTED_BY_USER;
181 }
182
183 dispatch_async(dispatch_get_main_queue(), ^{
184 handlerHolder->call(policy, certVerifierResult.cert_status);
185 });
186 }];
187 }];
188 }));
150 } 189 }
151 190
152 - (void)querySSLStatusForCertChain:(NSArray*)certChain 191 - (void)querySSLStatusForCertChain:(NSArray*)certChain
153 host:(NSString*)host 192 host:(NSString*)host
154 completionHandler:(web::StatusQueryHandler)completionHandler { 193 completionHandler:(web::StatusQueryHandler)completionHandler {
155 DCHECK_CURRENTLY_ON_WEB_THREAD(web::WebThread::UI); 194 DCHECK_CURRENTLY_ON_WEB_THREAD(web::WebThread::UI);
156 DCHECK(certChain.count); 195 DCHECK(certChain.count);
157 196
158 // Completion handler of |verifyCert:forHost:completionHandler:| will be 197 // Completion handler of |verifyCert:forHost:completionHandler:| will be
159 // deallocated on IO thread. |completionHandler| itself should never be 198 // deallocated on IO thread. |completionHandler| itself should never be
160 // released on background thread and |BlockHolder| ensures that. 199 // released on background thread and |BlockHolder| ensures that.
161 __block scoped_refptr<BlockHolder<web::StatusQueryHandler>> handlerHolder( 200 __block scoped_refptr<BlockHolder<web::StatusQueryHandler>> handlerHolder(
162 new BlockHolder<web::StatusQueryHandler>(completionHandler)); 201 new BlockHolder<web::StatusQueryHandler>(completionHandler));
163 scoped_refptr<net::X509Certificate> cert(web::CreateCertFromChain(certChain)); 202 web::WebThread::PostTask(web::WebThread::IO, FROM_HERE, base::BindBlock(^{
164 // Retrieve the net::CertStatus to allow Chromium-specific policies to be 203 scoped_refptr<net::X509Certificate> cert(
165 // enacted. This is called for all certificates (valid and invalid alike), 204 web::CreateCertFromChain(certChain));
166 // since the net::CertVerifier may reject or downgrade certificates that 205 // Retrieve the net::CertStatus to allow Chromium-specific policies to
167 // SecTrust accepts. 206 // be enacted. This is called for all certificates (valid and invalid
168 [self verifyCert:cert 207 // alike), since the net::CertVerifier may reject or downgrade certificates
169 forHost:host 208 // that SecTrust accepts.
170 completionHandler:^(net::CertVerifyResult certVerifierResult, int) { 209 [self verifyCert:cert
171 [self verifyTrust:web::CreateServerTrustFromChain(certChain, host) 210 forHost:host
172 completionHandler:^(SecTrustResultType trustResult) { 211 completionHandler:^(net::CertVerifyResult certVerifierResult, int) {
173 // If SecTrust has accepted a certificate, clear any error 212 [self verifyTrust:web::CreateServerTrustFromChain(certChain, host)
174 // statuses returned by net::CertVerifier, since the CertVerifier 213 completionHandler:^(SecTrustResultType trustResult) {
175 // may not have access to the same trust information 214 // If SecTrust has accepted a certificate, clear any error
176 // (e.g. custom-installed roots). Note, this also disables 215 // statuses returned by net::CertVerifier, since the
177 // net::CertVerifier's ability to reject certificates, such as via 216 // CertVerifier may not have access to the same trust
178 // CRLSets. However, copy any supplemental status bits over, since 217 // information (e.g. custom-installed roots). Note, this also
179 // those may be used as part of policy logic, such as deprecating 218 // disables net::CertVerifier's ability to reject certificates,
180 // SHA-1 or requiring Certificate Transparency. 219 // such as via CRLSets. However, copy any supplemental status
181 web::SecurityStyle security_style = 220 // bits over, since those may be used as part of policy logic,
182 web::GetSecurityStyleFromTrustResult(trustResult); 221 // such as deprecating SHA-1 or requiring Certificate
183 net::CertStatus cert_status = certVerifierResult.cert_status; 222 // Transparency.
184 if (security_style == web::SECURITY_STYLE_AUTHENTICATED) { 223 web::SecurityStyle security_style =
185 cert_status &= ~net::CERT_STATUS_ALL_ERRORS; 224 web::GetSecurityStyleFromTrustResult(trustResult);
186 } 225 net::CertStatus cert_status =
226 certVerifierResult.cert_status;
227 if (security_style == web::SECURITY_STYLE_AUTHENTICATED) {
228 cert_status &= ~net::CERT_STATUS_ALL_ERRORS;
229 }
187 230
188 dispatch_async(dispatch_get_main_queue(), ^{ 231 dispatch_async(dispatch_get_main_queue(), ^{
189 handlerHolder->call(security_style, cert_status); 232 handlerHolder->call(security_style, cert_status);
190 }); 233 });
191 }]; 234 }];
192 }]; 235 }];
236 }));
237 }
238
239 - (void)allowCert:(scoped_refptr<net::X509Certificate>)cert
240 forHost:(NSString*)host
241 status:(net::CertStatus)status {
242 DCHECK_CURRENTLY_ON_WEB_THREAD(web::WebThread::UI);
243 DCHECK(cert);
244 DCHECK_EQ(0U, cert->GetIntermediateCertificates().size());
245 web::WebThread::PostTask(web::WebThread::IO, FROM_HERE, base::BindBlock(^{
246 _certPolicyCache->AllowCertForHost(
247 cert.get(), base::SysNSStringToUTF8(host), status);
248 }));
193 } 249 }
194 250
195 - (void)shutDown { 251 - (void)shutDown {
196 DCHECK_CURRENTLY_ON_WEB_THREAD(web::WebThread::UI); 252 DCHECK_CURRENTLY_ON_WEB_THREAD(web::WebThread::UI);
197 web::WebThread::PostTask(web::WebThread::IO, FROM_HERE, base::BindBlock(^{ 253 web::WebThread::PostTask(web::WebThread::IO, FROM_HERE, base::BindBlock(^{
198 // This block captures |self| delaying its deallocation and causing dealloc 254 // This block captures |self| delaying its deallocation and causing dealloc
199 // to happen on either IO or UI thread (which is fine for this class). 255 // to happen on either IO or UI thread (which is fine for this class).
200 _certVerifier.reset(); 256 _certVerifier.reset();
201 })); 257 }));
202 } 258 }
(...skipping 18 matching lines...) Expand all
221 web::WebThread::PostTask(web::WebThread::IO, FROM_HERE, base::BindBlock(^{ 277 web::WebThread::PostTask(web::WebThread::IO, FROM_HERE, base::BindBlock(^{
222 net::URLRequestContext* context = _contextGetter->GetURLRequestContext(); 278 net::URLRequestContext* context = _contextGetter->GetURLRequestContext();
223 _certVerifier.reset(new web::CertVerifierBlockAdapter( 279 _certVerifier.reset(new web::CertVerifierBlockAdapter(
224 context->cert_verifier(), context->net_log())); 280 context->cert_verifier(), context->net_log()));
225 })); 281 }));
226 } 282 }
227 283
228 - (void)verifyCert:(const scoped_refptr<net::X509Certificate>&)cert 284 - (void)verifyCert:(const scoped_refptr<net::X509Certificate>&)cert
229 forHost:(NSString*)host 285 forHost:(NSString*)host
230 completionHandler:(void (^)(net::CertVerifyResult, int))completionHandler { 286 completionHandler:(void (^)(net::CertVerifyResult, int))completionHandler {
287 DCHECK(web::WebThread::CurrentlyOn(web::WebThread::IO));
231 DCHECK(completionHandler); 288 DCHECK(completionHandler);
232 __block scoped_refptr<net::X509Certificate> blockCert = cert; 289 if (!_certVerifier) {
233 web::WebThread::PostTask( 290 completionHandler(net::CertVerifyResult(), net::ERR_FAILED);
234 web::WebThread::IO, FROM_HERE, base::BindBlock(^{ 291 return;
235 // WeakNSObject does not work across different threads, hence this block 292 }
236 // retains self.
237 if (!_certVerifier) {
238 completionHandler(net::CertVerifyResult(), net::ERR_FAILED);
239 return;
240 }
241 293
242 web::CertVerifierBlockAdapter::Params params( 294 web::CertVerifierBlockAdapter::Params params(cert,
243 blockCert.Pass(), base::SysNSStringToUTF8(host)); 295 base::SysNSStringToUTF8(host));
244 params.flags = self.certVerifyFlags; 296 params.flags = self.certVerifyFlags;
245 params.crl_set = net::SSLConfigService::GetCRLSet(); 297 params.crl_set = net::SSLConfigService::GetCRLSet();
246 // OCSP response is not provided by iOS API. 298 // OCSP response is not provided by iOS API.
247 _certVerifier->Verify(params, completionHandler); 299 _certVerifier->Verify(params, completionHandler);
248 }));
249 } 300 }
250 301
251 - (void)verifyTrust:(base::ScopedCFTypeRef<SecTrustRef>)trust 302 - (void)verifyTrust:(base::ScopedCFTypeRef<SecTrustRef>)trust
252 completionHandler:(void (^)(SecTrustResultType))completionHandler { 303 completionHandler:(void (^)(SecTrustResultType))completionHandler {
253 DCHECK(web::WebThread::CurrentlyOn(web::WebThread::IO)); 304 DCHECK(web::WebThread::CurrentlyOn(web::WebThread::IO));
254 DCHECK(completionHandler); 305 DCHECK(completionHandler);
255 // SecTrustEvaluate performs trust evaluation synchronously, possibly making 306 // SecTrustEvaluate performs trust evaluation synchronously, possibly making
256 // betwork requests. IO thread should not be blocked by that operation. 307 // betwork requests. IO thread should not be blocked by that operation.
257 bool result = base::WorkerPool::PostTask(FROM_HERE, base::BindBlock(^{ 308 bool result = base::WorkerPool::PostTask(FROM_HERE, base::BindBlock(^{
258 SecTrustResultType trustResult = kSecTrustResultInvalid; 309 SecTrustResultType trustResult = kSecTrustResultInvalid;
259 if (SecTrustEvaluate(trust.get(), &trustResult) != errSecSuccess) { 310 if (SecTrustEvaluate(trust.get(), &trustResult) != errSecSuccess) {
260 trustResult = kSecTrustResultInvalid; 311 trustResult = kSecTrustResultInvalid;
261 } 312 }
262 web::WebThread::PostTask(web::WebThread::IO, FROM_HERE,base::BindBlock(^{ 313 web::WebThread::PostTask(web::WebThread::IO, FROM_HERE,base::BindBlock(^{
263 completionHandler(trustResult); 314 completionHandler(trustResult);
264 })); 315 }));
265 }), false /* task_is_slow */); 316 }), false /* task_is_slow */);
266 317
267 if (!result) { 318 if (!result) {
268 completionHandler(kSecTrustResultInvalid); 319 completionHandler(kSecTrustResultInvalid);
269 } 320 }
270 } 321 }
271 322
272 @end 323 @end
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698