Update V8 to version 4.3.37.
[chromium-blink-merge.git] / sandbox / win / src / filesystem_policy.cc
blob1ce21c8c20bffa55bc16471cd76a4f563c795a1a
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.
5 #include <string>
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"
18 namespace {
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,
25 ULONG share_access,
26 ULONG create_disposition,
27 ULONG create_options,
28 PVOID ea_buffer,
29 ULONG ea_lenght,
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)) {
40 return 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;
57 // Get an initialized anonymous level Security QOS.
58 SECURITY_QUALITY_OF_SERVICE GetAnonymousQOS() {
59 SECURITY_QUALITY_OF_SERVICE security_qos = {0};
60 security_qos.Length = sizeof(security_qos);
61 security_qos.ImpersonationLevel = SecurityAnonymous;
62 // Set dynamic tracking so that a pipe doesn't capture the broker's token
63 security_qos.ContextTrackingMode = SECURITY_DYNAMIC_TRACKING;
64 security_qos.EffectiveOnly = TRUE;
66 return security_qos;
69 } // namespace.
71 namespace sandbox {
73 bool FileSystemPolicy::GenerateRules(const wchar_t* name,
74 TargetPolicy::Semantics semantics,
75 LowLevelPolicy* policy) {
76 base::string16 mod_name(name);
77 if (mod_name.empty()) {
78 return false;
81 // Don't do any pre-processing if the name starts like the the native
82 // object manager style.
83 if (0 != _wcsnicmp(mod_name.c_str(), kNTObjManPrefix, kNTObjManPrefixLen)) {
84 // TODO(cpu) bug 32224: This prefix add is a hack because we don't have the
85 // infrastructure to normalize names. In any case we need to escape the
86 // question marks.
87 if (!PreProcessName(mod_name, &mod_name)) {
88 // The path to be added might contain a reparse point.
89 NOTREACHED();
90 return false;
93 mod_name = FixNTPrefixForMatch(mod_name);
94 name = mod_name.c_str();
97 EvalResult result = ASK_BROKER;
99 // List of supported calls for the filesystem.
100 const unsigned kCallNtCreateFile = 0x1;
101 const unsigned kCallNtOpenFile = 0x2;
102 const unsigned kCallNtQueryAttributesFile = 0x4;
103 const unsigned kCallNtQueryFullAttributesFile = 0x8;
104 const unsigned kCallNtSetInfoRename = 0x10;
106 DWORD rule_to_add = kCallNtOpenFile | kCallNtCreateFile |
107 kCallNtQueryAttributesFile |
108 kCallNtQueryFullAttributesFile | kCallNtSetInfoRename;
110 PolicyRule create(result);
111 PolicyRule open(result);
112 PolicyRule query(result);
113 PolicyRule query_full(result);
114 PolicyRule rename(result);
116 switch (semantics) {
117 case TargetPolicy::FILES_ALLOW_DIR_ANY: {
118 open.AddNumberMatch(IF, OpenFile::OPTIONS, FILE_DIRECTORY_FILE, AND);
119 create.AddNumberMatch(IF, OpenFile::OPTIONS, FILE_DIRECTORY_FILE, AND);
120 break;
122 case TargetPolicy::FILES_ALLOW_READONLY: {
123 // We consider all flags that are not known to be readonly as potentially
124 // used for write.
125 DWORD allowed_flags = FILE_READ_DATA | FILE_READ_ATTRIBUTES |
126 FILE_READ_EA | SYNCHRONIZE | FILE_EXECUTE |
127 GENERIC_READ | GENERIC_EXECUTE | READ_CONTROL;
128 DWORD restricted_flags = ~allowed_flags;
129 open.AddNumberMatch(IF_NOT, OpenFile::ACCESS, restricted_flags, AND);
130 open.AddNumberMatch(IF, OpenFile::DISPOSITION, FILE_OPEN, EQUAL);
131 create.AddNumberMatch(IF_NOT, OpenFile::ACCESS, restricted_flags, AND);
132 create.AddNumberMatch(IF, OpenFile::DISPOSITION, FILE_OPEN, EQUAL);
134 // Read only access don't work for rename.
135 rule_to_add &= ~kCallNtSetInfoRename;
136 break;
138 case TargetPolicy::FILES_ALLOW_QUERY: {
139 // Here we don't want to add policy for the open or the create.
140 rule_to_add &= ~(kCallNtOpenFile | kCallNtCreateFile |
141 kCallNtSetInfoRename);
142 break;
144 case TargetPolicy::FILES_ALLOW_ANY: {
145 break;
147 default: {
148 NOTREACHED();
149 return false;
153 if ((rule_to_add & kCallNtCreateFile) &&
154 (!create.AddStringMatch(IF, OpenFile::NAME, name, CASE_INSENSITIVE) ||
155 !policy->AddRule(IPC_NTCREATEFILE_TAG, &create))) {
156 return false;
159 if ((rule_to_add & kCallNtOpenFile) &&
160 (!open.AddStringMatch(IF, OpenFile::NAME, name, CASE_INSENSITIVE) ||
161 !policy->AddRule(IPC_NTOPENFILE_TAG, &open))) {
162 return false;
165 if ((rule_to_add & kCallNtQueryAttributesFile) &&
166 (!query.AddStringMatch(IF, FileName::NAME, name, CASE_INSENSITIVE) ||
167 !policy->AddRule(IPC_NTQUERYATTRIBUTESFILE_TAG, &query))) {
168 return false;
171 if ((rule_to_add & kCallNtQueryFullAttributesFile) &&
172 (!query_full.AddStringMatch(IF, FileName::NAME, name, CASE_INSENSITIVE)
173 || !policy->AddRule(IPC_NTQUERYFULLATTRIBUTESFILE_TAG,
174 &query_full))) {
175 return false;
178 if ((rule_to_add & kCallNtSetInfoRename) &&
179 (!rename.AddStringMatch(IF, FileName::NAME, name, CASE_INSENSITIVE) ||
180 !policy->AddRule(IPC_NTSETINFO_RENAME_TAG, &rename))) {
181 return false;
184 return true;
187 // Right now we insert two rules, to be evaluated before any user supplied rule:
188 // - go to the broker if the path doesn't look like the paths that we push on
189 // the policy (namely \??\something).
190 // - go to the broker if it looks like this is a short-name path.
192 // It is possible to add a rule to go to the broker in any case; it would look
193 // something like:
194 // rule = new PolicyRule(ASK_BROKER);
195 // rule->AddNumberMatch(IF_NOT, FileName::BROKER, TRUE, AND);
196 // policy->AddRule(service, rule);
197 bool FileSystemPolicy::SetInitialRules(LowLevelPolicy* policy) {
198 PolicyRule format(ASK_BROKER);
199 PolicyRule short_name(ASK_BROKER);
201 bool rv = format.AddNumberMatch(IF_NOT, FileName::BROKER, TRUE, AND);
202 rv &= format.AddStringMatch(IF_NOT, FileName::NAME, L"\\/?/?\\*",
203 CASE_SENSITIVE);
205 rv &= short_name.AddNumberMatch(IF_NOT, FileName::BROKER, TRUE, AND);
206 rv &= short_name.AddStringMatch(IF, FileName::NAME, L"*~*", CASE_SENSITIVE);
208 if (!rv || !policy->AddRule(IPC_NTCREATEFILE_TAG, &format))
209 return false;
211 if (!policy->AddRule(IPC_NTCREATEFILE_TAG, &short_name))
212 return false;
214 if (!policy->AddRule(IPC_NTOPENFILE_TAG, &format))
215 return false;
217 if (!policy->AddRule(IPC_NTOPENFILE_TAG, &short_name))
218 return false;
220 if (!policy->AddRule(IPC_NTQUERYATTRIBUTESFILE_TAG, &format))
221 return false;
223 if (!policy->AddRule(IPC_NTQUERYATTRIBUTESFILE_TAG, &short_name))
224 return false;
226 if (!policy->AddRule(IPC_NTQUERYFULLATTRIBUTESFILE_TAG, &format))
227 return false;
229 if (!policy->AddRule(IPC_NTQUERYFULLATTRIBUTESFILE_TAG, &short_name))
230 return false;
232 if (!policy->AddRule(IPC_NTSETINFO_RENAME_TAG, &format))
233 return false;
235 if (!policy->AddRule(IPC_NTSETINFO_RENAME_TAG, &short_name))
236 return false;
238 return true;
241 bool FileSystemPolicy::CreateFileAction(EvalResult eval_result,
242 const ClientInfo& client_info,
243 const base::string16 &file,
244 uint32 attributes,
245 uint32 desired_access,
246 uint32 file_attributes,
247 uint32 share_access,
248 uint32 create_disposition,
249 uint32 create_options,
250 HANDLE *handle,
251 NTSTATUS* nt_status,
252 ULONG_PTR *io_information) {
253 // The only action supported is ASK_BROKER which means create the requested
254 // file as specified.
255 if (ASK_BROKER != eval_result) {
256 *nt_status = STATUS_ACCESS_DENIED;
257 return false;
259 IO_STATUS_BLOCK io_block = {0};
260 UNICODE_STRING uni_name = {0};
261 OBJECT_ATTRIBUTES obj_attributes = {0};
262 SECURITY_QUALITY_OF_SERVICE security_qos = GetAnonymousQOS();
264 InitObjectAttribs(file, attributes, NULL, &obj_attributes,
265 &uni_name, IsPipe(file) ? &security_qos : NULL);
266 *nt_status = NtCreateFileInTarget(handle, desired_access, &obj_attributes,
267 &io_block, file_attributes, share_access,
268 create_disposition, create_options, NULL,
269 0, client_info.process);
271 *io_information = io_block.Information;
272 return true;
275 bool FileSystemPolicy::OpenFileAction(EvalResult eval_result,
276 const ClientInfo& client_info,
277 const base::string16 &file,
278 uint32 attributes,
279 uint32 desired_access,
280 uint32 share_access,
281 uint32 open_options,
282 HANDLE *handle,
283 NTSTATUS* nt_status,
284 ULONG_PTR *io_information) {
285 // The only action supported is ASK_BROKER which means open the requested
286 // file as specified.
287 if (ASK_BROKER != eval_result) {
288 *nt_status = STATUS_ACCESS_DENIED;
289 return true;
291 // An NtOpen is equivalent to an NtCreate with FileAttributes = 0 and
292 // CreateDisposition = FILE_OPEN.
293 IO_STATUS_BLOCK io_block = {0};
294 UNICODE_STRING uni_name = {0};
295 OBJECT_ATTRIBUTES obj_attributes = {0};
296 SECURITY_QUALITY_OF_SERVICE security_qos = GetAnonymousQOS();
298 InitObjectAttribs(file, attributes, NULL, &obj_attributes,
299 &uni_name, IsPipe(file) ? &security_qos : NULL);
300 *nt_status = NtCreateFileInTarget(handle, desired_access, &obj_attributes,
301 &io_block, 0, share_access, FILE_OPEN,
302 open_options, NULL, 0,
303 client_info.process);
305 *io_information = io_block.Information;
306 return true;
309 bool FileSystemPolicy::QueryAttributesFileAction(
310 EvalResult eval_result,
311 const ClientInfo& client_info,
312 const base::string16 &file,
313 uint32 attributes,
314 FILE_BASIC_INFORMATION* file_info,
315 NTSTATUS* nt_status) {
316 // The only action supported is ASK_BROKER which means query the requested
317 // file as specified.
318 if (ASK_BROKER != eval_result) {
319 *nt_status = STATUS_ACCESS_DENIED;
320 return true;
323 NtQueryAttributesFileFunction NtQueryAttributesFile = NULL;
324 ResolveNTFunctionPtr("NtQueryAttributesFile", &NtQueryAttributesFile);
326 UNICODE_STRING uni_name = {0};
327 OBJECT_ATTRIBUTES obj_attributes = {0};
328 SECURITY_QUALITY_OF_SERVICE security_qos = GetAnonymousQOS();
330 InitObjectAttribs(file, attributes, NULL, &obj_attributes,
331 &uni_name, IsPipe(file) ? &security_qos : NULL);
332 *nt_status = NtQueryAttributesFile(&obj_attributes, file_info);
334 return true;
337 bool FileSystemPolicy::QueryFullAttributesFileAction(
338 EvalResult eval_result,
339 const ClientInfo& client_info,
340 const base::string16 &file,
341 uint32 attributes,
342 FILE_NETWORK_OPEN_INFORMATION* file_info,
343 NTSTATUS* nt_status) {
344 // The only action supported is ASK_BROKER which means query the requested
345 // file as specified.
346 if (ASK_BROKER != eval_result) {
347 *nt_status = STATUS_ACCESS_DENIED;
348 return true;
351 NtQueryFullAttributesFileFunction NtQueryFullAttributesFile = NULL;
352 ResolveNTFunctionPtr("NtQueryFullAttributesFile", &NtQueryFullAttributesFile);
354 UNICODE_STRING uni_name = {0};
355 OBJECT_ATTRIBUTES obj_attributes = {0};
356 SECURITY_QUALITY_OF_SERVICE security_qos = GetAnonymousQOS();
358 InitObjectAttribs(file, attributes, NULL, &obj_attributes,
359 &uni_name, IsPipe(file) ? &security_qos : NULL);
360 *nt_status = NtQueryFullAttributesFile(&obj_attributes, file_info);
362 return true;
365 bool FileSystemPolicy::SetInformationFileAction(
366 EvalResult eval_result, const ClientInfo& client_info,
367 HANDLE target_file_handle, void* file_info, uint32 length,
368 uint32 info_class, IO_STATUS_BLOCK* io_block,
369 NTSTATUS* nt_status) {
370 // The only action supported is ASK_BROKER which means open the requested
371 // file as specified.
372 if (ASK_BROKER != eval_result) {
373 *nt_status = STATUS_ACCESS_DENIED;
374 return true;
377 NtSetInformationFileFunction NtSetInformationFile = NULL;
378 ResolveNTFunctionPtr("NtSetInformationFile", &NtSetInformationFile);
380 HANDLE local_handle = NULL;
381 if (!::DuplicateHandle(client_info.process, target_file_handle,
382 ::GetCurrentProcess(), &local_handle, 0, FALSE,
383 DUPLICATE_SAME_ACCESS)) {
384 *nt_status = STATUS_ACCESS_DENIED;
385 return true;
388 base::win::ScopedHandle handle(local_handle);
390 FILE_INFORMATION_CLASS file_info_class =
391 static_cast<FILE_INFORMATION_CLASS>(info_class);
392 *nt_status = NtSetInformationFile(local_handle, io_block, file_info, length,
393 file_info_class);
395 return true;
398 bool PreProcessName(const base::string16& path, base::string16* new_path) {
399 ConvertToLongPath(path, new_path);
401 bool reparsed = false;
402 if (ERROR_SUCCESS != IsReparsePoint(*new_path, &reparsed))
403 return false;
405 // We can't process reparsed file.
406 return !reparsed;
409 base::string16 FixNTPrefixForMatch(const base::string16& name) {
410 base::string16 mod_name = name;
412 // NT prefix escaped for rule matcher
413 const wchar_t kNTPrefixEscaped[] = L"\\/?/?\\";
414 const int kNTPrefixEscapedLen = arraysize(kNTPrefixEscaped) - 1;
416 if (0 != mod_name.compare(0, kNTPrefixLen, kNTPrefix)) {
417 if (0 != mod_name.compare(0, kNTPrefixEscapedLen, kNTPrefixEscaped)) {
418 // TODO(nsylvain): Find a better way to do name resolution. Right now we
419 // take the name and we expand it.
420 mod_name.insert(0, kNTPrefixEscaped);
422 } else {
423 // Start of name matches NT prefix, replace with escaped format
424 // Fixes bug: 334882
425 mod_name.replace(0, kNTPrefixLen, kNTPrefixEscaped);
428 return mod_name;
431 } // namespace sandbox