blob: c6475bc4aa83c0e5825d45a2105ba4fb06a9a4f3 [file] [log] [blame]
[email protected]1519d852011-10-13 01:32:411// Copyright (c) 2011 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/download_protection_service.h"
6
7#include "base/bind.h"
8#include "base/memory/scoped_ptr.h"
9#include "base/metrics/histogram.h"
10#include "base/stl_util.h"
11#include "chrome/browser/safe_browsing/safe_browsing_service.h"
12#include "chrome/common/net/http_return.h"
13#include "chrome/common/safe_browsing/csd.pb.h"
14#include "content/browser/browser_thread.h"
15#include "net/base/load_flags.h"
16#include "net/url_request/url_request_context_getter.h"
17#include "net/url_request/url_request_status.h"
18
19namespace safe_browsing {
20
21const char DownloadProtectionService::kDownloadRequestUrl[] =
22 "https://sb-ssl.google.com/safebrowsing/clientreport/download";
23
24DownloadProtectionService::DownloadInfo::DownloadInfo()
25 : total_bytes(0), user_initiated(false) {}
26
27DownloadProtectionService::DownloadInfo::~DownloadInfo() {}
28
29DownloadProtectionService::DownloadProtectionService(
30 SafeBrowsingService* sb_service,
31 net::URLRequestContextGetter* request_context_getter)
32 : sb_service_(sb_service),
33 request_context_getter_(request_context_getter),
34 enabled_(false) {}
35
36DownloadProtectionService::~DownloadProtectionService() {
37 STLDeleteContainerPairFirstPointers(download_requests_.begin(),
38 download_requests_.end());
39 download_requests_.clear();
40}
41
42void DownloadProtectionService::SetEnabled(bool enabled) {
43 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
44 BrowserThread::PostTask(
45 BrowserThread::IO,
46 FROM_HERE,
47 base::Bind(&DownloadProtectionService::SetEnabledOnIOThread,
48 this, enabled));
49}
50
51void DownloadProtectionService::SetEnabledOnIOThread(bool enabled) {
52 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
53 if (enabled == enabled_) {
54 return;
55 }
56 enabled_ = enabled;
57 if (!enabled_) {
58 for (std::map<const URLFetcher*, CheckDownloadCallback>::iterator it =
59 download_requests_.begin();
60 it != download_requests_.end(); ++it) {
61 it->second.Run(SAFE);
62 }
63 STLDeleteContainerPairFirstPointers(download_requests_.begin(),
64 download_requests_.end());
65 download_requests_.clear();
66 }
67}
68
69void DownloadProtectionService::OnURLFetchComplete(
70 const URLFetcher* source,
71 const GURL& url,
72 const net::URLRequestStatus& status,
73 int response_code,
74 const net::ResponseCookies& cookies,
75 const std::string& data) {
76 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
77 scoped_ptr<const URLFetcher> s(source); // will delete the URLFetcher object.
78 if (download_requests_.find(source) != download_requests_.end()) {
79 CheckDownloadCallback callback = download_requests_[source];
80 download_requests_.erase(source);
81 if (!enabled_) {
82 // SafeBrowsing got disabled. We can't do anything. Note: the request
83 // object will be deleted.
84 RecordStats(REASON_SB_DISABLED);
85 return;
86 }
87 DownloadCheckResultReason reason = REASON_MAX;
88 if (status.is_success() &&
89 RC_REQUEST_OK == response_code &&
90 data.size() > 0) {
91 // For now no matter what we'll always say the download is safe.
92 // TODO(noelutz): Parse the response body to see exactly what's going on.
93 reason = REASON_INVALID_RESPONSE_PROTO;
94 } else {
95 reason = REASON_SERVER_PING_FAILED;
96 }
97 BrowserThread::PostTask(
98 BrowserThread::UI,
99 FROM_HERE,
100 base::Bind(&DownloadProtectionService::EndCheckClientDownload,
101 this, SAFE, reason, callback));
102 } else {
103 NOTREACHED();
104 }
105}
106
107bool DownloadProtectionService::CheckClientDownload(
108 const DownloadInfo& info,
109 const CheckDownloadCallback& callback) {
110 // TODO(noelutz): implement some cache to make sure we don't issue the same
111 // request over and over again if a user downloads the same binary multiple
112 // times.
113 if (info.download_url_chain.empty()) {
114 RecordStats(REASON_INVALID_URL);
115 return true;
116 }
117 const GURL& final_url = info.download_url_chain.back();
118 if (!final_url.is_valid() || final_url.is_empty() ||
119 !final_url.SchemeIs("http")) {
120 RecordStats(REASON_INVALID_URL);
121 return true; // For now we only support HTTP download URLs.
122 }
123 // TODO(noelutz): DownloadInfo should also contain the IP address of every
124 // URL in the redirect chain. We also should check whether the download URL
125 // is hosted on the internal network.
126 BrowserThread::PostTask(
127 BrowserThread::IO,
128 FROM_HERE,
129 base::Bind(&DownloadProtectionService::StartCheckClientDownload,
130 this, info, callback));
131 return false;
132}
133
134void DownloadProtectionService::StartCheckClientDownload(
135 const DownloadInfo& info,
136 const CheckDownloadCallback& callback) {
137 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
138 if (!enabled_ || !sb_service_.get()) {
139 // This is a hard fail. We won't even call the callback in this case.
140 RecordStats(REASON_SB_DISABLED);
141 return;
142 }
143 DownloadCheckResultReason reason = REASON_MAX;
144 for (size_t i = 0; i < info.download_url_chain.size(); ++i) {
145 if (sb_service_->MatchDownloadWhitelistUrl(info.download_url_chain[i])) {
146 reason = REASON_WHITELISTED_URL;
147 break;
148 }
149 }
150 if (sb_service_->MatchDownloadWhitelistUrl(info.referrer_url)) {
151 reason = REASON_WHITELISTED_REFERRER;
152 }
153 // TODO(noelutz): check signature and CA against whitelist.
154
155 ClientDownloadRequest request;
156 request.set_url(info.download_url_chain.back().spec());
157 request.mutable_digests()->set_sha256(info.sha256_hash);
158 request.set_length(info.total_bytes);
159 for (size_t i = 0; i < info.download_url_chain.size(); ++i) {
160 ClientDownloadRequest::Resource* resource = request.add_resources();
161 resource->set_url(info.download_url_chain[i].spec());
162 if (i == info.download_url_chain.size() - 1) {
163 // The last URL in the chain is the download URL.
164 resource->set_type(ClientDownloadRequest::DOWNLOAD_URL);
165 resource->set_referrer(info.referrer_url.spec());
166 } else {
167 resource->set_type(ClientDownloadRequest::DOWNLOAD_REDIRECT);
168 }
169 // TODO(noelutz): fill out the remote IP addresses.
170 }
171 request.set_user_initiated(info.user_initiated);
172 std::string request_data;
173 if (!request.SerializeToString(&request_data)) {
174 reason = REASON_INVALID_REQUEST_PROTO;
175 }
176
177 if (reason != REASON_MAX) {
178 // We stop here because the download is considered safe.
179 BrowserThread::PostTask(
180 BrowserThread::UI,
181 FROM_HERE,
182 base::Bind(&DownloadProtectionService::EndCheckClientDownload,
183 this, SAFE, reason, callback));
184 return;
185 }
186
187 URLFetcher* fetcher = URLFetcher::Create(0 /* ID used for testing */,
188 GURL(kDownloadRequestUrl),
189 URLFetcher::POST,
190 this);
191 download_requests_[fetcher] = callback;
192 fetcher->set_load_flags(net::LOAD_DISABLE_CACHE);
193 fetcher->set_request_context(request_context_getter_.get());
194 fetcher->set_upload_data("application/octet-stream", request_data);
195 fetcher->Start();
196}
197
198void DownloadProtectionService::EndCheckClientDownload(
199 DownloadCheckResult result,
200 DownloadCheckResultReason reason,
201 const CheckDownloadCallback& callback) {
202 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
203 RecordStats(reason);
204 callback.Run(result);
205}
206
207void DownloadProtectionService::RecordStats(DownloadCheckResultReason reason) {
208 UMA_HISTOGRAM_ENUMERATION("SBClientDownload.CheckDownloadStats",
209 reason,
210 REASON_MAX);
211}
212} // namespace safe_browsing