security/sandbox/win/src/filesystem_policy.cc

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

michael@0 1 // Copyright (c) 2011 The Chromium Authors. All rights reserved.
michael@0 2 // Use of this source code is governed by a BSD-style license that can be
michael@0 3 // found in the LICENSE file.
michael@0 4
michael@0 5 #include <string>
michael@0 6
michael@0 7 #include "sandbox/win/src/filesystem_policy.h"
michael@0 8
michael@0 9 #include "base/logging.h"
michael@0 10 #include "base/win/scoped_handle.h"
michael@0 11 #include "sandbox/win/src/ipc_tags.h"
michael@0 12 #include "sandbox/win/src/policy_engine_opcodes.h"
michael@0 13 #include "sandbox/win/src/policy_params.h"
michael@0 14 #include "sandbox/win/src/sandbox_utils.h"
michael@0 15 #include "sandbox/win/src/sandbox_types.h"
michael@0 16 #include "sandbox/win/src/win_utils.h"
michael@0 17
michael@0 18 namespace {
michael@0 19
michael@0 20 NTSTATUS NtCreateFileInTarget(HANDLE* target_file_handle,
michael@0 21 ACCESS_MASK desired_access,
michael@0 22 OBJECT_ATTRIBUTES* obj_attributes,
michael@0 23 IO_STATUS_BLOCK* io_status_block,
michael@0 24 ULONG file_attributes,
michael@0 25 ULONG share_access,
michael@0 26 ULONG create_disposition,
michael@0 27 ULONG create_options,
michael@0 28 PVOID ea_buffer,
michael@0 29 ULONG ea_lenght,
michael@0 30 HANDLE target_process) {
michael@0 31 NtCreateFileFunction NtCreateFile = NULL;
michael@0 32 ResolveNTFunctionPtr("NtCreateFile", &NtCreateFile);
michael@0 33
michael@0 34 HANDLE local_handle = INVALID_HANDLE_VALUE;
michael@0 35 NTSTATUS status = NtCreateFile(&local_handle, desired_access, obj_attributes,
michael@0 36 io_status_block, NULL, file_attributes,
michael@0 37 share_access, create_disposition,
michael@0 38 create_options, ea_buffer, ea_lenght);
michael@0 39 if (!NT_SUCCESS(status)) {
michael@0 40 return status;
michael@0 41 }
michael@0 42
michael@0 43 if (!sandbox::SameObject(local_handle, obj_attributes->ObjectName->Buffer)) {
michael@0 44 // The handle points somewhere else. Fail the operation.
michael@0 45 ::CloseHandle(local_handle);
michael@0 46 return STATUS_ACCESS_DENIED;
michael@0 47 }
michael@0 48
michael@0 49 if (!::DuplicateHandle(::GetCurrentProcess(), local_handle,
michael@0 50 target_process, target_file_handle, 0, FALSE,
michael@0 51 DUPLICATE_CLOSE_SOURCE | DUPLICATE_SAME_ACCESS)) {
michael@0 52 return STATUS_ACCESS_DENIED;
michael@0 53 }
michael@0 54 return STATUS_SUCCESS;
michael@0 55 }
michael@0 56
michael@0 57 } // namespace.
michael@0 58
michael@0 59 namespace sandbox {
michael@0 60
michael@0 61 bool FileSystemPolicy::GenerateRules(const wchar_t* name,
michael@0 62 TargetPolicy::Semantics semantics,
michael@0 63 LowLevelPolicy* policy) {
michael@0 64 std::wstring mod_name(name);
michael@0 65 if (mod_name.empty()) {
michael@0 66 return false;
michael@0 67 }
michael@0 68
michael@0 69 // Don't do any pre-processing if the name starts like the the native
michael@0 70 // object manager style.
michael@0 71 if (0 != _wcsnicmp(mod_name.c_str(), kNTObjManPrefix, kNTObjManPrefixLen)) {
michael@0 72 // TODO(cpu) bug 32224: This prefix add is a hack because we don't have the
michael@0 73 // infrastructure to normalize names. In any case we need to escape the
michael@0 74 // question marks.
michael@0 75 if (!PreProcessName(mod_name, &mod_name)) {
michael@0 76 // The path to be added might contain a reparse point.
michael@0 77 NOTREACHED();
michael@0 78 return false;
michael@0 79 }
michael@0 80 if (0 != mod_name.compare(0, kNTPrefixLen, kNTPrefix)) {
michael@0 81 // TODO(nsylvain): Find a better way to do name resolution. Right now we
michael@0 82 // take the name and we expand it.
michael@0 83 mod_name.insert(0, L"\\/?/?\\");
michael@0 84 name = mod_name.c_str();
michael@0 85 }
michael@0 86 }
michael@0 87
michael@0 88 EvalResult result = ASK_BROKER;
michael@0 89
michael@0 90 // List of supported calls for the filesystem.
michael@0 91 const unsigned kCallNtCreateFile = 0x1;
michael@0 92 const unsigned kCallNtOpenFile = 0x2;
michael@0 93 const unsigned kCallNtQueryAttributesFile = 0x4;
michael@0 94 const unsigned kCallNtQueryFullAttributesFile = 0x8;
michael@0 95 const unsigned kCallNtSetInfoRename = 0x10;
michael@0 96
michael@0 97 DWORD rule_to_add = kCallNtOpenFile | kCallNtCreateFile |
michael@0 98 kCallNtQueryAttributesFile |
michael@0 99 kCallNtQueryFullAttributesFile | kCallNtSetInfoRename;
michael@0 100
michael@0 101 PolicyRule create(result);
michael@0 102 PolicyRule open(result);
michael@0 103 PolicyRule query(result);
michael@0 104 PolicyRule query_full(result);
michael@0 105 PolicyRule rename(result);
michael@0 106
michael@0 107 switch (semantics) {
michael@0 108 case TargetPolicy::FILES_ALLOW_DIR_ANY: {
michael@0 109 open.AddNumberMatch(IF, OpenFile::OPTIONS, FILE_DIRECTORY_FILE, AND);
michael@0 110 create.AddNumberMatch(IF, OpenFile::OPTIONS, FILE_DIRECTORY_FILE, AND);
michael@0 111 break;
michael@0 112 }
michael@0 113 case TargetPolicy::FILES_ALLOW_READONLY: {
michael@0 114 // We consider all flags that are not known to be readonly as potentially
michael@0 115 // used for write.
michael@0 116 DWORD allowed_flags = FILE_READ_DATA | FILE_READ_ATTRIBUTES |
michael@0 117 FILE_READ_EA | SYNCHRONIZE | FILE_EXECUTE |
michael@0 118 GENERIC_READ | GENERIC_EXECUTE | READ_CONTROL;
michael@0 119 DWORD restricted_flags = ~allowed_flags;
michael@0 120 open.AddNumberMatch(IF_NOT, OpenFile::ACCESS, restricted_flags, AND);
michael@0 121 create.AddNumberMatch(IF_NOT, OpenFile::ACCESS, restricted_flags, AND);
michael@0 122
michael@0 123 // Read only access don't work for rename.
michael@0 124 rule_to_add &= ~kCallNtSetInfoRename;
michael@0 125 break;
michael@0 126 }
michael@0 127 case TargetPolicy::FILES_ALLOW_QUERY: {
michael@0 128 // Here we don't want to add policy for the open or the create.
michael@0 129 rule_to_add &= ~(kCallNtOpenFile | kCallNtCreateFile |
michael@0 130 kCallNtSetInfoRename);
michael@0 131 break;
michael@0 132 }
michael@0 133 case TargetPolicy::FILES_ALLOW_ANY: {
michael@0 134 break;
michael@0 135 }
michael@0 136 default: {
michael@0 137 NOTREACHED();
michael@0 138 return false;
michael@0 139 }
michael@0 140 }
michael@0 141
michael@0 142 if ((rule_to_add & kCallNtCreateFile) &&
michael@0 143 (!create.AddStringMatch(IF, OpenFile::NAME, name, CASE_INSENSITIVE) ||
michael@0 144 !policy->AddRule(IPC_NTCREATEFILE_TAG, &create))) {
michael@0 145 return false;
michael@0 146 }
michael@0 147
michael@0 148 if ((rule_to_add & kCallNtOpenFile) &&
michael@0 149 (!open.AddStringMatch(IF, OpenFile::NAME, name, CASE_INSENSITIVE) ||
michael@0 150 !policy->AddRule(IPC_NTOPENFILE_TAG, &open))) {
michael@0 151 return false;
michael@0 152 }
michael@0 153
michael@0 154 if ((rule_to_add & kCallNtQueryAttributesFile) &&
michael@0 155 (!query.AddStringMatch(IF, FileName::NAME, name, CASE_INSENSITIVE) ||
michael@0 156 !policy->AddRule(IPC_NTQUERYATTRIBUTESFILE_TAG, &query))) {
michael@0 157 return false;
michael@0 158 }
michael@0 159
michael@0 160 if ((rule_to_add & kCallNtQueryFullAttributesFile) &&
michael@0 161 (!query_full.AddStringMatch(IF, FileName::NAME, name, CASE_INSENSITIVE)
michael@0 162 || !policy->AddRule(IPC_NTQUERYFULLATTRIBUTESFILE_TAG,
michael@0 163 &query_full))) {
michael@0 164 return false;
michael@0 165 }
michael@0 166
michael@0 167 if ((rule_to_add & kCallNtSetInfoRename) &&
michael@0 168 (!rename.AddStringMatch(IF, FileName::NAME, name, CASE_INSENSITIVE) ||
michael@0 169 !policy->AddRule(IPC_NTSETINFO_RENAME_TAG, &rename))) {
michael@0 170 return false;
michael@0 171 }
michael@0 172
michael@0 173 return true;
michael@0 174 }
michael@0 175
michael@0 176 // Right now we insert two rules, to be evaluated before any user supplied rule:
michael@0 177 // - go to the broker if the path doesn't look like the paths that we push on
michael@0 178 // the policy (namely \??\something).
michael@0 179 // - go to the broker if it looks like this is a short-name path.
michael@0 180 //
michael@0 181 // It is possible to add a rule to go to the broker in any case; it would look
michael@0 182 // something like:
michael@0 183 // rule = new PolicyRule(ASK_BROKER);
michael@0 184 // rule->AddNumberMatch(IF_NOT, FileName::BROKER, TRUE, AND);
michael@0 185 // policy->AddRule(service, rule);
michael@0 186 bool FileSystemPolicy::SetInitialRules(LowLevelPolicy* policy) {
michael@0 187 PolicyRule format(ASK_BROKER);
michael@0 188 PolicyRule short_name(ASK_BROKER);
michael@0 189
michael@0 190 bool rv = format.AddNumberMatch(IF_NOT, FileName::BROKER, TRUE, AND);
michael@0 191 rv &= format.AddStringMatch(IF_NOT, FileName::NAME, L"\\/?/?\\*",
michael@0 192 CASE_SENSITIVE);
michael@0 193
michael@0 194 rv &= short_name.AddNumberMatch(IF_NOT, FileName::BROKER, TRUE, AND);
michael@0 195 rv &= short_name.AddStringMatch(IF, FileName::NAME, L"*~*", CASE_SENSITIVE);
michael@0 196
michael@0 197 if (!rv || !policy->AddRule(IPC_NTCREATEFILE_TAG, &format))
michael@0 198 return false;
michael@0 199
michael@0 200 if (!policy->AddRule(IPC_NTCREATEFILE_TAG, &short_name))
michael@0 201 return false;
michael@0 202
michael@0 203 if (!policy->AddRule(IPC_NTOPENFILE_TAG, &format))
michael@0 204 return false;
michael@0 205
michael@0 206 if (!policy->AddRule(IPC_NTOPENFILE_TAG, &short_name))
michael@0 207 return false;
michael@0 208
michael@0 209 if (!policy->AddRule(IPC_NTQUERYATTRIBUTESFILE_TAG, &format))
michael@0 210 return false;
michael@0 211
michael@0 212 if (!policy->AddRule(IPC_NTQUERYATTRIBUTESFILE_TAG, &short_name))
michael@0 213 return false;
michael@0 214
michael@0 215 if (!policy->AddRule(IPC_NTQUERYFULLATTRIBUTESFILE_TAG, &format))
michael@0 216 return false;
michael@0 217
michael@0 218 if (!policy->AddRule(IPC_NTQUERYFULLATTRIBUTESFILE_TAG, &short_name))
michael@0 219 return false;
michael@0 220
michael@0 221 if (!policy->AddRule(IPC_NTSETINFO_RENAME_TAG, &format))
michael@0 222 return false;
michael@0 223
michael@0 224 if (!policy->AddRule(IPC_NTSETINFO_RENAME_TAG, &short_name))
michael@0 225 return false;
michael@0 226
michael@0 227 return true;
michael@0 228 }
michael@0 229
michael@0 230 bool FileSystemPolicy::CreateFileAction(EvalResult eval_result,
michael@0 231 const ClientInfo& client_info,
michael@0 232 const std::wstring &file,
michael@0 233 uint32 attributes,
michael@0 234 uint32 desired_access,
michael@0 235 uint32 file_attributes,
michael@0 236 uint32 share_access,
michael@0 237 uint32 create_disposition,
michael@0 238 uint32 create_options,
michael@0 239 HANDLE *handle,
michael@0 240 NTSTATUS* nt_status,
michael@0 241 ULONG_PTR *io_information) {
michael@0 242 // The only action supported is ASK_BROKER which means create the requested
michael@0 243 // file as specified.
michael@0 244 if (ASK_BROKER != eval_result) {
michael@0 245 *nt_status = STATUS_ACCESS_DENIED;
michael@0 246 return false;
michael@0 247 }
michael@0 248 IO_STATUS_BLOCK io_block = {0};
michael@0 249 UNICODE_STRING uni_name = {0};
michael@0 250 OBJECT_ATTRIBUTES obj_attributes = {0};
michael@0 251 InitObjectAttribs(file, attributes, NULL, &obj_attributes, &uni_name);
michael@0 252 *nt_status = NtCreateFileInTarget(handle, desired_access, &obj_attributes,
michael@0 253 &io_block, file_attributes, share_access,
michael@0 254 create_disposition, create_options, NULL,
michael@0 255 0, client_info.process);
michael@0 256
michael@0 257 *io_information = io_block.Information;
michael@0 258 return true;
michael@0 259 }
michael@0 260
michael@0 261 bool FileSystemPolicy::OpenFileAction(EvalResult eval_result,
michael@0 262 const ClientInfo& client_info,
michael@0 263 const std::wstring &file,
michael@0 264 uint32 attributes,
michael@0 265 uint32 desired_access,
michael@0 266 uint32 share_access,
michael@0 267 uint32 open_options,
michael@0 268 HANDLE *handle,
michael@0 269 NTSTATUS* nt_status,
michael@0 270 ULONG_PTR *io_information) {
michael@0 271 // The only action supported is ASK_BROKER which means open the requested
michael@0 272 // file as specified.
michael@0 273 if (ASK_BROKER != eval_result) {
michael@0 274 *nt_status = STATUS_ACCESS_DENIED;
michael@0 275 return true;
michael@0 276 }
michael@0 277 // An NtOpen is equivalent to an NtCreate with FileAttributes = 0 and
michael@0 278 // CreateDisposition = FILE_OPEN.
michael@0 279 IO_STATUS_BLOCK io_block = {0};
michael@0 280 UNICODE_STRING uni_name = {0};
michael@0 281 OBJECT_ATTRIBUTES obj_attributes = {0};
michael@0 282 InitObjectAttribs(file, attributes, NULL, &obj_attributes, &uni_name);
michael@0 283 *nt_status = NtCreateFileInTarget(handle, desired_access, &obj_attributes,
michael@0 284 &io_block, 0, share_access, FILE_OPEN,
michael@0 285 open_options, NULL, 0,
michael@0 286 client_info.process);
michael@0 287
michael@0 288 *io_information = io_block.Information;
michael@0 289 return true;
michael@0 290 }
michael@0 291
michael@0 292 bool FileSystemPolicy::QueryAttributesFileAction(
michael@0 293 EvalResult eval_result,
michael@0 294 const ClientInfo& client_info,
michael@0 295 const std::wstring &file,
michael@0 296 uint32 attributes,
michael@0 297 FILE_BASIC_INFORMATION* file_info,
michael@0 298 NTSTATUS* nt_status) {
michael@0 299 // The only action supported is ASK_BROKER which means query the requested
michael@0 300 // file as specified.
michael@0 301 if (ASK_BROKER != eval_result) {
michael@0 302 *nt_status = STATUS_ACCESS_DENIED;
michael@0 303 return true;
michael@0 304 }
michael@0 305
michael@0 306 NtQueryAttributesFileFunction NtQueryAttributesFile = NULL;
michael@0 307 ResolveNTFunctionPtr("NtQueryAttributesFile", &NtQueryAttributesFile);
michael@0 308
michael@0 309 UNICODE_STRING uni_name = {0};
michael@0 310 OBJECT_ATTRIBUTES obj_attributes = {0};
michael@0 311 InitObjectAttribs(file, attributes, NULL, &obj_attributes, &uni_name);
michael@0 312 *nt_status = NtQueryAttributesFile(&obj_attributes, file_info);
michael@0 313
michael@0 314 return true;
michael@0 315 }
michael@0 316
michael@0 317 bool FileSystemPolicy::QueryFullAttributesFileAction(
michael@0 318 EvalResult eval_result,
michael@0 319 const ClientInfo& client_info,
michael@0 320 const std::wstring &file,
michael@0 321 uint32 attributes,
michael@0 322 FILE_NETWORK_OPEN_INFORMATION* file_info,
michael@0 323 NTSTATUS* nt_status) {
michael@0 324 // The only action supported is ASK_BROKER which means query the requested
michael@0 325 // file as specified.
michael@0 326 if (ASK_BROKER != eval_result) {
michael@0 327 *nt_status = STATUS_ACCESS_DENIED;
michael@0 328 return true;
michael@0 329 }
michael@0 330
michael@0 331 NtQueryFullAttributesFileFunction NtQueryFullAttributesFile = NULL;
michael@0 332 ResolveNTFunctionPtr("NtQueryFullAttributesFile", &NtQueryFullAttributesFile);
michael@0 333
michael@0 334 UNICODE_STRING uni_name = {0};
michael@0 335 OBJECT_ATTRIBUTES obj_attributes = {0};
michael@0 336 InitObjectAttribs(file, attributes, NULL, &obj_attributes, &uni_name);
michael@0 337 *nt_status = NtQueryFullAttributesFile(&obj_attributes, file_info);
michael@0 338
michael@0 339 return true;
michael@0 340 }
michael@0 341
michael@0 342 bool FileSystemPolicy::SetInformationFileAction(
michael@0 343 EvalResult eval_result, const ClientInfo& client_info,
michael@0 344 HANDLE target_file_handle, void* file_info, uint32 length,
michael@0 345 uint32 info_class, IO_STATUS_BLOCK* io_block,
michael@0 346 NTSTATUS* nt_status) {
michael@0 347 // The only action supported is ASK_BROKER which means open the requested
michael@0 348 // file as specified.
michael@0 349 if (ASK_BROKER != eval_result) {
michael@0 350 *nt_status = STATUS_ACCESS_DENIED;
michael@0 351 return true;
michael@0 352 }
michael@0 353
michael@0 354 NtSetInformationFileFunction NtSetInformationFile = NULL;
michael@0 355 ResolveNTFunctionPtr("NtSetInformationFile", &NtSetInformationFile);
michael@0 356
michael@0 357 HANDLE local_handle = NULL;
michael@0 358 if (!::DuplicateHandle(client_info.process, target_file_handle,
michael@0 359 ::GetCurrentProcess(), &local_handle, 0, FALSE,
michael@0 360 DUPLICATE_SAME_ACCESS)) {
michael@0 361 *nt_status = STATUS_ACCESS_DENIED;
michael@0 362 return true;
michael@0 363 }
michael@0 364
michael@0 365 base::win::ScopedHandle handle(local_handle);
michael@0 366
michael@0 367 FILE_INFORMATION_CLASS file_info_class =
michael@0 368 static_cast<FILE_INFORMATION_CLASS>(info_class);
michael@0 369 *nt_status = NtSetInformationFile(local_handle, io_block, file_info, length,
michael@0 370 file_info_class);
michael@0 371
michael@0 372 return true;
michael@0 373 }
michael@0 374
michael@0 375 bool PreProcessName(const std::wstring& path, std::wstring* new_path) {
michael@0 376 ConvertToLongPath(path, new_path);
michael@0 377
michael@0 378 bool reparsed = false;
michael@0 379 if (ERROR_SUCCESS != IsReparsePoint(*new_path, &reparsed))
michael@0 380 return false;
michael@0 381
michael@0 382 // We can't process reparsed file.
michael@0 383 return !reparsed;
michael@0 384 }
michael@0 385
michael@0 386 } // namespace sandbox

mercurial