1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
|
// Copyright (c) 2011 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <shlobj.h>
#include <shobjidl.h>
#include "content/browser/safe_util_win.h"
#include "base/file_path.h"
#include "base/logging.h"
#include "base/path_service.h"
#include "base/string_util.h"
#include "base/win/scoped_comptr.h"
#include "ui/base/win/shell.h"
namespace {
// This GUID is associated with any 'don't ask me again' settings that the
// user can select for different file types.
// {2676A9A2-D919-4fee-9187-152100393AB2}
static const GUID kClientID = { 0x2676a9a2, 0xd919, 0x4fee,
{ 0x91, 0x87, 0x15, 0x21, 0x0, 0x39, 0x3a, 0xb2 } };
// Directly writes the ZoneIdentifier stream, without using the
// IAttachmentExecute service.
bool SetInternetZoneIdentifierDirectly(const FilePath& full_path) {
const DWORD kShare = FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE;
std::wstring path = full_path.value() + L":Zone.Identifier";
HANDLE file = CreateFile(path.c_str(), GENERIC_WRITE, kShare, NULL,
OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
if (INVALID_HANDLE_VALUE == file)
return false;
static const char kIdentifier[] = "[ZoneTransfer]\r\nZoneId=3\r\n";
// Don't include trailing null in data written.
static const DWORD kIdentifierSize = arraysize(kIdentifier) - 1;
DWORD written = 0;
BOOL result = WriteFile(file, kIdentifier, kIdentifierSize, &written,
NULL);
BOOL flush_result = FlushFileBuffers(file);
CloseHandle(file);
if (!result || !flush_result || written != kIdentifierSize) {
NOTREACHED();
return false;
}
return true;
}
}
namespace win_util {
// This function implementation is based on the attachment execution
// services functionally deployed with IE6 or Service pack 2. This
// functionality is exposed in the IAttachmentExecute COM interface.
// more information at:
// http://msdn2.microsoft.com/en-us/library/ms647048.aspx
bool SaferOpenItemViaShell(HWND hwnd, const std::wstring& window_title,
const FilePath& full_path,
const std::wstring& source_url) {
base::win::ScopedComPtr<IAttachmentExecute> attachment_services;
HRESULT hr = attachment_services.CreateInstance(CLSID_AttachmentServices);
if (FAILED(hr)) {
// We don't have Attachment Execution Services, it must be a pre-XP.SP2
// Windows installation, or the thread does not have COM initialized.
if (hr == CO_E_NOTINITIALIZED) {
NOTREACHED();
return false;
}
return ui::win::OpenItemViaShell(full_path);
}
attachment_services->SetClientGuid(kClientID);
if (!window_title.empty())
attachment_services->SetClientTitle(window_title.c_str());
// To help windows decide if the downloaded file is dangerous we can provide
// what the documentation calls evidence. Which we provide now:
//
// Set the file itself as evidence.
hr = attachment_services->SetLocalPath(full_path.value().c_str());
if (FAILED(hr))
return false;
// Set the origin URL as evidence.
hr = attachment_services->SetSource(source_url.c_str());
if (FAILED(hr))
return false;
// Now check the windows policy.
if (attachment_services->CheckPolicy() != S_OK) {
// It is possible that the above call returns an undocumented result
// equal to 0x800c000e which seems to indicate that the URL failed the
// the security check. If you proceed with the Prompt() call the
// Shell might show a dialog that says:
// "windows found that this file is potentially harmful. To help protect
// your computer, Windows has blocked access to this file."
// Upon dismissal of the dialog windows will delete the file (!!).
// So, we can 'return' in that case but maybe is best to let it happen to
// fail on the safe side.
ATTACHMENT_ACTION action;
// We cannot control what the prompt says or does directly but it
// is a pretty decent dialog; for example, if an executable is signed it can
// decode and show the publisher and the certificate.
hr = attachment_services->Prompt(hwnd, ATTACHMENT_PROMPT_EXEC, &action);
if (FAILED(hr) || (ATTACHMENT_ACTION_CANCEL == action)) {
// The user has declined opening the item.
return false;
}
}
return ui::win::OpenItemViaShellNoZoneCheck(full_path);
}
bool SetInternetZoneIdentifier(const FilePath& full_path,
const std::wstring& source_url) {
base::win::ScopedComPtr<IAttachmentExecute> attachment_services;
HRESULT hr = attachment_services.CreateInstance(CLSID_AttachmentServices);
if (FAILED(hr)) {
// We don't have Attachment Execution Services, it must be a pre-XP.SP2
// Windows installation, or the thread does not have COM initialized.
if (hr == CO_E_NOTINITIALIZED) {
NOTREACHED();
return false;
}
// Write the ZoneIdentifier file directly.
return SetInternetZoneIdentifierDirectly(full_path);
}
hr = attachment_services->SetClientGuid(kClientID);
if (FAILED(hr))
return false;
hr = attachment_services->SetLocalPath(full_path.value().c_str());
if (FAILED(hr))
return false;
// Source is necessary for files ending in ".tmp" to avoid error 0x800c000e.
hr = attachment_services->SetSource(source_url.c_str());
if (FAILED(hr))
return false;
hr = attachment_services->Save();
if (FAILED(hr))
return false;
return true;
}
} // namespace win_util
|