Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(963)

Side by Side Diff: content/browser/download/quarantine_win.cc

Issue 2123023002: [Downloads] Consolidate MOTW annotation APIs into a single API. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@move-safe-util-to-downloads
Patch Set: Rebase Created 4 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. 1 // Copyright 2016 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 "content/browser/download/quarantine.h"
6
7 #include <windows.h>
8
9 #include <cguid.h>
10 #include <objbase.h>
11 #include <shellapi.h>
5 #include <shlobj.h> 12 #include <shlobj.h>
6 #include <shobjidl.h> 13 #include <shobjidl.h>
7 14
8 #include "content/browser/safe_util_win.h" 15 #include "base/files/file_util.h"
9 16 #include "base/guid.h"
10 #include "base/files/file_path.h"
11 #include "base/logging.h" 17 #include "base/logging.h"
12 #include "base/macros.h" 18 #include "base/macros.h"
13 #include "base/strings/string_util.h" 19 #include "base/metrics/histogram_macros.h"
20 #include "base/metrics/sparse_histogram.h"
14 #include "base/strings/utf_string_conversions.h" 21 #include "base/strings/utf_string_conversions.h"
22 #include "base/threading/thread_restrictions.h"
15 #include "base/win/scoped_comptr.h" 23 #include "base/win/scoped_comptr.h"
16 #include "ui/base/win/shell.h" 24 #include "base/win/scoped_handle.h"
17 #include "url/gurl.h" 25 #include "url/gurl.h"
18 26
19 namespace content { 27 namespace content {
20 namespace { 28 namespace {
21 29
30 const base::FilePath::CharType kZoneIdentifierStreamSuffix[] =
31 FILE_PATH_LITERAL(":Zone.Identifier");
32
33 // UMA enumeration for recording Download.AttachmentServicesResult.
34 enum class AttachmentServicesResult : int {
35 SUCCESS_WITH_MOTW = 0,
36 SUCCESS_WITHOUT_MOTW = 1,
37 SUCCESS_WITHOUT_FILE = 2,
38 NO_ATTACHMENT_SERVICES = 3,
39 FAILED_TO_SET_PARAMETER = 4,
40 BLOCKED_WITH_FILE = 5,
41 BLOCKED_WITHOUT_FILE = 6,
42 INFECTED_WITH_FILE = 7,
43 INFECTED_WITHOUT_FILE = 8,
44 ACCESS_DENIED_WITH_FILE = 9,
45 ACCESS_DENIED_WITHOUT_FILE = 10,
46 OTHER_WITH_FILE = 11,
47 OTHER_WITHOUT_FILE = 12,
48 };
49
50 void RecordAttachmentServicesResult(AttachmentServicesResult type) {
51 UMA_HISTOGRAM_SPARSE_SLOWLY("Download.AttachmentServices.Result",
52 static_cast<int>(type));
53 }
54
55 bool ZoneIdentifierPresentForFile(const base::FilePath& path) {
56 const DWORD kShare = FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE;
57 base::FilePath::StringType zone_identifier_path =
58 path.value() + kZoneIdentifierStreamSuffix;
59 base::win::ScopedHandle file(
60 CreateFile(zone_identifier_path.c_str(), GENERIC_READ, kShare, nullptr,
61 OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr));
62 return file.IsValid();
63 }
cpu_(ooo_6.6-7.5) 2016/09/20 15:52:33 interesting function. This only tests the stream e
asanka 2016/09/21 14:57:00 Yup. The assumption being that if Attachment Servi
64
65 void RecordAttachmentServicesSaveResult(const base::FilePath& file,
66 HRESULT hr) {
67 bool file_exists = base::PathExists(file);
68 if (SUCCEEDED(hr)) {
69 bool motw_exists = file_exists && ZoneIdentifierPresentForFile(file);
70 RecordAttachmentServicesResult(
71 file_exists
72 ? motw_exists ? AttachmentServicesResult::SUCCESS_WITH_MOTW
73 : AttachmentServicesResult::SUCCESS_WITHOUT_MOTW
74 : AttachmentServicesResult::SUCCESS_WITHOUT_FILE);
75 return;
76 }
77
78 switch (hr) {
79 case INET_E_SECURITY_PROBLEM:
80 RecordAttachmentServicesResult(
81 file_exists ? AttachmentServicesResult::BLOCKED_WITH_FILE
82 : AttachmentServicesResult::BLOCKED_WITHOUT_FILE);
83 break;
84
85 case E_FAIL:
86 RecordAttachmentServicesResult(
87 file_exists ? AttachmentServicesResult::INFECTED_WITH_FILE
88 : AttachmentServicesResult::INFECTED_WITHOUT_FILE);
89 break;
90
91 case E_ACCESSDENIED:
92 case ERROR_ACCESS_DENIED:
93 RecordAttachmentServicesResult(
94 file_exists ? AttachmentServicesResult::ACCESS_DENIED_WITH_FILE
95 : AttachmentServicesResult::ACCESS_DENIED_WITHOUT_FILE);
96 break;
97
98 default:
99 RecordAttachmentServicesResult(
100 file_exists ? AttachmentServicesResult::OTHER_WITH_FILE
101 : AttachmentServicesResult::OTHER_WITHOUT_FILE);
102 }
103 }
104
22 // Sets the Zone Identifier on the file to "Internet" (3). Returns true if the 105 // Sets the Zone Identifier on the file to "Internet" (3). Returns true if the
23 // function succeeds, false otherwise. A failure is expected on system where 106 // function succeeds, false otherwise. A failure is expected if alternate
24 // the Zone Identifier is not supported, like a machine with a FAT32 filesystem. 107 // streams are not supported, like a file on a FAT32 filesystem. This function
25 // This function does not invoke Windows Attachment Execution Services. 108 // does not invoke Windows Attachment Execution Services.
26 // 109 //
27 // |full_path| is the path to the downloaded file. 110 // |full_path| is the path to the downloaded file.
28 bool SetInternetZoneIdentifierDirectly(const base::FilePath& full_path) { 111 QuarantineFileResult SetInternetZoneIdentifierDirectly(
112 const base::FilePath& full_path) {
29 const DWORD kShare = FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE; 113 const DWORD kShare = FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE;
30 std::wstring path = full_path.value() + L":Zone.Identifier"; 114 std::wstring path = full_path.value() + kZoneIdentifierStreamSuffix;
31 HANDLE file = CreateFile(path.c_str(), GENERIC_WRITE, kShare, NULL, 115 HANDLE file = CreateFile(path.c_str(), GENERIC_WRITE, kShare, nullptr,
32 OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); 116 OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr);
33 if (INVALID_HANDLE_VALUE == file) 117 if (INVALID_HANDLE_VALUE == file)
34 return false; 118 return QuarantineFileResult::ANNOTATION_FAILED;
35 119
36 static const char kIdentifier[] = "[ZoneTransfer]\r\nZoneId=3\r\n"; 120 static const char kIdentifier[] = "[ZoneTransfer]\r\nZoneId=3\r\n";
37 // Don't include trailing null in data written. 121 // Don't include trailing null in data written.
38 static const DWORD kIdentifierSize = arraysize(kIdentifier) - 1; 122 static const DWORD kIdentifierSize = arraysize(kIdentifier) - 1;
39 DWORD written = 0; 123 DWORD written = 0;
40 BOOL result = WriteFile(file, kIdentifier, kIdentifierSize, &written, NULL); 124 BOOL write_result =
125 WriteFile(file, kIdentifier, kIdentifierSize, &written, nullptr);
41 BOOL flush_result = FlushFileBuffers(file); 126 BOOL flush_result = FlushFileBuffers(file);
42 CloseHandle(file); 127 CloseHandle(file);
43 128
44 if (!result || !flush_result || written != kIdentifierSize) { 129 return write_result && flush_result && written == kIdentifierSize
45 NOTREACHED(); 130 ? QuarantineFileResult::OK
46 return false; 131 : QuarantineFileResult::ANNOTATION_FAILED;
47 } 132 }
48 133
49 return true; 134 // Invokes IAttachmentExecute::Save on CLSID_AttachmentServices to validate the
50 } 135 // downloaded file. The call may scan the file for viruses and if necessary,
51 136 // annotate it with evidence. As a result of the validation, the file may be
52 } // namespace 137 // deleted. See: http://msdn.microsoft.com/en-us/bb776299
cpu_(ooo_6.6-7.5) 2016/09/20 15:52:33 here ^^, see my previous comment about the file go
asanka 2016/09/21 14:57:00 Yup. Thanks!
53 138 //
54 HRESULT AVScanFile(const base::FilePath& full_path, 139 // IAE::Save() will delete the file if it was found to be blocked by local
55 const std::string& source_url, 140 // security policy or if it was found to be infected. The call may also delete
56 const GUID& client_guid) { 141 // the file due to other failures (http://crbug.com/153212). A failure code will
142 // be returned in these cases.
143 //
144 // The return value is |false| iff the function fails to invoke
145 // IAttachmentExecute::Save(). If the function returns |true|, then the result
146 // of invoking IAttachmentExecute::Save() is stored in |save_result|.
147 //
148 // Typical |save_result| values:
149 // S_OK : The file was okay. If any viruses were found, they were cleaned.
150 // E_FAIL : Virus infected.
151 // INET_E_SECURITY_PROBLEM : The file was blocked due to security policy.
152 //
153 // Any other return value indicates an unexpected error during the scan.
154 //
155 // |full_path| : is the path to the downloaded file. This should be the final
156 // path of the download. Must be present.
157 // |source_url|: the source URL for the download. If empty, the source will
158 // not be set.
159 // |client_guid|: the GUID to be set in the IAttachmentExecute client slot.
160 // Used to identify the app to the system AV function.
161 // If GUID_NULL is passed, no client GUID is set.
162 // |save_result|: Receives the result of invoking IAttachmentExecute::Save().
163 bool InvokeAttachmentServices(const base::FilePath& full_path,
164 const std::string& source_url,
165 const GUID& client_guid,
166 HRESULT* save_result) {
57 base::win::ScopedComPtr<IAttachmentExecute> attachment_services; 167 base::win::ScopedComPtr<IAttachmentExecute> attachment_services;
58 HRESULT hr = attachment_services.CreateInstance(CLSID_AttachmentServices); 168 HRESULT hr = attachment_services.CreateInstance(CLSID_AttachmentServices);
169 *save_result = S_OK;
59 170
60 if (FAILED(hr)) { 171 if (FAILED(hr)) {
61 // The thread must have COM initialized. 172 // The thread must have COM initialized.
62 DCHECK_NE(CO_E_NOTINITIALIZED, hr); 173 DCHECK_NE(CO_E_NOTINITIALIZED, hr);
63 174 RecordAttachmentServicesResult(
64 // We don't have Attachment Execution Services, it must be a pre-XP.SP2 175 AttachmentServicesResult::NO_ATTACHMENT_SERVICES);
65 // Windows installation, or the thread does not have COM initialized. Try to 176 return false;
66 // set the zone information directly. Failure is not considered an error. 177 }
67 SetInternetZoneIdentifierDirectly(full_path); 178
68 return hr; 179 hr = attachment_services->SetClientGuid(client_guid);
69 } 180 if (FAILED(hr)) {
70 181 RecordAttachmentServicesResult(
71 if (!IsEqualGUID(client_guid, GUID_NULL)) { 182 AttachmentServicesResult::FAILED_TO_SET_PARAMETER);
72 hr = attachment_services->SetClientGuid(client_guid); 183 return false;
73 if (FAILED(hr))
74 return hr;
75 } 184 }
76 185
77 hr = attachment_services->SetLocalPath(full_path.value().c_str()); 186 hr = attachment_services->SetLocalPath(full_path.value().c_str());
78 if (FAILED(hr)) 187 if (FAILED(hr)) {
79 return hr; 188 RecordAttachmentServicesResult(
189 AttachmentServicesResult::FAILED_TO_SET_PARAMETER);
190 return false;
191 }
80 192
81 // Note: SetSource looks like it needs to be called, even if empty. 193 // Note: SetSource looks like it needs to be called, even if empty.
82 // Docs say it is optional, but it appears not calling it at all sets 194 // Docs say it is optional, but it appears not calling it at all sets
83 // a zone that is too restrictive. 195 // a zone that is too restrictive.
84 hr = attachment_services->SetSource(base::UTF8ToWide(source_url).c_str()); 196 hr = attachment_services->SetSource(base::UTF8ToWide(source_url).c_str());
85 if (FAILED(hr)) 197 if (FAILED(hr)) {
86 return hr; 198 RecordAttachmentServicesResult(
87 199 AttachmentServicesResult::FAILED_TO_SET_PARAMETER);
88 // A failure in the Save() call below could result in the downloaded file 200 return false;
89 // being deleted. 201 }
90 return attachment_services->Save(); 202
203 {
204 // This method has been known to take longer than 10 seconds in some
205 // instances.
206 SCOPED_UMA_HISTOGRAM_LONG_TIMER("Download.AttachmentServices.Duration");
207 *save_result = attachment_services->Save();
208 }
209 RecordAttachmentServicesSaveResult(full_path, *save_result);
210 return true;
211 }
212
213 // Maps a return code from an unsuccessful IAttachmentExecute::Save() call to a
214 // QuarantineFileResult.
215 QuarantineFileResult FailedSaveResultToQuarantineResult(HRESULT result) {
216 switch (result) {
217 case INET_E_SECURITY_PROBLEM: // 0x800c000e
218 // This is returned if the download was blocked due to security
219 // restrictions. E.g. if the source URL was in the Restricted Sites zone
220 // and downloads are blocked on that zone, then the download would be
221 // deleted and this error code is returned.
222 return QuarantineFileResult::BLOCKED_BY_POLICY;
223
224 case E_FAIL: // 0x80004005
225 // Returned if an anti-virus product reports an infection in the
226 // downloaded file during IAE::Save().
227 return QuarantineFileResult::VIRUS_INFECTED;
228
229 default:
230 // Any other error that occurs during IAttachmentExecute::Save() likely
231 // indicates a problem with the security check, but not necessarily the
232 // download. This also includes cases where SUCCEEDED(result) is true. In
233 // the latter case we are likely dealing with a situation where the file
234 // is missing after a successful scan. See http://crbug.com/153212.
235 return QuarantineFileResult::SECURITY_CHECK_FAILED;
236 }
237 }
238
239 } // namespace
240
241 QuarantineFileResult QuarantineFile(const base::FilePath& file,
242 const GURL& source_url,
243 const GURL& referrer_url,
244 const std::string& client_guid) {
245 base::ThreadRestrictions::AssertIOAllowed();
246
247 int64_t file_size = 0;
248 if (!base::PathExists(file) || !base::GetFileSize(file, &file_size))
249 return QuarantineFileResult::FILE_MISSING;
250
251 std::string braces_guid = "{" + client_guid + "}";
252 GUID guid = GUID_NULL;
253 if (base::IsValidGUID(client_guid)) {
254 HRESULT hr = CLSIDFromString(base::UTF8ToUTF16(braces_guid).c_str(), &guid);
255 if (FAILED(hr))
256 guid = GUID_NULL;
257 }
258
259 if (file_size == 0 || IsEqualGUID(guid, GUID_NULL)) {
260 // Calling InvokeAttachmentServices on an empty file can result in the file
261 // being deleted. Also an anti-virus scan doesn't make a lot of sense to
262 // perform on an empty file.
263 return SetInternetZoneIdentifierDirectly(file);
264 }
265
266 HRESULT save_result = S_OK;
267 bool attachment_services_available =
268 InvokeAttachmentServices(file, source_url.spec(), guid, &save_result);
269 if (!attachment_services_available)
270 return SetInternetZoneIdentifierDirectly(file);
271
272 // If the download file is missing after the call, then treat this as an
273 // interrupted download.
274 //
275 // If InvokeAttachmentServices() failed, but the downloaded file is still
276 // around, then don't interrupt the download. Attachment Execution Services
277 // deletes the submitted file if the downloaded file is blocked by policy or
278 // if it was found to be infected.
279 //
280 // If the file is still there, then the error could be due to Windows
281 // Attachment Services not being available or some other error during the AES
282 // invocation. In either case, we don't surface the error to the user.
283 if (!base::PathExists(file))
284 return FailedSaveResultToQuarantineResult(save_result);
285 return QuarantineFileResult::OK;
91 } 286 }
92 287
93 } // namespace content 288 } // namespace content
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698