| 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 |
| (...skipping 26 matching lines...) Expand all Loading... |
| 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 Loading... |
| 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( |
| 243 blockCert.Pass(), base::SysNSStringToUTF8(host)); | 295 cert, 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 bool result = base::WorkerPool::PostTask(FROM_HERE, base::BindBlock(^{ | 306 bool result = base::WorkerPool::PostTask(FROM_HERE, base::BindBlock(^{ |
| 256 SecTrustResultType trustResult = kSecTrustResultInvalid; | 307 SecTrustResultType trustResult = kSecTrustResultInvalid; |
| 257 if (SecTrustEvaluate(trust.get(), &trustResult) != errSecSuccess) { | 308 if (SecTrustEvaluate(trust.get(), &trustResult) != errSecSuccess) { |
| 258 trustResult = kSecTrustResultInvalid; | 309 trustResult = kSecTrustResultInvalid; |
| 259 } | 310 } |
| 260 web::WebThread::PostTask(web::WebThread::IO, FROM_HERE,base::BindBlock(^{ | 311 web::WebThread::PostTask(web::WebThread::IO, FROM_HERE,base::BindBlock(^{ |
| 261 completionHandler(trustResult); | 312 completionHandler(trustResult); |
| 262 })); | 313 })); |
| 263 }), false /* task_is_slow */); | 314 }), false /* task_is_slow */); |
| 264 | 315 |
| 265 if (!result) { | 316 if (!result) { |
| 266 completionHandler(kSecTrustResultInvalid); | 317 completionHandler(kSecTrustResultInvalid); |
| 267 } | 318 } |
| 268 } | 319 } |
| 269 | 320 |
| 270 @end | 321 @end |
| OLD | NEW |