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/win/src/restricted_token_utils.h"
11 #include "base/logging.h"
12 #include "base/win/scoped_handle.h"
13 #include "base/win/windows_version.h"
14 #include "sandbox/win/src/job.h"
15 #include "sandbox/win/src/restricted_token.h"
16 #include "sandbox/win/src/security_level.h"
17 #include "sandbox/win/src/sid.h"
21 DWORD
CreateRestrictedToken(TokenLevel security_level
,
22 IntegrityLevel integrity_level
,
24 base::win::ScopedHandle
* token
) {
25 RestrictedToken restricted_token
;
26 restricted_token
.Init(NULL
); // Initialized with the current process token
28 std::vector
<base::string16
> privilege_exceptions
;
29 std::vector
<Sid
> sid_exceptions
;
31 bool deny_sids
= true;
32 bool remove_privileges
= true;
34 switch (security_level
) {
35 case USER_UNPROTECTED
: {
37 remove_privileges
= false;
40 case USER_RESTRICTED_SAME_ACCESS
: {
42 remove_privileges
= false;
44 unsigned err_code
= restricted_token
.AddRestrictingSidAllSids();
45 if (ERROR_SUCCESS
!= err_code
)
50 case USER_NON_ADMIN
: {
51 sid_exceptions
.push_back(WinBuiltinUsersSid
);
52 sid_exceptions
.push_back(WinWorldSid
);
53 sid_exceptions
.push_back(WinInteractiveSid
);
54 sid_exceptions
.push_back(WinAuthenticatedUserSid
);
55 privilege_exceptions
.push_back(SE_CHANGE_NOTIFY_NAME
);
58 case USER_INTERACTIVE
: {
59 sid_exceptions
.push_back(WinBuiltinUsersSid
);
60 sid_exceptions
.push_back(WinWorldSid
);
61 sid_exceptions
.push_back(WinInteractiveSid
);
62 sid_exceptions
.push_back(WinAuthenticatedUserSid
);
63 privilege_exceptions
.push_back(SE_CHANGE_NOTIFY_NAME
);
64 restricted_token
.AddRestrictingSid(WinBuiltinUsersSid
);
65 restricted_token
.AddRestrictingSid(WinWorldSid
);
66 restricted_token
.AddRestrictingSid(WinRestrictedCodeSid
);
67 restricted_token
.AddRestrictingSidCurrentUser();
68 restricted_token
.AddRestrictingSidLogonSession();
72 sid_exceptions
.push_back(WinBuiltinUsersSid
);
73 sid_exceptions
.push_back(WinWorldSid
);
74 sid_exceptions
.push_back(WinInteractiveSid
);
75 privilege_exceptions
.push_back(SE_CHANGE_NOTIFY_NAME
);
76 restricted_token
.AddRestrictingSid(WinBuiltinUsersSid
);
77 restricted_token
.AddRestrictingSid(WinWorldSid
);
78 restricted_token
.AddRestrictingSid(WinRestrictedCodeSid
);
80 // This token has to be able to create objects in BNO.
81 // Unfortunately, on vista, it needs the current logon sid
82 // in the token to achieve this. You should also set the process to be
83 // low integrity level so it can't access object created by other
85 if (base::win::GetVersion() >= base::win::VERSION_VISTA
)
86 restricted_token
.AddRestrictingSidLogonSession();
89 case USER_RESTRICTED
: {
90 privilege_exceptions
.push_back(SE_CHANGE_NOTIFY_NAME
);
91 restricted_token
.AddUserSidForDenyOnly();
92 restricted_token
.AddRestrictingSid(WinRestrictedCodeSid
);
96 restricted_token
.AddUserSidForDenyOnly();
97 restricted_token
.AddRestrictingSid(WinNullSid
);
101 return ERROR_BAD_ARGUMENTS
;
105 DWORD err_code
= ERROR_SUCCESS
;
107 err_code
= restricted_token
.AddAllSidsForDenyOnly(&sid_exceptions
);
108 if (ERROR_SUCCESS
!= err_code
)
112 if (remove_privileges
) {
113 err_code
= restricted_token
.DeleteAllPrivileges(&privilege_exceptions
);
114 if (ERROR_SUCCESS
!= err_code
)
118 restricted_token
.SetIntegrityLevel(integrity_level
);
120 switch (token_type
) {
122 err_code
= restricted_token
.GetRestrictedToken(token
);
125 case IMPERSONATION
: {
126 err_code
= restricted_token
.GetRestrictedTokenForImpersonation(token
);
130 err_code
= ERROR_BAD_ARGUMENTS
;
138 DWORD
SetObjectIntegrityLabel(HANDLE handle
, SE_OBJECT_TYPE type
,
139 const wchar_t* ace_access
,
140 const wchar_t* integrity_level_sid
) {
141 // Build the SDDL string for the label.
142 base::string16 sddl
= L
"S:("; // SDDL for a SACL.
143 sddl
+= SDDL_MANDATORY_LABEL
; // Ace Type is "Mandatory Label".
144 sddl
+= L
";;"; // No Ace Flags.
145 sddl
+= ace_access
; // Add the ACE access.
146 sddl
+= L
";;;"; // No ObjectType and Inherited Object Type.
147 sddl
+= integrity_level_sid
; // Trustee Sid.
150 DWORD error
= ERROR_SUCCESS
;
151 PSECURITY_DESCRIPTOR sec_desc
= NULL
;
154 BOOL sacl_present
= FALSE
;
155 BOOL sacl_defaulted
= FALSE
;
157 if (::ConvertStringSecurityDescriptorToSecurityDescriptorW(sddl
.c_str(),
160 if (::GetSecurityDescriptorSacl(sec_desc
, &sacl_present
, &sacl
,
162 error
= ::SetSecurityInfo(handle
, type
,
163 LABEL_SECURITY_INFORMATION
, NULL
, NULL
, NULL
,
166 error
= ::GetLastError();
169 ::LocalFree(sec_desc
);
171 return::GetLastError();
177 const wchar_t* GetIntegrityLevelString(IntegrityLevel integrity_level
) {
178 switch (integrity_level
) {
179 case INTEGRITY_LEVEL_SYSTEM
:
180 return L
"S-1-16-16384";
181 case INTEGRITY_LEVEL_HIGH
:
182 return L
"S-1-16-12288";
183 case INTEGRITY_LEVEL_MEDIUM
:
184 return L
"S-1-16-8192";
185 case INTEGRITY_LEVEL_MEDIUM_LOW
:
186 return L
"S-1-16-6144";
187 case INTEGRITY_LEVEL_LOW
:
188 return L
"S-1-16-4096";
189 case INTEGRITY_LEVEL_BELOW_LOW
:
190 return L
"S-1-16-2048";
191 case INTEGRITY_LEVEL_UNTRUSTED
:
193 case INTEGRITY_LEVEL_LAST
:
200 DWORD
SetTokenIntegrityLevel(HANDLE token
, IntegrityLevel integrity_level
) {
201 if (base::win::GetVersion() < base::win::VERSION_VISTA
)
202 return ERROR_SUCCESS
;
204 const wchar_t* integrity_level_str
= GetIntegrityLevelString(integrity_level
);
205 if (!integrity_level_str
) {
206 // No mandatory level specified, we don't change it.
207 return ERROR_SUCCESS
;
210 PSID integrity_sid
= NULL
;
211 if (!::ConvertStringSidToSid(integrity_level_str
, &integrity_sid
))
212 return ::GetLastError();
214 TOKEN_MANDATORY_LABEL label
= {};
215 label
.Label
.Attributes
= SE_GROUP_INTEGRITY
;
216 label
.Label
.Sid
= integrity_sid
;
218 DWORD size
= sizeof(TOKEN_MANDATORY_LABEL
) + ::GetLengthSid(integrity_sid
);
219 BOOL result
= ::SetTokenInformation(token
, TokenIntegrityLevel
, &label
,
221 ::LocalFree(integrity_sid
);
223 return result
? ERROR_SUCCESS
: ::GetLastError();
226 DWORD
SetProcessIntegrityLevel(IntegrityLevel integrity_level
) {
227 if (base::win::GetVersion() < base::win::VERSION_VISTA
)
228 return ERROR_SUCCESS
;
230 // We don't check for an invalid level here because we'll just let it
231 // fail on the SetTokenIntegrityLevel call later on.
232 if (integrity_level
== INTEGRITY_LEVEL_LAST
) {
233 // No mandatory level specified, we don't change it.
234 return ERROR_SUCCESS
;
238 if (!::OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_DEFAULT
,
240 return ::GetLastError();
242 base::win::ScopedHandle
token(token_handle
);
244 return SetTokenIntegrityLevel(token
.Get(), integrity_level
);
247 DWORD
HardenTokenIntegrityLevelPolicy(HANDLE token
) {
248 if (base::win::GetVersion() < base::win::VERSION_WIN7
)
249 return ERROR_SUCCESS
;
251 DWORD last_error
= 0;
252 DWORD length_needed
= 0;
254 ::GetKernelObjectSecurity(token
, LABEL_SECURITY_INFORMATION
,
255 NULL
, 0, &length_needed
);
257 last_error
= ::GetLastError();
258 if (last_error
!= ERROR_INSUFFICIENT_BUFFER
)
261 std::vector
<char> security_desc_buffer(length_needed
);
262 PSECURITY_DESCRIPTOR security_desc
=
263 reinterpret_cast<PSECURITY_DESCRIPTOR
>(&security_desc_buffer
[0]);
265 if (!::GetKernelObjectSecurity(token
, LABEL_SECURITY_INFORMATION
,
266 security_desc
, length_needed
,
268 return ::GetLastError();
271 BOOL sacl_present
= FALSE
;
272 BOOL sacl_defaulted
= FALSE
;
274 if (!::GetSecurityDescriptorSacl(security_desc
, &sacl_present
,
275 &sacl
, &sacl_defaulted
))
276 return ::GetLastError();
278 for (DWORD ace_index
= 0; ace_index
< sacl
->AceCount
; ++ace_index
) {
279 PSYSTEM_MANDATORY_LABEL_ACE ace
;
281 if (::GetAce(sacl
, ace_index
, reinterpret_cast<LPVOID
*>(&ace
))
282 && ace
->Header
.AceType
== SYSTEM_MANDATORY_LABEL_ACE_TYPE
) {
283 ace
->Mask
|= SYSTEM_MANDATORY_LABEL_NO_READ_UP
284 | SYSTEM_MANDATORY_LABEL_NO_EXECUTE_UP
;
289 if (!::SetKernelObjectSecurity(token
, LABEL_SECURITY_INFORMATION
,
291 return ::GetLastError();
293 return ERROR_SUCCESS
;
296 DWORD
HardenProcessIntegrityLevelPolicy() {
297 if (base::win::GetVersion() < base::win::VERSION_WIN7
)
298 return ERROR_SUCCESS
;
301 if (!::OpenProcessToken(GetCurrentProcess(), READ_CONTROL
| WRITE_OWNER
,
303 return ::GetLastError();
305 base::win::ScopedHandle
token(token_handle
);
307 return HardenTokenIntegrityLevelPolicy(token
.Get());
310 } // namespace sandbox