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 "sandbox/win/src/restricted_token.h"
9 #include "base/logging.h"
10 #include "base/memory/scoped_ptr.h"
11 #include "sandbox/win/src/acl.h"
12 #include "sandbox/win/src/win_utils.h"
16 // Calls GetTokenInformation with the desired |info_class| and returns a buffer
18 scoped_ptr
<BYTE
[]> GetTokenInfo(const base::win::ScopedHandle
& token
,
19 TOKEN_INFORMATION_CLASS info_class
,
21 // Get the required buffer size.
23 ::GetTokenInformation(token
.Get(), info_class
, NULL
, 0, &size
);
25 *error
= ::GetLastError();
29 scoped_ptr
<BYTE
[]> buffer(new BYTE
[size
]);
30 if (!::GetTokenInformation(token
.Get(), info_class
, buffer
.get(), size
,
32 *error
= ::GetLastError();
36 *error
= ERROR_SUCCESS
;
44 RestrictedToken::RestrictedToken()
45 : integrity_level_(INTEGRITY_LEVEL_LAST
),
49 RestrictedToken::~RestrictedToken() {
52 DWORD
RestrictedToken::Init(const HANDLE effective_token
) {
54 return ERROR_ALREADY_INITIALIZED
;
57 if (effective_token
) {
58 // We duplicate the handle to be able to use it even if the original handle
60 if (!::DuplicateHandle(::GetCurrentProcess(), effective_token
,
61 ::GetCurrentProcess(), &temp_token
,
62 0, FALSE
, DUPLICATE_SAME_ACCESS
)) {
63 return ::GetLastError();
66 if (!::OpenProcessToken(::GetCurrentProcess(), TOKEN_ALL_ACCESS
,
68 return ::GetLastError();
71 effective_token_
.Set(temp_token
);
77 DWORD
RestrictedToken::GetRestrictedToken(
78 base::win::ScopedHandle
* token
) const {
81 return ERROR_NO_TOKEN
;
83 size_t deny_size
= sids_for_deny_only_
.size();
84 size_t restrict_size
= sids_to_restrict_
.size();
85 size_t privileges_size
= privileges_to_disable_
.size();
87 SID_AND_ATTRIBUTES
*deny_only_array
= NULL
;
89 deny_only_array
= new SID_AND_ATTRIBUTES
[deny_size
];
91 for (unsigned int i
= 0; i
< sids_for_deny_only_
.size() ; ++i
) {
92 deny_only_array
[i
].Attributes
= SE_GROUP_USE_FOR_DENY_ONLY
;
93 deny_only_array
[i
].Sid
=
94 const_cast<SID
*>(sids_for_deny_only_
[i
].GetPSID());
98 SID_AND_ATTRIBUTES
*sids_to_restrict_array
= NULL
;
100 sids_to_restrict_array
= new SID_AND_ATTRIBUTES
[restrict_size
];
102 for (unsigned int i
= 0; i
< restrict_size
; ++i
) {
103 sids_to_restrict_array
[i
].Attributes
= 0;
104 sids_to_restrict_array
[i
].Sid
=
105 const_cast<SID
*>(sids_to_restrict_
[i
].GetPSID());
109 LUID_AND_ATTRIBUTES
*privileges_to_disable_array
= NULL
;
110 if (privileges_size
) {
111 privileges_to_disable_array
= new LUID_AND_ATTRIBUTES
[privileges_size
];
113 for (unsigned int i
= 0; i
< privileges_size
; ++i
) {
114 privileges_to_disable_array
[i
].Attributes
= 0;
115 privileges_to_disable_array
[i
].Luid
= privileges_to_disable_
[i
];
120 HANDLE new_token_handle
= NULL
;
121 // The SANDBOX_INERT flag did nothing in XP and it was just a way to tell
122 // if a token has ben restricted given the limiations of IsTokenRestricted()
123 // but it appears that in Windows 7 it hints the AppLocker subsystem to
125 if (deny_size
|| restrict_size
|| privileges_size
) {
126 result
= ::CreateRestrictedToken(effective_token_
.Get(),
128 static_cast<DWORD
>(deny_size
),
130 static_cast<DWORD
>(privileges_size
),
131 privileges_to_disable_array
,
132 static_cast<DWORD
>(restrict_size
),
133 sids_to_restrict_array
,
136 // Duplicate the token even if it's not modified at this point
137 // because any subsequent changes to this token would also affect the
139 result
= ::DuplicateTokenEx(effective_token_
.Get(), TOKEN_ALL_ACCESS
, NULL
,
140 SecurityIdentification
, TokenPrimary
,
143 auto last_error
= ::GetLastError();
146 delete[] deny_only_array
;
148 if (sids_to_restrict_array
)
149 delete[] sids_to_restrict_array
;
151 if (privileges_to_disable_array
)
152 delete[] privileges_to_disable_array
;
157 base::win::ScopedHandle
new_token(new_token_handle
);
159 // Modify the default dacl on the token to contain Restricted and the user.
160 if (!AddSidToDefaultDacl(new_token
.Get(), WinRestrictedCodeSid
, GENERIC_ALL
))
161 return ::GetLastError();
163 if (!AddUserSidToDefaultDacl(new_token
.Get(), GENERIC_ALL
))
164 return ::GetLastError();
166 DWORD error
= SetTokenIntegrityLevel(new_token
.Get(), integrity_level_
);
167 if (ERROR_SUCCESS
!= error
)
171 if (!::DuplicateHandle(::GetCurrentProcess(), new_token
.Get(),
172 ::GetCurrentProcess(), &token_handle
,
173 TOKEN_ALL_ACCESS
, FALSE
, // Don't inherit.
175 return ::GetLastError();
178 token
->Set(token_handle
);
179 return ERROR_SUCCESS
;
182 DWORD
RestrictedToken::GetRestrictedTokenForImpersonation(
183 base::win::ScopedHandle
* token
) const {
186 return ERROR_NO_TOKEN
;
188 base::win::ScopedHandle restricted_token
;
189 DWORD err_code
= GetRestrictedToken(&restricted_token
);
190 if (ERROR_SUCCESS
!= err_code
)
193 HANDLE impersonation_token_handle
;
194 if (!::DuplicateToken(restricted_token
.Get(),
195 SecurityImpersonation
,
196 &impersonation_token_handle
)) {
197 return ::GetLastError();
199 base::win::ScopedHandle
impersonation_token(impersonation_token_handle
);
202 if (!::DuplicateHandle(::GetCurrentProcess(), impersonation_token
.Get(),
203 ::GetCurrentProcess(), &token_handle
,
204 TOKEN_ALL_ACCESS
, FALSE
, // Don't inherit.
206 return ::GetLastError();
209 token
->Set(token_handle
);
210 return ERROR_SUCCESS
;
213 DWORD
RestrictedToken::AddAllSidsForDenyOnly(std::vector
<Sid
> *exceptions
) {
216 return ERROR_NO_TOKEN
;
219 scoped_ptr
<BYTE
[]> buffer
=
220 GetTokenInfo(effective_token_
, TokenGroups
, &error
);
225 TOKEN_GROUPS
* token_groups
= reinterpret_cast<TOKEN_GROUPS
*>(buffer
.get());
227 // Build the list of the deny only group SIDs
228 for (unsigned int i
= 0; i
< token_groups
->GroupCount
; ++i
) {
229 if ((token_groups
->Groups
[i
].Attributes
& SE_GROUP_INTEGRITY
) == 0 &&
230 (token_groups
->Groups
[i
].Attributes
& SE_GROUP_LOGON_ID
) == 0) {
231 bool should_ignore
= false;
233 for (unsigned int j
= 0; j
< exceptions
->size(); ++j
) {
234 if (::EqualSid(const_cast<SID
*>((*exceptions
)[j
].GetPSID()),
235 token_groups
->Groups
[i
].Sid
)) {
236 should_ignore
= true;
241 if (!should_ignore
) {
242 sids_for_deny_only_
.push_back(
243 reinterpret_cast<SID
*>(token_groups
->Groups
[i
].Sid
));
248 return ERROR_SUCCESS
;
251 DWORD
RestrictedToken::AddSidForDenyOnly(const Sid
&sid
) {
254 return ERROR_NO_TOKEN
;
256 sids_for_deny_only_
.push_back(sid
);
257 return ERROR_SUCCESS
;
260 DWORD
RestrictedToken::AddUserSidForDenyOnly() {
263 return ERROR_NO_TOKEN
;
265 DWORD size
= sizeof(TOKEN_USER
) + SECURITY_MAX_SID_SIZE
;
266 scoped_ptr
<BYTE
[]> buffer(new BYTE
[size
]);
267 TOKEN_USER
* token_user
= reinterpret_cast<TOKEN_USER
*>(buffer
.get());
269 BOOL result
= ::GetTokenInformation(effective_token_
.Get(), TokenUser
,
270 token_user
, size
, &size
);
273 return ::GetLastError();
275 Sid user
= reinterpret_cast<SID
*>(token_user
->User
.Sid
);
276 sids_for_deny_only_
.push_back(user
);
278 return ERROR_SUCCESS
;
281 DWORD
RestrictedToken::DeleteAllPrivileges(
282 const std::vector
<base::string16
> *exceptions
) {
285 return ERROR_NO_TOKEN
;
288 scoped_ptr
<BYTE
[]> buffer
=
289 GetTokenInfo(effective_token_
, TokenPrivileges
, &error
);
294 TOKEN_PRIVILEGES
* token_privileges
=
295 reinterpret_cast<TOKEN_PRIVILEGES
*>(buffer
.get());
297 // Build the list of privileges to disable
298 for (unsigned int i
= 0; i
< token_privileges
->PrivilegeCount
; ++i
) {
299 bool should_ignore
= false;
301 for (unsigned int j
= 0; j
< exceptions
->size(); ++j
) {
303 ::LookupPrivilegeValue(NULL
, (*exceptions
)[j
].c_str(), &luid
);
304 if (token_privileges
->Privileges
[i
].Luid
.HighPart
== luid
.HighPart
&&
305 token_privileges
->Privileges
[i
].Luid
.LowPart
== luid
.LowPart
) {
306 should_ignore
= true;
311 if (!should_ignore
) {
312 privileges_to_disable_
.push_back(token_privileges
->Privileges
[i
].Luid
);
316 return ERROR_SUCCESS
;
319 DWORD
RestrictedToken::DeletePrivilege(const wchar_t *privilege
) {
322 return ERROR_NO_TOKEN
;
325 if (LookupPrivilegeValue(NULL
, privilege
, &luid
))
326 privileges_to_disable_
.push_back(luid
);
328 return ::GetLastError();
330 return ERROR_SUCCESS
;
333 DWORD
RestrictedToken::AddRestrictingSid(const Sid
&sid
) {
336 return ERROR_NO_TOKEN
;
338 sids_to_restrict_
.push_back(sid
); // No attributes
339 return ERROR_SUCCESS
;
342 DWORD
RestrictedToken::AddRestrictingSidLogonSession() {
345 return ERROR_NO_TOKEN
;
348 scoped_ptr
<BYTE
[]> buffer
=
349 GetTokenInfo(effective_token_
, TokenGroups
, &error
);
354 TOKEN_GROUPS
* token_groups
= reinterpret_cast<TOKEN_GROUPS
*>(buffer
.get());
356 SID
*logon_sid
= NULL
;
357 for (unsigned int i
= 0; i
< token_groups
->GroupCount
; ++i
) {
358 if ((token_groups
->Groups
[i
].Attributes
& SE_GROUP_LOGON_ID
) != 0) {
359 logon_sid
= static_cast<SID
*>(token_groups
->Groups
[i
].Sid
);
365 sids_to_restrict_
.push_back(logon_sid
);
367 return ERROR_SUCCESS
;
370 DWORD
RestrictedToken::AddRestrictingSidCurrentUser() {
373 return ERROR_NO_TOKEN
;
375 DWORD size
= sizeof(TOKEN_USER
) + SECURITY_MAX_SID_SIZE
;
376 scoped_ptr
<BYTE
[]> buffer(new BYTE
[size
]);
377 TOKEN_USER
* token_user
= reinterpret_cast<TOKEN_USER
*>(buffer
.get());
379 BOOL result
= ::GetTokenInformation(effective_token_
.Get(), TokenUser
,
380 token_user
, size
, &size
);
383 return ::GetLastError();
385 Sid user
= reinterpret_cast<SID
*>(token_user
->User
.Sid
);
386 sids_to_restrict_
.push_back(user
);
388 return ERROR_SUCCESS
;
391 DWORD
RestrictedToken::AddRestrictingSidAllSids() {
394 return ERROR_NO_TOKEN
;
396 // Add the current user to the list.
397 DWORD error
= AddRestrictingSidCurrentUser();
398 if (ERROR_SUCCESS
!= error
)
401 scoped_ptr
<BYTE
[]> buffer
=
402 GetTokenInfo(effective_token_
, TokenGroups
, &error
);
407 TOKEN_GROUPS
* token_groups
= reinterpret_cast<TOKEN_GROUPS
*>(buffer
.get());
409 // Build the list of restricting sids from all groups.
410 for (unsigned int i
= 0; i
< token_groups
->GroupCount
; ++i
) {
411 if ((token_groups
->Groups
[i
].Attributes
& SE_GROUP_INTEGRITY
) == 0)
412 AddRestrictingSid(reinterpret_cast<SID
*>(token_groups
->Groups
[i
].Sid
));
415 return ERROR_SUCCESS
;
418 DWORD
RestrictedToken::SetIntegrityLevel(IntegrityLevel integrity_level
) {
419 integrity_level_
= integrity_level
;
420 return ERROR_SUCCESS
;
423 } // namespace sandbox