Add ICU message format support
[chromium-blink-merge.git] / sandbox / win / src / restricted_token.cc
blobdf5a87ccf0c0e60c0c4dcd6f9bfffb03705e271b
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);
144 if (deny_only_array)
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;
153 if (!result)
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)
167 return error;
169 HANDLE token_handle;
170 if (!::DuplicateHandle(::GetCurrentProcess(), new_token.Get(),
171 ::GetCurrentProcess(), &token_handle,
172 TOKEN_ALL_ACCESS, FALSE, // Don't inherit.
173 0)) {
174 return ::GetLastError();
177 token->Set(token_handle);
178 return ERROR_SUCCESS;
181 DWORD RestrictedToken::GetRestrictedTokenForImpersonation(
182 base::win::ScopedHandle* token) const {
183 DCHECK(init_);
184 if (!init_)
185 return ERROR_NO_TOKEN;
187 base::win::ScopedHandle restricted_token;
188 DWORD err_code = GetRestrictedToken(&restricted_token);
189 if (ERROR_SUCCESS != err_code)
190 return 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);
200 HANDLE token_handle;
201 if (!::DuplicateHandle(::GetCurrentProcess(), impersonation_token.Get(),
202 ::GetCurrentProcess(), &token_handle,
203 TOKEN_ALL_ACCESS, FALSE, // Don't inherit.
204 0)) {
205 return ::GetLastError();
208 token->Set(token_handle);
209 return ERROR_SUCCESS;
212 DWORD RestrictedToken::AddAllSidsForDenyOnly(std::vector<Sid> *exceptions) {
213 DCHECK(init_);
214 if (!init_)
215 return ERROR_NO_TOKEN;
217 DWORD error;
218 scoped_ptr<BYTE[]> buffer =
219 GetTokenInfo(effective_token_, TokenGroups, &error);
221 if (!buffer)
222 return 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;
231 if (exceptions) {
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;
236 break;
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) {
251 DCHECK(init_);
252 if (!init_)
253 return ERROR_NO_TOKEN;
255 sids_for_deny_only_.push_back(sid);
256 return ERROR_SUCCESS;
259 DWORD RestrictedToken::AddUserSidForDenyOnly() {
260 DCHECK(init_);
261 if (!init_)
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);
271 if (!result)
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) {
282 DCHECK(init_);
283 if (!init_)
284 return ERROR_NO_TOKEN;
286 DWORD error;
287 scoped_ptr<BYTE[]> buffer =
288 GetTokenInfo(effective_token_, TokenPrivileges, &error);
290 if (!buffer)
291 return 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;
299 if (exceptions) {
300 for (unsigned int j = 0; j < exceptions->size(); ++j) {
301 LUID luid = {0};
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;
306 break;
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) {
319 DCHECK(init_);
320 if (!init_)
321 return ERROR_NO_TOKEN;
323 LUID luid = {0};
324 if (LookupPrivilegeValue(NULL, privilege, &luid))
325 privileges_to_disable_.push_back(luid);
326 else
327 return ::GetLastError();
329 return ERROR_SUCCESS;
332 DWORD RestrictedToken::AddRestrictingSid(const Sid &sid) {
333 DCHECK(init_);
334 if (!init_)
335 return ERROR_NO_TOKEN;
337 sids_to_restrict_.push_back(sid); // No attributes
338 return ERROR_SUCCESS;
341 DWORD RestrictedToken::AddRestrictingSidLogonSession() {
342 DCHECK(init_);
343 if (!init_)
344 return ERROR_NO_TOKEN;
346 DWORD error;
347 scoped_ptr<BYTE[]> buffer =
348 GetTokenInfo(effective_token_, TokenGroups, &error);
350 if (!buffer)
351 return 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);
359 break;
363 if (logon_sid)
364 sids_to_restrict_.push_back(logon_sid);
366 return ERROR_SUCCESS;
369 DWORD RestrictedToken::AddRestrictingSidCurrentUser() {
370 DCHECK(init_);
371 if (!init_)
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);
381 if (!result)
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() {
391 DCHECK(init_);
392 if (!init_)
393 return ERROR_NO_TOKEN;
395 // Add the current user to the list.
396 DWORD error = AddRestrictingSidCurrentUser();
397 if (ERROR_SUCCESS != error)
398 return error;
400 scoped_ptr<BYTE[]> buffer =
401 GetTokenInfo(effective_token_, TokenGroups, &error);
403 if (!buffer)
404 return 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