OLD | NEW |
---|---|
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 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 #include "chrome/browser/renderer_host/safe_browsing_resource_throttle.h" | 5 #include "chrome/browser/renderer_host/safe_browsing_resource_throttle.h" |
6 | 6 |
7 #include "base/logging.h" | 7 #include "base/logging.h" |
8 #include "chrome/browser/browser_process.h" | 8 #include "chrome/browser/browser_process.h" |
9 #include "chrome/browser/prerender/prerender_contents.h" | 9 #include "chrome/browser/prerender/prerender_contents.h" |
10 #include "chrome/browser/safe_browsing/safe_browsing_service.h" | 10 #include "chrome/browser/safe_browsing/safe_browsing_service.h" |
(...skipping 10 matching lines...) Expand all Loading... | |
21 // verify a URL. After this amount of time the outstanding check will be | 21 // verify a URL. After this amount of time the outstanding check will be |
22 // aborted, and the URL will be treated as if it were safe. | 22 // aborted, and the URL will be treated as if it were safe. |
23 static const int kCheckUrlTimeoutMs = 5000; | 23 static const int kCheckUrlTimeoutMs = 5000; |
24 | 24 |
25 // TODO(eroman): Downgrade these CHECK()s to DCHECKs once there is more | 25 // TODO(eroman): Downgrade these CHECK()s to DCHECKs once there is more |
26 // unit test coverage. | 26 // unit test coverage. |
27 | 27 |
28 SafeBrowsingResourceThrottle::SafeBrowsingResourceThrottle( | 28 SafeBrowsingResourceThrottle::SafeBrowsingResourceThrottle( |
29 const net::URLRequest* request, | 29 const net::URLRequest* request, |
30 content::ResourceType resource_type, | 30 content::ResourceType resource_type, |
31 SafeBrowsingService* safe_browsing) | 31 SafeBrowsingService* safe_browsing, |
32 : state_(STATE_NONE), | 32 bool defer_at_start) |
33 : defer_at_start_(defer_at_start), | |
34 state_(STATE_NONE), | |
33 defer_state_(DEFERRED_NONE), | 35 defer_state_(DEFERRED_NONE), |
34 threat_type_(SB_THREAT_TYPE_SAFE), | 36 threat_type_(SB_THREAT_TYPE_SAFE), |
35 database_manager_(safe_browsing->database_manager()), | 37 database_manager_(safe_browsing->database_manager()), |
36 ui_manager_(safe_browsing->ui_manager()), | 38 ui_manager_(safe_browsing->ui_manager()), |
37 request_(request), | 39 request_(request), |
38 is_subresource_(resource_type != content::RESOURCE_TYPE_MAIN_FRAME), | 40 is_subresource_(resource_type != content::RESOURCE_TYPE_MAIN_FRAME), |
39 is_subframe_(resource_type == content::RESOURCE_TYPE_SUB_FRAME) { | 41 is_subframe_(resource_type == content::RESOURCE_TYPE_SUB_FRAME) { |
40 } | 42 } |
41 | 43 |
42 SafeBrowsingResourceThrottle::~SafeBrowsingResourceThrottle() { | 44 SafeBrowsingResourceThrottle::~SafeBrowsingResourceThrottle() { |
43 if (state_ == STATE_CHECKING_URL) | 45 if (state_ == STATE_CHECKING_URL) |
44 database_manager_->CancelCheck(this); | 46 database_manager_->CancelCheck(this); |
45 } | 47 } |
46 | 48 |
47 void SafeBrowsingResourceThrottle::WillStartRequest(bool* defer) { | 49 void SafeBrowsingResourceThrottle::WillStartRequest(bool* defer) { |
48 // We need to check the new URL before starting the request. | 50 // We need to check the new URL before starting the request. |
49 if (CheckUrl(request_->url())) | 51 if (CheckUrl(request_->url())) |
50 return; | 52 return; |
51 | 53 |
54 if (!defer_at_start_) | |
55 return; | |
56 | |
52 // If the URL couldn't be verified synchronously, defer starting the | 57 // If the URL couldn't be verified synchronously, defer starting the |
53 // request until the check has completed. | 58 // request until the check has completed. |
54 defer_state_ = DEFERRED_START; | 59 defer_state_ = DEFERRED_START; |
60 defer_start_time_ = base::TimeTicks::Now(); | |
55 *defer = true; | 61 *defer = true; |
56 } | 62 } |
57 | 63 |
64 void SafeBrowsingResourceThrottle::WillProcessResponse(bool* defer) { | |
65 CHECK_EQ(defer_state_, DEFERRED_NONE); | |
66 if (defer_at_start_) | |
67 return; | |
68 | |
69 if (state_ == STATE_CHECKING_URL || | |
70 state_ == STATE_DISPLAYING_BLOCKING_PAGE) { | |
71 defer_state_ = DEFERRED_PROCESSING; | |
72 defer_start_time_ = base::TimeTicks::Now(); | |
73 *defer = true; | |
74 } | |
75 } | |
76 | |
58 void SafeBrowsingResourceThrottle::WillRedirectRequest( | 77 void SafeBrowsingResourceThrottle::WillRedirectRequest( |
59 const net::RedirectInfo& redirect_info, | 78 const net::RedirectInfo& redirect_info, |
60 bool* defer) { | 79 bool* defer) { |
61 CHECK(state_ == STATE_NONE); | 80 CHECK_EQ(defer_state_, DEFERRED_NONE); |
62 CHECK(defer_state_ == DEFERRED_NONE); | |
63 | 81 |
64 // Save the redirect urls for possible malware detail reporting later. | 82 // Save the redirect urls for possible malware detail reporting later. |
65 redirect_urls_.push_back(redirect_info.new_url); | 83 redirect_urls_.push_back(redirect_info.new_url); |
mattm
2015/07/29 19:39:58
Doing it here means that if there is an in-progres
Nathan Parker
2015/07/30 18:42:52
I switched to do it only when the check is done.
mattm
2015/07/30 19:50:15
It should be fine to do it before the check, if it
Nathan Parker
2015/07/30 19:58:19
Done.
| |
66 | 84 |
67 // We need to check the new URL before following the redirect. | 85 // Prev check completed and was safe. |
68 if (CheckUrl(redirect_info.new_url)) | 86 if (state_ == STATE_NONE) { |
69 return; | 87 // We need to check the new URL before following the redirect. |
88 if (CheckUrl(redirect_info.new_url)) | |
89 return; | |
90 defer_state_ = DEFERRED_REDIRECT; | |
91 } else { | |
92 CHECK(state_ == STATE_CHECKING_URL || | |
93 state_ == STATE_DISPLAYING_BLOCKING_PAGE); | |
94 // We can't check this new URL until we have finished checking | |
95 // the prev one, or resumed from the blocking page. | |
96 unchecked_redirect_url_ = redirect_info.new_url; | |
97 defer_state_ = DEFERRED_UNCHECKED_REDIRECT; | |
98 } | |
70 | 99 |
71 // If the URL couldn't be verified synchronously, defer following the | 100 defer_start_time_ = base::TimeTicks::Now(); |
72 // redirect until the SafeBrowsing check is complete. Store the redirect | |
73 // context so we can pass it on to other handlers once we have completed | |
74 // our check. | |
75 defer_state_ = DEFERRED_REDIRECT; | |
76 *defer = true; | 101 *defer = true; |
77 } | 102 } |
78 | 103 |
79 const char* SafeBrowsingResourceThrottle::GetNameForLogging() const { | 104 const char* SafeBrowsingResourceThrottle::GetNameForLogging() const { |
80 return "SafeBrowsingResourceThrottle"; | 105 return "SafeBrowsingResourceThrottle"; |
81 } | 106 } |
82 | 107 |
83 // SafeBrowsingService::Client implementation, called on the IO thread once | 108 // SafeBrowsingService::Client implementation, called on the IO thread once |
84 // the URL has been classified. | 109 // the URL has been classified. |
85 void SafeBrowsingResourceThrottle::OnCheckBrowseUrlResult( | 110 void SafeBrowsingResourceThrottle::OnCheckBrowseUrlResult( |
86 const GURL& url, | 111 const GURL& url, |
87 SBThreatType threat_type, | 112 SBThreatType threat_type, |
88 const std::string& metadata) { | 113 const std::string& metadata) { |
89 CHECK(state_ == STATE_CHECKING_URL); | 114 CHECK_EQ(state_, STATE_CHECKING_URL); |
90 CHECK(defer_state_ != DEFERRED_NONE); | 115 CHECK_EQ(url, url_being_checked_); |
91 CHECK(url == url_being_checked_) << "Was expecting: " << url_being_checked_ | |
92 << " but got: " << url; | |
93 | 116 |
94 timer_.Stop(); // Cancel the timeout timer. | 117 timer_.Stop(); // Cancel the timeout timer. |
95 threat_type_ = threat_type; | 118 threat_type_ = threat_type; |
96 state_ = STATE_NONE; | 119 state_ = STATE_NONE; |
97 | 120 |
98 if (threat_type == SB_THREAT_TYPE_SAFE) { | 121 if (threat_type == SB_THREAT_TYPE_SAFE) { |
99 // Log how much time the safe browsing check cost us. | 122 if (defer_state_ != DEFERRED_NONE) { |
100 ui_manager_->LogPauseDelay(base::TimeTicks::Now() - url_check_start_time_); | 123 // Log how much time the safe browsing check cost us. |
101 | 124 ui_manager_->LogPauseDelay(base::TimeTicks::Now() - defer_start_time_); |
102 // Continue the request. | 125 ResumeRequest(); |
103 ResumeRequest(); | 126 } else { |
127 ui_manager_->LogPauseDelay(base::TimeDelta()); | |
128 } | |
104 return; | 129 return; |
105 } | 130 } |
106 | 131 |
107 if (request_->load_flags() & net::LOAD_PREFETCH) { | 132 if (request_->load_flags() & net::LOAD_PREFETCH) { |
108 // Don't prefetch resources that fail safe browsing, disallow | 133 // Don't prefetch resources that fail safe browsing, disallow them. |
109 // them. | |
110 controller()->Cancel(); | 134 controller()->Cancel(); |
111 return; | 135 return; |
112 } | 136 } |
113 | 137 |
114 const content::ResourceRequestInfo* info = | 138 const content::ResourceRequestInfo* info = |
115 content::ResourceRequestInfo::ForRequest(request_); | 139 content::ResourceRequestInfo::ForRequest(request_); |
116 | 140 |
117 SafeBrowsingUIManager::UnsafeResource resource; | 141 SafeBrowsingUIManager::UnsafeResource resource; |
118 resource.url = url; | 142 resource.url = url; |
119 resource.original_url = request_->original_url(); | 143 resource.original_url = request_->original_url(); |
(...skipping 13 matching lines...) Expand all Loading... | |
133 content::BrowserThread::UI, | 157 content::BrowserThread::UI, |
134 FROM_HERE, | 158 FROM_HERE, |
135 base::Bind(&SafeBrowsingResourceThrottle::StartDisplayingBlockingPage, | 159 base::Bind(&SafeBrowsingResourceThrottle::StartDisplayingBlockingPage, |
136 AsWeakPtr(), ui_manager_, resource)); | 160 AsWeakPtr(), ui_manager_, resource)); |
137 } | 161 } |
138 | 162 |
139 void SafeBrowsingResourceThrottle::StartDisplayingBlockingPage( | 163 void SafeBrowsingResourceThrottle::StartDisplayingBlockingPage( |
140 const base::WeakPtr<SafeBrowsingResourceThrottle>& throttle, | 164 const base::WeakPtr<SafeBrowsingResourceThrottle>& throttle, |
141 scoped_refptr<SafeBrowsingUIManager> ui_manager, | 165 scoped_refptr<SafeBrowsingUIManager> ui_manager, |
142 const SafeBrowsingUIManager::UnsafeResource& resource) { | 166 const SafeBrowsingUIManager::UnsafeResource& resource) { |
143 bool should_show_blocking_page = true; | |
144 | |
145 content::RenderViewHost* rvh = content::RenderViewHost::FromID( | 167 content::RenderViewHost* rvh = content::RenderViewHost::FromID( |
146 resource.render_process_host_id, resource.render_view_id); | 168 resource.render_process_host_id, resource.render_view_id); |
147 if (rvh) { | 169 if (rvh) { |
148 content::WebContents* web_contents = | 170 content::WebContents* web_contents = |
149 content::WebContents::FromRenderViewHost(rvh); | 171 content::WebContents::FromRenderViewHost(rvh); |
150 prerender::PrerenderContents* prerender_contents = | 172 prerender::PrerenderContents* prerender_contents = |
151 prerender::PrerenderContents::FromWebContents(web_contents); | 173 prerender::PrerenderContents::FromWebContents(web_contents); |
174 | |
152 if (prerender_contents) { | 175 if (prerender_contents) { |
153 prerender_contents->Destroy(prerender::FINAL_STATUS_SAFE_BROWSING); | 176 prerender_contents->Destroy(prerender::FINAL_STATUS_SAFE_BROWSING); |
154 should_show_blocking_page = false; | 177 } else { |
155 } | |
156 | |
157 if (should_show_blocking_page) { | |
158 ui_manager->DisplayBlockingPage(resource); | 178 ui_manager->DisplayBlockingPage(resource); |
159 return; | 179 return; |
160 } | 180 } |
161 } | 181 } |
162 | 182 |
163 // Tab is gone or it's being prerendered. | 183 // Tab is gone or it's being prerendered. |
164 content::BrowserThread::PostTask( | 184 content::BrowserThread::PostTask( |
165 content::BrowserThread::IO, | 185 content::BrowserThread::IO, |
166 FROM_HERE, | 186 FROM_HERE, |
167 base::Bind(&SafeBrowsingResourceThrottle::Cancel, throttle)); | 187 base::Bind(&SafeBrowsingResourceThrottle::Cancel, throttle)); |
168 } | 188 } |
169 | 189 |
170 void SafeBrowsingResourceThrottle::Cancel() { | 190 void SafeBrowsingResourceThrottle::Cancel() { |
171 controller()->Cancel(); | 191 controller()->Cancel(); |
172 } | 192 } |
173 | 193 |
174 // SafeBrowsingService::UrlCheckCallback implementation, called on the IO | 194 // SafeBrowsingService::UrlCheckCallback implementation, called on the IO |
175 // thread when the user has decided to proceed with the current request, or | 195 // thread when the user has decided to proceed with the current request, or |
176 // go back. | 196 // go back. |
177 void SafeBrowsingResourceThrottle::OnBlockingPageComplete(bool proceed) { | 197 void SafeBrowsingResourceThrottle::OnBlockingPageComplete(bool proceed) { |
178 CHECK(state_ == STATE_DISPLAYING_BLOCKING_PAGE); | 198 CHECK_EQ(state_, STATE_DISPLAYING_BLOCKING_PAGE); |
179 state_ = STATE_NONE; | 199 state_ = STATE_NONE; |
180 | 200 |
181 if (proceed) { | 201 if (proceed) { |
182 threat_type_ = SB_THREAT_TYPE_SAFE; | 202 threat_type_ = SB_THREAT_TYPE_SAFE; |
183 ResumeRequest(); | 203 if (defer_state_ != DEFERRED_NONE) { |
204 ResumeRequest(); | |
205 } | |
184 } else { | 206 } else { |
185 controller()->Cancel(); | 207 controller()->Cancel(); |
186 } | 208 } |
187 } | 209 } |
188 | 210 |
189 bool SafeBrowsingResourceThrottle::CheckUrl(const GURL& url) { | 211 bool SafeBrowsingResourceThrottle::CheckUrl(const GURL& url) { |
190 CHECK(state_ == STATE_NONE); | 212 CHECK_EQ(state_, STATE_NONE); |
191 bool succeeded_synchronously = database_manager_->CheckBrowseUrl(url, this); | 213 bool succeeded_synchronously = database_manager_->CheckBrowseUrl(url, this); |
192 if (succeeded_synchronously) { | 214 if (succeeded_synchronously) { |
193 threat_type_ = SB_THREAT_TYPE_SAFE; | 215 threat_type_ = SB_THREAT_TYPE_SAFE; |
194 ui_manager_->LogPauseDelay(base::TimeDelta()); // No delay. | 216 ui_manager_->LogPauseDelay(base::TimeDelta()); // No delay. |
195 return true; | 217 return true; |
196 } | 218 } |
197 | 219 |
198 state_ = STATE_CHECKING_URL; | 220 state_ = STATE_CHECKING_URL; |
199 url_being_checked_ = url; | 221 url_being_checked_ = url; |
200 | 222 |
201 // Record the start time of the check. | |
202 url_check_start_time_ = base::TimeTicks::Now(); | |
203 | |
204 // Start a timer to abort the check if it takes too long. | 223 // Start a timer to abort the check if it takes too long. |
224 // TODO(nparker): Set this only when we defer, based on remaining time, | |
225 // so we don't cancel earlier than necessary. | |
205 timer_.Start(FROM_HERE, | 226 timer_.Start(FROM_HERE, |
206 base::TimeDelta::FromMilliseconds(kCheckUrlTimeoutMs), | 227 base::TimeDelta::FromMilliseconds(kCheckUrlTimeoutMs), |
207 this, &SafeBrowsingResourceThrottle::OnCheckUrlTimeout); | 228 this, &SafeBrowsingResourceThrottle::OnCheckUrlTimeout); |
208 | 229 |
209 return false; | 230 return false; |
210 } | 231 } |
211 | 232 |
212 void SafeBrowsingResourceThrottle::OnCheckUrlTimeout() { | 233 void SafeBrowsingResourceThrottle::OnCheckUrlTimeout() { |
213 CHECK(state_ == STATE_CHECKING_URL); | 234 CHECK_EQ(state_, STATE_CHECKING_URL); |
214 CHECK(defer_state_ != DEFERRED_NONE); | |
215 | 235 |
216 database_manager_->CancelCheck(this); | 236 database_manager_->CancelCheck(this); |
217 OnCheckBrowseUrlResult( | 237 OnCheckBrowseUrlResult( |
218 url_being_checked_, SB_THREAT_TYPE_SAFE, std::string()); | 238 url_being_checked_, SB_THREAT_TYPE_SAFE, std::string()); |
219 } | 239 } |
220 | 240 |
221 void SafeBrowsingResourceThrottle::ResumeRequest() { | 241 void SafeBrowsingResourceThrottle::ResumeRequest() { |
222 CHECK(state_ == STATE_NONE); | 242 CHECK_EQ(state_, STATE_NONE); |
223 CHECK(defer_state_ != DEFERRED_NONE); | 243 CHECK_NE(defer_state_, DEFERRED_NONE); |
224 | 244 |
225 defer_state_ = DEFERRED_NONE; | 245 if (defer_state_ == DEFERRED_UNCHECKED_REDIRECT && |
226 controller()->Resume(); | 246 !CheckUrl(unchecked_redirect_url_)) { |
247 defer_state_ = DEFERRED_REDIRECT; | |
248 // We're now waiting for the unchecked_redirect_url_. | |
249 } else { | |
250 defer_state_ = DEFERRED_NONE; | |
251 controller()->Resume(); | |
252 } | |
227 } | 253 } |
OLD | NEW |