 Chromium Code Reviews
 Chromium Code Reviews Issue 2900563002:
  Network service: Safe browsing check for sub-resources from renderer.  (Closed)
    
  
    Issue 2900563002:
  Network service: Safe browsing check for sub-resources from renderer.  (Closed) 
  | OLD | NEW | 
|---|---|
| (Empty) | |
| 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 | |
| 3 // found in the LICENSE file. | |
| 4 | |
| 5 #include "chrome/browser/safe_browsing/safe_browsing_impl.h" | |
| 6 | |
| 7 #include <vector> | |
| 8 | |
| 9 #include "base/memory/ptr_util.h" | |
| 10 #include "base/memory/weak_ptr.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" | |
| 15 #include "components/safe_browsing_db/database_manager.h" | |
| 16 #include "content/public/browser/browser_thread.h" | |
| 17 #include "content/public/browser/render_frame_host.h" | |
| 18 #include "content/public/browser/web_contents.h" | |
| 19 #include "content/public/common/resource_type.h" | |
| 20 #include "mojo/public/cpp/bindings/strong_binding.h" | |
| 21 #include "net/base/load_flags.h" | |
| 22 | |
| 23 namespace safe_browsing { | |
| 24 namespace { | |
| 25 | |
| 26 // TODO(yzshen): Share such value with safe_browsing::BaseResourceThrottle. | |
| 27 // Maximum time in milliseconds to wait for the safe browsing service to | |
| 28 // verify a URL. After this amount of time the outstanding check will be | |
| 29 // aborted, and the URL will be treated as if it were safe. | |
| 30 const int kCheckUrlTimeoutMs = 5000; | |
| 31 | |
| 32 content::WebContents* GetWebContentsFromID(int render_process_id, | |
| 33 int render_frame_id) { | |
| 34 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); | |
| 35 content::RenderFrameHost* render_frame_host = | |
| 36 content::RenderFrameHost::FromID(render_process_id, render_frame_id); | |
| 37 if (!render_frame_host) | |
| 38 return nullptr; | |
| 39 | |
| 40 return content::WebContents::FromRenderFrameHost(render_frame_host); | |
| 41 } | |
| 42 | |
| 43 // TODO(yzshen): Handle the case where safe browsing is not enabled, or | |
| 44 // !database_manager()->IsSupported(). | |
| 45 // TODO(yzshen): Make sure it also works on Andorid. | |
| 46 // TODO(yzshen): Do all the logging like what BaseResourceThrottle does. | |
| 47 class SafeBrowsingUrlCheckerImpl : public chrome::mojom::SafeBrowsingUrlChecker, | |
| 48 public SafeBrowsingDatabaseManager::Client { | |
| 49 public: | |
| 50 SafeBrowsingUrlCheckerImpl( | |
| 51 int load_flags, | |
| 52 content::ResourceType resource_type, | |
| 53 scoped_refptr<SafeBrowsingDatabaseManager> database_manager, | |
| 54 scoped_refptr<SafeBrowsingUIManager> ui_manager, | |
| 55 int render_process_id, | |
| 56 int render_frame_id) | |
| 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 } | |
| 67 | |
| 68 ~SafeBrowsingUrlCheckerImpl() override { | |
| 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 callbacks_[i].Run(false); | |
| 76 } | |
| 77 | |
| 78 // chrome::mojom::SafeBrowsingUrlChecker implementation. | |
| 79 void CheckUrl(const GURL& url, const 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(callback); | |
| 85 | |
| 86 ProcessUrls(); | |
| 87 } | |
| 88 | |
| 89 private: | |
| 90 // SafeBrowsingDatabaseManager::Client implementation: | |
| 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 callbacks_[next_index_].Run(true); | |
| 102 callbacks_[next_index_].Reset(); | |
| 
kinuko
2017/05/29 13:36:50
Can we use OnceCallback here?
 
yzshen1
2017/05/31 00:30:01
Done. I split this mojom into a separate GN target
 | |
| 103 next_index_++; | |
| 104 ProcessUrls(); | |
| 105 return; | |
| 106 } | |
| 107 | |
| 108 if (load_flags_ & net::LOAD_PREFETCH) { | |
| 109 BlockAndProcessUrls(); | |
| 110 return; | |
| 111 } | |
| 112 | |
| 113 security_interstitials::UnsafeResource resource; | |
| 114 resource.url = url; | |
| 115 resource.original_url = urls_[0]; | |
| 116 if (urls_.size() > 1) | |
| 117 resource.redirect_urls = | |
| 118 std::vector<GURL>(urls_.begin() + 1, urls_.end()); | |
| 119 resource.is_subresource = true; | |
| 120 resource.is_subframe = false; | |
| 121 resource.threat_type = threat_type; | |
| 122 resource.threat_metadata = metadata; | |
| 123 resource.callback = | |
| 124 base::Bind(&SafeBrowsingUrlCheckerImpl::OnBlockingPageComplete, | |
| 125 weak_factory_.GetWeakPtr()); | |
| 126 resource.callback_thread = content::BrowserThread::GetTaskRunnerForThread( | |
| 127 content::BrowserThread::IO); | |
| 128 resource.web_contents_getter = | |
| 129 base::Bind(&GetWebContentsFromID, render_process_id_, render_frame_id_); | |
| 130 resource.threat_source = database_manager_->GetThreatSource(); | |
| 131 | |
| 132 state_ = STATE_DISPLAYING_BLOCKING_PAGE; | |
| 133 | |
| 134 content::BrowserThread::PostTask( | |
| 135 content::BrowserThread::UI, FROM_HERE, | |
| 136 base::Bind(&SafeBrowsingUrlCheckerImpl::StartDisplayingBlockingPage, | |
| 137 weak_factory_.GetWeakPtr(), ui_manager_, resource)); | |
| 138 } | |
| 139 | |
| 140 static void StartDisplayingBlockingPage( | |
| 141 const base::WeakPtr<SafeBrowsingUrlCheckerImpl>& checker, | |
| 142 scoped_refptr<BaseUIManager> ui_manager, | |
| 143 const security_interstitials::UnsafeResource& resource) { | |
| 144 content::WebContents* web_contents = resource.web_contents_getter.Run(); | |
| 145 if (web_contents) { | |
| 146 prerender::PrerenderContents* prerender_contents = | |
| 147 prerender::PrerenderContents::FromWebContents(web_contents); | |
| 148 if (prerender_contents) { | |
| 149 prerender_contents->Destroy(prerender::FINAL_STATUS_SAFE_BROWSING); | |
| 150 } else { | |
| 151 ui_manager->DisplayBlockingPage(resource); | |
| 152 return; | |
| 153 } | |
| 154 } | |
| 155 | |
| 156 // Tab is gone or it's being prerendered. | |
| 157 content::BrowserThread::PostTask( | |
| 158 content::BrowserThread::IO, FROM_HERE, | |
| 159 base::BindOnce(&SafeBrowsingUrlCheckerImpl::BlockAndProcessUrls, | |
| 160 checker)); | |
| 161 } | |
| 162 | |
| 163 void OnCheckUrlTimeout() { | |
| 164 database_manager_->CancelCheck(this); | |
| 165 | |
| 166 OnCheckBrowseUrlResult(urls_[next_index_], | |
| 167 safe_browsing::SB_THREAT_TYPE_SAFE, | |
| 168 ThreatMetadata()); | |
| 169 } | |
| 170 | |
| 171 void ProcessUrls() { | |
| 172 if (state_ == STATE_CHECKING_URL || | |
| 173 state_ == STATE_DISPLAYING_BLOCKING_PAGE) { | |
| 174 return; | |
| 175 } | |
| 176 | |
| 177 while (next_index_ < urls_.size()) { | |
| 178 if (state_ == STATE_BLOCKED) { | |
| 179 callbacks_[next_index_].Run(false); | |
| 180 callbacks_[next_index_].Reset(); | |
| 181 next_index_++; | |
| 182 continue; | |
| 183 } | |
| 184 | |
| 185 DCHECK_EQ(STATE_NONE, state_); | |
| 186 // TODO(yzshen): Consider moving CanCheckResourceType() to the renderer | |
| 187 // side. That would save some IPCs. | |
| 188 if (!database_manager_->CanCheckResourceType(resource_type_) || | |
| 189 database_manager_->CheckBrowseUrl(urls_[next_index_], this)) { | |
| 190 callbacks_[next_index_].Run(true); | |
| 191 callbacks_[next_index_].Reset(); | |
| 192 next_index_++; | |
| 193 continue; | |
| 194 } | |
| 195 | |
| 196 state_ = STATE_CHECKING_URL; | |
| 197 // Start a timer to abort the check if it takes too long. | |
| 198 timer_.Start(FROM_HERE, | |
| 199 base::TimeDelta::FromMilliseconds(kCheckUrlTimeoutMs), this, | |
| 200 &SafeBrowsingUrlCheckerImpl::OnCheckUrlTimeout); | |
| 201 | |
| 202 break; | |
| 203 } | |
| 204 } | |
| 205 | |
| 206 void BlockAndProcessUrls() { | |
| 207 DVLOG(1) << "SafeBrowsingUrlCheckerImpl blocks URL: " << urls_[next_index_]; | |
| 208 state_ = STATE_BLOCKED; | |
| 209 ProcessUrls(); | |
| 210 } | |
| 211 | |
| 212 void OnBlockingPageComplete(bool proceed) { | |
| 213 DCHECK_EQ(STATE_DISPLAYING_BLOCKING_PAGE, state_); | |
| 214 | |
| 215 if (proceed) { | |
| 216 state_ = STATE_NONE; | |
| 217 callbacks_[next_index_].Run(true); | |
| 218 callbacks_[next_index_].Reset(); | |
| 219 next_index_++; | |
| 220 ProcessUrls(); | |
| 221 } else { | |
| 222 BlockAndProcessUrls(); | |
| 223 } | |
| 224 } | |
| 225 | |
| 226 enum State { | |
| 227 // Haven't started checking or checking is complete. | |
| 228 STATE_NONE, | |
| 229 // We have one outstanding URL-check. | |
| 230 STATE_CHECKING_URL, | |
| 231 // We're displaying a blocking page. | |
| 232 STATE_DISPLAYING_BLOCKING_PAGE, | |
| 233 // The blocking page has returned *not* to proceed. | |
| 234 STATE_BLOCKED | |
| 235 }; | |
| 236 | |
| 237 const int load_flags_; | |
| 238 const content::ResourceType resource_type_; | |
| 239 const int render_process_id_; | |
| 240 const int render_frame_id_; | |
| 241 scoped_refptr<SafeBrowsingDatabaseManager> database_manager_; | |
| 242 scoped_refptr<BaseUIManager> ui_manager_; | |
| 243 | |
| 244 // The redirect chain for this resource, including the original URL and | |
| 245 // subsequent redirect URLs. | |
| 246 std::vector<GURL> urls_; | |
| 247 std::vector<CheckUrlCallback> callbacks_; | |
| 248 size_t next_index_ = 0; | |
| 249 | |
| 250 State state_ = STATE_NONE; | |
| 251 | |
| 252 // Timer to abort the safe browsing check if it takes too long. | |
| 253 base::OneShotTimer timer_; | |
| 254 | |
| 255 base::WeakPtrFactory<SafeBrowsingUrlCheckerImpl> weak_factory_; | |
| 256 | |
| 257 DISALLOW_COPY_AND_ASSIGN(SafeBrowsingUrlCheckerImpl); | |
| 258 }; | |
| 259 | |
| 260 } // namespace | |
| 261 | |
| 262 SafeBrowsingImpl::SafeBrowsingImpl( | |
| 263 scoped_refptr<SafeBrowsingDatabaseManager> database_manager, | |
| 264 scoped_refptr<SafeBrowsingUIManager> ui_manager, | |
| 265 int render_process_id) | |
| 266 : database_manager_(std::move(database_manager)), | |
| 267 ui_manager_(std::move(ui_manager)), | |
| 268 render_process_id_(render_process_id) {} | |
| 269 | |
| 270 SafeBrowsingImpl::~SafeBrowsingImpl() { | |
| 271 DCHECK_CURRENTLY_ON(content::BrowserThread::IO); | |
| 272 } | |
| 273 | |
| 274 // static | |
| 275 void SafeBrowsingImpl::Create( | |
| 276 scoped_refptr<SafeBrowsingDatabaseManager> database_manager, | |
| 277 scoped_refptr<SafeBrowsingUIManager> ui_manager, | |
| 278 int render_process_id, | |
| 279 const service_manager::BindSourceInfo& source_info, | |
| 280 chrome::mojom::SafeBrowsingRequest request) { | |
| 281 DCHECK_CURRENTLY_ON(content::BrowserThread::IO); | |
| 282 mojo::MakeStrongBinding(base::MakeUnique<SafeBrowsingImpl>( | |
| 283 std::move(database_manager), | |
| 284 std::move(ui_manager), render_process_id), | |
| 285 std::move(request)); | |
| 286 } | |
| 287 | |
| 288 void SafeBrowsingImpl::CreateCheckerAndCheck( | |
| 289 int32_t render_frame_id, | |
| 290 chrome::mojom::SafeBrowsingUrlCheckerRequest request, | |
| 291 const GURL& url, | |
| 292 int32_t load_flags, | |
| 293 content::ResourceType resource_type, | |
| 294 const CreateCheckerAndCheckCallback& callback) { | |
| 295 DCHECK_CURRENTLY_ON(content::BrowserThread::IO); | |
| 296 auto checker_impl = base::MakeUnique<SafeBrowsingUrlCheckerImpl>( | |
| 297 load_flags, resource_type, database_manager_, ui_manager_, | |
| 298 render_process_id_, render_frame_id); | |
| 299 checker_impl->CheckUrl(url, callback); | |
| 300 mojo::MakeStrongBinding(std::move(checker_impl), std::move(request)); | |
| 301 } | |
| 302 | |
| 303 } // namespace safe_browsing | |
| OLD | NEW |