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