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
3 // found in the LICENSE file.
7 #include "sandbox/src/filesystem_policy.h"
9 #include "base/logging.h"
10 #include "base/win/scoped_handle.h"
11 #include "sandbox/src/ipc_tags.h"
12 #include "sandbox/src/policy_engine_opcodes.h"
13 #include "sandbox/src/policy_params.h"
14 #include "sandbox/src/sandbox_utils.h"
15 #include "sandbox/src/sandbox_types.h"
16 #include "sandbox/src/win_utils.h"
20 NTSTATUS
NtCreateFileInTarget(HANDLE
* target_file_handle
,
21 ACCESS_MASK desired_access
,
22 OBJECT_ATTRIBUTES
* obj_attributes
,
23 IO_STATUS_BLOCK
* io_status_block
,
24 ULONG file_attributes
,
26 ULONG create_disposition
,
30 HANDLE target_process
) {
31 NtCreateFileFunction NtCreateFile
= NULL
;
32 ResolveNTFunctionPtr("NtCreateFile", &NtCreateFile
);
34 HANDLE local_handle
= INVALID_HANDLE_VALUE
;
35 NTSTATUS status
= NtCreateFile(&local_handle
, desired_access
, obj_attributes
,
36 io_status_block
, NULL
, file_attributes
,
37 share_access
, create_disposition
,
38 create_options
, ea_buffer
, ea_lenght
);
39 if (!NT_SUCCESS(status
)) {
43 if (!sandbox::SameObject(local_handle
, obj_attributes
->ObjectName
->Buffer
)) {
44 // The handle points somewhere else. Fail the operation.
45 ::CloseHandle(local_handle
);
46 return STATUS_ACCESS_DENIED
;
49 if (!::DuplicateHandle(::GetCurrentProcess(), local_handle
,
50 target_process
, target_file_handle
, 0, FALSE
,
51 DUPLICATE_CLOSE_SOURCE
| DUPLICATE_SAME_ACCESS
)) {
52 ::CloseHandle(local_handle
);
53 return STATUS_ACCESS_DENIED
;
55 return STATUS_SUCCESS
;
62 bool FileSystemPolicy::GenerateRules(const wchar_t* name
,
63 TargetPolicy::Semantics semantics
,
64 LowLevelPolicy
* policy
) {
65 std::wstring
mod_name(name
);
66 if (mod_name
.empty()) {
70 // Don't do any pre-processing if the name starts like the the native
71 // object manager style.
72 if (0 != _wcsnicmp(mod_name
.c_str(), kNTObjManPrefix
, kNTObjManPrefixLen
)) {
73 // TODO(cpu) bug 32224: This prefix add is a hack because we don't have the
74 // infrastructure to normalize names. In any case we need to escape the
76 if (!PreProcessName(mod_name
, &mod_name
)) {
77 // The path to be added might contain a reparse point.
81 if (0 != mod_name
.compare(0, kNTPrefixLen
, kNTPrefix
)) {
82 // TODO(nsylvain): Find a better way to do name resolution. Right now we
83 // take the name and we expand it.
84 mod_name
.insert(0, L
"\\/?/?\\");
85 name
= mod_name
.c_str();
89 EvalResult result
= ASK_BROKER
;
91 // List of supported calls for the filesystem.
92 const unsigned kCallNtCreateFile
= 0x1;
93 const unsigned kCallNtOpenFile
= 0x2;
94 const unsigned kCallNtQueryAttributesFile
= 0x4;
95 const unsigned kCallNtQueryFullAttributesFile
= 0x8;
96 const unsigned kCallNtSetInfoRename
= 0x10;
98 DWORD rule_to_add
= kCallNtOpenFile
| kCallNtCreateFile
|
99 kCallNtQueryAttributesFile
|
100 kCallNtQueryFullAttributesFile
| kCallNtSetInfoRename
;
102 PolicyRule
create(result
);
103 PolicyRule
open(result
);
104 PolicyRule
query(result
);
105 PolicyRule
query_full(result
);
106 PolicyRule
rename(result
);
109 case TargetPolicy::FILES_ALLOW_DIR_ANY
: {
110 open
.AddNumberMatch(IF
, OpenFile::OPTIONS
, FILE_DIRECTORY_FILE
, AND
);
111 create
.AddNumberMatch(IF
, OpenFile::OPTIONS
, FILE_DIRECTORY_FILE
, AND
);
114 case TargetPolicy::FILES_ALLOW_READONLY
: {
115 // We consider all flags that are not known to be readonly as potentially
117 DWORD allowed_flags
= FILE_READ_DATA
| FILE_READ_ATTRIBUTES
|
118 FILE_READ_EA
| SYNCHRONIZE
| FILE_EXECUTE
|
119 GENERIC_READ
| GENERIC_EXECUTE
| READ_CONTROL
;
120 DWORD restricted_flags
= ~allowed_flags
;
121 open
.AddNumberMatch(IF_NOT
, OpenFile::ACCESS
, restricted_flags
, AND
);
122 create
.AddNumberMatch(IF_NOT
, OpenFile::ACCESS
, restricted_flags
, AND
);
124 // Read only access don't work for rename.
125 rule_to_add
&= ~kCallNtSetInfoRename
;
128 case TargetPolicy::FILES_ALLOW_QUERY
: {
129 // Here we don't want to add policy for the open or the create.
130 rule_to_add
&= ~(kCallNtOpenFile
| kCallNtCreateFile
|
131 kCallNtSetInfoRename
);
134 case TargetPolicy::FILES_ALLOW_ANY
: {
143 if ((rule_to_add
& kCallNtCreateFile
) &&
144 (!create
.AddStringMatch(IF
, OpenFile::NAME
, name
, CASE_INSENSITIVE
) ||
145 !policy
->AddRule(IPC_NTCREATEFILE_TAG
, &create
))) {
149 if ((rule_to_add
& kCallNtOpenFile
) &&
150 (!open
.AddStringMatch(IF
, OpenFile::NAME
, name
, CASE_INSENSITIVE
) ||
151 !policy
->AddRule(IPC_NTOPENFILE_TAG
, &open
))) {
155 if ((rule_to_add
& kCallNtQueryAttributesFile
) &&
156 (!query
.AddStringMatch(IF
, FileName::NAME
, name
, CASE_INSENSITIVE
) ||
157 !policy
->AddRule(IPC_NTQUERYATTRIBUTESFILE_TAG
, &query
))) {
161 if ((rule_to_add
& kCallNtQueryFullAttributesFile
) &&
162 (!query_full
.AddStringMatch(IF
, FileName::NAME
, name
, CASE_INSENSITIVE
)
163 || !policy
->AddRule(IPC_NTQUERYFULLATTRIBUTESFILE_TAG
,
168 if ((rule_to_add
& kCallNtSetInfoRename
) &&
169 (!rename
.AddStringMatch(IF
, FileName::NAME
, name
, CASE_INSENSITIVE
) ||
170 !policy
->AddRule(IPC_NTSETINFO_RENAME_TAG
, &rename
))) {
177 // Right now we insert two rules, to be evaluated before any user supplied rule:
178 // - go to the broker if the path doesn't look like the paths that we push on
179 // the policy (namely \??\something).
180 // - go to the broker if it looks like this is a short-name path.
182 // It is possible to add a rule to go to the broker in any case; it would look
184 // rule = new PolicyRule(ASK_BROKER);
185 // rule->AddNumberMatch(IF_NOT, FileName::BROKER, TRUE, AND);
186 // policy->AddRule(service, rule);
187 bool FileSystemPolicy::SetInitialRules(LowLevelPolicy
* policy
) {
188 PolicyRule
format(ASK_BROKER
);
189 PolicyRule
short_name(ASK_BROKER
);
191 bool rv
= format
.AddNumberMatch(IF_NOT
, FileName::BROKER
, TRUE
, AND
);
192 rv
&= format
.AddStringMatch(IF_NOT
, FileName::NAME
, L
"\\/?/?\\*",
195 rv
&= short_name
.AddNumberMatch(IF_NOT
, FileName::BROKER
, TRUE
, AND
);
196 rv
&= short_name
.AddStringMatch(IF
, FileName::NAME
, L
"*~*", CASE_SENSITIVE
);
198 if (!rv
|| !policy
->AddRule(IPC_NTCREATEFILE_TAG
, &format
))
201 if (!policy
->AddRule(IPC_NTCREATEFILE_TAG
, &short_name
))
204 if (!policy
->AddRule(IPC_NTOPENFILE_TAG
, &format
))
207 if (!policy
->AddRule(IPC_NTOPENFILE_TAG
, &short_name
))
210 if (!policy
->AddRule(IPC_NTQUERYATTRIBUTESFILE_TAG
, &format
))
213 if (!policy
->AddRule(IPC_NTQUERYATTRIBUTESFILE_TAG
, &short_name
))
216 if (!policy
->AddRule(IPC_NTQUERYFULLATTRIBUTESFILE_TAG
, &format
))
219 if (!policy
->AddRule(IPC_NTQUERYFULLATTRIBUTESFILE_TAG
, &short_name
))
222 if (!policy
->AddRule(IPC_NTSETINFO_RENAME_TAG
, &format
))
225 if (!policy
->AddRule(IPC_NTSETINFO_RENAME_TAG
, &short_name
))
231 bool FileSystemPolicy::CreateFileAction(EvalResult eval_result
,
232 const ClientInfo
& client_info
,
233 const std::wstring
&file
,
235 uint32 desired_access
,
236 uint32 file_attributes
,
238 uint32 create_disposition
,
239 uint32 create_options
,
242 ULONG_PTR
*io_information
) {
243 // The only action supported is ASK_BROKER which means create the requested
244 // file as specified.
245 if (ASK_BROKER
!= eval_result
) {
246 *nt_status
= STATUS_ACCESS_DENIED
;
249 IO_STATUS_BLOCK io_block
= {0};
250 UNICODE_STRING uni_name
= {0};
251 OBJECT_ATTRIBUTES obj_attributes
= {0};
252 InitObjectAttribs(file
, attributes
, NULL
, &obj_attributes
, &uni_name
);
253 *nt_status
= NtCreateFileInTarget(handle
, desired_access
, &obj_attributes
,
254 &io_block
, file_attributes
, share_access
,
255 create_disposition
, create_options
, NULL
,
256 0, client_info
.process
);
258 *io_information
= io_block
.Information
;
262 bool FileSystemPolicy::OpenFileAction(EvalResult eval_result
,
263 const ClientInfo
& client_info
,
264 const std::wstring
&file
,
266 uint32 desired_access
,
271 ULONG_PTR
*io_information
) {
272 // The only action supported is ASK_BROKER which means open the requested
273 // file as specified.
274 if (ASK_BROKER
!= eval_result
) {
275 *nt_status
= STATUS_ACCESS_DENIED
;
278 // An NtOpen is equivalent to an NtCreate with FileAttributes = 0 and
279 // CreateDisposition = FILE_OPEN.
280 IO_STATUS_BLOCK io_block
= {0};
281 UNICODE_STRING uni_name
= {0};
282 OBJECT_ATTRIBUTES obj_attributes
= {0};
283 InitObjectAttribs(file
, attributes
, NULL
, &obj_attributes
, &uni_name
);
284 *nt_status
= NtCreateFileInTarget(handle
, desired_access
, &obj_attributes
,
285 &io_block
, 0, share_access
, FILE_OPEN
,
286 open_options
, NULL
, 0,
287 client_info
.process
);
289 *io_information
= io_block
.Information
;
293 bool FileSystemPolicy::QueryAttributesFileAction(
294 EvalResult eval_result
,
295 const ClientInfo
& client_info
,
296 const std::wstring
&file
,
298 FILE_BASIC_INFORMATION
* file_info
,
299 NTSTATUS
* nt_status
) {
300 // The only action supported is ASK_BROKER which means query the requested
301 // file as specified.
302 if (ASK_BROKER
!= eval_result
) {
303 *nt_status
= STATUS_ACCESS_DENIED
;
307 NtQueryAttributesFileFunction NtQueryAttributesFile
= NULL
;
308 ResolveNTFunctionPtr("NtQueryAttributesFile", &NtQueryAttributesFile
);
310 UNICODE_STRING uni_name
= {0};
311 OBJECT_ATTRIBUTES obj_attributes
= {0};
312 InitObjectAttribs(file
, attributes
, NULL
, &obj_attributes
, &uni_name
);
313 *nt_status
= NtQueryAttributesFile(&obj_attributes
, file_info
);
318 bool FileSystemPolicy::QueryFullAttributesFileAction(
319 EvalResult eval_result
,
320 const ClientInfo
& client_info
,
321 const std::wstring
&file
,
323 FILE_NETWORK_OPEN_INFORMATION
* file_info
,
324 NTSTATUS
* nt_status
) {
325 // The only action supported is ASK_BROKER which means query the requested
326 // file as specified.
327 if (ASK_BROKER
!= eval_result
) {
328 *nt_status
= STATUS_ACCESS_DENIED
;
332 NtQueryFullAttributesFileFunction NtQueryFullAttributesFile
= NULL
;
333 ResolveNTFunctionPtr("NtQueryFullAttributesFile", &NtQueryFullAttributesFile
);
335 UNICODE_STRING uni_name
= {0};
336 OBJECT_ATTRIBUTES obj_attributes
= {0};
337 InitObjectAttribs(file
, attributes
, NULL
, &obj_attributes
, &uni_name
);
338 *nt_status
= NtQueryFullAttributesFile(&obj_attributes
, file_info
);
343 bool FileSystemPolicy::SetInformationFileAction(
344 EvalResult eval_result
, const ClientInfo
& client_info
,
345 HANDLE target_file_handle
, void* file_info
, uint32 length
,
346 uint32 info_class
, IO_STATUS_BLOCK
* io_block
,
347 NTSTATUS
* nt_status
) {
348 // The only action supported is ASK_BROKER which means open the requested
349 // file as specified.
350 if (ASK_BROKER
!= eval_result
) {
351 *nt_status
= STATUS_ACCESS_DENIED
;
355 NtSetInformationFileFunction NtSetInformationFile
= NULL
;
356 ResolveNTFunctionPtr("NtSetInformationFile", &NtSetInformationFile
);
358 HANDLE local_handle
= NULL
;
359 if (!::DuplicateHandle(client_info
.process
, target_file_handle
,
360 ::GetCurrentProcess(), &local_handle
, 0, FALSE
,
361 DUPLICATE_SAME_ACCESS
)) {
362 *nt_status
= STATUS_ACCESS_DENIED
;
366 base::win::ScopedHandle
handle(local_handle
);
368 FILE_INFORMATION_CLASS file_info_class
=
369 static_cast<FILE_INFORMATION_CLASS
>(info_class
);
370 *nt_status
= NtSetInformationFile(local_handle
, io_block
, file_info
, length
,
376 bool PreProcessName(const std::wstring
& path
, std::wstring
* new_path
) {
377 ConvertToLongPath(path
, new_path
);
379 bool reparsed
= false;
380 if (ERROR_SUCCESS
!= IsReparsePoint(*new_path
, &reparsed
))
383 // We can't process reparsed file.
387 } // namespace sandbox