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_interception.h"
      6 
      7 #include "sandbox/win/src/crosscall_client.h"
      8 #include "sandbox/win/src/ipc_tags.h"
      9 #include "sandbox/win/src/policy_params.h"
     10 #include "sandbox/win/src/policy_target.h"
     11 #include "sandbox/win/src/sandbox_factory.h"
     12 #include "sandbox/win/src/sandbox_nt_util.h"
     13 #include "sandbox/win/src/sharedmem_ipc_client.h"
     14 #include "sandbox/win/src/target_services.h"
     15 
     16 namespace sandbox {
     17 
     18 SANDBOX_INTERCEPT NtExports g_nt;
     19 
     20 // Hooks NtOpenThread and proxy the call to the broker if it's trying to
     21 // open a thread in the same process.
     22 NTSTATUS WINAPI TargetNtOpenThread(NtOpenThreadFunction orig_OpenThread,
     23                                    PHANDLE thread, ACCESS_MASK desired_access,
     24                                    POBJECT_ATTRIBUTES object_attributes,
     25                                    PCLIENT_ID client_id) {
     26   NTSTATUS status = orig_OpenThread(thread, desired_access, object_attributes,
     27                                     client_id);
     28   if (NT_SUCCESS(status))
     29     return status;
     30 
     31   do {
     32     if (!SandboxFactory::GetTargetServices()->GetState()->InitCalled())
     33       break;
     34     if (!client_id)
     35       break;
     36 
     37     uint32 thread_id = 0;
     38     bool should_break = false;
     39     __try {
     40       // We support only the calls for the current process
     41       if (NULL != client_id->UniqueProcess)
     42         should_break = true;
     43 
     44       // Object attributes should be NULL or empty.
     45       if (!should_break && NULL != object_attributes) {
     46         if (0 != object_attributes->Attributes ||
     47             NULL != object_attributes->ObjectName ||
     48             NULL != object_attributes->RootDirectory ||
     49             NULL != object_attributes->SecurityDescriptor ||
     50             NULL != object_attributes->SecurityQualityOfService) {
     51           should_break = true;
     52         }
     53       }
     54 
     55       thread_id = static_cast<uint32>(
     56                       reinterpret_cast<ULONG_PTR>(client_id->UniqueThread));
     57     } __except(EXCEPTION_EXECUTE_HANDLER) {
     58       break;
     59     }
     60 
     61     if (should_break)
     62       break;
     63 
     64     if (!ValidParameter(thread, sizeof(HANDLE), WRITE))
     65       break;
     66 
     67     void* memory = GetGlobalIPCMemory();
     68     if (NULL == memory)
     69       break;
     70 
     71     SharedMemIPCClient ipc(memory);
     72     CrossCallReturn answer = {0};
     73     ResultCode code = CrossCall(ipc, IPC_NTOPENTHREAD_TAG, desired_access,
     74                                 thread_id, &answer);
     75     if (SBOX_ALL_OK != code)
     76       break;
     77 
     78     if (!NT_SUCCESS(answer.nt_status))
     79       // The nt_status here is most likely STATUS_INVALID_CID because
     80       // in the broker we set the process id in the CID (client ID) param
     81       // to be the current process. If you try to open a thread from another
     82       // process you will get this INVALID_CID error. On the other hand, if you
     83       // try to open a thread in your own process, it should return success.
     84       // We don't want to return STATUS_INVALID_CID here, so we return the
     85       // return of the original open thread status, which is most likely
     86       // STATUS_ACCESS_DENIED.
     87       break;
     88 
     89     __try {
     90       // Write the output parameters.
     91       *thread = answer.handle;
     92     } __except(EXCEPTION_EXECUTE_HANDLER) {
     93       break;
     94     }
     95 
     96     return answer.nt_status;
     97   } while (false);
     98 
     99   return status;
    100 }
    101 
    102 // Hooks NtOpenProcess and proxy the call to the broker if it's trying to
    103 // open the current process.
    104 NTSTATUS WINAPI TargetNtOpenProcess(NtOpenProcessFunction orig_OpenProcess,
    105                                    PHANDLE process, ACCESS_MASK desired_access,
    106                                    POBJECT_ATTRIBUTES object_attributes,
    107                                    PCLIENT_ID client_id) {
    108   NTSTATUS status = orig_OpenProcess(process, desired_access, object_attributes,
    109                                      client_id);
    110   if (NT_SUCCESS(status))
    111     return status;
    112 
    113   do {
    114     if (!SandboxFactory::GetTargetServices()->GetState()->InitCalled())
    115       break;
    116     if (!client_id)
    117       break;
    118 
    119     uint32 process_id = 0;
    120     bool should_break = false;
    121     __try {
    122       // Object attributes should be NULL or empty.
    123       if (!should_break && NULL != object_attributes) {
    124         if (0 != object_attributes->Attributes ||
    125             NULL != object_attributes->ObjectName ||
    126             NULL != object_attributes->RootDirectory ||
    127             NULL != object_attributes->SecurityDescriptor ||
    128             NULL != object_attributes->SecurityQualityOfService) {
    129           should_break = true;
    130         }
    131       }
    132 
    133       process_id = static_cast<uint32>(
    134                       reinterpret_cast<ULONG_PTR>(client_id->UniqueProcess));
    135     } __except(EXCEPTION_EXECUTE_HANDLER) {
    136       break;
    137     }
    138 
    139     if (should_break)
    140       break;
    141 
    142     if (!ValidParameter(process, sizeof(HANDLE), WRITE))
    143       break;
    144 
    145     void* memory = GetGlobalIPCMemory();
    146     if (NULL == memory)
    147       break;
    148 
    149     SharedMemIPCClient ipc(memory);
    150     CrossCallReturn answer = {0};
    151     ResultCode code = CrossCall(ipc, IPC_NTOPENPROCESS_TAG, desired_access,
    152                                 process_id, &answer);
    153     if (SBOX_ALL_OK != code)
    154       break;
    155 
    156     if (!NT_SUCCESS(answer.nt_status))
    157       return answer.nt_status;
    158 
    159     __try {
    160       // Write the output parameters.
    161       *process = answer.handle;
    162     } __except(EXCEPTION_EXECUTE_HANDLER) {
    163       break;
    164     }
    165 
    166     return answer.nt_status;
    167   } while (false);
    168 
    169   return status;
    170 }
    171 
    172 
    173 NTSTATUS WINAPI TargetNtOpenProcessToken(
    174     NtOpenProcessTokenFunction orig_OpenProcessToken, HANDLE process,
    175     ACCESS_MASK desired_access, PHANDLE token) {
    176   NTSTATUS status = orig_OpenProcessToken(process, desired_access, token);
    177   if (NT_SUCCESS(status))
    178     return status;
    179 
    180   do {
    181     if (!SandboxFactory::GetTargetServices()->GetState()->InitCalled())
    182       break;
    183 
    184     if (CURRENT_PROCESS != process)
    185       break;
    186 
    187     if (!ValidParameter(token, sizeof(HANDLE), WRITE))
    188       break;
    189 
    190     void* memory = GetGlobalIPCMemory();
    191     if (NULL == memory)
    192       break;
    193 
    194     SharedMemIPCClient ipc(memory);
    195     CrossCallReturn answer = {0};
    196     ResultCode code = CrossCall(ipc, IPC_NTOPENPROCESSTOKEN_TAG, process,
    197                                 desired_access, &answer);
    198     if (SBOX_ALL_OK != code)
    199       break;
    200 
    201     if (!NT_SUCCESS(answer.nt_status))
    202       return answer.nt_status;
    203 
    204     __try {
    205       // Write the output parameters.
    206       *token = answer.handle;
    207     } __except(EXCEPTION_EXECUTE_HANDLER) {
    208       break;
    209     }
    210 
    211     return answer.nt_status;
    212   } while (false);
    213 
    214   return status;
    215 }
    216 
    217 NTSTATUS WINAPI TargetNtOpenProcessTokenEx(
    218     NtOpenProcessTokenExFunction orig_OpenProcessTokenEx, HANDLE process,
    219     ACCESS_MASK desired_access, ULONG handle_attributes, PHANDLE token) {
    220   NTSTATUS status = orig_OpenProcessTokenEx(process, desired_access,
    221                                             handle_attributes, token);
    222   if (NT_SUCCESS(status))
    223     return status;
    224 
    225   do {
    226     if (!SandboxFactory::GetTargetServices()->GetState()->InitCalled())
    227       break;
    228 
    229     if (CURRENT_PROCESS != process)
    230       break;
    231 
    232     if (!ValidParameter(token, sizeof(HANDLE), WRITE))
    233       break;
    234 
    235     void* memory = GetGlobalIPCMemory();
    236     if (NULL == memory)
    237       break;
    238 
    239     SharedMemIPCClient ipc(memory);
    240     CrossCallReturn answer = {0};
    241     ResultCode code = CrossCall(ipc, IPC_NTOPENPROCESSTOKENEX_TAG, process,
    242                                 desired_access, handle_attributes, &answer);
    243     if (SBOX_ALL_OK != code)
    244       break;
    245 
    246     if (!NT_SUCCESS(answer.nt_status))
    247       return answer.nt_status;
    248 
    249     __try {
    250       // Write the output parameters.
    251       *token = answer.handle;
    252     } __except(EXCEPTION_EXECUTE_HANDLER) {
    253       break;
    254     }
    255 
    256     return answer.nt_status;
    257   } while (false);
    258 
    259   return status;
    260 }
    261 
    262 BOOL WINAPI TargetCreateProcessW(CreateProcessWFunction orig_CreateProcessW,
    263                                  LPCWSTR application_name, LPWSTR command_line,
    264                                  LPSECURITY_ATTRIBUTES process_attributes,
    265                                  LPSECURITY_ATTRIBUTES thread_attributes,
    266                                  BOOL inherit_handles, DWORD flags,
    267                                  LPVOID environment, LPCWSTR current_directory,
    268                                  LPSTARTUPINFOW startup_info,
    269                                  LPPROCESS_INFORMATION process_information) {
    270   if (orig_CreateProcessW(application_name, command_line, process_attributes,
    271                           thread_attributes, inherit_handles, flags,
    272                           environment, current_directory, startup_info,
    273                           process_information)) {
    274     return TRUE;
    275   }
    276 
    277   // We don't trust that the IPC can work this early.
    278   if (!SandboxFactory::GetTargetServices()->GetState()->InitCalled())
    279     return FALSE;
    280 
    281   DWORD original_error = ::GetLastError();
    282 
    283   do {
    284     if (!ValidParameter(process_information, sizeof(PROCESS_INFORMATION),
    285                         WRITE))
    286       break;
    287 
    288     void* memory = GetGlobalIPCMemory();
    289     if (NULL == memory)
    290       break;
    291 
    292     const wchar_t* cur_dir = NULL;
    293 
    294     wchar_t current_directory[MAX_PATH];
    295     DWORD result = ::GetCurrentDirectory(MAX_PATH, current_directory);
    296     if (0 != result && result < MAX_PATH)
    297       cur_dir = current_directory;
    298 
    299     SharedMemIPCClient ipc(memory);
    300     CrossCallReturn answer = {0};
    301 
    302     InOutCountedBuffer proc_info(process_information,
    303                                  sizeof(PROCESS_INFORMATION));
    304 
    305     ResultCode code = CrossCall(ipc, IPC_CREATEPROCESSW_TAG, application_name,
    306                                 command_line, cur_dir, proc_info, &answer);
    307     if (SBOX_ALL_OK != code)
    308       break;
    309 
    310     ::SetLastError(answer.win32_result);
    311     if (ERROR_SUCCESS != answer.win32_result)
    312       return FALSE;
    313 
    314     return TRUE;
    315   } while (false);
    316 
    317   ::SetLastError(original_error);
    318   return FALSE;
    319 }
    320 
    321 BOOL WINAPI TargetCreateProcessA(CreateProcessAFunction orig_CreateProcessA,
    322                                  LPCSTR application_name, LPSTR command_line,
    323                                  LPSECURITY_ATTRIBUTES process_attributes,
    324                                  LPSECURITY_ATTRIBUTES thread_attributes,
    325                                  BOOL inherit_handles, DWORD flags,
    326                                  LPVOID environment, LPCSTR current_directory,
    327                                  LPSTARTUPINFOA startup_info,
    328                                  LPPROCESS_INFORMATION process_information) {
    329   if (orig_CreateProcessA(application_name, command_line, process_attributes,
    330                           thread_attributes, inherit_handles, flags,
    331                           environment, current_directory, startup_info,
    332                           process_information)) {
    333     return TRUE;
    334   }
    335 
    336   // We don't trust that the IPC can work this early.
    337   if (!SandboxFactory::GetTargetServices()->GetState()->InitCalled())
    338     return FALSE;
    339 
    340   DWORD original_error = ::GetLastError();
    341 
    342   do {
    343     if (!ValidParameter(process_information, sizeof(PROCESS_INFORMATION),
    344                         WRITE))
    345       break;
    346 
    347     void* memory = GetGlobalIPCMemory();
    348     if (NULL == memory)
    349       break;
    350 
    351     // Convert the input params to unicode.
    352     UNICODE_STRING *cmd_unicode = NULL;
    353     UNICODE_STRING *app_unicode = NULL;
    354     if (command_line) {
    355       cmd_unicode = AnsiToUnicode(command_line);
    356       if (!cmd_unicode)
    357         break;
    358     }
    359 
    360     if (application_name) {
    361       app_unicode = AnsiToUnicode(application_name);
    362       if (!app_unicode) {
    363         operator delete(cmd_unicode, NT_ALLOC);
    364         break;
    365       }
    366     }
    367 
    368     const wchar_t* cmd_line = cmd_unicode ? cmd_unicode->Buffer : NULL;
    369     const wchar_t* app_name = app_unicode ? app_unicode->Buffer : NULL;
    370     const wchar_t* cur_dir = NULL;
    371 
    372     wchar_t current_directory[MAX_PATH];
    373     DWORD result = ::GetCurrentDirectory(MAX_PATH, current_directory);
    374     if (0 != result && result < MAX_PATH)
    375       cur_dir = current_directory;
    376 
    377     SharedMemIPCClient ipc(memory);
    378     CrossCallReturn answer = {0};
    379 
    380     InOutCountedBuffer proc_info(process_information,
    381                                  sizeof(PROCESS_INFORMATION));
    382 
    383     ResultCode code = CrossCall(ipc, IPC_CREATEPROCESSW_TAG, app_name,
    384                                 cmd_line, cur_dir, proc_info, &answer);
    385 
    386     operator delete(cmd_unicode, NT_ALLOC);
    387     operator delete(app_unicode, NT_ALLOC);
    388 
    389     if (SBOX_ALL_OK != code)
    390       break;
    391 
    392     ::SetLastError(answer.win32_result);
    393     if (ERROR_SUCCESS != answer.win32_result)
    394       return FALSE;
    395 
    396     return TRUE;
    397   } while (false);
    398 
    399   ::SetLastError(original_error);
    400   return FALSE;
    401 }
    402 
    403 // Creates a thread without registering with CSRSS. This is required if we
    404 // closed the CSRSS ALPC port after lockdown.
    405 HANDLE WINAPI TargetCreateThread(CreateThreadFunction orig_CreateThread,
    406                                  LPSECURITY_ATTRIBUTES thread_attributes,
    407                                  SIZE_T stack_size,
    408                                  LPTHREAD_START_ROUTINE start_address,
    409                                  PVOID parameter,
    410                                  DWORD creation_flags,
    411                                  LPDWORD thread_id) {
    412 // Try the normal CreateThread; switch to RtlCreateUserThread if needed.
    413   static bool use_create_thread = true;
    414   HANDLE thread;
    415   if (use_create_thread) {
    416     thread = orig_CreateThread(thread_attributes, stack_size, start_address,
    417                                parameter, creation_flags, thread_id);
    418     if (thread)
    419       return thread;
    420   }
    421 
    422   PSECURITY_DESCRIPTOR sd =
    423       thread_attributes ? thread_attributes->lpSecurityDescriptor : NULL;
    424   CLIENT_ID client_id;
    425 
    426   NTSTATUS result = g_nt.RtlCreateUserThread(NtCurrentProcess, sd,
    427                                              creation_flags & CREATE_SUSPENDED,
    428                                              0, stack_size, 0, start_address,
    429                                              parameter, &thread, &client_id);
    430   if (!NT_SUCCESS(result))
    431     return 0;
    432 
    433   // CSRSS is closed if we got here, so use RtlCreateUserThread from here on.
    434   use_create_thread = false;
    435   if (thread_id)
    436     *thread_id = HandleToUlong(client_id.UniqueThread);
    437   return thread;
    438 }
    439 
    440 // Cache the default LCID to avoid pinging CSRSS after lockdown.
    441 // TODO(jschuh): This approach will miss a default locale changes after
    442 // lockdown. In the future we may want to have the broker check instead.
    443 LCID WINAPI TargetGetUserDefaultLCID(
    444     GetUserDefaultLCIDFunction orig_GetUserDefaultLCID) {
    445   static LCID default_lcid = orig_GetUserDefaultLCID();
    446   return default_lcid;
    447 }
    448 
    449 }  // namespace sandbox
    450