Home | History | Annotate | Download | only in src
      1 // Copyright (c) 2013 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 "sandbox/win/src/process_thread_dispatcher.h"
      6 
      7 #include "base/basictypes.h"
      8 #include "base/logging.h"
      9 #include "sandbox/win/src/crosscall_client.h"
     10 #include "sandbox/win/src/interception.h"
     11 #include "sandbox/win/src/interceptors.h"
     12 #include "sandbox/win/src/ipc_tags.h"
     13 #include "sandbox/win/src/policy_broker.h"
     14 #include "sandbox/win/src/policy_params.h"
     15 #include "sandbox/win/src/process_thread_interception.h"
     16 #include "sandbox/win/src/process_thread_policy.h"
     17 #include "sandbox/win/src/sandbox.h"
     18 
     19 namespace {
     20 
     21 // Extracts the application name from a command line.
     22 //
     23 // The application name is the first element of the command line. If
     24 // there is no quotes, the first element is delimited by the first space.
     25 // If there are quotes, the first element is delimited by the quotes.
     26 //
     27 // The create process call is smarter than us. It tries really hard to launch
     28 // the process even if the command line is wrong. For example:
     29 // "c:\program files\test param" will first try to launch c:\program.exe then
     30 // c:\program files\test.exe. We don't do that, we stop after at the first
     31 // space when there is no quotes.
     32 base::string16 GetPathFromCmdLine(const base::string16 &cmd_line) {
     33   base::string16 exe_name;
     34   // Check if it starts with '"'.
     35   if (cmd_line[0] == L'\"') {
     36     // Find the position of the second '"', this terminates the path.
     37     base::string16::size_type pos = cmd_line.find(L'\"', 1);
     38     if (base::string16::npos == pos)
     39       return cmd_line;
     40     exe_name = cmd_line.substr(1, pos - 1);
     41   } else {
     42     // There is no '"', that means that the appname is terminated at the
     43     // first space.
     44     base::string16::size_type pos = cmd_line.find(L' ');
     45     if (base::string16::npos == pos) {
     46       // There is no space, the cmd_line contains only the app_name
     47       exe_name = cmd_line;
     48     } else {
     49       exe_name = cmd_line.substr(0, pos);
     50     }
     51   }
     52 
     53   return exe_name;
     54 }
     55 
     56 // Returns true is the path in parameter is relative. False if it's
     57 // absolute.
     58 bool IsPathRelative(const base::string16 &path) {
     59   // A path is Relative if it's not a UNC path beginnning with \\ or a
     60   // path beginning with a drive. (i.e. X:\)
     61   if (path.find(L"\\\\") == 0 || path.find(L":\\") == 1)
     62     return false;
     63   return true;
     64 }
     65 
     66 // Converts a relative path to an absolute path.
     67 bool ConvertToAbsolutePath(const base::string16& child_current_directory,
     68                            bool use_env_path, base::string16 *path) {
     69   wchar_t file_buffer[MAX_PATH];
     70   wchar_t *file_part = NULL;
     71 
     72   // Here we should start by looking at the path where the child application was
     73   // started. We don't have this information yet.
     74   DWORD result = 0;
     75   if (use_env_path) {
     76     // Try with the complete path
     77     result = ::SearchPath(NULL, path->c_str(), NULL, MAX_PATH, file_buffer,
     78                           &file_part);
     79   }
     80 
     81   if (0 == result) {
     82     // Try with the current directory of the child
     83     result = ::SearchPath(child_current_directory.c_str(), path->c_str(), NULL,
     84                           MAX_PATH, file_buffer, &file_part);
     85   }
     86 
     87   if (0 == result || result >= MAX_PATH)
     88     return false;
     89 
     90   *path = file_buffer;
     91   return true;
     92 }
     93 
     94 }  // namespace
     95 namespace sandbox {
     96 
     97 ThreadProcessDispatcher::ThreadProcessDispatcher(PolicyBase* policy_base)
     98     : policy_base_(policy_base) {
     99   static const IPCCall open_thread = {
    100     {IPC_NTOPENTHREAD_TAG, ULONG_TYPE, ULONG_TYPE},
    101     reinterpret_cast<CallbackGeneric>(
    102         &ThreadProcessDispatcher::NtOpenThread)
    103   };
    104 
    105   static const IPCCall open_process = {
    106     {IPC_NTOPENPROCESS_TAG, ULONG_TYPE, ULONG_TYPE},
    107     reinterpret_cast<CallbackGeneric>(
    108         &ThreadProcessDispatcher::NtOpenProcess)
    109   };
    110 
    111   static const IPCCall process_token = {
    112     {IPC_NTOPENPROCESSTOKEN_TAG, VOIDPTR_TYPE, ULONG_TYPE},
    113     reinterpret_cast<CallbackGeneric>(
    114         &ThreadProcessDispatcher::NtOpenProcessToken)
    115   };
    116 
    117   static const IPCCall process_tokenex = {
    118     {IPC_NTOPENPROCESSTOKENEX_TAG, VOIDPTR_TYPE, ULONG_TYPE, ULONG_TYPE},
    119     reinterpret_cast<CallbackGeneric>(
    120         &ThreadProcessDispatcher::NtOpenProcessTokenEx)
    121   };
    122 
    123   static const IPCCall create_params = {
    124     {IPC_CREATEPROCESSW_TAG, WCHAR_TYPE, WCHAR_TYPE, WCHAR_TYPE, INOUTPTR_TYPE},
    125     reinterpret_cast<CallbackGeneric>(
    126         &ThreadProcessDispatcher::CreateProcessW)
    127   };
    128 
    129   ipc_calls_.push_back(open_thread);
    130   ipc_calls_.push_back(open_process);
    131   ipc_calls_.push_back(process_token);
    132   ipc_calls_.push_back(process_tokenex);
    133   ipc_calls_.push_back(create_params);
    134 }
    135 
    136 bool ThreadProcessDispatcher::SetupService(InterceptionManager* manager,
    137                                            int service) {
    138   switch (service) {
    139     case IPC_NTOPENTHREAD_TAG:
    140     case IPC_NTOPENPROCESS_TAG:
    141     case IPC_NTOPENPROCESSTOKEN_TAG:
    142     case IPC_NTOPENPROCESSTOKENEX_TAG:
    143       // There is no explicit policy for these services.
    144       NOTREACHED();
    145       return false;
    146 
    147     case IPC_CREATEPROCESSW_TAG:
    148       return INTERCEPT_EAT(manager, kKerneldllName, CreateProcessW,
    149                            CREATE_PROCESSW_ID, 44) &&
    150              INTERCEPT_EAT(manager, L"kernel32.dll", CreateProcessA,
    151                            CREATE_PROCESSA_ID, 44);
    152 
    153     default:
    154       return false;
    155   }
    156 }
    157 
    158 bool ThreadProcessDispatcher::NtOpenThread(IPCInfo* ipc, DWORD desired_access,
    159                                            DWORD thread_id) {
    160   HANDLE handle;
    161   NTSTATUS ret = ProcessPolicy::OpenThreadAction(*ipc->client_info,
    162                                                  desired_access, thread_id,
    163                                                  &handle);
    164   ipc->return_info.nt_status = ret;
    165   ipc->return_info.handle = handle;
    166   return true;
    167 }
    168 
    169 bool ThreadProcessDispatcher::NtOpenProcess(IPCInfo* ipc, DWORD desired_access,
    170                                             DWORD process_id) {
    171   HANDLE handle;
    172   NTSTATUS ret = ProcessPolicy::OpenProcessAction(*ipc->client_info,
    173                                                   desired_access, process_id,
    174                                                   &handle);
    175   ipc->return_info.nt_status = ret;
    176   ipc->return_info.handle = handle;
    177   return true;
    178 }
    179 
    180 bool ThreadProcessDispatcher::NtOpenProcessToken(IPCInfo* ipc, HANDLE process,
    181                                                  DWORD desired_access) {
    182   HANDLE handle;
    183   NTSTATUS ret = ProcessPolicy::OpenProcessTokenAction(*ipc->client_info,
    184                                                        process, desired_access,
    185                                                        &handle);
    186   ipc->return_info.nt_status = ret;
    187   ipc->return_info.handle = handle;
    188   return true;
    189 }
    190 
    191 bool ThreadProcessDispatcher::NtOpenProcessTokenEx(IPCInfo* ipc, HANDLE process,
    192                                                    DWORD desired_access,
    193                                                    DWORD attributes) {
    194   HANDLE handle;
    195   NTSTATUS ret = ProcessPolicy::OpenProcessTokenExAction(*ipc->client_info,
    196                                                          process,
    197                                                          desired_access,
    198                                                          attributes, &handle);
    199   ipc->return_info.nt_status = ret;
    200   ipc->return_info.handle = handle;
    201   return true;
    202 }
    203 
    204 bool ThreadProcessDispatcher::CreateProcessW(IPCInfo* ipc, base::string16* name,
    205                                              base::string16* cmd_line,
    206                                              base::string16* cur_dir,
    207                                              CountedBuffer* info) {
    208   if (sizeof(PROCESS_INFORMATION) != info->Size())
    209     return false;
    210 
    211   // Check if there is an application name.
    212   base::string16 exe_name;
    213   if (!name->empty())
    214     exe_name = *name;
    215   else
    216     exe_name = GetPathFromCmdLine(*cmd_line);
    217 
    218   if (IsPathRelative(exe_name)) {
    219     if (!ConvertToAbsolutePath(*cur_dir, name->empty(), &exe_name)) {
    220       // Cannot find the path. Maybe the file does not exist.
    221       ipc->return_info.win32_result = ERROR_FILE_NOT_FOUND;
    222       return true;
    223     }
    224   }
    225 
    226   const wchar_t* const_exe_name = exe_name.c_str();
    227   CountedParameterSet<NameBased> params;
    228   params[NameBased::NAME] = ParamPickerMake(const_exe_name);
    229 
    230   EvalResult eval = policy_base_->EvalPolicy(IPC_CREATEPROCESSW_TAG,
    231                                              params.GetBase());
    232 
    233   PROCESS_INFORMATION* proc_info =
    234       reinterpret_cast<PROCESS_INFORMATION*>(info->Buffer());
    235   // Here we force the app_name to be the one we used for the policy lookup.
    236   // If our logic was wrong, at least we wont allow create a random process.
    237   DWORD ret = ProcessPolicy::CreateProcessWAction(eval, *ipc->client_info,
    238                                                   exe_name, *cmd_line,
    239                                                   proc_info);
    240 
    241   ipc->return_info.win32_result = ret;
    242   return true;
    243 }
    244 
    245 }  // namespace sandbox
    246