OLD | NEW |
| (Empty) |
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/src/filesystem_policy.h" | |
8 | |
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" | |
17 | |
18 namespace { | |
19 | |
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 ::CloseHandle(local_handle); | |
53 return STATUS_ACCESS_DENIED; | |
54 } | |
55 return STATUS_SUCCESS; | |
56 } | |
57 | |
58 } // namespace. | |
59 | |
60 namespace sandbox { | |
61 | |
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()) { | |
67 return false; | |
68 } | |
69 | |
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 | |
75 // question marks. | |
76 if (!PreProcessName(mod_name, &mod_name)) { | |
77 // The path to be added might contain a reparse point. | |
78 NOTREACHED(); | |
79 return false; | |
80 } | |
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(); | |
86 } | |
87 } | |
88 | |
89 EvalResult result = ASK_BROKER; | |
90 | |
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; | |
97 | |
98 DWORD rule_to_add = kCallNtOpenFile | kCallNtCreateFile | | |
99 kCallNtQueryAttributesFile | | |
100 kCallNtQueryFullAttributesFile | kCallNtSetInfoRename; | |
101 | |
102 PolicyRule create(result); | |
103 PolicyRule open(result); | |
104 PolicyRule query(result); | |
105 PolicyRule query_full(result); | |
106 PolicyRule rename(result); | |
107 | |
108 switch (semantics) { | |
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); | |
112 break; | |
113 } | |
114 case TargetPolicy::FILES_ALLOW_READONLY: { | |
115 // We consider all flags that are not known to be readonly as potentially | |
116 // used for write. | |
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); | |
123 | |
124 // Read only access don't work for rename. | |
125 rule_to_add &= ~kCallNtSetInfoRename; | |
126 break; | |
127 } | |
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); | |
132 break; | |
133 } | |
134 case TargetPolicy::FILES_ALLOW_ANY: { | |
135 break; | |
136 } | |
137 default: { | |
138 NOTREACHED(); | |
139 return false; | |
140 } | |
141 } | |
142 | |
143 if ((rule_to_add & kCallNtCreateFile) && | |
144 (!create.AddStringMatch(IF, OpenFile::NAME, name, CASE_INSENSITIVE) || | |
145 !policy->AddRule(IPC_NTCREATEFILE_TAG, &create))) { | |
146 return false; | |
147 } | |
148 | |
149 if ((rule_to_add & kCallNtOpenFile) && | |
150 (!open.AddStringMatch(IF, OpenFile::NAME, name, CASE_INSENSITIVE) || | |
151 !policy->AddRule(IPC_NTOPENFILE_TAG, &open))) { | |
152 return false; | |
153 } | |
154 | |
155 if ((rule_to_add & kCallNtQueryAttributesFile) && | |
156 (!query.AddStringMatch(IF, FileName::NAME, name, CASE_INSENSITIVE) || | |
157 !policy->AddRule(IPC_NTQUERYATTRIBUTESFILE_TAG, &query))) { | |
158 return false; | |
159 } | |
160 | |
161 if ((rule_to_add & kCallNtQueryFullAttributesFile) && | |
162 (!query_full.AddStringMatch(IF, FileName::NAME, name, CASE_INSENSITIVE) | |
163 || !policy->AddRule(IPC_NTQUERYFULLATTRIBUTESFILE_TAG, | |
164 &query_full))) { | |
165 return false; | |
166 } | |
167 | |
168 if ((rule_to_add & kCallNtSetInfoRename) && | |
169 (!rename.AddStringMatch(IF, FileName::NAME, name, CASE_INSENSITIVE) || | |
170 !policy->AddRule(IPC_NTSETINFO_RENAME_TAG, &rename))) { | |
171 return false; | |
172 } | |
173 | |
174 return true; | |
175 } | |
176 | |
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. | |
181 // | |
182 // It is possible to add a rule to go to the broker in any case; it would look | |
183 // something like: | |
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); | |
190 | |
191 bool rv = format.AddNumberMatch(IF_NOT, FileName::BROKER, TRUE, AND); | |
192 rv &= format.AddStringMatch(IF_NOT, FileName::NAME, L"\\/?/?\\*", | |
193 CASE_SENSITIVE); | |
194 | |
195 rv &= short_name.AddNumberMatch(IF_NOT, FileName::BROKER, TRUE, AND); | |
196 rv &= short_name.AddStringMatch(IF, FileName::NAME, L"*~*", CASE_SENSITIVE); | |
197 | |
198 if (!rv || !policy->AddRule(IPC_NTCREATEFILE_TAG, &format)) | |
199 return false; | |
200 | |
201 if (!policy->AddRule(IPC_NTCREATEFILE_TAG, &short_name)) | |
202 return false; | |
203 | |
204 if (!policy->AddRule(IPC_NTOPENFILE_TAG, &format)) | |
205 return false; | |
206 | |
207 if (!policy->AddRule(IPC_NTOPENFILE_TAG, &short_name)) | |
208 return false; | |
209 | |
210 if (!policy->AddRule(IPC_NTQUERYATTRIBUTESFILE_TAG, &format)) | |
211 return false; | |
212 | |
213 if (!policy->AddRule(IPC_NTQUERYATTRIBUTESFILE_TAG, &short_name)) | |
214 return false; | |
215 | |
216 if (!policy->AddRule(IPC_NTQUERYFULLATTRIBUTESFILE_TAG, &format)) | |
217 return false; | |
218 | |
219 if (!policy->AddRule(IPC_NTQUERYFULLATTRIBUTESFILE_TAG, &short_name)) | |
220 return false; | |
221 | |
222 if (!policy->AddRule(IPC_NTSETINFO_RENAME_TAG, &format)) | |
223 return false; | |
224 | |
225 if (!policy->AddRule(IPC_NTSETINFO_RENAME_TAG, &short_name)) | |
226 return false; | |
227 | |
228 return true; | |
229 } | |
230 | |
231 bool FileSystemPolicy::CreateFileAction(EvalResult eval_result, | |
232 const ClientInfo& client_info, | |
233 const std::wstring &file, | |
234 uint32 attributes, | |
235 uint32 desired_access, | |
236 uint32 file_attributes, | |
237 uint32 share_access, | |
238 uint32 create_disposition, | |
239 uint32 create_options, | |
240 HANDLE *handle, | |
241 NTSTATUS* nt_status, | |
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; | |
247 return false; | |
248 } | |
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); | |
257 | |
258 *io_information = io_block.Information; | |
259 return true; | |
260 } | |
261 | |
262 bool FileSystemPolicy::OpenFileAction(EvalResult eval_result, | |
263 const ClientInfo& client_info, | |
264 const std::wstring &file, | |
265 uint32 attributes, | |
266 uint32 desired_access, | |
267 uint32 share_access, | |
268 uint32 open_options, | |
269 HANDLE *handle, | |
270 NTSTATUS* nt_status, | |
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; | |
276 return true; | |
277 } | |
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); | |
288 | |
289 *io_information = io_block.Information; | |
290 return true; | |
291 } | |
292 | |
293 bool FileSystemPolicy::QueryAttributesFileAction( | |
294 EvalResult eval_result, | |
295 const ClientInfo& client_info, | |
296 const std::wstring &file, | |
297 uint32 attributes, | |
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; | |
304 return true; | |
305 } | |
306 | |
307 NtQueryAttributesFileFunction NtQueryAttributesFile = NULL; | |
308 ResolveNTFunctionPtr("NtQueryAttributesFile", &NtQueryAttributesFile); | |
309 | |
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); | |
314 | |
315 return true; | |
316 } | |
317 | |
318 bool FileSystemPolicy::QueryFullAttributesFileAction( | |
319 EvalResult eval_result, | |
320 const ClientInfo& client_info, | |
321 const std::wstring &file, | |
322 uint32 attributes, | |
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; | |
329 return true; | |
330 } | |
331 | |
332 NtQueryFullAttributesFileFunction NtQueryFullAttributesFile = NULL; | |
333 ResolveNTFunctionPtr("NtQueryFullAttributesFile", &NtQueryFullAttributesFile); | |
334 | |
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); | |
339 | |
340 return true; | |
341 } | |
342 | |
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; | |
352 return true; | |
353 } | |
354 | |
355 NtSetInformationFileFunction NtSetInformationFile = NULL; | |
356 ResolveNTFunctionPtr("NtSetInformationFile", &NtSetInformationFile); | |
357 | |
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; | |
363 return true; | |
364 } | |
365 | |
366 base::win::ScopedHandle handle(local_handle); | |
367 | |
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, | |
371 file_info_class); | |
372 | |
373 return true; | |
374 } | |
375 | |
376 bool PreProcessName(const std::wstring& path, std::wstring* new_path) { | |
377 ConvertToLongPath(path, new_path); | |
378 | |
379 bool reparsed = false; | |
380 if (ERROR_SUCCESS != IsReparsePoint(*new_path, &reparsed)) | |
381 return false; | |
382 | |
383 // We can't process reparsed file. | |
384 return !reparsed; | |
385 } | |
386 | |
387 } // namespace sandbox | |
OLD | NEW |