Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / sandbox / win / src / restricted_token.cc
blobf0fc4cbc37df144fc2f1899181f309ecdf3f91bf
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"
7 #include <vector>
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"
14 namespace {
16 // Calls GetTokenInformation with the desired |info_class| and returns a buffer
17 // with the result.
18 scoped_ptr<BYTE[]> GetTokenInfo(const base::win::ScopedHandle& token,
19 TOKEN_INFORMATION_CLASS info_class,
20 DWORD* error) {
21 // Get the required buffer size.
22 DWORD size = 0;
23 ::GetTokenInformation(token.Get(), info_class, NULL, 0, &size);
24 if (!size) {
25 *error = ::GetLastError();
26 return nullptr;
29 scoped_ptr<BYTE[]> buffer(new BYTE[size]);
30 if (!::GetTokenInformation(token.Get(), info_class, buffer.get(), size,
31 &size)) {
32 *error = ::GetLastError();
33 return nullptr;
36 *error = ERROR_SUCCESS;
37 return buffer.Pass();
40 } // namespace
42 namespace sandbox {
44 RestrictedToken::RestrictedToken()
45 : integrity_level_(INTEGRITY_LEVEL_LAST),
46 init_(false) {
49 RestrictedToken::~RestrictedToken() {
52 DWORD RestrictedToken::Init(const HANDLE effective_token) {
53 if (init_)
54 return ERROR_ALREADY_INITIALIZED;
56 HANDLE temp_token;
57 if (effective_token) {
58 // We duplicate the handle to be able to use it even if the original handle
59 // is closed.
60 if (!::DuplicateHandle(::GetCurrentProcess(), effective_token,
61 ::GetCurrentProcess(), &temp_token,
62 0, FALSE, DUPLICATE_SAME_ACCESS)) {
63 return ::GetLastError();
65 } else {
66 if (!::OpenProcessToken(::GetCurrentProcess(), TOKEN_ALL_ACCESS,
67 &temp_token)) {
68 return ::GetLastError();
71 effective_token_.Set(temp_token);
73 init_ = true;
74 return ERROR_SUCCESS;
77 DWORD RestrictedToken::GetRestrictedToken(
78 base::win::ScopedHandle* token) const {
79 DCHECK(init_);
80 if (!init_)
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;
88 if (deny_size) {
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;
99 if (restrict_size) {
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];
119 BOOL result = TRUE;
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
124 // leave us alone.
125 if (deny_size || restrict_size || privileges_size) {
126 result = ::CreateRestrictedToken(effective_token_.Get(),
127 SANDBOX_INERT,
128 static_cast<DWORD>(deny_size),
129 deny_only_array,
130 static_cast<DWORD>(privileges_size),
131 privileges_to_disable_array,
132 static_cast<DWORD>(restrict_size),
133 sids_to_restrict_array,
134 &new_token_handle);
135 } else {
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
138 // current process.
139 result = ::DuplicateTokenEx(effective_token_.Get(), TOKEN_ALL_ACCESS, NULL,
140 SecurityIdentification, TokenPrimary,
141 &new_token_handle);
143 auto last_error = ::GetLastError();
145 if (deny_only_array)
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;
154 if (!result)
155 return last_error;
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)
168 return error;
170 HANDLE token_handle;
171 if (!::DuplicateHandle(::GetCurrentProcess(), new_token.Get(),
172 ::GetCurrentProcess(), &token_handle,
173 TOKEN_ALL_ACCESS, FALSE, // Don't inherit.
174 0)) {
175 return ::GetLastError();
178 token->Set(token_handle);
179 return ERROR_SUCCESS;
182 DWORD RestrictedToken::GetRestrictedTokenForImpersonation(
183 base::win::ScopedHandle* token) const {
184 DCHECK(init_);
185 if (!init_)
186 return ERROR_NO_TOKEN;
188 base::win::ScopedHandle restricted_token;
189 DWORD err_code = GetRestrictedToken(&restricted_token);
190 if (ERROR_SUCCESS != err_code)
191 return 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);
201 HANDLE token_handle;
202 if (!::DuplicateHandle(::GetCurrentProcess(), impersonation_token.Get(),
203 ::GetCurrentProcess(), &token_handle,
204 TOKEN_ALL_ACCESS, FALSE, // Don't inherit.
205 0)) {
206 return ::GetLastError();
209 token->Set(token_handle);
210 return ERROR_SUCCESS;
213 DWORD RestrictedToken::AddAllSidsForDenyOnly(std::vector<Sid> *exceptions) {
214 DCHECK(init_);
215 if (!init_)
216 return ERROR_NO_TOKEN;
218 DWORD error;
219 scoped_ptr<BYTE[]> buffer =
220 GetTokenInfo(effective_token_, TokenGroups, &error);
222 if (!buffer)
223 return 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;
232 if (exceptions) {
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;
237 break;
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) {
252 DCHECK(init_);
253 if (!init_)
254 return ERROR_NO_TOKEN;
256 sids_for_deny_only_.push_back(sid);
257 return ERROR_SUCCESS;
260 DWORD RestrictedToken::AddUserSidForDenyOnly() {
261 DCHECK(init_);
262 if (!init_)
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);
272 if (!result)
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) {
283 DCHECK(init_);
284 if (!init_)
285 return ERROR_NO_TOKEN;
287 DWORD error;
288 scoped_ptr<BYTE[]> buffer =
289 GetTokenInfo(effective_token_, TokenPrivileges, &error);
291 if (!buffer)
292 return 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;
300 if (exceptions) {
301 for (unsigned int j = 0; j < exceptions->size(); ++j) {
302 LUID luid = {0};
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;
307 break;
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) {
320 DCHECK(init_);
321 if (!init_)
322 return ERROR_NO_TOKEN;
324 LUID luid = {0};
325 if (LookupPrivilegeValue(NULL, privilege, &luid))
326 privileges_to_disable_.push_back(luid);
327 else
328 return ::GetLastError();
330 return ERROR_SUCCESS;
333 DWORD RestrictedToken::AddRestrictingSid(const Sid &sid) {
334 DCHECK(init_);
335 if (!init_)
336 return ERROR_NO_TOKEN;
338 sids_to_restrict_.push_back(sid); // No attributes
339 return ERROR_SUCCESS;
342 DWORD RestrictedToken::AddRestrictingSidLogonSession() {
343 DCHECK(init_);
344 if (!init_)
345 return ERROR_NO_TOKEN;
347 DWORD error;
348 scoped_ptr<BYTE[]> buffer =
349 GetTokenInfo(effective_token_, TokenGroups, &error);
351 if (!buffer)
352 return 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);
360 break;
364 if (logon_sid)
365 sids_to_restrict_.push_back(logon_sid);
367 return ERROR_SUCCESS;
370 DWORD RestrictedToken::AddRestrictingSidCurrentUser() {
371 DCHECK(init_);
372 if (!init_)
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);
382 if (!result)
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() {
392 DCHECK(init_);
393 if (!init_)
394 return ERROR_NO_TOKEN;
396 // Add the current user to the list.
397 DWORD error = AddRestrictingSidCurrentUser();
398 if (ERROR_SUCCESS != error)
399 return error;
401 scoped_ptr<BYTE[]> buffer =
402 GetTokenInfo(effective_token_, TokenGroups, &error);
404 if (!buffer)
405 return 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