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
,
145 delete[] deny_only_array
;
147 if (sids_to_restrict_array
)
148 delete[] sids_to_restrict_array
;
150 if (privileges_to_disable_array
)
151 delete[] privileges_to_disable_array
;
154 return ::GetLastError();
156 base::win::ScopedHandle
new_token(new_token_handle
);
158 // Modify the default dacl on the token to contain Restricted and the user.
159 if (!AddSidToDefaultDacl(new_token
.Get(), WinRestrictedCodeSid
, GENERIC_ALL
))
160 return ::GetLastError();
162 if (!AddUserSidToDefaultDacl(new_token
.Get(), GENERIC_ALL
))
163 return ::GetLastError();
165 DWORD error
= SetTokenIntegrityLevel(new_token
.Get(), integrity_level_
);
166 if (ERROR_SUCCESS
!= error
)
170 if (!::DuplicateHandle(::GetCurrentProcess(), new_token
.Get(),
171 ::GetCurrentProcess(), &token_handle
,
172 TOKEN_ALL_ACCESS
, FALSE
, // Don't inherit.
174 return ::GetLastError();
177 token
->Set(token_handle
);
178 return ERROR_SUCCESS
;
181 DWORD
RestrictedToken::GetRestrictedTokenForImpersonation(
182 base::win::ScopedHandle
* token
) const {
185 return ERROR_NO_TOKEN
;
187 base::win::ScopedHandle restricted_token
;
188 DWORD err_code
= GetRestrictedToken(&restricted_token
);
189 if (ERROR_SUCCESS
!= err_code
)
192 HANDLE impersonation_token_handle
;
193 if (!::DuplicateToken(restricted_token
.Get(),
194 SecurityImpersonation
,
195 &impersonation_token_handle
)) {
196 return ::GetLastError();
198 base::win::ScopedHandle
impersonation_token(impersonation_token_handle
);
201 if (!::DuplicateHandle(::GetCurrentProcess(), impersonation_token
.Get(),
202 ::GetCurrentProcess(), &token_handle
,
203 TOKEN_ALL_ACCESS
, FALSE
, // Don't inherit.
205 return ::GetLastError();
208 token
->Set(token_handle
);
209 return ERROR_SUCCESS
;
212 DWORD
RestrictedToken::AddAllSidsForDenyOnly(std::vector
<Sid
> *exceptions
) {
215 return ERROR_NO_TOKEN
;
218 scoped_ptr
<BYTE
[]> buffer
=
219 GetTokenInfo(effective_token_
, TokenGroups
, &error
);
224 TOKEN_GROUPS
* token_groups
= reinterpret_cast<TOKEN_GROUPS
*>(buffer
.get());
226 // Build the list of the deny only group SIDs
227 for (unsigned int i
= 0; i
< token_groups
->GroupCount
; ++i
) {
228 if ((token_groups
->Groups
[i
].Attributes
& SE_GROUP_INTEGRITY
) == 0 &&
229 (token_groups
->Groups
[i
].Attributes
& SE_GROUP_LOGON_ID
) == 0) {
230 bool should_ignore
= false;
232 for (unsigned int j
= 0; j
< exceptions
->size(); ++j
) {
233 if (::EqualSid(const_cast<SID
*>((*exceptions
)[j
].GetPSID()),
234 token_groups
->Groups
[i
].Sid
)) {
235 should_ignore
= true;
240 if (!should_ignore
) {
241 sids_for_deny_only_
.push_back(
242 reinterpret_cast<SID
*>(token_groups
->Groups
[i
].Sid
));
247 return ERROR_SUCCESS
;
250 DWORD
RestrictedToken::AddSidForDenyOnly(const Sid
&sid
) {
253 return ERROR_NO_TOKEN
;
255 sids_for_deny_only_
.push_back(sid
);
256 return ERROR_SUCCESS
;
259 DWORD
RestrictedToken::AddUserSidForDenyOnly() {
262 return ERROR_NO_TOKEN
;
264 DWORD size
= sizeof(TOKEN_USER
) + SECURITY_MAX_SID_SIZE
;
265 scoped_ptr
<BYTE
[]> buffer(new BYTE
[size
]);
266 TOKEN_USER
* token_user
= reinterpret_cast<TOKEN_USER
*>(buffer
.get());
268 BOOL result
= ::GetTokenInformation(effective_token_
.Get(), TokenUser
,
269 token_user
, size
, &size
);
272 return ::GetLastError();
274 Sid user
= reinterpret_cast<SID
*>(token_user
->User
.Sid
);
275 sids_for_deny_only_
.push_back(user
);
277 return ERROR_SUCCESS
;
280 DWORD
RestrictedToken::DeleteAllPrivileges(
281 const std::vector
<base::string16
> *exceptions
) {
284 return ERROR_NO_TOKEN
;
287 scoped_ptr
<BYTE
[]> buffer
=
288 GetTokenInfo(effective_token_
, TokenPrivileges
, &error
);
293 TOKEN_PRIVILEGES
* token_privileges
=
294 reinterpret_cast<TOKEN_PRIVILEGES
*>(buffer
.get());
296 // Build the list of privileges to disable
297 for (unsigned int i
= 0; i
< token_privileges
->PrivilegeCount
; ++i
) {
298 bool should_ignore
= false;
300 for (unsigned int j
= 0; j
< exceptions
->size(); ++j
) {
302 ::LookupPrivilegeValue(NULL
, (*exceptions
)[j
].c_str(), &luid
);
303 if (token_privileges
->Privileges
[i
].Luid
.HighPart
== luid
.HighPart
&&
304 token_privileges
->Privileges
[i
].Luid
.LowPart
== luid
.LowPart
) {
305 should_ignore
= true;
310 if (!should_ignore
) {
311 privileges_to_disable_
.push_back(token_privileges
->Privileges
[i
].Luid
);
315 return ERROR_SUCCESS
;
318 DWORD
RestrictedToken::DeletePrivilege(const wchar_t *privilege
) {
321 return ERROR_NO_TOKEN
;
324 if (LookupPrivilegeValue(NULL
, privilege
, &luid
))
325 privileges_to_disable_
.push_back(luid
);
327 return ::GetLastError();
329 return ERROR_SUCCESS
;
332 DWORD
RestrictedToken::AddRestrictingSid(const Sid
&sid
) {
335 return ERROR_NO_TOKEN
;
337 sids_to_restrict_
.push_back(sid
); // No attributes
338 return ERROR_SUCCESS
;
341 DWORD
RestrictedToken::AddRestrictingSidLogonSession() {
344 return ERROR_NO_TOKEN
;
347 scoped_ptr
<BYTE
[]> buffer
=
348 GetTokenInfo(effective_token_
, TokenGroups
, &error
);
353 TOKEN_GROUPS
* token_groups
= reinterpret_cast<TOKEN_GROUPS
*>(buffer
.get());
355 SID
*logon_sid
= NULL
;
356 for (unsigned int i
= 0; i
< token_groups
->GroupCount
; ++i
) {
357 if ((token_groups
->Groups
[i
].Attributes
& SE_GROUP_LOGON_ID
) != 0) {
358 logon_sid
= static_cast<SID
*>(token_groups
->Groups
[i
].Sid
);
364 sids_to_restrict_
.push_back(logon_sid
);
366 return ERROR_SUCCESS
;
369 DWORD
RestrictedToken::AddRestrictingSidCurrentUser() {
372 return ERROR_NO_TOKEN
;
374 DWORD size
= sizeof(TOKEN_USER
) + SECURITY_MAX_SID_SIZE
;
375 scoped_ptr
<BYTE
[]> buffer(new BYTE
[size
]);
376 TOKEN_USER
* token_user
= reinterpret_cast<TOKEN_USER
*>(buffer
.get());
378 BOOL result
= ::GetTokenInformation(effective_token_
.Get(), TokenUser
,
379 token_user
, size
, &size
);
382 return ::GetLastError();
384 Sid user
= reinterpret_cast<SID
*>(token_user
->User
.Sid
);
385 sids_to_restrict_
.push_back(user
);
387 return ERROR_SUCCESS
;
390 DWORD
RestrictedToken::AddRestrictingSidAllSids() {
393 return ERROR_NO_TOKEN
;
395 // Add the current user to the list.
396 DWORD error
= AddRestrictingSidCurrentUser();
397 if (ERROR_SUCCESS
!= error
)
400 scoped_ptr
<BYTE
[]> buffer
=
401 GetTokenInfo(effective_token_
, TokenGroups
, &error
);
406 TOKEN_GROUPS
* token_groups
= reinterpret_cast<TOKEN_GROUPS
*>(buffer
.get());
408 // Build the list of restricting sids from all groups.
409 for (unsigned int i
= 0; i
< token_groups
->GroupCount
; ++i
) {
410 if ((token_groups
->Groups
[i
].Attributes
& SE_GROUP_INTEGRITY
) == 0)
411 AddRestrictingSid(reinterpret_cast<SID
*>(token_groups
->Groups
[i
].Sid
));
414 return ERROR_SUCCESS
;
417 DWORD
RestrictedToken::SetIntegrityLevel(IntegrityLevel integrity_level
) {
418 integrity_level_
= integrity_level
;
419 return ERROR_SUCCESS
;
422 } // namespace sandbox