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/win/src/filesystem_policy.h"
9 #include "base/logging.h"
10 #include "base/win/scoped_handle.h"
11 #include "sandbox/win/src/ipc_tags.h"
12 #include "sandbox/win/src/policy_engine_opcodes.h"
13 #include "sandbox/win/src/policy_params.h"
14 #include "sandbox/win/src/sandbox_utils.h"
15 #include "sandbox/win/src/sandbox_types.h"
16 #include "sandbox/win/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 return STATUS_ACCESS_DENIED
;
54 return STATUS_SUCCESS
;
61 bool FileSystemPolicy::GenerateRules(const wchar_t* name
,
62 TargetPolicy::Semantics semantics
,
63 LowLevelPolicy
* policy
) {
64 std::wstring
mod_name(name
);
65 if (mod_name
.empty()) {
69 // Don't do any pre-processing if the name starts like the the native
70 // object manager style.
71 if (0 != _wcsnicmp(mod_name
.c_str(), kNTObjManPrefix
, kNTObjManPrefixLen
)) {
72 // TODO(cpu) bug 32224: This prefix add is a hack because we don't have the
73 // infrastructure to normalize names. In any case we need to escape the
75 if (!PreProcessName(mod_name
, &mod_name
)) {
76 // The path to be added might contain a reparse point.
80 if (0 != mod_name
.compare(0, kNTPrefixLen
, kNTPrefix
)) {
81 // TODO(nsylvain): Find a better way to do name resolution. Right now we
82 // take the name and we expand it.
83 mod_name
.insert(0, L
"\\/?/?\\");
84 name
= mod_name
.c_str();
88 EvalResult result
= ASK_BROKER
;
90 // List of supported calls for the filesystem.
91 const unsigned kCallNtCreateFile
= 0x1;
92 const unsigned kCallNtOpenFile
= 0x2;
93 const unsigned kCallNtQueryAttributesFile
= 0x4;
94 const unsigned kCallNtQueryFullAttributesFile
= 0x8;
95 const unsigned kCallNtSetInfoRename
= 0x10;
97 DWORD rule_to_add
= kCallNtOpenFile
| kCallNtCreateFile
|
98 kCallNtQueryAttributesFile
|
99 kCallNtQueryFullAttributesFile
| kCallNtSetInfoRename
;
101 PolicyRule
create(result
);
102 PolicyRule
open(result
);
103 PolicyRule
query(result
);
104 PolicyRule
query_full(result
);
105 PolicyRule
rename(result
);
108 case TargetPolicy::FILES_ALLOW_DIR_ANY
: {
109 open
.AddNumberMatch(IF
, OpenFile::OPTIONS
, FILE_DIRECTORY_FILE
, AND
);
110 create
.AddNumberMatch(IF
, OpenFile::OPTIONS
, FILE_DIRECTORY_FILE
, AND
);
113 case TargetPolicy::FILES_ALLOW_READONLY
: {
114 // We consider all flags that are not known to be readonly as potentially
116 DWORD allowed_flags
= FILE_READ_DATA
| FILE_READ_ATTRIBUTES
|
117 FILE_READ_EA
| SYNCHRONIZE
| FILE_EXECUTE
|
118 GENERIC_READ
| GENERIC_EXECUTE
| READ_CONTROL
;
119 DWORD restricted_flags
= ~allowed_flags
;
120 open
.AddNumberMatch(IF_NOT
, OpenFile::ACCESS
, restricted_flags
, AND
);
121 create
.AddNumberMatch(IF_NOT
, OpenFile::ACCESS
, restricted_flags
, AND
);
123 // Read only access don't work for rename.
124 rule_to_add
&= ~kCallNtSetInfoRename
;
127 case TargetPolicy::FILES_ALLOW_QUERY
: {
128 // Here we don't want to add policy for the open or the create.
129 rule_to_add
&= ~(kCallNtOpenFile
| kCallNtCreateFile
|
130 kCallNtSetInfoRename
);
133 case TargetPolicy::FILES_ALLOW_ANY
: {
142 if ((rule_to_add
& kCallNtCreateFile
) &&
143 (!create
.AddStringMatch(IF
, OpenFile::NAME
, name
, CASE_INSENSITIVE
) ||
144 !policy
->AddRule(IPC_NTCREATEFILE_TAG
, &create
))) {
148 if ((rule_to_add
& kCallNtOpenFile
) &&
149 (!open
.AddStringMatch(IF
, OpenFile::NAME
, name
, CASE_INSENSITIVE
) ||
150 !policy
->AddRule(IPC_NTOPENFILE_TAG
, &open
))) {
154 if ((rule_to_add
& kCallNtQueryAttributesFile
) &&
155 (!query
.AddStringMatch(IF
, FileName::NAME
, name
, CASE_INSENSITIVE
) ||
156 !policy
->AddRule(IPC_NTQUERYATTRIBUTESFILE_TAG
, &query
))) {
160 if ((rule_to_add
& kCallNtQueryFullAttributesFile
) &&
161 (!query_full
.AddStringMatch(IF
, FileName::NAME
, name
, CASE_INSENSITIVE
)
162 || !policy
->AddRule(IPC_NTQUERYFULLATTRIBUTESFILE_TAG
,
167 if ((rule_to_add
& kCallNtSetInfoRename
) &&
168 (!rename
.AddStringMatch(IF
, FileName::NAME
, name
, CASE_INSENSITIVE
) ||
169 !policy
->AddRule(IPC_NTSETINFO_RENAME_TAG
, &rename
))) {
176 // Right now we insert two rules, to be evaluated before any user supplied rule:
177 // - go to the broker if the path doesn't look like the paths that we push on
178 // the policy (namely \??\something).
179 // - go to the broker if it looks like this is a short-name path.
181 // It is possible to add a rule to go to the broker in any case; it would look
183 // rule = new PolicyRule(ASK_BROKER);
184 // rule->AddNumberMatch(IF_NOT, FileName::BROKER, TRUE, AND);
185 // policy->AddRule(service, rule);
186 bool FileSystemPolicy::SetInitialRules(LowLevelPolicy
* policy
) {
187 PolicyRule
format(ASK_BROKER
);
188 PolicyRule
short_name(ASK_BROKER
);
190 bool rv
= format
.AddNumberMatch(IF_NOT
, FileName::BROKER
, TRUE
, AND
);
191 rv
&= format
.AddStringMatch(IF_NOT
, FileName::NAME
, L
"\\/?/?\\*",
194 rv
&= short_name
.AddNumberMatch(IF_NOT
, FileName::BROKER
, TRUE
, AND
);
195 rv
&= short_name
.AddStringMatch(IF
, FileName::NAME
, L
"*~*", CASE_SENSITIVE
);
197 if (!rv
|| !policy
->AddRule(IPC_NTCREATEFILE_TAG
, &format
))
200 if (!policy
->AddRule(IPC_NTCREATEFILE_TAG
, &short_name
))
203 if (!policy
->AddRule(IPC_NTOPENFILE_TAG
, &format
))
206 if (!policy
->AddRule(IPC_NTOPENFILE_TAG
, &short_name
))
209 if (!policy
->AddRule(IPC_NTQUERYATTRIBUTESFILE_TAG
, &format
))
212 if (!policy
->AddRule(IPC_NTQUERYATTRIBUTESFILE_TAG
, &short_name
))
215 if (!policy
->AddRule(IPC_NTQUERYFULLATTRIBUTESFILE_TAG
, &format
))
218 if (!policy
->AddRule(IPC_NTQUERYFULLATTRIBUTESFILE_TAG
, &short_name
))
221 if (!policy
->AddRule(IPC_NTSETINFO_RENAME_TAG
, &format
))
224 if (!policy
->AddRule(IPC_NTSETINFO_RENAME_TAG
, &short_name
))
230 bool FileSystemPolicy::CreateFileAction(EvalResult eval_result
,
231 const ClientInfo
& client_info
,
232 const std::wstring
&file
,
234 uint32 desired_access
,
235 uint32 file_attributes
,
237 uint32 create_disposition
,
238 uint32 create_options
,
241 ULONG_PTR
*io_information
) {
242 // The only action supported is ASK_BROKER which means create the requested
243 // file as specified.
244 if (ASK_BROKER
!= eval_result
) {
245 *nt_status
= STATUS_ACCESS_DENIED
;
248 IO_STATUS_BLOCK io_block
= {0};
249 UNICODE_STRING uni_name
= {0};
250 OBJECT_ATTRIBUTES obj_attributes
= {0};
251 InitObjectAttribs(file
, attributes
, NULL
, &obj_attributes
, &uni_name
);
252 *nt_status
= NtCreateFileInTarget(handle
, desired_access
, &obj_attributes
,
253 &io_block
, file_attributes
, share_access
,
254 create_disposition
, create_options
, NULL
,
255 0, client_info
.process
);
257 *io_information
= io_block
.Information
;
261 bool FileSystemPolicy::OpenFileAction(EvalResult eval_result
,
262 const ClientInfo
& client_info
,
263 const std::wstring
&file
,
265 uint32 desired_access
,
270 ULONG_PTR
*io_information
) {
271 // The only action supported is ASK_BROKER which means open the requested
272 // file as specified.
273 if (ASK_BROKER
!= eval_result
) {
274 *nt_status
= STATUS_ACCESS_DENIED
;
277 // An NtOpen is equivalent to an NtCreate with FileAttributes = 0 and
278 // CreateDisposition = FILE_OPEN.
279 IO_STATUS_BLOCK io_block
= {0};
280 UNICODE_STRING uni_name
= {0};
281 OBJECT_ATTRIBUTES obj_attributes
= {0};
282 InitObjectAttribs(file
, attributes
, NULL
, &obj_attributes
, &uni_name
);
283 *nt_status
= NtCreateFileInTarget(handle
, desired_access
, &obj_attributes
,
284 &io_block
, 0, share_access
, FILE_OPEN
,
285 open_options
, NULL
, 0,
286 client_info
.process
);
288 *io_information
= io_block
.Information
;
292 bool FileSystemPolicy::QueryAttributesFileAction(
293 EvalResult eval_result
,
294 const ClientInfo
& client_info
,
295 const std::wstring
&file
,
297 FILE_BASIC_INFORMATION
* file_info
,
298 NTSTATUS
* nt_status
) {
299 // The only action supported is ASK_BROKER which means query the requested
300 // file as specified.
301 if (ASK_BROKER
!= eval_result
) {
302 *nt_status
= STATUS_ACCESS_DENIED
;
306 NtQueryAttributesFileFunction NtQueryAttributesFile
= NULL
;
307 ResolveNTFunctionPtr("NtQueryAttributesFile", &NtQueryAttributesFile
);
309 UNICODE_STRING uni_name
= {0};
310 OBJECT_ATTRIBUTES obj_attributes
= {0};
311 InitObjectAttribs(file
, attributes
, NULL
, &obj_attributes
, &uni_name
);
312 *nt_status
= NtQueryAttributesFile(&obj_attributes
, file_info
);
317 bool FileSystemPolicy::QueryFullAttributesFileAction(
318 EvalResult eval_result
,
319 const ClientInfo
& client_info
,
320 const std::wstring
&file
,
322 FILE_NETWORK_OPEN_INFORMATION
* file_info
,
323 NTSTATUS
* nt_status
) {
324 // The only action supported is ASK_BROKER which means query the requested
325 // file as specified.
326 if (ASK_BROKER
!= eval_result
) {
327 *nt_status
= STATUS_ACCESS_DENIED
;
331 NtQueryFullAttributesFileFunction NtQueryFullAttributesFile
= NULL
;
332 ResolveNTFunctionPtr("NtQueryFullAttributesFile", &NtQueryFullAttributesFile
);
334 UNICODE_STRING uni_name
= {0};
335 OBJECT_ATTRIBUTES obj_attributes
= {0};
336 InitObjectAttribs(file
, attributes
, NULL
, &obj_attributes
, &uni_name
);
337 *nt_status
= NtQueryFullAttributesFile(&obj_attributes
, file_info
);
342 bool FileSystemPolicy::SetInformationFileAction(
343 EvalResult eval_result
, const ClientInfo
& client_info
,
344 HANDLE target_file_handle
, void* file_info
, uint32 length
,
345 uint32 info_class
, IO_STATUS_BLOCK
* io_block
,
346 NTSTATUS
* nt_status
) {
347 // The only action supported is ASK_BROKER which means open the requested
348 // file as specified.
349 if (ASK_BROKER
!= eval_result
) {
350 *nt_status
= STATUS_ACCESS_DENIED
;
354 NtSetInformationFileFunction NtSetInformationFile
= NULL
;
355 ResolveNTFunctionPtr("NtSetInformationFile", &NtSetInformationFile
);
357 HANDLE local_handle
= NULL
;
358 if (!::DuplicateHandle(client_info
.process
, target_file_handle
,
359 ::GetCurrentProcess(), &local_handle
, 0, FALSE
,
360 DUPLICATE_SAME_ACCESS
)) {
361 *nt_status
= STATUS_ACCESS_DENIED
;
365 base::win::ScopedHandle
handle(local_handle
);
367 FILE_INFORMATION_CLASS file_info_class
=
368 static_cast<FILE_INFORMATION_CLASS
>(info_class
);
369 *nt_status
= NtSetInformationFile(local_handle
, io_block
, file_info
, length
,
375 bool PreProcessName(const std::wstring
& path
, std::wstring
* new_path
) {
376 ConvertToLongPath(path
, new_path
);
378 bool reparsed
= false;
379 if (ERROR_SUCCESS
!= IsReparsePoint(*new_path
, &reparsed
))
382 // We can't process reparsed file.
386 } // namespace sandbox