1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/security/sandbox/win/src/filesystem_policy.cc Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,386 @@ 1.4 +// Copyright (c) 2011 The Chromium Authors. All rights reserved. 1.5 +// Use of this source code is governed by a BSD-style license that can be 1.6 +// found in the LICENSE file. 1.7 + 1.8 +#include <string> 1.9 + 1.10 +#include "sandbox/win/src/filesystem_policy.h" 1.11 + 1.12 +#include "base/logging.h" 1.13 +#include "base/win/scoped_handle.h" 1.14 +#include "sandbox/win/src/ipc_tags.h" 1.15 +#include "sandbox/win/src/policy_engine_opcodes.h" 1.16 +#include "sandbox/win/src/policy_params.h" 1.17 +#include "sandbox/win/src/sandbox_utils.h" 1.18 +#include "sandbox/win/src/sandbox_types.h" 1.19 +#include "sandbox/win/src/win_utils.h" 1.20 + 1.21 +namespace { 1.22 + 1.23 +NTSTATUS NtCreateFileInTarget(HANDLE* target_file_handle, 1.24 + ACCESS_MASK desired_access, 1.25 + OBJECT_ATTRIBUTES* obj_attributes, 1.26 + IO_STATUS_BLOCK* io_status_block, 1.27 + ULONG file_attributes, 1.28 + ULONG share_access, 1.29 + ULONG create_disposition, 1.30 + ULONG create_options, 1.31 + PVOID ea_buffer, 1.32 + ULONG ea_lenght, 1.33 + HANDLE target_process) { 1.34 + NtCreateFileFunction NtCreateFile = NULL; 1.35 + ResolveNTFunctionPtr("NtCreateFile", &NtCreateFile); 1.36 + 1.37 + HANDLE local_handle = INVALID_HANDLE_VALUE; 1.38 + NTSTATUS status = NtCreateFile(&local_handle, desired_access, obj_attributes, 1.39 + io_status_block, NULL, file_attributes, 1.40 + share_access, create_disposition, 1.41 + create_options, ea_buffer, ea_lenght); 1.42 + if (!NT_SUCCESS(status)) { 1.43 + return status; 1.44 + } 1.45 + 1.46 + if (!sandbox::SameObject(local_handle, obj_attributes->ObjectName->Buffer)) { 1.47 + // The handle points somewhere else. Fail the operation. 1.48 + ::CloseHandle(local_handle); 1.49 + return STATUS_ACCESS_DENIED; 1.50 + } 1.51 + 1.52 + if (!::DuplicateHandle(::GetCurrentProcess(), local_handle, 1.53 + target_process, target_file_handle, 0, FALSE, 1.54 + DUPLICATE_CLOSE_SOURCE | DUPLICATE_SAME_ACCESS)) { 1.55 + return STATUS_ACCESS_DENIED; 1.56 + } 1.57 + return STATUS_SUCCESS; 1.58 +} 1.59 + 1.60 +} // namespace. 1.61 + 1.62 +namespace sandbox { 1.63 + 1.64 +bool FileSystemPolicy::GenerateRules(const wchar_t* name, 1.65 + TargetPolicy::Semantics semantics, 1.66 + LowLevelPolicy* policy) { 1.67 + std::wstring mod_name(name); 1.68 + if (mod_name.empty()) { 1.69 + return false; 1.70 + } 1.71 + 1.72 + // Don't do any pre-processing if the name starts like the the native 1.73 + // object manager style. 1.74 + if (0 != _wcsnicmp(mod_name.c_str(), kNTObjManPrefix, kNTObjManPrefixLen)) { 1.75 + // TODO(cpu) bug 32224: This prefix add is a hack because we don't have the 1.76 + // infrastructure to normalize names. In any case we need to escape the 1.77 + // question marks. 1.78 + if (!PreProcessName(mod_name, &mod_name)) { 1.79 + // The path to be added might contain a reparse point. 1.80 + NOTREACHED(); 1.81 + return false; 1.82 + } 1.83 + if (0 != mod_name.compare(0, kNTPrefixLen, kNTPrefix)) { 1.84 + // TODO(nsylvain): Find a better way to do name resolution. Right now we 1.85 + // take the name and we expand it. 1.86 + mod_name.insert(0, L"\\/?/?\\"); 1.87 + name = mod_name.c_str(); 1.88 + } 1.89 + } 1.90 + 1.91 + EvalResult result = ASK_BROKER; 1.92 + 1.93 + // List of supported calls for the filesystem. 1.94 + const unsigned kCallNtCreateFile = 0x1; 1.95 + const unsigned kCallNtOpenFile = 0x2; 1.96 + const unsigned kCallNtQueryAttributesFile = 0x4; 1.97 + const unsigned kCallNtQueryFullAttributesFile = 0x8; 1.98 + const unsigned kCallNtSetInfoRename = 0x10; 1.99 + 1.100 + DWORD rule_to_add = kCallNtOpenFile | kCallNtCreateFile | 1.101 + kCallNtQueryAttributesFile | 1.102 + kCallNtQueryFullAttributesFile | kCallNtSetInfoRename; 1.103 + 1.104 + PolicyRule create(result); 1.105 + PolicyRule open(result); 1.106 + PolicyRule query(result); 1.107 + PolicyRule query_full(result); 1.108 + PolicyRule rename(result); 1.109 + 1.110 + switch (semantics) { 1.111 + case TargetPolicy::FILES_ALLOW_DIR_ANY: { 1.112 + open.AddNumberMatch(IF, OpenFile::OPTIONS, FILE_DIRECTORY_FILE, AND); 1.113 + create.AddNumberMatch(IF, OpenFile::OPTIONS, FILE_DIRECTORY_FILE, AND); 1.114 + break; 1.115 + } 1.116 + case TargetPolicy::FILES_ALLOW_READONLY: { 1.117 + // We consider all flags that are not known to be readonly as potentially 1.118 + // used for write. 1.119 + DWORD allowed_flags = FILE_READ_DATA | FILE_READ_ATTRIBUTES | 1.120 + FILE_READ_EA | SYNCHRONIZE | FILE_EXECUTE | 1.121 + GENERIC_READ | GENERIC_EXECUTE | READ_CONTROL; 1.122 + DWORD restricted_flags = ~allowed_flags; 1.123 + open.AddNumberMatch(IF_NOT, OpenFile::ACCESS, restricted_flags, AND); 1.124 + create.AddNumberMatch(IF_NOT, OpenFile::ACCESS, restricted_flags, AND); 1.125 + 1.126 + // Read only access don't work for rename. 1.127 + rule_to_add &= ~kCallNtSetInfoRename; 1.128 + break; 1.129 + } 1.130 + case TargetPolicy::FILES_ALLOW_QUERY: { 1.131 + // Here we don't want to add policy for the open or the create. 1.132 + rule_to_add &= ~(kCallNtOpenFile | kCallNtCreateFile | 1.133 + kCallNtSetInfoRename); 1.134 + break; 1.135 + } 1.136 + case TargetPolicy::FILES_ALLOW_ANY: { 1.137 + break; 1.138 + } 1.139 + default: { 1.140 + NOTREACHED(); 1.141 + return false; 1.142 + } 1.143 + } 1.144 + 1.145 + if ((rule_to_add & kCallNtCreateFile) && 1.146 + (!create.AddStringMatch(IF, OpenFile::NAME, name, CASE_INSENSITIVE) || 1.147 + !policy->AddRule(IPC_NTCREATEFILE_TAG, &create))) { 1.148 + return false; 1.149 + } 1.150 + 1.151 + if ((rule_to_add & kCallNtOpenFile) && 1.152 + (!open.AddStringMatch(IF, OpenFile::NAME, name, CASE_INSENSITIVE) || 1.153 + !policy->AddRule(IPC_NTOPENFILE_TAG, &open))) { 1.154 + return false; 1.155 + } 1.156 + 1.157 + if ((rule_to_add & kCallNtQueryAttributesFile) && 1.158 + (!query.AddStringMatch(IF, FileName::NAME, name, CASE_INSENSITIVE) || 1.159 + !policy->AddRule(IPC_NTQUERYATTRIBUTESFILE_TAG, &query))) { 1.160 + return false; 1.161 + } 1.162 + 1.163 + if ((rule_to_add & kCallNtQueryFullAttributesFile) && 1.164 + (!query_full.AddStringMatch(IF, FileName::NAME, name, CASE_INSENSITIVE) 1.165 + || !policy->AddRule(IPC_NTQUERYFULLATTRIBUTESFILE_TAG, 1.166 + &query_full))) { 1.167 + return false; 1.168 + } 1.169 + 1.170 + if ((rule_to_add & kCallNtSetInfoRename) && 1.171 + (!rename.AddStringMatch(IF, FileName::NAME, name, CASE_INSENSITIVE) || 1.172 + !policy->AddRule(IPC_NTSETINFO_RENAME_TAG, &rename))) { 1.173 + return false; 1.174 + } 1.175 + 1.176 + return true; 1.177 +} 1.178 + 1.179 +// Right now we insert two rules, to be evaluated before any user supplied rule: 1.180 +// - go to the broker if the path doesn't look like the paths that we push on 1.181 +// the policy (namely \??\something). 1.182 +// - go to the broker if it looks like this is a short-name path. 1.183 +// 1.184 +// It is possible to add a rule to go to the broker in any case; it would look 1.185 +// something like: 1.186 +// rule = new PolicyRule(ASK_BROKER); 1.187 +// rule->AddNumberMatch(IF_NOT, FileName::BROKER, TRUE, AND); 1.188 +// policy->AddRule(service, rule); 1.189 +bool FileSystemPolicy::SetInitialRules(LowLevelPolicy* policy) { 1.190 + PolicyRule format(ASK_BROKER); 1.191 + PolicyRule short_name(ASK_BROKER); 1.192 + 1.193 + bool rv = format.AddNumberMatch(IF_NOT, FileName::BROKER, TRUE, AND); 1.194 + rv &= format.AddStringMatch(IF_NOT, FileName::NAME, L"\\/?/?\\*", 1.195 + CASE_SENSITIVE); 1.196 + 1.197 + rv &= short_name.AddNumberMatch(IF_NOT, FileName::BROKER, TRUE, AND); 1.198 + rv &= short_name.AddStringMatch(IF, FileName::NAME, L"*~*", CASE_SENSITIVE); 1.199 + 1.200 + if (!rv || !policy->AddRule(IPC_NTCREATEFILE_TAG, &format)) 1.201 + return false; 1.202 + 1.203 + if (!policy->AddRule(IPC_NTCREATEFILE_TAG, &short_name)) 1.204 + return false; 1.205 + 1.206 + if (!policy->AddRule(IPC_NTOPENFILE_TAG, &format)) 1.207 + return false; 1.208 + 1.209 + if (!policy->AddRule(IPC_NTOPENFILE_TAG, &short_name)) 1.210 + return false; 1.211 + 1.212 + if (!policy->AddRule(IPC_NTQUERYATTRIBUTESFILE_TAG, &format)) 1.213 + return false; 1.214 + 1.215 + if (!policy->AddRule(IPC_NTQUERYATTRIBUTESFILE_TAG, &short_name)) 1.216 + return false; 1.217 + 1.218 + if (!policy->AddRule(IPC_NTQUERYFULLATTRIBUTESFILE_TAG, &format)) 1.219 + return false; 1.220 + 1.221 + if (!policy->AddRule(IPC_NTQUERYFULLATTRIBUTESFILE_TAG, &short_name)) 1.222 + return false; 1.223 + 1.224 + if (!policy->AddRule(IPC_NTSETINFO_RENAME_TAG, &format)) 1.225 + return false; 1.226 + 1.227 + if (!policy->AddRule(IPC_NTSETINFO_RENAME_TAG, &short_name)) 1.228 + return false; 1.229 + 1.230 + return true; 1.231 +} 1.232 + 1.233 +bool FileSystemPolicy::CreateFileAction(EvalResult eval_result, 1.234 + const ClientInfo& client_info, 1.235 + const std::wstring &file, 1.236 + uint32 attributes, 1.237 + uint32 desired_access, 1.238 + uint32 file_attributes, 1.239 + uint32 share_access, 1.240 + uint32 create_disposition, 1.241 + uint32 create_options, 1.242 + HANDLE *handle, 1.243 + NTSTATUS* nt_status, 1.244 + ULONG_PTR *io_information) { 1.245 + // The only action supported is ASK_BROKER which means create the requested 1.246 + // file as specified. 1.247 + if (ASK_BROKER != eval_result) { 1.248 + *nt_status = STATUS_ACCESS_DENIED; 1.249 + return false; 1.250 + } 1.251 + IO_STATUS_BLOCK io_block = {0}; 1.252 + UNICODE_STRING uni_name = {0}; 1.253 + OBJECT_ATTRIBUTES obj_attributes = {0}; 1.254 + InitObjectAttribs(file, attributes, NULL, &obj_attributes, &uni_name); 1.255 + *nt_status = NtCreateFileInTarget(handle, desired_access, &obj_attributes, 1.256 + &io_block, file_attributes, share_access, 1.257 + create_disposition, create_options, NULL, 1.258 + 0, client_info.process); 1.259 + 1.260 + *io_information = io_block.Information; 1.261 + return true; 1.262 +} 1.263 + 1.264 +bool FileSystemPolicy::OpenFileAction(EvalResult eval_result, 1.265 + const ClientInfo& client_info, 1.266 + const std::wstring &file, 1.267 + uint32 attributes, 1.268 + uint32 desired_access, 1.269 + uint32 share_access, 1.270 + uint32 open_options, 1.271 + HANDLE *handle, 1.272 + NTSTATUS* nt_status, 1.273 + ULONG_PTR *io_information) { 1.274 + // The only action supported is ASK_BROKER which means open the requested 1.275 + // file as specified. 1.276 + if (ASK_BROKER != eval_result) { 1.277 + *nt_status = STATUS_ACCESS_DENIED; 1.278 + return true; 1.279 + } 1.280 + // An NtOpen is equivalent to an NtCreate with FileAttributes = 0 and 1.281 + // CreateDisposition = FILE_OPEN. 1.282 + IO_STATUS_BLOCK io_block = {0}; 1.283 + UNICODE_STRING uni_name = {0}; 1.284 + OBJECT_ATTRIBUTES obj_attributes = {0}; 1.285 + InitObjectAttribs(file, attributes, NULL, &obj_attributes, &uni_name); 1.286 + *nt_status = NtCreateFileInTarget(handle, desired_access, &obj_attributes, 1.287 + &io_block, 0, share_access, FILE_OPEN, 1.288 + open_options, NULL, 0, 1.289 + client_info.process); 1.290 + 1.291 + *io_information = io_block.Information; 1.292 + return true; 1.293 +} 1.294 + 1.295 +bool FileSystemPolicy::QueryAttributesFileAction( 1.296 + EvalResult eval_result, 1.297 + const ClientInfo& client_info, 1.298 + const std::wstring &file, 1.299 + uint32 attributes, 1.300 + FILE_BASIC_INFORMATION* file_info, 1.301 + NTSTATUS* nt_status) { 1.302 + // The only action supported is ASK_BROKER which means query the requested 1.303 + // file as specified. 1.304 + if (ASK_BROKER != eval_result) { 1.305 + *nt_status = STATUS_ACCESS_DENIED; 1.306 + return true; 1.307 + } 1.308 + 1.309 + NtQueryAttributesFileFunction NtQueryAttributesFile = NULL; 1.310 + ResolveNTFunctionPtr("NtQueryAttributesFile", &NtQueryAttributesFile); 1.311 + 1.312 + UNICODE_STRING uni_name = {0}; 1.313 + OBJECT_ATTRIBUTES obj_attributes = {0}; 1.314 + InitObjectAttribs(file, attributes, NULL, &obj_attributes, &uni_name); 1.315 + *nt_status = NtQueryAttributesFile(&obj_attributes, file_info); 1.316 + 1.317 + return true; 1.318 +} 1.319 + 1.320 +bool FileSystemPolicy::QueryFullAttributesFileAction( 1.321 + EvalResult eval_result, 1.322 + const ClientInfo& client_info, 1.323 + const std::wstring &file, 1.324 + uint32 attributes, 1.325 + FILE_NETWORK_OPEN_INFORMATION* file_info, 1.326 + NTSTATUS* nt_status) { 1.327 + // The only action supported is ASK_BROKER which means query the requested 1.328 + // file as specified. 1.329 + if (ASK_BROKER != eval_result) { 1.330 + *nt_status = STATUS_ACCESS_DENIED; 1.331 + return true; 1.332 + } 1.333 + 1.334 + NtQueryFullAttributesFileFunction NtQueryFullAttributesFile = NULL; 1.335 + ResolveNTFunctionPtr("NtQueryFullAttributesFile", &NtQueryFullAttributesFile); 1.336 + 1.337 + UNICODE_STRING uni_name = {0}; 1.338 + OBJECT_ATTRIBUTES obj_attributes = {0}; 1.339 + InitObjectAttribs(file, attributes, NULL, &obj_attributes, &uni_name); 1.340 + *nt_status = NtQueryFullAttributesFile(&obj_attributes, file_info); 1.341 + 1.342 + return true; 1.343 +} 1.344 + 1.345 +bool FileSystemPolicy::SetInformationFileAction( 1.346 + EvalResult eval_result, const ClientInfo& client_info, 1.347 + HANDLE target_file_handle, void* file_info, uint32 length, 1.348 + uint32 info_class, IO_STATUS_BLOCK* io_block, 1.349 + NTSTATUS* nt_status) { 1.350 + // The only action supported is ASK_BROKER which means open the requested 1.351 + // file as specified. 1.352 + if (ASK_BROKER != eval_result) { 1.353 + *nt_status = STATUS_ACCESS_DENIED; 1.354 + return true; 1.355 + } 1.356 + 1.357 + NtSetInformationFileFunction NtSetInformationFile = NULL; 1.358 + ResolveNTFunctionPtr("NtSetInformationFile", &NtSetInformationFile); 1.359 + 1.360 + HANDLE local_handle = NULL; 1.361 + if (!::DuplicateHandle(client_info.process, target_file_handle, 1.362 + ::GetCurrentProcess(), &local_handle, 0, FALSE, 1.363 + DUPLICATE_SAME_ACCESS)) { 1.364 + *nt_status = STATUS_ACCESS_DENIED; 1.365 + return true; 1.366 + } 1.367 + 1.368 + base::win::ScopedHandle handle(local_handle); 1.369 + 1.370 + FILE_INFORMATION_CLASS file_info_class = 1.371 + static_cast<FILE_INFORMATION_CLASS>(info_class); 1.372 + *nt_status = NtSetInformationFile(local_handle, io_block, file_info, length, 1.373 + file_info_class); 1.374 + 1.375 + return true; 1.376 +} 1.377 + 1.378 +bool PreProcessName(const std::wstring& path, std::wstring* new_path) { 1.379 + ConvertToLongPath(path, new_path); 1.380 + 1.381 + bool reparsed = false; 1.382 + if (ERROR_SUCCESS != IsReparsePoint(*new_path, &reparsed)) 1.383 + return false; 1.384 + 1.385 + // We can't process reparsed file. 1.386 + return !reparsed; 1.387 +} 1.388 + 1.389 +} // namespace sandbox