filesystem_policy.cc revision 5821806d5e7f356e8fa4b058a389a808ea183019
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 18namespace { 19 20NTSTATUS 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 60namespace sandbox { 61 62bool 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); 187bool 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 231bool 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 262bool 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 293bool 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 318bool 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 343bool 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 376bool 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 388