ash: Extend launcher first button to include leading inset.
[chromium-blink-merge.git] / sandbox / src / restricted_token_utils.cc
blob8565f0a33d0f54d8a39c588db3108256a1ebc0d2
1 // Copyright (c) 2012 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.
5 #include <aclapi.h>
6 #include <sddl.h>
7 #include <vector>
9 #include "sandbox/src/restricted_token_utils.h"
11 #include "base/logging.h"
12 #include "base/win/scoped_handle.h"
13 #include "base/win/scoped_process_information.h"
14 #include "base/win/windows_version.h"
15 #include "sandbox/src/job.h"
16 #include "sandbox/src/restricted_token.h"
17 #include "sandbox/src/security_level.h"
18 #include "sandbox/src/sid.h"
20 namespace sandbox {
22 DWORD CreateRestrictedToken(HANDLE *token_handle,
23 TokenLevel security_level,
24 IntegrityLevel integrity_level,
25 TokenType token_type) {
26 if (!token_handle)
27 return ERROR_BAD_ARGUMENTS;
29 RestrictedToken restricted_token;
30 restricted_token.Init(NULL); // Initialized with the current process token
32 std::vector<std::wstring> privilege_exceptions;
33 std::vector<Sid> sid_exceptions;
35 bool deny_sids = true;
36 bool remove_privileges = true;
38 switch (security_level) {
39 case USER_UNPROTECTED: {
40 deny_sids = false;
41 remove_privileges = false;
42 break;
44 case USER_RESTRICTED_SAME_ACCESS: {
45 deny_sids = false;
46 remove_privileges = false;
48 unsigned err_code = restricted_token.AddRestrictingSidAllSids();
49 if (ERROR_SUCCESS != err_code)
50 return err_code;
52 break;
54 case USER_NON_ADMIN: {
55 sid_exceptions.push_back(WinBuiltinUsersSid);
56 sid_exceptions.push_back(WinWorldSid);
57 sid_exceptions.push_back(WinInteractiveSid);
58 sid_exceptions.push_back(WinAuthenticatedUserSid);
59 privilege_exceptions.push_back(SE_CHANGE_NOTIFY_NAME);
60 break;
62 case USER_INTERACTIVE: {
63 sid_exceptions.push_back(WinBuiltinUsersSid);
64 sid_exceptions.push_back(WinWorldSid);
65 sid_exceptions.push_back(WinInteractiveSid);
66 sid_exceptions.push_back(WinAuthenticatedUserSid);
67 privilege_exceptions.push_back(SE_CHANGE_NOTIFY_NAME);
68 restricted_token.AddRestrictingSid(WinBuiltinUsersSid);
69 restricted_token.AddRestrictingSid(WinWorldSid);
70 restricted_token.AddRestrictingSid(WinRestrictedCodeSid);
71 restricted_token.AddRestrictingSidCurrentUser();
72 restricted_token.AddRestrictingSidLogonSession();
73 break;
75 case USER_LIMITED: {
76 sid_exceptions.push_back(WinBuiltinUsersSid);
77 sid_exceptions.push_back(WinWorldSid);
78 sid_exceptions.push_back(WinInteractiveSid);
79 privilege_exceptions.push_back(SE_CHANGE_NOTIFY_NAME);
80 restricted_token.AddRestrictingSid(WinBuiltinUsersSid);
81 restricted_token.AddRestrictingSid(WinWorldSid);
82 restricted_token.AddRestrictingSid(WinRestrictedCodeSid);
84 // This token has to be able to create objects in BNO.
85 // Unfortunately, on vista, it needs the current logon sid
86 // in the token to achieve this. You should also set the process to be
87 // low integrity level so it can't access object created by other
88 // processes.
89 if (base::win::GetVersion() >= base::win::VERSION_VISTA)
90 restricted_token.AddRestrictingSidLogonSession();
91 break;
93 case USER_RESTRICTED: {
94 privilege_exceptions.push_back(SE_CHANGE_NOTIFY_NAME);
95 restricted_token.AddUserSidForDenyOnly();
96 restricted_token.AddRestrictingSid(WinRestrictedCodeSid);
97 break;
99 case USER_LOCKDOWN: {
100 restricted_token.AddUserSidForDenyOnly();
101 restricted_token.AddRestrictingSid(WinNullSid);
102 break;
104 default: {
105 return ERROR_BAD_ARGUMENTS;
109 DWORD err_code = ERROR_SUCCESS;
110 if (deny_sids) {
111 err_code = restricted_token.AddAllSidsForDenyOnly(&sid_exceptions);
112 if (ERROR_SUCCESS != err_code)
113 return err_code;
116 if (remove_privileges) {
117 err_code = restricted_token.DeleteAllPrivileges(&privilege_exceptions);
118 if (ERROR_SUCCESS != err_code)
119 return err_code;
122 restricted_token.SetIntegrityLevel(integrity_level);
124 switch (token_type) {
125 case PRIMARY: {
126 err_code = restricted_token.GetRestrictedTokenHandle(token_handle);
127 break;
129 case IMPERSONATION: {
130 err_code = restricted_token.GetRestrictedTokenHandleForImpersonation(
131 token_handle);
132 break;
134 default: {
135 err_code = ERROR_BAD_ARGUMENTS;
136 break;
140 return err_code;
143 DWORD StartRestrictedProcessInJob(wchar_t *command_line,
144 TokenLevel primary_level,
145 TokenLevel impersonation_level,
146 JobLevel job_level,
147 HANDLE *const job_handle_ret) {
148 Job job;
149 DWORD err_code = job.Init(job_level, NULL, 0);
150 if (ERROR_SUCCESS != err_code)
151 return err_code;
153 if (JOB_UNPROTECTED != job_level) {
154 // Share the Desktop handle to be able to use MessageBox() in the sandboxed
155 // application.
156 err_code = job.UserHandleGrantAccess(GetDesktopWindow());
157 if (ERROR_SUCCESS != err_code)
158 return err_code;
161 // Create the primary (restricted) token for the process
162 HANDLE primary_token_handle = NULL;
163 err_code = CreateRestrictedToken(&primary_token_handle,
164 primary_level,
165 INTEGRITY_LEVEL_LAST,
166 PRIMARY);
167 if (ERROR_SUCCESS != err_code) {
168 return err_code;
170 base::win::ScopedHandle primary_token(primary_token_handle);
172 // Create the impersonation token (restricted) to be able to start the
173 // process.
174 HANDLE impersonation_token_handle;
175 err_code = CreateRestrictedToken(&impersonation_token_handle,
176 impersonation_level,
177 INTEGRITY_LEVEL_LAST,
178 IMPERSONATION);
179 if (ERROR_SUCCESS != err_code) {
180 return err_code;
182 base::win::ScopedHandle impersonation_token(impersonation_token_handle);
184 // Start the process
185 STARTUPINFO startup_info = {0};
186 base::win::ScopedProcessInformation process_info;
188 if (!::CreateProcessAsUser(primary_token.Get(),
189 NULL, // No application name.
190 command_line,
191 NULL, // No security attribute.
192 NULL, // No thread attribute.
193 FALSE, // Do not inherit handles.
194 CREATE_SUSPENDED | CREATE_BREAKAWAY_FROM_JOB,
195 NULL, // Use the environment of the caller.
196 NULL, // Use current directory of the caller.
197 &startup_info,
198 process_info.Receive())) {
199 return ::GetLastError();
202 // Change the token of the main thread of the new process for the
203 // impersonation token with more rights.
205 HANDLE temp_thread = process_info.thread_handle();
206 if (!::SetThreadToken(&temp_thread, impersonation_token.Get())) {
207 ::TerminateProcess(process_info.process_handle(),
208 0); // exit code
209 return ::GetLastError();
213 err_code = job.AssignProcessToJob(process_info.process_handle());
214 if (ERROR_SUCCESS != err_code) {
215 ::TerminateProcess(process_info.process_handle(),
216 0); // exit code
217 return ::GetLastError();
220 // Start the application
221 ::ResumeThread(process_info.thread_handle());
223 (*job_handle_ret) = job.Detach();
225 return ERROR_SUCCESS;
228 DWORD SetObjectIntegrityLabel(HANDLE handle, SE_OBJECT_TYPE type,
229 const wchar_t* ace_access,
230 const wchar_t* integrity_level_sid) {
231 // Build the SDDL string for the label.
232 std::wstring sddl = L"S:("; // SDDL for a SACL.
233 sddl += SDDL_MANDATORY_LABEL; // Ace Type is "Mandatory Label".
234 sddl += L";;"; // No Ace Flags.
235 sddl += ace_access; // Add the ACE access.
236 sddl += L";;;"; // No ObjectType and Inherited Object Type.
237 sddl += integrity_level_sid; // Trustee Sid.
238 sddl += L")";
240 DWORD error = ERROR_SUCCESS;
241 PSECURITY_DESCRIPTOR sec_desc = NULL;
243 PACL sacl = NULL;
244 BOOL sacl_present = FALSE;
245 BOOL sacl_defaulted = FALSE;
247 if (::ConvertStringSecurityDescriptorToSecurityDescriptorW(sddl.c_str(),
248 SDDL_REVISION,
249 &sec_desc, NULL)) {
250 if (::GetSecurityDescriptorSacl(sec_desc, &sacl_present, &sacl,
251 &sacl_defaulted)) {
252 error = ::SetSecurityInfo(handle, type,
253 LABEL_SECURITY_INFORMATION, NULL, NULL, NULL,
254 sacl);
255 } else {
256 error = ::GetLastError();
259 ::LocalFree(sec_desc);
260 } else {
261 return::GetLastError();
264 return error;
267 const wchar_t* GetIntegrityLevelString(IntegrityLevel integrity_level) {
268 switch (integrity_level) {
269 case INTEGRITY_LEVEL_SYSTEM:
270 return L"S-1-16-16384";
271 case INTEGRITY_LEVEL_HIGH:
272 return L"S-1-16-12288";
273 case INTEGRITY_LEVEL_MEDIUM:
274 return L"S-1-16-8192";
275 case INTEGRITY_LEVEL_MEDIUM_LOW:
276 return L"S-1-16-6144";
277 case INTEGRITY_LEVEL_LOW:
278 return L"S-1-16-4096";
279 case INTEGRITY_LEVEL_BELOW_LOW:
280 return L"S-1-16-2048";
281 case INTEGRITY_LEVEL_UNTRUSTED:
282 return L"S-1-16-0";
283 case INTEGRITY_LEVEL_LAST:
284 return NULL;
287 NOTREACHED();
288 return NULL;
290 DWORD SetTokenIntegrityLevel(HANDLE token, IntegrityLevel integrity_level) {
291 if (base::win::GetVersion() < base::win::VERSION_VISTA)
292 return ERROR_SUCCESS;
294 const wchar_t* integrity_level_str = GetIntegrityLevelString(integrity_level);
295 if (!integrity_level_str) {
296 // No mandatory level specified, we don't change it.
297 return ERROR_SUCCESS;
300 PSID integrity_sid = NULL;
301 if (!::ConvertStringSidToSid(integrity_level_str, &integrity_sid))
302 return ::GetLastError();
304 TOKEN_MANDATORY_LABEL label = {0};
305 label.Label.Attributes = SE_GROUP_INTEGRITY;
306 label.Label.Sid = integrity_sid;
308 DWORD size = sizeof(TOKEN_MANDATORY_LABEL) + ::GetLengthSid(integrity_sid);
309 BOOL result = ::SetTokenInformation(token, TokenIntegrityLevel, &label,
310 size);
311 ::LocalFree(integrity_sid);
313 return result ? ERROR_SUCCESS : ::GetLastError();
316 DWORD SetProcessIntegrityLevel(IntegrityLevel integrity_level) {
317 if (base::win::GetVersion() < base::win::VERSION_VISTA)
318 return ERROR_SUCCESS;
320 // We don't check for an invalid level here because we'll just let it
321 // fail on the SetTokenIntegrityLevel call later on.
322 if (integrity_level == INTEGRITY_LEVEL_LAST) {
323 // No mandatory level specified, we don't change it.
324 return ERROR_SUCCESS;
327 HANDLE token_handle;
328 if (!::OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_DEFAULT,
329 &token_handle))
330 return ::GetLastError();
332 base::win::ScopedHandle token(token_handle);
334 return SetTokenIntegrityLevel(token.Get(), integrity_level);
337 } // namespace sandbox