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(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 |
OLD | NEW |