OLD | NEW |
1 // Copyright 2017 The Chromium Authors. All rights reserved. | 1 // Copyright 2017 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/safe_browsing/mojo_safe_browsing_impl.h" | 5 #include "chrome/browser/safe_browsing/mojo_safe_browsing_impl.h" |
6 | 6 |
7 #include <vector> | 7 #include <vector> |
8 | 8 |
9 #include "base/memory/ptr_util.h" | 9 #include "base/memory/ptr_util.h" |
10 #include "base/memory/weak_ptr.h" | 10 #include "chrome/browser/safe_browsing/safe_browsing_url_checker_impl.h" |
11 #include "base/timer/timer.h" | |
12 #include "chrome/browser/browser_process.h" | |
13 #include "chrome/browser/prerender/prerender_contents.h" | |
14 #include "chrome/browser/safe_browsing/ui_manager.h" | 11 #include "chrome/browser/safe_browsing/ui_manager.h" |
15 #include "components/safe_browsing_db/database_manager.h" | 12 #include "components/safe_browsing_db/database_manager.h" |
16 #include "content/public/browser/browser_thread.h" | 13 #include "content/public/browser/browser_thread.h" |
17 #include "content/public/browser/render_frame_host.h" | 14 #include "content/public/browser/render_frame_host.h" |
18 #include "content/public/browser/web_contents.h" | 15 #include "content/public/browser/web_contents.h" |
19 #include "content/public/common/resource_type.h" | 16 #include "content/public/common/resource_type.h" |
20 #include "mojo/public/cpp/bindings/strong_binding.h" | 17 #include "mojo/public/cpp/bindings/strong_binding.h" |
21 #include "net/base/load_flags.h" | 18 #include "net/base/load_flags.h" |
22 | 19 |
23 namespace safe_browsing { | 20 namespace safe_browsing { |
24 namespace { | 21 namespace { |
25 | 22 |
26 // TODO(yzshen): Share such value with safe_browsing::BaseResourceThrottle. | |
27 // Maximum time in milliseconds to wait for the SafeBrowsing service reputation | |
28 // check. After this amount of time the outstanding check will be aborted, and | |
29 // the resource will be treated as if it were safe. | |
30 const int kCheckUrlTimeoutMs = 5000; | |
31 | |
32 content::WebContents* GetWebContentsFromID(int render_process_id, | 23 content::WebContents* GetWebContentsFromID(int render_process_id, |
33 int render_frame_id) { | 24 int render_frame_id) { |
34 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); | 25 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
35 content::RenderFrameHost* render_frame_host = | 26 content::RenderFrameHost* render_frame_host = |
36 content::RenderFrameHost::FromID(render_process_id, render_frame_id); | 27 content::RenderFrameHost::FromID(render_process_id, render_frame_id); |
37 if (!render_frame_host) | 28 if (!render_frame_host) |
38 return nullptr; | 29 return nullptr; |
39 | 30 |
40 return content::WebContents::FromRenderFrameHost(render_frame_host); | 31 return content::WebContents::FromRenderFrameHost(render_frame_host); |
41 } | 32 } |
42 | 33 |
43 // TODO(yzshen): Handle the case where SafeBrowsing is not enabled, or | 34 // This class wraps a base::OnceCallback<void(bool)> and runs it on destruction, |
44 // !database_manager()->IsSupported(). | 35 // if it hasn't been run yet. |
45 // TODO(yzshen): Make sure it also works on Andorid. | 36 class BooleanCallbackWrapper { |
46 // TODO(yzshen): Do all the logging like what BaseResourceThrottle does. | |
47 class SafeBrowsingUrlCheckerImpl : public mojom::SafeBrowsingUrlChecker, | |
48 public SafeBrowsingDatabaseManager::Client { | |
49 public: | 37 public: |
50 SafeBrowsingUrlCheckerImpl( | 38 using BooleanCallback = base::OnceCallback<void(bool)>; |
51 int load_flags, | 39 |
52 content::ResourceType resource_type, | 40 explicit BooleanCallbackWrapper(BooleanCallback callback) |
53 scoped_refptr<SafeBrowsingDatabaseManager> database_manager, | 41 : callback_(std::move(callback)) {} |
54 scoped_refptr<SafeBrowsingUIManager> ui_manager, | 42 ~BooleanCallbackWrapper() { |
55 int render_process_id, | 43 if (callback_) |
56 int render_frame_id) | 44 Run(false); |
57 : load_flags_(load_flags), | |
58 resource_type_(resource_type), | |
59 render_process_id_(render_process_id), | |
60 render_frame_id_(render_frame_id), | |
61 database_manager_(std::move(database_manager)), | |
62 ui_manager_(std::move(ui_manager)), | |
63 weak_factory_(this) { | |
64 DCHECK_NE(resource_type, content::RESOURCE_TYPE_MAIN_FRAME); | |
65 DCHECK_NE(resource_type, content::RESOURCE_TYPE_SUB_FRAME); | |
66 } | 45 } |
67 | 46 |
68 ~SafeBrowsingUrlCheckerImpl() override { | 47 void Run(bool value) { std::move(callback_).Run(value); } |
69 DCHECK_CURRENTLY_ON(content::BrowserThread::IO); | |
70 | |
71 if (state_ == STATE_CHECKING_URL) | |
72 database_manager_->CancelCheck(this); | |
73 | |
74 for (size_t i = next_index_; i < callbacks_.size(); ++i) | |
75 std::move(callbacks_[i]).Run(false); | |
76 } | |
77 | |
78 // mojom::SafeBrowsingUrlChecker implementation. | |
79 void CheckUrl(const GURL& url, CheckUrlCallback callback) override { | |
80 DCHECK_CURRENTLY_ON(content::BrowserThread::IO); | |
81 | |
82 DVLOG(1) << "SafeBrowsingUrlCheckerImpl checks URL: " << url; | |
83 urls_.push_back(url); | |
84 callbacks_.push_back(std::move(callback)); | |
85 | |
86 ProcessUrls(); | |
87 } | |
88 | 48 |
89 private: | 49 private: |
90 // SafeBrowsingDatabaseManager::Client implementation: | 50 BooleanCallback callback_; |
91 void OnCheckBrowseUrlResult(const GURL& url, | |
92 SBThreatType threat_type, | |
93 const ThreatMetadata& metadata) override { | |
94 DCHECK_EQ(STATE_CHECKING_URL, state_); | |
95 DCHECK_LT(next_index_, urls_.size()); | |
96 DCHECK_EQ(urls_[next_index_], url); | |
97 | |
98 timer_.Stop(); | |
99 if (threat_type == SB_THREAT_TYPE_SAFE) { | |
100 state_ = STATE_NONE; | |
101 std::move(callbacks_[next_index_]).Run(true); | |
102 next_index_++; | |
103 ProcessUrls(); | |
104 return; | |
105 } | |
106 | |
107 if (load_flags_ & net::LOAD_PREFETCH) { | |
108 BlockAndProcessUrls(); | |
109 return; | |
110 } | |
111 | |
112 security_interstitials::UnsafeResource resource; | |
113 resource.url = url; | |
114 resource.original_url = urls_[0]; | |
115 if (urls_.size() > 1) | |
116 resource.redirect_urls = | |
117 std::vector<GURL>(urls_.begin() + 1, urls_.end()); | |
118 resource.is_subresource = true; | |
119 resource.is_subframe = false; | |
120 resource.threat_type = threat_type; | |
121 resource.threat_metadata = metadata; | |
122 resource.callback = | |
123 base::Bind(&SafeBrowsingUrlCheckerImpl::OnBlockingPageComplete, | |
124 weak_factory_.GetWeakPtr()); | |
125 resource.callback_thread = content::BrowserThread::GetTaskRunnerForThread( | |
126 content::BrowserThread::IO); | |
127 resource.web_contents_getter = | |
128 base::Bind(&GetWebContentsFromID, render_process_id_, render_frame_id_); | |
129 resource.threat_source = database_manager_->GetThreatSource(); | |
130 | |
131 state_ = STATE_DISPLAYING_BLOCKING_PAGE; | |
132 | |
133 content::BrowserThread::PostTask( | |
134 content::BrowserThread::UI, FROM_HERE, | |
135 base::Bind(&SafeBrowsingUrlCheckerImpl::StartDisplayingBlockingPage, | |
136 weak_factory_.GetWeakPtr(), ui_manager_, resource)); | |
137 } | |
138 | |
139 static void StartDisplayingBlockingPage( | |
140 const base::WeakPtr<SafeBrowsingUrlCheckerImpl>& checker, | |
141 scoped_refptr<BaseUIManager> ui_manager, | |
142 const security_interstitials::UnsafeResource& resource) { | |
143 content::WebContents* web_contents = resource.web_contents_getter.Run(); | |
144 if (web_contents) { | |
145 prerender::PrerenderContents* prerender_contents = | |
146 prerender::PrerenderContents::FromWebContents(web_contents); | |
147 if (prerender_contents) { | |
148 prerender_contents->Destroy(prerender::FINAL_STATUS_SAFE_BROWSING); | |
149 } else { | |
150 ui_manager->DisplayBlockingPage(resource); | |
151 return; | |
152 } | |
153 } | |
154 | |
155 // Tab is gone or it's being prerendered. | |
156 content::BrowserThread::PostTask( | |
157 content::BrowserThread::IO, FROM_HERE, | |
158 base::BindOnce(&SafeBrowsingUrlCheckerImpl::BlockAndProcessUrls, | |
159 checker)); | |
160 } | |
161 | |
162 void OnCheckUrlTimeout() { | |
163 database_manager_->CancelCheck(this); | |
164 | |
165 OnCheckBrowseUrlResult(urls_[next_index_], | |
166 safe_browsing::SB_THREAT_TYPE_SAFE, | |
167 ThreatMetadata()); | |
168 } | |
169 | |
170 void ProcessUrls() { | |
171 DCHECK_NE(STATE_BLOCKED, state_); | |
172 | |
173 if (state_ == STATE_CHECKING_URL || | |
174 state_ == STATE_DISPLAYING_BLOCKING_PAGE) { | |
175 return; | |
176 } | |
177 | |
178 while (next_index_ < urls_.size()) { | |
179 DCHECK_EQ(STATE_NONE, state_); | |
180 // TODO(yzshen): Consider moving CanCheckResourceType() to the renderer | |
181 // side. That would save some IPCs. It requires a method on the | |
182 // SafeBrowsing mojo interface to query all supported resource types. | |
183 if (!database_manager_->CanCheckResourceType(resource_type_) || | |
184 database_manager_->CheckBrowseUrl(urls_[next_index_], this)) { | |
185 std::move(callbacks_[next_index_]).Run(true); | |
186 next_index_++; | |
187 continue; | |
188 } | |
189 | |
190 state_ = STATE_CHECKING_URL; | |
191 // Start a timer to abort the check if it takes too long. | |
192 timer_.Start(FROM_HERE, | |
193 base::TimeDelta::FromMilliseconds(kCheckUrlTimeoutMs), this, | |
194 &SafeBrowsingUrlCheckerImpl::OnCheckUrlTimeout); | |
195 | |
196 break; | |
197 } | |
198 } | |
199 | |
200 void BlockAndProcessUrls() { | |
201 DVLOG(1) << "SafeBrowsingUrlCheckerImpl blocks URL: " << urls_[next_index_]; | |
202 state_ = STATE_BLOCKED; | |
203 | |
204 // If user decided to not proceed through a warning, mark all the remaining | |
205 // redirects as "bad". | |
206 for (; next_index_ < callbacks_.size(); ++next_index_) | |
207 std::move(callbacks_[next_index_]).Run(false); | |
208 } | |
209 | |
210 void OnBlockingPageComplete(bool proceed) { | |
211 DCHECK_EQ(STATE_DISPLAYING_BLOCKING_PAGE, state_); | |
212 | |
213 if (proceed) { | |
214 state_ = STATE_NONE; | |
215 std::move(callbacks_[next_index_]).Run(true); | |
216 next_index_++; | |
217 ProcessUrls(); | |
218 } else { | |
219 BlockAndProcessUrls(); | |
220 } | |
221 } | |
222 | |
223 enum State { | |
224 // Haven't started checking or checking is complete. | |
225 STATE_NONE, | |
226 // We have one outstanding URL-check. | |
227 STATE_CHECKING_URL, | |
228 // We're displaying a blocking page. | |
229 STATE_DISPLAYING_BLOCKING_PAGE, | |
230 // The blocking page has returned *not* to proceed. | |
231 STATE_BLOCKED | |
232 }; | |
233 | |
234 const int load_flags_; | |
235 const content::ResourceType resource_type_; | |
236 const int render_process_id_; | |
237 const int render_frame_id_; | |
238 scoped_refptr<SafeBrowsingDatabaseManager> database_manager_; | |
239 scoped_refptr<BaseUIManager> ui_manager_; | |
240 | |
241 // The redirect chain for this resource, including the original URL and | |
242 // subsequent redirect URLs. | |
243 std::vector<GURL> urls_; | |
244 // Callbacks corresponding to |urls_| to report check results. |urls_| and | |
245 // |callbacks_| are always of the same size. | |
246 std::vector<CheckUrlCallback> callbacks_; | |
247 // |urls_| before |next_index_| have been checked. If |next_index_| is smaller | |
248 // than the size of |urls_|, the URL at |next_index_| is being processed. | |
249 size_t next_index_ = 0; | |
250 | |
251 State state_ = STATE_NONE; | |
252 | |
253 // Timer to abort the SafeBrowsing check if it takes too long. | |
254 base::OneShotTimer timer_; | |
255 | |
256 base::WeakPtrFactory<SafeBrowsingUrlCheckerImpl> weak_factory_; | |
257 | |
258 DISALLOW_COPY_AND_ASSIGN(SafeBrowsingUrlCheckerImpl); | |
259 }; | 51 }; |
260 | 52 |
261 } // namespace | 53 } // namespace |
262 | 54 |
263 MojoSafeBrowsingImpl::MojoSafeBrowsingImpl( | 55 MojoSafeBrowsingImpl::MojoSafeBrowsingImpl( |
264 scoped_refptr<SafeBrowsingDatabaseManager> database_manager, | 56 scoped_refptr<SafeBrowsingDatabaseManager> database_manager, |
265 scoped_refptr<SafeBrowsingUIManager> ui_manager, | 57 scoped_refptr<SafeBrowsingUIManager> ui_manager, |
266 int render_process_id) | 58 int render_process_id) |
267 : database_manager_(std::move(database_manager)), | 59 : database_manager_(std::move(database_manager)), |
268 ui_manager_(std::move(ui_manager)), | 60 ui_manager_(std::move(ui_manager)), |
(...skipping 20 matching lines...) Expand all Loading... |
289 void MojoSafeBrowsingImpl::CreateCheckerAndCheck( | 81 void MojoSafeBrowsingImpl::CreateCheckerAndCheck( |
290 int32_t render_frame_id, | 82 int32_t render_frame_id, |
291 mojom::SafeBrowsingUrlCheckerRequest request, | 83 mojom::SafeBrowsingUrlCheckerRequest request, |
292 const GURL& url, | 84 const GURL& url, |
293 int32_t load_flags, | 85 int32_t load_flags, |
294 content::ResourceType resource_type, | 86 content::ResourceType resource_type, |
295 CreateCheckerAndCheckCallback callback) { | 87 CreateCheckerAndCheckCallback callback) { |
296 DCHECK_CURRENTLY_ON(content::BrowserThread::IO); | 88 DCHECK_CURRENTLY_ON(content::BrowserThread::IO); |
297 auto checker_impl = base::MakeUnique<SafeBrowsingUrlCheckerImpl>( | 89 auto checker_impl = base::MakeUnique<SafeBrowsingUrlCheckerImpl>( |
298 static_cast<int>(load_flags), resource_type, database_manager_, | 90 static_cast<int>(load_flags), resource_type, database_manager_, |
299 ui_manager_, render_process_id_, static_cast<int>(render_frame_id)); | 91 ui_manager_, |
300 checker_impl->CheckUrl(url, std::move(callback)); | 92 base::Bind(&GetWebContentsFromID, render_process_id_, |
| 93 static_cast<int>(render_frame_id))); |
| 94 |
| 95 checker_impl->CheckUrl( |
| 96 url, base::BindOnce( |
| 97 &BooleanCallbackWrapper::Run, |
| 98 base::Owned(new BooleanCallbackWrapper(std::move(callback))))); |
301 mojo::MakeStrongBinding(std::move(checker_impl), std::move(request)); | 99 mojo::MakeStrongBinding(std::move(checker_impl), std::move(request)); |
302 } | 100 } |
303 | 101 |
304 } // namespace safe_browsing | 102 } // namespace safe_browsing |
OLD | NEW |