OLD | NEW |
---|---|
1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. | 1 // 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 | 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/download_protection_service.h" | 5 #include "chrome/browser/safe_browsing/download_protection_service.h" |
6 | 6 |
7 #include "base/bind.h" | 7 #include "base/bind.h" |
8 #include "base/memory/scoped_ptr.h" | 8 #include "base/memory/scoped_ptr.h" |
9 #include "base/metrics/histogram.h" | 9 #include "base/metrics/histogram.h" |
10 #include "base/stl_util.h" | 10 #include "base/stl_util.h" |
11 #include "base/string_util.h" | |
11 #include "chrome/browser/safe_browsing/safe_browsing_service.h" | 12 #include "chrome/browser/safe_browsing/safe_browsing_service.h" |
13 #include "chrome/browser/safe_browsing/signature_util.h" | |
12 #include "chrome/common/net/http_return.h" | 14 #include "chrome/common/net/http_return.h" |
13 #include "chrome/common/safe_browsing/csd.pb.h" | 15 #include "chrome/common/safe_browsing/csd.pb.h" |
14 #include "content/browser/browser_thread.h" | 16 #include "content/browser/browser_thread.h" |
15 #include "net/base/load_flags.h" | 17 #include "net/base/load_flags.h" |
16 #include "net/url_request/url_request_context_getter.h" | 18 #include "net/url_request/url_request_context_getter.h" |
17 #include "net/url_request/url_request_status.h" | 19 #include "net/url_request/url_request_status.h" |
18 | 20 |
19 namespace safe_browsing { | 21 namespace safe_browsing { |
20 | 22 |
21 const char DownloadProtectionService::kDownloadRequestUrl[] = | 23 const char DownloadProtectionService::kDownloadRequestUrl[] = |
22 "https://sb-ssl.google.com/safebrowsing/clientreport/download"; | 24 "https://sb-ssl.google.com/safebrowsing/clientreport/download"; |
23 | 25 |
26 namespace { | |
27 bool IsBinaryFile(const FilePath& file) { | |
28 return (file.MatchesExtension(FILE_PATH_LITERAL(".exe")) || | |
29 file.MatchesExtension(FILE_PATH_LITERAL(".cab")) || | |
30 file.MatchesExtension(FILE_PATH_LITERAL(".msi"))); | |
31 } | |
32 } // namespace | |
33 | |
24 DownloadProtectionService::DownloadInfo::DownloadInfo() | 34 DownloadProtectionService::DownloadInfo::DownloadInfo() |
25 : total_bytes(0), user_initiated(false) {} | 35 : total_bytes(0), user_initiated(false) {} |
26 | 36 |
27 DownloadProtectionService::DownloadInfo::~DownloadInfo() {} | 37 DownloadProtectionService::DownloadInfo::~DownloadInfo() {} |
28 | 38 |
29 DownloadProtectionService::DownloadProtectionService( | 39 DownloadProtectionService::DownloadProtectionService( |
30 SafeBrowsingService* sb_service, | 40 SafeBrowsingService* sb_service, |
31 net::URLRequestContextGetter* request_context_getter) | 41 net::URLRequestContextGetter* request_context_getter) |
32 : sb_service_(sb_service), | 42 : sb_service_(sb_service), |
33 request_context_getter_(request_context_getter), | 43 request_context_getter_(request_context_getter), |
34 enabled_(false) {} | 44 enabled_(false) {} |
35 | 45 |
36 DownloadProtectionService::~DownloadProtectionService() { | 46 DownloadProtectionService::~DownloadProtectionService() { |
47 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | |
37 STLDeleteContainerPairFirstPointers(download_requests_.begin(), | 48 STLDeleteContainerPairFirstPointers(download_requests_.begin(), |
38 download_requests_.end()); | 49 download_requests_.end()); |
39 download_requests_.clear(); | 50 download_requests_.clear(); |
40 } | 51 } |
41 | 52 |
42 void DownloadProtectionService::SetEnabled(bool enabled) { | 53 void DownloadProtectionService::SetEnabled(bool enabled) { |
43 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | 54 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
44 BrowserThread::PostTask( | 55 BrowserThread::PostTask( |
45 BrowserThread::IO, | 56 BrowserThread::IO, |
46 FROM_HERE, | 57 FROM_HERE, |
47 base::Bind(&DownloadProtectionService::SetEnabledOnIOThread, | 58 base::Bind(&DownloadProtectionService::SetEnabledOnIOThread, |
48 this, enabled)); | 59 base::Unretained(this), enabled)); |
noelutz
2011/10/22 00:41:46
I just want to make sure I follow the code here.
| |
49 } | 60 } |
50 | 61 |
51 void DownloadProtectionService::SetEnabledOnIOThread(bool enabled) { | 62 void DownloadProtectionService::SetEnabledOnIOThread(bool enabled) { |
52 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | 63 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
53 if (enabled == enabled_) { | 64 if (enabled == enabled_) { |
54 return; | 65 return; |
55 } | 66 } |
56 enabled_ = enabled; | 67 enabled_ = enabled; |
57 if (!enabled_) { | 68 if (!enabled_) { |
58 for (std::map<const URLFetcher*, CheckDownloadCallback>::iterator it = | 69 for (std::map<const URLFetcher*, CheckDownloadCallback>::iterator it = |
(...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
91 // For now no matter what we'll always say the download is safe. | 102 // 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. | 103 // TODO(noelutz): Parse the response body to see exactly what's going on. |
93 reason = REASON_INVALID_RESPONSE_PROTO; | 104 reason = REASON_INVALID_RESPONSE_PROTO; |
94 } else { | 105 } else { |
95 reason = REASON_SERVER_PING_FAILED; | 106 reason = REASON_SERVER_PING_FAILED; |
96 } | 107 } |
97 BrowserThread::PostTask( | 108 BrowserThread::PostTask( |
98 BrowserThread::UI, | 109 BrowserThread::UI, |
99 FROM_HERE, | 110 FROM_HERE, |
100 base::Bind(&DownloadProtectionService::EndCheckClientDownload, | 111 base::Bind(&DownloadProtectionService::EndCheckClientDownload, |
101 this, SAFE, reason, callback)); | 112 base::Unretained(this), SAFE, reason, callback)); |
102 } else { | 113 } else { |
103 NOTREACHED(); | 114 NOTREACHED(); |
104 } | 115 } |
105 } | 116 } |
106 | 117 |
107 bool DownloadProtectionService::CheckClientDownload( | 118 bool DownloadProtectionService::CheckClientDownload( |
108 const DownloadInfo& info, | 119 const DownloadInfo& info, |
109 const CheckDownloadCallback& callback) { | 120 const CheckDownloadCallback& callback) { |
110 // TODO(noelutz): implement some cache to make sure we don't issue the same | 121 // 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 | 122 // request over and over again if a user downloads the same binary multiple |
112 // times. | 123 // times. |
113 if (info.download_url_chain.empty()) { | 124 if (info.download_url_chain.empty()) { |
114 RecordStats(REASON_INVALID_URL); | 125 RecordStats(REASON_INVALID_URL); |
115 return true; | 126 return true; |
116 } | 127 } |
117 const GURL& final_url = info.download_url_chain.back(); | 128 const GURL& final_url = info.download_url_chain.back(); |
118 if (!final_url.is_valid() || final_url.is_empty() || | 129 if (!final_url.is_valid() || final_url.is_empty() || |
119 !final_url.SchemeIs("http")) { | 130 !final_url.SchemeIs("http")) { |
120 RecordStats(REASON_INVALID_URL); | 131 RecordStats(REASON_INVALID_URL); |
121 return true; // For now we only support HTTP download URLs. | 132 return true; // For now we only support HTTP download URLs. |
122 } | 133 } |
134 | |
135 if (!IsBinaryFile(info.local_file)) { | |
136 RecordStats(REASON_NOT_BINARY_FILE); | |
137 return true; | |
138 } | |
139 | |
140 // Compute features from the file contents. Note that we record histograms | |
141 // based on the result, so this runs regardless of whether the pingbacks are | |
142 // enabled. Since we do blocking I/O, this happens on the file thread. | |
143 BrowserThread::PostTask( | |
144 BrowserThread::FILE, | |
145 FROM_HERE, | |
146 base::Bind(&DownloadProtectionService::ExtractFileFeatures, | |
147 base::Unretained(this), info, enabled_, callback)); | |
noelutz
2011/10/22 00:41:46
I think there is a race condition here. You acces
| |
148 return false; | |
149 } | |
150 | |
151 void DownloadProtectionService::ExtractFileFeatures( | |
152 const DownloadInfo& info, | |
153 bool pingback_enabled, | |
154 const CheckDownloadCallback& callback) { | |
155 bool is_signed; | |
156 if (safe_browsing::signature_util::IsSigned(info.local_file)) { | |
157 VLOG(2) << "Downloaded a signed binary: " << info.local_file.value(); | |
158 is_signed = true; | |
159 } else { | |
160 VLOG(2) << "Downloaded an unsigned binary: " << info.local_file.value(); | |
161 is_signed = false; | |
162 } | |
163 UMA_HISTOGRAM_BOOLEAN("SBClientDownload.SignedBinaryDownload", is_signed); | |
164 | |
123 // TODO(noelutz): DownloadInfo should also contain the IP address of every | 165 // 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 | 166 // URL in the redirect chain. We also should check whether the download URL |
125 // is hosted on the internal network. | 167 // is hosted on the internal network. |
126 BrowserThread::PostTask( | 168 BrowserThread::PostTask( |
127 BrowserThread::IO, | 169 BrowserThread::IO, |
128 FROM_HERE, | 170 FROM_HERE, |
129 base::Bind(&DownloadProtectionService::StartCheckClientDownload, | 171 base::Bind(&DownloadProtectionService::StartCheckClientDownload, |
130 this, info, callback)); | 172 base::Unretained(this), info, pingback_enabled, callback)); |
131 return false; | |
132 } | 173 } |
133 | 174 |
134 void DownloadProtectionService::StartCheckClientDownload( | 175 void DownloadProtectionService::StartCheckClientDownload( |
135 const DownloadInfo& info, | 176 const DownloadInfo& info, |
177 bool pingback_enabled, | |
136 const CheckDownloadCallback& callback) { | 178 const CheckDownloadCallback& callback) { |
137 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | 179 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
138 if (!enabled_ || !sb_service_.get()) { | 180 DownloadCheckResultReason reason = REASON_MAX; |
139 // This is a hard fail. We won't even call the callback in this case. | 181 if (!enabled_ || !sb_service_) { |
140 RecordStats(REASON_SB_DISABLED); | 182 reason = REASON_SB_DISABLED; |
183 RecordStats(reason); | |
184 BrowserThread::PostTask( | |
185 BrowserThread::UI, | |
186 FROM_HERE, | |
187 base::Bind(&DownloadProtectionService::EndCheckClientDownload, | |
188 base::Unretained(this), SAFE, reason, callback)); | |
noelutz
2011/10/22 00:41:46
Will this always work? What if the destructor is
| |
141 return; | 189 return; |
142 } | 190 } |
143 DownloadCheckResultReason reason = REASON_MAX; | |
144 for (size_t i = 0; i < info.download_url_chain.size(); ++i) { | 191 for (size_t i = 0; i < info.download_url_chain.size(); ++i) { |
145 if (sb_service_->MatchDownloadWhitelistUrl(info.download_url_chain[i])) { | 192 if (sb_service_->MatchDownloadWhitelistUrl(info.download_url_chain[i])) { |
146 reason = REASON_WHITELISTED_URL; | 193 reason = REASON_WHITELISTED_URL; |
147 break; | 194 break; |
148 } | 195 } |
149 } | 196 } |
150 if (sb_service_->MatchDownloadWhitelistUrl(info.referrer_url)) { | 197 if (sb_service_->MatchDownloadWhitelistUrl(info.referrer_url)) { |
151 reason = REASON_WHITELISTED_REFERRER; | 198 reason = REASON_WHITELISTED_REFERRER; |
152 } | 199 } |
153 // TODO(noelutz): check signature and CA against whitelist. | 200 // TODO(noelutz): check signature and CA against whitelist. |
(...skipping 19 matching lines...) Expand all Loading... | |
173 if (!request.SerializeToString(&request_data)) { | 220 if (!request.SerializeToString(&request_data)) { |
174 reason = REASON_INVALID_REQUEST_PROTO; | 221 reason = REASON_INVALID_REQUEST_PROTO; |
175 } | 222 } |
176 | 223 |
177 if (reason != REASON_MAX) { | 224 if (reason != REASON_MAX) { |
178 // We stop here because the download is considered safe. | 225 // We stop here because the download is considered safe. |
179 BrowserThread::PostTask( | 226 BrowserThread::PostTask( |
180 BrowserThread::UI, | 227 BrowserThread::UI, |
181 FROM_HERE, | 228 FROM_HERE, |
182 base::Bind(&DownloadProtectionService::EndCheckClientDownload, | 229 base::Bind(&DownloadProtectionService::EndCheckClientDownload, |
183 this, SAFE, reason, callback)); | 230 base::Unretained(this), SAFE, reason, callback)); |
noelutz
2011/10/22 00:41:46
same question as above.
| |
184 return; | 231 return; |
185 } | 232 } |
186 | 233 |
187 URLFetcher* fetcher = URLFetcher::Create(0 /* ID used for testing */, | 234 URLFetcher* fetcher = URLFetcher::Create(0 /* ID used for testing */, |
188 GURL(kDownloadRequestUrl), | 235 GURL(kDownloadRequestUrl), |
189 URLFetcher::POST, | 236 URLFetcher::POST, |
190 this); | 237 this); |
191 download_requests_[fetcher] = callback; | 238 download_requests_[fetcher] = callback; |
192 fetcher->set_load_flags(net::LOAD_DISABLE_CACHE); | 239 fetcher->set_load_flags(net::LOAD_DISABLE_CACHE); |
193 fetcher->set_request_context(request_context_getter_.get()); | 240 fetcher->set_request_context(request_context_getter_.get()); |
194 fetcher->set_upload_data("application/octet-stream", request_data); | 241 fetcher->set_upload_data("application/octet-stream", request_data); |
195 fetcher->Start(); | 242 fetcher->Start(); |
196 } | 243 } |
197 | 244 |
198 void DownloadProtectionService::EndCheckClientDownload( | 245 void DownloadProtectionService::EndCheckClientDownload( |
199 DownloadCheckResult result, | 246 DownloadCheckResult result, |
200 DownloadCheckResultReason reason, | 247 DownloadCheckResultReason reason, |
201 const CheckDownloadCallback& callback) { | 248 const CheckDownloadCallback& callback) { |
202 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | 249 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
203 RecordStats(reason); | 250 RecordStats(reason); |
204 callback.Run(result); | 251 callback.Run(result); |
205 } | 252 } |
206 | 253 |
207 void DownloadProtectionService::RecordStats(DownloadCheckResultReason reason) { | 254 void DownloadProtectionService::RecordStats(DownloadCheckResultReason reason) { |
208 UMA_HISTOGRAM_ENUMERATION("SBClientDownload.CheckDownloadStats", | 255 UMA_HISTOGRAM_ENUMERATION("SBClientDownload.CheckDownloadStats", |
209 reason, | 256 reason, |
210 REASON_MAX); | 257 REASON_MAX); |
211 } | 258 } |
212 } // namespace safe_browsing | 259 } // namespace safe_browsing |
OLD | NEW |