security/sandbox/win/src/filesystem_policy.cc

changeset 0
6474c204b198
     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

mercurial