Home | History | Annotate | Download | only in src
      1 // Copyright (c) 2006-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 "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   DWORD original_error = ::GetLastError();
    277 
    278   // We don't trust that the IPC can work this early.
    279   if (!SandboxFactory::GetTargetServices()->GetState()->InitCalled())
    280     return FALSE;
    281 
    282   do {
    283     if (!ValidParameter(process_information, sizeof(PROCESS_INFORMATION),
    284                         WRITE))
    285       break;
    286 
    287     void* memory = GetGlobalIPCMemory();
    288     if (NULL == memory)
    289       break;
    290 
    291     const wchar_t* cur_dir = NULL;
    292 
    293     wchar_t current_directory[MAX_PATH];
    294     DWORD result = ::GetCurrentDirectory(MAX_PATH, current_directory);
    295     if (0 != result && result < MAX_PATH)
    296       cur_dir = current_directory;
    297 
    298     SharedMemIPCClient ipc(memory);
    299     CrossCallReturn answer = {0};
    300 
    301     InOutCountedBuffer proc_info(process_information,
    302                                  sizeof(PROCESS_INFORMATION));
    303 
    304     ResultCode code = CrossCall(ipc, IPC_CREATEPROCESSW_TAG, application_name,
    305                                 command_line, cur_dir, proc_info, &answer);
    306     if (SBOX_ALL_OK != code)
    307       break;
    308 
    309     ::SetLastError(answer.win32_result);
    310     if (ERROR_SUCCESS != answer.win32_result)
    311       return FALSE;
    312 
    313     return TRUE;
    314   } while (false);
    315 
    316   ::SetLastError(original_error);
    317   return FALSE;
    318 }
    319 
    320 BOOL WINAPI TargetCreateProcessA(CreateProcessAFunction orig_CreateProcessA,
    321                                  LPCSTR application_name, LPSTR command_line,
    322                                  LPSECURITY_ATTRIBUTES process_attributes,
    323                                  LPSECURITY_ATTRIBUTES thread_attributes,
    324                                  BOOL inherit_handles, DWORD flags,
    325                                  LPVOID environment, LPCSTR current_directory,
    326                                  LPSTARTUPINFOA startup_info,
    327                                  LPPROCESS_INFORMATION process_information) {
    328   if (orig_CreateProcessA(application_name, command_line, process_attributes,
    329                           thread_attributes, inherit_handles, flags,
    330                           environment, current_directory, startup_info,
    331                           process_information)) {
    332     return TRUE;
    333   }
    334   DWORD original_error = ::GetLastError();
    335 
    336   // We don't trust that the IPC can work this early.
    337   if (!SandboxFactory::GetTargetServices()->GetState()->InitCalled())
    338     return FALSE;
    339 
    340   do {
    341     if (!ValidParameter(process_information, sizeof(PROCESS_INFORMATION),
    342                         WRITE))
    343       break;
    344 
    345     void* memory = GetGlobalIPCMemory();
    346     if (NULL == memory)
    347       break;
    348 
    349     // Convert the input params to unicode.
    350     UNICODE_STRING *cmd_unicode = NULL;
    351     UNICODE_STRING *app_unicode = NULL;
    352     if (command_line) {
    353       cmd_unicode = AnsiToUnicode(command_line);
    354       if (!cmd_unicode)
    355         break;
    356     }
    357 
    358     if (application_name) {
    359       app_unicode = AnsiToUnicode(application_name);
    360       if (!app_unicode) {
    361         operator delete(cmd_unicode, NT_ALLOC);
    362         break;
    363       }
    364     }
    365 
    366     const wchar_t* cmd_line = cmd_unicode ? cmd_unicode->Buffer : NULL;
    367     const wchar_t* app_name = app_unicode ? app_unicode->Buffer : NULL;
    368     const wchar_t* cur_dir = NULL;
    369 
    370     wchar_t current_directory[MAX_PATH];
    371     DWORD result = ::GetCurrentDirectory(MAX_PATH, current_directory);
    372     if (0 != result && result < MAX_PATH)
    373       cur_dir = current_directory;
    374 
    375     SharedMemIPCClient ipc(memory);
    376     CrossCallReturn answer = {0};
    377 
    378     InOutCountedBuffer proc_info(process_information,
    379                                  sizeof(PROCESS_INFORMATION));
    380 
    381     ResultCode code = CrossCall(ipc, IPC_CREATEPROCESSW_TAG, app_name,
    382                                 cmd_line, cur_dir, proc_info, &answer);
    383 
    384     operator delete(cmd_unicode, NT_ALLOC);
    385     operator delete(app_unicode, NT_ALLOC);
    386 
    387     if (SBOX_ALL_OK != code)
    388       break;
    389 
    390     ::SetLastError(answer.win32_result);
    391     if (ERROR_SUCCESS != answer.win32_result)
    392       return FALSE;
    393 
    394     return TRUE;
    395   } while (false);
    396 
    397   ::SetLastError(original_error);
    398   return FALSE;
    399 }
    400 
    401 // Creates a thread without registering with CSRSS. This is required if we
    402 // closed the CSRSS ALPC port after lockdown.
    403 HANDLE WINAPI TargetCreateThread(CreateThreadFunction orig_CreateThread,
    404                                  LPSECURITY_ATTRIBUTES thread_attributes,
    405                                  SIZE_T stack_size,
    406                                  LPTHREAD_START_ROUTINE start_address,
    407                                  PVOID parameter,
    408                                  DWORD creation_flags,
    409                                  LPDWORD thread_id) {
    410 // Try the normal CreateThread; switch to RtlCreateUserThread if needed.
    411   static bool use_create_thread = true;
    412   HANDLE thread;
    413   if (use_create_thread) {
    414     thread = orig_CreateThread(thread_attributes, stack_size, start_address,
    415                                parameter, creation_flags, thread_id);
    416     if (thread)
    417       return thread;
    418   }
    419 
    420   PSECURITY_DESCRIPTOR sd =
    421       thread_attributes ? thread_attributes->lpSecurityDescriptor : NULL;
    422   CLIENT_ID client_id;
    423 
    424   NTSTATUS result = g_nt.RtlCreateUserThread(NtCurrentProcess, sd,
    425                                              creation_flags & CREATE_SUSPENDED,
    426                                              0, stack_size, 0, start_address,
    427                                              parameter, &thread, &client_id);
    428   if (!NT_SUCCESS(result))
    429     return 0;
    430 
    431   // CSRSS is closed if we got here, so use RtlCreateUserThread from here on.
    432   use_create_thread = false;
    433   if (thread_id)
    434     *thread_id = HandleToUlong(client_id.UniqueThread);
    435   return thread;
    436 }
    437 
    438 // Cache the default LCID to avoid pinging CSRSS after lockdown.
    439 // TODO(jschuh): This approach will miss a default locale changes after
    440 // lockdown. In the future we may want to have the broker check instead.
    441 LCID WINAPI TargetGetUserDefaultLCID(
    442     GetUserDefaultLCIDFunction orig_GetUserDefaultLCID) {
    443   static LCID default_lcid = orig_GetUserDefaultLCID();
    444   return default_lcid;
    445 }
    446 
    447 }  // namespace sandbox
    448