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

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

Issue 2225483002: [ios] Removed CertVerifierBlockAdapter. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Updated includes Created 4 years, 4 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 <memory> 7 #include <memory>
8 8
9 #include "base/ios/block_types.h" 9 #include "base/ios/block_types.h"
10 #include "base/logging.h" 10 #include "base/logging.h"
11 #include "base/mac/bind_objc_block.h" 11 #include "base/mac/bind_objc_block.h"
12 #include "base/mac/scoped_block.h"
13 #import "base/memory/ref_counted.h" 12 #import "base/memory/ref_counted.h"
14 #include "base/metrics/histogram_macros.h"
15 #include "base/rand_util.h"
16 #include "base/strings/sys_string_conversions.h" 13 #include "base/strings/sys_string_conversions.h"
17 #include "base/threading/worker_pool.h" 14 #include "base/threading/worker_pool.h"
18 #include "ios/web/net/cert_verifier_block_adapter.h"
19 #include "ios/web/public/browser_state.h" 15 #include "ios/web/public/browser_state.h"
20 #include "ios/web/public/certificate_policy_cache.h" 16 #include "ios/web/public/certificate_policy_cache.h"
21 #include "ios/web/public/web_thread.h" 17 #include "ios/web/public/web_thread.h"
22 #import "ios/web/web_state/wk_web_view_security_util.h" 18 #import "ios/web/web_state/wk_web_view_security_util.h"
23 #include "net/cert/cert_verify_result.h" 19 #include "net/cert/cert_verify_proc_ios.h"
24 #include "net/url_request/url_request_context.h"
25 #include "net/url_request/url_request_context_getter.h"
26
27 namespace {
28
29 // Enum for Web.CertVerifyAgreement UMA metric to report certificate
30 // verification mismatch between SecTrust API and CertVerifier. SecTrust API is
31 // used for making load/no-load decision and CertVerifier is used for getting
32 // the reason of verification failure. It is expected that mismatches will
33 // happen for those 2 approaches (e.g. SecTrust API accepts the cert but
34 // CertVerifier rejects it). This metric helps to understand how common
35 // mismatches are.
36 // Note: This enum is used to back an UMA histogram, and should be treated as
37 // append-only.
38 enum CertVerifyAgreement {
39 // There is no mismach. Both SecTrust API and CertVerifier accepted this cert.
40 CERT_VERIFY_AGREEMENT_ACCEPTED_BY_BOTH = 0,
41 // There is no mismach. Both SecTrust API and CertVerifier rejected this cert.
42 CERT_VERIFY_AGREEMENT_REJECTED_BY_BOTH,
43 // SecTrust API accepted the cert, but CertVerifier rejected it (e.g. MDM cert
44 // or CertVerifier was more strict during verification), this mismach is
45 // expected to be common because of MDM certs.
46 CERT_VERIFY_AGREEMENT_ACCEPTED_ONLY_BY_IOS,
47 // SecTrust API rejected the cert, but CertVerifier accepted it. This mismatch
48 // is expected to be uncommon.
49 CERT_VERIFY_AGREEMENT_ACCEPTED_ONLY_BY_NSS,
50 CERT_VERIFY_AGREEMENT_COUNT,
51 };
52
53 // This class takes ownership of block and releases it on UI thread, even if
54 // |BlockHolder| is destructed on a background thread. On destruction calls its
55 // block with default arguments, if block was not called before. This way
56 // BlockHolder guarantees that block is always called to satisfy API contract
57 // for CRWCertVerificationController (completion handlers are always called).
58 template <class T>
59 class BlockHolder : public base::RefCountedThreadSafe<BlockHolder<T>> {
60 public:
61 // Takes ownership of |block|, which must not be null. On destruction calls
62 // this block with |DefaultArgs|, if block was not called before.
63 // |DefaultArgs| must be passed by value, not by ponter or reference.
64 template <typename... Arguments>
65 BlockHolder(T block, Arguments... DefaultArgs)
66 : block_([block copy]),
67 called_(false),
68 default_block_([^{
69 block(DefaultArgs...);
70 } copy]) {
71 DCHECK(block_);
72 }
73
74 // Calls underlying block with the given variadic arguments.
75 template <typename... Arguments>
76 void call(Arguments... Args) {
77 block_(Args...);
78 called_ = true;
79 }
80
81 private:
82 BlockHolder() = delete;
83 friend class base::RefCountedThreadSafe<BlockHolder>;
84
85 // Finalizes object's destruction on UI thread by calling |default_block| (if
86 // |block| has not been called yet) and releasing all blocks. Must be called
87 // on UI thread.
88 static void Finalize(id block,
89 ProceduralBlock default_block,
90 bool block_was_called) {
91 DCHECK_CURRENTLY_ON(web::WebThread::UI);
92 // By calling default_block, BlockHolder guarantees that block is always
93 // called to satisfy API contract for CRWCertVerificationController
94 // (completion handlers are always called).
95 if (!block_was_called)
96 default_block();
97 [block release];
98 [default_block release];
99 }
100
101 // Releases underlying |block_| on UI thread, calls |default_block_| if
102 // |block_| has not been called yet.
103 ~BlockHolder() {
104 if (web::WebThread::CurrentlyOn(web::WebThread::UI)) {
105 Finalize(block_, default_block_, called_);
106 } else {
107 web::WebThread::PostTask(
108 web::WebThread::UI, FROM_HERE,
109 base::Bind(&BlockHolder::Finalize, block_, default_block_, called_));
110 }
111 }
112
113 T block_;
114 // true if this |block_| has already been called.
115 bool called_;
116 // Called on destruction if |block_| was not called.
117 ProceduralBlock default_block_;
118 };
119
120 typedef scoped_refptr<BlockHolder<web::PolicyDecisionHandler>>
121 PolicyDecisionHandlerHolder;
122
123 } // namespace
124 20
125 @interface CRWCertVerificationController () { 21 @interface CRWCertVerificationController () {
126 // Cert verification object which wraps |net::CertVerifier|. Must be created,
127 // used and destroyed on IO Thread.
128 std::unique_ptr<web::CertVerifierBlockAdapter> _certVerifier;
129
130 // URLRequestContextGetter for obtaining net layer objects.
131 net::URLRequestContextGetter* _contextGetter;
132
133 // Used to remember user exceptions to invalid certs. 22 // Used to remember user exceptions to invalid certs.
134 scoped_refptr<web::CertificatePolicyCache> _certPolicyCache; 23 scoped_refptr<web::CertificatePolicyCache> _certPolicyCache;
135 } 24 }
136 25
137 // Cert verification flags. Must be used on IO Thread. 26 // Returns cert status for the given |trust|.
138 @property(nonatomic, readonly) int certVerifyFlags; 27 - (net::CertStatus)certStatusFromTrustResult:(SecTrustResultType)trustResult
139 28 serverTrust:
140 // Returns YES if CertVerifier should be run (even if SecTrust API considers 29 (base::ScopedCFTypeRef<SecTrustRef>)trust;
141 // cert as valid) and Web.CertVerifyAgreement UMA metric should be reported.
142 // The result of this method is random and nondeterministic.
143 - (BOOL)shouldReportCertVerifyAgreement;
144
145 // Reports Web.CertVerifyAgreement UMA metric.
146 - (void)reportUMAForCertVerifyAgreement:(CertVerifyAgreement)agreement;
147
148 // Creates _certVerifier object on IO thread.
149 - (void)createCertVerifier;
150 30
151 // Decides the policy for the given |trust| which was rejected by iOS and the 31 // Decides the policy for the given |trust| which was rejected by iOS and the
152 // given |host| and calls |handler| on completion. 32 // given |host| and calls |handler| on completion. Must be called on UI thread.
33 // |handler| can not be null and will be called on UI thread.
153 - (void) 34 - (void)
154 decideLoadPolicyForRejectedTrustResult:(SecTrustResultType)trustResult 35 decideLoadPolicyForRejectedTrustResult:(SecTrustResultType)trustResult
155 serverTrust:(base::ScopedCFTypeRef<SecTrustRef>)trust 36 serverTrust:(base::ScopedCFTypeRef<SecTrustRef>)trust
156 host:(NSString*)host 37 host:(NSString*)host
157 completionHandler:(PolicyDecisionHandlerHolder)handler; 38 completionHandler:(web::PolicyDecisionHandler)handler;
158
159 // Decides the policy for the given |trust| which was accepted by iOS and the
160 // given |host| and calls |handler| on completion.
161 - (void)
162 decideLoadPolicyForAcceptedTrustResult:(SecTrustResultType)trustResult
163 serverTrust:(base::ScopedCFTypeRef<SecTrustRef>)trust
164 host:(NSString*)host
165 completionHandler:(PolicyDecisionHandlerHolder)handler;
166
167 // Verifies the given |cert| for the given |host| using |net::CertVerifier| and
168 // calls |completionHandler| on completion. This method can be called on any
169 // thread. |completionHandler| cannot be null and will be called asynchronously
170 // on IO thread or will never be called if IO task can't start or complete.
171 - (void)verifyCert:(const scoped_refptr<net::X509Certificate>&)cert
172 forHost:(NSString*)host
173 completionHandler:(void (^)(net::CertVerifyResult))completionHandler;
174 39
175 // Verifies the given |trust| using SecTrustRef API. |completionHandler| cannot 40 // Verifies the given |trust| using SecTrustRef API. |completionHandler| cannot
176 // be null and will be either called asynchronously on Worker thread or will 41 // be null and will be called on UI thread or never be called if the worker task
177 // never be called if the worker task can't start or complete. 42 // can't start or complete. Must be called on UI thread.
178 - (void)verifyTrust:(base::ScopedCFTypeRef<SecTrustRef>)trust 43 - (void)verifyTrust:(base::ScopedCFTypeRef<SecTrustRef>)trust
179 completionHandler:(void (^)(SecTrustResultType))completionHandler; 44 completionHandler:(void (^)(SecTrustResultType))completionHandler;
180 45
181 // Returns cert accept policy for the given SecTrust result. |trustResult| must 46 // Returns cert accept policy for the given SecTrust result. |trustResult| must
182 // not be for a valid cert. Must be called on IO thread. 47 // not be for a valid cert. Must be called on IO thread.
Ryan Sleevi 2016/08/12 00:16:23 Just trying to make sure I understand why the thre
Eugene But (OOO till 7-30) 2016/08/12 16:16:39 Correct CertPolicyCache is constructed and owned b
183 - (web::CertAcceptPolicy) 48 - (web::CertAcceptPolicy)
184 loadPolicyForRejectedTrustResult:(SecTrustResultType)trustResult 49 loadPolicyForRejectedTrustResult:(SecTrustResultType)trustResult
185 certVerifierResult:(net::CertVerifyResult)certVerifierResult 50 certStatus:(net::CertStatus)certStatus
186 serverTrust:(SecTrustRef)trust 51 serverTrust:(SecTrustRef)trust
187 host:(NSString*)host; 52 host:(NSString*)host;
188 53
189 @end 54 @end
190 55
191 @implementation CRWCertVerificationController 56 @implementation CRWCertVerificationController
192 57
193 #pragma mark - Superclass
194
195 - (void)dealloc {
196 DCHECK(!_certVerifier);
197 [super dealloc];
198 }
199
200 #pragma mark - Public 58 #pragma mark - Public
201 59
202 - (instancetype)init { 60 - (instancetype)init {
203 NOTREACHED(); 61 NOTREACHED();
204 return nil; 62 return nil;
205 } 63 }
206 64
207 - (instancetype)initWithBrowserState:(web::BrowserState*)browserState { 65 - (instancetype)initWithBrowserState:(web::BrowserState*)browserState {
208 DCHECK(browserState); 66 DCHECK(browserState);
209 DCHECK_CURRENTLY_ON(web::WebThread::UI); 67 DCHECK_CURRENTLY_ON(web::WebThread::UI);
210 self = [super init]; 68 self = [super init];
211 if (self) { 69 if (self) {
212 _contextGetter = browserState->GetRequestContext();
213 DCHECK(_contextGetter);
214 _certPolicyCache = 70 _certPolicyCache =
215 web::BrowserState::GetCertificatePolicyCache(browserState); 71 web::BrowserState::GetCertificatePolicyCache(browserState);
216
217 [self createCertVerifier];
218 } 72 }
219 return self; 73 return self;
220 } 74 }
221 75
222 - (void)decideLoadPolicyForTrust:(base::ScopedCFTypeRef<SecTrustRef>)trust 76 - (void)decideLoadPolicyForTrust:(base::ScopedCFTypeRef<SecTrustRef>)trust
223 host:(NSString*)host 77 host:(NSString*)host
224 completionHandler:(web::PolicyDecisionHandler)completionHandler { 78 completionHandler:(web::PolicyDecisionHandler)completionHandler {
225 DCHECK_CURRENTLY_ON(web::WebThread::UI); 79 DCHECK_CURRENTLY_ON(web::WebThread::UI);
226 // completionHandler of |verifyCert:forHost:completionHandler:| is called on 80 DCHECK(completionHandler);
227 // IO thread and then bounces back to UI thread. As a result all objects 81
228 // captured by completionHandler may be released on either UI or IO thread.
229 // Since |completionHandler| can potentially capture multiple thread unsafe
230 // objects (like Web Controller) |completionHandler| itself should never be
231 // released on background thread and |BlockHolder| ensures that.
232 __block PolicyDecisionHandlerHolder handlerHolder(
233 new BlockHolder<web::PolicyDecisionHandler>(
234 completionHandler, web::CERT_ACCEPT_POLICY_NON_RECOVERABLE_ERROR,
235 net::CertStatus()));
236 [self verifyTrust:trust 82 [self verifyTrust:trust
237 completionHandler:^(SecTrustResultType trustResult) { 83 completionHandler:^(SecTrustResultType trustResult) {
84 DCHECK_CURRENTLY_ON(web::WebThread::UI);
238 if (web::GetSecurityStyleFromTrustResult(trustResult) == 85 if (web::GetSecurityStyleFromTrustResult(trustResult) ==
239 web::SECURITY_STYLE_AUTHENTICATED) { 86 web::SECURITY_STYLE_AUTHENTICATED) {
Ryan Sleevi 2016/08/12 00:16:23 COMMENT: It's unclear to me why we indirect throug
Eugene But (OOO till 7-30) 2016/08/12 16:16:39 |web::GetSecurityStyleFromTrustResult(trustResult)
Ryan Sleevi 2016/08/12 16:28:33 Right, I understood that part perfectly :)
Eugene But (OOO till 7-30) 2016/08/12 22:33:02 Yes, that's my reasoning.
240 [self decideLoadPolicyForAcceptedTrustResult:trustResult 87 completionHandler(web::CERT_ACCEPT_POLICY_ALLOW, net::CertStatus());
241 serverTrust:trust 88 return;
242 host:host
243 completionHandler:handlerHolder];
244 } else {
245 [self decideLoadPolicyForRejectedTrustResult:trustResult
246 serverTrust:trust
247 host:host
248 completionHandler:handlerHolder];
249 } 89 }
90 [self decideLoadPolicyForRejectedTrustResult:trustResult
91 serverTrust:trust
92 host:host
93 completionHandler:completionHandler];
250 }]; 94 }];
251 } 95 }
252 96
253 - (void)querySSLStatusForTrust:(base::ScopedCFTypeRef<SecTrustRef>)trust 97 - (void)querySSLStatusForTrust:(base::ScopedCFTypeRef<SecTrustRef>)trust
254 host:(NSString*)host 98 host:(NSString*)host
255 completionHandler:(web::StatusQueryHandler)completionHandler { 99 completionHandler:(web::StatusQueryHandler)completionHandler {
256 DCHECK_CURRENTLY_ON(web::WebThread::UI); 100 DCHECK_CURRENTLY_ON(web::WebThread::UI);
101 DCHECK(completionHandler);
257 102
258 // The completion handlers of |verifyCert:forHost:completionHandler:| and
259 // |verifyTrust:completionHandler:| will be deallocated on background thread.
260 // |completionHandler| itself should never be released on background thread
261 // and |BlockHolder| ensures that.
262 __block scoped_refptr<BlockHolder<web::StatusQueryHandler>> handlerHolder(
263 new BlockHolder<web::StatusQueryHandler>(
264 completionHandler, web::SECURITY_STYLE_UNKNOWN, net::CertStatus()));
265 [self verifyTrust:trust 103 [self verifyTrust:trust
266 completionHandler:^(SecTrustResultType trustResult) { 104 completionHandler:^(SecTrustResultType trustResult) {
267 web::SecurityStyle securityStyle = 105 web::SecurityStyle securityStyle =
268 web::GetSecurityStyleFromTrustResult(trustResult); 106 web::GetSecurityStyleFromTrustResult(trustResult);
269 if (securityStyle == web::SECURITY_STYLE_AUTHENTICATED) {
270 // SecTrust API considers this cert as valid.
271 dispatch_async(dispatch_get_main_queue(), ^{
272 handlerHolder->call(securityStyle, net::CertStatus());
273 });
274 return;
275 }
276 107
277 // Retrieve the net::CertStatus for invalid certificates to determine 108 net::CertStatus certStatus =
278 // the rejection reason, it is possible that rejection reason could not 109 [self certStatusFromTrustResult:trustResult serverTrust:trust];
279 // be determined and |cert_status| will be empty. 110 completionHandler(securityStyle, certStatus);
280 scoped_refptr<net::X509Certificate> cert(
281 web::CreateCertFromTrust(trust));
282 [self verifyCert:cert
283 forHost:host
284 completionHandler:^(net::CertVerifyResult certVerifierResult) {
285 dispatch_async(dispatch_get_main_queue(), ^{
286 handlerHolder->call(securityStyle,
287 certVerifierResult.cert_status);
288 });
289 }];
290 }]; 111 }];
291 } 112 }
292 113
293 - (void)allowCert:(scoped_refptr<net::X509Certificate>)cert 114 - (void)allowCert:(scoped_refptr<net::X509Certificate>)cert
294 forHost:(NSString*)host 115 forHost:(NSString*)host
295 status:(net::CertStatus)status { 116 status:(net::CertStatus)status {
296 DCHECK_CURRENTLY_ON(web::WebThread::UI); 117 DCHECK_CURRENTLY_ON(web::WebThread::UI);
297 // Store user decisions with the leaf cert, ignoring any intermediates. 118 // Store user decisions with the leaf cert, ignoring any intermediates.
298 // This is because WKWebView returns the verified certificate chain in 119 // This is because WKWebView returns the verified certificate chain in
299 // |webView:didReceiveAuthenticationChallenge:completionHandler:|, 120 // |webView:didReceiveAuthenticationChallenge:completionHandler:|,
300 // but the server-supplied chain in 121 // but the server-supplied chain in
301 // |webView:didFailProvisionalNavigation:withError:|. 122 // |webView:didFailProvisionalNavigation:withError:|.
302 if (!cert->GetIntermediateCertificates().empty()) { 123 if (!cert->GetIntermediateCertificates().empty()) {
303 cert = net::X509Certificate::CreateFromHandle( 124 cert = net::X509Certificate::CreateFromHandle(
304 cert->os_cert_handle(), net::X509Certificate::OSCertHandles()); 125 cert->os_cert_handle(), net::X509Certificate::OSCertHandles());
305 } 126 }
306 DCHECK(cert->GetIntermediateCertificates().empty()); 127 DCHECK(cert->GetIntermediateCertificates().empty());
307 web::WebThread::PostTask(web::WebThread::IO, FROM_HERE, base::BindBlock(^{ 128 web::WebThread::PostTask(web::WebThread::IO, FROM_HERE, base::BindBlock(^{
308 _certPolicyCache->AllowCertForHost( 129 _certPolicyCache->AllowCertForHost(
309 cert.get(), base::SysNSStringToUTF8(host), status); 130 cert.get(), base::SysNSStringToUTF8(host), status);
310 })); 131 }));
311 } 132 }
312 133
313 - (void)shutDown {
314 DCHECK_CURRENTLY_ON(web::WebThread::UI);
315 web::WebThread::PostTask(web::WebThread::IO, FROM_HERE, base::BindBlock(^{
316 // This block captures |self| delaying its deallocation and causing dealloc
317 // to happen on either IO or UI thread (which is fine for this class).
318 _certVerifier.reset();
319 }));
320 }
321
322 #pragma mark - Private 134 #pragma mark - Private
323 135
324 - (int)certVerifyFlags { 136 - (net::CertStatus)certStatusFromTrustResult:(SecTrustResultType)trustResult
325 DCHECK(web::WebThread::CurrentlyOn(web::WebThread::IO)); 137 serverTrust:
326 DCHECK(_contextGetter); 138 (base::ScopedCFTypeRef<SecTrustRef>)trust {
327 // |net::URLRequestContextGetter| lifetime is expected to be at least the same 139 web::SecurityStyle securityStyle =
328 // or longer than |BrowserState| lifetime. 140 web::GetSecurityStyleFromTrustResult(trustResult);
329 net::URLRequestContext* context = _contextGetter->GetURLRequestContext(); 141 if (securityStyle == web::SECURITY_STYLE_AUTHENTICATED)
Ryan Sleevi 2016/08/12 00:16:22 It's a little weird that you check securityStyle h
Eugene But (OOO till 7-30) 2016/08/12 16:16:39 I didn't want to use |trustResult| here because if
Ryan Sleevi 2016/08/12 16:28:33 I guess I disagree (see the other comment :P) Ulti
Eugene But (OOO till 7-30) 2016/08/12 22:33:02 Done.
330 DCHECK(context); 142 return net::CertStatus();
331 net::SSLConfigService* SSLConfigService = context->ssl_config_service();
332 DCHECK(SSLConfigService);
333 net::SSLConfig config;
334 SSLConfigService->GetSSLConfig(&config);
335 return config.GetCertVerifyFlags();
336 }
337 143
338 - (BOOL)shouldReportCertVerifyAgreement { 144 if (trustResult == kSecTrustResultDeny)
339 // Cert verification is an expensive operation. Since extra verification will 145 return net::CERT_STATUS_AUTHORITY_INVALID;
Ryan Sleevi 2016/08/12 00:16:23 Having this short-circuit *seems* like it means we
Eugene But (OOO till 7-30) 2016/08/12 16:16:39 Yes, this is from CertVerifyProciOS https://cs.chr
Ryan Sleevi 2016/08/12 16:28:33 In general, I would strongly discourage extracting
Eugene But (OOO till 7-30) 2016/08/12 22:33:02 CVPIOS has a switch-case so I matched that.
340 // be used only for UMA reporting purposes, do that only for 1% of cases.
341 return base::RandGenerator(100) == 0;
342 }
343 146
344 - (void)reportUMAForCertVerifyAgreement:(CertVerifyAgreement)agreement { 147 net::CertStatus certStatus =
345 UMA_HISTOGRAM_ENUMERATION("Web.CertVerifyAgreement", agreement, 148 net::CertVerifyProcIOS::GetCertFailureStatusFromTrust(trust);
346 CERT_VERIFY_AGREEMENT_COUNT); 149 if (!net::IsCertStatusError(certStatus)) {
347 } 150 // |cert_status| must not be no-error if SecTrust API considers the
348 151 // cert as invalid. Otherwise there will be issues with errors
349 - (void)createCertVerifier { 152 // reporting and recovery.
350 web::WebThread::PostTask(web::WebThread::IO, FROM_HERE, base::BindBlock(^{ 153 certStatus = net::CERT_STATUS_INVALID;
351 net::URLRequestContext* context = _contextGetter->GetURLRequestContext(); 154 }
352 _certVerifier.reset(new web::CertVerifierBlockAdapter( 155 return certStatus;
353 context->cert_verifier(), context->net_log()));
354 }));
355 } 156 }
356 157
357 - (void) 158 - (void)
358 decideLoadPolicyForRejectedTrustResult:(SecTrustResultType)trustResult 159 decideLoadPolicyForRejectedTrustResult:(SecTrustResultType)trustResult
359 serverTrust:(base::ScopedCFTypeRef<SecTrustRef>)trust 160 serverTrust:(base::ScopedCFTypeRef<SecTrustRef>)trust
360 host:(NSString*)host 161 host:(NSString*)host
361 completionHandler:(PolicyDecisionHandlerHolder)handler { 162 completionHandler:(web::PolicyDecisionHandler)handler {
362 // SecTrust API considers this cert as invalid. Check the reason and 163 DCHECK_CURRENTLY_ON(web::WebThread::UI);
363 // whether or not user has decided to proceed with this bad cert. 164 DCHECK(handler);
364 scoped_refptr<net::X509Certificate> cert(web::CreateCertFromTrust(trust)); 165 web::WebThread::PostTask(
365 [self verifyCert:cert 166 web::WebThread::IO, FROM_HERE, base::BindBlock(^{
Ryan Sleevi 2016/08/12 00:16:22 Making sure I've grokked the reasoning for this:
Eugene But (OOO till 7-30) 2016/08/12 16:16:39 Correct. I added comments to the code.
366 forHost:host 167 net::CertStatus certStatus =
367 completionHandler:^(net::CertVerifyResult certVerifierResult) { 168 [self certStatusFromTrustResult:trustResult serverTrust:trust];
368 if (!net::IsCertStatusError(certVerifierResult.cert_status)) {
369 // |cert_status| must not be no-error if SecTrust API considers the
370 // cert as invalid. Otherwise there will be issues with errors
371 // reporting and recovery.
372 certVerifierResult.cert_status = net::CERT_STATUS_INVALID;
373 }
374 169
375 web::CertAcceptPolicy policy = 170 web::CertAcceptPolicy policy =
376 [self loadPolicyForRejectedTrustResult:trustResult 171 [self loadPolicyForRejectedTrustResult:trustResult
377 certVerifierResult:certVerifierResult 172 certStatus:certStatus
378 serverTrust:trust.get() 173 serverTrust:trust.get()
379 host:host]; 174 host:host];
380 175
381 dispatch_async(dispatch_get_main_queue(), ^{ 176 dispatch_async(dispatch_get_main_queue(), ^{
382 if ([self shouldReportCertVerifyAgreement]) { 177 handler(policy, certStatus);
383 CertVerifyAgreement agreement =
384 net::IsCertStatusError(certVerifierResult.cert_status)
385 ? CERT_VERIFY_AGREEMENT_REJECTED_BY_BOTH
386 : CERT_VERIFY_AGREEMENT_ACCEPTED_ONLY_BY_NSS;
387 [self reportUMAForCertVerifyAgreement:agreement];
388 }
389 handler->call(policy, certVerifierResult.cert_status);
390 }); 178 });
391 }]; 179 }));
392 }
393
394 - (void)
395 decideLoadPolicyForAcceptedTrustResult:(SecTrustResultType)trustResult
396 serverTrust:(base::ScopedCFTypeRef<SecTrustRef>)trust
397 host:(NSString*)host
398 completionHandler:(PolicyDecisionHandlerHolder)handler {
399 // SecTrust API considers this cert as valid.
400 dispatch_async(dispatch_get_main_queue(), ^{
401 handler->call(web::CERT_ACCEPT_POLICY_ALLOW, net::CertStatus());
402 });
403
404 if ([self shouldReportCertVerifyAgreement]) {
405 // Execute CertVerifier::Verify to collect CertVerifyAgreement UMA.
406 scoped_refptr<net::X509Certificate> cert(web::CreateCertFromTrust(trust));
407 [self verifyCert:cert
408 forHost:host
409 completionHandler:^(net::CertVerifyResult certVerifierResult) {
410 // SecTrust API accepted this cert and |PolicyDecisionHandler| has
411 // been called already. |CertVerifier| verification is executed only
412 // to collect CertVerifyAgreement UMA.
413 dispatch_async(dispatch_get_main_queue(), ^{
414 CertVerifyAgreement agreement =
415 net::IsCertStatusError(certVerifierResult.cert_status)
416 ? CERT_VERIFY_AGREEMENT_ACCEPTED_ONLY_BY_IOS
417 : CERT_VERIFY_AGREEMENT_ACCEPTED_BY_BOTH;
418 [self reportUMAForCertVerifyAgreement:agreement];
419 });
420 }];
421 }
422 }
423
424 - (void)verifyCert:(const scoped_refptr<net::X509Certificate>&)cert
425 forHost:(NSString*)host
426 completionHandler:(void (^)(net::CertVerifyResult))completionHandler {
427 DCHECK(completionHandler);
428 __block scoped_refptr<net::X509Certificate> blockCert = cert;
429 web::WebThread::PostTask(web::WebThread::IO, FROM_HERE, base::BindBlock(^{
430 // WeakNSObject does not work across different threads, hence this block
431 // retains self.
432 if (!_certVerifier) {
433 completionHandler(net::CertVerifyResult());
434 return;
435 }
436
437 web::CertVerifierBlockAdapter::Params params(std::move(blockCert),
438 base::SysNSStringToUTF8(host));
439 params.flags = self.certVerifyFlags;
440 // OCSP response is not provided by iOS API.
441 // CRLSets are not used, as the OS is used to make load/no-load
442 // decisions, not the CertVerifier.
443 _certVerifier->Verify(params, ^(net::CertVerifyResult result, int) {
444 completionHandler(result);
445 });
446 }));
447 } 180 }
448 181
449 - (void)verifyTrust:(base::ScopedCFTypeRef<SecTrustRef>)trust 182 - (void)verifyTrust:(base::ScopedCFTypeRef<SecTrustRef>)trust
450 completionHandler:(void (^)(SecTrustResultType))completionHandler { 183 completionHandler:(void (^)(SecTrustResultType))completionHandler {
184 DCHECK_CURRENTLY_ON(web::WebThread::UI);
451 DCHECK(completionHandler); 185 DCHECK(completionHandler);
452 // SecTrustEvaluate performs trust evaluation synchronously, possibly making 186 // SecTrustEvaluate performs trust evaluation synchronously, possibly making
453 // network requests. The UI thread should not be blocked by that operation. 187 // network requests. The UI thread should not be blocked by that operation.
454 base::WorkerPool::PostTask(FROM_HERE, base::BindBlock(^{ 188 base::WorkerPool::PostTask(FROM_HERE, base::BindBlock(^{
455 SecTrustResultType trustResult = kSecTrustResultInvalid; 189 SecTrustResultType trustResult = kSecTrustResultInvalid;
456 if (SecTrustEvaluate(trust.get(), &trustResult) != errSecSuccess) { 190 if (SecTrustEvaluate(trust.get(), &trustResult) != errSecSuccess) {
Ryan Sleevi 2016/08/12 00:16:23 Curious: Is there any reason we don't/didn't use S
Eugene But (OOO till 7-30) 2016/08/12 16:16:39 SecTrustEvaluateAsync takes GCD queue as param and
Ryan Sleevi 2016/08/12 16:28:33 That feels like a very strange response to me, con
Eugene But (OOO till 7-30) 2016/08/12 22:33:02 |dispatch_async(dispatch_get_main_queue()| is equi
457 trustResult = kSecTrustResultInvalid; 191 trustResult = kSecTrustResultInvalid;
458 } 192 }
459 completionHandler(trustResult); 193 dispatch_async(dispatch_get_main_queue(), ^{
194 completionHandler(trustResult);
195 });
460 }), false /* task_is_slow */); 196 }), false /* task_is_slow */);
461 } 197 }
462 198
463 - (web::CertAcceptPolicy) 199 - (web::CertAcceptPolicy)
464 loadPolicyForRejectedTrustResult:(SecTrustResultType)trustResult 200 loadPolicyForRejectedTrustResult:(SecTrustResultType)trustResult
465 certVerifierResult:(net::CertVerifyResult)certVerifierResult 201 certStatus:(net::CertStatus)certStatus
466 serverTrust:(SecTrustRef)trust 202 serverTrust:(SecTrustRef)trust
467 host:(NSString*)host { 203 host:(NSString*)host {
468 DCHECK_CURRENTLY_ON(web::WebThread::IO); 204 DCHECK_CURRENTLY_ON(web::WebThread::IO);
469 DCHECK_NE(web::SECURITY_STYLE_AUTHENTICATED, 205 DCHECK_NE(web::SECURITY_STYLE_AUTHENTICATED,
470 web::GetSecurityStyleFromTrustResult(trustResult)); 206 web::GetSecurityStyleFromTrustResult(trustResult));
471 207
472 if (trustResult != kSecTrustResultRecoverableTrustFailure || 208 if (trustResult != kSecTrustResultRecoverableTrustFailure ||
473 SecTrustGetCertificateCount(trust) == 0) { 209 SecTrustGetCertificateCount(trust) == 0) {
474 // Trust result is not recoverable or leaf cert is missing. 210 // Trust result is not recoverable or leaf cert is missing.
475 return web::CERT_ACCEPT_POLICY_NON_RECOVERABLE_ERROR; 211 return web::CERT_ACCEPT_POLICY_NON_RECOVERABLE_ERROR;
476 } 212 }
477 213
478 // Check if user has decided to proceed with this bad cert. 214 // Check if user has decided to proceed with this bad cert.
479 scoped_refptr<net::X509Certificate> leafCert = 215 scoped_refptr<net::X509Certificate> leafCert =
480 net::X509Certificate::CreateFromHandle( 216 net::X509Certificate::CreateFromHandle(
481 SecTrustGetCertificateAtIndex(trust, 0), 217 SecTrustGetCertificateAtIndex(trust, 0),
482 net::X509Certificate::OSCertHandles()); 218 net::X509Certificate::OSCertHandles());
483 219
484 web::CertPolicy::Judgment judgment = _certPolicyCache->QueryPolicy( 220 web::CertPolicy::Judgment judgment = _certPolicyCache->QueryPolicy(
485 leafCert.get(), base::SysNSStringToUTF8(host), 221 leafCert.get(), base::SysNSStringToUTF8(host), certStatus);
486 certVerifierResult.cert_status);
487 222
488 return (judgment == web::CertPolicy::ALLOWED) 223 return (judgment == web::CertPolicy::ALLOWED)
489 ? web::CERT_ACCEPT_POLICY_RECOVERABLE_ERROR_ACCEPTED_BY_USER 224 ? web::CERT_ACCEPT_POLICY_RECOVERABLE_ERROR_ACCEPTED_BY_USER
490 : web::CERT_ACCEPT_POLICY_RECOVERABLE_ERROR_UNDECIDED_BY_USER; 225 : web::CERT_ACCEPT_POLICY_RECOVERABLE_ERROR_UNDECIDED_BY_USER;
491 } 226 }
492 227
493 @end 228 @end
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698