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.
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"
22 DWORD
CreateRestrictedToken(HANDLE
*token_handle
,
23 TokenLevel security_level
,
24 IntegrityLevel integrity_level
,
25 TokenType token_type
) {
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
: {
41 remove_privileges
= false;
44 case USER_RESTRICTED_SAME_ACCESS
: {
46 remove_privileges
= false;
48 unsigned err_code
= restricted_token
.AddRestrictingSidAllSids();
49 if (ERROR_SUCCESS
!= err_code
)
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
);
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();
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
89 if (base::win::GetVersion() >= base::win::VERSION_VISTA
)
90 restricted_token
.AddRestrictingSidLogonSession();
93 case USER_RESTRICTED
: {
94 privilege_exceptions
.push_back(SE_CHANGE_NOTIFY_NAME
);
95 restricted_token
.AddUserSidForDenyOnly();
96 restricted_token
.AddRestrictingSid(WinRestrictedCodeSid
);
100 restricted_token
.AddUserSidForDenyOnly();
101 restricted_token
.AddRestrictingSid(WinNullSid
);
105 return ERROR_BAD_ARGUMENTS
;
109 DWORD err_code
= ERROR_SUCCESS
;
111 err_code
= restricted_token
.AddAllSidsForDenyOnly(&sid_exceptions
);
112 if (ERROR_SUCCESS
!= err_code
)
116 if (remove_privileges
) {
117 err_code
= restricted_token
.DeleteAllPrivileges(&privilege_exceptions
);
118 if (ERROR_SUCCESS
!= err_code
)
122 restricted_token
.SetIntegrityLevel(integrity_level
);
124 switch (token_type
) {
126 err_code
= restricted_token
.GetRestrictedTokenHandle(token_handle
);
129 case IMPERSONATION
: {
130 err_code
= restricted_token
.GetRestrictedTokenHandleForImpersonation(
135 err_code
= ERROR_BAD_ARGUMENTS
;
143 DWORD
StartRestrictedProcessInJob(wchar_t *command_line
,
144 TokenLevel primary_level
,
145 TokenLevel impersonation_level
,
147 HANDLE
*const job_handle_ret
) {
149 DWORD err_code
= job
.Init(job_level
, NULL
, 0);
150 if (ERROR_SUCCESS
!= err_code
)
153 if (JOB_UNPROTECTED
!= job_level
) {
154 // Share the Desktop handle to be able to use MessageBox() in the sandboxed
156 err_code
= job
.UserHandleGrantAccess(GetDesktopWindow());
157 if (ERROR_SUCCESS
!= err_code
)
161 // Create the primary (restricted) token for the process
162 HANDLE primary_token_handle
= NULL
;
163 err_code
= CreateRestrictedToken(&primary_token_handle
,
165 INTEGRITY_LEVEL_LAST
,
167 if (ERROR_SUCCESS
!= err_code
) {
170 base::win::ScopedHandle
primary_token(primary_token_handle
);
172 // Create the impersonation token (restricted) to be able to start the
174 HANDLE impersonation_token_handle
;
175 err_code
= CreateRestrictedToken(&impersonation_token_handle
,
177 INTEGRITY_LEVEL_LAST
,
179 if (ERROR_SUCCESS
!= err_code
) {
182 base::win::ScopedHandle
impersonation_token(impersonation_token_handle
);
185 STARTUPINFO startup_info
= {0};
186 base::win::ScopedProcessInformation process_info
;
188 if (!::CreateProcessAsUser(primary_token
.Get(),
189 NULL
, // No application name.
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.
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(),
209 return ::GetLastError();
213 err_code
= job
.AssignProcessToJob(process_info
.process_handle());
214 if (ERROR_SUCCESS
!= err_code
) {
215 ::TerminateProcess(process_info
.process_handle(),
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.
240 DWORD error
= ERROR_SUCCESS
;
241 PSECURITY_DESCRIPTOR sec_desc
= NULL
;
244 BOOL sacl_present
= FALSE
;
245 BOOL sacl_defaulted
= FALSE
;
247 if (::ConvertStringSecurityDescriptorToSecurityDescriptorW(sddl
.c_str(),
250 if (::GetSecurityDescriptorSacl(sec_desc
, &sacl_present
, &sacl
,
252 error
= ::SetSecurityInfo(handle
, type
,
253 LABEL_SECURITY_INFORMATION
, NULL
, NULL
, NULL
,
256 error
= ::GetLastError();
259 ::LocalFree(sec_desc
);
261 return::GetLastError();
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
:
283 case INTEGRITY_LEVEL_LAST
:
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
,
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
;
328 if (!::OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_DEFAULT
,
330 return ::GetLastError();
332 base::win::ScopedHandle
token(token_handle
);
334 return SetTokenIntegrityLevel(token
.Get(), integrity_level
);
337 } // namespace sandbox