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