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 "sandbox/win/src/process_thread_policy.h"
6
7#include <string>
8
9#include "base/memory/scoped_ptr.h"
10#include "sandbox/win/src/ipc_tags.h"
11#include "sandbox/win/src/nt_internals.h"
12#include "sandbox/win/src/policy_engine_opcodes.h"
13#include "sandbox/win/src/policy_params.h"
14#include "sandbox/win/src/sandbox_types.h"
15#include "sandbox/win/src/win_utils.h"
16
17namespace {
18
19// These are the only safe rights that can be given to a sandboxed
20// process for the process created by the broker. All others are potential
21// vectors of privilege elevation.
22const DWORD kProcessRights = SYNCHRONIZE |
23                             PROCESS_QUERY_INFORMATION |
24                             PROCESS_QUERY_LIMITED_INFORMATION |
25                             PROCESS_TERMINATE |
26                             PROCESS_SUSPEND_RESUME;
27
28const DWORD kThreadRights = SYNCHRONIZE |
29                            THREAD_TERMINATE |
30                            THREAD_SUSPEND_RESUME |
31                            THREAD_QUERY_INFORMATION |
32                            THREAD_QUERY_LIMITED_INFORMATION |
33                            THREAD_SET_LIMITED_INFORMATION;
34
35// Creates a child process and duplicates the handles to 'target_process'. The
36// remaining parameters are the same as CreateProcess().
37BOOL CreateProcessExWHelper(HANDLE target_process, BOOL give_full_access,
38                            LPCWSTR lpApplicationName, LPWSTR lpCommandLine,
39                            LPSECURITY_ATTRIBUTES lpProcessAttributes,
40                            LPSECURITY_ATTRIBUTES lpThreadAttributes,
41                            BOOL bInheritHandles, DWORD dwCreationFlags,
42                            LPVOID lpEnvironment, LPCWSTR lpCurrentDirectory,
43                            LPSTARTUPINFOW lpStartupInfo,
44                            LPPROCESS_INFORMATION lpProcessInformation) {
45  if (!::CreateProcessW(lpApplicationName, lpCommandLine, lpProcessAttributes,
46                        lpThreadAttributes, bInheritHandles, dwCreationFlags,
47                        lpEnvironment, lpCurrentDirectory, lpStartupInfo,
48                        lpProcessInformation)) {
49    return FALSE;
50  }
51
52  DWORD process_access = kProcessRights;
53  DWORD thread_access = kThreadRights;
54  if (give_full_access) {
55    process_access = PROCESS_ALL_ACCESS;
56    thread_access = THREAD_ALL_ACCESS;
57  }
58  if (!::DuplicateHandle(::GetCurrentProcess(), lpProcessInformation->hProcess,
59                         target_process, &lpProcessInformation->hProcess,
60                         process_access, FALSE, DUPLICATE_CLOSE_SOURCE)) {
61    ::CloseHandle(lpProcessInformation->hThread);
62    return FALSE;
63  }
64  if (!::DuplicateHandle(::GetCurrentProcess(), lpProcessInformation->hThread,
65                         target_process, &lpProcessInformation->hThread,
66                         thread_access, FALSE, DUPLICATE_CLOSE_SOURCE)) {
67    return FALSE;
68  }
69  return TRUE;
70}
71
72}
73
74namespace sandbox {
75
76bool ProcessPolicy::GenerateRules(const wchar_t* name,
77                                  TargetPolicy::Semantics semantics,
78                                  LowLevelPolicy* policy) {
79  scoped_ptr<PolicyRule> process;
80  switch (semantics) {
81    case TargetPolicy::PROCESS_MIN_EXEC: {
82      process.reset(new PolicyRule(GIVE_READONLY));
83      break;
84    };
85    case TargetPolicy::PROCESS_ALL_EXEC: {
86      process.reset(new PolicyRule(GIVE_ALLACCESS));
87      break;
88    };
89    default: {
90      return false;
91    };
92  }
93
94  if (!process->AddStringMatch(IF, NameBased::NAME, name, CASE_INSENSITIVE)) {
95    return false;
96  }
97  if (!policy->AddRule(IPC_CREATEPROCESSW_TAG, process.get())) {
98    return false;
99  }
100  return true;
101}
102
103NTSTATUS ProcessPolicy::OpenThreadAction(const ClientInfo& client_info,
104                                         uint32 desired_access,
105                                         uint32 thread_id,
106                                         HANDLE* handle) {
107  *handle = NULL;
108
109  NtOpenThreadFunction NtOpenThread = NULL;
110  ResolveNTFunctionPtr("NtOpenThread", &NtOpenThread);
111
112  OBJECT_ATTRIBUTES attributes = {0};
113  attributes.Length = sizeof(attributes);
114  CLIENT_ID client_id = {0};
115  client_id.UniqueProcess = reinterpret_cast<PVOID>(
116                                static_cast<ULONG_PTR>(client_info.process_id));
117  client_id.UniqueThread =
118      reinterpret_cast<PVOID>(static_cast<ULONG_PTR>(thread_id));
119
120  HANDLE local_handle;
121  NTSTATUS status = NtOpenThread(&local_handle, desired_access, &attributes,
122                                 &client_id);
123  if (NT_SUCCESS(status)) {
124    if (!::DuplicateHandle(::GetCurrentProcess(), local_handle,
125                           client_info.process, handle, 0, FALSE,
126                           DUPLICATE_CLOSE_SOURCE | DUPLICATE_SAME_ACCESS)) {
127      return STATUS_ACCESS_DENIED;
128    }
129  }
130
131  return status;
132}
133
134NTSTATUS ProcessPolicy::OpenProcessAction(const ClientInfo& client_info,
135                                          uint32 desired_access,
136                                          uint32 process_id,
137                                          HANDLE* handle) {
138  *handle = NULL;
139
140  NtOpenProcessFunction NtOpenProcess = NULL;
141  ResolveNTFunctionPtr("NtOpenProcess", &NtOpenProcess);
142
143  if (client_info.process_id != process_id)
144    return STATUS_ACCESS_DENIED;
145
146  OBJECT_ATTRIBUTES attributes = {0};
147  attributes.Length = sizeof(attributes);
148  CLIENT_ID client_id = {0};
149  client_id.UniqueProcess = reinterpret_cast<PVOID>(
150                                static_cast<ULONG_PTR>(client_info.process_id));
151  HANDLE local_handle;
152  NTSTATUS status = NtOpenProcess(&local_handle, desired_access, &attributes,
153                                  &client_id);
154  if (NT_SUCCESS(status)) {
155    if (!::DuplicateHandle(::GetCurrentProcess(), local_handle,
156                           client_info.process, handle, 0, FALSE,
157                           DUPLICATE_CLOSE_SOURCE | DUPLICATE_SAME_ACCESS)) {
158      return STATUS_ACCESS_DENIED;
159    }
160  }
161
162  return status;
163}
164
165NTSTATUS ProcessPolicy::OpenProcessTokenAction(const ClientInfo& client_info,
166                                               HANDLE process,
167                                               uint32 desired_access,
168                                               HANDLE* handle) {
169  *handle = NULL;
170  NtOpenProcessTokenFunction NtOpenProcessToken = NULL;
171  ResolveNTFunctionPtr("NtOpenProcessToken", &NtOpenProcessToken);
172
173  if (CURRENT_PROCESS != process)
174    return STATUS_ACCESS_DENIED;
175
176  HANDLE local_handle;
177  NTSTATUS status = NtOpenProcessToken(client_info.process, desired_access,
178                                       &local_handle);
179  if (NT_SUCCESS(status)) {
180    if (!::DuplicateHandle(::GetCurrentProcess(), local_handle,
181                           client_info.process, handle, 0, FALSE,
182                           DUPLICATE_CLOSE_SOURCE | DUPLICATE_SAME_ACCESS)) {
183      return STATUS_ACCESS_DENIED;
184    }
185  }
186  return status;
187}
188
189NTSTATUS ProcessPolicy::OpenProcessTokenExAction(const ClientInfo& client_info,
190                                                 HANDLE process,
191                                                 uint32 desired_access,
192                                                 uint32 attributes,
193                                                 HANDLE* handle) {
194  *handle = NULL;
195  NtOpenProcessTokenExFunction NtOpenProcessTokenEx = NULL;
196  ResolveNTFunctionPtr("NtOpenProcessTokenEx", &NtOpenProcessTokenEx);
197
198  if (CURRENT_PROCESS != process)
199    return STATUS_ACCESS_DENIED;
200
201  HANDLE local_handle;
202  NTSTATUS status = NtOpenProcessTokenEx(client_info.process, desired_access,
203                                         attributes, &local_handle);
204  if (NT_SUCCESS(status)) {
205    if (!::DuplicateHandle(::GetCurrentProcess(), local_handle,
206                           client_info.process, handle, 0, FALSE,
207                           DUPLICATE_CLOSE_SOURCE | DUPLICATE_SAME_ACCESS)) {
208      return STATUS_ACCESS_DENIED;
209    }
210  }
211  return status;
212}
213
214DWORD ProcessPolicy::CreateProcessWAction(EvalResult eval_result,
215                                          const ClientInfo& client_info,
216                                          const base::string16 &app_name,
217                                          const base::string16 &command_line,
218                                          PROCESS_INFORMATION* process_info) {
219  // The only action supported is ASK_BROKER which means create the process.
220  if (GIVE_ALLACCESS != eval_result && GIVE_READONLY != eval_result) {
221    return ERROR_ACCESS_DENIED;
222  }
223
224  STARTUPINFO startup_info = {0};
225  startup_info.cb = sizeof(startup_info);
226  scoped_ptr<wchar_t, base::FreeDeleter>
227      cmd_line(_wcsdup(command_line.c_str()));
228
229  BOOL should_give_full_access = (GIVE_ALLACCESS == eval_result);
230  if (!CreateProcessExWHelper(client_info.process, should_give_full_access,
231                              app_name.c_str(), cmd_line.get(), NULL, NULL,
232                              FALSE, 0, NULL, NULL, &startup_info,
233                              process_info)) {
234    return ERROR_ACCESS_DENIED;
235  }
236  return ERROR_SUCCESS;
237}
238
239}  // namespace sandbox
240