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.
4
5 #include "sandbox/win/src/restricted_token.h"
6
7 #include <vector>
8
9 #include "base/logging.h"
10 #include "sandbox/win/src/acl.h"
11 #include "sandbox/win/src/win_utils.h"
12
13
14 namespace sandbox {
15
Init(const HANDLE effective_token)16 unsigned RestrictedToken::Init(const HANDLE effective_token) {
17 if (init_)
18 return ERROR_ALREADY_INITIALIZED;
19
20 if (effective_token) {
21 // We duplicate the handle to be able to use it even if the original handle
22 // is closed.
23 HANDLE effective_token_dup;
24 if (::DuplicateHandle(::GetCurrentProcess(),
25 effective_token,
26 ::GetCurrentProcess(),
27 &effective_token_dup,
28 0,
29 FALSE,
30 DUPLICATE_SAME_ACCESS)) {
31 effective_token_ = effective_token_dup;
32 } else {
33 return ::GetLastError();
34 }
35 } else {
36 if (!::OpenProcessToken(::GetCurrentProcess(),
37 TOKEN_ALL_ACCESS,
38 &effective_token_))
39 return ::GetLastError();
40 }
41
42 init_ = true;
43 return ERROR_SUCCESS;
44 }
45
GetRestrictedTokenHandle(HANDLE * token_handle) const46 unsigned RestrictedToken::GetRestrictedTokenHandle(HANDLE *token_handle) const {
47 DCHECK(init_);
48 if (!init_)
49 return ERROR_NO_TOKEN;
50
51 size_t deny_size = sids_for_deny_only_.size();
52 size_t restrict_size = sids_to_restrict_.size();
53 size_t privileges_size = privileges_to_disable_.size();
54
55 SID_AND_ATTRIBUTES *deny_only_array = NULL;
56 if (deny_size) {
57 deny_only_array = new SID_AND_ATTRIBUTES[deny_size];
58
59 for (unsigned int i = 0; i < sids_for_deny_only_.size() ; ++i) {
60 deny_only_array[i].Attributes = SE_GROUP_USE_FOR_DENY_ONLY;
61 deny_only_array[i].Sid =
62 const_cast<SID*>(sids_for_deny_only_[i].GetPSID());
63 }
64 }
65
66 SID_AND_ATTRIBUTES *sids_to_restrict_array = NULL;
67 if (restrict_size) {
68 sids_to_restrict_array = new SID_AND_ATTRIBUTES[restrict_size];
69
70 for (unsigned int i = 0; i < restrict_size; ++i) {
71 sids_to_restrict_array[i].Attributes = 0;
72 sids_to_restrict_array[i].Sid =
73 const_cast<SID*>(sids_to_restrict_[i].GetPSID());
74 }
75 }
76
77 LUID_AND_ATTRIBUTES *privileges_to_disable_array = NULL;
78 if (privileges_size) {
79 privileges_to_disable_array = new LUID_AND_ATTRIBUTES[privileges_size];
80
81 for (unsigned int i = 0; i < privileges_size; ++i) {
82 privileges_to_disable_array[i].Attributes = 0;
83 privileges_to_disable_array[i].Luid = privileges_to_disable_[i];
84 }
85 }
86
87 BOOL result = TRUE;
88 HANDLE new_token = NULL;
89 // The SANDBOX_INERT flag did nothing in XP and it was just a way to tell
90 // if a token has ben restricted given the limiations of IsTokenRestricted()
91 // but it appears that in Windows 7 it hints the AppLocker subsystem to
92 // leave us alone.
93 if (deny_size || restrict_size || privileges_size) {
94 result = ::CreateRestrictedToken(effective_token_,
95 SANDBOX_INERT,
96 static_cast<DWORD>(deny_size),
97 deny_only_array,
98 static_cast<DWORD>(privileges_size),
99 privileges_to_disable_array,
100 static_cast<DWORD>(restrict_size),
101 sids_to_restrict_array,
102 &new_token);
103 } else {
104 // Duplicate the token even if it's not modified at this point
105 // because any subsequent changes to this token would also affect the
106 // current process.
107 result = ::DuplicateTokenEx(effective_token_, TOKEN_ALL_ACCESS, NULL,
108 SecurityIdentification, TokenPrimary,
109 &new_token);
110 }
111
112 if (deny_only_array)
113 delete[] deny_only_array;
114
115 if (sids_to_restrict_array)
116 delete[] sids_to_restrict_array;
117
118 if (privileges_to_disable_array)
119 delete[] privileges_to_disable_array;
120
121 if (!result)
122 return ::GetLastError();
123
124 // Modify the default dacl on the token to contain Restricted and the user.
125 if (!AddSidToDefaultDacl(new_token, WinRestrictedCodeSid, GENERIC_ALL))
126 return ::GetLastError();
127
128 if (!AddUserSidToDefaultDacl(new_token, GENERIC_ALL))
129 return ::GetLastError();
130
131 DWORD error = SetTokenIntegrityLevel(new_token, integrity_level_);
132 if (ERROR_SUCCESS != error)
133 return error;
134
135 BOOL status = ::DuplicateHandle(::GetCurrentProcess(),
136 new_token,
137 ::GetCurrentProcess(),
138 token_handle,
139 TOKEN_ALL_ACCESS,
140 FALSE, // Don't inherit.
141 0);
142
143 if (new_token != effective_token_)
144 ::CloseHandle(new_token);
145
146 if (!status)
147 return ::GetLastError();
148
149 return ERROR_SUCCESS;
150 }
151
GetRestrictedTokenHandleForImpersonation(HANDLE * token_handle) const152 unsigned RestrictedToken::GetRestrictedTokenHandleForImpersonation(
153 HANDLE *token_handle) const {
154 DCHECK(init_);
155 if (!init_)
156 return ERROR_NO_TOKEN;
157
158 HANDLE restricted_token_handle;
159 unsigned err_code = GetRestrictedTokenHandle(&restricted_token_handle);
160 if (ERROR_SUCCESS != err_code)
161 return err_code;
162
163 HANDLE impersonation_token;
164 if (!::DuplicateToken(restricted_token_handle,
165 SecurityImpersonation,
166 &impersonation_token)) {
167 ::CloseHandle(restricted_token_handle);
168 return ::GetLastError();
169 }
170
171 ::CloseHandle(restricted_token_handle);
172
173 BOOL status = ::DuplicateHandle(::GetCurrentProcess(),
174 impersonation_token,
175 ::GetCurrentProcess(),
176 token_handle,
177 TOKEN_ALL_ACCESS,
178 FALSE, // Don't inherit.
179 0);
180
181 ::CloseHandle(impersonation_token);
182
183 if (!status)
184 return ::GetLastError();
185
186 return ERROR_SUCCESS;
187 }
188
AddAllSidsForDenyOnly(std::vector<Sid> * exceptions)189 unsigned RestrictedToken::AddAllSidsForDenyOnly(std::vector<Sid> *exceptions) {
190 DCHECK(init_);
191 if (!init_)
192 return ERROR_NO_TOKEN;
193
194 TOKEN_GROUPS *token_groups = NULL;
195 DWORD size = 0;
196
197 BOOL result = ::GetTokenInformation(effective_token_,
198 TokenGroups,
199 NULL, // No buffer.
200 0, // Size is 0.
201 &size);
202 if (!size)
203 return ::GetLastError();
204
205 token_groups = reinterpret_cast<TOKEN_GROUPS*>(new BYTE[size]);
206 result = ::GetTokenInformation(effective_token_,
207 TokenGroups,
208 token_groups,
209 size,
210 &size);
211 if (!result) {
212 delete[] reinterpret_cast<BYTE*>(token_groups);
213 return ::GetLastError();
214 }
215
216 // Build the list of the deny only group SIDs
217 for (unsigned int i = 0; i < token_groups->GroupCount ; ++i) {
218 if ((token_groups->Groups[i].Attributes & SE_GROUP_INTEGRITY) == 0 &&
219 (token_groups->Groups[i].Attributes & SE_GROUP_LOGON_ID) == 0) {
220 bool should_ignore = false;
221 if (exceptions) {
222 for (unsigned int j = 0; j < exceptions->size(); ++j) {
223 if (::EqualSid(const_cast<SID*>((*exceptions)[j].GetPSID()),
224 token_groups->Groups[i].Sid)) {
225 should_ignore = true;
226 break;
227 }
228 }
229 }
230 if (!should_ignore) {
231 sids_for_deny_only_.push_back(
232 reinterpret_cast<SID*>(token_groups->Groups[i].Sid));
233 }
234 }
235 }
236
237 delete[] reinterpret_cast<BYTE*>(token_groups);
238
239 return ERROR_SUCCESS;
240 }
241
AddSidForDenyOnly(const Sid & sid)242 unsigned RestrictedToken::AddSidForDenyOnly(const Sid &sid) {
243 DCHECK(init_);
244 if (!init_)
245 return ERROR_NO_TOKEN;
246
247 sids_for_deny_only_.push_back(sid);
248 return ERROR_SUCCESS;
249 }
250
AddUserSidForDenyOnly()251 unsigned RestrictedToken::AddUserSidForDenyOnly() {
252 DCHECK(init_);
253 if (!init_)
254 return ERROR_NO_TOKEN;
255
256 DWORD size = sizeof(TOKEN_USER) + SECURITY_MAX_SID_SIZE;
257 TOKEN_USER* token_user = reinterpret_cast<TOKEN_USER*>(new BYTE[size]);
258
259 BOOL result = ::GetTokenInformation(effective_token_,
260 TokenUser,
261 token_user,
262 size,
263 &size);
264
265 if (!result) {
266 delete[] reinterpret_cast<BYTE*>(token_user);
267 return ::GetLastError();
268 }
269
270 Sid user = reinterpret_cast<SID*>(token_user->User.Sid);
271 sids_for_deny_only_.push_back(user);
272
273 delete[] reinterpret_cast<BYTE*>(token_user);
274
275 return ERROR_SUCCESS;
276 }
277
DeleteAllPrivileges(const std::vector<base::string16> * exceptions)278 unsigned RestrictedToken::DeleteAllPrivileges(
279 const std::vector<base::string16> *exceptions) {
280 DCHECK(init_);
281 if (!init_)
282 return ERROR_NO_TOKEN;
283
284 // Get the list of privileges in the token
285 TOKEN_PRIVILEGES *token_privileges = NULL;
286 DWORD size = 0;
287
288 BOOL result = ::GetTokenInformation(effective_token_,
289 TokenPrivileges,
290 NULL, // No buffer.
291 0, // Size is 0.
292 &size);
293 if (!size)
294 return ::GetLastError();
295
296 token_privileges = reinterpret_cast<TOKEN_PRIVILEGES*>(new BYTE[size]);
297 result = ::GetTokenInformation(effective_token_,
298 TokenPrivileges,
299 token_privileges,
300 size,
301 &size);
302 if (!result) {
303 delete[] reinterpret_cast<BYTE *>(token_privileges);
304 return ::GetLastError();
305 }
306
307
308 // Build the list of privileges to disable
309 for (unsigned int i = 0; i < token_privileges->PrivilegeCount; ++i) {
310 bool should_ignore = false;
311 if (exceptions) {
312 for (unsigned int j = 0; j < exceptions->size(); ++j) {
313 LUID luid = {0};
314 ::LookupPrivilegeValue(NULL, (*exceptions)[j].c_str(), &luid);
315 if (token_privileges->Privileges[i].Luid.HighPart == luid.HighPart &&
316 token_privileges->Privileges[i].Luid.LowPart == luid.LowPart) {
317 should_ignore = true;
318 break;
319 }
320 }
321 }
322 if (!should_ignore) {
323 privileges_to_disable_.push_back(token_privileges->Privileges[i].Luid);
324 }
325 }
326
327 delete[] reinterpret_cast<BYTE *>(token_privileges);
328
329 return ERROR_SUCCESS;
330 }
331
DeletePrivilege(const wchar_t * privilege)332 unsigned RestrictedToken::DeletePrivilege(const wchar_t *privilege) {
333 DCHECK(init_);
334 if (!init_)
335 return ERROR_NO_TOKEN;
336
337 LUID luid = {0};
338 if (LookupPrivilegeValue(NULL, privilege, &luid))
339 privileges_to_disable_.push_back(luid);
340 else
341 return ::GetLastError();
342
343 return ERROR_SUCCESS;
344 }
345
AddRestrictingSid(const Sid & sid)346 unsigned RestrictedToken::AddRestrictingSid(const Sid &sid) {
347 DCHECK(init_);
348 if (!init_)
349 return ERROR_NO_TOKEN;
350
351 sids_to_restrict_.push_back(sid); // No attributes
352 return ERROR_SUCCESS;
353 }
354
AddRestrictingSidLogonSession()355 unsigned RestrictedToken::AddRestrictingSidLogonSession() {
356 DCHECK(init_);
357 if (!init_)
358 return ERROR_NO_TOKEN;
359
360 TOKEN_GROUPS *token_groups = NULL;
361 DWORD size = 0;
362
363 BOOL result = ::GetTokenInformation(effective_token_,
364 TokenGroups,
365 NULL, // No buffer.
366 0, // Size is 0.
367 &size);
368 if (!size)
369 return ::GetLastError();
370
371 token_groups = reinterpret_cast<TOKEN_GROUPS*>(new BYTE[size]);
372 result = ::GetTokenInformation(effective_token_,
373 TokenGroups,
374 token_groups,
375 size,
376 &size);
377 if (!result) {
378 delete[] reinterpret_cast<BYTE*>(token_groups);
379 return ::GetLastError();
380 }
381
382 SID *logon_sid = NULL;
383 for (unsigned int i = 0; i < token_groups->GroupCount ; ++i) {
384 if ((token_groups->Groups[i].Attributes & SE_GROUP_LOGON_ID) != 0) {
385 logon_sid = static_cast<SID*>(token_groups->Groups[i].Sid);
386 break;
387 }
388 }
389
390 if (logon_sid)
391 sids_to_restrict_.push_back(logon_sid);
392
393 delete[] reinterpret_cast<BYTE*>(token_groups);
394
395 return ERROR_SUCCESS;
396 }
397
AddRestrictingSidCurrentUser()398 unsigned RestrictedToken::AddRestrictingSidCurrentUser() {
399 DCHECK(init_);
400 if (!init_)
401 return ERROR_NO_TOKEN;
402
403 DWORD size = sizeof(TOKEN_USER) + SECURITY_MAX_SID_SIZE;
404 TOKEN_USER* token_user = reinterpret_cast<TOKEN_USER*>(new BYTE[size]);
405
406 BOOL result = ::GetTokenInformation(effective_token_,
407 TokenUser,
408 token_user,
409 size,
410 &size);
411
412 if (!result) {
413 delete[] reinterpret_cast<BYTE*>(token_user);
414 return ::GetLastError();
415 }
416
417 Sid user = reinterpret_cast<SID*>(token_user->User.Sid);
418 sids_to_restrict_.push_back(user);
419
420 delete[] reinterpret_cast<BYTE*>(token_user);
421
422 return ERROR_SUCCESS;
423 }
424
AddRestrictingSidAllSids()425 unsigned RestrictedToken::AddRestrictingSidAllSids() {
426 DCHECK(init_);
427 if (!init_)
428 return ERROR_NO_TOKEN;
429
430 // Add the current user to the list.
431 unsigned error = AddRestrictingSidCurrentUser();
432 if (ERROR_SUCCESS != error)
433 return error;
434
435 TOKEN_GROUPS *token_groups = NULL;
436 DWORD size = 0;
437
438 // Get the buffer size required.
439 BOOL result = ::GetTokenInformation(effective_token_, TokenGroups, NULL, 0,
440 &size);
441 if (!size)
442 return ::GetLastError();
443
444 token_groups = reinterpret_cast<TOKEN_GROUPS*>(new BYTE[size]);
445 result = ::GetTokenInformation(effective_token_,
446 TokenGroups,
447 token_groups,
448 size,
449 &size);
450 if (!result) {
451 delete[] reinterpret_cast<BYTE*>(token_groups);
452 return ::GetLastError();
453 }
454
455 // Build the list of restricting sids from all groups.
456 for (unsigned int i = 0; i < token_groups->GroupCount ; ++i) {
457 if ((token_groups->Groups[i].Attributes & SE_GROUP_INTEGRITY) == 0)
458 AddRestrictingSid(reinterpret_cast<SID*>(token_groups->Groups[i].Sid));
459 }
460
461 delete[] reinterpret_cast<BYTE*>(token_groups);
462
463 return ERROR_SUCCESS;
464 }
465
SetIntegrityLevel(IntegrityLevel integrity_level)466 unsigned RestrictedToken::SetIntegrityLevel(IntegrityLevel integrity_level) {
467 integrity_level_ = integrity_level;
468 return ERROR_SUCCESS;
469 }
470
471 } // namespace sandbox
472