Chromium Code Reviews| 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 |