• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.
4 
5 #include <string>
6 
7 #include "sandbox/win/src/filesystem_policy.h"
8 
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"
17 
18 namespace {
19 
NtCreateFileInTarget(HANDLE * target_file_handle,ACCESS_MASK desired_access,OBJECT_ATTRIBUTES * obj_attributes,IO_STATUS_BLOCK * io_status_block,ULONG file_attributes,ULONG share_access,ULONG create_disposition,ULONG create_options,PVOID ea_buffer,ULONG ea_lenght,HANDLE target_process)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);
33 
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;
41   }
42 
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;
47   }
48 
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;
53   }
54   return STATUS_SUCCESS;
55 }
56 
57 }  // namespace.
58 
59 namespace sandbox {
60 
GenerateRules(const wchar_t * name,TargetPolicy::Semantics semantics,LowLevelPolicy * policy)61 bool FileSystemPolicy::GenerateRules(const wchar_t* name,
62                                      TargetPolicy::Semantics semantics,
63                                      LowLevelPolicy* policy) {
64   base::string16 mod_name(name);
65   if (mod_name.empty()) {
66     return false;
67   }
68 
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
74     // question marks.
75     if (!PreProcessName(mod_name, &mod_name)) {
76       // The path to be added might contain a reparse point.
77       NOTREACHED();
78       return false;
79     }
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();
85     }
86   }
87 
88   EvalResult result = ASK_BROKER;
89 
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;
96 
97   DWORD  rule_to_add = kCallNtOpenFile | kCallNtCreateFile |
98                        kCallNtQueryAttributesFile |
99                        kCallNtQueryFullAttributesFile | kCallNtSetInfoRename;
100 
101   PolicyRule create(result);
102   PolicyRule open(result);
103   PolicyRule query(result);
104   PolicyRule query_full(result);
105   PolicyRule rename(result);
106 
107   switch (semantics) {
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);
111       break;
112     }
113     case TargetPolicy::FILES_ALLOW_READONLY: {
114       // We consider all flags that are not known to be readonly as potentially
115       // used for write.
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);
122 
123       // Read only access don't work for rename.
124       rule_to_add &= ~kCallNtSetInfoRename;
125       break;
126     }
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);
131       break;
132     }
133     case TargetPolicy::FILES_ALLOW_ANY: {
134       break;
135     }
136     default: {
137       NOTREACHED();
138       return false;
139     }
140   }
141 
142   if ((rule_to_add & kCallNtCreateFile) &&
143       (!create.AddStringMatch(IF, OpenFile::NAME, name, CASE_INSENSITIVE) ||
144        !policy->AddRule(IPC_NTCREATEFILE_TAG, &create))) {
145     return false;
146   }
147 
148   if ((rule_to_add & kCallNtOpenFile) &&
149       (!open.AddStringMatch(IF, OpenFile::NAME, name, CASE_INSENSITIVE) ||
150        !policy->AddRule(IPC_NTOPENFILE_TAG, &open))) {
151     return false;
152   }
153 
154   if ((rule_to_add & kCallNtQueryAttributesFile) &&
155       (!query.AddStringMatch(IF, FileName::NAME, name, CASE_INSENSITIVE) ||
156        !policy->AddRule(IPC_NTQUERYATTRIBUTESFILE_TAG, &query))) {
157     return false;
158   }
159 
160   if ((rule_to_add & kCallNtQueryFullAttributesFile) &&
161       (!query_full.AddStringMatch(IF, FileName::NAME, name, CASE_INSENSITIVE)
162        || !policy->AddRule(IPC_NTQUERYFULLATTRIBUTESFILE_TAG,
163                            &query_full))) {
164     return false;
165   }
166 
167   if ((rule_to_add & kCallNtSetInfoRename) &&
168       (!rename.AddStringMatch(IF, FileName::NAME, name, CASE_INSENSITIVE) ||
169        !policy->AddRule(IPC_NTSETINFO_RENAME_TAG, &rename))) {
170     return false;
171   }
172 
173   return true;
174 }
175 
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.
180 //
181 // It is possible to add a rule to go to the broker in any case; it would look
182 // something like:
183 //    rule = new PolicyRule(ASK_BROKER);
184 //    rule->AddNumberMatch(IF_NOT, FileName::BROKER, TRUE, AND);
185 //    policy->AddRule(service, rule);
SetInitialRules(LowLevelPolicy * policy)186 bool FileSystemPolicy::SetInitialRules(LowLevelPolicy* policy) {
187   PolicyRule format(ASK_BROKER);
188   PolicyRule short_name(ASK_BROKER);
189 
190   bool rv = format.AddNumberMatch(IF_NOT, FileName::BROKER, TRUE, AND);
191   rv &= format.AddStringMatch(IF_NOT, FileName::NAME, L"\\/?/?\\*",
192                               CASE_SENSITIVE);
193 
194   rv &= short_name.AddNumberMatch(IF_NOT, FileName::BROKER, TRUE, AND);
195   rv &= short_name.AddStringMatch(IF, FileName::NAME, L"*~*", CASE_SENSITIVE);
196 
197   if (!rv || !policy->AddRule(IPC_NTCREATEFILE_TAG, &format))
198     return false;
199 
200   if (!policy->AddRule(IPC_NTCREATEFILE_TAG, &short_name))
201     return false;
202 
203   if (!policy->AddRule(IPC_NTOPENFILE_TAG, &format))
204     return false;
205 
206   if (!policy->AddRule(IPC_NTOPENFILE_TAG, &short_name))
207     return false;
208 
209   if (!policy->AddRule(IPC_NTQUERYATTRIBUTESFILE_TAG, &format))
210     return false;
211 
212   if (!policy->AddRule(IPC_NTQUERYATTRIBUTESFILE_TAG, &short_name))
213     return false;
214 
215   if (!policy->AddRule(IPC_NTQUERYFULLATTRIBUTESFILE_TAG, &format))
216     return false;
217 
218   if (!policy->AddRule(IPC_NTQUERYFULLATTRIBUTESFILE_TAG, &short_name))
219     return false;
220 
221   if (!policy->AddRule(IPC_NTSETINFO_RENAME_TAG, &format))
222     return false;
223 
224   if (!policy->AddRule(IPC_NTSETINFO_RENAME_TAG, &short_name))
225     return false;
226 
227   return true;
228 }
229 
CreateFileAction(EvalResult eval_result,const ClientInfo & client_info,const base::string16 & file,uint32 attributes,uint32 desired_access,uint32 file_attributes,uint32 share_access,uint32 create_disposition,uint32 create_options,HANDLE * handle,NTSTATUS * nt_status,ULONG_PTR * io_information)230 bool FileSystemPolicy::CreateFileAction(EvalResult eval_result,
231                                         const ClientInfo& client_info,
232                                         const base::string16 &file,
233                                         uint32 attributes,
234                                         uint32 desired_access,
235                                         uint32 file_attributes,
236                                         uint32 share_access,
237                                         uint32 create_disposition,
238                                         uint32 create_options,
239                                         HANDLE *handle,
240                                         NTSTATUS* nt_status,
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;
246     return false;
247   }
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);
256 
257   *io_information = io_block.Information;
258   return true;
259 }
260 
OpenFileAction(EvalResult eval_result,const ClientInfo & client_info,const base::string16 & file,uint32 attributes,uint32 desired_access,uint32 share_access,uint32 open_options,HANDLE * handle,NTSTATUS * nt_status,ULONG_PTR * io_information)261 bool FileSystemPolicy::OpenFileAction(EvalResult eval_result,
262                                       const ClientInfo& client_info,
263                                       const base::string16 &file,
264                                       uint32 attributes,
265                                       uint32 desired_access,
266                                       uint32 share_access,
267                                       uint32 open_options,
268                                       HANDLE *handle,
269                                       NTSTATUS* nt_status,
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;
275     return true;
276   }
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);
287 
288   *io_information = io_block.Information;
289   return true;
290 }
291 
QueryAttributesFileAction(EvalResult eval_result,const ClientInfo & client_info,const base::string16 & file,uint32 attributes,FILE_BASIC_INFORMATION * file_info,NTSTATUS * nt_status)292 bool FileSystemPolicy::QueryAttributesFileAction(
293     EvalResult eval_result,
294     const ClientInfo& client_info,
295     const base::string16 &file,
296     uint32 attributes,
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;
303     return true;
304   }
305 
306   NtQueryAttributesFileFunction NtQueryAttributesFile = NULL;
307   ResolveNTFunctionPtr("NtQueryAttributesFile", &NtQueryAttributesFile);
308 
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);
313 
314   return true;
315 }
316 
QueryFullAttributesFileAction(EvalResult eval_result,const ClientInfo & client_info,const base::string16 & file,uint32 attributes,FILE_NETWORK_OPEN_INFORMATION * file_info,NTSTATUS * nt_status)317 bool FileSystemPolicy::QueryFullAttributesFileAction(
318     EvalResult eval_result,
319     const ClientInfo& client_info,
320     const base::string16 &file,
321     uint32 attributes,
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;
328     return true;
329   }
330 
331   NtQueryFullAttributesFileFunction NtQueryFullAttributesFile = NULL;
332   ResolveNTFunctionPtr("NtQueryFullAttributesFile", &NtQueryFullAttributesFile);
333 
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);
338 
339   return true;
340 }
341 
SetInformationFileAction(EvalResult eval_result,const ClientInfo & client_info,HANDLE target_file_handle,void * file_info,uint32 length,uint32 info_class,IO_STATUS_BLOCK * io_block,NTSTATUS * nt_status)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;
351     return true;
352   }
353 
354   NtSetInformationFileFunction NtSetInformationFile = NULL;
355   ResolveNTFunctionPtr("NtSetInformationFile", &NtSetInformationFile);
356 
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;
362     return true;
363   }
364 
365   base::win::ScopedHandle handle(local_handle);
366 
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,
370                                     file_info_class);
371 
372   return true;
373 }
374 
PreProcessName(const base::string16 & path,base::string16 * new_path)375 bool PreProcessName(const base::string16& path, base::string16* new_path) {
376   ConvertToLongPath(path, new_path);
377 
378   bool reparsed = false;
379   if (ERROR_SUCCESS != IsReparsePoint(*new_path, &reparsed))
380     return false;
381 
382   // We can't process reparsed file.
383   return !reparsed;
384 }
385 
386 }  // namespace sandbox
387